Botan 3.13.0
Crypto and TLS for C&
ocb.cpp
Go to the documentation of this file.
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
19namespace Botan {
20
21// Has to be in Botan namespace so unique_ptr can reference it
22class L_computer final {
23 public:
24 explicit L_computer(const BlockCipher& cipher) :
25 m_BS(cipher.block_size()), m_max_blocks(cipher.parallel_bytes() / m_BS) {
26 m_L_star.resize(m_BS);
27 cipher.encrypt(m_L_star);
28 m_L_dollar = poly_double(star());
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);
40 m_L.push_back(poly_double(dollar()));
41
42 while(m_L.size() < 8) {
43 m_L.push_back(poly_double(m_L.back()));
44 }
45
46 m_offset_buf.resize(m_BS * m_max_blocks);
47 }
48
49 void init(const secure_vector<uint8_t>& offset) { m_offset = offset; }
50
51 void reset() { m_offset.clear(); }
52
53 bool initialized() const { return !m_offset.empty(); }
54
55 const secure_vector<uint8_t>& star() const { return m_L_star; }
56
57 const secure_vector<uint8_t>& dollar() const { return m_L_dollar; }
58
59 const secure_vector<uint8_t>& offset() const { return m_offset; }
60
61 const secure_vector<uint8_t>& get(size_t i) const {
62 while(m_L.size() <= i) {
63 m_L.push_back(poly_double(m_L.back()));
64 }
65
66 return m_L[i];
67 }
68
69 const uint8_t* compute_offsets(uint64_t block_index, size_t blocks) {
70 BOTAN_ASSERT(blocks <= m_max_blocks, "OCB offsets");
71
72 uint8_t* offsets = m_offset_buf.data();
73
74 if(block_index % 4 == 0) {
75 const secure_vector<uint8_t>& L0 = get(0);
76 const secure_vector<uint8_t>& L1 = get(1);
77
78 while(blocks >= 4) {
79 // ntz(4*i+1) == 0
80 // ntz(4*i+2) == 1
81 // ntz(4*i+3) == 0
82 block_index += 4;
83 const size_t ntz4 = var_ctz64(block_index);
84
85 xor_buf(offsets, m_offset.data(), L0.data(), m_BS);
86 offsets += m_BS;
87
88 xor_buf(offsets, offsets - m_BS, L1.data(), m_BS);
89 offsets += m_BS;
90
91 xor_buf(m_offset.data(), L1.data(), m_BS);
92 copy_mem(offsets, m_offset.data(), m_BS);
93 offsets += m_BS;
94
95 xor_buf(m_offset.data(), get(ntz4).data(), m_BS);
96 copy_mem(offsets, m_offset.data(), m_BS);
97 offsets += m_BS;
98
99 blocks -= 4;
100 }
101 }
102
103 for(size_t i = 0; i != blocks; ++i) { // could be done in parallel
104 const size_t ntz = var_ctz64(block_index + i + 1);
105 xor_buf(m_offset.data(), get(ntz).data(), m_BS);
106 copy_mem(offsets, m_offset.data(), m_BS);
107 offsets += m_BS;
108 }
109
110 return m_offset_buf.data();
111 }
112
113 private:
114 static secure_vector<uint8_t> poly_double(const secure_vector<uint8_t>& in) {
115 secure_vector<uint8_t> out(in.size());
116 poly_double_n(out.data(), in.data(), out.size());
117 return out;
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
127namespace {
128
129/*
130* OCB's HASH
131*/
132secure_vector<uint8_t> ocb_hash(const L_computer& L, const BlockCipher& cipher, const uint8_t ad[], size_t ad_len) {
133 const size_t BS = cipher.block_size();
135 secure_vector<uint8_t> offset(BS);
136
138
139 const size_t ad_blocks = (ad_len / BS);
140 const size_t ad_remainder = (ad_len % BS);
141
142 for(size_t i = 0; i != ad_blocks; ++i) {
143 // this loop could run in parallel
144 offset ^= L.get(var_ctz64(i + 1));
145 buf = offset;
146 xor_buf(buf.data(), &ad[BS * i], BS);
147 cipher.encrypt(buf);
148 sum ^= buf;
149 }
150
151 if(ad_remainder > 0) {
152 offset ^= L.star();
153 buf = offset;
154 xor_buf(buf.data(), &ad[BS * ad_blocks], ad_remainder);
155 buf[ad_remainder] ^= 0x80;
156 cipher.encrypt(buf);
157 sum ^= buf;
158 }
159
160 return sum;
161}
162
163} // namespace
164
165OCB_Mode::OCB_Mode(std::unique_ptr<BlockCipher> cipher, size_t tag_size) :
166 m_cipher(std::move(cipher)),
167 m_checksum(m_cipher->parallel_bytes()),
169 m_tag_size(tag_size),
170 m_block_size(m_cipher->block_size()),
171 m_par_blocks(m_cipher->parallel_bytes() / m_block_size) {
172 const size_t BS = block_size();
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");
180
181 BOTAN_ARG_CHECK(m_tag_size % 4 == 0 && m_tag_size >= 8 && m_tag_size <= BS && m_tag_size <= 32,
182 "Invalid OCB tag length");
183}
184
185OCB_Mode::~OCB_Mode() = default;
186
188 m_cipher->clear();
189 m_L.reset();
191 reset();
192}
193
195 m_block_index = 0;
197 m_last_nonce.clear();
198 m_stretch.clear();
199 zeroise(m_nonce_buf);
200 zeroise(m_offset);
201 if(m_L) {
202 m_L->reset();
203 }
204}
205
206bool OCB_Mode::valid_nonce_length(size_t length) const {
207 if(length == 0) {
208 return false;
209 }
210 if(block_size() == 16) {
211 return length < 16;
212 } else {
213 return length < (block_size() - 1);
214 }
215}
216
217std::string OCB_Mode::name() const {
218 return m_cipher->name() + "/OCB"; // include tag size?
219}
220
222 return block_size();
223}
224
226 return (m_par_blocks * block_size());
227}
228
230 return m_cipher->key_spec();
231}
232
234 return m_cipher->has_keying_material();
235}
236
237void OCB_Mode::key_schedule(std::span<const uint8_t> key) {
238 m_cipher->set_key(key);
239 m_L = std::make_unique<L_computer>(*m_cipher);
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();
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.
250}
251
252void OCB_Mode::set_associated_data_n(size_t idx, std::span<const uint8_t> ad) {
253 BOTAN_ARG_CHECK(idx == 0, "OCB: cannot handle non-zero index in set_associated_data_n");
255 BOTAN_STATE_CHECK(!m_L->initialized());
256 m_ad_hash = ocb_hash(*m_L, *m_cipher, ad.data(), ad.size());
257}
258
259const secure_vector<uint8_t>& OCB_Mode::update_nonce(const uint8_t nonce[], size_t nonce_len) {
260 const size_t BS = block_size();
261
262 BOTAN_ASSERT(BS == 16 || BS == 24 || BS == 32 || BS == 64, "OCB block size is supported");
263
264 // NOLINTNEXTLINE(readability-avoid-nested-conditional-operator)
265 const size_t MASKLEN = (BS == 16 ? 6 : ((BS == 24) ? 7 : 8));
266
267 const uint8_t BOTTOM_MASK = static_cast<uint8_t>((static_cast<uint16_t>(1) << MASKLEN) - 1);
268
269 m_nonce_buf.resize(BS);
270 clear_mem(m_nonce_buf.data(), m_nonce_buf.size());
271
272 copy_mem(&m_nonce_buf[BS - nonce_len], nonce, nonce_len);
273 m_nonce_buf[0] = static_cast<uint8_t>(((tag_size() * 8) % (BS * 8)) << (BS <= 16 ? 1 : 0));
274
275 m_nonce_buf[BS - nonce_len - 1] ^= 1;
276
277 const uint8_t bottom = m_nonce_buf[BS - 1] & BOTTOM_MASK;
278 m_nonce_buf[BS - 1] &= ~BOTTOM_MASK;
279
280 const bool need_new_stretch = (m_last_nonce != m_nonce_buf);
281
282 if(need_new_stretch) {
283 m_last_nonce = m_nonce_buf;
284
285 m_cipher->encrypt(m_nonce_buf);
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) {
308 for(size_t i = 0; i != BS / 2; ++i) {
309 m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i + 1]);
310 }
311 } else if(BS == 24) {
312 for(size_t i = 0; i != 16; ++i) {
313 m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i + 5]);
314 }
315 } else if(BS == 32) {
316 for(size_t i = 0; i != BS; ++i) {
317 m_nonce_buf.push_back(m_nonce_buf[i] ^ (m_nonce_buf[i] << 1) ^ (m_nonce_buf[i + 1] >> 7));
318 }
319 } else if(BS == 64) {
320 for(size_t i = 0; i != BS / 2; ++i) {
321 m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i + 22]);
322 }
323 }
324
325 m_stretch = m_nonce_buf;
326 }
327
328 // now set the offset from stretch and bottom
329 const size_t shift_bytes = bottom / 8;
330 const size_t shift_bits = bottom % 8;
331
332 BOTAN_ASSERT(m_stretch.size() >= BS + shift_bytes + 1, "Size ok");
333
334 m_offset.resize(BS);
335 for(size_t i = 0; i != BS; ++i) {
336 m_offset[i] = (m_stretch[i + shift_bytes] << shift_bits);
337 m_offset[i] |= (m_stretch[i + shift_bytes + 1] >> (8 - shift_bits));
338 }
339
340 return m_offset;
341}
342
343void OCB_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
344 if(!valid_nonce_length(nonce_len)) {
345 throw Invalid_IV_Length(name(), nonce_len);
346 }
347
349 BOTAN_STATE_CHECK(!m_L->initialized());
350
351 m_L->init(update_nonce(nonce, nonce_len));
353 m_block_index = 0;
354}
355
356size_t OCB_Encryption::output_length(size_t input_length) const {
357 return add_or_throw(input_length, tag_size(), "OCB input too large");
358}
359
360void OCB_Encryption::encrypt(uint8_t buffer[], size_t blocks) {
362 BOTAN_STATE_CHECK(m_L->initialized());
363
364 const size_t BS = block_size();
365
366 while(blocks > 0) {
367 const size_t proc_blocks = std::min(blocks, par_blocks());
368 const size_t proc_bytes = proc_blocks * BS;
369
370 const uint8_t* offsets = m_L->compute_offsets(m_block_index, proc_blocks);
371
372 xor_buf(m_checksum.data(), buffer, proc_bytes);
373
374 xor_buf(buffer, offsets, proc_bytes);
375 m_cipher->encrypt_n(buffer, buffer, proc_blocks);
376 xor_buf(buffer, offsets, proc_bytes);
377
378 buffer += proc_bytes;
379 blocks -= proc_blocks;
380 m_block_index += proc_blocks;
381 }
382}
383
384size_t OCB_Encryption::process_msg(uint8_t buf[], size_t sz) {
385 BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid OCB input size");
386 encrypt(buf, sz / block_size());
387 return sz;
388}
389
390void OCB_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
392 BOTAN_STATE_CHECK(m_L->initialized());
393
394 const size_t BS = block_size();
395
396 BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
397 const size_t sz = buffer.size() - offset;
398 uint8_t* buf = buffer.data() + offset;
399
401
402 if(sz > 0) {
403 const size_t final_full_blocks = sz / BS;
404 const size_t remainder_bytes = sz - (final_full_blocks * BS);
405
406 encrypt(buf, final_full_blocks);
407 mac = m_L->offset();
408
409 if(remainder_bytes > 0) {
410 BOTAN_ASSERT(remainder_bytes < BS, "Only a partial block left");
411 uint8_t* remainder = &buf[sz - remainder_bytes];
412
413 xor_buf(m_checksum.data(), remainder, remainder_bytes);
414 m_checksum[remainder_bytes] ^= 0x80;
415
416 // Offset_*
417 mac ^= m_L->star();
418
420 m_cipher->encrypt(mac, pad);
421 xor_buf(remainder, pad.data(), remainder_bytes);
422 }
423 } else {
424 mac = m_L->offset();
425 }
426
427 // now compute the tag
428
429 // fold checksum
430 for(size_t i = 0; i != m_checksum.size(); i += BS) {
431 xor_buf(mac.data(), m_checksum.data() + i, BS);
432 }
433
434 xor_buf(mac.data(), m_L->dollar().data(), BS);
435 m_cipher->encrypt(mac);
436 xor_buf(mac.data(), m_ad_hash.data(), BS);
437
438 buffer += std::make_pair(mac.data(), tag_size());
439
440 reset();
441}
442
443size_t OCB_Decryption::output_length(size_t input_length) const {
444 BOTAN_ARG_CHECK(input_length >= tag_size(), "Message too short to be valid");
445 return input_length - tag_size();
446}
447
448void OCB_Decryption::decrypt(uint8_t buffer[], size_t blocks) {
450 BOTAN_STATE_CHECK(m_L->initialized());
451
452 const size_t BS = block_size();
453
454 while(blocks > 0) {
455 const size_t proc_blocks = std::min(blocks, par_blocks());
456 const size_t proc_bytes = proc_blocks * BS;
457
458 const uint8_t* offsets = m_L->compute_offsets(m_block_index, proc_blocks);
459
460 xor_buf(buffer, offsets, proc_bytes);
461 m_cipher->decrypt_n(buffer, buffer, proc_blocks);
462 xor_buf(buffer, offsets, proc_bytes);
463
464 xor_buf(m_checksum.data(), buffer, proc_bytes);
465
466 buffer += proc_bytes;
467 blocks -= proc_blocks;
468 m_block_index += proc_blocks;
469 }
470}
471
472size_t OCB_Decryption::process_msg(uint8_t buf[], size_t sz) {
473 BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid OCB input size");
474 decrypt(buf, sz / block_size());
475 return sz;
476}
477
478void OCB_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
480 BOTAN_STATE_CHECK(m_L->initialized());
481
482 const size_t BS = block_size();
483
484 BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
485 const size_t sz = buffer.size() - offset;
486 uint8_t* buf = buffer.data() + offset;
487
488 BOTAN_ARG_CHECK(sz >= tag_size(), "input did not include the tag");
489
490 const size_t remaining = sz - tag_size();
491
493
494 if(remaining > 0) {
495 const size_t final_full_blocks = remaining / BS;
496 const size_t final_bytes = remaining - (final_full_blocks * BS);
497
498 decrypt(buf, final_full_blocks);
499 mac ^= m_L->offset();
500
501 if(final_bytes > 0) {
502 BOTAN_ASSERT(final_bytes < BS, "Only a partial block left");
503
504 uint8_t* remainder = &buf[remaining - final_bytes];
505
506 mac ^= m_L->star();
508 m_cipher->encrypt(mac, pad); // P_*
509 xor_buf(remainder, pad.data(), final_bytes);
510
511 xor_buf(m_checksum.data(), remainder, final_bytes);
512 m_checksum[final_bytes] ^= 0x80;
513 }
514 } else {
515 mac = m_L->offset();
516 }
517
518 // compute the mac
519
520 // fold checksum
521 for(size_t i = 0; i != m_checksum.size(); i += BS) {
522 xor_buf(mac.data(), m_checksum.data() + i, BS);
523 }
524
525 mac ^= m_L->dollar();
526 m_cipher->encrypt(mac);
527 mac ^= m_ad_hash;
528
529 reset();
530
531 // compare mac
532 const uint8_t* included_tag = &buf[remaining];
533
534 if(!CT::is_equal(mac.data(), included_tag, tag_size()).as_bool()) {
535 clear_mem(std::span{buffer}.subspan(offset, remaining));
536 throw Invalid_Authentication_Tag("OCB tag check failed");
537 }
538
539 // remove tag from end of message
540 buffer.resize(remaining + offset);
541}
542
543} // namespace Botan
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:49
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:62
size_t output_length(size_t input_length) const override
Definition ocb.cpp:443
size_t output_length(size_t input_length) const override
Definition ocb.cpp:356
uint64_t m_block_index
Definition ocb.h:74
size_t block_size() const
Definition ocb.h:64
size_t par_blocks() const
Definition ocb.h:66
Key_Length_Specification key_spec() const final
Definition ocb.cpp:229
size_t tag_size() const final
Definition ocb.h:47
secure_vector< uint8_t > m_checksum
Definition ocb.h:76
~OCB_Mode() override
std::string name() const final
Definition ocb.cpp:217
bool valid_nonce_length(size_t length) const final
Definition ocb.cpp:206
std::unique_ptr< BlockCipher > m_cipher
Definition ocb.h:71
secure_vector< uint8_t > m_ad_hash
Definition ocb.h:77
size_t ideal_granularity() const final
Definition ocb.cpp:225
bool has_keying_material() const final
Definition ocb.cpp:233
void clear() final
Definition ocb.cpp:187
void reset() final
Definition ocb.cpp:194
OCB_Mode(std::unique_ptr< BlockCipher > cipher, size_t tag_size)
Definition ocb.cpp:165
void set_associated_data_n(size_t idx, std::span< const uint8_t > ad) final
Definition ocb.cpp:252
size_t update_granularity() const final
Definition ocb.cpp:221
std::unique_ptr< L_computer > m_L
Definition ocb.h:72
void assert_key_material_set() const
Definition sym_algo.h:180
constexpr CT::Mask< T > is_equal(const T x[], const T y[], size_t len)
Definition ct_utils.h:798
constexpr T add_or_throw(T a, T b, std::string_view msg)
Definition int_utils.h:66
void zeroise(std::vector< T, Alloc > &vec)
Definition secmem.h:241
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:144
BOTAN_FORCE_INLINE constexpr size_t var_ctz64(uint64_t n)
Definition bit_ops.h:180
constexpr void xor_buf(ranges::contiguous_output_range< uint8_t > auto &&out, ranges::contiguous_range< uint8_t > auto &&in)
Definition mem_ops.h:403
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:128
void poly_double_n(uint8_t out[], const uint8_t in[], size_t n)
Definition poly_dbl.cpp:81
constexpr void clear_mem(T *ptr, size_t n)
Definition mem_ops.h:118