Botan 3.0.0
Crypto and TLS for C&
ct_utils.cpp
Go to the documentation of this file.
1/*
2* (C) 2018,2021 Jack Lloyd
3*
4* Botan is released under the Simplified BSD License (see license.txt)
5*/
6
7#include <botan/internal/ct_utils.h>
8
9namespace Botan::CT {
10
12 const uint8_t input[],
13 size_t input_length,
14 size_t offset)
15 {
16 /*
17 * We do not poison the input here because if we did we would have
18 * to unpoison it at exit. We assume instead that callers have
19 * already poisoned the input and will unpoison it at their own
20 * time.
21 */
22 CT::poison(&offset, sizeof(size_t));
23
24 secure_vector<uint8_t> output(input_length);
25
26 auto bad_input = CT::Mask<size_t>::expand(bad_input_u8);
27
28 /*
29 * If the offset is greater than input_length then the arguments are
30 * invalid. Ideally we would through an exception but that leaks
31 * information about the offset. Instead treat it as if the input
32 * was invalid.
33 */
34 bad_input |= CT::Mask<size_t>::is_gt(offset, input_length);
35
36 /*
37 * If the input is invalid, then set offset == input_length as a result
38 * at the end we will set output_bytes == 0 causing the final result to
39 * be an empty vector.
40 */
41 offset = bad_input.select(input_length, offset);
42
43 /*
44 Move the desired output bytes to the front using a slow (O^n)
45 but constant time loop that does not leak the value of the offset
46 */
47 for(size_t i = 0; i != input_length; ++i)
48 {
49 /*
50 * If bad_input was set then we modified offset to equal the input_length.
51 * In that case, this_loop will be greater than input_length, and so is_eq
52 * mask will always be false. As a result none of the input values will be
53 * written to output.
54 *
55 * This is ignoring the possibility of integer overflow of offset + i. But
56 * for this to happen the input would have to consume nearly the entire
57 * address space, and we just allocated an output buffer of equal size.
58 */
59 const size_t this_loop = offset + i;
60
61 /*
62 start index from i rather than 0 since we know j must be >= i + offset
63 to have any effect, and starting from i does not reveal information
64 */
65 for(size_t j = i; j != input_length; ++j)
66 {
67 const uint8_t b = input[j];
68 const auto is_eq = CT::Mask<size_t>::is_equal(j, this_loop);
69 output[i] |= is_eq.if_set_return(b);
70 }
71 }
72
73 const size_t output_bytes = input_length - offset;
74
75 CT::unpoison(output.data(), output.size());
76 CT::unpoison(output_bytes);
77
78 /*
79 This is potentially not const time, depending on how std::vector is
80 implemented. But since we are always reducing length, it should
81 just amount to setting the member var holding the length.
82 */
83 output.resize(output_bytes);
84 return output;
85 }
86
87secure_vector<uint8_t> strip_leading_zeros(const uint8_t in[], size_t length)
88 {
89 size_t leading_zeros = 0;
90
91 auto only_zeros = Mask<uint8_t>::set();
92
93 for(size_t i = 0; i != length; ++i)
94 {
95 only_zeros &= CT::Mask<uint8_t>::is_zero(in[i]);
96 leading_zeros += only_zeros.if_set_return(1);
97 }
98
99 return copy_output(CT::Mask<uint8_t>::cleared(), in, length, leading_zeros);
100 }
101
102}
static Mask< T > is_gt(T x, T y)
Definition: ct_utils.h:163
static Mask< T > is_equal(T x, T y)
Definition: ct_utils.h:147
static Mask< T > is_zero(T x)
Definition: ct_utils.h:139
static Mask< T > expand(T v)
Definition: ct_utils.h:121
static Mask< T > set()
Definition: ct_utils.h:105
void poison(const T *p, size_t n)
Definition: ct_utils.h:48
secure_vector< uint8_t > copy_output(CT::Mask< uint8_t > bad_input_u8, const uint8_t input[], size_t input_length, size_t offset)
Definition: ct_utils.cpp:11
void unpoison(const T *p, size_t n)
Definition: ct_utils.h:58
secure_vector< uint8_t > strip_leading_zeros(const uint8_t in[], size_t length)
Definition: ct_utils.cpp:87
std::vector< T, secure_allocator< T > > secure_vector
Definition: secmem.h:64