Botan 3.5.0
Crypto and TLS for C&
Botan::CT Namespace Reference

Classes

class  Choice
 
class  Mask
 

Functions

template<typename T >
constexpr CT::Mask< Tall_zeros (const T elem[], size_t len)
 
template<typename T >
constexpr Mask< Tconditional_assign_mem (Choice cnd, T *sink, const T *src, size_t elems)
 
template<typename T >
constexpr Mask< Tconditional_assign_mem (T cnd, T *sink, const T *src, size_t elems)
 
template<typename T >
constexpr Mask< Tconditional_copy_mem (Mask< T > mask, T *to, const T *from0, const T *from1, size_t elems)
 
template<typename T >
constexpr Mask< Tconditional_copy_mem (T cnd, T *to, const T *from0, const T *from1, size_t elems)
 
template<typename T >
constexpr void conditional_swap (bool cnd, T &x, T &y)
 
template<typename T >
constexpr void conditional_swap_ptr (bool cnd, T &x, T &y)
 
secure_vector< uint8_t > copy_output (CT::Mask< uint8_t > bad_input_u8, const uint8_t input[], size_t input_length, size_t offset)
 
template<typename T >
constexpr CT::Mask< Tis_equal (const T x[], const T y[], size_t len)
 
template<typename T >
constexpr CT::Mask< Tis_not_equal (const T x[], const T y[], size_t len)
 
template<typename T >
constexpr void poison (const T *p, size_t n)
 
secure_vector< uint8_t > strip_leading_zeros (const secure_vector< uint8_t > &in)
 
secure_vector< uint8_t > strip_leading_zeros (const uint8_t in[], size_t length)
 
template<typename T >
constexpr void unpoison (const T *p, size_t n)
 
template<typename T >
constexpr void unpoison (T &p)
 
template<typename T >
constexpr T value_barrier (T x)
 

Function Documentation

◆ all_zeros()

template<typename T >
CT::Mask< T > Botan::CT::all_zeros ( const T elem[],
size_t len )
inlineconstexpr

◆ conditional_assign_mem() [1/2]

template<typename T >
Mask< T > Botan::CT::conditional_assign_mem ( Choice cnd,
T * sink,
const T * src,
size_t elems )
inlineconstexpr

Definition at line 445 of file ct_utils.h.

445 {
446 const auto mask = CT::Mask<T>::from_choice(cnd);
447 mask.select_n(sink, src, sink, elems);
448 return mask;
449}

References Botan::CT::Mask< T >::from_choice().

◆ conditional_assign_mem() [2/2]

template<typename T >
Mask< T > Botan::CT::conditional_assign_mem ( T cnd,
T * sink,
const T * src,
size_t elems )
inlineconstexpr

Definition at line 438 of file ct_utils.h.

438 {
439 const auto mask = CT::Mask<T>::expand(cnd);
440 mask.select_n(sink, src, sink, elems);
441 return mask;
442}

References Botan::CT::Mask< T >::expand().

Referenced by Botan::bigint_monty_maybe_sub(), Botan::bigint_monty_maybe_sub(), Botan::IntMod< Rep >::conditional_assign(), and Botan::Gf448Elem::ct_cond_assign().

◆ conditional_copy_mem() [1/2]

template<typename T >
Mask< T > Botan::CT::conditional_copy_mem ( Mask< T > mask,
T * to,
const T * from0,
const T * from1,
size_t elems )
inlineconstexpr

Definition at line 426 of file ct_utils.h.

426 {
427 mask.select_n(to, from0, from1, elems);
428 return mask;
429}
constexpr void select_n(T output[], const T x[], const T y[], size_t len) const
Definition ct_utils.h:373

References Botan::CT::Mask< T >::select_n().

Referenced by Botan::bigint_sub_abs(), conditional_copy_mem(), Botan::Kyber_KEM_Decryptor::decapsulate(), and Botan::BigInt::mod_add().

◆ conditional_copy_mem() [2/2]

template<typename T >
Mask< T > Botan::CT::conditional_copy_mem ( T cnd,
T * to,
const T * from0,
const T * from1,
size_t elems )
inlineconstexpr

Definition at line 432 of file ct_utils.h.

432 {
433 const auto mask = CT::Mask<T>::expand(cnd);
434 return CT::conditional_copy_mem(mask, to, from0, from1, elems);
435}

