██╗███████╗ █████╗ ██████╗ ███████╗██╗ ██╗ █████╗
██║██╔════╝██╔══██╗██╔══██╗██╔════╝██║ ██║ ██╔══██╗
██║███████╗███████║██████╔╝█████╗ ██║ ██║ ███████║
██║╚════██║██╔══██║██╔══██╗██╔══╝ ██║ ██║ ██╔══██║
██║███████║██║ ██║██████╔╝███████╗███████╗███████╗██║ ██║
╚═╝╚══════╝╚═╝ ╚═╝╚═════╝ ╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝
Formally Verified Lattice Cryptography
Isabelle + Lattice = Isabella
Isabella provides formally verified implementations of post-quantum cryptographic primitives. All code is extracted from machine-checked Isabelle/HOL proofs, guaranteeing mathematical correctness.
| Component | Description |
|---|---|
| Canon/ | Verified Isabelle/HOL theories (source of truth) |
| isabella.hs/ | Haskell library with CLI |
| isabella.ml/ | OCaml library with CLI |
| isabella.ts/ | TypeScript/JavaScript library (via js_of_ocaml) |
| tests/ | Cross-validation against noble-post-quantum |
| Algorithm | Standard | Status |
|---|---|---|
| ML-KEM (Kyber) | FIPS 203 | Verified |
| ML-DSA (Dilithium) | FIPS 204 | Verified |
| NTT | - | O(n log n) Cooley-Tukey |
| Regev PKE | - | Verified |
| SIS Commitments | - | Verified |
- Isabelle 2024+ (2025-2 recommended)
- GHC 9.4+ and Cabal 3.0+ (for Haskell)
- opam with OCaml 4.14+ (for OCaml/TypeScript)
- Node.js 16+ (for TypeScript)
make all # Build Canon + all libraries
make test # Run all tests
make examples # Run examples in all languagesmake canon # Build Isabelle theories
make haskell # Build Haskell library
make ocaml # Build OCaml library
make typescript # Build TypeScript libraryTo regenerate the Haskell and OCaml libraries from Isabelle:
./generate.sh # Full pipeline: build Canon + generate + compile
./generate.sh --build-only # Skip Isabelle, just compile existing code
./generate.sh --run-examples # Generate + build + run examples
./generate.sh --lang haskell # Only Haskell
./generate.sh --lang ocaml # Only OCaml
./generate.sh --clean # Clean generated artifactsisabella-crypto/
├── Canon/ # Verified Isabelle theories (source of truth)
│ ├── ROOT # Session definitions
│ ├── Prelude.thy # Named theorems, mod utilities
│ ├── Algebra/
│ │ └── Zq.thy # Modular arithmetic Z_q
│ ├── Linear/
│ │ └── ListVec.thy # Vector/matrix operations
│ ├── Analysis/
│ │ └── Norms.thy # L-infinity norm, bounds
│ ├── Gadgets/
│ │ └── Decomp.thy # Base-B gadget decomposition
│ ├── Hardness/
│ │ ├── LWE_Def.thy # LWE problem definition
│ │ └── SIS_Def.thy # SIS problem definition
│ ├── Rings/
│ │ ├── PolyMod.thy # Polynomial rings Z_q[X]/(X^n+1)
│ │ ├── ModuleLWE.thy # Module-LWE and Module-SIS
│ │ └── NTT.thy # Number Theoretic Transform (O(n log n))
│ └── Crypto/
│ ├── Regev_PKE.thy # Regev public-key encryption
│ ├── Commit_SIS.thy # SIS-based commitments
│ ├── Kyber.thy # CRYSTALS-Kyber (ML-KEM / FIPS 203)
│ └── Dilithium.thy # CRYSTALS-Dilithium (ML-DSA / FIPS 204)
│
├── isabella.hs/ # Haskell library
│ ├── src/Canon/ # Generated/maintained modules
│ │ ├── Algebra/Zq.hs
│ │ ├── Linear/ListVec.hs
│ │ ├── Analysis/Norms.hs
│ │ ├── Gadgets/Decomp.hs
│ │ ├── Hardness/LWE_Def.hs
│ │ ├── Hardness/SIS_Def.hs
│ │ ├── Rings/PolyMod.hs
│ │ ├── Rings/ModuleLWE.hs
│ │ ├── Rings/NTT.hs
│ │ ├── Crypto/Regev_PKE.hs
│ │ ├── Crypto/Commit_SIS.hs
│ │ ├── Crypto/Kyber.hs
│ │ └── Crypto/Dilithium.hs
│ ├── app/ # CLI application
│ └── test/ # Test suite
│
├── isabella.ml/ # OCaml library
│ ├── src/canon/ # Generated/maintained modules
│ │ ├── zq.ml
│ │ ├── listvec.ml
│ │ ├── norms.ml
│ │ ├── decomp.ml
│ │ ├── lwe_def.ml
│ │ ├── sis_def.ml
│ │ ├── polymod.ml
│ │ ├── modulelwe.ml
│ │ ├── ntt.ml
│ │ ├── regev_pke.ml
│ │ ├── commit_sis.ml
│ │ ├── kyber.ml
│ │ └── dilithium.ml
│ ├── src/cli/ # CLI application
│ └── src/js/ # js_of_ocaml bindings
│
├── isabella.ts/ # TypeScript library (via js_of_ocaml)
│ ├── src/ # TypeScript wrappers
│ └── examples/ # Tests and examples
│
├── Makefile # Build orchestration
├── generate.sh # Code generation script
│
└── bench/ # Cross-language benchmarks
| Module | Description | Key Functions |
|---|---|---|
Zq |
Modular arithmetic over Z_q | mod_centered, vec_mod, encode_bit, decode_bit |
ListVec |
Vector and matrix operations | vec_add, inner_prod, mat_vec_mult, transpose |
Norms |
Vector norms and bounds | linf_norm, all_bounded |
| Module | Description | Key Types |
|---|---|---|
LWE_Def |
Learning With Errors problem | LWEParams, LWEInstance, lwe_sample |
SIS_Def |
Short Integer Solution problem | SISParams, SISInstance, is_sis_solution |
| Module | Description | Key Functions |
|---|---|---|
Decomp |
Base-B gadget decomposition | digit, decomp_base, recompose, gadget_vec |
| Module | Description | Key Functions |
|---|---|---|
PolyMod |
Polynomial ring Z_q[X]/(X^n+1) | poly_mult, ring_mod, ring_mult |
ModuleLWE |
Module-LWE and Module-SIS | mod_inner_prod, mod_mat_vec_mult, mlwe_sample |
NTT |
Number Theoretic Transform | ntt_fast, intt_fast, ntt_pointwise_mult |
| Module | Description | Key Functions |
|---|---|---|
Regev_PKE |
Regev public-key encryption | regev_keygen, regev_encrypt, regev_decrypt |
Commit_SIS |
SIS-based commitment scheme | commit, verify_opening |
Kyber |
CRYSTALS-Kyber (ML-KEM / FIPS 203) | kyber_keygen, kyber_encrypt, kyber_decrypt, kyber_encaps_simple |
Dilithium |
CRYSTALS-Dilithium (ML-DSA / FIPS 204) | power2round, decompose, highbits, lowbits, make_hint, use_hint |
import Canon
-- Basic modular arithmetic
let x = mod_centered 15 17 -- => -2
let encoded = encode_bit 97 True -- => 48
-- Vector operations
let dot = inner_prod [1,2,3] [4,5,6] -- => 32
-- Gadget decomposition (base-2, 8 digits)
let digits = decomp_base 2 8 42 -- => [0,1,0,1,0,1,0,0]
let back = recompose 2 digits -- => 42
-- NTT operations (O(n log n) Cooley-Tukey)
import Canon.Rings.NTT
let a_hat = ntt_fast [1,2,3,4] 17 3329 4
let a_back = intt_fast a_hat 17 3329 4
-- Kyber KEM
import Canon.Crypto.Kyber
-- Use kyber768Params for NIST Level 3 security# Run CLI
cd isabella.hs && cabal run isabella-cli -- examplesopen Canon
(* Basic modular arithmetic *)
let x = Zq.mod_centered 15 17 (* => -2 *)
let encoded = Zq.encode_bit 97 true (* => 48 *)
(* Vector operations *)
let dot = Listvec.inner_prod [1;2;3] [4;5;6] (* => 32 *)
(* Gadget decomposition *)
let digits = Decomp.decomp_base 2 8 42
let back = Decomp.recompose 2 digits (* => 42 *)
(* NTT operations *)
let a_hat = Ntt.ntt_fast [1;2;3;4] 17 3329 4
let a_back = Ntt.intt_fast a_hat 17 3329 4# Run CLI
cd isabella.ml && dune exec isabella_cli -- examplesimport { Zq, Vec, Mat } from '@isabella/canon';
// Basic modular arithmetic
const x = Zq.modCentered(15, 17); // => -2
const encoded = Zq.encodeBit(97, true); // => 48
// Vector operations
const dot = Vec.dot([1,2,3], [4,5,6]); // => 32
// Matrix operations
const A = [[1,2,3], [4,5,6]];
const Ax = Mat.vecMult(A, [1,1,1]); // => [6, 15]# Run examples
cd isabella.ts && node examples/example.mjsThe Canon is the source of truth - all library code corresponds to these formally verified Isabelle theories.
cd Canon && isabelle build -D .Or using the Makefile:
make canonCanon is organized into multiple Isabelle sessions:
| Session | Dependencies | Contents |
|---|---|---|
Canon_Base |
HOL | Prelude, ListVec, Zq, Norms |
Canon_Hardness |
Canon_Base | LWE_Def, SIS_Def |
Canon_Gadgets |
Canon_Base | Decomp |
Canon_Rings |
Canon_Hardness | PolyMod, ModuleLWE, NTT |
Canon_Crypto |
Canon_Rings | Regev_PKE, Commit_SIS, Kyber, Dilithium |
Each theory includes export_code commands that specify what to export:
(* From NTT.thy *)
export_code
ntt_params.make valid_ntt_params
ntt_n ntt_q ntt_omega
power_mod is_primitive_root
twiddle twiddle_factors
ntt_naive ntt_coeff
intt_naive intt_coeff mod_inverse
ntt_pointwise_mult
butterfly ntt_fast intt_fast
kyber_ntt_params dilithium_ntt_params
in Haskell module_name "Canon.Rings.NTT"-
Create the theory in
Canon/:theory MyTheory imports Canon_Base.Prelude Canon_Base.ListVec begin (* definitions and proofs *) export_code my_function in Haskell module_name "Canon.MyTheory" end
-
Add to
Canon/ROOT:session Canon_MySession in "MyDir" = Canon_Base + theories MyTheory -
Build and verify:
make canon
-
Create corresponding Haskell module in
isabella.hs/src/Canon/MyTheory.hs -
Create corresponding OCaml module in
isabella.ml/src/canon/mytheory.ml -
Update module lists in:
isabella.hs/isabella.cabal(exposed-modules)isabella.ml/src/canon/dune(modules)isabella.hs/src/Canon.hs(re-exports)isabella.ml/src/canon/canon.ml(re-exports)
-
Build and test:
make all make test
Canon theories are authored directly under Canon/<Subdir>/<Theory>.thy and
verified incrementally with Isabelle builds.
This section documents the complete workflow for adding a new formally verified cryptographic algorithm to Isabella, from initial specification to tested multi-language implementation.
The pipeline consists of six stages:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 1. Spec │───▶│ 2. Theory │───▶│ 3. Isabelle │
│ Design │ │ Authoring │ │ Proof │
└─────────────┘ └─────────────┘ └─────────────┘
│
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 6. Test │◀───│ 5. Code │◀───│ 4. Library │
│ Harness │ │ Export │ │ Integration │
└─────────────┘ └─────────────┘ └─────────────┘
Create a structured algorithm specification in your PR notes or project docs.
The prompt should include:
- Theory name and session: Where it fits in the Canon hierarchy
- Imports: Dependencies from existing Canon theories
- Step-by-step structure: Incremental definitions with proofs
- Parameters and types: Data structures for the algorithm
- Key operations: Functions to implement with their specifications
- Correctness properties: Lemmas to prove
- Export specification: What to export for code generation
Implement the theory directly in Canon/<Subdir>/<Theory>.thy and iterate in
small, semantics-preserving proof steps.
Typical loop:
- Add definitions and lemmas for one logical step
- Build the target session
- Resolve proof obligations
- Continue to the next step on success
Handling failures: If a step fails repeatedly, manual intervention may be needed:
- Check if required lemmas exist in
Canon/Prelude.thy - Look for similar patterns in existing theories (e.g.,
Kyber.thy) - Simplify the proof goal or add helper lemmas
Once the theory compiles, verify the full session:
cd Canon && isabelle build -v -D .Key checks:
- All lemmas prove without
sorry - No
quick_and_dirtymode in production - Export code commands are present and correct
Add the theory to each language library:
Haskell (isabella.hs/):
# Create module at src/Canon/Crypto/Dilithium.hs
# Add to isabella.cabal exposed-modules
# Add to src/Canon.hs re-exportsOCaml (isabella.ml/):
# Create module at src/canon/dilithium.ml
# Add to src/canon/dune modules list
# Add to src/canon/canon.ml re-exportsCLI Commands: Add operations to isabella.ml/bin/isabella_cli.ml:
let cmd_dil_power2round args = ...
(* Register in command dispatch *)
| "dil-power2round" -> cmd_dil_power2round argsRun the code generation pipeline:
./generate.sh # Full rebuild
# or
make haskell ocaml # Just the librariesNote: Isabelle's raw export may use zarith types. Hand-written wrapper modules often provide cleaner interfaces:
(* dilithium.ml - Clean interface wrapping raw Isabelle export *)
let power2round_coeff r d =
let two_d = 1 lsl d in
let r0 = mod_centered r two_d in
let r1 = (r - r0) / two_d in
(r1, r0)Add comprehensive tests in tests/:
Reference tests (tests/src/dilithium.test.ts):
// Compare against noble-post-quantum or other reference implementations
import { ml_dsa44 } from '@noble/post-quantum/ml-dsa';
it('signs and verifies', () => {
const keys = generateDsaKeyPair(ml_dsa44);
const sig = signMessage(ml_dsa44, keys.secretKey, message);
expect(verifySignature(ml_dsa44, keys.publicKey, message, sig)).toBe(true);
});CLI integration tests (tests/src/dilithium-integration.test.ts):
// Validate CLI against reference implementations
it('power2round matches reference', () => {
const cliResult = dilPower2Round(100000, 13);
const refResult = refPower2Round(100000, 13);
expect(cliResult.r1).toBe(refResult[0]);
expect(cliResult.r0).toBe(refResult[1]);
});CLI wrappers (tests/src/isabella-cli.ts):
export function dilPower2Round(r: number, d: number): Power2RoundResult {
const output = runCli(['dil-power2round', r.toString(), d.toString()]);
return parseCliResult<Power2RoundResult>(output);
}Run the full test suite:
cd tests && npm testThe CRYSTALS-Dilithium implementation followed this exact workflow:
- Specification: 12-step design for
Canon/Crypto/Dilithium.thy - Theory Authoring: Implemented proofs directly in
Canon/Crypto/Dilithium.thy - Verification: Built with
isabelle build -d Canon Canon_Crypto - Integration:
isabella.ml/src/canon/dilithium.ml- OCaml wrapper- 10 CLI commands (
dil-params,dil-power2round, etc.)
- Export: Haskell/OCaml code generation
- Testing:
- 49 reference tests against noble-post-quantum
- 27 CLI integration tests
- Cross-validation of compression functions
| Stage | Command | Output |
|---|---|---|
| Spec | Write algorithm spec in notes/docs | Specification file |
| Author | Edit Canon/<Path>/<Theory>.thy |
Proven theory |
| Verify | isabelle build -D Canon |
Proof checking |
| Integrate | Manual module creation | isabella.{hs,ml}/ modules |
| Export | ./generate.sh |
Compiled libraries |
| Test | cd tests && npm test |
Test results |
Compare performance across languages:
./bench/run-benchmarks.sh
./bench/summarize.shmake test # All tests
make test-haskell # Haskell only
make test-ocaml # OCaml only
make test-typescript # TypeScript only
make test-validation # Cross-validation against noble-post-quantumThe tests/ directory contains a comprehensive test suite that validates the Isabelle-generated code against reference implementations:
- 156 tests across 6 test suites
- Cross-validation with noble-post-quantum
- CLI integration tests calling the OCaml executable
- FIPS 203 (ML-KEM) and FIPS 204 (ML-DSA) compliance checks
cd tests && npm install && npm testSee tests/README.md for detailed test documentation.
Isabella includes a complete implementation of CRYSTALS-Kyber (FIPS 203 ML-KEM):
import Canon.Crypto.Kyber
-- Parameter sets
kyber512Params -- NIST Level 1 (k=2)
kyber768Params -- NIST Level 3 (k=3)
kyber1024Params -- NIST Level 5 (k=4)
-- Key generation (requires randomness: matrix A, secret s, error e)
let (pk, sk) = kyber_keygen matrixA secretS errorE
-- Encryption (requires randomness: r, e1, e2)
let ciphertext = kyber_encrypt pk message randR errorE1 errorE2
-- Decryption
let recovered = kyber_decrypt sk ciphertextThe implementation uses O(n log n) NTT via Cooley-Tukey for efficient polynomial multiplication.
Isabella includes compression and hint functions for CRYSTALS-Dilithium (FIPS 204 ML-DSA):
open Canon.Dilithium
(* Parameter sets *)
mldsa44_params (* NIST Level 2: k=4, l=4 *)
mldsa65_params (* NIST Level 3: k=6, l=5 *)
mldsa87_params (* NIST Level 5: k=8, l=7 *)
(* Compression functions *)
let (r1, r0) = power2round_coeff r 13 (* Power2Round *)
let (r1, r0) = decompose r alpha (* Decompose with alpha = 2*gamma2 *)
let hi = highbits r alpha (* Extract high bits *)
let lo = lowbits r alpha (* Extract low bits *)
(* Hint mechanism *)
let h = make_hint z r alpha (* Create hint bit *)
let recovered = use_hint h r alpha (* Recover high bits using hint *)
(* Bounds checking *)
let ok = check_bound value bound (* Check |value| < bound *)- Add/modify theories in
Canon/ - Build and verify:
make canon - Create/update library modules in
isabella.hs/andisabella.ml/ - Build libraries:
make all - Run tests:
make test
MIT