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

randombit / botan / 11087146043

28 Sep 2024 09:28PM UTC coverage: 92.003% (+0.7%) from 91.274%
11087146043

push

github

web-flow
Create terraform.yml

82959 of 90170 relevant lines covered (92.0%)

9376319.11 hits per line

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

99.26
/src/lib/modes/aead/ocb/ocb.cpp
1
/*
2
* OCB Mode
3
* (C) 2013,2017 Jack Lloyd
4
* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
5
*
6
* Botan is released under the Simplified BSD License (see license.txt)
7
*/
8

9
#include <botan/internal/ocb.h>
10

11
#include <botan/block_cipher.h>
12
#include <botan/internal/bit_ops.h>
13
#include <botan/internal/ct_utils.h>
14
#include <botan/internal/poly_dbl.h>
15

16
namespace Botan {
17

18
// Has to be in Botan namespace so unique_ptr can reference it
19
class L_computer final {
20
   public:
21
      explicit L_computer(const BlockCipher& cipher) :
245✔
22
            m_BS(cipher.block_size()), m_max_blocks(cipher.parallel_bytes() / m_BS) {
490✔
23
         m_L_star.resize(m_BS);
245✔
24
         cipher.encrypt(m_L_star);
245✔
25
         m_L_dollar = poly_double(star());
245✔
26

27
         // Preallocate the m_L vector to the maximum expected size to avoid
28
         // re-allocations during runtime. This had caused a use-after-free in
29
         // earlier versions, due to references into this buffer becoming stale
30
         // in `compute_offset()`, after calling `get()` in the hot path.
31
         //
32
         // Note, that the list member won't be pre-allocated, so the expected
33
         // memory overhead is negligible.
34
         //
35
         // See also https://github.com/randombit/botan/issues/3812
36
         m_L.reserve(31);
245✔
37
         m_L.push_back(poly_double(dollar()));
245✔
38

39
         while(m_L.size() < 8) {
1,960✔
40
            m_L.push_back(poly_double(m_L.back()));
3,430✔
41
         }
42

43
         m_offset_buf.resize(m_BS * m_max_blocks);
245✔
44
      }
245✔
45

46
      void init(const secure_vector<uint8_t>& offset) { m_offset = offset; }
9,449✔
47

48
      bool initialized() const { return m_offset.empty() == false; }
16,589✔
49

50
      const secure_vector<uint8_t>& star() const { return m_L_star; }
6,068✔
51

52
      const secure_vector<uint8_t>& dollar() const { return m_L_dollar; }
9,602✔
53

54
      const secure_vector<uint8_t>& offset() const { return m_offset; }
9,357✔
55

56
      const secure_vector<uint8_t>& get(size_t i) const {
121,741✔
57
         while(m_L.size() <= i) {
121,828✔
58
            m_L.push_back(poly_double(m_L.back()));
87✔
59
         }
60

61
         return m_L[i];
121,741✔
62
      }
63

64
      const uint8_t* compute_offsets(size_t block_index, size_t blocks) {
14,623✔
65
         BOTAN_ASSERT(blocks <= m_max_blocks, "OCB offsets");
14,623✔
66

67
         uint8_t* offsets = m_offset_buf.data();
14,623✔
68

69
         if(block_index % 4 == 0) {
14,623✔
70
            const secure_vector<uint8_t>& L0 = get(0);
14,169✔
71
            const secure_vector<uint8_t>& L1 = get(1);
14,169✔
72

73
            while(blocks >= 4) {
65,095✔
74
               // ntz(4*i+1) == 0
75
               // ntz(4*i+2) == 1
76
               // ntz(4*i+3) == 0
77
               block_index += 4;
36,757✔
78
               const size_t ntz4 = var_ctz32(static_cast<uint32_t>(block_index));
36,757✔
79

80
               xor_buf(offsets, m_offset.data(), L0.data(), m_BS);
36,757✔
81
               offsets += m_BS;
36,757✔
82

83
               xor_buf(offsets, offsets - m_BS, L1.data(), m_BS);
36,757✔
84
               offsets += m_BS;
36,757✔
85

86
               xor_buf(m_offset.data(), L1.data(), m_BS);
36,757✔
87
               copy_mem(offsets, m_offset.data(), m_BS);
36,757✔
88
               offsets += m_BS;
36,757✔
89

90
               xor_buf(m_offset.data(), get(ntz4).data(), m_BS);
36,757✔
91
               copy_mem(offsets, m_offset.data(), m_BS);
36,757✔
92
               offsets += m_BS;
36,757✔
93

94
               blocks -= 4;
36,757✔
95
            }
96
         }
97

98
         for(size_t i = 0; i != blocks; ++i) {  // could be done in parallel
24,239✔
99
            const size_t ntz = var_ctz32(static_cast<uint32_t>(block_index + i + 1));
9,616✔
100
            xor_buf(m_offset.data(), get(ntz).data(), m_BS);
9,616✔
101
            copy_mem(offsets, m_offset.data(), m_BS);
9,616✔
102
            offsets += m_BS;
9,616✔
103
         }
104

105
         return m_offset_buf.data();
14,623✔
106
      }
107

108
   private:
109
      static secure_vector<uint8_t> poly_double(const secure_vector<uint8_t>& in) {
2,292✔
110
         secure_vector<uint8_t> out(in.size());
2,292✔
111
         poly_double_n(out.data(), in.data(), out.size());
2,292✔
112
         return out;
2,292✔
113
      }
×
114

115
      const size_t m_BS, m_max_blocks;
116
      secure_vector<uint8_t> m_L_dollar, m_L_star;
117
      secure_vector<uint8_t> m_offset;
118
      mutable std::vector<secure_vector<uint8_t>> m_L;
119
      secure_vector<uint8_t> m_offset_buf;
120
};
121

122
namespace {
123

124
/*
125
* OCB's HASH
126
*/
127
secure_vector<uint8_t> ocb_hash(const L_computer& L, const BlockCipher& cipher, const uint8_t ad[], size_t ad_len) {
9,497✔
128
   const size_t BS = cipher.block_size();
9,497✔
129
   secure_vector<uint8_t> sum(BS);
9,497✔
130
   secure_vector<uint8_t> offset(BS);
9,497✔
131

132
   secure_vector<uint8_t> buf(BS);
9,497✔
133

134
   const size_t ad_blocks = (ad_len / BS);
9,497✔
135
   const size_t ad_remainder = (ad_len % BS);
9,497✔
136

137
   for(size_t i = 0; i != ad_blocks; ++i) {
56,527✔
138
      // this loop could run in parallel
139
      offset ^= L.get(var_ctz32(static_cast<uint32_t>(i + 1)));
94,060✔
140
      buf = offset;
47,030✔
141
      xor_buf(buf.data(), &ad[BS * i], BS);
47,030✔
142
      cipher.encrypt(buf);
47,030✔
143
      sum ^= buf;
47,030✔
144
   }
145

146
   if(ad_remainder) {
9,497✔
147
      offset ^= L.star();
5,867✔
148
      buf = offset;
5,867✔
149
      xor_buf(buf.data(), &ad[BS * ad_blocks], ad_remainder);
5,867✔
150
      buf[ad_remainder] ^= 0x80;
5,867✔
151
      cipher.encrypt(buf);
5,867✔
152
      sum ^= buf;
5,867✔
153
   }
154

155
   return sum;
9,497✔
156
}
18,994✔
157

158
}  // namespace
159

160
OCB_Mode::OCB_Mode(std::unique_ptr<BlockCipher> cipher, size_t tag_size) :
337✔
161
      m_cipher(std::move(cipher)),
337✔
162
      m_checksum(m_cipher->parallel_bytes()),
674✔
163
      m_ad_hash(m_cipher->block_size()),
337✔
164
      m_tag_size(tag_size),
337✔
165
      m_block_size(m_cipher->block_size()),
337✔
166
      m_par_blocks(m_cipher->parallel_bytes() / m_block_size) {
1,011✔
167
   const size_t BS = block_size();
337✔
168

169
   /*
170
   * draft-krovetz-ocb-wide-d1 specifies OCB for several other block
171
   * sizes but only 128, 192, 256 and 512 bit are currently supported
172
   * by this implementation.
173
   */
174
   BOTAN_ARG_CHECK(BS == 16 || BS == 24 || BS == 32 || BS == 64, "Invalid block size for OCB");
337✔
175

176
   BOTAN_ARG_CHECK(m_tag_size % 4 == 0 && m_tag_size >= 8 && m_tag_size <= BS && m_tag_size <= 32,
337✔
177
                   "Invalid OCB tag length");
178
}
337✔
179

180
OCB_Mode::~OCB_Mode() = default;
1,991✔
181

182
void OCB_Mode::clear() {
92✔
183
   m_cipher->clear();
92✔
184
   m_L.reset();  // add clear here?
92✔
185
   reset();
92✔
186
}
92✔
187

188
void OCB_Mode::reset() {
410✔
189
   m_block_index = 0;
410✔
190
   zeroise(m_ad_hash);
410✔
191
   zeroise(m_checksum);
410✔
192
   m_last_nonce.clear();
410✔
193
   m_stretch.clear();
410✔
194
}
410✔
195

196
bool OCB_Mode::valid_nonce_length(size_t length) const {
9,495✔
197
   if(length == 0) {
9,495✔
198
      return false;
199
   }
200
   if(block_size() == 16) {
9,495✔
201
      return length < 16;
7,893✔
202
   } else {
203
      return length < (block_size() - 1);
1,602✔
204
   }
205
}
206

207
std::string OCB_Mode::name() const {
506✔
208
   return m_cipher->name() + "/OCB";  // include tag size?
1,012✔
209
}
210

211
size_t OCB_Mode::update_granularity() const {
1,321✔
212
   return block_size();
1,321✔
213
}
214

215
size_t OCB_Mode::ideal_granularity() const {
276✔
216
   return (m_par_blocks * block_size());
276✔
217
}
218

219
Key_Length_Specification OCB_Mode::key_spec() const {
245✔
220
   return m_cipher->key_spec();
245✔
221
}
222

223
bool OCB_Mode::has_keying_material() const {
36,317✔
224
   return m_cipher->has_keying_material();
36,317✔
225
}
226

227
void OCB_Mode::key_schedule(std::span<const uint8_t> key) {
245✔
228
   m_cipher->set_key(key);
245✔
229
   m_L = std::make_unique<L_computer>(*m_cipher);
245✔
230
}
245✔
231

232
void OCB_Mode::set_associated_data_n(size_t idx, std::span<const uint8_t> ad) {
9,681✔
233
   BOTAN_ARG_CHECK(idx == 0, "OCB: cannot handle non-zero index in set_associated_data_n");
9,681✔
234
   assert_key_material_set();
9,681✔
235
   m_ad_hash = ocb_hash(*m_L, *m_cipher, ad.data(), ad.size());
9,497✔
236
}
9,497✔
237

238
const secure_vector<uint8_t>& OCB_Mode::update_nonce(const uint8_t nonce[], size_t nonce_len) {
9,449✔
239
   const size_t BS = block_size();
9,449✔
240

241
   BOTAN_ASSERT(BS == 16 || BS == 24 || BS == 32 || BS == 64, "OCB block size is supported");
9,449✔
242

243
   const size_t MASKLEN = (BS == 16 ? 6 : ((BS == 24) ? 7 : 8));
9,449✔
244

245
   const uint8_t BOTTOM_MASK = static_cast<uint8_t>((static_cast<uint16_t>(1) << MASKLEN) - 1);
9,449✔
246

247
   m_nonce_buf.resize(BS);
9,449✔
248
   clear_mem(&m_nonce_buf[0], m_nonce_buf.size());
9,449✔
249

250
   copy_mem(&m_nonce_buf[BS - nonce_len], nonce, nonce_len);
9,449✔
251
   m_nonce_buf[0] = static_cast<uint8_t>(((tag_size() * 8) % (BS * 8)) << (BS <= 16 ? 1 : 0));
11,047✔
252

253
   m_nonce_buf[BS - nonce_len - 1] ^= 1;
9,449✔
254

255
   const uint8_t bottom = m_nonce_buf[BS - 1] & BOTTOM_MASK;
9,449✔
256
   m_nonce_buf[BS - 1] &= ~BOTTOM_MASK;
9,449✔
257

258
   const bool need_new_stretch = (m_last_nonce != m_nonce_buf);
9,449✔
259

260
   if(need_new_stretch) {
9,449✔
261
      m_last_nonce = m_nonce_buf;
683✔
262

263
      m_cipher->encrypt(m_nonce_buf);
683✔
264

265
      /*
266
      The loop bounds (BS vs BS/2) are derived from the relation
267
      between the block size and the MASKLEN. Using the terminology
268
      of draft-krovetz-ocb-wide, we have to derive enough bits in
269
      ShiftedKtop to read up to BLOCKLEN+bottom bits from Stretch.
270

271
                 +----------+---------+-------+---------+
272
                 | BLOCKLEN | RESIDUE | SHIFT | MASKLEN |
273
                 +----------+---------+-------+---------+
274
                 |       32 |     141 |    17 |    4    |
275
                 |       64 |      27 |    25 |    5    |
276
                 |       96 |    1601 |    33 |    6    |
277
                 |      128 |     135 |     8 |    6    |
278
                 |      192 |     135 |    40 |    7    |
279
                 |      256 |    1061 |     1 |    8    |
280
                 |      384 |    4109 |    80 |    8    |
281
                 |      512 |     293 |   176 |    8    |
282
                 |     1024 |  524355 |   352 |    9    |
283
                 +----------+---------+-------+---------+
284
      */
285
      if(BS == 16) {
683✔
286
         for(size_t i = 0; i != BS / 2; ++i) {
5,535✔
287
            m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i + 1]);
4,920✔
288
         }
289
      } else if(BS == 24) {
68✔
290
         for(size_t i = 0; i != 16; ++i) {
408✔
291
            m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i + 5]);
384✔
292
         }
