1use rand_core::{CryptoRng, RngCore, SeedableRng as _};
2
3#[derive(Debug, Clone, thiserror::Error)]
4pub enum RngError {
5 #[error("Failed to get random bytes: {0}")]
6 OsRng(getrandom::Error),
7 #[error("HKDF error: {0}")]
8 Hkdf(hkdf::InvalidLength),
9}
10
11pub fn os_rng_hkdf(
13 nonce: Option<&[u8]>,
14 context: &[u8],
15) -> Result<impl RngCore + CryptoRng, RngError> {
16 let mut ikm = [0u8; 32];
17 getrandom::fill(&mut ikm).map_err(RngError::OsRng)?;
18
19 let hk = hkdf::Hkdf::<sha2::Sha256>::new(nonce, &ikm);
20
21 let mut seed = [0u8; 32];
22 hk.expand(context, &mut seed).map_err(RngError::Hkdf)?;
23
24 Ok(rand_chacha::ChaCha20Rng::from_seed(seed))
25}
26
27#[cfg(test)]
28mod tests {
29 use super::*;
30
31 #[test]
32 fn test_os_rng_hkdf() {
33 let mut rng1 = os_rng_hkdf(None, b"test-context").unwrap();
34 let mut rng2 = os_rng_hkdf(Some(b"hey there!"), b"test-context").unwrap();
35
36 let mut buf1 = [0u8; 256];
37 let mut buf2 = [0u8; 256];
38
39 rng1.fill_bytes(&mut buf1);
40 rng2.fill_bytes(&mut buf2);
41
42 assert_ne!(buf1, buf2);
44 }
45}