dcrypt_common/security/
secret.rs1use core::convert::{AsMut, AsRef};
8use core::fmt;
9use core::ops::{Deref, DerefMut};
10use dcrypt_internal::zeroing::Zeroize;
11
12pub use dcrypt_api::types::SecretBytes as SecretBuffer;
13#[cfg(feature = "alloc")]
14pub use dcrypt_api::types::SecretVec;
15
16pub trait SecureZeroingType: Zeroize + Clone {
19 fn zeroed() -> Self;
21
22 fn secure_clone(&self) -> Self {
26 self.clone() }
28}
29
30impl<const N: usize> SecureZeroingType for SecretBuffer<N> {
31 fn zeroed() -> Self {
32 Self::zeroed()
33 }
34
35 fn secure_clone(&self) -> Self {
36 self.clone()
37 }
38}
39
40#[cfg(feature = "alloc")]
41impl SecureZeroingType for SecretVec {
42 fn zeroed() -> Self {
43 Self::empty()
44 }
45
46 fn secure_clone(&self) -> Self {
47 self.clone()
48 }
49}
50
51pub struct EphemeralSecret<T: Zeroize> {
57 inner: Option<T>,
58}
59
60impl<T: Zeroize> EphemeralSecret<T> {
61 pub fn new(value: T) -> Self {
63 Self { inner: Some(value) }
64 }
65
66 pub fn into_inner(self) -> T {
71 let mut this = self;
72 this.inner
73 .take()
74 .expect("EphemeralSecret value was already consumed")
75 }
76}
77
78impl<T: Zeroize> AsRef<T> for EphemeralSecret<T> {
80 fn as_ref(&self) -> &T {
81 self.inner
82 .as_ref()
83 .expect("EphemeralSecret value was already consumed")
84 }
85}
86
87impl<T: Zeroize> AsMut<T> for EphemeralSecret<T> {
88 fn as_mut(&mut self) -> &mut T {
89 self.inner
90 .as_mut()
91 .expect("EphemeralSecret value was already consumed")
92 }
93}
94
95impl<T: Zeroize> Drop for EphemeralSecret<T> {
96 fn drop(&mut self) {
97 if let Some(inner) = self.inner.as_mut() {
98 inner.zeroize();
99 }
100 }
101}
102
103impl<T: Zeroize + Clone> Clone for EphemeralSecret<T> {
104 fn clone(&self) -> Self {
105 Self::new(
106 self.inner
107 .as_ref()
108 .expect("EphemeralSecret value was already consumed")
109 .clone(),
110 )
111 }
112}
113
114impl<T: Zeroize + Default> Default for EphemeralSecret<T> {
115 fn default() -> Self {
116 Self::new(T::default())
117 }
118}
119
120impl<T: Zeroize> Deref for EphemeralSecret<T> {
121 type Target = T;
122
123 fn deref(&self) -> &Self::Target {
124 self.as_ref()
125 }
126}
127
128impl<T: Zeroize> DerefMut for EphemeralSecret<T> {
129 fn deref_mut(&mut self) -> &mut Self::Target {
130 self.as_mut()
131 }
132}
133
134impl<T: Zeroize> fmt::Debug for EphemeralSecret<T> {
135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136 write!(f, "EphemeralSecret([REDACTED])")
137 }
138}
139
140pub struct ZeroizeGuard<'a, T: Zeroize> {
145 value: &'a mut T,
146}
147
148impl<'a, T: Zeroize> ZeroizeGuard<'a, T> {
149 pub fn new(value: &'a mut T) -> Self {
151 Self { value }
152 }
153}
154
155impl<T: Zeroize> Drop for ZeroizeGuard<'_, T> {
157 fn drop(&mut self) {
158 self.value.zeroize();
159 }
160}
161
162impl<T: Zeroize> Deref for ZeroizeGuard<'_, T> {
163 type Target = T;
164
165 fn deref(&self) -> &Self::Target {
166 self.value
167 }
168}
169
170impl<T: Zeroize> DerefMut for ZeroizeGuard<'_, T> {
171 fn deref_mut(&mut self) -> &mut Self::Target {
172 self.value
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 #[test]
181 fn test_secret_buffer_basic() {
182 let mut buffer = SecretBuffer::<32>::new([42u8; 32]);
183 assert_eq!(buffer.len(), 32);
184 assert_eq!(buffer.as_slice()[0], 42);
185
186 buffer.as_mut_slice()[0] = 1;
188 assert_eq!(buffer.as_slice()[0], 1);
189 }
190
191 #[test]
192 fn test_secret_buffer_secure_clone() {
193 let buffer = SecretBuffer::<16>::new([0xAA; 16]);
194 let cloned = buffer.secure_clone();
195 assert_eq!(cloned.as_slice(), buffer.as_slice());
196 }
197
198 #[test]
199 fn test_secret_buffer_zeroed() {
200 let zeroed = SecretBuffer::<32>::zeroed();
201 assert_eq!(zeroed.as_slice(), &[0u8; 32]);
202 }
203
204 #[cfg(feature = "alloc")]
205 #[test]
206 fn test_secret_vec_operations() {
207 let mut vec = SecretVec::from_slice(&[1, 2, 3, 4]);
208 assert_eq!(vec.len(), 4);
209 assert_eq!(vec.as_slice(), &[1, 2, 3, 4]);
210
211 vec.extend_from_slice(&[5, 6]);
213 assert_eq!(vec.as_slice(), &[1, 2, 3, 4, 5, 6]);
214
215 vec.truncate(3);
217 assert_eq!(vec.as_slice(), &[1, 2, 3]);
218
219 vec.resize(5, 0xFF);
221 assert_eq!(vec.as_slice(), &[1, 2, 3, 0xFF, 0xFF]);
222 }
223
224 #[cfg(feature = "alloc")]
225 #[test]
226 fn secret_vec_uses_exact_size_storage() {
227 let secret = SecretVec::from_slice(&[0xA5; 4]);
228
229 assert_eq!(secret.as_slice(), &[0xA5; 4]);
230 assert_eq!(secret.capacity(), secret.len());
231 }
232
233 #[cfg(feature = "alloc")]
234 #[test]
235 fn secret_vec_wipes_bytes_removed_by_truncate_resize_clear_and_pop() {
236 let mut secret = SecretVec::from_slice(&[1, 2, 3, 4, 5, 6]);
237
238 secret.truncate(4);
239 assert_eq!(secret.as_slice(), &[1, 2, 3, 4]);
240 assert_eq!(secret.capacity(), secret.len());
241
242 secret.resize(2, 0xFF);
243 assert_eq!(secret.as_slice(), &[1, 2]);
244 assert_eq!(secret.capacity(), secret.len());
245
246 assert_eq!(secret.pop(), Some(2));
247 assert_eq!(secret.as_slice(), &[1]);
248 assert_eq!(secret.capacity(), secret.len());
249
250 secret.clear();
251 assert!(secret.is_empty());
252 assert_eq!(secret.capacity(), secret.len());
253 }
254
255 #[cfg(feature = "alloc")]
256 #[test]
257 fn secret_vec_growth_and_shrink_replace_allocations_securely() {
258 let mut secret = SecretVec::from_slice(&[0x11; 4]);
259 secret.extend_from_slice(&[0x22, 0x33]);
260 assert_eq!(secret.capacity(), 6);
261 assert_eq!(secret.as_slice(), &[0x11, 0x11, 0x11, 0x11, 0x22, 0x33]);
262 }
263
264 #[test]
265 fn test_ephemeral_secret() {
266 #[derive(Clone)]
267 struct TestSecret(u64);
268
269 impl Zeroize for TestSecret {
270 fn zeroize(&mut self) {
271 self.0.zeroize();
272 }
273 }
274
275 let secret = EphemeralSecret::new(TestSecret(42));
276 assert_eq!(secret.0, 42);
277
278 let value = secret.0;
280 assert_eq!(value, 42);
281
282 let cloned = secret.clone();
284 assert_eq!(cloned.0, 42);
285
286 let inner = secret.into_inner();
288 assert_eq!(inner.0, 42);
289 }
290
291 #[test]
292 fn test_zeroize_guard() {
293 let mut value = [1u8, 2, 3, 4];
294 {
295 let guard = ZeroizeGuard::new(&mut value);
296 assert_eq!(&*guard, &[1, 2, 3, 4]);
298 }
299 assert_eq!(value, [0; 4]);
300 }
301}