Skip to content

Commit 248a3aa

Browse files
committed
RFC8439Decrypt can output to multiple plaintexts
1 parent 980985f commit 248a3aa

File tree

6 files changed

+53
-26
lines changed

6 files changed

+53
-26
lines changed

src/bench/rfc8439.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ static void RFC8439_AEAD(benchmark::Bench& bench, size_t plaintext_size, bool in
3333

3434
if (include_decryption) {
3535
std::vector<std::byte> decrypted_plaintext(plaintext_size);
36-
auto authenticated = RFC8439Decrypt(aad, zero_key, nonce, output, decrypted_plaintext);
36+
std::vector<Span<std::byte>> plaintexts_out{MakeWritableByteSpan(decrypted_plaintext)};
37+
auto authenticated = RFC8439Decrypt(aad, zero_key, nonce, output, plaintexts_out);
3738
assert(authenticated);
3839
assert(memcmp(decrypted_plaintext.data(), plaintext_in.data(), plaintext_in.size()) == 0);
3940
}

src/crypto/bip324_suite.cpp

+2-8
Original file line numberDiff line numberDiff line change
@@ -83,17 +83,11 @@ bool BIP324CipherSuite::Crypt(const Span<const std::byte> input, Span<std::byte>
8383
// we must use BIP324CipherSuite::DecryptLength before calling BIP324CipherSuite::Crypt
8484
// input is encrypted (header + payload) and the mac tag
8585
// decrypted header will be put in flags and output will be payload.
86-
// TODO: This can be optimized by having RFC8439Decrypt() accept multiple places to put the plaintext
87-
std::vector<std::byte> decrypted_plaintext(input.size() - RFC8439_TAGLEN);
88-
auto authenticated = RFC8439Decrypt({}, payload_key, nonce, input, decrypted_plaintext);
86+
std::vector<Span<std::byte>> plaintexts{Span{reinterpret_cast<std::byte*>(&flags), BIP324_HEADER_LEN}, output};
87+
auto authenticated = RFC8439Decrypt({}, payload_key, nonce, input, plaintexts);
8988
if (!authenticated) {
9089
return false;
9190
}
92-
93-
memcpy(&flags, decrypted_plaintext.data(), BIP324_HEADER_LEN);
94-
if (!output.empty()) {
95-
memcpy(output.data(), decrypted_plaintext.data() + BIP324_HEADER_LEN, input.size() - BIP324_HEADER_LEN - RFC8439_TAGLEN);
96-
}
9791
}
9892

9993
msg_ctr++;

src/crypto/rfc8439.cpp

+44-14
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <crypto/chacha20.h>
88
#include <crypto/common.h>
99

10+
#include <algorithm>
1011
#include <cstring>
1112

1213
#ifndef RFC8439_TIMINGSAFE_BCMP
@@ -57,19 +58,40 @@ std::array<std::byte, POLY1305_KEYLEN> GetPoly1305Key(ChaCha20& c20)
5758
return polykey;
5859
}
5960

60-
void RFC8439Crypt(ChaCha20& c20, const std::vector<Span<const std::byte>>& in_bytes, Span<std::byte> out_bytes)
61+
void RFC8439Crypt(ChaCha20& c20, const std::vector<Span<const std::byte>>& in_bytes, std::vector<Span<std::byte>>& out_bytes)
6162
{
62-
size_t total_bytes = 0;
63-
for (auto in: in_bytes) {
64-
total_bytes += in.size();
63+
size_t total_in_bytes = 0;
64+
for (auto in : in_bytes) {
65+
total_in_bytes += in.size();
6566
}
66-
assert(total_bytes <= out_bytes.size());
67-
c20.SeekRFC8439(1);
67+
size_t total_out_bytes = 0;
68+
for (auto out : out_bytes) {
69+
total_out_bytes += out.size();
70+
}
71+
assert(total_in_bytes <= total_out_bytes);
6872

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+
c20.SeekRFC8439(1);
74+
size_t in_idx{0}, in_chunk_pos{0}, out_idx{0}, out_chunk_pos{0};
75+
while (in_idx < in_bytes.size()) {
76+
auto in_chunk = in_bytes[in_idx];
77+
auto out_chunk = out_bytes[out_idx];
78+
auto read_pos = in_chunk.begin() + in_chunk_pos;
79+
auto write_pos = out_chunk.begin() + out_chunk_pos;
80+
auto num_bytes = std::min(in_chunk.size() - in_chunk_pos, out_chunk.size() - out_chunk_pos);
81+
82+
c20.Crypt(reinterpret_cast<const unsigned char*>(read_pos), reinterpret_cast<unsigned char*>(write_pos), num_bytes);
83+
in_chunk_pos += num_bytes;
84+
out_chunk_pos += num_bytes;
85+
86+
if (in_chunk_pos >= in_chunk.size()) {
87+
in_idx++;
88+
in_chunk_pos = 0;
89+
}
90+
91+
if (out_chunk_pos >= out_chunk.size()) {
92+
out_idx++;
93+
out_chunk_pos = 0;
94+
}
7395
}
7496
}
7597

@@ -88,16 +110,24 @@ void RFC8439Encrypt(const Span<const std::byte> aad, const Span<const std::byte>
88110
}
89111

90112
assert(output.size() >= total_bytes + POLY1305_TAGLEN);
91-
RFC8439Crypt(c20, plaintexts, output);
113+
114+
std::vector<Span<std::byte>> outs{output};
115+
RFC8439Crypt(c20, plaintexts, outs);
92116
ComputeRFC8439Tag(polykey, aad,
93117
{output.data(), total_bytes},
94118
{output.data() + total_bytes, POLY1305_TAGLEN});
95119
}
96120