References conditional_copy_mem(), and Botan::CT::Mask< T >::expand().

◆ conditional_swap()

template<typename T >
void Botan::CT::conditional_swap ( bool cnd,
T & x,
T & y )
inlineconstexpr

Definition at line 452 of file ct_utils.h.

452 {
453 const auto swap = CT::Mask<T>::expand(cnd);
454
455 T t0 = swap.select(y, x);
456 T t1 = swap.select(x, y);
457 x = t0;
458 y = t1;
459}

References Botan::CT::Mask< T >::expand(), and T.

Referenced by Botan::bigint_sub_abs(), conditional_swap_ptr(), and Botan::Gf448Elem::ct_cond_swap().

◆ conditional_swap_ptr()

template<typename T >
void Botan::CT::conditional_swap_ptr ( bool cnd,
T & x,
T & y )
inlineconstexpr

Definition at line 462 of file ct_utils.h.

462 {
463 uintptr_t xp = reinterpret_cast<uintptr_t>(x);
464 uintptr_t yp = reinterpret_cast<uintptr_t>(y);
465
466 conditional_swap<uintptr_t>(cnd, xp, yp);
467
468 x = reinterpret_cast<T>(xp);
469 y = reinterpret_cast<T>(yp);
470}

References conditional_swap(), and T.

Referenced by Botan::bigint_sub_abs().

◆ copy_output()

BOTAN_TEST_API secure_vector< uint8_t > Botan::CT::copy_output ( CT::Mask< uint8_t > bad_input,
const uint8_t input[],
size_t input_length,
size_t offset )

If bad_input is unset, return input[offset:input_length] copied to new buffer. If bad_input is set, return an empty vector. In all cases, the capacity of the vector is equal to input_length

This function attempts to avoid leaking the following:

  • if bad_input was set or not
  • the value of offset
  • the values in input[]

This function leaks the value of input_length

Definition at line 11 of file ct_utils.cpp.

14 {
15 /*
16 * We do not poison the input here because if we did we would have
17 * to unpoison it at exit. We assume instead that callers have
18 * already poisoned the input and will unpoison it at their own
19 * time.
20 */
21 CT::poison(&offset, sizeof(size_t));
22
23 secure_vector<uint8_t> output(input_length);
24
25 auto bad_input = CT::Mask<size_t>::expand(bad_input_u8);
26
27 /*
28 * If the offset is greater than input_length then the arguments are
29 * invalid. Ideally we would through an exception but that leaks
30 * information about the offset. Instead treat it as if the input
31 * was invalid.
32 */
33 bad_input |= CT::Mask<size_t>::is_gt(offset, input_length);
34
35 /*
36 * If the input is invalid, then set offset == input_length as a result
37 * at the end we will set output_bytes == 0 causing the final result to
38 * be an empty vector.
39 */
40 offset = bad_input.select(input_length, offset);
41
42 /*
43 Move the desired output bytes to the front using a slow (O^n)
44 but constant time loop that does not leak the value of the offset
45 */
46 for(size_t i = 0; i != input_length; ++i) {
47 /*
48 * If bad_input was set then we modified offset to equal the input_length.
49 * In that case, this_loop will be greater than input_length, and so is_eq
50 * mask will always be false. As a result none of the input values will be
51 * written to output.
52 *
53 * This is ignoring the possibility of integer overflow of offset + i. But
54 * for this to happen the input would have to consume nearly the entire
55 * address space, and we just allocated an output buffer of equal size.
56 */
57 const size_t this_loop = offset + i;
58
59 /*
60 start index from i rather than 0 since we know j must be >= i + offset
61 to have any effect, and starting from i does not reveal information
62 */
63 for(size_t j = i; j != input_length; ++j) {
64 const uint8_t b = input[j];
65 const auto is_eq = CT::Mask<size_t>::is_equal(j, this_loop);
66 output[i] |= is_eq.if_set_return(b);
67 }
68 }
69
70 const size_t output_bytes = input_length - offset;
71
72 CT::unpoison(output.data(), output.size());
73 CT::unpoison(output_bytes);
74
75 /*
76 This is potentially not const time, depending on how std::vector is
77 implemented. But since we are always reducing length, it should
78 just amount to setting the member var holding the length.
79 */
80 output.resize(output_bytes);
81 return output;
82}
constexpr T select(T x, T y) const
Definition ct_utils.h:356
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:61

