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.

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

Every push and pull request
JobCovers
LinuxOCaml 4.14, 5.2, 5.4, and 5.5 — build, tests, and odoc on each
macOSOCaml 5.5 — the same three steps
zizmorStatic 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.