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

randombit / botan / 13258120944

11 Feb 2025 07:48AM UTC coverage: 91.661% (+0.002%) from 91.659%
13258120944

Pull #4647

github

web-flow
Merge 311224e67 into f372b5a9e
Pull Request #4647: Avoid using mem_ops.h or assert.h in public headers

94869 of 103500 relevant lines covered (91.66%)

11332192.06 hits per line

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

99.28
/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/mem_ops.h>
13
#include <botan/internal/bit_ops.h>
14
#include <botan/internal/ct_utils.h>
15
#include <botan/internal/poly_dbl.h>
16

17
namespace Botan {
18

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

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

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

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

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

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

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

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

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

57
      const secure_vector<uint8_t>& get(size_t i) const {
121,751✔
58
         while(m_L.size() <= i) {
121,838✔
59
            m_L.push_back(poly_double(m_L.back()));
174✔
60
         }
61

62
         return m_L[i];
121,751✔
63
      }
64

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

68
         uint8_t* offsets = m_offset_buf.data();
14,532✔
69

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

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

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

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

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

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

95
               blocks -= 4;
36,912✔
96
            }
97
         }
98

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

106
         return m_offset_buf.data();
14,532✔
107
      }
108

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

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

123
namespace {
124

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

133
   secure_vector<uint8_t> buf(BS);
9,515✔
134

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

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

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

156
   return sum;
9,515✔
157
}
19,030✔
158

159
}  // namespace
160

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

244
   // NOLINTNEXTLINE(readability-avoid-nested-conditional-operator)
245
   const size_t MASKLEN = (BS == 16 ? 6 : ((BS == 24) ? 7 : 8));
9,467✔
246

247
   const uint8_t BOTTOM_MASK = static_cast<uint8_t>((static_cast<uint16_t>(1) << MASKLEN) - 1);
9,467✔
248

249
   m_nonce_buf.resize(BS);
9,467✔
250
   clear_mem(&m_nonce_buf[0], m_nonce_buf.size());
9,467✔
251

252
   copy_mem(&m_nonce_buf[BS - nonce_len], nonce, nonce_len);
9,467✔
253
   m_nonce_buf[0] = static_cast<uint8_t>(((tag_size() * 8) % (BS * 8)) << (BS <= 16 ? 1 : 0));
11,065✔
254

255
   m_nonce_buf[BS - nonce_len - 1] ^= 1;
9,467✔
256

257
   const uint8_t bottom = m_nonce_buf[BS - 1] & BOTTOM_MASK;
9,467✔
258
   m_nonce_buf[BS - 1] &= ~BOTTOM_MASK;
9,467✔
259

260
   const bool need_new_stretch = (m_last_nonce != m_nonce_buf);
9,467✔
261

262
   if(need_new_stretch) {
9,467✔
263
      m_last_nonce = m_nonce_buf;
683✔
264

265
      m_cipher->encrypt(m_nonce_buf);
683✔
266

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

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

305
      m_stretch = m_nonce_buf;
683✔
306
   }
307

308
   // now set the offset from stretch and bottom
309
   const size_t shift_bytes = bottom / 8;
9,467✔
310
   const size_t shift_bits = bottom % 8;
9,467✔
311

312
   BOTAN_ASSERT(m_stretch.size() >= BS + shift_bytes + 1, "Size ok");
9,467✔
313

314
   m_offset.resize(BS);
9,467✔
315
   for(size_t i = 0; i != BS; ++i) {
195,875✔
316
      m_offset[i] = (m_stretch[i + shift_bytes] << shift_bits);
186,408✔
317
      m_offset[i] |= (m_stretch[i + shift_bytes + 1] >> (8 - shift_bits));
186,408✔
318
   }
319

320
   return m_offset;
9,467✔
321
}
322

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

328
   assert_key_material_set();
9,467✔
329

330
   m_L->init(update_nonce(nonce, nonce_len));
9,467✔
331
   zeroise(m_checksum);
