Botan 2.19.3
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 // When verifying intermediate certificates we need to truncate the
238 // cert_path so that the intermediate under investigation becomes the
239 // last certificate in the chain.
240 std::vector<std::shared_ptr<const X509_Certificate>> ocsp_cert_path(cert_path.begin() + i, cert_path.end());
241 Certificate_Status_Code ocsp_signature_status = ocsp_responses.at(i)->check_signature(trusted_certstores, ocsp_cert_path);
242
243 if(ocsp_signature_status == Certificate_Status_Code::OCSP_SIGNATURE_OK)
244 {
245 // Signature ok, so check the claimed status
246 Certificate_Status_Code ocsp_status = ocsp_responses.at(i)->status_for(*ca, *subject, ref_time, max_ocsp_age);
247 status.insert(ocsp_status);
248 }
249 else
250 {
251 // Some signature problem
252 status.insert(ocsp_signature_status);
253 }
254 }
255 catch(Exception&)
256 {
258 }
259 }
260 }
261
262 while(cert_status.size() > 0 && cert_status.back().empty())
263 cert_status.pop_back();
264
265 return cert_status;
266 }
267
269PKIX::check_crl(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
270 const std::vector<std::shared_ptr<const X509_CRL>>& crls,
271 std::chrono::system_clock::time_point ref_time)
272 {
273 if(cert_path.empty())
274 throw Invalid_Argument("PKIX::check_crl cert_path empty");
275
276 CertificatePathStatusCodes cert_status(cert_path.size());
277 const X509_Time validation_time(ref_time);
278
279 for(size_t i = 0; i != cert_path.size() - 1; ++i)
280 {
281 std::set<Certificate_Status_Code>& status = cert_status.at(i);
282
283 if(i < crls.size() && crls.at(i))
284 {
285 std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
286 std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
287
288 if(!ca->allowed_usage(CRL_SIGN))
290
291 if(validation_time < crls[i]->this_update())
293
294 if(validation_time > crls[i]->next_update())
296
297 if(crls[i]->check_signature(ca->subject_public_key()) == false)
299
301
302 if(crls[i]->is_revoked(*subject))
304
305 std::string dp = subject->crl_distribution_point();
306 if(!dp.empty())
307 {
308 if(dp != crls[i]->crl_issuing_distribution_point())
309 {
311 }
312 }
313
314 for(const auto& extension : crls[i]->extensions().extensions())
315 {
316 // XXX this is wrong - the OID might be defined but the extention not full parsed
317 // for example see #1652
318
319 // is the extension critical and unknown?
320 if(extension.second && OIDS::oid2str_or_empty(extension.first->oid_of()) == "")
321 {
322 /* NIST Certificate Path Valiadation Testing document: "When an implementation does not recognize a critical extension in the
323 * crlExtensions field, it shall assume that identified certificates have been revoked and are no longer valid"
324 */
326 }
327 }
328
329 }
330 }
331
332 while(cert_status.size() > 0 && cert_status.back().empty())
333 cert_status.pop_back();
334
335 return cert_status;
336 }
337
339PKIX::check_crl(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
340 const std::vector<Certificate_Store*>& certstores,
341 std::chrono::system_clock::time_point ref_time)
342 {
343 if(cert_path.empty())
344 throw Invalid_Argument("PKIX::check_crl cert_path empty");
345
346 if(certstores.empty())
347 throw Invalid_Argument("PKIX::check_crl certstores empty");
348
349 std::vector<std::shared_ptr<const X509_CRL>> crls(cert_path.size());
350
351 for(size_t i = 0; i != cert_path.size(); ++i)
352 {
353 BOTAN_ASSERT_NONNULL(cert_path[i]);
354 for(size_t c = 0; c != certstores.size(); ++c)
355 {
356 crls[i] = certstores[c]->find_crl_for(*cert_path[i]);
357 if(crls[i])
358 break;
359 }
360 }
361
362 return PKIX::check_crl(cert_path, crls, ref_time);
363 }
364
365#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
366
368PKIX::check_ocsp_online(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
369 const std::vector<Certificate_Store*>& trusted_certstores,
370 std::chrono::system_clock::time_point ref_time,
371 std::chrono::milliseconds timeout,
372 bool ocsp_check_intermediate_CAs,
373 std::chrono::seconds max_ocsp_age)
374 {
375 if(cert_path.empty())
376 throw Invalid_Argument("PKIX::check_ocsp_online cert_path empty");
377
378 std::vector<std::future<std::shared_ptr<const OCSP::Response>>> ocsp_response_futures;
379
380 size_t to_ocsp = 1;
381
382 if(ocsp_check_intermediate_CAs)
383 to_ocsp = cert_path.size() - 1;
384 if(cert_path.size() == 1)
385 to_ocsp = 0;
386
387 for(size_t i = 0; i < to_ocsp; ++i)
388 {
389 const std::shared_ptr<const X509_Certificate>& subject = cert_path.at(i);
390 const std::shared_ptr<const X509_Certificate>& issuer = cert_path.at(i+1);
391
392 if(subject->ocsp_responder() == "")
393 {
394 ocsp_response_futures.emplace_back(std::async(std::launch::deferred, [&]() -> std::shared_ptr<const OCSP::Response> {
395 return std::make_shared<const OCSP::Response>(Certificate_Status_Code::OCSP_NO_REVOCATION_URL);
396 }));
397 }
398 else
399 {
400 ocsp_response_futures.emplace_back(std::async(std::launch::async, [&]() -> std::shared_ptr<const OCSP::Response> {
401 OCSP::Request req(*issuer, BigInt::decode(subject->serial_number()));
402
403 HTTP::Response http;
404 try
405 {
406 http = HTTP::POST_sync(subject->ocsp_responder(),
407 "application/ocsp-request",
408 req.BER_encode(),
409 /*redirects*/1,
410 timeout);
411 }
412 catch(std::exception&)
413 {
414 // log e.what() ?
415 }
416 if (http.status_code() != 200)
417 return std::make_shared<const OCSP::Response>(Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE);
418 // Check the MIME type?
419
420 return std::make_shared<const OCSP::Response>(http.body());
421 }));
422 }
423 }
424
425 std::vector<std::shared_ptr<const OCSP::Response>> ocsp_responses;
426
427 for(size_t i = 0; i < ocsp_response_futures.size(); ++i)
428 {
429 ocsp_responses.push_back(ocsp_response_futures[i].get());
430 }
431
432 return PKIX::check_ocsp(cert_path, ocsp_responses, trusted_certstores, ref_time, max_ocsp_age);
433 }
434
436PKIX::check_crl_online(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
437 const std::vector<Certificate_Store*>& certstores,
438 Certificate_Store_In_Memory* crl_store,
439 std::chrono::system_clock::time_point ref_time,
440 std::chrono::milliseconds timeout)
441 {
442 if(cert_path.empty())
443 throw Invalid_Argument("PKIX::check_crl_online cert_path empty");
444 if(certstores.empty())
445 throw Invalid_Argument("PKIX::check_crl_online certstores empty");
446
447 std::vector<std::future<std::shared_ptr<const X509_CRL>>> future_crls;
448 std::vector<std::shared_ptr<const X509_CRL>> crls(cert_path.size());
449
450 for(size_t i = 0; i != cert_path.size(); ++i)
451 {
452 const std::shared_ptr<const X509_Certificate>& cert = cert_path.at(i);
453 for(size_t c = 0; c != certstores.size(); ++c)
454 {
455 crls[i] = certstores[c]->find_crl_for(*cert);
456 if(crls[i])
457 break;
458 }
459
460 // TODO: check if CRL is expired and re-request?
461
462 // Only request if we don't already have a CRL
463 if(crls[i])
464 {
465 /*
466 We already have a CRL, so just insert this empty one to hold a place in the vector
467 so that indexes match up
468 */
469 future_crls.emplace_back(std::future<std::shared_ptr<const X509_CRL>>());
470 }
471 else if(cert->crl_distribution_point() == "")
472 {
473 // Avoid creating a thread for this case
474 future_crls.emplace_back(std::async(std::launch::deferred, [&]() -> std::shared_ptr<const X509_CRL> {
475 throw Not_Implemented("No CRL distribution point for this certificate");
476 }));
477 }
478 else
479 {
480 future_crls.emplace_back(std::async(std::launch::async, [&]() -> std::shared_ptr<const X509_CRL> {
481 auto http = HTTP::GET_sync(cert->crl_distribution_point(),
482 /*redirects*/ 1, timeout);
483
484 http.throw_unless_ok();
485 // check the mime type?
486 return std::make_shared<const X509_CRL>(http.body());
487 }));
488 }
489 }
490
491 for(size_t i = 0; i != future_crls.size(); ++i)
492 {
493 if(future_crls[i].valid())
494 {
495 try
496 {
497 crls[i] = future_crls[i].get();
498 }
499 catch(std::exception&)
500 {
501 // crls[i] left null
502 // todo: log exception e.what() ?
503 }
504 }
505 }
506
507 const CertificatePathStatusCodes crl_status = PKIX::check_crl(cert_path, crls, ref_time);
508
509 if(crl_store)
510 {
511 for(size_t i = 0; i != crl_status.size(); ++i)
512 {
513 if(crl_status[i].count(Certificate_Status_Code::VALID_CRL_CHECKED))
514 {
515 // better be non-null, we supposedly validated it
516 BOTAN_ASSERT_NONNULL(crls[i]);
517 crl_store->add_crl(crls[i]);
518 }
519 }
520 }
521
522 return crl_status;
523 }
524
525#endif
526
528PKIX::build_certificate_path(std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
529 const std::vector<Certificate_Store*>& trusted_certstores,
530 const std::shared_ptr<const X509_Certificate>& end_entity,
531 const std::vector<std::shared_ptr<const X509_Certificate>>& end_entity_extra)
532 {
533 if(end_entity->is_self_signed())
534 {
536 }
537
538 /*
539 * This is an inelegant but functional way of preventing path loops
540 * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
541 * fingerprints in the path. If there is a duplicate, we error out.
542 * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
543 */
544 std::set<std::string> certs_seen;
545
546 cert_path.push_back(end_entity);
547 certs_seen.insert(end_entity->fingerprint("SHA-256"));
548
549 Certificate_Store_In_Memory ee_extras;
550 for(size_t i = 0; i != end_entity_extra.size(); ++i)
551 ee_extras.add_certificate(end_entity_extra[i]);
552
553 // iterate until we reach a root or cannot find the issuer
554 for(;;)
555 {
556 const X509_Certificate& last = *cert_path.back();
557 const X509_DN issuer_dn = last.issuer_dn();
558 const std::vector<uint8_t> auth_key_id = last.authority_key_id();
559
560 std::shared_ptr<const X509_Certificate> issuer;
561 bool trusted_issuer = false;
562
563 for(Certificate_Store* store : trusted_certstores)
564 {
565 issuer = store->find_cert(issuer_dn, auth_key_id);
566 if(issuer)
567 {
568 trusted_issuer = true;
569 break;
570 }
571 }
572
573 if(!issuer)
574 {
575 // fall back to searching supplemental certs
576 issuer = ee_extras.find_cert(issuer_dn, auth_key_id);
577 }
578
579 if(!issuer)
581
582 const std::string fprint = issuer->fingerprint("SHA-256");
583
584 if(certs_seen.count(fprint) > 0) // already seen?
585 {
587 }
588
589 certs_seen.insert(fprint);
590 cert_path.push_back(issuer);
591
592 if(issuer->is_self_signed())
593 {
594 if(trusted_issuer)
595 {
597 }
598 else
599 {
601 }
602 }
603 }
604 }
605
606/**
607 * utilities for PKIX::build_all_certificate_paths
608 */
609namespace
610{
611// <certificate, trusted?>
612using cert_maybe_trusted = std::pair<std::shared_ptr<const X509_Certificate>,bool>;
613}
614
615/**
616 * Build all possible certificate paths from the end certificate to self-signed trusted roots.
617 *
618 * All potentially valid paths are put into the cert_paths vector. If no potentially valid paths are found,
619 * one of the encountered errors is returned arbitrarily.
620 *
621 * todo add a path building function that returns detailed information on errors encountered while building
622 * the potentially numerous path candidates.
623 *
624 * Basically, a DFS is performed starting from the end certificate. A stack (vector) serves to control the DFS.
625 * At the beginning of each iteration, a pair is popped from the stack that contains (1) the next certificate
626 * to add to the path (2) a bool that indicates if the certificate is part of a trusted certstore. Ideally, we
627 * follow the unique issuer of the current certificate until a trusted root is reached. However, the issuer DN +
628 * authority key id need not be unique among the certificates used for building the path. In such a case,
629 * we consider all the matching issuers by pushing <IssuerCert, trusted?> on the stack for each of them.
630 *
631 */
633PKIX::build_all_certificate_paths(std::vector<std::vector<std::shared_ptr<const X509_Certificate>>>& cert_paths_out,
634 const std::vector<Certificate_Store*>& trusted_certstores,
635 const std::shared_ptr<const X509_Certificate>& end_entity,
636 const std::vector<std::shared_ptr<const X509_Certificate>>& end_entity_extra)
637 {
638 if(!cert_paths_out.empty())
639 {
640 throw Invalid_Argument("PKIX::build_all_certificate_paths: cert_paths_out must be empty");
641 }
642
643 if(end_entity->is_self_signed())
644 {
646 }
647
648 /*
649 * Pile up error messages
650 */
651 std::vector<Certificate_Status_Code> stats;
652
653 Certificate_Store_In_Memory ee_extras;
654 for(size_t i = 0; i != end_entity_extra.size(); ++i)
655 {
656 ee_extras.add_certificate(end_entity_extra[i]);
657 }
658
659 /*
660 * This is an inelegant but functional way of preventing path loops
661 * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
662 * fingerprints in the path. If there is a duplicate, we error out.
663 * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
664 */
665 std::set<std::string> certs_seen;
666
667 // new certs are added and removed from the path during the DFS
668 // it is copied into cert_paths_out when we encounter a trusted root
669 std::vector<std::shared_ptr<const X509_Certificate>> path_so_far;
670
671 // todo can we assume that the end certificate is not trusted?
672 std::vector<cert_maybe_trusted> stack = { {end_entity, false} };
673
674 while(!stack.empty())
675 {
676 // found a deletion marker that guides the DFS, backtracing
677 if(stack.back().first == nullptr)
678 {
679 stack.pop_back();
680 std::string fprint = path_so_far.back()->fingerprint("SHA-256");
681 certs_seen.erase(fprint);
682 path_so_far.pop_back();
683 }
684 // process next cert on the path
685 else
686 {
687 std::shared_ptr<const X509_Certificate> last = stack.back().first;
688 bool trusted = stack.back().second;
689 stack.pop_back();
690
691 // certificate already seen?
692 const std::string fprint = last->fingerprint("SHA-256");
693 if(certs_seen.count(fprint) == 1)
694 {
696 // the current path ended in a loop
697 continue;
698 }
699
700 // the current path ends here
701 if(last->is_self_signed())
702 {
703 // found a trust anchor
704 if(trusted)
705 {
706 cert_paths_out.push_back(path_so_far);
707 cert_paths_out.back().push_back(last);
708
709 continue;
710 }
711 // found an untrustworthy root
712 else
713 {
715 continue;
716 }
717 }
718
719 const X509_DN issuer_dn = last->issuer_dn();
720 const std::vector<uint8_t> auth_key_id = last->authority_key_id();
721
722 // search for trusted issuers
723 std::vector<std::shared_ptr<const X509_Certificate>> trusted_issuers;
724 for(Certificate_Store* store : trusted_certstores)
725 {
726 auto new_issuers = store->find_all_certs(issuer_dn, auth_key_id);
727 trusted_issuers.insert(trusted_issuers.end(), new_issuers.begin(), new_issuers.end());
728 }
729
730 // search the supplemental certs
731 std::vector<std::shared_ptr<const X509_Certificate>> misc_issuers =
732 ee_extras.find_all_certs(issuer_dn, auth_key_id);
733
734 // if we could not find any issuers, the current path ends here
735 if(trusted_issuers.size() + misc_issuers.size() == 0)
736 {
738 continue;
739 }
740
741 // push the latest certificate onto the path_so_far
742 path_so_far.push_back(last);
743 certs_seen.emplace(fprint);
744
745 // push a deletion marker on the stack for backtracing later
746 stack.push_back({std::shared_ptr<const X509_Certificate>(nullptr),false});
747
748 for(const auto& trusted_cert : trusted_issuers)
749 {
750 stack.push_back({trusted_cert,true});
751 }
752
753 for(const auto& misc : misc_issuers)
754 {
755 stack.push_back({misc,false});
756 }
757 }
758 }
759
760 // could not construct any potentially valid path
761 if(cert_paths_out.empty())
762 {
763 if(stats.empty())
764 throw Internal_Error("X509 path building failed for unknown reasons");
765 else
766 // arbitrarily return the first error
767 return stats[0];
768 }
769 else
770 {
772 }
773 }
774
775
776void PKIX::merge_revocation_status(CertificatePathStatusCodes& chain_status,
778 const CertificatePathStatusCodes& ocsp,
779 bool require_rev_on_end_entity,
780 bool require_rev_on_intermediates)
781 {
782 if(chain_status.empty())
783 throw Invalid_Argument("PKIX::merge_revocation_status chain_status was empty");
784
785 for(size_t i = 0; i != chain_status.size() - 1; ++i)
786 {
787 bool had_crl = false, had_ocsp = false;
788
789 if(i < crl.size() && crl[i].size() > 0)
790 {
791 for(auto&& code : crl[i])
792 {
794 {
795 had_crl = true;
796 }
797 chain_status[i].insert(code);
798 }
799 }
800
801 if(i < ocsp.size() && ocsp[i].size() > 0)
802 {
803 for(auto&& code : ocsp[i])
804 {
808 {
809 had_ocsp = true;
810 }
811
812 chain_status[i].insert(code);
813 }
814 }
815
816 if(had_crl == false && had_ocsp == false)
817 {
818 if((require_rev_on_end_entity && i == 0) ||
819 (require_rev_on_intermediates && i > 0))
820 {
821 chain_status[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA);
822 }
823 }
824 }
825 }
826
827Certificate_Status_Code PKIX::overall_status(const CertificatePathStatusCodes& cert_status)
828 {
829 if(cert_status.empty())
830 throw Invalid_Argument("PKIX::overall_status empty cert status");
831
833
834 // take the "worst" error as overall
835 for(const std::set<Certificate_Status_Code>& s : cert_status)
836 {
837 if(!s.empty())
838 {
839 auto worst = *s.rbegin();
840 // Leave informative OCSP/CRL confirmations on cert-level status only
841 if(worst >= Certificate_Status_Code::FIRST_ERROR_STATUS && worst > overall_status)
842 {
843 overall_status = worst;
844 }
845 }
846 }
847 return overall_status;
848 }
849
851 const std::vector<X509_Certificate>& end_certs,
852 const Path_Validation_Restrictions& restrictions,
853 const std::vector<Certificate_Store*>& trusted_roots,
854 const std::string& hostname,
855 Usage_Type usage,
856 std::chrono::system_clock::time_point ref_time,
857 std::chrono::milliseconds ocsp_timeout,
858 const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
859 {
860 if(end_certs.empty())
861 {
862 throw Invalid_Argument("x509_path_validate called with no subjects");
863 }
864
865 std::shared_ptr<const X509_Certificate> end_entity(std::make_shared<const X509_Certificate>(end_certs[0]));
866 std::vector<std::shared_ptr<const X509_Certificate>> end_entity_extra;
867 for(size_t i = 1; i < end_certs.size(); ++i)
868 {
869 end_entity_extra.push_back(std::make_shared<const X509_Certificate>(end_certs[i]));
870 }
871
872 std::vector<std::vector<std::shared_ptr<const X509_Certificate>>> cert_paths;
873 Certificate_Status_Code path_building_result = PKIX::build_all_certificate_paths(cert_paths, trusted_roots, end_entity, end_entity_extra);
874
875 // If we cannot successfully build a chain to a trusted self-signed root, stop now
876 if(path_building_result != Certificate_Status_Code::OK)
877 {
878 return Path_Validation_Result(path_building_result);
879 }
880
881 std::vector<Path_Validation_Result> error_results;
882 // Try validating all the potentially valid paths and return the first one to validate properly
883 for(auto cert_path : cert_paths)
884 {
886 PKIX::check_chain(cert_path, ref_time,
887 hostname, usage,
888 restrictions.minimum_key_strength(),
889 restrictions.trusted_hashes());
890
891 CertificatePathStatusCodes crl_status =
892 PKIX::check_crl(cert_path, trusted_roots, ref_time);
893
894 CertificatePathStatusCodes ocsp_status;
895
896 if(ocsp_resp.size() > 0)
897 {
898 ocsp_status = PKIX::check_ocsp(cert_path, ocsp_resp, trusted_roots, ref_time, restrictions.max_ocsp_age());
899 }
900
901 if(ocsp_status.empty() && ocsp_timeout != std::chrono::milliseconds(0))
902 {
903#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL)
904 ocsp_status = PKIX::check_ocsp_online(cert_path, trusted_roots, ref_time,
905 ocsp_timeout, restrictions.ocsp_all_intermediates());
906#else
907 ocsp_status.resize(1);
908 ocsp_status[0].insert(Certificate_Status_Code::OCSP_NO_HTTP);
909#endif
910 }
911
912 PKIX::merge_revocation_status(status, crl_status, ocsp_status,
913 restrictions.require_revocation_information(),
914 restrictions.ocsp_all_intermediates());
915
916 Path_Validation_Result pvd(status, std::move(cert_path));
917 if(pvd.successful_validation())
918 {
919 return pvd;
920 }
921 else
922 {
923 error_results.push_back(std::move(pvd));
924 }
925 }
926 return error_results[0];
927 }
928
930 const X509_Certificate& end_cert,
931 const Path_Validation_Restrictions& restrictions,
932 const std::vector<Certificate_Store*>& trusted_roots,
933 const std::string& hostname,
934 Usage_Type usage,
935 std::chrono::system_clock::time_point when,
936 std::chrono::milliseconds ocsp_timeout,
937 const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
938 {
939 std::vector<X509_Certificate> certs;
940 certs.push_back(end_cert);
941 return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
942 }
943
945 const std::vector<X509_Certificate>& end_certs,
946 const Path_Validation_Restrictions& restrictions,
947 const Certificate_Store& store,
948 const std::string& hostname,
949 Usage_Type usage,
950 std::chrono::system_clock::time_point when,
951 std::chrono::milliseconds ocsp_timeout,
952 const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
953 {
954 std::vector<Certificate_Store*> trusted_roots;
955 trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
956
957 return x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
958 }
959
961 const X509_Certificate& end_cert,
962 const Path_Validation_Restrictions& restrictions,
963 const Certificate_Store& store,
964 const std::string& hostname,
965 Usage_Type usage,
966 std::chrono::system_clock::time_point when,
967 std::chrono::milliseconds ocsp_timeout,
968 const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
969 {
970 std::vector<X509_Certificate> certs;
971 certs.push_back(end_cert);
972
973 std::vector<Certificate_Store*> trusted_roots;
974 trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
975
976 return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
977 }
978
980 size_t key_strength,
981 bool ocsp_intermediates,
982 std::chrono::seconds max_ocsp_age) :
983 m_require_revocation_information(require_rev),
984 m_ocsp_all_intermediates(ocsp_intermediates),
985 m_minimum_key_strength(key_strength),
986 m_max_ocsp_age(max_ocsp_age)
987 {
988 if(key_strength <= 80)
989 { m_trusted_hashes.insert("SHA-160"); }
990
991 m_trusted_hashes.insert("SHA-224");
992 m_trusted_hashes.insert("SHA-256");
993 m_trusted_hashes.insert("SHA-384");
994 m_trusted_hashes.insert("SHA-512");
995 }
996
997namespace {
998CertificatePathStatusCodes find_warnings(const CertificatePathStatusCodes& all_statuses)
999 {
1001 for(const auto& status_set_i : all_statuses)
1002 {
1003 std::set<Certificate_Status_Code> warning_set_i;
1004 for(const auto& code : status_set_i)
1005 {
1008 {
1009 warning_set_i.insert(code);
1010 }
1011 }
1012 warnings.push_back(warning_set_i);
1013 }
1014 return warnings;
1015 }
1016}
1017
1019 std::vector<std::shared_ptr<const X509_Certificate>>&& cert_chain) :
1020 m_all_status(status),
1021 m_warnings(find_warnings(m_all_status)),
1022 m_cert_path(cert_chain),
1023 m_overall(PKIX::overall_status(m_all_status))
1024 {
1025 }
1026
1028 {
1029 if(m_cert_path.empty())
1030 throw Invalid_State("Path_Validation_Result::trust_root no path set");
1032 throw Invalid_State("Path_Validation_Result::trust_root meaningless with invalid status");
1033
1034 return *m_cert_path[m_cert_path.size()-1];
1035 }
1036
1037std::set<std::string> Path_Validation_Result::trusted_hashes() const
1038 {
1039 std::set<std::string> hashes;
1040 for(size_t i = 0; i != m_cert_path.size(); ++i)
1041 hashes.insert(m_cert_path[i]->hash_used_for_signature());
1042 return hashes;
1043 }
1044
1051
1053 {
1054 for(auto status_set_i : m_warnings)
1055 if(!status_set_i.empty())
1056 return false;
1057 return true;
1058 }
1059
1061 {
1062 return m_warnings;
1063 }
1064
1066 {
1067 return status_string(result());
1068 }
1069
1071 {
1072 if(const char* s = to_string(code))
1073 return s;
1074
1075 return "Unknown error";
1076 }
1077
1079 {
1080 const std::string sep(", ");
1081 std::string res;
1082 for(size_t i = 0; i < m_warnings.size(); i++)
1083 {
1084 for(auto code : m_warnings[i])
1085 res += "[" + std::to_string(i) + "] " + status_string(code) + sep;
1086 }
1087 // remove last sep
1088 if(res.size() >= sep.size())
1089 res = res.substr(0, res.size() - sep.size());
1090 return res;
1091 }
1092}
#define BOTAN_ASSERT_NONNULL(ptr)
Definition assert.h:107
static BigInt decode(const uint8_t buf[], size_t length)
Definition bigint.h:805
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:979
Certificate_Status_Code result() const
Definition x509path.h:161
Path_Validation_Result(CertificatePathStatusCodes status, std::vector< std::shared_ptr< const X509_Certificate > > &&cert_chain)
static const char * status_string(Certificate_Status_Code code)
const X509_Certificate & trust_root() const
std::string result_string() const
std::set< std::string > trusted_hashes() const
std::string warnings_string() const
CertificatePathStatusCodes warnings() const
static size_t lookup_ub(const OID &oid)
Response GET_sync(const std::string &url, size_t allowable_redirects, std::chrono::milliseconds timeout)
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)
BOTAN_UNSTABLE_API std::string oid2str_or_empty(const OID &oid)
Definition oids.cpp:111
std::vector< std::set< Certificate_Status_Code > > CertificatePathStatusCodes
Definition x509path.h:29
ASN1_Time X509_Time
Definition asn1_obj.h:386
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:850
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