WIP - CryptoPP Port, remove refrences to OpenSSL, many things are currently broken

This commit is contained in:
Neo-Desktop 2024-02-04 20:26:29 -08:00
parent a827844cbd
commit 2ac7f9bc1e
27 changed files with 690 additions and 816 deletions

View File

@ -1,2 +1,2 @@
build*/
cmake-*/
cmake*/

4
.gitignore vendored
View File

@ -1,4 +1,4 @@
build/*
build*/
*.tar
*.exe
*.wasm
@ -66,7 +66,7 @@ umskt
# *.ipr
# CMake
cmake-*/
cmake*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml

View File

@ -19,7 +19,12 @@
# @Maintainer Neo
repos:
- repo: https://github.com/pre-commit/mirrors-clang-format
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: 'v17.0.6' # Use the sha / tag you want to point at
hooks:
- id: clang-format
- id: clang-format
# - repo: https://github.com/cheshirekow/cmake-format-precommit
# rev: v0.6.10
# hooks:
# - id: cmake-format

View File

@ -26,32 +26,19 @@ SET(CMAKE_OSX_SYSROOT "macosx" CACHE PATH "macOS SDK path")
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
OPTION(BUILD_SHARED_LIBS "Build all libraries as shared" OFF)
OPTION(UMSKT_USE_SHARED_OPENSSL "Force linking against the system-wide OpenSSL library" OFF)
OPTION(MUSL_STATIC "Enable fully static builds in a muslc environment (i.e. Alpine Linux)" OFF)
OPTION(DJGPP_WATT32 "Enable compilation and linking with DJGPP/WATT32/OpenSSL" OFF)
OPTION(DJGPP_WATT32 "Enable compilation and linking with DJGPP/WATT32" OFF)
OPTION(MSVC_MSDOS_STUB "Specify a custom MS-DOS stub for a 32-bit MSVC compilation" OFF)
SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS})
SET(UMSKT_LINK_DIRS ${UMSKT_LINK_DIRS})
# macOS does not support static build
IF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
SET(UMSKT_USE_SHARED_OPENSSL ON)
ENDIF ()
# neither does dos idk i'm trying random stuff
IF (DJGPP_WATT32)
SET(UMSKT_USE_SHARED_OPENSSL ON)
ENDIF ()
IF (UMSKT_USE_SHARED_OPENSSL)
SET(OPENSSL_USE_STATIC_LIBS FALSE)
SET(OPENSSL_MSVC_STATIC_RT FALSE)
MESSAGE(STATUS "[UMSKT] Requesting dynamic version of OpenSSL")
ELSE ()
SET(OPENSSL_USE_STATIC_LIBS TRUE)
SET(OPENSSL_MSVC_STATIC_RT TRUE)
MESSAGE(STATUS "[UMSKT] Requesting static version of OpenSSL")
IF (NOT MSVC)
SET(CMAKE_CXX_FLAGS "-Os -fdata-sections -ffunction-sections -flto -Wl,--gc-sections")
SET(CMAKE_CXX_FLAGS_DEBUG "-g")
SET(CMAKE_CXX_FLAGS_DEBUG_INIT "-Wall -Wextra")
SET(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -Wl,--gc-sections")
SET(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -Wl,--gc-sections")
ENDIF ()
IF (DJGPP_WATT32)
@ -72,9 +59,11 @@ ENDIF ()
# if we're compiling with MSVC, respect the DEBUG compile option
IF (MSVC)
SET(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
SET(CMAKE_CXX_FLAGS "/O1 /GL /LTCG /Gy /OPT:REF /OPT:ICF /EHsc")
SET(CMAKE_CXX_FLAGS_RELEASE "/MT")
SET(CMAKE_CXX_FLAGS_DEBUG "/MTd")
SET(CMAKE_EXE_LINKER_FLAGS "/INCREMENTAL:NO /NODEFAULTLIB:MSVCRT")
SET(CMAKE_CXX_FLAGS_DEBUG "/MTd /Z7")
SET(CMAKE_EXE_LINKER_FLAGS "/INCREMENTAL:NO /NODEFAULTLIB:MSVCRT /LTCG")
SET(CMAKE_EXE_LINKER_FLAGS_DEBUG "/DEBUG")
SET(CMAKE_ENABLE_EXPORTS ON)
SET(UMSKT_EXE_WINDOWS_EXTRA src/windows/umskt.rc)
SET(UMSKT_EXE_WINDOWS_DLL src/windows/dllmain.cpp)
@ -83,53 +72,11 @@ ENDIF ()
IF (MUSL_STATIC)
MESSAGE(STATUS "[UMSKT] Performing a fully static build using muslc")
SET(BUILD_SHARED_LIBS OFF)
SET(OPENSSL_USE_STATIC_LIBS TRUE)
SET(CMAKE_EXE_LINKER_FLAGS "-static -static-libgcc -static-libstdc++")
SET(CMAKE_SHARED_LINKER_FLAGS "-static -static-libgcc -static-libstdc++")
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static-libgcc -static-libstdc++")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++")
ENDIF ()
# find the system installed OpenSSL development library
FIND_PACKAGE(OpenSSL REQUIRED)
IF (NOT OPENSSL_FOUND)
MESSAGE(SEND_ERROR "OpenSSL Development Libraries Not Found")
MESSAGE(SEND_ERROR "Please consult your package manager of choice to install the prerequisite")
MESSAGE(SEND_ERROR "The package name is commonly called libssl-dev or openssl-dev depending on distribution")
MESSAGE(FATAL_ERROR "Can not continue without OpenSSL")
ENDIF ()
IF (NOT MUSL_STATIC)
# if we found shared libraries - do the following:
IF (OPENSSL_USE_STATIC_LIBS)
MESSAGE(STATUS "[UMSKT] requested static version of OpenSSL")
IF (NOT UMSKT_USE_SHARED_OPENSSL)
MESSAGE(STATUS "[UMSKT] not asked for shared version of OpenSSL")
ENDIF ()
IF (MSVC)
SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS} "ws2_32.lib")
SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS} "crypt32.lib")
MESSAGE(STATUS "[UMSKT] msvc adding ws2_32.lib crypt32.lib")
ENDIF ()
ENDIF ()
STRING(REGEX MATCH "(\\.so|\\.dll|\\.dylib)$" OPENSSL_CRYPTO_SHARED "${OPENSSL_CRYPTO_LIBRARY}")
IF (OPENSSL_CRYPTO_SHARED)
MESSAGE(STATUS "[UMSKT] Detected Shared library version of OpenSSL")
ELSE ()
MESSAGE(STATUS "[UMSKT] Detected Static Library version of OpenSSL")
# static libcrypto on Fedora needs -lz at link time
IF (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
FIND_PACKAGE(ZLIB REQUIRED)
IF (NOT ZLIB_FOUND)
MESSAGE(FATAL_ERROR "[UMSKT] linux static OpenSSL requires zlib")
ENDIF ()
ENDIF ()
ENDIF ()
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static -static-libgcc -static-libstdc++")
ENDIF ()
# initialize cpm.CMake
@ -166,13 +113,13 @@ CPMAddPackage(
)
# Include Crypto++ development library
#CPMAddPackage(
# NAME cryptopp-cmake
# GITHUB_REPOSITORY abdes/cryptopp-cmake
# GIT_TAG CRYPTOPP_8_8_0
# VERSION 8.8.0
# OPTIONS "CRYPTOPP_BUILD_TESTING OFF"
#)
CPMAddPackage(
NAME cryptopp-cmake
GITHUB_REPOSITORY abdes/cryptopp-cmake
GIT_TAG CRYPTOPP_8_9_0
VERSION 8.9.0
OPTIONS "CRYPTOPP_BUILD_TESTING OFF"
)
#include googletest unit testing library
#CPMAddPackage(
@ -190,7 +137,7 @@ SET(LIBUMSKT_PIDGEN3 src/libumskt/pidgen3/PIDGEN3.cpp src/libumskt/pidgen3/BINK1
SET(LIBUMSKT_CONFID src/libumskt/confid/confid.cpp src/libumskt/confid/polynomial.cpp src/libumskt/confid/residue.cpp src/libumskt/confid/divisor.cpp)
SET(LIBUMSKT_SRC src/libumskt/libumskt.cpp src/libumskt/debugoutput.cpp ${LIBUMSKT_PIDGEN2} ${LIBUMSKT_PIDGEN3} ${LIBUMSKT_CONFID})
SET(UMSKT_CLI_SRC src/main.cpp src/help.cpp src/cli.cpp src/generate.cpp)
SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS} ${OPENSSL_CRYPTO_LIBRARIES} ${ZLIB_LIBRARIES} fmt)
SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS} fmt cryptopp)
IF (NOT MSVC)
SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS} umskt::rc)
@ -202,7 +149,8 @@ ENDIF()
#### Separate Build Path for emscripten
IF (EMSCRIPTEN)
ADD_EXECUTABLE(umskt ${UMSKT_CLI_SRC} ${LIBUMSKT_SRC})
TARGET_INCLUDE_DIRECTORIES(umskt PUBLIC ${OPENSSL_INCLUDE_DIR})
TARGET_INCLUDE_DIRECTORIES(umskt PUBLIC ${CMAKE_BINARY_DIR})
TARGET_LINK_DIRECTORIES(umskt PUBLIC ${UMSKT_LINK_DIRS})
TARGET_LINK_LIBRARIES(umskt PUBLIC ${UMSKT_LINK_LIBS})
SET(CMAKE_EXECUTABLE_SUFFIX ".html")
@ -210,31 +158,38 @@ IF (EMSCRIPTEN)
SET_TARGET_PROPERTIES(umskt PROPERTIES LINK_FLAGS "-Os -sWASM=1 -sEXPORT_ALL=1 -sEXPORTED_RUNTIME_METHODS=ccall,cwrap --no-entry")
ELSE ()
## umskt.so/.dll creation
ADD_LIBRARY(_umskt SHARED ${LIBUMSKT_SRC} ${UMSKT_EXE_WINDOWS_EXTRA} ${UMSKT_EXE_WINDOWS_DLL})
TARGET_INCLUDE_DIRECTORIES(_umskt PUBLIC ${OPENSSL_INCLUDE_DIR} ${CMAKE_BINARY_DIR})
TARGET_LINK_DIRECTORIES(_umskt PUBLIC ${UMSKT_LINK_DIRS})
TARGET_LINK_LIBRARIES(_umskt PUBLIC ${UMSKT_LINK_LIBS})
IF(MSVC)
SET_TARGET_PROPERTIES(_umskt PROPERTIES OUTPUT_NAME libumskt)
ELSE()
SET_TARGET_PROPERTIES(_umskt PROPERTIES OUTPUT_NAME umskt)
ENDIF()
ADD_LIBRARY(libumskt SHARED ${LIBUMSKT_SRC} ${UMSKT_EXE_WINDOWS_EXTRA} ${UMSKT_EXE_WINDOWS_DLL})
TARGET_INCLUDE_DIRECTORIES(libumskt PUBLIC ${CMAKE_BINARY_DIR})
TARGET_LINK_DIRECTORIES(libumskt PUBLIC ${UMSKT_LINK_DIRS})
TARGET_LINK_LIBRARIES(libumskt PUBLIC ${UMSKT_LINK_LIBS})
IF (MSVC)
SET_TARGET_PROPERTIES(libumskt PROPERTIES OUTPUT_NAME libumskt)
ELSE ()
SET_TARGET_PROPERTIES(libumskt PROPERTIES OUTPUT_NAME umskt)
ENDIF ()
## umskt_static.a/.lib creation
ADD_LIBRARY(umskt_static ${LIBUMSKT_SRC} ${UMSKT_EXE_WINDOWS_EXTRA} ${UMSKT_EXE_WINDOWS_DLL})
TARGET_INCLUDE_DIRECTORIES(umskt_static PUBLIC ${OPENSSL_INCLUDE_DIR} ${CMAKE_BINARY_DIR})
TARGET_LINK_DIRECTORIES(umskt_static PUBLIC ${UMSKT_LINK_DIRS})
TARGET_LINK_LIBRARIES(umskt_static PUBLIC ${UMSKT_LINK_LIBS})
IF(MSVC)
SET_TARGET_PROPERTIES(umskt_static PROPERTIES OUTPUT_NAME libumskt_static)
ELSE()
SET_TARGET_PROPERTIES(umskt_static PROPERTIES OUTPUT_NAME umskt_static)
ENDIF()
ADD_LIBRARY(libumskt_static ${LIBUMSKT_SRC} ${UMSKT_EXE_WINDOWS_EXTRA} ${UMSKT_EXE_WINDOWS_DLL})
TARGET_INCLUDE_DIRECTORIES(libumskt_static PUBLIC ${CMAKE_BINARY_DIR})
TARGET_LINK_DIRECTORIES(libumskt_static PUBLIC ${UMSKT_LINK_DIRS})
TARGET_LINK_LIBRARIES(libumskt_static PUBLIC ${UMSKT_LINK_LIBS})
IF (MSVC)
SET_TARGET_PROPERTIES(libumskt_static PROPERTIES OUTPUT_NAME libumskt_static)
ELSE ()
SET_TARGET_PROPERTIES(libumskt_static PROPERTIES OUTPUT_NAME umskt_static)
ENDIF ()
### UMSKT executable compilation
ADD_EXECUTABLE(umskt ${UMSKT_CLI_SRC} ${UMSKT_EXE_WINDOWS_EXTRA})
TARGET_INCLUDE_DIRECTORIES(umskt PUBLIC ${OPENSSL_INCLUDE_DIR} ${CMAKE_BINARY_DIR})
TARGET_LINK_LIBRARIES(umskt PUBLIC umskt_static ${UMSKT_LINK_LIBS} nlohmann_json::nlohmann_json)
IF (NOT BUILD_SHARED_LIBS)
## Link against the static lib
ADD_EXECUTABLE(umskt ${UMSKT_CLI_SRC} ${UMSKT_EXE_WINDOWS_EXTRA})
TARGET_LINK_LIBRARIES(umskt PUBLIC libumskt_static ${UMSKT_LINK_LIBS} nlohmann_json::nlohmann_json)
ELSE ()
## Link against the dynamic lib
ADD_EXECUTABLE(umskt ${UMSKT_CLI_SRC} ${UMSKT_EXE_WINDOWS_EXTRA})
TARGET_LINK_LIBRARIES(umskt PUBLIC libumskt ${UMSKT_LINK_LIBS} nlohmann_json::nlohmann_json)
ENDIF()
TARGET_INCLUDE_DIRECTORIES(umskt PUBLIC ${CMAKE_BINARY_DIR})
TARGET_LINK_DIRECTORIES(umskt PUBLIC ${UMSKT_LINK_DIRS})
IF (MSVC)
SET_PROPERTY(TARGET umskt APPEND PROPERTY COMPILE_FLAGS /DUMSKT_CLI_WINRC_EMBED_JSON)
@ -246,10 +201,4 @@ ELSE ()
IF (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
INSTALL(TARGETS umskt DESTINATION bin)
ENDIF ()
### Copy Shared Libraries and dependency files
IF (OPENSSL_CRYPTO_SHARED)
GET_FILENAME_COMPONENT(OPENSSL_CRYPTO_LIBRARY_FILENAME ${OPENSSL_CRYPTO_LIBRARY} NAME)
CONFIGURE_FILE(${OPENSSL_CRYPTO_LIBRARY} "${CMAKE_CURRENT_BINARY_DIR}/${OPENSSL_CRYPTO_LIBRARY_FILENAME}" COPYONLY)
ENDIF ()
ENDIF ()

View File

@ -40,14 +40,15 @@ COPY . /src
# Build UMSKT from the local directory
RUN mkdir /src/build \
&& cd /src/build \
&& cmake -DMUSL_STATIC=ON .. \
&& make
&& cmake -B /src/build -DMUSL_STATIC=ON \
&& cmake --build /src/build -j 10
# Stage 3: Output
FROM scratch as output
COPY --from=builder /src/build/umskt /umskt
COPY --from=builder /src/build/libumskt.a /umskt
COPY --from=builder /src/build/libumskt.so /umskt
# invoke via
# docker build -o type=tar,dest=umskt.tar .

View File

@ -84,21 +84,8 @@ RUN git clone https://github.com/gvanem/Watt-32.git watt32 \
&& make -f djgpp.mak \
&& ln -s /djgpp/watt32/lib/libwatt.a /djgpp/lib
# Stage 4: compile OpenSSL for djgpp-i386/watt32
FROM watt32 as openssl
WORKDIR /tmp
SHELL ["/bin/bash", "-c"]
RUN git clone https://github.com/openssl/openssl.git openssl \
&& cd openssl \
&& git checkout openssl-3.1.1 \
&& source /djgpp/setenv \
&& wget https://gist.github.com/Neo-Desktop/ad26e888d64b22a59c743ab4e21ac186/raw/c9a73e1eb75ba8857883ac5c08691d2fe5b82594/50-djgpp.conf.patch -O Configurations/50-djgpp.conf.patch \
&& git apply Configurations/50-djgpp.conf.patch \
&& ./Configure no-threads -DOPENSSL_DEV_NO_ATOMICS --prefix=/djgpp DJGPP \
&& make && make install
# Stage 5: compile UMSKT
FROM openssl as build
FROM watt32 as build
WORKDIR /src
COPY . /src

View File

View File

@ -463,17 +463,56 @@
"BINK": [
"2C",
"2D"
]
],
"DPC": {
"2C": [
{
"min": 5,
"max": 85,
"isEvaluation": false
},
{
"min": 337,
"max": 359,
"isEvaluation": false
},
{
"min": 755,
"max": 779,
"isEvaluation": false
},
{
"min": 785,
"max": 789,
"isEvaluation": false
}
],
"2D": [
{
"min": 119,
"max": 119,
"isEvaluation": false
},
{
"min": 120,
"max": 169,
"isEvaluation": false
},
{
"min": 400,
"max": 699,
"isEvaluation": false
},
{
"min": 170,
"max": 269,
"isEvaluation": false
}
]
}
},
"PROK": {
"name": "Professional K",
"BINK": [
"30",
"31"
]
},
"PROVLK": {
"name": "Professional VLK",
"VLK": {
"name": "Home/Professional VLK",
"BINK": [
"2E",
"2F"
@ -484,16 +523,6 @@
"min": 640,
"max": 699,
"isEvaluation": false
},
{
"min": 700,
"max": 701,
"isEvaluation": false
},
{
"min": 704,
"max": 705,
"isEvaluation": false
}
]
},
@ -673,6 +702,20 @@
}
]
}
},
"CNOEMHOME": {
"name": "[CN] OEM Home",
"BINK": [
"30",
"31"
]
},
"CNOEMPRO": {
"name": "[CN] OEM Professional",
"BINK": [
"32",
"33"
]
}
}
},

