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;