Botan 3.0.0-alpha0
Crypto and TLS for C&
Functions
Botan::PKIX Namespace Reference

Functions

Certificate_Status_Code build_all_certificate_paths (std::vector< std::vector< X509_Certificate > > &cert_paths, const std::vector< Certificate_Store * > &trusted_certstores, const std::optional< X509_Certificate > &end_entity, const std::vector< X509_Certificate > &end_entity_extra)
 
Certificate_Status_Code build_certificate_path (std::vector< X509_Certificate > &cert_path_out, const std::vector< Certificate_Store * > &trusted_certstores, const X509_Certificate &end_entity, const std::vector< X509_Certificate > &end_entity_extra)
 
CertificatePathStatusCodes check_chain (const std::vector< 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< X509_Certificate > &cert_path, const std::vector< Certificate_Store * > &certstores, std::chrono::system_clock::time_point ref_time)
 
CertificatePathStatusCodes check_crl (const std::vector< X509_Certificate > &cert_path, const std::vector< std::optional< X509_CRL > > &crls, std::chrono::system_clock::time_point ref_time)
 
CertificatePathStatusCodes check_ocsp (const std::vector< X509_Certificate > &cert_path, const std::vector< std::optional< 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< X509_Certificate > > &  cert_paths_out,
const std::vector< Certificate_Store * > &  trusted_certstores,
const std::optional< X509_Certificate > &  end_entity,
const std::vector< 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 628 of file x509path.cpp.

632 {
633 if(!cert_paths_out.empty())
634 {
635 throw Invalid_Argument("PKIX::build_all_certificate_paths: cert_paths_out must be empty");
636 }
637
638 if(end_entity->is_self_signed())
639 {
640 return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
641 }
642
643 /*
644 * Pile up error messages
645 */
646 std::vector<Certificate_Status_Code> stats;
647
648 Certificate_Store_In_Memory ee_extras;
649 for(const auto& cert : end_entity_extra)
650 {
651 ee_extras.add_certificate(cert);
652 }
653
654 /*
655 * This is an inelegant but functional way of preventing path loops
656 * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
657 * fingerprints in the path. If there is a duplicate, we error out.
658 * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
659 */
660 std::set<std::string> certs_seen;
661
662 // new certs are added and removed from the path during the DFS
663 // it is copied into cert_paths_out when we encounter a trusted root
664 std::vector<X509_Certificate> path_so_far;
665
666 // todo can we assume that the end certificate is not trusted?
667 std::vector<cert_maybe_trusted> stack = { {end_entity, false} };
668
669 while(!stack.empty())
670 {
671 std::optional<X509_Certificate> last = stack.back().first;
672 // found a deletion marker that guides the DFS, backtracing
673 if(last == std::nullopt)
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 const bool trusted = stack.back().second;
684 stack.pop_back();
685
686 // certificate already seen?
687 const std::string fprint = last->fingerprint("SHA-256");
688 if(certs_seen.count(fprint) == 1)
689 {
690 stats.push_back(Certificate_Status_Code::CERT_CHAIN_LOOP);
691 // the current path ended in a loop
692 continue;
693 }
694
695 // the current path ends here
696 if(last->is_self_signed())
697 {
698 // found a trust anchor
699 if(trusted)
700 {
701 cert_paths_out.push_back(path_so_far);
702 cert_paths_out.back().push_back(*last);
703
704 continue;
705 }
706 // found an untrustworthy root
707 else
708 {
709 stats.push_back(Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);
710 continue;
711 }
712 }
713
714 const X509_DN issuer_dn = last->issuer_dn();
715 const std::vector<uint8_t> auth_key_id = last->authority_key_id();
716
717 // search for trusted issuers
718 std::vector<X509_Certificate> trusted_issuers;
719 for(Certificate_Store* store : trusted_certstores)
720 {
721 auto new_issuers = store->find_all_certs(issuer_dn, auth_key_id);
722 trusted_issuers.insert(trusted_issuers.end(), new_issuers.begin(), new_issuers.end());
723 }
724
725 // search the supplemental certs
726 std::vector<X509_Certificate> misc_issuers =
727 ee_extras.find_all_certs(issuer_dn, auth_key_id);
728
729 // if we could not find any issuers, the current path ends here
730 if(trusted_issuers.size() + misc_issuers.size() == 0)
731 {
732 stats.push_back(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND);
733 continue;
734 }
735
736 // push the latest certificate onto the path_so_far
737 path_so_far.push_back(*last);
738 certs_seen.emplace(fprint);
739
740 // push a deletion marker on the stack for backtracing later
741 stack.push_back({std::optional<X509_Certificate>(), false});
742
743 for(const auto& trusted_cert : trusted_issuers)
744 {
745 stack.push_back({trusted_cert,true});
746 }
747
748 for(const auto& misc : misc_issuers)
749 {
750 stack.push_back({misc,false});
751 }
752 }
753 }
754
755 // could not construct any potentially valid path
756 if(cert_paths_out.empty())
757 {
758 if(stats.empty())
759 throw Internal_Error("X509 path building failed for unknown reasons");
760 else
761 // arbitrarily return the first error
762 return stats[0];
763 }
764 else
765 {
766 return Certificate_Status_Code::OK;
767 }
768 }

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< X509_Certificate > &  cert_path_out,
const std::vector< Certificate_Store * > &  trusted_certstores,
const X509_Certificate end_entity,
const std::vector< 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 523 of file x509path.cpp.

