Skip to content

Commit 3da636e

Browse files
committed
crypto: refactor ChaCha20 classes to use Span<std::byte> interface
1 parent 6ce5e8f commit 3da636e

File tree

10 files changed

+201
-179
lines changed

10 files changed

+201
-179
lines changed

src/bench/chacha20.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ static const uint64_t BUFFER_SIZE_LARGE = 1024*1024;
1414

1515
static void CHACHA20(benchmark::Bench& bench, size_t buffersize)
1616
{
17-
std::vector<uint8_t> key(32,0);
18-
ChaCha20 ctx(key.data());
19-
ctx.Seek64({0, 0}, 0);
20-
std::vector<uint8_t> in(buffersize,0);
21-
std::vector<uint8_t> out(buffersize,0);
17+
std::vector<std::byte> key(32, {});
18+
ChaCha20 ctx(key);
19+
ctx.Seek({0, 0}, 0);
20+
std::vector<std::byte> in(buffersize, {});
21+
std::vector<std::byte> out(buffersize, {});
2222
bench.batch(in.size()).unit("byte").run([&] {
23-
ctx.Crypt(in.data(), out.data(), in.size());
23+
ctx.Crypt(in, out);
2424
});
2525
}
2626

src/crypto/chacha20.cpp

Lines changed: 67 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <crypto/common.h>
99
#include <crypto/chacha20.h>
1010
#include <support/cleanse.h>
11+
#include <span.h>
1112

1213
#include <algorithm>
1314
#include <string.h>
@@ -22,23 +23,24 @@ constexpr static inline uint32_t rotl32(uint32_t v, int c) { return (v << c) | (
2223

2324
#define REPEAT10(a) do { {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; } while(0)
2425

25-
void ChaCha20Aligned::SetKey32(const unsigned char* k)
26+
void ChaCha20Aligned::SetKey(Span<const std::byte> key) noexcept
2627
{
27-
input[0] = ReadLE32(k + 0);
28-
input[1] = ReadLE32(k + 4);
29-
input[2] = ReadLE32(k + 8);
30-
input[3] = ReadLE32(k + 12);
31-
input[4] = ReadLE32(k + 16);
32-
input[5] = ReadLE32(k + 20);
33-
input[6] = ReadLE32(k + 24);
34-
input[7] = ReadLE32(k + 28);
28+
assert(key.size() == KEYLEN);
29+
input[0] = ReadLE32(UCharCast(key.data() + 0));
30+
input[1] = ReadLE32(UCharCast(key.data() + 4));
31+
input[2] = ReadLE32(UCharCast(key.data() + 8));
32+
input[3] = ReadLE32(UCharCast(key.data() + 12));
33+
input[4] = ReadLE32(UCharCast(key.data() + 16));
34+
input[5] = ReadLE32(UCharCast(key.data() + 20));
35+
input[6] = ReadLE32(UCharCast(key.data() + 24));
36+
input[7] = ReadLE32(UCharCast(key.data() + 28));
3537
input[8] = 0;
3638
input[9] = 0;
3739
input[10] = 0;
3840
input[11] = 0;
3941
}
4042

41-
ChaCha20Aligned::ChaCha20Aligned()
43+
ChaCha20Aligned::ChaCha20Aligned() noexcept
4244
{
4345
memset(input, 0, sizeof(input));
4446
}
@@ -48,21 +50,25 @@ ChaCha20Aligned::~ChaCha20Aligned()
4850
memory_cleanse(input, sizeof(input));
4951
}
5052

51-
ChaCha20Aligned::ChaCha20Aligned(const unsigned char* key32)
53+
ChaCha20Aligned::ChaCha20Aligned(Span<const std::byte> key) noexcept
5254
{
53-
SetKey32(key32);
55+
SetKey(key);
5456
}
5557

56-
void ChaCha20Aligned::Seek64(Nonce96 nonce, uint32_t block_counter)
58+
void ChaCha20Aligned::Seek(Nonce96 nonce, uint32_t block_counter) noexcept
5759
{
5860
input[8] = block_counter;
5961
input[9] = nonce.first;
6062
input[10] = nonce.second;
6163
input[11] = nonce.second >> 32;
6264
}
6365

64-
inline void ChaCha20Aligned::Keystream64(unsigned char* c, size_t blocks)
66+
inline void ChaCha20Aligned::Keystream(Span<std::byte> output) noexcept
6567
{
68+
unsigned char* c = UCharCast(output.data());
69+
size_t blocks = output.size() / BLOCKLEN;
70+
assert(blocks * BLOCKLEN == output.size());
71+
6672
uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
6773
uint32_t j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
6874

@@ -154,12 +160,18 @@ inline void ChaCha20Aligned::Keystream64(unsigned char* c, size_t blocks)
154160
return;
155161
}
156162
blocks -= 1;
157-
c += 64;
163+
c += BLOCKLEN;
158164
}
159165
}
160166

