/src/botan/build/include/botan/mem_ops.h
Line | Count | Source (jump to first uncovered line) |
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/types.h> |
12 | | #include <cstring> |
13 | | #include <span> |
14 | | #include <type_traits> |
15 | | #include <vector> |
16 | | |
17 | | namespace Botan { |
18 | | |
19 | | /** |
20 | | * Allocate a memory buffer by some method. This should only be used for |
21 | | * primitive types (uint8_t, uint32_t, etc). |
22 | | * |
23 | | * @param elems the number of elements |
24 | | * @param elem_size the size of each element |
25 | | * @return pointer to allocated and zeroed memory, or throw std::bad_alloc on failure |
26 | | */ |
27 | | BOTAN_PUBLIC_API(2, 3) BOTAN_MALLOC_FN void* allocate_memory(size_t elems, size_t elem_size); |
28 | | |
29 | | /** |
30 | | * Free a pointer returned by allocate_memory |
31 | | * @param p the pointer returned by allocate_memory |
32 | | * @param elems the number of elements, as passed to allocate_memory |
33 | | * @param elem_size the size of each element, as passed to allocate_memory |
34 | | */ |
35 | | BOTAN_PUBLIC_API(2, 3) void deallocate_memory(void* p, size_t elems, size_t elem_size); |
36 | | |
37 | | /** |
38 | | * Ensure the allocator is initialized |
39 | | */ |
40 | | void BOTAN_UNSTABLE_API initialize_allocator(); |
41 | | |
42 | | class Allocator_Initializer final { |
43 | | public: |
44 | 1 | Allocator_Initializer() { initialize_allocator(); } |
45 | | }; |
46 | | |
47 | | /** |
48 | | * Scrub memory contents in a way that a compiler should not elide, |
49 | | * using some system specific technique. Note that this function might |
50 | | * not zero the memory (for example, in some hypothetical |
51 | | * implementation it might combine the memory contents with the output |
52 | | * of a system PRNG), but if you can detect any difference in behavior |
53 | | * at runtime then the clearing is side-effecting and you can just |
54 | | * use `clear_mem`. |
55 | | * |
56 | | * Use this function to scrub memory just before deallocating it, or on |
57 | | * a stack buffer before returning from the function. |
58 | | * |
59 | | * @param ptr a pointer to memory to scrub |
60 | | * @param n the number of bytes pointed to by ptr |
61 | | */ |
62 | | BOTAN_PUBLIC_API(2, 0) void secure_scrub_memory(void* ptr, size_t n); |
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 | | */ |
71 | | BOTAN_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 pointer to an array |
76 | | * @param y a pointer to another array |
77 | | * @param len the number of Ts in x and y |
78 | | * @return true iff x[i] == y[i] forall i in [0...n) |
79 | | */ |
80 | 0 | inline bool constant_time_compare(const uint8_t x[], const uint8_t y[], size_t len) { |
81 | 0 | return ct_compare_u8(x, y, len) == 0xFF; |
82 | 0 | } |
83 | | |
84 | | /** |
85 | | * Zero out some bytes. Warning: use secure_scrub_memory instead if the |
86 | | * memory is about to be freed or otherwise the compiler thinks it can |
87 | | * elide the writes. |
88 | | * |
89 | | * @param ptr a pointer to memory to zero |
90 | | * @param bytes the number of bytes to zero in ptr |
91 | | */ |
92 | 146M | inline constexpr void clear_bytes(void* ptr, size_t bytes) { |
93 | 146M | if(bytes > 0) { |
94 | 146M | std::memset(ptr, 0, bytes); |
95 | 146M | } |
96 | 146M | } |
97 | | |
98 | | /** |
99 | | * Zero memory before use. This simply calls memset and should not be |
100 | | * used in cases where the compiler cannot see the call as a |
101 | | * side-effecting operation (for example, if calling clear_mem before |
102 | | * deallocating memory, the compiler would be allowed to omit the call |
103 | | * to memset entirely under the as-if rule.) |
104 | | * |
105 | | * @param ptr a pointer to an array of Ts to zero |
106 | | * @param n the number of Ts pointed to by ptr |
107 | | */ |
108 | | template <typename T> |
109 | 146M | inline constexpr void clear_mem(T* ptr, size_t n) { |
110 | 146M | clear_bytes(ptr, sizeof(T) * n); |
111 | 146M | } void Botan::clear_mem<unsigned char>(unsigned char*, unsigned long) Line | Count | Source | 109 | 25 | inline constexpr void clear_mem(T* ptr, size_t n) { | 110 | 25 | clear_bytes(ptr, sizeof(T) * n); | 111 | 25 | } |
void Botan::clear_mem<unsigned long>(unsigned long*, unsigned long) Line | Count | Source | 109 | 146M | inline constexpr void clear_mem(T* ptr, size_t n) { | 110 | 146M | clear_bytes(ptr, sizeof(T) * n); | 111 | 146M | } |
void Botan::clear_mem<unsigned int>(unsigned int*, unsigned long) Line | Count | Source | 109 | 1 | inline constexpr void clear_mem(T* ptr, size_t n) { | 110 | 1 | clear_bytes(ptr, sizeof(T) * n); | 111 | 1 | } |
Unexecuted instantiation: void Botan::clear_mem<unsigned short>(unsigned short*, unsigned long) |
112 | | |
113 | | /** |
114 | | * Copy memory |
115 | | * @param out the destination array |
116 | | * @param in the source array |
117 | | * @param n the number of elements of in/out |
118 | | */ |
119 | | template <typename T> |
120 | | inline constexpr void copy_mem(T* out, const T* in, size_t n) |
121 | | requires std::is_trivial<typename std::decay<T>::type>::value |
122 | 1.53M | { |
123 | 1.53M | BOTAN_ASSERT_IMPLICATION(n > 0, in != nullptr && out != nullptr, "If n > 0 then args are not null"); |
124 | | |
125 | 1.53M | if(in != nullptr && out != nullptr && n > 0) { |
126 | 1.51M | std::memmove(out, in, sizeof(T) * n); |
127 | 1.51M | } |
128 | 1.53M | } void Botan::copy_mem<unsigned long>(unsigned long*, unsigned long const*, unsigned long) Line | Count | Source | 122 | 1.43M | { | 123 | 1.43M | BOTAN_ASSERT_IMPLICATION(n > 0, in != nullptr && out != nullptr, "If n > 0 then args are not null"); | 124 | | | 125 | 1.43M | if(in != nullptr && out != nullptr && n > 0) { | 126 | 1.42M | std::memmove(out, in, sizeof(T) * n); | 127 | 1.42M | } | 128 | 1.43M | } |
void Botan::copy_mem<unsigned char>(unsigned char*, unsigned char const*, unsigned long) Line | Count | Source | 122 | 101k | { | 123 | 101k | BOTAN_ASSERT_IMPLICATION(n > 0, in != nullptr && out != nullptr, "If n > 0 then args are not null"); | 124 | | | 125 | 101k | if(in != nullptr && out != nullptr && n > 0) { | 126 | 99.6k | std::memmove(out, in, sizeof(T) * n); | 127 | 99.6k | } | 128 | 101k | } |
Unexecuted instantiation: void Botan::copy_mem<unsigned int>(unsigned int*, unsigned int const*, unsigned long) |
129 | | |
130 | | template <typename T> |
131 | | inline constexpr void typecast_copy(uint8_t out[], T in[], size_t N) |
132 | | requires std::is_trivially_copyable<T>::value |
133 | 36 | { |
134 | 36 | std::memcpy(out, in, sizeof(T) * N); |
135 | 36 | } void Botan::typecast_copy<unsigned long>(unsigned char*, unsigned long*, unsigned long) Line | Count | Source | 133 | 4 | { | 134 | 4 | std::memcpy(out, in, sizeof(T) * N); | 135 | 4 | } |
Unexecuted instantiation: void Botan::typecast_copy<unsigned short>(unsigned char*, unsigned short*, unsigned long) void Botan::typecast_copy<unsigned int>(unsigned char*, unsigned int*, unsigned long) Line | Count | Source | 133 | 32 | { | 134 | 32 | std::memcpy(out, in, sizeof(T) * N); | 135 | 32 | } |
|
136 | | |
137 | | template <typename T> |
138 | | inline constexpr void typecast_copy(T out[], const uint8_t in[], size_t N) |
139 | | requires std::is_trivial<T>::value |
140 | 531k | { |
141 | 531k | std::memcpy(out, in, sizeof(T) * N); |
142 | 531k | } void Botan::typecast_copy<unsigned long>(unsigned long*, unsigned char const*, unsigned long) Line | Count | Source | 140 | 531k | { | 141 | 531k | std::memcpy(out, in, sizeof(T) * N); | 142 | 531k | } |
Unexecuted instantiation: void Botan::typecast_copy<unsigned short>(unsigned short*, unsigned char const*, unsigned long) void Botan::typecast_copy<unsigned int>(unsigned int*, unsigned char const*, unsigned long) Line | Count | Source | 140 | 162 | { | 141 | 162 | std::memcpy(out, in, sizeof(T) * N); | 142 | 162 | } |
|
143 | | |
144 | | template <typename T> |
145 | 36 | inline constexpr void typecast_copy(uint8_t out[], T in) { |
146 | 36 | typecast_copy(out, &in, 1); |
147 | 36 | } void Botan::typecast_copy<unsigned long>(unsigned char*, unsigned long) Line | Count | Source | 145 | 4 | inline constexpr void typecast_copy(uint8_t out[], T in) { | 146 | 4 | typecast_copy(out, &in, 1); | 147 | 4 | } |
Unexecuted instantiation: void Botan::typecast_copy<unsigned short>(unsigned char*, unsigned short) void Botan::typecast_copy<unsigned int>(unsigned char*, unsigned int) Line | Count | Source | 145 | 32 | inline constexpr void typecast_copy(uint8_t out[], T in) { | 146 | 32 | typecast_copy(out, &in, 1); | 147 | 32 | } |
|
148 | | |
149 | | template <typename T> |
150 | | inline constexpr void typecast_copy(T& out, const uint8_t in[]) |
151 | | requires std::is_trivial<typename std::decay<T>::type>::value |
152 | 531k | { |
153 | 531k | typecast_copy(&out, in, 1); |
154 | 531k | } void Botan::typecast_copy<unsigned long>(unsigned long&, unsigned char const*) Line | Count | Source | 152 | 531k | { | 153 | 531k | typecast_copy(&out, in, 1); | 154 | 531k | } |
Unexecuted instantiation: void Botan::typecast_copy<unsigned short>(unsigned short&, unsigned char const*) void Botan::typecast_copy<unsigned int>(unsigned int&, unsigned char const*) Line | Count | Source | 152 | 160 | { | 153 | 160 | typecast_copy(&out, in, 1); | 154 | 160 | } |
|
155 | | |
156 | | template <class To, class FromT> |
157 | | inline constexpr To typecast_copy(const FromT* src) noexcept |
158 | | requires std::is_trivially_copyable<FromT>::value && std::is_trivial<To>::value |
159 | | { |
160 | | To dst; |
161 | | std::memcpy(&dst, src, sizeof(To)); |
162 | | return dst; |
163 | | } |
164 | | |
165 | | /** |
166 | | * Set memory to a fixed value |
167 | | * @param ptr a pointer to an array of bytes |
168 | | * @param n the number of Ts pointed to by ptr |
169 | | * @param val the value to set each byte to |
170 | | */ |
171 | 0 | inline constexpr void set_mem(uint8_t* ptr, size_t n, uint8_t val) { |
172 | 0 | if(n > 0) { |
173 | 0 | std::memset(ptr, val, n); |
174 | 0 | } |
175 | 0 | } |
176 | | |
177 | 10 | inline const uint8_t* cast_char_ptr_to_uint8(const char* s) { return reinterpret_cast<const uint8_t*>(s); } |
178 | | |
179 | 10 | inline const char* cast_uint8_ptr_to_char(const uint8_t* b) { return reinterpret_cast<const char*>(b); } |
180 | | |
181 | 0 | inline uint8_t* cast_char_ptr_to_uint8(char* s) { return reinterpret_cast<uint8_t*>(s); } |
182 | | |
183 | 0 | inline char* cast_uint8_ptr_to_char(uint8_t* b) { return reinterpret_cast<char*>(b); } |
184 | | |
185 | | /** |
186 | | * Memory comparison, input insensitive |
187 | | * @param p1 a pointer to an array |
188 | | * @param p2 a pointer to another array |
189 | | * @param n the number of Ts in p1 and p2 |
190 | | * @return true iff p1[i] == p2[i] forall i in [0...n) |
191 | | */ |
192 | | template <typename T> |
193 | | inline bool same_mem(const T* p1, const T* p2, size_t n) { |
194 | | volatile T difference = 0; |
195 | | |
196 | | for(size_t i = 0; i != n; ++i) |
197 | | difference = difference | (p1[i] ^ p2[i]); |
198 | | |
199 | | return difference == 0; |
200 | | } |
201 | | |
202 | | template <typename T, typename Alloc> |
203 | 9 | size_t buffer_insert(std::vector<T, Alloc>& buf, size_t buf_offset, const T input[], size_t input_length) { |
204 | 9 | BOTAN_ASSERT_NOMSG(buf_offset <= buf.size()); |
205 | 9 | const size_t to_copy = std::min(input_length, buf.size() - buf_offset); |
206 | 9 | if(to_copy > 0) { |
207 | 3 | copy_mem(&buf[buf_offset], input, to_copy); |
208 | 3 | } |
209 | 9 | return to_copy; |
210 | 9 | } unsigned long Botan::buffer_insert<unsigned char, Botan::secure_allocator<unsigned char> >(std::__1::vector<unsigned char, Botan::secure_allocator<unsigned char> >&, unsigned long, unsigned char const*, unsigned long) Line | Count | Source | 203 | 9 | size_t buffer_insert(std::vector<T, Alloc>& buf, size_t buf_offset, const T input[], size_t input_length) { | 204 | 9 | BOTAN_ASSERT_NOMSG(buf_offset <= buf.size()); | 205 | 9 | const size_t to_copy = std::min(input_length, buf.size() - buf_offset); | 206 | 9 | if(to_copy > 0) { | 207 | 3 | copy_mem(&buf[buf_offset], input, to_copy); | 208 | 3 | } | 209 | 9 | return to_copy; | 210 | 9 | } |
Unexecuted instantiation: unsigned long Botan::buffer_insert<unsigned char, std::__1::allocator<unsigned char> >(std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >&, unsigned long, unsigned char const*, unsigned long) |
211 | | |
212 | | template <typename T, typename Alloc, typename Alloc2> |
213 | 0 | size_t buffer_insert(std::vector<T, Alloc>& buf, size_t buf_offset, const std::vector<T, Alloc2>& input) { |
214 | 0 | BOTAN_ASSERT_NOMSG(buf_offset <= buf.size()); |
215 | 0 | const size_t to_copy = std::min(input.size(), buf.size() - buf_offset); |
216 | 0 | if(to_copy > 0) { |
217 | 0 | copy_mem(&buf[buf_offset], input.data(), to_copy); |
218 | 0 | } |
219 | 0 | return to_copy; |
220 | 0 | } |
221 | | |
222 | | /** |
223 | | * XOR arrays. Postcondition out[i] = in[i] ^ out[i] forall i = 0...length |
224 | | * @param out the input/output buffer |
225 | | * @param in the read-only input buffer |
226 | | * @param length the length of the buffers |
227 | | */ |
228 | 0 | inline void xor_buf(uint8_t out[], const uint8_t in[], size_t length) { |
229 | 0 | const size_t blocks = length - (length % 32); |
230 | |
|
231 | 0 | for(size_t i = 0; i != blocks; i += 32) { |
232 | 0 | uint64_t x[4]; |
233 | 0 | uint64_t y[4]; |
234 | |
|
235 | 0 | typecast_copy(x, out + i, 4); |
236 | 0 | typecast_copy(y, in + i, 4); |
237 | |
|
238 | 0 | x[0] ^= y[0]; |
239 | 0 | x[1] ^= y[1]; |
240 | 0 | x[2] ^= y[2]; |
241 | 0 | x[3] ^= y[3]; |
242 | |
|
243 | 0 | typecast_copy(out + i, x, 4); |
244 | 0 | } |
245 | |
|
246 | 0 | for(size_t i = blocks; i != length; ++i) { |
247 | 0 | out[i] ^= in[i]; |
248 | 0 | } |
249 | 0 | } |
250 | | |
251 | | /** |
252 | | * XOR arrays. Postcondition out[i] = in[i] ^ in2[i] forall i = 0...length |
253 | | * @param out the output buffer |
254 | | * @param in the first input buffer |
255 | | * @param in2 the second output buffer |
256 | | * @param length the length of the three buffers |
257 | | */ |
258 | 0 | inline void xor_buf(uint8_t out[], const uint8_t in[], const uint8_t in2[], size_t length) { |
259 | 0 | const size_t blocks = length - (length % 32); |
260 | |
|
261 | 0 | for(size_t i = 0; i != blocks; i += 32) { |
262 | 0 | uint64_t x[4]; |
263 | 0 | uint64_t y[4]; |
264 | |
|
265 | 0 | typecast_copy(x, in + i, 4); |
266 | 0 | typecast_copy(y, in2 + i, 4); |
267 | |
|
268 | 0 | x[0] ^= y[0]; |
269 | 0 | x[1] ^= y[1]; |
270 | 0 | x[2] ^= y[2]; |
271 | 0 | x[3] ^= y[3]; |
272 | |
|
273 | 0 | typecast_copy(out + i, x, 4); |
274 | 0 | } |
275 | |
|
276 | 0 | for(size_t i = blocks; i != length; ++i) { |
277 | 0 | out[i] = in[i] ^ in2[i]; |
278 | 0 | } |
279 | 0 | } |
280 | | |
281 | 0 | inline void xor_buf(std::span<uint8_t> out, std::span<const uint8_t> in, size_t n) { |
282 | 0 | xor_buf(out.data(), in.data(), n); |
283 | 0 | } |
284 | | |
285 | | template <typename Alloc> |
286 | 0 | void xor_buf(std::vector<uint8_t, Alloc>& out, const uint8_t* in, size_t n) { |
287 | 0 | xor_buf(out.data(), in, n); |
288 | 0 | } |
289 | | |
290 | | template <typename Alloc, typename Alloc2> |
291 | | void xor_buf(std::vector<uint8_t, Alloc>& out, const uint8_t* in, const std::vector<uint8_t, Alloc2>& in2, size_t n) { |
292 | | xor_buf(out.data(), in, in2.data(), n); |
293 | | } |
294 | | |
295 | | template <typename Alloc, typename Alloc2> |
296 | 0 | std::vector<uint8_t, Alloc>& operator^=(std::vector<uint8_t, Alloc>& out, const std::vector<uint8_t, Alloc2>& in) { |
297 | 0 | if(out.size() < in.size()) |
298 | 0 | out.resize(in.size()); |
299 | |
|
300 | 0 | xor_buf(out.data(), in.data(), in.size()); |
301 | 0 | return out; |
302 | 0 | } |
303 | | |
304 | | } // namespace Botan |
305 | | |
306 | | #endif |