527 {
528 if(end_entity.is_self_signed())
529 {
530 return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
531 }
532
533 /*
534 * This is an inelegant but functional way of preventing path loops
535 * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
536 * fingerprints in the path. If there is a duplicate, we error out.
537 * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
538 */
539 std::set<std::string> certs_seen;
540
541 cert_path.push_back(end_entity);
542 certs_seen.insert(end_entity.fingerprint("SHA-256"));
543
545 for(const auto& cert : end_entity_extra)
546 ee_extras.add_certificate(cert);
547
548 // iterate until we reach a root or cannot find the issuer
549 for(;;)
550 {
551 const X509_Certificate& last = cert_path.back();
552 const X509_DN issuer_dn = last.issuer_dn();
553 const std::vector<uint8_t> auth_key_id = last.authority_key_id();
554
555 std::optional<X509_Certificate> issuer;
556 bool trusted_issuer = false;
557
558 for(Certificate_Store* store : trusted_certstores)
559 {
560 issuer = store->find_cert(issuer_dn, auth_key_id);
561 if(issuer)
562 {
563 trusted_issuer = true;
564 break;
565 }
566 }
567
568 if(!issuer)
569 {
570 // fall back to searching supplemental certs
571 issuer = ee_extras.find_cert(issuer_dn, auth_key_id);
572 }
573
574 if(!issuer)
575 return Certificate_Status_Code::CERT_ISSUER_NOT_FOUND;
576
577 const std::string fprint = issuer->fingerprint("SHA-256");
578
579 if(certs_seen.count(fprint) > 0) // already seen?
580 {
581 return Certificate_Status_Code::CERT_CHAIN_LOOP;
582 }
583
584 certs_seen.insert(fprint);
585 cert_path.push_back(*issuer);
586
587 if(issuer->is_self_signed())
588 {
589 if(trusted_issuer)
590 {
591 return Certificate_Status_Code::OK;
592 }
593 else
594 {
595 return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
596 }
597 }
598 }
599 }
std::optional< X509_Certificate > find_cert(const X509_DN &subject_dn, const std::vector< uint8_t > &key_id) const override
Definition: certstor.cpp:56
void add_certificate(const X509_Certificate &cert)
Definition: certstor.cpp:38
const std::vector< uint8_t > & authority_key_id() const
Definition: x509cert.cpp:439
const X509_DN & issuer_dn() const
Definition: x509cert.cpp:460
bool is_self_signed() const
Definition: x509cert.cpp:386
std::string fingerprint(const std::string &hash_name="SHA-1") const
Definition: x509cert.cpp:692

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::fingerprint(), Botan::X509_Certificate::is_self_signed(), Botan::X509_Certificate::issuer_dn(), and Botan::OK.

