Skip to content

Commit e37bcaa

Browse files
committed
Split ChaCha20 into aligned/unaligned variants
1 parent ceb74b8 commit e37bcaa

File tree

2 files changed

+97
-45
lines changed

2 files changed

+97
-45
lines changed

src/crypto/chacha20.cpp

+49-37
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <crypto/common.h>
99
#include <crypto/chacha20.h>
1010

11+
#include <algorithm>
1112
#include <string.h>
1213

1314
constexpr static inline uint32_t rotl32(uint32_t v, int c) { return (v << c) | (v >> (32 - c)); }
@@ -23,7 +24,7 @@ constexpr static inline uint32_t rotl32(uint32_t v, int c) { return (v << c) | (
2324
static const unsigned char sigma[] = "expand 32-byte k";
2425
static const unsigned char tau[] = "expand 16-byte k";
2526

26-
void ChaCha20::SetKey(const unsigned char* k, size_t keylen)
27+
void ChaCha20Aligned::SetKey(const unsigned char* k, size_t keylen)
2728
{
2829
const unsigned char *constants;
2930

@@ -51,37 +52,34 @@ void ChaCha20::SetKey(const unsigned char* k, size_t keylen)
5152
input[15] = 0;
5253
}
5354

54-
ChaCha20::ChaCha20()
55+
ChaCha20Aligned::ChaCha20Aligned()
5556
{
5657
memset(input, 0, sizeof(input));
5758
}
5859

59-
ChaCha20::ChaCha20(const unsigned char* k, size_t keylen)
60+
ChaCha20Aligned::ChaCha20Aligned(const unsigned char* k, size_t keylen)
6061
{
6162
SetKey(k, keylen);
6263
}
6364

64-
void ChaCha20::SetIV(uint64_t iv)
65+
void ChaCha20Aligned::SetIV(uint64_t iv)
6566
{
6667
input[14] = iv;
6768
input[15] = iv >> 32;
6869
}
6970

70-
void ChaCha20::Seek(uint64_t pos)
71+
void ChaCha20Aligned::Seek(uint64_t pos)
7172
{
7273
input[12] = pos;
7374
input[13] = pos >> 32;
7475
}
7576

76-
void ChaCha20::Keystream(unsigned char* c, size_t bytes)
77+
inline void ChaCha20Aligned::Keystream64(unsigned char* c, size_t blocks)
7778
{
7879
uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
7980
uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
80-
unsigned char *ctarget = nullptr;
81-
unsigned char tmp[64];
82-
unsigned int i;
8381

84-
if (!bytes) return;
82+
if (!blocks) return;
8583

8684
j0 = input[0];
8785
j1 = input[1];
@@ -101,10 +99,6 @@ void ChaCha20::Keystream(unsigned char* c, size_t bytes)
10199
j15 = input[15];
102100

103101
for (;;) {
104-
if (bytes < 64) {
105-
ctarget = c;
106-
c = tmp;
107-
}
108102
x0 = j0;
109103
x1 = j1;
110104
x2 = j2;
@@ -171,28 +165,22 @@ void ChaCha20::Keystream(unsigned char* c, size_t bytes)
171165
WriteLE32(c + 56, x14);
172166
WriteLE32(c + 60, x15);
173167

174-
if (bytes <= 64) {
175-
if (bytes < 64) {
176-
for (i = 0;i < bytes;++i) ctarget[i] = c[i];
177-
}
168+
if (blocks == 1) {
178169
input[12] = j12;
179170
input[13] = j13;
180171
return;
181172
}
182-
bytes -= 64;
173+
blocks -= 1;
183174
c += 64;
184175
}
185176
}
186177

187-
void ChaCha20::Crypt(const unsigned char* m, unsigned char* c, size_t bytes)
178+
inline void ChaCha20Aligned::Crypt64(const unsigned char* m, unsigned char* c, size_t blocks)
188179
{
189180
uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
190181
uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
191-
unsigned char *ctarget = nullptr;
192-
unsigned char tmp[64];
193-
unsigned int i;
194182

195-
if (!bytes) return;
183+
if (!blocks) return;
196184

197185
j0 = input[0];
198186
j1 = input[1];
@@ -212,14 +200,6 @@ void ChaCha20::Crypt(const unsigned char* m, unsigned char* c, size_t bytes)
212200
j15 = input[15];
213201

214202
for (;;) {
215-
if (bytes < 64) {
216-
// if m has fewer than 64 bytes available, copy m to tmp and
217-
// read from tmp instead
218-
for (i = 0;i < bytes;++i) tmp[i] = m[i];
219-
m = tmp;
220-
ctarget = c;
221-
c = tmp;
222-
}
223203
x0 = j0;
224204
x1 = j1;
225205
x2 = j2;
@@ -303,16 +283,48 @@ void ChaCha20::Crypt(const unsigned char* m, unsigned char* c, size_t bytes)
303283
WriteLE32(c + 56, x14);
304284
WriteLE32(c + 60, x15);
305285

306-
if (bytes <= 64) {
307-
if (bytes < 64) {
308-
for (i = 0;i < bytes;++i) ctarget[i] = c[i];
309-
}
286+
if (blocks == 1) {
310287
input[12] = j12;
311288
input[13] = j13;
312289
return;
313290
}
314-
bytes -= 64;
291+
blocks -= 1;
315292
c += 64;
316293
m += 64;
317294
}
318295
}
296+
297+
void ChaCha20::Keystream(unsigned char* c, size_t bytes)
298+
{
299+
if (!bytes) return;
300+
if (bytes >= 64) {
301+
size_t blocks = bytes / 64;
302+
m_aligned.Keystream64(c, blocks);
303+
c += blocks * 64;
304+
bytes -= blocks * 64;
305+
}
306+
if (bytes) {
307+
unsigned char buffer[64];
308+
m_aligned.Keystream64(buffer, 1);
309+
memcpy(c, buffer, bytes);
310+
}
311+
}
312+
313+
void ChaCha20::Crypt(const unsigned char* m, unsigned char* c, size_t bytes)
314+
{
315+
if (!bytes) return;
316+
if (bytes >= 64) {
317+
size_t blocks = bytes / 64;
318+
m_aligned.Crypt64(m, c, blocks);
319+
c += blocks * 64;
320+
m += blocks * 64;
321+
bytes -= blocks * 64;
322+
}
323+
if (bytes) {
324+
unsigned char buffer[64];
325+
m_aligned.Keystream64(buffer, 1);
326+
for (unsigned i = 0; i < bytes; i++) {
327+
c[i] = m[i] ^ buffer[i];
328+
}
329+
}
330+
}

src/crypto/chacha20.h

+48-8
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,59 @@
88
#include <cstdlib>
99
#include <stdint.h>
1010

11-
/** A class for ChaCha20 256-bit stream cipher developed by Daniel J. Bernstein
12-
https://cr.yp.to/chacha/chacha-20080128.pdf */
13-
class ChaCha20
11+
// classes for ChaCha20 256-bit stream cipher developed by Daniel J. Bernstein
12+
// https://cr.yp.to/chacha/chacha-20080128.pdf */
13+
14+
/** ChaCha20 cipher that only operates on multiples of 64 bytes. */
15+
class ChaCha20Aligned
1416
{
1517
private:
1618
uint32_t input[16];
1719

1820
public:
19-
ChaCha20();
20-
ChaCha20(const unsigned char* key, size_t keylen);
21-
void SetKey(const unsigned char* key, size_t keylen); //!< set key with flexible keylength; 256bit recommended */
22-
void SetIV(uint64_t iv); // set the 64bit nonce
23-
void Seek(uint64_t pos); // set the 64bit block counter
21+
ChaCha20Aligned();
22+
23+
/** Initialize a cipher with specified key (see SetKey for arguments). */
24+
ChaCha20Aligned(const unsigned char* key, size_t keylen);
25+
26+
/** set key with flexible keylength (16 or 32 bytes; 32 recommended). */
27+
void SetKey(const unsigned char* key, size_t keylen);
28+
29+
/** set the 64-bit nonce. */
30+
void SetIV(uint64_t iv);
31+
32+
/** set the 64bit block counter (pos seeks to byte position 64*pos). */
33+
void Seek(uint64_t pos);
34+
35+
/** outputs the keystream of size <64*blocks> into <c> */
36+
void Keystream64(unsigned char* c, size_t blocks);
37+
38+
/** enciphers the message <input> of length <64*blocks> and write the enciphered representation into <output>
39+
* Used for encryption and decryption (XOR)
40+
*/
41+
void Crypt64(const unsigned char* input, unsigned char* output, size_t blocks);
42+
};
43+
44+
/** Unrestricted ChaCha20 cipher. Seeks forward to a multiple of 64 bytes after every operation. */
45+
class ChaCha20
46+
{
47+
private:
48+
ChaCha20Aligned m_aligned;
49+
50+
public:
51+
ChaCha20() = default;
52+
53+
/** Initialize a cipher with specified key (see SetKey for arguments). */
54+
ChaCha20(const unsigned char* key, size_t keylen) : m_aligned(key, keylen) {}
55+
56+
/** set key with flexible keylength (16 or 32 bytes; 32 recommended). */
57+
void SetKey(const unsigned char* key, size_t keylen) { m_aligned.SetKey(key, keylen); }
58+
59+
/** set the 64-bit nonce. */
60+
void SetIV(uint64_t iv) { m_aligned.SetIV(iv); }
61+
62+
/** set the 64bit block counter (pos seeks to byte position 64*pos). */
63+
void Seek(uint64_t pos) { m_aligned.Seek(pos); }
2464

2565
/** outputs the keystream of size <bytes> into <c> */
2666
void Keystream(unsigned char* c, size_t bytes);

0 commit comments

Comments
 (0)