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

randombit / botan / 6546858227

17 Oct 2023 12:02PM UTC coverage: 91.71% (+0.002%) from 91.708%
6546858227

push

github

randombit
Merge GH #3760 Move constant time memory comparisons to ct_utils.h

80095 of 87335 relevant lines covered (91.71%)

8508512.25 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) :
243✔
22
            m_BS(cipher.block_size()), m_max_blocks(cipher.parallel_bytes() / m_BS) {
486✔
23
         m_L_star.resize(m_BS);
243✔
24
         cipher.encrypt(m_L_star);
243✔
25
         m_L_dollar = poly_double(star());
243✔
26
         m_L.push_back(poly_double(dollar()));
243✔
27

28
         while(m_L.size() < 8) {
1,944✔
29
            m_L.push_back(poly_double(m_L.back()));
3,402✔
30
         }
31

32
         m_offset_buf.resize(m_BS * m_max_blocks);
243✔
33
      }
243✔
34

35
      void init(const secure_vector<uint8_t>& offset) { m_offset = offset; }
9,455✔
36

37
      bool initialized() const { return m_offset.empty() == false; }
16,534✔
38

39
      const secure_vector<uint8_t>& star() const { return m_L_star; }
6,071✔
40

41
      const secure_vector<uint8_t>& dollar() const { return m_L_dollar; }
9,606✔
42

43
      const secure_vector<uint8_t>& offset() const { return m_offset; }
9,363✔
44

45
      const secure_vector<uint8_t>& get(size_t i) const {
72,328✔
46
         while(m_L.size() <= i) {
72,397✔
47
            m_L.push_back(poly_double(m_L.back()));
69✔
48
         }
49

50
         return m_L[i];
72,328✔
51
      }
52

53
      const uint8_t* compute_offsets(size_t block_index, size_t blocks) {
6,387✔
54
         BOTAN_ASSERT(blocks <= m_max_blocks, "OCB offsets");
6,387✔
55

56
         uint8_t* offsets = m_offset_buf.data();
6,387✔
57

58
         if(block_index % 4 == 0) {
6,387✔
59
            const secure_vector<uint8_t>& L0 = get(0);
5,933✔
60
            const secure_vector<uint8_t>& L1 = get(1);
5,933✔
61

62
            while(blocks >= 4) {
15,665✔
63
               // ntz(4*i+1) == 0
64
               // ntz(4*i+2) == 1
65
               // ntz(4*i+3) == 0
66
               block_index += 4;
3,799✔
67
               const size_t ntz4 = var_ctz32(static_cast<uint32_t>(block_index));
3,799✔
68

69
               xor_buf(offsets, m_offset.data(), L0.data(), m_BS);
3,799✔
70
               offsets += m_BS;
3,799✔
71

72
               xor_buf(offsets, offsets - m_BS, L1.data(), m_BS);
3,799✔
73
               offsets += m_BS;
3,799✔
74

75
               xor_buf(m_offset.data(), L1.data(), m_BS);
3,799✔
76
               copy_mem(offsets, m_offset.data(), m_BS);
3,799✔
77
               offsets += m_BS;
3,799✔
78

79
               xor_buf(m_offset.data(), get(ntz4).data(), m_BS);
3,799✔
80
               copy_mem(offsets, m_offset.data(), m_BS);
3,799✔
81
               offsets += m_BS;
3,799✔
82

83
               blocks -= 4;
3,799✔
84
            }
85
         }
86

87
         for(size_t i = 0; i != blocks; ++i) {  // could be done in parallel
16,025✔
88
            const size_t ntz = var_ctz32(static_cast<uint32_t>(block_index + i + 1));
9,638✔
89
            xor_buf(m_offset.data(), get(ntz).data(), m_BS);
9,638✔
90
            copy_mem(offsets, m_offset.data(), m_BS);
9,638✔
91
            offsets += m_BS;
9,638✔
92
         }
93

94
         return m_offset_buf.data();
6,387✔
95
      }
96

97
   private:
98
      static secure_vector<uint8_t> poly_double(const secure_vector<uint8_t>& in) {
2,256✔
99
         secure_vector<uint8_t> out(in.size());
2,256✔
100
         poly_double_n(out.data(), in.data(), out.size());
2,256✔
101
         return out;
2,256✔
102
      }
×
103

104
      const size_t m_BS, m_max_blocks;
105
      secure_vector<uint8_t> m_L_dollar, m_L_star;
106
      secure_vector<uint8_t> m_offset;
107
      mutable std::vector<secure_vector<uint8_t>> m_L;
