1 /** 2 * Unit test helper 3 * 4 * Copyright: 5 * (C) 2014-2015 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.cert.x509.test; 12 13 /* 14 Code to run the X.509v3 processing tests described in "Conformance 15 Testing of Relying Party Client Certificate Path Proccessing Logic", 16 which is available on NIST's web site. 17 18 Known Failures/Problems 19 20 TLSPolicy extensions are not implemented, so we skip tests #34-#53. 21 22 Tests #75 and #76 are skipped as they make use of relatively obscure CRL 23 extensions which are not supported. 24 */ 25 import botan.constants; 26 static if (BOTAN_TEST && BOTAN_HAS_X509_CERTIFICATES): 27 28 import botan.test; 29 import botan.cert.x509.x509path; 30 import botan.utils.types; 31 import std.file; 32 import std.array; 33 34 string[] dirListing(string dir_path) 35 { 36 auto dirfiles = dirEntries(dir_path, "*", SpanMode.shallow); 37 string[] files; 38 foreach(file; dirfiles) { 39 files ~= file.name; 40 } 41 files.sort(); 42 return files; 43 } 44 45 /* 46 The expected results are essentially the error codes that best coorespond 47 to the problem described in the testing documentation. 48 49 There are a few cases where the tests say there should or should not be an 50 error, and I disagree. A few of the tests have test results different from 51 what they "should" be: these changes are marked as such, and have comments 52 explaining the problem at hand. 53 */ 54 CertificateStatusCode[] getExpected() 55 { 56 CertificateStatusCode[] expected_results; 57 expected_results.length = 75; 58 59 /* OK, not a super great way of doing this... */ 60 expected_results[1] = CertificateStatusCode.VERIFIED; 61 expected_results[2] = CertificateStatusCode.SIGNATURE_ERROR; 62 expected_results[3] = CertificateStatusCode.SIGNATURE_ERROR; 63 expected_results[4] = CertificateStatusCode.VERIFIED; 64 expected_results[5] = CertificateStatusCode.CERT_NOT_YET_VALID; 65 expected_results[6] = CertificateStatusCode.CERT_NOT_YET_VALID; 66 expected_results[7] = CertificateStatusCode.VERIFIED; 67 expected_results[8] = CertificateStatusCode.CERT_NOT_YET_VALID; 68 expected_results[9] = CertificateStatusCode.CERT_HAS_EXPIRED; 69 expected_results[10] = CertificateStatusCode.CERT_HAS_EXPIRED; 70 expected_results[11] = CertificateStatusCode.CERT_HAS_EXPIRED; 71 expected_results[12] = CertificateStatusCode.VERIFIED; 72 expected_results[13] = CertificateStatusCode.CERT_ISSUER_NOT_FOUND; 73 74 expected_results[14] = CertificateStatusCode.CERT_ISSUER_NOT_FOUND; 75 expected_results[15] = CertificateStatusCode.VERIFIED; 76 expected_results[16] = CertificateStatusCode.VERIFIED; 77 expected_results[17] = CertificateStatusCode.VERIFIED; 78 expected_results[18] = CertificateStatusCode.VERIFIED; 79 80 expected_results[19] = CertificateStatusCode.NO_REVOCATION_DATA; 81 expected_results[20] = CertificateStatusCode.CERT_IS_REVOKED; 82 expected_results[21] = CertificateStatusCode.CERT_IS_REVOKED; 83 84 expected_results[22] = CertificateStatusCode.CA_CERT_NOT_FOR_CERT_ISSUER; 85 expected_results[23] = CertificateStatusCode.CA_CERT_NOT_FOR_CERT_ISSUER; 86 expected_results[24] = CertificateStatusCode.VERIFIED; 87 expected_results[25] = CertificateStatusCode.CA_CERT_NOT_FOR_CERT_ISSUER; 88 expected_results[26] = CertificateStatusCode.VERIFIED; 89 expected_results[27] = CertificateStatusCode.VERIFIED; 90 expected_results[28] = CertificateStatusCode.CA_CERT_NOT_FOR_CERT_ISSUER; 91 expected_results[29] = CertificateStatusCode.CA_CERT_NOT_FOR_CERT_ISSUER; 92 expected_results[30] = CertificateStatusCode.VERIFIED; 93 94 expected_results[31] = CertificateStatusCode.CA_CERT_NOT_FOR_CRL_ISSUER; 95 expected_results[32] = CertificateStatusCode.CA_CERT_NOT_FOR_CRL_ISSUER; 96 expected_results[33] = CertificateStatusCode.VERIFIED; 97 98 /* 99 TLSPolicy tests: a little trickier because there are other inputs 100 which affect the result. 101 102 In the case of the tests currently in the suite, the default 103 method (with acceptable policy being "any-policy" and with no 104 explict policy required), will almost always result in a verified 105 status. This is not particularly helpful. So, we should do several 106 different tests for each test set: 107 108 1) With the user policy as any-policy and no explicit policy 109 2) With the user policy as any-policy and an explicit policy required 110 3) With the user policy as test-policy-1 (2.16.840.1.101.3.1.48.1) and 111 an explict policy required 112 4) With the user policy as either test-policy-1 or test-policy-2 and an 113 explicit policy required 114 115 This provides reasonably good coverage of the possible outcomes. 116 */ 117 118 expected_results[34] = CertificateStatusCode.VERIFIED; 119 expected_results[35] = CertificateStatusCode.VERIFIED; 120 expected_results[36] = CertificateStatusCode.VERIFIED; 121 expected_results[37] = CertificateStatusCode.VERIFIED; 122 expected_results[38] = CertificateStatusCode.VERIFIED; 123 expected_results[39] = CertificateStatusCode.VERIFIED; 124 expected_results[40] = CertificateStatusCode.VERIFIED; 125 expected_results[41] = CertificateStatusCode.VERIFIED; 126 expected_results[42] = CertificateStatusCode.VERIFIED; 127 expected_results[43] = CertificateStatusCode.VERIFIED; 128 expected_results[44] = CertificateStatusCode.VERIFIED; 129 130 //expected_results[45] = CertificateStatusCode.EXPLICT_POLICY_REQUIRED; 131 //expected_results[46] = CertificateStatusCode.ACCEPT; 132 //expected_results[47] = CertificateStatusCode.EXPLICT_POLICY_REQUIRED; 133 134 expected_results[48] = CertificateStatusCode.VERIFIED; 135 expected_results[49] = CertificateStatusCode.VERIFIED; 136 expected_results[50] = CertificateStatusCode.VERIFIED; 137 expected_results[51] = CertificateStatusCode.VERIFIED; 138 expected_results[52] = CertificateStatusCode.VERIFIED; 139 expected_results[53] = CertificateStatusCode.VERIFIED; 140 141 expected_results[54] = CertificateStatusCode.CERT_CHAIN_TOO_LONG; 142 expected_results[55] = CertificateStatusCode.CERT_CHAIN_TOO_LONG; 143 expected_results[56] = CertificateStatusCode.VERIFIED; 144 expected_results[57] = CertificateStatusCode.VERIFIED; 145 expected_results[58] = CertificateStatusCode.CERT_CHAIN_TOO_LONG; 146 expected_results[59] = CertificateStatusCode.CERT_CHAIN_TOO_LONG; 147 expected_results[60] = CertificateStatusCode.CERT_CHAIN_TOO_LONG; 148 expected_results[61] = CertificateStatusCode.CERT_CHAIN_TOO_LONG; 149 expected_results[62] = CertificateStatusCode.VERIFIED; 150 expected_results[63] = CertificateStatusCode.VERIFIED; 151 152 expected_results[64] = CertificateStatusCode.CRL_BAD_SIGNATURE; 153 154 expected_results[65] = CertificateStatusCode.NO_REVOCATION_DATA; 155 expected_results[66] = CertificateStatusCode.NO_REVOCATION_DATA; 156 157 expected_results[67] = CertificateStatusCode.VERIFIED; 158 159 expected_results[68] = CertificateStatusCode.CERT_IS_REVOKED; 160 expected_results[69] = CertificateStatusCode.CERT_IS_REVOKED; 161 expected_results[70] = CertificateStatusCode.CERT_IS_REVOKED; 162 expected_results[71] = CertificateStatusCode.CERT_IS_REVOKED; 163 expected_results[72] = CertificateStatusCode.CRL_HAS_EXPIRED; 164 expected_results[73] = CertificateStatusCode.CRL_HAS_EXPIRED; 165 expected_results[74] = CertificateStatusCode.VERIFIED; 166 167 /* These tests use weird CRL extensions which aren't supported yet */ 168 //expected_results[75] = ; 169 //expected_results[76] = ; 170 171 return expected_results; 172 } 173 174 static if (BOTAN_HAS_TESTS && !SKIP_X509_TEST) unittest 175 { 176 import botan.libstate.global_state; 177 auto state = globalState(); // ensure initialized 178 179 logDebug("Testing x509/test.d ..."); 180 const string root_test_dir = "test_data/nist_x509/"; 181 182 size_t unexp_failure = 0; 183 size_t unexp_success = 0; 184 size_t wrong_error = 0; 185 size_t skipped = 0; 186 size_t ran = 0; 187 188 CertificateStatusCode[] expected_results = getExpected(); 189 190 try { 191 192 const string[] test_dirs = dirListing(root_test_dir); 193 194 for(size_t i = 0; i != 74; i++) 195 { 196 const size_t test_no = i+1; 197 logDebug("NIST X.509 test #", test_no); 198 199 const string test_dir = test_dirs[i]; 200 const string[] all_files = dirListing(test_dir); 201 202 Vector!string certs, crls; 203 string root_cert, to_verify; 204 205 for(size_t k = 0; k != all_files.length; k++) 206 { 207 const string current = all_files[k]; 208 209 if (current.canFind("int") && current.canFind(".crt")) 210 certs.pushBack(current); 211 else if (current.canFind("root.crt")) 212 root_cert = current; 213 else if (current.canFind("end.crt")) 214 to_verify = current; 215 else if (current.canFind(".crl")) 216 crls.pushBack(current); 217 } 218 219 if (expected_results.canFind(i+1) == -1) 220 { 221 skipped++; 222 continue; 223 } 224 225 ++ran; 226 227 auto store = scoped!CertificateStoreInMemory(); 228 229 //logTrace(root_cert); 230 store.addCertificate(X509Certificate(root_cert)); 231 232 X509Certificate end_user = X509Certificate(to_verify); 233 foreach(cert; certs[]) 234 store.addCertificate(X509Certificate(cert)); 235 236 foreach(crl; crls[]) 237 { 238 DataSourceStream input = DataSourceStream(crl); 239 X509CRL crl_ = X509CRL(cast(DataSource)input); 240 store.addCrl(crl_); 241 } 242 243 auto restrictions = PathValidationRestrictions(true); 244 245 PathValidationResult validation_result = x509PathValidate(end_user, restrictions, store); 246 auto expected = expected_results[test_no]; 247 CertificateStatusCode result = validation_result.result(); 248 if (result != expected) { 249 logError("NIST X.509 test #", test_no, " : "); 250 const string result_str = PathValidationResult.statusString(result); 251 const string exp_str = PathValidationResult.statusString(expected); 252 if (expected == CertificateStatusCode.VERIFIED) { 253 logError("unexpected failure: " ~ result_str); 254 unexp_failure++; 255 } 256 else if (result == CertificateStatusCode.VERIFIED) { 257 logError("unexpected success, expected " ~ exp_str); 258 unexp_success++; 259 } 260 else { 261 logError("wrong error, got '" ~ result_str ~ "' expected '" ~ exp_str ~ "'"); 262 wrong_error++; 263 assert(false); 264 } 265 } 266 } 267 } 268 catch(Throwable e) 269 { 270 logError(e.toString()); 271 logTrace(e.msg); 272 } 273 274 const size_t all_failures = unexp_failure + unexp_success + wrong_error; 275 276 testReport("NIST X.509 path validation", ran, all_failures); 277 }