{"slug": "hacl-pulse-re-verifying-hacl-in-pulse-with-agents", "title": "hacl-pulse: Re-verifying HACL* in Pulse, with Agents", "summary": "Gabriel Ebner's hacl-pulse project reimplemented the HACL* verified cryptographic library from scratch in F* and Pulse, extracting to C via F*'s new Custard backend, with nearly all of the roughly six-week effort written by AI agents running Claude Opus 5 and Opus 5.5. The library covers every algorithm HACL* offers and adds ML-KEM, ML-DSA, and HPKE in all four modes, comprising about 164,000 lines of F* and Pulse (57K specification, 101K implementation and proof) extracted to a single Hacl.c and Hacl.h, with no assembly. Every primitive runs within 1.32x of HACL*'s best configuration and 16 run faster, with 44 C test harnesses checking against published vectors.", "body_md": "# hacl-pulse: Re-verifying HACL* in Pulse, with Agents\n\n- Author: Gabriel Ebner\n\n**TL;DR:** [hacl-pulse](https://github.com/FStarLang/hacl-pulse) is a\nfrom-scratch reimplementation of the\n[HACL*](https://github.com/hacl-star/hacl-star) verified cryptographic\nlibrary in [F*](https://fstar-lang.org) and\n[Pulse](https://fstar-lang.org/tutorial/book/pulse/pulse.html), extracted\nto C by F*’s new Custard backend. It covers every algorithm HACL* offers\n(though not yet its platforms, bindings, or agile EverCrypt layer), and adds\nML-KEM, ML-DSA, and HPKE in all four modes. It proves correctness theorems\nabout the specs themselves, including the elliptic-curve group laws that\nHACL* admits. Every primitive runs within 1.32x of HACL*’s best\nconfiguration, and 16 run faster. It took about six weeks, and nearly all\nof it was written by AI agents running Claude Opus 5 and Opus 5.5.\n\n## Why rebuild HACL*?\n\n[Section titled “Why rebuild HACL*?”](#why-rebuild-hacl)\n\nHACL* is one of the success stories of formal verification. Its verified C\ncode ships in Firefox, Linux, the Tezos and Python ecosystems, and elsewhere.\nIt was written over many years in Low*, a C-like subset of F*, and carries\nworkarounds for features F* lacked at the time: one integer type indexed by\nwidth and secrecy, a custom tactic to specialize width-generic code, and\n32-bit lengths everywhere. Its fastest paths use\n[Vale](https://github.com/project-everest/vale), a separate framework for\nwriting and verifying x86-64 assembly.\n\nF* has changed a lot since then.\n[Pulse](https://fstar-lang.org/tutorial/book/pulse/pulse.html) is a mature\nseparation-logic language for imperative code, type classes are first-class,\nand [Custard](https://github.com/FStarLang/FStar/blob/master/doc/ref/custard.md)\nextracts Pulse straight to C, monomorphizing type-class code as it goes. And,\nas we’ve written in [earlier](https://risemsr.github.io/blog/2026-02-04-nik-agentic-pop)\n[posts](https://risemsr.github.io/blog/2026-03-06-autoclrs), agents can now carry most of the proof\nengineering. So we asked: **what would HACL* look like if we wrote it today?**\n\nThe answer is not a port. Wherever HACL* worked around a missing language feature, hacl-pulse uses the feature.\n\n## What’s in the box\n\n[Section titled “What’s in the box”](#whats-in-the-box)\n\nAll of the following are specified, implemented, verified, extracted to C, and tested against published vectors (44 C test harnesses):\n\n- **Hashes:** SHA-1, SHA-2, SHA-3/SHAKE, BLAKE2s/b, and MD5, one-shot and\nincremental.\n- **MACs and KDFs:** HMAC (one-shot and incremental), HKDF, and Poly1305.\n- **Ciphers and AEADs:** ChaCha20, Salsa20/XSalsa20, AES-GCM,\nChaCha20-Poly1305, and NaCl`secretbox` /`box` .\n- **Curves:** X25519, Ed25519, P-256, and secp256k1 (with low-S for Bitcoin).\n- **Bignum:** Montgomery arithmetic, constant-time modexp, FFDHE (all five\nRFC 7919 groups), and RSA-PSS.\n- **Randomness:** HMAC-DRBG with an OS entropy shim.\n- **Post-quantum:** FrodoKEM (all four parameter sets), and**ML-KEM** and**ML-DSA** (all three parameter sets each).\n- **HPKE** (RFC 9180) in all four modes with all three AEADs.\n\nThat’s about 164,000 lines of F* and Pulse (57K of specification, 101K of\nimplementation and proof), extracted to a single `Hacl.c` and `Hacl.h`. There\nis no assembly. Hardware fast paths (AES-NI, PCLMULQDQ, the SHA extensions,\nAVX2, and AVX-512) are C intrinsics behind narrow, axiomatized interfaces,\nselected at run time. On other architectures the same `Hacl.c` compiles to\nthe portable code alone, and CI tests it on aarch64.\n\n## Modernizing the design\n\n[Section titled “Modernizing the design”](#modernizing-the-design)\n\nhacl-pulse departs from HACL*’s design in five places (details in the\n[design notes](https://github.com/FStarLang/hacl-pulse/blob/master/design/notes.md#the-five-design-commitments)):\n\n1. **Secret integers are types, not an index.** The idea comes from HACL*’s\nsecret integers; hacl-pulse makes each secret width its own abstract\ntype, whose interface exposes only constant-time operations: no\ncomparison, no division, no branching. Declassification goes through a\nsingle module, so auditing where secrets become public is a search for one\nname. In C these are plain`uint32_t` and friends.\n2. **Width-generic code is a type class, not a tactic.** SHA-2’s core is\nwritten once against a`word` type class, and Custard monomorphizes it\ninto`uint32_t` and`uint64_t` copies with no indirection. Algorithms are\nparameter records, so SHA-224/256/384/512 become four specialized C\nfunctions with their constants as literals.\n3. **`size_t`, not 32-bit lengths.** Buffers larger than 4 GiB work\nwithout chunking.\n4. **No assembly.** The trusted C is small and explicit: the entropy shim,\nthe vector intrinsics, a 64-bit add-with-carry, and memory wiping.\n5. **Scratch memory is wiped, and the type system enforces it.** Every stack\narray carries a ghost`must_wipe` resource that only`wipe` consumes.\nPulse rejects a function that ends holding a resource, so a forgotten\nwipe is a*type error* , not a code-review comment. Lints and a`-fstack-usage` check also ensure no stack array is sized by caller input.\n\n## Proving the specs mean what they should\n\n[Section titled “Proving the specs mean what they should”](#proving-the-specs-mean-what-they-should)\n\nA refinement proof shows that the C computes what the spec says, not that\nthe spec is right. Test vectors are part of the answer. The rest is a set of\ntheorems *about the specifications*, the statements a cryptographer would\nwrite down first:\n\n- **AEADs:** decryption inverts encryption for ChaCha20-Poly1305, AES-GCM,\nand NaCl secretbox and box.\n- **Key exchange:** both parties agree for X25519, P-256 and secp256k1 ECDH,\nand all five FFDHE groups.\n- **Signatures:** honest signatures verify for Ed25519, ECDSA on both\ncurves, and RSA-PSS.\n- **KEMs and HPKE:** encapsulation and seal/open round-trip for ML-KEM,\nFrodoKEM, and every HPKE mode. The lattice KEM theorems assume an explicit\nnoise bound.\n\nAlong the way, the library proves facts that are usually assumed: that the\nP-256 and secp256k1 group orders are prime, that the ML-KEM NTT is a ring\nisomorphism, that the X25519 ladder computes a scalar multiple on the\nEdwards group, and **the group laws for Ed25519, P-256, and secp256k1**.\nThe last of these is the most interesting, so let’s zoom in.\n\n## Zooming in: the elliptic-curve group laws\n\n[Section titled “Zooming in: the elliptic-curve group laws”](#zooming-in-the-elliptic-curve-group-laws)\n\n**Why we needed them.** Fast implementations compute a fixed-base scalar\nmultiple *k G* from a precomputed table of multiples of *G*. For example,\na 4-bit window splits *k* into digits and adds up *d<sub>i</sub>\n(16<sup>i</sup> G)*. That’s only correct if scalar multiplication is a\nhomomorphism from the integers, which holds because the curve points form a\ncommutative group. So the table needs associativity of point addition.\nBenchmarks made this concrete. On P-256, variable-base ECDH (where HACL*\nhas no table either) was within 1.15x of HACL*, while fixed-base was at\n2.59x, so the whole gap was the table. The agent first wrote down the\ngroup-law statements as interfaces for all three curves, as a specification\nof the work to be done, and noticed something surprising along the way:\n**HACL* doesn’t prove these laws.** In its `Spec.{Ed25519,P256,K256}.Lemmas`\nthey are `admit()`, so HACL*’s windowed Ed25519, windowed P-256, and GLV\nsecp256k1 all rest on unproved facts.\n\n**The approach.** Associativity is the hard law, and we took the standard\nroute used by Bernstein and Lange and by\n[Fiat-Crypto](https://github.com/mit-plv/fiat-crypto) in Coq: clear\ndenominators, and show that the resulting polynomial identity holds modulo\nthe curve equations. That last step is ideal membership. Because the\nformulas are homogeneous, every point reduces to a canonical representative,\nand there the curve equation becomes a rewrite rule (on Ed25519,\n*u<sup>2</sup>v<sup>2</sup> → (v<sup>2</sup> − u<sup>2</sup> − 1)/d*). The\nrules form a Gröbner basis, so rewriting decides membership. If the\ndifference of the two sides reduces to zero, the identity holds.\n\n**A small verified computer algebra system.** The original plan was to\nclose these identities with F*’s ring-normalization tactic. But they are\nfar too large for that, or for an SMT solver. P-256 associativity is about\n26,000 monomials before reduction. So the agent wrote one:\n`Spec.Group.Poly` represents polynomials as lists of monomials, implements\naddition, multiplication, and reduction as ordinary functions, and proves\nthat each one commutes with evaluation modulo the prime. Running it with\nF*’s normalizer is a proof by reflection: if the computed result is the\nempty polynomial, the identity holds over the field. Formulas are\ntranscribed over a type that pairs each polynomial with the field element\nit denotes, so the transcription can’t drift from the spec.\n\n**Where the computer algebra certificates come in.** Associativity is pure\ncomputation, but other facts need a *search*, which is a job for an\nexternal computer algebra system (CAS). F* never trusts the CAS. It only\nchecks the certificates the CAS produces:\n\n- **Completeness** (the addition formulas never divide by zero) needs\npolynomial cofactors showing that a denominator lies in a certain ideal.[Singular](https://www.singular.uni-kl.de/) ’s`lift` computes them. On\nEd25519 the argument also uses the fact that*d* is not a square, which\nF* checks by computing Euler’s criterion.\n- **No roots.** On the two Weierstrass curves, completeness depends on\ncertain polynomials having no root in the field. For example, a root of*x<sup>3</sup> − 3x + b* would be a point of order two on P-256. The\ncertificate is a polynomial*u* with*u (x<sup>p</sup> − x) ≡ 1* modulo\nthe polynomial, which rules out a root by Fermat’s little theorem.[PARI/GP](https://pari.math.u-bordeaux.fr/) computes*u* , and F* checks\nthe product.\n- **Primality.** The proofs need the field primes to be prime, and ECDSA\nneeds the group orders to be prime. PARI/GP builds recursive Pocklington\ncertificates, and a verified checker in F* validates them.\n\nPython generators turn the CAS output into F* modules, and every\n[generator](https://github.com/FStarLang/hacl-pulse/tree/master/spec/group-gen)\nreproduces its committed module byte-for-byte. On top of the group laws,\n`Spec.Group.Smul` proves the scalar-multiplication facts a window needs\nonce, over an abstract commutative group, and instantiates them per curve.\nThat’s not just tidier. A goal over an abstract predicate takes\nmilliseconds of Z3 time, while the same goal over 78-digit field equations\ntakes minutes. In total this is about 10,000 lines, and it’s what made the\nfixed-base windows on all three curves possible.\n\nThe idea of an untrusted CAS paired with a trusted checker is an old one\n(Harrison and Théry’s\n[“skeptic’s approach”](https://doi.org/10.1023/A:1006023127567) to combining\nHOL and Maple). What’s new is the cost. Nobody built an interface between\nF* and Singular or PARI/GP. The agent wrote the scripts, ran them, read\nthe output, and wrote the generators.\n\n## Performance\n\n[Section titled “Performance”](#performance)\n\nA verified library that’s too slow to use isn’t much of a win, so\nperformance got as much attention as proofs. We compare against **HACL*\nconfigured for the machine and allowed everything it has**: Vale assembly,\nAVX2, and BMI2/ADX. On an AMD EPYC (Zen 3) with gcc 15:\n\n- **16 of 26 benchmarks beat HACL*’s best, and all are within 1.32x.**\n- Salsa20 and secretbox run at 0.24x of HACL*’s time (HACL* has no vector Salsa20), SHA-512, SHA-3, and SHAKE at about 0.5x, and RSA-PSS verification at 0.43x.\n- P-256 runs at 0.47x to 0.57x across the board, thanks to a field specialized to its prime and a four-base-point fixed-base table.\n- The slowest rows are X25519 and Ed25519 signing (about 1.3x, against HACL*’s BMI2/ADX assembly) and MD5. Under clang, X25519 matches HACL*.\n\nHACL* has no ML-KEM or ML-DSA, so we compare them with other libraries. Both use AVX2 and a four-way Keccak. ML-KEM-768 encapsulation takes 22 µs (1.5x of AWS-LC). ML-DSA is about 1.8x to 2.1x of the fastest implementation (mldsa-native or the pq-crystals AVX2 build), 2x to 3x faster than the pq-crystals reference, and faster still than OpenSSL.\n\nAgainst OpenSSL, AWS-LC, libsodium, libjade, Firedancer, and dalek, the symmetric primitives and X25519 are within 1.4x of the fastest library. The gaps that remain are where the leaders have assembly or vector backends that hacl-pulse hasn’t written yet: RSA, FFDHE, and SHA-512 at 1.7x to 2x, P-256 and Ed25519 at 2.3x to 3.9x, and SHA-1 (no SHA extensions yet) at 6x.\n\n## Verification makes optimization safe\n\n[Section titled “Verification makes optimization safe”](#verification-makes-optimization-safe)\n\nOptimized crypto code is where bugs hide: a carry handled slightly differently, a dropped lane in an unrolled loop, a deferred reduction that overflows on an untested input. In an unverified library, every speedup trades confidence for performance.\n\nIn hacl-pulse there is no such trade. **An optimized implementation must\nprove the same specification as the code it replaces.** SHA-3’s permutation\nmust equal the spec’s permutation, rolled or unrolled. X25519’s field\nmultiply must compute multiplication modulo 2^255 - 19, whether it uses five\n51-bit limbs or four 64-bit ones. If the change verifies, it’s correct, and\nnothing above it needs re-examining.\n\nThat changed what we were willing to try. The 86 parts of\n[`bench/REPORT.md`](https://github.com/FStarLang/hacl-pulse/blob/master/bench/REPORT.md)\nrecord each optimization (the motivating measurement, the experiment, and\nthe result), and many are changes you’d normally make only with great care:\n\n- **Unrolling Keccak by hand.** The theta, rho/pi, and chi steps are written\nout lane by lane. One helper is verified against a symbolic lane index and\napplied to 25 literal ones. SHA-3 now runs at about half of HACL*’s time.\n- **Changing a field representation.** Curve25519 went from eight 32-bit\nlimbs to five 51-bit limbs, then a straight-line multiply, then four\n64-bit limbs. Each step changed the carry discipline, which is exactly\nwhere field arithmetic usually breaks.\n- **Deferring algebra.** Four-way GHASH sums four raw 256-bit products and\nreduces once instead of four times. That’s correct only because the\nreduction is linear, which three small GF(2^128) lemmas make explicit.\nGHASH went from 2.11 to 1.28 cycles per byte.\n- **Replacing simple algorithms with clever ones:** windowed scalar\nmultiplication on all three curves, 4-bit windows and triangle squaring in\nmodexp, and a P-256 field specialized to its prime.\n\nThis matters even more with agents. An agent can propose and benchmark\ndozens of optimizations in an afternoon, far more than anyone could review\nline by line. With verification, people judge *whether a change is worth\nit*, and F* checks *whether it’s right*. The cost of an optimization shows\nup as proof effort, not bugs. The recurring fix for a slow proof is to split\nthe routine into helpers with tight specifications rather than raise solver\nlimits. One BLAKE2 module went from an 18-minute timeout to 25 seconds that\nway.\n\n### Three caveats\n\n[Section titled “Three caveats”](#three-caveats)\n\n“If it verifies, it’s correct” means correct *with respect to the spec,\nunder the stated assumptions*. Three caveats came up along the way.\n\n**Constant-time execution is not in the spec.** Windowed scalar\nmultiplication reads one of sixteen precomputed points, chosen by four\nsecret bits, so `Hacl.Bignum.Base.table_select` touches every entry and\nfolds each through a mask. F* proves the result is entry *d*, but it can’t\nstate that the time is independent of *d*. That’s a property of the emitted\ninstructions, not the result. Clang `-O3` recognized the sixteen masks as\ncomparisons against 0 to 15 and compiled them into a secret-dependent branch\ncascade, with a 158% timing spread across digits. The fix is a value\nbarrier, `Hacl.Launder.launder`: an identity function wrapping the mask in\nan empty inline `asm` the optimizer can’t see through. The spread dropped to\n1.2%, and `tests/test_consttime.c` and `tests/test_launder.c` fail if it\nreturns\n([full story](https://github.com/FStarLang/hacl-pulse/blob/master/design/notes.md#the-compiler-is-allowed-to-undo-a-constant-time-algorithm)).\n\n**Hardware intrinsics enlarge the trusted computing base.** The vector\nbackends and the 64-bit add-with-carry are described to F* by `assume val`\naxioms in `lib/Hacl.Vec128.fst`, `Vec256.fst`, `Vec512.fst`, and\n`Carry64.fst`, and implemented by handwritten C headers (` c/Hacl_*.h`). F*\nverifies the code that uses them *assuming* the axioms hold. Differential\ntests (`test_vec128.c`, `test_vec256.c`, `test_vec512.c`, `test_carry64.c`)\ncheck each intrinsic against an independent reference, and every new\nintrinsic gets careful human review.\n\n**The specs themselves need checking.** Early on, `Spec.Curve25519`’s\ndoubling step used `BB` where RFC 7748 says `AA`. Everything verified,\nbecause the code faithfully implemented the spec, typo and all. A small\nPython oracle run against the RFC 7748 vectors found it in minutes. This is\nwhy every primitive is tested against published vectors, and why the specs\nnow carry the correctness theorems above.\n\nSo verification doesn’t make the risk disappear. It concentrates it in a few small, named, tested places: the timing of a handful of secret-indexed routines, a short list of axiomatized intrinsics, and the specs. Everything else can be optimized as aggressively as the proofs allow.\n\n## Where hacl-pulse goes beyond HACL*\n\n[Section titled “Where hacl-pulse goes beyond HACL*”](#where-hacl-pulse-goes-beyond-hacl)\n\n- **New algorithms.** ML-KEM (FIPS 203) and ML-DSA (FIPS 204) in all three\nparameter sets each, which HACL* doesn’t have. HPKE in all four modes\nwith all three AEADs, where HACL* ships only Base mode over\nChaCha20-Poly1305. A portable, constant-time AES-GCM, where HACL* has only\nVale assembly.\n- **New theorems.** The group laws for all three curves, primality of the\nfield primes and group orders, and correctness theorems for every AEAD,\nkey exchange, signature scheme, and KEM. Some of these facts appear in\nHACL*, but unproved: its AEAD`correctness` lemma, the primality of the\nP-256 prime, and the group laws are all`admit()` .\n- **Verified memory wiping.** Every secret stack buffer must be wiped\nbefore the function returns, and Pulse’s resource discipline checks it: a\nforgotten wipe is a type error.\n- **Speed.** 16 of 26 benchmarks beat HACL*’s best configuration,\nincluding Salsa20 and secretbox (0.24x), SHA-512, SHA-3, and SHAKE (about\n0.5x), RSA-PSS verification (0.43x), and all of P-256 (0.47x to 0.57x).\n- **No assembly.** Every fast path is verified Pulse over a small set of\ntested C intrinsics, so there’s no separate assembly-verification\ntoolchain to maintain.\n\n## Where hacl-pulse still falls short of HACL*\n\n[Section titled “Where hacl-pulse still falls short of HACL*”](#where-hacl-pulse-still-falls-short-of-hacl)\n\nParity is about algorithms. On most other things that make a library usable in practice, HACL* is still well ahead.\n\n- **Fewer platforms.** The fast paths are x86-64 only. The C builds and\npasses its tests on aarch64, but runs only portable code there (no NEON).\nIt also uses`unsigned __int128` , so it needs a 64-bit target and GCC or\nClang: no 32-bit, no MSVC. HACL* supports all of those.\n- **No agile layer.** There’s no EverCrypt-style provider for choosing an\nalgorithm at run time behind one state type. This is deliberate, since\ndispatch (AES-NI or portable AES, say) happens inside each algorithm. But a\ncaller who wants “a hash” or “an AEAD” chosen at run time has to write the\nswitch.\n- **No other language targets.** HACL* has[OCaml bindings](https://opam.ocaml.org/packages/hacl-star/) , a\nWebAssembly/JavaScript build, and verified Rust used by[libcrux](https://github.com/cryspen/libcrux) . hacl-pulse produces only C.\n- **Not a drop-in replacement.** The API differs in names\n(`Hacl_Streaming_SHA2_*` , not`Hacl_Hash_SHA2_*` ), lengths, DRBG state\nownership, and error conventions. A few HACL* surfaces have no counterpart\nyet: streaming Poly1305, BLAKE2 salt and personalization, and the\nfixed-width`Bignum256` /`Bignum4096` and`GenericField` APIs.\n- **One amalgamated file.**`dist/` is a single, large`Hacl.c` . HACL* ships\none file per algorithm, so embedders can take only what they need.\n- **Assembly-level curve performance.** X25519 and Ed25519 signing are about\n1.3x behind HACL*’s BMI2/ADX assembly under gcc, and bignum about 2x\nbehind OpenSSL. That’s part of the cost of avoiding assembly.\n- **Maturity.** HACL* has years of deployment, external scrutiny, and\nstable releases. hacl-pulse is six weeks old, unaudited, tracks F*`master` , and has been checked with only one F* and Z3 toolchain.\n\nMost of these are engineering rather than research. ARM vector backends, Rust extraction, and per-algorithm distribution are all on the table. But today hacl-pulse is a demonstration of what a modern verified crypto library can look like, not a production replacement for HACL*.\n\n## How it was built\n\n[Section titled “How it was built”](#how-it-was-built)\n\nhacl-pulse went from an empty repository to feature parity in about six\nweeks and about 420 commits. Almost all of the specifications, code, and proofs\nwere written by AI agents running Claude Opus 5 and, later, Opus 5.5,\nfollowing the workflow from our [AutoCLRS](https://risemsr.github.io/blog/2026-03-06-autoclrs) and\n[agentic PoP](https://risemsr.github.io/blog/2026-02-04-nik-agentic-pop) posts. Humans set the\ndirection. The first commits were\n[design documents](https://github.com/FStarLang/hacl-pulse/tree/master/design):\nan inventory of HACL*’s API, the design changes above, and a feasibility\nstudy for replacing Vale. Everything the agents produced was checked by the\nverifier, the published test vectors, and the benchmark harness.\n\n- **The design documents paid off.** They gave the agents a stable target\nand gave us a checklist for parity.\n- **Proof engineering knowledge accumulates.** The design notes’[“Proof engineering notes”](https://github.com/FStarLang/hacl-pulse/blob/master/design/notes.md#proof-engineering-notes) record 72 lessons, each written down after it cost real time. One example\nis “a product of two variables under a quantifier poisons the whole SMT\nquery.” F* discharges proofs with the Z3 solver, which handles linear\narithmetic well and nonlinear arithmetic poorly. An invariant like “for\nall`k < i * n` , …” puts a product of variables under a`forall` , and\nthat can make*every* fact in the query unprovable, even`i == j` from`i < j + 1` and`not (i < j)` . FrodoKEM’s message encoding hit this and\ntook over four hours to verify. The fix was to restate the invariant so\nthat the bound stays linear. Notes like this help the next agent, or human.\n- **Specification review is still the bottleneck.** A verified refinement of\nthe wrong spec is still wrong, as the Curve25519 bug showed. That’s why the\nspecs carry their own theorems (AEADs decrypt what they encrypt, key\nexchanges agree, honest signatures verify, KEMs and HPKE round-trip),\nwhich are much easier to review than the spec functions themselves. See\nalso our posts on[intent formalization](https://risemsr.github.io/blog/2026-03-05-shuvendu-intent-formalization) and[small proof-oriented tests](https://risemsr.github.io/blog/2026-04-16-spotting-specs) .\n\n## Conclusions\n\n[Section titled “Conclusions”](#conclusions)\n\n**Large verified libraries can now be replicated quickly in a new\nlanguage.** Pulse is essentially a new language compared to Low*, with\nseparation logic instead of explicit memory footprints, type classes instead\nof tactics, and a different extraction pipeline. HACL*’s feature set, built\nover many years, was reproduced and extended in about six weeks.\n\n**Architectural changes are now viable as rewrites.** Secret integers as\nabstract types, `size_t` everywhere, type-enforced memory wiping, and no\nassembly are cross-cutting decisions that touch every file. In a mature\ncodebase, the migration would usually cost too much. With agents, you can\nenforce such decisions by rewriting everything in the new style.\n\n**State-of-the-art performance should be table stakes.** Other libraries\n(HACL*, OpenSSL, AWS-LC, libsodium, libjade, and more) were invaluable as\nperformance targets and sources of algorithmic ideas. When an agent can read\nhow the fastest implementation works, try the idea, and verify the result,\nreaching the state of the art (or any other measurable goal) becomes a matter\nof token spend. Verified code no longer has to be “correct but slower.”\n\n**The incumbents’ advantage has shrunk dramatically.** Mature libraries used\nto have a huge lead in accumulated investment that newcomers could hardly\nmatch. Now a newcomer with a better design can catch up rapidly. **Now is the\ntime to design new languages. The libraries come for free!**\n\n**Agents connect proof assistants to computer algebra without\ninfrastructure.** Connecting a computer algebra system to a proof assistant\nused to be a research project in its own right (see, for example, [*A\nbi-directional extensible interface between Lean and\nMathematica*](https://arxiv.org/abs/2101.07758)). For the group laws, the\nagent simply wrote Singular and PARI/GP scripts, ran them, and turned their\noutput into certificates that F* checks. No infrastructure was needed, and\nthe same approach works in any proof assistant.\n\n## Try it\n\n[Section titled “Try it”](#try-it)\n\nYou need a recent F* [nightly](https://github.com/FStarLang/FStar-nightly/releases)\nwith Pulse and Custard. Verification takes about half an\nhour on 24 cores. If you just want the C, each CI run publishes the\nextracted `dist/` as an artifact, and `make DIST_ONLY=1 test` builds and\ntests it without F*. hacl-pulse is licensed under Apache 2.0. The remaining\nperformance work is in the\n[roadmap](https://github.com/FStarLang/hacl-pulse/blob/master/design/notes.md#roadmap), and we’re\nconsidering an agile API and probabilistic failure bounds for the lattice\nKEMs. We’d love feedback, especially from HACL* users: what would it take\nfor you to switch?", "url": "https://wpnews.pro/news/hacl-pulse-re-verifying-hacl-in-pulse-with-agents", "canonical_source": "https://risemsr.github.io/blog/2026-10-20-hacl-pulse/", "published_at": "2026-10-20 00:00:00+00:00", "updated_at": "2026-10-22 05:41:40.866321+00:00", "lang": "en", "topics": ["ai-agents", "ai-research", "artificial-intelligence"], "entities": ["hacl-pulse", "HACL*", "Gabriel Ebner", "F*", "Pulse", "Custard", "Claude Opus 5", "ML-KEM"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/hacl-pulse-re-verifying-hacl-in-pulse-with-agents", "markdown": "https://wpnews.pro/news/hacl-pulse-re-verifying-hacl-in-pulse-with-agents.md", "text": "https://wpnews.pro/news/hacl-pulse-re-verifying-hacl-in-pulse-with-agents.txt", "jsonld": "https://wpnews.pro/news/hacl-pulse-re-verifying-hacl-in-pulse-with-agents.jsonld"}}