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