[{"data":1,"prerenderedAt":4},["ShallowReactive",2],{"JYpFPqGdcs":3},"# Cryptlib\n\n*A cryptographic library for Lean.*\n\nThis library has not been audited by any third party.\n\n\n## abstract\n\nThis library is an experiment in using dependent types for representing cryptographic primitives.\nInstead of writing some cryptographic code in lean and then writing some theorems and proofs about it later,\nwe take a different approach; instead we write a dependently typed struture in lean, that represents that\ndata and the operations of the primitive with one or more proposition fields whose hypothesis is the same\nas what we would want to prove about the cryptographic primitive. As an example take a look at our NIKE definition.\nIt's got a struct field called \"commutes\" we says that the group operation is commutative, g^x^y = g^y^x.\nThis is a proof burden to anyone writing an instance of the NIKE type, say for X25519, you need to prove\nthe \"commutes\" hypothesis for your specific type instance.\n\n\n## cryptographic primitives\n\n| NIKE: Non-Interactive Key Exchange |\n|:---:|\n\nClassical NIKEs, two independent implementations of the same exchange:\n* X25519 — constant-time Montgomery ladder (RFC 7748)\n* X25519 — group formulation over Mathlib's Weierstrass-curve API (commutativity is\n  `Nat.mul_comm`, not an axiom, unlike the ladder's)\n\n| KEM: Key Encapsulation Method |\n|:---:|\n\n* X25519, adapted to KEM via hashed ElGamal (NIKE-to-KEM adapter) — both `blake2b-xof`, hpqc's\n  deployed PRF, and `sha256-v1`, a portable stand-in for implementations without BLAKE2b\n* ML-KEM-768 (FIPS 203), built from [VCVio](https://github.com/dtumad/VCV-io)'s pure-Lean\n  primitives (NTT, CBD, encoding) with our own `keygen`/`encaps`/`decaps` composition — checked\n  against the official NIST ACVP known-answer vectors (keygen, encapsulation, decapsulation\n  including implicit rejection, and both key-validity checks)\n* Hedged ML-KEM-768 (`MLKEM768Hedged`): hashes the encapsulation message `m` before use,\n  restoring a randomness-hedging step NIST dropped when standardizing ML-KEM from Kyber. Not\n  FIPS 203, so there are no standard vectors for it\n* A security-preserving KEM combiner (Giacon–Heuer–Poettering split-PRF, real BLAKE2b-256 keyed),\n  generic over any number of ingredient KEMs — instantiated as an X25519 + ML-KEM-768 hybrid,\n  cross-checked byte-for-byte against [hpqc](https://github.com/katzenpost/hpqc)'s own combiner\n  (`MLKEM768X25519`), and the same combiner over hedged ML-KEM-768\n  (`MLKEM768HedgedX25519`)\n\n| SIGN: Cryptographic Signature Scheme |\n|:---:|\n* Ed25519 (RFC 8032), plain and blinded variants\n\n| AEAD: Authenticated Encryption with Associated Data |\n|:---:|\n* AES-256-GCM-SIV (RFC 8452), the misuse-resistant mode BACAP encrypts pigeonhole boxes with,\n  built on AES-256 and POLYVAL (RFC 8452 §3)\n\n| HASH: Cryptographic Hash Function |\n|:---:|\n* SHA-512, and its truncated SHA-512/256 variant\n* SHA-256\n* BLAKE2b, parameterized over digest length and an optional key (RFC 7693's keyed mode) — used at\n  512 bits unkeyed (BACAP, HKDF) and at 256 bits both unkeyed and keyed (the KEM combiner's PRF)\n* BLAKE2b's XOF, BLAKE2Xb (blake2x.pdf) — the deployed NIKE-to-KEM adapter PRF\n\n| MAC: Message Authentication Code |\n|:---:|\n* HMAC-SHA256 (RFC 2104), Sphinx's header MAC\n\n| Stream ciphers |\n|:---:|\n* AES-256-CTR, Sphinx's header/routing-info stream cipher\n* ChaCha20 (original/Bernstein construction, 64-bit nonce), Sphinx's deterministic RNG\n\n| SPRP: wide-block cipher |\n|:---:|\n* AEZ v5, restricted to Sphinx's exact usage (τ=0, pure-SPRP mode) — encrypts Sphinx's payload\n\n| KDF: Key Derivation Function |\n|:---:|\n* HKDF-BLAKE2b-512 (RFC 5869 with BLAKE2b-512 as the hash)\n* HKDF-SHA256 (RFC 5869, Expand-only), Sphinx's `PacketKeys` derivation\n* Argon2d, Argon2i and Argon2id (RFC 9106), the memory-hard password hash; Argon2id is the one to use\n\n| PRF: Pseuodo Random Function |\n|:---:|\n* BLAKE2b XOF (`blake2b-xof`)\n* SHA256 (`sha256-v1`)\n\n| DATAstructures |\n|:---:|\n* Binary Merkle Hash Tree polymorphic over the hash function\n\n\n## cryptographic protocol components\n\n* The Sphinx cryptographic packet format: KEM Sphinx and NIKE Sphinx, configurable to any number of hops, any KEM or NIKE, any payload size. Binary compatible with the Katzenpost mixnet's golang Sphinx implementation — including with a post-quantum/classical hybrid KEM (X25519 + ML-KEM-768) as the per-hop KEM.\n\n* BACAP: Blinded Cryptographic Capability. It's like having a private distributed hash table. Useful for building messaging systems.\n\n\n## building this software\n\n```bash\nlake build\n```\n\n## testing: how to run the unit tests\n\nSome tests load known-answer test vectors from `Cryptlib/testdata/`, vendored from\n[hpqc](https://github.com/katzenpost/hpqc) and [katzenpost](https://github.com/katzenpost/katzenpost)\nto ensure binary compatibility.\n\n```bash\nmake test   # build everything and run every suite, reporting all failures\nmake help   # list every make target, including one per suite\n```\n\nOr run any suite directly:\n\n```bash\nlake exe Cryptlib.Data.test\nlake exe Cryptlib.Primitives.NIKE.test\nlake exe Cryptlib.Primitives.KEM.test\nlake exe Cryptlib.Primitives.KEM.vectors        # NIKE-to-KEM adapter vectors from hpqc\nlake exe Cryptlib.Primitives.Hash.test          # SHA-512 vectors from hpqc\nlake exe Cryptlib.Primitives.Hash.hkdf_test     # HKDF-BLAKE2b raw function vectors\nlake exe Cryptlib.Primitives.Hash.hkdf_structured_test  # HKDF structured instance vectors\nlake exe Cryptlib.Primitives.KDF.argon2_test    # Argon2 vectors from RFC 9106 and x/crypto/argon2\nlake exe Cryptlib.Primitives.Cipher.test        # AES-256-GCM-SIV vectors from hpqc\nlake exe Cryptlib.Primitives.Sign.test\nlake exe Cryptlib.Primitives.Sign.blinded_test  # blinded Ed25519 vectors from hpqc\nlake exe Cryptlib.Protocols.BACAP.test         # BACAP vectors from hpqc\nlake exe Cryptlib.Protocols.Sphinx.crypto_test      # Sphinx Hash/MAC/Stream/KDF/ChaCha20 vectors from katzenpost\nlake exe Cryptlib.Primitives.WideBlockCipher.test    # AEZ v5 (Sphinx's SPRP) vectors from katzenpost\nlake exe Cryptlib.Protocols.Sphinx.commands_test    # Sphinx routing-command wire-format vectors from katzenpost\nlake exe Cryptlib.Protocols.Sphinx.nike_selftest    # NIKE-Sphinx round-trip self-tests\nlake exe Cryptlib.Protocols.Sphinx.kem_selftest     # KEM-Sphinx round-trip self-tests\nlake exe Cryptlib.Protocols.Sphinx.nike_vectors_test  # NIKE-Sphinx full-packet vectors from katzenpost\nlake exe Cryptlib.Protocols.Sphinx.kem_vectors_test   # KEM-Sphinx full-packet vectors from katzenpost\nlake exe Cryptlib.Primitives.KEM.MLKEM.mlkem_test      # ML-KEM-768 NIST ACVP known-answer vectors\nlake exe Cryptlib.Primitives.Hash.blake2b_256_test     # BLAKE2b-256 (unkeyed and keyed) vectors from hpqc\nlake exe Cryptlib.Primitives.KEM.mlkem768_x25519_combiner_test  # X25519+ML-KEM-768 hybrid vectors from hpqc\nlake exe Cryptlib.Protocols.Sphinx.kem_hybrid_vectors_test     # KEM-Sphinx hybrid full-packet vectors from katzenpost\n```\n\nA few suites also have their own dedicated `make` target, for running just that piece:\n\n```bash\nmake test-mlkem-kat      # just the ML-KEM-768 NIST ACVP known-answer vectors\nmake test-hybrid-sphinx  # just the KEM-Sphinx round-trip self-test for the hybrid KEM\nmake test-mlkem          # both of the above, together\n```\n\n### test vector files\n\nThese JSON files in `Cryptlib/testdata/` are vendored from hpqc's or katzenpost's own\n`testvectors/cmd/generate` tools, and are binary-compatible with the corresponding upstream test\nvectors — the `Source` column below is each file's canonical path in its own repo (what\n`scripts/verify-vectors.sh` actually compares against, not a symlink or a consuming test file):\n\n| File | Primitive | Source |\n|------|-----------|--------|\n| `sha512.json` | SHA-512 | `hpqc/testvectors/primitives/sha512.json` |\n| `hkdf_blake2b.json` | HKDF-BLAKE2b-512 (RFC 5869) | `hpqc/testvectors/primitives/hkdf_blake2b.json` |\n| `aes_gcm_siv.json` | AES-256-GCM-SIV (RFC 8452) | `hpqc/testvectors/primitives/aes_gcm_siv.json` |\n| `blinded_ed25519.json` | Blinded Ed25519 signatures | `hpqc/testvectors/primitives/blinded_ed25519.json` |\n| `blake2b_256.json` | BLAKE2b-256, unkeyed and keyed (RFC 7693) | `hpqc/testvectors/primitives/blake2b_256.json` |\n| `adapter_test_vectors.json` | NIKE-to-KEM adapter | `hpqc/testvectors/kem/adapter_test_vectors.json` |\n| `mlkem768_x25519_combiner.json` | X25519+ML-KEM-768 hybrid combiner (both components' raw inputs and every intermediate/combined output) | `hpqc/testvectors/kem/mlkem768_x25519_combiner.json` |\n| `sphinx_hash_sha512_256.json` | SHA-512/256 (Sphinx's replay-tag hash) | `katzenpost/core/sphinx/testvectors/primitives/hash_sha512_256.json` |\n| `sphinx_mac_hmac_sha256.json` | HMAC-SHA256 (Sphinx's header MAC) | `katzenpost/core/sphinx/testvectors/primitives/mac_hmac_sha256.json` |\n| `sphinx_stream_aes256ctr.json` | AES-256-CTR (Sphinx's header stream cipher) | `katzenpost/core/sphinx/testvectors/primitives/stream_aes256ctr.json` |\n| `sphinx_kdf.json` | HKDF-SHA256 (Sphinx's `PacketKeys` derivation) | `katzenpost/core/sphinx/testvectors/primitives/kdf_sphinx.json` |\n| `sphinx_chacha20_deterministic_rand.json` | ChaCha20 deterministic RNG | `katzenpost/core/sphinx/testvectors/primitives/chacha20_deterministic_rand.json` |\n| `sphinx_sprp_aez.json` | AEZ v5 (Sphinx's SPRP) | `katzenpost/core/sphinx/testvectors/primitives/sprp_aez.json` |\n| `sphinx_commands_vectors.json` | Sphinx routing-command wire format | `katzenpost/core/sphinx/commands/testdata/sphinx_commands_vectors.json` |\n| `sphinx_nike_vectors.json` | NIKE-Sphinx full packets (10: every hop count × `withSURB`) | `katzenpost/core/sphinx/testdata/sphinx_vectors.json` |\n| `sphinx_kem_vectors.json` | KEM-Sphinx full packets (10: every hop count × `withSURB`) | `katzenpost/core/sphinx/testdata/kemsphinx_vectors.json` |\n\n`argon2.json` has no upstream file to compare against. It holds the three RFC 9106 §5 vectors\n(Argon2d, Argon2i and Argon2id with a secret and associated data), and Argon2i and Argon2id tags\nfrom Go's `golang.org/x/crypto/argon2` v0.58.0 across memory sizes, lane counts and tag lengths.\n| `sphinx_kem_hybrid_vectors.json` | KEM-Sphinx full packets, X25519+ML-KEM-768 hybrid (10: every hop count × `withSURB`) | `katzenpost/core/sphinx/testdata/kemsphinx_mlkem768x25519_vectors.json` |\n| `mlkem768_keygen.json`, `mlkem768_encapdecap.json`, `mlkem768_keycheck.json` | ML-KEM-768 (FIPS 203) — NIST's own official ACVP known-answer vectors, not from hpqc or katzenpost | [NIST's `usnistgov/ACVP-Server`](https://github.com/usnistgov/ACVP-Server) |\n\nA few more files in that directory aren't vendored input — they're output, written by this\nrepo's own `gen_nike_vectors`/`gen_kem_vectors`/`gen_mlkem768_x25519_combiner_vectors` (Lean-built\npackets and combiner vectors, for hpqc's and katzenpost's own test suites to check against):\n`lean_nike_vectors.json`, `lean_kem_vectors.json`, `lean_kem_hybrid_vectors.json`,\n`lean_mlkem768_x25519_combiner_vectors.json`.\n\n### proving the vendored vectors actually match upstream\n\n`scripts/verify-vectors.sh` (or `make verify-vectors`) `sha256sum`-compares every vendored file\nabove against its source copy in a sibling `hpqc`/`katzenpost` checkout, printing both hashes side\nby side:\n\n```bash\nmake verify-vectors                                          # expects ../hpqc, ../katzenpost\nmake verify-vectors HPQC_DIR=~/hpqc KATZENPOST_DIR=~/katzenpost\n```\n\nThis is nothing Cryptlib-specific — it is the same check anyone can run by hand with two\n`sha256sum` invocations per file; the script just automates going through the whole list and\nreports a clean pass/fail.\n\n## benchmarks: how to run the benchmark tests\n\nOne [LeanBench](https://github.com/alok/LeanBench) executable benchmarks every registered NIKE,\nevery KEM (generate/encap/decap), and Sphinx packet creation/unwrap for every scheme:\n\n```bash\nmake bench                                                  # everything\nmake bench-nike                                             # one suite: bench-nike, bench-kem, bench-sphinx\nmake bench BENCH_ARGS=--list                                # every bench's name\nmake bench BENCH_ARGS='--match \"kem MLKEM768 decap\"'    # one specific bench\n```\n\nAny LeanBench flag works in `BENCH_ARGS`: e.g. `--samples 5`, `--tags unwrap`, or\n`--save baseline.json` then `--compare baseline.json`. To call Lake directly, use\n`cd bench && lake -q --log-level=error exe cryptlib-bench ...`; without those flags Lake replays\nbuild warnings from our dependencies first. No numbers here — they depend entirely on the machine.\n\n## licensing\n\nAGPLv3\n",1792957696928]