• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 4855067158

01 May 2023 10:17PM UTC coverage: 91.719% (-0.4%) from 92.147%
4855067158

push

github

77610 of 84617 relevant lines covered (91.72%)

11851668.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.93
/src/lib/stream/chacha/chacha.cpp
1
/*
2
* ChaCha
3
* (C) 2014,2018,2023 Jack Lloyd
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include <botan/internal/chacha.h>
9

10
#include <botan/exceptn.h>
11
#include <botan/internal/loadstor.h>
12
#include <botan/internal/rotate.h>
13
#include <botan/internal/cpuid.h>
14
#include <botan/internal/fmt.h>
15

16
namespace Botan {
17

18
namespace {
19

20
inline void chacha_quarter_round(uint32_t& a,
2,216,096✔
21
                                 uint32_t& b,
22
                                 uint32_t& c,
23
                                 uint32_t& d)
24
   {
25
   a += b; d ^= a; d = rotl<16>(d);
2,216,096✔
26
   c += d; b ^= c; b = rotl<12>(b);
2,216,096✔
27
   a += b; d ^= a; d = rotl< 8>(d);
2,216,096✔
28
   c += d; b ^= c; b = rotl< 7>(b);
2,216,096✔
29
   }
2,216,096✔
30

31
/*
32
* Generate HChaCha cipher stream (for XChaCha IV setup)
33
*/
34
void hchacha(uint32_t output[8], const uint32_t input[16], size_t rounds)
23,410✔
35
   {
36
   BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds");
23,410✔
37

38
   uint32_t x00 = input[ 0], x01 = input[ 1], x02 = input[ 2], x03 = input[ 3],
23,410✔
39
            x04 = input[ 4], x05 = input[ 5], x06 = input[ 6], x07 = input[ 7],
23,410✔
40
            x08 = input[ 8], x09 = input[ 9], x10 = input[10], x11 = input[11],
23,410✔
41
            x12 = input[12], x13 = input[13], x14 = input[14], x15 = input[15];
23,410✔
42

43
   for(size_t i = 0; i != rounds / 2; ++i)
257,510✔
44
      {
45
      chacha_quarter_round(x00, x04, x08, x12);
234,100✔
46
      chacha_quarter_round(x01, x05, x09, x13);
234,100✔
47
      chacha_quarter_round(x02, x06, x10, x14);
234,100✔
48
      chacha_quarter_round(x03, x07, x11, x15);
234,100✔
49

50
      chacha_quarter_round(x00, x05, x10, x15);
234,100✔
51
      chacha_quarter_round(x01, x06, x11, x12);
234,100✔
52
      chacha_quarter_round(x02, x07, x08, x13);
234,100✔
53
      chacha_quarter_round(x03, x04, x09, x14);
234,100✔
54
      }
55

56
   output[0] = x00;
23,410✔
57
   output[1] = x01;
23,410✔
58
   output[2] = x02;
23,410✔
59
   output[3] = x03;
23,410✔
60
   output[4] = x12;
23,410✔
61
   output[5] = x13;
23,410✔
62
   output[6] = x14;
23,410✔
63
   output[7] = x15;
23,410✔
64
   }
23,410✔
65

66
}
67

68
ChaCha::ChaCha(size_t rounds) : m_rounds(rounds)
16,090✔
69
   {
70
   BOTAN_ARG_CHECK(m_rounds == 8 || m_rounds == 12 || m_rounds == 20,
16,090✔
71
                   "ChaCha only supports 8, 12 or 20 rounds");
72
   }
16,090✔
73

74
size_t ChaCha::parallelism()
89,095✔
75
   {
76
#if defined(BOTAN_HAS_CHACHA_AVX512)
77
   if(CPUID::has_avx512())
89,095✔
78
      return 16;
79
#endif
80

81
#if defined(BOTAN_HAS_CHACHA_AVX2)
82
   if(CPUID::has_avx2())
89,095✔
83
      return 8;
88,445✔
84
#endif
85

86
   return 4;
87
   }
88

89
std::string ChaCha::provider() const
324✔
90
   {
91
#if defined(BOTAN_HAS_CHACHA_AVX512)
92
   if(CPUID::has_avx512())
324✔
93
      {
94
      return "avx512";
×
95
      }
96
#endif
97

98
#if defined(BOTAN_HAS_CHACHA_AVX2)
99
   if(CPUID::has_avx2())
324✔
100
      {
101
      return "avx2";
108✔
102
      }
103
#endif
104

105
#if defined(BOTAN_HAS_CHACHA_SIMD32)
106
   if(CPUID::has_simd_32())
216✔
107
      {
108
      return "simd32";
108✔
109
      }
110
#endif
111

112
   return "base";
108✔
113
   }
