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, ExposeSecret};
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::ExposeSecret;
32/// use secure_gate::fixed_alias_random;
33/// fixed_alias_random!(Nonce, 24);
34/// let nonce = Nonce::generate();
35/// # }
36/// ```
37pub struct FixedRandom<const N: usize>(pub(crate) Fixed<[u8; N]>);
38
39impl<const N: usize> FixedRandom<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::FixedRandom, ExposeSecret};
51    /// let random = FixedRandom::<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::FixedRandom;
73    /// let random: Result<FixedRandom<32>, rand::rand_core::OsError> = FixedRandom::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    /// Consume the wrapper and return the inner `Fixed<[u8; N]>`.
85    ///
86    /// This transfers ownership without exposing the secret bytes.
87    /// The returned `Fixed` retains all security guarantees (zeroize, etc.).
88    ///
89    /// # Example
90    ///
91    /// ```
92    /// # #[cfg(feature = "rand")]
93    /// # {
94    /// use secure_gate::{Fixed, random::FixedRandom};
95    /// let random = FixedRandom::<32>::generate();
96    /// let fixed: Fixed<[u8; 32]> = random.into_inner();
97    /// // Can now use fixed.expose_secret() as needed
98    /// # }
99    /// ```
100    #[inline(always)]
101    pub fn into_inner(self) -> Fixed<[u8; N]> {
102        self.0
103    }
104}
105
106/// Debug implementation (always redacted).
107impl<const N: usize> core::fmt::Debug for FixedRandom<N> {
108    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
109        f.write_str("[REDACTED]")
110    }
111}
112
113impl<const N: usize> From<FixedRandom<N>> for Fixed<[u8; N]> {
114    /// Convert a `FixedRandom` to `Fixed`, transferring ownership.
115    ///
116    /// This preserves all security guarantees. The `FixedRandom` type
117    /// ensures the value came from secure RNG, and this conversion
118    /// transfers that value to `Fixed` without exposing bytes.
119    ///
120    /// # Example
121    ///
122    /// ```
123    /// # #[cfg(feature = "rand")]
124    /// # {
125    /// use secure_gate::{Fixed, random::FixedRandom};
126    /// let key: Fixed<[u8; 32]> = FixedRandom::<32>::generate().into();
127    /// # }
128    /// ```
129    #[inline(always)]
130    fn from(rng: FixedRandom<N>) -> Self {
131        rng.into_inner()
132    }
133}