293
      } else if(BS == 32) {
44✔
294
         for(size_t i = 0; i != BS; ++i) {
1,089✔
295
            m_nonce_buf.push_back(m_nonce_buf[i] ^ (m_nonce_buf[i] << 1) ^ (m_nonce_buf[i + 1] >> 7));
1,056✔
296
         }
297
      } else if(BS == 64) {
298
         for(size_t i = 0; i != BS / 2; ++i) {
363✔
299
            m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i + 22]);
352✔
300
         }
301
      }
302

303
      m_stretch = m_nonce_buf;
683✔
304
   }
305

306
   // now set the offset from stretch and bottom
307
   const size_t shift_bytes = bottom / 8;
9,449✔
308
   const size_t shift_bits = bottom % 8;
9,449✔
309

310
   BOTAN_ASSERT(m_stretch.size() >= BS + shift_bytes + 1, "Size ok");
9,449✔
311

312
   m_offset.resize(BS);
9,449✔
313
   for(size_t i = 0; i != BS; ++i) {
195,569✔
314
      m_offset[i] = (m_stretch[i + shift_bytes] << shift_bits);
186,120✔
315
      m_offset[i] |= (m_stretch[i + shift_bytes + 1] >> (8 - shift_bits));
186,120✔
316
   }
317

318
   return m_offset;
