aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/x509/ocsp.cpp
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2016-11-21 20:07:26 -0500
committerJack Lloyd <[email protected]>2016-11-23 08:31:07 -0500
commitcdd2e2babc0506d0c727aff06d1fc430cedbf695 (patch)
tree136e8ade4c31e4086245db2d87552bde1c584fe1 /src/lib/x509/ocsp.cpp
parent7dbb31c5778ac1158fbf0979739f6c3c55a007f5 (diff)
Refactor X.509 path validation
Splits path building, path validation, CRL checks, and OCSP checks into distinct functions in namespace PKIX. The previous path validation APIs remain. Fixes to OCSP to store more information and to handle modern OCSP setups in at least some situations.
Diffstat (limited to 'src/lib/x509/ocsp.cpp')
-rw-r--r--src/lib/x509/ocsp.cpp208
1 files changed, 122 insertions, 86 deletions
diff --git a/src/lib/x509/ocsp.cpp b/src/lib/x509/ocsp.cpp
index 761c5b436..af7126580 100644
--- a/src/lib/x509/ocsp.cpp
+++ b/src/lib/x509/ocsp.cpp
@@ -14,7 +14,10 @@
#include <botan/base64.h>
#include <botan/pubkey.h>
#include <botan/x509path.h>
-#include <botan/http_util.h>
+
+#if defined(BOTAN_HAS_HTTP_UTIL)
+ #include <botan/http_util.h>
+#endif
namespace Botan {
@@ -22,6 +25,7 @@ namespace OCSP {
namespace {
+// TODO: should this be in a header somewhere?
void decode_optional_list(BER_Decoder& ber,
ASN1_Tag tag,
std::vector<X509_Certificate>& output)
@@ -44,65 +48,18 @@ void decode_optional_list(BER_Decoder& ber,
}
}
-void check_signature(const std::vector<byte>& tbs_response,
- const AlgorithmIdentifier& sig_algo,
- const std::vector<byte>& signature,
- const X509_Certificate& cert)
- {
- std::unique_ptr<Public_Key> pub_key(cert.subject_public_key());
-
- const std::vector<std::string> sig_info =
- split_on(OIDS::lookup(sig_algo.oid), '/');
-
- if(sig_info.size() != 2 || sig_info[0] != pub_key->algo_name())
- throw Exception("Information in OCSP response does not match cert");
-
- std::string padding = sig_info[1];
- Signature_Format format =
- (pub_key->message_parts() >= 2) ? DER_SEQUENCE : IEEE_1363;
-
- PK_Verifier verifier(*pub_key, padding, format);
-
- if(!verifier.verify_message(ASN1::put_in_sequence(tbs_response), signature))
- throw Exception("Signature on OCSP response does not verify");
- }
+}
-void check_signature(const std::vector<byte>& tbs_response,
- const AlgorithmIdentifier& sig_algo,
- const std::vector<byte>& signature,
- const Certificate_Store& trusted_roots,
- const std::vector<X509_Certificate>& certs)
+Request::Request(const X509_Certificate& issuer_cert,
+ const X509_Certificate& subject_cert) :
+ m_issuer(issuer_cert),
+ m_subject(subject_cert),
+ m_certid(m_issuer, m_subject)
{
- if(certs.size() < 1)
- throw Invalid_Argument("Short cert chain for check_signature");
-
- if(trusted_roots.certificate_known(certs[0]))
- return check_signature(tbs_response, sig_algo, signature, certs[0]);
-
- // Otherwise attempt to chain the signing cert to a trust root
-
- if(!certs[0].allowed_extended_usage("PKIX.OCSPSigning"))
- throw Exception("OCSP response cert does not allow OCSP signing");
-
- auto result = x509_path_validate(certs, Path_Validation_Restrictions(), trusted_roots);
-
- if(!result.successful_validation())
- throw Exception("Certificate validation failure: " + result.result_string());
-
- if(!trusted_roots.certificate_known(result.trust_root())) // not needed anymore?
- throw Exception("Certificate chain roots in unknown/untrusted CA");
-
- const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path = result.cert_path();
-
- check_signature(tbs_response, sig_algo, signature, *cert_path[0]);
}
-}
-
std::vector<byte> Request::BER_encode() const
{
- CertID certid(m_issuer, m_subject);
-
return DER_Encoder().start_cons(SEQUENCE)
.start_cons(SEQUENCE)
.start_explicit(0)
@@ -110,7 +67,7 @@ std::vector<byte> Request::BER_encode() const
.end_explicit()
.start_cons(SEQUENCE)
.start_cons(SEQUENCE)
- .encode(certid)
+ .encode(m_certid)
.end_cons()
.end_cons()
.end_cons()
@@ -122,8 +79,7 @@ std::string Request::base64_encode() const
return Botan::base64_encode(BER_encode());
}
-Response::Response(const Certificate_Store& trusted_roots,
- const std::vector<byte>& response_bits)
+Response::Response(const std::vector<byte>& response_bits)
{
BER_Decoder response_outer = BER_Decoder(response_bits).start_cons(SEQUENCE);
@@ -145,71 +101,141 @@ Response::Response(const Certificate_Store& trusted_roots,
BER_Decoder basicresponse =
BER_Decoder(response_bytes.get_next_octet_string()).start_cons(SEQUENCE);
- std::vector<byte> tbs_bits;
- AlgorithmIdentifier sig_algo;
- std::vector<byte> signature;
- std::vector<X509_Certificate> certs;
-
basicresponse.start_cons(SEQUENCE)
- .raw_bytes(tbs_bits)
+ .raw_bytes(m_tbs_bits)
.end_cons()
- .decode(sig_algo)
- .decode(signature, BIT_STRING);
- decode_optional_list(basicresponse, ASN1_Tag(0), certs);
+ .decode(m_sig_algo)
+ .decode(m_signature, BIT_STRING);
+ decode_optional_list(basicresponse, ASN1_Tag(0), m_certs);
size_t responsedata_version = 0;
- X509_DN name;
- std::vector<byte> key_hash;
- X509_Time produced_at;
Extensions extensions;
- BER_Decoder(tbs_bits)
+ BER_Decoder(m_tbs_bits)
.decode_optional(responsedata_version, ASN1_Tag(0),
ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
- .decode_optional(name, ASN1_Tag(1),
+ .decode_optional(m_signer_name, ASN1_Tag(1),
ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
- .decode_optional_string(key_hash, OCTET_STRING, 2,
+ .decode_optional_string(m_key_hash, OCTET_STRING, 2,
ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
- .decode(produced_at)
+ .decode(m_produced_at)
.decode_list(m_responses)
.decode_optional(extensions, ASN1_Tag(1),
ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
+ }
+
+ response_outer.end_cons();
+ }
+
+Certificate_Status_Code Response::verify_signature(const X509_Certificate& issuer) const
+ {
+ try
+ {
+ std::unique_ptr<Public_Key> pub_key(issuer.subject_public_key());
+
+ const std::vector<std::string> sig_info =
+ split_on(OIDS::lookup(m_sig_algo.oid), '/');
- if(certs.empty())
+ if(sig_info.size() != 2 || sig_info[0] != pub_key->algo_name())
+ return Certificate_Status_Code::OCSP_RESPONSE_INVALID;
+
+ std::string padding = sig_info[1];
+ Signature_Format format = (pub_key->message_parts() >= 2) ? DER_SEQUENCE : IEEE_1363;
+
+ PK_Verifier verifier(*pub_key, padding, format);
+
+ if(verifier.verify_message(ASN1::put_in_sequence(m_tbs_bits), m_signature))
+ return Certificate_Status_Code::OCSP_SIGNATURE_OK;
+ else
+ return Certificate_Status_Code::OCSP_SIGNATURE_ERROR;
+ }
+ catch(Exception&)
+ {
+ return Certificate_Status_Code::OCSP_SIGNATURE_ERROR;
+ }
+ }
+
+Certificate_Status_Code Response::check_signature(const std::vector<Certificate_Store*>& trusted_roots,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& ee_cert_path) const
+ {
+ std::shared_ptr<const X509_Certificate> signing_cert;
+
+ for(size_t i = 0; i != trusted_roots.size(); ++i)
+ {
+ if(m_signer_name.empty() && m_key_hash.empty())
+ return Certificate_Status_Code::OCSP_RESPONSE_INVALID;
+
+ if(!m_signer_name.empty())
{
- if(auto cert = trusted_roots.find_cert(name, std::vector<byte>()))
- certs.push_back(*cert);
- else
- throw Exception("Could not find certificate that signed OCSP response");
+ signing_cert = trusted_roots[i]->find_cert(m_signer_name, std::vector<byte>());
+ if(signing_cert)
+ {
+ break;
+ }
+ }
+
+ if(m_key_hash.size() > 0)
+ {
+ signing_cert = trusted_roots[i]->find_cert_by_pubkey_sha1(m_key_hash);
+ if(signing_cert)
+ {
+ break;
+ }
}
+ }
- check_signature(tbs_bits, sig_algo, signature, trusted_roots, certs);
+ if(!signing_cert)
+ {
+ for(size_t i = 1; i < ee_cert_path.size(); ++i)
+ {
+ // Check all CA certificates in the (assumed validated) EE cert path
+ if(!m_signer_name.empty() && ee_cert_path[i]->subject_dn() == m_signer_name)
+ {
+ signing_cert = ee_cert_path[i];
+ break;
+ }
+
+ if(m_key_hash.size() > 0 && ee_cert_path[i]->subject_public_key_bitstring_sha1() == m_key_hash)
+ {
+ signing_cert = ee_cert_path[i];
+ break;
+ }
+ }
}
- response_outer.end_cons();
+ // TODO: this ignores m_certs
+
+ if(!signing_cert)
+ return Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND;
+
+ if(!signing_cert->allowed_extended_usage("PKIX.OCSPSigning"))
+ return Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE;
+
+ return this->verify_signature(*signing_cert);
}
Certificate_Status_Code Response::status_for(const X509_Certificate& issuer,
- const X509_Certificate& subject) const
+ const X509_Certificate& subject,
+ std::chrono::system_clock::time_point ref_time) const
{
for(const auto& response : m_responses)
{
if(response.certid().is_id_for(issuer, subject))
{
- X509_Time current_time(std::chrono::system_clock::now());
+ X509_Time x509_ref_time(ref_time);
if(response.cert_status() == 1)
return Certificate_Status_Code::CERT_IS_REVOKED;
- if(response.this_update() > current_time)
+ if(response.this_update() > x509_ref_time)
return Certificate_Status_Code::OCSP_NOT_YET_VALID;
- if(response.next_update().time_is_set() && current_time > response.next_update())
+ if(response.next_update().time_is_set() && x509_ref_time > response.next_update())
return Certificate_Status_Code::OCSP_HAS_EXPIRED;
if(response.cert_status() == 0)
@@ -222,9 +248,11 @@ Certificate_Status_Code Response::status_for(const X509_Certificate& issuer,
return Certificate_Status_Code::OCSP_CERT_NOT_LISTED;
}
+#if defined(BOTAN_HAS_HTTP_UTIL)
+
Response online_check(const X509_Certificate& issuer,
const X509_Certificate& subject,
- const Certificate_Store* trusted_roots)
+ Certificate_Store* trusted_roots)
{
const std::string responder_url = subject.ocsp_responder();
@@ -241,11 +269,19 @@ Response online_check(const X509_Certificate& issuer,
// Check the MIME type?
- OCSP::Response response(*trusted_roots, http.body());
+ OCSP::Response response(http.body());
+
+ std::vector<Certificate_Store*> trusted_roots_vec;
+ trusted_roots_vec.push_back(trusted_roots);
+
+ if(trusted_roots)
+ response.check_signature(trusted_roots_vec);
return response;
}
+#endif
+
}
}