code wiki / (root) / nx_x509_chain_verify_test.nx

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}