code wiki / _hdl_build / nx_chacha20enc_extvec_gate.nx
nx_chacha20enc_extvec_gate.nx source
↩ module page · 237 lines · 9810 B
1// nx_chacha20enc_extvec_gate.nx -- SIXTH provably third-party-validated claim: the ChaCha20 CIPHER
2// (not just the block function) vs RFC 8439 2.4.2, from the same pinned document.
3//
4// ★THIS IS THE SECTION THAT BROKE THE PREVIOUS PARSER, AND THAT IS WHY IT IS WORTH DOING. 2.4.2's ASCII
5// gutter is ENGLISH -- "...would be it." -- and `be` is a whitespace-delimited, exactly-two-character,
6// ALL-HEX token. The content-classifying reader I first shipped would absorb it as byte 0xbe and shift
7// every byte after it. The LINE-BOUNDED reader used here takes token 0 as the offset, tokens 1..16 as
8// data, and ignores the rest BY POSITION, so the gutter cannot contribute no matter what it spells.
9// ★★★★LAW: A DISCRIMINATOR PROVEN ON ONE INSTANCE OF A DIALECT IS NOT PROVEN ON THE DIALECT.
10//
11// ★STRONGER THAN THE POLY1305 GATE: there, the message was ASCII prose so I supplied the input and let the
12// published tag verify it. Here BOTH the plaintext AND the ciphertext are hexdumps in the document, so
13// every byte on both sides of the comparison is read from the authority. Nothing is agent-supplied.
14// license_tier: ORIGINAL expect_exit: 0
15import "nx_syscalls.nx"
16import "nx_sha256_wasm.nx"
17import "nx_chacha20.nx"
18import "nx_gate_verdict.nx"
19
20func w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
21func wb(b: *u8, n: i64) -> i64 { sys_write(1, b, n); return 0 }
22
23func nn(v: i64) -> i64 {
24 var m: i64 = v
25 if m < 0 { w("-" as *u8); m = 0 - m }
26 let t: *u8 = sys_mmap(32)
27 var k: i64 = 0
28 if m == 0 { t[0] = 48 as u8; k = 1 }
29 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
30 let b: *u8 = sys_mmap(32)
31 var j: i64 = 0
32 while j < k { b[j] = t[k - 1 - j]; j = j + 1 }
33 sys_write(1, b, k)
34 return 0
35}
36
37func hexnib(v: i64) -> i64 { if v < 10 { return 48 + v } return 87 + v }
38
39func hexval(c: i64) -> i64 {
40 if c >= 48 { if c <= 57 { return c - 48 } }
41 if c >= 97 { if c <= 102 { return c - 87 } }
42 if c >= 65 { if c <= 70 { return c - 55 } }
43 return 0 - 1
44}
45
46func isws(c: i64) -> i64 {
47 if c == 32 { return 1 }
48 if c == 10 { return 1 }
49 if c == 13 { return 1 }
50 if c == 9 { return 1 }
51 return 0
52}
53
54func starts(b: *u8, n: i64, at: i64, s: *u8) -> i64 {
55 var i: i64 = 0
56 while s[i] != (0 as u8) {
57 if at + i >= n { return 0 }
58 if b[at + i] != s[i] { return 0 }
59 i = i + 1
60 }
61 return 1
62}
63
64func findfrom(b: *u8, n: i64, s: *u8, from: i64) -> i64 {
65 var p: i64 = from
66 while p < n {
67 if starts(b, n, p, s) == 1 { return p }
68 p = p + 1
69 }
70 return 0 - 1
71}
72
73func parsenib(b: *u8, n: i64, from: i64, out: *u8, want: i64) -> i64 {
74 var p: i64 = from
75 var got: i64 = 0
76 var have: i64 = 0
77 var hi: i64 = 0
78 while p < n {
79 if got >= want { return p }
80 let c: i64 = b[p] as i64
81 var sep: i64 = 0
82 if c == 58 { sep = 1 }
83 if isws(c) == 1 { sep = 1 }
84 if sep == 1 { p = p + 1 }
85 else {
86 let hv: i64 = hexval(c)
87 if hv < 0 {
88 if got > 0 { if got < want { got = 0; have = 0 } }
89 p = p + 1
90 } else {
91 if have == 0 { hi = hv; have = 1 } else { out[got] = ((hi * 16) + hv) as u8; got = got + 1; have = 0 }
92 p = p + 1
93 }
94 }
95 }
96 if got >= want { return p }
97 return 0 - 1
98}
99
100// LINE-BOUNDED hexdump reader: token 0 = offset, tokens 1..16 = data, remainder = gutter, ignored BY
101// POSITION. Immune to an ASCII gutter that happens to spell hex ("be", "ad", "de", "fa").
102func parsedump(b: *u8, n: i64, from: i64, out: *u8, want: i64) -> i64 {
103 var p: i64 = from
104 var got: i64 = 0
105 var dz: i64 = 0
106 while dz == 0 {
107 if p >= n { dz = 1 }
108 else { if b[p] == (10 as u8) { p = p + 1; dz = 1 } else { p = p + 1 } }
109 }
110 var tok: i64 = 0
111 while p < n {
112 if got >= want { return p }
113 var d1: i64 = 0
114 while d1 == 0 {
115 if p >= n { d1 = 1 }
116 else {
117 if b[p] == (10 as u8) { tok = 0; p = p + 1 }
118 else { if isws(b[p] as i64) == 1 { p = p + 1 } else { d1 = 1 } }
119 }
120 }
121 if p >= n { return 0 - 1 }
122 var end: i64 = p
123 var d2: i64 = 0
124 while d2 == 0 {
125 if end >= n { d2 = 1 }
126 else { if isws(b[end] as i64) == 1 { d2 = 1 } else { end = end + 1 } }
127 }
128 let len: i64 = end - p
129 if tok >= 1 { if tok <= 16 { if len == 2 {
130 let h1: i64 = hexval(b[p] as i64)
131 let h2: i64 = hexval(b[p + 1] as i64)
132 if h1 >= 0 { if h2 >= 0 { out[got] = ((h1 * 16) + h2) as u8; got = got + 1 } }
133 } } }
134 tok = tok + 1
135 p = end
136 }
137 if got >= want { return p }
138 return 0 - 1
139}
140
141func main() -> i64 {
142 w("nx_chacha20enc_extvec_gate -- ChaCha20 cipher vs RFC 8439 2.4.2, READ FROM THE FETCHED DOCUMENT\n" as *u8)
143
144 let lp: *i64 = sys_mmap(16) as *i64
145 lp[0] = 0
146 let b: *u8 = sys_read_file("knowledge/extvec/rfc8439.txt\x00" as *u8, lp)
147 if lp[0] <= 0 { w("RED: fetched vector file absent -- run nx_vecfetch.\n" as *u8); return 1 }
148
149 let ctx: *u8 = sys_mmap(1024)
150 let dig: *u8 = sys_mmap(64)
151 nx_sha256_one_shot(b, lp[0], ctx, dig)
152 let hx: *u8 = sys_mmap(80)
153 var i: i64 = 0
154 while i < 32 {
155 hx[i * 2] = hexnib(((dig[i] as i64) / 16) & 15) as u8
156 hx[i * 2 + 1] = hexnib((dig[i] as i64) & 15) as u8
157 i = i + 1
158 }
159 let want: *u8 = "25bef70fbf7a07ff45c2fe4cb7c6ce954eac687413d8610603268b4e4415324c\x00" as *u8
160 var pin: i64 = 1
161 i = 0
162 while i < 64 { if hx[i] != want[i] { pin = 0 } i = i + 1 }
163 w(" acquisition digest: " as *u8); wb(hx, 64); w("\n" as *u8)
164 if pin == 0 { w("RED: PIN FAILED.\n" as *u8); return 1 }
165 w(" PIN OK -- bytes match the digest computed in-process at the socket\n" as *u8)
166
167 let sec1: i64 = findfrom(b, lp[0], "2.4.2. Example and Test Vector for the ChaCha20 Cipher" as *u8, 0)
168 if sec1 < 0 { w("RED: no section 2.4.2\n" as *u8); return 1 }
169 let sec2: i64 = findfrom(b, lp[0], "2.4.2. Example and Test Vector for the ChaCha20 Cipher" as *u8, sec1 + 10)
170 var at: i64 = sec1
171 if sec2 >= 0 { at = sec2 }
172
173 let lk: i64 = findfrom(b, lp[0], "Key = " as *u8, at)
174 let key: *u8 = sys_mmap(64)
175 if lk < 0 { w("RED: no Key\n" as *u8); return 1 }
176 if parsenib(b, lp[0], lk + 6, key, 32) < 0 { w("RED: key short\n" as *u8); return 1 }
177
178 let ln: i64 = findfrom(b, lp[0], "Nonce = " as *u8, at)
179 let nonce: *u8 = sys_mmap(32)
180 if ln < 0 { w("RED: no Nonce\n" as *u8); return 1 }
181 if parsenib(b, lp[0], ln + 8, nonce, 12) < 0 { w("RED: nonce short\n" as *u8); return 1 }
182
183 let PT: i64 = 114
184 let lpt: i64 = findfrom(b, lp[0], "Plaintext Sunscreen:" as *u8, at)
185 let pt: *u8 = sys_mmap(256)
186 if lpt < 0 { w("RED: no 'Plaintext Sunscreen:'\n" as *u8); return 1 }
187 if parsedump(b, lp[0], lpt + 20, pt, PT) < 0 { w("RED: plaintext dump short\n" as *u8); return 1 }
188
189 let lct: i64 = findfrom(b, lp[0], "Ciphertext Sunscreen:" as *u8, at)
190 let ct: *u8 = sys_mmap(256)
191 if lct < 0 { w("RED: no 'Ciphertext Sunscreen:'\n" as *u8); return 1 }
192 if parsedump(b, lp[0], lct + 21, ct, PT) < 0 { w("RED: ciphertext dump short\n" as *u8); return 1 }
193
194 // Sanity on the parse itself: the plaintext must begin with "Lad" (0x4c 0x61 0x64). If the gutter had
195 // leaked into the data this check fires before any crypto is blamed.
196 var parse_ok: i64 = 0
197 if pt[0] == (76 as u8) { if pt[1] == (97 as u8) { if pt[2] == (100 as u8) { parse_ok = 1 } } }
198 if parse_ok == 0 {
199 w("RED: PARSE SELF-CHECK FAILED -- plaintext does not start with 'Lad'; the reader, not ChaCha20.\n" as *u8)
200 return 1
201 }
202 w(" parse self-check OK -- plaintext begins 'Lad' (gutter did not leak into the data)\n" as *u8)
203
204 let out: *u8 = sys_mmap(256)
205 chacha20_encrypt(key, 1, nonce, pt, PT, out)
206
207 var pass: i64 = 0
208 var fail: i64 = 0
209 var same: i64 = 1
210 i = 0
211 while i < PT { if out[i] != ct[i] { same = 0 } i = i + 1 }
212 if same == 1 { pass = pass + 1; w(" PASS T1: chacha20_encrypt over 114 bytes == the document's Ciphertext Sunscreen\n" as *u8) }
213 else { fail = fail + 1; w(" FAIL T1: ciphertext mismatch\n" as *u8) }
214
215 // NEGATIVE CONTROL: wrong initial counter must produce a different ciphertext.
216 chacha20_encrypt(key, 2, nonce, pt, PT, out)
217 var same2: i64 = 1
218 i = 0
219 while i < PT { if out[i] != ct[i] { same2 = 0 } i = i + 1 }
220 if same2 == 0 { pass = pass + 1; w(" PASS NEG: counter=2 yields different ciphertext (the check can fail)\n" as *u8) }
221 else { fail = fail + 1; w(" FAIL NEG: counter change did not alter the ciphertext\n" as *u8) }
222
223 w("\n refsrc=https://www.rfc-editor.org/rfc/rfc8439.txt\n" as *u8)
224 w(" refsrcdig=" as *u8); wb(hx, 64); w("\n" as *u8)
225 w(" ref=RFC8439-2.4.2 gate=nx_chacha20enc_extvec_gate\n" as *u8)
226 w(" NOTE: both plaintext AND ciphertext read from the document -- nothing agent-supplied.\n" as *u8)
227 w("nx_chacha20enc_extvec_gate: pass=" as *u8); nn(pass); w(" fail=" as *u8); nn(fail)
228 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
229 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
230 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
231 let ctr__dry: *i64 = gv_ctr()
232 ctr__dry[0] = pass
233 ctr__dry[1] = pass + fail
234 let rc__dry: i64 = gv_verdict("CHACHA20ENC-EXTVEC-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
235 sys_exit(rc__dry)
236 return rc__dry
237}