Skip to main content

cocoon_tpm_crypto/rng/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2023-2025 SUSE LLC
3// Author: Nicolai Stange <nstange@suse.de>
4
5//! Cryptographic random number generator interface traits and implementations.
6
7// Lifetimes are not obvious at first sight here, make the explicit.
8#![allow(clippy::needless_lifetimes)]
9
10use crate::utils_common::{
11    alloc::try_alloc_zeroizing_vec,
12    io_slices::{self, IoSlicesIterCommon as _},
13};
14/// Traits related to and implementation of cryptographic random number
15/// generators.
16use crate::{
17    CryptoError,
18    io_slices::{CryptoPeekableIoSlicesIter, CryptoWalkableIoSlicesMutIter, EmptyCryptoIoSlices},
19};
20
21use core::convert;
22
23mod chained;
24pub use chained::*;
25
26pub use crate::backend::rng::*;
27
28// The HashDrbg is special. Striclty speaking it belongs into the pure_rust
29// backend mod, but is used by a number of Known Answer Tests. So import from
30// here.
31#[cfg(any(not(feature = "boringssl"), test))]
32mod hash_drbg;
33#[cfg(any(not(feature = "boringssl"), test))]
34pub use hash_drbg::*;
35
36/// Error type returned by [`RngCore::generate()`](RngCore::generate).
37#[derive(Debug)]
38pub enum RngGenerateError {
39    /// A reseed is required before producing more random data.
40    ReseedRequired,
41    /// Some crypto primitive failed its operation.
42    CryptoError(CryptoError),
43}
44
45impl convert::From<convert::Infallible> for RngGenerateError {
46    fn from(value: convert::Infallible) -> Self {
47        match value {}
48    }
49}
50
51impl convert::From<CryptoError> for RngGenerateError {
52    fn from(value: CryptoError) -> Self {
53        RngGenerateError::CryptoError(value)
54    }
55}
56
57impl convert::From<RngGenerateError> for CryptoError {
58    fn from(value: RngGenerateError) -> Self {
59        match value {
60            RngGenerateError::ReseedRequired => CryptoError::RngFailure,
61            RngGenerateError::CryptoError(e) => e,
62        }
63    }
64}
65
66/// Main functionality implemented by cryptographic random number generators.
67///
68/// Note that the [Key derivation functions](crate::kdf::VariableChunkOutputKdf)
69/// commonly implement `RngCore` so that these can get used seaminglessly as the
70/// randomness source for any key generation primitives (which would then become
71/// key derivation primitives, strictly speaking).
72pub trait RngCore {
73    /// Generate random bytes.
74    ///
75    /// # Arguments:
76    ///
77    /// * `output` - Destination buffers to fill with random data.
78    /// * `additional_input` - Optional additional input to consider from the
79    ///   random number generation process. How it's used depend on the actual
80    ///   implementation, the most common cases being that the additional data
81    ///   is either not considered at all or that it's getting mixed into the
82    ///   random number generators internal state in a non-destructive manner
83    ///   before generating random output.
84    fn generate<'a, 'b, OI: CryptoWalkableIoSlicesMutIter<'a>, AII: CryptoPeekableIoSlicesIter<'b>>(
85        &mut self,
86        output: OI,
87        additional_input: Option<AII>,
88    ) -> Result<(), RngGenerateError>;
89}
90
91/// Cryptographic random number generator interface qualifying a a
92/// `dyn`-compatible trait.
93///
94/// Don't use it directly, see [`rng_dyn_dispatch_generate()`].
95pub trait RngCoreDispatchable {
96    // The output argument should get consumed as the iterator gets exhausted, but
97    // support for unsized fn params is unstable. For the time being, make the
98    // member function internal and provide the rng_dyn_dispatch_generate()
99    // helper.
100    /// Generate random bytes.
101    ///
102    /// # Arguments:
103    ///
104    /// * `output` - Destination buffers to fill with random data.
105    /// * `additional_input` - Optional additional input to consider from the
106    ///   random number generation process.
107    fn _generate<'a, 'b>(
108        &mut self,
109        output: &'a mut dyn CryptoWalkableIoSlicesMutIter<'b>,
110        additional_input: Option<&[Option<&[u8]>]>,
111    ) -> Result<(), RngGenerateError>;
112}
113
114impl<R: RngCore> RngCoreDispatchable for R {
115    fn _generate<'a, 'b>(
116        &mut self,
117        output: &'a mut dyn CryptoWalkableIoSlicesMutIter<'b>,
118        additional_input: Option<&[Option<&[u8]>]>,
119    ) -> Result<(), RngGenerateError> {
120        self.generate(
121            output,
122            additional_input.map(|additional_input| {
123                io_slices::GenericIoSlicesIter::new(additional_input.iter().filter_map(|b| b.map(Ok)), None)
124                    .map_infallible_err()
125            }),
126        )
127    }
128}
129
130/// Generate random bytes from a [random number generator `dyn`
131/// object](RngCoreDispatchable).
132///
133/// # Arguments:
134///
135/// * `output` - Destination buffers to fill with random data.
136/// * `additional_input` - Optional additional input to consider from the random
137///   number generation process. How it's used depend on the actual
138///   implementation, the most common cases being that the additional data is
139///   either not considered at all or that it's getting mixed into the random
140///   number generators internal state in a non-destructive manner before
141///   generating random output.
142pub fn rng_dyn_dispatch_generate<'a, OI: CryptoWalkableIoSlicesMutIter<'a>>(
143    rng: &mut dyn RngCoreDispatchable,
144    mut output: OI,
145    additional_input: Option<&[Option<&[u8]>]>,
146) -> Result<(), RngGenerateError> {
147    RngCoreDispatchable::_generate(rng, &mut output, additional_input)
148}
149
150/// Error type returned by
151/// [`ReseedableRngCore::reseed()`](ReseedableRngCore::reseed).
152#[derive(Debug)]
153pub enum RngReseedError {
154    CryptoError(CryptoError),
155}
156
157impl convert::From<convert::Infallible> for RngReseedError {
158    fn from(value: convert::Infallible) -> Self {
159        match value {}
160    }
161}
162
163impl convert::From<CryptoError> for RngReseedError {
164    fn from(value: CryptoError) -> Self {
165        RngReseedError::CryptoError(value)
166    }
167}
168
169/// Error type returned by
170/// [`ReseedableRngCore::reseed_from_parent()`](ReseedableRngCore::reseed_from_parent).
171#[derive(Debug)]
172pub enum RngReseedFromParentError {
173    ParentGenerateFailure(RngGenerateError),
174    CryptoError(CryptoError),
175}
176
177/// Reseedable random number generator.
178pub trait ReseedableRngCore: RngCore + Sized {
179    /// Minimum entropy data length in units of Bytes required for a reseed.
180    fn min_seed_entropy_len(&self) -> usize;
181
182    /// Reseed the random number generator.
183    ///
184    /// # Arguments:
185    ///
186    /// * `entropy` - The entropy to reseed the random number generator from.
187    /// * `additional_data` - Optional additional data to consider for the
188    ///   reseed process. How it's used depend on the actual implementation, the
189    ///   most common cases being that the additional data is either not
190    ///   considered at all or that it's getting mixed into the random number
191    ///   generators internal state alongside the `entropy`.
192    fn reseed<'a, AII: CryptoPeekableIoSlicesIter<'a>>(
193        &mut self,
194        entropy: &[u8],
195        additional_input: Option<AII>,
196    ) -> Result<(), RngReseedError>;
197
198    /// Reseed the random number generator from the random output of another
199    /// one.
200    ///
201    /// # Arguments:
202    ///
203    /// * `parent` - The random number generator to obtain fresh entropy for the
204    ///   reseed from.
205    /// * `additional_data` - Optional additional data to consider for the
206    ///   reseed process. How it's used depend on the actual implementation, the
207    ///   most common cases being that the additional data is either not
208    ///   considered at all or that it's getting mixed into the random number
209    ///   generators internal state alongside the `entropy`.
210    fn reseed_from_parent<'a, P: RngCore, AII: CryptoPeekableIoSlicesIter<'a>>(
211        &mut self,
212        parent: &mut P,
213        additional_input: Option<AII>,
214    ) -> Result<(), RngReseedFromParentError> {
215        let entropy_len = self.min_seed_entropy_len();
216        let mut entropy = try_alloc_zeroizing_vec::<u8>(entropy_len)
217            .map_err(|e| RngReseedFromParentError::CryptoError(CryptoError::from(e)))?;
218        parent
219            .generate::<_, EmptyCryptoIoSlices>(
220                &mut io_slices::SingletonIoSliceMut::new(entropy.as_mut_slice()).map_infallible_err(),
221                None,
222            )
223            .map_err(RngReseedFromParentError::ParentGenerateFailure)?;
224
225        self.reseed(entropy.as_slice(), additional_input).map_err(|e| match e {
226            RngReseedError::CryptoError(e) => RngReseedFromParentError::CryptoError(e),
227        })?;
228
229        Ok(())
230    }
231}
232
233#[cfg(not(feature = "boringssl"))]
234pub fn test_rng() -> HashDrbg {
235    extern crate alloc;
236    use super::hash;
237    use alloc::vec;
238
239    let hash_alg = hash::test_hash_alg();
240    let min_entropy_len = HashDrbg::min_seed_entropy_len(hash_alg);
241    let entropy = vec![0u8; min_entropy_len];
242    HashDrbg::instantiate(hash_alg, &entropy, None, None).unwrap()
243}
244
245#[cfg(feature = "boringssl")]
246pub fn test_rng() -> BsslRandBytesRng {
247    BsslRandBytesRng::new()
248}