Botan 3.13.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/assert.h>
12#include <botan/range_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* Zero memory contents in a way that a compiler should not elide,
55* using some system specific technique.
56*
57* @param data the data region to be scrubbed
58*/
59void secure_scrub_memory(ranges::contiguous_output_range auto&& data) {
60 secure_scrub_memory(std::ranges::data(data), ranges::size_bytes(data));
61}
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/**
74 * Memory comparison, input insensitive
75 * @param x a range of bytes
76 * @param y another range of bytes
77 * @return true iff x and y have equal lengths and x[i] == y[i] forall i in [0...n)
78 */
79BOTAN_PUBLIC_API(3, 3) bool constant_time_compare(std::span<const uint8_t> x, std::span<const uint8_t> y);
80
81/**
82* Memory comparison, input insensitive
83* @param x a pointer to an array
84* @param y a pointer to another array
85* @param len the number of Ts in x and y
86* @return true iff x[i] == y[i] forall i in [0...n)
87*/
88inline bool constant_time_compare(const uint8_t x[], const uint8_t y[], size_t len) {
89 // simply assumes that *x and *y point to len allocated bytes at least
90 return constant_time_compare({x, len}, {y, len});
91}
92
93/**
94* Zero out some bytes. Warning: use secure_scrub_memory instead if the
95* memory is about to be freed or otherwise the compiler thinks it can
96* elide the writes.
97*
98* @param ptr a pointer to memory to zero
99* @param bytes the number of bytes to zero in ptr
100*/
101inline constexpr void clear_bytes(void* ptr, size_t bytes) {
102 if(bytes > 0) {
103 std::memset(ptr, 0, bytes);
104 }
105}
106
107/**
108* Zero memory before use. This simply calls memset and should not be
109* used in cases where the compiler cannot see the call as a
110* side-effecting operation (for example, if calling clear_mem before
111* deallocating memory, the compiler would be allowed to omit the call
112* to memset entirely under the as-if rule.)
113*
114* @param ptr a pointer to an array of Ts to zero
115* @param n the number of Ts pointed to by ptr
116*/
117template <typename T>
118inline constexpr void clear_mem(T* ptr, size_t n) {
119 clear_bytes(ptr, sizeof(T) * n);
120}
121
122/**
123* Zero memory before use. This simply calls memset and should not be
124* used in cases where the compiler cannot see the call as a
125* side-effecting operation.
126*
127* @param mem a contiguous range of Ts to zero
128*/
129template <ranges::contiguous_output_range R>
130inline constexpr void clear_mem(R&& mem) // NOLINT(*-missing-std-forward)
131 requires std::is_trivially_copyable_v<std::ranges::range_value_t<R>>
132{
133 clear_bytes(std::ranges::data(mem), ranges::size_bytes(mem));
134}
135
136/**
137* Copy memory
138* @param out the destination array
139* @param in the source array
140* @param n the number of elements of in/out
141*/
142template <typename T>
143 requires std::is_trivial_v<std::decay_t<T>>
144inline constexpr void copy_mem(T* out, const T* in, size_t n) {
145 BOTAN_ASSERT_IMPLICATION(n > 0, in != nullptr && out != nullptr, "If n > 0 then args are not null");
146
147 if(in != nullptr && out != nullptr && n > 0) {
148 std::memmove(out, in, sizeof(T) * n);
149 }
150}
151
152/**
153* Copy memory
154* @param out the destination array
155* @param in the source array
156*/
157template <ranges::contiguous_output_range OutR, ranges::contiguous_range InR>
158 requires std::is_same_v<std::ranges::range_value_t<OutR>, std::ranges::range_value_t<InR>> &&
159 std::is_trivially_copyable_v<std::ranges::range_value_t<InR>>
160inline constexpr void copy_mem(OutR&& out /* NOLINT(*-std-forward) */, const InR& in) {
162 if(std::is_constant_evaluated()) {
163 std::copy(std::ranges::begin(in), std::ranges::end(in), std::ranges::begin(out));
164 } else 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 /* NOLINT(*-std-forward) */, const 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, const FromR& in) {
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 /* NOLINT(*-std-forward) */, 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_default_constructible_v<ToT> && std::is_trivially_copyable_v<ToT> &&
209 std::is_trivially_copyable_v<std::ranges::range_value_t<FromR>>
210inline constexpr ToT typecast_copy(const FromR& src) {
211 ToT dst; // NOLINT(*-member-init)
212 typecast_copy(dst, src);
213 return dst;
214}
215
216/**
217* Copy the bytes of an array of trivially copyable objects into a byte array
218* @param out the output byte array, must have room for sizeof(T)*N bytes
219* @param in the input array
220* @param N the number of elements in the input array
221*/
222// TODO: deprecate and replace
223template <typename T>
224inline constexpr void typecast_copy(uint8_t out[], T in[], size_t N)
225 requires std::is_trivially_copyable_v<T>
226{
227 // asserts that *in and *out point to the correct amount of memory
228 typecast_copy(std::span<uint8_t>(out, sizeof(T) * N), std::span<const T>(in, N));
229}
230
231/**
232* Reinterpret a byte array as an array of trivial objects
233* @param out the output array, must have room for N elements
234* @param in the input byte array, must hold sizeof(T)*N bytes
235* @param N the number of elements to produce
236*/
237// TODO: deprecate and replace
238template <typename T>
239inline constexpr void typecast_copy(T out[], const uint8_t in[], size_t N)
240 requires std::is_trivial_v<T>
241{
242 // asserts that *in and *out point to the correct amount of memory
243 typecast_copy(std::span<T>(out, N), std::span<const uint8_t>(in, N * sizeof(T)));
244}
245
246/**
247* Copy the bytes of a single object into a byte array
248* @param out the output byte array, must have room for sizeof(T) bytes
249* @param in the object to copy from
250*/
251// TODO: deprecate and replace
252template <typename T>
253inline constexpr void typecast_copy(uint8_t out[], const T& in) {
254 // asserts that *out points to the correct amount of memory
255 typecast_copy(std::span<uint8_t, sizeof(T)>(out, sizeof(T)), in);
256}
257
258/**
259* Reinterpret a byte array as a single trivial object
260* @param out the object to copy into
261* @param in the input byte array, must hold sizeof(T) bytes
262*/
263// TODO: deprecate and replace
264template <typename T>
265 requires std::is_trivial_v<std::decay_t<T>>
266inline constexpr void typecast_copy(T& out, const uint8_t in[]) {
267 // asserts that *in points to the correct amount of memory
268 typecast_copy(out, std::span<const uint8_t, sizeof(T)>(in, sizeof(T)));
269}
270
271/**
272* Reinterpret a byte array as a single trivial object
273* @param src the input byte array, must hold sizeof(To) bytes
274* @return the object read from src
275*/
276// TODO: deprecate and replace
277template <typename To>
278 requires std::is_trivial_v<To>
279inline constexpr To typecast_copy(const uint8_t src[]) noexcept {
280 // asserts that *src points to the correct amount of memory
281 return typecast_copy<To>(std::span<const uint8_t, sizeof(To)>(src, sizeof(To)));
282}
283
284#if !defined(BOTAN_IS_BEING_BUILT)
285/**
286* Set memory to a fixed value
287* @param ptr a pointer to an array of bytes
288* @param n the number of Ts pointed to by ptr
289* @param val the value to set each byte to
290*/
291BOTAN_DEPRECATED("This function is deprecated") inline constexpr void set_mem(uint8_t* ptr, size_t n, uint8_t val) {
292 if(n > 0) {
293 std::memset(ptr, val, n);
294 }
295}
296#endif
297
298#if !defined(BOTAN_IS_BEING_BUILT)
299/**
300* Cast a char pointer to a uint8_t pointer
301* @param s the pointer to cast
302* @return s viewed as a byte pointer
303*/
304inline const uint8_t* cast_char_ptr_to_uint8(const char* s) {
305 return reinterpret_cast<const uint8_t*>(s);
306}
307
308/**
309* Cast a char pointer to a uint8_t pointer
310* @param s the pointer to cast
311* @return s viewed as a byte pointer
312*/
313inline uint8_t* cast_char_ptr_to_uint8(char* s) {
314 return reinterpret_cast<uint8_t*>(s);
315}
316#endif
317
318/**
319* Cast a uint8_t pointer to a char pointer
320* @param b the pointer to cast
321* @return b viewed as a char pointer
322*/
323inline const char* cast_uint8_ptr_to_char(const uint8_t* b) {
324 return reinterpret_cast<const char*>(b);
325}
326
327/**
328* Cast a uint8_t pointer to a char pointer
329* @param b the pointer to cast
330* @return b viewed as a char pointer
331*/
332inline char* cast_uint8_ptr_to_char(uint8_t* b) {
333 return reinterpret_cast<char*>(b);
334}
335
336#if !defined(BOTAN_IS_BEING_BUILT)
337/**
338* Memory comparison, input insensitive
339* @param p1 a pointer to an array
340* @param p2 a pointer to another array
341* @param n the number of Ts in p1 and p2
342* @return true iff p1[i] == p2[i] forall i in [0...n)
343*/
344template <typename T>
345BOTAN_DEPRECATED("This function is deprecated")
346inline bool same_mem(const T* p1, const T* p2, size_t n) {
347 volatile T difference = 0;
348
349 for(size_t i = 0; i != n; ++i) {
350 difference = difference | (p1[i] ^ p2[i]);
351 }
352
353 return difference == 0;
354}
355#endif
356
357#if !defined(BOTAN_IS_BEING_BUILT)
358
359/**
360* Copy into a buffer at an offset, truncating to the space available
361* @param buf the buffer to write into
362* @param buf_offset the offset in buf to write at
363* @param input the elements to copy
364* @param input_length the number of elements in input
365* @return the number of elements actually copied
366*/
367template <typename T, typename Alloc>
368BOTAN_DEPRECATED("The buffer_insert functions are deprecated")
369size_t buffer_insert(std::vector<T, Alloc>& buf, size_t buf_offset, const T input[], size_t input_length) {
370 BOTAN_ASSERT_NOMSG(buf_offset <= buf.size());
371 const size_t to_copy = std::min(input_length, buf.size() - buf_offset);
372 if(to_copy > 0) {
373 copy_mem(&buf[buf_offset], input, to_copy);
374 }
375 return to_copy;
376}
377
378/**
379* Copy into a buffer at an offset, truncating to the space available
380* @param buf the buffer to write into
381* @param buf_offset the offset in buf to write at
382* @param input the elements to copy
383* @return the number of elements actually copied
384*/
385template <typename T, typename Alloc, typename Alloc2>
386BOTAN_DEPRECATED("The buffer_insert functions are deprecated")
387size_t buffer_insert(std::vector<T, Alloc>& buf, size_t buf_offset, const std::vector<T, Alloc2>& input) {
388 BOTAN_ASSERT_NOMSG(buf_offset <= buf.size());
389 const size_t to_copy = std::min(input.size(), buf.size() - buf_offset);
390 if(to_copy > 0) {
391 copy_mem(&buf[buf_offset], input.data(), to_copy);
392 }
393 return to_copy;
394}
395
396#endif
397
398/**
399* XOR arrays. Postcondition out[i] = in[i] ^ out[i] forall i = 0...length
400* @param out the input/output range
401* @param in the read-only input range
402*/
403inline constexpr void xor_buf(ranges::contiguous_output_range<uint8_t> auto&& out,
406
407 std::span<uint8_t> o(out);
408 std::span<const uint8_t> i(in);
409
410 for(; o.size_bytes() >= 32; o = o.subspan(32), i = i.subspan(32)) {
411 auto x = typecast_copy<std::array<uint64_t, 4>>(o.template first<32>());
412 const auto y = typecast_copy<std::array<uint64_t, 4>>(i.template first<32>());
413
414 x[0] ^= y[0];
415 x[1] ^= y[1];
416 x[2] ^= y[2];
417 x[3] ^= y[3];
418
419 typecast_copy(o.template first<32>(), x);
420 }
421
422 for(size_t off = 0; off != o.size_bytes(); ++off) {
423 o[off] ^= i[off];
424 }
425}
426
427/**
428* XOR arrays. Postcondition out[i] = in1[i] ^ in2[i] forall i = 0...length
429* @param out the output range
430* @param in1 the first input range
431* @param in2 the second input range
432*/
433inline constexpr void xor_buf(ranges::contiguous_output_range<uint8_t> auto&& out,
437
438 std::span o{out};
439 std::span i1{in1};
440 std::span i2{in2};
441
442 for(; o.size_bytes() >= 32; o = o.subspan(32), i1 = i1.subspan(32), i2 = i2.subspan(32)) {
443 auto x = typecast_copy<std::array<uint64_t, 4>>(i1.template first<32>());
444 const auto y = typecast_copy<std::array<uint64_t, 4>>(i2.template first<32>());
445
446 x[0] ^= y[0];
447 x[1] ^= y[1];
448 x[2] ^= y[2];
449 x[3] ^= y[3];
450
451 typecast_copy(o.template first<32>(), x);
452 }
453
454 for(size_t off = 0; off != o.size_bytes(); ++off) {
455 o[off] = i1[off] ^ i2[off];
456 }
457}
458
459/**
460* XOR arrays. Postcondition out[i] = in[i] ^ out[i] forall i = 0...length
461* @param out the input/output buffer
462* @param in the read-only input buffer
463* @param length the length of the buffers
464*/
465inline void xor_buf(uint8_t out[], const uint8_t in[], size_t length) {
466 // simply assumes that *out and *in point to "length" allocated bytes at least
467 xor_buf(std::span{out, length}, std::span{in, length});
468}
469
470/**
471* XOR arrays. Postcondition out[i] = in[i] ^ in2[i] forall i = 0...length
472* @param out the output buffer
473* @param in the first input buffer
474* @param in2 the second input buffer
475* @param length the length of the three buffers
476*/
477inline void xor_buf(uint8_t out[], const uint8_t in[], const uint8_t in2[], size_t length) {
478 // simply assumes that *out, *in, and *in2 point to "length" allocated bytes at least
479 xor_buf(std::span{out, length}, std::span{in, length}, std::span{in2, length});
480}
481
482/**
483* XOR the first n bytes of in into out
484* @param out the buffer to XOR into, must hold at least n bytes
485* @param in the buffer to read from, must hold at least n bytes
486* @param n the number of bytes to XOR
487*/
488// TODO: deprecate and replace, use .subspan()
489inline void xor_buf(std::span<uint8_t> out, std::span<const uint8_t> in, size_t n) {
490 BOTAN_ARG_CHECK(out.size() >= n, "output span is too small");
491 BOTAN_ARG_CHECK(in.size() >= n, "input span is too small");
492 xor_buf(out.first(n), in.first(n));
493}
494
495/**
496* XOR n bytes into the front of a vector
497* @param out the vector to XOR into, must hold at least n bytes
498* @param in the bytes to read from, must point to at least n bytes
499* @param n the number of bytes to XOR
500*/
501// TODO: deprecate and replace, use .subspan()
502template <typename Alloc>
503void xor_buf(std::vector<uint8_t, Alloc>& out, const uint8_t* in, size_t n) {
504 BOTAN_ARG_CHECK(out.size() >= n, "output vector is too small");
505 // simply assumes that *in points to "n" allocated bytes at least
506 xor_buf(std::span{out}.first(n), std::span{in, n});
507}
508
509/**
510* Set the front of a vector to the XOR of two inputs
511* @param out the vector to write into, must hold at least n bytes
512* @param in the first input, must point to at least n bytes
513* @param in2 the second input, must hold at least n bytes
514* @param n the number of bytes to process
515*/
516// TODO: deprecate and replace
517template <typename Alloc, typename Alloc2>
518void xor_buf(std::vector<uint8_t, Alloc>& out, const uint8_t* in, const std::vector<uint8_t, Alloc2>& in2, size_t n) {
519 BOTAN_ARG_CHECK(out.size() >= n, "output vector is too small");
520 BOTAN_ARG_CHECK(in2.size() >= n, "input vector is too small");
521 // simply assumes that *in points to "n" allocated bytes at least
522 xor_buf(std::span{out}.first(n), std::span{in, n}, std::span{in2}.first(n));
523}
524
525/**
526* XOR a vector into another, growing the destination if it is shorter
527* @param out the vector to XOR into
528* @param in the vector to read from
529* @return reference to out
530*/
531template <typename Alloc, typename Alloc2>
532std::vector<uint8_t, Alloc>& operator^=(std::vector<uint8_t, Alloc>& out, const std::vector<uint8_t, Alloc2>& in) {
533 if(out.size() < in.size()) {
534 out.resize(in.size());
535 }
536
537 xor_buf(std::span{out}.first(in.size()), in);
538 return out;
539}
540
541} // namespace Botan
542
543#endif
#define BOTAN_PUBLIC_API(maj, min)
Definition api.h:21
#define BOTAN_FUTURE_INTERNAL_HEADER(hdr)
Definition api.h:104
#define BOTAN_DEPRECATED(msg)
Definition api.h:73
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_ASSERT_IMPLICATION(expr1, expr2, msg)
Definition assert.h:101
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
constexpr size_t size_bytes(const spanable_range auto &r)
constexpr void assert_equal_byte_lengths(const R0 &r0, const Rs &... rs)
size_t buffer_insert(std::vector< T, Alloc > &buf, size_t buf_offset, const T input[], size_t input_length)
Definition mem_ops.h:369
constexpr void set_mem(uint8_t *ptr, size_t n, uint8_t val)
Definition mem_ops.h:291
constexpr void typecast_copy(ToR &&out, const FromR &in)
Definition mem_ops.h:176
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:144
constexpr void clear_bytes(void *ptr, size_t bytes)
Definition mem_ops.h:101
void secure_scrub_memory(void *ptr, size_t n)
Definition mem_utils.cpp:25
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:532
constexpr void xor_buf(ranges::contiguous_output_range< uint8_t > auto &&out, ranges::contiguous_range< uint8_t > auto &&in)
Definition mem_ops.h:403
bool same_mem(const T *p1, const T *p2, size_t n)
Definition mem_ops.h:346
const char * cast_uint8_ptr_to_char(const uint8_t *b)
Definition mem_ops.h:323
bool constant_time_compare(std::span< const uint8_t > x, std::span< const uint8_t > y)
Definition mem_ops.cpp:17
constexpr void clear_mem(T *ptr, size_t n)
Definition mem_ops.h:118
const uint8_t * cast_char_ptr_to_uint8(const char *s)
Definition mem_ops.h:304