9,449✔
319
}
320

321
void OCB_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
9,449✔
322
   if(!valid_nonce_length(nonce_len)) {
9,449✔
323
      throw Invalid_IV_Length(name(), nonce_len);
×
324
   }
325

326
   assert_key_material_set();
9,449✔
327

328
   m_L->init(update_nonce(nonce, nonce_len));
9,449✔
329
   zeroise(m_checksum);
9,449✔
330
   m_block_index = 0;
9,449✔
331
}
9,449✔
332

333
void OCB_Encryption::encrypt(uint8_t buffer[], size_t blocks) {
4,195✔
334
   assert_key_material_set();
4,195✔
335
   BOTAN_STATE_CHECK(m_L->initialized());
4,149✔
336

337
   const size_t BS = block_size();
4,103✔
338

339
   while(blocks) {
11,814✔
340
      const size_t proc_blocks = std::min(blocks, par_blocks());
7,711✔
341
      const size_t proc_bytes = proc_blocks * BS;
7,711✔
342

343
      const uint8_t* offsets = m_L->compute_offsets(m_block_index, proc_blocks);
7,711✔
344

345
      xor_buf(m_checksum.data(), buffer, proc_bytes);
7,711✔
346

347
      m_cipher->encrypt_n_xex(buffer, offsets, proc_blocks);
7,711✔
348

349
      buffer += proc_bytes;
7,711✔
350
      blocks -= proc_blocks;
7,711✔
351
      m_block_index += proc_blocks;
7,711✔
352
   }
353
}
4,103✔
354

