Skip to content

Commit

Permalink
RFC8439Decrypt can output to multiple plaintexts
Browse files Browse the repository at this point in the history
  • Loading branch information
dhruv committed Aug 11, 2022
1 parent 9b55b37 commit 6ea08aa
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 26 deletions.
3 changes: 2 additions & 1 deletion src/bench/rfc8439.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ static void RFC8439_AEAD(benchmark::Bench& bench, size_t plaintext_size, bool in

if (include_decryption) {
std::vector<std::byte> decrypted_plaintext(plaintext_size);
auto authenticated = RFC8439Decrypt(aad, zero_key, nonce, output, decrypted_plaintext);
std::vector<Span<std::byte>> 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);
}
Expand Down
10 changes: 2 additions & 8 deletions src/crypto/bip324_suite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,11 @@ bool BIP324CipherSuite::Crypt(const Span<const std::byte> input, Span<std::byte>
// 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<std::byte> decrypted_plaintext(input.size() - RFC8439_TAGLEN);
auto authenticated = RFC8439Decrypt({}, payload_key, nonce, input, decrypted_plaintext);
std::vector<Span<std::byte>> plaintexts{Span{reinterpret_cast<std::byte*>(&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++;
Expand Down
58 changes: 44 additions & 14 deletions src/crypto/rfc8439.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <crypto/chacha20.h>
#include <crypto/common.h>

#include <algorithm>
#include <cstring>

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

void RFC8439Crypt(ChaCha20& c20, const std::vector<Span<const std::byte>>& in_bytes, Span<std::byte> out_bytes)
void RFC8439Crypt(ChaCha20& c20, const std::vector<Span<const std::byte>>& in_bytes, std::vector<Span<std::byte>>& 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<const unsigned char*>(in.data()), reinterpret_cast<unsigned char*>(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<const unsigned char*>(read_pos), reinterpret_cast<unsigned char*>(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;
}
}
}

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

assert(output.size() >= total_bytes + POLY1305_TAGLEN);
RFC8439Crypt(c20, plaintexts, output);

std::vector<Span<std::byte>> outs{output};
RFC8439Crypt(c20, plaintexts, outs);
ComputeRFC8439Tag(polykey, aad,
{output.data(), total_bytes},
{output.data() + total_bytes, POLY1305_TAGLEN});
}

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)
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)
{
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<const unsigned char*>(key.data()), key.size()};
c20.SetRFC8439Nonce(nonce);
Expand All @@ -113,6 +143,6 @@ bool RFC8439Decrypt(const Span<const std::byte> aad, const Span<const std::byte>
}

std::vector<Span<const std::byte>> ins{{input.data(), input.size() - POLY1305_TAGLEN}};
RFC8439Crypt(c20, ins, plaintext);
RFC8439Crypt(c20, ins, plaintexts);
return true;
}
2 changes: 1 addition & 1 deletion src/crypto/rfc8439.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ constexpr static size_t RFC8439_TAGLEN = POLY1305_TAGLEN;
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);

// returns false if authentication fails
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);
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);
#endif // BITCOIN_CRYPTO_RFC8439_H
3 changes: 2 additions & 1 deletion src/test/crypto_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::byte> decrypted_plaintext(plaintext.size(), std::byte{0x00});
auto authenticated = RFC8439Decrypt(MakeByteSpan(aad), MakeByteSpan(key), nonce_arr, output, decrypted_plaintext);
std::vector<Span<std::byte>> 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()));
}
Expand Down
3 changes: 2 additions & 1 deletion src/test/fuzz/crypto_rfc8439.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ FUZZ_TARGET(crypto_rfc8439)
}

std::vector<std::byte> decrypted_plaintext(plaintext.size(), std::byte{0x00});
auto authenticated = RFC8439Decrypt(MakeByteSpan(aad), key, nonce, output, decrypted_plaintext);
std::vector<Span<std::byte>> plaintexts_out{MakeWritableByteSpan(decrypted_plaintext)};
auto authenticated = RFC8439Decrypt(MakeByteSpan(aad), key, nonce, output, plaintexts_out);
if (bit_flip_attack) {
assert(!authenticated);
} else {
Expand Down

0 comments on commit 6ea08aa

Please sign in to comment.