Skip to main content

mod_rand/
lib.rs

1//! # mod-rand
2//!
3//! Tiered random number generation for Rust. Zero dependencies. Pick
4//! the tier appropriate to your threat model.
5//!
6//! ## Tiers
7//!
8//! Each tier is also available under a plain name. The plain names
9//! are the same modules, so mixing them is fine.
10//!
11//! - **[`secure`]** (same as [`tier3`]): OS-backed cryptographic
12//!   random. The only choice for tokens, passwords, keys, session
13//!   IDs, and temp names in shared directories. Calls platform
14//!   syscalls directly.
15//! - **[`unique`]** (same as [`tier2`]): process-unique values derived
16//!   from PID + time + counter. For request IDs, log correlation, and
17//!   names in private directories. Fast enough for high-frequency use.
18//!   Predictable, so never a token or a password.
19//! - **[`seeded`]** (same as [`tier1`]): deterministic seedable PRNG
20//!   (xoshiro256\*\*). For simulation, fixture data, non-security
21//!   shuffling. Always available, works in `no_std`. Reproducible
22//!   from its seed, so never a token or a password.
23//!
24//! ## Quick example
25//!
26//! Raw draws:
27//!
28//! ```
29//! use mod_rand::tier1::Xoshiro256;
30//!
31//! let mut rng = Xoshiro256::seed_from_u64(42);
32//! let n: u64 = rng.next_u64();
33//! # let _ = n;
34//! ```
35//!
36//! Bounded ranges — every tier exposes a parallel `gen_range_*` /
37//! `range_*` / `random_range_*` family that uses Lemire's "Nearly
38//! Divisionless" rejection sampling under the hood, so output is
39//! uniformly distributed with no modulo bias:
40//!
41//! ```
42//! use mod_rand::tier1::Xoshiro256;
43//!
44//! let mut rng = Xoshiro256::seed_from_u64(42);
45//!
46//! // Half-open: never returns 100.
47//! let pct: u32 = rng.gen_range_u32(0..100);
48//!
49//! // Inclusive: classic six-sided die.
50//! let d6: u32 = rng.gen_range_inclusive_u32(1..=6);
51//! # let _ = (pct, d6);
52//! ```
53//!
54//! The same `..` (half-open) vs `..=` (inclusive) convention applies
55//! on Tier 2 (free functions) and Tier 3 (free functions returning
56//! `io::Result`):
57//!
58//! ```no_run
59//! # #[cfg(all(feature = "tier2", feature = "tier3"))]
60//! # fn demo() -> std::io::Result<()> {
61//! use mod_rand::{tier2, tier3};
62//!
63//! let id  = tier2::range_inclusive_u32(1..=1_000);
64//! let key = tier3::random_range_inclusive_u64(0..=u64::MAX)?;
65//! # let _ = (id, key);
66//! # Ok(())
67//! # }
68//! ```
69//!
70//! ## Choosing a tier
71//!
72//! | Use case                                         | Module              |
73//! |--------------------------------------------------|---------------------|
74//! | Test fixtures, simulation                        | `seeded` (tier 1)   |
75//! | Request IDs, names in a private directory        | `unique` (tier 2)   |
76//! | Tokens, passwords, session IDs, keys, salts      | `secure` (tier 3)   |
77//! | Temp file or directory names in `/tmp`           | `secure` (tier 3)   |
78//!
79//! If an attacker could gain anything by predicting the value, use
80//! `secure`.
81//!
82//! ```
83//! # #[cfg(feature = "tier3")]
84//! # fn demo() -> std::io::Result<()> {
85//! let token = mod_rand::secure::random_alphanumeric(32)?;
86//! let key = mod_rand::secure::random_bytes(32)?;
87//! # let _ = (token, key);
88//! # Ok(())
89//! # }
90//! ```
91//!
92//! ## Status
93//!
94//! `1.x` is API-stable under SemVer: full xoshiro256\*\* with
95//! splitmix64 seeding (tier 1), Stafford-mix-13 over (PID + nanos +
96//! atomic counter + per-process salt) (tier 2), and direct platform
97//! syscalls for tier 3 (`getrandom(2)` / `BCryptGenRandom` /
98//! `getentropy(3)`). All bounded-range methods use Lemire's
99//! "Nearly Divisionless" rejection sampling, verified for uniformity
100//! by 1,000,000-draw chi-squared tests at the integration level.
101//! See the [CHANGELOG](https://github.com/jamesgober/mod-rand/blob/main/CHANGELOG.md)
102//! for release history.
103
104#![cfg_attr(docsrs, feature(doc_cfg))]
105#![cfg_attr(not(feature = "std"), no_std)]
106#![warn(missing_docs)]
107#![warn(rust_2018_idioms)]
108
109pub mod charsets;
110pub mod tier1;
111
112#[cfg(feature = "tier2")]
113pub mod tier2;
114
115#[cfg(feature = "tier3")]
116pub mod tier3;
117
118/// OS-backed cryptographic random: the same module as [`tier3`].
119///
120/// The plain name for the tier to use for tokens, passwords, keys,
121/// session IDs, salts, and names in shared directories.
122///
123/// ```
124/// let token = mod_rand::secure::random_hex(16)?;
125/// assert_eq!(token.len(), 32);
126/// # Ok::<(), std::io::Error>(())
127/// ```
128#[cfg(feature = "tier3")]
129#[cfg_attr(docsrs, doc(cfg(feature = "tier3")))]
130pub mod secure {
131    pub use crate::tier3::*;
132}
133
134/// Process-unique values: the same module as [`tier2`].
135///
136/// Predictable to anyone who has seen earlier values, so never a
137/// token, a password, or a key; see [`tier2`] for what it is good for.
138/// The deprecated `tier2::random_*` string functions are not
139/// re-exported here.
140///
141/// ```
142/// let id = mod_rand::unique::unique_hex(16);
143/// assert_eq!(id.len(), 16);
144/// ```
145///
146/// The tier 2 string functions warn when used, because they share
147/// names with the secure ones:
148///
149/// ```compile_fail
150/// #![deny(deprecated)]
151/// let _ = mod_rand::tier2::random_alphanumeric(16);
152/// ```
153#[cfg(feature = "tier2")]
154#[cfg_attr(docsrs, doc(cfg(feature = "tier2")))]
155pub mod unique {
156    pub use crate::tier2::{
157        range_i128, range_i16, range_i32, range_i64, range_i8, range_inclusive_i128,
158        range_inclusive_i16, range_inclusive_i32, range_inclusive_i64, range_inclusive_i8,
159        range_inclusive_isize, range_inclusive_u128, range_inclusive_u16, range_inclusive_u32,
160        range_inclusive_u64, range_inclusive_u8, range_inclusive_usize, range_isize, range_u128,
161        range_u16, range_u32, range_u64, range_u8, range_usize, unique_base32, unique_hex,
162        unique_name, unique_u64,
163    };
164}
165
166/// Deterministic seedable PRNG: the same module as [`tier1`].
167///
168/// Reproducible from its seed, so never a token, a password, or a key.
169///
170/// ```
171/// let mut rng = mod_rand::seeded::Xoshiro256::seed_from_u64(42);
172/// let _ = rng.next_u64();
173/// ```
174pub mod seeded {
175    pub use crate::tier1::*;
176}