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}