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

randombit / botan / 5123321399

30 May 2023 04:06PM UTC coverage: 92.213% (+0.004%) from 92.209%
5123321399

Pull #3558

github

web-flow
Merge dd72f7389 into 057bcbc35
Pull Request #3558: Add braces around all if/else statements

75602 of 81986 relevant lines covered (92.21%)

11859779.3 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/cpuid.h>
12
#include <botan/internal/fmt.h>
13
#include <botan/internal/loadstor.h>
14
#include <botan/internal/rotate.h>
15

16
namespace Botan {
17

18
namespace {
19

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

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

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

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

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

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

67
}  // namespace
68

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

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

80
#if defined(BOTAN_HAS_CHACHA_AVX2)
81
   if(CPUID::has_avx2()) {
89,109✔
82
      return 8;
88,459✔
83
   }
84
#endif
85

86
   return 4;
87
}
88

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

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

102
#if defined(BOTAN_HAS_CHACHA_SIMD32)
103
   if(CPUID::has_simd_32()) {
216✔
104
      return "simd32";
108✔
105
   }
106
#endif
107

108
   return "base";
108✔
109
}
110

111
void ChaCha::chacha(uint8_t output[], size_t output_blocks, uint32_t state[16], size_t rounds) {
222,492✔
112
   BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds");
222,492✔
113

114
#if defined(BOTAN_HAS_CHACHA_AVX512)
115
   if(CPUID::has_avx512()) {
222,492✔
116
      while(output_blocks >= 16) {
×
117
         ChaCha::chacha_avx512_x16(output, state, rounds);
×
118
         output += 16 * 64;
×
119
         output_blocks -= 16;
×
120
      }
121
   }
122
#endif
123

124
#if defined(BOTAN_HAS_CHACHA_AVX2)
125
   if(CPUID::has_avx2()) {
222,492✔
126
      while(output_blocks >= 8) {
440,320✔
127
         ChaCha::chacha_avx2_x8(output, state, rounds);
220,160✔
128
         output += 8 * 64;
220,160✔
129
         output_blocks -= 8;
220,160✔
130
      }
131
   }
132
#endif
133

134
#if defined(BOTAN_HAS_CHACHA_SIMD32)
135
   if(CPUID::has_simd_32()) {
222,492✔
136
      while(output_blocks >= 4) {
222,492✔
137
         ChaCha::chacha_simd32_x4(output, state, rounds);
1,166✔
138
         output += 4 * 64;
1,166✔
139
         output_blocks -= 4;
1,166✔
140
      }
141
   }
142
#endif
143

144
   // TODO interleave rounds
145
   for(size_t i = 0; i != output_blocks; ++i) {
227,156✔
146
      uint32_t x00 = state[0], x01 = state[1], x02 = state[2], x03 = state[3], x04 = state[4], x05 = state[5],
4,664✔
147
               x06 = state[6], x07 = state[7], x08 = state[8], x09 = state[9], x10 = state[10], x11 = state[11],
4,664✔
148
               x12 = state[12], x13 = state[13], x14 = state[14], x15 = state[15];
4,664✔
149

150
      for(size_t r = 0; r != rounds / 2; ++r) {
47,576✔
151
         chacha_quarter_round(x00, x04, x08, x12);
42,912✔
152
         chacha_quarter_round(x01, x05, x09, x13);
42,912✔
153
         chacha_quarter_round(x02, x06, x10, x14);
42,912✔
154
         chacha_quarter_round(x03, x07, x11, x15);
42,912✔
155

156
         chacha_quarter_round(x00, x05, x10, x15);
42,912✔
157
         chacha_quarter_round(x01, x06, x11, x12);
42,912✔
158
         chacha_quarter_round(x02, x07, x08, x13);
42,912✔
159
         chacha_quarter_round(x03, x04, x09, x14);
42,912✔
160
      }
161

162
      x00 += state[0];
4,664✔
163
      x01 += state[1];
4,664✔
164
      x02 += state[2];
4,664✔
165
      x03 += state[3];
4,664✔
166
      x04 += state[4];
4,664✔
167
      x05 += state[5];
4,664✔
168
      x06 += state[6];
4,664✔
169
      x07 += state[7];
4,664✔
170
      x08 += state[8];
4,664✔
171
      x09 += state[9];
4,664✔
172
      x10 += state[10];
4,664✔
173
      x11 += state[11];
4,664✔
174
      x12 += state[12];
4,664✔
175
      x13 += state[13];
4,664✔
176
      x14 += state[14];
4,664✔
177
      x15 += state[15];
4,664✔
178

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

196
      state[12]++;
4,664✔
197
      state[13] += (state[12] == 0);
4,664✔
198
   }
199
}
222,492✔
200

