diff --git a/src/PackLib/M2Pack.cpp b/src/PackLib/M2Pack.cpp index 6fdf39d..b7a994b 100644 --- a/src/PackLib/M2Pack.cpp +++ b/src/PackLib/M2Pack.cpp @@ -40,12 +40,6 @@ bool ReadPod(const uint8_t* bytes, std::size_t size, std::size_t& offset, T& out bool CM2Pack::Load(const std::string& path) { - if (!HasM2PackRuntimeKeysForArchiveLoad()) - { - TraceError("CM2Pack::Load: runtime master key required for '%s'", path.c_str()); - return false; - } - std::error_code ec; m_file.map(path, ec); @@ -75,6 +69,15 @@ bool CM2Pack::Load(const std::string& path) return false; } + if (!HasM2PackRuntimeKeysForArchiveLoad(m_header.key_id)) + { + TraceError("CM2Pack::Load: runtime master key with key_id=%u required for '%s' (active key_id=%u)", + m_header.key_id, + path.c_str(), + GetM2PackActiveMasterKeyId()); + return false; + } + if (m_header.manifest_offset + m_header.manifest_size > m_file.size()) { TraceError("CM2Pack::Load: manifest out of bounds in '%s'", path.c_str()); @@ -111,11 +114,18 @@ bool CM2Pack::ValidateManifest() return false; } - if (crypto_sign_verify_detached( + const auto* publicKey = GetM2PackPublicKeyForKeyId(m_header.key_id); + if (!publicKey) + { + TraceError("CM2Pack::ValidateManifest: no public key configured for key_id=%u", m_header.key_id); + return false; + } + + if (crypto_sign_verify_detached( m_header.manifest_signature, m_manifest_bytes.data(), m_manifest_bytes.size(), - GetM2PackActivePublicKey().data()) != 0) + publicKey->data()) != 0) { TraceError("CM2Pack::ValidateManifest: manifest signature mismatch"); return false; diff --git a/src/PackLib/M2Pack.h b/src/PackLib/M2Pack.h index 51230cb..853a1be 100644 --- a/src/PackLib/M2Pack.h +++ b/src/PackLib/M2Pack.h @@ -26,11 +26,12 @@ struct TM2PackHeader char magic[M2PACK_MAGIC_SIZE]; uint32_t version; uint32_t flags; + uint32_t key_id; uint64_t manifest_offset; uint64_t manifest_size; uint8_t manifest_hash[M2PACK_HASH_SIZE]; uint8_t manifest_signature[M2PACK_SIGNATURE_SIZE]; - uint8_t reserved[64]; + uint8_t reserved[60]; }; struct TM2PackManifestHeader diff --git a/src/PackLib/M2PackKeys.h b/src/PackLib/M2PackKeys.h index 0ee6804..e3bbe69 100644 --- a/src/PackLib/M2PackKeys.h +++ b/src/PackLib/M2PackKeys.h @@ -9,6 +9,9 @@ constexpr bool M2PACK_RUNTIME_MASTER_KEY_REQUIRED = true; +constexpr uint32_t M2PACK_KEY_SLOT_COUNT = 1; +constexpr std::array M2PACK_SIGN_KEY_IDS = { 1 }; + constexpr std::array M2PACK_MASTER_KEY = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -16,9 +19,11 @@ constexpr std::array M2PACK_MASTER_KEY = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -constexpr std::array M2PACK_SIGN_PUBLIC_KEY = { +constexpr std::array, M2PACK_KEY_SLOT_COUNT> M2PACK_SIGN_PUBLIC_KEYS = {{ + { 0x22, 0x69, 0x26, 0xd0, 0xa9, 0xa5, 0x53, 0x4c, 0x95, 0x45, 0xec, 0xba, 0xe9, 0x32, 0x46, 0xc9, 0x43, 0x80, 0x5c, 0x1a, 0x2c, 0x57, 0xc0, 0x03, 0xd9, 0x72, 0x41, 0x19, 0xea, 0x0b, 0xc6, 0xa4 -}; + } +}}; diff --git a/src/PackLib/M2PackRuntimeKeyProvider.cpp b/src/PackLib/M2PackRuntimeKeyProvider.cpp index a8d32ae..45d14d3 100644 --- a/src/PackLib/M2PackRuntimeKeyProvider.cpp +++ b/src/PackLib/M2PackRuntimeKeyProvider.cpp @@ -17,6 +17,7 @@ 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"; +constexpr const char* M2PACK_ENV_KEY_ID = "M2PACK_KEY_ID"; #pragma pack(push, 1) struct M2PackSharedKeys @@ -24,6 +25,7 @@ struct M2PackSharedKeys char magic[8]; uint32_t version; uint32_t flags; + uint32_t key_id; uint8_t master_key[M2PACK_KEY_SIZE]; uint8_t sign_public_key[M2PACK_PUBLIC_KEY_SIZE]; }; @@ -31,8 +33,9 @@ struct M2PackSharedKeys struct RuntimeKeyState { + uint32_t master_key_id = 0; std::array master_key {}; - std::array public_key = M2PACK_SIGN_PUBLIC_KEY; + std::array public_key {}; bool runtime_master_key = false; bool runtime_public_key = false; bool initialized = false; @@ -82,6 +85,32 @@ std::string GetEnvString(const char* name) return std::string(buffer, buffer + len); } +uint32_t ParseUInt32(const std::string& value) +{ + if (value.empty()) + return 0; + + try + { + return static_cast(std::stoul(value)); + } + catch (...) + { + return 0; + } +} + +const std::array* FindCompiledPublicKey(uint32_t keyId) +{ + for (std::size_t i = 0; i < M2PACK_SIGN_KEY_IDS.size(); ++i) + { + if (M2PACK_SIGN_KEY_IDS[i] == keyId) + return &M2PACK_SIGN_PUBLIC_KEYS[i]; + } + + return nullptr; +} + bool LoadFromSharedMapping(const std::string& mappingName) { const HANDLE mapping = OpenFileMappingA(FILE_MAP_READ, FALSE, mappingName.c_str()); @@ -108,6 +137,7 @@ bool LoadFromSharedMapping(const std::string& mappingName) } memcpy(g_state.master_key.data(), blob.master_key, g_state.master_key.size()); + g_state.master_key_id = blob.key_id; 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; @@ -129,6 +159,20 @@ void ApplyCommandLineOption(const std::string& key, const std::string& value) return; } + if (key == "--m2pack-key-id") + { + const auto parsed = ParseUInt32(value); + if (parsed != 0) + { + g_state.master_key_id = parsed; + } + else + { + TraceError("Invalid value for --m2pack-key-id"); + } + return; + } + if (key == "--m2pack-pubkey-hex") { if (ParseHexInto(value, g_state.public_key)) @@ -177,6 +221,16 @@ bool InitializeM2PackRuntimeKeyProvider(const char* commandLine) TraceError("Invalid M2PACK_MASTER_KEY_HEX value"); } + const std::string envKeyId = GetEnvString(M2PACK_ENV_KEY_ID); + if (!envKeyId.empty()) + { + const auto parsed = ParseUInt32(envKeyId); + if (parsed != 0) + g_state.master_key_id = parsed; + else + TraceError("Invalid M2PACK_KEY_ID value"); + } + const std::string envPublic = GetEnvString(M2PACK_ENV_PUBLIC_KEY); if (!envPublic.empty()) { @@ -193,7 +247,7 @@ bool InitializeM2PackRuntimeKeyProvider(const char* commandLine) 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) + if ((key == "--m2pack-key-hex" || key == "--m2pack-pubkey-hex" || key == "--m2pack-key-map" || key == "--m2pack-key-id") && i + 1 < argc) { ApplyCommandLineOption(key, argv[i + 1]); ++i; @@ -223,9 +277,19 @@ const std::array& GetM2PackActiveMasterKey() return g_state.master_key; } -const std::array& GetM2PackActivePublicKey() +const std::array* GetM2PackPublicKeyForKeyId(uint32_t keyId) { - return g_state.public_key; + if (g_state.runtime_public_key && g_state.master_key_id == keyId) + { + return &g_state.public_key; + } + + return FindCompiledPublicKey(keyId); +} + +uint32_t GetM2PackActiveMasterKeyId() +{ + return g_state.master_key_id; } bool HasM2PackRuntimeMasterKey() @@ -233,9 +297,9 @@ bool HasM2PackRuntimeMasterKey() return g_state.runtime_master_key; } -bool HasM2PackRuntimeKeysForArchiveLoad() +bool HasM2PackRuntimeKeysForArchiveLoad(uint32_t keyId) { - return g_state.runtime_master_key; + return g_state.runtime_master_key && g_state.master_key_id == keyId; } bool IsM2PackUsingRuntimeMasterKey() diff --git a/src/PackLib/M2PackRuntimeKeyProvider.h b/src/PackLib/M2PackRuntimeKeyProvider.h index 8f30880..26d19fd 100644 --- a/src/PackLib/M2PackRuntimeKeyProvider.h +++ b/src/PackLib/M2PackRuntimeKeyProvider.h @@ -6,8 +6,9 @@ bool InitializeM2PackRuntimeKeyProvider(const char* commandLine); const std::array& GetM2PackActiveMasterKey(); -const std::array& GetM2PackActivePublicKey(); +const std::array* GetM2PackPublicKeyForKeyId(uint32_t keyId); +uint32_t GetM2PackActiveMasterKeyId(); bool HasM2PackRuntimeMasterKey(); -bool HasM2PackRuntimeKeysForArchiveLoad(); +bool HasM2PackRuntimeKeysForArchiveLoad(uint32_t keyId); bool IsM2PackUsingRuntimeMasterKey(); bool IsM2PackUsingRuntimePublicKey();