askar_crypto/
random.rs

1//! Support for random number generation
2
3use core::fmt::{self, Debug, Formatter};
4
5use aead::generic_array::{typenum::Unsigned, GenericArray};
6use chacha20::{
7    cipher::{KeyIvInit, KeySizeUser, StreamCipher},
8    ChaCha20,
9};
10use rand::{CryptoRng, RngCore, SeedableRng};
11
12#[cfg(all(feature = "alloc", feature = "getrandom"))]
13use crate::buffer::SecretBytes;
14use crate::error::Error;
15
16/// The expected length of a seed for `fill_random_deterministic`
17pub const DETERMINISTIC_SEED_LENGTH: usize = <ChaCha20 as KeySizeUser>::KeySize::USIZE;
18
19/// Combined trait for CryptoRng and RngCore
20pub trait Rng: CryptoRng + RngCore + Debug {}
21
22impl<T: CryptoRng + RngCore + Debug> Rng for T {}
23
24/// A trait for generating raw key material, generally
25/// cryptographically random bytes
26pub trait KeyMaterial {
27    /// Read key material from the generator
28    fn read_okm(&mut self, buf: &mut [u8]);
29}
30
31impl<C: CryptoRng + RngCore> KeyMaterial for C {
32    fn read_okm(&mut self, buf: &mut [u8]) {
33        self.fill_bytes(buf);
34    }
35}
36
37#[cfg(feature = "getrandom")]
38#[cfg_attr(docsrs, doc(cfg(feature = "getrandom")))]
39#[inline]
40/// Obtain an instance of the default random number generator
41pub fn default_rng() -> impl CryptoRng + RngCore + Debug + Clone {
42    #[cfg(feature = "std_rng")]
43    {
44        rand::rngs::ThreadRng::default()
45    }
46    #[cfg(not(feature = "std_rng"))]
47    {
48        rand::rngs::OsRng
49    }
50}
51
52/// Fill a mutable slice with random data using the
53/// system random number generator.
54#[cfg(feature = "getrandom")]
55#[inline(always)]
56pub fn fill_random(value: &mut [u8]) {
57    default_rng().fill_bytes(value);
58}
59
60/// Written to be compatible with randombytes_deterministic in libsodium,
61/// used to generate a deterministic symmetric encryption key
62pub fn fill_random_deterministic(seed: &[u8], output: &mut [u8]) -> Result<(), Error> {
63    RandomDet::new(seed).fill_bytes(output);
64    Ok(())
65}
66
67/// A generator for deterministic random bytes
68pub struct RandomDet {
69    cipher: ChaCha20,
70}
71
72impl Debug for RandomDet {
73    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
74        write!(f, "RandomDet {{}}")
75    }
76}
77
78impl SeedableRng for RandomDet {
79    type Seed = [u8; DETERMINISTIC_SEED_LENGTH];
80
81    #[inline]
82    fn from_seed(seed: Self::Seed) -> Self {
83        Self {
84            cipher: ChaCha20::new(
85                GenericArray::from_slice(&seed[..]),
86                GenericArray::from_slice(b"LibsodiumDRG"),
87            ),
88        }
89    }
90}
91
92impl CryptoRng for RandomDet {}
93
94impl RngCore for RandomDet {
95    #[inline]
96    fn next_u32(&mut self) -> u32 {
97        let mut buf = [0; 4];
98        self.cipher.apply_keystream(&mut buf[..]);
99        u32::from_le_bytes(buf)
100    }
101
102    #[inline]
103    fn next_u64(&mut self) -> u64 {
104        let mut buf = [0; 8];
105        self.cipher.apply_keystream(&mut buf[..]);
106        u64::from_le_bytes(buf)
107    }
108
109    #[inline]
110    fn fill_bytes(&mut self, bytes: &mut [u8]) {
111        bytes.iter_mut().for_each(|b| *b = 0u8);
112        self.cipher.apply_keystream(bytes);
113    }
114
115    #[inline]
116    fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> Result<(), rand::Error> {
117        bytes.iter_mut().for_each(|b| *b = 0u8);
118        self.cipher.apply_keystream(bytes);
119        Ok(())
120    }
121}
122
123impl RandomDet {
124    /// Construct a new `RandomDet` instance from a seed value
125    pub fn new(seed: &[u8]) -> Self {
126        let mut sd = [0u8; DETERMINISTIC_SEED_LENGTH];
127        let seed_len = seed.len().min(DETERMINISTIC_SEED_LENGTH);
128        sd[..seed_len].copy_from_slice(&seed[..seed_len]);
129        Self::from_seed(sd)
130    }
131}
132
133#[cfg(all(feature = "alloc", feature = "getrandom"))]
134#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
135/// Create a new `SecretBytes` instance with random data.
136#[inline(always)]
137pub fn random_secret(len: usize) -> SecretBytes {
138    SecretBytes::new_with(len, fill_random)
139}
140
141#[cfg(test)]
142mod tests {
143    use super::*;
144    use crate::buffer::HexRepr;
145    use std::string::ToString;
146
147    #[test]
148    fn fill_random_det_expected() {
149        let seed = b"testseed000000000000000000000001";
150        let mut output = [0u8; 32];
151        fill_random_deterministic(seed, &mut output).unwrap();
152        assert_eq!(
153            HexRepr(output).to_string(),
154            "b1923a011cd1adbe89552db9862470c29512a8f51d184dfd778bfe7f845390d1"
155        );
156    }
157}