secure_gate/
random.rs

1// ==========================================================================
2// src/random.rs
3// ==========================================================================
4
5use crate::{Dynamic, Fixed};
6use rand::rand_core::OsError;
7use rand::rngs::OsRng;
8use rand::TryRngCore;
9
10/// Fixed-length cryptographically secure random value.
11///
12/// This is a newtype over `Fixed<[u8; N]>` that enforces construction only via secure RNG.
13/// Guarantees freshness — cannot be created from arbitrary bytes.
14///
15/// Requires the "rand" feature.
16///
17/// # Examples
18///
19/// Basic usage:
20/// ```
21/// # #[cfg(feature = "rand")]
22/// # {
23/// use secure_gate::random::FixedRng;
24/// let random: FixedRng<32> = FixedRng::generate();
25/// assert_eq!(random.len(), 32);
26/// # }
27/// ```
28///
29/// With alias:
30/// ```
31/// # #[cfg(feature = "rand")]
32/// # {
33/// use secure_gate::fixed_alias_rng;
34/// fixed_alias_rng!(Nonce, 24);
35/// let nonce = Nonce::generate();
36/// # }
37/// ```
38pub struct FixedRng<const N: usize>(Fixed<[u8; N]>);
39
40impl<const N: usize> FixedRng<N> {
41    /// Generate fresh random bytes using the OS RNG.
42    ///
43    /// Uses `rand::rngs::OsRng` directly for maximum throughput.
44    /// Panics if the RNG fails (rare, but correct for crypto code).
45    ///
46    /// # Example
47    ///
48    /// ```
49    /// # #[cfg(feature = "rand")]
50    /// # {
51    /// use secure_gate::random::FixedRng;
52    /// let random = FixedRng::<16>::generate();
53    /// assert!(!random.is_empty());
54    /// # }
55    /// ```
56    pub fn generate() -> Self {
57        let mut bytes = [0u8; N];
58        OsRng
59            .try_fill_bytes(&mut bytes)
60            .expect("OsRng failed — this should never happen on supported platforms");
61        Self(Fixed::new(bytes))
62    }
63
64    /// Try to generate fresh random bytes using the OS RNG.
65    ///
66    /// Returns an error if the RNG fails.
67    ///
68    /// # Example
69    ///
70    /// ```
71    /// # #[cfg(feature = "rand")]
72    /// # {
73    /// use secure_gate::random::FixedRng;
74    /// let random: Result<FixedRng<32>, rand::rand_core::OsError> = FixedRng::try_generate();
75    /// assert!(random.is_ok());
76    /// # }
77    /// ```
78    pub fn try_generate() -> Result<Self, OsError> {
79        let mut bytes = [0u8; N];
80        OsRng
81            .try_fill_bytes(&mut bytes)
82            .map(|_| Self(Fixed::new(bytes)))
83    }
84
85    /// Expose the random bytes for read-only access.
86    ///
87    /// # Example
88    ///
89    /// ```
90    /// # #[cfg(feature = "rand")]
91    /// # {
92    /// use secure_gate::random::FixedRng;
93    /// let random = FixedRng::<4>::generate();
94    /// let bytes = random.expose_secret();
95    /// # }
96    /// ```
97    #[inline(always)]
98    pub fn expose_secret(&self) -> &[u8; N] {
99        self.0.expose_secret()
100    }
101
102    /// Returns the fixed length in bytes.
103    #[inline(always)]
104    pub const fn len(&self) -> usize {
105        N
106    }
107
108    /// Returns `true` if the length is zero.
109    #[inline(always)]
110    pub const fn is_empty(&self) -> bool {
111        N == 0
112    }
113
114    /// Consume the wrapper and return the inner `Fixed<[u8; N]>`.
115    ///
116    /// This transfers ownership without exposing the secret bytes.
117    /// The returned `Fixed` retains all security guarantees (zeroize, etc.).
118    ///
119    /// # Example
120    ///
121    /// ```
122    /// # #[cfg(feature = "rand")]
123    /// # {
124    /// use secure_gate::{Fixed, random::FixedRng};
125    /// let random = FixedRng::<32>::generate();
126    /// let fixed: Fixed<[u8; 32]> = random.into_inner();
127    /// // Can now use fixed.expose_secret() as needed
128    /// # }
129    /// ```
130    #[inline(always)]
131    pub fn into_inner(self) -> Fixed<[u8; N]> {
132        self.0
133    }
134}
135
136impl<const N: usize> core::fmt::Debug for FixedRng<N> {
137    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
138        f.write_str("[REDACTED]")
139    }
140}
141
142#[cfg(all(feature = "rand", feature = "encoding-hex"))]
143impl<const N: usize> FixedRng<N> {
144    /// Consume self and return the random bytes as a validated hex string.
145    ///
146    /// The raw bytes are zeroized immediately after encoding.
147    ///
148    /// # Example
149    ///
150    /// ```
151    /// # #[cfg(all(feature = "rand", feature = "encoding-hex"))]
152    /// # {
153    /// use secure_gate::random::FixedRng;
154    /// let hex = FixedRng::<16>::generate().into_hex();
155    /// println!("random hex: {}", hex.expose_secret());
156    /// # }
157    /// ```
158    pub fn into_hex(self) -> crate::encoding::hex::HexString {
159        use hex;
160        let hex = hex::encode(self.expose_secret());
161        crate::encoding::hex::HexString::new_unchecked(hex)
162    }
163
164    /// Encode to hex without consuming self, for cases where raw is still needed briefly.
165    ///
166    /// # Example
167    ///
168    /// ```
169    /// # #[cfg(all(feature = "rand", feature = "encoding-hex"))]
170    /// # {
171    /// use secure_gate::random::FixedRng;
172    /// let rng = FixedRng::<16>::generate();
173    /// let hex = rng.to_hex();
174    /// // Use rng for something else here
175    /// # }
176    /// ```
177    pub fn to_hex(&self) -> crate::encoding::hex::HexString {
178        use hex;
179        let hex = hex::encode(self.expose_secret());
180        crate::encoding::hex::HexString::new_unchecked(hex)
181    }
182}
183
184impl<const N: usize> From<FixedRng<N>> for Fixed<[u8; N]> {
185    /// Convert a `FixedRng` to `Fixed`, transferring ownership.
186    ///
187    /// This preserves all security guarantees. The `FixedRng` type
188    /// ensures the value came from secure RNG, and this conversion
189    /// transfers that value to `Fixed` without exposing bytes.
190    ///
191    /// # Example
192    ///
193    /// ```
194    /// # #[cfg(feature = "rand")]
195    /// # {
196    /// use secure_gate::{Fixed, random::FixedRng};
197    /// let key: Fixed<[u8; 32]> = FixedRng::<32>::generate().into();
198    /// # }
199    /// ```
200    #[inline(always)]
201    fn from(rng: FixedRng<N>) -> Self {
202        rng.into_inner()
203    }
204}
205
206/// Heap-allocated cryptographically secure random bytes.
207///
208/// This is a newtype over `Dynamic<Vec<u8>>` for semantic clarity.
209/// Like `FixedRng`, guarantees freshness via RNG construction.
210///
211/// Requires the "rand" feature.
212///
213/// # Examples
214///
215/// ```
216/// # #[cfg(feature = "rand")]
217/// # {
218/// use secure_gate::random::DynamicRng;
219/// let random = DynamicRng::generate(64);
220/// assert_eq!(random.len(), 64);
221/// # }
222/// ```
223pub struct DynamicRng(Dynamic<Vec<u8>>);
224
225impl DynamicRng {
226    /// Generate fresh random bytes of the specified length.
227    ///
228    /// Panics if the RNG fails.
229    ///
230    /// # Example
231    ///
232    /// ```
233    /// # #[cfg(feature = "rand")]
234    /// # {
235    /// use secure_gate::random::DynamicRng;
236    /// let random = DynamicRng::generate(128);
237    /// # }
238    /// ```
239    pub fn generate(len: usize) -> Self {
240        let mut bytes = vec![0u8; len];
241        OsRng
242            .try_fill_bytes(&mut bytes)
243            .expect("OsRng failed — this should never happen on supported platforms");
244        Self(Dynamic::from(bytes))
245    }
246
247    /// Try to generate fresh random bytes of the specified length.
248    ///
249    /// Returns an error if the RNG fails.
250    ///
251    /// # Example
252    ///
253    /// ```
254    /// # #[cfg(feature = "rand")]
255    /// # {
256    /// use secure_gate::random::DynamicRng;
257    /// let random: Result<DynamicRng, rand::rand_core::OsError> = DynamicRng::try_generate(64);
258    /// assert!(random.is_ok());
259    /// # }
260    /// ```
261    pub fn try_generate(len: usize) -> Result<Self, OsError> {
262        let mut bytes = vec![0u8; len];
263        OsRng
264            .try_fill_bytes(&mut bytes)
265            .map(|_| Self(Dynamic::from(bytes)))
266    }
267
268    /// Expose the random bytes for read-only access.
269    #[inline(always)]
270    pub fn expose_secret(&self) -> &[u8] {
271        self.0.expose_secret()
272    }
273
274    /// Returns the length in bytes.
275    #[inline(always)]
276    pub const fn len(&self) -> usize {
277        self.0.len()
278    }
279
280    /// Returns `true` if empty.
281    #[inline(always)]
282    pub const fn is_empty(&self) -> bool {
283        self.0.is_empty()
284    }
285
286    /// Consume and return the inner `Dynamic<Vec<u8>>`.
287    #[inline(always)]
288    pub fn into_inner(self) -> Dynamic<Vec<u8>> {
289        self.0
290    }
291}
292
293impl core::fmt::Debug for DynamicRng {
294    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
295        f.write_str("[REDACTED]")
296    }
297}
298
299impl From<DynamicRng> for Dynamic<Vec<u8>> {
300    /// Convert a `DynamicRng` to `Dynamic`, transferring ownership.
301    ///
302    /// This preserves all security guarantees. The `DynamicRng` type
303    /// ensures the value came from secure RNG, and this conversion
304    /// transfers that value to `Dynamic` without exposing bytes.
305    ///
306    /// # Example
307    ///
308    /// ```
309    /// # #[cfg(feature = "rand")]
310    /// # {
311    /// use secure_gate::{Dynamic, random::DynamicRng};
312    /// let random: Dynamic<Vec<u8>> = DynamicRng::generate(64).into();
313    /// # }
314    /// ```
315    #[inline(always)]
316    fn from(rng: DynamicRng) -> Self {
317        rng.into_inner()
318    }
319}