diff --git a/src/PackLib/M2Pack.cpp b/src/PackLib/M2Pack.cpp index b7a994b..ccddefb 100644 --- a/src/PackLib/M2Pack.cpp +++ b/src/PackLib/M2Pack.cpp @@ -190,24 +190,19 @@ bool CM2Pack::ValidateManifest() bool CM2Pack::DecryptEntryPayload(const TM2PackEntry& entry, std::vector& decrypted, CBufferPool* pPool) { const uint64_t begin = sizeof(TM2PackHeader) + entry.data_offset; - const auto* ciphertext = reinterpret_cast(m_file.data() + begin); - - std::vector ciphertext_copy; + const auto* ciphertext = reinterpret_cast(m_file.data() + begin); if (pPool) { - ciphertext_copy = pPool->Acquire(entry.stored_size); + decrypted = pPool->Acquire(entry.stored_size); } - ciphertext_copy.resize(entry.stored_size); - memcpy(ciphertext_copy.data(), ciphertext, entry.stored_size); - decrypted.resize(entry.stored_size); unsigned long long written = 0; if (crypto_aead_xchacha20poly1305_ietf_decrypt( decrypted.data(), &written, nullptr, - ciphertext_copy.data(), - ciphertext_copy.size(), + ciphertext, + entry.stored_size, reinterpret_cast(entry.path.data()), entry.path.size(), entry.nonce.data(), @@ -215,16 +210,12 @@ bool CM2Pack::DecryptEntryPayload(const TM2PackEntry& entry, std::vectorRelease(std::move(ciphertext_copy)); + pPool->Release(std::move(decrypted)); } return false; } decrypted.resize(static_cast(written)); - if (pPool) - { - pPool->Release(std::move(ciphertext_copy)); - } return true; } @@ -253,6 +244,10 @@ bool CM2Pack::GetFileWithPool(const TM2PackEntry& entry, std::vector& r result.resize(entry.original_size); ZSTD_DCtx* dctx = GetThreadLocalZstdContext(); size_t written = ZSTD_decompressDCtx(dctx, result.data(), result.size(), compressed.data(), compressed.size()); + if (pPool) + { + pPool->Release(std::move(compressed)); + } if (ZSTD_isError(written) || written != entry.original_size) { TraceError("CM2Pack::GetFileWithPool: zstd failed for '%s'", entry.path.c_str()); @@ -262,10 +257,19 @@ bool CM2Pack::GetFileWithPool(const TM2PackEntry& entry, std::vector& r } default: + if (pPool) + { + pPool->Release(std::move(compressed)); + } TraceError("CM2Pack::GetFileWithPool: unsupported compression %u for '%s'", entry.compression, entry.path.c_str()); return false; } + if (!ShouldVerifyM2PackPlaintextHash()) + { + return true; + } + std::array plain_hash {}; crypto_generichash( plain_hash.data(), diff --git a/src/PackLib/M2PackRuntimeKeyProvider.cpp b/src/PackLib/M2PackRuntimeKeyProvider.cpp index 45d14d3..20fcc1f 100644 --- a/src/PackLib/M2PackRuntimeKeyProvider.cpp +++ b/src/PackLib/M2PackRuntimeKeyProvider.cpp @@ -1,6 +1,7 @@ #include "M2PackRuntimeKeyProvider.h" #include +#include #include #include @@ -18,6 +19,7 @@ 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"; +constexpr const char* M2PACK_ENV_STRICT_HASH = "M2PACK_STRICT_HASH"; #pragma pack(push, 1) struct M2PackSharedKeys @@ -38,6 +40,7 @@ struct RuntimeKeyState std::array public_key {}; bool runtime_master_key = false; bool runtime_public_key = false; + bool verify_plaintext_hash = false; bool initialized = false; }; @@ -100,6 +103,31 @@ uint32_t ParseUInt32(const std::string& value) } } +bool ParseBoolFlag(std::string value, bool& out) +{ + value.erase(std::remove_if(value.begin(), value.end(), [](unsigned char ch) { + return std::isspace(ch) != 0; + }), value.end()); + + std::transform(value.begin(), value.end(), value.begin(), [](unsigned char ch) { + return static_cast(std::tolower(ch)); + }); + + if (value == "1" || value == "true" || value == "yes" || value == "on") + { + out = true; + return true; + } + + if (value == "0" || value == "false" || value == "no" || value == "off") + { + out = false; + return true; + } + + return false; +} + const std::array* FindCompiledPublicKey(uint32_t keyId) { for (std::size_t i = 0; i < M2PACK_SIGN_KEY_IDS.size(); ++i) @@ -191,6 +219,19 @@ void ApplyCommandLineOption(const std::string& key, const std::string& value) LoadFromSharedMapping(value); return; } + + if (key == "--m2pack-strict-hash") + { + bool enabled = false; + if (ParseBoolFlag(value, enabled)) + { + g_state.verify_plaintext_hash = enabled; + } + else + { + TraceError("Invalid value for --m2pack-strict-hash"); + } + } } } // namespace @@ -201,6 +242,9 @@ bool InitializeM2PackRuntimeKeyProvider(const char* commandLine) return true; g_state = RuntimeKeyState {}; +#ifdef _DEBUG + g_state.verify_plaintext_hash = true; +#endif const std::string mapName = GetEnvString(M2PACK_ENV_MAP_NAME); if (!mapName.empty()) @@ -240,6 +284,16 @@ bool InitializeM2PackRuntimeKeyProvider(const char* commandLine) TraceError("Invalid M2PACK_SIGN_PUBKEY_HEX value"); } + const std::string envStrictHash = GetEnvString(M2PACK_ENV_STRICT_HASH); + if (!envStrictHash.empty()) + { + bool enabled = false; + if (ParseBoolFlag(envStrictHash, enabled)) + g_state.verify_plaintext_hash = enabled; + else + TraceError("Invalid M2PACK_STRICT_HASH value"); + } + int argc = 0; PCHAR* argv = CommandLineToArgv(const_cast(commandLine ? commandLine : ""), &argc); if (argv) @@ -247,6 +301,31 @@ bool InitializeM2PackRuntimeKeyProvider(const char* commandLine) for (int i = 0; i < argc; ++i) { const std::string key = argv[i]; + if (key == "--m2pack-strict-hash") + { + bool enabled = true; + if (i + 1 < argc) + { + const std::string next = argv[i + 1]; + if (!next.starts_with("--")) + { + if (!ParseBoolFlag(next, enabled)) + { + TraceError("Invalid value for --m2pack-strict-hash"); + } + else + { + g_state.verify_plaintext_hash = enabled; + } + ++i; + continue; + } + } + + g_state.verify_plaintext_hash = enabled; + continue; + } + 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]); @@ -268,6 +347,11 @@ bool InitializeM2PackRuntimeKeyProvider(const char* commandLine) Tracef("M2Pack runtime key provider: no runtime master key available; .m2p loading will be denied\n"); } + if (g_state.verify_plaintext_hash) + { + Tracef("M2Pack runtime key provider: plaintext hash verification enabled\n"); + } + g_state.initialized = true; return true; } @@ -311,3 +395,8 @@ bool IsM2PackUsingRuntimePublicKey() { return g_state.runtime_public_key; } + +bool ShouldVerifyM2PackPlaintextHash() +{ + return g_state.verify_plaintext_hash; +} diff --git a/src/PackLib/M2PackRuntimeKeyProvider.h b/src/PackLib/M2PackRuntimeKeyProvider.h index 26d19fd..124b39f 100644 --- a/src/PackLib/M2PackRuntimeKeyProvider.h +++ b/src/PackLib/M2PackRuntimeKeyProvider.h @@ -12,3 +12,4 @@ bool HasM2PackRuntimeMasterKey(); bool HasM2PackRuntimeKeysForArchiveLoad(uint32_t keyId); bool IsM2PackUsingRuntimeMasterKey(); bool IsM2PackUsingRuntimePublicKey(); +bool ShouldVerifyM2PackPlaintextHash();