Random Numbers
Two Tiers
Simple API — Rand (recommended for most uses)
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
| Name | Description |
|---|---|
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(¶ms))?;
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()vspublic()/private_global()— the primary DRBG is the root of the OpenSSL global DRBG hierarchy. Public and private DRBGs are re-seeded from it. Useprimary()to inspect the root state or chain a custom DRBG off the hierarchy root. For generating random bytes, preferpublic()orRand::fill.Rand::fillvsRandCtx— for most applications,Rand::fillandRand::fill_privateare sufficient. The explicitRandCtxAPI 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.generatetakes&mut self— even thoughRandCtxisClone(viaup_ref),generatemodifies DRBG internal state. Concurrent access without a mutex is a data race;&mut selfprevents it.instantiatestrength parameter — thestrengthargument 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 fromSEED-SRCdirectly (no parent DRBG), many OS entropy sources cap at 128 bits; in that case, configureAES-128-CTRvia params and passstrength=128.RandState— checkctx.state()if you suspect the DRBG may have failed (e.g. after an entropy source error).RandState::Erroris unrecoverable; create a newRandCtxif this occurs.- Output size limit —
RAND_bytesaccepts onlyintlength (≤ ~2 GiB). The wrapper handles oversized requests by splitting into multiple calls internally.