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