Skip to content

netstar-labs/lattice

Repository files navigation

lattice

Pure-Go ML-DSA-65 (NIST FIPS 204, the standardized Dilithium) — a post-quantum lattice signature scheme, implemented for readability, with zero dependencies beyond the Go standard library.

⚠️ Educational implementation. Conformant and cross-checked (below), but not constant-time, side-channel audited, or FIPS validated. For production signing use an audited implementation — crypto/mldsa when the Go standard library ships it, or Cloudflare CIRCL today. This package is for studying how a lattice signature actually works, with a debugger in hand. Start with the introduction — it's the fun version.

Why it exists

Ed25519's security rests on discrete logs, which a large quantum computer breaks. The standardized post-quantum replacement rests on lattices — finding short vectors in thousands of dimensions — and reads like a different world: Fiat–Shamir with aborts, rejection sampling as a security mechanism, hints that patch rounding scars. The audited implementations are (rightly) optimized and hardened, which makes them hard to learn from. This one is the readable version: plain arithmetic, derived constants, spec names, and comments that explain why each rejection bound exists.

Conformance

For a battery of seeds, this implementation and Cloudflare CIRCL's ML-DSA-65 produce byte-identical key pairs and byte-identical deterministic signatures, and signatures cross-verify in both directions. Property tests pin the internals: NTT multiplication against schoolbook negacyclic convolution, the hint-recovery lemma over 500k inputs, canonical-encoding strictness, and every codec round-trip. go test -race clean.

Use

import "github.com/netstar-labs/lattice"

priv, _ := lattice.GenerateKey()            // or NewKeyFromSeed([32]byte{...})
pub := priv.PublicKey()

sig, _ := priv.Sign(msg, nil)               // hedged (default; nil = no context)
sig, _  = priv.SignDeterministic(msg, ctx)  // reproducible; ctx ≤ 255 bytes

ok := pub.Verify(sig, msg, nil)

// serialize / parse (FIPS 204 encodings)
pk := pub.Bytes()                           // 1952 bytes
sk := priv.Bytes()                          // 4032 bytes
pub2, _ := lattice.ParsePublicKey(pk)
priv2, _ := lattice.ParsePrivateKey(sk)     // integrity-checked against tr

A runnable demo lives in example/: go run ./example.

Sizes and speed

bytes Apple M2 Pro
public key 1,952 keygen ~242 µs
private key 4,032 sign ~385 µs (≈4 rejection passes avg)
signature 3,309 verify ~61 µs

(Ed25519 for scale: 32 / 64 / 64 bytes. Post-quantum is measured in bytes.)

Layout

path contents
params.go the ML-DSA-65 constants (FIPS 204 Table 1)
field.go mod-q arithmetic, rounding, hints (§7.4)
ntt.go the number-theoretic transform, twiddles derived at init (§7.5)
poly.go polynomials and module vectors (§6)
sample.go the four SHAKE samplers (§7.3)
pack.go bit codecs and the canonical hint encoding (§7.1)
lattice.go keys, sign, verify (§5–6)
docs/ introduction · architecture

License

GPL-3.0-or-later. See LICENSE.

About

ML-DSA-65 (FIPS 204) post-quantum lattice signatures in pure Go — a readable, conformant, educational implementation

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages