secure_gate/random/
fixed_random.rs

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