randombit / botan
Showing 2 of 3 files from the diff.

@@ -230,6 +230,46 @@
Loading
230 230
         result.test_eq_sz("CT::is_less32", Botan::CT::Mask<uint32_t>::is_lt(0xFFFFFFFF, 5).value(), 0x00000000);
231 231
         result.test_eq_sz("CT::is_less32", Botan::CT::Mask<uint32_t>::is_lt(5, 0xFFFFFFFF).value(), 0xFFFFFFFF);
232 232
233 +
         for(auto bad_input : { 0, 1 })
234 +
            {
235 +
            for(size_t input_length : { 0, 1, 2, 32 })
236 +
               {
237 +
               for(size_t offset = 0; offset != input_length + 1; ++offset)
238 +
                  {
239 +
                  const auto mask = Botan::CT::Mask<uint8_t>::expand(static_cast<uint8_t>(bad_input));
240 +
241 +
                  std::vector<uint8_t> input(input_length);
242 +
                  rng().randomize(input.data(), input.size());
243 +
244 +
                  auto output = Botan::CT::copy_output(mask,
245 +
                                                       input.data(),
246 +
                                                       input.size(),
247 +
                                                       offset);
248 +
249 +
                  result.test_eq_sz("CT::copy_output capacity", output.capacity(), input.size());
250 +
251 +
                  if(bad_input)
252 +
                     {
253 +
                     result.confirm("If bad input, no output", output.empty());
254 +
                     }
255 +
                  else
256 +
                     {
257 +
                     if(offset >= input_length)
258 +
                        {
259 +
                        result.confirm("If offset is too large, output is empty", output.empty());
260 +
                        }
261 +
                     else
262 +
                        {
263 +
                        result.test_eq_sz("CT::copy_output length", output.size(), input.size() - offset);
264 +
265 +
                        for(size_t i = 0; i != output.size(); ++i)
266 +
                           result.test_eq_sz("CT::copy_output offset", output[i], input[i + offset]);
267 +
                        }
268 +
                     }
269 +
                  }
270 +
               }
271 +
            }
272 +
233 273
         return {result};
234 274
         }
235 275
   };

@@ -1,5 +1,5 @@
Loading
1 1
/*
2 -
* (C) 2018 Jack Lloyd
2 +
* (C) 2018,2021 Jack Lloyd
3 3
*
4 4
* Botan is released under the Simplified BSD License (see license.txt)
5 5
*/
@@ -10,33 +10,56 @@
Loading
10 10
11 11
namespace CT {
12 12
13 -
secure_vector<uint8_t> copy_output(CT::Mask<uint8_t> bad_input,
13 +
secure_vector<uint8_t> copy_output(CT::Mask<uint8_t> bad_input_u8,
14 14
                                   const uint8_t input[],
15 15
                                   size_t input_length,
16 16
                                   size_t offset)
17 17
   {
18 -
   if(input_length == 0)
19 -
      return secure_vector<uint8_t>();
20 -
21 18
   /*
22 -
   * Ensure at runtime that offset <= input_length. This is an invalid input,
23 -
   * but we can't throw without using the poisoned value. Instead, if it happens,
24 -
   * set offset to be equal to the input length (so output_bytes becomes 0 and
25 -
   * the returned vector is empty)
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.
26 23
   */
27 -
   const auto valid_offset = CT::Mask<size_t>::is_lte(offset, input_length);
28 -
   offset = valid_offset.select(offset, input_length);
29 -
30 -
   const size_t output_bytes = input_length - offset;
24 +
   CT::poison(&offset, sizeof(size_t));
31 25
32 26
   secure_vector<uint8_t> output(input_length);
33 27
28 +
   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 +
   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 +
   offset = bad_input.select(input_length, offset);
44 +
34 45
   /*
35 46
   Move the desired output bytes to the front using a slow (O^n)
36 47
   but constant time loop that does not leak the value of the offset
37 48
   */
38 49
   for(size_t i = 0; i != input_length; ++i)
39 50
      {
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 +
      const size_t this_loop = offset + i;
62 +
40 63
      /*
41 64
      start index from i rather than 0 since we know j must be >= i + offset
42 65
      to have any effect, and starting from i does not reveal information
@@ -44,12 +67,12 @@
Loading
44 67
      for(size_t j = i; j != input_length; ++j)
45 68
         {
46 69
         const uint8_t b = input[j];
47 -
         const auto is_eq = CT::Mask<size_t>::is_equal(j, offset + i);
70 +
         const auto is_eq = CT::Mask<size_t>::is_equal(j, this_loop);
48 71
         output[i] |= is_eq.if_set_return(b);
49 72
         }
50 73
      }
51 74
52 -
   bad_input.if_set_zero_out(output.data(), output.size());
75 +
   const size_t output_bytes = input_length - offset;
53 76
54 77
   CT::unpoison(output.data(), output.size());
55 78
   CT::unpoison(output_bytes);
Files Coverage
src 92.38%
Project Totals (567 files) 92.38%
1
---
2

3
# Documentation
4
# https://github.com/codecov/support/wiki/Codecov-Yaml#full-yaml
5
#
6
# Validate this file
7
# curl --data-binary @codecov.yml https://codecov.io/validate
8

9
coverage:
10
  status:
11
    project:
12
      default:
13
        # Random seeds in tests lead to a +/-0.05% coverage span even for PRs
14
        # that do not change source code
15
        threshold: 0.05
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading