Skip to content

Commit

Permalink
Revise RNG docs; enable small_rng by default (#1455)
Browse files Browse the repository at this point in the history
  • Loading branch information
dhardy authored May 23, 2024
1 parent defeb0c commit ba7f515
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 122 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ You may also find the [Upgrade Guide](https://rust-random.github.io/book/update.
- Rename `Rng::gen` to `Rng::random` to avoid conflict with the new `gen` keyword in Rust 2024 (#1435)
- Move all benchmarks to new `benches` crate (#1439)
- Annotate panicking methods with `#[track_caller]` (#1442, #1447)
- Enable feature `small_rng` by default (#1455)

## [0.9.0-alpha.1] - 2024-03-18
- Add the `Slice::num_choices` method to the Slice distribution (#1402)
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ features = ["small_rng", "serde1"]

[features]
# Meta-features:
default = ["std", "std_rng", "getrandom"]
default = ["std", "std_rng", "getrandom", "small_rng"]
nightly = [] # some additions requiring nightly Rust
serde1 = ["serde", "rand_core/serde1"]

Expand Down
2 changes: 1 addition & 1 deletion rand_distr/tests/pdf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#![allow(clippy::float_cmp)]

use average::Histogram;
use rand::Rng;
use rand::{Rng, SeedableRng};
use rand_distr::{Normal, SkewNormal};

const HIST_LEN: usize = 100;
Expand Down
108 changes: 45 additions & 63 deletions src/rngs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,82 +8,61 @@

//! Random number generators and adapters
//!
//! ## Background: Random number generators (RNGs)
//! This crate provides a small selection of non-[portable] generators.
//! See also [Types of generators] and [Our RNGs] in the book.
//!
//! Computers cannot produce random numbers from nowhere. We classify
//! random number generators as follows:
//! ## Generators
//!
//! - "True" random number generators (TRNGs) use hard-to-predict data sources
//! (e.g. the high-resolution parts of event timings and sensor jitter) to
//! harvest random bit-sequences, apply algorithms to remove bias and
//! estimate available entropy, then combine these bits into a byte-sequence
//! or an entropy pool. This job is usually done by the operating system or
//! a hardware generator (HRNG).
//! - "Pseudo"-random number generators (PRNGs) use algorithms to transform a
//! seed into a sequence of pseudo-random numbers. These generators can be
//! fast and produce well-distributed unpredictable random numbers (or not).
//! They are usually deterministic: given algorithm and seed, the output
//! sequence can be reproduced. They have finite period and eventually loop;
//! with many algorithms this period is fixed and can be proven sufficiently
//! long, while others are chaotic and the period depends on the seed.
//! - "Cryptographically secure" pseudo-random number generators (CSPRNGs)
//! are the sub-set of PRNGs which are secure. Security of the generator
//! relies both on hiding the internal state and using a strong algorithm.
//! This crate provides a small selection of non-[portable] random number generators:
//!
//! ## Traits and functionality
//!
//! All RNGs implement the [`RngCore`] trait, as a consequence of which the
//! [`Rng`] extension trait is automatically implemented. Secure RNGs may
//! additionally implement the [`CryptoRng`] trait.
//!
//! All PRNGs require a seed to produce their random number sequence. The
//! [`SeedableRng`] trait provides three ways of constructing PRNGs:
//!
//! - `from_seed` accepts a type specific to the PRNG
//! - `from_rng` allows a PRNG to be seeded from any other RNG
//! - `seed_from_u64` allows any PRNG to be seeded from a `u64` insecurely
//! - `from_os_rng` securely seeds a PRNG from system randomness source
//!
//! Use the [`rand_core`] crate when implementing your own RNGs.
//!
//! ## Our generators
//!
//! This crate provides several random number generators:
//!
//! - [`OsRng`] is an interface to the operating system's random number
//! source. Typically the operating system uses a CSPRNG with entropy
//! provided by a TRNG and some type of on-going re-seeding.
//! - [`OsRng`] is a stateless interface over the operating system's random number
//! source. This is typically secure with some form of periodic re-seeding.
//! - [`ThreadRng`], provided by the [`thread_rng`] function, is a handle to a
//! thread-local CSPRNG with periodic seeding from [`OsRng`]. Because this
//! thread-local generator with periodic seeding from [`OsRng`]. Because this
//! is local, it is typically much faster than [`OsRng`]. It should be
//! secure, though the paranoid may prefer [`OsRng`].
//! secure, but see documentation on [`ThreadRng`].
//! - [`StdRng`] is a CSPRNG chosen for good performance and trust of security
//! (based on reviews, maturity and usage). The current algorithm is ChaCha12,
//! which is well established and rigorously analysed.
//! [`StdRng`] provides the algorithm used by [`ThreadRng`] but without
//! periodic reseeding.
//! - [`SmallRng`] is an **insecure** PRNG designed to be fast, simple, require
//! little memory, and have good output quality.
//! [`StdRng`] is the deterministic generator used by [`ThreadRng`] but
//! without the periodic reseeding or thread-local management.
//! - [`SmallRng`] is a relatively simple, insecure generator designed to be
//! fast, use little memory, and pass various statistical tests of
//! randomness quality.
//!
//! The algorithms selected for [`StdRng`] and [`SmallRng`] may change in any
//! release and may be platform-dependent, therefore they should be considered
//! **not reproducible**.
//! release and may be platform-dependent, therefore they are not
//! [reproducible][portable].
//!
//! ### Additional generators
//!
//! ## Additional generators
//! - The [`rdrand`] crate provides an interface to the RDRAND and RDSEED
//! instructions available in modern Intel and AMD CPUs.
//! - The [`rand_jitter`] crate provides a user-space implementation of
//! entropy harvesting from CPU timer jitter, but is very slow and has
//! [security issues](https://github.com/rust-random/rand/issues/699).
//! - The [`rand_chacha`] crate provides [portable] implementations of
//! generators derived from the [ChaCha] family of stream ciphers
//! - The [`rand_pcg`] crate provides [portable] implementations of a subset
//! of the [PCG] family of small, insecure generators
//! - The [`rand_xoshiro`] crate provides [portable] implementations of the
//! [xoshiro] family of small, insecure generators
//!
//! **TRNGs**: The [`rdrand`] crate provides an interface to the RDRAND and
//! RDSEED instructions available in modern Intel and AMD CPUs.
//! The [`rand_jitter`] crate provides a user-space implementation of
//! entropy harvesting from CPU timer jitter, but is very slow and has
//! [security issues](https://github.com/rust-random/rand/issues/699).
//! For more, search [crates with the `rng` tag].
//!
//! ## Traits and functionality
//!
//! **PRNGs**: Several companion crates are available, providing individual or
//! families of PRNG algorithms. These provide the implementations behind
//! [`StdRng`] and [`SmallRng`] but can also be used directly, indeed *should*
//! be used directly when **reproducibility** matters.
//! Some suggestions are: [`rand_chacha`], [`rand_pcg`], [`rand_xoshiro`].
//! A full list can be found by searching for crates with the [`rng` tag].
//! All generators implement [`RngCore`] and thus also [`Rng`][crate::Rng].
//! See also the [Random Values] chapter in the book.
//!
//! Secure RNGs may additionally implement the [`CryptoRng`] trait.
//!
//! Use the [`rand_core`] crate when implementing your own RNGs.
//!
//! [portable]: https://rust-random.github.io/book/crate-reprod.html
//! [Types of generators]: https://rust-random.github.io/book/guide-gen.html
//! [Our RNGs]: https://rust-random.github.io/book/guide-rngs.html
//! [Random Values]: https://rust-random.github.io/book/guide-values.html
//! [`Rng`]: crate::Rng
//! [`RngCore`]: crate::RngCore
//! [`CryptoRng`]: crate::CryptoRng
Expand All @@ -94,7 +73,10 @@
//! [`rand_chacha`]: https://crates.io/crates/rand_chacha
//! [`rand_pcg`]: https://crates.io/crates/rand_pcg
//! [`rand_xoshiro`]: https://crates.io/crates/rand_xoshiro
//! [`rng` tag]: https://crates.io/keywords/rng
//! [crates with the `rng` tag]: https://crates.io/keywords/rng
//! [chacha]: https://cr.yp.to/chacha.html
//! [PCG]: https://www.pcg-random.org/
//! [xoshiro]: https://prng.di.unimi.it/

mod reseeding;
pub use reseeding::ReseedingRng;
Expand Down
119 changes: 69 additions & 50 deletions src/rngs/small.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,86 @@ type Rng = super::xoshiro256plusplus::Xoshiro256PlusPlus;
#[cfg(not(target_pointer_width = "64"))]
type Rng = super::xoshiro128plusplus::Xoshiro128PlusPlus;

/// A small-state, fast non-crypto PRNG
/// A small-state, fast, non-crypto, non-portable PRNG
///
/// `SmallRng` may be a good choice when a PRNG with small state, cheap
/// initialization, good statistical quality and good performance are required.
/// Note that depending on the application, [`StdRng`] may be faster on many
/// modern platforms while providing higher-quality randomness. Furthermore,
/// `SmallRng` is **not** a good choice when:
/// This is the "standard small" RNG, a generator with the following properties:
///
/// - Portability is required. Its implementation is not fixed. Use a named
/// generator from an external crate instead, for example [rand_xoshiro] or
/// [rand_chacha]. Refer also to
/// [The Book](https://rust-random.github.io/book/guide-rngs.html).
/// - Security against prediction is important. Use [`StdRng`] instead.
/// - Non-[portable]: any future library version may replace the algorithm
/// and results may be platform-dependent.
/// (For a small portable generator, use the [rand_pcg] or [rand_xoshiro] crate.)
/// - Non-cryptographic: output is easy to predict (insecure)
/// - [Quality]: statistically good quality
/// - Fast: the RNG is fast for both bulk generation and single values, with
/// consistent cost of method calls
/// - Fast initialization
/// - Small state: little memory usage (current state size is 16-32 bytes
/// depending on platform)
///
/// The PRNG algorithm in `SmallRng` is chosen to be efficient on the current
/// platform, without consideration for cryptography or security. The size of
/// its state is much smaller than [`StdRng`]. The current algorithm is
/// The current algorithm is
/// `Xoshiro256PlusPlus` on 64-bit platforms and `Xoshiro128PlusPlus` on 32-bit
/// platforms. Both are also implemented by the [rand_xoshiro] crate.
///
/// ## Seeding (construction)
///
/// This generator implements the [`SeedableRng`] trait. All methods are
/// suitable for seeding, but note that, even with a fixed seed, output is not
/// [portable]. Some suggestions:
///
/// 1. Seed **from an integer** via `seed_from_u64`. This uses a hash function
/// internally to yield a (typically) good seed from any input.
/// ```
/// # use rand::{SeedableRng, rngs::SmallRng};
/// let rng = SmallRng::seed_from_u64(1);
/// # let _: SmallRng = rng;
/// ```
/// 2. With a fresh seed, **direct from the OS** (implies a syscall):
/// ```
/// # use rand::{SeedableRng, rngs::SmallRng};
/// let rng = SmallRng::from_os_rng();
/// # let _: SmallRng = rng;
/// ```
/// 3. Via [`SmallRng::from_thread_rng`]:
/// ```
/// # use rand::rngs::SmallRng;
/// let rng = SmallRng::from_thread_rng();
/// ```
///
/// See also [Seeding RNGs] in the book.
///
/// ## Generation
///
/// The generators implements [`RngCore`] and thus also [`Rng`][crate::Rng].
/// See also the [Random Values] chapter in the book.
///
/// [portable]: https://rust-random.github.io/book/crate-reprod.html
/// [Seeding RNGs]: https://rust-random.github.io/book/guide-seeding.html
/// [Random Values]: https://rust-random.github.io/book/guide-values.html
/// [Quality]: https://rust-random.github.io/book/guide-rngs.html#quality
/// [`StdRng`]: crate::rngs::StdRng
/// [rand_chacha]: https://crates.io/crates/rand_chacha
/// [rand_pcg]: https://crates.io/crates/rand_pcg
/// [rand_xoshiro]: https://crates.io/crates/rand_xoshiro
/// [`rand_chacha::ChaCha8Rng`]: https://docs.rs/rand_chacha/latest/rand_chacha/struct.ChaCha8Rng.html
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SmallRng(Rng);

impl SeedableRng for SmallRng {
// Fix to 256 bits. Changing this is a breaking change!
type Seed = [u8; 32];

#[inline(always)]
fn from_seed(seed: Self::Seed) -> Self {
// With MSRV >= 1.77: let seed = *seed.first_chunk().unwrap();
const LEN: usize = core::mem::size_of::<<Rng as SeedableRng>::Seed>();
let seed = (&seed[..LEN]).try_into().unwrap();
SmallRng(Rng::from_seed(seed))
}

#[inline(always)]
fn seed_from_u64(state: u64) -> Self {
SmallRng(Rng::seed_from_u64(state))
}
}

impl RngCore for SmallRng {
#[inline(always)]
fn next_u32(&mut self) -> u32 {
Expand All @@ -61,21 +115,6 @@ impl RngCore for SmallRng {
rand_core::impl_try_rng_from_rng_core!(SmallRng);

impl SmallRng {
/// Construct an instance seeded from another `Rng`
///
/// We recommend that the source (master) RNG uses a different algorithm
/// (i.e. is not `SmallRng`) to avoid correlations between the child PRNGs.
///
/// # Example
/// ```
/// # use rand::rngs::SmallRng;
/// let rng = SmallRng::from_rng(rand::thread_rng());
/// ```
#[inline(always)]
pub fn from_rng<R: RngCore>(rng: R) -> Self {
Self(Rng::from_rng(rng))
}

/// Construct an instance seeded from the thread-local RNG
///
/// # Panics
Expand All @@ -89,24 +128,4 @@ impl SmallRng {
crate::thread_rng().fill_bytes(seed.as_mut());
SmallRng(Rng::from_seed(seed))
}

/// Construct an instance from a `u64` seed
///
/// This provides a convenient method of seeding a `SmallRng` from a simple
/// number by use of another algorithm to mutate and expand the input.
/// This is suitable for use with low Hamming Weight numbers like 0 and 1.
///
/// **Warning:** the implementation is deterministic but not portable:
/// output values may differ according to platform and may be changed by a
/// future version of the library.
///
/// # Example
/// ```
/// # use rand::rngs::SmallRng;
/// let rng = SmallRng::seed_from_u64(1);
/// ```
#[inline(always)]
pub fn seed_from_u64(state: u64) -> Self {
SmallRng(Rng::seed_from_u64(state))
}
}
43 changes: 36 additions & 7 deletions src/rngs/std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,48 @@ pub(crate) use rand_chacha::ChaCha12Core as Core;

use rand_chacha::ChaCha12Rng as Rng;

/// The standard RNG. The PRNG algorithm in `StdRng` is chosen to be efficient
/// on the current platform, to be statistically strong and unpredictable
/// (meaning a cryptographically secure PRNG).
/// A strong, fast (amortized), non-portable RNG
///
/// This is the "standard" RNG, a generator with the following properties:
///
/// - Non-[portable]: any future library version may replace the algorithm
/// and results may be platform-dependent.
/// (For a portable version, use the [rand_chacha] crate directly.)
/// - [CSPRNG]: statistically good quality of randomness and [unpredictable]
/// - Fast ([amortized](https://en.wikipedia.org/wiki/Amortized_analysis)):
/// the RNG is fast for bulk generation, but the cost of method calls is not
/// consistent due to usage of an output buffer.
///
/// The current algorithm used is the ChaCha block cipher with 12 rounds. Please
/// see this relevant [rand issue] for the discussion. This may change as new
/// evidence of cipher security and performance becomes available.
///
/// The algorithm is deterministic but should not be considered reproducible
/// due to dependence on configuration and possible replacement in future
/// library versions. For a secure reproducible generator, we recommend use of
/// the [rand_chacha] crate directly.
/// ## Seeding (construction)
///
/// This generator implements the [`SeedableRng`] trait. Any method may be used,
/// but note that `seed_from_u64` is not suitable for usage where security is
/// important. Also note that, even with a fixed seed, output is not [portable].
///
/// It is suggested to use a fresh seed **direct from the OS** as the most
/// secure and convenient option:
/// ```
/// # use rand::{SeedableRng, rngs::StdRng};
/// let rng = StdRng::from_os_rng();
/// # let _: StdRng = rng;
/// ```
///
/// See also [Seeding RNGs] in the book.
///
/// ## Generation
///
/// The generators implements [`RngCore`] and thus also [`Rng`][crate::Rng].
/// See also the [Random Values] chapter in the book.
///
/// [portable]: https://rust-random.github.io/book/crate-reprod.html
/// [Seeding RNGs]: https://rust-random.github.io/book/guide-seeding.html
/// [unpredictable]: https://rust-random.github.io/book/guide-rngs.html#security
/// [Random Values]: https://rust-random.github.io/book/guide-values.html
/// [CSPRNG]: https://rust-random.github.io/book/guide-gen.html#cryptographically-secure-pseudo-random-number-generator
/// [rand_chacha]: https://crates.io/crates/rand_chacha
/// [rand issue]: https://github.com/rust-random/rand/issues/932
#[derive(Clone, Debug, PartialEq, Eq)]
Expand Down

0 comments on commit ba7f515

Please sign in to comment.