From a9cd9687398ca6c7a780dd91a213b514797fa9fc Mon Sep 17 00:00:00 2001 From: lloyd Date: Fri, 22 Apr 2011 20:57:44 +0000 Subject: Some changes circa Feb 22 --- checks/nist_tests/Makefile | 2 +- checks/nist_tests/x509test.cpp | 10 ++++------ checks/x509.cpp | 31 +++++++++++++++++-------------- 3 files changed, 22 insertions(+), 21 deletions(-) (limited to 'checks') diff --git a/checks/nist_tests/Makefile b/checks/nist_tests/Makefile index 6d0ebb78f..96c958dd7 100644 --- a/checks/nist_tests/Makefile +++ b/checks/nist_tests/Makefile @@ -4,7 +4,7 @@ BOTAN_CONFIG=botan-config CC=g++ FLAGS=-g -Os -W -Wall -ansi LDFLAGS=$(shell $(BOTAN_CONFIG) --libs) -CFLAGS=$(shell $(BOTAN_CONFIG) --cflags) +CFLAGS=$(shell $(BOTAN_CONFIG) --cflags) -I../../build/include x509test: x509test.cpp $(CC) $(FLAGS) $(CFLAGS) x509test.cpp $(LDFLAGS) -o x509test diff --git a/checks/nist_tests/x509test.cpp b/checks/nist_tests/x509test.cpp index 7f4fe94ee..d89e7c341 100644 --- a/checks/nist_tests/x509test.cpp +++ b/checks/nist_tests/x509test.cpp @@ -4,7 +4,7 @@ on NIST's web site. */ -#include +#include #include using namespace Botan; @@ -110,9 +110,9 @@ void run_one_test(u32bit test_no, X509_Code expected, X509_Code result = VERIFIED; - X509_Store store; + Certificate_Store_Memory store; - store.add_cert(X509_Certificate(root_cert), true); + store.add_cert(X509_Certificate(root_cert)); X509_Certificate end_user(to_verify); @@ -134,9 +134,7 @@ void run_one_test(u32bit test_no, X509_Code expected, std::cout << std::endl; } */ - result = store.add_crl(crl); - if(result != VERIFIED) - break; + store.add_crl(crl); } /* if everything has gone well up until now */ diff --git a/checks/x509.cpp b/checks/x509.cpp index 43d374411..e7d7b663d 100644 --- a/checks/x509.cpp +++ b/checks/x509.cpp @@ -21,7 +21,7 @@ #if defined(BOTAN_HAS_X509_CERTIFICATES) #include - #include + #include #include #include #endif @@ -191,19 +191,21 @@ void do_x509_tests(RandomNumberGenerator& rng) X509_CRL crl1 = ca.new_crl(rng); /* Verify the certs */ - X509_Store store; + Certificate_Store_Memory store; - store.add_cert(ca_cert, true); // second arg == true: trusted CA cert + store.add_certificate(ca_cert); std::cout << '.' << std::flush; - if(store.validate_cert(user1_cert) != VERIFIED) + + Path_Validation_Result result_u1 = x509_path_validate(user1_cert, store); + if(result_u1.validation_result != VERIFIED) std::cout << "\nFAILED: User cert #1 did not validate" << std::endl; - if(store.validate_cert(user2_cert) != VERIFIED) + Path_Validation_Result result_u2 = x509_path_validate(user2_cert, store); + if(result_u2.validation_result != VERIFIED) std::cout << "\nFAILED: User cert #2 did not validate" << std::endl; - if(store.add_crl(crl1) != VERIFIED) - std::cout << "\nFAILED: CRL #1 did not validate" << std::endl; + store.add_crl(crl1); std::vector revoked; revoked.push_back(CRL_Entry(user1_cert, CESSATION_OF_OPERATION)); @@ -211,23 +213,24 @@ void do_x509_tests(RandomNumberGenerator& rng) X509_CRL crl2 = ca.update_crl(crl1, revoked, rng); - if(store.add_crl(crl2) != VERIFIED) - std::cout << "\nFAILED: CRL #2 did not validate" << std::endl; + store.add_crl(crl2); - if(store.validate_cert(user1_cert) != CERT_IS_REVOKED) + result_u1 = x509_path_validate(user1_cert, store); + if(result_u1.validation_result != CERT_IS_REVOKED) std::cout << "\nFAILED: User cert #1 was not revoked" << std::endl; - if(store.validate_cert(user2_cert) != CERT_IS_REVOKED) + result_u2 = x509_path_validate(user2_cert, store); + if(result_u2.validation_result != CERT_IS_REVOKED) std::cout << "\nFAILED: User cert #2 was not revoked" << std::endl; revoked.clear(); revoked.push_back(CRL_Entry(user1_cert, REMOVE_FROM_CRL)); X509_CRL crl3 = ca.update_crl(crl2, revoked, rng); - if(store.add_crl(crl3) != VERIFIED) - std::cout << "\nFAILED: CRL #3 did not validate" << std::endl; + store.add_crl(crl3); - if(store.validate_cert(user1_cert) != VERIFIED) + result_u1 = x509_path_validate(user1_cert, store); + if(result_u1.validation_result != VERIFIED) std::cout << "\nFAILED: User cert #1 was not un-revoked" << std::endl; check_against_copy(ca_key, rng); -- cgit v1.2.3 From cd58927000ef86eacc9de5b80f361d4d05e71731 Mon Sep 17 00:00:00 2001 From: lloyd Date: Mon, 6 Feb 2012 14:12:35 +0000 Subject: 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. --- checks/nist_tests/Makefile | 4 +- checks/nist_tests/x509test.cpp | 58 +++---------- checks/x509.cpp | 6 +- src/asn1/x509_dn.cpp | 31 +++++++ src/asn1/x509_dn.h | 3 + src/cert/certstore/certstor.cpp | 19 ++-- src/cert/certstore/certstor.h | 14 ++- src/cert/x509cert/x509_ext.h | 2 +- src/cert/x509cert/x509_obj.cpp | 5 +- src/cert/x509cert/x509cert.cpp | 10 ++- src/cert/x509cert/x509cert.h | 10 ++- src/cert/x509crl/crl_ent.h | 20 +++++ src/cert/x509crl/x509_crl.cpp | 38 ++++++++ src/cert/x509crl/x509_crl.h | 7 ++ src/cert/x509path/x509path.cpp | 186 +++++++++++++++++++++++++++++++++++++--- src/cert/x509path/x509path.h | 48 +++++++---- src/pubkey/pubkey_enums.h | 20 ----- 17 files changed, 351 insertions(+), 130 deletions(-) (limited to 'checks') diff --git a/checks/nist_tests/Makefile b/checks/nist_tests/Makefile index 96c958dd7..52dc340c9 100644 --- a/checks/nist_tests/Makefile +++ b/checks/nist_tests/Makefile @@ -1,9 +1,9 @@ -BOTAN_CONFIG=botan-config +BOTAN_CONFIG=../../build/botan-config-1.10 CC=g++ FLAGS=-g -Os -W -Wall -ansi -LDFLAGS=$(shell $(BOTAN_CONFIG) --libs) +LDFLAGS=$(shell $(BOTAN_CONFIG) --libs) -L../.. -lbotan-1.10 CFLAGS=$(shell $(BOTAN_CONFIG) --cflags) -I../../build/include x509test: x509test.cpp diff --git a/checks/nist_tests/x509test.cpp b/checks/nist_tests/x509test.cpp index 66b274c6c..e4d55d252 100644 --- a/checks/nist_tests/x509test.cpp +++ b/checks/nist_tests/x509test.cpp @@ -20,12 +20,12 @@ using namespace Botan; std::vector dir_listing(const std::string&); -void run_one_test(u32bit, X509_Code, +void run_one_test(u32bit, X509_Path_Validation_Code, std::string, std::string, std::vector, std::vector); -std::map expected_results; +std::map expected_results; u32bit unexp_failure, unexp_success, wrong_error, skipped; @@ -96,7 +96,7 @@ int main() return 0; } -void run_one_test(u32bit test_no, X509_Code expected, +void run_one_test(u32bit test_no, X509_Path_Validation_Code expected, std::string root_cert, std::string to_verify, std::vector certs, std::vector crls) @@ -104,16 +104,14 @@ void run_one_test(u32bit test_no, X509_Code expected, std::cout << "Processing test #" << test_no << "... "; std::cout.flush(); - X509_Code result = VERIFIED; + Certificate_Store_In_Memory store; - Certificate_Store_Memory store; - - store.add_cert(X509_Certificate(root_cert)); + store.add_certificate(X509_Certificate(root_cert)); X509_Certificate end_user(to_verify); for(size_t j = 0; j != certs.size(); j++) - store.add_cert(X509_Certificate(certs[j])); + store.add_certificate(X509_Certificate(certs[j])); for(size_t j = 0; j != crls.size(); j++) { @@ -133,18 +131,9 @@ void run_one_test(u32bit test_no, X509_Code expected, store.add_crl(crl); } - /* if everything has gone well up until now */ - - if(result == VERIFIED) - { - result = store.validate_cert(end_user); - - X509_Code result2 = store.validate_cert(end_user); + Path_Validation_Result validation_result = x509_path_validate(end_user, store); - if(result != result2) - std::cout << "Two runs, two answers: " << result << " " - << result2 << std::endl; - } + X509_Path_Validation_Code result = validation_result.validation_result; if(result == expected) { @@ -232,15 +221,7 @@ void populate_expected_results() expected_results[17] = VERIFIED; expected_results[18] = VERIFIED; - /************* CHANGE OF TEST RESULT FOR TEST #19 ************************ - One of the certificates has no attached CRL. By strict X.509 rules, if - there is no good CRL in hand, then the certificate shouldn't be used for - CA stuff. But while this is usually a good idea, it interferes with simple - uses of certificates which shouldn't (IMO) force the use of CRLs. There is - no assigned error code for this scenario because I don't consider it to be - an error (probably would be something like NO_REVOCATION_DATA_AVAILABLE) - **************************************************************************/ - expected_results[19] = VERIFIED; + expected_results[19] = CRL_NOT_FOUND; expected_results[20] = CERT_IS_REVOKED; expected_results[21] = CERT_IS_REVOKED; @@ -314,23 +295,10 @@ void populate_expected_results() expected_results[64] = SIGNATURE_ERROR; - /************ CHANGE OF TEST RESULT FOR TEST #65 ************************* - I cannot figure out what exactly the problem here is supposed to be; - looking at it by hand, everything seems fine. If someone can explain I - would be happy to listen. - ************************************************************************/ - expected_results[65] = VERIFIED; - expected_results[66] = CRL_ISSUER_NOT_FOUND; - - /************ CHANGE OF TEST RESULT FOR TEST #67 ************************* - The test docs say this should be verified. However, the problem being that - there is an extra CRL with an unknown issuer. Returning VERIFIED in this - case is obviously bad, since the user may well want to know that the CRL - in question has no known issuer. So we return CRL_ISSUER_NOT_FOUND instead - of VERIFIED. The actual certificate path of course still verifies, but - it's kind of an all-or-nothing testing procedure. - ************************************************************************/ - expected_results[67] = CRL_ISSUER_NOT_FOUND; + expected_results[65] = CRL_NOT_FOUND; + expected_results[66] = CRL_NOT_FOUND; + + expected_results[67] = VERIFIED; expected_results[68] = CERT_IS_REVOKED; expected_results[69] = CERT_IS_REVOKED; diff --git a/checks/x509.cpp b/checks/x509.cpp index 919fa3508..9ae295d35 100644 --- a/checks/x509.cpp +++ b/checks/x509.cpp @@ -191,7 +191,7 @@ void do_x509_tests(RandomNumberGenerator& rng) X509_CRL crl1 = ca.new_crl(rng); /* Verify the certs */ - Certificate_Store_Memory store; + Certificate_Store_In_Memory store; store.add_certificate(ca_cert); @@ -199,11 +199,11 @@ void do_x509_tests(RandomNumberGenerator& rng) Path_Validation_Result result_u1 = x509_path_validate(user1_cert, store); if(result_u1.validation_result != VERIFIED) - std::cout << "\nFAILED: User cert #1 did not validate" << std::endl; + std::cout << "\nFAILED: User cert #1 did not validate - " << result_u1.validation_result << std::endl; Path_Validation_Result result_u2 = x509_path_validate(user2_cert, store); if(result_u2.validation_result != VERIFIED) - std::cout << "\nFAILED: User cert #2 did not validate" << std::endl; + std::cout << "\nFAILED: User cert #2 did not validate - " << result_u2.validation_result << std::endl; store.add_crl(crl1); diff --git a/src/asn1/x509_dn.cpp b/src/asn1/x509_dn.cpp index f91303296..37eecc6a3 100644 --- a/src/asn1/x509_dn.cpp +++ b/src/asn1/x509_dn.cpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace Botan { @@ -293,4 +294,34 @@ void X509_DN::decode_from(BER_Decoder& source) dn_bits = bits; } +namespace { + +std::string to_short_form(const std::string& long_id) + { + if(long_id == "X520.CommonName") + return "CN"; + + if(long_id == "X520.Organization") + return "O"; + + if(long_id == "X520.OrganizationalUnit") + return "OU"; + + return long_id; + } + +} + +std::ostream& operator<<(std::ostream& out, const X509_DN& dn) + { + std::multimap contents = dn.contents(); + + for(std::multimap::const_iterator i = contents.begin(); + i != contents.end(); ++i) + { + out << to_short_form(i->first) << "=" << i->second << ' '; + } + return out; + } + } diff --git a/src/asn1/x509_dn.h b/src/asn1/x509_dn.h index 3f63eb49c..de44f8c78 100644 --- a/src/asn1/x509_dn.h +++ b/src/asn1/x509_dn.h @@ -12,6 +12,7 @@ #include #include #include +#include namespace Botan { @@ -48,6 +49,8 @@ bool BOTAN_DLL operator==(const X509_DN&, const X509_DN&); bool BOTAN_DLL operator!=(const X509_DN&, const X509_DN&); bool BOTAN_DLL operator<(const X509_DN&, const X509_DN&); +BOTAN_DLL std::ostream& operator<<(std::ostream& out, const X509_DN& dn); + } #endif diff --git a/src/cert/certstore/certstor.cpp b/src/cert/certstore/certstor.cpp index 3cba2f39e..7aa528d04 100644 --- a/src/cert/certstore/certstor.cpp +++ b/src/cert/certstore/certstor.cpp @@ -9,12 +9,7 @@ namespace Botan { -Certificate_Store* Certificate_Store_Memory::clone() const - { - return new Certificate_Store_Memory(*this); - } - -void Certificate_Store_Memory::add_certificate(const X509_Certificate& cert) +void Certificate_Store_In_Memory::add_certificate(const X509_Certificate& cert) { for(size_t i = 0; i != certs.size(); ++i) { @@ -26,7 +21,7 @@ void Certificate_Store_Memory::add_certificate(const X509_Certificate& cert) } std::vector -Certificate_Store_Memory::find_cert_by_subject_and_key_id( +Certificate_Store_In_Memory::find_cert_by_subject_and_key_id( const X509_DN& subject_dn, const MemoryRegion& key_id) const { @@ -50,7 +45,7 @@ Certificate_Store_Memory::find_cert_by_subject_and_key_id( return result; } -void Certificate_Store_Memory::add_crl(const X509_CRL& crl) +void Certificate_Store_In_Memory::add_crl(const X509_CRL& crl) { X509_DN crl_issuer = crl.issuer_dn(); @@ -59,11 +54,9 @@ void Certificate_Store_Memory::add_crl(const X509_CRL& crl) // Found an update of a previously existing one; replace it if(crls[i].issuer_dn() == crl_issuer) { - if(crls[i].this_update() < crl.this_update()) - { + if(crls[i].this_update() <= crl.this_update()) crls[i] = crl; - return; - } + return; } } @@ -72,7 +65,7 @@ void Certificate_Store_Memory::add_crl(const X509_CRL& crl) } std::vector -Certificate_Store_Memory::find_crl_by_subject_and_key_id( +Certificate_Store_In_Memory::find_crl_by_issuer_and_key_id( const X509_DN& issuer_dn, const MemoryRegion& key_id) const { diff --git a/src/cert/certstore/certstor.h b/src/cert/certstore/certstor.h index 374013984..604541d52 100644 --- a/src/cert/certstore/certstor.h +++ b/src/cert/certstore/certstor.h @@ -21,8 +21,6 @@ class BOTAN_DLL Certificate_Store public: virtual ~Certificate_Store() {} - virtual Certificate_Store* clone() const = 0; - /** * Add a certificate; this may fail if the store is write-only */ @@ -45,7 +43,7 @@ class BOTAN_DLL Certificate_Store * Find CRLs by the DN and key id of the issuer */ virtual std::vector - find_crl_by_subject_and_key_id( + find_crl_by_issuer_and_key_id( const X509_DN& issuer_dn, const MemoryRegion& key_id) const = 0; }; @@ -53,11 +51,9 @@ class BOTAN_DLL Certificate_Store /** * In Memory Certificate Store */ -class BOTAN_DLL Certificate_Store_Memory : public Certificate_Store +class BOTAN_DLL Certificate_Store_In_Memory : public Certificate_Store { public: - Certificate_Store* clone() const; - void add_certificate(const X509_Certificate& cert); void add_crl(const X509_CRL& crl); @@ -66,13 +62,13 @@ class BOTAN_DLL Certificate_Store_Memory : public Certificate_Store const X509_DN& subject_dn, const MemoryRegion& key_id) const; - std::vector find_crl_by_subject_and_key_id( + std::vector find_crl_by_issuer_and_key_id( const X509_DN& issuer_dn, const MemoryRegion& key_id) const; - Certificate_Store_Memory() {} + Certificate_Store_In_Memory() {} private: - // TODO: Add indexing on the DN and key id to avoid linear search? + // TODO: Add indexing on the DN and key id to avoid linear search std::vector certs; std::vector crls; }; diff --git a/src/cert/x509cert/x509_ext.h b/src/cert/x509cert/x509_ext.h index 8799c5921..714e29562 100644 --- a/src/cert/x509cert/x509_ext.h +++ b/src/cert/x509cert/x509_ext.h @@ -12,7 +12,7 @@ #include #include #include -#include +#include namespace Botan { diff --git a/src/cert/x509cert/x509_obj.cpp b/src/cert/x509cert/x509_obj.cpp index 13193f09c..c58081225 100644 --- a/src/cert/x509cert/x509_obj.cpp +++ b/src/cert/x509cert/x509_obj.cpp @@ -16,6 +16,8 @@ #include #include +#include + namespace Botan { /* @@ -192,8 +194,9 @@ bool X509_Object::check_signature(Public_Key& pub_key) const return verifier.verify_message(tbs_data(), signature()); } - catch(...) + catch(std::exception& e) { + printf("Failure during validation %s\n", e.what()); return false; } } diff --git a/src/cert/x509cert/x509cert.cpp b/src/cert/x509cert/x509cert.cpp index 7d9370f2a..52115a1a8 100644 --- a/src/cert/x509cert/x509cert.cpp +++ b/src/cert/x509cert/x509cert.cpp @@ -206,9 +206,15 @@ bool X509_Certificate::is_CA_cert() const { if(!subject.get1_u32bit("X509v3.BasicConstraints.is_ca")) return false; - if((constraints() & KEY_CERT_SIGN) || (constraints() == NO_CONSTRAINTS)) + + return allowed_usage(KEY_CERT_SIGN); + } + +bool X509_Certificate::allowed_usage(Key_Constraints restriction) const + { + if(constraints() == NO_CONSTRAINTS) return true; - return false; + return (constraints() & restriction); } /* diff --git a/src/cert/x509cert/x509cert.h b/src/cert/x509cert/x509cert.h index 8798ef1c2..d25b97694 100644 --- a/src/cert/x509cert/x509cert.h +++ b/src/cert/x509cert/x509cert.h @@ -23,10 +23,10 @@ namespace Botan { class BOTAN_DLL X509_Certificate : public X509_Object { public: - /** - * Get the public key associated with this certificate. - * @return subject public key of this certificate - */ + /** + * Get the public key associated with this certificate. + * @return subject public key of this certificate + */ Public_Key* subject_public_key() const; /** @@ -111,6 +111,8 @@ class BOTAN_DLL X509_Certificate : public X509_Object */ bool is_CA_cert() const; + bool allowed_usage(Key_Constraints restriction) const; + /** * Get the path limit as defined in the BasicConstraints extension of * this certificate. diff --git a/src/cert/x509crl/crl_ent.h b/src/cert/x509crl/crl_ent.h index b3e696a86..ae9535484 100644 --- a/src/cert/x509crl/crl_ent.h +++ b/src/cert/x509crl/crl_ent.h @@ -12,6 +12,26 @@ namespace Botan { +/** +* X.509v2 CRL Reason Code. +*/ +enum CRL_Code { + UNSPECIFIED = 0, + KEY_COMPROMISE = 1, + CA_COMPROMISE = 2, + AFFILIATION_CHANGED = 3, + SUPERSEDED = 4, + CESSATION_OF_OPERATION = 5, + CERTIFICATE_HOLD = 6, + REMOVE_FROM_CRL = 8, + PRIVLEDGE_WITHDRAWN = 9, + AA_COMPROMISE = 10, + + DELETE_CRL_ENTRY = 0xFF00, + OCSP_GOOD = 0xFF01, + OCSP_UNKNOWN = 0xFF02 +}; + /** * This class represents CRL entries */ diff --git a/src/cert/x509crl/x509_crl.cpp b/src/cert/x509crl/x509_crl.cpp index 01fce4c52..9c6b891c7 100644 --- a/src/cert/x509crl/x509_crl.cpp +++ b/src/cert/x509crl/x509_crl.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -32,6 +33,43 @@ X509_CRL::X509_CRL(const std::string& in, bool touc) : do_decode(); } +/** +* Check if this particular certificate is listed in the CRL +*/ +bool X509_CRL::is_revoked(const X509_Certificate& cert) const + { + /* + If the cert wasn't issued by the CRL issuer, it's possible the cert + is revoked, but not by this CRL. Maybe throw an exception instead? + */ + if(cert.issuer_dn() != issuer_dn()) + return false; + + MemoryVector crl_akid = authority_key_id(); + MemoryVector cert_akid = cert.authority_key_id(); + + if(!crl_akid.empty() && !cert_akid.empty()) + if(crl_akid != cert_akid) + return false; + + MemoryVector cert_serial = cert.serial_number(); + + bool is_revoked = false; + + for(size_t i = 0; i != revoked.size(); ++i) + { + if(cert_serial == revoked[i].serial_number()) + { + if(revoked[i].reason_code() == REMOVE_FROM_CRL) + is_revoked = false; + else + is_revoked = true; + } + } + + return is_revoked; + } + /* * Decode the TBSCertList data */ diff --git a/src/cert/x509crl/x509_crl.h b/src/cert/x509crl/x509_crl.h index c2b3c4f5c..55eb8424b 100644 --- a/src/cert/x509crl/x509_crl.h +++ b/src/cert/x509crl/x509_crl.h @@ -14,6 +14,8 @@ namespace Botan { +class X509_Certificate; + /** * This class represents X.509 Certificate Revocation Lists (CRLs). */ @@ -29,6 +31,11 @@ class BOTAN_DLL X509_CRL : public X509_Object Exception("X509_CRL: " + error) {} }; + /** + * Check if this particular certificate is listed in the CRL + */ + bool is_revoked(const X509_Certificate& cert) const; + /** * Get the entries of this CRL in the form of a vector. * @return vector containing the entries of this CRL. 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& 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& certstores) { const X509_DN issuer_dn = cert.issuer_dn(); const MemoryVector auth_key_id = cert.authority_key_id(); - Path_Validation_Result result; + for(size_t i = 0; i != certstores.size(); ++i) + { + std::vector certs = + certstores[i]->find_cert_by_subject_and_key_id(issuer_dn, auth_key_id); - std::vector 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 find_crls_from(const X509_Certificate& cert, + const std::vector& certstores) + { + const X509_DN issuer_dn = cert.subject_dn(); + const MemoryVector auth_key_id = cert.subject_key_id(); for(size_t i = 0; i != certstores.size(); ++i) { - std::vector got = - certstores[i]->find_cert_by_subject_and_key_id(issuer_dn, auth_key_id); + std::vector crl = + certstores[i]->find_crl_by_issuer_and_key_id(issuer_dn, auth_key_id); + + if(!crl.empty()) + return crl; + } + + return std::vector(); + } + +} + +std::set Path_Validation_Result::trusted_hashes() const + { + std::set 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& certstores) + { + std::vector certs; + certs.push_back(end_cert); + return x509_path_validate(certs, certstores); + } + +Path_Validation_Result x509_path_validate( + const std::vector& end_certs, + Certificate_Store& store) + { + std::vector 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 certs; + certs.push_back(end_cert); + + std::vector certstores; + certstores.push_back(&store); + + return x509_path_validate(certs, certstores); + } + +Path_Validation_Result +x509_path_validate(const std::vector& end_certs, + const std::vector& 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 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 cert_path; + /** + * Returns the set of hash functions you are implicitly + * trusting by trusting this result. + */ std::set trusted_hashes() const; }; +Path_Validation_Result BOTAN_DLL x509_path_validate( + const std::vector& end_certs, + const std::vector& certstores); + Path_Validation_Result BOTAN_DLL x509_path_validate( const X509_Certificate& end_cert, const std::vector& 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 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& end_certs, + Certificate_Store& store); } diff --git a/src/pubkey/pubkey_enums.h b/src/pubkey/pubkey_enums.h index 99f804e0a..c64a8493d 100644 --- a/src/pubkey/pubkey_enums.h +++ b/src/pubkey/pubkey_enums.h @@ -37,26 +37,6 @@ void BOTAN_DLL decode(BER_Decoder&, Key_Constraints&); } -/** -* X.509v2 CRL Reason Code. -*/ -enum CRL_Code { - UNSPECIFIED = 0, - KEY_COMPROMISE = 1, - CA_COMPROMISE = 2, - AFFILIATION_CHANGED = 3, - SUPERSEDED = 4, - CESSATION_OF_OPERATION = 5, - CERTIFICATE_HOLD = 6, - REMOVE_FROM_CRL = 8, - PRIVLEDGE_WITHDRAWN = 9, - AA_COMPROMISE = 10, - - DELETE_CRL_ENTRY = 0xFF00, - OCSP_GOOD = 0xFF01, - OCSP_UNKNOWN = 0xFF02 -}; - /* * Various Other Enumerations */ -- cgit v1.2.3