diff --git a/src/cli.cpp b/src/cli.cpp index 16a2719..686da67 100644 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -29,7 +29,7 @@ json CLI::keys; BYTE CLI::Init(int argcIn, char **argvIn) { // set default options - options = {argcIn, argvIn, "2E", "", "", "", "", "", "WINXPPVLK", 0, + options = {argcIn, argvIn, "", "", "", "", "", "", "WINXPPVLK", 0, 0, 1, false, false, false, false, false, false, false, STATE_BINK1998_GENERATE}; SetHelpText(); @@ -140,6 +140,61 @@ BOOL CLI::processOptions() return false; } + if (!options.productCode.empty()) + { + const char *productCode = &options.productCode[0]; + auto product = keys["Products"][productCode]; + + if (options.verbose) + { + fmt::print("Selecting product: {}\n", productCode); + } + + if (options.oem) + { + options.binkid = product["BINK"][1].get(); + } + else + { + options.binkid = product["BINK"][0].get(); + } + + if (options.verbose) + { + fmt::print("Selected BINK: {}\n", options.binkid); + } + + std::vector filtered; + + if (product.contains("DPC") && options.channelID == 0) + { + for (auto const &i : product["DPC"][options.binkid].items()) + { + auto el = i.value(); + if (!el["IsEvaluation"].get()) + { + filtered.push_back(el); + } + } + + // roll a die to choose which DPC entry to pick + auto rand = UMSKT::getRandom(); + auto dpc = filtered[rand % filtered.size()]; + auto min = dpc["Min"].get(), max = dpc["Max"].get(); + options.channelID = min + (rand % (max - min)); + + if (options.verbose) + { + fmt::print("Selected channel ID: {} (DPC entry {})\n", options.channelID, rand % filtered.size()); + } + } + + if (options.channelID == 0) + { + options.channelID = UMSKT::getRandom() % 999; + } + } + DWORD intBinkID; sscanf(&options.binkid[0], "%x", &intBinkID); @@ -195,7 +250,7 @@ void CLI::printID(DWORD *pid) * * @param pk */ -void CLI::printKey(std::string pk) +void CLI::printKey(std::string &pk) { assert(pk.length() >= PK_LENGTH); @@ -249,57 +304,6 @@ BOOL CLI::stripKey(const std::string &in_key, std::string &out_key) */ BOOL CLI::InitPIDGEN3(PIDGEN3 *pidgen3) { - if (!options.productCode.empty()) - { - const char *productCode = &options.productCode[0]; - auto product = keys["Products"][productCode]; - - if (options.verbose) - { - fmt::print("Selecting product: {}\n", productCode); - } - - if (options.oem) - { - options.binkid = product["BINK"][1].get(); - } - else - { - options.binkid = product["BINK"][0].get(); - } - - if (options.verbose) - { - fmt::print("Selected BINK: {}\n", options.binkid); - } - - std::vector filtered; - - if (product.contains("DPC") && options.channelID == 0) - { - for (auto const &i : product["DPC"][options.binkid].items()) - { - auto el = i.value(); - if (!el["IsEvaluation"].get()) - { - filtered.push_back(el); - } - } - - // roll a die to choose which DPC entry to pick - auto rand = UMSKT::getRandom(); - auto dpc = filtered[rand % filtered.size()]; - auto min = dpc["Min"].get(), max = dpc["Max"].get(); - options.channelID = min + (rand % (max - min)); - if (options.verbose) - { - fmt::print("Selected channel ID: {} (DPC entry {})\n", options.channelID, rand % filtered.size()); - } - } - - return false; - } - const char *BINKID = &options.binkid[0]; auto bink = keys["BINK"][BINKID]; @@ -320,12 +324,10 @@ BOOL CLI::InitPIDGEN3(PIDGEN3 *pidgen3) fmt::print("\n"); } - pidgen3->LoadEllipticCurve(bink["p"].get(), bink["a"].get(), bink["b"].get(), - bink["g"]["x"].get(), bink["g"]["y"].get(), - bink["pub"]["x"].get(), bink["pub"]["y"].get(), - bink["n"].get(), bink["priv"].get()); + pidgen3->LoadEllipticCurve(bink["p"], bink["a"], bink["b"], bink["g"]["x"], bink["g"]["y"], bink["pub"]["x"], + bink["pub"]["y"], bink["n"], bink["priv"]); - pidgen3->setChannelID(options.channelID); + options.info.setChannelID(options.channelID); if (options.verbose) { fmt::print("> Channel ID: {:03d}\n", options.channelID); @@ -333,7 +335,7 @@ BOOL CLI::InitPIDGEN3(PIDGEN3 *pidgen3) if (options.serialSet) { - pidgen3->setSerial(options.serial); + options.info.setSerial(options.serial); if (options.verbose) { fmt::print("> Serial {:#09d}\n", options.serial); @@ -368,21 +370,22 @@ BOOL CLI::BINK1998Generate() // raw PID/serial value DWORD nRaw = options.channelID * 1'000'000; + DWORD serialRnd; - // using user-provided serial if (options.serialSet) { - // just in case, make sure it's less than 999999 - int serialRnd = (options.serial % 999999); - nRaw += serialRnd; + // using user-provided serial + serialRnd = options.serial; } else { // generate a random number to use as a serial - auto oRaw = UMSKT::getRandom(); - nRaw += (oRaw % 999999); // ensure our serial is less than 999999 + serialRnd = UMSKT::getRandom(); } + // make sure it's less than 999999 + nRaw += (serialRnd % 999999); + if (options.verbose) { // print the resulting Product ID @@ -392,12 +395,13 @@ BOOL CLI::BINK1998Generate() for (int i = 0; i < total; i++) { - bink1998.Generate(pKey); + options.info.setSerial(nRaw); + bink1998.Generate(options.info, pKey); bool isValid = bink1998.Verify(pKey); if (isValid) { - CLI::printKey(pKey); + printKey(pKey); if (i < total - 1 || options.verbose) { fmt::print("\n"); @@ -408,7 +412,7 @@ BOOL CLI::BINK1998Generate() { if (options.verbose) { - CLI::printKey(pKey); + printKey(pKey); fmt::print(" [Invalid]"); if (i < total - 1) { @@ -448,7 +452,7 @@ BOOL CLI::BINK2002Generate() fmt::print("> AuthInfo: {}\n", pAuthInfo); } - bink2002.Generate(pKey); + bink2002.Generate(options.info, pKey); bool isValid = bink2002.Verify(pKey); if (isValid) diff --git a/src/cli.h b/src/cli.h index 22a8efe..6dae26c 100644 --- a/src/cli.h +++ b/src/cli.h @@ -82,6 +82,8 @@ struct Options BOOL error; BOOL list; + PIDGEN3::KeyInfo info; + APPLICATION_STATE state; }; @@ -105,7 +107,8 @@ class CLI static BYTE Init(int argv, char *argc[]); static void SetHelpText(); - static CLIHandlerFunc loadJSON; + static BOOL loadJSON(const fs::path &filename); + static CLIHandlerFunc DisplayHelp; static CLIHandlerFunc DisplayErrorMessage; static CLIHandlerFunc SetVerboseOption; @@ -126,7 +129,7 @@ class CLI static BOOL parseCommandLine(); static BOOL processOptions(); static void printID(DWORD *pid); - static void printKey(std::string pk); + static void printKey(std::string &pk); static BOOL stripKey(const std::string &in_key, std::string &out_key); BOOL InitPIDGEN3(PIDGEN3 *pidgen3); diff --git a/src/libumskt/debugoutput.cpp b/src/libumskt/debugoutput.cpp index 9b5d8c6..1a8824c 100644 --- a/src/libumskt/debugoutput.cpp +++ b/src/libumskt/debugoutput.cpp @@ -34,8 +34,8 @@ std::FILE *UMSKT::debug = getFileStreamToNul(); std::FILE *UMSKT::debug = std::fopen("/dev/null", "w"); #endif -BOOL UMSKT::VERBOSE; -BOOL UMSKT::DEBUG; +BOOL UMSKT::VERBOSE = false; +BOOL UMSKT::DEBUG = false; void UMSKT::setDebugOutput(std::FILE *input) { diff --git a/src/libumskt/libumskt.cpp b/src/libumskt/libumskt.cpp index 6bb14f5..ac30927 100644 --- a/src/libumskt/libumskt.cpp +++ b/src/libumskt/libumskt.cpp @@ -108,7 +108,7 @@ FNEXPORT BOOL PIDGEN3_Generate(void *&ptrIn, char *&pKeyOut, int pKeySizeIn) auto *p3((PIDGEN3 *)ptrIn); std::string str; - BOOL retval = p3->Generate(str); + // BOOL retval = p3->Generate(str); if (pKeySizeIn > str.length() + 1) { @@ -118,7 +118,7 @@ FNEXPORT BOOL PIDGEN3_Generate(void *&ptrIn, char *&pKeyOut, int pKeySizeIn) memcpy(pKeyOut, &str[0], str.length()); pKeyOut[str.length()] = 0; - return retval; + return true; } FNEXPORT BOOL PIDGEN3_Verify(void *&ptrIn, char *pKeyIn) diff --git a/src/libumskt/libumskt.h b/src/libumskt/libumskt.h index d73127e..e1426ba 100644 --- a/src/libumskt/libumskt.h +++ b/src/libumskt/libumskt.h @@ -66,6 +66,12 @@ class UMSKT static BOOL VERBOSE; static BOOL DEBUG; + static void DESTRUCT() + { + std::fclose(debug); + debug = nullptr; + } + static void setDebugOutput(std::FILE *input); template static T getRandom() { diff --git a/src/libumskt/pidgen3/BINK1998.cpp b/src/libumskt/pidgen3/BINK1998.cpp index 6c4e9f7..fb31c0b 100644 --- a/src/libumskt/pidgen3/BINK1998.cpp +++ b/src/libumskt/pidgen3/BINK1998.cpp @@ -34,22 +34,22 @@ * * @param pRaw [out] *QWORD[2] raw product key output **/ -BOOL BINK1998::Unpack(QWORD (&pRaw)[2]) +BOOL BINK1998::Unpack(KeyInfo &info, QWORD *pRaw) { // We're assuming that the quantity of information within the product key is at most 114 bits. // log2(24^25) = 114. // Upgrade = Bit 0 - isUpgrade = FIRSTNBITS(pRaw[0], 1); + info.isUpgrade = FIRSTNBITS(pRaw[0], 1); // Serial = Bits [1..30] -> 30 bits - Serial = NEXTSNBITS(pRaw[0], 30, 1); + info.Serial = NEXTSNBITS(pRaw[0], 30, 1); // Hash = Bits [31..58] -> 28 bits - Hash = NEXTSNBITS(pRaw[0], 28, 31); + info.Hash = NEXTSNBITS(pRaw[0], 28, 31); // Signature = Bits [59..113] -> 56 bits - Signature = FIRSTNBITS(pRaw[1], 51) << 5 | NEXTSNBITS(pRaw[0], 5, 59); + info.Signature = FIRSTNBITS(pRaw[1], 51) << 5 | NEXTSNBITS(pRaw[0], 5, 59); return true; } @@ -59,15 +59,15 @@ BOOL BINK1998::Unpack(QWORD (&pRaw)[2]) * * @param pRaw [in] *QWORD[2] raw product key input **/ -BOOL BINK1998::Pack(QWORD (&pRaw)[2]) +BOOL BINK1998::Pack(const KeyInfo &info, QWORD *pRaw) { // 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(Signature, 5) << 59 | FIRSTNBITS(Hash, 28) << 31 | Serial << 1 | isUpgrade; - pRaw[1] = NEXTSNBITS(Signature, 51, 5); + pRaw[0] = FIRSTNBITS(info.Signature, 5) << 59 | FIRSTNBITS(info.Hash, 28) << 31 | info.Serial << 1 | info.isUpgrade; + pRaw[1] = NEXTSNBITS(info.Signature, 51, 5); return true; } @@ -87,23 +87,24 @@ BOOL BINK1998::Verify(std::string &pKey) } BN_CTX *numContext = BN_CTX_new(); + KeyInfo info; - QWORD pRaw[2]{}; + QWORD pRaw[2]; // Convert Base24 CD-key to bytecode. unbase24((BYTE *)pRaw, pKey); // Extract RPK, hash and signature from bytecode. - Unpack(pRaw); + Unpack(info, pRaw); fmt::print(UMSKT::debug, "Validation results:\n"); - fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", isUpgrade); - fmt::print(UMSKT::debug, " Serial: 0x{:08x}\n", Serial); - fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", Hash); - fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", Signature); + fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", info.isUpgrade); + fmt::print(UMSKT::debug, " Serial: 0x{:08x}\n", info.Serial); + fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", info.Hash); + fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", info.Signature); fmt::print(UMSKT::debug, "\n"); - DWORD pData = Serial << 1 | isUpgrade; + DWORD pData = info.Serial << 1 | info.isUpgrade; /* * @@ -120,12 +121,13 @@ BOOL BINK1998::Verify(std::string &pKey) * */ - BIGNUM *e = BN_lebin2bn((BYTE *)&Hash, sizeof(Hash), nullptr), - *s = BN_lebin2bn((BYTE *)&Signature, sizeof(Signature), nullptr), *x = BN_new(), *y = BN_new(); + BIGNUM *e = BN_lebin2bn((BYTE *)&info.Hash, sizeof(info.Hash), nullptr), + *s = BN_lebin2bn((BYTE *)&info.Hash, sizeof(info.Hash), nullptr); + + BIGNUM *x = BN_CTX_get(numContext), *y = BN_CTX_get(numContext); // Create 2 points on the elliptic curve. - EC_POINT *t = EC_POINT_new(eCurve); - EC_POINT *p = EC_POINT_new(eCurve); + EC_POINT *t = EC_POINT_new(eCurve), *p = EC_POINT_new(eCurve); // t = sG EC_POINT_mul(eCurve, t, nullptr, basePoint, s, numContext); @@ -139,16 +141,16 @@ BOOL BINK1998::Verify(std::string &pKey) // x = P.x; y = P.y; EC_POINT_get_affine_coordinates(eCurve, p, x, y, numContext); - BYTE msgDigest[SHA_DIGEST_LENGTH]{}, msgBuffer[SHA_MSG_LENGTH_XP]{}, xBin[FIELD_BYTES]{}, yBin[FIELD_BYTES]{}; + BYTE msgDigest[SHA_DIGEST_LENGTH], msgBuffer[SHA_MSG_LENGTH_XP], xBin[FIELD_BYTES], yBin[FIELD_BYTES]; // Convert resulting point coordinates to bytes. BN_bn2lebin(x, xBin, FIELD_BYTES); BN_bn2lebin(y, yBin, FIELD_BYTES); // 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); + memcpy(&msgBuffer[0], &pData, 4); + memcpy(&msgBuffer[4], xBin, FIELD_BYTES); + memcpy(&msgBuffer[4 + FIELD_BYTES], yBin, FIELD_BYTES); // compHash = SHA1(pSerial || P.x || P.y) SHA1(msgBuffer, SHA_MSG_LENGTH_XP, msgDigest); @@ -159,8 +161,6 @@ BOOL BINK1998::Verify(std::string &pKey) BN_free(e); BN_free(s); - BN_free(x); - BN_free(y); BN_CTX_free(numContext); @@ -168,7 +168,7 @@ BOOL BINK1998::Verify(std::string &pKey) EC_POINT_free(p); // If the computed hash checks out, the key is valid. - return compHash == Hash; + return compHash == info.Hash; } /** @@ -178,16 +178,17 @@ BOOL BINK1998::Verify(std::string &pKey) * * @return true on success, false on fail */ -BOOL BINK1998::Generate(std::string &pKey) +BOOL BINK1998::Generate(KeyInfo &info, std::string &pKey) { BN_CTX *numContext = BN_CTX_new(); - BIGNUM *c = BN_new(), *s = BN_new(), *x = BN_new(), *y = BN_new(); + BIGNUM *c = BN_CTX_get(numContext), *s = BN_CTX_get(numContext), *x = BN_CTX_get(numContext), + *y = BN_CTX_get(numContext); QWORD pRaw[2]{}; // Data segment of the RPK. - DWORD pData = Serial << 1 | isUpgrade; + DWORD pData = info.Serial << 1 | info.isUpgrade; do { @@ -204,23 +205,24 @@ BOOL BINK1998::Generate(std::string &pKey) // x = R.x; y = R.y; EC_POINT_get_affine_coordinates(eCurve, r, x, y, numContext); - BYTE msgDigest[SHA_DIGEST_LENGTH]{}, msgBuffer[SHA_MSG_LENGTH_XP]{}, xBin[FIELD_BYTES]{}, yBin[FIELD_BYTES]{}; + BYTE msgDigest[SHA_DIGEST_LENGTH]{}, msgBuffer[SHA_MSG_LENGTH_XP]{}; + BYTE xBin[FIELD_BYTES]{}, yBin[FIELD_BYTES]{}; // Convert coordinates to bytes. BN_bn2lebin(x, xBin, FIELD_BYTES); BN_bn2lebin(y, yBin, FIELD_BYTES); // 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); + memcpy(&msgBuffer[0], &pData, 4); + memcpy(&msgBuffer[4], xBin, FIELD_BYTES); + memcpy(&msgBuffer[4 + FIELD_BYTES], yBin, FIELD_BYTES); // pHash = SHA1(pSerial || R.x || R.y) SHA1(msgBuffer, SHA_MSG_LENGTH_XP, msgDigest); // 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); + info.Hash = BYDWORD(msgDigest) >> 4 & BITMASK(28); /* * @@ -243,26 +245,26 @@ BOOL BINK1998::Generate(std::string &pKey) // s = ek; BN_copy(s, privateKey); - BN_mul_word(s, pHash); + BN_mul_word(s, info.Hash); // s += c (mod n) BN_mod_add(s, s, c, genOrder, numContext); // Translate resulting scalar into a 64-bit integer (the byte order is little-endian). - BN_bn2lebinpad(s, (BYTE *)&Signature, BN_num_bytes(s)); + BN_bn2lebinpad(s, (BYTE *)&info.Signature, BN_num_bytes(s)); // Pack product key. - Pack(pRaw); + Pack(info, pRaw); fmt::print(UMSKT::debug, "Generation results:\n"); - fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", isUpgrade); - fmt::print(UMSKT::debug, " Serial: 0x{:08x}\n", Serial); - fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", Hash); - fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", Signature); + fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", info.isUpgrade); + fmt::print(UMSKT::debug, " Serial: 0x{:08x}\n", info.Serial); + fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", info.Hash); + fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", info.Signature); fmt::print(UMSKT::debug, "\n"); EC_POINT_free(r); - } while (Signature > BITMASK(55)); + } while (info.Signature > BITMASK(55)); // ↑ ↑ ↑ // The signature can't be longer than 55 bits, else it will // make the CD-key longer than 25 characters. @@ -270,11 +272,6 @@ BOOL BINK1998::Generate(std::string &pKey) // Convert bytecode to Base24 CD-key. base24(pKey, (BYTE *)pRaw); - BN_free(c); - BN_free(s); - BN_free(x); - BN_free(y); - BN_CTX_free(numContext); return true; diff --git a/src/libumskt/pidgen3/BINK1998.h b/src/libumskt/pidgen3/BINK1998.h index 65e7e68..9098751 100644 --- a/src/libumskt/pidgen3/BINK1998.h +++ b/src/libumskt/pidgen3/BINK1998.h @@ -27,12 +27,11 @@ class BINK1998 : public PIDGEN3 { - public: - BOOL Unpack(QWORD (&pRaw)[2]) override; - BOOL Pack(QWORD (&pRaw)[2]) override; + BOOL Unpack(KeyInfo &info, QWORD *pRaw) override; + BOOL Pack(const KeyInfo &info, QWORD *pRaw) override; BOOL Verify(std::string &pKey) override; - BOOL Generate(std::string &pKey) override; + BOOL Generate(KeyInfo &info, std::string &pKey) override; }; #endif // UMSKT_BINK1998_H diff --git a/src/libumskt/pidgen3/BINK2002.cpp b/src/libumskt/pidgen3/BINK2002.cpp index 0942559..a164d15 100644 --- a/src/libumskt/pidgen3/BINK2002.cpp +++ b/src/libumskt/pidgen3/BINK2002.cpp @@ -34,27 +34,27 @@ * * @param pRaw *QWORD[2] raw product key input **/ -BOOL BINK2002::Unpack(QWORD (&pRaw)[2]) +BOOL BINK2002::Unpack(KeyInfo &info, QWORD *pRaw) { // We're assuming that the quantity of information within the product key is at most 114 bits. // log2(24^25) = 114. // Upgrade = Bit 0 - isUpgrade = FIRSTNBITS(pRaw[0], 1); + info.isUpgrade = FIRSTNBITS(pRaw[0], 1); // Channel ID = Bits [1..10] -> 10 bits - ChannelID = NEXTSNBITS(pRaw[0], 10, 1); + info.ChannelID = NEXTSNBITS(pRaw[0], 10, 1); // Hash = Bits [11..41] -> 31 bits - Hash = NEXTSNBITS(pRaw[0], 31, 11); + info.Hash = 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) - Signature = NEXTSNBITS(pRaw[1], 30, 10) << 32 | FIRSTNBITS(pRaw[1], 10) << 22 | NEXTSNBITS(pRaw[0], 22, 42); + info.Signature = NEXTSNBITS(pRaw[1], 30, 10) << 32 | FIRSTNBITS(pRaw[1], 10) << 22 | NEXTSNBITS(pRaw[0], 22, 42); // AuthInfo = Bits [104..113] -> 10 bits - AuthInfo = NEXTSNBITS(pRaw[1], 10, 40); + info.AuthInfo = NEXTSNBITS(pRaw[1], 10, 40); return true; } @@ -64,11 +64,11 @@ BOOL BINK2002::Unpack(QWORD (&pRaw)[2]) * * @param pRaw *QWORD[2] raw product key output **/ -BOOL BINK2002::Pack(QWORD (&pRaw)[2]) +BOOL BINK2002::Pack(const KeyInfo &info, QWORD *pRaw) { // AuthInfo [113..104] <- Signature [103..42] <- Hash [41..11] <- Channel ID [10..1] <- Upgrade [0] - pRaw[0] = FIRSTNBITS(Signature, 22) << 42 | (QWORD)Hash << 11 | ChannelID << 1 | isUpgrade; - pRaw[1] = FIRSTNBITS(AuthInfo, 10) << 40 | NEXTSNBITS(Signature, 40, 22); + pRaw[0] = FIRSTNBITS(info.Signature, 22) << 42 | (QWORD)info.Hash << 11 | info.ChannelID << 1 | info.isUpgrade; + pRaw[1] = FIRSTNBITS(info.AuthInfo, 10) << 40 | NEXTSNBITS(info.Signature, 40, 22); return true; } @@ -81,23 +81,24 @@ BOOL BINK2002::Pack(QWORD (&pRaw)[2]) BOOL BINK2002::Verify(std::string &pKey) { BN_CTX *context = BN_CTX_new(); + KeyInfo info; - QWORD bKey[2]{}; + QWORD bKey[2]; // Convert Base24 CD-key to bytecode. unbase24((BYTE *)bKey, pKey.c_str()); // Extract product key segments from bytecode. - Unpack(bKey); + Unpack(info, bKey); - DWORD pData = ChannelID << 1 | isUpgrade; + DWORD pData = info.ChannelID << 1 | info.isUpgrade; fmt::print(UMSKT::debug, "Validation results:\n"); - fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", isUpgrade); - fmt::print(UMSKT::debug, "Channel ID: 0x{:08x}\n", ChannelID); - fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", Hash); - fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", Signature); - fmt::print(UMSKT::debug, " AuthInfo: 0x{:08x}\n", AuthInfo); + fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", info.isUpgrade); + fmt::print(UMSKT::debug, "Channel ID: 0x{:08x}\n", info.ChannelID); + fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", info.Hash); + fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", info.Signature); + fmt::print(UMSKT::debug, " AuthInfo: 0x{:08x}\n", info.AuthInfo); fmt::print(UMSKT::debug, "\n"); BYTE msgDigest[SHA_DIGEST_LENGTH]{}, msgBuffer[SHA_MSG_LENGTH_2003]{}, xBin[FIELD_BYTES_2003]{}, @@ -107,12 +108,12 @@ BOOL BINK2002::Verify(std::string &pKey) msgBuffer[0x00] = 0x5D; msgBuffer[0x01] = (pData & 0x00FF); msgBuffer[0x02] = (pData & 0xFF00) >> 8; - msgBuffer[0x03] = (Hash & 0x000000FF); - msgBuffer[0x04] = (Hash & 0x0000FF00) >> 8; - msgBuffer[0x05] = (Hash & 0x00FF0000) >> 16; - msgBuffer[0x06] = (Hash & 0xFF000000) >> 24; - msgBuffer[0x07] = (AuthInfo & 0x00FF); - msgBuffer[0x08] = (AuthInfo & 0xFF00) >> 8; + msgBuffer[0x03] = (info.Hash & 0x000000FF); + msgBuffer[0x04] = (info.Hash & 0x0000FF00) >> 8; + msgBuffer[0x05] = (info.Hash & 0x00FF0000) >> 16; + msgBuffer[0x06] = (info.Hash & 0xFF000000) >> 24; + msgBuffer[0x07] = (info.AuthInfo & 0x00FF); + msgBuffer[0x08] = (info.AuthInfo & 0xFF00) >> 8; msgBuffer[0x09] = 0x00; msgBuffer[0x0A] = 0x00; @@ -140,7 +141,7 @@ BOOL BINK2002::Verify(std::string &pKey) */ BIGNUM *e = BN_lebin2bn((BYTE *)&iSignature, sizeof(iSignature), nullptr), - *s = BN_lebin2bn((BYTE *)&Signature, sizeof(Signature), nullptr), *x = BN_new(), *y = BN_new(); + *s = BN_lebin2bn((BYTE *)&info.Signature, sizeof(info.Signature), nullptr), *x = BN_new(), *y = BN_new(); // Create 2 points on the elliptic curve. EC_POINT *p = EC_POINT_new(eCurve), *t = EC_POINT_new(eCurve); @@ -190,11 +191,11 @@ BOOL BINK2002::Verify(std::string &pKey) EC_POINT_free(t); // If the computed hash checks out, the key is valid. - return compHash == Hash; + return compHash == info.Hash; } /* Generates a Windows Server 2003-like Product Key. */ -BOOL BINK2002::Generate(std::string &pKey) +BOOL BINK2002::Generate(KeyInfo &info, std::string &pKey) { BN_CTX *numContext = BN_CTX_new(); @@ -203,7 +204,7 @@ BOOL BINK2002::Generate(std::string &pKey) QWORD pRaw[2]{}; // Data segment of the RPK. - DWORD pData = ChannelID << 1 | isUpgrade; + DWORD pData = info.ChannelID << 1 | info.isUpgrade; BOOL noSquare; @@ -251,8 +252,8 @@ BOOL BINK2002::Generate(std::string &pKey) msgBuffer[0x04] = (pHash & 0x0000FF00) >> 8; msgBuffer[0x05] = (pHash & 0x00FF0000) >> 16; msgBuffer[0x06] = (pHash & 0xFF000000) >> 24; - msgBuffer[0x07] = (AuthInfo & 0x00FF); - msgBuffer[0x08] = (AuthInfo & 0xFF00) >> 8; + msgBuffer[0x07] = (info.AuthInfo & 0x00FF); + msgBuffer[0x08] = (info.AuthInfo & 0xFF00) >> 8; msgBuffer[0x09] = 0x00; msgBuffer[0x0A] = 0x00; @@ -331,21 +332,21 @@ BOOL BINK2002::Generate(std::string &pKey) BN_rshift1(s, s); // Translate resulting scalar into a 64-bit integer (the byte order is little-endian). - BN_bn2lebinpad(s, (BYTE *)&Signature, BN_num_bytes(s)); + BN_bn2lebinpad(s, (BYTE *)&info.Signature, BN_num_bytes(s)); // Pack product key. - Pack(pRaw); + Pack(info, pRaw); fmt::print(UMSKT::debug, "Generation results:\n"); - fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", isUpgrade); - fmt::print(UMSKT::debug, "Channel ID: 0x{:08x}\n", ChannelID); - fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", Hash); - fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", Signature); - fmt::print(UMSKT::debug, " AuthInfo: 0x{:08x}\n", AuthInfo); + fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", info.isUpgrade); + fmt::print(UMSKT::debug, "Channel ID: 0x{:08x}\n", info.ChannelID); + fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", info.Hash); + fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", info.Signature); + fmt::print(UMSKT::debug, " AuthInfo: 0x{:08x}\n", info.AuthInfo); fmt::print(UMSKT::debug, "\n"); EC_POINT_free(r); - } while (Signature > BITMASK(62) || noSquare); + } while (info.Signature > BITMASK(62) || noSquare); // ↑ ↑ ↑ // The signature can't be longer than 62 bits, else it will // overlap with the AuthInfo segment next to it. diff --git a/src/libumskt/pidgen3/BINK2002.h b/src/libumskt/pidgen3/BINK2002.h index 8c7e677..5e11dd7 100644 --- a/src/libumskt/pidgen3/BINK2002.h +++ b/src/libumskt/pidgen3/BINK2002.h @@ -27,12 +27,11 @@ class BINK2002 : public PIDGEN3 { - public: - BOOL Unpack(QWORD (&pRaw)[2]) override; - BOOL Pack(QWORD (&pRaw)[2]) override; + BOOL Unpack(KeyInfo &info, QWORD *pRaw) override; + BOOL Pack(const KeyInfo &info, QWORD *pRaw) override; BOOL Verify(std::string &pKey) override; - BOOL Generate(std::string &pKey) override; + BOOL Generate(KeyInfo &info, std::string &pKey) override; }; #endif // UMSKT_BINK2002_H diff --git a/src/libumskt/pidgen3/PIDGEN3.cpp b/src/libumskt/pidgen3/PIDGEN3.cpp index 9bf0a60..88137f1 100644 --- a/src/libumskt/pidgen3/PIDGEN3.cpp +++ b/src/libumskt/pidgen3/PIDGEN3.cpp @@ -62,39 +62,36 @@ BOOL PIDGEN3::LoadEllipticCurve(const std::string pSel, const std::string aSel, // Initialize BIGNUM and BIGNUMCTX structures. // BIGNUM - Large numbers // BIGNUMCTX - Context large numbers (temporary) - BIGNUM *a, *b, *p, *generatorX, *generatorY, *publicKeyX, *publicKeyY; - BN_CTX *context; + + // Context variable + BN_CTX *context = BN_CTX_new(); // We're presented with an elliptic curve, a multivariable function y(x; p; a; b), where // y^2 % p = x^3 + ax + b % p. - a = BN_new(); - b = BN_new(); - p = BN_new(); + BIGNUM *a = BN_CTX_get(context), *b = BN_CTX_get(context), *p = BN_CTX_get(context); // Public key will consist of the resulting (x; y) values. - publicKeyX = BN_new(); - publicKeyY = BN_new(); + BIGNUM *publicKeyX = BN_CTX_get(context), *publicKeyY = BN_CTX_get(context); // G(x; y) is a generator function, its return value represents a point on the elliptic curve. - generatorX = BN_new(); - generatorY = BN_new(); + BIGNUM *generatorX = BN_CTX_get(context), *generatorY = BN_CTX_get(context); - // Context variable - context = BN_CTX_new(); + genOrder = BN_new(); + privateKey = BN_new(); /* Public data */ - BN_dec2bn(&p, pSel.c_str()); - BN_dec2bn(&a, aSel.c_str()); - BN_dec2bn(&b, bSel.c_str()); - BN_dec2bn(&generatorX, generatorXSel.c_str()); - BN_dec2bn(&generatorY, generatorYSel.c_str()); + BN_dec2bn(&p, &pSel[0]); + BN_dec2bn(&a, &aSel[0]); + BN_dec2bn(&b, &bSel[0]); + BN_dec2bn(&generatorX, &generatorXSel[0]); + BN_dec2bn(&generatorY, &generatorYSel[0]); - BN_dec2bn(&publicKeyX, publicKeyXSel.c_str()); - BN_dec2bn(&publicKeyY, publicKeyYSel.c_str()); + BN_dec2bn(&publicKeyX, &publicKeyXSel[0]); + BN_dec2bn(&publicKeyY, &publicKeyYSel[0]); /* Computed Data */ - BN_dec2bn(&genOrder, genOrderSel.c_str()); - BN_dec2bn(&privateKey, privateKeySel.c_str()); + BN_dec2bn(&genOrder, &genOrderSel[0]); + BN_dec2bn(&privateKey, &privateKeySel[0]); BN_sub(privateKey, genOrder, privateKey); /* Elliptic Curve calculations. */ @@ -118,13 +115,6 @@ BOOL PIDGEN3::LoadEllipticCurve(const std::string pSel, const std::string aSel, // Cleanup BN_CTX_free(context); - BN_free(p); - BN_free(a); - BN_free(b); - BN_free(generatorX); - BN_free(generatorY); - BN_free(publicKeyX); - BN_free(publicKeyY); return true; } @@ -182,7 +172,7 @@ int PIDGEN3::BN_bn2lebin(const BIGNUM *a, unsigned char *to, int tolen) **/ void PIDGEN3::base24(std::string &cdKey, BYTE *byteSeq) { - BYTE rbyteSeq[16]; + BYTE rbyteSeq[16], output[26]; BIGNUM *z; // Copy byte sequence to the reversed byte sequence. @@ -204,9 +194,13 @@ void PIDGEN3::base24(std::string &cdKey, BYTE *byteSeq) // Divide z by 24 and convert the remainder to a CD-key char. for (int i = 24; i >= 0; i--) { - cdKey[i] = pKeyCharset[BN_div_word(z, 24)]; + output[i] = pKeyCharset[BN_div_word(z, 24)]; } + output[25] = 0; + + cdKey = (char *)output; + BN_free(z); } diff --git a/src/libumskt/pidgen3/PIDGEN3.h b/src/libumskt/pidgen3/PIDGEN3.h index 272ba22..820c482 100644 --- a/src/libumskt/pidgen3/PIDGEN3.h +++ b/src/libumskt/pidgen3/PIDGEN3.h @@ -31,9 +31,6 @@ class PIDGEN3 EC_GROUP *eCurve; EC_POINT *basePoint, *publicKey, *genPoint, *pubPoint; BIGNUM *genOrder, *privateKey; - DWORD Serial, AuthInfo, ChannelID, Hash; - QWORD Signature; - BOOL isUpgrade; public: PIDGEN3() @@ -49,20 +46,40 @@ class PIDGEN3 EC_POINT_free(publicKey); BN_free(genOrder); BN_free(privateKey); - BN_free(privateKey); - BN_free(genOrder); } + struct KeyInfo + { + DWORD Serial = 0, AuthInfo = 0, ChannelID = 0, Hash = 0; + QWORD Signature = 0; + BOOL isUpgrade = false; + + void setSerial(DWORD serialIn) + { + Serial = serialIn; + } + + void setAuthInfo(DWORD AuthInfoIn) + { + AuthInfo = AuthInfoIn; + } + + void setChannelID(DWORD ChannelIDIn) + { + ChannelID = ChannelIDIn; + } + }; + static constexpr char pKeyCharset[] = "BCDFGHJKMPQRTVWXY2346789"; BOOL LoadEllipticCurve(std::string pSel, std::string aSel, std::string bSel, std::string generatorXSel, std::string generatorYSel, std::string publicKeyXSel, std::string publicKeyYSel, std::string genOrderSel, std::string privateKeySel); - virtual BOOL Unpack(QWORD (&pRaw)[2]) = 0; - virtual BOOL Pack(QWORD (&pRaw)[2]) = 0; + virtual BOOL Unpack(KeyInfo &info, QWORD *pRaw) = 0; + virtual BOOL Pack(const KeyInfo &info, QWORD *pRaw) = 0; virtual BOOL Verify(std::string &pKey) = 0; - virtual BOOL Generate(std::string &pKey) = 0; + virtual BOOL Generate(KeyInfo &info, std::string &pKey) = 0; // PIDGEN3.cpp // Hello OpenSSL developers, please tell me, where is this function at? @@ -71,21 +88,6 @@ class PIDGEN3 void base24(std::string &cdKey, BYTE *byteSeq); void unbase24(BYTE *byteSeq, std::string cdKey); - - void setSerial(DWORD serialIn) - { - Serial = serialIn; - } - - void setAuthInfo(DWORD AuthInfoIn) - { - AuthInfo = AuthInfoIn; - } - - void setChannelID(DWORD ChannelIDIn) - { - ChannelID = ChannelIDIn; - } }; #endif // UMSKT_PIDGEN3_H diff --git a/src/main.cpp b/src/main.cpp index c85ea8f..e570623 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -31,5 +31,8 @@ int main(int argc, char *argv[]) } auto cli = CLI(); - return cli.Run(); + status = cli.Run(); + + UMSKT::DESTRUCT(); + return status; } diff --git a/src/options.cpp b/src/options.cpp index b7cd67e..0e27ac6 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -41,7 +41,7 @@ void CLI::SetHelpText() true, "1", &SetNumberOption}; helpOptions[OPTION_PRODUCT] = {"p", "product", "which product to generate keys for", - true, options.productCode, nullptr}; + true, options.productCode, &SetProductCodeOption}; helpOptions[OPTION_ACTIVATIONID] = { "i", "instid", "(activation only) installation ID used to generate confirmation ID", @@ -116,7 +116,8 @@ BOOL CLI::parseCommandLine() } else { - nextarg = arg[i + 1]; + i++; + nextarg = std::string(options.argv[i]); } } @@ -311,8 +312,18 @@ BOOL CLI::SetProductIDOption(int count, char *product) return true; } -BOOL CLI::SetValidateOption(int count, char *product) +BOOL CLI::SetValidateOption(int count, char *productID) { - options.keyToCheck = product; + options.keyToCheck = productID; + return true; +} + +BOOL CLI::SetProductCodeOption(int, char *product) +{ + if (options.verbose) + { + fmt::print("Setting product code to {}\n", product); + } + options.productCode = product; return true; }