code wiki / _hdl_build / nx_acme_dns01_propagation_gate.nx

nx_acme_dns01_propagation_gate.nx source

↩ module page · 167 lines · 7391 B

1// nx_acme_dns01_propagation_gate.nx -- LIVE proof for the DNS-01 TXT 2// propagation poll that replaces the fixed sys_sleep_ms(360000) in 3// nx_acme_dns01_issue. 4// 5// Rows: 6// 1. SET a UNIQUE _acme-challenge.nishifamily.com TXT at Porkbun (TLS 1.2). 7// 2. POLL nx_dns01_wait_txt_propagated -> assert it returns VISIBLE(1) 8// within a bounded window, and report how long it took (this is the 9// "trigger as soon as propagated, not after a fixed 6 min" win). 10// 3. DELETE the TXT (ALWAYS runs -> guaranteed cleanup, fully reversible). 11// 4. NEG-CONTROL: poll for a value that was NEVER set, with a SHORT 12// timeout -> assert it returns TIMED-OUT(0) and is BOUNDED (does not 13// hang). This proves the ceiling path. 14// 15// AUTHORIZED scope: ONE Porkbun TXT set + delete (cleaned up), read-only DNS 16// queries. NO Let's Encrypt challenge is triggered. 17// 18// Creds: ./_offc/nx_secret_cli.elf get porkbun (/tmp/nxsecret.out; BOM stripped). 19// 20// expect_exit: 0 21// license_tier: ORIGINAL 22 23import "nx_syscalls.nx" 24import "nx_x509_trust_store.nx" 25import "nx_trust_store_load_from_certdata.nx" 26import "nx_acme_porkbun.nx" 27import "nx_acme_dns01_propagation.nx" 28 29func p_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 30func p_n(v: i64) -> i64 { 31 var m: i64=v 32 if m<0 { p_w("-" as *u8); m=0-m } 33 let t: *u8=sys_mmap(24); var k: i64=0 34 if m==0 { t[0]=48 as u8; k=1 } 35 while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } 36 let o: *u8=sys_mmap(24); var i: i64=0 37 while i<k { o[i]=t[k-1-i]; i=i+1 } 38 sys_write(1,o,k); return 0 39} 40func p_row(id: *u8, ok: i64, pass: *i64) -> i64 { 41 p_w(" " as *u8); p_w(id); p_w(": " as *u8) 42 if ok==1 { p_w("OK\n" as *u8); pass[0]=pass[0]+1 } else { p_w("FAIL\n" as *u8) } 43 return 0 44} 45func p_dec(buf: *u8, off: i64, v: i64) -> i64 { 46 var m: i64 = v 47 if m == 0 { buf[off]=48 as u8; return off+1 } 48 let t: *u8 = sys_mmap(24); var k: i64 = 0 49 while m > 0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } 50 var o: i64 = off; var i: i64 = k-1 51 while i >= 0 { buf[o]=t[i]; o=o+1; i=i-1 } 52 return o 53} 54func p_read_file(path: *u8, out: *u8, cap: i64) -> i64 { 55 let fd: i64 = sys_openat_rd(path) 56 if fd <= 0 { return 0 - 1 } 57 let n: i64 = sys_read(fd, out, cap) 58 sys_close(fd) 59 return n 60} 61 62func main() -> i64 { 63 let pass: *i64 = sys_mmap(8) as *i64; pass[0]=0 64 var total: i64 = 0 65 p_w("=== NX-ACME-DNS01-PROPAGATION GATE (live TXT set -> real propagation poll -> delete) ===\n" as *u8) 66 67 let lr: i64 = nx_trust_store_load_from_certdata("data/mozilla_certdata.txt\x00" as *u8, 300, 4194304) 68 if lr <= 0 { p_w(" trust load FAIL\nverdict=RED\n" as *u8); sys_exit(1); return 1 } 69 let store: *TrustStore = lr as *TrustStore 70 let now: i64 = sys_now_realtime_sec() 71 72 let raw: *u8 = sys_mmap(512) 73 let rawn: i64 = p_read_file("/tmp/nxsecret.out\x00" as *u8, raw, 512) 74 if rawn <= 0 { p_w(" creds read FAIL (/tmp/nxsecret.out)\nverdict=RED\n" as *u8); sys_exit(1); return 1 } 75 var base: i64 = 0 76 if rawn >= 3 { if raw[0]==(0xEF as u8) { if raw[1]==(0xBB as u8) { if raw[2]==(0xBF as u8) { base=3 } } } } 77 let creds: *u8 = (raw as i64 + base) as *u8 78 let clen: i64 = rawn - base 79 var nl: i64 = 0 80 while nl < clen { if creds[nl]==0x0a { break }; nl=nl+1 } 81 var ak_end: i64 = nl 82 if ak_end>0 { if creds[ak_end-1]==0x0d { ak_end=ak_end-1 } } 83 let ak: *u8 = creds 84 let akn: i64 = ak_end 85 let s2: i64 = nl + 1 86 var nl2: i64 = s2 87 while nl2 < clen { if creds[nl2]==0x0a { break }; nl2=nl2+1 } 88 var sk_end: i64 = nl2 89 if sk_end>s2 { if creds[sk_end-1]==0x0d { sk_end=sk_end-1 } } 90 let sk: *u8 = (creds as i64 + s2) as *u8 91 let skn: i64 = sk_end - s2 92 if akn<=0 { p_w(" apikey empty\nverdict=RED\n" as *u8); sys_exit(1); return 1 } 93 if skn<=0 { p_w(" secret empty\nverdict=RED\n" as *u8); sys_exit(1); return 1 } 94 p_w(" vault creds loaded (apikey len=" as *u8); p_n(akn); p_w(")\n" as *u8) 95 96 let dom: *u8 = "nishifamily.com" as *u8 97 let dom_n: i64 = 15 98 99 // Unique TXT value (>= a realistic 43-char ACME proof length not required; 100 // any unique string round-trips through Porkbun + the TXT parser). 101 let tval: *u8 = sys_mmap(64) 102 var vo: i64 = 0 103 let pfx: *u8 = "nishi-dns01-poll-" as *u8 104 var ppi: i64 = 0 105 while pfx[ppi] != (0 as u8) { tval[vo]=pfx[ppi]; vo=vo+1; ppi=ppi+1 } 106 vo = p_dec(tval, vo, now) 107 let vn: i64 = vo 108 p_w(" test TXT value = " as *u8); sys_write(1, tval, vn); p_w("\n" as *u8) 109 110 // ---- Row 1: SET ---- 111 let resp: *u8 = sys_mmap(16384) 112 let rn: *i64 = sys_mmap(8) as *i64 113 let sv: i64 = nx_porkbun_set_txt(ak, akn, sk, skn, dom, dom_n, tval, vn, store, now, resp, 16384, rn) 114 var set_ok: i64 = 0 115 if sv == NX_PORKBUN_OK { set_ok = 1 } 116 total=total+1; p_row("SET _acme-challenge.nishifamily.com TXT -> SUCCESS (TLS 1.2)" as *u8, set_ok, pass) 117 118 // ---- Row 2: POLL until visible (bounded 150s) ---- 119 var poll_ok: i64 = 0 120 var secs: i64 = 0 - 1 121 if set_ok == 1 { 122 let wants: *i64 = sys_mmap(16) as *i64 123 let wlens: *i64 = sys_mmap(16) as *i64 124 wants[0] = tval as i64 125 wlens[0] = vn 126 let t0: i64 = sys_now_realtime_sec() 127 let vis: i64 = nx_dns01_wait_txt_propagated(dom, dom_n, wants, wlens, 1, 150000, now) 128 let t1: i64 = sys_now_realtime_sec() 129 secs = t1 - t0 130 if vis == 1 { poll_ok = 1 } 131 p_w(" poll returned vis=" as *u8); p_n(vis); p_w(" after ~" as *u8); p_n(secs); p_w("s (fixed-sleep was 360s)\n" as *u8) 132 } 133 total=total+1; p_row("POLL -> VISIBLE(1) within bound (triggers as soon as propagated)" as *u8, poll_ok, pass) 134 135 // ---- Row 3: DELETE (always; cleanup) ---- 136 let resp2: *u8 = sys_mmap(16384) 137 let rn2: *i64 = sys_mmap(8) as *i64 138 let dv: i64 = nx_porkbun_delete_txt(ak, akn, sk, skn, dom, dom_n, store, now, resp2, 16384, rn2) 139 var del_ok: i64 = 0 140 if dv == NX_PORKBUN_OK { del_ok = 1 } 141 total=total+1; p_row("DELETE TXT (guaranteed cleanup, reversible)" as *u8, del_ok, pass) 142 143 // ---- Row 4: NEG-CONTROL: poll an ABSENT value, SHORT timeout -> 0, bounded ---- 144 let absent: *u8 = sys_mmap(64) 145 var ao: i64 = 0 146 let apfx: *u8 = "nishi-dns01-ABSENT-never-set-" as *u8 147 var api: i64 = 0 148 while apfx[api] != (0 as u8) { absent[ao]=apfx[api]; ao=ao+1; api=api+1 } 149 ao = p_dec(absent, ao, now) 150 let an: i64 = ao 151 let wants2: *i64 = sys_mmap(16) as *i64 152 let wlens2: *i64 = sys_mmap(16) as *i64 153 wants2[0] = absent as i64 154 wlens2[0] = an 155 let nt0: i64 = sys_now_realtime_sec() 156 let nvis: i64 = nx_dns01_wait_txt_propagated(dom, dom_n, wants2, wlens2, 1, 8000, now) 157 let nt1: i64 = sys_now_realtime_sec() 158 let nsecs: i64 = nt1 - nt0 159 var neg_ok: i64 = 0 160 if nvis == 0 { if nsecs <= 30 { neg_ok = 1 } } // returned not-visible AND bounded 161 p_w(" neg-control poll(absent, 8s timeout) returned vis=" as *u8); p_n(nvis); p_w(" after ~" as *u8); p_n(nsecs); p_w("s\n" as *u8) 162 total=total+1; p_row("NEG-CONTROL: absent value -> TIMED-OUT(0), bounded (no hang)" as *u8, neg_ok, pass) 163 164 p_w("NX-ACME-DNS01-PROPAGATION rows=" as *u8); p_n(total); p_w(" pass=" as *u8); p_n(pass[0]) 165 if pass[0]==total { p_w(" verdict=GREEN\n" as *u8); sys_exit(0); return 0 } 166 p_w(" verdict=RED\n" as *u8); sys_exit(1); return 1 167}