secure_gate/random/fixed_rng.rs
1// secure-gate/src/random/fixed_rng.rs
2use crate::Fixed;
3use rand::rand_core::OsError;
4use rand::rngs::OsRng;
5use rand::TryRngCore;
6
7/// Fixed-length cryptographically secure random value with encoding methods.
8///
9/// This is a newtype over `Fixed<[u8; N]>` that enforces construction only via secure RNG.
10/// Guarantees freshness — cannot be created from arbitrary bytes.
11///
12/// Requires the "rand" feature.
13///
14/// Supports direct encoding to Hex, Base64, Bech32, and Bech32m via convenience methods.
15///
16/// # Examples
17///
18/// Basic usage:
19/// ```
20/// # #[cfg(feature = "rand")]
21/// # {
22/// use secure_gate::random::FixedRng;
23/// let random: FixedRng<32> = FixedRng::generate();
24/// assert_eq!(random.len(), 32);
25/// # }
26/// ```
27///
28/// With alias:
29/// ```
30/// # #[cfg(feature = "rand")]
31/// # {
32/// use secure_gate::fixed_alias_rng;
33/// fixed_alias_rng!(Nonce, 24);
34/// let nonce = Nonce::generate();
35/// # }
36/// ```
37pub struct FixedRng<const N: usize>(Fixed<[u8; N]>);
38
39impl<const N: usize> FixedRng<N> {
40 /// Generate fresh random bytes using the OS RNG.
41 ///
42 /// Uses `rand::rngs::OsRng` directly for maximum throughput.
43 /// Panics if the RNG fails (rare, but correct for crypto code).
44 ///
45 /// # Example
46 ///
47 /// ```
48 /// # #[cfg(feature = "rand")]
49 /// # {
50 /// use secure_gate::random::FixedRng;
51 /// let random = FixedRng::<16>::generate();
52 /// assert!(!random.is_empty());
53 /// # }
54 /// ```
55 pub fn generate() -> Self {
56 let mut bytes = [0u8; N];
57 OsRng
58 .try_fill_bytes(&mut bytes)
59 .expect("OsRng failed — this should never happen on supported platforms");
60 Self(Fixed::new(bytes))
61 }
62
63 /// Try to generate fresh random bytes using the OS RNG.
64 ///
65 /// Returns an error if the RNG fails.
66 ///
67 /// # Example
68 ///
69 /// ```
70 /// # #[cfg(feature = "rand")]
71 /// # {
72 /// use secure_gate::random::FixedRng;
73 /// let random: Result<FixedRng<32>, rand::rand_core::OsError> = FixedRng::try_generate();
74 /// assert!(random.is_ok());
75 /// # }
76 /// ```
77 pub fn try_generate() -> Result<Self, OsError> {
78 let mut bytes = [0u8; N];
79 OsRng
80 .try_fill_bytes(&mut bytes)
81 .map(|_| Self(Fixed::new(bytes)))
82 }
83
84 /// Expose the random bytes for read-only access.
85 ///
86 /// # Example
87 ///
88 /// ```
89 /// # #[cfg(feature = "rand")]
90 /// # {
91 /// use secure_gate::random::FixedRng;
92 /// let random = FixedRng::<4>::generate();
93 /// let bytes = random.expose_secret();
94 /// # }
95 /// ```
96 #[inline(always)]
97 pub fn expose_secret(&self) -> &[u8; N] {
98 self.0.expose_secret()
99 }
100
101 /// Returns the fixed length in bytes.
102 #[inline(always)]
103 pub const fn len(&self) -> usize {
104 N
105 }
106
107 /// Returns `true` if the length is zero.
108 #[inline(always)]
109 pub const fn is_empty(&self) -> bool {
110 N == 0
111 }
112
113 /// Consume the wrapper and return the inner `Fixed<[u8; N]>`.
114 ///
115 /// This transfers ownership without exposing the secret bytes.
116 /// The returned `Fixed` retains all security guarantees (zeroize, etc.).
117 ///
118 /// # Example
119 ///
120 /// ```
121 /// # #[cfg(feature = "rand")]
122 /// # {
123 /// use secure_gate::{Fixed, random::FixedRng};
124 /// let random = FixedRng::<32>::generate();
125 /// let fixed: Fixed<[u8; 32]> = random.into_inner();
126 /// // Can now use fixed.expose_secret() as needed
127 /// # }
128 /// ```
129 #[inline(always)]
130 pub fn into_inner(self) -> Fixed<[u8; N]> {
131 self.0
132 }
133}
134
135impl<const N: usize> core::fmt::Debug for FixedRng<N> {
136 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
137 f.write_str("[REDACTED]")
138 }
139}
140
141impl<const N: usize> From<FixedRng<N>> for Fixed<[u8; N]> {
142 /// Convert a `FixedRng` to `Fixed`, transferring ownership.
143 ///
144 /// This preserves all security guarantees. The `FixedRng` type
145 /// ensures the value came from secure RNG, and this conversion
146 /// transfers that value to `Fixed` without exposing bytes.
147 ///
148 /// # Example
149 ///
150 /// ```
151 /// # #[cfg(feature = "rand")]
152 /// # {
153 /// use secure_gate::{Fixed, random::FixedRng};
154 /// let key: Fixed<[u8; 32]> = FixedRng::<32>::generate().into();
155 /// # }
156 /// ```
157 #[inline(always)]
158 fn from(rng: FixedRng<N>) -> Self {
159 rng.into_inner()
160 }
161}