diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/third_party/botan/src/lib/tls/msg_server_kex.cpp | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/third_party/botan/src/lib/tls/msg_server_kex.cpp')
-rw-r--r-- | comm/third_party/botan/src/lib/tls/msg_server_kex.cpp | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/comm/third_party/botan/src/lib/tls/msg_server_kex.cpp b/comm/third_party/botan/src/lib/tls/msg_server_kex.cpp new file mode 100644 index 0000000000..797907ed82 --- /dev/null +++ b/comm/third_party/botan/src/lib/tls/msg_server_kex.cpp @@ -0,0 +1,334 @@ +/* +* Server Key Exchange Message +* (C) 2004-2010,2012,2015,2016 Jack Lloyd +* 2017 Harry Reimann, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/tls_messages.h> +#include <botan/tls_extensions.h> +#include <botan/internal/tls_reader.h> +#include <botan/internal/tls_handshake_io.h> +#include <botan/internal/tls_handshake_state.h> +#include <botan/credentials_manager.h> +#include <botan/loadstor.h> +#include <botan/pubkey.h> + +#include <botan/dh.h> +#include <botan/ecdh.h> + +#if defined(BOTAN_HAS_CURVE_25519) + #include <botan/curve25519.h> +#endif + +#if defined(BOTAN_HAS_CECPQ1) + #include <botan/cecpq1.h> +#endif + +#if defined(BOTAN_HAS_SRP6) + #include <botan/srp6.h> +#endif + +namespace Botan { + +namespace TLS { + +/** +* Create a new Server Key Exchange message +*/ +Server_Key_Exchange::Server_Key_Exchange(Handshake_IO& io, + Handshake_State& state, + const Policy& policy, + Credentials_Manager& creds, + RandomNumberGenerator& rng, + const Private_Key* signing_key) + { + const std::string hostname = state.client_hello()->sni_hostname(); + const Kex_Algo kex_algo = state.ciphersuite().kex_method(); + + if(kex_algo == Kex_Algo::PSK || kex_algo == Kex_Algo::DHE_PSK || kex_algo == Kex_Algo::ECDHE_PSK) + { + std::string identity_hint = + creds.psk_identity_hint("tls-server", hostname); + + append_tls_length_value(m_params, identity_hint, 2); + } + + if(kex_algo == Kex_Algo::DH || kex_algo == Kex_Algo::DHE_PSK) + { + const std::vector<Group_Params> dh_groups = state.client_hello()->supported_dh_groups(); + + Group_Params shared_group = Group_Params::NONE; + + /* + If the client does not send any DH groups in the supported groups + extension, but does offer DH ciphersuites, we select a group arbitrarily + */ + + if(dh_groups.empty()) + { + shared_group = policy.default_dh_group(); + } + else + { + shared_group = policy.choose_key_exchange_group(dh_groups); + } + + if(shared_group == Group_Params::NONE) + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Could not agree on a DH group with the client"); + + BOTAN_ASSERT(group_param_is_dh(shared_group), "DH groups for the DH ciphersuites god"); + + const std::string group_name = state.callbacks().tls_decode_group_param(shared_group); + std::unique_ptr<DH_PrivateKey> dh(new DH_PrivateKey(rng, DL_Group(group_name))); + + append_tls_length_value(m_params, BigInt::encode(dh->get_domain().get_p()), 2); + append_tls_length_value(m_params, BigInt::encode(dh->get_domain().get_g()), 2); + append_tls_length_value(m_params, dh->public_value(), 2); + m_kex_key.reset(dh.release()); + } + else if(kex_algo == Kex_Algo::ECDH || kex_algo == Kex_Algo::ECDHE_PSK) + { + const std::vector<Group_Params> ec_groups = state.client_hello()->supported_ecc_curves(); + + if(ec_groups.empty()) + throw Internal_Error("Client sent no ECC extension but we negotiated ECDH"); + + Group_Params shared_group = policy.choose_key_exchange_group(ec_groups); + + if(shared_group == Group_Params::NONE) + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "No shared ECC group with client"); + + std::vector<uint8_t> ecdh_public_val; + + if(shared_group == Group_Params::X25519) + { +#if defined(BOTAN_HAS_CURVE_25519) + std::unique_ptr<Curve25519_PrivateKey> x25519(new Curve25519_PrivateKey(rng)); + ecdh_public_val = x25519->public_value(); + m_kex_key.reset(x25519.release()); +#else + throw Internal_Error("Negotiated X25519 somehow, but it is disabled"); +#endif + } + else + { + Group_Params curve = policy.choose_key_exchange_group(ec_groups); + + const std::string curve_name = state.callbacks().tls_decode_group_param(curve); + + EC_Group ec_group(curve_name); + std::unique_ptr<ECDH_PrivateKey> ecdh(new ECDH_PrivateKey(rng, ec_group)); + + // follow client's preference for point compression + ecdh_public_val = ecdh->public_value( + state.client_hello()->prefers_compressed_ec_points() ? + PointGFp::COMPRESSED : PointGFp::UNCOMPRESSED); + + m_kex_key.reset(ecdh.release()); + } + + const uint16_t named_curve_id = static_cast<uint16_t>(shared_group); + m_params.push_back(3); // named curve + m_params.push_back(get_byte(0, named_curve_id)); + m_params.push_back(get_byte(1, named_curve_id)); + + append_tls_length_value(m_params, ecdh_public_val, 1); + } +#if defined(BOTAN_HAS_SRP6) + else if(kex_algo == Kex_Algo::SRP_SHA) + { + const std::string srp_identifier = state.client_hello()->srp_identifier(); + + std::string group_id; + BigInt v; + std::vector<uint8_t> salt; + + const bool found = creds.srp_verifier("tls-server", hostname, + srp_identifier, + group_id, v, salt, + policy.hide_unknown_users()); + + if(!found) + throw TLS_Exception(Alert::UNKNOWN_PSK_IDENTITY, + "Unknown SRP user " + srp_identifier); + + m_srp_params.reset(new SRP6_Server_Session); + + BigInt B = m_srp_params->step1(v, group_id, + "SHA-1", rng); + + DL_Group group(group_id); + + append_tls_length_value(m_params, BigInt::encode(group.get_p()), 2); + append_tls_length_value(m_params, BigInt::encode(group.get_g()), 2); + append_tls_length_value(m_params, salt, 1); + append_tls_length_value(m_params, BigInt::encode(B), 2); + } +#endif +#if defined(BOTAN_HAS_CECPQ1) + else if(kex_algo == Kex_Algo::CECPQ1) + { + std::vector<uint8_t> cecpq1_offer(CECPQ1_OFFER_BYTES); + m_cecpq1_key.reset(new CECPQ1_key); + CECPQ1_offer(cecpq1_offer.data(), m_cecpq1_key.get(), rng); + append_tls_length_value(m_params, cecpq1_offer, 2); + } +#endif + else if(kex_algo != Kex_Algo::PSK) + { + throw Internal_Error("Server_Key_Exchange: Unknown kex type " + + kex_method_to_string(kex_algo)); + } + + if(state.ciphersuite().signature_used()) + { + BOTAN_ASSERT(signing_key, "Signing key was set"); + + std::pair<std::string, Signature_Format> format = + state.choose_sig_format(*signing_key, m_scheme, false, policy); + + std::vector<uint8_t> buf = state.client_hello()->random(); + + buf += state.server_hello()->random(); + buf += params(); + + m_signature = + state.callbacks().tls_sign_message(*signing_key, rng, + format.first, format.second, buf); + } + + state.hash().update(io.send(*this)); + } + +/** +* Deserialize a Server Key Exchange message +*/ +Server_Key_Exchange::Server_Key_Exchange(const std::vector<uint8_t>& buf, + const Kex_Algo kex_algo, + const Auth_Method auth_method, + Protocol_Version version) + { + TLS_Data_Reader reader("ServerKeyExchange", buf); + + /* + * Here we are deserializing enough to find out what offset the + * signature is at. All processing is done when the Client Key Exchange + * is prepared. + */ + + if(kex_algo == Kex_Algo::PSK || kex_algo == Kex_Algo::DHE_PSK || kex_algo == Kex_Algo::ECDHE_PSK) + { + reader.get_string(2, 0, 65535); // identity hint + } + + if(kex_algo == Kex_Algo::DH || kex_algo == Kex_Algo::DHE_PSK) + { + // 3 bigints, DH p, g, Y + + for(size_t i = 0; i != 3; ++i) + { + reader.get_range<uint8_t>(2, 1, 65535); + } + } + else if(kex_algo == Kex_Algo::ECDH || kex_algo == Kex_Algo::ECDHE_PSK) + { + reader.get_byte(); // curve type + reader.get_uint16_t(); // curve id + reader.get_range<uint8_t>(1, 1, 255); // public key + } + else if(kex_algo == Kex_Algo::SRP_SHA) + { + // 2 bigints (N,g) then salt, then server B + + reader.get_range<uint8_t>(2, 1, 65535); + reader.get_range<uint8_t>(2, 1, 65535); + reader.get_range<uint8_t>(1, 1, 255); + reader.get_range<uint8_t>(2, 1, 65535); + } + else if(kex_algo == Kex_Algo::CECPQ1) + { + // u16 blob + reader.get_range<uint8_t>(2, 1, 65535); + } + else if(kex_algo != Kex_Algo::PSK) + throw Decoding_Error("Server_Key_Exchange: Unsupported kex type " + + kex_method_to_string(kex_algo)); + + m_params.assign(buf.data(), buf.data() + reader.read_so_far()); + + if(auth_method != Auth_Method::ANONYMOUS && auth_method != Auth_Method::IMPLICIT) + { + if(version.supports_negotiable_signature_algorithms()) + { + m_scheme = static_cast<Signature_Scheme>(reader.get_uint16_t()); + } + + m_signature = reader.get_range<uint8_t>(2, 0, 65535); + } + + reader.assert_done(); + } + +/** +* Serialize a Server Key Exchange message +*/ +std::vector<uint8_t> Server_Key_Exchange::serialize() const + { + std::vector<uint8_t> buf = params(); + + if(m_signature.size()) + { + if(m_scheme != Signature_Scheme::NONE) + { + const uint16_t scheme_code = static_cast<uint16_t>(m_scheme); + buf.push_back(get_byte(0, scheme_code)); + buf.push_back(get_byte(1, scheme_code)); + } + + append_tls_length_value(buf, m_signature, 2); + } + + return buf; + } + +/** +* Verify a Server Key Exchange message +*/ +bool Server_Key_Exchange::verify(const Public_Key& server_key, + const Handshake_State& state, + const Policy& policy) const + { + policy.check_peer_key_acceptable(server_key); + + std::pair<std::string, Signature_Format> format = + state.parse_sig_format(server_key, m_scheme, false, policy); + + std::vector<uint8_t> buf = state.client_hello()->random(); + + buf += state.server_hello()->random(); + buf += params(); + + const bool signature_valid = + state.callbacks().tls_verify_message(server_key, format.first, format.second, + buf, m_signature); + +#if defined(BOTAN_UNSAFE_FUZZER_MODE) + BOTAN_UNUSED(signature_valid); + return true; +#else + return signature_valid; +#endif + } + +const Private_Key& Server_Key_Exchange::server_kex_key() const + { + BOTAN_ASSERT_NONNULL(m_kex_key); + return *m_kex_key; + } + +} + +} |