mirror of
https://github.com/Neo-Desktop/WindowsXPKg
synced 2024-11-22 05:41:01 +02:00
Compare commits
No commits in common. "015fd00d3f8117ff8ba00f0b777b4c3dc4fa7008" and "a827844cbd3dd394aee50a0e21bb991dc0c9b1d3" have entirely different histories.
015fd00d3f
...
a827844cbd
@ -1,2 +1,2 @@
|
||||
build*/
|
||||
cmake*/
|
||||
cmake-*/
|
||||
|
16
.github/workflows/dos-djgpp.yml
vendored
16
.github/workflows/dos-djgpp.yml
vendored
@ -57,13 +57,23 @@ jobs:
|
||||
source ${{ github.workspace }}/djgpp/setenv
|
||||
./configur.sh djgpp
|
||||
make -f djgpp.mak
|
||||
ln -s ${WATT_ROOT}/lib/libwatt.a ${CMAKE_FIND_ROOT_PATH}/lib
|
||||
ln -s ${WATT_ROOT}/lib/libwatt.a ${{ github.workspace }}/djgpp/lib
|
||||
|
||||
- name: Checkout and Cross Compile OpenSSL 3.1.2
|
||||
run: |
|
||||
git clone https://github.com/UMSKT/openssl.git openssl
|
||||
pushd openssl
|
||||
source ${{ github.workspace }}/djgpp/setenv
|
||||
./Configure no-threads -DOPENSSL_DEV_NO_ATOMICS --prefix=${{ github.workspace }}/djgpp DJGPP
|
||||
make && make install
|
||||
popd
|
||||
|
||||
- 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/
|
||||
pushd build
|
||||
cmake ../ -D DJGPP_WATT32=${WATT_ROOT}/lib/libwatt.a -D CMAKE_FIND_ROOT_PATH=${CMAKE_FIND_ROOT_PATH}
|
||||
make
|
||||
|
||||
- name: Move executable to upload directory
|
||||
run: |
|
||||
|
9
.github/workflows/freebsd.yml
vendored
9
.github/workflows/freebsd.yml
vendored
@ -41,14 +41,17 @@
|
||||
id: test
|
||||
uses: vmactions/freebsd-vm@v1
|
||||
with:
|
||||
envs: 'MYTOKEN MYTOKEN2'
|
||||
usesh: true
|
||||
prepare: |
|
||||
pkg install -y cmake git bash
|
||||
pkg install -y cmake openssl git bash
|
||||
|
||||
run: |
|
||||
mkdir build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release build/
|
||||
cmake --build build/
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
./umskt # Execute the test here
|
||||
|
||||
- name: Move files to correct directory
|
||||
run: |
|
||||
|
29
.github/workflows/linux.yml
vendored
29
.github/workflows/linux.yml
vendored
@ -31,10 +31,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
arch:
|
||||
- x86
|
||||
- x86_64
|
||||
- aarch64
|
||||
include:
|
||||
- arch: x86
|
||||
- arch: x86_64
|
||||
- arch: aarch64
|
||||
steps:
|
||||
- name: Checkout Source Tree
|
||||
uses: actions/checkout@v3
|
||||
@ -48,13 +48,16 @@ jobs:
|
||||
cmake
|
||||
git
|
||||
musl-dev
|
||||
openssl-dev
|
||||
openssl-libs-static
|
||||
zlib-dev
|
||||
arch: ${{ matrix.arch }}
|
||||
shell-name: alpine-target.sh
|
||||
|
||||
- name: Configure and build UMSKT
|
||||
uses: threeal/cmake-action@v1.3.0
|
||||
uses: threeal/cmake-action@7ef2eb8da6e5ec0a6de6b1ddc96987080bed06e8
|
||||
with:
|
||||
options: MUSL_STATIC=ON CMAKE_BUILD_TYPE=Release
|
||||
options: MUSL_STATIC=ON
|
||||
run-build: true
|
||||
shell: alpine-target.sh {0}
|
||||
|
||||
@ -68,3 +71,17 @@ jobs:
|
||||
with:
|
||||
name: UMSKT-linux-${{ matrix.arch }}-static
|
||||
path: build/actions_upload
|
||||
|
||||
- name: Configure and build static internal deps UMSKT
|
||||
uses: threeal/cmake-action@7ef2eb8da6e5ec0a6de6b1ddc96987080bed06e8
|
||||
with:
|
||||
options: MUSL_STATIC=OFF BUILD_SHARED_LIBS=OFF
|
||||
run-build: true
|
||||
shell: alpine-target.sh {0}
|
||||
|
||||
- name: Configure and build shared deps UMSKT
|
||||
uses: threeal/cmake-action@7ef2eb8da6e5ec0a6de6b1ddc96987080bed06e8
|
||||
with:
|
||||
options: MUSL_STATIC=OFF BUILD_SHARED_LIBS=ON
|
||||
run-build: true
|
||||
shell: alpine-target.sh {0}
|
||||
|
22
.github/workflows/macos.yml
vendored
22
.github/workflows/macos.yml
vendored
@ -27,30 +27,34 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
build-x86:
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
arch:
|
||||
- { name: arm64;x86_64, displayName: all-x86_64_arm64}
|
||||
- { name: x86_64, displayName: x86_64 }
|
||||
- { name: arm64, displayName: arm64 }
|
||||
include:
|
||||
- arch: x86_64
|
||||
steps:
|
||||
- name: Checkout Source Tree
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Configure and build UMSKT ${{ matrix.arch.displayName }}
|
||||
- name: Configure and build UMSKT
|
||||
run: |
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch.name }} build/
|
||||
cmake --build build/
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release ..
|
||||
make
|
||||
|
||||
- name: Move files to correct directory
|
||||
run: |
|
||||
mkdir -p build/actions_upload
|
||||
mv build/umskt build/actions_upload/umskt
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
cd build/actions_upload
|
||||
./umskt
|
||||
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
with:
|
||||
name: UMSKT-macOS-${{ matrix.arch.displayName }}
|
||||
name: UMSKT-macOS-${{ matrix.arch }}
|
||||
path: build/actions_upload
|
||||
|
97
.github/workflows/windows.yml
vendored
97
.github/workflows/windows.yml
vendored
@ -27,7 +27,7 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
build-32bit:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
# https://github.com/actions/runner-images/issues/6067#issuecomment-1213069040
|
||||
@ -52,30 +52,97 @@ jobs:
|
||||
exit 1
|
||||
}
|
||||
|
||||
- name: Download And Install 32-bit OpenSSL 3.1.4
|
||||
run: |
|
||||
$installDir = "$Env:ProgramFiles\OpenSSL"
|
||||
$installerURL = "https://slproweb.com/download/Win32OpenSSL-3_1_4.exe"
|
||||
$installerName = "Win32OpenSSL-3_1_4.exe"
|
||||
$installerPath = Join-Path -Path "${env:Temp}" -ChildPath "$installerName"
|
||||
|
||||
(New-Object System.Net.WebClient).DownloadFile($installerURL, $installerPath)
|
||||
|
||||
Remove-Item "$installDir" -Force -Recurse
|
||||
$installerArgs = '/silent', '/sp-', '/suppressmsgboxes', "/DIR=`"$installDir`""
|
||||
Start-Process -FilePath $installerPath -ArgumentList $installerArgs -Wait -PassThru
|
||||
|
||||
- name: Checkout Source Tree
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- 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
|
||||
- name: Configure UMSKT
|
||||
uses: threeal/cmake-action@v1.2.0
|
||||
with:
|
||||
options: CMAKE_BUILD_TYPE=Release
|
||||
generator: "Visual Studio 17 2022"
|
||||
args: -A "${{ matrix.arch.name }}" -T "v141_xp"
|
||||
run-build: true
|
||||
build-args: "--config Release"
|
||||
options: CMAKE_SYSTEM_VERSION="5.1.2600"
|
||||
args: -A "Win32" -T v141_xp
|
||||
|
||||
- name: Build UMSKT
|
||||
working-directory: build
|
||||
run: msbuild ALL_BUILD.vcxproj /P:Configuration=Release
|
||||
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
with:
|
||||
name: UMSKT-${{ matrix.values.displayName }}
|
||||
name: UMSKT-Win32
|
||||
path: build/Release
|
||||
|
||||
build-64bit:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- 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: Download And Install 64-bit OpenSSL 3.1.4
|
||||
run: |
|
||||
$installDir = "$Env:ProgramFiles\OpenSSL"
|
||||
$installerURL = "https://slproweb.com/download/Win64OpenSSL-3_1_4.exe"
|
||||
$installerName = "Win64OpenSSL-3_1_4.exe"
|
||||
$installerPath = Join-Path -Path "${env:Temp}" -ChildPath "$installerName"
|
||||
|
||||
(New-Object System.Net.WebClient).DownloadFile($installerURL, $installerPath)
|
||||
|
||||
Remove-Item "$installDir" -Force -Recurse
|
||||
$installerArgs = '/silent', '/sp-', '/suppressmsgboxes', "/DIR=`"$installDir`""
|
||||
Start-Process -FilePath $installerPath -ArgumentList $installerArgs -Wait -PassThru
|
||||
|
||||
- name: Checkout Source Tree
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup MSBuild
|
||||
uses: microsoft/setup-msbuild@v1
|
||||
|
||||
- name: Configure UMSKT
|
||||
uses: threeal/cmake-action@v1.2.0
|
||||
with:
|
||||
generator: "Visual Studio 17 2022"
|
||||
args: -A "x64" -T "v141_xp"
|
||||
|
||||
- name: Build UMSKT
|
||||
working-directory: build
|
||||
run: msbuild ALL_BUILD.vcxproj /P:Configuration=Release
|
||||
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
with:
|
||||
name: UMSKT-Win64
|
||||
path: build/Release
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,4 +1,4 @@
|
||||
build*/
|
||||
build/*
|
||||
*.tar
|
||||
*.exe
|
||||
*.wasm
|
||||
@ -66,7 +66,7 @@ umskt
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake*/
|
||||
cmake-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
@ -23,8 +23,3 @@ repos:
|
||||
rev: 'v17.0.6' # Use the sha / tag you want to point at
|
||||
hooks:
|
||||
- id: clang-format
|
||||
|
||||
# - repo: https://github.com/cheshirekow/cmake-format-precommit
|
||||
# rev: v0.6.10
|
||||
# hooks:
|
||||
# - id: cmake-format
|
144
CMakeLists.txt
144
CMakeLists.txt
@ -26,20 +26,32 @@ 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(UMSKT_USE_SHARED_OPENSSL "Force linking against the system-wide OpenSSL library" 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(DJGPP_WATT32 "Enable compilation and linking with DJGPP/WATT32/OpenSSL" OFF)
|
||||
OPTION(MSVC_MSDOS_STUB "Specify a custom MS-DOS stub for a 32-bit MSVC compilation" OFF)
|
||||
|
||||
SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS})
|
||||
SET(UMSKT_LINK_DIRS ${UMSKT_LINK_DIRS})
|
||||
|
||||
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_RELWITHDEBINFO "-g")
|
||||
SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT "-Wall -Wextra")
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
|
||||
# macOS does not support static build
|
||||
IF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
SET(UMSKT_USE_SHARED_OPENSSL ON)
|
||||
ENDIF ()
|
||||
|
||||
# neither does dos idk i'm trying random stuff
|
||||
IF (DJGPP_WATT32)
|
||||
SET(UMSKT_USE_SHARED_OPENSSL ON)
|
||||
ENDIF ()
|
||||
|
||||
IF (UMSKT_USE_SHARED_OPENSSL)
|
||||
SET(OPENSSL_USE_STATIC_LIBS FALSE)
|
||||
SET(OPENSSL_MSVC_STATIC_RT FALSE)
|
||||
MESSAGE(STATUS "[UMSKT] Requesting dynamic version of OpenSSL")
|
||||
ELSE ()
|
||||
SET(OPENSSL_USE_STATIC_LIBS TRUE)
|
||||
SET(OPENSSL_MSVC_STATIC_RT TRUE)
|
||||
MESSAGE(STATUS "[UMSKT] Requesting static version of OpenSSL")
|
||||
ENDIF ()
|
||||
|
||||
IF (DJGPP_WATT32)
|
||||
@ -60,11 +72,9 @@ ENDIF ()
|
||||
# if we're compiling with MSVC, respect the DEBUG compile option
|
||||
IF (MSVC)
|
||||
SET(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
SET(CMAKE_CXX_FLAGS "/O1 /GL /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")
|
||||
SET(CMAKE_EXE_LINKER_FLAGS_DEBUG "/DEBUG")
|
||||
SET(CMAKE_CXX_FLAGS_DEBUG "/MTd")
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "/INCREMENTAL:NO /NODEFAULTLIB:MSVCRT")
|
||||
SET(CMAKE_ENABLE_EXPORTS ON)
|
||||
SET(UMSKT_EXE_WINDOWS_EXTRA src/windows/umskt.rc)
|
||||
SET(UMSKT_EXE_WINDOWS_DLL src/windows/dllmain.cpp)
|
||||
@ -73,11 +83,53 @@ ENDIF ()
|
||||
IF (MUSL_STATIC)
|
||||
MESSAGE(STATUS "[UMSKT] Performing a fully static build using muslc")
|
||||
SET(BUILD_SHARED_LIBS OFF)
|
||||
SET(OPENSSL_USE_STATIC_LIBS TRUE)
|
||||
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "-static -static-libgcc -static-libstdc++")
|
||||
SET(CMAKE_SHARED_LINKER_FLAGS "-static -static-libgcc -static-libstdc++")
|
||||
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++")
|
||||
ENDIF ()
|
||||
|
||||
# find the system installed OpenSSL development library
|
||||
FIND_PACKAGE(OpenSSL REQUIRED)
|
||||
IF (NOT OPENSSL_FOUND)
|
||||
MESSAGE(SEND_ERROR "OpenSSL Development Libraries Not Found")
|
||||
MESSAGE(SEND_ERROR "Please consult your package manager of choice to install the prerequisite")
|
||||
MESSAGE(SEND_ERROR "The package name is commonly called libssl-dev or openssl-dev depending on distribution")
|
||||
MESSAGE(FATAL_ERROR "Can not continue without OpenSSL")
|
||||
ENDIF ()
|
||||
|
||||
IF (NOT MUSL_STATIC)
|
||||
# if we found shared libraries - do the following:
|
||||
IF (OPENSSL_USE_STATIC_LIBS)
|
||||
MESSAGE(STATUS "[UMSKT] requested static version of OpenSSL")
|
||||
IF (NOT UMSKT_USE_SHARED_OPENSSL)
|
||||
MESSAGE(STATUS "[UMSKT] not asked for shared version of OpenSSL")
|
||||
ENDIF ()
|
||||
|
||||
IF (MSVC)
|
||||
SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS} "ws2_32.lib")
|
||||
SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS} "crypt32.lib")
|
||||
MESSAGE(STATUS "[UMSKT] msvc adding ws2_32.lib crypt32.lib")
|
||||
ENDIF ()
|
||||
ENDIF ()
|
||||
|
||||
STRING(REGEX MATCH "(\\.so|\\.dll|\\.dylib)$" OPENSSL_CRYPTO_SHARED "${OPENSSL_CRYPTO_LIBRARY}")
|
||||
IF (OPENSSL_CRYPTO_SHARED)
|
||||
MESSAGE(STATUS "[UMSKT] Detected Shared library version of OpenSSL")
|
||||
ELSE ()
|
||||
MESSAGE(STATUS "[UMSKT] Detected Static Library version of OpenSSL")
|
||||
|
||||
# static libcrypto on Fedora needs -lz at link time
|
||||
IF (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
FIND_PACKAGE(ZLIB REQUIRED)
|
||||
IF (NOT ZLIB_FOUND)
|
||||
MESSAGE(FATAL_ERROR "[UMSKT] linux static OpenSSL requires zlib")
|
||||
ENDIF ()
|
||||
ENDIF ()
|
||||
ENDIF ()
|
||||
ENDIF ()
|
||||
|
||||
# initialize cpm.CMake
|
||||
@ -114,13 +166,13 @@ CPMAddPackage(
|
||||
)
|
||||
|
||||
# Include Crypto++ development library
|
||||
CPMAddPackage(
|
||||
NAME cryptopp-cmake
|
||||
GITHUB_REPOSITORY abdes/cryptopp-cmake
|
||||
GIT_TAG CRYPTOPP_8_9_0
|
||||
VERSION 8.9.0
|
||||
OPTIONS "CRYPTOPP_BUILD_TESTING OFF"
|
||||
)
|
||||
#CPMAddPackage(
|
||||
# NAME cryptopp-cmake
|
||||
# GITHUB_REPOSITORY abdes/cryptopp-cmake
|
||||
# GIT_TAG CRYPTOPP_8_8_0
|
||||
# VERSION 8.8.0
|
||||
# OPTIONS "CRYPTOPP_BUILD_TESTING OFF"
|
||||
#)
|
||||
|
||||
#include googletest unit testing library
|
||||
#CPMAddPackage(
|
||||
@ -136,9 +188,9 @@ CMRC_ADD_RESOURCE_LIBRARY(umskt-rc ALIAS umskt::rc NAMESPACE umskt keys.json)
|
||||
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(LIBUMSKT_SRC src/libumskt/libumskt.cpp src/libumskt/debugoutput.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_LINK_LIBS ${UMSKT_LINK_LIBS} ${OPENSSL_CRYPTO_LIBRARIES} ${ZLIB_LIBRARIES} fmt)
|
||||
|
||||
IF (NOT MSVC)
|
||||
SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS} umskt::rc)
|
||||
@ -150,47 +202,39 @@ 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_LINK_DIRECTORIES(umskt PUBLIC ${UMSKT_LINK_DIRS})
|
||||
TARGET_INCLUDE_DIRECTORIES(umskt PUBLIC ${OPENSSL_INCLUDE_DIR})
|
||||
TARGET_LINK_LIBRARIES(umskt PUBLIC ${UMSKT_LINK_LIBS})
|
||||
SET(CMAKE_EXECUTABLE_SUFFIX ".html")
|
||||
|
||||
SET_TARGET_PROPERTIES(umskt PROPERTIES COMPILE_FLAGS "-sEXPORTED_RUNTIME_METHODS=ccall,cwrap")
|
||||
SET_TARGET_PROPERTIES(umskt PROPERTIES LINK_FLAGS "-sWASM=1 -sEXPORT_ALL=1 -sEXPORTED_RUNTIME_METHODS=ccall,cwrap --no-entry")
|
||||
SET_TARGET_PROPERTIES(umskt PROPERTIES COMPILE_FLAGS "-Os -sEXPORTED_RUNTIME_METHODS=ccall,cwrap")
|
||||
SET_TARGET_PROPERTIES(umskt PROPERTIES LINK_FLAGS "-Os -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})
|
||||
ADD_LIBRARY(_umskt SHARED ${LIBUMSKT_SRC} ${UMSKT_EXE_WINDOWS_EXTRA} ${UMSKT_EXE_WINDOWS_DLL})
|
||||
TARGET_INCLUDE_DIRECTORIES(_umskt PUBLIC ${OPENSSL_INCLUDE_DIR} ${CMAKE_BINARY_DIR})
|
||||
TARGET_LINK_DIRECTORIES(_umskt PUBLIC ${UMSKT_LINK_DIRS})
|
||||
TARGET_LINK_LIBRARIES(_umskt PUBLIC ${UMSKT_LINK_LIBS})
|
||||
IF(MSVC)
|
||||
SET_TARGET_PROPERTIES(libumskt PROPERTIES OUTPUT_NAME libumskt)
|
||||
SET_TARGET_PROPERTIES(_umskt PROPERTIES OUTPUT_NAME libumskt)
|
||||
ELSE()
|
||||
SET_TARGET_PROPERTIES(libumskt PROPERTIES OUTPUT_NAME umskt)
|
||||
SET_TARGET_PROPERTIES(_umskt PROPERTIES OUTPUT_NAME umskt)
|
||||
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})
|
||||
ADD_LIBRARY(umskt_static ${LIBUMSKT_SRC} ${UMSKT_EXE_WINDOWS_EXTRA} ${UMSKT_EXE_WINDOWS_DLL})
|
||||
TARGET_INCLUDE_DIRECTORIES(umskt_static PUBLIC ${OPENSSL_INCLUDE_DIR} ${CMAKE_BINARY_DIR})
|
||||
TARGET_LINK_DIRECTORIES(umskt_static PUBLIC ${UMSKT_LINK_DIRS})
|
||||
TARGET_LINK_LIBRARIES(umskt_static PUBLIC ${UMSKT_LINK_LIBS})
|
||||
IF(MSVC)
|
||||
SET_TARGET_PROPERTIES(libumskt_static PROPERTIES OUTPUT_NAME libumskt_static)
|
||||
SET_TARGET_PROPERTIES(umskt_static PROPERTIES OUTPUT_NAME libumskt_static)
|
||||
ELSE()
|
||||
SET_TARGET_PROPERTIES(libumskt_static PROPERTIES OUTPUT_NAME umskt_static)
|
||||
SET_TARGET_PROPERTIES(umskt_static PROPERTIES OUTPUT_NAME umskt_static)
|
||||
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_INCLUDE_DIRECTORIES(umskt PUBLIC ${OPENSSL_INCLUDE_DIR} ${CMAKE_BINARY_DIR})
|
||||
TARGET_LINK_LIBRARIES(umskt PUBLIC umskt_static ${UMSKT_LINK_LIBS} nlohmann_json::nlohmann_json)
|
||||
TARGET_LINK_DIRECTORIES(umskt PUBLIC ${UMSKT_LINK_DIRS})
|
||||
IF (MSVC)
|
||||
SET_PROPERTY(TARGET umskt APPEND PROPERTY COMPILE_FLAGS /DUMSKT_CLI_WINRC_EMBED_JSON)
|
||||
@ -202,4 +246,10 @@ ELSE ()
|
||||
IF (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
INSTALL(TARGETS umskt DESTINATION bin)
|
||||
ENDIF ()
|
||||
|
||||
### Copy Shared Libraries and dependency files
|
||||
IF (OPENSSL_CRYPTO_SHARED)
|
||||
GET_FILENAME_COMPONENT(OPENSSL_CRYPTO_LIBRARY_FILENAME ${OPENSSL_CRYPTO_LIBRARY} NAME)
|
||||
CONFIGURE_FILE(${OPENSSL_CRYPTO_LIBRARY} "${CMAKE_CURRENT_BINARY_DIR}/${OPENSSL_CRYPTO_LIBRARY_FILENAME}" COPYONLY)
|
||||
ENDIF ()
|
||||
ENDIF ()
|
||||
|
@ -40,15 +40,14 @@ COPY . /src
|
||||
|
||||
# Build UMSKT from the local directory
|
||||
RUN mkdir /src/build \
|
||||
&& cmake -B /src/build -DMUSL_STATIC=ON \
|
||||
&& cmake --build /src/build -j 10
|
||||
&& cd /src/build \
|
||||
&& cmake -DMUSL_STATIC=ON .. \
|
||||
&& make
|
||||
|
||||
# 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
|
||||
|
||||
# invoke via
|
||||
# docker build -o type=tar,dest=umskt.tar .
|
||||
|
@ -19,54 +19,92 @@
|
||||
# @Maintainer Neo
|
||||
|
||||
# Stage 1: Install Prerequisites
|
||||
#FROM ubuntu:latest as prerequisites
|
||||
FROM alpine: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 apk add --no-cache \
|
||||
autoconf \
|
||||
automake \
|
||||
bash \
|
||||
bison \
|
||||
build-base \
|
||||
clang \
|
||||
cmake \
|
||||
coreutils \
|
||||
curl \
|
||||
elfutils-dev \
|
||||
findutils \
|
||||
git \
|
||||
gawk \
|
||||
flex \
|
||||
libelf \
|
||||
libslirp-dev \
|
||||
linux-headers \
|
||||
nasm \
|
||||
sed \
|
||||
slang-dev \
|
||||
texinfo \
|
||||
unzip \
|
||||
zlib-dev
|
||||
|
||||
|
||||
FROM djgpp-prerequisites:latest as djgpp-watt32
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
FROM prerequisites as djgpp
|
||||
|
||||
WORKDIR /
|
||||
WORKDIR /tmp
|
||||
|
||||
ENV CC=/djgpp/bin/i586-pc-msdosdjgpp-gcc CXX=/djgpp/bin/i586-pc-msdosdjgpp-g++ CMAKE_FIND_ROOT_PATH=/djgpp WATT_ROOT=/djgpp/watt32
|
||||
|
||||
# Stage 2: compile WATT32 for D
|
||||
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 \
|
||||
# Stage 2: compile djgpp for muslc
|
||||
ENV DJGPP_PREFIX=/djgpp BUILD_VER=12.2.0-i386
|
||||
RUN git clone https://github.com/andrewwutw/build-djgpp.git djgpp \
|
||||
&& cd djgpp \
|
||||
&& git clone https://github.com/UMSKT/Watt-32.git watt32 \
|
||||
&& cd script \
|
||||
&& wget https://gist.github.com/Neo-Desktop/4cfd708f61f5847a7bf457d38db3b59f/raw/25d24cf509b0fc486d5d18ecb6656f120c3d0e51/12.2.0-i386 -O 12.2.0-i386 \
|
||||
&& chmod +x 12.2.0-i386 \
|
||||
&& cd ../patch \
|
||||
&& wget https://gist.github.com/Neo-Desktop/4cfd708f61f5847a7bf457d38db3b59f/raw/25d24cf509b0fc486d5d18ecb6656f120c3d0e51/patch-alpine-Fix-attempt-to-use-poisoned-calloc-error-in-libgccji.patch -O patch-alpine-Fix-attempt-to-use-poisoned-calloc-error-in-libgccji.patch \
|
||||
&& cd .. \
|
||||
&& sed -i 's/i586/i386/g' setenv/setenv \
|
||||
&& sed -i 's/i586/i386/g' setenv/setenv.bat \
|
||||
&& ./build-djgpp.sh $BUILD_VER \
|
||||
&& rm -rf /tmp/djgpp
|
||||
|
||||
# Stage 3: compile watt32 for djgpp-i386
|
||||
FROM djgpp as watt32
|
||||
WORKDIR /djgpp
|
||||
ENV WATT_ROOT=/djgpp/watt32 DJGPP_PREFIX=i386-pc-msdosdjgpp
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
RUN git clone https://github.com/gvanem/Watt-32.git watt32 \
|
||||
&& cd watt32/util \
|
||||
&& make clean && make linux \
|
||||
&& cd ../src \
|
||||
&& source /djgpp/setenv \
|
||||
&& ./configur.sh djgpp \
|
||||
&& sed -i 's/i586/i386/g' djgpp.mak \
|
||||
&& wget https://gist.github.com/Neo-Desktop/ad26e888d64b22a59c743ab4e21ac186/raw/c9a73e1eb75ba8857883ac5c08691d2fe5b82594/djgpp.err -O ../inc/sys/djgpp.err \
|
||||
&& wget https://gist.github.com/Neo-Desktop/ad26e888d64b22a59c743ab4e21ac186/raw/c9a73e1eb75ba8857883ac5c08691d2fe5b82594/syserr.c -O build/djgpp/syserr.c \
|
||||
&& make -f djgpp.mak \
|
||||
&& ln -s ${WATT_ROOT}/lib/libwatt.a ${CMAKE_FIND_ROOT_PATH}/lib
|
||||
&& ln -s /djgpp/watt32/lib/libwatt.a /djgpp/lib
|
||||
|
||||
# Stage 3: compile UMSKT
|
||||
FROM djgpp-watt32 as build
|
||||
# Stage 4: compile OpenSSL for djgpp-i386/watt32
|
||||
FROM watt32 as openssl
|
||||
WORKDIR /tmp
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
RUN git clone https://github.com/openssl/openssl.git openssl \
|
||||
&& cd openssl \
|
||||
&& git checkout openssl-3.1.1 \
|
||||
&& source /djgpp/setenv \
|
||||
&& wget https://gist.github.com/Neo-Desktop/ad26e888d64b22a59c743ab4e21ac186/raw/c9a73e1eb75ba8857883ac5c08691d2fe5b82594/50-djgpp.conf.patch -O Configurations/50-djgpp.conf.patch \
|
||||
&& git apply Configurations/50-djgpp.conf.patch \
|
||||
&& ./Configure no-threads -DOPENSSL_DEV_NO_ATOMICS --prefix=/djgpp DJGPP \
|
||||
&& make && make install
|
||||
|
||||
# Stage 5: compile UMSKT
|
||||
FROM openssl as build
|
||||
|
||||
WORKDIR /src
|
||||
COPY . /src
|
||||
|
||||
ENV PKG_CONFIG_PATH=/djgpp/lib/pkgconfig VERBOSE=1
|
||||
ENV CC=/djgpp/bin/i386-pc-msdosdjgpp-gcc CXX=/djgpp/bin/i386-pc-msdosdjgpp-g++ PKG_CONFIG_PATH=/djgpp/lib/pkgconfig VERBOSE=1
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
# Build UMSKT from the local directory
|
||||
RUN mkdir /src/build \
|
||||
&& cd /src/build \
|
||||
@ -82,4 +120,4 @@ FROM scratch as output
|
||||
COPY --from=build /src/build/umskt.exe /umskt.exe
|
||||
|
||||
# invoke via
|
||||
# docker build -f Dockerfile.djgpp -o type=tar,dest=build-djgpp/umskt-dos.tar .
|
||||
# docker build -f Dockerfile.djgpp -o type=tar,dest=umskt-dos.tar .
|
||||
|
@ -57,9 +57,13 @@ In light of the recent exponential interest in this project I've decided to put
|
||||
|
||||
* **Note:** Before continuing, please ensure you have the `umskt` executable extracted and on UNIX-like systems, have execution permissions (`chmod +x umskt`).
|
||||
|
||||
#### 2. Run `umskt` to generate a key, or add `--help` or `-h` to see more options.
|
||||
#### 2. Install OpenSSL 3.1.2.
|
||||
For Windows, click [here](https://slproweb.com/products/Win32OpenSSL.html) and choose the right version. For other operating systems, consult your package manager.
|
||||
*Note: This only applies if the build you download has OpenSSL embedded (static library) or not. You can usually tell if the download size is measured in KB or MB. If it's MB, you don't need this.*
|
||||
|
||||
#### 3. *(Activation step for `Retail` and `OEM` only)*
|
||||
#### 3. Run `umskt` to generate a key, or add `--help` or `-h` to see more options.
|
||||
|
||||
#### 4. *(Activation step for `Retail` and `OEM` only)*
|
||||
* After installation, you will be prompted to activate Windows.
|
||||
|
||||
|
||||
|
0
build/.gitkeep
Normal file
0
build/.gitkeep
Normal file
105
keys.json
105
keys.json
@ -39,20 +39,14 @@
|
||||
"WIN95": {
|
||||
"meta": {
|
||||
"type": "PIDGEN2",
|
||||
"tags": [
|
||||
"windows",
|
||||
"legacyoempid"
|
||||
]
|
||||
"tags": "windows"
|
||||
},
|
||||
"name": "Windows 95 (all)"
|
||||
},
|
||||
"WINNT": {
|
||||
"meta": {
|
||||
"type": "PIDGEN2",
|
||||
"tags": [
|
||||
"windows",
|
||||
"legacyoempid"
|
||||
]
|
||||
"tags": "windows"
|
||||
},
|
||||
"name": "Windows NT (all)"
|
||||
},
|
||||
@ -60,8 +54,7 @@
|
||||
"meta": {
|
||||
"type": "PIDGEN3",
|
||||
"tags": [
|
||||
"windows",
|
||||
"legacyoempid"
|
||||
"windows"
|
||||
]
|
||||
},
|
||||
"name": "Windows 98 (all versions)",
|
||||
@ -74,8 +67,7 @@
|
||||
"meta": {
|
||||
"type": "PIDGEN3",
|
||||
"tags": [
|
||||
"office",
|
||||
"legacyoempid"
|
||||
"office"
|
||||
]
|
||||
},
|
||||
"name": "Office 2000 (all versions)",
|
||||
@ -129,8 +121,7 @@
|
||||
"type": "PIDGEN3",
|
||||
"default": "PRO",
|
||||
"tags": [
|
||||
"windows",
|
||||
"legacyoempid"
|
||||
"windows"
|
||||
]
|
||||
},
|
||||
"name": "Windows 2000",
|
||||
@ -168,8 +159,7 @@
|
||||
"meta": {
|
||||
"type": "PIDGEN3",
|
||||
"tags": [
|
||||
"windows",
|
||||
"legacypid"
|
||||
"windows"
|
||||
]
|
||||
},
|
||||
"name": "Windows ME",
|
||||
@ -293,7 +283,7 @@
|
||||
"WINXP": {
|
||||
"meta": {
|
||||
"type": "PIDGEN3",
|
||||
"default": "VLK",
|
||||
"default": "PROVLK",
|
||||
"tags": [
|
||||
"windows",
|
||||
"xpbrand"
|
||||
@ -473,56 +463,17 @@
|
||||
"BINK": [
|
||||
"2C",
|
||||
"2D"
|
||||
],
|
||||
"DPC": {
|
||||
"2C": [
|
||||
{
|
||||
"min": 5,
|
||||
"max": 85,
|
||||
"isEvaluation": false
|
||||
},
|
||||
{
|
||||
"min": 337,
|
||||
"max": 359,
|
||||
"isEvaluation": false
|
||||
},
|
||||
{
|
||||
"min": 755,
|
||||
"max": 779,
|
||||
"isEvaluation": false
|
||||
},
|
||||
{
|
||||
"min": 785,
|
||||
"max": 789,
|
||||
"isEvaluation": false
|
||||
}
|
||||
],
|
||||
"2D": [
|
||||
{
|
||||
"min": 119,
|
||||
"max": 119,
|
||||
"isEvaluation": false
|
||||
},
|
||||
{
|
||||
"min": 120,
|
||||
"max": 169,
|
||||
"isEvaluation": false
|
||||
},
|
||||
{
|
||||
"min": 400,
|
||||
"max": 699,
|
||||
"isEvaluation": false
|
||||
},
|
||||
{
|
||||
"min": 170,
|
||||
"max": 269,
|
||||
"isEvaluation": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"VLK": {
|
||||
"name": "Home/Professional VLK",
|
||||
"PROK": {
|
||||
"name": "Professional K",
|
||||
"BINK": [
|
||||
"30",
|
||||
"31"
|
||||
]
|
||||
},
|
||||
"PROVLK": {
|
||||
"name": "Professional VLK",
|
||||
"BINK": [
|
||||
"2E",
|
||||
"2F"
|
||||
@ -533,6 +484,16 @@
|
||||
"min": 640,
|
||||
"max": 699,
|
||||
"isEvaluation": false
|
||||
},
|
||||
{
|
||||
"min": 700,
|
||||
"max": 701,
|
||||
"isEvaluation": false
|
||||
},
|
||||
{
|
||||
"min": 704,
|
||||
"max": 705,
|
||||
"isEvaluation": false
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -712,20 +673,6 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"CNOEMHOME": {
|
||||
"name": "[CN] OEM Home",
|
||||
"BINK": [
|
||||
"30",
|
||||
"31"
|
||||
]
|
||||
},
|
||||
"CNOEMPRO": {
|
||||
"name": "[CN] OEM Professional",
|
||||
"BINK": [
|
||||
"32",
|
||||
"33"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
265
src/cli.cpp
265
src/cli.cpp
@ -23,7 +23,7 @@
|
||||
#include "cli.h"
|
||||
|
||||
// define static storage
|
||||
CLI::Options CLI::options;
|
||||
Options CLI::options;
|
||||
json CLI::keys;
|
||||
|
||||
/**
|
||||
@ -35,14 +35,8 @@ json CLI::keys;
|
||||
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;
|
||||
options = {argcIn, argvIn, "2E", "", "", "", "", "", "WINXP", "PROVLK", 0,
|
||||
0, 1, false, false, false, false, false, false, false, PIDGEN_3, STATE_PIDGEN_GENERATE};
|
||||
|
||||
SetHelpText();
|
||||
|
||||
@ -103,7 +97,7 @@ BOOL CLI::loadJSON(const fs::path &filename)
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Loading keys file: {}\n", options.keysFilename);
|
||||
fmt::print("Loading keys file {}\n", options.keysFilename);
|
||||
}
|
||||
|
||||
std::ifstream f(filename);
|
||||
@ -119,31 +113,74 @@ BOOL CLI::loadJSON(const fs::path &filename)
|
||||
|
||||
if (keys.is_discarded())
|
||||
{
|
||||
fmt::print("ERROR: Unable to parse keys from: {}\n", filename.string());
|
||||
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);
|
||||
fmt::print("Loaded keys from {} successfully\n", options.keysFilename);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param pid
|
||||
*/
|
||||
void CLI::printID(DWORD *pid)
|
||||
{
|
||||
char raw[12], b[6], c[8];
|
||||
char i, digit = 0;
|
||||
|
||||
// Convert PID to ascii-number (=raw)
|
||||
snprintf(raw, sizeof(raw), "%09lu", pid[0]);
|
||||
|
||||
// Make b-part {640-....}
|
||||
_strncpy(b, 6, &raw[0], 3);
|
||||
b[3] = 0;
|
||||
|
||||
// Make c-part {...-123456X...}
|
||||
_strcpy(c, &raw[3]);
|
||||
|
||||
// Make checksum digit-part {...56X-}
|
||||
assert(strlen(c) == 6);
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
digit += c[i] - '0'; // Sum digits
|
||||
}
|
||||
|
||||
digit %= 7;
|
||||
if (digit > 0)
|
||||
{
|
||||
digit = 7 - digit;
|
||||
}
|
||||
|
||||
c[6] = digit + '0';
|
||||
c[7] = 0;
|
||||
|
||||
DWORD binkid;
|
||||
_sscanf(options.binkID.c_str(), "%lx", &binkid);
|
||||
binkid /= 2;
|
||||
|
||||
fmt::print("> Product ID: PPPPP-{}-{}-{}xxx\n", b, c, binkid);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param pidgen3
|
||||
* @return success
|
||||
*/
|
||||
BOOL CLI::InitPIDGEN3(PIDGEN3 *p3)
|
||||
BOOL CLI::InitPIDGEN3(PIDGEN3 &pidgen3)
|
||||
{
|
||||
auto bink = keys["BINK"][options.binkID];
|
||||
const char *BINKID = &options.binkID[0];
|
||||
auto bink = keys["BINK"][BINKID];
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("{:->80}\n", "");
|
||||
fmt::print("Loaded the following elliptic curve parameters: BINK[{}]\n", options.binkID);
|
||||
fmt::print("Loaded the following elliptic curve parameters: BINK[{}]\n", BINKID);
|
||||
fmt::print("{:->80}\n", "");
|
||||
fmt::print("{:>6}: {}\n", "P", bink["p"]);
|
||||
fmt::print("{:>6}: {}\n", "a", bink["a"]);
|
||||
@ -155,38 +192,28 @@ BOOL CLI::InitPIDGEN3(PIDGEN3 *p3)
|
||||
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"]);
|
||||
pidgen3.LoadEllipticCurve(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)
|
||||
if (options.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;
|
||||
pidgen3.info.setChannelID(options.channelID);
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("> Channel ID: {:d}\n", options.channelID);
|
||||
fmt::print("> Channel ID: {:03d}\n", options.channelID);
|
||||
}
|
||||
|
||||
if (options.serial.NotZero() && p3->checkFieldIsBink1998())
|
||||
if (options.serialSet)
|
||||
{
|
||||
p3->info.Serial = options.serial;
|
||||
pidgen3.info.setSerial(options.serial);
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("> Serial {:d}\n", options.serial);
|
||||
fmt::print("> Serial {:#09d}\n", options.serial);
|
||||
}
|
||||
}
|
||||
else if (options.serial.NotZero() && !p3->checkFieldIsBink1998())
|
||||
{
|
||||
fmt::print("Warning: Discarding user-supplied serial for BINK2002\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -198,10 +225,15 @@ BOOL CLI::InitPIDGEN3(PIDGEN3 *p3)
|
||||
*/
|
||||
BOOL CLI::InitConfirmationID(ConfirmationID &confid)
|
||||
{
|
||||
auto ctx = BN_CTX_new();
|
||||
BIGNUM *pkey = BN_CTX_get(ctx), *nonresidue = BN_CTX_get(ctx), *modulous = BN_CTX_get(ctx);
|
||||
BIGNUM *fvals[6];
|
||||
QWORD fvalsq[6];
|
||||
|
||||
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);
|
||||
fmt::print("ERROR: product flavour {} does not have known activation values", options.productCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -209,7 +241,7 @@ BOOL CLI::InitConfirmationID(ConfirmationID &confid)
|
||||
|
||||
if (!keys["activation"].contains(meta["flavour"]))
|
||||
{
|
||||
fmt::print("ERROR: \"{}\" is an unknown activation flavour", meta["flavour"]);
|
||||
fmt::print("ERROR: {} is an unknown activation flavour", meta["flavour"]);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -232,10 +264,17 @@ BOOL CLI::InitConfirmationID(ConfirmationID &confid)
|
||||
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"]);
|
||||
for (BYTE i = 0; i < 6; i++)
|
||||
{
|
||||
fvals[i] = BN_CTX_get(ctx);
|
||||
auto xval = flavour["x"][fmt::format("{}", i)].get<std::string>();
|
||||
BN_dec2bn(&fvals[i], xval.c_str());
|
||||
UMSKT::BN_bn2lebin(fvals[i], (unsigned char *)&fvalsq[i], sizeof(*fvalsq));
|
||||
}
|
||||
|
||||
// confid.LoadHyperellipticCurve(fvals, );
|
||||
|
||||
BN_CTX_free(ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -243,58 +282,79 @@ BOOL CLI::InitConfirmationID(ConfirmationID &confid)
|
||||
*
|
||||
* @return success
|
||||
*/
|
||||
BOOL CLI::PIDGenerate()
|
||||
BOOL CLI::PIDGENGenerate()
|
||||
{
|
||||
BOOL retval = false;
|
||||
// TODO:
|
||||
// if options.pidgen2generate
|
||||
// return pidgen2generate
|
||||
// otherwise...
|
||||
|
||||
if (options.pidgenversion == Options::PIDGEN_VERSION::PIDGEN_2)
|
||||
const char *BINKID = &options.binkID[0];
|
||||
auto bink = keys["BINK"][BINKID];
|
||||
|
||||
std::string key;
|
||||
bink["p"].get_to(key);
|
||||
|
||||
if (PIDGEN3::checkFieldStrIsBink1998(key))
|
||||
{
|
||||
auto p2 = PIDGEN2();
|
||||
retval = PIDGEN2Generate(p2);
|
||||
return retval;
|
||||
}
|
||||
else if (options.pidgenversion == Options::PIDGEN_VERSION::PIDGEN_3)
|
||||
if (options.verbose)
|
||||
{
|
||||
auto bink = keys["BINK"][options.binkID];
|
||||
|
||||
auto p3 = PIDGEN3::Factory(bink["p"]);
|
||||
InitPIDGEN3(p3);
|
||||
retval = PIDGEN3Generate(p3);
|
||||
|
||||
delete p3;
|
||||
return retval;
|
||||
fmt::print("Detected a BINK1998 key\n");
|
||||
}
|
||||
|
||||
return retval;
|
||||
auto bink1998 = BINK1998();
|
||||
InitPIDGEN3(bink1998);
|
||||
return BINK1998Generate(bink1998);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Detected a BINK2002 key\n");
|
||||
}
|
||||
auto bink2002 = BINK2002();
|
||||
InitPIDGEN3(bink2002);
|
||||
return BINK2002Generate(bink2002);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return isValid
|
||||
*/
|
||||
BOOL CLI::PIDValidate()
|
||||
BOOL CLI::PIDGENValidate()
|
||||
{
|
||||
BOOL retval = false;
|
||||
// TODO:
|
||||
// if options.pidgen2validate
|
||||
// return pidgen2validate
|
||||
// otherwise...
|
||||
|
||||
if (options.pidgenversion == Options::PIDGEN_VERSION::PIDGEN_2)
|
||||
const char *BINKID = &options.binkID[0];
|
||||
auto bink = keys["BINK"][BINKID];
|
||||
|
||||
std::string key;
|
||||
bink["p"].get_to(key);
|
||||
|
||||
if (PIDGEN3::checkFieldStrIsBink1998(key))
|
||||
{
|
||||
auto p2 = PIDGEN2();
|
||||
retval = PIDGEN2Validate(p2);
|
||||
return retval;
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Detected a BINK1998 key\n");
|
||||
}
|
||||
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;
|
||||
auto bink1998 = BINK1998();
|
||||
InitPIDGEN3(bink1998);
|
||||
return BINK1998Validate(bink1998);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Detected a BINK2002 key\n");
|
||||
}
|
||||
auto bink2002 = BINK2002();
|
||||
InitPIDGEN3(bink2002);
|
||||
return BINK2002Validate(bink2002);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -310,16 +370,63 @@ int CLI::Run()
|
||||
*/
|
||||
switch (options.state)
|
||||
{
|
||||
case Options::APPLICATION_STATE::STATE_PIDGEN_GENERATE:
|
||||
return PIDGenerate();
|
||||
case STATE_PIDGEN_GENERATE:
|
||||
return PIDGENGenerate();
|
||||
|
||||
case Options::APPLICATION_STATE::STATE_PIDGEN_VALIDATE:
|
||||
return PIDValidate();
|
||||
case STATE_PIDGEN_VALIDATE:
|
||||
return PIDGENValidate();
|
||||
|
||||
case Options::APPLICATION_STATE::STATE_CONFIRMATION_ID:
|
||||
case STATE_CONFIRMATION_ID:
|
||||
return ConfirmationIDGenerate();
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a product key to stdout
|
||||
*
|
||||
* @param pk std::string to print
|
||||
*/
|
||||
void CLI::printKey(std::string &pk)
|
||||
{
|
||||
assert(pk.length() >= PK_LENGTH);
|
||||
|
||||
fmt::print("{}-{}-{}-{}-{}", pk.substr(0, 5), pk.substr(5, 5), pk.substr(10, 5), pk.substr(15, 5),
|
||||
pk.substr(20, 5));
|
||||
}
|
||||
|
||||
/**
|
||||
* std::BinaryOperation compatible accumulator for validating/stripping an input string against the PIDGEN3 charset
|
||||
* this can be moved to the PIDGEN3 at a later date
|
||||
*
|
||||
* @param accumulator
|
||||
* @param currentChar
|
||||
* @return
|
||||
*/
|
||||
std::string CLI::validateInputKeyCharset(std::string &accumulator, char currentChar)
|
||||
{
|
||||
char cchar = ::toupper(currentChar);
|
||||
if (std::find(std::begin(PIDGEN3::pKeyCharset), std::end(PIDGEN3::pKeyCharset), cchar) !=
|
||||
std::end(PIDGEN3::pKeyCharset))
|
||||
{
|
||||
accumulator.push_back(cchar);
|
||||
}
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param in_key
|
||||
* @param out_key
|
||||
* @return
|
||||
*/
|
||||
BOOL CLI::stripKey(const std::string &in_key, std::string &out_key)
|
||||
{
|
||||
// copy out the product key stripping out extraneous characters
|
||||
out_key = std::accumulate(in_key.begin(), in_key.end(), std::string(), validateInputKeyCharset);
|
||||
|
||||
// only return true if we've handled exactly PK_LENGTH chars
|
||||
return (out_key.length() == PK_LENGTH);
|
||||
}
|
||||
|
93
src/cli.h
93
src/cli.h
@ -23,12 +23,14 @@
|
||||
#ifndef UMSKT_CLI_H
|
||||
#define UMSKT_CLI_H
|
||||
|
||||
#include "libumskt/libumskt.h"
|
||||
#include "typedefs.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/color.h>
|
||||
#include <fmt/core.h>
|
||||
@ -67,10 +69,18 @@ template <> struct fmt::formatter<json> : ostream_formatter
|
||||
#define UMSKTCLI_VERSION_STRING "unknown version-dirty"
|
||||
#endif
|
||||
|
||||
class CLI
|
||||
enum APPLICATION_STATE
|
||||
{
|
||||
std::string pKey;
|
||||
DWORD32 count, total, iBinkID;
|
||||
STATE_PIDGEN_GENERATE,
|
||||
STATE_PIDGEN_VALIDATE,
|
||||
STATE_CONFIRMATION_ID
|
||||
};
|
||||
|
||||
enum PIDGEN_VERSION
|
||||
{
|
||||
PIDGEN_2 = 2,
|
||||
PIDGEN_3 = 3,
|
||||
};
|
||||
|
||||
struct Options
|
||||
{
|
||||
@ -86,34 +96,41 @@ class CLI
|
||||
std::string productCode;
|
||||
std::string productFlavour;
|
||||
|
||||
Integer channelID;
|
||||
Integer serial;
|
||||
DWORD32 numKeys;
|
||||
DWORD channelID;
|
||||
DWORD serial;
|
||||
DWORD numKeys;
|
||||
|
||||
BOOL oem;
|
||||
BOOL upgrade;
|
||||
BOOL serialSet;
|
||||
BOOL verbose;
|
||||
BOOL help;
|
||||
BOOL error;
|
||||
BOOL list;
|
||||
|
||||
struct Meta
|
||||
{
|
||||
std::string type;
|
||||
std::vector<std::string> tags;
|
||||
struct Activation
|
||||
{
|
||||
std::string flavour;
|
||||
int version;
|
||||
};
|
||||
};
|
||||
|
||||
PIDGEN3::KeyInfo info;
|
||||
|
||||
enum PIDGEN_VERSION
|
||||
{
|
||||
PIDGEN_2 = 2,
|
||||
PIDGEN_3 = 3,
|
||||
};
|
||||
PIDGEN_VERSION pidgenversion;
|
||||
|
||||
enum APPLICATION_STATE
|
||||
{
|
||||
STATE_PIDGEN_GENERATE,
|
||||
STATE_PIDGEN_VALIDATE,
|
||||
STATE_CONFIRMATION_ID
|
||||
};
|
||||
APPLICATION_STATE state;
|
||||
} static options;
|
||||
};
|
||||
|
||||
class CLI
|
||||
{
|
||||
std::string pKey;
|
||||
DWORD count, total, iBinkID;
|
||||
|
||||
static Options options;
|
||||
|
||||
public:
|
||||
CLI()
|
||||
@ -148,24 +165,42 @@ class CLI
|
||||
static CLIHandlerFunc SetValidateOption;
|
||||
static CLIHandlerFunc SetProductCodeOption;
|
||||
static CLIHandlerFunc SetFlavourOption;
|
||||
static CLIHandlerFunc SetAuthDataOption;
|
||||
|
||||
static BOOL parseCommandLine();
|
||||
static BOOL processOptions();
|
||||
static BOOL processListCommand();
|
||||
static void printID(DWORD *pid);
|
||||
static void printKey(std::string &pk);
|
||||
static BOOL stripKey(const std::string &in_key, std::string &out_key);
|
||||
static std::string validateInputKeyCharset(std::string &accumulator, char currentChar);
|
||||
|
||||
BOOL InitPIDGEN3(PIDGEN3 *p3);
|
||||
BOOL InitPIDGEN3(PIDGEN3 &pidgen3);
|
||||
BOOL InitConfirmationID(ConfirmationID &confid);
|
||||
|
||||
BOOL PIDGenerate();
|
||||
BOOL PIDValidate();
|
||||
BOOL PIDGENGenerate();
|
||||
BOOL PIDGENValidate();
|
||||
|
||||
BOOL PIDGEN2Generate(PIDGEN2 &p2);
|
||||
BOOL PIDGEN2Validate(PIDGEN2 &p2);
|
||||
BOOL PIDGEN3Generate(PIDGEN3 *p3);
|
||||
BOOL PIDGEN3Validate(PIDGEN3 *p3);
|
||||
BOOL PIDGEN2Generate(PIDGEN2 &pidgen2);
|
||||
BOOL PIDGEN2Validate(PIDGEN2 &pidgen2);
|
||||
BOOL BINK1998Generate(PIDGEN3 &pidgen3);
|
||||
BOOL BINK1998Validate(PIDGEN3 &pidgen3);
|
||||
BOOL BINK2002Generate(PIDGEN3 &pidgen3);
|
||||
BOOL BINK2002Validate(PIDGEN3 &pidgen3);
|
||||
BOOL ConfirmationIDGenerate();
|
||||
|
||||
INLINE static std::string strtolower(std::string &in)
|
||||
{
|
||||
auto retval = std::string(in);
|
||||
std::transform(retval.begin(), retval.end(), retval.begin(), ::tolower);
|
||||
return retval;
|
||||
}
|
||||
|
||||
INLINE static std::string strtoupper(std::string &in)
|
||||
{
|
||||
auto retval = std::string(in);
|
||||
std::transform(retval.begin(), retval.end(), retval.begin(), ::toupper);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int Run();
|
||||
};
|
||||
|
||||
|
173
src/generate.cpp
173
src/generate.cpp
@ -27,29 +27,9 @@
|
||||
* @param pidgen2
|
||||
* @return success
|
||||
*/
|
||||
BOOL CLI::PIDGEN2Generate(PIDGEN2 &p2)
|
||||
BOOL CLI::PIDGEN2Generate(PIDGEN2 &pidgen2)
|
||||
{
|
||||
p2.info.ChannelID = options.channelID;
|
||||
p2.info.Serial = options.serial;
|
||||
p2.info.isOEM = options.oem;
|
||||
|
||||
std::string serial;
|
||||
p2.Generate(serial);
|
||||
|
||||
serial = p2.StringifyKey(serial);
|
||||
|
||||
fmt::print("{}", serial);
|
||||
|
||||
auto retval = p2.Validate(serial);
|
||||
|
||||
if (!retval)
|
||||
{
|
||||
fmt::print(" [INVALID]");
|
||||
}
|
||||
|
||||
fmt::print("\n");
|
||||
|
||||
return retval;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -57,7 +37,7 @@ BOOL CLI::PIDGEN2Generate(PIDGEN2 &p2)
|
||||
* @param pidgen2
|
||||
* @return success
|
||||
*/
|
||||
BOOL CLI::PIDGEN2Validate(PIDGEN2 &p2)
|
||||
BOOL CLI::PIDGEN2Validate(PIDGEN2 &pidgen2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -66,14 +46,13 @@ BOOL CLI::PIDGEN2Validate(PIDGEN2 &p2)
|
||||
*
|
||||
* @return success
|
||||
*/
|
||||
BOOL CLI::PIDGEN3Generate(PIDGEN3 *p3)
|
||||
BOOL CLI::BINK1998Generate(PIDGEN3 &pidgen3)
|
||||
{
|
||||
// raw PID/serial value
|
||||
Integer serialRnd;
|
||||
DWORD nRaw = options.channelID * 1'000'000;
|
||||
DWORD serialRnd;
|
||||
|
||||
if (p3->checkFieldIsBink1998())
|
||||
{
|
||||
if (options.serial.NotZero())
|
||||
if (options.serialSet)
|
||||
{
|
||||
// using user-provided serial
|
||||
serialRnd = options.serial;
|
||||
@ -81,54 +60,28 @@ BOOL CLI::PIDGEN3Generate(PIDGEN3 *p3)
|
||||
else
|
||||
{
|
||||
// generate a random number to use as a serial
|
||||
serialRnd.Randomize(UMSKT::rng, sizeof(DWORD32) * 8);
|
||||
serialRnd = UMSKT::getRandom<DWORD>();
|
||||
}
|
||||
|
||||
// make sure it's less than 999999
|
||||
serialRnd %= 999999;
|
||||
}
|
||||
|
||||
p3->info.isOEM = options.oem;
|
||||
|
||||
for (DWORD32 i = 0; i < total; i++)
|
||||
{
|
||||
if (!p3->checkFieldIsBink1998())
|
||||
{
|
||||
if (options.authInfo.empty())
|
||||
{
|
||||
p3->info.AuthInfo.Randomize(UMSKT::rng, 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
p3->info.AuthInfo = CryptoPP::Crop(UMSKT::IntegerS(options.authInfo), 10);
|
||||
}
|
||||
nRaw += (serialRnd % 999999);
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("> AuthInfo: {:d}\n", p3->info.AuthInfo);
|
||||
// print the resulting Product ID
|
||||
// PID value is printed in BINK1998::Generate
|
||||
printID(&nRaw);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
for (int i = 0; i < total; i++)
|
||||
{
|
||||
p3->info.Serial = serialRnd;
|
||||
}
|
||||
pidgen3.info.setSerial(nRaw);
|
||||
pidgen3.Generate(pKey);
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("\n");
|
||||
}
|
||||
|
||||
p3->Generate(pKey);
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("> Product ID: {}\n\n", p3->StringifyProductID());
|
||||
}
|
||||
|
||||
bool isValid = p3->Validate(pKey);
|
||||
bool isValid = pidgen3.Validate(pKey);
|
||||
if (isValid)
|
||||
{
|
||||
fmt::print(p3->StringifyKey(pKey));
|
||||
printKey(pKey);
|
||||
if (i <= total - 1 || options.verbose)
|
||||
{
|
||||
fmt::print("\n");
|
||||
@ -139,7 +92,8 @@ BOOL CLI::PIDGEN3Generate(PIDGEN3 *p3)
|
||||
{
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("{} [Invalid]", p3->StringifyKey(pKey));
|
||||
printKey(pKey);
|
||||
fmt::print(" [Invalid]");
|
||||
if (i <= total - 1)
|
||||
{
|
||||
fmt::print("\n");
|
||||
@ -161,19 +115,96 @@ BOOL CLI::PIDGEN3Generate(PIDGEN3 *p3)
|
||||
*
|
||||
* @return success
|
||||
*/
|
||||
BOOL CLI::PIDGEN3Validate(PIDGEN3 *p3)
|
||||
BOOL CLI::BINK1998Validate(PIDGEN3 &bink1998)
|
||||
{
|
||||
std::string product_key;
|
||||
|
||||
if (!PIDGEN3::ValidateKeyString(options.keyToCheck, product_key))
|
||||
if (!CLI::stripKey(options.keyToCheck, product_key))
|
||||
{
|
||||
fmt::print("ERROR: Product key is in an incorrect format!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
fmt::print("{}\n", p3->StringifyKey(product_key));
|
||||
CLI::printKey(product_key);
|
||||
fmt::print("\n");
|
||||
if (!bink1998.Validate(product_key))
|
||||
{
|
||||
fmt::print("ERROR: Product key is invalid! Wrong BINK ID?\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!p3->Validate(product_key))
|
||||
fmt::print("Key validated successfully!\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return success
|
||||
*/
|
||||
BOOL CLI::BINK2002Generate(PIDGEN3 &pidgen3)
|
||||
{
|
||||
// generate a key
|
||||
for (int i = 0; i < total; i++)
|
||||
{
|
||||
pidgen3.info.AuthInfo = UMSKT::getRandom<DWORD>() & BITMASK(10);
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("> AuthInfo: {:#08x}\n", pidgen3.info.AuthInfo);
|
||||
}
|
||||
|
||||
pidgen3.Generate(pKey);
|
||||
|
||||
bool isValid = pidgen3.Validate(pKey);
|
||||
if (isValid)
|
||||
{
|
||||
CLI::printKey(pKey);
|
||||
if (i <= total - 1 || options.verbose)
|
||||
{ // check if end of list or verbose
|
||||
fmt::print("\n");
|
||||
}
|
||||
count += isValid; // add to count
|
||||
}
|
||||
else
|
||||
{
|
||||
if (options.verbose)
|
||||
{
|
||||
CLI::printKey(pKey); // print the key
|
||||
fmt::print(" [Invalid]"); // and add " [Invalid]" to the key
|
||||
if (i <= total - 1)
|
||||
{ // check if end of list
|
||||
fmt::print("\n");
|
||||
}
|
||||
}
|
||||
total++; // queue a redo, basically
|
||||
}
|
||||
}
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("\nSuccess count: {}/{}\n", count, total);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return success
|
||||
*/
|
||||
BOOL CLI::BINK2002Validate(PIDGEN3 &pidgen3)
|
||||
{
|
||||
std::string product_key;
|
||||
|
||||
if (!CLI::stripKey(options.keyToCheck, product_key))
|
||||
{
|
||||
fmt::print("ERROR: Product key is in an incorrect format!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
CLI::printKey(product_key);
|
||||
fmt::print("\n");
|
||||
if (!pidgen3.Validate(product_key))
|
||||
{
|
||||
fmt::print("ERROR: Product key is invalid! Wrong BINK ID?\n");
|
||||
return false;
|
||||
@ -197,7 +228,7 @@ BOOL CLI::ConfirmationIDGenerate()
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD32 err = confid.Generate(options.installationID, confirmation_id, options.productID);
|
||||
DWORD err = confid.Generate(options.installationID, confirmation_id, options.productID);
|
||||
|
||||
if (err == SUCCESS)
|
||||
{
|
||||
|
470
src/help.cpp
470
src/help.cpp
@ -76,7 +76,7 @@ void CLI::SetHelpText()
|
||||
|
||||
helpOptions[OPTION_AUTHDATA] = {
|
||||
"a", "authdata", "(advanced, PIDGEN 3 [BINK 2002] only) specify a value for the authentication data field",
|
||||
true, "", &SetAuthDataOption};
|
||||
true, "", nullptr};
|
||||
|
||||
helpOptions[OPTION_VALIDATE] = {
|
||||
"V", "validate", "validate a specified product ID against known BINKs and algorithms",
|
||||
@ -89,7 +89,7 @@ void CLI::SetHelpText()
|
||||
*/
|
||||
BOOL CLI::parseCommandLine()
|
||||
{
|
||||
for (DWORD32 i = 1; i < options.argc; i++)
|
||||
for (DWORD i = 1; i < options.argc; i++)
|
||||
{
|
||||
std::string arg = options.argv[i];
|
||||
|
||||
@ -132,7 +132,7 @@ BOOL CLI::parseCommandLine()
|
||||
continue;
|
||||
}
|
||||
|
||||
auto success = thisOption.handler(nextarg);
|
||||
auto success = thisOption.handler(1, &nextarg[0]);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
@ -155,13 +155,9 @@ BOOL CLI::parseCommandLine()
|
||||
}
|
||||
|
||||
CommandLineParseEnd:
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("\n");
|
||||
}
|
||||
if (options.error)
|
||||
{
|
||||
DisplayErrorMessage("");
|
||||
DisplayErrorMessage(0, nullptr);
|
||||
}
|
||||
return !options.error;
|
||||
}
|
||||
@ -179,180 +175,6 @@ BOOL CLI::processOptions()
|
||||
}
|
||||
|
||||
if (options.list)
|
||||
{
|
||||
return processListCommand();
|
||||
}
|
||||
|
||||
if (options.productCode.empty())
|
||||
{
|
||||
fmt::print("ERROR: product code is required. Exiting...\n");
|
||||
DisplayHelp("");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!keys["products"].contains(options.productCode))
|
||||
{
|
||||
fmt::print("ERROR: Product \"{}\" is unknown\n", options.productCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto product = keys["products"][options.productCode];
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Selecting product: {}\n", options.productCode);
|
||||
}
|
||||
|
||||
json flavour;
|
||||
if (product.contains("flavours"))
|
||||
{
|
||||
// no default flavour, no flavour specified
|
||||
if (!product["meta"].contains("default") && options.productFlavour.empty())
|
||||
{
|
||||
fmt::print("ERROR: Product \"{}\n does not have a default flavour. Please specify a flavour.",
|
||||
options.productCode);
|
||||
return false;
|
||||
}
|
||||
// yes flavour specified, but not found
|
||||
else if (!product["flavours"].contains(options.productFlavour) && !options.productFlavour.empty())
|
||||
{
|
||||
fmt::print("ERROR: Product \"{}\" does not have a flavour named \"{}\"\n", options.productCode,
|
||||
options.productFlavour);
|
||||
return false;
|
||||
}
|
||||
// yes default flavour, no flavour specified
|
||||
else if (product["meta"].contains("default") && options.productFlavour.empty())
|
||||
{
|
||||
flavour = product["flavours"][product["meta"]["default"]];
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Selecting default flavour: {}\n", product["meta"]["default"]);
|
||||
}
|
||||
}
|
||||
// yes flavour specified, and is found
|
||||
else
|
||||
{
|
||||
flavour = product["flavours"][options.productFlavour];
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Selecting flavour: {}\n", options.productFlavour);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// no variants, just go with what we have
|
||||
flavour = product;
|
||||
}
|
||||
|
||||
if (options.state != Options::STATE_PIDGEN_GENERATE && options.state != Options::STATE_PIDGEN_VALIDATE)
|
||||
{
|
||||
// exit early if we're not doing PIDGEN
|
||||
goto processOptionsExitEarly;
|
||||
}
|
||||
|
||||
if (flavour["meta"]["type"] == "PIDGEN3")
|
||||
{
|
||||
options.pidgenversion = Options::PIDGEN_VERSION::PIDGEN_3;
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting PIDGEN type to \"PIDGEN3\"\n");
|
||||
}
|
||||
|
||||
if (options.oem)
|
||||
{
|
||||
flavour["BINK"][1].get_to(options.binkID);
|
||||
}
|
||||
else
|
||||
{
|
||||
flavour["BINK"][0].get_to(options.binkID);
|
||||
}
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Selected BINK: {}\n", options.binkID);
|
||||
}
|
||||
}
|
||||
else if (flavour["meta"]["type"] == "PIDGEN2")
|
||||
{
|
||||
options.pidgenversion = Options::PIDGEN_VERSION::PIDGEN_2;
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting PIDGEN type to \"PIDGEN2\"\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (options.state != Options::STATE_PIDGEN_GENERATE)
|
||||
{
|
||||
// exit early if we're only validating
|
||||
goto processOptionsExitEarly;
|
||||
}
|
||||
|
||||
if (flavour.contains("DPC") && flavour["DPC"].contains(options.binkID) && options.channelID == 0)
|
||||
{
|
||||
std::vector<json> filtered;
|
||||
for (auto const &i : flavour["DPC"][options.binkID].items())
|
||||
{
|
||||
auto el = i.value();
|
||||
if (!el["isEvaluation"].get<bool>())
|
||||
{
|
||||
filtered.emplace_back(el);
|
||||
}
|
||||
}
|
||||
|
||||
// roll a die to choose which DPC entry to pick
|
||||
auto rand = UMSKT::getRandom<BYTE>();
|
||||
auto dpc = filtered[rand % filtered.size()];
|
||||
auto min = dpc["min"].get<WORD>(), max = dpc["max"].get<WORD>();
|
||||
|
||||
if (min == max)
|
||||
{
|
||||
options.channelID = min;
|
||||
}
|
||||
else
|
||||
{
|
||||
options.channelID = min + (rand % (max - min));
|
||||
}
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Selected channel ID: {} (DPC entry {}/{})\n", options.channelID, (rand % filtered.size()) + 1,
|
||||
filtered.size());
|
||||
}
|
||||
}
|
||||
|
||||
if (options.channelID.IsZero())
|
||||
{
|
||||
options.channelID.Randomize(UMSKT::rng, sizeof(DWORD32) * 8);
|
||||
options.channelID %= PIDGEN::MaxChannelID;
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Generated channel ID: {}\n", options.channelID);
|
||||
}
|
||||
}
|
||||
|
||||
// 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")
|
||||
{
|
||||
fmt::print("ERROR: Terminal Services BINKs (FE and FF) are unsupported at this time\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
processOptionsExitEarly:
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the contents of the input JSON file in an
|
||||
* intuitive and attractive pattern
|
||||
*
|
||||
* @return false
|
||||
*/
|
||||
BOOL CLI::processListCommand()
|
||||
{
|
||||
// the following code is absolutely unhinged
|
||||
// I'm so sorry
|
||||
@ -386,7 +208,7 @@ BOOL CLI::processListCommand()
|
||||
fmt::print("(default: {} {})\n", fmt::styled(el["meta"]["default"], fmt::emphasis::bold),
|
||||
el["flavours"][el["meta"]["default"]]["BINK"]);
|
||||
}
|
||||
else if (el["meta"]["type"] == "PIDGEN3")
|
||||
else if (el["meta"]["type"].get<std::string>() == "PIDGEN3")
|
||||
{
|
||||
fmt::print("[{}]\n", el["meta"]["type"]);
|
||||
}
|
||||
@ -394,7 +216,6 @@ BOOL CLI::processListCommand()
|
||||
{
|
||||
fmt::print("{}\n", fmt::styled("(no default)", fmt::emphasis::bold));
|
||||
}
|
||||
|
||||
if (containsFlavours)
|
||||
{
|
||||
auto flavours = el["flavours"];
|
||||
@ -402,10 +223,8 @@ BOOL CLI::processListCommand()
|
||||
{
|
||||
auto el2 = j.value();
|
||||
BOOL isLast = j == --flavours.end();
|
||||
|
||||
fmt::print("{}{}{} {:<9} {} ", !isLast ? leaf : last, line, line, fmt::format("{}:", j.key()),
|
||||
fmt::format("{} {}", el["name"], el2["name"]));
|
||||
|
||||
if (el2.contains("meta") && el2["meta"].contains("type"))
|
||||
{
|
||||
fmt::print("[{}]\n", el2["meta"]["type"]);
|
||||
@ -416,18 +235,135 @@ BOOL CLI::processListCommand()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt::print("\n");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (options.productCode.empty())
|
||||
{
|
||||
fmt::print("ERROR: product code is required. Exiting...");
|
||||
DisplayHelp(0, nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *productCode = &options.productCode[0];
|
||||
if (!keys["products"].contains(productCode))
|
||||
{
|
||||
fmt::print("ERROR: Product {} is unknown", productCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto product = keys["products"][productCode];
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Selecting product: {}\n", productCode);
|
||||
}
|
||||
|
||||
json flavour;
|
||||
if (product.contains("flavours"))
|
||||
{
|
||||
flavour = product["flavours"][options.productFlavour];
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Selecting flavour: {}\n", options.productFlavour);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
flavour = product;
|
||||
}
|
||||
|
||||
if (options.state != STATE_PIDGEN_GENERATE && options.state != STATE_PIDGEN_VALIDATE)
|
||||
{
|
||||
// exit early if we're not doing PIDGEN
|
||||
goto processOptionsExitEarly;
|
||||
}
|
||||
|
||||
if (options.oem)
|
||||
{
|
||||
flavour["BINK"][1].get_to(options.binkID);
|
||||
}
|
||||
else
|
||||
{
|
||||
flavour["BINK"][0].get_to(options.binkID);
|
||||
}
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Selected BINK: {}\n", options.binkID);
|
||||
}
|
||||
|
||||
if (options.state != STATE_PIDGEN_GENERATE)
|
||||
{
|
||||
// exit early if we're only validating
|
||||
goto processOptionsExitEarly;
|
||||
}
|
||||
|
||||
if (flavour.contains("DPC") && flavour["DPC"].contains(options.binkID) && options.channelID == 0)
|
||||
{
|
||||
std::vector<json> filtered;
|
||||
for (auto const &i : flavour["DPC"][options.binkID].items())
|
||||
{
|
||||
auto el = i.value();
|
||||
if (!el["isEvaluation"].get<bool>())
|
||||
{
|
||||
filtered.emplace_back(el);
|
||||
}
|
||||
}
|
||||
|
||||
// roll a die to choose which DPC entry to pick
|
||||
auto rand = UMSKT::getRandom<BYTE>();
|
||||
auto dpc = filtered[rand % filtered.size()];
|
||||
auto min = dpc["min"].get<WORD>(), max = dpc["max"].get<WORD>();
|
||||
|
||||
if (min == max)
|
||||
{
|
||||
options.channelID = min;
|
||||
}
|
||||
else
|
||||
{
|
||||
options.channelID = min + (rand % (max - min));
|
||||
}
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Selected channel ID: {} (DPC entry {}/{})\n", options.channelID, rand % filtered.size(),
|
||||
filtered.size());
|
||||
}
|
||||
}
|
||||
|
||||
if (options.channelID == 0)
|
||||
{
|
||||
options.channelID = UMSKT::getRandom<WORD>() % 999;
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Generated channel ID: {}\n", options.channelID);
|
||||
}
|
||||
}
|
||||
|
||||
// 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")
|
||||
{
|
||||
fmt::print("ERROR: Terminal Services BINKs (FE and FF) are unsupported at this time\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
processOptionsExitEarly:
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return success
|
||||
*/
|
||||
BOOL CLI::DisplayHelp(const std::string &)
|
||||
BOOL CLI::DisplayHelp(int, char *)
|
||||
{
|
||||
options.help = true;
|
||||
fmt::print("usage: {} \n", options.argv[0]);
|
||||
@ -435,7 +371,6 @@ BOOL CLI::DisplayHelp(const std::string &)
|
||||
for (BYTE i = 0; i < CLIHelpOptionID_END; i++)
|
||||
{
|
||||
CLIHelpOptions o = helpOptions[i];
|
||||
|
||||
if (o.Short.empty())
|
||||
{
|
||||
fmt::print("\t{:>2} --{:<15} {}", "", o.Long, o.HelpText);
|
||||
@ -461,31 +396,33 @@ BOOL CLI::DisplayHelp(const std::string &)
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::DisplayErrorMessage(const std::string &)
|
||||
BOOL CLI::DisplayErrorMessage(int, char *)
|
||||
{
|
||||
fmt::print("Error parsing command line options\n");
|
||||
DisplayHelp("");
|
||||
DisplayHelp(0, nullptr);
|
||||
options.error = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
BOOL CLI::SetVerboseOption(const std::string &)
|
||||
BOOL CLI::SetVerboseOption(int, char *)
|
||||
{
|
||||
fmt::print("Enabling verbose option\n\n");
|
||||
options.verbose = true;
|
||||
UMSKT::setVerboseOutput(stdout);
|
||||
fmt::print(UMSKT::verbose, "Enabling verbose option\n");
|
||||
UMSKT::VERBOSE = true;
|
||||
UMSKT::setDebugOutput(stderr);
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetDebugOption(const std::string &)
|
||||
BOOL CLI::SetDebugOption(int, char *)
|
||||
{
|
||||
fmt::print("Enabling debug option\n");
|
||||
options.verbose = true;
|
||||
UMSKT::setDebugOutput(stdout);
|
||||
fmt::print(UMSKT::debug, "Enabling debug option\n");
|
||||
UMSKT::DEBUG = true;
|
||||
UMSKT::setDebugOutput(stderr);
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetListOption(const std::string &)
|
||||
BOOL CLI::SetListOption(int, char *)
|
||||
{
|
||||
if (options.verbose)
|
||||
{
|
||||
@ -495,17 +432,17 @@ BOOL CLI::SetListOption(const std::string &)
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetOEMOption(const std::string &)
|
||||
BOOL CLI::SetOEMOption(int, char *)
|
||||
{
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting OEM option\n");
|
||||
fmt::print("Setting oem option\n");
|
||||
}
|
||||
options.oem = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetUpgradeOption(const std::string &)
|
||||
BOOL CLI::SetUpgradeOption(int, char *)
|
||||
{
|
||||
if (options.verbose)
|
||||
{
|
||||
@ -515,7 +452,7 @@ BOOL CLI::SetUpgradeOption(const std::string &)
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetFileOption(const std::string &file)
|
||||
BOOL CLI::SetFileOption(int count, char *file)
|
||||
{
|
||||
if (options.verbose)
|
||||
{
|
||||
@ -525,25 +462,39 @@ BOOL CLI::SetFileOption(const std::string &file)
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetNumberOption(const std::string &num)
|
||||
BOOL CLI::SetNumberOption(int count, char *num)
|
||||
{
|
||||
auto nKeys = UMSKT::IntegerS(num);
|
||||
int nKeys;
|
||||
if (!_sscanf(num, "%d", &nKeys))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting generation number option to: {}\n", num);
|
||||
}
|
||||
|
||||
options.numKeys = nKeys.ConvertToLong();
|
||||
options.numKeys = nKeys;
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetChannelIDOption(const std::string &channum)
|
||||
/**
|
||||
*
|
||||
* @param count
|
||||
* @param channum
|
||||
* @return
|
||||
*/
|
||||
BOOL CLI::SetChannelIDOption(int count, char *channum)
|
||||
{
|
||||
Integer channelID = UMSKT::IntegerS(channum);
|
||||
int siteID;
|
||||
if (!_sscanf(channum, "%d", &siteID))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// channel ids must be between 000 and 999
|
||||
if (channelID > PIDGEN::MaxChannelID)
|
||||
if (siteID > 999)
|
||||
{
|
||||
fmt::print("ERROR: refusing to create a key with a Channel ID greater than 999\n");
|
||||
return false;
|
||||
@ -551,117 +502,94 @@ BOOL CLI::SetChannelIDOption(const std::string &channum)
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting Channel ID option to: {}\n", channelID);
|
||||
fmt::print("Setting channel number option to: {}\n", siteID);
|
||||
}
|
||||
|
||||
options.channelID = channelID;
|
||||
options.channelID = siteID;
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetBINKOption(const std::string &bink)
|
||||
BOOL CLI::SetBINKOption(int count, char *bink)
|
||||
{
|
||||
auto strbinkid = std::string(bink);
|
||||
options.binkID = UMSKT::strtoupper(strbinkid);
|
||||
options.binkID = strtoupper(strbinkid);
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting BINK option to: {}\n", strbinkid);
|
||||
fmt::print("Setting BINK option to {}\n", strbinkid);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetFlavourOption(const std::string &flavour)
|
||||
BOOL CLI::SetFlavourOption(int count, char *flavour)
|
||||
{
|
||||
auto strFlavour = UMSKT::strtoupper(flavour);
|
||||
options.productFlavour = strFlavour;
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting flavour option to: {}\n", strFlavour);
|
||||
fmt::print("Setting flavour option to {}\n", flavour);
|
||||
}
|
||||
|
||||
options.productFlavour = flavour;
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetSerialOption(const std::string &arg)
|
||||
/**
|
||||
*
|
||||
* @param count
|
||||
* @param arg
|
||||
* @return
|
||||
*/
|
||||
BOOL CLI::SetSerialOption(int count, char *arg)
|
||||
{
|
||||
Integer Serial = UMSKT::IntegerS(arg);
|
||||
int serial_val;
|
||||
if (!_sscanf(arg, "%d", &serial_val))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// serials must be between 000000 and 999999
|
||||
if (Serial > PIDGEN::MaxSerial)
|
||||
if (serial_val > 999999)
|
||||
{
|
||||
fmt::print("ERROR: refusing to create a key with a Serial not between 000000 and 999999\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
options.serial = Serial;
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting serial number option to: {}\n", Serial);
|
||||
}
|
||||
options.serialSet = true;
|
||||
options.serial = serial_val;
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetActivationIDOption(const std::string &aid)
|
||||
BOOL CLI::SetActivationIDOption(int count, char *aid)
|
||||
{
|
||||
options.installationID = aid;
|
||||
options.state = Options::STATE_CONFIRMATION_ID;
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting program state to Confirmation ID Generation\n");
|
||||
}
|
||||
|
||||
options.state = STATE_CONFIRMATION_ID;
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetProductIDOption(const std::string &product)
|
||||
BOOL CLI::SetProductIDOption(int count, char *product)
|
||||
{
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting product ID to {}", product);
|
||||
}
|
||||
options.productID = product;
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting product ID option to: {}\n", product);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetValidateOption(const std::string &productID)
|
||||
BOOL CLI::SetValidateOption(int count, char *productID)
|
||||
{
|
||||
options.keyToCheck = productID;
|
||||
options.state = Options::STATE_PIDGEN_VALIDATE;
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting program state to PIDGEN Validation\n");
|
||||
}
|
||||
|
||||
options.state = STATE_PIDGEN_VALIDATE;
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetProductCodeOption(const std::string &product)
|
||||
BOOL CLI::SetProductCodeOption(int, char *product)
|
||||
{
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting product code to {}\n", product);
|
||||
}
|
||||
|
||||
auto strProduct = std::string(product);
|
||||
options.productCode = UMSKT::strtoupper(strProduct);
|
||||
options.productFlavour = "";
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting product code to: {}\n", strProduct);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetAuthDataOption(const std::string &authData)
|
||||
{
|
||||
auto strAuthData = std::string(authData);
|
||||
options.authInfo = strAuthData;
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting authdata option to: {}\n", strAuthData);
|
||||
}
|
||||
|
||||
options.productCode = strtoupper(strProduct);
|
||||
return true;
|
||||
}
|
||||
|
@ -23,15 +23,8 @@
|
||||
#ifndef UMSKT_HELP_H
|
||||
#define UMSKT_HELP_H
|
||||
|
||||
typedef BOOL CLIHandlerFunc(const std::string &);
|
||||
typedef BOOL CLIHandlerFunc(int, char *);
|
||||
|
||||
/**
|
||||
* CLI Options List.
|
||||
*
|
||||
* Note: options are processed in the order found in the ENUM
|
||||
* order matters mostly for UX.
|
||||
*
|
||||
*/
|
||||
enum CLIHelpOptionIDs
|
||||
{
|
||||
OPTION_HELP,
|
||||
|
@ -38,7 +38,7 @@
|
||||
* @param x4
|
||||
* @param x5
|
||||
* @param priv
|
||||
* @param modulus
|
||||
* @param modulous
|
||||
* @param nonresidue
|
||||
* @param isOffice
|
||||
* @param isXPBrand
|
||||
@ -46,74 +46,58 @@
|
||||
* @return
|
||||
*/
|
||||
BOOL ConfirmationID::LoadHyperellipticCurve(QWORD x0, QWORD x1, QWORD x2, QWORD x3, QWORD x4, QWORD x5, Q_OWORD priv,
|
||||
QWORD modulus, QWORD nonresidue, DWORD32 iidkey, BOOL isOffice,
|
||||
BOOL isXPBrand, BYTE flagVersion)
|
||||
QWORD modulous, QWORD nonresidue, BOOL isOffice, BOOL isXPBrand,
|
||||
BYTE flagVersion)
|
||||
{
|
||||
QWORD fvals[6] = {x0, x1, x2, x3, x4, x5};
|
||||
curve[0] = x0;
|
||||
curve[1] = x1;
|
||||
curve[2] = x2;
|
||||
curve[3] = x3;
|
||||
curve[4] = x4;
|
||||
curve[5] = x5;
|
||||
|
||||
return LoadHyperellipticCurve(fvals, priv, modulus, nonresidue, iidkey, isOffice, isXPBrand, flagVersion);
|
||||
memcpy(&privateKey, &priv, sizeof(Q_OWORD));
|
||||
|
||||
MOD = modulous;
|
||||
NON_RESIDUE = nonresidue;
|
||||
this->isOffice = isOffice;
|
||||
this->isXPBrand = isXPBrand;
|
||||
this->flagVersion = flagVersion;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param f
|
||||
* @param priv
|
||||
* @param modulus
|
||||
* @param modulous
|
||||
* @param nonresidue
|
||||
* @param isOffice
|
||||
* @param isXPBrand
|
||||
* @param flagVersion
|
||||
* @return
|
||||
*/
|
||||
BOOL ConfirmationID::LoadHyperellipticCurve(QWORD *f, Q_OWORD priv, QWORD modulus, QWORD nonresidue, DWORD32 iidkey,
|
||||
BOOL isOffice, BOOL isXPBrand, BYTE flagVersion)
|
||||
BOOL ConfirmationID::LoadHyperellipticCurve(QWORD *f, Q_OWORD priv, QWORD modulous, QWORD nonresidue, BOOL isOffice,
|
||||
BOOL isXPBrand, BYTE flagVersion)
|
||||
{
|
||||
memcpy(&curve, f, sizeof(curve));
|
||||
memcpy(&privateKey, &priv, sizeof(Q_OWORD));
|
||||
|
||||
MOD = modulus;
|
||||
MOD = modulous;
|
||||
NON_RESIDUE = nonresidue;
|
||||
this->isOffice = isOffice;
|
||||
this->isXPBrand = isXPBrand;
|
||||
this->flagVersion = flagVersion;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL ConfirmationID::LoadHyperellipticCurve(const std::string &x0, const std::string &x1, const std::string &x2,
|
||||
const std::string &x3, const std::string &x4, const std::string &x5,
|
||||
const std::string &priv, const std::string &modulus,
|
||||
const std::string &nonresidue, const std::string &iidkey, BOOL isOffice,
|
||||
BOOL isXPBrand, BYTE flagVersion)
|
||||
BOOL LoadHyperellipticCurve(const std::string &x0, const std::string &x1, const std::string &x2, const std::string &x3,
|
||||
const std::string &x4, const std::string &x5, const std::string &priv,
|
||||
const std::string &modulous, const std::string &nonresidue, BOOL isOffice, BOOL isXPBrand,
|
||||
BYTE flagVersion)
|
||||
{
|
||||
std::string f[6];
|
||||
f[0] = x0;
|
||||
f[1] = x1;
|
||||
f[2] = x2;
|
||||
f[3] = x3;
|
||||
f[4] = x4;
|
||||
f[5] = x5;
|
||||
|
||||
return LoadHyperellipticCurve(f, priv, modulus, nonresidue, iidkey, isOffice, isXPBrand, flagVersion);
|
||||
}
|
||||
|
||||
BOOL ConfirmationID::LoadHyperellipticCurve(const std::string *f, const std::string &priv, const std::string &modulus,
|
||||
const std::string &nonresidue, const std::string &iidkey, BOOL isOffice,
|
||||
BOOL isXPBrand, BYTE flagVersion)
|
||||
{
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
Integer(&f[i][0]).Encode((BYTE *)curve[i], sizeof(QWORD));
|
||||
}
|
||||
|
||||
Integer(&priv[0]).Encode(privateKey.byte, sizeof(Q_OWORD));
|
||||
|
||||
Integer(&modulus[0]).Encode((BYTE *)&MOD, sizeof(QWORD));
|
||||
|
||||
Integer(&nonresidue[0]).Encode((BYTE *)NON_RESIDUE, sizeof(QWORD));
|
||||
|
||||
this->isOffice = isOffice;
|
||||
this->isXPBrand = isXPBrand;
|
||||
this->flagVersion = flagVersion;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -123,9 +107,9 @@ BOOL ConfirmationID::LoadHyperellipticCurve(const std::string *f, const std::str
|
||||
* @param pid
|
||||
* @return
|
||||
*/
|
||||
DWORD32 ConfirmationID::calculateCheckDigit(DWORD32 pid)
|
||||
DWORD ConfirmationID::calculateCheckDigit(DWORD pid)
|
||||
{
|
||||
DWORD32 i = 0, j = 0, k = 0;
|
||||
DWORD i = 0, j = 0, k = 0;
|
||||
for (j = pid; j; i += k)
|
||||
{
|
||||
k = j % 10;
|
||||
@ -141,7 +125,7 @@ DWORD32 ConfirmationID::calculateCheckDigit(DWORD32 pid)
|
||||
* @param hwid
|
||||
* @param version
|
||||
*/
|
||||
void ConfirmationID::decode_iid_new_version(BYTE *iid, BYTE *hwid, DWORD32 *version)
|
||||
void ConfirmationID::decode_iid_new_version(BYTE *iid, BYTE *hwid, DWORD *version)
|
||||
{
|
||||
QWORD buffer[5];
|
||||
for (BYTE i = 0; i < 5; i++)
|
||||
@ -149,8 +133,8 @@ void ConfirmationID::decode_iid_new_version(BYTE *iid, BYTE *hwid, DWORD32 *vers
|
||||
memcpy(&buffer[i], (iid + (4 * i)), 4);
|
||||
}
|
||||
|
||||
DWORD32 v1 = (buffer[3] & 0xFFFFFFF8) | 2;
|
||||
DWORD32 v2 = ((buffer[3] & 7) << 29) | (buffer[2] >> 3);
|
||||
DWORD v1 = (buffer[3] & 0xFFFFFFF8) | 2;
|
||||
DWORD v2 = ((buffer[3] & 7) << 29) | (buffer[2] >> 3);
|
||||
QWORD hardwareIDVal = ((QWORD)v1 << 32) | v2;
|
||||
for (BYTE i = 0; i < 8; ++i)
|
||||
{
|
||||
@ -169,9 +153,8 @@ void ConfirmationID::decode_iid_new_version(BYTE *iid, BYTE *hwid, DWORD32 *vers
|
||||
*/
|
||||
void ConfirmationID::Mix(BYTE *buffer, BYTE bufSize, const BYTE *key, BYTE keySize)
|
||||
{
|
||||
BYTE sha1_input[64], sha1_result[SHA1::DIGESTSIZE];
|
||||
BYTE sha1_input[64], sha1_result[20];
|
||||
BYTE half = bufSize / 2;
|
||||
auto digest = SHA1();
|
||||
|
||||
// assert(half <= sizeof(sha1_result) && half + keySize <= sizeof(sha1_input) - 9);
|
||||
for (BYTE external_counter = 0; external_counter < 4; external_counter++)
|
||||
@ -198,8 +181,7 @@ void ConfirmationID::Mix(BYTE *buffer, BYTE bufSize, const BYTE *key, BYTE keySi
|
||||
sha1_input[sizeof(sha1_input) - 2] = (1 + half + keySize) * 8 / 0x100;
|
||||
}
|
||||
|
||||
digest.Update(sha1_input, sizeof(sha1_input));
|
||||
digest.Final(sha1_result);
|
||||
SHA1(sha1_input, sizeof(sha1_input), sha1_result);
|
||||
|
||||
for (BYTE i = half & ~3; i < half; i++)
|
||||
{
|
||||
@ -224,9 +206,8 @@ void ConfirmationID::Mix(BYTE *buffer, BYTE bufSize, const BYTE *key, BYTE keySi
|
||||
*/
|
||||
void ConfirmationID::Unmix(BYTE *buffer, BYTE bufSize, const BYTE key[4], BYTE keySize)
|
||||
{
|
||||
BYTE sha1_input[64], sha1_result[SHA1::DIGESTSIZE];
|
||||
BYTE sha1_input[64], sha1_result[20];
|
||||
BYTE half = bufSize / 2;
|
||||
auto digest = SHA1();
|
||||
// assert(half <= sizeof(sha1_result) && half + keySize <= sizeof(sha1_input) - 9);
|
||||
|
||||
for (BYTE external_counter = 0; external_counter < 4; external_counter++)
|
||||
@ -251,8 +232,7 @@ void ConfirmationID::Unmix(BYTE *buffer, BYTE bufSize, const BYTE key[4], BYTE k
|
||||
sha1_input[sizeof(sha1_input) - 2] = (1 + half + keySize) * 8 / 0x100;
|
||||
}
|
||||
|
||||
digest.Update(sha1_input, sizeof(sha1_input));
|
||||
digest.Final(sha1_result);
|
||||
SHA1(sha1_input, sizeof(sha1_input), sha1_result);
|
||||
|
||||
for (BYTE i = half & ~3; i < half; i++)
|
||||
{
|
||||
@ -278,13 +258,13 @@ void ConfirmationID::Unmix(BYTE *buffer, BYTE bufSize, const BYTE key[4], BYTE k
|
||||
CONFIRMATION_ID_STATUS ConfirmationID::Generate(const std::string &installationIDIn, std::string &confirmationIDOut,
|
||||
std::string &productIDIn)
|
||||
{
|
||||
DWORD32 version;
|
||||
DWORD version;
|
||||
BYTE hardwareID[8];
|
||||
BYTE installation_id[19]; // 10**45 < 256**19
|
||||
BYTE productID[4];
|
||||
|
||||
BYTE installation_id_len = 0;
|
||||
auto pid = &installationIDIn[0];
|
||||
auto pid = installationIDIn.c_str();
|
||||
|
||||
BYTE count = 0, totalCount = 0;
|
||||
unsigned check = 0;
|
||||
@ -461,8 +441,8 @@ CONFIRMATION_ID_STATUS ConfirmationID::Generate(const std::string &installationI
|
||||
QWORD x1 = ulowhi.qword[0] - x2 * MOD;
|
||||
x2++;
|
||||
|
||||
d.u.qword[0] = residue->sub(residue->mul(x1, x1), residue->mul(NON_RESIDUE, residue->mul(x2, x2)));
|
||||
d.u.qword[1] = residue->add(x1, x1);
|
||||
d.u[0] = residue->sub(residue->mul(x1, x1), residue->mul(NON_RESIDUE, residue->mul(x2, x2)));
|
||||
d.u[1] = residue->add(x1, x1);
|
||||
if (divisor->find_divisor_v(&d))
|
||||
{
|
||||
break;
|
||||
@ -482,23 +462,23 @@ CONFIRMATION_ID_STATUS ConfirmationID::Generate(const std::string &installationI
|
||||
Q_OWORD e;
|
||||
memset(&e, 0, sizeof(e));
|
||||
|
||||
if (d.u.qword[0] == BAD)
|
||||
if (d.u[0] == BAD)
|
||||
{
|
||||
// we can not get the zero divisor, actually...
|
||||
e.qword[0] = residue->__umul128(MOD + 2, MOD, &e.qword[1]);
|
||||
}
|
||||
else if (d.u.qword[1] == BAD)
|
||||
else if (d.u[1] == BAD)
|
||||
{
|
||||
// O(1/MOD) chance
|
||||
// encoded = (unsigned __int128)(MOD + 1) * d.u[0] + MOD; // * MOD + d.u[0] is fine too
|
||||
e.qword[0] = residue->__umul128(MOD + 1, d.u.qword[0], &e.qword[1]);
|
||||
e.qword[0] = residue->__umul128(MOD + 1, d.u[0], &e.qword[1]);
|
||||
e.qword[0] += MOD;
|
||||
e.qword[1] += (e.qword[0] < MOD);
|
||||
}
|
||||
else
|
||||
{
|
||||
QWORD x1 = (d.u.qword[1] % 2 ? d.u.qword[1] + MOD : d.u.qword[1]) / 2;
|
||||
QWORD x2sqr = residue->sub(residue->mul(x1, x1), d.u.qword[0]);
|
||||
QWORD x1 = (d.u[1] % 2 ? d.u[1] + MOD : d.u[1]) / 2;
|
||||
QWORD x2sqr = residue->sub(residue->mul(x1, x1), d.u[0]);
|
||||
QWORD x2 = residue->sqrt(x2sqr);
|
||||
|
||||
if (x2 == BAD)
|
||||
@ -513,9 +493,9 @@ CONFIRMATION_ID_STATUS ConfirmationID::Generate(const std::string &installationI
|
||||
{
|
||||
// points (-x1+x2, v(-x1+x2)) and (-x1-x2, v(-x1-x2))
|
||||
QWORD x1a = residue->sub(x1, x2);
|
||||
QWORD y1 = residue->sub(d.v.qword[0], residue->mul(d.v.qword[1], x1a));
|
||||
QWORD y1 = residue->sub(d.v[0], residue->mul(d.v[1], x1a));
|
||||
QWORD x2a = residue->add(x1, x2);
|
||||
QWORD y2 = residue->sub(d.v.qword[0], residue->mul(d.v.qword[1], x2a));
|
||||
QWORD y2 = residue->sub(d.v[0], residue->mul(d.v[1], x2a));
|
||||
if (x1a > x2a)
|
||||
{
|
||||
QWORD tmp = x1a;
|
||||
@ -554,7 +534,7 @@ CONFIRMATION_ID_STATUS ConfirmationID::Generate(const std::string &installationI
|
||||
decimal[34 - i] = c4;
|
||||
}
|
||||
|
||||
assert(e.byte[0] == 0 && e.byte[1] == 0 && e.byte[2] == 0 && e.byte[3] == 0);
|
||||
assert(e.encoded[0] == 0 && e.encoded[1] == 0 && e.encoded[2] == 0 && e.encoded[3] == 0);
|
||||
|
||||
char *q = &confirmationIDOut[0];
|
||||
|
||||
|
@ -41,7 +41,8 @@ enum CONFIRMATION_ID_STATUS
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Q_OWORD u, v;
|
||||
QWORD u[2];
|
||||
QWORD v[2];
|
||||
} TDivisor;
|
||||
|
||||
class EXPORT ConfirmationID
|
||||
@ -54,8 +55,8 @@ class EXPORT ConfirmationID
|
||||
BOOL isOffice = false, isXPBrand = false;
|
||||
unsigned flagVersion = 0;
|
||||
|
||||
DWORD32 calculateCheckDigit(DWORD32 pid);
|
||||
void decode_iid_new_version(BYTE *iid, BYTE *hwid, DWORD32 *version);
|
||||
DWORD calculateCheckDigit(DWORD pid);
|
||||
void decode_iid_new_version(BYTE *iid, BYTE *hwid, DWORD *version);
|
||||
void Mix(BYTE *buffer, BYTE bufSize, const BYTE *key, BYTE keySize);
|
||||
void Unmix(BYTE *buffer, BYTE bufSize, const BYTE *key, BYTE keySize);
|
||||
|
||||
@ -126,31 +127,26 @@ class EXPORT ConfirmationID
|
||||
residue = new Residue(this);
|
||||
polynomial = new Polynomial(this);
|
||||
divisor = new Divisor(this);
|
||||
privateKey.qword[0] = privateKey.qword[1] = 0x00;
|
||||
}
|
||||
|
||||
BOOL LoadHyperellipticCurve(const std::string *f, const std::string &priv, const std::string &modulus,
|
||||
const std::string &nonresidue, const std::string &iidkey, BOOL isOffice, BOOL isXPBrand,
|
||||
BYTE flagVersion);
|
||||
|
||||
BOOL LoadHyperellipticCurve(const std::string &x0, const std::string &x1, const std::string &x2,
|
||||
const std::string &x3, const std::string &x4, const std::string &x5,
|
||||
const std::string &priv, const std::string &modulus, const std::string &nonresidue,
|
||||
const std::string &iidkey, BOOL isOffice, BOOL isXPBrand, BYTE flagVersion);
|
||||
const std::string &priv, const std::string &modulous, const std::string &nonresidue,
|
||||
BOOL isOffice, BOOL isXPBrand, BYTE flagVersion);
|
||||
|
||||
BOOL LoadHyperellipticCurve(QWORD x0, QWORD x1, QWORD x2, QWORD x3, QWORD x4, QWORD x5, Q_OWORD priv, QWORD modulus,
|
||||
QWORD nonresidue, DWORD32 iidkey, BOOL isOffice, BOOL isXPBrand, BYTE flagVersion);
|
||||
BOOL LoadHyperellipticCurve(QWORD x0, QWORD x1, QWORD x2, QWORD x3, QWORD x4, QWORD x5, Q_OWORD priv,
|
||||
QWORD modulous, QWORD nonresidue, BOOL isOffice, BOOL isXPBrand, BYTE flagVersion);
|
||||
|
||||
BOOL LoadHyperellipticCurve(QWORD *f, Q_OWORD priv, QWORD modulus, QWORD nonresidue, DWORD32 iidkey, BOOL isOffice,
|
||||
BOOL isXPBrand, BYTE flagVersion);
|
||||
BOOL LoadHyperellipticCurve(QWORD *f, Q_OWORD priv, QWORD modulous, QWORD nonresidue, BOOL isOffice, BOOL isXPBrand,
|
||||
BYTE flagVersion);
|
||||
|
||||
CONFIRMATION_ID_STATUS Generate(const std::string &installation_id_str, std::string &confirmation_id,
|
||||
std::string &productid);
|
||||
|
||||
~ConfirmationID()
|
||||
{
|
||||
delete residue;
|
||||
delete polynomial;
|
||||
delete divisor;
|
||||
delete residue, polynomial, divisor;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -39,7 +39,7 @@ int ConfirmationID::ConfirmationID::Divisor::find_divisor_v(TDivisor *d)
|
||||
f2[i] = parent->curve[i];
|
||||
}
|
||||
|
||||
const QWORD u0 = d->u.qword[0], u1 = d->u.qword[1];
|
||||
const QWORD u0 = d->u[0], u1 = d->u[1];
|
||||
for (BYTE j = 4; j--;)
|
||||
{
|
||||
f2[j] = parent->residue->sub(f2[j], parent->residue->mul(u0, f2[j + 2]));
|
||||
@ -106,8 +106,8 @@ int ConfirmationID::ConfirmationID::Divisor::find_divisor_v(TDivisor *d)
|
||||
|
||||
QWORD v0 = parent->residue->mul(parent->residue->add(f1, parent->residue->mul(u1, parent->residue->mul(v1, v1))),
|
||||
parent->residue->inv(parent->residue->add(v1, v1)));
|
||||
d->v.qword[0] = v0;
|
||||
d->v.qword[1] = v1;
|
||||
d->v[0] = v0;
|
||||
d->v[1] = v1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -121,24 +121,24 @@ int ConfirmationID::ConfirmationID::Divisor::find_divisor_v(TDivisor *d)
|
||||
*/
|
||||
int ConfirmationID::ConfirmationID::Divisor::u2poly(const TDivisor *src, QWORD polyu[3], QWORD polyv[2])
|
||||
{
|
||||
if (src->u.qword[1] != BAD)
|
||||
if (src->u[1] != BAD)
|
||||
{
|
||||
polyu[0] = src->u.qword[0];
|
||||
polyu[1] = src->u.qword[1];
|
||||
polyu[0] = src->u[0];
|
||||
polyu[1] = src->u[1];
|
||||
polyu[2] = 1;
|
||||
|
||||
polyv[0] = src->v.qword[0];
|
||||
polyv[1] = src->v.qword[1];
|
||||
polyv[0] = src->v[0];
|
||||
polyv[1] = src->v[1];
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (src->u.qword[0] != BAD)
|
||||
if (src->u[0] != BAD)
|
||||
{
|
||||
polyu[0] = src->u.qword[0];
|
||||
polyu[0] = src->u[0];
|
||||
polyu[1] = 1;
|
||||
|
||||
polyv[0] = src->v.qword[0];
|
||||
polyv[0] = src->v[0];
|
||||
polyv[1] = 0;
|
||||
|
||||
return 1;
|
||||
@ -237,7 +237,7 @@ void ConfirmationID::Divisor::add(const TDivisor *src1, const TDivisor *src2, TD
|
||||
}
|
||||
}
|
||||
|
||||
vdeg = parent->polynomial->div_monic(vdeg, v, udeg, u, nullptr);
|
||||
vdeg = parent->polynomial->div_monic(vdeg, v, udeg, u, NULL);
|
||||
|
||||
while (udeg > 2)
|
||||
{
|
||||
@ -279,30 +279,30 @@ void ConfirmationID::Divisor::add(const TDivisor *src1, const TDivisor *src2, TD
|
||||
v[i] = parent->residue->sub(0, v[i]);
|
||||
}
|
||||
|
||||
vdeg = parent->polynomial->div_monic(vdeg, v, udeg, u, nullptr);
|
||||
vdeg = parent->polynomial->div_monic(vdeg, v, udeg, u, NULL);
|
||||
}
|
||||
|
||||
if (udeg == 2)
|
||||
{
|
||||
dst->u.qword[0] = u[0];
|
||||
dst->u.qword[1] = u[1];
|
||||
dst->v.qword[0] = (vdeg >= 0 ? v[0] : 0);
|
||||
dst->v.qword[1] = (vdeg >= 1 ? v[1] : 0);
|
||||
dst->u[0] = u[0];
|
||||
dst->u[1] = u[1];
|
||||
dst->v[0] = (vdeg >= 0 ? v[0] : 0);
|
||||
dst->v[1] = (vdeg >= 1 ? v[1] : 0);
|
||||
}
|
||||
else if (udeg == 1)
|
||||
{
|
||||
dst->u.qword[0] = u[0];
|
||||
dst->u.qword[1] = BAD;
|
||||
dst->v.qword[0] = (vdeg >= 0 ? v[0] : 0);
|
||||
dst->v.qword[1] = BAD;
|
||||
dst->u[0] = u[0];
|
||||
dst->u[1] = BAD;
|
||||
dst->v[0] = (vdeg >= 0 ? v[0] : 0);
|
||||
dst->v[1] = BAD;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(udeg == 0);
|
||||
dst->u.qword[0] = BAD;
|
||||
dst->u.qword[1] = BAD;
|
||||
dst->v.qword[0] = BAD;
|
||||
dst->v.qword[1] = BAD;
|
||||
dst->u[0] = BAD;
|
||||
dst->u[1] = BAD;
|
||||
dst->v[0] = BAD;
|
||||
dst->v[1] = BAD;
|
||||
}
|
||||
}
|
||||
|
||||
@ -318,10 +318,10 @@ void ConfirmationID::Divisor::mul(const TDivisor *src, QWORD mult, TDivisor *dst
|
||||
{
|
||||
if (mult == 0)
|
||||
{
|
||||
dst->u.qword[0] = BAD;
|
||||
dst->u.qword[1] = BAD;
|
||||
dst->v.qword[0] = BAD;
|
||||
dst->v.qword[1] = BAD;
|
||||
dst->u[0] = BAD;
|
||||
dst->u[1] = BAD;
|
||||
dst->v[0] = BAD;
|
||||
dst->v[1] = BAD;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -354,10 +354,10 @@ void ConfirmationID::Divisor::mul128(const TDivisor *src, QWORD mult_lo, QWORD m
|
||||
{
|
||||
if (mult_lo == 0 && mult_hi == 0)
|
||||
{
|
||||
dst->u.qword[0] = BAD;
|
||||
dst->u.qword[1] = BAD;
|
||||
dst->v.qword[0] = BAD;
|
||||
dst->v.qword[1] = BAD;
|
||||
dst->u[0] = BAD;
|
||||
dst->u[1] = BAD;
|
||||
dst->v[0] = BAD;
|
||||
dst->v[1] = BAD;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -22,26 +22,20 @@
|
||||
|
||||
#include "libumskt.h"
|
||||
|
||||
std::FILE *UMSKT::debug;
|
||||
std::FILE *UMSKT::verbose;
|
||||
|
||||
BOOL UMSKT::IS_CONSTRUCTED = UMSKT::CONSTRUCT();
|
||||
|
||||
/**
|
||||
* a static "constructor" that does some housekeeping for certain
|
||||
* platforms, in DJGPP for instance we need to setup the interval
|
||||
* timer for RNG.
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
BOOL UMSKT::CONSTRUCT()
|
||||
#ifdef _WIN32
|
||||
// this seems janky but it works, and doesn't use storage that would otherwise get clobbered
|
||||
std::FILE *getFileStreamToNul()
|
||||
{
|
||||
#ifdef __DJGPP__
|
||||
// this should be set up as early as possible
|
||||
auto now = uclock();
|
||||
#endif
|
||||
return true;
|
||||
fopen_s(&UMSKT::debug, "nul", "w");
|
||||
return UMSKT::debug;
|
||||
}
|
||||
std::FILE *UMSKT::debug = getFileStreamToNul();
|
||||
#else
|
||||
std::FILE *UMSKT::debug = std::fopen("/dev/null", "w");
|
||||
#endif
|
||||
|
||||
BOOL UMSKT::VERBOSE = false;
|
||||
BOOL UMSKT::DEBUG = false;
|
||||
|
||||
/**
|
||||
* sets the filestream used for debugging
|
||||
@ -52,13 +46,3 @@ void UMSKT::setDebugOutput(std::FILE *input)
|
||||
{
|
||||
debug = input;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the filestream used for verbose messages
|
||||
*
|
||||
* @param input std::FILE
|
||||
*/
|
||||
void UMSKT::setVerboseOutput(std::FILE *input)
|
||||
{
|
||||
verbose = input;
|
||||
}
|
@ -28,10 +28,10 @@
|
||||
#include "pidgen3/PIDGEN3.h"
|
||||
|
||||
std::map<UMSKT_TAG, UMSKT_Value> UMSKT::tags;
|
||||
CryptoPP::DefaultAutoSeededRNG UMSKT::rng;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
/**
|
||||
* Sets debug output to a given C++ File stream
|
||||
* if the memory allocated at filestream is "STDOUT" or "STDERR"
|
||||
@ -148,7 +148,7 @@ extern "C"
|
||||
|
||||
// ---------------------------------------------
|
||||
|
||||
EXPORT void *PIDGEN3_INIT(const char *binkid, const char *p, const char *a, const char *b, const char *generatorX,
|
||||
EXPORT void *PIDGEN3_INIT(const char *p, const char *a, const char *b, const char *generatorX,
|
||||
const char *generatorY, const char *publicKeyX, const char *publicKeyY,
|
||||
const char *genOrder, const char *privateKey)
|
||||
{
|
||||
@ -163,7 +163,7 @@ extern "C"
|
||||
p3 = new BINK2002();
|
||||
}
|
||||
|
||||
p3->LoadEllipticCurve(binkid, p, a, b, generatorX, generatorY, publicKeyX, publicKeyY, genOrder, privateKey);
|
||||
p3->LoadEllipticCurve(p, a, b, generatorX, generatorY, publicKeyX, publicKeyY, genOrder, privateKey);
|
||||
|
||||
return p3;
|
||||
}
|
||||
@ -195,7 +195,7 @@ extern "C"
|
||||
std::string str;
|
||||
BOOL retval = p3->Generate(str);
|
||||
|
||||
assert(pKeySizeIn >= str.length() + NULL_TERMINATOR);
|
||||
assert(pKeySizeIn >= str.length() + 1);
|
||||
|
||||
memcpy(pKeyOut, &str[0], str.length());
|
||||
pKeyOut[str.length()] = 0;
|
||||
@ -245,3 +245,48 @@ extern "C"
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
/**
|
||||
* Convert data between endianness types.
|
||||
*
|
||||
* @param data [in]
|
||||
* @param length [in]
|
||||
**/
|
||||
void UMSKT::endian(BYTE *data, int length)
|
||||
{
|
||||
for (int i = 0; i < length / 2; i++)
|
||||
{
|
||||
BYTE temp = data[i];
|
||||
data[i] = data[length - i - 1];
|
||||
data[length - i - 1] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an OpenSSL BigNumber to it's Little Endian binary equivalent
|
||||
*
|
||||
* @param a [in] BigNumber to convert
|
||||
* @param to [out] char* binary representation
|
||||
* @param tolen [in] length of the char* array
|
||||
*
|
||||
* @return length of number in to
|
||||
**/
|
||||
int UMSKT::BN_bn2lebin(const BIGNUM *a, unsigned char *to, int tolen)
|
||||
{
|
||||
if (a == nullptr || to == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int len = BN_bn2bin(a, to);
|
||||
|
||||
if (len > tolen)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Choke point inside BN_bn2lebinpad: OpenSSL uses len instead of tolen.
|
||||
endian(to, tolen);
|
||||
|
||||
return len;
|
||||
}
|
@ -25,90 +25,38 @@
|
||||
|
||||
#include "../typedefs.h"
|
||||
|
||||
#ifdef __DJGPP__
|
||||
#include <time.h>
|
||||
#endif
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <cryptopp/cryptlib.h>
|
||||
#include <cryptopp/ecp.h>
|
||||
#include <cryptopp/integer.h>
|
||||
#include <cryptopp/misc.h>
|
||||
#include <cryptopp/nbtheory.h>
|
||||
#include <cryptopp/osrng.h>
|
||||
#include <cryptopp/sha.h>
|
||||
|
||||
using ECP = CryptoPP::ECP;
|
||||
using SHA1 = CryptoPP::SHA1;
|
||||
using Integer = CryptoPP::Integer;
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
// fmt <-> CryptoPP linkage
|
||||
template <> class fmt::formatter<Integer>
|
||||
{
|
||||
char type_ = 'd';
|
||||
|
||||
public:
|
||||
constexpr auto parse(format_parse_context &ctx)
|
||||
{
|
||||
auto i = ctx.begin(), end = ctx.end();
|
||||
|
||||
if (i != end)
|
||||
{
|
||||
switch (*i)
|
||||
{
|
||||
case 'B':
|
||||
case 'b':
|
||||
case 'o':
|
||||
case 'X':
|
||||
case 'x':
|
||||
case 'd':
|
||||
type_ = *i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (i != end && *i != '}')
|
||||
{
|
||||
throw format_error("invalid format");
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
template <typename FmtContext> constexpr auto format(const Integer &i, FmtContext &ctx) const
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
case 'B':
|
||||
case 'b':
|
||||
return format_to(ctx.out(), "{}", IntToString(i, 2));
|
||||
|
||||
case 'o':
|
||||
return format_to(ctx.out(), "{}", IntToString(i, 8));
|
||||
|
||||
case 'X':
|
||||
case 'x':
|
||||
return format_to(ctx.out(), "{}", IntToString(i, 16));
|
||||
|
||||
case 'd':
|
||||
default:
|
||||
return format_to(ctx.out(), "{}", IntToString(i, 10));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Algorithm macros
|
||||
#define PK_LENGTH 25
|
||||
#define NULL_TERMINATOR 1
|
||||
|
||||
#define FIELD_BITS 384
|
||||
#define FIELD_BYTES 48
|
||||
#define FIELD_BITS_2003 512
|
||||
#define FIELD_BYTES_2003 64
|
||||
|
||||
#define SHA_MSG_LENGTH_XP (4 + 2 * FIELD_BYTES)
|
||||
#define SHA_MSG_LENGTH_2003 (3 + 2 * FIELD_BYTES_2003)
|
||||
|
||||
#define NEXTSNBITS(field, n, offset) (((QWORD)(field) >> (offset)) & ((1ULL << (n)) - 1))
|
||||
#define FIRSTNBITS(field, n) NEXTSNBITS((field), (n), 0)
|
||||
|
||||
#define HIBYTES(field, bytes) NEXTSNBITS((QWORD)(field), ((bytes) * 8), ((bytes) * 8))
|
||||
#define LOBYTES(field, bytes) FIRSTNBITS((QWORD)(field), ((bytes) * 8))
|
||||
|
||||
#define BYDWORD(n) (DWORD32)(*((n) + 0) | *((n) + 1) << 8 | *((n) + 2) << 16 | *((n) + 3) << 24)
|
||||
#define BYDWORD(n) (DWORD)(*((n) + 0) | *((n) + 1) << 8 | *((n) + 2) << 16 | *((n) + 3) << 24)
|
||||
#define BITMASK(n) ((1ULL << (n)) - 1)
|
||||
|
||||
#ifndef LIBUMSKT_VERSION_STRING
|
||||
@ -131,7 +79,7 @@ struct UMSKT_Value
|
||||
union {
|
||||
BOOL boolean;
|
||||
WORD word;
|
||||
DWORD32 dword;
|
||||
DWORD dword;
|
||||
QWORD qword;
|
||||
OWORD oword;
|
||||
char *chars;
|
||||
@ -155,141 +103,37 @@ enum UMSKT_TAG
|
||||
class EXPORT UMSKT
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Convert a std::string to an Integer
|
||||
*
|
||||
* @param in
|
||||
* @return
|
||||
*/
|
||||
INLINE static Integer IntegerS(const std::string &in)
|
||||
{
|
||||
return Integer(&in[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a std::string to an Integer
|
||||
*
|
||||
* @param in
|
||||
* @return
|
||||
*/
|
||||
INLINE static Integer IntegerHexS(const std::string &in)
|
||||
{
|
||||
return IntegerS("0x" + in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Native byte buffer to Integer
|
||||
*
|
||||
* @param buf
|
||||
* @param size
|
||||
* @return
|
||||
*/
|
||||
INLINE static Integer IntegerN(BYTE *buf, size_t size)
|
||||
{
|
||||
return {buf, size, Integer::UNSIGNED, CryptoPP::LITTLE_ENDIAN_ORDER};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Native Type T to Integer, where T is a concrete type
|
||||
*
|
||||
* @tparam T
|
||||
* @param in
|
||||
* @return
|
||||
*/
|
||||
template <typename T> INLINE static Integer IntegerN(const T &in)
|
||||
{
|
||||
return IntegerN((BYTE *)&in, sizeof(T));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode Integer to a Native byte buffer
|
||||
*
|
||||
* @param in
|
||||
* @param buf
|
||||
* @param buflen
|
||||
* @return
|
||||
*/
|
||||
INLINE static BYTE *EncodeN(const Integer &in, BYTE *buf, size_t buflen)
|
||||
{
|
||||
in.Encode(buf, buflen);
|
||||
std::reverse(buf, buf + buflen);
|
||||
return buf + buflen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode Integer to Native type T where T is a concrete type
|
||||
*
|
||||
* @tparam T
|
||||
* @param in
|
||||
* @param buf
|
||||
* @return
|
||||
*/
|
||||
template <typename T> INLINE static BYTE *EncodeN(const Integer &in, T &buf)
|
||||
{
|
||||
return EncodeN(in, (BYTE *)&buf, sizeof(T));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a random number into a Native concrete type
|
||||
*
|
||||
* @tparam T
|
||||
* @return
|
||||
*/
|
||||
template <typename T> INLINE static T getRandom()
|
||||
{
|
||||
T retval;
|
||||
rng.GenerateBlock((BYTE *)&retval, sizeof(retval));
|
||||
return retval;
|
||||
}
|
||||
|
||||
INLINE static std::string strtolower(std::string &in)
|
||||
{
|
||||
auto retval = std::string(in);
|
||||
std::transform(retval.begin(), retval.end(), retval.begin(), ::tolower);
|
||||
return retval;
|
||||
}
|
||||
|
||||
INLINE static std::string strtoupper(const std::string &in)
|
||||
{
|
||||
auto retval = std::string(in);
|
||||
std::transform(retval.begin(), retval.end(), retval.begin(), ::toupper);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the compiled-in version information
|
||||
*
|
||||
* @return Null-Terminated C-Style string pointer
|
||||
*/
|
||||
INLINE static const std::string VERSION()
|
||||
{
|
||||
return fmt::format("LIBUMSKT {} compiled on {} {}", LIBUMSKT_VERSION_STRING, __DATE__, __TIME__);
|
||||
}
|
||||
|
||||
static std::FILE *debug;
|
||||
static std::FILE *verbose;
|
||||
static BOOL IS_CONSTRUCTED;
|
||||
static BOOL VERBOSE;
|
||||
static BOOL DEBUG;
|
||||
static std::map<UMSKT_TAG, UMSKT_Value> tags;
|
||||
static CryptoPP::DefaultAutoSeededRNG rng;
|
||||
|
||||
static BOOL CONSTRUCT();
|
||||
// Hello OpenSSL developers, please tell me, where is this function at?
|
||||
static int BN_bn2lebin(const BIGNUM *a, unsigned char *to, int tolen);
|
||||
static void endian(BYTE *data, int length);
|
||||
|
||||
static void DESTRUCT()
|
||||
{
|
||||
if (debug != nullptr && debug != stdout && debug != stderr)
|
||||
if (debug != nullptr)
|
||||
{
|
||||
std::fclose(debug);
|
||||
}
|
||||
debug = nullptr;
|
||||
}
|
||||
if (verbose != nullptr && verbose != stdout && debug != stderr)
|
||||
{
|
||||
std::fclose(verbose);
|
||||
verbose = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void setDebugOutput(std::FILE *input);
|
||||
static void setVerboseOutput(std::FILE *input);
|
||||
|
||||
template <typename T> static T getRandom()
|
||||
{
|
||||
T retval;
|
||||
RAND_bytes((BYTE *)&retval, sizeof(retval));
|
||||
return retval;
|
||||
}
|
||||
|
||||
static const char *VERSION()
|
||||
{
|
||||
return fmt::format("LIBUMSKT {} compiled on {} {}", LIBUMSKT_VERSION_STRING, __DATE__, __TIME__).c_str();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // UMSKT_LIBUMSKT_H
|
||||
|
@ -1,76 +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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @FileCreated by Neo on 02/13/2024
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#include "pidgen.h"
|
||||
|
||||
/**
|
||||
* The number 7 in an Integer for optimization
|
||||
*/
|
||||
const Integer PIDGEN::SEVEN = Integer(7);
|
||||
|
||||
/**
|
||||
* The number 10 in an Integer for optimization
|
||||
*/
|
||||
const Integer PIDGEN::TEN = Integer(10);
|
||||
|
||||
/**
|
||||
* The maximum Channel ID size (PID 2.0/3.0) in an Integer for optimization
|
||||
* 000 - 999
|
||||
*/
|
||||
const Integer PIDGEN::MaxChannelID = Integer(1'000);
|
||||
|
||||
/**
|
||||
* The maximum serial size (PID 2.0/3.0) in an Integer for optimization
|
||||
* 000000 - 999999
|
||||
*/
|
||||
const Integer PIDGEN::MaxSerial = Integer(1'000'000);
|
||||
|
||||
/**
|
||||
* Generates a Mod7 check digit for a given Integer
|
||||
*
|
||||
* @param in Integer to generate
|
||||
* @return Mod7 check digit
|
||||
*/
|
||||
Integer PIDGEN::GenerateMod7(const Integer &in)
|
||||
{
|
||||
Integer Sum = 0, CheckNum = in;
|
||||
|
||||
while (CheckNum.NotZero())
|
||||
{
|
||||
Sum += CheckNum % TEN;
|
||||
CheckNum /= TEN;
|
||||
}
|
||||
|
||||
return SEVEN - (Sum % SEVEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the last digit (one's place) of a given Integer
|
||||
* is the expected check digit.
|
||||
*
|
||||
* @param in Integer to validate
|
||||
* @return validity
|
||||
*/
|
||||
BOOL PIDGEN::isValidMod7(const Integer &in)
|
||||
{
|
||||
return GenerateMod7(in / TEN) == (in % TEN);
|
||||
}
|
@ -1,51 +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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @FileCreated by Neo on 02/13/2024
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#include "libumskt.h"
|
||||
|
||||
#ifndef UMSKT_PIDGEN_H
|
||||
#define UMSKT_PIDGEN_H
|
||||
|
||||
/**
|
||||
* PIDGEN Interface
|
||||
*
|
||||
* Defines three entry points:
|
||||
* Generate, Validate, StringifyKey
|
||||
*/
|
||||
class PIDGEN : public UMSKT
|
||||
{
|
||||
public:
|
||||
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;
|
||||
|
||||
Integer GenerateMod7(const Integer &in);
|
||||
BOOL isValidMod7(const Integer &in);
|
||||
};
|
||||
|
||||
#endif // UMSKT_PIDGEN_H
|
@ -22,304 +22,187 @@
|
||||
|
||||
#include "PIDGEN2.h"
|
||||
|
||||
const std::vector<std::string> PIDGEN2::channelIDDisallowList = {"333", "444", "555", "666", "777", "888", "999"};
|
||||
const std::vector<std::string> PIDGEN2::validYears = {"95", "96", "97", "98", "99", "00", "01", "02"};
|
||||
|
||||
/**
|
||||
* Generates a PID 2.0 key, output is placed in pKey
|
||||
*
|
||||
* @param pKey
|
||||
* @return true
|
||||
* @param input
|
||||
* @return
|
||||
*/
|
||||
BOOL PIDGEN2::Generate(std::string &pKey)
|
||||
BOOL PIDGEN2::isNumericString(char *input)
|
||||
{
|
||||
Integer random;
|
||||
random.Randomize(rng, sizeof(DWORD32) * 8);
|
||||
|
||||
info.ChannelID = random % MaxChannelID;
|
||||
if (!isValidChannelID())
|
||||
for (int i = 0; i < strlen(input); i++)
|
||||
{
|
||||
info.ChannelID++;
|
||||
if (info.ChannelID <= Integer::Zero())
|
||||
if (input[i] < '0' || input[i] > '9')
|
||||
{
|
||||
info.ChannelID = Integer::One();
|
||||
return false;
|
||||
}
|
||||
else if (info.ChannelID >= 999)
|
||||
{
|
||||
info.ChannelID = 998;
|
||||
}
|
||||
}
|
||||
|
||||
random.Randomize(rng, sizeof(DWORD32) * 8);
|
||||
info.Serial = random % MaxSerial;
|
||||
|
||||
if (info.isOEM)
|
||||
{
|
||||
info.Day = (random % Integer(365)) + Integer::One();
|
||||
info.Year = IntegerS(validYears[random % validYears.size()]);
|
||||
|
||||
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);
|
||||
|
||||
if (debug)
|
||||
{
|
||||
fmt::print("\n{:03d}{:02d}-OEM-{:07d}-{:05d}\n", day, year, oemid, serial);
|
||||
}
|
||||
|
||||
pKey = fmt::format("{:03d}{:02d}{:07d}{:05d}", day, year, oemid, serial);
|
||||
}
|
||||
else if (info.isOffice)
|
||||
{
|
||||
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);
|
||||
|
||||
if (debug)
|
||||
{
|
||||
fmt::print("\n{:04d}-{:07d}\n", channelid, serial);
|
||||
}
|
||||
|
||||
pKey = fmt::format("{:04d}{:07d}", channelid, serial);
|
||||
}
|
||||
else
|
||||
{
|
||||
info.Serial = (info.Serial * TEN) + GenerateMod7(info.Serial);
|
||||
|
||||
fmt::print("{}\n", info.Serial);
|
||||
|
||||
DWORD32 channelid, serial;
|
||||
EncodeN(info.ChannelID, channelid);
|
||||
EncodeN(info.Serial, serial);
|
||||
|
||||
if (debug)
|
||||
{
|
||||
fmt::print("\n{:03d}-{:07d}\n", channelid, serial);
|
||||
}
|
||||
|
||||
pKey = fmt::format("{:03d}{:07d}", channelid, serial);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Valid serial types are:
|
||||
*
|
||||
* C = Channel/Site ID (001 - 998)
|
||||
* E = Office Channel ID (+1) Check Digit
|
||||
* N = Serial
|
||||
* K = Mod7 Check Digit
|
||||
*
|
||||
* -- OEM Specific
|
||||
* D = 3 Digit day (001 - 366)
|
||||
* Y = 2 Digit year
|
||||
* O = OEM ID - typically seen as a channel ID + the first digit of the serial + mod7 check digit
|
||||
*
|
||||
* note that the N segment for OEM serials do not have a Mod7 check
|
||||
*
|
||||
* CCC-NNNNNNK
|
||||
* CCCE-NNNNNNK
|
||||
* DDDYY-ZZOOONK-NNNNN
|
||||
* DDDYY-OEM-ZZOOONK-NNNNN
|
||||
*
|
||||
* we can determine what type of key we have
|
||||
* simply by counting the numeric characters
|
||||
*
|
||||
* @param pKey
|
||||
* @param input
|
||||
* @return
|
||||
*/
|
||||
BOOL PIDGEN2::Validate(const std::string &pKey)
|
||||
int PIDGEN2::addDigits(char *input)
|
||||
{
|
||||
std::string filtered;
|
||||
std::copy_if(pKey.begin(), pKey.end(), std::back_inserter(filtered), [](char c) { return std::isdigit(c); });
|
||||
int output = 0;
|
||||
|
||||
bool bIsValidChannelID, bIsValidSerial, bIsValidOEMDay, bIsValidOEMYear, bIsValidOEMID;
|
||||
|
||||
switch (filtered.length())
|
||||
if (!isNumericString(input))
|
||||
{
|
||||
case KeySize::FPP:
|
||||
// standard FPP/CCP has 10 digits
|
||||
info.ChannelID = IntegerS(filtered.substr(0, 3));
|
||||
info.Serial = IntegerS(filtered.substr(3, 7));
|
||||
|
||||
bIsValidChannelID = isValidChannelID();
|
||||
bIsValidSerial = isValidSerial();
|
||||
|
||||
if (debug)
|
||||
{
|
||||
fmt::print("\n\nisValidChannelID: {} isValidSerial: {}\n", bIsValidChannelID, bIsValidSerial);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return bIsValidChannelID && bIsValidSerial;
|
||||
|
||||
case KeySize::Office:
|
||||
// so far only office 97 has been documented using this
|
||||
info.isOffice = true;
|
||||
info.ChannelID = IntegerS(filtered.substr(0, 4));
|
||||
info.Serial = IntegerS(filtered.substr(4, 7));
|
||||
|
||||
bIsValidChannelID = isValidChannelID();
|
||||
bIsValidSerial = isValidSerial();
|
||||
|
||||
if (debug)
|
||||
for (int i = 0; i < strlen(input); i++)
|
||||
{
|
||||
fmt::print("\n\nisValidChannelID: {} isValidSerial: {}\n", bIsValidChannelID, bIsValidSerial);
|
||||
output += input[i] - '0';
|
||||
}
|
||||
|
||||
return bIsValidChannelID && bIsValidSerial;
|
||||
|
||||
case KeySize::OEM:
|
||||
// all OEM keys follow this pattern
|
||||
info.isOEM = true;
|
||||
info.Day = IntegerS(filtered.substr(0, 3));
|
||||
info.Year = IntegerS(filtered.substr(3, 2));
|
||||
info.OEMID = IntegerS(filtered.substr(5, 7)); // 6 + check digit
|
||||
info.Serial = IntegerS(filtered.substr(12, 5));
|
||||
|
||||
bIsValidOEMDay = isValidOEMDay();
|
||||
bIsValidOEMYear = isValidOEMYear();
|
||||
bIsValidOEMID = isValidOEMID();
|
||||
|
||||
if (debug)
|
||||
{
|
||||
fmt::print("\n\nisValidOEMDay: {} isValidOEMYear: {} isValidOEMID: {}\n", bIsValidOEMDay, bIsValidOEMYear,
|
||||
bIsValidOEMID);
|
||||
}
|
||||
|
||||
return bIsValidOEMDay && bIsValidOEMYear && bIsValidOEMID;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param pKey
|
||||
* @param channelID
|
||||
* @return
|
||||
*/
|
||||
std::string PIDGEN2::StringifyKey(const std::string &pKey)
|
||||
BOOL PIDGEN2::isValidChannelID(char *channelID)
|
||||
{
|
||||
switch (pKey.length())
|
||||
{
|
||||
case KeySize::FPP:
|
||||
return fmt::format("{}-{}", pKey.substr(0, 3), pKey.substr(3, 7));
|
||||
|
||||
case KeySize::Office:
|
||||
return fmt::format("{}-{}", pKey.substr(0, 4), pKey.substr(4, 7));
|
||||
|
||||
case KeySize::OEM:
|
||||
return fmt::format("{}-OEM-{}-{}", pKey.substr(0, 5), pKey.substr(5, 7), pKey.substr(12, 5));
|
||||
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
std::string PIDGEN2::StringifyProductID()
|
||||
{
|
||||
if (info.isOEM)
|
||||
{
|
||||
return fmt::format("{:d}{:d}-OEM-{:d}-{:d}", info.Year, info.Day, info.OEMID, info.Serial);
|
||||
}
|
||||
|
||||
return fmt::format("{}-{}", info.ChannelID, info.Serial);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the Serial with check digit a valid serial?
|
||||
*
|
||||
* standard Mod7 Check
|
||||
*
|
||||
* @return validity
|
||||
*/
|
||||
BOOL PIDGEN2::isValidSerial()
|
||||
{
|
||||
return isValidMod7(info.Serial);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the OEMID a valid?
|
||||
*
|
||||
* @return validity
|
||||
*/
|
||||
BOOL PIDGEN2::isValidOEMID()
|
||||
{
|
||||
if (info.OEMID.IsZero())
|
||||
if (strlen(channelID) > 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return isValidMod7(info.OEMID);
|
||||
for (int i = 0; i <= 6; i++)
|
||||
{
|
||||
if (strcmp(channelID, channelIDBlacklist[i]) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the Channel ID a valid Channel ID?
|
||||
* also validates Channel ID check digit if applicable
|
||||
*
|
||||
* Known invalid Channel IDs are:
|
||||
* 333, 444, 555, 666, 777, 888, 999
|
||||
*
|
||||
* @return validity
|
||||
* @param OEMID
|
||||
* @return
|
||||
*/
|
||||
BOOL PIDGEN2::isValidChannelID() const
|
||||
BOOL PIDGEN2::isValidOEMID(char *OEMID)
|
||||
{
|
||||
// if we're office, do the last digit +1 checksum
|
||||
if (info.isOffice)
|
||||
{
|
||||
Integer CheckDigit = (info.ChannelID % TEN), ChannelID = (info.ChannelID / TEN);
|
||||
|
||||
if (std::find(channelIDDisallowList.begin(), channelIDDisallowList.end(), IntToString(ChannelID)) !=
|
||||
channelIDDisallowList.end())
|
||||
if (!isNumericString(OEMID))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (ChannelID % TEN) + 1 == CheckDigit;
|
||||
if (strlen(OEMID) > 5)
|
||||
{
|
||||
if (OEMID[0] != '0' || OEMID[1] != '0')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise just make sure we're not in the disallow list
|
||||
return std::find(channelIDDisallowList.begin(), channelIDDisallowList.end(), IntToString(info.ChannelID)) ==
|
||||
channelIDDisallowList.end();
|
||||
int mod = addDigits(OEMID);
|
||||
|
||||
return (mod % 21 == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the OEM year in the allow list?
|
||||
*
|
||||
* Known allowed years are:
|
||||
* 95, 96, 97, 98, 99, 00, 01, 02
|
||||
*
|
||||
* @return validity
|
||||
* @param year
|
||||
* @return
|
||||
*/
|
||||
BOOL PIDGEN2::isValidOEMYear() const
|
||||
BOOL PIDGEN2::isValidYear(char *year)
|
||||
{
|
||||
auto year = fmt::format("{:02d}", info.Year.ConvertToLong());
|
||||
return std::find(validYears.begin(), validYears.end(), year) != validYears.end();
|
||||
for (int i = 0; i <= 7; i++)
|
||||
{
|
||||
if (year == validYears[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the OEM Day an allowed day?
|
||||
*
|
||||
* Allowed days are 1 - 366 inclusive
|
||||
*
|
||||
* @return validity
|
||||
* @param day
|
||||
* @return
|
||||
*/
|
||||
BOOL PIDGEN2::isValidOEMDay() const
|
||||
BOOL PIDGEN2::isValidDay(char *day)
|
||||
{
|
||||
return info.Day >= 0 && info.Day <= 366;
|
||||
if (!isNumericString(day))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int iDay = std::stoi(day);
|
||||
if (iDay == 0 || iDay >= 365)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param productID
|
||||
* @return
|
||||
*/
|
||||
BOOL PIDGEN2::isValidRetailProductID(char *productID)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param channelID
|
||||
* @param keyout
|
||||
* @return
|
||||
*/
|
||||
int PIDGEN2::GenerateRetail(char *channelID, char *&keyout)
|
||||
{
|
||||
if (!isValidChannelID(channelID))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param year
|
||||
* @param day
|
||||
* @param oem
|
||||
* @param keyout
|
||||
* @return
|
||||
*/
|
||||
int PIDGEN2::GenerateOEM(char *year, char *day, char *oem, char *&keyout)
|
||||
{
|
||||
if (!isValidOEMID(oem))
|
||||
{
|
||||
int mod = addDigits(oem);
|
||||
mod += mod % 21;
|
||||
|
||||
snprintf(oem, 8, "%07u", mod);
|
||||
}
|
||||
|
||||
if (!isValidYear(year))
|
||||
{
|
||||
_strncpy(year, 4, validYears[0], 4);
|
||||
}
|
||||
|
||||
if (!isValidDay(day))
|
||||
{
|
||||
auto iday = UMSKT::getRandom<int>();
|
||||
iday = (iday + 1) % 365;
|
||||
}
|
||||
|
||||
_strncpy(keyout, 32, &fmt::format("{}{}-OEM-{}-{}", year, day, oem, oem).c_str()[0], 32);
|
||||
|
||||
return 0;
|
||||
}
|
@ -23,37 +23,28 @@
|
||||
#ifndef UMSKT_PIDGEN2_H
|
||||
#define UMSKT_PIDGEN2_H
|
||||
|
||||
#include "../pidgen.h"
|
||||
#include "../libumskt.h"
|
||||
|
||||
class EXPORT PIDGEN2 : public PIDGEN
|
||||
class EXPORT PIDGEN2
|
||||
{
|
||||
static const std::vector<std::string> channelIDDisallowList;
|
||||
static const std::vector<std::string> validYears;
|
||||
DWORD year;
|
||||
DWORD day;
|
||||
BOOL isOEM;
|
||||
BOOL isOffice;
|
||||
|
||||
enum KeySize
|
||||
{
|
||||
FPP = 10,
|
||||
Office = 11,
|
||||
OEM = 17
|
||||
};
|
||||
static constexpr char channelIDBlacklist[7][4] = {"333", "444", "555", "666", "777", "888", "999"};
|
||||
static constexpr char validYears[8][3] = {"95", "96", "97", "98", "99", "00", "01", "02"};
|
||||
|
||||
public:
|
||||
struct KeyInfo
|
||||
{
|
||||
BOOL isOEM, isOffice;
|
||||
Integer Day, Year, OEMID, ChannelID, Serial;
|
||||
} info;
|
||||
|
||||
BOOL Generate(std::string &pKey) override;
|
||||
BOOL Validate(const std::string &pKey) override;
|
||||
std::string StringifyKey(const std::string &pKey) override;
|
||||
std::string StringifyProductID() override;
|
||||
|
||||
BOOL isValidSerial();
|
||||
BOOL isValidOEMID();
|
||||
[[nodiscard]] BOOL isValidChannelID() const;
|
||||
[[nodiscard]] BOOL isValidOEMYear() const;
|
||||
[[nodiscard]] BOOL isValidOEMDay() const;
|
||||
BOOL isNumericString(char *input);
|
||||
BOOL isValidChannelID(char *channelID);
|
||||
BOOL isValidOEMID(char *OEMID);
|
||||
BOOL isValidYear(char *year);
|
||||
BOOL isValidDay(char *day);
|
||||
BOOL isValidRetailProductID(char *productID);
|
||||
int addDigits(char *input);
|
||||
int GenerateRetail(char *channelID, char *&keyout);
|
||||
int GenerateOEM(char *year, char *day, char *oem, char *&keyout);
|
||||
};
|
||||
|
||||
#endif // UMSKT_PIDGEN2_H
|
||||
|
@ -24,7 +24,6 @@
|
||||
* and uploaded to GitHub by TheMCHK in August of 2019
|
||||
*
|
||||
* Endermanch (Andrew) rewrote the algorithm in May of 2023
|
||||
* Neo ported Endermanch's algorithm to CryptoPP in February of 2024
|
||||
* }
|
||||
*/
|
||||
|
||||
@ -33,130 +32,101 @@
|
||||
/**
|
||||
* Packs a Windows XP-like Product Key.
|
||||
*
|
||||
* @param ki
|
||||
* @return Integer representation of KeyInfo
|
||||
*/
|
||||
Integer BINK1998::Pack(const KeyInfo &ki)
|
||||
* @param pRaw [in] *QWORD[2] raw product key input
|
||||
**/
|
||||
BOOL BINK1998::Pack(QWORD *pRaw)
|
||||
{
|
||||
// The quantity of information the key provides is 114 bits.
|
||||
// We're storing it in 2 64-bit quad-words with 14 trailing bits.
|
||||
// 64 * 2 = 128
|
||||
auto serial = (ki.ChannelID * MaxSerial) + ki.Serial;
|
||||
|
||||
// Signature [114..59] <- Hash [58..31] <- Serial [30..1] <- Upgrade [0]
|
||||
Integer raw = CryptoPP::Crop(ki.Signature, 56) << 59 | CryptoPP::Crop(ki.Hash, 28) << 31 |
|
||||
CryptoPP::Crop(serial, 30) << 1 | ki.isUpgrade;
|
||||
pRaw[0] = FIRSTNBITS(info.Signature, 5) << 59 | FIRSTNBITS(info.Hash, 28) << 31 | info.Serial << 1 | info.isUpgrade;
|
||||
pRaw[1] = NEXTSNBITS(info.Signature, 51, 5);
|
||||
|
||||
if (debug)
|
||||
{
|
||||
fmt::print(debug, "pack: {:x}\n\n", raw);
|
||||
}
|
||||
|
||||
return raw;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpacks a Windows XP-like Product Key.
|
||||
*
|
||||
* @param raw Integer to unpack
|
||||
* @return populated PIDGEN3::KeyInfo struct
|
||||
*/
|
||||
BINK1998::KeyInfo BINK1998::Unpack(const Integer &raw)
|
||||
* @param pRaw [out] *QWORD[2] raw product key output
|
||||
**/
|
||||
BOOL BINK1998::Unpack(QWORD *pRaw)
|
||||
{
|
||||
KeyInfo ki;
|
||||
|
||||
// We're assuming that the quantity of information within the product key is at most 114 bits.
|
||||
// log2(24^25) = 114.
|
||||
|
||||
// Upgrade = Bit 0
|
||||
ki.isUpgrade = CryptoPP::Crop(raw, 1).ConvertToLong();
|
||||
info.isUpgrade = FIRSTNBITS(pRaw[0], 1);
|
||||
|
||||
// Serial = Bits [1..30] -> 30 bits
|
||||
auto serialPack = CryptoPP::Crop((raw >> 1), 30);
|
||||
ki.Serial = serialPack % MaxSerial;
|
||||
ki.ChannelID = ((serialPack - ki.Serial) / MaxSerial);
|
||||
info.Serial = NEXTSNBITS(pRaw[0], 30, 1);
|
||||
|
||||
// Hash = Bits [31..58] -> 28 bits
|
||||
ki.Hash = CryptoPP::Crop((raw >> 31), 28);
|
||||
info.Hash = NEXTSNBITS(pRaw[0], 28, 31);
|
||||
|
||||
// Signature = Bits [59..113] -> 56 bits
|
||||
ki.Signature = CryptoPP::Crop((raw >> 59), 56);
|
||||
info.Signature = FIRSTNBITS(pRaw[1], 51) << 5 | NEXTSNBITS(pRaw[0], 5, 59);
|
||||
|
||||
return ki;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a Windows XP-like Product Key.
|
||||
*
|
||||
* @param pKey [out]
|
||||
*
|
||||
* @return true on success, false on fail
|
||||
*/
|
||||
BOOL BINK1998::Generate(std::string &pKey)
|
||||
{
|
||||
Integer c, s, pRaw;
|
||||
SHA1 sha1;
|
||||
BN_CTX *numContext = BN_CTX_new();
|
||||
|
||||
// copy initial state from object
|
||||
auto ki = info;
|
||||
BIGNUM *c = BN_CTX_get(numContext), *s = BN_CTX_get(numContext), *x = BN_CTX_get(numContext),
|
||||
*y = BN_CTX_get(numContext);
|
||||
|
||||
QWORD pRaw[2];
|
||||
|
||||
// Data segment of the RPK.
|
||||
Integer serialPack = (ki.ChannelID * MaxSerial) + ki.Serial;
|
||||
Integer pData = (serialPack << 1) | ki.isUpgrade;
|
||||
DWORD pData = info.Serial << 1 | info.isUpgrade;
|
||||
|
||||
// prepare the private key for generation
|
||||
privateKey = genOrder - privateKey;
|
||||
BN_sub(privateKey, genOrder, privateKey);
|
||||
|
||||
do
|
||||
{
|
||||
ECP::Point R;
|
||||
EC_POINT *r = EC_POINT_new(eCurve);
|
||||
|
||||
// Generate a random number c consisting of 384 bits without any constraints.
|
||||
c.Randomize(UMSKT::rng, FieldBits);
|
||||
BN_rand(c, FIELD_BITS, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY);
|
||||
|
||||
// Pick a random derivative of the base point on the elliptic curve.
|
||||
// R = cG;
|
||||
R = eCurve.Multiply(c, genPoint);
|
||||
if (debug)
|
||||
{
|
||||
fmt::print(debug, "c: {:x}\n\n", c);
|
||||
fmt::print(debug, "R[x,y] [{:x},\n{:x}]\n\n", R.x, R.y);
|
||||
}
|
||||
EC_POINT_mul(eCurve, r, nullptr, genPoint, c, numContext);
|
||||
|
||||
// Acquire its coordinates.
|
||||
// x = R.x; y = R.y;
|
||||
EC_POINT_get_affine_coordinates(eCurve, r, x, y, numContext);
|
||||
|
||||
BYTE msgDigest[SHA1::DIGESTSIZE], msgBuffer[SHAMessageLength], *pMsgBuffer = msgBuffer;
|
||||
BYTE msgDigest[SHA_DIGEST_LENGTH], msgBuffer[SHA_MSG_LENGTH_XP];
|
||||
BYTE xBin[FIELD_BYTES], yBin[FIELD_BYTES];
|
||||
|
||||
// Convert coordinates to bytes.
|
||||
UMSKT::BN_bn2lebin(x, xBin, FIELD_BYTES);
|
||||
UMSKT::BN_bn2lebin(y, yBin, FIELD_BYTES);
|
||||
|
||||
// Assemble the SHA message.
|
||||
pMsgBuffer = EncodeN(pData, pMsgBuffer, 4);
|
||||
pMsgBuffer = EncodeN(R.x, pMsgBuffer, FieldBytes);
|
||||
EncodeN(R.y, pMsgBuffer, FieldBytes);
|
||||
memcpy(&msgBuffer[0], &pData, 4);
|
||||
memcpy(&msgBuffer[4], xBin, FIELD_BYTES);
|
||||
memcpy(&msgBuffer[4 + FIELD_BYTES], yBin, FIELD_BYTES);
|
||||
|
||||
// pHash = SHA1(pSerial || R.x || R.y)
|
||||
sha1.CalculateDigest(msgDigest, msgBuffer, sizeof(msgBuffer));
|
||||
|
||||
if (debug)
|
||||
{
|
||||
fmt::print(debug, "msgBuffer: ");
|
||||
for (BYTE b : msgBuffer)
|
||||
{
|
||||
fmt::print(debug, "{:x}", b);
|
||||
}
|
||||
fmt::print(debug, "\n\n");
|
||||
|
||||
fmt::print(debug, "msgDigest: ");
|
||||
for (BYTE b : msgDigest)
|
||||
{
|
||||
fmt::print(debug, "{:x}", b);
|
||||
}
|
||||
fmt::print(debug, "\n\n");
|
||||
}
|
||||
SHA1(msgBuffer, SHA_MSG_LENGTH_XP, msgDigest);
|
||||
|
||||
// Translate the byte digest into a 32-bit integer - this is our computed pHash.
|
||||
// Truncate the pHash to 28 bits.
|
||||
|
||||
ki.Hash = IntegerN(msgDigest, 4) >> 4;
|
||||
ki.Hash = CryptoPP::Crop(ki.Hash, 28);
|
||||
info.Hash = BYDWORD(msgDigest) >> 4 & BITMASK(28);
|
||||
|
||||
/*
|
||||
*
|
||||
@ -178,38 +148,37 @@ BOOL BINK1998::Generate(std::string &pKey)
|
||||
*/
|
||||
|
||||
// s = ek;
|
||||
s = privateKey * ki.Hash;
|
||||
BN_copy(s, privateKey);
|
||||
BN_mul_word(s, info.Hash);
|
||||
|
||||
// s += c (mod n)
|
||||
s += c;
|
||||
s %= genOrder;
|
||||
BN_mod_add(s, s, c, genOrder, numContext);
|
||||
|
||||
// Translate resulting scalar into an Integer.
|
||||
ki.Signature = s;
|
||||
// Translate resulting scalar into a 64-bit integer (the byte order is little-endian).
|
||||
BN_bn2lebinpad(s, (BYTE *)&info.Signature, BN_num_bytes(s));
|
||||
|
||||
// Pack product key.
|
||||
pRaw = Pack(ki);
|
||||
Pack(pRaw);
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
fmt::print(verbose, "Generation 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");
|
||||
}
|
||||
auto serial = fmt::format("{:d}", info.Serial);
|
||||
fmt::print(UMSKT::debug, "Generation results:\n");
|
||||
fmt::print(UMSKT::debug, "{:>10}: {:b}\n", "Upgrade", (bool)info.isUpgrade);
|
||||
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Channel ID", serial.substr(0, 3));
|
||||
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Sequence", serial.substr(3));
|
||||
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Hash", info.Hash);
|
||||
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Signature", info.Signature);
|
||||
fmt::print(UMSKT::debug, "\n");
|
||||
|
||||
} while (ki.Signature.BitCount() > 55);
|
||||
EC_POINT_free(r);
|
||||
} while (info.Signature > BITMASK(55));
|
||||
// ↑ ↑ ↑
|
||||
// The signature can't be longer than 55 bits, else it will
|
||||
// make the CD-key longer than 25 characters.
|
||||
|
||||
// Convert bytecode to Base24 CD-key.
|
||||
pKey = base24(pRaw);
|
||||
base24(pKey, (BYTE *)pRaw);
|
||||
|
||||
info = ki;
|
||||
BN_CTX_free(numContext);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -221,33 +190,33 @@ BOOL BINK1998::Generate(std::string &pKey)
|
||||
*
|
||||
* @return true if provided key validates against loaded curve
|
||||
*/
|
||||
BOOL BINK1998::Validate(const std::string &pKey)
|
||||
BOOL BINK1998::Validate(std::string &pKey)
|
||||
{
|
||||
if (pKey.length() != 25)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BN_CTX *numContext = BN_CTX_new();
|
||||
|
||||
QWORD pRaw[2];
|
||||
|
||||
// Convert Base24 CD-key to bytecode.
|
||||
Integer pRaw = unbase24(pKey);
|
||||
SHA1 sha1;
|
||||
unbase24((BYTE *)pRaw, pKey);
|
||||
|
||||
// Extract RPK, hash and signature from bytecode.
|
||||
KeyInfo ki = Unpack(pRaw);
|
||||
Unpack(pRaw);
|
||||
|
||||
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");
|
||||
}
|
||||
auto serial = fmt::format("{:d}", info.Serial);
|
||||
fmt::print(UMSKT::debug, "Validation results:\n");
|
||||
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Upgrade", (bool)info.isUpgrade);
|
||||
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Channel ID", serial.substr(0, 3));
|
||||
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Sequence", serial.substr(3));
|
||||
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Hash", info.Hash);
|
||||
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Signature", info.Signature);
|
||||
fmt::print(UMSKT::debug, "\n");
|
||||
|
||||
Integer serialPack = (ki.ChannelID * MaxSerial) + ki.Serial;
|
||||
Integer pData = serialPack << 1 | ki.isUpgrade;
|
||||
DWORD pData = info.Serial << 1 | info.isUpgrade;
|
||||
|
||||
/*
|
||||
*
|
||||
@ -264,49 +233,52 @@ BOOL BINK1998::Validate(const std::string &pKey)
|
||||
*
|
||||
*/
|
||||
|
||||
Integer e = ki.Hash, s = ki.Signature;
|
||||
BIGNUM *e = BN_lebin2bn((BYTE *)&info.Hash, sizeof(info.Hash), nullptr),
|
||||
*s = BN_lebin2bn((BYTE *)&info.Signature, sizeof(info.Signature), nullptr);
|
||||
|
||||
BIGNUM *x = BN_CTX_get(numContext), *y = BN_CTX_get(numContext);
|
||||
|
||||
// Create 2 points on the elliptic curve.
|
||||
ECP::Point t, P;
|
||||
EC_POINT *t = EC_POINT_new(eCurve), *p = EC_POINT_new(eCurve);
|
||||
|
||||
// t = sG
|
||||
t = eCurve.Multiply(s, genPoint);
|
||||
EC_POINT_mul(eCurve, t, nullptr, genPoint, s, numContext);
|
||||
|
||||
// P = eK
|
||||
P = eCurve.Multiply(e, pubPoint);
|
||||
EC_POINT_mul(eCurve, p, nullptr, pubPoint, e, numContext);
|
||||
|
||||
// P += t
|
||||
P = eCurve.Add(P, t);
|
||||
EC_POINT_add(eCurve, p, t, p, numContext);
|
||||
|
||||
if (debug)
|
||||
{
|
||||
fmt::print("\nP[x,y]: [{:x},\n{:x}]\n\n", P.x, P.y);
|
||||
}
|
||||
// x = P.x; y = P.y;
|
||||
EC_POINT_get_affine_coordinates(eCurve, p, x, y, numContext);
|
||||
|
||||
BYTE msgDigest[SHA1::DIGESTSIZE], msgBuffer[SHAMessageLength], *pMsgBuffer = msgBuffer;
|
||||
BYTE msgDigest[SHA_DIGEST_LENGTH], msgBuffer[SHA_MSG_LENGTH_XP], xBin[FIELD_BYTES], yBin[FIELD_BYTES];
|
||||
|
||||
// Convert resulting point coordinates to bytes.
|
||||
UMSKT::BN_bn2lebin(x, xBin, FIELD_BYTES);
|
||||
UMSKT::BN_bn2lebin(y, yBin, FIELD_BYTES);
|
||||
|
||||
// Assemble the SHA message.
|
||||
pMsgBuffer = EncodeN(pData, pMsgBuffer, 4);
|
||||
pMsgBuffer = EncodeN(P.x, pMsgBuffer, FieldBytes);
|
||||
EncodeN(P.y, pMsgBuffer, FieldBytes);
|
||||
memcpy(&msgBuffer[0], &pData, 4);
|
||||
memcpy(&msgBuffer[4], xBin, FIELD_BYTES);
|
||||
memcpy(&msgBuffer[4 + FIELD_BYTES], yBin, FIELD_BYTES);
|
||||
|
||||
// compHash = SHA1(pSerial || P.x || P.y)
|
||||
sha1.CalculateDigest(msgDigest, msgBuffer, SHAMessageLength);
|
||||
SHA1(msgBuffer, SHA_MSG_LENGTH_XP, msgDigest);
|
||||
|
||||
auto intDigest = IntegerN(msgDigest);
|
||||
if (debug)
|
||||
{
|
||||
fmt::print(debug, "hash: {:x}\n\n", intDigest);
|
||||
}
|
||||
|
||||
info = ki;
|
||||
|
||||
// Translate the byte sha1 into a 32-bit integer - this is our computed hash.
|
||||
// Translate the byte digest into a 32-bit integer - this is our computed hash.
|
||||
// Truncate the hash to 28 bits.
|
||||
Integer compHash = CryptoPP::Crop(intDigest >> 4, 28);
|
||||
DWORD compHash = BYDWORD(msgDigest) >> 4 & BITMASK(28);
|
||||
|
||||
BN_free(e);
|
||||
BN_free(s);
|
||||
|
||||
BN_CTX_free(numContext);
|
||||
|
||||
EC_POINT_free(t);
|
||||
EC_POINT_free(p);
|
||||
|
||||
// If the computed hash checks out, the key is valid.
|
||||
return compHash == ki.Hash;
|
||||
return compHash == info.Hash;
|
||||
}
|
||||
|
@ -29,7 +29,6 @@ class EXPORT BINK1998 : public PIDGEN3
|
||||
{
|
||||
public:
|
||||
using PIDGEN3::PIDGEN3;
|
||||
BINK1998() = default;
|
||||
explicit BINK1998(PIDGEN3 *p3)
|
||||
{
|
||||
privateKey = p3->privateKey;
|
||||
@ -39,21 +38,17 @@ class EXPORT BINK1998 : public PIDGEN3
|
||||
eCurve = p3->eCurve;
|
||||
}
|
||||
|
||||
static constexpr DWORD32 FieldBits = (48 * 8);
|
||||
static constexpr DWORD32 FieldBytes = (FieldBits / 8);
|
||||
static constexpr DWORD32 SHAMessageLength = (4 + 2 * FieldBytes);
|
||||
|
||||
using PIDGEN3::Pack;
|
||||
Integer Pack(const KeyInfo &ki) override;
|
||||
BOOL Pack(QWORD *pRaw) override;
|
||||
|
||||
using PIDGEN3::Unpack;
|
||||
KeyInfo Unpack(const Integer &raw) override;
|
||||
BOOL Unpack(QWORD *pRaw) override;
|
||||
|
||||
using PIDGEN3::Generate;
|
||||
BOOL Generate(std::string &pKey) override;
|
||||
|
||||
using PIDGEN3::Validate;
|
||||
BOOL Validate(const std::string &pKey) override;
|
||||
BOOL Validate(std::string &pKey) override;
|
||||
};
|
||||
|
||||
#endif // UMSKT_BINK1998_H
|
||||
|
@ -24,7 +24,6 @@
|
||||
* and uploaded to GitHub by TheMCHK in August of 2019
|
||||
*
|
||||
* Endermanch (Andrew) rewrote the algorithm in May of 2023
|
||||
* Neo ported Endermanch's algorithm to CryptoPP in February of 2024
|
||||
* }
|
||||
*/
|
||||
|
||||
@ -33,53 +32,45 @@
|
||||
/**
|
||||
* Packs a Windows Server 2003-like Product Key.
|
||||
*
|
||||
* @param ki PIDGEN3::KeyInfo struct to pack
|
||||
* @return Integer representation of the Product Key
|
||||
*/
|
||||
Integer BINK2002::Pack(const KeyInfo &ki)
|
||||
* @param pRaw *QWORD[2] raw product key output
|
||||
**/
|
||||
BOOL BINK2002::Pack(QWORD *pRaw)
|
||||
{
|
||||
// AuthInfo [113..104] <- Signature [103..42] <- Hash [41..11] <- Channel ID [10..1] <- Upgrade [0];
|
||||
Integer raw = CryptoPP::Crop(ki.AuthInfo, 10) << 104 | CryptoPP::Crop(ki.Signature, 62) << 42 |
|
||||
CryptoPP::Crop(ki.Hash, 31) << 11 | CryptoPP::Crop(ki.ChannelID, 10) << 1 | ki.isUpgrade;
|
||||
// AuthInfo [113..104] <- Signature [103..42] <- Hash [41..11] <- Channel ID [10..1] <- Upgrade [0]
|
||||
pRaw[0] = FIRSTNBITS(info.Signature, 22) << 42 | (QWORD)info.Hash << 11 | info.ChannelID << 1 | info.isUpgrade;
|
||||
pRaw[1] = FIRSTNBITS(info.AuthInfo, 10) << 40 | NEXTSNBITS(info.Signature, 40, 22);
|
||||
|
||||
if (debug)
|
||||
{
|
||||
fmt::print("pack: {:x}\n\n", raw);
|
||||
}
|
||||
|
||||
return raw;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpacks a Windows Server 2003-like Product Key.
|
||||
*
|
||||
* @param raw Integer representation of the product key
|
||||
* @return unpacked PIDGEN3::KeyInfo struct
|
||||
*/
|
||||
BINK2002::KeyInfo BINK2002::Unpack(const Integer &raw)
|
||||
* @param pRaw *QWORD[2] raw product key input
|
||||
**/
|
||||
BOOL BINK2002::Unpack(QWORD *pRaw)
|
||||
{
|
||||
// We're assuming that the quantity of information within the product key is at most 114 bits.
|
||||
// log2(24^25) = 114.
|
||||
KeyInfo ki;
|
||||
|
||||
// Upgrade = Bit 0
|
||||
ki.isUpgrade = CryptoPP::Crop(raw, 1).ConvertToLong();
|
||||
info.isUpgrade = FIRSTNBITS(pRaw[0], 1);
|
||||
|
||||
// Channel ID = Bits [1..10] -> 10 bits
|
||||
ki.ChannelID = CryptoPP::Crop(raw >> 1, 10);
|
||||
info.ChannelID = NEXTSNBITS(pRaw[0], 10, 1);
|
||||
|
||||
// Hash = Bits [11..41] -> 30 bits
|
||||
ki.Hash = CryptoPP::Crop(raw >> 11, 31);
|
||||
// Hash = Bits [11..41] -> 31 bits
|
||||
info.Hash = NEXTSNBITS(pRaw[0], 31, 11);
|
||||
|
||||
// Signature = Bits [42..103] -> 62 bits
|
||||
// The quad-word signature overlaps AuthInfo in bits 104 and 105,
|
||||
// hence Microsoft employs a secret technique called: Signature = HIDWORD(Signature) >> 2 | LODWORD(Signature)
|
||||
ki.Signature = CryptoPP::Crop(raw >> 42, 62);
|
||||
info.Signature = NEXTSNBITS(pRaw[1], 30, 10) << 32 | FIRSTNBITS(pRaw[1], 10) << 22 | NEXTSNBITS(pRaw[0], 22, 42);
|
||||
|
||||
// AuthInfo = Bits [104..113] -> 10 bits
|
||||
ki.AuthInfo = CryptoPP::Crop(raw >> 104, 10);
|
||||
info.AuthInfo = NEXTSNBITS(pRaw[1], 10, 40);
|
||||
|
||||
return ki;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -91,135 +82,77 @@ BINK2002::KeyInfo BINK2002::Unpack(const Integer &raw)
|
||||
*/
|
||||
BOOL BINK2002::Generate(std::string &pKey)
|
||||
{
|
||||
// copy the starting state from the class
|
||||
KeyInfo ki = info;
|
||||
SHA1 sha1;
|
||||
BN_CTX *numContext = BN_CTX_new();
|
||||
|
||||
Integer c, e, s, pRaw;
|
||||
BIGNUM *c = BN_CTX_get(numContext), *e = BN_CTX_get(numContext), *s = BN_CTX_get(numContext),
|
||||
*x = BN_CTX_get(numContext), *y = BN_CTX_get(numContext);
|
||||
|
||||
QWORD pRaw[2];
|
||||
|
||||
// Data segment of the RPK.
|
||||
Integer pData = ki.ChannelID << 1 | ki.isUpgrade;
|
||||
DWORD pData = info.ChannelID << 1 | info.isUpgrade;
|
||||
|
||||
BOOL noSquare;
|
||||
|
||||
do
|
||||
{
|
||||
ECP::Point R;
|
||||
EC_POINT *r = EC_POINT_new(eCurve);
|
||||
|
||||
// Generate a random number c consisting of 512 bits without any constraints.
|
||||
c.Randomize(UMSKT::rng, FieldBits);
|
||||
BN_rand(c, FIELD_BITS_2003, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY);
|
||||
|
||||
// R = cG
|
||||
R = eCurve.Multiply(c, genPoint);
|
||||
if (debug)
|
||||
{
|
||||
fmt::print(debug, "c: {:x}\n\n", c);
|
||||
fmt::print(debug, "R[x,y] [{:x},\n{:x}]\n\n", R.x, R.y);
|
||||
}
|
||||
EC_POINT_mul(eCurve, r, nullptr, genPoint, c, numContext);
|
||||
|
||||
BYTE msgDigest[SHA1::DIGESTSIZE], msgBuffer[SHAMessageLength], *pMsgBuffer = msgBuffer;
|
||||
// Acquire its coordinates.
|
||||
// x = R.x; y = R.y;
|
||||
EC_POINT_get_affine_coordinates(eCurve, r, x, y, numContext);
|
||||
|
||||
// Assemble the first SHA message.
|
||||
*pMsgBuffer = 0x79;
|
||||
pMsgBuffer++;
|
||||
|
||||
pMsgBuffer = EncodeN(pData, pMsgBuffer, 2);
|
||||
BYTE msgDigest[SHA_DIGEST_LENGTH], msgBuffer[SHA_MSG_LENGTH_2003], xBin[FIELD_BYTES_2003],
|
||||
yBin[FIELD_BYTES_2003];
|
||||
|
||||
// Convert resulting point coordinates to bytes.
|
||||
// and flip the endianness
|
||||
pMsgBuffer = EncodeN(R.x, pMsgBuffer, FieldBytes);
|
||||
EncodeN(R.y, pMsgBuffer, FieldBytes);
|
||||
UMSKT::BN_bn2lebin(x, xBin, FIELD_BYTES_2003);
|
||||
UMSKT::BN_bn2lebin(y, yBin, FIELD_BYTES_2003);
|
||||
|
||||
// Assemble the first SHA message.
|
||||
msgBuffer[0x00] = 0x79;
|
||||
msgBuffer[0x01] = (pData & 0x00FF);
|
||||
msgBuffer[0x02] = (pData & 0xFF00) >> 8;
|
||||
|
||||
memcpy(&msgBuffer[3], xBin, FIELD_BYTES_2003);
|
||||
memcpy(&msgBuffer[3 + FIELD_BYTES_2003], yBin, FIELD_BYTES_2003);
|
||||
|
||||
// pHash = SHA1(79 || Channel ID || R.x || R.y)
|
||||
sha1.CalculateDigest(msgDigest, msgBuffer, SHAMessageLength);
|
||||
SHA1(msgBuffer, SHA_MSG_LENGTH_2003, msgDigest);
|
||||
|
||||
if (debug)
|
||||
{
|
||||
fmt::print("msgBuffer[1]: ");
|
||||
for (BYTE b : msgBuffer)
|
||||
{
|
||||
fmt::print("{:x}", b);
|
||||
}
|
||||
fmt::print("\n\n");
|
||||
|
||||
fmt::print("msgDigest[1]: ");
|
||||
for (BYTE b : msgDigest)
|
||||
{
|
||||
fmt::print("{:x}", b);
|
||||
}
|
||||
fmt::print("\n\n");
|
||||
}
|
||||
|
||||
// Translate the byte sha1 into a 32-bit integer - this is our computed hash.
|
||||
// Translate the byte digest into a 32-bit integer - this is our computed hash.
|
||||
// Truncate the hash to 31 bits.
|
||||
ki.Hash = CryptoPP::Crop(IntegerN(msgDigest), 31);
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
BYTE buf[8];
|
||||
sha1.CalculateTruncatedDigest(buf, sizeof(buf), msgBuffer, SHAMessageLength);
|
||||
|
||||
fmt::print("truncated buffer: ");
|
||||
for (BYTE b : buf)
|
||||
{
|
||||
fmt::print("{:x}", b);
|
||||
}
|
||||
fmt::print("\n\n");
|
||||
|
||||
DWORD h0 = ((DWORD)buf[0] | ((DWORD)buf[1] << 8) | ((DWORD)buf[2] << 16) | ((DWORD)buf[3] << 24));
|
||||
DWORD h1 =
|
||||
((((DWORD)buf[4]) | ((DWORD)buf[5] << 8) | ((DWORD)buf[6] << 16) | ((DWORD)buf[7] << 24)) >> (32 - 19))
|
||||
<< 1;
|
||||
|
||||
h1 |= (h0 >> 31) & 1;
|
||||
|
||||
fmt::print("h0,1: {:x} {:x}\n\n", h0, h1);
|
||||
|
||||
ki.Serial = IntegerN(h1);
|
||||
|
||||
fmt::print("serial: {:d}\n\n", ki.Serial);
|
||||
}
|
||||
info.Hash = BYDWORD(msgDigest) & BITMASK(31);
|
||||
|
||||
// Assemble the second SHA message.
|
||||
pMsgBuffer = msgBuffer;
|
||||
msgBuffer[0x00] = 0x5D;
|
||||
pMsgBuffer++;
|
||||
|
||||
pMsgBuffer = EncodeN(pData, pMsgBuffer, 2);
|
||||
pMsgBuffer = EncodeN(ki.Hash, pMsgBuffer, 4);
|
||||
pMsgBuffer = EncodeN(ki.AuthInfo, pMsgBuffer, 2);
|
||||
|
||||
*pMsgBuffer = 0x00;
|
||||
pMsgBuffer++;
|
||||
|
||||
*pMsgBuffer = 0x00;
|
||||
pMsgBuffer++;
|
||||
msgBuffer[0x01] = (pData & 0x00FF);
|
||||
msgBuffer[0x02] = (pData & 0xFF00) >> 8;
|
||||
msgBuffer[0x03] = (info.Hash & 0x000000FF);
|
||||
msgBuffer[0x04] = (info.Hash & 0x0000FF00) >> 8;
|
||||
msgBuffer[0x05] = (info.Hash & 0x00FF0000) >> 16;
|
||||
msgBuffer[0x06] = (info.Hash & 0xFF000000) >> 24;
|
||||
msgBuffer[0x07] = (info.AuthInfo & 0x00FF);
|
||||
msgBuffer[0x08] = (info.AuthInfo & 0xFF00) >> 8;
|
||||
msgBuffer[0x09] = 0x00;
|
||||
msgBuffer[0x0A] = 0x00;
|
||||
|
||||
// newSignature = SHA1(5D || Channel ID || Hash || AuthInfo || 00 00)
|
||||
sha1.CalculateDigest(msgDigest, msgBuffer, pMsgBuffer - msgBuffer);
|
||||
SHA1(msgBuffer, 11, msgDigest);
|
||||
|
||||
if (debug)
|
||||
{
|
||||
fmt::print("msgBuffer[2]: ");
|
||||
for (BYTE b : msgBuffer)
|
||||
{
|
||||
fmt::print("{:x}", b);
|
||||
}
|
||||
fmt::print("\n\n");
|
||||
|
||||
fmt::print("msgDigest[2]: ");
|
||||
for (BYTE b : msgDigest)
|
||||
{
|
||||
fmt::print("{:x}", b);
|
||||
}
|
||||
fmt::print("\n\n");
|
||||
}
|
||||
|
||||
// Translate the byte sha1 into a 64-bit integer - this is our computed intermediate signature.
|
||||
// Translate the byte digest into a 64-bit integer - this is our computed intermediate signature.
|
||||
// As the signature is only 62 bits long at most, we have to truncate it by shifting the high DWORD right 2
|
||||
// bits (per spec).
|
||||
QWORD iSignature = NEXTSNBITS(BYDWORD(&msgDigest[4]), 30, 2) << 32 | BYDWORD(msgDigest);
|
||||
|
||||
BN_lebin2bn((BYTE *)&iSignature, sizeof(iSignature), e);
|
||||
|
||||
/*
|
||||
*
|
||||
* Scalars:
|
||||
@ -250,62 +183,63 @@ BOOL BINK2002::Generate(std::string &pKey)
|
||||
*/
|
||||
|
||||
// e = ek (mod n)
|
||||
e = CryptoPP::ModularMultiplication(IntegerN(iSignature), privateKey, genOrder);
|
||||
BN_mod_mul(e, e, privateKey, genOrder, numContext);
|
||||
|
||||
// s = e
|
||||
BN_copy(s, e);
|
||||
|
||||
// s = (ek (mod n))²
|
||||
s = CryptoPP::ModularExponentiation(e, Integer::Two(), genOrder);
|
||||
BN_mod_sqr(s, s, genOrder, numContext);
|
||||
|
||||
// c *= 4 (c <<= 2)
|
||||
c *= 4;
|
||||
BN_lshift(c, c, 2);
|
||||
|
||||
// s += c
|
||||
s += c;
|
||||
BN_add(s, s, c);
|
||||
|
||||
// Around half of numbers modulo a prime are not squares -> BN_sqrt_mod fails about half of the times,
|
||||
// hence if BN_sqrt_mod returns NULL, we need to restart with a different seed.
|
||||
// s = √((ek)² + 4c (mod n))
|
||||
s = CryptoPP::ModularSquareRoot(s, genOrder);
|
||||
noSquare = s.IsZero();
|
||||
noSquare = BN_mod_sqrt(s, s, genOrder, numContext) == nullptr;
|
||||
|
||||
// s = -ek + √((ek)² + 4c) (mod n)
|
||||
s -= e;
|
||||
s %= genOrder;
|
||||
BN_mod_sub(s, s, e, genOrder, numContext);
|
||||
|
||||
// If s is odd, add order to it.
|
||||
// The order is a prime, so it can't be even.
|
||||
if (s % Integer::Two() != 0)
|
||||
if (BN_is_odd(s))
|
||||
{
|
||||
// s = -ek + √((ek)² + 4c) + n
|
||||
s += genOrder;
|
||||
BN_add(s, s, genOrder);
|
||||
}
|
||||
|
||||
// s /= 2 (s >>= 1)
|
||||
s /= 2;
|
||||
BN_rshift1(s, s);
|
||||
|
||||
// Translate resulting scalar into a 64-bit integer (the byte order is little-endian).
|
||||
ki.Signature = s;
|
||||
BN_bn2lebinpad(s, (BYTE *)&info.Signature, BN_num_bytes(s));
|
||||
|
||||
// Pack product key.
|
||||
pRaw = Pack(ki);
|
||||
Pack(pRaw);
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
fmt::print(verbose, "Generation results:\n");
|
||||
fmt::print(verbose, "{:>10}: {}\n", "Upgrade", (bool)ki.isUpgrade);
|
||||
fmt::print(verbose, "{:>10}: {}\n", "Channel ID", ki.ChannelID);
|
||||
fmt::print(verbose, "{:>10}: {:x}\n", "Hash", ki.Hash);
|
||||
fmt::print(verbose, "{:>10}: {:x}\n", "Signature", ki.Signature);
|
||||
fmt::print(verbose, "{:>10}: {:x}\n", "AuthInfo", ki.AuthInfo);
|
||||
fmt::print(verbose, "\n");
|
||||
}
|
||||
} while (ki.Signature.BitCount() > 62 || noSquare);
|
||||
fmt::print(UMSKT::debug, "Generation results:\n");
|
||||
fmt::print(UMSKT::debug, "{:>10}: {:b}\n", "Upgrade", (bool)info.isUpgrade);
|
||||
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Channel ID", info.ChannelID);
|
||||
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Hash", info.Hash);
|
||||
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Signature", info.Signature);
|
||||
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "AuthInfo", info.AuthInfo);
|
||||
fmt::print(UMSKT::debug, "\n");
|
||||
|
||||
EC_POINT_free(r);
|
||||
} while (info.Signature > BITMASK(62) || noSquare);
|
||||
// ↑ ↑ ↑
|
||||
// The signature can't be longer than 62 bits, else it will
|
||||
// overlap with the AuthInfo segment next to it.
|
||||
// Convert bytecode to Base24 CD-key.
|
||||
pKey = base24(pRaw);
|
||||
|
||||
info = ki;
|
||||
// Convert bytecode to Base24 CD-key.
|
||||
base24(pKey, (BYTE *)pRaw);
|
||||
|
||||
BN_CTX_free(numContext);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -315,56 +249,47 @@ BOOL BINK2002::Generate(std::string &pKey)
|
||||
*
|
||||
* @param pKey
|
||||
**/
|
||||
BOOL BINK2002::Validate(const std::string &pKey)
|
||||
BOOL BINK2002::Validate(std::string &pKey)
|
||||
{
|
||||
Integer pRaw;
|
||||
SHA1 sha1;
|
||||
BN_CTX *context = BN_CTX_new();
|
||||
|
||||
QWORD bKey[2];
|
||||
|
||||
// Convert Base24 CD-key to bytecode.
|
||||
pRaw = unbase24(pKey);
|
||||
unbase24((BYTE *)bKey, &pKey[0]);
|
||||
|
||||
// Extract product key segments from bytecode.
|
||||
KeyInfo ki = Unpack(pRaw);
|
||||
Unpack(bKey);
|
||||
|
||||
Integer pData = ki.ChannelID << 1 | ki.isUpgrade;
|
||||
DWORD pData = info.ChannelID << 1 | info.isUpgrade;
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
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}: {:x}\n", "Hash", ki.Hash);
|
||||
fmt::print(verbose, "{:>10}: {:x}\n", "Signature", ki.Signature);
|
||||
fmt::print(verbose, "{:>10}: {:x}\n", "AuthInfo", ki.AuthInfo);
|
||||
fmt::print(verbose, "\n");
|
||||
}
|
||||
fmt::print(UMSKT::debug, "Validation results:\n");
|
||||
fmt::print(UMSKT::debug, "{:>10}: {:b}\n", "Upgrade", (bool)info.isUpgrade);
|
||||
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Channel ID", info.ChannelID);
|
||||
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Hash", info.Hash);
|
||||
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Signature", info.Signature);
|
||||
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "AuthInfo", info.AuthInfo);
|
||||
fmt::print(UMSKT::debug, "\n");
|
||||
|
||||
BYTE msgDigest[SHA1::DIGESTSIZE], msgBuffer[SHAMessageLength], *pMsgBuffer = msgBuffer;
|
||||
BYTE msgDigest[SHA_DIGEST_LENGTH], msgBuffer[SHA_MSG_LENGTH_2003], xBin[FIELD_BYTES_2003], yBin[FIELD_BYTES_2003];
|
||||
|
||||
// Assemble the first SHA message.
|
||||
msgBuffer[0x00] = 0x5D;
|
||||
pMsgBuffer++;
|
||||
|
||||
pMsgBuffer = EncodeN(pData, pMsgBuffer, 2);
|
||||
pMsgBuffer = EncodeN(ki.Hash, pMsgBuffer, 4);
|
||||
pMsgBuffer = EncodeN(ki.AuthInfo, pMsgBuffer, 2);
|
||||
|
||||
*pMsgBuffer = 0x00;
|
||||
pMsgBuffer++;
|
||||
|
||||
*pMsgBuffer = 0x00;
|
||||
pMsgBuffer++;
|
||||
msgBuffer[0x01] = (pData & 0x00FF);
|
||||
msgBuffer[0x02] = (pData & 0xFF00) >> 8;
|
||||
msgBuffer[0x03] = (info.Hash & 0x000000FF);
|
||||
msgBuffer[0x04] = (info.Hash & 0x0000FF00) >> 8;
|
||||
msgBuffer[0x05] = (info.Hash & 0x00FF0000) >> 16;
|
||||
msgBuffer[0x06] = (info.Hash & 0xFF000000) >> 24;
|
||||
msgBuffer[0x07] = (info.AuthInfo & 0x00FF);
|
||||
msgBuffer[0x08] = (info.AuthInfo & 0xFF00) >> 8;
|
||||
msgBuffer[0x09] = 0x00;
|
||||
msgBuffer[0x0A] = 0x00;
|
||||
|
||||
// newSignature = SHA1(5D || Channel ID || Hash || AuthInfo || 00 00)
|
||||
sha1.CalculateDigest(msgDigest, msgBuffer, pMsgBuffer - msgBuffer);
|
||||
SHA1(msgBuffer, 11, msgDigest);
|
||||
|
||||
if (debug)
|
||||
{
|
||||
auto intDigest = IntegerN(msgDigest);
|
||||
fmt::print(debug, "\nhash 1: {:x}\n\n", intDigest);
|
||||
}
|
||||
|
||||
// Translate the byte sha1 into a 64-bit integer - this is our computed intermediate signature.
|
||||
// Translate the byte digest into a 64-bit integer - this is our computed intermediate signature.
|
||||
// As the signature is only 62 bits long at most, we have to truncate it by shifting the high DWORD right 2 bits
|
||||
// (per spec).
|
||||
QWORD iSignature = NEXTSNBITS(BYDWORD(&msgDigest[4]), 30, 2) << 32 | BYDWORD(msgDigest);
|
||||
@ -383,52 +308,56 @@ BOOL BINK2002::Validate(const std::string &pKey)
|
||||
* P = s(sG + eK)
|
||||
*
|
||||
*/
|
||||
Integer e = IntegerN(iSignature), s = ki.Signature;
|
||||
|
||||
BIGNUM *e = BN_lebin2bn((BYTE *)&iSignature, sizeof(iSignature), nullptr),
|
||||
*s = BN_lebin2bn((BYTE *)&info.Signature, sizeof(info.Signature), nullptr);
|
||||
BIGNUM *x = BN_CTX_get(context), *y = BN_CTX_get(context);
|
||||
|
||||
// Create 2 points on the elliptic curve.
|
||||
ECP::Point P, t;
|
||||
EC_POINT *p = EC_POINT_new(eCurve), *t = EC_POINT_new(eCurve);
|
||||
|
||||
// t = sG
|
||||
t = eCurve.Multiply(s, genPoint);
|
||||
EC_POINT_mul(eCurve, t, nullptr, genPoint, s, context);
|
||||
|
||||
// P = eK
|
||||
P = eCurve.Multiply(e, pubPoint);
|
||||
// p = eK
|
||||
EC_POINT_mul(eCurve, p, nullptr, pubPoint, e, context);
|
||||
|
||||
// P += t
|
||||
P = eCurve.Add(P, t);
|
||||
// p += t
|
||||
EC_POINT_add(eCurve, p, t, p, context);
|
||||
|
||||
// P *= s
|
||||
P = eCurve.Multiply(s, P);
|
||||
// p *= s
|
||||
EC_POINT_mul(eCurve, p, nullptr, p, s, context);
|
||||
|
||||
if (debug)
|
||||
{
|
||||
fmt::print("P[x,y]: [{:x},\n{:x}]\n\n", P.x, P.y);
|
||||
}
|
||||
// x = p.x; y = p.y;
|
||||
EC_POINT_get_affine_coordinates(eCurve, p, x, y, context);
|
||||
|
||||
// Convert resulting point coordinates to bytes.
|
||||
UMSKT::BN_bn2lebin(x, xBin, FIELD_BYTES_2003);
|
||||
UMSKT::BN_bn2lebin(y, yBin, FIELD_BYTES_2003);
|
||||
|
||||
// Assemble the second SHA message.
|
||||
pMsgBuffer = msgBuffer;
|
||||
msgBuffer[0x00] = 0x79;
|
||||
pMsgBuffer++;
|
||||
msgBuffer[0x01] = (pData & 0x00FF);
|
||||
msgBuffer[0x02] = (pData & 0xFF00) >> 8;
|
||||
|
||||
pMsgBuffer = EncodeN(pData, pMsgBuffer, 2);
|
||||
pMsgBuffer = EncodeN(P.x, pMsgBuffer, FieldBytes);
|
||||
EncodeN(P.y, pMsgBuffer, FieldBytes);
|
||||
memcpy((void *)&msgBuffer[3], (void *)xBin, FIELD_BYTES_2003);
|
||||
memcpy((void *)&msgBuffer[3 + FIELD_BYTES_2003], (void *)yBin, FIELD_BYTES_2003);
|
||||
|
||||
// compHash = SHA1(79 || Channel ID || P.x || P.y)
|
||||
sha1.CalculateDigest(msgDigest, msgBuffer, SHAMessageLength);
|
||||
// compHash = SHA1(79 || Channel ID || p.x || p.y)
|
||||
SHA1(msgBuffer, SHA_MSG_LENGTH_2003, msgDigest);
|
||||
|
||||
auto intDigest = IntegerN(msgDigest);
|
||||
if (debug)
|
||||
{
|
||||
fmt::print(debug, "hash 2: {:x}\n\n", intDigest);
|
||||
}
|
||||
|
||||
// Translate the byte sha1 into a 32-bit integer - this is our computed hash.
|
||||
// Translate the byte digest into a 32-bit integer - this is our computed hash.
|
||||
// Truncate the hash to 31 bits.
|
||||
Integer compHash = CryptoPP::Crop(intDigest, 31);
|
||||
DWORD compHash = BYDWORD(msgDigest) & BITMASK(31);
|
||||
|
||||
info = ki;
|
||||
BN_free(s);
|
||||
BN_free(e);
|
||||
|
||||
EC_POINT_free(p);
|
||||
EC_POINT_free(t);
|
||||
|
||||
BN_CTX_free(context);
|
||||
|
||||
// If the computed hash checks out, the key is valid.
|
||||
return compHash == ki.Hash;
|
||||
return compHash == info.Hash;
|
||||
}
|
||||
|
@ -29,7 +29,6 @@ class EXPORT BINK2002 : public PIDGEN3
|
||||
{
|
||||
public:
|
||||
using PIDGEN3::PIDGEN3;
|
||||
BINK2002() = default;
|
||||
explicit BINK2002(PIDGEN3 *p3)
|
||||
{
|
||||
privateKey = p3->privateKey;
|
||||
@ -39,21 +38,17 @@ class EXPORT BINK2002 : public PIDGEN3
|
||||
eCurve = p3->eCurve;
|
||||
}
|
||||
|
||||
static constexpr DWORD32 FieldBits = (64 * 8);
|
||||
static constexpr DWORD32 FieldBytes = (FieldBits / 8);
|
||||
static constexpr DWORD32 SHAMessageLength = (3 + 2 * FieldBytes);
|
||||
|
||||
using PIDGEN3::Pack;
|
||||
Integer Pack(const KeyInfo &ki) override;
|
||||
BOOL Pack(QWORD *pRaw) override;
|
||||
|
||||
using PIDGEN3::Unpack;
|
||||
KeyInfo Unpack(const Integer &raw) override;
|
||||
BOOL Unpack(QWORD *pRaw) override;
|
||||
|
||||
using PIDGEN3::Generate;
|
||||
BOOL Generate(std::string &pKey) override;
|
||||
|
||||
using PIDGEN3::Validate;
|
||||
BOOL Validate(const std::string &pKey) override;
|
||||
BOOL Validate(std::string &pKey) override;
|
||||
};
|
||||
|
||||
#endif // UMSKT_BINK2002_H
|
||||
|
@ -25,19 +25,7 @@
|
||||
#include "BINK2002.h"
|
||||
|
||||
/**
|
||||
* PID 3.0 Product Key Character Set
|
||||
*/
|
||||
const std::string PIDGEN3::pKeyCharset = "BCDFGHJKMPQRTVWXY2346789";
|
||||
|
||||
/**
|
||||
* Maximum Field size for BINK 1998
|
||||
*/
|
||||
const DWORD32 PIDGEN3::MaxSizeBINK1998 = BINK1998::FieldBits + 1;
|
||||
|
||||
/**
|
||||
* RFC 1149.5 specifies 4 as the standard IEEE-vetted random number.
|
||||
*
|
||||
* see also: https://xkcd.com/221/
|
||||
* https://xkcd.com/221/
|
||||
*
|
||||
* @return 4
|
||||
*/
|
||||
@ -62,11 +50,10 @@ int getRandomNumber()
|
||||
*
|
||||
* @return true on success, false on fail
|
||||
*/
|
||||
BOOL PIDGEN3::LoadEllipticCurve(const std::string &BinkIDSel, const std::string &pSel, const std::string &aSel,
|
||||
const std::string &bSel, const std::string &generatorXSel,
|
||||
const std::string &generatorYSel, const std::string &publicKeyXSel,
|
||||
const std::string &publicKeyYSel, const std::string &genOrderSel,
|
||||
const std::string &privateKeySel)
|
||||
BOOL PIDGEN3::LoadEllipticCurve(const std::string pSel, const std::string aSel, const std::string bSel,
|
||||
const std::string generatorXSel, const std::string generatorYSel,
|
||||
const std::string publicKeyXSel, const std::string publicKeyYSel,
|
||||
const std::string genOrderSel, const std::string privateKeySel)
|
||||
{
|
||||
// We cannot produce a valid key without knowing the private key k. The reason for this is that
|
||||
// we need the result of the function K(x; y) = kG(x; y).
|
||||
@ -74,243 +61,217 @@ BOOL PIDGEN3::LoadEllipticCurve(const std::string &BinkIDSel, const std::string
|
||||
// We can, however, validate any given key using the available public key: {p, a, b, G, K}.
|
||||
// genOrder the order of the generator G, a value we have to reverse -> Schoof's Algorithm.
|
||||
|
||||
BINKID = IntegerHexS(BinkIDSel);
|
||||
// Initialize BIGNUM and BIGNUMCTX structures.
|
||||
// BIGNUM - Large numbers
|
||||
// BIGNUMCTX - Context large numbers (temporary)
|
||||
|
||||
// Context variable
|
||||
BN_CTX *context = BN_CTX_new();
|
||||
|
||||
// We're presented with an elliptic curve, a multivariable function y(x; p; a; b), where
|
||||
// y^2 % p = x^3 + ax + b % p.
|
||||
auto p = IntegerS(pSel), a = IntegerS(aSel), b = IntegerS(bSel);
|
||||
BIGNUM *a = BN_CTX_get(context), *b = BN_CTX_get(context), *p = BN_CTX_get(context);
|
||||
|
||||
// Public key will consist of the resulting (x; y) values.
|
||||
auto generatorX = IntegerS(generatorXSel), generatorY = IntegerS(generatorYSel);
|
||||
BIGNUM *publicKeyX = BN_CTX_get(context), *publicKeyY = BN_CTX_get(context);
|
||||
|
||||
// G(x; y) is a generator function, its return value represents a point on the elliptic curve.
|
||||
auto publicKeyX = IntegerS(publicKeyXSel), publicKeyY = IntegerS(publicKeyYSel);
|
||||
BIGNUM *generatorX = BN_CTX_get(context), *generatorY = BN_CTX_get(context);
|
||||
|
||||
genOrder = BN_new();
|
||||
privateKey = BN_new();
|
||||
|
||||
/* Public data */
|
||||
BN_dec2bn(&p, &pSel[0]);
|
||||
BN_dec2bn(&a, &aSel[0]);
|
||||
BN_dec2bn(&b, &bSel[0]);
|
||||
BN_dec2bn(&generatorX, &generatorXSel[0]);
|
||||
BN_dec2bn(&generatorY, &generatorYSel[0]);
|
||||
|
||||
BN_dec2bn(&publicKeyX, &publicKeyXSel[0]);
|
||||
BN_dec2bn(&publicKeyY, &publicKeyYSel[0]);
|
||||
|
||||
/* Computed Data */
|
||||
genOrder = IntegerS(genOrderSel);
|
||||
privateKey = IntegerS(privateKeySel);
|
||||
BN_dec2bn(&genOrder, &genOrderSel[0]);
|
||||
BN_dec2bn(&privateKey, &privateKeySel[0]);
|
||||
|
||||
/* Elliptic Curve calculations. */
|
||||
// The group is defined via Fp = all integers [0; p - 1], where p is prime.
|
||||
// The function EC_POINT_set_affine_coordinates() sets the x and y coordinates for the point p defined over the
|
||||
// curve given in group.
|
||||
eCurve = ECP(p, a, b);
|
||||
eCurve = EC_GROUP_new_curve_GFp(p, a, b, context);
|
||||
|
||||
// Create new point N for the generator on the elliptic curve and set its coordinates to (genX; genY).
|
||||
genPoint = ECP::Point(generatorX, generatorY);
|
||||
// Create new point for the generator on the elliptic curve and set its coordinates to (genX; genY).
|
||||
genPoint = EC_POINT_new(eCurve);
|
||||
EC_POINT_set_affine_coordinates(eCurve, genPoint, generatorX, generatorY, context);
|
||||
|
||||
// Create new point Q for the public key on the elliptic curve and set its coordinates to (pubX; pubY).
|
||||
pubPoint = ECP::Point(publicKeyX, publicKeyY);
|
||||
// Create new point for the public key on the elliptic curve and set its coordinates to (pubX; pubY).
|
||||
pubPoint = EC_POINT_new(eCurve);
|
||||
EC_POINT_set_affine_coordinates(eCurve, pubPoint, publicKeyX, publicKeyY, context);
|
||||
|
||||
// If generator and public key points are not on the elliptic curve, either the generator or the public key values
|
||||
// are incorrect.
|
||||
assert(eCurve.VerifyPoint(genPoint) == true);
|
||||
assert(eCurve.VerifyPoint(pubPoint) == true);
|
||||
assert(EC_POINT_is_on_curve(eCurve, genPoint, context) == true);
|
||||
assert(EC_POINT_is_on_curve(eCurve, pubPoint, context) == true);
|
||||
|
||||
// Cleanup
|
||||
BN_CTX_free(context);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a PID 3.0 generator based on a given field on the heap
|
||||
*
|
||||
* @param field
|
||||
* @return PIDGEN3 based on the field type
|
||||
*/
|
||||
PIDGEN3 *PIDGEN3::Factory(const std::string &field)
|
||||
{
|
||||
if (checkFieldStrIsBink1998(field))
|
||||
{
|
||||
return new BINK1998();
|
||||
}
|
||||
return new BINK2002();
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory-style Generate function, checks the currently instantiated field
|
||||
* creates the correct PIDGEN for the field type using the copy constructor
|
||||
* and invokes its Generate()
|
||||
*
|
||||
* @param pKey
|
||||
* @return successfulness
|
||||
*/
|
||||
BOOL PIDGEN3::Generate(std::string &pKey)
|
||||
{
|
||||
BOOL retval;
|
||||
|
||||
if (checkFieldIsBink1998())
|
||||
{
|
||||
auto p3 = BINK1998();
|
||||
retval = p3.Generate(pKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto p3 = BINK2002();
|
||||
retval = p3.Generate(pKey);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
BOOL PIDGEN3::Validate(std::string &pKey)
|
||||
{
|
||||
BOOL retval;
|
||||
|
||||
if (checkFieldIsBink1998())
|
||||
{
|
||||
auto p3 = BINK1998(this);
|
||||
return p3.Generate(pKey);
|
||||
retval = p3.Validate(pKey);
|
||||
}
|
||||
|
||||
auto p3 = BINK2002(this);
|
||||
return p3.Generate(pKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory style Validate function, see Generate() for more info
|
||||
*
|
||||
* @param pKey
|
||||
* @return successfulness
|
||||
*/
|
||||
BOOL PIDGEN3::Validate(const std::string &pKey)
|
||||
else
|
||||
{
|
||||
if (checkFieldIsBink1998())
|
||||
{
|
||||
auto p3 = BINK1998(this);
|
||||
return p3.Validate(pKey);
|
||||
auto p3 = BINK2002(this);
|
||||
retval = p3.Validate(pKey);
|
||||
}
|
||||
|
||||
auto p3 = BINK2002(this);
|
||||
return p3.Validate(pKey);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts from byte sequence to the CD-key.
|
||||
*
|
||||
* @param seq Integer representation
|
||||
* @return std::string CDKey output
|
||||
* @param cdKey [out] std::string CDKey input
|
||||
* @param byteSeq [in] BYTE*
|
||||
**/
|
||||
std::string PIDGEN3::base24(Integer &seq)
|
||||
void PIDGEN3::base24(std::string &cdKey, BYTE *byteSeq)
|
||||
{
|
||||
std::string cdKey;
|
||||
cdKey.reserve(PK_LENGTH);
|
||||
BYTE rbyteSeq[16], output[26];
|
||||
BIGNUM *z;
|
||||
|
||||
// Divide z by 24 and convert the remainder to a CD-key char.
|
||||
Integer r, q, a = seq;
|
||||
for (int i = PK_LENGTH - 1; i >= 0; i--)
|
||||
// Copy byte sequence to the reversed byte sequence.
|
||||
memcpy(rbyteSeq, byteSeq, sizeof(rbyteSeq));
|
||||
|
||||
// Skip trailing zeroes and reverse y.
|
||||
int length;
|
||||
|
||||
for (length = 15; rbyteSeq[length] <= 0; length--)
|
||||
{
|
||||
Integer::Divide(r, q, a, (WORD)pKeyCharset.length());
|
||||
cdKey.insert(cdKey.begin(), pKeyCharset[r.ConvertToLong()]);
|
||||
a = q;
|
||||
; // do nothing, just counting
|
||||
}
|
||||
|
||||
return cdKey;
|
||||
UMSKT::endian(rbyteSeq, ++length);
|
||||
|
||||
// Convert reversed byte sequence to BigNum z.
|
||||
z = BN_bin2bn(rbyteSeq, length, nullptr);
|
||||
|
||||
// Divide z by 24 and convert the remainder to a CD-key char.
|
||||
for (int i = 24; i >= 0; i--)
|
||||
{
|
||||
output[i] = pKeyCharset[BN_div_word(z, 24)];
|
||||
}
|
||||
|
||||
output[25] = 0;
|
||||
|
||||
cdKey = (char *)output;
|
||||
|
||||
BN_free(z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts from CD-key to a byte sequence.
|
||||
*
|
||||
* @param cdKey std::string CDKey to convert
|
||||
* @return Integer raw representation of the CDKey
|
||||
* @param byteSeq [out] *BYTE representation of the CDKey
|
||||
* @param cdKey [in] std::string CDKey to convert
|
||||
**/
|
||||
Integer PIDGEN3::unbase24(const std::string &cdKey)
|
||||
void PIDGEN3::unbase24(BYTE *byteSeq, std::string cdKey)
|
||||
{
|
||||
Integer result;
|
||||
BYTE pDecodedKey[PK_LENGTH + NULL_TERMINATOR]{};
|
||||
BIGNUM *y = BN_new();
|
||||
|
||||
for (char ch : cdKey)
|
||||
// Remove dashes from the CD-key and put it into a Base24 byte array.
|
||||
for (int i = 0, k = 0; i < cdKey.length() && k < PK_LENGTH; i++)
|
||||
{
|
||||
auto val = std::find(pKeyCharset.begin(), pKeyCharset.end(), ch);
|
||||
|
||||
// character is not in set, return early
|
||||
if (val == pKeyCharset.end())
|
||||
for (int j = 0; j < 24; j++)
|
||||
{
|
||||
return result;
|
||||
if (cdKey[i] != '-' && cdKey[i] == pKeyCharset[j])
|
||||
{
|
||||
pDecodedKey[k++] = j;
|
||||
break;
|
||||
}
|
||||
|
||||
// add the weighted sum to result
|
||||
result *= (int)pKeyCharset.length();
|
||||
result += (int)(val - pKeyCharset.begin());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the currently loaded Class-level KeyInfo and calculates the check digit for display.
|
||||
*
|
||||
* Algorithm directly taken from PIDGEN
|
||||
*
|
||||
* @return std::string representation of the Product ID as Displayed on the Product
|
||||
*/
|
||||
std::string PIDGEN3::StringifyProductID()
|
||||
{
|
||||
if (info.isOEM)
|
||||
{
|
||||
Integer OEMID = info.ChannelID * Integer(100);
|
||||
OEMID += ((info.Serial / (MaxSerial / TEN)) * TEN);
|
||||
OEMID += GenerateMod7(OEMID);
|
||||
|
||||
Integer Serial = info.Serial % (MaxSerial / TEN);
|
||||
|
||||
DWORD32 iOEMID = OEMID.ConvertToLong(), iSerial = Serial.ConvertToLong();
|
||||
return fmt::format("PPPPP-OEM-{:07d}-{:05d}", iOEMID, iSerial);
|
||||
}
|
||||
else
|
||||
{
|
||||
DWORD32 ChannelID = info.ChannelID.ConvertToLong(),
|
||||
Serial = (info.Serial * TEN + GenerateMod7(info.Serial)).ConvertToLong(),
|
||||
BinkID = (BINKID / Integer::Two()).ConvertToLong();
|
||||
return fmt::format("PPPPP-{:03d}-{:07d}-{:d}xxx", ChannelID, Serial, BinkID);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the currently instantiated PIDGEN3 object has a
|
||||
* field size greater than the maximum known BINK1998 size.
|
||||
*
|
||||
* @return boolean value
|
||||
*/
|
||||
// Empty byte sequence.
|
||||
memset(byteSeq, 0, 16);
|
||||
|
||||
// Calculate the weighed sum of byte array elements.
|
||||
for (int i = 0; i < PK_LENGTH; i++)
|
||||
{
|
||||
BN_mul_word(y, PK_LENGTH - 1);
|
||||
BN_add_word(y, pDecodedKey[i]);
|
||||
}
|
||||
|
||||
// Acquire length.
|
||||
int n = BN_num_bytes(y);
|
||||
|
||||
// Place the generated code into the byte sequence.
|
||||
BN_bn2bin(y, byteSeq);
|
||||
BN_free(y);
|
||||
|
||||
// Reverse the byte sequence.
|
||||
UMSKT::endian(byteSeq, n);
|
||||
}
|
||||
|
||||
BOOL PIDGEN3::checkFieldIsBink1998()
|
||||
{
|
||||
// is fieldSize < max?
|
||||
return (eCurve.FieldSize().BitCount() < MaxSizeBINK1998);
|
||||
auto *max = BN_new();
|
||||
|
||||
// 1 << 385 (or max size of BINK1998 field in bits + 1)
|
||||
BN_set_bit(max, (12 * 4 * 8) + 1);
|
||||
|
||||
// retval is -1 when (max < privateKey)
|
||||
int retval = BN_cmp(max, privateKey);
|
||||
|
||||
BN_free(max);
|
||||
|
||||
// is max > privateKey?
|
||||
return retval == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given field, in a std::string, is greater than
|
||||
* the maximum known BINK1998 size
|
||||
*
|
||||
* @param keyin std::string representation of a Field
|
||||
* @return boolean value
|
||||
*/
|
||||
BOOL PIDGEN3::checkFieldStrIsBink1998(std::string keyin)
|
||||
{
|
||||
auto check = IntegerS(keyin);
|
||||
auto *context = BN_CTX_new();
|
||||
auto max = BN_CTX_get(context), input = BN_CTX_get(context);
|
||||
|
||||
// is fieldSize < max?
|
||||
return (check.BitCount() < MaxSizeBINK1998);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a product key to stdout
|
||||
*
|
||||
* @param pk std::string to print
|
||||
*/
|
||||
std::string PIDGEN3::StringifyKey(const std::string &pKey)
|
||||
{
|
||||
assert(pKey.length() >= PK_LENGTH);
|
||||
|
||||
return fmt::format("{}-{}-{}-{}-{}", pKey.substr(0, 5), pKey.substr(5, 5), pKey.substr(10, 5), pKey.substr(15, 5),
|
||||
pKey.substr(20, 5));
|
||||
}
|
||||
|
||||
/**
|
||||
* std::BinaryOperation compatible accumulator for validating/stripping an input string against the PIDGEN3 charset
|
||||
*
|
||||
* @param accumulator
|
||||
* @param currentChar
|
||||
* @return
|
||||
*/
|
||||
std::string PIDGEN3::ValidateStringKeyInputCharset(std::string &accumulator, char currentChar)
|
||||
{
|
||||
char cchar = (char)::toupper(currentChar);
|
||||
if (std::find(pKeyCharset.begin(), pKeyCharset.begin(), cchar) != pKeyCharset.end())
|
||||
{
|
||||
accumulator.push_back(cchar);
|
||||
}
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param in_key
|
||||
* @param out_key
|
||||
* @return
|
||||
*/
|
||||
BOOL PIDGEN3::ValidateKeyString(const std::string &in_key, std::string &out_key)
|
||||
{
|
||||
// copy out the product key stripping out extraneous characters
|
||||
out_key = std::accumulate(in_key.begin(), in_key.end(), std::string(), ValidateStringKeyInputCharset);
|
||||
|
||||
// only return true if we've handled exactly PK_LENGTH chars
|
||||
return (out_key.length() == PK_LENGTH);
|
||||
BN_dec2bn(&input, &keyin[0]);
|
||||
|
||||
// 1 << 385 (or max size of BINK1998 field in bits + 1)
|
||||
BN_set_bit(max, (12 * 4 * 8) + 1);
|
||||
|
||||
// retval is -1 when (max < privateKey)
|
||||
int retval = BN_cmp(max, input);
|
||||
|
||||
BN_CTX_free(context);
|
||||
|
||||
// is max > privateKey?
|
||||
return retval == 1;
|
||||
}
|
||||
|
@ -23,84 +23,82 @@
|
||||
#ifndef UMSKT_PIDGEN3_H
|
||||
#define UMSKT_PIDGEN3_H
|
||||
|
||||
#include "../pidgen.h"
|
||||
#include "../libumskt.h"
|
||||
|
||||
class BINK1998;
|
||||
class BINK2002;
|
||||
|
||||
class EXPORT PIDGEN3 : public PIDGEN
|
||||
class EXPORT PIDGEN3
|
||||
{
|
||||
friend class BINK1998;
|
||||
friend class BINK2002;
|
||||
|
||||
protected:
|
||||
Integer BINKID;
|
||||
Integer privateKey, genOrder;
|
||||
ECP::Point pubPoint, genPoint;
|
||||
ECP eCurve;
|
||||
BIGNUM *privateKey, *genOrder;
|
||||
EC_POINT *genPoint, *pubPoint;
|
||||
EC_GROUP *eCurve;
|
||||
|
||||
public:
|
||||
PIDGEN3() = default;
|
||||
PIDGEN3()
|
||||
{
|
||||
}
|
||||
|
||||
PIDGEN3(PIDGEN3 &p3)
|
||||
{
|
||||
privateKey = p3.privateKey;
|
||||
genOrder = p3.genOrder;
|
||||
pubPoint = p3.pubPoint;
|
||||
genPoint = p3.genPoint;
|
||||
pubPoint = p3.pubPoint;
|
||||
eCurve = p3.eCurve;
|
||||
}
|
||||
|
||||
virtual ~PIDGEN3()
|
||||
{
|
||||
EC_GROUP_free(eCurve);
|
||||
EC_POINT_free(genPoint);
|
||||
EC_POINT_free(pubPoint);
|
||||
BN_free(genOrder);
|
||||
BN_free(privateKey);
|
||||
}
|
||||
|
||||
struct KeyInfo
|
||||
{
|
||||
Integer Serial = 0, AuthInfo = 0, ChannelID = 0, Hash = 0, Signature = 0, Rand = 0;
|
||||
BOOL isUpgrade = false, isOEM = false;
|
||||
DWORD Serial = 0, AuthInfo = 0, ChannelID = 0, Hash = 0;
|
||||
QWORD Signature = 0;
|
||||
BOOL isUpgrade = false;
|
||||
|
||||
void setSerial(DWORD32 SerialIn)
|
||||
void setSerial(DWORD serialIn)
|
||||
{
|
||||
Serial = IntegerN(SerialIn);
|
||||
Serial = serialIn;
|
||||
}
|
||||
|
||||
void setAuthInfo(DWORD32 AuthInfoIn)
|
||||
void setAuthInfo(DWORD AuthInfoIn)
|
||||
{
|
||||
AuthInfo = IntegerN(AuthInfoIn);
|
||||
AuthInfo = AuthInfoIn;
|
||||
}
|
||||
|
||||
void setChannelID(DWORD32 ChannelIDIn)
|
||||
void setChannelID(DWORD ChannelIDIn)
|
||||
{
|
||||
ChannelID = IntegerN(ChannelIDIn);
|
||||
ChannelID = ChannelIDIn;
|
||||
}
|
||||
} info;
|
||||
|
||||
static const std::string pKeyCharset;
|
||||
static const DWORD32 MaxSizeBINK1998;
|
||||
static constexpr char pKeyCharset[] = "BCDFGHJKMPQRTVWXY2346789";
|
||||
|
||||
BOOL LoadEllipticCurve(const std::string &BinkIDSel, const std::string &pSel, const std::string &aSel,
|
||||
const std::string &bSel, const std::string &generatorXSel, const std::string &generatorYSel,
|
||||
const std::string &publicKeyXSel, const std::string &publicKeyYSel,
|
||||
const std::string &genOrderSel, const std::string &privateKeySel);
|
||||
BOOL LoadEllipticCurve(std::string pSel, std::string aSel, std::string bSel, std::string generatorXSel,
|
||||
std::string generatorYSel, std::string publicKeyXSel, std::string publicKeyYSel,
|
||||
std::string genOrderSel, std::string privateKeySel);
|
||||
|
||||
BOOL Generate(std::string &pKey) override;
|
||||
BOOL Validate(const std::string &pKey) override;
|
||||
std::string StringifyKey(const std::string &pKey) override;
|
||||
std::string StringifyProductID() override;
|
||||
|
||||
virtual Integer Pack(const KeyInfo &ki) = 0;
|
||||
virtual KeyInfo Unpack(const Integer &raw) = 0;
|
||||
virtual BOOL Pack(QWORD *pRaw) = 0;
|
||||
virtual BOOL Unpack(QWORD *pRaw) = 0;
|
||||
virtual BOOL Generate(std::string &pKey);
|
||||
virtual BOOL Validate(std::string &pKey);
|
||||
|
||||
// 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);
|
||||
|
||||
void base24(std::string &cdKey, BYTE *byteSeq);
|
||||
void unbase24(BYTE *byteSeq, std::string cdKey);
|
||||
BOOL checkFieldIsBink1998();
|
||||
static BOOL checkFieldStrIsBink1998(std::string keyin);
|
||||
};
|
||||
|
||||
#endif // UMSKT_PIDGEN3_H
|
||||
|
@ -28,21 +28,16 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <intrin.h>
|
||||
#include <windows.h>
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
|
||||
#endif // defined(_MSC_VER)
|
||||
#endif // defined(WIN32)
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdbool>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if defined(DEBUG) || 1
|
||||
#ifdef DEBUG
|
||||
#include <cassert>
|
||||
#else
|
||||
#define assert(x) /* do nothing */
|
||||
@ -81,28 +76,45 @@
|
||||
#define INLINE FNINLINE
|
||||
#endif // ifdef __EMSCRIPTEN__
|
||||
|
||||
// POSIX <-> Windows compatability layer, because MS just *had* to be different
|
||||
#ifdef _MSC_VER
|
||||
#ifndef _sscanf
|
||||
#define _sscanf sscanf_s
|
||||
#endif
|
||||
#ifndef _strncpy
|
||||
#define _strncpy strncpy_s
|
||||
#endif
|
||||
#ifndef _strcpy
|
||||
#define _strcpy strcpy_s
|
||||
#endif
|
||||
#else
|
||||
#define _sscanf sscanf
|
||||
#define _strncpy(x, y, z, w) strncpy(x, z, w)
|
||||
#define _strcpy strcpy
|
||||
#endif // ifdef _MSC_VER
|
||||
|
||||
// Type definitions now with more windows compatability (unfortunately)
|
||||
using BOOL = int32_t;
|
||||
using BYTE = uint8_t;
|
||||
using WORD = uint16_t;
|
||||
using DWORD = unsigned long;
|
||||
using DWORD32 = uint32_t;
|
||||
using QWORD = uint64_t;
|
||||
|
||||
#if defined(_M_ARM) // for Windows on ARM ??
|
||||
using __m128 = __n128;
|
||||
#endif
|
||||
|
||||
#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 <intrin.h>
|
||||
#else // use the intel-supplied __m128 intrisic
|
||||
using uint128_t = __m128;
|
||||
#endif
|
||||
|
||||
using OWORD = uint128_t;
|
||||
|
||||
typedef union {
|
||||
OWORD oword;
|
||||
QWORD qword[2];
|
||||
DWORD32 dword32[4];
|
||||
DWORD dword[4];
|
||||
WORD word[8];
|
||||
BYTE byte[16];
|
||||
} Q_OWORD;
|
||||
|
Loading…
Reference in New Issue
Block a user