diff --git a/.github/workflows/dos-djgpp.yml b/.github/workflows/dos-djgpp.yml index e5d836b..1a32e32 100644 --- a/.github/workflows/dos-djgpp.yml +++ b/.github/workflows/dos-djgpp.yml @@ -23,21 +23,35 @@ name: C/C++ CI (DOS DJGPP) on: push: branches: [ "*" ] - paths-ignore: [ '**.md', 'doc/**', '.idea/**'] # If only these files are edited, skip + paths-ignore: [ '**.md', 'doc/**', '.idea/**' ] # If only these files are edited, skip + pull_request: + branches: [ "*" ] + paths-ignore: [ '**.md', 'doc/**', '.idea/**' ] # If only these files are edited, skip workflow_dispatch: -env: - CC: ${{ github.workspace }}/djgpp/bin/i586-pc-msdosdjgpp-gcc - CXX: ${{ github.workspace }}/djgpp/bin/i586-pc-msdosdjgpp-g++ - CMAKE_FIND_ROOT_PATH: ${{ github.workspace }}/djgpp - WATT_ROOT: ${{ github.workspace }}/djgpp/watt32 - jobs: build: runs-on: ubuntu-latest + env: + CC: ${{ github.workspace }}/djgpp/bin/i586-pc-msdosdjgpp-gcc + CXX: ${{ github.workspace }}/djgpp/bin/i586-pc-msdosdjgpp-g++ + CMAKE_FIND_ROOT_PATH: ${{ github.workspace }}/djgpp + GCC_EXEC_PREFIX: ${{ github.workspace }}/lib/gcc/ + DJDIR: ${{ github.workspace }}/djgpp/i586-pc-msdosdjgpp + PATH: ${{ github.workspace }}/djgpp/i586-pc-msdosdjgpp/bin/:${{ github.workspace }}/djgpp/bin/:${PATH} + steps: - name: Checkout Source Tree - uses: actions/checkout@v3 + uses: actions/checkout@v4.1.1 + + - name: Set up CPM cache + id: cache-cpm + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}/.cpm-cache + key: ${{ runner.os }}-cpm-${{ hashFiles('**/') }} + restore-keys: | + ${{ runner.os }}-cpm- - name: Setup build environment run: | @@ -46,24 +60,17 @@ jobs: - name: Download and Setup DJGPP Toolchain run: | - pushd ${{ github.workspace }} wget https://github.com/andrewwutw/build-djgpp/releases/download/v3.4/djgpp-linux64-gcc1220.tar.bz2 tar xjf djgpp-linux64-gcc1220.tar.bz2 - cd ${{ github.workspace }}/djgpp - git clone https://github.com/UMSKT/Watt-32.git watt32 - cd watt32/util - make clean && make linux - cd ../src - source ${{ github.workspace }}/djgpp/setenv - ./configur.sh djgpp - make -f djgpp.mak - ln -s ${WATT_ROOT}/lib/libwatt.a ${CMAKE_FIND_ROOT_PATH}/lib - - name: Build - run: | - source ${{ github.workspace }}/djgpp/setenv - cmake -DDJGPP_WATT32=${WATT_ROOT}/lib/libwatt.a -DCMAKE_FIND_ROOT_PATH=${CMAKE_FIND_ROOT_PATH} -DCMAKE_BUILD_TYPE=Release build/ - cmake --build build/ + - name: Build UMSKT for MSDOS via DJGPP + uses: threeal/cmake-action@v1.3.0 + with: + options: UMSKT_DJGPP_COMPILE=On CMAKE_FIND_ROOT_PATH=${CMAKE_FIND_ROOT_PATH} CMAKE_BUILD_TYPE=Release + build-dir: build + c-compiler: ${CC} + cxx-compiler: ${CXX} + build-args: -j 2 - name: Move executable to upload directory run: | @@ -71,7 +78,68 @@ jobs: mv build/umskt.exe build/actions_upload/ - name: Upload build artifact - uses: actions/upload-artifact@v3.1.2 + uses: actions/upload-artifact@v4.3.1 with: name: UMSKT-DOS path: build/actions_upload + + build-dos-win32-combined: + needs: build + runs-on: windows-latest + steps: + # https://github.com/actions/runner-images/issues/6067#issuecomment-1213069040 + - name: Install Windows XP Support for Visual Studio + run: | + Set-Location "C:\Program Files (x86)\Microsoft Visual Studio\Installer\" + $InstallPath = "C:\Program Files\Microsoft Visual Studio\2022\Enterprise" + $componentsToAdd = @( + "Microsoft.VisualStudio.Component.WinXP" + ) + [string]$workloadArgs = $componentsToAdd | ForEach-Object {" --add " + $_} + $Arguments = ('/c', "vs_installer.exe", 'modify', '--installPath', "`"$InstallPath`"",$workloadArgs, '--quiet', '--norestart', '--nocache') + $process = Start-Process -FilePath cmd.exe -ArgumentList $Arguments -Wait -PassThru -WindowStyle Hidden + if ($process.ExitCode -eq 0) + { + Write-Host "components have been successfully added" + Get-ChildItem C:\ProgramData\Microsoft\VisualStudio\Packages\Microsoft.Windows.XPSupport.* + } + else + { + Write-Host "components were not installed" + exit 1 + } + + - name: Setup MSBuild + uses: microsoft/setup-msbuild@v1 + + - name: Checkout Source Tree + uses: actions/checkout@v4.1.1 + + - name: Set up CPM cache + id: cache-cpm + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}/.cpm-cache + key: ${{ runner.os }}-cpm-${{ hashFiles('**/') }} + restore-keys: | + ${{ runner.os }}-cpm- + + - name: Download Built DOS Artifact + uses: actions/download-artifact@v4.1.2 + with: + name: UMSKT-DOS + + - name: Configure and build UMSKT for DOS+Win32 + uses: threeal/cmake-action@v1.3.0 + with: + options: UMSKT_MSVC_WINXP=On CMAKE_BUILD_TYPE=Release CPM_SOURCE_CACHE=${{ github.workspace }}/.cpm-cache UMSKT_MSVC_MSDOS_STUB=${{ github.workspace }}/umskt.exe + generator: "Visual Studio 17 2022" + args: -A "Win32" -T "v141_xp" + run-build: true + build-args: "--config Release -j 2" + + - name: Upload build artifact + uses: actions/upload-artifact@v4.3.1 + with: + name: UMSKT-DOS+Win32 + path: build/Release \ No newline at end of file diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 984b8c7..436d48b 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -17,46 +17,58 @@ # # @FileCreated by techguy16 on 07/23/2023 # @Maintainer techguy16 - - name: C/C++ CI (FreeBSD) - - on: + +name: C/C++ CI (FreeBSD) + +on: push: branches: [ "*" ] - paths-ignore: [ '**.md', 'doc/**', '.idea/**'] # If only these files are edited, skip + paths-ignore: [ '**.md', 'doc/**', '.idea/**' ] # If only these files are edited, skip pull_request: branches: [ "*" ] - paths-ignore: [ '**.md', 'doc/**', '.idea/**'] # If only these files are edited, skip - + paths-ignore: [ '**.md', 'doc/**', '.idea/**' ] # If only these files are edited, skip workflow_dispatch: - - jobs: - build: - runs-on: ubuntu-latest - name: build-x86_64 - steps: - - uses: actions/checkout@v3 - - - name: Build & Test in FreeBSD - id: test - uses: vmactions/freebsd-vm@v1 - with: - usesh: true - prepare: | - pkg install -y cmake git bash - - run: | - mkdir build - cmake -DCMAKE_BUILD_TYPE=Release build/ - cmake --build build/ - - - name: Move files to correct directory - run: | - mkdir -p build/actions_upload - mv build/umskt build/actions_upload/umskt - - - name: Upload build artifact - uses: actions/upload-artifact@v3.1.2 - with: - name: UMSKT-FreeBSD - path: build/actions_upload + +jobs: + build: + runs-on: ubuntu-latest + name: build-x86_64 + + steps: + - name: Checkout Source Tree + uses: actions/checkout@v4.1.1 + + - name: Set up CPM cache + id: cache-cpm + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}/.cpm-cache + key: ${{ runner.os }}-cpm-${{ hashFiles('**/') }} + restore-keys: | + ${{ runner.os }}-cpm- + + - name: Build & Test in FreeBSD + id: test + uses: vmactions/freebsd-vm@v1.0.6 + with: + usesh: true + prepare: | + pkg install -y cmake git bash + + run: | + mkdir build + cmake -DCMAKE_BUILD_TYPE=Release -DCPM_SOURCE_CACHE= ${{ github.workspace }}/.cpm-cache -DBUILD_TESTING=On -B build/ + cmake --build build/ -j 2 + cd build + ctest + + - name: Move files to correct directory + run: | + mkdir -p build/actions_upload + mv build/umskt build/actions_upload/umskt + + - name: Upload build artifact + uses: actions/upload-artifact@v4.3.1 + with: + name: UMSKT-FreeBSD + path: build/actions_upload diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 2878505..44a9a88 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -23,7 +23,10 @@ name: C/C++ CI (Linux) on: push: branches: [ "*" ] - paths-ignore: [ '**.md', 'doc/**', '.idea/**'] # If only these files are edited, skip + paths-ignore: [ '**.md', 'doc/**', '.idea/**' ] # If only these files are edited, skip + pull_request: + branches: [ "*" ] + paths-ignore: [ '**.md', 'doc/**', '.idea/**' ] # If only these files are edited, skip workflow_dispatch: jobs: @@ -35,36 +38,53 @@ jobs: - x86 - x86_64 - aarch64 + - armhf + - armv7 + - ppc64le + - riscv64 + - s390x + steps: - - name: Checkout Source Tree - uses: actions/checkout@v3 + - name: Checkout Source Tree + uses: actions/checkout@v4.1.1 - - name: Setup latest Alpine Linux for ${{ matrix.arch }} - uses: jirutka/setup-alpine@v1 - with: - packages: > - bash - build-base - cmake - git - musl-dev - arch: ${{ matrix.arch }} - shell-name: alpine-target.sh + - name: Set up CPM cache + id: cache-cpm + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}/.cpm-cache + key: ${{ runner.os }}-cpm-${{ hashFiles('**/') }} + restore-keys: | + ${{ runner.os }}-cpm- - - name: Configure and build UMSKT - uses: threeal/cmake-action@v1.3.0 - with: - options: MUSL_STATIC=ON CMAKE_BUILD_TYPE=Release - run-build: true - shell: alpine-target.sh {0} + - name: Setup latest Alpine Linux for ${{ matrix.arch }} + uses: jirutka/setup-alpine@v1 + with: + packages: > + bash + build-base + cmake + git + musl-dev + arch: ${{ matrix.arch }} + shell-name: alpine-target.sh - - name: Move files to correct directory - run: | + - name: Configure and build UMSKT + uses: threeal/cmake-action@v1.3.0 + with: + options: UMSKT_MUSL_STATIC=ON CMAKE_BUILD_TYPE=Release CPM_SOURCE_CACHE=${{ github.workspace }}/.cpm-cache BUILD_TESTING=On + run-build: true + build-args: -j 2 + run-test: true + shell: alpine-target.sh {0} + + - name: Move files to correct directory + run: | mkdir -p build/actions_upload mv build/umskt build/actions_upload/umskt - - name: Upload build artifact - uses: actions/upload-artifact@v3.1.2 - with: - name: UMSKT-linux-${{ matrix.arch }}-static - path: build/actions_upload + - name: Upload build artifact + uses: actions/upload-artifact@v4.3.1 + with: + name: UMSKT-linux-${{ matrix.arch }}-static + path: build/actions_upload diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 548757e..5fd16b0 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -23,7 +23,10 @@ name: C/C++ CI (macOS) on: push: branches: [ "*" ] - paths-ignore: [ '**.md', 'doc/**', '.idea/**'] # If only these files are edited, skip + paths-ignore: [ '**.md', 'doc/**', '.idea/**' ] # If only these files are edited, skip + pull_request: + branches: [ "*" ] + paths-ignore: [ '**.md', 'doc/**', '.idea/**' ] # If only these files are edited, skip workflow_dispatch: jobs: @@ -32,17 +35,29 @@ jobs: strategy: matrix: arch: - - { name: arm64;x86_64, displayName: all-x86_64_arm64} + - { name: arm64;x86_64, displayName: all-x86_64_arm64 } - { name: x86_64, displayName: x86_64 } - { name: arm64, displayName: arm64 } steps: - name: Checkout Source Tree - uses: actions/checkout@v3 + uses: actions/checkout@v4.1.1 - - name: Configure and build UMSKT ${{ matrix.arch.displayName }} - run: | - cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch.name }} build/ - cmake --build build/ + - name: Set up CPM cache + id: cache-cpm + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}/.cpm-cache + key: ${{ runner.os }}-cpm-${{ hashFiles('**/') }} + restore-keys: | + ${{ runner.os }}-cpm- + + - name: Configure and build UMSKT for ${{ matrix.arch.displayName }} + uses: threeal/cmake-action@v1.3.0 + with: + options: MUSL_STATIC=ON CMAKE_BUILD_TYPE=Release CPM_SOURCE_CACHE=${{ github.workspace }}/.cpm-cache CMAKE_OSX_ARCHITECTURES=${{ matrix.arch.name }} BUILD_TESTING=On + run-build: true + build-args: -j 2 + run-test: true - name: Move files to correct directory run: | @@ -50,7 +65,7 @@ jobs: mv build/umskt build/actions_upload/umskt - name: Upload build artifact - uses: actions/upload-artifact@v3.1.2 + uses: actions/upload-artifact@v4.3.1 with: name: UMSKT-macOS-${{ matrix.arch.displayName }} path: build/actions_upload diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 0f149d2..620b4b7 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -24,11 +24,17 @@ on: push: branches: [ "*" ] paths-ignore: [ '**.md', 'doc/**', '.idea/**'] # If only these files are edited, skip + pull_request: + branches: [ "*" ] + paths-ignore: [ '**.md', 'doc/**', '.idea/**'] # If only these files are edited, skip workflow_dispatch: jobs: - prepare: + build: runs-on: windows-latest + strategy: + matrix: + arch: [ { name: Win32, displayName: x86 }, { name: x64, displayName: x64 } ] steps: # https://github.com/actions/runner-images/issues/6067#issuecomment-1213069040 - name: Install Windows XP Support for Visual Studio @@ -52,30 +58,33 @@ jobs: exit 1 } + - name: Set up CPM cache + id: cache-cpm + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}/.cpm-cache + key: ${{ runner.os }}-cpm-${{ hashFiles('**/') }} + restore-keys: | + ${{ runner.os }}-cpm- + - name: Checkout Source Tree - uses: actions/checkout@v3 + uses: actions/checkout@v4.1.1 - name: Setup MSBuild uses: microsoft/setup-msbuild@v1 - build: - runs-on: windows-latest - needs: prepare - strategy: - matrix: - arch: [ { name: Win32, displayName: x86 }, { name: x64, displayName: x64} ] - steps: - name: Configure and build UMSKT for ${{ matrix.values.displayName }} uses: threeal/cmake-action@v1.3.0 with: - options: CMAKE_BUILD_TYPE=Release + options: CMAKE_BUILD_TYPE=Release CPM_SOURCE_CACHE=${{ github.workspace }}/.cpm-cache BUILD_TESTING=On generator: "Visual Studio 17 2022" args: -A "${{ matrix.arch.name }}" -T "v141_xp" run-build: true - build-args: "--config Release" + build-args: --config Release -j 2 + run-test: true - name: Upload build artifact - uses: actions/upload-artifact@v3.1.2 + uses: actions/upload-artifact@v4.3.1 with: name: UMSKT-${{ matrix.values.displayName }} - path: build/Release + path: build/Release \ No newline at end of file diff --git a/.gitignore b/.gitignore index 30a6828..d6d1a16 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ build*/ *.exe *.wasm umskt +.cpm-cache ### NotepadPP template # Notepad++ backups # diff --git a/CMakeLists.txt b/CMakeLists.txt index bf223fd..5f38dff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,48 +19,55 @@ # @Maintainer Neo CMAKE_MINIMUM_REQUIRED(VERSION 3.12) -PROJECT(UMSKT) +SET(PROJECT_NAME UMSKT) +PROJECT(${PROJECT_NAME}) SET(CMAKE_CXX_STANDARD 17) SET(CMAKE_OSX_SYSROOT "macosx" CACHE PATH "macOS SDK path") SET(CMAKE_POSITION_INDEPENDENT_CODE ON) -OPTION(BUILD_SHARED_LIBS "Build all libraries as shared" OFF) -OPTION(MUSL_STATIC "Enable fully static builds in a muslc environment (i.e. Alpine Linux)" OFF) -OPTION(DJGPP_WATT32 "Enable compilation and linking with DJGPP/WATT32" OFF) -OPTION(MSVC_MSDOS_STUB "Specify a custom MS-DOS stub for a 32-bit MSVC compilation" OFF) +OPTION(BUILD_TESTING "Build testing binaries for CTest" OFF) +OPTION(BUILD_SHARED_LIBS "Build all dependant libraries as shared" OFF) -SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS}) -SET(UMSKT_LINK_DIRS ${UMSKT_LINK_DIRS}) +OPTION(UMSKT_BUILD_SHARED_LIB "Build libumskt.so/.dll/.dylib" ON) +OPTION(UMSKT_BUILD_STATIC_LIB "Build libumskt_static.a/.lib" ON) +OPTION(UMSKT_MUSL_STATIC "Enable fully static builds in a muslc environment (i.e. Alpine Linux)" OFF) +OPTION(UMSKT_DJGPP_COMPILE "Enable compilation and linking with DJGPP" OFF) +OPTION(UMSKT_MSVC_MSDOS_STUB "Specify a custom MS-DOS stub for a 32-bit MSVC compilation" OFF) +OPTION(UMSKT_MSVC_WINXP "Specify compile-time flag overrides for Windows XP" OFF) IF (NOT MSVC) SET(CMAKE_CXX_FLAGS "-Os -fdata-sections -ffunction-sections -flto=auto -Wl,--gc-sections") SET(CMAKE_CXX_FLAGS_DEBUG "-g") - SET(CMAKE_CXX_FLAGS_DEBUG_INIT "-Wall -Wextra") + SET(CMAKE_CXX_FLAGS_DEBUG_INIT "-Wall -Wextra -pedantic") SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-g") - SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT "-Wall -Wextra") - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") + SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT "-Wall -Wextra -pedantic") + SET(CMAKE_CXX_FLAGS_RELEASE_INIT "-s") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static -Wl,--gc-sections") ENDIF () -IF (DJGPP_WATT32) +IF (UMSKT_DJGPP_COMPILE) SET(CMAKE_SYSTEM_NAME MSDOS) SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) SET(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) - SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS} ${DJGPP_WATT32}) - SET(UMSKT_LINK_DIRS ${UMSKT_LINK_DIRS} ${WATT_ROOT}/lib) + SET(UMSKT_BUILD_SHARED_LIB OFF) + SET(UMSKT_BUILD_STATIC_LIB OFF) + SET(CMAKE_POSITION_INDEPENDENT_CODE OFF) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-attributes -march=i386 -mtune=i386") + MESSAGE(STATUS "[UMSKT] DJGPP Build requested - Shared libraries forced OFF") ENDIF () IF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") SET(BUILD_SHARED_LIBS ON) - MESSAGE(STATUS "[UMSKT] macOS has no static library - Shared library forced on") + MESSAGE(STATUS "[UMSKT] macOS Build requested - Shared libraries forced ON") ENDIF () # if we're compiling with MSVC, respect the DEBUG compile option IF (MSVC) SET(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") - SET(CMAKE_CXX_FLAGS "/O1 /GL /LTCG /Gy /OPT:REF /OPT:ICF /EHsc") + SET(CMAKE_CXX_FLAGS "/Os /LTCG /Gy /OPT:REF /OPT:ICF /EHsc") SET(CMAKE_CXX_FLAGS_RELEASE "/MT") SET(CMAKE_CXX_FLAGS_DEBUG "/MTd /Z7") SET(CMAKE_EXE_LINKER_FLAGS "/INCREMENTAL:NO /NODEFAULTLIB:MSVCRT /LTCG") @@ -68,16 +75,18 @@ IF (MSVC) SET(CMAKE_ENABLE_EXPORTS ON) SET(UMSKT_EXE_WINDOWS_EXTRA src/windows/umskt.rc) SET(UMSKT_EXE_WINDOWS_DLL src/windows/dllmain.cpp) + IF (UMSKT_MSVC_WINXP) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /U_WIN32_WINNT /DWINVER=0x0501 /D_WIN32_WINNT=0x0501") + ENDIF () ENDIF () IF (MUSL_STATIC) - MESSAGE(STATUS "[UMSKT] Performing a fully static build using muslc") + MESSAGE(STATUS "[UMSKT] musl libc Build requested - Shared librares forced OFF") SET(BUILD_SHARED_LIBS OFF) - SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a") SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static-libgcc -static-libstdc++") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++") - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static -static-libgcc -static-libstdc++") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++") ENDIF () # initialize cpm.CMake @@ -88,6 +97,7 @@ FILE( EXPECTED_HASH SHA256=cc155ce02e7945e7b8967ddfaff0b050e958a723ef7aad3766d368940cb15494 ) INCLUDE(${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake) +CPMUsePackageLock(${CMAKE_SOURCE_DIR}/package-lock.cmake) # fetch cpm.CMake dependencies # Include JSON development library @@ -113,23 +123,23 @@ CPMAddPackage( VERSION 2.0.1 ) +# Include stdint128 +CPMAddPackage( + NAME stdint128 + GITHUB_REPOSITORY UMSKT/stdint128-cmake + GIT_TAG v1.0.0 + VERSION 1.0.0 +) + # Include Crypto++ development library CPMAddPackage( NAME cryptopp-cmake - GITHUB_REPOSITORY abdes/cryptopp-cmake + GITHUB_REPOSITORY UMSKT/cryptopp-cmake GIT_TAG CRYPTOPP_8_9_0 VERSION 8.9.0 OPTIONS "CRYPTOPP_BUILD_TESTING OFF" ) -#include googletest unit testing library -#CPMAddPackage( -# NAME googletest -# GITHUB_REPOSITORY google/googletest -# VERSION 1.13.0 -# OPTIONS "INSTALL_GTEST OFF" "gtest_force_shared_crt" -#) - ### Resource compilation CMRC_ADD_RESOURCE_LIBRARY(umskt-rc ALIAS umskt::rc NAMESPACE umskt keys.json) @@ -137,8 +147,8 @@ SET(LIBUMSKT_PIDGEN2 src/libumskt/pidgen2/PIDGEN2.cpp) SET(LIBUMSKT_PIDGEN3 src/libumskt/pidgen3/PIDGEN3.cpp src/libumskt/pidgen3/BINK1998.cpp src/libumskt/pidgen3/BINK2002.cpp) SET(LIBUMSKT_CONFID src/libumskt/confid/confid.cpp src/libumskt/confid/polynomial.cpp src/libumskt/confid/residue.cpp src/libumskt/confid/divisor.cpp) SET(LIBUMSKT_SRC src/libumskt/libumskt.cpp src/libumskt/init.cpp src/libumskt/pidgen.cpp ${LIBUMSKT_PIDGEN2} ${LIBUMSKT_PIDGEN3} ${LIBUMSKT_CONFID}) -SET(UMSKT_CLI_SRC src/main.cpp src/help.cpp src/cli.cpp src/generate.cpp) -SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS} fmt cryptopp) +SET(UMSKT_CLI_SRC src/main.cpp src/cli/help.cpp src/cli/cli.cpp src/cli/confirmationid.cpp src/cli/options.cpp src/cli/pidgen.cpp) +SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS} fmt cryptopp stdint128) IF (NOT MSVC) SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS} umskt::rc) @@ -150,7 +160,7 @@ ENDIF() #### Separate Build Path for emscripten IF (EMSCRIPTEN) ADD_EXECUTABLE(umskt ${UMSKT_CLI_SRC} ${LIBUMSKT_SRC}) - TARGET_INCLUDE_DIRECTORIES(umskt PUBLIC ${CMAKE_BINARY_DIR}) + TARGET_INCLUDE_DIRECTORIES(umskt PUBLIC ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR}) TARGET_LINK_DIRECTORIES(umskt PUBLIC ${UMSKT_LINK_DIRS}) TARGET_LINK_LIBRARIES(umskt PUBLIC ${UMSKT_LINK_LIBS}) SET(CMAKE_EXECUTABLE_SUFFIX ".html") @@ -159,47 +169,96 @@ IF (EMSCRIPTEN) SET_TARGET_PROPERTIES(umskt PROPERTIES LINK_FLAGS "-sWASM=1 -sEXPORT_ALL=1 -sEXPORTED_RUNTIME_METHODS=ccall,cwrap --no-entry") ELSE () ## umskt.so/.dll creation - ADD_LIBRARY(libumskt SHARED ${LIBUMSKT_SRC} ${UMSKT_EXE_WINDOWS_EXTRA} ${UMSKT_EXE_WINDOWS_DLL}) - TARGET_INCLUDE_DIRECTORIES(libumskt PUBLIC ${CMAKE_BINARY_DIR}) - TARGET_LINK_DIRECTORIES(libumskt PUBLIC ${UMSKT_LINK_DIRS}) - TARGET_LINK_LIBRARIES(libumskt PUBLIC ${UMSKT_LINK_LIBS}) - IF (MSVC) - SET_TARGET_PROPERTIES(libumskt PROPERTIES OUTPUT_NAME libumskt) - ELSE () - SET_TARGET_PROPERTIES(libumskt PROPERTIES OUTPUT_NAME umskt) - ENDIF () + IF (UMSKT_BUILD_SHARED_LIB) + ADD_LIBRARY(libumskt SHARED ${LIBUMSKT_SRC} ${UMSKT_EXE_WINDOWS_EXTRA} ${UMSKT_EXE_WINDOWS_DLL}) + TARGET_INCLUDE_DIRECTORIES(libumskt PUBLIC ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR}) + TARGET_LINK_DIRECTORIES(libumskt PUBLIC ${UMSKT_LINK_DIRS}) + TARGET_LINK_LIBRARIES(libumskt PUBLIC ${UMSKT_LINK_LIBS}) + IF (MSVC) + SET_TARGET_PROPERTIES(libumskt PROPERTIES OUTPUT_NAME libumskt) + ELSE () + SET_TARGET_PROPERTIES(libumskt PROPERTIES OUTPUT_NAME umskt) + ENDIF () + ENDIF() ## umskt_static.a/.lib creation - ADD_LIBRARY(libumskt_static ${LIBUMSKT_SRC} ${UMSKT_EXE_WINDOWS_EXTRA} ${UMSKT_EXE_WINDOWS_DLL}) - TARGET_INCLUDE_DIRECTORIES(libumskt_static PUBLIC ${CMAKE_BINARY_DIR}) - TARGET_LINK_DIRECTORIES(libumskt_static PUBLIC ${UMSKT_LINK_DIRS}) - TARGET_LINK_LIBRARIES(libumskt_static PUBLIC ${UMSKT_LINK_LIBS}) - IF (MSVC) - SET_TARGET_PROPERTIES(libumskt_static PROPERTIES OUTPUT_NAME libumskt_static) - ELSE () - SET_TARGET_PROPERTIES(libumskt_static PROPERTIES OUTPUT_NAME umskt_static) - ENDIF () + IF (UMSKT_BUILD_STATIC_LIB) + ADD_LIBRARY(libumskt_static ${LIBUMSKT_SRC} ${UMSKT_EXE_WINDOWS_EXTRA} ${UMSKT_EXE_WINDOWS_DLL}) + TARGET_INCLUDE_DIRECTORIES(libumskt_static PUBLIC ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR}) + TARGET_LINK_DIRECTORIES(libumskt_static PUBLIC ${UMSKT_LINK_DIRS}) + TARGET_LINK_LIBRARIES(libumskt_static PUBLIC ${UMSKT_LINK_LIBS}) + IF (MSVC) + SET_TARGET_PROPERTIES(libumskt_static PROPERTIES OUTPUT_NAME libumskt_static) + ELSE () + SET_TARGET_PROPERTIES(libumskt_static PROPERTIES OUTPUT_NAME umskt_static) + ENDIF () + ENDIF() ### UMSKT executable compilation - IF (NOT BUILD_SHARED_LIBS) - ## Link against the static lib - ADD_EXECUTABLE(umskt ${UMSKT_CLI_SRC} ${UMSKT_EXE_WINDOWS_EXTRA}) - TARGET_LINK_LIBRARIES(umskt PUBLIC libumskt_static ${UMSKT_LINK_LIBS} nlohmann_json::nlohmann_json) - ELSE () - ## Link against the dynamic lib - ADD_EXECUTABLE(umskt ${UMSKT_CLI_SRC} ${UMSKT_EXE_WINDOWS_EXTRA}) - TARGET_LINK_LIBRARIES(umskt PUBLIC libumskt ${UMSKT_LINK_LIBS} nlohmann_json::nlohmann_json) - ENDIF() - TARGET_INCLUDE_DIRECTORIES(umskt PUBLIC ${CMAKE_BINARY_DIR}) - TARGET_LINK_DIRECTORIES(umskt PUBLIC ${UMSKT_LINK_DIRS}) - IF (MSVC) - SET_PROPERTY(TARGET umskt APPEND PROPERTY COMPILE_FLAGS /DUMSKT_CLI_WINRC_EMBED_JSON) - IF (MSVC_MSDOS_STUB) - SET_PROPERTY(TARGET umskt APPEND PROPERTY LINK_FLAGS /STUB:${MSVC_MSDOS_STUB}) + IF (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + ### only build CLI if we're building UMSKT explicitly + IF (UMSKT_BUILD_STATIC_LIB) + ## Link against the static lib + ADD_EXECUTABLE(umskt ${UMSKT_CLI_SRC} ${UMSKT_EXE_WINDOWS_EXTRA}) + TARGET_LINK_LIBRARIES(umskt PUBLIC libumskt_static ${UMSKT_LINK_LIBS} nlohmann_json::nlohmann_json) + ELSEIF (UMSKT_BUILD_SHARED_LIB) + ## Link against the dynamic lib + ADD_EXECUTABLE(umskt ${UMSKT_CLI_SRC} ${UMSKT_EXE_WINDOWS_EXTRA}) + TARGET_LINK_LIBRARIES(umskt PUBLIC libumskt ${UMSKT_LINK_LIBS} nlohmann_json::nlohmann_json) + ELSE() + ## Don't link against our output, do a full libumskt+cli compile + ADD_EXECUTABLE(umskt ${LIBUMSKT_SRC} ${UMSKT_CLI_SRC} ${UMSKT_EXE_WINDOWS_EXTRA}) + TARGET_LINK_LIBRARIES(umskt PUBLIC ${UMSKT_LINK_LIBS} nlohmann_json::nlohmann_json) ENDIF() + + TARGET_INCLUDE_DIRECTORIES(umskt PUBLIC ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR}) + TARGET_LINK_DIRECTORIES(umskt PUBLIC ${UMSKT_LINK_DIRS}) + IF (MSVC) + SET_PROPERTY(TARGET umskt APPEND PROPERTY COMPILE_FLAGS /DUMSKT_CLI_WINRC_EMBED_JSON) + IF (MSVC_MSDOS_STUB) + SET_PROPERTY(TARGET umskt APPEND PROPERTY LINK_FLAGS /STUB:${MSVC_MSDOS_STUB}) + ENDIF() + ENDIF () + + IF (${CMAKE_SYSTEM_NAME} MATCHES "Linux") + INSTALL(TARGETS umskt DESTINATION bin) + ENDIF () ENDIF () - IF (${CMAKE_SYSTEM_NAME} MATCHES "Linux") - INSTALL(TARGETS umskt DESTINATION bin) - ENDIF () + ### Strip the built binary + IF (${CMAKE_BUILD_TYPE} MATCHES "Release" AND NOT MSVC) + ADD_CUSTOM_COMMAND(TARGET umskt POST_BUILD + COMMAND strip $ + COMMENT "Stripping symbols for release" + ) + ENDIF() +ENDIF () + + +#### Build Path for Unit Tests +IF ((CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME OR UMSKT_BUILD_TESTING) AND BUILD_TESTING) + INCLUDE(CTest) + ENABLE_TESTING() + + #include googletest unit testing library + CPMAddPackage( + NAME googletest + GITHUB_REPOSITORY google/googletest + VERSION 1.14.0 + OPTIONS "INSTALL_GTEST OFF" "gtest_force_shared_crt" + ) + + MACRO(ADD_GTEST TEST) + ADD_EXECUTABLE(${TEST} ${ARGN}) + TARGET_LINK_LIBRARIES(${TEST} gtest gmock gtest_main libumskt_static) + TARGET_COMPILE_FEATURES(${TEST} PRIVATE cxx_std_17) + SET_TARGET_PROPERTIES(${TEST} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/tests) + ADD_TEST(${TEST} tests/${TEST}) + ENDMACRO() + + ADD_GTEST(TEST_PIDGEN2 src/libumskt/pidgen2/PIDGEN2_unittest.cpp) + ADD_GTEST(TEST_PIDGEN3_BINK1998 src/libumskt/pidgen3/BINK1998_unittest.cpp) + ADD_GTEST(TEST_PIDGEN3_BINK2002 src/libumskt/pidgen3/BINK2002_unittest.cpp) + ADD_GTEST(TEST_CONFIRMATIONID src/libumskt/confid/confid_unittest.cpp) + ENDIF () diff --git a/Dockerfile b/Dockerfile index a983077..86de9c1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,9 +27,7 @@ RUN apk add --no-cache \ build-base \ cmake \ git \ - musl-dev \ - openssl-dev \ - openssl-libs-static + musl-dev # Stage 2: Build @@ -40,15 +38,15 @@ COPY . /src # Build UMSKT from the local directory RUN mkdir /src/build \ - && cmake -B /src/build -DMUSL_STATIC=ON \ + && cmake -B /src/build -DCPM_SOURCE_CACHE=/src/.cpm-cache -DCMAKE_BUILD_TYPE=Release -DUMSKT_MUSL_STATIC=ON \ && cmake --build /src/build -j 10 # Stage 3: Output FROM scratch as output -COPY --from=builder /src/build/umskt /umskt -COPY --from=builder /src/build/libumskt.a /umskt -COPY --from=builder /src/build/libumskt.so /umskt +COPY --from=builder /src/build/umskt / +COPY --from=builder /src/build/libumskt_static.a / +COPY --from=builder /src/build/libumskt.so / # invoke via -# docker build -o type=tar,dest=umskt.tar . +# docker build -o type=tar,dest=build-musl/umskt.tar . diff --git a/Dockerfile.djgpp b/Dockerfile.djgpp index 6cff3d4..0290372 100644 --- a/Dockerfile.djgpp +++ b/Dockerfile.djgpp @@ -19,67 +19,55 @@ # @Maintainer Neo # Stage 1: Install Prerequisites -#FROM ubuntu:latest as prerequisites +FROM ubuntu:latest as prerequisites # Stage 1: Install build dependencies -#RUN apt-get update && apt-get -y install \ -# build-essential \ -# cmake \ -# wget \ -# 7zip \ -# git \ -# flex \ -# libfl-dev \ -# nasm \ -# libslang2-dev \ -# pkg-config \ -# libslang2-modules \ -# gcc-multilib \ -# && rm -rf /var/lib/apt/lists/* +RUN apt-get update && apt-get -y install \ + build-essential \ + cmake \ + wget \ + 7zip \ + git \ + flex \ + libfl-dev \ + nasm \ + libslang2-dev \ + pkg-config \ + libslang2-modules \ + gcc-multilib \ + && rm -rf /var/lib/apt/lists/* - -FROM djgpp-prerequisites:latest as djgpp-watt32 +FROM prerequisites as djgpp SHELL ["/bin/bash", "-c"] WORKDIR / -ENV CC=/djgpp/bin/i586-pc-msdosdjgpp-gcc CXX=/djgpp/bin/i586-pc-msdosdjgpp-g++ CMAKE_FIND_ROOT_PATH=/djgpp WATT_ROOT=/djgpp/watt32 +ENV CC=/djgpp/bin/i586-pc-msdosdjgpp-gcc CXX=/djgpp/bin/i586-pc-msdosdjgpp-g++ CMAKE_FIND_ROOT_PATH=/djgpp -# Stage 2: compile WATT32 for D +# Stage 2: setup djgpp RUN wget https://github.com/andrewwutw/build-djgpp/releases/download/v3.4/djgpp-linux64-gcc1220.tar.bz2 \ && tar xjf djgpp-linux64-gcc1220.tar.bz2 \ - && rm -rf djgpp-linux64-gcc1220.tar.bz2 \ - && cd djgpp \ - && git clone https://github.com/UMSKT/Watt-32.git watt32 \ - && cd watt32/util \ - && make clean && make linux \ - && cd ../src \ - && source /djgpp/setenv \ - && ./configur.sh djgpp \ - && make -f djgpp.mak \ - && ln -s ${WATT_ROOT}/lib/libwatt.a ${CMAKE_FIND_ROOT_PATH}/lib + && rm -rf djgpp-linux64-gcc1220.tar.bz2 # Stage 3: compile UMSKT -FROM djgpp-watt32 as build +FROM djgpp as build SHELL ["/bin/bash", "-c"] WORKDIR /src COPY . /src -ENV PKG_CONFIG_PATH=/djgpp/lib/pkgconfig VERBOSE=1 # Build UMSKT from the local directory RUN mkdir /src/build \ - && cd /src/build \ && source /djgpp/setenv \ - && cmake -DDJGPP_WATT32=${WATT_ROOT}/lib/libwatt.a .. \ - && make + && cmake -DUMSKT_DJGPP_COMPILE=On -DCMAKE_BUILD_TYPE=Release -DCPM_SOURCE_CACHE=`pwd`/.cpm-cache -B build \ + && cmake --build build -j 10 --verbose CMD ["bash"] # Stage 6: Output FROM scratch as output -COPY --from=build /src/build/umskt.exe /umskt.exe +COPY --from=build /src/build/umskt.exe / # invoke via # docker build -f Dockerfile.djgpp -o type=tar,dest=build-djgpp/umskt-dos.tar . diff --git a/Dockerfile.windows b/Dockerfile.windows index 5c23d1c..ede7fe5 100644 --- a/Dockerfile.windows +++ b/Dockerfile.windows @@ -23,8 +23,8 @@ # Stage 1: Install Visual Studio FROM mcr.microsoft.com/dotnet/framework/runtime:4.8.1 as visualstudio # Download and install Build Tools for Visual Studio 2022 for native desktop workload. -#ADD https://aka.ms/vs/17/release/vs_buildtools.exe C:\TEMP\vs_buildtools.exe -ADD vs_buildtools.exe C:\TEMP\vs_buildtools.exe +ADD https://aka.ms/vs/17/release/vs_buildtools.exe C:\TEMP\vs_buildtools.exe +#ADD vs_buildtools.exe C:\TEMP\vs_buildtools.exe RUN C:\TEMP\vs_buildtools.exe --quiet --wait --norestart --nocache ` --add Microsoft.VisualStudio.Workload.VCTools ` --add Microsoft.VisualStudio.Workload.MSBuildTools ` @@ -40,26 +40,15 @@ RUN ["powershell.exe", "-NoLogo", "-ExecutionPolicy", "Bypass", "[System.Net.Ser RUN ["powershell.exe", "-NoLogo", "-ExecutionPolicy", "Bypass", "choco feature enable -n allowGlobalConfirmation"] RUN ["powershell.exe", "-NoLogo", "-ExecutionPolicy", "Bypass", "choco install git --params \"'/GitAndUnixToolsOnPath /WindowsTerminal /NoShellIntegration /NoGuiHereIntegration /NoShellHereIntegration /NoCredentialManager /SChannel'\""] -#Install OpenSSL 32bit 3.1.1 -#ADD https://slproweb.com/download/Win32OpenSSL-3_1_1.msi C:\TEMP\Win32OpenSSL-3_1_1.msi -ADD Win32OpenSSL-3_1_1.msi C:\TEMP\Win32OpenSSL-3_1_1.msi -RUN msiexec /i C:\TEMP\Win32OpenSSL-3_1_1.msi /quiet /qn /norestart - -#Install OpenSSL 64bit 3.1.1 -#ADD https://slproweb.com/download/Win64OpenSSL-3_1_1.msi C:\TEMP\Win64OpenSSL-3_1_1.msi -ADD Win64OpenSSL-3_1_1.msi C:\TEMP\Win64OpenSSL-3_1_1.msi -RUN msiexec /i C:\TEMP\Win64OpenSSL-3_1_1.msi /quiet /qn /norestart - # Stage 3: Build the 32-bit version of UMSKT FROM prereqisites as Build32 WORKDIR C:\umskt\ COPY . C:\umskt\ RUN C:\BuildTools\Common7\Tools\VsDevCmd.bat && ` - mkdir C:\umskt\build && ` - cd C:\umskt\build && ` - cmake -DMSVC_MSDOS_STUB:string=..\umskt.exe .. -G "Visual Studio 17 2022" -A "Win32" -T v141_xp && ` - msbuild ALL_BUILD.vcxproj /P:Configuration=Release + mkdir C:\umskt\build && cd C:\umskt && ` + cmake -B build -DCPM_SOURCE_CACHE=../.cpm-cache -DUMSKT_MSVC_WINXP=On -DCMAKE_BUILD_TYPE=Release -DMSVC_MSDOS_STUB=..\umskt.exe -G "Visual Studio 17 2022" -A "Win32" -T v141_xp && ` + cmake --build build --config Release -j 10 # Stage 4: Build the 64-bit version of UMSKT FROM prereqisites as Build64 @@ -67,12 +56,10 @@ FROM prereqisites as Build64 WORKDIR C:\umskt\ COPY . C:\umskt\ -ENV OPENSSL_ROOT_DIR="C:\Program Files\OpenSSL-Win64" RUN C:\BuildTools\Common7\Tools\VsDevCmd.bat && ` - mkdir C:\umskt\build && ` - cd C:\umskt\build && ` - cmake .. && ` - msbuild ALL_BUILD.vcxproj /P:Configuration=Release + cd C:\umskt && ` + cmake -B build -DCPM_SOURCE_CACHE=../.cpm-cache -DCMAKE_BUILD_TYPE=Release && ` + cmake --build build --config Release -j 10 # Stage 5: Copy binaries to an output/runtime image FROM mcr.microsoft.com/dotnet/framework/runtime:4.8.1 as output diff --git a/keys.json b/keys.json index bd0a497..ea55e0c 100644 --- a/keys.json +++ b/keys.json @@ -1235,21 +1235,6 @@ "y": "11647588982042777999933885074728841323429055317640349743317690400085264609368266409172384083304384956740124856614996" } }, - "0B": { - "a": "1", - "b": "0", - "g": { - "x": "17272533675023793624680016937607161394427776688401278127884215858369066406365237833207419170117031265147050748737186", - "y": "10897684556651576571671151674586120690608236542740270859915076272932083320838022698730208293779451126638581586588925" - }, - "n": "44682719955829289", - "p": "31123778862031392435299439090755153401162704597024288571183830527113563344679315725116915983118187065183839828632113", - "priv": "30177475288172038", - "pub": { - "x": "10584120526089473026246191383792758367144927589909587205278073830223938861208553884400816982485323081066790399437204", - "y": "19710761542152200618172612283139324015316083022563473705358032993141026289202915973780473937312193485361804450068338" - } - }, "0C": { "a": "1", "b": "0", diff --git a/src/cli.cpp b/src/cli.cpp deleted file mode 100644 index bfaf646..0000000 --- a/src/cli.cpp +++ /dev/null @@ -1,325 +0,0 @@ -/** - * This file is a part of the UMSKT Project - * - * Copyleft (C) 2019-2024 UMSKT Contributors (et.al.) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - * @FileCreated by Andrew on 01/06/2023 - * @Maintainer Neo - */ - -#include "cli.h" - -// define static storage -CLI::Options CLI::options; -json CLI::keys; - -/** - * - * @param argcIn - * @param argvIn - * @return exit status (where success is 0) - */ -BYTE CLI::Init(int argcIn, char **argvIn) -{ - // set default options - options.argc = argcIn; - options.argv = argvIn; - options.binkID = "2E"; - options.productCode = "WINXP"; - options.productFlavour = "VLK"; - options.numKeys = 1; - options.pidgenversion = Options::PIDGEN_VERSION::PIDGEN_3; - options.state = Options::APPLICATION_STATE::STATE_PIDGEN_GENERATE; - - SetHelpText(); - - BOOL success = parseCommandLine(); - if (!success) - { - return options.error; - } - - // if we displayed help, without an error - // return success - if (options.help) - { - return -1; - } - - success = processOptions(); - if (!success) - { - return 2; - } - - return 0; -} - -/** - * - * @param filename - * @return success - */ -BOOL CLI::loadJSON(const fs::path &filename) -{ - if (filename.empty()) - { - if (options.verbose) - { - fmt::print("Loading internal keys file\n"); - } - - auto retval = loadEmbeddedJSON(); - - if (retval && options.verbose) - { - fmt::print("Loaded internal keys file successfully\n\n"); - } - else if (!retval) - { - fmt::print("Error loading internal keys file...\n\n"); - } - return retval; - } - - if (!fs::exists(filename)) - { - fmt::print("ERROR: File {} does not exist\n", filename.string()); - return false; - } - - if (options.verbose) - { - fmt::print("Loading keys file: {}\n", options.keysFilename); - } - - std::ifstream f(filename); - try - { - keys = json::parse(f, nullptr, false, false); - } - catch (const json::exception &e) - { - fmt::print("ERROR: Exception thrown while parsing {}: {}\n", filename.string(), e.what()); - return false; - } - - if (keys.is_discarded()) - { - fmt::print("ERROR: Unable to parse keys from: {}\n", filename.string()); - return false; - } - - if (options.verbose) - { - fmt::print("Loaded keys from \"{}\" successfully\n", options.keysFilename); - } - - return true; -} - -/** - * - * @param pidgen3 - * @return success - */ -BOOL CLI::InitPIDGEN3(PIDGEN3 *p3) -{ - auto bink = keys["BINK"][options.binkID]; - - if (options.verbose) - { - fmt::print("{:->80}\n", ""); - fmt::print("Loaded the following elliptic curve parameters: BINK[{}]\n", options.binkID); - fmt::print("{:->80}\n", ""); - fmt::print("{:>6}: {}\n", "P", bink["p"]); - fmt::print("{:>6}: {}\n", "a", bink["a"]); - fmt::print("{:>6}: {}\n", "b", bink["b"]); - fmt::print("{:>6}: [{},\n{:>9}{}]\n", "G[x,y]", bink["g"]["x"], "", bink["g"]["y"]); - fmt::print("{:>6}: [{},\n{:>9}{}]\n", "K[x,y]", bink["pub"]["x"], "", bink["pub"]["y"]); - fmt::print("{:>6}: {}\n", "n", bink["n"]); - fmt::print("{:>6}: {}\n", "k", bink["priv"]); - fmt::print("\n"); - } - - p3->LoadEllipticCurve(options.binkID, bink["p"], bink["a"], bink["b"], bink["g"]["x"], bink["g"]["y"], - bink["pub"]["x"], bink["pub"]["y"], bink["n"], bink["priv"]); - - if (options.state != Options::APPLICATION_STATE::STATE_PIDGEN_GENERATE) - { - return true; - } - - if (options.channelID.IsZero()) - { - options.channelID.Randomize(UMSKT::rng, sizeof(DWORD32) * 8); - } - - options.channelID %= 999; - p3->info.ChannelID = options.channelID; - if (options.verbose) - { - fmt::print("> Channel ID: {:d}\n", options.channelID); - } - - if (options.serial.NotZero() && p3->checkFieldIsBink1998()) - { - p3->info.Serial = options.serial; - if (options.verbose) - { - fmt::print("> Serial {:d}\n", options.serial); - } - } - else if (options.serial.NotZero() && !p3->checkFieldIsBink1998()) - { - fmt::print("Warning: Discarding user-supplied serial for BINK2002\n"); - } - - return true; -} - -/** - * - * @param confid - * @return success - */ -BOOL CLI::InitConfirmationID(ConfirmationID &confid) -{ - if (!keys["products"][options.productCode].contains("meta") || - !keys["products"][options.productCode]["meta"].contains("activation")) - { - fmt::print("ERROR: product flavour \"{}\" does not have known activation values", options.productCode); - return false; - } - - auto meta = keys["products"][options.productCode]["meta"]["activation"]; - - if (!keys["activation"].contains(meta["flavour"])) - { - fmt::print("ERROR: \"{}\" is an unknown activation flavour", meta["flavour"]); - return false; - } - - auto flavour = keys["activation"][meta["flavour"]]; - - if (options.verbose) - { - fmt::print("{:->80}\n", ""); - fmt::print("Loaded the following hyperelliptic curve parameters: activation[{}]\n", meta["flavour"]); - fmt::print("{:->80}\n", ""); - fmt::print("{:>7}: {}\n", "name", flavour["name"]); - fmt::print("{:>7}: {}\n", "version", meta["version"]); - fmt::print("{:>7}: {}\n", "Fp", flavour["p"]); - fmt::print("{:>7}: [{}, {}, {},\n{:>10}{}, {}, {}]\n", "F[]", flavour["x"]["0"], flavour["x"]["1"], - flavour["x"]["2"], "", flavour["x"]["3"], flavour["x"]["4"], flavour["x"]["5"]); - fmt::print("{:>7}: {}\n", "INV", flavour["quotient"]); - fmt::print("{:>7}: {}\n", "mqnr", flavour["non_residue"]); - fmt::print("{:>7}: {}\n", "k", flavour["priv"]); - fmt::print("{:>7}: {}\n", "IID", flavour["iid_key"]); - fmt::print("\n"); - } - - confid.LoadHyperellipticCurve(flavour["x"]["0"], flavour["x"]["1"], flavour["x"]["2"], flavour["x"]["3"], - flavour["x"]["4"], flavour["x"]["5"], flavour["priv"], flavour["quotient"], - flavour["non_residue"], flavour["iid_key"], meta["tags"].contains("xpbrand"), - meta["tags"].contains("office"), meta["activation"]["version"]); - return false; -} - -/** - * - * @return success - */ -BOOL CLI::PIDGenerate() -{ - BOOL retval = false; - - if (options.pidgenversion == Options::PIDGEN_VERSION::PIDGEN_2) - { - auto p2 = PIDGEN2(); - retval = PIDGEN2Generate(p2); - return retval; - } - else if (options.pidgenversion == Options::PIDGEN_VERSION::PIDGEN_3) - { - auto bink = keys["BINK"][options.binkID]; - - auto p3 = PIDGEN3::Factory(bink["p"]); - InitPIDGEN3(p3); - retval = PIDGEN3Generate(p3); - - delete p3; - return retval; - } - - return retval; -} - -/** - * - * @return isValid - */ -BOOL CLI::PIDValidate() -{ - BOOL retval = false; - - if (options.pidgenversion == Options::PIDGEN_VERSION::PIDGEN_2) - { - auto p2 = PIDGEN2(); - retval = PIDGEN2Validate(p2); - return retval; - } - else if (options.pidgenversion == Options::PIDGEN_VERSION::PIDGEN_3) - { - auto bink = keys["BINK"][options.binkID]; - - auto p3 = PIDGEN3::Factory(bink["p"]); - InitPIDGEN3(p3); - retval = PIDGEN3Validate(p3); - - delete p3; - return retval; - } - - return retval; -} - -/** - * Process application state - * - * @return application status code - */ -int CLI::Run() -{ - /** - * TODO: we should be checking if the system's pseudorandom facilities work - * before attempting generation, validation does not require entropy - */ - switch (options.state) - { - case Options::APPLICATION_STATE::STATE_PIDGEN_GENERATE: - return PIDGenerate(); - - case Options::APPLICATION_STATE::STATE_PIDGEN_VALIDATE: - return PIDValidate(); - - case Options::APPLICATION_STATE::STATE_CONFIRMATION_ID: - return ConfirmationIDGenerate(); - - default: - return 1; - } -} \ No newline at end of file diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp new file mode 100644 index 0000000..b878b1a --- /dev/null +++ b/src/cli/cli.cpp @@ -0,0 +1,159 @@ +/** + * This file is a part of the UMSKT Project + * + * Copyleft (C) 2019-2024 UMSKT Contributors (et.al.) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * @FileCreated by Andrew on 01/06/2023 + * @Maintainer Neo + */ + +#include "cli.h" + +// define static storage +CLI::Options CLI::options; +json CLI::keys; + +/** + * + * @param argcIn + * @param argvIn + * @return exit status (where success is 0) + */ +BYTE CLI::Init(int argcIn, char **argvIn) +{ + // set default options + options.argc = argcIn; + options.argv = argvIn; + options.binkID = "2E"; + options.productCode = "WINXP"; + options.productFlavour = "VLK"; + options.numKeys = 1; + options.pidgenversion = Options::PIDGEN_VERSION::PIDGEN_3; + options.state = Options::APPLICATION_STATE::STATE_PIDGEN_GENERATE; + + SetHelpText(); + + BOOL success = parseCommandLine(); + if (!success) + { + return options.error; + } + + // if we displayed help, without an error + // return success + if (options.help) + { + return -1; + } + + success = processOptions(); + if (!success) + { + return 2; + } + + return 0; +} + +/** + * + * @param filename + * @return success + */ +BOOL CLI::loadJSON(const fs::path &filename) +{ + if (filename.empty()) + { + if (options.verbose) + { + fmt::print("Loading internal keys file\n"); + } + + auto retval = loadEmbeddedJSON(); + + if (retval && options.verbose) + { + fmt::print("Loaded internal keys file successfully\n\n"); + } + else if (!retval) + { + fmt::print("Error loading internal keys file...\n\n"); + } + return retval; + } + + if (!fs::exists(filename)) + { + fmt::print("ERROR: File {} does not exist\n", filename.string()); + return false; + } + + if (options.verbose) + { + fmt::print("Loading keys file: {}\n", options.keysFilename); + } + + std::ifstream f(filename); + try + { + keys = json::parse(f, nullptr, false, false); + } + catch (const json::exception &e) + { + fmt::print("ERROR: Exception thrown while parsing {}: {}\n", filename.string(), e.what()); + return false; + } + + if (keys.is_discarded()) + { + fmt::print("ERROR: Unable to parse keys from: {}\n", filename.string()); + return false; + } + + if (options.verbose) + { + fmt::print("Loaded keys from \"{}\" successfully\n", options.keysFilename); + } + + return true; +} + +/** + * Process application state + * + * @return application status code + */ +int CLI::Run() +{ + /** + * TODO: we should be checking if the system's pseudorandom facilities work + * before attempting generation, validation does not require entropy + */ + switch (options.state) + { + case Options::APPLICATION_STATE::STATE_PIDGEN_GENERATE: + return PIDGenerate(); + + case Options::APPLICATION_STATE::STATE_PIDGEN_VALIDATE: + return PIDValidate(); + + case Options::APPLICATION_STATE::STATE_CONFIRMATION_ID: + return ConfirmationIDGenerate(); + + default: + return 1; + } +} \ No newline at end of file diff --git a/src/cli.h b/src/cli/cli.h similarity index 89% rename from src/cli.h rename to src/cli/cli.h index 616171f..fff5bb9 100644 --- a/src/cli.h +++ b/src/cli/cli.h @@ -23,8 +23,7 @@ #ifndef UMSKT_CLI_H #define UMSKT_CLI_H -#include "libumskt/libumskt.h" -#include "typedefs.h" +#include #include #include @@ -32,7 +31,6 @@ #include #include -#include #include #include @@ -40,7 +38,7 @@ using json = nlohmann::json; namespace fs = std::filesystem; // fmt <-> json linkage -template <> struct fmt::formatter : ostream_formatter +template <> struct fmt::formatter : formatter { auto format(const json &j, format_context &ctx) const { @@ -50,18 +48,20 @@ template <> struct fmt::formatter : ostream_formatter } else { - return basic_ostream_formatter::format(j, ctx); + std::stringstream s; + s << j; + return formatter::format(s.str(), ctx); } } }; +#include "../libumskt/confid/confid.h" +#include "../libumskt/libumskt.h" +#include "../libumskt/pidgen2/PIDGEN2.h" +#include "../libumskt/pidgen3/BINK1998.h" +#include "../libumskt/pidgen3/BINK2002.h" +#include "../libumskt/pidgen3/PIDGEN3.h" #include "help.h" -#include "libumskt/confid/confid.h" -#include "libumskt/libumskt.h" -#include "libumskt/pidgen2/PIDGEN2.h" -#include "libumskt/pidgen3/BINK1998.h" -#include "libumskt/pidgen3/BINK2002.h" -#include "libumskt/pidgen3/PIDGEN3.h" #ifndef UMSKTCLI_VERSION_STRING #define UMSKTCLI_VERSION_STRING "unknown version-dirty" diff --git a/src/cli/confirmationid.cpp b/src/cli/confirmationid.cpp new file mode 100644 index 0000000..7645100 --- /dev/null +++ b/src/cli/confirmationid.cpp @@ -0,0 +1,127 @@ +/** + * This file is a part of the UMSKT Project + * + * Copyleft (C) 2019-2024 UMSKT Contributors (et.al.) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * @FileCreated by Neo on 02/18/2024 + * @Maintainer Neo + */ + +#include "cli.h" + +/** + * + * @param confid + * @return success + */ +BOOL CLI::InitConfirmationID(ConfirmationID &confid) +{ + if (!keys["products"][options.productCode].contains("meta") || + !keys["products"][options.productCode]["meta"].contains("activation")) + { + fmt::print("ERROR: product flavour \"{}\" does not have known activation values", options.productCode); + return false; + } + + auto meta = keys["products"][options.productCode]["meta"]["activation"]; + + if (!keys["activation"].contains(meta["flavour"])) + { + fmt::print("ERROR: \"{}\" is an unknown activation flavour", meta["flavour"]); + return false; + } + + auto flavour = keys["activation"][meta["flavour"]]; + + if (options.verbose) + { + fmt::print("{:->80}\n", ""); + fmt::print("Loaded the following hyperelliptic curve parameters: activation[{}]\n", meta["flavour"]); + fmt::print("{:->80}\n", ""); + fmt::print("{:>7}: {}\n", "name", flavour["name"]); + fmt::print("{:>7}: {}\n", "version", meta["version"]); + fmt::print("{:>7}: {}\n", "Fp", flavour["p"]); + fmt::print("{:>7}: [{}, {}, {},\n{:>10}{}, {}, {}]\n", "F[]", flavour["x"]["0"], flavour["x"]["1"], + flavour["x"]["2"], "", flavour["x"]["3"], flavour["x"]["4"], flavour["x"]["5"]); + fmt::print("{:>7}: {}\n", "INV", flavour["quotient"]); + fmt::print("{:>7}: {}\n", "mqnr", flavour["non_residue"]); + fmt::print("{:>7}: {}\n", "k", flavour["priv"]); + fmt::print("{:>7}: {}\n", "IID", flavour["iid_key"]); + fmt::print("\n"); + } + + confid.LoadHyperellipticCurve(flavour["x"]["0"], flavour["x"]["1"], flavour["x"]["2"], flavour["x"]["3"], + flavour["x"]["4"], flavour["x"]["5"], flavour["priv"], flavour["quotient"], + flavour["non_residue"], flavour["iid_key"], meta["tags"].contains("xpbrand"), + meta["tags"].contains("office"), meta["activation"]["version"]); + return false; +} + +/** + * + * @return success + */ +BOOL CLI::ConfirmationIDGenerate() +{ + auto confid = ConfirmationID(); + std::string confirmation_id; + + if (!InitConfirmationID(confid)) + { + return false; + } + + DWORD32 err = confid.Generate(options.installationID, confirmation_id, options.productID); + + if (err == SUCCESS) + { + fmt::print("{}\n", confirmation_id); + return true; + } + + switch (err) + { + case ERR_TOO_SHORT: + fmt::print("ERROR: Installation ID is too short.\n"); + break; + + case ERR_TOO_LARGE: + fmt::print("ERROR: Installation ID is too long.\n"); + break; + + case ERR_INVALID_CHARACTER: + fmt::print("ERROR: Invalid character in installation ID.\n"); + break; + + case ERR_INVALID_CHECK_DIGIT: + fmt::print("ERROR: Installation ID checksum failed. Please check that it is typed correctly.\n"); + break; + + case ERR_UNKNOWN_VERSION: + fmt::print("ERROR: Unknown installation ID version.\n"); + break; + + case ERR_UNLUCKY: + fmt::print("ERROR: Unable to generate valid confirmation ID.\n"); + break; + + default: + fmt::print("Unknown error occurred during Confirmation ID generation: {}\n", err); + break; + } + + return false; +} \ No newline at end of file diff --git a/src/help.cpp b/src/cli/help.cpp similarity index 95% rename from src/help.cpp rename to src/cli/help.cpp index 9016dfd..41d4fc9 100644 --- a/src/help.cpp +++ b/src/cli/help.cpp @@ -37,7 +37,7 @@ void CLI::SetHelpText() helpOptions[OPTION_DEBUG] = {"d", "debug", "enable debug output", false, "", &SetDebugOption}; helpOptions[OPTION_FILE] = { - "F", "file", "(advanced) specify which keys JSON file to load", true, "[embedded file]", &SetFileOption}; + "", "file", "(advanced) specify which keys JSON file to load", true, "[embedded file]", &SetFileOption}; helpOptions[OPTION_LIST] = {"l", "list", "list supported products", false, "", &SetListOption}; @@ -52,7 +52,7 @@ void CLI::SetHelpText() true, "1", &SetNumberOption}; helpOptions[OPTION_ACTIVATIONID] = { - "i", "installationID", "(activation only) installation ID used to generate confirmation ID", true, + "I", "installationID", "(activation only) installation ID used to generate confirmation ID", true, "", &SetActivationIDOption}; helpOptions[OPTION_ACTIVATIONPID] = { @@ -64,14 +64,14 @@ void CLI::SetHelpText() helpOptions[OPTION_UPGRADE] = {"U", "upgrade", "(PIDGEN 3 only) generate an upgrade key", false, "", &SetUpgradeOption}; - helpOptions[OPTION_BINK] = {"b", "binkID", "(advanced) override which BINK identifier to load", - true, "", &SetBINKOption}; + helpOptions[OPTION_BINK] = {"", "bink", "(advanced) override which BINK identifier to load", + true, "", &SetBINKOption}; - helpOptions[OPTION_CHANNELID] = {"c", "channelid", "(advanced) override which product channel to use", - true, "", &SetChannelIDOption}; + helpOptions[OPTION_CHANNELID] = {"", "channel", "(advanced) override which product channel to use", + true, "", &SetChannelIDOption}; helpOptions[OPTION_SERIAL] = { - "s", "serial", "(advanced, PIDGEN 2/3 [BINK 1998] only) specify a serial to generate", + "", "serial", "(advanced, PIDGEN 2/3 [BINK 1998] only) specify a serial to generate", true, "", &SetSerialOption}; helpOptions[OPTION_AUTHDATA] = { @@ -331,9 +331,9 @@ BOOL CLI::processOptions() } // FE and FF are BINK 1998, but use a different, currently unhandled encoding scheme, we return an error here - if (options.binkID == "FE" || options.binkID == "FF") + if (options.binkID == "TS00" || options.binkID == "TS01") { - fmt::print("ERROR: Terminal Services BINKs (FE and FF) are unsupported at this time\n"); + fmt::print("ERROR: Terminal Services BINKs (TS00 and TS01) are unsupported at this time\n"); return false; } @@ -357,7 +357,7 @@ BOOL CLI::processListCommand() // the following code is absolutely unhinged // I'm so sorry -#if defined(__UNICODE__) || defined(__GNUC__) +#if defined(UNICODE) || defined(__GNUC__) auto *leaf = "\u251C", *last = "\u2514", *line = "\u2500"; #else auto *leaf = "\xC3", *last = "\xC0", *line = "\xC4"; @@ -522,6 +522,7 @@ BOOL CLI::SetFileOption(const std::string &file) fmt::print("Setting file option to: {}\n", file); } options.keysFilename = file; + return true; } diff --git a/src/help.h b/src/cli/help.h similarity index 100% rename from src/help.h rename to src/cli/help.h diff --git a/src/cli/options.cpp b/src/cli/options.cpp new file mode 100644 index 0000000..a29d5a2 --- /dev/null +++ b/src/cli/options.cpp @@ -0,0 +1,3 @@ +// +// Created by neo on 2/18/2024. +// diff --git a/src/generate.cpp b/src/cli/pidgen.cpp similarity index 54% rename from src/generate.cpp rename to src/cli/pidgen.cpp index d2234d4..3146174 100644 --- a/src/generate.cpp +++ b/src/cli/pidgen.cpp @@ -16,12 +16,72 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * - * @FileCreated by Neo on 01/05/2024 + * @FileCreated by Neo on 02/18/2024 * @Maintainer Neo */ #include "cli.h" +/** + * + * @param pidgen3 + * @return success + */ +BOOL CLI::InitPIDGEN3(PIDGEN3 *p3) +{ + auto bink = keys["BINK"][options.binkID]; + + if (options.verbose) + { + fmt::print("{:->80}\n", ""); + fmt::print("Loaded the following elliptic curve parameters: BINK[{}]\n", options.binkID); + fmt::print("{:->80}\n", ""); + fmt::print("{:>6}: {}\n", "P", bink["p"]); + fmt::print("{:>6}: {}\n", "a", bink["a"]); + fmt::print("{:>6}: {}\n", "b", bink["b"]); + fmt::print("{:>6}: [{},\n{:>9}{}]\n", "G[x,y]", bink["g"]["x"], "", bink["g"]["y"]); + fmt::print("{:>6}: [{},\n{:>9}{}]\n", "K[x,y]", bink["pub"]["x"], "", bink["pub"]["y"]); + fmt::print("{:>6}: {}\n", "n", bink["n"]); + fmt::print("{:>6}: {}\n", "k", bink["priv"]); + fmt::print("\n"); + } + + p3->LoadEllipticCurve(options.binkID, bink["p"], bink["a"], bink["b"], bink["g"]["x"], bink["g"]["y"], + bink["pub"]["x"], bink["pub"]["y"], bink["n"], bink["priv"]); + + if (options.state != Options::APPLICATION_STATE::STATE_PIDGEN_GENERATE) + { + return true; + } + + if (options.channelID.IsZero()) + { + options.channelID.Randomize(UMSKT::rng, sizeof(DWORD32) * 8); + } + + options.channelID %= 999; + p3->info.ChannelID = options.channelID; + if (options.verbose) + { + fmt::print("> Channel ID: {:d}\n", options.channelID); + } + + if (options.serial.NotZero() && p3->checkFieldIsBink1998()) + { + p3->info.Serial = options.serial; + if (options.verbose) + { + fmt::print("> Serial {:d}\n", options.serial); + } + } + else if (options.serial.NotZero() && !p3->checkFieldIsBink1998()) + { + fmt::print("Warning: Discarding user-supplied serial for BINK2002\n"); + } + + return true; +} + /** * * @param pidgen2 @@ -59,6 +119,23 @@ BOOL CLI::PIDGEN2Generate(PIDGEN2 &p2) */ BOOL CLI::PIDGEN2Validate(PIDGEN2 &p2) { + std::string product_key; + + if (!p2.ValidateKeyString(options.keyToCheck, product_key)) + { + fmt::print("ERROR: Product key is in an incorrect format!\n"); + return false; + } + + fmt::print("{}\n", p2.StringifyKey(product_key)); + + if (!p2.Validate(product_key)) + { + fmt::print("ERROR: Product key is invalid! Wrong BINK ID?\n"); + return false; + } + + fmt::print("Key validated successfully!\n"); return true; } @@ -165,7 +242,7 @@ BOOL CLI::PIDGEN3Validate(PIDGEN3 *p3) { std::string product_key; - if (!PIDGEN3::ValidateKeyString(options.keyToCheck, product_key)) + if (!p3->ValidateKeyString(options.keyToCheck, product_key)) { fmt::print("ERROR: Product key is in an incorrect format!\n"); return false; @@ -187,54 +264,56 @@ BOOL CLI::PIDGEN3Validate(PIDGEN3 *p3) * * @return success */ -BOOL CLI::ConfirmationIDGenerate() +BOOL CLI::PIDGenerate() { - auto confid = ConfirmationID(); - std::string confirmation_id; + BOOL retval = false; - if (!InitConfirmationID(confid)) + if (options.pidgenversion == Options::PIDGEN_VERSION::PIDGEN_2) { - return false; + auto p2 = PIDGEN2(); + retval = PIDGEN2Generate(p2); + return retval; + } + else if (options.pidgenversion == Options::PIDGEN_VERSION::PIDGEN_3) + { + auto bink = keys["BINK"][options.binkID]; + + auto p3 = PIDGEN3::Factory(bink["p"]); + InitPIDGEN3(p3); + retval = PIDGEN3Generate(p3); + + delete p3; + return retval; } - DWORD32 err = confid.Generate(options.installationID, confirmation_id, options.productID); - - if (err == SUCCESS) - { - fmt::print("{}\n", confirmation_id); - return true; - } - - switch (err) - { - case ERR_TOO_SHORT: - fmt::print("ERROR: Installation ID is too short.\n"); - break; - - case ERR_TOO_LARGE: - fmt::print("ERROR: Installation ID is too long.\n"); - break; - - case ERR_INVALID_CHARACTER: - fmt::print("ERROR: Invalid character in installation ID.\n"); - break; - - case ERR_INVALID_CHECK_DIGIT: - fmt::print("ERROR: Installation ID checksum failed. Please check that it is typed correctly.\n"); - break; - - case ERR_UNKNOWN_VERSION: - fmt::print("ERROR: Unknown installation ID version.\n"); - break; - - case ERR_UNLUCKY: - fmt::print("ERROR: Unable to generate valid confirmation ID.\n"); - break; - - default: - fmt::print("Unknown error occurred during Confirmation ID generation: {}\n", err); - break; - } - - return false; + return retval; } + +/** + * + * @return isValid + */ +BOOL CLI::PIDValidate() +{ + BOOL retval = false; + + if (options.pidgenversion == Options::PIDGEN_VERSION::PIDGEN_2) + { + auto p2 = PIDGEN2(); + retval = PIDGEN2Validate(p2); + return retval; + } + else if (options.pidgenversion == Options::PIDGEN_VERSION::PIDGEN_3) + { + auto bink = keys["BINK"][options.binkID]; + + auto p3 = PIDGEN3::Factory(bink["p"]); + InitPIDGEN3(p3); + retval = PIDGEN3Validate(p3); + + delete p3; + return retval; + } + + return retval; +} \ No newline at end of file diff --git a/src/libumskt/confid/confid.cpp b/src/libumskt/confid/confid.cpp index 2d0cd2b..9563cb5 100644 --- a/src/libumskt/confid/confid.cpp +++ b/src/libumskt/confid/confid.cpp @@ -24,6 +24,8 @@ * the history provided by diamondggg is that they are the originator of the code * and was created in tandem with an acquaintance who knows number theory. * The file dates suggest this code was written sometime in 2017/2018 + * + * The algorithm was refactored by Neo in 2023 * } */ @@ -102,14 +104,14 @@ BOOL ConfirmationID::LoadHyperellipticCurve(const std::string *f, const std::str { for (int i = 0; i < 6; i++) { - Integer(&f[i][0]).Encode((BYTE *)curve[i], sizeof(QWORD)); + EncodeN(IntegerS(f[i]), curve[i]); } - Integer(&priv[0]).Encode(privateKey.byte, sizeof(Q_OWORD)); + EncodeN(IntegerS(priv), privateKey); - Integer(&modulus[0]).Encode((BYTE *)&MOD, sizeof(QWORD)); + EncodeN(IntegerS(modulus), MOD); - Integer(&nonresidue[0]).Encode((BYTE *)NON_RESIDUE, sizeof(QWORD)); + EncodeN(IntegerS(nonresidue), NON_RESIDUE); this->isOffice = isOffice; this->isXPBrand = isXPBrand; diff --git a/src/libumskt/confid/confid.h b/src/libumskt/confid/confid.h index a1ffb66..3d556f3 100644 --- a/src/libumskt/confid/confid.h +++ b/src/libumskt/confid/confid.h @@ -23,7 +23,7 @@ #ifndef UMSKT_CONFID_H #define UMSKT_CONFID_H -#include "../libumskt.h" +#include // Confirmation ID generator constants enum CONFIRMATION_ID_STATUS @@ -44,7 +44,7 @@ typedef struct Q_OWORD u, v; } TDivisor; -class EXPORT ConfirmationID +class EXPORT ConfirmationID : public UMSKT { QWORD MOD = 0, NON_RESIDUE = 0; QWORD curve[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; diff --git a/src/libumskt/confid/confid_unittest.cpp b/src/libumskt/confid/confid_unittest.cpp new file mode 100644 index 0000000..98ca825 --- /dev/null +++ b/src/libumskt/confid/confid_unittest.cpp @@ -0,0 +1,55 @@ +/** + * This file is a part of the UMSKT Project + * + * Copyleft (C) 2019-2024 UMSKT Contributors (et.al.) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * @FileCreated by Neo on 02/20/2024 + * @Maintainer Neo + */ + +#include "confid.h" +#include + +/** + * ConfirmationID must not be an abstract class. + */ +TEST(TestConfirmationID, InstantiateConfirmationID) +{ + auto p3 = new ConfirmationID(); + ASSERT_NE(p3, nullptr); + delete p3; +} + +class TestConfirmationID : public libumsktUnitTests +{ + protected: + ConfirmationID *cid; + + void SetUp() override + { + cid = new ConfirmationID(); + cid->LoadHyperellipticCurve() + } + + void TearDown() override + { + if (cid != nullptr) + { + delete cid; + cid = nullptr; + } + } +}; diff --git a/src/libumskt/init.cpp b/src/libumskt/init.cpp index 534e6e1..d27139e 100644 --- a/src/libumskt/init.cpp +++ b/src/libumskt/init.cpp @@ -20,7 +20,7 @@ * @Maintainer Neo */ -#include "libumskt.h" +#include std::FILE *UMSKT::debug; std::FILE *UMSKT::verbose; @@ -38,7 +38,7 @@ BOOL UMSKT::CONSTRUCT() { #ifdef __DJGPP__ // this should be set up as early as possible - auto now = uclock(); + uclock(); #endif return true; } diff --git a/src/libumskt/libumskt.cpp b/src/libumskt/libumskt.cpp index 0787030..362d1d4 100644 --- a/src/libumskt/libumskt.cpp +++ b/src/libumskt/libumskt.cpp @@ -20,12 +20,12 @@ * @Maintainer Neo */ -#include "libumskt.h" -#include "confid/confid.h" -#include "pidgen2/PIDGEN2.h" -#include "pidgen3/BINK1998.h" -#include "pidgen3/BINK2002.h" -#include "pidgen3/PIDGEN3.h" +#include +#include +#include +#include +#include +#include std::map UMSKT::tags; CryptoPP::DefaultAutoSeededRNG UMSKT::rng; diff --git a/src/libumskt/libumskt.h b/src/libumskt/libumskt.h index ce2547f..5933123 100644 --- a/src/libumskt/libumskt.h +++ b/src/libumskt/libumskt.h @@ -23,7 +23,7 @@ #ifndef UMSKT_LIBUMSKT_H #define UMSKT_LIBUMSKT_H -#include "../typedefs.h" +#include #ifdef __DJGPP__ #include @@ -43,7 +43,6 @@ using Integer = CryptoPP::Integer; #include #include -#include // fmt <-> CryptoPP linkage template <> class fmt::formatter @@ -229,6 +228,21 @@ class EXPORT UMSKT return EncodeN(in, (BYTE *)&buf, sizeof(T)); } + /** + * Encode Integer to Native type T where T is a concrete type + * + * @tparam T + * @param in + * @param buf + * @return + */ + template INLINE static T EncodeN(const Integer &in) + { + T buf; + EncodeN(in, (BYTE *)&buf, sizeof(T)); + return buf; + } + /** * Encode a random number into a Native concrete type * diff --git a/src/libumskt/libumskt_unittest.h b/src/libumskt/libumskt_unittest.h new file mode 100644 index 0000000..f5e1612 --- /dev/null +++ b/src/libumskt/libumskt_unittest.h @@ -0,0 +1,39 @@ +/** + * This file is a part of the UMSKT Project + * + * Copyleft (C) 2019-2024 UMSKT Contributors (et.al.) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * @FileCreated by Neo on 02/19/2024 + * @Maintainer Neo + */ +#ifndef UMSKT_LIBUMSKT_UNITTEST_H +#define UMSKT_LIBUMSKT_UNITTEST_H + +#include +#include + +class libumsktUnitTests : public testing::Test +{ + public: + libumsktUnitTests() + { + // UMSKT::setVerboseOutput(stderr); + // UMSKT::setDebugOutput(stderr); + } + ~libumsktUnitTests() override = default; +}; + +#endif // UMSKT_LIBUMSKT_UNITTEST_H diff --git a/src/libumskt/pidgen.h b/src/libumskt/pidgen.h index 88ef8aa..5e22b52 100644 --- a/src/libumskt/pidgen.h +++ b/src/libumskt/pidgen.h @@ -34,15 +34,36 @@ class PIDGEN : public UMSKT { public: + virtual ~PIDGEN() = default; static const Integer SEVEN; static const Integer TEN; static const Integer MaxChannelID; static const Integer MaxSerial; - virtual BOOL Generate(std::string &pKey) = 0; - virtual BOOL Validate(const std::string &pKey) = 0; - virtual std::string StringifyKey(const std::string &pKey) = 0; - virtual std::string StringifyProductID() = 0; + virtual BOOL Generate(std::string &pKey) + { + throw std::runtime_error("PIDGEN::Generate() pure virtual function call"); + } + + virtual BOOL Validate(const std::string &pKey) + { + throw std::runtime_error("PIDGEN::Validate() pure virtual function call"); + } + + virtual std::string StringifyKey(const std::string &pKey) + { + throw std::runtime_error("PIDGEN::StringifyKey() pure virtual function call"); + } + + virtual std::string StringifyProductID() + { + throw std::runtime_error("PIDGEN::StringifyProductID() pure virtual function call"); + } + + virtual BOOL ValidateKeyString(const std::string &in_key, std::string &out_key) + { + throw std::runtime_error("PIDGEN::ValidateKeyString() pure virtual function call"); + } Integer GenerateMod7(const Integer &in); BOOL isValidMod7(const Integer &in); diff --git a/src/libumskt/pidgen2/PIDGEN2.cpp b/src/libumskt/pidgen2/PIDGEN2.cpp index c67d871..6c36288 100644 --- a/src/libumskt/pidgen2/PIDGEN2.cpp +++ b/src/libumskt/pidgen2/PIDGEN2.cpp @@ -36,38 +36,21 @@ BOOL PIDGEN2::Generate(std::string &pKey) Integer random; random.Randomize(rng, sizeof(DWORD32) * 8); - info.ChannelID = random % MaxChannelID; - if (!isValidChannelID()) - { - info.ChannelID++; - if (info.ChannelID <= Integer::Zero()) - { - info.ChannelID = Integer::One(); - } - else if (info.ChannelID >= 999) - { - info.ChannelID = 998; - } - } - - random.Randomize(rng, sizeof(DWORD32) * 8); - info.Serial = random % MaxSerial; + info.ChannelID = info.ChannelID % MaxChannelID; + info.Serial = info.Serial % MaxSerial; if (info.isOEM) { - info.Day = (random % Integer(365)) + Integer::One(); - info.Year = IntegerS(validYears[random % validYears.size()]); + info.Day = info.Day % Integer(366); + // info.Year = info.Year; info.OEMID = (info.ChannelID * TEN) + (info.Serial / (MaxSerial / TEN)); info.Serial %= (MaxSerial / TEN); info.OEMID = (info.OEMID * TEN) + GenerateMod7(info.OEMID); - DWORD32 day, year, serial, oemid; - EncodeN(info.Day, day); - EncodeN(info.Year, year); - EncodeN(info.Serial, serial); - EncodeN(info.OEMID, oemid); + DWORD32 day = EncodeN(info.Day), year = EncodeN(info.Year), serial = EncodeN(info.Serial), + oemid = EncodeN(info.OEMID); if (debug) { @@ -81,9 +64,7 @@ BOOL PIDGEN2::Generate(std::string &pKey) info.ChannelID = (info.ChannelID * TEN) + ((info.ChannelID % TEN) + 1); info.Serial = (info.Serial * TEN) + GenerateMod7(info.Serial); - DWORD32 channelid, serial; - EncodeN(info.ChannelID, channelid); - EncodeN(info.Serial, serial); + DWORD32 channelid = EncodeN(info.ChannelID), serial = EncodeN(info.Serial); if (debug) { @@ -96,11 +77,7 @@ BOOL PIDGEN2::Generate(std::string &pKey) { info.Serial = (info.Serial * TEN) + GenerateMod7(info.Serial); - fmt::print("{}\n", info.Serial); - - DWORD32 channelid, serial; - EncodeN(info.ChannelID, channelid); - EncodeN(info.Serial, serial); + DWORD32 channelid = EncodeN(info.ChannelID), serial = EncodeN(info.Serial); if (debug) { @@ -142,7 +119,7 @@ BOOL PIDGEN2::Generate(std::string &pKey) BOOL PIDGEN2::Validate(const std::string &pKey) { std::string filtered; - std::copy_if(pKey.begin(), pKey.end(), std::back_inserter(filtered), [](char c) { return std::isdigit(c); }); + ValidateKeyString(pKey, filtered); bool bIsValidChannelID, bIsValidSerial, bIsValidOEMDay, bIsValidOEMYear, bIsValidOEMID; @@ -241,6 +218,19 @@ std::string PIDGEN2::StringifyProductID() return fmt::format("{}-{}", info.ChannelID, info.Serial); } +/** + * + * @param in_key + * @param out_key + * @return + */ +BOOL INLINE PIDGEN2::ValidateKeyString(const std::string &in_key, std::string &out_key) +{ + std::copy_if(in_key.begin(), in_key.end(), std::back_inserter(out_key), [](char c) { return std::isdigit(c); }); + + return out_key.length() == KeySize::FPP || out_key.length() == KeySize::Office || out_key.length() == KeySize::OEM; +} + /** * Is the Serial with check digit a valid serial? * diff --git a/src/libumskt/pidgen2/PIDGEN2.h b/src/libumskt/pidgen2/PIDGEN2.h index f8f6df4..c2b08c2 100644 --- a/src/libumskt/pidgen2/PIDGEN2.h +++ b/src/libumskt/pidgen2/PIDGEN2.h @@ -23,7 +23,7 @@ #ifndef UMSKT_PIDGEN2_H #define UMSKT_PIDGEN2_H -#include "../pidgen.h" +#include class EXPORT PIDGEN2 : public PIDGEN { @@ -38,6 +38,8 @@ class EXPORT PIDGEN2 : public PIDGEN }; public: + ~PIDGEN2() override = default; + struct KeyInfo { BOOL isOEM, isOffice; @@ -48,6 +50,7 @@ class EXPORT PIDGEN2 : public PIDGEN BOOL Validate(const std::string &pKey) override; std::string StringifyKey(const std::string &pKey) override; std::string StringifyProductID() override; + BOOL ValidateKeyString(const std::string &in_key, std::string &out_key) override; BOOL isValidSerial(); BOOL isValidOEMID(); diff --git a/src/libumskt/pidgen2/PIDGEN2_unittest.cpp b/src/libumskt/pidgen2/PIDGEN2_unittest.cpp new file mode 100644 index 0000000..662766f --- /dev/null +++ b/src/libumskt/pidgen2/PIDGEN2_unittest.cpp @@ -0,0 +1,115 @@ +/** + * This file is a part of the UMSKT Project + * + * Copyleft (C) 2019-2024 UMSKT Contributors (et.al.) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * @FileCreated by Neo on 02/19/2024 + * @Maintainer Neo + */ + +#include "PIDGEN2.h" +#include + +/** + * PIDGEN2 must not be an abstract class. + */ +TEST(PIDGEN2, InstantiatePIDGEN2) +{ + auto p2 = new PIDGEN2(); + ASSERT_NE(p2, nullptr); + delete p2; +} + +class TestPIDGEN2 : public libumsktUnitTests +{ + protected: + PIDGEN *p; + PIDGEN2 *p2; + + PIDGEN2::KeyInfo valid_ki = {false, false, 60, 99, 0, 95, 111111}; + + void SetUp() override + { + p2 = new PIDGEN2(); + } + + void TearDown() override + { + if (p != nullptr) + { + delete p; + p = nullptr; + } + if (p2 != nullptr) + { + delete p2; + p2 = nullptr; + } + } +}; + +TEST_F(TestPIDGEN2, TestStringifyKeyFPP) +{ + std::string pKey = "0951111111"; + pKey = p2->StringifyKey(pKey); + ASSERT_STRCASEEQ(&pKey[0], "095-1111111"); +} + +TEST_F(TestPIDGEN2, TestStringifyKeyOffice) +{ + std::string pKey = "09561111111"; + pKey = p2->StringifyKey(pKey); + ASSERT_STREQ(&pKey[0], "0956-1111111"); +} + +TEST_F(TestPIDGEN2, TestStringifyKeyOEM) +{ + std::string pKey = "06099000951611111"; + pKey = p2->StringifyKey(pKey); + ASSERT_STREQ(&pKey[0], "06099-OEM-0009516-11111"); +} + +TEST_F(TestPIDGEN2, GenerateValidFPPKey) +{ + p2->info = valid_ki; + + std::string pKey; + p2->Generate(pKey); + pKey = p2->StringifyKey(pKey); + ASSERT_STREQ(&pKey[0], "095-1111111"); +} + +TEST_F(TestPIDGEN2, GenerateValidOfficeKey) +{ + p2->info = valid_ki; + p2->info.isOffice = true; + + std::string pKey; + p2->Generate(pKey); + pKey = p2->StringifyKey(pKey); + ASSERT_STREQ(&pKey[0], "0956-1111111"); +} + +TEST_F(TestPIDGEN2, GenerateValidOEMKey) +{ + p2->info = valid_ki; + p2->info.isOEM = true; + + std::string pKey; + p2->Generate(pKey); + pKey = p2->StringifyKey(pKey); + ASSERT_STREQ(&pKey[0], "06099-OEM-0009516-11111"); +} \ No newline at end of file diff --git a/src/libumskt/pidgen3/BINK1998.cpp b/src/libumskt/pidgen3/BINK1998.cpp index 457d692..f3c8cce 100644 --- a/src/libumskt/pidgen3/BINK1998.cpp +++ b/src/libumskt/pidgen3/BINK1998.cpp @@ -99,6 +99,11 @@ BOOL BINK1998::Generate(std::string &pKey) // copy initial state from object auto ki = info; + if (!ki.Rand.IsZero()) + { + c = ki.Rand; + } + // Data segment of the RPK. Integer serialPack = (ki.ChannelID * MaxSerial) + ki.Serial; Integer pData = (serialPack << 1) | ki.isUpgrade; @@ -110,8 +115,11 @@ BOOL BINK1998::Generate(std::string &pKey) { ECP::Point R; - // Generate a random number c consisting of 384 bits without any constraints. - c.Randomize(UMSKT::rng, FieldBits); + if (ki.Rand.IsZero()) + { + // Generate a random number c consisting of 384 bits without any constraints. + c.Randomize(UMSKT::rng, FieldBits); + } // Pick a random derivative of the base point on the elliptic curve. // R = cG; @@ -201,7 +209,7 @@ BOOL BINK1998::Generate(std::string &pKey) fmt::print(verbose, "\n"); } - } while (ki.Signature.BitCount() > 55); + } while (ki.Signature.BitCount() > 55 && ki.Rand.IsZero()); // ↑ ↑ ↑ // The signature can't be longer than 55 bits, else it will // make the CD-key longer than 25 characters. @@ -237,13 +245,13 @@ BOOL BINK1998::Validate(const std::string &pKey) if (verbose) { - fmt::print(UMSKT::verbose, "Validation results:\n"); - fmt::print(UMSKT::verbose, "{:>10}: {}\n", "Upgrade", (bool)ki.isUpgrade); - fmt::print(UMSKT::verbose, "{:>10}: {}\n", "Channel ID", ki.ChannelID); - fmt::print(UMSKT::verbose, "{:>10}: {}\n", "Sequence", ki.Serial); - fmt::print(UMSKT::verbose, "{:>10}: {:x}\n", "Hash", ki.Hash); - fmt::print(UMSKT::verbose, "{:>10}: {:x}\n", "Signature", ki.Signature); - fmt::print(UMSKT::verbose, "\n"); + fmt::print(verbose, "Validation results:\n"); + fmt::print(verbose, "{:>10}: {}\n", "Upgrade", (bool)ki.isUpgrade); + fmt::print(verbose, "{:>10}: {}\n", "Channel ID", ki.ChannelID); + fmt::print(verbose, "{:>10}: {}\n", "Sequence", ki.Serial); + fmt::print(verbose, "{:>10}: {:x}\n", "Hash", ki.Hash); + fmt::print(verbose, "{:>10}: {:x}\n", "Signature", ki.Signature); + fmt::print(verbose, "\n"); } Integer serialPack = (ki.ChannelID * MaxSerial) + ki.Serial; @@ -280,7 +288,7 @@ BOOL BINK1998::Validate(const std::string &pKey) if (debug) { - fmt::print("\nP[x,y]: [{:x},\n{:x}]\n\n", P.x, P.y); + fmt::print(debug, "P[x,y]: [{:x},\n{:x}]\n\n", P.x, P.y); } BYTE msgDigest[SHA1::DIGESTSIZE], msgBuffer[SHAMessageLength], *pMsgBuffer = msgBuffer; diff --git a/src/libumskt/pidgen3/BINK1998.h b/src/libumskt/pidgen3/BINK1998.h index aa02761..cd77cde 100644 --- a/src/libumskt/pidgen3/BINK1998.h +++ b/src/libumskt/pidgen3/BINK1998.h @@ -39,6 +39,8 @@ class EXPORT BINK1998 : public PIDGEN3 eCurve = p3->eCurve; } + ~BINK1998() override = default; + static constexpr DWORD32 FieldBits = (48 * 8); static constexpr DWORD32 FieldBytes = (FieldBits / 8); static constexpr DWORD32 SHAMessageLength = (4 + 2 * FieldBytes); diff --git a/src/libumskt/pidgen3/BINK1998_unittest.cpp b/src/libumskt/pidgen3/BINK1998_unittest.cpp new file mode 100644 index 0000000..6303efa --- /dev/null +++ b/src/libumskt/pidgen3/BINK1998_unittest.cpp @@ -0,0 +1,110 @@ +/** + * This file is a part of the UMSKT Project + * + * Copyleft (C) 2019-2024 UMSKT Contributors (et.al.) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * @FileCreated by Neo on 02/19/2024 + * @Maintainer Neo + */ + +#include "../libumskt_unittest.h" +#include "BINK1998.h" + +/** + * BINK1998 must not be an abstract class. + */ +TEST(PIDGEN3_BINK1998, InstantiateBINK1998) +{ + auto p3 = new BINK1998(); + ASSERT_NE(p3, nullptr); + delete p3; +} + +class TestBINK1998 : public libumsktUnitTests +{ + protected: + PIDGEN *p; + PIDGEN3 *p3; + BINK1998 *bink1998; + + BINK1998::KeyInfo valid_ki = {false, false, 640, 111111, 0}; + + void SetUp() override + { + bink1998 = new BINK1998(); + bink1998->LoadEllipticCurve("0x2E", + "2260481414313563299067995668434431120981995280321627195247220485552475627515144045" + "6421260165232069708317717961315241", + "1", "0", + "1091074492220651278115691316907175015302838688467620894706280834607253141127048943" + "2930252839559606812441712224597826", + "1917099366991720451749161800061981867915210969017264186834961288993048036527467509" + "6509477191800826190959228181870174", + "1439923035396364333971294001595406158106423983592682351741971676961393703934682226" + "9422480779920783799484349086780408", + "5484731395987446993229594927733430043632089703338918322171291299699820472711849119" + "800714736923107362018017833200634", + "61760995553426173", "37454031876727861"); + bink1998->info = valid_ki; + } + + void TearDown() override + { + if (bink1998 != nullptr) + { + delete bink1998; + bink1998 = nullptr; + } + if (p3 != nullptr) + { + delete p3; + p3 = nullptr; + } + if (p != nullptr) + { + p = nullptr; + } + } +}; + +TEST_F(TestBINK1998, ValidateValidKeyString) +{ + std::string pKey; + auto ValidateKeyString = bink1998->ValidateKeyString("7KWK7-9W7H4-T64D6-DB8V7-BW7MW", pKey); + + ASSERT_TRUE(ValidateKeyString); + ASSERT_STREQ(&pKey[0], "7KWK79W7H4T64D6DB8V7BW7MW"); +} + +TEST_F(TestBINK1998, ValidateValidKey) +{ + std::string pKey = "7KWK79W7H4T64D6DB8V7BW7MW"; + + auto Validate = bink1998->Validate(pKey); + ASSERT_TRUE(Validate); +} + +TEST_F(TestBINK1998, GenerateValidKey) +{ + bink1998->info.Rand = UMSKT::IntegerS("3427338792529164195109698841758932126727183763685994848291878088992924483488" + "5768429236548372772607190036626858221847"); + + std::string pKey; + bink1998->Generate(pKey); + pKey = bink1998->StringifyKey(pKey); + + ASSERT_STREQ(&pKey[0], "7KWK7-9W7H4-T64D6-DB8V7-BW7MW"); +} diff --git a/src/libumskt/pidgen3/BINK2002.cpp b/src/libumskt/pidgen3/BINK2002.cpp index a8e26b3..721a76d 100644 --- a/src/libumskt/pidgen3/BINK2002.cpp +++ b/src/libumskt/pidgen3/BINK2002.cpp @@ -44,7 +44,7 @@ Integer BINK2002::Pack(const KeyInfo &ki) if (debug) { - fmt::print("pack: {:x}\n\n", raw); + fmt::print(debug, "pack: {:x}\n\n", raw); } return raw; @@ -97,6 +97,11 @@ BOOL BINK2002::Generate(std::string &pKey) Integer c, e, s, pRaw; + if (!ki.Rand.IsZero()) + { + c = ki.Rand; + } + // Data segment of the RPK. Integer pData = ki.ChannelID << 1 | ki.isUpgrade; @@ -106,8 +111,11 @@ BOOL BINK2002::Generate(std::string &pKey) { ECP::Point R; - // Generate a random number c consisting of 512 bits without any constraints. - c.Randomize(UMSKT::rng, FieldBits); + if (ki.Rand.IsZero()) + { + // Generate a random number c consisting of 512 bits without any constraints. + c.Randomize(UMSKT::rng, FieldBits); + } // R = cG R = eCurve.Multiply(c, genPoint); @@ -135,19 +143,19 @@ BOOL BINK2002::Generate(std::string &pKey) if (debug) { - fmt::print("msgBuffer[1]: "); + fmt::print(debug, "msgBuffer[1]: "); for (BYTE b : msgBuffer) { - fmt::print("{:x}", b); + fmt::print(debug, "{:x}", b); } - fmt::print("\n\n"); + fmt::print(debug, "\n\n"); - fmt::print("msgDigest[1]: "); + fmt::print(debug, "msgDigest[1]: "); for (BYTE b : msgDigest) { - fmt::print("{:x}", b); + fmt::print(debug, "{:x}", b); } - fmt::print("\n\n"); + fmt::print(debug, "\n\n"); } // Translate the byte sha1 into a 32-bit integer - this is our computed hash. @@ -159,12 +167,12 @@ BOOL BINK2002::Generate(std::string &pKey) BYTE buf[8]; sha1.CalculateTruncatedDigest(buf, sizeof(buf), msgBuffer, SHAMessageLength); - fmt::print("truncated buffer: "); + fmt::print(verbose, "truncated buffer: "); for (BYTE b : buf) { - fmt::print("{:x}", b); + fmt::print(verbose, "{:x}", b); } - fmt::print("\n\n"); + fmt::print(verbose, "\n\n"); DWORD h0 = ((DWORD)buf[0] | ((DWORD)buf[1] << 8) | ((DWORD)buf[2] << 16) | ((DWORD)buf[3] << 24)); DWORD h1 = @@ -173,11 +181,11 @@ BOOL BINK2002::Generate(std::string &pKey) h1 |= (h0 >> 31) & 1; - fmt::print("h0,1: {:x} {:x}\n\n", h0, h1); + fmt::print(verbose, "h0,1: {:x} {:x}\n\n", h0, h1); ki.Serial = IntegerN(h1); - fmt::print("serial: {:d}\n\n", ki.Serial); + fmt::print(verbose, "serial: {:d}\n\n", ki.Serial); } // Assemble the second SHA message. @@ -200,19 +208,19 @@ BOOL BINK2002::Generate(std::string &pKey) if (debug) { - fmt::print("msgBuffer[2]: "); + fmt::print(debug, "msgBuffer[2]: "); for (BYTE b : msgBuffer) { - fmt::print("{:x}", b); + fmt::print(debug, "{:x}", b); } - fmt::print("\n\n"); + fmt::print(debug, "\n\n"); - fmt::print("msgDigest[2]: "); + fmt::print(debug, "msgDigest[2]: "); for (BYTE b : msgDigest) { - fmt::print("{:x}", b); + fmt::print(debug, "{:x}", b); } - fmt::print("\n\n"); + fmt::print(debug, "\n\n"); } // Translate the byte sha1 into a 64-bit integer - this is our computed intermediate signature. @@ -298,7 +306,7 @@ BOOL BINK2002::Generate(std::string &pKey) fmt::print(verbose, "{:>10}: {:x}\n", "AuthInfo", ki.AuthInfo); fmt::print(verbose, "\n"); } - } while (ki.Signature.BitCount() > 62 || noSquare); + } while ((ki.Signature.BitCount() > 62 || noSquare) && ki.Rand.IsZero()); // ↑ ↑ ↑ // The signature can't be longer than 62 bits, else it will // overlap with the AuthInfo segment next to it. @@ -402,7 +410,7 @@ BOOL BINK2002::Validate(const std::string &pKey) if (debug) { - fmt::print("P[x,y]: [{:x},\n{:x}]\n\n", P.x, P.y); + fmt::print(debug, "P[x,y]: [{:x},\n{:x}]\n\n", P.x, P.y); } // Assemble the second SHA message. diff --git a/src/libumskt/pidgen3/BINK2002.h b/src/libumskt/pidgen3/BINK2002.h index 8434b47..041baf3 100644 --- a/src/libumskt/pidgen3/BINK2002.h +++ b/src/libumskt/pidgen3/BINK2002.h @@ -39,6 +39,8 @@ class EXPORT BINK2002 : public PIDGEN3 eCurve = p3->eCurve; } + ~BINK2002() override = default; + static constexpr DWORD32 FieldBits = (64 * 8); static constexpr DWORD32 FieldBytes = (FieldBits / 8); static constexpr DWORD32 SHAMessageLength = (3 + 2 * FieldBytes); diff --git a/src/libumskt/pidgen3/BINK2002_unittest.cpp b/src/libumskt/pidgen3/BINK2002_unittest.cpp new file mode 100644 index 0000000..9fb28b9 --- /dev/null +++ b/src/libumskt/pidgen3/BINK2002_unittest.cpp @@ -0,0 +1,121 @@ +/** + * This file is a part of the UMSKT Project + * + * Copyleft (C) 2019-2024 UMSKT Contributors (et.al.) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * @FileCreated by Neo on 02/19/2024 + * @Maintainer Neo + */ + +#include "BINK2002.h" +#include + +/** + * BINK2002 must not be an abstract class. + */ +TEST(PIDGEN3_BINK2002, InstantiateBINK2002) +{ + auto p3 = new BINK2002(); + ASSERT_NE(p3, nullptr); + delete p3; +} + +class TestBINK2002 : public libumsktUnitTests +{ + protected: + PIDGEN *p; + PIDGEN3 *p3; + BINK2002 *bink2002; + + BINK2002::KeyInfo valid_ki = {false, false, 640, 0, 701}; + + void SetUp() override + { + bink2002 = new BINK2002(); + bink2002->LoadEllipticCurve("0x54", + "1250964251969733259611431105354461862074700938981465222536952118871017192617497641" + "9995384745134703589248167610052719613586668754176591418831031596093374569", + "1", "0", + "8059057663701168311917532277618827622978515614146963913097592614451721430413021070" + "395782723330339842826599481063797559797462512297834269467666807971588275", + "1223930383017475319177970597922037862339473226753699711562597963240231208768364492" + "7405756146495100825573682155171145924668759419114616275413724686284123408", + "4895832170509729140211911021638266775170167022247175324972987673313207244495397975" + "379010973250279668424167408883454560376269866102669741515127286188717976", + "5846013328426281815512452704859777850382010968846722453046994319336479079120767834" + "777937190955827245502389471872759584209649693396095099112777776298051208", + "5622613991231344109", "1285511085175426271"); + bink2002->info = valid_ki; + } + + void TearDown() override + { + if (bink2002 != nullptr) + { + delete bink2002; + bink2002 = nullptr; + } + if (p3 != nullptr) + { + delete p3; + p3 = nullptr; + } + if (p != nullptr) + { + p = nullptr; + } + } +}; + +TEST_F(TestBINK2002, ValidateValidKeyString) +{ + std::string pKey; + auto ValidateKeyString = bink2002->ValidateKeyString("QX7C7-6668G-RHTTC-9XXD6-4QKVM", pKey); + ASSERT_TRUE(ValidateKeyString); + ASSERT_STREQ(&pKey[0], "QX7C76668GRHTTC9XXD64QKVM"); +} + +TEST_F(TestBINK2002, ValidateInvalidKeyString) +{ + std::string pKey; + auto ValidateKeyString = bink2002->ValidateKeyString("QX7C7-6668G-RHTTC-9XXD6-4QKVM-7", pKey); + ASSERT_FALSE(ValidateKeyString); +} + +TEST_F(TestBINK2002, ValidateValidKey) +{ + std::string pKey = "QX7C76668GRHTTC9XXD64QKVM"; + auto Validate = bink2002->Validate(pKey); + ASSERT_TRUE(Validate); +} + +TEST_F(TestBINK2002, ValidateInvalidKey) +{ + std::string pKey = "QX7C76668GRHTTC9XXD64QKV7"; + auto Validate = bink2002->Validate(pKey); + ASSERT_FALSE(Validate); +} + +TEST_F(TestBINK2002, GenerateValidKey) +{ + bink2002->info.Rand = UMSKT::IntegerS("2715417548459431244234182116258933974639514924173191881913315754156057922856" + "789413383072541627152533502894944768632184791880876163762899980230935"); + std::string pKey; + bink2002->Generate(pKey); + pKey = bink2002->StringifyKey(pKey); + + ASSERT_STREQ(&pKey[0], "QX7C7-6668G-RHTTC-9XXD6-4QKVM"); +} \ No newline at end of file diff --git a/src/libumskt/pidgen3/PIDGEN3.cpp b/src/libumskt/pidgen3/PIDGEN3.cpp index 35127d6..e01c4ee 100644 --- a/src/libumskt/pidgen3/PIDGEN3.cpp +++ b/src/libumskt/pidgen3/PIDGEN3.cpp @@ -290,10 +290,10 @@ std::string PIDGEN3::StringifyKey(const std::string &pKey) * @param currentChar * @return */ -std::string PIDGEN3::ValidateStringKeyInputCharset(std::string &accumulator, char currentChar) +std::string INLINE PIDGEN3::ValidateStringKeyInputCharset(std::string &accumulator, char currentChar) { char cchar = (char)::toupper(currentChar); - if (std::find(pKeyCharset.begin(), pKeyCharset.begin(), cchar) != pKeyCharset.end()) + if (std::find(pKeyCharset.begin(), pKeyCharset.end(), cchar) != pKeyCharset.end()) { accumulator.push_back(cchar); } diff --git a/src/libumskt/pidgen3/PIDGEN3.h b/src/libumskt/pidgen3/PIDGEN3.h index f9d8de2..fb765f7 100644 --- a/src/libumskt/pidgen3/PIDGEN3.h +++ b/src/libumskt/pidgen3/PIDGEN3.h @@ -23,7 +23,7 @@ #ifndef UMSKT_PIDGEN3_H #define UMSKT_PIDGEN3_H -#include "../pidgen.h" +#include class BINK1998; class BINK2002; @@ -51,14 +51,12 @@ class EXPORT PIDGEN3 : public PIDGEN eCurve = p3.eCurve; } - virtual ~PIDGEN3() - { - } + ~PIDGEN3() override = default; struct KeyInfo { - Integer Serial = 0, AuthInfo = 0, ChannelID = 0, Hash = 0, Signature = 0, Rand = 0; BOOL isUpgrade = false, isOEM = false; + Integer ChannelID = 0, Serial = 0, AuthInfo = 0, Rand = 0, Hash = 0, Signature = 0; void setSerial(DWORD32 SerialIn) { @@ -88,15 +86,22 @@ class EXPORT PIDGEN3 : public PIDGEN BOOL Validate(const std::string &pKey) override; std::string StringifyKey(const std::string &pKey) override; std::string StringifyProductID() override; + BOOL ValidateKeyString(const std::string &in_key, std::string &out_key) override; - virtual Integer Pack(const KeyInfo &ki) = 0; - virtual KeyInfo Unpack(const Integer &raw) = 0; + virtual Integer Pack(const KeyInfo &ki) + { + throw std::runtime_error("PIDGEN3::Pack() pure virtual function call"); + } + + virtual KeyInfo Unpack(const Integer &raw) + { + throw std::runtime_error("PIDGEN3::Unpack() pure virtual function call"); + } // PIDGEN3.cpp static PIDGEN3 *Factory(const std::string &field); static BOOL checkFieldStrIsBink1998(std::string keyin); static std::string ValidateStringKeyInputCharset(std::string &accumulator, char currentChar); - static BOOL ValidateKeyString(const std::string &in_key, std::string &out_key); static std::string base24(Integer &seq); static Integer unbase24(const std::string &cdKey); diff --git a/src/main.cpp b/src/main.cpp index 980c711..7cb8512 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,7 +20,7 @@ * @Maintainer Neo */ -#include "cli.h" +#include int main(int argc, char *argv[]) { diff --git a/src/rc.cpp b/src/rc.cpp index 852d055..45087fa 100644 --- a/src/rc.cpp +++ b/src/rc.cpp @@ -20,7 +20,7 @@ * @Maintainer Neo */ -#include "cli.h" +#include #include CMRC_DECLARE(umskt); diff --git a/src/typedefs.h b/src/typedefs.h index 688dfe5..e0c115c 100644 --- a/src/typedefs.h +++ b/src/typedefs.h @@ -25,20 +25,23 @@ #if defined(_MSC_VER) +// squelch the insane amount of warnings from cstdbool +#define _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS #define WIN32_LEAN_AND_MEAN -#include #include #undef WIN32_LEAN_AND_MEAN #endif // defined(_MSC_VER) #include +#include #include #include #include #include #include #include +#include #include #include @@ -88,15 +91,6 @@ using WORD = uint16_t; using DWORD = unsigned long; using DWORD32 = uint32_t; using QWORD = uint64_t; - -#if defined(__SIZEOF_INT128__) || defined(__int128) -using uint128_t = unsigned __int128; -#elif defined(_MSC_VER) && defined(_M_ARM) // for Windows on ARM ?? -using uint128_t = __n128; -#else // use the intel-supplied __m128 intrisic from -using uint128_t = __m128; -#endif - using OWORD = uint128_t; typedef union { diff --git a/src/windows/dllmain.cpp b/src/windows/dllmain.cpp index 6f18c8a..2896974 100644 --- a/src/windows/dllmain.cpp +++ b/src/windows/dllmain.cpp @@ -20,8 +20,8 @@ * @Maintainer Neo */ -#include "../typedefs.h" #include "resource.h" +#include "typedefs.h" BOOLEAN WINAPI DllMain(IN HINSTANCE hDllHandle, IN DWORD nReason, IN LPVOID Reserved) { diff --git a/src/windows/platform.cpp b/src/windows/platform.cpp index c8a4844..e0d0107 100644 --- a/src/windows/platform.cpp +++ b/src/windows/platform.cpp @@ -20,7 +20,7 @@ * @Maintainer Neo */ -#include "../cli.h" +#include "cli/cli.h" #include "resource.h" /** diff --git a/src/windows/umskt.rc b/src/windows/umskt.rc index dffd0ba..9c3d445 100644 Binary files a/src/windows/umskt.rc and b/src/windows/umskt.rc differ