Skip to main content

dcrypt_common/security/
secret.rs

1//! Secret data types that invoke zeroization for owned storage
2//!
3//! This module provides type-safe wrappers for sensitive data that ensure
4//! cleanup and zeroization when the data is no longer needed. Software
5//! zeroization cannot erase caller, compiler/register, or already-freed copies.
6
7use core::convert::{AsMut, AsRef};
8use core::fmt;
9use core::ops::{Deref, DerefMut};
10use zeroize::{Zeroize, ZeroizeOnDrop};
11
12// Handle Vec import based on features
13#[cfg(all(feature = "alloc", not(feature = "std")))]
14extern crate alloc;
15
16#[cfg(all(feature = "alloc", not(feature = "std")))]
17use alloc::vec::Vec;
18
19#[cfg(feature = "std")]
20use std::vec::Vec;
21
22/// Trait for types that can be securely zeroed and cloned
23pub trait SecureZeroingType: Zeroize + Clone {
24    /// Create a zeroed instance
25    fn zeroed() -> Self;
26
27    /// Create a secure clone that preserves security properties
28    ///
29    /// This method ensures that cloned instances maintain the same
30    /// security guarantees as the original, including proper zeroization.
31    fn secure_clone(&self) -> Self {
32        self.clone() // Default implementation uses regular clone
33    }
34}
35
36/// Fixed-size secret buffer that guarantees zeroization
37///
38/// This type provides:
39/// - Automatic zeroization on drop
40/// - Secure cloning that preserves security properties
41/// - Type-safe size guarantees at compile time
42#[derive(Clone, Zeroize, ZeroizeOnDrop)]
43pub struct SecretBuffer<const N: usize> {
44    data: [u8; N],
45}
46
47impl<const N: usize> SecretBuffer<N> {
48    /// Create a new secret buffer with the given data
49    pub fn new(data: [u8; N]) -> Self {
50        Self { data }
51    }
52
53    /// Create a zeroed secret buffer
54    pub fn zeroed() -> Self {
55        Self { data: [0u8; N] }
56    }
57
58    /// Get the length of the buffer
59    pub fn len(&self) -> usize {
60        N
61    }
62
63    /// Check if the buffer is empty (always false for non-zero N)
64    pub fn is_empty(&self) -> bool {
65        N == 0
66    }
67
68    /// Get a reference to the inner data
69    pub fn as_slice(&self) -> &[u8] {
70        &self.data
71    }
72
73    /// Get a mutable reference to the inner data
74    pub fn as_mut_slice(&mut self) -> &mut [u8] {
75        &mut self.data
76    }
77}
78
79impl<const N: usize> SecureZeroingType for SecretBuffer<N> {
80    fn zeroed() -> Self {
81        Self::zeroed()
82    }
83
84    fn secure_clone(&self) -> Self {
85        Self::new(self.data) // Fixed: removed .clone() since [u8; N] implements Copy
86    }
87}
88
89impl<const N: usize> AsRef<[u8]> for SecretBuffer<N> {
90    fn as_ref(&self) -> &[u8] {
91        &self.data
92    }
93}
94
95impl<const N: usize> AsMut<[u8]> for SecretBuffer<N> {
96    fn as_mut(&mut self) -> &mut [u8] {
97        &mut self.data
98    }
99}
100
101impl<const N: usize> fmt::Debug for SecretBuffer<N> {
102    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103        write!(f, "SecretBuffer<{}>([REDACTED])", N)
104    }
105}
106
107/// Variable-size secret vector that wipes storage it owns
108///
109/// This type provides:
110/// - Automatic zeroization on drop
111/// - Secure cloning that preserves security properties
112/// - Dynamic sizing with secure memory management
113#[cfg(feature = "alloc")]
114#[derive(Zeroize, ZeroizeOnDrop)]
115pub struct SecretVec {
116    data: Vec<u8>,
117}
118
119#[cfg(feature = "alloc")]
120impl SecretVec {
121    /// Create a new secret vector with the given data.
122    ///
123    /// The current allocation and its spare capacity become protected by this
124    /// type. Allocations the caller freed before passing this `Vec` cannot be
125    /// recovered or retroactively wiped.
126    pub fn new(mut data: Vec<u8>) -> Self {
127        // A Vec supplied by a caller may previously have been truncated. Wipe
128        // its unused allocation before accepting responsibility for it.
129        Self::zeroize_spare_capacity(&mut data);
130        Self { data }
131    }
132
133    /// Create a secret vector from a slice
134    pub fn from_slice(slice: &[u8]) -> Self {
135        Self::new(slice.to_vec())
136    }
137
138    /// Create an empty secret vector
139    pub fn empty() -> Self {
140        Self { data: Vec::new() }
141    }
142
143    /// Create a secret vector with the specified capacity
144    pub fn with_capacity(capacity: usize) -> Self {
145        let mut data = Vec::with_capacity(capacity);
146        Self::zeroize_spare_capacity(&mut data);
147        Self { data }
148    }
149
150    /// Get the length of the vector
151    pub fn len(&self) -> usize {
152        self.data.len()
153    }
154
155    /// Check if the vector is empty
156    pub fn is_empty(&self) -> bool {
157        self.data.is_empty()
158    }
159
160    /// Get a reference to the inner data
161    pub fn as_slice(&self) -> &[u8] {
162        &self.data
163    }
164
165    /// Get a mutable reference to the inner data
166    pub fn as_mut_slice(&mut self) -> &mut [u8] {
167        &mut self.data
168    }
169
170    /// Get the allocation capacity.
171    pub fn capacity(&self) -> usize {
172        self.data.capacity()
173    }
174
175    /// Extend the vector with additional data
176    pub fn extend_from_slice(&mut self, slice: &[u8]) {
177        self.ensure_additional_capacity(slice.len());
178        self.data.extend_from_slice(slice);
179    }
180
181    /// Resize the vector to the specified length
182    pub fn resize(&mut self, new_len: usize, value: u8) {
183        if new_len <= self.data.len() {
184            self.truncate(new_len);
185            return;
186        }
187
188        self.ensure_additional_capacity(new_len - self.data.len());
189        self.data.resize(new_len, value);
190    }
191
192    /// Truncate the vector to the specified length
193    pub fn truncate(&mut self, len: usize) {
194        if len >= self.data.len() {
195            return;
196        }
197
198        self.data[len..].zeroize();
199        self.data.truncate(len);
200    }
201
202    /// Remove all bytes while retaining a fully zeroed allocation.
203    pub fn clear(&mut self) {
204        self.data.zeroize();
205    }
206
207    /// Append one byte, securely replacing the allocation when it is full.
208    pub fn push(&mut self, value: u8) {
209        self.ensure_additional_capacity(1);
210        self.data.push(value);
211    }
212
213    /// Remove and return the last byte, wiping its allocation slot first.
214    pub fn pop(&mut self) -> Option<u8> {
215        let value = self.data.last().copied()?;
216        let new_len = self.data.len() - 1;
217        self.data[new_len].zeroize();
218        self.data.truncate(new_len);
219        Some(value)
220    }
221
222    /// Reserve room for at least `additional` more bytes.
223    ///
224    /// Unlike `Vec::reserve`, this never asks the allocator to resize the live
225    /// secret allocation. It copies into a new allocation and wipes the old
226    /// allocation before releasing it.
227    pub fn reserve(&mut self, additional: usize) {
228        self.ensure_additional_capacity(additional);
229    }
230
231    /// Reduce capacity to the current length while wiping the old allocation.
232    pub fn shrink_to_fit(&mut self) {
233        if self.data.capacity() > self.data.len() {
234            self.secure_reallocate(self.data.len());
235        }
236    }
237
238    fn ensure_additional_capacity(&mut self, additional: usize) {
239        let required = self
240            .data
241            .len()
242            .checked_add(additional)
243            .expect("SecretVec capacity overflow");
244
245        if required > self.data.capacity() {
246            self.secure_reallocate(required);
247        }
248    }
249
250    fn secure_reallocate(&mut self, capacity: usize) {
251        debug_assert!(capacity >= self.data.len());
252
253        let mut replacement = Vec::with_capacity(capacity);
254        replacement.extend_from_slice(&self.data);
255        Self::zeroize_spare_capacity(&mut replacement);
256
257        // zeroize() wipes both initialized elements and the entire spare
258        // capacity before clearing the Vec. Only then is the allocation freed.
259        self.data.zeroize();
260        #[cfg(test)]
261        assert!(Self::allocation_is_zeroed(&self.data));
262        self.data = replacement;
263    }
264
265    fn zeroize_spare_capacity(data: &mut Vec<u8>) {
266        data.spare_capacity_mut().zeroize();
267    }
268
269    #[cfg(test)]
270    fn allocation_is_zeroed(data: &Vec<u8>) -> bool {
271        // Callers use this only after Vec::zeroize(), which initializes the
272        // entire allocation with zero bytes and sets len to zero.
273        let allocation = unsafe { core::slice::from_raw_parts(data.as_ptr(), data.capacity()) };
274        allocation.iter().all(|byte| *byte == 0)
275    }
276}
277
278#[cfg(feature = "alloc")]
279impl Clone for SecretVec {
280    fn clone(&self) -> Self {
281        Self::from_slice(&self.data)
282    }
283}
284
285#[cfg(feature = "alloc")]
286impl SecureZeroingType for SecretVec {
287    fn zeroed() -> Self {
288        Self::empty()
289    }
290
291    fn secure_clone(&self) -> Self {
292        self.clone()
293    }
294}
295
296#[cfg(feature = "alloc")]
297impl AsRef<[u8]> for SecretVec {
298    fn as_ref(&self) -> &[u8] {
299        &self.data
300    }
301}
302
303#[cfg(feature = "alloc")]
304impl AsMut<[u8]> for SecretVec {
305    fn as_mut(&mut self) -> &mut [u8] {
306        &mut self.data
307    }
308}
309
310#[cfg(feature = "alloc")]
311impl From<Vec<u8>> for SecretVec {
312    fn from(data: Vec<u8>) -> Self {
313        Self::new(data)
314    }
315}
316
317#[cfg(feature = "alloc")]
318impl fmt::Debug for SecretVec {
319    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
320        write!(f, "SecretVec(len={}, [REDACTED])", self.data.len())
321    }
322}
323
324/// Ephemeral secret that is automatically zeroized after use
325///
326/// This type wraps any type T and ensures it is zeroized when dropped.
327/// It's useful for temporary secrets and intermediate cryptographic values.
328pub struct EphemeralSecret<T: Zeroize> {
329    inner: T,
330}
331
332impl<T: Zeroize> EphemeralSecret<T> {
333    /// Create a new ephemeral secret
334    pub fn new(value: T) -> Self {
335        Self { inner: value }
336    }
337
338    /// Consume the secret and return the inner value
339    ///
340    /// Note: After calling this method, the caller is responsible
341    /// for ensuring the value is properly zeroized.
342    pub fn into_inner(self) -> T {
343        let this = core::mem::ManuallyDrop::new(self);
344        unsafe { core::ptr::read(&this.inner) }
345    }
346}
347
348// Fixed: Implement actual AsRef and AsMut traits instead of methods
349impl<T: Zeroize> AsRef<T> for EphemeralSecret<T> {
350    fn as_ref(&self) -> &T {
351        &self.inner
352    }
353}
354
355impl<T: Zeroize> AsMut<T> for EphemeralSecret<T> {
356    fn as_mut(&mut self) -> &mut T {
357        &mut self.inner
358    }
359}
360
361impl<T: Zeroize> Drop for EphemeralSecret<T> {
362    fn drop(&mut self) {
363        self.inner.zeroize();
364    }
365}
366
367impl<T: Zeroize + Clone> Clone for EphemeralSecret<T> {
368    fn clone(&self) -> Self {
369        Self::new(self.inner.clone())
370    }
371}
372
373impl<T: Zeroize + Default> Default for EphemeralSecret<T> {
374    fn default() -> Self {
375        Self::new(T::default())
376    }
377}
378
379impl<T: Zeroize> Deref for EphemeralSecret<T> {
380    type Target = T;
381
382    fn deref(&self) -> &Self::Target {
383        &self.inner
384    }
385}
386
387impl<T: Zeroize> DerefMut for EphemeralSecret<T> {
388    fn deref_mut(&mut self) -> &mut Self::Target {
389        &mut self.inner
390    }
391}
392
393impl<T: Zeroize + fmt::Debug> fmt::Debug for EphemeralSecret<T> {
394    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
395        write!(f, "EphemeralSecret([REDACTED])")
396    }
397}
398
399/// Guard type that ensures a value is zeroized when dropped
400///
401/// This is useful for ensuring cleanup happens even in the presence
402/// of early returns or panics.
403pub struct ZeroizeGuard<'a, T: Zeroize> {
404    value: &'a mut T,
405}
406
407impl<'a, T: Zeroize> ZeroizeGuard<'a, T> {
408    /// Create a new zeroize guard for the given value
409    pub fn new(value: &'a mut T) -> Self {
410        Self { value }
411    }
412}
413
414// Fixed: Use lifetime elision instead of explicit lifetimes
415impl<T: Zeroize> Drop for ZeroizeGuard<'_, T> {
416    fn drop(&mut self) {
417        self.value.zeroize();
418    }
419}
420
421impl<T: Zeroize> Deref for ZeroizeGuard<'_, T> {
422    type Target = T;
423
424    fn deref(&self) -> &Self::Target {
425        self.value
426    }
427}
428
429impl<T: Zeroize> DerefMut for ZeroizeGuard<'_, T> {
430    fn deref_mut(&mut self) -> &mut Self::Target {
431        self.value
432    }
433}
434
435#[cfg(test)]
436mod tests {
437    use super::*;
438
439    #[cfg(all(not(feature = "std"), feature = "alloc"))]
440    use alloc::vec;
441
442    #[test]
443    fn test_secret_buffer_basic() {
444        let mut buffer = SecretBuffer::<32>::new([42u8; 32]);
445        assert_eq!(buffer.len(), 32);
446        assert_eq!(buffer.as_slice()[0], 42);
447
448        // Test mutation
449        buffer.as_mut_slice()[0] = 1;
450        assert_eq!(buffer.as_slice()[0], 1);
451    }
452
453    #[test]
454    fn test_secret_buffer_secure_clone() {
455        let buffer = SecretBuffer::<16>::new([0xAA; 16]);
456        let cloned = buffer.secure_clone();
457        assert_eq!(cloned.as_slice(), buffer.as_slice());
458    }
459
460    #[test]
461    fn test_secret_buffer_zeroed() {
462        let zeroed = SecretBuffer::<32>::zeroed();
463        assert_eq!(zeroed.as_slice(), &[0u8; 32]);
464    }
465
466    #[cfg(feature = "alloc")]
467    #[test]
468    fn test_secret_vec_operations() {
469        let mut vec = SecretVec::from_slice(&[1, 2, 3, 4]);
470        assert_eq!(vec.len(), 4);
471        assert_eq!(vec.as_slice(), &[1, 2, 3, 4]);
472
473        // Test extend
474        vec.extend_from_slice(&[5, 6]);
475        assert_eq!(vec.as_slice(), &[1, 2, 3, 4, 5, 6]);
476
477        // Test truncate
478        vec.truncate(3);
479        assert_eq!(vec.as_slice(), &[1, 2, 3]);
480
481        // Test resize
482        vec.resize(5, 0xFF);
483        assert_eq!(vec.as_slice(), &[1, 2, 3, 0xFF, 0xFF]);
484    }
485
486    #[cfg(feature = "alloc")]
487    fn assert_secret_vec_spare_capacity_is_zero(vec: &SecretVec) {
488        // SecretVec initializes every spare-capacity byte on construction and
489        // after each operation that can change the allocation.
490        let spare = unsafe {
491            core::slice::from_raw_parts(
492                vec.data.as_ptr().add(vec.data.len()),
493                vec.data.capacity() - vec.data.len(),
494            )
495        };
496        assert!(spare.iter().all(|byte| *byte == 0));
497    }
498
499    #[cfg(feature = "alloc")]
500    #[test]
501    fn secret_vec_wipes_preexisting_unused_capacity() {
502        let mut raw = vec![0xA5; 64];
503        raw.truncate(4);
504        let secret = SecretVec::new(raw);
505
506        assert_eq!(secret.as_slice(), &[0xA5; 4]);
507        assert_secret_vec_spare_capacity_is_zero(&secret);
508    }
509
510    #[cfg(feature = "alloc")]
511    #[test]
512    fn secret_vec_wipes_bytes_removed_by_truncate_resize_clear_and_pop() {
513        let mut secret = SecretVec::from_slice(&[1, 2, 3, 4, 5, 6]);
514
515        secret.truncate(4);
516        assert_eq!(secret.as_slice(), &[1, 2, 3, 4]);
517        assert_secret_vec_spare_capacity_is_zero(&secret);
518
519        secret.resize(2, 0xFF);
520        assert_eq!(secret.as_slice(), &[1, 2]);
521        assert_secret_vec_spare_capacity_is_zero(&secret);
522
523        assert_eq!(secret.pop(), Some(2));
524        assert_eq!(secret.as_slice(), &[1]);
525        assert_secret_vec_spare_capacity_is_zero(&secret);
526
527        secret.clear();
528        assert!(secret.is_empty());
529        assert_secret_vec_spare_capacity_is_zero(&secret);
530    }
531
532    #[cfg(feature = "alloc")]
533    #[test]
534    fn secret_vec_growth_and_shrink_replace_allocations_securely() {
535        let mut raw = Vec::with_capacity(4);
536        raw.extend_from_slice(&[0x11; 4]);
537        let mut secret = SecretVec::new(raw);
538        let initial_capacity = secret.capacity();
539
540        // This exercises secure_reallocate; its test assertion observes the
541        // old allocation after zeroization and before deallocation.
542        secret.extend_from_slice(&[0x22, 0x33]);
543        assert!(secret.capacity() > initial_capacity);
544        assert_eq!(secret.as_slice(), &[0x11, 0x11, 0x11, 0x11, 0x22, 0x33]);
545        assert_secret_vec_spare_capacity_is_zero(&secret);
546
547        secret.reserve(32);
548        assert!(secret.capacity() >= secret.len() + 32);
549        assert_secret_vec_spare_capacity_is_zero(&secret);
550
551        secret.shrink_to_fit();
552        assert_eq!(secret.capacity(), secret.len());
553        assert_eq!(secret.as_slice(), &[0x11, 0x11, 0x11, 0x11, 0x22, 0x33]);
554    }
555
556    #[test]
557    fn test_ephemeral_secret() {
558        #[derive(Clone, Zeroize)]
559        struct TestSecret(u64);
560
561        let secret = EphemeralSecret::new(TestSecret(42));
562        assert_eq!(secret.0, 42);
563
564        // Test deref
565        let value = secret.0;
566        assert_eq!(value, 42);
567
568        // Test clone
569        let cloned = secret.clone();
570        assert_eq!(cloned.0, 42);
571
572        // Test into_inner
573        let inner = secret.into_inner();
574        assert_eq!(inner.0, 42);
575    }
576
577    #[test]
578    fn test_zeroize_guard() {
579        let mut value = vec![1u8, 2, 3, 4];
580        {
581            let guard = ZeroizeGuard::new(&mut value);
582            // Simulate work with the value
583            assert_eq!(&**guard, &[1, 2, 3, 4]);
584        }
585        // Guard should have zeroized the value (which clears the Vec)
586        assert!(value.is_empty());
587    }
588}