Module crypto/random

crypto/random
Stability: unstable — the correctness work is done (rejection sampling in `random_range`, a 53-bit `random_f64`, and a `getrandom` loop that no longer accepts a short read: `plans/archive/STD_API_AUDIT.md` C5), and `tests/crypto/random.test.yo` covers the bounds and the format. What is not settled is how failure is reported. This module THROWS on an entropy failure and PANICS on an empty `random_range` — two failure modes in one call — while `std/rand`'s `rand_u32` / `rand_range` take no `exn` at all, so the same question is answered two ways in `std`. `uuid_v4` also hands back a `String` rather than a `Uuid` type, so a caller that needs the bytes back has to re-parse its own output; std has no `Uuid`. Freezing needs both calls made. — stable modules only change additively; this one may still change.

Cryptographically secure random number generation. Uses OS entropy sources (getrandom on Linux, arc4random_buf on macOS, BCryptGenRandom on Windows, getentropy on WASI).

Example

{ random_u64, uuid_v4 } :: import("std/crypto/random");

id := uuid_v4(exn);  // "550e8400-e29b-41d4-a716-446655440000"

Every function here takes an exn : Exception: reading OS entropy can fail, and a CSPRNG that silently returns a weak value on failure is worse than one that stops. std/rand is the unseeded-quality counterpart for simulations and shuffles — it is faster, needs no exn, and is NOT for keys, tokens or nonces.

Stability

unstable — the correctness work is done (rejection sampling in random_range, a 53-bit random_f64, and a getrandom loop that no longer accepts a short read: plans/archive/STD_API_AUDIT.md C5), and tests/crypto/random.test.yo covers the bounds and the format. What is not settled is how failure is reported. This module THROWS on an entropy failure and PANICS on an empty random_range — two failure modes in one call — while std/rand's rand_u32 / rand_range take no exn at all, so the same question is answered two ways in std. uuid_v4 also hands back a String rather than a Uuid type, so a caller that needs the bytes back has to re-parse its own output; std has no Uuid. Freezing needs both calls made.

Types

CryptoError

Errors that can occur during cryptographic random operations.

Variants

VariantFieldsDescription
Unavailable

Platform does not support secure random generation.

Othermsg: String

Other platform-specific error.

Trait Implementations

impl(generic(T : Type), where(T <: ToString), T : (ToString))
impl(generic(T : Type), where(T <: ToString), T : (ToString), Format)
format : fn(self : Self, spec : str) -> String

Render self under spec. An unrecognised spec degrades to the plain to_string() rendering rather than failing.

Parameters

NameTypeNotes
selfSelf
specstr

Returns: String

Methods
to_string : (CryptoError) fn(self : CryptoError) -> String

Parameters

NameTypeNotes
selfCryptoError

Returns: String

source : (CryptoError) fn(self : CryptoError) -> Option(dyn( + ToString))

The error that caused this one, or .None at the root of the chain.

Rust's Error::source. Defaulted to .None, so an error with nothing underneath it implements the trait by saying only what it is; a wrapper overrides it to hand back what it wrapped. Walking the chain to the root cause is not expressible yet — the returned Dyn loses the Error trait on an erased receiver, so a caller can print one link but cannot follow it (#521, issues/self-trait-in-a-return-type-loses-the-trait-on-an-erased-receiver.md).

Parameters

NameTypeNotes
selfCryptoError

Returns: Option(dyn( + ToString))

Functions

random_bytes function
fn(buf : RawSlice(u8), exn : Exception) -> unit

Fill buf with cryptographically secure random bytes from the OS — Rust's getrandom crate, or OsRng.

The whole slice is filled or the call throws: on Linux the getrandom syscall is looped until buf.len bytes have arrived (it may return short above 256 bytes) and EINTR is retried, and on WASI the request is chunked to the 256-byte random_get limit. macOS uses arc4random_buf, Windows BCryptGenRandom.

Throws CryptoError.Unavailable when the platform entropy source refuses. A short fill is never reported as success — the earlier single-shot getrandom left the tail of the buffer zeroed, which is predictable material from an apparently successful call.

This blocks only for as long as the OS needs; it is not async, so filling a very large buffer on the event-loop thread stalls it.

Parameters

NameTypeNotes
bufRawSlice(u8)
exnException

Returns: unit

random_u32 function
fn(exn : Exception) -> u32

A uniform random u32 — four OS-entropy bytes, assembled little-endian.

Parameters

NameTypeNotes
exnException

Returns: u32

random_u64 function
fn(exn : Exception) -> u64

A uniform random u64.

Two random_u32 draws, so two trips to the OS entropy source: prefer one random_bytes over a buffer when you need many values.

Parameters

NameTypeNotes
exnException

Returns: u64

random_f64 function
fn(exn : Exception) -> f64

A uniform random f64 in the half-open range [0.0, 1.0) — Rust's Rng::random::<f64>().

Built from the top 53 bits of a u64 draw, the double mantissa width, so every result is exactly representable and 1.0 is unreachable.

Parameters

NameTypeNotes
exnException

Returns: f64

random_range function
fn(r : Range(i64), exn : Exception) -> i64

A uniform random integer in the half-open range r — write it with ..: random_range(i64(10) .. i64(20), exn).

The bound is a Range(i64) rather than two i64 parameters so the half-open end is visible AT THE CALL SITE, and so this reads the same as Rng.range in std/rand.

PANICS on an empty range (r.end <= r.start), like Rng.range: a sampler must return a value FROM the range, and an empty range has none. It used to return r.start, which invented a value that was never in the range. (BTreeMap.range may yield an empty ITERATOR for an inverted window because "no elements" is a real answer there; "no number" is not.)

Uniform by REJECTION SAMPLING, so the number of entropy draws is not fixed — a draw above the largest multiple of the span is discarded and redrawn, with a retry probability below 1/2 even in the worst case. Plain draw % span would over-weight the low residues whenever the span does not divide 2^64, which is a real bias in a module advertising cryptographic security (plans/archive/STD_API_AUDIT.md C5).

Parameters

NameTypeNotes
rRange(i64)
exnException

Returns: i64

uuid_v4 function
fn(exn : Exception) -> String

A random UUID v4 as the 36-character hyphenated lowercase string "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx" — RFC 4122 §4.4.

122 bits come from random_bytes; the version nibble (4) and the two variant bits are set as the RFC requires, so two of the 128 bits are fixed. Returns a String, not a Uuid value — std has no UUID type — so a caller that needs the bytes must decode the hex again.

Parameters

NameTypeNotes
exnException

Returns: String