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

Random Numbers

Two Tiers

Wraps OpenSSL’s RAND_bytes / RAND_priv_bytes. Uses the library’s built-in DRBG hierarchy automatically. No setup required.

#![allow(unused)]
fn main() {
pub struct Rand;

impl Rand {
    /// Fill `buf` with cryptographically random bytes (public randomness).
    /// Zero-copy: writes directly into caller's buffer.
    pub fn fill(buf: &mut [u8]) -> Result<(), ErrorStack>;

    /// Fill `buf` with private randomness (suitable for key material).
    /// In FIPS mode this uses a separate private DRBG.
    pub fn fill_private(buf: &mut [u8]) -> Result<(), ErrorStack>;

    /// Allocate and fill `n` random bytes.
    pub fn bytes(n: usize) -> Result<Vec<u8>, ErrorStack>;

    /// Allocate and fill `n` private random bytes.
    /// Uses RAND_priv_bytes — suitable for key material and other secret values.
    pub fn bytes_private(n: usize) -> Result<Vec<u8>, ErrorStack>;
}
}

Explicit DRBG — RandAlg + RandCtx

For FIPS compliance, DRBG hierarchy management, or prediction-resistant generation.

#![allow(unused)]
fn main() {
pub struct RandAlg { /* EVP_RAND* */ }

impl RandAlg {
    pub fn fetch(name: &CStr, props: Option<&CStr>) -> Result<Self, ErrorStack>;

    /// Fetch within an explicit library context.
    ///
    /// Use this when the DRBG must be bound to a specific provider set
    /// (e.g. a FIPS-isolated LibCtx).
    pub fn fetch_in(ctx: &Arc<LibCtx>, name: &CStr, props: Option<&CStr>)
        -> Result<Self, ErrorStack>;

    /// Security strength of the algorithm descriptor in bits.
    ///
    /// Calls `EVP_RAND_get_params` with the `"strength"` key.
    /// Useful for verifying that a fetched algorithm meets a minimum strength
    /// requirement before creating a `RandCtx` from it.
    pub fn params_strength(&self) -> Result<u32, ErrorStack>;
}

pub struct RandCtx { /* EVP_RAND_CTX* */ }

impl RandCtx {
    /// Create with optional parent DRBG (for seeding hierarchy).
    pub fn new(alg: &RandAlg, parent: Option<&RandCtx>) -> Result<Self, ErrorStack>;

    /// Instantiate (seed) the DRBG.
    ///
    /// `strength` is the requested security strength in bits.
    /// Set `prediction_resistance` to true to reseed from entropy before instantiation.
    pub fn instantiate(
        &mut self,
        strength: u32,
        prediction_resistance: bool,
        params: Option<&Params<'_>>,
    ) -> Result<(), ErrorStack>;

    /// Generate with full control over strength and prediction resistance.
    pub fn generate(&mut self, out: &mut [u8], req: &GenerateRequest<'_>)
        -> Result<(), ErrorStack>;

    /// Simple fill with default parameters (strength=256, no prediction resistance).
    ///
    /// Equivalent to calling generate with GenerateRequest::default.
    pub fn fill(&mut self, out: &mut [u8]) -> Result<(), ErrorStack>;

    pub fn strength(&self) -> u32;

    /// Current lifecycle state of this DRBG instance.
    pub fn state(&self) -> RandState;

    /// Set DRBG parameters after instantiation (e.g. reseed interval,
    /// additional input length). Write counterpart to the read-side
    /// `RandAlg::params_strength` / `EVP_RAND_get_params` pattern.
    ///
    /// Common keys: `"reseed_requests"` (u32), `"reseed_time_interval"` (u32).
    pub fn set_params(&mut self, params: &Params<'_>) -> Result<(), ErrorStack>;

    // ── Global DRBG accessors (OpenSSL 3.2+) ──────────────────────────────────

    /// Borrow the process-wide public DRBG. Does not free the pointer on drop.
    #[cfg(ossl320)]
    pub fn public() -> Result<GlobalRandCtx, ErrorStack>;

    /// Borrow the process-wide private DRBG.
    #[cfg(ossl320)]
    pub fn private_global() -> Result<GlobalRandCtx, ErrorStack>;

    /// Borrow the process-wide primary (root) DRBG.
    /// The primary DRBG seeds both the public and private DRBGs.
    #[cfg(ossl320)]
    pub fn primary() -> Result<GlobalRandCtx, ErrorStack>;
}

    #[cfg(ossl310)]
    impl Clone for RandCtx { /* EVP_RAND_CTX_up_ref */ }
}

RandCtx is Clone (via EVP_RAND_CTX_up_ref, OpenSSL 3.1.0+) but generate takes &mut self because the DRBG state changes on each call. Use a Mutex<RandCtx> for shared access.

GenerateRequest

Wraps the EVP_RAND_generate parameters that go beyond the output buffer:

#![allow(unused)]
fn main() {
pub struct GenerateRequest<'a> {
    /// Minimum security strength in bits. Default: 256.
    pub strength: u32,
    /// If true, reseed from entropy source before generating.
    /// Has a performance cost; use only when required by policy.
    pub prediction_resistance: bool,
    /// Optional additional input to mix into the generation step.
    pub additional_input: Option<&'a [u8]>,
}

impl Default for GenerateRequest<'_> {
    fn default() -> Self {
        Self { strength: 256, prediction_resistance: false, additional_input: None }
    }
}
}

RandState — DRBG Lifecycle State

