diff options
author | lloyd <[email protected]> | 2012-02-06 14:12:35 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2012-02-06 14:12:35 +0000 |
commit | cd58927000ef86eacc9de5b80f361d4d05e71731 (patch) | |
tree | 975d2e50e77567d14ea3d24e6ebaf24a9e4d7c3b /src/cert/x509path | |
parent | 03bc906a6a94d236f192fa3b1bb370c013fc753a (diff) |
Fully working path validation. Even fixes the cases in PKITS where we
got the answer wrong before. Still no policy or name constraints
support, though.
Diffstat (limited to 'src/cert/x509path')
-rw-r--r-- | src/cert/x509path/x509path.cpp | 186 | ||||
-rw-r--r-- | src/cert/x509path/x509path.h | 48 |
2 files changed, 204 insertions, 30 deletions
diff --git a/src/cert/x509path/x509path.cpp b/src/cert/x509path/x509path.cpp index d0153309c..e18c3b2f8 100644 --- a/src/cert/x509path/x509path.cpp +++ b/src/cert/x509path/x509path.cpp @@ -15,33 +15,193 @@ namespace Botan { -Path_Validation_Result x509_path_validate( - const X509_Certificate& cert, - const std::vector<Certificate_Store*>& certstores) +namespace { + +class PKIX_Validation_Failure : public std::exception + { + public: + PKIX_Validation_Failure(X509_Path_Validation_Code code) : m_code(code) {} + + X509_Path_Validation_Code code() const { return m_code; } + + const char* what() { return "PKIX validation failed"; } + private: + X509_Path_Validation_Code m_code; + }; + +X509_Certificate find_issuing_cert(const X509_Certificate& cert, + const std::vector<Certificate_Store*>& certstores) { const X509_DN issuer_dn = cert.issuer_dn(); const MemoryVector<byte> auth_key_id = cert.authority_key_id(); - Path_Validation_Result result; + for(size_t i = 0; i != certstores.size(); ++i) + { + std::vector<X509_Certificate> certs = + certstores[i]->find_cert_by_subject_and_key_id(issuer_dn, auth_key_id); - std::vector<X509_Certificate> cert_path; + if(certs.size() == 0) + throw PKIX_Validation_Failure(CERT_ISSUER_NOT_FOUND); + else if(certs.size() > 1) + throw PKIX_Validation_Failure(CERT_MULTIPLE_ISSUERS_FOUND); - cert_path.push_back(cert); + return certs[0]; + } + + throw PKIX_Validation_Failure(CERT_ISSUER_NOT_FOUND); + } + +std::vector<X509_CRL> find_crls_from(const X509_Certificate& cert, + const std::vector<Certificate_Store*>& certstores) + { + const X509_DN issuer_dn = cert.subject_dn(); + const MemoryVector<byte> auth_key_id = cert.subject_key_id(); for(size_t i = 0; i != certstores.size(); ++i) { - std::vector<X509_Certificate> got = - certstores[i]->find_cert_by_subject_and_key_id(issuer_dn, auth_key_id); + std::vector<X509_CRL> crl = + certstores[i]->find_crl_by_issuer_and_key_id(issuer_dn, auth_key_id); + + if(!crl.empty()) + return crl; + } + + return std::vector<X509_CRL>(); + } + +} + +std::set<std::string> Path_Validation_Result::trusted_hashes() const + { + std::set<std::string> hashes; + for(size_t i = 0; i != cert_path.size(); ++i) + hashes.insert(cert_path[i].hash_used_for_signature()); + return hashes; + } + +Path_Validation_Result x509_path_validate( + const X509_Certificate& end_cert, + const std::vector<Certificate_Store*>& certstores) + { + std::vector<X509_Certificate> certs; + certs.push_back(end_cert); + return x509_path_validate(certs, certstores); + } + +Path_Validation_Result x509_path_validate( + const std::vector<X509_Certificate>& end_certs, + Certificate_Store& store) + { + std::vector<Certificate_Store*> certstores; + certstores.push_back(&store); + + return x509_path_validate(end_certs, certstores); + } + +Path_Validation_Result x509_path_validate( + const X509_Certificate& end_cert, + Certificate_Store& store) + { + std::vector<X509_Certificate> certs; + certs.push_back(end_cert); + + std::vector<Certificate_Store*> certstores; + certstores.push_back(&store); + + return x509_path_validate(certs, certstores); + } + +Path_Validation_Result +x509_path_validate(const std::vector<X509_Certificate>& end_certs, + const std::vector<Certificate_Store*>& certstores) + { + Path_Validation_Result r; + + r.cert_path = end_certs; - // What to do if it returns more than one match? - if(got.size() == 1) + try + { + // iterate until we reach a root or cannot find the issuer + + while(!r.cert_path.back().is_self_signed()) { - cert_path.push_back(got[0]); - break; + X509_Certificate cert = find_issuing_cert(r.cert_path.back(), + certstores); + + r.cert_path.push_back(cert); } + + /* + for(size_t i = 0; i != r.cert_path.size(); ++i) + std::cout << "Cert " << i << " = " << r.cert_path[i].subject_dn() << "\n"; + */ + + X509_Time current_time(system_time()); + + for(size_t i = 0; i != r.cert_path.size(); ++i) + { + const X509_Certificate& subject = r.cert_path[i]; + + // Check all certs for valid time range + if(current_time < X509_Time(subject.start_time())) + throw PKIX_Validation_Failure(CERT_NOT_YET_VALID); + + if(current_time > X509_Time(subject.end_time())) + throw PKIX_Validation_Failure(CERT_HAS_EXPIRED); + + const bool at_self_signed_root = (i == r.cert_path.size() - 1); + + const X509_Certificate& issuer = + r.cert_path[at_self_signed_root ? (i) : (i + 1)]; + + // Check issuer constraints + if(!issuer.is_CA_cert()) // require this for self-signed end-entity? + throw PKIX_Validation_Failure(CA_CERT_NOT_FOR_CERT_ISSUER); + + if(issuer.path_limit() < i) + throw PKIX_Validation_Failure(CERT_CHAIN_TOO_LONG); + + if(subject.check_signature(issuer.subject_public_key()) == false) + throw PKIX_Validation_Failure(SIGNATURE_ERROR); + } + + r.validation_result = VERIFIED; + + for(size_t i = 1; i != r.cert_path.size(); ++i) + { + const X509_Certificate& subject = r.cert_path[i-1]; + const X509_Certificate& ca = r.cert_path[i]; + + std::vector<X509_CRL> crls = find_crls_from(ca, certstores); + + if(crls.empty()) + throw PKIX_Validation_Failure(CRL_NOT_FOUND); + + const X509_CRL& crl = crls[0]; + + if(!ca.allowed_usage(CRL_SIGN)) + throw PKIX_Validation_Failure(CA_CERT_NOT_FOR_CRL_ISSUER); + + if(current_time < X509_Time(crl.this_update())) + throw PKIX_Validation_Failure(CRL_NOT_YET_VALID); + + if(current_time > X509_Time(crl.next_update())) + throw PKIX_Validation_Failure(CRL_HAS_EXPIRED); + + if(crl.check_signature(ca.subject_public_key()) == false) + throw PKIX_Validation_Failure(SIGNATURE_ERROR); + + if(crl.is_revoked(subject)) + throw PKIX_Validation_Failure(CERT_IS_REVOKED); + } + + } + catch(PKIX_Validation_Failure& e) + { + r.validation_result = e.code(); } - return result; + return r; } } diff --git a/src/cert/x509path/x509path.h b/src/cert/x509path/x509path.h index 57e4764cc..b32a69162 100644 --- a/src/cert/x509path/x509path.h +++ b/src/cert/x509path/x509path.h @@ -34,6 +34,7 @@ enum X509_Path_Validation_Code { CERT_HAS_EXPIRED, CERT_IS_REVOKED, + CRL_NOT_FOUND, CRL_FORMAT_ERROR, CRL_ISSUER_NOT_FOUND, CRL_NOT_YET_VALID, @@ -44,38 +45,51 @@ enum X509_Path_Validation_Code { CA_CERT_NOT_FOR_CRL_ISSUER }; -enum X509_Cert_Usage { - NO_RESTRICTIONS = 0x00, - TLS_SERVER = 0x01, - TLS_CLIENT = 0x02, - CODE_SIGNING = 0x04, - EMAIL_PROTECTION = 0x08, - TIME_STAMPING = 0x10, - CRL_SIGNING = 0x20 -}; + enum Usage_Restrictions { + NO_RESTRICTIONS = 0x00, + TLS_SERVER = 0x01, + TLS_CLIENT = 0x02, + CODE_SIGNING = 0x04, + EMAIL_PROTECTION = 0x08, + TIME_STAMPING = 0x10, + CRL_SIGNING = 0x20 + }; class Path_Validation_Result { public: + Path_Validation_Result() : + validation_result(UNKNOWN_X509_ERROR), + allowed_usages(NO_RESTRICTIONS) + {} + X509_Path_Validation_Code validation_result; - X509_Cert_Usage allowed_usages; + Usage_Restrictions allowed_usages; + std::vector<X509_Certificate> cert_path; + /** + * Returns the set of hash functions you are implicitly + * trusting by trusting this result. + */ std::set<std::string> trusted_hashes() const; }; Path_Validation_Result BOTAN_DLL x509_path_validate( + const std::vector<X509_Certificate>& end_certs, + const std::vector<Certificate_Store*>& certstores); + +Path_Validation_Result BOTAN_DLL x509_path_validate( const X509_Certificate& end_cert, const std::vector<Certificate_Store*>& certstores); -inline Path_Validation_Result x509_path_validate( +Path_Validation_Result BOTAN_DLL x509_path_validate( const X509_Certificate& end_cert, - Certificate_Store& store) - { - std::vector<Certificate_Store*> store_vec; - store_vec.push_back(&store); - return x509_path_validate(end_cert, store_vec); - } + Certificate_Store& store); + +Path_Validation_Result BOTAN_DLL x509_path_validate( + const std::vector<X509_Certificate>& end_certs, + Certificate_Store& store); } |