mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-12-11 05:03:16 +01:00
crypto: refactor ChaCha20 classes to use Span<std::byte> interface
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user