crypto: refactor ChaCha20 classes to use Span<std::byte> interface

This commit is contained in:
Pieter Wuille
2023-07-18 10:11:49 -04:00
parent 6ce5e8f475
commit 3da636e08b
10 changed files with 200 additions and 178 deletions

View File

@@ -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;
}