diff --git a/CMakeLists.txt b/CMakeLists.txt index b83942e..596fdb7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,6 @@ CONFIGURE_FILE(keys.json keys.json COPYONLY) #SET(BUILD_SHARED_LIBS OFF) #SET(CMAKE_EXE_LINKER_FLAGS "-static") -ADD_EXECUTABLE(xpkey src/main.cpp src/server.cpp src/xp.cpp src/key.cpp src/util.cpp src/cli.cpp src/confid.cpp) +ADD_EXECUTABLE(xpkey src/main.cpp src/BINK2002.cpp src/BINK1998.cpp src/key.cpp src/util.cpp src/cli.cpp src/confid.cpp) TARGET_INCLUDE_DIRECTORIES(xpkey PUBLIC crypto) TARGET_LINK_LIBRARIES(xpkey PUBLIC OpenSSL::Crypto nlohmann_json::nlohmann_json fmt) \ No newline at end of file diff --git a/src/xp.cpp b/src/BINK1998.cpp similarity index 96% rename from src/xp.cpp rename to src/BINK1998.cpp index e541b84..7a4efb5 100644 --- a/src/xp.cpp +++ b/src/BINK1998.cpp @@ -2,10 +2,10 @@ // Created by Andrew on 01/06/2023. // -#include "header.h" +#include "BINK1998.h" /* Unpacks the Windows XP-like Product Key. */ -void unpackXP( +void BINK1998::Unpack( QWORD (&pRaw)[2], DWORD &pSerial, DWORD &pHash, @@ -25,7 +25,7 @@ void unpackXP( } /* Packs the Windows XP-like Product Key. */ -void packXP( +void BINK1998::Pack( QWORD (&pRaw)[2], DWORD pSerial, DWORD pHash, @@ -41,7 +41,7 @@ void packXP( } /* Verifies the Windows XP-like Product Key. */ -bool verifyXPKey( +bool BINK1998::Verify( EC_GROUP *eCurve, EC_POINT *basePoint, EC_POINT *publicKey, @@ -59,7 +59,7 @@ bool verifyXPKey( unbase24((BYTE *)pRaw, pKey); // Extract RPK, hash and signature from bytecode. - unpackXP(pRaw, pSerial, pHash, pSignature); + Unpack(pRaw, pSerial, pHash, pSignature); /* * @@ -133,7 +133,7 @@ bool verifyXPKey( } /* Generate a valid Product Key. */ -void generateXPKey( +void BINK1998::Generate( EC_GROUP *eCurve, EC_POINT *basePoint, BIGNUM *genOrder, @@ -216,7 +216,7 @@ void generateXPKey( BN_bn2lebinpad(s, (BYTE *)&pSignature, BN_num_bytes(s)); // Pack product key. - packXP(pRaw, pSerial, pHash, pSignature); + Pack(pRaw, pSerial, pHash, pSignature); if (options.verbose) { fmt::print("Generation results:\n"); diff --git a/src/BINK1998.h b/src/BINK1998.h new file mode 100644 index 0000000..e6ac5f7 --- /dev/null +++ b/src/BINK1998.h @@ -0,0 +1,19 @@ +// +// Created by neo on 6/6/2023. +// + +#ifndef WINDOWSXPKG_BINK1998_H +#define WINDOWSXPKG_BINK1998_H + +#include "header.h" + +class BINK1998 { + static void Unpack (QWORD (&pRaw)[2], DWORD &pSerial, DWORD &pHash, QWORD &pSignature); + static void Pack (QWORD (&pRaw)[2], DWORD pSerial, DWORD pHash, QWORD pSignature); + +public: + static bool Verify (EC_GROUP *eCurve, EC_POINT *basePoint, EC_POINT *publicKey, char (&pKey)[25]); + static void Generate (EC_GROUP *eCurve, EC_POINT *basePoint, BIGNUM *genOrder, BIGNUM *privateKey, DWORD pSerial, char (&pKey)[25]); +}; + +#endif //WINDOWSXPKG_BINK1998_H diff --git a/src/server.cpp b/src/BINK2002.cpp similarity index 97% rename from src/server.cpp rename to src/BINK2002.cpp index 5f69080..08773a0 100644 --- a/src/server.cpp +++ b/src/BINK2002.cpp @@ -2,10 +2,10 @@ // Created by Andrew on 01/06/2023. // -#include "header.h" +#include "BINK2002.h" /* Unpacks the Windows Server 2003-like Product Key. */ -void unpackServer( +void BINK2002::Unpack( QWORD (&pRaw)[2], DWORD &pChannelID, DWORD &pHash, @@ -31,7 +31,7 @@ void unpackServer( } /* Packs the Windows Server 2003-like Product Key. */ -void packServer( +void BINK2002::Pack( QWORD (&pRaw)[2], DWORD pChannelID, DWORD pHash, @@ -44,7 +44,7 @@ void packServer( } /* Verifies the Windows Server 2003-like Product Key. */ -bool verifyServerKey( +bool BINK2002::Verify( EC_GROUP *eCurve, EC_POINT *basePoint, EC_POINT *publicKey, @@ -63,7 +63,7 @@ bool verifyServerKey( unbase24((BYTE *)bKey, cdKey); // Extract product key segments from bytecode. - unpackServer(bKey, pChannelID, pHash, pSignature, pAuthInfo); + Unpack(bKey, pChannelID, pHash, pSignature, pAuthInfo); if (options.verbose) { fmt::print("Validation results:\n"); @@ -171,7 +171,7 @@ bool verifyServerKey( return compHash == pHash; } -void generateServerKey( +void BINK2002::Generate( EC_GROUP *eCurve, EC_POINT *basePoint, BIGNUM *genOrder, @@ -320,7 +320,7 @@ void generateServerKey( BN_bn2lebinpad(s, (BYTE *)&pSignature, BN_num_bytes(s)); // Pack product key. - packServer(pRaw, pChannelID, pHash, pSignature, pAuthInfo); + Pack(pRaw, pChannelID, pHash, pSignature, pAuthInfo); if (options.verbose) { fmt::print("Generation results:\n"); diff --git a/src/BINK2002.h b/src/BINK2002.h new file mode 100644 index 0000000..c1e9f41 --- /dev/null +++ b/src/BINK2002.h @@ -0,0 +1,19 @@ +// +// Created by neo on 6/6/2023. +// + +#ifndef WINDOWSXPKG_BINK2002_H +#define WINDOWSXPKG_BINK2002_H + +#include "header.h" + +class BINK2002 { + static void Unpack (QWORD (&pRaw)[2], DWORD &pChannelID, DWORD &pHash, QWORD &pSignature, DWORD &pAuthInfo); + static void Pack (QWORD (&pRaw)[2], DWORD pChannelID, DWORD pHash, QWORD &pSignature, DWORD pAuthInfo); + +public: + static bool Verify (EC_GROUP *eCurve, EC_POINT *basePoint, EC_POINT *publicKey, char (&cdKey)[25]); + static void Generate (EC_GROUP *eCurve, EC_POINT *basePoint, BIGNUM *genOrder, BIGNUM *privateKey, DWORD pChannelID, DWORD pAuthInfo, char (&pKey)[25]); +}; + +#endif //WINDOWSXPKG_BINK2002_H diff --git a/src/cli.cpp b/src/cli.cpp index 954e21a..1b65b77 100644 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -2,9 +2,12 @@ // Created by Andrew on 01/06/2023. // -#include "header.h" +#include "cli.h" +#include "confid.h" +#include "BINK1998.h" +#include "BINK2002.h" -bool loadJSON(const fs::path& filename, json *output) { +bool CLI::loadJSON(const fs::path& filename, json *output) { if (!fs::exists(filename)) { fmt::print("ERROR: File {} does not exist\n", filename.string()); return false; @@ -22,7 +25,7 @@ bool loadJSON(const fs::path& filename, json *output) { } -void showHelp(char *argv[]) { +void CLI::showHelp(char *argv[]) { fmt::print("usage: {} \n", argv[0]); fmt::print("\t-h --help\tshow this message\n"); fmt::print("\t-v --verbose\tenable verbose output\n"); @@ -35,20 +38,20 @@ void showHelp(char *argv[]) { fmt::print("\n\n"); } -int parseCommandLine(int argc, char* argv[], Options* options) { - // set default options +int CLI::parseCommandLine(int argc, char* argv[], Options* options) { *options = Options { - "2E", - 640, - "keys.json", - 1, - "", - false, - false, - false, - false, - false + "2E", + "keys.json", + "", + 640, + 1, + false, + false, + false, + false, + MODE_BINK1998 }; + // set default options for (int i = 1; i < argc; i++) { std::string arg = argv[i]; @@ -117,7 +120,7 @@ int parseCommandLine(int argc, char* argv[], Options* options) { return !options->error; } -int validateCommandLine(Options* options, char *argv[], json *keys) { +int CLI::validateCommandLine(Options* options, char *argv[], json *keys) { if (options->verbose) { fmt::print("Loading keys file {}\n", options->keysFilename); } @@ -155,18 +158,18 @@ int validateCommandLine(Options* options, char *argv[], json *keys) { sscanf(options->binkid.c_str(), "%x", &intBinkID); if (intBinkID >= 0x40) { - options->isBink2002 = true; + options->applicationMode = MODE_BINK2002; } if (options->channelID > 999) { - fmt::print("ERROR: refusing to create a key with a siteID greater than 999\n"); + fmt::print("ERROR: refusing to create a key with a Channel ID greater than 999\n"); return 1; } return 0; } -void print_product_id(DWORD *pid) +void CLI::print_product_id(DWORD *pid) { char raw[12]; char b[6], c[8]; @@ -196,7 +199,7 @@ void print_product_id(DWORD *pid) fmt::print("Product ID: PPPPP-{}-{}-23xxx\n", b, c); } -void print_product_key(char *pk) { +void CLI::print_product_key(char *pk) { assert(strlen(pk) == 25); std::string spk = pk; @@ -206,4 +209,147 @@ void print_product_key(char *pk) { spk.substr(10,5), spk.substr(15,5), spk.substr(20,5)); -} \ No newline at end of file +} + +CLI::CLI(Options options, json keys) { + BINKID = options.binkid.c_str(); + + // We cannot produce a valid key without knowing the private key k. The reason for this is that + // we need the result of the function K(x; y) = kG(x; y). + privateKey = BN_new(); + + // We can, however, validate any given key using the available public key: {p, a, b, G, K}. + // genOrder the order of the generator G, a value we have to reverse -> Schoof's Algorithm. + genOrder = BN_new(); + + /* Computed data */ + BN_dec2bn(&genOrder, keys["BINK"][BINKID]["n"].get().c_str()); + BN_dec2bn(&privateKey, keys["BINK"][BINKID]["priv"].get().c_str()); + + if (options.verbose) { + fmt::print("----------------------------------------------------------- \n"); + fmt::print("Loaded the following curve constraints: BINK[{}]\n", BINKID); + fmt::print("----------------------------------------------------------- \n"); + fmt::print(" P: {}\n", keys["BINK"][BINKID]["p"].get()); + fmt::print(" a: {}\n", keys["BINK"][BINKID]["a"].get()); + fmt::print(" b: {}\n", keys["BINK"][BINKID]["b"].get()); + fmt::print("Gx: {}\n", keys["BINK"][BINKID]["g"]["x"].get()); + fmt::print("Gy: {}\n", keys["BINK"][BINKID]["g"]["y"].get()); + fmt::print("Kx: {}\n", keys["BINK"][BINKID]["pub"]["x"].get()); + fmt::print("Ky: {}\n", keys["BINK"][BINKID]["pub"]["y"].get()); + fmt::print(" n: {}\n", keys["BINK"][BINKID]["n"].get()); + fmt::print(" k: {}\n", keys["BINK"][BINKID]["priv"].get()); + fmt::print("\n"); + } + + eCurve = initializeEllipticCurve( + keys["BINK"][BINKID]["p"].get(), + keys["BINK"][BINKID]["a"].get(), + keys["BINK"][BINKID]["b"].get(), + keys["BINK"][BINKID]["g"]["x"].get(), + keys["BINK"][BINKID]["g"]["y"].get(), + keys["BINK"][BINKID]["pub"]["x"].get(), + keys["BINK"][BINKID]["pub"]["y"].get(), + genPoint, + pubPoint + ); + + total = options.numKeys; +} + +int CLI::BINK1998() { + DWORD nRaw = options.channelID * 1000000 ; /* <- change */ + + BIGNUM *bnrand = BN_new(); + BN_rand(bnrand, 19, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY); + + int oRaw; + char *cRaw = BN_bn2dec(bnrand); + + sscanf(cRaw, "%d", &oRaw); + nRaw += (oRaw &= 0xF423F); // ensure our serial is less than 999999 + + if (options.verbose) { + fmt::print("> PID: {:09d}\n", nRaw); + } + + // generate a key + BN_sub(privateKey, genOrder, privateKey); + nRaw <<= 1; + + for (int i = 0; i < total; i++) { + BINK1998::Generate(eCurve, genPoint, genOrder, privateKey, nRaw, pKey); + CLI::print_product_key(pKey); + fmt::print("\n\n"); + + // verify the key + count += BINK1998::Verify(eCurve, genPoint, pubPoint, pKey); + } + + fmt::print("Success count: {}/{}\n", count, total); + return 0; +} + +int CLI::BINK2002() { + DWORD pChannelID = options.channelID << 1; + + if (options.verbose) { + fmt::print("> Channel ID: {:03d}\n", options.channelID); + } + + // generate a key + for (int i = 0; i < total; i++) { + DWORD pAuthInfo; + RAND_bytes((BYTE *)&pAuthInfo, 4); + pAuthInfo &= 0x3ff; + + if (options.verbose) { + fmt::print("> AuthInfo: {}\n", pAuthInfo); + } + + BINK2002::Generate(eCurve, genPoint, genOrder, privateKey, pChannelID, pAuthInfo, pKey); + CLI::print_product_key(pKey); + fmt::print("\n\n"); + + // verify a key + count += BINK2002::Verify(eCurve, genPoint, pubPoint, pKey); + } + + fmt::print("Success count: {}/{}\n", count, total); + return 0; +} + +int CLI::ConfirmationID() { + char confirmation_id[49]; + int err = ConfirmationID::Generate(options.instid.c_str(), confirmation_id); + + switch (err) { + case ERR_TOO_SHORT: + fmt::print("ERROR: Installation ID is too short.\n"); + return 1; + case ERR_TOO_LARGE: + fmt::print("ERROR: Installation ID is too long.\n"); + return 1; + case ERR_INVALID_CHARACTER: + fmt::print("ERROR: Invalid character in installation ID.\n"); + return 1; + case ERR_INVALID_CHECK_DIGIT: + fmt::print("ERROR: Installation ID checksum failed. Please check that it is typed correctly.\n"); + return 1; + case ERR_UNKNOWN_VERSION: + fmt::print("ERROR: Unknown installation ID version.\n"); + return 1; + case ERR_UNLUCKY: + fmt::print("ERROR: Unable to generate valid confirmation ID.\n"); + return 1; + case SUCCESS: + fmt::print("Confirmation ID: {}\n", confirmation_id); + return 0; + + default: + fmt::print("Unknown error occurred during Confirmation ID generation: {}\n", err); + } + return 1; +} + + diff --git a/src/cli.h b/src/cli.h new file mode 100644 index 0000000..8602402 --- /dev/null +++ b/src/cli.h @@ -0,0 +1,35 @@ +// +// Created by neo on 6/6/2023. +// + +#ifndef WINDOWSXPKG_CLI_H +#define WINDOWSXPKG_CLI_H + +#include "header.h" + +class CLI { + Options options; + json keys; + const char* BINKID; + BIGNUM *privateKey, *genOrder; + EC_POINT *genPoint, *pubPoint; + EC_GROUP *eCurve; + char pKey[25]; + int count, total; + +public: + CLI(Options options, json keys); + + static bool loadJSON(const fs::path& filename, json *output); + static void showHelp(char *argv[]); + static int parseCommandLine(int argc, char* argv[], Options *options); + static int validateCommandLine(Options* options, char *argv[], json *keys); + static void print_product_id(DWORD *pid); + static void print_product_key(char *pk); + + int BINK1998(); + int BINK2002(); + int ConfirmationID(); +}; + +#endif //WINDOWSXPKG_CLI_H diff --git a/src/confid.cpp b/src/confid.cpp index f791537..d447598 100644 --- a/src/confid.cpp +++ b/src/confid.cpp @@ -2,32 +2,24 @@ // Created by WitherOrNot on 06/02/2023. // -#include "header.h" - -typedef int64_t i64; -typedef uint64_t ui64; +#include "confid.h" #define MOD 0x16A6B036D7F2A79ULL #define NON_RESIDUE 43 -static const ui64 f[6] = {0, 0x21840136C85381ULL, 0x44197B83892AD0ULL, 0x1400606322B3B04ULL, 0x1400606322B3B04ULL, 1}; +static const QWORD f[6] = {0, 0x21840136C85381ULL, 0x44197B83892AD0ULL, 0x1400606322B3B04ULL, 0x1400606322B3B04ULL, 1}; -typedef struct { - ui64 u[2]; - ui64 v[2]; -} TDivisor; - -static ui64 residue_add(ui64 x, ui64 y) +QWORD ConfirmationID::residue_add(QWORD x, QWORD y) { - ui64 z = x + y; + QWORD z = x + y; //z = z - (z >= MOD ? MOD : 0); if (z >= MOD) z -= MOD; return z; } -static ui64 residue_sub(ui64 x, ui64 y) +QWORD ConfirmationID::residue_sub(QWORD x, QWORD y) { - ui64 z = x - y; + QWORD z = x - y; //z += (x < y ? MOD : 0); if (x < y) z += MOD; @@ -36,82 +28,82 @@ static ui64 residue_sub(ui64 x, ui64 y) #if defined(__x86_64__) || defined(_M_AMD64) || defined(__aarch64__) || (defined(__arm64__) && defined(__APPLE__)) #ifdef __GNUC__ -static inline uint64_t __umul128(uint64_t a, uint64_t b, uint64_t* hi) +inline QWORD ConfirmationID::__umul128(QWORD a, QWORD b, QWORD* hi) { - unsigned __int128 r = (unsigned __int128) a * (unsigned __int128) b; + DQWORD r = (DQWORD)a * (DQWORD)b; *hi = r >> 64; - return (uint64_t) r; + return (QWORD) r; } #else #define __umul128 _umul128 #endif #elif defined(__i386__) || defined(_M_IX86) || defined(__arm__) -static inline uint64_t __umul128(uint64_t multiplier, uint64_t multiplicand, uint64_t *product_hi) { +inline QWORD ConfirmationID::__umul128(QWORD multiplier, QWORD multiplicand, QWORD *product_hi) { // multiplier = ab = a * 2^32 + b // multiplicand = cd = c * 2^32 + d // ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d - uint64_t a = multiplier >> 32; - uint64_t b = multiplier & 0xFFFFFFFF; - uint64_t c = multiplicand >> 32; - uint64_t d = multiplicand & 0xFFFFFFFF; + QWORD a = multiplier >> 32; + QWORD b = multiplier & 0xFFFFFFFF; + QWORD c = multiplicand >> 32; + QWORD d = multiplicand & 0xFFFFFFFF; - //uint64_t ac = a * c; - uint64_t ad = a * d; - //uint64_t bc = b * c; - uint64_t bd = b * d; + //QWORD ac = a * c; + QWORD ad = a * d; + //QWORD bc = b * c; + QWORD bd = b * d; - uint64_t adbc = ad + (b * c); - uint64_t adbc_carry = adbc < ad ? 1 : 0; + QWORD adbc = ad + (b * c); + QWORD adbc_carry = adbc < ad ? 1 : 0; // multiplier * multiplicand = product_hi * 2^64 + product_lo - uint64_t product_lo = bd + (adbc << 32); - uint64_t product_lo_carry = product_lo < bd ? 1 : 0; + QWORD product_lo = bd + (adbc << 32); + QWORD product_lo_carry = product_lo < bd ? 1 : 0; *product_hi = (a * c) + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry; return product_lo; } #endif -static ui64 ui128_quotient_mod(ui64 lo, ui64 hi) +QWORD ConfirmationID::ui128_quotient_mod(QWORD lo, QWORD hi) { // hi:lo * ceil(2**170/MOD) >> (64 + 64 + 42) - ui64 prod1; + QWORD prod1; __umul128(lo, 0x604fa6a1c6346a87, &prod1); - ui64 part1hi; - ui64 part1lo = __umul128(lo, 0x2d351c6d04f8b, &part1hi); - ui64 part2hi; - ui64 part2lo = __umul128(hi, 0x604fa6a1c6346a87, &part2hi); - ui64 sum1 = part1lo + part2lo; + QWORD part1hi; + QWORD part1lo = __umul128(lo, 0x2d351c6d04f8b, &part1hi); + QWORD part2hi; + QWORD part2lo = __umul128(hi, 0x604fa6a1c6346a87, &part2hi); + QWORD sum1 = part1lo + part2lo; unsigned sum1carry = (sum1 < part1lo); sum1 += prod1; sum1carry += (sum1 < prod1); - ui64 prod2 = part1hi + part2hi + sum1carry; - ui64 prod3hi; - ui64 prod3lo = __umul128(hi, 0x2d351c6d04f8b, &prod3hi); + QWORD prod2 = part1hi + part2hi + sum1carry; + QWORD prod3hi; + QWORD prod3lo = __umul128(hi, 0x2d351c6d04f8b, &prod3hi); prod3lo += prod2; prod3hi += (prod3lo < prod2); return (prod3lo >> 42) | (prod3hi << 22); } -static ui64 residue_mul(ui64 x, ui64 y) +QWORD ConfirmationID::residue_mul(QWORD x, QWORD y) { // * ceil(2**170/MOD) = 0x2d351 c6d04f8b|604fa6a1 c6346a87 for (p-1)*(p-1) max - ui64 hi; - ui64 lo = __umul128(x, y, &hi); - ui64 quotient = ui128_quotient_mod(lo, hi); + QWORD hi; + QWORD lo = __umul128(x, y, &hi); + QWORD quotient = ui128_quotient_mod(lo, hi); return lo - quotient * MOD; } -static ui64 residue_pow(ui64 x, ui64 y) +QWORD ConfirmationID::residue_pow(QWORD x, QWORD y) { if (y == 0) return 1; - ui64 cur = x; + QWORD cur = x; while (!(y & 1)) { cur = residue_mul(cur, cur); y >>= 1; } - ui64 res = cur; + QWORD res = cur; while ((y >>= 1) != 0) { cur = residue_mul(cur, cur); if (y & 1) @@ -120,14 +112,14 @@ static ui64 residue_pow(ui64 x, ui64 y) return res; } -static ui64 inverse(ui64 u, ui64 v) +QWORD ConfirmationID::inverse(QWORD u, QWORD v) { //assert(u); - i64 tmp; - i64 xu = 1, xv = 0; - ui64 v0 = v; + int64_t tmp; + int64_t xu = 1, xv = 0; + QWORD v0 = v; while (u > 1) { - ui64 d = v / u; ui64 remainder = v % u; + QWORD d = v / u; QWORD remainder = v % u; tmp = u; u = remainder; v = tmp; tmp = xu; xu = xv - d * xu; xv = tmp; } @@ -135,20 +127,27 @@ static ui64 inverse(ui64 u, ui64 v) return xu; } -static ui64 residue_inv(ui64 x) -{ return inverse(x, MOD); } -//{ return residue_pow(x, MOD - 2); } +QWORD ConfirmationID::residue_inv(QWORD x) +{ + return inverse(x, MOD); + // return residue_pow(x, MOD - 2); +} #define BAD 0xFFFFFFFFFFFFFFFFull -static ui64 residue_sqrt(ui64 what) +QWORD ConfirmationID::residue_sqrt(QWORD what) { - if (!what) - return 0; - ui64 g = NON_RESIDUE, z, y, r, x, b, t; - ui64 e = 0, q = MOD - 1; - while (!(q & 1)) - e++, q >>= 1; + if (!what) { + return 0; + } + + QWORD g = NON_RESIDUE, z, y, r, x, b, t; + QWORD e = 0, q = MOD - 1; + + while (!(q & 1)) { + e++, q >>= 1; + } + z = residue_pow(g, q); y = z; r = e; @@ -156,39 +155,46 @@ static ui64 residue_sqrt(ui64 what) b = residue_mul(residue_mul(what, x), x); x = residue_mul(what, x); while (b != 1) { - ui64 m = 0, b2 = b; - do { + QWORD m = 0, b2 = b; + + do { m++; b2 = residue_mul(b2, b2); } while (b2 != 1); - if (m == r) - return BAD; + + if (m == r) { + return BAD; + } + t = residue_pow(y, 1 << (r - m - 1)); y = residue_mul(t, t); r = m; x = residue_mul(x, t); b = residue_mul(b, y); } + if (residue_mul(x, x) != what) { //printf("internal error in sqrt\n"); return BAD; } + return x; } -int find_divisor_v(TDivisor* d) +int ConfirmationID::find_divisor_v(TDivisor* d) { // u | v^2 - f // u = u0 + u1*x + x^2 // f%u = f0 + f1*x - ui64 v1; - ui64 f2[6]; - int i, j; - for (i = 0; i < 6; i++) - f2[i] = f[i]; - const ui64 u0 = d->u[0]; - const ui64 u1 = d->u[1]; - for (j = 4; j--; ) { + QWORD v1, f2[6]; + + for (int i = 0; i < 6; i++) { + f2[i] = f[i]; + } + + const QWORD u0 = d->u[0]; + const QWORD u1 = d->u[1]; + for (int j = 4; j--; ) { f2[j] = residue_sub(f2[j], residue_mul(u0, f2[j + 2])); f2[j + 1] = residue_sub(f2[j + 1], residue_mul(u1, f2[j + 2])); f2[j + 2] = 0; @@ -200,11 +206,11 @@ int find_divisor_v(TDivisor* d) // v0^2 = f0 + u0*v1^2 = (f1 + u1*v1^2)^2 / (2*v1)^2 // (f1^2) + 2*(f1*u1-2*f0) * v1^2 + (u1^2-4*u0) * v1^4 = 0 // v1^2 = ((2*f0-f1*u1) +- 2*sqrt(-f0*f1*u1 + f0^2 + f1^2*u0))) / (u1^2-4*u0) - const ui64 f0 = f2[0]; - const ui64 f1 = f2[1]; - const ui64 u0double = residue_add(u0, u0); - const ui64 coeff2 = residue_sub(residue_mul(u1, u1), residue_add(u0double, u0double)); - const ui64 coeff1 = residue_sub(residue_add(f0, f0), residue_mul(f1, u1)); + const QWORD f0 = f2[0]; + const QWORD f1 = f2[1]; + const QWORD u0double = residue_add(u0, u0); + const QWORD coeff2 = residue_sub(residue_mul(u1, u1), residue_add(u0double, u0double)); + const QWORD coeff1 = residue_sub(residue_add(f0, f0), residue_mul(f1, u1)); if (coeff2 == 0) { if (coeff1 == 0) { if (f1 == 0) { @@ -213,34 +219,39 @@ int find_divisor_v(TDivisor* d) } return 0; } - ui64 sqr = residue_mul(residue_mul(f1, f1), residue_inv(residue_add(coeff1, coeff1))); + QWORD sqr = residue_mul(residue_mul(f1, f1), residue_inv(residue_add(coeff1, coeff1))); v1 = residue_sqrt(sqr); - if (v1 == BAD) - return 0; + if (v1 == BAD) { + return 0; + } } else { - ui64 d = residue_add(residue_mul(f0, f0), residue_mul(f1, residue_sub(residue_mul(f1, u0), residue_mul(f0, u1)))); + QWORD d = residue_add(residue_mul(f0, f0), residue_mul(f1, residue_sub(residue_mul(f1, u0), residue_mul(f0, u1)))); d = residue_sqrt(d); - if (d == BAD) - return 0; + if (d == BAD) { + return 0; + } + d = residue_add(d, d); - ui64 inv = residue_inv(coeff2); - ui64 root = residue_mul(residue_add(coeff1, d), inv); + QWORD inv = residue_inv(coeff2); + QWORD root = residue_mul(residue_add(coeff1, d), inv); v1 = residue_sqrt(root); if (v1 == BAD) { root = residue_mul(residue_sub(coeff1, d), inv); v1 = residue_sqrt(root); - if (v1 == BAD) - return 0; + if (v1 == BAD) { + return 0; + } } } - ui64 v0 = residue_mul(residue_add(f1, residue_mul(u1, residue_mul(v1, v1))), residue_inv(residue_add(v1, v1))); + + QWORD v0 = residue_mul(residue_add(f1, residue_mul(u1, residue_mul(v1, v1))), residue_inv(residue_add(v1, v1))); d->v[0] = v0; d->v[1] = v1; return 1; } // generic short slow code -static int polynomial_mul(int adeg, const ui64 a[], int bdeg, const ui64 b[], int resultprevdeg, ui64 result[]) +int ConfirmationID::polynomial_mul(int adeg, const QWORD a[], int bdeg, const QWORD b[], int resultprevdeg, QWORD result[]) { if (adeg < 0 || bdeg < 0) return resultprevdeg; @@ -255,13 +266,14 @@ static int polynomial_mul(int adeg, const ui64 a[], int bdeg, const ui64 b[], in --resultprevdeg; return resultprevdeg; } -static int polynomial_div_monic(int adeg, ui64 a[], int bdeg, const ui64 b[], ui64* quotient) + +int ConfirmationID::polynomial_div_monic(int adeg, QWORD a[], int bdeg, const QWORD b[], QWORD* quotient) { assert(bdeg >= 0); assert(b[bdeg] == 1); int i, j; for (i = adeg - bdeg; i >= 0; i--) { - ui64 q = a[i + bdeg]; + QWORD q = a[i + bdeg]; if (quotient) quotient[i] = q; for (j = 0; j < bdeg; j++) @@ -273,18 +285,18 @@ static int polynomial_div_monic(int adeg, ui64 a[], int bdeg, const ui64 b[], ui i--; return i; } -static void polynomial_xgcd(int adeg, const ui64 a[3], int bdeg, const ui64 b[3], int* pgcddeg, ui64 gcd[3], int* pmult1deg, ui64 mult1[3], int* pmult2deg, ui64 mult2[3]) +void ConfirmationID::polynomial_xgcd(int adeg, const QWORD a[3], int bdeg, const QWORD b[3], int* pgcddeg, QWORD gcd[3], int* pmult1deg, QWORD mult1[3], int* pmult2deg, QWORD mult2[3]) { int sdeg = -1; - ui64 s[3] = {0, 0, 0}; + QWORD s[3] = {0, 0, 0}; int mult1deg = 0; mult1[0] = 1; mult1[1] = 0; mult1[2] = 0; int tdeg = 0; - ui64 t[3] = {1, 0, 0}; + QWORD t[3] = {1, 0, 0}; int mult2deg = -1; mult2[0] = 0; mult2[1] = 0; mult2[2] = 0; int rdeg = bdeg; - ui64 r[3] = {b[0], b[1], b[2]}; + QWORD r[3] = {b[0], b[1], b[2]}; int gcddeg = adeg; gcd[0] = a[0]; gcd[1] = a[1]; gcd[2] = a[2]; // s*u1 + t*u2 = r @@ -296,7 +308,7 @@ static void polynomial_xgcd(int adeg, const ui64 a[3], int bdeg, const ui64 b[3] tmp = rdeg; rdeg = gcddeg; gcddeg = tmp; tmpi = sdeg; sdeg = mult1deg; mult1deg = tmpi; tmpi = tdeg; tdeg = mult2deg; mult2deg = tmpi; - ui64 tmp2; + QWORD tmp2; tmp2 = r[0]; r[0] = gcd[0]; gcd[0] = tmp2; tmp2 = r[1]; r[1] = gcd[1]; gcd[1] = tmp2; tmp2 = r[2]; r[2] = gcd[2]; gcd[2] = tmp2; @@ -309,7 +321,7 @@ static void polynomial_xgcd(int adeg, const ui64 a[3], int bdeg, const ui64 b[3] continue; } int delta = gcddeg - rdeg; - ui64 mult = residue_mul(gcd[gcddeg], residue_inv(r[rdeg])); + QWORD mult = residue_mul(gcd[gcddeg], residue_inv(r[rdeg])); // quotient = mult * x**delta assert(rdeg + delta < 3); for (int i = 0; i <= rdeg; i++) @@ -336,7 +348,8 @@ static void polynomial_xgcd(int adeg, const ui64 a[3], int bdeg, const ui64 b[3] *pmult1deg = mult1deg; *pmult2deg = mult2deg; } -static int u2poly(const TDivisor* src, ui64 polyu[3], ui64 polyv[2]) + +int ConfirmationID::u2poly(const TDivisor* src, QWORD polyu[3], QWORD polyv[2]) { if (src->u[1] != BAD) { polyu[0] = src->u[0]; @@ -358,27 +371,28 @@ static int u2poly(const TDivisor* src, ui64 polyu[3], ui64 polyv[2]) polyv[1] = 0; return 0; } -static void divisor_add(const TDivisor* src1, const TDivisor* src2, TDivisor* dst) + +void ConfirmationID::divisor_add(const TDivisor* src1, const TDivisor* src2, TDivisor* dst) { - ui64 u1[3], u2[3], v1[2], v2[2]; + QWORD u1[3], u2[3], v1[2], v2[2]; int u1deg = u2poly(src1, u1, v1); int u2deg = u2poly(src2, u2, v2); // extended gcd: d1 = gcd(u1, u2) = e1*u1 + e2*u2 int d1deg, e1deg, e2deg; - ui64 d1[3], e1[3], e2[3]; + QWORD d1[3], e1[3], e2[3]; polynomial_xgcd(u1deg, u1, u2deg, u2, &d1deg, d1, &e1deg, e1, &e2deg, e2); assert(e1deg <= 1); assert(e2deg <= 1); // extended gcd again: d = gcd(d1, v1+v2) = c1*d1 + c2*(v1+v2) - ui64 b[3] = {residue_add(v1[0], v2[0]), residue_add(v1[1], v2[1]), 0}; + QWORD b[3] = {residue_add(v1[0], v2[0]), residue_add(v1[1], v2[1]), 0}; int bdeg = (b[1] == 0 ? (b[0] == 0 ? -1 : 0) : 1); int ddeg, c1deg, c2deg; - ui64 d[3], c1[3], c2[3]; + QWORD d[3], c1[3], c2[3]; polynomial_xgcd(d1deg, d1, bdeg, b, &ddeg, d, &c1deg, c1, &c2deg, c2); assert(c1deg <= 0); assert(c2deg <= 1); assert(ddeg >= 0); - ui64 dmult = residue_inv(d[ddeg]); + QWORD dmult = residue_inv(d[ddeg]); int i; for (i = 0; i < ddeg; i++) d[i] = residue_mul(d[i], dmult); @@ -387,10 +401,10 @@ static void divisor_add(const TDivisor* src1, const TDivisor* src2, TDivisor* ds c1[i] = residue_mul(c1[i], dmult); for (i = 0; i <= c2deg; i++) c2[i] = residue_mul(c2[i], dmult); - ui64 u[5]; + QWORD u[5]; int udeg = polynomial_mul(u1deg, u1, u2deg, u2, -1, u); // u is monic - ui64 v[7], tmp[7]; + QWORD v[7], tmp[7]; int vdeg, tmpdeg; // c1*(e1*u1*v2 + e2*u2*v1) + c2*(v1*v2 + f) // c1*(e1*u1*(v2-v1) + d1*v1) + c2*(v1*v2 + f) @@ -407,7 +421,7 @@ static void divisor_add(const TDivisor* src1, const TDivisor* src2, TDivisor* ds vdeg = polynomial_mul(c2deg, c2, tmpdeg, tmp, vdeg, v); if (ddeg > 0) { assert(udeg >= 2*ddeg); - ui64 udiv[5]; + QWORD udiv[5]; polynomial_div_monic(udeg, u, ddeg, d, udiv); udeg -= ddeg; polynomial_div_monic(udeg, udiv, ddeg, d, u); udeg -= ddeg; if (vdeg >= 0) { @@ -429,10 +443,10 @@ static void divisor_add(const TDivisor* src1, const TDivisor* src2, TDivisor* ds for (; i <= 5; i++) tmp[i] = f[i]; tmpdeg = i - 1; - ui64 udiv[5]; + QWORD udiv[5]; polynomial_div_monic(tmpdeg, tmp, udeg, u, udiv); udeg = tmpdeg - udeg; - ui64 mult = residue_inv(udiv[udeg]); + QWORD mult = residue_inv(udiv[udeg]); for (i = 0; i < udeg; i++) u[i] = residue_mul(udiv[i], mult); u[i] = 1; @@ -458,9 +472,10 @@ static void divisor_add(const TDivisor* src1, const TDivisor* src2, TDivisor* ds dst->v[1] = BAD; } } + #define divisor_double(src, dst) divisor_add(src, src, dst) -static void divisor_mul(const TDivisor* src, ui64 mult, TDivisor* dst) +void ConfirmationID::divisor_mul(const TDivisor* src, QWORD mult, TDivisor* dst) { if (mult == 0) { dst->u[0] = BAD; @@ -482,7 +497,7 @@ static void divisor_mul(const TDivisor* src, ui64 mult, TDivisor* dst) } } -static void divisor_mul128(const TDivisor* src, ui64 mult_lo, ui64 mult_hi, TDivisor* dst) +void ConfirmationID::divisor_mul128(const TDivisor* src, QWORD mult_lo, QWORD mult_hi, TDivisor* dst) { if (mult_lo == 0 && mult_hi == 0) { dst->u[0] = BAD; @@ -513,13 +528,13 @@ static void divisor_mul128(const TDivisor* src, ui64 mult_lo, ui64 mult_hi, TDiv } } -static unsigned rol(unsigned x, int shift) +unsigned ConfirmationID::rol(unsigned x, int shift) { //assert(shift > 0 && shift < 32); return (x << shift) | (x >> (32 - shift)); } -static void sha1_single_block(unsigned char input[64], unsigned char output[20]) +void ConfirmationID::sha1_single_block(unsigned char input[64], unsigned char output[20]) { unsigned a, b, c, d, e; a = 0x67452301; @@ -577,7 +592,7 @@ static void sha1_single_block(unsigned char input[64], unsigned char output[20]) output[16] = e >> 24; output[17] = e >> 16; output[18] = e >> 8; output[19] = e; } -static void Mix(unsigned char* buffer, size_t bufSize, const unsigned char* key, size_t keySize) +void ConfirmationID::Mix(unsigned char* buffer, size_t bufSize, const unsigned char* key, size_t keySize) { unsigned char sha1_input[64]; unsigned char sha1_result[20]; @@ -591,7 +606,7 @@ static void Mix(unsigned char* buffer, size_t bufSize, const unsigned char* key, sha1_input[half + keySize] = 0x80; sha1_input[sizeof(sha1_input) - 1] = (half + keySize) * 8; sha1_input[sizeof(sha1_input) - 2] = (half + keySize) * 8 / 0x100; - sha1_single_block(sha1_input, sha1_result); + sha1_single_block(sha1_input, sha1_result); size_t i; for (i = half & ~3; i < half; i++) sha1_result[i] = sha1_result[i + 4 - (half & 3)]; @@ -603,7 +618,7 @@ static void Mix(unsigned char* buffer, size_t bufSize, const unsigned char* key, } } -static void Unmix(unsigned char* buffer, size_t bufSize, const unsigned char* key, size_t keySize) +void ConfirmationID::Unmix(unsigned char* buffer, size_t bufSize, const unsigned char* key, size_t keySize) { unsigned char sha1_input[64]; unsigned char sha1_result[20]; @@ -617,7 +632,7 @@ static void Unmix(unsigned char* buffer, size_t bufSize, const unsigned char* ke sha1_input[half + keySize] = 0x80; sha1_input[sizeof(sha1_input) - 1] = (half + keySize) * 8; sha1_input[sizeof(sha1_input) - 2] = (half + keySize) * 8 / 0x100; - sha1_single_block(sha1_input, sha1_result); + sha1_single_block(sha1_input, sha1_result); size_t i; for (i = half & ~3; i < half; i++) sha1_result[i] = sha1_result[i + 4 - (half & 3)]; @@ -629,7 +644,7 @@ static void Unmix(unsigned char* buffer, size_t bufSize, const unsigned char* ke } } -int generateConfId(const char* installation_id_str, char confirmation_id[49]) +int ConfirmationID::Generate(const char* installation_id_str, char confirmation_id[49]) { unsigned char installation_id[19]; // 10**45 < 256**19 size_t installation_id_len = 0; @@ -679,8 +694,8 @@ int generateConfId(const char* installation_id_str, char confirmation_id[49]) #pragma pack(push, 1) struct { - ui64 HardwareID; - ui64 ProductIDLow; + QWORD HardwareID; + QWORD ProductIDLow; unsigned char ProductIDHigh; unsigned short KeySHA1; } parsed; @@ -697,7 +712,7 @@ int generateConfId(const char* installation_id_str, char confirmation_id[49]) unsigned char keybuf[16]; memcpy(keybuf, &parsed.HardwareID, 8); - ui64 productIdMixed = (ui64)productId1 << 41 | (ui64)productId2 << 58 | (ui64)productId3 << 17 | productId4; + QWORD productIdMixed = (QWORD)productId1 << 41 | (QWORD)productId2 << 58 | (QWORD)productId3 << 17 | productId4; memcpy(keybuf + 8, &productIdMixed, 8); TDivisor d; @@ -706,16 +721,16 @@ int generateConfId(const char* installation_id_str, char confirmation_id[49]) union { unsigned char buffer[14]; struct { - ui64 lo; - ui64 hi; + QWORD lo; + QWORD hi; }; } u; u.lo = 0; u.hi = 0; u.buffer[7] = attempt; Mix(u.buffer, 14, keybuf, 16); - ui64 x2 = ui128_quotient_mod(u.lo, u.hi); - ui64 x1 = u.lo - x2 * MOD; + QWORD x2 = ui128_quotient_mod(u.lo, u.hi); + QWORD x1 = u.lo - 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); @@ -727,7 +742,7 @@ int generateConfId(const char* installation_id_str, char confirmation_id[49]) divisor_mul128(&d, 0x04e21b9d10f127c1, 0x40da7c36d44c, &d); union { struct { - ui64 encoded_lo, encoded_hi; + QWORD encoded_lo, encoded_hi; }; struct { uint32_t encoded[4]; @@ -743,9 +758,9 @@ int generateConfId(const char* installation_id_str, char confirmation_id[49]) e.encoded_lo += MOD; e.encoded_hi += (e.encoded_lo < MOD); } else { - ui64 x1 = (d.u[1] % 2 ? d.u[1] + MOD : d.u[1]) / 2; - ui64 x2sqr = residue_sub(residue_mul(x1, x1), d.u[0]); - ui64 x2 = residue_sqrt(x2sqr); + QWORD x1 = (d.u[1] % 2 ? d.u[1] + MOD : d.u[1]) / 2; + QWORD x2sqr = residue_sub(residue_mul(x1, x1), d.u[0]); + QWORD x2 = residue_sqrt(x2sqr); if (x2 == BAD) { x2 = residue_sqrt(residue_mul(x2sqr, residue_inv(NON_RESIDUE))); assert(x2 != BAD); @@ -754,17 +769,17 @@ int generateConfId(const char* installation_id_str, char confirmation_id[49]) e.encoded_hi += (e.encoded_lo < x1); } else { // points (-x1+x2, v(-x1+x2)) and (-x1-x2, v(-x1-x2)) - ui64 x1a = residue_sub(x1, x2); - ui64 y1 = residue_sub(d.v[0], residue_mul(d.v[1], x1a)); - ui64 x2a = residue_add(x1, x2); - ui64 y2 = residue_sub(d.v[0], residue_mul(d.v[1], x2a)); + QWORD x1a = residue_sub(x1, x2); + QWORD y1 = residue_sub(d.v[0], residue_mul(d.v[1], x1a)); + QWORD x2a = residue_add(x1, x2); + QWORD y2 = residue_sub(d.v[0], residue_mul(d.v[1], x2a)); if (x1a > x2a) { - ui64 tmp = x1a; + QWORD tmp = x1a; x1a = x2a; x2a = tmp; } if ((y1 ^ y2) & 1) { - ui64 tmp = x1a; + QWORD tmp = x1a; x1a = x2a; x2a = tmp; } @@ -777,14 +792,15 @@ int generateConfId(const char* installation_id_str, char confirmation_id[49]) for (i = 0; i < 35; i++) { unsigned c = e.encoded[3] % 10; e.encoded[3] /= 10; - unsigned c2 = ((ui64)c << 32 | e.encoded[2]) % 10; - e.encoded[2] = ((ui64)c << 32 | e.encoded[2]) / 10; - unsigned c3 = ((ui64)c2 << 32 | e.encoded[1]) % 10; - e.encoded[1] = ((ui64)c2 << 32 | e.encoded[1]) / 10; - unsigned c4 = ((ui64)c3 << 32 | e.encoded[0]) % 10; - e.encoded[0] = ((ui64)c3 << 32 | e.encoded[0]) / 10; + unsigned c2 = ((QWORD)c << 32 | e.encoded[2]) % 10; + e.encoded[2] = ((QWORD)c << 32 | e.encoded[2]) / 10; + unsigned c3 = ((QWORD)c2 << 32 | e.encoded[1]) % 10; + e.encoded[1] = ((QWORD)c2 << 32 | e.encoded[1]) / 10; + unsigned c4 = ((QWORD)c3 << 32 | e.encoded[0]) % 10; + e.encoded[0] = ((QWORD)c3 << 32 | e.encoded[0]) / 10; decimal[34 - i] = c4; } + assert(e.encoded[0] == 0 && e.encoded[1] == 0 && e.encoded[2] == 0 && e.encoded[3] == 0); char* q = confirmation_id; for (i = 0; i < 7; i++) { @@ -801,4 +817,4 @@ int generateConfId(const char* installation_id_str, char confirmation_id[49]) } *q++ = 0; return 0; -} \ No newline at end of file +} diff --git a/src/confid.h b/src/confid.h new file mode 100644 index 0000000..f9dcd89 --- /dev/null +++ b/src/confid.h @@ -0,0 +1,53 @@ +// +// Created by neo on 6/6/2023. +// + +#ifndef WINDOWSXPKG_CONFID_H +#define WINDOWSXPKG_CONFID_H + +#include "header.h" + +// Confirmation ID generator constants +#define SUCCESS 0 +#define ERR_TOO_SHORT 1 +#define ERR_TOO_LARGE 2 +#define ERR_INVALID_CHARACTER 3 +#define ERR_INVALID_CHECK_DIGIT 4 +#define ERR_UNKNOWN_VERSION 5 +#define ERR_UNLUCKY 6 + + +typedef struct { + QWORD u[2]; + QWORD v[2]; +} TDivisor; + +class ConfirmationID { + static QWORD residue_add(QWORD x, QWORD y); + static QWORD residue_sub(QWORD x, QWORD y); + static QWORD __umul128(QWORD a, QWORD b, QWORD* hi); + static QWORD ui128_quotient_mod(QWORD lo, QWORD hi); + static QWORD residue_mul(QWORD x, QWORD y); + static QWORD residue_pow(QWORD x, QWORD y); + static QWORD inverse(QWORD u, QWORD v); + static QWORD residue_inv(QWORD x); + static QWORD residue_sqrt(QWORD what); + static int find_divisor_v(TDivisor* d); + static int polynomial_mul(int adeg, const QWORD a[], int bdeg, const QWORD b[], int resultprevdeg, QWORD result[]); + static int polynomial_div_monic(int adeg, QWORD a[], int bdeg, const QWORD b[], QWORD* quotient); + static void polynomial_xgcd(int adeg, const QWORD a[3], int bdeg, const QWORD b[3], int* pgcddeg, QWORD gcd[3], int* pmult1deg, QWORD mult1[3], int* pmult2deg, QWORD mult2[3]); + static int u2poly(const TDivisor* src, QWORD polyu[3], QWORD polyv[2]); + static void divisor_add(const TDivisor* src1, const TDivisor* src2, TDivisor* dst); + static void divisor_mul(const TDivisor* src, QWORD mult, TDivisor* dst); + static void divisor_mul128(const TDivisor* src, QWORD mult_lo, QWORD mult_hi, TDivisor* dst); + static unsigned rol(unsigned x, int shift); + static void sha1_single_block(unsigned char input[64], unsigned char output[20]); + static void Mix(unsigned char* buffer, size_t bufSize, const unsigned char* key, size_t keySize); + static void Unmix(unsigned char* buffer, size_t bufSize, const unsigned char* key, size_t keySize); + +public: + static int Generate(const char* installation_id_str, char confirmation_id[49]); + static int CLIRun(); +}; + +#endif //WINDOWSXPKG_CONFID_H diff --git a/src/header.h b/src/header.h index 8e410ef..e047a9f 100644 --- a/src/header.h +++ b/src/header.h @@ -57,14 +57,27 @@ using json = nlohmann::json; namespace fs = std::filesystem; -// Confirmation ID generator constants -#define SUCCESS 0 -#define ERR_TOO_SHORT 1 -#define ERR_TOO_LARGE 2 -#define ERR_INVALID_CHARACTER 3 -#define ERR_INVALID_CHECK_DIGIT 4 -#define ERR_UNKNOWN_VERSION 5 -#define ERR_UNLUCKY 6 +enum MODE { + MODE_BINK1998 = 0, + MODE_BINK2002 = 1, + MODE_CONFIRMATION_ID = 2, +}; + +struct Options { + std::string binkid; + std::string keysFilename; + std::string instid; + int channelID; + int numKeys; + bool verbose; + bool help; + bool error; + bool list; + + MODE applicationMode; +}; + +extern Options options; // Type definitions typedef bool BOOL; @@ -73,8 +86,11 @@ typedef uint16_t WORD; typedef uint32_t DWORD; typedef uint64_t QWORD; +#ifdef __SIZEOF_INT128__ +typedef unsigned __int128 DQWORD; +#endif + // Global variables -extern char pCharset[]; // util.cpp int BN_bn2lebin(const BIGNUM *a, unsigned char *to, int tolen); // Hello OpenSSL developers, please tell me, where is this function at? @@ -96,65 +112,5 @@ EC_GROUP *initializeEllipticCurve( void unbase24(BYTE *byteSeq, const char *cdKey); void base24(char *cdKey, BYTE *byteSeq); -// cli.cpp -void print_product_key(char *pk); -void print_product_id(DWORD *pid); - -struct Options { - std::string binkid; - int channelID; - std::string keysFilename; - int numKeys; - std::string instid; - bool help; - bool list; - bool isBink2002; - bool verbose; - bool error; -}; -extern Options options; - -int parseCommandLine(int argc, char* argv[], Options* output); -int validateCommandLine(Options* options, char* argv[], json* output); -void showHelp(char* argv[]); -bool loadJSON(const fs::path& filename, json *output); - -// xp.cpp -bool verifyXPKey( - EC_GROUP *eCurve, - EC_POINT *basePoint, - EC_POINT *publicKey, - char (&pKey)[25] -); - -void generateXPKey( - EC_GROUP *eCurve, - EC_POINT *basePoint, - BIGNUM *genOrder, - BIGNUM *privateKey, - DWORD pSerial, - char (&pKey)[25] -); - -// server.cpp -bool verifyServerKey( - EC_GROUP *eCurve, - EC_POINT *basePoint, - EC_POINT *publicKey, - char (&cdKey)[25] -); - -void generateServerKey( - EC_GROUP *eCurve, - EC_POINT *basePoint, - BIGNUM *genOrder, - BIGNUM *privateKey, - DWORD pChannelID, - DWORD pAuthInfo, - char (&pKey)[25] -); - -// confid.cpp -int generateConfId(const char* installation_id_str, char confirmation_id[49]); #endif //WINDOWSXPKG_HEADER_H diff --git a/src/key.cpp b/src/key.cpp index 5dc4e66..202a1b2 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -4,6 +4,8 @@ #include "header.h" +char pCharset[] = "BCDFGHJKMPQRTVWXY2346789"; + /* Converts from CD-key to a byte sequence. */ void unbase24(BYTE *byteSeq, const char *cdKey) { BYTE pDecodedKey[PK_LENGTH + NULL_TERMINATOR]{}; diff --git a/src/main.cpp b/src/main.cpp index 03e9fe5..a8e3c83 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,163 +3,37 @@ // #include "header.h" +#include "cli.h" -char pCharset[] = "BCDFGHJKMPQRTVWXY2346789"; Options options; int main(int argc, char *argv[]) { - if (!parseCommandLine(argc, argv, &options)) { + if (!CLI::parseCommandLine(argc, argv, &options)) { fmt::print("error parsing command line options\n"); - showHelp(argv); + CLI::showHelp(argv); return !options.error ? 0 : 1; } json keys; - int status = validateCommandLine(&options, argv, &keys); + int status = CLI::validateCommandLine(&options, argv, &keys); if (status > 0) { return status; } - const char* BINKID = options.binkid.c_str(); + CLI* run = new CLI(options, keys); - // We cannot produce a valid key without knowing the private key k. The reason for this is that - // we need the result of the function K(x; y) = kG(x; y). - BIGNUM *privateKey = BN_new(); + switch(options.applicationMode) { + case MODE_BINK1998: + return run->BINK1998(); - // We can, however, validate any given key using the available public key: {p, a, b, G, K}. - // genOrder the order of the generator G, a value we have to reverse -> Schoof's Algorithm. - BIGNUM *genOrder = BN_new(); + case MODE_BINK2002: + return run->BINK2002(); - /* Computed data */ - BN_dec2bn(&genOrder, keys["BINK"][BINKID]["n"].get().c_str()); - BN_dec2bn(&privateKey, keys["BINK"][BINKID]["priv"].get().c_str()); + case MODE_CONFIRMATION_ID: + return run->ConfirmationID(); - if (options.verbose) { - fmt::print("----------------------------------------------------------- \n"); - fmt::print("Loaded the following curve constraints: BINK[{}]\n", BINKID); - fmt::print("----------------------------------------------------------- \n"); - fmt::print(" P: {}\n", keys["BINK"][BINKID]["p"].get()); - fmt::print(" a: {}\n", keys["BINK"][BINKID]["a"].get()); - fmt::print(" b: {}\n", keys["BINK"][BINKID]["b"].get()); - fmt::print("Gx: {}\n", keys["BINK"][BINKID]["g"]["x"].get()); - fmt::print("Gy: {}\n", keys["BINK"][BINKID]["g"]["y"].get()); - fmt::print("Kx: {}\n", keys["BINK"][BINKID]["pub"]["x"].get()); - fmt::print("Ky: {}\n", keys["BINK"][BINKID]["pub"]["y"].get()); - fmt::print(" n: {}\n", keys["BINK"][BINKID]["n"].get()); - fmt::print(" k: {}\n", keys["BINK"][BINKID]["priv"].get()); + default: + return 1; } - - EC_POINT *genPoint, *pubPoint; - EC_GROUP *eCurve = initializeEllipticCurve( - keys["BINK"][BINKID]["p"].get(), - keys["BINK"][BINKID]["a"].get(), - keys["BINK"][BINKID]["b"].get(), - keys["BINK"][BINKID]["g"]["x"].get(), - keys["BINK"][BINKID]["g"]["y"].get(), - keys["BINK"][BINKID]["pub"]["x"].get(), - keys["BINK"][BINKID]["pub"]["y"].get(), - genPoint, - pubPoint - ); - - if (!options.instid.empty()) { - char confirmation_id[49]; - int err = generateConfId(options.instid.c_str(), confirmation_id); - - switch (err) { - case ERR_TOO_SHORT: - fmt::print("ERROR: Installation ID is too short.\n"); - return 1; - case ERR_TOO_LARGE: - fmt::print("ERROR: Installation ID is too long.\n"); - return 1; - case ERR_INVALID_CHARACTER: - fmt::print("ERROR: Invalid character in installation ID.\n"); - return 1; - case ERR_INVALID_CHECK_DIGIT: - fmt::print("ERROR: Installation ID checksum failed. Please check that it is typed correctly.\n"); - return 1; - case ERR_UNKNOWN_VERSION: - fmt::print("ERROR: Unknown installation ID version.\n"); - return 1; - case ERR_UNLUCKY: - fmt::print("ERROR: Unable to generate valid confirmation ID.\n"); - return 1; - case SUCCESS: - fmt::print("Confirmation ID: {}\n", confirmation_id); - return 0; - - default: - fmt::print("Unknown error occurred during Confirmation ID generation: {}\n", err); - } - return 1; - } - - // Calculation - char pKey[25]; - int count = 0, total = options.numKeys; - - // BINK2002 Generation - if (options.isBink2002) { - DWORD pChannelID = options.channelID << 1; - - if (options.verbose) { - fmt::print("> Channel ID: {:03d}\n", options.channelID); - } - - // generate a key - for (int i = 0; i < total; i++) { - DWORD pAuthInfo; - RAND_bytes((BYTE *)&pAuthInfo, 4); - pAuthInfo &= 0x3ff; - - if (options.verbose) { - fmt::print("> AuthInfo: {}\n", pAuthInfo); - } - - generateServerKey(eCurve, genPoint, genOrder, privateKey, pChannelID, pAuthInfo, pKey); - print_product_key(pKey); - fmt::print("\n\n"); - - // verify a key - count += verifyServerKey(eCurve, genPoint, pubPoint, pKey); - } - - fmt::print("Success count: {}/{}\n", count, total); - return 0; - } - - // BINK1998 Generation - - DWORD nRaw = options.channelID * 1000000 ; /* <- change */ - - BIGNUM *bnrand = BN_new(); - BN_rand(bnrand, 19, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY); - - int oRaw; - char *cRaw = BN_bn2dec(bnrand); - - sscanf(cRaw, "%d", &oRaw); - nRaw += (oRaw &= 0xF423F); // ensure our serial is less than 999999 - - if (options.verbose) { - fmt::print("> PID: {:09d}\n", nRaw); - } - - // generate a key - BN_sub(privateKey, genOrder, privateKey); - nRaw <<= 1; - - for (int i = 0; i < total; i++) { - generateXPKey(eCurve, genPoint, genOrder, privateKey, nRaw, pKey); - print_product_key(pKey); - fmt::print("\n\n"); - - // verify the key - count += verifyXPKey(eCurve, genPoint, pubPoint, pKey); - } - - fmt::print("Success count: {}/{}\n", count, total); - return 0; }