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

randombit / botan / 26735862306

31 May 2026 11:43PM UTC coverage: 89.37%. Remained the same
26735862306

push

github

web-flow
Merge pull request #5631 from randombit/jack/overflow-checks

Systematically eliminate any possible integer overflows

110295 of 123414 relevant lines covered (89.37%)

11075877.11 hits per line

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

99.31
/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/exceptn.h>
13
#include <botan/mem_ops.h>
14
#include <botan/internal/bit_ops.h>
15
#include <botan/internal/ct_utils.h>
16
#include <botan/internal/int_utils.h>
17
#include <botan/internal/poly_dbl.h>
18

19
namespace Botan {
20

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

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

42
         while(m_L.size() < 8) {
2,536✔
43
            m_L.push_back(poly_double(m_L.back()));
4,438✔
44
         }
45

46
         m_offset_buf.resize(m_BS * m_max_blocks);
317✔
47
      }
317✔
48

49
      void init(const secure_vector<uint8_t>& offset) { m_offset = offset; }
9,877✔
50

51
      void reset() { m_offset.clear(); }
10,552✔
52

53
      bool initialized() const { return !m_offset.empty(); }
36,732✔
54

55
      const secure_vector<uint8_t>& star() const { return m_L_star; }
6,189✔
56

57
      const secure_vector<uint8_t>& dollar() const { return m_L_dollar; }
9,780✔
58

59
      const secure_vector<uint8_t>& offset() const { return m_offset; }
9,463✔
60

61
      const secure_vector<uint8_t>& get(size_t i) const {
122,052✔
62
         while(m_L.size() <= i) {
122,140✔
63
            m_L.push_back(poly_double(m_L.back()));
176✔
64
         }
65

66
         return m_L[i];
122,052✔
67
      }
68

69
      const uint8_t* compute_offsets(uint64_t block_index, size_t blocks) {
14,578✔
70
         BOTAN_ASSERT(blocks <= m_max_blocks, "OCB offsets");
14,578✔
71

72
         uint8_t* offsets = m_offset_buf.data();
14,578✔
73

74
         if(block_index % 4 == 0) {
14,578✔
75
            const secure_vector<uint8_t>& L0 = get(0);
14,124✔
76
            const secure_vector<uint8_t>& L1 = get(1);
14,124✔
77

78
            while(blocks >= 4) {
65,232✔
79
               // ntz(4*i+1) == 0
80
               // ntz(4*i+2) == 1
81
               // ntz(4*i+3) == 0
82
               block_index += 4;
36,984✔
83
               const size_t ntz4 = var_ctz64(block_index);
36,984✔
84

85
               xor_buf(offsets, m_offset.data(), L0.data(), m_BS);
36,984✔
86
               offsets += m_BS;
36,984✔
87

88
               xor_buf(offsets, offsets - m_BS, L1.data(), m_BS);
36,984✔
89
               offsets += m_BS;
36,984✔
90

91
               xor_buf(m_offset.data(), L1.data(), m_BS);
36,984✔
92
               copy_mem(offsets, m_offset.data(), m_BS);
36,984✔
93
               offsets += m_BS;
36,984✔
94

95
               xor_buf(m_offset.data(), get(ntz4).data(), m_BS);
36,984✔
96
               copy_mem(offsets, m_offset.data(), m_BS);
36,984✔
97
               offsets += m_BS;
36,984✔
98

99
               blocks -= 4;
36,984✔
100
            }
101
         }
102

103
         for(size_t i = 0; i != blocks; ++i) {  // could be done in parallel
24,278✔
104
            const size_t ntz = var_ctz64(block_index + i + 1);
9,700✔
105
            xor_buf(m_offset.data(), get(ntz).data(), m_BS);
9,700✔
106
            copy_mem(offsets, m_offset.data(), m_BS);
9,700✔
107
            offsets += m_BS;
9,700✔
108
         }
109

110
         return m_offset_buf.data();
14,578✔
111
      }
112

113
   private:
