mirror of
https://github.com/Neo-Desktop/WindowsXPKg
synced 2024-11-21 21:31:01 +02:00
Merge 7a0faa42e8
into d378c6499e
This commit is contained in:
commit
b89a473712
220
.clang-format
Normal file
220
.clang-format
Normal file
@ -0,0 +1,220 @@
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: Microsoft
|
||||
AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignArrayOfStructures: None
|
||||
AlignConsecutiveAssignments:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
PadOperators: true
|
||||
AlignConsecutiveBitFields:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveDeclarations:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveMacros:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
PadOperators: false
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: Align
|
||||
AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortEnumsOnASingleLine: false
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: MultiLine
|
||||
AttributeMacros:
|
||||
- __capability
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: true
|
||||
AfterControlStatement: Always
|
||||
AfterEnum: true
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterObjCDeclaration: true
|
||||
AfterStruct: true
|
||||
AfterUnion: false
|
||||
AfterExternBlock: true
|
||||
BeforeCatch: true
|
||||
BeforeElse: true
|
||||
BeforeLambdaBody: false
|
||||
BeforeWhile: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeConceptDeclarations: Always
|
||||
BreakBeforeBraces: Custom
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 120
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
QualifierAlignment: Leave
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DeriveLineEnding: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
EmptyLineAfterAccessModifier: Never
|
||||
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
PackConstructorInitializers: BinPack
|
||||
BasedOnStyle: ''
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
AllowAllConstructorInitializersOnNextLine: true
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IfMacros:
|
||||
- KJ_IF_MAYBE
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||
Priority: 2
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||
Priority: 3
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IncludeIsMainSourceRegex: ''
|
||||
IndentAccessModifiers: false
|
||||
IndentCaseLabels: false
|
||||
IndentCaseBlocks: false
|
||||
IndentGotoLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentExternBlock: AfterExternBlock
|
||||
IndentRequiresClause: true
|
||||
IndentWidth: 4
|
||||
IndentWrappedFunctionNames: false
|
||||
InsertBraces: false
|
||||
InsertTrailingCommas: None
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
LambdaBodyIndentation: Signature
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBinPackProtocolList: Auto
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCBreakBeforeNestedBlockParam: true
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakOpenParenthesis: 0
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 1000
|
||||
PenaltyIndentedWhitespace: 0
|
||||
PointerAlignment: Right
|
||||
PPIndentWidth: -1
|
||||
ReferenceAlignment: Pointer
|
||||
ReflowComments: true
|
||||
RemoveBracesLLVM: false
|
||||
RequiresClausePosition: OwnLine
|
||||
SeparateDefinitionBlocks: Leave
|
||||
ShortNamespaceLines: 1
|
||||
SortIncludes: CaseSensitive
|
||||
SortJavaStaticImport: Before
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCaseColon: false
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeParensOptions:
|
||||
AfterControlStatements: true
|
||||
AfterForeachMacros: true
|
||||
AfterFunctionDefinitionName: false
|
||||
AfterFunctionDeclarationName: false
|
||||
AfterIfMacros: true
|
||||
AfterOverloadedOperator: false
|
||||
AfterRequiresInClause: false
|
||||
AfterRequiresInExpression: false
|
||||
BeforeNonEmptyParentheses: false
|
||||
SpaceAroundPointerQualifiers: Default
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyBlock: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: Never
|
||||
SpacesInConditionalStatement: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInLineCommentPrefix:
|
||||
Minimum: 1
|
||||
Maximum: -1
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
SpaceBeforeSquareBrackets: false
|
||||
BitFieldColonSpacing: Both
|
||||
Standard: Latest
|
||||
StatementAttributeLikeMacros:
|
||||
- Q_EMIT
|
||||
StatementMacros:
|
||||
- Q_UNUSED
|
||||
- QT_REQUIRE_VERSION
|
||||
TabWidth: 4
|
||||
UseCRLF: false
|
||||
UseTab: Never
|
||||
WhitespaceSensitiveMacros:
|
||||
- STRINGIZE
|
||||
- PP_STRINGIZE
|
||||
- BOOST_PP_STRINGIZE
|
||||
- NS_SWIFT_NAME
|
||||
- CF_SWIFT_NAME
|
||||
...
|
||||
---
|
||||
Language: Json
|
||||
BasedOnStyle: LLVM
|
||||
...
|
||||
|
@ -1,3 +1,2 @@
|
||||
build*/
|
||||
cmake-build*/
|
||||
djgpp-build*/
|
||||
cmake*/
|
||||
|
127
.github/workflows/dos-djgpp.yml
vendored
127
.github/workflows/dos-djgpp.yml
vendored
@ -1,6 +1,6 @@
|
||||
# This file is a part of the UMSKT Project
|
||||
#
|
||||
# Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
# 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
|
||||
@ -24,20 +24,33 @@ on:
|
||||
push:
|
||||
branches: [ "*" ]
|
||||
paths-ignore: [ '**.md', 'doc/**', '.idea/**' ] # If only these files are edited, skip
|
||||
pull_request:
|
||||
branches: [ "*" ]
|
||||
paths-ignore: [ '**.md', 'doc/**', '.idea/**' ] # If only these files are edited, skip
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
CC: ${{ github.workspace }}/djgpp/bin/i586-pc-msdosdjgpp-gcc
|
||||
CXX: ${{ github.workspace }}/djgpp/bin/i586-pc-msdosdjgpp-g++
|
||||
CMAKE_FIND_ROOT_PATH: ${{ github.workspace }}/djgpp
|
||||
WATT_ROOT: ${{ github.workspace }}/djgpp/watt32
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CC: ${{ github.workspace }}/djgpp/bin/i586-pc-msdosdjgpp-gcc
|
||||
CXX: ${{ github.workspace }}/djgpp/bin/i586-pc-msdosdjgpp-g++
|
||||
CMAKE_FIND_ROOT_PATH: ${{ github.workspace }}/djgpp
|
||||
GCC_EXEC_PREFIX: ${{ github.workspace }}/lib/gcc/
|
||||
DJDIR: ${{ github.workspace }}/djgpp/i586-pc-msdosdjgpp
|
||||
|
||||
steps:
|
||||
- name: Checkout Source Tree
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Set up CPM cache
|
||||
id: cache-cpm
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ github.workspace }}/.cpm-cache
|
||||
key: ${{ runner.os }}-cpm-${{ hashFiles('**/') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-cpm-
|
||||
|
||||
- name: Setup build environment
|
||||
run: |
|
||||
@ -46,34 +59,19 @@ jobs:
|
||||
|
||||
- name: Download and Setup DJGPP Toolchain
|
||||
run: |
|
||||
pushd ${{ github.workspace }}
|
||||
wget https://github.com/andrewwutw/build-djgpp/releases/download/v3.4/djgpp-linux64-gcc1220.tar.bz2
|
||||
tar xjf djgpp-linux64-gcc1220.tar.bz2
|
||||
cd ${{ github.workspace }}/djgpp
|
||||
git clone https://github.com/UMSKT/Watt-32.git watt32
|
||||
cd watt32/util
|
||||
make clean && make linux
|
||||
cd ../src
|
||||
source ${{ github.workspace }}/djgpp/setenv
|
||||
./configur.sh djgpp
|
||||
make -f djgpp.mak
|
||||
ln -s ${WATT_ROOT}/lib/libwatt.a ${{ github.workspace }}/djgpp/lib
|
||||
echo "${{ github.workspace }}/djgpp/i586-pc-msdosdjgpp/bin/" >> "$GITHUB_ENV"
|
||||
echo "${{ github.workspace }}/djgpp/bin/" >> "$GITHUB_ENV"
|
||||
|
||||
- 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
|
||||
pushd build
|
||||
cmake ../ -D DJGPP_WATT32=${WATT_ROOT}/lib/libwatt.a -D CMAKE_FIND_ROOT_PATH=${CMAKE_FIND_ROOT_PATH}
|
||||
make
|
||||
- name: Build UMSKT for MSDOS via DJGPP
|
||||
uses: threeal/cmake-action@v1.3.0
|
||||
with:
|
||||
options: UMSKT_DJGPP_COMPILE=On CMAKE_FIND_ROOT_PATH=${CMAKE_FIND_ROOT_PATH} CMAKE_BUILD_TYPE=Release
|
||||
build-dir: build
|
||||
c-compiler: ${CC}
|
||||
cxx-compiler: ${CXX}
|
||||
build-args: -j 2
|
||||
|
||||
- name: Move executable to upload directory
|
||||
run: |
|
||||
@ -81,7 +79,68 @@ jobs:
|
||||
mv build/umskt.exe build/actions_upload/
|
||||
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
uses: actions/upload-artifact@v4.3.1
|
||||
with:
|
||||
name: UMSKT-DOS
|
||||
path: build/actions_upload
|
||||
|
||||
build-dos-win32-combined:
|
||||
needs: build
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
# https://github.com/actions/runner-images/issues/6067#issuecomment-1213069040
|
||||
- name: Install Windows XP Support for Visual Studio
|
||||
run: |
|
||||
Set-Location "C:\Program Files (x86)\Microsoft Visual Studio\Installer\"
|
||||
$InstallPath = "C:\Program Files\Microsoft Visual Studio\2022\Enterprise"
|
||||
$componentsToAdd = @(
|
||||
"Microsoft.VisualStudio.Component.WinXP"
|
||||
)
|
||||
[string]$workloadArgs = $componentsToAdd | ForEach-Object {" --add " + $_}
|
||||
$Arguments = ('/c', "vs_installer.exe", 'modify', '--installPath', "`"$InstallPath`"",$workloadArgs, '--quiet', '--norestart', '--nocache')
|
||||
$process = Start-Process -FilePath cmd.exe -ArgumentList $Arguments -Wait -PassThru -WindowStyle Hidden
|
||||
if ($process.ExitCode -eq 0)
|
||||
{
|
||||
Write-Host "components have been successfully added"
|
||||
Get-ChildItem C:\ProgramData\Microsoft\VisualStudio\Packages\Microsoft.Windows.XPSupport.*
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Host "components were not installed"
|
||||
exit 1
|
||||
}
|
||||
|
||||
- name: Setup MSBuild
|
||||
uses: microsoft/setup-msbuild@v1
|
||||
|
||||
- name: Checkout Source Tree
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Set up CPM cache
|
||||
id: cache-cpm
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ github.workspace }}/.cpm-cache
|
||||
key: ${{ runner.os }}-cpm-${{ hashFiles('**/') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-cpm-
|
||||
|
||||
- name: Download Built DOS Artifact
|
||||
uses: actions/download-artifact@v4.1.2
|
||||
with:
|
||||
name: UMSKT-DOS
|
||||
|
||||
- name: Configure and build UMSKT for DOS+Win32
|
||||
uses: threeal/cmake-action@v1.3.0
|
||||
with:
|
||||
options: UMSKT_MSVC_WINXP=On CMAKE_BUILD_TYPE=Release CPM_SOURCE_CACHE=${{ github.workspace }}/.cpm-cache UMSKT_MSVC_MSDOS_STUB=${{ github.workspace }}/umskt.exe
|
||||
generator: "Visual Studio 17 2022"
|
||||
args: -A "Win32" -T "v141_xp"
|
||||
run-build: true
|
||||
build-args: "--config Release -j 2"
|
||||
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4.3.1
|
||||
with:
|
||||
name: UMSKT-DOS+Win32
|
||||
path: build/Release
|
29
.github/workflows/freebsd.yml
vendored
29
.github/workflows/freebsd.yml
vendored
@ -1,6 +1,6 @@
|
||||
# This file is a part of the UMSKT Project
|
||||
#
|
||||
# Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
# 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
|
||||
@ -27,31 +27,40 @@
|
||||
pull_request:
|
||||
branches: [ "*" ]
|
||||
paths-ignore: [ '**.md', 'doc/**', '.idea/**' ] # If only these files are edited, skip
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
name: build-x86_64
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Checkout Source Tree
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Set up CPM cache
|
||||
id: cache-cpm
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ github.workspace }}/.cpm-cache
|
||||
key: ${{ runner.os }}-cpm-${{ hashFiles('**/') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-cpm-
|
||||
|
||||
- name: Build & Test in FreeBSD
|
||||
id: test
|
||||
uses: vmactions/freebsd-vm@v1
|
||||
uses: vmactions/freebsd-vm@v1.0.6
|
||||
with:
|
||||
envs: 'MYTOKEN MYTOKEN2'
|
||||
usesh: true
|
||||
prepare: |
|
||||
pkg install -y cmake openssl git bash
|
||||
pkg install -y cmake git bash
|
||||
|
||||
run: |
|
||||
mkdir build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCPM_SOURCE_CACHE= ${{ github.workspace }}/.cpm-cache -DBUILD_TESTING=On -B build/
|
||||
cmake --build build/ -j 2
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
./umskt # Execute the test here
|
||||
ctest
|
||||
|
||||
- name: Move files to correct directory
|
||||
run: |
|
||||
@ -59,7 +68,7 @@
|
||||
mv build/umskt build/actions_upload/umskt
|
||||
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
uses: actions/upload-artifact@v4.3.1
|
||||
with:
|
||||
name: UMSKT-FreeBSD
|
||||
path: build/actions_upload
|
||||
|
54
.github/workflows/linux.yml
vendored
54
.github/workflows/linux.yml
vendored
@ -1,6 +1,6 @@
|
||||
# This file is a part of the UMSKT Project
|
||||
#
|
||||
# Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
# 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
|
||||
@ -24,6 +24,9 @@ on:
|
||||
push:
|
||||
branches: [ "*" ]
|
||||
paths-ignore: [ '**.md', 'doc/**', '.idea/**' ] # If only these files are edited, skip
|
||||
pull_request:
|
||||
branches: [ "*" ]
|
||||
paths-ignore: [ '**.md', 'doc/**', '.idea/**' ] # If only these files are edited, skip
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@ -31,13 +34,27 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- arch: x86
|
||||
- arch: x86_64
|
||||
- arch: aarch64
|
||||
arch:
|
||||
- x86
|
||||
- x86_64
|
||||
- aarch64
|
||||
- armhf
|
||||
- armv7
|
||||
- ppc64le
|
||||
- s390x
|
||||
|
||||
steps:
|
||||
- name: Checkout Source Tree
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Set up CPM cache
|
||||
id: cache-cpm
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ github.workspace }}/.cpm-cache
|
||||
key: ${{ runner.os }}-cpm-${{ hashFiles('**/') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-cpm-
|
||||
|
||||
- name: Setup latest Alpine Linux for ${{ matrix.arch }}
|
||||
uses: jirutka/setup-alpine@v1
|
||||
@ -48,17 +65,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@7ef2eb8da6e5ec0a6de6b1ddc96987080bed06e8
|
||||
uses: threeal/cmake-action@v1.3.0
|
||||
with:
|
||||
options: MUSL_STATIC=ON
|
||||
options: UMSKT_MUSL_STATIC=ON CMAKE_BUILD_TYPE=Release CPM_SOURCE_CACHE=${{ github.workspace }}/.cpm-cache BUILD_TESTING=On
|
||||
run-build: true
|
||||
build-args: -j 2
|
||||
run-test: true
|
||||
shell: alpine-target.sh {0}
|
||||
|
||||
- name: Move files to correct directory
|
||||
@ -67,21 +83,7 @@ jobs:
|
||||
mv build/umskt build/actions_upload/umskt
|
||||
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
uses: actions/upload-artifact@v4.3.1
|
||||
with:
|
||||
name: UMSKT-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}
|
||||
|
45
.github/workflows/macos.yml
vendored
45
.github/workflows/macos.yml
vendored
@ -1,6 +1,6 @@
|
||||
# This file is a part of the UMSKT Project
|
||||
#
|
||||
# Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
# 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
|
||||
@ -24,37 +24,48 @@ on:
|
||||
push:
|
||||
branches: [ "*" ]
|
||||
paths-ignore: [ '**.md', 'doc/**', '.idea/**' ] # If only these files are edited, skip
|
||||
pull_request:
|
||||
branches: [ "*" ]
|
||||
paths-ignore: [ '**.md', 'doc/**', '.idea/**' ] # If only these files are edited, skip
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-x86:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- arch: x86_64
|
||||
arch:
|
||||
- { name: arm64;x86_64, displayName: all-x86_64_arm64 }
|
||||
- { name: x86_64, displayName: x86_64 }
|
||||
- { name: arm64, displayName: arm64 }
|
||||
steps:
|
||||
- name: Checkout Source Tree
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Configure and build UMSKT
|
||||
run: |
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release ..
|
||||
make
|
||||
- name: Set up CPM cache
|
||||
id: cache-cpm
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ github.workspace }}/.cpm-cache
|
||||
key: ${{ runner.os }}-cpm-${{ hashFiles('**/') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-cpm-
|
||||
|
||||
- name: Configure and build UMSKT for ${{ matrix.arch.displayName }}
|
||||
uses: threeal/cmake-action@v1.3.0
|
||||
with:
|
||||
options: MUSL_STATIC=ON CMAKE_BUILD_TYPE=Release CPM_SOURCE_CACHE=${{ github.workspace }}/.cpm-cache CMAKE_OSX_ARCHITECTURES="${{ matrix.arch.name }}" BUILD_TESTING=On
|
||||
run-build: true
|
||||
build-args: -j 2
|
||||
run-test: true
|
||||
|
||||
- name: Move files to correct directory
|
||||
run: |
|
||||
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
|
||||
uses: actions/upload-artifact@v4.3.1
|
||||
with:
|
||||
name: UMSKT-macOS-${{ matrix.arch }}
|
||||
name: UMSKT-macOS-${{ matrix.arch.displayName }}
|
||||
path: build/actions_upload
|
||||
|
110
.github/workflows/windows.yml
vendored
110
.github/workflows/windows.yml
vendored
@ -1,6 +1,6 @@
|
||||
# This file is a part of the UMSKT Project
|
||||
#
|
||||
# Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
# 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
|
||||
@ -24,11 +24,17 @@ on:
|
||||
push:
|
||||
branches: [ "*" ]
|
||||
paths-ignore: [ '**.md', 'doc/**', '.idea/**'] # If only these files are edited, skip
|
||||
pull_request:
|
||||
branches: [ "*" ]
|
||||
paths-ignore: [ '**.md', 'doc/**', '.idea/**'] # If only these files are edited, skip
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-32bit:
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [ { name: Win32, displayName: x86 }, { name: x64, displayName: x64 } ]
|
||||
steps:
|
||||
# https://github.com/actions/runner-images/issues/6067#issuecomment-1213069040
|
||||
- name: Install Windows XP Support for Visual Studio
|
||||
@ -52,97 +58,33 @@ 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: Set up CPM cache
|
||||
id: cache-cpm
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ github.workspace }}/.cpm-cache
|
||||
key: ${{ runner.os }}-cpm-${{ hashFiles('**/') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-cpm-
|
||||
|
||||
- name: Checkout Source Tree
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Setup MSBuild
|
||||
uses: microsoft/setup-msbuild@v1
|
||||
|
||||
- name: Configure UMSKT
|
||||
uses: threeal/cmake-action@v1.2.0
|
||||
- name: Configure and build UMSKT for ${{ matrix.values.displayName }}
|
||||
uses: threeal/cmake-action@v1.3.0
|
||||
with:
|
||||
options: CMAKE_BUILD_TYPE=Release CPM_SOURCE_CACHE=${{ github.workspace }}/.cpm-cache BUILD_TESTING=On
|
||||
generator: "Visual Studio 17 2022"
|
||||
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
|
||||
args: -A "${{ matrix.arch.name }}" -T "v141_xp"
|
||||
run-build: true
|
||||
build-args: --config Release -j 2
|
||||
run-test: true
|
||||
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
uses: actions/upload-artifact@v4.3.1
|
||||
with:
|
||||
name: UMSKT-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
|
||||
name: UMSKT-${{ matrix.values.displayName }}
|
||||
path: build/Release
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,8 +1,9 @@
|
||||
build/*
|
||||
build*/
|
||||
*.tar
|
||||
*.exe
|
||||
*.wasm
|
||||
umskt
|
||||
.cpm-cache
|
||||
|
||||
### NotepadPP template
|
||||
# Notepad++ backups #
|
||||
@ -66,7 +67,7 @@ umskt
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
cmake*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
2
.idea/UMSKT.iml
Normal file
2
.idea/UMSKT.iml
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module classpath="CMake" type="CPP_MODULE" version="4" />
|
90
.idea/editor.xml
Normal file
90
.idea/editor.xml
Normal file
@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="BackendCodeEditorSettings">
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppClangFormat/EnableClangFormatSupport/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/EditorConfig/EnableClangFormatSupport/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_BINARY_EXPRESSIONS_CHAIN/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_CALLS_CHAIN/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_EXPRESSION/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_FOR_STMT/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTIPLE_DECLARATION/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_TERNARY/@EntryValue" value="ALIGN_ALL" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_CLASS_DEFINITION/@EntryValue" value="1" type="int" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue" value="2" type="int" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/KEEP_BLANK_LINES_IN_CODE/@EntryValue" value="2" type="int" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/KEEP_USER_LINEBREAKS/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_CASE_FROM_SWITCH/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_COMMENT/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INT_ALIGN_EQ/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SIMPLE_BLOCK_STYLE/@EntryValue" value="DO_NOT_CHANGE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_COMMA_IN_TEMPLATE_ARGS/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_COMMA_IN_TEMPLATE_PARAMS/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_FOR_SEMICOLON/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_FOR_SEMICOLON/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_UNARY_OPERATOR/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_ARRAY_ACCESS_BRACKETS/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_CAST_EXPRESSION_PARENTHESES/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_INITIALIZER_BRACES/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_METHOD_PARENTHESES/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_INITIALIZER_BRACES/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPECIAL_ELSE_IF_TREATMENT/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_CAST_EXPRESSION_PARENTHESES/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_AFTER_BINARY_OPSIGN/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_TERNARY_OPSIGNS/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/TYPE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/OTHER_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/CASE_BLOCK_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_FUNCTION_DECLARATION/@EntryValue" value="1" type="int" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_FUNCTION_DEFINITION/@EntryValue" value="1" type="int" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_WHILE_ON_NEW_LINE/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_ELSE_ON_NEW_LINE/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_CATCH_ON_NEW_LINE/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/NAMESPACE_INDENTATION/@EntryValue" value="All" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_ARGUMENT/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_EXTENDS_LIST/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_PARAMETER/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_TYPE_ARGUMENT/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_TYPE_PARAMETER/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_DECLARATIONS/@EntryValue" value="0" type="int" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_ACCESS_SPECIFIERS_FROM_CLASS/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_CLASS_MEMBERS_FROM_ACCESS_SPECIFIERS/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/LINE_BREAK_AFTER_COLON_IN_MEMBER_INITIALIZER_LISTS/@EntryValue" value="ON_SINGLE_LINE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/MEMBER_INITIALIZER_LIST_STYLE/@EntryValue" value="DO_NOT_CHANGE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_NAMESPACE_DEFINITIONS_ON_SAME_LINE/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_COLON_IN_BITFIELD_DECLARATOR/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_COLON_IN_BITFIELD_DECLARATOR/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_EXTENDS_COLON/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_EXTENDS_COLON/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_FOR_COLON/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_FOR_COLON/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_REF_IN_DATA_MEMBERS/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_REF_IN_DATA_MEMBERS/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_PTR_IN_ABSTRACT_DECL/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_REF_IN_ABSTRACT_DECL/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_TEMPLATE_ARGS/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_TEMPLATE_PARAMS/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_DECLARATION_PARENTHESES/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_BLOCKS/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_TEMPLATE_PARAMS/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_TEMPLATE_ARGS/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_TEMPLATE_PARAMS/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_INVOCATION_LPAR/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_AFTER_INVOCATION_LPAR/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_INVOCATION_RPAR/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_DECLARATION_LPAR/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_AFTER_DECLARATION_LPAR/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_DECLARATION_RPAR/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_ARGUMENTS_STYLE/@EntryValue" value="WRAP_IF_LONG" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_PARAMETERS_STYLE/@EntryValue" value="WRAP_IF_LONG" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BREAK_TEMPLATE_DECLARATION/@EntryValue" value="LINE_BREAK" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/NAMESPACE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/FREE_BLOCK_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INVOCABLE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ANONYMOUS_METHOD_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INITIALIZER_BRACES/@EntryValue" value="END_OF_LINE_NO_SPACE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_STYLE/@EntryValue" value="Space" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_SIZE/@EntryValue" value="4" type="int" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/CONTINUOUS_LINE_INDENT/@EntryValue" value="Double" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/TAB_WIDTH/@EntryValue" value="4" type="int" />
|
||||
</component>
|
||||
</project>
|
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/UMSKT.iml" filepath="$PROJECT_DIR$/.idea/UMSKT.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
14
.idea/remote-targets.xml
Normal file
14
.idea/remote-targets.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RemoteTargetsManager">
|
||||
<option name="projectDefaultTargetUuid" value="36c80fd0-a122-4ef6-8ee6-1bd41007a378" />
|
||||
<targets>
|
||||
<target name="WSL - Ubuntu" type="wsl" uuid="36c80fd0-a122-4ef6-8ee6-1bd41007a378">
|
||||
<config>
|
||||
<option name="distributionMsId" value="Ubuntu" />
|
||||
<option name="projectRootOnTarget" value="{exitCode=0, timeout=false, cancelled=false, stdout=/mnt/c/Users/neo/AppData/Local/Programs/CLion/jbr/bin , stderr=}/UMSKT" />
|
||||
</config>
|
||||
</target>
|
||||
</targets>
|
||||
</component>
|
||||
</project>
|
30
.pre-commit-config.yaml
Normal file
30
.pre-commit-config.yaml
Normal file
@ -0,0 +1,30 @@
|
||||
# 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 01/03/2024
|
||||
# @Maintainer Neo
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
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
|
293
CMakeLists.txt
293
CMakeLists.txt
@ -1,6 +1,6 @@
|
||||
# This file is a part of the UMSKT Project
|
||||
#
|
||||
# Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
# 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
|
||||
@ -19,142 +19,100 @@
|
||||
# @Maintainer Neo
|
||||
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 3.12)
|
||||
PROJECT(UMSKT)
|
||||
SET(PROJECT_NAME UMSKT)
|
||||
PROJECT(${PROJECT_NAME})
|
||||
|
||||
SET(CMAKE_CXX_STANDARD 17)
|
||||
SET(CMAKE_OSX_SYSROOT "macosx" CACHE PATH "macOS SDK path")
|
||||
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
OPTION(BUILD_SHARED_LIBS "Build internal libraries as dynamic" 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/OpenSSL" OFF)
|
||||
OPTION(MSVC_MSDOS_STUB "Specify a custom MS-DOS stub for a 32-bit MSVC compilation" OFF)
|
||||
OPTION(BUILD_TESTING "Build testing binaries for CTest" OFF)
|
||||
OPTION(BUILD_SHARED_LIBS "Build all dependant libraries as shared" OFF)
|
||||
|
||||
SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS})
|
||||
SET(UMSKT_LINK_DIRS ${UMSKT_LINK_DIRS})
|
||||
OPTION(UMSKT_BUILD_SHARED_LIB "Build libumskt.so/.dll/.dylib" ON)
|
||||
OPTION(UMSKT_BUILD_STATIC_LIB "Build libumskt_static.a/.lib" ON)
|
||||
OPTION(UMSKT_MUSL_STATIC "Enable fully static builds in a muslc environment (i.e. Alpine Linux)" OFF)
|
||||
OPTION(UMSKT_DJGPP_COMPILE "Enable compilation and linking with DJGPP" OFF)
|
||||
OPTION(UMSKT_MSVC_MSDOS_STUB "Specify a custom MS-DOS stub for a 32-bit MSVC compilation" OFF)
|
||||
OPTION(UMSKT_MSVC_WINXP "Specify compile-time flag overrides for Windows XP" OFF)
|
||||
|
||||
# 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")
|
||||
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 -pedantic")
|
||||
SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-g")
|
||||
SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT "-Wall -Wextra -pedantic")
|
||||
SET(CMAKE_CXX_FLAGS_RELEASE_INIT "-s")
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static -Wl,--gc-sections")
|
||||
ENDIF ()
|
||||
|
||||
|
||||
|
||||
IF(DJGPP_WATT32)
|
||||
IF (UMSKT_DJGPP_COMPILE)
|
||||
SET(CMAKE_SYSTEM_NAME MSDOS)
|
||||
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
SET(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
||||
SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS} ${DJGPP_WATT32})
|
||||
SET(UMSKT_LINK_DIRS ${UMSKT_LINK_DIRS} ${WATT_ROOT}/lib)
|
||||
SET(UMSKT_BUILD_SHARED_LIB OFF)
|
||||
SET(UMSKT_BUILD_STATIC_LIB OFF)
|
||||
SET(CMAKE_POSITION_INDEPENDENT_CODE OFF)
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-attributes -march=i386 -mtune=i386")
|
||||
MESSAGE(STATUS "[UMSKT] DJGPP Build requested - Shared libraries forced OFF")
|
||||
ENDIF ()
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
IF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
SET(BUILD_SHARED_LIBS ON)
|
||||
MESSAGE(STATUS "[UMSKT] macOS has no static library - Shared library forced on")
|
||||
endif()
|
||||
MESSAGE(STATUS "[UMSKT] macOS Build requested - Shared libraries forced ON")
|
||||
ENDIF ()
|
||||
|
||||
# if we're compiling with MSVC, respect the DEBUG compile option
|
||||
IF (MSVC)
|
||||
SET(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
IF(NOT BUILD_SHARED_LIBS)
|
||||
SET(CMAKE_CXX_FLAGS "/Os /LTCG /Gy /OPT:REF /OPT:ICF /EHsc")
|
||||
SET(CMAKE_CXX_FLAGS_RELEASE "/MT")
|
||||
SET(CMAKE_CXX_FLAGS_DEBUG "/MTd")
|
||||
ELSE()
|
||||
SET(CMAKE_CXX_FLAGS_RELEASE "/MD")
|
||||
SET(CMAKE_CXX_FLAGS_DEBUG "/MDd")
|
||||
ENDIF()
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "/INCREMENTAL:NO /NODEFAULTLIB:MSVCRT")
|
||||
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_ENABLE_EXPORTS ON)
|
||||
SET(UMSKT_EXE_WINDOWS_EXTRA src/windows/umskt.rc)
|
||||
SET(UMSKT_EXE_WINDOWS_DLL src/windows/dllmain.cpp)
|
||||
IF (UMSKT_MSVC_WINXP)
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /U_WIN32_WINNT /DWINVER=0x0501 /D_WIN32_WINNT=0x0501")
|
||||
ENDIF ()
|
||||
ENDIF ()
|
||||
|
||||
IF (MUSL_STATIC)
|
||||
MESSAGE(STATUS "[UMSKT] Performing a fully static build using muslc")
|
||||
MESSAGE(STATUS "[UMSKT] musl libc Build requested - Shared librares forced OFF")
|
||||
SET(BUILD_SHARED_LIBS OFF)
|
||||
SET(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-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()
|
||||
|
||||
# initalize cpm.CMake
|
||||
INCLUDE(cmake/CPM.cmake)
|
||||
# initialize cpm.CMake
|
||||
FILE(
|
||||
DOWNLOAD
|
||||
https://github.com/cpm-cmake/CPM.cmake/releases/download/v0.38.3/CPM.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake
|
||||
EXPECTED_HASH SHA256=cc155ce02e7945e7b8967ddfaff0b050e958a723ef7aad3766d368940cb15494
|
||||
)
|
||||
INCLUDE(${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake)
|
||||
CPMUsePackageLock(${CMAKE_SOURCE_DIR}/package-lock.cmake)
|
||||
|
||||
# fetch cpm.CMake dependencies
|
||||
# Include JSON development library
|
||||
CPMAddPackage(
|
||||
NAME nlohmann_json
|
||||
GITHUB_REPOSITORY nlohmann/json
|
||||
VERSION 3.11.2
|
||||
VERSION 3.11.3
|
||||
)
|
||||
|
||||
# Include fmt development library
|
||||
CPMAddPackage(
|
||||
NAME fmt
|
||||
GITHUB_REPOSITORY fmtlib/fmt
|
||||
GIT_TAG 10.0.0
|
||||
VERSION 10.0.0
|
||||
GIT_TAG 10.2.0
|
||||
VERSION 10.2.0
|
||||
)
|
||||
|
||||
# Include cmrc resource compiler
|
||||
@ -165,59 +123,142 @@ CPMAddPackage(
|
||||
VERSION 2.0.1
|
||||
)
|
||||
|
||||
# Include Crypto++ development library
|
||||
#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 stdint128
|
||||
CPMAddPackage(
|
||||
NAME stdint128
|
||||
GITHUB_REPOSITORY UMSKT/stdint128-cmake
|
||||
GIT_TAG v1.0.0
|
||||
VERSION 1.0.0
|
||||
)
|
||||
|
||||
#include googletest unit testing library
|
||||
#CPMAddPackage(
|
||||
# NAME googletest
|
||||
# GITHUB_REPOSITORY google/googletest
|
||||
# VERSION 1.13.0
|
||||
# OPTIONS "INSTALL_GTEST OFF" "gtest_force_shared_crt"
|
||||
#)
|
||||
# Include Crypto++ development library
|
||||
CPMAddPackage(
|
||||
NAME cryptopp-cmake
|
||||
GITHUB_REPOSITORY UMSKT/cryptopp-cmake
|
||||
GIT_TAG CRYPTOPP_8_9_0
|
||||
VERSION 8.9.0
|
||||
OPTIONS "CRYPTOPP_BUILD_TESTING OFF"
|
||||
)
|
||||
|
||||
### Resource compilation
|
||||
CMRC_ADD_RESOURCE_LIBRARY(umskt-rc ALIAS umskt::rc NAMESPACE umskt keys.json)
|
||||
|
||||
SET(LIBUMSKT_SRC src/libumskt/libumskt.cpp src/libumskt/pidgen3/BINK1998.cpp src/libumskt/pidgen3/BINK2002.cpp src/libumskt/pidgen3/key.cpp src/libumskt/pidgen3/util.cpp src/libumskt/confid/confid.cpp src/libumskt/pidgen2/PIDGEN2.cpp src/libumskt/debugoutput.cpp)
|
||||
SET(LIBUMSKT_PIDGEN2 src/libumskt/pidgen2/PIDGEN2.cpp)
|
||||
SET(LIBUMSKT_PIDGEN3 src/libumskt/pidgen3/PIDGEN3.cpp src/libumskt/pidgen3/BINK1998.cpp src/libumskt/pidgen3/BINK2002.cpp)
|
||||
SET(LIBUMSKT_CONFID src/libumskt/confid/confid.cpp src/libumskt/confid/polynomial.cpp src/libumskt/confid/residue.cpp src/libumskt/confid/divisor.cpp)
|
||||
SET(LIBUMSKT_SRC src/libumskt/libumskt.cpp src/libumskt/init.cpp src/libumskt/pidgen.cpp ${LIBUMSKT_PIDGEN2} ${LIBUMSKT_PIDGEN3} ${LIBUMSKT_CONFID})
|
||||
SET(UMSKT_CLI_SRC src/main.cpp src/cli/help.cpp src/cli/cli.cpp src/cli/confirmationid.cpp src/cli/options.cpp src/cli/pidgen.cpp)
|
||||
SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS} fmt cryptopp stdint128)
|
||||
|
||||
IF (NOT MSVC)
|
||||
SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS} umskt::rc)
|
||||
SET(UMSKT_CLI_SRC ${UMSKT_CLI_SRC} src/rc.cpp)
|
||||
ELSE()
|
||||
SET(UMSKT_CLI_SRC ${UMSKT_CLI_SRC} src/windows/platform.cpp)
|
||||
ENDIF()
|
||||
|
||||
#### Separate Build Path for emscripten
|
||||
IF (EMSCRIPTEN)
|
||||
ADD_EXECUTABLE(umskt ${LIBUMSKT_SRC})
|
||||
TARGET_INCLUDE_DIRECTORIES(umskt PUBLIC ${OPENSSL_INCLUDE_DIR})
|
||||
TARGET_LINK_LIBRARIES(umskt -static OpenSSL::Crypto cryptopp::cryptopp fmt)
|
||||
ADD_EXECUTABLE(umskt ${UMSKT_CLI_SRC} ${LIBUMSKT_SRC})
|
||||
TARGET_INCLUDE_DIRECTORIES(umskt PUBLIC ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR})
|
||||
TARGET_LINK_DIRECTORIES(umskt PUBLIC ${UMSKT_LINK_DIRS})
|
||||
TARGET_LINK_LIBRARIES(umskt PUBLIC ${UMSKT_LINK_LIBS})
|
||||
SET(CMAKE_EXECUTABLE_SUFFIX ".html")
|
||||
|
||||
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")
|
||||
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")
|
||||
ELSE ()
|
||||
ADD_LIBRARY(_umskt ${LIBUMSKT_SRC} ${UMSKT_EXE_WINDOWS_EXTRA} ${UMSKT_EXE_WINDOWS_DLL})
|
||||
TARGET_INCLUDE_DIRECTORIES(_umskt PUBLIC ${OPENSSL_INCLUDE_DIR})
|
||||
TARGET_LINK_DIRECTORIES(_umskt PUBLIC ${UMSKT_LINK_DIRS})
|
||||
TARGET_LINK_LIBRARIES(_umskt ${OPENSSL_CRYPTO_LIBRARIES} fmt ${UMSKT_LINK_LIBS})
|
||||
## umskt.so/.dll creation
|
||||
IF (UMSKT_BUILD_SHARED_LIB)
|
||||
ADD_LIBRARY(libumskt SHARED ${LIBUMSKT_SRC} ${UMSKT_EXE_WINDOWS_EXTRA} ${UMSKT_EXE_WINDOWS_DLL})
|
||||
TARGET_INCLUDE_DIRECTORIES(libumskt PUBLIC ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR})
|
||||
TARGET_LINK_DIRECTORIES(libumskt PUBLIC ${UMSKT_LINK_DIRS})
|
||||
TARGET_LINK_LIBRARIES(libumskt PUBLIC ${UMSKT_LINK_LIBS})
|
||||
IF (MSVC)
|
||||
SET_TARGET_PROPERTIES(libumskt PROPERTIES OUTPUT_NAME libumskt)
|
||||
ELSE ()
|
||||
SET_TARGET_PROPERTIES(libumskt PROPERTIES OUTPUT_NAME umskt)
|
||||
ENDIF ()
|
||||
ENDIF()
|
||||
|
||||
## umskt_static.a/.lib creation
|
||||
IF (UMSKT_BUILD_STATIC_LIB)
|
||||
ADD_LIBRARY(libumskt_static ${LIBUMSKT_SRC} ${UMSKT_EXE_WINDOWS_EXTRA} ${UMSKT_EXE_WINDOWS_DLL})
|
||||
TARGET_INCLUDE_DIRECTORIES(libumskt_static PUBLIC ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR})
|
||||
TARGET_LINK_DIRECTORIES(libumskt_static PUBLIC ${UMSKT_LINK_DIRS})
|
||||
TARGET_LINK_LIBRARIES(libumskt_static PUBLIC ${UMSKT_LINK_LIBS})
|
||||
IF (MSVC)
|
||||
SET_TARGET_PROPERTIES(libumskt_static PROPERTIES OUTPUT_NAME libumskt_static)
|
||||
ELSE ()
|
||||
SET_TARGET_PROPERTIES(libumskt_static PROPERTIES OUTPUT_NAME umskt_static)
|
||||
ENDIF ()
|
||||
ENDIF()
|
||||
|
||||
### UMSKT executable compilation
|
||||
ADD_EXECUTABLE(umskt src/main.cpp src/cli.cpp ${UMSKT_EXE_WINDOWS_EXTRA})
|
||||
TARGET_INCLUDE_DIRECTORIES(umskt PUBLIC ${OPENSSL_INCLUDE_DIR})
|
||||
TARGET_LINK_LIBRARIES(umskt _umskt ${OPENSSL_CRYPTO_LIBRARIES} ${ZLIB_LIBRARIES} fmt nlohmann_json::nlohmann_json umskt::rc ${UMSKT_LINK_LIBS})
|
||||
IF (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
|
||||
### only build CLI if we're building UMSKT explicitly
|
||||
IF (UMSKT_BUILD_STATIC_LIB)
|
||||
## Link against the static lib
|
||||
ADD_EXECUTABLE(umskt ${UMSKT_CLI_SRC} ${UMSKT_EXE_WINDOWS_EXTRA})
|
||||
TARGET_LINK_LIBRARIES(umskt PUBLIC libumskt_static ${UMSKT_LINK_LIBS} nlohmann_json::nlohmann_json)
|
||||
ELSEIF (UMSKT_BUILD_SHARED_LIB)
|
||||
## Link against the dynamic lib
|
||||
ADD_EXECUTABLE(umskt ${UMSKT_CLI_SRC} ${UMSKT_EXE_WINDOWS_EXTRA})
|
||||
TARGET_LINK_LIBRARIES(umskt PUBLIC libumskt ${UMSKT_LINK_LIBS} nlohmann_json::nlohmann_json)
|
||||
ELSE()
|
||||
## Don't link against our output, do a full libumskt+cli compile
|
||||
ADD_EXECUTABLE(umskt ${LIBUMSKT_SRC} ${UMSKT_CLI_SRC} ${UMSKT_EXE_WINDOWS_EXTRA})
|
||||
TARGET_LINK_LIBRARIES(umskt PUBLIC ${UMSKT_LINK_LIBS} nlohmann_json::nlohmann_json)
|
||||
ENDIF()
|
||||
|
||||
TARGET_INCLUDE_DIRECTORIES(umskt PUBLIC ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR})
|
||||
TARGET_LINK_DIRECTORIES(umskt PUBLIC ${UMSKT_LINK_DIRS})
|
||||
IF(MSVC AND MSVC_MSDOS_STUB)
|
||||
IF (MSVC)
|
||||
SET_PROPERTY(TARGET umskt APPEND PROPERTY COMPILE_FLAGS /DUMSKT_CLI_WINRC_EMBED_JSON)
|
||||
IF (MSVC_MSDOS_STUB)
|
||||
SET_PROPERTY(TARGET umskt APPEND PROPERTY LINK_FLAGS /STUB:${MSVC_MSDOS_STUB})
|
||||
ENDIF()
|
||||
ENDIF ()
|
||||
|
||||
IF (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
install(TARGETS umskt DESTINATION bin)
|
||||
INSTALL(TARGETS umskt DESTINATION bin)
|
||||
ENDIF ()
|
||||
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)
|
||||
### Strip the built binary
|
||||
IF (${CMAKE_BUILD_TYPE} MATCHES "Release" AND NOT MSVC)
|
||||
ADD_CUSTOM_COMMAND(TARGET umskt POST_BUILD
|
||||
COMMAND strip $<TARGET_FILE:umskt>
|
||||
COMMENT "Stripping symbols for release"
|
||||
)
|
||||
ENDIF()
|
||||
ENDIF ()
|
||||
|
||||
|
||||
#### Build Path for Unit Tests
|
||||
IF ((CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME OR UMSKT_BUILD_TESTING) AND BUILD_TESTING)
|
||||
INCLUDE(CTest)
|
||||
ENABLE_TESTING()
|
||||
|
||||
#include googletest unit testing library
|
||||
CPMAddPackage(
|
||||
NAME googletest
|
||||
GITHUB_REPOSITORY google/googletest
|
||||
VERSION 1.14.0
|
||||
OPTIONS "INSTALL_GTEST OFF" "gtest_force_shared_crt"
|
||||
)
|
||||
|
||||
MACRO(ADD_GTEST TEST)
|
||||
ADD_EXECUTABLE(${TEST} ${ARGN})
|
||||
TARGET_LINK_LIBRARIES(${TEST} gtest gmock gtest_main libumskt_static)
|
||||
TARGET_COMPILE_FEATURES(${TEST} PRIVATE cxx_std_17)
|
||||
SET_TARGET_PROPERTIES(${TEST} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/tests)
|
||||
ADD_TEST(${TEST} tests/${TEST})
|
||||
ENDMACRO()
|
||||
|
||||
ADD_GTEST(TEST_PIDGEN2 src/libumskt/pidgen2/PIDGEN2_unittest.cpp)
|
||||
ADD_GTEST(TEST_PIDGEN3_BINK1998 src/libumskt/pidgen3/BINK1998_unittest.cpp)
|
||||
ADD_GTEST(TEST_PIDGEN3_BINK2002 src/libumskt/pidgen3/BINK2002_unittest.cpp)
|
||||
ADD_GTEST(TEST_CONFIRMATIONID src/libumskt/confid/confid_unittest.cpp)
|
||||
|
||||
ENDIF ()
|
||||
|
17
Dockerfile
17
Dockerfile
@ -1,6 +1,6 @@
|
||||
# This file is a part of the UMSKT Project
|
||||
#
|
||||
# Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
# 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
|
||||
@ -27,9 +27,7 @@ RUN apk add --no-cache \
|
||||
build-base \
|
||||
cmake \
|
||||
git \
|
||||
musl-dev \
|
||||
openssl-dev \
|
||||
openssl-libs-static
|
||||
musl-dev
|
||||
|
||||
|
||||
# Stage 2: Build
|
||||
@ -40,14 +38,15 @@ COPY . /src
|
||||
|
||||
# Build UMSKT from the local directory
|
||||
RUN mkdir /src/build \
|
||||
&& cd /src/build \
|
||||
&& cmake -DMUSL_STATIC=ON .. \
|
||||
&& make
|
||||
&& cmake -B /src/build -DCPM_SOURCE_CACHE=/src/.cpm-cache -DCMAKE_BUILD_TYPE=Release -DUMSKT_MUSL_STATIC=ON \
|
||||
&& cmake --build /src/build -j 10
|
||||
|
||||
# Stage 3: Output
|
||||
FROM scratch as output
|
||||
|
||||
COPY --from=builder /src/build/umskt /umskt
|
||||
COPY --from=builder /src/build/umskt /
|
||||
COPY --from=builder /src/build/libumskt_static.a /
|
||||
COPY --from=builder /src/build/libumskt.so /
|
||||
|
||||
# invoke via
|
||||
# docker build -o type=tar,dest=umskt.tar .
|
||||
# docker build -o type=tar,dest=build-musl/umskt.tar .
|
||||
|
104
Dockerfile.djgpp
104
Dockerfile.djgpp
@ -1,6 +1,6 @@
|
||||
# This file is a part of the UMSKT Project
|
||||
#
|
||||
# Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
# 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
|
||||
@ -19,105 +19,55 @@
|
||||
# @Maintainer Neo
|
||||
|
||||
# Stage 1: Install Prerequisites
|
||||
FROM alpine:latest as prerequisites
|
||||
FROM ubuntu:latest as prerequisites
|
||||
|
||||
# Stage 1: Install build dependencies
|
||||
RUN apk add --no-cache \
|
||||
autoconf \
|
||||
automake \
|
||||
bash \
|
||||
bison \
|
||||
build-base \
|
||||
clang \
|
||||
RUN apt-get update && apt-get -y install \
|
||||
build-essential \
|
||||
cmake \
|
||||
coreutils \
|
||||
curl \
|
||||
elfutils-dev \
|
||||
findutils \
|
||||
wget \
|
||||
7zip \
|
||||
git \
|
||||
gawk \
|
||||
flex \
|
||||
libelf \
|
||||
libslirp-dev \
|
||||
linux-headers \
|
||||
libfl-dev \
|
||||
nasm \
|
||||
sed \
|
||||
slang-dev \
|
||||
texinfo \
|
||||
unzip \
|
||||
zlib-dev
|
||||
|
||||
libslang2-dev \
|
||||
pkg-config \
|
||||
libslang2-modules \
|
||||
gcc-multilib \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
FROM prerequisites as djgpp
|
||||
|
||||
WORKDIR /tmp
|
||||
|
||||
# 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 \
|
||||
&& 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 /djgpp/watt32/lib/libwatt.a /djgpp/lib
|
||||
|
||||
# Stage 4: compile OpenSSL for djgpp-i386/watt32
|
||||
FROM watt32 as openssl
|
||||
WORKDIR /tmp
|
||||
WORKDIR /
|
||||
|
||||
ENV CC=/djgpp/bin/i586-pc-msdosdjgpp-gcc CXX=/djgpp/bin/i586-pc-msdosdjgpp-g++ CMAKE_FIND_ROOT_PATH=/djgpp
|
||||
|
||||
# Stage 2: setup djgpp
|
||||
RUN wget https://github.com/andrewwutw/build-djgpp/releases/download/v3.4/djgpp-linux64-gcc1220.tar.bz2 \
|
||||
&& tar xjf djgpp-linux64-gcc1220.tar.bz2 \
|
||||
&& rm -rf djgpp-linux64-gcc1220.tar.bz2
|
||||
|
||||
# Stage 3: compile UMSKT
|
||||
FROM djgpp as build
|
||||
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 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 \
|
||||
&& source /djgpp/setenv \
|
||||
&& cmake -DDJGPP_WATT32=${WATT_ROOT}/lib/libwatt.a .. \
|
||||
&& make
|
||||
&& cmake -DUMSKT_DJGPP_COMPILE=On -DCMAKE_BUILD_TYPE=Release -DCPM_SOURCE_CACHE=`pwd`/.cpm-cache -B build \
|
||||
&& cmake --build build -j 10 --verbose
|
||||
|
||||
CMD ["bash"]
|
||||
|
||||
# Stage 6: Output
|
||||
FROM scratch as output
|
||||
|
||||
COPY --from=build /src/build/umskt.exe /umskt.exe
|
||||
COPY --from=build /src/build/umskt.exe /
|
||||
|
||||
# invoke via
|
||||
# docker build -f Dockerfile.djgpp -o type=tar,dest=umskt-dos.tar .
|
||||
# docker build -f Dockerfile.djgpp -o type=tar,dest=build-djgpp/umskt-dos.tar .
|
||||
|
@ -1,6 +1,6 @@
|
||||
# This file is a part of the UMSKT Project
|
||||
#
|
||||
# Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
# 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
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
# This file is a part of the UMSKT Project
|
||||
#
|
||||
# Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
# 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
|
||||
@ -23,8 +23,8 @@
|
||||
# Stage 1: Install Visual Studio
|
||||
FROM mcr.microsoft.com/dotnet/framework/runtime:4.8.1 as visualstudio
|
||||
# Download and install Build Tools for Visual Studio 2022 for native desktop workload.
|
||||
#ADD https://aka.ms/vs/17/release/vs_buildtools.exe C:\TEMP\vs_buildtools.exe
|
||||
ADD vs_buildtools.exe C:\TEMP\vs_buildtools.exe
|
||||
ADD https://aka.ms/vs/17/release/vs_buildtools.exe C:\TEMP\vs_buildtools.exe
|
||||
#ADD vs_buildtools.exe C:\TEMP\vs_buildtools.exe
|
||||
RUN C:\TEMP\vs_buildtools.exe --quiet --wait --norestart --nocache `
|
||||
--add Microsoft.VisualStudio.Workload.VCTools `
|
||||
--add Microsoft.VisualStudio.Workload.MSBuildTools `
|
||||
@ -40,26 +40,15 @@ RUN ["powershell.exe", "-NoLogo", "-ExecutionPolicy", "Bypass", "[System.Net.Ser
|
||||
RUN ["powershell.exe", "-NoLogo", "-ExecutionPolicy", "Bypass", "choco feature enable -n allowGlobalConfirmation"]
|
||||
RUN ["powershell.exe", "-NoLogo", "-ExecutionPolicy", "Bypass", "choco install git --params \"'/GitAndUnixToolsOnPath /WindowsTerminal /NoShellIntegration /NoGuiHereIntegration /NoShellHereIntegration /NoCredentialManager /SChannel'\""]
|
||||
|
||||
#Install OpenSSL 32bit 3.1.1
|
||||
#ADD https://slproweb.com/download/Win32OpenSSL-3_1_1.msi C:\TEMP\Win32OpenSSL-3_1_1.msi
|
||||
ADD Win32OpenSSL-3_1_1.msi C:\TEMP\Win32OpenSSL-3_1_1.msi
|
||||
RUN msiexec /i C:\TEMP\Win32OpenSSL-3_1_1.msi /quiet /qn /norestart
|
||||
|
||||
#Install OpenSSL 64bit 3.1.1
|
||||
#ADD https://slproweb.com/download/Win64OpenSSL-3_1_1.msi C:\TEMP\Win64OpenSSL-3_1_1.msi
|
||||
ADD Win64OpenSSL-3_1_1.msi C:\TEMP\Win64OpenSSL-3_1_1.msi
|
||||
RUN msiexec /i C:\TEMP\Win64OpenSSL-3_1_1.msi /quiet /qn /norestart
|
||||
|
||||
# Stage 3: Build the 32-bit version of UMSKT
|
||||
FROM prereqisites as Build32
|
||||
WORKDIR C:\umskt\
|
||||
COPY . C:\umskt\
|
||||
|
||||
RUN C:\BuildTools\Common7\Tools\VsDevCmd.bat && `
|
||||
mkdir C:\umskt\build && `
|
||||
cd C:\umskt\build && `
|
||||
cmake -DMSVC_MSDOS_STUB:string=..\umskt.exe .. -G "Visual Studio 17 2022" -A "Win32" -T v141_xp && `
|
||||
msbuild ALL_BUILD.vcxproj /P:Configuration=Release
|
||||
mkdir C:\umskt\build && cd C:\umskt && `
|
||||
cmake -B build -DCPM_SOURCE_CACHE=../.cpm-cache -DUMSKT_MSVC_WINXP=On -DCMAKE_BUILD_TYPE=Release -DMSVC_MSDOS_STUB=..\umskt.exe -G "Visual Studio 17 2022" -A "Win32" -T v141_xp && `
|
||||
cmake --build build --config Release -j 10
|
||||
|
||||
# Stage 4: Build the 64-bit version of UMSKT
|
||||
FROM prereqisites as Build64
|
||||
@ -67,12 +56,10 @@ FROM prereqisites as Build64
|
||||
WORKDIR C:\umskt\
|
||||
COPY . C:\umskt\
|
||||
|
||||
ENV OPENSSL_ROOT_DIR="C:\Program Files\OpenSSL-Win64"
|
||||
RUN C:\BuildTools\Common7\Tools\VsDevCmd.bat && `
|
||||
mkdir C:\umskt\build && `
|
||||
cd C:\umskt\build && `
|
||||
cmake .. && `
|
||||
msbuild ALL_BUILD.vcxproj /P:Configuration=Release
|
||||
cd C:\umskt && `
|
||||
cmake -B build -DCPM_SOURCE_CACHE=../.cpm-cache -DCMAKE_BUILD_TYPE=Release && `
|
||||
cmake --build build --config Release -j 10
|
||||
|
||||
# Stage 5: Copy binaries to an output/runtime image
|
||||
FROM mcr.microsoft.com/dotnet/framework/runtime:4.8.1 as output
|
||||
|
@ -57,13 +57,9 @@ 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. 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.*
|
||||
#### 2. Run `umskt` to generate a key, or add `--help` or `-h` to see more options.
|
||||
|
||||
#### 3. Run `umskt` to generate a key, or add `--help` or `-h` to see more options.
|
||||
|
||||
#### 4. *(Activation step for `Retail` and `OEM` only)*
|
||||
#### 3. *(Activation step for `Retail` and `OEM` only)*
|
||||
* After installation, you will be prompted to activate Windows.
|
||||
|
||||
|
||||
|
@ -1,33 +0,0 @@
|
||||
set(CPM_DOWNLOAD_VERSION 0.38.1)
|
||||
|
||||
if(CPM_SOURCE_CACHE)
|
||||
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
||||
elseif(DEFINED ENV{CPM_SOURCE_CACHE})
|
||||
set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
||||
else()
|
||||
set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
||||
endif()
|
||||
|
||||
# Expand relative path. This is important if the provided path contains a tilde (~)
|
||||
get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE)
|
||||
|
||||
function(download_cpm)
|
||||
message(STATUS "Downloading CPM.cmake to ${CPM_DOWNLOAD_LOCATION}")
|
||||
file(DOWNLOAD
|
||||
https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake
|
||||
${CPM_DOWNLOAD_LOCATION}
|
||||
)
|
||||
endfunction()
|
||||
|
||||
if(NOT (EXISTS ${CPM_DOWNLOAD_LOCATION}))
|
||||
download_cpm()
|
||||
else()
|
||||
# resume download if it previously failed
|
||||
file(READ ${CPM_DOWNLOAD_LOCATION} check)
|
||||
if("${check}" STREQUAL "")
|
||||
download_cpm()
|
||||
endif()
|
||||
unset(check)
|
||||
endif()
|
||||
|
||||
include(${CPM_DOWNLOAD_LOCATION})
|
@ -47,7 +47,7 @@ The procedure for SLP 2.0 verification is as follows:
|
||||
|
||||
4. If the OEM product ID is detected, the OEM verification process starts, and it will check the installed OEM certificate <!-- "product certificate" ? --> is valid. It mainly uses the SLIC public key from BIOS which is loaded into the RAM, to verify the digital signature of product certificate <!-- Yes, this "product certificate" came from nowhere, so I guess this the OEM certificate that installed while installing the Windows Vista by OEM. -->. If validation failed, it is considered as inactivated.
|
||||
|
||||
5. Verify the OEM ID field between SLIC and RSDT (Root System Description Table), and compare the OEM ID field and OEM Table ID fields between SLIC and XSDT (Extended System Description Table). If they didn't match, it is considered as inactivated.
|
||||
5. Validate the OEM ID field between SLIC and RSDT (Root System Description Table), and compare the OEM ID field and OEM Table ID fields between SLIC and XSDT (Extended System Description Table). If they didn't match, it is considered as inactivated.
|
||||
|
||||
6. After the above hurdles, the OEM license will be considered as activated. If not then fallback to inactivated and continue with normal procedure, like require user to activate the Windows.
|
||||
|
||||
@ -65,9 +65,9 @@ Here is more detailed verification process:
|
||||
|
||||
4. Use OEM public key verify against the digital signature of the Marker inside SLIC. if verified (this means the Message in the Marker is correct. <!-- There is no where talked about what is that Message and Marker. -->) then continue on OEM activation process, otherwise continue on WPA activation process.
|
||||
|
||||
5. Verify the Windows Logo <!-- I guess? "Windows Logo" part can be directly translate to "Windows Flag Mark", and this "Flag" means that actual waving thing, not a digital bit. I guess this approach is similar as the Nintendo logo bitmap inside Game Boy game cartridge ROM thing. --> inside the Marker. If the Windows Logo is present, then continue on OEM activation process, otherwise continue on WPA activation process.
|
||||
5. Validate the Windows Logo <!-- I guess? "Windows Logo" part can be directly translate to "Windows Flag Mark", and this "Flag" means that actual waving thing, not a digital bit. I guess this approach is similar as the Nintendo logo bitmap inside Game Boy game cartridge ROM thing. --> inside the Marker. If the Windows Logo is present, then continue on OEM activation process, otherwise continue on WPA activation process.
|
||||
|
||||
6. Verify the version of Marker. If it's at least `0x20001` then continue on OEM activation process, otherwise OEM activation failed, and continue on WPA activation process.
|
||||
6. Validate the version of Marker. If it's at least `0x20001` then continue on OEM activation process, otherwise OEM activation failed, and continue on WPA activation process.
|
||||
|
||||
7. Compare the OEM ID and OEM Table ID against all corresponding ACPI table headers. If they match, then OEM activation completed successfully, otherwise continue on WPA activation process.
|
||||
|
||||
|
602
src/cli.cpp
602
src/cli.cpp
@ -1,602 +0,0 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2023 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 Andrew on 01/06/2023
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#include "cli.h"
|
||||
|
||||
CLI::~CLI()
|
||||
{
|
||||
EC_GROUP_free(eCurve);
|
||||
EC_POINT_free(genPoint);
|
||||
EC_POINT_free(pubPoint);
|
||||
BN_free(privateKey);
|
||||
BN_free(genOrder);
|
||||
}
|
||||
|
||||
bool CLI::loadJSON(const fs::path& filename, json *output) {
|
||||
if (!filename.empty() && !fs::exists(filename)) {
|
||||
fmt::print("ERROR: File {} does not exist\n", filename.string());
|
||||
return false;
|
||||
}
|
||||
else if (fs::exists(filename)) {
|
||||
std::ifstream f(filename);
|
||||
*output = json::parse(f, nullptr, false, false);
|
||||
}
|
||||
else if (filename.empty()) {
|
||||
cmrc::embedded_filesystem fs = cmrc::umskt::get_filesystem();
|
||||
cmrc::file keys = fs.open("keys.json");
|
||||
*output = json::parse(keys, nullptr, false, false);
|
||||
}
|
||||
|
||||
if (output->is_discarded()) {
|
||||
fmt::print("ERROR: Unable to parse keys from {}\n", filename.string());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CLI::showHelp(char *argv[]) {
|
||||
fmt::print("usage: {} \n", argv[0]);
|
||||
fmt::print("\t-h --help\tshow this message\n");
|
||||
fmt::print("\t-v --verbose\tenable verbose output\n");
|
||||
fmt::print("\t-n --number\tnumber of keys to generate (defaults to 1)\n");
|
||||
fmt::print("\t-f --file\tspecify which keys file to load\n");
|
||||
fmt::print("\t-i --instid\tinstallation ID used to generate confirmation ID\n");
|
||||
fmt::print("\t-m --mode\tproduct family to activate.\n\t\t\tvalid options are \"WINDOWS\", \"OFFICEXP\", \"OFFICE2K3\", \"OFFICE2K7\" or \"PLUSDME\"\n\t\t\t(defaults to \"WINDOWS\")\n");
|
||||
fmt::print("\t-p --productid\tthe product ID of the Program to activate. only required for Office 2K3 and Office 2K7 programs\n");
|
||||
fmt::print("\t-b --binkid\tspecify which BINK identifier to load (defaults to 2E)\n");
|
||||
fmt::print("\t-l --list\tshow which products/binks can be loaded\n");
|
||||
fmt::print("\t-c --channelid\tspecify which Channel Identifier to use (defaults to 640)\n");
|
||||
fmt::print("\t-s --serial\tspecifies a serial to use in the product ID (defaults to random, BINK1998 only)\n");
|
||||
fmt::print("\t-u --upgrade\tspecifies the Product Key will be an \"Upgrade\" version\n");
|
||||
fmt::print("\t-V --validate\tproduct key to validate signature\n");
|
||||
fmt::print("\t-N --nonewlines\tdisables newlines (for easier embedding in other apps)\n");
|
||||
fmt::print("\n");
|
||||
}
|
||||
|
||||
int CLI::parseCommandLine(int argc, char* argv[], Options* options) {
|
||||
// set default options
|
||||
*options = Options {
|
||||
"2E",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
640,
|
||||
0,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
MODE_BINK1998_GENERATE,
|
||||
WINDOWS
|
||||
};
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
std::string arg = argv[i];
|
||||
if (arg == "-v" || arg == "--verbose") {
|
||||
options->verbose = true;
|
||||
UMSKT::setDebugOutput(stderr);
|
||||
} else if (arg == "-h" || arg == "--help") {
|
||||
options->help = true;
|
||||
} else if (arg == "-n" || arg == "--number") {
|
||||
if (i == argc - 1) {
|
||||
options->error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
int nKeys;
|
||||
if (!sscanf(argv[i+1], "%d", &nKeys)) {
|
||||
options->error = true;
|
||||
} else {
|
||||
options->numKeys = nKeys;
|
||||
}
|
||||
i++;
|
||||
} else if (arg == "-b" || arg == "--bink") {
|
||||
if (i == argc - 1) {
|
||||
options->error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
options->binkid = argv[i+1];
|
||||
i++;
|
||||
} else if (arg == "-l" || arg == "--list") {
|
||||
options->list = true;
|
||||
} else if (arg == "-c" || arg == "--channelid") {
|
||||
if (i == argc - 1) {
|
||||
options->error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
int siteID;
|
||||
if (!sscanf(argv[i+1], "%d", &siteID)) {
|
||||
options->error = true;
|
||||
} else {
|
||||
options->channelID = siteID;
|
||||
}
|
||||
i++;
|
||||
} else if (arg == "-s" || arg == "--serial") {
|
||||
if (i == argc - 1) {
|
||||
options->error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
int serial_val;
|
||||
if (!sscanf(argv[i+1], "%d", &serial_val)) {
|
||||
options->error = true;
|
||||
} else {
|
||||
options->serialSet = true;
|
||||
options->serial = serial_val;
|
||||
}
|
||||
i++;
|
||||
} else if (arg == "-u" || arg == "--upgrade") {
|
||||
options->upgrade = true;
|
||||
} else if (arg == "-f" || arg == "--file") {
|
||||
if (i == argc - 1) {
|
||||
options->error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
options->keysFilename = argv[i+1];
|
||||
i++;
|
||||
} else if (arg == "-i" || arg == "--instid") {
|
||||
if (i == argc - 1) {
|
||||
options->error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
options->instid = argv[i+1];
|
||||
options->applicationMode = MODE_CONFIRMATION_ID;
|
||||
i++;
|
||||
} else if (arg == "-m" || arg == "--mode") {
|
||||
std::string mode = argv[i+1];
|
||||
char *p = &mode[0];
|
||||
for (; *p; p++) {
|
||||
*p = toupper((unsigned char)*p);
|
||||
}
|
||||
p = &mode[0];
|
||||
if (strcmp(p, "WINDOWS") == 0) {
|
||||
options->activationMode = WINDOWS;
|
||||
} else if (strcmp(p, "OFFICEXP") == 0) {
|
||||
options->activationMode = OFFICE_XP;
|
||||
} else if (strcmp(p, "OFFICE2K3") == 0) {
|
||||
options->activationMode = OFFICE_2K3;
|
||||
} else if (strcmp(p, "OFFICE2K7") == 0) {
|
||||
options->activationMode = OFFICE_2K7;
|
||||
} else if (strcmp(p, "PLUSDME") == 0) {
|
||||
options->activationMode = PLUS_DME;
|
||||
}
|
||||
i++;
|
||||
} else if (arg == "-p" || arg == "--productid") {
|
||||
if (i == argc -1) {
|
||||
options->error = true;
|
||||
break;
|
||||
}
|
||||
options->productid = argv[i+1];
|
||||
i++;
|
||||
} else if (arg == "-V" || arg == "--validate") {
|
||||
if (i == argc - 1) {
|
||||
options->error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
options->keyToCheck = argv[i+1];
|
||||
options->applicationMode = MODE_BINK1998_VALIDATE;
|
||||
i++;
|
||||
|
||||
} else if (arg == "-N" || arg == "--nonewlines") {
|
||||
options->nonewlines = true;
|
||||
} else {
|
||||
options->error = true;
|
||||
}
|
||||
}
|
||||
|
||||
// make sure that a product id is entered for OFFICE_2K3 or OFFICE_2K7 IIDs
|
||||
if ((options->activationMode == OFFICE_2K3 || options->activationMode == OFFICE_2K7) && (options->productid.empty() || options->instid.empty()) ) {
|
||||
return options->error = true;
|
||||
}
|
||||
|
||||
return !options->error;
|
||||
}
|
||||
|
||||
int CLI::validateCommandLine(Options* options, char *argv[], json *keys) {
|
||||
if (options->help || options->error) {
|
||||
if (options->error) {
|
||||
fmt::print("error parsing command line options\n");
|
||||
}
|
||||
showHelp(argv);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (options->verbose) {
|
||||
if(options->keysFilename.empty()) {
|
||||
fmt::print("Loading internal keys file\n");
|
||||
} else {
|
||||
fmt::print("Loading keys file {}\n", options->keysFilename);
|
||||
}
|
||||
}
|
||||
|
||||
if (!loadJSON(options->keysFilename, keys)) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (options->verbose) {
|
||||
if(options->keysFilename.empty()) {
|
||||
fmt::print("Loaded internal keys file successfully\n");
|
||||
} else {
|
||||
fmt::print("Loaded keys from {} successfully\n",options->keysFilename);
|
||||
}
|
||||
}
|
||||
|
||||
if (options->list) {
|
||||
for (auto el : (*keys)["Products"].items()) {
|
||||
int id;
|
||||
sscanf((el.value()["BINK"][0]).get<std::string>().c_str(), "%x", &id);
|
||||
std::cout << el.key() << ": " << el.value()["BINK"] << std::endl;
|
||||
}
|
||||
|
||||
fmt::print("\n\n");
|
||||
fmt::print("** Please note: any BINK ID other than 2E is considered experimental at this time **\n");
|
||||
fmt::print("\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int intBinkID;
|
||||
sscanf(options->binkid.c_str(), "%x", &intBinkID);
|
||||
|
||||
// FE and FF are BINK 1998, but do not generate valid keys, so we throw an error
|
||||
if (intBinkID >= 0xFE) {
|
||||
fmt::print("ERROR: Terminal Services BINKs (FE and FF) are unsupported at this time\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (intBinkID >= 0x40) {
|
||||
// set bink2002 validate mode if in bink1998 validate mode, else set bink2002 generate mode
|
||||
options->applicationMode = (options->applicationMode == MODE_BINK1998_VALIDATE) ? MODE_BINK2002_VALIDATE : MODE_BINK2002_GENERATE;
|
||||
}
|
||||
|
||||
if (options->channelID > 999) {
|
||||
fmt::print("ERROR: refusing to create a key with a Channel ID greater than 999\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// don't allow any serial not between 0 and 999999
|
||||
if (options->serial > 999999 || options->serial < 0) {
|
||||
fmt::print("ERROR: refusing to create a key with a Serial not between 000000 and 999999\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CLI::printID(DWORD *pid) {
|
||||
char raw[12];
|
||||
char b[6], c[8];
|
||||
int i, digit = 0;
|
||||
|
||||
// Convert PID to ascii-number (=raw)
|
||||
snprintf(raw, sizeof(raw), "%09u", pid[0]);
|
||||
|
||||
// Make b-part {640-....}
|
||||
strncpy(b, raw, 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
|
||||
|
||||
while (digit < 0)
|
||||
digit += 7;
|
||||
c[6] = digit + '0';
|
||||
c[7] = 0;
|
||||
|
||||
fmt::print("> Product ID: PPPPP-{}-{}-23xxx\n", b, c);
|
||||
}
|
||||
|
||||
void CLI::printKey(char *pk) {
|
||||
assert(strlen(pk) >= PK_LENGTH);
|
||||
|
||||
std::string spk = pk;
|
||||
fmt::print("{}-{}-{}-{}-{}",
|
||||
spk.substr(0,5),
|
||||
spk.substr(5,5),
|
||||
spk.substr(10,5),
|
||||
spk.substr(15,5),
|
||||
spk.substr(20,5));
|
||||
}
|
||||
|
||||
bool CLI::stripKey(const char *in_key, char out_key[PK_LENGTH]) {
|
||||
// copy out the product key stripping out extraneous characters
|
||||
const char *p = in_key;
|
||||
size_t i = 0;
|
||||
for (; *p; p++) {
|
||||
// strip out space or dash
|
||||
if (*p == ' ' || *p == '-')
|
||||
continue;
|
||||
// check if we've passed the product key length to avoid overflow
|
||||
if (i >= PK_LENGTH)
|
||||
return false;
|
||||
// convert to uppercase - if character allowed, copy into array
|
||||
for (int j = 0; j < strlen(PIDGEN3::pKeyCharset); j++) {
|
||||
if (toupper(*p) == PIDGEN3::pKeyCharset[j]) {
|
||||
out_key[i++] = toupper(*p);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
// only return true if we've handled exactly PK_LENGTH chars
|
||||
return (i == PK_LENGTH);
|
||||
}
|
||||
|
||||
CLI::CLI(Options options, json keys) {
|
||||
this->options = options;
|
||||
this->keys = keys;
|
||||
|
||||
this->BINKID = options.binkid.c_str();
|
||||
|
||||
// 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).
|
||||
this->privateKey = BN_new();
|
||||
|
||||
// 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.
|
||||
this->genOrder = BN_new();
|
||||
|
||||
/* Computed data */
|
||||
BN_dec2bn(&this->genOrder, this->keys["BINK"][this->BINKID]["n"]. get<std::string>().c_str());
|
||||
BN_dec2bn(&this->privateKey, this->keys["BINK"][this->BINKID]["priv"].get<std::string>().c_str());
|
||||
|
||||
if (options.verbose) {
|
||||
fmt::print("----------------------------------------------------------- \n");
|
||||
fmt::print("Loaded the following elliptic curve parameters: BINK[{}]\n", this->BINKID);
|
||||
fmt::print("----------------------------------------------------------- \n");
|
||||
fmt::print(" P: {}\n", this->keys["BINK"][this->BINKID]["p"].get<std::string>());
|
||||
fmt::print(" a: {}\n", this->keys["BINK"][this->BINKID]["a"].get<std::string>());
|
||||
fmt::print(" b: {}\n", this->keys["BINK"][this->BINKID]["b"].get<std::string>());
|
||||
fmt::print("Gx: {}\n", this->keys["BINK"][this->BINKID]["g"]["x"].get<std::string>());
|
||||
fmt::print("Gy: {}\n", this->keys["BINK"][this->BINKID]["g"]["y"].get<std::string>());
|
||||
fmt::print("Kx: {}\n", this->keys["BINK"][this->BINKID]["pub"]["x"].get<std::string>());
|
||||
fmt::print("Ky: {}\n", this->keys["BINK"][this->BINKID]["pub"]["y"].get<std::string>());
|
||||
fmt::print(" n: {}\n", this->keys["BINK"][this->BINKID]["n"].get<std::string>());
|
||||
fmt::print(" k: {}\n", this->keys["BINK"][this->BINKID]["priv"].get<std::string>());
|
||||
fmt::print("\n");
|
||||
}
|
||||
|
||||
|
||||
eCurve = PIDGEN3::initializeEllipticCurve(
|
||||
this->keys["BINK"][this->BINKID]["p"].get<std::string>(),
|
||||
this->keys["BINK"][this->BINKID]["a"].get<std::string>(),
|
||||
this->keys["BINK"][this->BINKID]["b"].get<std::string>(),
|
||||
this->keys["BINK"][this->BINKID]["g"]["x"].get<std::string>(),
|
||||
this->keys["BINK"][this->BINKID]["g"]["y"].get<std::string>(),
|
||||
this->keys["BINK"][this->BINKID]["pub"]["x"].get<std::string>(),
|
||||
this->keys["BINK"][this->BINKID]["pub"]["y"].get<std::string>(),
|
||||
this->genPoint,
|
||||
this->pubPoint
|
||||
);
|
||||
|
||||
this->count = 0;
|
||||
this->total = this->options.numKeys;
|
||||
}
|
||||
|
||||
int CLI::BINK1998Generate() {
|
||||
// raw PID/serial value
|
||||
DWORD nRaw = this->options.channelID * 1'000'000 ; /* <- change */
|
||||
|
||||
// using user-provided serial
|
||||
if (this->options.serialSet) {
|
||||
// just in case, make sure it's less than 999999
|
||||
int serialRnd = (this->options.serial % 999999);
|
||||
nRaw += serialRnd;
|
||||
} else {
|
||||
// generate a random number to use as a serial
|
||||
BIGNUM *bnrand = BN_new();
|
||||
BN_rand(bnrand, 19, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY);
|
||||
|
||||
int oRaw;
|
||||
char *cRaw = BN_bn2dec(bnrand);
|
||||
|
||||
sscanf(cRaw, "%d", &oRaw);
|
||||
nRaw += (oRaw % 999999); // ensure our serial is less than 999999
|
||||
BN_free(bnrand);
|
||||
}
|
||||
|
||||
if (this->options.verbose) {
|
||||
// print the resulting Product ID
|
||||
// PID value is printed in BINK1998::Generate
|
||||
printID(&nRaw);
|
||||
}
|
||||
|
||||
// generate a key
|
||||
BN_sub(this->privateKey, this->genOrder, this->privateKey);
|
||||
|
||||
for (int i = 0; i < this->total; i++) {
|
||||
PIDGEN3::BINK1998::Generate(this->eCurve, this->genPoint, this->genOrder, this->privateKey, nRaw, options.upgrade, this->pKey);
|
||||
|
||||
bool isValid = PIDGEN3::BINK1998::Verify(this->eCurve, this->genPoint, this->pubPoint, this->pKey);
|
||||
if (isValid) {
|
||||
CLI::printKey(this->pKey);
|
||||
if (i < this->total - 1 || this->options.verbose) {
|
||||
fmt::print("\n");
|
||||
}
|
||||
this->count += isValid;
|
||||
}
|
||||
else {
|
||||
if (this->options.verbose) {
|
||||
CLI::printKey(this->pKey);
|
||||
fmt::print(" [Invalid]");
|
||||
if (i < this->total - 1) {
|
||||
fmt::print("\n");
|
||||
}
|
||||
}
|
||||
this->total++; // queue a redo, basically
|
||||
}
|
||||
}
|
||||
|
||||
if (this->options.verbose) {
|
||||
fmt::print("\nSuccess count: {}/{}", this->count, this->total);
|
||||
}
|
||||
if (this->options.nonewlines == false) {
|
||||
fmt::print("\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CLI::BINK2002Generate() {
|
||||
DWORD pChannelID = this->options.channelID;
|
||||
|
||||
if (this->options.verbose) {
|
||||
fmt::print("> Channel ID: {:03d}\n", this->options.channelID);
|
||||
}
|
||||
|
||||
// generate a key
|
||||
for (int i = 0; i < this->total; i++) {
|
||||
DWORD pAuthInfo;
|
||||
RAND_bytes((BYTE *)&pAuthInfo, 4);
|
||||
pAuthInfo &= BITMASK(10);
|
||||
|
||||
if (this->options.verbose) {
|
||||
fmt::print("> AuthInfo: {}\n", pAuthInfo);
|
||||
}
|
||||
|
||||
PIDGEN3::BINK2002::Generate(this->eCurve, this->genPoint, this->genOrder, this->privateKey, pChannelID, pAuthInfo, options.upgrade, this->pKey);
|
||||
|
||||
bool isValid = PIDGEN3::BINK2002::Verify(this->eCurve, this->genPoint, this->pubPoint, this->pKey);
|
||||
if (isValid) {
|
||||
CLI::printKey(this->pKey);
|
||||
if (i < this->total - 1 || this->options.verbose) { // check if end of list or verbose
|
||||
fmt::print("\n");
|
||||
}
|
||||
this->count += isValid; // add to count
|
||||
}
|
||||
else {
|
||||
if (this->options.verbose) {
|
||||
CLI::printKey(this->pKey); // print the key
|
||||
fmt::print(" [Invalid]"); // and add " [Invalid]" to the key
|
||||
if (i < this->total - 1) { // check if end of list
|
||||
fmt::print("\n");
|
||||
}
|
||||
}
|
||||
this->total++; // queue a redo, basically
|
||||
}
|
||||
}
|
||||
|
||||
if (this->options.verbose) {
|
||||
fmt::print("\nSuccess count: {}/{}", this->count, this->total);
|
||||
}
|
||||
if (this->options.nonewlines == false) {
|
||||
fmt::print("\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CLI::BINK1998Validate() {
|
||||
char product_key[PK_LENGTH]{};
|
||||
|
||||
if (!CLI::stripKey(this->options.keyToCheck.c_str(), product_key)) {
|
||||
fmt::print("ERROR: Product key is in an incorrect format!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
CLI::printKey(product_key);
|
||||
fmt::print("\n");
|
||||
if (!PIDGEN3::BINK1998::Verify(this->eCurve, this->genPoint, this->pubPoint, product_key)) {
|
||||
fmt::print("ERROR: Product key is invalid! Wrong BINK ID?\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fmt::print("Key validated successfully!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CLI::BINK2002Validate() {
|
||||
char product_key[PK_LENGTH]{};
|
||||
|
||||
if (!CLI::stripKey(this->options.keyToCheck.c_str(), product_key)) {
|
||||
fmt::print("ERROR: Product key is in an incorrect format!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
CLI::printKey(product_key);
|
||||
fmt::print("\n");
|
||||
if (!PIDGEN3::BINK2002::Verify(this->eCurve, this->genPoint, this->pubPoint, product_key)) {
|
||||
fmt::print("ERROR: Product key is invalid! Wrong BINK ID?\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fmt::print("Key validated successfully!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CLI::ConfirmationID() {
|
||||
char confirmation_id[49];
|
||||
int err = ConfirmationID::Generate(this->options.instid.c_str(), confirmation_id, options.activationMode, options.productid);
|
||||
|
||||
switch (err) {
|
||||
case ERR_TOO_SHORT:
|
||||
fmt::print("ERROR: Installation ID is too short.\n");
|
||||
return 1;
|
||||
|
||||
case ERR_TOO_LARGE:
|
||||
fmt::print("ERROR: Installation ID is too long.\n");
|
||||
return 1;
|
||||
|
||||
case ERR_INVALID_CHARACTER:
|
||||
fmt::print("ERROR: Invalid character in installation ID.\n");
|
||||
return 1;
|
||||
|
||||
case ERR_INVALID_CHECK_DIGIT:
|
||||
fmt::print("ERROR: Installation ID checksum failed. Please check that it is typed correctly.\n");
|
||||
return 1;
|
||||
|
||||
case ERR_UNKNOWN_VERSION:
|
||||
fmt::print("ERROR: Unknown installation ID version.\n");
|
||||
return 1;
|
||||
|
||||
case ERR_UNLUCKY:
|
||||
fmt::print("ERROR: Unable to generate valid confirmation ID.\n");
|
||||
return 1;
|
||||
|
||||
case SUCCESS:
|
||||
fmt::print(confirmation_id);
|
||||
if (this->options.nonewlines == false) {
|
||||
fmt::print("\n");
|
||||
}
|
||||
return 0;
|
||||
|
||||
default:
|
||||
fmt::print("Unknown error occurred during Confirmation ID generation: {}\n", err);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
105
src/cli.h
105
src/cli.h
@ -1,105 +0,0 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2023 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 6/6/2023
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#ifndef UMSKT_CLI_H
|
||||
#define UMSKT_CLI_H
|
||||
|
||||
#include "header.h"
|
||||
|
||||
#include <cmrc/cmrc.hpp>
|
||||
|
||||
#include "libumskt/libumskt.h"
|
||||
#include "libumskt/pidgen2/PIDGEN2.h"
|
||||
#include "libumskt/pidgen3/PIDGEN3.h"
|
||||
#include "libumskt/pidgen3/BINK1998.h"
|
||||
#include "libumskt/pidgen3/BINK2002.h"
|
||||
#include "libumskt/confid/confid.h"
|
||||
|
||||
CMRC_DECLARE(umskt);
|
||||
|
||||
enum ACTIVATION_ALGORITHM {
|
||||
WINDOWS = 0,
|
||||
OFFICE_XP = 1,
|
||||
OFFICE_2K3 = 2,
|
||||
OFFICE_2K7 = 3,
|
||||
PLUS_DME = 4,
|
||||
};
|
||||
|
||||
enum MODE {
|
||||
MODE_BINK1998_GENERATE = 0,
|
||||
MODE_BINK2002_GENERATE = 1,
|
||||
MODE_CONFIRMATION_ID = 2,
|
||||
MODE_BINK1998_VALIDATE = 3,
|
||||
MODE_BINK2002_VALIDATE = 4,
|
||||
};
|
||||
|
||||
struct Options {
|
||||
std::string binkid;
|
||||
std::string keysFilename;
|
||||
std::string instid;
|
||||
std::string keyToCheck;
|
||||
std::string productid;
|
||||
int channelID;
|
||||
int serial;
|
||||
int numKeys;
|
||||
bool upgrade;
|
||||
bool serialSet;
|
||||
bool verbose;
|
||||
bool help;
|
||||
bool error;
|
||||
bool list;
|
||||
bool nonewlines;
|
||||
|
||||
MODE applicationMode;
|
||||
ACTIVATION_ALGORITHM activationMode;
|
||||
};
|
||||
|
||||
class CLI {
|
||||
Options options;
|
||||
json keys;
|
||||
const char* BINKID;
|
||||
BIGNUM *privateKey, *genOrder;
|
||||
EC_POINT *genPoint, *pubPoint;
|
||||
EC_GROUP *eCurve;
|
||||
char pKey[25];
|
||||
int count, total;
|
||||
|
||||
public:
|
||||
CLI(Options options, json keys);
|
||||
~CLI();
|
||||
|
||||
static bool loadJSON(const fs::path& filename, json *output);
|
||||
static void showHelp(char *argv[]);
|
||||
static int parseCommandLine(int argc, char* argv[], Options *options);
|
||||
static int validateCommandLine(Options* options, char *argv[], json *keys);
|
||||
static void printID(DWORD *pid);
|
||||
static void printKey(char *pk);
|
||||
static bool stripKey(const char *in_key, char out_key[PK_LENGTH]);
|
||||
|
||||
int BINK1998Generate();
|
||||
int BINK2002Generate();
|
||||
int BINK1998Validate();
|
||||
int BINK2002Validate();
|
||||
int ConfirmationID();
|
||||
};
|
||||
|
||||
#endif //UMSKT_CLI_H
|
159
src/cli/cli.cpp
Normal file
159
src/cli/cli.cpp
Normal file
@ -0,0 +1,159 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2024 UMSKT Contributors (et.al.)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @FileCreated by Andrew on 01/06/2023
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#include "cli.h"
|
||||
|
||||
// define static storage
|
||||
CLI::Options CLI::options;
|
||||
json CLI::keys;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param argcIn
|
||||
* @param argvIn
|
||||
* @return exit status (where success is 0)
|
||||
*/
|
||||
BYTE CLI::Init(int argcIn, char **argvIn)
|
||||
{
|
||||
// set default options
|
||||
options.argc = argcIn;
|
||||
options.argv = argvIn;
|
||||
options.binkID = "2E";
|
||||
options.productCode = "WINXP";
|
||||
options.productFlavour = "VLK";
|
||||
options.numKeys = 1;
|
||||
options.pidgenversion = Options::PIDGEN_VERSION::PIDGEN_3;
|
||||
options.state = Options::APPLICATION_STATE::STATE_PIDGEN_GENERATE;
|
||||
|
||||
SetHelpText();
|
||||
|
||||
BOOL success = parseCommandLine();
|
||||
if (!success)
|
||||
{
|
||||
return options.error;
|
||||
}
|
||||
|
||||
// if we displayed help, without an error
|
||||
// return success
|
||||
if (options.help)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
success = processOptions();
|
||||
if (!success)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param filename
|
||||
* @return success
|
||||
*/
|
||||
BOOL CLI::loadJSON(const fs::path &filename)
|
||||
{
|
||||
if (filename.empty())
|
||||
{
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Loading internal keys file\n");
|
||||
}
|
||||
|
||||
auto retval = loadEmbeddedJSON();
|
||||
|
||||
if (retval && options.verbose)
|
||||
{
|
||||
fmt::print("Loaded internal keys file successfully\n\n");
|
||||
}
|
||||
else if (!retval)
|
||||
{
|
||||
fmt::print("Error loading internal keys file...\n\n");
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (!fs::exists(filename))
|
||||
{
|
||||
fmt::print("ERROR: File {} does not exist\n", filename.string());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Loading keys file: {}\n", options.keysFilename);
|
||||
}
|
||||
|
||||
std::ifstream f(filename);
|
||||
try
|
||||
{
|
||||
keys = json::parse(f, nullptr, false, false);
|
||||
}
|
||||
catch (const json::exception &e)
|
||||
{
|
||||
fmt::print("ERROR: Exception thrown while parsing {}: {}\n", filename.string(), e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (keys.is_discarded())
|
||||
{
|
||||
fmt::print("ERROR: Unable to parse keys from: {}\n", filename.string());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Loaded keys from \"{}\" successfully\n", options.keysFilename);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process application state
|
||||
*
|
||||
* @return application status code
|
||||
*/
|
||||
int CLI::Run()
|
||||
{
|
||||
/**
|
||||
* TODO: we should be checking if the system's pseudorandom facilities work
|
||||
* before attempting generation, validation does not require entropy
|
||||
*/
|
||||
switch (options.state)
|
||||
{
|
||||
case Options::APPLICATION_STATE::STATE_PIDGEN_GENERATE:
|
||||
return PIDGenerate();
|
||||
|
||||
case Options::APPLICATION_STATE::STATE_PIDGEN_VALIDATE:
|
||||
return PIDValidate();
|
||||
|
||||
case Options::APPLICATION_STATE::STATE_CONFIRMATION_ID:
|
||||
return ConfirmationIDGenerate();
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
172
src/cli/cli.h
Normal file
172
src/cli/cli.h
Normal file
@ -0,0 +1,172 @@
|
||||
/**
|
||||
* 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 06/06/2023
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#ifndef UMSKT_CLI_H
|
||||
#define UMSKT_CLI_H
|
||||
|
||||
#include <libumskt/libumskt.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <fmt/color.h>
|
||||
#include <fmt/core.h>
|
||||
#include <fmt/ranges.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// fmt <-> json linkage
|
||||
template <> struct fmt::formatter<json> : formatter<string_view>
|
||||
{
|
||||
auto format(const json &j, format_context &ctx) const
|
||||
{
|
||||
if (j.is_string())
|
||||
{
|
||||
return formatter<string_view>::format(j.get<std::string>(), ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::stringstream s;
|
||||
s << j;
|
||||
return formatter<string_view>::format(s.str(), ctx);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#include "../libumskt/confid/confid.h"
|
||||
#include "../libumskt/libumskt.h"
|
||||
#include "../libumskt/pidgen2/PIDGEN2.h"
|
||||
#include "../libumskt/pidgen3/BINK1998.h"
|
||||
#include "../libumskt/pidgen3/BINK2002.h"
|
||||
#include "../libumskt/pidgen3/PIDGEN3.h"
|
||||
#include "help.h"
|
||||
|
||||
#ifndef UMSKTCLI_VERSION_STRING
|
||||
#define UMSKTCLI_VERSION_STRING "unknown version-dirty"
|
||||
#endif
|
||||
|
||||
class CLI
|
||||
{
|
||||
std::string pKey;
|
||||
DWORD32 count, total, iBinkID;
|
||||
|
||||
struct Options
|
||||
{
|
||||
int argc;
|
||||
char **argv;
|
||||
|
||||
std::string binkID;
|
||||
std::string keysFilename;
|
||||
std::string installationID;
|
||||
std::string keyToCheck;
|
||||
std::string productID;
|
||||
std::string authInfo;
|
||||
std::string productCode;
|
||||
std::string productFlavour;
|
||||
|
||||
Integer channelID;
|
||||
Integer serial;
|
||||
DWORD32 numKeys;
|
||||
|
||||
BOOL oem;
|
||||
BOOL upgrade;
|
||||
BOOL verbose;
|
||||
BOOL help;
|
||||
BOOL error;
|
||||
BOOL list;
|
||||
|
||||
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;
|
||||
|
||||
public:
|
||||
CLI()
|
||||
{
|
||||
count = 0;
|
||||
total = options.numKeys;
|
||||
}
|
||||
|
||||
static std::array<CLIHelpOptions, CLIHelpOptionID_END> helpOptions;
|
||||
static json keys;
|
||||
|
||||
static BYTE Init(int argv, char *argc[]);
|
||||
static void SetHelpText();
|
||||
|
||||
static BOOL loadJSON(const fs::path &filename);
|
||||
static BOOL loadEmbeddedJSON();
|
||||
|
||||
static CLIHandlerFunc DisplayHelp;
|
||||
static CLIHandlerFunc DisplayErrorMessage;
|
||||
static CLIHandlerFunc SetVerboseOption;
|
||||
static CLIHandlerFunc SetDebugOption;
|
||||
static CLIHandlerFunc SetListOption;
|
||||
static CLIHandlerFunc SetOEMOption;
|
||||
static CLIHandlerFunc SetUpgradeOption;
|
||||
static CLIHandlerFunc SetFileOption;
|
||||
static CLIHandlerFunc SetNumberOption;
|
||||
static CLIHandlerFunc SetChannelIDOption;
|
||||
static CLIHandlerFunc SetBINKOption;
|
||||
static CLIHandlerFunc SetSerialOption;
|
||||
static CLIHandlerFunc SetActivationIDOption;
|
||||
static CLIHandlerFunc SetProductIDOption;
|
||||
static CLIHandlerFunc SetValidateOption;
|
||||
static CLIHandlerFunc SetProductCodeOption;
|
||||
static CLIHandlerFunc SetFlavourOption;
|
||||
static CLIHandlerFunc SetAuthDataOption;
|
||||
|
||||
static BOOL parseCommandLine();
|
||||
static BOOL processOptions();
|
||||
static BOOL processListCommand();
|
||||
|
||||
BOOL InitPIDGEN3(PIDGEN3 *p3);
|
||||
BOOL InitConfirmationID(ConfirmationID &confid);
|
||||
|
||||
BOOL PIDGenerate();
|
||||
BOOL PIDValidate();
|
||||
|
||||
BOOL PIDGEN2Generate(PIDGEN2 &p2);
|
||||
BOOL PIDGEN2Validate(PIDGEN2 &p2);
|
||||
BOOL PIDGEN3Generate(PIDGEN3 *p3);
|
||||
BOOL PIDGEN3Validate(PIDGEN3 *p3);
|
||||
BOOL ConfirmationIDGenerate();
|
||||
|
||||
int Run();
|
||||
};
|
||||
|
||||
#endif // UMSKT_CLI_H
|
127
src/cli/confirmationid.cpp
Normal file
127
src/cli/confirmationid.cpp
Normal file
@ -0,0 +1,127 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2024 UMSKT Contributors (et.al.)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @FileCreated by Neo on 02/18/2024
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#include "cli.h"
|
||||
|
||||
/**
|
||||
*
|
||||
* @param confid
|
||||
* @return success
|
||||
*/
|
||||
BOOL CLI::InitConfirmationID(ConfirmationID &confid)
|
||||
{
|
||||
if (!keys["products"][options.productCode].contains("meta") ||
|
||||
!keys["products"][options.productCode]["meta"].contains("activation"))
|
||||
{
|
||||
fmt::print("ERROR: product flavour \"{}\" does not have known activation values", options.productCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto meta = keys["products"][options.productCode]["meta"]["activation"];
|
||||
|
||||
if (!keys["activation"].contains(meta["flavour"]))
|
||||
{
|
||||
fmt::print("ERROR: \"{}\" is an unknown activation flavour", meta["flavour"]);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto flavour = keys["activation"][meta["flavour"]];
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("{:->80}\n", "");
|
||||
fmt::print("Loaded the following hyperelliptic curve parameters: activation[{}]\n", meta["flavour"]);
|
||||
fmt::print("{:->80}\n", "");
|
||||
fmt::print("{:>7}: {}\n", "name", flavour["name"]);
|
||||
fmt::print("{:>7}: {}\n", "version", meta["version"]);
|
||||
fmt::print("{:>7}: {}\n", "Fp", flavour["p"]);
|
||||
fmt::print("{:>7}: [{}, {}, {},\n{:>10}{}, {}, {}]\n", "F[]", flavour["x"]["0"], flavour["x"]["1"],
|
||||
flavour["x"]["2"], "", flavour["x"]["3"], flavour["x"]["4"], flavour["x"]["5"]);
|
||||
fmt::print("{:>7}: {}\n", "INV", flavour["quotient"]);
|
||||
fmt::print("{:>7}: {}\n", "mqnr", flavour["non_residue"]);
|
||||
fmt::print("{:>7}: {}\n", "k", flavour["priv"]);
|
||||
fmt::print("{:>7}: {}\n", "IID", flavour["iid_key"]);
|
||||
fmt::print("\n");
|
||||
}
|
||||
|
||||
confid.LoadHyperellipticCurve(flavour["x"]["0"], flavour["x"]["1"], flavour["x"]["2"], flavour["x"]["3"],
|
||||
flavour["x"]["4"], flavour["x"]["5"], flavour["priv"], flavour["quotient"],
|
||||
flavour["non_residue"], flavour["iid_key"], meta["tags"].contains("xpbrand"),
|
||||
meta["tags"].contains("office"), meta["activation"]["version"]);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return success
|
||||
*/
|
||||
BOOL CLI::ConfirmationIDGenerate()
|
||||
{
|
||||
auto confid = ConfirmationID();
|
||||
std::string confirmation_id;
|
||||
|
||||
if (!InitConfirmationID(confid))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD32 err = confid.Generate(options.installationID, confirmation_id, options.productID);
|
||||
|
||||
if (err == SUCCESS)
|
||||
{
|
||||
fmt::print("{}\n", confirmation_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (err)
|
||||
{
|
||||
case ERR_TOO_SHORT:
|
||||
fmt::print("ERROR: Installation ID is too short.\n");
|
||||
break;
|
||||
|
||||
case ERR_TOO_LARGE:
|
||||
fmt::print("ERROR: Installation ID is too long.\n");
|
||||
break;
|
||||
|
||||
case ERR_INVALID_CHARACTER:
|
||||
fmt::print("ERROR: Invalid character in installation ID.\n");
|
||||
break;
|
||||
|
||||
case ERR_INVALID_CHECK_DIGIT:
|
||||
fmt::print("ERROR: Installation ID checksum failed. Please check that it is typed correctly.\n");
|
||||
break;
|
||||
|
||||
case ERR_UNKNOWN_VERSION:
|
||||
fmt::print("ERROR: Unknown installation ID version.\n");
|
||||
break;
|
||||
|
||||
case ERR_UNLUCKY:
|
||||
fmt::print("ERROR: Unable to generate valid confirmation ID.\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
fmt::print("Unknown error occurred during Confirmation ID generation: {}\n", err);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
668
src/cli/help.cpp
Normal file
668
src/cli/help.cpp
Normal file
@ -0,0 +1,668 @@
|
||||
/**
|
||||
* 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 01/02/2024
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#include "cli.h"
|
||||
|
||||
// define static storage
|
||||
std::array<CLIHelpOptions, CLIHelpOptionID_END> CLI::helpOptions;
|
||||
|
||||
void CLI::SetHelpText()
|
||||
{
|
||||
helpOptions[OPTION_HELP] = {"h", "help", "show this help text", false, "", &DisplayHelp};
|
||||
helpOptions[OPTION_HELP2] = {"?", "", "show this help text", false, "", &DisplayHelp};
|
||||
|
||||
helpOptions[OPTION_VERSION] = {"", "version", "show UMSKT CLI / libumskt versions", false, "", nullptr};
|
||||
|
||||
helpOptions[OPTION_VERBOSE] = {"v", "verbose", "enable verbose output", false, "", &SetVerboseOption};
|
||||
|
||||
helpOptions[OPTION_DEBUG] = {"d", "debug", "enable debug output", false, "", &SetDebugOption};
|
||||
|
||||
helpOptions[OPTION_FILE] = {
|
||||
"", "file", "(advanced) specify which keys JSON file to load", true, "[embedded file]", &SetFileOption};
|
||||
|
||||
helpOptions[OPTION_LIST] = {"l", "list", "list supported products", false, "", &SetListOption};
|
||||
|
||||
helpOptions[OPTION_PRODUCT] = {"p", "product", "[REQUIRED] which product to generate keys for",
|
||||
true, options.productCode, &SetProductCodeOption};
|
||||
|
||||
helpOptions[OPTION_FLAVOUR] = {
|
||||
"f", "flavour", "which product flavour to generate keys for (required in some instances)",
|
||||
true, "", &SetFlavourOption};
|
||||
|
||||
helpOptions[OPTION_NUMBER] = {"n", "number", "(PIDGEN only) number of keys to generate",
|
||||
true, "1", &SetNumberOption};
|
||||
|
||||
helpOptions[OPTION_ACTIVATIONID] = {
|
||||
"I", "installationID", "(activation only) installation ID used to generate confirmation ID", true,
|
||||
"", &SetActivationIDOption};
|
||||
|
||||
helpOptions[OPTION_ACTIVATIONPID] = {
|
||||
"P", "productID", "(Office activation only) product ID to generate confirmation ID for",
|
||||
true, "", &SetProductIDOption};
|
||||
|
||||
helpOptions[OPTION_OEM] = {"O", "oem", "(PIDGEN) generate an OEM key", false, "", &SetOEMOption};
|
||||
|
||||
helpOptions[OPTION_UPGRADE] = {"U", "upgrade", "(PIDGEN 3 only) generate an upgrade key",
|
||||
false, "", &SetUpgradeOption};
|
||||
|
||||
helpOptions[OPTION_BINK] = {"", "bink", "(advanced) override which BINK identifier to load",
|
||||
true, "", &SetBINKOption};
|
||||
|
||||
helpOptions[OPTION_CHANNELID] = {"", "channel", "(advanced) override which product channel to use",
|
||||
true, "", &SetChannelIDOption};
|
||||
|
||||
helpOptions[OPTION_SERIAL] = {
|
||||
"", "serial", "(advanced, PIDGEN 2/3 [BINK 1998] only) specify a serial to generate",
|
||||
true, "", &SetSerialOption};
|
||||
|
||||
helpOptions[OPTION_AUTHDATA] = {
|
||||
"a", "authdata", "(advanced, PIDGEN 3 [BINK 2002] only) specify a value for the authentication data field",
|
||||
true, "", &SetAuthDataOption};
|
||||
|
||||
helpOptions[OPTION_VALIDATE] = {
|
||||
"V", "validate", "validate a specified product ID against known BINKs and algorithms",
|
||||
true, "", &SetValidateOption};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return success
|
||||
*/
|
||||
BOOL CLI::parseCommandLine()
|
||||
{
|
||||
for (DWORD32 i = 1; i < options.argc; i++)
|
||||
{
|
||||
std::string arg = options.argv[i];
|
||||
|
||||
if (arg[0] == '-' || arg[0] == '/')
|
||||
{
|
||||
arg.erase(0, 1 + (arg[1] == '-' ? 1 : 0));
|
||||
}
|
||||
|
||||
if (arg.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (BYTE j = 0; j < CLIHelpOptionID_END; j++)
|
||||
{
|
||||
auto thisOption = helpOptions[j];
|
||||
if (arg != thisOption.Short && arg != thisOption.Long)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string nextarg;
|
||||
if (thisOption.hasArguments)
|
||||
{
|
||||
if (i == options.argc - 1)
|
||||
{
|
||||
options.error = true;
|
||||
goto CommandLineParseEnd;
|
||||
}
|
||||
else
|
||||
{
|
||||
i++;
|
||||
nextarg = std::string(options.argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (thisOption.handler == nullptr)
|
||||
{
|
||||
// prevent accidental segmentation faults
|
||||
continue;
|
||||
}
|
||||
|
||||
auto success = thisOption.handler(nextarg);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
options.error = true;
|
||||
goto CommandLineParseEnd;
|
||||
}
|
||||
if (options.help)
|
||||
{
|
||||
goto CommandLineParseEnd;
|
||||
}
|
||||
goto ParseNextCommandLineOption;
|
||||
}
|
||||
|
||||
fmt::print("unknown option: {}\n", arg);
|
||||
options.error = true;
|
||||
goto CommandLineParseEnd;
|
||||
|
||||
ParseNextCommandLineOption:
|
||||
continue;
|
||||
}
|
||||
|
||||
CommandLineParseEnd:
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("\n");
|
||||
}
|
||||
if (options.error)
|
||||
{
|
||||
DisplayErrorMessage("");
|
||||
}
|
||||
return !options.error;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return success
|
||||
*/
|
||||
BOOL CLI::processOptions()
|
||||
{
|
||||
if (!loadJSON(options.keysFilename))
|
||||
{
|
||||
options.error = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
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 == "TS00" || options.binkID == "TS01")
|
||||
{
|
||||
fmt::print("ERROR: Terminal Services BINKs (TS00 and TS01) 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
|
||||
|
||||
#if defined(UNICODE) || defined(__GNUC__)
|
||||
auto *leaf = "\u251C", *last = "\u2514", *line = "\u2500";
|
||||
#else
|
||||
auto *leaf = "\xC3", *last = "\xC0", *line = "\xC4";
|
||||
#endif
|
||||
|
||||
fmt::print("Listing known products and flavours: \n\n");
|
||||
|
||||
fmt::print("* The following product list uses this style of formatting:\n");
|
||||
fmt::print("{}: {} \n", fmt::styled("PRODUCT", fmt::emphasis::bold), "Product name");
|
||||
fmt::print("{}{}{} {}: {} \n", last, line, line, "FLAVOUR", "Flavour name");
|
||||
fmt::print("* Products that require a flavour are noted with {}\n\n",
|
||||
fmt::styled("(no default)", fmt::emphasis::bold));
|
||||
|
||||
for (auto const &i : keys["products"].items())
|
||||
{
|
||||
auto el = i.value();
|
||||
auto containsFlavours = el.contains("flavours");
|
||||
|
||||
fmt::print("{:<9} {} ", fmt::styled(fmt::format("{}:", i.key()), fmt::emphasis::bold), el["name"]);
|
||||
if (el.contains("BINK"))
|
||||
{
|
||||
fmt::print("{}\n", el["BINK"]);
|
||||
}
|
||||
else if (el["meta"].contains("default"))
|
||||
{
|
||||
fmt::print("(default: {} {})\n", fmt::styled(el["meta"]["default"], fmt::emphasis::bold),
|
||||
el["flavours"][el["meta"]["default"]]["BINK"]);
|
||||
}
|
||||
else if (el["meta"]["type"] == "PIDGEN3")
|
||||
{
|
||||
fmt::print("[{}]\n", el["meta"]["type"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::print("{}\n", fmt::styled("(no default)", fmt::emphasis::bold));
|
||||
}
|
||||
|
||||
if (containsFlavours)
|
||||
{
|
||||
auto flavours = el["flavours"];
|
||||
for (auto j = flavours.begin(); j != flavours.end(); j++)
|
||||
{
|
||||
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"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::print("{}\n", el2["BINK"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt::print("\n");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return success
|
||||
*/
|
||||
BOOL CLI::DisplayHelp(const std::string &)
|
||||
{
|
||||
options.help = true;
|
||||
fmt::print("usage: {} \n", options.argv[0]);
|
||||
|
||||
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);
|
||||
}
|
||||
else if (o.Long.empty())
|
||||
{
|
||||
fmt::print("\t-{} {:<15} {}", o.Short, "", o.HelpText);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::print("\t-{} --{:<15} {}", o.Short, o.Long, o.HelpText);
|
||||
}
|
||||
|
||||
if (!o.Default.empty())
|
||||
{
|
||||
fmt::print(" (defaults to {})", o.Default);
|
||||
}
|
||||
fmt::print("\n");
|
||||
}
|
||||
|
||||
fmt::print("\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::DisplayErrorMessage(const std::string &)
|
||||
{
|
||||
fmt::print("Error parsing command line options\n");
|
||||
DisplayHelp("");
|
||||
options.error = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
BOOL CLI::SetVerboseOption(const std::string &)
|
||||
{
|
||||
options.verbose = true;
|
||||
UMSKT::setVerboseOutput(stdout);
|
||||
fmt::print(UMSKT::verbose, "Enabling verbose option\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetDebugOption(const std::string &)
|
||||
{
|
||||
options.verbose = true;
|
||||
UMSKT::setDebugOutput(stdout);
|
||||
fmt::print(UMSKT::debug, "Enabling debug option\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetListOption(const std::string &)
|
||||
{
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting list option\n");
|
||||
}
|
||||
options.list = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetOEMOption(const std::string &)
|
||||
{
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting OEM option\n");
|
||||
}
|
||||
options.oem = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetUpgradeOption(const std::string &)
|
||||
{
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting upgrade option\n");
|
||||
}
|
||||
options.upgrade = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetFileOption(const std::string &file)
|
||||
{
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting file option to: {}\n", file);
|
||||
}
|
||||
options.keysFilename = file;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetNumberOption(const std::string &num)
|
||||
{
|
||||
auto nKeys = UMSKT::IntegerS(num);
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting generation number option to: {}\n", num);
|
||||
}
|
||||
|
||||
options.numKeys = nKeys.ConvertToLong();
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetChannelIDOption(const std::string &channum)
|
||||
{
|
||||
Integer channelID = UMSKT::IntegerS(channum);
|
||||
|
||||
// channel ids must be between 000 and 999
|
||||
if (channelID > PIDGEN::MaxChannelID)
|
||||
{
|
||||
fmt::print("ERROR: refusing to create a key with a Channel ID greater than 999\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting Channel ID option to: {}\n", channelID);
|
||||
}
|
||||
|
||||
options.channelID = channelID;
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetBINKOption(const std::string &bink)
|
||||
{
|
||||
auto strbinkid = std::string(bink);
|
||||
options.binkID = UMSKT::strtoupper(strbinkid);
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting BINK option to: {}\n", strbinkid);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetFlavourOption(const std::string &flavour)
|
||||
{
|
||||
auto strFlavour = UMSKT::strtoupper(flavour);
|
||||
options.productFlavour = strFlavour;
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting flavour option to: {}\n", strFlavour);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetSerialOption(const std::string &arg)
|
||||
{
|
||||
Integer Serial = UMSKT::IntegerS(arg);
|
||||
|
||||
// serials must be between 000000 and 999999
|
||||
if (Serial > PIDGEN::MaxSerial)
|
||||
{
|
||||
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);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetActivationIDOption(const std::string &aid)
|
||||
{
|
||||
options.installationID = aid;
|
||||
options.state = Options::STATE_CONFIRMATION_ID;
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting program state to Confirmation ID Generation\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetProductIDOption(const std::string &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)
|
||||
{
|
||||
options.keyToCheck = productID;
|
||||
options.state = Options::STATE_PIDGEN_VALIDATE;
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("Setting program state to PIDGEN Validation\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL CLI::SetProductCodeOption(const std::string &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);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
70
src/cli/help.h
Normal file
70
src/cli/help.h
Normal file
@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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 01/02/2024
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#ifndef UMSKT_HELP_H
|
||||
#define UMSKT_HELP_H
|
||||
|
||||
typedef BOOL CLIHandlerFunc(const std::string &);
|
||||
|
||||
/**
|
||||
* CLI Options List.
|
||||
*
|
||||
* Note: options are processed in the order found in the ENUM
|
||||
* order matters mostly for UX.
|
||||
*
|
||||
*/
|
||||
enum CLIHelpOptionIDs
|
||||
{
|
||||
OPTION_HELP,
|
||||
OPTION_HELP2,
|
||||
OPTION_VERSION,
|
||||
OPTION_VERBOSE,
|
||||
OPTION_DEBUG,
|
||||
OPTION_FILE,
|
||||
OPTION_LIST,
|
||||
OPTION_PRODUCT,
|
||||
OPTION_FLAVOUR,
|
||||
OPTION_NUMBER,
|
||||
OPTION_OEM,
|
||||
OPTION_UPGRADE,
|
||||
OPTION_ACTIVATIONID,
|
||||
OPTION_ACTIVATIONPID,
|
||||
OPTION_BINK,
|
||||
OPTION_CHANNELID,
|
||||
OPTION_SERIAL,
|
||||
OPTION_AUTHDATA,
|
||||
OPTION_VALIDATE,
|
||||
|
||||
CLIHelpOptionID_END
|
||||
};
|
||||
|
||||
struct CLIHelpOptions
|
||||
{
|
||||
std::string Short;
|
||||
std::string Long;
|
||||
std::string HelpText;
|
||||
BOOL hasArguments;
|
||||
std::string Default;
|
||||
CLIHandlerFunc *handler;
|
||||
};
|
||||
|
||||
#endif // UMSKT_HELP_H
|
3
src/cli/options.cpp
Normal file
3
src/cli/options.cpp
Normal file
@ -0,0 +1,3 @@
|
||||
//
|
||||
// Created by neo on 2/18/2024.
|
||||
//
|
319
src/cli/pidgen.cpp
Normal file
319
src/cli/pidgen.cpp
Normal file
@ -0,0 +1,319 @@
|
||||
/**
|
||||
* 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/18/2024
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#include "cli.h"
|
||||
|
||||
/**
|
||||
*
|
||||
* @param pidgen3
|
||||
* @return success
|
||||
*/
|
||||
BOOL CLI::InitPIDGEN3(PIDGEN3 *p3)
|
||||
{
|
||||
auto bink = keys["BINK"][options.binkID];
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("{:->80}\n", "");
|
||||
fmt::print("Loaded the following elliptic curve parameters: BINK[{}]\n", options.binkID);
|
||||
fmt::print("{:->80}\n", "");
|
||||
fmt::print("{:>6}: {}\n", "P", bink["p"]);
|
||||
fmt::print("{:>6}: {}\n", "a", bink["a"]);
|
||||
fmt::print("{:>6}: {}\n", "b", bink["b"]);
|
||||
fmt::print("{:>6}: [{},\n{:>9}{}]\n", "G[x,y]", bink["g"]["x"], "", bink["g"]["y"]);
|
||||
fmt::print("{:>6}: [{},\n{:>9}{}]\n", "K[x,y]", bink["pub"]["x"], "", bink["pub"]["y"]);
|
||||
fmt::print("{:>6}: {}\n", "n", bink["n"]);
|
||||
fmt::print("{:>6}: {}\n", "k", bink["priv"]);
|
||||
fmt::print("\n");
|
||||
}
|
||||
|
||||
p3->LoadEllipticCurve(options.binkID, bink["p"], bink["a"], bink["b"], bink["g"]["x"], bink["g"]["y"],
|
||||
bink["pub"]["x"], bink["pub"]["y"], bink["n"], bink["priv"]);
|
||||
|
||||
if (options.state != Options::APPLICATION_STATE::STATE_PIDGEN_GENERATE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (options.channelID.IsZero())
|
||||
{
|
||||
options.channelID.Randomize(UMSKT::rng, sizeof(DWORD32) * 8);
|
||||
}
|
||||
|
||||
options.channelID %= 999;
|
||||
p3->info.ChannelID = options.channelID;
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("> Channel ID: {:d}\n", options.channelID);
|
||||
}
|
||||
|
||||
if (options.serial.NotZero() && p3->checkFieldIsBink1998())
|
||||
{
|
||||
p3->info.Serial = options.serial;
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("> Serial {:d}\n", options.serial);
|
||||
}
|
||||
}
|
||||
else if (options.serial.NotZero() && !p3->checkFieldIsBink1998())
|
||||
{
|
||||
fmt::print("Warning: Discarding user-supplied serial for BINK2002\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param pidgen2
|
||||
* @return success
|
||||
*/
|
||||
BOOL CLI::PIDGEN2Generate(PIDGEN2 &p2)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param pidgen2
|
||||
* @return success
|
||||
*/
|
||||
BOOL CLI::PIDGEN2Validate(PIDGEN2 &p2)
|
||||
{
|
||||
std::string product_key;
|
||||
|
||||
if (!p2.ValidateKeyString(options.keyToCheck, product_key))
|
||||
{
|
||||
fmt::print("ERROR: Product key is in an incorrect format!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
fmt::print("{}\n", p2.StringifyKey(product_key));
|
||||
|
||||
if (!p2.Validate(product_key))
|
||||
{
|
||||
fmt::print("ERROR: Product key is invalid! Wrong BINK ID?\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
fmt::print("Key validated successfully!\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return success
|
||||
*/
|
||||
BOOL CLI::PIDGEN3Generate(PIDGEN3 *p3)
|
||||
{
|
||||
// raw PID/serial value
|
||||
Integer serialRnd;
|
||||
|
||||
if (p3->checkFieldIsBink1998())
|
||||
{
|
||||
if (options.serial.NotZero())
|
||||
{
|
||||
// using user-provided serial
|
||||
serialRnd = options.serial;
|
||||
}
|
||||
else
|
||||
{
|
||||
// generate a random number to use as a serial
|
||||
serialRnd.Randomize(UMSKT::rng, sizeof(DWORD32) * 8);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("> AuthInfo: {:d}\n", p3->info.AuthInfo);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
p3->info.Serial = serialRnd;
|
||||
}
|
||||
|
||||
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);
|
||||
if (isValid)
|
||||
{
|
||||
fmt::print(p3->StringifyKey(pKey));
|
||||
if (i <= total - 1 || options.verbose)
|
||||
{
|
||||
fmt::print("\n");
|
||||
}
|
||||
count += isValid;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("{} [Invalid]", p3->StringifyKey(pKey));
|
||||
if (i <= total - 1)
|
||||
{
|
||||
fmt::print("\n");
|
||||
}
|
||||
}
|
||||
total++; // queue a redo, basically
|
||||
}
|
||||
}
|
||||
|
||||
if (options.verbose)
|
||||
{
|
||||
fmt::print("\nSuccess count: {}/{}\n", count, total);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return success
|
||||
*/
|
||||
BOOL CLI::PIDGEN3Validate(PIDGEN3 *p3)
|
||||
{
|
||||
std::string product_key;
|
||||
|
||||
if (!p3->ValidateKeyString(options.keyToCheck, product_key))
|
||||
{
|
||||
fmt::print("ERROR: Product key is in an incorrect format!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
fmt::print("{}\n", p3->StringifyKey(product_key));
|
||||
|
||||
if (!p3->Validate(product_key))
|
||||
{
|
||||
fmt::print("ERROR: Product key is invalid! Wrong BINK ID?\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
fmt::print("Key validated successfully!\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return success
|
||||
*/
|
||||
BOOL CLI::PIDGenerate()
|
||||
{
|
||||
BOOL retval = false;
|
||||
|
||||
if (options.pidgenversion == Options::PIDGEN_VERSION::PIDGEN_2)
|
||||
{
|
||||
auto p2 = PIDGEN2();
|
||||
retval = PIDGEN2Generate(p2);
|
||||
return retval;
|
||||
}
|
||||
else if (options.pidgenversion == Options::PIDGEN_VERSION::PIDGEN_3)
|
||||
{
|
||||
auto bink = keys["BINK"][options.binkID];
|
||||
|
||||
auto p3 = PIDGEN3::Factory(bink["p"]);
|
||||
InitPIDGEN3(p3);
|
||||
retval = PIDGEN3Generate(p3);
|
||||
|
||||
delete p3;
|
||||
return retval;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return isValid
|
||||
*/
|
||||
BOOL CLI::PIDValidate()
|
||||
{
|
||||
BOOL retval = false;
|
||||
|
||||
if (options.pidgenversion == Options::PIDGEN_VERSION::PIDGEN_2)
|
||||
{
|
||||
auto p2 = PIDGEN2();
|
||||
retval = PIDGEN2Validate(p2);
|
||||
return retval;
|
||||
}
|
||||
else if (options.pidgenversion == Options::PIDGEN_VERSION::PIDGEN_3)
|
||||
{
|
||||
auto bink = keys["BINK"][options.binkID];
|
||||
|
||||
auto p3 = PIDGEN3::Factory(bink["p"]);
|
||||
InitPIDGEN3(p3);
|
||||
retval = PIDGEN3Validate(p3);
|
||||
|
||||
delete p3;
|
||||
return retval;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
* 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
|
||||
@ -16,58 +16,142 @@
|
||||
* 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 6/6/2023
|
||||
* @FileCreated by Neo on 06/06/2023
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#ifndef UMSKT_CONFID_H
|
||||
#define UMSKT_CONFID_H
|
||||
|
||||
#include "../libumskt.h"
|
||||
#include <libumskt/libumskt.h>
|
||||
|
||||
// Confirmation ID generator constants
|
||||
#define SUCCESS 0
|
||||
#define ERR_TOO_SHORT 1
|
||||
#define ERR_TOO_LARGE 2
|
||||
#define ERR_INVALID_CHARACTER 3
|
||||
#define ERR_INVALID_CHECK_DIGIT 4
|
||||
#define ERR_UNKNOWN_VERSION 5
|
||||
#define ERR_UNLUCKY 6
|
||||
enum CONFIRMATION_ID_STATUS
|
||||
{
|
||||
SUCCESS = 0,
|
||||
ERR_TOO_SHORT = 1,
|
||||
ERR_TOO_LARGE = 2,
|
||||
ERR_INVALID_CHARACTER = 3,
|
||||
ERR_INVALID_CHECK_DIGIT = 4,
|
||||
ERR_UNKNOWN_VERSION = 5,
|
||||
ERR_UNLUCKY = 6
|
||||
};
|
||||
|
||||
#define BAD 0xFFFFFFFFFFFFFFFFull
|
||||
|
||||
typedef struct {
|
||||
QWORD u[2];
|
||||
QWORD v[2];
|
||||
typedef struct
|
||||
{
|
||||
Q_OWORD u, v;
|
||||
} TDivisor;
|
||||
|
||||
EXPORT class ConfirmationID {
|
||||
static int calculateCheckDigit(int pid);
|
||||
static QWORD residue_add(QWORD x, QWORD y);
|
||||
static QWORD residue_sub(QWORD x, QWORD y);
|
||||
static QWORD __umul128(QWORD a, QWORD b, QWORD* hi);
|
||||
static QWORD ui128_quotient_mod(QWORD lo, QWORD hi);
|
||||
static QWORD residue_mul(QWORD x, QWORD y);
|
||||
static QWORD residue_pow(QWORD x, QWORD y);
|
||||
static QWORD inverse(QWORD u, QWORD v);
|
||||
static QWORD residue_inv(QWORD x);
|
||||
static QWORD residue_sqrt(QWORD what);
|
||||
static int find_divisor_v(TDivisor* d);
|
||||
static int polynomial_mul(int adeg, const QWORD a[], int bdeg, const QWORD b[], int resultprevdeg, QWORD result[]);
|
||||
static int polynomial_div_monic(int adeg, QWORD a[], int bdeg, const QWORD b[], QWORD* quotient);
|
||||
static void polynomial_xgcd(int adeg, const QWORD a[3], int bdeg, const QWORD b[3], int* pgcddeg, QWORD gcd[3], int* pmult1deg, QWORD mult1[3], int* pmult2deg, QWORD mult2[3]);
|
||||
static int u2poly(const TDivisor* src, QWORD polyu[3], QWORD polyv[2]);
|
||||
static void divisor_add(const TDivisor* src1, const TDivisor* src2, TDivisor* dst);
|
||||
static void divisor_mul(const TDivisor* src, QWORD mult, TDivisor* dst);
|
||||
static void divisor_mul128(const TDivisor* src, QWORD mult_lo, QWORD mult_hi, TDivisor* dst);
|
||||
static unsigned rol(unsigned x, int shift);
|
||||
static void sha1_single_block(unsigned char input[64], unsigned char output[20]);
|
||||
static void decode_iid_new_version(unsigned char* iid, unsigned char* hwid, int* version);
|
||||
static void Mix(unsigned char* buffer, size_t bufSize, const unsigned char* key, size_t keySize);
|
||||
static void Unmix(unsigned char* buffer, size_t bufSize, const unsigned char* key, size_t keySize);
|
||||
class EXPORT ConfirmationID : public UMSKT
|
||||
{
|
||||
QWORD MOD = 0, NON_RESIDUE = 0;
|
||||
QWORD curve[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
|
||||
Q_OWORD privateKey;
|
||||
|
||||
BYTE iid_key[4] = {0x0, 0x0, 0x0, 0x0};
|
||||
BOOL isOffice = false, isXPBrand = false;
|
||||
unsigned flagVersion = 0;
|
||||
|
||||
DWORD32 calculateCheckDigit(DWORD32 pid);
|
||||
void decode_iid_new_version(BYTE *iid, BYTE *hwid, DWORD32 *version);
|
||||
void Mix(BYTE *buffer, BYTE bufSize, const BYTE *key, BYTE keySize);
|
||||
void Unmix(BYTE *buffer, BYTE bufSize, const BYTE *key, BYTE keySize);
|
||||
|
||||
class Divisor
|
||||
{
|
||||
ConfirmationID *parent;
|
||||
|
||||
public:
|
||||
static int Generate(const char* installation_id_str, char confirmation_id[49], int mode, std::string productid);
|
||||
//EXPORT static int CLIRun();
|
||||
explicit Divisor(ConfirmationID *p)
|
||||
{
|
||||
parent = p;
|
||||
}
|
||||
|
||||
int find_divisor_v(TDivisor *d);
|
||||
void add(const TDivisor *src1, const TDivisor *src2, TDivisor *dst);
|
||||
void mul(const TDivisor *src, QWORD mult, TDivisor *dst);
|
||||
void mul128(const TDivisor *src, QWORD mult_lo, QWORD mult_hi, TDivisor *dst);
|
||||
int u2poly(const TDivisor *src, QWORD polyu[3], QWORD polyv[2]);
|
||||
};
|
||||
friend class Divisor;
|
||||
|
||||
class Residue
|
||||
{
|
||||
ConfirmationID *parent;
|
||||
|
||||
public:
|
||||
explicit Residue(ConfirmationID *p)
|
||||
{
|
||||
parent = p;
|
||||
}
|
||||
|
||||
QWORD add(QWORD x, QWORD y);
|
||||
QWORD sub(QWORD x, QWORD y);
|
||||
QWORD __umul128(QWORD a, QWORD b, QWORD *hi);
|
||||
QWORD ui128_quotient_mod(QWORD lo, QWORD hi);
|
||||
QWORD mul(QWORD x, QWORD y);
|
||||
QWORD pow(QWORD x, QWORD y);
|
||||
QWORD inverse(QWORD u, QWORD v);
|
||||
QWORD inv(QWORD x);
|
||||
QWORD sqrt(QWORD what);
|
||||
};
|
||||
friend class Residue;
|
||||
|
||||
class Polynomial
|
||||
{
|
||||
ConfirmationID *parent;
|
||||
|
||||
public:
|
||||
explicit Polynomial(ConfirmationID *p)
|
||||
{
|
||||
parent = p;
|
||||
}
|
||||
|
||||
int mul(int adeg, const QWORD a[], int bdeg, const QWORD b[], int resultprevdeg, QWORD result[]);
|
||||
int div_monic(int adeg, QWORD a[], int bdeg, const QWORD b[], QWORD *quotient);
|
||||
void xgcd(int adeg, const QWORD a[3], int bdeg, const QWORD b[3], int *pgcddeg, QWORD gcd[3], int *pmult1deg,
|
||||
QWORD mult1[3], int *pmult2deg, QWORD mult2[3]);
|
||||
};
|
||||
friend class Polynomial;
|
||||
|
||||
Residue *residue;
|
||||
Polynomial *polynomial;
|
||||
Divisor *divisor;
|
||||
|
||||
public:
|
||||
ConfirmationID()
|
||||
{
|
||||
residue = new Residue(this);
|
||||
polynomial = new Polynomial(this);
|
||||
divisor = new Divisor(this);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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 *f, Q_OWORD priv, QWORD modulus, QWORD nonresidue, DWORD32 iidkey, 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;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // UMSKT_CONFID_H
|
||||
|
55
src/libumskt/confid/confid_unittest.cpp
Normal file
55
src/libumskt/confid/confid_unittest.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2024 UMSKT Contributors (et.al.)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @FileCreated by Neo on 02/20/2024
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#include "confid.h"
|
||||
#include <libumskt/libumskt_unittest.h>
|
||||
|
||||
/**
|
||||
* ConfirmationID must not be an abstract class.
|
||||
*/
|
||||
TEST(TestConfirmationID, InstantiateConfirmationID)
|
||||
{
|
||||
auto p3 = new ConfirmationID();
|
||||
ASSERT_NE(p3, nullptr);
|
||||
delete p3;
|
||||
}
|
||||
|
||||
class TestConfirmationID : public libumsktUnitTests
|
||||
{
|
||||
protected:
|
||||
ConfirmationID *cid;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
cid = new ConfirmationID();
|
||||
cid->LoadHyperellipticCurve()
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
if (cid != nullptr)
|
||||
{
|
||||
delete cid;
|
||||
cid = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
397
src/libumskt/confid/divisor.cpp
Normal file
397
src/libumskt/confid/divisor.cpp
Normal file
@ -0,0 +1,397 @@
|
||||
/**
|
||||
* 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 12/05/2023
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#include "confid.h"
|
||||
|
||||
/**
|
||||
*
|
||||
* @param d
|
||||
* @return
|
||||
*/
|
||||
int ConfirmationID::ConfirmationID::Divisor::find_divisor_v(TDivisor *d)
|
||||
{
|
||||
// u | v^2 - curve
|
||||
// u = u0 + u1*x + x^2
|
||||
// curve%u = f0 + f1*x
|
||||
QWORD v1, f2[6];
|
||||
|
||||
for (BYTE i = 0; i < 6; i++)
|
||||
{
|
||||
f2[i] = parent->curve[i];
|
||||
}
|
||||
|
||||
const QWORD u0 = d->u.qword[0], u1 = d->u.qword[1];
|
||||
for (BYTE j = 4; j--;)
|
||||
{
|
||||
f2[j] = parent->residue->sub(f2[j], parent->residue->mul(u0, f2[j + 2]));
|
||||
f2[j + 1] = parent->residue->sub(f2[j + 1], parent->residue->mul(u1, f2[j + 2]));
|
||||
f2[j + 2] = 0;
|
||||
}
|
||||
|
||||
// v = v0 + v1*x
|
||||
// u | (v0^2 - f0) + (2*v0*v1 - f1)*x + v1^2*x^2 = u0*v1^2 + u1*v1^2*x + v1^2*x^2
|
||||
// v0^2 - f0 = u0*v1^2
|
||||
// 2*v0*v1 - f1 = u1*v1^2
|
||||
// v0^2 = f0 + u0*v1^2 = (f1 + u1*v1^2)^2 / (2*v1)^2
|
||||
// (f1^2) + 2*(f1*u1-2*f0) * v1^2 + (u1^2-4*u0) * v1^4 = 0
|
||||
// v1^2 = ((2*f0-f1*u1) +- 2*sqrt(-f0*f1*u1 + f0^2 + f1^2*u0))) / (u1^2-4*u0)
|
||||
const QWORD f0 = f2[0], f1 = f2[1];
|
||||
const QWORD u0double = parent->residue->add(u0, u0);
|
||||
const QWORD coeff2 = parent->residue->sub(parent->residue->mul(u1, u1), parent->residue->add(u0double, u0double));
|
||||
const QWORD coeff1 = parent->residue->sub(parent->residue->add(f0, f0), parent->residue->mul(f1, u1));
|
||||
|
||||
if (coeff2 == 0)
|
||||
{
|
||||
if (coeff1 == 0)
|
||||
{
|
||||
if (f1 == 0)
|
||||
{
|
||||
// impossible
|
||||
// printf("bad curve(), double root detected\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
QWORD sqr = parent->residue->mul(parent->residue->mul(f1, f1),
|
||||
parent->residue->inv(parent->residue->add(coeff1, coeff1)));
|
||||
v1 = parent->residue->sqrt(sqr);
|
||||
if (v1 == BAD)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QWORD d = parent->residue->add(
|
||||
parent->residue->mul(f0, f0),
|
||||
parent->residue->mul(f1, parent->residue->sub(parent->residue->mul(f1, u0), parent->residue->mul(f0, u1))));
|
||||
d = parent->residue->sqrt(d);
|
||||
if (d == BAD)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
d = parent->residue->add(d, d);
|
||||
QWORD inv = parent->residue->inv(coeff2);
|
||||
QWORD root = parent->residue->mul(parent->residue->add(coeff1, d), inv);
|
||||
v1 = parent->residue->sqrt(root);
|
||||
if (v1 == BAD)
|
||||
{
|
||||
root = parent->residue->mul(parent->residue->sub(coeff1, d), inv);
|
||||
v1 = parent->residue->sqrt(root);
|
||||
if (v1 == BAD)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param src
|
||||
* @param polyu
|
||||
* @param polyv
|
||||
* @return
|
||||
*/
|
||||
int ConfirmationID::ConfirmationID::Divisor::u2poly(const TDivisor *src, QWORD polyu[3], QWORD polyv[2])
|
||||
{
|
||||
if (src->u.qword[1] != BAD)
|
||||
{
|
||||
polyu[0] = src->u.qword[0];
|
||||
polyu[1] = src->u.qword[1];
|
||||
polyu[2] = 1;
|
||||
|
||||
polyv[0] = src->v.qword[0];
|
||||
polyv[1] = src->v.qword[1];
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (src->u.qword[0] != BAD)
|
||||
{
|
||||
polyu[0] = src->u.qword[0];
|
||||
polyu[1] = 1;
|
||||
|
||||
polyv[0] = src->v.qword[0];
|
||||
polyv[1] = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
polyu[0] = 1;
|
||||
polyv[0] = 0;
|
||||
polyv[1] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param src1
|
||||
* @param src2
|
||||
* @param dst
|
||||
*/
|
||||
void ConfirmationID::Divisor::add(const TDivisor *src1, const TDivisor *src2, TDivisor *dst)
|
||||
{
|
||||
QWORD u1[3], u2[3], v1[2], v2[2];
|
||||
int u1deg = u2poly(src1, u1, v1);
|
||||
int u2deg = u2poly(src2, u2, v2);
|
||||
|
||||
// extended gcd: d1 = gcd(u1, u2) = e1*u1 + e2*u2
|
||||
int d1deg, e1deg, e2deg;
|
||||
QWORD d1[3], e1[3], e2[3];
|
||||
parent->polynomial->xgcd(u1deg, u1, u2deg, u2, &d1deg, d1, &e1deg, e1, &e2deg, e2);
|
||||
assert(e1deg <= 1);
|
||||
assert(e2deg <= 1);
|
||||
|
||||
// extended gcd again: d = gcd(d1, v1+v2) = c1*d1 + c2*(v1+v2)
|
||||
QWORD b[3] = {parent->residue->add(v1[0], v2[0]), parent->residue->add(v1[1], v2[1]), 0};
|
||||
int bdeg = (b[1] == 0 ? (b[0] == 0 ? -1 : 0) : 1);
|
||||
int ddeg, c1deg, c2deg;
|
||||
QWORD d[3], c1[3], c2[3];
|
||||
parent->polynomial->xgcd(d1deg, d1, bdeg, b, &ddeg, d, &c1deg, c1, &c2deg, c2);
|
||||
assert(c1deg <= 0);
|
||||
assert(c2deg <= 1);
|
||||
assert(ddeg >= 0);
|
||||
QWORD dmult = parent->residue->inv(d[ddeg]);
|
||||
int i;
|
||||
for (i = 0; i < ddeg; i++)
|
||||
{
|
||||
d[i] = parent->residue->mul(d[i], dmult);
|
||||
}
|
||||
|
||||
d[i] = 1;
|
||||
for (i = 0; i <= c1deg; i++)
|
||||
{
|
||||
c1[i] = parent->residue->mul(c1[i], dmult);
|
||||
}
|
||||
|
||||
for (i = 0; i <= c2deg; i++)
|
||||
{
|
||||
c2[i] = parent->residue->mul(c2[i], dmult);
|
||||
}
|
||||
|
||||
QWORD u[5];
|
||||
int udeg = parent->polynomial->mul(u1deg, u1, u2deg, u2, -1, u);
|
||||
// u is monic
|
||||
|
||||
QWORD v[7], tmp[7];
|
||||
int vdeg, tmpdeg;
|
||||
// c1*(e1*u1*v2 + e2*u2*v1) + c2*(v1*v2 + curve)
|
||||
// c1*(e1*u1*(v2-v1) + d1*v1) + c2*(v1*v2 + curve)
|
||||
v[0] = parent->residue->sub(v2[0], v1[0]);
|
||||
v[1] = parent->residue->sub(v2[1], v1[1]);
|
||||
tmpdeg = parent->polynomial->mul(e1deg, e1, 1, v, -1, tmp);
|
||||
vdeg = parent->polynomial->mul(u1deg, u1, tmpdeg, tmp, -1, v);
|
||||
vdeg = parent->polynomial->mul(d1deg, d1, 1, v1, vdeg, v);
|
||||
|
||||
for (i = 0; i <= vdeg; i++)
|
||||
{
|
||||
v[i] = parent->residue->mul(v[i], c1[0]);
|
||||
}
|
||||
|
||||
memcpy(tmp, parent->curve, 6 * sizeof(parent->curve[0]));
|
||||
tmpdeg = 5;
|
||||
tmpdeg = parent->polynomial->mul(1, v1, 1, v2, tmpdeg, tmp);
|
||||
vdeg = parent->polynomial->mul(c2deg, c2, tmpdeg, tmp, vdeg, v);
|
||||
|
||||
if (ddeg > 0)
|
||||
{
|
||||
assert(udeg >= 2 * ddeg);
|
||||
QWORD udiv[5];
|
||||
parent->polynomial->div_monic(udeg, u, ddeg, d, udiv);
|
||||
udeg -= ddeg;
|
||||
parent->polynomial->div_monic(udeg, udiv, ddeg, d, u);
|
||||
udeg -= ddeg;
|
||||
if (vdeg >= 0)
|
||||
{
|
||||
assert(vdeg >= ddeg);
|
||||
parent->polynomial->div_monic(vdeg, v, ddeg, d, udiv);
|
||||
vdeg -= ddeg;
|
||||
memcpy(v, udiv, (vdeg + 1) * sizeof(v[0]));
|
||||
}
|
||||
}
|
||||
|
||||
vdeg = parent->polynomial->div_monic(vdeg, v, udeg, u, nullptr);
|
||||
|
||||
while (udeg > 2)
|
||||
{
|
||||
assert(udeg <= 4);
|
||||
assert(vdeg <= 3);
|
||||
// u' = monic((curve-v^2)/u), v'=-v mod u'
|
||||
tmpdeg = parent->polynomial->mul(vdeg, v, vdeg, v, -1, tmp);
|
||||
for (i = 0; i <= tmpdeg && i <= 5; i++)
|
||||
{
|
||||
tmp[i] = parent->residue->sub(parent->curve[i], tmp[i]);
|
||||
}
|
||||
|
||||
for (; i <= tmpdeg; i++)
|
||||
{
|
||||
tmp[i] = parent->residue->sub(0, tmp[i]);
|
||||
}
|
||||
|
||||
for (; i <= 5; i++)
|
||||
{
|
||||
tmp[i] = parent->curve[i];
|
||||
}
|
||||
|
||||
tmpdeg = i - 1;
|
||||
QWORD udiv[5];
|
||||
parent->polynomial->div_monic(tmpdeg, tmp, udeg, u, udiv);
|
||||
|
||||
udeg = tmpdeg - udeg;
|
||||
QWORD mult = parent->residue->inv(udiv[udeg]);
|
||||
|
||||
for (i = 0; i < udeg; i++)
|
||||
{
|
||||
u[i] = parent->residue->mul(udiv[i], mult);
|
||||
}
|
||||
|
||||
u[i] = 1;
|
||||
|
||||
for (i = 0; i <= vdeg; i++)
|
||||
{
|
||||
v[i] = parent->residue->sub(0, v[i]);
|
||||
}
|
||||
|
||||
vdeg = parent->polynomial->div_monic(vdeg, v, udeg, u, nullptr);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(udeg == 0);
|
||||
dst->u.qword[0] = BAD;
|
||||
dst->u.qword[1] = BAD;
|
||||
dst->v.qword[0] = BAD;
|
||||
dst->v.qword[1] = BAD;
|
||||
}
|
||||
}
|
||||
|
||||
#define divisor_double(src, dst) add(src, src, dst)
|
||||
|
||||
/**
|
||||
*
|
||||
* @param src
|
||||
* @param mult
|
||||
* @param dst
|
||||
*/
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
||||
TDivisor cur = *src;
|
||||
while (!(mult & 1))
|
||||
{
|
||||
divisor_double(&cur, &cur);
|
||||
mult >>= 1;
|
||||
}
|
||||
|
||||
*dst = cur;
|
||||
while ((mult >>= 1) != 0)
|
||||
{
|
||||
divisor_double(&cur, &cur);
|
||||
if (mult & 1)
|
||||
{
|
||||
add(dst, &cur, dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param src
|
||||
* @param mult_lo
|
||||
* @param mult_hi
|
||||
* @param dst
|
||||
*/
|
||||
void ConfirmationID::Divisor::mul128(const TDivisor *src, QWORD mult_lo, QWORD mult_hi, TDivisor *dst)
|
||||
{
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
||||
TDivisor cur = *src;
|
||||
while (!(mult_lo & 1))
|
||||
{
|
||||
divisor_double(&cur, &cur);
|
||||
mult_lo >>= 1;
|
||||
if (mult_hi & 1)
|
||||
{
|
||||
mult_lo |= (1ULL << 63);
|
||||
}
|
||||
mult_hi >>= 1;
|
||||
}
|
||||
|
||||
*dst = cur;
|
||||
for (;;)
|
||||
{
|
||||
mult_lo >>= 1;
|
||||
if (mult_hi & 1)
|
||||
{
|
||||
mult_lo |= (1ULL << 63);
|
||||
}
|
||||
|
||||
mult_hi >>= 1;
|
||||
if (mult_lo == 0 && mult_hi == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
divisor_double(&cur, &cur);
|
||||
if (mult_lo & 1)
|
||||
{
|
||||
add(dst, &cur, dst);
|
||||
}
|
||||
}
|
||||
}
|
261
src/libumskt/confid/polynomial.cpp
Normal file
261
src/libumskt/confid/polynomial.cpp
Normal file
@ -0,0 +1,261 @@
|
||||
/**
|
||||
* 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 12/05/2023
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#include "confid.h"
|
||||
|
||||
/**
|
||||
* generic short slow code
|
||||
*
|
||||
* @param adeg
|
||||
* @param a
|
||||
* @param bdeg
|
||||
* @param b
|
||||
* @param resultprevdeg
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
int ConfirmationID::Polynomial::mul(int adeg, const QWORD a[], int bdeg, const QWORD b[], int resultprevdeg,
|
||||
QWORD result[])
|
||||
{
|
||||
if (adeg < 0 || bdeg < 0)
|
||||
{
|
||||
return resultprevdeg;
|
||||
}
|
||||
|
||||
int i, j;
|
||||
|
||||
for (i = resultprevdeg + 1; i <= adeg + bdeg; i++)
|
||||
{
|
||||
result[i] = 0;
|
||||
}
|
||||
|
||||
resultprevdeg = i - 1;
|
||||
|
||||
for (i = 0; i <= adeg; i++)
|
||||
{
|
||||
for (j = 0; j <= bdeg; j++)
|
||||
{
|
||||
result[i + j] = parent->residue->add(result[i + j], parent->residue->mul(a[i], b[j]));
|
||||
}
|
||||
}
|
||||
|
||||
while (resultprevdeg >= 0 && result[resultprevdeg] == 0)
|
||||
{
|
||||
--resultprevdeg;
|
||||
}
|
||||
|
||||
return resultprevdeg;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param adeg
|
||||
* @param a
|
||||
* @param bdeg
|
||||
* @param b
|
||||
* @param quotient
|
||||
* @return
|
||||
*/
|
||||
int ConfirmationID::Polynomial::div_monic(int adeg, QWORD a[], int bdeg, const QWORD b[], QWORD *quotient)
|
||||
{
|
||||
assert(bdeg >= 0);
|
||||
assert(b[bdeg] == 1);
|
||||
int i, j;
|
||||
|
||||
for (i = adeg - bdeg; i >= 0; i--)
|
||||
{
|
||||
QWORD q = a[i + bdeg];
|
||||
if (quotient)
|
||||
{
|
||||
quotient[i] = q;
|
||||
}
|
||||
for (j = 0; j < bdeg; j++)
|
||||
{
|
||||
a[i + j] = parent->residue->sub(a[i + j], parent->residue->mul(q, b[j]));
|
||||
}
|
||||
a[i + j] = 0;
|
||||
}
|
||||
|
||||
i += bdeg;
|
||||
while (i >= 0 && a[i] == 0)
|
||||
{
|
||||
i--;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param adeg
|
||||
* @param a
|
||||
* @param bdeg
|
||||
* @param b
|
||||
* @param pgcddeg
|
||||
* @param gcd
|
||||
* @param pmult1deg
|
||||
* @param mult1
|
||||
* @param pmult2deg
|
||||
* @param mult2
|
||||
*/
|
||||
void ConfirmationID::Polynomial::xgcd(int adeg, const QWORD a[3], int bdeg, const QWORD b[3], int *pgcddeg,
|
||||
QWORD gcd[3], int *pmult1deg, QWORD mult1[3], int *pmult2deg, QWORD mult2[3])
|
||||
{
|
||||
int sdeg = -1;
|
||||
QWORD s[3] = {0, 0, 0};
|
||||
|
||||
int mult1deg = 0;
|
||||
mult1[0] = 1;
|
||||
mult1[1] = 0;
|
||||
mult1[2] = 0;
|
||||
|
||||
int tdeg = 0;
|
||||
QWORD t[3] = {1, 0, 0};
|
||||
|
||||
int mult2deg = -1;
|
||||
mult2[0] = 0;
|
||||
mult2[1] = 0;
|
||||
mult2[2] = 0;
|
||||
|
||||
int rdeg = bdeg;
|
||||
QWORD r[3] = {b[0], b[1], b[2]};
|
||||
|
||||
int gcddeg = adeg;
|
||||
gcd[0] = a[0];
|
||||
gcd[1] = a[1];
|
||||
gcd[2] = a[2];
|
||||
// s*u1 + t*u2 = r
|
||||
// mult1*u1 + mult2*u2 = gcd
|
||||
|
||||
while (rdeg >= 0)
|
||||
{
|
||||
if (rdeg > gcddeg)
|
||||
{
|
||||
unsigned tmp;
|
||||
int tmpi;
|
||||
|
||||
tmp = rdeg;
|
||||
rdeg = gcddeg;
|
||||
gcddeg = tmp;
|
||||
|
||||
tmpi = sdeg;
|
||||
sdeg = mult1deg;
|
||||
mult1deg = tmpi;
|
||||
|
||||
tmpi = tdeg;
|
||||
tdeg = mult2deg;
|
||||
mult2deg = tmpi;
|
||||
|
||||
QWORD tmp2;
|
||||
tmp2 = r[0];
|
||||
r[0] = gcd[0];
|
||||
gcd[0] = tmp2;
|
||||
|
||||
tmp2 = r[1];
|
||||
r[1] = gcd[1];
|
||||
gcd[1] = tmp2;
|
||||
|
||||
tmp2 = r[2];
|
||||
r[2] = gcd[2];
|
||||
gcd[2] = tmp2;
|
||||
|
||||
tmp2 = s[0];
|
||||
s[0] = mult1[0];
|
||||
mult1[0] = tmp2;
|
||||
|
||||
tmp2 = s[1];
|
||||
s[1] = mult1[1];
|
||||
mult1[1] = tmp2;
|
||||
|
||||
tmp2 = s[2];
|
||||
s[2] = mult1[2];
|
||||
mult1[2] = tmp2;
|
||||
|
||||
tmp2 = t[0];
|
||||
t[0] = mult2[0];
|
||||
mult2[0] = tmp2;
|
||||
|
||||
tmp2 = t[1];
|
||||
t[1] = mult2[1];
|
||||
mult2[1] = tmp2;
|
||||
|
||||
tmp2 = t[2];
|
||||
t[2] = mult2[2];
|
||||
mult2[2] = tmp2;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
int delta = gcddeg - rdeg;
|
||||
QWORD mult = parent->residue->mul(gcd[gcddeg], parent->residue->inv(r[rdeg]));
|
||||
// quotient = mult * x**delta
|
||||
|
||||
assert(rdeg + delta < 3);
|
||||
for (int i = 0; i <= rdeg; i++)
|
||||
{
|
||||
gcd[i + delta] = parent->residue->sub(gcd[i + delta], parent->residue->mul(mult, r[i]));
|
||||
}
|
||||
|
||||
while (gcddeg >= 0 && gcd[gcddeg] == 0)
|
||||
{
|
||||
gcddeg--;
|
||||
}
|
||||
assert(sdeg + delta < 3);
|
||||
|
||||
for (int i = 0; i <= sdeg; i++)
|
||||
{
|
||||
mult1[i + delta] = parent->residue->sub(mult1[i + delta], parent->residue->mul(mult, s[i]));
|
||||
}
|
||||
|
||||
if (mult1deg < sdeg + delta)
|
||||
{
|
||||
mult1deg = sdeg + delta;
|
||||
}
|
||||
|
||||
while (mult1deg >= 0 && mult1[mult1deg] == 0)
|
||||
{
|
||||
mult1deg--;
|
||||
}
|
||||
|
||||
assert(tdeg + delta < 3);
|
||||
|
||||
for (int i = 0; i <= tdeg; i++)
|
||||
{
|
||||
mult2[i + delta] = parent->residue->sub(mult2[i + delta], parent->residue->mul(mult, t[i]));
|
||||
}
|
||||
|
||||
if (mult2deg < tdeg + delta)
|
||||
{
|
||||
mult2deg = tdeg + delta;
|
||||
}
|
||||
|
||||
while (mult2deg >= 0 && mult2[mult2deg] == 0)
|
||||
{
|
||||
mult2deg--;
|
||||
}
|
||||
}
|
||||
|
||||
// d1 = gcd, e1 = mult1, e2 = mult2
|
||||
*pgcddeg = gcddeg;
|
||||
*pmult1deg = mult1deg;
|
||||
*pmult2deg = mult2deg;
|
||||
}
|
279
src/libumskt/confid/residue.cpp
Normal file
279
src/libumskt/confid/residue.cpp
Normal file
@ -0,0 +1,279 @@
|
||||
/**
|
||||
* 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 12/05/2023
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#include "confid.h"
|
||||
|
||||
#if defined(__x86_64__) || defined(_M_AMD64) || defined(__aarch64__) || (defined(__arm64__) && defined(__APPLE__))
|
||||
#ifdef __GNUC__
|
||||
QWORD ConfirmationID::Residue::__umul128(QWORD multiplier, QWORD multiplicand, QWORD *product_hi)
|
||||
{
|
||||
OWORD r = (OWORD)multiplier * (OWORD)multiplicand;
|
||||
*product_hi = r >> 64;
|
||||
return (QWORD)r;
|
||||
}
|
||||
#else // basically msvc
|
||||
/**
|
||||
*
|
||||
* @param multiplier
|
||||
* @param multiplicand
|
||||
* @param product_hi
|
||||
* @return
|
||||
*/
|
||||
QWORD ConfirmationID::Residue::__umul128(QWORD multiplier, QWORD multiplicand, QWORD *product_hi)
|
||||
{
|
||||
return _umul128(multiplier, multiplicand, product_hi);
|
||||
}
|
||||
#endif
|
||||
#elif defined(__i386__) || defined(_M_IX86) || defined(__arm__) || defined(__EMSCRIPTEN__)
|
||||
QWORD ConfirmationID::Residue::__umul128(QWORD multiplier, QWORD multiplicand, QWORD *product_hi)
|
||||
{
|
||||
// multiplier = ab = a * 2^32 + b
|
||||
// multiplicand = cd = c * 2^32 + d
|
||||
// ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d
|
||||
QWORD a = multiplier >> 32;
|
||||
QWORD b = multiplier & 0xFFFFFFFF;
|
||||
QWORD c = multiplicand >> 32;
|
||||
QWORD d = multiplicand & 0xFFFFFFFF;
|
||||
|
||||
// QWORD ac = a * c;
|
||||
QWORD ad = a * d;
|
||||
// QWORD bc = b * c;
|
||||
QWORD bd = b * d;
|
||||
|
||||
QWORD adbc = ad + (b * c);
|
||||
QWORD adbc_carry = adbc < ad ? 1 : 0;
|
||||
|
||||
// multiplier * multiplicand = product_hi * 2^64 + product_lo
|
||||
QWORD product_lo = bd + (adbc << 32);
|
||||
QWORD product_lo_carry = product_lo < bd ? 1 : 0;
|
||||
*product_hi = (a * c) + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry;
|
||||
|
||||
return product_lo;
|
||||
}
|
||||
#else
|
||||
#error Unknown architecture detected - please edit confid.cpp to tailor __umul128() your architecture
|
||||
#endif
|
||||
|
||||
/**
|
||||
*
|
||||
* @param lo
|
||||
* @param hi
|
||||
* @return
|
||||
*/
|
||||
QWORD ConfirmationID::Residue::ui128_quotient_mod(QWORD lo, QWORD hi)
|
||||
{
|
||||
// hi:lo * ceil(2**170/MOD) >> (64 + 64 + 42)
|
||||
QWORD prod1;
|
||||
__umul128(lo, parent->privateKey.qword[0], &prod1);
|
||||
|
||||
QWORD part1hi, part1lo = __umul128(lo, parent->privateKey.qword[1], &part1hi);
|
||||
QWORD part2hi, part2lo = __umul128(hi, parent->privateKey.qword[0], &part2hi);
|
||||
|
||||
QWORD sum1 = part1lo + part2lo;
|
||||
unsigned sum1carry = (sum1 < part1lo);
|
||||
sum1 += prod1;
|
||||
sum1carry += (sum1 < prod1);
|
||||
QWORD prod2 = part1hi + part2hi + sum1carry;
|
||||
|
||||
QWORD prod3hi, prod3lo = __umul128(hi, parent->privateKey.qword[1], &prod3hi);
|
||||
|
||||
prod3lo += prod2;
|
||||
prod3hi += (prod3lo < prod2);
|
||||
return (prod3lo >> 42) | (prod3hi << 22);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
* @return
|
||||
*/
|
||||
QWORD ConfirmationID::Residue::mul(QWORD x, QWORD y)
|
||||
{
|
||||
// * ceil(2**170/MOD) = 0x2d351 c6d04f8b|604fa6a1 c6346a87 for (p-1)*(p-1) max
|
||||
QWORD hi, lo = __umul128(x, y, &hi);
|
||||
QWORD quotient = ui128_quotient_mod(lo, hi);
|
||||
return lo - quotient * parent->MOD;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
* @return
|
||||
*/
|
||||
QWORD ConfirmationID::Residue::pow(QWORD x, QWORD y)
|
||||
{
|
||||
if (y == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
QWORD cur = x;
|
||||
while (!(y & 1))
|
||||
{
|
||||
cur = mul(cur, cur);
|
||||
y >>= 1;
|
||||
}
|
||||
|
||||
QWORD res = cur;
|
||||
while ((y >>= 1) != 0)
|
||||
{
|
||||
cur = mul(cur, cur);
|
||||
if (y & 1)
|
||||
{
|
||||
res = mul(res, cur);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
* @return
|
||||
*/
|
||||
QWORD ConfirmationID::Residue::add(QWORD x, QWORD y)
|
||||
{
|
||||
QWORD z = x + y;
|
||||
// z = z - (z >= MOD ? MOD : 0);
|
||||
if (z >= parent->MOD)
|
||||
{
|
||||
z -= parent->MOD;
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
* @return
|
||||
*/
|
||||
QWORD ConfirmationID::Residue::sub(QWORD x, QWORD y)
|
||||
{
|
||||
QWORD z = x - y;
|
||||
// z += (x < y ? MOD : 0);
|
||||
if (x < y)
|
||||
{
|
||||
z += parent->MOD;
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param u
|
||||
* @param v
|
||||
* @return
|
||||
*/
|
||||
QWORD ConfirmationID::Residue::inverse(QWORD u, QWORD v)
|
||||
{
|
||||
// assert(u);
|
||||
int64_t tmp;
|
||||
int64_t xu = 1, xv = 0;
|
||||
QWORD v0 = v;
|
||||
while (u > 1)
|
||||
{
|
||||
QWORD d = v / u;
|
||||
QWORD remainder = v % u;
|
||||
tmp = u;
|
||||
u = remainder;
|
||||
v = tmp;
|
||||
tmp = xu;
|
||||
xu = xv - d * xu;
|
||||
xv = tmp;
|
||||
}
|
||||
xu += (xu < 0 ? v0 : 0);
|
||||
return xu;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @return
|
||||
*/
|
||||
QWORD ConfirmationID::Residue::inv(QWORD x)
|
||||
{
|
||||
return inverse(x, parent->MOD);
|
||||
// return residue_pow(x, MOD - 2);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param what
|
||||
* @return
|
||||
*/
|
||||
QWORD ConfirmationID::Residue::sqrt(QWORD what)
|
||||
{
|
||||
if (!what)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
QWORD g = parent->NON_RESIDUE, z, y, r, x, b, t;
|
||||
QWORD e = 0, q = parent->MOD - 1;
|
||||
|
||||
while (!(q & 1))
|
||||
{
|
||||
e++, q >>= 1;
|
||||
}
|
||||
|
||||
z = pow(g, q);
|
||||
y = z;
|
||||
r = e;
|
||||
x = pow(what, (q - 1) / 2);
|
||||
b = mul(mul(what, x), x);
|
||||
x = mul(what, x);
|
||||
|
||||
while (b != 1)
|
||||
{
|
||||
QWORD m = 0, b2 = b;
|
||||
|
||||
do
|
||||
{
|
||||
m++;
|
||||
b2 = mul(b2, b2);
|
||||
} while (b2 != 1);
|
||||
|
||||
if (m == r)
|
||||
{
|
||||
return BAD;
|
||||
}
|
||||
|
||||
t = pow(y, 1 << (r - m - 1));
|
||||
y = mul(t, t);
|
||||
r = m;
|
||||
x = mul(x, t);
|
||||
b = mul(b, y);
|
||||
}
|
||||
|
||||
if (mul(x, x) != what)
|
||||
{
|
||||
// printf("internal error in sqrt\n");
|
||||
return BAD;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
64
src/libumskt/init.cpp
Normal file
64
src/libumskt/init.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* 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 06/25/2023
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#include <libumskt/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 __DJGPP__
|
||||
// this should be set up as early as possible
|
||||
uclock();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the filestream used for debugging
|
||||
*
|
||||
* @param input std::FILE
|
||||
*/
|
||||
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;
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
* 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
|
||||
@ -16,45 +16,232 @@
|
||||
* 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 6/25/2023
|
||||
* @FileCreated by Neo on 06/25/2023
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#include "libumskt.h"
|
||||
#include "confid/confid.h"
|
||||
#include "pidgen3/PIDGEN3.h"
|
||||
#include "pidgen3/BINK1998.h"
|
||||
#include "pidgen3/BINK2002.h"
|
||||
#include "pidgen2/PIDGEN2.h"
|
||||
#include <libumskt/confid/confid.h>
|
||||
#include <libumskt/libumskt.h>
|
||||
#include <libumskt/pidgen2/PIDGEN2.h>
|
||||
#include <libumskt/pidgen3/BINK1998.h>
|
||||
#include <libumskt/pidgen3/BINK2002.h>
|
||||
#include <libumskt/pidgen3/PIDGEN3.h>
|
||||
|
||||
FNEXPORT int ConfirmationID_Generate(const char* installation_id_str, char confirmation_id[49], int mode, std::string productid) {
|
||||
return ConfirmationID::Generate(installation_id_str, confirmation_id, mode, productid);
|
||||
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"
|
||||
* simply use the global vars allocated by *this* C++ runtime.
|
||||
* otherwise, assume that the input pointer is an ABI equivalent std::FILE
|
||||
*
|
||||
* @param char* or std::FILE "filestream"
|
||||
*/
|
||||
EXPORT BOOL UMSKT_SET_DEBUG_OUTPUT(void *filestream)
|
||||
{
|
||||
char buffer[7];
|
||||
memcpy(buffer, filestream, 6);
|
||||
buffer[6] = 0;
|
||||
auto buffstring = std::string(buffer);
|
||||
std::transform(buffstring.begin(), buffstring.end(), buffstring.begin(), ::tolower);
|
||||
|
||||
if (buffstring == "stdout")
|
||||
{
|
||||
UMSKT::debug = stdout;
|
||||
return true;
|
||||
}
|
||||
else if (buffstring == "stderr")
|
||||
{
|
||||
UMSKT::debug = stderr;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
UMSKT::debug = (std::FILE *)filestream;
|
||||
return true;
|
||||
}
|
||||
|
||||
FNEXPORT EC_GROUP* PIDGEN3_initializeEllipticCurve(char* pSel, char* aSel, char* bSel, char* generatorXSel, char* generatorYSel, char* publicKeyXSel, char* publicKeyYSel, EC_POINT *&genPoint, EC_POINT *&pubPoint) {
|
||||
return PIDGEN3::initializeEllipticCurve(pSel, aSel, bSel, generatorXSel, generatorYSel, publicKeyXSel, publicKeyYSel, genPoint, pubPoint);
|
||||
return false;
|
||||
}
|
||||
|
||||
FNEXPORT bool PIDGEN3_BINK1998_Verify(EC_GROUP *eCurve, EC_POINT *basePoint, EC_POINT *publicKey, char (&pKey)[25]) {
|
||||
return PIDGEN3::BINK1998::Verify(eCurve, basePoint, publicKey, pKey);
|
||||
// ---------------------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
* @param tag
|
||||
* @param value
|
||||
* @param valueSize
|
||||
* @return success
|
||||
*/
|
||||
EXPORT BOOL UMSKT_SET_TAG(UMSKT_TAG tag, char *value, size_t valueSize)
|
||||
{
|
||||
if (valueSize > sizeof(UMSKT_Value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FNEXPORT void PIDGEN3_BINK1998_Generate(EC_GROUP *eCurve, EC_POINT *basePoint, BIGNUM *genOrder, BIGNUM *privateKey, DWORD pSerial, BOOL pUpgrade,char (&pKey)[25]) {
|
||||
return PIDGEN3::BINK1998::Generate(eCurve, basePoint, genOrder, privateKey, pSerial, pUpgrade, pKey);
|
||||
// wipe/set the tag
|
||||
memset(&UMSKT::tags[tag], 0, sizeof(UMSKT_Value));
|
||||
memcpy(&UMSKT::tags[tag], value, valueSize);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FNEXPORT bool PIDGEN3_BINK2002_Verify(EC_GROUP *eCurve, EC_POINT *basePoint, EC_POINT *publicKey, char (&cdKey)[25]) {
|
||||
return PIDGEN3::BINK2002::Verify(eCurve, basePoint, publicKey, cdKey);
|
||||
EXPORT void UMSKT_RESET_TAGS()
|
||||
{
|
||||
UMSKT::tags.clear();
|
||||
}
|
||||
|
||||
FNEXPORT void PIDGEN3_BINK2002_Generate(EC_GROUP *eCurve, EC_POINT *basePoint, BIGNUM *genOrder, BIGNUM *privateKey, DWORD pChannelID, DWORD pAuthInfo, BOOL pUpgrade, char (&pKey)[25]) {
|
||||
return PIDGEN3::BINK2002::Generate(eCurve, basePoint, genOrder, privateKey, pChannelID, pAuthInfo, pUpgrade, pKey);
|
||||
// ---------------------------------------------
|
||||
|
||||
EXPORT void *CONFID_INIT()
|
||||
{
|
||||
auto cid = new ConfirmationID();
|
||||
|
||||
// cid->LoadHyperellipticCurve(0, 0, 0, 0, 0, 0, 0, 0, 0, false, false, 0);
|
||||
|
||||
return cid;
|
||||
}
|
||||
|
||||
FNEXPORT int PIDGEN2_GenerateRetail(char* channelID, char* &keyout) {
|
||||
return PIDGEN2::GenerateRetail(channelID, keyout);
|
||||
EXPORT BYTE CONFID_GENERATE(void *cidIn, const char *installation_id_str, char *&confirmation_id, char *productid)
|
||||
{
|
||||
ConfirmationID *cid;
|
||||
try
|
||||
{
|
||||
cid = static_cast<ConfirmationID *>(cidIn);
|
||||
}
|
||||
catch (const std::bad_cast &e)
|
||||
{
|
||||
fmt::print(UMSKT::debug, "{}: input is not a {} - {}", __FUNCTION__, e.what());
|
||||
return -1;
|
||||
}
|
||||
|
||||
FNEXPORT int PIDGEN2_GenerateOEM(char* year, char* day, char* oem, char* keyout) {
|
||||
return PIDGEN2::GenerateOEM(year, day, oem, keyout);
|
||||
for (auto const i : UMSKT::tags)
|
||||
{
|
||||
switch (i.first)
|
||||
{
|
||||
case UMSKT_tag_InstallationID:
|
||||
break;
|
||||
case UMSKT_tag_ProductID:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::string str, confid(confirmation_id), productids(productid);
|
||||
auto retval = cid->Generate(str, confid, productids);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
EXPORT void CONFID_END(void *cidIn)
|
||||
{
|
||||
auto *cid((ConfirmationID *)cidIn);
|
||||
delete cid;
|
||||
cid = nullptr;
|
||||
cidIn = nullptr;
|
||||
}
|
||||
|
||||
// ---------------------------------------------
|
||||
|
||||
EXPORT void *PIDGEN3_INIT(const char *binkid, 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)
|
||||
{
|
||||
PIDGEN3 *p3;
|
||||
|
||||
if (PIDGEN3::checkFieldStrIsBink1998(p))
|
||||
{
|
||||
p3 = new BINK1998();
|
||||
}
|
||||
else
|
||||
{
|
||||
p3 = new BINK2002();
|
||||
}
|
||||
|
||||
p3->LoadEllipticCurve(binkid, p, a, b, generatorX, generatorY, publicKeyX, publicKeyY, genOrder, privateKey);
|
||||
|
||||
return p3;
|
||||
}
|
||||
|
||||
EXPORT BOOL PIDGEN3_Generate(void *&ptrIn, char *&pKeyOut, int pKeySizeIn)
|
||||
{
|
||||
auto *p3((PIDGEN3 *)ptrIn);
|
||||
|
||||
for (auto const i : UMSKT::tags)
|
||||
{
|
||||
switch (i.first)
|
||||
{
|
||||
case UMSKT_tag_isUpgrade:
|
||||
p3->info.isUpgrade = i.second.boolean;
|
||||
break;
|
||||
case UMSKT_tag_ChannelID:
|
||||
p3->info.setChannelID(i.second.dword);
|
||||
break;
|
||||
case UMSKT_tag_Serial:
|
||||
p3->info.setSerial(i.second.dword);
|
||||
break;
|
||||
case UMSKT_tag_AuthData:
|
||||
p3->info.setAuthInfo(i.second.dword);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::string str;
|
||||
BOOL retval = p3->Generate(str);
|
||||
|
||||
assert(pKeySizeIn >= str.length() + NULL_TERMINATOR);
|
||||
|
||||
memcpy(pKeyOut, &str[0], str.length());
|
||||
pKeyOut[str.length()] = 0;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
EXPORT BOOL PIDGEN3_Validate(void *&ptrIn, char *pKeyIn)
|
||||
{
|
||||
auto *p3((PIDGEN3 *)ptrIn);
|
||||
std::string str(pKeyIn);
|
||||
|
||||
BOOL retval = p3->Validate(str);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
EXPORT void PIDGEN3_END(void *ptrIn)
|
||||
{
|
||||
auto *p3((PIDGEN3 *)ptrIn);
|
||||
delete p3;
|
||||
ptrIn = nullptr;
|
||||
p3 = nullptr;
|
||||
}
|
||||
|
||||
// ---------------------------------------------
|
||||
|
||||
EXPORT void *PIDGEN2_INIT()
|
||||
{
|
||||
auto p2 = new PIDGEN2();
|
||||
return p2;
|
||||
}
|
||||
|
||||
EXPORT BOOL PIDGEN2_GENERATE(void *ptrIn, char *&keyout)
|
||||
{
|
||||
auto p2 = (PIDGEN2 *)ptrIn;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
EXPORT void PIDGEN2_END(void *ptrIn)
|
||||
{
|
||||
auto p2 = (PIDGEN2 *)ptrIn;
|
||||
delete p2;
|
||||
p2 = nullptr;
|
||||
ptrIn = nullptr;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
* 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
|
||||
@ -16,58 +16,294 @@
|
||||
* 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 6/24/2023
|
||||
* @FileCreated by Neo on 06/24/2023
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#ifndef UMSKT_LIBUMSKT_H
|
||||
#define UMSKT_LIBUMSKT_H
|
||||
|
||||
#include "../typedefs.h"
|
||||
#include <typedefs.h>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#ifdef __DJGPP__
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/rand.h>
|
||||
#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 <fmt/core.h>
|
||||
#include <fmt/format.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) (DWORD)(*((n) + 0) | *((n) + 1) << 8 | *((n) + 2) << 16 | *((n) + 3) << 24)
|
||||
#define BYDWORD(n) (DWORD32)(*((n) + 0) | *((n) + 1) << 8 | *((n) + 2) << 16 | *((n) + 3) << 24)
|
||||
#define BITMASK(n) ((1ULL << (n)) - 1)
|
||||
|
||||
class UMSKT {
|
||||
public:
|
||||
static std::FILE* debug;
|
||||
class PIDGEN2;
|
||||
class PIDGEN3;
|
||||
class ConfigurationID;
|
||||
#ifndef LIBUMSKT_VERSION_STRING
|
||||
#define LIBUMSKT_VERSION_STRING "unknown version-dirty"
|
||||
#endif
|
||||
|
||||
static void setDebugOutput(std::FILE* input);
|
||||
enum ValueType
|
||||
{
|
||||
VALUE_BOOL,
|
||||
VALUE_WORD,
|
||||
VALUE_DWORD,
|
||||
VALUE_QWORD,
|
||||
VALUE_OWORD,
|
||||
VALUE_CHARPTR
|
||||
};
|
||||
|
||||
struct UMSKT_Value
|
||||
{
|
||||
ValueType type;
|
||||
union {
|
||||
BOOL boolean;
|
||||
WORD word;
|
||||
DWORD32 dword;
|
||||
QWORD qword;
|
||||
OWORD oword;
|
||||
char *chars;
|
||||
};
|
||||
};
|
||||
|
||||
enum UMSKT_TAG
|
||||
{
|
||||
UMSKT_tag_isOEM,
|
||||
UMSKT_tag_isUpgrade,
|
||||
UMSKT_tag_Year,
|
||||
UMSKT_tag_Day,
|
||||
UMSKT_tag_OEMID,
|
||||
UMSKT_tag_AuthData,
|
||||
UMSKT_tag_Serial,
|
||||
UMSKT_tag_ChannelID,
|
||||
UMSKT_tag_InstallationID,
|
||||
UMSKT_tag_ProductID
|
||||
};
|
||||
|
||||
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 Integer to Native type T where T is a concrete type
|
||||
*
|
||||
* @tparam T
|
||||
* @param in
|
||||
* @param buf
|
||||
* @return
|
||||
*/
|
||||
template <typename T = DWORD32> INLINE static T EncodeN(const Integer &in)
|
||||
{
|
||||
T buf;
|
||||
EncodeN(in, (BYTE *)&buf, sizeof(T));
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a random number into a Native concrete type
|
||||
*
|
||||
* @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 std::map<UMSKT_TAG, UMSKT_Value> tags;
|
||||
static CryptoPP::DefaultAutoSeededRNG rng;
|
||||
|
||||
static BOOL CONSTRUCT();
|
||||
|
||||
static void DESTRUCT()
|
||||
{
|
||||
if (debug != nullptr && debug != stdout && debug != stderr)
|
||||
{
|
||||
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);
|
||||
};
|
||||
|
||||
#endif // UMSKT_LIBUMSKT_H
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
* 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
|
||||
@ -16,26 +16,24 @@
|
||||
* 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 5/26/2023
|
||||
* @FileCreated by Neo on 02/19/2024
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
#ifndef UMSKT_LIBUMSKT_UNITTEST_H
|
||||
#define UMSKT_LIBUMSKT_UNITTEST_H
|
||||
|
||||
#ifndef UMSKT_HEADER_H
|
||||
#define UMSKT_HEADER_H
|
||||
#include <gtest/gtest.h>
|
||||
#include <libumskt/libumskt.h>
|
||||
|
||||
#include "typedefs.h"
|
||||
class libumsktUnitTests : public testing::Test
|
||||
{
|
||||
public:
|
||||
libumsktUnitTests()
|
||||
{
|
||||
// UMSKT::setVerboseOutput(stderr);
|
||||
// UMSKT::setDebugOutput(stderr);
|
||||
}
|
||||
~libumsktUnitTests() override = default;
|
||||
};
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
#endif //UMSKT_HEADER_H
|
||||
#endif // UMSKT_LIBUMSKT_UNITTEST_H
|
76
src/libumskt/pidgen.cpp
Normal file
76
src/libumskt/pidgen.cpp
Normal file
@ -0,0 +1,76 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
72
src/libumskt/pidgen.h
Normal file
72
src/libumskt/pidgen.h
Normal file
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 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:
|
||||
virtual ~PIDGEN() = default;
|
||||
static const Integer SEVEN;
|
||||
static const Integer TEN;
|
||||
static const Integer MaxChannelID;
|
||||
static const Integer MaxSerial;
|
||||
|
||||
virtual BOOL Generate(std::string &pKey)
|
||||
{
|
||||
throw std::runtime_error("PIDGEN::Generate() pure virtual function call");
|
||||
}
|
||||
|
||||
virtual BOOL Validate(const std::string &pKey)
|
||||
{
|
||||
throw std::runtime_error("PIDGEN::Validate() pure virtual function call");
|
||||
}
|
||||
|
||||
virtual std::string StringifyKey(const std::string &pKey)
|
||||
{
|
||||
throw std::runtime_error("PIDGEN::StringifyKey() pure virtual function call");
|
||||
}
|
||||
|
||||
virtual std::string StringifyProductID()
|
||||
{
|
||||
throw std::runtime_error("PIDGEN::StringifyProductID() pure virtual function call");
|
||||
}
|
||||
|
||||
virtual BOOL ValidateKeyString(const std::string &in_key, std::string &out_key)
|
||||
{
|
||||
throw std::runtime_error("PIDGEN::ValidateKeyString() pure virtual function call");
|
||||
}
|
||||
|
||||
Integer GenerateMod7(const Integer &in);
|
||||
BOOL isValidMod7(const Integer &in);
|
||||
};
|
||||
|
||||
#endif // UMSKT_PIDGEN_H
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
* 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
|
||||
@ -22,114 +22,294 @@
|
||||
|
||||
#include "PIDGEN2.h"
|
||||
|
||||
const char* channelIDBlacklist [7] = {"333", "444", "555", "666", "777", "888", "999"};
|
||||
const char* validYears[8] = { "95", "96", "97", "98", "99", "00", "01", "02"};
|
||||
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"};
|
||||
|
||||
bool PIDGEN2::isNumericString(char* input) {
|
||||
for(int i = 0; i < strlen(input); i++) {
|
||||
if (input[i] < '0' || input[i] > '9') {
|
||||
return false;
|
||||
/**
|
||||
* Generates a PID 2.0 key, output is placed in pKey
|
||||
*
|
||||
* @param pKey
|
||||
* @return true
|
||||
*/
|
||||
BOOL PIDGEN2::Generate(std::string &pKey)
|
||||
{
|
||||
Integer random;
|
||||
random.Randomize(rng, sizeof(DWORD32) * 8);
|
||||
|
||||
info.ChannelID = info.ChannelID % MaxChannelID;
|
||||
info.Serial = info.Serial % MaxSerial;
|
||||
|
||||
if (info.isOEM)
|
||||
{
|
||||
info.Day = info.Day % Integer(366);
|
||||
// info.Year = info.Year;
|
||||
|
||||
info.OEMID = (info.ChannelID * TEN) + (info.Serial / (MaxSerial / TEN));
|
||||
info.Serial %= (MaxSerial / TEN);
|
||||
|
||||
info.OEMID = (info.OEMID * TEN) + GenerateMod7(info.OEMID);
|
||||
|
||||
DWORD32 day = EncodeN(info.Day), year = EncodeN(info.Year), serial = EncodeN(info.Serial),
|
||||
oemid = EncodeN(info.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 = EncodeN(info.ChannelID), serial = EncodeN(info.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);
|
||||
|
||||
DWORD32 channelid = EncodeN(info.ChannelID), serial = EncodeN(info.Serial);
|
||||
|
||||
if (debug)
|
||||
{
|
||||
fmt::print("\n{:03d}-{:07d}\n", channelid, serial);
|
||||
}
|
||||
|
||||
pKey = fmt::format("{:03d}{:07d}", channelid, serial);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int PIDGEN2::addDigits(char* input) {
|
||||
int output = 0;
|
||||
/**
|
||||
* 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
|
||||
* @return
|
||||
*/
|
||||
BOOL PIDGEN2::Validate(const std::string &pKey)
|
||||
{
|
||||
std::string filtered;
|
||||
ValidateKeyString(pKey, filtered);
|
||||
|
||||
if (!isNumericString(input)) {
|
||||
return -1;
|
||||
bool bIsValidChannelID, bIsValidSerial, bIsValidOEMDay, bIsValidOEMYear, bIsValidOEMID;
|
||||
|
||||
switch (filtered.length())
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
for(int i = 0; i < strlen(input); i++) {
|
||||
output += input[i] - '0';
|
||||
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)
|
||||
{
|
||||
fmt::print("\n\nisValidChannelID: {} isValidSerial: {}\n", bIsValidChannelID, bIsValidSerial);
|
||||
}
|
||||
|
||||
return output;
|
||||
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);
|
||||
}
|
||||
|
||||
bool PIDGEN2::isValidChannelID(char* channelID) {
|
||||
if (strlen(channelID) > 3) {
|
||||
return false;
|
||||
}
|
||||
return bIsValidOEMDay && bIsValidOEMYear && bIsValidOEMID;
|
||||
|
||||
for (int i = 0; i <= 6; i++) {
|
||||
if (strcmp(channelID, channelIDBlacklist[i]) != 0) {
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
/**
|
||||
*
|
||||
* @param pKey
|
||||
* @return
|
||||
*/
|
||||
std::string PIDGEN2::StringifyKey(const std::string &pKey)
|
||||
{
|
||||
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 "";
|
||||
}
|
||||
}
|
||||
|
||||
bool PIDGEN2::isValidOEMID(char* OEMID) {
|
||||
if (!isNumericString(OEMID)) {
|
||||
/**
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param in_key
|
||||
* @param out_key
|
||||
* @return
|
||||
*/
|
||||
BOOL INLINE PIDGEN2::ValidateKeyString(const std::string &in_key, std::string &out_key)
|
||||
{
|
||||
std::copy_if(in_key.begin(), in_key.end(), std::back_inserter(out_key), [](char c) { return std::isdigit(c); });
|
||||
|
||||
return out_key.length() == KeySize::FPP || out_key.length() == KeySize::Office || out_key.length() == KeySize::OEM;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the Serial with check digit a valid serial?
|
||||
*
|
||||
* 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())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strlen(OEMID) > 5) {
|
||||
if (OEMID[0] != '0' || OEMID[1] != '0') {
|
||||
return false;
|
||||
}
|
||||
return isValidMod7(info.OEMID);
|
||||
}
|
||||
|
||||
int mod = addDigits(OEMID);
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
BOOL PIDGEN2::isValidChannelID() const
|
||||
{
|
||||
// if we're office, do the last digit +1 checksum
|
||||
if (info.isOffice)
|
||||
{
|
||||
Integer CheckDigit = (info.ChannelID % TEN), ChannelID = (info.ChannelID / TEN);
|
||||
|
||||
return (mod % 21 == 0);
|
||||
}
|
||||
|
||||
bool PIDGEN2::isValidYear(char* year) {
|
||||
for (int i = 0; i <= 7; i++) {
|
||||
if (year == validYears[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PIDGEN2::isValidDay(char* day) {
|
||||
if (!isNumericString(day)) {
|
||||
if (std::find(channelIDDisallowList.begin(), channelIDDisallowList.end(), IntToString(ChannelID)) !=
|
||||
channelIDDisallowList.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int iDay = std::stoi(day);
|
||||
if (iDay == 0 || iDay >= 365) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return (ChannelID % TEN) + 1 == CheckDigit;
|
||||
}
|
||||
|
||||
bool PIDGEN2::isValidRetailProductID(char* productID) {
|
||||
return true;
|
||||
// otherwise just make sure we're not in the disallow list
|
||||
return std::find(channelIDDisallowList.begin(), channelIDDisallowList.end(), IntToString(info.ChannelID)) ==
|
||||
channelIDDisallowList.end();
|
||||
}
|
||||
|
||||
int PIDGEN2::GenerateRetail(char* channelID, char* &keyout) {
|
||||
if (!isValidChannelID(channelID)) {
|
||||
return 1;
|
||||
/**
|
||||
* Is the OEM year in the allow list?
|
||||
*
|
||||
* Known allowed years are:
|
||||
* 95, 96, 97, 98, 99, 00, 01, 02
|
||||
*
|
||||
* @return validity
|
||||
*/
|
||||
BOOL PIDGEN2::isValidOEMYear() const
|
||||
{
|
||||
auto year = fmt::format("{:02d}", info.Year.ConvertToLong());
|
||||
return std::find(validYears.begin(), validYears.end(), year) != validYears.end();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PIDGEN2::GenerateOEM(char* year, char* day, char* oem, char* &keyout) {
|
||||
if (!isValidOEMID(oem)) {
|
||||
int mod = addDigits(oem);
|
||||
mod += mod % 21;
|
||||
|
||||
strcpy(oem, fmt::format("{:07d}", mod).c_str());
|
||||
}
|
||||
|
||||
if (!isValidYear(year)) {
|
||||
strcpy(year, validYears[0]);
|
||||
}
|
||||
|
||||
if (!isValidDay(day)) {
|
||||
int iday = std::stoi(day);
|
||||
iday = (iday + 1) % 365;
|
||||
}
|
||||
|
||||
strcpy(keyout, fmt::format("{}{}-OEM-{}-{}", year, day, oem, oem).c_str());
|
||||
|
||||
return 0;
|
||||
/**
|
||||
* Is the OEM Day an allowed day?
|
||||
*
|
||||
* Allowed days are 1 - 366 inclusive
|
||||
*
|
||||
* @return validity
|
||||
*/
|
||||
BOOL PIDGEN2::isValidOEMDay() const
|
||||
{
|
||||
return info.Day >= 0 && info.Day <= 366;
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
* 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
|
||||
@ -23,19 +23,40 @@
|
||||
#ifndef UMSKT_PIDGEN2_H
|
||||
#define UMSKT_PIDGEN2_H
|
||||
|
||||
#include "../libumskt.h"
|
||||
#include <libumskt/pidgen.h>
|
||||
|
||||
class EXPORT PIDGEN2 : public PIDGEN
|
||||
{
|
||||
static const std::vector<std::string> channelIDDisallowList;
|
||||
static const std::vector<std::string> validYears;
|
||||
|
||||
enum KeySize
|
||||
{
|
||||
FPP = 10,
|
||||
Office = 11,
|
||||
OEM = 17
|
||||
};
|
||||
|
||||
EXPORT class PIDGEN2 {
|
||||
public:
|
||||
static bool isNumericString(char* input);
|
||||
static bool isValidChannelID(char* channelID);
|
||||
static bool isValidOEMID(char* OEMID);
|
||||
static bool isValidYear(char* year);
|
||||
static bool isValidDay(char* day);
|
||||
static bool isValidRetailProductID(char* productID);
|
||||
static int addDigits(char* input);
|
||||
static int GenerateRetail(char* channelID, char* &keyout);
|
||||
static int GenerateOEM(char* year, char* day, char* oem, char* &keyout);
|
||||
~PIDGEN2() override = default;
|
||||
|
||||
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 ValidateKeyString(const std::string &in_key, std::string &out_key) override;
|
||||
|
||||
BOOL isValidSerial();
|
||||
BOOL isValidOEMID();
|
||||
[[nodiscard]] BOOL isValidChannelID() const;
|
||||
[[nodiscard]] BOOL isValidOEMYear() const;
|
||||
[[nodiscard]] BOOL isValidOEMDay() const;
|
||||
};
|
||||
|
||||
#endif // UMSKT_PIDGEN2_H
|
||||
|
115
src/libumskt/pidgen2/PIDGEN2_unittest.cpp
Normal file
115
src/libumskt/pidgen2/PIDGEN2_unittest.cpp
Normal file
@ -0,0 +1,115 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2024 UMSKT Contributors (et.al.)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @FileCreated by Neo on 02/19/2024
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#include "PIDGEN2.h"
|
||||
#include <libumskt/libumskt_unittest.h>
|
||||
|
||||
/**
|
||||
* PIDGEN2 must not be an abstract class.
|
||||
*/
|
||||
TEST(PIDGEN2, InstantiatePIDGEN2)
|
||||
{
|
||||
auto p2 = new PIDGEN2();
|
||||
ASSERT_NE(p2, nullptr);
|
||||
delete p2;
|
||||
}
|
||||
|
||||
class TestPIDGEN2 : public libumsktUnitTests
|
||||
{
|
||||
protected:
|
||||
PIDGEN *p;
|
||||
PIDGEN2 *p2;
|
||||
|
||||
PIDGEN2::KeyInfo valid_ki = {false, false, 60, 99, 0, 95, 111111};
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
p2 = new PIDGEN2();
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
if (p != nullptr)
|
||||
{
|
||||
delete p;
|
||||
p = nullptr;
|
||||
}
|
||||
if (p2 != nullptr)
|
||||
{
|
||||
delete p2;
|
||||
p2 = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestPIDGEN2, TestStringifyKeyFPP)
|
||||
{
|
||||
std::string pKey = "0951111111";
|
||||
pKey = p2->StringifyKey(pKey);
|
||||
ASSERT_STRCASEEQ(&pKey[0], "095-1111111");
|
||||
}
|
||||
|
||||
TEST_F(TestPIDGEN2, TestStringifyKeyOffice)
|
||||
{
|
||||
std::string pKey = "09561111111";
|
||||
pKey = p2->StringifyKey(pKey);
|
||||
ASSERT_STREQ(&pKey[0], "0956-1111111");
|
||||
}
|
||||
|
||||
TEST_F(TestPIDGEN2, TestStringifyKeyOEM)
|
||||
{
|
||||
std::string pKey = "06099000951611111";
|
||||
pKey = p2->StringifyKey(pKey);
|
||||
ASSERT_STREQ(&pKey[0], "06099-OEM-0009516-11111");
|
||||
}
|
||||
|
||||
TEST_F(TestPIDGEN2, GenerateValidFPPKey)
|
||||
{
|
||||
p2->info = valid_ki;
|
||||
|
||||
std::string pKey;
|
||||
p2->Generate(pKey);
|
||||
pKey = p2->StringifyKey(pKey);
|
||||
ASSERT_STREQ(&pKey[0], "095-1111111");
|
||||
}
|
||||
|
||||
TEST_F(TestPIDGEN2, GenerateValidOfficeKey)
|
||||
{
|
||||
p2->info = valid_ki;
|
||||
p2->info.isOffice = true;
|
||||
|
||||
std::string pKey;
|
||||
p2->Generate(pKey);
|
||||
pKey = p2->StringifyKey(pKey);
|
||||
ASSERT_STREQ(&pKey[0], "0956-1111111");
|
||||
}
|
||||
|
||||
TEST_F(TestPIDGEN2, GenerateValidOEMKey)
|
||||
{
|
||||
p2->info = valid_ki;
|
||||
p2->info.isOEM = true;
|
||||
|
||||
std::string pKey;
|
||||
p2->Generate(pKey);
|
||||
pKey = p2->StringifyKey(pKey);
|
||||
ASSERT_STREQ(&pKey[0], "06099-OEM-0009516-11111");
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
* 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
|
||||
@ -24,213 +24,147 @@
|
||||
* 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
|
||||
* }
|
||||
*/
|
||||
|
||||
#include "BINK1998.h"
|
||||
|
||||
/* Unpacks a Windows XP-like Product Key. */
|
||||
void PIDGEN3::BINK1998::Unpack(
|
||||
QWORD (&pRaw)[2],
|
||||
BOOL &pUpgrade,
|
||||
DWORD &pSerial,
|
||||
DWORD &pHash,
|
||||
QWORD &pSignature
|
||||
) {
|
||||
/**
|
||||
* Packs a Windows XP-like Product Key.
|
||||
*
|
||||
* @param ki
|
||||
* @return Integer representation of KeyInfo
|
||||
*/
|
||||
Integer BINK1998::Pack(const KeyInfo &ki)
|
||||
{
|
||||
// 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;
|
||||
|
||||
if (debug)
|
||||
{
|
||||
fmt::print(debug, "pack: {:x}\n\n", raw);
|
||||
}
|
||||
|
||||
return raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpacks a Windows XP-like Product Key.
|
||||
*
|
||||
* @param raw Integer to unpack
|
||||
* @return populated PIDGEN3::KeyInfo struct
|
||||
*/
|
||||
BINK1998::KeyInfo BINK1998::Unpack(const Integer &raw)
|
||||
{
|
||||
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
|
||||
pUpgrade = FIRSTNBITS(pRaw[0], 1);
|
||||
ki.isUpgrade = CryptoPP::Crop(raw, 1).ConvertToLong();
|
||||
|
||||
// Serial = Bits [1..30] -> 30 bits
|
||||
pSerial = NEXTSNBITS(pRaw[0], 30, 1);
|
||||
auto serialPack = CryptoPP::Crop((raw >> 1), 30);
|
||||
ki.Serial = serialPack % MaxSerial;
|
||||
ki.ChannelID = ((serialPack - ki.Serial) / MaxSerial);
|
||||
|
||||
// Hash = Bits [31..58] -> 28 bits
|
||||
pHash = NEXTSNBITS(pRaw[0], 28, 31);
|
||||
ki.Hash = CryptoPP::Crop((raw >> 31), 28);
|
||||
|
||||
// Signature = Bits [59..113] -> 56 bits
|
||||
pSignature = FIRSTNBITS(pRaw[1], 51) << 5 | NEXTSNBITS(pRaw[0], 5, 59);
|
||||
ki.Signature = CryptoPP::Crop((raw >> 59), 56);
|
||||
|
||||
return ki;
|
||||
}
|
||||
|
||||
/* Packs a Windows XP-like Product Key. */
|
||||
void PIDGEN3::BINK1998::Pack(
|
||||
QWORD (&pRaw)[2],
|
||||
BOOL pUpgrade,
|
||||
DWORD pSerial,
|
||||
DWORD pHash,
|
||||
QWORD pSignature
|
||||
) {
|
||||
// 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
|
||||
|
||||
// Signature [114..59] <- Hash [58..31] <- Serial [30..1] <- Upgrade [0]
|
||||
pRaw[0] = FIRSTNBITS(pSignature, 5) << 59 | FIRSTNBITS(pHash, 28) << 31 | pSerial << 1 | pUpgrade;
|
||||
pRaw[1] = NEXTSNBITS(pSignature, 51, 5);
|
||||
}
|
||||
|
||||
/* Verifies a Windows XP-like Product Key. */
|
||||
bool PIDGEN3::BINK1998::Verify(
|
||||
EC_GROUP *eCurve,
|
||||
EC_POINT *basePoint,
|
||||
EC_POINT *publicKey,
|
||||
char (&pKey)[25]
|
||||
) {
|
||||
BN_CTX *numContext = BN_CTX_new();
|
||||
|
||||
QWORD pRaw[2]{},
|
||||
pSignature;
|
||||
|
||||
DWORD pData,
|
||||
pSerial,
|
||||
pHash;
|
||||
|
||||
BOOL pUpgrade;
|
||||
|
||||
// Convert Base24 CD-key to bytecode.
|
||||
PIDGEN3::unbase24((BYTE *)pRaw, pKey);
|
||||
|
||||
// Extract RPK, hash and signature from bytecode.
|
||||
Unpack(pRaw, pUpgrade, pSerial, pHash, pSignature);
|
||||
|
||||
fmt::print(UMSKT::debug, "Validation results:\n");
|
||||
fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", pUpgrade);
|
||||
fmt::print(UMSKT::debug, " Serial: 0x{:08x}\n", pSerial);
|
||||
fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", pHash);
|
||||
fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", pSignature);
|
||||
fmt::print(UMSKT::debug, "\n");
|
||||
|
||||
pData = pSerial << 1 | pUpgrade;
|
||||
|
||||
/*
|
||||
*
|
||||
* Scalars:
|
||||
* e = Hash
|
||||
* s = Schnorr Signature
|
||||
*
|
||||
* Points:
|
||||
* G(x, y) = Generator (Base Point)
|
||||
* K(x, y) = Public Key
|
||||
*
|
||||
* Equation:
|
||||
* P = sG + eK
|
||||
/**
|
||||
* 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;
|
||||
|
||||
BIGNUM *e = BN_lebin2bn((BYTE *)&pHash, sizeof(pHash), nullptr),
|
||||
*s = BN_lebin2bn((BYTE *)&pSignature, sizeof(pSignature), nullptr),
|
||||
*x = BN_new(),
|
||||
*y = BN_new();
|
||||
// copy initial state from object
|
||||
auto ki = info;
|
||||
|
||||
// Create 2 points on the elliptic curve.
|
||||
EC_POINT *t = EC_POINT_new(eCurve);
|
||||
EC_POINT *p = EC_POINT_new(eCurve);
|
||||
|
||||
// t = sG
|
||||
EC_POINT_mul(eCurve, t, nullptr, basePoint, s, numContext);
|
||||
|
||||
// P = eK
|
||||
EC_POINT_mul(eCurve, p, nullptr, publicKey, e, numContext);
|
||||
|
||||
// P += t
|
||||
EC_POINT_add(eCurve, p, t, p, numContext);
|
||||
|
||||
// x = P.x; y = P.y;
|
||||
EC_POINT_get_affine_coordinates(eCurve, p, x, y, numContext);
|
||||
|
||||
BYTE msgDigest[SHA_DIGEST_LENGTH]{},
|
||||
msgBuffer[SHA_MSG_LENGTH_XP]{},
|
||||
xBin[FIELD_BYTES]{},
|
||||
yBin[FIELD_BYTES]{};
|
||||
|
||||
// Convert resulting point coordinates to bytes.
|
||||
BN_bn2lebin(x, xBin, FIELD_BYTES);
|
||||
BN_bn2lebin(y, yBin, FIELD_BYTES);
|
||||
|
||||
// Assemble the SHA message.
|
||||
memcpy((void *)&msgBuffer[0], (void *)&pData, 4);
|
||||
memcpy((void *)&msgBuffer[4], (void *)xBin, FIELD_BYTES);
|
||||
memcpy((void *)&msgBuffer[4 + FIELD_BYTES], (void *)yBin, FIELD_BYTES);
|
||||
|
||||
// compHash = SHA1(pSerial || P.x || P.y)
|
||||
SHA1(msgBuffer, SHA_MSG_LENGTH_XP, msgDigest);
|
||||
|
||||
// Translate the byte digest into a 32-bit integer - this is our computed hash.
|
||||
// Truncate the hash to 28 bits.
|
||||
DWORD compHash = BYDWORD(msgDigest) >> 4 & BITMASK(28);
|
||||
|
||||
BN_free(e);
|
||||
BN_free(s);
|
||||
BN_free(x);
|
||||
BN_free(y);
|
||||
|
||||
BN_CTX_free(numContext);
|
||||
|
||||
EC_POINT_free(t);
|
||||
EC_POINT_free(p);
|
||||
|
||||
// If the computed hash checks out, the key is valid.
|
||||
return compHash == pHash;
|
||||
if (!ki.Rand.IsZero())
|
||||
{
|
||||
c = ki.Rand;
|
||||
}
|
||||
|
||||
/* Generates a Windows XP-like Product Key. */
|
||||
void PIDGEN3::BINK1998::Generate(
|
||||
EC_GROUP *eCurve,
|
||||
EC_POINT *basePoint,
|
||||
BIGNUM *genOrder,
|
||||
BIGNUM *privateKey,
|
||||
DWORD pSerial,
|
||||
BOOL pUpgrade,
|
||||
char (&pKey)[25]
|
||||
) {
|
||||
BN_CTX *numContext = BN_CTX_new();
|
||||
|
||||
BIGNUM *c = BN_new(),
|
||||
*s = BN_new(),
|
||||
*x = BN_new(),
|
||||
*y = BN_new();
|
||||
|
||||
QWORD pRaw[2]{},
|
||||
pSignature = 0;
|
||||
|
||||
// Data segment of the RPK.
|
||||
DWORD pData = pSerial << 1 | pUpgrade;
|
||||
Integer serialPack = (ki.ChannelID * MaxSerial) + ki.Serial;
|
||||
Integer pData = (serialPack << 1) | ki.isUpgrade;
|
||||
|
||||
do {
|
||||
EC_POINT *r = EC_POINT_new(eCurve);
|
||||
// prepare the private key for generation
|
||||
privateKey = genOrder - privateKey;
|
||||
|
||||
do
|
||||
{
|
||||
ECP::Point R;
|
||||
|
||||
if (ki.Rand.IsZero())
|
||||
{
|
||||
// Generate a random number c consisting of 384 bits without any constraints.
|
||||
BN_rand(c, FIELD_BITS, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY);
|
||||
c.Randomize(UMSKT::rng, FieldBits);
|
||||
}
|
||||
|
||||
// Pick a random derivative of the base point on the elliptic curve.
|
||||
// R = cG;
|
||||
EC_POINT_mul(eCurve, r, nullptr, basePoint, c, numContext);
|
||||
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);
|
||||
}
|
||||
|
||||
// Acquire its coordinates.
|
||||
// x = R.x; y = R.y;
|
||||
EC_POINT_get_affine_coordinates(eCurve, r, x, y, numContext);
|
||||
|
||||
BYTE msgDigest[SHA_DIGEST_LENGTH]{},
|
||||
msgBuffer[SHA_MSG_LENGTH_XP]{},
|
||||
xBin[FIELD_BYTES]{},
|
||||
yBin[FIELD_BYTES]{};
|
||||
|
||||
// Convert coordinates to bytes.
|
||||
BN_bn2lebin(x, xBin, FIELD_BYTES);
|
||||
BN_bn2lebin(y, yBin, FIELD_BYTES);
|
||||
BYTE msgDigest[SHA1::DIGESTSIZE], msgBuffer[SHAMessageLength], *pMsgBuffer = msgBuffer;
|
||||
|
||||
// Assemble the SHA message.
|
||||
memcpy((void *)&msgBuffer[0], (void *)&pData, 4);
|
||||
memcpy((void *)&msgBuffer[4], (void *)xBin, FIELD_BYTES);
|
||||
memcpy((void *)&msgBuffer[4 + FIELD_BYTES], (void *)yBin, FIELD_BYTES);
|
||||
pMsgBuffer = EncodeN(pData, pMsgBuffer, 4);
|
||||
pMsgBuffer = EncodeN(R.x, pMsgBuffer, FieldBytes);
|
||||
EncodeN(R.y, pMsgBuffer, FieldBytes);
|
||||
|
||||
// pHash = SHA1(pSerial || R.x || R.y)
|
||||
SHA1(msgBuffer, SHA_MSG_LENGTH_XP, msgDigest);
|
||||
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");
|
||||
}
|
||||
|
||||
// Translate the byte digest into a 32-bit integer - this is our computed pHash.
|
||||
// Truncate the pHash to 28 bits.
|
||||
DWORD pHash = BYDWORD(msgDigest) >> 4 & BITMASK(28);
|
||||
|
||||
ki.Hash = IntegerN(msgDigest, 4) >> 4;
|
||||
ki.Hash = CryptoPP::Crop(ki.Hash, 28);
|
||||
|
||||
/*
|
||||
*
|
||||
@ -252,38 +186,135 @@ void PIDGEN3::BINK1998::Generate(
|
||||
*/
|
||||
|
||||
// s = ek;
|
||||
BN_copy(s, privateKey);
|
||||
BN_mul_word(s, pHash);
|
||||
s = privateKey * ki.Hash;
|
||||
|
||||
// s += c (mod n)
|
||||
BN_mod_add(s, s, c, genOrder, numContext);
|
||||
s += c;
|
||||
s %= genOrder;
|
||||
|
||||
// Translate resulting scalar into a 64-bit integer (the byte order is little-endian).
|
||||
BN_bn2lebinpad(s, (BYTE *)&pSignature, BN_num_bytes(s));
|
||||
// Translate resulting scalar into an Integer.
|
||||
ki.Signature = s;
|
||||
|
||||
// Pack product key.
|
||||
Pack(pRaw, pUpgrade, pSerial, pHash, pSignature);
|
||||
pRaw = Pack(ki);
|
||||
|
||||
fmt::print(UMSKT::debug, "Generation results:\n");
|
||||
fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", pUpgrade);
|
||||
fmt::print(UMSKT::debug, " Serial: 0x{:08x}\n", pSerial);
|
||||
fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", pHash);
|
||||
fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", pSignature);
|
||||
fmt::print(UMSKT::debug, "\n");
|
||||
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");
|
||||
}
|
||||
|
||||
EC_POINT_free(r);
|
||||
} while (pSignature > BITMASK(55));
|
||||
} while (ki.Signature.BitCount() > 55 && ki.Rand.IsZero());
|
||||
// ↑ ↑ ↑
|
||||
// The signature can't be longer than 55 bits, else it will
|
||||
// make the CD-key longer than 25 characters.
|
||||
|
||||
// Convert bytecode to Base24 CD-key.
|
||||
base24(pKey, (BYTE *)pRaw);
|
||||
pKey = base24(pRaw);
|
||||
|
||||
BN_free(c);
|
||||
BN_free(s);
|
||||
BN_free(x);
|
||||
BN_free(y);
|
||||
info = ki;
|
||||
|
||||
BN_CTX_free(numContext);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a Windows XP-like Product Key.
|
||||
*
|
||||
* @param pKey [in]
|
||||
*
|
||||
* @return true if provided key validates against loaded curve
|
||||
*/
|
||||
BOOL BINK1998::Validate(const std::string &pKey)
|
||||
{
|
||||
if (pKey.length() != 25)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert Base24 CD-key to bytecode.
|
||||
Integer pRaw = unbase24(pKey);
|
||||
SHA1 sha1;
|
||||
|
||||
// Extract RPK, hash and signature from bytecode.
|
||||
KeyInfo ki = Unpack(pRaw);
|
||||
|
||||
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}: {}\n", "Sequence", ki.Serial);
|
||||
fmt::print(verbose, "{:>10}: {:x}\n", "Hash", ki.Hash);
|
||||
fmt::print(verbose, "{:>10}: {:x}\n", "Signature", ki.Signature);
|
||||
fmt::print(verbose, "\n");
|
||||
}
|
||||
|
||||
Integer serialPack = (ki.ChannelID * MaxSerial) + ki.Serial;
|
||||
Integer pData = serialPack << 1 | ki.isUpgrade;
|
||||
|
||||
/*
|
||||
*
|
||||
* Scalars:
|
||||
* e = Hash
|
||||
* s = Schnorr Signature
|
||||
*
|
||||
* Points:
|
||||
* G(x, y) = Generator (Base Point)
|
||||
* K(x, y) = Public Key
|
||||
*
|
||||
* Equation:
|
||||
* P = sG + eK
|
||||
*
|
||||
*/
|
||||
|
||||
Integer e = ki.Hash, s = ki.Signature;
|
||||
|
||||
// Create 2 points on the elliptic curve.
|
||||
ECP::Point t, P;
|
||||
|
||||
// t = sG
|
||||
t = eCurve.Multiply(s, genPoint);
|
||||
|
||||
// P = eK
|
||||
P = eCurve.Multiply(e, pubPoint);
|
||||
|
||||
// P += t
|
||||
P = eCurve.Add(P, t);
|
||||
|
||||
if (debug)
|
||||
{
|
||||
fmt::print(debug, "P[x,y]: [{:x},\n{:x}]\n\n", P.x, P.y);
|
||||
}
|
||||
|
||||
BYTE msgDigest[SHA1::DIGESTSIZE], msgBuffer[SHAMessageLength], *pMsgBuffer = msgBuffer;
|
||||
|
||||
// Convert resulting point coordinates to bytes.
|
||||
|
||||
// Assemble the SHA message.
|
||||
pMsgBuffer = EncodeN(pData, pMsgBuffer, 4);
|
||||
pMsgBuffer = EncodeN(P.x, pMsgBuffer, FieldBytes);
|
||||
EncodeN(P.y, pMsgBuffer, FieldBytes);
|
||||
|
||||
// compHash = SHA1(pSerial || P.x || P.y)
|
||||
sha1.CalculateDigest(msgDigest, msgBuffer, SHAMessageLength);
|
||||
|
||||
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.
|
||||
// Truncate the hash to 28 bits.
|
||||
Integer compHash = CryptoPP::Crop(intDigest >> 4, 28);
|
||||
|
||||
// If the computed hash checks out, the key is valid.
|
||||
return compHash == ki.Hash;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
* 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
|
||||
@ -16,7 +16,7 @@
|
||||
* 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 6/6/2023
|
||||
* @FileCreated by Neo on 06/06/2023
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
@ -25,40 +25,37 @@
|
||||
|
||||
#include "PIDGEN3.h"
|
||||
|
||||
EXPORT class PIDGEN3::BINK1998 {
|
||||
class EXPORT BINK1998 : public PIDGEN3
|
||||
{
|
||||
public:
|
||||
static void Unpack(
|
||||
QWORD (&pRaw)[2],
|
||||
BOOL &pUpgrade,
|
||||
DWORD &pSerial,
|
||||
DWORD &pHash,
|
||||
QWORD &pSignature
|
||||
);
|
||||
using PIDGEN3::PIDGEN3;
|
||||
BINK1998() = default;
|
||||
explicit BINK1998(PIDGEN3 *p3)
|
||||
{
|
||||
privateKey = p3->privateKey;
|
||||
genOrder = p3->genOrder;
|
||||
genPoint = p3->genPoint;
|
||||
pubPoint = p3->pubPoint;
|
||||
eCurve = p3->eCurve;
|
||||
}
|
||||
|
||||
static void Pack(
|
||||
QWORD (&pRaw)[2],
|
||||
BOOL pUpgrade,
|
||||
DWORD pSerial,
|
||||
DWORD pHash,
|
||||
QWORD pSignature
|
||||
);
|
||||
~BINK1998() override = default;
|
||||
|
||||
static bool Verify(
|
||||
EC_GROUP *eCurve,
|
||||
EC_POINT *basePoint,
|
||||
EC_POINT *publicKey,
|
||||
char (&pKey)[25]
|
||||
);
|
||||
static constexpr DWORD32 FieldBits = (48 * 8);
|
||||
static constexpr DWORD32 FieldBytes = (FieldBits / 8);
|
||||
static constexpr DWORD32 SHAMessageLength = (4 + 2 * FieldBytes);
|
||||
|
||||
static void Generate(
|
||||
EC_GROUP *eCurve,
|
||||
EC_POINT *basePoint,
|
||||
BIGNUM *genOrder,
|
||||
BIGNUM *privateKey,
|
||||
DWORD pSerial,
|
||||
BOOL pUpgrade,
|
||||
char (&pKey)[25]
|
||||
);
|
||||
using PIDGEN3::Pack;
|
||||
Integer Pack(const KeyInfo &ki) override;
|
||||
|
||||
using PIDGEN3::Unpack;
|
||||
KeyInfo Unpack(const Integer &raw) override;
|
||||
|
||||
using PIDGEN3::Generate;
|
||||
BOOL Generate(std::string &pKey) override;
|
||||
|
||||
using PIDGEN3::Validate;
|
||||
BOOL Validate(const std::string &pKey) override;
|
||||
};
|
||||
|
||||
#endif // UMSKT_BINK1998_H
|
||||
|
110
src/libumskt/pidgen3/BINK1998_unittest.cpp
Normal file
110
src/libumskt/pidgen3/BINK1998_unittest.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2024 UMSKT Contributors (et.al.)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @FileCreated by Neo on 02/19/2024
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#include "../libumskt_unittest.h"
|
||||
#include "BINK1998.h"
|
||||
|
||||
/**
|
||||
* BINK1998 must not be an abstract class.
|
||||
*/
|
||||
TEST(PIDGEN3_BINK1998, InstantiateBINK1998)
|
||||
{
|
||||
auto p3 = new BINK1998();
|
||||
ASSERT_NE(p3, nullptr);
|
||||
delete p3;
|
||||
}
|
||||
|
||||
class TestBINK1998 : public libumsktUnitTests
|
||||
{
|
||||
protected:
|
||||
PIDGEN *p;
|
||||
PIDGEN3 *p3;
|
||||
BINK1998 *bink1998;
|
||||
|
||||
BINK1998::KeyInfo valid_ki = {false, false, 640, 111111, 0};
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
bink1998 = new BINK1998();
|
||||
bink1998->LoadEllipticCurve("0x2E",
|
||||
"2260481414313563299067995668434431120981995280321627195247220485552475627515144045"
|
||||
"6421260165232069708317717961315241",
|
||||
"1", "0",
|
||||
"1091074492220651278115691316907175015302838688467620894706280834607253141127048943"
|
||||
"2930252839559606812441712224597826",
|
||||
"1917099366991720451749161800061981867915210969017264186834961288993048036527467509"
|
||||
"6509477191800826190959228181870174",
|
||||
"1439923035396364333971294001595406158106423983592682351741971676961393703934682226"
|
||||
"9422480779920783799484349086780408",
|
||||
"5484731395987446993229594927733430043632089703338918322171291299699820472711849119"
|
||||
"800714736923107362018017833200634",
|
||||
"61760995553426173", "37454031876727861");
|
||||
bink1998->info = valid_ki;
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
if (bink1998 != nullptr)
|
||||
{
|
||||
delete bink1998;
|
||||
bink1998 = nullptr;
|
||||
}
|
||||
if (p3 != nullptr)
|
||||
{
|
||||
delete p3;
|
||||
p3 = nullptr;
|
||||
}
|
||||
if (p != nullptr)
|
||||
{
|
||||
p = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestBINK1998, ValidateValidKeyString)
|
||||
{
|
||||
std::string pKey;
|
||||
auto ValidateKeyString = bink1998->ValidateKeyString("7KWK7-9W7H4-T64D6-DB8V7-BW7MW", pKey);
|
||||
|
||||
ASSERT_TRUE(ValidateKeyString);
|
||||
ASSERT_STREQ(&pKey[0], "7KWK79W7H4T64D6DB8V7BW7MW");
|
||||
}
|
||||
|
||||
TEST_F(TestBINK1998, ValidateValidKey)
|
||||
{
|
||||
std::string pKey = "7KWK79W7H4T64D6DB8V7BW7MW";
|
||||
|
||||
auto Validate = bink1998->Validate(pKey);
|
||||
ASSERT_TRUE(Validate);
|
||||
}
|
||||
|
||||
TEST_F(TestBINK1998, GenerateValidKey)
|
||||
{
|
||||
bink1998->info.Rand = UMSKT::IntegerS("3427338792529164195109698841758932126727183763685994848291878088992924483488"
|
||||
"5768429236548372772607190036626858221847");
|
||||
|
||||
std::string pKey;
|
||||
bink1998->Generate(pKey);
|
||||
pKey = bink1998->StringifyKey(pKey);
|
||||
|
||||
ASSERT_STREQ(&pKey[0], "7KWK7-9W7H4-T64D6-DB8V7-BW7MW");
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
* 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
|
||||
@ -24,273 +24,210 @@
|
||||
* 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
|
||||
* }
|
||||
*/
|
||||
|
||||
#include "BINK2002.h"
|
||||
|
||||
/* Unpacks a Windows Server 2003-like Product Key. */
|
||||
void PIDGEN3::BINK2002::Unpack(
|
||||
QWORD (&pRaw)[2],
|
||||
BOOL &pUpgrade,
|
||||
DWORD &pChannelID,
|
||||
DWORD &pHash,
|
||||
QWORD &pSignature,
|
||||
DWORD &pAuthInfo
|
||||
) {
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
// 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;
|
||||
|
||||
if (debug)
|
||||
{
|
||||
fmt::print(debug, "pack: {:x}\n\n", raw);
|
||||
}
|
||||
|
||||
return raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
// 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
|
||||
pUpgrade = FIRSTNBITS(pRaw[0], 1);
|
||||
ki.isUpgrade = CryptoPP::Crop(raw, 1).ConvertToLong();
|
||||
|
||||
// Channel ID = Bits [1..10] -> 10 bits
|
||||
pChannelID = NEXTSNBITS(pRaw[0], 10, 1);
|
||||
ki.ChannelID = CryptoPP::Crop(raw >> 1, 10);
|
||||
|
||||
// Hash = Bits [11..41] -> 31 bits
|
||||
pHash = NEXTSNBITS(pRaw[0], 31, 11);
|
||||
// Hash = Bits [11..41] -> 30 bits
|
||||
ki.Hash = CryptoPP::Crop(raw >> 11, 31);
|
||||
|
||||
// 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)
|
||||
pSignature = NEXTSNBITS(pRaw[1], 30, 10) << 32 | FIRSTNBITS(pRaw[1], 10) << 22 | NEXTSNBITS(pRaw[0], 22, 42);
|
||||
ki.Signature = CryptoPP::Crop(raw >> 42, 62);
|
||||
|
||||
// AuthInfo = Bits [104..113] -> 10 bits
|
||||
pAuthInfo = NEXTSNBITS(pRaw[1], 10, 40);
|
||||
ki.AuthInfo = CryptoPP::Crop(raw >> 104, 10);
|
||||
|
||||
return ki;
|
||||
}
|
||||
|
||||
/* Packs a Windows Server 2003-like Product Key. */
|
||||
void PIDGEN3::BINK2002::Pack(
|
||||
QWORD (&pRaw)[2],
|
||||
BOOL pUpgrade,
|
||||
DWORD pChannelID,
|
||||
DWORD pHash,
|
||||
QWORD pSignature,
|
||||
DWORD pAuthInfo
|
||||
) {
|
||||
// AuthInfo [113..104] <- Signature [103..42] <- Hash [41..11] <- Channel ID [10..1] <- Upgrade [0]
|
||||
pRaw[0] = FIRSTNBITS(pSignature, 22) << 42 | (QWORD)pHash << 11 | pChannelID << 1 | pUpgrade;
|
||||
pRaw[1] = FIRSTNBITS(pAuthInfo, 10) << 40 | NEXTSNBITS(pSignature, 40, 22);
|
||||
}
|
||||
|
||||
/* Verifies a Windows Server 2003-like Product Key. */
|
||||
bool PIDGEN3::BINK2002::Verify(
|
||||
EC_GROUP *eCurve,
|
||||
EC_POINT *basePoint,
|
||||
EC_POINT *publicKey,
|
||||
char (&cdKey)[25]
|
||||
) {
|
||||
BN_CTX *context = BN_CTX_new();
|
||||
|
||||
QWORD bKey[2]{},
|
||||
pSignature = 0;
|
||||
|
||||
DWORD pData,
|
||||
pChannelID,
|
||||
pHash,
|
||||
pAuthInfo;
|
||||
|
||||
BOOL pUpgrade;
|
||||
|
||||
// Convert Base24 CD-key to bytecode.
|
||||
unbase24((BYTE *)bKey, cdKey);
|
||||
|
||||
// Extract product key segments from bytecode.
|
||||
Unpack(bKey, pUpgrade, pChannelID, pHash, pSignature, pAuthInfo);
|
||||
|
||||
pData = pChannelID << 1 | pUpgrade;
|
||||
|
||||
fmt::print(UMSKT::debug, "Validation results:\n");
|
||||
fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", pUpgrade);
|
||||
fmt::print(UMSKT::debug, "Channel ID: 0x{:08x}\n", pChannelID);
|
||||
fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", pHash);
|
||||
fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", pSignature);
|
||||
fmt::print(UMSKT::debug, " AuthInfo: 0x{:08x}\n", pAuthInfo);
|
||||
fmt::print(UMSKT::debug, "\n");
|
||||
|
||||
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;
|
||||
msgBuffer[0x01] = (pData & 0x00FF);
|
||||
msgBuffer[0x02] = (pData & 0xFF00) >> 8;
|
||||
msgBuffer[0x03] = (pHash & 0x000000FF);
|
||||
msgBuffer[0x04] = (pHash & 0x0000FF00) >> 8;
|
||||
msgBuffer[0x05] = (pHash & 0x00FF0000) >> 16;
|
||||
msgBuffer[0x06] = (pHash & 0xFF000000) >> 24;
|
||||
msgBuffer[0x07] = (pAuthInfo & 0x00FF);
|
||||
msgBuffer[0x08] = (pAuthInfo & 0xFF00) >> 8;
|
||||
msgBuffer[0x09] = 0x00;
|
||||
msgBuffer[0x0A] = 0x00;
|
||||
|
||||
// newSignature = SHA1(5D || Channel ID || Hash || AuthInfo || 00 00)
|
||||
SHA1(msgBuffer, 11, msgDigest);
|
||||
|
||||
// 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);
|
||||
|
||||
/*
|
||||
*
|
||||
* Scalars:
|
||||
* e = Hash
|
||||
* s = Schnorr Signature
|
||||
*
|
||||
* Points:
|
||||
* G(x, y) = Generator (Base Point)
|
||||
* K(x, y) = Public Key
|
||||
*
|
||||
* Equation:
|
||||
* P = s(sG + eK)
|
||||
/**
|
||||
* Generates a Windows Server 2003-like Product Key.
|
||||
*
|
||||
* @param info
|
||||
* @param pKey
|
||||
* @return
|
||||
*/
|
||||
BOOL BINK2002::Generate(std::string &pKey)
|
||||
{
|
||||
// copy the starting state from the class
|
||||
KeyInfo ki = info;
|
||||
SHA1 sha1;
|
||||
|
||||
BIGNUM *e = BN_lebin2bn((BYTE *)&iSignature, sizeof(iSignature), nullptr),
|
||||
*s = BN_lebin2bn((BYTE *)&pSignature, sizeof(pSignature), nullptr),
|
||||
*x = BN_new(),
|
||||
*y = BN_new();
|
||||
Integer c, e, s, pRaw;
|
||||
|
||||
// Create 2 points on the elliptic curve.
|
||||
EC_POINT *p = EC_POINT_new(eCurve);
|
||||
EC_POINT *t = EC_POINT_new(eCurve);
|
||||
|
||||
// t = sG
|
||||
EC_POINT_mul(eCurve, t, nullptr, basePoint, s, context);
|
||||
|
||||
// p = eK
|
||||
EC_POINT_mul(eCurve, p, nullptr, publicKey, e, context);
|
||||
|
||||
// p += t
|
||||
EC_POINT_add(eCurve, p, t, p, context);
|
||||
|
||||
// p *= s
|
||||
EC_POINT_mul(eCurve, p, nullptr, p, s, context);
|
||||
|
||||
// x = p.x; y = p.y;
|
||||
EC_POINT_get_affine_coordinates(eCurve, p, x, y, context);
|
||||
|
||||
// Convert resulting point coordinates to bytes.
|
||||
BN_bn2lebin(x, xBin, FIELD_BYTES_2003);
|
||||
BN_bn2lebin(y, yBin, FIELD_BYTES_2003);
|
||||
|
||||
// Assemble the second SHA message.
|
||||
msgBuffer[0x00] = 0x79;
|
||||
msgBuffer[0x01] = (pData & 0x00FF);
|
||||
msgBuffer[0x02] = (pData & 0xFF00) >> 8;
|
||||
|
||||
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(msgBuffer, SHA_MSG_LENGTH_2003, msgDigest);
|
||||
|
||||
// Translate the byte digest into a 32-bit integer - this is our computed hash.
|
||||
// Truncate the hash to 31 bits.
|
||||
DWORD compHash = BYDWORD(msgDigest) & BITMASK(31);
|
||||
|
||||
BN_free(s);
|
||||
BN_free(e);
|
||||
BN_free(x);
|
||||
BN_free(y);
|
||||
|
||||
BN_CTX_free(context);
|
||||
|
||||
EC_POINT_free(p);
|
||||
EC_POINT_free(t);
|
||||
|
||||
// If the computed hash checks out, the key is valid.
|
||||
return compHash == pHash;
|
||||
if (!ki.Rand.IsZero())
|
||||
{
|
||||
c = ki.Rand;
|
||||
}
|
||||
|
||||
/* Generates a Windows Server 2003-like Product Key. */
|
||||
void PIDGEN3::BINK2002::Generate(
|
||||
EC_GROUP *eCurve,
|
||||
EC_POINT *basePoint,
|
||||
BIGNUM *genOrder,
|
||||
BIGNUM *privateKey,
|
||||
DWORD pChannelID,
|
||||
DWORD pAuthInfo,
|
||||
BOOL pUpgrade,
|
||||
char (&pKey)[25]
|
||||
) {
|
||||
BN_CTX *numContext = BN_CTX_new();
|
||||
|
||||
BIGNUM *c = BN_new(),
|
||||
*e = BN_new(),
|
||||
*s = BN_new(),
|
||||
*x = BN_new(),
|
||||
*y = BN_new();
|
||||
|
||||
QWORD pRaw[2]{},
|
||||
pSignature = 0;
|
||||
|
||||
// Data segment of the RPK.
|
||||
DWORD pData = pChannelID << 1 | pUpgrade;
|
||||
Integer pData = ki.ChannelID << 1 | ki.isUpgrade;
|
||||
|
||||
BOOL noSquare;
|
||||
|
||||
do {
|
||||
EC_POINT *r = EC_POINT_new(eCurve);
|
||||
do
|
||||
{
|
||||
ECP::Point R;
|
||||
|
||||
if (ki.Rand.IsZero())
|
||||
{
|
||||
// Generate a random number c consisting of 512 bits without any constraints.
|
||||
BN_rand(c, FIELD_BITS_2003, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY);
|
||||
c.Randomize(UMSKT::rng, FieldBits);
|
||||
}
|
||||
|
||||
// R = cG
|
||||
EC_POINT_mul(eCurve, r, nullptr, basePoint, c, numContext);
|
||||
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);
|
||||
}
|
||||
|
||||
// Acquire its coordinates.
|
||||
// x = R.x; y = R.y;
|
||||
EC_POINT_get_affine_coordinates(eCurve, r, x, y, numContext);
|
||||
|
||||
BYTE msgDigest[SHA_DIGEST_LENGTH]{},
|
||||
msgBuffer[SHA_MSG_LENGTH_2003]{},
|
||||
xBin[FIELD_BYTES_2003]{},
|
||||
yBin[FIELD_BYTES_2003]{};
|
||||
|
||||
// Convert resulting point coordinates to bytes.
|
||||
BN_bn2lebin(x, xBin, FIELD_BYTES_2003);
|
||||
BN_bn2lebin(y, yBin, FIELD_BYTES_2003);
|
||||
BYTE msgDigest[SHA1::DIGESTSIZE], msgBuffer[SHAMessageLength], *pMsgBuffer = msgBuffer;
|
||||
|
||||
// Assemble the first SHA message.
|
||||
msgBuffer[0x00] = 0x79;
|
||||
msgBuffer[0x01] = (pData & 0x00FF);
|
||||
msgBuffer[0x02] = (pData & 0xFF00) >> 8;
|
||||
*pMsgBuffer = 0x79;
|
||||
pMsgBuffer++;
|
||||
|
||||
memcpy((void *)&msgBuffer[3], (void *)xBin, FIELD_BYTES_2003);
|
||||
memcpy((void *)&msgBuffer[3 + FIELD_BYTES_2003], (void *)yBin, FIELD_BYTES_2003);
|
||||
pMsgBuffer = EncodeN(pData, pMsgBuffer, 2);
|
||||
|
||||
// Convert resulting point coordinates to bytes.
|
||||
// and flip the endianness
|
||||
pMsgBuffer = EncodeN(R.x, pMsgBuffer, FieldBytes);
|
||||
EncodeN(R.y, pMsgBuffer, FieldBytes);
|
||||
|
||||
// pHash = SHA1(79 || Channel ID || R.x || R.y)
|
||||
SHA1(msgBuffer, SHA_MSG_LENGTH_2003, msgDigest);
|
||||
sha1.CalculateDigest(msgDigest, msgBuffer, SHAMessageLength);
|
||||
|
||||
// Translate the byte digest into a 32-bit integer - this is our computed hash.
|
||||
if (debug)
|
||||
{
|
||||
fmt::print(debug, "msgBuffer[1]: ");
|
||||
for (BYTE b : msgBuffer)
|
||||
{
|
||||
fmt::print(debug, "{:x}", b);
|
||||
}
|
||||
fmt::print(debug, "\n\n");
|
||||
|
||||
fmt::print(debug, "msgDigest[1]: ");
|
||||
for (BYTE b : msgDigest)
|
||||
{
|
||||
fmt::print(debug, "{:x}", b);
|
||||
}
|
||||
fmt::print(debug, "\n\n");
|
||||
}
|
||||
|
||||
// Translate the byte sha1 into a 32-bit integer - this is our computed hash.
|
||||
// Truncate the hash to 31 bits.
|
||||
DWORD pHash = BYDWORD(msgDigest) & BITMASK(31);
|
||||
ki.Hash = CryptoPP::Crop(IntegerN(msgDigest), 31);
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
BYTE buf[8];
|
||||
sha1.CalculateTruncatedDigest(buf, sizeof(buf), msgBuffer, SHAMessageLength);
|
||||
|
||||
fmt::print(verbose, "truncated buffer: ");
|
||||
for (BYTE b : buf)
|
||||
{
|
||||
fmt::print(verbose, "{:x}", b);
|
||||
}
|
||||
fmt::print(verbose, "\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(verbose, "h0,1: {:x} {:x}\n\n", h0, h1);
|
||||
|
||||
ki.Serial = IntegerN(h1);
|
||||
|
||||
fmt::print(verbose, "serial: {:d}\n\n", ki.Serial);
|
||||
}
|
||||
|
||||
// Assemble the second SHA message.
|
||||
pMsgBuffer = msgBuffer;
|
||||
msgBuffer[0x00] = 0x5D;
|
||||
msgBuffer[0x01] = (pData & 0x00FF);
|
||||
msgBuffer[0x02] = (pData & 0xFF00) >> 8;
|
||||
msgBuffer[0x03] = (pHash & 0x000000FF);
|
||||
msgBuffer[0x04] = (pHash & 0x0000FF00) >> 8;
|
||||
msgBuffer[0x05] = (pHash & 0x00FF0000) >> 16;
|
||||
msgBuffer[0x06] = (pHash & 0xFF000000) >> 24;
|
||||
msgBuffer[0x07] = (pAuthInfo & 0x00FF);
|
||||
msgBuffer[0x08] = (pAuthInfo & 0xFF00) >> 8;
|
||||
msgBuffer[0x09] = 0x00;
|
||||
msgBuffer[0x0A] = 0x00;
|
||||
pMsgBuffer++;
|
||||
|
||||
pMsgBuffer = EncodeN(pData, pMsgBuffer, 2);
|
||||
pMsgBuffer = EncodeN(ki.Hash, pMsgBuffer, 4);
|
||||
pMsgBuffer = EncodeN(ki.AuthInfo, pMsgBuffer, 2);
|
||||
|
||||
*pMsgBuffer = 0x00;
|
||||
pMsgBuffer++;
|
||||
|
||||
*pMsgBuffer = 0x00;
|
||||
pMsgBuffer++;
|
||||
|
||||
// newSignature = SHA1(5D || Channel ID || Hash || AuthInfo || 00 00)
|
||||
SHA1(msgBuffer, 11, msgDigest);
|
||||
sha1.CalculateDigest(msgDigest, msgBuffer, pMsgBuffer - msgBuffer);
|
||||
|
||||
// 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).
|
||||
if (debug)
|
||||
{
|
||||
fmt::print(debug, "msgBuffer[2]: ");
|
||||
for (BYTE b : msgBuffer)
|
||||
{
|
||||
fmt::print(debug, "{:x}", b);
|
||||
}
|
||||
fmt::print(debug, "\n\n");
|
||||
|
||||
fmt::print(debug, "msgDigest[2]: ");
|
||||
for (BYTE b : msgDigest)
|
||||
{
|
||||
fmt::print(debug, "{:x}", b);
|
||||
}
|
||||
fmt::print(debug, "\n\n");
|
||||
}
|
||||
|
||||
// Translate the byte sha1 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:
|
||||
@ -321,66 +258,185 @@ void PIDGEN3::BINK2002::Generate(
|
||||
*/
|
||||
|
||||
// e = ek (mod n)
|
||||
BN_mod_mul(e, e, privateKey, genOrder, numContext);
|
||||
|
||||
// s = e
|
||||
BN_copy(s, e);
|
||||
e = CryptoPP::ModularMultiplication(IntegerN(iSignature), privateKey, genOrder);
|
||||
|
||||
// s = (ek (mod n))²
|
||||
BN_mod_sqr(s, s, genOrder, numContext);
|
||||
s = CryptoPP::ModularExponentiation(e, Integer::Two(), genOrder);
|
||||
|
||||
// c *= 4 (c <<= 2)
|
||||
BN_lshift(c, c, 2);
|
||||
c *= 4;
|
||||
|
||||
// s += c
|
||||
BN_add(s, s, c);
|
||||
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))
|
||||
noSquare = BN_mod_sqrt(s, s, genOrder, numContext) == nullptr;
|
||||
s = CryptoPP::ModularSquareRoot(s, genOrder);
|
||||
noSquare = s.IsZero();
|
||||
|
||||
// s = -ek + √((ek)² + 4c) (mod n)
|
||||
BN_mod_sub(s, s, e, genOrder, numContext);
|
||||
s -= e;
|
||||
s %= genOrder;
|
||||
|
||||
// If s is odd, add order to it.
|
||||
// The order is a prime, so it can't be even.
|
||||
if (BN_is_odd(s))
|
||||
|
||||
if (s % Integer::Two() != 0)
|
||||
{
|
||||
// s = -ek + √((ek)² + 4c) + n
|
||||
BN_add(s, s, genOrder);
|
||||
s += genOrder;
|
||||
}
|
||||
|
||||
// s /= 2 (s >>= 1)
|
||||
BN_rshift1(s, s);
|
||||
s /= 2;
|
||||
|
||||
// Translate resulting scalar into a 64-bit integer (the byte order is little-endian).
|
||||
BN_bn2lebinpad(s, (BYTE *)&pSignature, BN_num_bytes(s));
|
||||
ki.Signature = s;
|
||||
|
||||
// Pack product key.
|
||||
Pack(pRaw, pUpgrade, pChannelID, pHash, pSignature, pAuthInfo);
|
||||
pRaw = Pack(ki);
|
||||
|
||||
fmt::print(UMSKT::debug, "Generation results:\n");
|
||||
fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", pUpgrade);
|
||||
fmt::print(UMSKT::debug, "Channel ID: 0x{:08x}\n", pChannelID);
|
||||
fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", pHash);
|
||||
fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", pSignature);
|
||||
fmt::print(UMSKT::debug, " AuthInfo: 0x{:08x}\n", pAuthInfo);
|
||||
fmt::print(UMSKT::debug, "\n");
|
||||
|
||||
EC_POINT_free(r);
|
||||
} while (pSignature > BITMASK(62) || noSquare);
|
||||
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) && ki.Rand.IsZero());
|
||||
// ↑ ↑ ↑
|
||||
// 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.
|
||||
base24(pKey, (BYTE *)pRaw);
|
||||
pKey = base24(pRaw);
|
||||
|
||||
BN_free(c);
|
||||
BN_free(s);
|
||||
BN_free(x);
|
||||
BN_free(y);
|
||||
BN_free(e);
|
||||
info = ki;
|
||||
|
||||
BN_CTX_free(numContext);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a Windows Server 2003-like Product Key.
|
||||
*
|
||||
* @param pKey
|
||||
**/
|
||||
BOOL BINK2002::Validate(const std::string &pKey)
|
||||
{
|
||||
Integer pRaw;
|
||||
SHA1 sha1;
|
||||
|
||||
// Convert Base24 CD-key to bytecode.
|
||||
pRaw = unbase24(pKey);
|
||||
|
||||
// Extract product key segments from bytecode.
|
||||
KeyInfo ki = Unpack(pRaw);
|
||||
|
||||
Integer pData = ki.ChannelID << 1 | ki.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");
|
||||
}
|
||||
|
||||
BYTE msgDigest[SHA1::DIGESTSIZE], msgBuffer[SHAMessageLength], *pMsgBuffer = msgBuffer;
|
||||
|
||||
// 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++;
|
||||
|
||||
// newSignature = SHA1(5D || Channel ID || Hash || AuthInfo || 00 00)
|
||||
sha1.CalculateDigest(msgDigest, msgBuffer, pMsgBuffer - msgBuffer);
|
||||
|
||||
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.
|
||||
// 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);
|
||||
|
||||
/*
|
||||
*
|
||||
* Scalars:
|
||||
* e = Hash
|
||||
* s = Schnorr Signature
|
||||
*
|
||||
* Points:
|
||||
* G(x, y) = Generator (Base Point)
|
||||
* K(x, y) = Public Key
|
||||
*
|
||||
* Equation:
|
||||
* P = s(sG + eK)
|
||||
*
|
||||
*/
|
||||
Integer e = IntegerN(iSignature), s = ki.Signature;
|
||||
|
||||
// Create 2 points on the elliptic curve.
|
||||
ECP::Point P, t;
|
||||
|
||||
// t = sG
|
||||
t = eCurve.Multiply(s, genPoint);
|
||||
|
||||
// P = eK
|
||||
P = eCurve.Multiply(e, pubPoint);
|
||||
|
||||
// P += t
|
||||
P = eCurve.Add(P, t);
|
||||
|
||||
// P *= s
|
||||
P = eCurve.Multiply(s, P);
|
||||
|
||||
if (debug)
|
||||
{
|
||||
fmt::print(debug, "P[x,y]: [{:x},\n{:x}]\n\n", P.x, P.y);
|
||||
}
|
||||
|
||||
// Assemble the second SHA message.
|
||||
pMsgBuffer = msgBuffer;
|
||||
msgBuffer[0x00] = 0x79;
|
||||
pMsgBuffer++;
|
||||
|
||||
pMsgBuffer = EncodeN(pData, pMsgBuffer, 2);
|
||||
pMsgBuffer = EncodeN(P.x, pMsgBuffer, FieldBytes);
|
||||
EncodeN(P.y, pMsgBuffer, FieldBytes);
|
||||
|
||||
// compHash = SHA1(79 || Channel ID || P.x || P.y)
|
||||
sha1.CalculateDigest(msgDigest, msgBuffer, SHAMessageLength);
|
||||
|
||||
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.
|
||||
// Truncate the hash to 31 bits.
|
||||
Integer compHash = CryptoPP::Crop(intDigest, 31);
|
||||
|
||||
info = ki;
|
||||
|
||||
// If the computed hash checks out, the key is valid.
|
||||
return compHash == ki.Hash;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
* 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
|
||||
@ -16,7 +16,7 @@
|
||||
* 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 6/6/2023
|
||||
* @FileCreated by Neo on 06/06/2023
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
@ -25,43 +25,37 @@
|
||||
|
||||
#include "PIDGEN3.h"
|
||||
|
||||
EXPORT class PIDGEN3::BINK2002 {
|
||||
class EXPORT BINK2002 : public PIDGEN3
|
||||
{
|
||||
public:
|
||||
static void Unpack(
|
||||
QWORD (&pRaw)[2],
|
||||
BOOL &pUpgrade,
|
||||
DWORD &pChannelID,
|
||||
DWORD &pHash,
|
||||
QWORD &pSignature,
|
||||
DWORD &pAuthInfo
|
||||
);
|
||||
using PIDGEN3::PIDGEN3;
|
||||
BINK2002() = default;
|
||||
explicit BINK2002(PIDGEN3 *p3)
|
||||
{
|
||||
privateKey = p3->privateKey;
|
||||
genOrder = p3->genOrder;
|
||||
genPoint = p3->genPoint;
|
||||
pubPoint = p3->pubPoint;
|
||||
eCurve = p3->eCurve;
|
||||
}
|
||||
|
||||
static void Pack(
|
||||
QWORD (&pRaw)[2],
|
||||
BOOL pUpgrade,
|
||||
DWORD pChannelID,
|
||||
DWORD pHash,
|
||||
QWORD pSignature,
|
||||
DWORD pAuthInfo
|
||||
);
|
||||
~BINK2002() override = default;
|
||||
|
||||
static bool Verify(
|
||||
EC_GROUP *eCurve,
|
||||
EC_POINT *basePoint,
|
||||
EC_POINT *publicKey,
|
||||
char (&cdKey)[25]
|
||||
);
|
||||
static constexpr DWORD32 FieldBits = (64 * 8);
|
||||
static constexpr DWORD32 FieldBytes = (FieldBits / 8);
|
||||
static constexpr DWORD32 SHAMessageLength = (3 + 2 * FieldBytes);
|
||||
|
||||
static void Generate(
|
||||
EC_GROUP *eCurve,
|
||||
EC_POINT *basePoint,
|
||||
BIGNUM *genOrder,
|
||||
BIGNUM *privateKey,
|
||||
DWORD pChannelID,
|
||||
DWORD pAuthInfo,
|
||||
BOOL pUpgrade,
|
||||
char (&pKey)[25]
|
||||
);
|
||||
using PIDGEN3::Pack;
|
||||
Integer Pack(const KeyInfo &ki) override;
|
||||
|
||||
using PIDGEN3::Unpack;
|
||||
KeyInfo Unpack(const Integer &raw) override;
|
||||
|
||||
using PIDGEN3::Generate;
|
||||
BOOL Generate(std::string &pKey) override;
|
||||
|
||||
using PIDGEN3::Validate;
|
||||
BOOL Validate(const std::string &pKey) override;
|
||||
};
|
||||
|
||||
#endif // UMSKT_BINK2002_H
|
||||
|
121
src/libumskt/pidgen3/BINK2002_unittest.cpp
Normal file
121
src/libumskt/pidgen3/BINK2002_unittest.cpp
Normal file
@ -0,0 +1,121 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2024 UMSKT Contributors (et.al.)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @FileCreated by Neo on 02/19/2024
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#include "BINK2002.h"
|
||||
#include <libumskt/libumskt_unittest.h>
|
||||
|
||||
/**
|
||||
* BINK2002 must not be an abstract class.
|
||||
*/
|
||||
TEST(PIDGEN3_BINK2002, InstantiateBINK2002)
|
||||
{
|
||||
auto p3 = new BINK2002();
|
||||
ASSERT_NE(p3, nullptr);
|
||||
delete p3;
|
||||
}
|
||||
|
||||
class TestBINK2002 : public libumsktUnitTests
|
||||
{
|
||||
protected:
|
||||
PIDGEN *p;
|
||||
PIDGEN3 *p3;
|
||||
BINK2002 *bink2002;
|
||||
|
||||
BINK2002::KeyInfo valid_ki = {false, false, 640, 0, 701};
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
bink2002 = new BINK2002();
|
||||
bink2002->LoadEllipticCurve("0x54",
|
||||
"1250964251969733259611431105354461862074700938981465222536952118871017192617497641"
|
||||
"9995384745134703589248167610052719613586668754176591418831031596093374569",
|
||||
"1", "0",
|
||||
"8059057663701168311917532277618827622978515614146963913097592614451721430413021070"
|
||||
"395782723330339842826599481063797559797462512297834269467666807971588275",
|
||||
"1223930383017475319177970597922037862339473226753699711562597963240231208768364492"
|
||||
"7405756146495100825573682155171145924668759419114616275413724686284123408",
|
||||
"4895832170509729140211911021638266775170167022247175324972987673313207244495397975"
|
||||
"379010973250279668424167408883454560376269866102669741515127286188717976",
|
||||
"5846013328426281815512452704859777850382010968846722453046994319336479079120767834"
|
||||
"777937190955827245502389471872759584209649693396095099112777776298051208",
|
||||
"5622613991231344109", "1285511085175426271");
|
||||
bink2002->info = valid_ki;
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
if (bink2002 != nullptr)
|
||||
{
|
||||
delete bink2002;
|
||||
bink2002 = nullptr;
|
||||
}
|
||||
if (p3 != nullptr)
|
||||
{
|
||||
delete p3;
|
||||
p3 = nullptr;
|
||||
}
|
||||
if (p != nullptr)
|
||||
{
|
||||
p = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestBINK2002, ValidateValidKeyString)
|
||||
{
|
||||
std::string pKey;
|
||||
auto ValidateKeyString = bink2002->ValidateKeyString("QX7C7-6668G-RHTTC-9XXD6-4QKVM", pKey);
|
||||
ASSERT_TRUE(ValidateKeyString);
|
||||
ASSERT_STREQ(&pKey[0], "QX7C76668GRHTTC9XXD64QKVM");
|
||||
}
|
||||
|
||||
TEST_F(TestBINK2002, ValidateInvalidKeyString)
|
||||
{
|
||||
std::string pKey;
|
||||
auto ValidateKeyString = bink2002->ValidateKeyString("QX7C7-6668G-RHTTC-9XXD6-4QKVM-7", pKey);
|
||||
ASSERT_FALSE(ValidateKeyString);
|
||||
}
|
||||
|
||||
TEST_F(TestBINK2002, ValidateValidKey)
|
||||
{
|
||||
std::string pKey = "QX7C76668GRHTTC9XXD64QKVM";
|
||||
auto Validate = bink2002->Validate(pKey);
|
||||
ASSERT_TRUE(Validate);
|
||||
}
|
||||
|
||||
TEST_F(TestBINK2002, ValidateInvalidKey)
|
||||
{
|
||||
std::string pKey = "QX7C76668GRHTTC9XXD64QKV7";
|
||||
auto Validate = bink2002->Validate(pKey);
|
||||
ASSERT_FALSE(Validate);
|
||||
}
|
||||
|
||||
TEST_F(TestBINK2002, GenerateValidKey)
|
||||
{
|
||||
bink2002->info.Rand = UMSKT::IntegerS("2715417548459431244234182116258933974639514924173191881913315754156057922856"
|
||||
"789413383072541627152533502894944768632184791880876163762899980230935");
|
||||
std::string pKey;
|
||||
bink2002->Generate(pKey);
|
||||
pKey = bink2002->StringifyKey(pKey);
|
||||
|
||||
ASSERT_STREQ(&pKey[0], "QX7C7-6668G-RHTTC-9XXD6-4QKVM");
|
||||
}
|
316
src/libumskt/pidgen3/PIDGEN3.cpp
Normal file
316
src/libumskt/pidgen3/PIDGEN3.cpp
Normal file
@ -0,0 +1,316 @@
|
||||
/**
|
||||
* 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 Andrew on 01/06/2023
|
||||
* @Maintainer Andrew
|
||||
*/
|
||||
|
||||
#include "PIDGEN3.h"
|
||||
#include "BINK1998.h"
|
||||
#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/
|
||||
*
|
||||
* @return 4
|
||||
*/
|
||||
int getRandomNumber()
|
||||
{
|
||||
return 4; // chosen by fair dice roll
|
||||
// guaranteed to be random
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the elliptic curve
|
||||
*
|
||||
* @param pSel [in] prime
|
||||
* @param aSel [in] a
|
||||
* @param bSel [in] b
|
||||
* @param generatorXSel [in] G[x]
|
||||
* @param generatorYSel [in] G[y]
|
||||
* @param publicKeyXSel [in] pub[x]
|
||||
* @param publicKeyYSel [in] pub[y]
|
||||
* @param genOrderSel [in] computed order of G
|
||||
* @param privateKeySel [in] computed private key
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
// 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).
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// Public key will consist of the resulting (x; y) values.
|
||||
auto generatorX = IntegerS(generatorXSel), generatorY = IntegerS(generatorYSel);
|
||||
|
||||
// G(x; y) is a generator function, its return value represents a point on the elliptic curve.
|
||||
auto publicKeyX = IntegerS(publicKeyXSel), publicKeyY = IntegerS(publicKeyYSel);
|
||||
|
||||
/* Computed Data */
|
||||
genOrder = IntegerS(genOrderSel);
|
||||
privateKey = IntegerS(privateKeySel);
|
||||
|
||||
/* 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);
|
||||
|
||||
// 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 Q for the public key on the elliptic curve and set its coordinates to (pubX; pubY).
|
||||
pubPoint = ECP::Point(publicKeyX, publicKeyY);
|
||||
|
||||
// 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);
|
||||
|
||||
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)
|
||||
{
|
||||
if (checkFieldIsBink1998())
|
||||
{
|
||||
auto p3 = BINK1998(this);
|
||||
return p3.Generate(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)
|
||||
{
|
||||
if (checkFieldIsBink1998())
|
||||
{
|
||||
auto p3 = BINK1998(this);
|
||||
return p3.Validate(pKey);
|
||||
}
|
||||
|
||||
auto p3 = BINK2002(this);
|
||||
return p3.Validate(pKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts from byte sequence to the CD-key.
|
||||
*
|
||||
* @param seq Integer representation
|
||||
* @return std::string CDKey output
|
||||
**/
|
||||
std::string PIDGEN3::base24(Integer &seq)
|
||||
{
|
||||
std::string cdKey;
|
||||
cdKey.reserve(PK_LENGTH);
|
||||
|
||||
// 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--)
|
||||
{
|
||||
Integer::Divide(r, q, a, (WORD)pKeyCharset.length());
|
||||
cdKey.insert(cdKey.begin(), pKeyCharset[r.ConvertToLong()]);
|
||||
a = q;
|
||||
}
|
||||
|
||||
return cdKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts from CD-key to a byte sequence.
|
||||
*
|
||||
* @param cdKey std::string CDKey to convert
|
||||
* @return Integer raw representation of the CDKey
|
||||
**/
|
||||
Integer PIDGEN3::unbase24(const std::string &cdKey)
|
||||
{
|
||||
Integer result;
|
||||
|
||||
for (char ch : cdKey)
|
||||
{
|
||||
auto val = std::find(pKeyCharset.begin(), pKeyCharset.end(), ch);
|
||||
|
||||
// character is not in set, return early
|
||||
if (val == pKeyCharset.end())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// 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
|
||||
*/
|
||||
BOOL PIDGEN3::checkFieldIsBink1998()
|
||||
{
|
||||
// is fieldSize < max?
|
||||
return (eCurve.FieldSize().BitCount() < MaxSizeBINK1998);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
// 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 INLINE PIDGEN3::ValidateStringKeyInputCharset(std::string &accumulator, char currentChar)
|
||||
{
|
||||
char cchar = (char)::toupper(currentChar);
|
||||
if (std::find(pKeyCharset.begin(), pKeyCharset.end(), 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);
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
* 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
|
||||
@ -16,39 +16,96 @@
|
||||
* 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 6/24/2023
|
||||
* @FileCreated by Neo on 06/24/2023
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#ifndef UMSKT_PIDGEN3_H
|
||||
#define UMSKT_PIDGEN3_H
|
||||
|
||||
#include "../libumskt.h"
|
||||
#include <libumskt/pidgen.h>
|
||||
|
||||
class PIDGEN3 {
|
||||
public:
|
||||
class BINK1998;
|
||||
class BINK2002;
|
||||
|
||||
// util.cpp
|
||||
static int BN_bn2lebin(const BIGNUM *a, unsigned char *to, int tolen); // Hello OpenSSL developers, please tell me, where is this function at?
|
||||
static void endian(BYTE *data, int length);
|
||||
static EC_GROUP* initializeEllipticCurve(
|
||||
std::string pSel,
|
||||
std::string aSel,
|
||||
std::string bSel,
|
||||
std::string generatorXSel,
|
||||
std::string generatorYSel,
|
||||
std::string publicKeyXSel,
|
||||
std::string publicKeyYSel,
|
||||
EC_POINT *&genPoint,
|
||||
EC_POINT *&pubPoint
|
||||
);
|
||||
class EXPORT PIDGEN3 : public PIDGEN
|
||||
{
|
||||
friend class BINK1998;
|
||||
friend class BINK2002;
|
||||
|
||||
// key.cpp
|
||||
static constexpr char pKeyCharset[] = "BCDFGHJKMPQRTVWXY2346789";
|
||||
static void unbase24(BYTE *byteSeq, const char *cdKey);
|
||||
static void base24(char *cdKey, BYTE *byteSeq);
|
||||
protected:
|
||||
Integer BINKID;
|
||||
Integer privateKey, genOrder;
|
||||
ECP::Point pubPoint, genPoint;
|
||||
ECP eCurve;
|
||||
|
||||
public:
|
||||
PIDGEN3() = default;
|
||||
|
||||
PIDGEN3(PIDGEN3 &p3)
|
||||
{
|
||||
privateKey = p3.privateKey;
|
||||
genOrder = p3.genOrder;
|
||||
pubPoint = p3.pubPoint;
|
||||
genPoint = p3.genPoint;
|
||||
eCurve = p3.eCurve;
|
||||
}
|
||||
|
||||
~PIDGEN3() override = default;
|
||||
|
||||
struct KeyInfo
|
||||
{
|
||||
BOOL isUpgrade = false, isOEM = false;
|
||||
Integer ChannelID = 0, Serial = 0, AuthInfo = 0, Rand = 0, Hash = 0, Signature = 0;
|
||||
|
||||
void setSerial(DWORD32 SerialIn)
|
||||
{
|
||||
Serial = IntegerN(SerialIn);
|
||||
}
|
||||
|
||||
void setAuthInfo(DWORD32 AuthInfoIn)
|
||||
{
|
||||
AuthInfo = IntegerN(AuthInfoIn);
|
||||
}
|
||||
|
||||
void setChannelID(DWORD32 ChannelIDIn)
|
||||
{
|
||||
ChannelID = IntegerN(ChannelIDIn);
|
||||
}
|
||||
} info;
|
||||
|
||||
static const std::string pKeyCharset;
|
||||
static const DWORD32 MaxSizeBINK1998;
|
||||
|
||||
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 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 ValidateKeyString(const std::string &in_key, std::string &out_key) override;
|
||||
|
||||
virtual Integer Pack(const KeyInfo &ki)
|
||||
{
|
||||
throw std::runtime_error("PIDGEN3::Pack() pure virtual function call");
|
||||
}
|
||||
|
||||
virtual KeyInfo Unpack(const Integer &raw)
|
||||
{
|
||||
throw std::runtime_error("PIDGEN3::Unpack() pure virtual function call");
|
||||
}
|
||||
|
||||
// PIDGEN3.cpp
|
||||
static PIDGEN3 *Factory(const std::string &field);
|
||||
static BOOL checkFieldStrIsBink1998(std::string keyin);
|
||||
static std::string ValidateStringKeyInputCharset(std::string &accumulator, char currentChar);
|
||||
static std::string base24(Integer &seq);
|
||||
static Integer unbase24(const std::string &cdKey);
|
||||
|
||||
BOOL checkFieldIsBink1998();
|
||||
};
|
||||
|
||||
#endif // UMSKT_PIDGEN3_H
|
||||
|
@ -1,86 +0,0 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2023 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 5/26/2023
|
||||
* @Maintainer Andrew
|
||||
*/
|
||||
|
||||
#include "PIDGEN3.h"
|
||||
|
||||
/* Converts from CD-key to a byte sequence. */
|
||||
void PIDGEN3::unbase24(BYTE *byteSeq, const char *cdKey) {
|
||||
BYTE pDecodedKey[PK_LENGTH + NULL_TERMINATOR]{};
|
||||
BIGNUM *y = BN_new();
|
||||
|
||||
BN_zero(y);
|
||||
|
||||
// Remove dashes from the CD-key and put it into a Base24 byte array.
|
||||
for (int i = 0, k = 0; i < strlen(cdKey) && k < PK_LENGTH; i++) {
|
||||
for (int j = 0; j < 24; j++) {
|
||||
if (cdKey[i] != '-' && cdKey[i] == pKeyCharset[j]) {
|
||||
pDecodedKey[k++] = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
endian(byteSeq, n);
|
||||
}
|
||||
|
||||
/* Converts from byte sequence to the CD-key. */
|
||||
void PIDGEN3::base24(char *cdKey, BYTE *byteSeq) {
|
||||
BYTE rbyteSeq[16];
|
||||
BIGNUM *z;
|
||||
|
||||
// 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--);
|
||||
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.
|
||||
cdKey[25] = 0;
|
||||
|
||||
for (int i = 24; i >= 0; i--)
|
||||
cdKey[i] = pKeyCharset[BN_div_word(z, 24)];
|
||||
|
||||
BN_free(z);
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2023 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 Andrew on 01/06/2023
|
||||
* @Maintainer Andrew
|
||||
*/
|
||||
|
||||
#include "PIDGEN3.h"
|
||||
|
||||
int randomRange() {
|
||||
return 4; // chosen by fair dice roll
|
||||
// guaranteed to be random
|
||||
}
|
||||
|
||||
/* Convert data between endianness types. */
|
||||
void PIDGEN3::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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initializes the elliptic curve. */
|
||||
EC_GROUP* PIDGEN3::initializeEllipticCurve(
|
||||
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,
|
||||
EC_POINT *&genPoint,
|
||||
EC_POINT *&pubPoint
|
||||
) {
|
||||
// Initialize BIGNUM and BIGNUMCTX structures.
|
||||
// BIGNUM - Large numbers
|
||||
// BIGNUMCTX - Context large numbers (temporary)
|
||||
BIGNUM *a, *b, *p, *generatorX, *generatorY, *publicKeyX, *publicKeyY;
|
||||
BN_CTX *context;
|
||||
|
||||
// We're presented with an elliptic curve, a multivariable function y(x; p; a; b), where
|
||||
// y^2 % p = x^3 + ax + b % p.
|
||||
a = BN_new();
|
||||
b = BN_new();
|
||||
p = BN_new();
|
||||
|
||||
// Public key will consist of the resulting (x; y) values.
|
||||
publicKeyX = BN_new();
|
||||
publicKeyY = BN_new();
|
||||
|
||||
// G(x; y) is a generator function, its return value represents a point on the elliptic curve.
|
||||
generatorX = BN_new();
|
||||
generatorY = BN_new();
|
||||
|
||||
// Context variable
|
||||
context = BN_CTX_new();
|
||||
|
||||
/* Public data */
|
||||
BN_dec2bn(&p, pSel.c_str());
|
||||
BN_dec2bn(&a, aSel.c_str());
|
||||
BN_dec2bn(&b, bSel.c_str());
|
||||
BN_dec2bn(&generatorX, generatorXSel.c_str());
|
||||
BN_dec2bn(&generatorY, generatorYSel.c_str());
|
||||
|
||||
BN_dec2bn(&publicKeyX, publicKeyXSel.c_str());
|
||||
BN_dec2bn(&publicKeyY, publicKeyYSel.c_str());
|
||||
|
||||
/* 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.
|
||||
EC_GROUP *eCurve = EC_GROUP_new_curve_GFp(p, a, b, context);
|
||||
|
||||
// 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 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(EC_POINT_is_on_curve(eCurve, genPoint, context) == true);
|
||||
assert(EC_POINT_is_on_curve(eCurve, pubPoint, context) == true);
|
||||
|
||||
// Cleanup
|
||||
BN_CTX_free(context);
|
||||
BN_free(p);
|
||||
BN_free(a);
|
||||
BN_free(b);
|
||||
BN_free(generatorX);
|
||||
BN_free(generatorY);
|
||||
BN_free(publicKeyX);
|
||||
BN_free(publicKeyY);
|
||||
|
||||
return eCurve;
|
||||
}
|
||||
|
||||
int PIDGEN3::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;
|
||||
}
|
47
src/main.cpp
47
src/main.cpp
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
* 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
|
||||
@ -20,44 +20,19 @@
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#include "header.h"
|
||||
#include "cli.h"
|
||||
#include <cli/cli.h>
|
||||
|
||||
Options options;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (!CLI::parseCommandLine(argc, argv, &options)) {
|
||||
fmt::print("error parsing command line options\n");
|
||||
CLI::showHelp(argv);
|
||||
return !options.error ? 0 : 1;
|
||||
}
|
||||
|
||||
json keys;
|
||||
|
||||
int status = CLI::validateCommandLine(&options, argv, &keys);
|
||||
if (status > 0) {
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int status;
|
||||
if (status = CLI::Init(argc, argv); status != 0)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
CLI run(options, keys);
|
||||
auto cli = CLI();
|
||||
status = cli.Run();
|
||||
|
||||
switch(options.applicationMode) {
|
||||
case MODE_BINK1998_GENERATE:
|
||||
return run.BINK1998Generate();
|
||||
|
||||
case MODE_BINK2002_GENERATE:
|
||||
return run.BINK2002Generate();
|
||||
|
||||
case MODE_BINK1998_VALIDATE:
|
||||
return run.BINK1998Validate();
|
||||
|
||||
case MODE_BINK2002_VALIDATE:
|
||||
return run.BINK2002Validate();
|
||||
|
||||
case MODE_CONFIRMATION_ID:
|
||||
return run.ConfirmationID();
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
UMSKT::DESTRUCT();
|
||||
return status;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
* 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
|
||||
@ -16,20 +16,25 @@
|
||||
* 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 6/25/2023
|
||||
* @FileCreated by Neo on 01/07/2024
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#include "libumskt.h"
|
||||
#include <cli/cli.h>
|
||||
|
||||
#include <cmrc/cmrc.hpp>
|
||||
CMRC_DECLARE(umskt);
|
||||
|
||||
#ifdef _WIN32
|
||||
std::FILE* UMSKT::debug = std::fopen("NUL:", "w");
|
||||
#else
|
||||
std::FILE* UMSKT::debug = std::fopen("/dev/null", "w");
|
||||
#endif
|
||||
/**
|
||||
* load a cmrc embedded JSON file
|
||||
*
|
||||
* @return success
|
||||
*/
|
||||
BOOL CLI::loadEmbeddedJSON()
|
||||
{
|
||||
auto fs = cmrc::umskt::get_filesystem();
|
||||
auto jsonFile = fs.open("keys.json");
|
||||
keys = json::parse(jsonFile, nullptr, false, false);
|
||||
|
||||
|
||||
void UMSKT::setDebugOutput(std::FILE* input) {
|
||||
debug = input;
|
||||
return true;
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
* 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
|
||||
@ -16,44 +16,89 @@
|
||||
* 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 6/24/2023
|
||||
* @FileCreated by Neo on 06/24/2023
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#ifndef UMSKT_TYPEDEFS_H
|
||||
#define UMSKT_TYPEDEFS_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdbool>
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
#ifdef DEBUG
|
||||
// squelch the insane amount of warnings from cstdbool
|
||||
#define _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
|
||||
#endif // defined(_MSC_VER)
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstdbool>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
#include <stdint128>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if defined(DEBUG) || 1
|
||||
#include <cassert>
|
||||
#else
|
||||
#define assert(x) /* nothing */
|
||||
#define assert(x) /* do nothing */
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define EXPORT extern "C" __declspec(dllexport)
|
||||
#if defined(_MSC_VER)
|
||||
#define FNEXPORT __declspec(dllexport)
|
||||
#define FNIMPORT __declspec(dllimport)
|
||||
#define FNINLINE __forceinline
|
||||
#elif defined(__GNUC__)
|
||||
#define FNEXPORT __attribute__((visibility("default")))
|
||||
#define FNIMPORT __attribute__((visibility("default")))
|
||||
#define FNINLINE inline __attribute__((__always_inline__))
|
||||
#elif defined(__CLANG__)
|
||||
#if __has_attribute(__always_inline__)
|
||||
#define forceinline inline __attribute__((__always_inline__))
|
||||
#else
|
||||
#define EXPORT extern "C"
|
||||
#endif
|
||||
#define forceinline inline
|
||||
#endif // __has_attribute(__always_inline__)
|
||||
#else
|
||||
#define FNEXPORT
|
||||
#define FNINLINE
|
||||
#warning "function inlining not handled"
|
||||
#endif // defined(_MSC_VER)
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#define FNEXPORT EMSCRIPTEN_KEEPALIVE EXPORT
|
||||
#define EXPORT EMSCRIPTEN_KEEPALIVE FNEXPORT
|
||||
#define INLINE FNINLINE
|
||||
#else
|
||||
#define FNEXPORT EXPORT
|
||||
#endif
|
||||
#ifdef UMSKT_IMPORT_LIB
|
||||
#define EXPORT FNIMPORT
|
||||
#else
|
||||
#define EXPORT FNEXPORT
|
||||
#endif // ifdef UMSKT_IMPORT_LIB
|
||||
#define INLINE FNINLINE
|
||||
#endif // ifdef __EMSCRIPTEN__
|
||||
|
||||
// Type definitions
|
||||
typedef bool BOOL;
|
||||
typedef uint8_t BYTE;
|
||||
typedef uint16_t WORD;
|
||||
typedef uint32_t DWORD;
|
||||
typedef uint64_t QWORD;
|
||||
// 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;
|
||||
using OWORD = uint128_t;
|
||||
|
||||
#ifdef __SIZEOF_INT128__
|
||||
typedef unsigned __int128 OWORD;
|
||||
#endif
|
||||
typedef union {
|
||||
OWORD oword;
|
||||
QWORD qword[2];
|
||||
DWORD32 dword32[4];
|
||||
WORD word[8];
|
||||
BYTE byte[16];
|
||||
} Q_OWORD;
|
||||
|
||||
#endif // UMSKT_TYPEDEFS_H
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
* 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
|
||||
@ -20,17 +20,16 @@
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include "resource.h"
|
||||
#include "typedefs.h"
|
||||
|
||||
BOOLEAN WINAPI DllMain( IN HINSTANCE hDllHandle,
|
||||
IN DWORD nReason,
|
||||
IN LPVOID Reserved ) {
|
||||
BOOLEAN WINAPI DllMain(IN HINSTANCE hDllHandle, IN DWORD nReason, IN LPVOID Reserved)
|
||||
{
|
||||
|
||||
BOOLEAN bSuccess = TRUE;
|
||||
// Perform global initialization.
|
||||
switch (nReason) {
|
||||
switch (nReason)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
// For optimization.
|
||||
DisableThreadLibraryCalls(hDllHandle);
|
||||
|
70
src/windows/platform.cpp
Normal file
70
src/windows/platform.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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 01/07/2024
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
#include "cli/cli.h"
|
||||
#include "resource.h"
|
||||
|
||||
/**
|
||||
*
|
||||
* @return success
|
||||
*/
|
||||
BOOL CLI::loadEmbeddedJSON()
|
||||
{
|
||||
HMODULE hModule = GetModuleHandle(nullptr);
|
||||
|
||||
// Find
|
||||
HRSRC hResource = FindResource(hModule, MAKEINTRESOURCE(IDR_JSON1), "JSON");
|
||||
if (hResource == nullptr)
|
||||
{
|
||||
fmt::print("ERROR: Could not find internal JSON resource?");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load
|
||||
HGLOBAL hResourceData = LoadResource(hModule, hResource);
|
||||
if (hResourceData == nullptr)
|
||||
{
|
||||
fmt::print("ERROR: Could not load internal JSON resource?");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Lock
|
||||
LPVOID pData = LockResource(hResourceData);
|
||||
if (pData == nullptr)
|
||||
{
|
||||
fmt::print("ERROR: Could not lock internal JSON resource?");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
keys = json::parse((char *)pData, nullptr, false, false);
|
||||
}
|
||||
catch (const json::exception &e)
|
||||
{
|
||||
fmt::print("ERROR: Exception occurred while parsing internal JSON file: {}\n", e.what());
|
||||
}
|
||||
|
||||
FreeResource(hResourceData);
|
||||
|
||||
return true;
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* This file is a part of the UMSKT Project
|
||||
*
|
||||
* Copyleft (C) 2019-2023 UMSKT Contributors (et.al.)
|
||||
* 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
|
||||
@ -16,7 +16,7 @@
|
||||
* 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 6/17/2023
|
||||
* @FileCreated by Neo on 06/17/2023
|
||||
* @Maintainer Neo
|
||||
*/
|
||||
|
||||
@ -25,12 +25,14 @@
|
||||
// Used by umskt.rc
|
||||
//
|
||||
#define IDI_ICON1 101
|
||||
#define IDR_JSON1 102
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 102
|
||||
#define _APS_NO_MFC 1
|
||||
#define _APS_NEXT_RESOURCE_VALUE 103
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user