diff --git a/Userland/Libraries/LibTLS/HandshakeClient.cpp b/Userland/Libraries/LibTLS/HandshakeClient.cpp index bea4d0ed4c..3128dfa532 100644 --- a/Userland/Libraries/LibTLS/HandshakeClient.cpp +++ b/Userland/Libraries/LibTLS/HandshakeClient.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include @@ -224,6 +226,43 @@ void TLSv12::build_rsa_pre_master_secret(PacketBuilder& builder) builder.append(outbuf); } +void TLSv12::build_dhe_rsa_pre_master_secret(PacketBuilder& builder) +{ + auto& dh = m_context.server_diffie_hellman_params; + auto dh_p = Crypto::UnsignedBigInteger::import_data(dh.p.data(), dh.p.size()); + auto dh_g = Crypto::UnsignedBigInteger::import_data(dh.g.data(), dh.g.size()); + auto dh_Ys = Crypto::UnsignedBigInteger::import_data(dh.Ys.data(), dh.Ys.size()); + auto dh_key_size = dh.p.size(); + + auto dh_random = Crypto::NumberTheory::random_number(0, dh_p); + auto dh_Yc = Crypto::NumberTheory::ModularPower(dh_g, dh_random, dh_p); + auto dh_Yc_bytes = ByteBuffer::create_uninitialized(dh_key_size); + dh_Yc.export_data(dh_Yc_bytes); + + auto premaster_key = Crypto::NumberTheory::ModularPower(dh_Ys, dh_random, dh_p); + m_context.premaster_key = ByteBuffer::create_uninitialized(dh_key_size); + premaster_key.export_data(m_context.premaster_key, true); + + dh.p.clear(); + dh.g.clear(); + dh.Ys.clear(); + + if constexpr (TLS_DEBUG) { + dbgln("dh_random: {}", dh_random.to_base(16)); + dbgln("dh_Yc: {:hex-dump}", (ReadonlyBytes)dh_Yc_bytes); + dbgln("premaster key: {:hex-dump}", (ReadonlyBytes)m_context.premaster_key); + } + + if (!compute_master_secret_from_pre_master_secret(48)) { + dbgln("oh noes we could not derive a master key :("); + return; + } + + builder.append_u24(dh_key_size + 2); + builder.append((u16)dh_key_size); + builder.append(dh_Yc_bytes); +} + ByteBuffer TLSv12::build_certificate() { PacketBuilder builder { MessageType::Handshake, m_context.options.version }; @@ -296,8 +335,7 @@ ByteBuffer TLSv12::build_client_key_exchange() TODO(); break; case KeyExchangeAlgorithm::DHE_RSA: - dbgln("Client key exchange for DHE_RSA is not implemented"); - TODO(); + build_dhe_rsa_pre_master_secret(builder); break; case KeyExchangeAlgorithm::DH_anon: dbgln("Client key exchange for DH_anon is not implemented"); diff --git a/Userland/Libraries/LibTLS/HandshakeServer.cpp b/Userland/Libraries/LibTLS/HandshakeServer.cpp index 7af7949f03..c3c3e3672a 100644 --- a/Userland/Libraries/LibTLS/HandshakeServer.cpp +++ b/Userland/Libraries/LibTLS/HandshakeServer.cpp @@ -199,7 +199,7 @@ ByteBuffer TLSv12::build_server_key_exchange() return {}; } -ssize_t TLSv12::handle_server_key_exchange(ReadonlyBytes) +ssize_t TLSv12::handle_server_key_exchange(ReadonlyBytes buffer) { switch (get_key_exchange_algorithm(m_context.cipher)) { case KeyExchangeAlgorithm::RSA: @@ -214,8 +214,7 @@ ssize_t TLSv12::handle_server_key_exchange(ReadonlyBytes) TODO(); break; case KeyExchangeAlgorithm::DHE_RSA: - dbgln("Server key exchange for DHE_RSA is not implemented"); - TODO(); + handle_dhe_rsa_server_key_exchange(buffer); break; case KeyExchangeAlgorithm::DH_anon: dbgln("Server key exchange for DH_anon is not implemented"); @@ -237,4 +236,29 @@ ssize_t TLSv12::handle_server_key_exchange(ReadonlyBytes) return 0; } +ssize_t TLSv12::handle_dhe_rsa_server_key_exchange(ReadonlyBytes buffer) +{ + auto dh_p_length = AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(3))); + auto dh_p = buffer.slice(5, dh_p_length); + m_context.server_diffie_hellman_params.p = ByteBuffer::copy(dh_p.data(), dh_p.size()); + + auto dh_g_length = AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(5 + dh_p_length))); + auto dh_g = buffer.slice(7 + dh_p_length, dh_g_length); + m_context.server_diffie_hellman_params.g = ByteBuffer::copy(dh_g.data(), dh_g.size()); + + auto dh_Ys_length = AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(7 + dh_p_length + dh_g_length))); + auto dh_Ys = buffer.slice(9 + dh_p_length + dh_g_length, dh_Ys_length); + m_context.server_diffie_hellman_params.Ys = ByteBuffer::copy(dh_Ys.data(), dh_Ys.size()); + + if constexpr (TLS_DEBUG) { + dbgln("dh_p: {:hex-dump}", dh_p); + dbgln("dh_g: {:hex-dump}", dh_g); + dbgln("dh_Ys: {:hex-dump}", dh_Ys); + } + + // FIXME: Validate signature of Diffie-Hellman parameters as defined in RFC 5246 section 7.4.3. + + return 0; +} + } diff --git a/Userland/Libraries/LibTLS/TLSv12.h b/Userland/Libraries/LibTLS/TLSv12.h index b91d362b94..d44c75cf61 100644 --- a/Userland/Libraries/LibTLS/TLSv12.h +++ b/Userland/Libraries/LibTLS/TLSv12.h @@ -300,6 +300,12 @@ struct Context { size_t send_retries { 0 }; time_t handshake_initiation_timestamp { 0 }; + + struct { + ByteBuffer p; + ByteBuffer g; + ByteBuffer Ys; + } server_diffie_hellman_params; }; class TLSv12 : public Core::Socket { @@ -397,6 +403,7 @@ private: ByteBuffer build_change_cipher_spec(); ByteBuffer build_verify_request(); void build_rsa_pre_master_secret(PacketBuilder&); + void build_dhe_rsa_pre_master_secret(PacketBuilder&); bool flush(); void write_into_socket(); @@ -408,6 +415,7 @@ private: ssize_t handle_handshake_finished(ReadonlyBytes, WritePacketStage&); ssize_t handle_certificate(ReadonlyBytes); ssize_t handle_server_key_exchange(ReadonlyBytes); + ssize_t handle_dhe_rsa_server_key_exchange(ReadonlyBytes); ssize_t handle_server_hello_done(ReadonlyBytes); ssize_t handle_certificate_verify(ReadonlyBytes); ssize_t handle_handshake_payload(ReadonlyBytes);