code wiki / (root) / nx_pq_hybrid.nx

nx_pq_hybrid.nx

buildroot/runtime/nx_pq_hybrid.nx

15810 B312 linesdepth 5pulls 12 transitivereach 0 importersview sourcekind tool
docsdependenciesstructsconstsfunctions

about

nx_pq_hybrid.nx -- C8 of the comms lane: POST-QUANTUM HYBRID KEY TRANSPORT for the C7 ratchet. Contract symbol pqh_kex == the /compare/comms C8 watch. REFEREE: nx_pq_hybrid_gate. DONE-RULE (comms.plan): hybrid classical+KEM ratcheting in the C7 tree (Signal SPQR / triple-ratchet class): the gate proves a classical-only decryption fails AND a KEM-only decryption fails -- both families must break. WHAT IT DOES: C7 distributes each epoch's commit_secret to the remaining members over X25519-only ECDH. C8 replaces that transport with the X25519MLKEM768 HYBRID KEM (TLS 1.3 named group 0x11EC, draft-ietf-tls-ecdhe-mlkem): the wrap key is derived from a 64-byte secret that is ml_kem_768_shared(32) || x25519_shared(32), so recovering it needs BOTH the elliptic-curve secret AND the lattice KEM secret. An adversary who breaks X25519 (a future quantum computer solving ECDH) still cannot read the commit secret without also breaking ML-KEM-768 -- and vice versa. Because the commit secret drives every subsequent epoch, this defeats harvest-now-decrypt-later on the whole forward chain, which is exactly the property Signal's SPQR triple ratchet added in Oct-2025. PURE COMPOSITION, no new crypto: it forks NOTHING and imports the sovereign hybrid KEM (nx_x25519mlkem768, itself x25519 + ml_kem_768) plus sha256 (HMAC/HKDF) and chacha20 (RFC 8439). No WebRTC, no OpenSSL, no liboqs -- the ML-KEM here is the estate's own FIPS 203 implementation. THE PROOF, mechanical: unwrap takes a `corrupt` argument that damages ONE half of the recovered 64-byte hybrid secret before deriving the wrap key -- corrupt=pq models an attacker who has the classical half but not the lattice half (broke ECDH, not ML-KEM), corrupt=classical the reverse. Either damaged half fails the HMAC and decrypts NOTHING; only corrupt=none succeeds. That is the two-independent-families claim made falsifiable. DEBT NAMED, not hidden: mg_hmac/mg_derive/hex/file helpers here are the same shapes as nx_mls_group. Extracting them to a shared nx_kdf_lib that BOTH C7 and C8 import is the correct DRY move and is filed (comms.plan debt kdf-lib-extract); shipping C8 independent now lands the verified exceed without re-opening the just-gated C7 binary in the same breath. license_tier: ORIGINAL No hw writes (Rule 26).

dependencies 5 imports · 0 importers

nx_syscalls.nx nx_itoa_lib.nx nx_x25519mlkem768.nx nx_sha256.nx nx_chacha20.nx nx_pq_hybrid.nx

imports: nx_syscalls.nxnx_itoa_lib.nxnx_x25519mlkem768.nxnx_sha256.nxnx_chacha20.nx

imported by: nobody (leaf or entry point)

call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown

main pqh_usage pqh_w sys_write sys_exit pqh_eq pqh_keygen sys_mmap nxa_die sys_write ↻ sys_exit ↻ nxa_lock_take nxa_lock_addr sys_write ↻ nxa_lock_give nxa_lock_addr ↻ nxa_report_overrun sys_write ↻ nxa_dump_printable sys_write ↻ nxa_dump_sizes sys_write ↻ pqh_hexd pqh_hexv pqh_len pqh_w ↻ pqh_hexe pqh_write sys_openat_wr sys_write ↻ sys_fsync sys_close pqh_wrap sys_mmap ↻ pqh_read sys_openat_rd sys_read sys_close ↻ pqh_w ↻ pqh_hexd ↻

structs

none

consts

37const PQH_EXIT_OK: i64 = 0
38const PQH_EXIT_USAGE: i64 = 2
39const PQH_EXIT_REFUSED: i64 = 3
40const PQH_EXIT_CORRUPT: i64 = 5
42const PQH_SHARE: i64 = 1216 // ek(1184) || x_pub(32) -- a member's hybrid public share
43const PQH_SECRET: i64 = 2432 // x_priv(32) || dk(2400) -- a member's hybrid secret
44const PQH_SRVSHARE: i64 = 1120 // ct(1088) || x_pub_s(32) -- the encapsulation to a member
45const PQH_SS: i64 = 64 // mlkem_ss(32) || x_ss(32) -- the hybrid shared secret
46const PQH_RAND96: i64 = 96
47const PQH_RAND64: i64 = 64
48const PQH_KEY: i64 = 32
49const PQH_MAC: i64 = 32
50const PQH_NONCE: i64 = 12
51const PQH_BLOCK: i64 = 64
52const PQH_IPAD: i64 = 54
53const PQH_OPAD: i64 = 92
54const PQH_NL: i64 = 10
55const PQH_PIPE: i64 = 124
56const PQH_FILECAP: i64 = 262144

functions

58func pqh_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
59func pqh_num(v: i64) -> i64 { nxi_out(v); return 0 }
calls 1: nxi_out
60func pqh_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
61func pqh_eq(a: *u8, b: *u8) -> i64
called by 2: pqh_unwrapmain
67func pqh_memeq(a: *u8, b: *u8, n: i64) -> i64 { var i: i64 = 0; while i < n { if a[i] != b[i] { return 0 } i = i + 1 } return 1 }
called by 1: pqh_unwrap
68func pqh_hexe(src: *u8, n: i64, dst: *u8) -> i64
81func pqh_hexv(c: i64) -> i64
called by 1: pqh_hexd
87func pqh_hexd(src: *u8, n: i64, dst: *u8) -> i64
100func pqh_read(path: *u8, buf: *u8, cap: i64) -> i64
108func pqh_write(path: *u8, buf: *u8, n: i64) -> i64
117func pqh_cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { d[o] = s[i]; o = o + 1; i = i + 1 } return o }
called by 2: pqh_derivepqh_wrap
119func pqh_field(line: *u8, n: i64, idx: i64, out: *u8) -> i64
called by 1: pqh_unwrap
133func pqh_hmac(key: *u8, klen: i64, msg: *u8, mlen: i64, out: *u8) -> i64
160func pqh_derive(secret: *u8, slen: i64, label: *u8, out: *u8) -> i64
called by 1: pqh_kex calls 3: sys_mmappqh_catpqh_hmac
171func pqh_kex(hybrid_ss: *u8, corrupt: i64, wrapkey: *u8) -> i64
182func pqh_keygen(share_out: *u8, secret_out: *u8, rand96_hex: *u8) -> i64
202func pqh_wrap(share_file: *u8, rand64_hex: *u8, payload_hex: *u8, wrap_out: *u8) -> i64
248func pqh_unwrap(secret_file: *u8, wrap_file: *u8, corrupt_s: *u8) -> i64
299func pqh_usage() -> i64
called by 1: main calls 1: pqh_w
303func main(argc: i64, argv: *i64) -> i64