Bitcoin Core  26.1.0
P2P Digital Currency
chacha20.cpp
Go to the documentation of this file.
1 // Copyright (c) 2017-2022 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 // Based on the public domain implementation 'merged' by D. J. Bernstein
6 // See https://cr.yp.to/chacha.html.
7 
8 #include <crypto/common.h>
9 #include <crypto/chacha20.h>
10 #include <support/cleanse.h>
11 #include <span.h>
12 
13 #include <algorithm>
14 #include <string.h>
15 
16 constexpr static inline uint32_t rotl32(uint32_t v, int c) { return (v << c) | (v >> (32 - c)); }
17 
18 #define QUARTERROUND(a,b,c,d) \
19  a += b; d = rotl32(d ^ a, 16); \
20  c += d; b = rotl32(b ^ c, 12); \
21  a += b; d = rotl32(d ^ a, 8); \
22  c += d; b = rotl32(b ^ c, 7);
23 
24 #define REPEAT10(a) do { {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; } while(0)
25 
27 {
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));
37  input[8] = 0;
38  input[9] = 0;
39  input[10] = 0;
40  input[11] = 0;
41 }
42 
44 {
45  memory_cleanse(input, sizeof(input));
46 }
47 
49 {
50  SetKey(key);
51 }
52 
53 void ChaCha20Aligned::Seek(Nonce96 nonce, uint32_t block_counter) noexcept
54 {
55  input[8] = block_counter;
56  input[9] = nonce.first;
57  input[10] = nonce.second;
58  input[11] = nonce.second >> 32;
59 }
60 
61 inline void ChaCha20Aligned::Keystream(Span<std::byte> output) noexcept
62 {
63  unsigned char* c = UCharCast(output.data());
64  size_t blocks = output.size() / BLOCKLEN;
65  assert(blocks * BLOCKLEN == output.size());
66 
67  uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
68  uint32_t j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
69 
70  if (!blocks) return;
71 
72  j4 = input[0];
73  j5 = input[1];
74  j6 = input[2];
75  j7 = input[3];
76  j8 = input[4];
77  j9 = input[5];
78  j10 = input[6];
79  j11 = input[7];
80  j12 = input[8];
81  j13 = input[9];
82  j14 = input[10];
83  j15 = input[11];
84 
85  for (;;) {
86  x0 = 0x61707865;
87  x1 = 0x3320646e;
88  x2 = 0x79622d32;
89  x3 = 0x6b206574;
90  x4 = j4;
91  x5 = j5;
92  x6 = j6;
93  x7 = j7;
94  x8 = j8;
95  x9 = j9;
96  x10 = j10;
97  x11 = j11;
98  x12 = j12;
99  x13 = j13;
100  x14 = j14;
101  x15 = j15;
102 
103  // The 20 inner ChaCha20 rounds are unrolled here for performance.
104  REPEAT10(
105  QUARTERROUND( x0, x4, x8,x12);
106  QUARTERROUND( x1, x5, x9,x13);
107  QUARTERROUND( x2, x6,x10,x14);
108  QUARTERROUND( x3, x7,x11,x15);
109  QUARTERROUND( x0, x5,x10,x15);
110  QUARTERROUND( x1, x6,x11,x12);
111  QUARTERROUND( x2, x7, x8,x13);
112  QUARTERROUND( x3, x4, x9,x14);
113  );
114 
115  x0 += 0x61707865;
116  x1 += 0x3320646e;
117  x2 += 0x79622d32;
118  x3 += 0x6b206574;
119  x4 += j4;
120  x5 += j5;
121  x6 += j6;
122  x7 += j7;
123  x8 += j8;
124  x9 += j9;
125  x10 += j10;
126  x11 += j11;
127  x12 += j12;
128  x13 += j13;
129  x14 += j14;
130  x15 += j15;
131 
132  ++j12;
133  if (!j12) ++j13;
134 
135  WriteLE32(c + 0, x0);
136  WriteLE32(c + 4, x1);
137  WriteLE32(c + 8, x2);
138  WriteLE32(c + 12, x3);
139  WriteLE32(c + 16, x4);
140  WriteLE32(c + 20, x5);
141  WriteLE32(c + 24, x6);
142  WriteLE32(c + 28, x7);
143  WriteLE32(c + 32, x8);
144  WriteLE32(c + 36, x9);
145  WriteLE32(c + 40, x10);
146  WriteLE32(c + 44, x11);
147  WriteLE32(c + 48, x12);
148  WriteLE32(c + 52, x13);
149  WriteLE32(c + 56, x14);
150  WriteLE32(c + 60, x15);
151 
152  if (blocks == 1) {
153  input[8] = j12;
154  input[9] = j13;
155  return;
156  }
157  blocks -= 1;
158  c += BLOCKLEN;
159  }
160 }
161 
162 inline void ChaCha20Aligned::Crypt(Span<const std::byte> in_bytes, Span<std::byte> out_bytes) noexcept
163 {
164  assert(in_bytes.size() == out_bytes.size());
165  const unsigned char* m = UCharCast(in_bytes.data());
166  unsigned char* c = UCharCast(out_bytes.data());
167  size_t blocks = out_bytes.size() / BLOCKLEN;
168  assert(blocks * BLOCKLEN == out_bytes.size());
169 
170  uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
171  uint32_t j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
172 
173  if (!blocks) return;
174 
175  j4 = input[0];
176  j5 = input[1];
177  j6 = input[2];
178  j7 = input[3];
179  j8 = input[4];
180  j9 = input[5];
181  j10 = input[6];
182  j11 = input[7];
183  j12 = input[8];
184  j13 = input[9];
185  j14 = input[10];
186  j15 = input[11];
187 
188  for (;;) {
189  x0 = 0x61707865;
190  x1 = 0x3320646e;
191  x2 = 0x79622d32;
192  x3 = 0x6b206574;
193  x4 = j4;
194  x5 = j5;
195  x6 = j6;
196  x7 = j7;
197  x8 = j8;
198  x9 = j9;
199  x10 = j10;
200  x11 = j11;
201  x12 = j12;
202  x13 = j13;
203  x14 = j14;
204  x15 = j15;
205 
206  // The 20 inner ChaCha20 rounds are unrolled here for performance.
207  REPEAT10(
208  QUARTERROUND( x0, x4, x8,x12);
209  QUARTERROUND( x1, x5, x9,x13);
210  QUARTERROUND( x2, x6,x10,x14);
211  QUARTERROUND( x3, x7,x11,x15);
212  QUARTERROUND( x0, x5,x10,x15);
213  QUARTERROUND( x1, x6,x11,x12);
214  QUARTERROUND( x2, x7, x8,x13);
215  QUARTERROUND( x3, x4, x9,x14);
216  );
217 
218  x0 += 0x61707865;
219  x1 += 0x3320646e;
220  x2 += 0x79622d32;
221  x3 += 0x6b206574;
222  x4 += j4;
223  x5 += j5;
224  x6 += j6;
225  x7 += j7;
226  x8 += j8;
227  x9 += j9;
228  x10 += j10;
229  x11 += j11;
230  x12 += j12;
231  x13 += j13;
232  x14 += j14;
233  x15 += j15;
234 
235  x0 ^= ReadLE32(m + 0);
236  x1 ^= ReadLE32(m + 4);
237  x2 ^= ReadLE32(m + 8);
238  x3 ^= ReadLE32(m + 12);
239  x4 ^= ReadLE32(m + 16);
240  x5 ^= ReadLE32(m + 20);
241  x6 ^= ReadLE32(m + 24);
242  x7 ^= ReadLE32(m + 28);
243  x8 ^= ReadLE32(m + 32);
244  x9 ^= ReadLE32(m + 36);
245  x10 ^= ReadLE32(m + 40);
246  x11 ^= ReadLE32(m + 44);
247  x12 ^= ReadLE32(m + 48);
248  x13 ^= ReadLE32(m + 52);
249  x14 ^= ReadLE32(m + 56);
250  x15 ^= ReadLE32(m + 60);
251 
252  ++j12;
253  if (!j12) ++j13;
254 
255  WriteLE32(c + 0, x0);
256  WriteLE32(c + 4, x1);
257  WriteLE32(c + 8, x2);
258  WriteLE32(c + 12, x3);
259  WriteLE32(c + 16, x4);
260  WriteLE32(c + 20, x5);
261  WriteLE32(c + 24, x6);
262  WriteLE32(c + 28, x7);
263  WriteLE32(c + 32, x8);
264  WriteLE32(c + 36, x9);
265  WriteLE32(c + 40, x10);
266  WriteLE32(c + 44, x11);
267  WriteLE32(c + 48, x12);
268  WriteLE32(c + 52, x13);
269  WriteLE32(c + 56, x14);
270  WriteLE32(c + 60, x15);
271 
272  if (blocks == 1) {
273  input[8] = j12;
274  input[9] = j13;
275  return;
276  }
277  blocks -= 1;
278  c += BLOCKLEN;
279  m += BLOCKLEN;
280  }
281 }
282 
284 {
285  if (out.empty()) return;
286  if (m_bufleft) {
287  unsigned reuse = std::min<size_t>(m_bufleft, out.size());
288  std::copy(m_buffer.end() - m_bufleft, m_buffer.end() - m_bufleft + reuse, out.begin());
289  m_bufleft -= reuse;
290  out = out.subspan(reuse);
291  }
292  if (out.size() >= m_aligned.BLOCKLEN) {
293  size_t blocks = out.size() / m_aligned.BLOCKLEN;
294  m_aligned.Keystream(out.first(blocks * m_aligned.BLOCKLEN));
295  out = out.subspan(blocks * m_aligned.BLOCKLEN);
296  }
297  if (!out.empty()) {
298  m_aligned.Keystream(m_buffer);
299  std::copy(m_buffer.begin(), m_buffer.begin() + out.size(), out.begin());
300  m_bufleft = m_aligned.BLOCKLEN - out.size();
301  }
302 }
303 
305 {
306  assert(input.size() == output.size());
307 
308  if (!input.size()) return;
309  if (m_bufleft) {
310  unsigned reuse = std::min<size_t>(m_bufleft, input.size());
311  for (unsigned i = 0; i < reuse; i++) {
312  output[i] = input[i] ^ m_buffer[m_aligned.BLOCKLEN - m_bufleft + i];
313  }
314  m_bufleft -= reuse;
315  output = output.subspan(reuse);
316  input = input.subspan(reuse);
317  }
318  if (input.size() >= m_aligned.BLOCKLEN) {
319  size_t blocks = input.size() / m_aligned.BLOCKLEN;
320  m_aligned.Crypt(input.first(blocks * m_aligned.BLOCKLEN), output.first(blocks * m_aligned.BLOCKLEN));
321  output = output.subspan(blocks * m_aligned.BLOCKLEN);
322  input = input.subspan(blocks * m_aligned.BLOCKLEN);
323  }
324  if (!input.empty()) {
325  m_aligned.Keystream(m_buffer);
326  for (unsigned i = 0; i < input.size(); i++) {
327  output[i] = input[i] ^ m_buffer[i];
328  }
329  m_bufleft = m_aligned.BLOCKLEN - input.size();
330  }
331 }
332 
334 {
335  memory_cleanse(m_buffer.data(), m_buffer.size());
336 }
337 
339 {
340  m_aligned.SetKey(key);
341  m_bufleft = 0;
342  memory_cleanse(m_buffer.data(), m_buffer.size());
343 }
344 
345 FSChaCha20::FSChaCha20(Span<const std::byte> key, uint32_t rekey_interval) noexcept :
346  m_chacha20(key), m_rekey_interval(rekey_interval)
347 {
348  assert(key.size() == KEYLEN);
349 }
350 
352 {
353  assert(input.size() == output.size());
354 
355  // Invoke internal stream cipher for actual encryption/decryption.
356  m_chacha20.Crypt(input, output);
357 
358  // Rekey after m_rekey_interval encryptions/decryptions.
359  if (++m_chunk_counter == m_rekey_interval) {
360  // Get new key from the stream cipher.
361  std::byte new_key[KEYLEN];
362  m_chacha20.Keystream(new_key);
363  // Update its key.
364  m_chacha20.SetKey(new_key);
365  // Wipe the key (a copy remains inside m_chacha20, where it'll be wiped on the next rekey
366  // or on destruction).
367  memory_cleanse(new_key, sizeof(new_key));
368  // Set the nonce for the new section of output.
369  m_chacha20.Seek({0, ++m_rekey_counter}, 0);
370  // Reset the chunk counter.
371  m_chunk_counter = 0;
372  }
373 }
assert(!tx.IsCoinBase())
unsigned int nonce
Definition: miner_tests.cpp:72
~ChaCha20()
Destructor to clean up private memory.
Definition: chacha20.cpp:333
void Keystream(Span< std::byte > out) noexcept
outputs the keystream to out.
Definition: chacha20.cpp:283
#define REPEAT10(a)
Definition: chacha20.cpp:24
void memory_cleanse(void *ptr, size_t len)
Secure overwrite a buffer (possibly containing secret data) with zero-bytes.
Definition: cleanse.cpp:14
void SetKey(Span< const std::byte > key) noexcept
Set 32-byte key, and seek to nonce 0 and block position 0.
Definition: chacha20.cpp:26
uint32_t input[12]
Definition: chacha20.h:28
void Seek(Nonce96 nonce, uint32_t block_counter) noexcept
Set the 96-bit nonce and 32-bit block counter.
Definition: chacha20.cpp:53
static void WriteLE32(unsigned char *ptr, uint32_t x)
Definition: common.h:44
~ChaCha20Aligned()
Destructor to clean up private memory.
Definition: chacha20.cpp:43
#define QUARTERROUND(a, b, c, d)
Definition: chacha20.cpp:18
void Crypt(Span< const std::byte > input, Span< std::byte > output) noexcept
Encrypt or decrypt a chunk.
Definition: chacha20.cpp:351
static constexpr uint32_t rotl32(uint32_t v, int c)
Definition: chacha20.cpp:16
ChaCha20Aligned() noexcept=delete
For safety, disallow initialization without key.
std::array< std::byte, ChaCha20Aligned::BLOCKLEN > m_buffer
Definition: chacha20.h:81
std::pair< uint32_t, uint64_t > Nonce96
Type for 96-bit nonces used by the Set function below.
Definition: chacha20.h:57
void Crypt(Span< const std::byte > in_bytes, Span< std::byte > out_bytes) noexcept
en/deciphers the message <in_bytes> and write the result into <out_bytes>
Definition: chacha20.cpp:304
void Crypt(Span< const std::byte > input, Span< std::byte > output) noexcept
en/deciphers the message <input> and write the result into <output>
Definition: chacha20.cpp:162
static uint32_t ReadLE32(const unsigned char *ptr)
Definition: common.h:24
unsigned char * UCharCast(char *c)
Definition: span.h:270
void Keystream(Span< std::byte > out) noexcept
outputs the keystream into out, whose length must be a multiple of BLOCKLEN.
Definition: chacha20.cpp:61
void SetKey(Span< const std::byte > key) noexcept
Set 32-byte key, and seek to nonce 0 and block position 0.
Definition: chacha20.cpp:338
FSChaCha20(const FSChaCha20 &)=delete