114

115
void ChaCha::chacha(uint8_t output[],
222,487✔
116
                    size_t output_blocks,
117
                    uint32_t state[16], size_t rounds)
118
   {
119
   BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds");
222,487✔
120

121
#if defined(BOTAN_HAS_CHACHA_AVX512)
122
   if(CPUID::has_avx512())
222,487✔
123
      {
124
      while(output_blocks >= 16)
×
125
         {
126
         ChaCha::chacha_avx512_x16(output, state, rounds);
×
127
         output += 16*64;
×
128
         output_blocks -= 16;
×
129
         }
130
      }
131
#endif
132

133
#if defined(BOTAN_HAS_CHACHA_AVX2)
134
   if(CPUID::has_avx2())
222,487✔
135
      {
136
      while(output_blocks >= 8)
440,310✔
137
         {
138
         ChaCha::chacha_avx2_x8(output, state, rounds);
220,155✔
139
         output += 8*64;
220,155✔
140
         output_blocks -= 8;
220,155✔
141
         }
142
      }
143
#endif
144

145
#if defined(BOTAN_HAS_CHACHA_SIMD32)
146
   if(CPUID::has_simd_32())
222,487✔
147
      {
148
      while(output_blocks >= 4)
222,487✔
149
         {
150
         ChaCha::chacha_simd32_x4(output, state, rounds);
1,166✔
151
         output += 4*64;
1,166✔
152
         output_blocks -= 4;
1,166✔
153
         }
154
      }
155
#endif
156

157
   // TODO interleave rounds
158
   for(size_t i = 0; i != output_blocks; ++i)
227,151✔
159
      {
160
      uint32_t x00 = state[ 0], x01 = state[ 1], x02 = state[ 2], x03 = state[ 3],
4,664✔
161
               x04 = state[ 4], x05 = state[ 5], x06 = state[ 6], x07 = state[ 7],
4,664✔
162
               x08 = state[ 8], x09 = state[ 9], x10 = state[10], x11 = state[11],
4,664✔
163
               x12 = state[12], x13 = state[13], x14 = state[14], x15 = state[15];
4,664✔
164

165
      for(size_t r = 0; r != rounds / 2; ++r)
47,576✔
166
         {
167
         chacha_quarter_round(x00, x04, x08, x12);
42,912✔
168
         chacha_quarter_round(x01, x05, x09, x13);
42,912✔
169
         chacha_quarter_round(x02, x06, x10, x14);
42,912✔
170
         chacha_quarter_round(x03, x07, x11, x15);
42,912✔
171

172
         chacha_quarter_round(x00, x05, x10, x15);
42,912✔
173
         chacha_quarter_round(x01, x06, x11, x12);
42,912✔
174
         chacha_quarter_round(x02, x07, x08, x13);
42,912✔
175
         chacha_quarter_round(x03, x04, x09, x14);
42,912✔
176
         }
177

178
      x00 += state[0];
4,664✔
179
      x01 += state[1];
4,664✔
180
      x02 += state[2];
4,664✔
181
      x03 += state[3];
4,664✔
182
      x04 += state[4];
4,664✔
183
      x05 += state[5];
4,664✔
184
      x06 += state[6];
4,664✔
185
      x07 += state[7];
4,664✔
186
      x08 += state[8];
4,664✔
187
      x09 += state[9];
4,664✔
188
      x10 += state[10];
4,664✔
189
      x11 += state[11];
4,664✔
190
      x12 += state[12];
4,664✔
191
      x13 += state[13];
4,664✔
192
      x14 += state[14];
4,664✔
193
      x15 += state[15];
4,664✔
194

195
      store_le(x00, output + 64 * i + 4 *  0);
4,664✔
196
      store_le(x01, output + 64 * i + 4 *  1);
4,664✔
197
      store_le(x02, output + 64 * i + 4 *  2);
4,664✔
198
      store_le(x03, output + 64 * i + 4 *  3);
4,664✔
199
      store_le(x04, output + 64 * i + 4 *  4);
4,664✔
200
      store_le(x05, output + 64 * i + 4 *  5);
4,664✔
201
      store_le(x06, output + 64 * i + 4 *  6);
4,664✔
202
      store_le(x07, output + 64 * i + 4 *  7);
4,664✔
203
      store_le(x08, output + 64 * i + 4 *  8);
4,664✔
204
      store_le(x09, output + 64 * i + 4 *  9);
4,664✔
205
      store_le(x10, output + 64 * i + 4 * 10);
4,664✔
206
      store_le(x11, output + 64 * i + 4 * 11);
4,664✔
207
      store_le(x12, output + 64 * i + 4 * 12);
4,664✔
208
      store_le(x13, output + 64 * i + 4 * 13);
4,664✔
209
      store_le(x14, output + 64 * i + 4 * 14);
4,664✔
210
      store_le(x15, output + 64 * i + 4 * 15);
4,664✔
211

212
      state[12]++;
4,664✔
213
      state[13] += (state[12] == 0);
4,664✔
214
      }
215
   }
