mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-06-01 08:44:02 +02:00
crypto: add FSChaCha20, a rekeying wrapper around ChaCha20
This adds the FSChaCha20 stream cipher as specified in BIP324, a wrapper around the ChaCha20 stream cipher (specified in RFC8439 section 2.4) which automatically rekeys every N messages, and manages the nonces used for encryption. Co-authored-by: dhruv <856960+dhruv@users.noreply.github.com>
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <crypto/common.h>
|
||||
#include <crypto/chacha20.h>
|
||||
#include <support/cleanse.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string.h>
|
||||
@@ -42,6 +43,11 @@ ChaCha20Aligned::ChaCha20Aligned()
|
||||
memset(input, 0, sizeof(input));
|
||||
}
|
||||
|
||||
ChaCha20Aligned::~ChaCha20Aligned()
|
||||
{
|
||||
memory_cleanse(input, sizeof(input));
|
||||
}
|
||||
|
||||
ChaCha20Aligned::ChaCha20Aligned(const unsigned char* key32)
|
||||
{
|
||||
SetKey32(key32);
|
||||
@@ -318,3 +324,38 @@ void ChaCha20::Crypt(const unsigned char* m, unsigned char* c, size_t bytes)
|
||||
m_bufleft = 64 - bytes;
|
||||
}
|
||||
}
|
||||
|
||||
ChaCha20::~ChaCha20()
|
||||
{
|
||||
memory_cleanse(m_buffer, sizeof(m_buffer));
|
||||
}
|
||||
|
||||
FSChaCha20::FSChaCha20(Span<const std::byte> key, uint32_t rekey_interval) noexcept :
|
||||
m_chacha20(UCharCast(key.data())), m_rekey_interval(rekey_interval)
|
||||
{
|
||||
assert(key.size() == KEYLEN);
|
||||
}
|
||||
|
||||
void FSChaCha20::Crypt(Span<const std::byte> input, Span<std::byte> output) noexcept
|
||||
{
|
||||
assert(input.size() == output.size());
|
||||
|
||||
// Invoke internal stream cipher for actual encryption/decryption.
|
||||
m_chacha20.Crypt(UCharCast(input.data()), UCharCast(output.data()), input.size());
|
||||
|
||||
// Rekey after m_rekey_interval encryptions/decryptions.
|
||||
if (++m_chunk_counter == m_rekey_interval) {
|
||||
// Get new key from the stream cipher.
|
||||
std::byte new_key[KEYLEN];
|
||||
m_chacha20.Keystream(UCharCast(new_key), sizeof(new_key));
|
||||
// Update its key.
|
||||
m_chacha20.SetKey32(UCharCast(new_key));
|
||||
// Wipe the key (a copy remains inside m_chacha20, where it'll be wiped on the next rekey
|
||||
// or on destruction).
|
||||
memory_cleanse(new_key, sizeof(new_key));
|
||||
// Set the nonce for the new section of output.
|
||||
m_chacha20.Seek64({0, ++m_rekey_counter}, 0);
|
||||
// Reset the chunk counter.
|
||||
m_chunk_counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
#ifndef BITCOIN_CRYPTO_CHACHA20_H
|
||||
#define BITCOIN_CRYPTO_CHACHA20_H
|
||||
|
||||
#include <span.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <stdint.h>
|
||||
#include <utility>
|
||||
@@ -29,6 +33,9 @@ public:
|
||||
/** Initialize a cipher with specified 32-byte key. */
|
||||
ChaCha20Aligned(const unsigned char* key32);
|
||||
|
||||
/** Destructor to clean up private memory. */
|
||||
~ChaCha20Aligned();
|
||||
|
||||
/** set 32-byte key. */
|
||||
void SetKey32(const unsigned char* key32);
|
||||
|
||||
@@ -72,6 +79,9 @@ public:
|
||||
/** Initialize a cipher with specified 32-byte key. */
|
||||
ChaCha20(const unsigned char* key32) : m_aligned(key32) {}
|
||||
|
||||
/** Destructor to clean up private memory. */
|
||||
~ChaCha20();
|
||||
|
||||
/** set 32-byte key. */
|
||||
void SetKey32(const unsigned char* key32)
|
||||
{
|
||||
@@ -98,4 +108,43 @@ public:
|
||||
void Crypt(const unsigned char* input, unsigned char* output, size_t bytes);
|
||||
};
|
||||
|
||||
/** Forward-secure ChaCha20
|
||||
*
|
||||
* This implements a stream cipher that automatically transitions to a new stream with a new key
|
||||
* and new nonce after a predefined number of encryptions or decryptions.
|
||||
*
|
||||
* See BIP324 for details.
|
||||
*/
|
||||
class FSChaCha20
|
||||
{
|
||||
private:
|
||||
/** Internal stream cipher. */
|
||||
ChaCha20 m_chacha20;
|
||||
|
||||
/** The number of encryptions/decryptions before a rekey happens. */
|
||||
const uint32_t m_rekey_interval;
|
||||
|
||||
/** The number of encryptions/decryptions since the last rekey. */
|
||||
uint32_t m_chunk_counter{0};
|
||||
|
||||
/** The number of rekey operations that have happened. */
|
||||
uint64_t m_rekey_counter{0};
|
||||
|
||||
public:
|
||||
/** Length of keys expected by the constructor. */
|
||||
static constexpr unsigned KEYLEN = 32;
|
||||
|
||||
// No copy or move to protect the secret.
|
||||
FSChaCha20(const FSChaCha20&) = delete;
|
||||
FSChaCha20(FSChaCha20&&) = delete;
|
||||
FSChaCha20& operator=(const FSChaCha20&) = delete;
|
||||
FSChaCha20& operator=(FSChaCha20&&) = delete;
|
||||
|
||||
/** Construct an FSChaCha20 cipher that rekeys every rekey_interval Crypt() calls. */
|
||||
FSChaCha20(Span<const std::byte> key, uint32_t rekey_interval) noexcept;
|
||||
|
||||
/** Encrypt or decrypt a chunk. */
|
||||
void Crypt(Span<const std::byte> input, Span<std::byte> output) noexcept;
|
||||
};
|
||||
|
||||
#endif // BITCOIN_CRYPTO_CHACHA20_H
|
||||
|
||||
Reference in New Issue
Block a user