114
      static secure_vector<uint8_t> poly_double(const secure_vector<uint8_t>& in) {
2,941✔
115
         secure_vector<uint8_t> out(in.size());
2,941✔
116
         poly_double_n(out.data(), in.data(), out.size());
2,941✔
117
         return out;
2,941✔
118
      }
×
119

120
      const size_t m_BS, m_max_blocks;
121
      secure_vector<uint8_t> m_L_dollar, m_L_star;
122
      secure_vector<uint8_t> m_offset;
123
      mutable std::vector<secure_vector<uint8_t>> m_L;
124
      secure_vector<uint8_t> m_offset_buf;
125
};
126

127
namespace {
128

129
/*
130
* OCB's HASH
131
*/
132
secure_vector<uint8_t> ocb_hash(const L_computer& L, const BlockCipher& cipher, const uint8_t ad[], size_t ad_len) {
9,715✔
133
   const size_t BS = cipher.block_size();
9,715✔
134
   secure_vector<uint8_t> sum(BS);
9,715✔
135
   secure_vector<uint8_t> offset(BS);
9,715✔
136

137
   secure_vector<uint8_t> buf(BS);
9,715✔
138

139
   const size_t ad_blocks = (ad_len / BS);
9,715✔
140
   const size_t ad_remainder = (ad_len % BS);
9,715✔
141

142
   for(size_t i = 0; i != ad_blocks; ++i) {
56,835✔
143
      // this loop could run in parallel
144
      offset ^= L.get(var_ctz64(i + 1));
94,240✔
145
      buf = offset;
47,120✔
146
      xor_buf(buf.data(), &ad[BS * i], BS);
47,120✔
147
      cipher.encrypt(buf);
47,120✔
148
      sum ^= buf;
47,120✔
149
   }
150

151
   if(ad_remainder > 0) {
9,715✔
152
      offset ^= L.star();
5,974✔
153
      buf = offset;
5,974✔
154
      xor_buf(buf.data(), &ad[BS * ad_blocks], ad_remainder);
5,974✔
155
      buf[ad_remainder] ^= 0x80;
5,974✔
156
      cipher.encrypt(buf);
5,974✔
157
      sum ^= buf;
5,974✔
158
   }
159

160
   return sum;
9,715✔
161
}
19,430✔
162

163
}  // namespace
164

165
OCB_Mode::OCB_Mode(std::unique_ptr<BlockCipher> cipher, size_t tag_size) :
363✔
166
      m_cipher(std::move(cipher)),
363✔
167
      m_checksum(m_cipher->parallel_bytes()),
726✔
168
      m_ad_hash(m_cipher->block_size()),
363✔
169
      m_tag_size(tag_size),
363✔
170
      m_block_size(m_cipher->block_size()),
363✔
171
      m_par_blocks(m_cipher->parallel_bytes() / m_block_size) {
1,089✔
172
   const size_t BS = block_size();
363✔
173

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

181
   BOTAN_ARG_CHECK(m_tag_size % 4 == 0 && m_tag_size >= 8 && m_tag_size <= BS && m_tag_size <= 32,
363✔
182
                   "Invalid OCB tag length");
183
}
363✔
184

185
OCB_Mode::~OCB_Mode() = default;
2,173✔
186

187
void OCB_Mode::clear() {
92✔
188
   m_cipher->clear();
92✔
189
   m_L.reset();
92✔
190
   zeroise(m_ad_hash);
92✔
191
   reset();
92✔
192
}
92✔
193

194
void OCB_Mode::reset() {
10,644✔
195
   m_block_index = 0;
10,644✔
196
   zeroise(m_checksum);
10,644✔
197
   m_last_nonce.clear();
10,644✔
198
   m_stretch.clear();
341,112✔
199
   zeroise(m_nonce_buf);
213,596✔
200
   zeroise(m_offset);
10,644✔
201
   if(m_L) {
10,644✔
202
      m_L->reset();
10,552✔
203
   }
204
}
10,644✔
205

