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