Botan 3.0.0
Crypto and TLS for C&
chacha.cpp
Go to the documentation of this file.
1/*
2* ChaCha
3* (C) 2014,2018 Jack Lloyd
4*
5* Botan is released under the Simplified BSD License (see license.txt)
6*/
7
8#include <botan/internal/chacha.h>
9#include <botan/exceptn.h>
10#include <botan/internal/loadstor.h>
11#include <botan/internal/rotate.h>
12#include <botan/internal/cpuid.h>
13#include <botan/internal/fmt.h>
14
15namespace Botan {
16
17namespace {
18
19inline void chacha_quarter_round(uint32_t& a,
20 uint32_t& b,
21 uint32_t& c,
22 uint32_t& d)
23 {
24 a += b; d ^= a; d = rotl<16>(d);
25 c += d; b ^= c; b = rotl<12>(b);
26 a += b; d ^= a; d = rotl< 8>(d);
27 c += d; b ^= c; b = rotl< 7>(b);
28 }
29
30/*
31* Generate HChaCha cipher stream (for XChaCha IV setup)
32*/
33void hchacha(uint32_t output[8], const uint32_t input[16], size_t rounds)
34 {
35 BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds");
36
37 uint32_t x00 = input[ 0], x01 = input[ 1], x02 = input[ 2], x03 = input[ 3],
38 x04 = input[ 4], x05 = input[ 5], x06 = input[ 6], x07 = input[ 7],
39 x08 = input[ 8], x09 = input[ 9], x10 = input[10], x11 = input[11],
40 x12 = input[12], x13 = input[13], x14 = input[14], x15 = input[15];
41
42 for(size_t i = 0; i != rounds / 2; ++i)
43 {
44 chacha_quarter_round(x00, x04, x08, x12);
45 chacha_quarter_round(x01, x05, x09, x13);
46 chacha_quarter_round(x02, x06, x10, x14);
47 chacha_quarter_round(x03, x07, x11, x15);
48
49 chacha_quarter_round(x00, x05, x10, x15);
50 chacha_quarter_round(x01, x06, x11, x12);
51 chacha_quarter_round(x02, x07, x08, x13);
52 chacha_quarter_round(x03, x04, x09, x14);
53 }
54
55 output[0] = x00;
56 output[1] = x01;
57 output[2] = x02;
58 output[3] = x03;
59 output[4] = x12;
60 output[5] = x13;
61 output[6] = x14;
62 output[7] = x15;
63 }
64
65}
66
67ChaCha::ChaCha(size_t rounds) : m_rounds(rounds)
68 {
69 BOTAN_ARG_CHECK(m_rounds == 8 || m_rounds == 12 || m_rounds == 20,
70 "ChaCha only supports 8, 12 or 20 rounds");
71 }
72
73size_t ChaCha::parallelism()
74 {
75#if defined(BOTAN_HAS_CHACHA_AVX2)
76 if(CPUID::has_avx2())
77 return 8;
78#endif
79
80 return 4;
81 }
82
83std::string ChaCha::provider() const
84 {
85#if defined(BOTAN_HAS_CHACHA_AVX2)
86 if(CPUID::has_avx2())
87 {
88 return "avx2";
89 }
90#endif
91
92#if defined(BOTAN_HAS_CHACHA_SIMD32)
94 {
95 return "simd32";
96 }
97#endif
98
99 return "base";
100 }
101
102void ChaCha::chacha(uint8_t output[],
103 size_t output_blocks,
104 uint32_t state[16], size_t rounds)
105 {
106 BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds");
107
108#if defined(BOTAN_HAS_CHACHA_AVX2)
109 if(CPUID::has_avx2())
110 {
111 while(output_blocks >= 8)
112 {
113 ChaCha::chacha_avx2_x8(output, state, rounds);
114 output += 8*64;
115 output_blocks -= 8;
116 }
117 }
118#endif
119
120#if defined(BOTAN_HAS_CHACHA_SIMD32)
122 {
123 while(output_blocks >= 4)
124 {
125 ChaCha::chacha_simd32_x4(output, state, rounds);
126 output += 4*64;
127 output_blocks -= 4;
128 }
129 }
130#endif
131
132 // TODO interleave rounds
133 for(size_t i = 0; i != output_blocks; ++i)
134 {
135 uint32_t x00 = state[ 0], x01 = state[ 1], x02 = state[ 2], x03 = state[ 3],
136 x04 = state[ 4], x05 = state[ 5], x06 = state[ 6], x07 = state[ 7],
137 x08 = state[ 8], x09 = state[ 9], x10 = state[10], x11 = state[11],
138 x12 = state[12], x13 = state[13], x14 = state[14], x15 = state[15];
139
140 for(size_t r = 0; r != rounds / 2; ++r)
141 {
142 chacha_quarter_round(x00, x04, x08, x12);
143 chacha_quarter_round(x01, x05, x09, x13);
144 chacha_quarter_round(x02, x06, x10, x14);
145 chacha_quarter_round(x03, x07, x11, x15);
146
147 chacha_quarter_round(x00, x05, x10, x15);
148 chacha_quarter_round(x01, x06, x11, x12);
149 chacha_quarter_round(x02, x07, x08, x13);
150 chacha_quarter_round(x03, x04, x09, x14);
151 }
152
153 x00 += state[0];
154 x01 += state[1];
155 x02 += state[2];
156 x03 += state[3];
157 x04 += state[4];
158 x05 += state[5];
159 x06 += state[6];
160 x07 += state[7];
161 x08 += state[8];
162 x09 += state[9];
163 x10 += state[10];
164 x11 += state[11];
165 x12 += state[12];
166 x13 += state[13];
167 x14 += state[14];
168 x15 += state[15];
169
170 store_le(x00, output + 64 * i + 4 * 0);
171 store_le(x01, output + 64 * i + 4 * 1);
172 store_le(x02, output + 64 * i + 4 * 2);
173 store_le(x03, output + 64 * i + 4 * 3);
174 store_le(x04, output + 64 * i + 4 * 4);
175 store_le(x05, output + 64 * i + 4 * 5);
176 store_le(x06, output + 64 * i + 4 * 6);
177 store_le(x07, output + 64 * i + 4 * 7);
178 store_le(x08, output + 64 * i + 4 * 8);
179 store_le(x09, output + 64 * i + 4 * 9);
180 store_le(x10, output + 64 * i + 4 * 10);
181 store_le(x11, output + 64 * i + 4 * 11);
182 store_le(x12, output + 64 * i + 4 * 12);
183 store_le(x13, output + 64 * i + 4 * 13);
184 store_le(x14, output + 64 * i + 4 * 14);
185 store_le(x15, output + 64 * i + 4 * 15);
186
187 state[12]++;
188 state[13] += (state[12] == 0);
189 }
190 }
191
192/*
193* Combine cipher stream with message
194*/
195void ChaCha::cipher_bytes(const uint8_t in[], uint8_t out[], size_t length)
196 {
198
199 while(length >= m_buffer.size() - m_position)
200 {
201 const size_t available = m_buffer.size() - m_position;
202
203 xor_buf(out, in, &m_buffer[m_position], available);
204 chacha(m_buffer.data(), m_buffer.size() / 64,
205 m_state.data(), m_rounds);
206
207 length -= available;
208 in += available;
209 out += available;
210 m_position = 0;
211 }
212
213 xor_buf(out, in, &m_buffer[m_position], length);
214
215 m_position += length;
216 }
217
218void ChaCha::generate_keystream(uint8_t out[], size_t length)
219 {
221
222 while(length >= m_buffer.size() - m_position)
223 {
224 const size_t available = m_buffer.size() - m_position;
225
226 // TODO: this could write directly to the output buffer
227 // instead of bouncing it through m_buffer first
228 copy_mem(out, &m_buffer[m_position], available);
229 chacha(m_buffer.data(), m_buffer.size() / 64,
230 m_state.data(), m_rounds);
231
232 length -= available;
233 out += available;
234 m_position = 0;
235 }
236
237 copy_mem(out, &m_buffer[m_position], length);
238
239 m_position += length;
240 }
241
242void ChaCha::initialize_state()
243 {
244 static const uint32_t TAU[] =
245 { 0x61707865, 0x3120646e, 0x79622d36, 0x6b206574 };
246
247 static const uint32_t SIGMA[] =
248 { 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 };
249
250 m_state[4] = m_key[0];
251 m_state[5] = m_key[1];
252 m_state[6] = m_key[2];
253 m_state[7] = m_key[3];
254
255 if(m_key.size() == 4)
256 {
257 m_state[0] = TAU[0];
258 m_state[1] = TAU[1];
259 m_state[2] = TAU[2];
260 m_state[3] = TAU[3];
261
262 m_state[8] = m_key[0];
263 m_state[9] = m_key[1];
264 m_state[10] = m_key[2];
265 m_state[11] = m_key[3];
266 }
267 else
268 {
269 m_state[0] = SIGMA[0];
270 m_state[1] = SIGMA[1];
271 m_state[2] = SIGMA[2];
272 m_state[3] = SIGMA[3];
273
274 m_state[8] = m_key[4];
275 m_state[9] = m_key[5];
276 m_state[10] = m_key[6];
277 m_state[11] = m_key[7];
278 }
279
280 m_state[12] = 0;
281 m_state[13] = 0;
282 m_state[14] = 0;
283 m_state[15] = 0;
284
285 m_position = 0;
286 }
287
289 {
290 return !m_state.empty();
291 }
292
294 {
295 return 64;
296 }
297
298/*
299* ChaCha Key Schedule
300*/
301void ChaCha::key_schedule(const uint8_t key[], size_t length)
302 {
303 m_key.resize(length / 4);
304 load_le<uint32_t>(m_key.data(), key, m_key.size());
305
306 m_state.resize(16);
307
308 const size_t chacha_block = 64;
309 m_buffer.resize(parallelism() * chacha_block);
310
311 set_iv(nullptr, 0);
312 }
313
315 {
316 return 24;
317 }
318
320 {
321 return Key_Length_Specification(16, 32, 16);
322 }
323
324std::unique_ptr<StreamCipher> ChaCha::new_object() const
325 {
326 return std::make_unique<ChaCha>(m_rounds);
327 }
328
329bool ChaCha::valid_iv_length(size_t iv_len) const
330 {
331 return (iv_len == 0 || iv_len == 8 || iv_len == 12 || iv_len == 24);
332 }
333
334void ChaCha::set_iv_bytes(const uint8_t iv[], size_t length)
335 {
337
338 if(!valid_iv_length(length))
339 throw Invalid_IV_Length(name(), length);
340
341 initialize_state();
342
343 if(length == 0)
344 {
345 // Treat zero length IV same as an all-zero IV
346 m_state[14] = 0;
347 m_state[15] = 0;
348 }
349 else if(length == 8)
350 {
351 m_state[14] = load_le<uint32_t>(iv, 0);
352 m_state[15] = load_le<uint32_t>(iv, 1);
353 }
354 else if(length == 12)
355 {
356 m_state[13] = load_le<uint32_t>(iv, 0);
357 m_state[14] = load_le<uint32_t>(iv, 1);
358 m_state[15] = load_le<uint32_t>(iv, 2);
359 }
360 else if(length == 24)
361 {
362 m_state[12] = load_le<uint32_t>(iv, 0);
363 m_state[13] = load_le<uint32_t>(iv, 1);
364 m_state[14] = load_le<uint32_t>(iv, 2);
365 m_state[15] = load_le<uint32_t>(iv, 3);
366
367 secure_vector<uint32_t> hc(8);
368 hchacha(hc.data(), m_state.data(), m_rounds);
369
370 m_state[ 4] = hc[0];
371 m_state[ 5] = hc[1];
372 m_state[ 6] = hc[2];
373 m_state[ 7] = hc[3];
374 m_state[ 8] = hc[4];
375 m_state[ 9] = hc[5];
376 m_state[10] = hc[6];
377 m_state[11] = hc[7];
378 m_state[12] = 0;
379 m_state[13] = 0;
380 m_state[14] = load_le<uint32_t>(iv, 4);
381 m_state[15] = load_le<uint32_t>(iv, 5);
382 }
383
384 chacha(m_buffer.data(), m_buffer.size() / 64,
385 m_state.data(), m_rounds);
386 m_position = 0;
387 }
388
390 {
391 zap(m_key);
392 zap(m_state);
393 zap(m_buffer);
394 m_position = 0;
395 }
396
397std::string ChaCha::name() const
398 {
399 return fmt("ChaCha({})", m_rounds);
400 }
401
402void ChaCha::seek(uint64_t offset)
403 {
405
406 // Find the block offset
407 const uint64_t counter = offset / 64;
408
409 uint8_t out[8];
410
411 store_le(counter, out);
412
413 m_state[12] = load_le<uint32_t>(out, 0);
414 m_state[13] += load_le<uint32_t>(out, 1);
415
416 chacha(m_buffer.data(), m_buffer.size() / 64, m_state.data(), m_rounds);
417 m_position = offset % 64;
418 }
419}
#define BOTAN_ARG_CHECK(expr, msg)
Definition: assert.h:36
#define BOTAN_ASSERT(expr, assertion_made)
Definition: assert.h:54
static bool has_simd_32()
Definition: cpuid.cpp:17
void clear() override
Definition: chacha.cpp:389
std::string name() const override
Definition: chacha.cpp:397
size_t buffer_size() const override
Definition: chacha.cpp:293
std::unique_ptr< StreamCipher > new_object() const override
Definition: chacha.cpp:324
Key_Length_Specification key_spec() const override
Definition: chacha.cpp:319
bool valid_iv_length(size_t iv_len) const override
Definition: chacha.cpp:329
size_t default_iv_length() const override
Definition: chacha.cpp:314
std::string provider() const override
Definition: chacha.cpp:83
ChaCha(size_t rounds=20)
Definition: chacha.cpp:67
bool has_keying_material() const override
Definition: chacha.cpp:288
void seek(uint64_t offset) override
Definition: chacha.cpp:402
void set_iv(const uint8_t iv[], size_t iv_len)
void assert_key_material_set() const
Definition: sym_algo.h:182
Definition: alg_id.cpp:12
constexpr uint32_t load_le< uint32_t >(const uint8_t in[], size_t off)
Definition: loadstor.h:209
constexpr void store_le(uint16_t in, uint8_t out[2])
Definition: loadstor.h:465
void zap(std::vector< T, Alloc > &vec)
Definition: secmem.h:129
std::string fmt(std::string_view format, const T &... args)
Definition: fmt.h:60
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition: mem_ops.h:126
void xor_buf(uint8_t out[], const uint8_t in[], size_t length)
Definition: mem_ops.h:255