Bitcoin Core 28.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
52void 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
60inline 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
161inline 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
344FSChaCha20::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:74
unsigned char * UCharCast(char *c)
Definition: span.h:281
assert(!tx.IsCoinBase())