Skip to content

Simple seedable insecure random number generation, stable across Rust versions #394

@joshtriplett

Description

@joshtriplett

API design partially based on discussions with @BartMassey. Revised based on feedback, in particular from @Amanieu and @joboet.

Proposal

Problem statement

People regularly want to generate random numbers for a variety of use cases. Doing so currently requires using a crate from crates.io. rand is the 6th most downloaded crate on crates.io, and fastrand is quite popular as well. Many, many people over the years have expressed frustration that random number generation is not available in the standard library (despite the standard library using randomness internally for HashMap).

There are multiple reasons why we have not historically added this capability. Primarily, there are three capabilities people want, and those capabilities seem to present a "pick any two" constraint:

  • Secure random number generation
  • Seedable random number generation
  • Stability between versions of Rust when seeded

These constraints arise from the possibility of a secure random number generator potentially requiring updates for security reasons. Changing the random number generator would result in different sequences for the same seed.

In addition to that primary constraint, there have also been design difficulties: there are numerous pieces of additional functionality people may want surrounding random number generation, which makes any proposal for it subject to massive scope creep and bikeshed painting. Most notably: users of random numbers may want to represent the state of the RNG explicitly as something they can pass around, or implicitly as global state for simplicity.

This ACP proposes a solution that aims to be as simple as possible, satisfy all the stated constraints on the problem, and allow for future expansion if desired. This ACP handles the "pick any two" constraint above by providing a seedable random source that is explicitly identified as insecure. This will allow us to keep the insecure seedable generator the same across Rust versions and targets.

This ACP builds on the accepted ACP 393, which added an RNG that is secure but does not guarantee identical output across Rust versions.

Motivating examples or use cases

  • Generating randomness for games (e.g. rolling dice).
    • Allowing "replay" of such games by providing a seed
  • Randomly shuffling a list
  • Randomly sampling elements from a set
  • Random test case generation / fuzz testing
    • Allowing reproduction of such test cases by providing a seed

Solution sketch

This builds upon the APIs added in ACP 393, and only specifies the new APIs.

This would live in the core::random module.

/// A seeded, insecure random number generator.
///
/// This source uses the ChaCha CSPRNG, but does not claim to be hardened for security; for secure
/// randomness use `DefaultRandomSource` or another `RandomSource`.
///
/// This source does not attempt to protect against `fork`, VM forks, or similar; if the process or
/// VM forks, this source will return the same series of outputs in both forks.
pub struct DeterministicRandomSource { /* no public fields */ }

impl DeterministicRandomSource {
    /// Create a seeded random source from the specified seed.
    ///
    /// Two instances of this source created from the same seed will produce the same series of
    /// outputs, even on different Rust versions or different targets.
    ///
    /// This generator makes no promises about security, and will compromise security for determinism
    /// if the two come into conflict. Do not use this for secure randomness.
    pub fn from_seed(seed: [u8; 32]) -> Self { /* ... */ }

    /// Get the original seed of this random source.
    // Unresolved question: does the generator need to store the seed for purposes other than this
    // method? If not, do we want this, or should people have to store it separately?
    pub fn seed(&self) -> [u8; 32] { /* ... */ }
}

impl RandomSource for DeterministicRandomSource { /* ... */ }

// Note that the Debug impl does not expose the seed or internal state.
impl Debug for DeterministicRandomSource { /* ... */ }

// A cloned copy of a random source will return the same series of outputs.
//
// The `DeterministicRandomSource` does not implement `Copy`, so that producing two sources with the
// same outputs requires an explicit clone.
impl Clone for DeterministicRandomSource { /* ... */ }

This ACP proposes using a simple implementation of a ChaCha-based RNG.

The seeded insecure random number generator, given the same seed, will provide the same sequence of random numbers, on all targets.

Alternatives

We could avoid providing seeded random number generation at all, and refer people who need seeded random number generation to external crates.

What happens now?

This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.

Possible responses

The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):

  • We think this problem seems worth solving, and the standard library might be the right place to solve it.
  • We think that this probably doesn't belong in the standard library.

Second, if there's a concrete solution:

  • We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
  • We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ACP-acceptedAPI Change Proposal is accepted (seconded with no objections)T-libs-apiapi-change-proposalA proposal to add or alter unstable APIs in the standard libraries

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions