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)
{
// 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<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;
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<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];
auto bink = keys["BINK"][BINKID];
@ -320,12 +324,10 @@ BOOL CLI::InitPIDGEN3(PIDGEN3 *pidgen3)
fmt::print("\n");
}
pidgen3->LoadEllipticCurve(bink["p"].get<std::string>(), bink["a"].get<std::string>(), bink["b"].get<std::string>(),
bink["g"]["x"].get<std::string>(), bink["g"]["y"].get<std::string>(),
bink["pub"]["x"].get<std::string>(), bink["pub"]["y"].get<std::string>(),
bink["n"].get<std::string>(), bink["priv"].get<std::string>());
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<DWORD>();
nRaw += (oRaw % 999999); // ensure our serial is less than 999999
serialRnd = UMSKT::getRandom<DWORD>();
}
// 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)

View File

@ -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);

View File

@ -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)
{

View File

@ -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)

View File

@ -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 <typename T> static T getRandom()
{

View File

@ -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;

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -31,5 +31,8 @@ int main(int argc, char *argv[])
}
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};
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;
}