code wiki / _hdl_build / nx_sha1_extvec_gate.nx
nx_sha1_extvec_gate.nx source
↩ module page · 270 lines · 12988 B
1// nx_sha1_extvec_gate.nx -- validated against RFC3174-resultarray0, read from the pinned+corroborated RFC3174 document.
2// ⚠HEADER CORRECTED 2026-08-01: this file was CLONED from nx_hmac_extvec_gate.nx and inherited its
3// header verbatim, so it claimed to be that gate validating HMAC-SHA-256 against RFC 4231. Nine of
4// twenty-three extvec gates carried the same wrong self-description. ★★★★★A CLONED FILE INHERITS ITS
5// PARENT'S CLAIMS, AND ON AN EVIDENCE ARTIFACT THE HEADER IS A PROVENANCE CLAIM, NOT A COMMENT --
6// an auditor reading headers would have concluded RFC 4231 validated all nine subjects.
7//
8// SUPERSEDES the single-case version. Going from 1 vector to 7 matters because the cases are deliberately
9// chosen by the authority to hit DIFFERENT code paths, and the ones I was NOT running are the interesting
10// ones: case 3 uses a 50-byte repeated data block, cases 6 and 7 use a 131-BYTE KEY (longer than the 64-byte
11// SHA-256 block, so the key must be HASHED first), and case 5 publishes a TRUNCATED 128-bit MAC.
12// ★A gate that ran only case 1 would never touch the key-longer-than-block branch -- the single most
13// commonly botched part of HMAC. Running one vector from a seven-vector suite is not "validated against
14// RFC 4231"; it is validated against one line of it.
15//
16// ⚠VARIABLE-LENGTH FIELDS, HANDLED BY TERMINATOR NOT BY LENGTH. Key/Data lengths differ per case and their
17// annotations are inconsistent -- "(20 bytes)" for keys but ("Hi There") for data -- so a length cannot be
18// read uniformly. Instead the hex run is read until the first `(`, which terminates both forms.
19// ⚠THE MAC HAS NO `(` TERMINATOR and case 5's is TRUNCATED to 16 bytes, so it is read as hex pairs until a
20// pair is not both-hex, capped at 32. That correctly stops at the section heading that follows -- note
21// "4.3." begins with '4', a HEX DIGIT, and is only rejected because '.' is not: the PAIR rule saves this,
22// a single-nibble rule would have swallowed it.
23//
24// Construction unchanged: no expected value in this source, document pinned to a socket-time digest, every
25// key/data/MAC read from that pinned document, and a completeness check that refuses GREEN below 7.
26// license_tier: ORIGINAL expect_exit: 0
27import "nx_syscalls.nx"
28import "nx_sha256_wasm.nx"
29import "nx_sha1.nx"
30import "nx_gate_verdict.nx"
31
32func w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
33func wb(b: *u8, n: i64) -> i64 { sys_write(1, b, n); return 0 }
34
35func nn(v: i64) -> i64 {
36 var m: i64 = v
37 if m < 0 { w("-" as *u8); m = 0 - m }
38 let t: *u8 = sys_mmap(32)
39 var k: i64 = 0
40 if m == 0 { t[0] = 48 as u8; k = 1 }
41 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
42 let b: *u8 = sys_mmap(32)
43 var j: i64 = 0
44 while j < k { b[j] = t[k - 1 - j]; j = j + 1 }
45 sys_write(1, b, k)
46 return 0
47}
48
49func hexnib(v: i64) -> i64 { if v < 10 { return 48 + v } return 87 + v }
50
51func hexval(c: i64) -> i64 {
52 if c >= 48 { if c <= 57 { return c - 48 } }
53 if c >= 97 { if c <= 102 { return c - 87 } }
54 if c >= 65 { if c <= 70 { return c - 55 } }
55 return 0 - 1
56}
57
58func isws(c: i64) -> i64 {
59 if c == 32 { return 1 }
60 if c == 10 { return 1 }
61 if c == 13 { return 1 }
62 if c == 9 { return 1 }
63 return 0
64}
65
66func starts(b: *u8, n: i64, at: i64, s: *u8) -> i64 {
67 var i: i64 = 0
68 while s[i] != (0 as u8) {
69 if at + i >= n { return 0 }
70 if b[at + i] != s[i] { return 0 }
71 i = i + 1
72 }
73 return 1
74}
75
76func findfrom(b: *u8, n: i64, s: *u8, from: i64) -> i64 {
77 var p: i64 = from
78 while p < n {
79 if starts(b, n, p, s) == 1 { return p }
80 p = p + 1
81 }
82 return 0 - 1
83}
84
85// Read hex PAIRS (whitespace between pairs is skipped) until a pair is not both-hex, or `(` is reached,
86// or cap is hit. Returns the byte count.
87func parserun(b: *u8, n: i64, from: i64, out: *u8, cap: i64) -> i64 {
88 var p: i64 = from
89 var got: i64 = 0
90 var done: i64 = 0
91 while done == 0 {
92 if got >= cap { done = 1 }
93 else {
94 // skip whitespace
95 var d1: i64 = 0
96 while d1 == 0 {
97 if p >= n { d1 = 1 }
98 else { if isws(b[p] as i64) == 1 { p = p + 1 } else { d1 = 1 } }
99 }
100 if p + 1 >= n { done = 1 }
101 else {
102 // `(` ends a LINE SEGMENT, not the value: RFC 4231 annotates EVERY wrapped line, e.g.
103 // Data = 7768...6e7420 ("what do ya want ")
104 // 666f...693f ("for nothing?")
105 // Treating `(` as the value terminator stopped case 2 at 16 of its 28 bytes. On `(`, skip
106 // to the next line and continue ONLY if it resumes with a hex pair; otherwise stop.
107 if b[p] == (40 as u8) {
108 var dl: i64 = 0
109 while dl == 0 {
110 if p >= n { dl = 1 }
111 else { if b[p] == (10 as u8) { p = p + 1; dl = 1 } else { p = p + 1 } }
112 }
113 var dw: i64 = 0
114 while dw == 0 {
115 if p >= n { dw = 1 }
116 else { if isws(b[p] as i64) == 1 { p = p + 1 } else { dw = 1 } }
117 }
118 // ⚠"Da" IS VALID HEX. The next line may be ` Data = 7768...`, and testing only
119 // "do the first two chars parse as hex" accepted D,a and read the LABEL as byte 0xDA --
120 // adding one phantom byte to every key (case 1 read 21 of 20, case 2 read 5 of 4).
121 // Third form of the same hazard today: an ASCII gutter, then English prose, now a
122 // FIELD LABEL that happens to spell hex.
123 // STRUCTURAL DISCRIMINATOR: a continuation line has NO '='; every label line has one.
124 var eqfound: i64 = 0
125 var sc: i64 = p
126 var de: i64 = 0
127 while de == 0 {
128 if sc >= n { de = 1 }
129 else { if b[sc] == (10 as u8) { de = 1 }
130 else { if b[sc] == (61 as u8) { eqfound = 1; de = 1 } else { sc = sc + 1 } } }
131 }
132 if eqfound == 1 { done = 1 }
133 else {
134 if p + 1 >= n { done = 1 }
135 else {
136 if hexval(b[p] as i64) < 0 { done = 1 }
137 else { if hexval(b[p + 1] as i64) < 0 { done = 1 } }
138 }
139 }
140 }
141 else {
142 let h1: i64 = hexval(b[p] as i64)
143 let h2: i64 = hexval(b[p + 1] as i64)
144 if h1 < 0 { done = 1 }
145 else { if h2 < 0 { done = 1 }
146 else {
147 out[got] = ((h1 * 16) + h2) as u8
148 got = got + 1
149 p = p + 2
150 } }
151 }
152 }
153 }
154 }
155 return got
156}
157
158// Find the next "Key" FIELD LABEL at line start. Verified against the raw bytes of RFC 4231:
159// line 192 " Key = 0b0b..." <- cases 1,2,4,5,6,7
160// line 251 " Key aaaa..." <- CASE 3: no '=' at all
161// line 177 " Keys, data, and digests..." <- PROSE. "Key" is a PREFIX of "Keys".
162// Anchoring on "\n Key" alone matched that prose line and drove the parse to ZERO cases. So the label is
163// accepted only when the character AFTER "Key" is a SPACE or '=' -- which "Keys" fails on 's'.
164// ★A prefix match is not a token match. Every anchor in this file is now checked against the byte AFTER it.
165func find_key_label(b: *u8, n: i64, from: i64) -> i64 {
166 var p: i64 = from
167 var done: i64 = 0
168 while done == 0 {
169 let h: i64 = findfrom(b, n, "\n Key" as *u8, p)
170 if h < 0 { return 0 - 1 }
171 let c: i64 = b[h + 7] as i64
172 if c == 32 { return h }
173 if c == 61 { return h }
174 p = h + 7
175 }
176 return 0 - 1
177}
178func main() -> i64 {
179 w("nx_sha1_extvec_gate -- SHA-1 vs RFC 3174 resultarray, READ FROM THE FETCHED DOCUMENT\n" as *u8)
180 w(" PURPOSE: ADJUDICATE the unresolved RFC 2202 HMAC-SHA-1 RED. This gate shares NOTHING with the\n" as *u8)
181 w(" HMAC layer, so it can tell a broken hash core from a broken HMAC wrapper or reader.\n" as *u8)
182 let lp: *i64 = sys_mmap(16) as *i64
183 lp[0] = 0
184 let b: *u8 = sys_read_file("knowledge/extvec/rfc3174.txt\x00" as *u8, lp)
185 if lp[0] <= 0 { w("RED: fetched vector file absent.\n" as *u8); return 1 }
186 let ctx: *u8 = sys_mmap(1024)
187 let dg: *u8 = sys_mmap(64)
188 nx_sha256_one_shot(b, lp[0], ctx, dg)
189 let hx: *u8 = sys_mmap(80)
190 var i: i64 = 0
191 while i < 32 { hx[i*2] = hexnib(((dg[i] as i64)/16)&15) as u8; hx[i*2+1] = hexnib((dg[i] as i64)&15) as u8; i = i + 1 }
192 let wnt: *u8 = "aa0ca4ef21609e64897980488c5555109266c29cd814fe97680c916566d29332\x00" as *u8
193 var pin: i64 = 1
194 i = 0
195 while i < 64 { if hx[i] != wnt[i] { pin = 0 } i = i + 1 }
196 w(" acquisition digest: " as *u8); wb(hx, 64); w("\n" as *u8)
197 if pin == 0 { w("RED: PIN FAILED.\n" as *u8); return 1 }
198 w(" PIN OK -- bytes match the digest computed in-process at the socket\n" as *u8)
199
200 // The message: #define TEST1 "abc" -- read from the document, not typed.
201 let t1: i64 = findfrom(b, lp[0], "#define TEST1" as *u8, 0)
202 if t1 < 0 { w("RED: no '#define TEST1'\n" as *u8); return 1 }
203 let q1: i64 = findfrom(b, lp[0], "\"" as *u8, t1)
204 if q1 < 0 { w("RED: no quote after TEST1\n" as *u8); return 1 }
205 let msg: *u8 = sys_mmap(64)
206 var mn: i64 = 0
207 var p: i64 = q1 + 1
208 var d: i64 = 0
209 while d == 0 {
210 if p >= lp[0] { d = 1 }
211 else { if b[p] == (34 as u8) { d = 1 } else { msg[mn] = b[p]; mn = mn + 1; p = p + 1 } }
212 }
213 if mn != 3 { w("RED: TEST1 parsed " as *u8); nn(mn); w(" bytes, expected 3 -- the READER.\n" as *u8); return 1 }
214
215 // The answer: the FIRST entry of char *resultarray[4]. Anchored on the array declaration so no
216 // intermediate or unrelated hex block can be mistaken for it.
217 let ra: i64 = findfrom(b, lp[0], "char *resultarray" as *u8, 0)
218 if ra < 0 { w("RED: no 'char *resultarray'\n" as *u8); return 1 }
219 let rq: i64 = findfrom(b, lp[0], "\"" as *u8, ra)
220 if rq < 0 { w("RED: no quoted result\n" as *u8); return 1 }
221 let exp: *u8 = sys_mmap(64)
222 let en: i64 = parserun(b, lp[0], rq + 1, exp, 20)
223 if en != 20 { w("RED: result parsed " as *u8); nn(en); w(" bytes, expected 20 -- the READER.\n" as *u8); return 1 }
224 w(" parsed TEST1 (3 bytes) and resultarray[0] (20 bytes) from the document\n" as *u8)
225
226 let got: *u8 = sys_mmap(64)
227 sha1(msg, mn, got)
228
229 var pass: i64 = 0
230 var fail: i64 = 0
231 var same: i64 = 1
232 i = 0
233 while i < 20 { if got[i] != exp[i] { same = 0 } i = i + 1 }
234 if same == 1 {
235 pass = pass + 1
236 w(" PASS: sha1(TEST1) == resultarray[0]\n" as *u8)
237 w(" ==> THE HASH CORE IS CORRECT. The RFC 2202 HMAC-SHA-1 RED is therefore NOT the hash;\n" as *u8)
238 w(" it is the HMAC wrapper or my RFC 2202 reader. Suspect list narrowed by elimination.\n" as *u8)
239 } else {
240 fail = fail + 1
241 w(" FAIL: sha1(TEST1) != resultarray[0]\n" as *u8)
242 w(" ==> THE HASH CORE ITSELF DISAGREES WITH RFC 3174. Both HMAC-SHA-1 paths are downstream\n" as *u8)
243 w(" victims, and the defect is in sha1(), not in the HMAC layer.\n" as *u8)
244 w(" expected[0..3]=" as *u8); nn(exp[0] as i64); w(" " as *u8); nn(exp[1] as i64)
245 w(" got[0..3]=" as *u8); nn(got[0] as i64); w(" " as *u8); nn(got[1] as i64); w("\n" as *u8)
246 }
247
248 // NEGATIVE CONTROL
249 let m2: *u8 = "abd\x00" as *u8
250 sha1(m2, 3, got)
251 var same2: i64 = 1
252 i = 0
253 while i < 20 { if got[i] != exp[i] { same2 = 0 } i = i + 1 }
254 if same2 == 0 { pass = pass + 1; w(" PASS NEG: \"abd\" differs (the check can fail)\n" as *u8) }
255 else { fail = fail + 1; w(" FAIL NEG\n" as *u8) }
256
257 w("\n refsrc=https://www.rfc-editor.org/rfc/rfc3174.txt\n" as *u8)
258 w(" refsrcdig=" as *u8); wb(hx, 64); w("\n" as *u8)
259 w(" ref=RFC3174-resultarray0 gate=nx_sha1_extvec_gate\n" as *u8)
260 w("nx_sha1_extvec_gate: pass=" as *u8); nn(pass); w(" fail=" as *u8); nn(fail)
261 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
262 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
263 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
264 let ctr__dry: *i64 = gv_ctr()
265 ctr__dry[0] = pass
266 ctr__dry[1] = pass + fail
267 let rc__dry: i64 = gv_verdict("SHA1-EXTVEC-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
268 sys_exit(rc__dry)
269 return rc__dry
270}