201
/*
202
* Combine cipher stream with message
203
*/
204
void ChaCha::cipher_bytes(const uint8_t in[], uint8_t out[], size_t length) {
288,089✔
205
   assert_key_material_set();
288,089✔
206

207
   while(length >= m_buffer.size() - m_position) {
575,873✔
208
      const size_t available = m_buffer.size() - m_position;
5,068✔
209

210
      xor_buf(out, in, &m_buffer[m_position], available);
5,068✔
211
      chacha(m_buffer.data(), m_buffer.size() / 64, m_state.data(), m_rounds);
5,068✔
212

213
      length -= available;
5,068✔
214
      in += available;
5,068✔
215
      out += available;
5,068✔
216
      m_position = 0;
5,068✔
217
   }
218

219
   xor_buf(out, in, &m_buffer[m_position], length);
282,716✔
220

221
   m_position += length;
282,716✔
222
}
282,716✔
223

224
void ChaCha::generate_keystream(uint8_t out[], size_t length) {
157,515✔
225
   assert_key_material_set();
157,515✔
226

227
   while(length >= m_buffer.size() - m_position) {
332,736✔
228
      const size_t available = m_buffer.size() - m_position;
17,706✔
229

230
      // TODO: this could write directly to the output buffer
231
      // instead of bouncing it through m_buffer first
232
      copy_mem(out, &m_buffer[m_position], available);
17,706✔
233
      chacha(m_buffer.data(), m_buffer.size() / 64, m_state.data(), m_rounds);
17,706✔
234

235
      length -= available;
17,706✔
236
      out += available;
17,706✔
237
      m_position = 0;
17,706✔
238
   }
239

240
   copy_mem(out, &m_buffer[m_position], length);
157,515✔
241

242
   m_position += length;
157,515✔
243
}
157,515✔
244

245
void ChaCha::initialize_state() {
198,827✔
246
   static const uint32_t TAU[] = {0x61707865, 0x3120646e, 0x79622d36, 0x6b206574};
198,827✔
247

248
   static const uint32_t SIGMA[] = {0x61707865, 0x3320646e, 0x79622d32, 0x6b206574};
198,827✔
249

250
   m_state[4] = m_key[0];
198,827✔
251
   m_state[5] = m_key[1];
198,827✔
252
   m_state[6] = m_key[2];
198,827✔
253
   m_state[7] = m_key[3];
198,827✔
254

255
   if(m_key.size() == 4) {
198,827✔
256
      m_state[0] = TAU[0];
210✔
257
      m_state[1] = TAU[1];
210✔
258
      m_state[2] = TAU[2];
210✔
259
      m_state[3] = TAU[3];
210✔
260

261
      m_state[8] = m_key[0];
210✔
262
      m_state[9] = m_key[1];
210✔
263
      m_state[10] = m_key[2];
210✔
264
      m_state[11] = m_key[3];
210✔
265
   } else {
266
      m_state[0] = SIGMA[0];
198,617✔
267
      m_state[1] = SIGMA[1];
198,617✔
268
      m_state[2] = SIGMA[2];
198,617✔
269
      m_state[3] = SIGMA[3];
198,617✔
270

271
      m_state[8] = m_key[4];
198,617✔
272
      m_state[9] = m_key[5];
198,617✔
273
      m_state[10] = m_key[6];
198,617✔
274
      m_state[11] = m_key[7];
198,617✔
275
   }
276

277
   m_state[12] = 0;
198,827✔
278
   m_state[13] = 0;
198,827✔
279
   m_state[14] = 0;
198,827✔
280
   m_state[15] = 0;
198,827✔
281

282
   m_position = 0;
198,827✔
283
}
198,827✔
284

285
bool ChaCha::has_keying_material() const { return !m_state.empty(); }
658,612✔
286

287
size_t ChaCha::buffer_size() const { return 64; }
324✔
288

289
/*
290
* ChaCha Key Schedule
291
*/
292
void ChaCha::key_schedule(const uint8_t key[], size_t length) {
89,109✔
293
   m_key.resize(length / 4);
89,109✔
294
   load_le<uint32_t>(m_key.data(), key, m_key.size());
89,109✔
295

296
   m_state.resize(16);
89,109✔
297

298
   const size_t chacha_block = 64;
89,109✔
299
   m_buffer.resize(parallelism() * chacha_block);
89,109✔
300

301
   set_iv(nullptr, 0);
89,109✔
302
}
89,109✔
303

