aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/installer
diff options
context:
space:
mode:
Diffstat (limited to 'installer')
-rw-r--r--installer/.gitignore2
-rw-r--r--installer/build.bat26
-rw-r--r--installer/customactions.c357
-rw-r--r--installer/fetcher/.gitignore6
-rw-r--r--installer/fetcher/Makefile42
-rw-r--r--installer/fetcher/constants.h17
-rw-r--r--installer/fetcher/crypto.c2252
-rw-r--r--installer/fetcher/crypto.h29
-rw-r--r--installer/fetcher/fetcher.c340
-rw-r--r--installer/fetcher/filelist.c168
-rw-r--r--installer/fetcher/filelist.h17
-rw-r--r--installer/fetcher/icon.svg92
-rw-r--r--installer/fetcher/manifest.xml34
-rw-r--r--installer/fetcher/resources.rc41
-rw-r--r--installer/fetcher/systeminfo.c74
-rw-r--r--installer/fetcher/systeminfo.h16
-rw-r--r--installer/fetcher/version.h12
-rw-r--r--installer/wireguard.wxs83
18 files changed, 3472 insertions, 136 deletions
diff --git a/installer/.gitignore b/installer/.gitignore
index 2933d030..fc4d2127 100644
--- a/installer/.gitignore
+++ b/installer/.gitignore
@@ -5,3 +5,5 @@
/dist
/x86
/amd64
+/arm
+/arm64
diff --git a/installer/build.bat b/installer/build.bat
index 864987d8..60d2558c 100644
--- a/installer/build.bat
+++ b/installer/build.bat
@@ -1,30 +1,27 @@
@echo off
rem SPDX-License-Identifier: MIT
-rem Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+rem Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
setlocal
set PATHEXT=.exe
set BUILDDIR=%~dp0
cd /d %BUILDDIR% || exit /b 1
-for /f "tokens=3" %%a in ('findstr /r "WIREGUARD_WINDOWS_VERSION_STRING.*[0-9.]*" ..\version\version.h') do set WIREGUARD_VERSION=%%a
+for /f "tokens=3" %%a in ('findstr /r "Number.*=.*[0-9.]*" ..\version\version.go') do set WIREGUARD_VERSION=%%a
set WIREGUARD_VERSION=%WIREGUARD_VERSION:"=%
set WIX_CANDLE_FLAGS=-nologo -dWIREGUARD_VERSION="%WIREGUARD_VERSION%"
set WIX_LIGHT_FLAGS=-nologo -spdb
-set WIX_LIGHT_FLAGS=%WIX_LIGHT_FLAGS% -sw1056
-set WIX_LIGHT_FLAGS=%WIX_LIGHT_FLAGS% -sice:ICE30
+set WIX_LIGHT_FLAGS=%WIX_LIGHT_FLAGS% -sice:ICE39
set WIX_LIGHT_FLAGS=%WIX_LIGHT_FLAGS% -sice:ICE61
-set WIX_LIGHT_FLAGS=%WIX_LIGHT_FLAGS% -sice:ICE09
+set WIX_LIGHT_FLAGS=%WIX_LIGHT_FLAGS% -sice:ICE03
if exist .deps\prepared goto :build
:installdeps
rmdir /s /q .deps 2> NUL
mkdir .deps || goto :error
cd .deps || goto :error
- call :download wintun-x86.msm https://www.wintun.net/builds/wintun-x86-0.8.1.msm 5b47f83ffa9c361a360196d692f64755183e82c65f4753accc92087e6736af10 || goto :error
- call :download wintun-amd64.msm https://www.wintun.net/builds/wintun-amd64-0.8.1.msm af9644438a716f5a022052e3574ee0404c3e3309daff84889d656178fbc6b168 || goto :error
- call :download wix-binaries.zip https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip 2c1888d5d1dba377fc7fa14444cf556963747ff9a0a289a3599cf09da03b9e2e || goto :error
+ call :download wix-binaries.zip https://wixtoolset.org/downloads/v3.14.0.4118/wix314-binaries.zip 34dcbba9952902bfb710161bd45ee2e721ffa878db99f738285a21c9b09c6edb || goto :error
echo [+] Extracting wix-binaries.zip
mkdir wix\bin || goto :error
tar -xf wix-binaries.zip -C wix\bin || goto :error
@@ -34,14 +31,15 @@ if exist .deps\prepared goto :build
cd .. || goto :error
:build
+ if exist ..\sign.bat call ..\sign.bat
+ set PATH=%BUILDDIR%..\.deps\llvm-mingw\bin;%PATH%
set WIX=%BUILDDIR%.deps\wix\
set CFLAGS=-O3 -Wall -std=gnu11 -DWINVER=0x0601 -D_WIN32_WINNT=0x0601 -municode -DUNICODE -D_UNICODE -DNDEBUG
set LDFLAGS=-shared -s -Wl,--kill-at -Wl,--major-os-version=6 -Wl,--minor-os-version=1 -Wl,--major-subsystem-version=6 -Wl,--minor-subsystem-version=1 -Wl,--tsaware -Wl,--dynamicbase -Wl,--nxcompat -Wl,--export-all-symbols
- set LDLIBS=-lmsi -lole32 -lshlwapi -lshell32 -luuid
+ set LDLIBS=-lmsi -lole32 -lshlwapi -lshell32 -luuid -lntdll
call :msi x86 i686 x86 || goto :error
- set CGO_LDFLAGS=%CGO_LDFLAGS% -Wl,--high-entropy-va
call :msi amd64 x86_64 x64 || goto :error
- if exist ..\sign.bat call ..\sign.bat
+ call :msi arm64 aarch64 arm64 || goto :error
if "%SigningCertificate%"=="" goto :success
if "%TimestampServer%"=="" goto :success
echo [+] Signing
@@ -59,11 +57,15 @@ if exist .deps\prepared goto :build
goto :eof
:msi
- set PATH=%BUILDDIR%..\.deps\%~2-w64-mingw32-native\bin;%PATH%
set CC=%~2-w64-mingw32-gcc
if not exist "%~1" mkdir "%~1"
echo [+] Compiling %1
%CC% %CFLAGS% %LDFLAGS% -o "%~1\customactions.dll" customactions.c %LDLIBS% || exit /b 1
+ if "%SigningCertificate%"=="" goto :skipsign
+ if "%TimestampServer%"=="" goto :skipsign
+ echo [+] Signing %1
+ signtool sign /sha1 "%SigningCertificate%" /fd sha256 /tr "%TimestampServer%" /td sha256 /d "WireGuard Setup Custom Actions" "%~1\customactions.dll" || exit /b 1
+:skipsign
"%WIX%bin\candle" %WIX_CANDLE_FLAGS% -dWIREGUARD_PLATFORM="%~1" -out "%~1\wireguard.wixobj" -arch %3 wireguard.wxs || exit /b %errorlevel%
echo [+] Linking %1
"%WIX%bin\light" %WIX_LIGHT_FLAGS% -out "dist\wireguard-%~1-%WIREGUARD_VERSION%.msi" "%~1\wireguard.wixobj" || exit /b %errorlevel%
diff --git a/installer/customactions.c b/installer/customactions.c
index 496d80b1..d3078a31 100644
--- a/installer/customactions.c
+++ b/installer/customactions.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
/*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
*/
#include <windows.h>
@@ -17,7 +17,7 @@
#define MANAGER_SERVICE_NAME TEXT("WireGuardManager")
#define TUNNEL_SERVICE_PREFIX TEXT("WireGuardTunnel$")
-enum log_level { LOG_LEVEL_INFO, LOG_LEVEL_WARN, LOG_LEVEL_ERR };
+enum log_level { LOG_LEVEL_INFO, LOG_LEVEL_WARN, LOG_LEVEL_ERR, LOG_LEVEL_MSIERR };
static void log_messagef(MSIHANDLE installer, enum log_level level, const TCHAR *format, ...)
{
@@ -49,6 +49,10 @@ static void log_messagef(MSIHANDLE installer, enum log_level level, const TCHAR
template = TEXT("WireGuard error: [1]");
type = INSTALLMESSAGE_ERROR;
break;
+ case LOG_LEVEL_MSIERR:
+ template = TEXT("[1]");
+ type = INSTALLMESSAGE_ERROR;
+ break;
default:
goto out;
}
@@ -78,6 +82,46 @@ static void log_errorf(MSIHANDLE installer, enum log_level level, DWORD error_co
LocalFree(system_message);
}
+__declspec(dllexport) UINT __stdcall CheckWow64(MSIHANDLE installer)
+{
+ UINT ret = ERROR_SUCCESS;
+ bool is_com_initialized = SUCCEEDED(CoInitialize(NULL));
+ HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
+ BOOL(WINAPI *IsWow64Process2)(HANDLE hProcess, USHORT *pProcessMachine, USHORT *pNativeMachine);
+ USHORT process_machine, native_machine;
+ BOOL is_wow64_process;
+
+ if (!kernel32) {
+ ret = GetLastError();
+ log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("Failed to get kernel32.dll handle"));
+ goto out;
+ }
+ *(FARPROC *)&IsWow64Process2 = GetProcAddress(kernel32, "IsWow64Process2");
+ if (IsWow64Process2) {
+ if (!IsWow64Process2(GetCurrentProcess(), &process_machine, &native_machine)) {
+ ret = GetLastError();
+ log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("Failed to determine Wow64 status from IsWow64Process2"));
+ goto out;
+ }
+ if (process_machine == IMAGE_FILE_MACHINE_UNKNOWN)
+ goto out;
+ } else {
+ if (!IsWow64Process(GetCurrentProcess(), &is_wow64_process)) {
+ ret = GetLastError();
+ log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("Failed to determine Wow64 status from IsWow64Process"));
+ goto out;
+ }
+ if (!is_wow64_process)
+ goto out;
+ }
+ log_messagef(installer, LOG_LEVEL_MSIERR, TEXT("You must use the native version of WireGuard on this computer."));
+ ret = ERROR_INSTALL_FAILURE;
+out:
+ if (is_com_initialized)
+ CoUninitialize();
+ return ret;
+}
+
static UINT insert_service_control(MSIHANDLE installer, MSIHANDLE view, const TCHAR *service_name, bool start)
{
static unsigned int index = 0;
@@ -126,62 +170,6 @@ out:
return ret;
}
-static bool remove_directory_recursive(MSIHANDLE installer, TCHAR path[MAX_PATH], unsigned int max_depth)
-{
- HANDLE find_handle;
- WIN32_FIND_DATA find_data;
- TCHAR *path_end;
-
- if (!max_depth) {
- log_messagef(installer, LOG_LEVEL_WARN, TEXT("Too many levels of nesting at \"%1\""), path);
- return false;
- }
-
- path_end = path + _tcsnlen(path, MAX_PATH);
- if (!PathAppend(path, TEXT("*.*"))) {
- log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("PathAppend(\"%1\", \"*.*\") failed"), path);
- return false;
- }
- find_handle = FindFirstFileEx(path, FindExInfoBasic, &find_data, FindExSearchNameMatch, NULL, 0);
- if (find_handle == INVALID_HANDLE_VALUE) {
- log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("FindFirstFileEx(\"%1\") failed"), path);
- return false;
- }
- do {
- if (find_data.cFileName[0] == TEXT('.') && (find_data.cFileName[1] == TEXT('\0') || (find_data.cFileName[1] == TEXT('.') && find_data.cFileName[2] == TEXT('\0'))))
- continue;
-
- path_end[0] = TEXT('\0');
- if (!PathAppend(path, find_data.cFileName)) {
- log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("PathAppend(\"%1\", \"%2\") failed"), path, find_data.cFileName);
- continue;
- }
-
- if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
- remove_directory_recursive(installer, path, max_depth - 1);
- continue;
- }
-
- if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) && !SetFileAttributes(path, find_data.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY))
- log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("SetFileAttributes(\"%1\") failed"), path);
-
- if (DeleteFile(path))
- log_messagef(installer, LOG_LEVEL_INFO, TEXT("Deleted \"%1\""), path);
- else
- log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("DeleteFile(\"%1\") failed"), path);
- } while (FindNextFile(find_handle, &find_data));
- FindClose(find_handle);
-
- path_end[0] = TEXT('\0');
- if (RemoveDirectory(path)) {
- log_messagef(installer, LOG_LEVEL_INFO, TEXT("Removed \"%1\""), path);
- return true;
- } else {
- log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("RemoveDirectory(\"%1\") failed"), path);
- return false;
- }
-}
-
__declspec(dllexport) UINT __stdcall EvaluateWireGuardServices(MSIHANDLE installer)
{
UINT ret = ERROR_INSTALL_FAILURE;
@@ -255,27 +243,82 @@ out:
return ret == ERROR_SUCCESS ? ret : ERROR_INSTALL_FAILURE;
}
-__declspec(dllexport) UINT __stdcall RemoveConfigFolder(MSIHANDLE installer)
+__declspec(dllexport) UINT __stdcall LaunchApplicationAndAbort(MSIHANDLE installer)
{
- LSTATUS ret;
+ UINT ret = ERROR_INSTALL_FAILURE;
TCHAR path[MAX_PATH];
+ DWORD path_len = _countof(path);
+ PROCESS_INFORMATION pi;
+ STARTUPINFO si = { .cb = sizeof(STARTUPINFO) };
+
+ ret = MsiGetProperty(installer, TEXT("WireGuardFolder"), path, &path_len);
+ if (ret != ERROR_SUCCESS) {
+ log_errorf(installer, LOG_LEVEL_WARN, ret, TEXT("MsiGetProperty(\"WireGuardFolder\") failed"));
+ goto out;
+ }
+ if (!path[0] || !PathAppend(path, TEXT("wireguard.exe")))
+ goto out;
+ log_messagef(installer, LOG_LEVEL_INFO, TEXT("Launching %1"), path);
+ if (!CreateProcess(path, TEXT("wireguard"), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
+ log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("Failed to create \"%1\" process"), path);
+ goto out;
+ }
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+out:
+ return ERROR_INSTALL_USEREXIT;
+}
+
+__declspec(dllexport) UINT __stdcall EvaluateWireGuardComponents(MSIHANDLE installer)
+{
+ UINT ret = ERROR_INSTALL_FAILURE;
bool is_com_initialized = SUCCEEDED(CoInitialize(NULL));
+ INSTALLSTATE component_installed, component_action;
+ TCHAR path[MAX_PATH];
+ DWORD path_len = _countof(path);
- ret = SHRegGetPath(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\S-1-5-18"),
- TEXT("ProfileImagePath"), path, 0);
+ ret = MsiGetComponentState(installer, TEXT("WireGuardExecutable"), &component_installed, &component_action);
if (ret != ERROR_SUCCESS) {
- log_errorf(installer, LOG_LEVEL_WARN, ret, TEXT("SHRegGetPath failed"));
+ log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("MsiGetComponentState(\"WireGuardExecutable\") failed"));
goto out;
}
- if (!PathAppend(path, TEXT("AppData\\Local\\WireGuard"))) {
- log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("PathAppend(\"%1\", \"AppData\\Local\\WireGuard\") failed"), path);
+ ret = MsiGetProperty(installer, TEXT("WireGuardFolder"), path, &path_len);
+ if (ret != ERROR_SUCCESS) {
+ log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("MsiGetProperty(\"WireGuardFolder\") failed"));
goto out;
}
- remove_directory_recursive(installer, path, 10);
+
+ if (component_action >= INSTALLSTATE_LOCAL) {
+ /* WireGuardExecutable component shall be installed. */
+ ret = MsiSetProperty(installer, TEXT("KillWireGuardProcesses"), path);
+ if (ret != ERROR_SUCCESS) {
+ log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("MsiSetProperty(\"KillWireGuardProcesses\") failed"));
+ goto out;
+ }
+ } else if (component_action >= INSTALLSTATE_REMOVED) {
+ /* WireGuardExecutable component shall be uninstalled. */
+ ret = MsiSetProperty(installer, TEXT("KillWireGuardProcesses"), path);
+ if (ret != ERROR_SUCCESS) {
+ log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("MsiSetProperty(\"KillWireGuardProcesses\") failed"));
+ goto out;
+ }
+ ret = MsiSetProperty(installer, TEXT("RemoveConfigFolder"), path);
+ if (ret != ERROR_SUCCESS) {
+ log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("MsiSetProperty(\"RemoveConfigFolder\") failed"));
+ goto out;
+ }
+ ret = MsiSetProperty(installer, TEXT("RemoveAdapters"), path);
+ if (ret != ERROR_SUCCESS) {
+ log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("MsiSetProperty(\"RemoveAdapters\") failed"));
+ goto out;
+ }
+ }
+ ret = ERROR_SUCCESS;
+
out:
if (is_com_initialized)
CoUninitialize();
- return ERROR_SUCCESS;
+ return ret == ERROR_SUCCESS ? ret : ERROR_INSTALL_FAILURE;
}
struct file_id { DWORD volume, index_high, index_low; };
@@ -299,36 +342,30 @@ static bool calculate_file_id(const TCHAR *path, struct file_id *id)
return true;
}
-static bool calculate_known_file_id(const KNOWNFOLDERID *known_folder, const TCHAR *file, struct file_id *id)
-{
- TCHAR *folder_path, process_path[MAX_PATH + 1];
- bool ret = false;
-
- if (SHGetKnownFolderPath(known_folder, KF_FLAG_DEFAULT, NULL, &folder_path) == S_OK) {
- if (PathCombine(process_path, folder_path, file)) {
- if (calculate_file_id(process_path, id))
- ret = true;
- }
- CoTaskMemFree(folder_path);
- }
- return ret;
-}
-
__declspec(dllexport) UINT __stdcall KillWireGuardProcesses(MSIHANDLE installer)
{
HANDLE snapshot, process;
PROCESSENTRY32 entry = { .dwSize = sizeof(PROCESSENTRY32) };
- TCHAR process_path[MAX_PATH + 1];
- DWORD process_path_len;
+ TCHAR process_path[MAX_PATH], executable[MAX_PATH];
+ DWORD process_path_len = _countof(process_path);
struct file_id file_ids[3], file_id;
size_t file_ids_len = 0;
bool is_com_initialized = SUCCEEDED(CoInitialize(NULL));
+ LSTATUS mret;
- if (calculate_known_file_id(&FOLDERID_System, TEXT("wg.exe"), &file_ids[file_ids_len]))
- ++file_ids_len;
- if (calculate_known_file_id(&FOLDERID_SystemX86, TEXT("wg.exe"), &file_ids[file_ids_len]))
+ mret = MsiGetProperty(installer, TEXT("CustomActionData"), process_path, &process_path_len);
+ if (mret != ERROR_SUCCESS) {
+ log_errorf(installer, LOG_LEVEL_WARN, mret, TEXT("MsiGetProperty(\"CustomActionData\") failed"));
+ goto out;
+ }
+ if (!process_path[0])
+ goto out;
+
+ log_messagef(installer, LOG_LEVEL_INFO, TEXT("Detecting running processes"));
+
+ if (PathCombine(executable, process_path, TEXT("wg.exe")) && calculate_file_id(executable, &file_ids[file_ids_len]))
++file_ids_len;
- if (calculate_known_file_id(&FOLDERID_ProgramFiles, TEXT("WireGuard\\wireguard.exe"), &file_ids[file_ids_len]))
+ if (PathCombine(executable, process_path, TEXT("wireguard.exe")) && calculate_file_id(executable, &file_ids[file_ids_len]))
++file_ids_len;
if (!file_ids_len)
goto out;
@@ -371,3 +408,147 @@ out:
CoUninitialize();
return ERROR_SUCCESS;
}
+
+static bool remove_directory_recursive(MSIHANDLE installer, TCHAR path[MAX_PATH], unsigned int max_depth)
+{
+ HANDLE find_handle;
+ WIN32_FIND_DATA find_data;
+ TCHAR *path_end;
+
+ if (!max_depth) {
+ log_messagef(installer, LOG_LEVEL_WARN, TEXT("Too many levels of nesting at \"%1\""), path);
+ return false;
+ }
+
+ path_end = path + _tcsnlen(path, MAX_PATH);
+ if (!PathAppend(path, TEXT("*.*"))) {
+ log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("PathAppend(\"%1\", \"*.*\") failed"), path);
+ return false;
+ }
+ find_handle = FindFirstFileEx(path, FindExInfoBasic, &find_data, FindExSearchNameMatch, NULL, 0);
+ if (find_handle == INVALID_HANDLE_VALUE) {
+ log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("FindFirstFileEx(\"%1\") failed"), path);
+ return false;
+ }
+ do {
+ if (find_data.cFileName[0] == TEXT('.') && (find_data.cFileName[1] == TEXT('\0') || (find_data.cFileName[1] == TEXT('.') && find_data.cFileName[2] == TEXT('\0'))))
+ continue;
+
+ path_end[0] = TEXT('\0');
+ if (!PathAppend(path, find_data.cFileName)) {
+ log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("PathAppend(\"%1\", \"%2\") failed"), path, find_data.cFileName);
+ continue;
+ }
+
+ if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ remove_directory_recursive(installer, path, max_depth - 1);
+ continue;
+ }
+
+ if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) && !SetFileAttributes(path, find_data.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY))
+ log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("SetFileAttributes(\"%1\") failed"), path);
+
+ if (DeleteFile(path))
+ log_messagef(installer, LOG_LEVEL_INFO, TEXT("Deleted \"%1\""), path);
+ else
+ log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("DeleteFile(\"%1\") failed"), path);
+ } while (FindNextFile(find_handle, &find_data));
+ FindClose(find_handle);
+
+ path_end[0] = TEXT('\0');
+ if (RemoveDirectory(path)) {
+ log_messagef(installer, LOG_LEVEL_INFO, TEXT("Removed \"%1\""), path);
+ return true;
+ } else {
+ log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("RemoveDirectory(\"%1\") failed"), path);
+ return false;
+ }
+}
+
+__declspec(dllexport) UINT __stdcall RemoveConfigFolder(MSIHANDLE installer)
+{
+ LSTATUS ret;
+ TCHAR path[MAX_PATH];
+ DWORD path_len = _countof(path);
+ bool is_com_initialized = SUCCEEDED(CoInitialize(NULL));
+
+ ret = MsiGetProperty(installer, TEXT("CustomActionData"), path, &path_len);
+ if (ret != ERROR_SUCCESS) {
+ log_errorf(installer, LOG_LEVEL_WARN, ret, TEXT("MsiGetProperty(\"CustomActionData\") failed"));
+ goto out;
+ }
+ if (!path[0] || !PathAppend(path, TEXT("Data")))
+ goto out;
+ remove_directory_recursive(installer, path, 10);
+ RegDeleteKey(HKEY_LOCAL_MACHINE, TEXT("Software\\WireGuard")); // Assumes no WOW.
+out:
+ if (is_com_initialized)
+ CoUninitialize();
+ return ERROR_SUCCESS;
+}
+
+__declspec(dllexport) UINT __stdcall RemoveAdapters(MSIHANDLE installer)
+{
+ UINT ret;
+ bool is_com_initialized = SUCCEEDED(CoInitialize(NULL));
+ TCHAR path[MAX_PATH];
+ DWORD path_len = _countof(path);
+ HANDLE pipe;
+ char buf[0x200];
+ DWORD offset = 0, size_read;
+ PROCESS_INFORMATION pi;
+ STARTUPINFO si = {
+ .cb = sizeof(STARTUPINFO),
+ .dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES,
+ .wShowWindow = SW_HIDE
+ };
+
+ ret = MsiGetProperty(installer, TEXT("CustomActionData"), path, &path_len);
+ if (ret != ERROR_SUCCESS) {
+ log_errorf(installer, LOG_LEVEL_WARN, ret, TEXT("MsiGetProperty(\"CustomActionData\") failed"));
+ goto out;
+ }
+ if (!path[0] || !PathAppend(path, TEXT("wireguard.exe")))
+ goto out;
+
+ if (!CreatePipe(&pipe, &si.hStdOutput, NULL, 0)) {
+ log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("CreatePipe failed"));
+ goto out;
+ }
+ if (!SetHandleInformation(si.hStdOutput, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) {
+ log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("SetHandleInformation failed"));
+ goto cleanup_pipe_w;
+ }
+ if (!CreateProcess(path, TEXT("wireguard /removedriver"), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
+ log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("Failed to create \"%1\" process"), path);
+ goto cleanup_pipe_w;
+ }
+ CloseHandle(si.hStdOutput);
+ buf[sizeof(buf) - 1] = '\0';
+ while (ReadFile(pipe, buf + offset, sizeof(buf) - offset - 1, &size_read, NULL)) {
+ char *nl;
+ buf[offset + size_read] = '\0';
+ nl = strchr(buf, '\n');
+ if (!nl) {
+ offset = size_read;
+ continue;
+ }
+ nl[0] = '\0';
+ log_messagef(installer, LOG_LEVEL_INFO, TEXT("%1!hs!"), buf);
+ offset = strlen(&nl[1]);
+ memmove(buf, &nl[1], offset);
+ }
+ WaitForSingleObject(pi.hProcess, INFINITE);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ goto cleanup_pipe_r;
+
+cleanup_pipe_w:
+ CloseHandle(si.hStdOutput);
+cleanup_pipe_r:
+ CloseHandle(pipe);
+out:
+ if (is_com_initialized)
+ CoUninitialize();
+ return ERROR_SUCCESS;
+}
diff --git a/installer/fetcher/.gitignore b/installer/fetcher/.gitignore
new file mode 100644
index 00000000..9e4c427d
--- /dev/null
+++ b/installer/fetcher/.gitignore
@@ -0,0 +1,6 @@
+*.ico
+*.o
+*.d
+*.exe
+*.pro
+*.pro.user
diff --git a/installer/fetcher/Makefile b/installer/fetcher/Makefile
new file mode 100644
index 00000000..a247adfe
--- /dev/null
+++ b/installer/fetcher/Makefile
@@ -0,0 +1,42 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2015-2022 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+
+CFLAGS ?= -Os
+DEPLOYMENT_HOST ?= winvm
+DEPLOYMENT_PATH ?= Desktop
+
+CFLAGS += -std=gnu11 -DWINVER=0x0601 -D_WIN32_WINNT=0x0601 -flto
+CFLAGS += -Wall -Wextra
+CFLAGS += -MMD -MP
+LDLIBS += -lkernel32 -lwinhttp -lntdll -lshlwapi -lmsi -lcomctl32 -luser32 -lshell32 -lwintrust -lbcrypt
+LDFLAGS += -s -flto -Wl,--dynamicbase -Wl,--nxcompat -Wl,--tsaware -mwindows
+LDFLAGS += -Wl,--major-os-version=6 -Wl,--minor-os-version=1 -Wl,--major-subsystem-version=6 -Wl,--minor-subsystem-version=1
+# The use of -Wl,/delayload: here implies we're using llvm-mingw
+LDFLAGS += -Wl,/delayload:winhttp.dll -Wl,/delayload:msi.dll -Wl,/delayload:wintrust.dll -Wl,/delayload:advapi32.dll -Wl,/delayload:shell32.dll -Wl,/delayload:shlwapi.dll -Wl,/delayload:gdi32.dll -Wl,/delayload:user32.dll -Wl,/delayload:comctl32.dll -Wl,/delayload:bcrypt.dll
+TARGET := wireguard-installer.exe
+CC := i686-w64-mingw32-clang
+WINDRES := i686-w64-mingw32-windres
+
+$(TARGET): $(sort $(patsubst %.c,%.o,$(wildcard *.c))) resources.o
+ $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
+
+%.ico: %.svg
+ convert -background none $< -define icon:auto-resize="64,32,16" -compress zip $@
+
+resources.o: resources.rc icon.ico manifest.xml
+ $(WINDRES) -O coff -c 65001 -i $< -o $@
+
+clean:
+ $(RM) $(TARGET) *.o *.d *.ico
+
+deploy: $(TARGET)
+ scp $< $(DEPLOYMENT_HOST):$(DEPLOYMENT_PATH)
+
+sign: deploy
+ ssh $(DEPLOYMENT_HOST) '"C:\Program Files (x86)\Windows Kits\10\App Certification Kit\signtool.exe"' sign /sha1 $(SIGNING_CERTIFICATE) /fd sha256 /tr $(TIMESTAMP_SERVER) /td sha256 /d '"WireGuard Installer"' '$(DEPLOYMENT_PATH)\$(TARGET)'
+ scp -T '$(DEPLOYMENT_HOST):$(DEPLOYMENT_PATH)\$(TARGET)' .
+
+.PHONY: clean deploy sign
+
+-include *.d
diff --git a/installer/fetcher/constants.h b/installer/fetcher/constants.h
new file mode 100644
index 00000000..96d88d0e
--- /dev/null
+++ b/installer/fetcher/constants.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2020-2022 Jason A. Donenfeld. All Rights Reserved.
+ */
+
+#ifndef _CONSTANTS_H
+#define _CONSTANTS_H
+
+#define release_public_key_base64 "RWRNqGKtBXftKTKPpBPGDMe8jHLnFQ0EdRy8Wg0apV6vTDFLAODD83G4"
+#define msi_arch_prefix "wireguard-%s-"
+#define msi_suffix ".msi"
+#define server "download.wireguard.com"
+#define port (443)
+#define latest_version_file "latest.sig"
+#define msi_path "/windows-client/"
+
+#endif
diff --git a/installer/fetcher/crypto.c b/installer/fetcher/crypto.c
new file mode 100644
index 00000000..33154efb
--- /dev/null
+++ b/installer/fetcher/crypto.c
@@ -0,0 +1,2252 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2022 Jason A. Donenfeld. All Rights Reserved.
+ * Copyright (c) 2020, Google Inc.
+ */
+
+#include "crypto.h"
+#include <stdint.h>
+#include <string.h>
+#include <winternl.h>
+#include <bcrypt.h>
+
+#if REG_DWORD == REG_DWORD_LITTLE_ENDIAN
+#define swap_le64(x) (x)
+#define swap_le32(x) (x)
+#elif REG_DWORD == REG_DWORD_BIG_ENDIAN
+#define swap_le64(x) __builtin_bswap64(x)
+#define swap_le32(x) __builtin_bswap32(x)
+#endif
+
+static void store_le64(uint8_t *dst, uint64_t src)
+{
+ src = swap_le64(src);
+ __builtin_memcpy(dst, &src, sizeof(src));
+}
+
+static uint64_t load_le64(const uint8_t *src)
+{
+ uint64_t dst;
+ __builtin_memcpy(&dst, src, sizeof(dst));
+ return swap_le64(dst);
+}
+
+static uint32_t load_le24(const uint8_t *in)
+{
+ uint32_t dst;
+ dst = (uint32_t)in[0];
+ dst |= ((uint32_t)in[1]) << 8;
+ dst |= ((uint32_t)in[2]) << 16;
+ return dst;
+}
+
+static uint32_t load_le32(const uint8_t *src)
+{
+ uint32_t dst;
+ __builtin_memcpy(&dst, src, sizeof(dst));
+ return swap_le32(dst);
+}
+
+static uint64_t ror64(uint64_t i, unsigned int s)
+{
+ return (i >> (s & 63)) | (i << ((-s) & 63));
+}
+
+static inline uint32_t value_barrier_u32(uint32_t a)
+{
+ __asm__("" : "+r"(a) : /* no inputs */);
+ return a;
+}
+
+static int memcmp_ct(const void *first, const void *second, size_t len)
+{
+ const uint8_t *a = first;
+ const uint8_t *b = second;
+ uint8_t diff = 0;
+
+ for (size_t i = 0; i < len; ++i) {
+ diff |= a[i] ^ b[i];
+ __asm__("" : "+r"(diff) : /* no inputs */);
+ }
+
+ return diff;
+}
+
+/*
+ * The function fiat_25519_addcarryx_u26 is an addition with carry.
+ * Postconditions:
+ * out1 = (arg1 + arg2 + arg3) mod 2^26
+ * out2 = ⌊(arg1 + arg2 + arg3) / 2^26⌋
+ *
+ * Input Bounds:
+ * arg1: [0x0 ~> 0x1]
+ * arg2: [0x0 ~> 0x3ffffff]
+ * arg3: [0x0 ~> 0x3ffffff]
+ * Output Bounds:
+ * out1: [0x0 ~> 0x3ffffff]
+ * out2: [0x0 ~> 0x1]
+ */
+static void fiat_25519_addcarryx_u26(uint32_t *out1, uint8_t *out2,
+ uint8_t arg1, uint32_t arg2, uint32_t arg3)
+{
+ uint32_t x1 = ((arg1 + arg2) + arg3);
+ uint32_t x2 = (x1 & UINT32_C(0x3ffffff));
+ uint8_t x3 = (uint8_t)(x1 >> 26);
+ *out1 = x2;
+ *out2 = x3;
+}
+
+/*
+ * The function fiat_25519_subborrowx_u26 is a subtraction with borrow.
+ * Postconditions:
+ * out1 = (-arg1 + arg2 + -arg3) mod 2^26
+ * out2 = -⌊(-arg1 + arg2 + -arg3) / 2^26⌋
+ *
+ * Input Bounds:
+ * arg1: [0x0 ~> 0x1]
+ * arg2: [0x0 ~> 0x3ffffff]
+ * arg3: [0x0 ~> 0x3ffffff]
+ * Output Bounds:
+ * out1: [0x0 ~> 0x3ffffff]
+ * out2: [0x0 ~> 0x1]
+ */
+static void fiat_25519_subborrowx_u26(uint32_t *out1, uint8_t *out2,
+ uint8_t arg1, uint32_t arg2,
+ uint32_t arg3)
+{
+ int32_t x1 = ((int32_t)(arg2 - arg1) - (int32_t)arg3);
+ int8_t x2 = (int8_t)(x1 >> 26);
+ uint32_t x3 = (x1 & UINT32_C(0x3ffffff));
+ *out1 = x3;
+ *out2 = (uint8_t)(0x0 - x2);
+}
+
+/*
+ * The function fiat_25519_addcarryx_u25 is an addition with carry.
+ * Postconditions:
+ * out1 = (arg1 + arg2 + arg3) mod 2^25
+ * out2 = ⌊(arg1 + arg2 + arg3) / 2^25⌋
+ *
+ * Input Bounds:
+ * arg1: [0x0 ~> 0x1]
+ * arg2: [0x0 ~> 0x1ffffff]
+ * arg3: [0x0 ~> 0x1ffffff]
+ * Output Bounds:
+ * out1: [0x0 ~> 0x1ffffff]
+ * out2: [0x0 ~> 0x1]
+ */
+static void fiat_25519_addcarryx_u25(uint32_t *out1, uint8_t *out2,
+ uint8_t arg1, uint32_t arg2, uint32_t arg3)
+{
+ uint32_t x1 = ((arg1 + arg2) + arg3);
+ uint32_t x2 = (x1 & UINT32_C(0x1ffffff));
+ uint8_t x3 = (uint8_t)(x1 >> 25);
+ *out1 = x2;
+ *out2 = x3;
+}
+
+/*
+ * The function fiat_25519_subborrowx_u25 is a subtraction with borrow.
+ * Postconditions:
+ * out1 = (-arg1 + arg2 + -arg3) mod 2^25
+ * out2 = -⌊(-arg1 + arg2 + -arg3) / 2^25⌋
+ *
+ * Input Bounds:
+ * arg1: [0x0 ~> 0x1]
+ * arg2: [0x0 ~> 0x1ffffff]
+ * arg3: [0x0 ~> 0x1ffffff]
+ * Output Bounds:
+ * out1: [0x0 ~> 0x1ffffff]
+ * out2: [0x0 ~> 0x1]
+ */
+static void fiat_25519_subborrowx_u25(uint32_t *out1, uint8_t *out2,
+ uint8_t arg1, uint32_t arg2,
+ uint32_t arg3)
+{
+ int32_t x1 = ((int32_t)(arg2 - arg1) - (int32_t)arg3);
+ int8_t x2 = (int8_t)(x1 >> 25);
+ uint32_t x3 = (x1 & UINT32_C(0x1ffffff));
+ *out1 = x3;
+ *out2 = (uint8_t)(0x0 - x2);
+}
+
+/*
+ * The function fiat_25519_cmovznz_u32 is a single-word conditional move.
+ * Postconditions:
+ * out1 = (if arg1 = 0 then arg2 else arg3)
+ *
+ * Input Bounds:
+ * arg1: [0x0 ~> 0x1]
+ * arg2: [0x0 ~> 0xffffffff]
+ * arg3: [0x0 ~> 0xffffffff]
+ * Output Bounds:
+ * out1: [0x0 ~> 0xffffffff]
+ */
+static void fiat_25519_cmovznz_u32(uint32_t *out1, uint8_t arg1, uint32_t arg2,
+ uint32_t arg3)
+{
+ uint8_t x1 = (!(!arg1));
+ uint32_t x2 = ((int8_t)(0x0 - x1) & UINT32_C(0xffffffff));
+ // Note this line has been patched from the synthesized code to add value
+ // barriers.
+ //
+ // Clang recognizes this pattern as a select. While it usually transforms it
+ // to a cmov, it sometimes further transforms it into a branch, which we do
+ // not want.
+ uint32_t x3 = ((value_barrier_u32(x2) & arg3) |
+ (value_barrier_u32(~x2) & arg2));
+ *out1 = x3;
+}
+
+/*
+ * The function fiat_25519_carry_mul multiplies two field elements and reduces the result.
+ * Postconditions:
+ * eval out1 mod m = (eval arg1 * eval arg2) mod m
+ *
+ * Input Bounds:
+ * arg1: [[0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999]]
+ * arg2: [[0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999]]
+ * Output Bounds:
+ * out1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]]
+ */
+static void fiat_25519_carry_mul(uint32_t out1[10], const uint32_t arg1[10],
+ const uint32_t arg2[10])
+{
+ uint64_t x1 = ((uint64_t)(arg1[9]) * ((arg2[9]) * UINT8_C(0x26)));
+ uint64_t x2 = ((uint64_t)(arg1[9]) * ((arg2[8]) * UINT8_C(0x13)));
+ uint64_t x3 = ((uint64_t)(arg1[9]) * ((arg2[7]) * UINT8_C(0x26)));
+ uint64_t x4 = ((uint64_t)(arg1[9]) * ((arg2[6]) * UINT8_C(0x13)));
+ uint64_t x5 = ((uint64_t)(arg1[9]) * ((arg2[5]) * UINT8_C(0x26)));
+ uint64_t x6 = ((uint64_t)(arg1[9]) * ((arg2[4]) * UINT8_C(0x13)));
+ uint64_t x7 = ((uint64_t)(arg1[9]) * ((arg2[3]) * UINT8_C(0x26)));
+ uint64_t x8 = ((uint64_t)(arg1[9]) * ((arg2[2]) * UINT8_C(0x13)));
+ uint64_t x9 = ((uint64_t)(arg1[9]) * ((arg2[1]) * UINT8_C(0x26)));
+ uint64_t x10 = ((uint64_t)(arg1[8]) * ((arg2[9]) * UINT8_C(0x13)));
+ uint64_t x11 = ((uint64_t)(arg1[8]) * ((arg2[8]) * UINT8_C(0x13)));
+ uint64_t x12 = ((uint64_t)(arg1[8]) * ((arg2[7]) * UINT8_C(0x13)));
+ uint64_t x13 = ((uint64_t)(arg1[8]) * ((arg2[6]) * UINT8_C(0x13)));
+ uint64_t x14 = ((uint64_t)(arg1[8]) * ((arg2[5]) * UINT8_C(0x13)));
+ uint64_t x15 = ((uint64_t)(arg1[8]) * ((arg2[4]) * UINT8_C(0x13)));
+ uint64_t x16 = ((uint64_t)(arg1[8]) * ((arg2[3]) * UINT8_C(0x13)));
+ uint64_t x17 = ((uint64_t)(arg1[8]) * ((arg2[2]) * UINT8_C(0x13)));
+ uint64_t x18 = ((uint64_t)(arg1[7]) * ((arg2[9]) * UINT8_C(0x26)));
+ uint64_t x19 = ((uint64_t)(arg1[7]) * ((arg2[8]) * UINT8_C(0x13)));
+ uint64_t x20 = ((uint64_t)(arg1[7]) * ((arg2[7]) * UINT8_C(0x26)));
+ uint64_t x21 = ((uint64_t)(arg1[7]) * ((arg2[6]) * UINT8_C(0x13)));
+ uint64_t x22 = ((uint64_t)(arg1[7]) * ((arg2[5]) * UINT8_C(0x26)));
+ uint64_t x23 = ((uint64_t)(arg1[7]) * ((arg2[4]) * UINT8_C(0x13)));
+ uint64_t x24 = ((uint64_t)(arg1[7]) * ((arg2[3]) * UINT8_C(0x26)));
+ uint64_t x25 = ((uint64_t)(arg1[6]) * ((arg2[9]) * UINT8_C(0x13)));
+ uint64_t x26 = ((uint64_t)(arg1[6]) * ((arg2[8]) * UINT8_C(0x13)));
+ uint64_t x27 = ((uint64_t)(arg1[6]) * ((arg2[7]) * UINT8_C(0x13)));
+ uint64_t x28 = ((uint64_t)(arg1[6]) * ((arg2[6]) * UINT8_C(0x13)));
+ uint64_t x29 = ((uint64_t)(arg1[6]) * ((arg2[5]) * UINT8_C(0x13)));
+ uint64_t x30 = ((uint64_t)(arg1[6]) * ((arg2[4]) * UINT8_C(0x13)));
+ uint64_t x31 = ((uint64_t)(arg1[5]) * ((arg2[9]) * UINT8_C(0x26)));
+ uint64_t x32 = ((uint64_t)(arg1[5]) * ((arg2[8]) * UINT8_C(0x13)));
+ uint64_t x33 = ((uint64_t)(arg1[5]) * ((arg2[7]) * UINT8_C(0x26)));
+ uint64_t x34 = ((uint64_t)(arg1[5]) * ((arg2[6]) * UINT8_C(0x13)));
+ uint64_t x35 = ((uint64_t)(arg1[5]) * ((arg2[5]) * UINT8_C(0x26)));
+ uint64_t x36 = ((uint64_t)(arg1[4]) * ((arg2[9]) * UINT8_C(0x13)));
+ uint64_t x37 = ((uint64_t)(arg1[4]) * ((arg2[8]) * UINT8_C(0x13)));
+ uint64_t x38 = ((uint64_t)(arg1[4]) * ((arg2[7]) * UINT8_C(0x13)));
+ uint64_t x39 = ((uint64_t)(arg1[4]) * ((arg2[6]) * UINT8_C(0x13)));
+ uint64_t x40 = ((uint64_t)(arg1[3]) * ((arg2[9]) * UINT8_C(0x26)));
+ uint64_t x41 = ((uint64_t)(arg1[3]) * ((arg2[8]) * UINT8_C(0x13)));
+ uint64_t x42 = ((uint64_t)(arg1[3]) * ((arg2[7]) * UINT8_C(0x26)));
+ uint64_t x43 = ((uint64_t)(arg1[2]) * ((arg2[9]) * UINT8_C(0x13)));
+ uint64_t x44 = ((uint64_t)(arg1[2]) * ((arg2[8]) * UINT8_C(0x13)));
+ uint64_t x45 = ((uint64_t)(arg1[1]) * ((arg2[9]) * UINT8_C(0x26)));
+ uint64_t x46 = ((uint64_t)(arg1[9]) * (arg2[0]));
+ uint64_t x47 = ((uint64_t)(arg1[8]) * (arg2[1]));
+ uint64_t x48 = ((uint64_t)(arg1[8]) * (arg2[0]));
+ uint64_t x49 = ((uint64_t)(arg1[7]) * (arg2[2]));
+ uint64_t x50 = ((uint64_t)(arg1[7]) * ((arg2[1]) * 0x2));
+ uint64_t x51 = ((uint64_t)(arg1[7]) * (arg2[0]));
+ uint64_t x52 = ((uint64_t)(arg1[6]) * (arg2[3]));
+ uint64_t x53 = ((uint64_t)(arg1[6]) * (arg2[2]));
+ uint64_t x54 = ((uint64_t)(arg1[6]) * (arg2[1]));
+ uint64_t x55 = ((uint64_t)(arg1[6]) * (arg2[0]));
+ uint64_t x56 = ((uint64_t)(arg1[5]) * (arg2[4]));
+ uint64_t x57 = ((uint64_t)(arg1[5]) * ((arg2[3]) * 0x2));
+ uint64_t x58 = ((uint64_t)(arg1[5]) * (arg2[2]));
+ uint64_t x59 = ((uint64_t)(arg1[5]) * ((arg2[1]) * 0x2));
+ uint64_t x60 = ((uint64_t)(arg1[5]) * (arg2[0]));
+ uint64_t x61 = ((uint64_t)(arg1[4]) * (arg2[5]));
+ uint64_t x62 = ((uint64_t)(arg1[4]) * (arg2[4]));
+ uint64_t x63 = ((uint64_t)(arg1[4]) * (arg2[3]));
+ uint64_t x64 = ((uint64_t)(arg1[4]) * (arg2[2]));
+ uint64_t x65 = ((uint64_t)(arg1[4]) * (arg2[1]));
+ uint64_t x66 = ((uint64_t)(arg1[4]) * (arg2[0]));
+ uint64_t x67 = ((uint64_t)(arg1[3]) * (arg2[6]));
+ uint64_t x68 = ((uint64_t)(arg1[3]) * ((arg2[5]) * 0x2));
+ uint64_t x69 = ((uint64_t)(arg1[3]) * (arg2[4]));
+ uint64_t x70 = ((uint64_t)(arg1[3]) * ((arg2[3]) * 0x2));
+ uint64_t x71 = ((uint64_t)(arg1[3]) * (arg2[2]));
+ uint64_t x72 = ((uint64_t)(arg1[3]) * ((arg2[1]) * 0x2));
+ uint64_t x73 = ((uint64_t)(arg1[3]) * (arg2[0]));
+ uint64_t x74 = ((uint64_t)(arg1[2]) * (arg2[7]));
+ uint64_t x75 = ((uint64_t)(arg1[2]) * (arg2[6]));
+ uint64_t x76 = ((uint64_t)(arg1[2]) * (arg2[5]));
+ uint64_t x77 = ((uint64_t)(arg1[2]) * (arg2[4]));
+ uint64_t x78 = ((uint64_t)(arg1[2]) * (arg2[3]));
+ uint64_t x79 = ((uint64_t)(arg1[2]) * (arg2[2]));
+ uint64_t x80 = ((uint64_t)(arg1[2]) * (arg2[1]));
+ uint64_t x81 = ((uint64_t)(arg1[2]) * (arg2[0]));
+ uint64_t x82 = ((uint64_t)(arg1[1]) * (arg2[8]));
+ uint64_t x83 = ((uint64_t)(arg1[1]) * ((arg2[7]) * 0x2));
+ uint64_t x84 = ((uint64_t)(arg1[1]) * (arg2[6]));
+ uint64_t x85 = ((uint64_t)(arg1[1]) * ((arg2[5]) * 0x2));
+ uint64_t x86 = ((uint64_t)(arg1[1]) * (arg2[4]));
+ uint64_t x87 = ((uint64_t)(arg1[1]) * ((arg2[3]) * 0x2));
+ uint64_t x88 = ((uint64_t)(arg1[1]) * (arg2[2]));
+ uint64_t x89 = ((uint64_t)(arg1[1]) * ((arg2[1]) * 0x2));
+ uint64_t x90 = ((uint64_t)(arg1[1]) * (arg2[0]));
+ uint64_t x91 = ((uint64_t)(arg1[0]) * (arg2[9]));
+ uint64_t x92 = ((uint64_t)(arg1[0]) * (arg2[8]));
+ uint64_t x93 = ((uint64_t)(arg1[0]) * (arg2[7]));
+ uint64_t x94 = ((uint64_t)(arg1[0]) * (arg2[6]));
+ uint64_t x95 = ((uint64_t)(arg1[0]) * (arg2[5]));
+ uint64_t x96 = ((uint64_t)(arg1[0]) * (arg2[4]));
+ uint64_t x97 = ((uint64_t)(arg1[0]) * (arg2[3]));
+ uint64_t x98 = ((uint64_t)(arg1[0]) * (arg2[2]));
+ uint64_t x99 = ((uint64_t)(arg1[0]) * (arg2[1]));
+ uint64_t x100 = ((uint64_t)(arg1[0]) * (arg2[0]));
+ uint64_t x101 =
+ (x100 +
+ (x45 +
+ (x44 + (x42 + (x39 + (x35 + (x30 + (x24 + (x17 + x9)))))))));
+ uint64_t x102 = (x101 >> 26);
+ uint32_t x103 = (uint32_t)(x101 & UINT32_C(0x3ffffff));
+ uint64_t x104 =
+ (x91 +
+ (x82 +
+ (x74 + (x67 + (x61 + (x56 + (x52 + (x49 + (x47 + x46)))))))));
+ uint64_t x105 =
+ (x92 +
+ (x83 +
+ (x75 + (x68 + (x62 + (x57 + (x53 + (x50 + (x48 + x1)))))))));
+ uint64_t x106 =
+ (x93 +
+ (x84 +
+ (x76 + (x69 + (x63 + (x58 + (x54 + (x51 + (x10 + x2)))))))));
+ uint64_t x107 =
+ (x94 +
+ (x85 +
+ (x77 + (x70 + (x64 + (x59 + (x55 + (x18 + (x11 + x3)))))))));
+ uint64_t x108 =
+ (x95 +
+ (x86 +
+ (x78 + (x71 + (x65 + (x60 + (x25 + (x19 + (x12 + x4)))))))));
+ uint64_t x109 =
+ (x96 +
+ (x87 +
+ (x79 + (x72 + (x66 + (x31 + (x26 + (x20 + (x13 + x5)))))))));
+ uint64_t x110 =
+ (x97 +
+ (x88 +
+ (x80 + (x73 + (x36 + (x32 + (x27 + (x21 + (x14 + x6)))))))));
+ uint64_t x111 =
+ (x98 +
+ (x89 +
+ (x81 + (x40 + (x37 + (x33 + (x28 + (x22 + (x15 + x7)))))))));
+ uint64_t x112 =
+ (x99 +
+ (x90 +
+ (x43 + (x41 + (x38 + (x34 + (x29 + (x23 + (x16 + x8)))))))));
+ uint64_t x113 = (x102 + x112);
+ uint64_t x114 = (x113 >> 25);
+ uint32_t x115 = (uint32_t)(x113 & UINT32_C(0x1ffffff));
+ uint64_t x116 = (x114 + x111);
+ uint64_t x117 = (x116 >> 26);
+ uint32_t x118 = (uint32_t)(x116 & UINT32_C(0x3ffffff));
+ uint64_t x119 = (x117 + x110);
+ uint64_t x120 = (x119 >> 25);
+ uint32_t x121 = (uint32_t)(x119 & UINT32_C(0x1ffffff));
+ uint64_t x122 = (x120 + x109);
+ uint64_t x123 = (x122 >> 26);
+ uint32_t x124 = (uint32_t)(x122 & UINT32_C(0x3ffffff));
+ uint64_t x125 = (x123 + x108);
+ uint64_t x126 = (x125 >> 25);
+ uint32_t x127 = (uint32_t)(x125 & UINT32_C(0x1ffffff));
+ uint64_t x128 = (x126 + x107);
+ uint64_t x129 = (x128 >> 26);
+ uint32_t x130 = (uint32_t)(x128 & UINT32_C(0x3ffffff));
+ uint64_t x131 = (x129 + x106);
+ uint64_t x132 = (x131 >> 25);
+ uint32_t x133 = (uint32_t)(x131 & UINT32_C(0x1ffffff));
+ uint64_t x134 = (x132 + x105);
+ uint64_t x135 = (x134 >> 26);
+ uint32_t x136 = (uint32_t)(x134 & UINT32_C(0x3ffffff));
+ uint64_t x137 = (x135 + x104);
+ uint64_t x138 = (x137 >> 25);
+ uint32_t x139 = (uint32_t)(x137 & UINT32_C(0x1ffffff));
+ uint64_t x140 = (x138 * UINT8_C(0x13));
+ uint64_t x141 = (x103 + x140);
+ uint32_t x142 = (uint32_t)(x141 >> 26);
+ uint32_t x143 = (uint32_t)(x141 & UINT32_C(0x3ffffff));
+ uint32_t x144 = (x142 + x115);
+ uint8_t x145 = (uint8_t)(x144 >> 25);
+ uint32_t x146 = (x144 & UINT32_C(0x1ffffff));
+ uint32_t x147 = (x145 + x118);
+ out1[0] = x143;
+ out1[1] = x146;
+ out1[2] = x147;
+ out1[3] = x121;
+ out1[4] = x124;
+ out1[5] = x127;
+ out1[6] = x130;
+ out1[7] = x133;
+ out1[8] = x136;
+ out1[9] = x139;
+}
+
+/*
+ * The function fiat_25519_carry_square squares a field element and reduces the result.
+ * Postconditions:
+ * eval out1 mod m = (eval arg1 * eval arg1) mod m
+ *
+ * Input Bounds:
+ * arg1: [[0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999]]
+ * Output Bounds:
+ * out1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]]
+ */
+static void fiat_25519_carry_square(uint32_t out1[10], const uint32_t arg1[10])
+{
+ uint32_t x1 = ((arg1[9]) * UINT8_C(0x13));
+ uint32_t x2 = (x1 * 0x2);
+ uint32_t x3 = ((arg1[9]) * 0x2);
+ uint32_t x4 = ((arg1[8]) * UINT8_C(0x13));
+ uint64_t x5 = ((uint64_t)x4 * 0x2);
+ uint32_t x6 = ((arg1[8]) * 0x2);
+ uint32_t x7 = ((arg1[7]) * UINT8_C(0x13));
+ uint32_t x8 = (x7 * 0x2);
+ uint32_t x9 = ((arg1[7]) * 0x2);
+ uint32_t x10 = ((arg1[6]) * UINT8_C(0x13));
+ uint64_t x11 = ((uint64_t)x10 * 0x2);
+ uint32_t x12 = ((arg1[6]) * 0x2);
+ uint32_t x13 = ((arg1[5]) * UINT8_C(0x13));
+ uint32_t x14 = ((arg1[5]) * 0x2);
+ uint32_t x15 = ((arg1[4]) * 0x2);
+ uint32_t x16 = ((arg1[3]) * 0x2);
+ uint32_t x17 = ((arg1[2]) * 0x2);
+ uint32_t x18 = ((arg1[1]) * 0x2);
+ uint64_t x19 = ((uint64_t)(arg1[9]) * (x1 * 0x2));
+ uint64_t x20 = ((uint64_t)(arg1[8]) * x2);
+ uint64_t x21 = ((uint64_t)(arg1[8]) * x4);
+ uint64_t x22 = ((arg1[7]) * ((uint64_t)x2 * 0x2));
+ uint64_t x23 = ((arg1[7]) * x5);
+ uint64_t x24 = ((uint64_t)(arg1[7]) * (x7 * 0x2));
+ uint64_t x25 = ((uint64_t)(arg1[6]) * x2);
+ uint64_t x26 = ((arg1[6]) * x5);
+ uint64_t x27 = ((uint64_t)(arg1[6]) * x8);
+ uint64_t x28 = ((uint64_t)(arg1[6]) * x10);
+ uint64_t x29 = ((arg1[5]) * ((uint64_t)x2 * 0x2));
+ uint64_t x30 = ((arg1[5]) * x5);
+ uint64_t x31 = ((arg1[5]) * ((uint64_t)x8 * 0x2));
+ uint64_t x32 = ((arg1[5]) * x11);
+ uint64_t x33 = ((uint64_t)(arg1[5]) * (x13 * 0x2));
+ uint64_t x34 = ((uint64_t)(arg1[4]) * x2);
+ uint64_t x35 = ((arg1[4]) * x5);
+ uint64_t x36 = ((uint64_t)(arg1[4]) * x8);
+ uint64_t x37 = ((arg1[4]) * x11);
+ uint64_t x38 = ((uint64_t)(arg1[4]) * x14);
+ uint64_t x39 = ((uint64_t)(arg1[4]) * (arg1[4]));
+ uint64_t x40 = ((arg1[3]) * ((uint64_t)x2 * 0x2));
+ uint64_t x41 = ((arg1[3]) * x5);
+ uint64_t x42 = ((arg1[3]) * ((uint64_t)x8 * 0x2));
+ uint64_t x43 = ((uint64_t)(arg1[3]) * x12);
+ uint64_t x44 = ((uint64_t)(arg1[3]) * (x14 * 0x2));
+ uint64_t x45 = ((uint64_t)(arg1[3]) * x15);
+ uint64_t x46 = ((uint64_t)(arg1[3]) * ((arg1[3]) * 0x2));
+ uint64_t x47 = ((uint64_t)(arg1[2]) * x2);
+ uint64_t x48 = ((arg1[2]) * x5);
+ uint64_t x49 = ((uint64_t)(arg1[2]) * x9);
+ uint64_t x50 = ((uint64_t)(arg1[2]) * x12);
+ uint64_t x51 = ((uint64_t)(arg1[2]) * x14);
+ uint64_t x52 = ((uint64_t)(arg1[2]) * x15);
+ uint64_t x53 = ((uint64_t)(arg1[2]) * x16);
+ uint64_t x54 = ((uint64_t)(arg1[2]) * (arg1[2]));
+ uint64_t x55 = ((arg1[1]) * ((uint64_t)x2 * 0x2));
+ uint64_t x56 = ((uint64_t)(arg1[1]) * x6);
+ uint64_t x57 = ((uint64_t)(arg1[1]) * (x9 * 0x2));
+ uint64_t x58 = ((uint64_t)(arg1[1]) * x12);
+ uint64_t x59 = ((uint64_t)(arg1[1]) * (x14 * 0x2));
+ uint64_t x60 = ((uint64_t)(arg1[1]) * x15);
+ uint64_t x61 = ((uint64_t)(arg1[1]) * (x16 * 0x2));
+ uint64_t x62 = ((uint64_t)(arg1[1]) * x17);
+ uint64_t x63 = ((uint64_t)(arg1[1]) * ((arg1[1]) * 0x2));
+ uint64_t x64 = ((uint64_t)(arg1[0]) * x3);
+ uint64_t x65 = ((uint64_t)(arg1[0]) * x6);
+ uint64_t x66 = ((uint64_t)(arg1[0]) * x9);
+ uint64_t x67 = ((uint64_t)(arg1[0]) * x12);
+ uint64_t x68 = ((uint64_t)(arg1[0]) * x14);
+ uint64_t x69 = ((uint64_t)(arg1[0]) * x15);
+ uint64_t x70 = ((uint64_t)(arg1[0]) * x16);
+ uint64_t x71 = ((uint64_t)(arg1[0]) * x17);
+ uint64_t x72 = ((uint64_t)(arg1[0]) * x18);
+ uint64_t x73 = ((uint64_t)(arg1[0]) * (arg1[0]));
+ uint64_t x74 = (x73 + (x55 + (x48 + (x42 + (x37 + x33)))));
+ uint64_t x75 = (x74 >> 26);
+ uint32_t x76 = (uint32_t)(x74 & UINT32_C(0x3ffffff));
+ uint64_t x77 = (x64 + (x56 + (x49 + (x43 + x38))));
+ uint64_t x78 = (x65 + (x57 + (x50 + (x44 + (x39 + x19)))));
+ uint64_t x79 = (x66 + (x58 + (x51 + (x45 + x20))));
+ uint64_t x80 = (x67 + (x59 + (x52 + (x46 + (x22 + x21)))));
+ uint64_t x81 = (x68 + (x60 + (x53 + (x25 + x23))));
+ uint64_t x82 = (x69 + (x61 + (x54 + (x29 + (x26 + x24)))));
+ uint64_t x83 = (x70 + (x62 + (x34 + (x30 + x27))));
+ uint64_t x84 = (x71 + (x63 + (x40 + (x35 + (x31 + x28)))));
+ uint64_t x85 = (x72 + (x47 + (x41 + (x36 + x32))));
+ uint64_t x86 = (x75 + x85);
+ uint64_t x87 = (x86 >> 25);
+ uint32_t x88 = (uint32_t)(x86 & UINT32_C(0x1ffffff));
+ uint64_t x89 = (x87 + x84);
+ uint64_t x90 = (x89 >> 26);
+ uint32_t x91 = (uint32_t)(x89 & UINT32_C(0x3ffffff));
+ uint64_t x92 = (x90 + x83);
+ uint64_t x93 = (x92 >> 25);
+ uint32_t x94 = (uint32_t)(x92 & UINT32_C(0x1ffffff));
+ uint64_t x95 = (x93 + x82);
+ uint64_t x96 = (x95 >> 26);
+ uint32_t x97 = (uint32_t)(x95 & UINT32_C(0x3ffffff));
+ uint64_t x98 = (x96 + x81);
+ uint64_t x99 = (x98 >> 25);
+ uint32_t x100 = (uint32_t)(x98 & UINT32_C(0x1ffffff));
+ uint64_t x101 = (x99 + x80);
+ uint64_t x102 = (x101 >> 26);
+ uint32_t x103 = (uint32_t)(x101 & UINT32_C(0x3ffffff));
+ uint64_t x104 = (x102 + x79);
+ uint64_t x105 = (x104 >> 25);
+ uint32_t x106 = (uint32_t)(x104 & UINT32_C(0x1ffffff));
+ uint64_t x107 = (x105 + x78);
+ uint64_t x108 = (x107 >> 26);
+ uint32_t x109 = (uint32_t)(x107 & UINT32_C(0x3ffffff));
+ uint64_t x110 = (x108 + x77);
+ uint64_t x111 = (x110 >> 25);
+ uint32_t x112 = (uint32_t)(x110 & UINT32_C(0x1ffffff));
+ uint64_t x113 = (x111 * UINT8_C(0x13));
+ uint64_t x114 = (x76 + x113);
+ uint32_t x115 = (uint32_t)(x114 >> 26);
+ uint32_t x116 = (uint32_t)(x114 & UINT32_C(0x3ffffff));
+ uint32_t x117 = (x115 + x88);
+ uint8_t x118 = (uint8_t)(x117 >> 25);
+ uint32_t x119 = (x117 & UINT32_C(0x1ffffff));
+ uint32_t x120 = (x118 + x91);
+ out1[0] = x116;
+ out1[1] = x119;
+ out1[2] = x120;
+ out1[3] = x94;
+ out1[4] = x97;
+ out1[5] = x100;
+ out1[6] = x103;
+ out1[7] = x106;
+ out1[8] = x109;
+ out1[9] = x112;
+}
+
+/*
+ * The function fiat_25519_carry reduces a field element.
+ * Postconditions:
+ * eval out1 mod m = eval arg1 mod m
+ *
+ * Input Bounds:
+ * arg1: [[0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999]]
+ * Output Bounds:
+ * out1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]]
+ */
+static void fiat_25519_carry(uint32_t out1[10], const uint32_t arg1[10])
+{
+ uint32_t x1 = (arg1[0]);
+ uint32_t x2 = ((x1 >> 26) + (arg1[1]));
+ uint32_t x3 = ((x2 >> 25) + (arg1[2]));
+ uint32_t x4 = ((x3 >> 26) + (arg1[3]));
+ uint32_t x5 = ((x4 >> 25) + (arg1[4]));
+ uint32_t x6 = ((x5 >> 26) + (arg1[5]));
+ uint32_t x7 = ((x6 >> 25) + (arg1[6]));
+ uint32_t x8 = ((x7 >> 26) + (arg1[7]));
+ uint32_t x9 = ((x8 >> 25) + (arg1[8]));
+ uint32_t x10 = ((x9 >> 26) + (arg1[9]));
+ uint32_t x11 =
+ ((x1 & UINT32_C(0x3ffffff)) + ((x10 >> 25) * UINT8_C(0x13)));
+ uint32_t x12 = ((uint8_t)(x11 >> 26) + (x2 & UINT32_C(0x1ffffff)));
+ uint32_t x13 = (x11 & UINT32_C(0x3ffffff));
+ uint32_t x14 = (x12 & UINT32_C(0x1ffffff));
+ uint32_t x15 = ((uint8_t)(x12 >> 25) + (x3 & UINT32_C(0x3ffffff)));
+ uint32_t x16 = (x4 & UINT32_C(0x1ffffff));
+ uint32_t x17 = (x5 & UINT32_C(0x3ffffff));
+ uint32_t x18 = (x6 & UINT32_C(0x1ffffff));
+ uint32_t x19 = (x7 & UINT32_C(0x3ffffff));
+ uint32_t x20 = (x8 & UINT32_C(0x1ffffff));
+ uint32_t x21 = (x9 & UINT32_C(0x3ffffff));
+ uint32_t x22 = (x10 & UINT32_C(0x1ffffff));
+ out1[0] = x13;
+ out1[1] = x14;
+ out1[2] = x15;
+ out1[3] = x16;
+ out1[4] = x17;
+ out1[5] = x18;
+ out1[6] = x19;
+ out1[7] = x20;
+ out1[8] = x21;
+ out1[9] = x22;
+}
+
+/*
+ * The function fiat_25519_add adds two field elements.
+ * Postconditions:
+ * eval out1 mod m = (eval arg1 + eval arg2) mod m
+ *
+ * Input Bounds:
+ * arg1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]]
+ * arg2: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]]
+ * Output Bounds:
+ * out1: [[0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999]]
+ */
+static void fiat_25519_add(uint32_t out1[10], const uint32_t arg1[10],
+ const uint32_t arg2[10])
+{
+ uint32_t x1 = ((arg1[0]) + (arg2[0]));
+ uint32_t x2 = ((arg1[1]) + (arg2[1]));
+ uint32_t x3 = ((arg1[2]) + (arg2[2]));
+ uint32_t x4 = ((arg1[3]) + (arg2[3]));
+ uint32_t x5 = ((arg1[4]) + (arg2[4]));
+ uint32_t x6 = ((arg1[5]) + (arg2[5]));
+ uint32_t x7 = ((arg1[6]) + (arg2[6]));
+ uint32_t x8 = ((arg1[7]) + (arg2[7]));
+ uint32_t x9 = ((arg1[8]) + (arg2[8]));
+ uint32_t x10 = ((arg1[9]) + (arg2[9]));
+ out1[0] = x1;
+ out1[1] = x2;
+ out1[2] = x3;
+ out1[3] = x4;
+ out1[4] = x5;
+ out1[5] = x6;
+ out1[6] = x7;
+ out1[7] = x8;
+ out1[8] = x9;
+ out1[9] = x10;
+}
+
+/*
+ * The function fiat_25519_sub subtracts two field elements.
+ * Postconditions:
+ * eval out1 mod m = (eval arg1 - eval arg2) mod m
+ *
+ * Input Bounds:
+ * arg1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]]
+ * arg2: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]]
+ * Output Bounds:
+ * out1: [[0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999]]
+ */
+static void fiat_25519_sub(uint32_t out1[10], const uint32_t arg1[10],
+ const uint32_t arg2[10])
+{
+ uint32_t x1 = ((UINT32_C(0x7ffffda) + (arg1[0])) - (arg2[0]));
+ uint32_t x2 = ((UINT32_C(0x3fffffe) + (arg1[1])) - (arg2[1]));
+ uint32_t x3 = ((UINT32_C(0x7fffffe) + (arg1[2])) - (arg2[2]));
+ uint32_t x4 = ((UINT32_C(0x3fffffe) + (arg1[3])) - (arg2[3]));
+ uint32_t x5 = ((UINT32_C(0x7fffffe) + (arg1[4])) - (arg2[4]));
+ uint32_t x6 = ((UINT32_C(0x3fffffe) + (arg1[5])) - (arg2[5]));
+ uint32_t x7 = ((UINT32_C(0x7fffffe) + (arg1[6])) - (arg2[6]));
+ uint32_t x8 = ((UINT32_C(0x3fffffe) + (arg1[7])) - (arg2[7]));
+ uint32_t x9 = ((UINT32_C(0x7fffffe) + (arg1[8])) - (arg2[8]));
+ uint32_t x10 = ((UINT32_C(0x3fffffe) + (arg1[9])) - (arg2[9]));
+ out1[0] = x1;
+ out1[1] = x2;
+ out1[2] = x3;
+ out1[3] = x4;
+ out1[4] = x5;
+ out1[5] = x6;
+ out1[6] = x7;
+ out1[7] = x8;
+ out1[8] = x9;
+ out1[9] = x10;
+}
+
+/*
+ * The function fiat_25519_opp negates a field element.
+ * Postconditions:
+ * eval out1 mod m = -eval arg1 mod m
+ *
+ * Input Bounds:
+ * arg1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]]
+ * Output Bounds:
+ * out1: [[0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999]]
+ */
+static void fiat_25519_opp(uint32_t out1[10], const uint32_t arg1[10])
+{
+ uint32_t x1 = (UINT32_C(0x7ffffda) - (arg1[0]));
+ uint32_t x2 = (UINT32_C(0x3fffffe) - (arg1[1]));
+ uint32_t x3 = (UINT32_C(0x7fffffe) - (arg1[2]));
+ uint32_t x4 = (UINT32_C(0x3fffffe) - (arg1[3]));
+ uint32_t x5 = (UINT32_C(0x7fffffe) - (arg1[4]));
+ uint32_t x6 = (UINT32_C(0x3fffffe) - (arg1[5]));
+ uint32_t x7 = (UINT32_C(0x7fffffe) - (arg1[6]));
+ uint32_t x8 = (UINT32_C(0x3fffffe) - (arg1[7]));
+ uint32_t x9 = (UINT32_C(0x7fffffe) - (arg1[8]));
+ uint32_t x10 = (UINT32_C(0x3fffffe) - (arg1[9]));
+ out1[0] = x1;
+ out1[1] = x2;
+ out1[2] = x3;
+ out1[3] = x4;
+ out1[4] = x5;
+ out1[5] = x6;
+ out1[6] = x7;
+ out1[7] = x8;
+ out1[8] = x9;
+ out1[9] = x10;
+}
+
+/*
+ * The function fiat_25519_to_bytes serializes a field element to bytes in little-endian order.
+ * Postconditions:
+ * out1 = map (λ x, ⌊((eval arg1 mod m) mod 2^(8 * (x + 1))) / 2^(8 * x)⌋) [0..31]
+ *
+ * Input Bounds:
+ * arg1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]]
+ * Output Bounds:
+ * out1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0x7f]]
+ */
+static void fiat_25519_to_bytes(uint8_t out1[32], const uint32_t arg1[10])
+{
+ uint32_t x1;
+ uint8_t x2;
+ fiat_25519_subborrowx_u26(&x1, &x2, 0x0, (arg1[0]),
+ UINT32_C(0x3ffffed));
+ uint32_t x3;
+ uint8_t x4;
+ fiat_25519_subborrowx_u25(&x3, &x4, x2, (arg1[1]), UINT32_C(0x1ffffff));
+ uint32_t x5;
+ uint8_t x6;
+ fiat_25519_subborrowx_u26(&x5, &x6, x4, (arg1[2]), UINT32_C(0x3ffffff));
+ uint32_t x7;
+ uint8_t x8;
+ fiat_25519_subborrowx_u25(&x7, &x8, x6, (arg1[3]), UINT32_C(0x1ffffff));
+ uint32_t x9;
+ uint8_t x10;
+ fiat_25519_subborrowx_u26(&x9, &x10, x8, (arg1[4]),
+ UINT32_C(0x3ffffff));
+ uint32_t x11;
+ uint8_t x12;
+ fiat_25519_subborrowx_u25(&x11, &x12, x10, (arg1[5]),
+ UINT32_C(0x1ffffff));
+ uint32_t x13;
+ uint8_t x14;
+ fiat_25519_subborrowx_u26(&x13, &x14, x12, (arg1[6]),
+ UINT32_C(0x3ffffff));
+ uint32_t x15;
+ uint8_t x16;
+ fiat_25519_subborrowx_u25(&x15, &x16, x14, (arg1[7]),
+ UINT32_C(0x1ffffff));
+ uint32_t x17;
+ uint8_t x18;
+ fiat_25519_subborrowx_u26(&x17, &x18, x16, (arg1[8]),
+ UINT32_C(0x3ffffff));
+ uint32_t x19;
+ uint8_t x20;
+ fiat_25519_subborrowx_u25(&x19, &x20, x18, (arg1[9]),
+ UINT32_C(0x1ffffff));
+ uint32_t x21;
+ fiat_25519_cmovznz_u32(&x21, x20, 0x0, UINT32_C(0xffffffff));
+ uint32_t x22;
+ uint8_t x23;
+ fiat_25519_addcarryx_u26(&x22, &x23, 0x0, x1,
+ (x21 & UINT32_C(0x3ffffed)));
+ uint32_t x24;
+ uint8_t x25;
+ fiat_25519_addcarryx_u25(&x24, &x25, x23, x3,
+ (x21 & UINT32_C(0x1ffffff)));
+ uint32_t x26;
+ uint8_t x27;
+ fiat_25519_addcarryx_u26(&x26, &x27, x25, x5,
+ (x21 & UINT32_C(0x3ffffff)));
+ uint32_t x28;
+ uint8_t x29;
+ fiat_25519_addcarryx_u25(&x28, &x29, x27, x7,
+ (x21 & UINT32_C(0x1ffffff)));
+ uint32_t x30;
+ uint8_t x31;
+ fiat_25519_addcarryx_u26(&x30, &x31, x29, x9,
+ (x21 & UINT32_C(0x3ffffff)));
+ uint32_t x32;
+ uint8_t x33;
+ fiat_25519_addcarryx_u25(&x32, &x33, x31, x11,
+ (x21 & UINT32_C(0x1ffffff)));
+ uint32_t x34;
+ uint8_t x35;
+ fiat_25519_addcarryx_u26(&x34, &x35, x33, x13,
+ (x21 & UINT32_C(0x3ffffff)));
+ uint32_t x36;
+ uint8_t x37;
+ fiat_25519_addcarryx_u25(&x36, &x37, x35, x15,
+ (x21 & UINT32_C(0x1ffffff)));
+ uint32_t x38;
+ uint8_t x39;
+ fiat_25519_addcarryx_u26(&x38, &x39, x37, x17,
+ (x21 & UINT32_C(0x3ffffff)));
+ uint32_t x40;
+ uint8_t x41;
+ fiat_25519_addcarryx_u25(&x40, &x41, x39, x19,
+ (x21 & UINT32_C(0x1ffffff)));
+ uint32_t x42 = (x40 << 6);
+ uint32_t x43 = (x38 << 4);
+ uint32_t x44 = (x36 << 3);
+ uint32_t x45 = (x34 * (uint32_t)0x2);
+ uint32_t x46 = (x30 << 6);
+ uint32_t x47 = (x28 << 5);
+ uint32_t x48 = (x26 << 3);
+ uint32_t x49 = (x24 << 2);
+ uint32_t x50 = (x22 >> 8);
+ uint8_t x51 = (uint8_t)(x22 & UINT8_C(0xff));
+ uint32_t x52 = (x50 >> 8);
+ uint8_t x53 = (uint8_t)(x50 & UINT8_C(0xff));
+ uint8_t x54 = (uint8_t)(x52 >> 8);
+ uint8_t x55 = (uint8_t)(x52 & UINT8_C(0xff));
+ uint32_t x56 = (x54 + x49);
+ uint32_t x57 = (x56 >> 8);
+ uint8_t x58 = (uint8_t)(x56 & UINT8_C(0xff));
+ uint32_t x59 = (x57 >> 8);
+ uint8_t x60 = (uint8_t)(x57 & UINT8_C(0xff));
+ uint8_t x61 = (uint8_t)(x59 >> 8);
+ uint8_t x62 = (uint8_t)(x59 & UINT8_C(0xff));
+ uint32_t x63 = (x61 + x48);
+ uint32_t x64 = (x63 >> 8);
+ uint8_t x65 = (uint8_t)(x63 & UINT8_C(0xff));
+ uint32_t x66 = (x64 >> 8);
+ uint8_t x67 = (uint8_t)(x64 & UINT8_C(0xff));
+ uint8_t x68 = (uint8_t)(x66 >> 8);
+ uint8_t x69 = (uint8_t)(x66 & UINT8_C(0xff));
+ uint32_t x70 = (x68 + x47);
+ uint32_t x71 = (x70 >> 8);
+ uint8_t x72 = (uint8_t)(x70 & UINT8_C(0xff));
+ uint32_t x73 = (x71 >> 8);
+ uint8_t x74 = (uint8_t)(x71 & UINT8_C(0xff));
+ uint8_t x75 = (uint8_t)(x73 >> 8);
+ uint8_t x76 = (uint8_t)(x73 & UINT8_C(0xff));
+ uint32_t x77 = (x75 + x46);
+ uint32_t x78 = (x77 >> 8);
+ uint8_t x79 = (uint8_t)(x77 & UINT8_C(0xff));
+ uint32_t x80 = (x78 >> 8);
+ uint8_t x81 = (uint8_t)(x78 & UINT8_C(0xff));
+ uint8_t x82 = (uint8_t)(x80 >> 8);
+ uint8_t x83 = (uint8_t)(x80 & UINT8_C(0xff));
+ uint8_t x84 = (uint8_t)(x82 & UINT8_C(0xff));
+ uint32_t x85 = (x32 >> 8);
+ uint8_t x86 = (uint8_t)(x32 & UINT8_C(0xff));
+ uint32_t x87 = (x85 >> 8);
+ uint8_t x88 = (uint8_t)(x85 & UINT8_C(0xff));
+ uint8_t x89 = (uint8_t)(x87 >> 8);
+ uint8_t x90 = (uint8_t)(x87 & UINT8_C(0xff));
+ uint32_t x91 = (x89 + x45);
+ uint32_t x92 = (x91 >> 8);
+ uint8_t x93 = (uint8_t)(x91 & UINT8_C(0xff));
+ uint32_t x94 = (x92 >> 8);
+ uint8_t x95 = (uint8_t)(x92 & UINT8_C(0xff));
+ uint8_t x96 = (uint8_t)(x94 >> 8);
+ uint8_t x97 = (uint8_t)(x94 & UINT8_C(0xff));
+ uint32_t x98 = (x96 + x44);
+ uint32_t x99 = (x98 >> 8);
+ uint8_t x100 = (uint8_t)(x98 & UINT8_C(0xff));
+ uint32_t x101 = (x99 >> 8);
+ uint8_t x102 = (uint8_t)(x99 & UINT8_C(0xff));
+ uint8_t x103 = (uint8_t)(x101 >> 8);
+ uint8_t x104 = (uint8_t)(x101 & UINT8_C(0xff));
+ uint32_t x105 = (x103 + x43);
+ uint32_t x106 = (x105 >> 8);
+ uint8_t x107 = (uint8_t)(x105 & UINT8_C(0xff));
+ uint32_t x108 = (x106 >> 8);
+ uint8_t x109 = (uint8_t)(x106 & UINT8_C(0xff));
+ uint8_t x110 = (uint8_t)(x108 >> 8);
+ uint8_t x111 = (uint8_t)(x108 & UINT8_C(0xff));
+ uint32_t x112 = (x110 + x42);
+ uint32_t x113 = (x112 >> 8);
+ uint8_t x114 = (uint8_t)(x112 & UINT8_C(0xff));
+ uint32_t x115 = (x113 >> 8);
+ uint8_t x116 = (uint8_t)(x113 & UINT8_C(0xff));
+ uint8_t x117 = (uint8_t)(x115 >> 8);
+ uint8_t x118 = (uint8_t)(x115 & UINT8_C(0xff));
+ out1[0] = x51;
+ out1[1] = x53;
+ out1[2] = x55;
+ out1[3] = x58;
+ out1[4] = x60;
+ out1[5] = x62;
+ out1[6] = x65;
+ out1[7] = x67;
+ out1[8] = x69;
+ out1[9] = x72;
+ out1[10] = x74;
+ out1[11] = x76;
+ out1[12] = x79;
+ out1[13] = x81;
+ out1[14] = x83;
+ out1[15] = x84;
+ out1[16] = x86;
+ out1[17] = x88;
+ out1[18] = x90;
+ out1[19] = x93;
+ out1[20] = x95;
+ out1[21] = x97;
+ out1[22] = x100;
+ out1[23] = x102;
+ out1[24] = x104;
+ out1[25] = x107;
+ out1[26] = x109;
+ out1[27] = x111;
+ out1[28] = x114;
+ out1[29] = x116;
+ out1[30] = x118;
+ out1[31] = x117;
+}
+
+/*
+ * The function fiat_25519_from_bytes deserializes a field element from bytes in little-endian order.
+ * Postconditions:
+ * eval out1 mod m = bytes_eval arg1 mod m
+ *
+ * Input Bounds:
+ * arg1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0x7f]]
+ * Output Bounds:
+ * out1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]]
+ */
+static void fiat_25519_from_bytes(uint32_t out1[10], const uint8_t arg1[32])
+{
+ uint32_t x1 = ((uint32_t)(arg1[31]) << 18);
+ uint32_t x2 = ((uint32_t)(arg1[30]) << 10);
+ uint32_t x3 = ((uint32_t)(arg1[29]) << 2);
+ uint32_t x4 = ((uint32_t)(arg1[28]) << 20);
+ uint32_t x5 = ((uint32_t)(arg1[27]) << 12);
+ uint32_t x6 = ((uint32_t)(arg1[26]) << 4);
+ uint32_t x7 = ((uint32_t)(arg1[25]) << 21);
+ uint32_t x8 = ((uint32_t)(arg1[24]) << 13);
+ uint32_t x9 = ((uint32_t)(arg1[23]) << 5);
+ uint32_t x10 = ((uint32_t)(arg1[22]) << 23);
+ uint32_t x11 = ((uint32_t)(arg1[21]) << 15);
+ uint32_t x12 = ((uint32_t)(arg1[20]) << 7);
+ uint32_t x13 = ((uint32_t)(arg1[19]) << 24);
+ uint32_t x14 = ((uint32_t)(arg1[18]) << 16);
+ uint32_t x15 = ((uint32_t)(arg1[17]) << 8);
+ uint8_t x16 = (arg1[16]);
+ uint32_t x17 = ((uint32_t)(arg1[15]) << 18);
+ uint32_t x18 = ((uint32_t)(arg1[14]) << 10);
+ uint32_t x19 = ((uint32_t)(arg1[13]) << 2);
+ uint32_t x20 = ((uint32_t)(arg1[12]) << 19);
+ uint32_t x21 = ((uint32_t)(arg1[11]) << 11);
+ uint32_t x22 = ((uint32_t)(arg1[10]) << 3);
+ uint32_t x23 = ((uint32_t)(arg1[9]) << 21);
+ uint32_t x24 = ((uint32_t)(arg1[8]) << 13);
+ uint32_t x25 = ((uint32_t)(arg1[7]) << 5);
+ uint32_t x26 = ((uint32_t)(arg1[6]) << 22);
+ uint32_t x27 = ((uint32_t)(arg1[5]) << 14);
+ uint32_t x28 = ((uint32_t)(arg1[4]) << 6);
+ uint32_t x29 = ((uint32_t)(arg1[3]) << 24);
+ uint32_t x30 = ((uint32_t)(arg1[2]) << 16);
+ uint32_t x31 = ((uint32_t)(arg1[1]) << 8);
+ uint8_t x32 = (arg1[0]);
+ uint32_t x33 = (x32 + (x31 + (x30 + x29)));
+ uint8_t x34 = (uint8_t)(x33 >> 26);
+ uint32_t x35 = (x33 & UINT32_C(0x3ffffff));
+ uint32_t x36 = (x3 + (x2 + x1));
+ uint32_t x37 = (x6 + (x5 + x4));
+ uint32_t x38 = (x9 + (x8 + x7));
+ uint32_t x39 = (x12 + (x11 + x10));
+ uint32_t x40 = (x16 + (x15 + (x14 + x13)));
+ uint32_t x41 = (x19 + (x18 + x17));
+ uint32_t x42 = (x22 + (x21 + x20));
+ uint32_t x43 = (x25 + (x24 + x23));
+ uint32_t x44 = (x28 + (x27 + x26));
+ uint32_t x45 = (x34 + x44);
+ uint8_t x46 = (uint8_t)(x45 >> 25);
+ uint32_t x47 = (x45 & UINT32_C(0x1ffffff));
+ uint32_t x48 = (x46 + x43);
+ uint8_t x49 = (uint8_t)(x48 >> 26);
+ uint32_t x50 = (x48 & UINT32_C(0x3ffffff));
+ uint32_t x51 = (x49 + x42);
+ uint8_t x52 = (uint8_t)(x51 >> 25);
+ uint32_t x53 = (x51 & UINT32_C(0x1ffffff));
+ uint32_t x54 = (x52 + x41);
+ uint32_t x55 = (x54 & UINT32_C(0x3ffffff));
+ uint8_t x56 = (uint8_t)(x40 >> 25);
+ uint32_t x57 = (x40 & UINT32_C(0x1ffffff));
+ uint32_t x58 = (x56 + x39);
+ uint8_t x59 = (uint8_t)(x58 >> 26);
+ uint32_t x60 = (x58 & UINT32_C(0x3ffffff));
+ uint32_t x61 = (x59 + x38);
+ uint8_t x62 = (uint8_t)(x61 >> 25);
+ uint32_t x63 = (x61 & UINT32_C(0x1ffffff));
+ uint32_t x64 = (x62 + x37);
+ uint8_t x65 = (uint8_t)(x64 >> 26);
+ uint32_t x66 = (x64 & UINT32_C(0x3ffffff));
+ uint32_t x67 = (x65 + x36);
+ out1[0] = x35;
+ out1[1] = x47;
+ out1[2] = x50;
+ out1[3] = x53;
+ out1[4] = x55;
+ out1[5] = x57;
+ out1[6] = x60;
+ out1[7] = x63;
+ out1[8] = x66;
+ out1[9] = x67;
+}
+
+// Definitions
+
+// fe means field element. Here the field is \Z/(2^255-19). An element t,
+// entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77
+// t[3]+2^102 t[4]+...+2^230 t[9].
+// fe limbs are bounded by 1.125*2^26,1.125*2^25,1.125*2^26,1.125*2^25,etc.
+// Multiplication and carrying produce fe from fe_loose.
+typedef struct fe {
+ uint32_t v[10];
+} fe;
+
+// fe_loose limbs are bounded by 3.375*2^26,3.375*2^25,3.375*2^26,3.375*2^25,etc.
+// Addition and subtraction produce fe_loose from (fe, fe).
+typedef struct fe_loose {
+ uint32_t v[10];
+} fe_loose;
+
+// ge means group element.
+//
+// Here the group is the set of pairs (x,y) of field elements (see fe.h)
+// satisfying -x^2 + y^2 = 1 + d x^2y^2
+// where d = -121665/121666.
+//
+// Representations:
+// ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z
+// ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
+// ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T
+// ge_precomp (Duif): (y+x,y-x,2dxy)
+
+typedef struct {
+ fe X;
+ fe Y;
+ fe Z;
+} ge_p2;
+
+typedef struct {
+ fe X;
+ fe Y;
+ fe Z;
+ fe T;
+} ge_p3;
+
+typedef struct {
+ fe_loose X;
+ fe_loose Y;
+ fe_loose Z;
+ fe_loose T;
+} ge_p1p1;
+
+typedef struct {
+ fe_loose yplusx;
+ fe_loose yminusx;
+ fe_loose xy2d;
+} ge_precomp;
+
+typedef struct {
+ fe_loose YplusX;
+ fe_loose YminusX;
+ fe_loose Z;
+ fe_loose T2d;
+} ge_cached;
+
+// Constants.
+
+static const fe d = { { 56195235, 13857412, 51736253, 6949390, 114729, 24766616,
+ 60832955, 30306712, 48412415, 21499315 } };
+
+static const fe sqrtm1 = { { 34513072, 25610706, 9377949, 3500415, 12389472,
+ 33281959, 41962654, 31548777, 326685, 11406482 } };
+
+static const fe d2 = { { 45281625, 27714825, 36363642, 13898781, 229458,
+ 15978800, 54557047, 27058993, 29715967, 9444199 } };
+
+// Bi[i] = (2*i+1)*B
+static const ge_precomp Bi[8] = {
+ {
+ { { 25967493, 19198397, 29566455, 3660896, 54414519, 4014786,
+ 27544626, 21800161, 61029707, 2047604
+
+ } },
+ { { 54563134, 934261, 64385954, 3049989, 66381436, 9406985,
+ 12720692, 5043384, 19500929, 18085054
+
+ } },
+ { { 58370664, 4489569, 9688441, 18769238, 10184608, 21191052,
+ 29287918, 11864899, 42594502, 29115885 } },
+ },
+ {
+ { { 15636272, 23865875, 24204772, 25642034, 616976, 16869170,
+ 27787599, 18782243, 28944399, 32004408 } },
+ { { 16568933, 4717097, 55552716, 32452109, 15682895, 21747389,
+ 16354576, 21778470, 7689661, 11199574 } },
+ { { 30464137, 27578307, 55329429, 17883566, 23220364, 15915852,
+ 7512774, 10017326, 49359771, 23634074 } },
+ },
+ {
+ { { 10861363, 11473154, 27284546, 1981175, 37044515, 12577860,
+ 32867885, 14515107, 51670560, 10819379 } },
+ { { 4708026, 6336745, 20377586, 9066809, 55836755, 6594695,
+ 41455196, 12483687, 54440373, 5581305 } },
+ { { 19563141, 16186464, 37722007, 4097518, 10237984, 29206317,
+ 28542349, 13850243, 43430843, 17738489 } },
+ },
+ {
+ { { 5153727, 9909285, 1723747, 30776558, 30523604, 5516873,
+ 19480852, 5230134, 43156425, 18378665 } },
+ { { 36839857, 30090922, 7665485, 10083793, 28475525, 1649722,
+ 20654025, 16520125, 30598449, 7715701 } },
+ { { 28881826, 14381568, 9657904, 3680757, 46927229, 7843315,
+ 35708204, 1370707, 29794553, 32145132 } },
+ },
+ {
+ { { 44589871, 26862249, 14201701, 24808930, 43598457, 8844725,
+ 18474211, 32192982, 54046167, 13821876 } },
+ { { 60653668, 25714560, 3374701, 28813570, 40010246, 22982724,
+ 31655027, 26342105, 18853321, 19333481 } },
+ { { 4566811, 20590564, 38133974, 21313742, 59506191, 30723862,
+ 58594505, 23123294, 2207752, 30344648 } },
+ },
+ {
+ { { 41954014, 29368610, 29681143, 7868801, 60254203, 24130566,
+ 54671499, 32891431, 35997400, 17421995 } },
+ { { 25576264, 30851218, 7349803, 21739588, 16472781, 9300885,
+ 3844789, 15725684, 171356, 6466918 } },
+ { { 23103977, 13316479, 9739013, 17404951, 817874, 18515490,
+ 8965338, 19466374, 36393951, 16193876 } },
+ },
+ {
+ { { 33587053, 3180712, 64714734, 14003686, 50205390, 17283591,
+ 17238397, 4729455, 49034351, 9256799 } },
+ { { 41926547, 29380300, 32336397, 5036987, 45872047, 11360616,
+ 22616405, 9761698, 47281666, 630304 } },
+ { { 53388152, 2639452, 42871404, 26147950, 9494426, 27780403,
+ 60554312, 17593437, 64659607, 19263131 } },
+ },
+ {
+ { { 63957664, 28508356, 9282713, 6866145, 35201802, 32691408,
+ 48168288, 15033783, 25105118, 25659556 } },
+ { { 42782475, 15950225, 35307649, 18961608, 55446126, 28463506,
+ 1573891, 30928545, 2198789, 17749813 } },
+ { { 64009494, 10324966, 64867251, 7453182, 61661885, 30818928,
+ 53296841, 17317989, 34647629, 21263748 } },
+ },
+};
+
+static void fe_frombytes_strict(fe *h, const uint8_t s[32])
+{
+ // |fiat_25519_from_bytes| requires the top-most bit be clear.
+ fiat_25519_from_bytes(h->v, s);
+}
+
+static void fe_frombytes(fe *h, const uint8_t s[32])
+{
+ uint8_t s_copy[32];
+ memcpy(s_copy, s, 32);
+ s_copy[31] &= 0x7f;
+ fe_frombytes_strict(h, s_copy);
+}
+
+static void fe_tobytes(uint8_t s[32], const fe *f)
+{
+ fiat_25519_to_bytes(s, f->v);
+}
+
+// h = 0
+static void fe_0(fe *h)
+{
+ memset(h, 0, sizeof(fe));
+}
+
+// h = 1
+static void fe_1(fe *h)
+{
+ memset(h, 0, sizeof(fe));
+ h->v[0] = 1;
+}
+
+// h = f + g
+// Can overlap h with f or g.
+static void fe_add(fe_loose *h, const fe *f, const fe *g)
+{
+ fiat_25519_add(h->v, f->v, g->v);
+}
+
+// h = f - g
+// Can overlap h with f or g.
+static void fe_sub(fe_loose *h, const fe *f, const fe *g)
+{
+ fiat_25519_sub(h->v, f->v, g->v);
+}
+
+static void fe_carry(fe *h, const fe_loose *f)
+{
+ fiat_25519_carry(h->v, f->v);
+}
+
+static void fe_mul_impl(uint32_t out[10], const uint32_t in1[10],
+ const uint32_t in2[10])
+{
+ fiat_25519_carry_mul(out, in1, in2);
+}
+
+static void fe_mul_ltt(fe_loose *h, const fe *f, const fe *g)
+{
+ fe_mul_impl(h->v, f->v, g->v);
+}
+
+static void fe_mul_ttt(fe *h, const fe *f, const fe *g)
+{
+ fe_mul_impl(h->v, f->v, g->v);
+}
+
+static void fe_mul_tlt(fe *h, const fe_loose *f, const fe *g)
+{
+ fe_mul_impl(h->v, f->v, g->v);
+}
+
+static void fe_mul_ttl(fe *h, const fe *f, const fe_loose *g)
+{
+ fe_mul_impl(h->v, f->v, g->v);
+}
+
+static void fe_mul_tll(fe *h, const fe_loose *f, const fe_loose *g)
+{
+ fe_mul_impl(h->v, f->v, g->v);
+}
+
+static void fe_sq_tl(fe *h, const fe_loose *f)
+{
+ fiat_25519_carry_square(h->v, f->v);
+}
+
+static void fe_sq_tt(fe *h, const fe *f)
+{
+ fiat_25519_carry_square(h->v, f->v);
+}
+
+// h = -f
+static void fe_neg(fe_loose *h, const fe *f)
+{
+ fiat_25519_opp(h->v, f->v);
+}
+
+// h = f
+static void fe_copy(fe *h, const fe *f)
+{
+ memmove(h, f, sizeof(fe));
+}
+
+static void fe_copy_lt(fe_loose *h, const fe *f)
+{
+ memmove(h, f, sizeof(fe));
+}
+
+static void fe_loose_invert(fe *out, const fe_loose *z)
+{
+ fe t0;
+ fe t1;
+ fe t2;
+ fe t3;
+ int i;
+
+ fe_sq_tl(&t0, z);
+ fe_sq_tt(&t1, &t0);
+ for (i = 1; i < 2; ++i) {
+ fe_sq_tt(&t1, &t1);
+ }
+ fe_mul_tlt(&t1, z, &t1);
+ fe_mul_ttt(&t0, &t0, &t1);
+ fe_sq_tt(&t2, &t0);
+ fe_mul_ttt(&t1, &t1, &t2);
+ fe_sq_tt(&t2, &t1);
+ for (i = 1; i < 5; ++i) {
+ fe_sq_tt(&t2, &t2);
+ }
+ fe_mul_ttt(&t1, &t2, &t1);
+ fe_sq_tt(&t2, &t1);
+ for (i = 1; i < 10; ++i) {
+ fe_sq_tt(&t2, &t2);
+ }
+ fe_mul_ttt(&t2, &t2, &t1);
+ fe_sq_tt(&t3, &t2);
+ for (i = 1; i < 20; ++i) {
+ fe_sq_tt(&t3, &t3);
+ }
+ fe_mul_ttt(&t2, &t3, &t2);
+ fe_sq_tt(&t2, &t2);
+ for (i = 1; i < 10; ++i) {
+ fe_sq_tt(&t2, &t2);
+ }
+ fe_mul_ttt(&t1, &t2, &t1);
+ fe_sq_tt(&t2, &t1);
+ for (i = 1; i < 50; ++i) {
+ fe_sq_tt(&t2, &t2);
+ }
+ fe_mul_ttt(&t2, &t2, &t1);
+ fe_sq_tt(&t3, &t2);
+ for (i = 1; i < 100; ++i) {
+ fe_sq_tt(&t3, &t3);
+ }
+ fe_mul_ttt(&t2, &t3, &t2);
+ fe_sq_tt(&t2, &t2);
+ for (i = 1; i < 50; ++i) {
+ fe_sq_tt(&t2, &t2);
+ }
+ fe_mul_ttt(&t1, &t2, &t1);
+ fe_sq_tt(&t1, &t1);
+ for (i = 1; i < 5; ++i) {
+ fe_sq_tt(&t1, &t1);
+ }
+ fe_mul_ttt(out, &t1, &t0);
+}
+
+static void fe_invert(fe *out, const fe *z)
+{
+ fe_loose l;
+ fe_copy_lt(&l, z);
+ fe_loose_invert(out, &l);
+}
+
+// return 0 if f == 0
+// return 1 if f != 0
+static int fe_isnonzero(const fe_loose *f)
+{
+ fe tight;
+ fe_carry(&tight, f);
+ uint8_t s[32];
+ fe_tobytes(s, &tight);
+
+ static const uint8_t zero[32] = { 0 };
+ return memcmp_ct(s, zero, sizeof(zero)) != 0;
+}
+
+// return 1 if f is in {1,3,5,...,q-2}
+// return 0 if f is in {0,2,4,...,q-1}
+static int fe_isnegative(const fe *f)
+{
+ uint8_t s[32];
+ fe_tobytes(s, f);
+ return s[0] & 1;
+}
+
+static void fe_sq2_tt(fe *h, const fe *f)
+{
+ // h = f^2
+ fe_sq_tt(h, f);
+
+ // h = h + h
+ fe_loose tmp;
+ fe_add(&tmp, h, h);
+ fe_carry(h, &tmp);
+}
+
+static void fe_pow22523(fe *out, const fe *z)
+{
+ fe t0;
+ fe t1;
+ fe t2;
+ int i;
+
+ fe_sq_tt(&t0, z);
+ fe_sq_tt(&t1, &t0);
+ for (i = 1; i < 2; ++i) {
+ fe_sq_tt(&t1, &t1);
+ }
+ fe_mul_ttt(&t1, z, &t1);
+ fe_mul_ttt(&t0, &t0, &t1);
+ fe_sq_tt(&t0, &t0);
+ fe_mul_ttt(&t0, &t1, &t0);
+ fe_sq_tt(&t1, &t0);
+ for (i = 1; i < 5; ++i) {
+ fe_sq_tt(&t1, &t1);
+ }
+ fe_mul_ttt(&t0, &t1, &t0);
+ fe_sq_tt(&t1, &t0);
+ for (i = 1; i < 10; ++i) {
+ fe_sq_tt(&t1, &t1);
+ }
+ fe_mul_ttt(&t1, &t1, &t0);
+ fe_sq_tt(&t2, &t1);
+ for (i = 1; i < 20; ++i) {
+ fe_sq_tt(&t2, &t2);
+ }
+ fe_mul_ttt(&t1, &t2, &t1);
+ fe_sq_tt(&t1, &t1);
+ for (i = 1; i < 10; ++i) {
+ fe_sq_tt(&t1, &t1);
+ }
+ fe_mul_ttt(&t0, &t1, &t0);
+ fe_sq_tt(&t1, &t0);
+ for (i = 1; i < 50; ++i) {
+ fe_sq_tt(&t1, &t1);
+ }
+ fe_mul_ttt(&t1, &t1, &t0);
+ fe_sq_tt(&t2, &t1);
+ for (i = 1; i < 100; ++i) {
+ fe_sq_tt(&t2, &t2);
+ }
+ fe_mul_ttt(&t1, &t2, &t1);
+ fe_sq_tt(&t1, &t1);
+ for (i = 1; i < 50; ++i) {
+ fe_sq_tt(&t1, &t1);
+ }
+ fe_mul_ttt(&t0, &t1, &t0);
+ fe_sq_tt(&t0, &t0);
+ for (i = 1; i < 2; ++i) {
+ fe_sq_tt(&t0, &t0);
+ }
+ fe_mul_ttt(out, &t0, z);
+}
+
+static void x25519_ge_tobytes(uint8_t s[32], const ge_p2 *h)
+{
+ fe recip;
+ fe x;
+ fe y;
+
+ fe_invert(&recip, &h->Z);
+ fe_mul_ttt(&x, &h->X, &recip);
+ fe_mul_ttt(&y, &h->Y, &recip);
+ fe_tobytes(s, &y);
+ s[31] ^= fe_isnegative(&x) << 7;
+}
+
+static int x25519_ge_frombytes_vartime(ge_p3 *h, const uint8_t s[32])
+{
+ fe u;
+ fe_loose v;
+ fe v3;
+ fe vxx;
+ fe_loose check;
+
+ fe_frombytes(&h->Y, s);
+ fe_1(&h->Z);
+ fe_sq_tt(&v3, &h->Y);
+ fe_mul_ttt(&vxx, &v3, &d);
+ fe_sub(&v, &v3, &h->Z); // u = y^2-1
+ fe_carry(&u, &v);
+ fe_add(&v, &vxx, &h->Z); // v = dy^2+1
+
+ fe_sq_tl(&v3, &v);
+ fe_mul_ttl(&v3, &v3, &v); // v3 = v^3
+ fe_sq_tt(&h->X, &v3);
+ fe_mul_ttl(&h->X, &h->X, &v);
+ fe_mul_ttt(&h->X, &h->X, &u); // x = uv^7
+
+ fe_pow22523(&h->X, &h->X); // x = (uv^7)^((q-5)/8)
+ fe_mul_ttt(&h->X, &h->X, &v3);
+ fe_mul_ttt(&h->X, &h->X, &u); // x = uv^3(uv^7)^((q-5)/8)
+
+ fe_sq_tt(&vxx, &h->X);
+ fe_mul_ttl(&vxx, &vxx, &v);
+ fe_sub(&check, &vxx, &u);
+ if (fe_isnonzero(&check)) {
+ fe_add(&check, &vxx, &u);
+ if (fe_isnonzero(&check)) {
+ return 0;
+ }
+ fe_mul_ttt(&h->X, &h->X, &sqrtm1);
+ }
+
+ if (fe_isnegative(&h->X) != (s[31] >> 7)) {
+ fe_loose t;
+ fe_neg(&t, &h->X);
+ fe_carry(&h->X, &t);
+ }
+
+ fe_mul_ttt(&h->T, &h->X, &h->Y);
+ return 1;
+}
+
+static void ge_p2_0(ge_p2 *h)
+{
+ fe_0(&h->X);
+ fe_1(&h->Y);
+ fe_1(&h->Z);
+}
+
+// r = p
+static void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p)
+{
+ fe_copy(&r->X, &p->X);
+ fe_copy(&r->Y, &p->Y);
+ fe_copy(&r->Z, &p->Z);
+}
+
+// r = p
+static void x25519_ge_p3_to_cached(ge_cached *r, const ge_p3 *p)
+{
+ fe_add(&r->YplusX, &p->Y, &p->X);
+ fe_sub(&r->YminusX, &p->Y, &p->X);
+ fe_copy_lt(&r->Z, &p->Z);
+ fe_mul_ltt(&r->T2d, &p->T, &d2);
+}
+
+// r = p
+static void x25519_ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p)
+{
+ fe_mul_tll(&r->X, &p->X, &p->T);
+ fe_mul_tll(&r->Y, &p->Y, &p->Z);
+ fe_mul_tll(&r->Z, &p->Z, &p->T);
+}
+
+// r = p
+static void x25519_ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p)
+{
+ fe_mul_tll(&r->X, &p->X, &p->T);
+ fe_mul_tll(&r->Y, &p->Y, &p->Z);
+ fe_mul_tll(&r->Z, &p->Z, &p->T);
+ fe_mul_tll(&r->T, &p->X, &p->Y);
+}
+
+// r = 2 * p
+static void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p)
+{
+ fe trX, trZ, trT;
+ fe t0;
+
+ fe_sq_tt(&trX, &p->X);
+ fe_sq_tt(&trZ, &p->Y);
+ fe_sq2_tt(&trT, &p->Z);
+ fe_add(&r->Y, &p->X, &p->Y);
+ fe_sq_tl(&t0, &r->Y);
+
+ fe_add(&r->Y, &trZ, &trX);
+ fe_sub(&r->Z, &trZ, &trX);
+ fe_carry(&trZ, &r->Y);
+ fe_sub(&r->X, &t0, &trZ);
+ fe_carry(&trZ, &r->Z);
+ fe_sub(&r->T, &trT, &trZ);
+}
+
+// r = 2 * p
+static void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p)
+{
+ ge_p2 q;
+ ge_p3_to_p2(&q, p);
+ ge_p2_dbl(r, &q);
+}
+
+// r = p + q
+static void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q)
+{
+ fe trY, trZ, trT;
+
+ fe_add(&r->X, &p->Y, &p->X);
+ fe_sub(&r->Y, &p->Y, &p->X);
+ fe_mul_tll(&trZ, &r->X, &q->yplusx);
+ fe_mul_tll(&trY, &r->Y, &q->yminusx);
+ fe_mul_tlt(&trT, &q->xy2d, &p->T);
+ fe_add(&r->T, &p->Z, &p->Z);
+ fe_sub(&r->X, &trZ, &trY);
+ fe_add(&r->Y, &trZ, &trY);
+ fe_carry(&trZ, &r->T);
+ fe_add(&r->Z, &trZ, &trT);
+ fe_sub(&r->T, &trZ, &trT);
+}
+
+// r = p - q
+static void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q)
+{
+ fe trY, trZ, trT;
+
+ fe_add(&r->X, &p->Y, &p->X);
+ fe_sub(&r->Y, &p->Y, &p->X);
+ fe_mul_tll(&trZ, &r->X, &q->yminusx);
+ fe_mul_tll(&trY, &r->Y, &q->yplusx);
+ fe_mul_tlt(&trT, &q->xy2d, &p->T);
+ fe_add(&r->T, &p->Z, &p->Z);
+ fe_sub(&r->X, &trZ, &trY);
+ fe_add(&r->Y, &trZ, &trY);
+ fe_carry(&trZ, &r->T);
+ fe_sub(&r->Z, &trZ, &trT);
+ fe_add(&r->T, &trZ, &trT);
+}
+
+// r = p + q
+static void x25519_ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q)
+{
+ fe trX, trY, trZ, trT;
+
+ fe_add(&r->X, &p->Y, &p->X);
+ fe_sub(&r->Y, &p->Y, &p->X);
+ fe_mul_tll(&trZ, &r->X, &q->YplusX);
+ fe_mul_tll(&trY, &r->Y, &q->YminusX);
+ fe_mul_tlt(&trT, &q->T2d, &p->T);
+ fe_mul_ttl(&trX, &p->Z, &q->Z);
+ fe_add(&r->T, &trX, &trX);
+ fe_sub(&r->X, &trZ, &trY);
+ fe_add(&r->Y, &trZ, &trY);
+ fe_carry(&trZ, &r->T);
+ fe_add(&r->Z, &trZ, &trT);
+ fe_sub(&r->T, &trZ, &trT);
+}
+
+// r = p - q
+static void x25519_ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q)
+{
+ fe trX, trY, trZ, trT;
+
+ fe_add(&r->X, &p->Y, &p->X);
+ fe_sub(&r->Y, &p->Y, &p->X);
+ fe_mul_tll(&trZ, &r->X, &q->YminusX);
+ fe_mul_tll(&trY, &r->Y, &q->YplusX);
+ fe_mul_tlt(&trT, &q->T2d, &p->T);
+ fe_mul_ttl(&trX, &p->Z, &q->Z);
+ fe_add(&r->T, &trX, &trX);
+ fe_sub(&r->X, &trZ, &trY);
+ fe_add(&r->Y, &trZ, &trY);
+ fe_carry(&trZ, &r->T);
+ fe_sub(&r->Z, &trZ, &trT);
+ fe_add(&r->T, &trZ, &trT);
+}
+
+static void slide(signed char *r, const uint8_t *a)
+{
+ int i;
+ int b;
+ int k;
+
+ for (i = 0; i < 256; ++i) {
+ r[i] = 1 & (a[i >> 3] >> (i & 7));
+ }
+
+ for (i = 0; i < 256; ++i) {
+ if (r[i]) {
+ for (b = 1; b <= 6 && i + b < 256; ++b) {
+ if (r[i + b]) {
+ if (r[i] + (r[i + b] << b) <= 15) {
+ r[i] += r[i + b] << b;
+ r[i + b] = 0;
+ } else if (r[i] - (r[i + b] << b) >=
+ -15) {
+ r[i] -= r[i + b] << b;
+ for (k = i + b; k < 256; ++k) {
+ if (!r[k]) {
+ r[k] = 1;
+ break;
+ }
+ r[k] = 0;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+// r = a * A + b * B
+// where a = a[0]+256*a[1]+...+256^31 a[31].
+// and b = b[0]+256*b[1]+...+256^31 b[31].
+// B is the Ed25519 base point (x,4/5) with x positive.
+static void ge_double_scalarmult_vartime(ge_p2 *r, const uint8_t *a,
+ const ge_p3 *A, const uint8_t *b)
+{
+ signed char aslide[256];
+ signed char bslide[256];
+ ge_cached Ai[8]; // A,3A,5A,7A,9A,11A,13A,15A
+ ge_p1p1 t;
+ ge_p3 u;
+ ge_p3 A2;
+ int i;
+
+ slide(aslide, a);
+ slide(bslide, b);
+
+ x25519_ge_p3_to_cached(&Ai[0], A);
+ ge_p3_dbl(&t, A);
+ x25519_ge_p1p1_to_p3(&A2, &t);
+ x25519_ge_add(&t, &A2, &Ai[0]);
+ x25519_ge_p1p1_to_p3(&u, &t);
+ x25519_ge_p3_to_cached(&Ai[1], &u);
+ x25519_ge_add(&t, &A2, &Ai[1]);
+ x25519_ge_p1p1_to_p3(&u, &t);
+ x25519_ge_p3_to_cached(&Ai[2], &u);
+ x25519_ge_add(&t, &A2, &Ai[2]);
+ x25519_ge_p1p1_to_p3(&u, &t);
+ x25519_ge_p3_to_cached(&Ai[3], &u);
+ x25519_ge_add(&t, &A2, &Ai[3]);
+ x25519_ge_p1p1_to_p3(&u, &t);
+ x25519_ge_p3_to_cached(&Ai[4], &u);
+ x25519_ge_add(&t, &A2, &Ai[4]);
+ x25519_ge_p1p1_to_p3(&u, &t);
+ x25519_ge_p3_to_cached(&Ai[5], &u);
+ x25519_ge_add(&t, &A2, &Ai[5]);
+ x25519_ge_p1p1_to_p3(&u, &t);
+ x25519_ge_p3_to_cached(&Ai[6], &u);
+ x25519_ge_add(&t, &A2, &Ai[6]);
+ x25519_ge_p1p1_to_p3(&u, &t);
+ x25519_ge_p3_to_cached(&Ai[7], &u);
+
+ ge_p2_0(r);
+
+ for (i = 255; i >= 0; --i) {
+ if (aslide[i] || bslide[i]) {
+ break;
+ }
+ }
+
+ for (; i >= 0; --i) {
+ ge_p2_dbl(&t, r);
+
+ if (aslide[i] > 0) {
+ x25519_ge_p1p1_to_p3(&u, &t);
+ x25519_ge_add(&t, &u, &Ai[aslide[i] / 2]);
+ } else if (aslide[i] < 0) {
+ x25519_ge_p1p1_to_p3(&u, &t);
+ x25519_ge_sub(&t, &u, &Ai[(-aslide[i]) / 2]);
+ }
+
+ if (bslide[i] > 0) {
+ x25519_ge_p1p1_to_p3(&u, &t);
+ ge_madd(&t, &u, &Bi[bslide[i] / 2]);
+ } else if (bslide[i] < 0) {
+ x25519_ge_p1p1_to_p3(&u, &t);
+ ge_msub(&t, &u, &Bi[(-bslide[i]) / 2]);
+ }
+
+ x25519_ge_p1p1_to_p2(r, &t);
+ }
+}
+
+// int64_lshift21 returns |a << 21| but is defined when shifting bits into the
+// sign bit. This works around a language flaw in C.
+static inline int64_t int64_lshift21(int64_t a)
+{
+ return (int64_t)((uint64_t)a << 21);
+}
+
+// The set of scalars is \Z/l
+// where l = 2^252 + 27742317777372353535851937790883648493.
+
+// Input:
+// s[0]+256*s[1]+...+256^63*s[63] = s
+//
+// Output:
+// s[0]+256*s[1]+...+256^31*s[31] = s mod l
+// where l = 2^252 + 27742317777372353535851937790883648493.
+// Overwrites s in place.
+static void x25519_sc_reduce(uint8_t s[64])
+{
+ int64_t s0 = 2097151 & load_le24(s);
+ int64_t s1 = 2097151 & (load_le32(s + 2) >> 5);
+ int64_t s2 = 2097151 & (load_le24(s + 5) >> 2);
+ int64_t s3 = 2097151 & (load_le32(s + 7) >> 7);
+ int64_t s4 = 2097151 & (load_le32(s + 10) >> 4);
+ int64_t s5 = 2097151 & (load_le24(s + 13) >> 1);
+ int64_t s6 = 2097151 & (load_le32(s + 15) >> 6);
+ int64_t s7 = 2097151 & (load_le24(s + 18) >> 3);
+ int64_t s8 = 2097151 & load_le24(s + 21);
+ int64_t s9 = 2097151 & (load_le32(s + 23) >> 5);
+ int64_t s10 = 2097151 & (load_le24(s + 26) >> 2);
+ int64_t s11 = 2097151 & (load_le32(s + 28) >> 7);
+ int64_t s12 = 2097151 & (load_le32(s + 31) >> 4);
+ int64_t s13 = 2097151 & (load_le24(s + 34) >> 1);
+ int64_t s14 = 2097151 & (load_le32(s + 36) >> 6);
+ int64_t s15 = 2097151 & (load_le24(s + 39) >> 3);
+ int64_t s16 = 2097151 & load_le24(s + 42);
+ int64_t s17 = 2097151 & (load_le32(s + 44) >> 5);
+ int64_t s18 = 2097151 & (load_le24(s + 47) >> 2);
+ int64_t s19 = 2097151 & (load_le32(s + 49) >> 7);
+ int64_t s20 = 2097151 & (load_le32(s + 52) >> 4);
+ int64_t s21 = 2097151 & (load_le24(s + 55) >> 1);
+ int64_t s22 = 2097151 & (load_le32(s + 57) >> 6);
+ int64_t s23 = (load_le32(s + 60) >> 3);
+ int64_t carry0;
+ int64_t carry1;
+ int64_t carry2;
+ int64_t carry3;
+ int64_t carry4;
+ int64_t carry5;
+ int64_t carry6;
+ int64_t carry7;
+ int64_t carry8;
+ int64_t carry9;
+ int64_t carry10;
+ int64_t carry11;
+ int64_t carry12;
+ int64_t carry13;
+ int64_t carry14;
+ int64_t carry15;
+ int64_t carry16;
+
+ s11 += s23 * 666643;
+ s12 += s23 * 470296;
+ s13 += s23 * 654183;
+ s14 -= s23 * 997805;
+ s15 += s23 * 136657;
+ s16 -= s23 * 683901;
+ s23 = 0;
+
+ s10 += s22 * 666643;
+ s11 += s22 * 470296;
+ s12 += s22 * 654183;
+ s13 -= s22 * 997805;
+ s14 += s22 * 136657;
+ s15 -= s22 * 683901;
+ s22 = 0;
+
+ s9 += s21 * 666643;
+ s10 += s21 * 470296;
+ s11 += s21 * 654183;
+ s12 -= s21 * 997805;
+ s13 += s21 * 136657;
+ s14 -= s21 * 683901;
+ s21 = 0;
+
+ s8 += s20 * 666643;
+ s9 += s20 * 470296;
+ s10 += s20 * 654183;
+ s11 -= s20 * 997805;
+ s12 += s20 * 136657;
+ s13 -= s20 * 683901;
+ s20 = 0;
+
+ s7 += s19 * 666643;
+ s8 += s19 * 470296;
+ s9 += s19 * 654183;
+ s10 -= s19 * 997805;
+ s11 += s19 * 136657;
+ s12 -= s19 * 683901;
+ s19 = 0;
+
+ s6 += s18 * 666643;
+ s7 += s18 * 470296;
+ s8 += s18 * 654183;
+ s9 -= s18 * 997805;
+ s10 += s18 * 136657;
+ s11 -= s18 * 683901;
+ s18 = 0;
+
+ carry6 = (s6 + (1 << 20)) >> 21;
+ s7 += carry6;
+ s6 -= int64_lshift21(carry6);
+ carry8 = (s8 + (1 << 20)) >> 21;
+ s9 += carry8;
+ s8 -= int64_lshift21(carry8);
+ carry10 = (s10 + (1 << 20)) >> 21;
+ s11 += carry10;
+ s10 -= int64_lshift21(carry10);
+ carry12 = (s12 + (1 << 20)) >> 21;
+ s13 += carry12;
+ s12 -= int64_lshift21(carry12);
+ carry14 = (s14 + (1 << 20)) >> 21;
+ s15 += carry14;
+ s14 -= int64_lshift21(carry14);
+ carry16 = (s16 + (1 << 20)) >> 21;
+ s17 += carry16;
+ s16 -= int64_lshift21(carry16);
+
+ carry7 = (s7 + (1 << 20)) >> 21;
+ s8 += carry7;
+ s7 -= int64_lshift21(carry7);
+ carry9 = (s9 + (1 << 20)) >> 21;
+ s10 += carry9;
+ s9 -= int64_lshift21(carry9);
+ carry11 = (s11 + (1 << 20)) >> 21;
+ s12 += carry11;
+ s11 -= int64_lshift21(carry11);
+ carry13 = (s13 + (1 << 20)) >> 21;
+ s14 += carry13;
+ s13 -= int64_lshift21(carry13);
+ carry15 = (s15 + (1 << 20)) >> 21;
+ s16 += carry15;
+ s15 -= int64_lshift21(carry15);
+
+ s5 += s17 * 666643;
+ s6 += s17 * 470296;
+ s7 += s17 * 654183;
+ s8 -= s17 * 997805;
+ s9 += s17 * 136657;
+ s10 -= s17 * 683901;
+ s17 = 0;
+
+ s4 += s16 * 666643;
+ s5 += s16 * 470296;
+ s6 += s16 * 654183;
+ s7 -= s16 * 997805;
+ s8 += s16 * 136657;
+ s9 -= s16 * 683901;
+ s16 = 0;
+
+ s3 += s15 * 666643;
+ s4 += s15 * 470296;
+ s5 += s15 * 654183;
+ s6 -= s15 * 997805;
+ s7 += s15 * 136657;
+ s8 -= s15 * 683901;
+ s15 = 0;
+
+ s2 += s14 * 666643;
+ s3 += s14 * 470296;
+ s4 += s14 * 654183;
+ s5 -= s14 * 997805;
+ s6 += s14 * 136657;
+ s7 -= s14 * 683901;
+ s14 = 0;
+
+ s1 += s13 * 666643;
+ s2 += s13 * 470296;
+ s3 += s13 * 654183;
+ s4 -= s13 * 997805;
+ s5 += s13 * 136657;
+ s6 -= s13 * 683901;
+ s13 = 0;
+
+ s0 += s12 * 666643;
+ s1 += s12 * 470296;
+ s2 += s12 * 654183;
+ s3 -= s12 * 997805;
+ s4 += s12 * 136657;
+ s5 -= s12 * 683901;
+ s12 = 0;
+
+ carry0 = (s0 + (1 << 20)) >> 21;
+ s1 += carry0;
+ s0 -= int64_lshift21(carry0);
+ carry2 = (s2 + (1 << 20)) >> 21;
+ s3 += carry2;
+ s2 -= int64_lshift21(carry2);
+ carry4 = (s4 + (1 << 20)) >> 21;
+ s5 += carry4;
+ s4 -= int64_lshift21(carry4);
+ carry6 = (s6 + (1 << 20)) >> 21;
+ s7 += carry6;
+ s6 -= int64_lshift21(carry6);
+ carry8 = (s8 + (1 << 20)) >> 21;
+ s9 += carry8;
+ s8 -= int64_lshift21(carry8);
+ carry10 = (s10 + (1 << 20)) >> 21;
+ s11 += carry10;
+ s10 -= int64_lshift21(carry10);
+
+ carry1 = (s1 + (1 << 20)) >> 21;
+ s2 += carry1;
+ s1 -= int64_lshift21(carry1);
+ carry3 = (s3 + (1 << 20)) >> 21;
+ s4 += carry3;
+ s3 -= int64_lshift21(carry3);
+ carry5 = (s5 + (1 << 20)) >> 21;
+ s6 += carry5;
+ s5 -= int64_lshift21(carry5);
+ carry7 = (s7 + (1 << 20)) >> 21;
+ s8 += carry7;
+ s7 -= int64_lshift21(carry7);
+ carry9 = (s9 + (1 << 20)) >> 21;
+ s10 += carry9;
+ s9 -= int64_lshift21(carry9);
+ carry11 = (s11 + (1 << 20)) >> 21;
+ s12 += carry11;
+ s11 -= int64_lshift21(carry11);
+
+ s0 += s12 * 666643;
+ s1 += s12 * 470296;
+ s2 += s12 * 654183;
+ s3 -= s12 * 997805;
+ s4 += s12 * 136657;
+ s5 -= s12 * 683901;
+ s12 = 0;
+
+ carry0 = s0 >> 21;
+ s1 += carry0;
+ s0 -= int64_lshift21(carry0);
+ carry1 = s1 >> 21;
+ s2 += carry1;
+ s1 -= int64_lshift21(carry1);
+ carry2 = s2 >> 21;
+ s3 += carry2;
+ s2 -= int64_lshift21(carry2);
+ carry3 = s3 >> 21;
+ s4 += carry3;
+ s3 -= int64_lshift21(carry3);
+ carry4 = s4 >> 21;
+ s5 += carry4;
+ s4 -= int64_lshift21(carry4);
+ carry5 = s5 >> 21;
+ s6 += carry5;
+ s5 -= int64_lshift21(carry5);
+ carry6 = s6 >> 21;
+ s7 += carry6;
+ s6 -= int64_lshift21(carry6);
+ carry7 = s7 >> 21;
+ s8 += carry7;
+ s7 -= int64_lshift21(carry7);
+ carry8 = s8 >> 21;
+ s9 += carry8;
+ s8 -= int64_lshift21(carry8);
+ carry9 = s9 >> 21;
+ s10 += carry9;
+ s9 -= int64_lshift21(carry9);
+ carry10 = s10 >> 21;
+ s11 += carry10;
+ s10 -= int64_lshift21(carry10);
+ carry11 = s11 >> 21;
+ s12 += carry11;
+ s11 -= int64_lshift21(carry11);
+
+ s0 += s12 * 666643;
+ s1 += s12 * 470296;
+ s2 += s12 * 654183;
+ s3 -= s12 * 997805;
+ s4 += s12 * 136657;
+ s5 -= s12 * 683901;
+ s12 = 0;
+
+ carry0 = s0 >> 21;
+ s1 += carry0;
+ s0 -= int64_lshift21(carry0);
+ carry1 = s1 >> 21;
+ s2 += carry1;
+ s1 -= int64_lshift21(carry1);
+ carry2 = s2 >> 21;
+ s3 += carry2;
+ s2 -= int64_lshift21(carry2);
+ carry3 = s3 >> 21;
+ s4 += carry3;
+ s3 -= int64_lshift21(carry3);
+ carry4 = s4 >> 21;
+ s5 += carry4;
+ s4 -= int64_lshift21(carry4);
+ carry5 = s5 >> 21;
+ s6 += carry5;
+ s5 -= int64_lshift21(carry5);
+ carry6 = s6 >> 21;
+ s7 += carry6;
+ s6 -= int64_lshift21(carry6);
+ carry7 = s7 >> 21;
+ s8 += carry7;
+ s7 -= int64_lshift21(carry7);
+ carry8 = s8 >> 21;
+ s9 += carry8;
+ s8 -= int64_lshift21(carry8);
+ carry9 = s9 >> 21;
+ s10 += carry9;
+ s9 -= int64_lshift21(carry9);
+ carry10 = s10 >> 21;
+ s11 += carry10;
+ s10 -= int64_lshift21(carry10);
+
+ s[0] = s0 >> 0;
+ s[1] = s0 >> 8;
+ s[2] = (s0 >> 16) | (s1 << 5);
+ s[3] = s1 >> 3;
+ s[4] = s1 >> 11;
+ s[5] = (s1 >> 19) | (s2 << 2);
+ s[6] = s2 >> 6;
+ s[7] = (s2 >> 14) | (s3 << 7);
+ s[8] = s3 >> 1;
+ s[9] = s3 >> 9;
+ s[10] = (s3 >> 17) | (s4 << 4);
+ s[11] = s4 >> 4;
+ s[12] = s4 >> 12;
+ s[13] = (s4 >> 20) | (s5 << 1);
+ s[14] = s5 >> 7;
+ s[15] = (s5 >> 15) | (s6 << 6);
+ s[16] = s6 >> 2;
+ s[17] = s6 >> 10;
+ s[18] = (s6 >> 18) | (s7 << 3);
+ s[19] = s7 >> 5;
+ s[20] = s7 >> 13;
+ s[21] = s8 >> 0;
+ s[22] = s8 >> 8;
+ s[23] = (s8 >> 16) | (s9 << 5);
+ s[24] = s9 >> 3;
+ s[25] = s9 >> 11;
+ s[26] = (s9 >> 19) | (s10 << 2);
+ s[27] = s10 >> 6;
+ s[28] = (s10 >> 14) | (s11 << 7);
+ s[29] = s11 >> 1;
+ s[30] = s11 >> 9;
+ s[31] = s11 >> 17;
+}
+
+bool ed25519_verify(const uint8_t signature[64], const uint8_t public_key[32],
+ const void *message, size_t message_size)
+{
+ ge_p3 A;
+ if ((signature[63] & 224) != 0 ||
+ !x25519_ge_frombytes_vartime(&A, public_key))
+ return false;
+
+ fe_loose t;
+ fe_neg(&t, &A.X);
+ fe_carry(&A.X, &t);
+ fe_neg(&t, &A.T);
+ fe_carry(&A.T, &t);
+
+ uint8_t pkcopy[32];
+ memcpy(pkcopy, public_key, 32);
+ uint8_t rcopy[32];
+ memcpy(rcopy, signature, 32);
+ union {
+ uint64_t u64[4];
+ uint8_t u8[32];
+ } scopy;
+ memcpy(&scopy.u8[0], signature + 32, 32);
+
+ // https://tools.ietf.org/html/rfc8032#section-5.1.7 requires that s be in
+ // the range [0, order) in order to prevent signature malleability.
+
+ // kOrder is the order of Curve25519 in little-endian form.
+ static const uint64_t kOrder[4] = {
+ UINT64_C(0x5812631a5cf5d3ed),
+ UINT64_C(0x14def9dea2f79cd6),
+ 0,
+ UINT64_C(0x1000000000000000),
+ };
+ for (size_t i = 3;; --i) {
+ uint64_t le = swap_le64(scopy.u64[i]);
+ if (le > kOrder[i]) {
+ return false;
+ } else if (le < kOrder[i]) {
+ break;
+ } else if (i == 0) {
+ return false;
+ }
+ }
+
+ uint8_t h[64];
+ BCRYPT_ALG_HANDLE alg, hash;
+ if (!NT_SUCCESS(BCryptOpenAlgorithmProvider(&alg, BCRYPT_SHA512_ALGORITHM, NULL, 0)) ||
+ !NT_SUCCESS(BCryptCreateHash(alg, &hash, NULL, 0, NULL, 0, 0)) ||
+ !NT_SUCCESS(BCryptHashData(hash, (PUCHAR)signature, 32, 0)) ||
+ !NT_SUCCESS(BCryptHashData(hash, (PUCHAR)public_key, 32, 0)) ||
+ !NT_SUCCESS(BCryptHashData(hash, (PUCHAR)message, message_size, 0)) ||
+ !NT_SUCCESS(BCryptFinishHash(hash, h, 64, 0)) ||
+ !NT_SUCCESS(BCryptDestroyHash(hash)) ||
+ !NT_SUCCESS(BCryptCloseAlgorithmProvider(alg, 0)))
+ return false;
+
+ x25519_sc_reduce(h);
+
+ ge_p2 R;
+ ge_double_scalarmult_vartime(&R, h, &A, scopy.u8);
+
+ uint8_t rcheck[32];
+ x25519_ge_tobytes(rcheck, &R);
+
+ return memcmp_ct(rcheck, rcopy, sizeof(rcheck)) == 0;
+}
+
+static const uint64_t blake2b_iv[8] = {
+ 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 0x3c6ef372fe94f82bULL,
+ 0xa54ff53a5f1d36f1ULL, 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL,
+ 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL
+};
+
+static const uint8_t blake2b_sigma[12][16] = {
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
+ { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
+ { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
+ { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
+ { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
+ { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
+ { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
+ { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
+ { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }
+};
+
+#define G(r, i, a, b, c, d) \
+ do { \
+ a = a + b + m[blake2b_sigma[r][2 * i + 0]]; \
+ d = ror64(d ^ a, 32); \
+ c = c + d; \
+ b = ror64(b ^ c, 24); \
+ a = a + b + m[blake2b_sigma[r][2 * i + 1]]; \
+ d = ror64(d ^ a, 16); \
+ c = c + d; \
+ b = ror64(b ^ c, 63); \
+ } while (0)
+
+#define ROUND(r) \
+ do { \
+ G(r, 0, v[0], v[4], v[8], v[12]); \
+ G(r, 1, v[1], v[5], v[9], v[13]); \
+ G(r, 2, v[2], v[6], v[10], v[14]); \
+ G(r, 3, v[3], v[7], v[11], v[15]); \
+ G(r, 4, v[0], v[5], v[10], v[15]); \
+ G(r, 5, v[1], v[6], v[11], v[12]); \
+ G(r, 6, v[2], v[7], v[8], v[13]); \
+ G(r, 7, v[3], v[4], v[9], v[14]); \
+ } while (0)
+
+static void blake2b256_compress(struct blake2b256_state *state,
+ const uint8_t block[128])
+{
+ uint64_t m[16];
+ uint64_t v[16];
+
+ for (int i = 0; i < 16; ++i)
+ m[i] = load_le64(block + i * sizeof(m[i]));
+
+ for (int i = 0; i < 8; ++i)
+ v[i] = state->h[i];
+
+ memcpy(v + 8, blake2b_iv, sizeof(blake2b_iv));
+ v[12] ^= state->t[0];
+ v[13] ^= state->t[1];
+ v[14] ^= state->f[0];
+ v[15] ^= state->f[1];
+
+ for (int i = 0; i < 12; ++i)
+ ROUND(i);
+ for (int i = 0; i < 8; ++i)
+ state->h[i] = state->h[i] ^ v[i] ^ v[i + 8];
+}
+
+void blake2b256_init(struct blake2b256_state *state)
+{
+ memset(state, 0, sizeof(*state));
+ memcpy(state->h, blake2b_iv, sizeof(state->h));
+ state->h[0] ^= 0x01010000 | 32;
+}
+
+void blake2b256_update(struct blake2b256_state *state, const uint8_t *in,
+ unsigned int inlen)
+{
+ const size_t left = state->buflen;
+ const size_t fill = 128 - left;
+
+ if (!inlen)
+ return;
+
+ if (inlen > fill) {
+ state->buflen = 0;
+ memcpy(state->buf + left, in, fill);
+ state->t[0] += 128;
+ state->t[1] += (state->t[0] < 128);
+ blake2b256_compress(state, state->buf);
+ in += fill;
+ inlen -= fill;
+ while (inlen > 128) {
+ state->t[0] += 128;
+ state->t[1] += (state->t[0] < 128);
+ blake2b256_compress(state, in);
+ in += 128;
+ inlen -= 128;
+ }
+ }
+ memcpy(state->buf + state->buflen, in, inlen);
+ state->buflen += inlen;
+}
+
+void blake2b256_final(struct blake2b256_state *state, uint8_t out[32])
+{
+ state->t[0] += state->buflen;
+ state->t[1] += (state->t[0] < state->buflen);
+ state->f[0] = (uint64_t)-1;
+ memset(state->buf + state->buflen, 0, 128 - state->buflen);
+ blake2b256_compress(state, state->buf);
+
+ for (int i = 0; i < 4; ++i)
+ store_le64(out + i * sizeof(state->h[i]), state->h[i]);
+}
diff --git a/installer/fetcher/crypto.h b/installer/fetcher/crypto.h
new file mode 100644
index 00000000..dc234ec7
--- /dev/null
+++ b/installer/fetcher/crypto.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2020-2022 Jason A. Donenfeld. All Rights Reserved.
+ */
+
+#ifndef _CRYPTO_H
+#define _CRYPTO_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+struct blake2b256_state {
+ uint64_t h[8];
+ uint64_t t[2];
+ uint64_t f[2];
+ uint8_t buf[128];
+ size_t buflen;
+};
+
+void blake2b256_init(struct blake2b256_state *state);
+void blake2b256_update(struct blake2b256_state *state, const uint8_t *in,
+ unsigned int inlen);
+void blake2b256_final(struct blake2b256_state *state, uint8_t out[32]);
+
+bool ed25519_verify(const uint8_t signature[64], const uint8_t public_key[32],
+ const void *message, size_t message_size);
+
+#endif
diff --git a/installer/fetcher/fetcher.c b/installer/fetcher/fetcher.c
new file mode 100644
index 00000000..5c688997
--- /dev/null
+++ b/installer/fetcher/fetcher.c
@@ -0,0 +1,340 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2022 Jason A. Donenfeld. All Rights Reserved.
+ */
+
+#include <windows.h>
+#include <delayimp.h>
+#include <commctrl.h>
+#include <shlwapi.h>
+#include <ntsecapi.h>
+#include <sddl.h>
+#include <winhttp.h>
+#include <wintrust.h>
+#include <softpub.h>
+#include <msi.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <wchar.h>
+#include "filelist.h"
+#include "crypto.h"
+#include "systeminfo.h"
+#include "constants.h"
+
+static char msi_filename[MAX_PATH];
+static volatile bool msi_filename_is_set, prompts = true;
+static volatile size_t g_current, g_total;
+static HWND progress;
+static HANDLE filehandle = INVALID_HANDLE_VALUE;
+
+static wchar_t *L(const char *a)
+{
+ static wchar_t w[0x2000];
+ if (!MultiByteToWideChar(CP_UTF8, 0, a, -1, w, sizeof(w)))
+ abort();
+ return w;
+}
+
+static bool random_string(char hex[static 65])
+{
+ uint8_t bytes[32];
+ if (!RtlGenRandom(bytes, sizeof(bytes)))
+ return false;
+ for (int i = 0; i < 32; ++i) {
+ hex[i * 2] = 87U + (bytes[i] >> 4) + ((((bytes[i] >> 4) - 10U) >> 8) & ~38U);
+ hex[i * 2 + 1] = 87U + (bytes[i] & 0xf) + ((((bytes[i] & 0xf) - 10U) >> 8) & ~38U);
+ }
+ hex[64] = '\0';
+ return true;
+}
+
+static void set_status(HWND progress, const char *status)
+{
+ LONG_PTR current_style = GetWindowLongPtrA(progress, GWL_STYLE);
+ char buf[0x1000];
+ g_total = 0;
+ _snprintf_s(buf, sizeof(buf), _TRUNCATE, "WireGuard: %s...", status);
+ SetWindowTextA(progress, buf);
+ if (!(current_style & PBS_MARQUEE)) {
+ SendMessageA(progress, PBM_SETRANGE32, 0, 100);
+ SendMessageA(progress, PBM_SETPOS, 0, 0);
+ SetWindowLongPtrA(progress, GWL_STYLE, current_style | PBS_MARQUEE);
+ SendMessageA(progress, PBM_SETMARQUEE, TRUE, 0);
+ }
+}
+
+static void set_progress(HWND progress, size_t current, size_t total)
+{
+ g_current = current;
+ g_total = total;
+ PostMessageA(progress, WM_APP, 0, 0);
+}
+
+static DWORD __stdcall download_thread(void *param)
+{
+ DWORD ret = 1, bytes_read, bytes_written, enable_http2 = WINHTTP_PROTOCOL_FLAG_HTTP2;
+ HINTERNET session = NULL, connection = NULL, request = NULL;
+ uint8_t hash[32], computed_hash[32], buf[512 * 1024];
+ char download_path[MAX_FILENAME_LEN + sizeof(msi_path)], random_filename[65];
+ wchar_t total_bytes_str[22];
+ size_t total_bytes, current_bytes;
+ const char *arch;
+ struct blake2b256_state hasher;
+ SECURITY_ATTRIBUTES security_attributes = { .nLength = sizeof(security_attributes) };
+ WINTRUST_FILE_INFO wintrust_fileinfo = { .cbStruct = sizeof(wintrust_fileinfo) };
+ WINTRUST_DATA wintrust_data = {
+ .cbStruct = sizeof(wintrust_data),
+ .dwUIChoice = WTD_UI_NONE,
+ .fdwRevocationChecks = WTD_REVOKE_WHOLECHAIN,
+ .dwUnionChoice = WTD_CHOICE_FILE,
+ .dwStateAction = WTD_STATEACTION_VERIFY,
+ .pFile = &wintrust_fileinfo
+ };
+
+ (void)param;
+
+ set_status(progress, "determining paths");
+ if (!ConvertStringSecurityDescriptorToSecurityDescriptorA("O:BAD:PAI(A;;FA;;;BA)", SDDL_REVISION_1, &security_attributes.lpSecurityDescriptor, NULL))
+ goto out;
+ if (!GetWindowsDirectoryA(msi_filename, sizeof(msi_filename)) || !PathAppendA(msi_filename, "Temp"))
+ goto out;
+ if (!random_string(random_filename))
+ goto out;
+ if (!PathAppendA(msi_filename, random_filename))
+ goto out;
+
+ set_status(progress, "determining architecture");
+ arch = architecture();
+ if (!arch)
+ goto out;
+
+ set_status(progress, "connecting to server");
+ session = WinHttpOpen(L(useragent()), is_win7() ? WINHTTP_ACCESS_TYPE_DEFAULT_PROXY : WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, NULL, NULL, 0);
+ if (!session)
+ goto out;
+ WinHttpSetOption(session, WINHTTP_OPTION_ENABLE_HTTP_PROTOCOL, &enable_http2, sizeof(enable_http2)); // Don't check return value, in case of old Windows
+ if (is_win8dotzero_or_below()) {
+ DWORD enable_tls12 = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2;
+ if (!WinHttpSetOption(session, WINHTTP_OPTION_SECURE_PROTOCOLS, &enable_tls12, sizeof(enable_tls12)))
+ goto out;
+ }
+
+ connection = WinHttpConnect(session, L(server), port, 0);
+ if (!connection)
+ goto out;
+
+ set_status(progress, "downloading installer list");
+ request = WinHttpOpenRequest(connection, L"GET", L(msi_path latest_version_file), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_REFRESH | WINHTTP_FLAG_SECURE);
+ if (!request)
+ goto out;
+ if (!WinHttpSendRequest(request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0))
+ goto out;
+ if (!WinHttpReceiveResponse(request, NULL))
+ goto out;
+ if (!WinHttpReadData(request, buf, sizeof(buf), &bytes_read))
+ goto out;
+ WinHttpCloseHandle(request);
+ request = NULL;
+ if (bytes_read <= 0 || bytes_read >= sizeof(buf))
+ goto out;
+
+ set_status(progress, "verifying installer list");
+ memcpy(download_path, msi_path, strlen(msi_path));
+ if (!extract_newest_file(download_path + strlen(msi_path), hash, (const char *)buf, bytes_read, arch))
+ goto out;
+
+ set_status(progress, "creating temporary file");
+ filehandle = CreateFileA(msi_filename, GENERIC_WRITE | DELETE, 0, &security_attributes, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY, NULL);
+ if (filehandle == INVALID_HANDLE_VALUE)
+ goto out;
+ msi_filename_is_set = true;
+
+ set_status(progress, "downloading installer");
+ request = WinHttpOpenRequest(connection, L"GET", L(download_path), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
+ if (!request)
+ goto out;
+ if (!WinHttpSendRequest(request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0))
+ goto out;
+ if (!WinHttpReceiveResponse(request, NULL))
+ goto out;
+ bytes_read = sizeof(total_bytes_str);
+ if (!WinHttpQueryHeaders(request, WINHTTP_QUERY_CONTENT_LENGTH, WINHTTP_HEADER_NAME_BY_INDEX, total_bytes_str, &bytes_read, WINHTTP_NO_HEADER_INDEX))
+ goto out;
+ total_bytes = wcstoul(total_bytes_str, NULL, 10);
+ if (total_bytes > 100 * 1024 * 1024)
+ goto out;
+ blake2b256_init(&hasher);
+ set_progress(progress, 0, total_bytes);
+ for (current_bytes = 0;;) {
+ if (!WinHttpReadData(request, buf, 8192, &bytes_read))
+ goto out;
+ if (!bytes_read)
+ break;
+ current_bytes += bytes_read;
+ if (current_bytes > 100 * 1024 * 1024)
+ goto out;
+ blake2b256_update(&hasher, buf, bytes_read);
+ if (!WriteFile(filehandle, buf, bytes_read, &bytes_written, NULL) || bytes_read != bytes_written)
+ goto out;
+ set_progress(progress, current_bytes, total_bytes);
+ }
+
+ set_status(progress, "verifying installer");
+ blake2b256_final(&hasher, computed_hash);
+ if (memcmp(hash, computed_hash, sizeof(hash)))
+ goto out;
+ CloseHandle(filehandle); //TODO: I wish this wasn't required.
+ filehandle = INVALID_HANDLE_VALUE;
+ wintrust_fileinfo.pcwszFilePath = L(msi_filename);
+ ret = WinVerifyTrustEx(INVALID_HANDLE_VALUE, &(GUID)WINTRUST_ACTION_GENERIC_VERIFY_V2, &wintrust_data);
+ wintrust_data.dwStateAction = WTD_STATEACTION_CLOSE;
+ WinVerifyTrustEx(INVALID_HANDLE_VALUE, &(GUID)WINTRUST_ACTION_GENERIC_VERIFY_V2, &wintrust_data);
+ if (ret)
+ goto out;
+
+ set_status(progress, "launching installer");
+ ShowWindow(progress, SW_HIDE);
+ ret = MsiInstallProductA(msi_filename, NULL);
+ ret = ret == ERROR_INSTALL_USEREXIT ? ERROR_SUCCESS : ret;
+
+out:
+ if (request)
+ WinHttpCloseHandle(request);
+ if (connection)
+ WinHttpCloseHandle(connection);
+ if (session)
+ WinHttpCloseHandle(session);
+ if (security_attributes.lpSecurityDescriptor)
+ LocalFree(security_attributes.lpSecurityDescriptor);
+
+ if (ret && prompts) {
+ ShowWindow(progress, SW_SHOWDEFAULT);
+ if (MessageBoxA(progress, "Something went wrong when downloading the WireGuard installer. Would you like to open your web browser to the MSI download page?", "Download Error", MB_YESNO | MB_ICONWARNING) == IDYES)
+ ShellExecuteA(progress, NULL, "https://" server msi_path, NULL, NULL, SW_SHOWNORMAL);
+ }
+ exit(ret);
+ return ret;
+}
+
+static int cleanup(void)
+{
+ BOOL did_delete_via_handle = FALSE;
+ FILE_DISPOSITION_INFO disposition = { TRUE };
+ if (filehandle != INVALID_HANDLE_VALUE) {
+ did_delete_via_handle = SetFileInformationByHandle(filehandle, FileDispositionInfo, &disposition, sizeof(disposition));
+ CloseHandle(filehandle);
+ filehandle = INVALID_HANDLE_VALUE;
+ }
+ if (msi_filename_is_set && !did_delete_via_handle) {
+ //TODO: how does DeleteFile deal with reparse points?
+ for (int i = 0; i < 200 && !DeleteFileA(msi_filename) && GetLastError() != ERROR_FILE_NOT_FOUND; ++i)
+ Sleep(200);
+ }
+ return 0;
+}
+
+static FARPROC WINAPI delayed_load_library_hook(unsigned dliNotify, PDelayLoadInfo pdli)
+{
+ HMODULE library;
+ if (dliNotify != dliNotePreLoadLibrary)
+ return NULL;
+ library = LoadLibraryExA(pdli->szDll, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
+ if (!library)
+ abort();
+ return (FARPROC)library;
+}
+
+PfnDliHook __pfnDliNotifyHook2 = delayed_load_library_hook;
+
+static LRESULT CALLBACK wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
+{
+ (void)uIdSubclass; (void)dwRefData;
+
+ switch (uMsg) {
+ case WM_CLOSE:
+ case WM_DESTROY: {
+ LRESULT ret = DefSubclassProc(hWnd, uMsg, wParam, lParam);
+ exit(0);
+ return ret;
+ }
+ case WM_APP: if (g_total) {
+ char buf[0x1000], *start, *paren;
+ LONG_PTR current_style;
+ int chars = GetWindowTextA(progress, buf, sizeof(buf));
+ if (chars) {
+ start = buf + chars;
+ if (start[-1] == '.' && start[-2] == '.' && start[-3] == '.')
+ start -= 3;
+ else if ((paren = memchr(buf, '(', chars)) && paren > buf)
+ start = paren - 1;
+ *start = '\0';
+ _snprintf_s(start, sizeof(buf) - (start - buf), _TRUNCATE, " (%.2f%%)", g_current * 100.0f / g_total);
+ SetWindowTextA(progress, buf);
+ }
+ current_style = GetWindowLongPtrA(progress, GWL_STYLE);
+ if (current_style & PBS_MARQUEE) {
+ SetWindowLongPtrA(progress, GWL_STYLE, current_style & ~PBS_MARQUEE);
+ SendMessageA(progress, PBM_SETMARQUEE, FALSE, 0);
+ }
+ SendMessageA(progress, PBM_SETRANGE32, 0, (LPARAM)g_total);
+ SendMessageA(progress, PBM_SETPOS, (WPARAM)g_current, 0);
+ break;
+ }
+ }
+ return DefSubclassProc(hWnd, uMsg, wParam, lParam);
+}
+
+static void parse_command_line(void)
+{
+ LPWSTR *argv;
+ int argc;
+ argv = CommandLineToArgvW(GetCommandLineW(), &argc);
+ if (!argv)
+ return;
+ for (int i = 1; i < argc; ++i) {
+ if (wcsicmp(argv[i], L"/noprompt") == 0)
+ prompts = false;
+ }
+ LocalFree(argv);
+}
+
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pCmdLine, int nCmdShow)
+{
+ MSG msg;
+ HICON icon;
+ HDC dc;
+ float scale;
+
+ (void)hPrevInstance; (void)pCmdLine; (void)nCmdShow;
+
+ if (!SetDllDirectoryA("") || !SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32))
+ return 1;
+
+ parse_command_line();
+
+ InitCommonControlsEx(&(INITCOMMONCONTROLSEX){ .dwSize = sizeof(INITCOMMONCONTROLSEX), .dwICC = ICC_PROGRESS_CLASS });
+
+ progress = CreateWindowExA(0, PROGRESS_CLASS, "WireGuard Installer",
+ (WS_OVERLAPPEDWINDOW & ~(WS_BORDER | WS_THICKFRAME | WS_MAXIMIZEBOX)) | PBS_MARQUEE | PBS_SMOOTH,
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ NULL, NULL, hInstance, NULL);
+ SetWindowSubclass(progress, wndproc, 0, 0);
+ dc = GetDC(progress);
+ scale = GetDeviceCaps(dc, LOGPIXELSY) / 96.0f;
+ ReleaseDC(progress, dc);
+ icon = LoadIconA(hInstance, MAKEINTRESOURCE(7));
+ SendMessageA(progress, WM_SETICON, ICON_BIG, (LPARAM)icon);
+ SendMessageA(progress, WM_SETICON, ICON_SMALL, (LPARAM)icon);
+ SendMessageA(progress, PBM_SETMARQUEE, TRUE, 0);
+ SetWindowPos(progress, HWND_TOPMOST, -1, -1, 500 * scale, 80 * scale, SWP_NOMOVE | SWP_SHOWWINDOW);
+
+ _onexit(cleanup);
+ CreateThread(NULL, 0, download_thread, NULL, 0, NULL);
+
+ while (GetMessage(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ return 0;
+}
diff --git a/installer/fetcher/filelist.c b/installer/fetcher/filelist.c
new file mode 100644
index 00000000..f938e324
--- /dev/null
+++ b/installer/fetcher/filelist.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2022 Jason A. Donenfeld. All Rights Reserved.
+ */
+
+#include "constants.h"
+#include "crypto.h"
+#include "filelist.h"
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static inline int decode_base64(const char src[static 4])
+{
+ int val = 0;
+
+ for (unsigned int i = 0; i < 4; ++i)
+ val |= (-1
+ + ((((('A' - 1) - src[i]) & (src[i] - ('Z' + 1))) >> 8) & (src[i] - 64))
+ + ((((('a' - 1) - src[i]) & (src[i] - ('z' + 1))) >> 8) & (src[i] - 70))
+ + ((((('0' - 1) - src[i]) & (src[i] - ('9' + 1))) >> 8) & (src[i] + 5))
+ + ((((('+' - 1) - src[i]) & (src[i] - ('+' + 1))) >> 8) & 63)
+ + ((((('/' - 1) - src[i]) & (src[i] - ('/' + 1))) >> 8) & 64)
+ ) << (18 - 6 * i);
+ return val;
+}
+
+bool signify_pubkey_from_base64(uint8_t key[static 42], const char base64[static 56])
+{
+ unsigned int i;
+ volatile uint8_t ret = 0;
+ int val;
+
+ for (i = 0; i < 42 / 3; ++i) {
+ val = decode_base64(&base64[i * 4]);
+ ret |= (uint32_t)val >> 31;
+ key[i * 3 + 0] = (val >> 16) & 0xff;
+ key[i * 3 + 1] = (val >> 8) & 0xff;
+ key[i * 3 + 2] = val & 0xff;
+ }
+
+ return 1 & ((ret - 1) >> 8);
+}
+
+bool signify_signature_from_base64(uint8_t sig[static 74], const char base64[static 100])
+{
+ unsigned int i;
+ volatile uint8_t ret = 0;
+ int val;
+
+ if (base64[99] != '=')
+ return false;
+
+ for (i = 0; i < 74 / 3; ++i) {
+ val = decode_base64(&base64[i * 4]);
+ ret |= (uint32_t)val >> 31;
+ sig[i * 3 + 0] = (val >> 16) & 0xff;
+ sig[i * 3 + 1] = (val >> 8) & 0xff;
+ sig[i * 3 + 2] = val & 0xff;
+ }
+ val = decode_base64((const char[]){ base64[i * 4 + 0], base64[i * 4 + 1], base64[i * 4 + 2], 'A' });
+ ret |= ((uint32_t)val >> 31) | (val & 0xff);
+ sig[i * 3 + 0] = (val >> 16) & 0xff;
+ sig[i * 3 + 1] = (val >> 8) & 0xff;
+
+ return 1 & ((ret - 1) >> 8);
+}
+
+bool hash_from_hex(uint8_t hash[static 32], const char hex[static 64])
+{
+ uint8_t c, c_acc, c_alpha0, c_alpha, c_num0, c_num, c_val;
+ volatile uint8_t ret = 0;
+
+ for (unsigned int i = 0; i < 64; i += 2) {
+ c = (uint8_t)hex[i];
+ c_num = c ^ 48U;
+ c_num0 = (c_num - 10U) >> 8;
+ c_alpha = (c & ~32U) - 55U;
+ c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8;
+ ret |= ((c_num0 | c_alpha0) - 1) >> 8;
+ c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha);
+ c_acc = c_val * 16U;
+
+ c = (uint8_t)hex[i + 1];
+ c_num = c ^ 48U;
+ c_num0 = (c_num - 10U) >> 8;
+ c_alpha = (c & ~32U) - 55U;
+ c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8;
+ ret |= ((c_num0 | c_alpha0) - 1) >> 8;
+ c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha);
+ hash[i / 2] = c_acc | c_val;
+ }
+
+ return 1 & ((ret - 1) >> 8);
+}
+
+static uint64_t parse_version(const char *str, size_t len)
+{
+ uint64_t version = 0;
+ unsigned long nibble;
+ const char *limit = str + len;
+ char *end;
+
+ for (int shift = 64 - 16; shift >= 0; shift -= 16, str = end + 1) {
+ if (str[0] == '-' || str[0] == '+')
+ return 0;
+ nibble = strtoul(str, &end, 10);
+ if (nibble > UINT16_MAX)
+ return 0;
+ version |= (uint64_t)nibble << shift;
+ if (end >= limit)
+ break;
+ if (end[0] != '.')
+ return 0;
+ }
+ return version;
+}
+
+bool extract_newest_file(char filename[static MAX_FILENAME_LEN], uint8_t hash[static 32], const char *list, size_t len, const char *arch)
+{
+ const char *first_nl, *second_nl, *line_start, *line_end;
+ char msi_prefix[sizeof(msi_arch_prefix) + 10];
+ int msi_prefix_len;
+ uint8_t pubkey[42], signature[74];
+ uint64_t biggest_version = 0, version;
+
+ if ((msi_prefix_len = _snprintf_s(msi_prefix, sizeof(msi_prefix), _TRUNCATE, msi_arch_prefix, arch)) < 0)
+ return false;
+ if (!signify_pubkey_from_base64(pubkey, release_public_key_base64))
+ return false;
+ first_nl = memchr(list, '\n', len);
+ if (!first_nl)
+ return false;
+ second_nl = memchr(first_nl + 1, '\n', len - (first_nl + 1 - list));
+ if (!second_nl)
+ return false;
+ if (len < 19 || memcmp(list, "untrusted comment: ", 19))
+ return false;
+ if (second_nl - first_nl != 101)
+ return false;
+ if (!signify_signature_from_base64(signature, first_nl + 1))
+ return false;
+ if (memcmp(pubkey, signature, 10))
+ return false;
+ if (!ed25519_verify(signature + 10, pubkey + 10, second_nl + 1, len - (second_nl + 1 - list)))
+ return false;
+ for (line_start = second_nl + 1; line_start < list + len; line_start = line_end + 1) {
+ line_end = memchr(line_start + 1, '\n', len - (line_start + 1 - list));
+ if (!line_end)
+ break;
+ if ((size_t)(line_end - line_start) < (64 + 2 + msi_prefix_len + strlen(msi_suffix) + 1) || line_start[64] != ' ' || line_start[65] != ' ')
+ continue;
+ if (memcmp(msi_prefix, line_start + 66, msi_prefix_len) || memcmp(msi_suffix, line_end - strlen(msi_suffix), strlen(msi_suffix)))
+ continue;
+ if (line_end - line_start - 66 > MAX_FILENAME_LEN - 1)
+ continue;
+ version = parse_version(line_start + 66 + msi_prefix_len, line_end - strlen(msi_suffix) - line_start - 66 - msi_prefix_len);
+ if (version < biggest_version)
+ continue;
+ if (!hash_from_hex(hash, line_start))
+ continue;
+ memcpy(filename, line_start + 66, line_end - line_start - 66);
+ filename[line_end - line_start - 66] = '\0';
+ biggest_version = version;
+ }
+ return biggest_version > 0;
+}
diff --git a/installer/fetcher/filelist.h b/installer/fetcher/filelist.h
new file mode 100644
index 00000000..7da57470
--- /dev/null
+++ b/installer/fetcher/filelist.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2020-2022 Jason A. Donenfeld. All Rights Reserved.
+ */
+
+#ifndef _FILELIST_H
+#define _FILELIST_H
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+enum { MAX_FILENAME_LEN = 0x400 };
+
+bool extract_newest_file(char filename[static MAX_FILENAME_LEN], uint8_t hash[static 32], const char *list, size_t len, const char *arch);
+
+#endif
diff --git a/installer/fetcher/icon.svg b/installer/fetcher/icon.svg
new file mode 100644
index 00000000..06a2db5c
--- /dev/null
+++ b/installer/fetcher/icon.svg
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="478.99808"
+ height="478.99808"
+ version="1.1"
+ viewBox="0 0 478.99808 478.99808"
+ xml:space="preserve"
+ id="svg35"
+ sodipodi:docname="wireguard-install.svg"
+ inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"><metadata
+ id="metadata39"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1023"
+ id="namedview37"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:zoom="1.2639534"
+ inkscape:cx="246.80998"
+ inkscape:cy="256.18157"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg35" /><defs
+ id="defs5"><clipPath
+ id="a"><path
+ d="M 0,300 H 300 V 0 H 0 Z"
+ id="path2" /></clipPath></defs><g
+ id="g2416"
+ transform="scale(1.3897777)"><g
+ transform="matrix(1.1488658,0,0,-1.1488658,0,344.65974)"
+ id="g33"><g
+ clip-path="url(#a)"
+ id="g31"><g
+ transform="translate(177.57,268.56)"
+ id="g9"><path
+ d="M 0,0 C 0.969,-0.066 2.097,0.81 3.987,1.635 2.125,2.437 1.014,3.29 0.048,3.228 -0.948,3.165 -2.544,2.197 -2.519,1.65 -2.492,1.047 -0.987,0.067 0,0"
+ fill="#871719"
+ id="path7" /></g><g
+ transform="translate(179.32,268.11)"
+ id="g13"><path
+ d="M 0,0 C 0.969,-0.066 2.097,0.81 3.987,1.635 2.125,2.437 1.014,3.29 0.048,3.228 -0.948,3.165 -2.544,2.197 -2.519,1.65 -2.492,1.047 -0.987,0.067 0,0"
+ fill="#871719"
+ id="path11" /></g><g
+ transform="translate(299.74,154.44)"
+ id="g17"><path
+ d="m 0,0 c 0,0 6.94,145.56 -153.04,145.56 -141.48,0 -145.9,-139.63 -145.9,-139.63 0,0 -20.811,-160.37 149.16,-160.37 C 13.24,-154.44 0,0 0,0"
+ fill="#871719"
+ id="path15" /></g><g
+ transform="translate(133.86,128.17)"
+ id="g21"><path
+ d="m 0,0 c -2.627,-1.39 -4.65,-2.414 -6.63,-3.517 -8.1,-4.512 -15.026,-10.419 -20.544,-17.868 -1.784,-2.409 -3.01,-2.603 -5.727,-0.941 -35.338,21.61 -37.609,75.843 0.983,99.453 C -1.901,95.491 36.447,84.267 50.817,56.65 53.54,51.416 53.886,43.359 52.162,37.868 46.207,18.913 32.147,8.282 12.849,3.766 18.538,8.636 23.067,14.159 24.508,21.791 25.96,29.478 24.424,36.429 19.966,42.747 13.193,52.343 0.098,56.291 -10.845,52.136 -22.726,47.625 -29.235,36.782 -28.061,23.453 -26.971,11.072 -17.577,3.048 0,0"
+ fill="#ffffff"
+ id="path19" /></g><g
+ transform="translate(58.513,66.293)"
+ id="g25"><path
+ d="M 0,0 C 2.838,19.152 25.265,36.788 44.23,34.776 38.356,26.832 35.643,17.846 34.988,8.883 28.686,7.722 22.747,6.941 16.981,5.478 11.304,4.037 5.803,1.903 0,0"
+ fill="#ffffff"
+ id="path23" /></g><g
+ transform="translate(183.79,273.09)"
+ id="g29"><path
+ d="M 0,0 C 1.061,0.812 2.155,1.494 3.472,0.408 4.222,-0.209 4.95,-0.849 5.858,-1.624 4.731,-2.219 3.816,-2.72 2.883,-3.191 1.577,-3.849 0.601,-3.409 -0.189,-2.369 -0.831,-1.525 -0.946,-0.724 0,0 m 15.447,-157.8 c -1.598,1.382 -2.611,1.381 -4.485,0.182 -6.359,-4.068 -12.867,-7.922 -19.481,-11.562 -3.792,-2.086 -7.898,-3.599 -12.653,-5.724 1.633,-0.421 2.418,-0.619 3.201,-0.827 17.776,-4.73 27.272,-20.335 23.065,-37.813 -3.741,-15.544 -19.52,-25.482 -34.812,-22.86 -12.748,2.186 -23.877,12.772 -25.735,25.456 -2.026,13.824 4.859,27.119 17.108,32.689 6.794,3.089 13.771,5.778 20.549,8.9 7.706,3.551 16.038,6.355 22.766,11.296 16.7,12.262 27.012,29.145 31.033,49.523 2.408,12.207 2.245,24.36 -3.339,35.95 -4.286,8.895 -11.319,15.357 -18.875,21.253 -7.775,6.068 -16.007,11.554 -23.747,17.664 -2.095,1.653 -3.509,4.505 -4.478,7.09 -0.411,1.095 0.925,4.066 1.819,4.227 4.746,0.852 9.596,1.29 14.425,1.473 5.574,0.21 11.164,0.032 16.746,-0.042 1.21,-0.015 2.853,0.141 3.549,-0.542 2.891,-2.843 5.159,-1.014 7.166,0.856 1.689,1.573 2.893,3.668 4.236,5.433 -0.815,0.12 -2.487,0.541 -4.168,0.581 -5.613,0.133 -11.233,0.047 -16.843,0.253 -1,0.037 -1.963,1.066 -2.942,1.637 1.031,0.409 2.058,1.165 3.093,1.175 9.682,0.091 19.366,0.054 29.057,0.054 C 41.713,-6.44 34.98,0.458 28.998,2.328 28.953,1.646 28.911,1.011 28.867,0.334 22.923,0.193 17.089,0.304 11.789,3.122 10.393,3.865 9.48,5.516 8.343,6.749 6.912,8.3 5.738,10.296 3.994,11.308 c -3.576,2.076 -7.48,3.58 -11.211,5.397 -13.259,6.458 -27.262,6.231 -42.302,4.854 8.991,-2.092 17.11,-3.982 25.23,-5.872 -0.093,-0.494 -0.185,-0.987 -0.278,-1.481 -10.86,-1.455 -21.134,2.528 -31.756,4.003 3.849,-2.254 7.749,-4.35 11.778,-6.158 4.095,-1.837 8.316,-3.39 12.538,-5.091 -5.364,-4.583 -10.746,-5.588 -17.488,-4.048 -3.686,0.842 -7.585,1.29 -11.348,1.106 -3.887,-0.19 -7.802,-1.147 -11.332,-3.506 3.78,-1.916 7.263,-3.506 10.549,-5.432 1.355,-0.795 2.909,-2.144 3.287,-3.536 0.904,-3.333 1.166,-6.841 1.687,-10.281 -6.188,-0.701 -17.071,-6.994 -19.27,-11.09 9.512,-1.831 19.868,0.383 28.942,-5.746 -2.989,-2.262 -9.949,-5.075 -12.502,-7.007 3.156,-0.827 10.469,-0.423 13.33,-0.229 2.409,0.164 3.521,0.223 4.508,-0.59 l 28.001,-21.921 c 2.944,-2.374 14.835,-13.629 17.939,-20.704 2.643,-6.023 2.966,-11.148 2.965,-12.398 -0.002,-3.355 -0.413,-8.609 -2.721,-14.469 -0.969,-2.461 -3.812,-7.912 -9.677,-14.267 -9.09,-9.847 -20.783,-15.17 -33.57,-17.807 -29.732,-6.13 -54.436,-37.881 -47.462,-72.884 8.142,-40.866 53.247,-62.991 90.107,-43.552 23.824,12.564 36.456,37.078 33.072,63.762 -2.045,16.12 -9.338,29.269 -21.563,39.839"
+ fill="#ffffff"
+ id="path27" /></g></g></g><image
+ width="292.86456"
+ height="292.86456"
+ preserveAspectRatio="none"
+ style="image-rendering:optimizeQuality"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAABGdBTUEAALGPC/xhBQAAACBjSFJN AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QA/wD/AP+gvaeTAACA AElEQVR42uz9d7xl2VnfCX+ftdbe+8Qb694KXVVd1Tm3pFZGiSwQYMwYp9cYDzjwGgcMg2EMJth4 7Hk9tsf2MGN7xtiYDxjLwGCShBAogKRuhVZnde6qrurKN5+0917h/WOtfe6pUrfULbUQtO/Tn+p7 7sn3nPXk3/N7YE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2 ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2 ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2 ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE/2ZE9epMiX+w3syZdOQgjT71dEwpXX/VGV5r1e+bc83/V78sWJ +nK/gT354qVR6tmfzeVZxf/joPwv9PfsKf+XRv5YHIg9efFypdd/mZRegPAC13PFbS903y/8xdPf MWvM9gzCyyPmy/0G9uSly6x3/1wK/hKVv1HcWQWeVfDne64r7/f5rpvK+94Pw6+B1wGHfeChT98j cBCRqy97Ve8f4dH7Py1AeDgEeQL4xB/2B/4Klr0I4I+hNB7wZfDuL0XZX+SbQ9Kjw+lnnuH9J09y 5B3vkDdPCJ86iegbCRsnT/AxuxLm6nU5wJFQmZNy5tJFRN3OcH0o9XgQCIFibim054Ign+HY0vFQ 16t8183R8//rJ4P8zetmooAQhL2o4CXLngH4YyxfpAF4MQovV9z+Wd7dpuu1LWV9s+A9z3je8Ton Zx7S4eKx4L+pb6ZK+Y//YF2rVVVcunip3Ue36mCyatLNLGvGZqKd0tqi1AKirNQyFpE8qCBOghtY r9sdmxWlbVmx1cTVstydDDYH9Wpbxj/6FdeMvtzfxx9H2TMAf0zk+bz+S4gCns/Tf677fc7nHG7C dgv6k0ucWtiHs6i1hx5jePSYfddyKwD8w48/k1eDyQFX6v2Vby0Hq5fWd3aOjDd2rrEM9itll7V2 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 me8hWHTJHr9/WifF/vpvYw3D6aHRB650ArDYKQzlyBbxDuZvG943b/Bm7jn13HvdKyVIa2gM6f75 MHne8GHWuJm7voi/v6gJZ7hTL3I286+dXheu3OWHty26Pv8Z59dnZPzpi91f/+0utcfv85oDcOUu vYh3YBbclm5n7jEwG1XArANYFA0MnRfMOoR54wlXuW2R8Q8fu2iHn08xFoXqV3sMzDoABo+df/8s uL7o99/32ncA/20vdQ337XV5hd4AVzqF4eP0Ho/b67nS7WHB61+tjXZ4W7p8vt11L3BtkUHPP254 /6L79nrtvX6/2tp3APvrD2Vdy3lwNYGL+VB9eH2eoPR8j12EAXw67zPhBH7utkWGlvLsa2HOhWu4 np5zr9edf324NqP+Azf8T+cL3V/7C65ujMnoFjmHvUBHrvL4vW6fxwAWrWvJkfeKFJ7vvs/k/qsZ 7x+aYV/r2ncA++szWc+XOswDjVf7m+dzBs/3Pj7THfTTAdOezwFcy3M83337a3/tr/21v/bX/tpf +2t/7a/9tb/21/7aX/trf+2v/bW/9tf+2l/7a3/tr/21v/bX/tpf+2t/7a/9tb/21/7aX/trf+2v /bW/9tf+2l/7a3/tr/21v/bX/tpf+2t/7a/9tb/21/7aX/trf+2v/bW/9tf+2l/7a3/tr/21v/bX /tpf+2t/7a/9tb/21/7aX/trf+2v/bW/9tf+2l/7a3/tr/21v/bX/tpf+2t/7a/9tb/21/7aX/tr f+2v/bW/9tf+2l/7a3/tr8/u9f8Hy0CzRRKiMyQAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjAtMTEt MTJUMTU6MjQ6MjcrMDA6MDDoOkHhAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIwLTExLTEyVDE1OjI0 OjI3KzAwOjAwmWf5XQAAAABJRU5ErkJggg== "
+ id="image1086"
+ x="47.787258"
+ y="85.720528" /></g></svg>
diff --git a/installer/fetcher/manifest.xml b/installer/fetcher/manifest.xml
new file mode 100644
index 00000000..92205f98
--- /dev/null
+++ b/installer/fetcher/manifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="wireguard-installer" type="win32" />
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- Windows 10 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
+ <!-- Windows 8.1 -->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
+ <!-- Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
+ <!-- Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
+ </application>
+ </compatibility>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*" />
+ </dependentAssembly>
+ </dependency>
+ <application xmlns="urn:schemas-microsoft-com:asm.v3">
+ <windowsSettings>
+ <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
+ <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True</dpiAware>
+ </windowsSettings>
+ </application>
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+ <security>
+ <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
+ <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+</assembly>
diff --git a/installer/fetcher/resources.rc b/installer/fetcher/resources.rc
new file mode 100644
index 00000000..f2bedb66
--- /dev/null
+++ b/installer/fetcher/resources.rc
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2020-2022 Jason A. Donenfeld. All Rights Reserved.
+ */
+
+#include <windows.h>
+#include "version.h"
+
+#pragma code_page(65001) // UTF-8
+
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST manifest.xml
+7 ICON icon.ico
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION VERSION_ARRAY
+PRODUCTVERSION VERSION_ARRAY
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_APP
+FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "WireGuard LLC"
+ VALUE "FileDescription", "WireGuard Installer: Fast, Modern, Secure VPN Tunnel"
+ VALUE "FileVersion", VERSION_STR
+ VALUE "InternalName", "wireguard-installer"
+ VALUE "LegalCopyright", "Copyright © 2015-2022 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved."
+ VALUE "OriginalFilename", "wireguard-installer.exe"
+ VALUE "ProductName", "WireGuard"
+ VALUE "ProductVersion", VERSION_STR
+ VALUE "Comments", "https://www.wireguard.com/"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 0x4b0
+ END
+END
diff --git a/installer/fetcher/systeminfo.c b/installer/fetcher/systeminfo.c
new file mode 100644
index 00000000..f1f80e7f
--- /dev/null
+++ b/installer/fetcher/systeminfo.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2022 Jason A. Donenfeld. All Rights Reserved.
+ */
+
+#include "systeminfo.h"
+#include "version.h"
+#include <windows.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+extern NTAPI __declspec(dllimport) void RtlGetNtVersionNumbers(DWORD *MajorVersion, DWORD *MinorVersion, DWORD *BuildNumber);
+
+const char *architecture(void)
+{
+ static const char *cached_arch;
+ HMODULE kernel32;
+ BOOL(WINAPI *IsWow64Process2)(HANDLE hProcess, USHORT *pProcessMachine, USHORT *pNativeMachine);
+ USHORT process_machine, native_machine;
+ BOOL is_wow64_process;
+
+ if (cached_arch)
+ return cached_arch;
+
+ kernel32 = GetModuleHandleA("kernel32.dll");
+ if (!kernel32)
+ return NULL;
+ *(FARPROC *)&IsWow64Process2 = GetProcAddress(kernel32, "IsWow64Process2");
+ if (IsWow64Process2) {
+ if (!IsWow64Process2(GetCurrentProcess(), &process_machine, &native_machine))
+ return NULL;
+ switch (native_machine) {
+ case IMAGE_FILE_MACHINE_I386:
+ return cached_arch = "x86";
+ case IMAGE_FILE_MACHINE_AMD64:
+ return cached_arch = "amd64";
+ case IMAGE_FILE_MACHINE_ARMNT:
+ return cached_arch = "arm";
+ case IMAGE_FILE_MACHINE_ARM64:
+ return cached_arch = "arm64";
+ }
+ } else {
+ if (!IsWow64Process(GetCurrentProcess(), &is_wow64_process))
+ return NULL;
+ return cached_arch = is_wow64_process ? "amd64" : "x86";
+ }
+ return NULL;
+}
+
+const char *useragent(void)
+{
+ static char useragent[0x200];
+ DWORD maj, min, build;
+
+ if (useragent[0])
+ return useragent;
+ RtlGetNtVersionNumbers(&maj, &min, &build);
+ _snprintf_s(useragent, sizeof(useragent), _TRUNCATE, "WireGuard-Fetcher/" VERSION_STR " (Windows %lu.%lu.%lu; %s)", maj, min, build & 0xffff, architecture());
+ return useragent;
+}
+
+bool is_win7(void)
+{
+ DWORD maj, min, build;
+ RtlGetNtVersionNumbers(&maj, &min, &build);
+ return maj == 6 && min == 1;
+}
+
+bool is_win8dotzero_or_below(void)
+{
+ DWORD maj, min, build;
+ RtlGetNtVersionNumbers(&maj, &min, &build);
+ return maj == 6 && min <= 2;
+}
diff --git a/installer/fetcher/systeminfo.h b/installer/fetcher/systeminfo.h
new file mode 100644
index 00000000..2c06a166
--- /dev/null
+++ b/installer/fetcher/systeminfo.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2020-2022 Jason A. Donenfeld. All Rights Reserved.
+ */
+
+#ifndef _SYSTEMINFO_H
+#define _SYSTEMINFO_H
+
+#include <stdbool.h>
+
+const char *architecture(void);
+const char *useragent(void);
+bool is_win7(void);
+bool is_win8dotzero_or_below(void);
+
+#endif
diff --git a/installer/fetcher/version.h b/installer/fetcher/version.h
new file mode 100644
index 00000000..dd6f54d1
--- /dev/null
+++ b/installer/fetcher/version.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2020-2022 Jason A. Donenfeld. All Rights Reserved.
+ */
+
+#ifndef _VERSION_H
+#define _VERSION_H
+
+#define VERSION_STR "1.0"
+#define VERSION_ARRAY 1,0,0,0
+
+#endif
diff --git a/installer/wireguard.wxs b/installer/wireguard.wxs
index 5bbb1ebb..ab90d3cd 100644
--- a/installer/wireguard.wxs
+++ b/installer/wireguard.wxs
@@ -2,18 +2,22 @@
<!--
SPDX-License-Identifier: GPL-2.0
- Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
-->
-<?if $(var.WIREGUARD_PLATFORM) = "x86"?>
- <?define PlatformProgramFilesFolder = "ProgramFilesFolder"?>
-<?else?>
+<?if $(var.WIREGUARD_PLATFORM) = "amd64" Or $(var.WIREGUARD_PLATFORM) = "arm64"?>
<?define PlatformProgramFilesFolder = "ProgramFiles64Folder"?>
+<?else?>
+ <?define PlatformProgramFilesFolder = "ProgramFilesFolder"?>
<?endif?>
<?if $(var.WIREGUARD_PLATFORM) = "amd64"?>
<?define UpgradeCode = "5e5a1da5-ba36-404d-92ec-41050d1c799c"?>
<?elseif $(var.WIREGUARD_PLATFORM) = "x86"?>
<?define UpgradeCode = "62754a0a-fee9-4412-b739-e8da2e7c9405"?>
+<?elseif $(var.WIREGUARD_PLATFORM) = "arm"?>
+ <?define UpgradeCode = "f90bca59-9627-431d-92b4-a5c2d9a529ff"?>
+<?elseif $(var.WIREGUARD_PLATFORM) = "arm64"?>
+ <?define UpgradeCode = "7ff76099-8940-4d3e-99b9-50a3b3ca1ee9"?>
<?else?>
<?error Unknown platform ?>
<?endif?>
@@ -27,7 +31,7 @@
Manufacturer="WireGuard LLC"
UpgradeCode="$(var.UpgradeCode)">
<Package
- InstallerVersion="400"
+ InstallerVersion="500"
Compressed="yes"
InstallScope="perMachine"
Description="WireGuard: Fast, Modern, Secure VPN Tunnel"
@@ -53,7 +57,8 @@
AllowDowngrades="no"
AllowSameVersionUpgrades="yes"
DowngradeErrorMessage="A newer version of [ProductName] is already installed."
- Schedule="afterInstallExecute" />
+ Schedule="afterInstallExecute"
+ IgnoreRemoveFailure="yes" />
<!--
Folders
@@ -63,10 +68,6 @@
<Directory Id="WireGuardFolder" Name="WireGuard" />
</Directory>
<Directory Id="ProgramMenuFolder" />
- <Directory Id="SystemFolder" />
- <?if $(var.WIREGUARD_PLATFORM) != "x86"?>
- <Directory Id="System64Folder" />
- <?endif?>
</Directory>
<!--
@@ -79,32 +80,26 @@
</File>
<ServiceControl Id="DummyService.3AA0C492_29F4_4342_B608_DB95B2DECB13" Name="DummyService.3AA0C492_29F4_4342_B608_DB95B2DECB13" /><!-- A dummy to make WiX create ServiceControl table for us. -->
</Component>
- <Component Directory="SystemFolder" Win64="no" Id="Wg32Executable" Guid="5ca31841-97d8-4614-a318-f1e268135ba7">
- <File Source="..\x86\wg.exe" Id="Wg32Executable" />
- </Component>
- <?if $(var.WIREGUARD_PLATFORM) != "x86"?>
- <Component Directory="System64Folder" Win64="yes" Id="Wg64Executable" Guid="d9b494ec-0959-442c-89ad-6aa175acfd03">
- <File Source="..\$(var.WIREGUARD_PLATFORM)\wg.exe" Id="Wg64Executable" />
+ <Component Directory="WireGuardFolder" Id="WgExecutable" Guid="540cf446-fcc3-4452-b9fb-eb4c02780251">
+ <File Source="..\$(var.WIREGUARD_PLATFORM)\wg.exe" KeyPath="yes" />
+ <Environment Id="PATH" Name="PATH" System="yes" Action="set" Part="last" Permanent="no" Value="[WireGuardFolder]" />
</Component>
- <?endif?>
</ComponentGroup>
<!--
- Merge modules
- -->
- <DirectoryRef Id="WireGuardFolder">
- <Merge Id="WintunMergeModule" Language="0" DiskId="1" SourceFile=".deps\wintun-$(var.WIREGUARD_PLATFORM).msm" />
- </DirectoryRef>
-
- <!--
Features
-->
<Feature Id="WireGuardFeature" Title="WireGuard" Level="1">
<ComponentGroupRef Id="WireGuardComponents" />
</Feature>
- <Feature Id="WintunFeature" Title="Wintun" Level="1">
- <MergeRef Id="WintunMergeModule" />
- </Feature>
+
+ <!--
+ Abort early if running under Wow64
+ -->
+ <CustomAction Id="CheckWow64" BinaryKey="customactions.dll" DllEntry="CheckWow64" />
+ <InstallExecuteSequence>
+ <Custom Action="CheckWow64" After="FindRelatedProducts">NOT REMOVE</Custom>
+ </InstallExecuteSequence>
<!--
Evaluate WireGuard services and populate ServiceControl table
@@ -115,11 +110,19 @@
</InstallExecuteSequence>
<!--
- Clear out our config folder on uninstall
+ Launch wireguard.exe on product reconfiguration (starting same MSI again)
-->
- <CustomAction Id="RemoveConfigFolder" BinaryKey="customactions.dll" DllEntry="RemoveConfigFolder" Execute="deferred" Impersonate="no" />
+ <CustomAction Id="LaunchApplicationAndAbort" BinaryKey="customactions.dll" DllEntry="LaunchApplicationAndAbort" />
+ <InstallExecuteSequence>
+ <Custom Action="LaunchApplicationAndAbort" After="CostFinalize">ProductState=5 AND NOT REMOVE AND NOT DO_NOT_LAUNCH</Custom>
+ </InstallExecuteSequence>
+
+ <!--
+ Evaluate WireGuard components
+ -->
+ <CustomAction Id="EvaluateWireGuardComponents" BinaryKey="customactions.dll" DllEntry="EvaluateWireGuardComponents" />
<InstallExecuteSequence>
- <Custom Action="RemoveConfigFolder" After="DeleteServices">(NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")</Custom>
+ <Custom Action="EvaluateWireGuardComponents" After="ProcessComponents" />
</InstallExecuteSequence>
<!--
@@ -131,19 +134,27 @@
</InstallExecuteSequence>
<!--
- Launch wireguard.exe after setup complete
+ Clear out our config folder on uninstall
-->
- <CustomAction Id="LaunchApplication" HideTarget="yes" Impersonate="no" Execute="deferred" FileKey="wireguard.exe" ExeCommand="" Return="asyncNoWait" />
+ <CustomAction Id="RemoveConfigFolder" BinaryKey="customactions.dll" DllEntry="RemoveConfigFolder" Execute="deferred" Impersonate="no" />
<InstallExecuteSequence>
- <Custom Action="LaunchApplication" Before="InstallFinalize">(&amp;WireGuardFeature = 3) AND NOT DO_NOT_LAUNCH</Custom>
+ <Custom Action="RemoveConfigFolder" After="DeleteServices" />
</InstallExecuteSequence>
<!--
- Launch wireguard.exe on product reconfiguration (starting same MSI again)
+ Clear out our adapters on uninstall
-->
- <CustomAction Id="LaunchApplicationAsOrdinaryUser" HideTarget="yes" FileKey="wireguard.exe" ExeCommand="" Return="asyncNoWait" />
+ <CustomAction Id="RemoveAdapters" BinaryKey="customactions.dll" DllEntry="RemoveAdapters" Execute="deferred" Impersonate="no" />
<InstallExecuteSequence>
- <Custom Action="LaunchApplicationAsOrdinaryUser" After="InstallFinalize">(&amp;WireGuardFeature = -1) AND (!WireGuardFeature = 3) AND NOT DO_NOT_LAUNCH</Custom>
+ <Custom Action="RemoveAdapters" Before="RemoveFiles" />
+ </InstallExecuteSequence>
+
+ <!--
+ Launch wireguard.exe after setup complete
+ -->
+ <CustomAction Id="LaunchApplication" HideTarget="yes" Impersonate="no" Execute="deferred" FileKey="wireguard.exe" ExeCommand="" Return="asyncNoWait" />
+ <InstallExecuteSequence>
+ <Custom Action="LaunchApplication" Before="InstallFinalize">(&amp;WireGuardFeature = 3) AND NOT DO_NOT_LAUNCH</Custom>
</InstallExecuteSequence>
</Product>
</Wix>