secure_gate/
fixed.rs

1// src/fixed.rs
2//! Stack-allocated, zero-cost secure wrappers for fixed-size secrets.
3//!
4//! `Fixed<T>` is a transparent wrapper around any type `T` that lives entirely on the stack.
5//! It provides:
6//! - Zero-cost abstraction via `Deref`/`DerefMut`.
7//! - Automatic redaction in `Debug` output.
8//! - Explicit access via `.expose_secret()` (canonical API).
9//! - Specialized ergonomics for `[u8; N]` arrays (e.g., crypto keys, nonces).
10//!
11//! # Examples
12//!
13//! ```
14//! use secure_gate::{fixed_alias, Fixed};
15//!
16//! fixed_alias!(Aes256Key, 32);
17//!
18//! let raw_key = [42u8; 32]; // In real code: use rand::Rng::gen()
19//! let key: Aes256Key = raw_key.into();
20//!
21//! assert_eq!(key.expose_secret()[0], 42);
22//! ```
23
24use core::convert::From;
25use core::ops::{Deref, DerefMut};
26
27/// A zero-cost, stack-allocated wrapper for sensitive data.
28///
29/// `Fixed<T>` stores its value directly in the struct (no heap allocation).
30/// It behaves exactly like `T` via `Deref`/`DerefMut`, but redacts itself
31/// in debug output and requires explicit access to the inner value.
32///
33/// Use this for fixed-size secrets like encryption keys or nonces.
34///
35/// # Examples
36///
37/// ```
38/// use secure_gate::Fixed;
39///
40/// let secret: Fixed<[u8; 4]> = [1, 2, 3, 4].into();
41/// assert_eq!(secret.expose_secret(), &[1, 2, 3, 4]);
42/// ```
43pub struct Fixed<T>(pub T);
44
45impl<T> Fixed<T> {
46    /// Creates a new `Fixed` wrapper around the given value.
47    ///
48    /// This is zero-cost and usually not called directly—prefer `fixed_alias!` + `.into()`.
49    ///
50    /// # Examples
51    ///
52    /// ```
53    /// use secure_gate::Fixed;
54    ///
55    /// let secret = Fixed::new([42u8; 32]);
56    /// assert_eq!(secret.len(), 32);
57    /// ```
58    #[inline(always)]
59    pub const fn new(value: T) -> Self {
60        Fixed(value)
61    }
62}
63
64impl<T> Deref for Fixed<T> {
65    type Target = T;
66
67    /// Dereferences the wrapper to access the inner value immutably.
68    #[inline(always)]
69    fn deref(&self) -> &T {
70        &self.0
71    }
72}
73
74impl<T> DerefMut for Fixed<T> {
75    /// Dereferences the wrapper mutably to access the inner value.
76    #[inline(always)]
77    fn deref_mut(&mut self) -> &mut T {
78        &mut self.0
79    }
80}
81
82/// Converts a byte slice into a fixed-size secret array.
83///
84/// # Panics
85///
86/// Panics if `bytes.len() != N` with the message "slice length mismatch".
87///
88/// # Examples
89///
90/// ```
91/// use secure_gate::Fixed;
92///
93/// let bytes = [42u8; 32];
94/// let secret = Fixed::from_slice(&bytes);
95/// assert_eq!(secret.expose_secret(), &[42u8; 32]);
96/// ```
97impl<const N: usize> Fixed<[u8; N]> {
98    #[inline]
99    pub fn from_slice(bytes: &[u8]) -> Self {
100        assert_eq!(bytes.len(), N, "slice length mismatch");
101        let mut arr = [0u8; N];
102        arr.copy_from_slice(&bytes[..N]);
103        Self::new(arr)
104    }
105}
106
107/// Converts a raw array into a fixed-size secret.
108///
109/// Enables idiomatic construction like `Aes256Key::from(rng.gen())`.
110///
111/// # Examples
112///
113/// ```
114/// use secure_gate::Fixed;
115///
116/// let secret: Fixed<[u8; 4]> = [1, 2, 3, 4].into();
117/// assert_eq!(secret.expose_secret(), &[1, 2, 3, 4]);
118/// ```
119impl<const N: usize> From<[u8; N]> for Fixed<[u8; N]> {
120    #[inline(always)]
121    fn from(arr: [u8; N]) -> Self {
122        Self::new(arr)
123    }
124}
125
126/// Borrows the fixed byte array as a slice.
127///
128/// Useful for passing to crypto APIs expecting `&[u8]`.
129///
130/// # Examples
131///
132/// ```
133/// use secure_gate::Fixed;
134///
135/// let secret: Fixed<[u8; 4]> = [1, 2, 3, 4].into();
136/// let slice: &[u8] = secret.as_ref();
137/// assert_eq!(slice, &[1, 2, 3, 4]);
138/// ```
139impl<const N: usize> AsRef<[u8]> for Fixed<[u8; N]> {
140    #[inline(always)]
141    fn as_ref(&self) -> &[u8] {
142        &self.0
143    }
144}
145
146/// Mutably borrows the fixed byte array as a slice.
147///
148/// Useful for in-place modifications like key scheduling.
149///
150/// # Examples
151///
152/// ```
153/// use secure_gate::Fixed;
154///
155/// let mut secret: Fixed<[u8; 4]> = [1, 2, 3, 4].into();
156/// let slice: &mut [u8] = secret.as_mut();
157/// slice[0] = 42;
158/// assert_eq!(secret.expose_secret(), &[42, 2, 3, 4]);
159/// ```
160impl<const N: usize> AsMut<[u8]> for Fixed<[u8; N]> {
161    #[inline(always)]
162    fn as_mut(&mut self) -> &mut [u8] {
163        &mut self.0
164    }
165}
166
167/// All `Fixed<T>` values print as "[REDACTED]" to prevent accidental leakage.
168impl<T> core::fmt::Debug for Fixed<T> {
169    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
170        f.write_str("[REDACTED]")
171    }
172}
173
174impl<T> Fixed<T> {
175    /// Accesses the secret value immutably.
176    ///
177    /// This is the canonical, explicit way to read the secret.
178    ///
179    /// # Examples
180    ///
181    /// ```
182    /// use secure_gate::Fixed;
183    ///
184    /// let secret: Fixed<i32> = Fixed::new(42);
185    /// assert_eq!(*secret.expose_secret(), 42);
186    /// ```
187    #[inline(always)]
188    pub fn expose_secret(&self) -> &T {
189        &self.0
190    }
191
192    /// Accesses the secret value mutably.
193    ///
194    /// Use for in-place modifications.
195    ///
196    /// # Examples
197    ///
198    /// ```
199    /// use secure_gate::Fixed;
200    ///
201    /// let mut secret: Fixed<i32> = Fixed::new(42);
202    /// *secret.expose_secret_mut() += 1;
203    /// assert_eq!(*secret.expose_secret(), 43);
204    /// ```
205    #[inline(always)]
206    pub fn expose_secret_mut(&mut self) -> &mut T {
207        &mut self.0
208    }
209
210    /// **Deprecated**: Use [`expose_secret`] instead.
211    ///
212    /// This method forwards to [`expose_secret`] for compatibility.
213    #[deprecated(since = "0.5.5", note = "use `expose_secret` instead")]
214    #[doc(hidden)]
215    #[inline(always)]
216    pub fn view(&self) -> &T {
217        self.expose_secret()
218    }
219
220    /// **Deprecated**: Use [`expose_secret_mut`] instead.
221    ///
222    /// This method forwards to [`expose_secret_mut`] for compatibility.
223    #[deprecated(since = "0.5.5", note = "use `expose_secret_mut` instead")]
224    #[doc(hidden)]
225    #[inline(always)]
226    pub fn view_mut(&mut self) -> &mut T {
227        self.expose_secret_mut()
228    }
229
230    /// Consumes the wrapper and returns the inner value.
231    ///
232    /// Useful for passing ownership to functions expecting `T`.
233    ///
234    /// # Examples
235    ///
236    /// ```
237    /// use secure_gate::Fixed;
238    ///
239    /// let secret: Fixed<i32> = Fixed::new(42);
240    /// let value: i32 = secret.into_inner();
241    /// assert_eq!(value, 42);
242    /// ```
243    #[inline(always)]
244    pub fn into_inner(self) -> T {
245        self.0
246    }
247}
248
249/// Implements `Clone` when the inner type is `Clone`.
250impl<T: Clone> Clone for Fixed<T> {
251    #[inline(always)]
252    fn clone(&self) -> Self {
253        Self(self.0.clone())
254    }
255}
256
257/// Implements `Copy` for small fixed-size byte arrays.
258impl<const N: usize> Copy for Fixed<[u8; N]> where [u8; N]: Copy {}