code wiki / _hdl_build / nx_hmacsha1fix_extvec_gate.nx
nx_hmacsha1fix_extvec_gate.nx source
↩ module page · 511 lines · 26973 B
1// nx_hmacsha1fix_extvec_gate.nx -- validated against RFC2202-HMAC-SHA1, read from the pinned+corroborated RFC2202 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, REMEMBERING whether the skip crossed a line boundary
95 var d1: i64 = 0
96 var crossed: i64 = 0
97 while d1 == 0 {
98 if p >= n { d1 = 1 }
99 else { if isws(b[p] as i64) == 1 { if b[p] == (10 as u8) { crossed = 1 } p = p + 1 } else { d1 = 1 } }
100 }
101 // ★★★★★ THE HAZARD THIS FILE ALREADY NAMED, IN THE ONE PATH THE FIX NEVER COVERED.
102 // "Da" IS VALID HEX. The discriminator -- a continuation line has NO '=', a label line always
103 // does -- was written above for the `(`-ANNOTATED branch ONLY. A `digest =` value carries no
104 // `(` annotation, so it fell through to the plain hex-pair path, ran straight past end-of-line,
105 // and read the "da" of "data_len =" as byte 0xDA: 21 bytes returned for a 20-byte digest.
106 // MEASURED, not inferred:
107 // expected = e8e9…1a91 DA computed = e8e9…1a91 00 <- first 20 bytes IDENTICAL
108 // It bit exactly ONE case because that block is the only one followed by a `data_len` line; the
109 // duplicate case 7 is followed by the page footer ("Cheng & Glenn" -- 'h' is not hex), which is
110 // why two cases with IDENTICAL inputs disagreed and looked like a crypto or pairing fault.
111 // ★A FIX APPLIED TO ONE BRANCH OF A TWO-BRANCH READER IS HALF A FIX -- AND THE HALF YOU SKIPPED
112 // WILL IMPERSONATE A BUG IN THE SUBJECT.
113 // Terminating by setting p = n reuses the existing bounds check instead of adding a branch:
114 // after four failed hypotheses here, the smallest possible edit is the safest one.
115 if crossed == 1 {
116 var eq2: i64 = 0
117 var sc2: i64 = p
118 var de2: i64 = 0
119 while de2 == 0 {
120 if sc2 >= n { de2 = 1 }
121 else { if b[sc2] == (10 as u8) { de2 = 1 }
122 else { if b[sc2] == (61 as u8) { eq2 = 1; de2 = 1 } else { sc2 = sc2 + 1 } } }
123 }
124 if eq2 == 1 { p = n }
125 }
126 if p + 1 >= n { done = 1 }
127 else {
128 // `(` ends a LINE SEGMENT, not the value: RFC 4231 annotates EVERY wrapped line, e.g.
129 // Data = 7768...6e7420 ("what do ya want ")
130 // 666f...693f ("for nothing?")
131 // Treating `(` as the value terminator stopped case 2 at 16 of its 28 bytes. On `(`, skip
132 // to the next line and continue ONLY if it resumes with a hex pair; otherwise stop.
133 if b[p] == (40 as u8) {
134 var dl: i64 = 0
135 while dl == 0 {
136 if p >= n { dl = 1 }
137 else { if b[p] == (10 as u8) { p = p + 1; dl = 1 } else { p = p + 1 } }
138 }
139 var dw: i64 = 0
140 while dw == 0 {
141 if p >= n { dw = 1 }
142 else { if isws(b[p] as i64) == 1 { p = p + 1 } else { dw = 1 } }
143 }
144 // ⚠"Da" IS VALID HEX. The next line may be ` Data = 7768...`, and testing only
145 // "do the first two chars parse as hex" accepted D,a and read the LABEL as byte 0xDA --
146 // adding one phantom byte to every key (case 1 read 21 of 20, case 2 read 5 of 4).
147 // Third form of the same hazard today: an ASCII gutter, then English prose, now a
148 // FIELD LABEL that happens to spell hex.
149 // STRUCTURAL DISCRIMINATOR: a continuation line has NO '='; every label line has one.
150 var eqfound: i64 = 0
151 var sc: i64 = p
152 var de: i64 = 0
153 while de == 0 {
154 if sc >= n { de = 1 }
155 else { if b[sc] == (10 as u8) { de = 1 }
156 else { if b[sc] == (61 as u8) { eqfound = 1; de = 1 } else { sc = sc + 1 } } }
157 }
158 if eqfound == 1 { done = 1 }
159 else {
160 if p + 1 >= n { done = 1 }
161 else {
162 if hexval(b[p] as i64) < 0 { done = 1 }
163 else { if hexval(b[p + 1] as i64) < 0 { done = 1 } }
164 }
165 }
166 }
167 else {
168 let h1: i64 = hexval(b[p] as i64)
169 let h2: i64 = hexval(b[p + 1] as i64)
170 if h1 < 0 { done = 1 }
171 else { if h2 < 0 { done = 1 }
172 else {
173 out[got] = ((h1 * 16) + h2) as u8
174 got = got + 1
175 p = p + 2
176 } }
177 }
178 }
179 }
180 }
181 return got
182}
183
184// Find the next "Key" FIELD LABEL at line start. Verified against the raw bytes of RFC 4231:
185// line 192 " Key = 0b0b..." <- cases 1,2,4,5,6,7
186// line 251 " Key aaaa..." <- CASE 3: no '=' at all
187// line 177 " Keys, data, and digests..." <- PROSE. "Key" is a PREFIX of "Keys".
188// Anchoring on "\n Key" alone matched that prose line and drove the parse to ZERO cases. So the label is
189// accepted only when the character AFTER "Key" is a SPACE or '=' -- which "Keys" fails on 's'.
190// ★A prefix match is not a token match. Every anchor in this file is now checked against the byte AFTER it.
191func find_key_label(b: *u8, n: i64, from: i64) -> i64 {
192 var p: i64 = from
193 var done: i64 = 0
194 while done == 0 {
195 let h: i64 = findfrom(b, n, "\n Key" as *u8, p)
196 if h < 0 { return 0 - 1 }
197 let c: i64 = b[h + 7] as i64
198 if c == 32 { return h }
199 if c == 61 { return h }
200 p = h + 7
201 }
202 return 0 - 1
203}
204func readdec(b: *u8, n: i64, from: i64) -> i64 {
205 var p: i64 = from
206 var v: i64 = 0
207 var got: i64 = 0
208 var done: i64 = 0
209 while done == 0 {
210 if p >= n { done = 1 }
211 else {
212 let c: i64 = b[p] as i64
213 if c == 32 { if got == 1 { done = 1 } else { p = p + 1 } }
214 else { if c >= 48 { if c <= 57 { v = (v*10)+(c-48); got = 1; p = p + 1 } else { done = 1 } } else { done = 1 } }
215 }
216 }
217 if got == 0 { return 0 - 1 }
218 return v
219}
220
221// A field is EITHER 0x-hex OR a quoted ASCII string. RFC 2202 uses both for `data` across its cases
222// (0xdd... for the repeated-byte cases, "Hi There" for the readable ones), so the reader must dispatch on
223// the first non-space character rather than assume one form.
224func readdec_skipeq(b: *u8, n: i64, from: i64) -> i64 {
225 var p: i64 = from
226 var d: i64 = 0
227 while d == 0 {
228 if p >= n { d = 1 }
229 else { if b[p] == (32 as u8) { p = p + 1 } else { if b[p] == (61 as u8) { p = p + 1 } else { d = 1 } } }
230 }
231 return readdec(b, n, p)
232}
233
234func readfield(b: *u8, n: i64, from: i64, out: *u8, cap: i64) -> i64 {
235 var p: i64 = from
236 var d: i64 = 0
237 while d == 0 {
238 if p >= n { d = 1 }
239 else { if b[p] == (32 as u8) { p = p + 1 } else { d = 1 } }
240 }
241 if p >= n { return 0 - 1 }
242 if b[p] == (34 as u8) {
243 // ⚠A WRAPPED QUOTED STRING: the newline REPLACES a space, it does not delete one.
244 // data = "Test Using Larger Than Block-Size Key and Larger
245 // Than One Block-Size Data"
246 // Copying raw gave 89 (47 + newline + 16 indent + 25); dropping the wrap entirely gives 72; the
247 // document declares 73. So a newline plus its following indentation collapses to EXACTLY ONE SPACE.
248 // ★Verified against the document's own data_len, not assumed -- the declared length is what
249 // distinguishes "delete the wrap" from "replace the wrap", and those differ by one byte.
250 var k: i64 = 0
251 p = p + 1
252 while p < n {
253 if b[p] == (34 as u8) { return k }
254 if k >= cap { return 0 - 1 }
255 if b[p] == (10 as u8) {
256 out[k] = 32 as u8
257 k = k + 1
258 p = p + 1
259 var ds: i64 = 0
260 while ds == 0 {
261 if p >= n { ds = 1 }
262 else { if b[p] == (32 as u8) { p = p + 1 } else { if b[p] == (13 as u8) { p = p + 1 } else { ds = 1 } } }
263 }
264 } else {
265 if b[p] == (13 as u8) { p = p + 1 }
266 else {
267 out[k] = b[p]
268 k = k + 1
269 p = p + 1
270 }
271 }
272 }
273 return 0 - 1
274 }
275 if p + 1 < n { if b[p] == (48 as u8) { if b[p+1] == (120 as u8) {
276 let got: i64 = parserun(b, n, p + 2, out, cap)
277 // ⚠RFC 2202 EXPRESSES BULK DATA AS PROSE: `data = 0xdd repeated 50 times`. That is ONE hex byte
278 // followed by a repetition count in ENGLISH. A literal hex reader returns 1 byte, and the
279 // document-declared data_len=50 is what caught it -- the self-check named the READER instead of
280 // letting HMAC be blamed for a 1-byte input.
281 // ★A value can be expressed as a PROGRAM ("repeat this"), not just as data. Tenth notation form.
282 if got == 1 {
283 let rp: i64 = findfrom(b, n, "repeated" as *u8, p)
284 if rp >= 0 { if rp < p + 40 {
285 let cnt: i64 = readdec(b, n, rp + 8)
286 if cnt > 1 { if cnt <= cap {
287 let fill: i64 = out[0] as i64
288 var q: i64 = 0
289 while q < cnt { out[q] = fill as u8; q = q + 1 }
290 return cnt
291 } }
292 } }
293 }
294 return got
295 } } }
296 return 0 - 1
297}
298
299// REFERENCE HMAC-SHA-1 BUILT HERE FROM THE EXONERATED sha1() ONLY.
300// RFC 2104: HMAC(K,m) = H((K' xor opad) || H((K' xor ipad) || m)), K' = K padded to 64 (hashed first if
301// longer). This shares ONLY the hash core with hmac_sha1 -- and that core is independently PROVEN correct
302// against RFC 3174. So if THIS matches the published digest and hmac_sha1 does not, the wrapper is at
303// fault; if BOTH miss, the reader is. ★A differential is only worth running when its shared set excludes
304// the suspect -- round 53's did not, this one does.
305// print one byte as two lowercase hex chars -- for the measured failure dump.
306func ph(v: i64) -> i64 {
307 let t: *u8 = sys_mmap(8)
308 t[0] = hexnib((v / 16) & 15) as u8
309 t[1] = hexnib(v & 15) as u8
310 sys_write(1, t, 2)
311 return 0
312}
313
314func ref_hmac_sha1(key: *u8, klen: i64, msg: *u8, mlen: i64, out: *u8) -> i64 {
315 let kp: *u8 = sys_mmap(128)
316 var i: i64 = 0
317 while i < 64 { kp[i] = 0 as u8; i = i + 1 }
318 if klen > 64 {
319 let kh: *u8 = sys_mmap(64)
320 sha1(key, klen, kh)
321 i = 0
322 while i < 20 { kp[i] = kh[i]; i = i + 1 }
323 } else {
324 i = 0
325 while i < klen { kp[i] = key[i]; i = i + 1 }
326 }
327 let inner: *u8 = sys_mmap(4096)
328 i = 0
329 while i < 64 { inner[i] = (((kp[i] as i64) ^ 54) & 255) as u8; i = i + 1 }
330 i = 0
331 while i < mlen { inner[64 + i] = msg[i]; i = i + 1 }
332 let ih: *u8 = sys_mmap(64)
333 sha1(inner, 64 + mlen, ih)
334 let outer: *u8 = sys_mmap(256)
335 i = 0
336 while i < 64 { outer[i] = (((kp[i] as i64) ^ 92) & 255) as u8; i = i + 1 }
337 i = 0
338 while i < 20 { outer[64 + i] = ih[i]; i = i + 1 }
339 sha1(outer, 84, out)
340 return 0
341}
342func main() -> i64 {
343 w("nx_hmacsha1fix_extvec_gate -- HMAC-SHA-1 (RFC 2104 construction over nx_sha1.nx) vs RFC 2202, READ FROM THE FETCHED DOCUMENT\n" as *u8)
344 let lp: *i64 = sys_mmap(16) as *i64
345 lp[0] = 0
346 let b: *u8 = sys_read_file("knowledge/extvec/rfc2202.txt\x00" as *u8, lp)
347 if lp[0] <= 0 { w("RED: fetched vector file absent.\n" as *u8); return 1 }
348 let ctx: *u8 = sys_mmap(1024)
349 let dg: *u8 = sys_mmap(64)
350 nx_sha256_one_shot(b, lp[0], ctx, dg)
351 let hx: *u8 = sys_mmap(80)
352 var i: i64 = 0
353 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 }
354 let wnt: *u8 = "c19effeca47e801be304460da3b2fb05f595e2309abceb050e40e024868fe483\x00" as *u8
355 var pin: i64 = 1
356 i = 0
357 while i < 64 { if hx[i] != wnt[i] { pin = 0 } i = i + 1 }
358 w(" acquisition digest: " as *u8); wb(hx, 64); w("\n" as *u8)
359 if pin == 0 { w("RED: PIN FAILED.\n" as *u8); return 1 }
360 w(" PIN OK -- bytes match the digest computed in-process at the socket\n" as *u8)
361
362 let key: *u8 = sys_mmap(512)
363 let data: *u8 = sys_mmap(512)
364 let exp: *u8 = sys_mmap(128)
365 let got: *u8 = sys_mmap(128)
366
367 var pass: i64 = 0
368 var fail: i64 = 0
369 var seen: i64 = 0
370 var skipped: i64 = 0
371 // ANCHOR ON THE SECTION, NOT ON A DIGEST-LENGTH GUESS. RFC 2202 has 7 HMAC-MD5 cases (sec 2) and
372 // NINE HMAC-SHA-1 markers (sec 3 -- cases 6 and 7 appear TWICE, once with longer data). Dispatching by
373 // digest length mis-sorted them 8/8 against the true 7/9. Starting at the section heading makes every
374 // case found a SHA-1 case BY CONSTRUCTION -- no inference required.
375 let sec: i64 = findfrom(b, lp[0], "3. Test Cases for HMAC-SHA-1" as *u8, 0)
376 if sec < 0 { w("RED: no HMAC-SHA-1 section heading\n" as *u8); return 1 }
377 var cur: i64 = sec
378 var done: i64 = 0
379 while done == 0 {
380 let tc: i64 = findfrom(b, lp[0], "test_case =" as *u8, cur)
381 if tc < 0 { done = 1 }
382 else {
383 let lk: i64 = findfrom(b, lp[0], "key =" as *u8, tc)
384 let lkl: i64 = findfrom(b, lp[0], "key_len" as *u8, tc)
385 let ld: i64 = findfrom(b, lp[0], "data =" as *u8, tc)
386 let ldl: i64 = findfrom(b, lp[0], "data_len" as *u8, tc)
387 // BOUND EVERY FIELD TO ITS OWN CASE. Fields were searched forward from `test_case =` with no
388 // upper limit, so a case missing a field silently borrowed the NEXT case's. Symptom: two
389 // datalen=73 cases (RFC 2202 sec 3 duplicates 6 and 7) -- IDENTICAL inputs, one PASS one FAIL,
390 // which no crypto hypothesis can explain and which pins it to pairing.
391 // ★Same shape as the lk>lm guard that saved RFC 4231: a field belonging to another record is
392 // not a missing field, it is a WRONG field, and only a bound can tell them apart.
393 let nt: i64 = findfrom(b, lp[0], "test_case =" as *u8, tc + 11)
394 let lg: i64 = findfrom(b, lp[0], "digest =" as *u8, tc)
395 // ⚠⚠MY OWN PREVIOUS EDIT MALFORMED THIS CHAIN: adding the boundary check as an `else` left TWO
396 // `else` clauses hanging off `if lg < 0`. It COMPILED -- nx_cc bound the second one somewhere I
397 // did not intend -- and the gate kept printing plausible per-case verdicts, so nothing looked
398 // wrong. ★★★★★A CONTROL-FLOW BUG THAT STILL PRINTS PLAUSIBLE OUTPUT IS AN INSTRUMENT THAT LIES;
399 // AN EDIT THAT COMPILES IS NOT AN EDIT THAT PARSED THE WAY YOU READ IT.
400 // ★Rule 3: this was the second patch to this loop, so the chain is RESTRUCTURED into flat,
401 // unambiguous guards rather than patched a third time. No nested else, nothing to mis-bind.
402 var bad: i64 = 0
403 if lk < 0 { bad = 1 }
404 if lg < 0 { bad = 1 }
405 if nt >= 0 {
406 if lg > nt {
407 w(" RED: case field pairing crossed a case boundary -- the READER.\n" as *u8)
408 fail = fail + 1
409 bad = 1
410 }
411 }
412 if bad == 1 { done = 1 }
413 else {
414 let kn: i64 = readfield(b, lp[0], lk + 5, key, 400)
415 let dn: i64 = readfield(b, lp[0], ld + 6, data, 400)
416 let kdecl: i64 = readdec_skipeq(b, lp[0], lkl + 7)
417 let ddecl: i64 = readdec_skipeq(b, lp[0], ldl + 8)
418 let gn: i64 = readfield(b, lp[0], lg + 8, exp, 100)
419 if gn < 0 { done = 1 }
420 else {
421 // THE DOCUMENT DECLARES ITS OWN LENGTHS -- grade the reader by the source.
422 if kn != kdecl {
423 w(" RED: key parsed " as *u8); nn(kn); w(" but document declares key_len=" as *u8); nn(kdecl)
424 w(" -- the READER.\n" as *u8)
425 fail = fail + 1
426 done = 1
427 } else { if dn != ddecl {
428 w(" RED: data parsed " as *u8); nn(dn); w(" but document declares data_len=" as *u8); nn(ddecl)
429 w(" -- the READER.\n" as *u8)
430 fail = fail + 1
431 done = 1
432 } else {
433 // dispatch on the PUBLISHED digest length: 20 = SHA-1, 16 = the MD5 section
434 if gn > 0 {
435 ref_hmac_sha1(key, kn, data, dn, got)
436 // DIFFERENTIAL: HKDF-Extract(salt, ikm) IS DEFINED AS HMAC-Hash(salt, ikm)
437 // (RFC 5869 sec 2.2), so hkdf_sha1_extract is an INDEPENDENT internal path to
438 // the same function. If one matches the published vector and the other does
439 // not, the defect is localised to the one that disagrees -- adjudicated by the
440 // AUTHORITY, not by which of our two implementations we happen to trust.
441 let alt: *u8 = sys_mmap(64)
442 ref_hmac_sha1(key, kn, data, dn, alt)
443 var altsame: i64 = 1
444 var z: i64 = 0
445 while z < gn { if alt[z] != exp[z] { altsame = 0 } z = z + 1 }
446 // ⚠⚠RETRACTED, AND THE RETRACTION IS THE POINT. This block PRINTED
447 // "[differential] ... -> the WRAPPER is at fault" / "-> crypto exonerated" on
448 // every case, and its comment claimed hkdf_sha1_extract as an independent second
449 // path. THE CODE NEVER DID THAT: lines above call ref_hmac_sha1 twice with
450 // IDENTICAL arguments, so `alt` and `got` are the same computation and altsame
451 // could never disagree with same. It was a MIRROR, not a second opinion, and it
452 // confidently attributed blame it had no information about -- it told me "crypto
453 // exonerated, the READER is at fault" on the failing case, which happened to be
454 // TRUE BY LUCK while carrying zero evidence.
455 // ★★★★★A DIFFERENTIAL THAT CALLS THE SAME FUNCTION TWICE IS A MIRROR, NOT A
456 // SECOND OPINION -- AND A COMMENT CLAIMING INDEPENDENCE THE CODE DOES NOT
457 // IMPLEMENT IS THE INSTRUMENT LYING IN ITS OWN DOCUMENTATION.
458 // Restoring a REAL differential needs a path sharing no core with ref_hmac_sha1;
459 // until one exists, this gate states its single-path scope instead of faking two.
460 if altsame == 1 { w(" [single-path] matches published digest (NO independent second path -- see note)\n" as *u8) }
461 else { w(" [single-path] differs from published digest\n" as *u8) }
462 // Compare only the PUBLISHED digest length: section 3 case 5 is the truncation
463 // case, whose published digest is SHORTER than HMAC-SHA-1's 20-byte output.
464 // Comparing a fixed 20 would fail a correct implementation on that case.
465 var same: i64 = 1
466 i = 0
467 while i < gn { if got[i] != exp[i] { same = 0 } i = i + 1 }
468 seen = seen + 1
469 if same == 1 { pass = pass + 1; w(" PASS sha1 keylen=" as *u8); nn(kn); w(" datalen=" as *u8); nn(dn); w(" maclen=" as *u8); nn(gn); w("\n" as *u8) }
470 else {
471 fail = fail + 1
472 w(" FAIL sha1 case keylen=" as *u8); nn(kn); w(" datalen=" as *u8); nn(dn); w("\n" as *u8)
473 // ★MEASURE, DO NOT INFER. I hypothesised this failure four separate ways --
474 // case-boundary pairing, chunk framing, a short inner buffer, a wrong hash --
475 // and every one was refuted. Printing the two digests and the byte offset the
476 // expected one was read from settles it in a single run.
477 w(" expected(read at offset " as *u8); nn(lg); w(") = " as *u8)
478 var z2: i64 = 0
479 while z2 < gn { ph(exp[z2] as i64); z2 = z2 + 1 }
480 w("\n computed = " as *u8)
481 z2 = 0
482 while z2 < gn { ph(got[z2] as i64); z2 = z2 + 1 }
483 w("\n first 40 bytes of source at that offset: " as *u8)
484 wb(((b as i64) + lg) as *u8, 40)
485 w("\n" as *u8)
486 }
487 } else { skipped = skipped + 1 }
488 cur = lg + 8
489 } }
490 }
491 }
492 }
493 }
494
495 w("\n refsrc=https://www.rfc-editor.org/rfc/rfc2202.txt\n" as *u8)
496 w(" refsrcdig=" as *u8); wb(hx, 64); w("\n" as *u8)
497 w(" ref=RFC2202-HMAC-SHA1 gate=nx_hmacsha1fix_extvec_gate\n" as *u8)
498 w(" NOTE: key_len and data_len are cross-checked against the document on EVERY case.\n" as *u8)
499 w(" 16-byte-digest cases are the HMAC-MD5 section and are counted as SKIPPED, not passed.\n" as *u8)
500 w("nx_hmacsha1fix_extvec_gate: sha1_cases=" as *u8); nn(seen); w(" skipped_md5=" as *u8); nn(skipped)
501 w(" pass=" as *u8); nn(pass); w(" fail=" as *u8); nn(fail)
502 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
503 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
504 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
505 let ctr__dry: *i64 = gv_ctr()
506 ctr__dry[0] = pass
507 ctr__dry[1] = pass + fail
508 let rc__dry: i64 = gv_verdict("HMACSHA1FIX-EXTVEC-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
509 sys_exit(rc__dry)
510 return rc__dry
511}