50 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
a0e7a2561f move msys 2025-07-07 15:28:52 -05:00
8d92cc123d Update README.md 2025-07-07 14:37:44 -05:00
7af4a1fbcf Update README.md 2025-07-07 14:37:33 -05:00
2796923983 Update README.md 2025-07-07 14:36:04 -05:00
6fda0c9ab8 lets see 2025-07-07 14:18:57 -05:00
5e9ebe10cd i686 2025-07-07 14:07:37 -05:00
f216019046 what the fuck is this naming scheme 2025-07-07 13:57:44 -05:00
9663669c0c i486 2025-07-07 13:48:01 -05:00
af35f41c3f add the atomic lib (2) 2025-07-07 13:30:44 -05:00
0a4561b1cd add the atomic lib 2025-07-07 13:15:47 -05:00
fe8e7c72e0 no atomics 2025-07-07 13:00:58 -05:00
f630ed1e53 compatibility fixes 2025-07-07 12:53:00 -05:00
7c8a7dd54c Compile OpenSSL in-action (#134) 2025-07-07 11:27:04 -05:00
befe149285 let's actually check that 2025-07-06 16:31:21 -05:00
5eb3ed11f8 use CWSDSTUB to avoid needing CWSDPMI (#131) 2025-07-06 15:08:03 -05:00
f3be9fb950 Update dos-djgpp.yml 2025-07-04 09:35:48 -05:00
761ba76081 Update linux.yml 2025-07-04 09:35:34 -05:00
c072d096f3 Update windows.yml 2025-07-04 09:34:58 -05:00
1002e0a6d5 Windows on ARM support + icon fixes (#129)
* Support for Windows on ARM (uses MSVC, but it's Windows 11 exclusive anyway so `¯\_(ツ)_/¯`)
* Adds the icon back for x86/x64 TDM-GCC builds
* Makes Linux compilation on ARM much faster
* added tests to every workflow
2025-07-04 09:29:23 -05:00
6b85771db1 Static OpenSSL for macOS (#127) 2025-07-03 13:03:11 -05:00
8db1fe25d7 Make UMSKT (mostly) static on macOS (#126) 2025-07-03 12:11:00 -05:00
0db5d7f982 Universal binary compilation for macOS (#124) 2025-07-03 10:31:15 -05:00
25ed8d4f43 Merge pull request #120 from UMSKT/nodashes-cmd
Command line argument to remove dashes from product key
2025-06-18 21:50:16 -05:00
324688e9ef Update cli.cpp 2025-06-18 21:33:07 -05:00
6ae46d6aa2 duh 2025-06-18 21:24:37 -05:00
e07c080ee8 Update cli.h 2025-06-18 21:18:11 -05:00
ce9ff7e624 Revert "Update cli.h"
This reverts commit 714895c61b.
2025-06-18 21:17:08 -05:00
714895c61b Update cli.h 2025-06-18 21:13:50 -05:00
6ecf9c5309 Update cli.h 2025-06-18 21:09:15 -05:00
9f7d53dab3 Update cli.cpp 2025-06-18 21:08:02 -05:00
a66e579377 remove everything in a 2 mile radius 2025-06-10 23:43:31 -05:00
e30a4713b6 Update CMakeLists.txt 2025-06-10 23:34:14 -05:00
e9228045d3 Update dos-djgpp.yml 2025-06-10 22:38:03 -05:00
36de07ecdf Update dos-djgpp.yml 2025-06-10 22:33:18 -05:00
a28f4b5ca3 Update dos-djgpp.yml 2025-06-10 22:28:02 -05:00
a0cb9e9869 Update dos-djgpp.yml 2025-06-10 22:26:20 -05:00
6d30abba98 Update dos-djgpp.yml 2025-06-10 22:23:14 -05:00
8b6c3b252b Update dos-djgpp.yml 2025-06-10 22:20:39 -05:00
22 changed files with 2933 additions and 142 deletions

View File

@ -42,7 +42,7 @@ jobs:
- name: Setup build environment - name: Setup build environment
run: | run: |
sudo apt -y update sudo apt -y update
sudo apt -y install build-essential cmake wget 7zip git flex libfl-dev nasm libslang2-dev pkg-config libslang2-modules gcc-multilib sudo apt -y install build-essential cmake wget 7zip git flex libfl-dev nasm libslang2-dev pkg-config libslang2-modules gcc-multilib dosbox tree
- name: Download and Setup DJGPP Toolchain - name: Download and Setup DJGPP Toolchain
run: | run: |
@ -50,6 +50,7 @@ jobs:
wget https://github.com/andrewwutw/build-djgpp/releases/download/v3.4/djgpp-linux64-gcc1220.tar.bz2 wget https://github.com/andrewwutw/build-djgpp/releases/download/v3.4/djgpp-linux64-gcc1220.tar.bz2
tar xjf djgpp-linux64-gcc1220.tar.bz2 tar xjf djgpp-linux64-gcc1220.tar.bz2
cd ${{ github.workspace }}/djgpp cd ${{ github.workspace }}/djgpp
tree . -S
git clone https://github.com/UMSKT/Watt-32.git watt32 git clone https://github.com/UMSKT/Watt-32.git watt32
cd watt32/util cd watt32/util
make clean && make linux make clean && make linux
@ -59,22 +60,124 @@ jobs:
make -f djgpp.mak make -f djgpp.mak
ln -s ${WATT_ROOT}/lib/libwatt.a ${{ github.workspace }}/djgpp/lib ln -s ${WATT_ROOT}/lib/libwatt.a ${{ github.workspace }}/djgpp/lib
- name: Cache OpenSSL 3.1.2
uses: actions/cache/restore@v4
id: cache-openssl
with:
path: |
${{ github.workspace }}/djgpp
key: openssl-3.1.2-${{ hashFiles('**/CMakeLists.txt') }}
- name: Checkout and Cross Compile OpenSSL 3.1.2 - name: Checkout and Cross Compile OpenSSL 3.1.2
if: steps.cache-openssl.outputs.cache-hit != 'true'
run: | run: |
git clone https://github.com/UMSKT/openssl.git openssl git clone https://github.com/UMSKT/openssl.git openssl
pushd openssl pushd openssl
source ${{ github.workspace }}/djgpp/setenv source ${{ github.workspace }}/djgpp/setenv
./Configure no-threads no-tests -DOPENSSL_DEV_NO_ATOMICS --prefix=${{ github.workspace }}/djgpp DJGPP ./Configure 386 no-threads no-tests no-sse2 no-asm -DOPENSSL_DEV_NO_ATOMICS -mno-mmx -mno-sse -mno-sse2 -march=i386 --prefix=${{ github.workspace }}/djgpp DJGPP
make && make install_sw make && make install_sw
popd popd
- name: Save OpenSSL 3.1.2
if: steps.cache-openssl.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: |
${{ github.workspace }}/djgpp
key: openssl-3.1.2-${{ hashFiles('**/CMakeLists.txt') }}
- name: Download stub
run: |
wget https://web.archive.org/web/20250308092209/https://www.ibiblio.org/pub/micro/pc-stuff/freedos/files/util/system/cwsdpmi/csdpmi7b.zip -O CSDPMI7B.ZIP
mkdir /home/runner/dpmibin
7z e CSDPMI7B.ZIP bin/CWSDSTUB.EXE -o/home/runner/dpmibin
- name: Build - name: Build
run: | run: |
source ${{ github.workspace }}/djgpp/setenv source ${{ github.workspace }}/djgpp/setenv
pushd build pushd build
cmake ../ -D DJGPP_WATT32=${WATT_ROOT}/lib/libwatt.a -D CMAKE_FIND_ROOT_PATH=${CMAKE_FIND_ROOT_PATH} cmake ../ -D DJGPP_WATT32=${WATT_ROOT}/lib/libwatt.a -D CMAKE_FIND_ROOT_PATH=${CMAKE_FIND_ROOT_PATH} -D CWSDSTUB_LOCATION=/home/runner/dpmibin/CWSDSTUB.EXE -D DJGPP_BIN_LOCATION=/home/runner/work/UMSKT/UMSKT/djgpp/i586-pc-msdosdjgpp/bin
make make
- name: Setup DOSBox test environment
run: |
mkdir -p dosbox_test
cp build/umskt.exe dosbox_test/
# Download DPMI server directly
# wget https://github.com/UMSKT/winactiontest/raw/refs/heads/main/CWSDPMI.EXE -O dosbox_test/CWSDPMI.EXE
# Create test batch file
cat > dosbox_test/test.bat << EOL
@echo off
echo Running test 1...
umskt.exe -b 2C -c 365 -s 069420 > TEST1.TXT
if errorlevel 1 goto error
echo Running test 2...
umskt.exe -i 253286028742154311079061239762245184619981623171292574 > TEST2.TXT
if errorlevel 1 goto error
echo Tests completed > DONE.TXT
goto end
:error
echo Test failed > ERROR.TXT
:end
exit
EOL
# Create DOSBox configuration
cat > dosbox_test/dosbox.conf << EOL
[sdl]
nosound=true
[cpu]
cputype=386
core=dynamic
cycles=max
[autoexec]
mount c .
c:
test.bat
exit
EOL
- name: Run tests in DOSBox
run: |
cd dosbox_test
timeout 30s dosbox -conf dosbox.conf -nogui -exit
# Check if the test completed successfully
if [ ! -f DONE.TXT ]; then
echo "Tests did not complete successfully"
if [ -f ERROR.TXT ]; then
echo "Test execution failed"
fi
if [ -f TEST1.TXT ]; then
echo "Test 1 output:"
cat TEST1.TXT
fi
if [ -f TEST2.TXT ]; then
echo "Test 2 output:"
cat TEST2.TXT
fi
exit 1
fi
# Verify test outputs
if [ ! -f TEST1.TXT ] || [ ! -f TEST2.TXT ]; then
echo "Test output files missing"
exit 1
fi
# Check test results - looking for key format patterns
if ! grep -qE '[A-Z0-9]{5}-[A-Z0-9]{5}-[A-Z0-9]{5}-[A-Z0-9]{5}-[A-Z0-9]{5}' TEST1.TXT || \
! grep -qE '[0-9]{6}-[0-9]{6}-[0-9]{6}-[0-9]{6}-[0-9]{6}-[0-9]{6}-[0-9]{6}' TEST2.TXT; then
echo "Tests failed - unexpected output format"
echo "Test 1 output:"
cat TEST1.TXT
echo "Test 2 output:"
cat TEST2.TXT
exit 1
else
echo "All tests passed successfully"
echo "Test 1 output:"
cat TEST1.TXT
echo "Test 2 output:"
cat TEST2.TXT
fi
- name: Move executable to upload directory - name: Move executable to upload directory
run: | run: |
mkdir build/actions_upload mkdir build/actions_upload
@ -83,5 +186,5 @@ jobs:
- name: Upload build artifact - name: Upload build artifact
uses: actions/upload-artifact@v4.6.2 uses: actions/upload-artifact@v4.6.2
with: with:
name: UMSKT-DOS name: UMSKT-DOS-x86
path: build/actions_upload path: build/actions_upload

View File

@ -51,7 +51,10 @@
cd build cd build
cmake .. cmake ..
make make
./umskt # Execute the test here echo Test 1 - generating key
./umskt -b 2C -c 365 -s 069420 -v
echo Test 2 - generating confid
./umskt -i 253286028742154311079061239762245184619981623171292574
- name: Move files to correct directory - name: Move files to correct directory
run: | run: |

View File

@ -28,18 +28,25 @@ on:
jobs: jobs:
build: build:
runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
include: include:
- arch: x86 - arch: x86
runner: ubuntu-latest
use_alpine: true
- arch: x86_64 - arch: x86_64
runner: ubuntu-latest
use_alpine: true
- arch: aarch64 - arch: aarch64
runner: ubuntu-24.04-arm
use_alpine: false
runs-on: ${{ matrix.runner }}
steps: steps:
- name: Checkout Source Tree - name: Checkout Source Tree
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Setup latest Alpine Linux for ${{ matrix.arch }} - name: Setup latest Alpine Linux
if: ${{ matrix.use_alpine }}
uses: jirutka/setup-alpine@v1 uses: jirutka/setup-alpine@v1
with: with:
packages: > packages: >
@ -54,13 +61,46 @@ jobs:
arch: ${{ matrix.arch }} arch: ${{ matrix.arch }}
shell-name: alpine-target.sh shell-name: alpine-target.sh
- name: Configure and build UMSKT - name: Install Dependencies (Ubuntu ARM64)
if: ${{ !matrix.use_alpine }}
run: |
sudo apt-get update
sudo apt-get install -y build-essential cmake git libssl-dev zlib1g-dev
- name: Configure and build UMSKT (Alpine)
if: ${{ matrix.use_alpine }}
uses: threeal/cmake-action@7ef2eb8da6e5ec0a6de6b1ddc96987080bed06e8 uses: threeal/cmake-action@7ef2eb8da6e5ec0a6de6b1ddc96987080bed06e8
with: with:
options: MUSL_STATIC=ON options: MUSL_STATIC=ON
run-build: true run-build: true
shell: alpine-target.sh {0} shell: alpine-target.sh {0}
- name: Configure and build UMSKT (Ubuntu)
if: ${{ !matrix.use_alpine }}
uses: threeal/cmake-action@7ef2eb8da6e5ec0a6de6b1ddc96987080bed06e8
with:
options: MUSL_STATIC=ON
run-build: true
- name: Test UMSKT (Alpine)
if: ${{ matrix.use_alpine }}
run: |
cd build
echo Test 1 - generating key
./umskt -b 2C -c 365 -s 069420 -v
echo Test 2 - generating confid
./umskt -i 253286028742154311079061239762245184619981623171292574
shell: alpine-target.sh {0}
- name: Test UMSKT (Ubuntu)
if: ${{ !matrix.use_alpine }}
run: |
cd build
echo Test 1 - generating key
./umskt -b 2C -c 365 -s 069420 -v
echo Test 2 - generating confid
./umskt -i 253286028742154311079061239762245184619981623171292574
- name: Move files to correct directory - name: Move files to correct directory
run: | run: |
mkdir -p build/actions_upload mkdir -p build/actions_upload
@ -69,19 +109,73 @@ jobs:
- name: Upload build artifact - name: Upload build artifact
uses: actions/upload-artifact@v4.6.2 uses: actions/upload-artifact@v4.6.2
with: with:
name: UMSKT-linux-${{ matrix.arch }}-static name: UMSKT-Linux-${{ matrix.arch }}-static
path: build/actions_upload path: build/actions_upload
- name: Configure and build static internal deps UMSKT - name: Configure and build static internal deps UMSKT (Alpine)
if: ${{ matrix.use_alpine }}
uses: threeal/cmake-action@7ef2eb8da6e5ec0a6de6b1ddc96987080bed06e8 uses: threeal/cmake-action@7ef2eb8da6e5ec0a6de6b1ddc96987080bed06e8
with: with:
options: MUSL_STATIC=OFF BUILD_SHARED_LIBS=OFF options: MUSL_STATIC=OFF BUILD_SHARED_LIBS=OFF
run-build: true run-build: true
shell: alpine-target.sh {0} shell: alpine-target.sh {0}
- name: Configure and build shared deps UMSKT - name: Configure and build static internal deps UMSKT (Ubuntu)
if: ${{ !matrix.use_alpine }}
uses: threeal/cmake-action@7ef2eb8da6e5ec0a6de6b1ddc96987080bed06e8
with:
options: MUSL_STATIC=OFF BUILD_SHARED_LIBS=OFF
run-build: true
- name: Test static internal deps UMSKT (Alpine)
if: ${{ matrix.use_alpine }}
run: |
cd build
echo Test 1 - generating key
./umskt -b 2C -c 365 -s 069420 -v
echo Test 2 - generating confid
./umskt -i 253286028742154311079061239762245184619981623171292574
shell: alpine-target.sh {0}
- name: Test static internal deps UMSKT (Ubuntu)
if: ${{ !matrix.use_alpine }}
run: |
cd build
echo Test 1 - generating key
./umskt -b 2C -c 365 -s 069420 -v
echo Test 2 - generating confid
./umskt -i 253286028742154311079061239762245184619981623171292574
- name: Configure and build shared deps UMSKT (Alpine)
if: ${{ matrix.use_alpine }}
uses: threeal/cmake-action@7ef2eb8da6e5ec0a6de6b1ddc96987080bed06e8 uses: threeal/cmake-action@7ef2eb8da6e5ec0a6de6b1ddc96987080bed06e8
with: with:
options: MUSL_STATIC=OFF BUILD_SHARED_LIBS=ON options: MUSL_STATIC=OFF BUILD_SHARED_LIBS=ON
run-build: true run-build: true
shell: alpine-target.sh {0} shell: alpine-target.sh {0}
- name: Configure and build shared deps UMSKT (Ubuntu)
if: ${{ !matrix.use_alpine }}
uses: threeal/cmake-action@7ef2eb8da6e5ec0a6de6b1ddc96987080bed06e8
with:
options: MUSL_STATIC=OFF BUILD_SHARED_LIBS=ON
run-build: true
- name: Test shared deps UMSKT (Alpine)
if: ${{ matrix.use_alpine }}
run: |
cd build
echo Test 1 - generating key
./umskt -b 2C -c 365 -s 069420 -v
echo Test 2 - generating confid
./umskt -i 253286028742154311079061239762245184619981623171292574
shell: alpine-target.sh {0}
- name: Test shared deps UMSKT (Ubuntu)
if: ${{ !matrix.use_alpine }}
run: |
cd build
echo Test 1 - generating key
./umskt -b 2C -c 365 -s 069420 -v
echo Test 2 - generating confid
./umskt -i 253286028742154311079061239762245184619981623171292574

View File

@ -27,34 +27,97 @@ on:
workflow_dispatch: workflow_dispatch:
jobs: jobs:
build-x86: build:
runs-on: macos-latest runs-on: macos-latest
strategy: strategy:
matrix: matrix:
include: include:
- arch: x86_64 - arch: x86_64
- arch: arm64
steps: steps:
- name: Checkout Source Tree - name: Checkout Source Tree
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Rosetta
if: matrix.arch == 'x86_64'
run: |
sudo softwareupdate --install-rosetta --agree-to-license || true
- name: Install Homebrew for x86_64
if: matrix.arch == 'x86_64'
env: { NONINTERACTIVE: 1 }
run: |
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
- name: Install x86_64 OpenSSL
if: matrix.arch == 'x86_64'
run: |
arch -x86_64 /usr/local/bin/brew install openssl@3
echo "OPENSSL_ROOT_DIR=/usr/local/opt/openssl@3" >> $GITHUB_ENV
echo "OPENSSL_INCLUDE_DIR=/usr/local/opt/openssl@3/include" >> $GITHUB_ENV
echo "PKG_CONFIG_PATH=/usr/local/opt/openssl@3/lib/pkgconfig" >> $GITHUB_ENV
- name: Install arm64 OpenSSL
if: matrix.arch == 'arm64'
run: |
brew install openssl@3
echo "OPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl@3" >> $GITHUB_ENV
echo "OPENSSL_INCLUDE_DIR=/opt/homebrew/opt/openssl@3/include" >> $GITHUB_ENV
echo "PKG_CONFIG_PATH=/opt/homebrew/opt/openssl@3/lib/pkgconfig" >> $GITHUB_ENV
- name: Configure and build UMSKT - name: Configure and build UMSKT
run: | run: |
cd build cd build
cmake -DCMAKE_BUILD_TYPE=Release .. cmake -DCMAKE_BUILD_TYPE=Release .. \
-DCMAKE_OSX_ARCHITECTURES=${{matrix.arch}} \
-DOPENSSL_ROOT_DIR=$OPENSSL_ROOT_DIR \
-DOPENSSL_INCLUDE_DIR=$OPENSSL_INCLUDE_DIR \
-DOPENSSL_USE_STATIC_LIBS=TRUE \
-DBUILD_SHARED_LIBS=OFF
make make
- name: Move files to correct directory - name: Move files to correct directory
run: | run: |
ls -a build
mkdir -p build/actions_upload mkdir -p build/actions_upload
mv build/umskt build/actions_upload/umskt mv build/umskt build/actions_upload/umskt
- name: Run tests - name: Run tests
run: | run: |
cd build/actions_upload cd build/actions_upload
./umskt echo Test 1 - generating key
./umskt -b 2C -c 365 -s 069420 -v
echo Test 2 - generating confid
./umskt -i 253286028742154311079061239762245184619981623171292574
- name: Upload build artifact - name: Upload platform-specific build
uses: actions/upload-artifact@v4.6.2 uses: actions/upload-artifact@v4.6.2
with: with:
name: UMSKT-macOS-${{ matrix.arch }} name: UMSKT-macOS-${{ matrix.arch }}
path: build/actions_upload path: build/actions_upload
universal:
needs: build
runs-on: macos-latest
steps:
- name: Download x86_64 slice
uses: actions/download-artifact@v4
with:
name: UMSKT-macOS-x86_64
path: x86
- name: Download arm64 slice
uses: actions/download-artifact@v4
with:
name: UMSKT-macOS-arm64
path: arm
- name: Create universal binary
shell: bash
run: |
mkdir fat
lipo -create x86/umskt arm/umskt -output fat/umskt
lipo -info fat/umskt
- name: Upload universal build
uses: actions/upload-artifact@v4
with:
name: UMSKT-macOS-universal
path: fat

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,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 TheTank20 on 06/13/2023
# @Maintainer Neo
name: C/C++ CI (Windows)
on:
push:
branches: [ "*" ]
paths-ignore: [ '**.md', 'doc/**', '.idea/**']
workflow_dispatch:
jobs:
build-tdm:
runs-on: windows-latest
strategy:
matrix:
arch: [x64, x86]
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: Download OpenSSL-TDM release asset
shell: pwsh
run: |
if ('${{ matrix.arch }}' -eq 'x64') {
$url = "https://github.com/UMSKT/winactiontest/releases/download/openssl/OpenSSL-TDM-64.zip"
$dest = "$env:GITHUB_WORKSPACE/OpenSSL-TDM-64"
$libdir = "lib64"
$cmake_flags = "-m64"
$openssl_root = "$env:GITHUB_WORKSPACE/OpenSSL-TDM-64"
} else {
$url = "https://github.com/UMSKT/winactiontest/releases/download/openssl/OpenSSL-TDM-32.zip"
$dest = "$env:GITHUB_WORKSPACE/OpenSSL-TDM-32"
$libdir = "lib32"
$cmake_flags = "-m32"
$openssl_root = "$env:GITHUB_WORKSPACE/OpenSSL-TDM-32"
}
Invoke-WebRequest -Uri $url -OutFile "OpenSSL-TDM.zip"
Expand-Archive -Path "OpenSSL-TDM.zip" -DestinationPath $env:GITHUB_WORKSPACE
echo "OPENSSL_ROOT=$openssl_root" | Out-File -FilePath $env:GITHUB_ENV -Append
echo "OPENSSL_LIBDIR=$libdir" | Out-File -FilePath $env:GITHUB_ENV -Append
echo "CMAKE_FLAGS=$cmake_flags" | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Configure UMSKT (TDM-GCC ${{ matrix.arch }})
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 (TDM-GCC ${{ matrix.arch }})
shell: pwsh
run: |
$env:PATH = 'C:\TDM-GCC-64\bin;' + $env:PATH
mingw32-make
- name: Upload build artifact
uses: actions/upload-artifact@v4.6.2
with:
name: UMSKT-TDM${{ matrix.arch }}
path: umskt.exe

10
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,10 @@
# Stub so pre-commit can shut up until the refactor
repos:
- repo: local
hooks:
- id: no-op
name: Noop hook (always passes)
entry: "true" # `/usr/bin/true` returns 0 and does nothing
language: system
pass_filenames: false # dont bother passing any file paths

View File

@ -26,6 +26,26 @@ if (WIN32 AND NOT MSVC)
set(CMAKE_C_COMPILER "C:/TDM-GCC-64/bin/gcc.exe" CACHE FILEPATH "C Compiler" FORCE) set(CMAKE_C_COMPILER "C:/TDM-GCC-64/bin/gcc.exe" CACHE FILEPATH "C Compiler" FORCE)
set(CMAKE_CXX_COMPILER "C:/TDM-GCC-64/bin/g++.exe" CACHE FILEPATH "C++ Compiler" FORCE) set(CMAKE_CXX_COMPILER "C:/TDM-GCC-64/bin/g++.exe" CACHE FILEPATH "C++ Compiler" FORCE)
message(STATUS "[UMSKT] Forcing use of TDM-GCC in C:/TDM-GCC-64") message(STATUS "[UMSKT] Forcing use of TDM-GCC in C:/TDM-GCC-64")
# Configure windres for resource compilation
set(CMAKE_RC_COMPILER "C:/TDM-GCC-64/bin/windres.exe")
set(CMAKE_RC_COMPILER_INIT windres)
enable_language(RC)
set(CMAKE_RC_FLAGS "--use-temp-file -c65001")
# Match resource architecture with target architecture
if(CMAKE_SIZEOF_VOID_P EQUAL 4)
set(CMAKE_RC_FLAGS "${CMAKE_RC_FLAGS} -F pe-i386 --target=pe-i386")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=i686 -mtune=generic -mno-sse -mno-sse2 -mno-mmx -mno-3dnow")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=i686 -mtune=generic -mno-sse -mno-sse2 -mno-mmx -mno-3dnow")
else()
set(CMAKE_RC_FLAGS "${CMAKE_RC_FLAGS} -F pe-x86-64 --target=pe-x86-64")
endif()
set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> <FLAGS> -O coff -I${CMAKE_CURRENT_SOURCE_DIR}/src/windows <DEFINES> -i <SOURCE> -o <OBJECT>")
# Set the Windows resource file for GCC builds
set(UMSKT_EXE_WINDOWS_EXTRA src/windows/umskt.rc)
endif() endif()
SET(CMAKE_CXX_STANDARD 17) SET(CMAKE_CXX_STANDARD 17)
@ -36,13 +56,14 @@ OPTION(UMSKT_USE_SHARED_OPENSSL "Force linking against the system-wide OpenSSL l
OPTION(MUSL_STATIC "Enable fully static builds in a muslc environment (i.e. Alpine Linux)" 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(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(MSVC_MSDOS_STUB "Specify a custom MS-DOS stub for a 32-bit MSVC compilation" OFF)
OPTION(WINDOWS_ARM "Enable compilation for Windows on ARM (requires appropriate toolchain)" OFF)
SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS}) SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS})
SET(UMSKT_LINK_DIRS ${UMSKT_LINK_DIRS}) SET(UMSKT_LINK_DIRS ${UMSKT_LINK_DIRS})
# macOS does not support static build
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
SET(UMSKT_USE_SHARED_OPENSSL ON) SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-dead_strip")
endif() endif()
# neither does dos idk i'm trying random stuff # neither does dos idk i'm trying random stuff
@ -60,7 +81,22 @@ ELSE()
MESSAGE(STATUS "[UMSKT] Requesting static version of OpenSSL") MESSAGE(STATUS "[UMSKT] Requesting static version of OpenSSL")
ENDIF() ENDIF()
# Configure ARM-specific settings if enabled
if (WINDOWS_ARM)
if (MSVC)
# MSVC ARM64 settings
set(CMAKE_SYSTEM_PROCESSOR ARM64)
set(CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE ARM64)
else()
# MinGW/GCC ARM settings
set(CMAKE_SYSTEM_PROCESSOR arm)
if (CMAKE_CROSSCOMPILING)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv8-a")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv8-a")
endif()
endif()
message(STATUS "[UMSKT] Configuring for Windows on ARM")
endif()
IF(DJGPP_WATT32) IF(DJGPP_WATT32)
SET(CMAKE_SYSTEM_NAME MSDOS) SET(CMAKE_SYSTEM_NAME MSDOS)
@ -70,12 +106,18 @@ IF(DJGPP_WATT32)
SET(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) SET(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS} ${DJGPP_WATT32}) SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS} ${DJGPP_WATT32})
SET(UMSKT_LINK_DIRS ${UMSKT_LINK_DIRS} ${WATT_ROOT}/lib) SET(UMSKT_LINK_DIRS ${UMSKT_LINK_DIRS} ${WATT_ROOT}/lib)
# Maximum compatibility flags for DOS/NTVDM
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=i386 -mtune=i386 -mno-sse -mno-sse2 -mno-mmx -mno-3dnow -mno-ssse3 -mno-sse3 -mno-sse4 -mno-sse4.1 -mno-sse4.2 -mno-avx -mno-avx2 -mno-fma -mno-fma4")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=i386 -mtune=i386 -mno-sse -mno-sse2 -mno-mmx -mno-3dnow -mno-ssse3 -mno-sse3 -mno-sse4 -mno-sse4.1 -mno-sse4.2 -mno-avx -mno-avx2 -mno-fma -mno-fma4")
MESSAGE(STATUS "[UMSKT] Using i386 target for DOS DGJPP with stub: ${CWSDSTUB_LOCATION}")
ENDIF() ENDIF()
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") ##if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
SET(BUILD_SHARED_LIBS ON) ## SET(BUILD_SHARED_LIBS ON)
MESSAGE(STATUS "[UMSKT] macOS has no static library - Shared library forced on") ## MESSAGE(STATUS "[UMSKT] macOS has no static library - Shared library forced on")
endif() ##endif()
# if we're compiling with MSVC, respect the DEBUG compile option # if we're compiling with MSVC, respect the DEBUG compile option
IF(MSVC) IF(MSVC)
@ -162,6 +204,7 @@ CPMAddPackage(
GITHUB_REPOSITORY fmtlib/fmt GITHUB_REPOSITORY fmtlib/fmt
GIT_TAG 10.0.0 GIT_TAG 10.0.0
VERSION 10.0.0 VERSION 10.0.0
#OPTIONS "FMT_INSTALL YES" "BUILD_SHARED_LIBS OFF"
) )
# Include cmrc resource compiler # Include cmrc resource compiler
@ -233,4 +276,21 @@ ELSE()
GET_FILENAME_COMPONENT(OPENSSL_CRYPTO_LIBRARY_FILENAME ${OPENSSL_CRYPTO_LIBRARY} NAME) 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) CONFIGURE_FILE(${OPENSSL_CRYPTO_LIBRARY} "${CMAKE_CURRENT_BINARY_DIR}/${OPENSSL_CRYPTO_LIBRARY_FILENAME}" COPYONLY)
ENDIF() ENDIF()
IF (DJGPP_WATT32)
message(STATUS "[UMSKT] Configuring DJGPP post-build commands")
# Set .exe suffix for DJGPP builds
set_target_properties(umskt PROPERTIES SUFFIX ".exe")
add_custom_command(TARGET umskt POST_BUILD
# Convert exe to coff, removing the default stub
COMMAND ${DJGPP_BIN_LOCATION}/exe2coff $<TARGET_FILE:umskt>
# Concatenate CWSDSTUB with the COFF file
COMMAND ${CMAKE_COMMAND} -E cat ${CWSDSTUB_LOCATION} $<TARGET_FILE_DIR:umskt>/umskt > $<TARGET_FILE:umskt>
# Clean up the COFF file
COMMAND ${CMAKE_COMMAND} -E rm $<TARGET_FILE_DIR:umskt>/umskt
)
message(STATUS "[UMSKT] STUBIFY_LOCATION set to: ${STUBIFY_LOCATION}")
message(STATUS "[UMSKT] CWSDSTUB_LOCATION set to: ${CWSDSTUB_LOCATION}")
message(STATUS "[UMSKT] DJGPP_BIN_LOCATION set to: ${DJGPP_BIN_LOCATION}")
ENDIF()
ENDIF() ENDIF()

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)
@ -44,6 +46,33 @@ In light of the recent exponential interest in this project I've decided to put
------ ------
### System Requirements
#### MS-DOS
* i386 processor or better
* MS-DOS 6.22 or later
* Any DOS-based version of Windows
* Windows NT 4.0 or later (via NTVDM)
#### Windows (x86/x64)
* i686 processor or better
* Windows XP or later
> [!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
* Apple Silicon or x86_64 processor
* Latest version of macOS
#### Linux
* modern ARM, x86, or x86_64 processor
* Latest version of your Linux distro
------
### **Usage** ### **Usage**
#### 1. Download the latest version of UMSKT #### 1. Download the latest version of UMSKT
@ -54,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)*
#### 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