References Botan::CT::Mask< T >::expand(), Botan::CT::Mask< T >::is_equal(), Botan::CT::Mask< T >::is_gt(), poison(), and unpoison().

Referenced by Botan::oaep_find_delim(), strip_leading_zeros(), and Botan::EME_PKCS1v15::unpad().

◆ is_equal()

template<typename T >
CT::Mask< T > Botan::CT::is_equal ( const T x[],
const T y[],
size_t len )
inlineconstexpr

Compare two arrays of equal size and return a Mask indicating if they are equal or not. The mask is set if they are identical.

Definition at line 486 of file ct_utils.h.

486 {
487 if(std::is_constant_evaluated()) {
488 T difference = 0;
489
490 for(size_t i = 0; i != len; ++i) {
491 difference = difference | (x[i] ^ y[i]);
492 }
493
494 return CT::Mask<T>::is_zero(difference);
495 } else {
496 volatile T difference = 0;
497
498 for(size_t i = 0; i != len; ++i) {
499 difference = difference | (x[i] ^ y[i]);
500 }
501
502 return CT::Mask<T>::is_zero(difference);
503 }
504}

References Botan::CT::Mask< T >::is_zero(), and T.

Referenced by Botan::argon2_check_pwhash(), botan_constant_time_compare(), Botan::Gf448Elem::bytes_are_canonical_representation(), Botan::check_bcrypt(), Botan::Ed25519_PublicKey::check_key(), Botan::X448_PrivateKey::check_key(), Botan::check_passhash9(), Botan::constant_time_compare(), Botan::FrodoMatrix::constant_time_compare(), Botan::Sodium::crypto_secretbox_open_detached(), Botan::Sodium::crypto_verify_16(), Botan::Sodium::crypto_verify_32(), Botan::Sodium::crypto_verify_64(), Botan::ct_compare_u8(), Botan::Kyber_KEM_Decryptor::decapsulate(), Botan::TLS::Session::decrypt(), Botan::CryptoBox::decrypt_bin(), Botan::ed25519_verify(), is_not_equal(), Botan::Gf448Elem::operator==(), Botan::IntMod< Rep >::operator==(), Botan::RTSS_Share::reconstruct(), Botan::Sodium::sodium_memcmp(), Botan::TLS::Finished_12::verify(), and Botan::MessageAuthenticationCode::verify_mac_result().

◆ is_not_equal()

template<typename T >
CT::Mask< T > Botan::CT::is_not_equal ( const T x[],
const T y[],
size_t len )
inlineconstexpr

Compare two arrays of equal size and return a Mask indicating if they are equal or not. The mask is set if they differ.

Definition at line 511 of file ct_utils.h.

511 {
512 return ~CT::is_equal(x, y, len);
513}

References is_equal().

Referenced by Botan::Sodium::crypto_secretbox_xsalsa20poly1305_open(), Botan::oaep_find_delim(), and Botan::IntMod< Rep >::operator!=().

◆ poison()

template<typename T >
void Botan::CT::poison ( const T * p,
size_t n )
inlineconstexpr

Use valgrind to mark the contents of memory as being undefined. Valgrind will accept operations which manipulate undefined values, but will warn if an undefined value is used to decided a conditional jump or a load/store address. So if we poison all of our inputs we can confirm that the operations in question are truly const time when compiled by whatever compiler is in use.

Even better, the VALGRIND_MAKE_MEM_* macros work even when the program is not run under valgrind (though with a few cycles of overhead, which is unfortunate in final binaries as these annotations tend to be used in fairly important loops).

