diff options
Diffstat (limited to 'installer')
-rw-r--r-- | installer/.gitignore | 2 | ||||
-rw-r--r-- | installer/build.bat | 26 | ||||
-rw-r--r-- | installer/customactions.c | 357 | ||||
-rw-r--r-- | installer/fetcher/.gitignore | 6 | ||||
-rw-r--r-- | installer/fetcher/Makefile | 42 | ||||
-rw-r--r-- | installer/fetcher/constants.h | 17 | ||||
-rw-r--r-- | installer/fetcher/crypto.c | 2252 | ||||
-rw-r--r-- | installer/fetcher/crypto.h | 29 | ||||
-rw-r--r-- | installer/fetcher/fetcher.c | 340 | ||||
-rw-r--r-- | installer/fetcher/filelist.c | 168 | ||||
-rw-r--r-- | installer/fetcher/filelist.h | 17 | ||||
-rw-r--r-- | installer/fetcher/icon.svg | 92 | ||||
-rw-r--r-- | installer/fetcher/manifest.xml | 34 | ||||
-rw-r--r-- | installer/fetcher/resources.rc | 41 | ||||
-rw-r--r-- | installer/fetcher/systeminfo.c | 74 | ||||
-rw-r--r-- | installer/fetcher/systeminfo.h | 16 | ||||
-rw-r--r-- | installer/fetcher/version.h | 12 | ||||
-rw-r--r-- | installer/wireguard.wxs | 83 |
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">(&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">(&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">(&WireGuardFeature = 3) AND NOT DO_NOT_LAUNCH</Custom> </InstallExecuteSequence> </Product> </Wix> |