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