355
size_t OCB_Encryption::process_msg(uint8_t buf[], size_t sz) {
500✔
356
   BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid OCB input size");
500✔
357
   encrypt(buf, sz / block_size());
500✔
358
   return sz;
408✔
359
}
360

361
void OCB_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
5,704✔
362
   assert_key_material_set();
5,704✔
363
   BOTAN_STATE_CHECK(m_L->initialized());
5,612✔
364

365
   const size_t BS = block_size();
5,566✔
366

367
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
5,566✔
368
   const size_t sz = buffer.size() - offset;
5,566✔
369
   uint8_t* buf = buffer.data() + offset;
5,566✔
370

371
   secure_vector<uint8_t> mac(BS);
5,566✔
372

373
   if(sz) {
5,566✔
374
      const size_t final_full_blocks = sz / BS;
3,695✔
375
      const size_t remainder_bytes = sz - (final_full_blocks * BS);
3,695✔
376

377
      encrypt(buf, final_full_blocks);
3,695✔
378
      mac = m_L->offset();
3,695✔
379

380
      if(remainder_bytes) {
3,695✔
381
         BOTAN_ASSERT(remainder_bytes < BS, "Only a partial block left");
3,492✔
382
         uint8_t* remainder = &buf[sz - remainder_bytes];
3,492✔
383

384
         xor_buf(m_checksum.data(), remainder, remainder_bytes);
3,492✔
385
         m_checksum[remainder_bytes] ^= 0x80;
3,492✔
386

387
         // Offset_*
388
         mac ^= m_L->star();
3,492✔
389

390
         secure_vector<uint8_t> pad(BS);
3,492✔
391
         m_cipher->encrypt(mac, pad);
3,492✔
392
         xor_buf(remainder, pad.data(), remainder_bytes);
3,492✔
393
      }
3,492✔
394
   } else {
395
      mac = m_L->offset();
1,871✔
396
   }
