Botan 2.19.1
Crypto and TLS for C&
Functions
Botan::PKIX Namespace Reference

Functions

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)
 
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)
 
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)
 
CertificatePathStatusCodes check_crl (const std::vector< std::shared_ptr< const X509_Certificate > > &cert_path, const std::vector< Certificate_Store * > &certstores, std::chrono::system_clock::time_point ref_time)
 
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)
 
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())
 
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)
 
Certificate_Status_Code overall_status (const CertificatePathStatusCodes &cert_status)
 

Detailed Description

namespace PKIX holds the building blocks that are called by x509_path_validate. This allows custom validation logic to be written by applications and makes for easier testing, but unless you're positive you know what you're doing you probably want to just call x509_path_validate instead.

Function Documentation

◆ build_all_certificate_paths()

Certificate_Status_Code Botan::PKIX::build_all_certificate_paths ( std::vector< std::vector< std::shared_ptr< const X509_Certificate > > > &  cert_paths_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 
)

Build all possible certificate paths from the end certificate to self-signed trusted roots.

All potentially valid paths are put into the cert_paths vector. If no potentially valid paths are found, one of the encountered errors is returned arbitrarily.

todo add a path building function that returns detailed information on errors encountered while building the potentially numerous path candidates.

Basically, a DFS is performed starting from the end certificate. A stack (vector) serves to control the DFS. At the beginning of each iteration, a pair is popped from the stack that contains (1) the next certificate to add to the path (2) a bool that indicates if the certificate is part of a trusted certstore. Ideally, we follow the unique issuer of the current certificate until a trusted root is reached. However, the issuer DN + authority key id need not be unique among the certificates used for building the path. In such a case, we consider all the matching issuers by pushing <IssuerCert, trusted?> on the stack for each of them.

Definition at line 629 of file x509path.cpp.

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 {
641 return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
642 }
643
644 /*
645 * Pile up error messages
646 */
647 std::vector<Certificate_Status_Code> stats;
648
649 Certificate_Store_In_Memory ee_extras;
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 {
691 stats.push_back(Certificate_Status_Code::CERT_CHAIN_LOOP);
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 {
710 stats.push_back(Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);
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 {
733 stats.push_back(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND);
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 {
767 return Certificate_Status_Code::OK;
768 }
769 }

References Botan::Certificate_Store_In_Memory::add_certificate(), Botan::CANNOT_ESTABLISH_TRUST, Botan::CERT_CHAIN_LOOP, Botan::CERT_ISSUER_NOT_FOUND, Botan::Certificate_Store_In_Memory::find_all_certs(), and Botan::OK.

Referenced by Botan::x509_path_validate().

◆ build_certificate_path()

Certificate_Status_Code Botan::PKIX::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 
)

Build certificate path

Parameters
cert_path_outoutput parameter, cert_path will be appended to this vector
trusted_certstoreslist of certificate stores that contain trusted certificates
end_entitythe cert to be validated
end_entity_extraoptional list of additional untrusted certs for path building
Returns
result of the path building operation (OK or error)

Definition at line 524 of file x509path.cpp.

528 {
529 if(end_entity->is_self_signed())
530 {
531 return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
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)
576 return Certificate_Status_Code::CERT_ISSUER_NOT_FOUND;
577
578 const std::string fprint = issuer->fingerprint("SHA-256");
579
580 if(certs_seen.count(fprint) > 0) // already seen?
581 {
582 return Certificate_Status_Code::CERT_CHAIN_LOOP;
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 {
592 return Certificate_Status_Code::OK;
593 }
594 else
595 {
596 return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
597 }
598 }
599 }
600 }
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
void add_certificate(const X509_Certificate &cert)
Definition: certstor.cpp:38
const std::vector< uint8_t > & authority_key_id() const
Definition: x509cert.cpp:450
const X509_DN & issuer_dn() const
Definition: x509cert.cpp:471

References Botan::Certificate_Store_In_Memory::add_certificate(), Botan::X509_Certificate::authority_key_id(), Botan::CANNOT_ESTABLISH_TRUST, Botan::CERT_CHAIN_LOOP, Botan::CERT_ISSUER_NOT_FOUND, Botan::Certificate_Store_In_Memory::find_cert(), Botan::X509_Certificate::issuer_dn(), and Botan::OK.

◆ check_chain()

CertificatePathStatusCodes Botan::PKIX::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 
)

