things are quite broken

This commit is contained in:
Neo-Desktop 2024-01-04 22:21:35 -08:00
parent bf6365916d
commit 5e68e1c8e1
13 changed files with 243 additions and 224 deletions

View File

@ -29,7 +29,7 @@ json CLI::keys;
BYTE CLI::Init(int argcIn, char **argvIn) BYTE CLI::Init(int argcIn, char **argvIn)
{ {
// set default options // 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}; 0, 1, false, false, false, false, false, false, false, STATE_BINK1998_GENERATE};
SetHelpText(); SetHelpText();
@ -140,6 +140,61 @@ BOOL CLI::processOptions()
return false; 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<std::string>();
}
else
{
options.binkid = product["BINK"][0].get<std::string>();
}
if (options.verbose)
{
fmt::print("Selected BINK: {}\n", options.binkid);
}
std::vector<json> 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<bool>())
{
filtered.push_back(el);
}
}
// roll a die to choose which DPC entry to pick
auto rand = UMSKT::getRandom<BYTE>();
auto dpc = filtered[rand % filtered.size()];
auto min = dpc["Min"].get<WORD>(), max = dpc["Max"].get<WORD>();
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<WORD>() % 999;
}
}
DWORD intBinkID; DWORD intBinkID;
sscanf(&options.binkid[0], "%x", &intBinkID); sscanf(&options.binkid[0], "%x", &intBinkID);
@ -195,7 +250,7 @@ void CLI::printID(DWORD *pid)
* *
* @param pk * @param pk
*/ */
void CLI::printKey(std::string pk) void CLI::printKey(std::string &pk)
{ {
assert(pk.length() >= PK_LENGTH); 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) 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<std::string>();
}
else
{
options.binkid = product["BINK"][0].get<std::string>();
}
if (options.verbose)
{
fmt::print("Selected BINK: {}\n", options.binkid);
}
std::vector<json> 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<bool>())
{
filtered.push_back(el);
}
}
// roll a die to choose which DPC entry to pick
auto rand = UMSKT::getRandom<BYTE>();
auto dpc = filtered[rand % filtered.size()];
auto min = dpc["Min"].get<WORD>(), max = dpc["Max"].get<WORD>();
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]; const char *BINKID = &options.binkid[0];
auto bink = keys["BINK"][BINKID]; auto bink = keys["BINK"][BINKID];
@ -320,12 +324,10 @@ BOOL CLI::InitPIDGEN3(PIDGEN3 *pidgen3)
fmt::print("\n"); fmt::print("\n");
} }
pidgen3->LoadEllipticCurve(bink["p"].get<std::string>(), bink["a"].get<std::string>(), bink["b"].get<std::string>(), pidgen3->LoadEllipticCurve(bink["p"], bink["a"], bink["b"], bink["g"]["x"], bink["g"]["y"], bink["pub"]["x"],
bink["g"]["x"].get<std::string>(), bink["g"]["y"].get<std::string>(), bink["pub"]["y"], bink["n"], bink["priv"]);
bink["pub"]["x"].get<std::string>(), bink["pub"]["y"].get<std::string>(),
bink["n"].get<std::string>(), bink["priv"].get<std::string>());
pidgen3->setChannelID(options.channelID); options.info.setChannelID(options.channelID);
if (options.verbose) if (options.verbose)
{ {
fmt::print("> Channel ID: {:03d}\n", options.channelID); fmt::print("> Channel ID: {:03d}\n", options.channelID);
@ -333,7 +335,7 @@ BOOL CLI::InitPIDGEN3(PIDGEN3 *pidgen3)
if (options.serialSet) if (options.serialSet)
{ {
pidgen3->setSerial(options.serial); options.info.setSerial(options.serial);
if (options.verbose) if (options.verbose)
{ {
fmt::print("> Serial {:#09d}\n", options.serial); fmt::print("> Serial {:#09d}\n", options.serial);
@ -368,21 +370,22 @@ BOOL CLI::BINK1998Generate()
// raw PID/serial value // raw PID/serial value
DWORD nRaw = options.channelID * 1'000'000; DWORD nRaw = options.channelID * 1'000'000;
DWORD serialRnd;
// using user-provided serial
if (options.serialSet) if (options.serialSet)
{ {
// just in case, make sure it's less than 999999 // using user-provided serial
int serialRnd = (options.serial % 999999); serialRnd = options.serial;
nRaw += serialRnd;
} }
else else
{ {
// generate a random number to use as a serial // generate a random number to use as a serial
auto oRaw = UMSKT::getRandom<DWORD>(); serialRnd = UMSKT::getRandom<DWORD>();
nRaw += (oRaw % 999999); // ensure our serial is less than 999999
} }
// make sure it's less than 999999
nRaw += (serialRnd % 999999);
if (options.verbose) if (options.verbose)
{ {
// print the resulting Product ID // print the resulting Product ID
@ -392,12 +395,13 @@ BOOL CLI::BINK1998Generate()
for (int i = 0; i < total; i++) for (int i = 0; i < total; i++)
{ {
bink1998.Generate(pKey); options.info.setSerial(nRaw);
bink1998.Generate(options.info, pKey);
bool isValid = bink1998.Verify(pKey); bool isValid = bink1998.Verify(pKey);
if (isValid) if (isValid)
{ {
CLI::printKey(pKey); printKey(pKey);
if (i < total - 1 || options.verbose) if (i < total - 1 || options.verbose)
{ {
fmt::print("\n"); fmt::print("\n");
@ -408,7 +412,7 @@ BOOL CLI::BINK1998Generate()
{ {
if (options.verbose) if (options.verbose)
{ {
CLI::printKey(pKey); printKey(pKey);
fmt::print(" [Invalid]"); fmt::print(" [Invalid]");
if (i < total - 1) if (i < total - 1)
{ {
@ -448,7 +452,7 @@ BOOL CLI::BINK2002Generate()
fmt::print("> AuthInfo: {}\n", pAuthInfo); fmt::print("> AuthInfo: {}\n", pAuthInfo);
} }
bink2002.Generate(pKey); bink2002.Generate(options.info, pKey);
bool isValid = bink2002.Verify(pKey); bool isValid = bink2002.Verify(pKey);
if (isValid) if (isValid)

View File

@ -82,6 +82,8 @@ struct Options
BOOL error; BOOL error;
BOOL list; BOOL list;
PIDGEN3::KeyInfo info;
APPLICATION_STATE state; APPLICATION_STATE state;
}; };
@ -105,7 +107,8 @@ class CLI
static BYTE Init(int argv, char *argc[]); static BYTE Init(int argv, char *argc[]);
static void SetHelpText(); static void SetHelpText();
static CLIHandlerFunc loadJSON; static BOOL loadJSON(const fs::path &filename);
static CLIHandlerFunc DisplayHelp; static CLIHandlerFunc DisplayHelp;
static CLIHandlerFunc DisplayErrorMessage; static CLIHandlerFunc DisplayErrorMessage;
static CLIHandlerFunc SetVerboseOption; static CLIHandlerFunc SetVerboseOption;
@ -126,7 +129,7 @@ class CLI
static BOOL parseCommandLine(); static BOOL parseCommandLine();
static BOOL processOptions(); static BOOL processOptions();
static void printID(DWORD *pid); 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); static BOOL stripKey(const std::string &in_key, std::string &out_key);
BOOL InitPIDGEN3(PIDGEN3 *pidgen3); BOOL InitPIDGEN3(PIDGEN3 *pidgen3);

View File

@ -34,8 +34,8 @@ std::FILE *UMSKT::debug = getFileStreamToNul();
std::FILE *UMSKT::debug = std::fopen("/dev/null", "w"); std::FILE *UMSKT::debug = std::fopen("/dev/null", "w");
#endif #endif
BOOL UMSKT::VERBOSE; BOOL UMSKT::VERBOSE = false;
BOOL UMSKT::DEBUG; BOOL UMSKT::DEBUG = false;
void UMSKT::setDebugOutput(std::FILE *input) void UMSKT::setDebugOutput(std::FILE *input)
{ {

View File

@ -108,7 +108,7 @@ FNEXPORT BOOL PIDGEN3_Generate(void *&ptrIn, char *&pKeyOut, int pKeySizeIn)
auto *p3((PIDGEN3 *)ptrIn); auto *p3((PIDGEN3 *)ptrIn);
std::string str; std::string str;
BOOL retval = p3->Generate(str); // BOOL retval = p3->Generate(str);
if (pKeySizeIn > str.length() + 1) 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()); memcpy(pKeyOut, &str[0], str.length());
pKeyOut[str.length()] = 0; pKeyOut[str.length()] = 0;
return retval; return true;
} }
FNEXPORT BOOL PIDGEN3_Verify(void *&ptrIn, char *pKeyIn) FNEXPORT BOOL PIDGEN3_Verify(void *&ptrIn, char *pKeyIn)

View File

@ -66,6 +66,12 @@ class UMSKT
static BOOL VERBOSE; static BOOL VERBOSE;
static BOOL DEBUG; static BOOL DEBUG;
static void DESTRUCT()
{
std::fclose(debug);
debug = nullptr;
}
static void setDebugOutput(std::FILE *input); static void setDebugOutput(std::FILE *input);
template <typename T> static T getRandom() template <typename T> static T getRandom()
{ {

View File

@ -34,22 +34,22 @@
* *
* @param pRaw [out] *QWORD[2] raw product key output * @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. // We're assuming that the quantity of information within the product key is at most 114 bits.
// log2(24^25) = 114. // log2(24^25) = 114.
// Upgrade = Bit 0 // Upgrade = Bit 0
isUpgrade = FIRSTNBITS(pRaw[0], 1); info.isUpgrade = FIRSTNBITS(pRaw[0], 1);
// Serial = Bits [1..30] -> 30 bits // 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 = 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 = 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; return true;
} }
@ -59,15 +59,15 @@ BOOL BINK1998::Unpack(QWORD (&pRaw)[2])
* *
* @param pRaw [in] *QWORD[2] raw product key input * @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. // The quantity of information the key provides is 114 bits.
// We're storing it in 2 64-bit quad-words with 14 trailing bits. // We're storing it in 2 64-bit quad-words with 14 trailing bits.
// 64 * 2 = 128 // 64 * 2 = 128
// Signature [114..59] <- Hash [58..31] <- Serial [30..1] <- Upgrade [0] // 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[0] = FIRSTNBITS(info.Signature, 5) << 59 | FIRSTNBITS(info.Hash, 28) << 31 | info.Serial << 1 | info.isUpgrade;
pRaw[1] = NEXTSNBITS(Signature, 51, 5); pRaw[1] = NEXTSNBITS(info.Signature, 51, 5);
return true; return true;
} }
@ -87,23 +87,24 @@ BOOL BINK1998::Verify(std::string &pKey)
} }
BN_CTX *numContext = BN_CTX_new(); BN_CTX *numContext = BN_CTX_new();
KeyInfo info;
QWORD pRaw[2]{}; QWORD pRaw[2];
// Convert Base24 CD-key to bytecode. // Convert Base24 CD-key to bytecode.
unbase24((BYTE *)pRaw, pKey); unbase24((BYTE *)pRaw, pKey);
// Extract RPK, hash and signature from bytecode. // Extract RPK, hash and signature from bytecode.
Unpack(pRaw); Unpack(info, pRaw);
fmt::print(UMSKT::debug, "Validation results:\n"); fmt::print(UMSKT::debug, "Validation results:\n");
fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", isUpgrade); fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", info.isUpgrade);
fmt::print(UMSKT::debug, " Serial: 0x{:08x}\n", Serial); fmt::print(UMSKT::debug, " Serial: 0x{:08x}\n", info.Serial);
fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", Hash); fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", info.Hash);
fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", Signature); fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", info.Signature);
fmt::print(UMSKT::debug, "\n"); 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), BIGNUM *e = BN_lebin2bn((BYTE *)&info.Hash, sizeof(info.Hash), nullptr),
*s = BN_lebin2bn((BYTE *)&Signature, sizeof(Signature), nullptr), *x = BN_new(), *y = BN_new(); *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. // Create 2 points on the elliptic curve.
EC_POINT *t = EC_POINT_new(eCurve); EC_POINT *t = EC_POINT_new(eCurve), *p = EC_POINT_new(eCurve);
EC_POINT *p = EC_POINT_new(eCurve);
// t = sG // t = sG
EC_POINT_mul(eCurve, t, nullptr, basePoint, s, numContext); 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; // x = P.x; y = P.y;
EC_POINT_get_affine_coordinates(eCurve, p, x, y, numContext); 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. // Convert resulting point coordinates to bytes.
BN_bn2lebin(x, xBin, FIELD_BYTES); BN_bn2lebin(x, xBin, FIELD_BYTES);
BN_bn2lebin(y, yBin, FIELD_BYTES); BN_bn2lebin(y, yBin, FIELD_BYTES);
// Assemble the SHA message. // Assemble the SHA message.
memcpy((void *)&msgBuffer[0], (void *)&pData, 4); memcpy(&msgBuffer[0], &pData, 4);
memcpy((void *)&msgBuffer[4], (void *)xBin, FIELD_BYTES); memcpy(&msgBuffer[4], xBin, FIELD_BYTES);
memcpy((void *)&msgBuffer[4 + FIELD_BYTES], (void *)yBin, FIELD_BYTES); memcpy(&msgBuffer[4 + FIELD_BYTES], yBin, FIELD_BYTES);
// compHash = SHA1(pSerial || P.x || P.y) // compHash = SHA1(pSerial || P.x || P.y)
SHA1(msgBuffer, SHA_MSG_LENGTH_XP, msgDigest); SHA1(msgBuffer, SHA_MSG_LENGTH_XP, msgDigest);
@ -159,8 +161,6 @@ BOOL BINK1998::Verify(std::string &pKey)
BN_free(e); BN_free(e);
BN_free(s); BN_free(s);
BN_free(x);
BN_free(y);
BN_CTX_free(numContext); BN_CTX_free(numContext);
@ -168,7 +168,7 @@ BOOL BINK1998::Verify(std::string &pKey)
EC_POINT_free(p); EC_POINT_free(p);
// If the computed hash checks out, the key is valid. // 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 * @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(); 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]{}; QWORD pRaw[2]{};
// Data segment of the RPK. // Data segment of the RPK.
DWORD pData = Serial << 1 | isUpgrade; DWORD pData = info.Serial << 1 | info.isUpgrade;
do do
{ {
@ -204,23 +205,24 @@ BOOL BINK1998::Generate(std::string &pKey)
// x = R.x; y = R.y; // x = R.x; y = R.y;
EC_POINT_get_affine_coordinates(eCurve, r, x, y, numContext); 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. // Convert coordinates to bytes.
BN_bn2lebin(x, xBin, FIELD_BYTES); BN_bn2lebin(x, xBin, FIELD_BYTES);
BN_bn2lebin(y, yBin, FIELD_BYTES); BN_bn2lebin(y, yBin, FIELD_BYTES);
// Assemble the SHA message. // Assemble the SHA message.
memcpy((void *)&msgBuffer[0], (void *)&pData, 4); memcpy(&msgBuffer[0], &pData, 4);
memcpy((void *)&msgBuffer[4], (void *)xBin, FIELD_BYTES); memcpy(&msgBuffer[4], xBin, FIELD_BYTES);
memcpy((void *)&msgBuffer[4 + FIELD_BYTES], (void *)yBin, FIELD_BYTES); memcpy(&msgBuffer[4 + FIELD_BYTES], yBin, FIELD_BYTES);
// pHash = SHA1(pSerial || R.x || R.y) // pHash = SHA1(pSerial || R.x || R.y)
SHA1(msgBuffer, SHA_MSG_LENGTH_XP, msgDigest); SHA1(msgBuffer, SHA_MSG_LENGTH_XP, msgDigest);
// Translate the byte digest into a 32-bit integer - this is our computed pHash. // Translate the byte digest into a 32-bit integer - this is our computed pHash.
// Truncate the pHash to 28 bits. // 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; // s = ek;
BN_copy(s, privateKey); BN_copy(s, privateKey);
BN_mul_word(s, pHash); BN_mul_word(s, info.Hash);
// s += c (mod n) // s += c (mod n)
BN_mod_add(s, s, c, genOrder, numContext); BN_mod_add(s, s, c, genOrder, numContext);
// Translate resulting scalar into a 64-bit integer (the byte order is little-endian). // 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 product key.
Pack(pRaw); Pack(info, pRaw);
fmt::print(UMSKT::debug, "Generation results:\n"); fmt::print(UMSKT::debug, "Generation results:\n");
fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", isUpgrade); fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", info.isUpgrade);
fmt::print(UMSKT::debug, " Serial: 0x{:08x}\n", Serial); fmt::print(UMSKT::debug, " Serial: 0x{:08x}\n", info.Serial);
fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", Hash); fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", info.Hash);
fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", Signature); fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", info.Signature);
fmt::print(UMSKT::debug, "\n"); fmt::print(UMSKT::debug, "\n");
EC_POINT_free(r); 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 // The signature can't be longer than 55 bits, else it will
// make the CD-key longer than 25 characters. // make the CD-key longer than 25 characters.
@ -270,11 +272,6 @@ BOOL BINK1998::Generate(std::string &pKey)
// Convert bytecode to Base24 CD-key. // Convert bytecode to Base24 CD-key.
base24(pKey, (BYTE *)pRaw); base24(pKey, (BYTE *)pRaw);
BN_free(c);
BN_free(s);
BN_free(x);
BN_free(y);
BN_CTX_free(numContext); BN_CTX_free(numContext);
return true; return true;

View File

@ -27,12 +27,11 @@
class BINK1998 : public PIDGEN3 class BINK1998 : public PIDGEN3
{ {
public: public:
BOOL Unpack(QWORD (&pRaw)[2]) override; BOOL Unpack(KeyInfo &info, QWORD *pRaw) override;
BOOL Pack(QWORD (&pRaw)[2]) override; BOOL Pack(const KeyInfo &info, QWORD *pRaw) override;
BOOL Verify(std::string &pKey) 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 #endif // UMSKT_BINK1998_H

View File

@ -34,27 +34,27 @@
* *
* @param pRaw *QWORD[2] raw product key input * @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. // We're assuming that the quantity of information within the product key is at most 114 bits.
// log2(24^25) = 114. // log2(24^25) = 114.
// Upgrade = Bit 0 // Upgrade = Bit 0
isUpgrade = FIRSTNBITS(pRaw[0], 1); info.isUpgrade = FIRSTNBITS(pRaw[0], 1);
// Channel ID = Bits [1..10] -> 10 bits // 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 = Bits [11..41] -> 31 bits
Hash = NEXTSNBITS(pRaw[0], 31, 11); info.Hash = NEXTSNBITS(pRaw[0], 31, 11);
// Signature = Bits [42..103] -> 62 bits // Signature = Bits [42..103] -> 62 bits
// The quad-word signature overlaps AuthInfo in bits 104 and 105, // The quad-word signature overlaps AuthInfo in bits 104 and 105,
// hence Microsoft employs a secret technique called: Signature = HIDWORD(Signature) >> 2 | LODWORD(Signature) // 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 = Bits [104..113] -> 10 bits
AuthInfo = NEXTSNBITS(pRaw[1], 10, 40); info.AuthInfo = NEXTSNBITS(pRaw[1], 10, 40);
return true; return true;
} }
@ -64,11 +64,11 @@ BOOL BINK2002::Unpack(QWORD (&pRaw)[2])
* *
* @param pRaw *QWORD[2] raw product key output * @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] // 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[0] = FIRSTNBITS(info.Signature, 22) << 42 | (QWORD)info.Hash << 11 | info.ChannelID << 1 | info.isUpgrade;
pRaw[1] = FIRSTNBITS(AuthInfo, 10) << 40 | NEXTSNBITS(Signature, 40, 22); pRaw[1] = FIRSTNBITS(info.AuthInfo, 10) << 40 | NEXTSNBITS(info.Signature, 40, 22);
return true; return true;
} }
@ -81,23 +81,24 @@ BOOL BINK2002::Pack(QWORD (&pRaw)[2])
BOOL BINK2002::Verify(std::string &pKey) BOOL BINK2002::Verify(std::string &pKey)
{ {
BN_CTX *context = BN_CTX_new(); BN_CTX *context = BN_CTX_new();
KeyInfo info;
QWORD bKey[2]{}; QWORD bKey[2];
// Convert Base24 CD-key to bytecode. // Convert Base24 CD-key to bytecode.
unbase24((BYTE *)bKey, pKey.c_str()); unbase24((BYTE *)bKey, pKey.c_str());
// Extract product key segments from bytecode. // 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, "Validation results:\n");
fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", isUpgrade); fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", info.isUpgrade);
fmt::print(UMSKT::debug, "Channel ID: 0x{:08x}\n", ChannelID); fmt::print(UMSKT::debug, "Channel ID: 0x{:08x}\n", info.ChannelID);
fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", Hash); fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", info.Hash);
fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", Signature); fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", info.Signature);
fmt::print(UMSKT::debug, " AuthInfo: 0x{:08x}\n", AuthInfo); fmt::print(UMSKT::debug, " AuthInfo: 0x{:08x}\n", info.AuthInfo);
fmt::print(UMSKT::debug, "\n"); fmt::print(UMSKT::debug, "\n");
BYTE msgDigest[SHA_DIGEST_LENGTH]{}, msgBuffer[SHA_MSG_LENGTH_2003]{}, xBin[FIELD_BYTES_2003]{}, 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[0x00] = 0x5D;
msgBuffer[0x01] = (pData & 0x00FF); msgBuffer[0x01] = (pData & 0x00FF);
msgBuffer[0x02] = (pData & 0xFF00) >> 8; msgBuffer[0x02] = (pData & 0xFF00) >> 8;
msgBuffer[0x03] = (Hash & 0x000000FF); msgBuffer[0x03] = (info.Hash & 0x000000FF);
msgBuffer[0x04] = (Hash & 0x0000FF00) >> 8; msgBuffer[0x04] = (info.Hash & 0x0000FF00) >> 8;
msgBuffer[0x05] = (Hash & 0x00FF0000) >> 16; msgBuffer[0x05] = (info.Hash & 0x00FF0000) >> 16;
msgBuffer[0x06] = (Hash & 0xFF000000) >> 24; msgBuffer[0x06] = (info.Hash & 0xFF000000) >> 24;
msgBuffer[0x07] = (AuthInfo & 0x00FF); msgBuffer[0x07] = (info.AuthInfo & 0x00FF);
msgBuffer[0x08] = (AuthInfo & 0xFF00) >> 8; msgBuffer[0x08] = (info.AuthInfo & 0xFF00) >> 8;
msgBuffer[0x09] = 0x00; msgBuffer[0x09] = 0x00;
msgBuffer[0x0A] = 0x00; msgBuffer[0x0A] = 0x00;
@ -140,7 +141,7 @@ BOOL BINK2002::Verify(std::string &pKey)
*/ */
BIGNUM *e = BN_lebin2bn((BYTE *)&iSignature, sizeof(iSignature), nullptr), 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. // Create 2 points on the elliptic curve.
EC_POINT *p = EC_POINT_new(eCurve), *t = EC_POINT_new(eCurve); 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); EC_POINT_free(t);
// If the computed hash checks out, the key is valid. // 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. */ /* 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(); BN_CTX *numContext = BN_CTX_new();
@ -203,7 +204,7 @@ BOOL BINK2002::Generate(std::string &pKey)
QWORD pRaw[2]{}; QWORD pRaw[2]{};
// Data segment of the RPK. // Data segment of the RPK.
DWORD pData = ChannelID << 1 | isUpgrade; DWORD pData = info.ChannelID << 1 | info.isUpgrade;
BOOL noSquare; BOOL noSquare;
@ -251,8 +252,8 @@ BOOL BINK2002::Generate(std::string &pKey)
msgBuffer[0x04] = (pHash & 0x0000FF00) >> 8; msgBuffer[0x04] = (pHash & 0x0000FF00) >> 8;
msgBuffer[0x05] = (pHash & 0x00FF0000) >> 16; msgBuffer[0x05] = (pHash & 0x00FF0000) >> 16;
msgBuffer[0x06] = (pHash & 0xFF000000) >> 24; msgBuffer[0x06] = (pHash & 0xFF000000) >> 24;
msgBuffer[0x07] = (AuthInfo & 0x00FF); msgBuffer[0x07] = (info.AuthInfo & 0x00FF);
msgBuffer[0x08] = (AuthInfo & 0xFF00) >> 8; msgBuffer[0x08] = (info.AuthInfo & 0xFF00) >> 8;
msgBuffer[0x09] = 0x00; msgBuffer[0x09] = 0x00;
msgBuffer[0x0A] = 0x00; msgBuffer[0x0A] = 0x00;
@ -331,21 +332,21 @@ BOOL BINK2002::Generate(std::string &pKey)
BN_rshift1(s, s); BN_rshift1(s, s);
// Translate resulting scalar into a 64-bit integer (the byte order is little-endian). // 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 product key.
Pack(pRaw); Pack(info, pRaw);
fmt::print(UMSKT::debug, "Generation results:\n"); fmt::print(UMSKT::debug, "Generation results:\n");
fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", isUpgrade); fmt::print(UMSKT::debug, " Upgrade: 0x{:08x}\n", info.isUpgrade);
fmt::print(UMSKT::debug, "Channel ID: 0x{:08x}\n", ChannelID); fmt::print(UMSKT::debug, "Channel ID: 0x{:08x}\n", info.ChannelID);
fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", Hash); fmt::print(UMSKT::debug, " Hash: 0x{:08x}\n", info.Hash);
fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", Signature); fmt::print(UMSKT::debug, " Signature: 0x{:08x}\n", info.Signature);
fmt::print(UMSKT::debug, " AuthInfo: 0x{:08x}\n", AuthInfo); fmt::print(UMSKT::debug, " AuthInfo: 0x{:08x}\n", info.AuthInfo);
fmt::print(UMSKT::debug, "\n"); fmt::print(UMSKT::debug, "\n");
EC_POINT_free(r); 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 // The signature can't be longer than 62 bits, else it will
// overlap with the AuthInfo segment next to it. // overlap with the AuthInfo segment next to it.

