README: rand is not a crypto library (#1514)

Closes #1358 by documenting what Rand is not.

Co-authored-by: Dan <dan.middleton@intel.com>
This commit is contained in:
Diggory Hardy 2024-10-28 14:20:59 +00:00 committed by GitHub
parent 0c36c6ca59
commit 24b9cc38ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 130 additions and 82 deletions

View File

@ -6,20 +6,20 @@
[![API](https://img.shields.io/badge/api-master-yellow.svg)](https://rust-random.github.io/rand/rand) [![API](https://img.shields.io/badge/api-master-yellow.svg)](https://rust-random.github.io/rand/rand)
[![API](https://docs.rs/rand/badge.svg)](https://docs.rs/rand) [![API](https://docs.rs/rand/badge.svg)](https://docs.rs/rand)
Rand is a Rust library supporting random generators: Rand is a set of crates supporting (pseudo-)random generators:
- A standard RNG trait: [`rand_core::RngCore`](https://docs.rs/rand_core/latest/rand_core/trait.RngCore.html) - Built over a standard RNG trait: [`rand_core::RngCore`](https://docs.rs/rand_core/latest/rand_core/trait.RngCore.html)
- Fast implementations of the best-in-class [cryptographic](https://rust-random.github.io/book/guide-rngs.html#cryptographically-secure-pseudo-random-number-generators-csprngs) and - With fast implementations of both [strong](https://rust-random.github.io/book/guide-rngs.html#cryptographically-secure-pseudo-random-number-generators-csprngs) and
[non-cryptographic](https://rust-random.github.io/book/guide-rngs.html#basic-pseudo-random-number-generators-prngs) generators: [`rand::rngs`](https://docs.rs/rand/latest/rand/rngs/index.html), and more RNGs: [`rand_chacha`](https://docs.rs/rand_chacha), [`rand_xoshiro`](https://docs.rs/rand_xoshiro/), [`rand_pcg`](https://docs.rs/rand_pcg/), [rngs repo](https://github.com/rust-random/rngs/) [small](https://rust-random.github.io/book/guide-rngs.html#basic-pseudo-random-number-generators-prngs) generators: [`rand::rngs`](https://docs.rs/rand/latest/rand/rngs/index.html), and more RNGs: [`rand_chacha`](https://docs.rs/rand_chacha), [`rand_xoshiro`](https://docs.rs/rand_xoshiro/), [`rand_pcg`](https://docs.rs/rand_pcg/), [rngs repo](https://github.com/rust-random/rngs/)
- [`rand::rng`](https://docs.rs/rand/latest/rand/fn.rng.html) is an asymtotically-fast, reasonably secure generator available on all `std` targets - [`rand::rng`](https://docs.rs/rand/latest/rand/fn.rng.html) is an asymptotically-fast, automatically-seeded and reasonably strong generator available on all `std` targets
- Secure seeding via the [`getrandom` crate](https://crates.io/crates/getrandom) - Direct support for seeding generators from the [`getrandom` crate](https://crates.io/crates/getrandom)
Supporting random value generation and random processes: With broad support for random value generation and random processes:
- [`Standard`](https://docs.rs/rand/latest/rand/distributions/struct.Standard.html) random value generation - [`Standard`](https://docs.rs/rand/latest/rand/distributions/struct.Standard.html) random value sampling,
- Ranged [`Uniform`](https://docs.rs/rand/latest/rand/distributions/struct.Uniform.html) number generation for many types [`Uniform`](https://docs.rs/rand/latest/rand/distributions/struct.Uniform.html)-ranged value sampling
- A flexible [`distributions`](https://docs.rs/rand/*/rand/distr/index.html) module and [more](https://docs.rs/rand/latest/rand/distr/index.html)
- Samplers for a large number of random number distributions via our own - Samplers for a large number of non-uniform random number distributions via our own
[`rand_distr`](https://docs.rs/rand_distr) and via [`rand_distr`](https://docs.rs/rand_distr) and via
the [`statrs`](https://docs.rs/statrs/0.13.0/statrs/) the [`statrs`](https://docs.rs/statrs/0.13.0/statrs/)
- Random processes (mostly choose and shuffle) via [`rand::seq`](https://docs.rs/rand/latest/rand/seq/index.html) traits - Random processes (mostly choose and shuffle) via [`rand::seq`](https://docs.rs/rand/latest/rand/seq/index.html) traits
@ -28,19 +28,23 @@ All with:
- [Portably reproducible output](https://rust-random.github.io/book/portability.html) - [Portably reproducible output](https://rust-random.github.io/book/portability.html)
- `#[no_std]` compatibility (partial) - `#[no_std]` compatibility (partial)
- *Many* performance optimisations - *Many* performance optimisations thanks to contributions from the wide
user-base
It's also worth pointing out what Rand *is not*: Rand **is not**:
- Small. Most low-level crates are small, but the higher-level `rand` and - Small (LOC). Most low-level crates are small, but the higher-level `rand`
`rand_distr` each contain a lot of functionality. and `rand_distr` each contain a lot of functionality.
- Simple (implementation). We have a strong focus on correctness, speed and flexibility, but - Simple (implementation). We have a strong focus on correctness, speed and flexibility, but
not simplicity. If you prefer a small-and-simple library, there are not simplicity. If you prefer a small-and-simple library, there are
alternatives including [fastrand](https://crates.io/crates/fastrand) alternatives including [fastrand](https://crates.io/crates/fastrand)
and [oorandom](https://crates.io/crates/oorandom). and [oorandom](https://crates.io/crates/oorandom).
- Slow. We take performance seriously, with considerations also for set-up - A cryptography library. Rand provides functionality for generating
time of new distributions, commonly-used parameters, and parameters of the unpredictable random data (potentially applicable depending on requirements)
current sampler. but does not provide high-level cryptography functionality.
Rand is a community project and cannot provide legally-binding guarantees of
security.
Documentation: Documentation:

View File

@ -1,31 +1,46 @@
# Security Policy # Security Policy
## No guarantees ## Disclaimer
Support is provided on a best-effort bases only. Rand is a community project and cannot provide legally-binding guarantees of
No binding guarantees can be provided. security.
## Security premises ## Security premises
Rand provides the trait `rand_core::CryptoRng` aka `rand::CryptoRng` as a marker ### Marker traits
trait. Generators implementing `RngCore` *and* `CryptoRng`, and given the
additional constraints that: Rand provides the marker traits `CryptoRng`, `TryCryptoRng` and
`CryptoBlockRng`. Generators implementing one of these traits and used in a way
which meets the following additional constraints:
- Instances of seedable RNGs (those implementing `SeedableRng`) are - Instances of seedable RNGs (those implementing `SeedableRng`) are
constructed with cryptographically secure seed values constructed with cryptographically secure seed values
- The state (memory) of the RNG and its seed value are not be exposed - The state (memory) of the RNG and its seed value are not exposed
are expected to provide the following: are expected to provide the following:
- An attacker can gain no advantage over chance (50% for each bit) in - An attacker cannot predict the output with more accuracy than what would be
predicting the RNG output, even with full knowledge of all prior outputs. expected through pure chance since each possible output value of any method
under the above traits which generates output bytes (including
`RngCore::next_u32`, `RngCore::next_u64`, `RngCore::fill_bytes`,
`TryRngCore::try_next_u32`, `TryRngCore::try_next_u64`,
`TryRngCore::try_fill_bytes` and `BlockRngCore::generate`) should be equally
likely
- Knowledge of prior outputs from the generator does not aid an attacker in
predicting future outputs
For some RNGs, notably `OsRng`, `ThreadRng` and those wrapped by `ReseedingRng`, ### Specific generators
we provide limited mitigations against side-channel attacks:
- After the state (memory) of an RNG is leaked, there is an upper-bound on the `OsRng` is a stateless "generator" implemented via [getrandom]. As such, it has
number of bits of output by the RNG before prediction of output by an no possible state to leak and cannot be improperly seeded.
observer again becomes computationally-infeasible
`ThreadRng` will periodically reseed itself, thus placing an upper bound on the
number of bits of output from an instance before any advantage an attacker may
have gained through state-compromising side-channel attacks is lost.
[getrandom]: https://crates.io/crates/getrandom
### Distributions
Additionally, derivations from such an RNG (including the `Rng` trait, Additionally, derivations from such an RNG (including the `Rng` trait,
implementations of the `Distribution` trait, and `seq` algorithms) should not implementations of the `Distribution` trait, and `seq` algorithms) should not

View File

@ -11,10 +11,9 @@
use crate::{TryCryptoRng, TryRngCore}; use crate::{TryCryptoRng, TryRngCore};
use getrandom::getrandom; use getrandom::getrandom;
/// A random number generator that retrieves randomness from the /// An interface over the operating-system's random data source
/// operating system.
/// ///
/// This is a zero-sized struct. It can be freely constructed with `OsRng`. /// This is a zero-sized struct. It can be freely constructed with just `OsRng`.
/// ///
/// The implementation is provided by the [getrandom] crate. Refer to /// The implementation is provided by the [getrandom] crate. Refer to
/// [getrandom] documentation for details. /// [getrandom] documentation for details.
@ -32,7 +31,8 @@ use getrandom::getrandom;
/// ///
/// After the first successful call, it is highly unlikely that failures or /// After the first successful call, it is highly unlikely that failures or
/// significant delays will occur (although performance should be expected to /// significant delays will occur (although performance should be expected to
/// be much slower than a user-space PRNG). /// be much slower than a user-space
/// [PRNG](https://rust-random.github.io/book/guide-gen.html#pseudo-random-number-generators)).
/// ///
/// # Usage example /// # Usage example
/// ``` /// ```

View File

@ -11,17 +11,7 @@
//! //!
//! [`Uniform`] is the standard distribution to sample uniformly from a range; //! [`Uniform`] is the standard distribution to sample uniformly from a range;
//! e.g. `Uniform::new_inclusive(1, 6).unwrap()` can sample integers from 1 to 6, like a //! e.g. `Uniform::new_inclusive(1, 6).unwrap()` can sample integers from 1 to 6, like a
//! standard die. [`Rng::random_range`] supports any type supported by [`Uniform`]. //! standard die. [`Rng::random_range`] is implemented over [`Uniform`].
//!
//! This distribution is provided with support for several primitive types
//! (all integer and floating-point types) as well as [`std::time::Duration`],
//! and supports extension to user-defined types via a type-specific *back-end*
//! implementation.
//!
//! The types [`UniformInt`], [`UniformFloat`] and [`UniformDuration`] are the
//! back-ends supporting sampling from primitive integer and floating-point
//! ranges as well as from [`std::time::Duration`]; these types do not normally
//! need to be used directly (unless implementing a derived back-end).
//! //!
//! # Example usage //! # Example usage
//! //!
@ -151,26 +141,38 @@ use serde::{Deserialize, Serialize};
/// Sample values uniformly between two bounds. /// Sample values uniformly between two bounds.
/// ///
/// # Construction
///
/// [`Uniform::new`] and [`Uniform::new_inclusive`] construct a uniform /// [`Uniform::new`] and [`Uniform::new_inclusive`] construct a uniform
/// distribution sampling from the given range; these functions may do extra /// distribution sampling from the given `low` and `high` limits. `Uniform` may
/// work up front to make sampling of multiple values faster. If only one sample /// also be constructed via [`TryFrom`] as in `Uniform::try_from(1..=6).unwrap()`.
/// from the range is required, [`Rng::random_range`] can be more efficient. ///
/// Constructors may do extra work up front to allow faster sampling of multiple
/// values. Where only a single sample is required it is suggested to use
/// [`Rng::random_range`] or one of the `sample_single` methods instead.
/// ///
/// When sampling from a constant range, many calculations can happen at /// When sampling from a constant range, many calculations can happen at
/// compile-time and all methods should be fast; for floating-point ranges and /// compile-time and all methods should be fast; for floating-point ranges and
/// the full range of integer types, this should have comparable performance to /// the full range of integer types, this should have comparable performance to
/// the `Standard` distribution. /// the `Standard` distribution.
/// ///
/// Steps are taken to avoid bias, which might be present in naive /// # Provided implementations
/// implementations; for example `rng.gen::<u8>() % 170` samples from the range
/// `[0, 169]` but is twice as likely to select numbers less than 85 than other
/// values. Further, the implementations here give more weight to the high-bits
/// generated by the RNG than the low bits, since with some RNGs the low-bits
/// are of lower quality than the high bits.
/// ///
/// Implementations must sample in `[low, high)` range for /// - `char` ([`UniformChar`]): samples a range over the implementation for `u32`
/// `Uniform::new(low, high)`, i.e., excluding `high`. In particular, care must /// - `f32`, `f64` ([`UniformFloat`]): samples approximately uniformly within a
/// be taken to ensure that rounding never results values `< low` or `>= high`. /// range; bias may be present in the least-significant bit of the significand
/// and the limits of the input range may be sampled even when an open
/// (exclusive) range is used
/// - Integer types ([`UniformInt`]) may show a small bias relative to the
/// expected uniform distribution of output. In the worst case, bias affects
/// 1 in `2^n` samples where n is 56 (`i8` and `u8`), 48 (`i16` and `u16`), 96
/// (`i32` and `u32`), 64 (`i64` and `u64`), 128 (`i128` and `u128`).
/// The `unbiased` feature flag fixes this bias.
/// - `usize` ([`UniformUsize`]) is handled specially, using the `u32`
/// implementation where possible to enable portable results across 32-bit and
/// 64-bit CPU architectures.
/// - `Duration` ([`UniformDuration`]): samples a range over the implementation
/// for `u32` or `u64`
/// ///
/// # Example /// # Example
/// ///

View File

@ -29,14 +29,17 @@ use serde::{Deserialize, Serialize};
/// ///
/// # Implementation notes /// # Implementation notes
/// ///
/// Instead of generating a float in the `[0, 1)` range using [`Standard`], the /// `UniformFloat` implementations convert RNG output to a float in the range
/// `UniformFloat` implementation converts the output of an PRNG itself. This /// `[1, 2)` via transmutation, map this to `[0, 1)`, then scale and translate
/// way one or two steps can be optimized out. /// to the desired range. Values produced this way have what equals 23 bits of
/// random digits for an `f32` and 52 for an `f64`.
/// ///
/// The floats are first converted to a value in the `[1, 2)` interval using a /// # Bias and range errors
/// transmute-based method, and then mapped to the expected range with a ///
/// multiply and addition. Values produced this way have what equals 23 bits of /// Bias may be expected within the least-significant bit of the significand.
/// random digits for an `f32`, and 52 for an `f64`. /// It is not guaranteed that exclusive limits of a range are respected; i.e.
/// when sampling the range `[a, b)` it is not guaranteed that `b` is never
/// sampled.
/// ///
/// [`new`]: UniformSampler::new /// [`new`]: UniformSampler::new
/// [`new_inclusive`]: UniformSampler::new_inclusive /// [`new_inclusive`]: UniformSampler::new_inclusive

View File

@ -58,6 +58,13 @@ use serde::{Deserialize, Serialize};
/// multiply by `range`, the result is in the high word. Then comparing the low /// multiply by `range`, the result is in the high word. Then comparing the low
/// word against `zone` makes sure our distribution is uniform. /// word against `zone` makes sure our distribution is uniform.
/// ///
/// # Bias
///
/// Unless the `unbiased` feature flag is used, outputs may have a small bias.
/// In the worst case, bias affects 1 in `2^n` samples where n is
/// 56 (`i8` and `u8`), 48 (`i16` and `u16`), 96 (`i32` and `u32`), 64 (`i64`
/// and `u64`), 128 (`i128` and `u128`).
///
/// [`Uniform`]: super::Uniform /// [`Uniform`]: super::Uniform
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]

View File

@ -41,16 +41,36 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64;
/// A reference to the thread-local generator /// A reference to the thread-local generator
/// ///
/// This type is a reference to a lazily-initialized thread-local generator. /// This type is a reference to a lazily-initialized thread-local generator.
/// An instance can be obtained via [`rand::rng()`][crate::rng())] or via /// An instance can be obtained via [`rand::rng()`][crate::rng()] or via
/// `ThreadRng::default()`. /// [`ThreadRng::default()`].
/// The handle cannot be passed between threads (is not `Send` or `Sync`). /// The handle cannot be passed between threads (is not `Send` or `Sync`).
/// ///
/// `ThreadRng` uses the same CSPRNG as [`StdRng`], ChaCha12. As with /// # Security
/// [`StdRng`], the algorithm may be changed, subject to reasonable expectations
/// of security and performance.
/// ///
/// `ThreadRng` is automatically seeded from [`OsRng`] with periodic reseeding /// Security must be considered relative to a threat model and validation
/// (every 64 kiB — see [`ReseedingRng`] documentation for details). /// requirements. The Rand project can provide no guarantee of fitness for
/// purpose. The design criteria for `ThreadRng` are as follows:
///
/// - Automatic seeding via [`OsRng`] and periodically thereafter (see
/// ([`ReseedingRng`] documentation). Limitation: there is no automatic
/// reseeding on process fork (see [below](#fork)).
/// - A rigorusly analyzed, unpredictable (cryptographic) pseudo-random generator
/// (see [the book on security](https://rust-random.github.io/book/guide-rngs.html#security)).
/// The currently selected algorithm is ChaCha (12-rounds).
/// See also [`StdRng`] documentation.
/// - Not to leak internal state through [`Debug`] or serialization
/// implementations.
/// - No further protections exist to in-memory state. In particular, the
/// implementation is not required to zero memory on exit (of the process or
/// thread). (This may change in the future.)
/// - Be fast enough for general-purpose usage. Note in particular that
/// `ThreadRng` is designed to be a "fast, reasonably secure generator"
/// (where "reasonably secure" implies the above criteria).
///
/// We leave it to the user to determine whether this generator meets their
/// security requirements. For an alternative, see [`OsRng`].
///
/// # Fork
/// ///
/// `ThreadRng` is not automatically reseeded on fork. It is recommended to /// `ThreadRng` is not automatically reseeded on fork. It is recommended to
/// explicitly call [`ThreadRng::reseed`] immediately after a fork, for example: /// explicitly call [`ThreadRng::reseed`] immediately after a fork, for example:
@ -68,13 +88,6 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64;
/// from an interrupt (e.g. a fork handler) unless it can be guaranteed that no /// from an interrupt (e.g. a fork handler) unless it can be guaranteed that no
/// other method on the same `ThreadRng` is currently executing. /// other method on the same `ThreadRng` is currently executing.
/// ///
/// Security must be considered relative to a threat model and validation
/// requirements. `ThreadRng` attempts to meet basic security considerations
/// for producing unpredictable random numbers: use a CSPRNG, use a
/// recommended platform-specific seed ([`OsRng`]), and avoid
/// leaking internal secrets e.g. via [`Debug`] implementation or serialization.
/// Memory is not zeroized on drop.
///
/// [`ReseedingRng`]: crate::rngs::ReseedingRng /// [`ReseedingRng`]: crate::rngs::ReseedingRng
/// [`StdRng`]: crate::rngs::StdRng /// [`StdRng`]: crate::rngs::StdRng
#[derive(Clone)] #[derive(Clone)]
@ -115,9 +128,9 @@ thread_local!(
} }
); );
/// Access a local, pre-initialized generator /// Access a fast, pre-initialized generator
/// ///
/// This is a reasonably fast unpredictable thread-local instance of [`ThreadRng`]. /// This is a handle to the local [`ThreadRng`].
/// ///
/// See also [`crate::rngs`] for alternatives. /// See also [`crate::rngs`] for alternatives.
/// ///
@ -139,6 +152,10 @@ thread_local!(
/// println!("A simulated die roll: {}", rng.random_range(1..=6)); /// println!("A simulated die roll: {}", rng.random_range(1..=6));
/// # } /// # }
/// ``` /// ```
///
/// # Security
///
/// Refer to [`ThreadRng#Security`].
pub fn rng() -> ThreadRng { pub fn rng() -> ThreadRng {
let rng = THREAD_RNG_KEY.with(|t| t.clone()); let rng = THREAD_RNG_KEY.with(|t| t.clone());
ThreadRng { rng } ThreadRng { rng }