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}