9,467✔
332
   m_block_index = 0;
9,467✔
333
}
9,467✔
334

335
void OCB_Encryption::encrypt(uint8_t buffer[], size_t blocks) {
4,204✔
336
   assert_key_material_set();
4,204✔
337
   BOTAN_STATE_CHECK(m_L->initialized());
4,158✔
338

339
   const size_t BS = block_size();
4,112✔
340

341
   while(blocks) {
11,790✔
342
      const size_t proc_blocks = std::min(blocks, par_blocks());
7,678✔
343
      const size_t proc_bytes = proc_blocks * BS;
7,678✔
344

345
      const uint8_t* offsets = m_L->compute_offsets(m_block_index, proc_blocks);
7,678✔
346

347
      xor_buf(m_checksum.data(), buffer, proc_bytes);
7,678✔
348

349
      xor_buf(buffer, offsets, proc_bytes);
7,678✔
350
      m_cipher->encrypt_n(buffer, buffer, proc_blocks);
7,678✔
351
      xor_buf(buffer, offsets, proc_bytes);
7,678✔
352

353
      buffer += proc_bytes;
7,678✔
354
      blocks -= proc_blocks;
7,678✔
355
      m_block_index += proc_blocks;
7,678✔
356
   }
357
}
4,112✔
358

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

365
void OCB_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
5,713✔
366
   assert_key_material_set();
5,713✔
367
   BOTAN_STATE_CHECK(m_L->initialized());
5,621✔
368

369
   const size_t BS = block_size();
5,575✔
370

371
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
5,575✔
372
   const size_t sz = buffer.size() - offset;
5,575✔
373
   uint8_t* buf = buffer.data() + offset;
5,575✔
374

375
   secure_vector<uint8_t> mac(BS);
5,575✔
376

377
   if(sz) {
5,575✔
378
      const size_t final_full_blocks = sz / BS;
3,704✔
379
      const size_t remainder_bytes = sz - (final_full_blocks * BS);
3,704✔
380

381
      encrypt(buf, final_full_blocks);
3,704✔
382
      mac = m_L->offset();
3,704✔
383

384
      if(remainder_bytes) {
3,704✔
385
         BOTAN_ASSERT(remainder_bytes < BS, "Only a partial block left");
3,501✔
386
         uint8_t* remainder = &buf[sz - remainder_bytes];
3,501✔
387

388
         xor_buf(m_checksum.data(), remainder, remainder_bytes);
3,501✔
389
         m_checksum[remainder_bytes] ^= 0x80;
3,501✔
390

391
         // Offset_*
392
         mac ^= m_L->star();
3,501✔
393

394
         secure_vector<uint8_t> pad(BS);
3,501✔
395
         m_cipher->encrypt(mac, pad);
3,501✔
396
         xor_buf(remainder, pad.data(), remainder_bytes);
3,501✔
397
      }
3,501✔
398
   } else {
399
      mac = m_L->offset();
1,871✔
400
   }
401

402
   // now compute the tag
403

404
   // fold checksum
405
   for(size_t i = 0; i != m_checksum.size(); i += BS) {
131,119✔
406
      xor_buf(mac.data(), m_checksum.data() + i, BS);
125,544✔
407
   }
408

409
   xor_buf(mac.data(), m_L->dollar().data(), BS);
5,575✔
410
   m_cipher->encrypt(mac);
5,575✔
411
   xor_buf(mac.data(), m_ad_hash.data(), BS);
5,575✔
412

413
   buffer += std::make_pair(mac.data(), tag_size());
5,575✔
414

415
   zeroise(m_checksum);
5,575✔
416
   m_block_index = 0;
5,575✔
417
}
5,575✔
418

