code wiki / (root) / nx_mls_group.nx

nx_mls_group.nx

buildroot/runtime/nx_mls_group.nx

35211 B799 linesdepth 5pulls 10 transitivereach 0 importersview sourcekind tool
docsdependenciesstructsconstsfunctions

about

nx_mls_group.nx -- C7 of the comms lane: MLS-CLASS EPOCHED GROUP RATCHET (RFC 9420 key schedule). Contract symbol mls_epoch_advance == the /compare/comms C7 watch. REFEREE: nx_mls_group_gate. DONE-RULE (comms.plan): RFC 9420-class epoched group keys on chat AND calls; member removal forces an epoch advance and the gate PROVES a removed member cannot decrypt the next frame (negative control), with forward-secrecy and post-compromise teeth; replaces the static room key. HONEST SCOPE, stated so nothing is over-claimed: this implements the RFC 9420 KEY SCHEDULE and its security properties -- one-way epoch chaining (forward secrecy), commit-secret injection (post-compromise security), and membership-bound epoch secrets (removal excludes). Commit secrets are distributed to the remaining members by FLAT per-member X25519 (O(n) wraps). TreeKEM's ratchet tree is the O(log n) DISTRIBUTION efficiency, and it is a follow-on, NOT a security gap -- every property proven by the gate is the same property TreeKEM delivers, just distributed pairwise here. PURE COMPOSITION of the sovereign primitives, no new crypto: x25519 (RFC 7748 ECDH) + sha256 (FIPS 180-4, as HMAC/HKDF-Expand) + chacha20 (RFC 8439). No WebRTC, no OpenSSL, one auditable stack. THE PROPERTIES, and how each is a tooth: * epoch_secret is per-member, never transmitted; only COMMITMENTS sha256("commit-check"||secret) are printed, so the gate can prove convergence without exposing a secret. * advance: prk = HMAC(old_epoch_secret, commit_secret); new = HKDF-Expand(prk,"epoch",group_ctx). One-way (HKDF) => forward secrecy. commit_secret fresh + ECDH-wrapped => post-compromise. * group_ctx = sha256(group_id || epoch || roster) => the epoch secret is BOUND to the membership, so a divergent roster diverges the secret (defence in depth behind the wrap exclusion). * a removed member is absent from the new roster, so no wrap is addressed to it: apply REFUSES, and even holding the old epoch_secret it cannot reach the new one (no commit_secret). * frames are real ChaCha20 sealed under framekey=HKDF(epoch_secret,"app",seq) + an HMAC tag, so open with the WRONG epoch_secret fails the MAC -- the removed member cannot open the next frame. license_tier: ORIGINAL No hw writes (Rule 26).

dependencies 5 imports · 0 importers

nx_syscalls.nx nx_itoa_lib.nx nx_x25519.nx nx_sha256.nx nx_chacha20.nx nx_mls_group.nx

imports: nx_syscalls.nxnx_itoa_lib.nxnx_x25519.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 mg_usage mg_w sys_write sys_exit mg_eq mg_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 ↻ mg_hexd mg_hexv mg_len mg_w ↻ sha256_digest sys_mmap ↻ sha256_init sys_mmap ↻ sha256_seed_allocated sha256_k sha256_update sha256_compress_ni_blocks blk_set_byte sha256_compress sha256_compress_ni blk_word blk_byte sha256_final

structs

none

consts

34const MG_EXIT_OK: i64 = 0
35const MG_EXIT_USAGE: i64 = 2
36const MG_EXIT_REFUSED: i64 = 3
37const MG_EXIT_CORRUPT: i64 = 5
39const MG_KEY: i64 = 32 // X25519 scalar / pubkey / epoch_secret / chacha key
40const MG_MAC: i64 = 32 // HMAC-SHA256 tag
41const MG_NONCE: i64 = 12 // chacha20 nonce
42const MG_BLOCK: i64 = 64 // sha256/HMAC block
43const MG_HEXCAP: i64 = 4096
44const MG_FILECAP: i64 = 262144
45const MG_NL: i64 = 10
46const MG_PIPE: i64 = 124
47const MG_COMMA: i64 = 44
48const MG_IPAD: i64 = 54 // 0x36
49const MG_OPAD: i64 = 92 // 0x5c
50const MG_ROSTER_MAX: i64 = 256