397

398
   // now compute the tag
399

400
   // fold checksum
401
   for(size_t i = 0; i != m_checksum.size(); i += BS) {
72,870✔
402
      xor_buf(mac.data(), m_checksum.data() + i, BS);
67,304✔
403
   }
404

405
   xor_buf(mac.data(), m_L->dollar().data(), BS);
5,566✔
406
   m_cipher->encrypt(mac);
5,566✔
407
   xor_buf(mac.data(), m_ad_hash.data(), BS);
5,566✔
408

409
   buffer += std::make_pair(mac.data(), tag_size());
5,566✔
410

411
   zeroise(m_checksum);
5,566✔
412
   m_block_index = 0;
5,566✔
413
}
5,566✔
414

415
void OCB_Decryption::decrypt(uint8_t buffer[], size_t blocks) {
3,037✔
416
   assert_key_material_set();
3,037✔
417
   BOTAN_STATE_CHECK(m_L->initialized());
2,991✔
418

419
   const size_t BS = block_size();
2,945✔
420

421
   while(blocks) {
9,857✔
422
      const size_t proc_blocks = std::min(blocks, par_blocks());
6,912✔
423
      const size_t proc_bytes = proc_blocks * BS;
6,912✔
424

425
      const uint8_t* offsets = m_L->compute_offsets(m_block_index, proc_blocks);
6,912✔
426

427
      m_cipher->decrypt_n_xex(buffer, offsets, proc_blocks);
6,912✔
428

429
      xor_buf(m_checksum.data(), buffer, proc_bytes);
6,912✔
430

431
      buffer += proc_bytes;
6,912✔
432
      blocks -= proc_blocks;
6,912✔
433
      m_block_index += proc_blocks;
6,912✔
434
   }
435
}
2,945✔
436

