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