code wiki / (root) / ml_kem_768.nx

ml_kem_768.nx source

↩ module page · 114 lines · 4361 B

1// ml_kem_768.nx -- ML-KEM-768 post-quantum key encapsulation. 2// 3// NIST FIPS 203 (August 2024). Lattice-based (Module-LWE), 4// security category 3 (AES-192 equivalent against both classical 5// and quantum adversaries). Drop-in replacement for the X25519 6// key-exchange primitive we ship today -- X25519 is broken by 7// Shor's algorithm once a sufficiently large fault-tolerant 8// quantum computer exists. ML-KEM stays secure even then. 9// 10// Parameters (FIPS 203 Table 2, ML-KEM-768): 11// n = 256 polynomial degree 12// q = 3329 modulus 13// k = 3 module rank 14// eta1 = 2 secret / error distribution parameter 15// eta2 = 2 noise distribution parameter 16// d_u = 10 ciphertext compression (u part) 17// d_v = 4 ciphertext compression (v part) 18// 19// Key / ciphertext sizes: 20// public key = 1184 bytes 21// secret key = 2400 bytes 22// ciphertext = 1088 bytes 23// shared secret = 32 bytes 24// 25// Status (2026-04-23): SKELETON. Structures + API signatures + 26// test vectors scaffolded. Heart of the impl -- NTT over 27// Z_3329[x]/(x^256+1), sample_poly, compress/decompress, K-PKE 28// wrapping -- is bounded ~600 LoC port from the NIST reference 29// and will ship next. 30// 31// Dependencies: 32// - sha3.nx (shipped) for SHAKE-128/256 + SHA3-256/512 33// - rand.nx (shipped) for 32-byte randomness at keygen + encap 34// 35// Invariants: 36// ML1 Output bytes match FIPS 203 Appendix A test vectors. 37// ML2 Constant-time critical paths (sample_ntt / compress / 38// decapsulate re-encrypt) -- no secret-dependent branches. 39// ML3 Zero dynamic allocation in decap hot path (fixed-size 40// buffers) for embedded deployability. 41 42import "syscalls.nx" 43 44const MLKEM_N: i64 = 256 45const MLKEM_Q: i64 = 3329 46const MLKEM_K: i64 = 3 47const MLKEM_ETA1: i64 = 2 48const MLKEM_ETA2: i64 = 2 49const MLKEM_DU: i64 = 10 50const MLKEM_DV: i64 = 4 51 52const MLKEM_PK_BYTES: i64 = 1184 53const MLKEM_SK_BYTES: i64 = 2400 54const MLKEM_CT_BYTES: i64 = 1088 55const MLKEM_SS_BYTES: i64 = 32 56 57const MLKEM_ERR_PENDING: i64 = -1 58const MLKEM_ERR_RANDOM: i64 = -2 59const MLKEM_ERR_DECAP: i64 = -3 60 61// ===== keygen ======================================================== 62// 63// KeyGen() -> (pk, sk) 64// Internal: pick 32 bytes of randomness, call K-PKE.KeyGen + hash 65// pk into sk so decapsulation can re-verify the implicit reject 66// path. 67func ml_kem_768_keygen(pk_out: *u8, sk_out: *u8) -> i64 { 68 // Placeholder: zero-fill to keep callers from reading uninit 69 // bytes during the skeleton phase. 70 var i: i64 = 0 71 while i < MLKEM_PK_BYTES { pk_out[i] = 0; i = i + 1 } 72 i = 0 73 while i < MLKEM_SK_BYTES { sk_out[i] = 0; i = i + 1 } 74 return MLKEM_ERR_PENDING 75} 76 77// Encaps(pk) -> (ct, ss) 78// Caller-visible: given Alice's public key, produce a ciphertext 79// to send her + the 32-byte shared secret. 80func ml_kem_768_encaps(pk_in: *u8, 81 ct_out: *u8, ss_out: *u8) -> i64 { 82 var i: i64 = 0 83 while i < MLKEM_CT_BYTES { ct_out[i] = 0; i = i + 1 } 84 i = 0 85 while i < MLKEM_SS_BYTES { ss_out[i] = 0; i = i + 1 } 86 return MLKEM_ERR_PENDING 87} 88 89// Decaps(sk, ct) -> ss 90// Caller-visible: given Alice's secret key + Bob's ciphertext, 91// recover the shared secret. Constant-time reject on 92// tampered ct (implicit rejection via rand hash of sk || ct). 93func ml_kem_768_decaps(sk_in: *u8, ct_in: *u8, 94 ss_out: *u8) -> i64 { 95 var i: i64 = 0 96 while i < MLKEM_SS_BYTES { ss_out[i] = 0; i = i + 1 } 97 return MLKEM_ERR_PENDING 98} 99 100// Compile-only smoke: confirm the API compiles + returns the 101// PENDING sentinel. Full test vector check lands with the 102// implementation. 103func main() -> i64 { 104 let pk: *u8 = sys_mmap(MLKEM_PK_BYTES) 105 let sk: *u8 = sys_mmap(MLKEM_SK_BYTES) 106 let ct: *u8 = sys_mmap(MLKEM_CT_BYTES) 107 let ss: *u8 = sys_mmap(MLKEM_SS_BYTES) 108 let ss2: *u8 = sys_mmap(MLKEM_SS_BYTES) 109 110 if ml_kem_768_keygen(pk, sk) != MLKEM_ERR_PENDING { return 1 } 111 if ml_kem_768_encaps(pk, ct, ss) != MLKEM_ERR_PENDING { return 2 } 112 if ml_kem_768_decaps(sk, ct, ss2) != MLKEM_ERR_PENDING { return 3 } 113 return 0 114}