Skip to content

Commit 24b9cc3

Browse files
dhardydcmiddle
andauthored
README: rand is not a crypto library (#1514)
Closes #1358 by documenting what Rand is not. Co-authored-by: Dan <dan.middleton@intel.com>
1 parent 0c36c6c commit 24b9cc3

File tree

7 files changed

+130
-82
lines changed

7 files changed

+130
-82
lines changed

Diff for: README.md

+22-18
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,20 @@
66
[![API](https://img.shields.io/badge/api-master-yellow.svg)](https://rust-random.github.io/rand/rand)
77
[![API](https://docs.rs/rand/badge.svg)](https://docs.rs/rand)
88

9-
Rand is a Rust library supporting random generators:
9+
Rand is a set of crates supporting (pseudo-)random generators:
1010

11-
- A standard RNG trait: [`rand_core::RngCore`](https://docs.rs/rand_core/latest/rand_core/trait.RngCore.html)
12-
- 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
13-
[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/)
14-
- [`rand::rng`](https://docs.rs/rand/latest/rand/fn.rng.html) is an asymtotically-fast, reasonably secure generator available on all `std` targets
15-
- Secure seeding via the [`getrandom` crate](https://crates.io/crates/getrandom)
11+
- Built over a standard RNG trait: [`rand_core::RngCore`](https://docs.rs/rand_core/latest/rand_core/trait.RngCore.html)
12+
- With fast implementations of both [strong](https://rust-random.github.io/book/guide-rngs.html#cryptographically-secure-pseudo-random-number-generators-csprngs) and
13+
[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/)
14+
- [`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
15+
- Direct support for seeding generators from the [`getrandom` crate](https://crates.io/crates/getrandom)
1616

17-
Supporting random value generation and random processes:
17+
With broad support for random value generation and random processes:
1818

19-
- [`Standard`](https://docs.rs/rand/latest/rand/distributions/struct.Standard.html) random value generation
20-
- Ranged [`Uniform`](https://docs.rs/rand/latest/rand/distributions/struct.Uniform.html) number generation for many types
21-
- A flexible [`distributions`](https://docs.rs/rand/*/rand/distr/index.html) module
22-
- Samplers for a large number of random number distributions via our own
19+
- [`Standard`](https://docs.rs/rand/latest/rand/distributions/struct.Standard.html) random value sampling,
20+
[`Uniform`](https://docs.rs/rand/latest/rand/distributions/struct.Uniform.html)-ranged value sampling
21+
and [more](https://docs.rs/rand/latest/rand/distr/index.html)
22+
- Samplers for a large number of non-uniform random number distributions via our own
2323
[`rand_distr`](https://docs.rs/rand_distr) and via
2424
the [`statrs`](https://docs.rs/statrs/0.13.0/statrs/)
2525
- 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:
2828

2929
- [Portably reproducible output](https://rust-random.github.io/book/portability.html)
3030
- `#[no_std]` compatibility (partial)
31-
- *Many* performance optimisations
31+
- *Many* performance optimisations thanks to contributions from the wide
32+
user-base
3233

33-
It's also worth pointing out what Rand *is not*:
34+
Rand **is not**:
3435

35-
- Small. Most low-level crates are small, but the higher-level `rand` and
36-
`rand_distr` each contain a lot of functionality.
36+
- Small (LOC). Most low-level crates are small, but the higher-level `rand`
37+
and `rand_distr` each contain a lot of functionality.
3738
- Simple (implementation). We have a strong focus on correctness, speed and flexibility, but
3839
not simplicity. If you prefer a small-and-simple library, there are
3940
alternatives including [fastrand](https://crates.io/crates/fastrand)
4041
and [oorandom](https://crates.io/crates/oorandom).
41-
- Slow. We take performance seriously, with considerations also for set-up
42-
time of new distributions, commonly-used parameters, and parameters of the
43-
current sampler.
42+
- A cryptography library. Rand provides functionality for generating
43+
unpredictable random data (potentially applicable depending on requirements)
44+
but does not provide high-level cryptography functionality.
45+
46+
Rand is a community project and cannot provide legally-binding guarantees of
47+
security.
4448

4549
Documentation:
4650

Diff for: SECURITY.md

+29-14
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,46 @@
11
# Security Policy
22

3-
## No guarantees
3+
## Disclaimer
44

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

88
## Security premises
99

10-
Rand provides the trait `rand_core::CryptoRng` aka `rand::CryptoRng` as a marker
11-
trait. Generators implementing `RngCore` *and* `CryptoRng`, and given the
12-
additional constraints that:
10+
### Marker traits
11+
12+
Rand provides the marker traits `CryptoRng`, `TryCryptoRng` and
13+
`CryptoBlockRng`. Generators implementing one of these traits and used in a way
14+
which meets the following additional constraints:
1315

1416
- Instances of seedable RNGs (those implementing `SeedableRng`) are
1517
constructed with cryptographically secure seed values
16-
- The state (memory) of the RNG and its seed value are not be exposed
18+
- The state (memory) of the RNG and its seed value are not exposed
1719

1820
are expected to provide the following:
1921

20-
- An attacker can gain no advantage over chance (50% for each bit) in
21-
predicting the RNG output, even with full knowledge of all prior outputs.
22+
- An attacker cannot predict the output with more accuracy than what would be
23+
expected through pure chance since each possible output value of any method
24+
under the above traits which generates output bytes (including
25+
`RngCore::next_u32`, `RngCore::next_u64`, `RngCore::fill_bytes`,
26+
`TryRngCore::try_next_u32`, `TryRngCore::try_next_u64`,
27+
`TryRngCore::try_fill_bytes` and `BlockRngCore::generate`) should be equally
28+
likely
29+
- Knowledge of prior outputs from the generator does not aid an attacker in
30+
predicting future outputs
31+
32+
### Specific generators
33+
34+
`OsRng` is a stateless "generator" implemented via [getrandom]. As such, it has
35+
no possible state to leak and cannot be improperly seeded.
36+
37+
`ThreadRng` will periodically reseed itself, thus placing an upper bound on the
38+
number of bits of output from an instance before any advantage an attacker may
39+
have gained through state-compromising side-channel attacks is lost.
2240

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

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

3045
Additionally, derivations from such an RNG (including the `Rng` trait,
3146
implementations of the `Distribution` trait, and `seq` algorithms) should not

Diff for: rand_core/src/os.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@
1111
use crate::{TryCryptoRng, TryRngCore};
1212
use getrandom::getrandom;
1313

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

Diff for: src/distr/uniform.rs

+25-23
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,7 @@
1111
//!
1212
//! [`Uniform`] is the standard distribution to sample uniformly from a range;
1313
//! e.g. `Uniform::new_inclusive(1, 6).unwrap()` can sample integers from 1 to 6, like a
14-
//! standard die. [`Rng::random_range`] supports any type supported by [`Uniform`].
15-
//!
16-
//! This distribution is provided with support for several primitive types
17-
//! (all integer and floating-point types) as well as [`std::time::Duration`],
18-
//! and supports extension to user-defined types via a type-specific *back-end*
19-
//! implementation.
20-
//!
21-
//! The types [`UniformInt`], [`UniformFloat`] and [`UniformDuration`] are the
22-
//! back-ends supporting sampling from primitive integer and floating-point
23-
//! ranges as well as from [`std::time::Duration`]; these types do not normally
24-
//! need to be used directly (unless implementing a derived back-end).
14+
//! standard die. [`Rng::random_range`] is implemented over [`Uniform`].
2515
//!
2616
//! # Example usage
2717
//!
@@ -151,26 +141,38 @@ use serde::{Deserialize, Serialize};
151141

152142
/// Sample values uniformly between two bounds.
153143
///
144+
/// # Construction
145+
///
154146
/// [`Uniform::new`] and [`Uniform::new_inclusive`] construct a uniform
155-
/// distribution sampling from the given range; these functions may do extra
156-
/// work up front to make sampling of multiple values faster. If only one sample
157-
/// from the range is required, [`Rng::random_range`] can be more efficient.
147+
/// distribution sampling from the given `low` and `high` limits. `Uniform` may
148+
/// also be constructed via [`TryFrom`] as in `Uniform::try_from(1..=6).unwrap()`.
149+
///
150+
/// Constructors may do extra work up front to allow faster sampling of multiple
151+
/// values. Where only a single sample is required it is suggested to use
152+
/// [`Rng::random_range`] or one of the `sample_single` methods instead.
158153
///
159154
/// When sampling from a constant range, many calculations can happen at
160155
/// compile-time and all methods should be fast; for floating-point ranges and
161156
/// the full range of integer types, this should have comparable performance to
162157
/// the `Standard` distribution.
163158
///
164-
/// Steps are taken to avoid bias, which might be present in naive
165-
/// implementations; for example `rng.gen::<u8>() % 170` samples from the range
166-
/// `[0, 169]` but is twice as likely to select numbers less than 85 than other
167-
/// values. Further, the implementations here give more weight to the high-bits
168-
/// generated by the RNG than the low bits, since with some RNGs the low-bits
169-
/// are of lower quality than the high bits.
159+
/// # Provided implementations
170160
///
171-
/// Implementations must sample in `[low, high)` range for
172-
/// `Uniform::new(low, high)`, i.e., excluding `high`. In particular, care must
173-
/// be taken to ensure that rounding never results values `< low` or `>= high`.
161+
/// - `char` ([`UniformChar`]): samples a range over the implementation for `u32`
162+
/// - `f32`, `f64` ([`UniformFloat`]): samples approximately uniformly within a
163+
/// range; bias may be present in the least-significant bit of the significand
164+
/// and the limits of the input range may be sampled even when an open
165+
/// (exclusive) range is used
166+
/// - Integer types ([`UniformInt`]) may show a small bias relative to the
167+
/// expected uniform distribution of output. In the worst case, bias affects
168+
/// 1 in `2^n` samples where n is 56 (`i8` and `u8`), 48 (`i16` and `u16`), 96
169+
/// (`i32` and `u32`), 64 (`i64` and `u64`), 128 (`i128` and `u128`).
170+
/// The `unbiased` feature flag fixes this bias.
171+
/// - `usize` ([`UniformUsize`]) is handled specially, using the `u32`
172+
/// implementation where possible to enable portable results across 32-bit and
173+
/// 64-bit CPU architectures.
174+
/// - `Duration` ([`UniformDuration`]): samples a range over the implementation
175+
/// for `u32` or `u64`
174176
///
175177
/// # Example
176178
///

Diff for: src/distr/uniform_float.rs

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

Diff for: src/distr/uniform_int.rs

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

Diff for: src/rngs/thread.rs

+33-16
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,36 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64;
4141
/// A reference to the thread-local generator
4242
///
4343
/// This type is a reference to a lazily-initialized thread-local generator.
44-
/// An instance can be obtained via [`rand::rng()`][crate::rng())] or via
45-
/// `ThreadRng::default()`.
44+
/// An instance can be obtained via [`rand::rng()`][crate::rng()] or via
45+
/// [`ThreadRng::default()`].
4646
/// The handle cannot be passed between threads (is not `Send` or `Sync`).
4747
///
48-
/// `ThreadRng` uses the same CSPRNG as [`StdRng`], ChaCha12. As with
49-
/// [`StdRng`], the algorithm may be changed, subject to reasonable expectations
50-
/// of security and performance.
48+
/// # Security
5149
///
52-
/// `ThreadRng` is automatically seeded from [`OsRng`] with periodic reseeding
53-
/// (every 64 kiB — see [`ReseedingRng`] documentation for details).
50+
/// Security must be considered relative to a threat model and validation
51+
/// requirements. The Rand project can provide no guarantee of fitness for
52+
/// purpose. The design criteria for `ThreadRng` are as follows:
53+
///
54+
/// - Automatic seeding via [`OsRng`] and periodically thereafter (see
55+
/// ([`ReseedingRng`] documentation). Limitation: there is no automatic
56+
/// reseeding on process fork (see [below](#fork)).
57+
/// - A rigorusly analyzed, unpredictable (cryptographic) pseudo-random generator
58+
/// (see [the book on security](https://rust-random.github.io/book/guide-rngs.html#security)).
59+
/// The currently selected algorithm is ChaCha (12-rounds).
60+
/// See also [`StdRng`] documentation.
61+
/// - Not to leak internal state through [`Debug`] or serialization
62+
/// implementations.
63+
/// - No further protections exist to in-memory state. In particular, the
64+
/// implementation is not required to zero memory on exit (of the process or
65+
/// thread). (This may change in the future.)
66+
/// - Be fast enough for general-purpose usage. Note in particular that
67+
/// `ThreadRng` is designed to be a "fast, reasonably secure generator"
68+
/// (where "reasonably secure" implies the above criteria).
69+
///
70+
/// We leave it to the user to determine whether this generator meets their
71+
/// security requirements. For an alternative, see [`OsRng`].
72+
///
73+
/// # Fork
5474
///
5575
/// `ThreadRng` is not automatically reseeded on fork. It is recommended to
5676
/// explicitly call [`ThreadRng::reseed`] immediately after a fork, for example:
@@ -68,13 +88,6 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64;
6888
/// from an interrupt (e.g. a fork handler) unless it can be guaranteed that no
6989
/// other method on the same `ThreadRng` is currently executing.
7090
///
71-
/// Security must be considered relative to a threat model and validation
72-
/// requirements. `ThreadRng` attempts to meet basic security considerations
73-
/// for producing unpredictable random numbers: use a CSPRNG, use a
74-
/// recommended platform-specific seed ([`OsRng`]), and avoid
75-
/// leaking internal secrets e.g. via [`Debug`] implementation or serialization.
76-
/// Memory is not zeroized on drop.
77-
///
7891
/// [`ReseedingRng`]: crate::rngs::ReseedingRng
7992
/// [`StdRng`]: crate::rngs::StdRng
8093
#[derive(Clone)]
@@ -115,9 +128,9 @@ thread_local!(
115128
}
116129
);
117130

118-
/// Access a local, pre-initialized generator
131+
/// Access a fast, pre-initialized generator
119132
///
120-
/// This is a reasonably fast unpredictable thread-local instance of [`ThreadRng`].
133+
/// This is a handle to the local [`ThreadRng`].
121134
///
122135
/// See also [`crate::rngs`] for alternatives.
123136
///
@@ -139,6 +152,10 @@ thread_local!(
139152
/// println!("A simulated die roll: {}", rng.random_range(1..=6));
140153
/// # }
141154
/// ```
155+
///
156+
/// # Security
157+
///
158+
/// Refer to [`ThreadRng#Security`].
142159
pub fn rng() -> ThreadRng {
143160
let rng = THREAD_RNG_KEY.with(|t| t.clone());
144161
ThreadRng { rng }

0 commit comments

Comments
 (0)