206
bool OCB_Mode::valid_nonce_length(size_t length) const {
10,015✔
207
   if(length == 0) {
10,015✔
208
      return false;
209
   }
210
   if(block_size() == 16) {
10,015✔
211
      return length < 16;
8,361✔
212
   } else {
213
      return length < (block_size() - 1);
1,654✔
214
   }
215
}
216

217
std::string OCB_Mode::name() const {
506✔
218
   return m_cipher->name() + "/OCB";  // include tag size?
1,012✔
219
}
220

221
size_t OCB_Mode::update_granularity() const {
1,597✔
222
   return block_size();
1,597✔
223
}
224

225
size_t OCB_Mode::ideal_granularity() const {
276✔
226
   return (m_par_blocks * block_size());
276✔
227
}
228

229
Key_Length_Specification OCB_Mode::key_spec() const {
317✔
230
   return m_cipher->key_spec();
317✔
231
}
232

233
bool OCB_Mode::has_keying_material() const {
37,514✔
234
   return m_cipher->has_keying_material();
37,514✔
235
}
236

237
void OCB_Mode::key_schedule(std::span<const uint8_t> key) {
317✔
238
   m_cipher->set_key(key);
317✔
239
   m_L = std::make_unique<L_computer>(*m_cipher);
317✔
240

241
   // Drop all key-dependent per-message state: m_last_nonce/m_stretch are
242
   // cached for the update_nonce() fast path and would otherwise allow a
243
   // start_msg() with a same-valued nonce under the new key to silently
244
   // reuse the stretch computed under the previous key.
245
   reset();
317✔
246

247
   // m_ad_hash was precomputed against the previous L values and cipher
248
   // key. Re-keying invalidates it; AD must be re-set after set_key.
249
   zeroise(m_ad_hash);
317✔
250
}
317✔
251

252
void OCB_Mode::set_associated_data_n(size_t idx, std::span<const uint8_t> ad) {
10,083✔
253
   BOTAN_ARG_CHECK(idx == 0, "OCB: cannot handle non-zero index in set_associated_data_n");
10,083✔
254
   assert_key_material_set();
9,991✔
255
   BOTAN_STATE_CHECK(!m_L->initialized());
9,807✔
256
   m_ad_hash = ocb_hash(*m_L, *m_cipher, ad.data(), ad.size());
9,715✔
257
}
9,715✔
258

259
const secure_vector<uint8_t>& OCB_Mode::update_nonce(const uint8_t nonce[], size_t nonce_len) {
9,877✔
260
   const size_t BS = block_size();
9,877✔
261

262
   BOTAN_ASSERT(BS == 16 || BS == 24 || BS == 32 || BS == 64, "OCB block size is supported");
9,877✔
263

264
   // NOLINTNEXTLINE(readability-avoid-nested-conditional-operator)
265
   const size_t MASKLEN = (BS == 16 ? 6 : ((BS == 24) ? 7 : 8));
9,877✔
266

267
   const uint8_t BOTTOM_MASK = static_cast<uint8_t>((static_cast<uint16_t>(1) << MASKLEN) - 1);
9,877✔
268

269
   m_nonce_buf.resize(BS);
9,877✔
270
   clear_mem(m_nonce_buf.data(), m_nonce_buf.size());
9,877✔
271

272
   copy_mem(&m_nonce_buf[BS - nonce_len], nonce, nonce_len);
9,877✔
273
   m_nonce_buf[0] = static_cast<uint8_t>(((tag_size() * 8) % (BS * 8)) << (BS <= 16 ? 1 : 0));
11,519✔
274

275
   m_nonce_buf[BS - nonce_len - 1] ^= 1;
9,877✔
276

277
   const uint8_t bottom = m_nonce_buf[BS - 1] & BOTTOM_MASK;
9,877✔
278
   m_nonce_buf[BS - 1] &= ~BOTTOM_MASK;
9,877✔
279

280
   const bool need_new_stretch = (m_last_nonce != m_nonce_buf);
9,877✔
281

282
   if(need_new_stretch) {
9,877✔
283
      m_last_nonce = m_nonce_buf;
9,877✔
284

285
      m_cipher->encrypt(m_nonce_buf);
9,877✔
286

287
      /*
288
      The loop bounds (BS vs BS/2) are derived from the relation
289
      between the block size and the MASKLEN. Using the terminology
290
      of draft-krovetz-ocb-wide, we have to derive enough bits in
291
      ShiftedKtop to read up to BLOCKLEN+bottom bits from Stretch.
292

293
                 +----------+---------+-------+---------+
294
                 | BLOCKLEN | RESIDUE | SHIFT | MASKLEN |
295
                 +----------+---------+-------+---------+
296
                 |       32 |     141 |    17 |    4    |
297
                 |       64 |      27 |    25 |    5    |
298
                 |       96 |    1601 |    33 |    6    |
299
                 |      128 |     135 |     8 |    6    |
300
                 |      192 |     135 |    40 |    7    |
301
                 |      256 |    1061 |     1 |    8    |
302
                 |      384 |    4109 |    80 |    8    |
303
                 |      512 |     293 |   176 |    8    |
304
                 |     1024 |  524355 |   352 |    9    |
305
                 +----------+---------+-------+---------+
306
      */
307
      if(BS == 16) {
9,877✔
308
         for(size_t i = 0; i != BS / 2; ++i) {
74,115✔
309
            m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i + 1]);
65,880✔
310
         }
311
      } else if(BS == 24) {
1,642✔
312
         for(size_t i = 0; i != 16; ++i) {
6,885✔
313
            m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i + 5]);
