Reduce m2pack client hot-path overhead
Some checks failed
build / Windows Build (push) Has been cancelled

This commit is contained in:
server
2026-04-15 15:43:26 +02:00
parent cb0867432e
commit ef7cdf2809
3 changed files with 108 additions and 14 deletions

View File

@@ -190,24 +190,19 @@ bool CM2Pack::ValidateManifest()
bool CM2Pack::DecryptEntryPayload(const TM2PackEntry& entry, std::vector<uint8_t>& decrypted, CBufferPool* pPool) bool CM2Pack::DecryptEntryPayload(const TM2PackEntry& entry, std::vector<uint8_t>& decrypted, CBufferPool* pPool)
{ {
const uint64_t begin = sizeof(TM2PackHeader) + entry.data_offset; const uint64_t begin = sizeof(TM2PackHeader) + entry.data_offset;
const auto* ciphertext = reinterpret_cast<const uint8_t*>(m_file.data() + begin); const auto* ciphertext = reinterpret_cast<const unsigned char*>(m_file.data() + begin);
std::vector<uint8_t> ciphertext_copy;
if (pPool) 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); decrypted.resize(entry.stored_size);
unsigned long long written = 0; unsigned long long written = 0;
if (crypto_aead_xchacha20poly1305_ietf_decrypt( if (crypto_aead_xchacha20poly1305_ietf_decrypt(
decrypted.data(), decrypted.data(),
&written, &written,
nullptr, nullptr,
ciphertext_copy.data(), ciphertext,
ciphertext_copy.size(), entry.stored_size,
reinterpret_cast<const unsigned char*>(entry.path.data()), reinterpret_cast<const unsigned char*>(entry.path.data()),
entry.path.size(), entry.path.size(),
entry.nonce.data(), entry.nonce.data(),
@@ -215,16 +210,12 @@ bool CM2Pack::DecryptEntryPayload(const TM2PackEntry& entry, std::vector<uint8_t
{ {
if (pPool) if (pPool)
{ {
pPool->Release(std::move(ciphertext_copy)); pPool->Release(std::move(decrypted));
} }
return false; return false;
} }
decrypted.resize(static_cast<std::size_t>(written)); decrypted.resize(static_cast<std::size_t>(written));
if (pPool)
{
pPool->Release(std::move(ciphertext_copy));
}
return true; return true;
} }
@@ -253,6 +244,10 @@ bool CM2Pack::GetFileWithPool(const TM2PackEntry& entry, std::vector<uint8_t>& r
result.resize(entry.original_size); result.resize(entry.original_size);
ZSTD_DCtx* dctx = GetThreadLocalZstdContext(); ZSTD_DCtx* dctx = GetThreadLocalZstdContext();
size_t written = ZSTD_decompressDCtx(dctx, result.data(), result.size(), compressed.data(), compressed.size()); 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) if (ZSTD_isError(written) || written != entry.original_size)
{ {
TraceError("CM2Pack::GetFileWithPool: zstd failed for '%s'", entry.path.c_str()); TraceError("CM2Pack::GetFileWithPool: zstd failed for '%s'", entry.path.c_str());
@@ -262,10 +257,19 @@ bool CM2Pack::GetFileWithPool(const TM2PackEntry& entry, std::vector<uint8_t>& r
} }
default: default:
if (pPool)
{
pPool->Release(std::move(compressed));
}
TraceError("CM2Pack::GetFileWithPool: unsupported compression %u for '%s'", entry.compression, entry.path.c_str()); TraceError("CM2Pack::GetFileWithPool: unsupported compression %u for '%s'", entry.compression, entry.path.c_str());
return false; return false;
} }
if (!ShouldVerifyM2PackPlaintextHash())
{
return true;
}
std::array<uint8_t, M2PACK_HASH_SIZE> plain_hash {}; std::array<uint8_t, M2PACK_HASH_SIZE> plain_hash {};
crypto_generichash( crypto_generichash(
plain_hash.data(), plain_hash.data(),

View File

@@ -1,6 +1,7 @@
#include "M2PackRuntimeKeyProvider.h" #include "M2PackRuntimeKeyProvider.h"
#include <algorithm> #include <algorithm>
#include <cctype>
#include <cstring> #include <cstring>
#include <string> #include <string>
@@ -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_PUBLIC_KEY = "M2PACK_SIGN_PUBKEY_HEX";
constexpr const char* M2PACK_ENV_MAP_NAME = "M2PACK_KEY_MAP"; 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_KEY_ID = "M2PACK_KEY_ID";
constexpr const char* M2PACK_ENV_STRICT_HASH = "M2PACK_STRICT_HASH";
#pragma pack(push, 1) #pragma pack(push, 1)
struct M2PackSharedKeys struct M2PackSharedKeys
@@ -38,6 +40,7 @@ struct RuntimeKeyState
std::array<uint8_t, M2PACK_PUBLIC_KEY_SIZE> public_key {}; std::array<uint8_t, M2PACK_PUBLIC_KEY_SIZE> public_key {};
bool runtime_master_key = false; bool runtime_master_key = false;
bool runtime_public_key = false; bool runtime_public_key = false;
bool verify_plaintext_hash = false;
bool initialized = 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<char>(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<uint8_t, M2PACK_PUBLIC_KEY_SIZE>* FindCompiledPublicKey(uint32_t keyId) const std::array<uint8_t, M2PACK_PUBLIC_KEY_SIZE>* FindCompiledPublicKey(uint32_t keyId)
{ {
for (std::size_t i = 0; i < M2PACK_SIGN_KEY_IDS.size(); ++i) 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); LoadFromSharedMapping(value);
return; 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 } // namespace
@@ -201,6 +242,9 @@ bool InitializeM2PackRuntimeKeyProvider(const char* commandLine)
return true; return true;
g_state = RuntimeKeyState {}; g_state = RuntimeKeyState {};
#ifdef _DEBUG
g_state.verify_plaintext_hash = true;
#endif
const std::string mapName = GetEnvString(M2PACK_ENV_MAP_NAME); const std::string mapName = GetEnvString(M2PACK_ENV_MAP_NAME);
if (!mapName.empty()) if (!mapName.empty())
@@ -240,6 +284,16 @@ bool InitializeM2PackRuntimeKeyProvider(const char* commandLine)
TraceError("Invalid M2PACK_SIGN_PUBKEY_HEX value"); 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; int argc = 0;
PCHAR* argv = CommandLineToArgv(const_cast<PCHAR>(commandLine ? commandLine : ""), &argc); PCHAR* argv = CommandLineToArgv(const_cast<PCHAR>(commandLine ? commandLine : ""), &argc);
if (argv) if (argv)
@@ -247,6 +301,31 @@ bool InitializeM2PackRuntimeKeyProvider(const char* commandLine)
for (int i = 0; i < argc; ++i) for (int i = 0; i < argc; ++i)
{ {
const std::string key = argv[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) 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]); 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"); 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; g_state.initialized = true;
return true; return true;
} }
@@ -311,3 +395,8 @@ bool IsM2PackUsingRuntimePublicKey()
{ {
return g_state.runtime_public_key; return g_state.runtime_public_key;
} }
bool ShouldVerifyM2PackPlaintextHash()
{
return g_state.verify_plaintext_hash;
}

View File

@@ -12,3 +12,4 @@ bool HasM2PackRuntimeMasterKey();
bool HasM2PackRuntimeKeysForArchiveLoad(uint32_t keyId); bool HasM2PackRuntimeKeysForArchiveLoad(uint32_t keyId);
bool IsM2PackUsingRuntimeMasterKey(); bool IsM2PackUsingRuntimeMasterKey();
bool IsM2PackUsingRuntimePublicKey(); bool IsM2PackUsingRuntimePublicKey();
bool ShouldVerifyM2PackPlaintextHash();