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}