From 93c41cbf8525463c598cb16cd34b1709c6b8120e Mon Sep 17 00:00:00 2001 From: Rene Meusel Date: Thu, 22 Sep 2022 16:47:57 +0200 Subject: [PATCH 1/8] Test::Result::test_is_eq() with more versatile to_string() --- src/tests/tests.h | 57 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/src/tests/tests.h b/src/tests/tests.h index 98a7a07ced3..0ef4c4b6535 100644 --- a/src/tests/tests.h +++ b/src/tests/tests.h @@ -134,6 +134,41 @@ class Test_Options bool m_abort_on_first_fail; }; +namespace detail { + +template +constexpr bool has_Botan_to_string = false; +template +constexpr bool has_Botan_to_string< + T, + std::void_t()))> +> = true; + +template +constexpr bool has_std_to_string = false; +template +constexpr bool has_std_to_string< + T, + std::void_t()))> +> = true; + +template +constexpr bool has_ostream_operator = false; +template +constexpr bool has_ostream_operator< + T, + std::void_t(), std::declval()))> +> = true; + +template +struct is_optional : std::false_type { }; +template +struct is_optional> : std::true_type { }; +template +constexpr bool is_optional_v = is_optional::value; + +} // namespace detail + /** * A code location consisting of the source file path and a line */ @@ -305,7 +340,7 @@ class Test } else { - out << " produced unexpected result '" << produced << "' expected '" << expected << "'"; + out << " produced unexpected result '" << to_string(produced) << "' expected '" << to_string(expected) << "'"; return test_failure(out.str()); } } @@ -542,6 +577,26 @@ class Test void set_code_location(CodeLocation where) { m_where = std::move(where); } const std::optional& code_location() const { return m_where; } + private: + template + std::string to_string(const T& v) + { + if constexpr(detail::is_optional_v) + return (v.has_value()) ? to_string(v.value()) : std::string("std::nullopt"); + else if constexpr(detail::has_Botan_to_string) + return Botan::to_string(v); + else if constexpr(detail::has_ostream_operator) + { + std::ostringstream oss; + oss << v; + return oss.str(); + } + else if constexpr(detail::has_std_to_string) + return std::to_string(v); + else + return ""; + } + private: std::string m_who; std::optional m_where; From ddef291ff248ce0a730c0257e05101ce2162a0c5 Mon Sep 17 00:00:00 2001 From: Rene Meusel Date: Mon, 19 Sep 2022 17:45:29 +0200 Subject: [PATCH 2/8] add support for PKIX.OCSP.NoCheck extension --- src/build-data/oids.txt | 1 + src/lib/asn1/oid_maps.cpp | 4 +++- src/lib/x509/x509_ext.cpp | 5 +++++ src/lib/x509/x509_ext.h | 29 +++++++++++++++++++++++++++++ src/tests/test_ocsp.cpp | 18 ++++++++++++++++++ src/tests/test_x509_path.cpp | 1 + 6 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/build-data/oids.txt b/src/build-data/oids.txt index 1ee95676f3a..d12158f47d8 100644 --- a/src/build-data/oids.txt +++ b/src/build-data/oids.txt @@ -296,6 +296,7 @@ 1.3.6.1.5.5.7.48.1 = PKIX.OCSP 1.3.6.1.5.5.7.48.1.1 = PKIX.OCSP.BasicResponse +1.3.6.1.5.5.7.48.1.5 = PKIX.OCSP.NoCheck 1.3.6.1.5.5.7.48.2 = PKIX.CertificateAuthorityIssuers 1.3.6.1.4.1.311.20.2.2 = Microsoft SmartcardLogon diff --git a/src/lib/asn1/oid_maps.cpp b/src/lib/asn1/oid_maps.cpp index 567b32e1fd4..85769a8b98d 100644 --- a/src/lib/asn1/oid_maps.cpp +++ b/src/lib/asn1/oid_maps.cpp @@ -1,7 +1,7 @@ /* * OID maps * -* This file was automatically generated by src/scripts/oids.py on 2022-01-10 +* This file was automatically generated by ./src/scripts/oids.py on 2022-09-19 * * All manual edits to this file will be lost. Edit the script * then regenerate this source file. @@ -187,6 +187,7 @@ std::unordered_map OIDS::load_oid2str_map() { "1.3.6.1.5.5.7.3.9", "PKIX.OCSPSigning" }, { "1.3.6.1.5.5.7.48.1", "PKIX.OCSP" }, { "1.3.6.1.5.5.7.48.1.1", "PKIX.OCSP.BasicResponse" }, + { "1.3.6.1.5.5.7.48.1.5", "PKIX.OCSP.NoCheck" }, { "1.3.6.1.5.5.7.48.2", "PKIX.CertificateAuthorityIssuers" }, { "1.3.6.1.5.5.7.8.5", "PKIX.XMPPAddr" }, { "2.16.840.1.101.3.4.1.2", "AES-128/CBC" }, @@ -391,6 +392,7 @@ std::unordered_map OIDS::load_str2oid_map() { "PKIX.IPsecUser", OID({1,3,6,1,5,5,7,3,7}) }, { "PKIX.OCSP", OID({1,3,6,1,5,5,7,48,1}) }, { "PKIX.OCSP.BasicResponse", OID({1,3,6,1,5,5,7,48,1,1}) }, + { "PKIX.OCSP.NoCheck", OID({1,3,6,1,5,5,7,48,1,5}) }, { "PKIX.OCSPSigning", OID({1,3,6,1,5,5,7,3,9}) }, { "PKIX.ServerAuth", OID({1,3,6,1,5,5,7,3,1}) }, { "PKIX.TimeStamping", OID({1,3,6,1,5,5,7,3,8}) }, diff --git a/src/lib/x509/x509_ext.cpp b/src/lib/x509/x509_ext.cpp index a3cdb0611e3..1b82107fd20 100644 --- a/src/lib/x509/x509_ext.cpp +++ b/src/lib/x509/x509_ext.cpp @@ -848,6 +848,11 @@ void CRL_Issuing_Distribution_Point::decode_inner(const std::vector& bu BER_Decoder(buf).decode(m_distribution_point).verify_end(); } +void OCSP_NoCheck::decode_inner(const std::vector& buf) + { + BER_Decoder(buf).verify_end(); + } + std::vector Unknown_Extension::encode_inner() const { return m_bytes; diff --git a/src/lib/x509/x509_ext.h b/src/lib/x509/x509_ext.h index b81548d199f..ffaa2a63ab6 100644 --- a/src/lib/x509/x509_ext.h +++ b/src/lib/x509/x509_ext.h @@ -452,6 +452,35 @@ class CRL_Issuing_Distribution_Point final : public Certificate_Extension CRL_Distribution_Points::Distribution_Point m_distribution_point; }; +/** +* OCSP NoCheck Extension +* +* RFC6960 4.2.2.2.1 +* A CA may specify that an OCSP client can trust a responder for the +* lifetime of the responder's certificate. The CA does so by +* including the extension id-pkix-ocsp-nocheck. +* +* In other words: OCSP responder certificates with this extension do not need +* to be validated against some revocation info. +*/ +class OCSP_NoCheck final : public Certificate_Extension + { + public: + OCSP_NoCheck() = default; + + std::unique_ptr copy() const override { return std::make_unique(); } + static OID static_oid() { return OID("1.3.6.1.5.5.7.48.1.5"); } + OID oid_of() const override { return static_oid(); } + + private: + std::string oid_name() const override + { return "PKIX.OCSP.NoCheck"; } + + bool should_encode() const override { return true; } + std::vector encode_inner() const override { return {}; } + void decode_inner(const std::vector&) override; + }; + /** * An unknown X.509 extension * Will add a failure to the path validation result, if critical diff --git a/src/tests/test_ocsp.cpp b/src/tests/test_ocsp.cpp index 825e795e294..7d15b60c62e 100644 --- a/src/tests/test_ocsp.cpp +++ b/src/tests/test_ocsp.cpp @@ -1,5 +1,6 @@ /* * (C) 2016 Jack Lloyd +* (C) 2022 René Meusel, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -342,6 +343,22 @@ class OCSP_Tests final : public Test } #endif + static Test::Result test_responder_cert_with_nocheck_extension() + { + Test::Result result("BDr's OCSP response contains certificate featuring NoCheck extension"); + + auto ocsp = load_test_OCSP_resp("x509/ocsp/bdr-ocsp-resp.der"); + const bool contains_cert_with_nocheck = + std::find_if(ocsp.certificates().cbegin(), ocsp.certificates().cend(), + [](const auto& cert) { + return cert.v3_extensions().extension_set(Botan::OID::from_string("PKIX.OCSP.NoCheck")); + }) != ocsp.certificates().end(); + + result.confirm("Contains NoCheck extension", contains_cert_with_nocheck); + + return result; + } + public: std::vector run() override { @@ -355,6 +372,7 @@ class OCSP_Tests final : public Test results.push_back(test_response_verification_without_next_update_with_max_age()); results.push_back(test_response_verification_without_next_update_without_max_age()); results.push_back(test_response_verification_softfail()); + results.push_back(test_responder_cert_with_nocheck_extension()); #if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS) if(Test::options().run_online_tests()) diff --git a/src/tests/test_x509_path.cpp b/src/tests/test_x509_path.cpp index 3151ecf77b2..0ff95b3c24c 100644 --- a/src/tests/test_x509_path.cpp +++ b/src/tests/test_x509_path.cpp @@ -1,5 +1,6 @@ /* * (C) 2006,2011,2012,2014,2015 Jack Lloyd +* (C) 2022 René Meusel, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ From ef8a97df712c337e83a8e6f84f98786462b8321a Mon Sep 17 00:00:00 2001 From: Rene Meusel Date: Tue, 20 Sep 2022 17:20:52 +0200 Subject: [PATCH 3/8] FIX: intermediates can sign their own OCSP responses --- src/lib/x509/x509path.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/lib/x509/x509path.cpp b/src/lib/x509/x509path.cpp index 70cf078fabe..4da0ce4a372 100644 --- a/src/lib/x509/x509path.cpp +++ b/src/lib/x509/x509path.cpp @@ -233,7 +233,12 @@ PKIX::check_ocsp(const std::vector& cert_path, { try { - Certificate_Status_Code ocsp_signature_status = ocsp_responses.at(i)->check_signature(trusted_certstores, cert_path); + // When verifying intermediate certificates we need to truncate the + // cert_path so that the intermediate under investigation becomes the + // last certificate in the chain. + auto ocsp_cert_path = cert_path; + ocsp_cert_path.erase(ocsp_cert_path.begin(), ocsp_cert_path.begin()+i); + Certificate_Status_Code ocsp_signature_status = ocsp_responses.at(i)->check_signature(trusted_certstores, ocsp_cert_path); if(ocsp_signature_status == Certificate_Status_Code::OCSP_SIGNATURE_OK) { From 6646b4ce2d97cb1670ff7a68151ad48a214e1d2f Mon Sep 17 00:00:00 2001 From: Rene Meusel Date: Mon, 7 Nov 2022 15:53:39 +0100 Subject: [PATCH 4/8] Rework validation of OSCP response signatures * introduce `trusted_ocsp_responders` in `Path_Validation_Restrictions` allowing applications to specify inherently trusted OCSP responder certificates * Introduce `OCSP::Response::find_signing_certificate()` to centralize the search logic for the various possible OCSP responder certs * Remove `OCSP::Response::check_signature()` obsoleted by explicitly calling `::find_signing_certificate()` followed by `::verify_signature()` * Perform path validation on authorized OCSP responder certificates see `verify_ocsp_signing_cert()` in x509path.cpp --- src/cli/x509.cpp | 2 +- src/lib/x509/ocsp.cpp | 125 ++++++++++--------------------- src/lib/x509/ocsp.h | 62 ++++++++++------ src/lib/x509/x509cert.cpp | 7 +- src/lib/x509/x509cert.h | 6 ++ src/lib/x509/x509path.cpp | 140 ++++++++++++++++++++++++++--------- src/lib/x509/x509path.h | 58 ++++++++------- src/tests/test_ocsp.cpp | 20 +++-- src/tests/test_x509_path.cpp | 2 +- 9 files changed, 239 insertions(+), 183 deletions(-) diff --git a/src/cli/x509.cpp b/src/cli/x509.cpp index 52874ce50cb..1f701fcc19f 100644 --- a/src/cli/x509.cpp +++ b/src/cli/x509.cpp @@ -228,7 +228,7 @@ class OCSP_Check final : public Command Botan::Certificate_Store_In_Memory cas; cas.add_certificate(issuer); - Botan::OCSP::Response resp = Botan::OCSP::online_check(issuer, subject, &cas, timeout); + Botan::OCSP::Response resp = Botan::OCSP::online_check(issuer, subject, timeout); auto status = resp.status_for(issuer, subject, std::chrono::system_clock::now()); diff --git a/src/lib/x509/ocsp.cpp b/src/lib/x509/ocsp.cpp index 27eb74b8c9d..7441f69f2bf 100644 --- a/src/lib/x509/ocsp.cpp +++ b/src/lib/x509/ocsp.cpp @@ -15,6 +15,8 @@ #include #include +#include + #if defined(BOTAN_HAS_HTTP_UTIL) #include #endif @@ -89,16 +91,14 @@ std::string Request::base64_encode() const } Response::Response(Certificate_Status_Code status) + : m_status(Response_Status_Code::Successful) + , m_dummy_response_status(status) { - m_status = Response_Status_Code::Successful; - m_dummy_response_status = status; } Response::Response(const uint8_t response_bits[], size_t response_bits_len) : m_response_bits(response_bits, response_bits + response_bits_len) { - m_dummy_response_status = Certificate_Status_Code::OCSP_RESPONSE_INVALID; - BER_Decoder response_outer = BER_Decoder(m_response_bits).start_sequence(); size_t resp_status = 0; @@ -154,8 +154,14 @@ Response::Response(const uint8_t response_bits[], size_t response_bits_len) : Certificate_Status_Code Response::verify_signature(const X509_Certificate& issuer) const { - if (m_responses.empty()) - return m_dummy_response_status; + if(m_dummy_response_status) + return m_dummy_response_status.value(); + + if(m_signer_name.empty() && m_key_hash.empty()) + return Certificate_Status_Code::OCSP_RESPONSE_INVALID; + + if(!is_issued_by(issuer)) + return Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND; try { @@ -183,96 +189,52 @@ Certificate_Status_Code Response::verify_signature(const X509_Certificate& issue } } -Certificate_Status_Code Response::check_signature(const std::vector& trusted_roots, - const std::vector& ee_cert_path) const - { - if (m_responses.empty()) - return m_dummy_response_status; - std::optional signing_cert; +std::optional +Response::find_signing_certificate(const X509_Certificate& issuer_certificate, + const Certificate_Store* trusted_ocsp_responders) const + { + using namespace std::placeholders; - for(const auto& trusted_root : trusted_roots) + // Check whether the CA issuing the certificate in question also signed this + if(is_issued_by(issuer_certificate)) { - if(m_signer_name.empty() && m_key_hash.empty()) - return Certificate_Status_Code::OCSP_RESPONSE_INVALID; - - if(!m_signer_name.empty()) - { - signing_cert = trusted_root->find_cert(m_signer_name, std::vector()); - if(signing_cert) - { - break; - } - } - - if(!m_key_hash.empty()) - { - signing_cert = trusted_root->find_cert_by_pubkey_sha1(m_key_hash); - if(signing_cert) - { - break; - } - } + return issuer_certificate; } - if(!signing_cert && ee_cert_path.size() > 1) + // Then try to find a delegated responder certificate in the stapled certs + auto match = std::find_if(m_certs.begin(), m_certs.end(), std::bind(&Response::is_issued_by, this, _1)); + if(match != m_certs.end()) { - // End entity cert is not allowed to sign their own OCSP request :) - 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.empty() && ee_cert_path[i].subject_public_key_bitstring_sha1() == m_key_hash) - { - signing_cert = ee_cert_path[i]; - break; - } - } + return *match; } - if(!signing_cert && !m_certs.empty()) + // Last resort: check the additionally provides trusted OCSP responders + if(trusted_ocsp_responders) { - for(const auto& cert : m_certs) + std::optional signing_cert; + if(!m_key_hash.empty() && (signing_cert = trusted_ocsp_responders->find_cert_by_pubkey_sha1(m_key_hash))) { - // Check all CA certificates in the (assumed validated) EE cert path - if(!m_signer_name.empty() && cert.subject_dn() == m_signer_name) - { - signing_cert = cert; - break; - } - - if(!m_key_hash.empty() && cert.subject_public_key_bitstring_sha1() == m_key_hash) - { - signing_cert = cert; - break; - } + return signing_cert; } - } - - if(!signing_cert) - return Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND; - if(!signing_cert->allowed_usage(CRL_SIGN) && - !signing_cert->allowed_extended_usage("PKIX.OCSPSigning")) - { - return Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE; + if(!m_signer_name.empty() && (signing_cert = trusted_ocsp_responders->find_cert(m_signer_name, {}))) + { + return signing_cert; + } } - return this->verify_signature(*signing_cert); + return std::nullopt; } + Certificate_Status_Code Response::status_for(const X509_Certificate& issuer, const X509_Certificate& subject, std::chrono::system_clock::time_point ref_time, std::chrono::seconds max_age) const { - if(m_responses.empty()) - { return m_dummy_response_status; } + if(m_dummy_response_status) + { return m_dummy_response_status.value(); } for(const auto& response : m_responses) { @@ -309,7 +271,6 @@ Certificate_Status_Code Response::status_for(const X509_Certificate& issuer, Response online_check(const X509_Certificate& issuer, const BigInt& subject_serial, const std::string& ocsp_responder, - Certificate_Store* trusted_roots, std::chrono::milliseconds timeout) { if(ocsp_responder.empty()) @@ -327,21 +288,12 @@ Response online_check(const X509_Certificate& issuer, // Check the MIME type? - OCSP::Response response(http.body()); - - std::vector trusted_roots_vec; - trusted_roots_vec.push_back(trusted_roots); - - if(trusted_roots) - response.check_signature(trusted_roots_vec); - - return response; + return OCSP::Response(http.body()); } Response online_check(const X509_Certificate& issuer, const X509_Certificate& subject, - Certificate_Store* trusted_roots, std::chrono::milliseconds timeout) { if(subject.issuer_dn() != issuer.subject_dn()) @@ -350,7 +302,6 @@ Response online_check(const X509_Certificate& issuer, return online_check(issuer, BigInt::decode(subject.serial_number()), subject.ocsp_responder(), - trusted_roots, timeout); } diff --git a/src/lib/x509/ocsp.h b/src/lib/x509/ocsp.h index 6060b1852b2..a4b9079c80e 100644 --- a/src/lib/x509/ocsp.h +++ b/src/lib/x509/ocsp.h @@ -12,7 +12,9 @@ #include #include #include + #include +#include namespace Botan { @@ -159,23 +161,31 @@ class BOTAN_PUBLIC_API(2,0) Response final size_t response_bits_len); /** - * Check signature and return status - * The optional cert_path is the (already validated!) certificate path of - * the end entity which is being inquired about - * @param trust_roots list of certstores containing trusted roots - * @param cert_path optionally, the (already verified!) certificate path for the certificate - * this is an OCSP response for. This is necessary to find the correct intermediate CA in - * some cases. + * Find the certificate that signed this OCSP response from all possible + * candidates and taking the attached certificates into account. + * + * @param issuer_certificate is the issuer of the certificate in question + * @param trusted_ocsp_responders optionally, a certificate store containing + * additionally trusted responder certificates + * + * @return the certificate that signed this response or std::nullopt if not found */ - Certificate_Status_Code check_signature(const std::vector& trust_roots, - const std::vector& cert_path = {}) const; + std::optional + find_signing_certificate(const X509_Certificate& issuer_certificate, + const Certificate_Store* trusted_ocsp_responders = nullptr) const; /** - * Verify that issuer's key signed this response - * @param issuer certificate of issuer - * @return if signature valid OCSP_SIGNATURE_OK else an error code + * Check signature of the OCSP response. + * + * Note: It is the responsibility of the caller to verify that signing + * certificate is trustworthy and authorized to do so. + * + * @param signing_certificate the certificate that signed this response + * (@sa Response::find_signing_certificate). + * + * @return status code indicating the validity of the signature */ - Certificate_Status_Code verify_signature(const X509_Certificate& issuer) const; + Certificate_Status_Code verify_signature(const X509_Certificate& signing_certificate) const; /** * @return the status of the response @@ -225,6 +235,18 @@ class BOTAN_PUBLIC_API(2,0) Response final */ const std::vector &certificates() const { return m_certs; } + /** + * @return the dummy response if this is a 'fake' OCSP response otherwise std::nullopt + */ + std::optional dummy_status() const { return m_dummy_response_status; } + + private: + bool is_issued_by(const X509_Certificate& candidate) const + { + return (!m_signer_name.empty() && candidate.subject_dn() == m_signer_name) || + (!m_key_hash.empty() && candidate.subject_public_key_bitstring_sha1() == m_key_hash); + } + private: Response_Status_Code m_status; std::vector m_response_bits; @@ -238,39 +260,35 @@ class BOTAN_PUBLIC_API(2,0) Response final std::vector m_responses; - Certificate_Status_Code m_dummy_response_status; + std::optional m_dummy_response_status; }; #if defined(BOTAN_HAS_HTTP_UTIL) /** -* Makes an online OCSP request via HTTP and returns the OCSP response. +* Makes an online OCSP request via HTTP and returns the (unverified) OCSP response. * @param issuer issuer certificate * @param subject_serial the subject's serial number * @param ocsp_responder the OCSP responder to query -* @param trusted_roots trusted roots for the OCSP response * @param timeout a timeout on the HTTP request * @return OCSP response */ -BOTAN_PUBLIC_API(2,1) +BOTAN_PUBLIC_API(3,0) Response online_check(const X509_Certificate& issuer, const BigInt& subject_serial, const std::string& ocsp_responder, - Certificate_Store* trusted_roots, std::chrono::milliseconds timeout = std::chrono::milliseconds(3000)); /** -* Makes an online OCSP request via HTTP and returns the OCSP response. +* Makes an online OCSP request via HTTP and returns the (unverified) OCSP response. * @param issuer issuer certificate * @param subject subject certificate -* @param trusted_roots trusted roots for the OCSP response * @param timeout a timeout on the HTTP request * @return OCSP response */ -BOTAN_PUBLIC_API(2,0) +BOTAN_PUBLIC_API(3,0) Response online_check(const X509_Certificate& issuer, const X509_Certificate& subject, - Certificate_Store* trusted_roots, std::chrono::milliseconds timeout = std::chrono::milliseconds(3000)); #endif diff --git a/src/lib/x509/x509cert.cpp b/src/lib/x509/x509cert.cpp index 02d8c80b928..59d41963263 100644 --- a/src/lib/x509/x509cert.cpp +++ b/src/lib/x509/x509cert.cpp @@ -558,7 +558,7 @@ bool X509_Certificate::allowed_usage(Usage_Type usage) const return (allowed_usage(DIGITAL_SIGNATURE) || allowed_usage(KEY_AGREEMENT)) && allowed_extended_usage("PKIX.ClientAuth"); case Usage_Type::OCSP_RESPONDER: - return (allowed_usage(DIGITAL_SIGNATURE) || allowed_usage(NON_REPUDIATION)) && allowed_extended_usage("PKIX.OCSPSigning"); + return (allowed_usage(DIGITAL_SIGNATURE) || allowed_usage(NON_REPUDIATION)) && has_ex_constraint("PKIX.OCSPSigning"); case Usage_Type::CERTIFICATE_AUTHORITY: return is_CA_cert(); @@ -580,6 +580,11 @@ bool X509_Certificate::has_constraints(Key_Constraints constraints) const return ((this->constraints() & constraints) != 0); } +bool X509_Certificate::has_ex_constraint(const std::string& ex_constraint) const + { + return has_ex_constraint(OID::from_string(ex_constraint)); + } + bool X509_Certificate::has_ex_constraint(const OID& usage) const { const std::vector& ex = extended_key_usage(); diff --git a/src/lib/x509/x509cert.h b/src/lib/x509/x509cert.h index a2e2218b374..021d82af907 100644 --- a/src/lib/x509/x509cert.h +++ b/src/lib/x509/x509cert.h @@ -239,6 +239,12 @@ class BOTAN_PUBLIC_API(2,0) X509_Certificate : public X509_Object */ bool has_constraints(Key_Constraints constraints) const; + /** + * Returns true if and only if OID @param ex_constraint is + * included in the extended key extension. + */ + bool has_ex_constraint(const std::string& ex_constraint) const; + /** * Returns true if and only if OID @param ex_constraint is * included in the extended key extension. diff --git a/src/lib/x509/x509path.cpp b/src/lib/x509/x509path.cpp index 4da0ce4a372..d931a21d0c9 100644 --- a/src/lib/x509/x509path.cpp +++ b/src/lib/x509/x509path.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -33,8 +34,7 @@ PKIX::check_chain(const std::vector& cert_path, std::chrono::system_clock::time_point ref_time, const std::string& hostname, Usage_Type usage, - size_t min_signature_algo_strength, - const std::set& trusted_hashes) + const Path_Validation_Restrictions& restrictions) { if(cert_path.empty()) throw Invalid_Argument("PKIX::check_chain cert_path empty"); @@ -49,7 +49,11 @@ PKIX::check_chain(const std::vector& cert_path, cert_status[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH); if(!cert_path[0].allowed_usage(usage)) + { + if(usage == Usage_Type::OCSP_RESPONDER) + cert_status[0].insert(Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE); cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE); + } if(cert_path[0].is_CA_cert() == false && cert_path[0].has_constraints(KEY_CERT_SIGN)) @@ -134,11 +138,12 @@ PKIX::check_chain(const std::vector& cert_path, if(sig_status != Certificate_Status_Code::VERIFIED) status.insert(sig_status); - if(issuer_key->estimated_strength() < min_signature_algo_strength) + if(issuer_key->estimated_strength() < restrictions.minimum_key_strength()) status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK); } // Ignore untrusted hashes on self-signed roots + const auto& trusted_hashes = restrictions.trusted_hashes(); if(!trusted_hashes.empty() && !at_self_signed_root) { if(trusted_hashes.count(subject.hash_used_for_signature()) == 0) @@ -209,12 +214,76 @@ PKIX::check_chain(const std::vector& cert_path, return cert_status; } +namespace { + +Certificate_Status_Code verify_ocsp_signing_cert( + const X509_Certificate& signing_cert, + const X509_Certificate& ca, + const std::vector& extra_certs, + const std::vector& certstores, + std::chrono::system_clock::time_point ref_time, + const Path_Validation_Restrictions& restrictions) + { + // RFC 6960 4.2.2.2 + // [Applications] MUST reject the response if the certificate + // required to validate the signature on the response does not + // meet at least one of the following criteria: + // + // 1. Matches a local configuration of OCSP signing authority + // for the certificate in question, or + if(restrictions.trusted_ocsp_responders()->certificate_known(signing_cert)) + return Certificate_Status_Code::OK; + + // RFC 6960 4.2.2.2 + // + // 2. Is the certificate of the CA that issued the certificate + // in question, or + if(signing_cert == ca) + return Certificate_Status_Code::OK; + + // RFC 6960 4.2.2.2 + // + // 3. Includes a value of id-kp-OCSPSigning in an extended key + // usage extension and is issued by the CA that issued the + // certificate in question as stated above. + + // TODO: Implement OCSP revocation check of OCSP signer certificate + // Note: This needs special care to prevent endless loops on specifically + // forged chains of OCSP responses referring to each other. + // + // Currently, we're disabling OCSP-based revocation checks by setting the + // timeout to 0. Additionally, the library's API would not allow an + // application to pass in the required "second order" OCSP responses. I.e. + // "second order" OCSP checks would need to rely on `check_ocsp_online()` + // which is not an option for some applications (e.g. that require a proxy + // for external HTTP requests). + const auto ocsp_timeout = std::chrono::milliseconds::zero(); + const auto relaxed_restrictions = + Path_Validation_Restrictions(false /* do not enforce revocation data */, + restrictions.minimum_key_strength(), + false /* OCSP is not available, so don't try for intermediates */, + restrictions.trusted_hashes()); + + const auto validation_result = x509_path_validate( + concat(std::vector{signing_cert}, extra_certs), + relaxed_restrictions, + certstores, + {} /* hostname */, + Botan::Usage_Type::OCSP_RESPONDER, + ref_time, + ocsp_timeout); + + return validation_result.result(); + } + +} + CertificatePathStatusCodes PKIX::check_ocsp(const std::vector& cert_path, const std::vector>& ocsp_responses, - const std::vector& trusted_certstores, + const std::vector& certstores, std::chrono::system_clock::time_point ref_time, - std::chrono::seconds max_ocsp_age) + const Path_Validation_Restrictions& restrictions) { if(cert_path.empty()) throw Invalid_Argument("PKIX::check_ocsp cert_path empty"); @@ -233,23 +302,26 @@ PKIX::check_ocsp(const std::vector& cert_path, { try { - // When verifying intermediate certificates we need to truncate the - // cert_path so that the intermediate under investigation becomes the - // last certificate in the chain. - auto ocsp_cert_path = cert_path; - ocsp_cert_path.erase(ocsp_cert_path.begin(), ocsp_cert_path.begin()+i); - Certificate_Status_Code ocsp_signature_status = ocsp_responses.at(i)->check_signature(trusted_certstores, ocsp_cert_path); - - if(ocsp_signature_status == Certificate_Status_Code::OCSP_SIGNATURE_OK) + const auto& ocsp_response = ocsp_responses.at(i); + + if(auto dummy_status = ocsp_response->dummy_status()) + { + // handle softfail conditions + status.insert(dummy_status.value()); + } + else if(auto signing_cert = ocsp_response->find_signing_certificate(ca, restrictions.trusted_ocsp_responders()); + !signing_cert) + { + status.insert(Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND); + } + else if(auto ocsp_signing_cert_status = verify_ocsp_signing_cert(signing_cert.value(), ca, concat(ocsp_response->certificates(), cert_path), certstores, ref_time, restrictions); + ocsp_signing_cert_status > Certificate_Status_Code::FIRST_ERROR_STATUS) { - // Signature ok, so check the claimed status - Certificate_Status_Code ocsp_status = ocsp_responses.at(i)->status_for(ca, subject, ref_time, max_ocsp_age); - status.insert(ocsp_status); + status.insert(ocsp_signing_cert_status); } else { - // Some signature problem - status.insert(ocsp_signature_status); + status.insert(ocsp_response->status_for(ca, subject, ref_time, restrictions.max_ocsp_age())); } } catch(Exception&) @@ -368,8 +440,7 @@ PKIX::check_ocsp_online(const std::vector& cert_path, const std::vector& trusted_certstores, std::chrono::system_clock::time_point ref_time, std::chrono::milliseconds timeout, - bool ocsp_check_intermediate_CAs, - std::chrono::seconds max_ocsp_age) + const Path_Validation_Restrictions& restrictions) { if(cert_path.empty()) throw Invalid_Argument("PKIX::check_ocsp_online cert_path empty"); @@ -378,7 +449,7 @@ PKIX::check_ocsp_online(const std::vector& cert_path, size_t to_ocsp = 1; - if(ocsp_check_intermediate_CAs) + if(restrictions.ocsp_all_intermediates()) to_ocsp = cert_path.size() - 1; if(cert_path.size() == 1) to_ocsp = 0; @@ -429,7 +500,7 @@ PKIX::check_ocsp_online(const std::vector& cert_path, ocsp_responses.push_back(ocsp_response_future.get()); } - return PKIX::check_ocsp(cert_path, ocsp_responses, trusted_certstores, ref_time, max_ocsp_age); + return PKIX::check_ocsp(cert_path, ocsp_responses, trusted_certstores, ref_time, restrictions); } CertificatePathStatusCodes @@ -776,8 +847,7 @@ PKIX::build_all_certificate_paths(std::vector>& ce void PKIX::merge_revocation_status(CertificatePathStatusCodes& chain_status, const CertificatePathStatusCodes& crl, const CertificatePathStatusCodes& ocsp, - bool require_rev_on_end_entity, - bool require_rev_on_intermediates) + const Path_Validation_Restrictions& restrictions) { if(chain_status.empty()) throw Invalid_Argument("PKIX::merge_revocation_status chain_status was empty"); @@ -815,8 +885,8 @@ void PKIX::merge_revocation_status(CertificatePathStatusCodes& chain_status, if(had_crl == false && had_ocsp == false) { - if((require_rev_on_end_entity && i == 0) || - (require_rev_on_intermediates && i > 0)) + if((restrictions.require_revocation_information() && i == 0) || + (restrictions.ocsp_all_intermediates() && i > 0)) { chain_status[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA); } @@ -884,9 +954,7 @@ Path_Validation_Result x509_path_validate( { CertificatePathStatusCodes status = PKIX::check_chain(cert_path, ref_time, - hostname, usage, - restrictions.minimum_key_strength(), - restrictions.trusted_hashes()); + hostname, usage, restrictions); CertificatePathStatusCodes crl_status = PKIX::check_crl(cert_path, trusted_roots, ref_time); @@ -895,23 +963,21 @@ Path_Validation_Result x509_path_validate( if(!ocsp_resp.empty()) { - ocsp_status = PKIX::check_ocsp(cert_path, ocsp_resp, trusted_roots, ref_time, restrictions.max_ocsp_age()); + ocsp_status = PKIX::check_ocsp(cert_path, ocsp_resp, trusted_roots, ref_time, restrictions); } if(ocsp_status.empty() && ocsp_timeout != std::chrono::milliseconds(0)) { #if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL) ocsp_status = PKIX::check_ocsp_online(cert_path, trusted_roots, ref_time, - ocsp_timeout, restrictions.ocsp_all_intermediates()); + ocsp_timeout, restrictions); #else ocsp_status.resize(1); ocsp_status[0].insert(Certificate_Status_Code::OCSP_NO_HTTP); #endif } - PKIX::merge_revocation_status(status, crl_status, ocsp_status, - restrictions.require_revocation_information(), - restrictions.ocsp_all_intermediates()); + PKIX::merge_revocation_status(status, crl_status, ocsp_status, restrictions); Path_Validation_Result pvd(status, std::move(cert_path)); if(pvd.successful_validation()) @@ -979,11 +1045,13 @@ Path_Validation_Result x509_path_validate( Path_Validation_Restrictions::Path_Validation_Restrictions(bool require_rev, size_t key_strength, bool ocsp_intermediates, - std::chrono::seconds max_ocsp_age) : + std::chrono::seconds max_ocsp_age, + std::unique_ptr trusted_ocsp_responders) : m_require_revocation_information(require_rev), m_ocsp_all_intermediates(ocsp_intermediates), m_minimum_key_strength(key_strength), - m_max_ocsp_age(max_ocsp_age) + m_max_ocsp_age(max_ocsp_age), + m_trusted_ocsp_responders(std::move(trusted_ocsp_responders)) { if(key_strength <= 80) { m_trusted_hashes.insert("SHA-160"); } diff --git a/src/lib/x509/x509path.h b/src/lib/x509/x509path.h index 9aee1497334..b125126c640 100644 --- a/src/lib/x509/x509path.h +++ b/src/lib/x509/x509path.h @@ -49,11 +49,14 @@ class BOTAN_PUBLIC_API(2,0) Path_Validation_Restrictions final * well as end entity (if OCSP enabled in path validation request) * @param max_ocsp_age maximum age of OCSP responses w/o next_update. * If zero, there is no maximum age + * @param trusted_ocsp_responders certificate store containing certificates + * of trusted OCSP responders (additionally to the CA's responders) */ Path_Validation_Restrictions(bool require_rev = false, size_t minimum_key_strength = 110, bool ocsp_all_intermediates = false, - std::chrono::seconds max_ocsp_age = std::chrono::seconds::zero()); + std::chrono::seconds max_ocsp_age = std::chrono::seconds::zero(), + std::unique_ptr trusted_ocsp_responders = std::make_unique()); /** * @param require_rev if true, revocation information is required @@ -67,17 +70,21 @@ class BOTAN_PUBLIC_API(2,0) Path_Validation_Restrictions final * rejected. * @param max_ocsp_age maximum age of OCSP responses w/o next_update. * If zero, there is no maximum age + * @param trusted_ocsp_responders certificate store containing certificates + * of trusted OCSP responders (additionally to the CA's responders) */ Path_Validation_Restrictions(bool require_rev, size_t minimum_key_strength, bool ocsp_all_intermediates, const std::set& trusted_hashes, - std::chrono::seconds max_ocsp_age = std::chrono::seconds::zero()) : + std::chrono::seconds max_ocsp_age = std::chrono::seconds::zero(), + std::unique_ptr trusted_ocsp_responders = std::make_unique()) : 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), - m_max_ocsp_age(max_ocsp_age) {} + m_max_ocsp_age(max_ocsp_age), + m_trusted_ocsp_responders(std::move(trusted_ocsp_responders)) {} /** * @return whether revocation information is required @@ -111,12 +118,21 @@ class BOTAN_PUBLIC_API(2,0) Path_Validation_Restrictions final std::chrono::seconds max_ocsp_age() const { return m_max_ocsp_age; } + /** + * Certificates in this store are trusted to sign OCSP responses + * additionally to the CA's responder certificates. + * @return certificate store containing trusted OCSP responder certs + */ + const Certificate_Store* trusted_ocsp_responders() const + { return m_trusted_ocsp_responders.get(); } + private: bool m_require_revocation_information; bool m_ocsp_all_intermediates; std::set m_trusted_hashes; size_t m_minimum_key_strength; std::chrono::seconds m_max_ocsp_age; + std::unique_ptr m_trusted_ocsp_responders; }; /** @@ -337,22 +353,17 @@ BOTAN_PUBLIC_API(2,0) build_certificate_path(std::vector& cert * against (normally current system clock) * @param hostname the hostname * @param usage end entity usage checks -* @param min_signature_algo_strength 80 or 110 typically -* Note 80 allows 1024 bit RSA and SHA-1. 110 allows 2048 bit RSA and SHA-2. -* Using 128 requires ECC (P-256) or ~3000 bit RSA keys. -* @param trusted_hashes set of trusted hash functions, empty means accept any -* hash we have an OID for +* @param restrictions the relevant path validation restrictions object * @return vector of results on per certificate in the path, each containing a set of * results. If all codes in the set are < Certificate_Status_Code::FIRST_ERROR_STATUS, * then the result for that certificate is successful. If all results are */ CertificatePathStatusCodes -BOTAN_PUBLIC_API(2,0) check_chain(const std::vector& cert_path, +BOTAN_PUBLIC_API(3,0) check_chain(const std::vector& cert_path, std::chrono::system_clock::time_point ref_time, const std::string& hostname, Usage_Type usage, - size_t min_signature_algo_strength, - const std::set& trusted_hashes); + const Path_Validation_Restrictions& restrictions); /** * Check OCSP responses for revocation information @@ -361,16 +372,15 @@ BOTAN_PUBLIC_API(2,0) check_chain(const std::vector& cert_path * @param certstores trusted roots * @param ref_time whatever time you want to perform the validation against * (normally current system clock) -* @param max_ocsp_age maximum age of OCSP responses w/o next_update. If zero, -* there is no maximum age +* @param restrictions the relevant path validation restrictions object * @return revocation status */ CertificatePathStatusCodes -BOTAN_PUBLIC_API(2, 0) check_ocsp(const std::vector& cert_path, +BOTAN_PUBLIC_API(3, 0) check_ocsp(const std::vector& cert_path, const std::vector>& ocsp_responses, const std::vector& certstores, std::chrono::system_clock::time_point ref_time, - std::chrono::seconds max_ocsp_age = std::chrono::seconds::zero()); + const Path_Validation_Restrictions& restrictions); /** * Check CRLs for revocation information @@ -411,19 +421,15 @@ BOTAN_PUBLIC_API(2,0) check_crl(const std::vector& cert_path, * (normally current system clock) * @param timeout for timing out the responses, though actually this function * may block for up to timeout*cert_path.size()*C for some small C. -* @param ocsp_check_intermediate_CAs if true also performs OCSP on any intermediate -* CA certificates. If false, only does OCSP on the end entity cert. -* @param max_ocsp_age maximum age of OCSP responses w/o next_update. If zero, -* there is no maximum age +* @param restrictions the relevant path validation restrictions object * @return revocation status */ CertificatePathStatusCodes -BOTAN_PUBLIC_API(2, 0) check_ocsp_online(const std::vector& cert_path, +BOTAN_PUBLIC_API(3, 0) check_ocsp_online(const std::vector& cert_path, const std::vector& trusted_certstores, std::chrono::system_clock::time_point ref_time, std::chrono::milliseconds timeout, - bool ocsp_check_intermediate_CAs, - std::chrono::seconds max_ocsp_age = std::chrono::seconds::zero()); + const Path_Validation_Restrictions& restrictions); /** * Check CRL using online (HTTP) access. Current version creates a thread and @@ -459,14 +465,12 @@ Certificate_Status_Code BOTAN_PUBLIC_API(2,0) overall_status(const CertificatePa * @param chain_status the certificate status * @param crl_status results from check_crl * @param ocsp_status results from check_ocsp -* @param require_rev_on_end_entity require valid CRL or OCSP on end-entity cert -* @param require_rev_on_intermediates require valid CRL or OCSP on all intermediate certificates +* @param restrictions the relevant path validation restrictions object */ -void BOTAN_PUBLIC_API(2,0) merge_revocation_status(CertificatePathStatusCodes& chain_status, +void BOTAN_PUBLIC_API(3,0) merge_revocation_status(CertificatePathStatusCodes& chain_status, const CertificatePathStatusCodes& crl_status, const CertificatePathStatusCodes& ocsp_status, - bool require_rev_on_end_entity, - bool require_rev_on_intermediates); + const Path_Validation_Restrictions& restrictions); } diff --git a/src/tests/test_ocsp.cpp b/src/tests/test_ocsp.cpp index 7d15b60c62e..e538bd5e3fb 100644 --- a/src/tests/test_ocsp.cpp +++ b/src/tests/test_ocsp.cpp @@ -5,7 +5,7 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include "tests.h" +#include "botan/build.h" #if defined(BOTAN_HAS_OCSP) #include @@ -15,6 +15,8 @@ #include #endif +#include "tests.h" + namespace Botan_Tests { #if defined(BOTAN_HAS_OCSP) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) @@ -146,7 +148,7 @@ class OCSP_Tests final : public Test auto check_ocsp = [&](const std::chrono::system_clock::time_point valid_time, const Botan::Certificate_Status_Code expected) { - const auto ocsp_status = Botan::PKIX::check_ocsp(cert_path, { ocsp }, { &certstore }, valid_time); + const auto ocsp_status = Botan::PKIX::check_ocsp(cert_path, { ocsp }, { &certstore }, valid_time, Botan::Path_Validation_Restrictions()); return result.test_eq("Expected size of ocsp_status", ocsp_status.size(), 1) && result.test_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1) && @@ -187,7 +189,8 @@ class OCSP_Tests final : public Test auto check_ocsp = [&](const std::chrono::system_clock::time_point valid_time, const Botan::Certificate_Status_Code expected) { - const auto ocsp_status = Botan::PKIX::check_ocsp(cert_path, { ocsp }, { &certstore }, valid_time, max_age); + Botan::Path_Validation_Restrictions pvr(false, 110, false, max_age); + const auto ocsp_status = Botan::PKIX::check_ocsp(cert_path, { ocsp }, { &certstore }, valid_time, pvr); return result.test_eq("Expected size of ocsp_status", ocsp_status.size(), 1) && result.test_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1) && @@ -228,7 +231,8 @@ class OCSP_Tests final : public Test auto check_ocsp = [&](const std::chrono::system_clock::time_point valid_time, const Botan::Certificate_Status_Code expected) { - const auto ocsp_status = Botan::PKIX::check_ocsp(cert_path, { ocsp }, { &certstore }, valid_time, max_age); + Botan::Path_Validation_Restrictions pvr(false, 110, false, max_age); + const auto ocsp_status = Botan::PKIX::check_ocsp(cert_path, { ocsp }, { &certstore }, valid_time, pvr); return result.test_eq("Expected size of ocsp_status", ocsp_status.size(), 1) && result.test_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1) && @@ -264,7 +268,7 @@ class OCSP_Tests final : public Test auto check_ocsp = [&](const std::chrono::system_clock::time_point valid_time, const Botan::Certificate_Status_Code expected) { - const auto ocsp_status = Botan::PKIX::check_ocsp(cert_path, { ocsp }, { &certstore }, valid_time); + const auto ocsp_status = Botan::PKIX::check_ocsp(cert_path, { ocsp }, { &certstore }, valid_time, Botan::Path_Validation_Restrictions()); return result.test_eq("Expected size of ocsp_status", ocsp_status.size(), 1) && result.test_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1) && @@ -299,13 +303,13 @@ class OCSP_Tests final : public Test // Some arbitrary time within the validity period of the test certs const auto valid_time = Botan::calendar_point(2016, 11, 20, 8, 30, 0).to_std_timepoint(); - const auto ocsp_status = Botan::PKIX::check_ocsp(cert_path, { ocsp }, { &certstore }, valid_time); + const auto ocsp_status = Botan::PKIX::check_ocsp(cert_path, { ocsp }, { &certstore }, valid_time, Botan::Path_Validation_Restrictions()); if(result.test_eq("Expected size of ocsp_status", ocsp_status.size(), 1)) { if(result.test_eq("Expected size of ocsp_status[0]", ocsp_status[0].size(), 1)) { - result.confirm("Status warning", ocsp_status[0].count(Botan::Certificate_Status_Code::OCSP_NO_REVOCATION_URL) > 0); + result.test_gt("Status warning", ocsp_status[0].count(Botan::Certificate_Status_Code::OCSP_NO_REVOCATION_URL), 0); } } @@ -327,7 +331,7 @@ class OCSP_Tests final : public Test const auto ocsp_timeout = std::chrono::milliseconds(3000); const auto now = std::chrono::system_clock::now(); - auto ocsp_status = Botan::PKIX::check_ocsp_online(cert_path, { &certstore }, now, ocsp_timeout, false); + auto ocsp_status = Botan::PKIX::check_ocsp_online(cert_path, { &certstore }, now, ocsp_timeout, Botan::Path_Validation_Restrictions()); if(result.test_eq("Expected size of ocsp_status", ocsp_status.size(), 1)) { diff --git a/src/tests/test_x509_path.cpp b/src/tests/test_x509_path.cpp index 0ff95b3c24c..1c2bbcc96db 100644 --- a/src/tests/test_x509_path.cpp +++ b/src/tests/test_x509_path.cpp @@ -1168,7 +1168,7 @@ class XMSS_Path_Validation_Tests final : public Test auto valid_time = Botan::calendar_point(2019, 10, 8, 4, 45, 0).to_std_timepoint(); auto status = Botan::PKIX::overall_status(Botan::PKIX::check_chain(cert_path, valid_time, - "", Botan::Usage_Type::UNSPECIFIED, restrictions.minimum_key_strength(), restrictions.trusted_hashes())); + "", Botan::Usage_Type::UNSPECIFIED, restrictions)); result.test_eq("Cert validation status", Botan::to_string(status), "Verified"); return result; From 7107258fdd1cc881479050aff2a9bfa4cea8a312 Mon Sep 17 00:00:00 2001 From: Rene Meusel Date: Mon, 7 Nov 2022 15:57:20 +0100 Subject: [PATCH 5/8] Regression Tests for the fixed issues --- .../data/x509/ocsp/bdr-int-ocsp-resp.der | Bin 0 -> 3000 bytes src/tests/data/x509/ocsp/bdr-int.pem | 35 +++ src/tests/data/x509/ocsp/bdr-ocsp-resp.der | Bin 0 -> 3645 bytes .../data/x509/ocsp/bdr-ocsp-responder.pem | 32 +++ src/tests/data/x509/ocsp/bdr-root.pem | 25 ++ src/tests/data/x509/ocsp/bdr.pem | 80 +++++++ src/tests/data/x509/ocsp/mychain_creater.sh | 224 ++++++++++++++++++ src/tests/data/x509/ocsp/mychain_ee.pem | 20 ++ src/tests/data/x509/ocsp/mychain_int.pem | 21 ++ .../mychain_int_ocsp_delegate_responder.pem | 21 ++ ...p_delegate_responder_no_ocsp_key_usage.pem | 21 ++ .../data/x509/ocsp/mychain_ocsp_for_ee.der | Bin 0 -> 512 bytes .../mychain_ocsp_for_ee_delegate_signed.der | Bin 0 -> 1423 bytes ..._ocsp_for_ee_delegate_signed_malformed.der | Bin 0 -> 1401 bytes .../ocsp/mychain_ocsp_for_ee_root_signed.der | Bin 0 -> 511 bytes .../ocsp/mychain_ocsp_for_int_self_signed.der | Bin 0 -> 512 bytes src/tests/data/x509/ocsp/mychain_root.pem | 22 ++ .../ocsp/randombit_ocsp_forged_responder.pem | 23 ++ .../ocsp/randombit_ocsp_forged_revoked.der | Bin 0 -> 1593 bytes .../x509/ocsp/randombit_ocsp_forged_valid.der | Bin 0 -> 1574 bytes .../randombit_ocsp_forged_valid_nocerts.der | Bin 0 -> 581 bytes .../data/x509/ocsp/randombit_ocsp_forger.sh | 119 ++++++++++ src/tests/test_ocsp.cpp | 72 ++++++ src/tests/test_x509_path.cpp | 164 ++++++++++++- 24 files changed, 878 insertions(+), 1 deletion(-) create mode 100644 src/tests/data/x509/ocsp/bdr-int-ocsp-resp.der create mode 100644 src/tests/data/x509/ocsp/bdr-int.pem create mode 100644 src/tests/data/x509/ocsp/bdr-ocsp-resp.der create mode 100644 src/tests/data/x509/ocsp/bdr-ocsp-responder.pem create mode 100644 src/tests/data/x509/ocsp/bdr-root.pem create mode 100644 src/tests/data/x509/ocsp/bdr.pem create mode 100644 src/tests/data/x509/ocsp/mychain_creater.sh create mode 100644 src/tests/data/x509/ocsp/mychain_ee.pem create mode 100644 src/tests/data/x509/ocsp/mychain_int.pem create mode 100644 src/tests/data/x509/ocsp/mychain_int_ocsp_delegate_responder.pem create mode 100644 src/tests/data/x509/ocsp/mychain_int_ocsp_delegate_responder_no_ocsp_key_usage.pem create mode 100644 src/tests/data/x509/ocsp/mychain_ocsp_for_ee.der create mode 100644 src/tests/data/x509/ocsp/mychain_ocsp_for_ee_delegate_signed.der create mode 100644 src/tests/data/x509/ocsp/mychain_ocsp_for_ee_delegate_signed_malformed.der create mode 100644 src/tests/data/x509/ocsp/mychain_ocsp_for_ee_root_signed.der create mode 100644 src/tests/data/x509/ocsp/mychain_ocsp_for_int_self_signed.der create mode 100644 src/tests/data/x509/ocsp/mychain_root.pem create mode 100644 src/tests/data/x509/ocsp/randombit_ocsp_forged_responder.pem create mode 100644 src/tests/data/x509/ocsp/randombit_ocsp_forged_revoked.der create mode 100644 src/tests/data/x509/ocsp/randombit_ocsp_forged_valid.der create mode 100644 src/tests/data/x509/ocsp/randombit_ocsp_forged_valid_nocerts.der create mode 100755 src/tests/data/x509/ocsp/randombit_ocsp_forger.sh diff --git a/src/tests/data/x509/ocsp/bdr-int-ocsp-resp.der b/src/tests/data/x509/ocsp/bdr-int-ocsp-resp.der new file mode 100644 index 0000000000000000000000000000000000000000..ac70762e276e00c313901cf7f5cfda73156e976a GIT binary patch literal 3000 zcmc(gdpK0<9>=Y@xlYP0#Oxu&xXe3aTtjLojv8YUnraWmC3`T;47MgQLsCs_hc0v* zh3Mp(!ahoEQk`g$b}og;>0&2x+Syc`HPYGDInUD{=eC|_t@nL@&%1uN^?g4Jh*j%U zAO)#dwE~D$%Q4E97zmA41Q3K0s}{nhLqH6Xi`XE9qzaTV$W17WCc?v$qzyFX3l&W@ z4{Lv30$+e%7Zd75S_z!wW~3$=HZwMD^v5%}TmhaM9m411ZSmCAcrxBI08a+M3GYkY zNXIk6`ElIXuy7vUPFI}_$YkI|aw1Vk_P~}2M2$dn5xBr{(Uyyl2z|5yN=u#gZo)6& z;v#P?itp}sulBDJb9+!)q0g#koL#aT3$1TNUShpY0^bb~5J>fr>@5L-G(S^eizGj&@GclQF^@2$e1<~_ohlYkwV zznB1vQ6^-1WlmusNENMs6hn}LUjn^s@eLD|IO4g1SZ@wP-*&q`f$qjA{d2ue>e))k z+x1tL2WU2bKO$JRYwTTC>D~isrrl85+;+_4KMD1P6-KM^H#9@djmtU`3D8dTK-JcW z_R5=U>NjXej!F3MQ?LDKVlKrdmDo2=*`G=jW)}@N4&ATb7nrx3k$7A)2>E{B0tdFaKT3GA!pCM|-HEzcbvpA}$p zV(tWSc$vHM%}%Z(yI&I@Jt%G6uH>#5T&+e{49s}0TdguO6JnLISv?I|&lij}>9SlI)JQPV(-9Figt5CXB7Ft>7UBto_R_yglZ z{ryi3zi1tx|7+!`eXcwLFrQbRn$cI42XITVBq!iRwgrw92Rl3FQY`tyw{x@u6np0X zK27bD<9MLOiBd}|;5ZfxURdSBLN_#XC+@P3>cVV>6 zofs~idwA3Owvd;e7T}bd9hfUUx^y<;T>Th9t=^%Wc`hc1(=sjV)vs&WQLKz$3@I8f>KbbVq~HJknH)1Nn}m4pWfLgh)}SG z03Dqf3H4;vRED|!#9#ebZa{o7^@myW zlUX^NuMs+R8p0}5T}X*72qdHcofbn)a7P;ESz~C5QUfX&r4N8YDg#6KdmAo7>7cYa zz72g1D%-vBW4AJ7wOxvqBfkUz_1_OD8ihbBfC%}1Jc<(sA1^5X3nXiAhOGHeuERIoxbw!v(~sSV1^XK8c8m4(AgWWX6&Mjz#zJW>M)I zZQ{Zrz&G+Z%Tc;OCrzs>f8Y90O@f|HdlheAihXyX!NW+fNWMWGg62>tBm(0OMBQ@3 zHz<(|h}wbZrc~4>6}4ohMu&yS^9YOLg|h`*Uh-$zP-(6U8JLoRJO_A~$1FVB%IB-$ z2N}LS1LPAJxK_UMH{|8O205**sIDHKtJvW@K~zK(I|QySJdq2JQQX+*D83-meW4L- zUi4?7;WcrgF)W_EdbS+zEL#>kghghBC$i+xY~Wp=D*Ph^vFmD8YhCfRoB3XQB9W)k z*<~*uM3&|;sw7)|xTrIze2rK_vc^916{?;U?~Rb1k6EtB$Q)W7C!44}t#8TEMOmc_ z+NiHPr`{H8RcRy+6?i)eyzrTq7p?FXy}xL9j!|6`uN46_ z9?TTG#cZ2sYK`u!;cEX&+-HOkjm+=zv$9`hj$A< zrw@Spl6U`k!hQ{J1+OwFaX%94fi(NlPL%9&Xj8#^@!`8WR3Z;KD!*AwJ7~-=IMAnU z{S^I9k8fO`eMF(j#r0fK^xON%ral!D?O}acZWayd%3YLx+h0RFhdT70ztr_zA5=Mv zyRWuIo7v;s(5f4Tdu&^KVQ&&mt+_X!qj{LCaeB6(9M!J=f9?T;fYp`84&ORV8uoTtV~J6IXD&yS?sFzwx~^X{q&0Eb*|1TzrRKZY zx|+54Q*UuA6KBs_eAwyy&O!T?eK0ziHSqPsw}M=HvY zibVf+fPl4959ykJVB|}l{#zKKj0u@8#rav|`b!cNiRWisW^jIHzwCKqG4SZsy*3l& z=T~K$meqmY+xoK@zf$dD>+xEjX1uwncW0@666IOgAfsluX+74&l5x0!2iJ7e)4x=qHd+JUx zyP&kUTZzqfT# literal 0 HcmV?d00001 diff --git a/src/tests/data/x509/ocsp/bdr-int.pem b/src/tests/data/x509/ocsp/bdr-int.pem new file mode 100644 index 00000000000..299fb22d705 --- /dev/null +++ b/src/tests/data/x509/ocsp/bdr-int.pem @@ -0,0 +1,35 @@ +-----BEGIN CERTIFICATE----- +MIIGHzCCBQegAwIBAgIDD+SOMA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0xNjExMTYwOTQ2MTlaFw0yOTExMDUwODUw +NDZaMF4xCzAJBgNVBAYTAkRFMRUwEwYDVQQKEwxELVRydXN0IEdtYkgxHzAdBgNV +BAMTFkQtVFJVU1QgQ0EgMi0yIEVWIDIwMTYxFzAVBgNVBGETDk5UUkRFLUhSQjc0 +MzQ2MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4PbGGVT4nCH+CzaZ +kZDWqXWiXbBu3UEpSPBmAKDepwkoc7b13vVlRrvehBm11yUNzaN7thsqB+VXEyF4 +OuxkCJkZRCJUfrS1zdnZXptf361oahCX+ch2E4Hdedeet45mypwKsD7FqSdz01KY +o6wFQMnnZsRQtamilglAgT03iTUf+Yn8a5msV7fscpfkLUGCtjeM2eWgfZ2I0pqi +m3DYrQU5/8in6DtZLrIAgZpnQsgJiB3glx0YcBXs5YZR9bfhOP71nLvM+9vkxNR4 +V90SOnwEzCbj5VforuNgP0sptC2TSPiqNG9sgdySBobz9aO5ryqG21GXMcfFp0vC +z2kxrwIDAQABo4IC8jCCAu4wHwYDVR0jBBgwFoAU05SKTGITKhkuzK9yin0215oc +3GcwggElBggrBgEFBQcBAQSCARcwggETMDcGCCsGAQUFBzABhitodHRwOi8vcm9v +dC1jMy1jYTItZXYtMjAwOS5vY3NwLmQtdHJ1c3QubmV0MFAGCCsGAQUFBzAChkRo +dHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NnaS1iaW4vRC1UUlVTVF9Sb290X0NsYXNz +XzNfQ0FfMl9FVl8yMDA5LmNydDCBhQYIKwYBBQUHMAKGeWxkYXA6Ly9kaXJlY3Rv +cnkuZC10cnVzdC5uZXQvQ049RC1UUlVTVCUyMFJvb3QlMjBDbGFzcyUyMDMlMjBD +QSUyMDIlMjBFViUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NBQ2VydGlm +aWNhdGU/YmFzZT8wfwYDVR0gBHgwdjAJBgcEAIvsQAEEMA0GCysGAQQBpTQCgRYE +MFoGCysGAQQBpTQCgUoBMEswSQYIKwYBBQUHAgEWPWh0dHA6Ly93d3cuZC10cnVz +dC5uZXQvaW50ZXJuZXQvZmlsZXMvRC1UUlVTVF9DU01fUEtJX0NQUy5wZGYwgd0G +A1UdHwSB1TCB0jCBh6CBhKCBgYZ/bGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l +dC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMEVWJTIw +MjAwOSxPPUQtVHJ1c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9u +bGlzdDBGoESgQoZAaHR0cDovL2NybC5kLXRydXN0Lm5ldC9jcmwvZC10cnVzdF9y +b290X2NsYXNzXzNfY2FfMl9ldl8yMDA5LmNybDAdBgNVHQ4EFgQUIa9qJphx6SYK +1duhjPfbpp2lJVwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAw +DQYJKoZIhvcNAQELBQADggEBAEITrEZFU4bOy+274S2THOe9lewgYy+5OYh/Wr7Q +WzRi/bMU6GRtag9fCnIsXon3+2wKGL22JgjI+WnZa5TRiazUOdtOjCEuwxXXMYH/ +PaBBb/BXmfGlEHGHL/ljNQauOrsfIQXXDYTfZk9jwLQgPmF54Ulm6oLsUrvYp1nq +4jSAyWOY+mcxFlGgZPt5jdL1DSkzdLtdWfGs+1USqmx/IBZLfCwavdk0Dm5fwQSG +iI+av54kU0E4ziDEOJ25rfiOBGqjh+4NFegAaQlTeVp1zOCjtKCf9YWDS8BgJT+O +Ri2UKV/O8WaWZ3qRLuVavpng14sx4oa8FLM9sKBWvI+H5XU= +-----END CERTIFICATE----- diff --git a/src/tests/data/x509/ocsp/bdr-ocsp-resp.der b/src/tests/data/x509/ocsp/bdr-ocsp-resp.der new file mode 100644 index 0000000000000000000000000000000000000000..423c2b170b8ba103292d24fa83b0016c87d520ef GIT binary patch literal 3645 zcmchZdpuO>AIIm6GgB^u8iaKjm0C1BGX_n&ZjBMC+_l-Zi7_;qTxNz+DreNVY-vR+ zx=qPit;#RAwNi@RD0ETbr4$ia6w*!Ucg`?d?d{j^kKOs>%sJoJdA{H0`JT`7{yadU z&QyVLGKo4JNYpKf$`(Y3K$r#~2$rZDqoGfL1adkW0)lC(K$)oI4-?6F7dA~Bkg*39 zvYHFkPZ%Q-Qo-AK43WGwY(kbi!3LVgFX|sSf_EHd8 zeTAQo3!CcZv)GPd&9K$gpaVJ`FlkI0gSL=n8wikJ08#_o(ZU1^btSw$0S9Yc9HqNo zz)?b$y7t{neOThi1hHQo+gwAcU>`^bU0xUpF&YiAAg3%>?P(MM|;_yRRm+z99eAmLMap?l+%x zxlUXw`r)wu-iIMST3M{R{NU$ty&ZW%f1L_LM(aRTulkYhrp(>V@HUmQ(RSD|2FyLW zbA5*^@yy}*HY$1(-^b&!3}ms_j6M3UMFG1W_Kk?zq|SFB_68Mgxb&Rq&W}Zi9k7C;2^3w&rBr zpLwEay`SjBrHSm_Z)Qmdi3ZI2oC9-5YH#V+XW?}PXKk5iGAEZm;I&p)2om*+#0n>V}nufhP^w( zix(tvjT!4-&%e_82u8NHyn&?`FtyG8RY z@WfugilYGxunSoBE^J4xGm9q_^F#UEATiG|I7q~E1jsnHVq+K?1;`VC49bvx8PY3_ z_-7{YuDp;J8;MSX9~mLwi^Ni!slIWAf)AF8K3h%ok;4s=_ly^-=$Sz3pcobsp%_Nx z3L;}dusrg<|Lu7BZZ}kibzm(s-{o7=!#acJ@Gh^rm3w3JYu$s}fI8M24G4M*!%BFf z6VP~vYXXcX;NIVD3O;9*B%6+GF6=mR;*0CmW~^$f9}oXDwrlw=R(%E8gwk=Ke8}t$ zWLkNK$dPnk*7?56A$CaZ(c*K7{`^e8dzOccHthd=qEPSZ0K*{dv3H$mfLF)ZXy3jX zvtLVO8=GtiC(35Gu09e&+oGS5cO}0*!;ECQ#+#J6tKkrGj>^13Eq3$UtTs&Nr`*eY z?C21-n_4xvW%uT^^Acmp@e1A1?XJtHwM))~_i$Rv{Y1+oPW@-y{kGJl-ia6%&S}1B zc4_2#=MCc)d~emf#_2@$pu}@^A zn8FeSi9{4@3dZHhlL27L;XCP@OolCu`3Ly^)A0Xa;JyFs7d^ih2TdkaZL`zAxm^%* z?5m=PyUu2APeO6Bj(p`$MP*|hV?5W&jvacXEvBT#+%09Fm{03jNjBNEXdq;Iww}vu zzilO@jcsjf4shZ!~yk4JOxUzgenAJ^nmXz7;YU`10J(PSS=h%U$){_M0+q(R|&sJHU z!67+ei|dq=4ew_e>PBe~^hmwO$~zZMj2*0O7;o>cy}4=S-Iojw;r8MLBT|6e*-Mm;j^$RIHkY=0XVEVGobPe*DxY>*0p1Z5yh9%_wmCC(SuojL z&$8jPFmg~OTxBk@#Zo7QJN%8QU<%@JF{q#)8gm$q++XA$TJWAf{OMuf1Al;sU?$cL zGUgZ40TKPfLw{-!i^+wqY+q^9mBGqRYKB2?)xH4=*Q$bJo}WP+X^bXuo8@r?NGFReu*Fp`N?{RTxdjTNW}NxBJEz z$;?_@`PHi7!g2qZCj{FmIv(*0^sCw!>Jgj^urzs3&iTW0eVrFJQEC?+syO)~4Gzyu z8X{@;;rPnFae*-n_j5~RN5*z0dQ`79bxd=mW}0!DhC{y%+njFM6L>EBe#_UiF6mjV PV*4Ulz}Y=XJu&|U=~e_O literal 0 HcmV?d00001 diff --git a/src/tests/data/x509/ocsp/bdr-ocsp-responder.pem b/src/tests/data/x509/ocsp/bdr-ocsp-responder.pem new file mode 100644 index 00000000000..4ea71835526 --- /dev/null +++ b/src/tests/data/x509/ocsp/bdr-ocsp-responder.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFlDCCBHygAwIBAgIQS46011w0OehAK/Gn1tYrmDANBgkqhkiG9w0BAQsFADBe +MQswCQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMR8wHQYDVQQDExZE +LVRSVVNUIENBIDItMiBFViAyMDE2MRcwFQYDVQRhEw5OVFJERS1IUkI3NDM0NjAe +Fw0xOTA5MTcxMjU2MTdaFw0yOTExMDUwODUwNDZaMGIxCzAJBgNVBAYTAkRFMRUw +EwYDVQQKEwxELVRydXN0IEdtYkgxIzAhBgNVBAMTGkQtVFJVU1QgT0NTUCA0IDIt +MiBFViAyMDE2MRcwFQYDVQRhEw5OVFJERS1IUkI3NDM0NjCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBANYwqY+taeXwfZqX4BKqsGtUc6rH/pmN/ReCBYMc +OcmamyTyxNUayZcCGGy3d7ykHQYtSFQHt0gMxx0BHkoVqVj0XHGSZ4krXPPqEKxS +hS2lY6kQkb/J58P9w0u6O9O98Oa9+LaDdY+kT9k+IcCTSyPNMa6IitERG/RP+rHy +ZYpB5gNQy2UvcYpEgDeCdu/qJEVbUfTPATME2HB4D20eYm2ShWqwTWfZ5wnttUkT +33HdlWDo0JFWhA0FK1CHGNPiWF5qzZNr/CfL2LikkTzYCf7B3pTg/zrEc5H0SiCz +TXlR8a6NYH0vh8SB1uFVN/gfB4ik6dsk08yls5wfyx3ts4MCAwEAAaOCAkgwggJE +MBMGA1UdJQQMMAoGCCsGAQUFBwMJMB8GA1UdIwQYMBaAFCGvaiaYcekmCtXboYz3 +26adpSVcMIHNBggrBgEFBQcBAQSBwDCBvTBFBggrBgEFBQcwAoY5aHR0cDovL3d3 +dy5kLXRydXN0Lm5ldC9jZ2ktYmluL0QtVFJVU1RfQ0FfMi0yX0VWXzIwMTYuY3J0 +MHQGCCsGAQUFBzAChmhsZGFwOi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQt +VFJVU1QlMjBDQSUyMDItMiUyMEVWJTIwMjAxNixPPUQtVHJ1c3QlMjBHbWJILEM9 +REU/Y0FDZXJ0aWZpY2F0ZT9iYXNlPzCB+wYDVR0fBIHzMIHwMIHtoIHqoIHnhm5s +ZGFwOi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBDQSUyMDIt +MiUyMEVWJTIwMjAxNixPPUQtVHJ1c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVy +ZXZvY2F0aW9ubGlzdIY1aHR0cDovL2NybC5kLXRydXN0Lm5ldC9jcmwvZC10cnVz +dF9jYV8yLTJfZXZfMjAxNi5jcmyGPmh0dHA6Ly9jZG4uZC10cnVzdC1jbG91ZGNy +bC5uZXQvY3JsL2QtdHJ1c3RfY2FfMi0yX2V2XzIwMTYuY3JsMB0GA1UdDgQWBBQp +U019kWfhYSNlymjVo9J6/BicfjAOBgNVHQ8BAf8EBAMCBkAwDwYJKwYBBQUHMAEF +BAIFADANBgkqhkiG9w0BAQsFAAOCAQEAl7SIJfJ6rN6fqljRwClDwC+4nrr1jE2M +Q8m5EyEg3pu37incASW7sgZfguTKFqjiPnbuDOlCvoNVaZRU3y6dHm2YPP6sGc/s +NByO71HIJVZQ3vXz6JXDKeu0oHfONgWqtiLYXJ91MXsak6XLp8mTKQ0lXlENlIzM +nYG+LTnLLbNIVHoM8TJpid+U7z8+Z40tve17jXqOv4IfgqS5GPN/RkwtxEeyauVf +2LdUc0yCQOq8SVR7yIrcbvFyI9PUKcfy0eHSH9cC570mzQgGDu6jU3L1IA1a6TLq +gkcgAszklWDPbLdBLs51cO5Svc3AS77b9xjXrPVzKHxyFFQaxJuwpA== +-----END CERTIFICATE----- diff --git a/src/tests/data/x509/ocsp/bdr-root.pem b/src/tests/data/x509/ocsp/bdr-root.pem new file mode 100644 index 00000000000..0a1a2b2dd69 --- /dev/null +++ b/src/tests/data/x509/ocsp/bdr-root.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw +NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV +BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn +ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0 +3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z +qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR +p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8 +HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw +ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea +HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw +Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh +c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E +RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt +dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku +Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp +3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF +CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na +xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX +KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- diff --git a/src/tests/data/x509/ocsp/bdr.pem b/src/tests/data/x509/ocsp/bdr.pem new file mode 100644 index 00000000000..604defc867f --- /dev/null +++ b/src/tests/data/x509/ocsp/bdr.pem @@ -0,0 +1,80 @@ +-----BEGIN CERTIFICATE----- +MIIOhDCCDWygAwIBAgIQR2P0PtEycYOZmkQw8teHNzANBgkqhkiG9w0BAQsFADBe +MQswCQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMR8wHQYDVQQDExZE +LVRSVVNUIENBIDItMiBFViAyMDE2MRcwFQYDVQRhEw5OVFJERS1IUkI3NDM0NjAe +Fw0yMjAzMjUwODE1NDVaFw0yMzAzMjgwNzE1NDVaMIIBIjELMAkGA1UEBhMCREUx +HTAbBgNVBAoTFEJ1bmRlc2RydWNrZXJlaSBHbWJIMQswCQYDVQQLEwJJVDEbMBkG +A1UEAxMSYnVuZGVzZHJ1Y2tlcmVpLmRlMQ8wDQYDVQQHEwZCZXJsaW4xEzARBgsr +BgEEAYI3PAIBAxMCREUxDjAMBgNVBBEMBTEwOTY5MR0wGwYDVQQPDBRQcml2YXRl +IE9yZ2FuaXphdGlvbjEcMBoGA1UECRMTS29tbWFuZGFudGVuc3RyLiAxODEUMBIG +A1UEBRMLSFJCIDcwNzY0IEIxFzAVBgsrBgEEAYI3PAIBAQwGQmVybGluMRcwFQYL +KwYBBAGCNzwCAQIMBkJlcmxpbjEPMA0GA1UECBMGQmVybGluMIICIjANBgkqhkiG +9w0BAQEFAAOCAg8AMIICCgKCAgEA3B1Rp2V3DQSrr57KSDLsZ6mUJ0Y9LWhcLvTo +b84DN1Y/U9ZCyGGJ2hYiDnwPpcIHFfp1v+jUaiE8Km4VA6tkG8o6Y5w2BMM9Ej20 +z2kwOtVdSh1wdC9zinmuGwjshmw2eSvr1C77y3jN6P1qDyjACdAQ6SM8hKV5JxFz +g0+UAN0lO51C9v61EXjteByo6ikDEGnjFc+fC5kQGGJGRy4+I1vfgIsYri1LhGOS +86xH9o4RejCiM5Az4wfMgzobmeizsugAljxXcwMpVM8jA/rzUyRUqAwjsIcC4qFt +K1tj7vQy9bpUN3xWc6VDvZjFOat/z551I6JM6kPshN5DoW6O0s3H7BoxSx0N69UA ++zb/Fefk/oy6BR4jwwvJboHjaOpliZUC/2uXOd2pp4/MCyhILz2ikRr6EMD7qCDd +9QFabRFjKe1GzKs0Uh6ewlrX1IHs4REmmf6f5+gCeBWGrwGAWhm69Pdbv2NgfS4t +OYob8Z2APvr+QsVsuwch7bcX99wp67gaw1Cgtsz4iAKLw73Aza6dJxoH6cC5x6PD +Fkpoo6sXYNVovVBPVDuq5Wnd+qvSBsjzlzILUQCfuVn+CcttYzFKMMX2LHvSSRhB +A64iOouMS/sWGvdamvqrlzUpKoeIpJhPit0D23xNq48LpEaHs3CZFsvung29Z9Vg +8flG6h0CAwEAAaOCCXYwgglyMIIBKwYDVR0RBIIBIjCCAR6CBmJkci5kZYIed3d3 +LnN1cHBvcnQuYnVuZGVzZHJ1Y2tlcmVpLmRlgg53d3cuc2lnbi1tZS5kZYIWd3d3 +LmJ1bmRlc2RydWNrZXJlaS5kZYIXd3d3LmJ1bmRlc2RydWNrZXJlaS5jb22CCnd3 +dy5iZHIuZGWCGnN1cHBvcnQuYnVuZGVzZHJ1Y2tlcmVpLmRlggpzaWduLW1lLmRl +ghpzZXJ2aWNlLmJ1bmRlc2RydWNrZXJlaS5kZYIdaW50ZXJha3Rpdi5idW5kZXNk +cnVja2VyZWkuZGWCG2hlbHBkZXNrLmJ1bmRlc2RydWNrZXJlaS5kZYISYnVuZGVz +ZHJ1Y2tlcmVpLmRlghNidW5kZXNkcnVja2VyZWkuY29tMB0GA1UdDgQWBBShj278 +UTgPVPGLerrSzyQ18D831TAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw +DgYDVR0PAQH/BAQDAgWgMIIBCQYIKwYBBQUHAQEEgfwwgfkwOgYIKwYBBQUHMAGG +Lmh0dHA6Ly9kLXRydXN0LWNhLTItMi1ldi0yMDE2Lm9jc3AuZC10cnVzdC5uZXQw +RQYIKwYBBQUHMAKGOWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY2dpLWJpbi9ELVRS +VVNUX0NBXzItMl9FVl8yMDE2LmNydDB0BggrBgEFBQcwAoZobGRhcDovL2RpcmVj +dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwQ0ElMjAyLTIlMjBFViUyMDIw +MTYsTz1ELVRydXN0JTIwR21iSCxDPURFP2NBQ2VydGlmaWNhdGU/YmFzZT8wgfsG +A1UdHwSB8zCB8DCB7aCB6qCB54ZubGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l +dC9DTj1ELVRSVVNUJTIwQ0ElMjAyLTIlMjBFViUyMDIwMTYsTz1ELVRydXN0JTIw +R21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3SGNWh0dHA6Ly9jcmwu +ZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3RfY2FfMi0yX2V2XzIwMTYuY3Jshj5odHRw +Oi8vY2RuLmQtdHJ1c3QtY2xvdWRjcmwubmV0L2NybC9kLXRydXN0X2NhXzItMl9l +dl8yMDE2LmNybDCB5wYIKwYBBQUHAQMEgdowgdcwCAYGBACORgEBMIG1BgYEAI5G +AQUwgaowUxZNaHR0cDovL3d3dy5kLXRydXN0Lm5ldC9pbnRlcm5ldC9maWxlcy9E +LVRSVVNUX1BLSV9EaXNjbG9zdXJlX1N0YXRlbWVudF9kZS5wZGYTAmRlMFMWTWh0 +dHA6Ly93d3cuZC10cnVzdC5uZXQvaW50ZXJuZXQvZmlsZXMvRC1UUlVTVF9QS0lf +RGlzY2xvc3VyZV9TdGF0ZW1lbnRfZW4ucGRmEwJlbjATBgYEAI5GAQYwCQYHBACO +RgEGAzCBiQYDVR0gBIGBMH8wCQYHBACL7EABBDAHBgVngQwBATANBgsrBgEEAaU0 +AoEWBDBaBgsrBgEEAaU0AoFKATBLMEkGCCsGAQUFBwIBFj1odHRwOi8vd3d3LmQt +dHJ1c3QubmV0L2ludGVybmV0L2ZpbGVzL0QtVFJVU1RfQ1NNX1BLSV9DUFMucGRm +MB8GA1UdIwQYMBaAFCGvaiaYcekmCtXboYz326adpSVcMIIETwYKKwYBBAHWeQIE +AgSCBD8EggQ7BDkAdwCt9776fP8QyIudPZwePhhqtGcpXc+xDCTKhYY069yCigAA +AX/AJVyRAAAEAwBIMEYCIQDHYr2J0KhX9Qw2DZcpukdrMtTPrSkQTG3WQ+9TJbfv +fAIhAIsgHLLnR3DBqqikp7qjOg2ge3rhLKae4EcfJ5OYH3bzAHcAs3N3B+GEUPhj +htYFqdwRCUp5LbFnDAuH3PADDnk2pZoAAAF/wCVdigAABAMASDBGAiEAv5hGLqwU +NARYcml1ScV/JumKME8Gh/+KFLd76xi69cICIQC5aK3LduJomzCkxLZecyDhIghV +zNwsNbB1XQY9TBepLAB2AOg+0No+9QY1MudXKLyJa8kD08vREWvs62nhd31tBr1u +AAABf8AlXP8AAAQDAEcwRQIgPkK0U2XQA0b4SS89AFPNRFo3TdcdNm90Z8015UBb +MpcCIQDMUkJimfKU5IvKyO7D8ibgsJSHE+NASD15Pixf8L25+wB2AFWB1MIWkDYB +SuoLm1c8U/DA5Dh4cCUIFy+jqh0HE9MMAAABf8AlXY8AAAQDAEcwRQIhAKX9i888 +VPeAjIztEESfZ8Izy051gTTSl9D1GBH7Z810AiBrBtrXTu+V39yPAfIK7YBpgsvS +C0vB8MCe1Q1nR5KK+gB3AHoyjFTYty22IOo44FIe6YQWcDIThU070ivBOlejUutS +AAABf8AlXTsAAAQDAEgwRgIhAPeWQ8o/CaW5HpEA3UkszILAlsKnixEHRDGFMl8q +GN+rAiEAmBQ7TBG8Xgru2e5c3GdUXecmDVjwI/G1ZthSFmMvNlgAdwBvU3asMfAx +GdiZAKRRFf93FRwR2QLBACkGjbIImjfZEwAAAX/AJV4ZAAAEAwBIMEYCIQC1HlH1 +MYjMp1pvFYmNXafGXvJ6oIiGiUtd1kHRtCt76QIhALj7dbiBFP4b9elj5kYMDPT0 +PAoflviX/f8klXtTFG27AHUA6H6nZgvCbPYALvVyXT/g4zG5OTu5L79Y6zuQSdr1 +Q1oAAAF/wCVhLwAABAMARjBEAiBszHijSAeBf3cec2LQgegrIJ3I4P9EQX28ZQ4S +yTvmDgIgMvxYYvRNu7+RY4AnFAZAhN9eX4WwXLrEdPOPhhxs0TwAdAA1zxkbv7Fs +V78PrUxtQsu7ticgJlHqP+Eq76gDwzvWTAAAAX/AJWAHAAAEAwBFMEMCIBhApuJO +EqEb0oq/6VWxM6jz2dbD7+ZjBDuvOioO/Cf9Ah9/QAHSUTU043F7VdV/REB12XGY +a63YqZJJeeIgTuZDAHYAtz77JN+cTbp18jnFulj0bF38Qs96nzXEnh0JgSXttJkA +AAF/wCVfSQAABAMARzBFAiEA4036r9QS+ngcG4FMBUc1Z36BywbwF00pHprDpNMQ +KhUCIGzTcK+3DnLBJOxwScoow/EJq39GmZV1sZz93r/d5qNdMA0GCSqGSIb3DQEB +CwUAA4IBAQBZvYmRmtu1gQLCA+QqN5C7ftPr0ioULQPBsmX8gHmQ1iHPrBrC99Ef +UD0//QB8V/aWqbt1NNdFXXEslN0V591m13uF7cp27SUjxNFwkPG2oypoqNM42I0U +136fs26VzFbLe/MLNLTiiTkIp4HfSnLoactqvWapU9X6pzRk3CoKbaGHkPpIn467 +6uq08dss4+W9DROLZynwuswtxhLdk4pi82mnIs0t8A+ZOHwKPrQ9zi8Mtc7T9xPY +PuGpbWQMTGKzCOjki81OvuD0ZU//hfHIM8Nh3Fb1LQ3ZRMudYdW+noIaW4FQRY2W +Pr1qU9fkcHml0htVexwhF6m1x5HZ332p +-----END CERTIFICATE----- diff --git a/src/tests/data/x509/ocsp/mychain_creater.sh b/src/tests/data/x509/ocsp/mychain_creater.sh new file mode 100644 index 00000000000..e7010600acb --- /dev/null +++ b/src/tests/data/x509/ocsp/mychain_creater.sh @@ -0,0 +1,224 @@ +#!/bin/sh + +# Helper script to generate a certificate chain +# alternative certificates might sign the OCSP responses. +# +# (C) 2022 Jack Lloyd +# (C) 2022 René Meusel (Rohde & Schwarz Cybersecurity) +# +# Botan is released under the Simplified BSD License (see license.txt) + +if [ $(date "+%y%m%d") != "220922" ]; then + echo "You should use a time machine to run this script..." + echo "Use libfaketime to set the system clock back to the 22nd of September 2022. This recreates the certificates with the same timestamps as used in the tests and saves you from re-setting the validation reference dates." + echo + echo "Like so (path is for Ubuntu, might vary):" + echo " LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1 FAKETIME=\"2022-09-22 12:00:00\" $0 $@" + exit 1 +fi + +set -ex + +PREFIX="mychain_" + +ROOTkey="root.key" +ROOTcsr="root.csr" +ROOTcert="${PREFIX}root.pem" +ROOTindex="root_index.txt" +ROOTconf="root.conf" + +INTkey="int.key" +INTcsr="int.csr" +INTcert="${PREFIX}int.pem" +INTindex="int_index.txt" +INTconf="int.conf" + +DELRESPkey="int_ocsp_delegate_responder.key" +DELRESPcsr="int_ocsp_delegate_responder.csr" +DELRESPcert="${PREFIX}int_ocsp_delegate_responder.pem" +DELRESPconf="int_ocsp_delegate_responder.conf" + +DELRESPnoOCSPcsr="int_ocsp_delegate_responder_no_ocsp_key_usage.csr" +DELRESPnoOCSPcert="${PREFIX}int_ocsp_delegate_responder_no_ocsp_key_usage.pem" +DELRESPnoOCSPconf="int_ocsp_delegate_responder_no_ocsp_key_usage.conf" + +EEkey="ee.key" +EEcsr="ee.csr" +EEcert="${PREFIX}ee.pem" +EEconf="ee.conf" + +# +# Create the Root CA +# +cat > $ROOTconf < $INTconf < $DELRESPconf < $DELRESPnoOCSPconf < $EEconf < $CAindex + elif [ "$subjectStatus" = "revoked" ]; then + formatted_currentdate=$(date "+%y%m%d%H%M%S") + echo "R\t${formatted_enddate}Z\t${formatted_currentdate}Z\t${serial}\tunknown\t${subject}" > $CAindex + else + echo "Don't understand OCSP response status: $subjectStatus" + exit 1 + fi + + if [ "$stapling" = "no_staple" ]; then + staple="-resp_no_certs" + else + staple="" + fi + + # generate an OCSP response using the just-created certificate + openssl ocsp -issuer $caCert -cert $subjectCert -reqout $ocspReq -text -no_nonce + openssl ocsp -reqin $ocspReq -rsigner $responderCert -rkey $responderKey -CA $caCert -index $CAindex -ndays 30 -respout $ocspResponse $staple -text +} + +# (Malformed) OCSP response for Intermediate signed by Intermediate itself +create_ocsp_response $INTcert $ROOTcert $INTcert $INTkey "valid" "${PREFIX}ocsp_for_int_self_signed.der" "no_staple" + +# (Malformed) OCSP response for End Entity signed by Root certificate +create_ocsp_response $EEcert $INTcert $ROOTcert $ROOTkey "valid" "${PREFIX}ocsp_for_ee_root_signed.der" "no_staple" + +# OCSP response for End Entity signed by Intermediate certificate +create_ocsp_response $EEcert $INTcert $INTcert $INTkey "valid" "${PREFIX}ocsp_for_ee.der" "no_staple" + +# OCSP response for End Entity signed by Delegate Responder of Intermediate certificate +create_ocsp_response $EEcert $INTcert $DELRESPcert $DELRESPkey "valid" "${PREFIX}ocsp_for_ee_delegate_signed.der" "staple" + +# OCSP response for End Entity signed by Delegate Responder of Intermediate certificate that does not have sufficient key usage flags +create_ocsp_response $EEcert $INTcert $DELRESPnoOCSPcert $DELRESPkey "valid" "${PREFIX}ocsp_for_ee_delegate_signed_malformed.der" "staple" diff --git a/src/tests/data/x509/ocsp/mychain_ee.pem b/src/tests/data/x509/ocsp/mychain_ee.pem new file mode 100644 index 00000000000..23b036320d4 --- /dev/null +++ b/src/tests/data/x509/ocsp/mychain_ee.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDXDCCAkSgAwIBAgIUQCu8J4C8/lLpesu+yxmEcgboaKwwDQYJKoZIhvcNAQEL +BQAwOTEZMBcGA1UEAwwQTXkgT0NTUCBMb2NhbCBDQTELMAkGA1UEBhMCREUxDzAN +BgNVBAcMBkJlcmxpbjAeFw0yMjA5MjIxMDAwMDBaFw0yMzA5MjIxMDAwMDBaMDsx +GzAZBgNVBAMMEk15IE9DU1AgRW5kIEVudGl0eTELMAkGA1UEBhMCREUxDzANBgNV +BAcMBkJlcmxpbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJjSgFM7 +veKLCerCj8LbDH2eyE/wsgt75EugNON2xcuxdnZKXl9kQP/lq2tjQF9VUKvUr7C0 +4BDTyXjg+0RnH8EUp2fooDsrJu9k1i+lDWFtAYAYYrYYxGMFzCC/h+GBD0FCFBwL +3gpZwPitoDga6jtPbtv/RwFMuPy7b0KUpNMkVAeaT/KVmqc/l+SgLqDEZciiMcaC +GG1rkMnElR7c/0lg5xNITXS1t1Z9bHbpO7lH5xDoFcSTEhOcDdFkN923sbfTT3m9 +4oKHYFDUSoAc+Y2jbwbDK+g6MyCwIiwdyUF+Kgv1fdacWxZMmr2aOA5CX/1+ZeX1 +97ameyxkyA2DZfsCAwEAAaNaMFgwHwYDVR0jBBgwFoAUm5xBswA2BO40FM22Q/5K +RR6PtNMwCQYDVR0TBAIwADALBgNVHQ8EBAMCB4AwHQYDVR0OBBYEFKnd1WBRL4H/ +IhofGjvLhtGSNh9lMA0GCSqGSIb3DQEBCwUAA4IBAQCoYIhu/w1Hp2aByrbV7Plm +aUhBJovJHqa3KixgSrV6Td6URaCSGHAiAFj1j0/dqzKL7QHMZYs43JRODuABAjsn +SktrpuoA+FILuSZXMm3UFEqNNJzFTwZLC3lSpxT1zvQ4PDgx4xFTMi9pyvGnDHjt +jkPLuLfjgI5PShcIB0Hd6yS07pBFdg/Dr3fCSU7OBAC4o44ubUa5kASvX35zjWoj +NulehNs+aa6Fm7qqSt4mz24qvnOG3SyYpkNKeu/FQjaKXV35A0tGN2ibEHSn0JBp +rZqzfegU5UuZrpGs3xUVeIH+rQdW8uBlllXP38djJ7mLb/3b1vLWakljPqAOOlsm +-----END CERTIFICATE----- diff --git a/src/tests/data/x509/ocsp/mychain_int.pem b/src/tests/data/x509/ocsp/mychain_int.pem new file mode 100644 index 00000000000..f9bd11adac1 --- /dev/null +++ b/src/tests/data/x509/ocsp/mychain_int.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDcTCCAlmgAwIBAgIUP4pG/mdA98wntpdjLKKDv50V4twwDQYJKoZIhvcNAQEL +BQAwODEYMBYGA1UEAwwPTXkgT0NTUCBSb290IENBMQswCQYDVQQGEwJERTEPMA0G +A1UEBwwGQmVybGluMB4XDTIyMDkyMjEwMDAwMFoXDTIzMDkyMjEwMDAwMFowOTEZ +MBcGA1UEAwwQTXkgT0NTUCBMb2NhbCBDQTELMAkGA1UEBhMCREUxDzANBgNVBAcM +BkJlcmxpbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAODHbsMfDefJ +fryKz8fY8AfCE8uAr5CzJ2gXQcIQd0rBJTPQxaxZ950+QbSfPAPAeFa1OWRc/Xby +3DXvtQ3yt879mvxAvsdvlUYOsOOi8b9tap3vLVSc668BJwByNwBZmAF6ByKsC4Yj +wwH7rfekE2KU89LzH0wWDJOybo/N62kXuzt23dO4uUXJat6ZlEghmzhAzHyfFdeD +H8V/7x7c6iQBFz0NSCeo/gzFzVNO0jKbyvScQmfLOvbwTm91nXPs6MWICzsNOliJ +vtfkJsqheq7dVX9HdLfh/1tdFx1WaPhtVf3VTolPGTs9w7hh6uaWsHZpFTbiK0Mc +DeNQkoX1ikcCAwEAAaNyMHAwHwYDVR0jBBgwFoAUk/nNvUAjr/JUj93s2WMZm25Y +zHUwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUH +AwkwHQYDVR0OBBYEFJucQbMANgTuNBTNtkP+SkUej7TTMA0GCSqGSIb3DQEBCwUA +A4IBAQCN1CeGhYZr/ghM45N0auoTJ+U5lAah3g6c7lGS6x6+XyaueI+Pxy0wC/1C +UCjEbErD44utxk+816uUnhmUOqSrDejV0xxPnQYokziOw8flLKm8/Y5ngQ14VshX +oJZMdaQywe3Je34b6t/BZZaZx/dtXHtkDTBdBgOXiv/O7JMDqEQzFb8uC3MPpM1b +TtC58Rtvh8nhy5ieig/uaXBIwcyc4ujlllzjmwV1yNg6iY1QVj3GMRsxvI1ZFkaZ +eZnbFNqwx5ZLL61c/cBV8pG47DKSqBhV9osWCK/vc6WHYcwyYBJ5YIuykl/zs41o +knoudoJ3BFGS9PaFZZQCA78WnYsd +-----END CERTIFICATE----- diff --git a/src/tests/data/x509/ocsp/mychain_int_ocsp_delegate_responder.pem b/src/tests/data/x509/ocsp/mychain_int_ocsp_delegate_responder.pem new file mode 100644 index 00000000000..6e65f1cf071 --- /dev/null +++ b/src/tests/data/x509/ocsp/mychain_int_ocsp_delegate_responder.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDgjCCAmqgAwIBAgIUFkhEhhLqatMopup9/noUhdFx5EkwDQYJKoZIhvcNAQEL +BQAwOTEZMBcGA1UEAwwQTXkgT0NTUCBMb2NhbCBDQTELMAkGA1UEBhMCREUxDzAN +BgNVBAcMBkJlcmxpbjAeFw0yMjA5MjIxMDAwMDBaFw0yMjEwMDcxMDAwMDBaMDox +GjAYBgNVBAMMEU15IE9DU1AgUmVzcG9uZGVyMQswCQYDVQQGEwJERTEPMA0GA1UE +BwwGQmVybGluMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3N3l6YZy +pFShnQWmMyplXu3JaDjUxlrNwEs6Dn5flUC0eAZVN+uaXws+tG//wV487n+OXnyh +Zgz1Mt97J1wYhw7R9bQakPrmkYztrTmTKemS70sWjrsH0Od/S851sv3qAGylWiKb +1n0SawRPo6T5bvYADwwGESRKmWwOwPIv2KdsZ3kUhN9aPj06CMVJIRYVennZRt4X +4tcpgpB/eBp4/iEmfe3BzrFgf9YJG4qcbM84lULGLOnVNuUbbEIlBe75U71OR8dV +El65LSMAVDQovjTV3mdQcLQNOiNnBlNDDaJEi590ki59qnFbJO0Zsf7a/rpHz/4J +LqK8b2by8KoFpwIDAQABo4GAMH4wHwYDVR0jBBgwFoAUm5xBswA2BO40FM22Q/5K +RR6PtNMwCQYDVR0TBAIwADALBgNVHQ8EBAMCAYIwEwYDVR0lBAwwCgYIKwYBBQUH +AwkwDwYJKwYBBQUHMAEFBAIFADAdBgNVHQ4EFgQU7cI/PXYEpWH9l6Bnu36V6Nzv +/MowDQYJKoZIhvcNAQELBQADggEBALv6KUJ0I/Kd/4ofDQHcgrHOe3u26zs1LC5J +X9ZMoLRwN2LbzwWogIg3DEYqLAr0whpiDDcueVQVxK0rYrI1kWAZYi/wkmdOI5D7 +GNtHxdMty62XgOLb4LwGmEMQ7SLH2GgEAgKjJAIVJ5TMlxH8NV2/hrQhmXDpkZc/ ++6I881LDW2273p8vKXxYnI1EFTdCVa9XnNJr3U+yhC9plf+gSr51iXyQf9MPdZ91 +fg187LQkn6oIRtKL7yZMAajemcTkU8avoF1+EX01Z5nu/v2Hgtp2VFDKvCrud+e/ +iKFLYBlsnqfNyZt4n3PAxDP5ziZ5adH2ELCvPDkJ3nneAhvP5So= +-----END CERTIFICATE----- diff --git a/src/tests/data/x509/ocsp/mychain_int_ocsp_delegate_responder_no_ocsp_key_usage.pem b/src/tests/data/x509/ocsp/mychain_int_ocsp_delegate_responder_no_ocsp_key_usage.pem new file mode 100644 index 00000000000..0cac96b7b3f --- /dev/null +++ b/src/tests/data/x509/ocsp/mychain_int_ocsp_delegate_responder_no_ocsp_key_usage.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDbDCCAlSgAwIBAgIUXMtIzKLTZ3oKE92bXWKGdS16GCYwDQYJKoZIhvcNAQEL +BQAwOTEZMBcGA1UEAwwQTXkgT0NTUCBMb2NhbCBDQTELMAkGA1UEBhMCREUxDzAN +BgNVBAcMBkJlcmxpbjAeFw0yMjA5MjIxMDAwMDBaFw0yMjEwMDcxMDAwMDBaMDox +GjAYBgNVBAMMEU15IE9DU1AgUmVzcG9uZGVyMQswCQYDVQQGEwJERTEPMA0GA1UE +BwwGQmVybGluMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3N3l6YZy +pFShnQWmMyplXu3JaDjUxlrNwEs6Dn5flUC0eAZVN+uaXws+tG//wV487n+OXnyh +Zgz1Mt97J1wYhw7R9bQakPrmkYztrTmTKemS70sWjrsH0Od/S851sv3qAGylWiKb +1n0SawRPo6T5bvYADwwGESRKmWwOwPIv2KdsZ3kUhN9aPj06CMVJIRYVennZRt4X +4tcpgpB/eBp4/iEmfe3BzrFgf9YJG4qcbM84lULGLOnVNuUbbEIlBe75U71OR8dV +El65LSMAVDQovjTV3mdQcLQNOiNnBlNDDaJEi590ki59qnFbJO0Zsf7a/rpHz/4J +LqK8b2by8KoFpwIDAQABo2swaTAfBgNVHSMEGDAWgBSbnEGzADYE7jQUzbZD/kpF +Ho+00zAJBgNVHRMEAjAAMAsGA1UdDwQEAwIHgDAPBgkrBgEFBQcwAQUEAgUAMB0G +A1UdDgQWBBTtwj89dgSlYf2XoGe7fpXo3O/8yjANBgkqhkiG9w0BAQsFAAOCAQEA +V17YOra6yl0wTjt6QbQDXxm5m02CpW3EZs8x1M8yZadWXK9dJ6mo7vetqF3nnOzd +TxesfAWigkrSZjR7HHHXXO5S9OjFLEyft+Xbx9+t8216Lbqk7WierREz1C21yCpn +B76DiQRXqY2lEm1cpgkZeSc+SSfoN4oOyXCb/r+sgEebXGHrQhqgdFAWq3BmF6U+ +VyIXG7PiJGoTmlJ9gfkr0+Y0MxNpTIr6OPc9H6+N4mYPhcj/9emTcj6R+0PvfAZC +GRc8U3fCW3UdPOTE28f86ZvMduavCZCSU4m74nZnzY6eKR83KNCjB0gJzYhwqcRm +f9I5C+O6SocALwTGGYkMbg== +-----END CERTIFICATE----- diff --git a/src/tests/data/x509/ocsp/mychain_ocsp_for_ee.der b/src/tests/data/x509/ocsp/mychain_ocsp_for_ee.der new file mode 100644 index 0000000000000000000000000000000000000000..1b6b837960e6d15f6df011bd017fccab4e452caf GIT binary patch literal 512 zcmXqLV*JC!$grS^@vA`-<3~15Z8k<$R(1nMMwTYVM?j%_293uTS{qmzN*ai>F^94+ z^9cA>D)>7G2PpXDCnx48I6E408*qZ8*o2u}Tn+gRctIR?9yX`cqMXb;34S93BO?P# zBO^lt5Qs7;Hz+mm1!`f{=3{1(Vr5_vxwrn7+o_4ncMG-3)l|5b`A)0kc*r6$dyeB~ z1~Zm-CL(9IIsfx=mFwSfnMuS!dyjg3 z()FXQO!<55zOR05w0y2-^Qlv*0m3X>8b7E1U%PTmHp9i7`@gO;dJCjqpS2*T zr+I;MKr^#4r_F|EK|bYqiD_RJxxZP%`FY3IzF#SOKH9C8vDx(Of=GzvhL9}C9Q zc{0oltEP0UZ`t!K<(K_caqWFhiXGCI)2)rxvcD9cx_yS4xK7wLPrcR_qx;*l=Lkt@ ip7@t{rTbMfB> zGnlcwGZ8tv&H0~~t6cw<%S<8;+I!R+_WTQaS#^5fX~~u%wig*|8W=EaSs(}zG6Y%< zvzV8SQ>)FR?K>|cBR4Apa}y&YL$Gsj;BD2(E4>66i#vS;lahn#XTI?BdUkxV?Z#I# z%k~HE-I!>g8PsDjyX{EZM6c-8!E+AWd+PC3)wnbH(38Efmo7bfSvZ@e=b6D;{%9W2 zmAc`J6~o>NE$oWWh`k}kV)p0QVctU7d((d_T-@CA?LYT}Z7yftA8huj+d1DyENJV> zW4zP-vhRfcsr$QNm37xEt=r$GPuJU-SH9M8lP>##_H&11KF)SnqBG}GjjqW-`?V_n zB3(pYCf~Q+>tyiJ{Pfo8dAE+`_g;IvUhzh@>Gvnz8LY7Bn$;88k7s0dbQ-6I0d#W+p}^ zCJ`|Ymo}kSS(i1Iy{i3JCDMAa@QEidUXg>(z|v6CKpc`L1Yl{xCqFqcN5R<C3jFB_RvvvMw{$N{xGa zGQ;A^v8b~Lysh}^;-@-nsbC8=e?2Rn+ipw#|ATQh@9O*FY8IyPd^NgXtsW!M&Uf+a z7O4rpo=xm|yVi2D=F3U%y~X-=vtM{#?|rUx)8AJNIZLCIW?!on%4YFjyyR!zHwJzl zHbE7ynK^t1KIz|Bo|9fF(sDn_&en?KsHdWsXjSD+w|nA`u4^_;sIQQ!_@}5=`}W|u zjS2PFIHkMhU2!!KQ&*WG#6)AC$<=%50R>xl zt(4Q*f}MF6xpdDjnWR^{sxVsRt>ngkxBl&NKmU(YZ_%Fov`-&au`Xv~W@KPo+}L1H zXCM!ZJy~TI2?MbPteFj*t!0H-m<$*UxIt?9Sy+JSqsc%R#8GA8G2mk3fRq@_oCf@e z(u0+SiIu@X7Nm-gMT|w{?IC;HGM1%@f2S`<-(5HL#hv$mP9aAUEI01{rRh|n{Auq0 zE_q(YJ53wURab9&ZEdQf=NW&^XTg>N^Q7D7Sywc4nDe-4>2Q5HB$dQtu2&f%dStD3 z(k9c136e?rA10;yDNpzXhuaB7d%yYlDzI~IB;NaoC2e)i<-iuuI{ju`(ur&gJH@teSg^){BA_bTr(NuPhJ F1ptgK8f^do literal 0 HcmV?d00001 diff --git a/src/tests/data/x509/ocsp/mychain_ocsp_for_ee_delegate_signed_malformed.der b/src/tests/data/x509/ocsp/mychain_ocsp_for_ee_delegate_signed_malformed.der new file mode 100644 index 0000000000000000000000000000000000000000..6b9fe3bea232ee57d01e6f76ab016b2725ec6919 GIT binary patch literal 1401 zcmXqLVlCxjWLVI|nrG0&n#IPc&Bn;e%5K2O$kN0b4HODDXgsmd#=y!@%0PmRIh2K& zN6@!Y!QVMJKp`l#xFA0-CAG+q+kg`!$0p3=;%dlmzzgEA^RPLk7Ug8-N$?vP7#SH@ z8W|ZHfIyT%xk0IcFHj4sHXk#S6e|OZ$i4Nq+)hnozFVkOuBO7h%y(KP$3qs8*>fB> zGnlcwGZ8tv&H0~~t6cw<%S<8;+I!R+_WTQaS#^5fX~~u%wig*|8W=EaSs(}zG6Y%< zvzV8SQ>)FR?K>|cBR4Apa}y&YL$Gsj;BD2(E4>66i#vS;lahn#XTI?BdUkxV?Z#I# z%k~HE-I!>g8PsDjyX{EZM6c-8!E+AWd+PC3)wnbH(38Efmo7bfSvZ@e=b6D;{%9W2 zmAc`J6~o>NE$oWWh`k}kV)p0QVctU7d((d_T-@CA?LYT}Z7yftA8huj+d1DyENJV> zW4zP-vhRfcsr$QNm37xEt=r$GPuJU-SH9M8lP>##_H&11KF)SnqBG}GjjqW-`?V_n zB3(pYCf~Q+>tyiJ{Pfo8dAE+`_g;IvUhzh@>Gvnz8LY7Bn%J7&I{#0CA2%6H~|nW+p}^ zCXtxa9%mL^POst;zB@ZMsjXDENzK ziSwc)2@n?;N#?MKq9C~$G%+e6yOa?e(fkZRaV{oMjNiHY^krMol8}XSS(h1WrN+HI znPG9|Sk&1A-d22d@lzePRIr7bzn&G(ZMP-=|G_w$clCX7H4D>tz8c-HR*#Wr=eziI zi`0Z)&nEV~U28d6^W~)X-eP^b*)Ke=_dZv;>F+CsoTX7pv#-?(WwZD%Uh*^V8v{QN zo1lu<%pAT0pY(4m&q=QoX}KR|XKTfA)KgJRw5sx^+dc6|*EO3a)K^GV{8Ln`eS7fS z#)SH7oYGx$a?V>!bvmZ=@~YWW=^Q6j)^|UH_xiaX4;6~rsjJKoVxqCnspZ~|Hw`fm(+NTezSeG*~GcqtP&Nj$2 zkOxMdtTKy)fmj39yavwIvcfD(1`Gz=AhrA~EX+*o4F>#(a)OnGiIu@X79`8ZBE}-} z_K>}88Ozedztb0_@2;Er;?Da&r;wuqmhZyjZdh&Gbt=}t&$`NS3v;~W&e^_AOLLE; zoj1I4-Y9i>Sj_rZ^_45$eP6pG_W7JQcm2iJ)UYmU^175}QY}+>J?34|mlsEMeCBU| zdi(hOwV!jVbayRzn=x;#pz#&mttYh7+4nVfvV^bfT`H6tvy4-+Qr*r|{e^iK-^qg6 z|MstGaGxEM_}WQoK}mqv>Vh=!rFP*;;?kQRsbmSy3aV}VseSpGiLr2|PuDMt@3!*m zdmp9ox1RX__2uLuyNSP@-`B7?Ns8M9mmi8Qm9=?t7G2Pg#P=a(orI~sBuaDrsmgqd7i4fzdtK^%4-HmB60oXk84ej@`TBLhn# zBSQlah%zWQC^hf}YGKvpV`h?KWndAxxBiyfsfo;Y3$@DCRJfP)&#jNyI^Wk9xzNe?c#+PVYM{*;2&zB4bSh1BNXN1R+9(K+9nk z^RjVjwRyCC=VfH%W@TV*Vq|0xXFV7a$saT8Qc{C$waeD4Pl`0eEnm1_y7BgwX$t4e zh6~fG{v=y8c*&h73+TR2fudyJqI)nWv7M-g0=^e!23( zdf65J-#;Xs=sLRZ^VHrHli70;+{1sgZJKhd?p>*$`y5-_LyVseEDL9uJx9Fr%Jv&c zC-)}#N6Y+ljuiPD`|0_r2JN%rL2uGGHhtK=a7SXJ=7-WBM;7MH{PF8S*rFwi^p4rh zELeL!-#&QfY8f|$41u2b9|E&~TYdQdZSBH?3ok4$etS*5Zq1q;=iaHW_byhOeBp!X kmKWZ0G{p{Yc|AA%?9y_MA7(6bEt6C#=7*l#|K)H#00Di@<^TWy literal 0 HcmV?d00001 diff --git a/src/tests/data/x509/ocsp/mychain_ocsp_for_int_self_signed.der b/src/tests/data/x509/ocsp/mychain_ocsp_for_int_self_signed.der new file mode 100644 index 0000000000000000000000000000000000000000..5a1867ca1f1073ed94f9893564ff22f4e7002177 GIT binary patch literal 512 zcmXqLV*JC!$grS^@vA`-<3~15Z8k<$R(1nMMwTYVM?j%_293uTS{qmzN*ai>F^94+ z^9cA>D)>7G2PpXDCnx48I6E408*qZ8*o2u}Tn+gRctIR?9yX`cqMXb;34S93BO?P# zBO^lt5Qs7;Hz+mm1!`f{=3{1(Vr5_vVf2ct@tm^lWZSm6LEm|-#S8i-U11TK{PXNy z2j%sjLi+E%xtT0EJ1^o)DU*nOm)pN|hwo?9w@pvhS=79LuIQsX4Gb8zED(eU83HYb zS~O2aC^JuP?~#bU1nb^1T-eKTi1Y_xptFy@AHt zz8OR=U!W-37~alXla%NJNN5$`-7!yw{5f)sy`JK<-(@ys;>TR7<7-I8TV5OzE zi&yEa|7NddpYd|DnRWO5H@i!xKUnNiVczVl${)bLO|-J#kn;it&+R2G2X)b%Y-j4Yu+e+ lTIc_F&4FnjRu;y8neyt~nolCmzqcorB|Kc?+cvM$0sy43&Zz(Z literal 0 HcmV?d00001 diff --git a/src/tests/data/x509/ocsp/mychain_root.pem b/src/tests/data/x509/ocsp/mychain_root.pem new file mode 100644 index 00000000000..192d71b8094 --- /dev/null +++ b/src/tests/data/x509/ocsp/mychain_root.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDsDCCApigAwIBAgIUWMiulqiEbnZwrB5iI7tL724j7qswDQYJKoZIhvcNAQEL +BQAwODEYMBYGA1UEAwwPTXkgT0NTUCBSb290IENBMQswCQYDVQQGEwJERTEPMA0G +A1UEBwwGQmVybGluMB4XDTIyMDkyMjEwMDAwMFoXDTIzMDkyMjEwMDAwMFowODEY +MBYGA1UEAwwPTXkgT0NTUCBSb290IENBMQswCQYDVQQGEwJERTEPMA0GA1UEBwwG +QmVybGluMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAukN6nemvQcIX +zq+/DKFsJWjQif6sTP2zXfZtpw445LagOt8T9fGFgv6BSNTFp/TMRatQMAfZteH8 +ExzInhOIatwZgOKfG5tE+OH+tOuo9JrgWQRMGrhCV4fClDOv3sPAvduYm00muazD +HeusESr/ykoA3HmJpS62EeOvMsY991TGSoTUSPLXJOyVTT5EcHdLrmosIBNx4nN9 +8xN5ENbhz/lZa3z1+NEtruMhDY5s13POVgpXRCZmgyhl6uCl0HZOYPfoWZwbZfuh +S6U9s0C+JMRjcz1fLyBW2dgsWG6TRSsF6R83DkFQx/9kazfjv/mOqLMw1irT3K0E +wtsxe0aKfQIDAQABo4GxMIGuMF0GA1UdIwRWMFShPKQ6MDgxGDAWBgNVBAMMD015 +IE9DU1AgUm9vdCBDQTELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpboIUWMiu +lqiEbnZwrB5iI7tL724j7qswDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAYYwEwYD +VR0lBAwwCgYIKwYBBQUHAwkwHQYDVR0OBBYEFJP5zb1AI6/yVI/d7NljGZtuWMx1 +MA0GCSqGSIb3DQEBCwUAA4IBAQCdGyFlbaBkoLgLwM2q91VcLUHAp54Gp6vvLavq +p+65K7sdzzFj/6P9p6Dsa0BJ3bXba0pfJ10f9nFHOIFISb0Aptmm34XjBwvUckbb +LYDU7InmyS5aeAIxK9+G7TllLfSslPQJspSxWWZkp3cY4Ys7bGidb1ad620F2cMe +I2c09zhQuySbLDgaCc2Hg9Z3trb6S91Mmk6P+fQMzqq0XkfUOqzmEm2D7lFb3G76 +DI6CouYjoIYndVEN6oVVIcD+01Emxssy60aO6wS5MaM8TbcCdx3ZxYCdKj6YcdRf +XhEN1KonHRKP71iZrlw/W+GfVvt1dJx5V5fqh3mGZVvk7Sc7 +-----END CERTIFICATE----- diff --git a/src/tests/data/x509/ocsp/randombit_ocsp_forged_responder.pem b/src/tests/data/x509/ocsp/randombit_ocsp_forged_responder.pem new file mode 100644 index 00000000000..9381a3eefe3 --- /dev/null +++ b/src/tests/data/x509/ocsp/randombit_ocsp_forged_responder.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID1TCCAr2gAwIBAgIUQi+O3XGTkbU8ihDwXOrV18vdTvMwDQYJKoZIhvcNAQEL +BQAwNzEXMBUGA1UEAwwORm9yZ2VkIE9DU1AgQ0ExCzAJBgNVBAYTAkRFMQ8wDQYD +VQQHDAZCZXJsaW4wHhcNMTYxMTE4MTEwMDAwWhcNMTcxMTE4MTEwMDAwWjB+MQsw +CQYDVQQGEwJERTEPMA0GA1UECAwGQmVybGluMQ8wDQYDVQQHDAZCZXJsaW4xFDAS +BgNVBAoMC0hhY2tlcnNwYWNlMRowGAYDVQQLDBFPQ1NQIEJyZWFraW5nIExhYjEb +MBkGA1UEAwwSRm9yZ2VkIE9DU1AgU2lnbmVyMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAq72Y4p9gCPcoNELOB5i104jhbzbEWfcXhAdXkmufOFFVVveq +HbiGx5GLi46cJATjSQoOL86Jwgp/v0nZukfQFIsWJGjG3eDQnMBGaAH9+SZh+udP +dhcuOvFqvFBkKk6rMIcW0Tqx2ixZUG7275JrqjEyNUjAGA9fRSkGoWyca/P6QCjE +sgAMr82n0XahLi7VVL0v/DcRK7h9slJJbG9UBmHuwPYU5C5Z9iQKCh3JZ3oOgO4d +OuAGXrRm69znN5jlkBxgowJbgPn4Xp2QyAZl2A0/mou3U9WuVGDOUDLRL1UbCv/T +VyX/WyUsAV54apAkxM9Hd5yZermoIZ7gPCv40wIDAQABo4GRMIGOMB8GA1UdIwQY +MBaAFE4W+nR1DcTuZYBY/YXQinJ1Y5PjMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgeA +MBMGA1UdJQQMMAoGCCsGAQUFBwMJMB8GA1UdEQQYMBaCFG9jc3AuaGFja2Vyc3Bh +Y2Uub3JnMB0GA1UdDgQWBBQAUq7vwa5MkBmRG9GuRC7N2F97BjANBgkqhkiG9w0B +AQsFAAOCAQEAzS/5VHLcyTkvnodS18mlkp6r4fKkxhrLR2cyGhQPqwEqkq+l4U8k +UMnem31+XoVHt8nN7N0+aOCna7xhvxzWDQioahG4oSxW3R0FNbO4+HXEBkUqbJQo +JaVxSc4vXYjXgLvvhcSAbwfg7o3jInHszCLWoEpNEWGI0Un/ngJX0E8H374LiPnd +Z7W8bNvqRgbpbZJmrgVfm2T3NIWlMYCB8GqyZMA/uLUtxkv25LTCsCTGKhn/ZQoI +XxCZ4OvZDbxLmGj+5GsgJUHVKVhDomo0fJQh+KrMw+0IyjFVjjyroN6d1A3JPmbL +dKUfISvTkfDCj67y8iASBRCOEs7EB4JzSg== +-----END CERTIFICATE----- diff --git a/src/tests/data/x509/ocsp/randombit_ocsp_forged_revoked.der b/src/tests/data/x509/ocsp/randombit_ocsp_forged_revoked.der new file mode 100644 index 0000000000000000000000000000000000000000..f31272e65f65afa5d07ba56f0aa64609c3fb32c8 GIT binary patch literal 1593 zcmXqLVl(ApWLVI|rf1N^rp3mo&Bn;e%5K2O$kN0n4HObLXkwIG*w|oDXUJ{9$;KSY z!Y0h*;%dlmzzgDV@US_h7Ug8-!35ah0wM-NAQfCZ+#ZR^*{Mav1&PV2hEfI+AW?1} zL4W7q00pO_)Wqz}ymSSh#3Vy$14)n^Gmns4eo=aAiUL?kaAta5YLNuLk%6I^p`oFL zp`if~L>V-;8#J~Ucmo~9s?EpDB*n_WB2xD(>v_?v&3}F@Qb@@WTd12*$PvpTvcfAX znWgsbu4fl5w=Z)|egApIn}tk5%rE9$sB-cyS{%PttlneEr>IK{1(9qq0^5Ql0I_xf zT#b=2%q6^RoLX%jZQpqr8M#>*n41_G88&BJwLZh(zwnN@&gW4ete~Sp|WlB6!xh`y#qWK;{0u;GE+$Z#xg;;x_PbRlb5J9*e17u-rqy!q|CU^RTb5>?>whHhh)*4p*{S|dFRe*EmF*w zcsH|s>0jfUix@TP{I4{B{ID-*#YT@=FTy&%U7hZ&9dIX-MRL+r_ZZVeq2EH+^3>&S z>iSE0i3Ew;TW{ROlMto)!{`mOLZ3~%>BB_V9iMkzI6L=t2g|MKr(e#VnbIz@bm{(> zGd(*Cs@4j;ui14l@S?2x_SgQAWr|@uiPxv499!skC8Otm&5nzzC)zK#vc)#1Eq|w5 zV6MaX&^&XJiqs5Qv*r5)HGBnPb}U%?rTFrS=#N}U<+ZPm_S@aKtjxsB$iTR`aiT$E zpMg9u0mv$|NEnDUh^SAiD}J~A=CXK!+vm@p-TL%o@u3`WQ6wwO!eqc;zztH%&%(mY z#NJ>a4C1J=@ECBhaX`v4W=@D{f*{kHMDml13-mG&C6XSns4EdG%V-cyYcbIrT zwQSD)6|tM9-&I?`Ox@=sa;(8JxTL(YPotae?(C}qU2~bY9dek^Klj$Ji2*L>3pCFJ zWSgt3>WNUj5iGZW_jLb)1^0g^g|9g)B|JAW;#1DQl@6`4SJZWO_a8fv7tel4b;;?d zJ+be?E-;zhR@v*IH)+dsKfw*x)#g?1K7DG-38$Vnnm-!DJf?X5{3Tludaj^*@=4*g z6`Je?Kb(4adiKnEbYbfxt7jXmmj{>C$Zk7a-mvG|loNZb=5G0#anRuE)oD{C@^@Mb zYa97o)D1XLx|C_#vK#iCcWgM<-C#SjqZJC6dQO0oy4lm$IK+f%D^I0_blsq(X7pXek@W*$q`$qn^4FR%ObMED=V3$ z_U^7{7cI9hb4`8!dBvNBOhU{r=3S_A@-A8&zgDc?W67teOAQPdb}SHt2^kr~tmS3n z)N1o+`_9YA$j!>Y+{DPpkYh1nA~(aqmDLILs{NY|JJcLI5E(SveQulB9ufK2nuV3l zm+pFfj?lV$RAaqUn!s`6O+Qb~);2vaAGzvgP?i8M@3*A~Vtu`KW^7pE)e;ykIX~t4 z0`p5-MSEiYr>ebr&$HgK#maGd&XvnD-F|-yqGxGMylSt-%hTP{|M_#Fx8bD8*Po;- zy!vl`CA4oR)0C#g!LhEbxr#C(`)188)W4@{%)BdNZO=)4%ZF+G-Att`oQs#mJFZgY z+CJmivjn~tn>#ipuHGRN6#`e({M~QKAf2oCPEMpG$0& zoIG)>O_#ujm{(V?pT6t&8JKF2gU`U+P~1Qi66t)%k?!n>HMzl4mw}u(FG_L%a?O!b z#{ef+V6IU@_A4VesqixZ#krV3X=e4_8IR^CaD3M=aXQC7W9#LPhxuklBEO5bu!m2| zo^KHt8uopa?2fkM6T7?n=BThd_T=KzKi7GPtA4-d&0X#nM7qUPGLGGSaAD2?w+zO= zKh+X{J@+pY*R%SVwI?7&%Wt(oyVymmjkk0n1MrFz*~GxJ{_OILWefH6u7>Q@|6?wwy`y$hkY`SQ2wUR21K&iR=tX{0;o_1#nO?=$ z@J`n10bAUbwAXi@o6mSUK_+1_Q*^`6A8~UhoM216!D~OOdwcNJbs-7o0*o%|he~t( zzZ|amKU!6XF|HzOg36Ke?&Wi4R_$D&IPZat_K(X<%!~|-iyJ2zH1-+D0~3I(GK++P zSc8b4*sqdO-Xrf)8zTO;Ug#<+O`iN1T<*vUvoIMj7;uBs^0TloGqE=q2!lAPEIbBW zY#fk+is<8C z-iWVeLyk3A20yF+Gor*d(`3(S1* z`X=ul?-?2Yo@6ViI$qU`a9)&UQZq&I$Eq`j-*TKX4DGX7z2M&5E4(M|(oUBwl~>fh ZJn_S!{&k-|DG0F&^a-6i!roNu1pu8}I!6Ei literal 0 HcmV?d00001 diff --git a/src/tests/data/x509/ocsp/randombit_ocsp_forged_valid_nocerts.der b/src/tests/data/x509/ocsp/randombit_ocsp_forged_valid_nocerts.der new file mode 100644 index 0000000000000000000000000000000000000000..1abef32ff886d0623a840bdd82e843116f838d38 GIT binary patch literal 581 zcmXqLVshkSWLVI|WM$CAWX8s+&Bn;e%5K2O$kN254ir*0Xkz4E*w|oDXUJ{9$;KSY z!Y0h*;%dlmzzgDV@US_h7Ug8-!35ah0wM-NAQfCZ+#ZR^*{Mav1&PV2hEfI+AW?1} zL4W7q00pO_)Wqz}ymSSh#3Vy$14)n^Gmns4eo=aAiUL?kaAta5YLNuLk%6I^p`oFL zp`if~L>ZJC6dQO0oy4lm$IK+f%D^I0_blsq(X7pXek@W*$q`$qn^4FR%ObMED=V3$ z_U^7{7cI9hb4`8!dBvNBOhU{r=3S_A@-A8&zgDc?W67teOAQPdb}SHt2^kr~tmS3n z)N1o+`_9YA$j!>Y+{DPpkYh1nA~(aqmDLILs{NY|JJcLI5E(SveQulB9ufK2nuV3l zm+pFfj?lV$RAaqUn!s`6O+Qb~);2vaAGzvgP?i8M@3*A~Vtu`KW^7pE)e;ykIX~t4 z0`p5-MSEiYr>ebr&$HgK#maGd&XvnD-F|-yqGxGMylSt-%hTP{|M_#Fx8bD8*Po;- zy!vl`CA4oR)0C#g!LhEbxr#C(`)188)W4@{%)BdNZO=)4%ZF+G-Att`oQs#mJFZgY z+CJmivjn~tn>#ipuHGRN6#`e({M~QKAf2oCPEMp " + exit 1 +fi + +if [ $(date "+%y%m%d") != "161118" ]; then + echo "You need a time machine to run this script..." + echo "Use libfaketime to set the system clock back to the 18th of November 2016" + echo "Like so (path is for Ubuntu, might vary):" + echo " LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1 FAKETIME=\"2016-11-18 12:00:00\" $0 $@" + exit 1 +fi + +set -ex + +Vcert="$1" +Vissuer="$2" +Vstatus="$3" + +RQ="req.der" +RP="forged_response.der" +RPnocerts="forged_response_nocerts.der" + +RPcakey="ca.key" +RPcacert="ca.pem" + +RPcsrconf="cert_csr.conf" +RPcsr="cert.csr" +RPcertconf="cert.conf" +RPkey="cert.key" +RPcert="cert.pem" +RPindex="index.txt" + +# create a forged Certificate Authority +openssl req -x509 \ + -sha256 -days 356 \ + -nodes \ + -newkey rsa:2048 \ + -subj "/CN=Forged OCSP CA/C=DE/L=Berlin" \ + -keyout $RPcakey -out $RPcacert + +# create a self-signed certificate +openssl genrsa -out $RPkey 2048 + +cat > $RPcsrconf < $RPcertconf < $RPindex +elif [ "$Vstatus" = "revoked" ]; then + formatted_currentdate=$(date "+%y%m%d%H%M%S") + echo "R\t${formatted_enddate}Z\t${formatted_currentdate}Z\t${serial}\tunknown\t${subject}" > $RPindex +else + echo "Don't understand OCSP response status: $Vstatus" + exit 1 +fi + +# generate an OCSP response using the just-created certificate +openssl ocsp -issuer $Vissuer -cert $Vcert -reqout $RQ -text -no_nonce +openssl ocsp -reqin $RQ -rsigner $RPcert -rkey $RPkey -CA $Vissuer -index $RPindex -ndays 5 -respout $RP -text +openssl ocsp -reqin $RQ -rsigner $RPcert -rkey $RPkey -CA $Vissuer -index $RPindex -ndays 5 -respout $RPnocerts -resp_no_certs -text diff --git a/src/tests/test_ocsp.cpp b/src/tests/test_ocsp.cpp index e538bd5e3fb..2d89bd4c477 100644 --- a/src/tests/test_ocsp.cpp +++ b/src/tests/test_ocsp.cpp @@ -130,6 +130,44 @@ class OCSP_Tests final : public Test return result; } + static Test::Result test_response_find_signing_certificate() + { + Test::Result result("OCSP response finding signature certificates"); + + const std::optional nullopt_cert; + + // OCSP response is signed by the issuing CA itself + auto randombit_ocsp = load_test_OCSP_resp("x509/ocsp/randombit_ocsp.der"); + auto randombit_ca = load_test_X509_cert("x509/ocsp/letsencrypt.pem"); + + // OCSP response is signed by an authorized responder certificate + // issued by the issuing CA and embedded in the response + auto bdr_ocsp = load_test_OCSP_resp("x509/ocsp/bdr-ocsp-resp.der"); + auto bdr_responder = load_test_X509_cert("x509/ocsp/bdr-ocsp-responder.pem"); + auto bdr_ca = load_test_X509_cert("x509/ocsp/bdr-int.pem"); + + // Dummy OCSP response is not signed at all + auto dummy_ocsp = Botan::OCSP::Response(Botan::Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE); + + // OCSP response is signed by 3rd party responder certificate that is + // not included in the OCSP response itself + auto randombit_alt_resp_ocsp = load_test_OCSP_resp("x509/ocsp/randombit_ocsp_forged_valid_nocerts.der"); + auto randombit_alt_resp_cert = load_test_X509_cert("x509/ocsp/randombit_ocsp_forged_responder.pem"); + + result.test_is_eq("Dummy has no signing certificate", dummy_ocsp.find_signing_certificate(Botan::X509_Certificate()), nullopt_cert); + + result.test_is_eq("CA is returned as signing certificate", randombit_ocsp.find_signing_certificate(randombit_ca), std::optional(randombit_ca)); + result.test_is_eq("No signer certificate is returned when signer couldn't be determined", randombit_ocsp.find_signing_certificate(bdr_ca), nullopt_cert); + + result.test_is_eq("Delegated responder certificate is returned for further validation", bdr_ocsp.find_signing_certificate(bdr_ca), std::optional(bdr_responder)); + + result.test_is_eq("Delegated responder without stapled certs does not find signer without user-provided certs", randombit_alt_resp_ocsp.find_signing_certificate(randombit_ca), nullopt_cert); + auto trusted_responders = std::make_unique(randombit_alt_resp_cert); + result.test_is_eq("Delegated responder returns user-provided cert", randombit_alt_resp_ocsp.find_signing_certificate(randombit_ca, trusted_responders.get()), std::optional(randombit_alt_resp_cert)); + + return result; + } + static Test::Result test_response_verification_with_next_update_without_max_age() { Test::Result result("OCSP request check with next_update w/o max_age"); @@ -347,6 +385,38 @@ class OCSP_Tests final : public Test } #endif + static Test::Result test_response_verification_with_additionally_trusted_responder() + { + Test::Result result("OCSP response with user-defined (additional) responder certificate"); + + // OCSP response is signed by 3rd party responder certificate that is + // not included in the OCSP response itself + auto ocsp = load_test_OCSP_resp("x509/ocsp/randombit_ocsp_forged_valid_nocerts.der"); + auto responder = load_test_X509_cert("x509/ocsp/randombit_ocsp_forged_responder.pem"); + auto ca = load_test_X509_cert("x509/ocsp/letsencrypt.pem"); + + std::optional nullopt_cert; + + Botan::Certificate_Store_In_Memory trusted_responders; + + // without providing the 3rd party responder certificate no issuer will be found + result.test_is_eq("cannot find signing certificate without trusted responders", + ocsp.find_signing_certificate(ca), nullopt_cert); + result.test_is_eq("cannot verify signature without additional help", + ocsp.find_signing_certificate(ca, &trusted_responders), nullopt_cert); + + // add the 3rd party responder certificate to the list of trusted OCSP responder certs + // to find the issuer certificate of this response + trusted_responders.add_certificate(responder); + result.test_is_eq("the responder certificate is returned when it is trusted", + ocsp.find_signing_certificate(ca, &trusted_responders), std::optional(responder)); + + result.test_is_eq("the responder's signature checks out", + ocsp.verify_signature(responder), Botan::Certificate_Status_Code::OCSP_SIGNATURE_OK); + + return result; + } + static Test::Result test_responder_cert_with_nocheck_extension() { Test::Result result("BDr's OCSP response contains certificate featuring NoCheck extension"); @@ -371,11 +441,13 @@ class OCSP_Tests final : public Test results.push_back(test_request_encoding()); results.push_back(test_response_parsing()); results.push_back(test_response_certificate_access()); + results.push_back(test_response_find_signing_certificate()); results.push_back(test_response_verification_with_next_update_without_max_age()); results.push_back(test_response_verification_with_next_update_with_max_age()); results.push_back(test_response_verification_without_next_update_with_max_age()); results.push_back(test_response_verification_without_next_update_without_max_age()); results.push_back(test_response_verification_softfail()); + results.push_back(test_response_verification_with_additionally_trusted_responder()); results.push_back(test_responder_cert_with_nocheck_extension()); #if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS) diff --git a/src/tests/test_x509_path.cpp b/src/tests/test_x509_path.cpp index 1c2bbcc96db..0e145c95e00 100644 --- a/src/tests/test_x509_path.cpp +++ b/src/tests/test_x509_path.cpp @@ -1080,12 +1080,174 @@ class Path_Validation_With_OCSP_Tests final : public Test return result; } + static Test::Result validate_with_ocsp_with_authorized_responder() + { + Test::Result result("path check with ocsp response from authorized responder certificate"); + Botan::Certificate_Store_In_Memory trusted; + + auto restrictions = Botan::Path_Validation_Restrictions(true, // require revocation info + 110, // minimum key strength + true); // OCSP for all intermediates + + auto ee = load_test_X509_cert("x509/ocsp/bdr.pem"); + auto ca = load_test_X509_cert("x509/ocsp/bdr-int.pem"); + auto trust_root = load_test_X509_cert("x509/ocsp/bdr-root.pem"); + + // These OCSP responses are signed by an authorized OCSP responder + // certificate issued by `ca` and `trust_root` respectively. Note that + // the responder certificates contain the "OCSP No Check" extension, + // meaning that they themselves do not need a revocation check via OCSP. + auto ocsp_ee = load_test_OCSP_resp("x509/ocsp/bdr-ocsp-resp.der"); + auto ocsp_ca = load_test_OCSP_resp("x509/ocsp/bdr-int-ocsp-resp.der"); + + trusted.add_certificate(trust_root); + const std::vector cert_path = { ee, ca, trust_root }; + + auto check_path = [&](const std::chrono::system_clock::time_point valid_time, + const Botan::Certificate_Status_Code expected) + { + const auto path_result = Botan::x509_path_validate(cert_path, restrictions, trusted, "", Botan::Usage_Type::UNSPECIFIED, + valid_time, std::chrono::milliseconds(0), {ocsp_ee, ocsp_ca}); + + return result.confirm(std::string("Status: '") + Botan::to_string(expected) + + "' should match '" + Botan::to_string(path_result.result()) + "'", + path_result.result()==expected); + }; + + check_path(Botan::calendar_point(2022, 9, 18, 16, 30, 0).to_std_timepoint(), + Botan::Certificate_Status_Code::OCSP_NOT_YET_VALID); + check_path(Botan::calendar_point(2022, 9, 19, 16, 30, 0).to_std_timepoint(), + Botan::Certificate_Status_Code::OK); + check_path(Botan::calendar_point(2022, 9, 20, 16, 30, 0).to_std_timepoint(), + Botan::Certificate_Status_Code::OCSP_HAS_EXPIRED); + + return result; + } + + static Test::Result validate_with_ocsp_with_authorized_responder_without_keyusage() + { + Test::Result result("path check with ocsp response from authorized responder certificate (without sufficient key usage)"); + Botan::Certificate_Store_In_Memory trusted; + + auto restrictions = Botan::Path_Validation_Restrictions(true, // require revocation info + 110, // minimum key strength + false); // OCSP for all intermediates + + auto ee = load_test_X509_cert("x509/ocsp/mychain_ee.pem"); + auto ca = load_test_X509_cert("x509/ocsp/mychain_int.pem"); + auto trust_root = load_test_X509_cert("x509/ocsp/mychain_root.pem"); + + auto ocsp_ee_delegate = load_test_OCSP_resp("x509/ocsp/mychain_ocsp_for_ee_delegate_signed.der").value(); + auto ocsp_ee_delegate_malformed = load_test_OCSP_resp("x509/ocsp/mychain_ocsp_for_ee_delegate_signed_malformed.der").value(); + + trusted.add_certificate(trust_root); + const std::vector cert_path = { ee, ca, trust_root }; + + auto check_path = [&](const std::chrono::system_clock::time_point valid_time, + const Botan::OCSP::Response& ocsp_ee, + const Botan::Certificate_Status_Code expected) + { + const auto path_result = Botan::x509_path_validate(cert_path, restrictions, trusted, "", Botan::Usage_Type::UNSPECIFIED, + valid_time, std::chrono::milliseconds(0), {ocsp_ee}); + + return result.confirm(std::string("Status: '") + Botan::to_string(expected) + + "' should match '" + Botan::to_string(path_result.result()) + "'", + path_result.result()==expected); + }; + + check_path( + Botan::calendar_point(2022, 9, 22, 23, 30, 0).to_std_timepoint(), + ocsp_ee_delegate, + Botan::Certificate_Status_Code::VERIFIED); + check_path( + Botan::calendar_point(2022, 10, 8, 23, 30, 0).to_std_timepoint(), + ocsp_ee_delegate, + Botan::Certificate_Status_Code::CERT_HAS_EXPIRED); + check_path( + Botan::calendar_point(2022, 9, 22, 23, 30, 0).to_std_timepoint(), + ocsp_ee_delegate_malformed, + Botan::Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE); + + return result; + } + + static Test::Result validate_with_forged_ocsp_using_self_signed_cert() + { + Test::Result result("path check with forged ocsp using self-signed certificate"); + Botan::Certificate_Store_In_Memory trusted; + + auto restrictions = Botan::Path_Validation_Restrictions(true, // require revocation info + 110, // minimum key strength + false); // OCSP for all intermediates + + auto ee = load_test_X509_cert("x509/ocsp/randombit.pem"); + auto ca = load_test_X509_cert("x509/ocsp/letsencrypt.pem"); + auto trust_root = load_test_X509_cert("x509/ocsp/identrust.pem"); + trusted.add_certificate(trust_root); + + const std::vector cert_path = { ee, ca, trust_root }; + + auto check_path = [&](const std::string &forged_ocsp, + const Botan::Certificate_Status_Code expected) + { + auto ocsp = load_test_OCSP_resp(forged_ocsp); + const auto path_result = Botan::x509_path_validate(cert_path, restrictions, trusted, "", Botan::Usage_Type::UNSPECIFIED, + Botan::calendar_point(2016, 11, 18, 12, 30, 0).to_std_timepoint(), std::chrono::milliseconds(0), {ocsp}); + + result.test_is_eq("Path validation with forged OCSP response should fail with", + path_result.result(), expected); + result.test_note(std::string("Failed with: ") + Botan::to_string(path_result.result())); + }; + + // In both cases the path validation should detect the forged OCSP + // response and generate an appropriate error. By no means it should + // follow the unauthentic OCSP response. + check_path("x509/ocsp/randombit_ocsp_forged_valid.der", Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND); + check_path("x509/ocsp/randombit_ocsp_forged_revoked.der", Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND); + + return result; + } + + static Test::Result validate_with_ocsp_self_signed_by_intermediate_cert() + { + Test::Result result("path check with ocsp response for intermediate that is (maliciously) self-signed by the intermediate"); + Botan::Certificate_Store_In_Memory trusted; + + auto restrictions = Botan::Path_Validation_Restrictions(true, // require revocation info + 110, // minimum key strength + true); // OCSP for all intermediates + + auto ee = load_test_X509_cert("x509/ocsp/mychain_ee.pem"); + auto ca = load_test_X509_cert("x509/ocsp/mychain_int.pem"); + auto trust_root = load_test_X509_cert("x509/ocsp/mychain_root.pem"); + + // this OCSP response for EE is valid (signed by intermediate cert) + auto ocsp_ee = load_test_OCSP_resp("x509/ocsp/mychain_ocsp_for_ee.der"); + + // this OCSP response for Intermediate is malicious (signed by intermediate itself) + auto ocsp_ca = load_test_OCSP_resp("x509/ocsp/mychain_ocsp_for_int_self_signed.der"); + + trusted.add_certificate(trust_root); + const std::vector cert_path = { ee, ca, trust_root }; + + const auto path_result = Botan::x509_path_validate(cert_path, restrictions, trusted, "", Botan::Usage_Type::UNSPECIFIED, + Botan::calendar_point(2022, 9, 22, 22, 30, 0).to_std_timepoint(), std::chrono::milliseconds(0), {ocsp_ee, ocsp_ca}); + result.confirm("should reject intermediate OCSP response", path_result.result() == Botan::Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND); + result.test_note(std::string("Failed with: ") + Botan::to_string(path_result.result())); + + return result; + } + std::vector run() override { return {validate_with_ocsp_with_next_update_without_max_age(), validate_with_ocsp_with_next_update_with_max_age(), validate_with_ocsp_without_next_update_without_max_age(), - validate_with_ocsp_without_next_update_with_max_age()}; + validate_with_ocsp_without_next_update_with_max_age(), + validate_with_ocsp_with_authorized_responder(), + validate_with_ocsp_with_authorized_responder_without_keyusage(), + validate_with_forged_ocsp_using_self_signed_cert(), + validate_with_ocsp_self_signed_by_intermediate_cert()}; } }; From dd045b1f92d68d5ff0c25f654f985ad39463aa97 Mon Sep 17 00:00:00 2001 From: Rene Meusel Date: Tue, 8 Nov 2022 08:45:07 +0100 Subject: [PATCH 6/8] add note about OCSP API changes to migration guide --- doc/migration_guide.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/migration_guide.rst b/doc/migration_guide.rst index dbdbc301c82..6b13f783302 100644 --- a/doc/migration_guide.rst +++ b/doc/migration_guide.rst @@ -97,6 +97,14 @@ should either call a specific function on ``X509_Certificate`` which returns the same information, or lacking that, iterate over the result of ``X509_Certificate::v3_extensions``. +OCSP Response Validation +------------------------ + +After mitigating CVE-2022-43705 the OCSP response signature validation was refactored. +This led to the removal of the `OCSP::Response::check_signature()` method. If you +must validate OCSP responses directly in your application please use the new method +`OCSP::Response::find_signing_certificate()` and `OCSP::Response::verify_signature()`. + Use of ``enum class`` -------------------------------- From 395e597dd7c4719bf46803b46249a0c85933a2f7 Mon Sep 17 00:00:00 2001 From: Rene Meusel Date: Wed, 9 Nov 2022 11:23:50 +0100 Subject: [PATCH 7/8] review comments --- src/lib/x509/cert_status.cpp | 2 ++ src/lib/x509/pkix_enums.h | 1 + src/lib/x509/x509path.cpp | 1 + src/tests/test_ocsp.cpp | 2 +- src/tests/test_x509_path.cpp | 38 +++++++++++++++++++++++++++--------- 5 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/lib/x509/cert_status.cpp b/src/lib/x509/cert_status.cpp index e22d6e1be1e..5248b5704d5 100644 --- a/src/lib/x509/cert_status.cpp +++ b/src/lib/x509/cert_status.cpp @@ -29,6 +29,8 @@ const char* to_string(Certificate_Status_Code code) return "OCSP URL not available"; case Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE: return "OCSP server not available"; + case Certificate_Status_Code::OCSP_ISSUER_NOT_TRUSTED: + return "OCSP issuer is not trustworthy"; case Certificate_Status_Code::NO_REVOCATION_DATA: return "No revocation data"; diff --git a/src/lib/x509/pkix_enums.h b/src/lib/x509/pkix_enums.h index 80e22cf62a8..8263abd3f25 100644 --- a/src/lib/x509/pkix_enums.h +++ b/src/lib/x509/pkix_enums.h @@ -38,6 +38,7 @@ enum class Certificate_Status_Code { UNTRUSTED_HASH = 1001, NO_REVOCATION_DATA = 1002, NO_MATCHING_CRLDP = 1003, + OCSP_ISSUER_NOT_TRUSTED = 1004, // Time problems CERT_NOT_YET_VALID = 2000, diff --git a/src/lib/x509/x509path.cpp b/src/lib/x509/x509path.cpp index d931a21d0c9..5d57cbbc1e9 100644 --- a/src/lib/x509/x509path.cpp +++ b/src/lib/x509/x509path.cpp @@ -318,6 +318,7 @@ PKIX::check_ocsp(const std::vector& cert_path, ocsp_signing_cert_status > Certificate_Status_Code::FIRST_ERROR_STATUS) { status.insert(ocsp_signing_cert_status); + status.insert(Certificate_Status_Code::OCSP_ISSUER_NOT_TRUSTED); } else { diff --git a/src/tests/test_ocsp.cpp b/src/tests/test_ocsp.cpp index 2d89bd4c477..ad86f29322a 100644 --- a/src/tests/test_ocsp.cpp +++ b/src/tests/test_ocsp.cpp @@ -402,7 +402,7 @@ class OCSP_Tests final : public Test // without providing the 3rd party responder certificate no issuer will be found result.test_is_eq("cannot find signing certificate without trusted responders", ocsp.find_signing_certificate(ca), nullopt_cert); - result.test_is_eq("cannot verify signature without additional help", + result.test_is_eq("cannot find signing certificate without additional help", ocsp.find_signing_certificate(ca, &trusted_responders), nullopt_cert); // add the 3rd party responder certificate to the list of trusted OCSP responder certs diff --git a/src/tests/test_x509_path.cpp b/src/tests/test_x509_path.cpp index 0e145c95e00..c8c5d5dd2f8 100644 --- a/src/tests/test_x509_path.cpp +++ b/src/tests/test_x509_path.cpp @@ -69,6 +69,18 @@ std::map read_results(const std::string& results_file, return m; } +std::set flatten(const Botan::CertificatePathStatusCodes& codes) + { + std::set result; + + for(const auto& statuses : codes) + { + result.insert(statuses.begin(), statuses.end()); + } + + return result; + } + class X509test_Path_Validation_Tests final : public Test { public: @@ -1145,14 +1157,18 @@ class Path_Validation_With_OCSP_Tests final : public Test auto check_path = [&](const std::chrono::system_clock::time_point valid_time, const Botan::OCSP::Response& ocsp_ee, - const Botan::Certificate_Status_Code expected) + const Botan::Certificate_Status_Code expected, + const std::optional also_expected = std::nullopt) { const auto path_result = Botan::x509_path_validate(cert_path, restrictions, trusted, "", Botan::Usage_Type::UNSPECIFIED, valid_time, std::chrono::milliseconds(0), {ocsp_ee}); - return result.confirm(std::string("Status: '") + Botan::to_string(expected) - + "' should match '" + Botan::to_string(path_result.result()) + "'", - path_result.result()==expected); + result.test_is_eq("should result in expected validation status code", + path_result.result(), expected); + if(also_expected) + { + result.confirm("Secondary error is also present", flatten(path_result.all_statuses()).count(also_expected.value()) > 0); + } }; check_path( @@ -1162,11 +1178,13 @@ class Path_Validation_With_OCSP_Tests final : public Test check_path( Botan::calendar_point(2022, 10, 8, 23, 30, 0).to_std_timepoint(), ocsp_ee_delegate, - Botan::Certificate_Status_Code::CERT_HAS_EXPIRED); + Botan::Certificate_Status_Code::CERT_HAS_EXPIRED, + Botan::Certificate_Status_Code::OCSP_ISSUER_NOT_TRUSTED); check_path( Botan::calendar_point(2022, 9, 22, 23, 30, 0).to_std_timepoint(), ocsp_ee_delegate_malformed, - Botan::Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE); + Botan::Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE, + Botan::Certificate_Status_Code::OCSP_ISSUER_NOT_TRUSTED); return result; } @@ -1188,7 +1206,8 @@ class Path_Validation_With_OCSP_Tests final : public Test const std::vector cert_path = { ee, ca, trust_root }; auto check_path = [&](const std::string &forged_ocsp, - const Botan::Certificate_Status_Code expected) + const Botan::Certificate_Status_Code expected, + const Botan::Certificate_Status_Code also_expected) { auto ocsp = load_test_OCSP_resp(forged_ocsp); const auto path_result = Botan::x509_path_validate(cert_path, restrictions, trusted, "", Botan::Usage_Type::UNSPECIFIED, @@ -1196,14 +1215,15 @@ class Path_Validation_With_OCSP_Tests final : public Test result.test_is_eq("Path validation with forged OCSP response should fail with", path_result.result(), expected); + result.confirm("Secondary error is also present", flatten(path_result.all_statuses()).count(also_expected) > 0); result.test_note(std::string("Failed with: ") + Botan::to_string(path_result.result())); }; // In both cases the path validation should detect the forged OCSP // response and generate an appropriate error. By no means it should // follow the unauthentic OCSP response. - check_path("x509/ocsp/randombit_ocsp_forged_valid.der", Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND); - check_path("x509/ocsp/randombit_ocsp_forged_revoked.der", Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND); + check_path("x509/ocsp/randombit_ocsp_forged_valid.der", Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND, Botan::Certificate_Status_Code::OCSP_ISSUER_NOT_TRUSTED); + check_path("x509/ocsp/randombit_ocsp_forged_revoked.der", Botan::Certificate_Status_Code::CERT_ISSUER_NOT_FOUND, Botan::Certificate_Status_Code::OCSP_ISSUER_NOT_TRUSTED); return result; } From c4295096690086b96c775ef8d7268a030b98865c Mon Sep 17 00:00:00 2001 From: Rene Meusel Date: Thu, 15 Dec 2022 14:43:48 +0100 Subject: [PATCH 8/8] Review comment: move generator scripts --- src/{tests/data/x509/ocsp => scripts}/mychain_creater.sh | 0 src/{tests/data/x509/ocsp => scripts}/randombit_ocsp_forger.sh | 0 src/tests/test_ocsp.cpp | 2 ++ src/tests/test_x509_path.cpp | 2 ++ 4 files changed, 4 insertions(+) rename src/{tests/data/x509/ocsp => scripts}/mychain_creater.sh (100%) rename src/{tests/data/x509/ocsp => scripts}/randombit_ocsp_forger.sh (100%) diff --git a/src/tests/data/x509/ocsp/mychain_creater.sh b/src/scripts/mychain_creater.sh similarity index 100% rename from src/tests/data/x509/ocsp/mychain_creater.sh rename to src/scripts/mychain_creater.sh diff --git a/src/tests/data/x509/ocsp/randombit_ocsp_forger.sh b/src/scripts/randombit_ocsp_forger.sh similarity index 100% rename from src/tests/data/x509/ocsp/randombit_ocsp_forger.sh rename to src/scripts/randombit_ocsp_forger.sh diff --git a/src/tests/test_ocsp.cpp b/src/tests/test_ocsp.cpp index ad86f29322a..1d51ec3ba0f 100644 --- a/src/tests/test_ocsp.cpp +++ b/src/tests/test_ocsp.cpp @@ -151,6 +151,7 @@ class OCSP_Tests final : public Test // OCSP response is signed by 3rd party responder certificate that is // not included in the OCSP response itself + // See `src/scripts/randombit_ocsp_forger.sh` for a helper script to recreate those. auto randombit_alt_resp_ocsp = load_test_OCSP_resp("x509/ocsp/randombit_ocsp_forged_valid_nocerts.der"); auto randombit_alt_resp_cert = load_test_X509_cert("x509/ocsp/randombit_ocsp_forged_responder.pem"); @@ -391,6 +392,7 @@ class OCSP_Tests final : public Test // OCSP response is signed by 3rd party responder certificate that is // not included in the OCSP response itself + // See `src/scripts/randombit_ocsp_forger.sh` for a helper script to recreate those. auto ocsp = load_test_OCSP_resp("x509/ocsp/randombit_ocsp_forged_valid_nocerts.der"); auto responder = load_test_X509_cert("x509/ocsp/randombit_ocsp_forged_responder.pem"); auto ca = load_test_X509_cert("x509/ocsp/letsencrypt.pem"); diff --git a/src/tests/test_x509_path.cpp b/src/tests/test_x509_path.cpp index c8c5d5dd2f8..2f86ee7b650 100644 --- a/src/tests/test_x509_path.cpp +++ b/src/tests/test_x509_path.cpp @@ -1145,6 +1145,7 @@ class Path_Validation_With_OCSP_Tests final : public Test 110, // minimum key strength false); // OCSP for all intermediates + // See `src/scripts/mychain_creater.sh` if you need to recreate those auto ee = load_test_X509_cert("x509/ocsp/mychain_ee.pem"); auto ca = load_test_X509_cert("x509/ocsp/mychain_int.pem"); auto trust_root = load_test_X509_cert("x509/ocsp/mychain_root.pem"); @@ -1237,6 +1238,7 @@ class Path_Validation_With_OCSP_Tests final : public Test 110, // minimum key strength true); // OCSP for all intermediates + // See `src/scripts/mychain_creater.sh` if you need to recreate those auto ee = load_test_X509_cert("x509/ocsp/mychain_ee.pem"); auto ca = load_test_X509_cert("x509/ocsp/mychain_int.pem"); auto trust_root = load_test_X509_cert("x509/ocsp/mychain_root.pem");