libcrux/
drbg.rs

1//! # NIST DRBG
2//!
3//! Deterministic Random Bit Generator (DRBG) (NIST, SP 800-90A).
4
5use crate::hacl::drbg;
6// re-export here for convenience
7pub use rand::{CryptoRng, RngCore};
8
9#[derive(Debug)]
10pub enum Error {
11    /// Invalid input.
12    InvalidInput,
13    /// The requested digest is not supported.
14    UnsupportedAlgorithm,
15    /// Unable to generate the requested randomness.
16    UnableToGenerate,
17}
18
19impl std::fmt::Display for Error {
20    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21        f.write_fmt(format_args!("{self:?}"))
22    }
23}
24
25impl std::error::Error for Error {}
26
27pub struct Drbg {
28    state: drbg::Drbg,
29    ctr: u32, // automatic reseed after 512 invocations. It has to happen at least every 1024 times.
30}
31
32impl Drbg {
33    /// Create a new DRBG state with the given hash function.
34    ///
35    /// The DRBG state is initialized with 128 bit entropy from the OS.
36    ///
37    /// This function requires the `rand` feature.
38    #[cfg(feature = "rand")]
39    pub fn new(alg: super::digest::Algorithm) -> Result<Self, Error> {
40        let mut entropy = [0u8; 16];
41        rand::rngs::OsRng.fill_bytes(&mut entropy);
42        Self::personalized(alg, &entropy, &[], "os seeded libcrux")
43    }
44
45    /// Create a new DRBG state with the given hash function.
46    /// This also initializes the DRBG state with the given entropy.
47    pub fn new_with_entropy(alg: super::digest::Algorithm, entropy: &[u8]) -> Result<Self, Error> {
48        Self::personalized(alg, entropy, &[], "libcrux")
49    }
50
51    /// Create a new DRBG state with the given hash function.
52    /// This also initializes the DRBG state with the given entropy, nonce and
53    /// personalization string.
54    pub fn personalized(
55        alg: super::digest::Algorithm,
56        entropy: &[u8],
57        nonce: &[u8],
58        personalization: &str,
59    ) -> Result<Self, Error> {
60        let algorithm = match alg {
61            crate::digest::Algorithm::Sha1 => drbg::Algorithm::Sha1,
62            crate::digest::Algorithm::Sha256 => drbg::Algorithm::Sha2_256,
63            crate::digest::Algorithm::Sha384 => drbg::Algorithm::Sha2_384,
64            crate::digest::Algorithm::Sha512 => drbg::Algorithm::Sha2_512,
65            crate::digest::Algorithm::Sha224
66            | crate::digest::Algorithm::Blake2s
67            | crate::digest::Algorithm::Blake2b
68            | crate::digest::Algorithm::Sha3_224
69            | crate::digest::Algorithm::Sha3_256
70            | crate::digest::Algorithm::Sha3_384
71            | crate::digest::Algorithm::Sha3_512 => return Err(Error::UnsupportedAlgorithm),
72        };
73
74        let state = drbg::Drbg::new(algorithm, entropy, nonce, personalization)
75            .map_err(|_| Error::InvalidInput)?;
76        Ok(Self { state, ctr: 0 })
77    }
78
79    /// Automatically reseed after a while.
80    #[cfg(feature = "rand")]
81    #[inline(always)]
82    fn auto_reseed(&mut self) -> Result<(), Error> {
83        if self.ctr > 512 {
84            let mut entropy = [0u8; 16];
85            rand::rngs::OsRng.fill_bytes(&mut entropy);
86            self.reseed(&entropy, b"reseed")?;
87            self.ctr = 0;
88        } else {
89            self.ctr += 1;
90        }
91        Ok(())
92    }
93
94    #[cfg(not(feature = "rand"))]
95    #[inline(always)]
96    fn auto_reseed(&mut self) -> Result<(), Error> {
97        Ok(())
98    }
99
100    /// Reseed the DRBG state.
101    pub fn reseed(&mut self, entropy: &[u8], additional_input: &[u8]) -> Result<(), Error> {
102        self.state
103            .reseed(entropy, additional_input)
104            .map_err(|_| Error::InvalidInput)
105    }
106
107    /// Generate random bytes.
108    ///
109    /// Note that you will need to call `reseed()` when `reseed_required` is true
110    /// and the `rand` feature is not enabled. If the `rand` feature is enabled,
111    /// the Drbg reseeds itself when needed, using `OsRng`.
112    pub fn generate(&mut self, output: &mut [u8]) -> Result<(), Error> {
113        self.auto_reseed()?;
114        self.state
115            .generate(output, &[])
116            .map_err(|_| Error::UnableToGenerate)
117    }
118
119    /// Generate random bytes with additional input mixed into the state.
120    ///
121    /// Note that you will need to call `reseed()` when `reseed_required` is true
122    /// and the `rand` feature is not enabled. If the `rand` feature is enabled,
123    /// the Drbg reseeds itself when needed, using `OsRng`.
124    pub fn generate_with_input(
125        &mut self,
126        output: &mut [u8],
127        additional_input: &[u8],
128    ) -> Result<(), Error> {
129        self.auto_reseed()?;
130        self.state
131            .generate(output, additional_input)
132            .map_err(|_| Error::UnableToGenerate)
133    }
134
135    /// Generate random bytes.
136    /// Allocates the vector of length `len`.
137    ///
138    /// Note that you will need to call `reseed()` when `reseed_required` is true
139    /// and the `rand` feature is not enabled. If the `rand` feature is enabled,
140    /// the Drbg reseeds itself when needed, using `OsRng`.
141    pub fn generate_vec(&mut self, len: usize) -> Result<Vec<u8>, Error> {
142        self.auto_reseed()?;
143        let mut output = vec![0u8; len];
144        self.state
145            .generate(&mut output, &[])
146            .map_err(|_| Error::UnableToGenerate)
147            .map(|()| output)
148    }
149
150    /// Generate random bytes.
151    /// Allocates the array of length `LEN`.
152    ///
153    /// Note that you will need to call `reseed()` when `reseed_required` is true
154    /// and the `rand` feature is not enabled. If the `rand` feature is enabled,
155    /// the Drbg reseeds itself when needed, using `OsRng`.
156    pub fn generate_array<const LEN: usize>(&mut self) -> Result<[u8; LEN], Error> {
157        self.auto_reseed()?;
158        let mut output = [0u8; LEN];
159        self.state
160            .generate(&mut output, &[])
161            .map_err(|_| Error::UnableToGenerate)
162            .map(|()| output)
163    }
164
165    /// Returns true if a reseed is required and false otherwise.
166    pub fn reseed_required(&self) -> bool {
167        self.ctr > 512
168    }
169}
170
171/// Implementation of the [`RngCore`] trait for the [`Drbg`].
172impl RngCore for Drbg {
173    fn next_u32(&mut self) -> u32 {
174        todo!()
175    }
176
177    fn next_u64(&mut self) -> u64 {
178        todo!()
179    }
180
181    fn fill_bytes(&mut self, dest: &mut [u8]) {
182        self.generate(dest).unwrap()
183    }
184
185    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
186        self.generate(dest).map_err(rand::Error::new)
187    }
188}
189
190impl CryptoRng for Drbg {}