nx_x509_chain_verify_test.nx source
↩ module page · 216 lines · 10990 B
1// nx_x509_chain_verify_test.nx -- KAT for the X.509 chain walker.
2//
3// Builds a synthetic 2-cert chain:
4// leaf: tbs="sample", sig_alg=ECDSA-P256+SHA-256, sig=DER RFC 6979,
5// issuer DN="CN=Root"
6// root: subject DN="CN=Root", pubkey_alg=id-ecPublicKey+prime256v1,
7// pubkey=RFC 6979 §A.2.5 uncompressed
8//
9// chain_verify([leaf, root]) should:
10// 1. DN match: leaf.issuer (17 bytes) == root.subject (17 bytes)
11// 2. Sig verify: leaf's outer sig under root's pubkey
12// (composes nx_x509_verify_under_issuer + nx_x509_verify_ecdsa_p256
13// which we already proved end-to-end against the RFC 6979 vector)
14//
15// expect_exit: 0
16// license_tier: ORIGINAL
17
18import "nx_syscalls.nx"
19import "nx_x509.nx"
20import "nx_x509_dn_match.nx"
21import "nx_x509_verify_under_issuer.nx"
22import "nx_x509_chain_verify.nx"
23
24// "CN=Root" DN bytes (17 bytes total):
25// 30 0F SEQUENCE 15
26// 31 0D SET 13
27// 30 0B SEQUENCE 11
28// 06 03 55 04 03 OID 2.5.4.3 (commonName)
29// 0C 04 52 6F 6F 74 UTF8String "Root"
30func write_root_dn(buf: *u8, off: i64) -> i64 {
31 buf[off] = 0x30; buf[off+1] = 0x0F
32 buf[off+2] = 0x31; buf[off+3] = 0x0D
33 buf[off+4] = 0x30; buf[off+5] = 0x0B
34 buf[off+6] = 0x06; buf[off+7] = 0x03
35 buf[off+8] = 0x55; buf[off+9] = 0x04; buf[off+10] = 0x03
36 buf[off+11] = 0x0C; buf[off+12] = 0x04
37 buf[off+13] = 0x52; buf[off+14] = 0x6F; buf[off+15] = 0x6F; buf[off+16] = 0x74
38 return 17
39}
40
41func zero_cert(cert: *X509Cert) -> i64 {
42 cert.tbs_off=0; cert.tbs_len=0
43 cert.serial_off=0; cert.serial_len=0
44 cert.sig_alg_off=0; cert.sig_alg_len=0
45 cert.spki_off=0; cert.spki_len=0
46 cert.sig_off=0; cert.sig_len=0
47 cert.pubkey_off=0; cert.pubkey_len=0
48 cert.pubkey_alg_off=0; cert.pubkey_alg_len=0
49 cert.validity_off=0; cert.validity_len=0
50 cert.extensions_off = 0 - 1; cert.extensions_len=0
51 cert.issuer_off=0; cert.issuer_len=0
52 cert.subject_off=0; cert.subject_len=0
53 return 0
54}
55
56func main() -> i64 {
57 // ---------- Build leaf cert buffer + struct ----------
58 // Layout: [sig_alg_blob (12)][tbs="sample" (6)][sig DER (72)][issuer DN (17)]
59 let leaf_buf: *u8 = sys_mmap(256)
60 // sig_alg @ 0: SEQUENCE { OID ecdsa-with-SHA256 } = 12 bytes
61 leaf_buf[0]=0x30; leaf_buf[1]=0x0A; leaf_buf[2]=0x06; leaf_buf[3]=0x08
62 leaf_buf[4]=0x2A; leaf_buf[5]=0x86; leaf_buf[6]=0x48; leaf_buf[7]=0xCE
63 leaf_buf[8]=0x3D; leaf_buf[9]=0x04; leaf_buf[10]=0x03; leaf_buf[11]=0x02
64 // tbs @ 12: "sample"
65 leaf_buf[12]=0x73; leaf_buf[13]=0x61; leaf_buf[14]=0x6D
66 leaf_buf[15]=0x70; leaf_buf[16]=0x6C; leaf_buf[17]=0x65
67 // sig DER @ 18: SEQUENCE { INTEGER r, INTEGER s } = 72 bytes
68 leaf_buf[18]=0x30; leaf_buf[19]=0x46
69 leaf_buf[20]=0x02; leaf_buf[21]=0x21; leaf_buf[22]=0x00
70 leaf_buf[23]=0xEF; leaf_buf[24]=0xD4; leaf_buf[25]=0x8B; leaf_buf[26]=0x2A
71 leaf_buf[27]=0xAC; leaf_buf[28]=0xB6; leaf_buf[29]=0xA8; leaf_buf[30]=0xFD
72 leaf_buf[31]=0x11; leaf_buf[32]=0x40; leaf_buf[33]=0xDD; leaf_buf[34]=0x9C
73 leaf_buf[35]=0xD4; leaf_buf[36]=0x5E; leaf_buf[37]=0x81; leaf_buf[38]=0xD6
74 leaf_buf[39]=0x9D; leaf_buf[40]=0x2C; leaf_buf[41]=0x87; leaf_buf[42]=0x7B
75 leaf_buf[43]=0x56; leaf_buf[44]=0xAA; leaf_buf[45]=0xF9; leaf_buf[46]=0x91
76 leaf_buf[47]=0xC3; leaf_buf[48]=0x4D; leaf_buf[49]=0x0E; leaf_buf[50]=0xA8
77 leaf_buf[51]=0x4E; leaf_buf[52]=0xAF; leaf_buf[53]=0x37; leaf_buf[54]=0x16
78 leaf_buf[55]=0x02; leaf_buf[56]=0x21; leaf_buf[57]=0x00
79 leaf_buf[58]=0xF7; leaf_buf[59]=0xCB; leaf_buf[60]=0x1C; leaf_buf[61]=0x94
80 leaf_buf[62]=0x2D; leaf_buf[63]=0x65; leaf_buf[64]=0x7C; leaf_buf[65]=0x41
81 leaf_buf[66]=0xD4; leaf_buf[67]=0x36; leaf_buf[68]=0xC7; leaf_buf[69]=0xA1
82 leaf_buf[70]=0xB6; leaf_buf[71]=0xE2; leaf_buf[72]=0x9F; leaf_buf[73]=0x65
83 leaf_buf[74]=0xF3; leaf_buf[75]=0xE9; leaf_buf[76]=0x00; leaf_buf[77]=0xDB
84 leaf_buf[78]=0xB9; leaf_buf[79]=0xAF; leaf_buf[80]=0xF4; leaf_buf[81]=0x06
85 leaf_buf[82]=0x4D; leaf_buf[83]=0xC4; leaf_buf[84]=0xAB; leaf_buf[85]=0x2F
86 leaf_buf[86]=0x84; leaf_buf[87]=0x3A; leaf_buf[88]=0xCD; leaf_buf[89]=0xA8
87 // issuer DN @ 90: "CN=Root" (17 bytes)
88 write_root_dn(leaf_buf, 90)
89
90 let leaf_cert_raw: *u8 = sys_mmap(256)
91 let leaf_cert: *X509Cert = leaf_cert_raw as *X509Cert
92 zero_cert(leaf_cert)
93 leaf_cert.sig_alg_off = 0; leaf_cert.sig_alg_len = 12
94 leaf_cert.tbs_off = 12; leaf_cert.tbs_len = 6
95 leaf_cert.sig_off = 18; leaf_cert.sig_len = 72
96 leaf_cert.issuer_off = 90; leaf_cert.issuer_len = 17
97
98 // ---------- Build root cert buffer + struct ----------
99 // Layout: [pubkey_alg_blob (21)][pubkey (65)][subject DN (17)]
100 let root_buf: *u8 = sys_mmap(256)
101 // pubkey_alg @ 0: SEQUENCE { id-ecPublicKey, prime256v1 } = 21 bytes
102 root_buf[0]=0x30; root_buf[1]=0x13
103 root_buf[2]=0x06; root_buf[3]=0x07
104 root_buf[4]=0x2A; root_buf[5]=0x86; root_buf[6]=0x48; root_buf[7]=0xCE
105 root_buf[8]=0x3D; root_buf[9]=0x02; root_buf[10]=0x01
106 root_buf[11]=0x06; root_buf[12]=0x08
107 root_buf[13]=0x2A; root_buf[14]=0x86; root_buf[15]=0x48; root_buf[16]=0xCE
108 root_buf[17]=0x3D; root_buf[18]=0x03; root_buf[19]=0x01; root_buf[20]=0x07
109 // pubkey @ 21: 0x04 || X(32) || Y(32) = 65 bytes (RFC 6979 §A.2.5)
110 root_buf[21]=0x04
111 root_buf[22]=0x60; root_buf[23]=0xFE; root_buf[24]=0xD4; root_buf[25]=0xBA
112 root_buf[26]=0x25; root_buf[27]=0x5A; root_buf[28]=0x9D; root_buf[29]=0x31
113 root_buf[30]=0xC9; root_buf[31]=0x61; root_buf[32]=0xEB; root_buf[33]=0x74
114 root_buf[34]=0xC6; root_buf[35]=0x35; root_buf[36]=0x6D; root_buf[37]=0x68
115 root_buf[38]=0xC0; root_buf[39]=0x49; root_buf[40]=0xB8; root_buf[41]=0x92
116 root_buf[42]=0x3B; root_buf[43]=0x61; root_buf[44]=0xFA; root_buf[45]=0x6C
117 root_buf[46]=0xE6; root_buf[47]=0x69; root_buf[48]=0x62; root_buf[49]=0x2E
118 root_buf[50]=0x60; root_buf[51]=0xF2; root_buf[52]=0x9F; root_buf[53]=0xB6
119 root_buf[54]=0x79; root_buf[55]=0x03; root_buf[56]=0xFE; root_buf[57]=0x10
120 root_buf[58]=0x08; root_buf[59]=0xB8; root_buf[60]=0xBC; root_buf[61]=0x99
121 root_buf[62]=0xA4; root_buf[63]=0x1A; root_buf[64]=0xE9; root_buf[65]=0xE9
122 root_buf[66]=0x56; root_buf[67]=0x28; root_buf[68]=0xBC; root_buf[69]=0x64
123 root_buf[70]=0xF2; root_buf[71]=0xF1; root_buf[72]=0xB2; root_buf[73]=0x0C
124 root_buf[74]=0x2D; root_buf[75]=0x7E; root_buf[76]=0x9F; root_buf[77]=0x51
125 root_buf[78]=0x77; root_buf[79]=0xA3; root_buf[80]=0xC2; root_buf[81]=0x94
126 root_buf[82]=0xD4; root_buf[83]=0x46; root_buf[84]=0x22; root_buf[85]=0x99
127 // subject DN @ 86: "CN=Root" (17 bytes)
128 write_root_dn(root_buf, 86)
129
130 let root_cert_raw: *u8 = sys_mmap(256)
131 let root_cert: *X509Cert = root_cert_raw as *X509Cert
132 zero_cert(root_cert)
133 root_cert.pubkey_alg_off = 0; root_cert.pubkey_alg_len = 21
134 root_cert.pubkey_off = 21; root_cert.pubkey_len = 65
135 root_cert.subject_off = 86; root_cert.subject_len = 17
136
137 // ---------- Build arrays of 2 pointers each ----------
138 let bufs_raw: *u8 = sys_mmap(16) // 2 * 8 bytes
139 let bufs: **u8 = bufs_raw as **u8
140 bufs[0] = leaf_buf
141 bufs[1] = root_buf
142 let certs_raw: *u8 = sys_mmap(16)
143 let certs: **X509Cert = certs_raw as **X509Cert
144 certs[0] = leaf_cert
145 certs[1] = root_cert
146
147 // ============================================================
148 // Test A: n=1 single-cert chain (the cert IS the trust anchor)
149 // ============================================================
150 let v1: i64 = nx_x509_chain_verify(bufs, certs, 1)
151 if v1 != NX_X509_CHAIN_OK { return 1 }
152
153 // ============================================================
154 // Test B: n=2 valid chain (leaf signed by root) <-- THE MILESTONE
155 // ============================================================
156 let v2: i64 = nx_x509_chain_verify(bufs, certs, 2)
157 if v2 != NX_X509_CHAIN_OK { return 2 }
158
159 // ============================================================
160 // Test C: n=0 -> EMPTY
161 // ============================================================
162 if nx_x509_chain_verify(bufs, certs, 0) != NX_X509_CHAIN_EMPTY { return 3 }
163
164 // ============================================================
165 // Test D: n > MAX_LEN -> TOO_LONG
166 // ============================================================
167 if nx_x509_chain_verify(bufs, certs, NX_X509_CHAIN_MAX_LEN + 1) != NX_X509_CHAIN_TOO_LONG { return 4 }
168
169 // ============================================================
170 // Test E: DN mismatch -> DN_MISMATCH
171 // Mutate one byte of root's subject DN; chain walk fails the
172 // linkage check BEFORE attempting the slow ECDSA verify.
173 // ============================================================
174 let save: i64 = root_buf[88] & 0xff
175 root_buf[88] = (save ^ 0x40) as u8
176 let v5: i64 = nx_x509_chain_verify(bufs, certs, 2)
177 if v5 != NX_X509_CHAIN_DN_MISMATCH { return 5 }
178 root_buf[88] = save as u8 // restore
179
180 // ============================================================
181 // Test F: corrupt leaf sig -> SIG_FAIL
182 // ============================================================
183 let save2: i64 = leaf_buf[23] & 0xff
184 leaf_buf[23] = (save2 ^ 0x01) as u8
185 let v6: i64 = nx_x509_chain_verify(bufs, certs, 2)
186 if v6 != NX_X509_CHAIN_SIG_FAIL { return 6 }
187 leaf_buf[23] = save2 as u8
188
189 // ============================================================
190 // Test G: dn_match unit checks
191 // ============================================================
192 // Same buffer, same range -> match
193 if nx_x509_dn_match(leaf_buf, 90, 17, root_buf, 86, 17) != 1 { return 10 }
194 // Different lengths -> no match
195 if nx_x509_dn_match(leaf_buf, 90, 17, root_buf, 86, 16) != 0 { return 11 }
196 // Zero length -> no match (defensive)
197 if nx_x509_dn_match(leaf_buf, 90, 0, root_buf, 86, 0) != 0 { return 12 }
198 // Differing byte -> no match
199 root_buf[86] = (root_buf[86] ^ 0x40) as u8
200 if nx_x509_dn_match(leaf_buf, 90, 17, root_buf, 86, 17) != 0 { return 13 }
201 root_buf[86] = (root_buf[86] ^ 0x40) as u8
202
203 // ============================================================
204 // Test H: verdict gate
205 // ============================================================
206 if nx_x509_chain_verdict_is_valid(NX_X509_CHAIN_OK) != 1 { return 20 }
207 if nx_x509_chain_verdict_is_valid(NX_X509_CHAIN_EMPTY) != 1 { return 21 }
208 if nx_x509_chain_verdict_is_valid(NX_X509_CHAIN_TOO_LONG) != 1 { return 22 }
209 if nx_x509_chain_verdict_is_valid(NX_X509_CHAIN_DN_MISMATCH) != 1 { return 23 }
210 if nx_x509_chain_verdict_is_valid(NX_X509_CHAIN_SIG_FAIL) != 1 { return 24 }
211 if nx_x509_chain_verdict_is_valid(NX_X509_CHAIN_VERDICT_N) != 0 { return 25 }
212 if nx_x509_chain_verdict_is_valid(0) != 0 { return 26 }
213 if nx_x509_chain_verdict_is_valid(0 - 1) != 0 { return 27 }
214
215 return 0
216}