Skip to content

Commit

Permalink
README: rand is not a crypto library (#1514)
Browse files Browse the repository at this point in the history
Closes #1358 by documenting what Rand is not.

Co-authored-by: Dan <[email protected]>
  • Loading branch information
dhardy and dcmiddle authored Oct 28, 2024
1 parent 0c36c6c commit 24b9cc3
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 82 deletions.
40 changes: 22 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@
[![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)

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)
- 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
[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/)
- [`rand::rng`](https://docs.rs/rand/latest/rand/fn.rng.html) is an asymtotically-fast, reasonably secure generator available on all `std` targets
- Secure seeding via the [`getrandom` crate](https://crates.io/crates/getrandom)
- Built over a standard RNG trait: [`rand_core::RngCore`](https://docs.rs/rand_core/latest/rand_core/trait.RngCore.html)
- With fast implementations of both [strong](https://rust-random.github.io/book/guide-rngs.html#cryptographically-secure-pseudo-random-number-generators-csprngs) and
[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 asymptotically-fast, automatically-seeded and reasonably strong generator available on all `std` targets
- 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
- Ranged [`Uniform`](https://docs.rs/rand/latest/rand/distributions/struct.Uniform.html) number generation for many types
- A flexible [`distributions`](https://docs.rs/rand/*/rand/distr/index.html) module
- Samplers for a large number of random number distributions via our own
- [`Standard`](https://docs.rs/rand/latest/rand/distributions/struct.Standard.html) random value sampling,
[`Uniform`](https://docs.rs/rand/latest/rand/distributions/struct.Uniform.html)-ranged value sampling
and [more](https://docs.rs/rand/latest/rand/distr/index.html)
- Samplers for a large number of non-uniform random number distributions via our own
[`rand_distr`](https://docs.rs/rand_distr) and via
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
Expand All @@ -28,19 +28,23 @@ All with:

- [Portably reproducible output](https://rust-random.github.io/book/portability.html)
- `#[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
`rand_distr` each contain a lot of functionality.
- Small (LOC). Most low-level crates are small, but the higher-level `rand`
and `rand_distr` each contain a lot of functionality.
- 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
alternatives including [fastrand](https://crates.io/crates/fastrand)
and [oorandom](https://crates.io/crates/oorandom).
- Slow. We take performance seriously, with considerations also for set-up
time of new distributions, commonly-used parameters, and parameters of the
current sampler.
- A cryptography library. Rand provides functionality for generating
unpredictable random data (potentially applicable depending on requirements)
but does not provide high-level cryptography functionality.

Rand is a community project and cannot provide legally-binding guarantees of
security.

Documentation:

Expand Down
43 changes: 29 additions & 14 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,46 @@
# Security Policy

## No guarantees
## Disclaimer

Support is provided on a best-effort bases only.
No binding guarantees can be provided.
Rand is a community project and cannot provide legally-binding guarantees of
security.

## Security premises

Rand provides the trait `rand_core::CryptoRng` aka `rand::CryptoRng` as a marker
trait. Generators implementing `RngCore` *and* `CryptoRng`, and given the
additional constraints that:
### Marker traits

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
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:

- An attacker can gain no advantage over chance (50% for each bit) in
predicting the RNG output, even with full knowledge of all prior outputs.
- An attacker cannot predict the output with more accuracy than what would be
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

### Specific generators

`OsRng` is a stateless "generator" implemented via [getrandom]. As such, it has
no possible state to leak and cannot be improperly seeded.

`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.

For some RNGs, notably `OsRng`, `ThreadRng` and those wrapped by `ReseedingRng`,
we provide limited mitigations against side-channel attacks:
[getrandom]: https://crates.io/crates/getrandom

- After the state (memory) of an RNG is leaked, there is an upper-bound on the
number of bits of output by the RNG before prediction of output by an
observer again becomes computationally-infeasible
### Distributions

Additionally, derivations from such an RNG (including the `Rng` trait,
implementations of the `Distribution` trait, and `seq` algorithms) should not
Expand Down
8 changes: 4 additions & 4 deletions rand_core/src/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@
use crate::{TryCryptoRng, TryRngCore};
use getrandom::getrandom;

/// A random number generator that retrieves randomness from the
/// operating system.
/// An interface over the operating-system's random data source
///
/// 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
/// [getrandom] documentation for details.
Expand All @@ -32,7 +31,8 @@ use getrandom::getrandom;
///
/// After the first successful call, it is highly unlikely that failures or
/// 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
/// ```
Expand Down
48 changes: 25 additions & 23 deletions src/distr/uniform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,7 @@
//!
//! [`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
//! standard die. [`Rng::random_range`] supports any type supported by [`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).
//! standard die. [`Rng::random_range`] is implemented over [`Uniform`].
//!
//! # Example usage
//!
Expand Down Expand Up @@ -151,26 +141,38 @@ use serde::{Deserialize, Serialize};

/// Sample values uniformly between two bounds.
///
/// # Construction
///
/// [`Uniform::new`] and [`Uniform::new_inclusive`] construct a uniform
/// distribution sampling from the given range; these functions may do extra
/// work up front to make sampling of multiple values faster. If only one sample
/// from the range is required, [`Rng::random_range`] can be more efficient.
/// distribution sampling from the given `low` and `high` limits. `Uniform` may
/// also be constructed via [`TryFrom`] as in `Uniform::try_from(1..=6).unwrap()`.
///
/// 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
/// 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 `Standard` distribution.
///
/// Steps are taken to avoid bias, which might be present in naive
/// 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.
/// # Provided implementations
///
/// Implementations must sample in `[low, high)` range for
/// `Uniform::new(low, high)`, i.e., excluding `high`. In particular, care must
/// be taken to ensure that rounding never results values `< low` or `>= high`.
/// - `char` ([`UniformChar`]): samples a range over the implementation for `u32`
/// - `f32`, `f64` ([`UniformFloat`]): samples approximately uniformly within a
/// 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
///
Expand Down
17 changes: 10 additions & 7 deletions src/distr/uniform_float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,17 @@ use serde::{Deserialize, Serialize};
///
/// # Implementation notes
///
/// Instead of generating a float in the `[0, 1)` range using [`Standard`], the
/// `UniformFloat` implementation converts the output of an PRNG itself. This
/// way one or two steps can be optimized out.
/// `UniformFloat` implementations convert RNG output to a float in the range
/// `[1, 2)` via transmutation, map this to `[0, 1)`, then scale and translate
/// 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
/// 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
/// random digits for an `f32`, and 52 for an `f64`.
/// # Bias and range errors
///
/// Bias may be expected within the least-significant bit of the significand.
/// 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_inclusive`]: UniformSampler::new_inclusive
Expand Down
7 changes: 7 additions & 0 deletions src/distr/uniform_int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ use serde::{Deserialize, Serialize};
/// multiply by `range`, the result is in the high word. Then comparing the low
/// 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
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
Expand Down
49 changes: 33 additions & 16 deletions src/rngs/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,36 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64;
/// A reference to the 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
/// `ThreadRng::default()`.
/// An instance can be obtained via [`rand::rng()`][crate::rng()] or via
/// [`ThreadRng::default()`].
/// The handle cannot be passed between threads (is not `Send` or `Sync`).
///
/// `ThreadRng` uses the same CSPRNG as [`StdRng`], ChaCha12. As with
/// [`StdRng`], the algorithm may be changed, subject to reasonable expectations
/// of security and performance.
/// # Security
///
/// `ThreadRng` is automatically seeded from [`OsRng`] with periodic reseeding
/// (every 64 kiB — see [`ReseedingRng`] documentation for details).
/// Security must be considered relative to a threat model and validation
/// 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
/// explicitly call [`ThreadRng::reseed`] immediately after a fork, for example:
Expand All @@ -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
/// 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
/// [`StdRng`]: crate::rngs::StdRng
#[derive(Clone)]
Expand Down Expand Up @@ -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.
///
Expand All @@ -139,6 +152,10 @@ thread_local!(
/// println!("A simulated die roll: {}", rng.random_range(1..=6));
/// # }
/// ```
///
/// # Security
///
/// Refer to [`ThreadRng#Security`].
pub fn rng() -> ThreadRng {
let rng = THREAD_RNG_KEY.with(|t| t.clone());
ThreadRng { rng }
Expand Down

0 comments on commit 24b9cc3

Please sign in to comment.