Botan 3.13.0
Crypto and TLS for C&
tls_cbc.cpp
Go to the documentation of this file.
1/*
2* TLS CBC Record Handling
3* (C) 2012,2013,2014,2015,2016,2020 Jack Lloyd
4* (C) 2016 Juraj Somorovsky
5* (C) 2016 Matthias Gierlings
6* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
7*
8* Botan is released under the Simplified BSD License (see license.txt)
9*/
10
11#include <botan/internal/tls_cbc.h>
12
13#include <botan/block_cipher.h>
14#include <botan/mac.h>
15#include <botan/tls_alert.h>
16#include <botan/tls_exceptn.h>
17#include <botan/tls_version.h>
18#include <botan/internal/cbc.h>
19#include <botan/internal/ct_utils.h>
20#include <botan/internal/int_utils.h>
21#include <botan/internal/loadstor.h>
22#include <botan/internal/rounding.h>
23
24namespace Botan::TLS {
25
27
28/*
29* TLS_CBC_HMAC_AEAD_Mode Constructor
30*/
32 std::unique_ptr<BlockCipher> cipher,
33 std::unique_ptr<MessageAuthenticationCode> mac,
34 size_t cipher_keylen,
35 size_t mac_keylen,
36 const Protocol_Version& version,
38 m_mac(std::move(mac)),
39 m_cipher_name(cipher->name()),
40 m_mac_name(m_mac->name()),
41 m_cipher_keylen(cipher_keylen),
42 m_block_size(cipher->block_size()),
43 m_iv_size(m_block_size),
44 m_mac_keylen(mac_keylen),
45 m_tag_size(m_mac->output_length()),
46 m_use_encrypt_then_mac(use_encrypt_then_mac),
47 m_is_datagram(version.is_datagram_protocol()) {
48 BOTAN_ASSERT_NOMSG(m_mac->valid_keylength(m_mac_keylen));
49 BOTAN_ASSERT_NOMSG(cipher->valid_keylength(m_cipher_keylen));
50
51 auto null_padding = std::make_unique<Null_Padding>();
52 if(dir == Cipher_Dir::Encryption) {
53 m_cbc = std::make_unique<CBC_Encryption>(std::move(cipher), std::move(null_padding));
54 } else {
55 m_cbc = std::make_unique<CBC_Decryption>(std::move(cipher), std::move(null_padding));
56 }
57}
58
60 cbc().clear();
61 mac().clear();
62 reset();
63}
64
66 cbc_state().clear();
67 m_ad.clear();
68 m_msg.clear();
69}
70
71std::string TLS_CBC_HMAC_AEAD_Mode::name() const {
72 return "TLS_CBC(" + m_cipher_name + "," + m_mac_name + ")";
73}
74
76 return 1; // just buffers anyway
77}
78
80 return 1; // just buffers anyway
81}
82
84 if(m_cbc_state.empty()) {
85 return nl == block_size();
86 }
87 return nl == iv_size();
88}
89
91 return Key_Length_Specification(m_cipher_keylen + m_mac_keylen);
92}
93
97
98void TLS_CBC_HMAC_AEAD_Mode::key_schedule(std::span<const uint8_t> key) {
99 // Both keys are of fixed length specified by the ciphersuite
100
101 if(key.size() != m_cipher_keylen + m_mac_keylen) {
102 throw Invalid_Key_Length(name(), key.size());
103 }
104
105 mac().set_key(key.first(m_mac_keylen));
106 cbc().set_key(key.subspan(m_mac_keylen, m_cipher_keylen));
107}
108
109void TLS_CBC_HMAC_AEAD_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
110 if(!valid_nonce_length(nonce_len)) {
111 throw Invalid_IV_Length(name(), nonce_len);
112 }
113
114 m_msg.clear();
115
116 if(nonce_len > 0) {
117 m_cbc_state.assign(nonce, nonce + nonce_len);
118 }
119}
120
121size_t TLS_CBC_HMAC_AEAD_Mode::process_msg(uint8_t buf[], size_t sz) {
122 m_msg.insert(m_msg.end(), buf, buf + sz);
123 return 0;
124}
125
126std::vector<uint8_t> TLS_CBC_HMAC_AEAD_Mode::assoc_data_with_len(uint16_t len) {
127 std::vector<uint8_t> ad = m_ad;
128 BOTAN_ASSERT(ad.size() == 13, "Expected AAD size");
129 ad[11] = get_byte<0>(len);
130 ad[12] = get_byte<1>(len);
131 return ad;
132}
133
134void TLS_CBC_HMAC_AEAD_Mode::set_associated_data_n(size_t idx, std::span<const uint8_t> ad) {
135 BOTAN_ARG_CHECK(idx == 0, "TLS 1.2 CBC/HMAC: cannot handle non-zero index in set_associated_data_n");
136 if(ad.size() != 13) {
137 throw Invalid_Argument("Invalid TLS AEAD associated data length");
138 }
139 m_ad.assign(ad.begin(), ad.end());
140}
141
143 std::unique_ptr<MessageAuthenticationCode> mac,
144 const size_t cipher_keylen,
145 const size_t mac_keylen,
146 const Protocol_Version& version,
149 std::move(cipher),
150 std::move(mac),
153 version,
155
156void TLS_CBC_HMAC_AEAD_Encryption::set_associated_data_n(size_t idx, std::span<const uint8_t> ad) {
158
160 // AAD hack for EtM
161 // EtM uses ciphertext size instead of plaintext size for AEAD input
162 const uint16_t pt_size = make_uint16(assoc_data()[11], assoc_data()[12]);
163 const uint16_t enc_size = static_cast<uint16_t>(round_up(iv_size() + pt_size + 1, block_size()));
164 assoc_data()[11] = get_byte<0, uint16_t>(enc_size);
165 assoc_data()[12] = get_byte<1, uint16_t>(enc_size);
166 }
167}
168
169void TLS_CBC_HMAC_AEAD_Encryption::cbc_encrypt_record(secure_vector<uint8_t>& buffer,
170 size_t offset,
171 size_t padding_length) {
172 // We always do short padding:
173 BOTAN_ASSERT_NOMSG(padding_length <= 16);
174
175 const size_t buf_with_padding = add_or_throw(buffer.size(), padding_length, "TLS CBC input too large");
176 buffer.resize(buf_with_padding);
177
178 const uint8_t padding_val = static_cast<uint8_t>(padding_length - 1);
179
180 CT::poison(&padding_val, 1);
181 CT::poison(&padding_length, 1);
182 CT::poison(buffer.data(), buffer.size());
183
184 const size_t last_block_starts = buffer.size() - block_size();
185 const size_t padding_starts = buffer.size() - padding_length;
186 for(size_t i = last_block_starts; i != buffer.size(); ++i) {
187 auto add_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, padding_starts));
188 buffer[i] = add_padding.select(padding_val, buffer[i]);
189 }
190
191 CT::unpoison(padding_val);
192 CT::unpoison(padding_length);
193 CT::unpoison(buffer.data(), buffer.size());
194
195 cbc().start(cbc_state());
196 cbc().process(&buffer[offset], buffer.size() - offset);
197
198 cbc_state().assign(buffer.data() + (buffer.size() - block_size()), buffer.data() + buffer.size());
199}
200
201size_t TLS_CBC_HMAC_AEAD_Encryption::output_length(size_t input_length) const {
202 const size_t mac_in_plaintext = use_encrypt_then_mac() ? 0 : tag_size();
203 const size_t mac_appended = use_encrypt_then_mac() ? tag_size() : 0;
204
205 // round_up() checks its own addition for overflow, but the addition feeding
206 // it and the trailing MAC must be checked separately. mac_in_plaintext + 1
207 // cannot overflow as the MAC length is small.
208 const size_t input_size = add_or_throw(input_length, mac_in_plaintext + 1, "TLS CBC input too large");
209 return add_or_throw(round_up(input_size, block_size()), mac_appended, "TLS CBC input too large");
210}
211
212void TLS_CBC_HMAC_AEAD_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
213 update(buffer, offset);
214
215 const size_t msg_size = msg().size();
216
217 const size_t input_size =
218 add_or_throw(msg_size, (use_encrypt_then_mac() ? 0 : tag_size()) + 1, "TLS CBC input too large");
219 const size_t enc_size = round_up(input_size, block_size());
220 BOTAN_DEBUG_ASSERT(enc_size % block_size() == 0);
221
222 const uint8_t padding_val = static_cast<uint8_t>(enc_size - input_size);
223 const size_t padding_length = static_cast<size_t>(padding_val) + 1;
224
225 const size_t output_size = add_or_throw(offset, msg_size, "TLS CBC input too large");
226 buffer.reserve(add_or_throw(output_size, padding_length + tag_size(), "TLS CBC input too large"));
227 buffer.resize(output_size);
228 if(msg_size > 0) {
229 copy_mem(&buffer[offset], msg().data(), msg_size);
230 }
231
232 mac().update(assoc_data());
233
235 if(iv_size() > 0) {
236 mac().update(cbc_state());
237 }
238
239 cbc_encrypt_record(buffer, offset, padding_length);
240 mac().update(&buffer[offset], enc_size);
241 buffer.resize(add_or_throw(buffer.size(), tag_size(), "TLS CBC input too large"));
242 mac().final(&buffer[buffer.size() - tag_size()]);
243 } else {
244 if(msg_size > 0) {
245 mac().update(&buffer[offset], msg_size);
246 }
247 buffer.resize(add_or_throw(buffer.size(), tag_size(), "TLS CBC input too large"));
248 mac().final(&buffer[buffer.size() - tag_size()]);
249 cbc_encrypt_record(buffer, offset, padding_length);
250 }
251}
252
253/*
254* Checks the TLS padding. Returns 0 if the padding is invalid (we
255* count the padding_length field as part of the padding size so a
256* valid padding will always be at least one byte long), or the length
257* of the padding otherwise. This is actually padding_length + 1
258* because both the padding and padding_length fields are padding from
259* our perspective.
260*
261* Returning 0 in the error case should ensure the MAC check will fail.
262* This approach is suggested in section 6.2.3.2 of RFC 5246.
263*/
264uint16_t check_tls_cbc_padding(const uint8_t record[], size_t record_len) {
265 if(record_len == 0 || record_len > 0xFFFF) {
266 return 0;
267 }
268
269 const uint16_t rec16 = static_cast<uint16_t>(record_len);
270
271 /*
272 * TLS v1.0 and up require all the padding bytes be the same value
273 * and allows up to 255 bytes.
274 */
275
276 const uint16_t to_check = std::min<uint16_t>(256, static_cast<uint16_t>(record_len));
277 const uint8_t pad_byte = record[record_len - 1];
278 const uint16_t pad_bytes = 1 + pad_byte;
279
280 auto pad_invalid = CT::Mask<uint16_t>::is_lt(rec16, pad_bytes);
281
282 for(uint16_t i = rec16 - to_check; i != rec16; ++i) {
283 const uint16_t offset = rec16 - i;
284 const auto in_pad_range = CT::Mask<uint16_t>::is_lte(offset, pad_bytes);
285 const auto pad_correct = CT::Mask<uint16_t>::is_equal(record[i], pad_byte);
286 pad_invalid |= in_pad_range & ~pad_correct;
287 }
288
289 return pad_invalid.if_not_set_return(pad_bytes);
290}
291
293 std::unique_ptr<MessageAuthenticationCode> mac,
294 const size_t cipher_keylen,
295 const size_t mac_keylen,
296 const Protocol_Version& version,
299 std::move(cipher),
300 std::move(mac),
303 version,
305
306void TLS_CBC_HMAC_AEAD_Decryption::cbc_decrypt_record(uint8_t record_contents[], size_t record_len) {
307 if(record_len == 0 || record_len % block_size() != 0) {
308 throw Decoding_Error("Received TLS CBC ciphertext with invalid length");
309 }
310
311 cbc().start(cbc_state());
312 cbc_state().assign(record_contents + record_len - block_size(), record_contents + record_len);
313
314 cbc().process(record_contents, record_len);
315}
316
317size_t TLS_CBC_HMAC_AEAD_Decryption::output_length(size_t /*input_length*/) const {
318 /*
319 * We don't know this because the padding is arbitrary
320 */
321 return 0;
322}
323
324/*
325* This function performs additional compression calls in order
326* to protect from the Lucky 13 attack. It adds new compression
327* function calls over dummy data, by computing additional HMAC updates.
328*
329* The countermeasure was described (in a similar way) in the Lucky 13 paper.
330*
331* Background:
332* - One SHA-1/SHA-256 compression is performed with 64 bytes of data.
333* - HMAC adds 8 byte length field and padding (at least 1 byte) so that we have:
334* - 0 - 55 bytes: 1 compression
335* - 56 - 55+64 bytes: 2 compressions
336* - 56+64 - 55+2*64 bytes: 3 compressions ...
337* - For SHA-384, this works similarly, but we have 128 byte blocks and 16 byte
338* long length field. This results in:
339* - 0 - 111 bytes: 1 compression
340* - 112 - 111+128 bytes: 2 compressions ...
341*
342* The implemented countermeasure works as follows:
343* 1) It computes max_compressions: number of maximum compressions performed on
344* the decrypted data
345* 2) It computes current_compressions: number of compressions performed on the
346* decrypted data, after padding has been removed
347* 3) If current_compressions != max_compressions: It invokes an HMAC update
348* over dummy data so that (max_compressions - current_compressions)
349* compressions are performed. Otherwise, it invokes an HMAC update so that
350* no compressions are performed.
351*
352* Note that the padding validation in Botan is always performed over
353* min(plen,256) bytes, see the function check_tls_cbc_padding. This differs
354* from the countermeasure described in the paper.
355*
356* Note that the padding length padlen does also count the last byte
357* of the decrypted plaintext. This is different from the Lucky 13 paper.
358*
359* This countermeasure leaves a difference of about 100 clock cycles (in
360* comparison to >1000 clock cycles observed without it).
361*
362* plen represents the length of the decrypted plaintext message P
363* padlen represents the padding length
364*
365*/
366void TLS_CBC_HMAC_AEAD_Decryption::perform_additional_compressions(size_t plen, size_t padlen) {
367 const bool is_sha384 = mac().name() == "HMAC(SHA-384)";
368 const uint16_t block_size = is_sha384 ? 128 : 64;
369 const uint16_t max_bytes_in_first_block = is_sha384 ? 111 : 55;
370
371 // number of maximum MACed bytes
372 const uint16_t L1 = static_cast<uint16_t>(13 + plen - tag_size());
373 // number of current MACed bytes (L1 - padlen)
374 // Here the Lucky 13 paper is different because the padlen length in the paper
375 // does not count the last message byte.
376 const uint16_t L2 = static_cast<uint16_t>(13 + plen - padlen - tag_size());
377 // From the paper, for SHA-256/SHA-1 compute: ceil((L1-55)/64) and ceil((L2-55)/64)
378 // ceil((L1-55)/64) = floor((L1+64-1-55)/64)
379 // Here we compute number of compressions for SHA-* in general
380 const uint16_t max_compresssions = ((L1 + block_size - 1 - max_bytes_in_first_block) / block_size);
381 const uint16_t current_compressions = ((L2 + block_size - 1 - max_bytes_in_first_block) / block_size);
382 // number of additional compressions we have to perform
383 const uint16_t add_compressions = max_compresssions - current_compressions;
384 const uint16_t equal = CT::Mask<uint16_t>::is_equal(max_compresssions, current_compressions).if_set_return(1);
385 // We compute the data length we need to achieve the number of compressions.
386 // If there are no compressions, we just add 55/111 dummy bytes so that no
387 // compression is performed.
388 const uint16_t data_len = block_size * add_compressions + equal * max_bytes_in_first_block;
389 std::vector<uint8_t> data(data_len);
390 mac().update(data);
391 // we do not need to clear the MAC since the connection is broken anyway
392}
393
394void TLS_CBC_HMAC_AEAD_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
395 update(buffer, offset);
396 buffer.resize(offset);
397
398 const size_t record_len = msg().size();
399 uint8_t* record_contents = msg().data();
400
401 // This early exit does not leak info because all the values compared are public
402 if(record_len < tag_size() || (record_len - (use_encrypt_then_mac() ? tag_size() : 0)) % block_size() != 0) {
403 throw TLS_Exception(Alert::BadRecordMac, "Message authentication failure");
404 }
405
407 const size_t enc_size = record_len - tag_size();
408 const size_t enc_iv_size = enc_size + iv_size();
409
410 BOTAN_ASSERT_NOMSG(enc_iv_size <= 0xFFFF);
411
412 mac().update(assoc_data_with_len(static_cast<uint16_t>(enc_iv_size)));
413 if(iv_size() > 0) {
414 mac().update(cbc_state());
415 }
416 mac().update(record_contents, enc_size);
417
418 std::vector<uint8_t> mac_buf(tag_size());
419 mac().final(mac_buf.data());
420
421 const size_t mac_offset = enc_size;
422
423 const auto mac_ok = CT::is_equal(&record_contents[mac_offset], mac_buf.data(), tag_size());
424
425 if(!mac_ok.as_bool()) {
426 throw TLS_Exception(Alert::BadRecordMac, "Message authentication failure");
427 }
428
429 cbc_decrypt_record(record_contents, enc_size);
430
431 // 0 if padding was invalid, otherwise 1 + padding_bytes
432 const uint16_t pad_size = check_tls_cbc_padding(record_contents, enc_size);
433
434 // No oracle here, whoever sent us this had the key since MAC check passed
435 if(pad_size == 0) {
436 throw TLS_Exception(Alert::BadRecordMac, "Message authentication failure");
437 }
438
439 const uint8_t* plaintext_block = &record_contents[0];
440 const size_t plaintext_length = enc_size - pad_size;
441
442 buffer.insert(buffer.end(), plaintext_block, plaintext_block + plaintext_length);
443 } else {
444 cbc_decrypt_record(record_contents, record_len);
445
446 CT::poison(record_contents, record_len);
447
448 // 0 if padding was invalid, otherwise 1 + padding_bytes
449 uint16_t pad_size = check_tls_cbc_padding(record_contents, record_len);
450
451 /*
452 This mask is zero if there is not enough room in the packet to get a valid MAC.
453
454 We have to accept empty packets, since otherwise we are not compatible
455 with how OpenSSL's countermeasure for fixing BEAST in TLS 1.0 CBC works
456 (sending empty records, instead of 1/(n-1) splitting)
457 */
458
459 // We know the cast cannot overflow as pad_size <= 256 && tag_size <= 32
460 const auto size_ok_mask =
461 CT::Mask<uint16_t>::is_lte(static_cast<uint16_t>(tag_size() + pad_size), static_cast<uint16_t>(record_len));
462
463 pad_size = size_ok_mask.if_set_return(pad_size);
464
465 CT::unpoison(record_contents, record_len);
466
467 /*
468 This is unpoisoned sooner than it should. The pad_size leaks to plaintext_length and
469 then to the timing channel in the MAC computation described in the Lucky 13 paper.
470 */
471 CT::unpoison(pad_size);
472
473 const uint8_t* plaintext_block = &record_contents[0];
474 const uint16_t plaintext_length = static_cast<uint16_t>(record_len - tag_size() - pad_size);
475
476 mac().update(assoc_data_with_len(plaintext_length));
477 mac().update(plaintext_block, plaintext_length);
478
479 std::vector<uint8_t> mac_buf(tag_size());
480 mac().final(mac_buf.data());
481
482 const size_t mac_offset = record_len - (tag_size() + pad_size);
483
484 const auto mac_ok = CT::is_equal(&record_contents[mac_offset], mac_buf.data(), tag_size());
485
486 const auto ok_mask = size_ok_mask & CT::Mask<uint16_t>::expand(mac_ok) & CT::Mask<uint16_t>::expand(pad_size);
487
488 CT::unpoison(ok_mask);
489
490 if(ok_mask.as_bool()) {
491 buffer.insert(buffer.end(), plaintext_block, plaintext_block + plaintext_length);
492 } else {
493 perform_additional_compressions(record_len, pad_size);
494
495 /*
496 * In DTLS case we have to finish computing the MAC since we require the
497 * MAC state be reset for future packets. This extra timing channel may
498 * be exploitable in a Lucky13 variant.
499 */
501 mac().final(mac_buf);
502 }
503 throw TLS_Exception(Alert::BadRecordMac, "Message authentication failure");
504 }
505 }
506}
507
508} // namespace Botan::TLS
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_DEBUG_ASSERT(expr)
Definition assert.h:129
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:62
void update(const uint8_t in[], size_t length)
Definition buf_comp.h:35
void final(uint8_t out[])
Definition buf_comp.h:97
static constexpr Mask< T > is_lte(T x, T y)
Definition ct_utils.h:463
static constexpr Mask< T > is_gte(T x, T y)
Definition ct_utils.h:468
static constexpr Mask< T > expand(T v)
Definition ct_utils.h:392
static constexpr Mask< T > is_equal(T x, T y)
Definition ct_utils.h:442
static constexpr Mask< T > is_lt(T x, T y)
Definition ct_utils.h:450
void start(std::span< const uint8_t > nonce)
Definition cipher_mode.h:98
void update(T &buffer, size_t offset=0)
size_t process(std::span< uint8_t > msg)
virtual size_t output_length(size_t input_length) const =0
virtual size_t tag_size() const
virtual std::string name() const =0
virtual bool has_keying_material() const =0
virtual void clear()=0
void set_key(const OctetString &key)
Definition sym_algo.cpp:14
TLS_CBC_HMAC_AEAD_Decryption(std::unique_ptr< BlockCipher > cipher, std::unique_ptr< MessageAuthenticationCode > mac, size_t cipher_keylen, size_t mac_keylen, const Protocol_Version &version, bool use_encrypt_then_mac)
Definition tls_cbc.cpp:292
size_t output_length(size_t input_length) const override
Definition tls_cbc.cpp:317
void set_associated_data_n(size_t idx, std::span< const uint8_t > ad) override
Definition tls_cbc.cpp:156
size_t output_length(size_t input_length) const override
Definition tls_cbc.cpp:201
TLS_CBC_HMAC_AEAD_Encryption(std::unique_ptr< BlockCipher > cipher, std::unique_ptr< MessageAuthenticationCode > mac, size_t cipher_keylen, size_t mac_keylen, const Protocol_Version &version, bool use_encrypt_then_mac)
Definition tls_cbc.cpp:142
size_t update_granularity() const final
Definition tls_cbc.cpp:75
void set_associated_data_n(size_t idx, std::span< const uint8_t > ad) override
Definition tls_cbc.cpp:134
secure_vector< uint8_t > & cbc_state()
Definition tls_cbc.h:86
Key_Length_Specification key_spec() const final
Definition tls_cbc.cpp:90
bool has_keying_material() const final
Definition tls_cbc.cpp:94
size_t ideal_granularity() const final
Definition tls_cbc.cpp:79
std::vector< uint8_t > assoc_data_with_len(uint16_t len)
Definition tls_cbc.cpp:126
std::string name() const final
Definition tls_cbc.cpp:71
std::vector< uint8_t > & assoc_data()
Definition tls_cbc.h:88
secure_vector< uint8_t > & msg()
Definition tls_cbc.h:90
TLS_CBC_HMAC_AEAD_Mode(const TLS_CBC_HMAC_AEAD_Mode &other)=delete
MessageAuthenticationCode & mac() const
Definition tls_cbc.h:84
Cipher_Mode & cbc() const
Definition tls_cbc.h:82
size_t tag_size() const final
Definition tls_cbc.h:43
bool valid_nonce_length(size_t nl) const final
Definition tls_cbc.cpp:83
constexpr CT::Mask< T > is_equal(const T x[], const T y[], size_t len)
Definition ct_utils.h:798
constexpr void unpoison(const T *p, size_t n)
Definition ct_utils.h:67
constexpr void poison(const T *p, size_t n)
Definition ct_utils.h:56
uint16_t check_tls_cbc_padding(const uint8_t record[], size_t record_len)
Definition tls_cbc.cpp:264
constexpr T add_or_throw(T a, T b, std::string_view msg)
Definition int_utils.h:66
constexpr uint8_t get_byte(T input)
Definition loadstor.h:79
constexpr size_t round_up(size_t n, size_t align_to)
Definition rounding.h:26
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:144
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:128
constexpr uint16_t make_uint16(uint8_t i0, uint8_t i1)
Definition loadstor.h:92