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}