12 Commits

Author SHA1 Message Date
f7427792e3 Add Wither's gists as references (#137)
* Add Wither's gists as references

* remove broken ones

---------

Co-authored-by: WitherOrNot <damemem@gmail.com>
2025-07-09 20:12:49 -05:00
c0aca723b8 Update windows-x86-x64.yml 2025-07-09 16:04:20 -05:00
8c98a3d173 Update windows-x86-x64.yml 2025-07-09 16:02:26 -05:00
a1845d9d6a Update windows-x86-x64.yml 2025-07-09 16:01:50 -05:00
817695d988 Update windows-x86-x64.yml 2025-07-09 15:57:08 -05:00
8dc6dfb97e Downgrade OpenSSL to 1.1.1 (#136)
* Update windows-x86-x64.yml

* Update windows-arm.yml

* Update windows-arm.yml

* Update windows-arm.yml

* Update windows-arm.yml

* Update windows-arm.yml
2025-07-09 15:43:33 -05:00
8d964c6fad Update README.md 2025-07-09 13:15:30 -05:00
e66f0cc724 Update README.md 2025-07-09 13:14:57 -05:00
ca7db29776 Update README.md 2025-07-09 13:12:44 -05:00
ed19cf2148 Update windows-arm.yml 2025-07-09 12:55:38 -05:00
4de84b6439 lower sdk target 2025-07-09 12:22:52 -05:00
827c26bc45 ARM32 support for Windows RT (#135) 2025-07-09 12:17:15 -05:00
13 changed files with 2525 additions and 288 deletions

147
.github/workflows/windows-arm.yml vendored Normal file
View File

@ -0,0 +1,147 @@
# 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 TheTank20 on 07/08/2025
# @Maintainer Neo
name: C/C++ CI (Windows ARM)
on:
push:
branches: [ "*" ]
paths-ignore: [ '**.md', 'doc/**', '.idea/**']
workflow_dispatch:
jobs:
build:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
- arch: arm32
arch_compilename: amd64_arm
vcpkg_arch: arm-windows-static
cmake_arch: ARM
sdk_version: '10.0.17763.0'
- arch: arm64
arch_compilename: amd64_arm64
vcpkg_arch: arm64-windows-static
cmake_arch: ARM64
sdk_version: '10.0.17763.0'
steps:
- name: Setup MSVC for ${{ matrix.arch }}
uses: ilammy/msvc-dev-cmd@v1
with:
arch: ${{ matrix.arch_compilename }}
sdk: ${{ matrix.sdk_version }}
- name: Checkout Source Tree
uses: actions/checkout@v4
- name: Cache OpenSSL Binaries
uses: actions/cache/restore@v4
with:
path: |
${{ github.workspace }}-OpenSSL-MSVC-${{ matrix.arch }}
key: openssl-1.1.1-${{ matrix.arch }}-${{ hashFiles('**/CMakeLists.txt') }}
- name: Checkout and Compile OpenSSL v1.1.1
shell: pwsh
run: |
git clone https://github.com/UMSKT/openssl --branch OpenSSL_1_1_1-stable --depth 1 openssl-src
cd openssl-src
# Install Perl if needed
choco install strawberryperl -y
# Configure and build OpenSSL for ARM32
$env:PATH = "C:\Strawberry\perl\bin;$env:PATH"
# First set up Visual Studio environment
$vsPath = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath
$vcvarsall = Join-Path $vsPath "VC\Auxiliary\Build\vcvarsall.bat"
$vcTarget = ""
if ("${{ matrix.arch}}" -eq "arm32") {
$vcTarget = "VC-WIN32-ARM"
} else {
$vcTarget = "VC-WIN64-ARM"
}
# Configure OpenSSL
perl Configure $vcTarget no-shared no-asm no-engine --prefix="$env:GITHUB_WORKSPACE/OpenSSL-MSVC-${{ matrix.arch }}"
# Build using MSVC ARM32 tools
cmd /c "call `"$vcvarsall`" ${{matrix.arch_compilename}} && nmake && nmake install_sw"
cd ..
- name: Save OpenSSL Binaries
uses: actions/cache/save@v4
with:
path: |
${{ github.workspace }}/OpenSSL-MSVC-${{ matrix.arch }}
key: openssl-1.1.1-${{ matrix.arch }}-${{ hashFiles('**/CMakeLists.txt') }}
- name: Set OpenSSL Environment
shell: pwsh
run: |
echo "OPENSSL_ROOT=$env:GITHUB_WORKSPACE/OpenSSL-MSVC-${{ matrix.arch }}" | Out-File -FilePath $env:GITHUB_ENV -Append
echo "OPENSSL_LIBDIR=lib" | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Configure UMSKT
shell: pwsh
run: |
cmake -G "Visual Studio 17 2022" `
-A ${{ matrix.cmake_arch }} `
-DWINDOWS_ARM=ON `
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" `
-DVCPKG_TARGET_TRIPLET=${{ matrix.vcpkg_arch }} `
.
- name: Build UMSKT
shell: pwsh
run: |
cmake --build . --config Release
- name: Upload build artifact
uses: actions/upload-artifact@v4.6.2
with:
name: UMSKT-WinNT-${{ matrix.arch }}
path: Release/umskt.exe
test-arm64:
needs: build
if: success()
runs-on: windows-11-arm
strategy:
matrix:
arch: [arm64]
steps:
- name: Download ARM64 artifact
uses: actions/download-artifact@v4
with:
name: UMSKT-WinNT-${{ matrix.arch }}
path: .
- name: Run tests
shell: pwsh
run: |
Write-Host Test 1 - generating key
.\umskt.exe -b 2C -c 365 -s 069420 -v
Write-Host Test 2 - generatng confid
.\umskt.exe -i 253286028742154311079061239762245184619981623171292574

160
.github/workflows/windows-x86-x64.yml vendored Normal file
View File

@ -0,0 +1,160 @@
# 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 TheTank20 on 07/08/2025
# @Maintainer Neo
name: C/C++ CI (Windows x86+64)
on:
push:
branches: [ "*" ]
paths-ignore: [ '**.md', 'doc/**', '.idea/**']
workflow_dispatch:
jobs:
build:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
- arch: x86
msystem: MINGW
cmake_flag: -m32
- arch: x64
msystem: MINGW64
cmake_flag: -m64
steps:
- name: Setup TDM-GCC
run: |
Write-Host Downloading TDM-GCC v10.3.0...
Invoke-WebRequest -Uri 'https://github.com/jmeubank/tdm-gcc/releases/download/v10.3.0-tdm64-2/tdm64-gcc-10.3.0-2.exe' -OutFile 'C:\Windows\temp\TDM-GCC-64.exe'
Write-Host Creating directory...
New-Item -ItemType Directory -Path 'C:\TDM-GCC-64'
Write-Host Copying files [Set 1/3]...
Start-Process '7z' -ArgumentList 'e C:\Windows\temp\TDM-GCC-64.exe -oC:\TDM-GCC-64 -y' -Wait
Write-Host Copying files [Set 2/3]...
Start-Process '7z' -ArgumentList 'e C:\TDM-GCC-64\*.tar.xz -oC:\TDM-GCC-64 -y' -Wait
Write-Host Copying files [Set 3/3]...
Start-Process '7z' -ArgumentList 'x C:\TDM-GCC-64\*.tar -oC:\TDM-GCC-64 -y' -Wait
Write-Host Adding environment variables...
$env:PATH = 'C:\TDM-GCC-64\bin;' + $env:PATH
[Environment]::SetEnvironmentVariable('PATH', $env:PATH, [EnvironmentVariableTarget]::Machine)
- name: Checkout Source Tree
uses: actions/checkout@v4
- name: Cache OpenSSL 1.1.1
uses: actions/cache/restore@v4
id: cache-openssl
with:
path: |
${{ github.workspace }}/OpenSSL-TDM-${{ matrix.arch }}
key: openssl-1.1.1-${{ matrix.arch }}-${{ hashFiles('**/CMakeLists.txt') }}
- name: Setup MSYS2
if: steps.cache-openssl.outputs.cache-hit != 'true'
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.arch == 'x86' && 'MINGW32' || 'MINGW64' }}
update: true
install: >-
mingw-w64-${{ matrix.arch == 'x86' && 'i686' || 'x86_64' }}-gcc
mingw-w64-${{ matrix.arch == 'x86' && 'i686' || 'x86_64' }}-make
mingw-w64-x86_64-perl
perl
git
- name: Checkout and Compile OpenSSL 1.1.1
if: steps.cache-openssl.outputs.cache-hit != 'true'
shell: msys2 {0}
run: |
# Clone UMSKT's OpenSSL fork
echo "Cloning UMSKT OpenSSL fork..."
git clone https://github.com/UMSKT/openssl.git --branch OpenSSL_1_1_1-stable --depth 1 openssl-1.1.1
# Set up proper MSYS2 environment
export MSYS=winsymlinks:nativestrict
export MSYS2_PATH_TYPE=strict
export LANG=en_US.UTF-8
export PATH="/usr/bin:/mingw64/bin:$PATH"
export PATH="/c/TDM-GCC-64/bin:$PATH"
export MSYSTEM=${{ matrix.msystem }}
cd openssl-1.1.1
if [[ ${{ matrix.arch }} == "x86" ]]; then
/usr/bin/perl Configure mingw --prefix=$(cygpath -u "$GITHUB_WORKSPACE")/OpenSSL-TDM-${{ matrix.arch }} --openssldir=$(cygpath -u "$GITHUB_WORKSPACE")/OpenSSL-TDM-32 no-tests no-sse2 no-asm no-threads -DOPENSSL_DEV_NO_ATOMICS -mno-mmx -mno-sse -mno-sse2 -march=i686 -mtune=generic
else
/usr/bin/perl Configure mingw64 --prefix=$(cygpath -u "$GITHUB_WORKSPACE")/OpenSSL-TDM-${{ matrix.arch }} --openssldir=$(cygpath -u "$GITHUB_WORKSPACE")/OpenSSL-TDM-64 no-tests no-asm -DOPENSSL_DEV_NO_ATOMICS -mno-mmx
fi
mingw32-make -j
mingw32-make install_sw
cd ..
- name: Save OpenSSL 1.1.1
if: steps.cache-openssl.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: |
${{ github.workspace }}/OpenSSL-TDM-${{ matrix.arch }}
key: openssl-1.1.1-${{ matrix.arch }}-${{ hashFiles('**/CMakeLists.txt') }}
- name: Set OpenSSL Environment
shell: pwsh
run: |
echo "OPENSSL_ROOT=$env:GITHUB_WORKSPACE/OpenSSL-TDM-${{ matrix.arch }}" | Out-File -FilePath $env:GITHUB_ENV -Append
echo "OPENSSL_LIBDIR=lib" | Out-File -FilePath $env:GITHUB_ENV -Append
echo "CMAKE_FLAGS=${{ matrix.cmake_flag }}" | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Configure UMSKT
shell: pwsh
run: |
$env:PATH = 'C:\TDM-GCC-64\bin;' + $env:PATH
$OPENSSL_ROOT = "$env:OPENSSL_ROOT"
$OPENSSL_LIB = "$OPENSSL_ROOT/$env:OPENSSL_LIBDIR"
$OPENSSL_INC = "$OPENSSL_ROOT/include"
cmake -G "MinGW Makefiles" `
-DCMAKE_C_COMPILER="gcc" `
-DCMAKE_CXX_COMPILER="g++" `
-DOPENSSL_ROOT_DIR="$OPENSSL_ROOT" `
-DOPENSSL_INCLUDE_DIR="$OPENSSL_INC" `
-DOPENSSL_CRYPTO_LIBRARY="$OPENSSL_LIB/libcrypto.a" `
-DOPENSSL_SSL_LIBRARY="$OPENSSL_LIB/libssl.a" `
-DCMAKE_C_FLAGS="$env:CMAKE_FLAGS" `
-DCMAKE_CXX_FLAGS="$env:CMAKE_FLAGS" `
.
- name: Build UMSKT
shell: pwsh
run: |
$env:PATH = 'C:\TDM-GCC-64\bin;' + $env:PATH
mingw32-make
- name: Run tests
shell: pwsh
run: |
Write-Host Test 1 - generating key
.\umskt.exe -b 2C -c 365 -s 069420 -v
Write-Host Test 2 - generatng confid
.\umskt.exe -i 253286028742154311079061239762245184619981623171292574
- name: Upload build artifact
uses: actions/upload-artifact@v4.6.2
with:
name: UMSKT-WinNT-${{ matrix.arch }}
path: umskt.exe

View File

@ -1,276 +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 TheTank20 on 06/13/2023
# @Maintainer Neo
name: C/C++ CI (Windows)
on:
push:
branches: [ "*" ]
paths-ignore: [ '**.md', 'doc/**', '.idea/**']
workflow_dispatch:
jobs:
build:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
arch: [x64, x86, arm64]
steps:
- name: Setup TDM-GCC
if: matrix.arch != 'arm64'
run: |
Write-Host Downloading TDM-GCC v10.3.0...
Invoke-WebRequest -Uri 'https://github.com/jmeubank/tdm-gcc/releases/download/v10.3.0-tdm64-2/tdm64-gcc-10.3.0-2.exe' -OutFile 'C:\Windows\temp\TDM-GCC-64.exe'
Write-Host Creating directory...
New-Item -ItemType Directory -Path 'C:\TDM-GCC-64'
Write-Host Copying files [Set 1/3]...
Start-Process '7z' -ArgumentList 'e C:\Windows\temp\TDM-GCC-64.exe -oC:\TDM-GCC-64 -y' -Wait
Write-Host Copying files [Set 2/3]...
Start-Process '7z' -ArgumentList 'e C:\TDM-GCC-64\*.tar.xz -oC:\TDM-GCC-64 -y' -Wait
Write-Host Copying files [Set 3/3]...
Start-Process '7z' -ArgumentList 'x C:\TDM-GCC-64\*.tar -oC:\TDM-GCC-64 -y' -Wait
Write-Host Adding environment variables...
$env:PATH = 'C:\TDM-GCC-64\bin;' + $env:PATH
[Environment]::SetEnvironmentVariable('PATH', $env:PATH, [EnvironmentVariableTarget]::Machine)
- name: Setup MSVC for ARM64
if: matrix.arch == 'arm64'
uses: ilammy/msvc-dev-cmd@v1
with:
arch: arm64
- name: Checkout Source Tree
uses: actions/checkout@v4
- name: Cache OpenSSL 3.1.2 (32-bit)
uses: actions/cache/restore@v4
if: matrix.arch == 'x86'
id: cache-openssl-32
with:
path: |
${{ github.workspace }}/OpenSSL-TDM-32
key: openssl-3.1.2-x86-${{ hashFiles('**/CMakeLists.txt') }}
- name: Cache OpenSSL 3.1.2 (64-bit)
uses: actions/cache/restore@v4
if: matrix.arch == 'x64'
id: cache-openssl-64
with:
path: |
${{ github.workspace }}/OpenSSL-TDM-64
key: openssl-3.1.2-x64-${{ hashFiles('**/CMakeLists.txt') }}
- name: Setup MSYS2
if: matrix.arch != 'arm64' && steps.cache-openssl-32.outputs.cache-hit != 'true' && steps.cache-openssl-64.outputs.cache-hit != 'true'
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.arch == 'x86' && 'MINGW32' || 'MINGW64' }}
update: true
install: >-
mingw-w64-${{ matrix.arch == 'x86' && 'i686' || 'x86_64' }}-gcc
mingw-w64-${{ matrix.arch == 'x86' && 'i686' || 'x86_64' }}-make
mingw-w64-x86_64-perl
perl
git
- name: Checkout and Compile OpenSSL 3.1.2
if: matrix.arch != 'arm64' && steps.cache-openssl-32.outputs.cache-hit != 'true' && steps.cache-openssl-64.outputs.cache-hit != 'true'
shell: msys2 {0}
run: |
# Clone UMSKT's OpenSSL fork
echo "Cloning UMSKT OpenSSL fork..."
git clone https://github.com/UMSKT/openssl.git --depth 1 openssl-3.1.2
- name: Checkout and Compile OpenSSL 3.1.2 (32-bit)
if: matrix.arch == 'x86' && steps.cache-openssl-32.outputs.cache-hit != 'true'
shell: msys2 {0}
run: |
# Set up proper MSYS2 environment
export MSYS=winsymlinks:nativestrict
export MSYS2_PATH_TYPE=strict
export LANG=en_US.UTF-8
export PATH="/usr/bin:/mingw64/bin:$PATH"
export PATH="/c/TDM-GCC-64/bin:$PATH"
export MSYSTEM=MINGW32
echo "Compiling 32-bit OpenSSL..."
cd openssl-3.1.2
/usr/bin/perl Configure mingw --prefix=$(cygpath -u "$GITHUB_WORKSPACE")/OpenSSL-TDM-32 --openssldir=$(cygpath -u "$GITHUB_WORKSPACE")/OpenSSL-TDM-32 no-tests no-sse2 no-asm no-threads -DOPENSSL_DEV_NO_ATOMICS -mno-mmx -mno-sse -mno-sse2 -march=i686 -mtune=generic
mingw32-make -j
mingw32-make install_sw
cd ..
- name: Checkout and Compile OpenSSL 3.1.2 (64-bit)
if: matrix.arch == 'x64' && steps.cache-openssl-64.outputs.cache-hit != 'true'
shell: msys2 {0}
run: |
# Set up proper MSYS2 environment
export MSYS=winsymlinks:nativestrict
export MSYS2_PATH_TYPE=strict
export LANG=en_US.UTF-8
export PATH="/usr/bin:/mingw64/bin:$PATH"
export PATH="/c/TDM-GCC-64/bin:$PATH"
export MSYSTEM=MINGW64
echo "Compiling 64-bit OpenSSL..."
cd openssl-3.1.2
/usr/bin/perl Configure mingw64 --prefix=$(cygpath -u "$GITHUB_WORKSPACE")/OpenSSL-TDM-64 --openssldir=$(cygpath -u "$GITHUB_WORKSPACE")/OpenSSL-TDM-64 no-tests no-asm -DOPENSSL_DEV_NO_ATOMICS -mno-mmx
mingw32-make -j
mingw32-make install_sw
cd ..
- name: Save OpenSSL 3.1.2 (32-bit)
if: matrix.arch == 'x86' && steps.cache-openssl-32.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: |
${{ github.workspace }}/OpenSSL-TDM-32
key: openssl-3.1.2-x86-${{ hashFiles('**/CMakeLists.txt') }}
- name: Save OpenSSL 3.1.2 (64-bit)
if: matrix.arch == 'x64' && steps.cache-openssl-64.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: |
${{ github.workspace }}/OpenSSL-TDM-64
key: openssl-3.1.2-x64-${{ hashFiles('**/CMakeLists.txt') }}
- name: Set OpenSSL Environment (32-bit)
if: matrix.arch == 'x86'
shell: pwsh
run: |
echo "OPENSSL_ROOT=$env:GITHUB_WORKSPACE/OpenSSL-TDM-32" | Out-File -FilePath $env:GITHUB_ENV -Append
echo "OPENSSL_LIBDIR=lib" | Out-File -FilePath $env:GITHUB_ENV -Append
echo "CMAKE_FLAGS=-m32" | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Set OpenSSL Environment (64-bit)
if: matrix.arch == 'x64'
shell: pwsh
run: |
echo "OPENSSL_ROOT=$env:GITHUB_WORKSPACE/OpenSSL-TDM-64" | Out-File -FilePath $env:GITHUB_ENV -Append
echo "OPENSSL_LIBDIR=lib" | Out-File -FilePath $env:GITHUB_ENV -Append
echo "CMAKE_FLAGS=-m64" | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Setup vcpkg for ARM64
if: matrix.arch == 'arm64'
shell: pwsh
run: |
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
.\bootstrap-vcpkg.bat
echo "VCPKG_ROOT=$env:GITHUB_WORKSPACE/vcpkg" | Out-File -FilePath $env:GITHUB_ENV -Append
echo "VCPKG_DEFAULT_BINARY_CACHE=$env:GITHUB_WORKSPACE/vcpkg/bincache" | Out-File -FilePath $env:GITHUB_ENV -Append
echo "VCPKG_BINARY_SOURCES=clear;default,readwrite" | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Cache vcpkg packages
if: matrix.arch == 'arm64'
uses: actions/cache@v4
with:
path: |
${{ github.workspace }}/vcpkg/bincache
${{ github.workspace }}/vcpkg/installed
${{ github.workspace }}/vcpkg/packages
key: vcpkg-arm64-${{ hashFiles('**/CMakeLists.txt') }}-${{ hashFiles('**/*.cmake') }}
restore-keys: |
vcpkg-arm64-${{ hashFiles('**/CMakeLists.txt') }}-
vcpkg-arm64-
- name: Install OpenSSL for ARM64
if: matrix.arch == 'arm64'
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path $env:VCPKG_DEFAULT_BINARY_CACHE
& "$env:VCPKG_ROOT\vcpkg.exe" install openssl:arm64-windows-static --clean-after-build
- name: Configure UMSKT (TDM-GCC ${{ matrix.arch }})
if: matrix.arch != 'arm64'
shell: pwsh
run: |
$env:PATH = 'C:\TDM-GCC-64\bin;' + $env:PATH
$OPENSSL_ROOT = "$env:OPENSSL_ROOT"
$OPENSSL_LIB = "$OPENSSL_ROOT/$env:OPENSSL_LIBDIR"
$OPENSSL_INC = "$OPENSSL_ROOT/include"
cmake -G "MinGW Makefiles" `
-DCMAKE_C_COMPILER="gcc" `
-DCMAKE_CXX_COMPILER="g++" `
-DOPENSSL_ROOT_DIR="$OPENSSL_ROOT" `
-DOPENSSL_INCLUDE_DIR="$OPENSSL_INC" `
-DOPENSSL_CRYPTO_LIBRARY="$OPENSSL_LIB/libcrypto.a" `
-DOPENSSL_SSL_LIBRARY="$OPENSSL_LIB/libssl.a" `
-DCMAKE_C_FLAGS="$env:CMAKE_FLAGS" `
-DCMAKE_CXX_FLAGS="$env:CMAKE_FLAGS" `
.
- name: Configure UMSKT (MSVC ARM64)
if: matrix.arch == 'arm64'
shell: pwsh
run: |
cmake -G "Visual Studio 17 2022" `
-A ARM64 `
-DWINDOWS_ARM=ON `
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" `
-DVCPKG_TARGET_TRIPLET=arm64-windows-static `
.
- name: Build UMSKT (TDM-GCC ${{ matrix.arch }})
if: matrix.arch != 'arm64'
shell: pwsh
run: |
$env:PATH = 'C:\TDM-GCC-64\bin;' + $env:PATH
mingw32-make
- name: Build UMSKT (MSVC ARM64)
if: matrix.arch == 'arm64'
shell: pwsh
run: |
cmake --build . --config Release
- name: Run tests (x86/x64)
if: matrix.arch != 'arm64'
shell: pwsh
run: |
Write-Host Test 1 - generating key
.\umskt.exe -b 2C -c 365 -s 069420 -v
Write-Host Test 2 - generatng confid
.\umskt.exe -i 253286028742154311079061239762245184619981623171292574
- name: Upload build artifact
uses: actions/upload-artifact@v4.6.2
with:
name: UMSKT-WinNT-${{ matrix.arch }}
path: ${{ matrix.arch == 'arm64' && 'Release/umskt.exe' || 'umskt.exe' }}
test-arm64:
needs: build
if: success()
runs-on: windows-11-arm
steps:
- name: Download ARM64 artifact
uses: actions/download-artifact@v4
with:
name: UMSKT-WinNT-arm64
path: .
- name: Run tests (ARM64)
shell: pwsh
run: |
Write-Host Test 1 - generating key
.\umskt.exe -b 2C -c 365 -s 069420 -v
Write-Host Test 2 - generatng confid
.\umskt.exe -i 253286028742154311079061239762245184619981623171292574

View File

@ -4,11 +4,13 @@
[![Zulip chat](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg)](https://umskt.zulipchat.com) [![Zulip chat](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg)](https://umskt.zulipchat.com)
[![libera.chat - #mspid](https://img.shields.io/badge/libera.chat-%23mspid-brightgreen)](https://web.libera.chat/gamja/?nick=Guest?#mspid) [![libera.chat - #mspid](https://img.shields.io/badge/libera.chat-%23mspid-brightgreen)](https://web.libera.chat/gamja/?nick=Guest?#mspid)
[![Discord](https://dcbadge.vercel.app/api/server/cUZxfNNSdt?style=flat)](https://discord.gg/PpBSpuphWM) [![Discord](https://img.shields.io/discord/1154155510887620729?label=discord&color=%235865F2)](https://discord.gg/PpBSpuphWM)
**Build status** **Build status**
[![C/C++ CI (Windows)](https://github.com/UMSKT/UMSKT/actions/workflows/windows.yml/badge.svg)](../../actions/workflows/windows.yml) [![C/C++ CI (Windows x86+64)](https://github.com/UMSKT/UMSKT/actions/workflows/windows-x86-x64.yml/badge.svg)](../../actions/workflows/windows-x86-x64.yml)
[![C/C++ CI (Windows ARM)](https://github.com/UMSKT/UMSKT/actions/workflows/windows-arm.yml/badge.svg)](../../actions/workflows/windows-arm.yml)
[![C/C++ CI (macOS)](https://github.com/UMSKT/UMSKT/actions/workflows/macos.yml/badge.svg)](../../actions/workflows/macos.yml) [![C/C++ CI (macOS)](https://github.com/UMSKT/UMSKT/actions/workflows/macos.yml/badge.svg)](../../actions/workflows/macos.yml)
@ -50,11 +52,18 @@ In light of the recent exponential interest in this project I've decided to put
* MS-DOS 6.22 or later * MS-DOS 6.22 or later
* Any DOS-based version of Windows * Any DOS-based version of Windows
* Windows NT 4.0 or later (via NTVDM) * Windows NT 4.0 or later (via NTVDM)
#### Windows #### Windows (x86/x64)
* i686 processor or better * i686 processor or better
* Windows XP or later * Windows XP or later
*Note: Processors barely meeting the minimum system requirements for XP may not work. Use the MS-DOS version via NTVDM in that case.* > [!WARNING]
> Processors barely meeting the minimum system requirements for XP may not work. Use the MS-DOS version via NTVDM in that case.
#### Windows (ARM32/64)
* Windows 11 21H2 or later
> [!NOTE]
> This is just all we can test. Try to get it to run on Windows RT, make an issue if it doesn't run and we'll try to fix it.
#### macOS #### macOS
* Apple Silicon or x86_64 processor * Apple Silicon or x86_64 processor
* Latest version of macOS * Latest version of macOS
@ -74,15 +83,14 @@ In light of the recent exponential interest in this project I've decided to put
* *(GitHub account \*not\* required)* * *(GitHub account \*not\* required)*
* Download the latest release for your operating system and architecture from [the releases page](../../releases) * Download the latest release for your operating system and architecture from [the releases page](../../releases)
* **Note:** Before continuing, please ensure you have the `umskt` executable extracted and on UNIX-like systems, have execution permissions (`chmod +x umskt`). > [!IMPORTANT]
> Before continuing, please ensure you have the `umskt` executable extracted and on UNIX-like systems, have execution permissions (`chmod +x umskt`).
#### 2. Install OpenSSL. #### 2. Run `umskt` to generate a key, or add `--help` or `-h` to see more options.
For Windows, click [here](https://slproweb.com/products/Win32OpenSSL.html) and choose the right version. For other operating systems, consult your package manager. > [!IMPORTANT]
*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.* > On macOS, like all unsigned executables, you'll need to hold Ctrl while right clicking and selecting Open to actually open it.
#### 3. Run `umskt` to generate a key, or add `--help` or `-h` to see more options. #### 3. *(Activation step for `Retail` and `OEM` only)*
*Note: on macOS, like all unsigned executables, you'll need to hold Ctrl while right clicking and selecting Open to actually open it.*
#### 4. *(Activation step for `Retail` and `OEM` only)*
* After installation, you will be prompted to activate Windows. * After installation, you will be prompted to activate Windows.

View File

@ -0,0 +1,302 @@
import hashlib
def add_pid_cksum(pid):
sumPID = 0
val = pid
while val != 0:
sumPID += val % 10
val //= 10
return pid * 10 + 7 - (sumPID % 7)
def decode_iid_new_version(iid, pid):
buffer = [0] * 5
for i in range(len(buffer)):
buffer[i] = int.from_bytes(iid[i*4:i*4+4], byteorder='little')
# print("buffer[" + str(i) + "] = " + hex(buffer[i])[2:].zfill(8))
v1 = (buffer[3] & 0xFFFFFFF8) | 2 # Not really sure but seems to work
v2 = (buffer[3] & 7) << 29 | buffer[2] >> 3
hardwareId = (v1) << 32 | v2
hardwareId = int(hardwareId).to_bytes(8, byteorder='little')
v3 = (buffer[0] & 0xFFFFFF80) >> 7 & 0xFFFFFFFF
unknown1 = v3 & 0x000007FF
v4 = v3 & 0xFFFFF800
v5 = buffer[1] & 0x7F
v6 = buffer[1] >> 7
v7 = (v5 << 25 | v4) >> 11
productID1 = v7 & 0x000003FF
v8 = v7 & 0xFFFFFC00
v9 = (v6 >> 11) & 0x00001FFF
v10 = v9 & 0x00001C00
v11 = v9 & 0x000003FF
v12 = ((v6 << 21) & 0xFFFFFFFF | v8) >> 10
v13 = (v11 << 22) & 0xFFFFFFFF
v14 = v13 | v12
productID3RandomPart = (v14 & 0x3FF00000) >> 20
productID2NoChecksum = v14 & 0x000FFFFF
v15 = v13 >> 30 # 0x00000003
v16 = v10 >> 8 # 0x0000001C
v17 = (buffer[2] & 7) << 6 # 0x000001C0
v18 = (buffer[4] & 1) << 9 # 0x00000200
authInfo = v18 | v17 | v16 | v15 # Not that important bug: bit 5 is not present
productID0 = pid[0]
productID2 = add_pid_cksum(productID2NoChecksum)
productID3 = (pid[3] // 1000) * 1000 + productID3RandomPart
# Just to remember: public key index I of pid (XXXXX-XXX-XXXXXXX-IIXXX) = BINK ID // 2
# Where is actually located the version number?
# version1 = buffer[0] & 7
# print("Decoded IID Version1?: " + str(version1))
# version2 = (int.from_bytes(iid[8:17], byteorder='little') >> 52) & 7
# print("Decoded IID Version2?: " + str(version2))
# version3 = buffer[3] & 7
# print("Decoded IID Version3?: " + str(version3))
if productID1 != pid[1] or productID2 != pid[2] or pid[3] % 1000 != productID3RandomPart:
print("Error: Product ID not matching!")
return 0, 0, 0
return hardwareId, authInfo, unknown1
# Validate installation ID checksum
def validate_cksum(n):
print("Checksumming installation ID...")
n = n.replace("-", "")
cksum = 0
for i, k in enumerate(map(int, n)):
if (i + 1) % 6 == 0 or i == len(n) - 1:
print("Expected last digit", cksum % 7, "got", k)
if cksum % 7 != k:
return None
cksum = 0
else:
cksum += k * (i % 2 + 1)
parts = [n[i:i+5] for i in range(0, len(n), 6)]
n_out = "".join(parts)
if len(n_out) == 42:
n_out = n_out[:-1]
if len(n_out) != 45 and len(n_out) != 41:
return None
return int(n_out)
# Insert checksum digits into confirmation ID
def add_cksum(n):
cksums = []
n = str(n).zfill(35)
parts = [n[i:i+5] for i in range(0, len(n), 5)]
for p in parts:
cksum = 0
for i, k in enumerate(map(int, p)):
cksum += k * (i % 2 + 1)
cksums.append(str(cksum % 7))
n_out = ""
for i in range(7):
n_out += parts[i] + cksums[i] + ("-" if i != 6 else "")
return n_out
def encrypt(decrypted, key):
size_half = len(decrypted) // 2
size_half_dwords = size_half - (size_half % 4)
last = decrypted[size_half*2:]
decrypted = decrypted[:size_half*2]
for i in range(4):
first = decrypted[:size_half]
second = decrypted[size_half:]
# A magic byte 0x79 is now added at the beginning of the list of bytes to hash
sha1_result = hashlib.sha1(bytearray.fromhex("79") + second + key).digest()
sha1_result = (sha1_result[:size_half_dwords] +
sha1_result[size_half_dwords+4-(size_half%4) : size_half+4-(size_half%4)])
decrypted = second + bytes(x^^y for x,y in zip(first, sha1_result))
return decrypted + last
def decrypt(encrypted, key):
size_half = len(encrypted) // 2
size_half_dwords = size_half - (size_half % 4)
last = encrypted[size_half*2:]
encrypted = encrypted[:size_half*2]
for i in range(4):
first = encrypted[:size_half]
second = encrypted[size_half:]
# A magic byte 0x79 is now added at the beginning of the list of bytes to hash
sha1_result = hashlib.sha1(bytearray.fromhex("79") + first + key).digest()
sha1_result = (sha1_result[:size_half_dwords] +
sha1_result[size_half_dwords+4-(size_half%4) : size_half+4-(size_half%4)])
encrypted = bytes(x^^y for x,y in zip(second, sha1_result)) + first
return encrypted + last
# Find v of divisor (u, v) of curve y^2 = F(x)
def find_v(u):
f = F % u
c2 = u[1]^2 - 4 * u[0]
c1 = 2 * f[0] - f[1] * u[1]
if c2 == 0:
if c1 == 0:
return None
try:
v1 = sqrt(f[1]^2 / (2 * c1))
v1.lift()
except:
return None
else:
try:
d = 2 * sqrt(f[0]^2 + f[1] * (f[1] * u[0] - f[0] * u[1]))
v1_1 = sqrt((c1 - d)/c2)
v1_2 = sqrt((c1 + d)/c2)
except:
return None
try:
v1_1.lift()
v1 = v1_1
except:
try:
v1_2.lift()
v1 = v1_2
except:
return None
v0 = (f[1] + u[1] * v1^2) / (2 * v1)
v = v0 + v1 * x
assert (v^2 - f) % u == 0
return v
# order of field Fp
p = 0x16E48DD18451FE9
# Coefficients of F
coeffs = [0, 0xE5F5ECD95C8FD2, 0xFF28276F11F61, 0xFB2BD9132627E6, 0xE5F5ECD95C8FD2, 1]
# This constant inverts multiplication by 0x10001 in verification
INV = 0x01fb8cf48a70dfefe0302a1f7a5341
# Key to decrypt installation IDs
IID_KEY = b'\x5A\x30\xB9\xF3'
#"""
# minimal quadratic non-residue of p
mqnr = least_quadratic_nonresidue(p)
# Galois field of order p
Fp = GF(p)
# Polynomial field Fp[x] over Fp
Fpx.<x> = Fp[]
# Hyperellptic curve function
F = sum(k*x^i for i, k in enumerate(coeffs))
# Hyperelliptic curve E: y^2 = F(x) over Fp
E = HyperellipticCurve(F)
# The jacobian over E
J = E.jacobian()
# unpack&decrypt installationId
installationId = validate_cksum(input("Installation ID (dashes optional): "))
productId = input("Product ID (with dashes): ").split("-")
pid = [int(x) for x in productId]
# Office 2003 Professional Edition FWYTB-C7PPP-4497G-FV737-2HQWG (UMSKT generated)
# installationId = 020572391118023984229275432949036355811509788 # 020570-239116-180233-984220-927546-329495-036352-581151-097880
# pid = [73931, 746, 6952006, 57345] # 73931-746-6952006-57345
# Office 2007 Enterprise Edition XGQ68-R77XM-FPYFH-B436K-46QDY (UMSKT generated)
# installationId = 032422660398632786377841998280144793681167281 # 032424-266032-986324-786370-784193-982801-144791-368115-672814
# pid = [89388, 864, 6523093, 65443] # 89388-864-6523093-65443
print(installationId)
if not installationId:
raise Exception("Invalid Installation ID (checksum fail)")
installationIdSize = 19 if len(str(installationId)) > 41 else 17 # 17 for XP Gold, 19 for SP1+ (includes 12 bits of sha1(product key))
iid = int(installationId).to_bytes(installationIdSize, byteorder='little')
iid = decrypt(iid, IID_KEY)
hwid, authInfo, unknown1 = decode_iid_new_version(iid, pid)
print("\nDecoded Hardware ID: " + hex(int.from_bytes(hwid, byteorder='big')))
print("Decoded AuthInfo: " + hex(authInfo))
print("Decoded Unknown1: " + hex(unknown1))
assert hwid != 0
key = hwid + int((pid[0] << 41 | pid[1] << 58 | pid[2] << 17 | pid[3]) & ((1 << 64) - 1)).to_bytes(8, byteorder='little')
data = [0x00] * 14
# data = b'\xb9g\xdd\xe1\xb0\xef-\x1e\xbd\x0frE\xd8\xbe'
print("\nConfirmation IDs:")
for i in range(0x81):
data[6] = i # Attempt number was byte 7 in older confirmation ID version but it is now byte 6
# Encrypt conf ID, find u of divisor (u, v)
encrypted = encrypt(bytes(data), key)
encrypted = int.from_bytes(encrypted, byteorder="little")
x1, x2 = Fp(encrypted % p), Fp((encrypted // p) + 1)
u1, u0 = x1 * 2, (x1 ^ 2) - ((x2 ^ 2) * mqnr)
u = x^2 + u1 * x + u0
# Generate original divisor
v = find_v(u)
if not v:
print(v)
continue
d2 = J(u, v)
divisor = d2 * INV
# Get x1 and x2
roots = [x for x, y in divisor[0].roots()]
if len(roots) > 0:
y = [divisor[1](r) for r in roots]
x1 = (-roots[0]).lift()
x2 = (-roots[1]).lift()
if (x1 > x2) or (y[0].lift() % 2 != y[1].lift() % 2):
x1 = (-roots[1]).lift()
x2 = (-roots[0]).lift()
else:
x2 = (divisor[0][1] / 2).lift()
x1 = sqrt((x2^2 - divisor[0][0]) / mqnr).lift() + p
# Win
conf_id = x1 * (p + 1) + x2
conf_id = add_cksum(conf_id)
print(conf_id)

433
extras/confid.ipynb Normal file
View File

@ -0,0 +1,433 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Constants (run this cell first!)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\"\"\"\n",
"# MS Plus! DME\n",
"\n",
"# order of field Fp \n",
"p = 0x16A5DABA0605983\n",
"# Coefficients of F\n",
"coeffs = [0x334f24f75caa0e, 0x1392ff62889bd7b, 0x135131863ba2db8, 0x153208e78006010, 0x163694f26056db, 1]\n",
"# This constant inverts multiplication by 0x10001 in verification\n",
"INV = 0x01c61212ece6107c4254c43a5d1181\n",
"# Key to decrypt installation IDs\n",
"IID_KEY = b'\\x6A\\xC8\\x5E\\xD4'\n",
"#\"\"\"\n",
"\n",
"#\"\"\"\n",
"# Office XP/2003\n",
"\n",
"# order of field Fp \n",
"p = 0x16E48DD18451FE9\n",
"# Coefficients of F\n",
"coeffs = [0, 0xE5F5ECD95C8FD2, 0xFF28276F11F61, 0xFB2BD9132627E6, 0xE5F5ECD95C8FD2, 1]\n",
"# This constant inverts multiplication by 0x10001 in verification\n",
"INV = 0x01fb8cf48a70dfefe0302a1f7a5341\n",
"# Key to decrypt installation IDs\n",
"IID_KEY = b'\\x5A\\x30\\xB9\\xF3'\n",
"#\"\"\"\n",
"\n",
"\"\"\"\n",
"# Whistler 2428 (could be others)\n",
"\n",
"# order of field Fp \n",
"p = 0x16BD82821354FA3\n",
"# Coefficients of F\n",
"coeffs = [0, 0xDEFD8C5651954F, 0xA23AA12556ECE5, 0x89D79AD61B786D, 0xCCA087F0A6A4A4, 1]\n",
"# This constant inverts multiplication by 0x10001 in verification\n",
"INV = 0xd9ed873ed84a45761c23fd7fafd1\n",
"# Key to decrypt installation IDs\n",
"IID_KEY = b'\\x6A\\xC8\\x5E\\xD4'\n",
"#\"\"\"\n",
"\n",
"\n",
"\"\"\"\n",
"# Windows XP/Server 2003/Longhorn Pre-Reset\n",
"\n",
"# order of field Fp \n",
"p = 0x16A6B036D7F2A79\n",
"# Coefficients of F\n",
"coeffs = [0, 0x21840136C85381, 0x44197B83892AD0, 0x1400606322B3B04, 0x1400606322B3B04, 1]\n",
"# This constant inverts multiplication by 0x10001 in verification\n",
"INV = 0x40DA7C36D44C04E21B9D10F127C1\n",
"# Key to decrypt installation IDs\n",
"IID_KEY = b'\\x6A\\xC8\\x5E\\xD4'\n",
"#\"\"\"\n",
"\n",
"# minimal quadratic non-residue of p\n",
"mqnr = least_quadratic_nonresidue(p)\n",
"# Galois field of order p\n",
"Fp = GF(p)\n",
"# Polynomial field Fp[x] over Fp\n",
"Fpx.<x> = Fp[]\n",
"\n",
"# Hyperellptic curve function\n",
"F = sum(k*x^i for i, k in enumerate(coeffs))\n",
"# Hyperelliptic curve E: y^2 = F(x) over Fp\n",
"E = HyperellipticCurve(F)\n",
"# The jacobian over E\n",
"J = E.jacobian()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Generate Confirmation ID"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"import hashlib\n",
"\n",
"# Validate installation ID checksum\n",
"def validate_cksum(n):\n",
" print(\"Checksumming installation ID...\")\n",
" n = n.replace(\"-\", \"\")\n",
"\n",
" cksum = 0\n",
" for i, k in enumerate(map(int, n)):\n",
" if (i + 1) % 6 == 0 or i == len(n) - 1:\n",
" print(\"Expected last digit\", cksum % 7, \"got\", k)\n",
" if cksum % 7 != k:\n",
" return None\n",
" \n",
" cksum = 0\n",
" else:\n",
" cksum += k * (i % 2 + 1)\n",
" \n",
" parts = [n[i:i+5] for i in range(0, len(n), 6)]\n",
" n_out = \"\".join(parts)\n",
" \n",
" if len(n_out) == 42:\n",
" n_out = n_out[:-1]\n",
" \n",
" if len(n_out) != 45 and len(n_out) != 41:\n",
" return None\n",
" \n",
" return int(n_out)\n",
"\n",
"# Insert checksum digits into confirmation ID\n",
"def add_cksum(n):\n",
" cksums = []\n",
" n = str(n).zfill(35)\n",
" parts = [n[i:i+5] for i in range(0, len(n), 5)]\n",
" \n",
" for p in parts:\n",
" cksum = 0\n",
" \n",
" for i, k in enumerate(map(int, p)):\n",
" cksum += k * (i % 2 + 1)\n",
" \n",
" cksums.append(str(cksum % 7))\n",
" \n",
" n_out = \"\"\n",
" \n",
" for i in range(7):\n",
" n_out += parts[i] + cksums[i] + (\"-\" if i != 6 else \"\")\n",
" \n",
" return n_out\n",
"\n",
"def encrypt(decrypted, key):\n",
" size_half = len(decrypted) // 2\n",
" size_half_dwords = size_half - (size_half % 4)\n",
" last = decrypted[size_half*2:]\n",
" decrypted = decrypted[:size_half*2]\n",
" for i in range(4):\n",
" first = decrypted[:size_half]\n",
" second = decrypted[size_half:]\n",
" sha1_result = hashlib.sha1(second + key).digest()\n",
" sha1_result = (sha1_result[:size_half_dwords] +\n",
" sha1_result[size_half_dwords+4-(size_half%4) : size_half+4-(size_half%4)])\n",
" decrypted = second + bytes(x^^y for x,y in zip(first, sha1_result))\n",
" return decrypted + last\n",
"\n",
"def decrypt(encrypted, key):\n",
" size_half = len(encrypted) // 2\n",
" size_half_dwords = size_half - (size_half % 4)\n",
" last = encrypted[size_half*2:]\n",
" encrypted = encrypted[:size_half*2]\n",
" for i in range(4):\n",
" first = encrypted[:size_half]\n",
" second = encrypted[size_half:]\n",
" sha1_result = hashlib.sha1(first + key).digest()\n",
" sha1_result = (sha1_result[:size_half_dwords] +\n",
" sha1_result[size_half_dwords+4-(size_half%4) : size_half+4-(size_half%4)])\n",
" encrypted = bytes(x^^y for x,y in zip(second, sha1_result)) + first\n",
" return encrypted + last\n",
"\n",
"# Find v of divisor (u, v) of curve y^2 = F(x)\n",
"def find_v(u):\n",
" f = F % u\n",
" c2 = u[1]^2 - 4 * u[0]\n",
" c1 = 2 * f[0] - f[1] * u[1]\n",
" \n",
" if c2 == 0:\n",
" if c1 == 0:\n",
" return None\n",
" \n",
" try:\n",
" v1 = sqrt(f[1]^2 / (2 * c1))\n",
" v1.lift()\n",
" except:\n",
" return None\n",
" else:\n",
" try:\n",
" d = 2 * sqrt(f[0]^2 + f[1] * (f[1] * u[0] - f[0] * u[1]))\n",
" v1_1 = sqrt((c1 - d)/c2)\n",
" v1_2 = sqrt((c1 + d)/c2)\n",
" except:\n",
" return None\n",
"\n",
" try:\n",
" v1_1.lift()\n",
" v1 = v1_1\n",
" except:\n",
" try:\n",
" v1_2.lift()\n",
" v1 = v1_2\n",
" except:\n",
" return None\n",
" \n",
" v0 = (f[1] + u[1] * v1^2) / (2 * v1)\n",
" v = v0 + v1 * x\n",
" \n",
" assert (v^2 - f) % u == 0\n",
" return v\n",
"\n",
"# unpack&decrypt installationId\n",
"installationId = validate_cksum(input(\"Installation ID (dashes optional): \"))\n",
"# installationId = 11234597509478704096883784033789146715149\n",
"print(installationId)\n",
"\n",
"if not installationId:\n",
" raise Exception(\"Invalid Installation ID (checksum fail)\")\n",
"\n",
"installationIdSize = 19 if len(str(installationId)) > 41 else 17 # 17 for XP Gold, 19 for SP1+ (includes 12 bits of sha1(product key))\n",
"iid = int(installationId).to_bytes(installationIdSize, byteorder='little')\n",
"iid = decrypt(iid, IID_KEY)\n",
"hwid = iid[:8]\n",
"productid = int.from_bytes(iid[8:17], byteorder='little')\n",
"productkeyhash = iid[17:]\n",
"pid1 = productid & ((1 << 17) - 1)\n",
"pid2 = (productid >> 17) & ((1 << 10) - 1)\n",
"pid3 = (productid >> 27) & ((1 << 24) - 1)\n",
"version = (productid >> 52) & 7\n",
"pid4 = productid >> 55\n",
"\n",
"if version != (4 if len(iid) == 17 else 5):\n",
" print(f\"Invalid Installation ID (unknown version {version})\")\n",
"\n",
"print(installationIdSize)\n",
"print(pid1, pid2, pid3, pid4)\n",
"\n",
"key = hwid + int((pid1 << 41 | pid2 << 58 | pid3 << 17 | pid4) & ((1 << 64) - 1)).to_bytes(8, byteorder='little')\n",
"\n",
"data = [0x00] * 14\n",
"# data = b'\\xb9g\\xdd\\xe1\\xb0\\xef-\\x1e\\xbd\\x0frE\\xd8\\xbe'\n",
"print(\"\\nConfirmation IDs:\")\n",
"\n",
"for i in range(0x81):\n",
" data[4] = i\n",
" # Encrypt conf ID, find u of divisor (u, v)\n",
" encrypted = encrypt(bytes(data), key)\n",
" encrypted = int.from_bytes(encrypted, byteorder=\"little\")\n",
" x1, x2 = Fp(encrypted % p), Fp((encrypted // p) + 1)\n",
" u1, u0 = x1 * 2, (x1 ^ 2) - ((x2 ^ 2) * mqnr)\n",
" u = x^2 + u1 * x + u0\n",
"\n",
" # Generate original divisor\n",
" v = find_v(u)\n",
" \n",
" if not v:\n",
" continue\n",
" \n",
" d2 = J(u, v)\n",
" divisor = d2 * INV\n",
" \n",
" # Get x1 and x2\n",
" roots = [x for x, y in divisor[0].roots()]\n",
"\n",
" if len(roots) > 0:\n",
" y = [divisor[1](r) for r in roots]\n",
" x1 = (-roots[0]).lift()\n",
" x2 = (-roots[1]).lift()\n",
"\n",
" if (x1 > x2) or (y[0].lift() % 2 != y[1].lift() % 2):\n",
" x1 = (-roots[1]).lift()\n",
" x2 = (-roots[0]).lift()\n",
" else:\n",
" x2 = (divisor[0][1] / 2).lift()\n",
" x1 = sqrt((x2^2 - divisor[0][0]) / mqnr).lift() + p\n",
"\n",
" # Win\n",
" conf_id = x1 * (p + 1) + x2\n",
" conf_id = add_cksum(conf_id)\n",
" print(conf_id)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Validate Confirmation ID (originally by diamondggg)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"import hashlib\n",
"\n",
"# 226512-274743-842923-777124-961370-722240-570042-517722-757426\n",
"installationId = 44706039542602728435285810860722693781\n",
"installationIdSize = 17 # 17 for XP Gold, 19 for SP1+ (includes 12 bits of sha1(product key))\n",
"# all three of following are valid generated\n",
"# 013705-060122-603141-961392-086136-909901-494476\n",
"confid = input(\"Confirmation ID: \").replace(\"-\", \"\")\n",
"confirmationId = int(\"\".join([confid[i:i+5] for i in range(0, len(confid), 6)]))\n",
"# confirmationId = 13009861034010972507754924748629391\n",
"print(confirmationId)\n",
"# 022032-220754-159721-909624-985141-504586-914001\n",
"#confirmationId = 2203220751597290962985145045891400\n",
"# 137616-847280-708585-827476-874935-313366-790880\n",
"#confirmationId = 13761847287085882747874933133679088\n",
"\n",
"def decrypt(encrypted, key):\n",
" size_half = len(encrypted) // 2\n",
" size_half_dwords = size_half - (size_half % 4)\n",
" last = encrypted[size_half*2:]\n",
" encrypted = encrypted[:size_half*2]\n",
" for i in range(4):\n",
" first = encrypted[:size_half]\n",
" second = encrypted[size_half:]\n",
" sha1_result = hashlib.sha1(first + key).digest()\n",
" sha1_result = (sha1_result[:size_half_dwords] +\n",
" sha1_result[size_half_dwords+4-(size_half%4) : size_half+4-(size_half%4)])\n",
" encrypted = bytes(x^^y for x,y in zip(second, sha1_result)) + first\n",
" return encrypted + last\n",
"\n",
"# unpack&decrypt installationId\n",
"iid = int(installationId).to_bytes(installationIdSize, byteorder='little')\n",
"iid = decrypt(iid, IID_KEY)\n",
"hwid = iid[:8]\n",
"productid = int.from_bytes(iid[8:17], byteorder='little')\n",
"# productkeyhash is not used for validation, it exists just to allow the activation server to reject keygenned pids\n",
"productkeyhash = iid[17:]\n",
"pid1 = productid & ((1 << 17) - 1)\n",
"pid2 = (productid >> 17) & ((1 << 10) - 1)\n",
"pid3 = (productid >> 27) & ((1 << 24) - 1)\n",
"version = (productid >> 52) & 7\n",
"pid4 = productid >> 55\n",
"\n",
"print(pid1, pid2, pid3, pid4)\n",
"\n",
"if version != (4 if len(iid) == 17 else 5):\n",
" print(version)\n",
"\n",
"key = hwid + int((pid1 << 41 | pid2 << 58 | pid3 << 17 | pid4) & ((1 << 64) - 1)).to_bytes(8, byteorder='little')\n",
"\n",
"# deserialize divisor\n",
"x1 = confirmationId // (p + 1)\n",
"x2 = confirmationId % (p + 1)\n",
"if x1 <= p:\n",
" # two or less points over GF(p)\n",
" point1 = E.lift_x(Fp(-x1)) if x1 != p else None\n",
" point2 = E.lift_x(Fp(-x2)) if x2 != p else None\n",
" if point1 is not None and point2 is not None:\n",
" # there are 4 variants of how lift_x() could select both y-s\n",
" # we don't distinguish D and -D, but this still leaves 2 variants\n",
" # the chosen one is encoded by order of x1 <=> x2\n",
" lastbit1 = point1[1].lift() & 1\n",
" lastbit2 = point2[1].lift() & 1\n",
" if x2 < x1:\n",
" if lastbit1 == lastbit2:\n",
" point2 = E(point2[0], -point2[1])\n",
" else:\n",
" if lastbit1 != lastbit2:\n",
" point2 = E(point2[0], -point2[1])\n",
" point1 = J(point1) if point1 is not None else J(0)\n",
" point2 = J(point2) if point2 is not None else J(0)\n",
" divisor = point1 + point2\n",
"else:\n",
" # a pair of conjugate points over GF(p^2)\n",
" f = (x+x2)*(x+x2) - mqnr*x1*x1 # 43 is the minimal quadratic non-residue in Fp\n",
" Fp2 = GF(p^2)\n",
" point1 = E.lift_x(f.roots(Fp2)[0][0])\n",
" point2 = E(Fp2)(point1[0].conjugate(), point1[1].conjugate())\n",
" divisor = J(Fp2)(point1) + J(Fp2)(point2)\n",
" divisor = J(Fpx(divisor[0]), Fpx(divisor[1])) #return from Fp2 to Fp\n",
"\n",
"d2 = divisor * 0x10001\n",
"assert d2[0].degree() == 2\n",
"x1 = d2[0][1]/2\n",
"x2 = sqrt((x1*x1-d2[0][0])/mqnr)\n",
"\n",
"encrypted = x1.lift() + (x2.lift() - 1) * p\n",
"encrypted = int(encrypted).to_bytes(14,byteorder='little')\n",
"\n",
"# end of the math\n",
"decrypted = decrypt(encrypted, key)\n",
"print(decrypted.hex())\n",
"# 0000000000000001000000000000 for the first confirmationId\n",
"# 0000000000000002000000000000 for the second confirmationId\n",
"# 0000000000000006000000000000 for the last confirmationId\n",
"assert decrypted[8:] == b'\\0' * 6\n",
"assert decrypted[7] <= 0x80\n",
"# all zeroes in decrypted[0:7] are okay for the checker\n",
"# more precisely: if decrypted[6] == 0, first 6 bytes can be anything\n",
"# otherwise, decrypted[0] = length, and decrypted[1:1+length] must match first length bytes of sha1(product key)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "SageMath 9.0",
"language": "sage",
"name": "sagemath"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.10"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

65
extras/parse_dpcdll.py Normal file
View File

@ -0,0 +1,65 @@
from glob import glob
from struct import unpack
import json
import sys
def readint(f):
return unpack("<I", f.read(4))[0]
if __name__ == "__main__":
if len(sys.argv) < 3:
print(f"usage: {sys.argv[0]} dpcdll.dll out.json")
lic_types = ["NULL", "Volume", "Retail", "Evaluation", "Tablet", "OEM-SLP", "Embedded"]
dpcdata = []
with open(sys.argv[1], "rb") as f:
tmp = f.read()
f.seek(tmp.find(b"\x1e\x00\x00\x00\xff\xff\xff\x7f") - 20)
del tmp
while f.read(4) != b"\x00\x00\x00\x00":
f.seek(-164, 1)
f.seek(-4, 1)
last_ind = -1
ind = 0
dpcentry = {}
while True:
ind = readint(f)
bink_id = hex(readint(f))[2:].zfill(8).upper()
min_pid = readint(f)
max_pid = readint(f)
if min_pid > 999 or max_pid > 999:
break
lic_type = readint(f)
if lic_type > 6:
break
if lic_type == 2 and int(bink_id, 16) % 2 == 1:
lic_type_str = "OEM-COA"
else:
lic_type_str = lic_types[lic_type]
days_to_act = readint(f)
eval_days = readint(f)
sig_len = readint(f)
f.read(sig_len)
dpcdata.append({
"index": ind,
"bink": bink_id,
"pid_range": [min_pid, max_pid],
"type": lic_type_str,
"days_to_activate": days_to_act,
"days_evaluation": eval_days
})
with open(sys.argv[2], "w") as f:
f.write(json.dumps(dpcdata, indent=4))

98
extras/parse_pubkey.py Normal file
View File

@ -0,0 +1,98 @@
from struct import pack, unpack, calcsize
from json import dumps
from os.path import basename
import sys
def readint(f):
return unpack("<I", f.read(4))[0]
def readstc(f, s):
s = "<" + s
sz = calcsize(s)
return unpack(s, f.read(sz))
pubkey_data = {}
with open(sys.argv[1], "rb") as f:
magic1 = readint(f)
if magic1 != 0x44556677:
raise Exception("Invalid pubkey format")
f.read(4)
field_data_size = readint(f)
magic2 = readint(f)
if magic2 != 0x00112233:
raise Exception("Invalid pubkey format")
f.read(4)
data = readstc(f, "B" * 3 + "I" * 9)
must_be_0 = data[0]
if must_be_0 != 0:
raise Exception("Invalid field data")
size_modulus = data[1]
size_order = data[2]
ext_deg1 = data[3]
ext_deg2 = data[4]
offset_modulus = data[8]
f.read(1)
h1_bases = list(readstc(f, "B" * size_modulus))
modulus = int.from_bytes(f.read(size_modulus), "little")
order = int.from_bytes(f.read(size_order), "little")
ext_minpoly1 = list(readstc(f, "B" * (ext_deg1 + 1)))
ext_minpoly2 = list(readstc(f, "B" * (ext_deg2 + 1)))
f.read(size_modulus * 2)
ec_a = int.from_bytes(f.read(size_modulus), "little")
ec_b = int.from_bytes(f.read(size_modulus), "little")
f.seek(field_data_size + 12)
points = []
for i in range(size_modulus):
x = []
y = []
for i in range(ext_deg1):
x.append(int.from_bytes(f.read(size_modulus), "little"))
for i in range(ext_deg1):
y.append(int.from_bytes(f.read(size_modulus), "little"))
points.append({"x": x, "y": y})
pairing_val = []
for i in range(ext_deg2):
ext1_val = []
for j in range(ext_deg1):
ext1_val.append(int.from_bytes(f.read(size_modulus), "little"))
pairing_val.append(ext1_val)
pubkey_data = {
"field": {
"modulus": modulus,
"ec_base_order": order,
"k3_minpoly": ext_minpoly1,
"k6_minpoly": ext_minpoly2,
},
"h1_bases": h1_bases,
"curve": {
"a": ec_a,
"b": ec_b
},
"points": points,
"pairing_val": pairing_val
}
with open("pubkey_info/" + basename(sys.argv[1]).replace(".pubkey", ".json"), "w") as g:
g.write(dumps(pubkey_data, indent=4))

348
extras/pidgenx.ipynb Normal file
View File

@ -0,0 +1,348 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The math in this notebook is described in [this patent](https://patentimages.storage.googleapis.com/a3/27/c1/3c0948a078cb28/US7587605.pdf). Be warned, the math is very complicated."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Windows 7 Retail/GVLK, public key ID 170\n",
"\n",
"pubkey_data = {\n",
" \"field\": {\n",
" \"modulus\": 886368969471450739924935101400677,\n",
" \"ec_base_order\": 886368969471450710152985728350703,\n",
" \"k3_minpoly\": [\n",
" 4,\n",
" 1,\n",
" 0,\n",
" 1\n",
" ],\n",
" \"k6_minpoly\": [\n",
" 2,\n",
" 0,\n",
" 1\n",
" ]\n",
" },\n",
" \"h1_bases\": [\n",
" 1,\n",
" 3,\n",
" 8,\n",
" 15,\n",
" 15,\n",
" 15,\n",
" 31,\n",
" 3,\n",
" 3,\n",
" 3,\n",
" 3,\n",
" 3,\n",
" 7,\n",
" 63\n",
" ],\n",
" \"curve\": {\n",
" \"a\": 26392827536965106777121445123290,\n",
" \"b\": 372325368096095544195525883520589\n",
" },\n",
" \"points\": [\n",
" {\n",
" \"x\": [\n",
" 365236101742748463929673543888206,\n",
" 858097895593939865996182272259769,\n",
" 148438159087534462792506738986740\n",
" ],\n",
" \"y\": [\n",
" 776418047571862972603801173382237,\n",
" 873677028107508092012208744232957,\n",
" 622138327043805563266794621920098\n",
" ]\n",
" },\n",
" {\n",
" \"x\": [\n",
" 940136574680879136511599445781,\n",
" 566978253317108608042529258054523,\n",
" 176284220413545220121710961573292\n",
" ],\n",
" \"y\": [\n",
" 828856809691743749590800150937649,\n",
" 225146018128364550960496522448712,\n",
" 348601659612301002638949468744847\n",
" ]\n",
" },\n",
" {\n",
" \"x\": [\n",
" 733747358623171948496764545320051,\n",
" 728506535527490173098593825125337,\n",
" 82462451162574422717677160727098\n",
" ],\n",
" \"y\": [\n",
" 416331132638004657079841565104549,\n",
" 366794872410090667339979925100938,\n",
" 154519017608105570119249112044121\n",
" ]\n",
" },\n",
" {\n",
" \"x\": [\n",
" 849718119860311390685018324089317,\n",
" 69736499980142833460080381132368,\n",
" 72139323263966224829624934948858\n",
" ],\n",
" \"y\": [\n",
" 122550620604034160835298121626961,\n",
" 232865179257577260620478614346661,\n",
" 96495922331236902442197840422963\n",
" ]\n",
" },\n",
" {\n",
" \"x\": [\n",
" 737949449696062407373114808812927,\n",
" 526576673882551145431025311648593,\n",
" 577710732700754839750249914833193\n",
" ],\n",
" \"y\": [\n",
" 245977198113437420529250724111432,\n",
" 316396368275232555978824338443046,\n",
" 755792900000892204654488821885538\n",
" ]\n",
" },\n",
" {\n",
" \"x\": [\n",
" 712586405875738967442641545880322,\n",
" 615445286425878710157557053762371,\n",
" 734183236086095230968388017605820\n",
" ],\n",
" \"y\": [\n",
" 851284376759840359812981263306021,\n",
" 769237654873203944088649987250083,\n",
" 359324880331507581802773028306633\n",
" ]\n",
" },\n",
" {\n",
" \"x\": [\n",
" 579665839598807564349118802507078,\n",
" 793103874095793223248478622956780,\n",
" 502860226530799804560661048077280\n",
" ],\n",
" \"y\": [\n",
" 526775274489316486107329470634542,\n",
" 828721161962151275145535457964404,\n",
" 204415317809040518371881977645416\n",
" ]\n",
" },\n",
" {\n",
" \"x\": [\n",
" 804790447447351785544412956578788,\n",
" 119046642031064430140912082580578,\n",
" 475159529884254928674792290619954\n",
" ],\n",
" \"y\": [\n",
" 458245266057063984580129835988070,\n",
" 338411981227059768831710308435687,\n",
" 577923375329917551735757167190702\n",
" ]\n",
" },\n",
" {\n",
" \"x\": [\n",
" 448295070796654878810211055051604,\n",
" 482910785083759911781193909334072,\n",
" 795628820954832750108065551162801\n",
" ],\n",
" \"y\": [\n",
" 417757375223493128894380427308216,\n",
" 755520039102173573177271365439537,\n",
" 863842006193777913816171128026446\n",
" ]\n",
" },\n",
" {\n",
" \"x\": [\n",
" 663389221842281261857262032548436,\n",
" 846447543951704162020988219326272,\n",
" 686142287698732386980948449542167\n",
" ],\n",
" \"y\": [\n",
" 769015970121598916167134609518482,\n",
" 738460771147019950148429256265493,\n",
" 613009789239563486872501072748270\n",
" ]\n",
" },\n",
" {\n",
" \"x\": [\n",
" 23530113060362511985797534739195,\n",
" 718131004725002854064127778364823,\n",
" 140870968646848990835780066321375\n",
" ],\n",
" \"y\": [\n",
" 641031697928634900295866764583620,\n",
" 295544383156746469642549388283327,\n",
" 133766761871461067699690599056442\n",
" ]\n",
" },\n",
" {\n",
" \"x\": [\n",
" 7518354584460889742005963384331,\n",
" 340825540582760123772991939806390,\n",
" 525549834323799848592419044187971\n",
" ],\n",
" \"y\": [\n",
" 585295007893871934790357000030208,\n",
" 117490751031779271453224407217079,\n",
" 838852298106199238437827740364400\n",
" ]\n",
" },\n",
" {\n",
" \"x\": [\n",
" 806036388470182281562651653929939,\n",
" 266085928879449679004785507000719,\n",
" 201474020142460453395308745398496\n",
" ],\n",
" \"y\": [\n",
" 573468377549807523205344415925956,\n",
" 667459718759242575444856430313959,\n",
" 226975716159080217447594275999935\n",
" ]\n",
" },\n",
" {\n",
" \"x\": [\n",
" 794167987155642331621801361756614,\n",
" 809201520617560616339201020039820,\n",
" 198696155869194654384403079624544\n",
" ],\n",
" \"y\": [\n",
" 725959545288387914551997303844726,\n",
" 49262476800238214847233993847181,\n",
" 537326577113493149345527624223733\n",
" ]\n",
" }\n",
" ],\n",
" \"pairing_val\": [\n",
" [\n",
" 242940802691096077821709859741616,\n",
" 178851543248946074944443141484182,\n",
" 802059826004050667481466713086225\n",
" ],\n",
" [\n",
" 701042518368651902930590425782509,\n",
" 265571225406900742458432149860962,\n",
" 699432283102586243018242179516873\n",
" ]\n",
" ]\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"p = pubkey_data[\"field\"][\"modulus\"]\n",
"a = pubkey_data[\"curve\"][\"a\"]\n",
"b = pubkey_data[\"curve\"][\"b\"]\n",
"order = pubkey_data[\"field\"][\"ec_base_order\"]\n",
"h1_bases = list(map(lambda x: x+1, pubkey_data[\"h1_bases\"]))\n",
"KCHARS = \"BCDFGHJKMPQRTVWXY2346789\"\n",
"\n",
"def decode_pkey(k):\n",
" k = k.replace(\"-\", \"\")\n",
" out = 0\n",
" \n",
" for c in k:\n",
" out *= 24\n",
" out += KCHARS.index(c)\n",
" \n",
" return out\n",
"\n",
"K = GF(p)\n",
"Kx.<x> = K[]\n",
"K3.<u> = K.extension(Kx(pubkey_data[\"field\"][\"k3_minpoly\"]))\n",
"K3y.<y> = K3[]\n",
"K6.<t> = K3.extension(K3y(pubkey_data[\"field\"][\"k6_minpoly\"]))\n",
"\n",
"E = EllipticCurve(K, [a, b])\n",
"E6 = EllipticCurve(K6, [a, b])\n",
"\n",
"Qi = [E6(K3(point[\"x\"]) * t^-2, K3(point[\"y\"]) * t^-3) for point in pubkey_data[\"points\"]]\n",
"\n",
"# pairing_val = e_m(P, S)\n",
"pairing_val = K6([K3(pubkey_data[\"pairing_val\"][0]), K3(pubkey_data[\"pairing_val\"][1])])\n",
"\n",
"assert is_prime(p)\n",
"assert len(h1_bases) == len(Qi)\n",
"assert h1_bases[0] == 2\n",
"assert pairing_val^order == 1"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pkey_chars = \"33PXH-7Y6KF-2VJC9-XBBR8-HVTHH\"\n",
"\n",
"# pkey = HASH(M)\n",
"# HASH is a currently unknown hash function\n",
"pkey = decode_pkey(pkey_chars)\n",
"\n",
"# h1_coeffs = H1(M)\n",
"# During validation, coeffs must be found by a search that i havent implemented\n",
"# h1_coeffs = [1, 0, 7, 1, 4, 15, 9, 1, 1, 0, 2, 1, 0, 19]\n",
"\n",
"# 10 bits unknown, 30 bits product ID, 1 bit unknown (upgrade?)\n",
"key_data = (342 << 31 | 918500000 << 1 | 0)\n",
"h1_coeffs = [1]\n",
"\n",
"for i in range(len(h1_bases) - 1):\n",
" h1_coeffs.append(key_data % h1_bases[i + 1])\n",
" key_data //= h1_bases[i + 1]\n",
"\n",
"print(h1_coeffs)\n",
"\n",
"# H2(M) = E.lift_x(HASH(M) % p)\n",
"T = E6(E.lift_x(pkey % p))\n",
"Q = sum(map(lambda x: x[0] * x[1], zip(h1_coeffs, Qi))) \n",
"\n",
"test_pairing = T.tate_pairing(Q, order, 6, q=p)\n",
"\n",
"print(test_pairing == pairing_val or test_pairing == 1/pairing_val)\n",
"\n",
"key_data = 0\n",
"\n",
"for i in range(len(h1_bases) - 1, 0, -1):\n",
" key_data *= h1_bases[i]\n",
" key_data += h1_coeffs[i]\n",
" print(h1_bases[i], h1_coeffs[i], key_data)\n",
"\n",
"pid = (key_data & ((1 << 31) - 1)) >> 1"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "SageMath 9.0",
"language": "sage",
"name": "sagemath"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.10"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

159
extras/tspkgen.py Normal file
View File

@ -0,0 +1,159 @@
from Crypto.Cipher import ARC4
from hashlib import sha1, md5
from random import randint
from ecutils.core import Point, EllipticCurve
from sys import argv
KCHARS = "BCDFGHJKMPQRTVWXY2346789"
SPK_ECKEY = {
"a": 1,
"b": 0,
"g": {
"x": 10692194187797070010417373067833672857716423048889432566885309624149667762706899929433420143814127803064297378514651,
"y": 14587399915883137990539191966406864676102477026583239850923355829082059124877792299572208431243410905713755917185109
},
"n": 629063109922370885449,
"p": 21782971228112002125810473336838725345308036616026120243639513697227789232461459408261967852943809534324870610618161,
"priv": 153862071918555979944,
"pub": {
"x": 3917395608307488535457389605368226854270150445881753750395461980792533894109091921400661704941484971683063487980768,
"y": 8858262671783403684463979458475735219807686373661776500155868309933327116988404547349319879900761946444470688332645
}
}
LKP_ECKEY = {
"a": 1,
"b": 0,
"g": {
"x": 18999816458520350299014628291870504329073391058325678653840191278128672378485029664052827205905352913351648904170809,
"y": 7233699725243644729688547165924232430035643592445942846958231777803539836627943189850381859836033366776176689124317
},
"n": 675048016158598417213,
"p": 28688293616765795404141427476803815352899912533728694325464374376776313457785622361119232589082131818578591461837297,
"priv": 100266970209474387075,
"pub": {
"x": 7147768390112741602848314103078506234267895391544114241891627778383312460777957307647946308927283757886117119137500,
"y": 20525272195909974311677173484301099561025532568381820845650748498800315498040161314197178524020516408371544778243934
}
}
def encode_pkey(n):
out = ""
while n > 0:
out = KCHARS[n % 24] + out
n //= 24
out = "-".join([out[i:i+5] for i in range(0, len(out), 5)])
return out
def decode_pkey(k):
k = k.replace("-", "")
out = 0
for c in k:
out *= 24
out += KCHARS.index(c)
return out
def int_to_bytes(n, l=None):
n = int(n)
if not l:
l = (n.bit_length() + 7) // 8
return n.to_bytes(l, byteorder="little")
def make_curve(curve_def):
G = Point(x=curve_def["g"]["x"], y=curve_def["g"]["y"])
K = Point(x=curve_def["pub"]["x"], y=curve_def["pub"]["y"])
E = EllipticCurve(p=curve_def["p"], a=curve_def["a"], b=curve_def["b"], G=G, n=curve_def["n"], h=1)
return E, G, K
def get_spkid(pid):
spkid_s = pid[10:16] + pid[18:23]
return int(spkid_s.split("-")[0])
def validate_tskey(pid, tskey, is_spk=True):
keydata = decode_pkey(tskey).to_bytes(21, "little")
rk = md5(pid.encode("utf-16-le")).digest()[:5] + b"\x00" * 11
c = ARC4.new(rk)
dc_kdata = c.decrypt(keydata)
keydata = dc_kdata[:7]
sigdata = int.from_bytes(dc_kdata[7:], "little")
h = sigdata & 0x7ffffffff
s = (sigdata >> 35) & 0x1fffffffffffffffff
params = SPK_ECKEY if is_spk else LKP_ECKEY
E, G, K = make_curve(params)
R = E.add_points(E.multiply_point(h, K), E.multiply_point(s, G))
md = sha1(keydata + int_to_bytes(R.x, 48) + int_to_bytes(R.y, 48)).digest()
ht = ((int.from_bytes(md[4:8], "little") >> 29) << 32) | (int.from_bytes(md[:4], "little"))
spkid = int.from_bytes(keydata, "little") & 0x1FFFFFFFFF
return h == ht and (not is_spk or spkid == get_spkid(pid))
def generate_tskey(pid, keydata, is_spk=True):
params = SPK_ECKEY if is_spk else LKP_ECKEY
priv = SPK_ECKEY["priv"] if is_spk else LKP_ECKEY["priv"]
E, G, K = make_curve(params)
s = 0
while True:
c = randint(1, E.n - 1)
R = E.multiply_point(c, G)
md = sha1(keydata + int_to_bytes(R.x, 48) + int_to_bytes(R.y, 48)).digest()
h = ((int.from_bytes(md[4:8], "little") >> 29) << 32) | (int.from_bytes(md[:4], "little"))
s = ((-priv * h + c) % E.n) & 0x1fffffffffffffffff
keyinf = int.from_bytes(keydata, "little")
pkdata = ((s << 91) | (h << 56) | keyinf).to_bytes(21, "little")
rk = md5(pid.encode("utf-16-le")).digest()[:5] + b"\x00" * 11
c = ARC4.new(rk)
pke = c.encrypt(pkdata)[:20]
pk = int.from_bytes(pke, "little")
pkstr = encode_pkey(pk)
if s < 0x1fffffffffffffff and validate_tskey(pid, pkstr, is_spk):
return pkstr
def generate_spk(pid):
spkid = get_spkid(pid)
spkdata = spkid.to_bytes(7, "little")
return generate_tskey(pid, spkdata)
def generate_lkp(pid, count, major_ver, minor_ver, chid):
version = 1
if (major_ver == 5 and minor_ver > 0) or major_ver > 5:
version = (major_ver << 3) | minor_ver
lkpinfo = (chid << 46) | (count << 32) | (2 << 18) | (144 << 10) | (version << 3)
lkpdata = lkpinfo.to_bytes(7, "little")
return generate_tskey(pid, lkpdata, False)
if __name__ == "__main__":
if len(argv) == 2:
pid = argv[1]
print(f"License Server ID: {generate_spk(pid)}")
elif len(argv) == 5:
pid = argv[1]
count = int(argv[2])
ver_major, ver_minor = map(int, argv[3].split("."))
chid = int(argv[4])
print(f"License Key Pack ID: {generate_lkp(pid, count, ver_major, ver_minor, chid)}")
else:
print(f"Usage: {argv[0]} <pid> [<count> <version> <chid>]")
print(f"Example: {argv[0]} 00490-92005-99454-AT527 1234 10.3 32")

526
extras/winxp_act.ipynb Normal file
View File

@ -0,0 +1,526 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Product Key Generator - Windows XP"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Paste JSON object for BINK data here:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Windows XP Professional Retail (Bink ID 2C)\n",
"key_data = {\n",
" \"p\": 24412280675538104642884792561502783185577987209710041026341163083973933860854736635268965257725055809364646140091249,\n",
" \"a\": 1,\n",
" \"b\": 0,\n",
" \"B\": [\n",
" 21673361717619259910600499419800485528178801849923454062050055236231939594233283543796077751210469045350919066368895,\n",
" 5232476492611604888729825305639232005017822876108144652169892952989580351454246958886421453535493897842819359154864\n",
" ],\n",
" \"K\": [\n",
" 21551722775458524408480112576069559265917312687549112053580919391285918530584174752292844347621326558272739603979057,\n",
" 13463977158522661542654520438933687107907187215503371589980428235633526671841388652148099285621876350916055100879930\n",
" ],\n",
" \"order\": 55681564377333977,\n",
" \"private_key\": 30951839223306173\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Run this cell to generate key"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"import hashlib\n",
"\n",
"# p = order of field Fp\n",
"# Fp = Galois field of order p\n",
"# E = Elliptic curve y^2 = x^3 + ax + b over Fp\n",
"# B = generator on E\n",
"# K = inverse of public key\n",
"# order = order of E\n",
"\n",
"p = key_data[\"p\"]\n",
"Fp = GF(p)\n",
"E = EllipticCurve(Fp, [0, 0, 0, key_data[\"a\"], key_data[\"b\"]])\n",
"B = E.point(key_data[\"B\"])\n",
"K = E.point(key_data[\"K\"])\n",
"order = key_data[\"order\"]\n",
"private_key = -key_data[\"private_key\"] % order\n",
"\n",
"# PID of product key\n",
"pid = 756_696969\n",
"\n",
"# Key alphabet\n",
"KCHARS = \"BCDFGHJKMPQRTVWXY2346789\"\n",
"\n",
"def int_to_bytes(n, l=None):\n",
" n = int(n)\n",
" \n",
" if not l:\n",
" l = (n.bit_length() + 7) // 8\n",
" \n",
" return n.to_bytes(l, byteorder=\"little\")\n",
"\n",
"def encode_pkey(n):\n",
" out = \"\"\n",
" \n",
" while n > 0:\n",
" out = KCHARS[n % 24] + out\n",
" n //= 24\n",
" \n",
" out = \"-\".join([out[i:i+5] for i in range(0, len(out), 5)])\n",
" return out\n",
"\n",
"pid <<= 1\n",
"\n",
"while True:\n",
" k = getrandbits(384)\n",
" r = k * B\n",
" x, y = r.xy()\n",
"\n",
" md = hashlib.sha1(int_to_bytes(pid, 4) + int_to_bytes(x, 48) + int_to_bytes(y, 48)).digest()\n",
" h = int.from_bytes(md[:4], byteorder=\"little\") >> 4\n",
" h &= 0xfffffff\n",
"\n",
" s = int(abs((private_key * h + k) % order))\n",
" raw_pkey = s << 59 | h << 31 | pid\n",
" \n",
" print(hex(pid)[2:], hex(h)[2:], hex(s)[2:], hex(raw_pkey)[2:])\n",
" \n",
" if raw_pkey >> 96 < 0x40000:\n",
" break\n",
"\n",
"print(encode_pkey(raw_pkey))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Key decoder (run above cell first)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"def decode_pkey(k):\n",
" k = k.replace(\"-\", \"\")\n",
" out = 0\n",
" \n",
" for c in k:\n",
" out *= 24\n",
" out += KCHARS.index(c)\n",
" \n",
" return out\n",
"\n",
"pkey = input(\"Product Key (dashes optional): \")\n",
"raw_pkey = decode_pkey(pkey)\n",
"\n",
"kpid = (raw_pkey & 0x7fffffff) >> 1\n",
"verify = (kpid // 1000000) == ((pid >> 1) // 1000000)\n",
"print(kpid, pid >> 1)\n",
"\n",
"if verify:\n",
" h = (raw_pkey >> 31) & 0xfffffff\n",
" s = (raw_pkey >> 59) & 0x7ffffffffffffff\n",
"\n",
" r = h * K + s * B\n",
" x, y = r.xy()\n",
"\n",
" md = hashlib.sha1(int_to_bytes(kpid << 1, 4) + int_to_bytes(x, 48) + int_to_bytes(y, 48)).digest()\n",
" hp = int.from_bytes(md[:4], byteorder=\"little\") >> 4\n",
" hp &= 0xfffffff\n",
"\n",
" print(h, hp)\n",
" \n",
" if h == hp:\n",
" print(\"Valid key\")\n",
" else:\n",
" print(\"Invalid key\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Confirmation ID generator"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import hashlib\n",
"\n",
"# order of field Fp \n",
"p = 0x16A6B036D7F2A79\n",
"# Galois field of order p\n",
"Fp = GF(p)\n",
"# Polynomial field Fp[x] over Fp\n",
"Fpx.<x> = Fp[]\n",
"# Hyperellptic curve function\n",
"F = x^5+0x1400606322B3B04*x^4+0x1400606322B3B04*x^3+0x44197B83892AD0*x^2+0x21840136C85381*x\n",
"# Hyperelliptic curve E: y^2 = F(x) over Fp\n",
"E = HyperellipticCurve(F)\n",
"# The jacobian over E\n",
"J = E.jacobian()\n",
"\n",
"# This constant inverts multiplication by 0x1001 in verification\n",
"# My best guess for how it was calculated: INV = 0x10001^-1 (mod |J|)\n",
"# |J| is hard to compute, how can we calculate for other curves?\n",
"INV = 0x40DA7C36D44C04E21B9D10F127C1\n",
"\n",
"# Key to decrypt installation IDs\n",
"IID_KEY = b'\\x6A\\xC8\\x5E\\xD4'\n",
"\n",
"# Validate installation ID checksum\n",
"def validate_cksum(n):\n",
" print(\"Checksumming installation ID...\")\n",
" n = n.replace(\"-\", \"\")\n",
"\n",
" cksum = 0\n",
" for i, k in enumerate(map(int, n)):\n",
" if (i + 1) % 6 == 0 or i == len(n) - 1:\n",
" print(\"Expected last digit\", cksum % 7, \"got\", k)\n",
" if cksum % 7 != k:\n",
" return None\n",
" \n",
" cksum = 0\n",
" else:\n",
" cksum += k * (i % 2 + 1)\n",
" \n",
" parts = [n[i:i+5] for i in range(0, len(n), 6)]\n",
" n_out = \"\".join(parts)\n",
" \n",
" if len(n_out) == 42:\n",
" n_out = n_out[:-1]\n",
" \n",
" if len(n_out) != 45 and len(n_out) != 41:\n",
" return None\n",
" \n",
" return int(\"\".join(parts))\n",
"\n",
"# Insert checksum digits into confirmation ID\n",
"def add_cksum(n):\n",
" cksums = []\n",
" n = str(n).zfill(35)\n",
" parts = [n[i:i+5] for i in range(0, len(n), 5)]\n",
" \n",
" for p in parts:\n",
" cksum = 0\n",
" \n",
" for i, k in enumerate(map(int, p)):\n",
" cksum += k * (i % 2 + 1)\n",
" \n",
" cksums.append(str(cksum % 7))\n",
" \n",
" n_out = \"\"\n",
" \n",
" for i in range(7):\n",
" n_out += parts[i] + cksums[i] + (\"-\" if i != 6 else \"\")\n",
" \n",
" return n_out\n",
"\n",
"def encrypt(decrypted, key):\n",
" size_half = len(decrypted) // 2\n",
" size_half_dwords = size_half - (size_half % 4)\n",
" last = decrypted[size_half*2:]\n",
" decrypted = decrypted[:size_half*2]\n",
" for i in range(4):\n",
" first = decrypted[:size_half]\n",
" second = decrypted[size_half:]\n",
" sha1_result = hashlib.sha1(second + key).digest()\n",
" sha1_result = (sha1_result[:size_half_dwords] +\n",
" sha1_result[size_half_dwords+4-(size_half%4) : size_half+4-(size_half%4)])\n",
" decrypted = second + bytes(x^^y for x,y in zip(first, sha1_result))\n",
" return decrypted + last\n",
"\n",
"def decrypt(encrypted, key):\n",
" size_half = len(encrypted) // 2\n",
" size_half_dwords = size_half - (size_half % 4)\n",
" last = encrypted[size_half*2:]\n",
" encrypted = encrypted[:size_half*2]\n",
" for i in range(4):\n",
" first = encrypted[:size_half]\n",
" second = encrypted[size_half:]\n",
" sha1_result = hashlib.sha1(first + key).digest()\n",
" sha1_result = (sha1_result[:size_half_dwords] +\n",
" sha1_result[size_half_dwords+4-(size_half%4) : size_half+4-(size_half%4)])\n",
" encrypted = bytes(x^^y for x,y in zip(second, sha1_result)) + first\n",
" return encrypted + last\n",
"\n",
"# Find v of divisor (u, v) of curve y^2 = F(x)\n",
"def find_v(u):\n",
" f = F % u\n",
" c2 = u[1]^2 - 4 * u[0]\n",
" c1 = 2 * f[0] - f[1] * u[1]\n",
" \n",
" if c2 == 0:\n",
" if c1 == 0:\n",
" return None\n",
" \n",
" try:\n",
" v1 = sqrt(f[1]^2 / (2 * c1))\n",
" v1.lift()\n",
" except:\n",
" return None\n",
" else:\n",
" try:\n",
" d = 2 * sqrt(f[0]^2 + f[1] * (f[1] * u[0] - f[0] * u[1]))\n",
" v1_1 = sqrt((c1 - d)/c2)\n",
" v1_2 = sqrt((c1 + d)/c2)\n",
" except:\n",
" return None\n",
"\n",
" try:\n",
" v1_1.lift()\n",
" v1 = v1_1\n",
" except:\n",
" try:\n",
" v1_2.lift()\n",
" v1 = v1_2\n",
" except:\n",
" return None\n",
" \n",
" v0 = (f[1] + u[1] * v1^2) / (2 * v1)\n",
" v = v0 + v1 * x\n",
" \n",
" assert (v^2 - f) % u == 0\n",
" return v\n",
"\n",
"# unpack&decrypt installationId\n",
"installationId = validate_cksum(input(\"Installation ID (dashes optional): \"))\n",
"print(installationId)\n",
"\n",
"if not installationId:\n",
" raise Exception(\"Invalid Installation ID (checksum fail)\")\n",
"\n",
"installationIdSize = 19 if len(str(installationId)) > 41 else 17 # 17 for XP Gold, 19 for SP1+ (includes 12 bits of sha1(product key))\n",
"iid = int(installationId).to_bytes(installationIdSize, byteorder='little')\n",
"iid = decrypt(iid, IID_KEY)\n",
"hwid = iid[:8]\n",
"productid = int.from_bytes(iid[8:17], byteorder='little')\n",
"productkeyhash = iid[17:]\n",
"pid1 = productid & ((1 << 17) - 1)\n",
"pid2 = (productid >> 17) & ((1 << 10) - 1)\n",
"pid3 = (productid >> 27) & ((1 << 25) - 1)\n",
"version = (productid >> 52) & 7\n",
"pid4 = productid >> 55\n",
"\n",
"assert version == (4 if len(iid) == 17 else 5)\n",
"\n",
"key = hwid + int((pid1 << 41 | pid2 << 58 | pid3 << 17 | pid4) & ((1 << 64) - 1)).to_bytes(8, byteorder='little')\n",
"\n",
"data = [0x00] * 14\n",
"\n",
"print(\"\\nConfirmation IDs:\")\n",
"\n",
"for i in range(0x81):\n",
" data[7] = i\n",
" # Encrypt conf ID, find u of divisor (u, v)\n",
" encrypted = encrypt(bytes(data), key)\n",
" encrypted = int.from_bytes(encrypted, byteorder=\"little\")\n",
" x1, x2 = Fp(encrypted % p), Fp((encrypted // p) + 1)\n",
" u1, u0 = x1 * 2, (x1 ^ 2) - ((x2 ^ 2) * 43)\n",
" u = x^2 + u1 * x + u0\n",
"\n",
" # Generate original divisor\n",
" v = find_v(u)\n",
" \n",
" if not v:\n",
" continue\n",
" \n",
" d2 = J(u, v)\n",
" divisor = d2 * INV\n",
" \n",
" # Get x1 and x2\n",
" roots = [x for x, y in divisor[0].roots()]\n",
"\n",
" if len(roots) > 0:\n",
" y = [divisor[1](r) for r in roots]\n",
" x1 = (-roots[0]).lift()\n",
" x2 = (-roots[1]).lift()\n",
"\n",
" if (x1 > x2) or (y[0].lift() % 2 != y[1].lift() % 2):\n",
" x1 = (-roots[1]).lift()\n",
" x2 = (-roots[0]).lift()\n",
" else:\n",
" x2 = (divisor[0][1] / 2).lift()\n",
" x1 = sqrt((x2^2 - divisor[0][0]) / 43).lift() + p\n",
"\n",
" # Win\n",
" conf_id = x1 * (p + 1) + x2\n",
" conf_id = add_cksum(conf_id)\n",
" print(conf_id)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Confirmation ID decoder/validator (made by diamondggg)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import hashlib\n",
"\n",
"# 226512-274743-842923-777124-961370-722240-570042-517722-757426\n",
"installationId = 114535500880440159787527912804896629001083118\n",
"installationIdSize = 19 # 17 for XP Gold, 19 for SP1+ (includes 12 bits of sha1(product key))\n",
"# all three of following are valid generated\n",
"# 013705-060122-603141-961392-086136-909901-494476\n",
"confirmationId = 15771960290497900806797040541467113\n",
"# 022032-220754-159721-909624-985141-504586-914001\n",
"#confirmationId = 02203220751597290962985145045891400\n",
"# 137616-847280-708585-827476-874935-313366-790880\n",
"#confirmationId = 13761847287085882747874933133679088\n",
"\n",
"def decrypt(encrypted, key):\n",
" size_half = len(encrypted) // 2\n",
" size_half_dwords = size_half - (size_half % 4)\n",
" last = encrypted[size_half*2:]\n",
" encrypted = encrypted[:size_half*2]\n",
" for i in range(4):\n",
" first = encrypted[:size_half]\n",
" second = encrypted[size_half:]\n",
" sha1_result = hashlib.sha1(first + key).digest()\n",
" sha1_result = (sha1_result[:size_half_dwords] +\n",
" sha1_result[size_half_dwords+4-(size_half%4) : size_half+4-(size_half%4)])\n",
" encrypted = bytes(x^^y for x,y in zip(second, sha1_result)) + first\n",
" return encrypted + last\n",
"\n",
"# unpack&decrypt installationId\n",
"iid = int(installationId).to_bytes(installationIdSize, byteorder='little')\n",
"iid = decrypt(iid, b'\\x6A\\xC8\\x5E\\xD4')\n",
"hwid = iid[:8]\n",
"productid = int.from_bytes(iid[8:17], byteorder='little')\n",
"productkeyhash = iid[17:]\n",
"pid1 = productid & ((1 << 17) - 1)\n",
"pid2 = (productid >> 17) & ((1 << 10) - 1)\n",
"pid3 = (productid >> 27) & ((1 << 25) - 1)\n",
"version = (productid >> 52) & 7\n",
"pid4 = productid >> 55\n",
"\n",
"assert version == (4 if len(iid) == 17 else 5)\n",
"\n",
"key = hwid + int((pid1 << 41 | pid2 << 58 | pid3 << 17 | pid4) & ((1 << 64) - 1)).to_bytes(8, byteorder='little')\n",
"# productkeyhash is not used for validation, it exists just to allow the activation server to reject keygenned pids\n",
"\n",
"# now the math\n",
"\n",
"p = 0x16A6B036D7F2A79\n",
"Fp = GF(p)\n",
"Fpx.<x> = Fp[]\n",
"E = HyperellipticCurve(x^5+0x1400606322B3B04*x^4+0x1400606322B3B04*x^3+0x44197B83892AD0*x^2+0x21840136C85381*x)\n",
"J = E.jacobian()\n",
"\n",
"# deserialize divisor\n",
"x1 = confirmationId // (p + 1)\n",
"x2 = confirmationId % (p + 1)\n",
"if x1 <= p:\n",
" # two or less points over GF(p)\n",
" point1 = E.lift_x(Fp(-x1)) if x1 != p else None\n",
" point2 = E.lift_x(Fp(-x2)) if x2 != p else None\n",
" if point1 is not None and point2 is not None:\n",
" # there are 4 variants of how lift_x() could select both y-s\n",
" # we don't distinguish D and -D, but this still leaves 2 variants\n",
" # the chosen one is encoded by order of x1 <=> x2\n",
" lastbit1 = point1[1].lift() & 1\n",
" lastbit2 = point2[1].lift() & 1\n",
" if x2 < x1:\n",
" if lastbit1 == lastbit2:\n",
" point2 = E(point2[0], -point2[1])\n",
" else:\n",
" if lastbit1 != lastbit2:\n",
" point2 = E(point2[0], -point2[1])\n",
" point1 = J(point1) if point1 is not None else J(0)\n",
" point2 = J(point2) if point2 is not None else J(0)\n",
" divisor = point1 + point2\n",
"else:\n",
" # a pair of conjugate points over GF(p^2)\n",
" f = (x+x2)*(x+x2)-43*x1*x1 # 43 is the minimal quadratic non-residue in Fp\n",
" Fp2 = GF(p^2)\n",
" point1 = E.lift_x(f.roots(Fp2)[0][0])\n",
" point2 = E(Fp2)(point1[0].conjugate(), point1[1].conjugate())\n",
" divisor = J(Fp2)(point1) + J(Fp2)(point2)\n",
" divisor = J(Fpx(divisor[0]), Fpx(divisor[1])) #return from Fp2 to Fp\n",
"\n",
"d2 = divisor * 0x10001\n",
"assert d2[0].degree() == 2\n",
"x1 = d2[0][1]/2\n",
"x2 = sqrt((x1*x1-d2[0][0])/43)\n",
"\n",
"encrypted = x1.lift() + (x2.lift() - 1) * p\n",
"encrypted = int(encrypted).to_bytes(14,byteorder='little')\n",
"\n",
"# end of the math\n",
"decrypted = decrypt(encrypted, key)\n",
"print(decrypted.hex())\n",
"# 0000000000000001000000000000 for the first confirmationId\n",
"# 0000000000000002000000000000 for the second confirmationId\n",
"# 0000000000000006000000000000 for the last confirmationId\n",
"assert decrypted[8:] == b'\\0' * 6\n",
"assert decrypted[7] <= 0x80\n",
"# all zeroes in decrypted[0:7] are okay for the checker\n",
"# more precisely: if decrypted[6] == 0, first 6 bytes can be anything\n",
"# otherwise, decrypted[0] = length, and decrypted[1:1+length] must match first length bytes of sha1(product key)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "SageMath 9.0",
"language": "sage",
"name": "sagemath"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.10"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@ -0,0 +1,267 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [],
"source": [
"# From Longhorn 4074\n",
"# Any version that accepts TCP8W-T8PQJ-WWRRH-QH76C-99FBW will work\n",
"key_data = {\n",
" \"p\": 7181106593102322766813520532476531209871483588988471009176871145241389568314039093657656718839885029493125387894856821599452867350054864568294961595970889,\n",
" \"a\": 1,\n",
" \"b\": 0,\n",
" \"B\": [\n",
" 520282615406607935808830413235837609227529008118239433194891765554084261177667142590192616462797266047427714603514505726507565809100858610756034340614180,\n",
" 4557046395510954851157569206449480560848332315791566919607580280750304632075435589109908909351625686398512699199297926705742962219032991805095344264722444\n",
" ],\n",
" \"K\": [\n",
" 1748427561645745685508888890965804844329037567281415535239953290167653001827496844268667372126127464466687812723744919132659150838866693283679107969476861,\n",
" 6808711632346399211426562555523956018872055718394662707289722207520029794097689415773036615424757895159410496488301598927496012713658489637493990459415502\n",
" ],\n",
" \"order\": 4633201844252750473,\n",
" \"private_key\": 4329540238250287790\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Product Key Generator, run above cell first"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"10928323 10928323 2003509697754295848 6615195716181683752\n",
"868186915 868186915 878552257861989116 5490238276289377020\n",
"446954708 446954708 776512975037445878 776512975037445878\n",
"353785506 353785506 1772257197849916146 6383943216277304050\n",
"1598465793 1598465793 385978941068063200 4997664959495451104\n",
"1118655069 1118655069 374356218385227934 374356218385227934\n",
"52 531faff484f609e 42ad525d d2\n",
"CH89R-TPQRK-GPJMW-7KTYQ-F8PJD\n"
]
}
],
"source": [
"import hashlib\n",
"\n",
"# p = order of field Fp\n",
"# Fp = Galois field of order p\n",
"# E = Elliptic curve y^2 = x^3 + ax + b over Fp\n",
"# B = generator on E\n",
"# K = inverse of public key\n",
"# order = order of E\n",
"# Ro = Ring Z/orderZ\n",
"\n",
"p = key_data[\"p\"]\n",
"Fp = GF(p)\n",
"E = EllipticCurve(Fp, [0, 0, 0, key_data[\"a\"], key_data[\"b\"]])\n",
"B = E.point(key_data[\"B\"])\n",
"K = E.point(key_data[\"K\"])\n",
"order = key_data[\"order\"]\n",
"Ro = Integers(order)\n",
"private_key = -key_data[\"private_key\"] % order\n",
"\n",
"# OS Family of product key\n",
"# x64 VLK - 652\n",
"# x64 Retail - 306\n",
"os_family = 105\n",
"\n",
"# Key alphabet\n",
"KCHARS = \"BCDFGHJKMPQRTVWXY2346789\"\n",
"\n",
"def int_to_bytes(n, l=None):\n",
" n = int(n)\n",
" \n",
" if not l:\n",
" l = (n.bit_length() + 7) // 8\n",
" \n",
" return n.to_bytes(l, byteorder=\"little\")\n",
"\n",
"def encode_pkey(n):\n",
" out = \"\"\n",
" \n",
" for i in range(25):\n",
" out = KCHARS[n % 24] + out\n",
" n //= 24\n",
" \n",
" out = \"-\".join([out[i:i+5] for i in range(0, len(out), 5)])\n",
" return out\n",
"\n",
"os_family <<= 1\n",
"\n",
"while True:\n",
" k = getrandbits(512)\n",
" prefix = getrandbits(32) & 0x3ff\n",
" \n",
" r = k * B\n",
" x, y = r.xy()\n",
"\n",
" mde = hashlib.sha1(b\"\\x79\" + int_to_bytes(os_family, 2) + int_to_bytes(x, 64) + int_to_bytes(y, 64)).digest()\n",
" e = int.from_bytes(mde[:4], byteorder=\"little\")\n",
" e &= 0x7fffffff\n",
" \n",
" mdh = hashlib.sha1(b\"\\x5d\" + int_to_bytes(os_family, 2) + int_to_bytes(e, 4) + int_to_bytes(prefix, 4)).digest()\n",
" h1 = int.from_bytes(mdh[:4], byteorder=\"little\")\n",
" h2 = int.from_bytes(mdh[4:8], byteorder=\"little\") >> 2\n",
" h2 &= 0x3fffffff\n",
" h = h2 << 32 | h1\n",
" b = Ro(-h * private_key)\n",
" \n",
" try:\n",
" s = Ro(b)\n",
" s = int((-b + sqrt(b^2 + 4 * Ro(k))) / 2)\n",
" except:\n",
" continue\n",
" \n",
" if s % 2 == 1:\n",
" s += order\n",
" \n",
" if (s * (s * B + h * K)) != (s * (s * B + int(b) * B)):\n",
" continue\n",
" \n",
" raw_pkey = prefix << 104 | s << 42 | e << 11 | os_family\n",
" \n",
" print((raw_pkey >> 11) & 0x7fffffff, e, (raw_pkey >> 42) & 0x3fffffffffffffff, s)\n",
" \n",
" # I could fix whatever bug made this necessary, but it works so I don't care\n",
" if ((raw_pkey >> 11) & 0x7fffffff) != e or ((raw_pkey >> 42) & 0x3fffffffffffffff) != s:\n",
" continue\n",
" \n",
" if (raw_pkey >> 32) & 0xffffffff < 0x40000000:\n",
" break\n",
"\n",
"print(hex(prefix)[2:], hex(s)[2:], hex(e)[2:], hex(os_family)[2:])\n",
"print(encode_pkey(raw_pkey))\n",
"pkey = encode_pkey(raw_pkey)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Product Key Verifier (must run above cell first)"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Product Key (dashes optional): TCP8W-T8PQJ-WWRRH-QH76C-99FBW\n",
"TCP8W-T8PQJ-WWRRH-QH76C-99FBW\n",
"318 3e550ae1709773d8 6674d45a ce\n",
"f0ce4281d7695e3\n",
"110001100011111001010101000010101110000101110000100101110111001111011000110011001110100110101000101101000011001110\n",
"110001100011111001010101000010101110000101110000100101110111001111011000110011001110100110101000101101000011001110\n",
"1718932570 1718932570 True\n"
]
}
],
"source": [
"def decode_pkey(k):\n",
" k = k.replace(\"-\", \"\")\n",
" out = 0\n",
" \n",
" for c in k:\n",
" out *= 24\n",
" out += KCHARS.index(c)\n",
" \n",
" return out\n",
"\n",
"pkey = input(\"Product Key (dashes optional): \")\n",
"print(pkey)\n",
"raw_key = decode_pkey(pkey)\n",
"\n",
"osf = raw_key & 0x7ff\n",
"e = (raw_key >> 11) & 0x7fffffff\n",
"s = (raw_key >> 42) & 0x3fffffffffffffff\n",
"pf = (raw_key >> 104) & 0x3ff\n",
"\n",
"mdh = hashlib.sha1(b\"\\x5d\" + int_to_bytes(osf, 2) + int_to_bytes(e, 4) + int_to_bytes(pf, 4)).digest()\n",
"h1 = int.from_bytes(mdh[:4], byteorder=\"little\")\n",
"h2 = int.from_bytes(mdh[4:8], byteorder=\"little\") >> 2\n",
"h2 &= 0x3fffffff\n",
"h = h2 << 32 | h1\n",
"\n",
"print(hex(pf)[2:], hex(s)[2:], hex(e)[2:], hex(osf)[2:])\n",
"print(hex(h)[2:])\n",
"print(bin(raw_key)[2:])\n",
"print(bin(pf << 104 | s << 42 | e << 11 | osf)[2:])\n",
"\n",
"v = s * (s * B + h * K)\n",
"x, y = v.xy()\n",
"\n",
"mde = hashlib.sha1(b\"\\x79\" + int_to_bytes(osf, 2) + int_to_bytes(x, 64) + int_to_bytes(y, 64)).digest()\n",
"ep = int.from_bytes(mde[:4], byteorder=\"little\")\n",
"ep &= 0x7fffffff\n",
"\n",
"print(e, ep, e == ep)"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"71"
]
},
"execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"0x8e/2"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "SageMath 9.0",
"language": "sage",
"name": "sagemath"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.10"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -85,7 +85,7 @@ inline QWORD ConfirmationID::__umul128(QWORD a, QWORD b, QWORD* hi)
*hi = __umulh(a, b); *hi = __umulh(a, b);
return a * b; return a * b;
} }
#elif defined(__i386__) || defined(_M_IX86) || defined(__arm__) || defined(__EMSCRIPTEN__) #elif defined(__i386__) || defined(_M_IX86) || defined(__arm__) || defined(__EMSCRIPTEN__) || defined(_M_ARM)
inline QWORD ConfirmationID::__umul128(QWORD multiplier, QWORD multiplicand, QWORD *product_hi) { inline QWORD ConfirmationID::__umul128(QWORD multiplier, QWORD multiplicand, QWORD *product_hi) {
// multiplier = ab = a * 2^32 + b // multiplier = ab = a * 2^32 + b
// multiplicand = cd = c * 2^32 + d // multiplicand = cd = c * 2^32 + d