6,480✔
314
         }
315
      } else if(BS == 32) {
1,237✔
316
         for(size_t i = 0; i != BS; ++i) {
27,423✔
317
            m_nonce_buf.push_back(m_nonce_buf[i] ^ (m_nonce_buf[i] << 1) ^ (m_nonce_buf[i + 1] >> 7));
26,592✔
318
         }
319
      } else if(BS == 64) {
320
         for(size_t i = 0; i != BS / 2; ++i) {
13,398✔
321
            m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i + 22]);
12,992✔
322
         }
323
      }
324

325
      m_stretch = m_nonce_buf;
9,877✔
326
   }
327

328
   // now set the offset from stretch and bottom
329
   const size_t shift_bytes = bottom / 8;
9,877✔
330
   const size_t shift_bits = bottom % 8;
9,877✔
331

332
   BOTAN_ASSERT(m_stretch.size() >= BS + shift_bytes + 1, "Size ok");
9,877✔
333

334
   m_offset.resize(BS);
9,877✔
335
   for(size_t i = 0; i != BS; ++i) {
203,933✔
336
      m_offset[i] = (m_stretch[i + shift_bytes] << shift_bits);
194,056✔
337
      m_offset[i] |= (m_stretch[i + shift_bytes + 1] >> (8 - shift_bits));
194,056✔
338
   }
339

340
   return m_offset;
9,877✔
341
}
342

343
void OCB_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
9,969✔
344
   if(!valid_nonce_length(nonce_len)) {
9,969✔
345
      throw Invalid_IV_Length(name(), nonce_len);
×
346
   }
347

348
   assert_key_material_set();
9,969✔
349
   BOTAN_STATE_CHECK(!m_L->initialized());
9,969✔
350

351
   m_L->init(update_nonce(nonce, nonce_len));
9,877✔
352
   zeroise(m_checksum);
9,877✔
353
   m_block_index = 0;
9,877✔
354
}
9,877✔
355

356
size_t OCB_Encryption::output_length(size_t input_length) const {
100✔
357
   return add_or_throw(input_length, tag_size(), "OCB input too large");
100✔
358
}
359

