Skip to main content

sec_mem/
secret_box.rs

1use core::{any, fmt::{self, Debug}};
2use zeroize::{Zeroize, ZeroizeOnDrop};
3
4use std::sync::OnceLock;
5
6#[inline(always)]
7fn global_canary() -> usize {
8    static CANARY: OnceLock<usize> = OnceLock::new();
9    *CANARY.get_or_init(|| {
10        #[cfg(all(feature = "sec_mem", target_os = "linux"))]
11        {
12            let mut canary = 0usize;
13            unsafe {
14                if libc::getrandom(
15                    &mut canary as *mut _ as *mut libc::c_void,
16                    core::mem::size_of::<usize>(),
17                    0,
18                ) == core::mem::size_of::<usize>() as isize {
19                    return canary;
20                }
21            }
22        }
23
24        #[cfg(target_arch = "x86_64")]
25        {
26            let mut canary = 0u64;
27            unsafe {
28                if core::arch::x86_64::_rdrand64_step(&mut canary) == 1 {
29                    return canary as usize;
30                }
31            }
32        }
33
34        #[cfg(target_arch = "x86")]
35        {
36            let mut canary = 0u32;
37            unsafe {
38                if core::arch::x86::_rdrand32_step(&mut canary) == 1 {
39                    return canary as usize;
40                }
41            }
42        }
43
44        // Fallback: ASLR pointer mixed with timestamp
45        use std::time::SystemTime;
46        let time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_nanos() as usize;
47        let aslr = global_canary as *const () as usize;
48        time.wrapping_mul(0x9E37_79B9_7F4A_7C15).wrapping_add(aslr)
49    })
50}
51
52/// A pure-software, stack-native hardened wrapper for sensitive values (`no_std` compatible).
53///
54/// `SecretBox` protects inline data by surrounding it with dynamically generated, randomized canaries
55/// (verified via compiler-optimization-proof `read_volatile` checks) to immediately panic during stack
56/// smashing buffer overflows. 
57///
58/// It implements mathematically strict `&mut self` concurrency locks, forces zeroization on drop, and 
59/// mandates that data can only be touched inside strictly scoped `.with_secret()` closures to 
60/// eliminate accidental memory leaks. It strictly forbids `Clone` to prevent key scattering.
61///
62/// # Examples
63/// ```
64/// use sec_mem::SecretBox;
65///
66/// // Wrap a basic 32-bit integer or a 256-bit array entirely on the stack
67/// let mut secret = SecretBox::new([0x42u8; 32]);
68///
69/// // The data can only be accessed exclusively inside a closure
70/// secret.with_secret_mut(|s| {
71///     s[0] = 0x99;
72/// }); 
73/// // Any stack corruption that occurred is detected here, panicking instantly.
74/// ```
75#[repr(C)]
76pub struct SecretBox<S: Zeroize> {
77    canary_front: usize,
78    inner_secret: S,
79    canary_back: usize,
80}
81
82impl<S: Zeroize> Zeroize for SecretBox<S> {
83    fn zeroize(&mut self) {
84        self.inner_secret.zeroize()
85    }
86}
87
88impl<S: Zeroize> Drop for SecretBox<S> {
89    fn drop(&mut self) {
90        self.zeroize()
91    }
92}
93
94impl<S: Zeroize> ZeroizeOnDrop for SecretBox<S> {}
95
96impl<S: Zeroize> From<S> for SecretBox<S> {
97    fn from(source: S) -> Self {
98        Self::new(source)
99    }
100}
101
102impl<S: Zeroize> SecretBox<S> {
103    #[inline(always)]
104    fn front_canary_expected() -> usize {
105        global_canary()
106    }
107
108    #[inline(always)]
109    fn back_canary_expected() -> usize {
110        !global_canary()
111    }
112
113    /// Create a secret value using an inline value.
114    pub fn new(secret: S) -> Self {
115        Self {
116            canary_front: Self::front_canary_expected(),
117            inner_secret: secret,
118            canary_back: Self::back_canary_expected(),
119        }
120    }
121
122    #[inline(always)]
123    fn check_canaries(&self) {
124        unsafe {
125            // Read volatile to absolutely prevent LLVM from dead-code-eliminating the check
126            // The compiler cannot mathematically optimize away volatile reads!
127            let front = core::ptr::read_volatile(&self.canary_front);
128            let back = core::ptr::read_volatile(&self.canary_back);
129
130            if front != Self::front_canary_expected() || back != Self::back_canary_expected() {
131                panic!("SECURITY ALERT: SecretBox stack memory corruption detected!");
132            }
133        }
134    }
135
136    /// Safer alternative: Provide an exclusive closure-based interface.
137    /// Requires `&mut self` to mathematically prevent concurrent multi-threaded 
138    /// aliasing or reference sharing.
139    pub fn with_secret<F, R>(&mut self, f: F) -> R
140    where
141        F: FnOnce(&S) -> R,
142    {
143        self.check_canaries();
144        f(&self.inner_secret)
145    }
146
147    /// Safer mutable alternative: Provide a closure-based interface.
148    pub fn with_secret_mut<F, R>(&mut self, f: F) -> R
149    where
150        F: FnOnce(&mut S) -> R,
151    {
152        self.check_canaries();
153        f(&mut self.inner_secret)
154    }
155}
156
157impl<S: Zeroize + Default> SecretBox<S> {
158    /// Create a secret value using a function that can initialize the value in-place.
159    pub fn init_with_mut(ctr: impl FnOnce(&mut S)) -> Self {
160        let mut secret = Self::default();
161        secret.with_secret_mut(|s| ctr(s));
162        secret
163    }
164}
165
166impl<S: Zeroize + Default> Default for SecretBox<S> {
167    fn default() -> Self {
168        Self {
169            canary_front: Self::front_canary_expected(),
170            inner_secret: S::default(),
171            canary_back: Self::back_canary_expected(),
172        }
173    }
174}
175
176impl<S: Zeroize> Debug for SecretBox<S> {
177    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178        write!(f, "SecretBox<{}>([REDACTED])", any::type_name::<S>())
179    }
180}
181
182// Clone is strictly removed. 
183// A high-security SecretBox should never blindly duplicate memory allocations.
184// If duplication is required, the user must explicitly construct a new SecretBox.
185
186impl<S: Zeroize> SecretBox<S> {
187    /// Performs a constant-time comparison of two SecretBoxes.
188    /// Requires `&mut` on both to enforce exclusive access and check canaries.
189    pub fn constant_time_eq(&mut self, other: &mut Self) -> subtle::Choice 
190    where 
191        S: subtle::ConstantTimeEq 
192    {
193        self.check_canaries();
194        other.check_canaries();
195        self.inner_secret.ct_eq(&other.inner_secret)
196    }
197}