code wiki / _hdl_build / nx_cap_provision.nx
nx_cap_provision.nx source
↩ module page · 55 lines · 2747 B
1// nx_cap_provision.nx -- FAIL-CLOSED capability-secret provisioning check (closes audit risk #1:
2// the tools-api's baked placeholder HMAC secret is forgeable by anyone reading the binary). A cap-
3// signing secret MUST come from the sovereign vault keyfile, NEVER the compiled placeholder. This
4// PURE check lets the tools-api serve organ FAIL-FAST at startup (CLAUDE.md #20): if the key is
5// absent / too short / still the placeholder -> refuse to bind rather than verify caps against a
6// constant. ADDITIVE + zero-collision (new organ, no edit to the contended nx_tools_api gate suite).
7// The serve-startup wiring + provisioning the real 0600 keyfile on the NAS = operator-coordinated last mile.
8// license_tier: ORIGINAL
9import "nx_syscalls.nx"
10
11// minimum acceptable key length in bytes (a 1-byte keyfile must NOT count as provisioned).
12const CAP_MINKEY: i64 = 16
13// the exact baked placeholder from nx_tools_api.nx L17 -- a keyfile containing THIS is unprovisioned.
14const CAP_PLACEHOLDER: *u8 = "nishi-tools-cap-hmac-secret-v1-REPLACE-FROM-VAULT" as *u8
15
16func cp_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
17
18// does the first n bytes of buf exactly equal the nul-terminated pat (same length AND bytes)?
19func cp_eq_bytes(buf: *u8, n: i64, pat: *u8) -> i64 {
20 let pl: i64 = cp_slen(pat)
21 if n != pl { return 0 }
22 var i: i64 = 0
23 while i < n { if buf[i] != pat[i] { return 0 } i = i + 1 }
24 return 1
25}
26
27// THE fail-closed verdict: 1 = a real vault key is present (bind allowed); 0 = FAIL CLOSED (refuse).
28// keyfile absent -> 0 ; shorter than minkey -> 0 ; byte-identical to the placeholder -> 0 ; else 1.
29func cap_provisioned(keyfile: *u8, minkey: i64) -> i64 {
30 let szp: *i64 = sys_mmap(16) as *i64
31 let buf: *u8 = sys_read_file(keyfile, szp)
32 if (buf as i64) == 0 { return 0 }
33 let n: i64 = szp[0]
34 if n < minkey { return 0 }
35 if cp_eq_bytes(buf, n, CAP_PLACEHOLDER) == 1 { return 0 }
36 return 1
37}
38
39// convenience: the production default (the real keyfile path + CAP_MINKEY).
40func cap_provisioned_default() -> i64 {
41 return cap_provisioned("tools_cap_secret.key" as *u8, CAP_MINKEY)
42}
43
44func cp_p(s: *u8) -> i64 { let n: i64 = cp_slen(s); sys_write(1, s, n); return 0 }
45
46func main(argc: i64, argv: *i64) -> i64 {
47 var keyfile: *u8 = "tools_cap_secret.key"
48 if argc >= 2 { keyfile = argv[1] as *u8 }
49 let v: i64 = cap_provisioned(keyfile, CAP_MINKEY)
50 cp_p("CAP-PROVISION keyfile=" as *u8); cp_p(keyfile)
51 if v == 1 { cp_p(" provisioned=YES (real vault key -> serve may bind)\n" as *u8) }
52 else { cp_p(" provisioned=NO (absent/short/placeholder -> FAIL-CLOSED: serve must refuse to bind)\n" as *u8) }
53 sys_exit(1 - v)
54 return 1 - v
55}