secure_gate/random/
dynamic_rng.rs

1// secure-gate/src/random/dynamic_rng.rs
2use crate::Dynamic;
3use rand::rand_core::OsError;
4use rand::rngs::OsRng;
5use rand::TryRngCore;
6
7/// Heap-allocated cryptographically secure random bytes with encoding methods.
8///
9/// This is a newtype over `Dynamic<Vec<u8>>` for semantic clarity.
10/// Like `FixedRng`, guarantees freshness via RNG construction.
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/// ```
19/// # #[cfg(feature = "rand")]
20/// # {
21/// use secure_gate::random::DynamicRng;
22/// let random = DynamicRng::generate(64);
23/// assert_eq!(random.len(), 64);
24/// # }
25/// ```
26pub struct DynamicRng(Dynamic<Vec<u8>>);
27
28impl DynamicRng {
29    /// Generate fresh random bytes of the specified length.
30    ///
31    /// Panics if the RNG fails.
32    ///
33    /// # Example
34    ///
35    /// ```
36    /// # #[cfg(feature = "rand")]
37    /// # {
38    /// use secure_gate::random::DynamicRng;
39    /// let random = DynamicRng::generate(128);
40    /// # }
41    /// ```
42    pub fn generate(len: usize) -> Self {
43        let mut bytes = vec![0u8; len];
44        OsRng
45            .try_fill_bytes(&mut bytes)
46            .expect("OsRng failed — this should never happen on supported platforms");
47        Self(Dynamic::from(bytes))
48    }
49
50    /// Try to generate fresh random bytes of the specified length.
51    ///
52    /// Returns an error if the RNG fails.
53    ///
54    /// # Example
55    ///
56    /// ```
57    /// # #[cfg(feature = "rand")]
58    /// # {
59    /// use secure_gate::random::DynamicRng;
60    /// let random: Result<DynamicRng, rand::rand_core::OsError> = DynamicRng::try_generate(64);
61    /// assert!(random.is_ok());
62    /// # }
63    /// ```
64    pub fn try_generate(len: usize) -> Result<Self, OsError> {
65        let mut bytes = vec![0u8; len];
66        OsRng
67            .try_fill_bytes(&mut bytes)
68            .map(|_| Self(Dynamic::from(bytes)))
69    }
70
71    /// Expose the random bytes for read-only access.
72    #[inline(always)]
73    pub fn expose_secret(&self) -> &[u8] {
74        self.0.expose_secret()
75    }
76
77    /// Returns the length in bytes.
78    #[inline(always)]
79    pub const fn len(&self) -> usize {
80        self.0.len()
81    }
82
83    /// Returns `true` if empty.
84    #[inline(always)]
85    pub const fn is_empty(&self) -> bool {
86        self.0.is_empty()
87    }
88
89    /// Consume and return the inner `Dynamic<Vec<u8>>`.
90    #[inline(always)]
91    pub fn into_inner(self) -> Dynamic<Vec<u8>> {
92        self.0
93    }
94}
95
96impl core::fmt::Debug for DynamicRng {
97    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
98        f.write_str("[REDACTED]")
99    }
100}
101
102impl From<DynamicRng> for Dynamic<Vec<u8>> {
103    /// Convert a `DynamicRng` to `Dynamic`, transferring ownership.
104    ///
105    /// This preserves all security guarantees. The `DynamicRng` type
106    /// ensures the value came from secure RNG, and this conversion
107    /// transfers that value to `Dynamic` without exposing bytes.
108    ///
109    /// # Example
110    ///
111    /// ```
112    /// # #[cfg(feature = "rand")]
113    /// # {
114    /// use secure_gate::{Dynamic, random::DynamicRng};
115    /// let random: Dynamic<Vec<u8>> = DynamicRng::generate(64).into();
116    /// # }
117    /// ```
118    #[inline(always)]
119    fn from(rng: DynamicRng) -> Self {
120        rng.into_inner()
121    }
122}