161-
inline void ChaCha20Aligned::Crypt64(const unsigned char* m, unsigned char* c, size_t blocks)
167+
inline void ChaCha20Aligned::Crypt(Span<const std::byte> in_bytes, Span<std::byte> out_bytes) noexcept
162168
{
169+
assert(in_bytes.size() == out_bytes.size());
170+
const unsigned char* m = UCharCast(in_bytes.data());
171+
unsigned char* c = UCharCast(out_bytes.data());
172+
size_t blocks = out_bytes.size() / BLOCKLEN;
173+
assert(blocks * BLOCKLEN == out_bytes.size());
174+
163175
uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
164176
uint32_t j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
165177

@@ -268,70 +280,68 @@ inline void ChaCha20Aligned::Crypt64(const unsigned char* m, unsigned char* c, s
268280
return;
269281
}
270282
blocks -= 1;
271-
c += 64;
272-
m += 64;
283+
c += BLOCKLEN;
284+
m += BLOCKLEN;
273285
}
274286
}
275287

276-
void ChaCha20::Keystream(unsigned char* c, size_t bytes)
288+
void ChaCha20::Keystream(Span<std::byte> out) noexcept
277289
{
278-
if (!bytes) return;
290+
if (out.empty()) return;
279291
if (m_bufleft) {
280-
unsigned reuse = std::min<size_t>(m_bufleft, bytes);
281-
memcpy(c, m_buffer + 64 - m_bufleft, reuse);
292+
unsigned reuse = std::min<size_t>(m_bufleft, out.size());
293+
std::copy(m_buffer.end() - m_bufleft, m_buffer.end() - m_bufleft + reuse, out.begin());
282294
m_bufleft -= reuse;
283-
bytes -= reuse;
284-
c += reuse;
295+
out = out.subspan(reuse);
285296
}
286-
if (bytes >= 64) {
287-
size_t blocks = bytes / 64;
288-
m_aligned.Keystream64(c, blocks);
289-
c += blocks * 64;
290-
bytes -= blocks * 64;
297+
if (out.size() >= m_aligned.BLOCKLEN) {
298+
size_t blocks = out.size() / m_aligned.BLOCKLEN;
299+
m_aligned.Keystream(out.first(blocks * m_aligned.BLOCKLEN));
300+
out = out.subspan(blocks * m_aligned.BLOCKLEN);
291301
}
292-
if (bytes) {
293-
m_aligned.Keystream64(m_buffer, 1);
294-
memcpy(c, m_buffer, bytes);
295-
m_bufleft = 64 - bytes;
302+
if (!out.empty()) {
303+
m_aligned.Keystream(m_buffer);
304+
std::copy(m_buffer.begin(), m_buffer.begin() + out.size(), out.begin());
305+
m_bufleft = m_aligned.BLOCKLEN - out.size();
296306
}
297307
}
298308

299-
void ChaCha20::Crypt(const unsigned char* m, unsigned char* c, size_t bytes)
309+
void ChaCha20::Crypt(Span<const std::byte> input, Span<std::byte> output) noexcept
300310
{
301-
if (!bytes) return;
311+
assert(input.size() == output.size());
312+
313+
if (!input.size()) return;
302314
if (m_bufleft) {
303-
unsigned reuse = std::min<size_t>(m_bufleft, bytes);
315+
unsigned reuse = std::min<size_t>(m_bufleft, input.size());
304316
for (unsigned i = 0; i < reuse; i++) {
305-
c[i] = m[i] ^ m_buffer[64 - m_bufleft + i];
317+
output[i] = input[i] ^ m_buffer[m_aligned.BLOCKLEN - m_bufleft + i];
306318
}
307319
m_bufleft -= reuse;
308-
bytes -= reuse;
309-
c += reuse;
310-
m += reuse;
320+
output = output.subspan(reuse);
321+
input = input.subspan(reuse);
311322
}
312-
if (bytes >= 64) {
313-
size_t blocks = bytes / 64;
314-
m_aligned.Crypt64(m, c, blocks);
315-
c += blocks * 64;
316-
m += blocks * 64;
317-
bytes -= blocks * 64;
323+
if (input.size() >= m_aligned.BLOCKLEN) {
324+
size_t blocks = input.size() / m_aligned.BLOCKLEN;
325+
m_aligned.Crypt(input.first(blocks * m_aligned.BLOCKLEN), output.first(blocks * m_aligned.BLOCKLEN));
326+
output = output.subspan(blocks * m_aligned.BLOCKLEN);
327+
input = input.subspan(blocks * m_aligned.BLOCKLEN);
318328
}
319-
if (bytes) {
320-
m_aligned.Keystream64(m_buffer, 1);
321-
for (unsigned i = 0; i < bytes; i++) {
322-
c[i] = m[i] ^ m_buffer[i];
329+
if (!input.empty()) {
330+
m_aligned.Keystream(m_buffer);
331+
for (unsigned i = 0; i < input.size(); i++) {
332+
output[i] = input[i] ^ m_buffer[i];
323333
}
324-
m_bufleft = 64 - bytes;
334+
m_bufleft = m_aligned.BLOCKLEN - input.size();
325335
}
326336
}
327337

328338
ChaCha20::~ChaCha20()
329339
{
330-
memory_cleanse(m_buffer, sizeof(m_buffer));
340+
memory_cleanse(m_buffer.data(), m_buffer.size());
331341
}
332342

333343
FSChaCha20::FSChaCha20(Span<const std::byte> key, uint32_t rekey_interval) noexcept :
334-
m_chacha20(UCharCast(key.data())), m_rekey_interval(rekey_interval)
344+
m_chacha20(key), m_rekey_interval(rekey_interval)
335345
{
336346
assert(key.size() == KEYLEN);
337347
}
@@ -341,20 +351,20 @@ void FSChaCha20::Crypt(Span<const std::byte> input, Span<std::byte> output) noex
341351
assert(input.size() == output.size());
342352

343353
// Invoke internal stream cipher for actual encryption/decryption.
344-
m_chacha20.Crypt(UCharCast(input.data()), UCharCast(output.data()), input.size());
354+
m_chacha20.Crypt(input, output);
345355

346356
// Rekey after m_rekey_interval encryptions/decryptions.
347357
if (++m_chunk_counter == m_rekey_interval) {
348358
// Get new key from the stream cipher.
349359
std::byte new_key[KEYLEN];
350-
m_chacha20.Keystream(UCharCast(new_key), sizeof(new_key));
360+
m_chacha20.Keystream(new_key);
351361
// Update its key.
352-
m_chacha20.SetKey32(UCharCast(new_key));
362+
m_chacha20.SetKey(new_key);
353363
// Wipe the key (a copy remains inside m_chacha20, where it'll be wiped on the next rekey
354364
// or on destruction).
355365
memory_cleanse(new_key, sizeof(new_key));
356366
// Set the nonce for the new section of output.
357-
m_chacha20.Seek64({0, ++m_rekey_counter}, 0);
367+
m_chacha20.Seek({0, ++m_rekey_counter}, 0);
358368
// Reset the chunk counter.
359369
m_chunk_counter = 0;
360370
}

src/crypto/chacha20.h

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,22 @@ class ChaCha20Aligned
2828
uint32_t input[12];
2929

3030
public:
31-
ChaCha20Aligned();
31+
/** Expected key length in constructor and SetKey. */
32+
static constexpr unsigned KEYLEN{32};
33+
34+
/** Block size (inputs/outputs to Keystream / Crypt should be multiples of this). */
35+
static constexpr unsigned BLOCKLEN{64};
36+
37+
ChaCha20Aligned() noexcept;
3238

3339
/** Initialize a cipher with specified 32-byte key. */
34-
ChaCha20Aligned(const unsigned char* key32);
40+
ChaCha20Aligned(Span<const std::byte> key) noexcept;
3541

3642
/** Destructor to clean up private memory. */
3743
~ChaCha20Aligned();
3844

39-
/** set 32-byte key. */
40-
void SetKey32(const unsigned char* key32);
45+
/** Set 32-byte key, and seek to nonce 0 and block position 0. */
46+
void SetKey(Span<const std::byte> key) noexcept;
4147

4248
/** Type for 96-bit nonces used by the Set function below.
4349
*
@@ -51,61 +57,66 @@ class ChaCha20Aligned
5157

5258
/** Set the 96-bit nonce and 32-bit block counter.
5359
*
54-
* Block_counter selects a position to seek to (to byte 64*block_counter). After 256 GiB, the
55-
* block counter overflows, and nonce.first is incremented.
60+
* Block_counter selects a position to seek to (to byte BLOCKLEN*block_counter). After 256 GiB,
61+
* the block counter overflows, and nonce.first is incremented.
5662
*/
57-
void Seek64(Nonce96 nonce, uint32_t block_counter);
63+
void Seek(Nonce96 nonce, uint32_t block_counter) noexcept;
5864

59-
/** outputs the keystream of size <64*blocks> into <c> */
60-
void Keystream64(unsigned char* c, size_t blocks);
65+
/** outputs the keystream into out, whose length must be a multiple of BLOCKLEN. */
66+
void Keystream(Span<std::byte> out) noexcept;
6167

62-
/** enciphers the message <input> of length <64*blocks> and write the enciphered representation into <output>
63-
* Used for encryption and decryption (XOR)
68+
/** en/deciphers the message <input> and write the result into <output>
69+
*
70+
* The size of input and output must be equal, and be a multiple of BLOCKLEN.
6471
*/
65-
void Crypt64(const unsigned char* input, unsigned char* output, size_t blocks);
72+
void Crypt(Span<const std::byte> input, Span<std::byte> output) noexcept;
6673
};
6774

6875
/** Unrestricted ChaCha20 cipher. */
6976
class ChaCha20
7077
{
7178
private:
7279
ChaCha20Aligned m_aligned;
73-
unsigned char m_buffer[64] = {0};
80+
std::array<std::byte, ChaCha20Aligned::BLOCKLEN> m_buffer;
7481
unsigned m_bufleft{0};
7582

7683
public:
77-
ChaCha20() = default;
84+
/** Expected key length in constructor and SetKey. */
85+
static constexpr unsigned KEYLEN = ChaCha20Aligned::KEYLEN;
86+
87+
ChaCha20() noexcept = default;
7888

7989
/** Initialize a cipher with specified 32-byte key. */
80-
ChaCha20(const unsigned char* key32) : m_aligned(key32) {}
90+
ChaCha20(Span<const std::byte> key) noexcept : m_aligned(key) {}
8191

8292
/** Destructor to clean up private memory. */
8393
~ChaCha20();
8494

85-
/** set 32-byte key. */
86-
void SetKey32(const unsigned char* key32)
95+
/** Set 32-byte key, and seek to nonce 0 and block position 0. */
96+
void SetKey(Span<const std::byte> key) noexcept
8797
{
88-
m_aligned.SetKey32(key32);
98+
m_aligned.SetKey(key);
8999
m_bufleft = 0;
90100
}
91101

92102
/** 96-bit nonce type. */
93103
using Nonce96 = ChaCha20Aligned::Nonce96;
94104

95-
/** Set the 96-bit nonce and 32-bit block counter. */
96-
void Seek64(Nonce96 nonce, uint32_t block_counter)
105+
/** Set the 96-bit nonce and 32-bit block counter. See ChaCha20Aligned::Seek. */
106+
void Seek(Nonce96 nonce, uint32_t block_counter) noexcept
97107
{
98-
m_aligned.Seek64(nonce, block_counter);
108+
m_aligned.Seek(nonce, block_counter);
99109
m_bufleft = 0;
100110
}
101111

102-
/** outputs the keystream of size <bytes> into <c> */
103-
void Keystream(unsigned char* c, size_t bytes);
104-
105-
/** enciphers the message <input> of length <bytes> and write the enciphered representation into <output>
106-
* Used for encryption and decryption (XOR)
112+
/** en/deciphers the message <in_bytes> and write the result into <out_bytes>
113+
*
114+
* The size of in_bytes and out_bytes must be equal.
107115
*/
108-
void Crypt(const unsigned char* input, unsigned char* output, size_t bytes);
116+
void Crypt(Span<const std::byte> in_bytes, Span<std::byte> out_bytes) noexcept;
117+
118+
/** outputs the keystream to out. */
119+
void Keystream(Span<std::byte> out) noexcept;
109120
};
110121

111122
/** Forward-secure ChaCha20

0 commit comments

Comments
 (0)