#![allow(unused)]
fn main() {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RandState {
    /// Context created but not yet instantiated (seeded).
    Uninitialised,
    /// Context is seeded and ready to generate.
    Ready,
    /// Context entered an unrecoverable error state.
    Error,
    /// Unrecognised value returned by OpenSSL (forward-compat guard).
    Unknown(i32),
}
}

RandCtx::state returns RandState::Ready after a successful instantiate. Check the state before calling generate if the DRBG may have entered an error state (e.g. entropy source failure).

DRBG Algorithm Names

NameDescription
c"CTR-DRBG"NIST SP 800-90A CTR_DRBG (AES-256) — default
c"HASH-DRBG"NIST SP 800-90A Hash_DRBG (SHA-512)
c"HMAC-DRBG"NIST SP 800-90A HMAC_DRBG (SHA-256)
c"SEED-SRC"Entropy source (seeder for parent DRBG)

Examples

Simple Random Bytes

#![allow(unused)]
fn main() {
use native_ossl::rand::Rand;

// Fill a key buffer in place.
let mut key = [0u8; 32];
Rand::fill(&mut key)?;

// Allocate a random nonce.
let nonce = Rand::bytes(12)?;

// Private randomness for key material (uses RAND_priv_bytes).
let mut session_key = [0u8; 32];
Rand::fill_private(&mut session_key)?;

// Allocating variant for private randomness.
let key_bytes = Rand::bytes_private(32)?;
}

Explicit DRBG

use native_ossl::rand::{RandAlg, RandCtx, GenerateRequest};
use native_ossl::params::ParamBuilder;

let alg = RandAlg::fetch(c"CTR-DRBG", None)?;
let mut ctx = RandCtx::new(&alg, None)?;

// CTR-DRBG defaults to AES-256-CTR (256-bit strength). Many system entropy
// sources cap at 128 bits; use AES-128-CTR if instantiation fails.
let params = ParamBuilder::new()?
    .push_utf8_string(c"cipher", c"AES-128-CTR")?
    .build()?;
ctx.instantiate(128, false, Some(&params))?;

let mut buf = [0u8; 32];
ctx.fill(&mut buf)?;

Prediction-Resistant Generation

use native_ossl::rand::{RandAlg, RandCtx, GenerateRequest};

let alg = RandAlg::fetch(c"CTR-DRBG", None)?;
let mut ctx = RandCtx::new(&alg, None)?;
ctx.instantiate(128, false, None)?;

let mut buf = [0u8; 32];
let req = GenerateRequest {
    strength: 128,
    prediction_resistance: true,
    additional_input: Some(b"personalization string"),
};
ctx.generate(&mut buf, &req)?;

DRBG Hierarchy

Chain a private DRBG off the global public DRBG to avoid the SEED-SRC entropy strength limit:

use native_ossl::rand::{RandAlg, RandCtx};

// Use the global public DRBG as parent (does not free it on drop).
let parent = RandCtx::public()?;

let drbg_alg = RandAlg::fetch(c"CTR-DRBG", None)?;
let mut child = RandCtx::new(&drbg_alg, Some(&parent))?;
child.instantiate(256, false, None)?;

let mut buf = [0u8; 64];
child.fill(&mut buf)?;

Fetch within a FIPS Library Context

use native_ossl::rand::{RandAlg, RandCtx};
use std::sync::Arc;

let fips_ctx = Arc::new(native_ossl::lib_ctx::LibCtx::new()?);
// load FIPS provider into fips_ctx ...

let alg = RandAlg::fetch_in(&fips_ctx, c"CTR-DRBG", Some(c"fips=yes"))?;
let mut ctx = RandCtx::new(&alg, None)?;
ctx.instantiate(256, false, None)?;

let mut buf = [0u8; 32];
ctx.fill(&mut buf)?;

Query DRBG State

use native_ossl::rand::{RandAlg, RandCtx, RandState};

let alg = RandAlg::fetch(c"CTR-DRBG", None)?;
let mut ctx = RandCtx::new(&alg, None)?;
assert_eq!(ctx.state(), RandState::Uninitialised);

ctx.instantiate(128, false, None)?;
assert_eq!(ctx.state(), RandState::Ready);

Design Notes

  • RandCtx::primary() vs public() / private_global() — the primary DRBG is the root of the OpenSSL global DRBG hierarchy. Public and private DRBGs are re-seeded from it. Use primary() to inspect the root state or chain a custom DRBG off the hierarchy root. For generating random bytes, prefer public() or Rand::fill.
  • Rand::fill vs RandCtx — for most applications, Rand::fill and Rand::fill_private are sufficient. The explicit RandCtx API is for FIPS-validated use cases where the caller must manage the DRBG instance directly.
  • Rand::fill_private / bytes_private — in FIPS mode, public and private DRBGs are seeded independently. Use these for key material and other secret values.
  • generate takes &mut self — even though RandCtx is Clone (via up_ref), generate modifies DRBG internal state. Concurrent access without a mutex is a data race; &mut self prevents it.
  • instantiate strength parameter — the strength argument is the requested minimum security strength in bits. The DRBG may provide more than requested. For CTR-DRBG with AES-128-CTR, the maximum is 128 bits; with AES-256-CTR it is 256. When seeding from SEED-SRC directly (no parent DRBG), many OS entropy sources cap at 128 bits; in that case, configure AES-128-CTR via params and pass strength=128.
  • RandState — check ctx.state() if you suspect the DRBG may have failed (e.g. after an entropy source error). RandState::Error is unrecoverable; create a new RandCtx if this occurs.
  • Output size limitRAND_bytes accepts only int length (≤ ~2 GiB). The wrapper handles oversized requests by splitting into multiple calls internally.