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}