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}