Get Started
Installation
Or add it to your v.mod dependencies:
Module {
name: 'my_app'
dependencies: ['veil']
}
Key exchange (ML-KEM)
Establish a shared secret with ML-KEM-768:
// alice: generate a keypair
dk := mlkem.DecapsulationKey.generate(.ml_kem_768)!
ek := dk.encapsulation_key()
// send ek.bytes() to bob...
// bob: encapsulate a shared secret
shared_key, ciphertext := ek.encapsulate()!
// send ciphertext back to alice...
// alice: recover the shared secret
recovered := dk.decapsulate(ciphertext)!
assert shared_key == recovered
The result is a 32-byte shared key you can feed into whatever symmetric cipher you want (AES-256-GCM, ChaCha20-Poly1305, you pick).
Digital signatures (ML-DSA)
Sign and verify a message with ML-DSA-65:
sk := mldsa.PrivateKey.generate(.ml_dsa_65)!
pk := sk.public_key()
msg := 'important document'.bytes()
sig := sk.sign(msg)!
assert pk.verify(msg, sig)! == true
Serialization
Keys and signatures serialize to and from []u8:
// serialize
ek_bytes := ek.bytes()
sk_bytes := sk.seed() // 32-byte seed for ML-DSA
dk_bytes := dk.bytes() // 64-byte seed (d || z) for ML-KEM
// deserialize
ek2 := mlkem.EncapsulationKey.from_bytes(ek_bytes, .ml_kem_768)!
sk2 := mldsa.PrivateKey.from_seed(sk_bytes, .ml_dsa_65)!
dk2 := mlkem.DecapsulationKey.from_seed(dk_bytes, .ml_kem_768)!
Which parameter set?
For most applications, go with ML-KEM-768 and ML-DSA-65 (Category 3, roughly AES-192 equivalent). Use Category 5 (ML-KEM-1024 / ML-DSA-87) if regulations demand it. Category 1 (ML-KEM-512 / ML-DSA-44) exists for constrained environments but offers less security margin.