Botan 3.5.0
Crypto and TLS for C&
ct_utils.h
Go to the documentation of this file.
1/*
2* Functions for constant time operations on data and testing of
3* constant time annotations using valgrind.
4*
5* For more information about constant time programming see
6* Wagner, Molnar, et al "The Program Counter Security Model"
7*
8* (C) 2010 Falko Strenzke
9* (C) 2015,2016,2018,2024 Jack Lloyd
10*
11* Botan is released under the Simplified BSD License (see license.txt)
12*/
13
14#ifndef BOTAN_CT_UTILS_H_
15#define BOTAN_CT_UTILS_H_
16
17#include <botan/secmem.h>
18#include <botan/internal/bit_ops.h>
19#include <type_traits>
20#include <vector>
21
22#if defined(BOTAN_HAS_VALGRIND)
23 #include <valgrind/memcheck.h>
24#endif
25
26namespace Botan::CT {
27
28/**
29* Use valgrind to mark the contents of memory as being undefined.
30* Valgrind will accept operations which manipulate undefined values,
31* but will warn if an undefined value is used to decided a conditional
32* jump or a load/store address. So if we poison all of our inputs we
33* can confirm that the operations in question are truly const time
34* when compiled by whatever compiler is in use.
35*
36* Even better, the VALGRIND_MAKE_MEM_* macros work even when the
37* program is not run under valgrind (though with a few cycles of
38* overhead, which is unfortunate in final binaries as these
39* annotations tend to be used in fairly important loops).
40*
41* This approach was first used in ctgrind (https://github.com/agl/ctgrind)
42* but calling the valgrind mecheck API directly works just as well and
43* doesn't require a custom patched valgrind.
44*/
45template <typename T>
46constexpr inline void poison(const T* p, size_t n) {
47#if defined(BOTAN_HAS_VALGRIND)
48 if(!std::is_constant_evaluated()) {
49 VALGRIND_MAKE_MEM_UNDEFINED(p, n * sizeof(T));
50 }
51#endif
52
53 BOTAN_UNUSED(p, n);
54}
55
56template <typename T>
57constexpr inline void unpoison(const T* p, size_t n) {
58#if defined(BOTAN_HAS_VALGRIND)
59 if(!std::is_constant_evaluated()) {
60 VALGRIND_MAKE_MEM_DEFINED(p, n * sizeof(T));
61 }
62#endif
63
64 BOTAN_UNUSED(p, n);
65}
66
67template <typename T>
68constexpr inline void unpoison(T& p) {
69#if defined(BOTAN_HAS_VALGRIND)
70 if(!std::is_constant_evaluated()) {
71 VALGRIND_MAKE_MEM_DEFINED(&p, sizeof(T));
72 }
73#endif
74
75 BOTAN_UNUSED(p);
76}
77
78/**
79* This function returns its argument, but (if called in a non-constexpr context)
80* attempts to prevent the compiler from reasoning about the value or the possible
81* range of values. Such optimizations have a way of breaking constant time code.
82*/
83template <typename T>
84constexpr inline T value_barrier(T x) {
85 if(!std::is_constant_evaluated()) {
86 /*
87 * For compilers without inline asm, is there something else we can do?
88 *
89 * For instance we could potentially launder the value through a
90 * `volatile T` or `volatile T*`. This would require some experimentation.
91 *
92 * GCC has an attribute noipa which disables interprocedural analysis, which
93 * might be useful here. However Clang does not currently support this attribute.
94 *
95 * We may want a "stronger" statement such as
96 * asm volatile("" : "+r,m"(x) : : "memory);
97 * (see https://theunixzoo.co.uk/blog/2021-10-14-preventing-optimisations.html)
98 * however the current approach seems sufficient with current compilers,
99 * and is minimally damaging with regards to degrading code generation.
100 */
101#if defined(BOTAN_USE_GCC_INLINE_ASM)
102 asm("" : "+r"(x) : /* no input */);
103#endif
104 return x;
105 } else {
106 return x;
107 }
108}
109
110/**
111* A Choice is used for constant-time conditionals.
112*
113* Internally it always is either |0| (all 0 bits) or |1| (all 1 bits)
114* and measures are taken to block compilers from reasoning about the
115* expected value of a Choice.
116*/
118 public:
119 /**
120 * If v == 0 return an unset (false) Choice, otherwise a set Choice
121 */
122 template <typename T>
123 requires std::unsigned_integral<T> && (!std::same_as<bool, T>)
124 constexpr static Choice from_int(T v) {
125 // Mask of T that is either |0| or |1|
126 const T v_is_0 = ct_is_zero<T>(value_barrier<T>(v));
127
128 // We want the mask to be set if v != 0 so we must check that
129 // v_is_0 is itself zero.
130 //
131 // Also sizeof(T) may not equal sizeof(uint32_t) so we must
132 // use ct_is_zero<uint32_t>. It's ok to either truncate or
133 // zero extend v_is_0 to 32 bits since we know it is |0| or |1|
134 // so even just the low bit is sufficient.
135 return Choice(ct_is_zero<uint32_t>(static_cast<uint32_t>(v_is_0)));
136 }
137
138 constexpr static Choice yes() { return Choice(static_cast<uint32_t>(-1)); }
139
140 constexpr static Choice no() { return Choice(0); }
141
142 constexpr Choice operator!() const { return Choice(~value()); }
143
144 constexpr Choice operator&&(const Choice& other) const { return Choice(value() & other.value()); }
145
146 constexpr Choice operator||(const Choice& other) const { return Choice(value() | other.value()); }
147
148 constexpr Choice operator!=(const Choice& other) const { return Choice(value() ^ other.value()); }
149
150 /**
151 * Unsafe conversion to bool
152 *
153 * This conversion itself is (probably) constant time, but once the
154 * choice is reduced to a simple bool, it's entirely possible for the
155 * compiler to perform range analysis on the values, since there are just
156 * the two. As a consequence even if the caller is not using this in an
157 * obviously branchy way (`if(choice.as_bool()) ...`) a smart compiler
158 * may introduce branches depending on the value.
159 */
160 constexpr bool as_bool() const { return m_value != 0; }
161
162 /// Return the masked value
163 constexpr uint32_t value() const { return value_barrier(m_value); }
164
165 constexpr Choice(const Choice& other) = default;
166 constexpr Choice(Choice&& other) = default;
167 constexpr Choice& operator=(const Choice& other) noexcept = default;
168 constexpr Choice& operator=(Choice&& other) noexcept = default;
169
170 private:
171 constexpr explicit Choice(uint32_t v) : m_value(v) {}
172
173 uint32_t m_value;
174};
175
176/**
177* A Mask type used for constant-time operations. A Mask<T> always has value
178* either |0| (all bits cleared) or |1| (all bits set). All operations in a Mask<T>
179* are intended to compile to code which does not contain conditional jumps.
180* This must be verified with tooling (eg binary disassembly or using valgrind)
181* since you never know what a compiler might do.
182*/
183template <typename T>
184class Mask final {
185 public:
186 static_assert(std::is_unsigned<T>::value && !std::is_same<bool, T>::value,
187 "Only unsigned integer types are supported by CT::Mask");
188
189 Mask(const Mask<T>& other) = default;
190 Mask<T>& operator=(const Mask<T>& other) = default;
191
192 /**
193 * Derive a Mask from a Mask of a larger type
194 */
195 template <typename U>
196 constexpr Mask(Mask<U> o) : m_mask(static_cast<T>(o.value())) {
197 static_assert(sizeof(U) > sizeof(T), "sizes ok");
198 }
199
200 /**
201 * Return a Mask<T> of |1| (all bits set)
202 */
203 static constexpr Mask<T> set() { return Mask<T>(static_cast<T>(~0)); }
204
205 /**
206 * Return a Mask<T> of |0| (all bits cleared)
207 */
208 static constexpr Mask<T> cleared() { return Mask<T>(0); }
209
210 /**
211 * Return a Mask<T> which is set if v is != 0
212 */
213 static constexpr Mask<T> expand(T v) { return ~Mask<T>::is_zero(value_barrier<T>(v)); }
214
215 /**
216 * Return a Mask<T> which is set if choice is set
217 */
218 static constexpr Mask<T> from_choice(Choice c) {
219 if constexpr(sizeof(T) <= sizeof(uint32_t)) {
220 // Take advantage of the fact that Choice's mask is always
221 // either |0| or |1|
222 return Mask<T>(static_cast<T>(c.value()));
223 } else {
224 return ~Mask<T>::is_zero(c.value());
225 }
226 }
227
228 /**
229 * Return a Mask<T> which is set if the top bit of v is set
230 */
232
233 /**
234 * Return a Mask<T> which is set if m is set
235 */
236 template <typename U>
237 static constexpr Mask<T> expand(Mask<U> m) {
238 static_assert(sizeof(U) < sizeof(T), "sizes ok");
239 return ~Mask<T>::is_zero(m.value());
240 }
241
242 /**
243 * Return a Mask<T> which is set if v is == 0 or cleared otherwise
244 */
245 static constexpr Mask<T> is_zero(T x) { return Mask<T>(ct_is_zero<T>(value_barrier<T>(x))); }
246
247 /**
248 * Return a Mask<T> which is set if x == y
249 */
250 static constexpr Mask<T> is_equal(T x, T y) {
251 const T diff = value_barrier(x) ^ value_barrier(y);
252 return Mask<T>::is_zero(diff);
253 }
254
255 /**
256 * Return a Mask<T> which is set if x < y
257 */
258 static constexpr Mask<T> is_lt(T x, T y) {
259 T u = x ^ ((x ^ y) | ((x - y) ^ x));
260 return Mask<T>::expand_top_bit(u);
261 }
262
263 /**
264 * Return a Mask<T> which is set if x > y
265 */
266 static constexpr Mask<T> is_gt(T x, T y) { return Mask<T>::is_lt(y, x); }
267
268 /**
269 * Return a Mask<T> which is set if x <= y
270 */
271 static constexpr Mask<T> is_lte(T x, T y) { return ~Mask<T>::is_gt(x, y); }
272
273 /**
274 * Return a Mask<T> which is set if x >= y
275 */
276 static constexpr Mask<T> is_gte(T x, T y) { return ~Mask<T>::is_lt(x, y); }
277
278 static constexpr Mask<T> is_within_range(T v, T l, T u) {
279 //return Mask<T>::is_gte(v, l) & Mask<T>::is_lte(v, u);
280
281 const T v_lt_l = v ^ ((v ^ l) | ((v - l) ^ v));
282 const T v_gt_u = u ^ ((u ^ v) | ((u - v) ^ u));
283 const T either = value_barrier(v_lt_l) | value_barrier(v_gt_u);
284 return ~Mask<T>::expand_top_bit(either);
285 }
286
287 static constexpr Mask<T> is_any_of(T v, std::initializer_list<T> accepted) {
288 T accept = 0;
289
290 for(auto a : accepted) {
291 const T diff = a ^ v;
292 const T eq_zero = value_barrier<T>(~diff & (diff - 1));
293 accept |= eq_zero;
294 }
295
296 return Mask<T>::expand_top_bit(accept);
297 }
298
299 /**
300 * AND-combine two masks
301 */
303 m_mask &= o.value();
304 return (*this);
305 }
306
307 /**
308 * XOR-combine two masks
309 */
311 m_mask ^= o.value();
312 return (*this);
313 }
314
315 /**
316 * OR-combine two masks
317 */
319 m_mask |= o.value();
320 return (*this);
321 }
322
323 /**
324 * AND-combine two masks
325 */
326 friend Mask<T> operator&(Mask<T> x, Mask<T> y) { return Mask<T>(x.value() & y.value()); }
327
328 /**
329 * XOR-combine two masks
330 */
331 friend Mask<T> operator^(Mask<T> x, Mask<T> y) { return Mask<T>(x.value() ^ y.value()); }
332
333 /**
334 * OR-combine two masks
335 */
336 friend Mask<T> operator|(Mask<T> x, Mask<T> y) { return Mask<T>(x.value() | y.value()); }
337
338 /**
339 * Negate this mask
340 */
341 constexpr Mask<T> operator~() const { return Mask<T>(~value()); }
342
343 /**
344 * Return x if the mask is set, or otherwise zero
345 */
346 constexpr T if_set_return(T x) const { return value() & x; }
347
348 /**
349 * Return x if the mask is cleared, or otherwise zero
350 */
351 constexpr T if_not_set_return(T x) const { return ~value() & x; }
352
353 /**
354 * If this mask is set, return x, otherwise return y
355 */
356 constexpr T select(T x, T y) const { return choose(value(), x, y); }
357
358 constexpr T select_and_unpoison(T x, T y) const {
359 T r = this->select(x, y);
360 CT::unpoison(r);
361 return r;
362 }
363
364 /**
365 * If this mask is set, return x, otherwise return y
366 */
367 Mask<T> select_mask(Mask<T> x, Mask<T> y) const { return Mask<T>(select(x.value(), y.value())); }
368
369 /**
370 * Conditionally set output to x or y, depending on if mask is set or
371 * cleared (resp)
372 */
373 constexpr void select_n(T output[], const T x[], const T y[], size_t len) const {
374 for(size_t i = 0; i != len; ++i) {
375 output[i] = this->select(x[i], y[i]);
376 }
377 }
378
379 /**
380 * If this mask is set, zero out buf, otherwise do nothing
381 */
382 constexpr void if_set_zero_out(T buf[], size_t elems) {
383 for(size_t i = 0; i != elems; ++i) {
384 buf[i] = this->if_not_set_return(buf[i]);
385 }
386 }
387
388 /**
389 * Return the value of the mask, unpoisoned
390 */
391 constexpr T unpoisoned_value() const {
392 T r = value();
393 CT::unpoison(r);
394 return r;
395 }
396
397 /**
398 * Unsafe conversion to bool
399 *
400 * This conversion itself is (probably) constant time, but once the
401 * mask is reduced to a simple bool, it's entirely possible for the
402 * compiler to perform range analysis on the values, since there are just
403 * the two. As a consequence even if the caller is not using this in an
404 * obviously branchy way (`if(mask.as_bool()) ...`) a smart compiler
405 * may introduce branches depending on the value.
406 */
407 constexpr bool as_bool() const { return unpoisoned_value() != 0; }
408
409 /**
410 * Return a Choice based on this mask
411 */
413
414 /**
415 * Return the underlying value of the mask
416 */
417 constexpr T value() const { return value_barrier<T>(m_mask); }
418
419 private:
420 constexpr Mask(T m) : m_mask(m) {}
421
422 T m_mask;
423};
424
425template <typename T>
426constexpr inline Mask<T> conditional_copy_mem(Mask<T> mask, T* to, const T* from0, const T* from1, size_t elems) {
427 mask.select_n(to, from0, from1, elems);
428 return mask;
429}
430
431template <typename T>
432constexpr inline Mask<T> conditional_copy_mem(T cnd, T* to, const T* from0, const T* from1, size_t elems) {
433 const auto mask = CT::Mask<T>::expand(cnd);
434 return CT::conditional_copy_mem(mask, to, from0, from1, elems);
435}
436
437template <typename T>
438constexpr inline Mask<T> conditional_assign_mem(T cnd, T* sink, const T* src, size_t elems) {
439 const auto mask = CT::Mask<T>::expand(cnd);
440 mask.select_n(sink, src, sink, elems);
441 return mask;
442}
443
444template <typename T>
445constexpr inline Mask<T> conditional_assign_mem(Choice cnd, T* sink, const T* src, size_t elems) {
446 const auto mask = CT::Mask<T>::from_choice(cnd);
447 mask.select_n(sink, src, sink, elems);
448 return mask;
449}
450
451template <typename T>
452constexpr inline void conditional_swap(bool cnd, T& x, T& y) {
453 const auto swap = CT::Mask<T>::expand(cnd);
454
455 T t0 = swap.select(y, x);
456 T t1 = swap.select(x, y);
457 x = t0;
458 y = t1;
459}
460
461template <typename T>
462constexpr inline void conditional_swap_ptr(bool cnd, T& x, T& y) {
463 uintptr_t xp = reinterpret_cast<uintptr_t>(x);
464 uintptr_t yp = reinterpret_cast<uintptr_t>(y);
465
466 conditional_swap<uintptr_t>(cnd, xp, yp);
467
468 x = reinterpret_cast<T>(xp);
469 y = reinterpret_cast<T>(yp);
470}
471
472template <typename T>
473constexpr inline CT::Mask<T> all_zeros(const T elem[], size_t len) {
474 T sum = 0;
475 for(size_t i = 0; i != len; ++i) {
476 sum |= elem[i];
477 }
478 return CT::Mask<T>::is_zero(sum);
479}
480
481/**
482* Compare two arrays of equal size and return a Mask indicating if
483* they are equal or not. The mask is set if they are identical.
484*/
485template <typename T>
486constexpr inline CT::Mask<T> is_equal(const T x[], const T y[], size_t len) {
487 if(std::is_constant_evaluated()) {
488 T difference = 0;
489
490 for(size_t i = 0; i != len; ++i) {
491 difference = difference | (x[i] ^ y[i]);
492 }
493
494 return CT::Mask<T>::is_zero(difference);
495 } else {
496 volatile T difference = 0;
497
498 for(size_t i = 0; i != len; ++i) {
499 difference = difference | (x[i] ^ y[i]);
500 }
501
502 return CT::Mask<T>::is_zero(difference);
503 }
504}
505
506/**
507* Compare two arrays of equal size and return a Mask indicating if
508* they are equal or not. The mask is set if they differ.
509*/
510template <typename T>
511constexpr inline CT::Mask<T> is_not_equal(const T x[], const T y[], size_t len) {
512 return ~CT::is_equal(x, y, len);
513}
514
515/**
516* If bad_input is unset, return input[offset:input_length] copied to new
517* buffer. If bad_input is set, return an empty vector. In all cases, the capacity
518* of the vector is equal to input_length
519*
520* This function attempts to avoid leaking the following:
521* - if bad_input was set or not
522* - the value of offset
523* - the values in input[]
524*
525* This function leaks the value of input_length
526*/
529 const uint8_t input[],
530 size_t input_length,
531 size_t offset);
532
533secure_vector<uint8_t> strip_leading_zeros(const uint8_t in[], size_t length);
534
536 return strip_leading_zeros(in.data(), in.size());
537}
538
539} // namespace Botan::CT
540
541#endif
#define BOTAN_UNUSED
Definition assert.h:118
static constexpr Choice yes()
Definition ct_utils.h:138
static constexpr Choice from_int(T v)
Definition ct_utils.h:124
constexpr Choice & operator=(const Choice &other) noexcept=default
constexpr Choice(const Choice &other)=default
constexpr Choice operator!=(const Choice &other) const
Definition ct_utils.h:148
constexpr Choice operator||(const Choice &other) const
Definition ct_utils.h:146
constexpr Choice operator!() const
Definition ct_utils.h:142
static constexpr Choice no()
Definition ct_utils.h:140
constexpr bool as_bool() const
Definition ct_utils.h:160
constexpr Choice(Choice &&other)=default
constexpr Choice operator&&(const Choice &other) const
Definition ct_utils.h:144
constexpr uint32_t value() const
Return the masked value.
Definition ct_utils.h:163
constexpr Choice & operator=(Choice &&other) noexcept=default
Mask< T > select_mask(Mask< T > x, Mask< T > y) const
Definition ct_utils.h:367
constexpr T value() const
Definition ct_utils.h:417
constexpr void if_set_zero_out(T buf[], size_t elems)
Definition ct_utils.h:382
static constexpr Mask< T > is_lte(T x, T y)
Definition ct_utils.h:271
static constexpr Mask< T > is_gte(T x, T y)
Definition ct_utils.h:276
constexpr T if_not_set_return(T x) const
Definition ct_utils.h:351
constexpr Mask(Mask< U > o)
Definition ct_utils.h:196
static constexpr Mask< T > set()
Definition ct_utils.h:203
friend Mask< T > operator|(Mask< T > x, Mask< T > y)
Definition ct_utils.h:336
Mask< T > & operator^=(Mask< T > o)
Definition ct_utils.h:310
constexpr T unpoisoned_value() const
Definition ct_utils.h:391
constexpr void select_n(T output[], const T x[], const T y[], size_t len) const
Definition ct_utils.h:373
friend Mask< T > operator^(Mask< T > x, Mask< T > y)
Definition ct_utils.h:331
constexpr T if_set_return(T x) const
Definition ct_utils.h:346
Mask< T > & operator=(const Mask< T > &other)=default
Mask(const Mask< T > &other)=default
static constexpr Mask< T > expand(Mask< U > m)
Definition ct_utils.h:237
Mask< T > & operator&=(Mask< T > o)
Definition ct_utils.h:302
constexpr T select_and_unpoison(T x, T y) const
Definition ct_utils.h:358
static constexpr Mask< T > expand(T v)
Definition ct_utils.h:213
constexpr T select(T x, T y) const
Definition ct_utils.h:356
static constexpr Mask< T > expand_top_bit(T v)
Definition ct_utils.h:231
static constexpr Mask< T > is_within_range(T v, T l, T u)
Definition ct_utils.h:278
constexpr CT::Choice as_choice() const
Definition ct_utils.h:412
static constexpr Mask< T > from_choice(Choice c)
Definition ct_utils.h:218
static constexpr Mask< T > is_equal(T x, T y)
Definition ct_utils.h:250
Mask< T > & operator|=(Mask< T > o)
Definition ct_utils.h:318
constexpr Mask< T > operator~() const
Definition ct_utils.h:341
static constexpr Mask< T > is_gt(T x, T y)
Definition ct_utils.h:266
constexpr bool as_bool() const
Definition ct_utils.h:407
static constexpr Mask< T > is_lt(T x, T y)
Definition ct_utils.h:258
friend Mask< T > operator&(Mask< T > x, Mask< T > y)
Definition ct_utils.h:326
static constexpr Mask< T > is_any_of(T v, std::initializer_list< T > accepted)
Definition ct_utils.h:287
static constexpr Mask< T > is_zero(T x)
Definition ct_utils.h:245
static constexpr Mask< T > cleared()
Definition ct_utils.h:208
int(* final)(unsigned char *, CTX *)
#define BOTAN_TEST_API
Definition compiler.h:51
FE_25519 T
Definition ge.cpp:34
constexpr T value_barrier(T x)
Definition ct_utils.h:84
constexpr void conditional_swap_ptr(bool cnd, T &x, T &y)
Definition ct_utils.h:462
constexpr void conditional_swap(bool cnd, T &x, T &y)
Definition ct_utils.h:452
constexpr Mask< T > conditional_assign_mem(T cnd, T *sink, const T *src, size_t elems)
Definition ct_utils.h:438
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
secure_vector< uint8_t > strip_leading_zeros(const uint8_t in[], size_t length)
Definition ct_utils.cpp:84
constexpr Mask< T > conditional_copy_mem(Mask< T > mask, T *to, const T *from0, const T *from1, size_t elems)
Definition ct_utils.h:426
constexpr CT::Mask< T > is_not_equal(const T x[], const T y[], size_t len)
Definition ct_utils.h:511
constexpr CT::Mask< T > is_equal(const T x[], const T y[], size_t len)
Definition ct_utils.h:486
constexpr void unpoison(const T *p, size_t n)
Definition ct_utils.h:57
constexpr CT::Mask< T > all_zeros(const T elem[], size_t len)
Definition ct_utils.h:473
constexpr void poison(const T *p, size_t n)
Definition ct_utils.h:46
constexpr T choose(T mask, T a, T b)
Definition bit_ops.h:193
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:61
constexpr T ct_is_zero(T x)
Definition bit_ops.h:33
constexpr T expand_top_bit(T a)
Definition bit_ops.h:23