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