nx_ecdsa_p384_real_bisect_test.nx source
↩ module page · 286 lines · 10644 B
1// nx_ecdsa_p384_real_bisect_test.nx -- bisect ECDSA-P384 verify
2// pipeline against the cert2 -> cert3 pair of the real example.com
3// chain to locate the root cause of VUI_VERIFY_FAIL (verdict 6).
4//
5// Steps (each value is compared inline with a golden hex computed
6// off-substrate via Python cryptography):
7// 1. SHA-384(cert2.tbs) -- 48 bytes
8// 2. nx_ecdsa_sig_parse_der_384(cert2.sig) -- r, s (12 LE limbs each)
9// 3. nx_x509_pubkey_extract_ec_p384(cert3) -- pub_x, pub_y
10//
11// All bytes are dumped as hex on stderr so we can `diff` against the
12// Python golden. The test always exits 0; the smoke checks the
13// printed hex matches expected.
14//
15// expect_exit: 0.
16
17import "nx_syscalls.nx"
18import "nx_u384.nx"
19import "nx_x509.nx"
20import "nx_x509_pubkey_ec.nx"
21import "nx_sha512.nx"
22import "nx_ecdsa_sig_der_384.nx"
23import "nx_ecdsa_sig_der.nx"
24import "nx_ecdsa_p384.nx"
25import "nx_x509_verify_ecdsa_p384.nx"
26import "nx_x509_verify_under_issuer.nx"
27import "nx_u256.nx"
28import "nx_x509_verify_ecdsa.nx"
29import "nx_sha256.nx"
30
31func read_to(path: *u8, out_buf: *u8, max: i64) -> i64 {
32 let len_p: *i64 = sys_mmap(16) as *i64
33 let p: *u8 = sys_read_file(path, len_p)
34 let n: i64 = *len_p
35 if n <= 0 { return 0 - 1 }
36 if n > max { return 0 - 2 }
37 var i: i64 = 0
38 while i < n { out_buf[i] = p[i]; i = i + 1 }
39 return n
40}
41
42func emit_label(c0: i64, c1: i64, c2: i64, c3: i64, c4: i64) -> i64 {
43 let b: *u8 = sys_mmap(8)
44 b[0]=c0 as u8; b[1]=c1 as u8; b[2]=c2 as u8; b[3]=c3 as u8
45 b[4]=c4 as u8; b[5]=0x3D
46 sys_write(2, b, 6)
47 return 0
48}
49
50func emit_nl() -> i64 {
51 let nl: *u8 = sys_mmap(8); nl[0]=0x0A; sys_write(2, nl, 1)
52 return 0
53}
54
55func hexnib(v: i64) -> i64 {
56 if v < 10 { return 0x30 + v }
57 return 0x61 + (v - 10)
58}
59
60func dump_bytes(bytes: *u8, n: i64) -> i64 {
61 let buf: *u8 = sys_mmap(n * 2 + 4)
62 var i: i64 = 0
63 while i < n {
64 let v: i64 = bytes[i] & 0xff
65 buf[i*2] = hexnib(v >> 4) as u8
66 buf[i*2 + 1] = hexnib(v & 0xf) as u8
67 i = i + 1
68 }
69 sys_write(2, buf, n * 2)
70 return 0
71}
72
73// Dump U384 (12 LE limbs of 32 bits each) as 96-char big-endian hex.
74func dump_u384(p: *i64) -> i64 {
75 let bytes: *u8 = sys_mmap(48)
76 var i: i64 = 0
77 // U384 LE-limb layout: limb[0] = least-sig 32 bits.
78 // For BE hex we want byte 0 = MSB of limb[11].
79 while i < 12 {
80 let limb: i64 = p[11 - i] & 0xffffffff
81 bytes[i*4 + 0] = (limb >> 24) & 0xff
82 bytes[i*4 + 1] = (limb >> 16) & 0xff
83 bytes[i*4 + 2] = (limb >> 8) & 0xff
84 bytes[i*4 + 3] = limb & 0xff
85 i = i + 1
86 }
87 dump_bytes(bytes, 48)
88 return 0
89}
90
91func main() -> i64 {
92 // Load cert2.der and cert3.der.
93 let p2: *u8 = sys_mmap(64)
94 p2[0]=0x2F;p2[1]=0x74;p2[2]=0x6D;p2[3]=0x70;p2[4]=0x2F
95 p2[5]=0x63;p2[6]=0x65;p2[7]=0x72;p2[8]=0x74
96 p2[9]=0x32;p2[10]=0x2E;p2[11]=0x64;p2[12]=0x65;p2[13]=0x72
97 p2[14]=0
98 let buf2: *u8 = sys_mmap(4096)
99 let n2: i64 = read_to(p2, buf2, 4096)
100 if n2 <= 0 { return 1 }
101
102 let p3: *u8 = sys_mmap(64)
103 p3[0]=0x2F;p3[1]=0x74;p3[2]=0x6D;p3[3]=0x70;p3[4]=0x2F
104 p3[5]=0x63;p3[6]=0x65;p3[7]=0x72;p3[8]=0x74
105 p3[9]=0x33;p3[10]=0x2E;p3[11]=0x64;p3[12]=0x65;p3[13]=0x72
106 p3[14]=0
107 let buf3: *u8 = sys_mmap(4096)
108 let n3: i64 = read_to(p3, buf3, 4096)
109 if n3 <= 0 { return 2 }
110
111 let c2_raw: *u8 = sys_mmap(512); let c2: *X509Cert = c2_raw as *X509Cert
112 let c3_raw: *u8 = sys_mmap(512); let c3: *X509Cert = c3_raw as *X509Cert
113 if x509_parse(buf2, n2, c2) < 0 { return 3 }
114 if x509_parse(buf3, n3, c3) < 0 { return 4 }
115
116 // ---- 0. SHA-256(cert2.tbs) BEFORE any P-384 work, as a control ----
117 let h256_early: *u8 = sys_mmap(32)
118 sha256_digest(buf2 + c2.tbs_off, c2.tbs_len, h256_early)
119 emit_label(0x53, 0x32, 0x35, 0x36, 0x30) // "S2560" (early sha256)
120 dump_bytes(h256_early, 32); emit_nl()
121
122 // ---- 1. SHA-384(cert2.tbs) ----
123 let h: *u8 = sys_mmap(48)
124 let tbs2: *u8 = buf2 + c2.tbs_off
125 sha384_digest(tbs2, c2.tbs_len, h)
126 emit_label(0x48, 0x32, 0x53, 0x33, 0x38) // "H2S38"
127 dump_bytes(h, 48); emit_nl()
128
129 // ---- 2. parse cert2.sig as P-384 DER ----
130 let r: *i64 = u384_alloc()
131 let s: *i64 = u384_alloc()
132 let sig_ptr: *u8 = buf2 + c2.sig_off
133 let pp: i64 = nx_ecdsa_sig_parse_der_384(sig_ptr, c2.sig_len, r, s)
134 emit_label(0x44, 0x50, 0x52, 0x43, 0x32) // "DPRC2" (DER parse rc)
135 let prc_buf: *u8 = sys_mmap(8)
136 prc_buf[0] = hexnib(pp & 0xf) as u8
137 sys_write(2, prc_buf, 1); emit_nl()
138
139 emit_label(0x52, 0x32, 0x32, 0x32, 0x32) // "R2222"
140 dump_u384(r); emit_nl()
141 emit_label(0x53, 0x32, 0x32, 0x32, 0x32) // "S2222"
142 dump_u384(s); emit_nl()
143
144 // ---- 3. extract cert3 pubkey ----
145 let px: *i64 = u384_alloc()
146 let py: *i64 = u384_alloc()
147 let ex: i64 = nx_x509_pubkey_extract_ec_p384(buf3, c3, px, py)
148 emit_label(0x45, 0x58, 0x52, 0x43, 0x33) // "EXRC3"
149 let xrc_buf: *u8 = sys_mmap(8)
150 xrc_buf[0] = hexnib(ex & 0xf) as u8
151 sys_write(2, xrc_buf, 1); emit_nl()
152
153 emit_label(0x58, 0x33, 0x33, 0x33, 0x33) // "X3333"
154 dump_u384(px); emit_nl()
155 emit_label(0x59, 0x33, 0x33, 0x33, 0x33) // "Y3333"
156 dump_u384(py); emit_nl()
157
158 // ---- 4. Call nx_ecdsa_p384_verify directly with these inputs ----
159 let e: *i64 = u384_alloc()
160 u384_load_be(e, h)
161 let v: i64 = nx_ecdsa_p384_verify(px, py, e, r, s)
162 emit_label(0x56, 0x52, 0x44, 0x43, 0x54) // "VRDCT"
163 let v_buf: *u8 = sys_mmap(8)
164 v_buf[0] = hexnib(v & 0xf) as u8
165 sys_write(2, v_buf, 1); emit_nl()
166
167 // ---- 5. Call x509_verify_ecdsa_p384 (the wrapper) ----
168 let v2: i64 = nx_x509_verify_ecdsa_p384(
169 buf2 + c2.tbs_off, c2.tbs_len,
170 buf2 + c2.sig_off, c2.sig_len,
171 px, py
172 )
173 emit_label(0x57, 0x52, 0x44, 0x43, 0x54) // "WRDCT" (wrapper verdict)
174 let v2_buf: *u8 = sys_mmap(8)
175 v2_buf[0] = hexnib(v2 & 0xf) as u8
176 sys_write(2, v2_buf, 1); emit_nl()
177
178 // ---- 6. Call x509_verify_under_issuer (the dispatcher) ----
179 let v3: i64 = nx_x509_verify_under_issuer(buf2, c2, buf3, c3)
180 emit_label(0x44, 0x52, 0x44, 0x43, 0x54) // "DRDCT" (dispatch verdict)
181 let v3_buf: *u8 = sys_mmap(8)
182 v3_buf[0] = hexnib(v3 & 0xf) as u8
183 sys_write(2, v3_buf, 1); emit_nl()
184
185 // ---- 7. Pollution check: parse cert1, verify cert1->cert2 first,
186 // then RE-verify cert2->cert3 to see if prior state breaks
187 // the P-384 path.
188 let p1: *u8 = sys_mmap(64)
189 p1[0]=0x2F;p1[1]=0x74;p1[2]=0x6D;p1[3]=0x70;p1[4]=0x2F
190 p1[5]=0x63;p1[6]=0x65;p1[7]=0x72;p1[8]=0x74
191 p1[9]=0x31;p1[10]=0x2E;p1[11]=0x64;p1[12]=0x65;p1[13]=0x72
192 p1[14]=0
193 let buf1: *u8 = sys_mmap(4096)
194 let n1: i64 = read_to(p1, buf1, 4096)
195 if n1 <= 0 { return 5 }
196 let c1_raw: *u8 = sys_mmap(512); let c1: *X509Cert = c1_raw as *X509Cert
197 if x509_parse(buf1, n1, c1) < 0 { return 6 }
198 let vA: i64 = nx_x509_verify_under_issuer(buf1, c1, buf2, c2)
199 emit_label(0x50, 0x4F, 0x4C, 0x31, 0x32) // "POL12" (post cert1->cert2)
200 let vA_buf: *u8 = sys_mmap(8)
201 vA_buf[0] = hexnib(vA & 0xf) as u8
202 sys_write(2, vA_buf, 1); emit_nl()
203
204 let vB: i64 = nx_x509_verify_under_issuer(buf2, c2, buf3, c3)
205 emit_label(0x50, 0x4F, 0x4C, 0x32, 0x33) // "POL23" (re-cert2->cert3)
206 let vB_buf: *u8 = sys_mmap(8)
207 vB_buf[0] = hexnib(vB & 0xf) as u8
208 sys_write(2, vB_buf, 1); emit_nl()
209
210 // ---- 8. Call cert1->cert2 P-256 TWICE in a row to test self-repeat
211 let vC: i64 = nx_x509_verify_under_issuer(buf1, c1, buf2, c2)
212 emit_label(0x52, 0x50, 0x54, 0x31, 0x32) // "RPT12" (P-256 repeat 1)
213 let vC_buf: *u8 = sys_mmap(8)
214 vC_buf[0] = hexnib(vC & 0xf) as u8
215 sys_write(2, vC_buf, 1); emit_nl()
216
217 let vD: i64 = nx_x509_verify_under_issuer(buf1, c1, buf2, c2)
218 emit_label(0x52, 0x50, 0x54, 0x31, 0x33) // "RPT13" (P-256 repeat 2)
219 let vD_buf: *u8 = sys_mmap(8)
220 vD_buf[0] = hexnib(vD & 0xf) as u8
221 sys_write(2, vD_buf, 1); emit_nl()
222
223 // ---- 9. BYPASS dispatcher: call nx_x509_verify_ecdsa_p256 directly
224 // after the P-384 wrapper has already been called.
225 let p2x: *i64 = u256_alloc()
226 let p2y: *i64 = u256_alloc()
227 let exA: i64 = nx_x509_pubkey_extract_ec_p256(buf2, c2, p2x, p2y)
228 emit_label(0x45, 0x58, 0x32, 0x35, 0x36) // "EX256"
229 let exA_buf: *u8 = sys_mmap(8)
230 exA_buf[0] = hexnib(exA & 0xf) as u8
231 sys_write(2, exA_buf, 1); emit_nl()
232
233 let vE: i64 = nx_x509_verify_ecdsa_p256(
234 buf1 + c1.tbs_off, c1.tbs_len,
235 buf1 + c1.sig_off, c1.sig_len,
236 p2x, p2y
237 )
238 emit_label(0x42, 0x50, 0x32, 0x35, 0x36) // "BP256" (bypass P-256)
239 let vE_buf: *u8 = sys_mmap(8)
240 vE_buf[0] = hexnib(vE & 0xf) as u8
241 sys_write(2, vE_buf, 1); emit_nl()
242
243 // ---- 10. Re-parse fresh cert1, cert2 and retry P-256 verify ----
244 let c1b_raw: *u8 = sys_mmap(512); let c1b: *X509Cert = c1b_raw as *X509Cert
245 let c2b_raw: *u8 = sys_mmap(512); let c2b: *X509Cert = c2b_raw as *X509Cert
246 if x509_parse(buf1, n1, c1b) < 0 { return 7 }
247 if x509_parse(buf2, n2, c2b) < 0 { return 8 }
248 let p2bx: *i64 = u256_alloc()
249 let p2by: *i64 = u256_alloc()
250 nx_x509_pubkey_extract_ec_p256(buf2, c2b, p2bx, p2by)
251 let vF: i64 = nx_x509_verify_ecdsa_p256(
252 buf1 + c1b.tbs_off, c1b.tbs_len,
253 buf1 + c1b.sig_off, c1b.sig_len,
254 p2bx, p2by
255 )
256 emit_label(0x46, 0x50, 0x32, 0x35, 0x36) // "FP256" (fresh-parse P-256)
257 let vF_buf: *u8 = sys_mmap(8)
258 vF_buf[0] = hexnib(vF & 0xf) as u8
259 sys_write(2, vF_buf, 1); emit_nl()
260
261 // ---- 11. Dump TBS offsets to compare against the leaf test ----
262 let off1: i64 = c1.tbs_off
263 let len1: i64 = c1.tbs_len
264 let off2: i64 = c1.sig_off
265 let len2: i64 = c1.sig_len
266 emit_label(0x54, 0x42, 0x4F, 0x46, 0x46) // "TBOFF"
267 let o_buf: *u8 = sys_mmap(8)
268 o_buf[0]=(0x30 + ((off1 / 100) % 10)) as u8
269 o_buf[1]=(0x30 + ((off1 / 10) % 10)) as u8
270 o_buf[2]=(0x30 + (off1 % 10)) as u8
271 sys_write(2, o_buf, 3); emit_nl()
272 emit_label(0x54, 0x42, 0x4C, 0x45, 0x4E) // "TBLEN"
273 let l_buf: *u8 = sys_mmap(8)
274 l_buf[0]=(0x30 + ((len1 / 100) % 10)) as u8
275 l_buf[1]=(0x30 + ((len1 / 10) % 10)) as u8
276 l_buf[2]=(0x30 + (len1 % 10)) as u8
277 sys_write(2, l_buf, 3); emit_nl()
278
279 // ---- 12. SHA-256 isolation check after all the P-384 calls ----
280 let h256: *u8 = sys_mmap(32)
281 sha256_digest(buf1 + c1.tbs_off, c1.tbs_len, h256)
282 emit_label(0x53, 0x32, 0x35, 0x36, 0x3A) // "S256:"
283 dump_bytes(h256, 32); emit_nl()
284
285 return 0
286}