304
size_t ChaCha::default_iv_length() const { return 24; }
648✔
305

306
Key_Length_Specification ChaCha::key_spec() const { return Key_Length_Specification(16, 32, 16); }
89,758✔
307

308
std::unique_ptr<StreamCipher> ChaCha::new_object() const { return std::make_unique<ChaCha>(m_rounds); }
324✔
309

310
bool ChaCha::valid_iv_length(size_t iv_len) const {
200,124✔
311
   return (iv_len == 0 || iv_len == 8 || iv_len == 12 || iv_len == 24);
973✔
312
}
313

314
void ChaCha::set_iv_bytes(const uint8_t iv[], size_t length) {
199,472✔
315
   assert_key_material_set();
199,472✔
316

317
   if(!valid_iv_length(length)) {
199,151✔
318
      throw Invalid_IV_Length(name(), length);
648✔
319
   }
320

321
   initialize_state();
198,827✔
322

323
   if(length == 0) {
198,827✔
324
      // Treat zero length IV same as an all-zero IV
325
      m_state[14] = 0;
89,112✔
326
      m_state[15] = 0;
89,112✔
327
   } else if(length == 8) {
109,715✔
328
      m_state[14] = load_le<uint32_t>(iv, 0);
310✔
329
      m_state[15] = load_le<uint32_t>(iv, 1);
310✔
330
   } else if(length == 12) {
109,405✔
331
      m_state[13] = load_le<uint32_t>(iv, 0);
85,995✔
332
      m_state[14] = load_le<uint32_t>(iv, 1);
85,995✔
333
      m_state[15] = load_le<uint32_t>(iv, 2);
85,995✔
334
   } else if(length == 24) {
23,410✔
335
      m_state[12] = load_le<uint32_t>(iv, 0);
23,410✔
336
      m_state[13] = load_le<uint32_t>(iv, 1);
23,410✔
337
      m_state[14] = load_le<uint32_t>(iv, 2);
23,410✔
338
      m_state[15] = load_le<uint32_t>(iv, 3);
23,410✔
339

340
      secure_vector<uint32_t> hc(8);
23,410✔
341
      hchacha(hc.data(), m_state.data(), m_rounds);
23,410✔
342

343
      m_state[4] = hc[0];
23,410✔
344
      m_state[5] = hc[1];
23,410✔
345
      m_state[6] = hc[2];
23,410✔
346
      m_state[7] = hc[3];
23,410✔
347
      m_state[8] = hc[4];
23,410✔
348
      m_state[9] = hc[5];
23,410✔
349
      m_state[10] = hc[6];
23,410✔
350
      m_state[11] = hc[7];
23,410✔
351
      m_state[12] = 0;
23,410✔
352
      m_state[13] = 0;
23,410✔
353
      m_state[14] = load_le<uint32_t>(iv, 4);
23,410✔
354
      m_state[15] = load_le<uint32_t>(iv, 5);
23,410✔
355
   }
23,410✔
356

357
   chacha(m_buffer.data(), m_buffer.size() / 64, m_state.data(), m_rounds);
198,827✔
358
   m_position = 0;
198,827✔
359
}
198,827✔
360

361
void ChaCha::clear() {
3,474✔
362
   zap(m_key);
3,474✔
363
   zap(m_state);
3,474✔
364
   zap(m_buffer);
3,474✔
365
   m_position = 0;
3,474✔
366
}
3,474✔
367

368
std::string ChaCha::name() const { return fmt("ChaCha({})", m_rounds); }
7,315✔
369

370
void ChaCha::seek(uint64_t offset) {
1,215✔
371
   assert_key_material_set();
1,215✔
372

373
   // Find the block offset
374
   const uint64_t counter = offset / 64;
891✔
375

376
   uint8_t out[8];
891✔
377

378
   store_le(counter, out);
891✔
379

380
   m_state[12] = load_le<uint32_t>(out, 0);
891✔
381
   m_state[13] += load_le<uint32_t>(out, 1);
891✔
382

383
   chacha(m_buffer.data(), m_buffer.size() / 64, m_state.data(), m_rounds);
891✔
384
   m_position = offset % 64;
891✔
385
}
891✔
386
}  // namespace Botan
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