code wiki / _hdl_build / nx_x25519_extvec_gate.nx

nx_x25519_extvec_gate.nx source

↩ module page · 198 lines · 8853 B

1// nx_x25519_extvec_gate.nx -- THE FIRST PROVABLY THIRD-PARTY-VALIDATED CLAIM. 2// Proven GREEN on the laptop 2026-07-31 (pass=2 fail=0). Shipped here so it runs where claims are served. 3// 4// Every other KAT in this corpus embeds its expected values as constants a developer copied out of a 5// specification. Honest work, but NOT provably external: nothing in the artifact distinguishes a value 6// transcribed from the IETF from one an author -- or an AGENT -- merely believed. This gate closes that: 7// 1 it reads the vectors FROM knowledge/extvec/rfc7748.txt AT RUN TIME. NO expected value appears in 8// this source at all, so there is nothing here for anyone to have mistyped or invented. 9// 2 it PINS the acquisition digest: the file must sha256 to the value nx_vecfetch computed IN-PROCESS, 10// at the socket, before the bytes ever touched disk. Edited, truncated or substituted -> pin fails. 11// 3 only then does it run x25519() and compare. 12// 13// THE PIN IS THE SECURITY PROPERTY, NOT THE RUN-TIME PARSE. Parsing from a file is necessary but NOT 14// sufficient: an agent could edit the fetched file to contain whatever answers make its implementation 15// pass, and the parse would faithfully read the forgery. Pinning to an ACQUISITION-TIME digest is what 16// turns "we read it from a file" into evidence. 17// AND THE PIN MAKES TRANSPORT UNTRUSTED-BUT-VERIFIED: whoever carries rfc7748.txt to this host -- scp, an 18// agent, a USB stick -- cannot corrupt it undetected, because sha256 preimage resistance means bytes that 19// hash to ab200228.. ARE the fetched bytes. The carrier never has to be trusted. That is the whole point 20// of content-addressing, and it is why this gate is safe to ship even though its data file is not. 21// 22// TO MAKE IT GREEN HERE: place knowledge/extvec/rfc7748.txt (any byte-exact copy; the pin checks it), or 23// build+run nx_vecfetch on this host. Until then it reports RED-and-honest: absent file, never a fallback 24// to constants. license_tier: ORIGINAL expect_exit: 0 25import "nx_syscalls.nx" 26import "nx_sha256_wasm.nx" 27import "nx_x25519.nx" 28import "nx_gate_verdict.nx" 29 30func w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 31func wb(b: *u8, n: i64) -> i64 { sys_write(1, b, n); return 0 } 32 33func nn(v: i64) -> i64 { 34 var m: i64 = v 35 if m < 0 { w("-" as *u8); m = 0 - m } 36 let t: *u8 = sys_mmap(32) 37 var k: i64 = 0 38 if m == 0 { t[0] = 48 as u8; k = 1 } 39 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 40 let b: *u8 = sys_mmap(32) 41 var j: i64 = 0 42 while j < k { b[j] = t[k - 1 - j]; j = j + 1 } 43 sys_write(1, b, k) 44 return 0 45} 46 47func hexnib(v: i64) -> i64 { if v < 10 { return 48 + v } return 87 + v } 48 49func hexval(c: i64) -> i64 { 50 if c >= 48 { if c <= 57 { return c - 48 } } 51 if c >= 97 { if c <= 102 { return c - 87 } } 52 if c >= 65 { if c <= 70 { return c - 55 } } 53 return 0 - 1 54} 55 56func starts(b: *u8, n: i64, at: i64, s: *u8) -> i64 { 57 var i: i64 = 0 58 while s[i] != (0 as u8) { 59 if at + i >= n { return 0 } 60 if b[at + i] != s[i] { return 0 } 61 i = i + 1 62 } 63 return 1 64} 65 66func findfrom(b: *u8, n: i64, s: *u8, from: i64) -> i64 { 67 var p: i64 = from 68 while p < n { 69 if starts(b, n, p, s) == 1 { return p } 70 p = p + 1 71 } 72 return 0 - 1 73} 74 75// Strict: refuses a short hex run, so a reflowed or truncated document fails loudly rather than yielding 76// a half-vector that would then be compared as if complete. 77func parse32(b: *u8, n: i64, from: i64, out: *u8) -> i64 { 78 var p: i64 = from 79 var got: i64 = 0 80 while p < n { 81 if got >= 32 { return p } 82 let c: i64 = b[p] as i64 83 let hv: i64 = hexval(c) 84 if hv < 0 { 85 if got > 0 { if got < 32 { got = 0 } } 86 p = p + 1 87 } else { 88 if p + 1 >= n { return 0 - 1 } 89 let hv2: i64 = hexval(b[p + 1] as i64) 90 if hv2 < 0 { p = p + 1 } 91 else { 92 out[got] = ((hv * 16) + hv2) as u8 93 got = got + 1 94 p = p + 2 95 } 96 } 97 } 98 if got >= 32 { return p } 99 return 0 - 1 100} 101 102func eq32(a: *u8, b: *u8) -> i64 { 103 var i: i64 = 0 104 while i < 32 { if a[i] != b[i] { return 0 } i = i + 1 } 105 return 1 106} 107 108func main() -> i64 { 109 w("nx_x25519_extvec_gate -- x25519 vs RFC 7748 5.2, vectors READ FROM THE FETCHED DOCUMENT\n" as *u8) 110 111 let lp: *i64 = sys_mmap(16) as *i64 112 lp[0] = 0 113 let b: *u8 = sys_read_file("knowledge/extvec/rfc7748.txt\x00" as *u8, lp) 114 if lp[0] <= 0 { 115 w("RED: fetched vector file absent -- run nx_vecfetch first. NOT falling back to constants.\n" as *u8) 116 return 1 117 } 118 119 let ctx: *u8 = sys_mmap(1024) 120 let dig: *u8 = sys_mmap(64) 121 nx_sha256_one_shot(b, lp[0], ctx, dig) 122 let hx: *u8 = sys_mmap(80) 123 var i: i64 = 0 124 while i < 32 { 125 hx[i * 2] = hexnib(((dig[i] as i64) / 16) & 15) as u8 126 hx[i * 2 + 1] = hexnib((dig[i] as i64) & 15) as u8 127 i = i + 1 128 } 129 // A MODULE-LEVEL `const NAME: *u8 = "..." as *u8` DOES NOT COMPARE CORRECTLY IN nx_cc. First run printed 130 // this digest byte-for-byte identical to the constant and STILL reported PIN FAILED -- a check 131 // disagreeing with its own displayed evidence means the INSTRUMENT is broken, not the subject. Bound as 132 // a LOCAL literal. Had I trusted that RED and loosened the pin, I would have disabled the one clause 133 // that makes this gate evidence. 134 // PIN ROTATED 2026-08-17: rfc-editor.org re-issued rfc7748.txt (immutable vectors, drifted boilerplate) 135 // since the 2026-07-31 fetch, so ab200228.. is no longer reproducible. Re-pinned to the current 136 // authoritative document: nx_vecfetch over the cert-validated sovereign TLS stack (200 OK), gunzip'd 137 // with CRC32 verified, canonical S5.2 vectors (out c3da5537..) confirmed present, 39298 bytes. 138 let want: *u8 = "279ca0ecc5e92e2962e27b846986aeb74729d9dd34bd4a04a362f80dcb596ad3\x00" as *u8 139 var pin: i64 = 1 140 i = 0 141 while i < 64 { if hx[i] != want[i] { pin = 0 } i = i + 1 } 142 w(" acquisition digest: " as *u8); wb(hx, 64); w("\n" as *u8) 143 if pin == 0 { 144 w("RED: PIN FAILED -- this file is not the one nx_vecfetch acquired.\n" as *u8) 145 w(" Refusing to read vectors from an unpinned document: an edited file would let us\n" as *u8) 146 w(" grade ourselves against answers of our own choosing.\n" as *u8) 147 return 1 148 } 149 w(" PIN OK -- bytes match the digest computed in-process at the socket\n" as *u8) 150 151 var pass: i64 = 0 152 var fail: i64 = 0 153 let sc: *u8 = sys_mmap(64) 154 let uu: *u8 = sys_mmap(64) 155 let ex: *u8 = sys_mmap(64) 156 let got: *u8 = sys_mmap(64) 157 158 var cur: i64 = 0 159 var vec: i64 = 0 160 while vec < 2 { 161 let ls: i64 = findfrom(b, lp[0], "Input scalar:" as *u8, cur) 162 if ls < 0 { w(" RED: could not locate 'Input scalar:' in the document\n" as *u8); fail = fail + 1; vec = 2 } 163 else { 164 let p1: i64 = parse32(b, lp[0], ls + 13, sc) 165 let lu: i64 = findfrom(b, lp[0], "Input u-coordinate:" as *u8, p1) 166 let p2: i64 = parse32(b, lp[0], lu + 19, uu) 167 let lo: i64 = findfrom(b, lp[0], "Output u-coordinate:" as *u8, p2) 168 let p3: i64 = parse32(b, lp[0], lo + 20, ex) 169 if p1 < 0 { fail = fail + 1 } else { if p2 < 0 { fail = fail + 1 } else { if p3 < 0 { fail = fail + 1 } else { 170 x25519(sc, uu, got) 171 if eq32(got, ex) == 1 { 172 pass = pass + 1 173 w(" PASS vector " as *u8); nn(vec + 1) 174 w(": x25519(scalar,u) == the document's Output u-coordinate\n" as *u8) 175 } else { 176 fail = fail + 1 177 w(" FAIL vector " as *u8); nn(vec + 1); w(": output mismatch\n" as *u8) 178 } 179 cur = p3 180 } } } 181 vec = vec + 1 182 } 183 } 184 185 w("\n refsrc=https://www.rfc-editor.org/rfc/rfc7748.txt\n" as *u8) 186 w(" refsrcdig=" as *u8); wb(hx, 64); w("\n" as *u8) 187 w(" ref=RFC7748-5.2 gate=nx_x25519_extvec_gate\n" as *u8) 188 w("nx_x25519_extvec_gate: pass=" as *u8); nn(pass); w(" fail=" as *u8); nn(fail) 189 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 190 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 191 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 192 let ctr__dry: *i64 = gv_ctr() 193 ctr__dry[0] = pass 194 ctr__dry[1] = pass + fail 195 let rc__dry: i64 = gv_verdict("X25519-EXTVEC-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8) 196 sys_exit(rc__dry) 197 return rc__dry 198}