108
      secure_vector<uint8_t> m_offset_buf;
109
};
110

111
namespace {
112

113
/*
114
* OCB's HASH
115
*/
116
secure_vector<uint8_t> ocb_hash(const L_computer& L, const BlockCipher& cipher, const uint8_t ad[], size_t ad_len) {
9,505✔
117
   const size_t BS = cipher.block_size();
9,505✔
118
   secure_vector<uint8_t> sum(BS);
9,505✔
119
   secure_vector<uint8_t> offset(BS);
9,505✔
120

121
   secure_vector<uint8_t> buf(BS);
9,505✔
122

123
   const size_t ad_blocks = (ad_len / BS);
9,505✔
124
   const size_t ad_remainder = (ad_len % BS);
9,505✔
125

126
   for(size_t i = 0; i != ad_blocks; ++i) {
56,530✔
127
      // this loop could run in parallel
128
      offset ^= L.get(var_ctz32(static_cast<uint32_t>(i + 1)));
94,050✔
129
      buf = offset;
47,025✔
130
      xor_buf(buf.data(), &ad[BS * i], BS);
47,025✔
131
      cipher.encrypt(buf);
47,025✔
132
      sum ^= buf;
47,025✔
133
   }
134

135
   if(ad_remainder) {
9,505✔
136
      offset ^= L.star();
5,875✔
137
      buf = offset;
5,875✔
138
      xor_buf(buf.data(), &ad[BS * ad_blocks], ad_remainder);
5,875✔
139
      buf[ad_remainder] ^= 0x80;
5,875✔
140
      cipher.encrypt(buf);
5,875✔
141
      sum ^= buf;
5,875✔
142
   }
143

144
   return sum;
9,505✔
145
}
19,010✔
146

147
}  // namespace
148

149
OCB_Mode::OCB_Mode(std::unique_ptr<BlockCipher> cipher, size_t tag_size) :
335✔
150
      m_cipher(std::move(cipher)),
335✔
151
      m_checksum(m_cipher->parallel_bytes()),
670✔
152
      m_ad_hash(m_cipher->block_size()),
335✔
153
      m_tag_size(tag_size),
335✔
154
      m_block_size(m_cipher->block_size()),
335✔
155
      m_par_blocks(m_cipher->parallel_bytes() / m_block_size) {
1,005✔
156
   const size_t BS = block_size();
335✔
157

158
   /*
159
   * draft-krovetz-ocb-wide-d1 specifies OCB for several other block
160
   * sizes but only 128, 192, 256 and 512 bit are currently supported
161
   * by this implementation.
162
   */
163
   BOTAN_ARG_CHECK(BS == 16 || BS == 24 || BS == 32 || BS == 64, "Invalid block size for OCB");
335✔
164

165
   BOTAN_ARG_CHECK(m_tag_size % 4 == 0 && m_tag_size >= 8 && m_tag_size <= BS && m_tag_size <= 32,
335✔
166
                   "Invalid OCB tag length");
167
}
335✔
168

169
OCB_Mode::~OCB_Mode() = default;
1,977✔
170

171
void OCB_Mode::clear() {
92✔
172
   m_cipher->clear();
92✔
173
   m_L.reset();  // add clear here?
92✔
174
   reset();
92✔
175
}
92✔
176

177
void OCB_Mode::reset() {
410✔
178
   m_block_index = 0;
410✔
179
   zeroise(m_ad_hash);
410✔
180
   zeroise(m_checksum);
410✔
181
   m_last_nonce.clear();
410✔
182
   m_stretch.clear();
410✔
183
}
410✔
184

185
bool OCB_Mode::valid_nonce_length(size_t length) const {
9,501✔
186
   if(length == 0) {
9,501✔
187
      return false;
188
   }
189
   if(block_size() == 16) {
9,501✔
190
      return length < 16;
7,899✔
191
   } else {
192
      return length < (block_size() - 1);
1,602✔
193
   }
194
}
195

196
std::string OCB_Mode::name() const {
506✔
197
   return m_cipher->name() + "/OCB";  // include tag size?
1,012✔
198
}
199

200
size_t OCB_Mode::update_granularity() const {
1,254✔
201
   return block_size();
1,254✔
202
}
203

204
size_t OCB_Mode::ideal_granularity() const {
276✔
205
   return (m_par_blocks * block_size());
276✔
206
}
207

208
Key_Length_Specification OCB_Mode::key_spec() const {
243✔
209
   return m_cipher->key_spec();
243✔
210
}
211

212
bool OCB_Mode::has_keying_material() const {
36,276✔
213
   return m_cipher->has_keying_material();
36,276✔
214
}
215

216
void OCB_Mode::key_schedule(std::span<const uint8_t> key) {
243✔
217
   m_cipher->set_key(key);
243✔
218
   m_L = std::make_unique<L_computer>(*m_cipher);
243✔
219
}
243✔
220

221
void OCB_Mode::set_associated_data_n(size_t idx, std::span<const uint8_t> ad) {
9,689✔
222
   BOTAN_ARG_CHECK(idx == 0, "OCB: cannot handle non-zero index in set_associated_data_n");
9,689✔
223
   assert_key_material_set();
9,689✔
224
   m_ad_hash = ocb_hash(*m_L, *m_cipher, ad.data(), ad.size());
9,505✔
225
}
9,505✔
226

227
const secure_vector<uint8_t>& OCB_Mode::update_nonce(const uint8_t nonce[], size_t nonce_len) {
9,455✔
228
   const size_t BS = block_size();
9,455✔
229

230
   BOTAN_ASSERT(BS == 16 || BS == 24 || BS == 32 || BS == 64, "OCB block size is supported");
9,455✔
231

232
   const size_t MASKLEN = (BS == 16 ? 6 : ((BS == 24) ? 7 : 8));
9,455✔
233

234
   const uint8_t BOTTOM_MASK = static_cast<uint8_t>((static_cast<uint16_t>(1) << MASKLEN) - 1);
9,455✔
235

236
   m_nonce_buf.resize(BS);
9,455✔
237
   clear_mem(&m_nonce_buf[0], m_nonce_buf.size());
9,455✔
238

239
   copy_mem(&m_nonce_buf[BS - nonce_len], nonce, nonce_len);
9,455✔
240
   m_nonce_buf[0] = static_cast<uint8_t>(((tag_size() * 8) % (BS * 8)) << (BS <= 16 ? 1 : 0));
11,053✔
241

242
   m_nonce_buf[BS - nonce_len - 1] ^= 1;
9,455✔
243

244
   const uint8_t bottom = m_nonce_buf[BS - 1] & BOTTOM_MASK;
9,455✔
245
   m_nonce_buf[BS - 1] &= ~BOTTOM_MASK;
9,455✔
246

247
   const bool need_new_stretch = (m_last_nonce != m_nonce_buf);
9,455✔
248

249
   if(need_new_stretch) {
9,455✔
250
      m_last_nonce = m_nonce_buf;
681✔
251

252
      m_cipher->encrypt(m_nonce_buf);
681✔
253

254
      /*
255
      The loop bounds (BS vs BS/2) are derived from the relation
256
      between the block size and the MASKLEN. Using the terminology
257
      of draft-krovetz-ocb-wide, we have to derive enough bits in
258
      ShiftedKtop to read up to BLOCKLEN+bottom bits from Stretch.
259

260
                 +----------+---------+-------+---------+
261
                 | BLOCKLEN | RESIDUE | SHIFT | MASKLEN |
262
                 +----------+---------+-------+---------+
263
                 |       32 |     141 |    17 |    4    |
264
                 |       64 |      27 |    25 |    5    |
265
                 |       96 |    1601 |    33 |    6    |
266
                 |      128 |     135 |     8 |    6    |
267
                 |      192 |     135 |    40 |    7    |
268
                 |      256 |    1061 |     1 |    8    |
269
                 |      384 |    4109 |    80 |    8    |
270
                 |      512 |     293 |   176 |    8    |
271
                 |     1024 |  524355 |   352 |    9    |
272
                 +----------+---------+-------+---------+
273
      */
274
      if(BS == 16) {
681✔
275
         for(size_t i = 0; i != BS / 2; ++i) {
5,517✔
276
            m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i + 1]);
4,904✔
277
         }
278
      } else if(BS == 24) {
68✔
279
         for(size_t i = 0; i != 16; ++i) {
408✔
280
            m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i + 5]);
384✔
281
         }
282
      } else if(BS == 32) {
44✔
283
         for(size_t i = 0; i != BS; ++i) {
1,089✔
284
            m_nonce_buf.push_back(m_nonce_buf[i] ^ (m_nonce_buf[i] << 1) ^ (m_nonce_buf[i + 1] >> 7));
1,056✔
285
         }
286
      } else if(BS == 64) {
287
         for(size_t i = 0; i != BS / 2; ++i) {
363✔
288
            m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i + 22]);
352✔
289
         }
290
      }
291

