10#include <botan/assert.h>
11#include <botan/base64.h>
12#include <botan/ber_dec.h>
13#include <botan/certstor.h>
14#include <botan/der_enc.h>
15#include <botan/hash.h>
16#include <botan/pubkey.h>
18#include <botan/x509_ext.h>
19#include <botan/x509path.h>
20#include <botan/internal/fmt.h>
22#if defined(BOTAN_HAS_HTTP_UTIL)
23 #include <botan/internal/http_util.h>
36void check_generalized_time(
const ASN1_Time& time,
const char* field) {
38 throw Decoding_Error(
fmt(
"OCSP response {} was not encoded as GeneralizedTime", field));
48 m_subject_serial(subject_serial) {
57 m_issuer_dn_hash = hash->process<std::vector<uint8_t>>(issuer.
raw_subject_dn());
62 if(subject.
serial() != m_subject_serial) {
66 const auto hash_algo = m_hash_id.oid().registered_name();
79 if(hash_algo ==
"SHA-1") {
86 }
else if(hash_algo ==
"SHA-256") {
130 if(!m_hash_id.parameters_are_null_or_empty()) {
131 throw Decoding_Error(
"OCSP CertID hashAlgorithm has unexpected parameters");
150 std::optional<CRL_Code> reason,
159 std::optional<X509_Time> revocation_time,
160 std::optional<CRL_Code> revocation_reason,
163 m_certid(std::move(certid)),
164 m_cert_status(cert_status),
165 m_thisupdate(std::move(this_update)),
166 m_nextupdate(std::move(next_update)),
167 m_revocation_time(std::move(revocation_time)),
168 m_revocation_reason(revocation_reason) {
169 const auto require_generalized_time = [](
const X509_Time& t,
const char* field) {
175 require_generalized_time(m_thisupdate,
"thisUpdate");
177 require_generalized_time(m_nextupdate,
"nextUpdate");
179 if(m_cert_status == 1) {
180 if(!m_revocation_time.has_value()) {
181 throw Invalid_Argument(
"Revoked OCSP SingleResponse lacks a revocation time");
183 require_generalized_time(*m_revocation_time,
"revocationTime");
192 if(m_cert_status == 1) {
203 const std::span<const uint8_t> empty;
208 if(m_nextupdate.time_is_set()) {
243 check_generalized_time(m_thisupdate,
"thisUpdate");
244 if(m_nextupdate.time_is_set()) {
245 check_generalized_time(m_nextupdate,
"nextUpdate");
248 if(seq.more_items()) {
249 const BER_Object next = seq.get_next_object();
260 const auto cert_status_class =
cert_status.get_class();
263 throw Decoding_Error(
"OCSP::SingleResponse: certStatus has unexpected class tag");
266 m_cert_status =
static_cast<uint32_t
>(
cert_status.type());
267 if(m_cert_status > 2) {
271 m_revocation_time.reset();
272 m_revocation_reason.reset();
274 if(m_cert_status == 1) {
284 if(reason == 7 || reason > 10) {
287 m_revocation_reason =
static_cast<CRL_Code>(reason);
302void decode_optional_list(
BER_Decoder& ber,
ASN1_Type tag, std::vector<X509_Certificate>& output) {
311 auto seq = list.start_sequence();
312 while(seq.more_items()) {
313 output.push_back([&] {
326 m_issuer(issuer_cert), m_certid(m_issuer, subject_cert.serial()) {
328 throw Invalid_Argument(
"Invalid cert pair to OCSP::Request (mismatched issuer,subject args?)");
333 m_issuer(issuer_cert), m_certid(m_issuer, subject_serial) {}
350 std::vector<uint8_t> output;
355 .
encode(
static_cast<size_t>(0))
376 m_response_bits(response_bits, response_bits + response_bits_len) {
393 size_t resp_status = 0;
410 if(resp_status == 4 || resp_status >= 7) {
421 const bool has_response_bytes = response_outer.
more_items();
423 if(successful && !has_response_bytes) {
424 throw Decoding_Error(
"OCSP response with successful status is missing responseBytes");
426 if(!successful && has_response_bytes) {
427 throw Decoding_Error(
"OCSP response with non-successful status includes responseBytes");
435 "Unknown response type in OCSP response");
454 decode_optional_list(basicresponse,
ASN1_Type(0), m_certs);
473 size_t responsedata_version = 0;
489 check_generalized_time(m_produced_at,
"producedAt");
503 const bool has_signer = !m_signer_name.empty();
504 const bool has_key_hash = !m_key_hash.empty();
506 if(has_signer && has_key_hash) {
507 throw Decoding_Error(
"OCSP response includes both byName and byKey in responderID field");
509 if(!has_signer && !has_key_hash) {
510 throw Decoding_Error(
"OCSP response contains neither byName nor byKey in responderID field");
512 if(has_key_hash && m_key_hash.size() != 20) {
514 throw Decoding_Error(
"OCSP response contains a byKey with invalid length");
527 if(m_has_unknown_critical_ext ==
false) {
529 for(
const auto& sr : m_responses) {
530 if(sr.has_unknown_critical_extension()) {
531 m_has_unknown_critical_ext =
true;
539 if(!m_signer_name.
empty()) {
540 return (candidate.
subject_dn() == m_signer_name);
543 if(!m_key_hash.empty()) {
558 if(m_dummy_response_status) {
559 return m_dummy_response_status.value();
562 if(m_signer_name.empty() && m_key_hash.empty()) {
566 if(!is_issued_by(issuer)) {
575 verifier.
update(m_tbs_bits);
578 if(valid_signature ==
false) {
582 if(m_has_unknown_critical_ext) {
587 if(!trusted_hashes.empty() && !trusted_hashes.contains(verifier.
hash_function())) {
603 using namespace std::placeholders;
606 if(is_issued_by(issuer_certificate)) {
607 return issuer_certificate;
611 for(
const auto& cert : m_certs) {
612 if(this->is_issued_by(cert)) {
618 if(trusted_ocsp_responders !=
nullptr) {
619 if(!m_key_hash.empty()) {
626 if(!m_signer_name.empty()) {
627 auto signing_cert = trusted_ocsp_responders->
find_cert(m_signer_name, {});
639 std::chrono::system_clock::time_point ref_time,
640 std::chrono::seconds max_age)
const {
641 if(m_dummy_response_status) {
642 return m_dummy_response_status.value();
645 for(
const auto& response : m_responses) {
646 if(response.certid().is_id_for(issuer, subject)) {
656 if(response.cert_status() == 1) {
661 if(response.this_update() > x509_ref_time) {
665 if(response.next_update().time_is_set()) {
666 if(x509_ref_time > response.next_update()) {
669 }
else if(max_age > std::chrono::seconds::zero() &&
670 ref_time - response.this_update().to_std_timepoint() > max_age) {
678 if(response.cert_status() == 0) {
689#if defined(BOTAN_HAS_HTTP_UTIL)
692 const BigInt& subject_serial,
693 std::string_view ocsp_responder,
694 std::chrono::milliseconds timeout) {
695 if(ocsp_responder.empty()) {
700 return online_check(issuer, subject_serial, *uri, timeout);
702 throw Invalid_Argument(
"Unparsable URI for OCSP responder");
706Response online_check(
const X509_Certificate& issuer,
707 const BigInt& subject_serial,
708 const URI& ocsp_responder,
709 std::chrono::milliseconds timeout) {
710 const OCSP::Request req(issuer, subject_serial);
713 "application/ocsp-request",
715 HTTP::RequestLimits().set_timeout(timeout).set_max_body_size(64 * 1024));
717 http.throw_unless_ok();
721 return OCSP::Response(http.body());
724Response online_check(
const X509_Certificate& issuer,
725 const X509_Certificate& subject,
726 std::chrono::milliseconds timeout) {
727 if(subject.issuer_dn() != issuer.subject_dn()) {
728 throw Invalid_Argument(
"Invalid cert pair to OCSP::online_check (mismatched issuer,subject args?)");
733 if(responders.empty()) {
734 throw Invalid_Argument(
"No HTTP OCSP responder URLs available for this certificate");
737 const auto subject_serial = subject.serial().to_bigint();
740 for(
size_t i = 0; i + 1 < responders.size(); ++i) {
742 return online_check(issuer, subject_serial, responders[i], timeout);
747 return online_check(issuer, subject_serial, responders.back(), timeout);
ASN1_Type tagging() const
Return the tag (UtcTime or GeneralizedTime) this time was encoded with.
bool time_is_set() const
Return if the time has been set somehow.
const BER_Object & peek_next_object()
void push_back(const BER_Object &obj)
BER_Object get_next_object()
BER_Decoder & decode(bool &out)
BER_Decoder & raw_bytes(std::vector< uint8_t, Alloc > &out)
std::vector< uint8_t > get_next_octet_string()
BER_Decoder & verify_end()
BER_Decoder start_sequence()
BER_Decoder start_context_specific(uint32_t tag)
BER_Decoder & decode_optional(T &out, ASN1_Type type_tag, ASN1_Class class_tag, const T &default_value=T())
BER_Decoder & decode_and_check(const T &expected, std::string_view error_msg)
BER_Decoder & decode_optional_string(std::vector< uint8_t, Alloc > &out, ASN1_Type real_type, uint32_t expected_tag, ASN1_Class class_tag=ASN1_Class::ContextSpecific)
BER_Decoder & get_next(BER_Object &ber)
BER_Decoder & decode_list(std::vector< T > &vec, ASN1_Type type_tag=ASN1_Type::Sequence, ASN1_Class class_tag=ASN1_Class::Universal)
BER_Decoder & decode_octet_aligned_bitstring(std::vector< uint8_t, Alloc > &out, ASN1_Type type_tag=ASN1_Type::BitString, ASN1_Class class_tag=ASN1_Class::Universal)
bool is_a(ASN1_Type type_tag, ASN1_Class class_tag) const
virtual std::optional< X509_Certificate > find_cert_by_pubkey_sha1(const std::vector< uint8_t > &key_hash) const =0
virtual std::optional< X509_Certificate > find_cert(const X509_DN &subject_dn, const std::vector< uint8_t > &key_id) const
DER_Encoder & add_object(ASN1_Type type_tag, ASN1_Class class_tag, const uint8_t rep[], size_t length)
DER_Encoder & end_explicit()
DER_Encoder & start_explicit(uint16_t type_tag)
DER_Encoder & start_sequence()
DER_Encoder & start_cons(ASN1_Type type_tag, ASN1_Class class_tag)
DER_Encoder & encode(bool b)
std::vector< OID > critical_extensions() const
void decode_from(BER_Decoder &from) override
static std::unique_ptr< HashFunction > create_or_throw(std::string_view algo_spec, std::string_view provider="")
void decode_from(BER_Decoder &from) override
void encode_into(DER_Encoder &to) const override
bool is_id_for(const X509_Certificate &issuer, const X509_Certificate &subject) const
std::string base64_encode() const
Request(const X509_Certificate &issuer_cert, const X509_Certificate &subject_cert)
std::vector< uint8_t > BER_encode() const
BOTAN_FUTURE_EXPLICIT Response(Certificate_Status_Code status)
Certificate_Status_Code status_for(const X509_Certificate &issuer, const X509_Certificate &subject, std::chrono::system_clock::time_point ref_time=std::chrono::system_clock::now(), std::chrono::seconds max_age=std::chrono::seconds::zero()) const
Response_Status_Code status() const
std::optional< X509_Certificate > find_signing_certificate(const X509_Certificate &issuer_certificate, const Certificate_Store *trusted_ocsp_responders=nullptr) const
Certificate_Status_Code verify_signature(const X509_Certificate &signing_certificate) const
const std::optional< X509_Time > & revocation_time() const
The revocationTime; set only when cert_status() is 1 (revoked).
const CertID & certid() const
static SingleResponse unknown(CertID certid, X509_Time this_update, X509_Time next_update)
As good(), but asserting an unknown status.
const X509_Time & next_update() const
static SingleResponse revoked(CertID certid, X509_Time revocation_time, std::optional< CRL_Code > reason, X509_Time this_update, X509_Time next_update)
As good(), but asserting a revoked status with the given RevokedInfo.
static SingleResponse good(CertID certid, X509_Time this_update, X509_Time next_update)
void decode_from(BER_Decoder &from) override
const X509_Time & this_update() const
size_t cert_status() const
void encode_into(DER_Encoder &to) const override
static OID from_string(std::string_view str)
std::string hash_function() const
bool check_signature(const uint8_t sig[], size_t length)
const std::set< std::string > & trusted_hashes() const
size_t minimum_key_strength() const
static std::optional< URI > from_string(std::string_view raw)
static std::vector< URI > filter_scheme(std::string_view scheme, std::span< const URI > uris)
const X509_DN & subject_dn() const
const X509_Serial_Number & serial() const
const std::vector< uint8_t > & raw_subject_dn() const
std::span< const uint8_t, 32 > subject_public_key_bitstring_sha256() const
const std::vector< uint8_t > & subject_public_key_bitstring_sha1() const
const X509_DN & issuer_dn() const
const std::vector< uint8_t > & raw_issuer_dn_sha256() const
const std::vector< uint8_t > & subject_public_key_bitstring() const
std::unique_ptr< Public_Key > subject_public_key() const
std::span< const uint8_t, 20 > raw_issuer_dn_sha1() const
void decode_from(BER_Decoder &from) override
std::vector< uint8_t > der_sequence_header(size_t contents_len)
Response POST_sync(const URI &uri, std::string_view content_type, const std::vector< uint8_t > &body, const RequestLimits &limits)
std::string fmt(std::string_view format, const T &... args)
@ UNKNOWN_CRITICAL_EXTENSION
@ SIGNATURE_METHOD_TOO_WEAK
size_t base64_encode(char out[], const uint8_t in[], size_t input_length, size_t &input_consumed, bool final_inputs)