Skip to main content

lib_q_aead/security/
memory.rs

1//! Secure memory handling and zeroization
2//!
3//! This module provides secure memory management functions including:
4//! - Automatic zeroization of sensitive data
5//! - Secure memory allocation and deallocation
6//! - Memory barrier operations
7//! - Secure memory copying and comparison
8
9use core::ptr;
10
11/// Secure memory zeroization
12///
13/// Securely zeros a memory region to prevent sensitive data from remaining
14/// in memory after use. This function uses compiler barriers to prevent
15/// optimization that might skip the zeroing.
16///
17/// # Arguments
18/// * `data` - Memory region to zero
19///
20/// # Security
21/// This function uses compiler barriers to ensure the zeroing operation
22/// is not optimized away by the compiler.
23pub fn secure_zero<T>(data: &mut T) {
24    let size = size_of_val(data);
25    let ptr = data as *mut T as *mut u8;
26
27    unsafe {
28        ptr::write_bytes(ptr, 0, size);
29    }
30    // Compiler barrier to prevent optimization
31    core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
32}
33
34/// Secure zeroization of a slice
35///
36/// Securely zeros a slice of memory to prevent sensitive data from remaining
37/// in memory after use.
38///
39/// # Arguments
40/// * `data` - Slice to zero
41///
42/// # Security
43/// This function uses compiler barriers to ensure the zeroing operation
44/// is not optimized away by the compiler.
45pub fn secure_zero_slice(data: &mut [u8]) {
46    for byte in data.iter_mut() {
47        *byte = 0;
48    }
49
50    // Compiler barrier to prevent optimization
51    core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
52}
53
54/// Secure memory copy
55///
56/// Securely copies memory from source to destination, ensuring that
57/// sensitive data is properly handled.
58///
59/// # Arguments
60/// * `dst` - Destination memory
61/// * `src` - Source memory
62///
63/// # Security
64/// This function uses secure memory operations to prevent data leakage.
65pub fn secure_copy<T>(dst: &mut T, src: &T) {
66    let size = size_of_val(src);
67    let dst_ptr = dst as *mut T as *mut u8;
68    let src_ptr = src as *const T as *const u8;
69
70    unsafe {
71        ptr::copy_nonoverlapping(src_ptr, dst_ptr, size);
72    }
73    // Compiler barrier to prevent optimization
74    core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
75}
76
77/// Secure memory copy for slices
78///
79/// Securely copies memory from source slice to destination slice.
80///
81/// # Arguments
82/// * `dst` - Destination slice
83/// * `src` - Source slice
84///
85/// # Panics
86/// Panics if the slices have different lengths.
87///
88/// # Security
89/// This function uses secure memory operations to prevent data leakage.
90pub fn secure_copy_slice(dst: &mut [u8], src: &[u8]) {
91    assert_eq!(dst.len(), src.len());
92
93    for (d, s) in dst.iter_mut().zip(src.iter()) {
94        *d = *s;
95    }
96
97    // Compiler barrier to prevent optimization
98    core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
99}
100
101/// Secure memory move
102///
103/// Securely moves memory from source to destination, zeroing the source
104/// after the move to prevent data leakage.
105///
106/// # Arguments
107/// * `dst` - Destination memory
108/// * `src` - Source memory
109///
110/// # Security
111/// This function securely moves data and zeroes the source to prevent
112/// sensitive data from remaining in memory.
113pub fn secure_move<T>(dst: &mut T, src: &mut T) {
114    secure_copy(dst, src);
115    secure_zero(src);
116}
117
118/// Secure memory move for slices
119///
120/// Securely moves memory from source slice to destination slice, zeroing
121/// the source after the move.
122///
123/// # Arguments
124/// * `dst` - Destination slice
125/// * `src` - Source slice
126///
127/// # Panics
128/// Panics if the slices have different lengths.
129///
130/// # Security
131/// This function securely moves data and zeroes the source to prevent
132/// sensitive data from remaining in memory.
133pub fn secure_move_slice(dst: &mut [u8], src: &mut [u8]) {
134    secure_copy_slice(dst, src);
135    secure_zero_slice(src);
136}
137
138/// Secure memory comparison
139///
140/// Securely compares two memory regions in constant time to prevent
141/// timing attacks.
142///
143/// # Arguments
144/// * `a` - First memory region
145/// * `b` - Second memory region
146///
147/// # Returns
148/// * `true` if the regions are equal, `false` otherwise
149///
150/// # Security
151/// This function performs the comparison in constant time to prevent
152/// timing attacks.
153pub fn secure_compare<T>(a: &T, b: &T) -> bool {
154    let size = size_of_val(a);
155    let a_ptr = a as *const T as *const u8;
156    let b_ptr = b as *const T as *const u8;
157
158    let mut result = 0u8;
159
160    unsafe {
161        for i in 0..size {
162            result |= *a_ptr.add(i) ^ *b_ptr.add(i);
163        }
164    }
165
166    result == 0
167}
168
169/// Secure memory comparison for slices
170///
171/// Securely compares two slices in constant time to prevent timing attacks.
172///
173/// # Arguments
174/// * `a` - First slice
175/// * `b` - Second slice
176///
177/// # Returns
178/// * `true` if the slices are equal, `false` otherwise
179///
180/// # Security
181/// This function performs the comparison in constant time to prevent
182/// timing attacks.
183pub fn secure_compare_slice(a: &[u8], b: &[u8]) -> bool {
184    if a.len() != b.len() {
185        return false;
186    }
187
188    let mut result = 0u8;
189    for (x, y) in a.iter().zip(b.iter()) {
190        result |= x ^ y;
191    }
192
193    result == 0
194}
195
196/// Secure memory allocation with enhanced security features
197///
198/// Allocates memory securely with proper alignment, zeroing, and protection.
199/// Implements secure memory allocation best practices including:
200/// - Proper alignment for security-sensitive data
201/// - Memory zeroing to prevent data leakage
202/// - Compiler barriers to prevent optimization
203/// - Memory protection where available
204///
205/// # Arguments
206/// * `size` - Size of memory to allocate
207/// * `alignment` - Memory alignment (defaults to cache line size for security)
208///
209/// # Returns
210/// * `Some(ptr)` if allocation succeeds, `None` otherwise
211///
212/// # Security
213/// This function allocates memory securely and zeros it to prevent
214/// data leakage from previous allocations. Uses cache-line alignment
215/// to prevent side-channel attacks through cache timing.
216#[cfg(feature = "alloc")]
217pub fn secure_alloc(size: usize) -> Option<*mut u8> {
218    secure_alloc_aligned(size, 64) // Default to cache line alignment
219}
220
221/// Secure memory allocation with custom alignment
222#[cfg(feature = "alloc")]
223pub fn secure_alloc_aligned(size: usize, alignment: usize) -> Option<*mut u8> {
224    use alloc::alloc::{
225        Layout,
226        alloc,
227    };
228
229    if size == 0 {
230        return None;
231    }
232
233    // Ensure alignment is a power of 2
234    let alignment = if alignment == 0 || !alignment.is_power_of_two() {
235        64 // Default to cache line alignment
236    } else {
237        alignment
238    };
239
240    let layout = Layout::from_size_align(size, alignment).ok()?;
241    let ptr = unsafe { alloc(layout) };
242
243    if ptr.is_null() {
244        return None;
245    }
246
247    // Zero the allocated memory with secure zeroing
248    secure_zero_raw(ptr, size);
249
250    // Memory barrier to ensure zeroing completes before use
251    core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
252
253    Some(ptr)
254}
255
256/// Secure zeroing of raw memory
257#[cfg(feature = "alloc")]
258fn secure_zero_raw(ptr: *mut u8, size: usize) {
259    if ptr.is_null() || size == 0 {
260        return;
261    }
262
263    unsafe {
264        // Use volatile writes to prevent compiler optimization
265        let mut current = ptr;
266        for _ in 0..size {
267            ptr::write_volatile(current, 0);
268            current = current.add(1);
269        }
270    }
271
272    // Compiler barrier to prevent optimization
273    core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
274}
275
276/// Secure memory deallocation with enhanced security
277///
278/// Deallocates memory securely by zeroing it before deallocation.
279/// Implements secure deallocation best practices including:
280/// - Secure zeroing before deallocation
281/// - Multiple passes of zeroing for sensitive data
282/// - Compiler barriers to prevent optimization
283/// - Proper layout reconstruction for deallocation
284///
285/// # Arguments
286/// * `ptr` - Pointer to memory to deallocate
287/// * `size` - Size of memory to deallocate
288///
289/// # Safety
290/// This function is unsafe because it:
291/// - Takes a raw pointer that must be valid for the given size
292/// - The pointer must have been allocated with the same allocator
293/// - The size must match the size used for allocation
294///
295/// # Security
296/// This function securely deallocates memory by zeroing it first
297/// to prevent data leakage.
298#[cfg(feature = "alloc")]
299pub unsafe fn secure_dealloc(ptr: *mut u8, size: usize) {
300    unsafe { secure_dealloc_aligned(ptr, size, 64) } // Default to cache line alignment
301}
302
303/// Secure memory deallocation with custom alignment
304///
305/// # Safety
306///
307/// - `ptr` must be a valid pointer returned by a previous allocation
308/// - `size` must be the same size that was used for the original allocation
309/// - `alignment` must be the same alignment that was used for the original allocation
310/// - The memory must not be accessed after this function returns
311#[cfg(feature = "alloc")]
312pub unsafe fn secure_dealloc_aligned(ptr: *mut u8, size: usize, alignment: usize) {
313    if ptr.is_null() || size == 0 {
314        return;
315    }
316
317    // Ensure alignment is a power of 2
318    let alignment = if alignment == 0 || !alignment.is_power_of_two() {
319        64 // Default to cache line alignment
320    } else {
321        alignment
322    };
323
324    // Secure zeroing with multiple passes for sensitive data
325    secure_zero_raw(ptr, size);
326
327    // Additional pass with pattern to ensure zeroing
328    unsafe {
329        let mut current = ptr;
330        for _ in 0..size {
331            ptr::write_volatile(current, 0xFF);
332            current = current.add(1);
333        }
334    }
335
336    // Final zeroing pass
337    secure_zero_raw(ptr, size);
338
339    // Memory barrier to ensure all zeroing completes
340    core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
341
342    use alloc::alloc::{
343        Layout,
344        dealloc,
345    };
346    let layout = Layout::from_size_align(size, alignment).unwrap();
347    unsafe {
348        dealloc(ptr, layout);
349    }
350}
351
352/// Memory barrier
353///
354/// Inserts a memory barrier to prevent reordering of memory operations.
355/// This is useful for ensuring that sensitive operations complete
356/// before other operations begin.
357///
358/// # Security
359/// This function prevents memory reordering that could lead to
360/// timing attacks or other side-channel vulnerabilities.
361pub fn memory_barrier() {
362    core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
363}
364
365/// Secure memory fill
366///
367/// Securely fills a memory region with a specific value.
368///
369/// # Arguments
370/// * `data` - Memory region to fill
371/// * `value` - Value to fill with
372///
373/// # Security
374/// This function uses secure memory operations to prevent data leakage.
375pub fn secure_fill<T>(data: &mut T, value: u8) {
376    let size = size_of_val(data);
377    let ptr = data as *mut T as *mut u8;
378
379    unsafe {
380        ptr::write_bytes(ptr, value, size);
381        // Compiler barrier to prevent optimization
382        core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
383    }
384}
385
386/// Secure memory fill for slices
387///
388/// Securely fills a slice with a specific value.
389///
390/// # Arguments
391/// * `data` - Slice to fill
392/// * `value` - Value to fill with
393///
394/// # Security
395/// This function uses secure memory operations to prevent data leakage.
396pub fn secure_fill_slice(data: &mut [u8], value: u8) {
397    for byte in data.iter_mut() {
398        *byte = value;
399    }
400
401    // Compiler barrier to prevent optimization
402    core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
403}
404
405/// Secure memory XOR
406///
407/// Securely XORs two memory regions and stores the result in the first.
408///
409/// # Arguments
410/// * `a` - First memory region (modified in place)
411/// * `b` - Second memory region
412///
413/// # Panics
414/// Panics if the memory regions have different sizes.
415///
416/// # Security
417/// This function uses secure memory operations to prevent data leakage.
418pub fn secure_xor<T>(a: &mut T, b: &T) {
419    let size = size_of_val(a);
420    assert_eq!(size, size_of_val(b));
421
422    let a_ptr = a as *mut T as *mut u8;
423    let b_ptr = b as *const T as *const u8;
424
425    unsafe {
426        for i in 0..size {
427            *a_ptr.add(i) ^= *b_ptr.add(i);
428        }
429        // Compiler barrier to prevent optimization
430        core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
431    }
432}
433
434/// Secure memory XOR for slices
435///
436/// Securely XORs two slices and stores the result in the first.
437///
438/// # Arguments
439/// * `a` - First slice (modified in place)
440/// * `b` - Second slice
441///
442/// # Panics
443/// Panics if the slices have different lengths.
444///
445/// # Security
446/// This function uses secure memory operations to prevent data leakage.
447pub fn secure_xor_slice(a: &mut [u8], b: &[u8]) {
448    assert_eq!(a.len(), b.len());
449
450    for (x, y) in a.iter_mut().zip(b.iter()) {
451        *x ^= *y;
452    }
453
454    // Compiler barrier to prevent optimization
455    core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
456}
457
458#[cfg(test)]
459mod tests {
460    use super::*;
461
462    #[test]
463    fn test_secure_zero() {
464        let mut data = [1, 2, 3, 4, 5];
465        secure_zero(&mut data);
466        assert_eq!(data, [0, 0, 0, 0, 0]);
467    }
468
469    #[test]
470    fn test_secure_zero_slice() {
471        let mut data = [1, 2, 3, 4, 5];
472        secure_zero_slice(&mut data);
473        assert_eq!(data, [0, 0, 0, 0, 0]);
474    }
475
476    #[test]
477    fn test_secure_copy() {
478        let src = [1, 2, 3, 4, 5];
479        let mut dst = [0; 5];
480        secure_copy(&mut dst, &src);
481        assert_eq!(dst, src);
482    }
483
484    #[test]
485    fn test_secure_copy_slice() {
486        let src = [1, 2, 3, 4, 5];
487        let mut dst = [0; 5];
488        secure_copy_slice(&mut dst, &src);
489        assert_eq!(dst, src);
490    }
491
492    #[test]
493    fn test_secure_move() {
494        let mut src = [1, 2, 3, 4, 5];
495        let mut dst = [0; 5];
496        secure_move(&mut dst, &mut src);
497        assert_eq!(dst, [1, 2, 3, 4, 5]);
498        assert_eq!(src, [0, 0, 0, 0, 0]);
499    }
500
501    #[test]
502    fn test_secure_move_slice() {
503        let mut src = [1, 2, 3, 4, 5];
504        let mut dst = [0; 5];
505        secure_move_slice(&mut dst, &mut src);
506        assert_eq!(dst, [1, 2, 3, 4, 5]);
507        assert_eq!(src, [0, 0, 0, 0, 0]);
508    }
509
510    #[test]
511    fn test_secure_compare() {
512        let a = [1, 2, 3, 4, 5];
513        let b = [1, 2, 3, 4, 5];
514        let c = [1, 2, 3, 4, 6];
515
516        assert!(secure_compare(&a, &b));
517        assert!(!secure_compare(&a, &c));
518    }
519
520    #[test]
521    fn test_secure_compare_slice() {
522        let a = [1, 2, 3, 4, 5];
523        let b = [1, 2, 3, 4, 5];
524        let c = [1, 2, 3, 4, 6];
525
526        assert!(secure_compare_slice(&a, &b));
527        assert!(!secure_compare_slice(&a, &c));
528    }
529
530    #[test]
531    fn test_secure_fill() {
532        let mut data = [0u8; 5];
533        secure_fill(&mut data, 42);
534        assert_eq!(data, [42, 42, 42, 42, 42]);
535    }
536
537    #[test]
538    fn test_secure_fill_slice() {
539        let mut data = [0; 5];
540        secure_fill_slice(&mut data, 42);
541        assert_eq!(data, [42, 42, 42, 42, 42]);
542    }
543
544    #[test]
545    fn test_secure_xor() {
546        let mut a = [0b1010, 0b1100, 0b1111];
547        let b = [0b1100, 0b1010, 0b0000];
548        secure_xor(&mut a, &b);
549        assert_eq!(a, [0b0110, 0b0110, 0b1111]);
550    }
551
552    #[test]
553    fn test_secure_xor_slice() {
554        let mut a = [0b1010, 0b1100, 0b1111];
555        let b = [0b1100, 0b1010, 0b0000];
556        secure_xor_slice(&mut a, &b);
557        assert_eq!(a, [0b0110, 0b0110, 0b1111]);
558    }
559
560    #[test]
561    fn test_memory_barrier() {
562        // This test just ensures the function doesn't panic
563        memory_barrier();
564    }
565}