/* * Server Key Exchange Message * (C) 2004-2010,2012 Jack Lloyd * * Released under the terms of the Botan license */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Botan { namespace TLS { /** * Create a new Server Key Exchange message */ Server_Key_Exchange::Server_Key_Exchange(Record_Writer& writer, Handshake_State* state, const Policy& policy, Credentials_Manager& creds, RandomNumberGenerator& rng, const Private_Key* signing_key) : m_kex_key(nullptr), m_srp_params(nullptr) { const std::string hostname = state->client_hello->sni_hostname(); const std::string kex_algo = state->suite.kex_algo(); if(kex_algo == "PSK" || kex_algo == "DHE_PSK" || 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 == "DH" || kex_algo == "DHE_PSK") { std::unique_ptr dh(new DH_PrivateKey(rng, policy.dh_group())); 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 = dh.release(); } else if(kex_algo == "ECDH" || kex_algo == "ECDHE_PSK") { const std::vector& curves = state->client_hello->supported_ecc_curves(); if(curves.empty()) throw Internal_Error("Client sent no ECC extension but we negotiated ECDH"); const std::string curve_name = policy.choose_curve(curves); if(curve_name == "") throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Could not agree on an ECC curve with the client"); EC_Group ec_group(curve_name); std::unique_ptr ecdh(new ECDH_PrivateKey(rng, ec_group)); const std::string ecdh_domain_oid = ecdh->domain().get_oid(); const std::string domain = OIDS::lookup(OID(ecdh_domain_oid)); if(domain == "") throw Internal_Error("Could not find name of ECDH domain " + ecdh_domain_oid); const u16bit named_curve_id = Supported_Elliptic_Curves::name_to_curve_id(domain); 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_value(), 1); m_kex_key = ecdh.release(); } else if(kex_algo == "SRP_SHA") { const std::string srp_identifier = state->client_hello->srp_identifier(); std::string group_id; BigInt v; std::vector 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 = 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); } else if(kex_algo != "PSK") throw Internal_Error("Server_Key_Exchange: Unknown kex type " + kex_algo); if(state->suite.sig_algo() != "") { BOTAN_ASSERT(signing_key, "No signing key set"); std::pair format = state->choose_sig_format(signing_key, m_hash_algo, m_sig_algo, false); PK_Signer signer(*signing_key, format.first, format.second); signer.update(state->client_hello->random()); signer.update(state->server_hello->random()); signer.update(params()); m_signature = signer.signature(rng); } state->hash.update(writer.send(*this)); } /** * Deserialize a Server Key Exchange message */ Server_Key_Exchange::Server_Key_Exchange(const std::vector& buf, const std::string& kex_algo, const std::string& sig_algo, Protocol_Version version) : m_kex_key(nullptr), m_srp_params(nullptr) { if(buf.size() < 6) throw Decoding_Error("Server_Key_Exchange: Packet corrupted"); TLS_Data_Reader reader(buf); /* * We really are just serializing things back to what they were * before, but unfortunately to know where the signature is we need * to be able to parse the whole thing anyway. */ if(kex_algo == "PSK" || kex_algo == "DHE_PSK" || kex_algo == "ECDHE_PSK") { const std::string identity_hint = reader.get_string(2, 0, 65535); append_tls_length_value(m_params, identity_hint, 2); } if(kex_algo == "DH" || kex_algo == "DHE_PSK") { // 3 bigints, DH p, g, Y for(size_t i = 0; i != 3; ++i) { BigInt v = BigInt::decode(reader.get_range(2, 1, 65535)); append_tls_length_value(m_params, BigInt::encode(v), 2); } } else if(kex_algo == "ECDH" || kex_algo == "ECDHE_PSK") { const byte curve_type = reader.get_byte(); if(curve_type != 3) throw Decoding_Error("Server_Key_Exchange: Server sent non-named ECC curve"); const u16bit curve_id = reader.get_u16bit(); const std::string name = Supported_Elliptic_Curves::curve_id_to_name(curve_id); std::vector ecdh_key = reader.get_range(1, 1, 255); if(name == "") throw Decoding_Error("Server_Key_Exchange: Server sent unknown named curve " + std::to_string(curve_id)); m_params.push_back(curve_type); m_params.push_back(get_byte(0, curve_id)); m_params.push_back(get_byte(1, curve_id)); append_tls_length_value(m_params, ecdh_key, 1); } else if(kex_algo == "SRP_SHA") { // 2 bigints (N,g) then salt, then server B const BigInt N = BigInt::decode(reader.get_range(2, 1, 65535)); const BigInt g = BigInt::decode(reader.get_range(2, 1, 65535)); std::vector salt = reader.get_range(1, 1, 255); const BigInt B = BigInt::decode(reader.get_range(2, 1, 65535)); append_tls_length_value(m_params, BigInt::encode(N), 2); append_tls_length_value(m_params, BigInt::encode(g), 2); append_tls_length_value(m_params, salt, 1); append_tls_length_value(m_params, BigInt::encode(B), 2); } else if(kex_algo != "PSK") throw Decoding_Error("Server_Key_Exchange: Unsupported kex type " + kex_algo); if(sig_algo != "") { if(version >= Protocol_Version::TLS_V12) { m_hash_algo = Signature_Algorithms::hash_algo_name(reader.get_byte()); m_sig_algo = Signature_Algorithms::sig_algo_name(reader.get_byte()); } m_signature = reader.get_range(2, 0, 65535); } reader.assert_done(); } Server_Key_Exchange::~Server_Key_Exchange() { delete m_kex_key; delete m_srp_params; } /** * Serialize a Server Key Exchange message */ std::vector Server_Key_Exchange::serialize() const { std::vector buf = params(); if(m_signature.size()) { // This should be an explicit version check if(m_hash_algo != "" && m_sig_algo != "") { buf.push_back(Signature_Algorithms::hash_algo_code(m_hash_algo)); buf.push_back(Signature_Algorithms::sig_algo_code(m_sig_algo)); } append_tls_length_value(buf, m_signature, 2); } return buf; } /** * Verify a Server Key Exchange message */ bool Server_Key_Exchange::verify(const X509_Certificate& cert, Handshake_State* state) const { std::unique_ptr key(cert.subject_public_key()); std::pair format = state->understand_sig_format(key.get(), m_hash_algo, m_sig_algo, false); PK_Verifier verifier(*key, format.first, format.second); verifier.update(state->client_hello->random()); verifier.update(state->server_hello->random()); verifier.update(params()); return verifier.check_signature(m_signature); } const Private_Key& Server_Key_Exchange::server_kex_key() const { BOTAN_ASSERT(m_kex_key, "Key is non-NULL"); return *m_kex_key; } // Only valid for SRP negotiation SRP6_Server_Session& Server_Key_Exchange::server_srp_params() { BOTAN_ASSERT(m_srp_params, "SRP params are non-NULL"); return *m_srp_params; } } }