◆ check_chain()

CertificatePathStatusCodes Botan::PKIX::check_chain ( const std::vector< 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 X509_Certificate& subject = cert_path[i];
75 const X509_Certificate& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
76
77 if(at_self_signed_root && (issuer.is_self_signed() == false))
78 {
79 status.insert(Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT);
80 }
81
82 if(subject.issuer_dn() != issuer.subject_dn())
83 {
84 status.insert(Certificate_Status_Code::CHAIN_NAME_MISMATCH);
85 }
86
87 // Check the serial number
88 if(subject.is_serial_negative())
89 {
90 status.insert(Certificate_Status_Code::CERT_SERIAL_NEGATIVE);
91 }
92
93 // Check the subject's DN components' length
94
95 for(const auto& dn_pair : subject.subject_dn().dn_info())
96 {
97 const size_t dn_ub = X509_DN::lookup_ub(dn_pair.first);
98 // dn_pair = <OID,str>
99 if(dn_ub > 0 && dn_pair.second.size() > dn_ub)
100 {
101 status.insert(Certificate_Status_Code::DN_TOO_LONG);
102 }
103 }
104
105 // Check all certs for valid time range
106 if(validation_time < subject.not_before())
107 status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID);
108
109 if(validation_time > subject.not_after())
110 status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);
111
112 // Check issuer constraints
113 if(!issuer.is_CA_cert() && !self_signed_ee_cert)
114 status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);
115
116 std::unique_ptr<Public_Key> issuer_key(issuer.subject_public_key());
117
118 // Check the signature algorithm is known
119 if(OIDS::oid2str_or_empty(subject.signature_algorithm().get_oid()).empty())
120 {
121 status.insert(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN);
122 }
123 else
124 {
125 // only perform the following checks if the signature algorithm is known
126 if(!issuer_key)
127 {
128 status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID);
129 }
130 else
131 {
132 const Certificate_Status_Code sig_status = subject.verify_signature(*issuer_key);
133
134 if(sig_status != Certificate_Status_Code::VERIFIED)
135 status.insert(sig_status);
136
137 if(issuer_key->estimated_strength() < min_signature_algo_strength)
138 status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);
139 }
140
141 // Ignore untrusted hashes on self-signed roots
142 if(!trusted_hashes.empty() && !at_self_signed_root)
143 {
144 if(trusted_hashes.count(subject.hash_used_for_signature()) == 0)
145 status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
146 }
147 }
148
149 // Check cert extensions
150
151 if(subject.x509_version() == 1)
152 {
153 if(subject.v2_issuer_key_id().empty() == false ||
154 subject.v2_subject_key_id().empty() == false)
155 {
156 status.insert(Certificate_Status_Code::V2_IDENTIFIERS_IN_V1_CERT);
157 }
158 }
159
160 Extensions extensions = subject.v3_extensions();
161 const auto& extensions_vec = extensions.extensions();
162 if(subject.x509_version() < 3 && !extensions_vec.empty())
163 {
164 status.insert(Certificate_Status_Code::EXT_IN_V1_V2_CERT);
165 }
166 for(auto& extension : extensions_vec)
167 {
168 extension.first->validate(subject, issuer, cert_path, cert_status, i);
169 }
170 if(extensions.extensions().size() != extensions.get_extension_oids().size())
171 {
172 status.insert(Certificate_Status_Code::DUPLICATE_CERT_EXTENSION);
173 }
174 }
175
176 // path len check
177 size_t max_path_length = cert_path.size();
178 for(size_t i = cert_path.size() - 1; i > 0 ; --i)
179 {
180 std::set<Certificate_Status_Code>& status = cert_status.at(i);
181 const X509_Certificate& subject = cert_path[i];
182
183 /*
184 * If the certificate was not self-issued, verify that max_path_length is
185 * greater than zero and decrement max_path_length by 1.
186 */
187 if(subject.subject_dn() != subject.issuer_dn())
188 {
189 if(max_path_length > 0)
190 {
191 --max_path_length;
192 }
193 else
194 {
195 status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG);
196 }
197 }
198
199 /*
200 * If pathLenConstraint is present in the certificate and is less than max_path_length,
201 * set max_path_length to the value of pathLenConstraint.
202 */
203 if(subject.path_limit() != Cert_Extension::NO_CERT_PATH_LIMIT && subject.path_limit() < max_path_length)
204 {
205 max_path_length = subject.path_limit();
206 }
207 }
208
209 return cert_status;
210 }
const OID & get_oid() const
Definition: asn1_obj.h:447
std::vector< std::pair< std::unique_ptr< Certificate_Extension >, bool > > extensions() const
Definition: x509_ext.cpp:210
bool is_CA_cert() const
Definition: x509cert.cpp:480
const X509_DN & subject_dn() const
Definition: x509cert.cpp:465
uint32_t path_limit() const
Definition: x509cert.cpp:488
const X509_Time & not_after() const
Definition: x509cert.cpp:396
const Extensions & v3_extensions() const
Definition: x509cert.cpp:516
const std::vector< uint8_t > & v2_issuer_key_id() const
Definition: x509cert.cpp:406
uint32_t x509_version() const
Definition: x509cert.cpp:381
Public_Key * subject_public_key() const
Definition: x509cert.cpp:673
bool is_serial_negative() const
Definition: x509cert.cpp:454
const std::vector< uint8_t > & v2_subject_key_id() const
Definition: x509cert.cpp:411
const X509_Time & not_before() const
Definition: x509cert.cpp:391
const std::vector< std::pair< OID, ASN1_String > > & dn_info() const
Definition: pkix_types.h:75
std::string hash_used_for_signature() const
Definition: x509_obj.cpp:138
Certificate_Status_Code verify_signature(const Public_Key &key) const
Definition: x509_obj.cpp:184
const AlgorithmIdentifier & signature_algorithm() const
Definition: x509_obj.h:48
@ KEY_CERT_SIGN
Definition: ffi.h:1582
BOTAN_UNSTABLE_API std::string oid2str_or_empty(const OID &oid)
Definition: oids.cpp:105
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::X509_DN::dn_info(), Botan::DN_TOO_LONG, Botan::DUPLICATE_CERT_EXTENSION, Botan::EXT_IN_V1_V2_CERT, Botan::Extensions::extensions(), Botan::Extensions::get_extension_oids(), Botan::AlgorithmIdentifier::get_oid(), Botan::X509_Object::hash_used_for_signature(), Botan::INVALID_USAGE, Botan::X509_Certificate::is_CA_cert(), Botan::X509_Certificate::is_self_signed(), Botan::X509_Certificate::is_serial_negative(), Botan::X509_Certificate::issuer_dn(), Botan::KEY_CERT_SIGN, Botan::X509_DN::lookup_ub(), Botan::X509_Certificate::not_after(), Botan::X509_Certificate::not_before(), Botan::OIDS::oid2str_or_empty(), Botan::X509_Certificate::path_limit(), Botan::SIGNATURE_ALGO_UNKNOWN, Botan::X509_Object::signature_algorithm(), Botan::SIGNATURE_METHOD_TOO_WEAK, Botan::X509_Certificate::subject_dn(), Botan::X509_Certificate::subject_public_key(), Botan::UNTRUSTED_HASH, Botan::V2_IDENTIFIERS_IN_V1_CERT, Botan::X509_Certificate::v2_issuer_key_id(), Botan::X509_Certificate::v2_subject_key_id(), Botan::X509_Certificate::v3_extensions(), Botan::VERIFIED, Botan::X509_Object::verify_signature(), and Botan::X509_Certificate::x509_version().

