Skip to main content

kdk_zeroize/
sensitive.rs

1use core::fmt;
2use core::marker::PhantomData;
3use core::sync::atomic;
4
5/// Disciplines yourself. This trait does NOT enforce:
6/// - `Drop` wipes the storage on out-of-scope.
7/// - Redacting `fmt::Debug` impl (never derive).
8/// - No `Clone` / no `Copy`.
9pub trait Sensitive {
10    /// The type whose bytes are the secret material.
11    type Inner: ?Sized;
12
13    /// Explicit-secret-access getter. Mirrors the inherent
14    /// `expose_secret` every sensitive type provides.
15    fn expose_secret(&self) -> &Self::Inner;
16}
17
18/// Read-only sensitive types (computed/derived values like a wrapped
19/// `bip39::Mnemonic`) impl only [`Sensitive`], not this trait —
20/// generic code that requires mutation takes `<S: SensitiveMut>` and
21/// will refuse those at compile time.
22pub trait SensitiveMut: Sensitive {
23    /// Mutable explicit-secret-access getter.
24    fn expose_secret_mut(&mut self) -> &mut Self::Inner;
25}
26
27/// Caller must ensure that after this function returns, **no code
28/// reads `*value` as a `T`**.
29///
30/// # Safety
31///
32/// Zeroing the bytes may produce an invalid `T` (niches, validity invariants
33/// on inner fields, etc.). Typically used inside `Drop`, where Rust's drop
34/// semantics already guarantee no further reads.
35///
36/// WARN: authorship is from [@luisschwab](https://github.com/luisschwab)
37/// while reading a go discord's topic. It's improvised and need auction.
38pub unsafe fn wipe_in_place_mut<T>(value: &mut T) {
39    let ptr = (value as *mut T).cast::<u8>();
40    let len = core::mem::size_of::<T>();
41    for i in 0..len {
42        unsafe {
43            core::ptr::write_volatile(ptr.add(i), 0);
44        }
45        atomic::compiler_fence(atomic::Ordering::SeqCst);
46    }
47}
48
49/// `N` is the byte length and `O` is a chosen zero-sized type that lets the
50/// type system track where the bytes came from (BIP39 seed, dice entropy, AES
51/// key, decrypted KEF payload, etc...).
52///
53/// # Example
54///
55/// ```
56/// use kdk_zeroize::SensitiveBytes;
57///
58/// // A caller-chosen marker type.
59/// pub enum AesKey {}
60///
61/// type AesKey256 = SensitiveBytes<32, AesKey>;
62///
63///
64/// let key = AesKey256::new([0u8; 32]);
65/// ```
66pub struct SensitiveBytes<const N: usize, O> {
67    bytes: [u8; N],
68    _origin: PhantomData<O>,
69}
70
71impl<const N: usize, O> SensitiveBytes<N, O> {
72    /// Wrap a `[u8; N]` as origin-tagged sensitive bytes.
73    pub const fn new(bytes: [u8; N]) -> Self {
74        Self {
75            bytes,
76            _origin: PhantomData,
77        }
78    }
79
80    /// Same bytes as `Sensitive::expose_secret`; the looser `&[u8]`
81    /// return is fine for non-cryptographic reads (e.g. equality
82    /// checks in tests).
83    pub fn as_slice(&self) -> &[u8] {
84        &self.bytes
85    }
86}
87
88impl<const N: usize, O> Drop for SensitiveBytes<N, O> {
89    fn drop(&mut self) {
90        unsafe { wipe_in_place_mut(self) };
91    }
92}
93
94impl<const N: usize, O> fmt::Debug for SensitiveBytes<N, O> {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        f.write_str("SensitiveBytes(REDACTED)")
97    }
98}
99
100// Mirrors the redacted `Debug` so `{}` is also safe; raw `[u8; N]` has
101// no `Display` impl at all, so this is an additive safety surface.
102impl<const N: usize, O> fmt::Display for SensitiveBytes<N, O> {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        f.write_str("SensitiveBytes(REDACTED)")
105    }
106}
107
108impl<const N: usize, O> Sensitive for SensitiveBytes<N, O> {
109    type Inner = [u8; N];
110
111    fn expose_secret(&self) -> &[u8; N] {
112        &self.bytes
113    }
114}
115
116impl<const N: usize, O> SensitiveMut for SensitiveBytes<N, O> {
117    fn expose_secret_mut(&mut self) -> &mut [u8; N] {
118        &mut self.bytes
119    }
120}