Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

std.secrets

import std.secrets returns unpredictable bytes from the operating system as buffers, text tokens, and integers, with one error type, SecretsError.

Use it for session tokens, keys, nonces, and password-reset links. For repeatable simulation and sampling use std.random; the two modules share no state and no code path.

Entropy comes from the operating system. Plenty implements no random generator for secrets: native OS services supply every byte.

Policy is checked Plenty code over one native primitive, the intrinsic __io_entropy_read, which is reserved for the standard library and makes a single OS request. Linux uses getrandom(2) (glibc 2.25 or later, Linux 3.17 or later). The macOS runtime uses getentropy, with at most 256 bytes per request; Windows uses the system-preferred BCryptGenRandom generator, with at most u32::MAX bytes per request. Short counts are normal and the Plenty layer retries until its requested range is full. See platform support for compiler qualification; an entropy backend alone does not qualify a port.

import std.secrets
from std.secrets import SecretsError

def main() -> Result[(), IoError | SecretsError | OverflowError[u64]]:
    session = std.secrets.token_urlsafe(32)?
    reset_code = std.secrets.token_hex(16)?
    roll = std.secrets.below(6u64)? + 1u64
    # The values differ on every run; their shapes do not.
    print(len(session), len(reset_code), roll >= 1u64 and roll <= 6u64)?
    Ok(())
43 32 True

Functions

FunctionResultContract
fill(target: &mut Bytes, start: i64, stop: i64)Result[(), SecretsError]Overwrite target[start:stop] with secret bytes
token_bytes(count: i64)Result[Bytes, SecretsError]A new buffer of exactly count secret bytes
token_hex(count: i64)Result[str, SecretsError]count secret bytes as 2 * count lowercase hex digits
token_urlsafe(count: i64)Result[str, SecretsError]count secret bytes as unpadded base64url, ceil(4 * count / 3) characters
random_u64()Result[u64, SecretsError]A uniform u64
below(bound: u64)Result[u64, SecretsError]Uniform in [0, bound) without modulo bias; zero is EmptyRange

count is a number of bytes, not characters, and has no default: choose it for the secret’s purpose. Thirty-two bytes is ample for a bearer token. A negative count is a size that cannot be allocated, so it is Allocation(AllocError.CapacityOverflow); zero gives an empty result without consulting the system. fill follows the range rule. The text forms use the std.encoding codecs.

Failure policy

  • No fallback. If the system cannot supply entropy, the operation returns SecretsError.Entropy. It never substitutes the clock, an address, a seeded generator, or /dev/urandom.
  • Blocking. Linux waits until its kernel generator has been initialized, which matters only very early in boot. The API has no nonblocking form.
  • Short and interrupted requests are retried until the range is full. A request that reports zero bytes for a nonempty range is UnexpectedEof; a count larger than the request is InvalidProgress.
  • No partial secrets. When fill fails, the whole requested range is zero. A failed token call releases its buffer and returns only the error.
  • Scrubbing. token_hex, token_urlsafe, random_u64, and below zero their intermediate byte buffer before releasing it. The values they return, and a token_bytes buffer, are the caller’s to protect: Plenty does not zero memory on drop, and the encoder’s own intermediate buffer is not scrubbed.

Sources

The functions above are shorthand for methods on Source[R], which applies the same policy to any byte reader.

ItemContract
os()Source[OsEntropy], the operating-system source
source_from[R: Reader](inner: R)Source[R] over a std.io reader
Source[R].fill, token_bytes, token_hex, token_urlsafe, next_u64, belowAs the functions above, with self: &mut Source[R]; next_u64 is random_u64
Source[R].innerThe reader, public so its owner can inspect or replace it
os_entropy()OsEntropy, a std.io Reader whose read makes one request and may return a short count

source_from exists to test code against scripted short reads and failures, and to use a hardware device opened as a stream. The secrecy of its output is exactly that of the reader supplied; only os() is backed by the kernel generator. OsEntropy has a private field and can only be made by os_entropy() and os().

next_u64 reads eight bytes as a little-endian integer. below draws next_u64() until the value is at least 2^64 mod bound and returns it modulo bound.

Errors

pub enum SecretsError:
    OutOfRange
    EmptyRange
    Entropy(StreamError)
    Allocation(AllocError)
VariantMeaning
OutOfRangeA range outside the buffer; the source was not consulted
EmptyRangebelow(0)
Entropy(cause)The source failed; cause is the StreamError
Allocation(cause)A token or scratch buffer could not be allocated, including CapacityOverflow for a negative count

From the operating system, cause is StreamError.Io(IoError.System(code)) with the getrandom error number: for example 38 (ENOSYS) on a kernel without the call, or under a sandbox that denies it.

Ownership and threads

Targets are exclusive loans. Source[OsEntropy] and OsEntropy hold no descriptor and no buffered bytes; creating one costs nothing and they need no closing. The module has no global state, passes the ordinary worker-effect checks, and may be used from any thread. Each request goes to the kernel, so threads never share generator state in the process, and a forked child cannot repeat its parent’s values.

below validates its bound into NonZero[u64] before rejection sampling, so its remainder operations are statically infallible. Reading an eight-byte word uses one documented bounded shift-offset opt-out. Neither changes the entropy source, retry policy, zeroization or public error type.

Costs

OperationAllocatesSystem requests
fillNothingOne per 33,554,431 bytes at most; one for up to 256 bytes
token_bytes(count)One buffer of count bytesAs fill
token_hex, token_urlsafeThe token buffer, the encoded buffer, and the strAs fill
random_u64One 8-byte scratch bufferOne
belowAs random_u64, per attemptFewer than two expected

Nothing is buffered between calls, so every operation is at least one system call. To draw many small values, fill one buffer and decode it, or seed a std.random generator when the values need not be secret. Throughput has not been measured on an idle machine.

Limits

  • Linux only, through getrandom(2); there is no /dev/urandom path for kernels older than 3.17.
  • No nonblocking or deadline-bounded request.
  • No key derivation; see backlog B67. Digests, HMAC, and constant-time comparison are in std.crypto.
  • No zero-on-drop buffer type.
  • No choice or shuffle helpers; combine below with indexing.