Referenced by Botan::x509_path_validate().

◆ check_crl() [1/2]

CertificatePathStatusCodes Botan::PKIX::check_crl ( const std::vector< 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 334 of file x509path.cpp.

337 {
338 if(cert_path.empty())
339 throw Invalid_Argument("PKIX::check_crl cert_path empty");
340
341 if(certstores.empty())
342 throw Invalid_Argument("PKIX::check_crl certstores empty");
343
344 std::vector<std::optional<X509_CRL>> crls(cert_path.size());
345
346 for(size_t i = 0; i != cert_path.size(); ++i)
347 {
348 for(auto certstore : certstores)
349 {
350 crls[i] = certstore->find_crl_for(cert_path[i]);
351 if(crls[i])
352 break;
353 }
354 }
355
356 return PKIX::check_crl(cert_path, crls, ref_time);
357 }
CertificatePathStatusCodes check_crl(const std::vector< X509_Certificate > &cert_path, const std::vector< std::optional< X509_CRL > > &crls, std::chrono::system_clock::time_point ref_time)
Definition: x509path.cpp:264

References check_crl().

◆ check_crl() [2/2]

CertificatePathStatusCodes Botan::PKIX::check_crl ( const std::vector< X509_Certificate > &  cert_path,
const std::vector< std::optional< 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 264 of file x509path.cpp.

267 {
268 if(cert_path.empty())
269 throw Invalid_Argument("PKIX::check_crl cert_path empty");
270
271 CertificatePathStatusCodes cert_status(cert_path.size());
272 const X509_Time validation_time(ref_time);
273
274 for(size_t i = 0; i != cert_path.size() - 1; ++i)
275 {
276 std::set<Certificate_Status_Code>& status = cert_status.at(i);
277
278 if(i < crls.size() && crls[i].has_value())
279 {
280 const X509_Certificate& subject = cert_path.at(i);
281 const X509_Certificate& ca = cert_path.at(i+1);
282
283 if(!ca.allowed_usage(CRL_SIGN))
284 status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);
285
286 if(validation_time < crls[i]->this_update())
287 status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID);
288
289 if(validation_time > crls[i]->next_update())
290 status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED);
291
292 if(crls[i]->check_signature(ca.subject_public_key()) == false)
293 status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE);
294
295 status.insert(Certificate_Status_Code::VALID_CRL_CHECKED);
296
297 if(crls[i]->is_revoked(subject))
298 status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
299
300 std::string dp = subject.crl_distribution_point();
301 if(!dp.empty())
302 {
303 if(dp != crls[i]->crl_issuing_distribution_point())
304 {
305 status.insert(Certificate_Status_Code::NO_MATCHING_CRLDP);
306 }
307 }
308
309 for(const auto& extension : crls[i]->extensions().extensions())
310 {
311 // XXX this is wrong - the OID might be defined but the extention not full parsed
312 // for example see #1652
313
314 // is the extension critical and unknown?
315 if(extension.second && OIDS::oid2str_or_empty(extension.first->oid_of()).empty())
316 {
317 /* NIST Certificate Path Valiadation Testing document: "When an implementation does not recognize a critical extension in the
318 * crlExtensions field, it shall assume that identified certificates have been revoked and are no longer valid"
319 */
320 status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
321 }
322 }
323
324 }
325 }
326
327 while(!cert_status.empty() && cert_status.back().empty())
328 cert_status.pop_back();
329
330 return cert_status;
331 }
bool allowed_usage(Key_Constraints usage) const
Definition: x509cert.cpp:521
std::string crl_distribution_point() const
Definition: x509cert.cpp:607
@ CRL_SIGN
Definition: ffi.h:1583

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

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

