code wiki / _hdl_build / _drv_f1_gate.nx
_drv_f1_gate.nx source
↩ module page · 176 lines · 9328 B
1// _drv_f1_gate.nx -- gate for CONSENT-GATED DEVICE TELEMETRY (X-DRV-F1). NO mocks.
2//
3// Drives the REAL nx_telemetry through the full consent lifecycle and asserts the privacy contract:
4// (1) NO-SHARE-BY-DEFAULT -- with a consent file carrying NO opt-in rows, the shared outbox stays
5// EMPTY and the local store is populated. Nothing leaves the device by default.
6// (2) OPT-IN -- a consent row "2 SHARED" makes deviceid=2's record appear in the shared outbox,
7// carrying a signature + the node public key (provenance).
8// (3) SIGNED-VERIFIES -- the shared record's ed25519 signature genuinely verifies over its payload
9// with the published pubkey (ed25519_verify_full == SIG_OK); a TAMPER (flip a payload byte)
10// makes verification FAIL -> the signature really binds the payload, not a decorative tag.
11// (4) REVOKE -- flipping the consent back to "2 LOCAL" makes the next run share NOTHING new.
12//
13// Evidence -> knowledge/status/telemetry.log (DRVF1GATE row). Sovereign. license_tier: ORIGINAL
14import "nx_syscalls.nx"
15import "nx_ed25519_signature.nx"
16
17const F_TELE: *u8 = "_offc/nx_telemetry.elf"
18const F_DEVMAP: *u8 = "knowledge/registry/emu_devmap.tsv"
19const F_SHARED: *u8 = "knowledge/status/telemetry_shared.log"
20const F_LOCAL: *u8 = "knowledge/status/telemetry_local.log"
21
22func g_p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
23func g_fp(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 }
24func g_fn(fd: i64, v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;k=1}; while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(fd,bb,k); return 0 }
25
26func g_run(prog: *u8, a1: *u8, a2: *u8) -> i64 {
27 let pid: i64 = sys_fork()
28 if pid == 0 {
29 let dn: i64 = sys_openat_wr("/tmp/_f1_run.out" as *u8, 0x1a4); if dn >= 0 { sys_dup3(dn, 1, 0); sys_dup3(dn, 2, 0) }
30 let argv: *i64 = sys_mmap(32) as *i64
31 argv[0] = prog as i64
32 var k: i64 = 1
33 if a1 != (0 as *u8) { argv[k] = a1 as i64; k = k + 1 }
34 if a2 != (0 as *u8) { argv[k] = a2 as i64; k = k + 1 }
35 argv[k] = 0
36 let envp: *i64 = sys_mmap(16) as *i64
37 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64; envp[1] = 0
38 sys_execve(prog, argv, envp)
39 sys_exit(127)
40 }
41 let st: *i64 = sys_mmap(16) as *i64
42 sys_wait4(pid, st, 0)
43 let sig: i64 = st[0] & 0x7f
44 if sig != 0 { return 128 + sig }
45 return (st[0] >> 8) & 0xff
46}
47
48func g_read(path: *u8, buf: *u8, cap: i64) -> i64 {
49 let fd: i64 = sys_openat_rd(path)
50 if fd < 0 { return 0 }
51 var n: i64 = 0
52 var go: i64 = 1
53 while go == 1 { let r: i64 = sys_read(fd, (buf as i64 + n) as *u8, cap - 1 - n); if r <= 0 { go = 0 } else { n = n + r } if n >= cap - 1 { go = 0 } }
54 sys_close(fd)
55 return n
56}
57
58func g_write_file(path: *u8, s: *u8) -> i64 {
59 let fd: i64 = sys_openat_wr(path, 0x1a4)
60 if fd < 0 { return 0 - 1 }
61 var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 }
62 sys_write(fd, s, n); sys_close(fd)
63 return 0
64}
65func g_truncate(path: *u8) -> i64 { let fd: i64 = sys_openat_wr(path, 0x1a4); if fd >= 0 { sys_close(fd) } return 0 }
66
67func g_strlen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
68
69// count occurrences of pat in buf[0,n).
70func g_count(buf: *u8, n: i64, pat: *u8) -> i64 {
71 let pl: i64 = g_strlen(pat)
72 if pl <= 0 { return 0 }
73 var c: i64 = 0; var i: i64 = 0
74 while i + pl <= n { var k: i64=0; var hit: i64=1; while k<pl { if buf[i+k]!=pat[k] { hit=0; k=pl } else { k=k+1 } } if hit==1 { c=c+1; i=i+pl } else { i=i+1 } }
75 return c
76}
77
78// find first index of pat in buf[from,n); -1 if absent.
79func g_find(buf: *u8, n: i64, from: i64, pat: *u8) -> i64 {
80 let pl: i64 = g_strlen(pat)
81 if pl <= 0 { return 0 - 1 }
82 var i: i64 = from
83 while i + pl <= n { var k: i64=0; var hit: i64=1; while k<pl { if buf[i+k]!=pat[k] { hit=0; k=pl } else { k=k+1 } } if hit==1 { return i } i=i+1 }
84 return 0 - 1
85}
86
87// hex-decode 2*m chars at src into out (m bytes); return m (or -1 on bad char).
88func g_hexdec(src: *u8, hexlen: i64, out: *u8) -> i64 {
89 if (hexlen % 2) != 0 { return 0 - 1 }
90 var i: i64 = 0
91 while i < hexlen / 2 {
92 let c0: i64 = src[i*2] as i64
93 let c1: i64 = src[i*2+1] as i64
94 var h0: i64 = 0 - 1; var h1: i64 = 0 - 1
95 if c0>=48 { if c0<=57 { h0=c0-48 } } if c0>=97 { if c0<=102 { h0=c0-87 } } if c0>=65 { if c0<=70 { h0=c0-55 } }
96 if c1>=48 { if c1<=57 { h1=c1-48 } } if c1>=97 { if c1<=102 { h1=c1-87 } } if c1>=65 { if c1<=70 { h1=c1-55 } }
97 if h0<0 { return 0-1 } if h1<0 { return 0-1 }
98 out[i] = ((h0<<4)|h1) as u8
99 i = i + 1
100 }
101 return hexlen / 2
102}
103
104func main() -> i64 {
105 g_p("=== consent-gated telemetry gate (X-DRV-F1: no-share-by-default + opt-in + signed + revoke) ===\n" as *u8)
106 let lfd: i64 = sys_openat_append("knowledge/status/telemetry.log" as *u8, 0x1a4)
107
108 // (1) NO-SHARE-BY-DEFAULT
109 g_write_file("/tmp/_f1_default.tsv" as *u8, "# no opt-in rows -> all LOCAL\n" as *u8)
110 g_truncate(F_SHARED); g_truncate(F_LOCAL)
111 g_run(F_TELE, "/tmp/_f1_default.tsv" as *u8, F_DEVMAP)
112 let sb1: *u8 = sys_mmap(65536); let sn1: i64 = g_read(F_SHARED, sb1, 65536)
113 let lb1: *u8 = sys_mmap(65536); let ln1: i64 = g_read(F_LOCAL, lb1, 65536)
114 var no_share_default: i64 = 0
115 if g_count(sb1, sn1, "SHARED " as *u8) == 0 { if g_count(lb1, ln1, "LOCAL " as *u8) >= 1 { no_share_default = 1 } }
116
117 // (2) OPT-IN: deviceid 2 -> SHARED
118 g_write_file("/tmp/_f1_optin.tsv" as *u8, "2\tSHARED\n" as *u8)
119 g_truncate(F_SHARED)
120 g_run(F_TELE, "/tmp/_f1_optin.tsv" as *u8, F_DEVMAP)
121 let sb2: *u8 = sys_mmap(65536); let sn2: i64 = g_read(F_SHARED, sb2, 65536)
122 let shared_n: i64 = g_count(sb2, sn2, "SHARED " as *u8)
123 var opt_in: i64 = 0
124 if shared_n == 1 { if g_find(sb2, sn2, 0, "sig=" as *u8) >= 0 { if g_find(sb2, sn2, 0, "pub=" as *u8) >= 0 { opt_in = 1 } } }
125
126 // (3) SIGNED-VERIFIES + TAMPER: extract payload / sig / pub from the shared line, ed25519-verify.
127 var verifies: i64 = 0
128 var tamper_fails: i64 = 0
129 let ps: i64 = g_find(sb2, sn2, 0, "SHARED " as *u8)
130 let sigpos: i64 = g_find(sb2, sn2, 0, " sig=" as *u8)
131 let pubpos: i64 = g_find(sb2, sn2, 0, " pub=" as *u8)
132 if ps >= 0 { if sigpos >= 0 { if pubpos >= 0 {
133 let pstart: i64 = ps + 7 // after "SHARED "
134 let plen: i64 = sigpos - pstart // payload bytes (exactly what was signed)
135 let msg: *u8 = sys_mmap(2048)
136 var i: i64 = 0
137 while i < plen { msg[i] = sb2[pstart + i]; i = i + 1 }
138 let sighex: i64 = sigpos + 5 // after " sig="
139 let sig: *u8 = sys_mmap(64)
140 g_hexdec((sb2 as i64 + sighex) as *u8, 128, sig)
141 let pubhex: i64 = pubpos + 5 // after " pub="
142 let pub: *u8 = sys_mmap(32)
143 g_hexdec((sb2 as i64 + pubhex) as *u8, 64, pub)
144 if ed25519_verify_full(pub, msg, plen, sig) == NX_ED25519_SIG_OK { verifies = 1 }
145 // tamper: flip a payload byte -> verification must FAIL (signature binds the payload)
146 msg[0] = (msg[0] + 1) as u8
147 if ed25519_verify_full(pub, msg, plen, sig) != NX_ED25519_SIG_OK { tamper_fails = 1 }
148 } } }
149
150 // (4) REVOKE: deviceid 2 -> LOCAL again -> shares nothing new
151 g_write_file("/tmp/_f1_revoke.tsv" as *u8, "2\tLOCAL\n" as *u8)
152 g_truncate(F_SHARED)
153 g_run(F_TELE, "/tmp/_f1_revoke.tsv" as *u8, F_DEVMAP)
154 let sb3: *u8 = sys_mmap(65536); let sn3: i64 = g_read(F_SHARED, sb3, 65536)
155 var revoke_ok: i64 = 0
156 if g_count(sb3, sn3, "SHARED " as *u8) == 0 { revoke_ok = 1 }
157
158 g_p(" no_share_default=" as *u8); g_fn(1, no_share_default)
159 g_p(" opt_in=" as *u8); g_fn(1, opt_in)
160 g_p(" signed_verifies=" as *u8); g_fn(1, verifies)
161 g_p(" tamper_fails=" as *u8); g_fn(1, tamper_fails)
162 g_p(" revoke=" as *u8); g_fn(1, revoke_ok); g_p("\n" as *u8)
163
164 var pass: i64 = 0
165 if no_share_default == 1 { if opt_in == 1 { if verifies == 1 { if tamper_fails == 1 { if revoke_ok == 1 { pass = 1 } } } } }
166
167 if pass == 1 {
168 g_p("DRVF1GATE verdict=GREEN (consent-gated telemetry: DEFAULT LOCAL-ONLY [shared outbox empty], opt-in SHARES a record carrying a GENUINE ed25519 signature [verifies with the node pubkey; a payload tamper makes it FAIL], and REVOKE stops sharing -- privacy-first, no-share-by-default, sovereign)\n" as *u8)
169 if lfd >= 0 { g_fp(lfd, "DRVF1GATE verdict=GREEN keystone=consent-gated-telemetry no-share-by-default=yes opt-in=yes ed25519-signed=verified tamper=rejected revoke=yes epoch=" as *u8); g_fn(lfd, sys_now_realtime_sec()); g_fp(lfd, "\n" as *u8); sys_close(lfd) }
170 sys_exit(0); return 0
171 }
172 g_p("DRVF1GATE verdict=RED (not all green)\n" as *u8)
173 if lfd >= 0 { g_fp(lfd, "DRVF1GATE verdict=RED nsd=" as *u8); g_fn(lfd, no_share_default); g_fp(lfd, " optin=" as *u8); g_fn(lfd, opt_in); g_fp(lfd, " verify=" as *u8); g_fn(lfd, verifies); g_fp(lfd, " tamper=" as *u8); g_fn(lfd, tamper_fails); g_fp(lfd, " revoke=" as *u8); g_fn(lfd, revoke_ok); g_fp(lfd, "\n" as *u8); sys_close(lfd) }
174 sys_exit(1)
175 return 1
176}