Development
Build and test
git clone https://github.com/thevilledev/ocaml-ascon.git
cd ocaml-ascon
opam install . --deps-only --with-test --with-doc
dune build
dune runtest --no-buffer
dune build @doc
Each example is a runnable program:
dune exec examples/hash256.exe
dune exec examples/xof128.exe
dune exec examples/cxof128.exe
dune exec examples/aead128.exe
Layout
The public surface is a single Ascon module. Everything
under lib/internal/ is built into the library but not
installed, so it can change without a breaking release.
lib/ascon.{ml,mli} the four public constructions and typed APIs
lib/internal/state.* mutable five-word 320-bit state
lib/internal/endian.* portable little-endian loads, stores, padding
lib/internal/permutation.* shared p[12] / p[8] round implementation
lib/internal/sponge.* persistent 64-bit-rate absorb/squeeze machinery
lib/internal/constant_time.* full-scan authentication-tag comparison
bench/ the throughput and allocation matrix
examples/ one runnable program per construction
test/ vectors, boundaries, properties
tools/differential/ optional harness against an official C checkout
The layering is deliberate: state and
permutation know nothing about constructions,
sponge knows nothing about which IV it was handed, and
ascon.ml is where the standard's parameters live. AEAD does
not use sponge at all — its 16-byte rate and
p[8] schedule are different enough that sharing would
obscure both.
What the test run covers
dune runtest runs one executable that starts from the
primitives and works up to the constructions. It is deterministic:
there is no time source and no unseeded randomness anywhere in it.
- Little-endian load, store, and padding, tested independently of the permutation.
p[8]andp[12]zero-state outputs sourced from the official C reference.- NIST Appendix B's three precomputed hash, XOF, and CXOF initialization states.
- The complete official vector corpus — 1,089 AEAD, 1,025 Hash256, 1,025 XOF128, and 1,089 CXOF128 vectors, 4,228 in total.
- Every rate boundary that matters: lengths 0, 1, 7, 8, 9, 15, 16, 17, 31, 32, 33, 63, 64, and 65.
- Incremental chunking and multi-call squeezing, checked against the one-shot result.
- Authentication failure after modifying ciphertext, tag, associated data, or nonce — including a single-bit sweep.
- 250 reproducible property-test cases.
The vector corpus
The four KAT files under
test/vectors/
are copied byte for byte from
ascon/ascon-c
at commit 446347f2, with the upstream CC0 1.0 Universal
licence and attribution alongside them. They are the SP 800-232
vectors; the older v1.2 files are deliberately absent, because having
both in a tree is how an implementation ends up quietly testing the
wrong standard.
The test asserts the record count of each file before checking any vector, so a truncated or partially copied corpus fails loudly instead of silently passing fewer cases.
Differential harness
An optional developer tool compares this implementation against a real
ascon-c checkout on randomized inputs:
python3 tools/differential/run.py /path/to/ascon-c --cases 100
It builds the C reference in a temporary directory. The C code is never a package dependency, is not vendored, and nothing in the normal build or test path touches it — supply the checkout yourself when you want the comparison.
Continuous integration
| Job | Covers |
|---|---|
| Linux | OCaml 4.14, 5.2, 5.4, and 5.5 — build, tests, and odoc on each |
| macOS | OCaml 5.5 — the same three steps |
| zizmor | Static analysis of the workflows themselves |
Actions are pinned by commit SHA and run without persisted credentials. The oldest supported compiler is a first-class matrix leg, not a hope: 4.14 is what the opam constraint promises, so it is what CI proves on every commit. There is no 32-bit runtime leg yet — the code is written for one, and that gap is recorded rather than glossed over.
Contributing
The interesting constraint on this codebase is that its output is fixed by a published standard. A change that alters any byte of any output is a bug unless it makes a currently failing vector pass. That makes the review question unusually simple: does the corpus still pass, and is the new behaviour something the standard actually specifies?
Deferred surface — tag truncation, nonce masking, incremental AEAD, arbitrary bit-length inputs — is deferred on purpose, not forgotten. Each would widen what a caller can get wrong, so each needs its own argument before it needs an implementation.
Use issues for bugs and proposals, but not for suspected vulnerabilities — those go through the private channel. Contributions are ISC-licensed.