x.crypto.mldsa #
mldsa
Pure V implementation of ML-DSA (FIPS 204), a post-quantum digital signature algorithm. Supports all three parameter sets (ML-DSA-44, ML-DSA-65, ML-DSA-87).
This is still experimental > It is verified against NIST ACVP test vectors for keygen, > signing, and verification, > but not yet production-ready.
Example
import x.crypto.mldsa
fn main() {
// generate a new ML-DSA-65 key pair
sk := mldsa.PrivateKey.generate(.ml_dsa_65)!
pk := sk.public_key()
// sign a message (with an optional context string)
msg := 'Hello ML-DSA'.bytes()
sig := sk.sign(msg, context: 'not-a-drill')!
// verify the signature with the same context
verified := pk.verify(msg, sig, context: 'not-a-drill')!
assert verified // true
// deterministic signing is also available
sig2 := sk.sign(msg, context: 'not-a-drill', deterministic: true)!
verified2 := pk.verify(msg, sig2, context: 'not-a-drill')!
assert verified2 // true
}
Constants #
const seed_size = 32
const public_key_size_44 = 32 + 4 * n * 10 / 8
s. 4, table 2
const public_key_size_65 = 32 + 6 * n * 10 / 8
const public_key_size_87 = 32 + 8 * n * 10 / 8
const signature_size_44 = 128 / 4 + 4 * n * (17 + 1) / 8 + 80 + 4
s. 4, table 2
const signature_size_65 = 192 / 4 + 5 * n * (19 + 1) / 8 + 55 + 6
const signature_size_87 = 256 / 4 + 7 * n * (19 + 1) / 8 + 75 + 8
fn compute_mu #
fn compute_mu(tr []u8, msg []u8, context string) [64]u8
algo. 2, lines 10-11: M' = 0x00 || |ctx| || ctx || M; mu = H(tr || M', 64) compute_mu computes mu = H(tr || M', 64) where M' = 0x00 || |ctx| || ctx || msg.
fn compute_mu_prehash #
fn compute_mu_prehash(tr []u8, msg []u8, context string, ph PreHash) [64]u8
algo. 4, line 23: M' = 0x01 || |ctx| || ctx || OID || PH(M) algo. 7, line 6: mu = H(tr || M') compute_mu_prehash computes mu for prehash mode: H(tr || 0x01 || |ctx| || ctx || OID || PH(msg), 64).
fn PrivateKey.from_bytes #
fn PrivateKey.from_bytes(raw []u8, kind Kind) !PrivateKey
from FIPS 204 semi-expanded encoding. seed() and equal() are meaningless on the result — use from_seed when possible.
fn PrivateKey.from_seed #
fn PrivateKey.from_seed(seed []u8, kind Kind) !PrivateKey
fn PrivateKey.generate #
fn PrivateKey.generate(kind Kind) !PrivateKey
algo. 1: ML-DSA.KeyGen (s. 5.1)
fn PublicKey.from_bytes #
fn PublicKey.from_bytes(raw []u8, kind Kind) !PublicKey
enum Kind #
enum Kind {
ml_dsa_44
ml_dsa_65
ml_dsa_87
}
s. 4, table 1
fn (Kind) public_key_size #
fn (k Kind) public_key_size() int
fn (Kind) private_key_size #
fn (k Kind) private_key_size() int
fn (Kind) signature_size #
fn (k Kind) signature_size() int
enum PreHash #
enum PreHash {
none // pure ML-DSA (default)
sha2_224
sha2_256
sha2_384
sha2_512
sha2_512_224
sha2_512_256
sha3_224
sha3_256
sha3_384
sha3_512
shake_128
shake_256
}
FIPS 204 s. 5.4: approved pre-hash functions for HashML-DSA.
struct PrivateKey #
struct PrivateKey {
seed [32]u8
pk PublicKey
s1 []NttElement // len = l
s2 []NttElement // len = k
t0 []NttElement // len = k
k [32]u8
}
fn (PrivateKey) public_key #
fn (sk &PrivateKey) public_key() &PublicKey
fn (PrivateKey) seed #
fn (sk &PrivateKey) seed() []u8
fn (PrivateKey) bytes #
fn (sk &PrivateKey) bytes() []u8
fn (PrivateKey) equal #
fn (sk &PrivateKey) equal(other &PrivateKey) bool
seed-based constant-time comparison. not meaningful for from_bytes keys.
fn (PrivateKey) equal_bytes #
fn (sk &PrivateKey) equal_bytes(other &PrivateKey) bool
constant-time comparison of the serialized key material. slower but works for from_bytes keys.
fn (PrivateKey) sign #
fn (sk &PrivateKey) sign(msg []u8, opts SignerOpts) ![]u8
algo. 2/4: ML-DSA.Sign / HashML-DSA.Sign (s. 5.2, 5.4.1)
fn (PrivateKey) sign_mu #
fn (sk &PrivateKey) sign_mu(mu []u8, rnd []u8) ![]u8
sign_mu signs a precomputed mu value with explicit randomness. mu must be 64 bytes. rnd must be 32 bytes (use all zeros for deterministic signing).
struct PublicKey #
struct PublicKey {
raw []u8
p Params
a []NttElement // k*l matrix in NTT domain
t1 []NttElement // len = k, NTT(t1 * 2^d)
tr [64]u8
}
fn (PublicKey) bytes #
fn (pk &PublicKey) bytes() []u8
fn (PublicKey) tr #
fn (pk &PublicKey) tr() []u8
tr returns the 64-byte transcript hash (H(pk)) used in mu computation.
fn (PublicKey) equal #
fn (pk &PublicKey) equal(other &PublicKey) bool
fn (PublicKey) verify #
fn (pk &PublicKey) verify(msg []u8, sig []u8, opts SignerOpts) !bool
algo. 3/5: ML-DSA.Verify / HashML-DSA.Verify (s. 5.3, 5.4.1)
fn (PublicKey) verify_mu #
fn (pk &PublicKey) verify_mu(mu []u8, sig []u8) !bool
struct SignerOpts #
struct SignerOpts {
pub:
context string
deterministic bool
prehash PreHash
}