code wiki / _hdl_build / nx_sites_sni_cert_gate.nx
nx_sites_sni_cert_gate.nx source
↩ module page · 360 lines · 20484 B
1import "nx_gate_base.nx"
2import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
3// nx_sites_sni_cert_gate.nx -- SOVEREIGN gate for Route B (SNI-aware per-domain cert
4// selection in the sites TLS daemon). TWO layers:
5//
6// LAYER 1 -- UNIT (no socket): build real ClientHellos carrying a known SNI and assert the
7// SHARED selector scs_pick_from_clienthello / scs_pick_index returns the right cert index,
8// including the fail-safe defaults: EMPTY SNI, unknown SNI, look-alike "evilnishifamily.com"
9// (a label-boundary trap that MUST fall to the default, not be mis-selected), a non-handshake
10// record, a truncated name, and an empty buffer -- all -> default (nishifamily). This is the
11// exact decision codepath the daemon runs.
12//
13// LAYER 2 -- END-TO-END (loopback TLS 1.3 handshake): fork a server that mirrors the daemon
14// (MSG_PEEK the ClientHello -> scs_pick_from_clienthello -> serve the selected wildcard chain
15// via the PROVEN nx_tls13_server_session_run). A real sovereign TLS client (session_new +
16// emit_ch with the chosen SNI + recv_sh) drives it, then this gate DECRYPTS the server's
17// handshake flight and reads the served LEAF certificate off the wire, identifying it by
18// byte-for-byte DER identity against the two loaded chains. Asserts:
19// SNI nishifamily.com -> leaf == the *.nishifamily.com chain (NISHI)
20// SNI andelinwest.com -> leaf == the *.andelinwest.com chain (ANDELIN)
21// SNI app.nishifamily.com -> NISHI (wildcard subdomain)
22// SNI example.org -> NISHI (unknown SNI -> default, over a real handshake)
23// FAILS if the wrong cert is served or the handshake breaks.
24//
25// Cert FIXTURES are read from /tmp (staged by the run wrapper from the two validated wildcard
26// chains): /tmp/sni_nishi_chain.der + /tmp/sni_nishi_key.bin and /tmp/sni_andelin_chain.der +
27// /tmp/sni_andelin_key.bin. Each chain is concat-DER leaf-first; each key is a 32-byte P-256
28// scalar -- the same on-disk format the daemon loads.
29//
30// HONESTY: Layer 2 identifies the served cert by exact leaf-DER identity (equivalent to, and
31// stronger than, "SAN includes *.nishifamily.com" -- it IS the cert whose SAN is that). The
32// SAN text itself is cross-checked out-of-band with openssl in the build notes, not parsed here.
33// The literal no-SNI-EXTENSION wire case is covered at Layer 1 (the emitter mandates an SNI, so
34// it cannot be produced via emit_ch); a no-SNI CH reaches the identical hn<=0 default branch.
35//
36// license_tier: ORIGINAL
37import "nx_syscalls.nx"
38import "nx_csprng.nx"
39import "nx_http_server.nx"
40import "nx_sni_cert_select.nx"
41import "nx_tls13_record.nx"
42import "nx_tls13_read_record_from_fd.nx"
43import "nx_tls13_server_session.nx"
44import "nx_tls13_server_session_run.nx"
45import "nx_tls13_client_session.nx"
46import "nx_tls13_client_session_recv_sh.nx"
47
48const GATE_PORT: i64 = 18443
49const GATE_CERT_MIN: i64 = 100
50const NX_SD_MSG_PEEK_GATE: i64 = 2 // MSG_PEEK
51
52func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw("
53" as *u8); return ok }
54func g_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
55func g_puti(v: i64) -> i64 {
56 let t: *u8 = sys_mmap(24); var m: i64=v
57 if m<0 { sys_write(1,"-" as *u8,1); m=0-m }
58 var k: i64=0
59 if m==0 { t[0]=48 as u8; k=1 } else { while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } }
60 let o: *u8 = sys_mmap(24); var i: i64=0; while i<k { o[i]=t[k-1-i]; i=i+1 } sys_write(1,o,k); return 0
61}
62func g_putidx(idx: i64) -> i64 {
63 if idx == NX_SCS_CERT_NISHI { g_puts("NISHI" as *u8); return 0 }
64 if idx == NX_SCS_CERT_ANDELIN { g_puts("ANDELIN" as *u8); return 0 }
65 g_puts("idx="); g_puti(idx); return 0
66}
67
68func g_mem_eq(a: *u8, b: *u8, n: i64) -> i64 {
69 var i: i64=0; while i<n { if a[i]!=b[i] { return 0 } i=i+1 } return 1
70}
71func g_write_n(fd: i64, buf: *u8, n: i64) -> i64 {
72 var off: i64=0
73 while off<n { let w: i64=sys_write(fd,(buf as i64+off) as *u8,n-off); if w<=0 { return 0-1 } off=off+w }
74 return 0
75}
76
77// ===== Layer 1 fixtures: build a ClientHello carrying SNI=host[0..hlen) (UNIT-test only;
78// NOT a full crypto handshake) -- same shape as nx_aw_sni_router_gate's mk_ch. hlen==0 emits
79// an EMPTY server_name (the empty-SNI case). =====
80func mk_ch(buf: *u8, host: *u8, hlen: i64) -> i64 {
81 buf[0]=22 as u8; buf[1]=3 as u8; buf[2]=1 as u8
82 buf[5]=1 as u8
83 buf[9]=3 as u8; buf[10]=3 as u8
84 var i: i64=0; while i<32 { buf[11+i]=170 as u8; i=i+1 }
85 buf[43]=0 as u8
86 var q: i64=44
87 buf[q]=0 as u8; buf[q+1]=2 as u8; buf[q+2]=19 as u8; buf[q+3]=1 as u8; q=q+4
88 buf[q]=1 as u8; buf[q+1]=0 as u8; q=q+2
89 let entrylen: i64 = 3 + hlen
90 let listlen: i64 = entrylen
91 let extdata: i64 = 2 + listlen
92 let exttot: i64 = 4 + extdata
93 buf[q]=((exttot>>8)&0xff) as u8; buf[q+1]=(exttot&0xff) as u8; q=q+2
94 buf[q]=0 as u8; buf[q+1]=0 as u8
95 buf[q+2]=((extdata>>8)&0xff) as u8; buf[q+3]=(extdata&0xff) as u8; q=q+4
96 buf[q]=((listlen>>8)&0xff) as u8; buf[q+1]=(listlen&0xff) as u8; q=q+2
97 buf[q]=0 as u8; q=q+1
98 buf[q]=((hlen>>8)&0xff) as u8; buf[q+1]=(hlen&0xff) as u8; q=q+2
99 var k: i64=0; while k<hlen { buf[q+k]=host[k]; k=k+1 } q=q+hlen
100 let reclen: i64 = q - 5
101 buf[3]=((reclen>>8)&0xff) as u8; buf[4]=(reclen&0xff) as u8
102 let hslen: i64 = q - 9
103 buf[6]=((hslen>>16)&0xff) as u8; buf[7]=((hslen>>8)&0xff) as u8; buf[8]=(hslen&0xff) as u8
104 return q
105}
106
107// UNIT check: build a CH with this SNI, assert the shared selector returns expect_idx. 1=FAIL.
108func unit_check(host: *u8, hlen: i64, expect_idx: i64) -> i64 {
109 let buf: *u8 = sys_mmap(1024)
110 let n: i64 = mk_ch(buf, host, hlen)
111 let host_scr: *u8 = sys_mmap(256)
112 let got: i64 = scs_pick_from_clienthello(buf, n, host_scr, 256)
113 g_puts(" SNI='"); if hlen>0 { sys_write(1, host, hlen) } g_puts("' -> "); g_putidx(got); g_puts(" expect="); g_putidx(expect_idx)
114 if got == expect_idx { g_puts(" OK\n"); return 0 }
115 g_puts(" FAIL\n"); return 1
116}
117
118// ===== Layer 2: identify a served leaf DER by byte-for-byte identity vs the two chains.
119// Returns 0 (NISHI), 1 (ANDELIN), or negative if it matches neither. =====
120func g_identify(leaf: *u8, leaf_len: i64,
121 nchain: *u8, nlen: i64, achain: *u8, alen: i64) -> i64 {
122 if leaf_len <= 0 { return 0-6 }
123 if leaf_len <= nlen { if g_mem_eq(leaf, nchain, leaf_len) == 1 { return NX_SCS_CERT_NISHI } }
124 if leaf_len <= alen { if g_mem_eq(leaf, achain, leaf_len) == 1 { return NX_SCS_CERT_ANDELIN } }
125 return 0-7
126}
127
128// connect to 127.0.0.1:port -> fd or -1
129func g_connect(port: i64) -> i64 {
130 let bfd: i64 = sys_socket(2, 1, 0)
131 if bfd < 0 { return 0-1 }
132 let addr: *u8 = sys_mmap(16)
133 addr[0]=2 as u8; addr[1]=0 as u8
134 addr[2]=((port>>8)&0xff) as u8; addr[3]=(port&0xff) as u8
135 addr[4]=127 as u8; addr[5]=0 as u8; addr[6]=0 as u8; addr[7]=1 as u8
136 var z: i64=8; while z<16 { addr[z]=0 as u8; z=z+1 }
137 if nx_connect_bounded(bfd, addr, 16, NX_CONN_DEFAULT_MS) < 0 { sys_close(bfd); return 0-1 }
138 return bfd
139}
140
141// E2E client probe: drive a real TLS handshake with the given SNI, decrypt the server's
142// handshake flight, extract the served LEAF cert and identify it (0 NISHI / 1 ANDELIN /
143// negative error). Reads only up to the Certificate, then closes (the server's recv_cf then
144// returns on EOF -- the Certificate was already emitted, so selection is fully observed).
145func g_probe(port: i64, sni: *u8, sni_len: i64,
146 nchain: *u8, nlen: i64, achain: *u8, alen: i64) -> i64 {
147 let fd: i64 = g_connect(port)
148 if fd < 0 { return 0-1 }
149 sys_set_socket_timeout(fd, 5)
150
151 let cr: *u8 = sys_mmap(32); let xp: *u8 = sys_mmap(32)
152 nx_csprng_fill(cr, 32); nx_csprng_fill(xp, 32)
153 let s: *Tls13ClientSession = nx_tls13_client_session_new(cr, xp)
154 if (s as i64) == 0 { sys_close(fd); return 0-2 }
155
156 let ch: *u8 = sys_mmap(1024)
157 let chn: i64 = nx_tls13_client_session_emit_ch(s, sni, sni_len, ch, 1024)
158 if chn < 0 { sys_close(fd); return 0-3 }
159 // wrap CH handshake bytes in a TLSPlaintext record (type 22, legacy version 0x0301)
160 let rec: *u8 = sys_mmap(1024 + 5)
161 rec[0]=22 as u8; rec[1]=3 as u8; rec[2]=1 as u8
162 rec[3]=((chn>>8)&0xff) as u8; rec[4]=(chn&0xff) as u8
163 var ci: i64=0; while ci<chn { rec[5+ci]=ch[ci]; ci=ci+1 }
164 if g_write_n(fd, rec, 5+chn) < 0 { sys_close(fd); return 0-4 }
165
166 // read ServerHello (plaintext record) + recv_sh -> derives server handshake keys, seq=0
167 let shrec: *u8 = sys_mmap(16645)
168 let sht: i64 = nx_tls13_read_record_from_fd(fd, shrec, 16645)
169 if sht < 0 { sys_close(fd); return 0-5 }
170 let rs: i64 = nx_tls13_client_session_recv_sh(s, (shrec as i64 + 5) as *u8, sht - 5)
171 if rs != NX_TLS13_RECV_SH_OK { sys_close(fd); return 0-6 }
172
173 // Read + decrypt the server's ENTIRE handshake flight (EE, Certificate, CV, SF = 4
174 // encrypted records), capturing the leaf from the Certificate. We DRAIN all four before
175 // closing so the server completes every write and ends on its CF-read (clean EOF) -- never
176 // a write to a half-closed socket (which would SIGPIPE the server). Each AEAD record uses
177 // the next server_seq (EE=0, Cert=1, CV=2, SF=3), mirroring the server's record layer.
178 var result: i64 = 0-8
179 var enc: i64 = 0
180 var stop: i64 = 0
181 while stop == 0 {
182 if enc >= 4 { stop = 1 } else {
183 let hrec: *u8 = sys_mmap(16645)
184 let ht: i64 = nx_tls13_read_record_from_fd(fd, hrec, 16645)
185 if ht < 0 { stop = 1 } else {
186 if (hrec[0] & 0xff) == 0x14 {
187 stop = stop // bare ChangeCipherSpec: not AEAD/seq'd -- skip, don't count
188 } else {
189 enc = enc + 1
190 let ct_len: i64 = ht - 5 - 16
191 if ct_len < 1 { stop = 1 } else {
192 let header: *u8 = hrec
193 let ct: *u8 = (hrec as i64 + 5) as *u8
194 let tag: *u8 = (hrec as i64 + ht - 16) as *u8
195 let inner: *u8 = sys_mmap(ct_len + 16)
196 let rct: *i64 = (sys_mmap(16)) as *i64
197 let rln: *i64 = (sys_mmap(16)) as *i64
198 let dv: i64 = nx_tls13_record_decrypt_v2(
199 s.cipher_suite, s.server_hs_traffic_key, s.server_hs_iv, s.server_seq,
200 header, ct, ct_len, tag, inner, rct, rln)
201 s.server_seq = s.server_seq + 1
202 if dv == NX_TLS13_REC_VERDICT_OK {
203 if rct[0] == 22 {
204 let plen: i64 = rln[0]
205 var off: i64 = 0
206 var inner_done: i64 = 0
207 while inner_done == 0 {
208 if off + 4 > plen { inner_done = 1 } else {
209 let mtype: i64 = inner[off] & 0xff
210 let blen: i64 = ((inner[off+1]&0xff)<<16)|((inner[off+2]&0xff)<<8)|(inner[off+3]&0xff)
211 if off + 4 + blen > plen { inner_done = 1 } else {
212 if mtype == 11 {
213 if result < 0 {
214 // Certificate body = inner+off+4:
215 // [0] ctx_len, u24 cert_list_len, u24 cert_data_len, leaf DER
216 let body: *u8 = (inner as i64 + off + 4) as *u8
217 let ctxlen: i64 = body[0] & 0xff
218 var bp: i64 = 1 + ctxlen
219 bp = bp + 3
220 let cdl: i64 = ((body[bp]&0xff)<<16)|((body[bp+1]&0xff)<<8)|((body[bp+2]&0xff))
221 bp = bp + 3
222 let leaf: *u8 = (body as i64 + bp) as *u8
223 result = g_identify(leaf, cdl, nchain, nlen, achain, alen)
224 }
225 }
226 off = off + 4 + blen
227 }
228 }
229 }
230 }
231 }
232 }
233 }
234 }
235 }
236 }
237 sys_close(fd)
238 return result
239}
240
241// Server side (mirrors the daemon EXACTLY): peek the SNI, select, serve via the proven run.
242func g_serve_one(cfd: i64,
243 ncert: *u8, nlen: i64, nkey: *u8,
244 acert: *u8, alen: i64, akey: *u8) -> i64 {
245 sys_set_socket_timeout(cfd, 5)
246 let srv_rand: *u8 = sys_mmap(32); let srv_xp: *u8 = sys_mmap(32)
247 nx_csprng_fill(srv_rand, 32); nx_csprng_fill(srv_xp, 32)
248 let peek: *u8 = sys_mmap(8192); let host: *u8 = sys_mmap(256)
249 let pk: i64 = sys_recvfrom(cfd, peek, 8192, NX_SD_MSG_PEEK_GATE, 0 as *u8, 0 as *i64)
250 var idx: i64 = NX_SCS_CERT_NISHI
251 if pk > 0 { idx = scs_pick_from_clienthello(peek, pk, host, 256) }
252 var c: *u8 = ncert; var cl: i64 = nlen; var k: *u8 = nkey
253 if idx == NX_SCS_CERT_ANDELIN { c = acert; cl = alen; k = akey }
254 let rc: i64 = nx_tls13_server_session_run(cfd, srv_rand, srv_xp, c, cl, k)
255 return rc
256}
257
258// E2E case (fork-per-connection, mirroring the daemon): fork a one-shot server child that
259// accepts exactly this connection + serves the SNI-selected cert, drive it with the probe,
260// then reap the child. Isolated per case so one connection can never affect the next.
261// Returns 1 on FAIL, 0 on PASS.
262func e2e_one(lfd: i64, port: i64, label: *u8, sni: *u8, sni_len: i64, expect: i64,
263 nchain: *u8, nlen: i64, nkey: *u8, achain: *u8, alen: i64, akey: *u8) -> i64 {
264 let pid: i64 = sys_fork()
265 if pid == 0 {
266 sys_set_socket_timeout(lfd, 8)
267 let sock_addr: *u8 = sys_mmap(64)
268 let sock_len: *i64 = (sys_mmap(8)) as *i64
269 sock_len[0] = 16
270 let cfd: i64 = sys_accept_with_addr(lfd, sock_addr, sock_len)
271 if cfd >= 0 { g_serve_one(cfd, nchain, nlen, nkey, achain, alen, akey); sys_close(cfd) }
272 sys_exit(0)
273 }
274 let got: i64 = g_probe(port, sni, sni_len, nchain, nlen, achain, alen)
275 let st: *i64 = (sys_mmap(8)) as *i64
276 sys_wait4(pid, st, 0)
277 g_puts(" SNI="); sys_write(1, label, g_slen(label)); g_puts(" -> served=")
278 g_putidx(got); g_puts(" expect="); g_putidx(expect)
279 if got == expect { g_puts(" OK\n"); return 0 }
280 g_puts(" FAIL\n"); return 1
281}
282
283func main() -> i64 {
284 var fail: i64 = 0
285 g_puts("== nx_sites_sni_cert_gate (Route B: SNI-aware per-domain cert) ==\n")
286
287 // ---- load the two staged wildcard chains + keys ----
288 let nlb: *i64 = (sys_mmap(8)) as *i64; nlb[0]=0
289 let nchain: *u8 = sys_read_file("/tmp/sni_nishi_chain.der" as *u8, nlb)
290 let alb: *i64 = (sys_mmap(8)) as *i64; alb[0]=0
291 let achain: *u8 = sys_read_file("/tmp/sni_andelin_chain.der" as *u8, alb)
292 let nklb: *i64 = (sys_mmap(8)) as *i64; nklb[0]=0
293 let nkey: *u8 = sys_read_file("/tmp/sni_nishi_key.bin" as *u8, nklb)
294 let aklb: *i64 = (sys_mmap(8)) as *i64; aklb[0]=0
295 let akey: *u8 = sys_read_file("/tmp/sni_andelin_key.bin" as *u8, aklb)
296 if (nchain as i64)==0 { g_puts("FIXTURE-MISSING /tmp/sni_nishi_chain.der\n"); return 20 }
297 if (achain as i64)==0 { g_puts("FIXTURE-MISSING /tmp/sni_andelin_chain.der\n"); return 20 }
298 if (nkey as i64)==0 { g_puts("FIXTURE-MISSING /tmp/sni_nishi_key.bin\n"); return 20 }
299 if (akey as i64)==0 { g_puts("FIXTURE-MISSING /tmp/sni_andelin_key.bin\n"); return 20 }
300 let nlen: i64 = nlb[0]; let alen: i64 = alb[0]
301 if nlen < GATE_CERT_MIN { g_puts("FIXTURE-TOO-SMALL nishi\n"); return 20 }
302 if alen < GATE_CERT_MIN { g_puts("FIXTURE-TOO-SMALL andelin\n"); return 20 }
303 if nklb[0] != 32 { g_puts("FIXTURE-BAD-KEY nishi\n"); return 20 }
304 if aklb[0] != 32 { g_puts("FIXTURE-BAD-KEY andelin\n"); return 20 }
305 // sanity: the two leaves must actually differ, else the e2e test cannot discriminate
306 if g_mem_eq(nchain, achain, nlen) == 1 { g_puts("FIXTURE-IDENTICAL-CHAINS (cannot discriminate)\n"); return 20 }
307 g_puts(" fixtures: nishi="); g_puti(nlen); g_puts("B andelin="); g_puti(alen); g_puts("B (distinct leaves)\n")
308
309 // ---- LAYER 1: UNIT selection decisions (the daemon's exact codepath) ----
310 g_puts("-- Layer 1: shared selector (scs_pick_from_clienthello) --\n")
311 fail = fail + unit_check("nishifamily.com" as *u8, 15, NX_SCS_CERT_NISHI)
312 fail = fail + unit_check("andelinwest.com" as *u8, 15, NX_SCS_CERT_ANDELIN)
313 fail = fail + unit_check("app.nishifamily.com" as *u8, 19, NX_SCS_CERT_NISHI)
314 fail = fail + unit_check("shop.andelinwest.com" as *u8, 20, NX_SCS_CERT_ANDELIN)
315 fail = fail + unit_check("NISHIFAMILY.COM" as *u8, 15, NX_SCS_CERT_NISHI) // case-insensitive
316 fail = fail + unit_check("evilnishifamily.com" as *u8, 19, NX_SCS_CERT_NISHI) // label-boundary trap -> default
317 fail = fail + unit_check("notandelinwest.com" as *u8, 18, NX_SCS_CERT_NISHI) // label-boundary trap -> default
318 fail = fail + unit_check("example.org" as *u8, 11, NX_SCS_CERT_NISHI) // unknown -> default
319 fail = fail + unit_check("" as *u8, 0, NX_SCS_CERT_NISHI) // EMPTY SNI -> default
320
321 g_puts("-- Layer 1: malformed / fail-safe --\n")
322 // non-handshake record -> no SNI -> default
323 let nb: *u8 = sys_mmap(256); let nn: i64 = mk_ch(nb, "andelinwest.com" as *u8, 15); nb[0]=23 as u8
324 let hs1: *u8 = sys_mmap(256)
325 let r1: i64 = scs_pick_from_clienthello(nb, nn, hs1, 256)
326 g_puts(" non-handshake -> "); g_putidx(r1); if r1==NX_SCS_CERT_NISHI { g_puts(" OK\n") } else { g_puts(" FAIL\n"); fail=fail+1 }
327 // truncated mid-name -> bounds guard -> default
328 let tb: *u8 = sys_mmap(256); let tn: i64 = mk_ch(tb, "andelinwest.com" as *u8, 15)
329 let r2: i64 = scs_pick_from_clienthello(tb, tn-5, hs1, 256)
330 g_puts(" truncated-name -> "); g_putidx(r2); if r2==NX_SCS_CERT_NISHI { g_puts(" OK\n") } else { g_puts(" FAIL\n"); fail=fail+1 }
331 // empty buffer -> default
332 let zb: *u8 = sys_mmap(8)
333 let r3: i64 = scs_pick_from_clienthello(zb, 0, hs1, 256)
334 g_puts(" empty-buffer -> "); g_putidx(r3); if r3==NX_SCS_CERT_NISHI { g_puts(" OK\n") } else { g_puts(" FAIL\n"); fail=fail+1 }
335
336 // ---- LAYER 2: end-to-end loopback handshakes ----
337 g_puts("-- Layer 2: end-to-end TLS handshake (served leaf read off the wire) --\n")
338 let addr_buf: *u8 = sys_mmap(16)
339 nx_http_server_addr_any(addr_buf, GATE_PORT)
340 let lv: *i64 = (sys_mmap(8)) as *i64
341 let lfd: i64 = nx_http_server_listen(addr_buf, 16, lv)
342 if lfd < 0 { g_puts("LISTEN-FAIL on :"); g_puti(GATE_PORT); g_puts("\n"); return 21 }
343
344 // ---- drive the 4 cases, each its own one-shot server child (fork-per-connection) ----
345 fail = fail + e2e_one(lfd, GATE_PORT, "nishifamily.com" as *u8, "nishifamily.com" as *u8, 15, NX_SCS_CERT_NISHI, nchain, nlen, nkey, achain, alen, akey)
346 fail = fail + e2e_one(lfd, GATE_PORT, "andelinwest.com" as *u8, "andelinwest.com" as *u8, 15, NX_SCS_CERT_ANDELIN, nchain, nlen, nkey, achain, alen, akey)
347 fail = fail + e2e_one(lfd, GATE_PORT, "app.nishifamily.com" as *u8, "app.nishifamily.com" as *u8, 19, NX_SCS_CERT_NISHI, nchain, nlen, nkey, achain, alen, akey)
348 fail = fail + e2e_one(lfd, GATE_PORT, "example.org(unknown)" as *u8, "example.org" as *u8, 11, NX_SCS_CERT_NISHI, nchain, nlen, nkey, achain, alen, akey)
349 sys_close(lfd)
350
351 // persist the verdict (census-readable; the referee line, not a claim)
352 let lg: i64 = sys_openat_append("knowledge/status/sites_sni_cert.log" as *u8, 0x1a4)
353 if lg >= 0 {
354 if fail == 0 { let m: *u8 = "SNI-CERT unit=12 e2e=4 verdict=GREEN\n" as *u8; sys_write(lg, m, 37) }
355 else { let m2: *u8 = "SNI-CERT verdict=RED\n" as *u8; sys_write(lg, m2, 21) }
356 sys_close(lg)
357 }
358 if fail == 0 { g_puts("SNI-CERT-GATE GREEN\n") } else { g_puts("SNI-CERT-GATE RED fails="); g_puti(fail); g_puts("\n") }
359 return fail
360}