refactor project to use classes (#19)

This commit is contained in:
Neo 2023-06-07 12:23:59 -07:00 committed by GitHub
parent f6755b0c22
commit 0b94791ee1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 510 additions and 390 deletions

View File

@ -25,6 +25,6 @@ CONFIGURE_FILE(keys.json keys.json COPYONLY)
#SET(BUILD_SHARED_LIBS OFF) #SET(BUILD_SHARED_LIBS OFF)
#SET(CMAKE_EXE_LINKER_FLAGS "-static") #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_INCLUDE_DIRECTORIES(xpkey PUBLIC crypto)
TARGET_LINK_LIBRARIES(xpkey PUBLIC OpenSSL::Crypto nlohmann_json::nlohmann_json fmt) TARGET_LINK_LIBRARIES(xpkey PUBLIC OpenSSL::Crypto nlohmann_json::nlohmann_json fmt)

View File

@ -2,10 +2,10 @@
// Created by Andrew on 01/06/2023. // Created by Andrew on 01/06/2023.
// //
#include "header.h" #include "BINK1998.h"
/* Unpacks the Windows XP-like Product Key. */ /* Unpacks the Windows XP-like Product Key. */
void unpackXP( void BINK1998::Unpack(
QWORD (&pRaw)[2], QWORD (&pRaw)[2],
DWORD &pSerial, DWORD &pSerial,
DWORD &pHash, DWORD &pHash,
@ -25,7 +25,7 @@ void unpackXP(
} }
/* Packs the Windows XP-like Product Key. */ /* Packs the Windows XP-like Product Key. */
void packXP( void BINK1998::Pack(
QWORD (&pRaw)[2], QWORD (&pRaw)[2],
DWORD pSerial, DWORD pSerial,
DWORD pHash, DWORD pHash,
@ -41,7 +41,7 @@ void packXP(
} }
/* Verifies the Windows XP-like Product Key. */ /* Verifies the Windows XP-like Product Key. */
bool verifyXPKey( bool BINK1998::Verify(
EC_GROUP *eCurve, EC_GROUP *eCurve,
EC_POINT *basePoint, EC_POINT *basePoint,
EC_POINT *publicKey, EC_POINT *publicKey,
@ -59,7 +59,7 @@ bool verifyXPKey(
unbase24((BYTE *)pRaw, pKey); unbase24((BYTE *)pRaw, pKey);
// Extract RPK, hash and signature from bytecode. // 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. */ /* Generate a valid Product Key. */
void generateXPKey( void BINK1998::Generate(
EC_GROUP *eCurve, EC_GROUP *eCurve,
EC_POINT *basePoint, EC_POINT *basePoint,
BIGNUM *genOrder, BIGNUM *genOrder,
@ -216,7 +216,7 @@ void generateXPKey(
BN_bn2lebinpad(s, (BYTE *)&pSignature, BN_num_bytes(s)); BN_bn2lebinpad(s, (BYTE *)&pSignature, BN_num_bytes(s));
// Pack product key. // Pack product key.
packXP(pRaw, pSerial, pHash, pSignature); Pack(pRaw, pSerial, pHash, pSignature);
if (options.verbose) { if (options.verbose) {
fmt::print("Generation results:\n"); fmt::print("Generation results:\n");

19
src/BINK1998.h Normal file
View File

@ -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

View File

@ -2,10 +2,10 @@
// Created by Andrew on 01/06/2023. // Created by Andrew on 01/06/2023.
// //
#include "header.h" #include "BINK2002.h"
/* Unpacks the Windows Server 2003-like Product Key. */ /* Unpacks the Windows Server 2003-like Product Key. */
void unpackServer( void BINK2002::Unpack(
QWORD (&pRaw)[2], QWORD (&pRaw)[2],
DWORD &pChannelID, DWORD &pChannelID,
DWORD &pHash, DWORD &pHash,
@ -31,7 +31,7 @@ void unpackServer(
} }
/* Packs the Windows Server 2003-like Product Key. */ /* Packs the Windows Server 2003-like Product Key. */
void packServer( void BINK2002::Pack(
QWORD (&pRaw)[2], QWORD (&pRaw)[2],
DWORD pChannelID, DWORD pChannelID,
DWORD pHash, DWORD pHash,
@ -44,7 +44,7 @@ void packServer(
} }
/* Verifies the Windows Server 2003-like Product Key. */ /* Verifies the Windows Server 2003-like Product Key. */
bool verifyServerKey( bool BINK2002::Verify(
EC_GROUP *eCurve, EC_GROUP *eCurve,
EC_POINT *basePoint, EC_POINT *basePoint,
EC_POINT *publicKey, EC_POINT *publicKey,
@ -63,7 +63,7 @@ bool verifyServerKey(
unbase24((BYTE *)bKey, cdKey); unbase24((BYTE *)bKey, cdKey);
// Extract product key segments from bytecode. // Extract product key segments from bytecode.
unpackServer(bKey, pChannelID, pHash, pSignature, pAuthInfo); Unpack(bKey, pChannelID, pHash, pSignature, pAuthInfo);
if (options.verbose) { if (options.verbose) {
fmt::print("Validation results:\n"); fmt::print("Validation results:\n");
@ -171,7 +171,7 @@ bool verifyServerKey(
return compHash == pHash; return compHash == pHash;
} }
void generateServerKey( void BINK2002::Generate(
EC_GROUP *eCurve, EC_GROUP *eCurve,
EC_POINT *basePoint, EC_POINT *basePoint,
BIGNUM *genOrder, BIGNUM *genOrder,
@ -320,7 +320,7 @@ void generateServerKey(
BN_bn2lebinpad(s, (BYTE *)&pSignature, BN_num_bytes(s)); BN_bn2lebinpad(s, (BYTE *)&pSignature, BN_num_bytes(s));
// Pack product key. // Pack product key.
packServer(pRaw, pChannelID, pHash, pSignature, pAuthInfo); Pack(pRaw, pChannelID, pHash, pSignature, pAuthInfo);
if (options.verbose) { if (options.verbose) {
fmt::print("Generation results:\n"); fmt::print("Generation results:\n");

19
src/BINK2002.h Normal file
View File

@ -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

View File

@ -2,9 +2,12 @@
// Created by Andrew on 01/06/2023. // 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)) { if (!fs::exists(filename)) {
fmt::print("ERROR: File {} does not exist\n", filename.string()); fmt::print("ERROR: File {} does not exist\n", filename.string());
return false; 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("usage: {} \n", argv[0]);
fmt::print("\t-h --help\tshow this message\n"); fmt::print("\t-h --help\tshow this message\n");
fmt::print("\t-v --verbose\tenable verbose output\n"); fmt::print("\t-v --verbose\tenable verbose output\n");
@ -35,20 +38,20 @@ void showHelp(char *argv[]) {
fmt::print("\n\n"); fmt::print("\n\n");
} }
int parseCommandLine(int argc, char* argv[], Options* options) { int CLI::parseCommandLine(int argc, char* argv[], Options* options) {
// set default options
*options = Options { *options = Options {
"2E", "2E",
640, "keys.json",
"keys.json", "",
1, 640,
"", 1,
false, false,
false, false,
false, false,
false, false,
false MODE_BINK1998
}; };
// set default options
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
std::string arg = argv[i]; std::string arg = argv[i];
@ -117,7 +120,7 @@ int parseCommandLine(int argc, char* argv[], Options* options) {
return !options->error; return !options->error;
} }
int validateCommandLine(Options* options, char *argv[], json *keys) { int CLI::validateCommandLine(Options* options, char *argv[], json *keys) {
if (options->verbose) { if (options->verbose) {
fmt::print("Loading keys file {}\n", options->keysFilename); 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); sscanf(options->binkid.c_str(), "%x", &intBinkID);
if (intBinkID >= 0x40) { if (intBinkID >= 0x40) {
options->isBink2002 = true; options->applicationMode = MODE_BINK2002;
} }
if (options->channelID > 999) { 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 1;
} }
return 0; return 0;
} }
void print_product_id(DWORD *pid) void CLI::print_product_id(DWORD *pid)
{ {
char raw[12]; char raw[12];
char b[6], c[8]; char b[6], c[8];
@ -196,7 +199,7 @@ void print_product_id(DWORD *pid)
fmt::print("Product ID: PPPPP-{}-{}-23xxx\n", b, c); 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); assert(strlen(pk) == 25);
std::string spk = pk; std::string spk = pk;
@ -206,4 +209,147 @@ void print_product_key(char *pk) {
spk.substr(10,5), spk.substr(10,5),
spk.substr(15,5), spk.substr(15,5),
spk.substr(20,5)); spk.substr(20,5));
} }
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<std::string>().c_str());
BN_dec2bn(&privateKey, keys["BINK"][BINKID]["priv"].get<std::string>().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<std::string>());
fmt::print(" a: {}\n", keys["BINK"][BINKID]["a"].get<std::string>());
fmt::print(" b: {}\n", keys["BINK"][BINKID]["b"].get<std::string>());
fmt::print("Gx: {}\n", keys["BINK"][BINKID]["g"]["x"].get<std::string>());
fmt::print("Gy: {}\n", keys["BINK"][BINKID]["g"]["y"].get<std::string>());
fmt::print("Kx: {}\n", keys["BINK"][BINKID]["pub"]["x"].get<std::string>());
fmt::print("Ky: {}\n", keys["BINK"][BINKID]["pub"]["y"].get<std::string>());
fmt::print(" n: {}\n", keys["BINK"][BINKID]["n"].get<std::string>());
fmt::print(" k: {}\n", keys["BINK"][BINKID]["priv"].get<std::string>());
fmt::print("\n");
}
eCurve = initializeEllipticCurve(
keys["BINK"][BINKID]["p"].get<std::string>(),
keys["BINK"][BINKID]["a"].get<std::string>(),
keys["BINK"][BINKID]["b"].get<std::string>(),
keys["BINK"][BINKID]["g"]["x"].get<std::string>(),
keys["BINK"][BINKID]["g"]["y"].get<std::string>(),
keys["BINK"][BINKID]["pub"]["x"].get<std::string>(),
keys["BINK"][BINKID]["pub"]["y"].get<std::string>(),
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;
}

35
src/cli.h Normal file
View File

@ -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

View File

@ -2,32 +2,24 @@
// Created by WitherOrNot on 06/02/2023. // Created by WitherOrNot on 06/02/2023.
// //
#include "header.h" #include "confid.h"
typedef int64_t i64;
typedef uint64_t ui64;
#define MOD 0x16A6B036D7F2A79ULL #define MOD 0x16A6B036D7F2A79ULL
#define NON_RESIDUE 43 #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 { QWORD ConfirmationID::residue_add(QWORD x, QWORD y)
ui64 u[2];
ui64 v[2];
} TDivisor;
static ui64 residue_add(ui64 x, ui64 y)
{ {
ui64 z = x + y; QWORD z = x + y;
//z = z - (z >= MOD ? MOD : 0); //z = z - (z >= MOD ? MOD : 0);
if (z >= MOD) if (z >= MOD)
z -= MOD; z -= MOD;
return z; 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); //z += (x < y ? MOD : 0);
if (x < y) if (x < y)
z += MOD; 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__)) #if defined(__x86_64__) || defined(_M_AMD64) || defined(__aarch64__) || (defined(__arm64__) && defined(__APPLE__))
#ifdef __GNUC__ #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; *hi = r >> 64;
return (uint64_t) r; return (QWORD) r;
} }
#else #else
#define __umul128 _umul128 #define __umul128 _umul128
#endif #endif
#elif defined(__i386__) || defined(_M_IX86) || defined(__arm__) #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 // multiplier = ab = a * 2^32 + b
// multiplicand = cd = c * 2^32 + d // multiplicand = cd = c * 2^32 + d
// ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d // ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d
uint64_t a = multiplier >> 32; QWORD a = multiplier >> 32;
uint64_t b = multiplier & 0xFFFFFFFF; QWORD b = multiplier & 0xFFFFFFFF;
uint64_t c = multiplicand >> 32; QWORD c = multiplicand >> 32;
uint64_t d = multiplicand & 0xFFFFFFFF; QWORD d = multiplicand & 0xFFFFFFFF;
//uint64_t ac = a * c; //QWORD ac = a * c;
uint64_t ad = a * d; QWORD ad = a * d;
//uint64_t bc = b * c; //QWORD bc = b * c;
uint64_t bd = b * d; QWORD bd = b * d;
uint64_t adbc = ad + (b * c); QWORD adbc = ad + (b * c);
uint64_t adbc_carry = adbc < ad ? 1 : 0; QWORD adbc_carry = adbc < ad ? 1 : 0;
// multiplier * multiplicand = product_hi * 2^64 + product_lo // multiplier * multiplicand = product_hi * 2^64 + product_lo
uint64_t product_lo = bd + (adbc << 32); QWORD product_lo = bd + (adbc << 32);
uint64_t product_lo_carry = product_lo < bd ? 1 : 0; QWORD product_lo_carry = product_lo < bd ? 1 : 0;
*product_hi = (a * c) + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry; *product_hi = (a * c) + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry;
return product_lo; return product_lo;
} }
#endif #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) // hi:lo * ceil(2**170/MOD) >> (64 + 64 + 42)
ui64 prod1; QWORD prod1;
__umul128(lo, 0x604fa6a1c6346a87, &prod1); __umul128(lo, 0x604fa6a1c6346a87, &prod1);
ui64 part1hi; QWORD part1hi;
ui64 part1lo = __umul128(lo, 0x2d351c6d04f8b, &part1hi); QWORD part1lo = __umul128(lo, 0x2d351c6d04f8b, &part1hi);
ui64 part2hi; QWORD part2hi;
ui64 part2lo = __umul128(hi, 0x604fa6a1c6346a87, &part2hi); QWORD part2lo = __umul128(hi, 0x604fa6a1c6346a87, &part2hi);
ui64 sum1 = part1lo + part2lo; QWORD sum1 = part1lo + part2lo;
unsigned sum1carry = (sum1 < part1lo); unsigned sum1carry = (sum1 < part1lo);
sum1 += prod1; sum1 += prod1;
sum1carry += (sum1 < prod1); sum1carry += (sum1 < prod1);
ui64 prod2 = part1hi + part2hi + sum1carry; QWORD prod2 = part1hi + part2hi + sum1carry;
ui64 prod3hi; QWORD prod3hi;
ui64 prod3lo = __umul128(hi, 0x2d351c6d04f8b, &prod3hi); QWORD prod3lo = __umul128(hi, 0x2d351c6d04f8b, &prod3hi);
prod3lo += prod2; prod3lo += prod2;
prod3hi += (prod3lo < prod2); prod3hi += (prod3lo < prod2);
return (prod3lo >> 42) | (prod3hi << 22); 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 // * ceil(2**170/MOD) = 0x2d351 c6d04f8b|604fa6a1 c6346a87 for (p-1)*(p-1) max
ui64 hi; QWORD hi;
ui64 lo = __umul128(x, y, &hi); QWORD lo = __umul128(x, y, &hi);
ui64 quotient = ui128_quotient_mod(lo, hi); QWORD quotient = ui128_quotient_mod(lo, hi);
return lo - quotient * MOD; return lo - quotient * MOD;
} }
static ui64 residue_pow(ui64 x, ui64 y) QWORD ConfirmationID::residue_pow(QWORD x, QWORD y)
{ {
if (y == 0) if (y == 0)
return 1; return 1;
ui64 cur = x; QWORD cur = x;
while (!(y & 1)) { while (!(y & 1)) {
cur = residue_mul(cur, cur); cur = residue_mul(cur, cur);
y >>= 1; y >>= 1;
} }
ui64 res = cur; QWORD res = cur;
while ((y >>= 1) != 0) { while ((y >>= 1) != 0) {
cur = residue_mul(cur, cur); cur = residue_mul(cur, cur);
if (y & 1) if (y & 1)
@ -120,14 +112,14 @@ static ui64 residue_pow(ui64 x, ui64 y)
return res; return res;
} }
static ui64 inverse(ui64 u, ui64 v) QWORD ConfirmationID::inverse(QWORD u, QWORD v)
{ {
//assert(u); //assert(u);
i64 tmp; int64_t tmp;
i64 xu = 1, xv = 0; int64_t xu = 1, xv = 0;
ui64 v0 = v; QWORD v0 = v;
while (u > 1) { 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 = u; u = remainder; v = tmp;
tmp = xu; xu = xv - d * xu; xv = tmp; tmp = xu; xu = xv - d * xu; xv = tmp;
} }
@ -135,20 +127,27 @@ static ui64 inverse(ui64 u, ui64 v)
return xu; return xu;
} }
static ui64 residue_inv(ui64 x) QWORD ConfirmationID::residue_inv(QWORD x)
{ return inverse(x, MOD); } {
//{ return residue_pow(x, MOD - 2); } return inverse(x, MOD);
// return residue_pow(x, MOD - 2);
}
#define BAD 0xFFFFFFFFFFFFFFFFull #define BAD 0xFFFFFFFFFFFFFFFFull
static ui64 residue_sqrt(ui64 what) QWORD ConfirmationID::residue_sqrt(QWORD what)
{ {
if (!what) if (!what) {
return 0; return 0;
ui64 g = NON_RESIDUE, z, y, r, x, b, t; }
ui64 e = 0, q = MOD - 1;
while (!(q & 1)) QWORD g = NON_RESIDUE, z, y, r, x, b, t;
e++, q >>= 1; QWORD e = 0, q = MOD - 1;
while (!(q & 1)) {
e++, q >>= 1;
}
z = residue_pow(g, q); z = residue_pow(g, q);
y = z; y = z;
r = e; r = e;
@ -156,39 +155,46 @@ static ui64 residue_sqrt(ui64 what)
b = residue_mul(residue_mul(what, x), x); b = residue_mul(residue_mul(what, x), x);
x = residue_mul(what, x); x = residue_mul(what, x);
while (b != 1) { while (b != 1) {
ui64 m = 0, b2 = b; QWORD m = 0, b2 = b;
do {
do {
m++; m++;
b2 = residue_mul(b2, b2); b2 = residue_mul(b2, b2);
} while (b2 != 1); } while (b2 != 1);
if (m == r)
return BAD; if (m == r) {
return BAD;
}
t = residue_pow(y, 1 << (r - m - 1)); t = residue_pow(y, 1 << (r - m - 1));
y = residue_mul(t, t); y = residue_mul(t, t);
r = m; r = m;
x = residue_mul(x, t); x = residue_mul(x, t);
b = residue_mul(b, y); b = residue_mul(b, y);
} }
if (residue_mul(x, x) != what) { if (residue_mul(x, x) != what) {
//printf("internal error in sqrt\n"); //printf("internal error in sqrt\n");
return BAD; return BAD;
} }
return x; return x;
} }
int find_divisor_v(TDivisor* d) int ConfirmationID::find_divisor_v(TDivisor* d)
{ {
// u | v^2 - f // u | v^2 - f
// u = u0 + u1*x + x^2 // u = u0 + u1*x + x^2
// f%u = f0 + f1*x // f%u = f0 + f1*x
ui64 v1; QWORD v1, f2[6];
ui64 f2[6];
int i, j; for (int i = 0; i < 6; i++) {
for (i = 0; i < 6; i++) f2[i] = f[i];
f2[i] = f[i]; }
const ui64 u0 = d->u[0];
const ui64 u1 = d->u[1]; const QWORD u0 = d->u[0];
for (j = 4; j--; ) { 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] = 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 + 1] = residue_sub(f2[j + 1], residue_mul(u1, f2[j + 2]));
f2[j + 2] = 0; 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 // 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 // (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) // 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 QWORD f0 = f2[0];
const ui64 f1 = f2[1]; const QWORD f1 = f2[1];
const ui64 u0double = residue_add(u0, u0); const QWORD u0double = residue_add(u0, u0);
const ui64 coeff2 = residue_sub(residue_mul(u1, u1), residue_add(u0double, u0double)); const QWORD 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 coeff1 = residue_sub(residue_add(f0, f0), residue_mul(f1, u1));
if (coeff2 == 0) { if (coeff2 == 0) {
if (coeff1 == 0) { if (coeff1 == 0) {
if (f1 == 0) { if (f1 == 0) {
@ -213,34 +219,39 @@ int find_divisor_v(TDivisor* d)
} }
return 0; 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); v1 = residue_sqrt(sqr);
if (v1 == BAD) if (v1 == BAD) {
return 0; return 0;
}
} else { } 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); d = residue_sqrt(d);
if (d == BAD) if (d == BAD) {
return 0; return 0;
}
d = residue_add(d, d); d = residue_add(d, d);
ui64 inv = residue_inv(coeff2); QWORD inv = residue_inv(coeff2);
ui64 root = residue_mul(residue_add(coeff1, d), inv); QWORD root = residue_mul(residue_add(coeff1, d), inv);
v1 = residue_sqrt(root); v1 = residue_sqrt(root);
if (v1 == BAD) { if (v1 == BAD) {
root = residue_mul(residue_sub(coeff1, d), inv); root = residue_mul(residue_sub(coeff1, d), inv);
v1 = residue_sqrt(root); v1 = residue_sqrt(root);
if (v1 == BAD) if (v1 == BAD) {
return 0; 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[0] = v0;
d->v[1] = v1; d->v[1] = v1;
return 1; return 1;
} }
// generic short slow code // 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) if (adeg < 0 || bdeg < 0)
return resultprevdeg; return resultprevdeg;
@ -255,13 +266,14 @@ static int polynomial_mul(int adeg, const ui64 a[], int bdeg, const ui64 b[], in
--resultprevdeg; --resultprevdeg;
return 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(bdeg >= 0);
assert(b[bdeg] == 1); assert(b[bdeg] == 1);
int i, j; int i, j;
for (i = adeg - bdeg; i >= 0; i--) { for (i = adeg - bdeg; i >= 0; i--) {
ui64 q = a[i + bdeg]; QWORD q = a[i + bdeg];
if (quotient) if (quotient)
quotient[i] = q; quotient[i] = q;
for (j = 0; j < bdeg; j++) 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--; i--;
return 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; int sdeg = -1;
ui64 s[3] = {0, 0, 0}; QWORD s[3] = {0, 0, 0};
int mult1deg = 0; int mult1deg = 0;
mult1[0] = 1; mult1[1] = 0; mult1[2] = 0; mult1[0] = 1; mult1[1] = 0; mult1[2] = 0;
int tdeg = 0; int tdeg = 0;
ui64 t[3] = {1, 0, 0}; QWORD t[3] = {1, 0, 0};
int mult2deg = -1; int mult2deg = -1;
mult2[0] = 0; mult2[1] = 0; mult2[2] = 0; mult2[0] = 0; mult2[1] = 0; mult2[2] = 0;
int rdeg = bdeg; int rdeg = bdeg;
ui64 r[3] = {b[0], b[1], b[2]}; QWORD r[3] = {b[0], b[1], b[2]};
int gcddeg = adeg; int gcddeg = adeg;
gcd[0] = a[0]; gcd[1] = a[1]; gcd[2] = a[2]; gcd[0] = a[0]; gcd[1] = a[1]; gcd[2] = a[2];
// s*u1 + t*u2 = r // 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; tmp = rdeg; rdeg = gcddeg; gcddeg = tmp;
tmpi = sdeg; sdeg = mult1deg; mult1deg = tmpi; tmpi = sdeg; sdeg = mult1deg; mult1deg = tmpi;
tmpi = tdeg; tdeg = mult2deg; mult2deg = tmpi; tmpi = tdeg; tdeg = mult2deg; mult2deg = tmpi;
ui64 tmp2; QWORD tmp2;
tmp2 = r[0]; r[0] = gcd[0]; gcd[0] = tmp2; tmp2 = r[0]; r[0] = gcd[0]; gcd[0] = tmp2;
tmp2 = r[1]; r[1] = gcd[1]; gcd[1] = tmp2; tmp2 = r[1]; r[1] = gcd[1]; gcd[1] = tmp2;
tmp2 = r[2]; r[2] = gcd[2]; gcd[2] = 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; continue;
} }
int delta = gcddeg - rdeg; 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 // quotient = mult * x**delta
assert(rdeg + delta < 3); assert(rdeg + delta < 3);
for (int i = 0; i <= rdeg; i++) 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; *pmult1deg = mult1deg;
*pmult2deg = mult2deg; *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) { if (src->u[1] != BAD) {
polyu[0] = src->u[0]; polyu[0] = src->u[0];
@ -358,27 +371,28 @@ static int u2poly(const TDivisor* src, ui64 polyu[3], ui64 polyv[2])
polyv[1] = 0; polyv[1] = 0;
return 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 u1deg = u2poly(src1, u1, v1);
int u2deg = u2poly(src2, u2, v2); int u2deg = u2poly(src2, u2, v2);
// extended gcd: d1 = gcd(u1, u2) = e1*u1 + e2*u2 // extended gcd: d1 = gcd(u1, u2) = e1*u1 + e2*u2
int d1deg, e1deg, e2deg; 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); polynomial_xgcd(u1deg, u1, u2deg, u2, &d1deg, d1, &e1deg, e1, &e2deg, e2);
assert(e1deg <= 1); assert(e1deg <= 1);
assert(e2deg <= 1); assert(e2deg <= 1);
// extended gcd again: d = gcd(d1, v1+v2) = c1*d1 + c2*(v1+v2) // 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 bdeg = (b[1] == 0 ? (b[0] == 0 ? -1 : 0) : 1);
int ddeg, c1deg, c2deg; 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); polynomial_xgcd(d1deg, d1, bdeg, b, &ddeg, d, &c1deg, c1, &c2deg, c2);
assert(c1deg <= 0); assert(c1deg <= 0);
assert(c2deg <= 1); assert(c2deg <= 1);
assert(ddeg >= 0); assert(ddeg >= 0);
ui64 dmult = residue_inv(d[ddeg]); QWORD dmult = residue_inv(d[ddeg]);
int i; int i;
for (i = 0; i < ddeg; i++) for (i = 0; i < ddeg; i++)
d[i] = residue_mul(d[i], dmult); 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); c1[i] = residue_mul(c1[i], dmult);
for (i = 0; i <= c2deg; i++) for (i = 0; i <= c2deg; i++)
c2[i] = residue_mul(c2[i], dmult); c2[i] = residue_mul(c2[i], dmult);
ui64 u[5]; QWORD u[5];
int udeg = polynomial_mul(u1deg, u1, u2deg, u2, -1, u); int udeg = polynomial_mul(u1deg, u1, u2deg, u2, -1, u);
// u is monic // u is monic
ui64 v[7], tmp[7]; QWORD v[7], tmp[7];
int vdeg, tmpdeg; int vdeg, tmpdeg;
// c1*(e1*u1*v2 + e2*u2*v1) + c2*(v1*v2 + f) // c1*(e1*u1*v2 + e2*u2*v1) + c2*(v1*v2 + f)
// c1*(e1*u1*(v2-v1) + d1*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); vdeg = polynomial_mul(c2deg, c2, tmpdeg, tmp, vdeg, v);
if (ddeg > 0) { if (ddeg > 0) {
assert(udeg >= 2*ddeg); assert(udeg >= 2*ddeg);
ui64 udiv[5]; QWORD udiv[5];
polynomial_div_monic(udeg, u, ddeg, d, udiv); udeg -= ddeg; polynomial_div_monic(udeg, u, ddeg, d, udiv); udeg -= ddeg;
polynomial_div_monic(udeg, udiv, ddeg, d, u); udeg -= ddeg; polynomial_div_monic(udeg, udiv, ddeg, d, u); udeg -= ddeg;
if (vdeg >= 0) { if (vdeg >= 0) {
@ -429,10 +443,10 @@ static void divisor_add(const TDivisor* src1, const TDivisor* src2, TDivisor* ds
for (; i <= 5; i++) for (; i <= 5; i++)
tmp[i] = f[i]; tmp[i] = f[i];
tmpdeg = i - 1; tmpdeg = i - 1;
ui64 udiv[5]; QWORD udiv[5];
polynomial_div_monic(tmpdeg, tmp, udeg, u, udiv); polynomial_div_monic(tmpdeg, tmp, udeg, u, udiv);
udeg = tmpdeg - udeg; udeg = tmpdeg - udeg;
ui64 mult = residue_inv(udiv[udeg]); QWORD mult = residue_inv(udiv[udeg]);
for (i = 0; i < udeg; i++) for (i = 0; i < udeg; i++)
u[i] = residue_mul(udiv[i], mult); u[i] = residue_mul(udiv[i], mult);
u[i] = 1; u[i] = 1;
@ -458,9 +472,10 @@ static void divisor_add(const TDivisor* src1, const TDivisor* src2, TDivisor* ds
dst->v[1] = BAD; dst->v[1] = BAD;
} }
} }
#define divisor_double(src, dst) divisor_add(src, src, dst) #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) { if (mult == 0) {
dst->u[0] = BAD; 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) { if (mult_lo == 0 && mult_hi == 0) {
dst->u[0] = BAD; 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); //assert(shift > 0 && shift < 32);
return (x << shift) | (x >> (32 - shift)); 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; unsigned a, b, c, d, e;
a = 0x67452301; 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; 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_input[64];
unsigned char sha1_result[20]; 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[half + keySize] = 0x80;
sha1_input[sizeof(sha1_input) - 1] = (half + keySize) * 8; sha1_input[sizeof(sha1_input) - 1] = (half + keySize) * 8;
sha1_input[sizeof(sha1_input) - 2] = (half + keySize) * 8 / 0x100; 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; size_t i;
for (i = half & ~3; i < half; i++) for (i = half & ~3; i < half; i++)
sha1_result[i] = sha1_result[i + 4 - (half & 3)]; 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_input[64];
unsigned char sha1_result[20]; 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[half + keySize] = 0x80;
sha1_input[sizeof(sha1_input) - 1] = (half + keySize) * 8; sha1_input[sizeof(sha1_input) - 1] = (half + keySize) * 8;
sha1_input[sizeof(sha1_input) - 2] = (half + keySize) * 8 / 0x100; 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; size_t i;
for (i = half & ~3; i < half; i++) for (i = half & ~3; i < half; i++)
sha1_result[i] = sha1_result[i + 4 - (half & 3)]; 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 unsigned char installation_id[19]; // 10**45 < 256**19
size_t installation_id_len = 0; 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) #pragma pack(push, 1)
struct { struct {
ui64 HardwareID; QWORD HardwareID;
ui64 ProductIDLow; QWORD ProductIDLow;
unsigned char ProductIDHigh; unsigned char ProductIDHigh;
unsigned short KeySHA1; unsigned short KeySHA1;
} parsed; } parsed;
@ -697,7 +712,7 @@ int generateConfId(const char* installation_id_str, char confirmation_id[49])
unsigned char keybuf[16]; unsigned char keybuf[16];
memcpy(keybuf, &parsed.HardwareID, 8); 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); memcpy(keybuf + 8, &productIdMixed, 8);
TDivisor d; TDivisor d;
@ -706,16 +721,16 @@ int generateConfId(const char* installation_id_str, char confirmation_id[49])
union { union {
unsigned char buffer[14]; unsigned char buffer[14];
struct { struct {
ui64 lo; QWORD lo;
ui64 hi; QWORD hi;
}; };
} u; } u;
u.lo = 0; u.lo = 0;
u.hi = 0; u.hi = 0;
u.buffer[7] = attempt; u.buffer[7] = attempt;
Mix(u.buffer, 14, keybuf, 16); Mix(u.buffer, 14, keybuf, 16);
ui64 x2 = ui128_quotient_mod(u.lo, u.hi); QWORD x2 = ui128_quotient_mod(u.lo, u.hi);
ui64 x1 = u.lo - x2 * MOD; QWORD x1 = u.lo - x2 * MOD;
x2++; x2++;
d.u[0] = residue_sub(residue_mul(x1, x1), residue_mul(NON_RESIDUE, residue_mul(x2, 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[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); divisor_mul128(&d, 0x04e21b9d10f127c1, 0x40da7c36d44c, &d);
union { union {
struct { struct {
ui64 encoded_lo, encoded_hi; QWORD encoded_lo, encoded_hi;
}; };
struct { struct {
uint32_t encoded[4]; 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_lo += MOD;
e.encoded_hi += (e.encoded_lo < MOD); e.encoded_hi += (e.encoded_lo < MOD);
} else { } else {
ui64 x1 = (d.u[1] % 2 ? d.u[1] + MOD : d.u[1]) / 2; QWORD x1 = (d.u[1] % 2 ? d.u[1] + MOD : d.u[1]) / 2;
ui64 x2sqr = residue_sub(residue_mul(x1, x1), d.u[0]); QWORD x2sqr = residue_sub(residue_mul(x1, x1), d.u[0]);
ui64 x2 = residue_sqrt(x2sqr); QWORD x2 = residue_sqrt(x2sqr);
if (x2 == BAD) { if (x2 == BAD) {
x2 = residue_sqrt(residue_mul(x2sqr, residue_inv(NON_RESIDUE))); x2 = residue_sqrt(residue_mul(x2sqr, residue_inv(NON_RESIDUE)));
assert(x2 != BAD); 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); e.encoded_hi += (e.encoded_lo < x1);
} else { } else {
// points (-x1+x2, v(-x1+x2)) and (-x1-x2, v(-x1-x2)) // points (-x1+x2, v(-x1+x2)) and (-x1-x2, v(-x1-x2))
ui64 x1a = residue_sub(x1, x2); QWORD x1a = residue_sub(x1, x2);
ui64 y1 = residue_sub(d.v[0], residue_mul(d.v[1], x1a)); QWORD y1 = residue_sub(d.v[0], residue_mul(d.v[1], x1a));
ui64 x2a = residue_add(x1, x2); QWORD x2a = residue_add(x1, x2);
ui64 y2 = residue_sub(d.v[0], residue_mul(d.v[1], x2a)); QWORD y2 = residue_sub(d.v[0], residue_mul(d.v[1], x2a));
if (x1a > x2a) { if (x1a > x2a) {
ui64 tmp = x1a; QWORD tmp = x1a;
x1a = x2a; x1a = x2a;
x2a = tmp; x2a = tmp;
} }
if ((y1 ^ y2) & 1) { if ((y1 ^ y2) & 1) {
ui64 tmp = x1a; QWORD tmp = x1a;
x1a = x2a; x1a = x2a;
x2a = tmp; x2a = tmp;
} }
@ -777,14 +792,15 @@ int generateConfId(const char* installation_id_str, char confirmation_id[49])
for (i = 0; i < 35; i++) { for (i = 0; i < 35; i++) {
unsigned c = e.encoded[3] % 10; unsigned c = e.encoded[3] % 10;
e.encoded[3] /= 10; e.encoded[3] /= 10;
unsigned c2 = ((ui64)c << 32 | e.encoded[2]) % 10; unsigned c2 = ((QWORD)c << 32 | e.encoded[2]) % 10;
e.encoded[2] = ((ui64)c << 32 | e.encoded[2]) / 10; e.encoded[2] = ((QWORD)c << 32 | e.encoded[2]) / 10;
unsigned c3 = ((ui64)c2 << 32 | e.encoded[1]) % 10; unsigned c3 = ((QWORD)c2 << 32 | e.encoded[1]) % 10;
e.encoded[1] = ((ui64)c2 << 32 | e.encoded[1]) / 10; e.encoded[1] = ((QWORD)c2 << 32 | e.encoded[1]) / 10;
unsigned c4 = ((ui64)c3 << 32 | e.encoded[0]) % 10; unsigned c4 = ((QWORD)c3 << 32 | e.encoded[0]) % 10;
e.encoded[0] = ((ui64)c3 << 32 | e.encoded[0]) / 10; e.encoded[0] = ((QWORD)c3 << 32 | e.encoded[0]) / 10;
decimal[34 - i] = c4; decimal[34 - i] = c4;
} }
assert(e.encoded[0] == 0 && e.encoded[1] == 0 && e.encoded[2] == 0 && e.encoded[3] == 0); assert(e.encoded[0] == 0 && e.encoded[1] == 0 && e.encoded[2] == 0 && e.encoded[3] == 0);
char* q = confirmation_id; char* q = confirmation_id;
for (i = 0; i < 7; i++) { for (i = 0; i < 7; i++) {
@ -801,4 +817,4 @@ int generateConfId(const char* installation_id_str, char confirmation_id[49])
} }
*q++ = 0; *q++ = 0;
return 0; return 0;
} }

53
src/confid.h Normal file
View File

@ -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

View File

@ -57,14 +57,27 @@
using json = nlohmann::json; using json = nlohmann::json;
namespace fs = std::filesystem; namespace fs = std::filesystem;
// Confirmation ID generator constants enum MODE {
#define SUCCESS 0 MODE_BINK1998 = 0,
#define ERR_TOO_SHORT 1 MODE_BINK2002 = 1,
#define ERR_TOO_LARGE 2 MODE_CONFIRMATION_ID = 2,
#define ERR_INVALID_CHARACTER 3 };
#define ERR_INVALID_CHECK_DIGIT 4
#define ERR_UNKNOWN_VERSION 5 struct Options {
#define ERR_UNLUCKY 6 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 // Type definitions
typedef bool BOOL; typedef bool BOOL;
@ -73,8 +86,11 @@ typedef uint16_t WORD;
typedef uint32_t DWORD; typedef uint32_t DWORD;
typedef uint64_t QWORD; typedef uint64_t QWORD;
#ifdef __SIZEOF_INT128__
typedef unsigned __int128 DQWORD;
#endif
// Global variables // Global variables
extern char pCharset[];
// util.cpp // util.cpp
int BN_bn2lebin(const BIGNUM *a, unsigned char *to, int tolen); // Hello OpenSSL developers, please tell me, where is this function at? 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 unbase24(BYTE *byteSeq, const char *cdKey);
void base24(char *cdKey, BYTE *byteSeq); 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 #endif //WINDOWSXPKG_HEADER_H

View File

@ -4,6 +4,8 @@
#include "header.h" #include "header.h"
char pCharset[] = "BCDFGHJKMPQRTVWXY2346789";
/* Converts from CD-key to a byte sequence. */ /* Converts from CD-key to a byte sequence. */
void unbase24(BYTE *byteSeq, const char *cdKey) { void unbase24(BYTE *byteSeq, const char *cdKey) {
BYTE pDecodedKey[PK_LENGTH + NULL_TERMINATOR]{}; BYTE pDecodedKey[PK_LENGTH + NULL_TERMINATOR]{};

View File

@ -3,163 +3,37 @@
// //
#include "header.h" #include "header.h"
#include "cli.h"
char pCharset[] = "BCDFGHJKMPQRTVWXY2346789";
Options options; Options options;
int main(int argc, char *argv[]) { 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"); fmt::print("error parsing command line options\n");
showHelp(argv); CLI::showHelp(argv);
return !options.error ? 0 : 1; return !options.error ? 0 : 1;
} }
json keys; json keys;
int status = validateCommandLine(&options, argv, &keys);
int status = CLI::validateCommandLine(&options, argv, &keys);
if (status > 0) { if (status > 0) {
return status; 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 switch(options.applicationMode) {
// we need the result of the function K(x; y) = kG(x; y). case MODE_BINK1998:
BIGNUM *privateKey = BN_new(); return run->BINK1998();
// We can, however, validate any given key using the available public key: {p, a, b, G, K}. case MODE_BINK2002:
// genOrder the order of the generator G, a value we have to reverse -> Schoof's Algorithm. return run->BINK2002();
BIGNUM *genOrder = BN_new();
/* Computed data */ case MODE_CONFIRMATION_ID:
BN_dec2bn(&genOrder, keys["BINK"][BINKID]["n"].get<std::string>().c_str()); return run->ConfirmationID();
BN_dec2bn(&privateKey, keys["BINK"][BINKID]["priv"].get<std::string>().c_str());
if (options.verbose) { default:
fmt::print("----------------------------------------------------------- \n"); return 1;
fmt::print("Loaded the following curve constraints: BINK[{}]\n", BINKID);
fmt::print("----------------------------------------------------------- \n");
fmt::print(" P: {}\n", keys["BINK"][BINKID]["p"].get<std::string>());
fmt::print(" a: {}\n", keys["BINK"][BINKID]["a"].get<std::string>());
fmt::print(" b: {}\n", keys["BINK"][BINKID]["b"].get<std::string>());
fmt::print("Gx: {}\n", keys["BINK"][BINKID]["g"]["x"].get<std::string>());
fmt::print("Gy: {}\n", keys["BINK"][BINKID]["g"]["y"].get<std::string>());
fmt::print("Kx: {}\n", keys["BINK"][BINKID]["pub"]["x"].get<std::string>());
fmt::print("Ky: {}\n", keys["BINK"][BINKID]["pub"]["y"].get<std::string>());
fmt::print(" n: {}\n", keys["BINK"][BINKID]["n"].get<std::string>());
fmt::print(" k: {}\n", keys["BINK"][BINKID]["priv"].get<std::string>());
} }
EC_POINT *genPoint, *pubPoint;
EC_GROUP *eCurve = initializeEllipticCurve(
keys["BINK"][BINKID]["p"].get<std::string>(),
keys["BINK"][BINKID]["a"].get<std::string>(),
keys["BINK"][BINKID]["b"].get<std::string>(),
keys["BINK"][BINKID]["g"]["x"].get<std::string>(),
keys["BINK"][BINKID]["g"]["y"].get<std::string>(),
keys["BINK"][BINKID]["pub"]["x"].get<std::string>(),
keys["BINK"][BINKID]["pub"]["y"].get<std::string>(),
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;
} }