Botan 3.3.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/internal/bit_ops.h>
13#include <botan/internal/ct_utils.h>
14#include <botan/internal/poly_dbl.h>
15
16namespace Botan {
17
18// Has to be in Botan namespace so unique_ptr can reference it
19class L_computer final {
20 public:
21 explicit L_computer(const BlockCipher& cipher) :
22 m_BS(cipher.block_size()), m_max_blocks(cipher.parallel_bytes() / m_BS) {
23 m_L_star.resize(m_BS);
24 cipher.encrypt(m_L_star);
25 m_L_dollar = poly_double(star());
26
27 // Preallocate the m_L vector to the maximum expected size to avoid
28 // re-allocations during runtime. This had caused a use-after-free in
29 // earlier versions, due to references into this buffer becoming stale
30 // in `compute_offset()`, after calling `get()` in the hot path.
31 //
32 // Note, that the list member won't be pre-allocated, so the expected
33 // memory overhead is negligible.
34 //
35 // See also https://github.com/randombit/botan/issues/3812
36 m_L.reserve(31);
37 m_L.push_back(poly_double(dollar()));
38
39 while(m_L.size() < 8) {
40 m_L.push_back(poly_double(m_L.back()));
41 }
42
43 m_offset_buf.resize(m_BS * m_max_blocks);
44 }
45
46 void init(const secure_vector<uint8_t>& offset) { m_offset = offset; }
47
48 bool initialized() const { return m_offset.empty() == false; }
49
50 const secure_vector<uint8_t>& star() const { return m_L_star; }
51
52 const secure_vector<uint8_t>& dollar() const { return m_L_dollar; }
53
54 const secure_vector<uint8_t>& offset() const { return m_offset; }
55
56 const secure_vector<uint8_t>& get(size_t i) const {
57 while(m_L.size() <= i) {
58 m_L.push_back(poly_double(m_L.back()));
59 }
60
61 return m_L[i];
62 }
63
64 const uint8_t* compute_offsets(size_t block_index, size_t blocks) {
65 BOTAN_ASSERT(blocks <= m_max_blocks, "OCB offsets");
66
67 uint8_t* offsets = m_offset_buf.data();
68
69 if(block_index % 4 == 0) {
70 const secure_vector<uint8_t>& L0 = get(0);
71 const secure_vector<uint8_t>& L1 = get(1);
72
73 while(blocks >= 4) {
74 // ntz(4*i+1) == 0
75 // ntz(4*i+2) == 1
76 // ntz(4*i+3) == 0
77 block_index += 4;
78 const size_t ntz4 = var_ctz32(static_cast<uint32_t>(block_index));
79
80 xor_buf(offsets, m_offset.data(), L0.data(), m_BS);
81 offsets += m_BS;
82
83 xor_buf(offsets, offsets - m_BS, L1.data(), m_BS);
84 offsets += m_BS;
85
86 xor_buf(m_offset.data(), L1.data(), m_BS);
87 copy_mem(offsets, m_offset.data(), m_BS);
88 offsets += m_BS;
89
90 xor_buf(m_offset.data(), get(ntz4).data(), m_BS);
91 copy_mem(offsets, m_offset.data(), m_BS);
92 offsets += m_BS;
93
94 blocks -= 4;
95 }
96 }
97
98 for(size_t i = 0; i != blocks; ++i) { // could be done in parallel
99 const size_t ntz = var_ctz32(static_cast<uint32_t>(block_index + i + 1));
100 xor_buf(m_offset.data(), get(ntz).data(), m_BS);
101 copy_mem(offsets, m_offset.data(), m_BS);
102 offsets += m_BS;
103 }
104
105 return m_offset_buf.data();
106 }
107
108 private:
109 static secure_vector<uint8_t> poly_double(const secure_vector<uint8_t>& in) {
110 secure_vector<uint8_t> out(in.size());
111 poly_double_n(out.data(), in.data(), out.size());
112 return out;
113 }
114
115 const size_t m_BS, m_max_blocks;
116 secure_vector<uint8_t> m_L_dollar, m_L_star;
117 secure_vector<uint8_t> m_offset;
118 mutable std::vector<secure_vector<uint8_t>> m_L;
119 secure_vector<uint8_t> m_offset_buf;
120};
121
122namespace {
123
124/*
125* OCB's HASH
126*/
127secure_vector<uint8_t> ocb_hash(const L_computer& L, const BlockCipher& cipher, const uint8_t ad[], size_t ad_len) {
128 const size_t BS = cipher.block_size();
129 secure_vector<uint8_t> sum(BS);
130 secure_vector<uint8_t> offset(BS);
131
132 secure_vector<uint8_t> buf(BS);
133
134 const size_t ad_blocks = (ad_len / BS);
135 const size_t ad_remainder = (ad_len % BS);
136
137 for(size_t i = 0; i != ad_blocks; ++i) {
138 // this loop could run in parallel
139 offset ^= L.get(var_ctz32(static_cast<uint32_t>(i + 1)));
140 buf = offset;
141 xor_buf(buf.data(), &ad[BS * i], BS);
142 cipher.encrypt(buf);
143 sum ^= buf;
144 }
145
146 if(ad_remainder) {
147 offset ^= L.star();
148 buf = offset;
149 xor_buf(buf.data(), &ad[BS * ad_blocks], ad_remainder);
150 buf[ad_remainder] ^= 0x80;
151 cipher.encrypt(buf);
152 sum ^= buf;
153 }
154
155 return sum;
156}
157
158} // namespace
159
160OCB_Mode::OCB_Mode(std::unique_ptr<BlockCipher> cipher, size_t tag_size) :
161 m_cipher(std::move(cipher)),
162 m_checksum(m_cipher->parallel_bytes()),
163 m_ad_hash(m_cipher->block_size()),
164 m_tag_size(tag_size),
165 m_block_size(m_cipher->block_size()),
166 m_par_blocks(m_cipher->parallel_bytes() / m_block_size) {
167 const size_t BS = block_size();
168
169 /*
170 * draft-krovetz-ocb-wide-d1 specifies OCB for several other block
171 * sizes but only 128, 192, 256 and 512 bit are currently supported
172 * by this implementation.
173 */
174 BOTAN_ARG_CHECK(BS == 16 || BS == 24 || BS == 32 || BS == 64, "Invalid block size for OCB");
175
176 BOTAN_ARG_CHECK(m_tag_size % 4 == 0 && m_tag_size >= 8 && m_tag_size <= BS && m_tag_size <= 32,
177 "Invalid OCB tag length");
178}
179
180OCB_Mode::~OCB_Mode() = default;
181
183 m_cipher->clear();
184 m_L.reset(); // add clear here?
185 reset();
186}
187
189 m_block_index = 0;
192 m_last_nonce.clear();
193 m_stretch.clear();
194}
195
196bool OCB_Mode::valid_nonce_length(size_t length) const {
197 if(length == 0) {
198 return false;
199 }
200 if(block_size() == 16) {
201 return length < 16;
202 } else {
203 return length < (block_size() - 1);
204 }
205}
206
207std::string OCB_Mode::name() const {
208 return m_cipher->name() + "/OCB"; // include tag size?
209}
210
212 return block_size();
213}
214
216 return (m_par_blocks * block_size());
217}
218
220 return m_cipher->key_spec();
221}
222
224 return m_cipher->has_keying_material();
225}
226
227void OCB_Mode::key_schedule(std::span<const uint8_t> key) {
228 m_cipher->set_key(key);
229 m_L = std::make_unique<L_computer>(*m_cipher);
230}
231
232void OCB_Mode::set_associated_data_n(size_t idx, std::span<const uint8_t> ad) {
233 BOTAN_ARG_CHECK(idx == 0, "OCB: cannot handle non-zero index in set_associated_data_n");
235 m_ad_hash = ocb_hash(*m_L, *m_cipher, ad.data(), ad.size());
236}
237
238const secure_vector<uint8_t>& OCB_Mode::update_nonce(const uint8_t nonce[], size_t nonce_len) {
239 const size_t BS = block_size();
240
241 BOTAN_ASSERT(BS == 16 || BS == 24 || BS == 32 || BS == 64, "OCB block size is supported");
242
243 const size_t MASKLEN = (BS == 16 ? 6 : ((BS == 24) ? 7 : 8));
244
245 const uint8_t BOTTOM_MASK = static_cast<uint8_t>((static_cast<uint16_t>(1) << MASKLEN) - 1);
246
247 m_nonce_buf.resize(BS);
248 clear_mem(&m_nonce_buf[0], m_nonce_buf.size());
249
250 copy_mem(&m_nonce_buf[BS - nonce_len], nonce, nonce_len);
251 m_nonce_buf[0] = static_cast<uint8_t>(((tag_size() * 8) % (BS * 8)) << (BS <= 16 ? 1 : 0));
252
253 m_nonce_buf[BS - nonce_len - 1] ^= 1;
254
255 const uint8_t bottom = m_nonce_buf[BS - 1] & BOTTOM_MASK;
256 m_nonce_buf[BS - 1] &= ~BOTTOM_MASK;
257
258 const bool need_new_stretch = (m_last_nonce != m_nonce_buf);
259
260 if(need_new_stretch) {
261 m_last_nonce = m_nonce_buf;
262
263 m_cipher->encrypt(m_nonce_buf);
264
265 /*
266 The loop bounds (BS vs BS/2) are derived from the relation
267 between the block size and the MASKLEN. Using the terminology
268 of draft-krovetz-ocb-wide, we have to derive enough bits in
269 ShiftedKtop to read up to BLOCKLEN+bottom bits from Stretch.
270
271 +----------+---------+-------+---------+
272 | BLOCKLEN | RESIDUE | SHIFT | MASKLEN |
273 +----------+---------+-------+---------+
274 | 32 | 141 | 17 | 4 |
275 | 64 | 27 | 25 | 5 |
276 | 96 | 1601 | 33 | 6 |
277 | 128 | 135 | 8 | 6 |
278 | 192 | 135 | 40 | 7 |
279 | 256 | 1061 | 1 | 8 |
280 | 384 | 4109 | 80 | 8 |
281 | 512 | 293 | 176 | 8 |
282 | 1024 | 524355 | 352 | 9 |
283 +----------+---------+-------+---------+
284 */
285 if(BS == 16) {
286 for(size_t i = 0; i != BS / 2; ++i) {
287 m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i + 1]);
288 }
289 } else if(BS == 24) {
290 for(size_t i = 0; i != 16; ++i) {
291 m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i + 5]);
292 }
293 } else if(BS == 32) {
294 for(size_t i = 0; i != BS; ++i) {
295 m_nonce_buf.push_back(m_nonce_buf[i] ^ (m_nonce_buf[i] << 1) ^ (m_nonce_buf[i + 1] >> 7));
296 }
297 } else if(BS == 64) {
298 for(size_t i = 0; i != BS / 2; ++i) {
299 m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i + 22]);
300 }
301 }
302
303 m_stretch = m_nonce_buf;
304 }
305
306 // now set the offset from stretch and bottom
307 const size_t shift_bytes = bottom / 8;
308 const size_t shift_bits = bottom % 8;
309
310 BOTAN_ASSERT(m_stretch.size() >= BS + shift_bytes + 1, "Size ok");
311
312 m_offset.resize(BS);
313 for(size_t i = 0; i != BS; ++i) {
314 m_offset[i] = (m_stretch[i + shift_bytes] << shift_bits);
315 m_offset[i] |= (m_stretch[i + shift_bytes + 1] >> (8 - shift_bits));
316 }
317
318 return m_offset;
319}
320
321void OCB_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
322 if(!valid_nonce_length(nonce_len)) {
323 throw Invalid_IV_Length(name(), nonce_len);
324 }
325
327
328 m_L->init(update_nonce(nonce, nonce_len));
330 m_block_index = 0;
331}
332
333void OCB_Encryption::encrypt(uint8_t buffer[], size_t blocks) {
335 BOTAN_STATE_CHECK(m_L->initialized());
336
337 const size_t BS = block_size();
338
339 while(blocks) {
340 const size_t proc_blocks = std::min(blocks, par_blocks());
341 const size_t proc_bytes = proc_blocks * BS;
342
343 const uint8_t* offsets = m_L->compute_offsets(m_block_index, proc_blocks);
344
345 xor_buf(m_checksum.data(), buffer, proc_bytes);
346
347 m_cipher->encrypt_n_xex(buffer, offsets, proc_blocks);
348
349 buffer += proc_bytes;
350 blocks -= proc_blocks;
351 m_block_index += proc_blocks;
352 }
353}
354
355size_t OCB_Encryption::process_msg(uint8_t buf[], size_t sz) {
356 BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid OCB input size");
357 encrypt(buf, sz / block_size());
358 return sz;
359}
360
361void OCB_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
363 BOTAN_STATE_CHECK(m_L->initialized());
364
365 const size_t BS = block_size();
366
367 BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
368 const size_t sz = buffer.size() - offset;
369 uint8_t* buf = buffer.data() + offset;
370
371 secure_vector<uint8_t> mac(BS);
372
373 if(sz) {
374 const size_t final_full_blocks = sz / BS;
375 const size_t remainder_bytes = sz - (final_full_blocks * BS);
376
377 encrypt(buf, final_full_blocks);
378 mac = m_L->offset();
379
380 if(remainder_bytes) {
381 BOTAN_ASSERT(remainder_bytes < BS, "Only a partial block left");
382 uint8_t* remainder = &buf[sz - remainder_bytes];
383
384 xor_buf(m_checksum.data(), remainder, remainder_bytes);
385 m_checksum[remainder_bytes] ^= 0x80;
386
387 // Offset_*
388 mac ^= m_L->star();
389
390 secure_vector<uint8_t> pad(BS);
391 m_cipher->encrypt(mac, pad);
392 xor_buf(remainder, pad.data(), remainder_bytes);
393 }
394 } else {
395 mac = m_L->offset();
396 }
397
398 // now compute the tag
399
400 // fold checksum
401 for(size_t i = 0; i != m_checksum.size(); i += BS) {
402 xor_buf(mac.data(), m_checksum.data() + i, BS);
403 }
404
405 xor_buf(mac.data(), m_L->dollar().data(), BS);
406 m_cipher->encrypt(mac);
407 xor_buf(mac.data(), m_ad_hash.data(), BS);
408
409 buffer += std::make_pair(mac.data(), tag_size());
410
412 m_block_index = 0;
413}
414
415void OCB_Decryption::decrypt(uint8_t buffer[], size_t blocks) {
417 BOTAN_STATE_CHECK(m_L->initialized());
418
419 const size_t BS = block_size();
420
421 while(blocks) {
422 const size_t proc_blocks = std::min(blocks, par_blocks());
423 const size_t proc_bytes = proc_blocks * BS;
424
425 const uint8_t* offsets = m_L->compute_offsets(m_block_index, proc_blocks);
426
427 m_cipher->decrypt_n_xex(buffer, offsets, proc_blocks);
428
429 xor_buf(m_checksum.data(), buffer, proc_bytes);
430
431 buffer += proc_bytes;
432 blocks -= proc_blocks;
433 m_block_index += proc_blocks;
434 }
435}
436
437size_t OCB_Decryption::process_msg(uint8_t buf[], size_t sz) {
438 BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid OCB input size");
439 decrypt(buf, sz / block_size());
440 return sz;
441}
442
443void OCB_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
445 BOTAN_STATE_CHECK(m_L->initialized());
446
447 const size_t BS = block_size();
448
449 BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
450 const size_t sz = buffer.size() - offset;
451 uint8_t* buf = buffer.data() + offset;
452
453 BOTAN_ARG_CHECK(sz >= tag_size(), "input did not include the tag");
454
455 const size_t remaining = sz - tag_size();
456
457 secure_vector<uint8_t> mac(BS);
458
459 if(remaining) {
460 const size_t final_full_blocks = remaining / BS;
461 const size_t final_bytes = remaining - (final_full_blocks * BS);
462
463 decrypt(buf, final_full_blocks);
464 mac ^= m_L->offset();
465
466 if(final_bytes) {
467 BOTAN_ASSERT(final_bytes < BS, "Only a partial block left");
468
469 uint8_t* remainder = &buf[remaining - final_bytes];
470
471 mac ^= m_L->star();
472 secure_vector<uint8_t> pad(BS);
473 m_cipher->encrypt(mac, pad); // P_*
474 xor_buf(remainder, pad.data(), final_bytes);
475
476 xor_buf(m_checksum.data(), remainder, final_bytes);
477 m_checksum[final_bytes] ^= 0x80;
478 }
479 } else {
480 mac = m_L->offset();
481 }
482
483 // compute the mac
484
485 // fold checksum
486 for(size_t i = 0; i != m_checksum.size(); i += BS) {
487 xor_buf(mac.data(), m_checksum.data() + i, BS);
488 }
489
490 mac ^= m_L->dollar();
491 m_cipher->encrypt(mac);
492 mac ^= m_ad_hash;
493
494 // reset state
496 m_block_index = 0;
497
498 // compare mac
499 const uint8_t* included_tag = &buf[remaining];
500
501 if(!CT::is_equal(mac.data(), included_tag, tag_size()).as_bool()) {
502 throw Invalid_Authentication_Tag("OCB tag check failed");
503 }
504
505 // remove tag from end of message
506 buffer.resize(remaining + offset);
507}
508
509} // namespace Botan
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:41
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:29
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:50
std::string name() const override final
Definition ocb.cpp:207
size_t block_size() const
Definition ocb.h:62
size_t par_blocks() const
Definition ocb.h:64
size_t tag_size() const override final
Definition ocb.h:45
bool has_keying_material() const override final
Definition ocb.cpp:223
size_t ideal_granularity() const override final
Definition ocb.cpp:215
bool valid_nonce_length(size_t) const override final
Definition ocb.cpp:196
void reset() override final
Definition ocb.cpp:188
secure_vector< uint8_t > m_checksum
Definition ocb.h:74
std::unique_ptr< BlockCipher > m_cipher
Definition ocb.h:69
secure_vector< uint8_t > m_ad_hash
Definition ocb.h:75
void clear() override final
Definition ocb.cpp:182
void set_associated_data_n(size_t idx, std::span< const uint8_t > ad) override final
Definition ocb.cpp:232
size_t update_granularity() const override final
Definition ocb.cpp:211
size_t m_block_index
Definition ocb.h:72
Key_Length_Specification key_spec() const override final
Definition ocb.cpp:219
OCB_Mode(std::unique_ptr< BlockCipher > cipher, size_t tag_size)
Definition ocb.cpp:160
std::unique_ptr< L_computer > m_L
Definition ocb.h:70
void assert_key_material_set() const
Definition sym_algo.h:139
int(* init)(CTX *)
int(* final)(unsigned char *, CTX *)
CT::Mask< T > is_equal(const T x[], const T y[], size_t len)
Definition ct_utils.h:339
void zeroise(std::vector< T, Alloc > &vec)
Definition secmem.h:108
constexpr size_t var_ctz32(uint32_t n)
Definition bit_ops.h:151
constexpr void xor_buf(ranges::contiguous_output_range< uint8_t > auto &&out, ranges::contiguous_range< uint8_t > auto &&in)
Definition mem_ops.h:340
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:61
void poly_double_n(uint8_t out[], const uint8_t in[], size_t n)
Definition poly_dbl.cpp:74
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:146
constexpr void clear_mem(T *ptr, size_t n)
Definition mem_ops.h:120