1 /*
2 * (C) 2009,2019 Jack Lloyd
3 * (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
4 *
5 * Botan is released under the Simplified BSD License (see license.txt)
6 */
7
8 #include "tests.h"
9
10 #if defined(BOTAN_HAS_X509_CERTIFICATES)
11 #include <botan/calendar.h>
12 #include <botan/pkcs10.h>
13 #include <botan/pkcs8.h>
14 #include <botan/x509self.h>
15 #include <botan/x509path.h>
16 #include <botan/x509_ca.h>
17 #include <botan/x509_ext.h>
18 #include <botan/pk_algs.h>
19 #include <botan/ber_dec.h>
20 #include <botan/der_enc.h>
21 #include <botan/oids.h>
22 #include <botan/internal/padding.h>
23 #endif
24
25 namespace Botan_Tests {
26
27 namespace {
28
29 #if defined(BOTAN_HAS_X509_CERTIFICATES)
30
from_date(const int y,const int m,const int d)31 Botan::X509_Time from_date(const int y, const int m, const int d)
32 {
33 const size_t this_year = Botan::calendar_value(std::chrono::system_clock::now()).get_year();
34
35 Botan::calendar_point t(static_cast<uint32_t>(this_year + y), m, d, 0, 0, 0);
36 return Botan::X509_Time(t.to_std_timepoint());
37 }
38
39 /* Return some option sets */
ca_opts(const std::string & sig_padding="")40 Botan::X509_Cert_Options ca_opts(const std::string& sig_padding = "")
41 {
42 Botan::X509_Cert_Options opts("Test CA/US/Botan Project/Testing");
43
44 opts.uri = "https://botan.randombit.net";
45 opts.dns = "botan.randombit.net";
46 opts.email = "testing@randombit.net";
47 opts.set_padding_scheme(sig_padding);
48
49 opts.CA_key(1);
50
51 return opts;
52 }
53
req_opts1(const std::string & algo,const std::string & sig_padding="")54 Botan::X509_Cert_Options req_opts1(const std::string& algo, const std::string& sig_padding = "")
55 {
56 Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
57
58 opts.uri = "https://botan.randombit.net";
59 opts.dns = "botan.randombit.net";
60 opts.email = "testing@randombit.net";
61 opts.set_padding_scheme(sig_padding);
62
63 opts.not_before("160101200000Z");
64 opts.not_after("300101200000Z");
65
66 opts.challenge = "zoom";
67
68 if(algo == "RSA")
69 {
70 opts.constraints = Botan::Key_Constraints(Botan::KEY_ENCIPHERMENT);
71 }
72 else if(algo == "DSA" || algo == "ECDSA" || algo == "ECGDSA" || algo == "ECKCDSA")
73 {
74 opts.constraints = Botan::Key_Constraints(Botan::DIGITAL_SIGNATURE);
75 }
76
77 return opts;
78 }
79
req_opts2(const std::string & sig_padding="")80 Botan::X509_Cert_Options req_opts2(const std::string& sig_padding = "")
81 {
82 Botan::X509_Cert_Options opts("Test User 2/US/Botan Project/Testing");
83
84 opts.uri = "https://botan.randombit.net";
85 opts.dns = "botan.randombit.net";
86 opts.email = "testing@randombit.net";
87 opts.set_padding_scheme(sig_padding);
88
89 opts.add_ex_constraint("PKIX.EmailProtection");
90
91 return opts;
92 }
93
req_opts3(const std::string & sig_padding="")94 Botan::X509_Cert_Options req_opts3(const std::string& sig_padding = "")
95 {
96 Botan::X509_Cert_Options opts("Test User 2/US/Botan Project/Testing");
97
98 opts.uri = "https://botan.randombit.net";
99 opts.dns = "botan.randombit.net";
100 opts.email = "testing@randombit.net";
101 opts.set_padding_scheme(sig_padding);
102
103 opts.more_org_units.push_back("IT");
104 opts.more_org_units.push_back("Security");
105 opts.more_dns.push_back("www.botan.randombit.net");
106
107 return opts;
108 }
109
make_a_private_key(const std::string & algo)110 std::unique_ptr<Botan::Private_Key> make_a_private_key(const std::string& algo)
111 {
112 const std::string params = [&]
113 {
114 // Here we override defaults as needed
115 if(algo == "RSA")
116 {
117 return "1024";
118 }
119 if(algo == "GOST-34.10")
120 {
121 return "gost_256A";
122 }
123 if(algo == "ECKCDSA" || algo == "ECGDSA")
124 {
125 return "brainpool256r1";
126 }
127 return ""; // default "" means choose acceptable algo-specific params
128 }();
129
130 return Botan::create_private_key(algo, Test::rng(), params);
131 }
132
test_cert_status_strings()133 Test::Result test_cert_status_strings()
134 {
135 Test::Result result("Certificate_Status_Code to_string");
136
137 std::set<std::string> seen;
138
139 result.test_eq("Same string",
140 Botan::to_string(Botan::Certificate_Status_Code::OK),
141 Botan::to_string(Botan::Certificate_Status_Code::VERIFIED));
142
143 const Botan::Certificate_Status_Code codes[]
144 {
145 Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD,
146 Botan::Certificate_Status_Code::OCSP_SIGNATURE_OK,
147 Botan::Certificate_Status_Code::VALID_CRL_CHECKED,
148 Botan::Certificate_Status_Code::OCSP_NO_HTTP,
149
150 Botan::Certificate_Status_Code::CERT_SERIAL_NEGATIVE,
151 Botan::Certificate_Status_Code::DN_TOO_LONG,
152
153 Botan::Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK,
154 Botan::Certificate_Status_Code::NO_MATCHING_CRLDP,
155 Botan::Certificate_Status_Code::UNTRUSTED_HASH,
156 Botan::Certificate_Status_Code::NO_REVOCATION_DATA,
157 Botan::Certificate_Status_Code::CERT_NOT_YET_VALID,
158 Botan::Certificate_Status_Code::CERT_HAS_EXPIRED,
159 Botan::Certificate_Status_Code::OCSP_NOT_YET_VALID,
160 Botan::Certificate_Status_Code::OCSP_HAS_EXPIRED,
161 Botan::Certificate_Status_Code::CRL_NOT_YET_VALID,
162 Botan::Certificate_Status_Code::CRL_HAS_EXPIRED,
163 Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND,
164 Botan::Certificate_Status_Code::CANNOT_ESTABLISH_TRUST,
165 Botan::Certificate_Status_Code::CERT_CHAIN_LOOP,
166 Botan::Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT,
167 Botan::Certificate_Status_Code::CHAIN_NAME_MISMATCH,
168 Botan::Certificate_Status_Code::POLICY_ERROR,
169 Botan::Certificate_Status_Code::DUPLICATE_CERT_POLICY,
170 Botan::Certificate_Status_Code::INVALID_USAGE,
171 Botan::Certificate_Status_Code::CERT_CHAIN_TOO_LONG,
172 Botan::Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER,
173 Botan::Certificate_Status_Code::NAME_CONSTRAINT_ERROR,
174 Botan::Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER,
175 Botan::Certificate_Status_Code::OCSP_CERT_NOT_LISTED,
176 Botan::Certificate_Status_Code::OCSP_BAD_STATUS,
177 Botan::Certificate_Status_Code::CERT_NAME_NOMATCH,
178 Botan::Certificate_Status_Code::UNKNOWN_CRITICAL_EXTENSION,
179 Botan::Certificate_Status_Code::DUPLICATE_CERT_EXTENSION,
180 Botan::Certificate_Status_Code::EXT_IN_V1_V2_CERT,
181 Botan::Certificate_Status_Code::OCSP_SIGNATURE_ERROR,
182 Botan::Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND,
183 Botan::Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE,
184 Botan::Certificate_Status_Code::OCSP_RESPONSE_INVALID,
185 Botan::Certificate_Status_Code::CERT_IS_REVOKED,
186 Botan::Certificate_Status_Code::CRL_BAD_SIGNATURE,
187 Botan::Certificate_Status_Code::SIGNATURE_ERROR,
188 Botan::Certificate_Status_Code::CERT_PUBKEY_INVALID,
189 Botan::Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN,
190 Botan::Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS,
191 };
192
193 for(const auto code : codes)
194 {
195 const std::string s = Botan::to_string(code);
196 result.confirm("String is long enough to be informative", s.size() > 12);
197 result.test_eq("No duplicates", seen.count(s), 0);
198 seen.insert(s);
199 }
200
201 return result;
202
203 }
204
test_x509_extension()205 Test::Result test_x509_extension()
206 {
207 Test::Result result("X509 Extensions API");
208
209 Botan::Extensions extn;
210
211 const auto oid_bc = Botan::OID::from_string("X509v3.BasicConstraints");
212 const auto oid_skid = Botan::OID::from_string("X509v3.SubjectKeyIdentifier");
213
214 extn.add(new Botan::Cert_Extension::Basic_Constraints(true), true);
215
216 result.confirm("Basic constraints is set", extn.extension_set(oid_bc));
217 result.confirm("Basic constraints is critical", extn.critical_extension_set(oid_bc));
218 result.confirm("SKID is not set", !extn.extension_set(oid_skid));
219 result.confirm("SKID is not critical", !extn.critical_extension_set(oid_skid));
220
221 result.test_eq("Extension::get_extension_bits",
222 extn.get_extension_bits(oid_bc), "30060101FF020100");
223
224 result.test_throws("Extension::get_extension_bits throws if not set",
225 [&]() { extn.get_extension_bits(oid_skid); });
226
227 result.test_throws("Extension::add throws on second add",
228 [&]() { extn.add(new Botan::Cert_Extension::Basic_Constraints(false), false); });
229
230 result.test_eq("Extension::get_extension_bits",
231 extn.get_extension_bits(oid_bc), "30060101FF020100");
232
233 result.confirm("Returns false since extension already existed",
234 !extn.add_new(new Botan::Cert_Extension::Basic_Constraints(false), false));
235
236 result.confirm("Basic constraints is still critical", extn.critical_extension_set(oid_bc));
237
238 extn.replace(new Botan::Cert_Extension::Basic_Constraints(false), false);
239 result.confirm("Replaced basic constraints is not critical", !extn.critical_extension_set(oid_bc));
240 result.test_eq("Extension::get_extension_bits", extn.get_extension_bits(oid_bc), "3000");
241
242 result.confirm("Delete returns false if extn not set", !extn.remove(oid_skid));
243 result.confirm("Delete returns true if extn was set", extn.remove(oid_bc));
244 result.confirm("Basic constraints is not set", !extn.extension_set(oid_bc));
245 result.confirm("Basic constraints is not critical", !extn.critical_extension_set(oid_bc));
246
247 return result;
248 }
249
test_x509_dates()250 Test::Result test_x509_dates()
251 {
252 Test::Result result("X509 Time");
253
254 Botan::X509_Time time;
255 result.confirm("unset time not set", !time.time_is_set());
256 time = Botan::X509_Time("080201182200Z", Botan::ASN1_Tag::UTC_TIME);
257 result.confirm("time set after construction", time.time_is_set());
258 result.test_eq("time readable_string", time.readable_string(), "2008/02/01 18:22:00 UTC");
259
260 time = Botan::X509_Time("200305100350Z", Botan::ASN1_Tag::UTC_TIME);
261 result.test_eq("UTC_TIME readable_string", time.readable_string(), "2020/03/05 10:03:50 UTC");
262
263 time = Botan::X509_Time("200305100350Z", Botan::ASN1_Tag::UTC_OR_GENERALIZED_TIME);
264 result.test_eq("UTC_OR_GENERALIZED_TIME from UTC_TIME readable_string", time.readable_string(),
265 "2020/03/05 10:03:50 UTC");
266
267 time = Botan::X509_Time("20200305100350Z", Botan::ASN1_Tag::UTC_OR_GENERALIZED_TIME);
268 result.test_eq("UTC_OR_GENERALIZED_TIME from GENERALIZED_TIME readable_string", time.readable_string(),
269 "2020/03/05 10:03:50 UTC");
270
271 time = Botan::X509_Time("20200305100350Z", Botan::ASN1_Tag::GENERALIZED_TIME);
272 result.test_eq("GENERALIZED_TIME readable_string", time.readable_string(), "2020/03/05 10:03:50 UTC");
273
274 // Dates that are valid per X.500 but rejected as unsupported
275 const std::string valid_but_unsup[]
276 {
277 "0802010000-0000",
278 "0802011724+0000",
279 "0406142334-0500",
280 "9906142334+0500",
281 "0006142334-0530",
282 "0006142334+0530",
283
284 "080201000000-0000",
285 "080201172412+0000",
286 "040614233433-0500",
287 "990614233444+0500",
288 "000614233455-0530",
289 "000614233455+0530",
290 };
291
292 // valid length 13
293 const std::string valid_utc[]
294 {
295 "080201000000Z",
296 "080201172412Z",
297 "040614233433Z",
298 "990614233444Z",
299 "000614233455Z",
300 };
301
302 const std::string invalid_utc[]
303 {
304 "",
305 " ",
306 "2008`02-01",
307 "9999-02-01",
308 "2000-02-01 17",
309 "999921",
310
311 // No seconds
312 "0802010000Z",
313 "0802011724Z",
314 "0406142334Z",
315 "9906142334Z",
316 "0006142334Z",
317
318 // valid length 13 -> range check
319 "080201000061Z", // seconds too big (61)
320 "080201000060Z", // seconds too big (60, leap seconds not covered by the standard)
321 "0802010000-1Z", // seconds too small (-1)
322 "080201006000Z", // minutes too big (60)
323 "080201240000Z", // hours too big (24:00)
324
325 // valid length 13 -> invalid numbers
326 "08020123112 Z",
327 "08020123112!Z",
328 "08020123112,Z",
329 "08020123112\nZ",
330 "080201232 33Z",
331 "080201232!33Z",
332 "080201232,33Z",
333 "080201232\n33Z",
334 "0802012 3344Z",
335 "0802012!3344Z",
336 "0802012,3344Z",
337 "08022\n334455Z",
338 "08022 334455Z",
339 "08022!334455Z",
340 "08022,334455Z",
341 "08022\n334455Z",
342 "082 33445511Z",
343 "082!33445511Z",
344 "082,33445511Z",
345 "082\n33445511Z",
346 "2 2211221122Z",
347 "2!2211221122Z",
348 "2,2211221122Z",
349 "2\n2211221122Z",
350
351 // wrong time zone
352 "080201000000",
353 "080201000000z",
354
355 // Fractional seconds
356 "170217180154.001Z",
357
358 // Timezone offset
359 "170217180154+0100",
360
361 // Extra digits
362 "17021718015400Z",
363
364 // Non-digits
365 "17021718015aZ",
366
367 // Trailing garbage
368 "170217180154Zlongtrailinggarbage",
369
370 // Swapped type
371 "20170217180154Z",
372 };
373
374 // valid length 15
375 const std::string valid_generalized_time[]
376 {
377 "20000305100350Z",
378 };
379
380 const std::string invalid_generalized[]
381 {
382 // No trailing Z
383 "20000305100350",
384
385 // No seconds
386 "200003051003Z",
387
388 // Fractional seconds
389 "20000305100350.001Z",
390
391 // Timezone offset
392 "20170217180154+0100",
393
394 // Extra digits
395 "2017021718015400Z",
396
397 // Non-digits
398 "2017021718015aZ",
399
400 // Trailing garbage
401 "20170217180154Zlongtrailinggarbage",
402
403 // Swapped type
404 "170217180154Z",
405 };
406
407 for(const auto& v : valid_but_unsup)
408 {
409 result.test_throws("valid but unsupported", [v]() { Botan::X509_Time t(v, Botan::ASN1_Tag::UTC_TIME); });
410 }
411
412 for(const auto& v : valid_utc)
413 {
414 Botan::X509_Time t(v, Botan::ASN1_Tag::UTC_TIME);
415 }
416
417 for(const auto& v : valid_generalized_time)
418 {
419 Botan::X509_Time t(v, Botan::ASN1_Tag::GENERALIZED_TIME);
420 }
421
422 for(const auto& v : invalid_utc)
423 {
424 result.test_throws("invalid", [v]() { Botan::X509_Time t(v, Botan::ASN1_Tag::UTC_TIME); });
425 }
426
427 for(const auto& v : invalid_generalized)
428 {
429 result.test_throws("invalid", [v]() { Botan::X509_Time t(v, Botan::ASN1_Tag::GENERALIZED_TIME); });
430 }
431
432 return result;
433 }
434
435 #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
436
test_crl_dn_name()437 Test::Result test_crl_dn_name()
438 {
439 Test::Result result("CRL DN name");
440
441 // See GH #1252
442
443 #if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1)
444 const Botan::OID dc_oid("0.9.2342.19200300.100.1.25");
445
446 Botan::X509_Certificate cert(Test::data_file("x509/misc/opcuactt_ca.der"));
447
448 Botan::DataSource_Stream key_input(Test::data_file("x509/misc/opcuactt_ca.pem"));
449 std::unique_ptr<Botan::Private_Key> key = Botan::PKCS8::load_key(key_input);
450 Botan::X509_CA ca(cert, *key, "SHA-256", Test::rng());
451
452 Botan::X509_CRL crl = ca.new_crl(Test::rng());
453
454 result.confirm("matches issuer cert", crl.issuer_dn() == cert.subject_dn());
455
456 result.confirm("contains DC component",
457 crl.issuer_dn().get_attributes().count(dc_oid) == 1);
458 #endif
459
460 return result;
461 }
462
test_rdn_multielement_set_name()463 Test::Result test_rdn_multielement_set_name()
464 {
465 Test::Result result("DN with multiple elements in RDN");
466
467 // GH #2611
468
469 Botan::X509_Certificate cert(Test::data_file("x509/misc/rdn_set.crt"));
470
471 result.confirm("contains expected name components",
472 cert.issuer_dn().get_attributes().size() == 4);
473
474 return result;
475 }
476
test_rsa_oaep()477 Test::Result test_rsa_oaep()
478 {
479 Test::Result result("RSA OAEP decoding");
480
481 #if defined(BOTAN_HAS_RSA)
482 Botan::X509_Certificate cert(Test::data_file("x509/misc/rsa_oaep.pem"));
483
484 auto public_key = cert.load_subject_public_key();
485 result.test_not_null("Decoding RSA-OAEP worked", public_key.get());
486 auto pk_info = cert.subject_public_key_algo();
487
488 result.test_eq("RSA-OAEP OID", pk_info.get_oid().to_string(), Botan::OID::from_string("RSA/OAEP").to_string());
489 #endif
490
491 return result;
492 }
493
test_x509_decode_list()494 Test::Result test_x509_decode_list()
495 {
496 Test::Result result("X509_Certificate list decode");
497
498 Botan::DataSource_Stream input(Test::data_file("x509/misc/cert_seq.der"), true);
499
500 Botan::BER_Decoder dec(input);
501 std::vector<Botan::X509_Certificate> certs;
502 dec.decode_list(certs);
503
504 result.test_eq("Expected number of certs in list", certs.size(), 2);
505
506 result.test_eq("Expected cert 1 CN", certs[0].subject_dn().get_first_attribute("CN"), "CA1-PP.01.02");
507 result.test_eq("Expected cert 2 CN", certs[1].subject_dn().get_first_attribute("CN"), "User1-PP.01.02");
508
509 return result;
510 }
511
test_x509_utf8()512 Test::Result test_x509_utf8()
513 {
514 Test::Result result("X509 with UTF-8 encoded fields");
515
516 try
517 {
518 Botan::X509_Certificate utf8_cert(Test::data_file("x509/misc/contains_utf8string.pem"));
519
520 // UTF-8 encoded fields of test certificate (contains cyrillic letters)
521 const std::string organization =
522 "\xD0\x9C\xD0\xBE\xD1\x8F\x20\xD0\xBA\xD0\xBE\xD0"
523 "\xBC\xD0\xBF\xD0\xB0\xD0\xBD\xD0\xB8\xD1\x8F";
524 const std::string organization_unit =
525 "\xD0\x9C\xD0\xBE\xD1\x91\x20\xD0\xBF\xD0\xBE\xD0\xB4\xD1\x80\xD0\xB0"
526 "\xD0\xB7\xD0\xB4\xD0\xB5\xD0\xBB\xD0\xB5\xD0\xBD\xD0\xB8\xD0\xB5";
527 const std::string common_name =
528 "\xD0\x9E\xD0\xBF\xD0\xB8\xD1\x81\xD0\xB0\xD0\xBD\xD0\xB8"
529 "\xD0\xB5\x20\xD1\x81\xD0\xB0\xD0\xB9\xD1\x82\xD0\xB0";
530 const std::string location =
531 "\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0";
532
533 const Botan::X509_DN& issuer_dn = utf8_cert.issuer_dn();
534
535 result.test_eq("O", issuer_dn.get_first_attribute("O"), organization);
536 result.test_eq("OU", issuer_dn.get_first_attribute("OU"), organization_unit);
537 result.test_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
538 result.test_eq("L", issuer_dn.get_first_attribute("L"), location);
539 }
540 catch (const Botan::Decoding_Error &ex)
541 {
542 result.test_failure(ex.what());
543 }
544
545 return result;
546 }
547
test_x509_bmpstring()548 Test::Result test_x509_bmpstring()
549 {
550 Test::Result result("X509 with UCS-2 (BMPString) encoded fields");
551
552 try
553 {
554 Botan::X509_Certificate ucs2_cert(Test::data_file("x509/misc/contains_bmpstring.pem"));
555
556 // UTF-8 encoded fields of test certificate (contains cyrillic and greek letters)
557 const std::string organization =
558 "\x6E\x65\xCF\x87\xCF\xB5\x6E\x69\xCF\x89";
559 const std::string common_name =
560 "\xC3\xA8\x6E\xC7\x9D\xD0\xAF\x20\xD0\x9C\xC7\x9D\xD0\xB9\xD0\xB7\xD1\x8D\xD0\xBB";
561
562 // UTF-8 encoded fields of test certificate (contains only ASCII characters)
563 const std::string location = "Berlin";
564
565 const Botan::X509_DN& issuer_dn = ucs2_cert.issuer_dn();
566
567 result.test_eq("O", issuer_dn.get_first_attribute("O"), organization);
568 result.test_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
569 result.test_eq("L", issuer_dn.get_first_attribute("L"), location);
570 }
571 catch (const Botan::Decoding_Error &ex)
572 {
573 result.test_failure(ex.what());
574 }
575
576 return result;
577 }
578
test_x509_authority_info_access_extension()579 Test::Result test_x509_authority_info_access_extension()
580 {
581 Test::Result result("X509 with PKIX.AuthorityInformationAccess extension");
582
583 // contains no AIA extension
584 Botan::X509_Certificate no_aia_cert(Test::data_file("x509/misc/contains_utf8string.pem"));
585
586 result.test_eq("number of ca_issuers URLs", no_aia_cert.ca_issuers().size(), 0);
587 result.test_eq("CA issuer URL matches", no_aia_cert.ocsp_responder(), "");
588
589 // contains AIA extension with 1 CA issuer URL and 1 OCSP responder
590 Botan::X509_Certificate aia_cert(Test::data_file("x509/misc/contains_authority_info_access.pem"));
591
592 const auto ca_issuers = aia_cert.ca_issuers();
593
594 result.test_eq("number of ca_issuers URLs", ca_issuers.size(), 1);
595 if (result.tests_failed())
596 return result;
597
598 result.test_eq("CA issuer URL matches", ca_issuers[0], "http://gp.symcb.com/gp.crt");
599 result.test_eq("OCSP responder URL matches", aia_cert.ocsp_responder(), "http://gp.symcd.com");
600
601 // contains AIA extension with 2 CA issuer URL and 1 OCSP responder
602 Botan::X509_Certificate aia_cert_2ca(Test::data_file("x509/misc/contains_authority_info_access_with_two_ca_issuers.pem"));
603
604 const auto ca_issuers2 = aia_cert_2ca.ca_issuers();
605
606 result.test_eq("number of ca_issuers URLs", ca_issuers2.size(), 2);
607 if (result.tests_failed())
608 return result;
609
610 result.test_eq("CA issuer URL matches", ca_issuers2[0], "http://www.d-trust.net/cgi-bin/Bdrive_Test_CA_1-2_2017.crt");
611 result.test_eq("CA issuer URL matches", ca_issuers2[1], "ldap://directory.d-trust.net/CN=Bdrive%20Test%20CA%201-2%202017,O=Bundesdruckerei%20GmbH,C=DE?cACertificate?base?");
612 result.test_eq("OCSP responder URL matches", aia_cert_2ca.ocsp_responder(), "http://staging.ocsp.d-trust.net");
613
614 return result;
615 }
616
test_verify_gost2012_cert()617 Test::Result test_verify_gost2012_cert()
618 {
619 Test::Result result("X509 GOST-2012 certificates");
620
621 #if defined(BOTAN_HAS_GOST_34_10_2012) && defined(BOTAN_HAS_STREEBOG) && defined(BOTAN_HAS_EMSA1)
622 try
623 {
624 Botan::X509_Certificate root_cert(Test::data_file("x509/gost/gost_root.pem"));
625 Botan::X509_Certificate root_int(Test::data_file("x509/gost/gost_int.pem"));
626
627 Botan::Certificate_Store_In_Memory trusted;
628 trusted.add_certificate(root_cert);
629
630 const Botan::Path_Validation_Restrictions restrictions(false, 128, false, {"Streebog-256"});
631 const Botan::Path_Validation_Result validation_result = Botan::x509_path_validate(root_int, restrictions, trusted);
632
633 result.confirm("GOST certificate validates", validation_result.successful_validation());
634 }
635 catch(const Botan::Decoding_Error& e)
636 {
637 result.test_failure(e.what());
638 }
639 #endif
640
641 return result;
642 }
643
644 /*
645 * @brief checks the configurability of the EMSA4(RSA-PSS) signature scheme
646 *
647 * For the other algorithms than RSA, only one padding is supported right now.
648 */
649 #if defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && defined(BOTAN_HAS_RSA)
test_padding_config()650 Test::Result test_padding_config() {
651 // Throughout the test, some synonyms for EMSA4 are used, e.g. PSSR, EMSA-PSS
652 Test::Result test_result("X509 Padding Config");
653
654 std::unique_ptr<Botan::Private_Key> sk(Botan::PKCS8::load_key(
655 Test::data_file("x509/misc/rsa_key.pem"), Test::rng()));
656
657 // Create X509 CA certificate; EMSA3 is used for signing by default
658 Botan::X509_Cert_Options opt("TESTCA");
659 opt.CA_key();
660
661 Botan::X509_Certificate ca_cert_def = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", Test::rng());
662 test_result.test_eq("CA certificate signature algorithm (default)",
663 Botan::OIDS::oid2str_or_throw(ca_cert_def.signature_algorithm().get_oid()),"RSA/EMSA3(SHA-512)");
664
665 // Create X509 CA certificate; RSA-PSS is explicitly set
666 opt.set_padding_scheme("PSSR");
667 Botan::X509_Certificate ca_cert_exp = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", Test::rng());
668 test_result.test_eq("CA certificate signature algorithm (explicit)",
669 Botan::OIDS::oid2str_or_throw(ca_cert_exp.signature_algorithm().get_oid()),"RSA/EMSA4");
670
671 #if defined(BOTAN_HAS_EMSA1)
672
673 // Try to set a padding scheme that is not supported for signing with the given key type
674 opt.set_padding_scheme("EMSA1");
675 try
676 {
677 Botan::X509_Certificate ca_cert_wrong = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", Test::rng());
678 test_result.test_failure("Could build CA cert with invalid encoding scheme EMSA1 for key type " + sk->algo_name());
679 }
680 catch (const Botan::Invalid_Argument& e)
681 {
682 test_result.test_eq("Build CA certitiface with invalid encoding scheme EMSA1 for key type " +
683 sk->algo_name(), e.what(),
684 "Encoding scheme with canonical name EMSA1 not supported for signature algorithm RSA");
685 }
686 #endif
687
688 test_result.test_eq("CA certificate signature algorithm (explicit)",
689 Botan::OIDS::oid2str_or_throw(ca_cert_exp.signature_algorithm().get_oid()),"RSA/EMSA4");
690
691 const Botan::X509_Time not_before = from_date(-1, 1, 1);
692 const Botan::X509_Time not_after = from_date(2, 1, 2);
693
694 // Prepare a signing request for the end certificate
695 Botan::X509_Cert_Options req_opt("endpoint");
696 req_opt.set_padding_scheme("EMSA4(SHA-512,MGF1,64)");
697 Botan::PKCS10_Request end_req = Botan::X509::create_cert_req(req_opt, (*sk), "SHA-512", Test::rng());
698 test_result.test_eq("Certificate request signature algorithm", Botan::OIDS::oid2str_or_throw(end_req.signature_algorithm().get_oid()),"RSA/EMSA4");
699
700 // Create X509 CA object: will fail as the chosen hash functions differ
701 try
702 {
703 Botan::X509_CA ca_fail(ca_cert_exp, (*sk), {{"padding","EMSA4(SHA-256)"}},"SHA-512", Test::rng());
704 test_result.test_failure("Configured conflicting hash functions for CA");
705 }
706 catch(const Botan::Invalid_Argument& e)
707 {
708 test_result.test_eq("Configured conflicting hash functions for CA",
709 e.what(),
710 "Hash function from opts and hash_fn argument need to be identical");
711 }
712
713 // Create X509 CA object: its signer will use the padding scheme from the CA certificate, i.e. EMSA3
714 Botan::X509_CA ca_def(ca_cert_def, (*sk), "SHA-512", Test::rng());
715 Botan::X509_Certificate end_cert_emsa3 = ca_def.sign_request(end_req, Test::rng(), not_before, not_after);
716 test_result.test_eq("End certificate signature algorithm", Botan::OIDS::oid2str_or_throw(end_cert_emsa3.signature_algorithm().get_oid()), "RSA/EMSA3(SHA-512)");
717
718 // Create X509 CA object: its signer will use the explicitly configured padding scheme, which is different from the CA certificate's scheme
719 Botan::X509_CA ca_diff(ca_cert_def, (*sk), {{"padding","EMSA-PSS"}}, "SHA-512", Test::rng());
720 Botan::X509_Certificate end_cert_diff_emsa4 = ca_diff.sign_request(end_req, Test::rng(), not_before, not_after);
721 test_result.test_eq("End certificate signature algorithm", Botan::OIDS::oid2str_or_throw(end_cert_diff_emsa4.signature_algorithm().get_oid()), "RSA/EMSA4");
722
723 // Create X509 CA object: its signer will use the explicitly configured padding scheme, which is identical to the CA certificate's scheme
724 Botan::X509_CA ca_exp(ca_cert_exp, (*sk), {{"padding","EMSA4(SHA-512,MGF1,64)"}},"SHA-512", Test::rng());
725 Botan::X509_Certificate end_cert_emsa4= ca_exp.sign_request(end_req, Test::rng(), not_before, not_after);
726 test_result.test_eq("End certificate signature algorithm", Botan::OIDS::oid2str_or_throw(end_cert_emsa4.signature_algorithm().get_oid()), "RSA/EMSA4");
727
728 // Check CRL signature algorithm
729 Botan::X509_CRL crl = ca_exp.new_crl(Test::rng());
730 test_result.test_eq("CRL signature algorithm", Botan::OIDS::oid2str_or_throw(crl.signature_algorithm().get_oid()), "RSA/EMSA4");
731
732 // sanity check for verification, the heavy lifting is done in the other unit tests
733 const Botan::Certificate_Store_In_Memory trusted(ca_exp.ca_certificate());
734 const Botan::Path_Validation_Restrictions restrictions(false, 80);
735 const Botan::Path_Validation_Result validation_result = Botan::x509_path_validate(
736 end_cert_emsa4, restrictions, trusted);
737 test_result.confirm("EMSA4-signed certificate validates", validation_result.successful_validation());
738
739 return test_result;
740 }
741 #endif
742
743 #endif
744
test_pkcs10_ext(const Botan::Private_Key & key,const std::string & sig_padding,const std::string & hash_fn="SHA-256")745 Test::Result test_pkcs10_ext(const Botan::Private_Key& key,
746 const std::string& sig_padding,
747 const std::string& hash_fn = "SHA-256")
748 {
749 Test::Result result("PKCS10 extensions");
750
751 Botan::X509_Cert_Options opts;
752
753 opts.padding_scheme = sig_padding;
754
755 Botan::AlternativeName alt_name;
756 alt_name.add_attribute("DNS", "example.org");
757 alt_name.add_attribute("DNS", "example.com");
758 alt_name.add_attribute("DNS", "example.net");
759
760 opts.extensions.add(new Botan::Cert_Extension::Subject_Alternative_Name(alt_name));
761
762 Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, key, hash_fn, Test::rng());
763
764 std::vector<std::string> alt_dns_names = req.subject_alt_name().get_attribute("DNS");
765
766 result.test_eq("Expected number of DNS names", alt_dns_names.size(), 3);
767
768 // The order is not guaranteed so sort before comparing
769 std::sort(alt_dns_names.begin(), alt_dns_names.end());
770
771 result.test_eq("Expected DNS name 1", alt_dns_names.at(0), "example.com");
772 result.test_eq("Expected DNS name 2", alt_dns_names.at(1), "example.net");
773 result.test_eq("Expected DNS name 3", alt_dns_names.at(2), "example.org");
774
775 return result;
776 }
777
test_x509_cert(const Botan::Private_Key & ca_key,const std::string & sig_algo,const std::string & sig_padding="",const std::string & hash_fn="SHA-256")778 Test::Result test_x509_cert(const Botan::Private_Key& ca_key,
779 const std::string& sig_algo,
780 const std::string& sig_padding = "",
781 const std::string& hash_fn = "SHA-256")
782 {
783 Test::Result result("X509 Unit");
784
785 /* Create the self-signed cert */
786 const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, Test::rng());
787
788 {
789 const auto constraints = Botan::Key_Constraints(Botan::KEY_CERT_SIGN | Botan::CRL_SIGN);
790 result.test_eq("ca key usage", (ca_cert.constraints() & constraints) == constraints, true);
791 }
792
793 /* Create user #1's key and cert request */
794 std::unique_ptr<Botan::Private_Key> user1_key(make_a_private_key(sig_algo));
795
796 Botan::PKCS10_Request user1_req =
797 Botan::X509::create_cert_req(req_opts1(sig_algo, sig_padding),
798 *user1_key,
799 hash_fn,
800 Test::rng());
801
802 result.test_eq("PKCS10 challenge password parsed",
803 user1_req.challenge_password(), "zoom");
804
805 /* Create user #2's key and cert request */
806 std::unique_ptr<Botan::Private_Key> user2_key(make_a_private_key(sig_algo));
807
808 Botan::PKCS10_Request user2_req =
809 Botan::X509::create_cert_req(req_opts2(sig_padding),
810 *user2_key,
811 hash_fn,
812 Test::rng());
813
814 // /* Create user #3's key and cert request */
815 std::unique_ptr<Botan::Private_Key> user3_key(make_a_private_key(sig_algo));
816
817 Botan::PKCS10_Request user3_req =
818 Botan::X509::create_cert_req(req_opts3(sig_padding),
819 *user3_key,
820 hash_fn,
821 Test::rng());
822
823 /* Create the CA object */
824 Botan::X509_CA ca(ca_cert, ca_key, {{"padding",sig_padding}}, hash_fn, Test::rng());
825
826 const BigInt user1_serial = 99;
827
828 /* Sign the requests to create the certs */
829 Botan::X509_Certificate user1_cert =
830 ca.sign_request(user1_req, Test::rng(), user1_serial,
831 from_date(-1, 01, 01),
832 from_date(2, 01, 01));
833
834 result.test_eq("User1 serial size matches expected", user1_cert.serial_number().size(), 1);
835 result.test_eq("User1 serial matches expected", user1_cert.serial_number().at(0), size_t(99));
836
837 Botan::X509_Certificate user2_cert =
838 ca.sign_request(user2_req, Test::rng(),
839 from_date(-1, 01, 01),
840 from_date(2, 01, 01));
841
842 Botan::X509_Certificate user3_cert =
843 ca.sign_request(user3_req, Test::rng(),
844 from_date(-1, 01, 01),
845 from_date(2, 01, 01));
846
847 // user#1 creates a self-signed cert on the side
848 const auto user1_ss_cert =
849 Botan::X509::create_self_signed_cert(req_opts1(sig_algo, sig_padding), *user1_key, hash_fn, Test::rng());
850
851 {
852 auto constrains = req_opts1(sig_algo).constraints;
853 result.test_eq("user1 key usage", (user1_cert.constraints() & constrains) == constrains, true);
854 }
855
856 /* Copy, assign and compare */
857 Botan::X509_Certificate user1_cert_copy(user1_cert);
858 result.test_eq("certificate copy", user1_cert == user1_cert_copy, true);
859
860 user1_cert_copy = user2_cert;
861 result.test_eq("certificate assignment", user2_cert == user1_cert_copy, true);
862
863 Botan::X509_Certificate user1_cert_differ =
864 ca.sign_request(user1_req, Test::rng(),
865 from_date(-1, 01, 01),
866 from_date(2, 01, 01));
867
868 result.test_eq("certificate differs", user1_cert == user1_cert_differ, false);
869
870 /* Get cert data */
871 result.test_eq("x509 version", user1_cert.x509_version(), size_t(3));
872
873 const Botan::X509_DN& user1_issuer_dn = user1_cert.issuer_dn();
874 result.test_eq("issuer info CN", user1_issuer_dn.get_first_attribute("CN"), ca_opts().common_name);
875 result.test_eq("issuer info Country", user1_issuer_dn.get_first_attribute("C"), ca_opts().country);
876 result.test_eq("issuer info Orga", user1_issuer_dn.get_first_attribute("O"), ca_opts().organization);
877 result.test_eq("issuer info OrgaUnit", user1_issuer_dn.get_first_attribute("OU"), ca_opts().org_unit);
878
879 const Botan::X509_DN& user3_subject_dn = user3_cert.subject_dn();
880 result.test_eq("subject OrgaUnit count", user3_subject_dn.get_attribute("OU").size(), req_opts3(sig_algo).more_org_units.size() + 1);
881 result.test_eq("subject OrgaUnit #2", user3_subject_dn.get_attribute("OU").at(1), req_opts3(sig_algo).more_org_units.at(0));
882
883 const Botan::AlternativeName& user1_altname = user1_cert.subject_alt_name();
884 result.test_eq("subject alt email", user1_altname.get_first_attribute("RFC822"), "testing@randombit.net");
885 result.test_eq("subject alt dns", user1_altname.get_first_attribute("DNS"), "botan.randombit.net");
886 result.test_eq("subject alt uri", user1_altname.get_first_attribute("URI"), "https://botan.randombit.net");
887
888 const Botan::AlternativeName& user3_altname = user3_cert.subject_alt_name();
889 result.test_eq("subject alt dns count", user3_altname.get_attribute("DNS").size(), req_opts3(sig_algo).more_dns.size() + 1);
890 result.test_eq("subject alt dns #2", user3_altname.get_attribute("DNS").at(1), req_opts3(sig_algo).more_dns.at(0));
891
892 const Botan::X509_CRL crl1 = ca.new_crl(Test::rng());
893
894 /* Verify the certs */
895 Botan::Path_Validation_Restrictions restrictions(false, 80);
896 Botan::Certificate_Store_In_Memory store;
897
898 // First try with an empty store
899 Botan::Path_Validation_Result result_no_issuer = Botan::x509_path_validate(user1_cert, restrictions, store);
900 result.test_eq("user 1 issuer not found",
901 result_no_issuer.result_string(),
902 Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND));
903
904 store.add_certificate(ca.ca_certificate());
905
906 Botan::Path_Validation_Result result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
907 if(!result.confirm("user 1 validates", result_u1.successful_validation()))
908 {
909 result.test_note("user 1 validation result was " + result_u1.result_string());
910 }
911 else
912 {
913 const std::set<std::string> u1_hashes = result_u1.trusted_hashes();
914
915 if(result.test_eq("Single trusted hash", u1_hashes.size(), 1))
916 {
917 result.test_eq("Hash matches test", *u1_hashes.begin(), hash_fn);
918 }
919 }
920
921 Botan::Path_Validation_Result result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
922 if(!result.confirm("user 2 validates", result_u2.successful_validation()))
923 {
924 result.test_note("user 2 validation result was " + result_u2.result_string());
925 }
926
927 Botan::Path_Validation_Result result_self_signed = Botan::x509_path_validate(user1_ss_cert, restrictions, store);
928 result.test_eq("user 1 issuer not found",
929 result_no_issuer.result_string(),
930 Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND));
931 store.add_crl(crl1);
932
933 std::vector<Botan::CRL_Entry> revoked;
934 revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::CESSATION_OF_OPERATION));
935 revoked.push_back(user2_cert);
936
937 const Botan::X509_CRL crl2 = ca.update_crl(crl1, revoked, Test::rng());
938
939 store.add_crl(crl2);
940
941 const std::string revoked_str =
942 Botan::Path_Validation_Result::status_string(Botan::Certificate_Status_Code::CERT_IS_REVOKED);
943
944 result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
945 result.test_eq("user 1 revoked", result_u1.result_string(), revoked_str);
946
947 result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
948 result.test_eq("user 1 revoked", result_u2.result_string(), revoked_str);
949
950 revoked.clear();
951 revoked.push_back(Botan::CRL_Entry(user1_cert, Botan::REMOVE_FROM_CRL));
952 Botan::X509_CRL crl3 = ca.update_crl(crl2, revoked, Test::rng());
953
954 store.add_crl(crl3);
955
956 result_u1 = Botan::x509_path_validate(user1_cert, restrictions, store);
957 if(!result.confirm("user 1 validates", result_u1.successful_validation()))
958 {
959 result.test_note("user 1 validation result was " + result_u1.result_string());
960 }
961
962 result_u2 = Botan::x509_path_validate(user2_cert, restrictions, store);
963 result.test_eq("user 2 still revoked", result_u2.result_string(), revoked_str);
964
965 return result;
966 }
967
test_usage(const Botan::Private_Key & ca_key,const std::string & sig_algo,const std::string & hash_fn="SHA-256")968 Test::Result test_usage(const Botan::Private_Key& ca_key,
969 const std::string& sig_algo,
970 const std::string& hash_fn = "SHA-256")
971 {
972 using Botan::Key_Constraints;
973 using Botan::Usage_Type;
974
975 Test::Result result("X509 Usage");
976
977 /* Create the self-signed cert */
978 const Botan::X509_Certificate ca_cert = Botan::X509::create_self_signed_cert(
979 ca_opts(),
980 ca_key,
981 hash_fn,
982 Test::rng());
983
984 /* Create the CA object */
985 const Botan::X509_CA ca(ca_cert, ca_key, hash_fn, Test::rng());
986
987 std::unique_ptr<Botan::Private_Key> user1_key(make_a_private_key(sig_algo));
988
989 Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
990 opts.constraints = Key_Constraints::DIGITAL_SIGNATURE;
991
992 const Botan::PKCS10_Request user1_req = Botan::X509::create_cert_req(
993 opts,
994 *user1_key,
995 hash_fn,
996 Test::rng());
997
998 const Botan::X509_Certificate user1_cert = ca.sign_request(
999 user1_req,
1000 Test::rng(),
1001 from_date(-1, 01, 01),
1002 from_date(2, 01, 01));
1003
1004 // cert only allows digitalSignature, but we check for both digitalSignature and cRLSign
1005 result.test_eq("key usage cRLSign not allowed",
1006 user1_cert.allowed_usage(
1007 Key_Constraints(Key_Constraints::DIGITAL_SIGNATURE | Key_Constraints::CRL_SIGN)), false);
1008 result.test_eq("encryption is not allowed",
1009 user1_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
1010
1011 // cert only allows digitalSignature, so checking for only that should be ok
1012 result.confirm("key usage digitalSignature allowed", user1_cert.allowed_usage(Key_Constraints::DIGITAL_SIGNATURE));
1013
1014 opts.constraints = Key_Constraints(Key_Constraints::DIGITAL_SIGNATURE | Key_Constraints::CRL_SIGN);
1015
1016 const Botan::PKCS10_Request mult_usage_req = Botan::X509::create_cert_req(
1017 opts,
1018 *user1_key,
1019 hash_fn,
1020 Test::rng());
1021
1022 const Botan::X509_Certificate mult_usage_cert = ca.sign_request(
1023 mult_usage_req,
1024 Test::rng(),
1025 from_date(-1, 01, 01),
1026 from_date(2, 01, 01));
1027
1028 // cert allows multiple usages, so each one of them as well as both together should be allowed
1029 result.confirm("key usage multiple digitalSignature allowed",
1030 mult_usage_cert.allowed_usage(Key_Constraints::DIGITAL_SIGNATURE));
1031 result.confirm("key usage multiple cRLSign allowed", mult_usage_cert.allowed_usage(Key_Constraints::CRL_SIGN));
1032 result.confirm("key usage multiple digitalSignature and cRLSign allowed", mult_usage_cert.allowed_usage(
1033 Key_Constraints(Key_Constraints::DIGITAL_SIGNATURE | Key_Constraints::CRL_SIGN)));
1034 result.test_eq("encryption is not allowed",
1035 mult_usage_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
1036
1037
1038 opts.constraints = Key_Constraints::NO_CONSTRAINTS;
1039
1040 const Botan::PKCS10_Request no_usage_req = Botan::X509::create_cert_req(opts, *user1_key, hash_fn, Test::rng());
1041
1042 const Botan::X509_Certificate no_usage_cert =
1043 ca.sign_request(no_usage_req, Test::rng(),
1044 from_date(-1, 01, 01),
1045 from_date(2, 01, 01));
1046
1047 // cert allows every usage
1048 result.confirm("key usage digitalSignature allowed", no_usage_cert.allowed_usage(Key_Constraints::DIGITAL_SIGNATURE));
1049 result.confirm("key usage cRLSign allowed", no_usage_cert.allowed_usage(Key_Constraints::CRL_SIGN));
1050 result.confirm("key usage encryption allowed", no_usage_cert.allowed_usage(Usage_Type::ENCRYPTION));
1051
1052 if (sig_algo == "RSA")
1053 {
1054 // cert allows data encryption
1055 opts.constraints = Key_Constraints(Key_Constraints::KEY_ENCIPHERMENT | Key_Constraints::DATA_ENCIPHERMENT);
1056
1057 const Botan::PKCS10_Request enc_req = Botan::X509::create_cert_req(
1058 opts,
1059 *user1_key,
1060 hash_fn,
1061 Test::rng());
1062
1063 const Botan::X509_Certificate enc_cert = ca.sign_request(
1064 enc_req,
1065 Test::rng(),
1066 from_date(-1, 01, 01),
1067 from_date(2, 01, 01));
1068
1069 result.confirm("cert allows encryption", enc_cert.allowed_usage(Usage_Type::ENCRYPTION));
1070 result.confirm("cert does not allow TLS client auth", !enc_cert.allowed_usage(Usage_Type::TLS_CLIENT_AUTH));
1071 }
1072
1073 return result;
1074 }
1075
test_self_issued(const Botan::Private_Key & ca_key,const std::string & sig_algo,const std::string & sig_padding="",const std::string & hash_fn="SHA-256")1076 Test::Result test_self_issued(const Botan::Private_Key& ca_key,
1077 const std::string& sig_algo,
1078 const std::string& sig_padding = "",
1079 const std::string& hash_fn = "SHA-256")
1080 {
1081 using Botan::Key_Constraints;
1082
1083 Test::Result result("X509 Self Issued");
1084
1085 // create the self-signed cert
1086 const Botan::X509_Certificate ca_cert = Botan::X509::create_self_signed_cert(
1087 ca_opts(sig_padding), ca_key, hash_fn, Test::rng());
1088
1089 /* Create the CA object */
1090 const Botan::X509_CA ca(ca_cert, ca_key, {{"padding",sig_padding}}, hash_fn, Test::rng());
1091
1092 std::unique_ptr<Botan::Private_Key> user_key(make_a_private_key(sig_algo));
1093
1094 // create a self-issued certificate, that is, a certificate with subject dn == issuer dn,
1095 // but signed by a CA, not signed by it's own private key
1096 Botan::X509_Cert_Options opts = ca_opts();
1097 opts.constraints = Key_Constraints::DIGITAL_SIGNATURE;
1098 opts.set_padding_scheme(sig_padding);
1099
1100 const Botan::PKCS10_Request self_issued_req = Botan::X509::create_cert_req(opts, *user_key, hash_fn, Test::rng());
1101
1102 const Botan::X509_Certificate self_issued_cert = ca.sign_request(
1103 self_issued_req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
1104
1105 // check that this chain can can be verified successfully
1106 const Botan::Certificate_Store_In_Memory trusted(ca.ca_certificate());
1107
1108 const Botan::Path_Validation_Restrictions restrictions(false, 80);
1109
1110 const Botan::Path_Validation_Result validation_result = Botan::x509_path_validate(
1111 self_issued_cert, restrictions, trusted);
1112
1113 result.confirm("chain with self-issued cert validates", validation_result.successful_validation());
1114
1115 return result;
1116 }
1117
test_x509_uninit()1118 Test::Result test_x509_uninit()
1119 {
1120 Test::Result result("X509 object uninitialized access");
1121
1122 Botan::X509_Certificate cert;
1123 result.test_throws("uninitialized cert access causes exception",
1124 "X509_Certificate uninitialized",
1125 [&cert]() { cert.x509_version(); });
1126
1127 Botan::X509_CRL crl;
1128 result.test_throws("uninitialized crl access causes exception",
1129 "X509_CRL uninitialized",
1130 [&crl]() { crl.crl_number(); });
1131
1132 return result;
1133 }
1134
1135
1136 using Botan::Key_Constraints;
1137
1138 /**
1139 * @brief Some typical key usage scenarios (taken from RFC 5280, sec. 4.2.1.3)
1140 */
1141 struct typical_usage_constraints
1142 {
1143 // ALL constraints are not typical at all, but we use them for a negative test
1144 Key_Constraints all = Key_Constraints(
1145 Key_Constraints::DIGITAL_SIGNATURE |
1146 Key_Constraints::NON_REPUDIATION |
1147 Key_Constraints::KEY_ENCIPHERMENT |
1148 Key_Constraints::DATA_ENCIPHERMENT |
1149 Key_Constraints::KEY_AGREEMENT |
1150 Key_Constraints::KEY_CERT_SIGN |
1151 Key_Constraints::CRL_SIGN |
1152 Key_Constraints::ENCIPHER_ONLY |
1153 Key_Constraints::DECIPHER_ONLY);
1154
1155 Key_Constraints ca = Key_Constraints(Key_Constraints::KEY_CERT_SIGN);
1156 Key_Constraints sign_data = Key_Constraints(Key_Constraints::DIGITAL_SIGNATURE);
1157 Key_Constraints non_repudiation = Key_Constraints(
1158 Key_Constraints::NON_REPUDIATION |
1159 Key_Constraints::DIGITAL_SIGNATURE);
1160 Key_Constraints key_encipherment = Key_Constraints(Key_Constraints::KEY_ENCIPHERMENT);
1161 Key_Constraints data_encipherment = Key_Constraints(Key_Constraints::DATA_ENCIPHERMENT);
1162 Key_Constraints key_agreement = Key_Constraints(Key_Constraints::KEY_AGREEMENT);
1163 Key_Constraints key_agreement_encipher_only = Key_Constraints(
1164 Key_Constraints::KEY_AGREEMENT |
1165 Key_Constraints::ENCIPHER_ONLY);
1166 Key_Constraints key_agreement_decipher_only = Key_Constraints(
1167 Key_Constraints::KEY_AGREEMENT |
1168 Key_Constraints::DECIPHER_ONLY);
1169 Key_Constraints crl_sign = Key_Constraints::CRL_SIGN;
1170 Key_Constraints sign_everything = Key_Constraints(
1171 Key_Constraints::DIGITAL_SIGNATURE |
1172 Key_Constraints::KEY_CERT_SIGN |
1173 Key_Constraints::CRL_SIGN);
1174 };
1175
1176
test_valid_constraints(const Botan::Private_Key & key,const std::string & pk_algo)1177 Test::Result test_valid_constraints(const Botan::Private_Key& key,
1178 const std::string& pk_algo)
1179 {
1180 Test::Result result("X509 Valid Constraints");
1181
1182 // should not throw on empty constraints
1183 verify_cert_constraints_valid_for_key_type(key, Key_Constraints(Key_Constraints::NO_CONSTRAINTS));
1184
1185 // now check some typical usage scenarios for the given key type
1186 typical_usage_constraints typical_usage;
1187
1188 if(pk_algo == "DH" || pk_algo == "ECDH")
1189 {
1190 // DH and ECDH only for key agreement
1191 result.test_throws("all constraints not permitted", [&key, &typical_usage]()
1192 {
1193 verify_cert_constraints_valid_for_key_type(key, typical_usage.all);
1194 });
1195 result.test_throws("cert sign not permitted", [&key, &typical_usage]()
1196 {
1197 verify_cert_constraints_valid_for_key_type(key, typical_usage.ca);
1198 });
1199 result.test_throws("signature not permitted", [&key, &typical_usage]()
1200 {
1201 verify_cert_constraints_valid_for_key_type(key, typical_usage.sign_data);
1202 });
1203 result.test_throws("non repudiation not permitted", [&key, &typical_usage]()
1204 {
1205 verify_cert_constraints_valid_for_key_type(key, typical_usage.non_repudiation);
1206 });
1207 result.test_throws("key encipherment not permitted", [&key, &typical_usage]()
1208 {
1209 verify_cert_constraints_valid_for_key_type(key, typical_usage.key_encipherment);
1210 });
1211 result.test_throws("data encipherment not permitted", [&key, &typical_usage]()
1212 {
1213 verify_cert_constraints_valid_for_key_type(key, typical_usage.data_encipherment);
1214 });
1215
1216 verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement);
1217 verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_encipher_only);
1218 verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_decipher_only);
1219
1220 result.test_throws("crl sign not permitted", [&key, &typical_usage]()
1221 {
1222 verify_cert_constraints_valid_for_key_type(key, typical_usage.crl_sign);
1223 });
1224 result.test_throws("sign, cert sign, crl sign not permitted", [&key, &typical_usage]()
1225 {
1226 verify_cert_constraints_valid_for_key_type(key, typical_usage.sign_everything);
1227 });
1228 }
1229 else if(pk_algo == "RSA")
1230 {
1231 // RSA can do everything except key agreement
1232 result.test_throws("all constraints not permitted", [&key, &typical_usage]()
1233 {
1234 verify_cert_constraints_valid_for_key_type(key, typical_usage.all);
1235 });
1236
1237 verify_cert_constraints_valid_for_key_type(key, typical_usage.ca);
1238 verify_cert_constraints_valid_for_key_type(key, typical_usage.sign_data);
1239 verify_cert_constraints_valid_for_key_type(key, typical_usage.non_repudiation);
1240 verify_cert_constraints_valid_for_key_type(key, typical_usage.key_encipherment);
1241 verify_cert_constraints_valid_for_key_type(key, typical_usage.data_encipherment);
1242
1243 result.test_throws("key agreement not permitted", [&key, &typical_usage]()
1244 {
1245 verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement);
1246 });
1247 result.test_throws("key agreement, encipher only not permitted", [&key, &typical_usage]()
1248 {
1249 verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_encipher_only);
1250 });
1251 result.test_throws("key agreement, decipher only not permitted", [&key, &typical_usage]()
1252 {
1253 verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_decipher_only);
1254 });
1255
1256 verify_cert_constraints_valid_for_key_type(key, typical_usage.crl_sign);
1257 verify_cert_constraints_valid_for_key_type(key, typical_usage.sign_everything);
1258 }
1259 else if(pk_algo == "ElGamal")
1260 {
1261 // only ElGamal encryption is currently implemented
1262 result.test_throws("all constraints not permitted", [&key, &typical_usage]()
1263 {
1264 verify_cert_constraints_valid_for_key_type(key, typical_usage.all);
1265 });
1266 result.test_throws("cert sign not permitted", [&key, &typical_usage]()
1267 {
1268 verify_cert_constraints_valid_for_key_type(key, typical_usage.ca);
1269 });
1270
1271 verify_cert_constraints_valid_for_key_type(key, typical_usage.data_encipherment);
1272 verify_cert_constraints_valid_for_key_type(key, typical_usage.key_encipherment);
1273
1274 result.test_throws("key agreement not permitted", [&key, &typical_usage]()
1275 {
1276 verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement);
1277 });
1278 result.test_throws("key agreement, encipher only not permitted", [&key, &typical_usage]()
1279 {
1280 verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_encipher_only);
1281 });
1282 result.test_throws("key agreement, decipher only not permitted", [&key, &typical_usage]()
1283 {
1284 verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_decipher_only);
1285 });
1286 result.test_throws("crl sign not permitted", [&key, &typical_usage]()
1287 {
1288 verify_cert_constraints_valid_for_key_type(key, typical_usage.crl_sign);
1289 });
1290 result.test_throws("sign, cert sign, crl sign not permitted not permitted", [&key, &typical_usage]()
1291 {
1292 verify_cert_constraints_valid_for_key_type(key, typical_usage.sign_everything);
1293 });
1294 }
1295 else if(pk_algo == "DSA" || pk_algo == "ECDSA" || pk_algo == "ECGDSA" || pk_algo == "ECKCDSA" ||
1296 pk_algo == "GOST-34.10")
1297 {
1298 // these are signature algorithms only
1299 result.test_throws("all constraints not permitted", [&key, &typical_usage]()
1300 {
1301 verify_cert_constraints_valid_for_key_type(key, typical_usage.all);
1302 });
1303
1304 verify_cert_constraints_valid_for_key_type(key, typical_usage.ca);
1305 verify_cert_constraints_valid_for_key_type(key, typical_usage.sign_data);
1306 verify_cert_constraints_valid_for_key_type(key, typical_usage.non_repudiation);
1307
1308 result.test_throws("key encipherment not permitted", [&key, &typical_usage]()
1309 {
1310 verify_cert_constraints_valid_for_key_type(key, typical_usage.key_encipherment);
1311 });
1312 result.test_throws("data encipherment not permitted", [&key, &typical_usage]()
1313 {
1314 verify_cert_constraints_valid_for_key_type(key, typical_usage.data_encipherment);
1315 });
1316 result.test_throws("key agreement not permitted", [&key, &typical_usage]()
1317 {
1318 verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement);
1319 });
1320 result.test_throws("key agreement, encipher only not permitted", [&key, &typical_usage]()
1321 {
1322 verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_encipher_only);
1323 });
1324 result.test_throws("key agreement, decipher only not permitted", [&key, &typical_usage]()
1325 {
1326 verify_cert_constraints_valid_for_key_type(key, typical_usage.key_agreement_decipher_only);
1327 });
1328
1329 verify_cert_constraints_valid_for_key_type(key, typical_usage.crl_sign);
1330 verify_cert_constraints_valid_for_key_type(key, typical_usage.sign_everything);
1331 }
1332
1333 return result;
1334 }
1335
1336 /**
1337 * @brief X.509v3 extension that encodes a given string
1338 */
1339 class String_Extension final : public Botan::Certificate_Extension
1340 {
1341 public:
1342 String_Extension() = default;
String_Extension(const std::string & val)1343 String_Extension(const std::string& val) : m_contents(val) {}
1344
value() const1345 std::string value() const
1346 {
1347 return m_contents;
1348 }
1349
copy() const1350 String_Extension* copy() const override
1351 {
1352 return new String_Extension(m_contents);
1353 }
1354
oid_of() const1355 Botan::OID oid_of() const override
1356 {
1357 return Botan::OID("1.2.3.4.5.6.7.8.9.1");
1358 }
1359
should_encode() const1360 bool should_encode() const override
1361 {
1362 return true;
1363 }
1364
oid_name() const1365 std::string oid_name() const override
1366 {
1367 return "String Extension";
1368 }
1369
contents_to(Botan::Data_Store &,Botan::Data_Store &) const1370 void contents_to(Botan::Data_Store&, Botan::Data_Store&) const override {}
1371
encode_inner() const1372 std::vector<uint8_t> encode_inner() const override
1373 {
1374 std::vector<uint8_t> bits;
1375 Botan::DER_Encoder(bits).encode(Botan::ASN1_String(m_contents, Botan::UTF8_STRING));
1376 return bits;
1377 }
1378
decode_inner(const std::vector<uint8_t> & in)1379 void decode_inner(const std::vector<uint8_t>& in) override
1380 {
1381 Botan::ASN1_String str;
1382 Botan::BER_Decoder(in).decode(str, Botan::UTF8_STRING).verify_end();
1383 m_contents = str.value();
1384 }
1385
1386 private:
1387 std::string m_contents;
1388 };
1389
test_custom_dn_attr(const Botan::Private_Key & ca_key,const std::string & sig_algo,const std::string & sig_padding="",const std::string & hash_fn="SHA-256")1390 Test::Result test_custom_dn_attr(const Botan::Private_Key& ca_key,
1391 const std::string& sig_algo,
1392 const std::string& sig_padding = "",
1393 const std::string& hash_fn = "SHA-256")
1394 {
1395 Test::Result result("X509 Custom DN");
1396
1397 /* Create the self-signed cert */
1398 Botan::X509_Certificate ca_cert =
1399 Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, Test::rng());
1400
1401 /* Create the CA object */
1402 Botan::X509_CA ca(ca_cert, ca_key, {{"padding",sig_padding}}, hash_fn, Test::rng());
1403
1404 std::unique_ptr<Botan::Private_Key> user_key(make_a_private_key(sig_algo));
1405
1406 Botan::X509_DN subject_dn;
1407
1408 const Botan::OID attr1(Botan::OID("1.3.6.1.4.1.25258.9.1.1"));
1409 const Botan::OID attr2(Botan::OID("1.3.6.1.4.1.25258.9.1.2"));
1410 const Botan::ASN1_String val1("Custom Attr 1", Botan::PRINTABLE_STRING);
1411 const Botan::ASN1_String val2("12345", Botan::UTF8_STRING);
1412
1413 subject_dn.add_attribute(attr1, val1);
1414 subject_dn.add_attribute(attr2, val2);
1415
1416 Botan::Extensions extensions;
1417
1418 Botan::PKCS10_Request req =
1419 Botan::PKCS10_Request::create(*user_key,
1420 subject_dn,
1421 extensions,
1422 hash_fn,
1423 Test::rng(),
1424 sig_padding);
1425
1426 Botan::X509_DN req_dn = req.subject_dn();
1427
1428 result.test_eq("Expected number of DN entries", req_dn.dn_info().size(), 2);
1429
1430 Botan::ASN1_String req_val1 = req_dn.get_first_attribute(attr1);
1431 Botan::ASN1_String req_val2 = req_dn.get_first_attribute(attr2);
1432 result.confirm("Attr1 matches encoded", req_val1 == val1);
1433 result.confirm("Attr2 matches encoded", req_val2 == val2);
1434 result.confirm("Attr1 tag matches encoded", req_val1.tagging() == val1.tagging());
1435 result.confirm("Attr2 tag matches encoded", req_val2.tagging() == val2.tagging());
1436
1437 Botan::X509_Time not_before("100301123001Z", Botan::UTC_TIME);
1438 Botan::X509_Time not_after("300301123001Z", Botan::UTC_TIME);
1439
1440 auto cert = ca.sign_request(req, Test::rng(), not_before, not_after);
1441
1442 Botan::X509_DN cert_dn = cert.subject_dn();
1443
1444 result.test_eq("Expected number of DN entries", cert_dn.dn_info().size(), 2);
1445
1446 Botan::ASN1_String cert_val1 = cert_dn.get_first_attribute(attr1);
1447 Botan::ASN1_String cert_val2 = cert_dn.get_first_attribute(attr2);
1448 result.confirm("Attr1 matches encoded", cert_val1 == val1);
1449 result.confirm("Attr2 matches encoded", cert_val2 == val2);
1450 result.confirm("Attr1 tag matches encoded", cert_val1.tagging() == val1.tagging());
1451 result.confirm("Attr2 tag matches encoded", cert_val2.tagging() == val2.tagging());
1452
1453 return result;
1454 }
1455
1456
test_x509_extensions(const Botan::Private_Key & ca_key,const std::string & sig_algo,const std::string & sig_padding="",const std::string & hash_fn="SHA-256")1457 Test::Result test_x509_extensions(const Botan::Private_Key& ca_key,
1458 const std::string& sig_algo,
1459 const std::string& sig_padding = "",
1460 const std::string& hash_fn = "SHA-256")
1461 {
1462 using Botan::Key_Constraints;
1463
1464 Test::Result result("X509 Extensions");
1465
1466 /* Create the self-signed cert */
1467 Botan::X509_Certificate ca_cert =
1468 Botan::X509::create_self_signed_cert(ca_opts(sig_padding), ca_key, hash_fn, Test::rng());
1469
1470 /* Create the CA object */
1471 Botan::X509_CA ca(ca_cert, ca_key, {{"padding",sig_padding}}, hash_fn, Test::rng());
1472
1473 std::unique_ptr<Botan::Private_Key> user_key(make_a_private_key(sig_algo));
1474
1475 Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
1476 opts.constraints = Key_Constraints::DIGITAL_SIGNATURE;
1477
1478 // include a custom extension in the request
1479 Botan::Extensions req_extensions;
1480 const Botan::OID oid("1.2.3.4.5.6.7.8.9.1");
1481 const Botan::OID ku_oid = Botan::OID::from_string("X509v3.KeyUsage");
1482 req_extensions.add(new String_Extension("AAAAAAAAAAAAAABCDEF"), false);
1483 opts.extensions = req_extensions;
1484 opts.set_padding_scheme(sig_padding);
1485
1486 /* Create a self-signed certificate */
1487 const Botan::X509_Certificate self_signed_cert = Botan::X509::create_self_signed_cert(
1488 opts, *user_key, hash_fn, Test::rng());
1489
1490 result.confirm("Extensions::extension_set true for Key_Usage", self_signed_cert.v3_extensions().extension_set(ku_oid));
1491
1492 // check if known Key_Usage extension is present in self-signed cert
1493 auto key_usage_ext = self_signed_cert.v3_extensions().get(ku_oid);
1494 if(result.confirm("Key_Usage extension present in self-signed certificate", key_usage_ext != nullptr))
1495 {
1496 result.confirm("Key_Usage extension value matches in self-signed certificate",
1497 dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints() == opts.constraints);
1498 }
1499
1500 // check if custom extension is present in self-signed cert
1501 auto string_ext = self_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
1502 if(result.confirm("Custom extension present in self-signed certificate", string_ext != nullptr))
1503 {
1504 result.test_eq("Custom extension value matches in self-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
1505 }
1506
1507
1508 const Botan::PKCS10_Request user_req = Botan::X509::create_cert_req(opts, *user_key, hash_fn, Test::rng());
1509
1510 /* Create a CA-signed certificate */
1511 const Botan::X509_Certificate ca_signed_cert =
1512 ca.sign_request(user_req, Test::rng(),
1513 from_date(-1, 01, 01),
1514 from_date(2, 01, 01));
1515
1516 // check if known Key_Usage extension is present in CA-signed cert
1517 result.confirm("Extensions::extension_set true for Key_Usage", ca_signed_cert.v3_extensions().extension_set(ku_oid));
1518
1519 key_usage_ext = ca_signed_cert.v3_extensions().get(ku_oid);
1520 if(result.confirm("Key_Usage extension present in CA-signed certificate", key_usage_ext != nullptr))
1521 {
1522 result.confirm("Key_Usage extension value matches in user certificate",
1523 dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints() == Botan::DIGITAL_SIGNATURE);
1524 }
1525
1526 // check if custom extension is present in CA-signed cert
1527 result.confirm("Extensions::extension_set true for String_Extension", ca_signed_cert.v3_extensions().extension_set(oid));
1528 string_ext = ca_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
1529 if(result.confirm("Custom extension present in CA-signed certificate", string_ext != nullptr))
1530 {
1531 result.test_eq("Custom extension value matches in CA-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
1532 }
1533
1534 return result;
1535 }
1536
test_hashes(const Botan::Private_Key & key,const std::string & hash_fn="SHA-256")1537 Test::Result test_hashes(const Botan::Private_Key& key,
1538 const std::string& hash_fn = "SHA-256")
1539 {
1540 Test::Result result("X509 Hashes");
1541
1542 struct TestData
1543 {
1544 const std::string issuer, subject, issuer_hash, subject_hash;
1545 } const cases[]
1546 {
1547 {
1548 "",
1549 "",
1550 "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95",
1551 "E4F60D0AA6D7F3D3B6A6494B1C861B99F649C6F9EC51ABAF201B20F297327C95"
1552 },
1553 {
1554 "a",
1555 "b",
1556 "BC2E013472F39AC579964880E422737C82BA812CB8BC2FD17E013060D71E6E19",
1557 "5E31CFAA3FAFB1A5BA296A0D2BAB9CA44D7936E9BF0BBC54637D0C53DBC4A432"
1558 },
1559 {
1560 "A",
1561 "B",
1562 "4B3206201C4BC9B6CD6C36532A97687DF9238155D99ADB60C66BF2B2220643D8",
1563 "FFF635A52A16618B4A0E9CD26B5E5A2FA573D343C051E6DE8B0811B1ACC89B86"
1564 },
1565 {
1566 "Test Issuer/US/Botan Project/Testing",
1567 "Test Subject/US/Botan Project/Testing",
1568 "ACB4F373004A56A983A23EB8F60FA4706312B5DB90FD978574FE7ACC84E093A5",
1569 "87039231C2205B74B6F1F3830A66272C0B41F71894B03AC3150221766D95267B",
1570 },
1571 {
1572 "Test Subject/US/Botan Project/Testing",
1573 "Test Issuer/US/Botan Project/Testing",
1574 "87039231C2205B74B6F1F3830A66272C0B41F71894B03AC3150221766D95267B",
1575 "ACB4F373004A56A983A23EB8F60FA4706312B5DB90FD978574FE7ACC84E093A5",
1576 }
1577 };
1578
1579 for(const auto& a : cases)
1580 {
1581 Botan::X509_Cert_Options opts{a.issuer};
1582 opts.CA_key();
1583
1584 const Botan::X509_Certificate issuer_cert =
1585 Botan::X509::create_self_signed_cert(opts, key, hash_fn, Test::rng());
1586
1587 result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_issuer_dn_sha256()), a.issuer_hash);
1588 result.test_eq(a.issuer, Botan::hex_encode(issuer_cert.raw_subject_dn_sha256()), a.issuer_hash);
1589
1590 const Botan::X509_CA ca(issuer_cert, key, hash_fn, Test::rng());
1591 const Botan::PKCS10_Request req =
1592 Botan::X509::create_cert_req(a.subject, key, hash_fn, Test::rng());
1593 const Botan::X509_Certificate subject_cert =
1594 ca.sign_request(req, Test::rng(), from_date(-1, 01, 01), from_date(2, 01, 01));
1595
1596 result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_issuer_dn_sha256()), a.issuer_hash);
1597 result.test_eq(a.subject, Botan::hex_encode(subject_cert.raw_subject_dn_sha256()), a.subject_hash);
1598 }
1599 return result;
1600 }
1601
1602 class X509_Cert_Unit_Tests final : public Test
1603 {
1604 public:
run()1605 std::vector<Test::Result> run() override
1606 {
1607 std::vector<Test::Result> results;
1608
1609 const std::string sig_algos[] { "RSA", "DSA", "ECDSA", "ECGDSA", "ECKCDSA", "GOST-34.10", "Ed25519" };
1610
1611 for(const std::string& algo : sig_algos)
1612 {
1613 #if !defined(BOTAN_HAS_EMSA_PKCS1)
1614 if(algo == "RSA")
1615 continue;
1616 #endif
1617
1618 #if !defined(BOTAN_HAS_EMSA1)
1619 if(algo != "RSA" && algo != "Ed25519")
1620 continue;
1621 #endif
1622
1623 std::unique_ptr<Botan::Private_Key> key = make_a_private_key(algo);
1624
1625 if(key == nullptr)
1626 continue;
1627
1628 results.push_back(test_hashes(*key));
1629 results.push_back(test_valid_constraints(*key, algo));
1630
1631 Test::Result usage_result("X509 Usage");
1632 try
1633 {
1634 usage_result.merge(test_usage(*key, algo));
1635 }
1636 catch(std::exception& e)
1637 {
1638 usage_result.test_failure("test_usage " + algo, e.what());
1639 }
1640 results.push_back(usage_result);
1641
1642 for(auto padding_scheme : Botan::get_sig_paddings(algo))
1643 {
1644 Test::Result cert_result("X509 Unit");
1645 try
1646 {
1647 cert_result.merge(test_x509_cert(*key, algo, padding_scheme));
1648 }
1649 catch(std::exception& e)
1650 {
1651 cert_result.test_failure("test_x509_cert " + algo, e.what());
1652 }
1653 results.push_back(cert_result);
1654
1655 Test::Result pkcs10_result("PKCS10 extensions");
1656 try
1657 {
1658 pkcs10_result.merge(test_pkcs10_ext(*key, padding_scheme));
1659 }
1660 catch(std::exception& e)
1661 {
1662 pkcs10_result.test_failure("test_pkcs10_ext " + algo, e.what());
1663 }
1664 results.push_back(pkcs10_result);
1665
1666 Test::Result self_issued_result("X509 Self Issued");
1667 try
1668 {
1669 self_issued_result.merge(test_self_issued(*key, algo, padding_scheme));
1670 }
1671 catch(std::exception& e)
1672 {
1673 self_issued_result.test_failure("test_self_issued " + algo, e.what());
1674 }
1675 results.push_back(self_issued_result);
1676
1677 Test::Result extensions_result("X509 Extensions");
1678 try
1679 {
1680 extensions_result.merge(test_x509_extensions(*key, algo, padding_scheme));
1681 }
1682 catch(std::exception& e)
1683 {
1684 extensions_result.test_failure("test_extensions " + algo, e.what());
1685 }
1686 results.push_back(extensions_result);
1687
1688 Test::Result custom_dn_result("X509 Custom DN");
1689 try
1690 {
1691 custom_dn_result.merge(test_custom_dn_attr(*key, algo, padding_scheme));
1692 }
1693 catch(std::exception& e)
1694 {
1695 custom_dn_result.test_failure("test_custom_dn_attr " + algo, e.what());
1696 }
1697 results.push_back(custom_dn_result);
1698 }
1699
1700 }
1701
1702 /*
1703 These are algos which cannot sign but can be included in certs
1704 */
1705 const std::vector<std::string> enc_algos = { "DH", "ECDH", "ElGamal" };
1706
1707 for(std::string algo : enc_algos)
1708 {
1709 std::unique_ptr<Botan::Private_Key> key = make_a_private_key(algo);
1710
1711 if(key)
1712 {
1713 results.push_back(test_valid_constraints(*key, algo));
1714 }
1715 }
1716
1717
1718 #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) && defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_HAS_EMSA_PSSR) && defined(BOTAN_HAS_RSA)
1719 Test::Result pad_config_result("X509 Padding Config");
1720 try
1721 {
1722 pad_config_result.merge(test_padding_config());
1723 }
1724 catch(const std::exception& e)
1725 {
1726 pad_config_result.test_failure("test_padding_config", e.what());
1727 }
1728 results.push_back(pad_config_result);
1729 #endif
1730
1731 #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1732 results.push_back(test_x509_utf8());
1733 results.push_back(test_x509_bmpstring());
1734 results.push_back(test_crl_dn_name());
1735 results.push_back(test_rdn_multielement_set_name());
1736 results.push_back(test_x509_decode_list());
1737 results.push_back(test_rsa_oaep());
1738 results.push_back(test_x509_authority_info_access_extension());
1739 results.push_back(test_verify_gost2012_cert());
1740 #endif
1741
1742 results.push_back(test_x509_extension());
1743 results.push_back(test_x509_dates());
1744 results.push_back(test_cert_status_strings());
1745 results.push_back(test_x509_uninit());
1746
1747 return results;
1748 }
1749 };
1750
1751 BOTAN_REGISTER_TEST("x509", "x509_unit", X509_Cert_Unit_Tests);
1752
1753 #endif
1754
1755 }
1756
1757 }
1758