diff --git a/Userland/Libraries/LibTLS/CipherSuite.h b/Userland/Libraries/LibTLS/CipherSuite.h index 3680cedf23..ac39e7999c 100644 --- a/Userland/Libraries/LibTLS/CipherSuite.h +++ b/Userland/Libraries/LibTLS/CipherSuite.h @@ -31,6 +31,10 @@ enum class CipherSuite { DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E, DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F, + // RFC 5289 - ECDHE for AES-GCM + ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F, + ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030, + // All recommended cipher suites (according to https://ciphersuite.info/cs/) // RFC 5288 - DH, DHE and RSA for AES-GCM @@ -185,4 +189,33 @@ constexpr size_t cipher_key_size(CipherAlgorithm algorithm) } } +enum class NamedCurve : u16 { + secp256r1 = 23, + secp384r1 = 24, + secp521r1 = 25, + x25519 = 29, + x448 = 30, +}; + +constexpr size_t named_curve_key_size(NamedCurve group) +{ + switch (group) { + case NamedCurve::secp256r1: + case NamedCurve::secp384r1: + case NamedCurve::secp521r1: + // FIXME: Add the correct key size for these elliptic curves + return 0; + case NamedCurve::x25519: + return 256; + case NamedCurve::x448: + return 448; + default: + return 0; + } +} + +enum class ECPointFormat : u8 { + Uncompressed = 0, +}; + } diff --git a/Userland/Libraries/LibTLS/Handshake.cpp b/Userland/Libraries/LibTLS/Handshake.cpp index 4fb065b677..ee3e708081 100644 --- a/Userland/Libraries/LibTLS/Handshake.cpp +++ b/Userland/Libraries/LibTLS/Handshake.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Ali Mohammad Pur + * Copyright (c) 2022, Michiel Visser * * SPDX-License-Identifier: BSD-2-Clause */ @@ -71,12 +72,20 @@ ByteBuffer TLSv12::build_hello() if (!m_context.extensions.SNI.is_null() && m_context.options.use_sni) sni_length = m_context.extensions.SNI.length(); + auto elliptic_curves_length = 2 * m_context.options.elliptic_curves.size(); + auto supported_ec_point_formats_length = m_context.options.supported_ec_point_formats.size(); + bool supports_elliptic_curves = elliptic_curves_length && supported_ec_point_formats_length; + // signature_algorithms: 2b extension ID, 2b extension length, 2b vector length, 2xN signatures and hashes extension_length += 2 + 2 + 2 + 2 * m_context.options.supported_signature_algorithms.size(); if (sni_length) extension_length += sni_length + 9; + // Only send elliptic_curves and ec_point_formats extensions if both are supported + if (supports_elliptic_curves) + extension_length += 6 + elliptic_curves_length + 5 + supported_ec_point_formats_length; + builder.append((u16)extension_length); if (sni_length) { @@ -105,6 +114,22 @@ ByteBuffer TLSv12::build_hello() builder.append((u8)entry.signature); } + if (supports_elliptic_curves) { + // elliptic_curves extension + builder.append((u16)HandshakeExtension::EllipticCurves); + builder.append((u16)(2 + elliptic_curves_length)); + builder.append((u16)elliptic_curves_length); + for (auto& curve : m_context.options.elliptic_curves) + builder.append((u16)curve); + + // ec_point_formats extension + builder.append((u16)HandshakeExtension::ECPointFormats); + builder.append((u16)(1 + supported_ec_point_formats_length)); + builder.append((u8)supported_ec_point_formats_length); + for (auto& format : m_context.options.supported_ec_point_formats) + builder.append((u8)format); + } + if (alpn_length) { // TODO VERIFY_NOT_REACHED(); diff --git a/Userland/Libraries/LibTLS/HandshakeClient.cpp b/Userland/Libraries/LibTLS/HandshakeClient.cpp index bed9ffbb9d..4d75f62413 100644 --- a/Userland/Libraries/LibTLS/HandshakeClient.cpp +++ b/Userland/Libraries/LibTLS/HandshakeClient.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Ali Mohammad Pur + * Copyright (c) 2022, Michiel Visser * * SPDX-License-Identifier: BSD-2-Clause */ @@ -9,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -292,6 +294,51 @@ void TLSv12::build_dhe_rsa_pre_master_secret(PacketBuilder& builder) builder.append(dh_Yc_bytes); } +void TLSv12::build_ecdhe_rsa_pre_master_secret(PacketBuilder& builder) +{ + size_t const key_size = named_curve_key_size(NamedCurve::x25519) / 8; + u8 generator_point[key_size] { 9 }; + ReadonlyBytes generator_point_bytes { generator_point, key_size }; + + // Create a random private key + u8 private_key[key_size]; + fill_with_random(private_key, key_size); + ReadonlyBytes private_key_bytes { private_key, key_size }; + + // Calculate the public key by multiplying the private key with 9 + auto public_key_result = Crypto::Curves::X25519::compute_coordinate(private_key_bytes, generator_point_bytes); + if (public_key_result.is_error()) { + dbgln("Failed to build ECDHE_RSA premaster secret: not enough memory"); + return; + } + auto public_key = public_key_result.release_value(); + + // Calculate the pre master secret by multiplying the client private key and the server public key + ReadonlyBytes server_public_key_bytes = m_context.server_diffie_hellman_params.p; + auto pre_master_secret_result = Crypto::Curves::X25519::compute_coordinate(private_key_bytes, server_public_key_bytes); + if (pre_master_secret_result.is_error()) { + dbgln("Failed to build ECDHE_RSA premaster secret: not enough memory"); + return; + } + m_context.premaster_key = pre_master_secret_result.release_value(); + + if constexpr (TLS_DEBUG) { + dbgln("Build ECDHE_RSA pre master secret"); + dbgln("client private key: {:hex-dump}", private_key_bytes); + dbgln("client public key: {:hex-dump}", (ReadonlyBytes)public_key); + 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(key_size + 1); + builder.append((u8)key_size); + builder.append(public_key); +} + ByteBuffer TLSv12::build_certificate() { PacketBuilder builder { MessageType::Handshake, m_context.options.version }; @@ -371,6 +418,8 @@ ByteBuffer TLSv12::build_client_key_exchange() TODO(); break; case KeyExchangeAlgorithm::ECDHE_RSA: + build_ecdhe_rsa_pre_master_secret(builder); + break; case KeyExchangeAlgorithm::ECDH_ECDSA: case KeyExchangeAlgorithm::ECDH_RSA: case KeyExchangeAlgorithm::ECDHE_ECDSA: diff --git a/Userland/Libraries/LibTLS/HandshakeServer.cpp b/Userland/Libraries/LibTLS/HandshakeServer.cpp index 60c4e95a3f..c2cb8ec27f 100644 --- a/Userland/Libraries/LibTLS/HandshakeServer.cpp +++ b/Userland/Libraries/LibTLS/HandshakeServer.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Ali Mohammad Pur + * Copyright (c) 2022, Michiel Visser * * SPDX-License-Identifier: BSD-2-Clause */ @@ -171,6 +172,16 @@ ssize_t TLSv12::handle_server_hello(ReadonlyBytes buffer, WritePacketStage& writ print_buffer(buffer.slice(res, extension_length)); res += extension_length; // FIXME: what are we supposed to do here? + } else if (extension_type == HandshakeExtension::ECPointFormats) { + // RFC8422 section 5.2: A server that selects an ECC cipher suite in response to a ClientHello message + // including a Supported Point Formats Extension appends this extension (along with others) to its + // ServerHello message, enumerating the point formats it can parse. The Supported Point Formats Extension, + // when used, MUST contain the value 0 (uncompressed) as one of the items in the list of point formats. + // + // The current implementation only supports uncompressed points, and the server is required to support + // uncompressed points. Therefore, this extension can be safely ignored as it should always inform us + // that the server supports uncompressed points. + res += extension_length; } else { dbgln("Encountered unknown extension {} with length {}", (u16)extension_type, extension_length); res += extension_length; @@ -221,6 +232,7 @@ ssize_t TLSv12::handle_server_key_exchange(ReadonlyBytes buffer) TODO(); break; case KeyExchangeAlgorithm::ECDHE_RSA: + return handle_ecdhe_rsa_server_key_exchange(buffer); case KeyExchangeAlgorithm::ECDH_ECDSA: case KeyExchangeAlgorithm::ECDH_RSA: case KeyExchangeAlgorithm::ECDHE_ECDSA: @@ -276,4 +288,39 @@ ssize_t TLSv12::handle_dhe_rsa_server_key_exchange(ReadonlyBytes buffer) return 0; } +ssize_t TLSv12::handle_ecdhe_rsa_server_key_exchange(ReadonlyBytes buffer) +{ + auto x25519_key_size_bytes = named_curve_key_size(NamedCurve::x25519) / 8; + if (buffer.size() < x25519_key_size_bytes + 7) + return (i8)Error::NeedMoreData; + + auto curve_type = buffer[3]; + if (curve_type != (u8)ECCurveType::NamedCurve) + return (i8)Error::FeatureNotSupported; + + auto curve = AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(4))); + if (curve != (u16)NamedCurve::x25519) + return (i8)Error::FeatureNotSupported; + + auto server_public_key_length = buffer[6]; + if (server_public_key_length != x25519_key_size_bytes) + return (i8)Error::FeatureNotSupported; + + auto server_public_key = buffer.slice(7, server_public_key_length); + auto server_public_key_copy_result = ByteBuffer::copy(server_public_key); + if (server_public_key_copy_result.is_error()) { + dbgln("ecdhe_rsa_server_key_exchange failed: Not enough memory"); + return 0; + } + m_context.server_diffie_hellman_params.p = server_public_key_copy_result.release_value(); + + if constexpr (TLS_DEBUG) { + dbgln("ECDHE server public key: {:hex-dump}", server_public_key); + } + + // FIXME: Validate signature of Elliptic Curve Diffie-Hellman public key + + return 0; +} + } diff --git a/Userland/Libraries/LibTLS/TLSv12.h b/Userland/Libraries/LibTLS/TLSv12.h index 58d7664500..6727794191 100644 --- a/Userland/Libraries/LibTLS/TLSv12.h +++ b/Userland/Libraries/LibTLS/TLSv12.h @@ -132,8 +132,10 @@ enum HandshakeType { enum class HandshakeExtension : u16 { ServerName = 0x00, - ApplicationLayerProtocolNegotiation = 0x10, + EllipticCurves = 0x0a, + ECPointFormats = 0x0b, SignatureAlgorithms = 0x0d, + ApplicationLayerProtocolNegotiation = 0x10, }; enum class NameType : u8 { @@ -160,19 +162,25 @@ enum ClientVerificationStaus { VerificationNeeded, }; +enum class ECCurveType : u8 { + NamedCurve = 3, +}; + // Note for the 16 iv length instead of 8: // 4 bytes of fixed IV, 8 random (nonce) bytes, 4 bytes for counter // GCM specifically asks us to transmit only the nonce, the counter is zero // and the fixed IV is derived from the premaster key. -#define ENUMERATE_CIPHERS(C) \ - C(true, CipherSuite::RSA_WITH_AES_128_CBC_SHA, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_128_CBC, Crypto::Hash::SHA1, 16, false) \ - C(true, CipherSuite::RSA_WITH_AES_256_CBC_SHA, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_256_CBC, Crypto::Hash::SHA1, 16, false) \ - C(true, CipherSuite::RSA_WITH_AES_128_CBC_SHA256, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_128_CBC, Crypto::Hash::SHA256, 16, false) \ - C(true, CipherSuite::RSA_WITH_AES_256_CBC_SHA256, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_256_CBC, Crypto::Hash::SHA256, 16, false) \ - C(true, CipherSuite::RSA_WITH_AES_128_GCM_SHA256, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_128_GCM, Crypto::Hash::SHA256, 8, true) \ - C(true, CipherSuite::RSA_WITH_AES_256_GCM_SHA384, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_256_GCM, Crypto::Hash::SHA384, 8, true) \ - C(true, CipherSuite::DHE_RSA_WITH_AES_128_GCM_SHA256, KeyExchangeAlgorithm::DHE_RSA, CipherAlgorithm::AES_128_GCM, Crypto::Hash::SHA256, 8, true) \ - C(true, CipherSuite::DHE_RSA_WITH_AES_256_GCM_SHA384, KeyExchangeAlgorithm::DHE_RSA, CipherAlgorithm::AES_256_GCM, Crypto::Hash::SHA384, 8, true) +#define ENUMERATE_CIPHERS(C) \ + C(true, CipherSuite::RSA_WITH_AES_128_CBC_SHA, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_128_CBC, Crypto::Hash::SHA1, 16, false) \ + C(true, CipherSuite::RSA_WITH_AES_256_CBC_SHA, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_256_CBC, Crypto::Hash::SHA1, 16, false) \ + C(true, CipherSuite::RSA_WITH_AES_128_CBC_SHA256, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_128_CBC, Crypto::Hash::SHA256, 16, false) \ + C(true, CipherSuite::RSA_WITH_AES_256_CBC_SHA256, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_256_CBC, Crypto::Hash::SHA256, 16, false) \ + C(true, CipherSuite::RSA_WITH_AES_128_GCM_SHA256, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_128_GCM, Crypto::Hash::SHA256, 8, true) \ + C(true, CipherSuite::RSA_WITH_AES_256_GCM_SHA384, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_256_GCM, Crypto::Hash::SHA384, 8, true) \ + C(true, CipherSuite::DHE_RSA_WITH_AES_128_GCM_SHA256, KeyExchangeAlgorithm::DHE_RSA, CipherAlgorithm::AES_128_GCM, Crypto::Hash::SHA256, 8, true) \ + C(true, CipherSuite::DHE_RSA_WITH_AES_256_GCM_SHA384, KeyExchangeAlgorithm::DHE_RSA, CipherAlgorithm::AES_256_GCM, Crypto::Hash::SHA384, 8, true) \ + C(true, CipherSuite::ECDHE_RSA_WITH_AES_128_GCM_SHA256, KeyExchangeAlgorithm::ECDHE_RSA, CipherAlgorithm::AES_128_GCM, Crypto::Hash::SHA256, 8, true) \ + C(true, CipherSuite::ECDHE_RSA_WITH_AES_256_GCM_SHA384, KeyExchangeAlgorithm::ECDHE_RSA, CipherAlgorithm::AES_256_GCM, Crypto::Hash::SHA384, 8, true) constexpr KeyExchangeAlgorithm get_key_exchange_algorithm(CipherSuite suite) { @@ -233,6 +241,8 @@ struct Options { { HashAlgorithm::SHA384, SignatureAlgorithm::RSA }, { HashAlgorithm::SHA256, SignatureAlgorithm::RSA }, { HashAlgorithm::SHA1, SignatureAlgorithm::RSA }); + OPTION_WITH_DEFAULTS(Vector, elliptic_curves, NamedCurve::x25519) + OPTION_WITH_DEFAULTS(Vector, supported_ec_point_formats, ECPointFormat::Uncompressed) OPTION_WITH_DEFAULTS(bool, use_sni, true) OPTION_WITH_DEFAULTS(bool, use_compression, false) @@ -448,6 +458,7 @@ private: ByteBuffer build_verify_request(); void build_rsa_pre_master_secret(PacketBuilder&); void build_dhe_rsa_pre_master_secret(PacketBuilder&); + void build_ecdhe_rsa_pre_master_secret(PacketBuilder&); ErrorOr flush(); void write_into_socket(); @@ -461,6 +472,7 @@ private: ssize_t handle_certificate(ReadonlyBytes); ssize_t handle_server_key_exchange(ReadonlyBytes); ssize_t handle_dhe_rsa_server_key_exchange(ReadonlyBytes); + ssize_t handle_ecdhe_rsa_server_key_exchange(ReadonlyBytes); ssize_t handle_server_hello_done(ReadonlyBytes); ssize_t handle_certificate_verify(ReadonlyBytes); ssize_t handle_handshake_payload(ReadonlyBytes);