Botan 2.19.2
Crypto and TLS for C&
x509path.cpp
Go to the documentation of this file.
1/*
2* X.509 Certificate Path Validation
3* (C) 2010,2011,2012,2014,2016 Jack Lloyd
4* (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity
5*
6* Botan is released under the Simplified BSD License (see license.txt)
7*/
8
9#include <botan/x509path.h>
10#include <botan/x509_ext.h>
11#include <botan/pk_keys.h>
12#include <botan/ocsp.h>
13#include <botan/oids.h>
14#include <algorithm>
15#include <chrono>
16#include <vector>
17#include <set>
18#include <string>
19#include <sstream>
20
21#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
22 #include <future>
23 #include <botan/http_util.h>
24#endif
25
26namespace Botan {
27
28/*
29* PKIX path validation
30*/
32PKIX::check_chain(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
33 std::chrono::system_clock::time_point ref_time,
34 const std::string& hostname,
35 Usage_Type usage,
36 size_t min_signature_algo_strength,
37 const std::set<std::string>& trusted_hashes)
38 {
39 if(cert_path.empty())
40 throw Invalid_Argument("PKIX::check_chain cert_path empty");
41
42 const bool self_signed_ee_cert = (cert_path.size() == 1);
43
44 X509_Time validation_time(ref_time);
45
46 CertificatePathStatusCodes cert_status(cert_path.size());
47
48 if(!hostname.empty() && !cert_path[0]->matches_dns_name(hostname))
49 cert_status[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH);
50
51 if(!cert_path[0]->allowed_usage(usage))
52 cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
53
54 if(cert_path[0]->is_CA_cert() == false &&
55 cert_path[0]->has_constraints(KEY_CERT_SIGN))
56 {
57 /*
58 "If the keyCertSign bit is asserted, then the cA bit in the
59 basic constraints extension (Section 4.2.1.9) MUST also be
60 asserted." - RFC 5280
61
62 We don't bother doing this check on the rest of the path since they
63 must have the cA bit asserted or the validation will fail anyway.
64 */
65 cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
66 }
67
68 for(size_t i = 0; i != cert_path.size(); ++i)
69 {
70 std::set<Certificate_Status_Code>& status = cert_status.at(i);
71
72 const bool at_self_signed_root = (i == cert_path.size() - 1);
73
74 const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
75
76 const std::shared_ptr<const X509_Certificate>& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
77
78 if(at_self_signed_root && (issuer->is_self_signed() == false))
79 {
81 }
82
83 if(subject->issuer_dn() != issuer->subject_dn())
84 {
86 }
87
88 // Check the serial number
89 if(subject->is_serial_negative())
90 {
92 }
93
94 // Check the subject's DN components' length
95
96 for(const auto& dn_pair : subject->subject_dn().dn_info())
97 {
98 const size_t dn_ub = X509_DN::lookup_ub(dn_pair.first);
99 // dn_pair = <OID,str>
100 if(dn_ub > 0 && dn_pair.second.size() > dn_ub)
101 {
103 }
104 }
105
106 // Check all certs for valid time range
107 if(validation_time < subject->not_before())
109
110 if(validation_time > subject->not_after())
112
113 // Check issuer constraints
114 if(!issuer->is_CA_cert() && !self_signed_ee_cert)
116
117 std::unique_ptr<Public_Key> issuer_key(issuer->subject_public_key());
118
119 // Check the signature algorithm is known
120 if(OIDS::oid2str_or_empty(subject->signature_algorithm().get_oid()).empty())
121 {
123 }
124 else
125 {
126 // only perform the following checks if the signature algorithm is known
127 if(!issuer_key)
128 {
130 }
131 else
132 {
133 const Certificate_Status_Code sig_status = subject->verify_signature(*issuer_key);
134
135 if(sig_status != Certificate_Status_Code::VERIFIED)
136 status.insert(sig_status);
137
138 if(issuer_key->estimated_strength() < min_signature_algo_strength)
140 }
141
142 // Ignore untrusted hashes on self-signed roots
143 if(trusted_hashes.size() > 0 && !at_self_signed_root)
144 {
145 if(trusted_hashes.count(subject->hash_used_for_signature()) == 0)
147 }
148 }
149
150 // Check cert extensions
151
152 if(subject->x509_version() == 1)
153 {
154 if(subject->v2_issuer_key_id().empty() == false ||
155 subject->v2_subject_key_id().empty() == false)
156 {
158 }
159 }
160
161 Extensions extensions = subject->v3_extensions();
162 const auto& extensions_vec = extensions.extensions();
163 if(subject->x509_version() < 3 && !extensions_vec.empty())
164 {
166 }
167 for(auto& extension : extensions_vec)
168 {
169 extension.first->validate(*subject, *issuer, cert_path, cert_status, i);
170 }
171 if(extensions.extensions().size() != extensions.get_extension_oids().size())
172 {
174 }
175 }
176
177 // path len check
178 size_t max_path_length = cert_path.size();
179 for(size_t i = cert_path.size() - 1; i > 0 ; --i)
180 {
181 std::set<Certificate_Status_Code>& status = cert_status.at(i);
182 const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
183
184 /*
185 * If the certificate was not self-issued, verify that max_path_length is
186 * greater than zero and decrement max_path_length by 1.
187 */
188 if(subject->subject_dn() != subject->issuer_dn())
189 {
190 if(max_path_length > 0)
191 {
192 --max_path_length;
193 }
194 else
195 {
197 }
198 }
199
200 /*
201 * If pathLenConstraint is present in the certificate and is less than max_path_length,
202 * set max_path_length to the value of pathLenConstraint.
203 */
204 if(subject->path_limit() != Cert_Extension::NO_CERT_PATH_LIMIT && subject->path_limit() < max_path_length)
205 {
206 max_path_length = subject->path_limit();
207 }
208 }
209
210 return cert_status;
211 }
212
214PKIX::check_ocsp(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
215 const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_responses,
216 const std::vector<Certificate_Store*>& trusted_certstores,
217 std::chrono::system_clock::time_point ref_time,
218 std::chrono::seconds max_ocsp_age)
219 {
220 if(cert_path.empty())
221 throw Invalid_Argument("PKIX::check_ocsp cert_path empty");
222
223 CertificatePathStatusCodes cert_status(cert_path.size() - 1);
224
225 for(size_t i = 0; i != cert_path.size() - 1; ++i)
226 {
227 std::set<Certificate_Status_Code>& status = cert_status.at(i);
228
229 std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
230 std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
231
232 if(i < ocsp_responses.size() && (ocsp_responses.at(i) != nullptr)
233 && (ocsp_responses.at(i)->status() == OCSP::Response_Status_Code::Successful))
234 {
235 try
236 {
237 Certificate_Status_Code ocsp_signature_status = ocsp_responses.at(i)->check_signature(trusted_certstores, cert_path);
238
239 if(ocsp_signature_status == Certificate_Status_Code::OCSP_SIGNATURE_OK)
240 {
241 // Signature ok, so check the claimed status
242 Certificate_Status_Code ocsp_status = ocsp_responses.at(i)->status_for(*ca, *subject, ref_time, max_ocsp_age);
243 status.insert(ocsp_status);
244 }
245 else
246 {
247 // Some signature problem
248 status.insert(ocsp_signature_status);
249 }
250 }
251 catch(Exception&)
252 {
254 }
255 }
256 }
257
258 while(cert_status.size() > 0 && cert_status.back().empty())
259 cert_status.pop_back();
260
261 return cert_status;
262 }
263
265PKIX::check_crl(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
266 const std::vector<std::shared_ptr<const X509_CRL>>& crls,
267 std::chrono::system_clock::time_point ref_time)
268 {
269 if(cert_path.empty())
270 throw Invalid_Argument("PKIX::check_crl cert_path empty");
271
272 CertificatePathStatusCodes cert_status(cert_path.size());
273 const X509_Time validation_time(ref_time);
274
275 for(size_t i = 0; i != cert_path.size() - 1; ++i)
276 {
277 std::set<Certificate_Status_Code>& status = cert_status.at(i);
278
279 if(i < crls.size() && crls.at(i))
280 {
281 std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
282 std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
283
284 if(!ca->allowed_usage(CRL_SIGN))
286
287 if(validation_time < crls[i]->this_update())
289
290 if(validation_time > crls[i]->next_update())
292
293 if(crls[i]->check_signature(ca->subject_public_key()) == false)
295
297
298 if(crls[i]->is_revoked(*subject))
300
301 std::string dp = subject->crl_distribution_point();
302 if(!dp.empty())
303 {
304 if(dp != crls[i]->crl_issuing_distribution_point())
305 {
307 }
308 }
309
310 for(const auto& extension : crls[i]->extensions().extensions())
311 {
312 // XXX this is wrong - the OID might be defined but the extention not full parsed
313 // for example see #1652
314
315 // is the extension critical and unknown?
316 if(extension.second && OIDS::oid2str_or_empty(extension.first->oid_of()) == "")
317 {
318 /* NIST Certificate Path Valiadation Testing document: "When an implementation does not recognize a critical extension in the
319 * crlExtensions field, it shall assume that identified certificates have been revoked and are no longer valid"
320 */
322 }
323 }
324
325 }
326 }
327
328 while(cert_status.size() > 0 && cert_status.back().empty())
329 cert_status.pop_back();
330
331 return cert_status;
332 }
333
335PKIX::check_crl(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
336 const std::vector<Certificate_Store*>& certstores,
337 std::chrono::system_clock::time_point ref_time)
338 {
339 if(cert_path.empty())
340 throw Invalid_Argument("PKIX::check_crl cert_path empty");
341
342 if(certstores.empty())
343 throw Invalid_Argument("PKIX::check_crl certstores empty");
344
345 std::vector<std::shared_ptr<const X509_CRL>> crls(cert_path.size());
346
347 for(size_t i = 0; i != cert_path.size(); ++i)
348 {
349 BOTAN_ASSERT_NONNULL(cert_path[i]);
350 for(size_t c = 0; c != certstores.size(); ++c)
351 {
352 crls[i] = certstores[c]->find_crl_for(*cert_path[i]);
353 if(crls[i])
354 break;
355 }
356 }
357
358 return PKIX::check_crl(cert_path, crls, ref_time);
359 }
360
361#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
362
364PKIX::check_ocsp_online(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
365 const std::vector<Certificate_Store*>& trusted_certstores,
366 std::chrono::system_clock::time_point ref_time,
367 std::chrono::milliseconds timeout,
368 bool ocsp_check_intermediate_CAs,
369 std::chrono::seconds max_ocsp_age)
370 {
371 if(cert_path.empty())
372 throw Invalid_Argument("PKIX::check_ocsp_online cert_path empty");
373
374 std::vector<std::future<std::shared_ptr<const OCSP::Response>>> ocsp_response_futures;
375
376 size_t to_ocsp = 1;
377
378 if(ocsp_check_intermediate_CAs)
379 to_ocsp = cert_path.size() - 1;
380 if(cert_path.size() == 1)
381 to_ocsp = 0;
382
383 for(size_t i = 0; i < to_ocsp; ++i)
384 {
385 const std::shared_ptr<const X509_Certificate>& subject = cert_path.at(i);
386 const std::shared_ptr<const X509_Certificate>& issuer = cert_path.at(i+1);
387
388 if(subject->ocsp_responder() == "")
389 {
390 ocsp_response_futures.emplace_back(std::async(std::launch::deferred, [&]() -> std::shared_ptr<const OCSP::Response> {
391 return std::make_shared<const OCSP::Response>(Certificate_Status_Code::OCSP_NO_REVOCATION_URL);
392 }));
393 }
394 else
395 {
396 ocsp_response_futures.emplace_back(std::async(std::launch::async, [&]() -> std::shared_ptr<const OCSP::Response> {
397 OCSP::Request req(*issuer, BigInt::decode(subject->serial_number()));
398
399 HTTP::Response http;
400 try
401 {
402 http = HTTP::POST_sync(subject->ocsp_responder(),
403 "application/ocsp-request",
404 req.BER_encode(),
405 /*redirects*/1,
406 timeout);
407 }
408 catch(std::exception&)
409 {
410 // log e.what() ?
411 }
412 if (http.status_code() != 200)
413 return std::make_shared<const OCSP::Response>(Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE);
414 // Check the MIME type?
415
416 return std::make_shared<const OCSP::Response>(http.body());
417 }));
418 }
419 }
420
421 std::vector<std::shared_ptr<const OCSP::Response>> ocsp_responses;
422
423 for(size_t i = 0; i < ocsp_response_futures.size(); ++i)
424 {
425 ocsp_responses.push_back(ocsp_response_futures[i].get());
426 }
427
428 return PKIX::check_ocsp(cert_path, ocsp_responses, trusted_certstores, ref_time, max_ocsp_age);
429 }
430
432PKIX::check_crl_online(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
433 const std::vector<Certificate_Store*>& certstores,
434 Certificate_Store_In_Memory* crl_store,
435 std::chrono::system_clock::time_point ref_time,
436 std::chrono::milliseconds timeout)
437 {
438 if(cert_path.empty())
439 throw Invalid_Argument("PKIX::check_crl_online cert_path empty");
440 if(certstores.empty())
441 throw Invalid_Argument("PKIX::check_crl_online certstores empty");
442
443 std::vector<std::future<std::shared_ptr<const X509_CRL>>> future_crls;
444 std::vector<std::shared_ptr<const X509_CRL>> crls(cert_path.size());
445
446 for(size_t i = 0; i != cert_path.size(); ++i)
447 {
448 const std::shared_ptr<const X509_Certificate>& cert = cert_path.at(i);
449 for(size_t c = 0; c != certstores.size(); ++c)
450 {
451 crls[i] = certstores[c]->find_crl_for(*cert);
452 if(crls[i])
453 break;
454 }
455
456 // TODO: check if CRL is expired and re-request?
457
458 // Only request if we don't already have a CRL
459 if(crls[i])
460 {
461 /*
462 We already have a CRL, so just insert this empty one to hold a place in the vector
463 so that indexes match up
464 */
465 future_crls.emplace_back(std::future<std::shared_ptr<const X509_CRL>>());
466 }
467 else if(cert->crl_distribution_point() == "")
468 {
469 // Avoid creating a thread for this case
470 future_crls.emplace_back(std::async(std::launch::deferred, [&]() -> std::shared_ptr<const X509_CRL> {
471 throw Not_Implemented("No CRL distribution point for this certificate");
472 }));
473 }
474 else
475 {
476 future_crls.emplace_back(std::async(std::launch::async, [&]() -> std::shared_ptr<const X509_CRL> {
477 auto http = HTTP::GET_sync(cert->crl_distribution_point(),
478 /*redirects*/ 1, timeout);
479
480 http.throw_unless_ok();
481 // check the mime type?
482 return std::make_shared<const X509_CRL>(http.body());
483 }));
484 }
485 }
486
487 for(size_t i = 0; i != future_crls.size(); ++i)
488 {
489 if(future_crls[i].valid())
490 {
491 try
492 {
493 crls[i] = future_crls[i].get();
494 }
495 catch(std::exception&)
496 {
497 // crls[i] left null
498 // todo: log exception e.what() ?
499 }
500 }
501 }
502
503 const CertificatePathStatusCodes crl_status = PKIX::check_crl(cert_path, crls, ref_time);
504
505 if(crl_store)
506 {
507 for(size_t i = 0; i != crl_status.size(); ++i)
508 {
509 if(crl_status[i].count(Certificate_Status_Code::VALID_CRL_CHECKED))
510 {
511 // better be non-null, we supposedly validated it
512 BOTAN_ASSERT_NONNULL(crls[i]);
513 crl_store->add_crl(crls[i]);
514 }
515 }
516 }
517
518 return crl_status;
519 }
520
521#endif
522
524PKIX::build_certificate_path(std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
525 const std::vector<Certificate_Store*>& trusted_certstores,
526 const std::shared_ptr<const X509_Certificate>& end_entity,
527 const std::vector<std::shared_ptr<const X509_Certificate>>& end_entity_extra)
528 {
529 if(end_entity->is_self_signed())
530 {
532 }
533
534 /*
535 * This is an inelegant but functional way of preventing path loops
536 * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
537 * fingerprints in the path. If there is a duplicate, we error out.
538 * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
539 */
540 std::set<std::string> certs_seen;
541
542 cert_path.push_back(end_entity);
543 certs_seen.insert(end_entity->fingerprint("SHA-256"));
544
546 for(size_t i = 0; i != end_entity_extra.size(); ++i)
547 ee_extras.add_certificate(end_entity_extra[i]);
548
549 // iterate until we reach a root or cannot find the issuer
550 for(;;)
551 {
552 const X509_Certificate& last = *cert_path.back();
553 const X509_DN issuer_dn = last.issuer_dn();
554 const std::vector<uint8_t> auth_key_id = last.authority_key_id();
555
556 std::shared_ptr<const X509_Certificate> issuer;
557 bool trusted_issuer = false;
558
559 for(Certificate_Store* store : trusted_certstores)
560 {
561 issuer = store->find_cert(issuer_dn, auth_key_id);
562 if(issuer)
563 {
564 trusted_issuer = true;
565 break;
566 }
567 }
568
569 if(!issuer)
570 {
571 // fall back to searching supplemental certs
572 issuer = ee_extras.find_cert(issuer_dn, auth_key_id);
573 }
574
575 if(!issuer)
577
578 const std::string fprint = issuer->fingerprint("SHA-256");
579
580 if(certs_seen.count(fprint) > 0) // already seen?
581 {
583 }
584
585 certs_seen.insert(fprint);
586 cert_path.push_back(issuer);
587
588 if(issuer->is_self_signed())
589 {
590 if(trusted_issuer)
591 {
593 }
594 else
595 {
597 }
598 }
599 }
600 }
601
602/**
603 * utilities for PKIX::build_all_certificate_paths
604 */
605namespace
606{
607// <certificate, trusted?>
608using cert_maybe_trusted = std::pair<std::shared_ptr<const X509_Certificate>,bool>;
609}
610
611/**
612 * Build all possible certificate paths from the end certificate to self-signed trusted roots.
613 *
614 * All potentially valid paths are put into the cert_paths vector. If no potentially valid paths are found,
615 * one of the encountered errors is returned arbitrarily.
616 *
617 * todo add a path building function that returns detailed information on errors encountered while building
618 * the potentially numerous path candidates.
619 *
620 * Basically, a DFS is performed starting from the end certificate. A stack (vector) serves to control the DFS.
621 * At the beginning of each iteration, a pair is popped from the stack that contains (1) the next certificate
622 * to add to the path (2) a bool that indicates if the certificate is part of a trusted certstore. Ideally, we
623 * follow the unique issuer of the current certificate until a trusted root is reached. However, the issuer DN +
624 * authority key id need not be unique among the certificates used for building the path. In such a case,
625 * we consider all the matching issuers by pushing <IssuerCert, trusted?> on the stack for each of them.
626 *
627 */
629PKIX::build_all_certificate_paths(std::vector<std::vector<std::shared_ptr<const X509_Certificate>>>& cert_paths_out,
630 const std::vector<Certificate_Store*>& trusted_certstores,
631 const std::shared_ptr<const X509_Certificate>& end_entity,
632 const std::vector<std::shared_ptr<const X509_Certificate>>& end_entity_extra)
633 {
634 if(!cert_paths_out.empty())
635 {
636 throw Invalid_Argument("PKIX::build_all_certificate_paths: cert_paths_out must be empty");
637 }
638
639 if(end_entity->is_self_signed())
640 {
642 }
643
644 /*
645 * Pile up error messages
646 */
647 std::vector<Certificate_Status_Code> stats;
648
650 for(size_t i = 0; i != end_entity_extra.size(); ++i)
651 {
652 ee_extras.add_certificate(end_entity_extra[i]);
653 }
654
655 /*
656 * This is an inelegant but functional way of preventing path loops
657 * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
658 * fingerprints in the path. If there is a duplicate, we error out.
659 * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
660 */
661 std::set<std::string> certs_seen;
662
663 // new certs are added and removed from the path during the DFS
664 // it is copied into cert_paths_out when we encounter a trusted root
665 std::vector<std::shared_ptr<const X509_Certificate>> path_so_far;
666
667 // todo can we assume that the end certificate is not trusted?
668 std::vector<cert_maybe_trusted> stack = { {end_entity, false} };
669
670 while(!stack.empty())
671 {
672 // found a deletion marker that guides the DFS, backtracing
673 if(stack.back().first == nullptr)
674 {
675 stack.pop_back();
676 std::string fprint = path_so_far.back()->fingerprint("SHA-256");
677 certs_seen.erase(fprint);
678 path_so_far.pop_back();
679 }
680 // process next cert on the path
681 else
682 {
683 std::shared_ptr<const X509_Certificate> last = stack.back().first;
684 bool trusted = stack.back().second;
685 stack.pop_back();
686
687 // certificate already seen?
688 const std::string fprint = last->fingerprint("SHA-256");
689 if(certs_seen.count(fprint) == 1)
690 {
692 // the current path ended in a loop
693 continue;
694 }
695
696 // the current path ends here
697 if(last->is_self_signed())
698 {
699 // found a trust anchor
700 if(trusted)
701 {
702 cert_paths_out.push_back(path_so_far);
703 cert_paths_out.back().push_back(last);
704
705 continue;
706 }
707 // found an untrustworthy root
708 else
709 {
711 continue;
712 }
713 }
714
715 const X509_DN issuer_dn = last->issuer_dn();
716 const std::vector<uint8_t> auth_key_id = last->authority_key_id();
717
718 // search for trusted issuers
719 std::vector<std::shared_ptr<const X509_Certificate>> trusted_issuers;
720 for(Certificate_Store* store : trusted_certstores)
721 {
722 auto new_issuers = store->find_all_certs(issuer_dn, auth_key_id);
723 trusted_issuers.insert(trusted_issuers.end(), new_issuers.begin(), new_issuers.end());
724 }
725
726 // search the supplemental certs
727 std::vector<std::shared_ptr<const X509_Certificate>> misc_issuers =
728 ee_extras.find_all_certs(issuer_dn, auth_key_id);
729
730 // if we could not find any issuers, the current path ends here
731 if(trusted_issuers.size() + misc_issuers.size() == 0)
732 {
734 continue;
735 }
736
737 // push the latest certificate onto the path_so_far
738 path_so_far.push_back(last);
739 certs_seen.emplace(fprint);
740
741 // push a deletion marker on the stack for backtracing later
742 stack.push_back({std::shared_ptr<const X509_Certificate>(nullptr),false});
743
744 for(const auto& trusted_cert : trusted_issuers)
745 {
746 stack.push_back({trusted_cert,true});
747 }
748
749 for(const auto& misc : misc_issuers)
750 {
751 stack.push_back({misc,false});
752 }
753 }
754 }
755
756 // could not construct any potentially valid path
757 if(cert_paths_out.empty())
758 {
759 if(stats.empty())
760 throw Internal_Error("X509 path building failed for unknown reasons");
761 else
762 // arbitrarily return the first error
763 return stats[0];
764 }
765 else
766 {
768 }
769 }
770
771
774 const CertificatePathStatusCodes& ocsp,
775 bool require_rev_on_end_entity,
776 bool require_rev_on_intermediates)
777 {
778 if(chain_status.empty())
779 throw Invalid_Argument("PKIX::merge_revocation_status chain_status was empty");
780
781 for(size_t i = 0; i != chain_status.size() - 1; ++i)
782 {
783 bool had_crl = false, had_ocsp = false;
784
785 if(i < crl.size() && crl[i].size() > 0)
786 {
787 for(auto&& code : crl[i])
788 {
790 {
791 had_crl = true;
792 }
793 chain_status[i].insert(code);
794 }
795 }
796
797 if(i < ocsp.size() && ocsp[i].size() > 0)
798 {
799 for(auto&& code : ocsp[i])
800 {
804 {
805 had_ocsp = true;
806 }
807
808 chain_status[i].insert(code);
809 }
810 }
811
812 if(had_crl == false && had_ocsp == false)
813 {
814 if((require_rev_on_end_entity && i == 0) ||
815 (require_rev_on_intermediates && i > 0))
816 {
817 chain_status[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA);
818 }
819 }
820 }
821 }
822
824 {
825 if(cert_status.empty())
826 throw Invalid_Argument("PKIX::overall_status empty cert status");
827
829
830 // take the "worst" error as overall
831 for(const std::set<Certificate_Status_Code>& s : cert_status)
832 {
833 if(!s.empty())
834 {
835 auto worst = *s.rbegin();
836 // Leave informative OCSP/CRL confirmations on cert-level status only
838 {
839 overall_status = worst;
840 }
841 }
842 }
843 return overall_status;
844 }
845
847 const std::vector<X509_Certificate>& end_certs,
848 const Path_Validation_Restrictions& restrictions,
849 const std::vector<Certificate_Store*>& trusted_roots,
850 const std::string& hostname,
851 Usage_Type usage,
852 std::chrono::system_clock::time_point ref_time,
853 std::chrono::milliseconds ocsp_timeout,
854 const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
855 {
856 if(end_certs.empty())
857 {
858 throw Invalid_Argument("x509_path_validate called with no subjects");
859 }
860
861 std::shared_ptr<const X509_Certificate> end_entity(std::make_shared<const X509_Certificate>(end_certs[0]));
862 std::vector<std::shared_ptr<const X509_Certificate>> end_entity_extra;
863 for(size_t i = 1; i < end_certs.size(); ++i)
864 {
865 end_entity_extra.push_back(std::make_shared<const X509_Certificate>(end_certs[i]));
866 }
867
868 std::vector<std::vector<std::shared_ptr<const X509_Certificate>>> cert_paths;
869 Certificate_Status_Code path_building_result = PKIX::build_all_certificate_paths(cert_paths, trusted_roots, end_entity, end_entity_extra);
870
871 // If we cannot successfully build a chain to a trusted self-signed root, stop now
872 if(path_building_result != Certificate_Status_Code::OK)
873 {
874 return Path_Validation_Result(path_building_result);
875 }
876
877 std::vector<Path_Validation_Result> error_results;
878 // Try validating all the potentially valid paths and return the first one to validate properly
879 for(auto cert_path : cert_paths)
880 {
882 PKIX::check_chain(cert_path, ref_time,
883 hostname, usage,
884 restrictions.minimum_key_strength(),
885 restrictions.trusted_hashes());
886
887 CertificatePathStatusCodes crl_status =
888 PKIX::check_crl(cert_path, trusted_roots, ref_time);
889
890 CertificatePathStatusCodes ocsp_status;
891
892 if(ocsp_resp.size() > 0)
893 {
894 ocsp_status = PKIX::check_ocsp(cert_path, ocsp_resp, trusted_roots, ref_time, restrictions.max_ocsp_age());
895 }
896
897 if(ocsp_status.empty() && ocsp_timeout != std::chrono::milliseconds(0))
898 {
899#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL)
900 ocsp_status = PKIX::check_ocsp_online(cert_path, trusted_roots, ref_time,
901 ocsp_timeout, restrictions.ocsp_all_intermediates());
902#else
903 ocsp_status.resize(1);
904 ocsp_status[0].insert(Certificate_Status_Code::OCSP_NO_HTTP);
905#endif
906 }
907
908 PKIX::merge_revocation_status(status, crl_status, ocsp_status,
909 restrictions.require_revocation_information(),
910 restrictions.ocsp_all_intermediates());
911
912 Path_Validation_Result pvd(status, std::move(cert_path));
913 if(pvd.successful_validation())
914 {
915 return pvd;
916 }
917 else
918 {
919 error_results.push_back(std::move(pvd));
920 }
921 }
922 return error_results[0];
923 }
924
926 const X509_Certificate& end_cert,
927 const Path_Validation_Restrictions& restrictions,
928 const std::vector<Certificate_Store*>& trusted_roots,
929 const std::string& hostname,
930 Usage_Type usage,
931 std::chrono::system_clock::time_point when,
932 std::chrono::milliseconds ocsp_timeout,
933 const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
934 {
935 std::vector<X509_Certificate> certs;
936 certs.push_back(end_cert);
937 return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
938 }
939
941 const std::vector<X509_Certificate>& end_certs,
942 const Path_Validation_Restrictions& restrictions,
943 const Certificate_Store& store,
944 const std::string& hostname,
945 Usage_Type usage,
946 std::chrono::system_clock::time_point when,
947 std::chrono::milliseconds ocsp_timeout,
948 const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
949 {
950 std::vector<Certificate_Store*> trusted_roots;
951 trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
952
953 return x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
954 }
955
957 const X509_Certificate& end_cert,
958 const Path_Validation_Restrictions& restrictions,
959 const Certificate_Store& store,
960 const std::string& hostname,
961 Usage_Type usage,
962 std::chrono::system_clock::time_point when,
963 std::chrono::milliseconds ocsp_timeout,
964 const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
965 {
966 std::vector<X509_Certificate> certs;
967 certs.push_back(end_cert);
968
969 std::vector<Certificate_Store*> trusted_roots;
970 trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
971
972 return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
973 }
974
976 size_t key_strength,
977 bool ocsp_intermediates,
978 std::chrono::seconds max_ocsp_age) :
979 m_require_revocation_information(require_rev),
980 m_ocsp_all_intermediates(ocsp_intermediates),
981 m_minimum_key_strength(key_strength),
982 m_max_ocsp_age(max_ocsp_age)
983 {
984 if(key_strength <= 80)
985 { m_trusted_hashes.insert("SHA-160"); }
986
987 m_trusted_hashes.insert("SHA-224");
988 m_trusted_hashes.insert("SHA-256");
989 m_trusted_hashes.insert("SHA-384");
990 m_trusted_hashes.insert("SHA-512");
991 }
992
993namespace {
994CertificatePathStatusCodes find_warnings(const CertificatePathStatusCodes& all_statuses)
995 {
997 for(const auto& status_set_i : all_statuses)
998 {
999 std::set<Certificate_Status_Code> warning_set_i;
1000 for(const auto& code : status_set_i)
1001 {
1004 {
1005 warning_set_i.insert(code);
1006 }
1007 }
1008 warnings.push_back(warning_set_i);
1009 }
1010 return warnings;
1011 }
1012}
1013
1015 std::vector<std::shared_ptr<const X509_Certificate>>&& cert_chain) :
1016 m_all_status(status),
1017 m_warnings(find_warnings(m_all_status)),
1018 m_cert_path(cert_chain),
1019 m_overall(PKIX::overall_status(m_all_status))
1020 {
1021 }
1022
1024 {
1025 if(m_cert_path.empty())
1026 throw Invalid_State("Path_Validation_Result::trust_root no path set");
1028 throw Invalid_State("Path_Validation_Result::trust_root meaningless with invalid status");
1029
1030 return *m_cert_path[m_cert_path.size()-1];
1031 }
1032
1033std::set<std::string> Path_Validation_Result::trusted_hashes() const
1034 {
1035 std::set<std::string> hashes;
1036 for(size_t i = 0; i != m_cert_path.size(); ++i)
1037 hashes.insert(m_cert_path[i]->hash_used_for_signature());
1038 return hashes;
1039 }
1040
1042 {
1046 }
1047
1049 {
1050 for(auto status_set_i : m_warnings)
1051 if(!status_set_i.empty())
1052 return false;
1053 return true;
1054 }
1055
1057 {
1058 return m_warnings;
1059 }
1060
1062 {
1063 return status_string(result());
1064 }
1065
1067 {
1068 if(const char* s = to_string(code))
1069 return s;
1070
1071 return "Unknown error";
1072 }
1073
1075 {
1076 const std::string sep(", ");
1077 std::string res;
1078 for(size_t i = 0; i < m_warnings.size(); i++)
1079 {
1080 for(auto code : m_warnings[i])
1081 res += "[" + std::to_string(i) + "] " + status_string(code) + sep;
1082 }
1083 // remove last sep
1084 if(res.size() >= sep.size())
1085 res = res.substr(0, res.size() - sep.size());
1086 return res;
1087 }
1088}
#define BOTAN_ASSERT_NONNULL(ptr)
Definition: assert.h:107
static BigInt decode(const uint8_t buf[], size_t length)
Definition: bigint.h:805
std::shared_ptr< const X509_Certificate > find_cert(const X509_DN &subject_dn, const std::vector< uint8_t > &key_id) const override
Definition: certstor.cpp:65
std::vector< std::shared_ptr< const X509_Certificate > > find_all_certs(const X509_DN &subject_dn, const std::vector< uint8_t > &key_id) const override
Definition: certstor.cpp:86
void add_certificate(const X509_Certificate &cert)
Definition: certstor.cpp:38
const std::vector< OID > & get_extension_oids() const
Definition: pkix_types.h:451
std::vector< std::pair< std::unique_ptr< Certificate_Extension >, bool > > extensions() const
Definition: x509_ext.cpp:217
bool require_revocation_information() const
Definition: x509path.h:85
const std::set< std::string > & trusted_hashes() const
Definition: x509path.h:98
std::chrono::seconds max_ocsp_age() const
Definition: x509path.h:111
Path_Validation_Restrictions(bool require_rev=false, size_t minimum_key_strength=110, bool ocsp_all_intermediates=false, std::chrono::seconds max_ocsp_age=std::chrono::seconds::zero())
Definition: x509path.cpp:975
Certificate_Status_Code result() const
Definition: x509path.h:161
Path_Validation_Result(CertificatePathStatusCodes status, std::vector< std::shared_ptr< const X509_Certificate > > &&cert_chain)
Definition: x509path.cpp:1014
bool successful_validation() const
Definition: x509path.cpp:1041
static const char * status_string(Certificate_Status_Code code)
Definition: x509path.cpp:1066
const X509_Certificate & trust_root() const
Definition: x509path.cpp:1023
std::string result_string() const
Definition: x509path.cpp:1061
std::set< std::string > trusted_hashes() const
Definition: x509path.cpp:1033
std::string warnings_string() const
Definition: x509path.cpp:1074
CertificatePathStatusCodes warnings() const
Definition: x509path.cpp:1056
const std::vector< uint8_t > & authority_key_id() const
Definition: x509cert.cpp:450
const X509_DN & issuer_dn() const
Definition: x509cert.cpp:471
static size_t lookup_ub(const OID &oid)
Definition: x509_dn_ub.cpp:47
std::string to_string(const BER_Object &obj)
Definition: asn1_obj.cpp:213
Response GET_sync(const std::string &url, size_t allowable_redirects, std::chrono::milliseconds timeout)
Definition: http_util.cpp:249
Response POST_sync(const std::string &url, const std::string &content_type, const std::vector< uint8_t > &body, size_t allowable_redirects, std::chrono::milliseconds timeout)
Definition: http_util.cpp:256
BOTAN_UNSTABLE_API std::string oid2str_or_empty(const OID &oid)
Definition: oids.cpp:111
void merge_revocation_status(CertificatePathStatusCodes &chain_status, const CertificatePathStatusCodes &crl_status, const CertificatePathStatusCodes &ocsp_status, bool require_rev_on_end_entity, bool require_rev_on_intermediates)
Definition: x509path.cpp:772
CertificatePathStatusCodes check_chain(const std::vector< std::shared_ptr< const X509_Certificate > > &cert_path, std::chrono::system_clock::time_point ref_time, const std::string &hostname, Usage_Type usage, size_t min_signature_algo_strength, const std::set< std::string > &trusted_hashes)
Definition: x509path.cpp:32
CertificatePathStatusCodes check_crl(const std::vector< std::shared_ptr< const X509_Certificate > > &cert_path, const std::vector< std::shared_ptr< const X509_CRL > > &crls, std::chrono::system_clock::time_point ref_time)
Definition: x509path.cpp:265
Certificate_Status_Code overall_status(const CertificatePathStatusCodes &cert_status)
Definition: x509path.cpp:823
CertificatePathStatusCodes check_ocsp(const std::vector< std::shared_ptr< const X509_Certificate > > &cert_path, const std::vector< std::shared_ptr< const OCSP::Response > > &ocsp_responses, const std::vector< Certificate_Store * > &certstores, std::chrono::system_clock::time_point ref_time, std::chrono::seconds max_ocsp_age=std::chrono::seconds::zero())
Definition: x509path.cpp:214
Certificate_Status_Code build_certificate_path(std::vector< std::shared_ptr< const X509_Certificate > > &cert_path_out, const std::vector< Certificate_Store * > &trusted_certstores, const std::shared_ptr< const X509_Certificate > &end_entity, const std::vector< std::shared_ptr< const X509_Certificate > > &end_entity_extra)
Definition: x509path.cpp:524
Certificate_Status_Code build_all_certificate_paths(std::vector< std::vector< std::shared_ptr< const X509_Certificate > > > &cert_paths, const std::vector< Certificate_Store * > &trusted_certstores, const std::shared_ptr< const X509_Certificate > &end_entity, const std::vector< std::shared_ptr< const X509_Certificate > > &end_entity_extra)
Definition: x509path.cpp:629
Definition: alg_id.cpp:13
std::vector< std::set< Certificate_Status_Code > > CertificatePathStatusCodes
Definition: x509path.h:29
Path_Validation_Result x509_path_validate(const std::vector< X509_Certificate > &end_certs, const Path_Validation_Restrictions &restrictions, const std::vector< Certificate_Store * > &trusted_roots, const std::string &hostname, Usage_Type usage, std::chrono::system_clock::time_point ref_time, std::chrono::milliseconds ocsp_timeout, const std::vector< std::shared_ptr< const OCSP::Response > > &ocsp_resp)
Definition: x509path.cpp:846
Usage_Type
Definition: x509cert.h:23
std::string to_string(ErrorType type)
Convert an ErrorType to string.
Definition: exceptn.cpp:11
Certificate_Status_Code
Definition: pkix_enums.h:17
@ CRL_SIGN
Definition: pkix_enums.h:114
@ KEY_CERT_SIGN
Definition: pkix_enums.h:113