View File

@ -129,13 +129,13 @@ BOOL CLI::loadJSON(const fs::path &filename)
*
* @param pid
*/
void CLI::printID(DWORD *pid)
void CLI::printID(DWORD32 *pid)
{
char raw[12], b[6], c[8];
char i, digit = 0;
// Convert PID to ascii-number (=raw)
snprintf(raw, sizeof(raw), "%09lu", pid[0]);
snprintf(raw, sizeof(raw), "%09u", pid[0]);
// Make b-part {640-....}
_strncpy(b, 6, &raw[0], 3);
@ -160,8 +160,8 @@ void CLI::printID(DWORD *pid)
c[6] = digit + '0';
c[7] = 0;
DWORD binkid;
_sscanf(options.binkID.c_str(), "%lx", &binkid);
DWORD32 binkid;
_sscanf(&options.binkID[0], "%x", &binkid);
binkid /= 2;
fmt::print("> Product ID: PPPPP-{}-{}-{}xxx\n", b, c, binkid);
@ -172,7 +172,7 @@ void CLI::printID(DWORD *pid)
* @param pidgen3
* @return success
*/
BOOL CLI::InitPIDGEN3(PIDGEN3 &pidgen3)
BOOL CLI::InitPIDGEN3(PIDGEN3 *p3)
{
const char *BINKID = &options.binkID[0];
auto bink = keys["BINK"][BINKID];
@ -192,26 +192,26 @@ BOOL CLI::InitPIDGEN3(PIDGEN3 &pidgen3)
fmt::print("\n");
}
pidgen3.LoadEllipticCurve(bink["p"], bink["a"], bink["b"], bink["g"]["x"], bink["g"]["y"], bink["pub"]["x"],
bink["pub"]["y"], bink["n"], bink["priv"]);
p3->LoadEllipticCurve(bink["p"], bink["a"], bink["b"], bink["g"]["x"], bink["g"]["y"], bink["pub"]["x"],
bink["pub"]["y"], bink["n"], bink["priv"]);
if (options.state != STATE_PIDGEN_GENERATE)
{
return true;
}
pidgen3.info.setChannelID(options.channelID);
p3->info.setChannelID(options.channelID);
if (options.verbose)
{
fmt::print("> Channel ID: {:03d}\n", options.channelID);
fmt::print("> Channel ID: {:#03d}\n", options.channelID);
}
if (options.serialSet)
{
pidgen3.info.setSerial(options.serial);
p3->info.setSerial(options.serial);
if (options.verbose)
{
fmt::print("> Serial {:#09d}\n", options.serial);
fmt::print("> Serial {:#06d}\n", options.serial);
}
}
@ -225,11 +225,6 @@ BOOL CLI::InitPIDGEN3(PIDGEN3 &pidgen3)
*/
BOOL CLI::InitConfirmationID(ConfirmationID &confid)
{
auto ctx = BN_CTX_new();
BIGNUM *pkey = BN_CTX_get(ctx), *nonresidue = BN_CTX_get(ctx), *modulous = BN_CTX_get(ctx);
BIGNUM *fvals[6];
QWORD fvalsq[6];
if (!keys["products"][options.productCode].contains("meta") ||
!keys["products"][options.productCode]["meta"].contains("activation"))
{
@ -264,17 +259,10 @@ BOOL CLI::InitConfirmationID(ConfirmationID &confid)
fmt::print("\n");
}
for (BYTE i = 0; i < 6; i++)
{
fvals[i] = BN_CTX_get(ctx);
auto xval = flavour["x"][fmt::format("{}", i)].get<std::string>();
BN_dec2bn(&fvals[i], xval.c_str());
UMSKT::BN_bn2lebin(fvals[i], (unsigned char *)&fvalsq[i], sizeof(*fvalsq));
}
// confid.LoadHyperellipticCurve(fvals, );
BN_CTX_free(ctx);
confid.LoadHyperellipticCurve(flavour["x"]["0"], flavour["x"]["1"], flavour["x"]["2"], flavour["x"]["3"],
flavour["x"]["4"], flavour["x"]["5"], flavour["priv"], flavour["quotient"],
flavour["non_residue"], flavour["iid_key"], meta["tags"].contains("xpbrand"),
meta["tags"].contains("office"), meta["activation"]["version"]);
return false;
}
@ -282,7 +270,7 @@ BOOL CLI::InitConfirmationID(ConfirmationID &confid)
*
* @return success
*/
BOOL CLI::PIDGENGenerate()
BOOL CLI::PIDGenerate()
{
// TODO:
// if options.pidgen2generate
@ -295,34 +283,20 @@ BOOL CLI::PIDGENGenerate()
std::string key;
bink["p"].get_to(key);
if (PIDGEN3::checkFieldStrIsBink1998(key))
{
if (options.verbose)
{
fmt::print("Detected a BINK1998 key\n");
}
auto p3 = PIDGEN3::Factory(key);
InitPIDGEN3(p3);
auto bink1998 = BINK1998();
InitPIDGEN3(bink1998);
return BINK1998Generate(bink1998);
}
else
{
if (options.verbose)
{
fmt::print("Detected a BINK2002 key\n");
}
auto bink2002 = BINK2002();
InitPIDGEN3(bink2002);
return BINK2002Generate(bink2002);
}
auto retval = PIDGEN3Generate(p3);
delete p3;
return retval;
}
/**
*
* @return isValid
*/
BOOL CLI::PIDGENValidate()
BOOL CLI::PIDValidate()
{
// TODO:
// if options.pidgen2validate
@ -335,26 +309,12 @@ BOOL CLI::PIDGENValidate()
std::string key;
bink["p"].get_to(key);
if (PIDGEN3::checkFieldStrIsBink1998(key))
{
if (options.verbose)
{
fmt::print("Detected a BINK1998 key\n");
}
auto bink1998 = BINK1998();
InitPIDGEN3(bink1998);
return BINK1998Validate(bink1998);
}
else
{
if (options.verbose)
{
fmt::print("Detected a BINK2002 key\n");
}
auto bink2002 = BINK2002();
InitPIDGEN3(bink2002);
return BINK2002Validate(bink2002);
}
auto p3 = PIDGEN3::Factory(key);
InitPIDGEN3(p3);
auto retval = PIDGEN3Validate(p3);
delete p3;
return retval;
}
/**
@ -371,10 +331,10 @@ int CLI::Run()
switch (options.state)
{
case STATE_PIDGEN_GENERATE:
return PIDGENGenerate();
return PIDGenerate();
case STATE_PIDGEN_VALIDATE:
return PIDGENValidate();
return PIDValidate();
case STATE_CONFIRMATION_ID:
return ConfirmationIDGenerate();

View File

@ -23,6 +23,7 @@
#ifndef UMSKT_CLI_H
#define UMSKT_CLI_H
#include "libumskt/libumskt.h"
#include "typedefs.h"
#include <filesystem>
@ -96,9 +97,9 @@ struct Options
std::string productCode;
std::string productFlavour;
DWORD channelID;
DWORD serial;
DWORD numKeys;
DWORD32 channelID;
DWORD32 serial;
DWORD32 numKeys;
BOOL oem;
BOOL upgrade;
@ -128,7 +129,7 @@ struct Options
class CLI
{
std::string pKey;
DWORD count, total, iBinkID;
DWORD32 count, total, iBinkID;
static Options options;
@ -168,23 +169,21 @@ class CLI
static BOOL parseCommandLine();
static BOOL processOptions();
static void printID(DWORD *pid);
static void printID(DWORD32 *pid);
static void printKey(std::string &pk);
static BOOL stripKey(const std::string &in_key, std::string &out_key);
static std::string validateInputKeyCharset(std::string &accumulator, char currentChar);
BOOL InitPIDGEN3(PIDGEN3 &pidgen3);
BOOL InitPIDGEN3(PIDGEN3 *p3);
BOOL InitConfirmationID(ConfirmationID &confid);
BOOL PIDGENGenerate();
BOOL PIDGENValidate();
BOOL PIDGenerate();
BOOL PIDValidate();
BOOL PIDGEN2Generate(PIDGEN2 &pidgen2);
BOOL PIDGEN2Validate(PIDGEN2 &pidgen2);
BOOL BINK1998Generate(PIDGEN3 &pidgen3);
BOOL BINK1998Validate(PIDGEN3 &pidgen3);
BOOL BINK2002Generate(PIDGEN3 &pidgen3);
BOOL BINK2002Validate(PIDGEN3 &pidgen3);
BOOL PIDGEN2Generate(PIDGEN2 &p2);
BOOL PIDGEN2Validate(PIDGEN2 &p2);
BOOL PIDGEN3Generate(PIDGEN3 *p3);
BOOL PIDGEN3Validate(PIDGEN3 *p3);
BOOL ConfirmationIDGenerate();
INLINE static std::string strtolower(std::string &in)

View File

@ -46,39 +46,56 @@ BOOL CLI::PIDGEN2Validate(PIDGEN2 &pidgen2)
*
* @return success
*/
BOOL CLI::BINK1998Generate(PIDGEN3 &pidgen3)
BOOL CLI::PIDGEN3Generate(PIDGEN3 *p3)
{
// raw PID/serial value
DWORD nRaw = options.channelID * 1'000'000;
DWORD serialRnd;
DWORD32 nRaw = options.channelID * 1'000'000;
DWORD32 serialRnd;
if (options.serialSet)
if (p3->checkFieldIsBink1998())
{
// using user-provided serial
serialRnd = options.serial;
}
else
{
// generate a random number to use as a serial
serialRnd = UMSKT::getRandom<DWORD>();
if (options.serialSet)
{
// using user-provided serial
serialRnd = options.serial;
}
else
{
// generate a random number to use as a serial
serialRnd = UMSKT::getRandom<DWORD32>();
}
// make sure it's less than 999999
nRaw += (serialRnd % 999999);
if (options.verbose)
{
// print the resulting Product ID
// PID value is printed in BINK1998::Generate
printID(&nRaw);
}
}
// make sure it's less than 999999
nRaw += (serialRnd % 999999);
if (options.verbose)
for (DWORD32 i = 0; i < total; i++)
{
// print the resulting Product ID
// PID value is printed in BINK1998::Generate
printID(&nRaw);
}
if (!p3->checkFieldIsBink1998())
{
auto authvalue = UMSKT::getRandom<DWORD32>() & BITMASK(10);
p3->info.AuthInfo.Decode((BYTE *)&authvalue, sizeof(DWORD32));
for (int i = 0; i < total; i++)
{
pidgen3.info.setSerial(nRaw);
pidgen3.Generate(pKey);
if (options.verbose)
{
fmt::print("> AuthInfo: {:#08x}\n", p3->info.AuthInfo);
}
}
else
{
p3->info.setSerial(nRaw);
}
bool isValid = pidgen3.Validate(pKey);
p3->Generate(pKey);
bool isValid = p3->Validate(pKey);
if (isValid)
{
printKey(pKey);
@ -115,7 +132,7 @@ BOOL CLI::BINK1998Generate(PIDGEN3 &pidgen3)
*
* @return success
*/
BOOL CLI::BINK1998Validate(PIDGEN3 &bink1998)
BOOL CLI::PIDGEN3Validate(PIDGEN3 *p3)
{
std::string product_key;
@ -127,84 +144,7 @@ BOOL CLI::BINK1998Validate(PIDGEN3 &bink1998)
CLI::printKey(product_key);
fmt::print("\n");
if (!bink1998.Validate(product_key))
{
fmt::print("ERROR: Product key is invalid! Wrong BINK ID?\n");
return false;
}
fmt::print("Key validated successfully!\n");
return true;
}
/**
*
* @return success
*/
BOOL CLI::BINK2002Generate(PIDGEN3 &pidgen3)
{
// generate a key
for (int i = 0; i < total; i++)
{
pidgen3.info.AuthInfo = UMSKT::getRandom<DWORD>() & BITMASK(10);
if (options.verbose)
{
fmt::print("> AuthInfo: {:#08x}\n", pidgen3.info.AuthInfo);
}
pidgen3.Generate(pKey);
bool isValid = pidgen3.Validate(pKey);
if (isValid)
{
CLI::printKey(pKey);
if (i <= total - 1 || options.verbose)
{ // check if end of list or verbose
fmt::print("\n");
}
count += isValid; // add to count
}
else
{
if (options.verbose)
{
CLI::printKey(pKey); // print the key
fmt::print(" [Invalid]"); // and add " [Invalid]" to the key
if (i <= total - 1)
{ // check if end of list
fmt::print("\n");
}
}
total++; // queue a redo, basically
}
}
if (options.verbose)
{
fmt::print("\nSuccess count: {}/{}\n", count, total);
}
return true;
}
/**
*
* @return success
*/
BOOL CLI::BINK2002Validate(PIDGEN3 &pidgen3)
{
std::string product_key;
if (!CLI::stripKey(options.keyToCheck, product_key))
{
fmt::print("ERROR: Product key is in an incorrect format!\n");
return false;
}
CLI::printKey(product_key);
fmt::print("\n");
if (!pidgen3.Validate(product_key))
if (!p3->Validate(product_key))
{
fmt::print("ERROR: Product key is invalid! Wrong BINK ID?\n");
return false;
@ -228,7 +168,7 @@ BOOL CLI::ConfirmationIDGenerate()
return false;
}
DWORD err = confid.Generate(options.installationID, confirmation_id, options.productID);
DWORD32 err = confid.Generate(options.installationID, confirmation_id, options.productID);
if (err == SUCCESS)
{

View File

@ -89,7 +89,7 @@ void CLI::SetHelpText()
*/
BOOL CLI::parseCommandLine()
{
for (DWORD i = 1; i < options.argc; i++)
for (DWORD32 i = 1; i < options.argc; i++)
{
std::string arg = options.argv[i];
@ -436,7 +436,7 @@ BOOL CLI::SetOEMOption(int, char *)
{
if (options.verbose)
{
fmt::print("Setting oem option\n");
fmt::print("Setting OEM option\n");
}
options.oem = true;
return true;
@ -523,12 +523,13 @@ BOOL CLI::SetBINKOption(int count, char *bink)
BOOL CLI::SetFlavourOption(int count, char *flavour)
{
auto strflavour = std::string(flavour);
options.productFlavour = strtoupper(strflavour);
if (options.verbose)
{
fmt::print("Setting flavour option to {}\n", flavour);
fmt::print("Setting flavour option to {}\n", strflavour);
}
options.productFlavour = flavour;
return true;
}
@ -584,12 +585,12 @@ BOOL CLI::SetValidateOption(int count, char *productID)
BOOL CLI::SetProductCodeOption(int, char *product)
{
if (options.verbose)
{
fmt::print("Setting product code to {}\n", product);
}
auto strProduct = std::string(product);
options.productCode = strtoupper(strProduct);
if (options.verbose)
{
fmt::print("Setting product code to {}\n", strProduct);
}
return true;
}

View File

@ -38,7 +38,7 @@
* @param x4
* @param x5
* @param priv
* @param modulous
* @param modulus
* @param nonresidue
* @param isOffice
* @param isXPBrand
@ -46,58 +46,74 @@
* @return
*/
BOOL ConfirmationID::LoadHyperellipticCurve(QWORD x0, QWORD x1, QWORD x2, QWORD x3, QWORD x4, QWORD x5, Q_OWORD priv,
QWORD modulous, QWORD nonresidue, BOOL isOffice, BOOL isXPBrand,
BYTE flagVersion)
QWORD modulus, QWORD nonresidue, DWORD32 iidkey, BOOL isOffice,
BOOL isXPBrand, BYTE flagVersion)
{
curve[0] = x0;
curve[1] = x1;
curve[2] = x2;
curve[3] = x3;
curve[4] = x4;
curve[5] = x5;
QWORD fvals[6] = {x0, x1, x2, x3, x4, x5};
memcpy(&privateKey, &priv, sizeof(Q_OWORD));
MOD = modulous;
NON_RESIDUE = nonresidue;
this->isOffice = isOffice;
this->isXPBrand = isXPBrand;
this->flagVersion = flagVersion;
return true;
return LoadHyperellipticCurve(fvals, priv, modulus, nonresidue, iidkey, isOffice, isXPBrand, flagVersion);
}
/**
*
* @param f
* @param priv
* @param modulous
* @param modulus
* @param nonresidue
* @param isOffice
* @param isXPBrand
* @param flagVersion
* @return
*/
BOOL ConfirmationID::LoadHyperellipticCurve(QWORD *f, Q_OWORD priv, QWORD modulous, QWORD nonresidue, BOOL isOffice,
BOOL isXPBrand, BYTE flagVersion)
BOOL ConfirmationID::LoadHyperellipticCurve(QWORD *f, Q_OWORD priv, QWORD modulus, QWORD nonresidue, DWORD32 iidkey,
BOOL isOffice, BOOL isXPBrand, BYTE flagVersion)
{
memcpy(&curve, f, sizeof(curve));
memcpy(&privateKey, &priv, sizeof(Q_OWORD));
MOD = modulous;
MOD = modulus;
NON_RESIDUE = nonresidue;
this->isOffice = isOffice;
this->isXPBrand = isXPBrand;
this->flagVersion = flagVersion;
return true;
}
BOOL LoadHyperellipticCurve(const std::string &x0, const std::string &x1, const std::string &x2, const std::string &x3,
const std::string &x4, const std::string &x5, const std::string &priv,
const std::string &modulous, const std::string &nonresidue, BOOL isOffice, BOOL isXPBrand,
BYTE flagVersion)
BOOL ConfirmationID::LoadHyperellipticCurve(const std::string &x0, const std::string &x1, const std::string &x2,
const std::string &x3, const std::string &x4, const std::string &x5,
const std::string &priv, const std::string &modulus,
const std::string &nonresidue, const std::string &iidkey, BOOL isOffice,
BOOL isXPBrand, BYTE flagVersion)
{
std::string f[6];
f[0] = x0;
f[1] = x1;
f[2] = x2;
f[3] = x3;
f[4] = x4;
f[5] = x5;
return LoadHyperellipticCurve(f, priv, modulus, nonresidue, iidkey, isOffice, isXPBrand, flagVersion);
}
BOOL ConfirmationID::LoadHyperellipticCurve(const std::string *f, const std::string &priv, const std::string &modulus,
const std::string &nonresidue, const std::string &iidkey, BOOL isOffice,
BOOL isXPBrand, BYTE flagVersion)
{
for (int i = 0; i < 6; i++)
{
Integer(&f[i][0]).Encode((BYTE *)curve[i], sizeof(QWORD));
}
Integer(&priv[0]).Encode(privateKey.byte, sizeof(Q_OWORD));
Integer(&modulus[0]).Encode((BYTE *)&MOD, sizeof(QWORD));
Integer(&nonresidue[0]).Encode((BYTE *)NON_RESIDUE, sizeof(QWORD));
this->isOffice = isOffice;
this->isXPBrand = isXPBrand;
this->flagVersion = flagVersion;
return true;
}
@ -107,9 +123,9 @@ BOOL LoadHyperellipticCurve(const std::string &x0, const std::string &x1, const
* @param pid
* @return
*/
DWORD ConfirmationID::calculateCheckDigit(DWORD pid)
DWORD32 ConfirmationID::calculateCheckDigit(DWORD32 pid)
{
DWORD i = 0, j = 0, k = 0;
DWORD32 i = 0, j = 0, k = 0;
for (j = pid; j; i += k)
{
k = j % 10;
@ -125,7 +141,7 @@ DWORD ConfirmationID::calculateCheckDigit(DWORD pid)
* @param hwid
* @param version
*/
void ConfirmationID::decode_iid_new_version(BYTE *iid, BYTE *hwid, DWORD *version)
void ConfirmationID::decode_iid_new_version(BYTE *iid, BYTE *hwid, DWORD32 *version)
{
QWORD buffer[5];
for (BYTE i = 0; i < 5; i++)
@ -133,8 +149,8 @@ void ConfirmationID::decode_iid_new_version(BYTE *iid, BYTE *hwid, DWORD *versio
memcpy(&buffer[i], (iid + (4 * i)), 4);
}
DWORD v1 = (buffer[3] & 0xFFFFFFF8) | 2;
DWORD v2 = ((buffer[3] & 7) << 29) | (buffer[2] >> 3);
DWORD32 v1 = (buffer[3] & 0xFFFFFFF8) | 2;
DWORD32 v2 = ((buffer[3] & 7) << 29) | (buffer[2] >> 3);
QWORD hardwareIDVal = ((QWORD)v1 << 32) | v2;
for (BYTE i = 0; i < 8; ++i)
{
@ -153,8 +169,9 @@ void ConfirmationID::decode_iid_new_version(BYTE *iid, BYTE *hwid, DWORD *versio
*/
void ConfirmationID::Mix(BYTE *buffer, BYTE bufSize, const BYTE *key, BYTE keySize)
{
BYTE sha1_input[64], sha1_result[20];
BYTE sha1_input[64], sha1_result[SHA::DIGESTSIZE];
BYTE half = bufSize / 2;
auto digest = SHA();
// assert(half <= sizeof(sha1_result) && half + keySize <= sizeof(sha1_input) - 9);
for (BYTE external_counter = 0; external_counter < 4; external_counter++)
@ -181,7 +198,8 @@ void ConfirmationID::Mix(BYTE *buffer, BYTE bufSize, const BYTE *key, BYTE keySi
sha1_input[sizeof(sha1_input) - 2] = (1 + half + keySize) * 8 / 0x100;
}
SHA1(sha1_input, sizeof(sha1_input), sha1_result);
digest.Update(sha1_input, sizeof(sha1_input));
digest.Final(sha1_result);
for (BYTE i = half & ~3; i < half; i++)
{
@ -206,8 +224,9 @@ void ConfirmationID::Mix(BYTE *buffer, BYTE bufSize, const BYTE *key, BYTE keySi
*/
void ConfirmationID::Unmix(BYTE *buffer, BYTE bufSize, const BYTE key[4], BYTE keySize)
{
BYTE sha1_input[64], sha1_result[20];
BYTE sha1_input[64], sha1_result[SHA::DIGESTSIZE];
BYTE half = bufSize / 2;
auto digest = SHA();
// assert(half <= sizeof(sha1_result) && half + keySize <= sizeof(sha1_input) - 9);
for (BYTE external_counter = 0; external_counter < 4; external_counter++)
@ -232,7 +251,8 @@ void ConfirmationID::Unmix(BYTE *buffer, BYTE bufSize, const BYTE key[4], BYTE k
sha1_input[sizeof(sha1_input) - 2] = (1 + half + keySize) * 8 / 0x100;
}
SHA1(sha1_input, sizeof(sha1_input), sha1_result);
digest.Update(sha1_input, sizeof(sha1_input));
digest.Final(sha1_result);
for (BYTE i = half & ~3; i < half; i++)
{
@ -258,13 +278,13 @@ void ConfirmationID::Unmix(BYTE *buffer, BYTE bufSize, const BYTE key[4], BYTE k
CONFIRMATION_ID_STATUS ConfirmationID::Generate(const std::string &installationIDIn, std::string &confirmationIDOut,
std::string &productIDIn)
{
DWORD version;
DWORD32 version;
BYTE hardwareID[8];
BYTE installation_id[19]; // 10**45 < 256**19
BYTE productID[4];
BYTE installation_id_len = 0;
auto pid = installationIDIn.c_str();
auto pid = &installationIDIn[0];
BYTE count = 0, totalCount = 0;
unsigned check = 0;
@ -441,8 +461,8 @@ CONFIRMATION_ID_STATUS ConfirmationID::Generate(const std::string &installationI
QWORD x1 = ulowhi.qword[0] - x2 * MOD;
x2++;
d.u[0] = residue->sub(residue->mul(x1, x1), residue->mul(NON_RESIDUE, residue->mul(x2, x2)));
d.u[1] = residue->add(x1, x1);
d.u.qword[0] = residue->sub(residue->mul(x1, x1), residue->mul(NON_RESIDUE, residue->mul(x2, x2)));
d.u.qword[1] = residue->add(x1, x1);
if (divisor->find_divisor_v(&d))
{
break;
@ -462,23 +482,23 @@ CONFIRMATION_ID_STATUS ConfirmationID::Generate(const std::string &installationI
Q_OWORD e;
memset(&e, 0, sizeof(e));
if (d.u[0] == BAD)
if (d.u.qword[0] == BAD)
{
// we can not get the zero divisor, actually...
e.qword[0] = residue->__umul128(MOD + 2, MOD, &e.qword[1]);
}
else if (d.u[1] == BAD)
else if (d.u.qword[1] == BAD)
{
// O(1/MOD) chance
// encoded = (unsigned __int128)(MOD + 1) * d.u[0] + MOD; // * MOD + d.u[0] is fine too
e.qword[0] = residue->__umul128(MOD + 1, d.u[0], &e.qword[1]);
e.qword[0] = residue->__umul128(MOD + 1, d.u.qword[0], &e.qword[1]);
e.qword[0] += MOD;
e.qword[1] += (e.qword[0] < MOD);
}
else
{
QWORD x1 = (d.u[1] % 2 ? d.u[1] + MOD : d.u[1]) / 2;
QWORD x2sqr = residue->sub(residue->mul(x1, x1), d.u[0]);
QWORD x1 = (d.u.qword[1] % 2 ? d.u.qword[1] + MOD : d.u.qword[1]) / 2;
QWORD x2sqr = residue->sub(residue->mul(x1, x1), d.u.qword[0]);
QWORD x2 = residue->sqrt(x2sqr);
if (x2 == BAD)
@ -493,9 +513,9 @@ CONFIRMATION_ID_STATUS ConfirmationID::Generate(const std::string &installationI
{
// points (-x1+x2, v(-x1+x2)) and (-x1-x2, v(-x1-x2))
QWORD x1a = residue->sub(x1, x2);
QWORD y1 = residue->sub(d.v[0], residue->mul(d.v[1], x1a));
QWORD y1 = residue->sub(d.v.qword[0], residue->mul(d.v.qword[1], x1a));
QWORD x2a = residue->add(x1, x2);
QWORD y2 = residue->sub(d.v[0], residue->mul(d.v[1], x2a));
QWORD y2 = residue->sub(d.v.qword[0], residue->mul(d.v.qword[1], x2a));
if (x1a > x2a)
{
QWORD tmp = x1a;

View File

@ -41,8 +41,7 @@ enum CONFIRMATION_ID_STATUS
typedef struct
{
QWORD u[2];
QWORD v[2];
Q_OWORD u, v;
} TDivisor;
class EXPORT ConfirmationID
@ -55,8 +54,8 @@ class EXPORT ConfirmationID
BOOL isOffice = false, isXPBrand = false;
unsigned flagVersion = 0;
DWORD calculateCheckDigit(DWORD pid);
void decode_iid_new_version(BYTE *iid, BYTE *hwid, DWORD *version);
DWORD32 calculateCheckDigit(DWORD32 pid);
void decode_iid_new_version(BYTE *iid, BYTE *hwid, DWORD32 *version);
void Mix(BYTE *buffer, BYTE bufSize, const BYTE *key, BYTE keySize);
void Unmix(BYTE *buffer, BYTE bufSize, const BYTE *key, BYTE keySize);
@ -127,26 +126,31 @@ class EXPORT ConfirmationID
residue = new Residue(this);
polynomial = new Polynomial(this);
divisor = new Divisor(this);
privateKey.qword[0] = privateKey.qword[1] = 0x00;
}
BOOL LoadHyperellipticCurve(const std::string *f, const std::string &priv, const std::string &modulus,
const std::string &nonresidue, const std::string &iidkey, BOOL isOffice, BOOL isXPBrand,
BYTE flagVersion);
BOOL LoadHyperellipticCurve(const std::string &x0, const std::string &x1, const std::string &x2,
const std::string &x3, const std::string &x4, const std::string &x5,
const std::string &priv, const std::string &modulous, const std::string &nonresidue,
BOOL isOffice, BOOL isXPBrand, BYTE flagVersion);
const std::string &priv, const std::string &modulus, const std::string &nonresidue,
const std::string &iidkey, BOOL isOffice, BOOL isXPBrand, BYTE flagVersion);
BOOL LoadHyperellipticCurve(QWORD x0, QWORD x1, QWORD x2, QWORD x3, QWORD x4, QWORD x5, Q_OWORD priv,
QWORD modulous, QWORD nonresidue, BOOL isOffice, BOOL isXPBrand, BYTE flagVersion);
BOOL LoadHyperellipticCurve(QWORD x0, QWORD x1, QWORD x2, QWORD x3, QWORD x4, QWORD x5, Q_OWORD priv, QWORD modulus,
QWORD nonresidue, DWORD32 iidkey, BOOL isOffice, BOOL isXPBrand, BYTE flagVersion);
BOOL LoadHyperellipticCurve(QWORD *f, Q_OWORD priv, QWORD modulous, QWORD nonresidue, BOOL isOffice, BOOL isXPBrand,
BYTE flagVersion);
BOOL LoadHyperellipticCurve(QWORD *f, Q_OWORD priv, QWORD modulus, QWORD nonresidue, DWORD32 iidkey, BOOL isOffice,
BOOL isXPBrand, BYTE flagVersion);
CONFIRMATION_ID_STATUS Generate(const std::string &installation_id_str, std::string &confirmation_id,
std::string &productid);
~ConfirmationID()
{
delete residue, polynomial, divisor;
delete residue;
delete polynomial;
delete divisor;
}
};

View File

@ -39,7 +39,7 @@ int ConfirmationID::ConfirmationID::Divisor::find_divisor_v(TDivisor *d)
f2[i] = parent->curve[i];
}
const QWORD u0 = d->u[0], u1 = d->u[1];
const QWORD u0 = d->u.qword[0], u1 = d->u.qword[1];
for (BYTE j = 4; j--;)
{
f2[j] = parent->residue->sub(f2[j], parent->residue->mul(u0, f2[j + 2]));
@ -106,8 +106,8 @@ int ConfirmationID::ConfirmationID::Divisor::find_divisor_v(TDivisor *d)
QWORD v0 = parent->residue->mul(parent->residue->add(f1, parent->residue->mul(u1, parent->residue->mul(v1, v1))),
parent->residue->inv(parent->residue->add(v1, v1)));
d->v[0] = v0;
d->v[1] = v1;
d->v.qword[0] = v0;
d->v.qword[1] = v1;
return 1;
}
@ -121,24 +121,24 @@ int ConfirmationID::ConfirmationID::Divisor::find_divisor_v(TDivisor *d)
*/
int ConfirmationID::ConfirmationID::Divisor::u2poly(const TDivisor *src, QWORD polyu[3], QWORD polyv[2])
{
if (src->u[1] != BAD)
if (src->u.qword[1] != BAD)
{
polyu[0] = src->u[0];
polyu[1] = src->u[1];
polyu[0] = src->u.qword[0];
polyu[1] = src->u.qword[1];
polyu[2] = 1;
polyv[0] = src->v[0];
polyv[1] = src->v[1];
polyv[0] = src->v.qword[0];
polyv[1] = src->v.qword[1];
return 2;
}
if (src->u[0] != BAD)
if (src->u.qword[0] != BAD)
{
polyu[0] = src->u[0];
polyu[0] = src->u.qword[0];
polyu[1] = 1;
polyv[0] = src->v[0];
polyv[0] = src->v.qword[0];
polyv[1] = 0;
return 1;
@ -237,7 +237,7 @@ void ConfirmationID::Divisor::add(const TDivisor *src1, const TDivisor *src2, TD
}
}
vdeg = parent->polynomial->div_monic(vdeg, v, udeg, u, NULL);
vdeg = parent->polynomial->div_monic(vdeg, v, udeg, u, nullptr);
while (udeg > 2)
{
@ -279,30 +279,30 @@ void ConfirmationID::Divisor::add(const TDivisor *src1, const TDivisor *src2, TD
v[i] = parent->residue->sub(0, v[i]);
}
vdeg = parent->polynomial->div_monic(vdeg, v, udeg, u, NULL);
vdeg = parent->polynomial->div_monic(vdeg, v, udeg, u, nullptr);
}
if (udeg == 2)
{
dst->u[0] = u[0];
dst->u[1] = u[1];
dst->v[0] = (vdeg >= 0 ? v[0] : 0);
dst->v[1] = (vdeg >= 1 ? v[1] : 0);
dst->u.qword[0] = u[0];
dst->u.qword[1] = u[1];
dst->v.qword[0] = (vdeg >= 0 ? v[0] : 0);
dst->v.qword[1] = (vdeg >= 1 ? v[1] : 0);
}
else if (udeg == 1)
{
dst->u[0] = u[0];
dst->u[1] = BAD;
dst->v[0] = (vdeg >= 0 ? v[0] : 0);
dst->v[1] = BAD;
dst->u.qword[0] = u[0];
dst->u.qword[1] = BAD;
dst->v.qword[0] = (vdeg >= 0 ? v[0] : 0);
dst->v.qword[1] = BAD;
}
else
{
assert(udeg == 0);
dst->u[0] = BAD;
dst->u[1] = BAD;
dst->v[0] = BAD;
dst->v[1] = BAD;
dst->u.qword[0] = BAD;
dst->u.qword[1] = BAD;
dst->v.qword[0] = BAD;
dst->v.qword[1] = BAD;
}
}
@ -318,10 +318,10 @@ void ConfirmationID::Divisor::mul(const TDivisor *src, QWORD mult, TDivisor *dst
{
if (mult == 0)
{
dst->u[0] = BAD;
dst->u[1] = BAD;
dst->v[0] = BAD;
dst->v[1] = BAD;
dst->u.qword[0] = BAD;
dst->u.qword[1] = BAD;
dst->v.qword[0] = BAD;
dst->v.qword[1] = BAD;
return;
}
@ -354,10 +354,10 @@ void ConfirmationID::Divisor::mul128(const TDivisor *src, QWORD mult_lo, QWORD m
{
if (mult_lo == 0 && mult_hi == 0)
{
dst->u[0] = BAD;
dst->u[1] = BAD;
dst->v[0] = BAD;
dst->v[1] = BAD;
dst->u.qword[0] = BAD;
dst->u.qword[1] = BAD;
dst->v.qword[0] = BAD;
dst->v.qword[1] = BAD;
return;
}

View File

@ -22,17 +22,7 @@
#include "libumskt.h"
#ifdef _WIN32
// this seems janky but it works, and doesn't use storage that would otherwise get clobbered
std::FILE *getFileStreamToNul()
{
fopen_s(&UMSKT::debug, "nul", "w");
return UMSKT::debug;
}
std::FILE *UMSKT::debug = getFileStreamToNul();
#else
std::FILE *UMSKT::debug = std::fopen("/dev/null", "w");
#endif
std::FILE *UMSKT::debug;
BOOL UMSKT::VERBOSE = false;
BOOL UMSKT::DEBUG = false;

View File

@ -28,10 +28,10 @@
#include "pidgen3/PIDGEN3.h"
std::map<UMSKT_TAG, UMSKT_Value> UMSKT::tags;
CryptoPP::DefaultAutoSeededRNG UMSKT::rng;
extern "C"
{
/**
* Sets debug output to a given C++ File stream
* if the memory allocated at filestream is "STDOUT" or "STDERR"
@ -195,7 +195,7 @@ extern "C"
std::string str;
BOOL retval = p3->Generate(str);
assert(pKeySizeIn >= str.length() + 1);
assert(pKeySizeIn >= str.length() + NULL_TERMINATOR);
memcpy(pKeyOut, &str[0], str.length());
pKeyOut[str.length()] = 0;
@ -245,48 +245,3 @@ extern "C"
}
} // extern "C"
/**
* Convert data between endianness types.
*
* @param data [in]
* @param length [in]
**/
void UMSKT::endian(BYTE *data, int length)
{
for (int i = 0; i < length / 2; i++)
{
BYTE temp = data[i];
data[i] = data[length - i - 1];
data[length - i - 1] = temp;
}
}
/**
* Converts an OpenSSL BigNumber to it's Little Endian binary equivalent
*
* @param a [in] BigNumber to convert
* @param to [out] char* binary representation
* @param tolen [in] length of the char* array
*
* @return length of number in to
**/
int UMSKT::BN_bn2lebin(const BIGNUM *a, unsigned char *to, int tolen)
{
if (a == nullptr || to == nullptr)
{
return 0;
}
int len = BN_bn2bin(a, to);
if (len > tolen)
{
return -1;
}
// Choke point inside BN_bn2lebinpad: OpenSSL uses len instead of tolen.
endian(to, tolen);
return len;
}

View File

@ -29,34 +29,68 @@
#include <sstream>
#include <string>
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include <cryptopp/cryptlib.h>
#include <cryptopp/ecp.h>
#include <cryptopp/filters.h>
#include <cryptopp/hex.h>
#include <cryptopp/integer.h>
#include <cryptopp/nbtheory.h>
#include <cryptopp/osrng.h>
#include <cryptopp/randpool.h>
#include <cryptopp/rng.h>
#include <cryptopp/sha.h>
using Integer = CryptoPP::Integer;
using ECP = CryptoPP::ECP;
using SHA = CryptoPP::SHA1;
#include <fmt/core.h>
#include <fmt/format.h>
#include <fmt/ostream.h>
class HexInteger : public Integer
{
};
// fmt <-> CryptoPP linkage
template <> struct fmt::formatter<HexInteger> : fmt::formatter<std::string_view>
{
auto format(const HexInteger &i, format_context &ctx) const
{
size_t size = i.MinEncodedSize();
CryptoPP::SecByteBlock encoded;
encoded.resize(size);
i.Encode(encoded, size);
std::string hexString;
CryptoPP::HexEncoder encoder(new CryptoPP::StringSink(hexString), false);
encoder.Put(encoded, size);
encoder.MessageEnd();
return fmt::formatter<std::string_view>::format(hexString, ctx);
}
};
template <> struct fmt::formatter<Integer> : ostream_formatter
{
auto format(const Integer &i, format_context &ctx) const
{
return basic_ostream_formatter<char>::format(i, ctx);
}
};
// Algorithm macros
#define PK_LENGTH 25
#define NULL_TERMINATOR 1
#define FIELD_BITS 384
#define FIELD_BYTES 48
#define FIELD_BITS_2003 512
#define FIELD_BYTES_2003 64
#define SHA_MSG_LENGTH_XP (4 + 2 * FIELD_BYTES)
#define SHA_MSG_LENGTH_2003 (3 + 2 * FIELD_BYTES_2003)
#define NEXTSNBITS(field, n, offset) (((QWORD)(field) >> (offset)) & ((1ULL << (n)) - 1))
#define FIRSTNBITS(field, n) NEXTSNBITS((field), (n), 0)
#define HIBYTES(field, bytes) NEXTSNBITS((QWORD)(field), ((bytes) * 8), ((bytes) * 8))
#define LOBYTES(field, bytes) FIRSTNBITS((QWORD)(field), ((bytes) * 8))
#define BYDWORD(n) (DWORD)(*((n) + 0) | *((n) + 1) << 8 | *((n) + 2) << 16 | *((n) + 3) << 24)
#define BYDWORD(n) (DWORD32)(*((n) + 0) | *((n) + 1) << 8 | *((n) + 2) << 16 | *((n) + 3) << 24)
#define BITMASK(n) ((1ULL << (n)) - 1)
#ifndef LIBUMSKT_VERSION_STRING
@ -79,7 +113,7 @@ struct UMSKT_Value
union {
BOOL boolean;
WORD word;
DWORD dword;
DWORD32 dword;
QWORD qword;
OWORD oword;
char *chars;
@ -107,10 +141,7 @@ class EXPORT UMSKT
static BOOL VERBOSE;
static BOOL DEBUG;
static std::map<UMSKT_TAG, UMSKT_Value> tags;
// Hello OpenSSL developers, please tell me, where is this function at?
static int BN_bn2lebin(const BIGNUM *a, unsigned char *to, int tolen);
static void endian(BYTE *data, int length);
static CryptoPP::DefaultAutoSeededRNG rng;
static void DESTRUCT()
{
@ -126,7 +157,7 @@ class EXPORT UMSKT
template <typename T> static T getRandom()
{
T retval;
RAND_bytes((BYTE *)&retval, sizeof(retval));
rng.GenerateBlock((BYTE *)&retval, sizeof(retval));
return retval;
}

View File

@ -199,10 +199,10 @@ int PIDGEN2::GenerateOEM(char *year, char *day, char *oem, char *&keyout)
if (!isValidDay(day))
{
auto iday = UMSKT::getRandom<int>();
iday = (iday + 1) % 365;
iday = (iday + NULL_TERMINATOR) % 365;
}
_strncpy(keyout, 32, &fmt::format("{}{}-OEM-{}-{}", year, day, oem, oem).c_str()[0], 32);
_strncpy(keyout, 32, fmt::format("{}{}-OEM-{}-{}", year, day, oem, oem).c_str(), 32);
return 0;
}

View File

@ -27,8 +27,8 @@
class EXPORT PIDGEN2
{
DWORD year;
DWORD day;
DWORD32 year;
DWORD32 day;
BOOL isOEM;
BOOL isOffice;

View File

@ -34,15 +34,20 @@
*
* @param pRaw [in] *QWORD[2] raw product key input
**/
BOOL BINK1998::Pack(QWORD *pRaw)
BOOL BINK1998::Pack(Q_OWORD *pRaw)
{
// The quantity of information the key provides is 114 bits.
// We're storing it in 2 64-bit quad-words with 14 trailing bits.
// 64 * 2 = 128
// Signature [114..59] <- Hash [58..31] <- Serial [30..1] <- Upgrade [0]
pRaw[0] = FIRSTNBITS(info.Signature, 5) << 59 | FIRSTNBITS(info.Hash, 28) << 31 | info.Serial << 1 | info.isUpgrade;
pRaw[1] = NEXTSNBITS(info.Signature, 51, 5);
Integer raw;
raw += info.Signature << 59;
raw += (info.Hash & BITMASK(28)) << 31;
raw += info.Serial << 1;
raw += info.isUpgrade;
raw.Encode(pRaw->byte, sizeof(Q_OWORD));
return true;
}
@ -52,22 +57,22 @@ BOOL BINK1998::Pack(QWORD *pRaw)
*
* @param pRaw [out] *QWORD[2] raw product key output
**/
BOOL BINK1998::Unpack(QWORD *pRaw)
BOOL BINK1998::Unpack(Q_OWORD *pRaw)
{
// We're assuming that the quantity of information within the product key is at most 114 bits.
// log2(24^25) = 114.
// Upgrade = Bit 0
info.isUpgrade = FIRSTNBITS(pRaw[0], 1);
info.isUpgrade = FIRSTNBITS(pRaw->qword[0], 1);
// Serial = Bits [1..30] -> 30 bits
info.Serial = NEXTSNBITS(pRaw[0], 30, 1);
info.Serial = NEXTSNBITS(pRaw->qword[0], 30, 1);
// Hash = Bits [31..58] -> 28 bits
info.Hash = NEXTSNBITS(pRaw[0], 28, 31);
info.Hash = NEXTSNBITS(pRaw->qword[0], 28, 31);
// Signature = Bits [59..113] -> 56 bits
info.Signature = FIRSTNBITS(pRaw[1], 51) << 5 | NEXTSNBITS(pRaw[0], 5, 59);
info.Signature = FIRSTNBITS(pRaw->qword[1], 51) << 5 | NEXTSNBITS(pRaw->qword[0], 5, 59);
return true;
}
@ -81,51 +86,55 @@ BOOL BINK1998::Unpack(QWORD *pRaw)
*/
BOOL BINK1998::Generate(std::string &pKey)
{
BN_CTX *numContext = BN_CTX_new();
Integer c, s;
BIGNUM *c = BN_CTX_get(numContext), *s = BN_CTX_get(numContext), *x = BN_CTX_get(numContext),
*y = BN_CTX_get(numContext);
QWORD pRaw[2];
Q_OWORD pRaw;
// Data segment of the RPK.
DWORD pData = info.Serial << 1 | info.isUpgrade;
Integer pData = info.Serial << 1 | info.isUpgrade;
// prepare the private key for generation
BN_sub(privateKey, genOrder, privateKey);
privateKey -= genOrder;
Integer limit;
limit.SetBit(55);
limit -= 1;
do
{
EC_POINT *r = EC_POINT_new(eCurve);
ECP::Point R;
// Generate a random number c consisting of 384 bits without any constraints.
BN_rand(c, FIELD_BITS, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY);
c.Randomize(UMSKT::rng, FieldBits);
// Pick a random derivative of the base point on the elliptic curve.
// R = cG;
EC_POINT_mul(eCurve, r, nullptr, genPoint, c, numContext);
R = eCurve.Multiply(c, genPoint);
// Acquire its coordinates.
// x = R.x; y = R.y;
EC_POINT_get_affine_coordinates(eCurve, r, x, y, numContext);
BYTE msgDigest[SHA_DIGEST_LENGTH], msgBuffer[SHA_MSG_LENGTH_XP];
BYTE xBin[FIELD_BYTES], yBin[FIELD_BYTES];
// Convert coordinates to bytes.
UMSKT::BN_bn2lebin(x, xBin, FIELD_BYTES);
UMSKT::BN_bn2lebin(y, yBin, FIELD_BYTES);
BYTE msgDigest[SHA::DIGESTSIZE], msgBuffer[SHAMessageLength], *pMsgBuffer = msgBuffer;
// Assemble the SHA message.
memcpy(&msgBuffer[0], &pData, 4);
memcpy(&msgBuffer[4], xBin, FIELD_BYTES);
memcpy(&msgBuffer[4 + FIELD_BYTES], yBin, FIELD_BYTES);
pData.Encode((CryptoPP::byte *)msgBuffer, 4);
pMsgBuffer += 4;
R.x.Encode(pMsgBuffer, FieldBytes);
pMsgBuffer += FieldBytes;
R.y.Encode(pMsgBuffer, FieldBytes);
pMsgBuffer += FieldBytes;
// pHash = SHA1(pSerial || R.x || R.y)
SHA1(msgBuffer, SHA_MSG_LENGTH_XP, msgDigest);
auto digest = SHA();
digest.Update(msgBuffer, SHAMessageLength);
digest.Final(msgDigest);
// Translate the byte digest into a 32-bit integer - this is our computed pHash.
// Truncate the pHash to 28 bits.
info.Hash.Decode(msgDigest, SHAMessageLength);
info.Hash = BYDWORD(msgDigest) >> 4 & BITMASK(28);
/*
@ -148,37 +157,33 @@ BOOL BINK1998::Generate(std::string &pKey)
*/
// s = ek;
BN_copy(s, privateKey);
BN_mul_word(s, info.Hash);
s = privateKey * info.Hash;
// s += c (mod n)
BN_mod_add(s, s, c, genOrder, numContext);
s = s + c % genOrder;
// Translate resulting scalar into a 64-bit integer (the byte order is little-endian).
BN_bn2lebinpad(s, (BYTE *)&info.Signature, BN_num_bytes(s));
info.Signature = s;
// Pack product key.
Pack(pRaw);
Pack(&pRaw);
auto serial = fmt::format("{:d}", info.Serial);
fmt::print(UMSKT::debug, "Generation results:\n");
fmt::print(UMSKT::debug, "{:>10}: {:b}\n", "Upgrade", (bool)info.isUpgrade);
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Upgrade", (bool)info.isUpgrade);
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Channel ID", serial.substr(0, 3));
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Sequence", serial.substr(3));
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Hash", info.Hash);
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Signature", info.Signature);
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Hash", info.Hash);
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Signature", info.Signature);
fmt::print(UMSKT::debug, "\n");
EC_POINT_free(r);
} while (info.Signature > BITMASK(55));
} while (info.Signature > limit);
// ↑ ↑ ↑
// The signature can't be longer than 55 bits, else it will
// make the CD-key longer than 25 characters.
// Convert bytecode to Base24 CD-key.
base24(pKey, (BYTE *)pRaw);
BN_CTX_free(numContext);
base24(pKey, pRaw.byte);
return true;
}
@ -197,26 +202,24 @@ BOOL BINK1998::Validate(std::string &pKey)
return false;
}
BN_CTX *numContext = BN_CTX_new();
QWORD pRaw[2];
Q_OWORD pRaw;
// Convert Base24 CD-key to bytecode.
unbase24((BYTE *)pRaw, pKey);
unbase24(pRaw.byte, pKey);
// Extract RPK, hash and signature from bytecode.
Unpack(pRaw);
Unpack(&pRaw);
auto serial = fmt::format("{:d}", info.Serial);
fmt::print(UMSKT::debug, "Validation results:\n");
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Upgrade", (bool)info.isUpgrade);
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Channel ID", serial.substr(0, 3));
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Sequence", serial.substr(3));
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Hash", info.Hash);
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Signature", info.Signature);
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Hash", info.Hash);
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Signature", info.Signature);
fmt::print(UMSKT::debug, "\n");
DWORD pData = info.Serial << 1 | info.isUpgrade;
Integer pData = info.Serial << 1 | info.isUpgrade;
/*
*
@ -233,51 +236,43 @@ BOOL BINK1998::Validate(std::string &pKey)
*
*/
BIGNUM *e = BN_lebin2bn((BYTE *)&info.Hash, sizeof(info.Hash), nullptr),
*s = BN_lebin2bn((BYTE *)&info.Signature, sizeof(info.Signature), nullptr);
BIGNUM *x = BN_CTX_get(numContext), *y = BN_CTX_get(numContext);
Integer e = info.Hash, s;
s.Decode((BYTE *)&info.Signature, sizeof(info.Signature));
// Create 2 points on the elliptic curve.
EC_POINT *t = EC_POINT_new(eCurve), *p = EC_POINT_new(eCurve);
ECP::Point t, P;
// t = sG
EC_POINT_mul(eCurve, t, nullptr, genPoint, s, numContext);
t = eCurve.Multiply(s, genPoint);
// P = eK
EC_POINT_mul(eCurve, p, nullptr, pubPoint, e, numContext);
P = eCurve.Multiply(e, pubPoint);
// P += t
EC_POINT_add(eCurve, p, t, p, numContext);
P = eCurve.Add(P, t);
// x = P.x; y = P.y;
EC_POINT_get_affine_coordinates(eCurve, p, x, y, numContext);
BYTE msgDigest[SHA_DIGEST_LENGTH], msgBuffer[SHA_MSG_LENGTH_XP], xBin[FIELD_BYTES], yBin[FIELD_BYTES];
BYTE msgDigest[SHA::DIGESTSIZE], msgBuffer[SHAMessageLength], *pMsgBuffer = msgBuffer;
// Convert resulting point coordinates to bytes.
UMSKT::BN_bn2lebin(x, xBin, FIELD_BYTES);
UMSKT::BN_bn2lebin(y, yBin, FIELD_BYTES);
// Assemble the SHA message.
memcpy(&msgBuffer[0], &pData, 4);
memcpy(&msgBuffer[4], xBin, FIELD_BYTES);
memcpy(&msgBuffer[4 + FIELD_BYTES], yBin, FIELD_BYTES);
pData.Encode(pMsgBuffer, 4);
pMsgBuffer += 4;
P.x.Encode(pMsgBuffer, FieldBytes);
pMsgBuffer += FieldBytes;
P.y.Encode(pMsgBuffer, FieldBytes);
pMsgBuffer += FieldBytes;
// compHash = SHA1(pSerial || P.x || P.y)
SHA1(msgBuffer, SHA_MSG_LENGTH_XP, msgDigest);
auto digest = SHA();
digest.Update(msgBuffer, SHAMessageLength);
digest.Final(msgDigest);
// Translate the byte digest into a 32-bit integer - this is our computed hash.
// Truncate the hash to 28 bits.
DWORD compHash = BYDWORD(msgDigest) >> 4 & BITMASK(28);
BN_free(e);
BN_free(s);
BN_CTX_free(numContext);
EC_POINT_free(t);
EC_POINT_free(p);
Integer compHash = BYDWORD(msgDigest) >> 4 & BITMASK(28);
// If the computed hash checks out, the key is valid.
return compHash == info.Hash;

View File

@ -29,6 +29,7 @@ class EXPORT BINK1998 : public PIDGEN3
{
public:
using PIDGEN3::PIDGEN3;
BINK1998() = default;
explicit BINK1998(PIDGEN3 *p3)
{
privateKey = p3->privateKey;
@ -38,11 +39,15 @@ class EXPORT BINK1998 : public PIDGEN3
eCurve = p3->eCurve;
}
static constexpr DWORD32 FieldBits = 384;
static constexpr DWORD32 FieldBytes = FieldBits / 8;
static constexpr DWORD32 SHAMessageLength = (4 + 2 * FieldBytes);
using PIDGEN3::Pack;
BOOL Pack(QWORD *pRaw) override;
BOOL Pack(Q_OWORD *pRaw) override;
using PIDGEN3::Unpack;
BOOL Unpack(QWORD *pRaw) override;
BOOL Unpack(Q_OWORD *pRaw) override;
using PIDGEN3::Generate;
BOOL Generate(std::string &pKey) override;

View File

@ -32,13 +32,19 @@
/**
* Packs a Windows Server 2003-like Product Key.
*
* @param pRaw *QWORD[2] raw product key output
* @param pRaw [out] *OWORD raw product key
**/
BOOL BINK2002::Pack(QWORD *pRaw)
BOOL BINK2002::Pack(Q_OWORD *pRaw)
{
// AuthInfo [113..104] <- Signature [103..42] <- Hash [41..11] <- Channel ID [10..1] <- Upgrade [0]
pRaw[0] = FIRSTNBITS(info.Signature, 22) << 42 | (QWORD)info.Hash << 11 | info.ChannelID << 1 | info.isUpgrade;
pRaw[1] = FIRSTNBITS(info.AuthInfo, 10) << 40 | NEXTSNBITS(info.Signature, 40, 22);
Integer raw;
// AuthInfo [113..104] <- Signature [103..42] <- Hash [41..11] <- Channel ID [10..1] <- Upgrade [0];
raw += (info.AuthInfo & ((1 << 11) - 1)) << 104;
raw += info.Signature << 42;
raw += info.Hash << 11;
raw += info.ChannelID << 1;
raw += info.isUpgrade;
raw.Encode((BYTE *)pRaw, sizeof(QWORD) * 2);
return true;
}
@ -46,29 +52,30 @@ BOOL BINK2002::Pack(QWORD *pRaw)
/**
* Unpacks a Windows Server 2003-like Product Key.
*
* @param pRaw *QWORD[2] raw product key input
* @param pRaw [in] *OWORD raw product key input
**/
BOOL BINK2002::Unpack(QWORD *pRaw)
BOOL BINK2002::Unpack(Q_OWORD *pRaw)
{
// We're assuming that the quantity of information within the product key is at most 114 bits.
// log2(24^25) = 114.
// Upgrade = Bit 0
info.isUpgrade = FIRSTNBITS(pRaw[0], 1);
info.isUpgrade = FIRSTNBITS(pRaw->qword[0], 1);
// Channel ID = Bits [1..10] -> 10 bits
info.ChannelID = NEXTSNBITS(pRaw[0], 10, 1);
info.ChannelID = NEXTSNBITS(pRaw->qword[0], 10, 1);
// Hash = Bits [11..41] -> 31 bits
info.Hash = NEXTSNBITS(pRaw[0], 31, 11);
info.Hash = NEXTSNBITS(pRaw->qword[0], 31, 11);
// Signature = Bits [42..103] -> 62 bits
// The quad-word signature overlaps AuthInfo in bits 104 and 105,
// hence Microsoft employs a secret technique called: Signature = HIDWORD(Signature) >> 2 | LODWORD(Signature)
info.Signature = NEXTSNBITS(pRaw[1], 30, 10) << 32 | FIRSTNBITS(pRaw[1], 10) << 22 | NEXTSNBITS(pRaw[0], 22, 42);
info.Signature = NEXTSNBITS(pRaw->qword[0], 30, 10) << 32 | FIRSTNBITS(pRaw->qword[1], 10) << 22 |
NEXTSNBITS(pRaw->qword[0], 22, 42);
// AuthInfo = Bits [104..113] -> 10 bits
info.AuthInfo = NEXTSNBITS(pRaw[1], 10, 40);
info.AuthInfo = NEXTSNBITS(pRaw->qword[1], 10, 40);
return true;
}
@ -82,76 +89,83 @@ BOOL BINK2002::Unpack(QWORD *pRaw)
*/
BOOL BINK2002::Generate(std::string &pKey)
{
BN_CTX *numContext = BN_CTX_new();
Integer c, e, s;
BIGNUM *c = BN_CTX_get(numContext), *e = BN_CTX_get(numContext), *s = BN_CTX_get(numContext),
*x = BN_CTX_get(numContext), *y = BN_CTX_get(numContext);
Q_OWORD pRaw;
QWORD pRaw[2];
Integer limit;
limit.SetBit(62);
limit--;
// Data segment of the RPK.
DWORD pData = info.ChannelID << 1 | info.isUpgrade;
Integer pData = info.ChannelID << 1 | info.isUpgrade;
BOOL noSquare;
do
{
EC_POINT *r = EC_POINT_new(eCurve);
ECP::Point R;
// Generate a random number c consisting of 512 bits without any constraints.
BN_rand(c, FIELD_BITS_2003, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY);
c.Randomize(UMSKT::rng, FieldBits);
// R = cG
EC_POINT_mul(eCurve, r, nullptr, genPoint, c, numContext);
R = eCurve.Multiply(c, genPoint);
// Acquire its coordinates.
// x = R.x; y = R.y;
EC_POINT_get_affine_coordinates(eCurve, r, x, y, numContext);
BYTE msgDigest[SHA_DIGEST_LENGTH], msgBuffer[SHA_MSG_LENGTH_2003], xBin[FIELD_BYTES_2003],
yBin[FIELD_BYTES_2003];
// Convert resulting point coordinates to bytes.
UMSKT::BN_bn2lebin(x, xBin, FIELD_BYTES_2003);
UMSKT::BN_bn2lebin(y, yBin, FIELD_BYTES_2003);
BYTE msgDigest[SHA::DIGESTSIZE], msgBuffer[SHAMessageLength], *pMsgBuffer = msgBuffer;
// Assemble the first SHA message.
msgBuffer[0x00] = 0x79;
msgBuffer[0x01] = (pData & 0x00FF);
msgBuffer[0x02] = (pData & 0xFF00) >> 8;
msgBuffer[0] = 0x79;
pMsgBuffer++;
memcpy(&msgBuffer[3], xBin, FIELD_BYTES_2003);
memcpy(&msgBuffer[3 + FIELD_BYTES_2003], yBin, FIELD_BYTES_2003);
pData.Encode(pMsgBuffer, 2);
pMsgBuffer += 2;
// Convert resulting point coordinates to bytes.
// and flip the endianness
R.x.Encode(pMsgBuffer, FieldBytes);
std::reverse(pMsgBuffer, pMsgBuffer + FieldBytes);
pMsgBuffer += FieldBytes;
R.y.Encode(pMsgBuffer, FieldBytes);
std::reverse(pMsgBuffer, pMsgBuffer + FieldBytes);
pMsgBuffer += FieldBytes;
// pHash = SHA1(79 || Channel ID || R.x || R.y)
SHA1(msgBuffer, SHA_MSG_LENGTH_2003, msgDigest);
auto digest = SHA();
digest.Update(msgBuffer, FieldBytes);
digest.Final(msgDigest);
// Translate the byte digest into a 32-bit integer - this is our computed hash.
// Truncate the hash to 31 bits.
info.Hash = BYDWORD(msgDigest) & BITMASK(31);
// Assemble the second SHA message.
pMsgBuffer = msgBuffer;
msgBuffer[0x00] = 0x5D;
msgBuffer[0x01] = (pData & 0x00FF);
msgBuffer[0x02] = (pData & 0xFF00) >> 8;
msgBuffer[0x03] = (info.Hash & 0x000000FF);
msgBuffer[0x04] = (info.Hash & 0x0000FF00) >> 8;
msgBuffer[0x05] = (info.Hash & 0x00FF0000) >> 16;
msgBuffer[0x06] = (info.Hash & 0xFF000000) >> 24;
msgBuffer[0x07] = (info.AuthInfo & 0x00FF);
msgBuffer[0x08] = (info.AuthInfo & 0xFF00) >> 8;
pMsgBuffer++;
pData.Encode(pMsgBuffer, 2);
pMsgBuffer += 2;
info.Hash.Encode(pMsgBuffer, 4);
pMsgBuffer += 4;
info.AuthInfo.Encode(pMsgBuffer, 2);
pMsgBuffer += 2;
msgBuffer[0x09] = 0x00;
msgBuffer[0x0A] = 0x00;
// newSignature = SHA1(5D || Channel ID || Hash || AuthInfo || 00 00)
SHA1(msgBuffer, 11, msgDigest);
digest = SHA();
digest.Update(msgBuffer, 0x0B);
digest.Final(msgDigest);
// Translate the byte digest into a 64-bit integer - this is our computed intermediate signature.
// As the signature is only 62 bits long at most, we have to truncate it by shifting the high DWORD right 2
// bits (per spec).
QWORD iSignature = NEXTSNBITS(BYDWORD(&msgDigest[4]), 30, 2) << 32 | BYDWORD(msgDigest);
BN_lebin2bn((BYTE *)&iSignature, sizeof(iSignature), e);
Integer iSignature = NEXTSNBITS(BYDWORD(&msgDigest[4]), 30, 2) << 32 | BYDWORD(msgDigest);
/*
*
@ -183,63 +197,57 @@ BOOL BINK2002::Generate(std::string &pKey)
*/
// e = ek (mod n)
BN_mod_mul(e, e, privateKey, genOrder, numContext);
// s = e
BN_copy(s, e);
e = iSignature * privateKey % genOrder;
// s = (ek (mod n))²
BN_mod_sqr(s, s, genOrder, numContext);
s = CryptoPP::ModularExponentiation(e, Integer::Two(), genOrder);
// c *= 4 (c <<= 2)
BN_lshift(c, c, 2);
c <<= 2;
// s += c
BN_add(s, s, c);
s += c;
// Around half of numbers modulo a prime are not squares -> BN_sqrt_mod fails about half of the times,
// hence if BN_sqrt_mod returns NULL, we need to restart with a different seed.
// s = √((ek)² + 4c (mod n))
noSquare = BN_mod_sqrt(s, s, genOrder, numContext) == nullptr;
s = CryptoPP::ModularSquareRoot(s, genOrder);
// s = -ek + √((ek)² + 4c) (mod n)
BN_mod_sub(s, s, e, genOrder, numContext);
s = s - e % genOrder;
// If s is odd, add order to it.
// The order is a prime, so it can't be even.
if (BN_is_odd(s))
if (s % Integer::Two() != 0)
{
// s = -ek + √((ek)² + 4c) + n
BN_add(s, s, genOrder);
s += genOrder;
}
// s /= 2 (s >>= 1)
BN_rshift1(s, s);
s >>= 1;
// Translate resulting scalar into a 64-bit integer (the byte order is little-endian).
BN_bn2lebinpad(s, (BYTE *)&info.Signature, BN_num_bytes(s));
info.Signature = s;
// Pack product key.
Pack(pRaw);
Pack(&pRaw);
fmt::print(UMSKT::debug, "Generation results:\n");
fmt::print(UMSKT::debug, "{:>10}: {:b}\n", "Upgrade", (bool)info.isUpgrade);
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Channel ID", info.ChannelID);
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Hash", info.Hash);
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Signature", info.Signature);
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "AuthInfo", info.AuthInfo);
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Upgrade", (bool)info.isUpgrade);
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Channel ID", info.ChannelID);
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Hash", info.Hash);
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Signature", info.Signature);
fmt::print(UMSKT::debug, "{:>10}: {}\n", "AuthInfo", info.AuthInfo);
fmt::print(UMSKT::debug, "\n");
EC_POINT_free(r);
} while (info.Signature > BITMASK(62) || noSquare);
} while (info.Signature > limit || noSquare);
// ↑ ↑ ↑
// The signature can't be longer than 62 bits, else it will
// overlap with the AuthInfo segment next to it.
// Convert bytecode to Base24 CD-key.
base24(pKey, (BYTE *)pRaw);
BN_CTX_free(numContext);
base24(pKey, pRaw.byte);
return true;
}
@ -251,48 +259,51 @@ BOOL BINK2002::Generate(std::string &pKey)
**/
BOOL BINK2002::Validate(std::string &pKey)
{
BN_CTX *context = BN_CTX_new();
QWORD bKey[2];
Q_OWORD bKey;
// Convert Base24 CD-key to bytecode.
unbase24((BYTE *)bKey, &pKey[0]);
unbase24(bKey.byte, &pKey[0]);
// Extract product key segments from bytecode.
Unpack(bKey);
Unpack(&bKey);
DWORD pData = info.ChannelID << 1 | info.isUpgrade;
Integer pData = info.ChannelID << 1 | info.isUpgrade;
fmt::print(UMSKT::debug, "Validation results:\n");
fmt::print(UMSKT::debug, "{:>10}: {:b}\n", "Upgrade", (bool)info.isUpgrade);
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Upgrade", (bool)info.isUpgrade);
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Channel ID", info.ChannelID);
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Hash", info.Hash);
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Signature", info.Signature);
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "AuthInfo", info.AuthInfo);
fmt::print(UMSKT::debug, "\n");
BYTE msgDigest[SHA_DIGEST_LENGTH], msgBuffer[SHA_MSG_LENGTH_2003], xBin[FIELD_BYTES_2003], yBin[FIELD_BYTES_2003];
BYTE msgDigest[SHA::DIGESTSIZE], msgBuffer[SHAMessageLength], *pMsgBuffer = msgBuffer;
// Assemble the first SHA message.
msgBuffer[0x00] = 0x5D;
msgBuffer[0x01] = (pData & 0x00FF);
msgBuffer[0x02] = (pData & 0xFF00) >> 8;
msgBuffer[0x03] = (info.Hash & 0x000000FF);
msgBuffer[0x04] = (info.Hash & 0x0000FF00) >> 8;
msgBuffer[0x05] = (info.Hash & 0x00FF0000) >> 16;
msgBuffer[0x06] = (info.Hash & 0xFF000000) >> 24;
msgBuffer[0x07] = (info.AuthInfo & 0x00FF);
msgBuffer[0x08] = (info.AuthInfo & 0xFF00) >> 8;
pMsgBuffer++;
pData.Encode(pMsgBuffer, 2);
pMsgBuffer += 2;
info.Hash.Encode(pMsgBuffer, 4);
pMsgBuffer += 4;
info.AuthInfo.Encode(pMsgBuffer, 2);
pMsgBuffer += 2;
msgBuffer[0x09] = 0x00;
msgBuffer[0x0A] = 0x00;
// newSignature = SHA1(5D || Channel ID || Hash || AuthInfo || 00 00)
SHA1(msgBuffer, 11, msgDigest);
auto digest = SHA();
digest.Update(msgBuffer, 0x0B);
digest.Final(msgDigest);
// Translate the byte digest into a 64-bit integer - this is our computed intermediate signature.
// As the signature is only 62 bits long at most, we have to truncate it by shifting the high DWORD right 2 bits
// (per spec).
QWORD iSignature = NEXTSNBITS(BYDWORD(&msgDigest[4]), 30, 2) << 32 | BYDWORD(msgDigest);
Integer iSignature = NEXTSNBITS(BYDWORD(&msgDigest[4]), 30, 2) << 32 | BYDWORD(msgDigest);
/*
*
@ -308,55 +319,44 @@ BOOL BINK2002::Validate(std::string &pKey)
* P = s(sG + eK)
*
*/
BIGNUM *e = BN_lebin2bn((BYTE *)&iSignature, sizeof(iSignature), nullptr),
*s = BN_lebin2bn((BYTE *)&info.Signature, sizeof(info.Signature), nullptr);
BIGNUM *x = BN_CTX_get(context), *y = BN_CTX_get(context);
Integer e = iSignature, s = info.Signature;
// Create 2 points on the elliptic curve.
EC_POINT *p = EC_POINT_new(eCurve), *t = EC_POINT_new(eCurve);
ECP::Point p, t;
// t = sG
EC_POINT_mul(eCurve, t, nullptr, genPoint, s, context);
t = eCurve.Multiply(s, genPoint);
// p = eK
EC_POINT_mul(eCurve, p, nullptr, pubPoint, e, context);
p = eCurve.Multiply(e, pubPoint);
// p += t
EC_POINT_add(eCurve, p, t, p, context);
p = eCurve.Add(p, t);
// p *= s
EC_POINT_mul(eCurve, p, nullptr, p, s, context);
// x = p.x; y = p.y;
EC_POINT_get_affine_coordinates(eCurve, p, x, y, context);
// Convert resulting point coordinates to bytes.
UMSKT::BN_bn2lebin(x, xBin, FIELD_BYTES_2003);
UMSKT::BN_bn2lebin(y, yBin, FIELD_BYTES_2003);
p = eCurve.Multiply(s, p);
// Assemble the second SHA message.
pMsgBuffer = msgBuffer;
msgBuffer[0x00] = 0x79;
msgBuffer[0x01] = (pData & 0x00FF);
msgBuffer[0x02] = (pData & 0xFF00) >> 8;
pMsgBuffer++;
memcpy((void *)&msgBuffer[3], (void *)xBin, FIELD_BYTES_2003);
memcpy((void *)&msgBuffer[3 + FIELD_BYTES_2003], (void *)yBin, FIELD_BYTES_2003);
pData.Encode(pMsgBuffer, 2);
pMsgBuffer += 2;
p.x.Encode(pMsgBuffer, FieldBytes);
pMsgBuffer += FieldBytes;
p.y.Encode(pMsgBuffer, FieldBytes);
pMsgBuffer += FieldBytes;
// compHash = SHA1(79 || Channel ID || p.x || p.y)
SHA1(msgBuffer, SHA_MSG_LENGTH_2003, msgDigest);
digest.Update(msgBuffer, SHAMessageLength);
digest.Final(msgDigest);
// Translate the byte digest into a 32-bit integer - this is our computed hash.
// Truncate the hash to 31 bits.
DWORD compHash = BYDWORD(msgDigest) & BITMASK(31);
BN_free(s);
BN_free(e);
EC_POINT_free(p);
EC_POINT_free(t);
BN_CTX_free(context);
Integer compHash = BYDWORD(msgDigest) & BITMASK(31);
// If the computed hash checks out, the key is valid.
return compHash == info.Hash;

View File

@ -29,6 +29,7 @@ class EXPORT BINK2002 : public PIDGEN3
{
public:
using PIDGEN3::PIDGEN3;
BINK2002() = default;
explicit BINK2002(PIDGEN3 *p3)
{
privateKey = p3->privateKey;
@ -38,11 +39,15 @@ class EXPORT BINK2002 : public PIDGEN3
eCurve = p3->eCurve;
}
static constexpr DWORD32 FieldBits = 512;
static constexpr DWORD32 FieldBytes = FieldBits / 8;
static constexpr DWORD32 SHAMessageLength = (3 + 2 * FieldBytes);
using PIDGEN3::Pack;
BOOL Pack(QWORD *pRaw) override;
BOOL Pack(Q_OWORD *pRaw) override;
using PIDGEN3::Unpack;
BOOL Unpack(QWORD *pRaw) override;
BOOL Unpack(Q_OWORD *pRaw) override;
using PIDGEN3::Generate;
BOOL Generate(std::string &pKey) override;

View File

@ -35,6 +35,24 @@ int getRandomNumber()
// guaranteed to be random
}
/**
* Creates an Integer that populates PIDGEN3::MaxSizeBINK1998
* Invoked during Runtime startup
*
* @return
*/
Integer MakeMaxSizeBINK1998()
{
Integer max;
// 1 << 385 (or max size of BINK1998 field in bits + 1)
max.SetBit((12 * 4 * 8) + 1);
return max;
}
const Integer PIDGEN3::MaxSizeBINK1998 = MakeMaxSizeBINK1998();
/**
* Initializes the elliptic curve
*
@ -65,61 +83,49 @@ BOOL PIDGEN3::LoadEllipticCurve(const std::string pSel, const std::string aSel,
// BIGNUM - Large numbers
// BIGNUMCTX - Context large numbers (temporary)
// Context variable
BN_CTX *context = BN_CTX_new();
// We're presented with an elliptic curve, a multivariable function y(x; p; a; b), where
// y^2 % p = x^3 + ax + b % p.
BIGNUM *a = BN_CTX_get(context), *b = BN_CTX_get(context), *p = BN_CTX_get(context);
auto p = Integer(&pSel[0]), a = Integer(&aSel[0]), b = Integer(&bSel[0]),
// Public key will consist of the resulting (x; y) values.
BIGNUM *publicKeyX = BN_CTX_get(context), *publicKeyY = BN_CTX_get(context);
// Public key will consist of the resulting (x; y) values.
generatorX = Integer(&generatorXSel[0]), generatorY = Integer(&generatorYSel[0]),
// G(x; y) is a generator function, its return value represents a point on the elliptic curve.
BIGNUM *generatorX = BN_CTX_get(context), *generatorY = BN_CTX_get(context);
genOrder = BN_new();
privateKey = BN_new();
/* Public data */
BN_dec2bn(&p, &pSel[0]);
BN_dec2bn(&a, &aSel[0]);
BN_dec2bn(&b, &bSel[0]);
BN_dec2bn(&generatorX, &generatorXSel[0]);
BN_dec2bn(&generatorY, &generatorYSel[0]);
BN_dec2bn(&publicKeyX, &publicKeyXSel[0]);
BN_dec2bn(&publicKeyY, &publicKeyYSel[0]);
// G(x; y) is a generator function, its return value represents a point on the elliptic curve.
publicKeyX = Integer(&publicKeyXSel[0]), publicKeyY = Integer(&publicKeyYSel[0]);
/* Computed Data */
BN_dec2bn(&genOrder, &genOrderSel[0]);
BN_dec2bn(&privateKey, &privateKeySel[0]);
genOrder = Integer(&genOrderSel[0]);
privateKey = Integer(&privateKeySel[0]);
/* Elliptic Curve calculations. */
// The group is defined via Fp = all integers [0; p - 1], where p is prime.
// The function EC_POINT_set_affine_coordinates() sets the x and y coordinates for the point p defined over the
// curve given in group.
eCurve = EC_GROUP_new_curve_GFp(p, a, b, context);
eCurve = ECP(p, a, b);
// Create new point for the generator on the elliptic curve and set its coordinates to (genX; genY).
genPoint = EC_POINT_new(eCurve);
EC_POINT_set_affine_coordinates(eCurve, genPoint, generatorX, generatorY, context);
// Create new point N for the generator on the elliptic curve and set its coordinates to (genX; genY).
genPoint = ECP::Point(generatorX, generatorY);
// Create new point for the public key on the elliptic curve and set its coordinates to (pubX; pubY).
pubPoint = EC_POINT_new(eCurve);
EC_POINT_set_affine_coordinates(eCurve, pubPoint, publicKeyX, publicKeyY, context);
// Create new point Q for the public key on the elliptic curve and set its coordinates to (pubX; pubY).
pubPoint = ECP::Point(publicKeyX, publicKeyY);
// If generator and public key points are not on the elliptic curve, either the generator or the public key values
// are incorrect.
assert(EC_POINT_is_on_curve(eCurve, genPoint, context) == true);
assert(EC_POINT_is_on_curve(eCurve, pubPoint, context) == true);
// Cleanup
BN_CTX_free(context);
assert(eCurve.VerifyPoint(genPoint) == true);
assert(eCurve.VerifyPoint(pubPoint) == true);
return true;
}
PIDGEN3 *PIDGEN3::Factory(const std::string &field)
{
if (checkFieldStrIsBink1998(field))
{
return new BINK1998();
}
return new BINK2002();
}
BOOL PIDGEN3::Generate(std::string &pKey)
{
BOOL retval;
@ -165,7 +171,6 @@ BOOL PIDGEN3::Validate(std::string &pKey)
void PIDGEN3::base24(std::string &cdKey, BYTE *byteSeq)
{
BYTE rbyteSeq[16], output[26];
BIGNUM *z;
// Copy byte sequence to the reversed byte sequence.
memcpy(rbyteSeq, byteSeq, sizeof(rbyteSeq));
@ -178,22 +183,18 @@ void PIDGEN3::base24(std::string &cdKey, BYTE *byteSeq)
; // do nothing, just counting
}
UMSKT::endian(rbyteSeq, ++length);
// Convert reversed byte sequence to BigNum z.
z = BN_bin2bn(rbyteSeq, length, nullptr);
auto z = Integer((BYTE *)&rbyteSeq, sizeof(rbyteSeq));
// Divide z by 24 and convert the remainder to a CD-key char.
for (int i = 24; i >= 0; i--)
{
output[i] = pKeyCharset[BN_div_word(z, 24)];
output[i] = pKeyCharset[z.Modulo(24)];
}
output[25] = 0;
cdKey = (char *)output;
BN_free(z);
}
/**
@ -205,7 +206,7 @@ void PIDGEN3::base24(std::string &cdKey, BYTE *byteSeq)
void PIDGEN3::unbase24(BYTE *byteSeq, std::string cdKey)
{
BYTE pDecodedKey[PK_LENGTH + NULL_TERMINATOR]{};
BIGNUM *y = BN_new();
Integer y;
// Remove dashes from the CD-key and put it into a Base24 byte array.
for (int i = 0, k = 0; i < cdKey.length() && k < PK_LENGTH; i++)
@ -226,52 +227,40 @@ void PIDGEN3::unbase24(BYTE *byteSeq, std::string cdKey)
// Calculate the weighed sum of byte array elements.
for (int i = 0; i < PK_LENGTH; i++)
{
BN_mul_word(y, PK_LENGTH - 1);
BN_add_word(y, pDecodedKey[i]);
y *= PK_LENGTH - 1;
y += pDecodedKey[i];
}
// Acquire length.
int n = BN_num_bytes(y);
auto n = y.ByteCount();
// Place the generated code into the byte sequence.
BN_bn2bin(y, byteSeq);
BN_free(y);
// Reverse the byte sequence.
UMSKT::endian(byteSeq, n);
y.Encode(byteSeq, 16);
}
/**
* Checks to see if the currently instantiated PIDGEN3 object has a
* field size greater than the maximum known BINK1998 size.
*
* @return
*/
BOOL PIDGEN3::checkFieldIsBink1998()
{
auto *max = BN_new();
// 1 << 385 (or max size of BINK1998 field in bits + 1)
BN_set_bit(max, (12 * 4 * 8) + 1);
// retval is -1 when (max < privateKey)
int retval = BN_cmp(max, privateKey);
BN_free(max);
// is max > privateKey?
return retval == 1;
return (MaxSizeBINK1998 > privateKey);
}
/**
* Checks if a given field, in a std::string, is greater than
* the maximum known BINK1998 size
*
* @param keyin
* @return
*/
BOOL PIDGEN3::checkFieldStrIsBink1998(std::string keyin)
{
auto *context = BN_CTX_new();
auto max = BN_CTX_get(context), input = BN_CTX_get(context);
Integer check(&keyin[0]);
BN_dec2bn(&input, &keyin[0]);
// 1 << 385 (or max size of BINK1998 field in bits + 1)
BN_set_bit(max, (12 * 4 * 8) + 1);
// retval is -1 when (max < privateKey)
int retval = BN_cmp(max, input);
BN_CTX_free(context);
// is max > privateKey?
return retval == 1;
// is max > check?
return (MaxSizeBINK1998 > check);
}

View File

@ -34,14 +34,12 @@ class EXPORT PIDGEN3
friend class BINK2002;
protected:
BIGNUM *privateKey, *genOrder;
EC_POINT *genPoint, *pubPoint;
EC_GROUP *eCurve;
Integer privateKey, genOrder;
ECP::Point genPoint, pubPoint;
ECP eCurve;
public:
PIDGEN3()
{
}
PIDGEN3() = default;
PIDGEN3(PIDGEN3 &p3)
{
@ -54,43 +52,38 @@ class EXPORT PIDGEN3
virtual ~PIDGEN3()
{
EC_GROUP_free(eCurve);
EC_POINT_free(genPoint);
EC_POINT_free(pubPoint);
BN_free(genOrder);
BN_free(privateKey);
}
struct KeyInfo
{
DWORD Serial = 0, AuthInfo = 0, ChannelID = 0, Hash = 0;
QWORD Signature = 0;
Integer Serial = 0, AuthInfo = 0, ChannelID = 0, Hash = 0, Signature = 0;
BOOL isUpgrade = false;
void setSerial(DWORD serialIn)
void setSerial(DWORD32 serialIn)
{
Serial = serialIn;
Serial.Decode((BYTE *)&serialIn, sizeof(serialIn));
}
void setAuthInfo(DWORD AuthInfoIn)
void setAuthInfo(DWORD32 AuthInfoIn)
{
AuthInfo = AuthInfoIn;
AuthInfo.Decode((BYTE *)&AuthInfoIn, sizeof(AuthInfoIn));
}
void setChannelID(DWORD ChannelIDIn)
void setChannelID(DWORD32 ChannelIDIn)
{
ChannelID = ChannelIDIn;
ChannelID.Decode((BYTE *)&ChannelIDIn, sizeof(ChannelIDIn));
}
} info;
static constexpr char pKeyCharset[] = "BCDFGHJKMPQRTVWXY2346789";
static const Integer MaxSizeBINK1998;
BOOL LoadEllipticCurve(std::string pSel, std::string aSel, std::string bSel, std::string generatorXSel,
std::string generatorYSel, std::string publicKeyXSel, std::string publicKeyYSel,
std::string genOrderSel, std::string privateKeySel);
virtual BOOL Pack(QWORD *pRaw) = 0;
virtual BOOL Unpack(QWORD *pRaw) = 0;
virtual BOOL Pack(Q_OWORD *pRaw) = 0;
virtual BOOL Unpack(Q_OWORD *pRaw) = 0;
virtual BOOL Generate(std::string &pKey);
virtual BOOL Validate(std::string &pKey);
@ -99,6 +92,7 @@ class EXPORT PIDGEN3
void unbase24(BYTE *byteSeq, std::string cdKey);
BOOL checkFieldIsBink1998();
static BOOL checkFieldStrIsBink1998(std::string keyin);
static PIDGEN3 *Factory(const std::string &field);
};
#endif // UMSKT_PIDGEN3_H

View File

@ -98,9 +98,10 @@ using BOOL = int32_t;
using BYTE = uint8_t;
using WORD = uint16_t;
using DWORD = unsigned long;
using DWORD32 = uint32_t;
using QWORD = uint64_t;
#if defined(_M_ARM) // for Windows on ARM ??
#if defined(_MSC_VER) && defined(_M_ARM) // for Windows on ARM ??
using __m128 = __n128;
#endif
@ -114,7 +115,7 @@ using OWORD = uint128_t;
typedef union {
OWORD oword;
QWORD qword[2];
DWORD dword[4];
DWORD32 dword32[4];
WORD word[8];
BYTE byte[16];
} Q_OWORD;