Skip to content

Commit 5c3dce7

Browse files
committed
RFC8439Encrypt can take multiple plaintexts
1 parent aceb08e commit 5c3dce7

File tree

6 files changed

+34
-27
lines changed

6 files changed

+34
-27
lines changed

src/bench/rfc8439.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@ std::array<std::byte, 12> nonce = {std::byte{0x00}, std::byte{0x01}, std::byte{0
2323

2424
static void RFC8439_AEAD(benchmark::Bench& bench, size_t plaintext_size, bool include_decryption)
2525
{
26-
std::vector<std::byte> plaintext_in(plaintext_size, std::byte{0x00});
26+
const std::vector<std::byte> plaintext_in(plaintext_size, std::byte{0x00});
27+
std::vector<Span<const std::byte>> ins{plaintext_in};
2728

2829
bench.batch(plaintext_size).unit("byte").run([&] {
29-
auto encrypted = RFC8439Encrypt(aad, zero_key, nonce, plaintext_in);
30+
auto encrypted = RFC8439Encrypt(aad, zero_key, nonce, ins);
3031

3132
if (include_decryption) {
3233
auto decrypted = RFC8439Decrypt(aad, zero_key, nonce, encrypted);

src/crypto/bip324_suite.cpp

+2-13
Original file line numberDiff line numberDiff line change
@@ -51,19 +51,8 @@ bool BIP324CipherSuite::Crypt(Span<const std::byte> input, Span<std::byte> outpu
5151
uint32_t ciphertext_len = BIP324_HEADER_LEN + input.size();
5252
WriteLE32(reinterpret_cast<unsigned char*>(&ciphertext_len), ciphertext_len);
5353

54-
std::vector<std::byte> input_vec;
55-
input_vec.resize(BIP324_HEADER_LEN + input.size());
56-
57-
// TODO: this can be optimized by changing the RFC8439Encrypt interface to accept a list of inputs.
58-
// But, at the moment, there's a potential bug in out ChaCha20 implementation for plaintexts that
59-
// are not a multiple of 64 bytes -- the rest of the "block" is discarded. An update is in progress
60-
// which will help here.
61-
memcpy(input_vec.data(), &flags, BIP324_HEADER_LEN);
62-
if (!input.empty()) {
63-
memcpy(input_vec.data() + BIP324_HEADER_LEN, input.data(), input.size());
64-
}
65-
66-
auto encrypted = RFC8439Encrypt({}, payload_key, nonce, input_vec);
54+
std::vector<Span<const std::byte>> ins{Span{reinterpret_cast<std::byte*>(&flags), BIP324_HEADER_LEN}, input};
55+
auto encrypted = RFC8439Encrypt({}, payload_key, nonce, ins);
6756

6857
auto write_pos = output.data();
6958
fsc20.Crypt({reinterpret_cast<std::byte*>(&ciphertext_len), BIP324_LENGTH_FIELD_LEN},

src/crypto/rfc8439.cpp

+23-7
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,23 @@ std::array<std::byte, POLY1305_KEYLEN> GetPoly1305Key(ChaCha20& c20)
5757
return polykey;
5858
}
5959

60-
void RFC8439Crypt(ChaCha20& c20, Span<const std::byte> in_bytes, Span<std::byte> out_bytes)
60+
void RFC8439Crypt(ChaCha20& c20, const std::vector<Span<const std::byte>>& in_bytes, Span<std::byte> out_bytes)
6161
{
62-
assert(in_bytes.size() == out_bytes.size());
62+
size_t total_bytes = 0;
63+
for (auto in: in_bytes) {
64+
total_bytes += in.size();
65+
}
66+
assert(total_bytes == out_bytes.size());
6367
c20.SeekRFC8439(1);
64-
c20.Crypt(reinterpret_cast<const unsigned char*>(in_bytes.data()), reinterpret_cast<unsigned char*>(out_bytes.data()), in_bytes.size());
68+
69+
auto write_pos = out_bytes.data();
70+
for (auto in: in_bytes) {
71+
c20.Crypt(reinterpret_cast<const unsigned char*>(in.data()), reinterpret_cast<unsigned char*>(write_pos), in.size());
72+
write_pos += in.size();
73+
}
6574
}
6675

67-
RFC8439Encrypted RFC8439Encrypt(Span<const std::byte> aad, Span<const std::byte> key, const std::array<std::byte, 12>& nonce, Span<const std::byte> plaintext)
76+
RFC8439Encrypted RFC8439Encrypt(Span<const std::byte> aad, Span<const std::byte> key, const std::array<std::byte, 12>& nonce, const std::vector<Span<const std::byte>>& plaintexts)
6877
{
6978
assert(key.size() == RFC8439_KEYLEN);
7079
RFC8439Encrypted ret;
@@ -74,8 +83,13 @@ RFC8439Encrypted RFC8439Encrypt(Span<const std::byte> aad, Span<const std::byte>
7483

7584
std::array<std::byte, POLY1305_KEYLEN> polykey{GetPoly1305Key(c20)};
7685

77-
ret.ciphertext.resize(plaintext.size());
78-
RFC8439Crypt(c20, plaintext, ret.ciphertext);
86+
size_t total_bytes = 0;
87+
for (auto plaintext: plaintexts) {
88+
total_bytes += plaintext.size();
89+
}
90+
91+
ret.ciphertext.resize(total_bytes);
92+
RFC8439Crypt(c20, plaintexts, ret.ciphertext);
7993
ret.tag = ComputeRFC8439Tag(polykey, aad, ret.ciphertext);
8094
return ret;
8195
}
@@ -101,6 +115,8 @@ RFC8439Decrypted RFC8439Decrypt(Span<const std::byte> aad, Span<const std::byte>
101115

102116
ret.success = true;
103117
ret.plaintext.resize(encrypted.ciphertext.size());
104-
RFC8439Crypt(c20, encrypted.ciphertext, ret.plaintext);
118+
119+
std::vector<Span<const std::byte>> ins{encrypted.ciphertext};
120+
RFC8439Crypt(c20, ins, ret.plaintext);
105121
return ret;
106122
}

src/crypto/rfc8439.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ struct RFC8439Decrypted {
2525
std::vector<std::byte> plaintext;
2626
};
2727

28-
RFC8439Encrypted RFC8439Encrypt(Span<const std::byte> aad, Span<const std::byte> key, const std::array<std::byte, 12>& nonce, Span<const std::byte> plaintext);
28+
RFC8439Encrypted RFC8439Encrypt(Span<const std::byte> aad, Span<const std::byte> key, const std::array<std::byte, 12>& nonce, const std::vector<Span<const std::byte>>& plaintexts);
2929

3030
RFC8439Decrypted RFC8439Decrypt(Span<const std::byte> aad, Span<const std::byte> key, const std::array<std::byte, 12>& nonce, const RFC8439Encrypted& encrypted);
3131
#endif // BITCOIN_CRYPTO_RFC8439_H

src/test/crypto_tests.cpp

+2-3
Original file line numberDiff line numberDiff line change
@@ -807,8 +807,6 @@ static void TestBIP324CipherSuite(const std::string& hex_input, const std::strin
807807

808808
BOOST_AUTO_TEST_CASE(bip324_cipher_suite_testvectors)
809809
{
810-
/* test bip324 cipher suite */
811-
812810
// encrypting an empty message should result in 20 bytes:
813811
// 3 bytes of encrypted length, 1 byte header and 16 bytes MAC
814812
TestBIP324CipherSuite(/* plaintext */ "",
@@ -1080,7 +1078,8 @@ static void TestRFC8439AEAD(const std::string& hex_aad, const std::string& hex_k
10801078
std::array<std::byte, 12> nonce_arr;
10811079
memcpy(nonce_arr.data(), nonce.data(), 12);
10821080
auto plaintext = ParseHex(hex_plaintext);
1083-
auto encrypted = RFC8439Encrypt(MakeByteSpan(aad), MakeByteSpan(key), nonce_arr, MakeByteSpan(plaintext));
1081+
std::vector<Span<const std::byte>> ins{MakeByteSpan(plaintext)};
1082+
auto encrypted = RFC8439Encrypt(MakeByteSpan(aad), MakeByteSpan(key), nonce_arr, ins);
10841083

10851084
BOOST_CHECK_EQUAL(HexStr(MakeByteSpan(encrypted.ciphertext)), hex_expected_ciphertext);
10861085
BOOST_CHECK_EQUAL(HexStr(MakeByteSpan(encrypted.tag)), hex_expected_auth_tag);

src/test/fuzz/crypto_rfc8439.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ FUZZ_TARGET(crypto_rfc8439)
2828
auto plaintext = fdp.ConsumeBytes<std::byte>(plaintext_len);
2929
plaintext.resize(plaintext_len);
3030

31-
auto encrypted = RFC8439Encrypt(aad, key, nonce, plaintext);
31+
std::vector<Span<const std::byte>> ins{plaintext};
32+
33+
auto encrypted = RFC8439Encrypt(aad, key, nonce, ins);
3234
assert(encrypted.ciphertext.size() == plaintext.size());
3335

3436
auto bit_flip_attack = !plaintext.empty() && fdp.ConsumeBool();

0 commit comments

Comments
 (0)