419
void OCB_Decryption::decrypt(uint8_t buffer[], size_t blocks) {
3,045✔
420
   assert_key_material_set();
3,045✔
421
   BOTAN_STATE_CHECK(m_L->initialized());
2,999✔
422

423
   const size_t BS = block_size();
2,953✔
424

425
   while(blocks) {
9,807✔
426
      const size_t proc_blocks = std::min(blocks, par_blocks());
6,854✔
427
      const size_t proc_bytes = proc_blocks * BS;
6,854✔
428

429
      const uint8_t* offsets = m_L->compute_offsets(m_block_index, proc_blocks);
6,854✔
430

431
      xor_buf(buffer, offsets, proc_bytes);
6,854✔
432
      m_cipher->decrypt_n(buffer, buffer, proc_blocks);
6,854✔
433
      xor_buf(buffer, offsets, proc_bytes);
6,854✔
434

435
      xor_buf(m_checksum.data(), buffer, proc_bytes);
6,854✔
436

437
      buffer += proc_bytes;
6,854✔
438
      blocks -= proc_blocks;
6,854✔
439
      m_block_index += proc_blocks;
6,854✔
440
   }
441
}
2,953✔
442

443
size_t OCB_Decryption::process_msg(uint8_t buf[], size_t sz) {
513✔
444
   BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid OCB input size");
513✔
445
   decrypt(buf, sz / block_size());
513✔
446
   return sz;
421✔
447
}
448

449
void OCB_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
3,938✔
450
   assert_key_material_set();
3,938✔
451
   BOTAN_STATE_CHECK(m_L->initialized());
3,846✔
452

453
   const size_t BS = block_size();
3,800✔
454

455
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
3,800✔
456
   const size_t sz = buffer.size() - offset;
3,800✔
457
   uint8_t* buf = buffer.data() + offset;
3,800✔
458

459
   BOTAN_ARG_CHECK(sz >= tag_size(), "input did not include the tag");
3,800✔
460

461
   const size_t remaining = sz - tag_size();
3,800✔
462

463
   secure_vector<uint8_t> mac(BS);
3,800✔
464

465
   if(remaining) {
3,800✔
466
      const size_t final_full_blocks = remaining / BS;
2,532✔
467
      const size_t final_bytes = remaining - (final_full_blocks * BS);
2,532✔
468

469
      decrypt(buf, final_full_blocks);
2,532✔
470
      mac ^= m_L->offset();
2,532✔
471

472
      if(final_bytes) {
2,532✔
473
         BOTAN_ASSERT(final_bytes < BS, "Only a partial block left");
2,337✔
474

475
         uint8_t* remainder = &buf[remaining - final_bytes];
2,337✔
476

477
         mac ^= m_L->star();
2,337✔
478
         secure_vector<uint8_t> pad(BS);
2,475✔
479
         m_cipher->encrypt(mac, pad);  // P_*
2,337✔
480
         xor_buf(remainder, pad.data(), final_bytes);
2,337✔
481

482
         xor_buf(m_checksum.data(), remainder, final_bytes);
2,337✔
483
         m_checksum[final_bytes] ^= 0x80;
2,337✔
484
      }
2,337✔
485
   } else {
486
      mac = m_L->offset();
1,268✔
487
   }
488

489
   // compute the mac
490

491
   // fold checksum
492
   for(size_t i = 0; i != m_checksum.size(); i += BS) {
124,556✔
493
      xor_buf(mac.data(), m_checksum.data() + i, BS);
120,756✔
494
   }
495

496
   mac ^= m_L->dollar();
3,800✔
497
   m_cipher->encrypt(mac);
3,800✔
498
   mac ^= m_ad_hash;
3,800✔
499

500
   // reset state
501
   zeroise(m_checksum);
3,800✔
502
   m_block_index = 0;
3,800✔
503

504
   // compare mac
505
   const uint8_t* included_tag = &buf[remaining];
3,800✔
506

507
   if(!CT::is_equal(mac.data(), included_tag, tag_size()).as_bool()) {
7,600✔
508
      throw Invalid_Authentication_Tag("OCB tag check failed");
138✔
509
   }
510

511
   // remove tag from end of message
512
   buffer.resize(remaining + offset);
3,662✔
513
}
3,662✔
514

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