Coverage Report

Created: 2024-11-21 07:00

/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