Page
Library
Module
Module type
Parameter
Class
Class type
Source
SecretSourceSecret key material kept outside the OCaml heap.
A t owns a fixed-length byte buffer allocated in C memory. The bytes never reside in the OCaml heap, so the garbage collector cannot copy them (minor-to-major promotion and compaction copy heap blocks and leave the old copy behind, unzeroed). The buffer is zeroized when destroy is called, when the handle is garbage-collected, and at normal program exit (wipe_all is registered with Stdlib.at_exit).
This is an experimental, unaudited pre-1.0 implementation. Its memory hardening is defense in depth, not a substitute for process isolation or hardware-backed key storage.
compare, Hashtbl.hash or Marshal (compare and Marshal raise Invalid_argument; Hashtbl.hash ignores the contents).destroy, on garbage collection of the handle and on normal exit, before its memory is ever reused.equal runs in time that depends only on the lengths.Scratch-based construction or exposure callbacks and functions whose names contain expose, unsafe or view.expose/unsafe_*.string keys and build their own key schedules in the OCaml heap.Unix._exit or a runtime fatal error: no handler runs.mlock/core-dump exclusion are best effort: they depend on resource limits and on the platform (no core-dump exclusion on macOS) and their outcome is reported by status, never assumed.ptrace, cold-boot attacks, or a compromised kernel or hypervisor.A secret. Values of this type cannot be compared, hashed or marshalled. A t may be used from any domain. Concurrent read-only operations are safe. Callers must synchronize every mutation with all other accesses to that t, and must not call destroy concurrently with an operation on it (as for Bytes). Calling destroy concurrently more than once is safe.
Raised by random when the OS has no entropy source (for example on MirageOS) and no fallback was installed with set_entropy_source.
create ?hardened n is a zero-filled secret of n bytes (n >= 0).
hardened (default false) requests the page-backed tier: guard pages, a canary, mlock, and exclusion from core dumps where the OS supports it. Each feature is best effort; check status. Cost: at least three OS pages of address space per secret. The default tier uses calloc memory with zero-on-release and is meant for many small or short-lived keys.
Raises Invalid_argument if n < 0 and Out_of_memory if allocation fails.
random ?hardened n is a secret of n bytes of OS entropy (getrandom/getentropy/BCryptGenRandom) written directly into the secret memory; no copy is made in the OCaml heap. When the platform has no OS source, the generator installed with set_entropy_source is used (it writes into a Scratch buffer that is wiped afterwards).
of_bytes ~wipe_source b copies b. With ~wipe_source:true the source is zeroized afterwards with the same primitive as destroy, including if allocation or copying raises. This cannot reach copies the GC may already have made of b (a bytes allocated in the minor heap is copied when promoted); use Scratch buffers to avoid that.
init n f creates a secret of n bytes and calls f with a temporary mutable Scratch buffer so that a producer (a PRNG, a KDF, a decoder) can initialize it. The buffer is copied into the secret, then zeroized when f returns or raises. If f retains the buffer, it observes only zeroes after init returns. Use Unsafe.init when a zero-copy initializer is required. This is subject to the major-heap compaction limitation documented by Scratch.
with_secret n f is f (create n); the secret is destroyed when f returns or raises.
with_random n f is f (random n); the random secret is destroyed when f returns or raises.
Length in bytes. The length is not considered secret. Usable after destroy.
Constant-time equality of the contents (implemented in C). Lengths are compared first with an ordinary branch; the contents comparison takes time proportional to the length and independent of the values. There is deliberately no compare and no hash.
Same as equal against an OCaml string (e.g. a received MAC tag).
Prints <secret:32B> or <secret:destroyed>, never the contents.
type lock = [ | `Lockedpages are locked in RAM
*)| `Failed of intmlock failed with this errno (ENOMEM: RLIMIT_MEMLOCK; EPERM: no IPC_LOCK)
| `Lost_on_fork| `Unsupportedthis platform cannot lock pages
*)| `Not_requestedthe secret is not hardened
*) ]Raised by require_hardening with the first requirement that was not met.
require_hardening requirements t returns t if every requirement is currently met. Otherwise it destroys t before raising Hardening_unavailable. This makes the function safe to use in a pipeline, for example random ~hardened:true 32 |> require_hardening [`Page_backed; `Locked].
type capabilities = {hardened_tier : bool;can_lock : bool;can_exclude_from_dumps : bool;can_wipe_on_fork : bool;os_random : bool;atfork : bool;zeroize_primitive : string;e.g. "explicit_bzero"
page_size : int;}What this build and platform can provide. The per-value truth is status.
memmove between secrets. Raises Invalid_argument on bounds errors.
A copy of a range (never an alias). Tier defaults to that of the source.
Zeroizes the contents now and releases or pools unviewed memory. Memory that has produced an unscoped view is instead permanently retained and is never reused. Idempotent. After this every accessor through the owner raises Destroyed. Destroy secrets as soon as they are no longer needed: relying on the GC delays the wipe until the handle is collected.
These are the only ways to get secret bytes into OCaml values.
expose t f calls f with a temporary copy of the contents in a Scratch buffer (allocated directly in the major heap, so the minor collector never duplicates it) and zeroizes the buffer when f returns or raises. f must not retain the buffer. Anything f does with the bytes (Bytes.to_string, Buffer.add_bytes, passing them to a string-keyed API) creates copies this module cannot wipe. Major-heap compaction can also leave a historical copy as documented by Scratch.
Copies into a caller-owned buffer (ideally a Scratch buffer).
A fresh immutable copy that lives in the OCaml heap until collected and cannot be wiped. Only for legacy APIs that retain their key argument. Allocated in the major heap to avoid promotion copies.
Process-wide hardening. Every feature reports its outcome; nothing is silent. Apply process-global controls during single-threaded startup. In particular, scrub_env must not race another environment access.
Destroys every live secret in the process. Registered with Stdlib.at_exit at module initialisation, so it runs after all handlers registered later (i.e. after every handler of code that uses this module). Does not run on Unix._exit, signals or runtime fatal errors. Callers must ensure that no secret operation, scoped or unscoped view access, or blocking Secret_unix I/O is in flight. In particular, quiesce worker domains before an explicit process-wide wipe. Wiped storage is released when its owning handle is finalized.
Number of allocated handles not yet finalized (diagnostics).
Number of released payload blocks held in the reuse pool (diagnostics).
Number of destroyed payload blocks that had produced an unscoped view and are therefore permanently retained (diagnostics). The library keeps parked and pooled storage reachable, so memory-leak tools report it as reachable memory rather than as a leak; this counter is how to observe the process-lifetime cost of retained unscoped views.
POSIX only (no-op elsewhere). `Keep (default): the child inherits copies of all secrets; memory locks are lost (see after_fork). `Wipe_in_child: an atfork child handler zeroizes every secret in the child; on Linux, live and subsequently created hardened secrets also get MADV_WIPEONFORK. Switching back to `Keep revokes that advice. Set the policy before starting worker domains or forking.
Call in a forked child to re-establish mlock on hardened secrets.