Botan 3.7.1
Crypto and TLS for C&
mem_ops.h
Go to the documentation of this file.
1/*
2* Memory Operations
3* (C) 1999-2009,2012,2015 Jack Lloyd
4*
5* Botan is released under the Simplified BSD License (see license.txt)
6*/
7
8#ifndef BOTAN_MEMORY_OPS_H_
9#define BOTAN_MEMORY_OPS_H_
10
11#include <botan/assert.h>
12#include <botan/concepts.h>
13#include <botan/types.h>
14#include <array>
15#include <cstring>
16#include <ranges>
17#include <span>
18#include <type_traits>
19#include <vector>
20
21/*
22The header mem_ops.h previously included the contents of allocator.h
23
24Library code should always include allocator.h to see these
25declarations; however when we are not building the library continue to
26include the header here to avoid breaking application code.
27*/
28#if !defined(BOTAN_IS_BEING_BUILT)
29 #include <botan/allocator.h>
30#endif
31
32namespace Botan {
33
34/**
35* Scrub memory contents in a way that a compiler should not elide,
36* using some system specific technique. Note that this function might
37* not zero the memory (for example, in some hypothetical
38* implementation it might combine the memory contents with the output
39* of a system PRNG), but if you can detect any difference in behavior
40* at runtime then the clearing is side-effecting and you can just
41* use `clear_mem`.
42*
43* Use this function to scrub memory just before deallocating it, or on
44* a stack buffer before returning from the function.
45*
46* @param ptr a pointer to memory to scrub
47* @param n the number of bytes pointed to by ptr
48*/
49BOTAN_PUBLIC_API(2, 0) void secure_scrub_memory(void* ptr, size_t n);
50
51/**
52* Scrub memory contents in a way that a compiler should not elide,
53* using some system specific technique. Note that this function might
54* not zero the memory.
55*
56* @param data the data region to be scrubbed
57*/
58void secure_scrub_memory(ranges::contiguous_output_range auto&& data) {
59 secure_scrub_memory(std::ranges::data(data), ranges::size_bytes(data));
60}
61
62#if !defined(BOTAN_IS_BEGIN_BUILT)
63
64/**
65* Memory comparison, input insensitive
66* @param x a pointer to an array
67* @param y a pointer to another array
68* @param len the number of Ts in x and y
69* @return 0xFF iff x[i] == y[i] forall i in [0...n) or 0x00 otherwise
70*/
71BOTAN_DEPRECATED("This function is deprecated, use constant_time_compare()")
72BOTAN_PUBLIC_API(2, 9) uint8_t ct_compare_u8(const uint8_t x[], const uint8_t y[], size_t len);
73
74#endif
75
76/**
77 * Memory comparison, input insensitive
78 * @param x a range of bytes
79 * @param y another range of bytes
80 * @return true iff x and y have equal lengths and x[i] == y[i] forall i in [0...n)
81 */
82BOTAN_PUBLIC_API(3, 3) bool constant_time_compare(std::span<const uint8_t> x, std::span<const uint8_t> y);
83
84/**
85* Memory comparison, input insensitive
86* @param x a pointer to an array
87* @param y a pointer to another array
88* @param len the number of Ts in x and y
89* @return true iff x[i] == y[i] forall i in [0...n)
90*/
91inline bool constant_time_compare(const uint8_t x[], const uint8_t y[], size_t len) {
92 // simply assumes that *x and *y point to len allocated bytes at least
93 return constant_time_compare({x, len}, {y, len});
94}
95
96/**
97* Zero out some bytes. Warning: use secure_scrub_memory instead if the
98* memory is about to be freed or otherwise the compiler thinks it can
99* elide the writes.
100*
101* @param ptr a pointer to memory to zero
102* @param bytes the number of bytes to zero in ptr
103*/
104inline constexpr void clear_bytes(void* ptr, size_t bytes) {
105 if(bytes > 0) {
106 std::memset(ptr, 0, bytes);
107 }
108}
109
110/**
111* Zero memory before use. This simply calls memset and should not be
112* used in cases where the compiler cannot see the call as a
113* side-effecting operation (for example, if calling clear_mem before
114* deallocating memory, the compiler would be allowed to omit the call
115* to memset entirely under the as-if rule.)
116*
117* @param ptr a pointer to an array of Ts to zero
118* @param n the number of Ts pointed to by ptr
119*/
120template <typename T>
121inline constexpr void clear_mem(T* ptr, size_t n) {
122 clear_bytes(ptr, sizeof(T) * n);
123}
124
125/**
126* Zero memory before use. This simply calls memset and should not be
127* used in cases where the compiler cannot see the call as a
128* side-effecting operation.
129*
130* @param mem a contiguous range of Ts to zero
131*/
132template <ranges::contiguous_output_range R>
133inline constexpr void clear_mem(R&& mem)
134 requires std::is_trivially_copyable_v<std::ranges::range_value_t<R>>
135{
136 clear_bytes(std::ranges::data(mem), ranges::size_bytes(mem));
137}
138
139/**
140* Copy memory
141* @param out the destination array
142* @param in the source array
143* @param n the number of elements of in/out
144*/
145template <typename T>
146 requires std::is_trivial<typename std::decay<T>::type>::value
147inline constexpr void copy_mem(T* out, const T* in, size_t n) {
148 BOTAN_ASSERT_IMPLICATION(n > 0, in != nullptr && out != nullptr, "If n > 0 then args are not null");
149
150 if(in != nullptr && out != nullptr && n > 0) {
151 std::memmove(out, in, sizeof(T) * n);
152 }
153}
154
155/**
156* Copy memory
157* @param out the destination array
158* @param in the source array
159*/
160template <ranges::contiguous_output_range OutR, ranges::contiguous_range InR>
161 requires std::is_same_v<std::ranges::range_value_t<OutR>, std::ranges::range_value_t<InR>> &&
162 std::is_trivially_copyable_v<std::ranges::range_value_t<InR>>
163inline constexpr void copy_mem(OutR&& out, InR&& in) {
165 if(std::is_constant_evaluated()) {
166 std::copy(std::ranges::begin(in), std::ranges::end(in), std::ranges::begin(out));
167 } else if(ranges::size_bytes(out) > 0) {
168 std::memmove(std::ranges::data(out), std::ranges::data(in), ranges::size_bytes(out));
169 }
170}
171
172/**
173 * Copy a range of a trivially copyable type into another range of trivially
174 * copyable type of matching byte length.
175 */
176template <ranges::contiguous_output_range ToR, ranges::contiguous_range FromR>
177 requires std::is_trivially_copyable_v<std::ranges::range_value_t<FromR>> &&
178 std::is_trivially_copyable_v<std::ranges::range_value_t<ToR>>
179inline constexpr void typecast_copy(ToR&& out, FromR&& in) {
181 std::memcpy(std::ranges::data(out), std::ranges::data(in), ranges::size_bytes(out));
182}
183
184/**
185 * Copy a range of trivially copyable type into an instance of trivially
186 * copyable type with matching length.
187 */
188template <typename ToT, ranges::contiguous_range FromR>
189 requires std::is_trivially_copyable_v<std::ranges::range_value_t<FromR>> && std::is_trivially_copyable_v<ToT> &&
190 (!std::ranges::range<ToT>)
191inline constexpr void typecast_copy(ToT& out, FromR&& in) noexcept {
192 typecast_copy(std::span<ToT, 1>(&out, 1), in);
193}
194
195/**
196 * Copy an instance of trivially copyable type into a range of trivially
197 * copyable type with matching length.
198 */
199template <ranges::contiguous_output_range ToR, typename FromT>
200 requires std::is_trivially_copyable_v<FromT> &&
201 (!std::ranges::range<FromT>) && std::is_trivially_copyable_v<std::ranges::range_value_t<ToR>>
202inline constexpr void typecast_copy(ToR&& out, const FromT& in) {
203 typecast_copy(out, std::span<const FromT, 1>(&in, 1));
204}
205
206/**
207 * Create a trivial type by bit-casting a range of trivially copyable type with
208 * matching length into it.
209 */
210template <typename ToT, ranges::contiguous_range FromR>
211 requires std::is_default_constructible_v<ToT> && std::is_trivially_copyable_v<ToT> &&
212 std::is_trivially_copyable_v<std::ranges::range_value_t<FromR>>
213inline constexpr ToT typecast_copy(FromR&& src) noexcept {
214 ToT dst;
215 typecast_copy(dst, src);
216 return dst;
217}
218
219// TODO: deprecate and replace
220template <typename T>
221inline constexpr void typecast_copy(uint8_t out[], T in[], size_t N)
222 requires std::is_trivially_copyable<T>::value
223{
224 // asserts that *in and *out point to the correct amount of memory
225 typecast_copy(std::span<uint8_t>(out, sizeof(T) * N), std::span<const T>(in, N));
226}
227
228// TODO: deprecate and replace
229template <typename T>
230inline constexpr void typecast_copy(T out[], const uint8_t in[], size_t N)
231 requires std::is_trivial<T>::value
232{
233 // asserts that *in and *out point to the correct amount of memory
234 typecast_copy(std::span<T>(out, N), std::span<const uint8_t>(in, N * sizeof(T)));
235}
236
237// TODO: deprecate and replace
238template <typename T>
239inline constexpr void typecast_copy(uint8_t out[], const T& in) {
240 // asserts that *out points to the correct amount of memory
241 typecast_copy(std::span<uint8_t, sizeof(T)>(out, sizeof(T)), in);
242}
243
244// TODO: deprecate and replace
245template <typename T>
246 requires std::is_trivial<typename std::decay<T>::type>::value
247inline constexpr void typecast_copy(T& out, const uint8_t in[]) {
248 // asserts that *in points to the correct amount of memory
249 typecast_copy(out, std::span<const uint8_t, sizeof(T)>(in, sizeof(T)));
250}
251
252// TODO: deprecate and replace
253template <typename To>
254 requires std::is_trivial<To>::value
255inline constexpr To typecast_copy(const uint8_t src[]) noexcept {
256 // asserts that *src points to the correct amount of memory
257 return typecast_copy<To>(std::span<const uint8_t, sizeof(To)>(src, sizeof(To)));
258}
259
260#if !defined(BOTAN_IS_BEGIN_BUILT)
261/**
262* Set memory to a fixed value
263* @param ptr a pointer to an array of bytes
264* @param n the number of Ts pointed to by ptr
265* @param val the value to set each byte to
266*/
267BOTAN_DEPRECATED("This function is deprecated") inline constexpr void set_mem(uint8_t* ptr, size_t n, uint8_t val) {
268 if(n > 0) {
269 std::memset(ptr, val, n);
270 }
271}
272#endif
273
274inline const uint8_t* cast_char_ptr_to_uint8(const char* s) {
275 return reinterpret_cast<const uint8_t*>(s);
276}
277
278inline const char* cast_uint8_ptr_to_char(const uint8_t* b) {
279 return reinterpret_cast<const char*>(b);
280}
281
282inline uint8_t* cast_char_ptr_to_uint8(char* s) {
283 return reinterpret_cast<uint8_t*>(s);
284}
285
286inline char* cast_uint8_ptr_to_char(uint8_t* b) {
287 return reinterpret_cast<char*>(b);
288}
289
290#if !defined(BOTAN_IS_BEING_BUILT)
291/**
292* Memory comparison, input insensitive
293* @param p1 a pointer to an array
294* @param p2 a pointer to another array
295* @param n the number of Ts in p1 and p2
296* @return true iff p1[i] == p2[i] forall i in [0...n)
297*/
298template <typename T>
299BOTAN_DEPRECATED("This function is deprecated")
300inline bool same_mem(const T* p1, const T* p2, size_t n) {
301 volatile T difference = 0;
302
303 for(size_t i = 0; i != n; ++i) {
304 difference = difference | (p1[i] ^ p2[i]);
305 }
306
307 return difference == 0;
308}
309#endif
310
311#if !defined(BOTAN_IS_BEING_BUILT)
312
313template <typename T, typename Alloc>
314BOTAN_DEPRECATED("The buffer_insert functions are deprecated")
315size_t buffer_insert(std::vector<T, Alloc>& buf, size_t buf_offset, const T input[], size_t input_length) {
316 BOTAN_ASSERT_NOMSG(buf_offset <= buf.size());
317 const size_t to_copy = std::min(input_length, buf.size() - buf_offset);
318 if(to_copy > 0) {
319 copy_mem(&buf[buf_offset], input, to_copy);
320 }
321 return to_copy;
322}
323
324template <typename T, typename Alloc, typename Alloc2>
325BOTAN_DEPRECATED("The buffer_insert functions are deprecated")
326size_t buffer_insert(std::vector<T, Alloc>& buf, size_t buf_offset, const std::vector<T, Alloc2>& input) {
327 BOTAN_ASSERT_NOMSG(buf_offset <= buf.size());
328 const size_t to_copy = std::min(input.size(), buf.size() - buf_offset);
329 if(to_copy > 0) {
330 copy_mem(&buf[buf_offset], input.data(), to_copy);
331 }
332 return to_copy;
333}
334
335#endif
336
337/**
338* XOR arrays. Postcondition out[i] = in[i] ^ out[i] forall i = 0...length
339* @param out the input/output range
340* @param in the read-only input range
341*/
342inline constexpr void xor_buf(ranges::contiguous_output_range<uint8_t> auto&& out,
345
346 std::span<uint8_t> o(out);
347 std::span<const uint8_t> i(in);
348
349 for(; o.size_bytes() >= 32; o = o.subspan(32), i = i.subspan(32)) {
350 auto x = typecast_copy<std::array<uint64_t, 4>>(o.template first<32>());
351 const auto y = typecast_copy<std::array<uint64_t, 4>>(i.template first<32>());
352
353 x[0] ^= y[0];
354 x[1] ^= y[1];
355 x[2] ^= y[2];
356 x[3] ^= y[3];
357
358 typecast_copy(o.template first<32>(), x);
359 }
360
361 for(size_t off = 0; off != o.size_bytes(); ++off) {
362 o[off] ^= i[off];
363 }
364}
365
366/**
367* XOR arrays. Postcondition out[i] = in1[i] ^ in2[i] forall i = 0...length
368* @param out the output range
369* @param in1 the first input range
370* @param in2 the second input range
371*/
372inline constexpr void xor_buf(ranges::contiguous_output_range<uint8_t> auto&& out,
376
377 std::span o{out};
378 std::span i1{in1};
379 std::span i2{in2};
380
381 for(; o.size_bytes() >= 32; o = o.subspan(32), i1 = i1.subspan(32), i2 = i2.subspan(32)) {
382 auto x = typecast_copy<std::array<uint64_t, 4>>(i1.template first<32>());
383 const auto y = typecast_copy<std::array<uint64_t, 4>>(i2.template first<32>());
384
385 x[0] ^= y[0];
386 x[1] ^= y[1];
387 x[2] ^= y[2];
388 x[3] ^= y[3];
389
390 typecast_copy(o.template first<32>(), x);
391 }
392
393 for(size_t off = 0; off != o.size_bytes(); ++off) {
394 o[off] = i1[off] ^ i2[off];
395 }
396}
397
398/**
399* XOR arrays. Postcondition out[i] = in[i] ^ out[i] forall i = 0...length
400* @param out the input/output buffer
401* @param in the read-only input buffer
402* @param length the length of the buffers
403*/
404inline void xor_buf(uint8_t out[], const uint8_t in[], size_t length) {
405 // simply assumes that *out and *in point to "length" allocated bytes at least
406 xor_buf(std::span{out, length}, std::span{in, length});
407}
408
409/**
410* XOR arrays. Postcondition out[i] = in[i] ^ in2[i] forall i = 0...length
411* @param out the output buffer
412* @param in the first input buffer
413* @param in2 the second input buffer
414* @param length the length of the three buffers
415*/
416inline void xor_buf(uint8_t out[], const uint8_t in[], const uint8_t in2[], size_t length) {
417 // simply assumes that *out, *in, and *in2 point to "length" allocated bytes at least
418 xor_buf(std::span{out, length}, std::span{in, length}, std::span{in2, length});
419}
420
421// TODO: deprecate and replace, use .subspan()
422inline void xor_buf(std::span<uint8_t> out, std::span<const uint8_t> in, size_t n) {
423 BOTAN_ARG_CHECK(out.size() >= n, "output span is too small");
424 BOTAN_ARG_CHECK(in.size() >= n, "input span is too small");
425 xor_buf(out.first(n), in.first(n));
426}
427
428// TODO: deprecate and replace, use .subspan()
429template <typename Alloc>
430void xor_buf(std::vector<uint8_t, Alloc>& out, const uint8_t* in, size_t n) {
431 BOTAN_ARG_CHECK(out.size() >= n, "output vector is too small");
432 // simply assumes that *in points to "n" allocated bytes at least
433 xor_buf(std::span{out}.first(n), std::span{in, n});
434}
435
436// TODO: deprecate and replace
437template <typename Alloc, typename Alloc2>
438void xor_buf(std::vector<uint8_t, Alloc>& out, const uint8_t* in, const std::vector<uint8_t, Alloc2>& in2, size_t n) {
439 BOTAN_ARG_CHECK(out.size() >= n, "output vector is too small");
440 BOTAN_ARG_CHECK(in2.size() >= n, "input vector is too small");
441 // simply assumes that *in points to "n" allocated bytes at least
442 xor_buf(std::span{out}.first(n), std::span{in, n}, std::span{in2}.first(n));
443}
444
445template <typename Alloc, typename Alloc2>
446std::vector<uint8_t, Alloc>& operator^=(std::vector<uint8_t, Alloc>& out, const std::vector<uint8_t, Alloc2>& in) {
447 if(out.size() < in.size()) {
448 out.resize(in.size());
449 }
450
451 xor_buf(std::span{out}.first(in.size()), in);
452 return out;
453}
454
455} // namespace Botan
456
457#endif
#define BOTAN_PUBLIC_API(maj, min)
Definition api.h:19
#define BOTAN_DEPRECATED(msg)
Definition api.h:59
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59
#define BOTAN_ASSERT_IMPLICATION(expr1, expr2, msg)
Definition assert.h:77
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:29
FE_25519 T
Definition ge.cpp:34
constexpr size_t size_bytes(spanable_range auto &&r)
Definition concepts.h:96
constexpr void assert_equal_byte_lengths(R0 &&r0, Rs &&... rs)
Definition concepts.h:128
size_t buffer_insert(std::vector< T, Alloc > &buf, size_t buf_offset, const T input[], size_t input_length)
Definition mem_ops.h:315
constexpr void typecast_copy(ToR &&out, FromR &&in)
Definition mem_ops.h:179
constexpr void set_mem(uint8_t *ptr, size_t n, uint8_t val)
Definition mem_ops.h:267
constexpr void clear_bytes(void *ptr, size_t bytes)
Definition mem_ops.h:104
void secure_scrub_memory(void *ptr, size_t n)
Definition mem_utils.cpp:19
uint8_t ct_compare_u8(const uint8_t x[], const uint8_t y[], size_t len)
Definition mem_ops.cpp:13
std::vector< uint8_t, Alloc > & operator^=(std::vector< uint8_t, Alloc > &out, const std::vector< uint8_t, Alloc2 > &in)
Definition mem_ops.h:446
constexpr void xor_buf(ranges::contiguous_output_range< uint8_t > auto &&out, ranges::contiguous_range< uint8_t > auto &&in)
Definition mem_ops.h:342
bool same_mem(const T *p1, const T *p2, size_t n)
Definition mem_ops.h:300
const char * cast_uint8_ptr_to_char(const uint8_t *b)
Definition mem_ops.h:278
const SIMD_8x32 & b
bool constant_time_compare(std::span< const uint8_t > x, std::span< const uint8_t > y)
Definition mem_ops.cpp:17
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:147
constexpr void clear_mem(T *ptr, size_t n)
Definition mem_ops.h:121
const uint8_t * cast_char_ptr_to_uint8(const char *s)
Definition mem_ops.h:274