win_crypto_ng/
random.rs

1//! Cryptographically secure random number generation
2//!
3//! # Usage
4//!
5//! To generate cryptographically secure random numbers, start by opening a
6//! [`RandomNumberGenerator`]. This can be done either via the [`open`] method
7//! where you specify the random algorithm to use or with the [`system_preferred`]
8//! method, where the system default is used. Then, to fill a buffer with random
9//! numbers, call the [`gen_random`] method.
10//!
11//! ```
12//! use win_crypto_ng::random::{RandomAlgorithmId, RandomNumberGenerator};
13//!
14//! let mut buffer = [0u8; 32];
15//! let rng = RandomNumberGenerator::open(RandomAlgorithmId::Rng).unwrap();
16//! rng.gen_random(&mut buffer).unwrap();
17//!
18//! assert_ne!(&buffer, &[0u8; 32]);
19//! ```
20//!
21//! [`RandomNumberGenerator`]: struct.RandomNumberGenerator.html
22//! [`open`]: struct.RandomNumberGenerator.html#method.open
23//! [`system_preferred`]: struct.RandomNumberGenerator.html#method.system_preferred
24//! [`gen_random`]: struct.RandomNumberGenerator.html#method.gen_random
25
26use crate::helpers::{AlgoHandle, Handle};
27use crate::Error;
28use core::convert::TryFrom;
29use core::fmt;
30use core::ptr;
31use winapi::shared::bcrypt::*;
32use winapi::shared::ntdef::ULONG;
33
34/// Random number generation algorithms identifiers
35#[derive(Clone, Copy, PartialOrd, PartialEq)]
36pub enum RandomAlgorithmId {
37    /// The random-number generator algorithm.
38    ///
39    /// Standard: FIPS 186-2, FIPS 140-2, NIST SP 800-90
40    ///
41    /// Beginning with Windows Vista with SP1 and Windows Server 2008, the
42    /// random number generator is based on the AES counter mode specified in
43    /// the NIST SP 800-90 standard.
44    ///
45    /// **Windows Vista**: The random number generator is based on the hash-based
46    /// random number generator specified in the FIPS 186-2 standard.
47    ///
48    /// **Windows 8**: Beginning with Windows 8, the RNG algorithm supports
49    /// FIPS 186-3. Keys less than or equal to 1024 bits adhere to FIPS 186-2
50    /// and keys greater than 1024 to FIPS 186-3.
51    Rng,
52    /// The dual elliptic curve random-number generator algorithm.
53    ///
54    /// Standard: SP800-90.
55    ///
56    /// **Windows 8**: Beginning with Windows 8, the EC RNG algorithm supports
57    /// FIPS 186-3. Keys less than or equal to 1024 bits adhere to FIPS 186-2
58    /// and keys greater than 1024 to FIPS 186-3.
59    ///
60    /// **Windows 10**: Beginning with Windows 10, the dual elliptic curve random
61    /// number generator algorithm has been removed. Existing uses of this
62    /// algorithm will continue to work; however, the random number generator is
63    /// based on the AES counter mode specified in the NIST SP 800-90 standard.
64    /// New code should use [`Rng`](#variant.Rng), and it is recommended that
65    /// existing code be changed to use [`Rng`](#variant.Rng).
66    DualECRng,
67    /// The random-number generator algorithm suitable for DSA (Digital
68    /// Signature RandomAlgorithmId).
69    ///
70    /// Standard: FIPS 186-2.
71    ///
72    /// **Windows 8**: Support for FIPS 186-3 begins.
73    Fips186DsaRng,
74}
75
76impl<'a> TryFrom<&'a str> for RandomAlgorithmId {
77    type Error = &'a str;
78
79    fn try_from(value: &'a str) -> Result<RandomAlgorithmId, Self::Error> {
80        match value {
81            BCRYPT_RNG_ALGORITHM => Ok(RandomAlgorithmId::Rng),
82            BCRYPT_RNG_DUAL_EC_ALGORITHM => Ok(RandomAlgorithmId::DualECRng),
83            BCRYPT_RNG_FIPS186_DSA_ALGORITHM => Ok(RandomAlgorithmId::Fips186DsaRng),
84            _ => Err(value),
85        }
86    }
87}
88
89impl From<RandomAlgorithmId> for &'static str {
90    fn from(val: RandomAlgorithmId) -> Self {
91        match val {
92            RandomAlgorithmId::Rng => BCRYPT_RNG_ALGORITHM,
93            RandomAlgorithmId::DualECRng => BCRYPT_RNG_DUAL_EC_ALGORITHM,
94            RandomAlgorithmId::Fips186DsaRng => BCRYPT_RNG_FIPS186_DSA_ALGORITHM,
95        }
96    }
97}
98
99impl fmt::Display for RandomAlgorithmId {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        write!(f, "{}", Into::<&'static str>::into(*self))
102    }
103}
104
105/// Random number generator
106///
107/// Main type that is capable of generating random
108/// numbers.
109pub struct RandomNumberGenerator {
110    handle: RandomAlgoHandle,
111}
112
113impl RandomNumberGenerator {
114    /// Open a random number generator using the provided algorithm.
115    ///
116    /// # Examples
117    ///
118    /// ```
119    /// # use win_crypto_ng::random::{RandomAlgorithmId, RandomNumberGenerator};
120    /// let rng = RandomNumberGenerator::open(RandomAlgorithmId::Rng);
121    ///
122    /// assert!(rng.is_ok());
123    /// ```
124    pub fn open(id: RandomAlgorithmId) -> crate::Result<RandomNumberGenerator> {
125        let handle = RandomAlgoHandle::open(id)?;
126        Ok(Self { handle })
127    }
128
129    /// Open a random number generator using the system preferred algorithm.
130    ///
131    /// **Windows Vista**: This is not supported.
132    pub fn system_preferred() -> RandomNumberGenerator {
133        let handle = RandomAlgoHandle::SystemPreferred;
134        Self { handle }
135    }
136
137    /// Fills a buffer with random bytes.
138    ///
139    /// Use a random number for the entropy.
140    ///
141    /// # Examples
142    ///
143    /// ```
144    /// # use win_crypto_ng::random::{RandomAlgorithmId, RandomNumberGenerator};
145    /// let mut buffer = [0u8; 32];
146    /// let rng = RandomNumberGenerator::system_preferred();
147    /// rng.gen_random(&mut buffer).unwrap();
148    ///
149    /// assert_ne!(&buffer, &[0u8; 32]);
150    /// ```
151    pub fn gen_random(&self, buffer: &mut [u8]) -> crate::Result<()> {
152        self.gen_random_with_opts(buffer, self.handle.flags())
153    }
154
155    /// Fills a buffer with random bytes.
156    ///
157    /// This function will use the number in the buffer as additional
158    /// entropy for the random number.
159    ///
160    /// **Windows 8 and later**: This does the exact same thing as
161    /// [`gen_random`](#method.gen_random).
162    ///
163    /// # Examples
164    ///
165    /// ```
166    /// # use win_crypto_ng::random::{RandomAlgorithmId, RandomNumberGenerator};
167    /// let mut buffer = [0u8; 32];
168    /// let rng = RandomNumberGenerator::system_preferred();
169    /// rng.gen_random_with_entropy_in_buffer(&mut buffer).unwrap();
170    ///
171    /// assert_ne!(&buffer, &[0u8; 32]);
172    /// ```
173    pub fn gen_random_with_entropy_in_buffer(&self, buffer: &mut [u8]) -> crate::Result<()> {
174        self.gen_random_with_opts(
175            buffer,
176            self.handle.flags() | BCRYPT_RNG_USE_ENTROPY_IN_BUFFER,
177        )
178    }
179
180    fn gen_random_with_opts(&self, buffer: &mut [u8], opts: ULONG) -> crate::Result<()> {
181        let handle = self.handle.handle();
182
183        Error::check(unsafe {
184            BCryptGenRandom(handle, buffer.as_mut_ptr(), buffer.len() as ULONG, opts)
185        })
186    }
187}
188
189#[cfg(feature = "rand")]
190impl rand_core::CryptoRng for RandomNumberGenerator {}
191
192#[cfg(feature = "rand")]
193impl rand_core::RngCore for RandomNumberGenerator {
194    fn next_u32(&mut self) -> u32 {
195        rand_core::impls::next_u32_via_fill(self)
196    }
197
198    fn next_u64(&mut self) -> u64 {
199        rand_core::impls::next_u64_via_fill(self)
200    }
201
202    fn fill_bytes(&mut self, dst: &mut [u8]) {
203        // Panics are allowed in `fill_bytes`
204        self.try_fill_bytes(dst).unwrap()
205    }
206
207    fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), rand_core::Error> {
208        self.gen_random(dst)
209            .map_err(|e| From::<core::num::NonZeroU32>::from(e.into()))
210    }
211}
212
213/// Wrapper around `AlgoHandle` that can only specify RNG algorithms.
214enum RandomAlgoHandle {
215    /// System-preferred algorithm provider.
216    SystemPreferred,
217    /// An already opened provider for a specified algorithm.
218    Specified(AlgoHandle),
219}
220
221impl RandomAlgoHandle {
222    fn open(id: RandomAlgorithmId) -> crate::Result<Self> {
223        Ok(Self::Specified(AlgoHandle::open(id.into())?))
224    }
225
226    fn handle(&self) -> BCRYPT_ALG_HANDLE {
227        match self {
228            Self::SystemPreferred => ptr::null_mut(),
229            Self::Specified(handle) => handle.as_ptr(),
230        }
231    }
232
233    fn flags(&self) -> ULONG {
234        match self {
235            Self::SystemPreferred => BCRYPT_USE_SYSTEM_PREFERRED_RNG,
236            Self::Specified(_) => 0,
237        }
238    }
239}
240
241#[cfg(test)]
242mod tests {
243    use super::*;
244
245    fn test_rng(rng: RandomNumberGenerator) {
246        let empty = vec![0; 32];
247
248        let mut buf = empty.clone();
249        rng.gen_random(&mut buf).expect("RNG to succeed");
250        assert_ne!(&buf, &empty);
251
252        let mut buf2 = buf.clone();
253        rng.gen_random_with_entropy_in_buffer(&mut buf2)
254            .expect("RNG to succeeed");
255        assert_ne!(&buf2, &empty);
256        assert_ne!(&buf2, &buf);
257    }
258
259    #[test]
260    fn system_preferred() {
261        let rng = RandomNumberGenerator::system_preferred();
262        test_rng(rng);
263    }
264
265    #[test]
266    fn rng() {
267        let rng = RandomNumberGenerator::open(RandomAlgorithmId::Rng).unwrap();
268        test_rng(rng);
269    }
270
271    #[test]
272    fn dualecrng() {
273        let rng = RandomNumberGenerator::open(RandomAlgorithmId::DualECRng).unwrap();
274        test_rng(rng);
275    }
276
277    #[test]
278    fn fips186dsarng() {
279        let rng = RandomNumberGenerator::open(RandomAlgorithmId::Fips186DsaRng).unwrap();
280        test_rng(rng);
281    }
282}