/* * Certificate Message * (C) 2022 Jack Lloyd * 2022 Hannes Rantzsch, René Meusel - neXenio GmbH * 2023 René Meusel, Fabian Albert - Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Botan::TLS { namespace { bool certificate_allows_signing(const X509_Certificate& cert) { const auto constraints = cert.constraints(); if(constraints.empty()) { return true; } return constraints.includes_any(Key_Constraints::DigitalSignature, Key_Constraints::NonRepudiation); } std::vector filter_signature_schemes(const std::vector& peer_scheme_preference) { std::vector compatible_schemes; for(const auto& scheme : peer_scheme_preference) { if(scheme.is_available() && scheme.is_compatible_with(Protocol_Version::TLS_V13)) { compatible_schemes.push_back(scheme.algorithm_name()); } } if(compatible_schemes.empty()) { throw TLS_Exception(Alert::HandshakeFailure, "Failed to agree on any signature algorithm"); } return compatible_schemes; } } // namespace bool Certificate_13::has_certificate_chain() const { return !empty() && m_entries.front().has_certificate(); } bool Certificate_13::is_raw_public_key() const { return !empty() && !has_certificate_chain(); } std::vector Certificate_13::cert_chain() const { BOTAN_STATE_CHECK(has_certificate_chain()); std::vector result; std::transform(m_entries.cbegin(), m_entries.cend(), std::back_inserter(result), [](const auto& cert_entry) { return cert_entry.certificate(); }); return result; } void Certificate_13::validate_extensions(const std::set& requested_extensions, Callbacks& cb) const { // RFC 8446 4.4.2 // Extensions in the Certificate message from the server MUST // correspond to ones from the ClientHello message. Extensions in // the Certificate message from the client MUST correspond to // extensions in the CertificateRequest message from the server. for(const auto& entry : m_entries) { if(entry.extensions().contains_other_than(requested_extensions)) { throw TLS_Exception(Alert::IllegalParameter, "Certificate Entry contained an extension that was not offered"); } cb.tls_examine_extensions(entry.extensions(), m_side, type()); } } std::shared_ptr Certificate_13::public_key() const { BOTAN_STATE_CHECK(!empty()); return m_entries.front().public_key(); } const X509_Certificate& Certificate_13::leaf() const { BOTAN_STATE_CHECK(!empty()); return m_entries.front().certificate(); } void Certificate_13::verify(Callbacks& callbacks, const Policy& policy, Credentials_Manager& creds, std::string_view hostname, bool use_ocsp) const { const auto usage = (m_side == Connection_Side::Client) ? Usage_Type::TLS_CLIENT_AUTH : Usage_Type::TLS_SERVER_AUTH; if(is_raw_public_key()) { callbacks.tls_verify_raw_public_key(*public_key(), usage, hostname, policy); } else { verify_certificate_chain(callbacks, policy, creds, hostname, use_ocsp, usage); } } void Certificate_13::verify_certificate_chain(Callbacks& callbacks, const Policy& policy, Credentials_Manager& creds, std::string_view hostname, bool use_ocsp, Usage_Type usage_type) const { std::vector certs; std::vector> ocsp_responses; for(const auto& entry : m_entries) { certs.push_back(entry.certificate()); if(use_ocsp) { if(entry.extensions().has()) { ocsp_responses.push_back(callbacks.tls_parse_ocsp_response( entry.extensions().get()->get_ocsp_response())); } else { ocsp_responses.emplace_back(); } } } const auto& server_cert = m_entries.front().certificate(); if(!certificate_allows_signing(server_cert)) { throw TLS_Exception(Alert::BadCertificate, "Certificate usage constraints do not allow signing"); } // Note that m_side represents the sender, so the usages here are swapped const auto trusted_CAs = creds.trusted_certificate_authorities( m_side == Connection_Side::Client ? "tls-server" : "tls-client", std::string(hostname)); callbacks.tls_verify_cert_chain(certs, ocsp_responses, trusted_CAs, usage_type, hostname, policy); } void Certificate_13::setup_entries(std::vector cert_chain, const Certificate_Status_Request* csr, Callbacks& callbacks) { // RFC 8446 4.4.2.1 // A server MAY request that a client present an OCSP response with its // certificate by sending an empty "status_request" extension in its // CertificateRequest message. const auto ocsp_responses = (csr != nullptr) ? callbacks.tls_provide_cert_chain_status(cert_chain, *csr) : std::vector>(cert_chain.size()); if(ocsp_responses.size() != cert_chain.size()) { throw TLS_Exception(Alert::InternalError, "Application didn't provide the correct number of OCSP responses"); } for(size_t i = 0; i < cert_chain.size(); ++i) { auto& entry = m_entries.emplace_back(cert_chain[i]); if(!ocsp_responses[i].empty()) { entry.extensions().add(new Certificate_Status_Request(ocsp_responses[i])); } // This will call the modification callback multiple times. Once for // each certificate in the `cert_chain`. Users that want to add an // extension to a specific Certificate Entry might have a hard time // to distinguish them. // // TODO: Callbacks::tls_modify_extensions() might need even more // context depending on the message whose extensions should be // manipulatable. callbacks.tls_modify_extensions(entry.extensions(), m_side, type()); } } void Certificate_13::setup_entry(std::shared_ptr raw_public_key, Callbacks& callbacks) { BOTAN_ASSERT_NONNULL(raw_public_key); auto& entry = m_entries.emplace_back(std::move(raw_public_key)); callbacks.tls_modify_extensions(entry.extensions(), m_side, type()); } /** * Create a Client Certificate message */ Certificate_13::Certificate_13(const Certificate_Request_13& cert_request, std::string_view hostname, Credentials_Manager& credentials_manager, Callbacks& callbacks, Certificate_Type cert_type) : m_request_context(cert_request.context()), m_side(Connection_Side::Client) { const auto key_types = filter_signature_schemes(cert_request.signature_schemes()); const auto op_type = "tls-client"; if(cert_type == Certificate_Type::X509) { setup_entries( credentials_manager.find_cert_chain(key_types, to_algorithm_identifiers(cert_request.certificate_signature_schemes()), cert_request.acceptable_CAs(), op_type, std::string(hostname)), cert_request.extensions().get(), callbacks); } else if(cert_type == Certificate_Type::RawPublicKey) { auto raw_public_key = credentials_manager.find_raw_public_key(key_types, op_type, std::string(hostname)); // RFC 8446 4.4.2 // If the RawPublicKey certificate type was negotiated, then the // certificate_list MUST contain no more than one CertificateEntry // [...]. // A client will send an empty certificate_list if it does not have // an appropriate certificate to send in response to the server's // authentication request. if(raw_public_key) { setup_entry(std::move(raw_public_key), callbacks); } } } /** * Create a Server Certificate message */ Certificate_13::Certificate_13(const Client_Hello_13& client_hello, Credentials_Manager& credentials_manager, Callbacks& callbacks, Certificate_Type cert_type) : // RFC 8446 4.4.2: // [In the case of server authentication], this field // SHALL be zero length m_request_context(), m_side(Connection_Side::Server) { BOTAN_ASSERT_NOMSG(client_hello.extensions().has()); const auto key_types = filter_signature_schemes(client_hello.signature_schemes()); const auto op_type = "tls-server"; const auto context = client_hello.sni_hostname(); if(cert_type == Certificate_Type::X509) { auto cert_chain = credentials_manager.find_cert_chain( key_types, to_algorithm_identifiers(client_hello.certificate_signature_schemes()), {}, op_type, context); // RFC 8446 4.4.2 // The server's certificate_list MUST always be non-empty. if(cert_chain.empty()) { throw TLS_Exception(Alert::HandshakeFailure, "No sufficient server certificate available"); } setup_entries(std::move(cert_chain), client_hello.extensions().get(), callbacks); } else if(cert_type == Certificate_Type::RawPublicKey) { auto raw_public_key = credentials_manager.find_raw_public_key(key_types, op_type, context); // RFC 8446 4.4.2 // If the RawPublicKey certificate type was negotiated, then the // certificate_list MUST contain no more than one CertificateEntry // [...]. // The server's certificate_list MUST always be non-empty if(!raw_public_key) { throw TLS_Exception(Alert::HandshakeFailure, "No sufficient server raw public key available"); } setup_entry(std::move(raw_public_key), callbacks); } } Certificate_13::Certificate_Entry::Certificate_Entry(TLS_Data_Reader& reader, const Connection_Side side, const Certificate_Type cert_type) { switch(cert_type) { case Certificate_Type::X509: // RFC 8446 4.2.2 // [...] each CertificateEntry contains a DER-encoded X.509 // certificate. m_certificate = X509_Certificate(reader.get_tls_length_value(3)); m_raw_public_key = m_certificate->subject_public_key(); break; case Certificate_Type::RawPublicKey: // RFC 7250 3. // This specification uses raw public keys whereby the already // available encoding used in a PKIX certificate in the form of a // SubjectPublicKeyInfo structure is reused. m_raw_public_key = X509::load_key(reader.get_tls_length_value(3)); break; default: throw TLS_Exception(Alert::InternalError, "Unknown certificate type"); } // Extensions are simply tacked at the end of the certificate entry. This // is a departure from the typical "tag-length-value" in a sense that the // Extensions deserializer needs the length value of the extensions. const auto extensions_length = reader.peek_uint16_t(); const auto exts_buf = reader.get_fixed(extensions_length + 2); TLS_Data_Reader exts_reader("extensions reader", exts_buf); m_extensions.deserialize(exts_reader, side, Handshake_Type::Certificate); if(cert_type == Certificate_Type::X509) { // RFC 8446 4.4.2 // Valid extensions for server certificates at present include the // OCSP Status extension [RFC6066] and the SignedCertificateTimestamp // extension [RFC6962]; future extensions may be defined for this // message as well. // // RFC 8446 4.4.2.1 // A server MAY request that a client present an OCSP response with its // certificate by sending an empty "status_request" extension in its // CertificateRequest message. if(m_extensions.contains_implemented_extensions_other_than({ Extension_Code::CertificateStatusRequest, // Extension_Code::SignedCertificateTimestamp })) { throw TLS_Exception(Alert::IllegalParameter, "Certificate Entry contained an extension that is not allowed"); } } else if(m_extensions.contains_implemented_extensions_other_than({})) { throw TLS_Exception( Alert::IllegalParameter, "Certificate Entry holding something else than a certificate contained unexpected extensions"); } } Certificate_13::Certificate_Entry::Certificate_Entry(X509_Certificate cert) : m_certificate(std::move(cert)), m_raw_public_key(m_certificate->subject_public_key()) {} Certificate_13::Certificate_Entry::Certificate_Entry(std::shared_ptr raw_public_key) : m_certificate(std::nullopt), m_raw_public_key(std::move(raw_public_key)) { BOTAN_ASSERT_NONNULL(m_raw_public_key); } const X509_Certificate& Certificate_13::Certificate_Entry::certificate() const { BOTAN_STATE_CHECK(has_certificate()); return m_certificate.value(); } std::shared_ptr Certificate_13::Certificate_Entry::public_key() const { BOTAN_ASSERT_NONNULL(m_raw_public_key); return m_raw_public_key; } std::vector Certificate_13::Certificate_Entry::serialize() const { return (has_certificate()) ? m_certificate->BER_encode() : X509::BER_encode(*m_raw_public_key); } /** * Deserialize a Certificate message */ Certificate_13::Certificate_13(const std::vector& buf, const Policy& policy, Connection_Side side, Certificate_Type cert_type) : m_side(side) { TLS_Data_Reader reader("cert message reader", buf); m_request_context = reader.get_range(1, 0, 255); // RFC 8446 4.4.2 // [...] in the case of server authentication, this field SHALL be zero length. if(m_side == Connection_Side::Server && !m_request_context.empty()) { throw TLS_Exception(Alert::IllegalParameter, "Server Certificate message must not contain a request context"); } const auto cert_entries_len = reader.get_uint24_t(); if(reader.remaining_bytes() != cert_entries_len) { throw TLS_Exception(Alert::DecodeError, "Certificate: Message malformed"); } const size_t max_size = policy.maximum_certificate_chain_size(); if(max_size > 0 && cert_entries_len > max_size) { throw Decoding_Error("Certificate chain exceeds policy specified maximum size"); } while(reader.has_remaining()) { m_entries.emplace_back(reader, side, cert_type); } // RFC 8446 4.4.2 // The server's certificate_list MUST always be non-empty. A client // will send an empty certificate_list if it does not have an // appropriate certificate to send in response to the server's // authentication request. if(m_entries.empty()) { // RFC 8446 4.4.2.4 // If the server supplies an empty Certificate message, the client MUST // abort the handshake with a "decode_error" alert. if(m_side == Connection_Side::Server) { throw TLS_Exception(Alert::DecodeError, "No certificates sent by server"); } return; } BOTAN_ASSERT_NOMSG(!m_entries.empty()); // RFC 8446 4.4.2.2 // The certificate type MUST be X.509v3 [RFC5280], unless explicitly // negotiated otherwise (e.g., [RFC7250]). // // TLS 1.0 through 1.3 all seem to require that the certificate be // precisely a v3 certificate. In fact the strict wording would seem // to require that every certificate in the chain be v3. But often // the intermediates are outside of the control of the server. // But, require that the leaf certificate be v3. if(cert_type == Certificate_Type::X509 && m_entries.front().certificate().x509_version() != 3) { throw TLS_Exception(Alert::BadCertificate, "The leaf certificate must be v3"); } // RFC 8446 4.4.2 // If the RawPublicKey certificate type was negotiated, then the // certificate_list MUST contain no more than one CertificateEntry. if(cert_type == Certificate_Type::RawPublicKey && m_entries.size() != 1) { throw TLS_Exception(Alert::IllegalParameter, "Certificate message contained more than one RawPublicKey"); } // Validate the provided (certificate) public key against our policy auto pubkey = public_key(); policy.check_peer_key_acceptable(*pubkey); if(!policy.allowed_signature_method(pubkey->algo_name())) { throw TLS_Exception(Alert::HandshakeFailure, "Rejecting " + pubkey->algo_name() + " signature"); } } /** * Serialize a Certificate message */ std::vector Certificate_13::serialize() const { std::vector buf; append_tls_length_value(buf, m_request_context, 1); std::vector entries; for(const auto& entry : m_entries) { append_tls_length_value(entries, entry.serialize(), 3); // Extensions are tacked at the end of certificate entries. Note that // Extensions::serialize() usually emits the required length field, // except when no extensions are added at all, then it returns an // empty buffer. // // TODO: look into this issue more generally when overhauling the // message marshalling. auto extensions = entry.extensions().serialize(m_side); entries += (!extensions.empty()) ? extensions : std::vector{0, 0}; } append_tls_length_value(buf, entries, 3); return buf; } } // namespace Botan::TLS