code wiki / (root) / ml_dsa_65.nx

ml_dsa_65.nx source

↩ module page · 106 lines · 3681 B

1// ml_dsa_65.nx -- ML-DSA-65 post-quantum digital signature. 2// 3// NIST FIPS 204 (August 2024). Lattice-based (Module-LWE + 4// rejection sampling). Drop-in replacement for Ed25519 once 5// fault-tolerant quantum computers exist -- Ed25519 is broken 6// by Shor's algorithm. 7// 8// Parameters (FIPS 204 Table 2, ML-DSA-65 = NIST security 9// category 3, AES-192 equivalent): 10// n = 256 polynomial degree 11// q = 8380417 prime modulus 12// d = 13 dropped-bits parameter 13// tau = 49 signing challenge weight 14// lambda = 192 collision-resistance security parameter 15// gamma1 = 524288 16// gamma2 = 261888 17// (k, l) = (6, 5) matrix dimensions 18// eta = 4 19// beta = tau * eta = 196 20// omega = 55 21// 22// Key / signature sizes: 23// public key = 1952 bytes 24// secret key = 4032 bytes 25// signature = 3309 bytes 26// 27// Status (2026-04-23): SKELETON. Same scaffolded-then-filled 28// approach as ml_kem_768.nx -- API locked so TLS / X.509 / OTR 29// / email crypto callers can write against the signatures now; 30// the NTT + ExpandA + Keccak-streamed sampling core lands as a 31// separate push (~800 LoC). 32// 33// Dependencies (pending full impl): 34// - sha3.nx: SHAKE-256 for ExpandA, ExpandS, challenge c 35// - rand.nx: 32-byte seed at keygen 36// - ct.nx: constant-time rejection sampling 37// 38// Invariants: 39// DSA1 Output bytes match FIPS 204 Appendix A test vectors. 40// DSA2 Verify ALWAYS accepts a genuine signature from the 41// same (msg, pk) pair; rejects any bit-flip in ct mode. 42// DSA3 Sign uses deterministic (kappa-based) randomness to 43// avoid Sony-PS3-ECDSA class nonce-reuse attacks. 44 45import "syscalls.nx" 46 47const MLDSA_N: i64 = 256 48const MLDSA_Q: i64 = 8380417 49const MLDSA_D: i64 = 13 50const MLDSA_TAU: i64 = 49 51const MLDSA_LAMBDA: i64 = 192 52const MLDSA_K: i64 = 6 53const MLDSA_L: i64 = 5 54const MLDSA_ETA: i64 = 4 55const MLDSA_BETA: i64 = 196 56const MLDSA_OMEGA: i64 = 55 57 58const MLDSA_PK_BYTES: i64 = 1952 59const MLDSA_SK_BYTES: i64 = 4032 60const MLDSA_SIG_BYTES: i64 = 3309 61 62const MLDSA_ERR_PENDING: i64 = -1 63const MLDSA_ERR_RANDOM: i64 = -2 64const MLDSA_ERR_VERIFY: i64 = -3 65 66// KeyGen() -> (pk, sk) 67func ml_dsa_65_keygen(pk_out: *u8, sk_out: *u8) -> i64 { 68 var i: i64 = 0 69 while i < MLDSA_PK_BYTES { pk_out[i] = 0; i = i + 1 } 70 i = 0 71 while i < MLDSA_SK_BYTES { sk_out[i] = 0; i = i + 1 } 72 return MLDSA_ERR_PENDING 73} 74 75// Sign(sk, msg, msg_len) -> sig 76// FIPS 204 ยง5.2 deterministic signing (kappa counter-based nonce). 77func ml_dsa_65_sign(sk_in: *u8, 78 msg: *u8, msg_len: i64, 79 sig_out: *u8) -> i64 { 80 var i: i64 = 0 81 while i < MLDSA_SIG_BYTES { sig_out[i] = 0; i = i + 1 } 82 return MLDSA_ERR_PENDING 83} 84 85// Verify(pk, msg, msg_len, sig) -> 0 if good, MLDSA_ERR_VERIFY 86// if bad. Constant-time in the SIGNATURE; message can branch. 87func ml_dsa_65_verify(pk_in: *u8, 88 msg: *u8, msg_len: i64, 89 sig_in: *u8) -> i64 { 90 return MLDSA_ERR_PENDING 91} 92 93// Compile-only smoke. 94func main() -> i64 { 95 let pk: *u8 = sys_mmap(MLDSA_PK_BYTES) 96 let sk: *u8 = sys_mmap(MLDSA_SK_BYTES) 97 let sig: *u8 = sys_mmap(MLDSA_SIG_BYTES) 98 if ml_dsa_65_keygen(pk, sk) != MLDSA_ERR_PENDING { return 1 } 99 if ml_dsa_65_sign(sk, "hello", 5, sig) != MLDSA_ERR_PENDING { 100 return 2 101 } 102 if ml_dsa_65_verify(pk, "hello", 5, sig) != MLDSA_ERR_PENDING { 103 return 3 104 } 105 return 0 106}