◆ check_ocsp()

CertificatePathStatusCodes Botan::PKIX::check_ocsp ( const std::vector< X509_Certificate > &  cert_path,
const std::vector< std::optional< 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 213 of file x509path.cpp.

218 {
219 if(cert_path.empty())
220 throw Invalid_Argument("PKIX::check_ocsp cert_path empty");
221
222 CertificatePathStatusCodes cert_status(cert_path.size() - 1);
223
224 for(size_t i = 0; i != cert_path.size() - 1; ++i)
225 {
226 std::set<Certificate_Status_Code>& status = cert_status.at(i);
227
228 const X509_Certificate& subject = cert_path.at(i);
229 const X509_Certificate& ca = cert_path.at(i+1);
230
231 if(i < ocsp_responses.size() && (ocsp_responses.at(i) != std::nullopt)
232 && (ocsp_responses.at(i)->status() == OCSP::Response_Status_Code::Successful))
233 {
234 try
235 {
236 Certificate_Status_Code ocsp_signature_status = ocsp_responses.at(i)->check_signature(trusted_certstores, cert_path);
237
238 if(ocsp_signature_status == Certificate_Status_Code::OCSP_SIGNATURE_OK)
239 {
240 // Signature ok, so check the claimed status
241 Certificate_Status_Code ocsp_status = ocsp_responses.at(i)->status_for(ca, subject, ref_time, max_ocsp_age);
242 status.insert(ocsp_status);
243 }
244 else
245 {
246 // Some signature problem
247 status.insert(ocsp_signature_status);
248 }
249 }
250 catch(Exception&)
251 {
252 status.insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID);
253 }
254 }
255 }
256
257 while(!cert_status.empty() && cert_status.back().empty())
258 cert_status.pop_back();
259
260 return cert_status;
261 }
bool check_signature(const Public_Key &key) const
Definition: x509_obj.cpp:178