222,487✔
216

217
/*
218
* Combine cipher stream with message
219
*/
220
void ChaCha::cipher_bytes(const uint8_t in[], uint8_t out[], size_t length)
288,104✔
221
   {
222
   assert_key_material_set();
288,104✔
223

224
   while(length >= m_buffer.size() - m_position)
575,978✔
225
      {
226
      const size_t available = m_buffer.size() - m_position;
5,143✔
227

228
      xor_buf(out, in, &m_buffer[m_position], available);
5,143✔
229
      chacha(m_buffer.data(), m_buffer.size() / 64,
5,143✔
230
             m_state.data(), m_rounds);
231

232
      length -= available;
5,143✔
233
      in += available;
5,143✔
234
      out += available;
5,143✔
235
      m_position = 0;
5,143✔
236
      }
237

238
   xor_buf(out, in, &m_buffer[m_position], length);
282,731✔
239

240
   m_position += length;
282,731✔
241
   }
282,731✔
242

243
void ChaCha::generate_keystream(uint8_t out[], size_t length)
157,252✔
244
   {
245
   assert_key_material_set();
157,252✔
246

247
   while(length >= m_buffer.size() - m_position)
332,181✔
248
      {
249
      const size_t available = m_buffer.size() - m_position;
17,677✔
250

251
      // TODO: this could write directly to the output buffer
252
      // instead of bouncing it through m_buffer first
253
      copy_mem(out, &m_buffer[m_position], available);
17,677✔
254
      chacha(m_buffer.data(), m_buffer.size() / 64,
17,677✔
255
             m_state.data(), m_rounds);
256

257
      length -= available;
17,677✔
258
      out += available;
17,677✔
259
      m_position = 0;
17,677✔
260
      }
261

262
   copy_mem(out, &m_buffer[m_position], length);
157,252✔
263

264
   m_position += length;
157,252✔
265
   }
157,252✔
266

267
void ChaCha::initialize_state()
198,776✔
268
   {
269
   static const uint32_t TAU[] =
198,776✔
270
      { 0x61707865, 0x3120646e, 0x79622d36, 0x6b206574 };
271

272
   static const uint32_t SIGMA[] =
198,776✔
273
      { 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 };
274

275
   m_state[4] = m_key[0];
198,776✔
276
   m_state[5] = m_key[1];
198,776✔
277
   m_state[6] = m_key[2];
198,776✔
278
   m_state[7] = m_key[3];
198,776✔
279

280
   if(m_key.size() == 4)
198,776✔
281
      {
282
      m_state[0] = TAU[0];
210✔
283
      m_state[1] = TAU[1];
210✔
284
      m_state[2] = TAU[2];
210✔
285
      m_state[3] = TAU[3];
210✔
286

287
      m_state[8] = m_key[0];
210✔
288
      m_state[9] = m_key[1];
210✔
289
      m_state[10] = m_key[2];
210✔
290
      m_state[11] = m_key[3];
210✔
291
      }
292
   else
293
      {
294
      m_state[0] = SIGMA[0];
198,566✔
295
      m_state[1] = SIGMA[1];
198,566✔
296
      m_state[2] = SIGMA[2];
198,566✔
297
      m_state[3] = SIGMA[3];
198,566✔
298

299
      m_state[8] = m_key[4];
198,566✔
300
      m_state[9] = m_key[5];
198,566✔
301
      m_state[10] = m_key[6];
198,566✔
302
      m_state[11] = m_key[7];
198,566✔
303
      }
304

305
   m_state[12] = 0;
198,776✔
306
   m_state[13] = 0;
198,776✔
307
   m_state[14] = 0;
198,776✔
308
   m_state[15] = 0;
198,776✔
309

310
   m_position = 0;
198,776✔
311
   }
198,776✔
312

313
bool ChaCha::has_keying_material() const
658,313✔
314
   {
315
   return !m_state.empty();
658,313✔
316
   }
317

318
size_t ChaCha::buffer_size() const
324✔
319
   {
320
   return 64;
324✔
321
   }
322

323
/*
324
* ChaCha Key Schedule
325
*/
326
void ChaCha::key_schedule(const uint8_t key[], size_t length)
89,095✔
327
   {
328
   m_key.resize(length / 4);
89,095✔
329
   load_le<uint32_t>(m_key.data(), key, m_key.size());
89,095✔
330

331
   m_state.resize(16);
89,095✔
332

333
   const size_t chacha_block = 64;
89,095✔
334
   m_buffer.resize(parallelism() * chacha_block);
89,095✔
335

336
   set_iv(nullptr, 0);
89,095✔
337
   }