437
size_t OCB_Decryption::process_msg(uint8_t buf[], size_t sz) {
513✔
438
   BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid OCB input size");
513✔
439
   decrypt(buf, sz / block_size());
513✔
440
   return sz;
421✔
441
}
442

443
void OCB_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
3,929✔
444
   assert_key_material_set();
3,929✔
445
   BOTAN_STATE_CHECK(m_L->initialized());
3,837✔
446

447
   const size_t BS = block_size();
3,791✔
448

449
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
3,791✔
450
   const size_t sz = buffer.size() - offset;
3,791✔
451
   uint8_t* buf = buffer.data() + offset;
3,791✔
452

453
   BOTAN_ARG_CHECK(sz >= tag_size(), "input did not include the tag");
3,791✔
454

455
   const size_t remaining = sz - tag_size();
3,791✔
456

457
   secure_vector<uint8_t> mac(BS);
3,791✔
458

459
   if(remaining) {
3,791✔
460
      const size_t final_full_blocks = remaining / BS;
2,524✔
461
      const size_t final_bytes = remaining - (final_full_blocks * BS);
2,524✔
462

463
      decrypt(buf, final_full_blocks);
2,524✔
464
      mac ^= m_L->offset();
2,524✔
465

466
      if(final_bytes) {
2,524✔
467
         BOTAN_ASSERT(final_bytes < BS, "Only a partial block left");
2,331✔
468

469
         uint8_t* remainder = &buf[remaining - final_bytes];
2,331✔
470

471
         mac ^= m_L->star();
2,331✔
472
         secure_vector<uint8_t> pad(BS);
2,469✔
473
         m_cipher->encrypt(mac, pad);  // P_*
2,331✔
474
         xor_buf(remainder, pad.data(), final_bytes);
2,331✔
475

476
         xor_buf(m_checksum.data(), remainder, final_bytes);
2,331✔
477
         m_checksum[final_bytes] ^= 0x80;
2,331✔
478
      }
2,331✔
479
   } else {
480
      mac = m_L->offset();
1,267✔
481
   }
482

483
   // compute the mac
484

485
   // fold checksum
486
   for(size_t i = 0; i != m_checksum.size(); i += BS) {
64,131✔
487
      xor_buf(mac.data(), m_checksum.data() + i, BS);
60,340✔
488
   }
489

490
   mac ^= m_L->dollar();
3,791✔
491
   m_cipher->encrypt(mac);
3,791✔
492
   mac ^= m_ad_hash;
3,791✔
493

494
   // reset state
495
   zeroise(m_checksum);
3,791✔
496
   m_block_index = 0;
3,791✔
497

498
   // compare mac
499
   const uint8_t* included_tag = &buf[remaining];
3,791✔
500

501
   if(!CT::is_equal(mac.data(), included_tag, tag_size()).as_bool()) {
7,582✔
502
      throw Invalid_Authentication_Tag("OCB tag check failed");
138✔
503
   }
504

505
   // remove tag from end of message
506
   buffer.resize(remaining + offset);
3,653✔
507
}
3,653✔
508

509
}  // 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