code wiki / (root) / nx_room_key.nx

nx_room_key.nx source

↩ module page · 87 lines · 3715 B

1// nx_room_key.nx -- ROOM CAPABILITY TOKENS (task #41 ladder: the last table-stakes gap vs the field -- 2// LiveKit JWT / Janus+Ant tokens; ours = capability LINKS, sovereign). token = hex16 of sha256(base_room 3// || secret): no server DB, no third party, secret is a FILE (data-driven). Protection is a data-driven 4// LIST (rooms_protected.txt) -- empty list = every room open = today's behavior BY CONSTRUCTION. The lane 5// suffix (#1/#2 shard rooms) is stripped for the check so ONE key covers all lanes of a room. This organ 6// is the SSOT both the relay and the invite tool import -- they cannot drift. license_tier: ORIGINAL 7import "nx_syscalls.nx" 8import "nx_sha256.nx" 9const RK_MAGIC_4096: i64 = 4096 10 11const RK_TOK_LEN: i64 = 16 // hex chars of the first 8 digest bytes 12 13// strip the lane suffix: base length = up to the first '#' (byte 35). "vault#1" -> 5. 14func rk_base_len(room: *u8, n: i64) -> i64 { 15 var i: i64 = 0 16 while i < n { if room[i] == (35 as u8) { return i } i = i + 1 } 17 return n } 18 19// find "?k=" in a path; copy the token (up to cap) into k_out; returns token length or -1. 20func rk_parse_k(path: *u8, n: i64, k_out: *u8, cap: i64) -> i64 { 21 var i: i64 = 0 22 while i + 3 <= n { 23 if path[i] == (63 as u8) { if path[i+1] == (107 as u8) { if path[i+2] == (61 as u8) { 24 var j: i64 = i + 3 25 var w: i64 = 0 26 while j < n { 27 if w >= cap { return 0 - 1 } 28 if path[j] == (38 as u8) { j = n } else { k_out[w] = path[j]; w = w + 1; j = j + 1 } 29 } 30 if w == 0 { return 0 - 1 } 31 return w 32 } } } 33 i = i + 1 34 } 35 return 0 - 1 } 36 37// path length up to (excluding) the query '?' (byte 63) -- the ROOM IDENTITY must never include ?k=... 38func rk_path_no_query(path: *u8, n: i64) -> i64 { 39 var i: i64 = 0 40 while i < n { if path[i] == (63 as u8) { return i } i = i + 1 } 41 return n } 42 43// token = hex16(sha256(base_room || secret)[0..8)). out needs RK_TOK_LEN bytes. 44func rk_token(room: *u8, room_len: i64, secret: *u8, secret_len: i64, out: *u8) -> i64 { 45 let c: *Sha256 = sys_mmap(RK_MAGIC_4096) as *Sha256 46 sha256_init(c) 47 sha256_update(c, room, room_len) 48 sha256_update(c, secret, secret_len) 49 let dg: *u8 = sys_mmap(64) 50 sha256_final(c, dg) 51 let hx: *u8 = "0123456789abcdef" as *u8 52 var i: i64 = 0 53 while i < 8 { 54 out[i*2] = hx[(dg[i] >> 4) & 15] 55 out[i*2+1] = hx[dg[i] & 15] 56 i = i + 1 57 } 58 return RK_TOK_LEN } 59 60// is base room (room[0..blen)) in the protected list (newline-separated)? exact line match. 61func rk_protected(list: *u8, ln: i64, room: *u8, blen: i64) -> i64 { 62 if ln <= 0 { return 0 } 63 if blen <= 0 { return 0 } 64 var i: i64 = 0 65 while i < ln { 66 var e: i64 = i // find line end [i, e) 67 var scanning: i64 = 1 68 while scanning == 1 { 69 if e >= ln { scanning = 0 } 70 else { if list[e] == (10 as u8) { scanning = 0 } 71 else { if list[e] == (13 as u8) { scanning = 0 } else { e = e + 1 } } } 72 } 73 if e - i == blen { 74 var q: i64 = 0 75 var eq: i64 = 1 76 while q < blen { if list[i + q] != room[q] { eq = 0; q = blen } else { q = q + 1 } } 77 if eq == 1 { return 1 } 78 } 79 i = e // skip EOL run to the next line 80 var skipping: i64 = 1 81 while skipping == 1 { 82 if i >= ln { skipping = 0 } 83 else { if list[i] == (10 as u8) { i = i + 1 } 84 else { if list[i] == (13 as u8) { i = i + 1 } else { skipping = 0 } } } 85 } 86 } 87 return 0 }