secure_gate/
zeroize.rs

1// src/zeroize.rs — FIXED VERSION with newtype for DynamicZeroizing
2// Changes:
3// - Added T: Zeroize bound on struct definition to enforce at compile time
4// - Added T: DefaultIsZeroes to ?Sized impls where required for Zeroize on unsized types
5// - Fixed ExposeSecret impl to use generic S (matches secrecy's trait definition)
6// - Implemented redacted Debug manually (avoids derive issues with bounds)
7// - Kept From impls with proper bounds
8
9#[cfg(feature = "zeroize")]
10use zeroize::{DefaultIsZeroes, Zeroize, ZeroizeOnDrop, Zeroizing};
11
12#[cfg(feature = "zeroize")]
13use secrecy::{ExposeSecret, SecretBox};
14
15#[cfg(feature = "zeroize")]
16pub type FixedZeroizing<T> = Zeroizing<T>;
17
18// NEWTYPE: Wrap SecretBox to own the type and impl foreign traits safely
19#[cfg(feature = "zeroize")]
20pub struct DynamicZeroizing<T: ?Sized + Zeroize>(SecretBox<T>);
21
22#[cfg(feature = "zeroize")]
23impl<T: ?Sized + Zeroize> DynamicZeroizing<T> {
24    #[inline(always)]
25    pub fn new(value: Box<T>) -> Self {
26        Self(SecretBox::new(value))
27    }
28}
29
30// Redacted Debug (manual impl to avoid bound issues)
31#[cfg(feature = "zeroize")]
32impl<T: ?Sized + Zeroize> core::fmt::Debug for DynamicZeroizing<T> {
33    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
34        f.write_str("[REDACTED]")
35    }
36}
37
38// Forward ExposeSecret (fixed with generic S)
39#[cfg(feature = "zeroize")]
40impl<S: ?Sized + Zeroize> ExposeSecret<S> for DynamicZeroizing<S> {
41    #[inline(always)]
42    fn expose_secret(&self) -> &S {
43        self.0.expose_secret()
44    }
45}
46
47// Forward Zeroize
48#[cfg(feature = "zeroize")]
49impl<T: Zeroize + DefaultIsZeroes> Zeroize for DynamicZeroizing<T> {
50    fn zeroize(&mut self) {
51        self.0.zeroize();
52    }
53}
54
55// Forward ZeroizeOnDrop (no additional bounds needed)
56#[cfg(feature = "zeroize")]
57impl<T: ?Sized + Zeroize> ZeroizeOnDrop for DynamicZeroizing<T> {}
58
59// Conversions from non-zeroizing wrappers
60#[cfg(feature = "zeroize")]
61impl<T: Zeroize> From<crate::Fixed<T>> for FixedZeroizing<T> {
62    #[inline(always)]
63    fn from(fixed: crate::Fixed<T>) -> Self {
64        Zeroizing::new(fixed.0)
65    }
66}
67
68#[cfg(feature = "zeroize")]
69impl<T: ?Sized + Zeroize> From<crate::Dynamic<T>> for DynamicZeroizing<T> {
70    #[inline(always)]
71    fn from(dynamic: crate::Dynamic<T>) -> Self {
72        Self(SecretBox::new(dynamic.0))
73    }
74}
75
76// Zeroize impls for non-zeroizing wrappers
77#[cfg(feature = "zeroize")]
78impl<T: Zeroize> Zeroize for crate::Fixed<T> {
79    fn zeroize(&mut self) {
80        self.0.zeroize();
81    }
82}
83
84#[cfg(feature = "zeroize")]
85impl<T: Zeroize + DefaultIsZeroes> Zeroize for crate::Dynamic<T> {
86    fn zeroize(&mut self) {
87        self.0.zeroize();
88    }
89}
90
91#[cfg(feature = "zeroize")]
92impl<T: Zeroize> ZeroizeOnDrop for crate::Fixed<T> {}
93
94#[cfg(feature = "zeroize")]
95impl<T: ?Sized + Zeroize> ZeroizeOnDrop for crate::Dynamic<T> {}
96
97// ————————————————————————————————————————————————————————————————
98// Ergonomics: .into() support
99// ————————————————————————————————————————————————————————————————
100
101#[cfg(feature = "zeroize")]
102impl<T: Zeroize + Send + 'static> From<T> for DynamicZeroizing<T> {
103    #[inline(always)]
104    fn from(value: T) -> Self {
105        Self::new(Box::new(value))
106    }
107}
108
109#[cfg(feature = "zeroize")]
110impl<T: Zeroize + DefaultIsZeroes + Send + 'static> From<Box<T>> for DynamicZeroizing<T> {
111    #[inline(always)]
112    fn from(boxed: Box<T>) -> Self {
113        Self::new(boxed)
114    }
115}
116
117#[cfg(feature = "zeroize")]
118impl From<&str> for DynamicZeroizing<String> {
119    #[inline(always)]
120    fn from(s: &str) -> Self {
121        Self::new(Box::new(s.to_string()))
122    }
123}