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}