aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPieter Wuille <pieter@wuille.net>2023-07-18 10:11:49 -0400
committerPieter Wuille <pieter@wuille.net>2023-08-17 15:26:34 -0400
commit3da636e08b781fa2f7c1c23bb937015185732a75 (patch)
tree11908cad2b43f2995362e497a96cb694910b7573
parent6ce5e8f4758e6fccf59b3980af417ec6c75e383e (diff)
downloadbitcoin-3da636e08b781fa2f7c1c23bb937015185732a75.tar.xz
crypto: refactor ChaCha20 classes to use Span<std::byte> interface
-rw-r--r--src/bench/chacha20.cpp12
-rw-r--r--src/crypto/chacha20.cpp124
-rw-r--r--src/crypto/chacha20.h65
-rw-r--r--src/crypto/chacha20poly1305.cpp28
-rw-r--r--src/crypto/muhash.cpp3
-rw-r--r--src/random.cpp15
-rw-r--r--src/random.h6
-rw-r--r--src/test/crypto_tests.cpp68
-rw-r--r--src/test/fuzz/crypto_chacha20.cpp41
-rw-r--r--src/test/fuzz/crypto_diff_fuzz_chacha20.cpp18
10 files changed, 201 insertions, 179 deletions
diff --git a/src/bench/chacha20.cpp b/src/bench/chacha20.cpp
index d8bebf9319..e0bb07d8be 100644
--- a/src/bench/chacha20.cpp
+++ b/src/bench/chacha20.cpp
@@ -14,13 +14,13 @@ static const uint64_t BUFFER_SIZE_LARGE = 1024*1024;
static void CHACHA20(benchmark::Bench& bench, size_t buffersize)
{
- std::vector<uint8_t> key(32,0);
- ChaCha20 ctx(key.data());
- ctx.Seek64({0, 0}, 0);
- std::vector<uint8_t> in(buffersize,0);
- std::vector<uint8_t> out(buffersize,0);
+ std::vector<std::byte> key(32, {});
+ ChaCha20 ctx(key);
+ ctx.Seek({0, 0}, 0);
+ std::vector<std::byte> in(buffersize, {});
+ std::vector<std::byte> out(buffersize, {});
bench.batch(in.size()).unit("byte").run([&] {
- ctx.Crypt(in.data(), out.data(), in.size());
+ ctx.Crypt(in, out);
});
}
diff --git a/src/crypto/chacha20.cpp b/src/crypto/chacha20.cpp
index 469b280494..438e451c9a 100644
--- a/src/crypto/chacha20.cpp
+++ b/src/crypto/chacha20.cpp
@@ -8,6 +8,7 @@
#include <crypto/common.h>
#include <crypto/chacha20.h>
#include <support/cleanse.h>
+#include <span.h>
#include <algorithm>
#include <string.h>
@@ -22,23 +23,24 @@ constexpr static inline uint32_t rotl32(uint32_t v, int c) { return (v << c) | (
#define REPEAT10(a) do { {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; } while(0)
-void ChaCha20Aligned::SetKey32(const unsigned char* k)
+void ChaCha20Aligned::SetKey(Span<const std::byte> key) noexcept
{
- input[0] = ReadLE32(k + 0);
- input[1] = ReadLE32(k + 4);
- input[2] = ReadLE32(k + 8);
- input[3] = ReadLE32(k + 12);
- input[4] = ReadLE32(k + 16);
- input[5] = ReadLE32(k + 20);
- input[6] = ReadLE32(k + 24);
- input[7] = ReadLE32(k + 28);
+ assert(key.size() == KEYLEN);
+ input[0] = ReadLE32(UCharCast(key.data() + 0));
+ input[1] = ReadLE32(UCharCast(key.data() + 4));
+ input[2] = ReadLE32(UCharCast(key.data() + 8));
+ input[3] = ReadLE32(UCharCast(key.data() + 12));
+ input[4] = ReadLE32(UCharCast(key.data() + 16));
+ input[5] = ReadLE32(UCharCast(key.data() + 20));
+ input[6] = ReadLE32(UCharCast(key.data() + 24));
+ input[7] = ReadLE32(UCharCast(key.data() + 28));
input[8] = 0;
input[9] = 0;
input[10] = 0;
input[11] = 0;
}
-ChaCha20Aligned::ChaCha20Aligned()
+ChaCha20Aligned::ChaCha20Aligned() noexcept
{
memset(input, 0, sizeof(input));
}
@@ -48,12 +50,12 @@ ChaCha20Aligned::~ChaCha20Aligned()
memory_cleanse(input, sizeof(input));
}
-ChaCha20Aligned::ChaCha20Aligned(const unsigned char* key32)
+ChaCha20Aligned::ChaCha20Aligned(Span<const std::byte> key) noexcept
{
- SetKey32(key32);
+ SetKey(key);
}
-void ChaCha20Aligned::Seek64(Nonce96 nonce, uint32_t block_counter)
+void ChaCha20Aligned::Seek(Nonce96 nonce, uint32_t block_counter) noexcept
{
input[8] = block_counter;
input[9] = nonce.first;
@@ -61,8 +63,12 @@ void ChaCha20Aligned::Seek64(Nonce96 nonce, uint32_t block_counter)
input[11] = nonce.second >> 32;
}
-inline void ChaCha20Aligned::Keystream64(unsigned char* c, size_t blocks)
+inline void ChaCha20Aligned::Keystream(Span<std::byte> output) noexcept
{
+ unsigned char* c = UCharCast(output.data());
+ size_t blocks = output.size() / BLOCKLEN;
+ assert(blocks * BLOCKLEN == output.size());
+
uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
uint32_t j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
@@ -154,12 +160,18 @@ inline void ChaCha20Aligned::Keystream64(unsigned char* c, size_t blocks)
return;
}
blocks -= 1;
- c += 64;
+ c += BLOCKLEN;
}
}
-inline void ChaCha20Aligned::Crypt64(const unsigned char* m, unsigned char* c, size_t blocks)
+inline void ChaCha20Aligned::Crypt(Span<const std::byte> in_bytes, Span<std::byte> out_bytes) noexcept
{
+ assert(in_bytes.size() == out_bytes.size());
+ const unsigned char* m = UCharCast(in_bytes.data());
+ unsigned char* c = UCharCast(out_bytes.data());
+ size_t blocks = out_bytes.size() / BLOCKLEN;
+ assert(blocks * BLOCKLEN == out_bytes.size());
+
uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
uint32_t j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
@@ -268,70 +280,68 @@ inline void ChaCha20Aligned::Crypt64(const unsigned char* m, unsigned char* c, s
return;
}
blocks -= 1;
- c += 64;
- m += 64;
+ c += BLOCKLEN;
+ m += BLOCKLEN;
}
}
-void ChaCha20::Keystream(unsigned char* c, size_t bytes)
+void ChaCha20::Keystream(Span<std::byte> out) noexcept
{
- if (!bytes) return;
+ if (out.empty()) return;
if (m_bufleft) {
- unsigned reuse = std::min<size_t>(m_bufleft, bytes);
- memcpy(c, m_buffer + 64 - m_bufleft, reuse);
+ unsigned reuse = std::min<size_t>(m_bufleft, out.size());
+ std::copy(m_buffer.end() - m_bufleft, m_buffer.end() - m_bufleft + reuse, out.begin());
m_bufleft -= reuse;
- bytes -= reuse;
- c += reuse;
+ out = out.subspan(reuse);
}
- if (bytes >= 64) {
- size_t blocks = bytes / 64;
- m_aligned.Keystream64(c, blocks);
- c += blocks * 64;
- bytes -= blocks * 64;
+ if (out.size() >= m_aligned.BLOCKLEN) {
+ size_t blocks = out.size() / m_aligned.BLOCKLEN;
+ m_aligned.Keystream(out.first(blocks * m_aligned.BLOCKLEN));
+ out = out.subspan(blocks * m_aligned.BLOCKLEN);
}
- if (bytes) {
- m_aligned.Keystream64(m_buffer, 1);
- memcpy(c, m_buffer, bytes);
- m_bufleft = 64 - bytes;
+ if (!out.empty()) {
+ m_aligned.Keystream(m_buffer);
+ std::copy(m_buffer.begin(), m_buffer.begin() + out.size(), out.begin());
+ m_bufleft = m_aligned.BLOCKLEN - out.size();
}
}
-void ChaCha20::Crypt(const unsigned char* m, unsigned char* c, size_t bytes)
+void ChaCha20::Crypt(Span<const std::byte> input, Span<std::byte> output) noexcept
{
- if (!bytes) return;
+ assert(input.size() == output.size());
+
+ if (!input.size()) return;
if (m_bufleft) {
- unsigned reuse = std::min<size_t>(m_bufleft, bytes);
+ unsigned reuse = std::min<size_t>(m_bufleft, input.size());
for (unsigned i = 0; i < reuse; i++) {
- c[i] = m[i] ^ m_buffer[64 - m_bufleft + i];
+ output[i] = input[i] ^ m_buffer[m_aligned.BLOCKLEN - m_bufleft + i];
}
m_bufleft -= reuse;
- bytes -= reuse;
- c += reuse;
- m += reuse;
+ output = output.subspan(reuse);
+ input = input.subspan(reuse);
}
- if (bytes >= 64) {
- size_t blocks = bytes / 64;
- m_aligned.Crypt64(m, c, blocks);
- c += blocks * 64;
- m += blocks * 64;
- bytes -= blocks * 64;
+ if (input.size() >= m_aligned.BLOCKLEN) {
+ size_t blocks = input.size() / m_aligned.BLOCKLEN;
+ m_aligned.Crypt(input.first(blocks * m_aligned.BLOCKLEN), output.first(blocks * m_aligned.BLOCKLEN));
+ output = output.subspan(blocks * m_aligned.BLOCKLEN);
+ input = input.subspan(blocks * m_aligned.BLOCKLEN);
}
- if (bytes) {
- m_aligned.Keystream64(m_buffer, 1);
- for (unsigned i = 0; i < bytes; i++) {
- c[i] = m[i] ^ m_buffer[i];
+ if (!input.empty()) {
+ m_aligned.Keystream(m_buffer);
+ for (unsigned i = 0; i < input.size(); i++) {
+ output[i] = input[i] ^ m_buffer[i];
}
- m_bufleft = 64 - bytes;
+ m_bufleft = m_aligned.BLOCKLEN - input.size();
}
}
ChaCha20::~ChaCha20()
{
- memory_cleanse(m_buffer, sizeof(m_buffer));
+ memory_cleanse(m_buffer.data(), m_buffer.size());
}
FSChaCha20::FSChaCha20(Span<const std::byte> key, uint32_t rekey_interval) noexcept :
- m_chacha20(UCharCast(key.data())), m_rekey_interval(rekey_interval)
+ m_chacha20(key), m_rekey_interval(rekey_interval)
{
assert(key.size() == KEYLEN);
}
@@ -341,20 +351,20 @@ void FSChaCha20::Crypt(Span<const std::byte> input, Span<std::byte> output) noex
assert(input.size() == output.size());
// Invoke internal stream cipher for actual encryption/decryption.
- m_chacha20.Crypt(UCharCast(input.data()), UCharCast(output.data()), input.size());
+ m_chacha20.Crypt(input, output);
// 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));
+ m_chacha20.Keystream(new_key);
// Update its key.
- m_chacha20.SetKey32(UCharCast(new_key));
+ m_chacha20.SetKey(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);
+ m_chacha20.Seek({0, ++m_rekey_counter}, 0);
// Reset the chunk counter.
m_chunk_counter = 0;
}
diff --git a/src/crypto/chacha20.h b/src/crypto/chacha20.h
index d1b2094e7e..a766f7d13c 100644
--- a/src/crypto/chacha20.h
+++ b/src/crypto/chacha20.h
@@ -28,16 +28,22 @@ private:
uint32_t input[12];
public:
- ChaCha20Aligned();
+ /** Expected key length in constructor and SetKey. */
+ static constexpr unsigned KEYLEN{32};
+
+ /** Block size (inputs/outputs to Keystream / Crypt should be multiples of this). */
+ static constexpr unsigned BLOCKLEN{64};
+
+ ChaCha20Aligned() noexcept;
/** Initialize a cipher with specified 32-byte key. */
- ChaCha20Aligned(const unsigned char* key32);
+ ChaCha20Aligned(Span<const std::byte> key) noexcept;
/** Destructor to clean up private memory. */
~ChaCha20Aligned();
- /** set 32-byte key. */
- void SetKey32(const unsigned char* key32);
+ /** Set 32-byte key, and seek to nonce 0 and block position 0. */
+ void SetKey(Span<const std::byte> key) noexcept;
/** Type for 96-bit nonces used by the Set function below.
*
@@ -51,18 +57,19 @@ public:
/** Set the 96-bit nonce and 32-bit block counter.
*
- * Block_counter selects a position to seek to (to byte 64*block_counter). After 256 GiB, the
- * block counter overflows, and nonce.first is incremented.
+ * Block_counter selects a position to seek to (to byte BLOCKLEN*block_counter). After 256 GiB,
+ * the block counter overflows, and nonce.first is incremented.
*/
- void Seek64(Nonce96 nonce, uint32_t block_counter);
+ void Seek(Nonce96 nonce, uint32_t block_counter) noexcept;
- /** outputs the keystream of size <64*blocks> into <c> */
- void Keystream64(unsigned char* c, size_t blocks);
+ /** outputs the keystream into out, whose length must be a multiple of BLOCKLEN. */
+ void Keystream(Span<std::byte> out) noexcept;
- /** enciphers the message <input> of length <64*blocks> and write the enciphered representation into <output>
- * Used for encryption and decryption (XOR)
+ /** en/deciphers the message <input> and write the result into <output>
+ *
+ * The size of input and output must be equal, and be a multiple of BLOCKLEN.
*/
- void Crypt64(const unsigned char* input, unsigned char* output, size_t blocks);
+ void Crypt(Span<const std::byte> input, Span<std::byte> output) noexcept;
};
/** Unrestricted ChaCha20 cipher. */
@@ -70,42 +77,46 @@ class ChaCha20
{
private:
ChaCha20Aligned m_aligned;
- unsigned char m_buffer[64] = {0};
+ std::array<std::byte, ChaCha20Aligned::BLOCKLEN> m_buffer;
unsigned m_bufleft{0};
public:
- ChaCha20() = default;
+ /** Expected key length in constructor and SetKey. */
+ static constexpr unsigned KEYLEN = ChaCha20Aligned::KEYLEN;
+
+ ChaCha20() noexcept = default;
/** Initialize a cipher with specified 32-byte key. */
- ChaCha20(const unsigned char* key32) : m_aligned(key32) {}
+ ChaCha20(Span<const std::byte> key) noexcept : m_aligned(key) {}
/** Destructor to clean up private memory. */
~ChaCha20();
- /** set 32-byte key. */
- void SetKey32(const unsigned char* key32)
+ /** Set 32-byte key, and seek to nonce 0 and block position 0. */
+ void SetKey(Span<const std::byte> key) noexcept
{
- m_aligned.SetKey32(key32);
+ m_aligned.SetKey(key);
m_bufleft = 0;
}
/** 96-bit nonce type. */
using Nonce96 = ChaCha20Aligned::Nonce96;
- /** Set the 96-bit nonce and 32-bit block counter. */
- void Seek64(Nonce96 nonce, uint32_t block_counter)
+ /** Set the 96-bit nonce and 32-bit block counter. See ChaCha20Aligned::Seek. */
+ void Seek(Nonce96 nonce, uint32_t block_counter) noexcept
{
- m_aligned.Seek64(nonce, block_counter);
+ m_aligned.Seek(nonce, block_counter);
m_bufleft = 0;
}
- /** outputs the keystream of size <bytes> into <c> */
- void Keystream(unsigned char* c, size_t bytes);
-
- /** enciphers the message <input> of length <bytes> and write the enciphered representation into <output>
- * Used for encryption and decryption (XOR)
+ /** en/deciphers the message <in_bytes> and write the result into <out_bytes>
+ *
+ * The size of in_bytes and out_bytes must be equal.
*/
- void Crypt(const unsigned char* input, unsigned char* output, size_t bytes);
+ void Crypt(Span<const std::byte> in_bytes, Span<std::byte> out_bytes) noexcept;
+
+ /** outputs the keystream to out. */
+ void Keystream(Span<std::byte> out) noexcept;
};
/** Forward-secure ChaCha20
diff --git a/src/crypto/chacha20poly1305.cpp b/src/crypto/chacha20poly1305.cpp
index 26161641bb..59671d304c 100644
--- a/src/crypto/chacha20poly1305.cpp
+++ b/src/crypto/chacha20poly1305.cpp
@@ -13,7 +13,7 @@
#include <assert.h>
#include <cstddef>
-AEADChaCha20Poly1305::AEADChaCha20Poly1305(Span<const std::byte> key) noexcept : m_chacha20(UCharCast(key.data()))
+AEADChaCha20Poly1305::AEADChaCha20Poly1305(Span<const std::byte> key) noexcept : m_chacha20(key)
{
assert(key.size() == KEYLEN);
}
@@ -21,7 +21,7 @@ AEADChaCha20Poly1305::AEADChaCha20Poly1305(Span<const std::byte> key) noexcept :
void AEADChaCha20Poly1305::SetKey(Span<const std::byte> key) noexcept
{
assert(key.size() == KEYLEN);
- m_chacha20.SetKey32(UCharCast(key.data()));
+ m_chacha20.SetKey(key);
}
namespace {
@@ -46,8 +46,8 @@ void ComputeTag(ChaCha20& chacha20, Span<const std::byte> aad, Span<const std::b
static const std::byte PADDING[16] = {{}};
// Get block of keystream (use a full 64 byte buffer to avoid the need for chacha20's own buffering).
- std::byte first_block[64];
- chacha20.Keystream(UCharCast(first_block), sizeof(first_block));
+ std::byte first_block[ChaCha20Aligned::BLOCKLEN];
+ chacha20.Keystream(first_block);
// Use the first 32 bytes of the first keystream block as poly1305 key.
Poly1305 poly1305{Span{first_block}.first(Poly1305::KEYLEN)};
@@ -76,12 +76,12 @@ void AEADChaCha20Poly1305::Encrypt(Span<const std::byte> plain1, Span<const std:
assert(cipher.size() == plain1.size() + plain2.size() + EXPANSION);
// Encrypt using ChaCha20 (starting at block 1).
- m_chacha20.Seek64(nonce, 1);
- m_chacha20.Crypt(UCharCast(plain1.data()), UCharCast(cipher.data()), plain1.size());
- m_chacha20.Crypt(UCharCast(plain2.data()), UCharCast(cipher.data() + plain1.size()), plain2.size());
+ m_chacha20.Seek(nonce, 1);
+ m_chacha20.Crypt(plain1, cipher.first(plain1.size()));
+ m_chacha20.Crypt(plain2, cipher.subspan(plain1.size()).first(plain2.size()));
// Seek to block 0, and compute tag using key drawn from there.
- m_chacha20.Seek64(nonce, 0);
+ m_chacha20.Seek(nonce, 0);
ComputeTag(m_chacha20, aad, cipher.first(cipher.size() - EXPANSION), cipher.last(EXPANSION));
}
@@ -90,22 +90,22 @@ bool AEADChaCha20Poly1305::Decrypt(Span<const std::byte> cipher, Span<const std:
assert(cipher.size() == plain1.size() + plain2.size() + EXPANSION);
// Verify tag (using key drawn from block 0).
- m_chacha20.Seek64(nonce, 0);
+ m_chacha20.Seek(nonce, 0);
std::byte expected_tag[EXPANSION];
ComputeTag(m_chacha20, aad, cipher.first(cipher.size() - EXPANSION), expected_tag);
if (timingsafe_bcmp(UCharCast(expected_tag), UCharCast(cipher.last(EXPANSION).data()), EXPANSION)) return false;
// Decrypt (starting at block 1).
- m_chacha20.Crypt(UCharCast(cipher.data()), UCharCast(plain1.data()), plain1.size());
- m_chacha20.Crypt(UCharCast(cipher.data() + plain1.size()), UCharCast(plain2.data()), plain2.size());
+ m_chacha20.Crypt(cipher.first(plain1.size()), plain1);
+ m_chacha20.Crypt(cipher.subspan(plain1.size()).first(plain2.size()), plain2);
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());
+ m_chacha20.Seek(nonce, 1);
+ m_chacha20.Keystream(keystream);
}
void FSChaCha20Poly1305::NextPacket() noexcept
@@ -113,7 +113,7 @@ 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];
+ std::byte one_block[ChaCha20Aligned::BLOCKLEN];
m_aead.Keystream({0xFFFFFFFF, m_rekey_counter}, one_block);
// Switch keys.
m_aead.SetKey(Span{one_block}.first(KEYLEN));
diff --git a/src/crypto/muhash.cpp b/src/crypto/muhash.cpp
index 471ee6af97..9c35b0689d 100644
--- a/src/crypto/muhash.cpp
+++ b/src/crypto/muhash.cpp
@@ -299,7 +299,8 @@ Num3072 MuHash3072::ToNum3072(Span<const unsigned char> in) {
unsigned char tmp[Num3072::BYTE_SIZE];
uint256 hashed_in{(HashWriter{} << in).GetSHA256()};
- ChaCha20Aligned(hashed_in.data()).Keystream64(tmp, Num3072::BYTE_SIZE / 64);
+ static_assert(sizeof(tmp) % ChaCha20Aligned::BLOCKLEN == 0);
+ ChaCha20Aligned{MakeByteSpan(hashed_in)}.Keystream(MakeWritableByteSpan(tmp));
Num3072 out{tmp};
return out;
diff --git a/src/random.cpp b/src/random.cpp
index 5ff6f573b8..ec7ade8ff6 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -16,6 +16,7 @@
#include <sync.h>
#include <util/time.h>
+#include <array>
#include <cmath>
#include <cstdlib>
#include <thread>
@@ -577,7 +578,7 @@ uint256 GetRandHash() noexcept
void FastRandomContext::RandomSeed()
{
uint256 seed = GetRandHash();
- rng.SetKey32(seed.begin());
+ rng.SetKey(MakeByteSpan(seed));
requires_seed = false;
}
@@ -585,7 +586,7 @@ uint256 FastRandomContext::rand256() noexcept
{
if (requires_seed) RandomSeed();
uint256 ret;
- rng.Keystream(ret.data(), ret.size());
+ rng.Keystream(MakeWritableByteSpan(ret));
return ret;
}
@@ -595,7 +596,7 @@ std::vector<B> FastRandomContext::randbytes(size_t len)
if (requires_seed) RandomSeed();
std::vector<B> ret(len);
if (len > 0) {
- rng.Keystream(UCharCast(ret.data()), len);
+ rng.Keystream(MakeWritableByteSpan(ret));
}
return ret;
}
@@ -605,12 +606,12 @@ template std::vector<std::byte> FastRandomContext::randbytes(size_t);
void FastRandomContext::fillrand(Span<std::byte> output)
{
if (requires_seed) RandomSeed();
- rng.Keystream(UCharCast(output.data()), output.size());
+ rng.Keystream(output);
}
FastRandomContext::FastRandomContext(const uint256& seed) noexcept : requires_seed(false), bitbuf_size(0)
{
- rng.SetKey32(seed.begin());
+ rng.SetKey(MakeByteSpan(seed));
}
bool Random_SanityCheck()
@@ -664,8 +665,8 @@ FastRandomContext::FastRandomContext(bool fDeterministic) noexcept : requires_se
if (!fDeterministic) {
return;
}
- uint256 seed;
- rng.SetKey32(seed.begin());
+ static constexpr std::array<std::byte, ChaCha20::KEYLEN> ZERO{};
+ rng.SetKey(ZERO);
}
FastRandomContext& FastRandomContext::operator=(FastRandomContext&& from) noexcept
diff --git a/src/random.h b/src/random.h
index 3b15477ae9..76bae5838d 100644
--- a/src/random.h
+++ b/src/random.h
@@ -175,9 +175,9 @@ public:
uint64_t rand64() noexcept
{
if (requires_seed) RandomSeed();
- unsigned char buf[8];
- rng.Keystream(buf, 8);
- return ReadLE64(buf);
+ std::array<std::byte, 8> buf;
+ rng.Keystream(buf);
+ return ReadLE64(UCharCast(buf.data()));
}
/** Generate a random (bits)-bit integer. */
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index 6fbe74a680..52ac7cff5b 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -133,27 +133,27 @@ static void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, b
static void TestChaCha20(const std::string &hex_message, const std::string &hexkey, ChaCha20::Nonce96 nonce, uint32_t seek, const std::string& hexout)
{
- std::vector<unsigned char> key = ParseHex(hexkey);
+ auto key = ParseHex<std::byte>(hexkey);
assert(key.size() == 32);
- std::vector<unsigned char> m = ParseHex(hex_message);
- ChaCha20 rng(key.data());
- rng.Seek64(nonce, seek);
- std::vector<unsigned char> outres;
+ auto m = ParseHex<std::byte>(hex_message);
+ ChaCha20 rng{key};
+ rng.Seek(nonce, seek);
+ std::vector<std::byte> outres;
outres.resize(hexout.size() / 2);
assert(hex_message.empty() || m.size() * 2 == hexout.size());
// perform the ChaCha20 round(s), if message is provided it will output the encrypted ciphertext otherwise the keystream
if (!hex_message.empty()) {
- rng.Crypt(m.data(), outres.data(), outres.size());
+ rng.Crypt(m, outres);
} else {
- rng.Keystream(outres.data(), outres.size());
+ rng.Keystream(outres);
}
BOOST_CHECK_EQUAL(hexout, HexStr(outres));
if (!hex_message.empty()) {
// Manually XOR with the keystream and compare the output
- rng.Seek64(nonce, seek);
- std::vector<unsigned char> only_keystream(outres.size());
- rng.Keystream(only_keystream.data(), only_keystream.size());
+ rng.Seek(nonce, seek);
+ std::vector<std::byte> only_keystream(outres.size());
+ rng.Keystream(only_keystream);
for (size_t i = 0; i != m.size(); i++) {
outres[i] = m[i] ^ only_keystream[i];
}
@@ -167,14 +167,14 @@ static void TestChaCha20(const std::string &hex_message, const std::string &hexk
lens[1] = InsecureRandRange(hexout.size() / 2U + 1U - lens[0]);
lens[2] = hexout.size() / 2U - lens[0] - lens[1];
- rng.Seek64(nonce, seek);
- outres.assign(hexout.size() / 2U, 0);
+ rng.Seek(nonce, seek);
+ outres.assign(hexout.size() / 2U, {});
size_t pos = 0;
for (int j = 0; j < 3; ++j) {
if (!hex_message.empty()) {
- rng.Crypt(m.data() + pos, outres.data() + pos, lens[j]);
+ rng.Crypt(Span{m}.subspan(pos, lens[j]), Span{outres}.subspan(pos, lens[j]));
} else {
- rng.Keystream(outres.data() + pos, lens[j]);
+ rng.Keystream(Span{outres}.subspan(pos, lens[j]));
}
pos += lens[j];
}
@@ -190,7 +190,7 @@ static void TestFSChaCha20(const std::string& hex_plaintext, const std::string&
auto plaintext = ParseHex<std::byte>(hex_plaintext);
auto fsc20 = FSChaCha20{key, rekey_interval};
- auto c20 = ChaCha20{UCharCast(key.data())};
+ auto c20 = ChaCha20{key};
std::vector<std::byte> fsc20_output;
fsc20_output.resize(plaintext.size());
@@ -200,23 +200,23 @@ static void TestFSChaCha20(const std::string& hex_plaintext, const std::string&
for (size_t i = 0; i < rekey_interval; i++) {
fsc20.Crypt(plaintext, fsc20_output);
- c20.Crypt(UCharCast(plaintext.data()), UCharCast(c20_output.data()), plaintext.size());
+ c20.Crypt(plaintext, c20_output);
BOOST_CHECK(c20_output == fsc20_output);
}
// At the rotation interval, the outputs will no longer match
fsc20.Crypt(plaintext, fsc20_output);
auto c20_copy = c20;
- c20.Crypt(UCharCast(plaintext.data()), UCharCast(c20_output.data()), plaintext.size());
+ c20.Crypt(plaintext, c20_output);
BOOST_CHECK(c20_output != fsc20_output);
std::byte new_key[FSChaCha20::KEYLEN];
- c20_copy.Keystream(UCharCast(new_key), sizeof(new_key));
- c20.SetKey32(UCharCast(new_key));
- c20.Seek64({0, 1}, 0);
+ c20_copy.Keystream(new_key);
+ c20.SetKey(new_key);
+ c20.Seek({0, 1}, 0);
// Outputs should match again after simulating key rotation
- c20.Crypt(UCharCast(plaintext.data()), UCharCast(c20_output.data()), plaintext.size());
+ c20.Crypt(plaintext, c20_output);
BOOST_CHECK(c20_output == fsc20_output);
BOOST_CHECK_EQUAL(HexStr(fsc20_output), ciphertext_after_rotation);
@@ -823,20 +823,20 @@ BOOST_AUTO_TEST_CASE(chacha20_testvector)
BOOST_AUTO_TEST_CASE(chacha20_midblock)
{
- auto key = ParseHex("0000000000000000000000000000000000000000000000000000000000000000");
- ChaCha20 c20{key.data()};
+ auto key = ParseHex<std::byte>("0000000000000000000000000000000000000000000000000000000000000000");
+ ChaCha20 c20{key};
// get one block of keystream
- unsigned char block[64];
- c20.Keystream(block, sizeof(block));
- unsigned char b1[5], b2[7], b3[52];
- c20 = ChaCha20{key.data()};
- c20.Keystream(b1, 5);
- c20.Keystream(b2, 7);
- c20.Keystream(b3, 52);
-
- BOOST_CHECK_EQUAL(0, memcmp(b1, block, 5));
- BOOST_CHECK_EQUAL(0, memcmp(b2, block + 5, 7));
- BOOST_CHECK_EQUAL(0, memcmp(b3, block + 12, 52));
+ std::byte block[64];
+ c20.Keystream(block);
+ std::byte b1[5], b2[7], b3[52];
+ c20 = ChaCha20{key};
+ c20.Keystream(b1);
+ c20.Keystream(b2);
+ c20.Keystream(b3);
+
+ BOOST_CHECK(Span{block}.first(5) == Span{b1});
+ BOOST_CHECK(Span{block}.subspan(5, 7) == Span{b2});
+ BOOST_CHECK(Span{block}.last(52) == Span{b3});
}
BOOST_AUTO_TEST_CASE(poly1305_testvector)
diff --git a/src/test/fuzz/crypto_chacha20.cpp b/src/test/fuzz/crypto_chacha20.cpp
index 76370b4e57..160eebbe8a 100644
--- a/src/test/fuzz/crypto_chacha20.cpp
+++ b/src/test/fuzz/crypto_chacha20.cpp
@@ -20,17 +20,17 @@ FUZZ_TARGET(crypto_chacha20)
ChaCha20 chacha20;
if (fuzzed_data_provider.ConsumeBool()) {
const std::vector<unsigned char> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, 32);
- chacha20 = ChaCha20{key.data()};
+ chacha20 = ChaCha20{MakeByteSpan(key)};
}
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
CallOneOf(
fuzzed_data_provider,
[&] {
std::vector<unsigned char> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, 32);
- chacha20.SetKey32(key.data());
+ chacha20.SetKey(MakeByteSpan(key));
},
[&] {
- chacha20.Seek64(
+ chacha20.Seek(
{
fuzzed_data_provider.ConsumeIntegral<uint32_t>(),
fuzzed_data_provider.ConsumeIntegral<uint64_t>()
@@ -38,12 +38,12 @@ FUZZ_TARGET(crypto_chacha20)
},
[&] {
std::vector<uint8_t> output(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
- chacha20.Keystream(output.data(), output.size());
+ chacha20.Keystream(MakeWritableByteSpan(output));
},
[&] {
std::vector<uint8_t> output(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
const std::vector<uint8_t> input = ConsumeFixedLengthByteVector(fuzzed_data_provider, output.size());
- chacha20.Crypt(input.data(), output.data(), input.size());
+ chacha20.Crypt(MakeByteSpan(input), MakeWritableByteSpan(output));
});
}
}
@@ -62,9 +62,8 @@ template<bool UseCrypt>
void ChaCha20SplitFuzz(FuzzedDataProvider& provider)
{
// Determine key, iv, start position, length.
- unsigned char key[32] = {0};
- auto key_bytes = provider.ConsumeBytes<unsigned char>(32);
- std::copy(key_bytes.begin(), key_bytes.end(), key);
+ auto key_bytes = provider.ConsumeBytes<std::byte>(ChaCha20::KEYLEN);
+ key_bytes.resize(ChaCha20::KEYLEN);
uint64_t iv = provider.ConsumeIntegral<uint64_t>();
uint32_t iv_prefix = provider.ConsumeIntegral<uint32_t>();
uint64_t total_bytes = provider.ConsumeIntegralInRange<uint64_t>(0, 1000000);
@@ -72,13 +71,13 @@ void ChaCha20SplitFuzz(FuzzedDataProvider& provider)
uint32_t seek = provider.ConsumeIntegralInRange<uint32_t>(0, ~(uint32_t)(total_bytes >> 6));
// Initialize two ChaCha20 ciphers, with the same key/iv/position.
- ChaCha20 crypt1(key);
- ChaCha20 crypt2(key);
- crypt1.Seek64({iv_prefix, iv}, seek);
- crypt2.Seek64({iv_prefix, iv}, seek);
+ ChaCha20 crypt1(key_bytes);
+ ChaCha20 crypt2(key_bytes);
+ crypt1.Seek({iv_prefix, iv}, seek);
+ crypt2.Seek({iv_prefix, iv}, seek);
// Construct vectors with data.
- std::vector<unsigned char> data1, data2;
+ std::vector<std::byte> data1, data2;
data1.resize(total_bytes);
data2.resize(total_bytes);
@@ -90,14 +89,14 @@ void ChaCha20SplitFuzz(FuzzedDataProvider& provider)
uint64_t bytes = 0;
while (bytes < (total_bytes & ~uint64_t{7})) {
uint64_t val = rng();
- WriteLE64(data1.data() + bytes, val);
- WriteLE64(data2.data() + bytes, val);
+ WriteLE64(UCharCast(data1.data() + bytes), val);
+ WriteLE64(UCharCast(data2.data() + bytes), val);
bytes += 8;
}
if (bytes < total_bytes) {
- unsigned char valbytes[8];
+ std::byte valbytes[8];
uint64_t val = rng();
- WriteLE64(valbytes, val);
+ WriteLE64(UCharCast(valbytes), val);
std::copy(valbytes, valbytes + (total_bytes - bytes), data1.data() + bytes);
std::copy(valbytes, valbytes + (total_bytes - bytes), data2.data() + bytes);
}
@@ -108,9 +107,9 @@ void ChaCha20SplitFuzz(FuzzedDataProvider& provider)
// Encrypt data1, the whole array at once.
if constexpr (UseCrypt) {
- crypt1.Crypt(data1.data(), data1.data(), total_bytes);
+ crypt1.Crypt(data1, data1);
} else {
- crypt1.Keystream(data1.data(), total_bytes);
+ crypt1.Keystream(data1);
}
// Encrypt data2, in at most 256 chunks.
@@ -127,9 +126,9 @@ void ChaCha20SplitFuzz(FuzzedDataProvider& provider)
// This tests that Keystream() has the same behavior as Crypt() applied
// to 0x00 input bytes.
if (UseCrypt || provider.ConsumeBool()) {
- crypt2.Crypt(data2.data() + bytes2, data2.data() + bytes2, now);
+ crypt2.Crypt(Span{data2}.subspan(bytes2, now), Span{data2}.subspan(bytes2, now));
} else {
- crypt2.Keystream(data2.data() + bytes2, now);
+ crypt2.Keystream(Span{data2}.subspan(bytes2, now));
}
bytes2 += now;
if (is_last) break;
diff --git a/src/test/fuzz/crypto_diff_fuzz_chacha20.cpp b/src/test/fuzz/crypto_diff_fuzz_chacha20.cpp
index 285ea2dfe0..f368629fbb 100644
--- a/src/test/fuzz/crypto_diff_fuzz_chacha20.cpp
+++ b/src/test/fuzz/crypto_diff_fuzz_chacha20.cpp
@@ -275,14 +275,14 @@ FUZZ_TARGET(crypto_diff_fuzz_chacha20)
if (fuzzed_data_provider.ConsumeBool()) {
const std::vector<unsigned char> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, 32);
- chacha20 = ChaCha20{key.data()};
+ chacha20 = ChaCha20{MakeByteSpan(key)};
ECRYPT_keysetup(&ctx, key.data(), key.size() * 8, 0);
} else {
// The default ChaCha20 constructor is equivalent to using the all-0 key.
ECRYPT_keysetup(&ctx, ZEROKEY, 256, 0);
}
- // ECRYPT_keysetup() doesn't set the counter and nonce to 0 while SetKey32() does
+ // ECRYPT_keysetup() doesn't set the counter and nonce to 0 while SetKey() does
static const uint8_t iv[8] = {0, 0, 0, 0, 0, 0, 0, 0};
ChaCha20::Nonce96 nonce{0, 0};
uint32_t counter{0};
@@ -293,11 +293,11 @@ FUZZ_TARGET(crypto_diff_fuzz_chacha20)
fuzzed_data_provider,
[&] {
const std::vector<unsigned char> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, 32);
- chacha20.SetKey32(key.data());
+ chacha20.SetKey(MakeByteSpan(key));
nonce = {0, 0};
counter = 0;
ECRYPT_keysetup(&ctx, key.data(), key.size() * 8, 0);
- // ECRYPT_keysetup() doesn't set the counter and nonce to 0 while SetKey32() does
+ // ECRYPT_keysetup() doesn't set the counter and nonce to 0 while SetKey() does
uint8_t iv[8] = {0, 0, 0, 0, 0, 0, 0, 0};
ECRYPT_ivsetup(&ctx, iv);
},
@@ -306,7 +306,7 @@ FUZZ_TARGET(crypto_diff_fuzz_chacha20)
uint64_t iv = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
nonce = {iv_prefix, iv};
counter = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
- chacha20.Seek64(nonce, counter);
+ chacha20.Seek(nonce, counter);
ctx.input[12] = counter;
ctx.input[13] = iv_prefix;
ctx.input[14] = iv;
@@ -315,7 +315,7 @@ FUZZ_TARGET(crypto_diff_fuzz_chacha20)
[&] {
uint32_t integralInRange = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096);
std::vector<uint8_t> output(integralInRange);
- chacha20.Keystream(output.data(), output.size());
+ chacha20.Keystream(MakeWritableByteSpan(output));
std::vector<uint8_t> djb_output(integralInRange);
ECRYPT_keystream_bytes(&ctx, djb_output.data(), djb_output.size());
assert(output == djb_output);
@@ -324,7 +324,7 @@ FUZZ_TARGET(crypto_diff_fuzz_chacha20)
counter += (integralInRange + 63) >> 6;
if (counter < old_counter) ++nonce.first;
if (integralInRange & 63) {
- chacha20.Seek64(nonce, counter);
+ chacha20.Seek(nonce, counter);
}
assert(counter == ctx.input[12]);
},
@@ -332,7 +332,7 @@ FUZZ_TARGET(crypto_diff_fuzz_chacha20)
uint32_t integralInRange = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096);
std::vector<uint8_t> output(integralInRange);
const std::vector<uint8_t> input = ConsumeFixedLengthByteVector(fuzzed_data_provider, output.size());
- chacha20.Crypt(input.data(), output.data(), input.size());
+ chacha20.Crypt(MakeByteSpan(input), MakeWritableByteSpan(output));
std::vector<uint8_t> djb_output(integralInRange);
ECRYPT_encrypt_bytes(&ctx, input.data(), djb_output.data(), input.size());
assert(output == djb_output);
@@ -341,7 +341,7 @@ FUZZ_TARGET(crypto_diff_fuzz_chacha20)
counter += (integralInRange + 63) >> 6;
if (counter < old_counter) ++nonce.first;
if (integralInRange & 63) {
- chacha20.Seek64(nonce, counter);
+ chacha20.Seek(nonce, counter);
}
assert(counter == ctx.input[12]);
});