crypto: add FSChaCha20Poly1305, rekeying wrapper around ChaCha20Poly1305

This adds the FSChaCha20Poly1305 AEAD as specified in BIP324, a wrapper
around the ChaCha20Poly1305 AEAD (as specified in RFC8439 section 2.8) which
automatically rekeys every N messages, and automatically increments the nonce
every message.
This commit is contained in:
Pieter Wuille
2023-06-29 14:55:16 -04:00
parent 0fee267792
commit aa8cee9334
3 changed files with 159 additions and 0 deletions

View File

@@ -8,6 +8,7 @@
#include <crypto/chacha20.h>
#include <crypto/poly1305.h>
#include <span.h>
#include <support/cleanse.h>
#include <assert.h>
#include <cstdint>
@@ -99,3 +100,41 @@ bool AEADChaCha20Poly1305::Decrypt(Span<const std::byte> cipher, Span<const std:
m_chacha20.Crypt(UCharCast(cipher.data()), UCharCast(plain.data()), plain.size());
return true;
}
void AEADChaCha20Poly1305::Keystream(Nonce96 nonce, Span<std::byte> keystream) noexcept
{
// Skip the first output block, as it's used for generating the poly1305 key.
m_chacha20.Seek64(nonce, 1);
m_chacha20.Keystream(UCharCast(keystream.data()), keystream.size());
}
void FSChaCha20Poly1305::NextPacket() noexcept
{
if (++m_packet_counter == m_rekey_interval) {
// Generate a full block of keystream, to avoid needing the ChaCha20 buffer, even though
// we only need KEYLEN (32) bytes.
std::byte one_block[64];
m_aead.Keystream({0xFFFFFFFF, m_rekey_counter}, one_block);
// Switch keys.
m_aead.SetKey(Span{one_block}.first(KEYLEN));
// Wipe the generated keystream (a copy remains inside m_aead, which will be cleaned up
// once it cycles again, or is destroyed).
memory_cleanse(one_block, sizeof(one_block));
// Update counters.
m_packet_counter = 0;
++m_rekey_counter;
}
}
void FSChaCha20Poly1305::Encrypt(Span<const std::byte> plain, Span<const std::byte> aad, Span<std::byte> cipher) noexcept
{
m_aead.Encrypt(plain, aad, {m_packet_counter, m_rekey_counter}, cipher);
NextPacket();
}
bool FSChaCha20Poly1305::Decrypt(Span<const std::byte> cipher, Span<const std::byte> aad, Span<std::byte> plain) noexcept
{
bool ret = m_aead.Decrypt(cipher, aad, {m_packet_counter, m_rekey_counter}, plain);
NextPacket();
return ret;
}