secure_gate/random/
dynamic_random.rs

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