This approach was first used in ctgrind (https://github.com/agl/ctgrind) but calling the valgrind mecheck API directly works just as well and doesn't require a custom patched valgrind.

Definition at line 46 of file ct_utils.h.

46 {
47#if defined(BOTAN_HAS_VALGRIND)
48 if(!std::is_constant_evaluated()) {
49 VALGRIND_MAKE_MEM_UNDEFINED(p, n * sizeof(T));
50 }
51#endif
52
53 BOTAN_UNUSED(p, n);
54}
#define BOTAN_UNUSED
Definition assert.h:118

References BOTAN_UNUSED, and T.

Referenced by Botan::ANSI_X923_Padding::add_padding(), Botan::ESP_Padding::add_padding(), Botan::OneAndZeros_Padding::add_padding(), Botan::PKCS7_Padding::add_padding(), Botan::BlindedScalarBits< C, WindowBits >::BlindedScalarBits(), Botan::BOTAN_FUNC_ISA(), copy_output(), Botan::IntMod< Rep >::ct_poison(), Botan::curve25519_donna(), Botan::oaep_find_delim(), Botan::ANSI_X923_Padding::unpad(), Botan::EME_PKCS1v15::unpad(), Botan::ESP_Padding::unpad(), Botan::OneAndZeros_Padding::unpad(), and Botan::PKCS7_Padding::unpad().

◆ strip_leading_zeros() [1/2]

secure_vector< uint8_t > Botan::CT::strip_leading_zeros ( const secure_vector< uint8_t > & in)
inline

Definition at line 535 of file ct_utils.h.

535 {
536 return strip_leading_zeros(in.data(), in.size());
537}
secure_vector< uint8_t > strip_leading_zeros(const uint8_t in[], size_t length)
Definition ct_utils.cpp:84

References strip_leading_zeros().

◆ strip_leading_zeros() [2/2]

secure_vector< uint8_t > Botan::CT::strip_leading_zeros ( const uint8_t in[],
size_t length )

Definition at line 84 of file ct_utils.cpp.

84 {
85 size_t leading_zeros = 0;
86
87 auto only_zeros = Mask<uint8_t>::set();
88
89 for(size_t i = 0; i != length; ++i) {
90 only_zeros &= CT::Mask<uint8_t>::is_zero(in[i]);
91 leading_zeros += only_zeros.if_set_return(1);
92 }
93
94 return copy_output(CT::Mask<uint8_t>::cleared(), in, length, leading_zeros);
95}

References copy_output(), Botan::CT::Mask< T >::is_zero(), and Botan::CT::Mask< T >::set().

Referenced by Botan::TLS::Client_Key_Exchange::Client_Key_Exchange(), Botan::TLS::Client_Key_Exchange::Client_Key_Exchange(), and strip_leading_zeros().

◆ unpoison() [1/2]

◆ unpoison() [2/2]

template<typename T >
void Botan::CT::unpoison ( T & p)
inlineconstexpr

Definition at line 68 of file ct_utils.h.

68 {
69#if defined(BOTAN_HAS_VALGRIND)
70 if(!std::is_constant_evaluated()) {
71 VALGRIND_MAKE_MEM_DEFINED(&p, sizeof(T));
72 }
73#endif
74
75 BOTAN_UNUSED(p);
76}

References BOTAN_UNUSED, and T.

◆ value_barrier()

template<typename T >
T Botan::CT::value_barrier ( T x)
inlineconstexpr

This function returns its argument, but (if called in a non-constexpr context) attempts to prevent the compiler from reasoning about the value or the possible range of values. Such optimizations have a way of breaking constant time code.

Definition at line 84 of file ct_utils.h.

84 {
85 if(!std::is_constant_evaluated()) {
86 /*
87 * For compilers without inline asm, is there something else we can do?
88 *
89 * For instance we could potentially launder the value through a
90 * `volatile T` or `volatile T*`. This would require some experimentation.
91 *
92 * GCC has an attribute noipa which disables interprocedural analysis, which
93 * might be useful here. However Clang does not currently support this attribute.
94 *
95 * We may want a "stronger" statement such as
96 * asm volatile("" : "+r,m"(x) : : "memory);
97 * (see https://theunixzoo.co.uk/blog/2021-10-14-preventing-optimisations.html)
98 * however the current approach seems sufficient with current compilers,
99 * and is minimally damaging with regards to degrading code generation.
100 */
101#if defined(BOTAN_USE_GCC_INLINE_ASM)
102 asm("" : "+r"(x) : /* no input */);
103#endif
104 return x;
105 } else {
106 return x;
107 }
108}

Referenced by Botan::CT::Mask< T >::expand(), Botan::CT::Mask< T >::expand_top_bit(), Botan::CT::Choice::from_int(), Botan::CT::Mask< T >::is_any_of(), Botan::CT::Mask< T >::is_equal(), Botan::CT::Mask< T >::is_within_range(), Botan::CT::Mask< T >::is_zero(), Botan::BigInt::top_bits_free(), Botan::CT::Choice::value(), and Botan::CT::Mask< T >::value().