March Docs

MlDsa

MlDsa: ML-DSA-65 digital signatures (FIPS 204), the NIST post-quantum signature scheme.

Pure ML-DSA with an empty context string. Signing is hedged by default (sign mixes 32 fresh random bytes in, FIPS 204's default); sign_deterministic is the deterministic variant. Both verify the same way.

Backed by the vendored mldsa-native (runtime/pqc.c, runtime/third_party/), portable C, the same code on the compiled and interpreted backends.

Types

typeKeyPairKeyPair = { public_key : Bytes, secret_key : Bytes }#

Functions

fnkeypairkeypair() : KeyPair#

A fresh key pair from 32 random bytes.

let kp = MlDsa.keypair()
-- kp.public_key: 1952 bytes, kp.secret_key: 4032 bytes
fnkeypair_from_seedkeypair_from_seed(seed : Bytes) : Result(KeyPair, String)#

The key pair a 32-byte seed determines (FIPS 204 ML-DSA.KeyGen_internal). Err for a seed of any other length.

fnpublic_key_bytespublic_key_bytes() : Int do 1952 end#

Public key size in bytes: 1952.

fnsecret_key_bytessecret_key_bytes() : Int do 4032 end#

Secret key size in bytes: 4032.

fnsignsign(secret_key : Bytes, msg : Bytes) : Result(Bytes, String)#

Sign [msg] with [secret_key] (hedged: 32 fresh random bytes are mixed in, so two signatures of one message differ). Err for a wrong-length key.

match MlDsa.sign(kp.secret_key, Bytes.from_string("hello")) do
  Ok(s) -> MlDsa.verify(kp.public_key, Bytes.from_string("hello"), s)  -- true
  Err(e) -> ...
end
fnsign_deterministicsign_deterministic(secret_key : Bytes, msg : Bytes) : Result(Bytes, String)#

Sign [msg] with [secret_key] deterministically (FIPS 204's deterministic variant: the same key and message always give the same signature).

fnsignature_bytessignature_bytes() : Int do 3309 end#

Signature size in bytes: 3309.

fnverifyverify(public_key : Bytes, msg : Bytes, signature : Bytes) : Bool#

Whether [signature] is a valid signature of [msg] under [public_key]. False for any malformed argument, never an error.