From df687c198e452706542fbd1b0560d081ba199817 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 14 Jun 2023 16:27:22 +0300 Subject: [PATCH] v3 milestone --- .idea/.gitignore | 8 + .idea/XPKeygen.iml | 8 + .idea/modules.xml | 8 + .idea/vcs.xml | 6 + Keygen.vcxproj | 1 + Keygen.vcxproj.filters | 42 ++- bink.cpp | 138 +++++++++ header.h | 187 +++++++----- key.cpp | 153 +++++----- main.cpp | 6 +- server.cpp | 635 +++++++++++++++++++++++------------------ utilities.cpp | 19 +- windows.cpp | 2 +- xp.cpp | 402 ++++++++++++++------------ 14 files changed, 991 insertions(+), 624 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/XPKeygen.iml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 bink.cpp diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/XPKeygen.iml b/.idea/XPKeygen.iml new file mode 100644 index 0000000..bc2cd87 --- /dev/null +++ b/.idea/XPKeygen.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..10bc744 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Keygen.vcxproj b/Keygen.vcxproj index 030da13..1e38300 100644 --- a/Keygen.vcxproj +++ b/Keygen.vcxproj @@ -272,6 +272,7 @@ + diff --git a/Keygen.vcxproj.filters b/Keygen.vcxproj.filters index 430b619..9298848 100644 --- a/Keygen.vcxproj.filters +++ b/Keygen.vcxproj.filters @@ -19,12 +19,17 @@ Source Files + + Source Files + Header Files - + + Resources + @@ -33,19 +38,38 @@ {DD359385-3E10-39A8-BDBE-E651CB1745C6} + + {8c8a7e56-fe48-4168-8eb0-0715690de23e} + - + + Resources + + + Resources + - - + + Resources + + + Resources + + + Resources + + + Resources + + + Resources + - - - - - + + Resources + \ No newline at end of file diff --git a/bink.cpp b/bink.cpp new file mode 100644 index 0000000..ee0f1d5 --- /dev/null +++ b/bink.cpp @@ -0,0 +1,138 @@ +// +// Created by Andrew on 24/05/2023. +// + +#include "header.h" + +#define BINK_RETAIL MAKEINTRESOURCEW(1) +#define BINK_OEM MAKEINTRESOURCEW(2) + +#define RT_BINK L"BINK" + +/* + Bink resource doesn't exist + The file you selected isn't a library + Bink resource is invalid +*/ +typedef struct _EC_BYTE_POINT { + CHAR x[256]; // x-coordinate of the point on the elliptic curve. + CHAR y[256]; // y-coordinate of the point on the elliptic curve. +} EC_BYTE_POINT; + +typedef struct _BINKHDR { + // BINK version - not stored in the resource. + ULONG32 dwVersion; + + // Original BINK header. + ULONG32 dwID; + ULONG32 dwSize; + ULONG32 dwHeaderLength; + ULONG32 dwChecksum; + ULONG32 dwDate; + ULONG32 dwKeySizeInDWORDs; + ULONG32 dwHashLength; + ULONG32 dwSignatureLength; + + // Extended BINK header. (Windows Server 2003+) + ULONG32 dwAuthCodeLength; + ULONG32 dwProductIDLength; +} BINKHDR; + +typedef struct _BINKDATA { + CHAR p[256]; // Finite Field order p. + CHAR a[256]; // Elliptic Curve parameter a. + CHAR b[256]; // Elliptic Curve parameter b. + + EC_BYTE_POINT G; // Base point (Generator) G. + EC_BYTE_POINT K; // Public key K. + EC_BYTE_POINT I; // Inverse of the public key K. +} BINKDATA; + +typedef struct _BINKEY { + BINKHDR header; + BINKDATA data; +} BINKEY; + +DWORD extractBINKResource(HMODULE hLibrary, BYTE **pData) { + HRSRC hRes = FindResourceW(hLibrary, BINK_OEM, RT_BINK); + DWORD dwSize = 0; + + if (hRes != NULL) { + dwSize = SizeofResource(hLibrary, hRes); + *pData = (BYTE *)LoadResource(hLibrary, hRes); + } + + return dwSize; +} + +BYTE hexToDecDigit(CHAR nDigit) { + nDigit = toupper(nDigit); + + if (nDigit >= '0' && nDigit <= '9') + return nDigit - '0'; + + else + return nDigit - 'A' + 10; +} + +ULONG32 byteToInteger(BYTE *pByte) { + return hexToDecDigit(pByte[0]) << 4 + hexToDecDigit(pByte[1]); +} + +void reverseBytes(CONST BYTE *pBytes, ULONG32 nBytes, BYTE *pReversed) { + for (int i = nBytes - 1; i >= 0; i--) { + memcpy((BYTE *)&pBytes[i * 2], (BYTE *)&pReversed[(nBytes - i + 1) * 2], 2 * sizeof(BYTE)); + } +} + +ULONG32 ulToInteger(BYTE *pUL, BOOL bLittleEndian) { + BYTE pULCopy[8] = { 0 }; + ULONG32 nUL = 0; + + if (pUL == NULL) + return 0; + + if (bLittleEndian) + reverseBytes(pUL, 4, pULCopy); + + for (int i = 0; i < 4; i++) { + nUL += byteToInteger(&pULCopy[i * 2]); + } + + return nUL; +} + +void decodeBINKResource(BYTE *pData, ULONG32 nLength, BINKEY *pBINK) { + ULONG32 nBlockBytes = 4; + + // If BINK is incomplete, return. + if (nLength < 0x170) return; + + ulToInteger(pData, TRUE); + + /*/ Read BINK header. + for (ULONG32 nOffset = 0; nOffset < sizeof(BINKHDR); nOffset += nBlockBytes) { + pBINK[nOffset] = + }*/ + +} + +void base(WCHAR *pPath) { + HMODULE pIDgen = LoadLibraryExW(pPath, NULL, LOAD_LIBRARY_AS_DATAFILE); + + if (pIDgen == NULL) + return; + + BYTE *pBuffer = NULL; + ULONG32 nLength = extractBINKResource(pIDgen, &pBuffer); + + if (nLength == 0) { + return; + } + + BINKEY pBINK = { 0 }; + + decodeBINKResource(pBuffer, nLength, &pBINK); + + FreeLibrary(pIDgen); +} \ No newline at end of file diff --git a/header.h b/header.h index 9035795..91941a4 100644 --- a/header.h +++ b/header.h @@ -16,14 +16,26 @@ #include #include -#define FIELD_BITS 384 -#define FIELD_BYTES (FIELD_BITS / 8) +#define PK_LENGTH 25 +#define NULL_TERMINATOR 1 -#define FIELD_BITS_2003 512 -#define FIELD_BYTES_2003 (FIELD_BITS_2003 / 8) +#define FIELD_BITS 384 +#define FIELD_BYTES (FIELD_BITS / 8) -#define PK_LENGTH 25 -#define NULL_TERMINATOR 1 +#define FIELD_BITS_2003 512 +#define FIELD_BYTES_2003 (FIELD_BITS_2003 / 8) + +#define SHA_MSG_LENGTH_XP (4 + 2 * FIELD_BYTES) +#define SHA_MSG_LENGTH_2003 (3 + 2 * FIELD_BYTES_2003) + +#define NEXTSNBITS(field, n, offset) (((QWORD)(field) >> (offset)) & ((1ULL << (n)) - 1)) +#define FIRSTNBITS(field, n) NEXTSNBITS((field), (n), 0) + +#define HIBYTES(field, bytes) NEXTSNBITS((QWORD)(field), ((bytes) * 8), ((bytes) * 8)) +#define LOBYTES(field, bytes) FIRSTNBITS((QWORD)(field), ((bytes) * 8)) + +#define BYDWORD(n) (DWORD)(*((n) + 0) | *((n) + 1) << 8 | *((n) + 2) << 16 | *((n) + 3) << 24) +#define BITMASK(n) ((1ULL << (n)) - 1) #define IDC_BUTTON1 1000 #define IDC_BUTTON2 1001 @@ -47,117 +59,140 @@ #define IDC_LABEL4 1058 #define IDC_LABEL5 1059 -typedef unsigned long ul32; +typedef uint64_t QWORD; -extern byte charset[]; +extern char pCharset[]; extern const char pXP[]; extern const long aXP; extern const long bXP; + // xp.cpp -bool keyXP( - char *pKey, - ul32 nRaw +VOID unpackXP( + QWORD (&pRaw)[2], + BOOL &pUpgrade, + DWORD &pSerial, + DWORD &pHash, + QWORD &pSignature ); -void unpackXP( - ul32 *serial, - ul32 *hash, - ul32 *sig, - ul32 *raw +VOID packXP( + QWORD (&pRaw)[2], + BOOL pUpgrade, + DWORD pSerial, + DWORD pHash, + QWORD pSignature ); -void packXP( - ul32 *raw, - ul32 *serial, - ul32 *hash, - ul32 *sig +BOOL verifyXPKey( + EC_GROUP *eCurve, + EC_POINT *basePoint, + EC_POINT *publicKey, + CHAR (&pKey)[PK_LENGTH + NULL_TERMINATOR] ); -bool verifyXPKey( - EC_GROUP *eCurve, - EC_POINT *generator, - EC_POINT *publicKey, - char *cdKey +VOID generateXPKey( + EC_GROUP *eCurve, + EC_POINT *basePoint, + BIGNUM *genOrder, + BIGNUM *privateKey, + DWORD pChannelID, + DWORD pSequence, + BOOL pUpgrade, + CHAR (&pKey)[PK_LENGTH + NULL_TERMINATOR] ); -void generateXPKey( - char *pKey, - EC_GROUP *eCurve, - EC_POINT *generator, - BIGNUM *order, - BIGNUM *privateKey, - ul32 *pRaw +BOOL keyXP( + CHAR(&pKey)[PK_LENGTH + NULL_TERMINATOR], + DWORD nChannelID, + DWORD nSequence, + BOOL bUpgrade ); + // server.cpp -bool keyServer( - char *pKey +VOID unpackServer( + QWORD (&pRaw)[2], + BOOL &pUpgrade, + DWORD &pChannelID, + DWORD &pHash, + QWORD &pSignature, + DWORD &pAuthInfo ); -void unpackServer( - ul32 *osFamily, - ul32 *hash, - ul32 *sig, - ul32 *prefix, - ul32 *raw +VOID packServer( + QWORD (&pRaw)[2], + BOOL pUpgrade, + DWORD pChannelID, + DWORD pHash, + QWORD pSignature, + DWORD pAuthInfo ); -void packServer( - ul32 *raw, - ul32 *osFamily, - ul32 *hash, - ul32 *sig, - ul32 *prefix +BOOL verifyServerKey( + EC_GROUP *eCurve, + EC_POINT *basePoint, + EC_POINT *publicKey, + CHAR (&cdKey)[PK_LENGTH + NULL_TERMINATOR] ); -bool verifyServerKey( - EC_GROUP *eCurve, - EC_POINT *generator, - EC_POINT *public_key, - char *cdKey +VOID generateServerKey( + EC_GROUP *eCurve, + EC_POINT *basePoint, + BIGNUM *genOrder, + BIGNUM *privateKey, + DWORD pChannelID, + DWORD pAuthInfo, + BOOL pUpgrade, + CHAR (&pKey)[PK_LENGTH + NULL_TERMINATOR] ); -void generateServerKey( - char *pKey, - EC_GROUP *eCurve, - EC_POINT *generator, - BIGNUM *order, - BIGNUM *privateKey, - ul32 *osFamily, - ul32 *prefix +BOOL keyServer( + CHAR (&pKey)[PK_LENGTH + NULL_TERMINATOR], + DWORD nChannelID, + DWORD nAuthInfo, + BOOL bUpgrade ); + // utilities.cpp -void endiannessConvert(byte *data, int length); -ul32 randomRange(ul32 dwLow, ul32 dwHigh); +void endian(byte *data, int length); +DWORD randomRange(DWORD dwLow, DWORD dwHigh); void stopAudio(); bool playAudio(HINSTANCE hInstance, WCHAR *lpName, UINT bFlags); EC_GROUP *initializeEllipticCurve( - const char *pSel, - long aSel, - long bSel, - const char *generatorXSel, - const char *generatorYSel, - const char *publicKeyXSel, - const char *publicKeyYSel, - BIGNUM *genOrderSel, - BIGNUM *privateKeySel, - EC_POINT **genPoint, - EC_POINT **pubPoint - ); + const char *pSel, + long aSel, + long bSel, + const char *generatorXSel, + const char *generatorYSel, + const char *publicKeyXSel, + const char *publicKeyYSel, + BIGNUM *genOrderSel, + BIGNUM *privateKeySel, + EC_POINT **genPoint, + EC_POINT **pubPoint +); + +int BN_bn2lebin(const BIGNUM *a, unsigned char *to, int tolen); + // key.cpp -void unbase24(ul32 *byteSeq, const char *cdKey); -void base24(char *cdKey, ul32 *byteSeq); +bool unbase24(BYTE *byteSeq, CHAR (&pKey)[PK_LENGTH + NULL_TERMINATOR]); +void base24(BYTE *byteSeq, CHAR(&pKey)[PK_LENGTH + NULL_TERMINATOR]); void formatXP(WCHAR *pBSection, WCHAR *pCSection, WCHAR *pText); void formatServer(WCHAR *pText); + // windows.cpp bool InitializeWindow(HINSTANCE hInstance); + +// bink.cpp +void base(WCHAR *pPath); + #endif //KEYGEN_HEADER_H diff --git a/key.cpp b/key.cpp index 56a52ee..9a040ad 100644 --- a/key.cpp +++ b/key.cpp @@ -4,47 +4,24 @@ #include "header.h" -/* Converts from byte sequence to the CD-key. */ -void base24(char *cdKey, ul32 *byteSeq) { - byte rbs[16]; - BIGNUM *z; - - // Copy byte sequence to the reversed byte sequence. - memcpy(rbs, byteSeq, sizeof(rbs)); - - // Skip trailing zeroes and reverse y. - int length; - - for (length = 15; rbs[length] == 0; length--); - endiannessConvert(rbs, ++length); - - // Convert reversed byte sequence to BigNum z. - z = BN_bin2bn(rbs, length, nullptr); - - // Divide z by 24 and convert the remainder to a CD-key char. - cdKey[25] = 0; - - for (int i = 24; i >= 0; i--) - cdKey[i] = charset[BN_div_word(z, 24)]; - - BN_free(z); -} - /* Converts from CD-key to a byte sequence. */ -void unbase24(ul32 *byteSeq, const char *cdKey) { - byte pDecodedKey[PK_LENGTH + NULL_TERMINATOR]{}; +bool unbase24(BYTE *byteSeq, CHAR(&pKey)[PK_LENGTH + NULL_TERMINATOR]) { + BYTE pDecodedKey[PK_LENGTH + NULL_TERMINATOR]{}; BIGNUM *y = BN_new(); BN_zero(y); // Remove dashes from the CD-key and put it into a Base24 byte array. - for (int i = 0, k = 0; i < strlen(cdKey) && k < PK_LENGTH; i++) { - for (int j = 0; j < 24; j++) { - if (cdKey[i] != '-' && cdKey[i] == charset[j]) { + for (int i = 0, k = 0; i < strlen(pKey) && k < PK_LENGTH; i++) { + for (int j = 0; j < strlen(pCharset); j++) { + if (pKey[i] == pCharset[j]) { pDecodedKey[k++] = j; break; } } + + // If the k-index hasn't been incremented, and it's due to the key being garbage, quit. + if (pKey[i] != '-' && k == i) return false; } // Empty byte sequence. @@ -52,7 +29,7 @@ void unbase24(ul32 *byteSeq, const char *cdKey) { // Calculate the weighed sum of byte array elements. for (int i = 0; i < PK_LENGTH; i++) { - BN_mul_word(y, PK_LENGTH - 1); + BN_mul_word(y, strlen(pCharset)); BN_add_word(y, pDecodedKey[i]); } @@ -60,16 +37,44 @@ void unbase24(ul32 *byteSeq, const char *cdKey) { int n = BN_num_bytes(y); // Place the generated code into the byte sequence. - BN_bn2bin(y, (byte *)byteSeq); + BN_bn2bin(y, byteSeq); BN_free(y); // Reverse the byte sequence. - endiannessConvert((byte *) byteSeq, n); + endian(byteSeq, n); + + return true; +} + +/* Converts from byte sequence to the CD-key. */ +void base24(BYTE *byteSeq, CHAR(&pKey)[PK_LENGTH + NULL_TERMINATOR]) { + BYTE rbyteSeq[16]; + BIGNUM *z; + + // Copy byte sequence to the reversed byte sequence. + memcpy(rbyteSeq, byteSeq, sizeof(rbyteSeq)); + + // Skip trailing zeroes and reverse y. + int length; + + for (length = 15; rbyteSeq[length] == 0; length--); + endian(rbyteSeq, ++length); + + // Convert reversed byte sequence to BigNum z. + z = BN_bin2bn(rbyteSeq, length, nullptr); + + // Divide z by 24 and convert the remainder to a CD-key char. + pKey[PK_LENGTH] = '\0'; + + for (int i = PK_LENGTH - 1; i >= 0; i--) + pKey[i] = pCharset[BN_div_word(z, 24)]; + + BN_free(z); } /* Formats Windows XP key output. */ void formatXP(WCHAR *pBSection, WCHAR *pCSection, WCHAR *pText) { - WCHAR pFPK[32]{}; + WCHAR pDashedKey[PK_LENGTH + 4 + NULL_TERMINATOR]{}; int pSSection = 0; @@ -79,65 +84,69 @@ void formatXP(WCHAR *pBSection, WCHAR *pCSection, WCHAR *pText) { while (pSSection < 0) pSSection += 7; - char pKey[PK_LENGTH + NULL_TERMINATOR]{}; - ul32 msDigits = _wtoi(pBSection), - lsDigits = _wtoi(pCSection); + CHAR pKey[PK_LENGTH + NULL_TERMINATOR]{}; + DWORD pChannelID = _wtoi(pBSection), + pSequence = _wtoi(pCSection); - ul32 nRPK = msDigits * 1'000'000 + lsDigits, - hash = 0, - bKey[4]{}, - bSig[2]{}; + DWORD pHash; + QWORD pRaw[2]{}, + pSignature; - bool bValid = keyXP(pKey, nRPK); + bool bValid = keyXP(pKey, pChannelID, pSequence, false); - unbase24(bKey, pKey); - unpackXP(nullptr, &hash, bSig, bKey); + DWORD pSerial; + BOOL pUpgrade = false; + + unbase24((BYTE *)pRaw, pKey); + unpackXP(pRaw, pUpgrade, pSerial, pHash, pSignature); for (int i = 0; i < 5; i++) - wsprintfW(pFPK, L"%s%s%.5S", pFPK, i != 0 ? L"-" : L"", &pKey[5 * i]); + wsprintfW(pDashedKey, L"%s%s%.5S", pDashedKey, i != 0 ? L"-" : L"", &pKey[5 * i]); - wsprintfW( + swprintf( pText, - L"Product ID:\tPPPPP-%03d-%06d%d-23XXX\r\n\r\nBytecode:\t%08lX %08lX %08lX %08lX\r\nHash:\t\t%08lX\r\nSignature:\t%08lX %08lX\r\nCurve Point:\t%s\r\n\r\n%s\r\n", - nRPK / 1'000'000, - nRPK % 1'000'000, + L"Product ID:\tPPPPP-%03d-%06d%d-23XXX\r\n\r\nBytecode:\t%016llX %016llX\r\nHash:\t\t%lX\r\nSignature:\t%llX\r\nCurve Point:\t%s\r\n\r\n%s\r\n", + pSerial / 1'000'000, + pSerial % 1'000'000, pSSection, - bKey[3], bKey[2], bKey[1], bKey[0], - hash, - bSig[1], bSig[0], + pRaw[1], pRaw[0], + pHash, + pSignature, bValid ? L"True" : L"False", - pFPK + pDashedKey ); } /* Formats Windows Server 2003 key output. */ void formatServer(WCHAR *pText) { - WCHAR pFPK[32]{}; + WCHAR pDashedKey[32]{}; char pKey[PK_LENGTH + NULL_TERMINATOR]{}; - ul32 hash = 0, - osFamily = 0, - prefix = 0, - bKey[4]{}, - bSig[2]{}; + DWORD pHash = 0, + pChannelID = 0, + pAuthInfo = 0; - bool bValid = keyServer(pKey); + QWORD pRaw[2]{}, + pSignature; - unbase24(bKey, pKey); - unpackServer(&osFamily, &hash, bSig, &prefix, bKey); + BOOL pUpgrade = false; + bool bValid = keyServer(pKey, 640, 0, pUpgrade); + + unbase24((BYTE *)pRaw, pKey); + unpackServer(pRaw, pUpgrade, pChannelID, pHash, pSignature, pAuthInfo); for (int i = 0; i < 5; i++) - wsprintfW(pFPK, L"%s%s%.5S", pFPK, i != 0 ? L"-" : L"", &pKey[5 * i]); + wsprintfW(pDashedKey, L"%s%s%.5S", pDashedKey, i != 0 ? L"-" : L"", &pKey[5 * i]); - wsprintfW( + swprintf( pText, - L"Bytecode:\t%08lX %08lX %08lX %08lX\r\nOS Family:\t%d\r\nHash:\t\t%08lX\r\nSignature:\t%08lX %08lX\r\nPrefix:\t\t%04lX\r\nCurve Point:\t%s\r\n\r\n%s\r\n", - bKey[3], bKey[2], bKey[1], bKey[0], - osFamily, - hash, - bSig[1], bSig[0], - prefix, + L"Bytecode:\t%016llX %016llX\r\nChannel ID:\t%d\r\nHash:\t\t%lX\r\nSignature:\t%llX\r\nAuthInfo:\t%d\r\nCurve Point:\t%s\r\n\r\n%s\r\n", + pRaw[1], pRaw[0], + pChannelID, + pHash, + pSignature, + pAuthInfo, bValid ? L"True" : L"False", - pFPK + pDashedKey ); } diff --git a/main.cpp b/main.cpp index e72141e..dad37a6 100644 --- a/main.cpp +++ b/main.cpp @@ -61,10 +61,12 @@ #include "header.h" -byte charset[] = "BCDFGHJKMPQRTVWXY2346789"; +char pCharset[] = "BCDFGHJKMPQRTVWXY2346789"; -INT wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ WCHAR *pCmdLine, _In_ INT nCmdShow) { +INT wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ WCHAR *pCmdLine, _In_ INT nCmdShow) { srand(GetTickCount64()); + //base(L"D:\\Desktop\\ECC Research\\pIDgen\\pidgenxp.dll"); + return InitializeWindow(hInstance); } diff --git a/server.cpp b/server.cpp index f982bc1..d77b07b 100644 --- a/server.cpp +++ b/server.cpp @@ -4,6 +4,7 @@ #include "header.h" +/* Windows Server 2003 */ const char pSv[] = "C9AE7AED19F6A7E100AADE98134111AD8118E59B8264734327940064BC675A0C682E19C89695FBFA3A4653E47D47FD7592258C7E3C3C61BBEA07FE5A7E842379"; const long aSv = 1; const long bSv = 0; @@ -22,349 +23,427 @@ const char genOrderSv[] = "4CC5C56529F0237D"; // Computed private key const char privateKeySv[] = "2606120F59C05118"; -void unpackServer(ul32 *osFamily, ul32 *hash, ul32 *sig, ul32 *prefix, ul32 *raw) { - - // We're assuming that the quantity of information within the product key is at most 114 bits. - // log2(24^25) = 114. +/* Windows XP x64 +Public key (-K) = (1989960177638374390878377737764297057685259206834686428253479199374616869742150776410973898745805799780071536831208959469038333664656928533078897351495263; 2680493145252003995204016438404731303203625133293449171132691660710342616258476835192643732221910418645447349019141673820306444587247165566828458285756618) +Order of base point G (n) = 4710798293276956193 +Private key (k) = 4699066967014190092 for INVERSE. 11731326262766101 - // OS Family = Bits [0..10] -> 11 bits - osFamily[0] = raw[0] & 0x7ff; - // Hash = Bits [11..41] -> 31 bits - hash[0] = ((raw[0] >> 11) | (raw[1] << 21)) & 0x7fffffff; - - // Signature = Bits [42..103] -> 62 bits - sig[0] = (raw[1] >> 10) | (raw[2] << 22); - sig[1] = ((raw[2] >> 10) | (raw[3] << 22)) & 0x3fffffff; - - // Prefix = Bits [104..113] -> 10 bits - prefix[0] = (raw[3] >> 8) & 0x3ff; +const char pSv[] = "D4B49D04A01EF209121C370DCF0D6292569EC65B8F147A8C62319B6B90DEA2D1CD45199B93582732BFEE27F40BF62D7EB2559BCD08041E301E0D14037A25D989"; +const long aSv = 1; +const long bSv = 0; + +const char genXSv[] = "828A23E65A03F2CE12342DC2B3AA4089C1447DD5C4DC36C0470885A4662F10187037F72B2216C3F671B434267A329BD3363BB27055F0EBBA8A0ABEF451D3F6A3"; +const char genYSv[] = "23B0823295C9CB669E1643B298624083F68C58F14FEEC55D0B247EF37B353A1066F502D7BC71050056C7D006156A26CC9222F5135FB8B255D7773AE0CDCA31E2"; + +const char pubXSv[] = "25FEB90513F63C0833F1096369149E65C9359F4BCC8DE9A8F647030F96485BC71929594FF369DB967910B8F0A59BC7C30CF0D38311486293BA0B2952EE648E5F"; +const char pubYSv[] = "A186A2C2913E5584F05E97D3CD49E354E6C41BE329877D7FCC7B2BF877A0B00C9298901D305D7FF012FF7902B4202D4ED64D6A90C6AD05960253BAB8F69D68BF"; + +// Order of G <- CALCULATED ON MY i7-12700K in 20 seconds +const char genOrderSv[] = "41601E16BF4A1621"; + +// Computed private key <- CALCULATED ON MY i7-12700K in 5 minutes 40 seconds +const char privateKeySv[] = "4136708280A72C0C";*/ + + +/* Windows XP x64 OEM +const char pSv[] = "A6FEDE9568C7863685F783F864A5943D34DED45EC460EEB2EC0455B01BC3C4D21FE081E479F2338BAAF7B10903AC89D23774938F41FDBFB6F16A615ECE5A04A1"; +const long aSv = 1; +const long bSv = 0; + +const char genXSv[] = "3CCFE20244697894A5CF8F8A57F335462C8C7C4935E171A373C2C1BA85C304D121A48931A99E4DD911945B410E10DEF21C00B2ED33FEF4E8F6FCBE16014E0AA8"; +const char genYSv[] = "7D3F4583D6A45EF6547532B2AE6AC83281317A212223A47ADA92FB48DF055A225DD3E8DF17850EBFAD744780C8166B14F0A39C96B3D216E2247A89518985F6F8"; + +const char pubXSv[] = "19D3C8A75DACEAB3CE42970BCF3097F712FD3F6D3B171BE55D7AEF6210C48194480E998AFAC181935DCB9E66BD23769AF5E7ABB8ED2A7E5FAABD4FD1F8D24F7C"; +const char pubYSv[] = "47A138CDB3C51BEB5443A00FD24734C6DE5DCE6DBA3B2EC337984C09B1CB108E45E8B50F78AEE5FBCA068C0B285576AC26099BD4D52AE2AF9F32A30A340705AF"; + +// Order of G <- CALCULATED ON MY i7-12700K in 2 hours (single threaded). +const char genOrderSv[] = "4782F84242B0A5E1"; + +// Computed private key <- CALCULATED ON MY i7-12700K in 5 minutes 40 seconds +const char privateKeySv[] = "15F9B7336005CB82";// or "3189410EE2AADA5F"; +*/ + +/* Unpacks the Windows Server 2003-like Product Key. */ +VOID unpackServer( + QWORD (&pRaw)[2], + BOOL &pUpgrade, + DWORD &pChannelID, + DWORD &pHash, + QWORD &pSignature, + DWORD &pAuthInfo +) { + // We're assuming that the quantity of information within the product key is at most 114 bits. + // log2(24^25) = 114. + + // Upgrade = Bit 0 + pUpgrade = FIRSTNBITS(pRaw[0], 1); + + // Channel ID = Bits [1..10] -> 10 bits + pChannelID = NEXTSNBITS(pRaw[0], 10, 1); + + // Hash = Bits [11..41] -> 31 bits + pHash = NEXTSNBITS(pRaw[0], 31, 11); + + // Signature = Bits [42..103] -> 62 bits + // The quad-word signature overlaps AuthInfo in bits 104 and 105, + // hence Microsoft employs a secret technique called: Signature = HIDWORD(Signature) >> 2 | LODWORD(Signature) + pSignature = NEXTSNBITS(pRaw[1], 30, 10) << 32 | FIRSTNBITS(pRaw[1], 10) << 22 | NEXTSNBITS(pRaw[0], 22, 42); + + // AuthInfo = Bits [104..113] -> 10 bits + pAuthInfo = NEXTSNBITS(pRaw[1], 10, 40); } -void packServer(ul32 *raw, ul32 *osFamily, ul32 *hash, ul32 *sig, ul32 *prefix) { - raw[0] = osFamily[0] | (hash[0] << 11); - raw[1] = (hash[0] >> 21) | (sig[0] << 10); - raw[2] = (sig[0] >> 22) | (sig[1] << 10); - raw[3] = (sig[1] >> 22) | (prefix[0] << 8); +/* Packs the Windows Server 2003-like Product Key. */ +VOID packServer( + QWORD (&pRaw)[2], + BOOL pUpgrade, + DWORD pChannelID, + DWORD pHash, + QWORD pSignature, + DWORD pAuthInfo +) { + // AuthInfo [113..104] <- Signature [103..42] <- Hash [41..11] <- Channel ID [10..1] <- Upgrade [0] + pRaw[0] = FIRSTNBITS(pSignature, 22) << 42 | (QWORD)pHash << 11 | (QWORD)pChannelID << 1 | pUpgrade; + pRaw[1] = FIRSTNBITS(pAuthInfo, 10) << 40 | NEXTSNBITS(pSignature, 40, 22); } -bool verifyServerKey(EC_GROUP *eCurve, EC_POINT *generator, EC_POINT *publicKey, char *cdKey) { - BN_CTX *context = BN_CTX_new(); - - // Convert Base24 CD-key to bytecode. - ul32 osFamily, hash, sig[2], prefix; - ul32 bKey[4]{}; - unbase24(bKey, cdKey); +/* Verifies the Windows Server 2003-like Product Key. */ +BOOL verifyServerKey( + EC_GROUP *eCurve, + EC_POINT *basePoint, + EC_POINT *publicKey, + CHAR (&pKey)[PK_LENGTH + NULL_TERMINATOR] +) { + BN_CTX *context = BN_CTX_new(); - // Extract segments from the bytecode and reverse the signature. - unpackServer(&osFamily, &hash, sig, &prefix, bKey); - endiannessConvert((byte *)sig, 8); + QWORD bKey[2]{}, + pSignature = 0; - byte t[FIELD_BYTES_2003]{}, md[SHA_DIGEST_LENGTH]{}; - ul32 checkHash, newHash[2]{}; - - SHA_CTX hContext; - - // H = SHA-1(5D || OS Family || Hash || Prefix || 00 00) - SHA1_Init(&hContext); - - t[0] = 0x5D; - t[1] = (osFamily & 0xff); - t[2] = (osFamily & 0xff00) >> 8; - t[3] = (hash & 0xff); - t[4] = (hash & 0xff00) >> 8; - t[5] = (hash & 0xff0000) >> 16; - t[6] = (hash & 0xff000000) >> 24; - t[7] = (prefix & 0xff); - t[8] = (prefix & 0xff00) >> 8; - t[9] = 0x00; - t[10] = 0x00; - - SHA1_Update(&hContext, t, 11); - SHA1_Final(md, &hContext); - - // First word. - newHash[0] = md[0] | (md[1] << 8) | (md[2] << 16) | (md[3] << 24); + DWORD pData, + pChannelID, + pHash, + pAuthInfo; - // Second word, right shift 2 bits. - newHash[1] = (md[4] | (md[5] << 8) | (md[6] << 16) | (md[7] << 24)) >> 2; - newHash[1] &= 0x3FFFFFFF; + BOOL pUpgrade; - endiannessConvert((byte *)newHash, 8); + // Convert Base24 CD-key to bytecode. + unbase24((BYTE *)bKey, pKey); - BIGNUM *x = BN_new(); - BIGNUM *y = BN_new(); - BIGNUM *s = BN_bin2bn((byte *)sig, 8, nullptr); - BIGNUM *e = BN_bin2bn((byte *)newHash, 8, nullptr); + // Extract product key segments from bytecode. + unpackServer(bKey, pUpgrade, pChannelID, pHash, pSignature, pAuthInfo); - EC_POINT *u = EC_POINT_new(eCurve); - EC_POINT *v = EC_POINT_new(eCurve); + pData = pChannelID << 1 | pUpgrade; - // EC_POINT_mul calculates r = generator * n + q * m. - // v = s * (s * generator + e * publicKey) + BYTE msgDigest[SHA_DIGEST_LENGTH]{}, + msgBuffer[SHA_MSG_LENGTH_2003]{}, + xBin[FIELD_BYTES_2003]{}, + yBin[FIELD_BYTES_2003]{}; - // u = generator * s - EC_POINT_mul(eCurve, u, nullptr, generator, s, context); + // Assemble the first SHA message. + msgBuffer[0x00] = 0x5D; + msgBuffer[0x01] = (pData & 0x00FF); + msgBuffer[0x02] = (pData & 0xFF00) >> 8; + msgBuffer[0x03] = (pHash & 0x000000FF); + msgBuffer[0x04] = (pHash & 0x0000FF00) >> 8; + msgBuffer[0x05] = (pHash & 0x00FF0000) >> 16; + msgBuffer[0x06] = (pHash & 0xFF000000) >> 24; + msgBuffer[0x07] = (pAuthInfo & 0x00FF); + msgBuffer[0x08] = (pAuthInfo & 0xFF00) >> 8; + msgBuffer[0x09] = 0x00; + msgBuffer[0x0A] = 0x00; - // v = publicKey * e - EC_POINT_mul(eCurve, v, nullptr, publicKey, e, context); + // newSignature = SHA1(5D || Channel ID || Hash || AuthInfo || 00 00) + SHA1(msgBuffer, 11, msgDigest); - // v += u - EC_POINT_add(eCurve, v, u, v, context); - - // v *= s - EC_POINT_mul(eCurve, v, nullptr, v, s, context); + // Translate the byte digest into a 64-bit integer - this is our computed intermediate signature. + // As the signature is only 62 bits long at most, we have to truncate it by shifting the high DWORD right 2 bits (per spec). + QWORD iSignature = NEXTSNBITS(BYDWORD(&msgDigest[4]), 30, 2) << 32 | BYDWORD(msgDigest); - // 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_GFp(eCurve, v, x, y, context); + /* + * + * Scalars: + * e = Hash + * s = Schnorr Signature + * + * Points: + * G(x, y) = Generator (Base Point) + * K(x, y) = Public Key + * + * Equation: + * P = s(sG + eK) + * + */ - // Hash = First31(SHA-1(79 || OS Family || v.x || v.y)) - SHA1_Init(&hContext); + BIGNUM *e = BN_lebin2bn((BYTE *)&iSignature, sizeof(iSignature), nullptr), + *s = BN_lebin2bn((BYTE *)&pSignature, sizeof(pSignature), nullptr), + *x = BN_new(), + *y = BN_new(); - t[0] = 0x79; - t[1] = (osFamily & 0xff); - t[2] = (osFamily & 0xff00) >> 8; + // Create 2 points on the elliptic curve. + EC_POINT *p = EC_POINT_new(eCurve); + EC_POINT *t = EC_POINT_new(eCurve); - // Hash chunk of data. - SHA1_Update(&hContext, t, 3); - - // Empty buffer, place v.y in little-endian. - memset(t, 0, FIELD_BYTES_2003); - BN_bn2bin(x, t); - endiannessConvert(t, FIELD_BYTES_2003); + // t = sG + EC_POINT_mul(eCurve, t, nullptr, basePoint, s, context); - // Hash chunk of data. - SHA1_Update(&hContext, t, FIELD_BYTES_2003); + // p = eK + EC_POINT_mul(eCurve, p, nullptr, publicKey, e, context); - // Empty buffer, place v.y in little-endian. - memset(t, 0, FIELD_BYTES_2003); - BN_bn2bin(y, t); - endiannessConvert(t, FIELD_BYTES_2003); + // p += t + EC_POINT_add(eCurve, p, t, p, context); - // Hash chunk of data. - SHA1_Update(&hContext, t, FIELD_BYTES_2003); - - // Store the final message from hContext in md. - SHA1_Final(md, &hContext); + // p *= s + EC_POINT_mul(eCurve, p, nullptr, p, s, context); - // Hash = First31(SHA-1(79 || OS Family || v.x || v.y)) - checkHash = (md[0] | (md[1] << 8) | (md[2] << 16) | (md[3] << 24)) & 0x7fffffff; + // x = p.x; y = p.y; + EC_POINT_get_affine_coordinates(eCurve, p, x, y, context); - BN_free(s); - BN_free(e); - BN_free(x); - BN_free(y); + // Convert resulting point coordinates to bytes. + BN_bn2lebin(x, xBin, FIELD_BYTES_2003); + BN_bn2lebin(y, yBin, FIELD_BYTES_2003); - BN_CTX_free(context); + // Assemble the second SHA message. + msgBuffer[0x00] = 0x79; + msgBuffer[0x01] = (pData & 0x00FF); + msgBuffer[0x02] = (pData & 0xFF00) >> 8; - EC_POINT_free(v); - EC_POINT_free(u); + memcpy((void *)&msgBuffer[3], (void *)xBin, FIELD_BYTES_2003); + memcpy((void *)&msgBuffer[3 + FIELD_BYTES_2003], (void *)yBin, FIELD_BYTES_2003); - // If we managed to generate a key with the same hash, the key is correct. - return checkHash == hash; + // compHash = SHA1(79 || Channel ID || p.x || p.y) + SHA1(msgBuffer, SHA_MSG_LENGTH_2003, msgDigest); + + // Translate the byte digest into a 32-bit integer - this is our computed hash. + // Truncate the hash to 31 bits. + DWORD compHash = BYDWORD(msgDigest) & BITMASK(31); + + BN_free(s); + BN_free(e); + BN_free(x); + BN_free(y); + + BN_CTX_free(context); + + EC_POINT_free(p); + EC_POINT_free(t); + + // If the computed hash checks out, the key is valid. + return compHash == pHash; } -void generateServerKey(char *pKey, EC_GROUP *eCurve, EC_POINT *generator, BIGNUM *order, BIGNUM *privateKey, ul32 *osFamily, ul32 *prefix) { - EC_POINT *r = EC_POINT_new(eCurve); - BN_CTX *ctx = BN_CTX_new(); +/* Generates the Windows Server 2003-like Product Key. */ +VOID generateServerKey( + EC_GROUP *eCurve, + EC_POINT *basePoint, + BIGNUM *genOrder, + BIGNUM *privateKey, + DWORD pChannelID, + DWORD pAuthInfo, + BOOL pUpgrade, + CHAR (&pKey)[PK_LENGTH + NULL_TERMINATOR] +) { + BN_CTX *numContext = BN_CTX_new(); - ul32 bKey[4]{}, - bSig[2]{}; - - do { - BIGNUM *c = BN_new(); - BIGNUM *s = BN_new(); - BIGNUM *x = BN_new(); - BIGNUM *y = BN_new(); - BIGNUM *b = BN_new(); + BIGNUM *c = BN_new(), + *e = BN_new(), + *s = BN_new(), + *x = BN_new(), + *y = BN_new(); - ul32 hash = 0, h[2]{}; + QWORD pRaw[2]{}, + pSignature = 0; - memset(bKey, 0, 4); - memset(bSig, 0, 2); + // Data segment of the RPK. + DWORD pData = pChannelID << 1 | pUpgrade; + BOOL noSquare; - // Generate a random number c consisting of 512 bits without any constraints. - BN_rand(c, FIELD_BITS_2003, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY); - - // r = generator * c - EC_POINT_mul(eCurve, r, nullptr, generator, c, ctx); + do { + EC_POINT *r = EC_POINT_new(eCurve); - // x = r.x; y = r.y; - EC_POINT_get_affine_coordinates(eCurve, r, x, y, ctx); - - SHA_CTX hContext; - byte md[SHA_DIGEST_LENGTH]{}, buf[FIELD_BYTES_2003]{}; + // Generate a random number c consisting of 512 bits without any constraints. + BN_rand(c, FIELD_BITS_2003, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY); - // Hash = SHA-1(79 || OS Family || r.x || r.y) - SHA1_Init(&hContext); + // R = cG + EC_POINT_mul(eCurve, r, nullptr, basePoint, c, numContext); - buf[0] = 0x79; + // Acquire its coordinates. + // x = R.x; y = R.y; + EC_POINT_get_affine_coordinates(eCurve, r, x, y, numContext); - buf[1] = (*osFamily & 0xff); - buf[2] = (*osFamily & 0xff00) >> 8; + BYTE msgDigest[SHA_DIGEST_LENGTH]{}, + msgBuffer[SHA_MSG_LENGTH_2003]{}, + xBin[FIELD_BYTES_2003]{}, + yBin[FIELD_BYTES_2003]{}; - SHA1_Update(&hContext, buf, 3); - - memset(buf, 0, FIELD_BYTES_2003); + // Convert resulting point coordinates to bytes. + BN_bn2lebin(x, xBin, FIELD_BYTES_2003); + BN_bn2lebin(y, yBin, FIELD_BYTES_2003); - BN_bn2bin(x, buf); - endiannessConvert((byte *) buf, FIELD_BYTES_2003); - SHA1_Update(&hContext, buf, FIELD_BYTES_2003); - - memset(buf, 0, FIELD_BYTES_2003); + // Assemble the first SHA message. + msgBuffer[0x00] = 0x79; + msgBuffer[0x01] = (pData & 0x00FF); + msgBuffer[0x02] = (pData & 0xFF00) >> 8; - BN_bn2bin(y, buf); - endiannessConvert((byte *) buf, FIELD_BYTES_2003); + memcpy((void *)&msgBuffer[3], (void *)xBin, FIELD_BYTES_2003); + memcpy((void *)&msgBuffer[3 + FIELD_BYTES_2003], (void *)yBin, FIELD_BYTES_2003); - SHA1_Update(&hContext, buf, FIELD_BYTES_2003); - SHA1_Final(md, &hContext); + // pHash = SHA1(79 || Channel ID || R.x || R.y) + SHA1(msgBuffer, SHA_MSG_LENGTH_2003, msgDigest); - hash = (md[0] | (md[1] << 8) | (md[2] << 16) | (md[3] << 24)) & 0x7fffffff; - - // H = SHA-1(5D || OS Family || Hash || Prefix || 00 00) - SHA1_Init(&hContext); - buf[0] = 0x5D; - - buf[1] = (*osFamily & 0xff); - buf[2] = (*osFamily & 0xff00) >> 8; - - buf[3] = (hash & 0xff); - buf[4] = (hash & 0xff00) >> 8; - buf[5] = (hash & 0xff0000) >> 16; - buf[6] = (hash & 0xff000000) >> 24; - - buf[7] = prefix[0] & 0xff; - buf[8] = (prefix[0] & 0xff00) >> 8; - - buf[9] = 0x00; - buf[10] = 0x00; + // Translate the byte digest into a 32-bit integer - this is our computed hash. + // Truncate the hash to 31 bits. + DWORD pHash = BYDWORD(msgDigest) & BITMASK(31); - // Input length is 11 bytes. - SHA1_Update(&hContext, buf, 11); - SHA1_Final(md, &hContext); + // Assemble the second SHA message. + msgBuffer[0x00] = 0x5D; + msgBuffer[0x01] = (pData & 0x00FF); + msgBuffer[0x02] = (pData & 0xFF00) >> 8; + msgBuffer[0x03] = (pHash & 0x000000FF); + msgBuffer[0x04] = (pHash & 0x0000FF00) >> 8; + msgBuffer[0x05] = (pHash & 0x00FF0000) >> 16; + msgBuffer[0x06] = (pHash & 0xFF000000) >> 24; + msgBuffer[0x07] = (pAuthInfo & 0x00FF); + msgBuffer[0x08] = (pAuthInfo & 0xFF00) >> 8; + msgBuffer[0x09] = 0x00; + msgBuffer[0x0A] = 0x00; - // First word. - h[0] = md[0] | (md[1] << 8) | (md[2] << 16) | (md[3] << 24); - - // Second word, right shift 2 bits. - h[1] = (md[4] | (md[5] << 8) | (md[6] << 16) | (md[7] << 24)) >> 2; - h[1] &= 0x3FFFFFFF; + // newSignature = SHA1(5D || Channel ID || Hash || AuthInfo || 00 00) + SHA1(msgBuffer, 11, msgDigest); - endiannessConvert((byte *)h, 8); - BN_bin2bn((byte *)h, 8, b); - - /* - * Signature * (Signature * G + H * K) = rG (mod p) - * ↓ K = kG ↓ - * - * Signature * (Signature * G + H * k * G) = rG (mod p) - * Signature^2 * G + Signature * HkG = rG (mod p) - * G(Signature^2 + Signature * HkG) = G (mod p) * r - * ↓ G^(-1)(G (mod p)) = (mod n), n = order of G ↓ - * - * Signature^2 + Hk * Signature = r (mod n) - * Signature = -(b +- sqrt(D)) / 2a → Signature = (-Hk +- sqrt((Hk)^2 + 4r)) / 2 - * - * S = (-Hk +- sqrt((Hk)^2 + 4r)) (mod n) / 2 - * - * S = s - * H = b - * k = privateKey - * n = order - * r = c - * - * s = ( ( -b * privateKey +- sqrt( (b * privateKey)^2 + 4c ) ) / 2 ) % order - */ + // Translate the byte digest into a 64-bit integer - this is our computed intermediate signature. + // As the signature is only 62 bits long at most, we have to truncate it by shifting the high DWORD right 2 bits (per spec). + QWORD iSignature = NEXTSNBITS(BYDWORD(&msgDigest[4]), 30, 2) << 32 | BYDWORD(msgDigest); - // b = (b * privateKey) % order - BN_mod_mul(b, b, privateKey, order, ctx); - - // s = b - BN_copy(s, b); + BN_lebin2bn((BYTE *)&iSignature, sizeof(iSignature), e); - // s = (s % order)^2 - BN_mod_sqr(s, s, order, ctx); + /* + * + * Scalars: + * c = Random multiplier + * e = Intermediate Signature + * s = Signature + * n = Order of G + * k = Private Key + * + * Points: + * G(x, y) = Generator (Base Point) + * R(x, y) = Random derivative of the generator + * K(x, y) = Public Key + * + * Equation: + * s(sG + eK) = R (mod p) + * ↓ K = kG; R = cG ↓ + * + * s(sG + ekG) = cG (mod p) + * s(s + ek)G = cG (mod p) + * ↓ G cancels out, the scalar arithmetic shrinks to order n ↓ + * + * s(s + ek) = c (mod n) + * s² + (ek)s - c = 0 (mod n) + * ↓ This is a quadratic equation in respect to the signature ↓ + * + * s = (-ek ± √((ek)² + 4c)) / 2 (mod n) + */ - // c <<= 2 (c = 4c) - BN_lshift(c, c, 2); + // e = ek (mod n) + BN_mod_mul(e, e, privateKey, genOrder, numContext); - // s = s + c - BN_add(s, s, c); + // s = e + BN_copy(s, e); - // s^2 = s % order (order must be prime) - BN_mod_sqrt(s, s, order, ctx); + // s = (ek (mod n))² + BN_mod_sqr(s, s, genOrder, numContext); - // s = s - b - BN_mod_sub(s, s, b, order, ctx); - - // if s is odd, s = s + order - if (BN_is_odd(s)) { - BN_add(s, s, order); - } + // c *= 4 (c <<= 2) + BN_lshift(c, c, 2); - // s >>= 1 (s = s / 2) - BN_rshift1(s, s); + // s += c + BN_add(s, s, c); - // Convert s from BigNum back to bytecode and reverse the endianness. - BN_bn2bin(s, (byte *)bSig); - endiannessConvert((byte *)bSig, BN_num_bytes(s)); + // Around half of numbers modulo a prime are not squares -> BN_sqrt_mod fails about half of the times, + // hence if BN_sqrt_mod returns NULL, we need to restart with a different seed. + // s = √((ek)² + 4c (mod n)) + noSquare = BN_mod_sqrt(s, s, genOrder, numContext) == nullptr; - // Pack product key. - packServer(bKey, osFamily, &hash, bSig, prefix); + // s = -ek + √((ek)² + 4c) (mod n) + BN_mod_sub(s, s, e, genOrder, numContext); - BN_free(c); - BN_free(s); - BN_free(x); - BN_free(y); - BN_free(b); - } while (bSig[1] >= 0x40000000); + // If s is odd, add order to it. + // The order is a prime, so it can't be even. + if (BN_is_odd(s)) - base24(pKey, bKey); + // s = -ek + √((ek)² + 4c) + n + BN_add(s, s, genOrder); - BN_CTX_free(ctx); - EC_POINT_free(r); + // s /= 2 (s >>= 1) + BN_rshift1(s, 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. + packServer(pRaw, pUpgrade, pChannelID, pHash, pSignature, pAuthInfo); + + EC_POINT_free(r); + } while (pSignature > BITMASK(62) || noSquare); + // ↑ ↑ ↑ + // The signature can't be longer than 62 bits, else it will + // overlap with the AuthInfo segment next to it. + + // Convert bytecode to Base24 CD-key. + base24((BYTE *)pRaw, pKey); + + BN_free(c); + BN_free(s); + BN_free(x); + BN_free(y); + BN_free(e); + + BN_CTX_free(numContext); } -bool keyServer(char *pKey) { +BOOL keyServer( + CHAR (&pKey)[PK_LENGTH + NULL_TERMINATOR], + DWORD nChannelID, + DWORD nAuthInfo, + BOOL bUpgrade +) { + // If the Channel ID isn't valid, quit. + if (nChannelID >= 1'000) + return false; - // 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(); + BIGNUM *privateKey = BN_new(); + BIGNUM *genOrder = 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. - BIGNUM *genOrder = BN_new(); + BN_hex2bn(&privateKey, privateKeySv); + BN_hex2bn(&genOrder, genOrderSv); - /* Computed data */ - BN_hex2bn(&genOrder, genOrderSv); - BN_hex2bn(&privateKey, privateKeySv); + EC_POINT *genPoint, *pubPoint; + EC_GROUP *eCurve = initializeEllipticCurve( + pSv, + aSv, + bSv, + genXSv, + genYSv, + pubXSv, + pubYSv, + genOrder, + privateKey, + &genPoint, + &pubPoint + ); - EC_POINT *genPoint, *pubPoint; - EC_GROUP *eCurve = initializeEllipticCurve( - pSv, - aSv, - bSv, - genXSv, - genYSv, - pubXSv, - pubYSv, - genOrder, - privateKey, - &genPoint, - &pubPoint - ); + // Generate a stub 10-bit AuthInfo segment if none is specified. + if (nAuthInfo == 0) { + RAND_bytes((byte *)&nAuthInfo, 4); + nAuthInfo &= 0x3FF; + } - ul32 osFamily = 1280, prefix = 0; + do { + generateServerKey(eCurve, genPoint, genOrder, privateKey, nChannelID, nAuthInfo, bUpgrade, pKey); + } while (!verifyServerKey(eCurve, genPoint, pubPoint, pKey)); - // Generate a 30-bit prefix. - RAND_bytes((byte *)&prefix, 4); - prefix &= 0x3FF; - - do { - generateServerKey(pKey, eCurve, genPoint, genOrder, privateKey, &osFamily, &prefix); - } while (!verifyServerKey(eCurve, genPoint, pubPoint, pKey)); - - return true; + return true; } \ No newline at end of file diff --git a/utilities.cpp b/utilities.cpp index ae74061..899ed97 100644 --- a/utilities.cpp +++ b/utilities.cpp @@ -5,7 +5,7 @@ #include "header.h" /* Convert data between endianness types. */ -void endiannessConvert(byte *data, int length) { +void endian(byte *data, int length) { for (int i = 0; i < length / 2; i++) { byte temp = data[i]; data[i] = data[length - i - 1]; @@ -14,7 +14,7 @@ void endiannessConvert(byte *data, int length) { } /* Generates a random 32-bit integer in range. */ -ul32 randomRange(ul32 dwLow, ul32 dwHigh) { +DWORD randomRange(DWORD dwLow, DWORD dwHigh) { return rand() % (dwHigh - dwLow) + dwLow; } @@ -112,4 +112,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/windows.cpp b/windows.cpp index aa912cb..20d1640 100644 --- a/windows.cpp +++ b/windows.cpp @@ -463,7 +463,7 @@ LRESULT CALLBACK WNDProc(HWND hWindow, UINT uMessage, WPARAM wParam, LPARAM lPar } case IDC_BUTTON4: { - ul32 msDigits = randomRange(0, 999), + DWORD msDigits = randomRange(0, 999), lsDigits = randomRange(0, 999'999); WCHAR pBSection[4]{}, pCSection[8]{}; diff --git a/xp.cpp b/xp.cpp index 69bfedd..2a2bd27 100644 --- a/xp.cpp +++ b/xp.cpp @@ -4,6 +4,7 @@ #include "header.h" +/* Windows XP */ const char pXP[] = "92ddcf14cb9e71f4489a2e9ba350ae29454d98cb93bdbcc07d62b502ea12238ee904a8b20d017197aae0c103b32713a9"; const long aXP = 1; const long bXP = 0; @@ -12,7 +13,7 @@ const long bXP = 0; const char genXXP[] = "46E3775ECE21B0898D39BEA57050D422A0AF989E497962BAEE2CB17E0A28D5360D5476B8DC966443E37A14F1AEF37742"; const char genYXP[] = "7C8E741D2C34F4478E325469CD491603D807222C9C4AC09DDB2B31B3CE3F7CC191B3580079932BC6BEF70BE27604F65E"; -// Inverse of the public key +// The public key const char pubXXP[] = "5D8DBE75198015EC41C45AAB6143542EB098F6A5CC9CE4178A1B8A1E7ABBB5BC64DF64FAF6177DC1B0988AB00BA94BF8"; const char pubYXP[] = "23A2909A0B4803C89F910C7191758B48746CEA4D5FF07667444ACDB9512080DBCA55E6EBF30433672B894F44ACE92BFA"; @@ -22,251 +23,288 @@ const char genOrderXP[] = "DB6B4C58EFBAFD"; // The private key was computed in 10 hours using a Pentium III 450 const char privateKeyXP[] = "565B0DFF8496C8"; -/* Unpacks the Product Key. */ -void unpackXP(ul32 *serial, ul32 *hash, ul32 *sig, ul32 *raw) { +/* Windows 98 +const char pXP[] = "ec224ff2613a9fe1411b51e89634643f79a272402ee146b012a3f71098c7e75df4bf8b3713c4f0ce56691ce56b9b5029"; +const long aXP = 1; +const long bXP = 0; + +// Base point G (Generator) +const char genXXP[] = "b5e1957b19951b5523204a62fd83ab22056f59a13bf8aaaf16ac10b7540f8ea92ba28dbfa68996fa12510c024f912340"; +const char genYXP[] = "a84fbc02f311b1fd4521773e01821bd047f067c496ad54ce1504315cb88667d69130caa25efb2cb1e479ed50efb40d6b"; + +// The public key +const char pubXXP[] = "26ea9efe57ab6da485225a13ed66533c143f81b7b9528e38c8568bb726a8f0f5607da0e8d85aebf2e1425758b409e811"; +const char pubYXP[] = "1a7c4cebe5f3919e96876a447a813efcd920979e9610d2b2146a04fab1041b31ae65e24efa3e0b0d61622483655716c2"; + +// The order of G was computed in 18 hours using a Pentium III 450 +const char genOrderXP[] = "E778E33AEE6B3D"; + +// The private key was computed in 10 hours using a Pentium III 450 +const char privateKeyXP[] = "B9E99B9BB9812E"; // "677A485D4BE4A0";*/ + + +/* Unpacks a Windows XP-like Product Key. */ +VOID unpackXP( + QWORD (&pRaw)[2], + BOOL &pUpgrade, + DWORD &pSerial, + DWORD &pHash, + QWORD &pSignature +) { // We're assuming that the quantity of information within the product key is at most 114 bits. // log2(24^25) = 114. - // Serial = Bits [0..30] -> 31 bits - if (serial) - serial[0] = raw[0] & 0x7fffffff; - - // Hash (e) = Bits [31..58] -> 28 bits - if (hash) - hash[0] = ((raw[0] >> 31) | (raw[1] << 1)) & 0xfffffff; - - // Signature (s) = Bits [59..113] -> 55 bits - if (sig) { - sig[0] = (raw[1] >> 27) | (raw[2] << 5); - sig[1] = (raw[2] >> 27) | (raw[3] << 5); - } + // Upgrade = Bit 0 + pUpgrade = FIRSTNBITS(pRaw[0], 1); + + // Serial = Bits [1..30] -> 30 bits + pSerial = NEXTSNBITS(pRaw[0], 30, 1); + + // Hash = Bits [31..58] -> 28 bits + pHash = NEXTSNBITS(pRaw[0], 28, 31); + + // Signature = Bits [59..113] -> 56 bits + pSignature = FIRSTNBITS(pRaw[1], 51) << 5 | NEXTSNBITS(pRaw[0], 5, 59); } -/* Repacks the Product Key. */ -void packXP(ul32 *raw, ul32 *serial, ul32 *hash, ul32 *sig) { - raw[0] = serial[0] | ((hash[0] & 1) << 31); - raw[1] = (hash[0] >> 1) | ((sig[0] & 0x1f) << 27); - raw[2] = (sig[0] >> 5) | (sig[1] << 27); - raw[3] = sig[1] >> 5; +/* Packs a Windows XP-like Product Key. */ +VOID packXP( + QWORD (&pRaw)[2], + BOOL pUpgrade, + DWORD pSerial, + DWORD pHash, + QWORD pSignature +) { + // The quantity of information the key provides is 114 bits. + // We're storing it in 2 64-bit quad-words with 14 trailing bits. + // 64 * 2 = 128 + + // Signature [114..59] <- Hash [58..31] <- Serial [30..1] <- Upgrade [0] + pRaw[0] = FIRSTNBITS(pSignature, 5) << 59 | FIRSTNBITS(pHash, 28) << 31 | (QWORD)pSerial << 1 | pUpgrade; + pRaw[1] = NEXTSNBITS(pSignature, 51, 5); } -/* Verify Product Key */ -bool verifyXPKey(EC_GROUP *eCurve, EC_POINT *generator, EC_POINT *publicKey, char *cdKey) { - BN_CTX *context = BN_CTX_new(); - +/* Verifies a Windows XP-like Product Key. */ +BOOL verifyXPKey( + EC_GROUP *eCurve, + EC_POINT *basePoint, + EC_POINT *publicKey, + CHAR (&pKey)[PK_LENGTH + NULL_TERMINATOR] +) { + BN_CTX *numContext = BN_CTX_new(); + + QWORD pRaw[2]{}, + pSignature; + + DWORD pData, + pSerial, + pHash; + + BOOL pUpgrade; + // Convert Base24 CD-key to bytecode. - ul32 bKey[4]{}; - ul32 pID, checkHash, sig[2]; + unbase24((BYTE *)pRaw, pKey); - unbase24(bKey, cdKey); + // Extract RPK, hash and signature from bytecode. + unpackXP(pRaw, pUpgrade, pSerial, pHash, pSignature); - // Extract data, hash and signature from the bytecode. - unpackXP(&pID, &checkHash, sig, bKey); + pData = pSerial << 1 | pUpgrade; - // e = Hash - // s = Signature - BIGNUM *e, *s; + /* + * + * Scalars: + * e = Hash + * s = Schnorr Signature + * + * Points: + * G(x, y) = Generator (Base Point) + * K(x, y) = Public Key + * + * Equation: + * P = sG + eK + * + */ - // Put hash word into BigNum e. - e = BN_new(); - BN_set_word(e, checkHash); + BIGNUM *e = BN_lebin2bn((BYTE *)&pHash, sizeof(pHash), nullptr), + *s = BN_lebin2bn((BYTE *)&pSignature, sizeof(pSignature), nullptr), + *x = BN_new(), + *y = BN_new(); - // Reverse signature and create a new BigNum s. - endiannessConvert((byte *) sig, sizeof(sig)); - s = BN_bin2bn((byte *)sig, sizeof(sig), nullptr); + // Create 2 points on the elliptic curve. + EC_POINT *t = EC_POINT_new(eCurve); + EC_POINT *p = EC_POINT_new(eCurve); - // Create x and y. - BIGNUM *x = BN_new(); - BIGNUM *y = BN_new(); + // t = sG + EC_POINT_mul(eCurve, t, nullptr, basePoint, s, numContext); - // Create 2 new points on the existing elliptic curve. - EC_POINT *u = EC_POINT_new(eCurve); - EC_POINT *v = EC_POINT_new(eCurve); + // P = eK + EC_POINT_mul(eCurve, p, nullptr, publicKey, e, numContext); - // EC_POINT_mul calculates r = generator * n + q * m. - // v = s * generator + e * (-publicKey) + // P += t + EC_POINT_add(eCurve, p, t, p, numContext); - // u = generator * s - EC_POINT_mul(eCurve, u, nullptr, generator, s, context); + // x = P.x; y = P.y; + EC_POINT_get_affine_coordinates(eCurve, p, x, y, numContext); - // v = publicKey * e - EC_POINT_mul(eCurve, v, nullptr, publicKey, e, context); + BYTE msgDigest[SHA_DIGEST_LENGTH]{}, + msgBuffer[SHA_MSG_LENGTH_XP]{}, + xBin[FIELD_BYTES]{}, + yBin[FIELD_BYTES]{}; - // v += u - EC_POINT_add(eCurve, v, u, v, context); + // Convert resulting point coordinates to bytes. + BN_bn2lebin(x, xBin, FIELD_BYTES); + BN_bn2lebin(y, 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); + // Assemble the SHA message. + memcpy((void *)&msgBuffer[0], (void *)&pData, 4); + memcpy((void *)&msgBuffer[4], (void *)xBin, FIELD_BYTES); + memcpy((void *)&msgBuffer[4 + FIELD_BYTES], (void *)yBin, FIELD_BYTES); + // compHash = SHA1(pSerial || P.x || P.y) + SHA1(msgBuffer, SHA_MSG_LENGTH_XP, msgDigest); - byte buf[FIELD_BYTES], md[SHA_DIGEST_LENGTH], t[4]; - ul32 newHash; + // Translate the byte digest into a 32-bit integer - this is our computed hash. + // Truncate the hash to 28 bits. + DWORD compHash = BYDWORD(msgDigest) >> 4 & BITMASK(28); - 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); - endiannessConvert(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); - endiannessConvert(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; - 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(char *pKey, EC_GROUP *eCurve, EC_POINT *generator, BIGNUM *order, BIGNUM *privateKey, ul32 *pRaw) { - EC_POINT *r = EC_POINT_new(eCurve); - BN_CTX *ctx = BN_CTX_new(); +/* Generates a Windows XP-like Product Key. */ +VOID generateXPKey( + EC_GROUP *eCurve, + EC_POINT *basePoint, + BIGNUM *genOrder, + BIGNUM *privateKey, + DWORD pChannelID, + DWORD pSequence, + BOOL pUpgrade, + CHAR (&pKey)[PK_LENGTH + NULL_TERMINATOR] +) { + BN_CTX *numContext = BN_CTX_new(); - BIGNUM *c = BN_new(); - BIGNUM *s = BN_new(); - BIGNUM *x = BN_new(); - BIGNUM *y = BN_new(); + BIGNUM *c = BN_new(), + *s = BN_new(), + *x = BN_new(), + *y = BN_new(); - ul32 bKey[4]{}; + QWORD pRaw[2]{}, + pSignature = 0; + + // Data segment of the RPK. + DWORD pData = (pChannelID * 1'000'000 + pSequence) << 1 | pUpgrade; do { - ul32 hash = 0, sig[2]{}; - - memset(bKey, 0, 4); + EC_POINT *r = EC_POINT_new(eCurve); // 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); - - SHA_CTX hContext; - byte md[SHA_DIGEST_LENGTH]{}, buf[FIELD_BYTES]{}, t[4]{}; + // Acquire its coordinates. + // x = R.x; y = R.y; + EC_POINT_get_affine_coordinates(eCurve, r, x, y, numContext); - // h = (First-32(SHA1(pRaw, r.x, r.y)) >> 4 - SHA1_Init(&hContext); + BYTE msgDigest[SHA_DIGEST_LENGTH]{}, + msgBuffer[SHA_MSG_LENGTH_XP]{}, + xBin[FIELD_BYTES]{}, + 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; + // Convert coordinates to bytes. + BN_bn2lebin(x, xBin, FIELD_BYTES); + BN_bn2lebin(y, yBin, FIELD_BYTES); - // Hash chunk of data. - SHA1_Update(&hContext, t, sizeof(t)); + // Assemble the SHA message. + memcpy((void *)&msgBuffer[0], (void *)&pData, 4); + memcpy((void *)&msgBuffer[4], (void *)xBin, FIELD_BYTES); + memcpy((void *)&msgBuffer[4 + FIELD_BYTES], (void *)yBin, FIELD_BYTES); - // Empty buffer, place r.x in little-endiannessConvert. - memset(buf, 0, FIELD_BYTES); - BN_bn2bin(x, buf); - endiannessConvert(buf, FIELD_BYTES); + // pHash = SHA1(pSerial || R.x || R.y) + SHA1(msgBuffer, SHA_MSG_LENGTH_XP, msgDigest); - // Hash chunk of data. - SHA1_Update(&hContext, buf, FIELD_BYTES); + // Translate the byte digest into a 32-bit integer - this is our computed pHash. + // Truncate the pHash to 28 bits. + DWORD pHash = BYDWORD(msgDigest) >> 4 & BITMASK(28); - // Empty buffer, place r.y in little-endiannessConvert. - memset(buf, 0, FIELD_BYTES); - BN_bn2bin(y, buf); - endiannessConvert(buf, FIELD_BYTES); + /* + * + * Scalars: + * c = Random multiplier + * e = Hash + * s = Signature + * n = Order of G + * k = Private Key + * + * Points: + * G(x, y) = Generator (Base Point) + * R(x, y) = Random derivative of the generator + * K(x, y) = Public Key + * + * 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 + */ - // 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); - endiannessConvert((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); - } while (bKey[3] >= 0x40000); - // ↑ ↑ ↑ - // bKey[3] can't be longer than 18 bits, else the signature part will make - // the CD-key longer than 25 characters. + packXP(pRaw, pUpgrade, pChannelID * 1'000'000 + pSequence, pHash, pSignature); + + EC_POINT_free(r); + } while (pSignature > BITMASK(55)); + // ↑ ↑ ↑ + // The signature can't be longer than 55 bits, else it will + // make the CD-key longer than 25 characters. + + // Convert bytecode to Base24 CD-key. + base24((BYTE *)pRaw, pKey); - // Convert the key to Base24. - base24(pKey, bKey); - BN_free(c); BN_free(s); BN_free(x); BN_free(y); - BN_CTX_free(ctx); - EC_POINT_free(r); + BN_CTX_free(numContext); } -bool keyXP(char *pKey, ul32 nRaw) { - assert(nRaw <= 1'000'000'000); +BOOL keyXP( + CHAR (&pKey)[PK_LENGTH + NULL_TERMINATOR], + DWORD nChannelID, + DWORD nSequence, + BOOL bUpgrade +) { + // If the Channel ID or the random sequence aren't valid, quit. + if (nChannelID >= 1'000 || nSequence >= 1'000'000) + return false; - // 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(); - - // 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(); - /* Computed data */ - BN_hex2bn(&genOrder, genOrderXP); BN_hex2bn(&privateKey, privateKeyXP); + BN_hex2bn(&genOrder, genOrderXP); EC_POINT *genPoint, *pubPoint; EC_GROUP *eCurve = initializeEllipticCurve( @@ -283,12 +321,8 @@ bool keyXP(char *pKey, ul32 nRaw) { &pubPoint ); - // Shift left once. - nRaw <<= 1; - - // Generate the key until it's valid. (In XP it's valid 100% of the times) do { - generateXPKey(pKey, eCurve, genPoint, genOrder, privateKey, &nRaw); + generateXPKey(eCurve, genPoint, genOrder, privateKey, nChannelID, nSequence, bUpgrade, pKey); } while (!verifyXPKey(eCurve, genPoint, pubPoint, pKey)); return true;