97-
bool RFC8439Decrypt(const Span<const std::byte> aad, const Span<const std::byte> key, const std::array<std::byte, 12>& nonce, const Span<const std::byte> input, Span<std::byte> plaintext)
121+
bool RFC8439Decrypt(const Span<const std::byte> aad, const Span<const std::byte> key, const std::array<std::byte, 12>& nonce, const Span<const std::byte> input, std::vector<Span<std::byte>>& plaintexts)
98122
{
99123
assert(key.size() == RFC8439_KEYLEN);
100-
assert(plaintext.size() >= input.size() - POLY1305_TAGLEN);
124+
125+
size_t total_bytes = 0;
126+
for (auto plaintext: plaintexts) {
127+
total_bytes += plaintext.size();
128+
}
129+
130+
assert(total_bytes >= input.size() - POLY1305_TAGLEN);
101131

102132
ChaCha20 c20{reinterpret_cast<const unsigned char*>(key.data()), key.size()};
103133
c20.SetRFC8439Nonce(nonce);
@@ -113,6 +143,6 @@ bool RFC8439Decrypt(const Span<const std::byte> aad, const Span<const std::byte>
113143
}
114144

115145
std::vector<Span<const std::byte>> ins{{input.data(), input.size() - POLY1305_TAGLEN}};
116-
RFC8439Crypt(c20, ins, plaintext);
146+
RFC8439Crypt(c20, ins, plaintexts);
117147
return true;
118148
}

src/crypto/rfc8439.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ constexpr static size_t RFC8439_TAGLEN = POLY1305_TAGLEN;
1818
void RFC8439Encrypt(const Span<const std::byte> aad, const Span<const std::byte> key, const std::array<std::byte, 12>& nonce, const std::vector<Span<const std::byte>>& plaintexts, Span<std::byte> output);
1919

2020
// returns false if authentication fails
21-
bool RFC8439Decrypt(const Span<const std::byte> aad, const Span<const std::byte> key, const std::array<std::byte, 12>& nonce, const Span<const std::byte> input, Span<std::byte> plaintext);
21+
bool RFC8439Decrypt(const Span<const std::byte> aad, const Span<const std::byte> key, const std::array<std::byte, 12>& nonce, const Span<const std::byte> input, std::vector<Span<std::byte>>& plaintexts);
2222
#endif // BITCOIN_CRYPTO_RFC8439_H

src/test/crypto_tests.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -1087,7 +1087,8 @@ static void TestRFC8439AEAD(const std::string& hex_aad, const std::string& hex_k
10871087
BOOST_CHECK_EQUAL(HexStr({output.data() + output.size() - POLY1305_TAGLEN, POLY1305_TAGLEN}), hex_expected_auth_tag);
10881088

10891089
std::vector<std::byte> decrypted_plaintext(plaintext.size(), std::byte{0x00});
1090-
auto authenticated = RFC8439Decrypt(MakeByteSpan(aad), MakeByteSpan(key), nonce_arr, output, decrypted_plaintext);
1090+
std::vector<Span<std::byte>> plaintexts{MakeWritableByteSpan(decrypted_plaintext)};
1091+
auto authenticated = RFC8439Decrypt(MakeByteSpan(aad), MakeByteSpan(key), nonce_arr, output, plaintexts);
10911092
BOOST_CHECK(authenticated);
10921093
BOOST_CHECK_EQUAL(0, memcmp(decrypted_plaintext.data(), plaintext.data(), plaintext.size()));
10931094
}

src/test/fuzz/crypto_rfc8439.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ FUZZ_TARGET(crypto_rfc8439)
4141
}
4242

4343
std::vector<std::byte> decrypted_plaintext(plaintext.size(), std::byte{0x00});
44-
auto authenticated = RFC8439Decrypt(MakeByteSpan(aad), key, nonce, output, decrypted_plaintext);
44+
std::vector<Span<std::byte>> plaintexts_out{MakeWritableByteSpan(decrypted_plaintext)};
45+
auto authenticated = RFC8439Decrypt(MakeByteSpan(aad), key, nonce, output, plaintexts_out);
4546
if (bit_flip_attack) {
4647
assert(!authenticated);
4748
} else {

0 commit comments

Comments
 (0)