diff --git a/src/archive.cpp b/src/archive.cpp index 4019575..b27b340 100644 --- a/src/archive.cpp +++ b/src/archive.cpp @@ -15,6 +15,7 @@ namespace { constexpr char kArchiveMagic[kMagicSize] = {'M', '2', 'P', 'A', 'C', 'K', '2', '\0'}; +constexpr std::uint32_t kArchiveVersion = 1; constexpr std::size_t kCompressionMinSavingsBytes = 64; constexpr std::size_t kCompressionMinSavingsPercent = 5; @@ -45,35 +46,6 @@ Compression select_compression_mode( return Compression::Zstd; } -bool supports_archive_version(std::uint32_t version) -{ - return version == kArchiveVersionAead || version == kArchiveVersionStream; -} - -std::vector decrypt_payload_for_entry( - std::uint32_t version, - const std::vector& ciphertext, - const ManifestEntry& entry, - const std::array& master_key) -{ - if (version == kArchiveVersionAead) - { - return decrypt_payload(ciphertext, master_key, entry.nonce, entry.path); - } - - if (version == kArchiveVersionStream) - { - auto payload = decrypt_payload_stream(ciphertext, master_key, entry.nonce); - if (hash_bytes(payload) != entry.payload_hash) - { - fail("Payload hash mismatch: " + entry.path); - } - return payload; - } - - fail("Unsupported archive version"); -} - } // namespace std::string normalize_path(const std::filesystem::path& root, const std::filesystem::path& file) @@ -102,7 +74,7 @@ std::vector collect_files(const std::filesystem::path& ro return files; } -std::vector serialize_manifest(const std::vector& entries, std::uint32_t version) +std::vector serialize_manifest(const std::vector& entries) { std::vector bytes; const ManifestFixedHeader fixed { @@ -118,45 +90,23 @@ std::vector serialize_manifest(const std::vector& e fail("Path too long for manifest entry: " + entry.path); } - if (version == kArchiveVersionAead) - { - ManifestEntryFixed pod {}; - pod.path_size = static_cast(entry.path.size()); - pod.compression = static_cast(entry.compression); - pod.flags = 0; - pod.data_offset = entry.data_offset; - pod.original_size = entry.original_size; - pod.stored_size = entry.stored_size; - std::memcpy(pod.nonce, entry.nonce.data(), entry.nonce.size()); - std::memcpy(pod.plaintext_hash, entry.plaintext_hash.data(), entry.plaintext_hash.size()); - append_pod(bytes, pod); - } - else if (version == kArchiveVersionStream) - { - ManifestEntryFixedV2 pod {}; - pod.path_size = static_cast(entry.path.size()); - pod.compression = static_cast(entry.compression); - pod.flags = 0; - pod.data_offset = entry.data_offset; - pod.original_size = entry.original_size; - pod.stored_size = entry.stored_size; - std::memcpy(pod.nonce, entry.nonce.data(), entry.nonce.size()); - std::memcpy(pod.payload_hash, entry.payload_hash.data(), entry.payload_hash.size()); - std::memcpy(pod.plaintext_hash, entry.plaintext_hash.data(), entry.plaintext_hash.size()); - append_pod(bytes, pod); - } - else - { - fail("Unsupported archive version"); - } - + ManifestEntryFixed pod {}; + pod.path_size = static_cast(entry.path.size()); + pod.compression = static_cast(entry.compression); + pod.flags = 0; + pod.data_offset = entry.data_offset; + pod.original_size = entry.original_size; + pod.stored_size = entry.stored_size; + std::memcpy(pod.nonce, entry.nonce.data(), entry.nonce.size()); + std::memcpy(pod.plaintext_hash, entry.plaintext_hash.data(), entry.plaintext_hash.size()); + append_pod(bytes, pod); bytes.insert(bytes.end(), entry.path.begin(), entry.path.end()); } return bytes; } -std::vector parse_manifest(const std::vector& bytes, std::uint32_t version) +std::vector parse_manifest(const std::vector& bytes) { std::size_t offset = 0; const auto fixed = read_pod(bytes, offset); @@ -166,44 +116,21 @@ std::vector parse_manifest(const std::vector& bytes for (std::uint32_t i = 0; i < fixed.entry_count; ++i) { - ManifestEntry entry; - std::uint16_t path_size = 0; - - if (version == kArchiveVersionAead) - { - const auto pod = read_pod(bytes, offset); - path_size = pod.path_size; - entry.compression = static_cast(pod.compression); - entry.data_offset = pod.data_offset; - entry.original_size = pod.original_size; - entry.stored_size = pod.stored_size; - std::memcpy(entry.nonce.data(), pod.nonce, entry.nonce.size()); - std::memcpy(entry.plaintext_hash.data(), pod.plaintext_hash, entry.plaintext_hash.size()); - } - else if (version == kArchiveVersionStream) - { - const auto pod = read_pod(bytes, offset); - path_size = pod.path_size; - entry.compression = static_cast(pod.compression); - entry.data_offset = pod.data_offset; - entry.original_size = pod.original_size; - entry.stored_size = pod.stored_size; - std::memcpy(entry.nonce.data(), pod.nonce, entry.nonce.size()); - std::memcpy(entry.payload_hash.data(), pod.payload_hash, entry.payload_hash.size()); - std::memcpy(entry.plaintext_hash.data(), pod.plaintext_hash, entry.plaintext_hash.size()); - } - else - { - fail("Unsupported archive version"); - } - - if (offset + path_size > bytes.size()) + const auto pod = read_pod(bytes, offset); + if (offset + pod.path_size > bytes.size()) { fail("Manifest path data exceeds buffer"); } - entry.path.assign(reinterpret_cast(bytes.data() + offset), path_size); - offset += path_size; + ManifestEntry entry; + entry.path.assign(reinterpret_cast(bytes.data() + offset), pod.path_size); + offset += pod.path_size; + entry.compression = static_cast(pod.compression); + entry.data_offset = pod.data_offset; + entry.original_size = pod.original_size; + entry.stored_size = pod.stored_size; + std::memcpy(entry.nonce.data(), pod.nonce, entry.nonce.size()); + std::memcpy(entry.plaintext_hash.data(), pod.plaintext_hash, entry.plaintext_hash.size()); entries.push_back(std::move(entry)); } @@ -244,23 +171,10 @@ BuildResult build_archive( entry.data_offset = payload_bytes.size(); entry.original_size = plain.size(); entry.nonce = random_nonce(); - const auto& payload = entry.compression == Compression::Zstd ? compressed : plain; - entry.payload_hash = hash_bytes(payload); entry.plaintext_hash = hash_bytes(plain); - std::vector encrypted; - if (kCurrentArchiveVersion == kArchiveVersionAead) - { - encrypted = encrypt_payload(payload, require_master_key(keys), entry.nonce, entry.path); - } - else if (kCurrentArchiveVersion == kArchiveVersionStream) - { - encrypted = encrypt_payload_stream(payload, require_master_key(keys), entry.nonce); - } - else - { - fail("Unsupported archive version"); - } + const auto& payload = entry.compression == Compression::Zstd ? compressed : plain; + const auto encrypted = encrypt_payload(payload, require_master_key(keys), entry.nonce, entry.path); entry.stored_size = encrypted.size(); payload_bytes.insert(payload_bytes.end(), encrypted.begin(), encrypted.end()); @@ -271,13 +185,13 @@ BuildResult build_archive( result.total_stored_bytes += encrypted.size(); } - const auto manifest_bytes = serialize_manifest(manifest_entries, kCurrentArchiveVersion); + const auto manifest_bytes = serialize_manifest(manifest_entries); const auto manifest_hash = hash_bytes(manifest_bytes); const auto signature = sign_detached(manifest_bytes, *keys.signing_secret_key); ArchiveHeader header {}; std::memcpy(header.magic, kArchiveMagic, sizeof(kArchiveMagic)); - header.version = kCurrentArchiveVersion; + header.version = kArchiveVersion; header.flags = 0; header.key_id = keys.key_id; header.manifest_offset = sizeof(ArchiveHeader) + payload_bytes.size(); @@ -325,7 +239,7 @@ LoadedArchive load_archive(const std::filesystem::path& archive_path) { fail("Archive magic mismatch"); } - if (!supports_archive_version(archive.header.version)) + if (archive.header.version != kArchiveVersion) { fail("Unsupported archive version"); } @@ -338,7 +252,7 @@ LoadedArchive load_archive(const std::filesystem::path& archive_path) archive.file_bytes.begin() + static_cast(archive.header.manifest_offset), archive.file_bytes.begin() + static_cast(archive.header.manifest_offset + archive.header.manifest_size)); - archive.entries = parse_manifest(archive.manifest_bytes, archive.header.version); + archive.entries = parse_manifest(archive.manifest_bytes); return archive; } @@ -394,14 +308,11 @@ bool verify_archive( for (const auto& entry : archive.entries) { const auto plain = extract_entry(archive, entry, keys.master_key); - if (archive.header.version == kArchiveVersionAead || archive.header.version == kArchiveVersionStream) + const auto hash = hash_bytes(plain); + if (hash != entry.plaintext_hash) { - const auto hash = hash_bytes(plain); - if (hash != entry.plaintext_hash) - { - error = "Plaintext hash mismatch: " + entry.path; - return false; - } + error = "Plaintext hash mismatch: " + entry.path; + return false; } } } @@ -426,13 +337,13 @@ std::vector extract_entry( archive.file_bytes.begin() + static_cast(begin), archive.file_bytes.begin() + static_cast(end)); - const auto payload = decrypt_payload_for_entry(archive.header.version, ciphertext, entry, master_key); + const auto compressed = decrypt_payload(ciphertext, master_key, entry.nonce, entry.path); switch (entry.compression) { case Compression::None: - return payload; + return compressed; case Compression::Zstd: - return decompress_zstd(payload, static_cast(entry.original_size)); + return decompress_zstd(compressed, static_cast(entry.original_size)); default: fail("Unsupported compression mode"); } diff --git a/src/archive.h b/src/archive.h index d4c4ffa..e55354c 100644 --- a/src/archive.h +++ b/src/archive.h @@ -16,9 +16,6 @@ constexpr std::size_t kHashSize = 32; constexpr std::size_t kSignatureSize = 64; constexpr std::size_t kAeadKeySize = 32; constexpr std::size_t kAeadNonceSize = 24; -constexpr std::uint32_t kArchiveVersionAead = 1; -constexpr std::uint32_t kArchiveVersionStream = 2; -constexpr std::uint32_t kCurrentArchiveVersion = kArchiveVersionAead; enum class Compression : std::uint8_t { @@ -57,19 +54,6 @@ struct ManifestEntryFixed std::uint8_t nonce[kAeadNonceSize]; std::uint8_t plaintext_hash[kHashSize]; }; - -struct ManifestEntryFixedV2 -{ - std::uint16_t path_size; - std::uint8_t compression; - std::uint8_t flags; - std::uint64_t data_offset; - std::uint64_t original_size; - std::uint64_t stored_size; - std::uint8_t nonce[kAeadNonceSize]; - std::uint8_t payload_hash[kHashSize]; - std::uint8_t plaintext_hash[kHashSize]; -}; #pragma pack(pop) struct ManifestEntry @@ -80,7 +64,6 @@ struct ManifestEntry std::uint64_t original_size = 0; std::uint64_t stored_size = 0; std::array nonce {}; - std::array payload_hash {}; std::array plaintext_hash {}; }; @@ -133,7 +116,7 @@ void extract_all( const std::filesystem::path& output_dir, const std::array& master_key); -std::vector serialize_manifest(const std::vector& entries, std::uint32_t version); -std::vector parse_manifest(const std::vector& bytes, std::uint32_t version); +std::vector serialize_manifest(const std::vector& entries); +std::vector parse_manifest(const std::vector& bytes); } // namespace m2pack diff --git a/src/crypto.cpp b/src/crypto.cpp index 5a39508..f1364af 100644 --- a/src/crypto.cpp +++ b/src/crypto.cpp @@ -205,42 +205,6 @@ std::vector decrypt_payload( return plaintext; } -std::vector encrypt_payload_stream( - const std::vector& plaintext, - const std::array& key, - const std::array& nonce) -{ - std::vector ciphertext(plaintext.size()); - if (!plaintext.empty()) - { - crypto_stream_xchacha20_xor( - ciphertext.data(), - plaintext.data(), - plaintext.size(), - nonce.data(), - key.data()); - } - return ciphertext; -} - -std::vector decrypt_payload_stream( - const std::vector& ciphertext, - const std::array& key, - const std::array& nonce) -{ - std::vector plaintext(ciphertext.size()); - if (!ciphertext.empty()) - { - crypto_stream_xchacha20_xor( - plaintext.data(), - ciphertext.data(), - ciphertext.size(), - nonce.data(), - key.data()); - } - return plaintext; -} - std::vector sign_detached( const std::vector& data, const std::vector& secret_key) diff --git a/src/crypto.h b/src/crypto.h index e6bdb9e..2e131e1 100644 --- a/src/crypto.h +++ b/src/crypto.h @@ -38,16 +38,6 @@ std::vector decrypt_payload( const std::array& nonce, std::string_view associated_data); -std::vector encrypt_payload_stream( - const std::vector& plaintext, - const std::array& key, - const std::array& nonce); - -std::vector decrypt_payload_stream( - const std::vector& ciphertext, - const std::array& key, - const std::array& nonce); - std::vector sign_detached( const std::vector& data, const std::vector& secret_key);