Get Started

Installation

v install --git https://codeberg.org/cstef/veil

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:

import veil.mlkem

// 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:

import veil.mldsa

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.