From 6ea08aa1538fb791bcb4b4f7e2fbbfe7941442e1 Mon Sep 17 00:00:00 2001 From: dhruv <856960+dhruv@users.noreply.github.com> Date: Thu, 11 Aug 2022 10:36:45 -0700 Subject: [PATCH] RFC8439Decrypt can output to multiple plaintexts --- src/bench/rfc8439.cpp | 3 +- src/crypto/bip324_suite.cpp | 10 ++---- src/crypto/rfc8439.cpp | 58 ++++++++++++++++++++++++-------- src/crypto/rfc8439.h | 2 +- src/test/crypto_tests.cpp | 3 +- src/test/fuzz/crypto_rfc8439.cpp | 3 +- 6 files changed, 53 insertions(+), 26 deletions(-) diff --git a/src/bench/rfc8439.cpp b/src/bench/rfc8439.cpp index e9f02edf5f..e57feb6071 100644 --- a/src/bench/rfc8439.cpp +++ b/src/bench/rfc8439.cpp @@ -33,7 +33,8 @@ static void RFC8439_AEAD(benchmark::Bench& bench, size_t plaintext_size, bool in if (include_decryption) { std::vector decrypted_plaintext(plaintext_size); - auto authenticated = RFC8439Decrypt(aad, zero_key, nonce, output, decrypted_plaintext); + std::vector> plaintexts_out{MakeWritableByteSpan(decrypted_plaintext)}; + auto authenticated = RFC8439Decrypt(aad, zero_key, nonce, output, plaintexts_out); assert(authenticated); assert(memcmp(decrypted_plaintext.data(), plaintext_in.data(), plaintext_in.size()) == 0); } diff --git a/src/crypto/bip324_suite.cpp b/src/crypto/bip324_suite.cpp index ca035b11e3..dd7df6aa05 100644 --- a/src/crypto/bip324_suite.cpp +++ b/src/crypto/bip324_suite.cpp @@ -83,17 +83,11 @@ bool BIP324CipherSuite::Crypt(const Span input, Span // we must use BIP324CipherSuite::DecryptLength before calling BIP324CipherSuite::Crypt // input is encrypted (header + payload) and the mac tag // decrypted header will be put in flags and output will be payload. - // TODO: This can be optimized by having RFC8439Decrypt() accept multiple places to put the plaintext - std::vector decrypted_plaintext(input.size() - RFC8439_TAGLEN); - auto authenticated = RFC8439Decrypt({}, payload_key, nonce, input, decrypted_plaintext); + std::vector> plaintexts{Span{reinterpret_cast(&flags), BIP324_HEADER_LEN}, output}; + auto authenticated = RFC8439Decrypt({}, payload_key, nonce, input, plaintexts); if (!authenticated) { return false; } - - memcpy(&flags, decrypted_plaintext.data(), BIP324_HEADER_LEN); - if (!output.empty()) { - memcpy(output.data(), decrypted_plaintext.data() + BIP324_HEADER_LEN, input.size() - BIP324_HEADER_LEN - RFC8439_TAGLEN); - } } msg_ctr++; diff --git a/src/crypto/rfc8439.cpp b/src/crypto/rfc8439.cpp index 41f01ad1b3..7fc4861625 100644 --- a/src/crypto/rfc8439.cpp +++ b/src/crypto/rfc8439.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #ifndef RFC8439_TIMINGSAFE_BCMP @@ -57,19 +58,40 @@ std::array GetPoly1305Key(ChaCha20& c20) return polykey; } -void RFC8439Crypt(ChaCha20& c20, const std::vector>& in_bytes, Span out_bytes) +void RFC8439Crypt(ChaCha20& c20, const std::vector>& in_bytes, std::vector>& out_bytes) { - size_t total_bytes = 0; - for (auto in: in_bytes) { - total_bytes += in.size(); + size_t total_in_bytes = 0; + for (auto in : in_bytes) { + total_in_bytes += in.size(); } - assert(total_bytes <= out_bytes.size()); - c20.SeekRFC8439(1); + size_t total_out_bytes = 0; + for (auto out : out_bytes) { + total_out_bytes += out.size(); + } + assert(total_in_bytes <= total_out_bytes); - auto write_pos = out_bytes.data(); - for (auto in: in_bytes) { - c20.Crypt(reinterpret_cast(in.data()), reinterpret_cast(write_pos), in.size()); - write_pos += in.size(); + c20.SeekRFC8439(1); + size_t in_idx{0}, in_chunk_pos{0}, out_idx{0}, out_chunk_pos{0}; + while (in_idx < in_bytes.size()) { + auto in_chunk = in_bytes[in_idx]; + auto out_chunk = out_bytes[out_idx]; + auto read_pos = in_chunk.begin() + in_chunk_pos; + auto write_pos = out_chunk.begin() + out_chunk_pos; + auto num_bytes = std::min(in_chunk.size() - in_chunk_pos, out_chunk.size() - out_chunk_pos); + + c20.Crypt(reinterpret_cast(read_pos), reinterpret_cast(write_pos), num_bytes); + in_chunk_pos += num_bytes; + out_chunk_pos += num_bytes; + + if (in_chunk_pos >= in_chunk.size()) { + in_idx++; + in_chunk_pos = 0; + } + + if (out_chunk_pos >= out_chunk.size()) { + out_idx++; + out_chunk_pos = 0; + } } } @@ -88,16 +110,24 @@ void RFC8439Encrypt(const Span aad, const Span } assert(output.size() >= total_bytes + POLY1305_TAGLEN); - RFC8439Crypt(c20, plaintexts, output); + + std::vector> outs{output}; + RFC8439Crypt(c20, plaintexts, outs); ComputeRFC8439Tag(polykey, aad, {output.data(), total_bytes}, {output.data() + total_bytes, POLY1305_TAGLEN}); } -bool RFC8439Decrypt(const Span aad, const Span key, const std::array& nonce, const Span input, Span plaintext) +bool RFC8439Decrypt(const Span aad, const Span key, const std::array& nonce, const Span input, std::vector>& plaintexts) { assert(key.size() == RFC8439_KEYLEN); - assert(plaintext.size() >= input.size() - POLY1305_TAGLEN); + + size_t total_bytes = 0; + for (auto plaintext: plaintexts) { + total_bytes += plaintext.size(); + } + + assert(total_bytes >= input.size() - POLY1305_TAGLEN); ChaCha20 c20{reinterpret_cast(key.data()), key.size()}; c20.SetRFC8439Nonce(nonce); @@ -113,6 +143,6 @@ bool RFC8439Decrypt(const Span aad, const Span } std::vector> ins{{input.data(), input.size() - POLY1305_TAGLEN}}; - RFC8439Crypt(c20, ins, plaintext); + RFC8439Crypt(c20, ins, plaintexts); return true; } diff --git a/src/crypto/rfc8439.h b/src/crypto/rfc8439.h index d39eecf5ed..f261c06116 100644 --- a/src/crypto/rfc8439.h +++ b/src/crypto/rfc8439.h @@ -18,5 +18,5 @@ constexpr static size_t RFC8439_TAGLEN = POLY1305_TAGLEN; void RFC8439Encrypt(const Span aad, const Span key, const std::array& nonce, const std::vector>& plaintexts, Span output); // returns false if authentication fails -bool RFC8439Decrypt(const Span aad, const Span key, const std::array& nonce, const Span input, Span plaintext); +bool RFC8439Decrypt(const Span aad, const Span key, const std::array& nonce, const Span input, std::vector>& plaintexts); #endif // BITCOIN_CRYPTO_RFC8439_H diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index 5fa1468b3d..c6b1197ee1 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -1087,7 +1087,8 @@ static void TestRFC8439AEAD(const std::string& hex_aad, const std::string& hex_k BOOST_CHECK_EQUAL(HexStr({output.data() + output.size() - POLY1305_TAGLEN, POLY1305_TAGLEN}), hex_expected_auth_tag); std::vector decrypted_plaintext(plaintext.size(), std::byte{0x00}); - auto authenticated = RFC8439Decrypt(MakeByteSpan(aad), MakeByteSpan(key), nonce_arr, output, decrypted_plaintext); + std::vector> plaintexts{MakeWritableByteSpan(decrypted_plaintext)}; + auto authenticated = RFC8439Decrypt(MakeByteSpan(aad), MakeByteSpan(key), nonce_arr, output, plaintexts); BOOST_CHECK(authenticated); BOOST_CHECK_EQUAL(0, memcmp(decrypted_plaintext.data(), plaintext.data(), plaintext.size())); } diff --git a/src/test/fuzz/crypto_rfc8439.cpp b/src/test/fuzz/crypto_rfc8439.cpp index 8e5b478f04..bcf8d31f8a 100644 --- a/src/test/fuzz/crypto_rfc8439.cpp +++ b/src/test/fuzz/crypto_rfc8439.cpp @@ -41,7 +41,8 @@ FUZZ_TARGET(crypto_rfc8439) } std::vector decrypted_plaintext(plaintext.size(), std::byte{0x00}); - auto authenticated = RFC8439Decrypt(MakeByteSpan(aad), key, nonce, output, decrypted_plaintext); + std::vector> plaintexts_out{MakeWritableByteSpan(decrypted_plaintext)}; + auto authenticated = RFC8439Decrypt(MakeByteSpan(aad), key, nonce, output, plaintexts_out); if (bit_flip_attack) { assert(!authenticated); } else {