Check the certificate chain, but not any revocation data

Parameters
cert_pathpath built by build_certificate_path with OK result
ref_timewhatever time you want to perform the validation against (normally current system clock)
hostnamethe hostname
usageend entity usage checks
min_signature_algo_strength80 or 110 typically Note 80 allows 1024 bit RSA and SHA-1. 110 allows 2048 bit RSA and SHA-2. Using 128 requires ECC (P-256) or ~3000 bit RSA keys.
trusted_hashesset of trusted hash functions, empty means accept any hash we have an OID for
Returns
vector of results on per certificate in the path, each containing a set of results. If all codes in the set are < Certificate_Status_Code::FIRST_ERROR_STATUS, then the result for that certificate is successful. If all results are

Definition at line 32 of file x509path.cpp.

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 {
80 status.insert(Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT);
81 }
82
83 if(subject->issuer_dn() != issuer->subject_dn())
84 {
85 status.insert(Certificate_Status_Code::CHAIN_NAME_MISMATCH);
86 }
87
88 // Check the serial number
89 if(subject->is_serial_negative())
90 {
91 status.insert(Certificate_Status_Code::CERT_SERIAL_NEGATIVE);
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 {
102 status.insert(Certificate_Status_Code::DN_TOO_LONG);
103 }
104 }
105
106 // Check all certs for valid time range
107 if(validation_time < subject->not_before())
108 status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID);
109
110 if(validation_time > subject->not_after())
111 status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);
112
113 // Check issuer constraints
114 if(!issuer->is_CA_cert() && !self_signed_ee_cert)
115 status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);
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 {
122 status.insert(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN);
123 }
124 else
125 {
126 // only perform the following checks if the signature algorithm is known
127 if(!issuer_key)
128 {
129 status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID);
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)
139 status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);
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)
146 status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
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 {
157 status.insert(Certificate_Status_Code::V2_IDENTIFIERS_IN_V1_CERT);
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 {
165 status.insert(Certificate_Status_Code::EXT_IN_V1_V2_CERT);
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 {
173 status.insert(Certificate_Status_Code::DUPLICATE_CERT_EXTENSION);
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 {
196 status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG);
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 }
@ KEY_CERT_SIGN
Definition: ffi.h:1571
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
Certificate_Status_Code
Definition: pkix_enums.h:17

References Botan::CA_CERT_NOT_FOR_CERT_ISSUER, Botan::CERT_CHAIN_TOO_LONG, Botan::CERT_HAS_EXPIRED, Botan::CERT_NAME_NOMATCH, Botan::CERT_NOT_YET_VALID, Botan::CERT_PUBKEY_INVALID, Botan::CERT_SERIAL_NEGATIVE, Botan::CHAIN_LACKS_TRUST_ROOT, Botan::CHAIN_NAME_MISMATCH, Botan::DN_TOO_LONG, Botan::DUPLICATE_CERT_EXTENSION, Botan::EXT_IN_V1_V2_CERT, Botan::Extensions::extensions(), Botan::Extensions::get_extension_oids(), Botan::INVALID_USAGE, Botan::KEY_CERT_SIGN, Botan::X509_DN::lookup_ub(), Botan::OIDS::oid2str_or_empty(), Botan::SIGNATURE_ALGO_UNKNOWN, Botan::SIGNATURE_METHOD_TOO_WEAK, Botan::UNTRUSTED_HASH, Botan::V2_IDENTIFIERS_IN_V1_CERT, and Botan::VERIFIED.

Referenced by Botan::x509_path_validate().

◆ check_crl() [1/2]

CertificatePathStatusCodes Botan::PKIX::check_crl ( const std::vector< std::shared_ptr< const X509_Certificate > > &  cert_path,
const std::vector< Certificate_Store * > &  certstores,
std::chrono::system_clock::time_point  ref_time 
)

Check CRLs for revocation information

Parameters
cert_pathpath already validated by check_chain
certstoresa list of certificate stores to query for the CRL
ref_timewhatever time you want to perform the validation against (normally current system clock)
Returns
revocation status

Definition at line 335 of file x509path.cpp.

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 }
#define BOTAN_ASSERT_NONNULL(ptr)
Definition: assert.h:107
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

References BOTAN_ASSERT_NONNULL, and check_crl().

◆ check_crl() [2/2]

CertificatePathStatusCodes Botan::PKIX::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 
)

