mirror of
https://github.com/Neo-Desktop/WindowsXPKg
synced 2024-11-21 21:31:01 +02:00
WIP more rewrite/rework changes, major refactoring
This commit is contained in:
parent
783a5afb3a
commit
3e51fe1c8b
@ -1,3 +1,2 @@
|
|||||||
build*/
|
build*/
|
||||||
cmake-build*/
|
cmake-*/
|
||||||
djgpp-build*/
|
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -66,7 +66,7 @@ umskt
|
|||||||
# *.ipr
|
# *.ipr
|
||||||
|
|
||||||
# CMake
|
# CMake
|
||||||
cmake-build-*/
|
cmake-*/
|
||||||
|
|
||||||
# Mongo Explorer plugin
|
# Mongo Explorer plugin
|
||||||
.idea/**/mongoSettings.xml
|
.idea/**/mongoSettings.xml
|
||||||
|
@ -20,6 +20,6 @@
|
|||||||
|
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
- 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:
|
hooks:
|
||||||
- id: clang-format
|
- id: clang-format
|
@ -23,8 +23,9 @@ PROJECT(UMSKT)
|
|||||||
|
|
||||||
SET(CMAKE_CXX_STANDARD 17)
|
SET(CMAKE_CXX_STANDARD 17)
|
||||||
SET(CMAKE_OSX_SYSROOT "macosx" CACHE PATH "macOS SDK path")
|
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(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(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)
|
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")
|
MESSAGE(STATUS "[UMSKT] Requesting static version of OpenSSL")
|
||||||
ENDIF ()
|
ENDIF ()
|
||||||
|
|
||||||
|
|
||||||
IF (DJGPP_WATT32)
|
IF (DJGPP_WATT32)
|
||||||
SET(CMAKE_SYSTEM_NAME MSDOS)
|
SET(CMAKE_SYSTEM_NAME MSDOS)
|
||||||
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
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 we're compiling with MSVC, respect the DEBUG compile option
|
||||||
IF (MSVC)
|
IF (MSVC)
|
||||||
SET(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
SET(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||||
IF (NOT BUILD_SHARED_LIBS)
|
|
||||||
SET(CMAKE_CXX_FLAGS_RELEASE "/MT")
|
SET(CMAKE_CXX_FLAGS_RELEASE "/MT")
|
||||||
SET(CMAKE_CXX_FLAGS_DEBUG "/MTd")
|
SET(CMAKE_CXX_FLAGS_DEBUG "/MTd")
|
||||||
ELSE ()
|
|
||||||
SET(CMAKE_CXX_FLAGS_RELEASE "/MD")
|
|
||||||
SET(CMAKE_CXX_FLAGS_DEBUG "/MDd")
|
|
||||||
ENDIF ()
|
|
||||||
SET(CMAKE_EXE_LINKER_FLAGS "/INCREMENTAL:NO /NODEFAULTLIB:MSVCRT")
|
SET(CMAKE_EXE_LINKER_FLAGS "/INCREMENTAL:NO /NODEFAULTLIB:MSVCRT")
|
||||||
SET(CMAKE_ENABLE_EXPORTS ON)
|
SET(CMAKE_ENABLE_EXPORTS ON)
|
||||||
SET(UMSKT_EXE_WINDOWS_EXTRA src/windows/umskt.rc)
|
SET(UMSKT_EXE_WINDOWS_EXTRA src/windows/umskt.rc)
|
||||||
@ -151,15 +146,15 @@ INCLUDE(${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake)
|
|||||||
CPMAddPackage(
|
CPMAddPackage(
|
||||||
NAME nlohmann_json
|
NAME nlohmann_json
|
||||||
GITHUB_REPOSITORY nlohmann/json
|
GITHUB_REPOSITORY nlohmann/json
|
||||||
VERSION 3.11.2
|
VERSION 3.11.3
|
||||||
)
|
)
|
||||||
|
|
||||||
# Include fmt development library
|
# Include fmt development library
|
||||||
CPMAddPackage(
|
CPMAddPackage(
|
||||||
NAME fmt
|
NAME fmt
|
||||||
GITHUB_REPOSITORY fmtlib/fmt
|
GITHUB_REPOSITORY fmtlib/fmt
|
||||||
GIT_TAG 10.0.0
|
GIT_TAG 10.2.0
|
||||||
VERSION 10.0.0
|
VERSION 10.2.0
|
||||||
)
|
)
|
||||||
|
|
||||||
# Include cmrc resource compiler
|
# 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_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_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(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
|
#### Separate Build Path for emscripten
|
||||||
IF (EMSCRIPTEN)
|
IF (EMSCRIPTEN)
|
||||||
ADD_EXECUTABLE(umskt ${UMSKT_CLI_SRC} ${LIBUMSKT_SRC})
|
ADD_EXECUTABLE(umskt ${UMSKT_CLI_SRC} ${LIBUMSKT_SRC})
|
||||||
TARGET_INCLUDE_DIRECTORIES(umskt PUBLIC ${OPENSSL_INCLUDE_DIR})
|
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(CMAKE_EXECUTABLE_SUFFIX ".html")
|
||||||
|
|
||||||
SET_TARGET_PROPERTIES(umskt PROPERTIES COMPILE_FLAGS "-Os -sEXPORTED_RUNTIME_METHODS=ccall,cwrap")
|
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")
|
SET_TARGET_PROPERTIES(umskt PROPERTIES LINK_FLAGS "-Os -sWASM=1 -sEXPORT_ALL=1 -sEXPORTED_RUNTIME_METHODS=ccall,cwrap --no-entry")
|
||||||
ELSE ()
|
ELSE ()
|
||||||
ADD_LIBRARY(_umskt ${LIBUMSKT_SRC} ${UMSKT_EXE_WINDOWS_EXTRA} ${UMSKT_EXE_WINDOWS_DLL})
|
## umskt.so/.dll creation
|
||||||
TARGET_INCLUDE_DIRECTORIES(_umskt PUBLIC ${OPENSSL_INCLUDE_DIR})
|
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_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
|
### UMSKT executable compilation
|
||||||
ADD_EXECUTABLE(umskt ${UMSKT_CLI_SRC} ${UMSKT_EXE_WINDOWS_EXTRA})
|
ADD_EXECUTABLE(umskt ${UMSKT_CLI_SRC} ${UMSKT_EXE_WINDOWS_EXTRA})
|
||||||
TARGET_INCLUDE_DIRECTORIES(umskt PUBLIC ${OPENSSL_INCLUDE_DIR})
|
TARGET_INCLUDE_DIRECTORIES(umskt PUBLIC ${OPENSSL_INCLUDE_DIR} ${CMAKE_BINARY_DIR})
|
||||||
TARGET_LINK_LIBRARIES(umskt _umskt ${OPENSSL_CRYPTO_LIBRARIES} ${ZLIB_LIBRARIES} fmt nlohmann_json::nlohmann_json umskt::rc ${UMSKT_LINK_LIBS})
|
TARGET_LINK_LIBRARIES(umskt PUBLIC umskt_static ${UMSKT_LINK_LIBS} nlohmann_json::nlohmann_json)
|
||||||
TARGET_LINK_DIRECTORIES(umskt PUBLIC ${UMSKT_LINK_DIRS})
|
TARGET_LINK_DIRECTORIES(umskt PUBLIC ${UMSKT_LINK_DIRS})
|
||||||
IF (MSVC AND 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})
|
SET_PROPERTY(TARGET umskt APPEND PROPERTY LINK_FLAGS /STUB:${MSVC_MSDOS_STUB})
|
||||||
|
ENDIF()
|
||||||
ENDIF ()
|
ENDIF ()
|
||||||
|
|
||||||
IF (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
IF (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||||
|
@ -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.
|
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.
|
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.
|
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.
|
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.
|
||||||
|
|
||||||
|
672
src/cli.cpp
672
src/cli.cpp
@ -26,11 +26,17 @@
|
|||||||
Options CLI::options;
|
Options CLI::options;
|
||||||
json CLI::keys;
|
json CLI::keys;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param argcIn
|
||||||
|
* @param argvIn
|
||||||
|
* @return exit status (where success is 0)
|
||||||
|
*/
|
||||||
BYTE CLI::Init(int argcIn, char **argvIn)
|
BYTE CLI::Init(int argcIn, char **argvIn)
|
||||||
{
|
{
|
||||||
// set default options
|
// set default options
|
||||||
options = {argcIn, argvIn, "", "", "", "", "", "", "WINXPPVLK", 0,
|
options = {argcIn, argvIn, "2E", "", "", "", "", "", "WINXP", "PROVLK", 0,
|
||||||
0, 1, false, false, false, false, false, false, false, STATE_BINK1998_GENERATE};
|
0, 1, false, false, false, false, false, false, false, PIDGEN_3, STATE_PIDGEN_GENERATE};
|
||||||
|
|
||||||
SetHelpText();
|
SetHelpText();
|
||||||
|
|
||||||
@ -40,6 +46,13 @@ BYTE CLI::Init(int argcIn, char **argvIn)
|
|||||||
return options.error;
|
return options.error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if we displayed help, without an error
|
||||||
|
// return success
|
||||||
|
if (options.help)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
success = processOptions();
|
success = processOptions();
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
@ -56,13 +69,37 @@ BYTE CLI::Init(int argcIn, char **argvIn)
|
|||||||
*/
|
*/
|
||||||
BOOL CLI::loadJSON(const fs::path &filename)
|
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());
|
fmt::print("ERROR: File {} does not exist\n", filename.string());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (fs::exists(filename))
|
|
||||||
|
if (options.verbose)
|
||||||
{
|
{
|
||||||
|
fmt::print("Loading keys file {}\n", options.keysFilename);
|
||||||
|
}
|
||||||
|
|
||||||
std::ifstream f(filename);
|
std::ifstream f(filename);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -71,13 +108,7 @@ BOOL CLI::loadJSON(const fs::path &filename)
|
|||||||
catch (const json::exception &e)
|
catch (const json::exception &e)
|
||||||
{
|
{
|
||||||
fmt::print("ERROR: Exception thrown while parsing {}: {}\n", filename.string(), e.what());
|
fmt::print("ERROR: Exception thrown while parsing {}: {}\n", filename.string(), e.what());
|
||||||
}
|
return false;
|
||||||
}
|
|
||||||
else if (filename.empty())
|
|
||||||
{
|
|
||||||
cmrc::embedded_filesystem fs = cmrc::umskt::get_filesystem();
|
|
||||||
cmrc::file jsonFile = fs.open("keys.json");
|
|
||||||
keys = json::parse(jsonFile, nullptr, false, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keys.is_discarded())
|
if (keys.is_discarded())
|
||||||
@ -86,215 +117,54 @@ BOOL CLI::loadJSON(const fs::path &filename)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.verbose)
|
||||||
|
{
|
||||||
|
fmt::print("Loaded keys from {} successfully\n", options.keysFilename);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
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)
|
void CLI::printID(DWORD *pid)
|
||||||
{
|
{
|
||||||
char raw[12], b[6], c[8];
|
char raw[12], b[6], c[8];
|
||||||
char i, digit = 0;
|
char i, digit = 0;
|
||||||
|
|
||||||
// Convert PID to ascii-number (=raw)
|
// Convert PID to ascii-number (=raw)
|
||||||
snprintf(raw, sizeof(raw), "%09u", pid[0]);
|
snprintf(raw, sizeof(raw), "%09lu", pid[0]);
|
||||||
|
|
||||||
// Make b-part {640-....}
|
// Make b-part {640-....}
|
||||||
strncpy(b, raw, 3);
|
_strncpy(b, 6, &raw[0], 3);
|
||||||
b[3] = 0;
|
b[3] = 0;
|
||||||
|
|
||||||
// Make c-part {...-123456X...}
|
// Make c-part {...-123456X...}
|
||||||
strcpy(c, raw + 3);
|
_strcpy(c, &raw[3]);
|
||||||
|
|
||||||
// Make checksum digit-part {...56X-}
|
// Make checksum digit-part {...56X-}
|
||||||
assert(strlen(c) == 6);
|
assert(strlen(c) == 6);
|
||||||
for (i = 0; i < 6; i++)
|
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[6] = digit + '0';
|
||||||
c[7] = 0;
|
c[7] = 0;
|
||||||
|
|
||||||
fmt::print("> Product ID: PPPPP-{}-{}-BBxxx\n", b, c);
|
DWORD binkid;
|
||||||
}
|
_sscanf(options.binkID.c_str(), "%lx", &binkid);
|
||||||
|
binkid /= 2;
|
||||||
|
|
||||||
/**
|
fmt::print("> Product ID: PPPPP-{}-{}-{}xxx\n", b, c, binkid);
|
||||||
*
|
|
||||||
* @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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -302,32 +172,35 @@ BOOL CLI::stripKey(const std::string &in_key, std::string &out_key)
|
|||||||
* @param pidgen3
|
* @param pidgen3
|
||||||
* @return success
|
* @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];
|
auto bink = keys["BINK"][BINKID];
|
||||||
|
|
||||||
if (options.verbose)
|
if (options.verbose)
|
||||||
{
|
{
|
||||||
fmt::print("----------------------------------------------------------- \n");
|
fmt::print("{:->80}\n", "");
|
||||||
fmt::print("Loaded the following elliptic curve parameters: BINK[{}]\n", BINKID);
|
fmt::print("Loaded the following elliptic curve parameters: BINK[{}]\n", BINKID);
|
||||||
fmt::print("----------------------------------------------------------- \n");
|
fmt::print("{:->80}\n", "");
|
||||||
fmt::print(" P: {}\n", bink["p"].get<std::string>());
|
fmt::print("{:>6}: {}\n", "P", bink["p"]);
|
||||||
fmt::print(" a: {}\n", bink["a"].get<std::string>());
|
fmt::print("{:>6}: {}\n", "a", bink["a"]);
|
||||||
fmt::print(" b: {}\n", bink["b"].get<std::string>());
|
fmt::print("{:>6}: {}\n", "b", bink["b"]);
|
||||||
fmt::print("Gx: {}\n", bink["g"]["x"].get<std::string>());
|
fmt::print("{:>6}: [{},\n{:>9}{}]\n", "G[x,y]", bink["g"]["x"], "", bink["g"]["y"]);
|
||||||
fmt::print("Gy: {}\n", bink["g"]["y"].get<std::string>());
|
fmt::print("{:>6}: [{},\n{:>9}{}]\n", "K[x,y]", bink["pub"]["x"], "", bink["pub"]["y"]);
|
||||||
fmt::print("Kx: {}\n", bink["pub"]["x"].get<std::string>());
|
fmt::print("{:>6}: {}\n", "n", bink["n"]);
|
||||||
fmt::print("Ky: {}\n", bink["pub"]["y"].get<std::string>());
|
fmt::print("{:>6}: {}\n", "k", bink["priv"]);
|
||||||
fmt::print(" n: {}\n", bink["n"].get<std::string>());
|
|
||||||
fmt::print(" k: {}\n", bink["priv"].get<std::string>());
|
|
||||||
fmt::print("\n");
|
fmt::print("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
pidgen3->LoadEllipticCurve(bink["p"], bink["a"], bink["b"], bink["g"]["x"], bink["g"]["y"], bink["pub"]["x"],
|
pidgen3.LoadEllipticCurve(bink["p"], bink["a"], bink["b"], bink["g"]["x"], bink["g"]["y"], bink["pub"]["x"],
|
||||||
bink["pub"]["y"], bink["n"], bink["priv"]);
|
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)
|
if (options.verbose)
|
||||||
{
|
{
|
||||||
fmt::print("> Channel ID: {:03d}\n", options.channelID);
|
fmt::print("> Channel ID: {:03d}\n", options.channelID);
|
||||||
@ -335,7 +208,7 @@ BOOL CLI::InitPIDGEN3(PIDGEN3 *pidgen3)
|
|||||||
|
|
||||||
if (options.serialSet)
|
if (options.serialSet)
|
||||||
{
|
{
|
||||||
options.info.setSerial(options.serial);
|
pidgen3.info.setSerial(options.serial);
|
||||||
if (options.verbose)
|
if (options.verbose)
|
||||||
{
|
{
|
||||||
fmt::print("> Serial {:#09d}\n", options.serial);
|
fmt::print("> Serial {:#09d}\n", options.serial);
|
||||||
@ -350,270 +223,158 @@ BOOL CLI::InitPIDGEN3(PIDGEN3 *pidgen3)
|
|||||||
* @param confid
|
* @param confid
|
||||||
* @return success
|
* @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
|
* @return success
|
||||||
*/
|
*/
|
||||||
BOOL CLI::BINK1998Generate()
|
BOOL CLI::PIDGENGenerate()
|
||||||
{
|
{
|
||||||
|
// TODO:
|
||||||
|
// if options.pidgen2generate
|
||||||
|
// return pidgen2generate
|
||||||
|
// otherwise...
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
fmt::print("Detected a BINK1998 key\n");
|
||||||
|
}
|
||||||
|
|
||||||
auto bink1998 = BINK1998();
|
auto bink1998 = BINK1998();
|
||||||
BOOL retval = InitPIDGEN3(&bink1998);
|
InitPIDGEN3(bink1998);
|
||||||
if (!retval)
|
return BINK1998Generate(bink1998);
|
||||||
{
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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++)
|
|
||||||
{
|
|
||||||
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
|
else
|
||||||
{
|
{
|
||||||
if (options.verbose)
|
if (options.verbose)
|
||||||
{
|
{
|
||||||
printKey(pKey);
|
fmt::print("Detected a BINK2002 key\n");
|
||||||
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();
|
auto bink2002 = BINK2002();
|
||||||
InitPIDGEN3(&bink2002);
|
InitPIDGEN3(bink2002);
|
||||||
|
return BINK2002Generate(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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bink2002.Generate(options.info, pKey);
|
|
||||||
|
|
||||||
bool isValid = bink2002.Verify(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
|
* @return isValid
|
||||||
*/
|
*/
|
||||||
BOOL CLI::BINK1998Validate()
|
BOOL CLI::PIDGENValidate()
|
||||||
{
|
{
|
||||||
|
// TODO:
|
||||||
|
// if options.pidgen2validate
|
||||||
|
// return pidgen2validate
|
||||||
|
// otherwise...
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
fmt::print("Detected a BINK1998 key\n");
|
||||||
|
}
|
||||||
auto bink1998 = BINK1998();
|
auto bink1998 = BINK1998();
|
||||||
InitPIDGEN3(&bink1998);
|
InitPIDGEN3(bink1998);
|
||||||
|
return BINK1998Validate(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;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
CLI::printKey(product_key);
|
|
||||||
fmt::print("\n");
|
|
||||||
if (!bink1998.Verify(product_key))
|
|
||||||
{
|
{
|
||||||
fmt::print("ERROR: Product key is invalid! Wrong BINK ID?\n");
|
if (options.verbose)
|
||||||
return false;
|
{
|
||||||
|
fmt::print("Detected a BINK2002 key\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::print("Key validated successfully!\n");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @return success
|
|
||||||
*/
|
|
||||||
BOOL CLI::BINK2002Validate()
|
|
||||||
{
|
|
||||||
auto bink2002 = BINK2002();
|
auto bink2002 = BINK2002();
|
||||||
InitPIDGEN3(&bink2002);
|
InitPIDGEN3(bink2002);
|
||||||
|
return BINK2002Validate(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Process application state
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
*
|
*
|
||||||
* @return application status code
|
* @return application status code
|
||||||
*/
|
*/
|
||||||
int CLI::Run()
|
int CLI::Run()
|
||||||
{
|
{
|
||||||
BINK1998Generate();
|
/**
|
||||||
/*
|
* TODO: we should be checking if the system's pseudorandom facilities work
|
||||||
switch (state)
|
* before attempting generation, validation does not require entropy
|
||||||
|
*/
|
||||||
|
switch (options.state)
|
||||||
{
|
{
|
||||||
case STATE_BINK1998_GENERATE:
|
case STATE_PIDGEN_GENERATE:
|
||||||
return BINK1998Generate();
|
return PIDGENGenerate();
|
||||||
|
|
||||||
case STATE_BINK2002_GENERATE:
|
case STATE_PIDGEN_VALIDATE:
|
||||||
return BINK2002Generate();
|
return PIDGENValidate();
|
||||||
|
|
||||||
case STATE_BINK1998_VALIDATE:
|
|
||||||
return BINK1998Validate();
|
|
||||||
|
|
||||||
case STATE_BINK2002_VALIDATE:
|
|
||||||
return BINK2002Validate();
|
|
||||||
|
|
||||||
case STATE_CONFIRMATION_ID:
|
case STATE_CONFIRMATION_ID:
|
||||||
return ConfirmationIDGenerate();
|
return ConfirmationIDGenerate();
|
||||||
@ -621,6 +382,51 @@ int CLI::Run()
|
|||||||
default:
|
default:
|
||||||
return 1;
|
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);
|
||||||
}
|
}
|
||||||
|
96
src/cli.h
96
src/cli.h
@ -32,30 +32,54 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <cmrc/cmrc.hpp>
|
#include <fmt/color.h>
|
||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
|
#include <fmt/ostream.h>
|
||||||
|
#include <fmt/ranges.h>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
namespace fs = std::filesystem;
|
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/confid/confid.h"
|
||||||
#include "libumskt/libumskt.h"
|
#include "libumskt/libumskt.h"
|
||||||
#include "libumskt/pidgen2/PIDGEN2.h"
|
#include "libumskt/pidgen2/PIDGEN2.h"
|
||||||
#include "libumskt/pidgen3/BINK1998.h"
|
#include "libumskt/pidgen3/BINK1998.h"
|
||||||
#include "libumskt/pidgen3/BINK2002.h"
|
#include "libumskt/pidgen3/BINK2002.h"
|
||||||
#include "libumskt/pidgen3/PIDGEN3.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
|
enum APPLICATION_STATE
|
||||||
{
|
{
|
||||||
STATE_BINK1998_GENERATE,
|
STATE_PIDGEN_GENERATE,
|
||||||
STATE_BINK2002_GENERATE,
|
STATE_PIDGEN_VALIDATE,
|
||||||
STATE_CONFIRMATION_ID,
|
STATE_CONFIRMATION_ID
|
||||||
STATE_BINK1998_VALIDATE,
|
};
|
||||||
STATE_BINK2002_VALIDATE
|
|
||||||
|
enum PIDGEN_VERSION
|
||||||
|
{
|
||||||
|
PIDGEN_2 = 2,
|
||||||
|
PIDGEN_3 = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Options
|
struct Options
|
||||||
@ -63,17 +87,19 @@ struct Options
|
|||||||
int argc;
|
int argc;
|
||||||
char **argv;
|
char **argv;
|
||||||
|
|
||||||
std::string binkid;
|
std::string binkID;
|
||||||
std::string keysFilename;
|
std::string keysFilename;
|
||||||
std::string instid;
|
std::string installationID;
|
||||||
std::string keyToCheck;
|
std::string keyToCheck;
|
||||||
std::string productid;
|
std::string productID;
|
||||||
std::string authInfo;
|
std::string authInfo;
|
||||||
std::string productCode;
|
std::string productCode;
|
||||||
|
std::string productFlavour;
|
||||||
|
|
||||||
DWORD channelID;
|
DWORD channelID;
|
||||||
DWORD serial;
|
DWORD serial;
|
||||||
DWORD numKeys;
|
DWORD numKeys;
|
||||||
|
|
||||||
BOOL oem;
|
BOOL oem;
|
||||||
BOOL upgrade;
|
BOOL upgrade;
|
||||||
BOOL serialSet;
|
BOOL serialSet;
|
||||||
@ -82,15 +108,27 @@ struct Options
|
|||||||
BOOL error;
|
BOOL error;
|
||||||
BOOL list;
|
BOOL list;
|
||||||
|
|
||||||
|
struct Meta
|
||||||
|
{
|
||||||
|
std::string type;
|
||||||
|
std::vector<std::string> tags;
|
||||||
|
struct Activation
|
||||||
|
{
|
||||||
|
std::string flavour;
|
||||||
|
int version;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
PIDGEN3::KeyInfo info;
|
PIDGEN3::KeyInfo info;
|
||||||
|
|
||||||
|
PIDGEN_VERSION pidgenversion;
|
||||||
APPLICATION_STATE state;
|
APPLICATION_STATE state;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CLI
|
class CLI
|
||||||
{
|
{
|
||||||
std::string pKey;
|
std::string pKey;
|
||||||
DWORD count, total;
|
DWORD count, total, iBinkID;
|
||||||
|
|
||||||
static Options options;
|
static Options options;
|
||||||
|
|
||||||
@ -108,6 +146,7 @@ class CLI
|
|||||||
static void SetHelpText();
|
static void SetHelpText();
|
||||||
|
|
||||||
static BOOL loadJSON(const fs::path &filename);
|
static BOOL loadJSON(const fs::path &filename);
|
||||||
|
static BOOL loadEmbeddedJSON();
|
||||||
|
|
||||||
static CLIHandlerFunc DisplayHelp;
|
static CLIHandlerFunc DisplayHelp;
|
||||||
static CLIHandlerFunc DisplayErrorMessage;
|
static CLIHandlerFunc DisplayErrorMessage;
|
||||||
@ -125,22 +164,43 @@ class CLI
|
|||||||
static CLIHandlerFunc SetProductIDOption;
|
static CLIHandlerFunc SetProductIDOption;
|
||||||
static CLIHandlerFunc SetValidateOption;
|
static CLIHandlerFunc SetValidateOption;
|
||||||
static CLIHandlerFunc SetProductCodeOption;
|
static CLIHandlerFunc SetProductCodeOption;
|
||||||
|
static CLIHandlerFunc SetFlavourOption;
|
||||||
|
|
||||||
static BOOL parseCommandLine();
|
static BOOL parseCommandLine();
|
||||||
static BOOL processOptions();
|
static BOOL processOptions();
|
||||||
static void printID(DWORD *pid);
|
static void printID(DWORD *pid);
|
||||||
static void printKey(std::string &pk);
|
static void printKey(std::string &pk);
|
||||||
static BOOL stripKey(const std::string &in_key, std::string &out_key);
|
static BOOL stripKey(const std::string &in_key, std::string &out_key);
|
||||||
|
static std::string validateInputKeyCharset(std::string &accumulator, char currentChar);
|
||||||
|
|
||||||
BOOL InitPIDGEN3(PIDGEN3 *pidgen3);
|
BOOL InitPIDGEN3(PIDGEN3 &pidgen3);
|
||||||
BOOL InitConfirmationID(ConfirmationID *confid);
|
BOOL InitConfirmationID(ConfirmationID &confid);
|
||||||
|
|
||||||
BOOL BINK1998Generate();
|
BOOL PIDGENGenerate();
|
||||||
BOOL BINK2002Generate();
|
BOOL PIDGENValidate();
|
||||||
BOOL BINK1998Validate();
|
|
||||||
BOOL BINK2002Validate();
|
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();
|
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();
|
int Run();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
271
src/generate.cpp
Normal file
271
src/generate.cpp
Normal 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
595
src/help.cpp
Normal 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;
|
||||||
|
}
|
@ -20,20 +20,23 @@
|
|||||||
* @Maintainer Neo
|
* @Maintainer Neo
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef UMSKT_OPTIONS_H
|
#ifndef UMSKT_HELP_H
|
||||||
#define UMSKT_OPTIONS_H
|
#define UMSKT_HELP_H
|
||||||
|
|
||||||
typedef BOOL CLIHandlerFunc(int, char *);
|
typedef BOOL CLIHandlerFunc(int, char *);
|
||||||
|
|
||||||
enum CLIHelpOptionIDs
|
enum CLIHelpOptionIDs
|
||||||
{
|
{
|
||||||
OPTION_HELP,
|
OPTION_HELP,
|
||||||
|
OPTION_HELP2,
|
||||||
|
OPTION_VERSION,
|
||||||
OPTION_VERBOSE,
|
OPTION_VERBOSE,
|
||||||
OPTION_DEBUG,
|
OPTION_DEBUG,
|
||||||
OPTION_FILE,
|
OPTION_FILE,
|
||||||
OPTION_LIST,
|
OPTION_LIST,
|
||||||
OPTION_NUMBER,
|
|
||||||
OPTION_PRODUCT,
|
OPTION_PRODUCT,
|
||||||
|
OPTION_FLAVOUR,
|
||||||
|
OPTION_NUMBER,
|
||||||
OPTION_OEM,
|
OPTION_OEM,
|
||||||
OPTION_UPGRADE,
|
OPTION_UPGRADE,
|
||||||
OPTION_ACTIVATIONID,
|
OPTION_ACTIVATIONID,
|
||||||
@ -57,4 +60,4 @@ struct CLIHelpOptions
|
|||||||
CLIHandlerFunc *handler;
|
CLIHandlerFunc *handler;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // UMSKT_OPTIONS_H
|
#endif // UMSKT_HELP_H
|
@ -29,6 +29,22 @@
|
|||||||
|
|
||||||
#include "confid.h"
|
#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,
|
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,
|
QWORD modulous, QWORD nonresidue, BOOL isOffice, BOOL isXPBrand,
|
||||||
BYTE flagVersion)
|
BYTE flagVersion)
|
||||||
@ -51,6 +67,17 @@ BOOL ConfirmationID::LoadHyperellipticCurve(QWORD x0, QWORD x1, QWORD x2, QWORD
|
|||||||
return true;
|
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 ConfirmationID::LoadHyperellipticCurve(QWORD *f, Q_OWORD priv, QWORD modulous, QWORD nonresidue, BOOL isOffice,
|
||||||
BOOL isXPBrand, BYTE flagVersion)
|
BOOL isXPBrand, BYTE flagVersion)
|
||||||
{
|
{
|
||||||
@ -66,6 +93,20 @@ BOOL ConfirmationID::LoadHyperellipticCurve(QWORD *f, Q_OWORD priv, QWORD modulo
|
|||||||
return true;
|
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 ConfirmationID::calculateCheckDigit(DWORD pid)
|
||||||
{
|
{
|
||||||
DWORD i = 0, j = 0, k = 0;
|
DWORD i = 0, j = 0, k = 0;
|
||||||
@ -78,6 +119,12 @@ DWORD ConfirmationID::calculateCheckDigit(DWORD pid)
|
|||||||
return ((10 * pid) - (i % 7)) + 7;
|
return ((10 * pid) - (i % 7)) + 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param iid
|
||||||
|
* @param hwid
|
||||||
|
* @param version
|
||||||
|
*/
|
||||||
void ConfirmationID::decode_iid_new_version(BYTE *iid, BYTE *hwid, DWORD *version)
|
void ConfirmationID::decode_iid_new_version(BYTE *iid, BYTE *hwid, DWORD *version)
|
||||||
{
|
{
|
||||||
QWORD buffer[5];
|
QWORD buffer[5];
|
||||||
@ -97,6 +144,13 @@ void ConfirmationID::decode_iid_new_version(BYTE *iid, BYTE *hwid, DWORD *versio
|
|||||||
*version = buffer[0] & 7;
|
*version = buffer[0] & 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param buffer
|
||||||
|
* @param bufSize
|
||||||
|
* @param key
|
||||||
|
* @param keySize
|
||||||
|
*/
|
||||||
void ConfirmationID::Mix(BYTE *buffer, BYTE bufSize, const BYTE *key, BYTE keySize)
|
void ConfirmationID::Mix(BYTE *buffer, BYTE bufSize, const BYTE *key, BYTE keySize)
|
||||||
{
|
{
|
||||||
BYTE sha1_input[64], sha1_result[20];
|
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)
|
void ConfirmationID::Unmix(BYTE *buffer, BYTE bufSize, const BYTE key[4], BYTE keySize)
|
||||||
{
|
{
|
||||||
BYTE sha1_input[64], sha1_result[20];
|
BYTE sha1_input[64], sha1_result[20];
|
||||||
@ -194,7 +255,7 @@ void ConfirmationID::Unmix(BYTE *buffer, BYTE bufSize, const BYTE key[4], BYTE k
|
|||||||
* @param productIDIn
|
* @param productIDIn
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
DWORD ConfirmationID::Generate(const std::string &installationIDIn, std::string &confirmationIDOut,
|
CONFIRMATION_ID_STATUS ConfirmationID::Generate(const std::string &installationIDIn, std::string &confirmationIDOut,
|
||||||
std::string &productIDIn)
|
std::string &productIDIn)
|
||||||
{
|
{
|
||||||
DWORD version;
|
DWORD version;
|
||||||
@ -494,5 +555,5 @@ DWORD ConfirmationID::Generate(const std::string &installationIDIn, std::string
|
|||||||
q += 6;
|
q += 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ typedef struct
|
|||||||
QWORD v[2];
|
QWORD v[2];
|
||||||
} TDivisor;
|
} TDivisor;
|
||||||
|
|
||||||
class ConfirmationID
|
class EXPORT ConfirmationID
|
||||||
{
|
{
|
||||||
QWORD MOD = 0, NON_RESIDUE = 0;
|
QWORD MOD = 0, NON_RESIDUE = 0;
|
||||||
QWORD curve[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
|
QWORD curve[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
|
||||||
@ -130,13 +130,19 @@ class ConfirmationID
|
|||||||
privateKey.qword[0] = privateKey.qword[1] = 0x00;
|
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,
|
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);
|
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,
|
BOOL LoadHyperellipticCurve(QWORD *f, Q_OWORD priv, QWORD modulous, QWORD nonresidue, BOOL isOffice, BOOL isXPBrand,
|
||||||
BYTE flagVersion);
|
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()
|
~ConfirmationID()
|
||||||
{
|
{
|
||||||
|
@ -22,6 +22,11 @@
|
|||||||
|
|
||||||
#include "confid.h"
|
#include "confid.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param d
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
int ConfirmationID::ConfirmationID::Divisor::find_divisor_v(TDivisor *d)
|
int ConfirmationID::ConfirmationID::Divisor::find_divisor_v(TDivisor *d)
|
||||||
{
|
{
|
||||||
// u | v^2 - curve
|
// u | v^2 - curve
|
||||||
@ -107,6 +112,13 @@ int ConfirmationID::ConfirmationID::Divisor::find_divisor_v(TDivisor *d)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param src
|
||||||
|
* @param polyu
|
||||||
|
* @param polyv
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
int ConfirmationID::ConfirmationID::Divisor::u2poly(const TDivisor *src, QWORD polyu[3], QWORD polyv[2])
|
int ConfirmationID::ConfirmationID::Divisor::u2poly(const TDivisor *src, QWORD polyu[3], QWORD polyv[2])
|
||||||
{
|
{
|
||||||
if (src->u[1] != BAD)
|
if (src->u[1] != BAD)
|
||||||
@ -138,6 +150,12 @@ int ConfirmationID::ConfirmationID::Divisor::u2poly(const TDivisor *src, QWORD p
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param src1
|
||||||
|
* @param src2
|
||||||
|
* @param dst
|
||||||
|
*/
|
||||||
void ConfirmationID::Divisor::add(const TDivisor *src1, const TDivisor *src2, TDivisor *dst)
|
void ConfirmationID::Divisor::add(const TDivisor *src1, const TDivisor *src2, TDivisor *dst)
|
||||||
{
|
{
|
||||||
QWORD u1[3], u2[3], v1[2], v2[2];
|
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)
|
#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)
|
void ConfirmationID::Divisor::mul(const TDivisor *src, QWORD mult, TDivisor *dst)
|
||||||
{
|
{
|
||||||
if (mult == 0)
|
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)
|
void ConfirmationID::Divisor::mul128(const TDivisor *src, QWORD mult_lo, QWORD mult_hi, TDivisor *dst)
|
||||||
{
|
{
|
||||||
if (mult_lo == 0 && mult_hi == 0)
|
if (mult_lo == 0 && mult_hi == 0)
|
||||||
|
@ -22,7 +22,17 @@
|
|||||||
|
|
||||||
#include "confid.h"
|
#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,
|
int ConfirmationID::Polynomial::mul(int adeg, const QWORD a[], int bdeg, const QWORD b[], int resultprevdeg,
|
||||||
QWORD result[])
|
QWORD result[])
|
||||||
{
|
{
|
||||||
@ -56,6 +66,15 @@ int ConfirmationID::Polynomial::mul(int adeg, const QWORD a[], int bdeg, const Q
|
|||||||
return resultprevdeg;
|
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)
|
int ConfirmationID::Polynomial::div_monic(int adeg, QWORD a[], int bdeg, const QWORD b[], QWORD *quotient)
|
||||||
{
|
{
|
||||||
assert(bdeg >= 0);
|
assert(bdeg >= 0);
|
||||||
@ -85,6 +104,19 @@ int ConfirmationID::Polynomial::div_monic(int adeg, QWORD a[], int bdeg, const Q
|
|||||||
return i;
|
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,
|
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])
|
QWORD gcd[3], int *pmult1deg, QWORD mult1[3], int *pmult2deg, QWORD mult2[3])
|
||||||
{
|
{
|
||||||
|
@ -24,20 +24,27 @@
|
|||||||
|
|
||||||
#if defined(__x86_64__) || defined(_M_AMD64) || defined(__aarch64__) || (defined(__arm64__) && defined(__APPLE__))
|
#if defined(__x86_64__) || defined(_M_AMD64) || defined(__aarch64__) || (defined(__arm64__) && defined(__APPLE__))
|
||||||
#ifdef __GNUC__
|
#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;
|
OWORD r = (OWORD)multiplier * (OWORD)multiplicand;
|
||||||
*product_hi = r >> 64;
|
*product_hi = r >> 64;
|
||||||
return (QWORD)r;
|
return (QWORD)r;
|
||||||
}
|
}
|
||||||
#else
|
#else // basically msvc
|
||||||
inline QWORD ConfirmationID::Residue::__umul128(QWORD multiplier, QWORD multiplicand, QWORD *product_hi)
|
/**
|
||||||
|
*
|
||||||
|
* @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);
|
return _umul128(multiplier, multiplicand, product_hi);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#elif defined(__i386__) || defined(_M_IX86) || defined(__arm__) || defined(__EMSCRIPTEN__)
|
#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
|
// multiplier = ab = a * 2^32 + b
|
||||||
// multiplicand = cd = c * 2^32 + d
|
// 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
|
#error Unknown architecture detected - please edit confid.cpp to tailor __umul128() your architecture
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param lo
|
||||||
|
* @param hi
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
QWORD ConfirmationID::Residue::ui128_quotient_mod(QWORD lo, QWORD hi)
|
QWORD ConfirmationID::Residue::ui128_quotient_mod(QWORD lo, QWORD hi)
|
||||||
{
|
{
|
||||||
// hi:lo * ceil(2**170/MOD) >> (64 + 64 + 42)
|
// 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);
|
return (prod3lo >> 42) | (prod3hi << 22);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
QWORD ConfirmationID::Residue::mul(QWORD x, QWORD y)
|
QWORD ConfirmationID::Residue::mul(QWORD x, QWORD y)
|
||||||
{
|
{
|
||||||
// * ceil(2**170/MOD) = 0x2d351 c6d04f8b|604fa6a1 c6346a87 for (p-1)*(p-1) max
|
// * 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;
|
return lo - quotient * parent->MOD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
QWORD ConfirmationID::Residue::pow(QWORD x, QWORD y)
|
QWORD ConfirmationID::Residue::pow(QWORD x, QWORD y)
|
||||||
{
|
{
|
||||||
if (y == 0)
|
if (y == 0)
|
||||||
@ -123,6 +148,12 @@ QWORD ConfirmationID::Residue::pow(QWORD x, QWORD y)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
QWORD ConfirmationID::Residue::add(QWORD x, QWORD y)
|
QWORD ConfirmationID::Residue::add(QWORD x, QWORD y)
|
||||||
{
|
{
|
||||||
QWORD z = x + y;
|
QWORD z = x + y;
|
||||||
@ -134,6 +165,12 @@ QWORD ConfirmationID::Residue::add(QWORD x, QWORD y)
|
|||||||
return z;
|
return z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
QWORD ConfirmationID::Residue::sub(QWORD x, QWORD y)
|
QWORD ConfirmationID::Residue::sub(QWORD x, QWORD y)
|
||||||
{
|
{
|
||||||
QWORD z = x - y;
|
QWORD z = x - y;
|
||||||
@ -145,6 +182,12 @@ QWORD ConfirmationID::Residue::sub(QWORD x, QWORD y)
|
|||||||
return z;
|
return z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param u
|
||||||
|
* @param v
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
QWORD ConfirmationID::Residue::inverse(QWORD u, QWORD v)
|
QWORD ConfirmationID::Residue::inverse(QWORD u, QWORD v)
|
||||||
{
|
{
|
||||||
// assert(u);
|
// assert(u);
|
||||||
@ -166,12 +209,22 @@ QWORD ConfirmationID::Residue::inverse(QWORD u, QWORD v)
|
|||||||
return xu;
|
return xu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
QWORD ConfirmationID::Residue::inv(QWORD x)
|
QWORD ConfirmationID::Residue::inv(QWORD x)
|
||||||
{
|
{
|
||||||
return inverse(x, parent->MOD);
|
return inverse(x, parent->MOD);
|
||||||
// return residue_pow(x, MOD - 2);
|
// return residue_pow(x, MOD - 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param what
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
QWORD ConfirmationID::Residue::sqrt(QWORD what)
|
QWORD ConfirmationID::Residue::sqrt(QWORD what)
|
||||||
{
|
{
|
||||||
if (!what)
|
if (!what)
|
||||||
|
@ -37,6 +37,11 @@ std::FILE *UMSKT::debug = std::fopen("/dev/null", "w");
|
|||||||
BOOL UMSKT::VERBOSE = false;
|
BOOL UMSKT::VERBOSE = false;
|
||||||
BOOL UMSKT::DEBUG = false;
|
BOOL UMSKT::DEBUG = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sets the filestream used for debugging
|
||||||
|
*
|
||||||
|
* @param input std::FILE
|
||||||
|
*/
|
||||||
void UMSKT::setDebugOutput(std::FILE *input)
|
void UMSKT::setDebugOutput(std::FILE *input)
|
||||||
{
|
{
|
||||||
debug = input;
|
debug = input;
|
||||||
|
@ -27,145 +27,266 @@
|
|||||||
#include "pidgen3/BINK2002.h"
|
#include "pidgen3/BINK2002.h"
|
||||||
#include "pidgen3/PIDGEN3.h"
|
#include "pidgen3/PIDGEN3.h"
|
||||||
|
|
||||||
FNEXPORT BOOL LIBUMSKT_SET_DEBUG_OUTPUT(void *filestream)
|
std::map<UMSKT_TAG, UMSKT_Value> UMSKT::tags;
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
char buffer[7];
|
char buffer[7];
|
||||||
memcpy(buffer, filestream, 6);
|
memcpy(buffer, filestream, 6);
|
||||||
buffer[6] = 0;
|
buffer[6] = 0;
|
||||||
auto buff_string = std::string(buffer);
|
auto buffstring = std::string(buffer);
|
||||||
|
std::transform(buffstring.begin(), buffstring.end(), buffstring.begin(), ::tolower);
|
||||||
|
|
||||||
if (strcasecmp("STDOUT", &buffer[0]) != 0)
|
if (buffstring == "stdout")
|
||||||
{
|
{
|
||||||
UMSKT::debug = stdout;
|
UMSKT::debug = stdout;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (strcasecmp("STDERR", &buffer[0]) != 0)
|
else if (buffstring == "stderr")
|
||||||
{
|
{
|
||||||
UMSKT::debug = stderr;
|
UMSKT::debug = stderr;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UMSKT::debug = (std::FILE *)filestream;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------
|
// ---------------------------------------------
|
||||||
|
|
||||||
FNEXPORT void *CONFID_INIT()
|
/**
|
||||||
{
|
*
|
||||||
|
* @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();
|
auto cid = new ConfirmationID();
|
||||||
|
|
||||||
// cid->LoadHyperellipticCurve(0, 0, 0, 0, 0, 0, 0, 0, 0, false, false, 0);
|
// cid->LoadHyperellipticCurve(0, 0, 0, 0, 0, 0, 0, 0, 0, false, false, 0);
|
||||||
|
|
||||||
return cid;
|
return cid;
|
||||||
}
|
}
|
||||||
|
|
||||||
FNEXPORT BYTE CONFID_GENERATE(void *cidIn, const char *installation_id_str, char *&confirmation_id, char *productid)
|
EXPORT BYTE CONFID_GENERATE(void *cidIn, const char *installation_id_str, char *&confirmation_id, char *productid)
|
||||||
{
|
{
|
||||||
auto *cid((ConfirmationID *)cidIn);
|
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);
|
std::string str, confid(confirmation_id), productids(productid);
|
||||||
auto retval = cid->Generate(str, confid, productids);
|
auto retval = cid->Generate(str, confid, productids);
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
FNEXPORT BYTE CONFID_END(void *cidIn)
|
EXPORT void CONFID_END(void *cidIn)
|
||||||
{
|
{
|
||||||
auto *cid((ConfirmationID *)cidIn);
|
auto *cid((ConfirmationID *)cidIn);
|
||||||
delete cid;
|
delete cid;
|
||||||
|
cid = nullptr;
|
||||||
|
cidIn = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
// ---------------------------------------------
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------
|
EXPORT void *PIDGEN3_INIT(const char *p, const char *a, const char *b, const char *generatorX,
|
||||||
|
|
||||||
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 *generatorY, const char *publicKeyX, const char *publicKeyY,
|
||||||
const char *genOrder, const char *privateKey)
|
const char *genOrder, const char *privateKey)
|
||||||
{
|
{
|
||||||
|
PIDGEN3 *p3;
|
||||||
|
|
||||||
auto *bink1998 = new BINK1998();
|
if (PIDGEN3::checkFieldStrIsBink1998(p))
|
||||||
|
{
|
||||||
|
p3 = new BINK1998();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p3 = new BINK2002();
|
||||||
|
}
|
||||||
|
|
||||||
bink1998->LoadEllipticCurve(p, a, b, generatorX, generatorY, publicKeyX, publicKeyY, genOrder, privateKey);
|
p3->LoadEllipticCurve(p, a, b, generatorX, generatorY, publicKeyX, publicKeyY, genOrder, privateKey);
|
||||||
|
|
||||||
return bink1998;
|
return p3;
|
||||||
}
|
}
|
||||||
|
|
||||||
FNEXPORT void *PIDGEN3_BINK2002_INIT(const char *p, const char *a, const char *b, const char *generatorX,
|
EXPORT BOOL PIDGEN3_Generate(void *&ptrIn, char *&pKeyOut, int pKeySizeIn)
|
||||||
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);
|
auto *p3((PIDGEN3 *)ptrIn);
|
||||||
|
|
||||||
std::string str;
|
for (auto const i : UMSKT::tags)
|
||||||
// BOOL retval = p3->Generate(str);
|
|
||||||
|
|
||||||
if (pKeySizeIn > str.length() + 1)
|
|
||||||
{
|
{
|
||||||
return false;
|
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());
|
memcpy(pKeyOut, &str[0], str.length());
|
||||||
pKeyOut[str.length()] = 0;
|
pKeyOut[str.length()] = 0;
|
||||||
|
|
||||||
return true;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
FNEXPORT BOOL PIDGEN3_Verify(void *&ptrIn, char *pKeyIn)
|
EXPORT BOOL PIDGEN3_Validate(void *&ptrIn, char *pKeyIn)
|
||||||
{
|
{
|
||||||
auto *p3((PIDGEN3 *)ptrIn);
|
auto *p3((PIDGEN3 *)ptrIn);
|
||||||
std::string str(pKeyIn);
|
std::string str(pKeyIn);
|
||||||
|
|
||||||
BOOL retval = p3->Verify(str);
|
BOOL retval = p3->Validate(str);
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
FNEXPORT void PIDGEN3_END(void *ptrIn)
|
EXPORT void PIDGEN3_END(void *ptrIn)
|
||||||
{
|
{
|
||||||
auto *p3((PIDGEN3 *)ptrIn);
|
auto *p3((PIDGEN3 *)ptrIn);
|
||||||
delete p3;
|
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;
|
||||||
|
|
||||||
FNEXPORT BOOL PIDGEN2_INIT()
|
|
||||||
{
|
|
||||||
return true;
|
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 PIDGEN2_GENERATE()
|
/**
|
||||||
|
* 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)
|
||||||
{
|
{
|
||||||
return true;
|
if (a == nullptr || to == nullptr)
|
||||||
}
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
FNEXPORT BOOL PIDGEN2_END()
|
int len = BN_bn2bin(a, to);
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
FNEXPORT BOOL PIDGEN2_GenerateRetail(char *channelID, char *&keyout)
|
if (len > tolen)
|
||||||
{
|
{
|
||||||
auto P2 = new PIDGEN2();
|
return -1;
|
||||||
BOOL retval = P2->GenerateRetail(channelID, keyout);
|
}
|
||||||
delete P2;
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
FNEXPORT BOOL PIDGEN2_GenerateOEM(char *year, char *day, char *oem, char *&keyout)
|
// Choke point inside BN_bn2lebinpad: OpenSSL uses len instead of tolen.
|
||||||
{
|
endian(to, tolen);
|
||||||
auto P2 = new PIDGEN2();
|
|
||||||
BOOL retval = P2->GenerateOEM(year, day, oem, keyout);
|
return len;
|
||||||
delete P2;
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
@ -53,32 +53,87 @@
|
|||||||
#define NEXTSNBITS(field, n, offset) (((QWORD)(field) >> (offset)) & ((1ULL << (n)) - 1))
|
#define NEXTSNBITS(field, n, offset) (((QWORD)(field) >> (offset)) & ((1ULL << (n)) - 1))
|
||||||
#define FIRSTNBITS(field, n) NEXTSNBITS((field), (n), 0)
|
#define FIRSTNBITS(field, n) NEXTSNBITS((field), (n), 0)
|
||||||
|
|
||||||
#define HIBYTES(field, bytes) NEXTSNBITS((QWORD)(field), ((bytes)*8), ((bytes)*8))
|
#define HIBYTES(field, bytes) NEXTSNBITS((QWORD)(field), ((bytes) * 8), ((bytes) * 8))
|
||||||
#define LOBYTES(field, bytes) FIRSTNBITS((QWORD)(field), ((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 BYDWORD(n) (DWORD)(*((n) + 0) | *((n) + 1) << 8 | *((n) + 2) << 16 | *((n) + 3) << 24)
|
||||||
#define BITMASK(n) ((1ULL << (n)) - 1)
|
#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:
|
public:
|
||||||
static std::FILE *debug;
|
static std::FILE *debug;
|
||||||
static BOOL VERBOSE;
|
static BOOL VERBOSE;
|
||||||
static BOOL DEBUG;
|
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()
|
static void DESTRUCT()
|
||||||
|
{
|
||||||
|
if (debug != nullptr)
|
||||||
{
|
{
|
||||||
std::fclose(debug);
|
std::fclose(debug);
|
||||||
|
}
|
||||||
debug = nullptr;
|
debug = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setDebugOutput(std::FILE *input);
|
static void setDebugOutput(std::FILE *input);
|
||||||
|
|
||||||
template <typename T> static T getRandom()
|
template <typename T> static T getRandom()
|
||||||
{
|
{
|
||||||
T retval;
|
T retval;
|
||||||
RAND_bytes((BYTE *)&retval, sizeof(retval));
|
RAND_bytes((BYTE *)&retval, sizeof(retval));
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *VERSION()
|
||||||
|
{
|
||||||
|
return fmt::format("LIBUMSKT {} compiled on {} {}", LIBUMSKT_VERSION_STRING, __DATE__, __TIME__).c_str();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // UMSKT_LIBUMSKT_H
|
#endif // UMSKT_LIBUMSKT_H
|
||||||
|
@ -22,7 +22,12 @@
|
|||||||
|
|
||||||
#include "PIDGEN2.h"
|
#include "PIDGEN2.h"
|
||||||
|
|
||||||
bool PIDGEN2::isNumericString(char *input)
|
/**
|
||||||
|
*
|
||||||
|
* @param input
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
BOOL PIDGEN2::isNumericString(char *input)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < strlen(input); i++)
|
for (int i = 0; i < strlen(input); i++)
|
||||||
{
|
{
|
||||||
@ -35,6 +40,11 @@ bool PIDGEN2::isNumericString(char *input)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param input
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
int PIDGEN2::addDigits(char *input)
|
int PIDGEN2::addDigits(char *input)
|
||||||
{
|
{
|
||||||
int output = 0;
|
int output = 0;
|
||||||
@ -52,7 +62,12 @@ int PIDGEN2::addDigits(char *input)
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PIDGEN2::isValidChannelID(char *channelID)
|
/**
|
||||||
|
*
|
||||||
|
* @param channelID
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
BOOL PIDGEN2::isValidChannelID(char *channelID)
|
||||||
{
|
{
|
||||||
if (strlen(channelID) > 3)
|
if (strlen(channelID) > 3)
|
||||||
{
|
{
|
||||||
@ -70,7 +85,12 @@ bool PIDGEN2::isValidChannelID(char *channelID)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PIDGEN2::isValidOEMID(char *OEMID)
|
/**
|
||||||
|
*
|
||||||
|
* @param OEMID
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
BOOL PIDGEN2::isValidOEMID(char *OEMID)
|
||||||
{
|
{
|
||||||
if (!isNumericString(OEMID))
|
if (!isNumericString(OEMID))
|
||||||
{
|
{
|
||||||
@ -90,7 +110,12 @@ bool PIDGEN2::isValidOEMID(char *OEMID)
|
|||||||
return (mod % 21 == 0);
|
return (mod % 21 == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PIDGEN2::isValidYear(char *year)
|
/**
|
||||||
|
*
|
||||||
|
* @param year
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
BOOL PIDGEN2::isValidYear(char *year)
|
||||||
{
|
{
|
||||||
for (int i = 0; i <= 7; i++)
|
for (int i = 0; i <= 7; i++)
|
||||||
{
|
{
|
||||||
@ -102,7 +127,12 @@ bool PIDGEN2::isValidYear(char *year)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PIDGEN2::isValidDay(char *day)
|
/**
|
||||||
|
*
|
||||||
|
* @param day
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
BOOL PIDGEN2::isValidDay(char *day)
|
||||||
{
|
{
|
||||||
if (!isNumericString(day))
|
if (!isNumericString(day))
|
||||||
{
|
{
|
||||||
@ -117,11 +147,22 @@ bool PIDGEN2::isValidDay(char *day)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PIDGEN2::isValidRetailProductID(char *productID)
|
/**
|
||||||
|
*
|
||||||
|
* @param productID
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
BOOL PIDGEN2::isValidRetailProductID(char *productID)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param channelID
|
||||||
|
* @param keyout
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
int PIDGEN2::GenerateRetail(char *channelID, char *&keyout)
|
int PIDGEN2::GenerateRetail(char *channelID, char *&keyout)
|
||||||
{
|
{
|
||||||
if (!isValidChannelID(channelID))
|
if (!isValidChannelID(channelID))
|
||||||
@ -132,6 +173,14 @@ int PIDGEN2::GenerateRetail(char *channelID, char *&keyout)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param year
|
||||||
|
* @param day
|
||||||
|
* @param oem
|
||||||
|
* @param keyout
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
int PIDGEN2::GenerateOEM(char *year, char *day, char *oem, char *&keyout)
|
int PIDGEN2::GenerateOEM(char *year, char *day, char *oem, char *&keyout)
|
||||||
{
|
{
|
||||||
if (!isValidOEMID(oem))
|
if (!isValidOEMID(oem))
|
||||||
@ -139,21 +188,21 @@ int PIDGEN2::GenerateOEM(char *year, char *day, char *oem, char *&keyout)
|
|||||||
int mod = addDigits(oem);
|
int mod = addDigits(oem);
|
||||||
mod += mod % 21;
|
mod += mod % 21;
|
||||||
|
|
||||||
strcpy(oem, fmt::format("{:07d}", mod).c_str());
|
snprintf(oem, 8, "%07u", mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isValidYear(year))
|
if (!isValidYear(year))
|
||||||
{
|
{
|
||||||
strcpy(year, validYears[0]);
|
_strncpy(year, 4, validYears[0], 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isValidDay(day))
|
if (!isValidDay(day))
|
||||||
{
|
{
|
||||||
int iday = std::stoi(day);
|
auto iday = UMSKT::getRandom<int>();
|
||||||
iday = (iday + 1) % 365;
|
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;
|
return 0;
|
||||||
}
|
}
|
@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
#include "../libumskt.h"
|
#include "../libumskt.h"
|
||||||
|
|
||||||
class PIDGEN2
|
class EXPORT PIDGEN2
|
||||||
{
|
{
|
||||||
DWORD year;
|
DWORD year;
|
||||||
DWORD day;
|
DWORD day;
|
||||||
|
@ -29,12 +29,30 @@
|
|||||||
|
|
||||||
#include "BINK1998.h"
|
#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.
|
* Unpacks a Windows XP-like Product Key.
|
||||||
*
|
*
|
||||||
* @param pRaw [out] *QWORD[2] raw product key output
|
* @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.
|
// We're assuming that the quantity of information within the product key is at most 114 bits.
|
||||||
// log2(24^25) = 114.
|
// log2(24^25) = 114.
|
||||||
@ -54,123 +72,6 @@ BOOL BINK1998::Unpack(KeyInfo &info, QWORD *pRaw)
|
|||||||
return true;
|
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.
|
* Generates a Windows XP-like Product Key.
|
||||||
*
|
*
|
||||||
@ -178,7 +79,7 @@ BOOL BINK1998::Verify(std::string &pKey)
|
|||||||
*
|
*
|
||||||
* @return true on success, false on fail
|
* @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();
|
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];
|
BYTE xBin[FIELD_BYTES], yBin[FIELD_BYTES];
|
||||||
|
|
||||||
// Convert coordinates to bytes.
|
// Convert coordinates to bytes.
|
||||||
BN_bn2lebin(x, xBin, FIELD_BYTES);
|
UMSKT::BN_bn2lebin(x, xBin, FIELD_BYTES);
|
||||||
BN_bn2lebin(y, yBin, FIELD_BYTES);
|
UMSKT::BN_bn2lebin(y, yBin, FIELD_BYTES);
|
||||||
|
|
||||||
// Assemble the SHA message.
|
// Assemble the SHA message.
|
||||||
memcpy(&msgBuffer[0], &pData, 4);
|
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));
|
BN_bn2lebinpad(s, (BYTE *)&info.Signature, BN_num_bytes(s));
|
||||||
|
|
||||||
// Pack product key.
|
// 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, "Generation results:\n");
|
||||||
fmt::print(UMSKT::debug, " Upgrade: {:#08x}\n", info.isUpgrade);
|
fmt::print(UMSKT::debug, "{:>10}: {:b}\n", "Upgrade", (bool)info.isUpgrade);
|
||||||
fmt::print(UMSKT::debug, " Serial: {:#08x}\n", info.Serial);
|
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Channel ID", serial.substr(0, 3));
|
||||||
fmt::print(UMSKT::debug, " Hash: {:#08x}\n", info.Hash);
|
fmt::print(UMSKT::debug, "{:>10}: {}\n", "Sequence", serial.substr(3));
|
||||||
fmt::print(UMSKT::debug, " Signature: {:#08x}\n", info.Signature);
|
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");
|
fmt::print(UMSKT::debug, "\n");
|
||||||
|
|
||||||
EC_POINT_free(r);
|
EC_POINT_free(r);
|
||||||
@ -279,3 +182,103 @@ BOOL BINK1998::Generate(KeyInfo &info, std::string &pKey)
|
|||||||
|
|
||||||
return true;
|
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;
|
||||||
|
}
|
||||||
|
@ -25,13 +25,30 @@
|
|||||||
|
|
||||||
#include "PIDGEN3.h"
|
#include "PIDGEN3.h"
|
||||||
|
|
||||||
class BINK1998 : public PIDGEN3
|
class EXPORT BINK1998 : public PIDGEN3
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BOOL Unpack(KeyInfo &info, QWORD *pRaw) override;
|
using PIDGEN3::PIDGEN3;
|
||||||
BOOL Pack(const KeyInfo &info, QWORD *pRaw) override;
|
explicit BINK1998(PIDGEN3 *p3)
|
||||||
BOOL Verify(std::string &pKey) override;
|
{
|
||||||
BOOL Generate(KeyInfo &info, std::string &pKey) override;
|
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
|
#endif // UMSKT_BINK1998_H
|
||||||
|
@ -29,12 +29,26 @@
|
|||||||
|
|
||||||
#include "BINK2002.h"
|
#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.
|
* Unpacks a Windows Server 2003-like Product Key.
|
||||||
*
|
*
|
||||||
* @param pRaw *QWORD[2] raw product key input
|
* @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.
|
// We're assuming that the quantity of information within the product key is at most 114 bits.
|
||||||
// log2(24^25) = 114.
|
// log2(24^25) = 114.
|
||||||
@ -60,140 +74,13 @@ BOOL BINK2002::Unpack(KeyInfo &info, QWORD *pRaw)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Packs a Windows Server 2003-like Product Key.
|
* Generates 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.
|
|
||||||
*
|
*
|
||||||
|
* @param info
|
||||||
* @param pKey
|
* @param pKey
|
||||||
**/
|
* @return
|
||||||
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)
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
BOOL BINK2002::Generate(std::string &pKey)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
BN_CTX *numContext = BN_CTX_new();
|
BN_CTX *numContext = BN_CTX_new();
|
||||||
|
|
||||||
@ -225,8 +112,8 @@ BOOL BINK2002::Generate(KeyInfo &info, std::string &pKey)
|
|||||||
yBin[FIELD_BYTES_2003];
|
yBin[FIELD_BYTES_2003];
|
||||||
|
|
||||||
// Convert resulting point coordinates to bytes.
|
// Convert resulting point coordinates to bytes.
|
||||||
BN_bn2lebin(x, xBin, FIELD_BYTES_2003);
|
UMSKT::BN_bn2lebin(x, xBin, FIELD_BYTES_2003);
|
||||||
BN_bn2lebin(y, yBin, FIELD_BYTES_2003);
|
UMSKT::BN_bn2lebin(y, yBin, FIELD_BYTES_2003);
|
||||||
|
|
||||||
// Assemble the first SHA message.
|
// Assemble the first SHA message.
|
||||||
msgBuffer[0x00] = 0x79;
|
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));
|
BN_bn2lebinpad(s, (BYTE *)&info.Signature, BN_num_bytes(s));
|
||||||
|
|
||||||
// Pack product key.
|
// Pack product key.
|
||||||
Pack(info, pRaw);
|
Pack(pRaw);
|
||||||
|
|
||||||
fmt::print(UMSKT::debug, "Generation results:\n");
|
fmt::print(UMSKT::debug, "Generation results:\n");
|
||||||
fmt::print(UMSKT::debug, " Upgrade: {:#08x}\n", info.isUpgrade);
|
fmt::print(UMSKT::debug, "{:>10}: {:b}\n", "Upgrade", (bool)info.isUpgrade);
|
||||||
fmt::print(UMSKT::debug, "Channel ID: {:#08x}\n", info.ChannelID);
|
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Channel ID", info.ChannelID);
|
||||||
fmt::print(UMSKT::debug, " Hash: {:#08x}\n", info.Hash);
|
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Hash", info.Hash);
|
||||||
fmt::print(UMSKT::debug, " Signature: {:#08x}\n", info.Signature);
|
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "Signature", info.Signature);
|
||||||
fmt::print(UMSKT::debug, " AuthInfo: {:#08x}\n", info.AuthInfo);
|
fmt::print(UMSKT::debug, "{:>10}: {:d}\n", "AuthInfo", info.AuthInfo);
|
||||||
fmt::print(UMSKT::debug, "\n");
|
fmt::print(UMSKT::debug, "\n");
|
||||||
|
|
||||||
EC_POINT_free(r);
|
EC_POINT_free(r);
|
||||||
@ -356,3 +243,121 @@ BOOL BINK2002::Generate(KeyInfo &info, std::string &pKey)
|
|||||||
|
|
||||||
return true;
|
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;
|
||||||
|
}
|
||||||
|
@ -25,13 +25,30 @@
|
|||||||
|
|
||||||
#include "PIDGEN3.h"
|
#include "PIDGEN3.h"
|
||||||
|
|
||||||
class BINK2002 : public PIDGEN3
|
class EXPORT BINK2002 : public PIDGEN3
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BOOL Unpack(KeyInfo &info, QWORD *pRaw) override;
|
using PIDGEN3::PIDGEN3;
|
||||||
BOOL Pack(const KeyInfo &info, QWORD *pRaw) override;
|
explicit BINK2002(PIDGEN3 *p3)
|
||||||
BOOL Verify(std::string &pKey) override;
|
{
|
||||||
BOOL Generate(KeyInfo &info, std::string &pKey) override;
|
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
|
#endif // UMSKT_BINK2002_H
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "PIDGEN3.h"
|
#include "PIDGEN3.h"
|
||||||
|
#include "BINK1998.h"
|
||||||
|
#include "BINK2002.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* https://xkcd.com/221/
|
* https://xkcd.com/221/
|
||||||
@ -118,49 +120,40 @@ BOOL PIDGEN3::LoadEllipticCurve(const std::string pSel, const std::string aSel,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
BOOL PIDGEN3::Generate(std::string &pKey)
|
||||||
* Convert data between endianness types.
|
|
||||||
*
|
|
||||||
* @param data [in]
|
|
||||||
* @param length [in]
|
|
||||||
**/
|
|
||||||
inline void PIDGEN3::endian(BYTE *data, int length)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < length / 2; i++)
|
BOOL retval;
|
||||||
|
|
||||||
|
if (checkFieldIsBink1998())
|
||||||
{
|
{
|
||||||
BYTE temp = data[i];
|
auto p3 = BINK1998();
|
||||||
data[i] = data[length - i - 1];
|
retval = p3.Generate(pKey);
|
||||||
data[length - i - 1] = temp;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto p3 = BINK2002();
|
||||||
|
retval = p3.Generate(pKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
BOOL PIDGEN3::Validate(std::string &pKey)
|
||||||
* 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)
|
|
||||||
{
|
{
|
||||||
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);
|
return retval;
|
||||||
|
|
||||||
if (len > tolen)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Choke point inside BN_bn2lebinpad: OpenSSL uses len instead of tolen.
|
|
||||||
endian(to, tolen);
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -185,7 +178,7 @@ void PIDGEN3::base24(std::string &cdKey, BYTE *byteSeq)
|
|||||||
; // do nothing, just counting
|
; // do nothing, just counting
|
||||||
}
|
}
|
||||||
|
|
||||||
endian(rbyteSeq, ++length);
|
UMSKT::endian(rbyteSeq, ++length);
|
||||||
|
|
||||||
// Convert reversed byte sequence to BigNum z.
|
// Convert reversed byte sequence to BigNum z.
|
||||||
z = BN_bin2bn(rbyteSeq, length, nullptr);
|
z = BN_bin2bn(rbyteSeq, length, nullptr);
|
||||||
@ -214,8 +207,6 @@ void PIDGEN3::unbase24(BYTE *byteSeq, std::string cdKey)
|
|||||||
BYTE pDecodedKey[PK_LENGTH + NULL_TERMINATOR]{};
|
BYTE pDecodedKey[PK_LENGTH + NULL_TERMINATOR]{};
|
||||||
BIGNUM *y = BN_new();
|
BIGNUM *y = BN_new();
|
||||||
|
|
||||||
BN_zero(y);
|
|
||||||
|
|
||||||
// Remove dashes from the CD-key and put it into a Base24 byte array.
|
// 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++)
|
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);
|
BN_free(y);
|
||||||
|
|
||||||
// Reverse the byte sequence.
|
// Reverse the byte sequence.
|
||||||
endian(byteSeq, n);
|
UMSKT::endian(byteSeq, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL PIDGEN3::getIsBINK1998()
|
BOOL PIDGEN3::checkFieldIsBink1998()
|
||||||
{
|
{
|
||||||
BIGNUM *max = BN_new();
|
auto *max = BN_new();
|
||||||
BN_hex2bn(&max, MAX_BINK1998);
|
|
||||||
|
|
||||||
|
// 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);
|
int retval = BN_cmp(max, privateKey);
|
||||||
|
|
||||||
BN_free(max);
|
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;
|
||||||
}
|
}
|
||||||
|
@ -24,23 +24,35 @@
|
|||||||
#define UMSKT_PIDGEN3_H
|
#define UMSKT_PIDGEN3_H
|
||||||
|
|
||||||
#include "../libumskt.h"
|
#include "../libumskt.h"
|
||||||
#define MAX_BINK1998 \
|
|
||||||
"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
|
||||||
|
|
||||||
class PIDGEN3
|
class BINK1998;
|
||||||
|
class BINK2002;
|
||||||
|
|
||||||
|
class EXPORT PIDGEN3
|
||||||
{
|
{
|
||||||
|
friend class BINK1998;
|
||||||
|
friend class BINK2002;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
BIGNUM *privateKey, *genOrder;
|
BIGNUM *privateKey, *genOrder;
|
||||||
EC_POINT *genPoint, *pubPoint;
|
EC_POINT *genPoint, *pubPoint;
|
||||||
EC_GROUP *eCurve;
|
EC_GROUP *eCurve;
|
||||||
BOOL isBINK1998;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PIDGEN3()
|
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_GROUP_free(eCurve);
|
||||||
EC_POINT_free(genPoint);
|
EC_POINT_free(genPoint);
|
||||||
@ -69,7 +81,7 @@ class PIDGEN3
|
|||||||
{
|
{
|
||||||
ChannelID = ChannelIDIn;
|
ChannelID = ChannelIDIn;
|
||||||
}
|
}
|
||||||
};
|
} info;
|
||||||
|
|
||||||
static constexpr char pKeyCharset[] = "BCDFGHJKMPQRTVWXY2346789";
|
static constexpr char pKeyCharset[] = "BCDFGHJKMPQRTVWXY2346789";
|
||||||
|
|
||||||
@ -77,19 +89,16 @@ class PIDGEN3
|
|||||||
std::string generatorYSel, std::string publicKeyXSel, std::string publicKeyYSel,
|
std::string generatorYSel, std::string publicKeyXSel, std::string publicKeyYSel,
|
||||||
std::string genOrderSel, std::string privateKeySel);
|
std::string genOrderSel, std::string privateKeySel);
|
||||||
|
|
||||||
virtual BOOL Unpack(KeyInfo &info, QWORD *pRaw) = 0;
|
virtual BOOL Pack(QWORD *pRaw) = 0;
|
||||||
virtual BOOL Pack(const KeyInfo &info, QWORD *pRaw) = 0;
|
virtual BOOL Unpack(QWORD *pRaw) = 0;
|
||||||
virtual BOOL Verify(std::string &pKey) = 0;
|
virtual BOOL Generate(std::string &pKey);
|
||||||
virtual BOOL Generate(KeyInfo &info, std::string &pKey) = 0;
|
virtual BOOL Validate(std::string &pKey);
|
||||||
|
|
||||||
// PIDGEN3.cpp
|
// 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 base24(std::string &cdKey, BYTE *byteSeq);
|
||||||
void unbase24(BYTE *byteSeq, std::string cdKey);
|
void unbase24(BYTE *byteSeq, std::string cdKey);
|
||||||
BOOL getIsBINK1998();
|
BOOL checkFieldIsBink1998();
|
||||||
|
static BOOL checkFieldStrIsBink1998(std::string keyin);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // UMSKT_PIDGEN3_H
|
#endif // UMSKT_PIDGEN3_H
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
if (status = CLI::Init(argc, argv); status > 0)
|
if (status = CLI::Init(argc, argv); status != 0)
|
||||||
{
|
{
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
329
src/options.cpp
329
src/options.cpp
@ -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
40
src/rc.cpp
Normal 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;
|
||||||
|
}
|
@ -23,8 +23,19 @@
|
|||||||
#ifndef UMSKT_TYPEDEFS_H
|
#ifndef UMSKT_TYPEDEFS_H
|
||||||
#define 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 <cstdbool>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
@ -32,53 +43,76 @@
|
|||||||
#define assert(x) /* do nothing */
|
#define assert(x) /* do nothing */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#if defined(_MSC_VER)
|
||||||
#define EXPORT extern "C" __declspec(dllexport)
|
#define FNEXPORT __declspec(dllexport)
|
||||||
#define INLINE __forceinline
|
#define FNIMPORT __declspec(dllimport)
|
||||||
|
#define FNINLINE __forceinline
|
||||||
#elif defined(__GNUC__)
|
#elif defined(__GNUC__)
|
||||||
#define EXPORT extern "C" __attribute__((visibility("default")))
|
#define FNEXPORT __attribute__((visibility("default")))
|
||||||
#define INLINE __attribute__((always_inline))
|
#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
|
#else
|
||||||
#define EXPORT extern "C"
|
#define forceinline inline
|
||||||
#define INLINE
|
#endif // __has_attribute(__always_inline__)
|
||||||
|
#else
|
||||||
|
#define FNEXPORT
|
||||||
|
#define FNINLINE
|
||||||
#warning "function inlining not handled"
|
#warning "function inlining not handled"
|
||||||
#endif
|
#endif // defined(_MSC_VER)
|
||||||
|
|
||||||
#ifdef __EMSCRIPTEN__
|
#ifdef __EMSCRIPTEN__
|
||||||
#include <emscripten/emscripten.h>
|
#include <emscripten/emscripten.h>
|
||||||
#define FNEXPORT EMSCRIPTEN_KEEPALIVE EXPORT
|
#define EXPORT EMSCRIPTEN_KEEPALIVE FNEXPORT
|
||||||
|
#define INLINE FNINLINE
|
||||||
#else
|
#else
|
||||||
#define FNEXPORT EXPORT
|
#ifdef UMSKT_IMPORT_LIB
|
||||||
#endif
|
#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
|
#ifdef _MSC_VER
|
||||||
#ifndef strncasecmp
|
#ifndef _sscanf
|
||||||
#define strncasecmp _strnicmp
|
#define _sscanf sscanf_s
|
||||||
#endif
|
#endif
|
||||||
#ifndef strcasecmp
|
#ifndef _strncpy
|
||||||
#define strcasecmp _stricmp
|
#define _strncpy strncpy_s
|
||||||
#endif
|
#endif
|
||||||
#ifndef strcmp
|
#ifndef _strcpy
|
||||||
#define strcmp strcmp_s
|
#define _strcpy strcpy_s
|
||||||
#endif
|
|
||||||
#ifndef sscanf
|
|
||||||
#define sscanf sscanf_s
|
|
||||||
#endif
|
#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
|
#endif
|
||||||
|
|
||||||
// Type definitions
|
#if defined(__SIZEOF_INT128__) || defined(__int128)
|
||||||
typedef bool BOOL;
|
using uint128_t = unsigned __int128;
|
||||||
typedef uint8_t BYTE;
|
#else // use the intel-supplied __m128 intrisic
|
||||||
typedef uint16_t WORD;
|
using uint128_t = __m128;
|
||||||
typedef uint32_t DWORD;
|
|
||||||
typedef uint64_t QWORD;
|
|
||||||
|
|
||||||
#ifdef __SIZEOF_INT128__
|
|
||||||
typedef unsigned __int128 OWORD;
|
|
||||||
#endif
|
#endif
|
||||||
|
using OWORD = uint128_t;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
// OWORD oword;
|
OWORD oword;
|
||||||
QWORD qword[2];
|
QWORD qword[2];
|
||||||
DWORD dword[4];
|
DWORD dword[4];
|
||||||
WORD word[8];
|
WORD word[8];
|
||||||
|
@ -20,9 +20,8 @@
|
|||||||
* @Maintainer Neo
|
* @Maintainer Neo
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#include "../typedefs.h"
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
BOOLEAN WINAPI DllMain(IN HINSTANCE hDllHandle, IN DWORD nReason, IN LPVOID Reserved)
|
BOOLEAN WINAPI DllMain(IN HINSTANCE hDllHandle, IN DWORD nReason, IN LPVOID Reserved)
|
||||||
{
|
{
|
||||||
|
70
src/windows/platform.cpp
Normal file
70
src/windows/platform.cpp
Normal 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;
|
||||||
|
}
|
@ -25,12 +25,14 @@
|
|||||||
// Used by umskt.rc
|
// Used by umskt.rc
|
||||||
//
|
//
|
||||||
#define IDI_ICON1 101
|
#define IDI_ICON1 101
|
||||||
|
#define IDR_JSON1 102
|
||||||
|
|
||||||
// Next default values for new objects
|
// Next default values for new objects
|
||||||
//
|
//
|
||||||
#ifdef APSTUDIO_INVOKED
|
#ifdef APSTUDIO_INVOKED
|
||||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
#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_COMMAND_VALUE 40001
|
||||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||||
#define _APS_NEXT_SYMED_VALUE 101
|
#define _APS_NEXT_SYMED_VALUE 101
|
||||||
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user