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 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#[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 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 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 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 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 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
182impl<S: Zeroize> SecretBox<S> {
187 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}