89,095✔
338

339
size_t ChaCha::default_iv_length() const
648✔
340
   {
341
   return 24;
648✔
342
   }
343

344
Key_Length_Specification ChaCha::key_spec() const
89,744✔
345
   {
346
   return Key_Length_Specification(16, 32, 16);
89,744✔
347
   }
348

349
std::unique_ptr<StreamCipher> ChaCha::new_object() const
324✔
350
   {
351
   return std::make_unique<ChaCha>(m_rounds);
324✔
352
   }
353

354
bool ChaCha::valid_iv_length(size_t iv_len) const
200,073✔
355
   {
356
   return (iv_len == 0 || iv_len == 8 || iv_len == 12 || iv_len == 24);
973✔
357
   }
358

359
void ChaCha::set_iv_bytes(const uint8_t iv[], size_t length)
199,421✔
360
   {
361
   assert_key_material_set();
199,421✔
362

363
   if(!valid_iv_length(length))
199,100✔
364
      throw Invalid_IV_Length(name(), length);
648✔
365

366
   initialize_state();
198,776✔
367

368
   if(length == 0)
198,776✔
369
      {
370
      // Treat zero length IV same as an all-zero IV
371
      m_state[14] = 0;
89,098✔
372
      m_state[15] = 0;
89,098✔
373
      }
374
   else if(length == 8)
109,678✔
375
      {
376
      m_state[14] = load_le<uint32_t>(iv, 0);
310✔
377
      m_state[15] = load_le<uint32_t>(iv, 1);
310✔
378
      }
379
   else if(length == 12)
109,368✔
380
      {
381
      m_state[13] = load_le<uint32_t>(iv, 0);
85,958✔
382
      m_state[14] = load_le<uint32_t>(iv, 1);
85,958✔
383
      m_state[15] = load_le<uint32_t>(iv, 2);
85,958✔
384
      }
385
   else if(length == 24)
23,410✔
386
      {
387
      m_state[12] = load_le<uint32_t>(iv, 0);
23,410✔
388
      m_state[13] = load_le<uint32_t>(iv, 1);
23,410✔
389
      m_state[14] = load_le<uint32_t>(iv, 2);
23,410✔
390
      m_state[15] = load_le<uint32_t>(iv, 3);
23,410✔
391

392
      secure_vector<uint32_t> hc(8);
23,410✔
393
      hchacha(hc.data(), m_state.data(), m_rounds);
23,410✔
394

395
      m_state[ 4] = hc[0];
23,410✔
396
      m_state[ 5] = hc[1];
23,410✔
397
      m_state[ 6] = hc[2];
23,410✔
398
      m_state[ 7] = hc[3];
23,410✔
399
      m_state[ 8] = hc[4];
23,410✔
400
      m_state[ 9] = hc[5];
23,410✔
401
      m_state[10] = hc[6];
23,410✔
402
      m_state[11] = hc[7];
23,410✔
403
      m_state[12] = 0;
23,410✔
404
      m_state[13] = 0;
23,410✔
405
      m_state[14] = load_le<uint32_t>(iv, 4);
23,410✔
406
      m_state[15] = load_le<uint32_t>(iv, 5);
23,410✔
407
      }
23,410✔
408

409
   chacha(m_buffer.data(), m_buffer.size() / 64,
198,776✔
410
          m_state.data(), m_rounds);
411
   m_position = 0;
198,776✔
412
   }
198,776✔
413

414
void ChaCha::clear()
3,474✔
415
   {
416
   zap(m_key);
3,474✔
417
   zap(m_state);
3,474✔
418
   zap(m_buffer);
3,474✔
419
   m_position = 0;
3,474✔
420
   }
3,474✔
421

422
std::string ChaCha::name() const
7,315✔
423
   {
424
   return fmt("ChaCha({})", m_rounds);
7,315✔
425
   }
426

427
void ChaCha::seek(uint64_t offset)
1,215✔
428
   {
429
   assert_key_material_set();
1,215✔
430

431
   // Find the block offset
432
   const uint64_t counter = offset / 64;
891✔
433

434
   uint8_t out[8];
891✔
435

436
   store_le(counter, out);
891✔
437

438
   m_state[12] = load_le<uint32_t>(out, 0);
891✔
439
   m_state[13] += load_le<uint32_t>(out, 1);
891✔
440

441
   chacha(m_buffer.data(), m_buffer.size() / 64, m_state.data(), m_rounds);
891✔
442
   m_position = offset % 64;
891✔
443
   }
891✔
444
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc