• 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

99.25
/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/poly_dbl.h>
14

15
namespace Botan {
16

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

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

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

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

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

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

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

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

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

49
         return m_L[i];
72,183✔
50
      }
51

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

55
         uint8_t* offsets = m_offset_buf.data();
6,365✔
56

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

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

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

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

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

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

82
               blocks -= 4;
3,711✔
83
            }
84
         }
85

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

93
         return m_offset_buf.data();
6,365✔
94
      }
95

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

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

110
namespace {
111

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

120
   secure_vector<uint8_t> buf(BS);
9,499✔
121

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

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

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

143
   return sum;
9,499✔
144
}
18,998✔
145

146
}  // namespace
147

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

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

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

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

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

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

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

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

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

201
size_t OCB_Mode::ideal_granularity() const { return (m_par_blocks * block_size()); }
276✔
202

203
Key_Length_Specification OCB_Mode::key_spec() const { return m_cipher->key_spec(); }
243✔
204

205
bool OCB_Mode::has_keying_material() const { return m_cipher->has_keying_material(); }
36,252✔
206

207
void OCB_Mode::key_schedule(const uint8_t key[], size_t length) {
243✔
208
   m_cipher->set_key(key, length);
243✔
209
   m_L = std::make_unique<L_computer>(*m_cipher);
243✔
210
}
243✔
211

212
void OCB_Mode::set_associated_data_n(size_t idx, std::span<const uint8_t> ad) {
9,683✔
213
   BOTAN_ARG_CHECK(idx == 0, "OCB: cannot handle non-zero index in set_associated_data_n");
9,683✔
214
   assert_key_material_set();
9,683✔
215
   m_ad_hash = ocb_hash(*m_L, *m_cipher, ad.data(), ad.size());
9,499✔
216
}
9,499✔
217

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

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

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

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

227
   m_nonce_buf.resize(BS);
9,449✔
228
   clear_mem(&m_nonce_buf[0], m_nonce_buf.size());
9,449✔
229

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

233
   m_nonce_buf[BS - nonce_len - 1] ^= 1;
9,449✔
234

235
   const uint8_t bottom = m_nonce_buf[BS - 1] & BOTTOM_MASK;
9,449✔
236
   m_nonce_buf[BS - 1] &= ~BOTTOM_MASK;
9,449✔
237

238
   const bool need_new_stretch = (m_last_nonce != m_nonce_buf);
9,449✔
239

