code wiki / _hdl_build / nx_md5_extvec_gate.nx
nx_md5_extvec_gate.nx source
↩ module page · 207 lines · 8726 B
1// nx_md5_extvec_gate.nx -- TENTH provably third-party-validated claim: MD5 vs the RFC 1321 test suite.
2//
3// ⚠TWO OF THE SEVEN VECTORS WRAP ACROSS LINES, AND ONE OF THEM WRAPS *INSIDE THE QUOTED INPUT*:
4// MD5 ("123456789012345678901234567890123456789012345678901234567890123456
5// 78901234567890") = 57edf4a22be3c955ac49da2e2107b67a
6// A naive "copy bytes until the closing quote" reader would put a newline INTO the message, hash the wrong
7// bytes, and report a broken MD5. So this reader SKIPS \n and \r while copying a quoted value.
8// ★THAT IS AN ASSUMPTION ABOUT THIS SECTION, AND IT IS STATED RATHER THAN HIDDEN: the RFC 1321 test inputs
9// are printable ASCII containing no literal newline, so dropping newlines restores exactly the intended
10// message. It would be WRONG for any document whose test inputs legitimately contain newlines -- do not
11// lift this reader to such a source without re-proving it.
12// ★Sixth vector dialect of the session. The lesson has not changed once: there is no general parser, only
13// one proven against the section in front of you.
14//
15// Construction identical to the other nine: no expected value in this source, document pinned to a digest
16// nx_vecfetch computed IN-PROCESS AT THE SOCKET, inputs AND digests read from that pinned document, and a
17// completeness check so a partial read cannot print GREEN.
18// license_tier: ORIGINAL expect_exit: 0
19import "nx_syscalls.nx"
20import "nx_sha256_wasm.nx"
21import "nx_md5_canonical.nx"
22import "nx_gate_verdict.nx"
23
24func w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
25func wb(b: *u8, n: i64) -> i64 { sys_write(1, b, n); return 0 }
26
27func nn(v: i64) -> i64 {
28 var m: i64 = v
29 if m < 0 { w("-" as *u8); m = 0 - m }
30 let t: *u8 = sys_mmap(32)
31 var k: i64 = 0
32 if m == 0 { t[0] = 48 as u8; k = 1 }
33 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
34 let b: *u8 = sys_mmap(32)
35 var j: i64 = 0
36 while j < k { b[j] = t[k - 1 - j]; j = j + 1 }
37 sys_write(1, b, k)
38 return 0
39}
40
41func hexnib(v: i64) -> i64 { if v < 10 { return 48 + v } return 87 + v }
42
43func hexval(c: i64) -> i64 {
44 if c >= 48 { if c <= 57 { return c - 48 } }
45 if c >= 97 { if c <= 102 { return c - 87 } }
46 if c >= 65 { if c <= 70 { return c - 55 } }
47 return 0 - 1
48}
49
50func isws(c: i64) -> i64 {
51 if c == 32 { return 1 }
52 if c == 10 { return 1 }
53 if c == 13 { return 1 }
54 if c == 9 { return 1 }
55 return 0
56}
57
58func starts(b: *u8, n: i64, at: i64, s: *u8) -> i64 {
59 var i: i64 = 0
60 while s[i] != (0 as u8) {
61 if at + i >= n { return 0 }
62 if b[at + i] != s[i] { return 0 }
63 i = i + 1
64 }
65 return 1
66}
67
68func findfrom(b: *u8, n: i64, s: *u8, from: i64) -> i64 {
69 var p: i64 = from
70 while p < n {
71 if starts(b, n, p, s) == 1 { return p }
72 p = p + 1
73 }
74 return 0 - 1
75}
76
77// Copy a quoted value, DROPPING the document's line wraps (\n, \r). Empty is valid: MD5 ("") is vector 1.
78func parseq(b: *u8, n: i64, from: i64, out: *u8, cap: i64, endout: *i64) -> i64 {
79 var p: i64 = from
80 var k: i64 = 0
81 while p < n {
82 if b[p] == (34 as u8) { endout[0] = p; return k }
83 if b[p] == (10 as u8) { p = p + 1 }
84 else { if b[p] == (13 as u8) { p = p + 1 }
85 else {
86 if k >= cap { return 0 - 1 }
87 out[k] = b[p]
88 k = k + 1
89 p = p + 1
90 } }
91 }
92 return 0 - 1
93}
94
95// Read `want` bytes of hex; whitespace continues the run (the digest may sit on the following line).
96func parsehex(b: *u8, n: i64, from: i64, out: *u8, want: i64, endout: *i64) -> i64 {
97 var p: i64 = from
98 var got: i64 = 0
99 while p < n {
100 if got >= want { endout[0] = p; return got }
101 let c: i64 = b[p] as i64
102 if isws(c) == 1 { p = p + 1 }
103 else {
104 let h1: i64 = hexval(c)
105 if h1 < 0 { return 0 - 1 }
106 if p + 1 >= n { return 0 - 1 }
107 let h2: i64 = hexval(b[p + 1] as i64)
108 if h2 < 0 { return 0 - 1 }
109 out[got] = ((h1 * 16) + h2) as u8
110 got = got + 1
111 p = p + 2
112 }
113 }
114 if got >= want { endout[0] = p; return got }
115 return 0 - 1
116}
117
118func main() -> i64 {
119 w("nx_md5_extvec_gate -- MD5 vs the RFC 1321 test suite, READ FROM THE FETCHED DOCUMENT\n" as *u8)
120
121 let lp: *i64 = sys_mmap(16) as *i64
122 lp[0] = 0
123 let b: *u8 = sys_read_file("knowledge/extvec/rfc1321.txt\x00" as *u8, lp)
124 if lp[0] <= 0 { w("RED: fetched vector file absent -- run nx_vecfetch.\n" as *u8); return 1 }
125
126 let ctx: *u8 = sys_mmap(1024)
127 let dg: *u8 = sys_mmap(64)
128 nx_sha256_one_shot(b, lp[0], ctx, dg)
129 let hx: *u8 = sys_mmap(80)
130 var i: i64 = 0
131 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 }
132 let wnt: *u8 = "284a79d148400d9cd2a423211d1103b5cef0fb9256a4cbe6d7ebe5197c3149dd\x00" as *u8
133 var pin: i64 = 1
134 i = 0
135 while i < 64 { if hx[i] != wnt[i] { pin = 0 } i = i + 1 }
136 w(" acquisition digest: " as *u8); wb(hx, 64); w("\n" as *u8)
137 if pin == 0 { w("RED: PIN FAILED -- not the file nx_vecfetch acquired.\n" as *u8); return 1 }
138 w(" PIN OK -- bytes match the digest computed in-process at the socket\n" as *u8)
139
140 let at: i64 = findfrom(b, lp[0], "MD5 test suite:" as *u8, 0)
141 if at < 0 { w("RED: no 'MD5 test suite:' block\n" as *u8); return 1 }
142
143 let inb: *u8 = sys_mmap(256)
144 let exp: *u8 = sys_mmap(64)
145 let got: *u8 = sys_mmap(64)
146 let ep: *i64 = sys_mmap(16) as *i64
147
148 var pass: i64 = 0
149 var fail: i64 = 0
150 var seen: i64 = 0
151 var cur: i64 = at
152 var done: i64 = 0
153 while done == 0 {
154 let h: i64 = findfrom(b, lp[0], "MD5 (\"" as *u8, cur)
155 if h < 0 { done = 1 }
156 else {
157 ep[0] = 0
158 let inlen: i64 = parseq(b, lp[0], h + 6, inb, 250, ep)
159 if inlen < 0 { done = 1 }
160 else {
161 // The separator is "=" ALONE, not "= ". Vector 6 prints ") =" then a NEWLINE before its
162 // digest, so matching "= " skipped it, ran on, and stole VECTOR 7's digest -- corrupting
163 // one vector and consuming another. One wrong separator, two symptoms.
164 // The completeness check (7 published) is what surfaced the swallowed vector; without it
165 // this gate would have reported 6 parsed / 5 passed and looked merely "mostly fine".
166 let q: i64 = findfrom(b, lp[0], "=" as *u8, ep[0])
167 if q < 0 { done = 1 }
168 else {
169 ep[0] = 0
170 if parsehex(b, lp[0], q + 1, exp, 16, ep) < 0 { done = 1 }
171 else {
172 md5(inb, inlen, got)
173 var same: i64 = 1
174 i = 0
175 while i < 16 { if got[i] != exp[i] { same = 0 } i = i + 1 }
176 seen = seen + 1
177 if same == 1 { pass = pass + 1; w(" PASS MD5 len=" as *u8); nn(inlen); w("\n" as *u8) }
178 else { fail = fail + 1; w(" FAIL MD5 len=" as *u8); nn(inlen); w(" digest mismatch\n" as *u8) }
179 cur = ep[0]
180 }
181 }
182 }
183 }
184 }
185
186 // RFC 1321 publishes SEVEN vectors. Grading fewer while printing GREEN is coverage-gaming.
187 if seen < 7 {
188 w(" RED: only " as *u8); nn(seen); w(" of 7 published vectors parsed -- refusing GREEN on a partial read.\n" as *u8)
189 fail = fail + 1
190 }
191
192 w("\n refsrc=https://www.rfc-editor.org/rfc/rfc1321.txt\n" as *u8)
193 w(" refsrcdig=" as *u8); wb(hx, 64); w("\n" as *u8)
194 w(" ref=RFC1321-test-suite gate=nx_md5_extvec_gate\n" as *u8)
195 w(" NOTE: MD5 is cryptographically BROKEN for collision resistance. This gate proves CONFORMANCE to\n" as *u8)
196 w(" the published digest values only -- it is not an endorsement of MD5 for any security use.\n" as *u8)
197 w("nx_md5_extvec_gate: vectors=" as *u8); nn(seen); w(" pass=" as *u8); nn(pass); w(" fail=" as *u8); nn(fail)
198 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
199 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
200 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
201 let ctr__dry: *i64 = gv_ctr()
202 ctr__dry[0] = pass
203 ctr__dry[1] = pass + fail
204 let rc__dry: i64 = gv_verdict("MD5-EXTVEC-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
205 sys_exit(rc__dry)
206 return rc__dry
207}