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}