Skip to main content

dcrypt_common/security/
memory.rs

1//! Memory safety patterns and secure operations
2//!
3//! This module provides traits and utilities for ensuring memory safety
4//! in cryptographic operations.
5
6// Handle Box imports based on features
7#[cfg(feature = "std")]
8use std::boxed::Box;
9
10#[cfg(all(not(feature = "std"), feature = "alloc"))]
11extern crate alloc as rust_alloc;
12
13#[cfg(all(not(feature = "std"), feature = "alloc"))]
14use rust_alloc::boxed::Box;
15
16/// Trait for types that can be securely compared
17///
18/// This trait provides constant-time comparison operations to prevent
19/// timing attacks.
20pub trait SecureCompare: Sized {
21    /// Compare two values in constant time
22    fn secure_eq(&self, other: &Self) -> bool;
23
24    /// Compare two values and return a constant-time choice
25    fn secure_cmp(&self, other: &Self) -> dcrypt_internal::constant_time::Choice;
26}
27
28impl<const N: usize> SecureCompare for [u8; N] {
29    fn secure_eq(&self, other: &Self) -> bool {
30        use dcrypt_internal::constant_time::ConstantTimeEq;
31        bool::from(self.ct_eq(other))
32    }
33
34    fn secure_cmp(&self, other: &Self) -> dcrypt_internal::constant_time::Choice {
35        use dcrypt_internal::constant_time::ConstantTimeEq;
36        self.ct_eq(other)
37    }
38}
39
40impl SecureCompare for &[u8] {
41    fn secure_eq(&self, other: &Self) -> bool {
42        use dcrypt_internal::constant_time::ConstantTimeEq;
43        bool::from(self.ct_eq(other))
44    }
45
46    fn secure_cmp(&self, other: &Self) -> dcrypt_internal::constant_time::Choice {
47        use dcrypt_internal::constant_time::ConstantTimeEq;
48        self.ct_eq(other)
49    }
50}
51
52/// Memory barrier utilities
53pub mod barrier {
54    use core::sync::atomic::{compiler_fence, fence, Ordering};
55
56    /// Insert a compiler fence to prevent reordering
57    #[inline(always)]
58    pub fn compiler_fence_seq_cst() {
59        compiler_fence(Ordering::SeqCst);
60    }
61
62    /// Insert a full memory fence
63    #[inline(always)]
64    pub fn memory_fence_seq_cst() {
65        fence(Ordering::SeqCst);
66    }
67
68    /// Execute a closure with memory barriers before and after
69    #[inline(always)]
70    pub fn with_barriers<T, F: FnOnce() -> T>(f: F) -> T {
71        compiler_fence_seq_cst();
72        let result = f();
73        compiler_fence_seq_cst();
74        result
75    }
76}
77
78/// Exact-size initialized storage helpers for sensitive values.
79///
80/// These helpers do not lock pages, alter allocator behavior, or claim to
81/// protect memory from the operating system. Doing so would require native
82/// interfaces that are outside dcrypt's implementation boundary.
83#[cfg(feature = "alloc")]
84pub mod alloc {
85    use super::*;
86    use dcrypt_internal::zeroing::Zeroize;
87
88    #[cfg(all(not(feature = "std"), feature = "alloc"))]
89    use super::rust_alloc::vec;
90
91    /// Allocate an exact-size boxed slice of initialized values.
92    pub fn zeroizing_box<T: Default + Zeroize + Clone>(size: usize) -> Box<[T]> {
93        vec![T::default(); size].into_boxed_slice()
94    }
95
96    /// Clear every initialized element before releasing a boxed slice.
97    pub fn clear_box<T: Zeroize>(mut data: Box<[T]>) {
98        data.zeroize();
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn test_secure_compare() {
108        let a = [1u8, 2, 3, 4];
109        let b = [1u8, 2, 3, 4];
110        let c = [1u8, 2, 3, 5];
111
112        assert!(a.secure_eq(&b));
113        assert!(!a.secure_eq(&c));
114    }
115
116    #[test]
117    fn test_memory_barriers() {
118        use barrier::*;
119
120        let result = with_barriers(|| {
121            let mut x = 42;
122            x += 1;
123            x
124        });
125
126        assert_eq!(result, 43);
127    }
128
129    #[test]
130    #[cfg(feature = "alloc")]
131    fn exact_size_sensitive_storage_helpers_round_trip() {
132        let mut data = alloc::zeroizing_box::<u8>(17);
133        assert_eq!(data.len(), 17);
134        data.fill(0x5a);
135        alloc::clear_box(data);
136    }
137}