/src/botan/src/lib/utils/ct_utils.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
9 | | #include <botan/mem_ops.h> |
10 | | |
11 | | namespace Botan { |
12 | | |
13 | | CT::Option<size_t> CT::copy_output(CT::Choice accept, |
14 | | std::span<uint8_t> output, |
15 | | std::span<const uint8_t> input, |
16 | 0 | size_t offset) { |
17 | | // This leaks information about the input length, but this happens |
18 | | // unavoidably since we are unable to ready any bytes besides those |
19 | | // in input[0..n] |
20 | 0 | BOTAN_ARG_CHECK(output.size() >= input.size(), "Invalid span lengths"); |
21 | | |
22 | | /* |
23 | | * We do not poison the input here because if we did we would have |
24 | | * to unpoison it at exit. We assume instead that callers have |
25 | | * already poisoned the input and will unpoison it at their own |
26 | | * time. |
27 | | */ |
28 | 0 | CT::poison(offset); |
29 | | |
30 | | /** |
31 | | * Zeroize the entire output buffer to get started |
32 | | */ |
33 | 0 | clear_mem(output); |
34 | | |
35 | | /* |
36 | | * If the offset is greater than input length, then the arguments are |
37 | | * invalid. Ideally we would throw an exception, but that leaks |
38 | | * information about the offset. Instead treat it as if the input |
39 | | * was invalid. |
40 | | */ |
41 | 0 | accept = accept && CT::Mask<size_t>::is_lte(offset, input.size()).as_choice(); |
42 | | |
43 | | /* |
44 | | * If the input is invalid, then set offset == input_length |
45 | | */ |
46 | 0 | offset = CT::Mask<size_t>::from_choice(accept).select(offset, input.size()); |
47 | | |
48 | | /* |
49 | | * Move the desired output bytes to the front using a slow (O^n) |
50 | | * but constant time loop that does not leak the value of the offset |
51 | | */ |
52 | 0 | for(size_t i = 0; i != input.size(); ++i) { |
53 | | /* |
54 | | * If bad_input was set then we modified offset to equal the input_length. |
55 | | * In that case, this_loop will be greater than input_length, and so is_eq |
56 | | * mask will always be false. As a result none of the input values will be |
57 | | * written to output. |
58 | | * |
59 | | * This is ignoring the possibility of integer overflow of offset + i. But |
60 | | * for this to happen the input would have to consume nearly the entire |
61 | | * address space. |
62 | | */ |
63 | 0 | const size_t this_loop = offset + i; |
64 | | |
65 | | /* |
66 | | start index from i rather than 0 since we know j must be >= i + offset |
67 | | to have any effect, and starting from i does not reveal information |
68 | | */ |
69 | 0 | for(size_t j = i; j != input.size(); ++j) { |
70 | 0 | const uint8_t b = input[j]; |
71 | 0 | const auto is_eq = CT::Mask<size_t>::is_equal(j, this_loop); |
72 | 0 | output[i] |= is_eq.if_set_return(b); |
73 | 0 | } |
74 | 0 | } |
75 | | |
76 | | // This will always be zero if the input was invalid |
77 | 0 | const size_t output_bytes = input.size() - offset; |
78 | |
|
79 | 0 | CT::unpoison_all(output, output_bytes); |
80 | |
|
81 | 0 | return CT::Option<size_t>(output_bytes, accept); |
82 | 0 | } |
83 | | |
84 | 0 | size_t CT::count_leading_zero_bytes(std::span<const uint8_t> input) { |
85 | 0 | size_t leading_zeros = 0; |
86 | 0 | auto only_zeros = Mask<uint8_t>::set(); |
87 | 0 | for(size_t i = 0; i != input.size(); ++i) { |
88 | 0 | only_zeros &= CT::Mask<uint8_t>::is_zero(input[i]); |
89 | 0 | leading_zeros += only_zeros.if_set_return(1); |
90 | 0 | } |
91 | 0 | return leading_zeros; |
92 | 0 | } |
93 | | |
94 | 0 | secure_vector<uint8_t> CT::strip_leading_zeros(std::span<const uint8_t> input) { |
95 | 0 | const size_t leading_zeros = CT::count_leading_zero_bytes(input); |
96 | |
|
97 | 0 | secure_vector<uint8_t> output(input.size()); |
98 | |
|
99 | 0 | const auto written = CT::copy_output(CT::Choice::yes(), output, input, leading_zeros); |
100 | | |
101 | | /* |
102 | | This is potentially not const time, depending on how std::vector is |
103 | | implemented. But since we are always reducing length, it should |
104 | | just amount to setting the member var holding the length. |
105 | | */ |
106 | 0 | output.resize(written.value_or(0)); |
107 | |
|
108 | 0 | return output; |
109 | 0 | } |
110 | | |
111 | | } // namespace Botan |