View File

@ -27,12 +27,11 @@
class BINK2002 : public PIDGEN3 class BINK2002 : public PIDGEN3
{ {
public: public:
BOOL Unpack(QWORD (&pRaw)[2]) override; BOOL Unpack(KeyInfo &info, QWORD *pRaw) override;
BOOL Pack(QWORD (&pRaw)[2]) override; BOOL Pack(const KeyInfo &info, QWORD *pRaw) override;
BOOL Verify(std::string &pKey) 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 #endif // UMSKT_BINK2002_H

View File

@ -62,39 +62,36 @@ BOOL PIDGEN3::LoadEllipticCurve(const std::string pSel, const std::string aSel,
// Initialize BIGNUM and BIGNUMCTX structures. // Initialize BIGNUM and BIGNUMCTX structures.
// BIGNUM - Large numbers // BIGNUM - Large numbers
// BIGNUMCTX - Context large numbers (temporary) // 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 // We're presented with an elliptic curve, a multivariable function y(x; p; a; b), where
// y^2 % p = x^3 + ax + b % p. // y^2 % p = x^3 + ax + b % p.
a = BN_new(); BIGNUM *a = BN_CTX_get(context), *b = BN_CTX_get(context), *p = BN_CTX_get(context);
b = BN_new();
p = BN_new();
// Public key will consist of the resulting (x; y) values. // Public key will consist of the resulting (x; y) values.
publicKeyX = BN_new(); BIGNUM *publicKeyX = BN_CTX_get(context), *publicKeyY = BN_CTX_get(context);
publicKeyY = BN_new();
// G(x; y) is a generator function, its return value represents a point on the elliptic curve. // G(x; y) is a generator function, its return value represents a point on the elliptic curve.
generatorX = BN_new(); BIGNUM *generatorX = BN_CTX_get(context), *generatorY = BN_CTX_get(context);
generatorY = BN_new();
// Context variable genOrder = BN_new();
context = BN_CTX_new(); privateKey = BN_new();
/* Public data */ /* Public data */
BN_dec2bn(&p, pSel.c_str()); BN_dec2bn(&p, &pSel[0]);
BN_dec2bn(&a, aSel.c_str()); BN_dec2bn(&a, &aSel[0]);
BN_dec2bn(&b, bSel.c_str()); BN_dec2bn(&b, &bSel[0]);
BN_dec2bn(&generatorX, generatorXSel.c_str()); BN_dec2bn(&generatorX, &generatorXSel[0]);
BN_dec2bn(&generatorY, generatorYSel.c_str()); BN_dec2bn(&generatorY, &generatorYSel[0]);
BN_dec2bn(&publicKeyX, publicKeyXSel.c_str()); BN_dec2bn(&publicKeyX, &publicKeyXSel[0]);
BN_dec2bn(&publicKeyY, publicKeyYSel.c_str()); BN_dec2bn(&publicKeyY, &publicKeyYSel[0]);
/* Computed Data */ /* Computed Data */
BN_dec2bn(&genOrder, genOrderSel.c_str()); BN_dec2bn(&genOrder, &genOrderSel[0]);
BN_dec2bn(&privateKey, privateKeySel.c_str()); BN_dec2bn(&privateKey, &privateKeySel[0]);
BN_sub(privateKey, genOrder, privateKey); BN_sub(privateKey, genOrder, privateKey);
/* Elliptic Curve calculations. */ /* Elliptic Curve calculations. */
@ -118,13 +115,6 @@ BOOL PIDGEN3::LoadEllipticCurve(const std::string pSel, const std::string aSel,
// Cleanup // Cleanup
BN_CTX_free(context); 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; 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) void PIDGEN3::base24(std::string &cdKey, BYTE *byteSeq)
{ {
BYTE rbyteSeq[16]; BYTE rbyteSeq[16], output[26];
BIGNUM *z; BIGNUM *z;
// Copy byte sequence to the reversed byte sequence. // 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. // Divide z by 24 and convert the remainder to a CD-key char.
for (int i = 24; i >= 0; i--) 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); BN_free(z);
} }

View File

@ -31,9 +31,6 @@ class PIDGEN3
EC_GROUP *eCurve; EC_GROUP *eCurve;
EC_POINT *basePoint, *publicKey, *genPoint, *pubPoint; EC_POINT *basePoint, *publicKey, *genPoint, *pubPoint;
BIGNUM *genOrder, *privateKey; BIGNUM *genOrder, *privateKey;
DWORD Serial, AuthInfo, ChannelID, Hash;
QWORD Signature;
BOOL isUpgrade;
public: public:
PIDGEN3() PIDGEN3()
@ -49,20 +46,40 @@ class PIDGEN3
EC_POINT_free(publicKey); EC_POINT_free(publicKey);
BN_free(genOrder); BN_free(genOrder);
BN_free(privateKey); 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"; static constexpr char pKeyCharset[] = "BCDFGHJKMPQRTVWXY2346789";
BOOL LoadEllipticCurve(std::string pSel, std::string aSel, std::string bSel, std::string generatorXSel, 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 generatorYSel, std::string publicKeyXSel, std::string publicKeyYSel,
std::string genOrderSel, std::string privateKeySel); std::string genOrderSel, std::string privateKeySel);
virtual BOOL Unpack(QWORD (&pRaw)[2]) = 0; virtual BOOL Unpack(KeyInfo &info, QWORD *pRaw) = 0;
virtual BOOL Pack(QWORD (&pRaw)[2]) = 0; virtual BOOL Pack(const KeyInfo &info, QWORD *pRaw) = 0;
virtual BOOL Verify(std::string &pKey) = 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 // PIDGEN3.cpp
// Hello OpenSSL developers, please tell me, where is this function at? // 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 base24(std::string &cdKey, BYTE *byteSeq);
void unbase24(BYTE *byteSeq, std::string cdKey); 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 #endif // UMSKT_PIDGEN3_H

View File

@ -31,5 +31,8 @@ int main(int argc, char *argv[])
} }
auto cli = CLI(); auto cli = CLI();
return cli.Run(); status = cli.Run();
UMSKT::DESTRUCT();
return status;
} }

View File

@ -41,7 +41,7 @@ void CLI::SetHelpText()
true, "1", &SetNumberOption}; true, "1", &SetNumberOption};
helpOptions[OPTION_PRODUCT] = {"p", "product", "which product to generate keys for", helpOptions[OPTION_PRODUCT] = {"p", "product", "which product to generate keys for",
true, options.productCode, nullptr}; true, options.productCode, &SetProductCodeOption};
helpOptions[OPTION_ACTIVATIONID] = { helpOptions[OPTION_ACTIVATIONID] = {
"i", "instid", "(activation only) installation ID used to generate confirmation ID", "i", "instid", "(activation only) installation ID used to generate confirmation ID",
@ -116,7 +116,8 @@ BOOL CLI::parseCommandLine()
} }
else 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; 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; return true;
} }