securer_string/secure_types/
boxed.rs1use core::fmt;
2use std::borrow::{Borrow, BorrowMut};
3use std::mem::MaybeUninit;
4
5use subtle::ConstantTimeEq;
6use zeroize::Zeroize;
7
8use crate::secure_utils::memlock;
9
10pub struct SecureBox<T>
23where
24 T: Copy,
25{
26 content: Option<Box<T>>,
29 is_locked: bool,
32}
33
34impl<T> SecureBox<T>
35where
36 T: Copy,
37{
38 #[must_use]
39 pub fn new(mut cont: Box<T>) -> Self {
40 let is_locked = memlock::mlock(&raw mut *cont, 1).is_ok();
41 SecureBox {
42 content: Some(cont),
43 is_locked,
44 }
45 }
46
47 #[must_use]
53 pub fn unsecure(&self) -> &T {
54 self.content
55 .as_deref()
56 .expect("SecureBox content accessed after drop")
57 }
58
59 #[must_use]
65 pub fn unsecure_mut(&mut self) -> &mut T {
66 self.content
67 .as_deref_mut()
68 .expect("SecureBox content accessed after drop")
69 }
70}
71
72impl<T: Copy> Clone for SecureBox<T> {
73 fn clone(&self) -> Self {
74 Self::new(Box::new(*self.unsecure()))
75 }
76}
77
78impl<T: Copy + ConstantTimeEq> ConstantTimeEq for SecureBox<T> {
79 fn ct_eq(&self, other: &Self) -> subtle::Choice {
80 self.unsecure().ct_eq(other.unsecure())
81 }
82}
83
84impl<T: Copy + ConstantTimeEq> PartialEq for SecureBox<T> {
85 fn eq(&self, other: &Self) -> bool {
86 self.ct_eq(other).into()
87 }
88}
89
90impl<T: Copy + ConstantTimeEq> Eq for SecureBox<T> {}
91
92impl<T, U> std::ops::Index<U> for SecureBox<T>
94where
95 T: std::ops::Index<U> + Copy,
96{
97 type Output = <T as std::ops::Index<U>>::Output;
98
99 fn index(&self, index: U) -> &Self::Output {
100 std::ops::Index::index(self.unsecure(), index)
101 }
102}
103
104impl<T> Borrow<T> for SecureBox<T>
106where
107 T: Copy,
108{
109 fn borrow(&self) -> &T {
110 self.unsecure()
111 }
112}
113impl<T> BorrowMut<T> for SecureBox<T>
114where
115 T: Copy,
116{
117 fn borrow_mut(&mut self) -> &mut T {
118 self.unsecure_mut()
119 }
120}
121
122impl<T> Drop for SecureBox<T>
124where
125 T: Copy,
126{
127 fn drop(&mut self) {
128 let ptr = Box::into_raw(self.content.take().expect("SecureBox dropped twice"));
133
134 unsafe {
142 std::slice::from_raw_parts_mut::<MaybeUninit<u8>>(
143 ptr.cast::<MaybeUninit<u8>>(),
144 std::mem::size_of::<T>(),
145 )
146 .zeroize();
147 }
148
149 if self.is_locked {
150 memlock::munlock(ptr, 1);
151 }
152
153 if std::mem::size_of::<T>() != 0 {
155 unsafe { std::alloc::dealloc(ptr.cast::<u8>(), std::alloc::Layout::new::<T>()) };
160 }
161 }
162}
163
164impl<T> fmt::Debug for SecureBox<T>
166where
167 T: Copy,
168{
169 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
170 f.debug_struct("SecureBox").finish_non_exhaustive()
171 }
172}
173
174impl<T> fmt::Display for SecureBox<T>
175where
176 T: Copy,
177{
178 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
179 f.write_str("***SECRET***").map_err(|_| fmt::Error)
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 use std::mem::MaybeUninit;
186
187 use zeroize::Zeroize;
188
189 use super::SecureBox;
190 use crate::test_utils::{PRIVATE_KEY_1, PRIVATE_KEY_2, Packed, Padded};
191
192 unsafe fn zero_out_secure_box<T>(secure_box: &mut SecureBox<T>)
198 where
199 T: Copy,
200 {
201 unsafe {
202 std::slice::from_raw_parts_mut::<MaybeUninit<u8>>(
206 std::ptr::from_mut::<T>(secure_box.unsecure_mut()).cast::<MaybeUninit<u8>>(),
207 std::mem::size_of::<T>(),
208 )
209 .zeroize();
210 }
211 }
212
213 #[test]
214 fn test_secure_box() {
215 let key_1 = SecureBox::new(Box::new(PRIVATE_KEY_1));
216 let key_2 = SecureBox::new(Box::new(PRIVATE_KEY_2));
217 let key_3 = SecureBox::new(Box::new(PRIVATE_KEY_1));
218 assert_eq!(key_1, key_1);
219 assert_ne!(key_1, key_2);
220 assert_ne!(key_2, key_3);
221 assert_eq!(key_1, key_3);
222
223 let mut final_key = key_1.clone();
224 unsafe {
225 zero_out_secure_box(&mut final_key);
226 }
227 assert_eq!(final_key.unsecure().0, [0; 32]);
228 }
229
230 #[test]
231 fn test_repr_c_with_padding() {
232 assert_eq!(std::mem::size_of::<Padded>(), 4); let sec_a = SecureBox::new(Box::new(Padded { x: 1, y: 2 }));
235 let sec_b = SecureBox::new(Box::new(Padded { x: 1, y: 2 }));
236 assert_eq!(sec_a, sec_b);
237
238 let sec_c = SecureBox::new(Box::new(Padded { x: 1, y: 3 }));
239 assert_ne!(sec_a, sec_c);
240
241 let sec_d = SecureBox::new(Box::new(Padded { x: 2, y: 2 }));
242 assert_ne!(sec_a, sec_d);
243 }
244
245 #[test]
246 fn test_repr_c_packed() {
247 assert_eq!(std::mem::size_of::<Packed>(), 3);
248
249 let sec_a = SecureBox::new(Box::new(Packed { x: 42, y: 1000 }));
250 let sec_b = SecureBox::new(Box::new(Packed { x: 42, y: 1000 }));
251 let sec_c = SecureBox::new(Box::new(Packed { x: 42, y: 1001 }));
252 let sec_d = SecureBox::new(Box::new(Packed { x: 43, y: 1000 }));
253
254 assert_eq!(sec_a, sec_b);
255 assert_ne!(sec_a, sec_c);
256 assert_ne!(sec_a, sec_d);
257 }
258}