From dd6273e81b8c88f029521b382edfe907c988be5b Mon Sep 17 00:00:00 2001 From: dhruv <856960+dhruv@users.noreply.github.com> Date: Sun, 12 Jun 2022 14:09:24 -0700 Subject: [PATCH] RFC8439Encrypt can take multiple plaintexts --- src/bench/rfc8439.cpp | 5 +++-- src/crypto/bip324_suite.cpp | 15 ++------------- src/crypto/rfc8439.cpp | 30 +++++++++++++++++++++++------- src/crypto/rfc8439.h | 2 +- src/test/crypto_tests.cpp | 5 ++--- src/test/fuzz/crypto_rfc8439.cpp | 4 +++- 6 files changed, 34 insertions(+), 27 deletions(-) diff --git a/src/bench/rfc8439.cpp b/src/bench/rfc8439.cpp index 56c7863e25..1c4fc68fa6 100644 --- a/src/bench/rfc8439.cpp +++ b/src/bench/rfc8439.cpp @@ -23,10 +23,11 @@ std::array nonce = {std::byte{0x00}, std::byte{0x01}, std::byte{0 static void RFC8439_AEAD(benchmark::Bench& bench, size_t plaintext_size, bool include_decryption) { - std::vector plaintext_in(plaintext_size, std::byte{0x00}); + const std::vector plaintext_in(plaintext_size, std::byte{0x00}); + std::vector> ins{plaintext_in}; bench.batch(plaintext_size).unit("byte").run([&] { - auto encrypted = RFC8439Encrypt(aad, zero_key, nonce, plaintext_in); + auto encrypted = RFC8439Encrypt(aad, zero_key, nonce, ins); if (include_decryption) { auto decrypted = RFC8439Decrypt(aad, zero_key, nonce, encrypted); diff --git a/src/crypto/bip324_suite.cpp b/src/crypto/bip324_suite.cpp index 75784249d2..b5a53ba146 100644 --- a/src/crypto/bip324_suite.cpp +++ b/src/crypto/bip324_suite.cpp @@ -52,19 +52,8 @@ bool BIP324CipherSuite::Crypt(Span input, Span outpu uint32_t ciphertext_len = BIP324_HEADER_LEN + input.size(); WriteLE32(reinterpret_cast(&ciphertext_len), ciphertext_len); - std::vector input_vec; - input_vec.resize(BIP324_HEADER_LEN + input.size()); - - // TODO: this can be optimized by changing the RFC8439Encrypt interface to accept a list of inputs. - // But, at the moment, there's a potential bug in out ChaCha20 implementation for plaintexts that - // are not a multiple of 64 bytes -- the rest of the "block" is discarded. An update is in progress - // which will help here. - memcpy(input_vec.data(), &flags, BIP324_HEADER_LEN); - if (!input.empty()) { - memcpy(input_vec.data() + BIP324_HEADER_LEN, input.data(), input.size()); - } - - auto encrypted = RFC8439Encrypt({}, payload_key, nonce, input_vec); + std::vector> ins{Span{reinterpret_cast(&flags), BIP324_HEADER_LEN}, input}; + auto encrypted = RFC8439Encrypt({}, payload_key, nonce, ins); auto write_pos = output.data(); fsc20.Crypt({reinterpret_cast(&ciphertext_len), BIP324_LENGTH_FIELD_LEN}, diff --git a/src/crypto/rfc8439.cpp b/src/crypto/rfc8439.cpp index beddc3f47d..f7a3a0aa02 100644 --- a/src/crypto/rfc8439.cpp +++ b/src/crypto/rfc8439.cpp @@ -57,14 +57,23 @@ std::array GetPoly1305Key(ChaCha20& c20) return polykey; } -void RFC8439Crypt(ChaCha20& c20, Span in_bytes, Span out_bytes) +void RFC8439Crypt(ChaCha20& c20, const std::vector>& in_bytes, Span out_bytes) { - assert(in_bytes.size() == out_bytes.size()); + size_t total_bytes = 0; + for (auto in: in_bytes) { + total_bytes += in.size(); + } + assert(total_bytes == out_bytes.size()); c20.SeekRFC8439(1); - c20.Crypt(reinterpret_cast(in_bytes.data()), reinterpret_cast(out_bytes.data()), in_bytes.size()); + + 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(); + } } -RFC8439Encrypted RFC8439Encrypt(Span aad, Span key, const std::array& nonce, Span plaintext) +RFC8439Encrypted RFC8439Encrypt(Span aad, Span key, const std::array& nonce, const std::vector>& plaintexts) { assert(key.size() == RFC8439_KEYLEN); RFC8439Encrypted ret; @@ -74,8 +83,13 @@ RFC8439Encrypted RFC8439Encrypt(Span aad, Span std::array polykey{GetPoly1305Key(c20)}; - ret.ciphertext.resize(plaintext.size()); - RFC8439Crypt(c20, plaintext, ret.ciphertext); + size_t total_bytes = 0; + for (auto plaintext: plaintexts) { + total_bytes += plaintext.size(); + } + + ret.ciphertext.resize(total_bytes); + RFC8439Crypt(c20, plaintexts, ret.ciphertext); ret.tag = ComputeRFC8439Tag(polykey, aad, ret.ciphertext); return ret; } @@ -101,6 +115,8 @@ RFC8439Decrypted RFC8439Decrypt(Span aad, Span ret.success = true; ret.plaintext.resize(encrypted.ciphertext.size()); - RFC8439Crypt(c20, encrypted.ciphertext, ret.plaintext); + + std::vector> ins{encrypted.ciphertext}; + RFC8439Crypt(c20, ins, ret.plaintext); return ret; } diff --git a/src/crypto/rfc8439.h b/src/crypto/rfc8439.h index dc60df3b6b..480f3fe404 100644 --- a/src/crypto/rfc8439.h +++ b/src/crypto/rfc8439.h @@ -25,7 +25,7 @@ struct RFC8439Decrypted { std::vector plaintext; }; -RFC8439Encrypted RFC8439Encrypt(Span aad, Span key, const std::array& nonce, Span plaintext); +RFC8439Encrypted RFC8439Encrypt(Span aad, Span key, const std::array& nonce, const std::vector>& plaintexts); RFC8439Decrypted RFC8439Decrypt(Span aad, Span key, const std::array& nonce, const RFC8439Encrypted& encrypted); #endif // BITCOIN_CRYPTO_RFC8439_H diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index 31ab5b38d8..64d8b08b95 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -808,8 +808,6 @@ static void TestBIP324CipherSuite(const std::string& hex_input, const std::strin BOOST_AUTO_TEST_CASE(bip324_cipher_suite_testvectors) { - /* test bip324 cipher suite */ - // encrypting an empty message should result in 20 bytes: // 3 bytes of encrypted length, 1 byte header and 16 bytes MAC TestBIP324CipherSuite(/* plaintext */ "", @@ -1081,7 +1079,8 @@ static void TestRFC8439AEAD(const std::string& hex_aad, const std::string& hex_k std::array nonce_arr; memcpy(nonce_arr.data(), nonce.data(), 12); auto plaintext = ParseHex(hex_plaintext); - auto encrypted = RFC8439Encrypt(MakeByteSpan(aad), MakeByteSpan(key), nonce_arr, MakeByteSpan(plaintext)); + std::vector> ins{MakeByteSpan(plaintext)}; + auto encrypted = RFC8439Encrypt(MakeByteSpan(aad), MakeByteSpan(key), nonce_arr, ins); BOOST_CHECK_EQUAL(HexStr(MakeByteSpan(encrypted.ciphertext)), hex_expected_ciphertext); BOOST_CHECK_EQUAL(HexStr(MakeByteSpan(encrypted.tag)), hex_expected_auth_tag); diff --git a/src/test/fuzz/crypto_rfc8439.cpp b/src/test/fuzz/crypto_rfc8439.cpp index de8bdd7192..de3c8904e8 100644 --- a/src/test/fuzz/crypto_rfc8439.cpp +++ b/src/test/fuzz/crypto_rfc8439.cpp @@ -28,7 +28,9 @@ FUZZ_TARGET(crypto_rfc8439) auto plaintext = fdp.ConsumeBytes(plaintext_len); plaintext.resize(plaintext_len); - auto encrypted = RFC8439Encrypt(aad, key, nonce, plaintext); + std::vector> ins{plaintext}; + + auto encrypted = RFC8439Encrypt(aad, key, nonce, ins); assert(encrypted.ciphertext.size() == plaintext.size()); auto bit_flip_attack = !plaintext.empty() && fdp.ConsumeBool();