360
void OCB_Encryption::encrypt(uint8_t buffer[], size_t blocks) {
4,250✔
361
   assert_key_material_set();
4,250✔
362
   BOTAN_STATE_CHECK(m_L->initialized());
4,204✔
363

364
   const size_t BS = block_size();
4,158✔
365

366
   while(blocks > 0) {
11,876✔
367
      const size_t proc_blocks = std::min(blocks, par_blocks());
7,718✔
368
      const size_t proc_bytes = proc_blocks * BS;
7,718✔
369

370
      const uint8_t* offsets = m_L->compute_offsets(m_block_index, proc_blocks);
7,718✔
371

372
      xor_buf(m_checksum.data(), buffer, proc_bytes);
7,718✔
373

374
      xor_buf(buffer, offsets, proc_bytes);
7,718✔
375
      m_cipher->encrypt_n(buffer, buffer, proc_blocks);
7,718✔
376
      xor_buf(buffer, offsets, proc_bytes);
7,718✔
377

378
      buffer += proc_bytes;
7,718✔
379
      blocks -= proc_blocks;
7,718✔
380
      m_block_index += proc_blocks;
7,718✔
381
   }
382
}
4,158✔
383

384
size_t OCB_Encryption::process_msg(uint8_t buf[], size_t sz) {
500✔
385
   BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid OCB input size");
500✔
386
   encrypt(buf, sz / block_size());
500✔
387
   return sz;
408✔
388
}
389

390
void OCB_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
5,862✔
391
   assert_key_material_set();
5,862✔
392
   BOTAN_STATE_CHECK(m_L->initialized());
5,770✔
393

394
   const size_t BS = block_size();
5,678✔
395

396
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
5,678✔
397
   const size_t sz = buffer.size() - offset;
5,632✔
398
   uint8_t* buf = buffer.data() + offset;
5,632✔
399

400
   secure_vector<uint8_t> mac(BS);
5,632✔
401

402
   if(sz > 0) {
5,632✔
403
      const size_t final_full_blocks = sz / BS;
3,750✔
404
      const size_t remainder_bytes = sz - (final_full_blocks * BS);
3,750✔
405

406
      encrypt(buf, final_full_blocks);
3,750✔
407
      mac = m_L->offset();
3,750✔
408

409
      if(remainder_bytes > 0) {
3,750✔
410
         BOTAN_ASSERT(remainder_bytes < BS, "Only a partial block left");
3,528✔
411
         uint8_t* remainder = &buf[sz - remainder_bytes];
3,528✔
412

413
         xor_buf(m_checksum.data(), remainder, remainder_bytes);
3,528✔
414
         m_checksum[remainder_bytes] ^= 0x80;
3,528✔
415

416
         // Offset_*
417
         mac ^= m_L->star();
3,528✔
418

419
         secure_vector<uint8_t> pad(BS);
3,528✔
420
         m_cipher->encrypt(mac, pad);
3,528✔
421
         xor_buf(remainder, pad.data(), remainder_bytes);
3,528✔
422
      }
3,528✔
423
   } else {
424
      mac = m_L->offset();
1,882✔
425
   }
426

427
   // now compute the tag
428

429
   // fold checksum
430
   for(size_t i = 0; i != m_checksum.size(); i += BS) {
132,748✔
431
      xor_buf(mac.data(), m_checksum.data() + i, BS);
127,116✔
432
   }
433

434
   xor_buf(mac.data(), m_L->dollar().data(), BS);
5,632✔
435
   m_cipher->encrypt(mac);
5,632✔
436
   xor_buf(mac.data(), m_ad_hash.data(), BS);
5,632✔
437

438
   buffer += std::make_pair(mac.data(), tag_size());
5,632✔
439

440
   reset();
5,632✔
441
}
5,632✔
442

443
size_t OCB_Decryption::output_length(size_t input_length) const {
112✔
444
   BOTAN_ARG_CHECK(input_length >= tag_size(), "Message too short to be valid");
112✔
445
   return input_length - tag_size();
112✔
446
}
447