functions

52func mg_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
53func mg_wn(s: *u8, n: i64) -> i64 { sys_write(1, s, n); return 0 }
called by 1: mg_open calls 1: sys_write
54func mg_num(v: i64) -> i64 { nxi_out(v); return 0 }
55func mg_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
56func mg_eq(a: *u8, b: *u8) -> i64
called by 1: main
62func mg_cpy(d: *u8, s: *u8, n: i64) -> i64 { var i: i64 = 0; while i < n { d[i] = s[i]; i = i + 1 } return 0 }
63func mg_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 2: mg_unwrapmg_open
65func mg_hexe(src: *u8, n: i64, dst: *u8) -> i64
78func mg_hexv(c: i64) -> i64
called by 1: mg_hexd
84func mg_hexd(src: *u8, n: i64, dst: *u8) -> i64
98func mg_read(path: *u8, buf: *u8, cap: i64) -> i64
106func mg_write(path: *u8, buf: *u8, n: i64) -> i64
115func mg_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 }
116func mg_catn(d: *u8, o: i64, v: i64) -> i64
127func mg_hmac(key: *u8, klen: i64, msg: *u8, mlen: i64, out: *u8) -> i64
155func mg_derive(secret: *u8, label: *u8, ctx: *u8, clen: i64, out: *u8) -> i64
167func mg_ctx(group_id: *u8, epoch: i64, roster: *u8, out: *u8) -> i64
182func mls_epoch_advance(old_secret: *u8, commit_secret: *u8, group_id: *u8, epoch: i64, roster: *u8, out: *u8) -> i64
191func mg_clamp(p: *u8) -> i64
196func mg_pub(priv: *u8, pub: *u8) -> i64
205func mg_wrap(eph_priv: *u8, member_pub: *u8, payload: *u8, out: *u8) -> i64
218func mg_unwrap(member_priv: *u8, eph_pub: *u8, blob: *u8, payload: *u8) -> i64
232func mg_commit_hash(secret: *u8, out: *u8) -> i64
241func mg_roster_idx(roster: *u8, mypubhex: *u8) -> i64
called by 2: mg_joinmg_apply calls 1: mg_len
262func mg_field(line: *u8, n: i64, idx: i64, out: *u8) -> i64
276func mg_find_wrap(buf: *u8, n: i64, idx: i64, blob: *u8) -> i64
308func mg_keygen(priv_out: *u8, seed_hex: *u8) -> i64
329func mg_pubverb(priv_file: *u8) -> i64
345func mg_create(group_id: *u8, seed_hex: *u8, eph_hex: *u8, welcome_out: *u8, roster: *u8) -> i64
407func mg_join(welcome_file: *u8, priv_file: *u8, state_out: *u8) -> i64
461func mg_state_line(buf: *u8, n: i64, which: i64, out: *u8) -> i64
476func mg_commit(state_file: *u8, eph_hex: *u8, new_roster: *u8, commit_out: *u8, new_state_out: *u8) -> i64
579func mg_apply(commit_file: *u8, priv_file: *u8, old_state_file: *u8, new_state_out: *u8) -> i64
655func mg_epochverb(state_file: *u8) -> i64
677func mg_framekey(es: *u8, seq: i64, fk: *u8, nonce: *u8) -> i64
688func mg_seal(state_file: *u8, seq: i64, plaintext: *u8, frame_out: *u8) -> i64
735func mg_open(state_file: *u8, frame_file: *u8) -> i64
780func mg_usage() -> i64
called by 1: main calls 1: mg_w
784func main(argc: i64, argv: *i64) -> i64