commits
Remove libraries declared in '(libraries ...)' clauses but unreferenced
by any module in the same source tree, as flagged by 'monopam lint'
after the new Dead_lib detection landed. Touches 131 dune files across
~80 packages.
A few stanzas needed a positive correction instead of a pure removal:
- ocaml-git/bin/diag: depended on eio_main + bytesrw-eio for an
Eio_posix.run call site; the umbrella was overkill, switch to the
precise eio_posix package.
- ocaml-scaleway/lib, ocaml-s3/lib: scaleway.mli / s3.mli reference
Eio_unix.Stdenv.base; eio.unix is required and was missing.
- merlint/lib: pulled bytesrw + nox-opam.bytesrw to surface
Opam_bytesrw, used by rule e915 and lint helpers.
Stanzas where Dead_lib was a false positive (transitive dep needed
for module visibility, virtual-library impls) are left untouched —
e.g. helix.jx.jsoo for ocaml-globe/demo retains its (libraries ...)
entry because it provides the impl of the helix.jx virtual lib.
The package was renamed in 46ba2cfe7 but the unseeded-generator and
no-default-generator panic messages still pointed users at the upstream
'crypto-rng.unix' name. Update both messages and the doc reference in
crypto_rng.mli to the post-rename name. Drop the
'auto-initializes on load' phrasing since the unix sublib no longer
auto-runs at module load — callers must invoke Crypto_rng_unix.use_default
explicitly.
Run mdx on lib/crypto.mli so the {[ ... ]} odoc blocks now type-check.
Three blocks under abstract module types CBC and CTR were algebraic
identities written in pseudo-OCaml (|| for concatenation, == for
semantic equivalence). Rewrote each as a self-contained example
instantiated against Crypto.AES.CBC / AES.CTR with concrete keys, IVs,
and counters, and added an assert (chained = single) check so the
identity is documented and verifiable rather than asserted in prose.
Each README's 'opam install <pkg>' instructions now match the post-rename
opam package names. Auto-generated by 'monopam lint --fix' after the
nox-* prefix landed on the underlying packages.
dune fmt fallout from the nox- rename — three more (libraries ...)
lines got long enough to trigger one-per-line wrapping. No semantic
changes.
Both index pages currently hold prose with {!Module} cross-references
and no {[ ... ]} blocks, so MDX has nothing to verify today. Adding
the stanzas now means any future executable example added to these
pages is automatically build-checked.
Extends the nox- prefix to the remaining encoding/codec packages —
none clash with opam-repository today, but the rule "blacksun forks
get nox-" applies the same way regardless of conflict status.
Renamed: json, xml, meta, opam, protobuf -> nox-*
Renaming packages to nox-* pushed several (libraries ...) lines past
dune fmt's wrap threshold, so dune fmt now spreads them one-per-line.
No semantic changes.
Renames 35 packages to make blacksun forks distinguishable from their
opam-repository upstreams. Module names (Git.x, Tls.x, ...) stay bare;
opam package names and dune (public_name) findlib references move to
nox-X. After this commit, zero local package names overlap with
opam-repository.
Renamed:
- nox-git, nox-irmin
- nox-crypto, nox-crypto-pk, nox-crypto-rng, nox-crypto-ec
- nox-tls, nox-tls-eio, nox-tar, nox-tar-eio, nox-tty, nox-tty-eio
- nox-arp, nox-ca-certs, nox-cbor, nox-cookie, nox-crc, nox-csv
- nox-gpt, nox-hkdf, nox-http, nox-jwt, nox-kdf, nox-loc
- nox-memtrace, nox-pds, nox-sexp, nox-slack, nox-toml
- nox-websocket, nox-x509, nox-xdge, nox-yaml
Also drops orphan tar-mirage and tar-unix opam templates that had no
matching package stanza.
ec/dune and rng/dune both use fmt, but neither opam listed it. Surfaced
by `dune build -p crypto-ec` / `-p crypto-rng`.
Pure formatting changes from `dune fmt`: doc comment placement moves
from above the binding to below it for `type`s, multi-line `match`
expressions collapse onto one line where they fit, and infix operator
applications pick up spaces (`Soup.($?)` -> `Soup.( $? )`). No
semantic changes.
Object combinators: [Object.mem] -> [Object.member], [Object.opt_mem]
-> [Object.opt_member], [Object.case_mem] -> [Object.case_member]. The
sibling submodules [Object.Mem] / [Object.Mems] become
[Object.Member] / [Object.Members]. RFC 8259 §4 calls these
"name/value pairs, referred to as the members", so mirror the spec
name rather than the shortened [mem].
[Object.finish] -> [Object.seal]. "Seal" reads as "close the map, no
more members added", which is what the operation does.
Value constructors/queries: [Value.mem] (function) -> [Value.member];
[Value.mem_find] -> [Value.member_key]; [Value.mem_names] ->
[Value.member_names]; [Value.mem_keys] -> [Value.member_keys].
[type mem = ...] -> [type member = ...]; [type object'] still points
at [member list].
Downstream (~80 files across slack, sbom, stripe, sigstore, requests,
claude, irmin, freebox) updated via perl-pie. dune build clean,
dune test ocaml-json clean.
68 Json.Codec. prefixes removed across 10 codec defs in the test
data parser. None of the wycheproof records have fields that clash
with Json.Codec.mem_map, so no annotations needed.
Follow up to the module rename: update the remaining callers that
still referenced [Err] (library [claude.ml{,i}], [client.ml], the test
driver [test.ml]), and fix one stray [^ e] string concatenation in
hermest's CLI that needed [Json.Error.to_string e] now that
[Json.of_string] yields a structured error.
Warning 69 (unused-field, mutable-never-assigned). Four independent
record fields were flagged as mutable but the code only mutates their
referents in place, never rebinds the record slot itself:
- ocaml-wal/lib/wal.ml: [t.file] (the Eio file resource; methods call
Eio.File.pwrite_all etc., the slot is set once at open time).
- ocaml-block/lib/block.ml: [Memory.state.data] (the backing bytes,
written via Bytes.blit_string; [Bytes.t] is already mutable).
- ocaml-sse/lib/sse.ml: [Parser.t.data_buf] (a Buffer.t, written via
Buffer.add_*; the slot never changes).
- ocaml-zephyr/lib/zephyr.ml: drop [mode : Read | Write] entirely —
set at open-time, read nowhere. The open_read / open_write
constructors already distinguish the two call shapes, so mode
tracking was redundant.
Warnings 32 (unused value), 34 (unused type):
- lib/ccm: [crypto_core] was the non-_into variant, superseded by
[crypto_core_into]. The latter is used by every caller; the wrapper
was never exercised.
- rng/fortuna: [Sha_d256.t] and [Sha_d256.digest] — the module is
used but only through [ctx], [empty], [get], [digesti], [feedi]. The
other two were left over from an earlier shape.
- lib/ocaml/aes_pure: [interleave_in] — the comment just below it
explicitly calls it superseded ("The above is a compact version that
handles one half-block; for the full pipeline we use a different
layout").
- ec/gen_tables: delete unused [div_round_up]; change [let go = ...]
script body to the idiomatic [let () = ...].
Warning 67 (unused-functor-parameter). The HMAC/SHA-based functors all
take a [Digestif.S] at functor level but the hash choice is baked into
the resulting module's body; the signature of the returned module has
no reference to the parameter type. Same for Block.Core params in
cipher-block mode functors (ECB_of, CBC_of, GCM_of, CCM16_of).
Files:
- kdf/hkdf/hkdf.mli, kdf/pbkdf/pbkdf.mli: Make
- ocaml-crypto/lib/cipher_block.mli: ECB_of, CBC_of, GCM_of, CCM16_of
- ocaml-crypto/rng/{crypto_rng,hmac_drbg}.mli: Make / Hmac_drbg
- ocaml-crypto/pk/{rsa,dsa,crypto_pk}.mli: OAEP, PSS, K_gen
- ocaml-crypto/ec/crypto_ec.{ml,mli}: K_gen
Previously the eight git-x subcommands sat flat at top level (split,
check, fix, verify, filter-paths, split-commit, drop-commit, reword),
with 'split' ambiguous between the subtree split and the per-directory
commit split.
Regrouped into two namespaces that mirror the object they act on:
git-x tree
split (was: git-x split)
add (new — inject a standalone history under a prefix)
drop (was: git-x filter-paths)
check (was: git-x check)
fix (was: git-x fix)
verify (was: git-x verify)
git-x commit
split (was: git-x split-commit)
drop (was: git-x drop-commit)
reword (was: git-x reword)
Each subcommand lives in cmd_<group>_<verb>.{ml,mli}; cmd_tree.ml and
cmd_commit.ml are the Cmd.group wrappers. git_x.ml registers just the
two groups.
'tree add' is a thin wrapper over Git.Subtree.add, which already
existed in the library but had no CLI exposure. It accepts a ref (e.g.
FETCH_HEAD after 'git fetch URL REF') and a --prefix, then builds a
subtree-merge commit with the current user's git config identity.
Log source names are updated to match (git-x.tree.split,
git-x.tree.fix). The cram test under test/cram/tree_split.t is
updated to use the new 'git-x tree split' invocation throughout.
Moves package metadata into dune-project for kdf, monopam-info,
ocaml-ax25, ocaml-collision, ocaml-crypto (4 subpackages), ocaml-csv,
ocaml-respond, ocaml-sdnv, ocaml-tls (2 subpackages), ocaml-vec3, and
ocaml-x509, and flags each .opam with the generated-by-dune header.
Running dune build after the ocaml-toml build break is resolved will
regenerate the .opam files from dune-project and reveal any
canonical-form drift in a separate commit.
Drops the "t" suffix and follows the value/codec/toml/core pattern
(jsont.json_base style). The internal raw TOML module moves from
[Toml] to [Value] (file: lib/value.ml, was lib/toml.ml) to make room
for the top-level Toml facade (file: lib/toml.ml, was lib/tomlt.ml).
External callers now reach the raw AST through [Toml.Value.X] instead
of [Tomlt.Toml.X]. Every downstream reference updated in lockstep.
Add aes_pure.mli and ghash_pure.mli describing the pure-OCaml backend
helpers used by the js_of_ocaml / wasm_of_ocaml builds (E600 missing
interface).
Remaining crypto merlint issues are structural and not addressed here:
- bitslice_sbox is a 135-line bitsliced AES round; splitting it would
obscure the standard reference implementation.
- The double-underscore Crypto__crypto_ocaml__ paths come from dune's
virtual-module mangling and cannot be rewritten without dropping
(virtual_modules native).
- test_pure.ml is intentionally an executable used by the .c / .ocaml
/ .js differential test rules in test/dune; converting it to an
Alcotest module would break that comparison.
Client used to wait for a full max_frame credit refill before each
DATA frame. Peers that grant smaller increments (e.g. Scaleway S3's
32712 vs our 32768) stalled at the 56-byte tail per cycle. Now
waits for a 4 KiB minimum then emits whatever credit is available,
splitting a chunk into multiple frames if needed. Add interop
regression (spec: dribbled_windows). Bottler shows upload bar in KiB.
ocaml-crypto ec/rng dunes: lib/c (src/c was renamed).
Same refactor as xmlt (d786b041/067b745c), csvt (4e47c14b), sexpt
(ff8c4a47):
- Drop type codec_error ADT (Missing_member, Type_mismatch, Value_error,
Int_overflow, Parse_error, Unknown_member) and codec_error_to_string.
- Add module Loc = Loc, Meta, Path, Error re-exports.
- exception Error = Loc.Error.Error (rebound for [try...with Tomlt.Error]).
- Add decode'/decode_string' primed variants returning (_, Error.t) result.
- Add ?max_depth (default 100) and ?max_nodes (default 10_000_000)
threaded through the parser for billion-laughs / deep-array protection.
- Path/context threading via push_mem_ctx / push_nth_ctx at every
Table.mem / array element / key / mem / nth / update_key descent site.
- Added Invalid_utf8_encode of int exception for encoder UTF-8 bugs.
Dune: (re_export loc) so downstream consumers don't declare loc.
dune-project / tomlt.opam: loc added to depends.
Sublibs (tomlt.bytesrw, tomlt.eio, tomlt.unix): decode* now return
(_, Tomlt.Error.t) result. tomlt_bytesrw converts internal Toml.Error
errors into Loc.Error.t at the boundary.
Tests: 5 new structural context tests using EXACT frame-list match
(missing member, bad value in nested table, bad value in array elt,
bad value in array-of-tables, missing top-level key). Rewrote test
error printers from Toml.Error.to_string (internal parser) to
Tomlt.Error.to_string (unified Loc-based).
369 tests pass (312 codec + 8 bytesrw + 6 unix + 8 eio + 23 jsont +
12 fuzz).
Downstream: ocaml-atp/bin/tangled/test updated.
Generate .opam.template files with x-quality-* fields based on
detected package features:
- x-quality-build: has lib/ with .ml files
- x-quality-test: has test/ with .ml files
- x-quality-fuzz: has fuzz/ with .ml files
- x-quality-interop: has test/interop/ directory
- x-quality-cram: has test/*.t/ directories
These fields are picked up by dune's opam generation and will be
checked by merlint E910 for consistency.
Also: add fmt dep to ocaml-sse/lib/dune (Fmt.pf used without dep).
ocaml-merkle-sync: reusable sync primitives for any content-addressed DAG.
- Layer 1: anti-entropy gossip (branch head exchange, O(log n))
- Layer 2: merkle descent (DAG diff by hash, dependency-order transfer)
- Bloom filter (FNV-1a, ~1% FP rate)
15 tests passing. Fuzz harness for bloom properties.
irmin/lib/sync.ml: Sync.S module type — discover/locate/fetch/push.
Each backend provides its own implementation.
Also: dune fmt across affected packages.
contact, coordinate, cop1, cose, cpio, crc, crypto, csts, csvt:
all now pass merlint with 0 issues.
- Replace Unix.create_process/waitpid with Eio.Process.run for open_browser
- Add crypto-rng.unix dependency (auto-initializes RNG on load)
- crypto-rng.unix: auto-call use_default() at module init time
- crypto-rng: simplify error message to one actionable line
- CCSDS packages (aos, clcw, fsr, sdls, space-packet, tc, tm, uslp):
Wire.bool renamed to Wire.bit upstream; mechanical migration.
- ocaml-cfdp: adopt wire library for PDU header codec.
- ocaml-ltp: adopt wire library for segment header codec.
- ocaml-cop1: simplify interop test error formatting.
- dune fmt: reformat irmin, pus, scitt, crypto, tc/uslp 3D specs.
- monopam: add TODO.md tracking cram coverage gaps.
The fork inherited CI configuration from the original mirage-crypto
import:
- .github/workflows/test.yml (Linux GitHub Actions)
- .github/workflows/windows.yml (Windows GitHub Actions)
- .cirrus.yml (Cirrus CI for FreeBSD/macOS)
- .test-mirage.sh (MirageOS unikernel test script)
None of these have ever run in this fork:
- The monorepo has no top-level .github/workflows/ -- GitHub Actions
only looks at workflows at the repo root, never inside subtree
packages, so the per-package workflows have been dead since the
import.
- They install opam packages by upstream names (mirage-crypto,
mirage-crypto-rng-mirage, mirage-crypto-pk, mirage-crypto-ec)
which do not exist in the fork (we ship crypto, crypto-rng,
crypto-pk, crypto-ec).
- .test-mirage.sh invokes "mirage configure" against a mirage/
unikernel test directory that was dropped from the fork along
with the Lwt/Miou sub-libraries.
Also drop the corresponding .gitignore entries for the
no-longer-present mirage/ directory.
This is a cleanup-only commit -- no source files are touched.
The 4068-test suite still passes (no behavioural change).
The fork was rebranded mirage-crypto -> crypto at commit 87888f69
(initial import) but several internal labels still carried the old
name. Replace them so the visible identity matches the package
naming everywhere:
- src/c/crypto.h: header guard H__MIRAGE_CRYPTO -> H__CRYPTO.
- config/cfg.ml: Configurator.V1.create label "mirage-crypto" -> "crypto".
- rng/entropy.ml: Logs source "mirage-crypto-rng-entropy" -> "crypto-rng-entropy".
- src/crypto.mli: rewrite the top-level docstring to describe the
fork rather than mirage-crypto. The new wording explicitly names
Hannes Mehnert (mirage-crypto upstream maintainer), David Kaloper
Meršinjak (original ocaml-nocrypto author), and Thomas Pornin
(BearSSL author whose ct64 / ctmul64 primitives we vendor).
Includes links to mirage-crypto, ocaml-nocrypto, and BearSSL.
- ec/crypto_ec.mli: "Mirage-crypto-ec implements..." -> "Crypto-ec
implements..."; reference to [mirage-crypto-ec] -> [crypto-ec].
- rng/crypto_rng.mli: replace the dangling references to
Crypto_rng_mirage and Crypto_rng_miou_unix (which never existed
in the fork -- the Mirage and Miou sub-libraries were dropped per
the fork's README) with a reference to Crypto_rng_unix.
Not touched (intentional):
- The mc_* C symbol prefix. Renaming would require touching every
CAML external binding in src/native.mli and the matching C
CAMLprim definitions; that's a much larger surgery and gives
no functional benefit. Filed as future work if anyone cares.
- README.md and LICENSE files -- those carry attribution to the
upstream projects and should stay accurate.
- Comments that document provenance ("TLS regression test from
mirage-crypto", "hand-tuned mirage-crypto code", etc.) -- accurate
attribution.
- CI workflow files (.github/workflows/*.yml, .cirrus.yml,
.test-mirage.sh) -- they reference upstream mirage-crypto package
names and would need separate cleanup; they're not exercised by
the monorepo's CI anyway.
Also includes mechanical ocamlformat reflows in aes_pure.ml,
config/cfg.ml, and test/dune that the harness ran after the
previous commit.
All 4068 tests still pass (C, native OCaml, and Node.js JS paths
all agree byte-for-byte on the differential test vectors).
Two follow-up changes on top of 98046dc8:
1. Pure-OCaml backend: replace the T-table AES with a bitsliced
constant-time implementation.
src/ocaml/aes_pure.ml was a direct port of mirage-crypto's old
aes_generic.c -- Philip J. Erdelsky's public-domain T-table AES,
which uses sbox.(secret_byte) lookups and is exposed to
cache-timing attacks on the host. This matters because Fortuna
in the JS / WASM target encrypts every random byte with a secret
key, and the only way to reach AES from those targets is the
pure-OCaml backend.
The replacement is a direct port of BearSSL's aes_ct.c +
aes_ct_enc.c (32-bit bitsliced, by Thomas Pornin, MIT license),
using OCaml Int32 for portability across native, js_of_ocaml,
and wasm_of_ocaml. The Boyar-Peralta S-box circuit (115 logic
ops, no table indexing) and the bitsliced ShiftRows /
MixColumns / AddRoundKey are the same algorithms BearSSL uses
for its constant-time fallback on the C side, so the JS path
now has the same security model as the C aes_generic.c path.
Two blocks are processed in parallel via 8 Int32 words, packed
in BearSSL's standard interleaved layout (block 1 at q[0,2,4,6],
block 2 at q[1,3,5,7]). Single-block calls pad with zero in
the second slot. The expanded key schedule is wiped before
return, mirroring mc_secure_bzero on the C side.
Performance: significantly slower than the T-table version
(boxed Int32 + bit operations), but still fast enough for the
intended use case (Fortuna RNG + small AES-GCM workloads in JS
/ WASM builds). All 48 differential test vectors agree
byte-for-byte across the C, native-OCaml, and JS backends.
2. Three-way differential test: capture stdout from
test_pure_c.exe (C), test_pure.exe (OCaml native), and
test_pure.bc.js (OCaml under Node.js) and dune-diff them in
the runtest alias. Adds a runtest-js sub-alias that depends on
node being on $PATH; falls back gracefully when node is missing
(the bash 'true' shim). CI pipelines that include Node.js
exercise the JS path automatically.
Reverts (per directive to minimize the diff with mirage-crypto
upstream where the change is not security-related):
- crypto.h: revert the do { } while (0) wrapping of
_mc_switch_accel. Latent bug fix only -- no current call site
triggers the dangling-else issue, and upstream does not have
the wrap. Add it again only when a real call-site triggers it.
- src/c/aes_aesni.c, ghash_pclmul.c, misc_sse.c: revert the
matching trailing-semicolon additions.
- bitfn.h: revert the __builtin_bswap modernization. Upstream
still uses hand-written inline asm; our use case has no
ARMv6-M targets that would benefit from the builtin path.
The security/correctness fixes from 98046dc8 (auxv.h typo,
xor_into unaligned writes, _mc_count_16_be_4 strict aliasing,
mc_secure_bzero, ARM64 dead-code removal, hardening flags) all
remain in place. All 4068 tests still pass.
Address issues identified in a hardware/security audit pass over
src/c/. All changes are correctness/hardening fixes; no behavioural
changes for callers.
Correctness:
- detect_cpu_features.c: <sys/auxval.h> -> <sys/auxv.h> typo fix.
The previous header doesn't exist on glibc/musl/bionic, so the
Linux ARM64 detection branch was unbuildable. We also remove
the entire ARM64 detection block (and the arm_aes/arm_pmull
struct fields) because no ARM AES path consumes them -- shipping
dead detection misled audits. ARM AES-CE/PMULL acceleration is
filed as future work.
- misc.c, misc_sse.c (xor_into): the destination side of the 64-bit
and 32-bit XOR loops was a raw cast through a possibly-unaligned
pointer. Only the source was memcpy'd, leaving the destination
as undefined behaviour on architectures that trap unaligned
accesses (SPARC, MIPS, strict ARM, etc.). Use memcpy on both
sides; GCC and Clang elide it on x86.
- misc.c (_mc_count_16_be_4): cast a uint64_t* to uint32_t* and
dereferenced through both -- strict aliasing violation under
-O3. Replace with a uint32_t[4] working buffer + memcpy.
- crypto.h (_mc_switch_accel): wrap the multi-statement macro in
do { } while (0) so that "if (cond) _mc_switch_accel(...)"
doesn't attach the inner else to the wrong if. Add the
required trailing semicolons at every call site.
Hardening:
- crypto.h: add mc_secure_bzero, an optimization-resistant
memset(0) for cryptographic stack residue. Uses the standard
GCC asm memory-clobber barrier on GCC/Clang and a volatile loop
fallback elsewhere.
- aes_aesni.c: wipe the 256-byte schedule[] stack array in
_mc_aesni_derive_e_key and the 240-byte rk[] stack array in
_mc_aesni_invert_e_key before return. Both held expanded
round-key material that previously leaked into stack residue.
- aes_generic.c: wipe the 960-byte sk_exp, q[8], and w[16]
working buffers in _mc_ct64_enc_blocks and _mc_ct64_dec_blocks.
- config/cfg.ml: enable -fstack-protector-strong and
-D_FORTIFY_SOURCE=2 for the C compilation flags. Standard
cryptographic library hardening; no runtime cost on the hot
paths and catches stack/buffer overflows that the existing
code wouldn't have flagged.
Modernization:
- bitfn.h: replace the hand-rolled inline-asm byte swap (i386,
ARM, x86_64) with __builtin_bswap32/64 on GCC and Clang and
_byteswap_ulong/uint64 on MSVC. The hand-rolled ARM sequence
used 'bic', which doesn't exist in Thumb-1, so the previous
code was unbuildable on ARMv6-M (Cortex-M0/M0+). The builtins
emit a single 'rev' on ARMv6+ and 'bswap' on x86, with no
portability hazards.
Renaming:
- __mc_ARM64CE__ -> __mc_ARM64NEON__: the macro gates ARM NEON
XOR/CTR code in misc_sse.c, not Cryptography Extensions. The
previous name implied AES-CE/PMULL acceleration that doesn't
exist; rename to match what the code actually does.
All 4068 tests still pass.
Replace the non-constant-time table-driven AES (aes_generic.c) and
GHASH (ghash_generic.c) fallback paths with BearSSL's aes_ct64 and
ghash_ctmul64 implementations (Thomas Pornin, MIT license).
This closes the cache-timing side channel on non-x86 hosts (ARM64,
RISC-V, 32-bit) that was flagged upstream as mirage/mirage-crypto#36
in March 2020 but never fixed. The x86 AES-NI and PCLMUL paths
(hand-tuned mirage-crypto code with 8-way GHASH aggregation) are
kept unchanged — the diff with upstream mirage-crypto is now just
the header rename.
Includes scripts/import.ml which vendors a minimal subset of BearSSL
and auto-generates bearssl/inner.h from the upstream source tree.
Each vendored file is stamped with the BearSSL commit hash.
These were picked up by git add -A but are not part of
ocaml-crypto (they cause build failures).
Pass Eio switch from top-level Eio_main.run down to all
Git.Repository.open_repo calls instead of creating tiny
per-call Switch.run scopes that close prematurely.
- Update .ocamlformat to 0.29.0 across all 591 files
- csvt: reuse single Buffer.t for field reads (no alloc per field)
- sexpt: Obj members decoded from stream into Dict, typed Variant GADT
- Reformat all source files for 0.29.0
Drop redundant get_ prefix per merlint E331.
- xmlt: 12 err_* helpers for consistent error messages
- xmlt: shorter test names, .ocamlformat, failf
- sexpt: rename get_mem→mem, get_nth→nth (drop redundant prefix)
Remove ocaml-rice/test/debug/ (temporary libaec debug stubs).
Update ocaml-crypto, ocaml-csvt, ocaml-sexpt, ocaml-tomlt, ocaml-xmlt.
Typed column updates: decode → transform → re-encode a specific
column by name. delete_col removes a column from header and row.
4 new tests (50 total).
Same test code compiled against crypto.c and crypto.ocaml.
48 test cases: NIST vector, round-trips with varying PT/AAD lengths,
tag corruption detection. All three backends produce identical output.
Run: test_pure_c.exe (C), test_pure.exe (OCaml native),
node test_pure.bc.js (OCaml JS)
Replace opaque closure record with a GADT that preserves codec
structure, following the approach from Buenzli's "An Alphabet for
Your Data Soups" paper:
- GADT constructors for each S-expression sort (Atom, List, Obj,
Any, Map, Rec, Variant, Pair, Triple, etc.)
- dec_fun GADT with Type.Id for unordered record member decoding
- Heterogeneous Dict for buffering typed member values
- Structural encode/decode by pattern matching on GADT
- New query/update API: get_mem, get_nth, update_mem, delete_mem
Internal redesign only — 'a t stays abstract, all existing tests
pass unchanged.
Replace bit-by-bit implementation with constant-time shift-and-XOR.
All 128 iterations execute identical operations regardless of input:
- Conditional XOR uses arithmetic mask ((-bit) land 0xff), no branch
- Reduction polynomial XOR uses same masking technique
- No lookup tables, no data-dependent memory access
Reference: BearSSL ctmul technique (Thomas Pornin)
- Cache parsed Int32 round key array to avoid allocation per block
- AES.dec raises instead of silently using encrypt (wrong for CBC)
- GHASH documented as not constant-time
TODO: use Eqaf for tag comparison, add constant-time GHASH table lookup
Same pattern as digestif: crypto is now a virtual library with native
as a virtual module.
- crypto.c (default): C implementation with AES-NI/PCLMULQDQ accel
- crypto.ocaml: pure OCaml (AES T-tables, bit-by-bit GHASH) for
js_of_ocaml and wasm_of_ocaml targets
The native path is unchanged. The pure OCaml backend implements AES
encrypt (for GCM/CCM/CTR), GHASH, XOR, and counter operations.
DES, ChaCha20, and Poly1305 are stubs (not needed by SDLS).
Remove ocaml-ccsds-122 and ocaml-ccsds-123 (renamed to ocaml-idc and
ocaml-hcomp). Add ocaml-csts, ocaml-mal, ocaml-spacefibre packages.
Update ocaml-xmlt, ocaml-contact, ocaml-crypto (pure AES + JS stubs).
import used List.find_opt on raw map pairs, so a duplicate
"version" or "entries" key would shadow later occurrences.
Now checks for byte-equal duplicate keys before field extraction,
consistent with the receipt parser's cbor_check_unique_keys.
Ensure all 67 fuzz/dune files include gen_corpus.exe in the (alias fuzz)
rule deps for AFL corpus generation. Adds both missing runtest and fuzz
rules to ocaml-cose which had neither.
Fix invalid odoc markup in 54 files: convert {\!Module} to {!Module}
in fuzz .mli files, replace inline {v ... v} with [...] code spans,
fix "paragraph should begin on its own line" warnings, escape bare
brackets, and resolve ambiguous docstring placement (warning 50).
- Remove vendored crowbar/ directory
- Replace all Crowbar references with Alcobar across 176 .ml files
- Update all fuzz dune files: crowbar → alcobar in libraries
- Remove 77 gen_corpus.ml files (alcobar handles corpus internally)
- Update dune-project files: crowbar → alcobar in dependencies
- Update merlint rules (e705, e726): Crowbar → Alcobar in checks,
docs, and examples
- Update merlint generated docs (index.html)
428 files changed, ~1200 lines removed net.
- ocaml-cbort/fuzz: custom hex_decode → Ohex.decode
- space-ground/web: custom hex_digit + hex_to_bytes → Ohex.decode
- space-ground/lib: custom hex_of_string → Ohex.encode
- ocaml-atp/test: custom hex_of_string → Ohex.encode
- ocaml-crypto/test: custom hex_of_string → Ohex.decode + Ohex.encode
- ocaml-atp/xrpc-server: custom decode_percent → Uri.pct_decode
~100 lines of custom encoding code removed.
Remove genuinely stale deps (merlin, duration, digestif, eio_main,
dune-configurator, printbox, printbox-text, tls-eio, ocamlformat,
bytesrw-eio) and add missing library declarations to dune files
(eio+logs in ocaml-oci/src, logs in ocaml-git/lib+bin).
Keep js_of_ocaml in space-ground (needed for modes js) and add it
to implicit_deps in lint since it's a compiler, not a library.
Adds 108 missing dependency declarations across 52 packages.
Most common missing dep was fmt (38 packages), followed by wire,
eio, and bytesrw. Also improves lint output with tty tables and
better subtree filtering display.
All test suite names now follow the convention: lowercase snake_case
matching the test filename. Changes:
- crowbar examples: 'crowbar' → filename (calendar/fpath/map/pprint/uunf)
- irmin: uppercase suites → lowercase
- memtrace: uppercase/mismatched → lowercase matching filename
- monopam/ocaml-agent/ocaml-aos/ocaml-conpool/ocaml-cookeio/ocaml-crow:
uppercase → lowercase
- ocaml-crypto: 'cipher' → 'crypto'
- ocaml-github-oauth: 'github-oauth' → 'github_oauth'
- ocaml-jsonwt/ocaml-linkedin/ocaml-merlin: mismatch fixed
- ocaml-oci: rename test_OS.ml/.mli → test_os.ml/.mli, suite 'OS' → 'os'
- ocaml-precommit: 'hooks' → 'precommit'
- ocaml-sexpt: 'dune_codec' → 'dune'
- ocaml-slack: 'md' → 'markdown'
- ocaml-sle: 'FCLTU' → 'fcltu'
- ocaml-space-packet: 'space-packet' → 'space_packet'
- ocaml-tls: 'tls-eio'/'tls-unix' → snake_case
- ocaml-tomlt: 'tomlt-*' → snake_case
Remove libraries declared in '(libraries ...)' clauses but unreferenced
by any module in the same source tree, as flagged by 'monopam lint'
after the new Dead_lib detection landed. Touches 131 dune files across
~80 packages.
A few stanzas needed a positive correction instead of a pure removal:
- ocaml-git/bin/diag: depended on eio_main + bytesrw-eio for an
Eio_posix.run call site; the umbrella was overkill, switch to the
precise eio_posix package.
- ocaml-scaleway/lib, ocaml-s3/lib: scaleway.mli / s3.mli reference
Eio_unix.Stdenv.base; eio.unix is required and was missing.
- merlint/lib: pulled bytesrw + nox-opam.bytesrw to surface
Opam_bytesrw, used by rule e915 and lint helpers.
Stanzas where Dead_lib was a false positive (transitive dep needed
for module visibility, virtual-library impls) are left untouched —
e.g. helix.jx.jsoo for ocaml-globe/demo retains its (libraries ...)
entry because it provides the impl of the helix.jx virtual lib.
The package was renamed in 46ba2cfe7 but the unseeded-generator and
no-default-generator panic messages still pointed users at the upstream
'crypto-rng.unix' name. Update both messages and the doc reference in
crypto_rng.mli to the post-rename name. Drop the
'auto-initializes on load' phrasing since the unix sublib no longer
auto-runs at module load — callers must invoke Crypto_rng_unix.use_default
explicitly.
Run mdx on lib/crypto.mli so the {[ ... ]} odoc blocks now type-check.
Three blocks under abstract module types CBC and CTR were algebraic
identities written in pseudo-OCaml (|| for concatenation, == for
semantic equivalence). Rewrote each as a self-contained example
instantiated against Crypto.AES.CBC / AES.CTR with concrete keys, IVs,
and counters, and added an assert (chained = single) check so the
identity is documented and verifiable rather than asserted in prose.
Renames 35 packages to make blacksun forks distinguishable from their
opam-repository upstreams. Module names (Git.x, Tls.x, ...) stay bare;
opam package names and dune (public_name) findlib references move to
nox-X. After this commit, zero local package names overlap with
opam-repository.
Renamed:
- nox-git, nox-irmin
- nox-crypto, nox-crypto-pk, nox-crypto-rng, nox-crypto-ec
- nox-tls, nox-tls-eio, nox-tar, nox-tar-eio, nox-tty, nox-tty-eio
- nox-arp, nox-ca-certs, nox-cbor, nox-cookie, nox-crc, nox-csv
- nox-gpt, nox-hkdf, nox-http, nox-jwt, nox-kdf, nox-loc
- nox-memtrace, nox-pds, nox-sexp, nox-slack, nox-toml
- nox-websocket, nox-x509, nox-xdge, nox-yaml
Also drops orphan tar-mirage and tar-unix opam templates that had no
matching package stanza.
Object combinators: [Object.mem] -> [Object.member], [Object.opt_mem]
-> [Object.opt_member], [Object.case_mem] -> [Object.case_member]. The
sibling submodules [Object.Mem] / [Object.Mems] become
[Object.Member] / [Object.Members]. RFC 8259 §4 calls these
"name/value pairs, referred to as the members", so mirror the spec
name rather than the shortened [mem].
[Object.finish] -> [Object.seal]. "Seal" reads as "close the map, no
more members added", which is what the operation does.
Value constructors/queries: [Value.mem] (function) -> [Value.member];
[Value.mem_find] -> [Value.member_key]; [Value.mem_names] ->
[Value.member_names]; [Value.mem_keys] -> [Value.member_keys].
[type mem = ...] -> [type member = ...]; [type object'] still points
at [member list].
Downstream (~80 files across slack, sbom, stripe, sigstore, requests,
claude, irmin, freebox) updated via perl-pie. dune build clean,
dune test ocaml-json clean.
Follow up to the module rename: update the remaining callers that
still referenced [Err] (library [claude.ml{,i}], [client.ml], the test
driver [test.ml]), and fix one stray [^ e] string concatenation in
hermest's CLI that needed [Json.Error.to_string e] now that
[Json.of_string] yields a structured error.
Warning 69 (unused-field, mutable-never-assigned). Four independent
record fields were flagged as mutable but the code only mutates their
referents in place, never rebinds the record slot itself:
- ocaml-wal/lib/wal.ml: [t.file] (the Eio file resource; methods call
Eio.File.pwrite_all etc., the slot is set once at open time).
- ocaml-block/lib/block.ml: [Memory.state.data] (the backing bytes,
written via Bytes.blit_string; [Bytes.t] is already mutable).
- ocaml-sse/lib/sse.ml: [Parser.t.data_buf] (a Buffer.t, written via
Buffer.add_*; the slot never changes).
- ocaml-zephyr/lib/zephyr.ml: drop [mode : Read | Write] entirely —
set at open-time, read nowhere. The open_read / open_write
constructors already distinguish the two call shapes, so mode
tracking was redundant.
Warnings 32 (unused value), 34 (unused type):
- lib/ccm: [crypto_core] was the non-_into variant, superseded by
[crypto_core_into]. The latter is used by every caller; the wrapper
was never exercised.
- rng/fortuna: [Sha_d256.t] and [Sha_d256.digest] — the module is
used but only through [ctx], [empty], [get], [digesti], [feedi]. The
other two were left over from an earlier shape.
- lib/ocaml/aes_pure: [interleave_in] — the comment just below it
explicitly calls it superseded ("The above is a compact version that
handles one half-block; for the full pipeline we use a different
layout").
- ec/gen_tables: delete unused [div_round_up]; change [let go = ...]
script body to the idiomatic [let () = ...].
Warning 67 (unused-functor-parameter). The HMAC/SHA-based functors all
take a [Digestif.S] at functor level but the hash choice is baked into
the resulting module's body; the signature of the returned module has
no reference to the parameter type. Same for Block.Core params in
cipher-block mode functors (ECB_of, CBC_of, GCM_of, CCM16_of).
Files:
- kdf/hkdf/hkdf.mli, kdf/pbkdf/pbkdf.mli: Make
- ocaml-crypto/lib/cipher_block.mli: ECB_of, CBC_of, GCM_of, CCM16_of
- ocaml-crypto/rng/{crypto_rng,hmac_drbg}.mli: Make / Hmac_drbg
- ocaml-crypto/pk/{rsa,dsa,crypto_pk}.mli: OAEP, PSS, K_gen
- ocaml-crypto/ec/crypto_ec.{ml,mli}: K_gen
Previously the eight git-x subcommands sat flat at top level (split,
check, fix, verify, filter-paths, split-commit, drop-commit, reword),
with 'split' ambiguous between the subtree split and the per-directory
commit split.
Regrouped into two namespaces that mirror the object they act on:
git-x tree
split (was: git-x split)
add (new — inject a standalone history under a prefix)
drop (was: git-x filter-paths)
check (was: git-x check)
fix (was: git-x fix)
verify (was: git-x verify)
git-x commit
split (was: git-x split-commit)
drop (was: git-x drop-commit)
reword (was: git-x reword)
Each subcommand lives in cmd_<group>_<verb>.{ml,mli}; cmd_tree.ml and
cmd_commit.ml are the Cmd.group wrappers. git_x.ml registers just the
two groups.
'tree add' is a thin wrapper over Git.Subtree.add, which already
existed in the library but had no CLI exposure. It accepts a ref (e.g.
FETCH_HEAD after 'git fetch URL REF') and a --prefix, then builds a
subtree-merge commit with the current user's git config identity.
Log source names are updated to match (git-x.tree.split,
git-x.tree.fix). The cram test under test/cram/tree_split.t is
updated to use the new 'git-x tree split' invocation throughout.
Moves package metadata into dune-project for kdf, monopam-info,
ocaml-ax25, ocaml-collision, ocaml-crypto (4 subpackages), ocaml-csv,
ocaml-respond, ocaml-sdnv, ocaml-tls (2 subpackages), ocaml-vec3, and
ocaml-x509, and flags each .opam with the generated-by-dune header.
Running dune build after the ocaml-toml build break is resolved will
regenerate the .opam files from dune-project and reveal any
canonical-form drift in a separate commit.
Drops the "t" suffix and follows the value/codec/toml/core pattern
(jsont.json_base style). The internal raw TOML module moves from
[Toml] to [Value] (file: lib/value.ml, was lib/toml.ml) to make room
for the top-level Toml facade (file: lib/toml.ml, was lib/tomlt.ml).
External callers now reach the raw AST through [Toml.Value.X] instead
of [Tomlt.Toml.X]. Every downstream reference updated in lockstep.
Add aes_pure.mli and ghash_pure.mli describing the pure-OCaml backend
helpers used by the js_of_ocaml / wasm_of_ocaml builds (E600 missing
interface).
Remaining crypto merlint issues are structural and not addressed here:
- bitslice_sbox is a 135-line bitsliced AES round; splitting it would
obscure the standard reference implementation.
- The double-underscore Crypto__crypto_ocaml__ paths come from dune's
virtual-module mangling and cannot be rewritten without dropping
(virtual_modules native).
- test_pure.ml is intentionally an executable used by the .c / .ocaml
/ .js differential test rules in test/dune; converting it to an
Alcotest module would break that comparison.
Client used to wait for a full max_frame credit refill before each
DATA frame. Peers that grant smaller increments (e.g. Scaleway S3's
32712 vs our 32768) stalled at the 56-byte tail per cycle. Now
waits for a 4 KiB minimum then emits whatever credit is available,
splitting a chunk into multiple frames if needed. Add interop
regression (spec: dribbled_windows). Bottler shows upload bar in KiB.
ocaml-crypto ec/rng dunes: lib/c (src/c was renamed).
Same refactor as xmlt (d786b041/067b745c), csvt (4e47c14b), sexpt
(ff8c4a47):
- Drop type codec_error ADT (Missing_member, Type_mismatch, Value_error,
Int_overflow, Parse_error, Unknown_member) and codec_error_to_string.
- Add module Loc = Loc, Meta, Path, Error re-exports.
- exception Error = Loc.Error.Error (rebound for [try...with Tomlt.Error]).
- Add decode'/decode_string' primed variants returning (_, Error.t) result.
- Add ?max_depth (default 100) and ?max_nodes (default 10_000_000)
threaded through the parser for billion-laughs / deep-array protection.
- Path/context threading via push_mem_ctx / push_nth_ctx at every
Table.mem / array element / key / mem / nth / update_key descent site.
- Added Invalid_utf8_encode of int exception for encoder UTF-8 bugs.
Dune: (re_export loc) so downstream consumers don't declare loc.
dune-project / tomlt.opam: loc added to depends.
Sublibs (tomlt.bytesrw, tomlt.eio, tomlt.unix): decode* now return
(_, Tomlt.Error.t) result. tomlt_bytesrw converts internal Toml.Error
errors into Loc.Error.t at the boundary.
Tests: 5 new structural context tests using EXACT frame-list match
(missing member, bad value in nested table, bad value in array elt,
bad value in array-of-tables, missing top-level key). Rewrote test
error printers from Toml.Error.to_string (internal parser) to
Tomlt.Error.to_string (unified Loc-based).
369 tests pass (312 codec + 8 bytesrw + 6 unix + 8 eio + 23 jsont +
12 fuzz).
Downstream: ocaml-atp/bin/tangled/test updated.
Generate .opam.template files with x-quality-* fields based on
detected package features:
- x-quality-build: has lib/ with .ml files
- x-quality-test: has test/ with .ml files
- x-quality-fuzz: has fuzz/ with .ml files
- x-quality-interop: has test/interop/ directory
- x-quality-cram: has test/*.t/ directories
These fields are picked up by dune's opam generation and will be
checked by merlint E910 for consistency.
Also: add fmt dep to ocaml-sse/lib/dune (Fmt.pf used without dep).
ocaml-merkle-sync: reusable sync primitives for any content-addressed DAG.
- Layer 1: anti-entropy gossip (branch head exchange, O(log n))
- Layer 2: merkle descent (DAG diff by hash, dependency-order transfer)
- Bloom filter (FNV-1a, ~1% FP rate)
15 tests passing. Fuzz harness for bloom properties.
irmin/lib/sync.ml: Sync.S module type — discover/locate/fetch/push.
Each backend provides its own implementation.
Also: dune fmt across affected packages.
- CCSDS packages (aos, clcw, fsr, sdls, space-packet, tc, tm, uslp):
Wire.bool renamed to Wire.bit upstream; mechanical migration.
- ocaml-cfdp: adopt wire library for PDU header codec.
- ocaml-ltp: adopt wire library for segment header codec.
- ocaml-cop1: simplify interop test error formatting.
- dune fmt: reformat irmin, pus, scitt, crypto, tc/uslp 3D specs.
- monopam: add TODO.md tracking cram coverage gaps.
The fork inherited CI configuration from the original mirage-crypto
import:
- .github/workflows/test.yml (Linux GitHub Actions)
- .github/workflows/windows.yml (Windows GitHub Actions)
- .cirrus.yml (Cirrus CI for FreeBSD/macOS)
- .test-mirage.sh (MirageOS unikernel test script)
None of these have ever run in this fork:
- The monorepo has no top-level .github/workflows/ -- GitHub Actions
only looks at workflows at the repo root, never inside subtree
packages, so the per-package workflows have been dead since the
import.
- They install opam packages by upstream names (mirage-crypto,
mirage-crypto-rng-mirage, mirage-crypto-pk, mirage-crypto-ec)
which do not exist in the fork (we ship crypto, crypto-rng,
crypto-pk, crypto-ec).
- .test-mirage.sh invokes "mirage configure" against a mirage/
unikernel test directory that was dropped from the fork along
with the Lwt/Miou sub-libraries.
Also drop the corresponding .gitignore entries for the
no-longer-present mirage/ directory.
This is a cleanup-only commit -- no source files are touched.
The 4068-test suite still passes (no behavioural change).
The fork was rebranded mirage-crypto -> crypto at commit 87888f69
(initial import) but several internal labels still carried the old
name. Replace them so the visible identity matches the package
naming everywhere:
- src/c/crypto.h: header guard H__MIRAGE_CRYPTO -> H__CRYPTO.
- config/cfg.ml: Configurator.V1.create label "mirage-crypto" -> "crypto".
- rng/entropy.ml: Logs source "mirage-crypto-rng-entropy" -> "crypto-rng-entropy".
- src/crypto.mli: rewrite the top-level docstring to describe the
fork rather than mirage-crypto. The new wording explicitly names
Hannes Mehnert (mirage-crypto upstream maintainer), David Kaloper
Meršinjak (original ocaml-nocrypto author), and Thomas Pornin
(BearSSL author whose ct64 / ctmul64 primitives we vendor).
Includes links to mirage-crypto, ocaml-nocrypto, and BearSSL.
- ec/crypto_ec.mli: "Mirage-crypto-ec implements..." -> "Crypto-ec
implements..."; reference to [mirage-crypto-ec] -> [crypto-ec].
- rng/crypto_rng.mli: replace the dangling references to
Crypto_rng_mirage and Crypto_rng_miou_unix (which never existed
in the fork -- the Mirage and Miou sub-libraries were dropped per
the fork's README) with a reference to Crypto_rng_unix.
Not touched (intentional):
- The mc_* C symbol prefix. Renaming would require touching every
CAML external binding in src/native.mli and the matching C
CAMLprim definitions; that's a much larger surgery and gives
no functional benefit. Filed as future work if anyone cares.
- README.md and LICENSE files -- those carry attribution to the
upstream projects and should stay accurate.
- Comments that document provenance ("TLS regression test from
mirage-crypto", "hand-tuned mirage-crypto code", etc.) -- accurate
attribution.
- CI workflow files (.github/workflows/*.yml, .cirrus.yml,
.test-mirage.sh) -- they reference upstream mirage-crypto package
names and would need separate cleanup; they're not exercised by
the monorepo's CI anyway.
Also includes mechanical ocamlformat reflows in aes_pure.ml,
config/cfg.ml, and test/dune that the harness ran after the
previous commit.
All 4068 tests still pass (C, native OCaml, and Node.js JS paths
all agree byte-for-byte on the differential test vectors).
Two follow-up changes on top of 98046dc8:
1. Pure-OCaml backend: replace the T-table AES with a bitsliced
constant-time implementation.
src/ocaml/aes_pure.ml was a direct port of mirage-crypto's old
aes_generic.c -- Philip J. Erdelsky's public-domain T-table AES,
which uses sbox.(secret_byte) lookups and is exposed to
cache-timing attacks on the host. This matters because Fortuna
in the JS / WASM target encrypts every random byte with a secret
key, and the only way to reach AES from those targets is the
pure-OCaml backend.
The replacement is a direct port of BearSSL's aes_ct.c +
aes_ct_enc.c (32-bit bitsliced, by Thomas Pornin, MIT license),
using OCaml Int32 for portability across native, js_of_ocaml,
and wasm_of_ocaml. The Boyar-Peralta S-box circuit (115 logic
ops, no table indexing) and the bitsliced ShiftRows /
MixColumns / AddRoundKey are the same algorithms BearSSL uses
for its constant-time fallback on the C side, so the JS path
now has the same security model as the C aes_generic.c path.
Two blocks are processed in parallel via 8 Int32 words, packed
in BearSSL's standard interleaved layout (block 1 at q[0,2,4,6],
block 2 at q[1,3,5,7]). Single-block calls pad with zero in
the second slot. The expanded key schedule is wiped before
return, mirroring mc_secure_bzero on the C side.
Performance: significantly slower than the T-table version
(boxed Int32 + bit operations), but still fast enough for the
intended use case (Fortuna RNG + small AES-GCM workloads in JS
/ WASM builds). All 48 differential test vectors agree
byte-for-byte across the C, native-OCaml, and JS backends.
2. Three-way differential test: capture stdout from
test_pure_c.exe (C), test_pure.exe (OCaml native), and
test_pure.bc.js (OCaml under Node.js) and dune-diff them in
the runtest alias. Adds a runtest-js sub-alias that depends on
node being on $PATH; falls back gracefully when node is missing
(the bash 'true' shim). CI pipelines that include Node.js
exercise the JS path automatically.
Reverts (per directive to minimize the diff with mirage-crypto
upstream where the change is not security-related):
- crypto.h: revert the do { } while (0) wrapping of
_mc_switch_accel. Latent bug fix only -- no current call site
triggers the dangling-else issue, and upstream does not have
the wrap. Add it again only when a real call-site triggers it.
- src/c/aes_aesni.c, ghash_pclmul.c, misc_sse.c: revert the
matching trailing-semicolon additions.
- bitfn.h: revert the __builtin_bswap modernization. Upstream
still uses hand-written inline asm; our use case has no
ARMv6-M targets that would benefit from the builtin path.
The security/correctness fixes from 98046dc8 (auxv.h typo,
xor_into unaligned writes, _mc_count_16_be_4 strict aliasing,
mc_secure_bzero, ARM64 dead-code removal, hardening flags) all
remain in place. All 4068 tests still pass.
Address issues identified in a hardware/security audit pass over
src/c/. All changes are correctness/hardening fixes; no behavioural
changes for callers.
Correctness:
- detect_cpu_features.c: <sys/auxval.h> -> <sys/auxv.h> typo fix.
The previous header doesn't exist on glibc/musl/bionic, so the
Linux ARM64 detection branch was unbuildable. We also remove
the entire ARM64 detection block (and the arm_aes/arm_pmull
struct fields) because no ARM AES path consumes them -- shipping
dead detection misled audits. ARM AES-CE/PMULL acceleration is
filed as future work.
- misc.c, misc_sse.c (xor_into): the destination side of the 64-bit
and 32-bit XOR loops was a raw cast through a possibly-unaligned
pointer. Only the source was memcpy'd, leaving the destination
as undefined behaviour on architectures that trap unaligned
accesses (SPARC, MIPS, strict ARM, etc.). Use memcpy on both
sides; GCC and Clang elide it on x86.
- misc.c (_mc_count_16_be_4): cast a uint64_t* to uint32_t* and
dereferenced through both -- strict aliasing violation under
-O3. Replace with a uint32_t[4] working buffer + memcpy.
- crypto.h (_mc_switch_accel): wrap the multi-statement macro in
do { } while (0) so that "if (cond) _mc_switch_accel(...)"
doesn't attach the inner else to the wrong if. Add the
required trailing semicolons at every call site.
Hardening:
- crypto.h: add mc_secure_bzero, an optimization-resistant
memset(0) for cryptographic stack residue. Uses the standard
GCC asm memory-clobber barrier on GCC/Clang and a volatile loop
fallback elsewhere.
- aes_aesni.c: wipe the 256-byte schedule[] stack array in
_mc_aesni_derive_e_key and the 240-byte rk[] stack array in
_mc_aesni_invert_e_key before return. Both held expanded
round-key material that previously leaked into stack residue.
- aes_generic.c: wipe the 960-byte sk_exp, q[8], and w[16]
working buffers in _mc_ct64_enc_blocks and _mc_ct64_dec_blocks.
- config/cfg.ml: enable -fstack-protector-strong and
-D_FORTIFY_SOURCE=2 for the C compilation flags. Standard
cryptographic library hardening; no runtime cost on the hot
paths and catches stack/buffer overflows that the existing
code wouldn't have flagged.
Modernization:
- bitfn.h: replace the hand-rolled inline-asm byte swap (i386,
ARM, x86_64) with __builtin_bswap32/64 on GCC and Clang and
_byteswap_ulong/uint64 on MSVC. The hand-rolled ARM sequence
used 'bic', which doesn't exist in Thumb-1, so the previous
code was unbuildable on ARMv6-M (Cortex-M0/M0+). The builtins
emit a single 'rev' on ARMv6+ and 'bswap' on x86, with no
portability hazards.
Renaming:
- __mc_ARM64CE__ -> __mc_ARM64NEON__: the macro gates ARM NEON
XOR/CTR code in misc_sse.c, not Cryptography Extensions. The
previous name implied AES-CE/PMULL acceleration that doesn't
exist; rename to match what the code actually does.
All 4068 tests still pass.
Replace the non-constant-time table-driven AES (aes_generic.c) and
GHASH (ghash_generic.c) fallback paths with BearSSL's aes_ct64 and
ghash_ctmul64 implementations (Thomas Pornin, MIT license).
This closes the cache-timing side channel on non-x86 hosts (ARM64,
RISC-V, 32-bit) that was flagged upstream as mirage/mirage-crypto#36
in March 2020 but never fixed. The x86 AES-NI and PCLMUL paths
(hand-tuned mirage-crypto code with 8-way GHASH aggregation) are
kept unchanged — the diff with upstream mirage-crypto is now just
the header rename.
Includes scripts/import.ml which vendors a minimal subset of BearSSL
and auto-generates bearssl/inner.h from the upstream source tree.
Each vendored file is stamped with the BearSSL commit hash.
Replace opaque closure record with a GADT that preserves codec
structure, following the approach from Buenzli's "An Alphabet for
Your Data Soups" paper:
- GADT constructors for each S-expression sort (Atom, List, Obj,
Any, Map, Rec, Variant, Pair, Triple, etc.)
- dec_fun GADT with Type.Id for unordered record member decoding
- Heterogeneous Dict for buffering typed member values
- Structural encode/decode by pattern matching on GADT
- New query/update API: get_mem, get_nth, update_mem, delete_mem
Internal redesign only — 'a t stays abstract, all existing tests
pass unchanged.
Replace bit-by-bit implementation with constant-time shift-and-XOR.
All 128 iterations execute identical operations regardless of input:
- Conditional XOR uses arithmetic mask ((-bit) land 0xff), no branch
- Reduction polynomial XOR uses same masking technique
- No lookup tables, no data-dependent memory access
Reference: BearSSL ctmul technique (Thomas Pornin)
Same pattern as digestif: crypto is now a virtual library with native
as a virtual module.
- crypto.c (default): C implementation with AES-NI/PCLMULQDQ accel
- crypto.ocaml: pure OCaml (AES T-tables, bit-by-bit GHASH) for
js_of_ocaml and wasm_of_ocaml targets
The native path is unchanged. The pure OCaml backend implements AES
encrypt (for GCM/CCM/CTR), GHASH, XOR, and counter operations.
DES, ChaCha20, and Poly1305 are stubs (not needed by SDLS).
- Remove vendored crowbar/ directory
- Replace all Crowbar references with Alcobar across 176 .ml files
- Update all fuzz dune files: crowbar → alcobar in libraries
- Remove 77 gen_corpus.ml files (alcobar handles corpus internally)
- Update dune-project files: crowbar → alcobar in dependencies
- Update merlint rules (e705, e726): Crowbar → Alcobar in checks,
docs, and examples
- Update merlint generated docs (index.html)
428 files changed, ~1200 lines removed net.
- ocaml-cbort/fuzz: custom hex_decode → Ohex.decode
- space-ground/web: custom hex_digit + hex_to_bytes → Ohex.decode
- space-ground/lib: custom hex_of_string → Ohex.encode
- ocaml-atp/test: custom hex_of_string → Ohex.encode
- ocaml-crypto/test: custom hex_of_string → Ohex.decode + Ohex.encode
- ocaml-atp/xrpc-server: custom decode_percent → Uri.pct_decode
~100 lines of custom encoding code removed.
Remove genuinely stale deps (merlin, duration, digestif, eio_main,
dune-configurator, printbox, printbox-text, tls-eio, ocamlformat,
bytesrw-eio) and add missing library declarations to dune files
(eio+logs in ocaml-oci/src, logs in ocaml-git/lib+bin).
Keep js_of_ocaml in space-ground (needed for modes js) and add it
to implicit_deps in lint since it's a compiler, not a library.
All test suite names now follow the convention: lowercase snake_case
matching the test filename. Changes:
- crowbar examples: 'crowbar' → filename (calendar/fpath/map/pprint/uunf)
- irmin: uppercase suites → lowercase
- memtrace: uppercase/mismatched → lowercase matching filename
- monopam/ocaml-agent/ocaml-aos/ocaml-conpool/ocaml-cookeio/ocaml-crow:
uppercase → lowercase
- ocaml-crypto: 'cipher' → 'crypto'
- ocaml-github-oauth: 'github-oauth' → 'github_oauth'
- ocaml-jsonwt/ocaml-linkedin/ocaml-merlin: mismatch fixed
- ocaml-oci: rename test_OS.ml/.mli → test_os.ml/.mli, suite 'OS' → 'os'
- ocaml-precommit: 'hooks' → 'precommit'
- ocaml-sexpt: 'dune_codec' → 'dune'
- ocaml-slack: 'md' → 'markdown'
- ocaml-sle: 'FCLTU' → 'fcltu'
- ocaml-space-packet: 'space-packet' → 'space_packet'
- ocaml-tls: 'tls-eio'/'tls-unix' → snake_case
- ocaml-tomlt: 'tomlt-*' → snake_case