dcrypt_internal/
zeroing.rs1#[cfg(any(feature = "alloc", feature = "std"))]
4use alloc::{boxed::Box, string::String, vec, vec::Vec};
5use core::fmt;
6use core::hint::black_box;
7use core::ops::{Deref, DerefMut};
8use core::sync::atomic::{compiler_fence, Ordering};
9
10pub trait Zeroize {
16 fn zeroize(&mut self);
17}
18
19pub trait ZeroizeOnDrop {}
21
22macro_rules! impl_zeroize_integer {
23 ($($ty:ty),+ $(,)?) => {$ (
24 impl Zeroize for $ty {
25 #[inline(never)]
26 fn zeroize(&mut self) {
27 *self = 0;
28 compiler_fence(Ordering::SeqCst);
29 black_box(self);
30 }
31 }
32 )+ };
33}
34
35impl_zeroize_integer!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
36
37impl Zeroize for bool {
38 #[inline(never)]
39 fn zeroize(&mut self) {
40 *self = false;
41 compiler_fence(Ordering::SeqCst);
42 black_box(self);
43 }
44}
45
46impl<T: Zeroize> Zeroize for [T] {
47 #[inline(never)]
48 fn zeroize(&mut self) {
49 for item in self.iter_mut() {
50 item.zeroize();
51 }
52 compiler_fence(Ordering::SeqCst);
53 black_box(self);
54 }
55}
56
57impl<T: Zeroize, const N: usize> Zeroize for [T; N] {
58 #[inline(never)]
59 fn zeroize(&mut self) {
60 self.as_mut_slice().zeroize();
61 }
62}
63
64impl<T: Zeroize> Zeroize for Option<T> {
65 fn zeroize(&mut self) {
66 if let Some(value) = self.as_mut() {
67 value.zeroize();
68 }
69 *self = None;
70 }
71}
72
73#[cfg(any(feature = "alloc", feature = "std"))]
74impl<T: Zeroize> Zeroize for Vec<T> {
75 fn zeroize(&mut self) {
76 self.as_mut_slice().zeroize();
77 self.clear();
78 }
79}
80
81#[cfg(any(feature = "alloc", feature = "std"))]
82impl<T: Zeroize> Zeroize for Box<[T]> {
83 fn zeroize(&mut self) {
84 self.as_mut().zeroize();
85 }
86}
87
88#[cfg(any(feature = "alloc", feature = "std"))]
89impl Zeroize for String {
90 fn zeroize(&mut self) {
91 let mut bytes = core::mem::take(self).into_bytes();
92 bytes.zeroize();
93 }
94}
95
96#[derive(Clone, Default, Eq, Ord, PartialEq, PartialOrd)]
98pub struct Zeroizing<T: Zeroize>(T);
99
100impl<T: Zeroize> fmt::Debug for Zeroizing<T> {
101 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
102 formatter.write_str("Zeroizing([REDACTED])")
103 }
104}
105
106impl<T: Zeroize> Zeroizing<T> {
107 pub const fn new(value: T) -> Self {
108 Self(value)
109 }
110
111 pub fn into_inner(mut self) -> T
117 where
118 T: Default,
119 {
120 core::mem::take(&mut self.0)
121 }
122}
123
124impl<const N: usize> Zeroizing<[u8; N]> {
125 pub fn into_inner_array(mut self) -> [u8; N] {
131 core::mem::replace(&mut self.0, [0u8; N])
132 }
133}
134
135impl<T: Zeroize> Deref for Zeroizing<T> {
136 type Target = T;
137
138 fn deref(&self) -> &Self::Target {
139 &self.0
140 }
141}
142
143impl<T: Zeroize> DerefMut for Zeroizing<T> {
144 fn deref_mut(&mut self) -> &mut Self::Target {
145 &mut self.0
146 }
147}
148
149impl<T: Zeroize> AsRef<T> for Zeroizing<T> {
150 fn as_ref(&self) -> &T {
151 &self.0
152 }
153}
154
155impl<T: Zeroize> AsMut<T> for Zeroizing<T> {
156 fn as_mut(&mut self) -> &mut T {
157 &mut self.0
158 }
159}
160
161impl<T: Zeroize> Drop for Zeroizing<T> {
162 fn drop(&mut self) {
163 self.0.zeroize();
164 }
165}
166
167impl<T: Zeroize> Zeroize for Zeroizing<T> {
168 fn zeroize(&mut self) {
169 self.0.zeroize();
170 }
171}
172
173impl<T: Zeroize> ZeroizeOnDrop for Zeroizing<T> {}
174
175#[cfg(any(feature = "alloc", feature = "std"))]
180pub type ZeroizingBytes = Zeroizing<Box<[u8]>>;
181
182#[cfg(any(feature = "alloc", feature = "std"))]
183impl Zeroizing<Box<[u8]>> {
184 pub fn as_slice(&self) -> &[u8] {
186 &self.0
187 }
188
189 pub fn as_mut_slice(&mut self) -> &mut [u8] {
191 &mut self.0
192 }
193
194 pub fn capacity(&self) -> usize {
196 self.0.len()
197 }
198}
199
200#[cfg(any(feature = "alloc", feature = "std"))]
201impl AsRef<[u8]> for Zeroizing<Box<[u8]>> {
202 fn as_ref(&self) -> &[u8] {
203 &self.0
204 }
205}
206
207#[cfg(any(feature = "alloc", feature = "std"))]
208impl AsMut<[u8]> for Zeroizing<Box<[u8]>> {
209 fn as_mut(&mut self) -> &mut [u8] {
210 &mut self.0
211 }
212}
213
214#[cfg(any(feature = "alloc", feature = "std"))]
219pub fn boxed_bytes_zeroed(len: usize) -> Box<[u8]> {
220 vec![0u8; len].into_boxed_slice()
221}
222
223#[cfg(any(feature = "alloc", feature = "std"))]
225pub fn boxed_bytes_from_slice(data: &[u8]) -> Box<[u8]> {
226 let mut boxed = boxed_bytes_zeroed(data.len());
227 boxed.copy_from_slice(data);
228 boxed
229}
230
231#[cfg(any(feature = "alloc", feature = "std"))]
233pub fn zeroizing_bytes_from_slice(data: &[u8]) -> ZeroizingBytes {
234 Zeroizing::new(boxed_bytes_from_slice(data))
235}
236
237pub fn secure_zero(data: &mut [u8]) {
243 data.zeroize();
244}
245
246#[cfg(any(feature = "alloc", feature = "std"))]
252pub fn secure_clone_and_zero(data: &mut [u8]) -> Box<[u8]> {
253 let result = boxed_bytes_from_slice(data);
254 secure_zero(data);
255 result
256}
257
258pub struct ZeroGuard<'a>(&'a mut [u8]);
263
264impl<'a> ZeroGuard<'a> {
265 pub fn new(data: &'a mut [u8]) -> Self {
267 Self(data)
268 }
269
270 pub fn data(&self) -> &[u8] {
272 self.0
273 }
274
275 pub fn data_mut(&mut self) -> &mut [u8] {
277 self.0
278 }
279}
280
281impl Drop for ZeroGuard<'_> {
282 fn drop(&mut self) {
283 secure_zero(self.0);
284 }
285}
286
287#[cfg(all(test, any(feature = "alloc", feature = "std")))]
288mod tests {
289 use super::{
290 boxed_bytes_from_slice, boxed_bytes_zeroed, secure_clone_and_zero, Zeroize, Zeroizing,
291 ZeroizingBytes,
292 };
293
294 #[cfg(not(feature = "std"))]
295 use alloc::format;
296
297 #[test]
298 fn boxed_byte_helpers_use_exact_length_storage() {
299 let zeroed = boxed_bytes_zeroed(17);
300 assert_eq!(zeroed.len(), 17);
301 assert!(zeroed.iter().all(|byte| *byte == 0));
302
303 let copied = boxed_bytes_from_slice(&[1, 2, 3, 4]);
304 assert_eq!(&*copied, &[1, 2, 3, 4]);
305 }
306
307 #[test]
308 fn secure_clone_moves_secret_into_box_and_clears_source() {
309 let mut source = [0xA5; 8];
310 let copied = secure_clone_and_zero(&mut source);
311 assert_eq!(&*copied, &[0xA5; 8]);
312 assert_eq!(source, [0; 8]);
313 }
314
315 #[test]
316 fn zeroizing_bytes_can_be_cleared_in_place() {
317 let mut secret = ZeroizingBytes::new(boxed_bytes_from_slice(&[7, 8, 9]));
318 assert_eq!(format!("{secret:?}"), "Zeroizing([REDACTED])");
319 secret.zeroize();
320 assert_eq!(&**secret, &[0, 0, 0]);
321 }
322
323 #[test]
324 fn zeroizing_value_can_be_moved_out_without_copying() {
325 let bytes = ZeroizingBytes::new(boxed_bytes_from_slice(&[1, 2, 3]));
326 let inner = bytes.into_inner();
327 assert_eq!(&*inner, &[1, 2, 3]);
328 }
329
330 #[test]
331 fn arbitrary_length_byte_array_can_be_moved_out_safely() {
332 let bytes = Zeroizing::new([0x5a; 66]);
333 assert_eq!(bytes.into_inner_array(), [0x5a; 66]);
334 }
335}