Line data Source code
1 : // Copyright (c) 2017-2019 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 :
25 965586 : void ChaCha20Aligned::SetKey(Span<const std::byte> key) noexcept
26 : {
27 965586 : assert(key.size() == KEYLEN);
28 965586 : input[0] = ReadLE32(UCharCast(key.data() + 0));
29 965586 : input[1] = ReadLE32(UCharCast(key.data() + 4));
30 965586 : input[2] = ReadLE32(UCharCast(key.data() + 8));
31 965586 : input[3] = ReadLE32(UCharCast(key.data() + 12));
32 965586 : input[4] = ReadLE32(UCharCast(key.data() + 16));
33 965586 : input[5] = ReadLE32(UCharCast(key.data() + 20));
34 965586 : input[6] = ReadLE32(UCharCast(key.data() + 24));
35 965586 : input[7] = ReadLE32(UCharCast(key.data() + 28));
36 965586 : input[8] = 0;
37 965586 : input[9] = 0;
38 965586 : input[10] = 0;
39 965586 : input[11] = 0;
40 965586 : }
41 :
42 989896 : ChaCha20Aligned::~ChaCha20Aligned()
43 494948 : {
44 494948 : memory_cleanse(input, sizeof(input));
45 989896 : }
46 :
47 990476 : ChaCha20Aligned::ChaCha20Aligned(Span<const std::byte> key) noexcept
48 495238 : {
49 495238 : SetKey(key);
50 990476 : }
51 :
52 2038081 : void ChaCha20Aligned::Seek(Nonce96 nonce, uint32_t block_counter) noexcept
53 : {
54 2038081 : input[8] = block_counter;
55 2038081 : input[9] = nonce.first;
56 2038081 : input[10] = nonce.second;
57 2038081 : input[11] = nonce.second >> 32;
58 2038081 : }
59 :
60 4358719 : inline void ChaCha20Aligned::Keystream(Span<std::byte> output) noexcept
61 : {
62 4358719 : unsigned char* c = UCharCast(output.data());
63 4358719 : size_t blocks = output.size() / BLOCKLEN;
64 4358719 : 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 4358719 : if (!blocks) return;
70 :
71 4358719 : j4 = input[0];
72 4358719 : j5 = input[1];
73 4358719 : j6 = input[2];
74 4358719 : j7 = input[3];
75 4358719 : j8 = input[4];
76 4358719 : j9 = input[5];
77 4358719 : j10 = input[6];
78 4358719 : j11 = input[7];
79 4358719 : j12 = input[8];
80 4358719 : j13 = input[9];
81 4358719 : j14 = input[10];
82 4358719 : j15 = input[11];
83 :
84 6510420 : for (;;) {
85 6510420 : x0 = 0x61707865;
86 6510420 : x1 = 0x3320646e;
87 6510420 : x2 = 0x79622d32;
88 6510420 : x3 = 0x6b206574;
89 6510420 : x4 = j4;
90 6510420 : x5 = j5;
91 6510420 : x6 = j6;
92 6510420 : x7 = j7;
93 6510420 : x8 = j8;
94 6510420 : x9 = j9;
95 6510420 : x10 = j10;
96 6510420 : x11 = j11;
97 6510420 : x12 = j12;
98 6510420 : x13 = j13;
99 6510420 : x14 = j14;
100 6510420 : x15 = j15;
101 :
102 : // The 20 inner ChaCha20 rounds are unrolled here for performance.
103 6510420 : 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 6510420 : x0 += 0x61707865;
115 6510420 : x1 += 0x3320646e;
116 6510420 : x2 += 0x79622d32;
117 6510420 : x3 += 0x6b206574;
118 6510420 : x4 += j4;
119 6510420 : x5 += j5;
120 6510420 : x6 += j6;
121 6510420 : x7 += j7;
122 6510420 : x8 += j8;
123 6510420 : x9 += j9;
124 6510420 : x10 += j10;
125 6510420 : x11 += j11;
126 6510420 : x12 += j12;
127 6510420 : x13 += j13;
128 6510420 : x14 += j14;
129 6510420 : x15 += j15;
130 :
131 6510420 : ++j12;
132 6510420 : if (!j12) ++j13;
133 :
134 6510420 : WriteLE32(c + 0, x0);
135 6510420 : WriteLE32(c + 4, x1);
136 6510420 : WriteLE32(c + 8, x2);
137 6510420 : WriteLE32(c + 12, x3);
138 6510420 : WriteLE32(c + 16, x4);
139 6510420 : WriteLE32(c + 20, x5);
140 6510420 : WriteLE32(c + 24, x6);
141 6510420 : WriteLE32(c + 28, x7);
142 6510420 : WriteLE32(c + 32, x8);
143 6510420 : WriteLE32(c + 36, x9);
144 6510420 : WriteLE32(c + 40, x10);
145 6510420 : WriteLE32(c + 44, x11);
146 6510420 : WriteLE32(c + 48, x12);
147 6510420 : WriteLE32(c + 52, x13);
148 6510420 : WriteLE32(c + 56, x14);
149 6510420 : WriteLE32(c + 60, x15);
150 :
151 6510420 : if (blocks == 1) {
152 4358719 : input[8] = j12;
153 4358719 : input[9] = j13;
154 4358719 : return;
155 : }
156 2151701 : blocks -= 1;
157 2151701 : c += BLOCKLEN;
158 : }
159 4358719 : }
160 :
161 85426 : inline void ChaCha20Aligned::Crypt(Span<const std::byte> in_bytes, Span<std::byte> out_bytes) noexcept
162 : {
163 85426 : assert(in_bytes.size() == out_bytes.size());
164 85426 : const unsigned char* m = UCharCast(in_bytes.data());
165 85426 : unsigned char* c = UCharCast(out_bytes.data());
166 85426 : size_t blocks = out_bytes.size() / BLOCKLEN;
167 85426 : 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 85426 : if (!blocks) return;
173 :
174 85426 : j4 = input[0];
175 85426 : j5 = input[1];
176 85426 : j6 = input[2];
177 85426 : j7 = input[3];
178 85426 : j8 = input[4];
179 85426 : j9 = input[5];
180 85426 : j10 = input[6];
181 85426 : j11 = input[7];
182 85426 : j12 = input[8];
183 85426 : j13 = input[9];
184 85426 : j14 = input[10];
185 85426 : j15 = input[11];
186 :
187 4365480 : for (;;) {
188 4365480 : x0 = 0x61707865;
189 4365480 : x1 = 0x3320646e;
190 4365480 : x2 = 0x79622d32;
191 4365480 : x3 = 0x6b206574;
192 4365480 : x4 = j4;
193 4365480 : x5 = j5;
194 4365480 : x6 = j6;
195 4365480 : x7 = j7;
196 4365480 : x8 = j8;
197 4365480 : x9 = j9;
198 4365480 : x10 = j10;
199 4365480 : x11 = j11;
200 4365480 : x12 = j12;
201 4365480 : x13 = j13;
202 4365480 : x14 = j14;
203 4365480 : x15 = j15;
204 :
205 : // The 20 inner ChaCha20 rounds are unrolled here for performance.
206 4365480 : 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 4365480 : x0 += 0x61707865;
218 4365480 : x1 += 0x3320646e;
219 4365480 : x2 += 0x79622d32;
220 4365480 : x3 += 0x6b206574;
221 4365480 : x4 += j4;
222 4365480 : x5 += j5;
223 4365480 : x6 += j6;
224 4365480 : x7 += j7;
225 4365480 : x8 += j8;
226 4365480 : x9 += j9;
227 4365480 : x10 += j10;
228 4365480 : x11 += j11;
229 4365480 : x12 += j12;
230 4365480 : x13 += j13;
231 4365480 : x14 += j14;
232 4365480 : x15 += j15;
233 :
234 4365480 : x0 ^= ReadLE32(m + 0);
235 4365480 : x1 ^= ReadLE32(m + 4);
236 4365480 : x2 ^= ReadLE32(m + 8);
237 4365480 : x3 ^= ReadLE32(m + 12);
238 4365480 : x4 ^= ReadLE32(m + 16);
239 4365480 : x5 ^= ReadLE32(m + 20);
240 4365480 : x6 ^= ReadLE32(m + 24);
241 4365480 : x7 ^= ReadLE32(m + 28);
242 4365480 : x8 ^= ReadLE32(m + 32);
243 4365480 : x9 ^= ReadLE32(m + 36);
244 4365480 : x10 ^= ReadLE32(m + 40);
245 4365480 : x11 ^= ReadLE32(m + 44);
246 4365480 : x12 ^= ReadLE32(m + 48);
247 4365480 : x13 ^= ReadLE32(m + 52);
248 4365480 : x14 ^= ReadLE32(m + 56);
249 4365480 : x15 ^= ReadLE32(m + 60);
250 :
251 4365480 : ++j12;
252 4365480 : if (!j12) ++j13;
253 :
254 4365480 : WriteLE32(c + 0, x0);
255 4365480 : WriteLE32(c + 4, x1);
256 4365480 : WriteLE32(c + 8, x2);
257 4365480 : WriteLE32(c + 12, x3);
258 4365480 : WriteLE32(c + 16, x4);
259 4365480 : WriteLE32(c + 20, x5);
260 4365480 : WriteLE32(c + 24, x6);
261 4365480 : WriteLE32(c + 28, x7);
262 4365480 : WriteLE32(c + 32, x8);
263 4365480 : WriteLE32(c + 36, x9);
264 4365480 : WriteLE32(c + 40, x10);
265 4365480 : WriteLE32(c + 44, x11);
266 4365480 : WriteLE32(c + 48, x12);
267 4365480 : WriteLE32(c + 52, x13);
268 4365480 : WriteLE32(c + 56, x14);
269 4365480 : WriteLE32(c + 60, x15);
270 :
271 4365480 : if (blocks == 1) {
272 85426 : input[8] = j12;
273 85426 : input[9] = j13;
274 85426 : return;
275 : }
276 4280054 : blocks -= 1;
277 4280054 : c += BLOCKLEN;
278 4280054 : m += BLOCKLEN;
279 : }
280 85426 : }
281 :
282 18137561 : void ChaCha20::Keystream(Span<std::byte> out) noexcept
283 : {
284 18137561 : if (out.empty()) return;
285 18137304 : if (m_bufleft) {
286 14181532 : unsigned reuse = std::min<size_t>(m_bufleft, out.size());
287 14181532 : std::copy(m_buffer.end() - m_bufleft, m_buffer.end() - m_bufleft + reuse, out.begin());
288 14181532 : m_bufleft -= reuse;
289 14181532 : out = out.subspan(reuse);
290 14181532 : }
291 18137304 : if (out.size() >= m_aligned.BLOCKLEN) {
292 1424260 : size_t blocks = out.size() / m_aligned.BLOCKLEN;
293 1424260 : m_aligned.Keystream(out.first(blocks * m_aligned.BLOCKLEN));
294 1424260 : out = out.subspan(blocks * m_aligned.BLOCKLEN);
295 1424260 : }
296 18137304 : if (!out.empty()) {
297 2689143 : m_aligned.Keystream(m_buffer);
298 2689143 : std::copy(m_buffer.begin(), m_buffer.begin() + out.size(), out.begin());
299 2689143 : m_bufleft = m_aligned.BLOCKLEN - out.size();
300 2689143 : }
301 18137561 : }
302 :
303 1666309 : void ChaCha20::Crypt(Span<const std::byte> input, Span<std::byte> output) noexcept
304 : {
305 1666309 : assert(input.size() == output.size());
306 :
307 1666309 : if (!input.size()) return;
308 412230 : if (m_bufleft) {
309 262587 : unsigned reuse = std::min<size_t>(m_bufleft, input.size());
310 6839098 : for (unsigned i = 0; i < reuse; i++) {
311 6576511 : output[i] = input[i] ^ m_buffer[m_aligned.BLOCKLEN - m_bufleft + i];
312 6576511 : }
313 262587 : m_bufleft -= reuse;
314 262587 : output = output.subspan(reuse);
315 262587 : input = input.subspan(reuse);
316 262587 : }
317 412230 : if (input.size() >= m_aligned.BLOCKLEN) {
318 85426 : size_t blocks = input.size() / m_aligned.BLOCKLEN;
319 85426 : m_aligned.Crypt(input.first(blocks * m_aligned.BLOCKLEN), output.first(blocks * m_aligned.BLOCKLEN));
320 85426 : output = output.subspan(blocks * m_aligned.BLOCKLEN);
321 85426 : input = input.subspan(blocks * m_aligned.BLOCKLEN);
322 85426 : }
323 412230 : if (!input.empty()) {
324 244926 : m_aligned.Keystream(m_buffer);
325 3396921 : for (unsigned i = 0; i < input.size(); i++) {
326 3151995 : output[i] = input[i] ^ m_buffer[i];
327 3151995 : }
328 244926 : m_bufleft = m_aligned.BLOCKLEN - input.size();
329 244926 : }
330 1666309 : }
331 :
332 989116 : ChaCha20::~ChaCha20()
333 494558 : {
334 494558 : memory_cleanse(m_buffer.data(), m_buffer.size());
335 989116 : }
336 :
337 470348 : void ChaCha20::SetKey(Span<const std::byte> key) noexcept
338 : {
339 470348 : m_aligned.SetKey(key);
340 470348 : m_bufleft = 0;
341 470348 : memory_cleanse(m_buffer.data(), m_buffer.size());
342 470348 : }
343 :
344 1006 : FSChaCha20::FSChaCha20(Span<const std::byte> key, uint32_t rekey_interval) noexcept :
345 503 : m_chacha20(key), m_rekey_interval(rekey_interval)
346 503 : {
347 : assert(key.size() == KEYLEN);
348 503 : }
349 :
350 167671 : void FSChaCha20::Crypt(Span<const std::byte> input, Span<std::byte> output) noexcept
351 : {
352 167671 : assert(input.size() == output.size());
353 :
354 : // Invoke internal stream cipher for actual encryption/decryption.
355 167671 : m_chacha20.Crypt(input, output);
356 :
357 : // Rekey after m_rekey_interval encryptions/decryptions.
358 167671 : if (++m_chunk_counter == m_rekey_interval) {
359 : // Get new key from the stream cipher.
360 : std::byte new_key[KEYLEN];
361 674 : m_chacha20.Keystream(new_key);
362 : // Update its key.
363 674 : 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 674 : memory_cleanse(new_key, sizeof(new_key));
367 : // Set the nonce for the new section of output.
368 674 : m_chacha20.Seek({0, ++m_rekey_counter}, 0);
369 : // Reset the chunk counter.
370 674 : m_chunk_counter = 0;
371 674 : }
372 167671 : }
|