Check CRLs for revocation information

Parameters
cert_pathpath already validated by check_chain
crlsthe list of CRLs to check, it is assumed that crls[i] (if not null) is the associated CRL for the subject in cert_path[i].
ref_timewhatever time you want to perform the validation against (normally current system clock)
Returns
revocation status

Definition at line 265 of file x509path.cpp.

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))
285 status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);
286
287 if(validation_time < crls[i]->this_update())
288 status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID);
289
290 if(validation_time > crls[i]->next_update())
291 status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED);
292
293 if(crls[i]->check_signature(ca->subject_public_key()) == false)
294 status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE);
295
296 status.insert(Certificate_Status_Code::VALID_CRL_CHECKED);
297
298 if(crls[i]->is_revoked(*subject))
299 status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
300
301 std::string dp = subject->crl_distribution_point();
302 if(!dp.empty())
303 {
304 if(dp != crls[i]->crl_issuing_distribution_point())
305 {
306 status.insert(Certificate_Status_Code::NO_MATCHING_CRLDP);
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 */
321 status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
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 }
@ CRL_SIGN
Definition: ffi.h:1572

References Botan::CA_CERT_NOT_FOR_CRL_ISSUER, Botan::CERT_IS_REVOKED, Botan::CRL_BAD_SIGNATURE, Botan::CRL_HAS_EXPIRED, Botan::CRL_NOT_YET_VALID, Botan::CRL_SIGN, Botan::NO_MATCHING_CRLDP, Botan::OIDS::oid2str_or_empty(), and Botan::VALID_CRL_CHECKED.

Referenced by check_crl(), and Botan::x509_path_validate().

◆ check_ocsp()

CertificatePathStatusCodes Botan::PKIX::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() 
)

Check OCSP responses for revocation information

Parameters
cert_pathpath already validated by check_chain
ocsp_responsesthe OCSP responses to consider
certstorestrusted roots
ref_timewhatever time you want to perform the validation against (normally current system clock)
max_ocsp_agemaximum age of OCSP responses w/o next_update. If zero, there is no maximum age
Returns
revocation status

Definition at line 214 of file x509path.cpp.

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 {
253 status.insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID);
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 }

References Botan::OCSP_RESPONSE_INVALID, Botan::OCSP_SIGNATURE_OK, and Botan::OCSP::Successful.

Referenced by Botan::x509_path_validate().

◆ merge_revocation_status()

void Botan::PKIX::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 
)

Merge the results from CRL and/or OCSP checks into chain_status

Parameters
chain_statusthe certificate status
crl_statusresults from check_crl
ocsp_statusresults from check_ocsp
require_rev_on_end_entityrequire valid CRL or OCSP on end-entity cert
require_rev_on_intermediatesrequire valid CRL or OCSP on all intermediate certificates

Definition at line 772 of file x509path.cpp.

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 {
789 if(code == Certificate_Status_Code::VALID_CRL_CHECKED)
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 {
801 if(code == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
802 code == Certificate_Status_Code::OCSP_NO_REVOCATION_URL || // softfail
803 code == Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE) // softfail
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 }

References Botan::NO_REVOCATION_DATA, Botan::OCSP_NO_REVOCATION_URL, Botan::OCSP_RESPONSE_GOOD, Botan::OCSP_SERVER_NOT_AVAILABLE, and Botan::VALID_CRL_CHECKED.

Referenced by Botan::x509_path_validate().

◆ overall_status()

Certificate_Status_Code Botan::PKIX::overall_status ( const CertificatePathStatusCodes cert_status)

Find overall status (OK, error) of a validation

Parameters
cert_statusresult of merge_revocation_status or check_chain

Definition at line 823 of file x509path.cpp.

824 {
825 if(cert_status.empty())
826 throw Invalid_Argument("PKIX::overall_status empty cert status");
827
828 Certificate_Status_Code overall_status = Certificate_Status_Code::OK;
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
837 if(worst >= Certificate_Status_Code::FIRST_ERROR_STATUS && worst > overall_status)
838 {
839 overall_status = worst;
840 }
841 }
842 }
843 return overall_status;
844 }
Certificate_Status_Code overall_status(const CertificatePathStatusCodes &cert_status)
Definition: x509path.cpp:823

References Botan::FIRST_ERROR_STATUS, Botan::OK, and overall_status().

Referenced by overall_status().