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