292
      m_stretch = m_nonce_buf;
681✔
293
   }
294

295
   // now set the offset from stretch and bottom
296
   const size_t shift_bytes = bottom / 8;
9,455✔
297
   const size_t shift_bits = bottom % 8;
9,455✔
298

299
   BOTAN_ASSERT(m_stretch.size() >= BS + shift_bytes + 1, "Size ok");
9,455✔
300

301
   m_offset.resize(BS);
9,455✔
302
   for(size_t i = 0; i != BS; ++i) {
195,671✔
303
      m_offset[i] = (m_stretch[i + shift_bytes] << shift_bits);
186,216✔
304
      m_offset[i] |= (m_stretch[i + shift_bytes + 1] >> (8 - shift_bits));
186,216✔
305
   }
306

307
   return m_offset;
9,455✔
308
}
309

310
void OCB_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
9,455✔
311
   if(!valid_nonce_length(nonce_len)) {
9,455✔
312
      throw Invalid_IV_Length(name(), nonce_len);
×
313
   }
314

315
   assert_key_material_set();
9,455✔
316

317
   m_L->init(update_nonce(nonce, nonce_len));
9,455✔
318
   zeroise(m_checksum);
9,455✔
319
   m_block_index = 0;
9,455✔
320
}
9,455✔
321

322
void OCB_Encryption::encrypt(uint8_t buffer[], size_t blocks) {
4,166✔
323
   assert_key_material_set();
4,166✔
324
   BOTAN_STATE_CHECK(m_L->initialized());
4,120✔
325

326
   const size_t BS = block_size();
4,074✔
327

328
   while(blocks) {
7,731✔
329
      const size_t proc_blocks = std::min(blocks, par_blocks());
3,657✔
330
      const size_t proc_bytes = proc_blocks * BS;
3,657✔
331

332
      const uint8_t* offsets = m_L->compute_offsets(m_block_index, proc_blocks);
3,657✔
333

334
      xor_buf(m_checksum.data(), buffer, proc_bytes);
3,657✔
335

336
      m_cipher->encrypt_n_xex(buffer, offsets, proc_blocks);
3,657✔
337

338
      buffer += proc_bytes;
3,657✔
339
      blocks -= proc_blocks;
3,657✔
340
      m_block_index += proc_blocks;
3,657✔
341
   }
342
}
4,074✔
343

344
size_t OCB_Encryption::process_msg(uint8_t buf[], size_t sz) {
467✔
345
   BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid OCB input size");
467✔
346
   encrypt(buf, sz / block_size());
467✔
347
   return sz;
375✔
348
}
349

350
void OCB_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
5,707✔
351
   assert_key_material_set();
5,707✔
352
   BOTAN_STATE_CHECK(m_L->initialized());
5,615✔
353

354
   const size_t BS = block_size();
5,569✔
355

356
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
5,569✔
357
   const size_t sz = buffer.size() - offset;
5,569✔
358
   uint8_t* buf = buffer.data() + offset;
5,569✔
359

360
   secure_vector<uint8_t> mac(BS);
5,569✔
361

362
   if(sz) {
5,569✔
363
      const size_t final_full_blocks = sz / BS;
3,699✔
364
      const size_t remainder_bytes = sz - (final_full_blocks * BS);
3,699✔
365

366
      encrypt(buf, final_full_blocks);
3,699✔
367
      mac = m_L->offset();
3,699✔
368

369
      if(remainder_bytes) {
3,699✔
370
         BOTAN_ASSERT(remainder_bytes < BS, "Only a partial block left");
3,496✔
371
         uint8_t* remainder = &buf[sz - remainder_bytes];
3,496✔
372

373
         xor_buf(m_checksum.data(), remainder, remainder_bytes);
3,496✔
374
         m_checksum[remainder_bytes] ^= 0x80;
3,496✔
375

376
         // Offset_*
377
         mac ^= m_L->star();
3,496✔
378

379
         secure_vector<uint8_t> pad(BS);
3,496✔
380
         m_cipher->encrypt(mac, pad);
3,496✔
381
         xor_buf(remainder, pad.data(), remainder_bytes);
3,496✔
382
      }
3,496✔
383
   } else {
384
      mac = m_L->offset();
1,870✔
385
   }
386

387
   // now compute the tag
388

389
   // fold checksum
390
   for(size_t i = 0; i != m_checksum.size(); i += BS) {
82,305✔
391
      xor_buf(mac.data(), m_checksum.data() + i, BS);
76,736✔
392
   }
393

394
   xor_buf(mac.data(), m_L->dollar().data(), BS);
