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