@@ -2,26 +2,26 @@ use core::fmt;
22use core:: marker:: PhantomData ;
33use core:: sync:: atomic;
44
5- /// `N` is the byte length and `O` is a chosen zero-sized type that lets the
6- /// type system track where the bytes came from (BIP39 seed, dice entropy, AES
7- /// key, decrypted KEF payload, etc... ).
8- ///
9- /// # Example
10- ///
11- /// ```
12- /// use kdk_zeroize::SensitiveBytes;
13- ///
14- /// // A caller-chosen marker type.
15- /// pub enum AesKey {}
16- ///
17- /// type AesKey256 = SensitiveBytes<32, AesKey>;
18- ///
19- ///
20- /// let key = AesKey256::new([0u8; 32]);
21- /// ```
22- pub struct SensitiveBytes < const N : usize , O > {
23- bytes : [ u8 ; N ] ,
24- _origin : PhantomData < O > ,
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`.
9+ pub 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.
22+ pub trait SensitiveMut : Sensitive {
23+ /// Mutable explicit-secret-access getter.
24+ fn expose_secret_mut ( & mut self ) -> & mut Self :: Inner ;
2525}
2626
2727/// Caller must ensure that after this function returns, **no code
@@ -46,6 +46,28 @@ pub unsafe fn wipe_in_place_mut<T>(value: &mut T) {
4646 }
4747}
4848
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+ /// ```
66+ pub struct SensitiveBytes < const N : usize , O > {
67+ bytes : [ u8 ; N ] ,
68+ _origin : PhantomData < O > ,
69+ }
70+
4971impl < const N : usize , O > SensitiveBytes < N , O > {
5072 /// Wrap a `[u8; N]` as origin-tagged sensitive bytes.
5173 pub const fn new ( bytes : [ u8 ; N ] ) -> Self {
@@ -55,20 +77,9 @@ impl<const N: usize, O> SensitiveBytes<N, O> {
5577 }
5678 }
5779
58- /// Explicit-secret-access getter. Prefer this over `as_slice`
59- /// when the bytes are about to be fed into a cryptographic
60- /// primitive — the name makes the secrecy visible in diffs.
61- pub fn expose_secret ( & self ) -> & [ u8 ; N ] {
62- & self . bytes
63- }
64-
65- /// Mutable explicit-secret-access getter
66- pub fn expose_secret_mut ( & mut self ) -> & mut [ u8 ; N ] {
67- & mut self . bytes
68- }
69-
70- /// Same bytes as `expose_secret`; the looser name is fine for
71- /// non-cryptographic reads (e.g. equality checks in tests).
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).
7283 pub fn as_slice ( & self ) -> & [ u8 ] {
7384 & self . bytes
7485 }
@@ -85,3 +96,17 @@ impl<const N: usize, O> fmt::Debug for SensitiveBytes<N, O> {
8596 f. write_str ( "SensitiveBytes(REDACTED)" )
8697 }
8798}
99+
100+ impl < const N : usize , O > Sensitive for SensitiveBytes < N , O > {
101+ type Inner = [ u8 ; N ] ;
102+
103+ fn expose_secret ( & self ) -> & [ u8 ; N ] {
104+ & self . bytes
105+ }
106+ }
107+
108+ impl < const N : usize , O > SensitiveMut for SensitiveBytes < N , O > {
109+ fn expose_secret_mut ( & mut self ) -> & mut [ u8 ; N ] {
110+ & mut self . bytes
111+ }
112+ }
0 commit comments