aboutsummaryrefslogtreecommitdiffstats
path: root/src/cert/x509path
diff options
context:
space:
mode:
authorlloyd <[email protected]>2012-02-06 14:12:35 +0000
committerlloyd <[email protected]>2012-02-06 14:12:35 +0000
commitcd58927000ef86eacc9de5b80f361d4d05e71731 (patch)
tree975d2e50e77567d14ea3d24e6ebaf24a9e4d7c3b /src/cert/x509path
parent03bc906a6a94d236f192fa3b1bb370c013fc753a (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.cpp186
-rw-r--r--src/cert/x509path/x509path.h48
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);
}