summaryrefslogtreecommitdiffstats
path: root/comm/third_party/botan/src/lib/tls/tls_server.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/third_party/botan/src/lib/tls/tls_server.cpp')
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_server.cpp1025
1 files changed, 1025 insertions, 0 deletions
diff --git a/comm/third_party/botan/src/lib/tls/tls_server.cpp b/comm/third_party/botan/src/lib/tls/tls_server.cpp
new file mode 100644
index 0000000000..e2a0bf2428
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_server.cpp
@@ -0,0 +1,1025 @@
+/*
+* TLS Server
+* (C) 2004-2011,2012,2016 Jack Lloyd
+* 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_server.h>
+#include <botan/tls_messages.h>
+#include <botan/internal/tls_handshake_state.h>
+#include <botan/internal/stl_util.h>
+#include <botan/tls_magic.h>
+
+namespace Botan {
+
+namespace TLS {
+
+class Server_Handshake_State final : public Handshake_State
+ {
+ public:
+ Server_Handshake_State(Handshake_IO* io, Callbacks& cb)
+ : Handshake_State(io, cb) {}
+
+ Private_Key* server_rsa_kex_key() { return m_server_rsa_kex_key; }
+ void set_server_rsa_kex_key(Private_Key* key)
+ { m_server_rsa_kex_key = key; }
+
+ bool allow_session_resumption() const
+ { return m_allow_session_resumption; }
+ void set_allow_session_resumption(bool allow_session_resumption)
+ { m_allow_session_resumption = allow_session_resumption; }
+
+ const std::vector<X509_Certificate>& resume_peer_certs() const
+ { return m_resume_peer_certs; }
+
+ void set_resume_certs(const std::vector<X509_Certificate>& certs)
+ { m_resume_peer_certs = certs; }
+
+ void mark_as_resumption() { m_is_a_resumption = true; }
+
+ bool is_a_resumption() const { return m_is_a_resumption; }
+
+ private:
+ // Used by the server only, in case of RSA key exchange. Not owned
+ Private_Key* m_server_rsa_kex_key = nullptr;
+
+ /*
+ * Used by the server to know if resumption should be allowed on
+ * a server-initiated renegotiation
+ */
+ bool m_allow_session_resumption = true;
+
+ bool m_is_a_resumption = false;
+
+ std::vector<X509_Certificate> m_resume_peer_certs;
+ };
+
+namespace {
+
+bool check_for_resume(Session& session_info,
+ Session_Manager& session_manager,
+ Credentials_Manager& credentials,
+ const Client_Hello* client_hello,
+ std::chrono::seconds session_ticket_lifetime)
+ {
+ const std::vector<uint8_t>& client_session_id = client_hello->session_id();
+ const std::vector<uint8_t>& session_ticket = client_hello->session_ticket();
+
+ if(session_ticket.empty())
+ {
+ if(client_session_id.empty()) // not resuming
+ return false;
+
+ // not found
+ if(!session_manager.load_from_session_id(client_session_id, session_info))
+ return false;
+ }
+ else
+ {
+ // If a session ticket was sent, ignore client session ID
+ try
+ {
+ session_info = Session::decrypt(
+ session_ticket,
+ credentials.psk("tls-server", "session-ticket", ""));
+
+ if(session_ticket_lifetime != std::chrono::seconds(0) &&
+ session_info.session_age() > session_ticket_lifetime)
+ return false; // ticket has expired
+ }
+ catch(...)
+ {
+ return false;
+ }
+ }
+
+ // wrong version
+ if(client_hello->version() != session_info.version())
+ return false;
+
+ // client didn't send original ciphersuite
+ if(!value_exists(client_hello->ciphersuites(),
+ session_info.ciphersuite_code()))
+ return false;
+
+#if defined(BOTAN_HAS_SRP6)
+ // client sent a different SRP identity
+ if(client_hello->srp_identifier() != "")
+ {
+ if(client_hello->srp_identifier() != session_info.srp_identifier())
+ return false;
+ }
+#endif
+
+ // client sent a different SNI hostname
+ if(client_hello->sni_hostname() != "")
+ {
+ if(client_hello->sni_hostname() != session_info.server_info().hostname())
+ return false;
+ }
+
+ // Checking extended_master_secret on resume (RFC 7627 section 5.3)
+ if(client_hello->supports_extended_master_secret() != session_info.supports_extended_master_secret())
+ {
+ if(!session_info.supports_extended_master_secret())
+ {
+ return false; // force new handshake with extended master secret
+ }
+ else
+ {
+ /*
+ Client previously negotiated session with extended master secret,
+ but has now attempted to resume without the extension: abort
+ */
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Client resumed extended ms session without sending extension");
+ }
+ }
+
+ // Checking encrypt_then_mac on resume (RFC 7366 section 3.1)
+ if(!client_hello->supports_encrypt_then_mac() && session_info.supports_encrypt_then_mac())
+ {
+ /*
+ Client previously negotiated session with Encrypt-then-MAC,
+ but has now attempted to resume without the extension: abort
+ */
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Client resumed Encrypt-then-MAC session without sending extension");
+ }
+
+ return true;
+ }
+
+/*
+* Choose which ciphersuite to use
+*/
+uint16_t choose_ciphersuite(
+ const Policy& policy,
+ Protocol_Version version,
+ Credentials_Manager& creds,
+ const std::map<std::string, std::vector<X509_Certificate>>& cert_chains,
+ const Client_Hello& client_hello)
+ {
+ const bool our_choice = policy.server_uses_own_ciphersuite_preferences();
+ const bool have_srp = creds.attempt_srp("tls-server", client_hello.sni_hostname());
+ const std::vector<uint16_t> client_suites = client_hello.ciphersuites();
+ const std::vector<uint16_t> server_suites = policy.ciphersuite_list(version, have_srp);
+
+ if(server_suites.empty())
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Policy forbids us from negotiating any ciphersuite");
+
+ const bool have_shared_ecc_curve =
+ (policy.choose_key_exchange_group(client_hello.supported_ecc_curves()) != Group_Params::NONE);
+
+ /*
+ Walk down one list in preference order
+ */
+ std::vector<uint16_t> pref_list = server_suites;
+ std::vector<uint16_t> other_list = client_suites;
+
+ if(!our_choice)
+ std::swap(pref_list, other_list);
+
+ for(auto suite_id : pref_list)
+ {
+ if(!value_exists(other_list, suite_id))
+ continue;
+
+ const Ciphersuite suite = Ciphersuite::by_id(suite_id);
+
+ if(suite.valid() == false)
+ {
+ continue;
+ }
+
+ if(have_shared_ecc_curve == false && suite.ecc_ciphersuite())
+ {
+ continue;
+ }
+
+ // For non-anon ciphersuites
+ if(suite.signature_used())
+ {
+ const std::string sig_algo = suite.sig_algo();
+
+ // Do we have any certificates for this sig?
+ if(cert_chains.count(sig_algo) == 0)
+ {
+ continue;
+ }
+
+ if(version.supports_negotiable_signature_algorithms())
+ {
+ const std::vector<Signature_Scheme> allowed =
+ policy.allowed_signature_schemes();
+
+ std::vector<Signature_Scheme> client_sig_methods =
+ client_hello.signature_schemes();
+
+ if(client_sig_methods.empty())
+ {
+ // If empty, then implicit SHA-1 (TLS v1.2 rules)
+ client_sig_methods.push_back(Signature_Scheme::RSA_PKCS1_SHA1);
+ client_sig_methods.push_back(Signature_Scheme::ECDSA_SHA1);
+ client_sig_methods.push_back(Signature_Scheme::DSA_SHA1);
+ }
+
+ bool we_support_some_hash_by_client = false;
+
+ for(Signature_Scheme scheme : client_sig_methods)
+ {
+ if(signature_scheme_is_known(scheme) == false)
+ continue;
+
+ if(signature_algorithm_of_scheme(scheme) == suite.sig_algo() &&
+ policy.allowed_signature_hash(hash_function_of_scheme(scheme)))
+ {
+ we_support_some_hash_by_client = true;
+ }
+ }
+
+ if(we_support_some_hash_by_client == false)
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Policy does not accept any hash function supported by client");
+ }
+ }
+ }
+
+#if defined(BOTAN_HAS_SRP6)
+ /*
+ The client may offer SRP cipher suites in the hello message but
+ omit the SRP extension. If the server would like to select an
+ SRP cipher suite in this case, the server SHOULD return a fatal
+ "unknown_psk_identity" alert immediately after processing the
+ client hello message.
+ - RFC 5054 section 2.5.1.2
+ */
+ if(suite.kex_method() == Kex_Algo::SRP_SHA && client_hello.srp_identifier() == "")
+ throw TLS_Exception(Alert::UNKNOWN_PSK_IDENTITY,
+ "Client wanted SRP but did not send username");
+#endif
+
+ return suite_id;
+ }
+
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Can't agree on a ciphersuite with client");
+ }
+
+std::map<std::string, std::vector<X509_Certificate>>
+get_server_certs(const std::string& hostname,
+ Credentials_Manager& creds)
+ {
+ const char* cert_types[] = { "RSA", "ECDSA", "DSA", nullptr };
+
+ std::map<std::string, std::vector<X509_Certificate>> cert_chains;
+
+ for(size_t i = 0; cert_types[i]; ++i)
+ {
+ const std::vector<X509_Certificate> certs =
+ creds.cert_chain_single_type(cert_types[i], "tls-server", hostname);
+
+ if(!certs.empty())
+ cert_chains[cert_types[i]] = certs;
+ }
+
+ return cert_chains;
+ }
+
+}
+
+/*
+* TLS Server Constructor
+*/
+Server::Server(Callbacks& callbacks,
+ Session_Manager& session_manager,
+ Credentials_Manager& creds,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ bool is_datagram,
+ size_t io_buf_sz) :
+ Channel(callbacks, session_manager, rng, policy,
+ true, is_datagram, io_buf_sz),
+ m_creds(creds)
+ {
+ }
+
+Server::Server(output_fn output,
+ data_cb got_data_cb,
+ alert_cb recv_alert_cb,
+ handshake_cb hs_cb,
+ Session_Manager& session_manager,
+ Credentials_Manager& creds,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ next_protocol_fn next_proto,
+ bool is_datagram,
+ size_t io_buf_sz) :
+ Channel(output, got_data_cb, recv_alert_cb, hs_cb,
+ Channel::handshake_msg_cb(), session_manager,
+ rng, policy, true, is_datagram, io_buf_sz),
+ m_creds(creds),
+ m_choose_next_protocol(next_proto)
+ {
+ }
+
+Server::Server(output_fn output,
+ data_cb got_data_cb,
+ alert_cb recv_alert_cb,
+ handshake_cb hs_cb,
+ handshake_msg_cb hs_msg_cb,
+ Session_Manager& session_manager,
+ Credentials_Manager& creds,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ next_protocol_fn next_proto,
+ bool is_datagram) :
+ Channel(output, got_data_cb, recv_alert_cb, hs_cb, hs_msg_cb,
+ session_manager, rng, policy, true, is_datagram),
+ m_creds(creds),
+ m_choose_next_protocol(next_proto)
+ {
+ }
+
+Handshake_State* Server::new_handshake_state(Handshake_IO* io)
+ {
+ std::unique_ptr<Handshake_State> state(new Server_Handshake_State(io, callbacks()));
+
+ state->set_expected_next(CLIENT_HELLO);
+ return state.release();
+ }
+
+std::vector<X509_Certificate>
+Server::get_peer_cert_chain(const Handshake_State& state_base) const
+ {
+ const Server_Handshake_State& state = dynamic_cast<const Server_Handshake_State&>(state_base);
+ if(state.resume_peer_certs().size() > 0)
+ return state.resume_peer_certs();
+
+ if(state.client_certs())
+ return state.client_certs()->cert_chain();
+ return std::vector<X509_Certificate>();
+ }
+
+/*
+* Send a hello request to the client
+*/
+void Server::initiate_handshake(Handshake_State& state,
+ bool force_full_renegotiation)
+ {
+ dynamic_cast<Server_Handshake_State&>(state).
+ set_allow_session_resumption(!force_full_renegotiation);
+
+ Hello_Request hello_req(state.handshake_io());
+ }
+
+namespace {
+
+Protocol_Version select_version(const Botan::TLS::Policy& policy,
+ Protocol_Version client_offer,
+ Protocol_Version active_version,
+ bool is_fallback,
+ const std::vector<Protocol_Version>& supported_versions)
+ {
+ const bool is_datagram = client_offer.is_datagram_protocol();
+ const bool initial_handshake = (active_version.valid() == false);
+
+ const Protocol_Version latest_supported = policy.latest_supported_version(is_datagram);
+
+ if(is_fallback)
+ {
+ if(latest_supported > client_offer)
+ throw TLS_Exception(Alert::INAPPROPRIATE_FALLBACK,
+ "Client signalled fallback SCSV, possible attack");
+ }
+
+ if(supported_versions.size() > 0)
+ {
+ if(is_datagram)
+ {
+ if(policy.allow_dtls12() && value_exists(supported_versions, Protocol_Version(Protocol_Version::DTLS_V12)))
+ return Protocol_Version::DTLS_V12;
+#if defined(BOTAN_HAS_TLS_V10)
+ if(policy.allow_dtls10() && value_exists(supported_versions, Protocol_Version(Protocol_Version::DTLS_V10)))
+ return Protocol_Version::DTLS_V10;
+#endif
+ throw TLS_Exception(Alert::PROTOCOL_VERSION, "No shared DTLS version");
+ }
+ else
+ {
+ if(policy.allow_tls12() && value_exists(supported_versions, Protocol_Version(Protocol_Version::TLS_V12)))
+ return Protocol_Version::TLS_V12;
+#if defined(BOTAN_HAS_TLS_V10)
+ if(policy.allow_tls11() && value_exists(supported_versions, Protocol_Version(Protocol_Version::TLS_V11)))
+ return Protocol_Version::TLS_V11;
+ if(policy.allow_tls10() && value_exists(supported_versions, Protocol_Version(Protocol_Version::TLS_V10)))
+ return Protocol_Version::TLS_V10;
+#endif
+ throw TLS_Exception(Alert::PROTOCOL_VERSION, "No shared TLS version");
+ }
+ }
+
+ const bool client_offer_acceptable =
+ client_offer.known_version() && policy.acceptable_protocol_version(client_offer);
+
+ if(!initial_handshake)
+ {
+ /*
+ * If this is a renegotiation, and the client has offered a
+ * later version than what it initially negotiated, negotiate
+ * the old version. This matches OpenSSL's behavior. If the
+ * client is offering a version earlier than what it initially
+ * negotiated, reject as a probable attack.
+ */
+ if(active_version > client_offer)
+ {
+ throw TLS_Exception(Alert::PROTOCOL_VERSION,
+ "Client negotiated " +
+ active_version.to_string() +
+ " then renegotiated with " +
+ client_offer.to_string());
+ }
+ else
+ {
+ return active_version;
+ }
+ }
+ else if(client_offer_acceptable)
+ {
+ return client_offer;
+ }
+ else if(!client_offer.known_version() || client_offer > latest_supported)
+ {
+ /*
+ The client offered some version newer than the latest we
+ support. Offer them the best we know.
+ */
+ return latest_supported;
+ }
+ else
+ {
+ throw TLS_Exception(Alert::PROTOCOL_VERSION,
+ "Client version " + client_offer.to_string() +
+ " is unacceptable by policy");
+ }
+ }
+
+}
+
+/*
+* Process a CLIENT HELLO Message
+*/
+void Server::process_client_hello_msg(const Handshake_State* active_state,
+ Server_Handshake_State& pending_state,
+ const std::vector<uint8_t>& contents,
+ bool epoch0_restart)
+ {
+ BOTAN_ASSERT_IMPLICATION(epoch0_restart, active_state != nullptr, "Can't restart with a dead connection");
+
+ const bool initial_handshake = epoch0_restart || !active_state;
+
+ if(initial_handshake == false && policy().allow_client_initiated_renegotiation() == false)
+ {
+ if(policy().abort_connection_on_undesired_renegotiation())
+ throw TLS_Exception(Alert::NO_RENEGOTIATION, "Server policy prohibits renegotiation");
+ else
+ send_warning_alert(Alert::NO_RENEGOTIATION);
+ return;
+ }
+
+ if(!policy().allow_insecure_renegotiation() &&
+ !(initial_handshake || secure_renegotiation_supported()))
+ {
+ send_warning_alert(Alert::NO_RENEGOTIATION);
+ return;
+ }
+
+ pending_state.client_hello(new Client_Hello(contents));
+ const Protocol_Version client_offer = pending_state.client_hello()->version();
+ const bool datagram = client_offer.is_datagram_protocol();
+
+ if(datagram)
+ {
+ if(client_offer.major_version() == 0xFF)
+ throw TLS_Exception(Alert::PROTOCOL_VERSION, "Client offered DTLS version with major version 0xFF");
+ }
+ else
+ {
+ if(client_offer.major_version() < 3)
+ throw TLS_Exception(Alert::PROTOCOL_VERSION, "Client offered TLS version with major version under 3");
+ if(client_offer.major_version() == 3 && client_offer.minor_version() == 0)
+ throw TLS_Exception(Alert::PROTOCOL_VERSION, "SSLv3 is not supported");
+ }
+
+ /*
+ * BoGo test suite expects that we will send the hello verify with a record
+ * version matching the version that is eventually negotiated. This is wrong
+ * but harmless, so go with it. Also doing the version negotiation step first
+ * allows to immediately close the connection with an alert if the client has
+ * offered a version that we are not going to negotiate anyway, instead of
+ * making them first do the cookie exchange and then telling them no.
+ *
+ * There is no issue with amplification here, since the alert is just 2 bytes.
+ */
+ const Protocol_Version negotiated_version =
+ select_version(policy(), client_offer,
+ active_state ? active_state->version() : Protocol_Version(),
+ pending_state.client_hello()->sent_fallback_scsv(),
+ pending_state.client_hello()->supported_versions());
+
+ pending_state.set_version(negotiated_version);
+
+ const auto compression_methods = pending_state.client_hello()->compression_methods();
+ if(!value_exists(compression_methods, uint8_t(0)))
+ throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Client did not offer NULL compression");
+
+ if(initial_handshake && datagram)
+ {
+ SymmetricKey cookie_secret;
+
+ try
+ {
+ cookie_secret = m_creds.psk("tls-server", "dtls-cookie-secret", "");
+ }
+ catch(...) {}
+
+ if(cookie_secret.size() > 0)
+ {
+ const std::string client_identity = callbacks().tls_peer_network_identity();
+ Hello_Verify_Request verify(pending_state.client_hello()->cookie_input_data(), client_identity, cookie_secret);
+
+ if(pending_state.client_hello()->cookie() != verify.cookie())
+ {
+ if(epoch0_restart)
+ pending_state.handshake_io().send_under_epoch(verify, 0);
+ else
+ pending_state.handshake_io().send(verify);
+
+ pending_state.client_hello(nullptr);
+ pending_state.set_expected_next(CLIENT_HELLO);
+ return;
+ }
+ }
+ else if(epoch0_restart)
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Reuse of DTLS association requires DTLS cookie secret be set");
+ }
+ }
+
+ if(epoch0_restart)
+ {
+ // If we reached here then we were able to verify the cookie
+ reset_active_association_state();
+ }
+
+ secure_renegotiation_check(pending_state.client_hello());
+
+ callbacks().tls_examine_extensions(pending_state.client_hello()->extensions(), CLIENT);
+
+ Session session_info;
+ const bool resuming =
+ pending_state.allow_session_resumption() &&
+ check_for_resume(session_info,
+ session_manager(),
+ m_creds,
+ pending_state.client_hello(),
+ std::chrono::seconds(policy().session_ticket_lifetime()));
+
+ bool have_session_ticket_key = false;
+
+ try
+ {
+ have_session_ticket_key =
+ m_creds.psk("tls-server", "session-ticket", "").length() > 0;
+ }
+ catch(...) {}
+
+ m_next_protocol = "";
+ if(pending_state.client_hello()->supports_alpn())
+ {
+ m_next_protocol = callbacks().tls_server_choose_app_protocol(pending_state.client_hello()->next_protocols());
+
+ // if the callback return was empty, fall back to the (deprecated) std::function
+ if(m_next_protocol.empty() && m_choose_next_protocol)
+ {
+ m_next_protocol = m_choose_next_protocol(pending_state.client_hello()->next_protocols());
+ }
+ }
+
+ if(resuming)
+ {
+ this->session_resume(pending_state, have_session_ticket_key, session_info);
+ }
+ else // new session
+ {
+ this->session_create(pending_state, have_session_ticket_key);
+ }
+ }
+
+void Server::process_certificate_msg(Server_Handshake_State& pending_state,
+ const std::vector<uint8_t>& contents)
+ {
+ pending_state.client_certs(new Certificate(contents, policy()));
+
+ // CERTIFICATE_REQUIRED would make more sense but BoGo expects handshake failure alert
+ if(pending_state.client_certs()->empty() && policy().require_client_certificate_authentication())
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Policy requires client send a certificate, but it did not");
+
+ pending_state.set_expected_next(CLIENT_KEX);
+ }
+
+void Server::process_client_key_exchange_msg(Server_Handshake_State& pending_state,
+ const std::vector<uint8_t>& contents)
+ {
+ if(pending_state.received_handshake_msg(CERTIFICATE) && !pending_state.client_certs()->empty())
+ pending_state.set_expected_next(CERTIFICATE_VERIFY);
+ else
+ pending_state.set_expected_next(HANDSHAKE_CCS);
+
+ pending_state.client_kex(new Client_Key_Exchange(contents, pending_state,
+ pending_state.server_rsa_kex_key(),
+ m_creds, policy(), rng()));
+
+ pending_state.compute_session_keys();
+ }
+
+void Server::process_change_cipher_spec_msg(Server_Handshake_State& pending_state)
+ {
+ pending_state.set_expected_next(FINISHED);
+ change_cipher_spec_reader(SERVER);
+ }
+
+void Server::process_certificate_verify_msg(Server_Handshake_State& pending_state,
+ Handshake_Type type,
+ const std::vector<uint8_t>& contents)
+ {
+ pending_state.client_verify(new Certificate_Verify(contents, pending_state.version()));
+
+ const std::vector<X509_Certificate>& client_certs =
+ pending_state.client_certs()->cert_chain();
+
+ const bool sig_valid =
+ pending_state.client_verify()->verify(client_certs[0], pending_state, policy());
+
+ pending_state.hash().update(pending_state.handshake_io().format(contents, type));
+
+ /*
+ * Using DECRYPT_ERROR looks weird here, but per RFC 4346 is for
+ * "A handshake cryptographic operation failed, including being
+ * unable to correctly verify a signature, ..."
+ */
+ if(!sig_valid)
+ throw TLS_Exception(Alert::DECRYPT_ERROR, "Client cert verify failed");
+
+ try
+ {
+ const std::string sni_hostname = pending_state.client_hello()->sni_hostname();
+ auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-server", sni_hostname);
+
+ callbacks().tls_verify_cert_chain(client_certs,
+ {}, // ocsp
+ trusted_CAs,
+ Usage_Type::TLS_CLIENT_AUTH,
+ sni_hostname,
+ policy());
+ }
+ catch(std::exception& e)
+ {
+ throw TLS_Exception(Alert::BAD_CERTIFICATE, e.what());
+ }
+
+ pending_state.set_expected_next(HANDSHAKE_CCS);
+ }
+
+void Server::process_finished_msg(Server_Handshake_State& pending_state,
+ Handshake_Type type,
+ const std::vector<uint8_t>& contents)
+ {
+ pending_state.set_expected_next(HANDSHAKE_NONE);
+
+ pending_state.client_finished(new Finished(contents));
+
+ if(!pending_state.client_finished()->verify(pending_state, CLIENT))
+ throw TLS_Exception(Alert::DECRYPT_ERROR,
+ "Finished message didn't verify");
+
+ if(!pending_state.server_finished())
+ {
+ // already sent finished if resuming, so this is a new session
+
+ pending_state.hash().update(pending_state.handshake_io().format(contents, type));
+
+ Session session_info(
+ pending_state.server_hello()->session_id(),
+ pending_state.session_keys().master_secret(),
+ pending_state.server_hello()->version(),
+ pending_state.server_hello()->ciphersuite(),
+ SERVER,
+ pending_state.server_hello()->supports_extended_master_secret(),
+ pending_state.server_hello()->supports_encrypt_then_mac(),
+ get_peer_cert_chain(pending_state),
+ std::vector<uint8_t>(),
+ Server_Information(pending_state.client_hello()->sni_hostname()),
+ pending_state.srp_identifier(),
+ pending_state.server_hello()->srtp_profile());
+
+ if(save_session(session_info))
+ {
+ if(pending_state.server_hello()->supports_session_ticket())
+ {
+ try
+ {
+ const SymmetricKey ticket_key = m_creds.psk("tls-server", "session-ticket", "");
+
+ pending_state.new_session_ticket(
+ new New_Session_Ticket(pending_state.handshake_io(),
+ pending_state.hash(),
+ session_info.encrypt(ticket_key, rng()),
+ policy().session_ticket_lifetime()));
+ }
+ catch(...) {}
+ }
+ else
+ session_manager().save(session_info);
+ }
+
+ if(!pending_state.new_session_ticket() &&
+ pending_state.server_hello()->supports_session_ticket())
+ {
+ pending_state.new_session_ticket(
+ new New_Session_Ticket(pending_state.handshake_io(), pending_state.hash()));
+ }
+
+ pending_state.handshake_io().send(Change_Cipher_Spec());
+
+ change_cipher_spec_writer(SERVER);
+
+ pending_state.server_finished(new Finished(pending_state.handshake_io(), pending_state, SERVER));
+ }
+
+ activate_session();
+ }
+
+/*
+* Process a handshake message
+*/
+void Server::process_handshake_msg(const Handshake_State* active_state,
+ Handshake_State& state_base,
+ Handshake_Type type,
+ const std::vector<uint8_t>& contents,
+ bool epoch0_restart)
+ {
+ Server_Handshake_State& state = dynamic_cast<Server_Handshake_State&>(state_base);
+ state.confirm_transition_to(type);
+
+ /*
+ * The change cipher spec message isn't technically a handshake
+ * message so it's not included in the hash. The finished and
+ * certificate verify messages are verified based on the current
+ * state of the hash *before* this message so we delay adding them
+ * to the hash computation until we've processed them below.
+ */
+ if(type != HANDSHAKE_CCS && type != FINISHED && type != CERTIFICATE_VERIFY)
+ {
+ state.hash().update(state.handshake_io().format(contents, type));
+ }
+
+ switch(type)
+ {
+ case CLIENT_HELLO:
+ return this->process_client_hello_msg(active_state, state, contents, epoch0_restart);
+
+ case CERTIFICATE:
+ return this->process_certificate_msg(state, contents);
+
+ case CLIENT_KEX:
+ return this->process_client_key_exchange_msg(state, contents);
+
+ case CERTIFICATE_VERIFY:
+ return this->process_certificate_verify_msg(state, type, contents);
+
+ case HANDSHAKE_CCS:
+ return this->process_change_cipher_spec_msg(state);
+
+ case FINISHED:
+ return this->process_finished_msg(state, type, contents);
+
+ default:
+ throw Unexpected_Message("Unknown handshake message received");
+ }
+ }
+
+void Server::session_resume(Server_Handshake_State& pending_state,
+ bool have_session_ticket_key,
+ Session& session_info)
+ {
+ // Only offer a resuming client a new ticket if they didn't send one this time,
+ // ie, resumed via server-side resumption. TODO: also send one if expiring soon?
+
+ const bool offer_new_session_ticket =
+ (pending_state.client_hello()->supports_session_ticket() &&
+ pending_state.client_hello()->session_ticket().empty() &&
+ have_session_ticket_key);
+
+ pending_state.server_hello(new Server_Hello(
+ pending_state.handshake_io(),
+ pending_state.hash(),
+ policy(),
+ callbacks(),
+ rng(),
+ secure_renegotiation_data_for_server_hello(),
+ *pending_state.client_hello(),
+ session_info,
+ offer_new_session_ticket,
+ m_next_protocol));
+
+ secure_renegotiation_check(pending_state.server_hello());
+
+ pending_state.mark_as_resumption();
+ pending_state.compute_session_keys(session_info.master_secret());
+ pending_state.set_resume_certs(session_info.peer_certs());
+
+ if(!save_session(session_info))
+ {
+ session_manager().remove_entry(session_info.session_id());
+
+ if(pending_state.server_hello()->supports_session_ticket()) // send an empty ticket
+ {
+ pending_state.new_session_ticket(
+ new New_Session_Ticket(pending_state.handshake_io(),
+ pending_state.hash()));
+ }
+ }
+
+ if(pending_state.server_hello()->supports_session_ticket() && !pending_state.new_session_ticket())
+ {
+ try
+ {
+ const SymmetricKey ticket_key = m_creds.psk("tls-server", "session-ticket", "");
+
+ pending_state.new_session_ticket(
+ new New_Session_Ticket(pending_state.handshake_io(),
+ pending_state.hash(),
+ session_info.encrypt(ticket_key, rng()),
+ policy().session_ticket_lifetime()));
+ }
+ catch(...) {}
+
+ if(!pending_state.new_session_ticket())
+ {
+ pending_state.new_session_ticket(
+ new New_Session_Ticket(pending_state.handshake_io(), pending_state.hash()));
+ }
+ }
+
+ pending_state.handshake_io().send(Change_Cipher_Spec());
+
+ change_cipher_spec_writer(SERVER);
+
+ pending_state.server_finished(new Finished(pending_state.handshake_io(), pending_state, SERVER));
+ pending_state.set_expected_next(HANDSHAKE_CCS);
+ }
+
+void Server::session_create(Server_Handshake_State& pending_state,
+ bool have_session_ticket_key)
+ {
+ std::map<std::string, std::vector<X509_Certificate>> cert_chains;
+
+ const std::string sni_hostname = pending_state.client_hello()->sni_hostname();
+
+ cert_chains = get_server_certs(sni_hostname, m_creds);
+
+ if(sni_hostname != "" && cert_chains.empty())
+ {
+ cert_chains = get_server_certs("", m_creds);
+
+ /*
+ * Only send the unrecognized_name alert if we couldn't
+ * find any certs for the requested name but did find at
+ * least one cert to use in general. That avoids sending an
+ * unrecognized_name when a server is configured for purely
+ * anonymous/PSK operation.
+ */
+ if(!cert_chains.empty())
+ send_warning_alert(Alert::UNRECOGNIZED_NAME);
+ }
+
+ const uint16_t ciphersuite = choose_ciphersuite(policy(), pending_state.version(),
+ m_creds, cert_chains,
+ *pending_state.client_hello());
+
+ Server_Hello::Settings srv_settings(
+ make_hello_random(rng(), policy()), // new session ID
+ pending_state.version(),
+ ciphersuite,
+ have_session_ticket_key);
+
+ pending_state.server_hello(new Server_Hello(
+ pending_state.handshake_io(),
+ pending_state.hash(),
+ policy(),
+ callbacks(),
+ rng(),
+ secure_renegotiation_data_for_server_hello(),
+ *pending_state.client_hello(),
+ srv_settings,
+ m_next_protocol));
+
+ secure_renegotiation_check(pending_state.server_hello());
+
+ const Ciphersuite& pending_suite = pending_state.ciphersuite();
+
+ Private_Key* private_key = nullptr;
+
+ if(pending_suite.signature_used() || pending_suite.kex_method() == Kex_Algo::STATIC_RSA)
+ {
+ const std::string algo_used =
+ pending_suite.signature_used() ? pending_suite.sig_algo() : "RSA";
+
+ BOTAN_ASSERT(!cert_chains[algo_used].empty(),
+ "Attempting to send empty certificate chain");
+
+ pending_state.server_certs(new Certificate(pending_state.handshake_io(),
+ pending_state.hash(),
+ cert_chains[algo_used]));
+
+ if(pending_state.client_hello()->supports_cert_status_message() && pending_state.is_a_resumption() == false)
+ {
+ auto csr = pending_state.client_hello()->extensions().get<Certificate_Status_Request>();
+ // csr is non-null if client_hello()->supports_cert_status_message()
+ BOTAN_ASSERT_NOMSG(csr != nullptr);
+ const auto resp_bytes = callbacks().tls_provide_cert_status(cert_chains[algo_used], *csr);
+ if(resp_bytes.size() > 0)
+ {
+ pending_state.server_cert_status(new Certificate_Status(
+ pending_state.handshake_io(),
+ pending_state.hash(),
+ resp_bytes
+ ));
+ }
+ }
+
+ private_key = m_creds.private_key_for(
+ pending_state.server_certs()->cert_chain()[0],
+ "tls-server",
+ sni_hostname);
+
+ if(!private_key)
+ throw Internal_Error("No private key located for associated server cert");
+ }
+
+ if(pending_suite.kex_method() == Kex_Algo::STATIC_RSA)
+ {
+ pending_state.set_server_rsa_kex_key(private_key);
+ }
+ else
+ {
+ pending_state.server_kex(new Server_Key_Exchange(pending_state.handshake_io(),
+ pending_state, policy(),
+ m_creds, rng(), private_key));
+ }
+
+ auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-server", sni_hostname);
+
+ std::vector<X509_DN> client_auth_CAs;
+
+ for(auto store : trusted_CAs)
+ {
+ auto subjects = store->all_subjects();
+ client_auth_CAs.insert(client_auth_CAs.end(), subjects.begin(), subjects.end());
+ }
+
+ const bool request_cert =
+ (client_auth_CAs.empty() == false) ||
+ policy().request_client_certificate_authentication();
+
+ if(request_cert && pending_state.ciphersuite().signature_used())
+ {
+ pending_state.cert_req(
+ new Certificate_Req(pending_state.handshake_io(),
+ pending_state.hash(),
+ policy(),
+ client_auth_CAs,
+ pending_state.version()));
+
+ /*
+ SSLv3 allowed clients to skip the Certificate message entirely
+ if they wanted. In TLS v1.0 and later clients must send a
+ (possibly empty) Certificate message
+ */
+ pending_state.set_expected_next(CERTIFICATE);
+ }
+ else
+ {
+ pending_state.set_expected_next(CLIENT_KEX);
+ }
+
+ pending_state.server_hello_done(new Server_Hello_Done(pending_state.handshake_io(), pending_state.hash()));
+ }
+}
+
+}