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}