code wiki / _hdl_build / nx_office_closed_gate.nx

nx_office_closed_gate.nx source

↩ module page · 203 lines · 12262 B

1// nx_office_closed_gate.nx -- END-TO-END GATE for the CLOSED, realm-bound office instance (LP3 of /compare/legalpractice): 2// ONE nx_office_daemon binary, a tenant instance that accepts ONLY sessions of ITS realm, owns its documents through ITS 3// own ReBAC plane, resolves handles from a docportal-shaped uidmap, and never exposes the open-mode namespace. 4// The gate builds a FRESH realm under /tmp/<gate>/<now_us> (its own OPAQUE keys + account store, two registered users, a 5// uidmap it writes itself), spawns the DEPLOYED ./nx_office_daemon.elf on a probe port it first proves FREE, and drives 6// it over a real loopback socket. Inherits nx_gate_verdict: gv_check per tooth, gv_bite on the closed-mode gate, the 7// verdict in the EXIT CODE; the daemon is killed and reaped before the verdict. license_tier: ORIGINAL 8import "nx_office_serve.nx" 9import "nx_opaque_login.nx" 10import "nx_gate_verdict.nx" 11 12const OC_SCRATCH: *u8 = "/tmp/nx_office_closed_gate" 13const OC_DAEMON: *u8 = "./nx_office_daemon.elf" 14const OC_DIRMODE: i64 = 493 15const OC_SMALL: i64 = 256 16const OC_TOK: i64 = 600 17const OC_OUT: i64 = 1048576 18const OC_REQ: i64 = 65536 19const OC_CTX: i64 = 256 20const OC_MN: i64 = 512 21const OC_I64: i64 = 16 22const OC_REALM: *u8 = "officegate_realm" 23const OC_LABEL: *u8 = "Office gate realm" 24const OC_USER1: *u8 = "firmtest" 25const OC_USER2: *u8 = "otherlawyer" 26const OC_PW1: *u8 = "correct horse battery staple 1" 27const OC_PW2: *u8 = "another long passphrase 2" 28// light argon2id KSF for the gate (the daemon validates only; register/login run here, in-process) 29const OC_KSF_M: i64 = 256 30const OC_KSF_T: i64 = 2 31const OC_KSF_P: i64 = 1 32// probe port: a window of loopback ports, the pick DERIVED from the clock and proven FREE before the spawn 33const OC_PORT_BASE: i64 = 18600 34const OC_PORT_SPAN: i64 = 100 35const OC_CONNECT_MS: i64 = 1500 36const OC_SPAWN_TRIES: i64 = 40 37const OC_SPAWN_WAIT_MS: i64 = 250 38const OC_LOOPBACK_A: i64 = 127 39const OC_SIGKILL: i64 = 9 40const OC_DOC: *u8 = "closed-doc" 41 42func oc_path(out: *u8, root: *u8, tail: *u8) -> i64 { 43 var o: i64 = of_cat(out, 0, root) 44 o = of_cat(out, o, tail) 45 out[o] = 0 as u8 46 return o 47} 48func oc_status_is(out: *u8, n: i64, code: *u8) -> i64 { 49 if n < 12 { return 0 } 50 return of_memhas(out, 13, code) 51} 52// one HTTP exchange over loopback: connect (bounded), send, drain. Returns bytes read, -1 if it could not connect. 53func oc_send(port: i64, req: *u8, n: i64, out: *u8, cap: i64) -> i64 { 54 let addr: *u8 = sys_mmap(16) 55 nx_http_client_sockaddr_ipv4(addr, OC_LOOPBACK_A, 0, 0, 1, port) 56 let fd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0) 57 if fd < 0 { sys_munmap(addr, 16); return 0 - 1 } 58 if nx_connect_bounded(fd, addr, 16, OC_CONNECT_MS) < 0 { sys_close(fd); sys_munmap(addr, 16); return 0 - 1 } 59 let wr: i64 = sys_write(fd, req, n) 60 var got: i64 = 0 - 1 61 if wr == n { got = _drain(fd, out, cap) } 62 sys_close(fd) 63 sys_munmap(addr, 16) 64 return got 65} 66func oc_get(port: i64, path: *u8, tok: *u8, req: *u8, out: *u8) -> i64 { 67 var ro: i64 = of_cat(req, 0, "GET " as *u8) 68 ro = of_cat(req, ro, path) 69 ro = of_cat(req, ro, " HTTP/1.1\r\nHost: office.local\r\n" as *u8) 70 if tok[0] != (0 as u8) { ro = of_cat(req, ro, "X-Nishi-Session: " as *u8); ro = of_cat(req, ro, tok); ro = of_cat(req, ro, "\r\n" as *u8) } 71 ro = of_cat(req, ro, "Connection: close\r\n\r\n" as *u8) 72 return oc_send(port, req, ro, out, OC_OUT) 73} 74func oc_post(port: i64, path: *u8, tok: *u8, body: *u8, req: *u8, out: *u8) -> i64 { 75 var ro: i64 = of_cat(req, 0, "POST " as *u8) 76 ro = of_cat(req, ro, path) 77 ro = of_cat(req, ro, " HTTP/1.1\r\nHost: office.local\r\n" as *u8) 78 if tok[0] != (0 as u8) { ro = of_cat(req, ro, "X-Nishi-Session: " as *u8); ro = of_cat(req, ro, tok); ro = of_cat(req, ro, "\r\n" as *u8) } 79 ro = of_cat(req, ro, "Content-Length: " as *u8) 80 ro = of_catn(req, ro, of_slen(body)) 81 ro = of_cat(req, ro, "\r\nConnection: close\r\n\r\n" as *u8) 82 ro = of_cat(req, ro, body) 83 return oc_send(port, req, ro, out, OC_OUT) 84} 85// register + login one user in the fixture realm; writes the base64 token (NUL-terminated) to tok; appends the 86// docportal-shaped uidmap row for it. Returns 1 on success. 87func oc_user(ctx: *NxAuthContext, handle: *u8, pw: *u8, uidmap: *u8, tok: *u8) -> i64 { 88 let mn: *u8 = sys_mmap(OC_MN) 89 let mnn: *i64 = sys_mmap(OC_I64) as *i64 90 if olg_register(ctx, handle, of_slen(handle), pw, of_slen(pw), mn, OC_MN, mnn) != NX_MAUTH_OK { return 0 } 91 let tn: *i64 = sys_mmap(OC_I64) as *i64 92 if olg_login(ctx, handle, of_slen(handle), pw, of_slen(pw), tok, OC_TOK, tn) != NX_MAUTH_OK { return 0 } 93 tok[tn[0]] = 0 as u8 94 let uid: *u8 = sys_mmap(OC_SMALL) 95 let uidn: *i64 = sys_mmap(OC_I64) as *i64 96 if olg_whoami(ctx, tok, tn[0], sys_now_realtime_sec(), uid, OC_SMALL, uidn) != NX_MAUTH_OK { return 0 } 97 let hx: *u8 = sys_mmap(OC_SMALL) 98 rb_hex(hx, uid, uidn[0]) 99 let row: *u8 = sys_mmap(OC_SMALL + OC_SMALL) 100 var o: i64 = of_cat(row, 0, "MAP " as *u8) 101 o = of_cat(row, o, hx) 102 o = of_cat(row, o, " " as *u8) 103 o = of_cat(row, o, handle) 104 o = of_catc(row, o, 10) 105 if of_append_line(uidmap, row, o) != 0 { return 0 } 106 return 1 107} 108 109func main() -> i64 { 110 gv_head("NX-OFFICE-CLOSED-GATE -- LP3: the realm-bound CLOSED office instance over a real loopback socket (fresh OPAQUE realm, two users, own ReBAC plane, docportal-shaped uidmap; the deployed daemon binary is spawned on a port proven free)" as *u8) 111 let ctr: *i64 = gv_ctr() 112 sys_mkdir(OC_SCRATCH, OC_DIRMODE) 113 let fx: *u8 = sys_mmap(OC_SMALL) 114 var fo: i64 = of_cat(fx, 0, OC_SCRATCH) 115 fo = of_cat(fx, fo, "/" as *u8) 116 fo = of_catn(fx, fo, sys_now_us()) 117 fx[fo] = 0 as u8 118 sys_mkdir(fx, OC_DIRMODE) 119 let keys: *u8 = sys_mmap(OC_SMALL); oc_path(keys, fx, "/keys.bin" as *u8) 120 let store: *u8 = sys_mmap(OC_SMALL); oc_path(store, fx, "/store.log" as *u8) 121 let uidmap: *u8 = sys_mmap(OC_SMALL); oc_path(uidmap, fx, "/store.log.uidmap" as *u8) 122 let root: *u8 = sys_mmap(OC_SMALL); oc_path(root, fx, "/root" as *u8) 123 let az: *u8 = sys_mmap(OC_SMALL); oc_path(az, fx, "/az_" as *u8) 124 gv_puts(" fixture: " as *u8); gv_puts(fx); gv_puts("\n" as *u8) 125 // ---- the fixture realm: keys + store + two users ---- 126 let ctx: *NxAuthContext = sys_mmap(OC_CTX) as *NxAuthContext 127 let ctxok: i64 = olg_ctx_setup(ctx, keys, store, OC_REALM, of_slen(OC_REALM), OC_LABEL, of_slen(OC_LABEL), OC_KSF_M, OC_KSF_T, OC_KSF_P) 128 gv_check("fixture-realm-keys-and-store-initialised" as *u8, (ctxok == 0) as i64, ctr) 129 let tok1: *u8 = sys_mmap(OC_TOK) 130 let tok2: *u8 = sys_mmap(OC_TOK) 131 tok1[0] = 0 as u8; tok2[0] = 0 as u8 132 gv_check("user-1-registered-logged-in-and-mapped-in-the-uidmap" as *u8, oc_user(ctx, OC_USER1, OC_PW1, uidmap, tok1), ctr) 133 gv_check("user-2-registered-logged-in-and-mapped-in-the-uidmap" as *u8, oc_user(ctx, OC_USER2, OC_PW2, uidmap, tok2), ctr) 134 // ---- probe port: derived, then PROVEN free (a refused connect) before anything listens on it ---- 135 let port: i64 = OC_PORT_BASE + (sys_now_us() % OC_PORT_SPAN) 136 let req: *u8 = sys_mmap(OC_REQ) 137 let out: *u8 = sys_mmap(OC_OUT) 138 let notok: *u8 = sys_mmap(4); notok[0] = 0 as u8 139 let pre: i64 = oc_get(port, "/office" as *u8, notok, req, out) 140 gv_puts(" probe port=" as *u8); gv_num(port); gv_puts(" pre-spawn connect=" as *u8); gv_num(pre); gv_puts("\n" as *u8) 141 if gv_need("probe-port-free-before-spawn (connect must be refused)" as *u8, (pre < 0) as i64, ctr) == 1 { 142 // ---- spawn the DEPLOYED daemon as a closed, realm-bound tenant instance ---- 143 let pstr: *u8 = sys_mmap(OC_SMALL) 144 var po: i64 = of_catn(pstr, 0, port); pstr[po] = 0 as u8 145 let av: *i64 = sys_mmap(8 * 12) as *i64 146 av[0] = OC_DAEMON as i64; av[1] = pstr as i64; av[2] = keys as i64; av[3] = store as i64; av[4] = OC_REALM as i64 147 av[5] = root as i64; av[6] = ("/office" as *u8) as i64; av[7] = uidmap as i64; av[8] = ("closed" as *u8) as i64; av[9] = az as i64; av[10] = 0 148 let kid: i64 = sys_fork() 149 if kid == 0 { 150 sys_execve_clean(OC_DAEMON, av, 0 as *i64) 151 sys_exit(127) 152 } 153 var up: i64 = 0 154 var tries: i64 = 0 155 var n: i64 = 0 - 1 156 while tries < OC_SPAWN_TRIES { if up == 0 { 157 sys_sleep_ms(OC_SPAWN_WAIT_MS) 158 n = oc_get(port, "/office" as *u8, notok, req, out) 159 if n > 0 { up = 1 } 160 } tries = tries + 1 } 161 gv_puts(" daemon pid=" as *u8); gv_num(kid); gv_puts(" up=" as *u8); gv_num(up); gv_puts(" after tries=" as *u8); gv_num(tries); gv_puts("\n" as *u8) 162 gv_check("deployed-daemon-listens-on-the-probe-port" as *u8, up, ctr) 163 // ---- closed mode ---- 164 let no_sess_401: i64 = oc_status_is(out, n, "401" as *u8) 165 gv_check("neg-control-no-session-refused-401-naming-the-login-surface" as *u8, (no_sess_401) & ((of_memhas(out, n, "closed" as *u8)) as i64), ctr) 166 let bad: *u8 = sys_mmap(OC_TOK) 167 var bi: i64 = 0 168 while tok1[bi] != (0 as u8) { bad[bi] = tok1[bi]; bi = bi + 1 } 169 bad[bi] = 0 as u8 170 if bi > 8 { if bad[8] == (65 as u8) { bad[8] = 66 as u8 } else { bad[8] = 65 as u8 } } 171 n = oc_get(port, "/office" as *u8, bad, req, out) 172 gv_check("neg-control-tampered-token-refused-401" as *u8, oc_status_is(out, n, "401" as *u8), ctr) 173 n = oc_get(port, "/office" as *u8, tok1, req, out) 174 let sess_200: i64 = oc_status_is(out, n, "200" as *u8) 175 let sess_401: i64 = oc_status_is(out, n, "401" as *u8) 176 gv_check("realm-session-opens-the-office-200" as *u8, (sess_200) & ((of_memhas(out, n, "Nishi Office" as *u8)) as i64), ctr) 177 gv_bite("closed-mode-gate" as *u8, no_sess_401, sess_401, ctr) 178 // ---- ownership claimed by the realm handle, through THIS instance's own ReBAC plane ---- 179 n = oc_post(port, "/office/save" as *u8, tok1, "name=closed-doc&kind=doc&spec=H+Closed+Doc%0AP+Owned+by+the+firm+user." as *u8, req, out) 180 gv_check("authenticated-save-answers-303" as *u8, oc_status_is(out, n, "303" as *u8), ctr) 181 let dref: *u8 = sys_mmap(OC_SMALL); of_docref(dref, OC_DOC) 182 let owned: i64 = rb_obj_has_owner(az, dref) 183 let by1: i64 = rb_tuple_current(az, dref, "owner" as *u8, "user:firmtest" as *u8) 184 gv_puts(" owner tuple present=" as *u8); gv_num(owned); gv_puts(" by user:firmtest=" as *u8); gv_num(by1); gv_puts("\n" as *u8) 185 gv_check("save-claims-ownership-for-the-handle-resolved-from-the-uidmap" as *u8, ((owned == 1) as i64) & ((by1 == 1) as i64), ctr) 186 gv_check("neg-control-ownership-lands-on-the-tenant-plane-not-the-default-office-plane" as *u8, (rb_obj_has_owner(OF_AUTHZ, dref) == 0) as i64, ctr) 187 n = oc_get(port, "/office/doc/closed-doc" as *u8, notok, req, out) 188 gv_check("neg-control-unauthenticated-read-of-the-owned-document-refused-401" as *u8, oc_status_is(out, n, "401" as *u8), ctr) 189 n = oc_get(port, "/office/doc/closed-doc" as *u8, tok2, req, out) 190 gv_check("neg-control-another-realm-user-without-a-share-is-denied-the-owned-document" as *u8, oc_status_is(out, n, "404" as *u8), ctr) 191 n = oc_get(port, "/office/doc/closed-doc" as *u8, tok1, req, out) 192 gv_check("owner-reads-the-document-200" as *u8, (oc_status_is(out, n, "200" as *u8)) & ((of_memhas(out, n, "Closed Doc" as *u8)) as i64), ctr) 193 // ---- the open-mode namespace is not this instance: a document of the shared office root is absent here ---- 194 n = oc_get(port, "/office/doc/agenda" as *u8, tok1, req, out) 195 gv_check("open-mode-namespace-not-exposed-to-the-tenant-404" as *u8, oc_status_is(out, n, "404" as *u8), ctr) 196 // ---- teardown: kill + reap the daemon (the restore is part of the experiment) ---- 197 nx_kill(kid, OC_SIGKILL) 198 let wst: *i64 = sys_mmap(OC_I64) as *i64 199 let reaped: i64 = sys_wait4(kid, wst, 0) 200 gv_check("daemon-killed-and-reaped" as *u8, (reaped == kid) as i64, ctr) 201 } 202 return gv_verdict("OFFICE-CLOSED-GATE" as *u8, ctr, "LP3: one binary, a closed realm-bound tenant instance driven over loopback with its own realm, plane and uidmap" as *u8) 203}