WIP more rewrite/rework changes, major refactoring

This commit is contained in:
Neo-Desktop 2024-01-24 20:04:36 -08:00
parent 783a5afb3a
commit 3e51fe1c8b
35 changed files with 3313 additions and 2106 deletions

View File

@ -1,3 +1,2 @@
build*/
cmake-build*/
djgpp-build*/
cmake-*/

2
.gitignore vendored
View File

@ -66,7 +66,7 @@ umskt
# *.ipr
# CMake
cmake-build-*/
cmake-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml

View File

@ -20,6 +20,6 @@
repos:
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: 'v15.0.7' # Use the sha / tag you want to point at
rev: 'v17.0.6' # Use the sha / tag you want to point at
hooks:
- id: clang-format

View File

@ -23,8 +23,9 @@ PROJECT(UMSKT)
SET(CMAKE_CXX_STANDARD 17)
SET(CMAKE_OSX_SYSROOT "macosx" CACHE PATH "macOS SDK path")
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
OPTION(BUILD_SHARED_LIBS "Build internal libraries as dynamic" OFF)
OPTION(BUILD_SHARED_LIBS "Build all libraries as shared" OFF)
OPTION(UMSKT_USE_SHARED_OPENSSL "Force linking against the system-wide OpenSSL library" OFF)
OPTION(MUSL_STATIC "Enable fully static builds in a muslc environment (i.e. Alpine Linux)" OFF)
OPTION(DJGPP_WATT32 "Enable compilation and linking with DJGPP/WATT32/OpenSSL" OFF)
@ -53,7 +54,6 @@ ELSE ()
MESSAGE(STATUS "[UMSKT] Requesting static version of OpenSSL")
ENDIF ()
IF (DJGPP_WATT32)
SET(CMAKE_SYSTEM_NAME MSDOS)
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
@ -72,13 +72,8 @@ ENDIF ()
# if we're compiling with MSVC, respect the DEBUG compile option
IF (MSVC)
SET(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
IF (NOT BUILD_SHARED_LIBS)
SET(CMAKE_CXX_FLAGS_RELEASE "/MT")
SET(CMAKE_CXX_FLAGS_DEBUG "/MTd")
ELSE ()
SET(CMAKE_CXX_FLAGS_RELEASE "/MD")
SET(CMAKE_CXX_FLAGS_DEBUG "/MDd")
ENDIF ()
SET(CMAKE_CXX_FLAGS_RELEASE "/MT")
SET(CMAKE_CXX_FLAGS_DEBUG "/MTd")
SET(CMAKE_EXE_LINKER_FLAGS "/INCREMENTAL:NO /NODEFAULTLIB:MSVCRT")
SET(CMAKE_ENABLE_EXPORTS ON)
SET(UMSKT_EXE_WINDOWS_EXTRA src/windows/umskt.rc)
@ -151,15 +146,15 @@ INCLUDE(${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake)
CPMAddPackage(
NAME nlohmann_json
GITHUB_REPOSITORY nlohmann/json
VERSION 3.11.2
VERSION 3.11.3
)
# Include fmt development library
CPMAddPackage(
NAME fmt
GITHUB_REPOSITORY fmtlib/fmt
GIT_TAG 10.0.0
VERSION 10.0.0
GIT_TAG 10.2.0
VERSION 10.2.0
)
# Include cmrc resource compiler
@ -194,30 +189,58 @@ SET(LIBUMSKT_PIDGEN2 src/libumskt/pidgen2/PIDGEN2.cpp)
SET(LIBUMSKT_PIDGEN3 src/libumskt/pidgen3/PIDGEN3.cpp src/libumskt/pidgen3/BINK1998.cpp src/libumskt/pidgen3/BINK2002.cpp)
SET(LIBUMSKT_CONFID src/libumskt/confid/confid.cpp src/libumskt/confid/polynomial.cpp src/libumskt/confid/residue.cpp src/libumskt/confid/divisor.cpp)
SET(LIBUMSKT_SRC src/libumskt/libumskt.cpp src/libumskt/debugoutput.cpp ${LIBUMSKT_PIDGEN2} ${LIBUMSKT_PIDGEN3} ${LIBUMSKT_CONFID})
SET(UMSKT_CLI_SRC src/main.cpp src/cli.cpp src/options.cpp)
SET(UMSKT_CLI_SRC src/main.cpp src/help.cpp src/cli.cpp src/generate.cpp)
SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS} ${OPENSSL_CRYPTO_LIBRARIES} ${ZLIB_LIBRARIES} fmt)
IF (NOT MSVC)
SET(UMSKT_LINK_LIBS ${UMSKT_LINK_LIBS} umskt::rc)
SET(UMSKT_CLI_SRC ${UMSKT_CLI_SRC} src/rc.cpp)
ELSE()
SET(UMSKT_CLI_SRC ${UMSKT_CLI_SRC} src/windows/platform.cpp)
ENDIF()
#### Separate Build Path for emscripten
IF (EMSCRIPTEN)
ADD_EXECUTABLE(umskt ${UMSKT_CLI_SRC} ${LIBUMSKT_SRC})
TARGET_INCLUDE_DIRECTORIES(umskt PUBLIC ${OPENSSL_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(umskt -static OpenSSL::Crypto cryptopp::cryptopp fmt)
TARGET_LINK_LIBRARIES(umskt PUBLIC ${UMSKT_LINK_LIBS})
SET(CMAKE_EXECUTABLE_SUFFIX ".html")
SET_TARGET_PROPERTIES(umskt PROPERTIES COMPILE_FLAGS "-Os -sEXPORTED_RUNTIME_METHODS=ccall,cwrap")
SET_TARGET_PROPERTIES(umskt PROPERTIES LINK_FLAGS "-Os -sWASM=1 -sEXPORT_ALL=1 -sEXPORTED_RUNTIME_METHODS=ccall,cwrap --no-entry")
ELSE ()
ADD_LIBRARY(_umskt ${LIBUMSKT_SRC} ${UMSKT_EXE_WINDOWS_EXTRA} ${UMSKT_EXE_WINDOWS_DLL})
TARGET_INCLUDE_DIRECTORIES(_umskt PUBLIC ${OPENSSL_INCLUDE_DIR})
## umskt.so/.dll creation
ADD_LIBRARY(_umskt SHARED ${LIBUMSKT_SRC} ${UMSKT_EXE_WINDOWS_EXTRA} ${UMSKT_EXE_WINDOWS_DLL})
TARGET_INCLUDE_DIRECTORIES(_umskt PUBLIC ${OPENSSL_INCLUDE_DIR} ${CMAKE_BINARY_DIR})
TARGET_LINK_DIRECTORIES(_umskt PUBLIC ${UMSKT_LINK_DIRS})
TARGET_LINK_LIBRARIES(_umskt ${OPENSSL_CRYPTO_LIBRARIES} fmt ${UMSKT_LINK_LIBS})
TARGET_LINK_LIBRARIES(_umskt PUBLIC ${UMSKT_LINK_LIBS})
IF(MSVC)
SET_TARGET_PROPERTIES(_umskt PROPERTIES OUTPUT_NAME libumskt)
ELSE()
SET_TARGET_PROPERTIES(_umskt PROPERTIES OUTPUT_NAME umskt)
ENDIF()
## umskt_static.a/.lib creation
ADD_LIBRARY(umskt_static ${LIBUMSKT_SRC} ${UMSKT_EXE_WINDOWS_EXTRA} ${UMSKT_EXE_WINDOWS_DLL})
TARGET_INCLUDE_DIRECTORIES(umskt_static PUBLIC ${OPENSSL_INCLUDE_DIR} ${CMAKE_BINARY_DIR})
TARGET_LINK_DIRECTORIES(umskt_static PUBLIC ${UMSKT_LINK_DIRS})
TARGET_LINK_LIBRARIES(umskt_static PUBLIC ${UMSKT_LINK_LIBS})
IF(MSVC)
SET_TARGET_PROPERTIES(umskt_static PROPERTIES OUTPUT_NAME libumskt_static)
ELSE()
SET_TARGET_PROPERTIES(umskt_static PROPERTIES OUTPUT_NAME umskt_static)
ENDIF()
### UMSKT executable compilation
ADD_EXECUTABLE(umskt ${UMSKT_CLI_SRC} ${UMSKT_EXE_WINDOWS_EXTRA})
TARGET_INCLUDE_DIRECTORIES(umskt PUBLIC ${OPENSSL_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(umskt _umskt ${OPENSSL_CRYPTO_LIBRARIES} ${ZLIB_LIBRARIES} fmt nlohmann_json::nlohmann_json umskt::rc ${UMSKT_LINK_LIBS})
TARGET_INCLUDE_DIRECTORIES(umskt PUBLIC ${OPENSSL_INCLUDE_DIR} ${CMAKE_BINARY_DIR})
TARGET_LINK_LIBRARIES(umskt PUBLIC umskt_static ${UMSKT_LINK_LIBS} nlohmann_json::nlohmann_json)
TARGET_LINK_DIRECTORIES(umskt PUBLIC ${UMSKT_LINK_DIRS})
IF (MSVC AND MSVC_MSDOS_STUB)
SET_PROPERTY(TARGET umskt APPEND PROPERTY LINK_FLAGS /STUB:${MSVC_MSDOS_STUB})
IF (MSVC)
SET_PROPERTY(TARGET umskt APPEND PROPERTY COMPILE_FLAGS /DUMSKT_CLI_WINRC_EMBED_JSON)
IF (MSVC_MSDOS_STUB)
SET_PROPERTY(TARGET umskt APPEND PROPERTY LINK_FLAGS /STUB:${MSVC_MSDOS_STUB})
ENDIF()
ENDIF ()
IF (${CMAKE_SYSTEM_NAME} MATCHES "Linux")

View File

@ -47,7 +47,7 @@ The procedure for SLP 2.0 verification is as follows:
4. If the OEM product ID is detected, the OEM verification process starts, and it will check the installed OEM certificate <!-- "product certificate" ? --> is valid. It mainly uses the SLIC public key from BIOS which is loaded into the RAM, to verify the digital signature of product certificate <!-- Yes, this "product certificate" came from nowhere, so I guess this the OEM certificate that installed while installing the Windows Vista by OEM. -->. If validation failed, it is considered as inactivated.
5. Verify the OEM ID field between SLIC and RSDT (Root System Description Table), and compare the OEM ID field and OEM Table ID fields between SLIC and XSDT (Extended System Description Table). If they didn't match, it is considered as inactivated.
5. Validate the OEM ID field between SLIC and RSDT (Root System Description Table), and compare the OEM ID field and OEM Table ID fields between SLIC and XSDT (Extended System Description Table). If they didn't match, it is considered as inactivated.
6. After the above hurdles, the OEM license will be considered as activated. If not then fallback to inactivated and continue with normal procedure, like require user to activate the Windows.
@ -65,9 +65,9 @@ Here is more detailed verification process:
4. Use OEM public key verify against the digital signature of the Marker inside SLIC. if verified (this means the Message in the Marker is correct. <!-- There is no where talked about what is that Message and Marker. -->) then continue on OEM activation process, otherwise continue on WPA activation process.
5. Verify the Windows Logo <!-- I guess? "Windows Logo" part can be directly translate to "Windows Flag Mark", and this "Flag" means that actual waving thing, not a digital bit. I guess this approach is similar as the Nintendo logo bitmap inside Game Boy game cartridge ROM thing. --> inside the Marker. If the Windows Logo is present, then continue on OEM activation process, otherwise continue on WPA activation process.
5. Validate the Windows Logo <!-- I guess? "Windows Logo" part can be directly translate to "Windows Flag Mark", and this "Flag" means that actual waving thing, not a digital bit. I guess this approach is similar as the Nintendo logo bitmap inside Game Boy game cartridge ROM thing. --> inside the Marker. If the Windows Logo is present, then continue on OEM activation process, otherwise continue on WPA activation process.
6. Verify the version of Marker. If it's at least `0x20001` then continue on OEM activation process, otherwise OEM activation failed, and continue on WPA activation process.
6. Validate the version of Marker. If it's at least `0x20001` then continue on OEM activation process, otherwise OEM activation failed, and continue on WPA activation process.
7. Compare the OEM ID and OEM Table ID against all corresponding ACPI table headers. If they match, then OEM activation completed successfully, otherwise continue on WPA activation process.

1682
keys.json

File diff suppressed because it is too large Load Diff

View File

@ -26,11 +26,17 @@
Options CLI::options;
json CLI::keys;
/**
*
* @param argcIn
* @param argvIn
* @return exit status (where success is 0)
*/
BYTE CLI::Init(int argcIn, char **argvIn)
{
// set default options
options = {argcIn, argvIn, "", "", "", "", "", "", "WINXPPVLK", 0,
0, 1, false, false, false, false, false, false, false, STATE_BINK1998_GENERATE};
options = {argcIn, argvIn, "2E", "", "", "", "", "", "WINXP", "PROVLK", 0,
0, 1, false, false, false, false, false, false, false, PIDGEN_3, STATE_PIDGEN_GENERATE};
SetHelpText();
@ -40,6 +46,13 @@ BYTE CLI::Init(int argcIn, char **argvIn)
return options.error;
}
// if we displayed help, without an error
// return success
if (options.help)
{
return -1;
}
success = processOptions();
if (!success)
{
@ -56,28 +69,46 @@ BYTE CLI::Init(int argcIn, char **argvIn)
*/
BOOL CLI::loadJSON(const fs::path &filename)
{
if (!filename.empty() && !fs::exists(filename))
if (filename.empty())
{
if (options.verbose)
{
fmt::print("Loading internal keys file\n");
}
auto retval = loadEmbeddedJSON();
if (retval && options.verbose)
{
fmt::print("Loaded internal keys file successfully\n\n");
}
else if (!retval)
{
fmt::print("Error loading internal keys file...\n\n");
}
return retval;
}
if (!fs::exists(filename))
{
fmt::print("ERROR: File {} does not exist\n", filename.string());
return false;
}
else if (fs::exists(filename))
if (options.verbose)
{
std::ifstream f(filename);
try
{
keys = json::parse(f, nullptr, false, false);
}
catch (const json::exception &e)
{
fmt::print("ERROR: Exception thrown while parsing {}: {}\n", filename.string(), e.what());
}
fmt::print("Loading keys file {}\n", options.keysFilename);
}
else if (filename.empty())
std::ifstream f(filename);
try
{
cmrc::embedded_filesystem fs = cmrc::umskt::get_filesystem();
cmrc::file jsonFile = fs.open("keys.json");
keys = json::parse(jsonFile, nullptr, false, false);
keys = json::parse(f, nullptr, false, false);
}
catch (const json::exception &e)
{
fmt::print("ERROR: Exception thrown while parsing {}: {}\n", filename.string(), e.what());
return false;
}
if (keys.is_discarded())
@ -86,215 +117,54 @@ BOOL CLI::loadJSON(const fs::path &filename)
return false;
}
if (options.verbose)
{
fmt::print("Loaded keys from {} successfully\n", options.keysFilename);
}
return true;
}
/**
*
* @return success
* @param pid
*/
BOOL CLI::processOptions()
{
if (options.verbose)
{
if (options.keysFilename.empty())
{
fmt::print("Loading internal keys file\n");
}
else
{
fmt::print("Loading keys file {}\n", options.keysFilename);
}
}
if (!loadJSON(options.keysFilename))
{
options.error = true;
return false;
}
if (options.verbose)
{
if (options.keysFilename.empty())
{
fmt::print("Loaded internal keys file successfully\n");
}
else
{
fmt::print("Loaded keys from {} successfully\n", options.keysFilename);
}
}
if (options.list)
{
for (auto const &i : keys["Products"].items())
{
auto el = i.value();
int id;
sscanf(&(el["BINK"][0]).get<std::string>()[0], "%x", &id);
fmt::print("{}\n\tName: {}\n\tBINKs: ", i.key(), el["Name"].get<std::string>());
std::cout << el["BINK"] << std::endl << std::endl;
}
fmt::print("\n");
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);
// FE and FF are BINK 1998, but do not generate valid keys, so we throw an error
if (intBinkID >= 0xFE)
{
fmt::print("ERROR: Terminal Services BINKs (FE and FF) are unsupported at this time\n");
return false;
}
if (intBinkID >= 0x40)
{
// set bink2002 validate mode if in bink1998 validate mode, else set bink2002 generate mode
options.state = (options.state == STATE_BINK1998_VALIDATE) ? STATE_BINK2002_VALIDATE : STATE_BINK2002_GENERATE;
}
return true;
}
void CLI::printID(DWORD *pid)
{
char raw[12], b[6], c[8];
char i, digit = 0;
// Convert PID to ascii-number (=raw)
snprintf(raw, sizeof(raw), "%09u", pid[0]);
snprintf(raw, sizeof(raw), "%09lu", pid[0]);
// Make b-part {640-....}
strncpy(b, raw, 3);
_strncpy(b, 6, &raw[0], 3);
b[3] = 0;
// Make c-part {...-123456X...}
strcpy(c, raw + 3);
_strcpy(c, &raw[3]);
// Make checksum digit-part {...56X-}
assert(strlen(c) == 6);
for (i = 0; i < 6; i++)
{
digit -= c[i] - '0'; // Sum digits
digit += c[i] - '0'; // Sum digits
}
while (digit < 0)
digit %= 7;
if (digit > 0)
{
digit += 7;
digit = 7 - digit;
}
c[6] = digit + '0';
c[7] = 0;
fmt::print("> Product ID: PPPPP-{}-{}-BBxxx\n", b, c);
}
DWORD binkid;
_sscanf(options.binkID.c_str(), "%lx", &binkid);
binkid /= 2;
/**
*
* @param pk
*/
void CLI::printKey(std::string &pk)
{
assert(pk.length() >= PK_LENGTH);
fmt::print("{}-{}-{}-{}-{}", pk.substr(0, 5), pk.substr(5, 5), pk.substr(10, 5), pk.substr(15, 5),
pk.substr(20, 5));
}
/**
*
* @param in_key
* @param out_key
* @return
*/
BOOL CLI::stripKey(const std::string &in_key, std::string &out_key)
{
// copy out the product key stripping out extraneous characters
const char *p = &in_key[0];
BYTE i = 0;
for (; *p; p++)
{
// strip out space or dash
if (*p == ' ' || *p == '-')
{
continue;
}
// check if we've passed the product key length to avoid overflow
if (i >= PK_LENGTH)
{
return false;
}
// convert to uppercase - if character allowed, copy into array
for (int j = 0; j < strlen(PIDGEN3::pKeyCharset); j++)
{
if (toupper(*p) == PIDGEN3::pKeyCharset[j])
{
out_key[i++] = toupper(*p);
continue;
}
}
}
// only return true if we've handled exactly PK_LENGTH chars
return (i == PK_LENGTH);
fmt::print("> Product ID: PPPPP-{}-{}-{}xxx\n", b, c, binkid);
}
/**
@ -302,32 +172,35 @@ BOOL CLI::stripKey(const std::string &in_key, std::string &out_key)
* @param pidgen3
* @return success
*/
BOOL CLI::InitPIDGEN3(PIDGEN3 *pidgen3)
BOOL CLI::InitPIDGEN3(PIDGEN3 &pidgen3)
{
const char *BINKID = &options.binkid[0];
const char *BINKID = &options.binkID[0];
auto bink = keys["BINK"][BINKID];
if (options.verbose)
{
fmt::print("----------------------------------------------------------- \n");
fmt::print("{:->80}\n", "");
fmt::print("Loaded the following elliptic curve parameters: BINK[{}]\n", BINKID);
fmt::print("----------------------------------------------------------- \n");
fmt::print(" P: {}\n", bink["p"].get<std::string>());
fmt::print(" a: {}\n", bink["a"].get<std::string>());
fmt::print(" b: {}\n", bink["b"].get<std::string>());
fmt::print("Gx: {}\n", bink["g"]["x"].get<std::string>());
fmt::print("Gy: {}\n", bink["g"]["y"].get<std::string>());
fmt::print("Kx: {}\n", bink["pub"]["x"].get<std::string>());
fmt::print("Ky: {}\n", bink["pub"]["y"].get<std::string>());
fmt::print(" n: {}\n", bink["n"].get<std::string>());
fmt::print(" k: {}\n", bink["priv"].get<std::string>());
fmt::print("{:->80}\n", "");
fmt::print("{:>6}: {}\n", "P", bink["p"]);
fmt::print("{:>6}: {}\n", "a", bink["a"]);
fmt::print("{:>6}: {}\n", "b", bink["b"]);
fmt::print("{:>6}: [{},\n{:>9}{}]\n", "G[x,y]", bink["g"]["x"], "", bink["g"]["y"]);
fmt::print("{:>6}: [{},\n{:>9}{}]\n", "K[x,y]", bink["pub"]["x"], "", bink["pub"]["y"]);
fmt::print("{:>6}: {}\n", "n", bink["n"]);
fmt::print("{:>6}: {}\n", "k", bink["priv"]);
fmt::print("\n");
}
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.LoadEllipticCurve(bink["p"], bink["a"], bink["b"], bink["g"]["x"], bink["g"]["y"], bink["pub"]["x"],
bink["pub"]["y"], bink["n"], bink["priv"]);
options.info.setChannelID(options.channelID);
if (options.state != STATE_PIDGEN_GENERATE)
{
return true;
}
pidgen3.info.setChannelID(options.channelID);
if (options.verbose)
{
fmt::print("> Channel ID: {:03d}\n", options.channelID);
@ -335,7 +208,7 @@ BOOL CLI::InitPIDGEN3(PIDGEN3 *pidgen3)
if (options.serialSet)
{
options.info.setSerial(options.serial);
pidgen3.info.setSerial(options.serial);
if (options.verbose)
{
fmt::print("> Serial {:#09d}\n", options.serial);
@ -350,270 +223,158 @@ BOOL CLI::InitPIDGEN3(PIDGEN3 *pidgen3)
* @param confid
* @return success
*/
BOOL CLI::InitConfirmationID(ConfirmationID *confid)
BOOL CLI::InitConfirmationID(ConfirmationID &confid)
{
return true;
auto ctx = BN_CTX_new();
BIGNUM *pkey = BN_CTX_get(ctx), *nonresidue = BN_CTX_get(ctx), *modulous = BN_CTX_get(ctx);
BIGNUM *fvals[6];
QWORD fvalsq[6];
if (!keys["products"][options.productCode].contains("meta") ||
!keys["products"][options.productCode]["meta"].contains("activation"))
{
fmt::print("ERROR: product flavour {} does not have known activation values", options.productCode);
return false;
}
auto meta = keys["products"][options.productCode]["meta"]["activation"];
if (!keys["activation"].contains(meta["flavour"]))
{
fmt::print("ERROR: {} is an unknown activation flavour", meta["flavour"]);
return false;
}
auto flavour = keys["activation"][meta["flavour"]];
if (options.verbose)
{
fmt::print("{:->80}\n", "");
fmt::print("Loaded the following hyperelliptic curve parameters: activation[{}]\n", meta["flavour"]);
fmt::print("{:->80}\n", "");
fmt::print("{:>7}: {}\n", "name", flavour["name"]);
fmt::print("{:>7}: {}\n", "version", meta["version"]);
fmt::print("{:>7}: {}\n", "Fp", flavour["p"]);
fmt::print("{:>7}: [{}, {}, {},\n{:>10}{}, {}, {}]\n", "F[]", flavour["x"]["0"], flavour["x"]["1"],
flavour["x"]["2"], "", flavour["x"]["3"], flavour["x"]["4"], flavour["x"]["5"]);
fmt::print("{:>7}: {}\n", "INV", flavour["quotient"]);
fmt::print("{:>7}: {}\n", "mqnr", flavour["non_residue"]);
fmt::print("{:>7}: {}\n", "k", flavour["priv"]);
fmt::print("{:>7}: {}\n", "IID", flavour["iid_key"]);
fmt::print("\n");
}
for (BYTE i = 0; i < 6; i++)
{
fvals[i] = BN_CTX_get(ctx);
auto xval = flavour["x"][fmt::format("{}", i)].get<std::string>();
BN_dec2bn(&fvals[i], xval.c_str());
UMSKT::BN_bn2lebin(fvals[i], (unsigned char *)&fvalsq[i], sizeof(*fvalsq));
}
// confid.LoadHyperellipticCurve(fvals, );
BN_CTX_free(ctx);
return false;
}
/**
*
* @return success
*/
BOOL CLI::BINK1998Generate()
BOOL CLI::PIDGENGenerate()
{
auto bink1998 = BINK1998();
BOOL retval = InitPIDGEN3(&bink1998);
if (!retval)
{
return retval;
}
// TODO:
// if options.pidgen2generate
// return pidgen2generate
// otherwise...
// raw PID/serial value
DWORD nRaw = options.channelID * 1'000'000;
DWORD serialRnd;
const char *BINKID = &options.binkID[0];
auto bink = keys["BINK"][BINKID];
if (options.serialSet)
std::string key;
bink["p"].get_to(key);
if (PIDGEN3::checkFieldStrIsBink1998(key))
{
// using user-provided serial
serialRnd = options.serial;
if (options.verbose)
{
fmt::print("Detected a BINK1998 key\n");
}
auto bink1998 = BINK1998();
InitPIDGEN3(bink1998);
return BINK1998Generate(bink1998);
}
else
{
// generate a random number to use as a serial
serialRnd = UMSKT::getRandom<DWORD>();
}
// make sure it's less than 999999
nRaw += (serialRnd % 999999);
if (options.verbose)
{
// print the resulting Product ID
// PID value is printed in BINK1998::Generate
printID(&nRaw);
}
for (int i = 0; i < total; i++)
{
options.info.setSerial(nRaw);
bink1998.Generate(options.info, pKey);
bool isValid = bink1998.Verify(pKey);
if (isValid)
{
printKey(pKey);
if (i <= total - 1 || options.verbose)
{
fmt::print("\n");
}
count += isValid;
}
else
{
if (options.verbose)
{
printKey(pKey);
fmt::print(" [Invalid]");
if (i <= total - 1)
{
fmt::print("\n");
}
}
total++; // queue a redo, basically
}
}
if (options.verbose)
{
fmt::print("\nSuccess count: {}/{}\n", count, total);
}
return true;
}
/**
*
* @return success
*/
BOOL CLI::BINK2002Generate()
{
auto bink2002 = BINK2002();
InitPIDGEN3(&bink2002);
// generate a key
for (int i = 0; i < total; i++)
{
options.info.AuthInfo = UMSKT::getRandom<DWORD>() & BITMASK(10);
if (options.verbose)
{
fmt::print("> AuthInfo: {:#08x}\n", options.info.AuthInfo);
fmt::print("Detected a BINK2002 key\n");
}
auto bink2002 = BINK2002();
InitPIDGEN3(bink2002);
return BINK2002Generate(bink2002);
}
}
bink2002.Generate(options.info, pKey);
/**
*
* @return isValid
*/
BOOL CLI::PIDGENValidate()
{
// TODO:
// if options.pidgen2validate
// return pidgen2validate
// otherwise...
bool isValid = bink2002.Verify(pKey);
if (isValid)
const char *BINKID = &options.binkID[0];
auto bink = keys["BINK"][BINKID];
std::string key;
bink["p"].get_to(key);
if (PIDGEN3::checkFieldStrIsBink1998(key))
{
if (options.verbose)
{
CLI::printKey(pKey);
if (i <= total - 1 || options.verbose)
{ // check if end of list or verbose
fmt::print("\n");
}
count += isValid; // add to count
fmt::print("Detected a BINK1998 key\n");
}
else
auto bink1998 = BINK1998();
InitPIDGEN3(bink1998);
return BINK1998Validate(bink1998);
}
else
{
if (options.verbose)
{
if (options.verbose)
{
CLI::printKey(pKey); // print the key
fmt::print(" [Invalid]"); // and add " [Invalid]" to the key
if (i <= total - 1)
{ // check if end of list
fmt::print("\n");
}
}
total++; // queue a redo, basically
fmt::print("Detected a BINK2002 key\n");
}
auto bink2002 = BINK2002();
InitPIDGEN3(bink2002);
return BINK2002Validate(bink2002);
}
if (options.verbose)
{
fmt::print("\nSuccess count: {}/{}\n", count, total);
}
return true;
}
/**
*
* @return success
*/
BOOL CLI::BINK1998Validate()
{
auto bink1998 = BINK1998();
InitPIDGEN3(&bink1998);
std::string product_key;
if (!CLI::stripKey(options.keyToCheck, product_key))
{
fmt::print("ERROR: Product key is in an incorrect format!\n");
return false;
}
CLI::printKey(product_key);
fmt::print("\n");
if (!bink1998.Verify(product_key))
{
fmt::print("ERROR: Product key is invalid! Wrong BINK ID?\n");
return false;
}
fmt::print("Key validated successfully!\n");
return true;
}
/**
*
* @return success
*/
BOOL CLI::BINK2002Validate()
{
auto bink2002 = BINK2002();
InitPIDGEN3(&bink2002);
std::string product_key;
if (!CLI::stripKey(options.keyToCheck, product_key))
{
fmt::print("ERROR: Product key is in an incorrect format!\n");
return false;
}
CLI::printKey(product_key);
fmt::print("\n");
if (!bink2002.Verify(product_key))
{
fmt::print("ERROR: Product key is invalid! Wrong BINK ID?\n");
return false;
}
fmt::print("Key validated successfully!\n");
return true;
}
/**
*
* @return success
*/
BOOL CLI::ConfirmationIDGenerate()
{
std::string confirmation_id;
auto confid = ConfirmationID();
int err = confid.Generate(options.instid, confirmation_id, options.productid);
if (err == SUCCESS)
{
fmt::print("{}\n", confirmation_id);
return true;
}
switch (err)
{
case ERR_TOO_SHORT:
fmt::print("ERROR: Installation ID is too short.\n");
break;
case ERR_TOO_LARGE:
fmt::print("ERROR: Installation ID is too long.\n");
break;
case ERR_INVALID_CHARACTER:
fmt::print("ERROR: Invalid character in installation ID.\n");
break;
case ERR_INVALID_CHECK_DIGIT:
fmt::print("ERROR: Installation ID checksum failed. Please check that it is typed correctly.\n");
break;
case ERR_UNKNOWN_VERSION:
fmt::print("ERROR: Unknown installation ID version.\n");
break;
case ERR_UNLUCKY:
fmt::print("ERROR: Unable to generate valid confirmation ID.\n");
break;
default:
fmt::print("Unknown error occurred during Confirmation ID generation: {}\n", err);
break;
}
return false;
}
/**
* Process application state
*
* @return application status code
*/
int CLI::Run()
{
BINK1998Generate();
/*
switch (state)
/**
* TODO: we should be checking if the system's pseudorandom facilities work
* before attempting generation, validation does not require entropy
*/
switch (options.state)
{
case STATE_BINK1998_GENERATE:
return BINK1998Generate();
case STATE_PIDGEN_GENERATE:
return PIDGENGenerate();
case STATE_BINK2002_GENERATE:
return BINK2002Generate();
case STATE_BINK1998_VALIDATE:
return BINK1998Validate();
case STATE_BINK2002_VALIDATE:
return BINK2002Validate();
case STATE_PIDGEN_VALIDATE:
return PIDGENValidate();
case STATE_CONFIRMATION_ID:
return ConfirmationIDGenerate();
@ -621,6 +382,51 @@ int CLI::Run()
default:
return 1;
}
*/
return 0;
}
/**
* Prints a product key to stdout
*
* @param pk std::string to print
*/
void CLI::printKey(std::string &pk)
{
assert(pk.length() >= PK_LENGTH);
fmt::print("{}-{}-{}-{}-{}", pk.substr(0, 5), pk.substr(5, 5), pk.substr(10, 5), pk.substr(15, 5),
pk.substr(20, 5));
}
/**
* std::BinaryOperation compatible accumulator for validating/stripping an input string against the PIDGEN3 charset
* this can be moved to the PIDGEN3 at a later date
*
* @param accumulator
* @param currentChar
* @return
*/
std::string CLI::validateInputKeyCharset(std::string &accumulator, char currentChar)
{
char cchar = ::toupper(currentChar);
if (std::find(std::begin(PIDGEN3::pKeyCharset), std::end(PIDGEN3::pKeyCharset), cchar) !=
std::end(PIDGEN3::pKeyCharset))
{
accumulator.push_back(cchar);
}
return accumulator;
}
/**
*
* @param in_key
* @param out_key
* @return
*/
BOOL CLI::stripKey(const std::string &in_key, std::string &out_key)
{
// copy out the product key stripping out extraneous characters
out_key = std::accumulate(in_key.begin(), in_key.end(), std::string(), validateInputKeyCharset);
// only return true if we've handled exactly PK_LENGTH chars
return (out_key.length() == PK_LENGTH);
}

View File

@ -32,30 +32,54 @@
#include <unordered_map>
#include <vector>
#include <cmrc/cmrc.hpp>
#include <fmt/color.h>
#include <fmt/core.h>
#include <fmt/ostream.h>
#include <fmt/ranges.h>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
namespace fs = std::filesystem;
// fmt <-> json linkage
template <> struct fmt::formatter<json> : ostream_formatter
{
auto format(const json &j, format_context &ctx) const
{
if (j.is_string())
{
return formatter<string_view>::format(j.get<std::string>(), ctx);
}
else
{
return basic_ostream_formatter<char>::format(j, ctx);
}
}
};
#include "help.h"
#include "libumskt/confid/confid.h"
#include "libumskt/libumskt.h"
#include "libumskt/pidgen2/PIDGEN2.h"
#include "libumskt/pidgen3/BINK1998.h"
#include "libumskt/pidgen3/BINK2002.h"
#include "libumskt/pidgen3/PIDGEN3.h"
#include "options.h"
CMRC_DECLARE(umskt);
#ifndef UMSKTCLI_VERSION_STRING
#define UMSKTCLI_VERSION_STRING "unknown version-dirty"
#endif
enum APPLICATION_STATE
{
STATE_BINK1998_GENERATE,
STATE_BINK2002_GENERATE,
STATE_CONFIRMATION_ID,
STATE_BINK1998_VALIDATE,
STATE_BINK2002_VALIDATE
STATE_PIDGEN_GENERATE,
STATE_PIDGEN_VALIDATE,
STATE_CONFIRMATION_ID
};
enum PIDGEN_VERSION
{
PIDGEN_2 = 2,
PIDGEN_3 = 3,
};
struct Options
@ -63,17 +87,19 @@ struct Options
int argc;
char **argv;
std::string binkid;
std::string binkID;
std::string keysFilename;
std::string instid;
std::string installationID;
std::string keyToCheck;
std::string productid;
std::string productID;
std::string authInfo;
std::string productCode;
std::string productFlavour;
DWORD channelID;
DWORD serial;
DWORD numKeys;
BOOL oem;
BOOL upgrade;
BOOL serialSet;
@ -82,15 +108,27 @@ struct Options
BOOL error;
BOOL list;
struct Meta
{
std::string type;
std::vector<std::string> tags;
struct Activation
{
std::string flavour;
int version;
};
};
PIDGEN3::KeyInfo info;
PIDGEN_VERSION pidgenversion;
APPLICATION_STATE state;
};
class CLI
{
std::string pKey;
DWORD count, total;
DWORD count, total, iBinkID;
static Options options;
@ -108,6 +146,7 @@ class CLI
static void SetHelpText();
static BOOL loadJSON(const fs::path &filename);
static BOOL loadEmbeddedJSON();
static CLIHandlerFunc DisplayHelp;
static CLIHandlerFunc DisplayErrorMessage;
@ -125,22 +164,43 @@ class CLI
static CLIHandlerFunc SetProductIDOption;
static CLIHandlerFunc SetValidateOption;
static CLIHandlerFunc SetProductCodeOption;
static CLIHandlerFunc SetFlavourOption;
static BOOL parseCommandLine();
static BOOL processOptions();
static void printID(DWORD *pid);
static void printKey(std::string &pk);
static BOOL stripKey(const std::string &in_key, std::string &out_key);
static std::string validateInputKeyCharset(std::string &accumulator, char currentChar);
BOOL InitPIDGEN3(PIDGEN3 *pidgen3);
BOOL InitConfirmationID(ConfirmationID *confid);
BOOL InitPIDGEN3(PIDGEN3 &pidgen3);
BOOL InitConfirmationID(ConfirmationID &confid);
BOOL BINK1998Generate();
BOOL BINK2002Generate();
BOOL BINK1998Validate();
BOOL BINK2002Validate();
BOOL PIDGENGenerate();
BOOL PIDGENValidate();
BOOL PIDGEN2Generate(PIDGEN2 &pidgen2);
BOOL PIDGEN2Validate(PIDGEN2 &pidgen2);
BOOL BINK1998Generate(PIDGEN3 &pidgen3);
BOOL BINK1998Validate(PIDGEN3 &pidgen3);
BOOL BINK2002Generate(PIDGEN3 &pidgen3);
BOOL BINK2002Validate(PIDGEN3 &pidgen3);
BOOL ConfirmationIDGenerate();
INLINE static std::string strtolower(std::string &in)
{
auto retval = std::string(in);
std::transform(retval.begin(), retval.end(), retval.begin(), ::tolower);
return retval;
}
INLINE static std::string strtoupper(std::string &in)
{
auto retval = std::string(in);
std::transform(retval.begin(), retval.end(), retval.begin(), ::toupper);
return retval;
}
int Run();
};

271
src/generate.cpp Normal file
View File

@ -0,0 +1,271 @@
/**
* This file is a part of the UMSKT Project
*
* Copyleft (C) 2019-2024 UMSKT Contributors (et.al.)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @FileCreated by Neo on 01/05/2024
* @Maintainer Neo
*/
#include "cli.h"
/**
*
* @param pidgen2
* @return success
*/
BOOL CLI::PIDGEN2Generate(PIDGEN2 &pidgen2)
{
return true;
}
/**
*
* @param pidgen2
* @return success
*/
BOOL CLI::PIDGEN2Validate(PIDGEN2 &pidgen2)
{
return true;
}
/**
*
* @return success
*/
BOOL CLI::BINK1998Generate(PIDGEN3 &pidgen3)
{
// raw PID/serial value
DWORD nRaw = options.channelID * 1'000'000;
DWORD serialRnd;
if (options.serialSet)
{
// using user-provided serial
serialRnd = options.serial;
}
else
{
// generate a random number to use as a serial
serialRnd = UMSKT::getRandom<DWORD>();
}
// make sure it's less than 999999
nRaw += (serialRnd % 999999);
if (options.verbose)
{
// print the resulting Product ID
// PID value is printed in BINK1998::Generate
printID(&nRaw);
}
for (int i = 0; i < total; i++)
{
pidgen3.info.setSerial(nRaw);
pidgen3.Generate(pKey);
bool isValid = pidgen3.Validate(pKey);
if (isValid)
{
printKey(pKey);
if (i <= total - 1 || options.verbose)
{
fmt::print("\n");
}
count += isValid;
}
else
{
if (options.verbose)
{
printKey(pKey);
fmt::print(" [Invalid]");
if (i <= total - 1)
{
fmt::print("\n");
}
}
total++; // queue a redo, basically
}
}
if (options.verbose)
{
fmt::print("\nSuccess count: {}/{}\n", count, total);
}
return true;
}
/**
*
* @return success
*/
BOOL CLI::BINK1998Validate(PIDGEN3 &bink1998)
{
std::string product_key;
if (!CLI::stripKey(options.keyToCheck, product_key))
{
fmt::print("ERROR: Product key is in an incorrect format!\n");
return false;
}
CLI::printKey(product_key);
fmt::print("\n");
if (!bink1998.Validate(product_key))
{
fmt::print("ERROR: Product key is invalid! Wrong BINK ID?\n");
return false;
}
fmt::print("Key validated successfully!\n");
return true;
}
/**
*
* @return success
*/
BOOL CLI::BINK2002Generate(PIDGEN3 &pidgen3)
{
// generate a key
for (int i = 0; i < total; i++)
{
pidgen3.info.AuthInfo = UMSKT::getRandom<DWORD>() & BITMASK(10);
if (options.verbose)
{
fmt::print("> AuthInfo: {:#08x}\n", pidgen3.info.AuthInfo);
}
pidgen3.Generate(pKey);
bool isValid = pidgen3.Validate(pKey);
if (isValid)
{
CLI::printKey(pKey);
if (i <= total - 1 || options.verbose)
{ // check if end of list or verbose
fmt::print("\n");
}
count += isValid; // add to count
}
else
{
if (options.verbose)
{
CLI::printKey(pKey); // print the key
fmt::print(" [Invalid]"); // and add " [Invalid]" to the key
if (i <= total - 1)
{ // check if end of list
fmt::print("\n");
}
}
total++; // queue a redo, basically
}
}
if (options.verbose)
{
fmt::print("\nSuccess count: {}/{}\n", count, total);
}
return true;
}
/**
*
* @return success
*/
BOOL CLI::BINK2002Validate(PIDGEN3 &pidgen3)
{
std::string product_key;
if (!CLI::stripKey(options.keyToCheck, product_key))
{
fmt::print("ERROR: Product key is in an incorrect format!\n");
return false;
}
CLI::printKey(product_key);
fmt::print("\n");
if (!pidgen3.Validate(product_key))
{
fmt::print("ERROR: Product key is invalid! Wrong BINK ID?\n");
return false;
}
fmt::print("Key validated successfully!\n");
return true;
}
/**
*
* @return success
*/
BOOL CLI::ConfirmationIDGenerate()
{
auto confid = ConfirmationID();
std::string confirmation_id;
if (!InitConfirmationID(confid))
{
return false;
}
DWORD err = confid.Generate(options.installationID, confirmation_id, options.productID);
if (err == SUCCESS)
{
fmt::print("{}\n", confirmation_id);
return true;
}
switch (err)
{
case ERR_TOO_SHORT:
fmt::print("ERROR: Installation ID is too short.\n");
break;
case ERR_TOO_LARGE:
fmt::print("ERROR: Installation ID is too long.\n");
break;
case ERR_INVALID_CHARACTER:
fmt::print("ERROR: Invalid character in installation ID.\n");
break;
case ERR_INVALID_CHECK_DIGIT:
fmt::print("ERROR: Installation ID checksum failed. Please check that it is typed correctly.\n");
break;
case ERR_UNKNOWN_VERSION:
fmt::print("ERROR: Unknown installation ID version.\n");
break;
case ERR_UNLUCKY:
fmt::print("ERROR: Unable to generate valid confirmation ID.\n");
break;
default:
fmt::print("Unknown error occurred during Confirmation ID generation: {}\n", err);
break;
}
return false;
}

595
src/help.cpp Normal file
View File

@ -0,0 +1,595 @@
/**
* This file is a part of the UMSKT Project
*
* Copyleft (C) 2019-2024 UMSKT Contributors (et.al.)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @FileCreated by Neo on 01/02/2024
* @Maintainer Neo
*/
#include "cli.h"
// define static storage
std::array<CLIHelpOptions, CLIHelpOptionID_END> CLI::helpOptions;
void CLI::SetHelpText()
{
helpOptions[OPTION_HELP] = {"h", "help", "show this help text", false, "", &DisplayHelp};
helpOptions[OPTION_HELP2] = {"?", "", "show this help text", false, "", &DisplayHelp};
helpOptions[OPTION_VERSION] = {"", "version", "show UMSKT CLI / libumskt versions", false, "", nullptr};
helpOptions[OPTION_VERBOSE] = {"v", "verbose", "enable verbose output", false, "", &SetVerboseOption};
helpOptions[OPTION_DEBUG] = {"d", "debug", "enable debug output", false, "", &SetDebugOption};
helpOptions[OPTION_FILE] = {
"F", "file", "(advanced) specify which keys JSON file to load", true, "[embedded file]", &SetFileOption};
helpOptions[OPTION_LIST] = {"l", "list", "list supported products", false, "", &SetListOption};
helpOptions[OPTION_PRODUCT] = {"p", "product", "[REQUIRED] which product to generate keys for",
true, options.productCode, &SetProductCodeOption};
helpOptions[OPTION_FLAVOUR] = {
"f", "flavour", "which product flavour to generate keys for (required in some instances)",
true, "", &SetFlavourOption};
helpOptions[OPTION_NUMBER] = {"n", "number", "(PIDGEN only) number of keys to generate",
true, "1", &SetNumberOption};
helpOptions[OPTION_ACTIVATIONID] = {
"i", "installationID", "(activation only) installation ID used to generate confirmation ID", true,
"", &SetActivationIDOption};
helpOptions[OPTION_ACTIVATIONPID] = {
"P", "productID", "(Office activation only) product ID to generate confirmation ID for",
true, "", &SetProductIDOption};
helpOptions[OPTION_OEM] = {"O", "oem", "(PIDGEN) generate an OEM key", false, "", &SetOEMOption};
helpOptions[OPTION_UPGRADE] = {"U", "upgrade", "(PIDGEN 3 only) generate an upgrade key",
false, "", &SetUpgradeOption};
helpOptions[OPTION_BINK] = {"b", "binkID", "(advanced) override which BINK identifier to load",
true, "", &SetBINKOption};
helpOptions[OPTION_CHANNELID] = {"c", "channelid", "(advanced) override which product channel to use",
true, "", &SetChannelIDOption};
helpOptions[OPTION_SERIAL] = {
"s", "serial", "(advanced, PIDGEN 2/3 [BINK 1998] only) specify a serial to generate",
true, "", &SetSerialOption};
helpOptions[OPTION_AUTHDATA] = {
"a", "authdata", "(advanced, PIDGEN 3 [BINK 2002] only) specify a value for the authentication data field",
true, "", nullptr};
helpOptions[OPTION_VALIDATE] = {
"V", "validate", "validate a specified product ID against known BINKs and algorithms",
true, "", &SetValidateOption};
}
/**
*
* @return success
*/
BOOL CLI::parseCommandLine()
{
for (DWORD i = 1; i < options.argc; i++)
{
std::string arg = options.argv[i];
if (arg[0] == '-' || arg[0] == '/')
{
arg.erase(0, 1 + (arg[1] == '-' ? 1 : 0));
}
if (arg.empty())
{
continue;
}
for (BYTE j = 0; j < CLIHelpOptionID_END; j++)
{
auto thisOption = helpOptions[j];
if (arg != thisOption.Short && arg != thisOption.Long)
{
continue;
}
std::string nextarg;
if (thisOption.hasArguments)
{
if (i == options.argc - 1)
{
options.error = true;
goto CommandLineParseEnd;
}
else
{
i++;
nextarg = std::string(options.argv[i]);
}
}
if (thisOption.handler == nullptr)
{
// prevent accidental segmentation faults
continue;
}
auto success = thisOption.handler(1, &nextarg[0]);
if (!success)
{
options.error = true;
goto CommandLineParseEnd;
}
if (options.help)
{
goto CommandLineParseEnd;
}
goto ParseNextCommandLineOption;
}
fmt::print("unknown option: {}\n", arg);
options.error = true;
goto CommandLineParseEnd;
ParseNextCommandLineOption:
continue;
}
CommandLineParseEnd:
if (options.error)
{
DisplayErrorMessage(0, nullptr);
}
return !options.error;
}
/**
*
* @return success
*/
BOOL CLI::processOptions()
{
if (!loadJSON(options.keysFilename))
{
options.error = true;
return false;
}
if (options.list)
{
// the following code is absolutely unhinged
// I'm so sorry
#if defined(__UNICODE__) || defined(__GNUC__)
auto *leaf = "\u251C", *last = "\u2514", *line = "\u2500";
#else
auto *leaf = "\xC3", *last = "\xC0", *line = "\xC4";
#endif
fmt::print("Listing known products and flavours: \n\n");
fmt::print("* The following product list uses this style of formatting:\n");
fmt::print("{}: {} \n", fmt::styled("PRODUCT", fmt::emphasis::bold), "Product name");
fmt::print("{}{}{} {}: {} \n", last, line, line, "FLAVOUR", "Flavour name");
fmt::print("* Products that require a flavour are noted with {}\n\n",
fmt::styled("(no default)", fmt::emphasis::bold));
for (auto const &i : keys["products"].items())
{
auto el = i.value();
auto containsFlavours = el.contains("flavours");
fmt::print("{:<9} {} ", fmt::styled(fmt::format("{}:", i.key()), fmt::emphasis::bold), el["name"]);
if (el.contains("BINK"))
{
fmt::print("{}\n", el["BINK"]);
}
else if (el["meta"].contains("default"))
{
fmt::print("(default: {} {})\n", fmt::styled(el["meta"]["default"], fmt::emphasis::bold),
el["flavours"][el["meta"]["default"]]["BINK"]);
}
else if (el["meta"]["type"].get<std::string>() == "PIDGEN3")
{
fmt::print("[{}]\n", el["meta"]["type"]);
}
else
{
fmt::print("{}\n", fmt::styled("(no default)", fmt::emphasis::bold));
}
if (containsFlavours)
{
auto flavours = el["flavours"];
for (auto j = flavours.begin(); j != flavours.end(); j++)
{
auto el2 = j.value();
BOOL isLast = j == --flavours.end();
fmt::print("{}{}{} {:<9} {} ", !isLast ? leaf : last, line, line, fmt::format("{}:", j.key()),
fmt::format("{} {}", el["name"], el2["name"]));
if (el2.contains("meta") && el2["meta"].contains("type"))
{
fmt::print("[{}]\n", el2["meta"]["type"]);
}
else
{
fmt::print("{}\n", el2["BINK"]);
}
}
}
fmt::print("\n");
}
return false;
}
if (options.productCode.empty())
{
fmt::print("ERROR: product code is required. Exiting...");
DisplayHelp(0, nullptr);
return false;
}
const char *productCode = &options.productCode[0];
if (!keys["products"].contains(productCode))
{
fmt::print("ERROR: Product {} is unknown", productCode);
return false;
}
auto product = keys["products"][productCode];
if (options.verbose)
{
fmt::print("Selecting product: {}\n", productCode);
}
json flavour;
if (product.contains("flavours"))
{
flavour = product["flavours"][options.productFlavour];
if (options.verbose)
{
fmt::print("Selecting flavour: {}\n", options.productFlavour);
}
}
else
{
flavour = product;
}
if (options.state != STATE_PIDGEN_GENERATE && options.state != STATE_PIDGEN_VALIDATE)
{
// exit early if we're not doing PIDGEN
goto processOptionsExitEarly;
}
if (options.oem)
{
flavour["BINK"][1].get_to(options.binkID);
}
else
{
flavour["BINK"][0].get_to(options.binkID);
}
if (options.verbose)
{
fmt::print("Selected BINK: {}\n", options.binkID);
}
if (options.state != STATE_PIDGEN_GENERATE)
{
// exit early if we're only validating
goto processOptionsExitEarly;
}
if (flavour.contains("DPC") && flavour["DPC"].contains(options.binkID) && options.channelID == 0)
{
std::vector<json> filtered;
for (auto const &i : flavour["DPC"][options.binkID].items())
{
auto el = i.value();
if (!el["isEvaluation"].get<bool>())
{
filtered.emplace_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>();
if (min == max)
{
options.channelID = min;
}
else
{
options.channelID = min + (rand % (max - min));
}
if (options.verbose)
{
fmt::print("Selected channel ID: {} (DPC entry {}/{})\n", options.channelID, rand % filtered.size(),
filtered.size());
}
}
if (options.channelID == 0)
{
options.channelID = UMSKT::getRandom<WORD>() % 999;
if (options.verbose)
{
fmt::print("Generated channel ID: {}\n", options.channelID);
}
}
// FE and FF are BINK 1998, but use a different, currently unhandled encoding scheme, we return an error here
if (options.binkID == "FE" || options.binkID == "FF")
{
fmt::print("ERROR: Terminal Services BINKs (FE and FF) are unsupported at this time\n");
return false;
}
processOptionsExitEarly:
if (options.verbose)
{
fmt::print("\n");
}
return true;
}
/**
*
* @return success
*/
BOOL CLI::DisplayHelp(int, char *)
{
options.help = true;
fmt::print("usage: {} \n", options.argv[0]);
for (BYTE i = 0; i < CLIHelpOptionID_END; i++)
{
CLIHelpOptions o = helpOptions[i];
if (o.Short.empty())
{
fmt::print("\t{:>2} --{:<15} {}", "", o.Long, o.HelpText);
}
else if (o.Long.empty())
{
fmt::print("\t-{} {:<15} {}", o.Short, "", o.HelpText);
}
else
{
fmt::print("\t-{} --{:<15} {}", o.Short, o.Long, o.HelpText);
}
if (!o.Default.empty())
{
fmt::print(" (defaults to {})", o.Default);
}
fmt::print("\n");
}
fmt::print("\n");
return true;
}
BOOL CLI::DisplayErrorMessage(int, char *)
{
fmt::print("Error parsing command line options\n");
DisplayHelp(0, nullptr);
options.error = true;
return false;
}
BOOL CLI::SetVerboseOption(int, char *)
{
fmt::print("Enabling verbose option\n\n");
options.verbose = true;
UMSKT::VERBOSE = true;
UMSKT::setDebugOutput(stderr);
return true;
}
BOOL CLI::SetDebugOption(int, char *)
{
fmt::print("Enabling debug option\n");
options.verbose = true;
UMSKT::DEBUG = true;
UMSKT::setDebugOutput(stderr);
return true;
}
BOOL CLI::SetListOption(int, char *)
{
if (options.verbose)
{
fmt::print("Setting list option\n");
}
options.list = true;
return true;
}
BOOL CLI::SetOEMOption(int, char *)
{
if (options.verbose)
{
fmt::print("Setting oem option\n");
}
options.oem = true;
return true;
}
BOOL CLI::SetUpgradeOption(int, char *)
{
if (options.verbose)
{
fmt::print("Setting upgrade option\n");
}
options.upgrade = true;
return true;
}
BOOL CLI::SetFileOption(int count, char *file)
{
if (options.verbose)
{
fmt::print("Setting file option to: {}\n", file);
}
options.keysFilename = file;
return true;
}
BOOL CLI::SetNumberOption(int count, char *num)
{
int nKeys;
if (!_sscanf(num, "%d", &nKeys))
{
return false;
}
if (options.verbose)
{
fmt::print("Setting generation number option to: {}\n", num);
}
options.numKeys = nKeys;
return true;
}
/**
*
* @param count
* @param channum
* @return
*/
BOOL CLI::SetChannelIDOption(int count, char *channum)
{
int siteID;
if (!_sscanf(channum, "%d", &siteID))
{
return false;
}
// channel ids must be between 000 and 999
if (siteID > 999)
{
fmt::print("ERROR: refusing to create a key with a Channel ID greater than 999\n");
return false;
}
if (options.verbose)
{
fmt::print("Setting channel number option to: {}\n", siteID);
}
options.channelID = siteID;
return true;
}
BOOL CLI::SetBINKOption(int count, char *bink)
{
auto strbinkid = std::string(bink);
options.binkID = strtoupper(strbinkid);
if (options.verbose)
{
fmt::print("Setting BINK option to {}\n", strbinkid);
}
return true;
}
BOOL CLI::SetFlavourOption(int count, char *flavour)
{
if (options.verbose)
{
fmt::print("Setting flavour option to {}\n", flavour);
}
options.productFlavour = flavour;
return true;
}
/**
*
* @param count
* @param arg
* @return
*/
BOOL CLI::SetSerialOption(int count, char *arg)
{
int serial_val;
if (!_sscanf(arg, "%d", &serial_val))
{
return false;
}
// serials must be between 000000 and 999999
if (serial_val > 999999)
{
fmt::print("ERROR: refusing to create a key with a Serial not between 000000 and 999999\n");
return false;
}
options.serialSet = true;
options.serial = serial_val;
return true;
}
BOOL CLI::SetActivationIDOption(int count, char *aid)
{
options.installationID = aid;
options.state = STATE_CONFIRMATION_ID;
return true;
}
BOOL CLI::SetProductIDOption(int count, char *product)
{
if (options.verbose)
{
fmt::print("Setting product ID to {}", product);
}
options.productID = product;
return true;
}
BOOL CLI::SetValidateOption(int count, char *productID)
{
options.keyToCheck = productID;
options.state = STATE_PIDGEN_VALIDATE;
return true;
}
BOOL CLI::SetProductCodeOption(int, char *product)
{
if (options.verbose)
{
fmt::print("Setting product code to {}\n", product);
}
auto strProduct = std::string(product);
options.productCode = strtoupper(strProduct);
return true;
}

View File

@ -20,20 +20,23 @@
* @Maintainer Neo
*/
#ifndef UMSKT_OPTIONS_H
#define UMSKT_OPTIONS_H
#ifndef UMSKT_HELP_H
#define UMSKT_HELP_H
typedef BOOL CLIHandlerFunc(int, char *);
enum CLIHelpOptionIDs
{
OPTION_HELP,
OPTION_HELP2,
OPTION_VERSION,
OPTION_VERBOSE,
OPTION_DEBUG,
OPTION_FILE,
OPTION_LIST,
OPTION_NUMBER,
OPTION_PRODUCT,
OPTION_FLAVOUR,
OPTION_NUMBER,
OPTION_OEM,
OPTION_UPGRADE,
OPTION_ACTIVATIONID,
@ -57,4 +60,4 @@ struct CLIHelpOptions
CLIHandlerFunc *handler;
};
#endif // UMSKT_OPTIONS_H
#endif // UMSKT_HELP_H

View File

@ -29,6 +29,22 @@
#include "confid.h"
/**
*
* @param x0
* @param x1
* @param x2
* @param x3
* @param x4
* @param x5
* @param priv
* @param modulous
* @param nonresidue
* @param isOffice
* @param isXPBrand
* @param flagVersion
* @return
*/
BOOL ConfirmationID::LoadHyperellipticCurve(QWORD x0, QWORD x1, QWORD x2, QWORD x3, QWORD x4, QWORD x5, Q_OWORD priv,
QWORD modulous, QWORD nonresidue, BOOL isOffice, BOOL isXPBrand,
BYTE flagVersion)
@ -51,6 +67,17 @@ BOOL ConfirmationID::LoadHyperellipticCurve(QWORD x0, QWORD x1, QWORD x2, QWORD
return true;
}
/**
*
* @param f
* @param priv
* @param modulous
* @param nonresidue
* @param isOffice
* @param isXPBrand
* @param flagVersion
* @return
*/
BOOL ConfirmationID::LoadHyperellipticCurve(QWORD *f, Q_OWORD priv, QWORD modulous, QWORD nonresidue, BOOL isOffice,
BOOL isXPBrand, BYTE flagVersion)
{
@ -66,6 +93,20 @@ BOOL ConfirmationID::LoadHyperellipticCurve(QWORD *f, Q_OWORD priv, QWORD modulo
return true;
}
BOOL LoadHyperellipticCurve(const std::string &x0, const std::string &x1, const std::string &x2, const std::string &x3,
const std::string &x4, const std::string &x5, const std::string &priv,
const std::string &modulous, const std::string &nonresidue, BOOL isOffice, BOOL isXPBrand,
BYTE flagVersion)
{
return true;
}
/**
*
* @param pid
* @return
*/
DWORD ConfirmationID::calculateCheckDigit(DWORD pid)
{
DWORD i = 0, j = 0, k = 0;
@ -78,6 +119,12 @@ DWORD ConfirmationID::calculateCheckDigit(DWORD pid)
return ((10 * pid) - (i % 7)) + 7;
}
/**
*
* @param iid
* @param hwid
* @param version
*/
void ConfirmationID::decode_iid_new_version(BYTE *iid, BYTE *hwid, DWORD *version)
{
QWORD buffer[5];
@ -97,6 +144,13 @@ void ConfirmationID::decode_iid_new_version(BYTE *iid, BYTE *hwid, DWORD *versio
*version = buffer[0] & 7;
}
/**
*
* @param buffer
* @param bufSize
* @param key
* @param keySize
*/
void ConfirmationID::Mix(BYTE *buffer, BYTE bufSize, const BYTE *key, BYTE keySize)
{
BYTE sha1_input[64], sha1_result[20];
@ -143,6 +197,13 @@ void ConfirmationID::Mix(BYTE *buffer, BYTE bufSize, const BYTE *key, BYTE keySi
}
}
/**
*
* @param buffer
* @param bufSize
* @param key
* @param keySize
*/
void ConfirmationID::Unmix(BYTE *buffer, BYTE bufSize, const BYTE key[4], BYTE keySize)
{
BYTE sha1_input[64], sha1_result[20];
@ -194,8 +255,8 @@ void ConfirmationID::Unmix(BYTE *buffer, BYTE bufSize, const BYTE key[4], BYTE k
* @param productIDIn
* @return
*/
DWORD ConfirmationID::Generate(const std::string &installationIDIn, std::string &confirmationIDOut,
std::string &productIDIn)
CONFIRMATION_ID_STATUS ConfirmationID::Generate(const std::string &installationIDIn, std::string &confirmationIDOut,
std::string &productIDIn)
{
DWORD version;
BYTE hardwareID[8];
@ -494,5 +555,5 @@ DWORD ConfirmationID::Generate(const std::string &installationIDIn, std::string
q += 6;
}
return 0;
return SUCCESS;
}

View File

@ -45,7 +45,7 @@ typedef struct
QWORD v[2];
} TDivisor;
class ConfirmationID
class EXPORT ConfirmationID
{
QWORD MOD = 0, NON_RESIDUE = 0;
QWORD curve[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
@ -130,13 +130,19 @@ class ConfirmationID
privateKey.qword[0] = privateKey.qword[1] = 0x00;
}
BOOL LoadHyperellipticCurve(const std::string &x0, const std::string &x1, const std::string &x2,
const std::string &x3, const std::string &x4, const std::string &x5,
const std::string &priv, const std::string &modulous, const std::string &nonresidue,
BOOL isOffice, BOOL isXPBrand, BYTE flagVersion);
BOOL LoadHyperellipticCurve(QWORD x0, QWORD x1, QWORD x2, QWORD x3, QWORD x4, QWORD x5, Q_OWORD priv,
QWORD modulous, QWORD nonresidue, BOOL isOffice, BOOL isXPBrand, BYTE flagVersion);
BOOL LoadHyperellipticCurve(QWORD *f, Q_OWORD priv, QWORD modulous, QWORD nonresidue, BOOL isOffice, BOOL isXPBrand,
BYTE flagVersion);
DWORD Generate(const std::string &installation_id_str, std::string &confirmation_id, std::string &productid);
CONFIRMATION_ID_STATUS Generate(const std::string &installation_id_str, std::string &confirmation_id,
std::string &productid);
~ConfirmationID()
{

View File

@ -22,6 +22,11 @@
#include "confid.h"
/**
*
* @param d
* @return
*/
int ConfirmationID::ConfirmationID::Divisor::find_divisor_v(TDivisor *d)
{
// u | v^2 - curve
@ -107,6 +112,13 @@ int ConfirmationID::ConfirmationID::Divisor::find_divisor_v(TDivisor *d)
return 1;
}
/**
*
* @param src
* @param polyu
* @param polyv
* @return
*/
int ConfirmationID::ConfirmationID::Divisor::u2poly(const TDivisor *src, QWORD polyu[3], QWORD polyv[2])
{
if (src->u[1] != BAD)
@ -138,6 +150,12 @@ int ConfirmationID::ConfirmationID::Divisor::u2poly(const TDivisor *src, QWORD p
return 0;
}
/**
*
* @param src1
* @param src2
* @param dst
*/
void ConfirmationID::Divisor::add(const TDivisor *src1, const TDivisor *src2, TDivisor *dst)
{
QWORD u1[3], u2[3], v1[2], v2[2];
@ -290,6 +308,12 @@ void ConfirmationID::Divisor::add(const TDivisor *src1, const TDivisor *src2, TD
#define divisor_double(src, dst) add(src, src, dst)
/**
*
* @param src
* @param mult
* @param dst
*/
void ConfirmationID::Divisor::mul(const TDivisor *src, QWORD mult, TDivisor *dst)
{
if (mult == 0)
@ -319,6 +343,13 @@ void ConfirmationID::Divisor::mul(const TDivisor *src, QWORD mult, TDivisor *dst
}
}
/**
*
* @param src
* @param mult_lo
* @param mult_hi
* @param dst
*/
void ConfirmationID::Divisor::mul128(const TDivisor *src, QWORD mult_lo, QWORD mult_hi, TDivisor *dst)
{
if (mult_lo == 0 && mult_hi == 0)

View File

@ -22,7 +22,17 @@
#include "confid.h"
// generic short slow code
/**
* generic short slow code
*
* @param adeg
* @param a
* @param bdeg
* @param b
* @param resultprevdeg
* @param result
* @return
*/
int ConfirmationID::Polynomial::mul(int adeg, const QWORD a[], int bdeg, const QWORD b[], int resultprevdeg,
QWORD result[])
{
@ -56,6 +66,15 @@ int ConfirmationID::Polynomial::mul(int adeg, const QWORD a[], int bdeg, const Q
return resultprevdeg;
}
/**
*
* @param adeg
* @param a
* @param bdeg
* @param b
* @param quotient
* @return
*/
int ConfirmationID::Polynomial::div_monic(int adeg, QWORD a[], int bdeg, const QWORD b[], QWORD *quotient)
{
assert(bdeg >= 0);
@ -85,6 +104,19 @@ int ConfirmationID::Polynomial::div_monic(int adeg, QWORD a[], int bdeg, const Q
return i;
}
/**
*
* @param adeg
* @param a
* @param bdeg
* @param b
* @param pgcddeg
* @param gcd
* @param pmult1deg
* @param mult1
* @param pmult2deg
* @param mult2
*/
void ConfirmationID::Polynomial::xgcd(int adeg, const QWORD a[3], int bdeg, const QWORD b[3], int *pgcddeg,
QWORD gcd[3], int *pmult1deg, QWORD mult1[3], int *pmult2deg, QWORD mult2[3])
{

View File

@ -24,20 +24,27 @@
#if defined(__x86_64__) || defined(_M_AMD64) || defined(__aarch64__) || (defined(__arm64__) && defined(__APPLE__))
#ifdef __GNUC__
inline QWORD ConfirmationID::Residue::__umul128(QWORD multiplier, QWORD multiplicand, QWORD *product_hi)
QWORD ConfirmationID::Residue::__umul128(QWORD multiplier, QWORD multiplicand, QWORD *product_hi)
{
OWORD r = (OWORD)multiplier * (OWORD)multiplicand;
*product_hi = r >> 64;
return (QWORD)r;
}
#else
inline QWORD ConfirmationID::Residue::__umul128(QWORD multiplier, QWORD multiplicand, QWORD *product_hi)
#else // basically msvc
/**
*
* @param multiplier
* @param multiplicand
* @param product_hi
* @return
*/
QWORD ConfirmationID::Residue::__umul128(QWORD multiplier, QWORD multiplicand, QWORD *product_hi)
{
return _umul128(multiplier, multiplicand, product_hi);
}
#endif
#elif defined(__i386__) || defined(_M_IX86) || defined(__arm__) || defined(__EMSCRIPTEN__)
inline QWORD ConfirmationID::Residue::__umul128(QWORD multiplier, QWORD multiplicand, QWORD *product_hi)
QWORD ConfirmationID::Residue::__umul128(QWORD multiplier, QWORD multiplicand, QWORD *product_hi)
{
// multiplier = ab = a * 2^32 + b
// multiplicand = cd = c * 2^32 + d
@ -66,6 +73,12 @@ inline QWORD ConfirmationID::Residue::__umul128(QWORD multiplier, QWORD multipli
#error Unknown architecture detected - please edit confid.cpp to tailor __umul128() your architecture
#endif
/**
*
* @param lo
* @param hi
* @return
*/
QWORD ConfirmationID::Residue::ui128_quotient_mod(QWORD lo, QWORD hi)
{
// hi:lo * ceil(2**170/MOD) >> (64 + 64 + 42)
@ -88,6 +101,12 @@ QWORD ConfirmationID::Residue::ui128_quotient_mod(QWORD lo, QWORD hi)
return (prod3lo >> 42) | (prod3hi << 22);
}
/**
*
* @param x
* @param y
* @return
*/
QWORD ConfirmationID::Residue::mul(QWORD x, QWORD y)
{
// * ceil(2**170/MOD) = 0x2d351 c6d04f8b|604fa6a1 c6346a87 for (p-1)*(p-1) max
@ -96,6 +115,12 @@ QWORD ConfirmationID::Residue::mul(QWORD x, QWORD y)
return lo - quotient * parent->MOD;
}
/**
*
* @param x
* @param y
* @return
*/
QWORD ConfirmationID::Residue::pow(QWORD x, QWORD y)
{
if (y == 0)
@ -123,6 +148,12 @@ QWORD ConfirmationID::Residue::pow(QWORD x, QWORD y)
return res;
}
/**
*
* @param x
* @param y
* @return
*/
QWORD ConfirmationID::Residue::add(QWORD x, QWORD y)
{
QWORD z = x + y;
@ -134,6 +165,12 @@ QWORD ConfirmationID::Residue::add(QWORD x, QWORD y)
return z;
}
/**
*
* @param x
* @param y
* @return
*/
QWORD ConfirmationID::Residue::sub(QWORD x, QWORD y)
{
QWORD z = x - y;
@ -145,6 +182,12 @@ QWORD ConfirmationID::Residue::sub(QWORD x, QWORD y)
return z;
}
/**
*
* @param u
* @param v
* @return
*/
QWORD ConfirmationID::Residue::inverse(QWORD u, QWORD v)
{
// assert(u);
@ -166,12 +209,22 @@ QWORD ConfirmationID::Residue::inverse(QWORD u, QWORD v)
return xu;
}
/**
*
* @param x
* @return
*/
QWORD ConfirmationID::Residue::inv(QWORD x)
{
return inverse(x, parent->MOD);
// return residue_pow(x, MOD - 2);
}
/**
*
* @param what
* @return
*/
QWORD ConfirmationID::Residue::sqrt(QWORD what)
{
if (!what)

View File

@ -37,6 +37,11 @@ std::FILE *UMSKT::debug = std::fopen("/dev/null", "w");
BOOL UMSKT::VERBOSE = false;
BOOL UMSKT::DEBUG = false;
/**
* sets the filestream used for debugging
*
* @param input std::FILE
*/
void UMSKT::setDebugOutput(std::FILE *input)
{
debug = input;

View File

@ -27,145 +27,266 @@
#include "pidgen3/BINK2002.h"
#include "pidgen3/PIDGEN3.h"
FNEXPORT BOOL LIBUMSKT_SET_DEBUG_OUTPUT(void *filestream)
{
char buffer[7];
memcpy(buffer, filestream, 6);
buffer[6] = 0;
auto buff_string = std::string(buffer);
std::map<UMSKT_TAG, UMSKT_Value> UMSKT::tags;
if (strcasecmp("STDOUT", &buffer[0]) != 0)
extern "C"
{
/**
* Sets debug output to a given C++ File stream
* if the memory allocated at filestream is "STDOUT" or "STDERR"
* simply use the global vars allocated by *this* C++ runtime.
* otherwise, assume that the input pointer is an ABI equivalent std::FILE
*
* @param char* or std::FILE "filestream"
*/
EXPORT BOOL UMSKT_SET_DEBUG_OUTPUT(void *filestream)
{
UMSKT::debug = stdout;
return true;
}
else if (strcasecmp("STDERR", &buffer[0]) != 0)
{
UMSKT::debug = stderr;
return true;
}
char buffer[7];
memcpy(buffer, filestream, 6);
buffer[6] = 0;
auto buffstring = std::string(buffer);
std::transform(buffstring.begin(), buffstring.end(), buffstring.begin(), ::tolower);
return false;
}
if (buffstring == "stdout")
{
UMSKT::debug = stdout;
return true;
}
else if (buffstring == "stderr")
{
UMSKT::debug = stderr;
return true;
}
else
{
UMSKT::debug = (std::FILE *)filestream;
return true;
}
// ---------------------------------------------
FNEXPORT void *CONFID_INIT()
{
auto cid = new ConfirmationID();
// cid->LoadHyperellipticCurve(0, 0, 0, 0, 0, 0, 0, 0, 0, false, false, 0);
return cid;
}
FNEXPORT BYTE CONFID_GENERATE(void *cidIn, const char *installation_id_str, char *&confirmation_id, char *productid)
{
auto *cid((ConfirmationID *)cidIn);
std::string str, confid(confirmation_id), productids(productid);
auto retval = cid->Generate(str, confid, productids);
return retval;
}
FNEXPORT BYTE CONFID_END(void *cidIn)
{
auto *cid((ConfirmationID *)cidIn);
delete cid;
return true;
}
// ---------------------------------------------
FNEXPORT void *PIDGEN3_BINK1998_INIT(const char *p, const char *a, const char *b, const char *generatorX,
const char *generatorY, const char *publicKeyX, const char *publicKeyY,
const char *genOrder, const char *privateKey)
{
auto *bink1998 = new BINK1998();
bink1998->LoadEllipticCurve(p, a, b, generatorX, generatorY, publicKeyX, publicKeyY, genOrder, privateKey);
return bink1998;
}
FNEXPORT void *PIDGEN3_BINK2002_INIT(const char *p, const char *a, const char *b, const char *generatorX,
const char *generatorY, const char *publicKeyX, const char *publicKeyY,
const char *genOrder, const char *privateKey, const char *authinfo)
{
auto bink2002 = new BINK2002();
bink2002->LoadEllipticCurve(p, a, b, generatorX, generatorY, publicKeyX, publicKeyY, genOrder, privateKey);
return bink2002;
}
FNEXPORT BOOL PIDGEN3_Generate(void *&ptrIn, char *&pKeyOut, int pKeySizeIn)
{
auto *p3((PIDGEN3 *)ptrIn);
std::string str;
// BOOL retval = p3->Generate(str);
if (pKeySizeIn > str.length() + 1)
{
return false;
}
memcpy(pKeyOut, &str[0], str.length());
pKeyOut[str.length()] = 0;
// ---------------------------------------------
return true;
/**
*
* @param tag
* @param value
* @param valueSize
* @return success
*/
EXPORT BOOL UMSKT_SET_TAG(UMSKT_TAG tag, char *value, size_t valueSize)
{
if (valueSize > sizeof(UMSKT_Value))
{
return false;
}
// wipe/set the tag
memset(&UMSKT::tags[tag], 0, sizeof(UMSKT_Value));
memcpy(&UMSKT::tags[tag], value, valueSize);
return true;
}
EXPORT void UMSKT_RESET_TAGS()
{
UMSKT::tags.clear();
}
// ---------------------------------------------
EXPORT void *CONFID_INIT()
{
auto cid = new ConfirmationID();
// cid->LoadHyperellipticCurve(0, 0, 0, 0, 0, 0, 0, 0, 0, false, false, 0);
return cid;
}
EXPORT BYTE CONFID_GENERATE(void *cidIn, const char *installation_id_str, char *&confirmation_id, char *productid)
{
ConfirmationID *cid;
try
{
cid = static_cast<ConfirmationID *>(cidIn);
}
catch (const std::bad_cast &e)
{
fmt::print(UMSKT::debug, "{}: input is not a {} - {}", __FUNCTION__, e.what());
return -1;
}
for (auto const i : UMSKT::tags)
{
switch (i.first)
{
case UMSKT_tag_InstallationID:
break;
case UMSKT_tag_ProductID:
break;
default:
break;
}
}
std::string str, confid(confirmation_id), productids(productid);
auto retval = cid->Generate(str, confid, productids);
return retval;
}
EXPORT void CONFID_END(void *cidIn)
{
auto *cid((ConfirmationID *)cidIn);
delete cid;
cid = nullptr;
cidIn = nullptr;
}
// ---------------------------------------------
EXPORT void *PIDGEN3_INIT(const char *p, const char *a, const char *b, const char *generatorX,
const char *generatorY, const char *publicKeyX, const char *publicKeyY,
const char *genOrder, const char *privateKey)
{
PIDGEN3 *p3;
if (PIDGEN3::checkFieldStrIsBink1998(p))
{
p3 = new BINK1998();
}
else
{
p3 = new BINK2002();
}
p3->LoadEllipticCurve(p, a, b, generatorX, generatorY, publicKeyX, publicKeyY, genOrder, privateKey);
return p3;
}
EXPORT BOOL PIDGEN3_Generate(void *&ptrIn, char *&pKeyOut, int pKeySizeIn)
{
auto *p3((PIDGEN3 *)ptrIn);
for (auto const i : UMSKT::tags)
{
switch (i.first)
{
case UMSKT_tag_isUpgrade:
p3->info.isUpgrade = i.second.boolean;
break;
case UMSKT_tag_ChannelID:
p3->info.setChannelID(i.second.dword);
break;
case UMSKT_tag_Serial:
p3->info.setSerial(i.second.dword);
break;
case UMSKT_tag_AuthData:
p3->info.setAuthInfo(i.second.dword);
default:
break;
}
}
std::string str;
BOOL retval = p3->Generate(str);
assert(pKeySizeIn >= str.length() + 1);
memcpy(pKeyOut, &str[0], str.length());
pKeyOut[str.length()] = 0;
return retval;
}
EXPORT BOOL PIDGEN3_Validate(void *&ptrIn, char *pKeyIn)
{
auto *p3((PIDGEN3 *)ptrIn);
std::string str(pKeyIn);
BOOL retval = p3->Validate(str);
return retval;
}
EXPORT void PIDGEN3_END(void *ptrIn)
{
auto *p3((PIDGEN3 *)ptrIn);
delete p3;
ptrIn = nullptr;
p3 = nullptr;
}
// ---------------------------------------------
EXPORT void *PIDGEN2_INIT()
{
auto p2 = new PIDGEN2();
return p2;
}
EXPORT BOOL PIDGEN2_GENERATE(void *ptrIn, char *&keyout)
{
auto p2 = (PIDGEN2 *)ptrIn;
return true;
}
EXPORT void PIDGEN2_END(void *ptrIn)
{
auto p2 = (PIDGEN2 *)ptrIn;
delete p2;
p2 = nullptr;
ptrIn = nullptr;
}
} // extern "C"
/**
* Convert data between endianness types.
*
* @param data [in]
* @param length [in]
**/
void UMSKT::endian(BYTE *data, int length)
{
for (int i = 0; i < length / 2; i++)
{
BYTE temp = data[i];
data[i] = data[length - i - 1];
data[length - i - 1] = temp;
}
}
FNEXPORT BOOL PIDGEN3_Verify(void *&ptrIn, char *pKeyIn)
/**
* Converts an OpenSSL BigNumber to it's Little Endian binary equivalent
*
* @param a [in] BigNumber to convert
* @param to [out] char* binary representation
* @param tolen [in] length of the char* array
*
* @return length of number in to
**/
int UMSKT::BN_bn2lebin(const BIGNUM *a, unsigned char *to, int tolen)
{
auto *p3((PIDGEN3 *)ptrIn);
std::string str(pKeyIn);
if (a == nullptr || to == nullptr)
{
return 0;
}
BOOL retval = p3->Verify(str);
int len = BN_bn2bin(a, to);
return retval;
}
if (len > tolen)
{
return -1;
}
FNEXPORT void PIDGEN3_END(void *ptrIn)
{
auto *p3((PIDGEN3 *)ptrIn);
delete p3;
}
// Choke point inside BN_bn2lebinpad: OpenSSL uses len instead of tolen.
endian(to, tolen);
// ---------------------------------------------
FNEXPORT BOOL PIDGEN2_INIT()
{
return true;
}
FNEXPORT BOOL PIDGEN2_GENERATE()
{
return true;
}
FNEXPORT BOOL PIDGEN2_END()
{
return true;
}
FNEXPORT BOOL PIDGEN2_GenerateRetail(char *channelID, char *&keyout)
{
auto P2 = new PIDGEN2();
BOOL retval = P2->GenerateRetail(channelID, keyout);
delete P2;
return retval;
}
FNEXPORT BOOL PIDGEN2_GenerateOEM(char *year, char *day, char *oem, char *&keyout)
{
auto P2 = new PIDGEN2();
BOOL retval = P2->GenerateOEM(year, day, oem, keyout);
delete P2;
return retval;
return len;
}

View File

@ -53,32 +53,87 @@
#define NEXTSNBITS(field, n, offset) (((QWORD)(field) >> (offset)) & ((1ULL << (n)) - 1))
#define FIRSTNBITS(field, n) NEXTSNBITS((field), (n), 0)
#define HIBYTES(field, bytes) NEXTSNBITS((QWORD)(field), ((bytes)*8), ((bytes)*8))
#define LOBYTES(field, bytes) FIRSTNBITS((QWORD)(field), ((bytes)*8))
#define HIBYTES(field, bytes) NEXTSNBITS((QWORD)(field), ((bytes) * 8), ((bytes) * 8))
#define LOBYTES(field, bytes) FIRSTNBITS((QWORD)(field), ((bytes) * 8))
#define BYDWORD(n) (DWORD)(*((n) + 0) | *((n) + 1) << 8 | *((n) + 2) << 16 | *((n) + 3) << 24)
#define BITMASK(n) ((1ULL << (n)) - 1)
class UMSKT
#ifndef LIBUMSKT_VERSION_STRING
#define LIBUMSKT_VERSION_STRING "unknown version-dirty"
#endif
enum ValueType
{
VALUE_BOOL,
VALUE_WORD,
VALUE_DWORD,
VALUE_QWORD,
VALUE_OWORD,
VALUE_CHARPTR
};
struct UMSKT_Value
{
ValueType type;
union {
BOOL boolean;
WORD word;
DWORD dword;
QWORD qword;
OWORD oword;
char *chars;
};
};
enum UMSKT_TAG
{
UMSKT_tag_isOEM,
UMSKT_tag_isUpgrade,
UMSKT_tag_Year,
UMSKT_tag_Day,
UMSKT_tag_OEMID,
UMSKT_tag_AuthData,
UMSKT_tag_Serial,
UMSKT_tag_ChannelID,
UMSKT_tag_InstallationID,
UMSKT_tag_ProductID
};
class EXPORT UMSKT
{
public:
static std::FILE *debug;
static BOOL VERBOSE;
static BOOL DEBUG;
static std::map<UMSKT_TAG, UMSKT_Value> tags;
// Hello OpenSSL developers, please tell me, where is this function at?
static int BN_bn2lebin(const BIGNUM *a, unsigned char *to, int tolen);
static void endian(BYTE *data, int length);
static void DESTRUCT()
{
std::fclose(debug);
if (debug != nullptr)
{
std::fclose(debug);
}
debug = nullptr;
}
static void setDebugOutput(std::FILE *input);
template <typename T> static T getRandom()
{
T retval;
RAND_bytes((BYTE *)&retval, sizeof(retval));
return retval;
}
static const char *VERSION()
{
return fmt::format("LIBUMSKT {} compiled on {} {}", LIBUMSKT_VERSION_STRING, __DATE__, __TIME__).c_str();
}
};
#endif // UMSKT_LIBUMSKT_H

View File

@ -22,7 +22,12 @@
#include "PIDGEN2.h"
bool PIDGEN2::isNumericString(char *input)
/**
*
* @param input
* @return
*/
BOOL PIDGEN2::isNumericString(char *input)
{
for (int i = 0; i < strlen(input); i++)
{
@ -35,6 +40,11 @@ bool PIDGEN2::isNumericString(char *input)
return true;
}
/**
*
* @param input
* @return
*/
int PIDGEN2::addDigits(char *input)
{
int output = 0;
@ -52,7 +62,12 @@ int PIDGEN2::addDigits(char *input)
return output;
}
bool PIDGEN2::isValidChannelID(char *channelID)
/**
*
* @param channelID
* @return
*/
BOOL PIDGEN2::isValidChannelID(char *channelID)
{
if (strlen(channelID) > 3)
{
@ -70,7 +85,12 @@ bool PIDGEN2::isValidChannelID(char *channelID)
return true;
}
bool PIDGEN2::isValidOEMID(char *OEMID)
/**
*
* @param OEMID
* @return
*/
BOOL PIDGEN2::isValidOEMID(char *OEMID)
{
if (!isNumericString(OEMID))
{
@ -90,7 +110,12 @@ bool PIDGEN2::isValidOEMID(char *OEMID)
return (mod % 21 == 0);
}
bool PIDGEN2::isValidYear(char *year)
/**
*
* @param year
* @return
*/
BOOL PIDGEN2::isValidYear(char *year)
{
for (int i = 0; i <= 7; i++)
{
@ -102,7 +127,12 @@ bool PIDGEN2::isValidYear(char *year)
return true;
}
bool PIDGEN2::isValidDay(char *day)
/**
*
* @param day
* @return
*/
BOOL PIDGEN2::isValidDay(char *day)
{
if (!isNumericString(day))
{
@ -117,11 +147,22 @@ bool PIDGEN2::isValidDay(char *day)
return true;
}
bool PIDGEN2::isValidRetailProductID(char *productID)
/**
*
* @param productID
* @return
*/
BOOL PIDGEN2::isValidRetailProductID(char *productID)
{
return true;
}
/**
*
* @param channelID
* @param keyout
* @return
*/
int PIDGEN2::GenerateRetail(char *channelID, char *&keyout)
{
if (!isValidChannelID(channelID))
@ -132,6 +173,14 @@ int PIDGEN2::GenerateRetail(char *channelID, char *&keyout)
return 0;
}
/**
*
* @param year
* @param day
* @param oem
* @param keyout
* @return
*/
int PIDGEN2::GenerateOEM(char *year, char *day, char *oem, char *&keyout)
{
if (!isValidOEMID(oem))
@ -139,21 +188,21 @@ int PIDGEN2::GenerateOEM(char *year, char *day, char *oem, char *&keyout)
int mod = addDigits(oem);
mod += mod % 21;
strcpy(oem, fmt::format("{:07d}", mod).c_str());
snprintf(oem, 8, "%07u", mod);
}
if (!isValidYear(year))
{
strcpy(year, validYears[0]);
_strncpy(year, 4, validYears[0], 4);
}
if (!isValidDay(day))
{
int iday = std::stoi(day);
auto iday = UMSKT::getRandom<int>();
iday = (iday + 1) % 365;
}
strcpy(keyout, fmt::format("{}{}-OEM-{}-{}", year, day, oem, oem).c_str());
_strncpy(keyout, 32, &fmt::format("{}{}-OEM-{}-{}", year, day, oem, oem).c_str()[0], 32);
return 0;
}

View File

@ -25,7 +25,7 @@
#include "../libumskt.h"
class PIDGEN2
class EXPORT PIDGEN2
{
DWORD year;
DWORD day;

View File

@ -29,12 +29,30 @@
#include "BINK1998.h"
/**
* Packs a Windows XP-like Product Key.
*
* @param pRaw [in] *QWORD[2] raw product key input
**/
BOOL BINK1998::Pack(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(info.Signature, 5) << 59 | FIRSTNBITS(info.Hash, 28) << 31 | info.Serial << 1 | info.isUpgrade;
pRaw[1] = NEXTSNBITS(info.Signature, 51, 5);
return true;
}
/**
* Unpacks a Windows XP-like Product Key.
*
* @param pRaw [out] *QWORD[2] raw product key output
**/
BOOL BINK1998::Unpack(KeyInfo &info, QWORD *pRaw)
BOOL BINK1998::Unpack(QWORD *pRaw)
{
// We're assuming that the quantity of information within the product key is at most 114 bits.
// log2(24^25) = 114.
@ -54,123 +72,6 @@ BOOL BINK1998::Unpack(KeyInfo &info, QWORD *pRaw)
return true;
}
/**
* Packs a Windows XP-like Product Key.
*
* @param pRaw [in] *QWORD[2] raw product key input
**/
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(info.Signature, 5) << 59 | FIRSTNBITS(info.Hash, 28) << 31 | info.Serial << 1 | info.isUpgrade;
pRaw[1] = NEXTSNBITS(info.Signature, 51, 5);
return true;
}
/**
* Verifies a Windows XP-like Product Key.
*
* @param pKey [in]
*
* @return true if provided key validates against loaded curve
*/
BOOL BINK1998::Verify(std::string &pKey)
{
if (pKey.length() != 25)
{
return false;
}
BN_CTX *numContext = BN_CTX_new();
KeyInfo info;
QWORD pRaw[2];
// Convert Base24 CD-key to bytecode.
unbase24((BYTE *)pRaw, pKey);
// Extract RPK, hash and signature from bytecode.
Unpack(info, pRaw);
fmt::print(UMSKT::debug, "Validation results:\n");
fmt::print(UMSKT::debug, " Upgrade: {:#08x}\n", info.isUpgrade);
fmt::print(UMSKT::debug, " Serial: {:#08x}\n", info.Serial);
fmt::print(UMSKT::debug, " Hash: {:#08x}\n", info.Hash);
fmt::print(UMSKT::debug, " Signature: {:#08x}\n", info.Signature);
fmt::print(UMSKT::debug, "\n");
DWORD pData = info.Serial << 1 | info.isUpgrade;
/*
*
* Scalars:
* e = Hash
* s = Schnorr Signature
*
* Points:
* G(x, y) = Generator (Base Point)
* K(x, y) = Public Key
*
* Equation:
* P = sG + eK
*
*/
BIGNUM *e = BN_lebin2bn((BYTE *)&info.Hash, sizeof(info.Hash), nullptr),
*s = BN_lebin2bn((BYTE *)&info.Signature, sizeof(info.Signature), 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), *p = EC_POINT_new(eCurve);
// t = sG
EC_POINT_mul(eCurve, t, nullptr, genPoint, s, numContext);
// P = eK
EC_POINT_mul(eCurve, p, nullptr, pubPoint, e, numContext);
// P += t
EC_POINT_add(eCurve, p, t, p, numContext);
// 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];
// Convert resulting point coordinates to bytes.
BN_bn2lebin(x, xBin, FIELD_BYTES);
BN_bn2lebin(y, yBin, FIELD_BYTES);
// Assemble the SHA message.
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);
// Translate the byte digest into a 32-bit integer - this is our computed hash.
// Truncate the hash to 28 bits.
DWORD compHash = BYDWORD(msgDigest) >> 4 & BITMASK(28);
BN_free(e);
BN_free(s);
BN_CTX_free(numContext);
EC_POINT_free(t);
EC_POINT_free(p);
// If the computed hash checks out, the key is valid.
return compHash == info.Hash;
}
/**
* Generates a Windows XP-like Product Key.
*
@ -178,7 +79,7 @@ BOOL BINK1998::Verify(std::string &pKey)
*
* @return true on success, false on fail
*/
BOOL BINK1998::Generate(KeyInfo &info, std::string &pKey)
BOOL BINK1998::Generate(std::string &pKey)
{
BN_CTX *numContext = BN_CTX_new();
@ -212,8 +113,8 @@ BOOL BINK1998::Generate(KeyInfo &info, std::string &pKey)
BYTE xBin[FIELD_BYTES], yBin[FIELD_BYTES];
// Convert coordinates to bytes.
BN_bn2lebin(x, xBin, FIELD_BYTES);
BN_bn2lebin(y, yBin, FIELD_BYTES);
UMSKT::BN_bn2lebin(x, xBin, FIELD_BYTES);
UMSKT::BN_bn2lebin(y, yBin, FIELD_BYTES);
// Assemble the SHA message.
memcpy(&msgBuffer[0], &pData, 4);
@ -257,13 +158,15 @@ BOOL BINK1998::Generate(KeyInfo &info, std::string &pKey)
BN_bn2lebinpad(s, (BYTE *)&info.Signature, BN_num_bytes(s));
// Pack product key.
Pack(info, pRaw);
Pack(pRaw);
auto serial = fmt::format("{:d}", info.Serial);
fmt::print(UMSKT::debug, "Generation results:\n");
fmt::print(UMSKT::debug, " Upgrade: {:#08x}\n", info.isUpgrade);
fmt::print(UMSKT::debug, " Serial: {:#08x}\n", info.Serial);
fmt::print(UMSKT::debug, " Hash: {:#08x}\n", info.Hash);
fmt::print(UMSKT::debug, " Signature: {:#08x}\n", info.Signature);
fmt::print(UMSKT::debug, "{:>10}: {:b}\n", "Upgrade", (bool)info.isUpgrade);
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Channel ID", serial.substr(0, 3));
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Sequence", serial.substr(3));
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Hash", info.Hash);
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Signature", info.Signature);
fmt::print(UMSKT::debug, "\n");
EC_POINT_free(r);
@ -279,3 +182,103 @@ BOOL BINK1998::Generate(KeyInfo &info, std::string &pKey)
return true;
}
/**
* Validate a Windows XP-like Product Key.
*
* @param pKey [in]
*
* @return true if provided key validates against loaded curve
*/
BOOL BINK1998::Validate(std::string &pKey)
{
if (pKey.length() != 25)
{
return false;
}
BN_CTX *numContext = BN_CTX_new();
QWORD pRaw[2];
// Convert Base24 CD-key to bytecode.
unbase24((BYTE *)pRaw, pKey);
// Extract RPK, hash and signature from bytecode.
Unpack(pRaw);
auto serial = fmt::format("{:d}", info.Serial);
fmt::print(UMSKT::debug, "Validation results:\n");
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Upgrade", (bool)info.isUpgrade);
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Channel ID", serial.substr(0, 3));
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Sequence", serial.substr(3));
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Hash", info.Hash);
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Signature", info.Signature);
fmt::print(UMSKT::debug, "\n");
DWORD pData = info.Serial << 1 | info.isUpgrade;
/*
*
* Scalars:
* e = Hash
* s = Schnorr Signature
*
* Points:
* G(x, y) = Generator (Base Point)
* K(x, y) = Public Key
*
* Equation:
* P = sG + eK
*
*/
BIGNUM *e = BN_lebin2bn((BYTE *)&info.Hash, sizeof(info.Hash), nullptr),
*s = BN_lebin2bn((BYTE *)&info.Signature, sizeof(info.Signature), 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), *p = EC_POINT_new(eCurve);
// t = sG
EC_POINT_mul(eCurve, t, nullptr, genPoint, s, numContext);
// P = eK
EC_POINT_mul(eCurve, p, nullptr, pubPoint, e, numContext);
// P += t
EC_POINT_add(eCurve, p, t, p, numContext);
// 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];
// Convert resulting point coordinates to bytes.
UMSKT::BN_bn2lebin(x, xBin, FIELD_BYTES);
UMSKT::BN_bn2lebin(y, yBin, FIELD_BYTES);
// Assemble the SHA message.
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);
// Translate the byte digest into a 32-bit integer - this is our computed hash.
// Truncate the hash to 28 bits.
DWORD compHash = BYDWORD(msgDigest) >> 4 & BITMASK(28);
BN_free(e);
BN_free(s);
BN_CTX_free(numContext);
EC_POINT_free(t);
EC_POINT_free(p);
// If the computed hash checks out, the key is valid.
return compHash == info.Hash;
}

View File

@ -25,13 +25,30 @@
#include "PIDGEN3.h"
class BINK1998 : public PIDGEN3
class EXPORT BINK1998 : public PIDGEN3
{
public:
BOOL Unpack(KeyInfo &info, QWORD *pRaw) override;
BOOL Pack(const KeyInfo &info, QWORD *pRaw) override;
BOOL Verify(std::string &pKey) override;
BOOL Generate(KeyInfo &info, std::string &pKey) override;
using PIDGEN3::PIDGEN3;
explicit BINK1998(PIDGEN3 *p3)
{
privateKey = p3->privateKey;
genOrder = p3->genOrder;
genPoint = p3->genPoint;
pubPoint = p3->pubPoint;
eCurve = p3->eCurve;
}
using PIDGEN3::Pack;
BOOL Pack(QWORD *pRaw) override;
using PIDGEN3::Unpack;
BOOL Unpack(QWORD *pRaw) override;
using PIDGEN3::Generate;
BOOL Generate(std::string &pKey) override;
using PIDGEN3::Validate;
BOOL Validate(std::string &pKey) override;
};
#endif // UMSKT_BINK1998_H

View File

@ -29,12 +29,26 @@
#include "BINK2002.h"
/**
* Packs a Windows Server 2003-like Product Key.
*
* @param pRaw *QWORD[2] raw product key output
**/
BOOL BINK2002::Pack(QWORD *pRaw)
{
// AuthInfo [113..104] <- Signature [103..42] <- Hash [41..11] <- Channel ID [10..1] <- Upgrade [0]
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;
}
/**
* Unpacks a Windows Server 2003-like Product Key.
*
* @param pRaw *QWORD[2] raw product key input
**/
BOOL BINK2002::Unpack(KeyInfo &info, QWORD *pRaw)
BOOL BINK2002::Unpack(QWORD *pRaw)
{
// We're assuming that the quantity of information within the product key is at most 114 bits.
// log2(24^25) = 114.
@ -60,140 +74,13 @@ BOOL BINK2002::Unpack(KeyInfo &info, QWORD *pRaw)
}
/**
* Packs a Windows Server 2003-like Product Key.
*
* @param pRaw *QWORD[2] raw product key output
**/
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(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;
}
/**
* Verifies a Windows Server 2003-like Product Key.
* Generates a Windows Server 2003-like Product Key.
*
* @param info
* @param pKey
**/
BOOL BINK2002::Verify(std::string &pKey)
{
BN_CTX *context = BN_CTX_new();
KeyInfo info;
QWORD bKey[2];
// Convert Base24 CD-key to bytecode.
unbase24((BYTE *)bKey, &pKey[0]);
// Extract product key segments from bytecode.
Unpack(info, bKey);
DWORD pData = info.ChannelID << 1 | info.isUpgrade;
fmt::print(UMSKT::debug, "Validation results:\n");
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], yBin[FIELD_BYTES_2003];
// Assemble the first SHA message.
msgBuffer[0x00] = 0x5D;
msgBuffer[0x01] = (pData & 0x00FF);
msgBuffer[0x02] = (pData & 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;
// newSignature = SHA1(5D || Channel ID || Hash || AuthInfo || 00 00)
SHA1(msgBuffer, 11, msgDigest);
// Translate the byte digest into a 64-bit integer - this is our computed intermediate signature.
// As the signature is only 62 bits long at most, we have to truncate it by shifting the high DWORD right 2 bits
// (per spec).
QWORD iSignature = NEXTSNBITS(BYDWORD(&msgDigest[4]), 30, 2) << 32 | BYDWORD(msgDigest);
/*
*
* Scalars:
* e = Hash
* s = Schnorr Signature
*
* Points:
* G(x, y) = Generator (Base Point)
* K(x, y) = Public Key
*
* Equation:
* P = s(sG + eK)
*
*/
BIGNUM *e = BN_lebin2bn((BYTE *)&iSignature, sizeof(iSignature), nullptr),
*s = BN_lebin2bn((BYTE *)&info.Signature, sizeof(info.Signature), nullptr);
BIGNUM *x = BN_CTX_get(context), *y = BN_CTX_get(context);
// Create 2 points on the elliptic curve.
EC_POINT *p = EC_POINT_new(eCurve), *t = EC_POINT_new(eCurve);
// t = sG
EC_POINT_mul(eCurve, t, nullptr, genPoint, s, context);
// p = eK
EC_POINT_mul(eCurve, p, nullptr, pubPoint, e, context);
// p += t
EC_POINT_add(eCurve, p, t, p, context);
// p *= s
EC_POINT_mul(eCurve, p, nullptr, p, s, context);
// x = p.x; y = p.y;
EC_POINT_get_affine_coordinates(eCurve, p, x, y, context);
// Convert resulting point coordinates to bytes.
BN_bn2lebin(x, xBin, FIELD_BYTES_2003);
BN_bn2lebin(y, yBin, FIELD_BYTES_2003);
// Assemble the second SHA message.
msgBuffer[0x00] = 0x79;
msgBuffer[0x01] = (pData & 0x00FF);
msgBuffer[0x02] = (pData & 0xFF00) >> 8;
memcpy((void *)&msgBuffer[3], (void *)xBin, FIELD_BYTES_2003);
memcpy((void *)&msgBuffer[3 + FIELD_BYTES_2003], (void *)yBin, FIELD_BYTES_2003);
// compHash = SHA1(79 || Channel ID || p.x || p.y)
SHA1(msgBuffer, SHA_MSG_LENGTH_2003, msgDigest);
// Translate the byte digest into a 32-bit integer - this is our computed hash.
// Truncate the hash to 31 bits.
DWORD compHash = BYDWORD(msgDigest) & BITMASK(31);
BN_free(s);
BN_free(e);
EC_POINT_free(p);
EC_POINT_free(t);
BN_CTX_free(context);
// If the computed hash checks out, the key is valid.
return compHash == info.Hash;
}
/* Generates a Windows Server 2003-like Product Key. */
BOOL BINK2002::Generate(KeyInfo &info, std::string &pKey)
* @return
*/
BOOL BINK2002::Generate(std::string &pKey)
{
BN_CTX *numContext = BN_CTX_new();
@ -225,8 +112,8 @@ BOOL BINK2002::Generate(KeyInfo &info, std::string &pKey)
yBin[FIELD_BYTES_2003];
// Convert resulting point coordinates to bytes.
BN_bn2lebin(x, xBin, FIELD_BYTES_2003);
BN_bn2lebin(y, yBin, FIELD_BYTES_2003);
UMSKT::BN_bn2lebin(x, xBin, FIELD_BYTES_2003);
UMSKT::BN_bn2lebin(y, yBin, FIELD_BYTES_2003);
// Assemble the first SHA message.
msgBuffer[0x00] = 0x79;
@ -333,14 +220,14 @@ BOOL BINK2002::Generate(KeyInfo &info, std::string &pKey)
BN_bn2lebinpad(s, (BYTE *)&info.Signature, BN_num_bytes(s));
// Pack product key.
Pack(info, pRaw);
Pack(pRaw);
fmt::print(UMSKT::debug, "Generation results:\n");
fmt::print(UMSKT::debug, " Upgrade: {:#08x}\n", info.isUpgrade);
fmt::print(UMSKT::debug, "Channel ID: {:#08x}\n", info.ChannelID);
fmt::print(UMSKT::debug, " Hash: {:#08x}\n", info.Hash);
fmt::print(UMSKT::debug, " Signature: {:#08x}\n", info.Signature);
fmt::print(UMSKT::debug, " AuthInfo: {:#08x}\n", info.AuthInfo);
fmt::print(UMSKT::debug, "{:>10}: {:b}\n", "Upgrade", (bool)info.isUpgrade);
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Channel ID", info.ChannelID);
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Hash", info.Hash);
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Signature", info.Signature);
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "AuthInfo", info.AuthInfo);
fmt::print(UMSKT::debug, "\n");
EC_POINT_free(r);
@ -356,3 +243,121 @@ BOOL BINK2002::Generate(KeyInfo &info, std::string &pKey)
return true;
}
/**
* Validates a Windows Server 2003-like Product Key.
*
* @param pKey
**/
BOOL BINK2002::Validate(std::string &pKey)
{
BN_CTX *context = BN_CTX_new();
QWORD bKey[2];
// Convert Base24 CD-key to bytecode.
unbase24((BYTE *)bKey, &pKey[0]);
// Extract product key segments from bytecode.
Unpack(bKey);
DWORD pData = info.ChannelID << 1 | info.isUpgrade;
fmt::print(UMSKT::debug, "Validation results:\n");
fmt::print(UMSKT::debug, "{:>10}: {:b}\n", "Upgrade", (bool)info.isUpgrade);
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Channel ID", info.ChannelID);
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Hash", info.Hash);
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Signature", info.Signature);
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "AuthInfo", info.AuthInfo);
fmt::print(UMSKT::debug, "\n");
BYTE msgDigest[SHA_DIGEST_LENGTH], msgBuffer[SHA_MSG_LENGTH_2003], xBin[FIELD_BYTES_2003], yBin[FIELD_BYTES_2003];
// Assemble the first SHA message.
msgBuffer[0x00] = 0x5D;
msgBuffer[0x01] = (pData & 0x00FF);
msgBuffer[0x02] = (pData & 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;
// newSignature = SHA1(5D || Channel ID || Hash || AuthInfo || 00 00)
SHA1(msgBuffer, 11, msgDigest);
// Translate the byte digest into a 64-bit integer - this is our computed intermediate signature.
// As the signature is only 62 bits long at most, we have to truncate it by shifting the high DWORD right 2 bits
// (per spec).
QWORD iSignature = NEXTSNBITS(BYDWORD(&msgDigest[4]), 30, 2) << 32 | BYDWORD(msgDigest);
/*
*
* Scalars:
* e = Hash
* s = Schnorr Signature
*
* Points:
* G(x, y) = Generator (Base Point)
* K(x, y) = Public Key
*
* Equation:
* P = s(sG + eK)
*
*/
BIGNUM *e = BN_lebin2bn((BYTE *)&iSignature, sizeof(iSignature), nullptr),
*s = BN_lebin2bn((BYTE *)&info.Signature, sizeof(info.Signature), nullptr);
BIGNUM *x = BN_CTX_get(context), *y = BN_CTX_get(context);
// Create 2 points on the elliptic curve.
EC_POINT *p = EC_POINT_new(eCurve), *t = EC_POINT_new(eCurve);
// t = sG
EC_POINT_mul(eCurve, t, nullptr, genPoint, s, context);
// p = eK
EC_POINT_mul(eCurve, p, nullptr, pubPoint, e, context);
// p += t
EC_POINT_add(eCurve, p, t, p, context);
// p *= s
EC_POINT_mul(eCurve, p, nullptr, p, s, context);
// x = p.x; y = p.y;
EC_POINT_get_affine_coordinates(eCurve, p, x, y, context);
// Convert resulting point coordinates to bytes.
UMSKT::BN_bn2lebin(x, xBin, FIELD_BYTES_2003);
UMSKT::BN_bn2lebin(y, yBin, FIELD_BYTES_2003);
// Assemble the second SHA message.
msgBuffer[0x00] = 0x79;
msgBuffer[0x01] = (pData & 0x00FF);
msgBuffer[0x02] = (pData & 0xFF00) >> 8;
memcpy((void *)&msgBuffer[3], (void *)xBin, FIELD_BYTES_2003);
memcpy((void *)&msgBuffer[3 + FIELD_BYTES_2003], (void *)yBin, FIELD_BYTES_2003);
// compHash = SHA1(79 || Channel ID || p.x || p.y)
SHA1(msgBuffer, SHA_MSG_LENGTH_2003, msgDigest);
// Translate the byte digest into a 32-bit integer - this is our computed hash.
// Truncate the hash to 31 bits.
DWORD compHash = BYDWORD(msgDigest) & BITMASK(31);
BN_free(s);
BN_free(e);
EC_POINT_free(p);
EC_POINT_free(t);
BN_CTX_free(context);
// If the computed hash checks out, the key is valid.
return compHash == info.Hash;
}

View File

@ -25,13 +25,30 @@
#include "PIDGEN3.h"
class BINK2002 : public PIDGEN3
class EXPORT BINK2002 : public PIDGEN3
{
public:
BOOL Unpack(KeyInfo &info, QWORD *pRaw) override;
BOOL Pack(const KeyInfo &info, QWORD *pRaw) override;
BOOL Verify(std::string &pKey) override;
BOOL Generate(KeyInfo &info, std::string &pKey) override;
using PIDGEN3::PIDGEN3;
explicit BINK2002(PIDGEN3 *p3)
{
privateKey = p3->privateKey;
genOrder = p3->genOrder;
genPoint = p3->genPoint;
pubPoint = p3->pubPoint;
eCurve = p3->eCurve;
}
using PIDGEN3::Pack;
BOOL Pack(QWORD *pRaw) override;
using PIDGEN3::Unpack;
BOOL Unpack(QWORD *pRaw) override;
using PIDGEN3::Generate;
BOOL Generate(std::string &pKey) override;
using PIDGEN3::Validate;
BOOL Validate(std::string &pKey) override;
};
#endif // UMSKT_BINK2002_H

View File

@ -21,6 +21,8 @@
*/
#include "PIDGEN3.h"
#include "BINK1998.h"
#include "BINK2002.h"
/**
* https://xkcd.com/221/
@ -118,49 +120,40 @@ BOOL PIDGEN3::LoadEllipticCurve(const std::string pSel, const std::string aSel,
return true;
}
/**
* Convert data between endianness types.
*
* @param data [in]
* @param length [in]
**/
inline void PIDGEN3::endian(BYTE *data, int length)
BOOL PIDGEN3::Generate(std::string &pKey)
{
for (int i = 0; i < length / 2; i++)
BOOL retval;
if (checkFieldIsBink1998())
{
BYTE temp = data[i];
data[i] = data[length - i - 1];
data[length - i - 1] = temp;
auto p3 = BINK1998();
retval = p3.Generate(pKey);
}
else
{
auto p3 = BINK2002();
retval = p3.Generate(pKey);
}
return retval;
}
/**
* Converts an OpenSSL BigNumber to it's Little Endian binary equivalent
*
* @param a [in] BigNumber to convert
* @param to [out] char* binary representation
* @param tolen [in] length of the char* array
*
* @return length of number in to
**/
int PIDGEN3::BN_bn2lebin(const BIGNUM *a, unsigned char *to, int tolen)
BOOL PIDGEN3::Validate(std::string &pKey)
{
if (a == nullptr || to == nullptr)
BOOL retval;
if (checkFieldIsBink1998())
{
return 0;
auto p3 = BINK1998(this);
retval = p3.Validate(pKey);
}
else
{
auto p3 = BINK2002(this);
retval = p3.Validate(pKey);
}
int len = BN_bn2bin(a, to);
if (len > tolen)
{
return -1;
}
// Choke point inside BN_bn2lebinpad: OpenSSL uses len instead of tolen.
endian(to, tolen);
return len;
return retval;
}
/**
@ -185,7 +178,7 @@ void PIDGEN3::base24(std::string &cdKey, BYTE *byteSeq)
; // do nothing, just counting
}
endian(rbyteSeq, ++length);
UMSKT::endian(rbyteSeq, ++length);
// Convert reversed byte sequence to BigNum z.
z = BN_bin2bn(rbyteSeq, length, nullptr);
@ -214,8 +207,6 @@ void PIDGEN3::unbase24(BYTE *byteSeq, std::string cdKey)
BYTE pDecodedKey[PK_LENGTH + NULL_TERMINATOR]{};
BIGNUM *y = BN_new();
BN_zero(y);
// Remove dashes from the CD-key and put it into a Base24 byte array.
for (int i = 0, k = 0; i < cdKey.length() && k < PK_LENGTH; i++)
{
@ -247,17 +238,40 @@ void PIDGEN3::unbase24(BYTE *byteSeq, std::string cdKey)
BN_free(y);
// Reverse the byte sequence.
endian(byteSeq, n);
UMSKT::endian(byteSeq, n);
}
BOOL PIDGEN3::getIsBINK1998()
BOOL PIDGEN3::checkFieldIsBink1998()
{
BIGNUM *max = BN_new();
BN_hex2bn(&max, MAX_BINK1998);
auto *max = BN_new();
// 1 << 385 (or max size of BINK1998 field in bits + 1)
BN_set_bit(max, (12 * 4 * 8) + 1);
// retval is -1 when (max < privateKey)
int retval = BN_cmp(max, privateKey);
BN_free(max);
return retval == true;
// is max > privateKey?
return retval == 1;
}
BOOL PIDGEN3::checkFieldStrIsBink1998(std::string keyin)
{
auto *context = BN_CTX_new();
auto max = BN_CTX_get(context), input = BN_CTX_get(context);
BN_dec2bn(&input, &keyin[0]);
// 1 << 385 (or max size of BINK1998 field in bits + 1)
BN_set_bit(max, (12 * 4 * 8) + 1);
// retval is -1 when (max < privateKey)
int retval = BN_cmp(max, input);
BN_CTX_free(context);
// is max > privateKey?
return retval == 1;
}

View File

@ -24,23 +24,35 @@
#define UMSKT_PIDGEN3_H
#include "../libumskt.h"
#define MAX_BINK1998 \
"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
class PIDGEN3
class BINK1998;
class BINK2002;
class EXPORT PIDGEN3
{
friend class BINK1998;
friend class BINK2002;
protected:
BIGNUM *privateKey, *genOrder;
EC_POINT *genPoint, *pubPoint;
EC_GROUP *eCurve;
BOOL isBINK1998;
public:
PIDGEN3()
{
}
~PIDGEN3()
PIDGEN3(PIDGEN3 &p3)
{
privateKey = p3.privateKey;
genOrder = p3.genOrder;
genPoint = p3.genPoint;
pubPoint = p3.pubPoint;
eCurve = p3.eCurve;
}
virtual ~PIDGEN3()
{
EC_GROUP_free(eCurve);
EC_POINT_free(genPoint);
@ -69,7 +81,7 @@ class PIDGEN3
{
ChannelID = ChannelIDIn;
}
};
} info;
static constexpr char pKeyCharset[] = "BCDFGHJKMPQRTVWXY2346789";
@ -77,19 +89,16 @@ class PIDGEN3
std::string generatorYSel, std::string publicKeyXSel, std::string publicKeyYSel,
std::string genOrderSel, std::string privateKeySel);
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(KeyInfo &info, std::string &pKey) = 0;
virtual BOOL Pack(QWORD *pRaw) = 0;
virtual BOOL Unpack(QWORD *pRaw) = 0;
virtual BOOL Generate(std::string &pKey);
virtual BOOL Validate(std::string &pKey);
// PIDGEN3.cpp
// Hello OpenSSL developers, please tell me, where is this function at?
int BN_bn2lebin(const BIGNUM *a, unsigned char *to, int tolen);
void endian(BYTE *data, int length);
void base24(std::string &cdKey, BYTE *byteSeq);
void unbase24(BYTE *byteSeq, std::string cdKey);
BOOL getIsBINK1998();
BOOL checkFieldIsBink1998();
static BOOL checkFieldStrIsBink1998(std::string keyin);
};
#endif // UMSKT_PIDGEN3_H

View File

@ -25,7 +25,7 @@
int main(int argc, char *argv[])
{
int status;
if (status = CLI::Init(argc, argv); status > 0)
if (status = CLI::Init(argc, argv); status != 0)
{
return status;
}

View File

@ -1,329 +0,0 @@
/**
* This file is a part of the UMSKT Project
*
* Copyleft (C) 2019-2024 UMSKT Contributors (et.al.)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @FileCreated by Neo on 01/02/2024
* @Maintainer Neo
*/
#include "cli.h"
// define static storage
std::array<CLIHelpOptions, CLIHelpOptionID_END> CLI::helpOptions;
void CLI::SetHelpText()
{
helpOptions[OPTION_HELP] = {"h", "help", "show this help text", false, "", &DisplayHelp};
helpOptions[OPTION_VERBOSE] = {"v", "verbose", "enable verbose output", false, "", &SetVerboseOption};
helpOptions[OPTION_DEBUG] = {"d", "debug", "enable debug output", false, "", &SetDebugOption};
helpOptions[OPTION_FILE] = {"f", "file", "specify which keys file to load", true, "", &SetFileOption};
helpOptions[OPTION_LIST] = {"l", "list", "list supported products", false, "", &SetListOption};
helpOptions[OPTION_NUMBER] = {"n", "number", "(PIDGEN only) number of keys to generate",
true, "1", &SetNumberOption};
helpOptions[OPTION_PRODUCT] = {"p", "product", "which product to generate keys for",
true, options.productCode, &SetProductCodeOption};
helpOptions[OPTION_ACTIVATIONID] = {
"i", "instid", "(activation only) installation ID used to generate confirmation ID",
true, "", &SetActivationIDOption};
helpOptions[OPTION_ACTIVATIONPID] = {
"P", "productid", "(Office activation only) product ID to generate confirmation ID for",
true, "", &SetProductIDOption};
helpOptions[OPTION_OEM] = {"o", "oem", "\t(PIDGEN) generate an OEM key", false, "", &SetOEMOption};
helpOptions[OPTION_UPGRADE] = {"u", "upgrade", "(PIDGEN 3 only) generate an upgrade key",
false, "", &SetUpgradeOption};
helpOptions[OPTION_BINK] = {"b", "binkid", "(advanced) override which BINK identifier to load",
true, "", &SetBINKOption};
helpOptions[OPTION_CHANNELID] = {"c", "channelid", "(advanced) override which product channel to use",
true, "", &SetChannelIDOption};
helpOptions[OPTION_SERIAL] = {
"s", "serial", "(advanced, PIDGEN 2/3 [BINK 1998] only) specify a serial to generate",
true, "", &SetSerialOption};
helpOptions[OPTION_AUTHDATA] = {
"a", "authdata", "(advanced, PIDGEN 3 [BINK 2000] only) specify a value for the authentication data field",
true, "", nullptr};
helpOptions[OPTION_VALIDATE] = {
"V", "validate", "validate a specified product ID against known BINKs and algorithms",
true, "", &SetValidateOption};
}
/**
*
* @return success
*/
BOOL CLI::parseCommandLine()
{
for (DWORD i = 1; i < options.argc; i++)
{
std::string arg = options.argv[i];
if (arg[0] == '-')
{
arg.erase(0, 1);
}
if (arg[0] == '-')
{
arg.erase(0, 1);
}
if (arg.empty())
{
continue;
}
for (BYTE j = 0; j < CLIHelpOptionID_END; j++)
{
if (arg != helpOptions[j].Short && arg != helpOptions[j].Long)
{
continue;
}
std::string nextarg;
if (helpOptions[j].hasArguments)
{
if (i == options.argc - 1)
{
options.error = true;
goto CommandLineParseEnd;
}
else
{
i++;
nextarg = std::string(options.argv[i]);
}
}
auto success = helpOptions[j].handler(1, &nextarg[0]);
if (!success)
{
options.error = true;
goto CommandLineParseEnd;
}
if (options.help)
{
goto CommandLineParseEnd;
}
goto ParseNextCommandLineOption;
}
fmt::print("unknown option: {}\n", arg);
options.error = true;
goto CommandLineParseEnd;
ParseNextCommandLineOption:
continue;
}
CommandLineParseEnd:
if (options.error)
{
DisplayErrorMessage(0, nullptr);
}
return !options.error;
}
/**
*
* @return success
*/
BOOL CLI::DisplayHelp(int, char *)
{
options.help = true;
fmt::print("usage: {} \n", options.argv[0]);
for (BYTE i = 0; i < CLIHelpOptionID_END; i++)
{
CLIHelpOptions o = helpOptions[i];
fmt::print(" -{} --{}\t{}", o.Short, o.Long, o.HelpText);
if (!o.Default.empty())
{
fmt::print(" (defaults to {})", o.Default);
}
fmt::print("\n");
}
fmt::print("\n");
return true;
}
BOOL CLI::DisplayErrorMessage(int, char *)
{
fmt::print("error parsing command line options\n");
DisplayHelp(0, nullptr);
options.error = true;
return false;
}
BOOL CLI::SetVerboseOption(int, char *)
{
fmt::print("enabling verbose option\n");
options.verbose = true;
UMSKT::VERBOSE = true;
UMSKT::setDebugOutput(stderr);
return true;
}
BOOL CLI::SetDebugOption(int, char *)
{
fmt::print("enabling debug option\n");
options.verbose = true;
UMSKT::DEBUG = true;
UMSKT::setDebugOutput(stderr);
return true;
}
BOOL CLI::SetListOption(int, char *)
{
options.list = true;
return true;
}
BOOL CLI::SetOEMOption(int, char *)
{
options.oem = true;
return true;
}
BOOL CLI::SetUpgradeOption(int, char *)
{
options.upgrade = true;
return true;
}
BOOL CLI::SetFileOption(int count, char *file)
{
options.keysFilename = file;
return true;
}
BOOL CLI::SetNumberOption(int count, char *num)
{
int nKeys;
if (!sscanf(num, "%d", &nKeys))
{
return false;
}
options.numKeys = nKeys;
return true;
}
/**
*
* @param count
* @param channum
* @return
*/
BOOL CLI::SetChannelIDOption(int count, char *channum)
{
int siteID;
if (!sscanf(channum, "%d", &siteID))
{
return false;
}
// channel ids must be between 000 and 999
if (siteID > 999)
{
fmt::print("ERROR: refusing to create a key with a Channel ID greater than 999\n");
return false;
}
options.channelID = siteID;
return true;
}
BOOL CLI::SetBINKOption(int count, char *bink)
{
options.binkid = bink;
return true;
}
/**
*
* @param count
* @param arg
* @return
*/
BOOL CLI::SetSerialOption(int count, char *arg)
{
int serial_val;
if (!sscanf(arg, "%d", &serial_val))
{
return false;
}
// serials must be between 000000 and 999999
if (serial_val > 999999)
{
fmt::print("ERROR: refusing to create a key with a Serial not between 000000 and 999999\n");
return false;
}
options.serialSet = true;
options.serial = serial_val;
return true;
}
BOOL CLI::SetActivationIDOption(int count, char *aid)
{
options.instid = aid;
options.state = STATE_CONFIRMATION_ID;
return true;
}
BOOL CLI::SetProductIDOption(int count, char *product)
{
if (options.verbose)
{
fmt::print("Setting product ID to {}", product);
}
options.productid = product;
return true;
}
BOOL CLI::SetValidateOption(int count, char *productID)
{
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;
}

40
src/rc.cpp Normal file
View File

@ -0,0 +1,40 @@
/**
* This file is a part of the UMSKT Project
*
* Copyleft (C) 2019-2024 UMSKT Contributors (et.al.)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @FileCreated by Neo on 01/07/2024
* @Maintainer Neo
*/
#include "cli.h"
#include <cmrc/cmrc.hpp>
CMRC_DECLARE(umskt);
/**
* load a cmrc embedded JSON file
*
* @return success
*/
BOOL CLI::loadEmbeddedJSON()
{
auto fs = cmrc::umskt::get_filesystem();
auto jsonFile = fs.open("keys.json");
keys = json::parse(jsonFile, nullptr, false, false);
return true;
}

View File

@ -23,8 +23,19 @@
#ifndef UMSKT_TYPEDEFS_H
#define UMSKT_TYPEDEFS_H
#if defined(_MSC_VER)
#define WIN32_LEAN_AND_MEAN
#include <intrin.h>
#include <windows.h>
#endif // defined(WIN32)
#include <algorithm>
#include <cstdbool>
#include <cstdint>
#include <map>
#include <vector>
#ifdef DEBUG
#include <cassert>
@ -32,53 +43,76 @@
#define assert(x) /* do nothing */
#endif
#ifdef _MSC_VER
#define EXPORT extern "C" __declspec(dllexport)
#define INLINE __forceinline
#if defined(_MSC_VER)
#define FNEXPORT __declspec(dllexport)
#define FNIMPORT __declspec(dllimport)
#define FNINLINE __forceinline
#elif defined(__GNUC__)
#define EXPORT extern "C" __attribute__((visibility("default")))
#define INLINE __attribute__((always_inline))
#define FNEXPORT __attribute__((visibility("default")))
#define FNIMPORT __attribute__((visibility("default")))
#define FNINLINE inline __attribute__((__always_inline__))
#elif defined(__CLANG__)
#if __has_attribute(__always_inline__)
#define forceinline inline __attribute__((__always_inline__))
#else
#define EXPORT extern "C"
#define INLINE
#define forceinline inline
#endif // __has_attribute(__always_inline__)
#else
#define FNEXPORT
#define FNINLINE
#warning "function inlining not handled"
#endif
#endif // defined(_MSC_VER)
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#define FNEXPORT EMSCRIPTEN_KEEPALIVE EXPORT
#define EXPORT EMSCRIPTEN_KEEPALIVE FNEXPORT
#define INLINE FNINLINE
#else
#define FNEXPORT EXPORT
#endif
#ifdef UMSKT_IMPORT_LIB
#define EXPORT FNIMPORT
#else
#define EXPORT FNEXPORT
#endif // ifdef UMSKT_IMPORT_LIB
#define INLINE FNINLINE
#endif // ifdef __EMSCRIPTEN__
// POSIX <-> Windows compatability layer, because MS just *had* to be different
#ifdef _MSC_VER
#ifndef strncasecmp
#define strncasecmp _strnicmp
#ifndef _sscanf
#define _sscanf sscanf_s
#endif
#ifndef strcasecmp
#define strcasecmp _stricmp
#ifndef _strncpy
#define _strncpy strncpy_s
#endif
#ifndef strcmp
#define strcmp strcmp_s
#endif
#ifndef sscanf
#define sscanf sscanf_s
#ifndef _strcpy
#define _strcpy strcpy_s
#endif
#else
#define _sscanf sscanf
#define _strncpy(x, y, z, w) strncpy(x, z, w)
#define _strcpy strcpy
#endif // ifdef _MSC_VER
// Type definitions now with more windows compatability (unfortunately)
using BOOL = int32_t;
using BYTE = uint8_t;
using WORD = uint16_t;
using DWORD = unsigned long;
using QWORD = uint64_t;
#if defined(_M_ARM) // for Windows on ARM ??
using __m128 = __n128;
#endif
// Type definitions
typedef bool BOOL;
typedef uint8_t BYTE;
typedef uint16_t WORD;
typedef uint32_t DWORD;
typedef uint64_t QWORD;
#ifdef __SIZEOF_INT128__
typedef unsigned __int128 OWORD;
#if defined(__SIZEOF_INT128__) || defined(__int128)
using uint128_t = unsigned __int128;
#else // use the intel-supplied __m128 intrisic
using uint128_t = __m128;
#endif
using OWORD = uint128_t;
typedef union {
// OWORD oword;
OWORD oword;
QWORD qword[2];
DWORD dword[4];
WORD word[8];

View File

@ -20,9 +20,8 @@
* @Maintainer Neo
*/
#define WIN32_LEAN_AND_MEAN
#include "../typedefs.h"
#include "resource.h"
#include <windows.h>
BOOLEAN WINAPI DllMain(IN HINSTANCE hDllHandle, IN DWORD nReason, IN LPVOID Reserved)
{

70
src/windows/platform.cpp Normal file
View File

@ -0,0 +1,70 @@
/**
* This file is a part of the UMSKT Project
*
* Copyleft (C) 2019-2024 UMSKT Contributors (et.al.)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @FileCreated by Neo on 01/07/2024
* @Maintainer Neo
*/
#include "../cli.h"
#include "resource.h"
/**
*
* @return success
*/
BOOL CLI::loadEmbeddedJSON()
{
HMODULE hModule = GetModuleHandle(nullptr);
// Find
HRSRC hResource = FindResource(hModule, MAKEINTRESOURCE(IDR_JSON1), "JSON");
if (hResource == nullptr)
{
fmt::print("ERROR: Could not find internal JSON resource?");
return false;
}
// Load
HGLOBAL hResourceData = LoadResource(hModule, hResource);
if (hResourceData == nullptr)
{
fmt::print("ERROR: Could not load internal JSON resource?");
return false;
}
// Lock
LPVOID pData = LockResource(hResourceData);
if (pData == nullptr)
{
fmt::print("ERROR: Could not lock internal JSON resource?");
return false;
}
try
{
keys = json::parse((char *)pData, nullptr, false, false);
}
catch (const json::exception &e)
{
fmt::print("ERROR: Exception occurred while parsing internal JSON file: {}\n", e.what());
}
FreeResource(hResourceData);
return true;
}

View File

@ -25,12 +25,14 @@
// Used by umskt.rc
//
#define IDI_ICON1 101
#define IDR_JSON1 102
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NO_MFC 1
#define _APS_NEXT_RESOURCE_VALUE 103
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101

Binary file not shown.