Botan 3.4.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 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>
46inline 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* A Mask type used for constant-time operations. A Mask<T> always has value
80* either 0 (all bits cleared) or ~0 (all bits set). All operations in a Mask<T>
81* are intended to compile to code which does not contain conditional jumps.
82* This must be verified with tooling (eg binary disassembly or using valgrind)
83* since you never know what a compiler might do.
84*/
85template <typename T>
86class Mask final {
87 public:
88 static_assert(std::is_unsigned<T>::value && !std::is_same<bool, T>::value,
89 "Only unsigned integer types are supported by CT::Mask");
90
91 Mask(const Mask<T>& other) = default;
92 Mask<T>& operator=(const Mask<T>& other) = default;
93
94 /**
95 * Derive a Mask from a Mask of a larger type
96 */
97 template <typename U>
98 constexpr Mask(Mask<U> o) : m_mask(static_cast<T>(o.value())) {
99 static_assert(sizeof(U) > sizeof(T), "sizes ok");
100 }
101
102 /**
103 * Return a Mask<T> with all bits set
104 */
105 static constexpr Mask<T> set() { return Mask<T>(static_cast<T>(~0)); }
106
107 /**
108 * Return a Mask<T> with all bits cleared
109 */
110 static constexpr Mask<T> cleared() { return Mask<T>(0); }
111
112 /**
113 * Return a Mask<T> which is set if v is != 0
114 */
115 static constexpr Mask<T> expand(T v) { return ~Mask<T>::is_zero(v); }
116
117 /**
118 * Return a Mask<T> which is set if m is set
119 */
120 template <typename U>
121 static constexpr Mask<T> expand(Mask<U> m) {
122 static_assert(sizeof(U) < sizeof(T), "sizes ok");
123 return ~Mask<T>::is_zero(m.value());
124 }
125
126 /**
127 * Return a Mask<T> which is set if v is == 0 or cleared otherwise
128 */
129 static constexpr Mask<T> is_zero(T x) { return Mask<T>(ct_is_zero<T>(x)); }
130
131 /**
132 * Return a Mask<T> which is set if x == y
133 */
134 static constexpr Mask<T> is_equal(T x, T y) { return Mask<T>::is_zero(static_cast<T>(x ^ y)); }
135
136 /**
137 * Return a Mask<T> which is set if x < y
138 */
139 static constexpr Mask<T> is_lt(T x, T y) { return Mask<T>(expand_top_bit<T>(x ^ ((x ^ y) | ((x - y) ^ x)))); }
140
141 /**
142 * Return a Mask<T> which is set if x > y
143 */
144 static constexpr Mask<T> is_gt(T x, T y) { return Mask<T>::is_lt(y, x); }
145
146 /**
147 * Return a Mask<T> which is set if x <= y
148 */
149 static constexpr Mask<T> is_lte(T x, T y) { return ~Mask<T>::is_gt(x, y); }
150
151 /**
152 * Return a Mask<T> which is set if x >= y
153 */
154 static constexpr Mask<T> is_gte(T x, T y) { return ~Mask<T>::is_lt(x, y); }
155
156 static constexpr Mask<T> is_within_range(T v, T l, T u) {
157 //return Mask<T>::is_gte(v, l) & Mask<T>::is_lte(v, u);
158
159 const T v_lt_l = v ^ ((v ^ l) | ((v - l) ^ v));
160 const T v_gt_u = u ^ ((u ^ v) | ((u - v) ^ u));
161 const T either = v_lt_l | v_gt_u;
162 return ~Mask<T>(expand_top_bit(either));
163 }
164
165 static constexpr Mask<T> is_any_of(T v, std::initializer_list<T> accepted) {
166 T accept = 0;
167
168 for(auto a : accepted) {
169 const T diff = a ^ v;
170 const T eq_zero = ~diff & (diff - 1);
171 accept |= eq_zero;
172 }
173
174 return Mask<T>(expand_top_bit(accept));
175 }
176
177 /**
178 * AND-combine two masks
179 */
181 m_mask &= o.value();
182 return (*this);
183 }
184
185 /**
186 * XOR-combine two masks
187 */
189 m_mask ^= o.value();
190 return (*this);
191 }
192
193 /**
194 * OR-combine two masks
195 */
197 m_mask |= o.value();
198 return (*this);
199 }
200
201 /**
202 * AND-combine two masks
203 */
204 friend Mask<T> operator&(Mask<T> x, Mask<T> y) { return Mask<T>(x.value() & y.value()); }
205
206 /**
207 * XOR-combine two masks
208 */
209 friend Mask<T> operator^(Mask<T> x, Mask<T> y) { return Mask<T>(x.value() ^ y.value()); }
210
211 /**
212 * OR-combine two masks
213 */
214 friend Mask<T> operator|(Mask<T> x, Mask<T> y) { return Mask<T>(x.value() | y.value()); }
215
216 /**
217 * Negate this mask
218 */
219 constexpr Mask<T> operator~() const { return Mask<T>(~value()); }
220
221 /**
222 * Return x if the mask is set, or otherwise zero
223 */
224 constexpr T if_set_return(T x) const { return m_mask & x; }
225
226 /**
227 * Return x if the mask is cleared, or otherwise zero
228 */
229 constexpr T if_not_set_return(T x) const { return ~m_mask & x; }
230
231 /**
232 * If this mask is set, return x, otherwise return y
233 */
234 constexpr T select(T x, T y) const { return choose(value(), x, y); }
235
236 constexpr T select_and_unpoison(T x, T y) const {
237 T r = this->select(x, y);
238 CT::unpoison(r);
239 return r;
240 }
241
242 /**
243 * If this mask is set, return x, otherwise return y
244 */
245 Mask<T> select_mask(Mask<T> x, Mask<T> y) const { return Mask<T>(select(x.value(), y.value())); }
246
247 /**
248 * Conditionally set output to x or y, depending on if mask is set or
249 * cleared (resp)
250 */
251 constexpr void select_n(T output[], const T x[], const T y[], size_t len) const {
252 for(size_t i = 0; i != len; ++i) {
253 output[i] = this->select(x[i], y[i]);
254 }
255 }
256
257 /**
258 * If this mask is set, zero out buf, otherwise do nothing
259 */
260 constexpr void if_set_zero_out(T buf[], size_t elems) {
261 for(size_t i = 0; i != elems; ++i) {
262 buf[i] = this->if_not_set_return(buf[i]);
263 }
264 }
265
266 /**
267 * Return the value of the mask, unpoisoned
268 */
269 constexpr T unpoisoned_value() const {
270 T r = value();
271 CT::unpoison(r);
272 return r;
273 }
274
275 /**
276 * Return true iff this mask is set
277 */
278 constexpr bool as_bool() const { return unpoisoned_value() != 0; }
279
280 /**
281 * Return the underlying value of the mask
282 */
283 constexpr T value() const { return m_mask; }
284
285 private:
286 constexpr Mask(T m) : m_mask(m) {}
287
288 T m_mask;
289};
290
291template <typename T>
292constexpr inline Mask<T> conditional_copy_mem(Mask<T> mask, T* to, const T* from0, const T* from1, size_t elems) {
293 mask.select_n(to, from0, from1, elems);
294 return mask;
295}
296
297template <typename T>
298constexpr inline Mask<T> conditional_copy_mem(T cnd, T* to, const T* from0, const T* from1, size_t elems) {
299 const auto mask = CT::Mask<T>::expand(cnd);
300 return CT::conditional_copy_mem(mask, to, from0, from1, elems);
301}
302
303template <typename T>
304constexpr inline Mask<T> conditional_assign_mem(T cnd, T* sink, const T* src, size_t elems) {
305 const auto mask = CT::Mask<T>::expand(cnd);
306 mask.select_n(sink, src, sink, elems);
307 return mask;
308}
309
310template <typename T>
311constexpr inline void conditional_swap(bool cnd, T& x, T& y) {
312 const auto swap = CT::Mask<T>::expand(cnd);
313
314 T t0 = swap.select(y, x);
315 T t1 = swap.select(x, y);
316 x = t0;
317 y = t1;
318}
319
320template <typename T>
321constexpr inline void conditional_swap_ptr(bool cnd, T& x, T& y) {
322 uintptr_t xp = reinterpret_cast<uintptr_t>(x);
323 uintptr_t yp = reinterpret_cast<uintptr_t>(y);
324
325 conditional_swap<uintptr_t>(cnd, xp, yp);
326
327 x = reinterpret_cast<T>(xp);
328 y = reinterpret_cast<T>(yp);
329}
330
331template <typename T>
332constexpr inline CT::Mask<T> all_zeros(const T elem[], size_t len) {
333 T sum = 0;
334 for(size_t i = 0; i != len; ++i) {
335 sum |= elem[i];
336 }
337 return CT::Mask<T>::is_zero(sum);
338}
339
340/**
341* Compare two arrays of equal size and return a Mask indicating if
342* they are equal or not. The mask is set if they are identical.
343*/
344template <typename T>
345constexpr inline CT::Mask<T> is_equal(const T x[], const T y[], size_t len) {
346 if(std::is_constant_evaluated()) {
347 T difference = 0;
348
349 for(size_t i = 0; i != len; ++i) {
350 difference = difference | (x[i] ^ y[i]);
351 }
352
353 return CT::Mask<T>::is_zero(difference);
354 } else {
355 volatile T difference = 0;
356
357 for(size_t i = 0; i != len; ++i) {
358 difference = difference | (x[i] ^ y[i]);
359 }
360
361 return CT::Mask<T>::is_zero(difference);
362 }
363}
364
365/**
366* Compare two arrays of equal size and return a Mask indicating if
367* they are equal or not. The mask is set if they differ.
368*/
369template <typename T>
370constexpr inline CT::Mask<T> is_not_equal(const T x[], const T y[], size_t len) {
371 return ~CT::is_equal(x, y, len);
372}
373
374/**
375* If bad_input is unset, return input[offset:input_length] copied to new
376* buffer. If bad_input is set, return an empty vector. In all cases, the capacity
377* of the vector is equal to input_length
378*
379* This function attempts to avoid leaking the following:
380* - if bad_input was set or not
381* - the value of offset
382* - the values in input[]
383*
384* This function leaks the value of input_length
385*/
388 const uint8_t input[],
389 size_t input_length,
390 size_t offset);
391
392secure_vector<uint8_t> strip_leading_zeros(const uint8_t in[], size_t length);
393
395 return strip_leading_zeros(in.data(), in.size());
396}
397
398} // namespace Botan::CT
399
400#endif
#define BOTAN_UNUSED
Definition assert.h:118
Mask< T > select_mask(Mask< T > x, Mask< T > y) const
Definition ct_utils.h:245
constexpr T value() const
Definition ct_utils.h:283
constexpr void if_set_zero_out(T buf[], size_t elems)
Definition ct_utils.h:260
static constexpr Mask< T > is_lte(T x, T y)
Definition ct_utils.h:149
static constexpr Mask< T > is_gte(T x, T y)
Definition ct_utils.h:154
constexpr T if_not_set_return(T x) const
Definition ct_utils.h:229
constexpr Mask(Mask< U > o)
Definition ct_utils.h:98
static constexpr Mask< T > set()
Definition ct_utils.h:105
friend Mask< T > operator|(Mask< T > x, Mask< T > y)
Definition ct_utils.h:214
Mask< T > & operator^=(Mask< T > o)
Definition ct_utils.h:188
constexpr T unpoisoned_value() const
Definition ct_utils.h:269
constexpr void select_n(T output[], const T x[], const T y[], size_t len) const
Definition ct_utils.h:251
friend Mask< T > operator^(Mask< T > x, Mask< T > y)
Definition ct_utils.h:209
constexpr T if_set_return(T x) const
Definition ct_utils.h:224
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:121
Mask< T > & operator&=(Mask< T > o)
Definition ct_utils.h:180
constexpr T select_and_unpoison(T x, T y) const
Definition ct_utils.h:236
static constexpr Mask< T > expand(T v)
Definition ct_utils.h:115
constexpr T select(T x, T y) const
Definition ct_utils.h:234
static constexpr Mask< T > is_within_range(T v, T l, T u)
Definition ct_utils.h:156
static constexpr Mask< T > is_equal(T x, T y)
Definition ct_utils.h:134
Mask< T > & operator|=(Mask< T > o)
Definition ct_utils.h:196
constexpr Mask< T > operator~() const
Definition ct_utils.h:219
static constexpr Mask< T > is_gt(T x, T y)
Definition ct_utils.h:144
constexpr bool as_bool() const
Definition ct_utils.h:278
static constexpr Mask< T > is_lt(T x, T y)
Definition ct_utils.h:139
friend Mask< T > operator&(Mask< T > x, Mask< T > y)
Definition ct_utils.h:204
static constexpr Mask< T > is_any_of(T v, std::initializer_list< T > accepted)
Definition ct_utils.h:165
static constexpr Mask< T > is_zero(T x)
Definition ct_utils.h:129
static constexpr Mask< T > cleared()
Definition ct_utils.h:110
int(* final)(unsigned char *, CTX *)
#define BOTAN_TEST_API
Definition compiler.h:51
FE_25519 T
Definition ge.cpp:34
constexpr void conditional_swap_ptr(bool cnd, T &x, T &y)
Definition ct_utils.h:321
void poison(const T *p, size_t n)
Definition ct_utils.h:46
constexpr void conditional_swap(bool cnd, T &x, T &y)
Definition ct_utils.h:311
constexpr Mask< T > conditional_assign_mem(T cnd, T *sink, const T *src, size_t elems)
Definition ct_utils.h:304
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:292
constexpr CT::Mask< T > is_not_equal(const T x[], const T y[], size_t len)
Definition ct_utils.h:370
constexpr CT::Mask< T > is_equal(const T x[], const T y[], size_t len)
Definition ct_utils.h:345
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:332
constexpr T choose(T mask, T a, T b)
Definition bit_ops.h:180
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:61
constexpr T expand_top_bit(T a)
Definition bit_ops.h:23