References Botan::X509_Object::check_signature(), 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 771 of file x509path.cpp.

776 {
777 if(chain_status.empty())
778 throw Invalid_Argument("PKIX::merge_revocation_status chain_status was empty");
779
780 for(size_t i = 0; i != chain_status.size() - 1; ++i)
781 {
782 bool had_crl = false, had_ocsp = false;
783
784 if(i < crl.size() && !crl[i].empty())
785 {
786 for(auto&& code : crl[i])
787 {
788 if(code == Certificate_Status_Code::VALID_CRL_CHECKED)
789 {
790 had_crl = true;
791 }
792 chain_status[i].insert(code);
793 }
794 }
795
796 if(i < ocsp.size() && !ocsp[i].empty())
797 {
798 for(auto&& code : ocsp[i])
799 {
800 if(code == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
801 code == Certificate_Status_Code::OCSP_NO_REVOCATION_URL || // softfail
802 code == Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE) // softfail
803 {
804 had_ocsp = true;
805 }
806
807 chain_status[i].insert(code);
808 }
809 }
810
811 if(had_crl == false && had_ocsp == false)
812 {
813 if((require_rev_on_end_entity && i == 0) ||
814 (require_rev_on_intermediates && i > 0))
815 {
816 chain_status[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA);
817 }
818 }
819 }
820 }

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 822 of file x509path.cpp.

823 {
824 if(cert_status.empty())
825 throw Invalid_Argument("PKIX::overall_status empty cert status");
826
827 Certificate_Status_Code overall_status = Certificate_Status_Code::OK;
828
829 // take the "worst" error as overall
830 for(const std::set<Certificate_Status_Code>& s : cert_status)
831 {
832 if(!s.empty())
833 {
834 auto worst = *s.rbegin();
835 // Leave informative OCSP/CRL confirmations on cert-level status only
836 if(worst >= Certificate_Status_Code::FIRST_ERROR_STATUS && worst > overall_status)
837 {
838 overall_status = worst;
839 }
840 }
841 }
842 return overall_status;
843 }
Certificate_Status_Code overall_status(const CertificatePathStatusCodes &cert_status)
Definition: x509path.cpp:822

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

Referenced by overall_status().