Support experimental stream M2Pack archives
Some checks failed
build / Windows Build (push) Has been cancelled
Some checks failed
build / Windows Build (push) Has been cancelled
This commit is contained in:
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
|
||||
#include <zstd.h>
|
||||
|
||||
#include "EterBase/Debug.h"
|
||||
@@ -38,6 +37,44 @@ bool ReadPod(const uint8_t* bytes, std::size_t size, std::size_t& offset, T& out
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsSupportedM2PackVersion(uint32_t version)
|
||||
{
|
||||
return version == M2PACK_VERSION_AEAD || version == M2PACK_VERSION_STREAM;
|
||||
}
|
||||
|
||||
bool VerifyM2PackHash(
|
||||
std::string_view stage,
|
||||
const uint8_t* input,
|
||||
std::size_t inputSize,
|
||||
const std::array<uint8_t, M2PACK_HASH_SIZE>& expected,
|
||||
const char* path)
|
||||
{
|
||||
std::array<uint8_t, M2PACK_HASH_SIZE> actual {};
|
||||
const auto hashStart = std::chrono::steady_clock::now();
|
||||
crypto_generichash(
|
||||
actual.data(),
|
||||
actual.size(),
|
||||
input,
|
||||
inputSize,
|
||||
nullptr,
|
||||
0);
|
||||
RecordPackProfileStage(
|
||||
"m2p",
|
||||
stage,
|
||||
inputSize,
|
||||
actual.size(),
|
||||
static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::steady_clock::now() - hashStart).count()));
|
||||
|
||||
if (memcmp(actual.data(), expected.data(), actual.size()) != 0)
|
||||
{
|
||||
TraceError("CM2Pack: %s mismatch for '%s'", std::string(stage).c_str(), path);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool CM2Pack::Load(const std::string& path)
|
||||
@@ -66,7 +103,7 @@ bool CM2Pack::Load(const std::string& path)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_header.version != 1)
|
||||
if (!IsSupportedM2PackVersion(m_header.version))
|
||||
{
|
||||
TraceError("CM2Pack::Load: unsupported version %u in '%s'", m_header.version, path.c_str());
|
||||
return false;
|
||||
@@ -170,26 +207,54 @@ bool CM2Pack::ValidateManifest()
|
||||
|
||||
for (uint32_t i = 0; i < manifest_header.entry_count; ++i)
|
||||
{
|
||||
TM2PackManifestEntryFixed fixed {};
|
||||
if (!ReadPod(m_manifest_bytes.data(), m_manifest_bytes.size(), offset, fixed))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (offset + fixed.path_size > m_manifest_bytes.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TM2PackEntry entry;
|
||||
entry.path.assign(reinterpret_cast<const char*>(m_manifest_bytes.data() + offset), fixed.path_size);
|
||||
offset += fixed.path_size;
|
||||
entry.compression = fixed.compression;
|
||||
entry.data_offset = fixed.data_offset;
|
||||
entry.original_size = fixed.original_size;
|
||||
entry.stored_size = fixed.stored_size;
|
||||
memcpy(entry.nonce.data(), fixed.nonce, entry.nonce.size());
|
||||
memcpy(entry.plaintext_hash.data(), fixed.plaintext_hash, entry.plaintext_hash.size());
|
||||
uint16_t pathSize = 0;
|
||||
|
||||
if (m_header.version == M2PACK_VERSION_AEAD)
|
||||
{
|
||||
TM2PackManifestEntryFixed fixed {};
|
||||
if (!ReadPod(m_manifest_bytes.data(), m_manifest_bytes.size(), offset, fixed))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pathSize = fixed.path_size;
|
||||
entry.compression = fixed.compression;
|
||||
entry.data_offset = fixed.data_offset;
|
||||
entry.original_size = fixed.original_size;
|
||||
entry.stored_size = fixed.stored_size;
|
||||
memcpy(entry.nonce.data(), fixed.nonce, entry.nonce.size());
|
||||
memcpy(entry.plaintext_hash.data(), fixed.plaintext_hash, entry.plaintext_hash.size());
|
||||
}
|
||||
else if (m_header.version == M2PACK_VERSION_STREAM)
|
||||
{
|
||||
TM2PackManifestEntryFixedV2 fixed {};
|
||||
if (!ReadPod(m_manifest_bytes.data(), m_manifest_bytes.size(), offset, fixed))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pathSize = fixed.path_size;
|
||||
entry.compression = fixed.compression;
|
||||
entry.data_offset = fixed.data_offset;
|
||||
entry.original_size = fixed.original_size;
|
||||
entry.stored_size = fixed.stored_size;
|
||||
memcpy(entry.nonce.data(), fixed.nonce, entry.nonce.size());
|
||||
memcpy(entry.payload_hash.data(), fixed.payload_hash, entry.payload_hash.size());
|
||||
memcpy(entry.plaintext_hash.data(), fixed.plaintext_hash, entry.plaintext_hash.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (offset + pathSize > m_manifest_bytes.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
entry.path.assign(reinterpret_cast<const char*>(m_manifest_bytes.data() + offset), pathSize);
|
||||
offset += pathSize;
|
||||
|
||||
const uint64_t payload_begin = sizeof(TM2PackHeader);
|
||||
const uint64_t payload_end = m_header.manifest_offset;
|
||||
@@ -231,17 +296,66 @@ bool CM2Pack::DecryptEntryPayload(const TM2PackEntry& entry, std::vector<uint8_t
|
||||
}
|
||||
decrypted.resize(entry.stored_size);
|
||||
const auto decryptStart = std::chrono::steady_clock::now();
|
||||
unsigned long long written = 0;
|
||||
if (crypto_aead_xchacha20poly1305_ietf_decrypt(
|
||||
decrypted.data(),
|
||||
&written,
|
||||
nullptr,
|
||||
ciphertext,
|
||||
entry.stored_size,
|
||||
reinterpret_cast<const unsigned char*>(entry.path.data()),
|
||||
entry.path.size(),
|
||||
entry.nonce.data(),
|
||||
GetM2PackActiveMasterKey().data()) != 0)
|
||||
std::size_t written = entry.stored_size;
|
||||
|
||||
if (m_header.version == M2PACK_VERSION_AEAD)
|
||||
{
|
||||
unsigned long long aeadWritten = 0;
|
||||
if (crypto_aead_xchacha20poly1305_ietf_decrypt(
|
||||
decrypted.data(),
|
||||
&aeadWritten,
|
||||
nullptr,
|
||||
ciphertext,
|
||||
entry.stored_size,
|
||||
reinterpret_cast<const unsigned char*>(entry.path.data()),
|
||||
entry.path.size(),
|
||||
entry.nonce.data(),
|
||||
GetM2PackActiveMasterKey().data()) != 0)
|
||||
{
|
||||
if (pPool)
|
||||
{
|
||||
pPool->Release(std::move(decrypted));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
written = static_cast<std::size_t>(aeadWritten);
|
||||
RecordPackProfileStage(
|
||||
"m2p",
|
||||
"aead_decrypt",
|
||||
entry.stored_size,
|
||||
written,
|
||||
static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::steady_clock::now() - decryptStart).count()));
|
||||
}
|
||||
else if (m_header.version == M2PACK_VERSION_STREAM)
|
||||
{
|
||||
if (!decrypted.empty())
|
||||
{
|
||||
crypto_stream_xchacha20_xor(
|
||||
decrypted.data(),
|
||||
ciphertext,
|
||||
entry.stored_size,
|
||||
entry.nonce.data(),
|
||||
GetM2PackActiveMasterKey().data());
|
||||
}
|
||||
RecordPackProfileStage(
|
||||
"m2p",
|
||||
"stream_decrypt",
|
||||
entry.stored_size,
|
||||
written,
|
||||
static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::steady_clock::now() - decryptStart).count()));
|
||||
|
||||
if (!VerifyM2PackHash("payload_hash", decrypted.data(), decrypted.size(), entry.payload_hash, entry.path.c_str()))
|
||||
{
|
||||
if (pPool)
|
||||
{
|
||||
pPool->Release(std::move(decrypted));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pPool)
|
||||
{
|
||||
@@ -249,15 +363,8 @@ bool CM2Pack::DecryptEntryPayload(const TM2PackEntry& entry, std::vector<uint8_t
|
||||
}
|
||||
return false;
|
||||
}
|
||||
RecordPackProfileStage(
|
||||
"m2p",
|
||||
"aead_decrypt",
|
||||
entry.stored_size,
|
||||
written,
|
||||
static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::steady_clock::now() - decryptStart).count()));
|
||||
|
||||
decrypted.resize(static_cast<std::size_t>(written));
|
||||
decrypted.resize(written);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -315,32 +422,12 @@ bool CM2Pack::GetFileWithPool(const TM2PackEntry& entry, std::vector<uint8_t>& r
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ShouldVerifyM2PackPlaintextHash())
|
||||
if (ShouldVerifyM2PackPlaintextHash())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
std::array<uint8_t, M2PACK_HASH_SIZE> plain_hash {};
|
||||
const auto hashStart = std::chrono::steady_clock::now();
|
||||
crypto_generichash(
|
||||
plain_hash.data(),
|
||||
plain_hash.size(),
|
||||
result.data(),
|
||||
result.size(),
|
||||
nullptr,
|
||||
0);
|
||||
RecordPackProfileStage(
|
||||
"m2p",
|
||||
"plaintext_hash",
|
||||
result.size(),
|
||||
plain_hash.size(),
|
||||
static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::steady_clock::now() - hashStart).count()));
|
||||
|
||||
if (memcmp(plain_hash.data(), entry.plaintext_hash.data(), plain_hash.size()) != 0)
|
||||
{
|
||||
TraceError("CM2Pack::GetFileWithPool: plaintext hash mismatch for '%s'", entry.path.c_str());
|
||||
return false;
|
||||
if (!VerifyM2PackHash("plaintext_hash", result.data(), result.size(), entry.plaintext_hash, entry.path.c_str()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -19,6 +19,8 @@ constexpr std::size_t M2PACK_SIGNATURE_SIZE = 64;
|
||||
constexpr std::size_t M2PACK_KEY_SIZE = crypto_aead_xchacha20poly1305_ietf_KEYBYTES;
|
||||
constexpr std::size_t M2PACK_NONCE_SIZE = crypto_aead_xchacha20poly1305_ietf_NPUBBYTES;
|
||||
constexpr std::size_t M2PACK_PUBLIC_KEY_SIZE = crypto_sign_PUBLICKEYBYTES;
|
||||
constexpr uint32_t M2PACK_VERSION_AEAD = 1;
|
||||
constexpr uint32_t M2PACK_VERSION_STREAM = 2;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct TM2PackHeader
|
||||
@@ -51,6 +53,19 @@ struct TM2PackManifestEntryFixed
|
||||
uint8_t nonce[M2PACK_NONCE_SIZE];
|
||||
uint8_t plaintext_hash[M2PACK_HASH_SIZE];
|
||||
};
|
||||
|
||||
struct TM2PackManifestEntryFixedV2
|
||||
{
|
||||
uint16_t path_size;
|
||||
uint8_t compression;
|
||||
uint8_t flags;
|
||||
uint64_t data_offset;
|
||||
uint64_t original_size;
|
||||
uint64_t stored_size;
|
||||
uint8_t nonce[M2PACK_NONCE_SIZE];
|
||||
uint8_t payload_hash[M2PACK_HASH_SIZE];
|
||||
uint8_t plaintext_hash[M2PACK_HASH_SIZE];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
struct TM2PackEntry
|
||||
@@ -61,6 +76,7 @@ struct TM2PackEntry
|
||||
uint64_t original_size = 0;
|
||||
uint64_t stored_size = 0;
|
||||
std::array<uint8_t, M2PACK_NONCE_SIZE> nonce {};
|
||||
std::array<uint8_t, M2PACK_HASH_SIZE> payload_hash {};
|
||||
std::array<uint8_t, M2PACK_HASH_SIZE> plaintext_hash {};
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user