5,569✔
395
   m_cipher->encrypt(mac);
5,569✔
396
   xor_buf(mac.data(), m_ad_hash.data(), BS);
5,569✔
397

398
   buffer += std::make_pair(mac.data(), tag_size());
5,569✔
399

400
   zeroise(m_checksum);
5,569✔
401
   m_block_index = 0;
5,569✔
402
}
5,569✔
403

404
void OCB_Decryption::decrypt(uint8_t buffer[], size_t blocks) {
3,005✔
405
   assert_key_material_set();
3,005✔
406
   BOTAN_STATE_CHECK(m_L->initialized());
2,959✔
407

408
   const size_t BS = block_size();
2,913✔
409

410
   while(blocks) {
5,643✔
411
      const size_t proc_blocks = std::min(blocks, par_blocks());
2,730✔
412
      const size_t proc_bytes = proc_blocks * BS;
2,730✔
413

414
      const uint8_t* offsets = m_L->compute_offsets(m_block_index, proc_blocks);
2,730✔
415

416
      m_cipher->decrypt_n_xex(buffer, offsets, proc_blocks);
2,730✔
417

418
      xor_buf(m_checksum.data(), buffer, proc_bytes);
2,730✔
419

420
      buffer += proc_bytes;
2,730✔
421
      blocks -= proc_blocks;
2,730✔
422
      m_block_index += proc_blocks;
2,730✔
423
   }
424
}
2,913✔
425

426
size_t OCB_Decryption::process_msg(uint8_t buf[], size_t sz) {
479✔
427
   BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid OCB input size");
479✔
428
   decrypt(buf, sz / block_size());
479✔
429
   return sz;
387✔
430
}
431

432
void OCB_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
3,932✔
433
   assert_key_material_set();
3,932✔
434
   BOTAN_STATE_CHECK(m_L->initialized());
3,840✔
435

436
   const size_t BS = block_size();
3,794✔
437

438
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
3,794✔
439
   const size_t sz = buffer.size() - offset;
3,794✔
440
   uint8_t* buf = buffer.data() + offset;
3,794✔
441

442
   BOTAN_ARG_CHECK(sz >= tag_size(), "input did not include the tag");
3,794✔
443

444
   const size_t remaining = sz - tag_size();
3,794✔
445

446
   secure_vector<uint8_t> mac(BS);
3,794✔
447

448
   if(remaining) {
3,794✔
449
      const size_t final_full_blocks = remaining / BS;
2,526✔
450
      const size_t final_bytes = remaining - (final_full_blocks * BS);
2,526✔
451

452
      decrypt(buf, final_full_blocks);
2,526✔
453
      mac ^= m_L->offset();
2,526✔
454

455
      if(final_bytes) {
2,526✔
456
         BOTAN_ASSERT(final_bytes < BS, "Only a partial block left");
2,332✔
457

458
         uint8_t* remainder = &buf[remaining - final_bytes];
2,332✔
459

460
         mac ^= m_L->star();
2,332✔
461
         secure_vector<uint8_t> pad(BS);
2,470✔
462
         m_cipher->encrypt(mac, pad);  // P_*
2,332✔
463
         xor_buf(remainder, pad.data(), final_bytes);
2,332✔
464

465
         xor_buf(m_checksum.data(), remainder, final_bytes);
2,332✔
466
         m_checksum[final_bytes] ^= 0x80;
2,332✔
467
      }
2,332✔
468
   } else {
469
      mac = m_L->offset();
1,268✔
470
   }
471

472
   // compute the mac
473

474
   // fold checksum
475
   for(size_t i = 0; i != m_checksum.size(); i += BS) {
64,590✔
476
      xor_buf(mac.data(), m_checksum.data() + i, BS);
60,796✔
477
   }
478

479
   mac ^= m_L->dollar();
3,794✔
480
   m_cipher->encrypt(mac);
3,794✔
481
   mac ^= m_ad_hash;
3,794✔
482

483
   // reset state
484
   zeroise(m_checksum);
3,794✔
485
   m_block_index = 0;
3,794✔
486

487
   // compare mac
488
   const uint8_t* included_tag = &buf[remaining];
3,794✔
489

490
   if(!CT::is_equal(mac.data(), included_tag, tag_size()).as_bool()) {
7,588✔
491
      throw Invalid_Authentication_Tag("OCB tag check failed");
138✔
492
   }
493

494
   // remove tag from end of message
495
   buffer.resize(remaining + offset);
3,656✔
496
}
3,656✔
497

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