crypto - Cryptographic primitives for OCaml#
Warning. This is a work-in-progress experimental fork of mirage-crypto. Do not use it in production. As the experiments described under Why this fork stabilise, the intent is to upstream them back to
mirage/mirage-cryptoand retire the fork. Until that happens, usemirage-cryptofrom opam directly for anything you care about.
What this fork adds#
- Vendored BearSSL constant-time AES.
lib/c/bearssl/imports theaes_ct64/ghash_ctmul64subset from BearSSL. On architectures that have no hardware-accelerated AES, this is the fallback block implementation. The vendored files are auto-generated byscripts/import.mlso the import is reproducible against a pinned upstream commit. - Pure-OCaml AES and GHASH backends.
lib/ocaml/aes_pure.mlis a bitsliced AES encryptor andlib/ocaml/ghash_pure.mla constant-time GHASH over bitwise reduction. Both run under js_of_ocaml where no C compiler is available; upstream mirage-crypto requires C stubs. The OCaml GCM path glues these together so AEAD works in the browser. - Packages renamed and Lwt/Miou bindings dropped. Downstream Eio-based services that cannot pull in Lwt or Miou. Upstream leaks those into the dependency graph under some opam resolutions; this fork renames the packages so both can coexist in the same opam switch.
The rest of the API mirrors the upstream modules with the packages renamed:
| Upstream | This fork |
|---|---|
mirage-crypto |
crypto |
mirage-crypto-rng |
crypto-rng |
mirage-crypto-rng.unix |
crypto-rng.unix |
mirage-crypto-pk |
crypto-pk |
mirage-crypto-ec |
crypto-ec |
Installation#
$ opam install nox-crypto nox-crypto-rng nox-crypto-pk nox-crypto-ec
If opam cannot find the packages, they may not yet be released in the public
opam-repository. Add the overlay repository, then install them:
$ opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git
$ opam update
$ opam install nox-crypto nox-crypto-rng nox-crypto-pk nox-crypto-ec
Usage#
Seed the RNG#
Every key-generation or random-dependent operation fails with
Unseeded_generator until a generator is installed. Call use_default ()
once at program start:
let () = Crypto_rng_unix.use_default ()
On Linux this reads from getrandom(2), on BSD/macOS from getentropy(3),
on Windows 10+ from BCryptGenRandom. Older Windows is unsupported.
AEAD: AES-GCM and ChaCha20-Poly1305#
let aead_roundtrip () =
let key = Crypto.AES.GCM.of_secret (Crypto_rng.generate 32) in
let nonce = Crypto_rng.generate 12 in
let adata = "headers" in
let plaintext = "secret message" in
let ciphertext =
Crypto.AES.GCM.authenticate_encrypt ~key ~nonce ~adata plaintext
in
match
Crypto.AES.GCM.authenticate_decrypt ~key ~nonce ~adata ciphertext
with
| Some recovered -> assert (recovered = plaintext)
| None -> failwith "authentication failed"
Crypto.Chacha20 and Crypto.AES.CCM16 expose the same AEAD signature.
Mismatched nonce/tag or tampered ciphertext return None -- never raise.
Ed25519 signatures#
let sign_and_verify () =
let priv, pub = Crypto_ec.Ed25519.generate () in
let msg = "approved transfer of 42 units" in
let sig_ = Crypto_ec.Ed25519.sign ~key:priv msg in
assert (Crypto_ec.Ed25519.verify ~key:pub sig_ ~msg);
(* Tampering the message invalidates the signature: *)
assert (not (Crypto_ec.Ed25519.verify ~key:pub sig_ ~msg:"changed"))
X25519 key exchange#
let x25519_shared () =
let a_priv, a_pub = Crypto_ec.X25519.gen_key () in
let b_priv, b_pub = Crypto_ec.X25519.gen_key () in
let a_shared = Crypto_ec.X25519.key_exchange a_priv b_pub in
let b_shared = Crypto_ec.X25519.key_exchange b_priv a_pub in
assert (a_shared = b_shared)
RSA#
let rsa_encrypt_decrypt () =
let priv = Crypto_pk.Rsa.generate ~bits:2048 () in
let pub = Crypto_pk.Rsa.pub_of_priv priv in
let msg = "short message" in
let ct = Crypto_pk.Rsa.PKCS1.encrypt ~key:pub msg in
match Crypto_pk.Rsa.PKCS1.decrypt ~key:priv ct with
| Some pt -> assert (pt = msg)
| None -> failwith "decrypt failed"
Use Rsa.OAEP or Rsa.PSS for new designs; Rsa.PKCS1 is kept for
compatibility with older protocols and is vulnerable to Bleichenbacher-
style oracle attacks if error paths leak timing.
Modules#
| Package | Module | Purpose |
|---|---|---|
crypto |
Crypto.AES |
AES-128/192/256 in ECB/CBC/CTR/GCM/CCM |
crypto |
Crypto.DES |
DES in ECB/CBC/CTR (legacy) |
crypto |
Crypto.Chacha20 |
ChaCha20-Poly1305 AEAD |
crypto |
Crypto.Poly1305 |
Raw Poly1305 MAC |
crypto |
Crypto.ARC4 |
RC4 stream cipher (legacy) |
crypto-rng |
Crypto_rng |
RNG interface, Fortuna, HMAC-DRBG |
crypto-rng.unix |
Crypto_rng_unix |
OS entropy seeders (getrandom, BCrypt) |
crypto-pk |
Crypto_pk.Rsa |
RSA keygen, PKCS1, OAEP, PSS |
crypto-pk |
Crypto_pk.Dsa |
FIPS 186-4 DSA |
crypto-pk |
Crypto_pk.Dh |
Finite-field Diffie-Hellman |
crypto-ec |
Crypto_ec.P256 / P384 / P521 |
NIST curves (ECDH + ECDSA) |
crypto-ec |
Crypto_ec.X25519 |
Curve25519 ECDH |
crypto-ec |
Crypto_ec.Ed25519 |
Curve25519 EdDSA |
Side-channel notes#
Inherited from upstream: RSA operations use blinding; AES operations
delegate to AES-NI when available (Crypto.accelerated lists detected
capabilities); P-256/P-384/P-521 point arithmetic uses constant-time code
generated by fiat-crypto. This fork has not been re-audited; if a
side-channel property matters to you, verify against upstream mirage-crypto
and its review history.
Why this fork#
The fork exists so we can explore the design space without churning
upstream. Two pieces of work that mirage-crypto does not ship today:
- A BearSSL-based constant-time AES fallback for platforms without AES-NI or equivalent hardware.
- Pure-OCaml AES and GHASH so AEAD works under js_of_ocaml with no C stubs.
The package-renaming and Lwt/Miou-removal is bookkeeping needed so the
fork can live alongside upstream in the same opam switch while the above
two pieces stabilise. Once they hold up under review and interop testing,
the intent is to propose upstreaming them to mirage/mirage-crypto; if
upstream agrees, this fork stops being a fork and the renamed packages
get deprecated.
No primitive algorithms are modified. Anything this fork gets wrong is upstream's fault, and anything this fork gets right is upstream's credit -- apart from the two additions above, which are the point of the fork.
Licence#
ISC. See LICENSE.md for the original mirage-crypto notice and LICENSE.md.mirage-crypto-ec and LICENSE.md.mirage-crypto-rng-mirage for upstream-specific licence text.