code wiki / (root) / nx_connect_seal.nx

nx_connect_seal.nx source

↩ module page · 62 lines · 3294 B

1// nx_connect_seal.nx -- CONNECT message sealing on the REAL, RFC-8439-verified ChaCha20-Poly1305 AEAD, 2// built 2026-07-24 to retire the 78-line additive-cipher stand-in (nx_connect_e2ee_min) that the maturity 3// gauge honestly graded STUB. "State of the art from the first byte up": this composes the sovereign AEAD 4// whose KAT (nx_chacha20_poly1305_test) matches RFC 8439 2.8.2 byte-exact AND rejects a single-bit tamper. 5// 6// SECURITY DISCIPLINE (the parts a real messenger needs, that the stand-in never had): 7// * AUTHENTICATED: every message carries a Poly1305 tag; open() REJECTS any tampered ciphertext/tag. 8// * NONCE = per-message counter (4 zero bytes + 8-byte LE seq). Never reused for a key -> no keystream 9// reuse. Nonce reuse is the cardinal AEAD failure; the counter makes it structural. 10// * SEQUENCE-BOUND: the message counter is the AAD, so a replayed/reordered message fails to open under a 11// different expected seq -> reorder/replay is detected, not just tampering. 12// * CONTENT-BLIND BY CONSTRUCTION: the server stores only {seq, ciphertext, tag} and holds NO key, so it 13// cannot read (no key) and cannot forge (AEAD auth). Key EXCHANGE (getting the conv key to both peers 14// without the server) is a SEPARATE rung (X25519/MLS) -- not claimed here. 15// PURE CORE (no main): key/nonce/buffers are caller-supplied. Consumers: nx_connect_seal_gate, and (once the 16// AEAD chain is reachable from _hdl_build) the live serve. license_tier: ORIGINAL 17import "nx_chacha20_poly1305.nx" 18 19const CSEAL_KEY: i64 = 32 20const CSEAL_NONCE: i64 = 12 21const CSEAL_TAG: i64 = 16 22const CSEAL_OK: i64 = 1 // == NX_AEAD_VERDICT_OK 23const CSEAL_REJECT: i64 = 0 24 25// build the 12-byte nonce for message sequence `seq`: [0,0,0,0][seq as 8 LE bytes] 26func cseal_nonce(seq: i64, out: *u8) -> i64 { 27 out[0]=0 as u8; out[1]=0 as u8; out[2]=0 as u8; out[3]=0 as u8 28 var v: i64 = seq 29 var i: i64 = 4 30 while i < 12 { out[i] = (v & 0xff) as u8; v = v / 256; i = i + 1 } 31 return 0 32} 33// the AAD is the 8-byte LE sequence -> binds message order into the tag. 34func cseal_aad(seq: i64, out: *u8) -> i64 { 35 var v: i64 = seq 36 var i: i64 = 0 37 while i < 8 { out[i] = (v & 0xff) as u8; v = v / 256; i = i + 1 } 38 return 0 39} 40 41// SEAL: encrypt+authenticate plaintext at sequence seq. Writes ct[0..plen) and tag[0..16). Returns CSEAL_OK. 42func cseal_seal(key: *u8, seq: i64, pt: *u8, plen: i64, ct: *u8, tag: *u8) -> i64 { 43 let nonce: *u8 = sys_mmap(16) 44 let aad: *u8 = sys_mmap(16) 45 cseal_nonce(seq, nonce) 46 cseal_aad(seq, aad) 47 let v: i64 = nx_chacha20_poly1305_encrypt(key, nonce, aad, 8, pt, plen, ct, tag) 48 if v == NX_AEAD_VERDICT_OK { return CSEAL_OK } 49 return CSEAL_REJECT 50} 51 52// OPEN: verify+decrypt. Returns CSEAL_OK and fills pt[0..clen) ONLY if the tag verifies for THIS seq; 53// otherwise CSEAL_REJECT and pt is not trusted (tamper, wrong key, or wrong sequence). 54func cseal_open(key: *u8, seq: i64, ct: *u8, clen: i64, tag: *u8, pt: *u8) -> i64 { 55 let nonce: *u8 = sys_mmap(16) 56 let aad: *u8 = sys_mmap(16) 57 cseal_nonce(seq, nonce) 58 cseal_aad(seq, aad) 59 let v: i64 = nx_chacha20_poly1305_decrypt(key, nonce, aad, 8, ct, clen, tag, pt) 60 if v == NX_AEAD_VERDICT_OK { return CSEAL_OK } 61 return CSEAL_REJECT 62}