1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! Owned zeroizing wrapper for secrets extracted via [`RevealSecret::into_inner`].
//!
//! This is part of the `revealed_secrets` module. See `mod.rs` for overview
//! and `SECURITY.md` for the 3-tier access model and audit guidance.
/// Owned, zeroizing secret extracted via [`RevealSecret::into_inner`].
///
/// `InnerSecret<T>` preserves the zeroization contract by wrapping
/// [`zeroize::Zeroizing<T>`], while restoring a strict redaction policy for `Debug`:
/// formatting this type always prints `[REDACTED]`, regardless of `T`.
///
/// This is **not** a secret wrapper like [`Fixed`](crate::Fixed) or
/// [`Dynamic`](crate::Dynamic) — it is the owned extraction result from
/// [`into_inner()`](crate::RevealSecret::into_inner), and it implements
/// `Deref<Target = T>` by design. `Fixed` and `Dynamic` deliberately do not deref;
/// the other output wrapper, [`EncodedSecret`](crate::EncodedSecret), derefs to `str`.
///
/// # What this type does and does not protect
///
/// It zeroizes the buffer it owns on drop, and its `Debug` prints `[REDACTED]`. It does
/// **not** track copies made through `Deref`: `*inner` on a `Copy` type such as
/// `[u8; N]`, or `.to_owned()` on the derefed value, yields ordinary untracked
/// plaintext. That is the point of extraction — see
/// [Where accident-prevention ends](crate#where-accident-prevention-ends).
///
/// Note also that `Debug` redaction does not survive a deref:
/// `format!("{:?}", inner)` prints `[REDACTED]`, but `format!("{:?}", &*inner)` prints
/// the secret. Redaction is a property of this wrapper, not of `T`.
///
/// Use [`into_zeroizing`](Self::into_zeroizing) only when an API explicitly requires
/// a `Zeroizing<T>` value.
///
/// # Examples
///
/// ```rust
/// use secure_gate::{Fixed, RevealSecret};
///
/// let key = Fixed::new([0xABu8; 4]);
/// let owned = key.into_inner();
///
/// // Deref access to the inner value.
/// assert_eq!(owned[0], 0xAB);
///
/// // Debug is redacted.
/// assert_eq!(format!("{:?}", owned), "[REDACTED]");
///
/// // Convert to Zeroizing<T> for interop.
/// let z = owned.into_zeroizing();
/// ```
///
/// See also [`EncodedSecret`](crate::EncodedSecret) — the encoded-string counterpart.
;
/// Clones the extracted secret, keeping it wrapped.
///
/// This impl exists to close a method-resolution hole rather than for convenience.
/// Without it, `inner.clone()` does not fail to compile — it autoderefs and resolves to
/// `T::clone`, silently producing a bare, unprotected `T` (a plain `String`, `Vec<u8>`,
/// etc.) from a call site that names no exit. With it, `inner.clone()` resolves here and
/// the result is another `InnerSecret<T>`: independently owned, and independently
/// zeroized on drop.
///
/// This is deliberately **not** gated on [`CloneableSecret`](crate::CloneableSecret).
/// That marker gates cloning a live [`Fixed`](crate::Fixed) / [`Dynamic`](crate::Dynamic)
/// wrapper. An `InnerSecret` is already past the named extraction, so gating here would
/// buy no protection — it would only re-open the silent `T::clone` fallthrough for
/// inner types that lack the marker.
///
/// ```rust
/// use secure_gate::{Fixed, InnerSecret, RevealSecret};
///
/// let owned: InnerSecret<[u8; 4]> = Fixed::new([0xABu8; 4]).into_inner();
/// let copy: InnerSecret<[u8; 4]> = owned.clone(); // stays wrapped, not a bare [u8; 4]
/// assert_eq!(format!("{copy:?}"), "[REDACTED]");
/// ```
///
/// The heap case is the one the fallthrough actually bit, and needs `alloc`:
///
/// ```rust
/// # #[cfg(feature = "alloc")] {
/// use secure_gate::{Dynamic, InnerSecret, RevealSecret};
///
/// let owned: InnerSecret<String> = Dynamic::<String>::new("s3cret".to_string()).into_inner();
/// let copy: InnerSecret<String> = owned.clone(); // was a bare String before this impl
/// assert_eq!(format!("{copy:?}"), "[REDACTED]");
/// # }
/// ```
/// Provides `&T` access via `*inner_secret`. Deref is the intended API for an output
/// wrapper; the secret wrappers [`Fixed`](crate::Fixed) and [`Dynamic`](crate::Dynamic)
/// deliberately do not implement it.