448
void OCB_Decryption::decrypt(uint8_t buffer[], size_t blocks) {
3,059✔
449
   assert_key_material_set();
3,059✔
450
   BOTAN_STATE_CHECK(m_L->initialized());
3,013✔
451

452
   const size_t BS = block_size();
2,967✔
453

454
   while(blocks > 0) {
9,827✔
455
      const size_t proc_blocks = std::min(blocks, par_blocks());
6,860✔
456
      const size_t proc_bytes = proc_blocks * BS;
6,860✔
457

458
      const uint8_t* offsets = m_L->compute_offsets(m_block_index, proc_blocks);
6,860✔
459

460
      xor_buf(buffer, offsets, proc_bytes);
6,860✔
461
      m_cipher->decrypt_n(buffer, buffer, proc_blocks);
6,860✔
462
      xor_buf(buffer, offsets, proc_bytes);
6,860✔
463

464
      xor_buf(m_checksum.data(), buffer, proc_bytes);
6,860✔
465

466
      buffer += proc_bytes;
6,860✔
467
      blocks -= proc_blocks;
6,860✔
468
      m_block_index += proc_blocks;
6,860✔
469
   }
470
}
2,967✔
471

472
size_t OCB_Decryption::process_msg(uint8_t buf[], size_t sz) {
513✔
473
   BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid OCB input size");
513✔
474
   decrypt(buf, sz / block_size());
513✔
475
   return sz;
421✔
476
}
477

478
void OCB_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
4,061✔
479
   assert_key_material_set();
4,061✔
480
   BOTAN_STATE_CHECK(m_L->initialized());
3,969✔
481

482
   const size_t BS = block_size();
3,877✔
483

484
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
3,877✔
485
   const size_t sz = buffer.size() - offset;
3,831✔
486
   uint8_t* buf = buffer.data() + offset;
3,831✔
487

488
   BOTAN_ARG_CHECK(sz >= tag_size(), "input did not include the tag");
3,831✔
489

490
   const size_t remaining = sz - tag_size();
3,831✔
491

492
   secure_vector<uint8_t> mac(BS);
3,831✔
493

494
   if(remaining > 0) {
3,831✔
495
      const size_t final_full_blocks = remaining / BS;
2,546✔
496
      const size_t final_bytes = remaining - (final_full_blocks * BS);
2,546✔
497

498
      decrypt(buf, final_full_blocks);
2,546✔
499
      mac ^= m_L->offset();
2,546✔
500

501
      if(final_bytes > 0) {
2,546✔
502
         BOTAN_ASSERT(final_bytes < BS, "Only a partial block left");
2,344✔
503

504
         uint8_t* remainder = &buf[remaining - final_bytes];
2,344✔
505

506
         mac ^= m_L->star();
2,344✔
507
         secure_vector<uint8_t> pad(BS);
2,482✔
508
         m_cipher->encrypt(mac, pad);  // P_*
2,344✔
509
         xor_buf(remainder, pad.data(), final_bytes);
2,344✔
510

511
         xor_buf(m_checksum.data(), remainder, final_bytes);
2,344✔
512
         m_checksum[final_bytes] ^= 0x80;
2,344✔
513
      }
2,344✔
514
   } else {
515
      mac = m_L->offset();
1,285✔
516
   }
517

518
   // compute the mac
519

520
   // fold checksum
521
   for(size_t i = 0; i != m_checksum.size(); i += BS) {
125,427✔
522
      xor_buf(mac.data(), m_checksum.data() + i, BS);
121,596✔
523
   }
524

525
   mac ^= m_L->dollar();
3,831✔
526
   m_cipher->encrypt(mac);
3,831✔
527
   mac ^= m_ad_hash;
3,831✔
528

529
   reset();
3,831✔
530

531
   // compare mac
532
   const uint8_t* included_tag = &buf[remaining];
3,831✔
533

534
   if(!CT::is_equal(mac.data(), included_tag, tag_size()).as_bool()) {
3,831✔
535
      clear_mem(std::span{buffer}.subspan(offset, remaining));
138✔
536
      throw Invalid_Authentication_Tag("OCB tag check failed");
138✔
537
   }
538

539
   // remove tag from end of message
540
   buffer.resize(remaining + offset);
3,693✔
541
}
3,693✔
542

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