Coverage Report

Created: 2021-05-04 09:02

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