240
   if(need_new_stretch) {
9,449✔
241
      m_last_nonce = m_nonce_buf;
681✔
242

243
      m_cipher->encrypt(m_nonce_buf);
681✔
244

245
      /*
246
      The loop bounds (BS vs BS/2) are derived from the relation
247
      between the block size and the MASKLEN. Using the terminology
248
      of draft-krovetz-ocb-wide, we have to derive enough bits in
249
      ShiftedKtop to read up to BLOCKLEN+bottom bits from Stretch.
250

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

283
      m_stretch = m_nonce_buf;
681✔
284
   }
285

286
   // now set the offset from stretch and bottom
287
   const size_t shift_bytes = bottom / 8;
9,449✔
288
   const size_t shift_bits = bottom % 8;
9,449✔
289

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

292
   m_offset.resize(BS);
9,449✔
293
   for(size_t i = 0; i != BS; ++i) {
195,569✔
294
      m_offset[i] = (m_stretch[i + shift_bytes] << shift_bits);
186,120✔
295
      m_offset[i] |= (m_stretch[i + shift_bytes + 1] >> (8 - shift_bits));
186,120✔
296
   }
297

298
   return m_offset;
9,449✔
299
}
300

301
void OCB_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
9,449✔
302
   if(!valid_nonce_length(nonce_len)) {
9,449✔
303
      throw Invalid_IV_Length(name(), nonce_len);
×
304
   }
305

306
   assert_key_material_set();
9,449✔
307

308
   m_L->init(update_nonce(nonce, nonce_len));
9,449✔
309
   zeroise(m_checksum);
9,449✔
310
   m_block_index = 0;
9,449✔
311
}
9,449✔
312

313
void OCB_Encryption::encrypt(uint8_t buffer[], size_t blocks) {
4,163✔
314
   assert_key_material_set();
4,163✔
315
   BOTAN_STATE_CHECK(m_L->initialized());
4,117✔
316

317
   const size_t BS = block_size();
4,071✔
318

319
   while(blocks) {
7,717✔
320
      const size_t proc_blocks = std::min(blocks, par_blocks());
3,646✔
321
      const size_t proc_bytes = proc_blocks * BS;
3,646✔
322

323
      const uint8_t* offsets = m_L->compute_offsets(m_block_index, proc_blocks);
3,646✔
324

325
      xor_buf(m_checksum.data(), buffer, proc_bytes);
3,646✔
326

327
      m_cipher->encrypt_n_xex(buffer, offsets, proc_blocks);
3,646✔
328

329
      buffer += proc_bytes;
3,646✔
330
      blocks -= proc_blocks;
3,646✔
331
      m_block_index += proc_blocks;
3,646✔
332
   }
333
}
4,071✔
334

335
size_t OCB_Encryption::process_msg(uint8_t buf[], size_t sz) {
467✔
336
   BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid OCB input size");
467✔
337
   encrypt(buf, sz / block_size());
467✔
338
   return sz;
375✔
339
}
340

341
void OCB_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
5,704✔
342
   assert_key_material_set();
5,704✔
343
   BOTAN_STATE_CHECK(m_L->initialized());
5,612✔
344

345
   const size_t BS = block_size();
5,566✔
346

347
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
5,566✔
348
   const size_t sz = buffer.size() - offset;
5,566✔
349
   uint8_t* buf = buffer.data() + offset;
5,566✔
350

351
   secure_vector<uint8_t> mac(BS);
5,566✔
352

353
   if(sz) {
5,566✔
354
      const size_t final_full_blocks = sz / BS;
3,696✔
355
      const size_t remainder_bytes = sz - (final_full_blocks * BS);
3,696✔
356

357
      encrypt(buf, final_full_blocks);
3,696✔
358
      mac = m_L->offset();
3,696✔
359

360
      if(remainder_bytes) {
3,696✔
361
         BOTAN_ASSERT(remainder_bytes < BS, "Only a partial block left");
3,494✔
362
         uint8_t* remainder = &buf[sz - remainder_bytes];
3,494✔
363

364
         xor_buf(m_checksum.data(), remainder, remainder_bytes);
3,494✔
365
         m_checksum[remainder_bytes] ^= 0x80;
3,494✔
366

367
         // Offset_*
368
         mac ^= m_L->star();
3,494✔
369

370
         secure_vector<uint8_t> pad(BS);
3,494✔
371
         m_cipher->encrypt(mac, pad);
3,494✔
372
         xor_buf(remainder, pad.data(), remainder_bytes);
3,494✔
373
      }
3,494✔
374
   } else {
375
      mac = m_L->offset();
1,870✔
376
   }
377

378
   // now compute the tag
379

380
   // fold checksum
381
   for(size_t i = 0; i != m_checksum.size(); i += BS) {
82,254✔
382
      xor_buf(mac.data(), m_checksum.data() + i, BS);
76,688✔
383
   }
384

385
   xor_buf(mac.data(), m_L->dollar().data(), BS);
5,566✔
386
   m_cipher->encrypt(mac);
5,566✔
387
   xor_buf(mac.data(), m_ad_hash.data(), BS);
5,566✔
388

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

391
   zeroise(m_checksum);
5,566✔
392
   m_block_index = 0;
5,566✔
393
}
5,566✔
394

395
void OCB_Decryption::decrypt(uint8_t buffer[], size_t blocks) {
3,002✔
396
   assert_key_material_set();
3,002✔
397
   BOTAN_STATE_CHECK(m_L->initialized());
2,956✔
398

399
   const size_t BS = block_size();
2,910✔
400

401
   while(blocks) {
5,629✔
402
      const size_t proc_blocks = std::min(blocks, par_blocks());
2,719✔
403
      const size_t proc_bytes = proc_blocks * BS;
2,719✔
404

405
      const uint8_t* offsets = m_L->compute_offsets(m_block_index, proc_blocks);
2,719✔
406

407
      m_cipher->decrypt_n_xex(buffer, offsets, proc_blocks);
2,719✔
408

409
      xor_buf(m_checksum.data(), buffer, proc_bytes);
2,719✔
410

411
      buffer += proc_bytes;
2,719✔
412
      blocks -= proc_blocks;
2,719✔
413
      m_block_index += proc_blocks;
2,719✔
414
   }
415
}
2,910✔
416

417
size_t OCB_Decryption::process_msg(uint8_t buf[], size_t sz) {
479✔
418
   BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid OCB input size");
479✔
419
   decrypt(buf, sz / block_size());
479✔
420
   return sz;
387✔
421
}
422

423
void OCB_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
3,929✔
424
   assert_key_material_set();
3,929✔
425
   BOTAN_STATE_CHECK(m_L->initialized());
3,837✔
426

427
   const size_t BS = block_size();
3,791✔
428

429
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
3,791✔
430
   const size_t sz = buffer.size() - offset;
3,791✔
431
   uint8_t* buf = buffer.data() + offset;
3,791✔
432

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

435
   const size_t remaining = sz - tag_size();
3,791✔
436

437
   secure_vector<uint8_t> mac(BS);
3,791✔
438

439
   if(remaining) {
3,791✔
440
      const size_t final_full_blocks = remaining / BS;
2,523✔
441
      const size_t final_bytes = remaining - (final_full_blocks * BS);
2,523✔
442

443
      decrypt(buf, final_full_blocks);
2,523✔
444
      mac ^= m_L->offset();
2,523✔
445

446
      if(final_bytes) {
2,523✔
447
         BOTAN_ASSERT(final_bytes < BS, "Only a partial block left");
2,329✔
448

449
         uint8_t* remainder = &buf[remaining - final_bytes];
2,329✔
450

451
         mac ^= m_L->star();
2,329✔
452
         secure_vector<uint8_t> pad(BS);
2,467✔
453
         m_cipher->encrypt(mac, pad);  // P_*
2,329✔
454
         xor_buf(remainder, pad.data(), final_bytes);
2,329✔
455

456
         xor_buf(m_checksum.data(), remainder, final_bytes);
2,329✔
457
         m_checksum[final_bytes] ^= 0x80;
2,329✔
458
      }
2,329✔
459
   } else {
460
      mac = m_L->offset();
1,268✔
461
   }
462

463
   // compute the mac
464

465
   // fold checksum
466
   for(size_t i = 0; i != m_checksum.size(); i += BS) {
64,539✔
467
      xor_buf(mac.data(), m_checksum.data() + i, BS);
60,748✔
468
   }
469

470
   mac ^= m_L->dollar();
3,791✔
471
   m_cipher->encrypt(mac);
3,791✔
472
   mac ^= m_ad_hash;
3,791✔
473

474
   // reset state
475
   zeroise(m_checksum);
3,791✔
476
   m_block_index = 0;
3,791✔
477

478
   // compare mac
479
   const uint8_t* included_tag = &buf[remaining];
3,791✔
480

481
   if(!constant_time_compare(mac.data(), included_tag, tag_size())) {
3,791✔
482
      throw Invalid_Authentication_Tag("OCB tag check failed");
138✔
483
   }
484

485
   // remove tag from end of message
486
   buffer.resize(remaining + offset);
3,653✔
487
}
3,653✔
488

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