diff --git a/src/header.h b/src/header.h index dbadd9c..0e52861 100644 --- a/src/header.h +++ b/src/header.h @@ -28,20 +28,26 @@ #include #include #include +#include #include // Algorithm macros -#define PK_LENGTH 25 -#define NULL_TERMINATOR 1 +#define PK_LENGTH 25 +#define NULL_TERMINATOR 1 -#define FIELD_BITS 384 -#define FIELD_BYTES 48 -#define FIELD_BITS_2003 512 -#define FIELD_BYTES_2003 64 +#define FIELD_BITS 384 +#define FIELD_BYTES 48 +#define FIELD_BITS_2003 512 +#define FIELD_BYTES_2003 64 -#define NEXTSNBITS(field, n, offset) (((QWORD)field >> offset) & ((1ULL << (n)) - 1)) +#define SHA_MSG_LENGTH_XP (4 + 2 * FIELD_BYTES) + +#define NEXTSNBITS(field, n, offset) (((QWORD)field >> offset) & ((1ULL << (n)) - 1)) #define FIRSTNBITS(field, n) NEXTSNBITS(field, n, 0) +#define BYDWORD(n) (n[0] | n[1] << 8 | n[2] << 16 | n[3] << 24) +#define BITMASK(n) ((1ULL << n) - 1) + // Confirmation ID generator constants #define SUCCESS 0 #define ERR_TOO_SHORT 1 @@ -62,7 +68,9 @@ typedef uint64_t QWORD; 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? void endian(BYTE *data, int length); + EC_GROUP *initializeEllipticCurve( std::string pSel, std::string aSel, @@ -71,8 +79,8 @@ EC_GROUP *initializeEllipticCurve( std::string generatorYSel, std::string publicKeyXSel, std::string publicKeyYSel, - EC_POINT *&genPoint, - EC_POINT *&pubPoint + EC_POINT *&genPoint, + EC_POINT *&pubPoint ); // key.cpp @@ -98,18 +106,18 @@ void showHelp(char *argv[]); // xp.cpp bool verifyXPKey( EC_GROUP *eCurve, - EC_POINT *generator, + EC_POINT *basePoint, EC_POINT *publicKey, - char (&cdKey)[25] + char (&cdKey)[25] ); void generateXPKey( EC_GROUP *eCurve, - EC_POINT *generator, - BIGNUM *order, - BIGNUM *privateKey, - DWORD pRaw, - char (&cdKey)[25] + EC_POINT *basePoint, + BIGNUM *genOrder, + BIGNUM *privateKey, + DWORD pSerial, + char (&cdKey)[25] ); // server.cpp diff --git a/src/server.cpp b/src/server.cpp index 780f35a..d278190 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1,3 +1,7 @@ +// +// Created by Andrew on 01/06/2023. +// + #include "header.h" char pCharset[] = "BCDFGHJKMPQRTVWXY2346789"; diff --git a/src/util.cpp b/src/util.cpp index 0575499..f3c79d5 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -84,4 +84,19 @@ EC_GROUP *initializeEllipticCurve( BN_CTX_free(context); return eCurve; +} + +int BN_bn2lebin(const BIGNUM *a, unsigned char *to, int tolen) { + if (a == nullptr || to == nullptr) + return 0; + + int len = BN_bn2bin(a, to); + + if (len > tolen) + return -1; + + // Choke point inside BN_bn2lebinpad: OpenSSL uses len instead of tolen. + endian(to, tolen); + + return len; } \ No newline at end of file diff --git a/src/xp.cpp b/src/xp.cpp index ef1e9ab..cd6cc6a 100644 --- a/src/xp.cpp +++ b/src/xp.cpp @@ -1,23 +1,10 @@ -/* - Windows XP CD Key Verification/Generator v0.03 - by z22 - - Compile with OpenSSL libs, modify to suit your needs. - http://gnuwin32.sourceforge.net/packages/openssl.htm - - History: - 0.03 Stack corruptionerror on exit fixed (now pkey is large enough) - More Comments added - 0.02 Changed name the *.cpp; - Fixed minor bugs & Make it compilable on VC++ - 0.01 First version compilable MingW - - -*/ +// +// Created by Andrew on 01/06/2023. +// #include "header.h" -/* Unpacks the Windows XP Product Key. */ +/* Unpacks the Windows XP-like Product Key. */ void unpackXP( QWORD (&pRaw)[2], DWORD &pSerial, @@ -37,7 +24,7 @@ void unpackXP( pSignature = FIRSTNBITS(pRaw[1], 51) << 5 | NEXTSNBITS(pRaw[0], 5, 59); } -/* Packs the Windows XP Product Key. */ +/* Packs the Windows XP-like Product Key. */ void packXP( QWORD (&pRaw)[2], DWORD pSerial, @@ -53,213 +40,207 @@ void packXP( pRaw[1] = NEXTSNBITS(pSignature, 51, 5); } -/* Verify Product Key */ -bool verifyXPKey(EC_GROUP *eCurve, EC_POINT *generator, EC_POINT *publicKey, char (&cdKey)[25]) { - BN_CTX *context = BN_CTX_new(); +/* Verifies the Windows XP-like Product Key. */ +bool verifyXPKey( + EC_GROUP *eCurve, + EC_POINT *basePoint, + EC_POINT *publicKey, + char (&cdKey)[25] +) { + BN_CTX *numContext = BN_CTX_new(); + + QWORD pRaw[2]{}, + pSignature = 0; + + DWORD pSerial = 0, + pHash = 0; // Convert Base24 CD-key to bytecode. - QWORD bKey[2]{}; - DWORD pID, checkHash; + unbase24((BYTE *)pRaw, cdKey); - QWORD sig = 0; + // Extract RPK, hash and signature from bytecode. + unpackXP(pRaw, pSerial, pHash, pSignature); - unbase24((BYTE *)bKey, cdKey); + /* + * + * Scalars: + * e = Hash + * s = Schnorr Signature + * + * Points: + * G(x, y) = Generator (Base Point) + * K(x, y) = Public Key + * + * Equation: + * P = sG + eK + * + */ - // Extract data, hash and signature from the bytecode. - unpackXP(bKey, pID, checkHash, sig); + BIGNUM *e = BN_lebin2bn((BYTE *)&pHash, sizeof(pHash), nullptr), + *s = BN_lebin2bn((BYTE *)&pSignature, sizeof(pSignature), nullptr), + *x = BN_new(), + *y = BN_new(); - // e = Hash - // s = Signature - BIGNUM *e, *s; + // Create 2 points on the elliptic curve. + EC_POINT *t = EC_POINT_new(eCurve); + EC_POINT *p = EC_POINT_new(eCurve); - // Put hash word into BigNum e. - e = BN_new(); - BN_set_word(e, checkHash); + // t = sG + EC_POINT_mul(eCurve, t, nullptr, basePoint, s, numContext); - // Reverse signature and create a new BigNum s. - endian((BYTE *)&sig, sizeof(sig)); - s = BN_bin2bn((BYTE *)&sig, sizeof(sig), nullptr); + // p = eK + EC_POINT_mul(eCurve, p, nullptr, publicKey, e, numContext); - // Create x and y. - BIGNUM *x = BN_new(); - BIGNUM *y = BN_new(); + // p += t + EC_POINT_add(eCurve, p, t, p, numContext); - // Create 2 new points on the existing elliptic curve. - EC_POINT *u = EC_POINT_new(eCurve); - EC_POINT *v = EC_POINT_new(eCurve); + // x = p.x; y = p.y; + EC_POINT_get_affine_coordinates(eCurve, p, x, y, numContext); - // EC_POINT_mul calculates r = generator * n + q * m. - // v = s * generator + e * (-publicKey) + BYTE msgDigest[SHA_DIGEST_LENGTH]{}, + msgBuffer[SHA_MSG_LENGTH_XP]{}, + xBin[FIELD_BYTES]{}, + yBin[FIELD_BYTES]{}; - // u = generator * s - EC_POINT_mul(eCurve, u, nullptr, generator, s, context); + DWORD compHash; - // v = publicKey * e - EC_POINT_mul(eCurve, v, nullptr, publicKey, e, context); + // Convert resulting point coordinates to bytes. + BN_bn2lebin(x, xBin, FIELD_BYTES); + BN_bn2lebin(y, yBin, FIELD_BYTES); - // v += u - EC_POINT_add(eCurve, v, u, v, context); + // Assemble the SHA message. + memcpy((void *)&msgBuffer[0], (void *)&pSerial, 4); + memcpy((void *)&msgBuffer[4], (void *)xBin, FIELD_BYTES); + memcpy((void *)&msgBuffer[4 + FIELD_BYTES], (void *)yBin, FIELD_BYTES); - // EC_POINT_get_affine_coordinates() sets x and y, either of which may be nullptr, to the corresponding coordinates of p. - // x = v.x; y = v.y; - EC_POINT_get_affine_coordinates(eCurve, v, x, y, context); + // Retrieve the message digest. + SHA1(msgBuffer, SHA_MSG_LENGTH_XP, msgDigest); - BYTE buf[FIELD_BYTES], md[SHA_DIGEST_LENGTH], t[4]; - DWORD newHash; - - SHA_CTX hContext; - - // h = First32(SHA-1(pID || v.x || v.y)) >> 4 - SHA1_Init(&hContext); - - // Chop Product ID into 4 bytes. - t[0] = (pID & 0xff); // First 8 bits - t[1] = (pID & 0xff00) >> 8; // Second 8 bits - t[2] = (pID & 0xff0000) >> 16; // Third 8 bits - t[3] = (pID & 0xff000000) >> 24; // Fourth 8 bits - - // Hash chunk of data. - SHA1_Update(&hContext, t, sizeof(t)); - - // Empty buffer, place v.x in little-endian. - memset(buf, 0, FIELD_BYTES); - BN_bn2bin(x, buf); - endian(buf, FIELD_BYTES); - - // Hash chunk of data. - SHA1_Update(&hContext, buf, FIELD_BYTES); - - // Empty buffer, place v.y in little-endian. - memset(buf, 0, FIELD_BYTES); - BN_bn2bin(y, buf); - endian(buf, FIELD_BYTES); - - // Hash chunk of data. - SHA1_Update(&hContext, buf, FIELD_BYTES); - - // Store the final message from hContext in md. - SHA1_Final(md, &hContext); - - // h = First32(SHA-1(pID || v.x || v.y)) >> 4 - newHash = (md[0] | (md[1] << 8) | (md[2] << 16) | (md[3] << 24)) >> 4; - newHash &= 0xfffffff; + // Translate the byte digest into a 32-bit integer - this is our computed hash. + // Truncate the hash to 28 bits. + compHash = BYDWORD(msgDigest) >> 4; + compHash &= BITMASK(28); BN_free(e); BN_free(s); BN_free(x); BN_free(y); - BN_CTX_free(context); + BN_CTX_free(numContext); - EC_POINT_free(u); - EC_POINT_free(v); + EC_POINT_free(t); + EC_POINT_free(p); - // If we managed to generate a key with the same hash, the key is correct. - return newHash == checkHash; + // If the computed hash checks out, the key is valid. + return compHash == pHash; } /* Generate a valid Product Key. */ -void generateXPKey(EC_GROUP *eCurve, EC_POINT *generator, BIGNUM *order, BIGNUM *privateKey, DWORD pRaw, char (&pKey)[25]) { - EC_POINT *r = EC_POINT_new(eCurve); - BN_CTX *ctx = BN_CTX_new(); +void generateXPKey( + EC_GROUP *eCurve, + EC_POINT *basePoint, + BIGNUM *genOrder, + BIGNUM *privateKey, + DWORD pSerial, + char (&pKey)[25] +) { + BN_CTX *numContext = BN_CTX_new(); BIGNUM *c = BN_new(); BIGNUM *s = BN_new(); BIGNUM *x = BN_new(); BIGNUM *y = BN_new(); - QWORD bKey[2]{}; + QWORD pRaw[2]{}; do { - DWORD hash = 0; - QWORD sig = 0; + EC_POINT *r = EC_POINT_new(eCurve); - memset(bKey, 0, 2 * sizeof(QWORD)); + QWORD pSignature = 0; + DWORD pHash; // Generate a random number c consisting of 384 bits without any constraints. BN_rand(c, FIELD_BITS, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY); - // r = generator * c; - EC_POINT_mul(eCurve, r, nullptr, generator, c, ctx); + // Pick a random derivative of the base point on the elliptic curve. + // R = cG; + EC_POINT_mul(eCurve, r, nullptr, basePoint, c, numContext); - // x = r.x; y = r.y; - EC_POINT_get_affine_coordinates(eCurve, r, x, y, ctx); + // Acquire its coordinates. + // x = R.x; y = R.y; + EC_POINT_get_affine_coordinates(eCurve, r, x, y, numContext); - SHA_CTX hContext; - BYTE md[SHA_DIGEST_LENGTH]{}, buf[FIELD_BYTES]{}, t[4]{}; + BYTE msgDigest[SHA_DIGEST_LENGTH]{}, + msgBuffer[SHA_MSG_LENGTH_XP]{}, + xBin[FIELD_BYTES]{}, + yBin[FIELD_BYTES]{}; - // h = (First-32(SHA1(pRaw, r.x, r.y)) >> 4 - SHA1_Init(&hContext); + // Convert coordinates to bytes. + BN_bn2lebin(x, xBin, FIELD_BYTES); + BN_bn2lebin(y, yBin, FIELD_BYTES); - // Chop Raw Product Key into 4 bytes. - t[0] = (pRaw & 0xff); - t[1] = (pRaw & 0xff00) >> 8; - t[2] = (pRaw & 0xff0000) >> 16; - t[3] = (pRaw & 0xff000000) >> 24; + // Assemble the SHA message. + memcpy((void *)&msgBuffer[0], (void *)&pSerial, 4); + memcpy((void *)&msgBuffer[4], (void *)xBin, FIELD_BYTES); + memcpy((void *)&msgBuffer[4 + FIELD_BYTES], (void *)yBin, FIELD_BYTES); - // Hash chunk of data. - SHA1_Update(&hContext, t, sizeof(t)); + // Retrieve the message digest. + SHA1(msgBuffer, SHA_MSG_LENGTH_XP, msgDigest); - // Empty buffer, place r.x in little-endian - memset(buf, 0, FIELD_BYTES); - BN_bn2bin(x, buf); - endian(buf, FIELD_BYTES); + // Translate the byte digest into a 32-bit integer - this is our computed pHash. + // Truncate the pHash to 28 bits. + pHash = BYDWORD(msgDigest) >> 4; + pHash &= BITMASK(28); - // Hash chunk of data. - SHA1_Update(&hContext, buf, FIELD_BYTES); + /* + * + * Scalars: + * c = Random multiplier + * e = Hash + * s = Signature + * n = Order of G + * k = Private Key + * K = Public Key + * + * Points: + * G(x, y) = Generator (Base Point) + * + * We need to find the signature s that satisfies the equation with a given hash: + * P = sG + eK + * s = ek + c (mod n) <- computation optimization + */ - // Empty buffer, place r.y in little-endian. - memset(buf, 0, FIELD_BYTES); - BN_bn2bin(y, buf); - endian(buf, FIELD_BYTES); - - // Hash chunk of data. - SHA1_Update(&hContext, buf, FIELD_BYTES); - - // Store the final message from hContext in md. - SHA1_Final(md, &hContext); - - // h = (First-32(SHA1(pRaw, r.x, r.y)) >> 4 - hash = (md[0] | (md[1] << 8) | (md[2] << 16) | (md[3] << 24)) >> 4; - hash &= 0xfffffff; - - /* s = privateKey * hash + c; */ - // s = privateKey; + // s = ek; BN_copy(s, privateKey); + BN_mul_word(s, pHash); - // s *= hash; - BN_mul_word(s, hash); + // s += c (mod n) + BN_mod_add(s, s, c, genOrder, numContext); - // BN_mod_add() adds a to b % m and places the non-negative result in r. - // s = |s + c % order|; - BN_mod_add(s, s, c, order, ctx); - - // Convert s from BigNum back to bytecode and reverse the endianness. - BN_bn2bin(s, (BYTE *)&sig); - endian((BYTE *)&sig, BN_num_bytes(s)); + // Translate resulting scalar into a 64-bit integer (the byte order is little-endian). + BN_bn2lebinpad(s, (BYTE *)&pSignature, BN_num_bytes(s)); // Pack product key. - packXP(bKey, pRaw, hash, sig); + packXP(pRaw, pSerial, pHash, pSignature); - //printf("PID: %.8X\nHash: %.8X\nSig: %.8X %.8X\n", pRaw[0], hash, sig[1], sig[0]); - std::cout << " PID: " << std::hex << std::setw(8) << std::setfill('0') << pRaw << std::endl - << "Hash: " << std::hex << std::setw(8) << std::setfill('0') << hash << std::endl - << " Sig: " << std::hex << std::setw(8) << std::setfill('0') << sig << std::endl - << std::endl; + std::cout << " Serial: 0x" << std::hex << std::setw(8) << std::setfill('0') << pSerial << std::endl + << " Hash: 0x" << std::hex << std::setw(8) << std::setfill('0') << pHash << std::endl + << " Signature: 0x" << std::hex << std::setw(8) << std::setfill('0') << pSignature << std::endl + << std::endl; - } while (bKey[1] >= (1ULL << 50)); + EC_POINT_free(r); + } while (pRaw[1] > BITMASK(50)); // ↑ ↑ ↑ - // bKey[1] can't be longer than 50 bits, else the signature part will make - // the CD-key longer than 25 characters. + // pRaw[1] can't be longer than 50 bits, else the signature part + // will make the CD-key longer than 25 characters. - // Convert the key to Base24. - base24(pKey, (BYTE *)bKey); + // Convert bytecode to Base24 CD-key. + base24(pKey, (BYTE *)pRaw); BN_free(c); BN_free(s); BN_free(x); BN_free(y); - BN_CTX_free(ctx); - EC_POINT_free(r); + BN_CTX_free(numContext); } \ No newline at end of file