$ coqc -Q . FV

Formal Verification

// why formal methods?

What & Why

Formal verification is the act of mathematically proving that a program meets its specification, not testing it on a handful of inputs, but guaranteeing correctness across all possible executions.

I am deeply invested in this field and convinced that the future of computer-science security runs through writing programs in formal languages. As software grows more complex and AI-generated code spreads, stating a property once and getting a machine-checked proof that it holds forever is the strongest assurance we can offer. Rather than patching vulnerabilities after deployment, we prevent them at the source, by making incorrect programs impossible to express.

Testing

finds bugs

Runs the program on chosen inputs. A green suite means "no bug seen yet": the untested paths stay unknown.

Proving

proves their absence

A machine-checked argument covers every input at once. If it type-checks, the property holds, permanently.

// proof-assistants

General-Purpose Proof Assistants

These systems let you state arbitrary theorems about programs and prove them interactively. They (almost) all share the same pipeline: you write a specification and a proof, a tiny trusted kernel checks it, and then verified code is extracted or compiled out.

Specificationwhat "correct" meansProof scripttactics / termsKernel checksmall trusted coreExtractionto OCaml / C / …Executableproven correct↑ the only code you must trust
The whole edifice rests on the kernel: if it accepts the proof, the theorem is true, even if the proof was written by a machine or an LLM.

They differ mainly in where trust lives. Kernel-checked systems re-verify every proof through a minimal core; SMT-backed systems trade some trust for automation by leaning on an external solver (Z3) that sits inside the trusted base.

Rocq (Coq)

kernel-checked
foundation
Calculus of Inductive Constructions, full dependent types.
get code out
Extraction to OCaml / Haskell / Scheme; CertiCoq & CertiRocq to C; Fiat-Crypto to C / Rust / Go / Zig.
sweet spot
Maximal expressiveness: verified compilers and deep mathematics.
in the wild
CompCert, seL4 (partial), Fiat-Crypto in BoringSSL & Linux crypto.

Lean 4

kernel-checked
foundation
Dependent type theory with first-class metaprogramming.
get code out
Built-in Lean→C backend (reference counting), no extraction step.
sweet spot
Unified maths library (Mathlib); best fit for LLM-assisted proving.
in the wild
Mathlib, a fast-growing AI theorem-proving research ecosystem.

Isabelle/HOL

kernel-checked
foundation
Higher-order logic on a tiny LCF-style kernel.
get code out
Code generation to Haskell / SML / OCaml; AutoCorres verifies existing C.
sweet spot
Large-scale system proofs; Sledgehammer automation (E, Vampire, Z3).
in the wild
seL4 verified microkernel, TLS 1.3 analysis, concurrent-GC proofs.

F*

SMT-backed
foundation
Dependent + refinement types + effects, discharged by Z3.
get code out
KaRaMeL extracts a low-level, constant-time C subset (no GC).
sweet spot
Verified cryptography with high automation.
in the wild
HACL*, EverCrypt, miTLS: shipping in Firefox and Linux.

SPARK / Ada

SMT-backed
foundation
Contract-based (pre/post, invariants), not a full proof assistant.
get code out
Native GNAT compiler; bare-metal, no GC, WCET-friendly.
sweet spot
Fully automatic proofs for safety-critical embedded code.
in the wild
Airbus A350, Ariane 6, DO-178C avionics, secure microcontrollers.

// verify --crypto

Cryptographic Verification

Cryptography needs two guarantees at once: the maths must be secure (a break implies solving a hard problem) and the implementation must match that maths, in constant time, down to the assembly. No single tool does everything, so they hand off to one another across four layers: from a high-level security model down to the running binary.

modelspeccodebinaryEasyCryptproveJasmincompileCryptoBAPrecoverCryptoVerifproveSecurity spec+ proofExtracted modelchecked vs specJasmin source.jazzAssemblyverifiedRecovered modelsound abstractionLift + sym-execbinary → modelExisting binarynot yoursProtocol modelhand-writtenSecurity proofautomated gamesproof / modelcode / binarycompiler outtool hand-off
Read it bottom-up on the right (recover a model from an untrusted binary) and top-down on the left (prove a spec, then compile it to verified assembly).

Jasmin : write it

An assembly-like language whose compiler is itself proved correct. You get predictable, constant-time machine code with no hidden compiler surprises, ideal for primitives like ChaCha20 or Kyber.

EasyCrypt : prove the maths

Game-based proofs: it formalises reductions such as "breaking this scheme means solving a hard problem" (IND-CCA2, EUF-CMA), and its extracted model is checked against the Jasmin code.

CryptoVerif : prove the protocol

Works in the computational model and automates the sequence of game transformations, proving whole protocols secure: TLS 1.3, Signal, WireGuard.

CryptoBAP : audit what you can't rewrite

Lifts an existing binary through symbolic execution into a sound formal model, so a deployed closed-source library can be fed to CryptoVerif or EasyCrypt for analysis.

// verify --smt <program>

SMT-Based Verifiers

Instead of interactive proofs, these tools verify programs written in mainstream languages (Rust, C…). You annotate the code with contracts; the tool turns the "what could go wrong?" question into logical formulas and hands them to an automated solver. When it succeeds there is no manual proof to write; when it fails you often get a concrete counterexample.

Source + specscontracts, invariantsVC generationweakest preconditionSMT solverZ3 / CVC5 / Alt-Ergo✓ Provedholds ∀ inputs✗ Counter-ex.an input that breaks it
The solver sits in the trusted base: you trust that Z3's "yes" is sound, in exchange for push-button automation.

Frama-C

C
how it works
ACSL contracts in /*@ … */ comments. The WP plugin generates weakest-preconditions, sent through Why3 to Z3 / CVC5 / Alt-Ergo.
why it stands out
The most mature C verifier: copes with pointers, unions and volatile in real industrial code.
in the wild
Aerospace, automotive (ISO 26262), verified TLS fragments.

Creusot

Rust
how it works
Pearlite specs as Rust proc-macros. Translates ownership-typed Rust into WhyML, then Why3 dispatches to SMT solvers.
why it stands out
High automation that stays close to the normal Rust workflow; handles iterators and closures.
in the wild
CreuSAT: a fully verified SAT solver written in Rust.

Aeneas

Rust
how it works
Lifts safe Rust (via the Charon/LLBC frontend) into pure functional models; proofs are then written in Lean, Rocq, F* or HOL4.
why it stands out
Rust's borrow checker removes aliasing, so the generated models are clean and pointer-free.
in the wild
Verified cryptographic primitives; shares Charon with hax & Eurydice.

hax

Rust
how it works
Extracts Rust to F* or Rocq for proofs, or to ProVerif for protocol analysis. Trait contracts guide the translation.
why it stands out
Crypto-focused and plugs into the existing F* / HACL* ecosystem.
in the wild
libcrux and formally verified ML-KEM (post-quantum KEM).

Kani

Rust
how it works
Bounded model checking: translates Rust MIR to CBMC and exhaustively explores all paths up to a bound. Harnesses look like tests.
why it stands out
Very low adoption cost: great for memory-safety, panic-freedom and API contracts.
in the wild
AWS Firecracker, s2n-quic, the Rust std-library verification effort.

A companion worth noting: Eurydice is not a verifier but a compiler that turns a safe Rust subset (via the same Charon frontend as Aeneas & hax) into readable low-level C, the bridge from Rust-based proofs to embedded and kernel deployment.