1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
//! A cryptographically secure source of randomness.
//!
//! The randomness is provided by a global, cryptographically secure
//! pseudorandom number generator. More specifically,
//! [`rand_chacha::ChaCha20Rng`].
//!
//! The CSPRNG is instantiated using [`lazy_static`] and hence it is initialized
//! lazily on the first request for randomness. It attempts to obtains a seed
//! from the following sources in order:
//! - `RDSEED`
//! - `RDRAND`
//! - `TSC`
//!
//! An error will be logged if the `TSC` is used as it is not a high quality
//! source of randomness.
//!
//! If a consumer requires one-off randomness, [`next_u32`], [`next_u64`], or
//! [`fill_bytes`] should be used. Otherwise, [`init_rng`] should be used to
//! seed a local PRNG, which can then be used as a source of randomness. Using a
//! local PRNG avoids contention on the global CSPRNG and allows for PRNGs
//! better suited for the task (e.g. non-crypto PRNGs).
#![no_std]
use rand_chacha::{
rand_core::{RngCore, SeedableRng},
ChaCha20Rng,
};
use spin::mutex::Mutex;
pub use rand_chacha::rand_core::Error;
lazy_static::lazy_static! {
/// The global random number generator.
///
/// This PRNG is cryptographically-secure. It can be directly accessed using
/// [`next_u32`], [`next_u64`], and [`fill_bytes`] for one-off randomness.
/// However, it should mostly be used to seed local PRNGs using
/// [`init_rng`].
///
/// Using a single global CSPRNG allows us to feed it with entropy from
/// device drivers and such.
static ref CSPRNG: Mutex<ChaCha20Rng> = {
let seed = rdseed_seed()
.or_else(rdrand_seed)
.unwrap_or_else(tsc_seed);
Mutex::new(ChaCha20Rng::from_seed(seed))
};
}
/// Tries to generate a 32 byte seed using the RDSEED x86 instruction.
fn rdseed_seed() -> Option<[u8; 32]> {
match rdrand::RdSeed::new() {
Ok(mut generator) => {
let mut seed = [0; 32];
match generator.try_fill_bytes(&mut seed) {
Ok(_) => {
log::info!("using RDSEED for CSPRNG seed");
Some(seed)
}
Err(_) => {
log::warn!("failed to generate seed from RDSEED");
None
}
}
}
Err(_) => {
log::warn!("failed to initialise RDSEED");
None
}
}
}
/// Tries to generate a 32 byte seed using the RDRAND x86 instruction.
fn rdrand_seed() -> Option<[u8; 32]> {
match rdrand::RdRand::new() {
Ok(mut generator) => {
let mut seed = [0; 32];
match generator.try_fill_bytes(&mut seed) {
Ok(_) => {
log::info!("using RDRAND for CSPRNG seed");
Some(seed)
}
Err(_) => {
log::warn!("failed to generate seed from RDRAND");
None
}
}
}
Err(_) => {
log::warn!("failed to initialise RDRAND");
None
}
}
}
/// Generates a 32 byte seed using the TSC.
///
/// The TSC isn't a high quality source of randomness and so it is only used as
/// a last resort if both RDSEED and RDRAND fail.
fn tsc_seed() -> [u8; 32] {
let mut seed = [0; 32];
for s in &mut seed {
// The last byte is the _most_ random.
*s = tsc::tsc_value().to_be_bytes().into_iter().last().unwrap();
}
// The TSC isn't a high quality source of randomness.
log::error!("using TSC for CSPRNG seed - this is not ok");
seed
}
/// Returns a random [`u32`].
///
/// Consider using [`init_rng`] if calling this function in a loop, or if you
/// don't require cryptographically secure random numbers.
pub fn next_u32() -> u32 {
let mut csprng = CSPRNG.lock();
csprng.next_u32()
}
/// Returns a random [`u64`].
///
/// Consider using [`init_rng`] if calling this function in a loop, or if you
/// don't require cryptographically secure random numbers.
pub fn next_u64() -> u64 {
let mut csprng = CSPRNG.lock();
csprng.next_u64()
}
/// Fills `dest` with random data.
///
/// Consider using [`init_rng`] if calling this function in a loop, or if you
/// don't require cryptographically secure random numbers.
pub fn fill_bytes(dest: &mut [u8]) {
let mut csprng = CSPRNG.lock();
csprng.fill_bytes(dest);
}
/// Initialises a `T` RNG.
///
/// Directly accessing the global CSPRNG can be expensive and so it is often
/// better to seed a local PRNG from the global CSPRNG. Using a local PRNG
/// also allows for much faster cryptographically insecure PRNGs to be used.
pub fn init_rng<T>() -> Result<T, Error>
where
T: SeedableRng,
{
let mut csprng = CSPRNG.lock();
T::from_rng(&mut *csprng)
}