@ -71,7 +71,8 @@ void CLI::showHelp(char *argv[]) {
fmt::print("\t-u --upgrade\tspecifies the Product Key will be an \"Upgrade\" version\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-V --validate\tproduct key to validate signature\n");
fmt::print("\t-N --nonewlines\tdisables newlines (for easier embedding in other apps)\n"); fmt::print("\t-N --nonewlines\tdisables newlines (for easier embedding in other apps)\n");
fmt::print("\t-o --override\tDisables version check for confirmation ID's, if you need this send an issue on GitHub"); fmt::print("\t-o --override\tDisables version check for confirmation IDs, if you need this send an issue on GitHub\n");
fmt::print("\t-D --nodashes\tDisables dashes in product keys and confirmation IDs (for easier copy-pasting)");
fmt::print("\n"); fmt::print("\n");
} }
@ -94,6 +95,7 @@ int CLI::parseCommandLine(int argc, char* argv[], Options* options) {
false, false,
false, false,
false, false,
false,
MODE_BINK1998_GENERATE, MODE_BINK1998_GENERATE,
WINDOWS WINDOWS
}; };
@ -214,6 +216,8 @@ int CLI::parseCommandLine(int argc, char* argv[], Options* options) {
options->nonewlines = true; options->nonewlines = true;
} else if (arg == "-o" || arg == "--override") { } else if (arg == "-o" || arg == "--override") {
options->overrideVersion = true; options->overrideVersion = true;
} else if (arg == "-D" || arg == "--nodashes") {
options->nodashes = true;
} else { } else {
options->error = true; options->error = true;
} }
@ -327,9 +331,14 @@ void CLI::printID(DWORD *pid) {
void CLI::printKey(char *pk) { void CLI::printKey(char *pk) {
assert(strlen(pk) >= PK_LENGTH); assert(strlen(pk) >= PK_LENGTH);
std::string keyFormat = "{}-{}-{}-{}-{}";
if (this->options.nodashes == true) {
keyFormat = "{}{}{}{}{}";
}
std::string spk = pk; std::string spk = pk;
fmt::print("{}-{}-{}-{}-{}", fmt::print(keyFormat,
spk.substr(0,5), spk.substr(0,5),
spk.substr(5,5), spk.substr(5,5),
spk.substr(10,5), spk.substr(10,5),
@ -591,7 +600,17 @@ int CLI::ConfirmationID() {
return 1; return 1;
case SUCCESS: case SUCCESS:
fmt::print(confirmation_id); if (this->options.nodashes == true) {
int j = 0;
for (int i = 0; confirmation_id[i] != '\0'; ++i) {
if (confirmation_id[i] != '-') {
confirmation_id[j++] = confirmation_id[i];
}
}
confirmation_id[j] = '\0';
}
fmt::print(confirmation_id);
if (this->options.nonewlines == false) { if (this->options.nonewlines == false) {
fmt::print("\n"); fmt::print("\n");
} }

View File

@ -69,6 +69,7 @@ struct Options {
bool list; bool list;
bool nonewlines; bool nonewlines;
bool overrideVersion; bool overrideVersion;
bool nodashes;
MODE applicationMode; MODE applicationMode;
ACTIVATION_ALGORITHM activationMode; ACTIVATION_ALGORITHM activationMode;
@ -93,7 +94,7 @@ public:
static int parseCommandLine(int argc, char* argv[], Options *options); static int parseCommandLine(int argc, char* argv[], Options *options);
static int validateCommandLine(Options* options, char *argv[], json *keys); static int validateCommandLine(Options* options, char *argv[], json *keys);
static void printID(DWORD *pid); static void printID(DWORD *pid);
static void printKey(char *pk); void printKey(char *pk);
static bool stripKey(const char *in_key, char out_key[PK_LENGTH]); static bool stripKey(const char *in_key, char out_key[PK_LENGTH]);
int BINK1998Generate(); int BINK1998Generate();

View File

@ -29,6 +29,10 @@
#include "confid.h" #include "confid.h"
#if defined(_MSC_VER)
#include <intrin.h>
#endif
QWORD MOD = 0; QWORD MOD = 0;
QWORD NON_RESIDUE = 0; QWORD NON_RESIDUE = 0;
QWORD f[6] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; QWORD f[6] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
@ -75,7 +79,13 @@ inline QWORD ConfirmationID::__umul128(QWORD a, QWORD b, QWORD* hi)
#else #else
#define __umul128 _umul128 #define __umul128 _umul128
#endif #endif
#elif defined(__i386__) || defined(_M_IX86) || defined(__arm__) || defined(__EMSCRIPTEN__) #elif defined(_M_ARM64) // Microsoft implementation of ARM64
inline QWORD ConfirmationID::__umul128(QWORD a, QWORD b, QWORD* hi)
{
*hi = __umulh(a, b);
return a * b;
}
#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

Binary file not shown.