aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlloyd <[email protected]>2013-11-29 22:50:03 +0000
committerlloyd <[email protected]>2013-11-29 22:50:03 +0000
commit9ee17c62aba097b3b682d1ec217c31fdf8f2dedb (patch)
tree85cb25107dec4272b98e3c86651201afb4e02f40
parent41cc764815c5cdf1902e923b7bea3f3eec288c31 (diff)
First pass at automatic OCSP checks
-rw-r--r--checks/x509.cpp4
-rw-r--r--src/asn1/asn1_time.h2
-rw-r--r--src/cert/x509/cert_status.h3
-rw-r--r--src/cert/x509/ocsp.cpp7
-rw-r--r--src/cert/x509/ocsp.h2
-rw-r--r--src/cert/x509/ocsp_types.h2
-rw-r--r--src/cert/x509/x509path.cpp138
-rw-r--r--src/cert/x509/x509path.h14
8 files changed, 112 insertions, 60 deletions
diff --git a/checks/x509.cpp b/checks/x509.cpp
index 46b748da6..77d4e803f 100644
--- a/checks/x509.cpp
+++ b/checks/x509.cpp
@@ -219,12 +219,12 @@ void do_x509_tests(RandomNumberGenerator& rng)
store.add_crl(crl2);
result_u1 = x509_path_validate(user1_cert, restrictions, store);
- if(result_u1.result() != Path_Validation_Result::CERT_IS_REVOKED)
+ if(result_u1.result() != Certificate_Status_Code::CERT_IS_REVOKED)
std::cout << "FAILED: User cert #1 was not revoked - "
<< result_u1.result_string() << std::endl;
result_u2 = x509_path_validate(user2_cert, restrictions, store);
- if(result_u2.result() != Path_Validation_Result::CERT_IS_REVOKED)
+ if(result_u2.result() != Certificate_Status_Code::CERT_IS_REVOKED)
std::cout << "FAILED: User cert #2 was not revoked - "
<< result_u2.result_string() << std::endl;
diff --git a/src/asn1/asn1_time.h b/src/asn1/asn1_time.h
index 3e7e0ef1c..95baacd86 100644
--- a/src/asn1/asn1_time.h
+++ b/src/asn1/asn1_time.h
@@ -26,6 +26,8 @@ class BOTAN_DLL X509_Time : public ASN1_Object
std::string readable_string() const;
bool time_is_set() const;
+ std::string to_string() const { return readable_string(); }
+
s32bit cmp(const X509_Time&) const;
void set_to(const std::string&);
diff --git a/src/cert/x509/cert_status.h b/src/cert/x509/cert_status.h
index d78f14473..b688cbcd7 100644
--- a/src/cert/x509/cert_status.h
+++ b/src/cert/x509/cert_status.h
@@ -32,7 +32,8 @@ enum Certificate_Status_Code {
CERT_HAS_EXPIRED,
CERT_IS_REVOKED,
- CRL_NOT_FOUND,
+ NO_REVOCATION_DATA,
+
CRL_FORMAT_ERROR,
CRL_NOT_YET_VALID,
CRL_HAS_EXPIRED,
diff --git a/src/cert/x509/ocsp.cpp b/src/cert/x509/ocsp.cpp
index 2ea413e76..a118a9c4e 100644
--- a/src/cert/x509/ocsp.cpp
+++ b/src/cert/x509/ocsp.cpp
@@ -224,7 +224,7 @@ Certificate_Status_Code Response::status_for(const X509_Certificate& issuer,
Response online_check(const X509_Certificate& issuer,
const X509_Certificate& subject,
- const Certificate_Store& trusted_roots)
+ const Certificate_Store* trusted_roots)
{
const std::string responder_url = subject.ocsp_responder();
@@ -237,12 +237,11 @@ Response online_check(const X509_Certificate& issuer,
"application/ocsp-request",
req.BER_encode());
- if(http.status_code() != 200)
- throw std::runtime_error("HTTP error: " + http.status_message());
+ http.throw_unless_ok();
// Check the MIME type?
- OCSP::Response response(trusted_roots, http.body());
+ OCSP::Response response(*trusted_roots, http.body());
return response;
}
diff --git a/src/cert/x509/ocsp.h b/src/cert/x509/ocsp.h
index 0c40bc282..b2a06b9a4 100644
--- a/src/cert/x509/ocsp.h
+++ b/src/cert/x509/ocsp.h
@@ -52,7 +52,7 @@ class BOTAN_DLL Response
BOTAN_DLL Response online_check(const X509_Certificate& issuer,
const X509_Certificate& subject,
- const Certificate_Store& trusted_roots);
+ const Certificate_Store* trusted_roots);
}
diff --git a/src/cert/x509/ocsp_types.h b/src/cert/x509/ocsp_types.h
index f693600ea..a2983f10b 100644
--- a/src/cert/x509/ocsp_types.h
+++ b/src/cert/x509/ocsp_types.h
@@ -48,7 +48,7 @@ class BOTAN_DLL SingleResponse : public ASN1_Object
X509_Time this_update() const { return m_thisupdate; }
- X509_Time next_update() const { return m_thisupdate; }
+ X509_Time next_update() const { return m_nextupdate; }
void encode_into(class DER_Encoder& to) const override;
diff --git a/src/cert/x509/x509path.cpp b/src/cert/x509/x509path.cpp
index 3ed57206d..6aa7251e7 100644
--- a/src/cert/x509/x509path.cpp
+++ b/src/cert/x509/x509path.cpp
@@ -6,31 +6,22 @@
*/
#include <botan/x509path.h>
+#include <botan/ocsp.h>
+#include <botan/http_util.h>
#include <botan/parsing.h>
#include <botan/pubkey.h>
#include <botan/oids.h>
#include <algorithm>
#include <chrono>
#include <memory>
+#include <iostream>
namespace Botan {
namespace {
-class PKIX_Validation_Failure : public std::exception
- {
- public:
- PKIX_Validation_Failure(Certificate_Status_Code code) : m_code(code) {}
-
- Certificate_Status_Code code() const { return m_code; }
-
- const char* what() const noexcept { return "PKIX validation failed"; }
- private:
- Certificate_Status_Code m_code;
- };
-
-X509_Certificate find_issuing_cert(const X509_Certificate& cert,
- const std::vector<Certificate_Store*>& certstores)
+const X509_Certificate* find_issuing_cert(const X509_Certificate& cert,
+ const std::vector<Certificate_Store*>& certstores)
{
const X509_DN issuer_dn = cert.issuer_dn();
const std::vector<byte> auth_key_id = cert.authority_key_id();
@@ -38,14 +29,14 @@ X509_Certificate find_issuing_cert(const X509_Certificate& cert,
for(size_t i = 0; i != certstores.size(); ++i)
{
if(const X509_Certificate* cert = certstores[i]->find_cert(issuer_dn, auth_key_id))
- return *cert;
+ return cert;
}
- throw PKIX_Validation_Failure(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND);
+ return nullptr;
}
const X509_CRL* find_crls_from(const X509_Certificate& cert,
- const std::vector<Certificate_Store*>& certstores)
+ const std::vector<Certificate_Store*>& certstores)
{
const X509_DN issuer_dn = cert.subject_dn();
const std::vector<byte> auth_key_id = cert.subject_key_id();
@@ -56,6 +47,24 @@ const X509_CRL* find_crls_from(const X509_Certificate& cert,
return crl;
}
+#if 0
+ const std::string crl_url = cert.crl_distribution_point();
+ if(crl_url != "")
+ {
+ std::cout << "Downloading CRL " << crl_url << "\n";
+ auto http = HTTP::GET_sync(crl_url);
+
+ std::cout << http.status_message() << "\n";
+
+ http.throw_unless_ok();
+ // check the mime type
+
+ std::unique_ptr<X509_CRL> crl(new X509_CRL(http.body()));
+
+ return crl.release();
+ }
+#endif
+
return nullptr;
}
@@ -69,10 +78,23 @@ Certificate_Status_Code check_chain(const std::vector<X509_Certificate>& cert_pa
X509_Time current_time(std::chrono::system_clock::now());
+ std::vector<std::future<OCSP::Response>> ocsp_responses;
+
for(size_t i = 0; i != cert_path.size(); ++i)
{
+ const bool at_self_signed_root = (i == cert_path.size() - 1);
+
const X509_Certificate& subject = cert_path[i];
+ const X509_Certificate& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
+
+ const Certificate_Store* trusted = certstores[0]; // fixme
+
+ if(i == 0 || restrictions.ocsp_all_intermediates())
+ ocsp_responses.push_back(
+ std::async(std::launch::async,
+ OCSP::online_check, issuer, subject, trusted));
+
// Check all certs for valid time range
if(current_time < X509_Time(subject.start_time()))
return Certificate_Status_Code::CERT_NOT_YET_VALID;
@@ -80,11 +102,6 @@ Certificate_Status_Code check_chain(const std::vector<X509_Certificate>& cert_pa
if(current_time > X509_Time(subject.end_time()))
return Certificate_Status_Code::CERT_HAS_EXPIRED;
- const bool at_self_signed_root = (i == cert_path.size() - 1);
-
- const X509_Certificate& issuer =
- cert_path[at_self_signed_root ? (i) : (i + 1)];
-
// Check issuer constraints
// Don't require CA bit set on self-signed end entity cert
@@ -107,17 +124,42 @@ Certificate_Status_Code check_chain(const std::vector<X509_Certificate>& cert_pa
return Certificate_Status_Code::UNTRUSTED_HASH;
}
- for(size_t i = 1; i != cert_path.size(); ++i)
+ for(size_t i = 0; i != cert_path.size() - 1; ++i)
{
- const X509_Certificate& subject = cert_path[i-1];
- const X509_Certificate& ca = cert_path[i];
+ const X509_Certificate& subject = cert_path[i];
+ const X509_Certificate& ca = cert_path[i+1];
+
+ if(i < ocsp_responses.size())
+ {
+ try
+ {
+ OCSP::Response ocsp = ocsp_responses[i].get();
+
+ auto status = ocsp.status_for(ca, subject);
+
+ if(status == CERT_IS_REVOKED)
+ return status;
+
+ if(status == OCSP_RESPONSE_GOOD)
+ {
+ if(i == 0 && !restrictions.ocsp_all_intermediates())
+ return status; // return immediately to just OCSP end cert
+ else
+ continue;
+ }
+ }
+ catch(std::exception& e)
+ {
+ }
+ }
const X509_CRL* crl_p = find_crls_from(ca, certstores);
if(!crl_p)
{
if(restrictions.require_revocation_information())
- return Certificate_Status_Code::CRL_NOT_FOUND;
+ return Certificate_Status_Code::NO_REVOCATION_DATA;
+ std::cout << "No revocation information for " << subject.subject_dn() << "\n";
continue;
}
@@ -147,7 +189,6 @@ Certificate_Status_Code check_chain(const std::vector<X509_Certificate>& cert_pa
}
-
Path_Validation_Result x509_path_validate(
const std::vector<X509_Certificate>& end_certs,
const Path_Validation_Restrictions& restrictions,
@@ -158,26 +199,18 @@ Path_Validation_Result x509_path_validate(
std::vector<X509_Certificate> cert_path = end_certs;
- try
- {
- // iterate until we reach a root or cannot find the issuer
- while(!cert_path.back().is_self_signed())
- {
- cert_path.push_back(
- find_issuing_cert(cert_path.back(), certstores)
- );
- }
-
- Certificate_Status_Code res = check_chain(cert_path, restrictions, certstores);
-
- return Path_Validation_Result(res, std::move(cert_path));
- }
- catch(PKIX_Validation_Failure& e)
+ // iterate until we reach a root or cannot find the issuer
+ while(!cert_path.back().is_self_signed())
{
- return Path_Validation_Result(e.code());
+ const X509_Certificate* cert = find_issuing_cert(cert_path.back(), certstores);
+ if(!cert)
+ return Path_Validation_Result(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND);
+ cert_path.push_back(*cert);
}
- return Path_Validation_Result(Certificate_Status_Code::UNKNOWN_X509_ERROR);
+ Certificate_Status_Code res = check_chain(cert_path, restrictions, certstores);
+
+ return Path_Validation_Result(res, std::move(cert_path));
}
Path_Validation_Result x509_path_validate(
@@ -216,8 +249,10 @@ Path_Validation_Result x509_path_validate(
}
Path_Validation_Restrictions::Path_Validation_Restrictions(bool require_rev,
- size_t key_strength) :
+ size_t key_strength,
+ bool ocsp_all) :
m_require_revocation_information(require_rev),
+ m_ocsp_all_intermediates(ocsp_all),
m_minimum_key_strength(key_strength)
{
if(key_strength <= 80)
@@ -242,7 +277,14 @@ std::set<std::string> Path_Validation_Result::trusted_hashes() const
return hashes;
}
-std::string Path_Validation_Result::result_string() constmtn
+bool Path_Validation_Result::successful_validation() const
+ {
+ if(status() == VERIFIED || status() == OCSP_RESPONSE_GOOD)
+ return true;
+ return false;
+ }
+
+std::string Path_Validation_Result::result_string() const
{
return status_string(m_status);
}
@@ -283,8 +325,8 @@ std::string Path_Validation_Result::status_string(Certificate_Status_Code code)
return "Certificate has expired";
case CERT_IS_REVOKED:
return "Certificate is revoked";
- case CRL_NOT_FOUND:
- return "CRL not found";
+ case NO_REVOCATION_DATA:
+ return "No revocation data available";
case CRL_FORMAT_ERROR:
return "CRL format error";
case CRL_NOT_YET_VALID:
diff --git a/src/cert/x509/x509path.h b/src/cert/x509/x509path.h
index c935daa77..2c3268529 100644
--- a/src/cert/x509/x509path.h
+++ b/src/cert/x509/x509path.h
@@ -25,10 +25,12 @@ class BOTAN_DLL Path_Validation_Restrictions
* @param require_rev if true, revocation information is required
* @param minimum_key_strength is the minimum strength (in terms of
* operations, eg 80 means 2^80) of a signature. Signatures
- * weaker than this are rejected.
+ * weaker than this are rejected. If more than 80, SHA-1
+ * signatures are also rejected.
*/
Path_Validation_Restrictions(bool require_rev = false,
- size_t minimum_key_strength = 80);
+ size_t minimum_key_strength = 80,
+ bool ocsp_all_intermediates = false);
/**
* @param require_rev if true, revocation information is required
@@ -41,14 +43,19 @@ class BOTAN_DLL Path_Validation_Restrictions
*/
Path_Validation_Restrictions(bool require_rev,
size_t minimum_key_strength,
+ bool ocsp_all_intermediates,
const std::set<std::string>& trusted_hashes) :
m_require_revocation_information(require_rev),
+ m_ocsp_all_intermediates(ocsp_all_intermediates),
m_trusted_hashes(trusted_hashes),
m_minimum_key_strength(minimum_key_strength) {}
bool require_revocation_information() const
{ return m_require_revocation_information; }
+ bool ocsp_all_intermediates() const
+ { return m_ocsp_all_intermediates; }
+
const std::set<std::string>& trusted_hashes() const
{ return m_trusted_hashes; }
@@ -57,6 +64,7 @@ class BOTAN_DLL Path_Validation_Restrictions
private:
bool m_require_revocation_information;
+ bool m_ocsp_all_intermediates;
std::set<std::string> m_trusted_hashes;
size_t m_minimum_key_strength;
};
@@ -86,7 +94,7 @@ class BOTAN_DLL Path_Validation_Result
/**
* @return true iff the validation was succesful
*/
- bool successful_validation() const { return status() == VERIFIED; }
+ bool successful_validation() const;
/**
* @return validation result code