#include "M2PackRuntimeKeyProvider.h" #include #include #include #include #include "EterBase/Debug.h" #include "EterBase/Utils.h" namespace { constexpr char M2PACK_SHARED_KEY_MAGIC[8] = {'M', '2', 'K', 'E', 'Y', 'S', '1', '\0'}; constexpr const char* M2PACK_DEFAULT_MAP_NAME = "Local\\M2PackSharedKeys"; constexpr const char* M2PACK_ENV_MASTER_KEY = "M2PACK_MASTER_KEY_HEX"; constexpr const char* M2PACK_ENV_PUBLIC_KEY = "M2PACK_SIGN_PUBKEY_HEX"; constexpr const char* M2PACK_ENV_MAP_NAME = "M2PACK_KEY_MAP"; #pragma pack(push, 1) struct M2PackSharedKeys { char magic[8]; uint32_t version; uint32_t flags; uint8_t master_key[M2PACK_KEY_SIZE]; uint8_t sign_public_key[M2PACK_PUBLIC_KEY_SIZE]; }; #pragma pack(pop) struct RuntimeKeyState { std::array master_key {}; std::array public_key = M2PACK_SIGN_PUBLIC_KEY; bool runtime_master_key = false; bool runtime_public_key = false; bool initialized = false; }; RuntimeKeyState g_state; uint8_t HexNibble(char ch) { if (ch >= '0' && ch <= '9') return static_cast(ch - '0'); if (ch >= 'a' && ch <= 'f') return static_cast(10 + ch - 'a'); if (ch >= 'A' && ch <= 'F') return static_cast(10 + ch - 'A'); return 0xff; } template bool ParseHexInto(std::string value, std::array& out) { value.erase(std::remove_if(value.begin(), value.end(), [](unsigned char ch) { return std::isspace(ch) != 0; }), value.end()); if (value.size() != N * 2) return false; for (std::size_t i = 0; i < N; ++i) { const uint8_t hi = HexNibble(value[i * 2]); const uint8_t lo = HexNibble(value[i * 2 + 1]); if (hi == 0xff || lo == 0xff) return false; out[i] = static_cast((hi << 4) | lo); } return true; } std::string GetEnvString(const char* name) { char buffer[512]; const DWORD len = GetEnvironmentVariableA(name, buffer, sizeof(buffer)); if (len == 0 || len >= sizeof(buffer)) return {}; return std::string(buffer, buffer + len); } bool LoadFromSharedMapping(const std::string& mappingName) { const HANDLE mapping = OpenFileMappingA(FILE_MAP_READ, FALSE, mappingName.c_str()); if (!mapping) return false; void* view = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, sizeof(M2PackSharedKeys)); if (!view) { CloseHandle(mapping); return false; } M2PackSharedKeys blob {}; memcpy(&blob, view, sizeof(blob)); UnmapViewOfFile(view); CloseHandle(mapping); if (memcmp(blob.magic, M2PACK_SHARED_KEY_MAGIC, sizeof(blob.magic)) != 0 || blob.version != 1) { TraceError("M2Pack key mapping exists but has invalid header"); return false; } memcpy(g_state.master_key.data(), blob.master_key, g_state.master_key.size()); memcpy(g_state.public_key.data(), blob.sign_public_key, g_state.public_key.size()); g_state.runtime_master_key = true; g_state.runtime_public_key = true; return true; } void ApplyCommandLineOption(const std::string& key, const std::string& value) { if (key == "--m2pack-key-hex") { if (ParseHexInto(value, g_state.master_key)) { g_state.runtime_master_key = true; } else { TraceError("Invalid value for --m2pack-key-hex"); } return; } if (key == "--m2pack-pubkey-hex") { if (ParseHexInto(value, g_state.public_key)) { g_state.runtime_public_key = true; } else { TraceError("Invalid value for --m2pack-pubkey-hex"); } return; } if (key == "--m2pack-key-map") { LoadFromSharedMapping(value); return; } } } // namespace bool InitializeM2PackRuntimeKeyProvider(const char* commandLine) { if (g_state.initialized) return true; g_state = RuntimeKeyState {}; const std::string mapName = GetEnvString(M2PACK_ENV_MAP_NAME); if (!mapName.empty()) { LoadFromSharedMapping(mapName); } else { LoadFromSharedMapping(M2PACK_DEFAULT_MAP_NAME); } const std::string envMaster = GetEnvString(M2PACK_ENV_MASTER_KEY); if (!envMaster.empty()) { if (ParseHexInto(envMaster, g_state.master_key)) g_state.runtime_master_key = true; else TraceError("Invalid M2PACK_MASTER_KEY_HEX value"); } const std::string envPublic = GetEnvString(M2PACK_ENV_PUBLIC_KEY); if (!envPublic.empty()) { if (ParseHexInto(envPublic, g_state.public_key)) g_state.runtime_public_key = true; else TraceError("Invalid M2PACK_SIGN_PUBKEY_HEX value"); } int argc = 0; PCHAR* argv = CommandLineToArgv(const_cast(commandLine ? commandLine : ""), &argc); if (argv) { for (int i = 0; i < argc; ++i) { const std::string key = argv[i]; if ((key == "--m2pack-key-hex" || key == "--m2pack-pubkey-hex" || key == "--m2pack-key-map") && i + 1 < argc) { ApplyCommandLineOption(key, argv[i + 1]); ++i; } } SAFE_FREE_GLOBAL(argv); } if (g_state.runtime_master_key || g_state.runtime_public_key) { Tracef("M2Pack runtime key provider: override active (master=%d public=%d)\n", g_state.runtime_master_key ? 1 : 0, g_state.runtime_public_key ? 1 : 0); } else { Tracef("M2Pack runtime key provider: no runtime master key available; .m2p loading will be denied\n"); } g_state.initialized = true; return true; } const std::array& GetM2PackActiveMasterKey() { return g_state.master_key; } const std::array& GetM2PackActivePublicKey() { return g_state.public_key; } bool HasM2PackRuntimeMasterKey() { return g_state.runtime_master_key; } bool HasM2PackRuntimeKeysForArchiveLoad() { return g_state.runtime_master_key; } bool IsM2PackUsingRuntimeMasterKey() { return g_state.runtime_master_key; } bool IsM2PackUsingRuntimePublicKey() { return g_state.runtime_public_key; }