diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c6236b..f14a081 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,18 +19,20 @@ CPMAddPackage( VERSION 3.11.2 ) +CPMAddPackage("gh:fmtlib/fmt#7.1.3") + CONFIGURE_FILE(keys.json keys.json COPYONLY) #SET(BUILD_SHARED_LIBS OFF) #SET(CMAKE_EXE_LINKER_FLAGS "-static") ADD_EXECUTABLE(xpkey src/main.cpp src/xp.cpp src/key.cpp src/util.cpp src/cli.cpp) TARGET_INCLUDE_DIRECTORIES(xpkey PUBLIC crypto) -TARGET_LINK_LIBRARIES(xpkey PUBLIC OpenSSL::Crypto nlohmann_json::nlohmann_json) +TARGET_LINK_LIBRARIES(xpkey PUBLIC OpenSSL::Crypto nlohmann_json::nlohmann_json fmt) ADD_EXECUTABLE(srv2003key src/server.cpp src/key.cpp src/util.cpp src/cli.cpp) TARGET_INCLUDE_DIRECTORIES(srv2003key PUBLIC crypto) -TARGET_LINK_LIBRARIES(srv2003key PUBLIC OpenSSL::Crypto nlohmann_json::nlohmann_json) +TARGET_LINK_LIBRARIES(srv2003key PUBLIC OpenSSL::Crypto nlohmann_json::nlohmann_json fmt) ADD_EXECUTABLE(xpactivate src/confid.cpp) TARGET_INCLUDE_DIRECTORIES(xpactivate PUBLIC crypto) -TARGET_LINK_LIBRARIES(xpactivate PUBLIC OpenSSL::Crypto nlohmann_json::nlohmann_json) \ No newline at end of file +TARGET_LINK_LIBRARIES(xpactivate PUBLIC OpenSSL::Crypto nlohmann_json::nlohmann_json fmt) \ No newline at end of file diff --git a/src/cli.cpp b/src/cli.cpp index 51f504a..cc15721 100644 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -4,20 +4,36 @@ #include "header.h" -void showHelp(char *argv[]) { - std::cout << "usage: " << argv[0] << std::endl << std::endl - << "\t-h --help\tshow this message" << std::endl - << "\t-v --verbose\tenable verbose output" << std::endl - << "\t-b --binkid\tspecify which BINK identifier to load (defaults to 2E)" << std::endl - << "\t-l --list\tshow which products/binks can be loaded" << std::endl - << "\t-c --channelid\tspecify which Channel Identifier to use (defaults to 640)" << std::endl - << std::endl << std::endl; +bool loadJSON(const fs::path& filename, json *output) { + if (!fs::exists(filename)) { + fmt::print("{} does not exist", filename.string()); + return false; + } + + std::ifstream f(filename); + *output = json::parse(f); + + return true; } -Options parseCommandLine(int argc, char* argv[]) { - Options options = { + +void showHelp(char *argv[]) { + fmt::print("usage: {} \n", argv[0]); + fmt::print("\t-h --help\tshow this message\n"); + fmt::print("\t-v --verbose\tenable verbose output\n"); + fmt::print("\t-f --file\tspecify which keys file to load (defaults to keys.json)\n"); + fmt::print("\t-b --binkid\tspecify which BINK identifier to load (defaults to 2E)\n"); + fmt::print("\t-l --list\tshow which products/binks can be loaded\n"); + fmt::print("\t-c --channelid\tspecify which Channel Identifier to use (defaults to 640)\n"); + fmt::print("\n\n"); +} + +int parseCommandLine(int argc, char* argv[], Options* options) { + // set default options + *options = Options { "2E", 640, + "keys.json", false, false, false @@ -27,28 +43,84 @@ Options parseCommandLine(int argc, char* argv[]) { std::string arg = argv[i]; if (arg == "-v" || arg == "--verbose") { - options.verbose = true; + options->verbose = true; } else if (arg == "-h" || arg == "--help") { - options.help = true; + options->help = true; } else if (arg == "-b" || arg == "--bink") { - options.binkid = argv[i+1]; + options->binkid = argv[i+1]; i++; } else if (arg == "-l" || arg == "--list") { - options.list = true; + options->list = true; } else if (arg == "-c" || arg == "--channelid") { int siteID; if (!sscanf(argv[i+1], "%d", &siteID)) { - options.error = true; + options->error = true; } else { - options.channelID = siteID; + options->channelID = siteID; } i++; + } else if (arg == "-f" || arg == "--file") { + options->keysFilename = argv[i+1]; + i++; } else { - options.error = true; + options->error = true; } } - return options; + return !options->error; +} + +int validateCommandLine(Options* options, char *argv[], json *keys) { + if (options->help || options->error) { + if (options->error) { + fmt::print("error parsing command line options\n"); + } + showHelp(argv); + return 1; + } + + if (options->verbose) { + fmt::print("loading {}\n", options->keysFilename); + } + + if (!loadJSON(options->keysFilename, keys)) { + return 2; + } + + if (options->verbose) { + fmt::print("loaded {} successfully\n",options->keysFilename); + } + + if (options->list) { + for (auto el : (*keys)["Products"].items()) { + int id; + sscanf((el.value()["BINK"][0]).get().c_str(), "%x", &id); + if (id >= 0x50) { + continue; + } + std::cout << el.key() << ": " << el.value()["BINK"] << std::endl; + } + + fmt::print("\n\n"); + fmt::print("** Please note: any BINK ID other than 2E is considered experimental at this time **\n"); + fmt::print("\n"); + return 1; + } + + int intBinkID; + sscanf(options->binkid.c_str(), "%x", &intBinkID); + + if (intBinkID >= 0x50) { + std::cout << "ERROR: BINK2002 and beyond is not supported in this application at this time" << std::endl; + return 1; + } + + if (options->channelID > 999) { + std::cout << "ERROR: refusing to create a key with a siteID greater than 999" << std::endl; + return 1; + } + + return 0; } void print_product_id(DWORD *pid) @@ -57,7 +129,7 @@ void print_product_id(DWORD *pid) char b[6], c[8]; int i, digit = 0; - // Cut a away last bit of pid and convert it to an accii-number (=raw) + // Cut away last bit of pid and convert it to an accii-number (=raw) sprintf(raw, "%iu", pid[0] >> 1); // Make b-part {640-....} @@ -66,7 +138,7 @@ void print_product_id(DWORD *pid) // Make c-part {...-123456X...} strcpy(c, raw + 3); - printf("> %s\n", c); + fmt::print("> {}\n", c); // Make checksum digit-part {...56X-} assert(strlen(c) == 6); @@ -78,14 +150,17 @@ void print_product_id(DWORD *pid) c[6] = digit + '0'; c[7] = 0; - printf("Product ID: PPPPP-%s-%s-23xxx\n", b, c); + fmt::print("Product ID: PPPPP-{}-{}-23xxx\n", b, c); } void print_product_key(char *pk) { - int i; assert(strlen(pk) == 25); - for (i = 0; i < 25; i++) { - putchar(pk[i]); - if (i != 24 && i % 5 == 4) putchar('-'); - } + + std::string spk = pk; + fmt::print("{}-{}-{}-{}-{}", + spk.substr(0,5), + spk.substr(5,5), + spk.substr(10,5), + spk.substr(15,5), + spk.substr(20,5)); } \ No newline at end of file diff --git a/src/header.h b/src/header.h index afe0a4c..f1689a8 100644 --- a/src/header.h +++ b/src/header.h @@ -19,10 +19,12 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -52,6 +54,9 @@ #define BYDWORD(n) (DWORD)(*((n) + 0) | *((n) + 1) << 8 | *((n) + 2) << 16 | *((n) + 3) << 24) #define BITMASK(n) ((1ULL << (n)) - 1) +using json = nlohmann::json; +namespace fs = std::filesystem; + // Confirmation ID generator constants #define SUCCESS 0 #define ERR_TOO_SHORT 1 @@ -98,14 +103,17 @@ void print_product_id(DWORD *pid); struct Options { std::string binkid; int channelID; + std::string keysFilename; bool verbose; bool help; bool list; bool error; }; -Options parseCommandLine(int argc, char* argv[]); -void showHelp(char *argv[]); +int parseCommandLine(int argc, char* argv[], Options* output); +int validateCommandLine(Options* options, char* argv[], json* output); +void showHelp(char* argv[]); +bool loadJSON(const fs::path& filename, json *output); // xp.cpp bool verifyXPKey( diff --git a/src/main.cpp b/src/main.cpp index 1e5a9a4..40df42d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,58 +5,17 @@ #include "header.h" char pCharset[] = "BCDFGHJKMPQRTVWXY2346789"; -const std::string filename = "keys.json"; - -using json = nlohmann::json; int main(int argc, char *argv[]) { - Options options = parseCommandLine(argc, argv); + Options options; - if (options.help || options.error) { - if (options.error) { - std::cout << "error parsing command line options" << std::endl; - } - showHelp(argv); - return 0; + if (!parseCommandLine(argc, argv, &options)) { + fmt::print("error parsing command line\n"); + return !options.error ? 0 : 1; } - if (options.verbose) { - std::cout << "loading " << filename << std::endl; - } - - std::ifstream f(filename); - json keys = json::parse(f); - - if (options.verbose) { - std::cout << "loaded " << filename << " successfully" << std::endl; - } - - if (options.list) { - for (auto el : keys["Products"].items()) { - int id; - sscanf((el.value()["BINK"][0]).get().c_str(), "%x", &id); - if (id >= 0x50) { - continue; - } - std::cout << el.key() << ": " << el.value()["BINK"] << std::endl; - } - - std::cout << std::endl << std::endl - << "** Please note: any BINK ID other than 2E is considered experimental at this time **" - << std::endl; - return 0; - } - - int intBinkID; - sscanf(options.binkid.c_str(), "%x", &intBinkID); - - if (intBinkID >= 0x50) { - std::cout << "ERROR: BINK2002 and beyond is not supported in this application at this time" << std::endl; - return 1; - } - - if (options.channelID > 999) { - std::cout << "ERROR: refusing to create a key with a siteID greater than 999" << std::endl; + json keys; + if (validateCommandLine(&options, argv, &keys) < 0) { return 1; } @@ -75,19 +34,18 @@ int main(int argc, char *argv[]) { BN_dec2bn(&privateKey, keys["BINK"][BINKID]["priv"].get().c_str()); if (options.verbose) { - std::cout << "-----------------------------------------------------------" << std::endl - << "Loaded the following curve constraints: BINK[" << BINKID << "]" << std::endl - << "-----------------------------------------------------------" << std::endl - << " P: " << keys["BINK"][BINKID]["p"].get() << std::endl - << " a: " << keys["BINK"][BINKID]["a"].get() << std::endl - << " b: " << keys["BINK"][BINKID]["b"].get() << std::endl - << "Gx: " << keys["BINK"][BINKID]["g"]["x"].get() << std::endl - << "Gy: " << keys["BINK"][BINKID]["g"]["y"].get() << std::endl - << "Kx: " << keys["BINK"][BINKID]["pub"]["x"].get() << std::endl - << "Ky: " << keys["BINK"][BINKID]["pub"]["y"].get() << std::endl - << " n: " << keys["BINK"][BINKID]["n"].get() << std::endl - << " k: " << keys["BINK"][BINKID]["priv"].get() << std::endl - << std::endl << std::endl; + fmt::print("----------------------------------------------------------- \n"); + fmt::print("Loaded the following curve constraints: BINK[{}]\n", BINKID); + fmt::print("----------------------------------------------------------- \n"); + fmt::print(" P: {}\n", keys["BINK"][BINKID]["p"].get()); + fmt::print(" a: {}\n", keys["BINK"][BINKID]["a"].get()); + fmt::print(" b: {}\n", keys["BINK"][BINKID]["b"].get()); + fmt::print("Gx: {}\n", keys["BINK"][BINKID]["g"]["x"].get()); + fmt::print("Gy: {}\n", keys["BINK"][BINKID]["g"]["y"].get()); + fmt::print("Kx: {}\n", keys["BINK"][BINKID]["pub"]["x"].get()); + fmt::print("Ky: {}\n", keys["BINK"][BINKID]["pub"]["y"].get()); + fmt::print(" n: {}\n", keys["BINK"][BINKID]["n"].get()); + fmt::print(" k: {}\n", keys["BINK"][BINKID]["priv"].get()); } EC_POINT *genPoint, *pubPoint; @@ -118,7 +76,7 @@ int main(int argc, char *argv[]) { nRaw += (oRaw &= 0xF423F); // ensure our serial is less than 999999 if (options.verbose) { - std::cout << "> PID: " << std::setw(9) << std::setfill('0') << nRaw << std::endl; + fmt::print("> PID: {:09d}\n", nRaw); } // generate a key @@ -129,13 +87,13 @@ int main(int argc, char *argv[]) { for (int i = 0; i < total; i++) { generateXPKey(eCurve, genPoint, genOrder, privateKey, nRaw, pKey); print_product_key(pKey); - std::cout << std::endl << std::endl; + fmt::print("\n\n"); // verify the key count += verifyXPKey(eCurve, genPoint, pubPoint, pKey); } - std::cout << "Success count: " << std::dec << count << "/" << total << std::endl; + fmt::print("Success count: {}/{}\n", count, total); return 0; } diff --git a/src/xp.cpp b/src/xp.cpp index b9b89d3..61c748b 100644 --- a/src/xp.cpp +++ b/src/xp.cpp @@ -219,10 +219,10 @@ void generateXPKey( // Pack product key. packXP(pRaw, pSerial, pHash, pSignature); - std::cout << " Serial: 0x" << std::hex << std::setw(8) << std::setfill('0') << pSerial << std::endl - << " Hash: 0x" << std::hex << std::setw(8) << std::setfill('0') << pHash << std::endl - << " Signature: 0x" << std::hex << std::setw(8) << std::setfill('0') << pSignature << std::endl - << std::endl; + fmt::print(" Serial: 0x{:08x}\n", pSerial); + fmt::print(" Hash: 0x{:08x}\n", pHash); + fmt::print(" Signature: 0x{:08x}\n", pSignature); + fmt::print("\n"); EC_POINT_free(r); } while (pRaw[1] > BITMASK(50));