diff --git a/Ladybird/Android/src/main/cpp/RequestServerService.cpp b/Ladybird/Android/src/main/cpp/RequestServerService.cpp index b72361994b4..75278a5801d 100644 --- a/Ladybird/Android/src/main/cpp/RequestServerService.cpp +++ b/Ladybird/Android/src/main/cpp/RequestServerService.cpp @@ -31,8 +31,7 @@ ErrorOr find_certificates(StringView serenity_resource_root) ErrorOr service_main(int ipc_socket) { // Ensure the certificates are read out here. - DefaultRootCACertificates::set_default_certificate_paths(Vector { TRY(find_certificates(s_serenity_resource_root)) }); - [[maybe_unused]] auto& certs = DefaultRootCACertificates::the(); + TLS::WolfTLS::install_certificate_store_paths({ TRY(find_certificates(s_serenity_resource_root)) }); Core::EventLoop event_loop; diff --git a/Ladybird/RequestServer/main.cpp b/Ladybird/RequestServer/main.cpp index 90544511bcd..420d789f4c0 100644 --- a/Ladybird/RequestServer/main.cpp +++ b/Ladybird/RequestServer/main.cpp @@ -48,8 +48,8 @@ ErrorOr serenity_main(Main::Arguments arguments) // Ensure the certificates are read out here. if (certificates.is_empty()) certificates.append(TRY(find_certificates(serenity_resource_root))); - DefaultRootCACertificates::set_default_certificate_paths(certificates.span()); - [[maybe_unused]] auto& certs = DefaultRootCACertificates::the(); + + TLS::WolfTLS::install_certificate_store_paths(move(certificates)); Core::EventLoop event_loop; diff --git a/Meta/Lagom/CMakeLists.txt b/Meta/Lagom/CMakeLists.txt index 3f9a30ad765..2f2a98baae0 100644 --- a/Meta/Lagom/CMakeLists.txt +++ b/Meta/Lagom/CMakeLists.txt @@ -530,7 +530,6 @@ if (BUILD_TESTING) endforeach() # LibTLS needs a special working directory to find cacert.pem - lagom_test(../../Tests/LibTLS/TestTLSHandshake.cpp LibTLS LIBS LibTLS LibCrypto) lagom_test(../../Tests/LibTLS/TestTLSCertificateParser.cpp LibTLS LIBS LibTLS LibCrypto) # The FLAC tests need a special working directory to find the test files diff --git a/README.md b/README.md index d6577596e36..938722ec906 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,8 @@ At the moment, many core library support components are inherited from SerenityO - LibWeb: Web rendering engine - LibJS: JavaScript engine - LibWasm: WebAssembly implementation -- LibCrypto/LibTLS: Cryptography primitives and Transport Layer Security +- LibCrypto: Cryptography primitives +- LibTLS: Some certificate parsing primitives - LibHTTP: HTTP/1.1 client - LibGfx: 2D Graphics Library, Image Decoding and Rendering - LibArchive: Archive file format support diff --git a/Tests/LibTLS/CMakeLists.txt b/Tests/LibTLS/CMakeLists.txt index 7badc93a4f2..ce4eaa9ffcc 100644 --- a/Tests/LibTLS/CMakeLists.txt +++ b/Tests/LibTLS/CMakeLists.txt @@ -1,6 +1,5 @@ set(TEST_SOURCES TestTLSCertificateParser.cpp - TestTLSHandshake.cpp ) foreach(source IN LISTS TEST_SOURCES) diff --git a/Tests/LibTLS/TestTLSHandshake.cpp b/Tests/LibTLS/TestTLSHandshake.cpp deleted file mode 100644 index 2f42c29fbb6..00000000000 --- a/Tests/LibTLS/TestTLSHandshake.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2021, Peter Bocan - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static StringView ca_certs_file = "./cacert.pem"sv; -static int port = 443; - -constexpr auto DEFAULT_SERVER = "www.google.com"sv; - -static ByteBuffer operator""_b(char const* string, size_t length) -{ - return ByteBuffer::copy(string, length).release_value(); -} - -ErrorOr> load_certificates(); -ByteString locate_ca_certs_file(); - -ByteString locate_ca_certs_file() -{ - if (FileSystem::exists(ca_certs_file)) { - return ca_certs_file; - } - auto on_target_path = ByteString("/etc/cacert.pem"); - if (FileSystem::exists(on_target_path)) { - return on_target_path; - } - return ""; -} - -ErrorOr> load_certificates() -{ - auto cacert_file = TRY(Core::File::open(locate_ca_certs_file(), Core::File::OpenMode::Read)); - auto data = TRY(cacert_file->read_until_eof()); - return TRY(DefaultRootCACertificates::parse_pem_root_certificate_authorities(data)); -} - -TEST_CASE(test_TLS_hello_handshake) -{ - Core::EventLoop loop; - TLS::Options options; - options.set_root_certificates(TRY_OR_FAIL(load_certificates())); - options.set_alert_handler([&](TLS::AlertDescription) { - FAIL("Connection failure"); - loop.quit(1); - }); - options.set_finish_callback([&] { - loop.quit(0); - }); - - auto tls = TRY_OR_FAIL(TLS::TLSv12::connect(DEFAULT_SERVER, port, move(options))); - ByteBuffer contents; - tls->on_ready_to_read = [&] { - (void)TRY_OR_FAIL(tls->read_some(contents.must_get_bytes_for_writing(4 * KiB))); - loop.quit(0); - }; - - if (tls->write_until_depleted("GET / HTTP/1.1\r\nHost: "_b).is_error()) { - FAIL("write(0) failed"); - return; - } - - auto the_server = DEFAULT_SERVER; - if (tls->write_until_depleted(the_server.bytes()).is_error()) { - FAIL("write(1) failed"); - return; - } - if (tls->write_until_depleted("\r\nConnection : close\r\n\r\n"_b).is_error()) { - FAIL("write(2) failed"); - return; - } - - loop.exec(); -} diff --git a/Userland/Libraries/LibCore/Socket.h b/Userland/Libraries/LibCore/Socket.h index bcac9803ea7..df8f622dd1c 100644 --- a/Userland/Libraries/LibCore/Socket.h +++ b/Userland/Libraries/LibCore/Socket.h @@ -196,6 +196,8 @@ public: virtual ~TCPSocket() override { close(); } + int fd() { return m_helper.fd(); } + private: explicit TCPSocket(PreventSIGPIPE prevent_sigpipe = PreventSIGPIPE::Yes) : Socket(prevent_sigpipe) diff --git a/Userland/Libraries/LibCrypto/Authentication/HMAC.h b/Userland/Libraries/LibCrypto/Authentication/HMAC.h index c9baee936c9..45262d6bf5c 100644 --- a/Userland/Libraries/LibCrypto/Authentication/HMAC.h +++ b/Userland/Libraries/LibCrypto/Authentication/HMAC.h @@ -13,11 +13,11 @@ #include #include +namespace Crypto::Authentication { + constexpr static auto IPAD = 0x36; constexpr static auto OPAD = 0x5c; -namespace Crypto::Authentication { - template class HMAC { public: diff --git a/Userland/Libraries/LibHTTP/HttpsJob.cpp b/Userland/Libraries/LibHTTP/HttpsJob.cpp index 28a2a5f6010..2c158bef656 100644 --- a/Userland/Libraries/LibHTTP/HttpsJob.cpp +++ b/Userland/Libraries/LibHTTP/HttpsJob.cpp @@ -10,7 +10,8 @@ namespace HTTP { void HttpsJob::set_certificate(ByteString certificate, ByteString key) { - m_received_client_certificates = TLS::TLSv12::parse_pem_certificate(certificate.bytes(), key.bytes()); + (void)certificate; + (void)key; } } diff --git a/Userland/Libraries/LibTLS/CMakeLists.txt b/Userland/Libraries/LibTLS/CMakeLists.txt index 13ce1c6ba16..02dfd4bc761 100644 --- a/Userland/Libraries/LibTLS/CMakeLists.txt +++ b/Userland/Libraries/LibTLS/CMakeLists.txt @@ -2,16 +2,12 @@ add_compile_options(-Wvla) set(SOURCES Certificate.cpp - Handshake.cpp - HandshakeCertificate.cpp - HandshakeClient.cpp - HandshakeServer.cpp - Record.cpp - Socket.cpp TLSv12.cpp ) +find_package(WolfSSL REQUIRED) + serenity_lib(LibTLS tls) -target_link_libraries(LibTLS PRIVATE LibCore LibCrypto LibFileSystem) +target_link_libraries(LibTLS PRIVATE LibCore LibCrypto wolfssl::wolfssl) include(ca_certificates_data) diff --git a/Userland/Libraries/LibTLS/Certificate.h b/Userland/Libraries/LibTLS/Certificate.h index 69559b4482f..d57665c9640 100644 --- a/Userland/Libraries/LibTLS/Certificate.h +++ b/Userland/Libraries/LibTLS/Certificate.h @@ -293,25 +293,6 @@ public: private: Optional m_is_self_signed; }; - -class DefaultRootCACertificates { -public: - DefaultRootCACertificates(); - - Vector const& certificates() const { return m_ca_certificates; } - - static ErrorOr> parse_pem_root_certificate_authorities(ByteBuffer&); - static ErrorOr> load_certificates(Span custom_cert_paths = {}); - - static DefaultRootCACertificates& the(); - - static void set_default_certificate_paths(Span paths); - -private: - Vector m_ca_certificates; -}; - } using TLS::Certificate; -using TLS::DefaultRootCACertificates; diff --git a/Userland/Libraries/LibTLS/CipherSuite.h b/Userland/Libraries/LibTLS/CipherSuite.h deleted file mode 100644 index 8f446b58116..00000000000 --- a/Userland/Libraries/LibTLS/CipherSuite.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2020, Ali Mohammad Pur - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace TLS { - -// Defined in RFC 5246 section 7.4.1.4.1 -struct SignatureAndHashAlgorithm { - HashAlgorithm hash; - SignatureAlgorithm signature; -}; - -enum class KeyExchangeAlgorithm { - Invalid, - // Defined in RFC 5246 section 7.4.2 / RFC 4279 section 4 - RSA_PSK, - // Defined in RFC 5246 section 7.4.3 - DHE_DSS, - DHE_RSA, - DH_anon, - RSA, - DH_DSS, - DH_RSA, - // Defined in RFC 4492 section 2 - ECDHE_RSA, - ECDH_ECDSA, - ECDH_RSA, - ECDHE_ECDSA, - ECDH_anon, -}; - -// Defined in RFC 5246 section 7.4.1.4.1 -constexpr SignatureAlgorithm signature_for_key_exchange_algorithm(KeyExchangeAlgorithm algorithm) -{ - switch (algorithm) { - case KeyExchangeAlgorithm::RSA: - case KeyExchangeAlgorithm::DHE_RSA: - case KeyExchangeAlgorithm::DH_RSA: - case KeyExchangeAlgorithm::RSA_PSK: - case KeyExchangeAlgorithm::ECDH_RSA: - case KeyExchangeAlgorithm::ECDHE_RSA: - return SignatureAlgorithm::RSA; - case KeyExchangeAlgorithm::DHE_DSS: - case KeyExchangeAlgorithm::DH_DSS: - return SignatureAlgorithm::DSA; - case KeyExchangeAlgorithm::ECDH_ECDSA: - case KeyExchangeAlgorithm::ECDHE_ECDSA: - return SignatureAlgorithm::ECDSA; - case KeyExchangeAlgorithm::DH_anon: - case KeyExchangeAlgorithm::ECDH_anon: - default: - return SignatureAlgorithm::ANONYMOUS; - } -} - -enum class CipherAlgorithm { - Invalid, - AES_128_CBC, - AES_128_GCM, - AES_128_CCM, - AES_128_CCM_8, - AES_256_CBC, - AES_256_GCM, -}; - -constexpr size_t cipher_key_size(CipherAlgorithm algorithm) -{ - switch (algorithm) { - case CipherAlgorithm::AES_128_CBC: - case CipherAlgorithm::AES_128_GCM: - case CipherAlgorithm::AES_128_CCM: - case CipherAlgorithm::AES_128_CCM_8: - return 128; - case CipherAlgorithm::AES_256_CBC: - case CipherAlgorithm::AES_256_GCM: - return 256; - case CipherAlgorithm::Invalid: - default: - return 0; - } -} - -} diff --git a/Userland/Libraries/LibTLS/Handshake.cpp b/Userland/Libraries/LibTLS/Handshake.cpp deleted file mode 100644 index 72f5993d966..00000000000 --- a/Userland/Libraries/LibTLS/Handshake.cpp +++ /dev/null @@ -1,559 +0,0 @@ -/* - * Copyright (c) 2020, Ali Mohammad Pur - * Copyright (c) 2022, Michiel Visser - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -#include -#include -#include - -namespace TLS { - -ByteBuffer TLSv12::build_hello() -{ - fill_with_random(m_context.local_random); - - auto packet_version = (u16)m_context.options.version; - auto version = (u16)m_context.options.version; - PacketBuilder builder { ContentType::HANDSHAKE, packet_version }; - - builder.append(to_underlying(HandshakeType::CLIENT_HELLO)); - - // hello length (for later) - u8 dummy[3] = {}; - builder.append(dummy, 3); - - auto start_length = builder.length(); - - builder.append(version); - builder.append(m_context.local_random, sizeof(m_context.local_random)); - - builder.append(m_context.session_id_size); - if (m_context.session_id_size) - builder.append(m_context.session_id, m_context.session_id_size); - - size_t extension_length = 0; - size_t alpn_length = 0; - size_t alpn_negotiated_length = 0; - - // ALPN - if (!m_context.negotiated_alpn.is_empty()) { - alpn_negotiated_length = m_context.negotiated_alpn.length(); - alpn_length = alpn_negotiated_length + 1; - extension_length += alpn_length + 6; - } else if (m_context.alpn.size()) { - for (auto& alpn : m_context.alpn) { - size_t length = alpn.length(); - alpn_length += length + 1; - } - if (alpn_length) - extension_length += alpn_length + 6; - } - - // Ciphers - builder.append((u16)(m_context.options.usable_cipher_suites.size() * sizeof(u16))); - for (auto suite : m_context.options.usable_cipher_suites) - builder.append((u16)suite); - - // we don't like compression - VERIFY(!m_context.options.use_compression); - builder.append((u8)1); - builder.append((u8)m_context.options.use_compression); - - // set SNI if we have one, and the user hasn't explicitly asked us to omit it. - auto sni_length = 0; - if (!m_context.extensions.SNI.is_empty() && 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; - bool enable_extended_master_secret = m_context.options.enable_extended_master_secret; - - // 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; - - if (enable_extended_master_secret) - extension_length += 4; - - builder.append((u16)extension_length); - - if (sni_length) { - // SNI extension - builder.append((u16)ExtensionType::SERVER_NAME); - // extension length - builder.append((u16)(sni_length + 5)); - // SNI length - builder.append((u16)(sni_length + 3)); - // SNI type - builder.append((u8)0); - // SNI host length + value - builder.append((u16)sni_length); - builder.append((u8 const*)m_context.extensions.SNI.characters(), sni_length); - } - - // signature_algorithms extension - builder.append((u16)ExtensionType::SIGNATURE_ALGORITHMS); - // Extension length - builder.append((u16)(2 + 2 * m_context.options.supported_signature_algorithms.size())); - // Vector count - builder.append((u16)(m_context.options.supported_signature_algorithms.size() * 2)); - // Entries - for (auto& entry : m_context.options.supported_signature_algorithms) { - builder.append((u8)entry.hash); - builder.append((u8)entry.signature); - } - - if (supports_elliptic_curves) { - // elliptic_curves extension - builder.append((u16)ExtensionType::SUPPORTED_GROUPS); - 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)ExtensionType::EC_POINT_FORMATS); - 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 (enable_extended_master_secret) { - // extended_master_secret extension - builder.append((u16)ExtensionType::EXTENDED_MASTER_SECRET); - builder.append((u16)0); - } - - if (alpn_length) { - // TODO - VERIFY_NOT_REACHED(); - } - - // set the "length" field of the packet - size_t remaining = builder.length() - start_length; - size_t payload_position = 6; - builder.set(payload_position, remaining / 0x10000); - remaining %= 0x10000; - builder.set(payload_position + 1, remaining / 0x100); - remaining %= 0x100; - builder.set(payload_position + 2, remaining); - - auto packet = builder.build(); - update_packet(packet); - - return packet; -} - -ByteBuffer TLSv12::build_change_cipher_spec() -{ - PacketBuilder builder { ContentType::CHANGE_CIPHER_SPEC, m_context.options.version, 64 }; - builder.append((u8)1); - auto packet = builder.build(); - update_packet(packet); - m_context.local_sequence_number = 0; - return packet; -} - -ByteBuffer TLSv12::build_handshake_finished() -{ - PacketBuilder builder { ContentType::HANDSHAKE, m_context.options.version, 12 + 64 }; - builder.append((u8)HandshakeType::FINISHED); - - // RFC 5246 section 7.4.9: "In previous versions of TLS, the verify_data was always 12 octets - // long. In the current version of TLS, it depends on the cipher - // suite. Any cipher suite which does not explicitly specify - // verify_data_length has a verify_data_length equal to 12." - // Simplification: Assume that verify_data_length is always 12. - constexpr u32 verify_data_length = 12; - - builder.append_u24(verify_data_length); - - u8 out[verify_data_length]; - auto outbuffer = Bytes { out, verify_data_length }; - ByteBuffer dummy; - - auto digest = m_context.handshake_hash.digest(); - auto hashbuf = ReadonlyBytes { digest.immutable_data(), m_context.handshake_hash.digest_size() }; - pseudorandom_function(outbuffer, m_context.master_key, (u8 const*)"client finished", 15, hashbuf, dummy); - - builder.append(outbuffer); - auto packet = builder.build(); - update_packet(packet); - - return packet; -} - -ssize_t TLSv12::handle_handshake_finished(ReadonlyBytes buffer, WritePacketStage& write_packets) -{ - if (m_context.connection_status < ConnectionStatus::KeyExchange || m_context.connection_status == ConnectionStatus::Established) { - dbgln("unexpected finished message"); - return (i8)Error::UnexpectedMessage; - } - - write_packets = WritePacketStage::Initial; - - if (buffer.size() < 3) { - return (i8)Error::NeedMoreData; - } - - size_t index = 3; - - u32 size = buffer[0] * 0x10000 + buffer[1] * 0x100 + buffer[2]; - - if (size < 12) { - dbgln_if(TLS_DEBUG, "finished packet smaller than minimum size: {}", size); - return (i8)Error::BrokenPacket; - } - - if (size < buffer.size() - index) { - dbgln_if(TLS_DEBUG, "not enough data after length: {} > {}", size, buffer.size() - index); - return (i8)Error::NeedMoreData; - } - - // TODO: Compare Hashes - dbgln_if(TLS_DEBUG, "FIXME: handle_handshake_finished :: Check message validity"); - m_context.connection_status = ConnectionStatus::Established; - - if (m_handshake_timeout_timer) { - // Disable the handshake timeout timer as handshake has been established. - m_handshake_timeout_timer->stop(); - m_handshake_timeout_timer->remove_from_parent(); - m_handshake_timeout_timer = nullptr; - } - - if (on_connected) - on_connected(); - - return index + size; -} - -ssize_t TLSv12::handle_handshake_payload(ReadonlyBytes vbuffer) -{ - if (m_context.connection_status == ConnectionStatus::Established) { - dbgln_if(TLS_DEBUG, "Renegotiation attempt ignored"); - // FIXME: We should properly say "NoRenegotiation", but that causes a handshake failure - // so we just roll with it and pretend that we _did_ renegotiate - // This will cause issues when we decide to have long-lasting connections, but - // we do not have those at the moment :^) - return 1; - } - auto buffer = vbuffer; - auto buffer_length = buffer.size(); - auto original_length = buffer_length; - while (buffer_length >= 4 && !m_context.critical_error) { - ssize_t payload_res = 0; - if (buffer_length < 1) - return (i8)Error::NeedMoreData; - auto type = static_cast(buffer[0]); - auto write_packets { WritePacketStage::Initial }; - size_t payload_size = buffer[1] * 0x10000 + buffer[2] * 0x100 + buffer[3] + 3; - dbgln_if(TLS_DEBUG, "payload size: {} buffer length: {}", payload_size, buffer_length); - if (payload_size + 1 > buffer_length) - return (i8)Error::NeedMoreData; - - switch (type) { - case HandshakeType::HELLO_REQUEST_RESERVED: - if (m_context.handshake_messages[0] >= 1) { - dbgln("unexpected hello request message"); - payload_res = (i8)Error::UnexpectedMessage; - break; - } - ++m_context.handshake_messages[0]; - dbgln("hello request (renegotiation?)"); - if (m_context.connection_status == ConnectionStatus::Established) { - // renegotiation - payload_res = (i8)Error::NoRenegotiation; - } else { - // :shrug: - payload_res = (i8)Error::UnexpectedMessage; - } - break; - case HandshakeType::CLIENT_HELLO: - // FIXME: We only support client mode right now - if (m_context.is_server) { - VERIFY_NOT_REACHED(); - } - payload_res = (i8)Error::UnexpectedMessage; - break; - case HandshakeType::SERVER_HELLO: - if (m_context.handshake_messages[2] >= 1) { - dbgln("unexpected server hello message"); - payload_res = (i8)Error::UnexpectedMessage; - break; - } - ++m_context.handshake_messages[2]; - dbgln_if(TLS_DEBUG, "server hello"); - if (m_context.is_server) { - dbgln("unsupported: server mode"); - VERIFY_NOT_REACHED(); - } - payload_res = handle_server_hello(buffer.slice(1, payload_size), write_packets); - break; - case HandshakeType::HELLO_VERIFY_REQUEST_RESERVED: - dbgln("unsupported: DTLS"); - payload_res = (i8)Error::UnexpectedMessage; - break; - case HandshakeType::CERTIFICATE: - if (m_context.handshake_messages[4] >= 1) { - dbgln("unexpected certificate message"); - payload_res = (i8)Error::UnexpectedMessage; - break; - } - ++m_context.handshake_messages[4]; - dbgln_if(TLS_DEBUG, "certificate"); - if (m_context.connection_status == ConnectionStatus::Negotiating) { - if (m_context.is_server) { - dbgln("unsupported: server mode"); - VERIFY_NOT_REACHED(); - } - payload_res = handle_certificate(buffer.slice(1, payload_size)); - } else { - payload_res = (i8)Error::UnexpectedMessage; - } - break; - case HandshakeType::SERVER_KEY_EXCHANGE_RESERVED: - if (m_context.handshake_messages[5] >= 1) { - dbgln("unexpected server key exchange message"); - payload_res = (i8)Error::UnexpectedMessage; - break; - } - ++m_context.handshake_messages[5]; - dbgln_if(TLS_DEBUG, "server key exchange"); - if (m_context.is_server) { - dbgln("unsupported: server mode"); - VERIFY_NOT_REACHED(); - } else { - payload_res = handle_server_key_exchange(buffer.slice(1, payload_size)); - } - break; - case HandshakeType::CERTIFICATE_REQUEST: - if (m_context.handshake_messages[6] >= 1) { - dbgln("unexpected certificate request message"); - payload_res = (i8)Error::UnexpectedMessage; - break; - } - ++m_context.handshake_messages[6]; - if (m_context.is_server) { - dbgln("invalid request"); - dbgln("unsupported: server mode"); - VERIFY_NOT_REACHED(); - } else { - // we do not support "certificate request" - dbgln("certificate request"); - if (on_tls_certificate_request) - on_tls_certificate_request(*this); - m_context.client_verified = VerificationNeeded; - } - break; - case HandshakeType::SERVER_HELLO_DONE_RESERVED: - if (m_context.handshake_messages[7] >= 1) { - dbgln("unexpected server hello done message"); - payload_res = (i8)Error::UnexpectedMessage; - break; - } - ++m_context.handshake_messages[7]; - dbgln_if(TLS_DEBUG, "server hello done"); - if (m_context.is_server) { - dbgln("unsupported: server mode"); - VERIFY_NOT_REACHED(); - } else { - payload_res = handle_server_hello_done(buffer.slice(1, payload_size)); - if (payload_res > 0) - write_packets = WritePacketStage::ClientHandshake; - } - break; - case HandshakeType::CERTIFICATE_VERIFY: - if (m_context.handshake_messages[8] >= 1) { - dbgln("unexpected certificate verify message"); - payload_res = (i8)Error::UnexpectedMessage; - break; - } - ++m_context.handshake_messages[8]; - dbgln_if(TLS_DEBUG, "certificate verify"); - if (m_context.connection_status == ConnectionStatus::KeyExchange) { - payload_res = handle_certificate_verify(buffer.slice(1, payload_size)); - } else { - payload_res = (i8)Error::UnexpectedMessage; - } - break; - case HandshakeType::CLIENT_KEY_EXCHANGE_RESERVED: - if (m_context.handshake_messages[9] >= 1) { - dbgln("unexpected client key exchange message"); - payload_res = (i8)Error::UnexpectedMessage; - break; - } - ++m_context.handshake_messages[9]; - dbgln_if(TLS_DEBUG, "client key exchange"); - if (m_context.is_server) { - dbgln("unsupported: server mode"); - VERIFY_NOT_REACHED(); - } else { - payload_res = (i8)Error::UnexpectedMessage; - } - break; - case HandshakeType::FINISHED: - m_context.cached_handshake.clear(); - if (m_context.handshake_messages[10] >= 1) { - dbgln("unexpected finished message"); - payload_res = (i8)Error::UnexpectedMessage; - break; - } - ++m_context.handshake_messages[10]; - dbgln_if(TLS_DEBUG, "finished"); - payload_res = handle_handshake_finished(buffer.slice(1, payload_size), write_packets); - if (payload_res > 0) { - memset(m_context.handshake_messages, 0, sizeof(m_context.handshake_messages)); - } - break; - default: - dbgln("message type not understood: {}", enum_to_string(type)); - return (i8)Error::NotUnderstood; - } - - if (type != HandshakeType::HELLO_REQUEST_RESERVED) { - update_hash(buffer.slice(0, payload_size + 1), 0); - } - - // if something went wrong, send an alert about it - if (payload_res < 0) { - switch ((Error)payload_res) { - case Error::UnexpectedMessage: { - auto packet = build_alert(true, (u8)AlertDescription::UNEXPECTED_MESSAGE); - write_packet(packet); - break; - } - case Error::CompressionNotSupported: { - auto packet = build_alert(true, (u8)AlertDescription::DECOMPRESSION_FAILURE_RESERVED); - write_packet(packet); - break; - } - case Error::BrokenPacket: { - auto packet = build_alert(true, (u8)AlertDescription::DECODE_ERROR); - write_packet(packet); - break; - } - case Error::NotVerified: { - auto packet = build_alert(true, (u8)AlertDescription::BAD_RECORD_MAC); - write_packet(packet); - break; - } - case Error::BadCertificate: { - auto packet = build_alert(true, (u8)AlertDescription::BAD_CERTIFICATE); - write_packet(packet); - break; - } - case Error::UnsupportedCertificate: { - auto packet = build_alert(true, (u8)AlertDescription::UNSUPPORTED_CERTIFICATE); - write_packet(packet); - break; - } - case Error::NoCommonCipher: { - auto packet = build_alert(true, (u8)AlertDescription::INSUFFICIENT_SECURITY); - write_packet(packet); - break; - } - case Error::NotUnderstood: - case Error::OutOfMemory: { - auto packet = build_alert(true, (u8)AlertDescription::INTERNAL_ERROR); - write_packet(packet); - break; - } - case Error::NoRenegotiation: { - auto packet = build_alert(true, (u8)AlertDescription::NO_RENEGOTIATION_RESERVED); - write_packet(packet); - break; - } - case Error::DecryptionFailed: { - auto packet = build_alert(true, (u8)AlertDescription::DECRYPTION_FAILED_RESERVED); - write_packet(packet); - break; - } - case Error::NotSafe: { - auto packet = build_alert(true, (u8)AlertDescription::DECRYPT_ERROR); - write_packet(packet); - break; - } - case Error::NeedMoreData: - // Ignore this, as it's not an "error" - dbgln_if(TLS_DEBUG, "More data needed"); - break; - default: - dbgln("Unknown TLS::Error with value {}", payload_res); - VERIFY_NOT_REACHED(); - break; - } - if (payload_res < 0) - return payload_res; - } - switch (write_packets) { - case WritePacketStage::Initial: - // nothing to write - break; - case WritePacketStage::ClientHandshake: - if (m_context.client_verified == VerificationNeeded) { - dbgln_if(TLS_DEBUG, "> Client Certificate"); - auto packet = build_certificate(); - write_packet(packet); - m_context.client_verified = Verified; - } - { - dbgln_if(TLS_DEBUG, "> Key exchange"); - auto packet = build_client_key_exchange(); - write_packet(packet); - } - { - dbgln_if(TLS_DEBUG, "> change cipher spec"); - auto packet = build_change_cipher_spec(); - write_packet(packet); - } - m_context.cipher_spec_set = 1; - m_context.local_sequence_number = 0; - { - dbgln_if(TLS_DEBUG, "> client finished"); - auto packet = build_handshake_finished(); - write_packet(packet); - } - m_context.cipher_spec_set = 0; - break; - case WritePacketStage::ServerHandshake: - // server handshake - dbgln("UNSUPPORTED: Server mode"); - VERIFY_NOT_REACHED(); - break; - case WritePacketStage::Finished: - // finished - { - dbgln_if(TLS_DEBUG, "> change cipher spec"); - auto packet = build_change_cipher_spec(); - write_packet(packet); - } - { - dbgln_if(TLS_DEBUG, "> client finished"); - auto packet = build_handshake_finished(); - write_packet(packet); - } - m_context.connection_status = ConnectionStatus::Established; - break; - } - payload_size++; - buffer_length -= payload_size; - buffer = buffer.slice(payload_size, buffer_length); - } - return original_length; -} -} diff --git a/Userland/Libraries/LibTLS/HandshakeCertificate.cpp b/Userland/Libraries/LibTLS/HandshakeCertificate.cpp deleted file mode 100644 index 8c79e4afc84..00000000000 --- a/Userland/Libraries/LibTLS/HandshakeCertificate.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2020, Ali Mohammad Pur - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -#include -#include -#include - -namespace TLS { - -ssize_t TLSv12::handle_certificate(ReadonlyBytes buffer) -{ - ssize_t res = 0; - - if (buffer.size() < 3) { - dbgln_if(TLS_DEBUG, "not enough certificate header data"); - return (i8)Error::NeedMoreData; - } - - u32 certificate_total_length = buffer[0] * 0x10000 + buffer[1] * 0x100 + buffer[2]; - - dbgln_if(TLS_DEBUG, "total length: {}", certificate_total_length); - - if (certificate_total_length <= 4) - return 3 * certificate_total_length; - - res += 3; - - if (certificate_total_length > buffer.size() - res) { - dbgln_if(TLS_DEBUG, "not enough data for claimed total cert length"); - return (i8)Error::NeedMoreData; - } - size_t size = certificate_total_length; - - bool valid_certificate = false; - - while (size > 0) { - if (buffer.size() - res < 3) { - dbgln_if(TLS_DEBUG, "not enough data for certificate length"); - return (i8)Error::NeedMoreData; - } - size_t certificate_size = buffer[res] * 0x10000 + buffer[res + 1] * 0x100 + buffer[res + 2]; - res += 3; - - if (buffer.size() - res < certificate_size) { - dbgln_if(TLS_DEBUG, "not enough data for certificate body"); - return (i8)Error::NeedMoreData; - } - - auto res_cert = res; - auto remaining = certificate_size; - - do { - if (remaining <= 3) { - dbgln("Ran out of data"); - break; - } - if (buffer.size() < (size_t)res_cert + 3) { - dbgln("not enough data to read cert size ({} < {})", buffer.size(), res_cert + 3); - break; - } - size_t certificate_size_specific = buffer[res_cert] * 0x10000 + buffer[res_cert + 1] * 0x100 + buffer[res_cert + 2]; - res_cert += 3; - remaining -= 3; - - if (certificate_size_specific > remaining) { - dbgln("invalid certificate size (expected {} but got {})", remaining, certificate_size_specific); - break; - } - remaining -= certificate_size_specific; - - auto certificate = Certificate::parse_certificate(buffer.slice(res_cert, certificate_size_specific), false); - if (!certificate.is_error()) { - m_context.certificates.empend(certificate.value()); - valid_certificate = true; - } else { - dbgln("Failed to parse client cert: {}", certificate.error()); - dbgln("{:hex-dump}", buffer.slice(res_cert, certificate_size_specific)); - dbgln(""); - } - res_cert += certificate_size_specific; - } while (remaining > 0); - if (remaining) { - dbgln("extraneous {} bytes left over after parsing certificates", remaining); - } - size -= certificate_size + 3; - res += certificate_size; - } - if (!valid_certificate) - return (i8)Error::UnsupportedCertificate; - - if ((size_t)res != buffer.size()) - dbgln("some data left unread: {} bytes out of {}", res, buffer.size()); - - return res; -} - -ssize_t TLSv12::handle_certificate_verify(ReadonlyBytes) -{ - dbgln("FIXME: parse_verify"); - return 0; -} - -} diff --git a/Userland/Libraries/LibTLS/HandshakeClient.cpp b/Userland/Libraries/LibTLS/HandshakeClient.cpp deleted file mode 100644 index 7a62a75062c..00000000000 --- a/Userland/Libraries/LibTLS/HandshakeClient.cpp +++ /dev/null @@ -1,412 +0,0 @@ -/* - * Copyright (c) 2020, Ali Mohammad Pur - * Copyright (c) 2022, Michiel Visser - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace TLS { - -bool TLSv12::expand_key() -{ - u8 key[192]; // soooooooo many constants - auto key_buffer = Bytes { key, sizeof(key) }; - - auto is_aead = this->is_aead(); - - if (m_context.master_key.size() == 0) { - dbgln("expand_key() with empty master key"); - return false; - } - - auto key_size = key_length(); - VERIFY(key_size); - auto mac_size = mac_length(); - auto iv_size = iv_length(); - - pseudorandom_function( - key_buffer, - m_context.master_key, - (u8 const*)"key expansion", 13, - ReadonlyBytes { m_context.remote_random, sizeof(m_context.remote_random) }, - ReadonlyBytes { m_context.local_random, sizeof(m_context.local_random) }); - - size_t offset = 0; - if (is_aead) { - iv_size = 4; // Explicit IV size. - } else { - memcpy(m_context.crypto.local_mac, key + offset, mac_size); - offset += mac_size; - memcpy(m_context.crypto.remote_mac, key + offset, mac_size); - offset += mac_size; - } - - auto client_key = key + offset; - offset += key_size; - auto server_key = key + offset; - offset += key_size; - auto client_iv = key + offset; - offset += iv_size; - auto server_iv = key + offset; - offset += iv_size; - - if constexpr (TLS_DEBUG) { - dbgln("client key"); - print_buffer(client_key, key_size); - dbgln("server key"); - print_buffer(server_key, key_size); - dbgln("client iv"); - print_buffer(client_iv, iv_size); - dbgln("server iv"); - print_buffer(server_iv, iv_size); - if (!is_aead) { - dbgln("client mac key"); - print_buffer(m_context.crypto.local_mac, mac_size); - dbgln("server mac key"); - print_buffer(m_context.crypto.remote_mac, mac_size); - } - } - - switch (get_cipher_algorithm(m_context.cipher)) { - case CipherAlgorithm::AES_128_CBC: - case CipherAlgorithm::AES_256_CBC: { - VERIFY(!is_aead); - memcpy(m_context.crypto.local_iv, client_iv, iv_size); - memcpy(m_context.crypto.remote_iv, server_iv, iv_size); - - m_cipher_local = Crypto::Cipher::AESCipher::CBCMode(ReadonlyBytes { client_key, key_size }, key_size * 8, Crypto::Cipher::Intent::Encryption, Crypto::Cipher::PaddingMode::RFC5246); - m_cipher_remote = Crypto::Cipher::AESCipher::CBCMode(ReadonlyBytes { server_key, key_size }, key_size * 8, Crypto::Cipher::Intent::Decryption, Crypto::Cipher::PaddingMode::RFC5246); - break; - } - case CipherAlgorithm::AES_128_GCM: - case CipherAlgorithm::AES_256_GCM: { - VERIFY(is_aead); - memcpy(m_context.crypto.local_aead_iv, client_iv, iv_size); - memcpy(m_context.crypto.remote_aead_iv, server_iv, iv_size); - - m_cipher_local = Crypto::Cipher::AESCipher::GCMMode(ReadonlyBytes { client_key, key_size }, key_size * 8, Crypto::Cipher::Intent::Encryption, Crypto::Cipher::PaddingMode::RFC5246); - m_cipher_remote = Crypto::Cipher::AESCipher::GCMMode(ReadonlyBytes { server_key, key_size }, key_size * 8, Crypto::Cipher::Intent::Decryption, Crypto::Cipher::PaddingMode::RFC5246); - break; - } - case CipherAlgorithm::AES_128_CCM: - dbgln("Requested unimplemented AES CCM cipher"); - TODO(); - case CipherAlgorithm::AES_128_CCM_8: - dbgln("Requested unimplemented AES CCM-8 block cipher"); - TODO(); - default: - dbgln("Requested unknown block cipher"); - VERIFY_NOT_REACHED(); - } - - m_context.crypto.created = 1; - - return true; -} - -bool TLSv12::compute_master_secret_from_pre_master_secret(size_t length) -{ - if (m_context.premaster_key.size() == 0 || length < 48) { - dbgln("there's no way I can make a master secret like this"); - dbgln("I'd like to talk to your manager about this length of {}", length); - return false; - } - - if (m_context.master_key.try_resize(length).is_error()) { - dbgln("Couldn't allocate enough space for the master key :("); - return false; - } - - if (m_context.extensions.extended_master_secret) { - Crypto::Hash::Manager handshake_hash_copy = m_context.handshake_hash.copy(); - auto digest = handshake_hash_copy.digest(); - auto session_hash = ReadonlyBytes { digest.immutable_data(), handshake_hash_copy.digest_size() }; - - pseudorandom_function( - m_context.master_key, - m_context.premaster_key, - (u8 const*)"extended master secret", 22, - session_hash, - {}); - } else { - pseudorandom_function( - m_context.master_key, - m_context.premaster_key, - (u8 const*)"master secret", 13, - ReadonlyBytes { m_context.local_random, sizeof(m_context.local_random) }, - ReadonlyBytes { m_context.remote_random, sizeof(m_context.remote_random) }); - } - - m_context.premaster_key.clear(); - if constexpr (TLS_DEBUG) { - dbgln("master key:"); - print_buffer(m_context.master_key); - } - - expand_key(); - return true; -} - -void TLSv12::build_rsa_pre_master_secret(PacketBuilder& builder) -{ - u8 random_bytes[48]; - size_t bytes = 48; - - fill_with_random(random_bytes); - - // remove zeros from the random bytes - for (size_t i = 0; i < bytes; ++i) { - if (!random_bytes[i]) - random_bytes[i--] = get_random(); - } - - if (m_context.is_server) { - dbgln("Server mode not supported"); - return; - } else { - *(u16*)random_bytes = AK::convert_between_host_and_network_endian((u16)ProtocolVersion::VERSION_1_2); - } - - auto premaster_key_result = ByteBuffer::copy(random_bytes, bytes); - if (premaster_key_result.is_error()) { - dbgln("RSA premaster key generation failed, not enough memory"); - return; - } - m_context.premaster_key = premaster_key_result.release_value(); - - // RFC5246 section 7.4.2: The sender's certificate MUST come first in the list. - auto& certificate = m_context.certificates.first(); - if constexpr (TLS_DEBUG) { - dbgln("PreMaster secret"); - print_buffer(m_context.premaster_key); - } - - Crypto::PK::RSA_PKCS1_EME rsa(certificate.public_key.rsa.modulus(), 0, certificate.public_key.rsa.public_exponent()); - - Vector out; - out.resize(rsa.output_size()); - auto outbuf = out.span(); - rsa.encrypt(m_context.premaster_key, outbuf); - - if constexpr (TLS_DEBUG) { - dbgln("Encrypted: "); - print_buffer(outbuf); - } - - builder.append_u24(outbuf.size() + 2); - builder.append((u16)outbuf.size()); - 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_result = ByteBuffer::create_uninitialized(dh_key_size); - if (dh_Yc_bytes_result.is_error()) { - dbgln("Failed to build DHE_RSA premaster secret: not enough memory"); - return; - } - auto dh_Yc_bytes = dh_Yc_bytes_result.release_value(); - dh_Yc.export_data(dh_Yc_bytes); - - auto premaster_key = Crypto::NumberTheory::ModularPower(dh_Ys, dh_random, dh_p); - auto premaster_key_result = ByteBuffer::create_uninitialized(dh_key_size); - if (premaster_key_result.is_error()) { - dbgln("Failed to build DHE_RSA premaster secret: not enough memory"); - return; - } - m_context.premaster_key = premaster_key_result.release_value(); - 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_deprecated(16)); - dbgln("dh_Yc: {:hex-dump}", (ReadonlyBytes)dh_Yc_bytes); - dbgln("premaster key: {:hex-dump}", (ReadonlyBytes)m_context.premaster_key); - } - - builder.append_u24(dh_key_size + 2); - builder.append((u16)dh_key_size); - builder.append(dh_Yc_bytes); -} - -void TLSv12::build_ecdhe_rsa_pre_master_secret(PacketBuilder& builder) -{ - // Create a random private key - auto private_key_result = m_context.server_key_exchange_curve->generate_private_key(); - if (private_key_result.is_error()) { - dbgln("Failed to build ECDHE_RSA premaster secret: not enough memory"); - return; - } - auto private_key = private_key_result.release_value(); - - // Calculate the public key from the private key - auto public_key_result = m_context.server_key_exchange_curve->generate_public_key(private_key); - 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 shared point by multiplying the client private key and the server public key - ReadonlyBytes server_public_key_bytes = m_context.server_diffie_hellman_params.p; - auto shared_point_result = m_context.server_key_exchange_curve->compute_coordinate(private_key, server_public_key_bytes); - if (shared_point_result.is_error()) { - dbgln("Failed to build ECDHE_RSA premaster secret: not enough memory"); - return; - } - auto shared_point = shared_point_result.release_value(); - - // Derive the premaster key from the shared point - auto premaster_key_result = m_context.server_key_exchange_curve->derive_premaster_key(shared_point); - if (premaster_key_result.is_error()) { - dbgln("Failed to build ECDHE_RSA premaster secret: not enough memory"); - return; - } - m_context.premaster_key = premaster_key_result.release_value(); - - if constexpr (TLS_DEBUG) { - dbgln("Build ECDHE_RSA pre master secret"); - dbgln("client private key: {:hex-dump}", (ReadonlyBytes)private_key); - dbgln("client public key: {:hex-dump}", (ReadonlyBytes)public_key); - dbgln("premaster key: {:hex-dump}", (ReadonlyBytes)m_context.premaster_key); - } - - builder.append_u24(public_key.size() + 1); - builder.append((u8)public_key.size()); - builder.append(public_key); -} - -ByteBuffer TLSv12::build_certificate() -{ - PacketBuilder builder { ContentType::HANDSHAKE, m_context.options.version }; - - Vector certificates; - Vector* local_certificates = nullptr; - - if (m_context.is_server) { - dbgln("Unsupported: Server mode"); - VERIFY_NOT_REACHED(); - } else { - local_certificates = &m_context.client_certificates; - } - - constexpr size_t der_length_delta = 3; - constexpr size_t certificate_vector_header_size = 3; - - size_t total_certificate_size = 0; - - for (size_t i = 0; i < local_certificates->size(); ++i) { - auto& certificate = local_certificates->at(i); - if (!certificate.der.is_empty()) { - total_certificate_size += certificate.der.size() + der_length_delta; - - // FIXME: Check for and respond with only the requested certificate types. - if (true) { - certificates.append(certificate); - } - } - } - - builder.append((u8)HandshakeType::CERTIFICATE); - - if (!total_certificate_size) { - dbgln_if(TLS_DEBUG, "No certificates, sending empty certificate message"); - builder.append_u24(certificate_vector_header_size); - builder.append_u24(total_certificate_size); - } else { - builder.append_u24(total_certificate_size + certificate_vector_header_size); // 3 bytes for header - builder.append_u24(total_certificate_size); - - for (auto& certificate : certificates) { - if (!certificate.der.is_empty()) { - builder.append_u24(certificate.der.size()); - builder.append(certificate.der.bytes()); - } - } - } - auto packet = builder.build(); - update_packet(packet); - return packet; -} - -ByteBuffer TLSv12::build_client_key_exchange() -{ - bool chain_verified = m_context.verify_chain(m_context.extensions.SNI); - if (!chain_verified) { - dbgln("certificate verification failed :("); - alert(AlertLevel::FATAL, AlertDescription::BAD_CERTIFICATE); - return {}; - } - - PacketBuilder builder { ContentType::HANDSHAKE, m_context.options.version }; - builder.append((u8)HandshakeType::CLIENT_KEY_EXCHANGE_RESERVED); - - switch (get_key_exchange_algorithm(m_context.cipher)) { - case KeyExchangeAlgorithm::RSA: - build_rsa_pre_master_secret(builder); - break; - case KeyExchangeAlgorithm::DHE_DSS: - dbgln("Client key exchange for DHE_DSS is not implemented"); - TODO(); - break; - case KeyExchangeAlgorithm::DH_DSS: - case KeyExchangeAlgorithm::DH_RSA: - dbgln("Client key exchange for DH algorithms is not implemented"); - TODO(); - break; - case KeyExchangeAlgorithm::DHE_RSA: - build_dhe_rsa_pre_master_secret(builder); - break; - case KeyExchangeAlgorithm::DH_anon: - dbgln("Client key exchange for DH_anon is not implemented"); - TODO(); - break; - case KeyExchangeAlgorithm::ECDHE_RSA: - case KeyExchangeAlgorithm::ECDHE_ECDSA: - build_ecdhe_rsa_pre_master_secret(builder); - break; - case KeyExchangeAlgorithm::ECDH_ECDSA: - case KeyExchangeAlgorithm::ECDH_RSA: - case KeyExchangeAlgorithm::ECDH_anon: - dbgln("Client key exchange for ECDHE algorithms is not implemented"); - TODO(); - break; - default: - dbgln("Unknown client key exchange algorithm"); - VERIFY_NOT_REACHED(); - break; - } - - m_context.connection_status = ConnectionStatus::KeyExchange; - - auto packet = builder.build(); - - update_packet(packet); - - if (!compute_master_secret_from_pre_master_secret(48)) { - dbgln("oh noes we could not derive a master key :("); - } - - return packet; -} - -} diff --git a/Userland/Libraries/LibTLS/HandshakeServer.cpp b/Userland/Libraries/LibTLS/HandshakeServer.cpp deleted file mode 100644 index 4f94b95f460..00000000000 --- a/Userland/Libraries/LibTLS/HandshakeServer.cpp +++ /dev/null @@ -1,530 +0,0 @@ -/* - * Copyright (c) 2020, Ali Mohammad Pur - * Copyright (c) 2022, Michiel Visser - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace TLS { - -ssize_t TLSv12::handle_server_hello(ReadonlyBytes buffer, WritePacketStage& write_packets) -{ - write_packets = WritePacketStage::Initial; - if (m_context.connection_status != ConnectionStatus::Disconnected && m_context.connection_status != ConnectionStatus::Renegotiating) { - dbgln("unexpected hello message"); - return (i8)Error::UnexpectedMessage; - } - ssize_t res = 0; - size_t min_hello_size = 41; - - if (min_hello_size > buffer.size()) { - dbgln("need more data"); - return (i8)Error::NeedMoreData; - } - size_t following_bytes = buffer[0] * 0x10000 + buffer[1] * 0x100 + buffer[2]; - res += 3; - if (buffer.size() - res < following_bytes) { - dbgln("not enough data after header: {} < {}", buffer.size() - res, following_bytes); - return (i8)Error::NeedMoreData; - } - - if (buffer.size() - res < 2) { - dbgln("not enough data for version"); - return (i8)Error::NeedMoreData; - } - auto version = static_cast(AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(res)))); - - res += 2; - if (!supports_version(version)) - return (i8)Error::NotSafe; - - memcpy(m_context.remote_random, buffer.offset_pointer(res), sizeof(m_context.remote_random)); - res += sizeof(m_context.remote_random); - - u8 session_length = buffer[res++]; - if (buffer.size() - res < session_length) { - dbgln("not enough data for session id"); - return (i8)Error::NeedMoreData; - } - - if (session_length && session_length <= 32) { - memcpy(m_context.session_id, buffer.offset_pointer(res), session_length); - m_context.session_id_size = session_length; - if constexpr (TLS_DEBUG) { - dbgln("Remote session ID:"); - print_buffer(ReadonlyBytes { m_context.session_id, session_length }); - } - } else { - m_context.session_id_size = 0; - } - res += session_length; - - if (buffer.size() - res < 2) { - dbgln("not enough data for cipher suite listing"); - return (i8)Error::NeedMoreData; - } - auto cipher = static_cast(AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(res)))); - res += 2; - if (!supports_cipher(cipher)) { - m_context.cipher = CipherSuite::TLS_NULL_WITH_NULL_NULL; - dbgln("No supported cipher could be agreed upon"); - return (i8)Error::NoCommonCipher; - } - m_context.cipher = cipher; - dbgln_if(TLS_DEBUG, "Cipher: {}", enum_to_string(cipher)); - - // Simplification: We only support handshake hash functions via HMAC - m_context.handshake_hash.initialize(hmac_hash()); - - // Compression method - if (buffer.size() - res < 1) - return (i8)Error::NeedMoreData; - u8 compression = buffer[res++]; - if (compression != 0) - return (i8)Error::CompressionNotSupported; - - if (m_context.connection_status != ConnectionStatus::Renegotiating) - m_context.connection_status = ConnectionStatus::Negotiating; - if (m_context.is_server) { - dbgln("unsupported: server mode"); - write_packets = WritePacketStage::ServerHandshake; - } - - // Presence of extensions is determined by availability of bytes after compression_method - if (buffer.size() - res >= 2) { - auto extensions_bytes_total = AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(res += 2))); - dbgln_if(TLS_DEBUG, "Extensions bytes total: {}", extensions_bytes_total); - } - - while (buffer.size() - res >= 4) { - auto extension_type = (ExtensionType)AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(res))); - res += 2; - u16 extension_length = AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(res))); - res += 2; - - dbgln_if(TLS_DEBUG, "Extension {} with length {}", enum_to_string(extension_type), extension_length); - - if (buffer.size() - res < extension_length) - return (i8)Error::NeedMoreData; - - if (extension_type == ExtensionType::SERVER_NAME) { - // RFC6066 section 3: SNI extension_data can be empty in the server hello - if (extension_length > 0) { - // ServerNameList total size - if (buffer.size() - res < 2) - return (i8)Error::NeedMoreData; - auto sni_name_list_bytes = AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(res += 2))); - dbgln_if(TLS_DEBUG, "SNI: expecting ServerNameList of {} bytes", sni_name_list_bytes); - - // Exactly one ServerName should be present - if (buffer.size() - res < 3) - return (i8)Error::NeedMoreData; - auto sni_name_type = (NameType)(*(u8 const*)buffer.offset_pointer(res++)); - auto sni_name_length = AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(res += 2))); - - if (sni_name_type != NameType::HOST_NAME) - return (i8)Error::NotUnderstood; - - if (sizeof(sni_name_type) + sizeof(sni_name_length) + sni_name_length != sni_name_list_bytes) - return (i8)Error::BrokenPacket; - - // Read out the host_name - if (buffer.size() - res < sni_name_length) - return (i8)Error::NeedMoreData; - m_context.extensions.SNI = ByteString { (char const*)buffer.offset_pointer(res), sni_name_length }; - res += sni_name_length; - dbgln("SNI host_name: {}", m_context.extensions.SNI); - } - } else if (extension_type == ExtensionType::APPLICATION_LAYER_PROTOCOL_NEGOTIATION && m_context.alpn.size()) { - if (buffer.size() - res > 2) { - auto alpn_length = AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(res))); - if (alpn_length && alpn_length <= extension_length - 2) { - u8 const* alpn = buffer.offset_pointer(res + 2); - size_t alpn_position = 0; - while (alpn_position < alpn_length) { - u8 alpn_size = alpn[alpn_position++]; - if (alpn_size + alpn_position >= extension_length) - break; - ByteString alpn_str { (char const*)alpn + alpn_position, alpn_length }; - if (alpn_size && m_context.alpn.contains_slow(alpn_str)) { - m_context.negotiated_alpn = alpn_str; - dbgln("negotiated alpn: {}", alpn_str); - break; - } - alpn_position += alpn_length; - if (!m_context.is_server) // server hello must contain one ALPN - break; - } - } - } - res += extension_length; - } else if (extension_type == ExtensionType::SIGNATURE_ALGORITHMS) { - dbgln("supported signatures: "); - print_buffer(buffer.slice(res, extension_length)); - res += extension_length; - // FIXME: what are we supposed to do here? - } else if (extension_type == ExtensionType::EC_POINT_FORMATS) { - // 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 if (extension_type == ExtensionType::EXTENDED_MASTER_SECRET) { - m_context.extensions.extended_master_secret = true; - res += extension_length; - } else { - dbgln("Encountered unknown extension {} with length {}", enum_to_string(extension_type), extension_length); - res += extension_length; - } - } - - return res; -} - -ssize_t TLSv12::handle_server_hello_done(ReadonlyBytes buffer) -{ - if (buffer.size() < 3) - return (i8)Error::NeedMoreData; - - size_t size = buffer[0] * 0x10000 + buffer[1] * 0x100 + buffer[2]; - - if (buffer.size() - 3 < size) - return (i8)Error::NeedMoreData; - - return size + 3; -} - -ByteBuffer TLSv12::build_server_key_exchange() -{ - dbgln("FIXME: build_server_key_exchange"); - return {}; -} - -ssize_t TLSv12::handle_server_key_exchange(ReadonlyBytes buffer) -{ - switch (get_key_exchange_algorithm(m_context.cipher)) { - case KeyExchangeAlgorithm::RSA: - case KeyExchangeAlgorithm::DH_DSS: - case KeyExchangeAlgorithm::DH_RSA: - // RFC 5246 section 7.4.3. Server Key Exchange Message - // It is not legal to send the server key exchange message for RSA, DH_DSS, DH_RSA - dbgln("Server key exchange received for RSA, DH_DSS or DH_RSA is not legal"); - return (i8)Error::UnexpectedMessage; - case KeyExchangeAlgorithm::DHE_DSS: - dbgln("Server key exchange for DHE_DSS is not implemented"); - TODO(); - break; - case KeyExchangeAlgorithm::DHE_RSA: - return handle_dhe_rsa_server_key_exchange(buffer); - case KeyExchangeAlgorithm::DH_anon: - dbgln("Server key exchange for DH_anon is not implemented"); - TODO(); - break; - case KeyExchangeAlgorithm::ECDHE_RSA: - return handle_ecdhe_rsa_server_key_exchange(buffer); - case KeyExchangeAlgorithm::ECDHE_ECDSA: - return handle_ecdhe_ecdsa_server_key_exchange(buffer); - case KeyExchangeAlgorithm::ECDH_ECDSA: - case KeyExchangeAlgorithm::ECDH_RSA: - case KeyExchangeAlgorithm::ECDH_anon: - dbgln("Server key exchange for ECDHE algorithms is not implemented"); - TODO(); - break; - default: - dbgln("Unknown server key exchange algorithm"); - VERIFY_NOT_REACHED(); - break; - } - 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); - auto p_result = ByteBuffer::copy(dh_p); - if (p_result.is_error()) { - dbgln("dhe_rsa_server_key_exchange failed: Not enough memory"); - return (i8)Error::OutOfMemory; - } - m_context.server_diffie_hellman_params.p = p_result.release_value(); - - 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); - auto g_result = ByteBuffer::copy(dh_g); - if (g_result.is_error()) { - dbgln("dhe_rsa_server_key_exchange failed: Not enough memory"); - return (i8)Error::OutOfMemory; - } - m_context.server_diffie_hellman_params.g = g_result.release_value(); - - 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); - auto Ys_result = ByteBuffer::copy(dh_Ys); - if (Ys_result.is_error()) { - dbgln("dhe_rsa_server_key_exchange failed: Not enough memory"); - return (i8)Error::OutOfMemory; - } - m_context.server_diffie_hellman_params.Ys = Ys_result.release_value(); - - 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); - } - - auto server_key_info = buffer.slice(3, 6 + dh_p_length + dh_g_length + dh_Ys_length); - auto signature = buffer.slice(9 + dh_p_length + dh_g_length + dh_Ys_length); - return verify_rsa_server_key_exchange(server_key_info, signature); -} - -ssize_t TLSv12::handle_ecdhe_server_key_exchange(ReadonlyBytes buffer, u8& server_public_key_length) -{ - if (buffer.size() < 7) - return (i8)Error::NeedMoreData; - - auto curve_type = buffer[3]; - if (curve_type != (u8)ECCurveType::NAMED_CURVE) - return (i8)Error::NotUnderstood; - - auto curve = static_cast(AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(4)))); - if (!m_context.options.elliptic_curves.contains_slow(curve)) - return (i8)Error::NotUnderstood; - - switch ((SupportedGroup)curve) { - case SupportedGroup::X25519: - m_context.server_key_exchange_curve = make(); - break; - case SupportedGroup::X448: - m_context.server_key_exchange_curve = make(); - break; - case SupportedGroup::SECP256R1: - m_context.server_key_exchange_curve = make(); - break; - case SupportedGroup::SECP384R1: - m_context.server_key_exchange_curve = make(); - break; - default: - return (i8)Error::NotUnderstood; - } - - server_public_key_length = buffer[6]; - if (server_public_key_length != m_context.server_key_exchange_curve->key_size()) - return (i8)Error::NotUnderstood; - - if (buffer.size() < 7u + server_public_key_length) - return (i8)Error::NeedMoreData; - - 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 (i8)Error::OutOfMemory; - } - 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); - } - - return 0; -} - -ssize_t TLSv12::handle_ecdhe_rsa_server_key_exchange(ReadonlyBytes buffer) -{ - u8 server_public_key_length; - if (auto result = handle_ecdhe_server_key_exchange(buffer, server_public_key_length)) { - return result; - } - - auto server_key_info = buffer.slice(3, 4 + server_public_key_length); - auto signature = buffer.slice(7 + server_public_key_length); - return verify_rsa_server_key_exchange(server_key_info, signature); -} - -ssize_t TLSv12::verify_rsa_server_key_exchange(ReadonlyBytes server_key_info_buffer, ReadonlyBytes signature_buffer) -{ - auto signature_hash = signature_buffer[0]; - auto signature_algorithm = static_cast(signature_buffer[1]); - if (signature_algorithm != SignatureAlgorithm::RSA) { - dbgln("verify_rsa_server_key_exchange failed: Signature algorithm is not RSA, instead {}", enum_to_string(signature_algorithm)); - return (i8)Error::NotUnderstood; - } - - auto signature_length = AK::convert_between_host_and_network_endian(ByteReader::load16(signature_buffer.offset_pointer(2))); - auto signature = signature_buffer.slice(4, signature_length); - - if (m_context.certificates.is_empty()) { - dbgln("verify_rsa_server_key_exchange failed: Attempting to verify signature without certificates"); - return (i8)Error::NotSafe; - } - // RFC5246 section 7.4.2: The sender's certificate MUST come first in the list. - auto certificate_public_key = m_context.certificates.first().public_key; - Crypto::PK::RSAPrivateKey dummy_private_key; - auto rsa = Crypto::PK::RSA(certificate_public_key.rsa, dummy_private_key); - - auto signature_verify_buffer_result = ByteBuffer::create_uninitialized(signature_length); - if (signature_verify_buffer_result.is_error()) { - dbgln("verify_rsa_server_key_exchange failed: Not enough memory"); - return (i8)Error::OutOfMemory; - } - auto signature_verify_buffer = signature_verify_buffer_result.release_value(); - auto signature_verify_bytes = signature_verify_buffer.bytes(); - rsa.verify(signature, signature_verify_bytes); - - auto message_result = ByteBuffer::create_uninitialized(64 + server_key_info_buffer.size()); - if (message_result.is_error()) { - dbgln("verify_rsa_server_key_exchange failed: Not enough memory"); - return (i8)Error::OutOfMemory; - } - auto message = message_result.release_value(); - message.overwrite(0, m_context.local_random, 32); - message.overwrite(32, m_context.remote_random, 32); - message.overwrite(64, server_key_info_buffer.data(), server_key_info_buffer.size()); - - Crypto::Hash::HashKind hash_kind; - switch ((HashAlgorithm)signature_hash) { - case HashAlgorithm::SHA1: - hash_kind = Crypto::Hash::HashKind::SHA1; - break; - case HashAlgorithm::SHA256: - hash_kind = Crypto::Hash::HashKind::SHA256; - break; - case HashAlgorithm::SHA384: - hash_kind = Crypto::Hash::HashKind::SHA384; - break; - case HashAlgorithm::SHA512: - hash_kind = Crypto::Hash::HashKind::SHA512; - break; - default: - dbgln("verify_rsa_server_key_exchange failed: Hash algorithm is not SHA1/256/384/512, instead {}", signature_hash); - return (i8)Error::NotUnderstood; - } - - auto pkcs1 = Crypto::PK::EMSA_PKCS1_V1_5(hash_kind); - auto verification = pkcs1.verify(message, signature_verify_bytes, signature_length * 8); - - if (verification == Crypto::VerificationConsistency::Inconsistent) { - dbgln("verify_rsa_server_key_exchange failed: Verification of signature inconsistent"); - return (i8)Error::NotSafe; - } - - return 0; -} - -ssize_t TLSv12::handle_ecdhe_ecdsa_server_key_exchange(ReadonlyBytes buffer) -{ - u8 server_public_key_length; - if (auto result = handle_ecdhe_server_key_exchange(buffer, server_public_key_length)) { - return result; - } - - auto server_key_info = buffer.slice(3, 4 + server_public_key_length); - auto signature = buffer.slice(7 + server_public_key_length); - return verify_ecdsa_server_key_exchange(server_key_info, signature); -} - -ssize_t TLSv12::verify_ecdsa_server_key_exchange(ReadonlyBytes server_key_info_buffer, ReadonlyBytes signature_buffer) -{ - auto signature_hash = signature_buffer[0]; - auto signature_algorithm = signature_buffer[1]; - if (signature_algorithm != (u8)SignatureAlgorithm::ECDSA) { - dbgln("verify_ecdsa_server_key_exchange failed: Signature algorithm is not ECDSA, instead {}", signature_algorithm); - return (i8)Error::NotUnderstood; - } - - auto signature_length = AK::convert_between_host_and_network_endian(ByteReader::load16(signature_buffer.offset_pointer(2))); - auto signature = signature_buffer.slice(4, signature_length); - - if (m_context.certificates.is_empty()) { - dbgln("verify_ecdsa_server_key_exchange failed: Attempting to verify signature without certificates"); - return (i8)Error::NotSafe; - } - ReadonlyBytes server_point = m_context.certificates.first().public_key.raw_key; - - auto message_result = ByteBuffer::create_uninitialized(64 + server_key_info_buffer.size()); - if (message_result.is_error()) { - dbgln("verify_ecdsa_server_key_exchange failed: Not enough memory"); - return (i8)Error::OutOfMemory; - } - auto message = message_result.release_value(); - message.overwrite(0, m_context.local_random, 32); - message.overwrite(32, m_context.remote_random, 32); - message.overwrite(64, server_key_info_buffer.data(), server_key_info_buffer.size()); - - Crypto::Hash::HashKind hash_kind; - switch ((HashAlgorithm)signature_hash) { - case HashAlgorithm::SHA256: - hash_kind = Crypto::Hash::HashKind::SHA256; - break; - case HashAlgorithm::SHA384: - hash_kind = Crypto::Hash::HashKind::SHA384; - break; - case HashAlgorithm::SHA512: - hash_kind = Crypto::Hash::HashKind::SHA512; - break; - default: - dbgln("verify_ecdsa_server_key_exchange failed: Hash algorithm is not SHA256/384/512, instead {}", signature_hash); - return (i8)Error::NotUnderstood; - } - - ErrorOr res = AK::Error::from_errno(ENOTSUP); - auto& public_key = m_context.certificates.first().public_key; - switch (public_key.algorithm.ec_parameters) { - case SupportedGroup::SECP256R1: { - Crypto::Hash::Manager manager(hash_kind); - manager.update(message); - auto digest = manager.digest(); - - Crypto::Curves::SECP256r1 curve; - res = curve.verify(digest.bytes(), server_point, signature); - break; - } - case SupportedGroup::SECP384R1: { - Crypto::Hash::Manager manager(hash_kind); - manager.update(message); - auto digest = manager.digest(); - - Crypto::Curves::SECP384r1 curve; - res = curve.verify(digest.bytes(), server_point, signature); - break; - } - default: { - dbgln("verify_ecdsa_server_key_exchange failed: Server certificate public key algorithm is not supported: {}", to_underlying(public_key.algorithm.ec_parameters)); - break; - } - } - - if (res.is_error()) { - dbgln("verify_ecdsa_server_key_exchange failed: {}", res.error()); - return (i8)Error::NotUnderstood; - } - - bool verification_ok = res.release_value(); - if (!verification_ok) { - dbgln("verify_ecdsa_server_key_exchange failed: Verification of signature failed"); - return (i8)Error::NotSafe; - } - - return 0; -} - -} diff --git a/Userland/Libraries/LibTLS/Record.cpp b/Userland/Libraries/LibTLS/Record.cpp deleted file mode 100644 index be65ba54e7a..00000000000 --- a/Userland/Libraries/LibTLS/Record.cpp +++ /dev/null @@ -1,572 +0,0 @@ -/* - * Copyright (c) 2020, Ali Mohammad Pur - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace TLS { - -ByteBuffer TLSv12::build_alert(bool critical, u8 code) -{ - PacketBuilder builder(ContentType::ALERT, (u16)m_context.options.version); - builder.append((u8)(critical ? AlertLevel::FATAL : AlertLevel::WARNING)); - builder.append(code); - - if (critical) - m_context.critical_error = code; - - auto packet = builder.build(); - update_packet(packet); - - return packet; -} - -void TLSv12::alert(AlertLevel level, AlertDescription code) -{ - auto the_alert = build_alert(level == AlertLevel::FATAL, (u8)code); - write_packet(the_alert, true); - MUST(flush()); -} - -void TLSv12::write_packet(ByteBuffer& packet, bool immediately) -{ - auto schedule_or_perform_flush = [&](bool immediate) { - if (m_context.connection_status > ConnectionStatus::Disconnected) { - if (!m_has_scheduled_write_flush && !immediate) { - dbgln_if(TLS_DEBUG, "Scheduling write of {}", m_context.tls_buffer.size()); - Core::deferred_invoke([this] { write_into_socket(); }); - m_has_scheduled_write_flush = true; - } else { - // multiple packet are available, let's flush some out - dbgln_if(TLS_DEBUG, "Flushing scheduled write of {}", m_context.tls_buffer.size()); - write_into_socket(); - // the deferred invoke is still in place - m_has_scheduled_write_flush = true; - } - } - }; - // Record size limit is 18432 bytes, leave some headroom and flush at 16K. - if (m_context.tls_buffer.size() + packet.size() > 16 * KiB) - schedule_or_perform_flush(true); - - if (m_context.tls_buffer.try_append(packet.data(), packet.size()).is_error()) { - // Toooooo bad, drop the record on the ground. - return; - } - schedule_or_perform_flush(immediately); -} - -void TLSv12::update_packet(ByteBuffer& packet) -{ - u32 header_size = 5; - ByteReader::store(packet.offset_pointer(3), AK::convert_between_host_and_network_endian((u16)(packet.size() - header_size))); - - if (packet[0] != (u8)ContentType::CHANGE_CIPHER_SPEC) { - if (packet[0] == (u8)ContentType::HANDSHAKE && packet.size() > header_size) { - auto handshake_type = static_cast(packet[header_size]); - if (handshake_type != HandshakeType::HELLO_REQUEST_RESERVED && handshake_type != HandshakeType::HELLO_VERIFY_REQUEST_RESERVED) { - update_hash(packet.bytes(), header_size); - } - } - if (m_context.cipher_spec_set && m_context.crypto.created) { - size_t length = packet.size() - header_size; - size_t block_size = 0; - size_t padding = 0; - size_t mac_size = 0; - - m_cipher_local.visit( - [&](Empty&) { VERIFY_NOT_REACHED(); }, - [&](Crypto::Cipher::AESCipher::GCMMode& gcm) { - VERIFY(is_aead()); - block_size = gcm.cipher().block_size(); - padding = 0; - mac_size = 0; // AEAD provides its own authentication scheme. - }, - [&](Crypto::Cipher::AESCipher::CBCMode& cbc) { - VERIFY(!is_aead()); - block_size = cbc.cipher().block_size(); - // If the length is already a multiple a block_size, - // an entire block of padding is added. - // In short, we _never_ have no padding. - mac_size = mac_length(); - length += mac_size; - padding = block_size - length % block_size; - length += padding; - }); - - if (m_context.crypto.created == 1) { - // `buffer' will continue to be encrypted - auto buffer_result = ByteBuffer::create_uninitialized(length); - if (buffer_result.is_error()) { - dbgln("LibTLS: Failed to allocate enough memory"); - VERIFY_NOT_REACHED(); - } - auto buffer = buffer_result.release_value(); - size_t buffer_position = 0; - auto iv_size = iv_length(); - - // copy the packet, sans the header - buffer.overwrite(buffer_position, packet.offset_pointer(header_size), packet.size() - header_size); - buffer_position += packet.size() - header_size; - - ByteBuffer ct; - - m_cipher_local.visit( - [&](Empty&) { VERIFY_NOT_REACHED(); }, - [&](Crypto::Cipher::AESCipher::GCMMode& gcm) { - VERIFY(is_aead()); - // We need enough space for a header, the data, a tag, and the IV - auto ct_buffer_result = ByteBuffer::create_uninitialized(length + header_size + iv_size + 16); - if (ct_buffer_result.is_error()) { - dbgln("LibTLS: Failed to allocate enough memory for the ciphertext"); - VERIFY_NOT_REACHED(); - } - ct = ct_buffer_result.release_value(); - - // copy the header over - ct.overwrite(0, packet.data(), header_size - 2); - - // AEAD AAD (13) - // Seq. no (8) - // content type (1) - // version (2) - // length (2) - u8 aad[13]; - Bytes aad_bytes { aad, 13 }; - FixedMemoryStream aad_stream { aad_bytes }; - - u64 seq_no = AK::convert_between_host_and_network_endian(m_context.local_sequence_number); - u16 len = AK::convert_between_host_and_network_endian((u16)(packet.size() - header_size)); - - MUST(aad_stream.write_value(seq_no)); // sequence number - MUST(aad_stream.write_until_depleted(packet.bytes().slice(0, 3))); // content-type + version - MUST(aad_stream.write_value(len)); // length - VERIFY(MUST(aad_stream.tell()) == MUST(aad_stream.size())); - - // AEAD IV (12) - // IV (4) - // (Nonce) (8) - // -- Our GCM impl takes 16 bytes - // zero (4) - u8 iv[16]; - Bytes iv_bytes { iv, 16 }; - Bytes { m_context.crypto.local_aead_iv, 4 }.copy_to(iv_bytes); - fill_with_random(iv_bytes.slice(4, 8)); - memset(iv_bytes.offset(12), 0, 4); - - // write the random part of the iv out - iv_bytes.slice(4, 8).copy_to(ct.bytes().slice(header_size)); - - // Write the encrypted data and the tag - gcm.encrypt( - packet.bytes().slice(header_size, length), - ct.bytes().slice(header_size + 8, length), - iv_bytes, - aad_bytes, - ct.bytes().slice(header_size + 8 + length, 16)); - - VERIFY(header_size + 8 + length + 16 == ct.size()); - }, - [&](Crypto::Cipher::AESCipher::CBCMode& cbc) { - VERIFY(!is_aead()); - // We need enough space for a header, iv_length bytes of IV and whatever the packet contains - auto ct_buffer_result = ByteBuffer::create_uninitialized(length + header_size + iv_size); - if (ct_buffer_result.is_error()) { - dbgln("LibTLS: Failed to allocate enough memory for the ciphertext"); - VERIFY_NOT_REACHED(); - } - ct = ct_buffer_result.release_value(); - - // copy the header over - ct.overwrite(0, packet.data(), header_size - 2); - - // get the appropriate HMAC value for the entire packet - auto mac = hmac_message(packet, {}, mac_size, true); - - // write the MAC - buffer.overwrite(buffer_position, mac.data(), mac.size()); - buffer_position += mac.size(); - - // Apply the padding (a packet MUST always be padded) - memset(buffer.offset_pointer(buffer_position), padding - 1, padding); - buffer_position += padding; - - VERIFY(buffer_position == buffer.size()); - - auto iv_buffer_result = ByteBuffer::create_uninitialized(iv_size); - if (iv_buffer_result.is_error()) { - dbgln("LibTLS: Failed to allocate memory for IV"); - VERIFY_NOT_REACHED(); - } - auto iv = iv_buffer_result.release_value(); - fill_with_random(iv); - - // write it into the ciphertext portion of the message - ct.overwrite(header_size, iv.data(), iv.size()); - - VERIFY(header_size + iv_size + length == ct.size()); - VERIFY(length % block_size == 0); - - // get a block to encrypt into - auto view = ct.bytes().slice(header_size + iv_size, length); - cbc.encrypt(buffer, view, iv); - }); - - // store the correct ciphertext length into the packet - u16 ct_length = (u16)ct.size() - header_size; - - ByteReader::store(ct.offset_pointer(header_size - 2), AK::convert_between_host_and_network_endian(ct_length)); - - // replace the packet with the ciphertext - packet = ct; - } - } - } - ++m_context.local_sequence_number; -} - -void TLSv12::update_hash(ReadonlyBytes message, size_t header_size) -{ - dbgln_if(TLS_DEBUG, "Update hash with message of size {}", message.size()); - m_context.handshake_hash.update(message.slice(header_size)); -} - -void TLSv12::ensure_hmac(size_t digest_size, bool local) -{ - if (local && m_hmac_local) - return; - - if (!local && m_hmac_remote) - return; - - auto hash_kind = Crypto::Hash::HashKind::None; - - switch (digest_size) { - case Crypto::Hash::SHA1::DigestSize: - hash_kind = Crypto::Hash::HashKind::SHA1; - break; - case Crypto::Hash::SHA256::DigestSize: - hash_kind = Crypto::Hash::HashKind::SHA256; - break; - case Crypto::Hash::SHA384::DigestSize: - hash_kind = Crypto::Hash::HashKind::SHA384; - break; - case Crypto::Hash::SHA512::DigestSize: - hash_kind = Crypto::Hash::HashKind::SHA512; - break; - default: - dbgln("Failed to find a suitable hash for size {}", digest_size); - break; - } - - auto hmac = make>(ReadonlyBytes { local ? m_context.crypto.local_mac : m_context.crypto.remote_mac, digest_size }, hash_kind); - if (local) - m_hmac_local = move(hmac); - else - m_hmac_remote = move(hmac); -} - -ByteBuffer TLSv12::hmac_message(ReadonlyBytes buf, Optional const buf2, size_t mac_length, bool local) -{ - u64 sequence_number = AK::convert_between_host_and_network_endian(local ? m_context.local_sequence_number : m_context.remote_sequence_number); - ensure_hmac(mac_length, local); - auto& hmac = local ? *m_hmac_local : *m_hmac_remote; - if constexpr (TLS_DEBUG) { - dbgln("========================= PACKET DATA =========================="); - print_buffer((u8 const*)&sequence_number, sizeof(u64)); - print_buffer(buf.data(), buf.size()); - if (buf2.has_value()) - print_buffer(buf2.value().data(), buf2.value().size()); - dbgln("========================= PACKET DATA =========================="); - } - hmac.update((u8 const*)&sequence_number, sizeof(u64)); - hmac.update(buf); - if (buf2.has_value() && buf2.value().size()) { - hmac.update(buf2.value()); - } - auto digest = hmac.digest(); - auto mac_result = ByteBuffer::copy(digest.immutable_data(), digest.data_length()); - if (mac_result.is_error()) { - dbgln("Failed to calculate message HMAC: Not enough memory"); - return {}; - } - - if constexpr (TLS_DEBUG) { - dbgln("HMAC of the block for sequence number {}", sequence_number); - print_buffer(mac_result.value()); - } - - return mac_result.release_value(); -} - -ssize_t TLSv12::handle_message(ReadonlyBytes buffer) -{ - auto res { 5ll }; - size_t header_size = res; - ssize_t payload_res = 0; - - dbgln_if(TLS_DEBUG, "buffer size: {}", buffer.size()); - - if (buffer.size() < 5) { - return (i8)Error::NeedMoreData; - } - - auto type = (ContentType)buffer[0]; - size_t buffer_position { 1 }; - - // FIXME: Read the version and verify it - - if constexpr (TLS_DEBUG) { - auto version = static_cast(ByteReader::load16(buffer.offset_pointer(buffer_position))); - dbgln("type={}, version={}", enum_to_string(type), enum_to_string(version)); - } - - buffer_position += 2; - - auto length = AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(buffer_position))); - - dbgln_if(TLS_DEBUG, "record length: {} at offset: {}", length, buffer_position); - buffer_position += 2; - - if (buffer_position + length > buffer.size()) { - dbgln_if(TLS_DEBUG, "record length more than what we have: {}", buffer.size()); - return (i8)Error::NeedMoreData; - } - - dbgln_if(TLS_DEBUG, "message type: {}, length: {}", enum_to_string(type), length); - auto plain = buffer.slice(buffer_position, buffer.size() - buffer_position); - - ByteBuffer decrypted; - - if (m_context.cipher_spec_set && type != ContentType::CHANGE_CIPHER_SPEC) { - if constexpr (TLS_DEBUG) { - dbgln("Encrypted: "); - print_buffer(buffer.slice(header_size, length)); - } - - Error return_value = Error::NoError; - m_cipher_remote.visit( - [&](Empty&) { VERIFY_NOT_REACHED(); }, - [&](Crypto::Cipher::AESCipher::GCMMode& gcm) { - VERIFY(is_aead()); - if (length < 24) { - dbgln("Invalid packet length"); - auto packet = build_alert(true, (u8)AlertDescription::DECRYPT_ERROR); - write_packet(packet); - return_value = Error::BrokenPacket; - return; - } - - auto packet_length = length - iv_length() - 16; - auto payload = plain; - auto decrypted_result = ByteBuffer::create_uninitialized(packet_length); - if (decrypted_result.is_error()) { - dbgln("Failed to allocate memory for the packet"); - return_value = Error::DecryptionFailed; - return; - } - decrypted = decrypted_result.release_value(); - - // AEAD AAD (13) - // Seq. no (8) - // content type (1) - // version (2) - // length (2) - u8 aad[13]; - Bytes aad_bytes { aad, 13 }; - FixedMemoryStream aad_stream { aad_bytes }; - - u64 seq_no = AK::convert_between_host_and_network_endian(m_context.remote_sequence_number); - u16 len = AK::convert_between_host_and_network_endian((u16)packet_length); - - MUST(aad_stream.write_value(seq_no)); // sequence number - MUST(aad_stream.write_until_depleted(buffer.slice(0, header_size - 2))); // content-type + version - MUST(aad_stream.write_value(len)); // length - VERIFY(MUST(aad_stream.tell()) == MUST(aad_stream.size())); - - auto nonce = payload.slice(0, iv_length()); - payload = payload.slice(iv_length()); - - // AEAD IV (12) - // IV (4) - // (Nonce) (8) - // -- Our GCM impl takes 16 bytes - // zero (4) - u8 iv[16]; - Bytes iv_bytes { iv, 16 }; - Bytes { m_context.crypto.remote_aead_iv, 4 }.copy_to(iv_bytes); - nonce.copy_to(iv_bytes.slice(4)); - memset(iv_bytes.offset(12), 0, 4); - - auto ciphertext = payload.slice(0, payload.size() - 16); - auto tag = payload.slice(ciphertext.size()); - - auto consistency = gcm.decrypt( - ciphertext, - decrypted, - iv_bytes, - aad_bytes, - tag); - - if (consistency != Crypto::VerificationConsistency::Consistent) { - dbgln("integrity check failed (tag length {})", tag.size()); - auto packet = build_alert(true, (u8)AlertDescription::BAD_RECORD_MAC); - write_packet(packet); - - return_value = Error::IntegrityCheckFailed; - return; - } - - plain = decrypted; - }, - [&](Crypto::Cipher::AESCipher::CBCMode& cbc) { - VERIFY(!is_aead()); - auto iv_size = iv_length(); - - auto decrypted_result = cbc.create_aligned_buffer(length - iv_size); - if (decrypted_result.is_error()) { - dbgln("Failed to allocate memory for the packet"); - return_value = Error::DecryptionFailed; - return; - } - decrypted = decrypted_result.release_value(); - auto iv = buffer.slice(header_size, iv_size); - - Bytes decrypted_span = decrypted; - cbc.decrypt(buffer.slice(header_size + iv_size, length - iv_size), decrypted_span, iv); - - length = decrypted_span.size(); - - if constexpr (TLS_DEBUG) { - dbgln("Decrypted: "); - print_buffer(decrypted); - } - - auto mac_size = mac_length(); - if (length < mac_size) { - dbgln("broken packet"); - auto packet = build_alert(true, (u8)AlertDescription::DECRYPT_ERROR); - write_packet(packet); - return_value = Error::BrokenPacket; - return; - } - - length -= mac_size; - - u8 const* message_hmac = decrypted_span.offset(length); - u8 temp_buf[5]; - memcpy(temp_buf, buffer.offset_pointer(0), 3); - *(u16*)(temp_buf + 3) = AK::convert_between_host_and_network_endian(length); - auto hmac = hmac_message({ temp_buf, 5 }, decrypted_span.slice(0, length), mac_size); - auto message_mac = ReadonlyBytes { message_hmac, mac_size }; - if (hmac != message_mac) { - dbgln("integrity check failed (mac length {})", mac_size); - dbgln("mac received:"); - print_buffer(message_mac); - dbgln("mac computed:"); - print_buffer(hmac); - auto packet = build_alert(true, (u8)AlertDescription::BAD_RECORD_MAC); - write_packet(packet); - - return_value = Error::IntegrityCheckFailed; - return; - } - plain = decrypted.bytes().slice(0, length); - }); - - if (return_value != Error::NoError) { - return (i8)return_value; - } - } - m_context.remote_sequence_number++; - - switch (type) { - case ContentType::APPLICATION_DATA: - if (m_context.connection_status != ConnectionStatus::Established) { - dbgln("unexpected application data"); - payload_res = (i8)Error::UnexpectedMessage; - auto packet = build_alert(true, (u8)AlertDescription::UNEXPECTED_MESSAGE); - write_packet(packet); - } else { - dbgln_if(TLS_DEBUG, "application data message of size {}", plain.size()); - - if (m_context.application_buffer.try_append(plain).is_error()) { - payload_res = (i8)Error::DecryptionFailed; - auto packet = build_alert(true, (u8)AlertDescription::DECRYPTION_FAILED_RESERVED); - write_packet(packet); - } else { - notify_client_for_app_data(); - } - } - break; - case ContentType::HANDSHAKE: - dbgln_if(TLS_DEBUG, "tls handshake message"); - payload_res = handle_handshake_payload(plain); - break; - case ContentType::CHANGE_CIPHER_SPEC: - if (m_context.connection_status != ConnectionStatus::KeyExchange) { - dbgln("unexpected change cipher message"); - auto packet = build_alert(true, (u8)AlertDescription::UNEXPECTED_MESSAGE); - write_packet(packet); - payload_res = (i8)Error::UnexpectedMessage; - } else { - dbgln_if(TLS_DEBUG, "change cipher spec message"); - m_context.cipher_spec_set = true; - m_context.remote_sequence_number = 0; - } - break; - case ContentType::ALERT: - dbgln_if(TLS_DEBUG, "alert message of length {}", length); - if (length >= 2) { - if constexpr (TLS_DEBUG) - print_buffer(plain); - - auto level = plain[0]; - auto code = plain[1]; - dbgln_if(TLS_DEBUG, "Alert received with level {}, code {}", level, code); - - if (level == (u8)AlertLevel::FATAL) { - dbgln("We were alerted of a critical error: {} ({})", code, enum_to_string((AlertDescription)code)); - m_context.critical_error = code; - try_disambiguate_error(); - res = (i8)Error::UnknownError; - } - - if (code == (u8)AlertDescription::CLOSE_NOTIFY) { - res += 2; - alert(AlertLevel::FATAL, AlertDescription::CLOSE_NOTIFY); - if (!m_context.cipher_spec_set) { - // AWS CloudFront hits this. - dbgln("Server sent a close notify and we haven't agreed on a cipher suite. Treating it as a handshake failure."); - m_context.critical_error = (u8)AlertDescription::HANDSHAKE_FAILURE; - try_disambiguate_error(); - } - m_context.close_notify = true; - } - m_context.error_code = (Error)code; - check_connection_state(false); - notify_client_for_app_data(); // Give the user one more chance to observe the EOF - } - break; - default: - dbgln("message not understood"); - return (i8)Error::NotUnderstood; - } - - if (payload_res < 0) - return payload_res; - - if (res > 0) - return header_size + length; - - return res; -} - -} diff --git a/Userland/Libraries/LibTLS/Socket.cpp b/Userland/Libraries/LibTLS/Socket.cpp deleted file mode 100644 index 495d0e0d553..00000000000 --- a/Userland/Libraries/LibTLS/Socket.cpp +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Copyright (c) 2020, Ali Mohammad Pur - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -// Each record can hold at most 18432 bytes, leaving some headroom and rounding down to -// a nice number gives us a maximum of 16 KiB for user-supplied application data, -// which will be sent as a single record containing a single ApplicationData message. -constexpr static size_t MaximumApplicationDataChunkSize = 16 * KiB; - -namespace TLS { - -ErrorOr TLSv12::read_some(Bytes bytes) -{ - m_eof = false; - auto size_to_read = min(bytes.size(), m_context.application_buffer.size()); - if (size_to_read == 0) { - m_eof = true; - return Bytes {}; - } - - m_context.application_buffer.transfer(bytes, size_to_read); - return Bytes { bytes.data(), size_to_read }; -} - -ErrorOr TLSv12::write_some(ReadonlyBytes bytes) -{ - if (m_context.connection_status != ConnectionStatus::Established) { - dbgln_if(TLS_DEBUG, "write request while not connected"); - return AK::Error::from_string_literal("TLS write request while not connected"); - } - - for (size_t offset = 0; offset < bytes.size(); offset += MaximumApplicationDataChunkSize) { - PacketBuilder builder { ContentType::APPLICATION_DATA, m_context.options.version, bytes.size() - offset }; - builder.append(bytes.slice(offset, min(bytes.size() - offset, MaximumApplicationDataChunkSize))); - auto packet = builder.build(); - - update_packet(packet); - write_packet(packet); - } - - return bytes.size(); -} - -ErrorOr> TLSv12::connect(ByteString const& host, u16 port, Options options) -{ - auto promise = Core::Promise::construct(); - OwnPtr tcp_socket = TRY(Core::TCPSocket::connect(host, port)); - TRY(tcp_socket->set_blocking(false)); - auto tls_socket = make(move(tcp_socket), move(options)); - tls_socket->set_sni(host); - tls_socket->on_connected = [&] { - promise->resolve({}); - }; - tls_socket->on_tls_error = [&](auto alert) { - tls_socket->try_disambiguate_error(); - promise->reject(AK::Error::from_string_view(enum_to_string(alert))); - }; - - TRY(promise->await()); - - tls_socket->on_tls_error = nullptr; - tls_socket->on_connected = nullptr; - tls_socket->m_context.should_expect_successful_read = true; - return tls_socket; -} - -ErrorOr> TLSv12::connect(ByteString const& host, Core::Socket& underlying_stream, Options options) -{ - auto promise = Core::Promise::construct(); - TRY(underlying_stream.set_blocking(false)); - auto tls_socket = make(&underlying_stream, move(options)); - tls_socket->set_sni(host); - tls_socket->on_connected = [&] { - promise->resolve({}); - }; - tls_socket->on_tls_error = [&](auto alert) { - tls_socket->try_disambiguate_error(); - promise->reject(AK::Error::from_string_view(enum_to_string(alert))); - }; - TRY(promise->await()); - - tls_socket->on_tls_error = nullptr; - tls_socket->on_connected = nullptr; - tls_socket->m_context.should_expect_successful_read = true; - return tls_socket; -} - -void TLSv12::setup_connection() -{ - Core::deferred_invoke([this] { - auto& stream = underlying_stream(); - stream.on_ready_to_read = [this] { - auto result = read_from_socket(); - if (result.is_error()) - dbgln("Read error: {}", result.error()); - }; - - m_handshake_timeout_timer = Core::Timer::create_single_shot( - m_max_wait_time_for_handshake_in_seconds * 1000, [&] { - dbgln("Handshake timeout :("); - auto timeout_diff = Core::DateTime::now().timestamp() - m_context.handshake_initiation_timestamp; - // If the timeout duration was actually within the max wait time (with a margin of error), - // we're not operating slow, so the server timed out. - // otherwise, it's our fault that the negotiation is taking too long, so extend the timer :P - if (timeout_diff < m_max_wait_time_for_handshake_in_seconds + 1) { - // The server did not respond fast enough, - // time the connection out. - alert(AlertLevel::FATAL, AlertDescription::USER_CANCELED); - m_context.tls_buffer.clear(); - m_context.error_code = Error::TimedOut; - m_context.critical_error = (u8)Error::TimedOut; - check_connection_state(false); // Notify the client. - } else { - // Extend the timer, we are too slow. - m_handshake_timeout_timer->restart(m_max_wait_time_for_handshake_in_seconds * 1000); - } - }); - auto packet = build_hello(); - write_packet(packet); - write_into_socket(); - m_handshake_timeout_timer->start(); - m_context.handshake_initiation_timestamp = Core::DateTime::now().timestamp(); - }); - m_has_scheduled_write_flush = true; -} - -void TLSv12::notify_client_for_app_data() -{ - if (m_context.application_buffer.size() > 0) { - if (on_ready_to_read) - on_ready_to_read(); - } else { - if (m_context.connection_finished && !m_context.has_invoked_finish_or_error_callback) { - m_context.has_invoked_finish_or_error_callback = true; - if (on_tls_finished) - on_tls_finished(); - } - } - m_has_scheduled_app_data_flush = false; -} - -ErrorOr TLSv12::read_from_socket() -{ - // If there's anything before we consume stuff, let the client know - // since we won't be consuming things if the connection is terminated. - notify_client_for_app_data(); - - ScopeGuard notify_guard { - [this] { - // If anything new shows up, tell the client about the event. - notify_client_for_app_data(); - } - }; - - if (!check_connection_state(true)) - return {}; - - u8 buffer[16 * KiB]; - Bytes bytes { buffer, array_size(buffer) }; - Bytes read_bytes {}; - auto& stream = underlying_stream(); - do { - auto result = stream.read_some(bytes); - if (result.is_error()) { - if (result.error().is_errno() && result.error().code() != EINTR) { - if (result.error().code() != EAGAIN) - dbgln("TLS Socket read failed, error: {}", result.error()); - break; - } - continue; - } - read_bytes = result.release_value(); - consume(read_bytes); - } while (!read_bytes.is_empty() && !m_context.critical_error); - - if (m_context.should_expect_successful_read && read_bytes.is_empty()) { - // read_some() returned an empty span, this is either an EOF (from improper closure) - // or some sort of weird even that is showing itself as an EOF. - // To guard against servers closing the connection weirdly or just improperly, make sure - // to check the connection state here and send the appropriate notifications. - stream.close(); - - check_connection_state(true); - } - - return {}; -} - -void TLSv12::write_into_socket() -{ - dbgln_if(TLS_DEBUG, "Flushing cached records: {} established? {}", m_context.tls_buffer.size(), is_established()); - - m_has_scheduled_write_flush = false; - if (!check_connection_state(false)) - return; - - MUST(flush()); -} - -bool TLSv12::check_connection_state(bool read) -{ - if (m_context.connection_finished) - return false; - - if (m_context.close_notify) - m_context.connection_finished = true; - - auto& stream = underlying_stream(); - - if (!stream.is_open()) { - // an abrupt closure (the server is a jerk) - dbgln_if(TLS_DEBUG, "Socket not open, assuming abrupt closure"); - m_context.connection_finished = true; - m_context.connection_status = ConnectionStatus::Disconnected; - close(); - m_context.has_invoked_finish_or_error_callback = true; - if (on_ready_to_read) - on_ready_to_read(); // Notify the client about the weird event. - if (on_tls_finished) - on_tls_finished(); - return false; - } - - if (read && stream.is_eof()) { - if (m_context.application_buffer.size() == 0 && m_context.connection_status != ConnectionStatus::Disconnected) { - m_context.has_invoked_finish_or_error_callback = true; - if (on_tls_finished) - on_tls_finished(); - } - return false; - } - - if (m_context.critical_error) { - dbgln_if(TLS_DEBUG, "CRITICAL ERROR {} :(", m_context.critical_error); - - m_context.has_invoked_finish_or_error_callback = true; - if (on_tls_error) - on_tls_error((AlertDescription)m_context.critical_error); - m_context.connection_finished = true; - m_context.connection_status = ConnectionStatus::Disconnected; - close(); - return false; - } - - if (((read && m_context.application_buffer.size() == 0) || !read) && m_context.connection_finished) { - if (m_context.application_buffer.size() == 0 && m_context.connection_status != ConnectionStatus::Disconnected) { - m_context.has_invoked_finish_or_error_callback = true; - if (on_tls_finished) - on_tls_finished(); - } - if (m_context.tls_buffer.size()) { - dbgln_if(TLS_DEBUG, "connection closed without finishing data transfer, {} bytes still in buffer and {} bytes in application buffer", - m_context.tls_buffer.size(), - m_context.application_buffer.size()); - } - if (!m_context.application_buffer.size()) { - return false; - } - } - return true; -} - -ErrorOr TLSv12::flush() -{ - auto out_bytes = m_context.tls_buffer.bytes(); - - if (out_bytes.is_empty()) - return true; - - if constexpr (TLS_DEBUG) { - dbgln("SENDING..."); - print_buffer(out_bytes); - } - - auto& stream = underlying_stream(); - Optional error; - size_t written; - do { - auto result = stream.write_some(out_bytes); - if (result.is_error()) { - if (result.error().code() != EINTR && result.error().code() != EAGAIN) { - error = result.release_error(); - dbgln("TLS Socket write error: {}", *error); - break; - } - continue; - } - written = result.value(); - out_bytes = out_bytes.slice(written); - } while (!out_bytes.is_empty()); - - if (out_bytes.is_empty() && !error.has_value()) { - m_context.tls_buffer.clear(); - return true; - } - - if (m_context.send_retries++ == 10) { - // drop the records, we can't send - dbgln_if(TLS_DEBUG, "Dropping {} bytes worth of TLS records as max retries has been reached", m_context.tls_buffer.size()); - m_context.tls_buffer.clear(); - m_context.send_retries = 0; - } - return false; -} - -void TLSv12::close() -{ - if (underlying_stream().is_open()) - alert(AlertLevel::FATAL, AlertDescription::CLOSE_NOTIFY); - // bye bye. - m_context.connection_status = ConnectionStatus::Disconnected; -} - -} diff --git a/Userland/Libraries/LibTLS/TLSPacketBuilder.h b/Userland/Libraries/LibTLS/TLSPacketBuilder.h deleted file mode 100644 index 07076638024..00000000000 --- a/Userland/Libraries/LibTLS/TLSPacketBuilder.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2020, Ali Mohammad Pur - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace TLS { - -class PacketBuilder { -public: - PacketBuilder(ContentType type, u16 version, size_t size_hint = 0xfdf) - : PacketBuilder(type, (ProtocolVersion)version, size_hint) - { - } - - PacketBuilder(ContentType type, ProtocolVersion version, size_t size_hint = 0xfdf) - { - // FIXME: Handle possible OOM situation. - m_packet_data = ByteBuffer::create_uninitialized(size_hint + 16).release_value_but_fixme_should_propagate_errors(); - m_current_length = 5; - m_packet_data[0] = (u8)type; - ByteReader::store(m_packet_data.offset_pointer(1), AK::convert_between_host_and_network_endian((u16)version)); - } - - inline void append(u16 value) - { - value = AK::convert_between_host_and_network_endian(value); - append((u8 const*)&value, sizeof(value)); - } - inline void append(u8 value) - { - append((u8 const*)&value, sizeof(value)); - } - inline void append(ReadonlyBytes data) - { - append(data.data(), data.size()); - } - inline void append_u24(u32 value) - { - u8 buf[3]; - buf[0] = value / 0x10000; - value %= 0x10000; - buf[1] = value / 0x100; - value %= 0x100; - buf[2] = value; - - append(buf, 3); - } - inline void append(u8 const* data, size_t bytes) - { - if (bytes == 0) - return; - - auto old_length = m_current_length; - m_current_length += bytes; - - if (m_packet_data.size() < m_current_length) { - m_packet_data.resize(m_current_length); - } - - m_packet_data.overwrite(old_length, data, bytes); - } - inline ByteBuffer build() - { - auto length = m_current_length; - m_current_length = 0; - // FIXME: Propagate errors. - return MUST(m_packet_data.slice(0, length)); - } - inline void set(size_t offset, u8 value) - { - VERIFY(offset < m_current_length); - m_packet_data[offset] = value; - } - size_t length() const { return m_current_length; } - -private: - ByteBuffer m_packet_data; - size_t m_current_length; -}; - -} diff --git a/Userland/Libraries/LibTLS/TLSv12.cpp b/Userland/Libraries/LibTLS/TLSv12.cpp index c49a3056ddb..b1b5328b82c 100644 --- a/Userland/Libraries/LibTLS/TLSv12.cpp +++ b/Userland/Libraries/LibTLS/TLSv12.cpp @@ -4,622 +4,162 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include +#include +#include #ifndef SOCK_NONBLOCK # include #endif +static Vector s_certificate_store_paths; + namespace TLS { -void TLSv12::consume(ReadonlyBytes record) +static thread_local bool g_initialized_wolfssl = false; + +static int errno_to_wolfssl_error(int error) { - if (m_context.critical_error) { - dbgln("There has been a critical error ({}), refusing to continue", (i8)m_context.critical_error); - return; - } - - if (record.size() == 0) { - return; - } - - dbgln_if(TLS_DEBUG, "Consuming {} bytes", record.size()); - - if (m_context.message_buffer.try_append(record).is_error()) { - dbgln("Not enough space in message buffer, dropping the record"); - return; - } - - size_t index { 0 }; - size_t buffer_length = m_context.message_buffer.size(); - - size_t size_offset { 3 }; // read the common record header - size_t header_size { 5 }; - - dbgln_if(TLS_DEBUG, "message buffer length {}", buffer_length); - - while (buffer_length >= 5) { - auto length = AK::convert_between_host_and_network_endian(ByteReader::load16(m_context.message_buffer.offset_pointer(index + size_offset))) + header_size; - if (length > buffer_length) { - dbgln_if(TLS_DEBUG, "Need more data: {} > {}", length, buffer_length); - break; - } - auto consumed = handle_message(m_context.message_buffer.bytes().slice(index, length)); - - if constexpr (TLS_DEBUG) { - if (consumed > 0) - dbgln("consumed {} bytes", consumed); - else - dbgln("error: {}", consumed); - } - - if (consumed != (i8)Error::NeedMoreData) { - if (consumed < 0) { - dbgln("Consumed an error: {}", consumed); - if (!m_context.critical_error) - m_context.critical_error = (i8)consumed; - m_context.error_code = (Error)consumed; - break; - } - } else { - continue; - } - - index += length; - buffer_length -= length; - if (m_context.critical_error) { - dbgln("Broken connection"); - m_context.error_code = Error::BrokenConnection; - break; - } - } - if (m_context.error_code != Error::NoError && m_context.error_code != Error::NeedMoreData) { - dbgln("consume error: {}", (i8)m_context.error_code); - m_context.message_buffer.clear(); - return; - } - - if (index) { - // FIXME: Propagate errors. - m_context.message_buffer = MUST(m_context.message_buffer.slice(index, m_context.message_buffer.size() - index)); + switch (error) { + case EAGAIN: + return WOLFSSL_CBIO_ERR_WANT_READ; + case ETIMEDOUT: + return WOLFSSL_CBIO_ERR_TIMEOUT; + case ECONNRESET: + return WOLFSSL_CBIO_ERR_CONN_RST; + case EINTR: + return WOLFSSL_CBIO_ERR_ISR; + case ECONNREFUSED: + return WOLFSSL_CBIO_ERR_WANT_READ; + case ECONNABORTED: + return WOLFSSL_CBIO_ERR_CONN_CLOSE; + default: + return WOLFSSL_CBIO_ERR_GENERAL; } } -bool Certificate::is_valid() const +StringView WolfTLS::error_text(WOLFSSL* ssl, int error_code) { - auto now = UnixDateTime::now(); + auto error = wolfSSL_get_error(ssl, error_code); + static char buffer[WOLFSSL_MAX_ERROR_SZ]; + auto* ptr = wolfSSL_ERR_error_string(error, buffer); + return StringView { ptr, strlen(ptr) }; +} - if (now < validity.not_before) { - dbgln("certificate expired (not yet valid, signed for {})", Core::DateTime::from_timestamp(validity.not_before.seconds_since_epoch())); - return false; +ErrorOr> WolfTLS::connect(const AK::ByteString& host, u16 port) +{ + if (!g_initialized_wolfssl) { + wolfSSL_Init(); + g_initialized_wolfssl = true; } - if (validity.not_after < now) { - dbgln("certificate expired (expiry date {})", Core::DateTime::from_timestamp(validity.not_after.seconds_since_epoch())); - return false; + auto* context = wolfSSL_CTX_new(wolfTLSv1_2_client_method()); + if (!context) + return Error::from_string_literal("Failed to create a new TLS context"); + + if (s_certificate_store_paths.is_empty()) + s_certificate_store_paths.append("/etc/ssl/cert.pem"); // We're just guessing this, the embedder should provide this. + + for (auto& path : s_certificate_store_paths) { + if (wolfSSL_CTX_load_verify_locations(context, path.characters(), nullptr) != SSL_SUCCESS) + return Error::from_string_literal("Failed to load CA certificates"); } + auto ssl = wolfSSL_new(context); + if (!ssl) + return Error::from_string_literal("Failed to create a new SSL object"); + + wolfSSL_SSLSetIOSend(ssl, [](WOLFSSL*, char* buf, int sz, void* ctx) -> int { + auto& self = *static_cast(ctx); + auto result = self.m_underlying->write_some({ buf, static_cast(sz) }); + if (result.is_error() && result.error().is_errno()) + return errno_to_wolfssl_error(result.error().code()); + if (result.is_error()) + return -1; + return static_cast(result.value()); + }); + wolfSSL_SSLSetIORecv(ssl, [](WOLFSSL*, char* buf, int sz, void* ctx) -> int { + auto& self = *static_cast(ctx); + auto result = self.m_underlying->read_some({ buf, static_cast(sz) }); + if (result.is_error() && result.error().is_errno()) + return errno_to_wolfssl_error(result.error().code()); + if (result.is_error()) + return -1; + return static_cast(result.value().size()); + }); + + auto tcp_socket = TRY(Core::TCPSocket::connect(host, port)); + + auto object = make(context, ssl, move(tcp_socket)); + wolfSSL_SetIOReadCtx(ssl, static_cast(object.ptr())); + wolfSSL_SetIOWriteCtx(ssl, static_cast(object.ptr())); + + if (wolfSSL_CTX_UseSNI(context, WOLFSSL_SNI_HOST_NAME, host.bytes().data(), host.bytes().size()) != WOLFSSL_SUCCESS) + return Error::from_string_literal("Failed to set SNI hostname"); + + if (auto rc = wolfSSL_connect(ssl); rc != SSL_SUCCESS) + return Error::from_string_view(error_text(ssl, rc)); + + return object; +} + +WolfTLS::~WolfTLS() +{ + close(); + wolfSSL_free(m_ssl); + wolfSSL_CTX_free(m_context); +} + +ErrorOr WolfTLS::read_some(Bytes bytes) +{ + auto result = wolfSSL_read(m_ssl, bytes.data(), bytes.size()); + if (result < 0) + return Error::from_string_view(error_text(m_ssl, result)); + return Bytes { bytes.data(), static_cast(result) }; +} + +ErrorOr WolfTLS::write_some(ReadonlyBytes bytes) +{ + auto result = wolfSSL_write(m_ssl, bytes.data(), bytes.size()); + if (result < 0) + return Error::from_string_view(error_text(m_ssl, result)); + return static_cast(result); +} + +bool WolfTLS::is_eof() const +{ + return m_underlying->is_eof() && wolfSSL_pending(m_ssl) == 0; +} + +bool WolfTLS::is_open() const +{ + return m_underlying->is_open(); +} + +void WolfTLS::close() +{ + wolfSSL_shutdown(m_ssl); +} + +ErrorOr WolfTLS::pending_bytes() const +{ + return wolfSSL_pending(m_ssl); +} + +ErrorOr WolfTLS::can_read_without_blocking(int) const +{ return true; } -// https://www.ietf.org/rfc/rfc5280.html#page-12 -bool Certificate::is_self_signed() +ErrorOr WolfTLS::set_blocking(bool) { - if (m_is_self_signed.has_value()) - return *m_is_self_signed; - - // Self-signed certificates are self-issued certificates where the digital - // signature may be verified by the public key bound into the certificate. - if (!this->is_self_issued) - m_is_self_signed.emplace(false); - - // FIXME: Actually check if we sign ourself - - m_is_self_signed.emplace(true); - return *m_is_self_signed; + return {}; } -void TLSv12::try_disambiguate_error() const +void WolfTLS::install_certificate_store_paths(Vector paths) { - dbgln("Possible failure cause(s): "); - switch ((AlertDescription)m_context.critical_error) { - case AlertDescription::HANDSHAKE_FAILURE: - if (!m_context.cipher_spec_set) { - dbgln("- No cipher suite in common with {}", m_context.extensions.SNI); - } else { - dbgln("- Unknown internal issue"); - } - break; - case AlertDescription::INSUFFICIENT_SECURITY: - dbgln("- No cipher suite in common with {} (the server is oh so secure)", m_context.extensions.SNI); - break; - case AlertDescription::PROTOCOL_VERSION: - dbgln("- The server refused to negotiate with TLS 1.2 :("); - break; - case AlertDescription::UNEXPECTED_MESSAGE: - dbgln("- We sent an invalid message for the state we're in."); - break; - case AlertDescription::BAD_RECORD_MAC: - dbgln("- Bad MAC record from our side."); - dbgln("- Ciphertext wasn't an even multiple of the block length."); - dbgln("- Bad block cipher padding."); - dbgln("- If both sides are compliant, the only cause is messages being corrupted in the network."); - break; - case AlertDescription::RECORD_OVERFLOW: - dbgln("- Sent a ciphertext record which has a length bigger than 18432 bytes."); - dbgln("- Sent record decrypted to a compressed record that has a length bigger than 18432 bytes."); - dbgln("- If both sides are compliant, the only cause is messages being corrupted in the network."); - break; - case AlertDescription::DECOMPRESSION_FAILURE_RESERVED: - dbgln("- We sent invalid input for decompression (e.g. data that would expand to excessive length)"); - break; - case AlertDescription::ILLEGAL_PARAMETER: - dbgln("- We sent a parameter in the handshake that is out of range or inconsistent with the other parameters."); - break; - case AlertDescription::DECODE_ERROR: - dbgln("- The message we sent cannot be decoded because a field was out of range or the length was incorrect."); - dbgln("- If both sides are compliant, the only cause is messages being corrupted in the network."); - break; - case AlertDescription::DECRYPT_ERROR: - dbgln("- A handshake crypto operation failed. This includes signature verification and validating Finished."); - break; - case AlertDescription::ACCESS_DENIED: - dbgln("- The certificate is valid, but once access control was applied, the sender decided to stop negotiation."); - break; - case AlertDescription::INTERNAL_ERROR: - dbgln("- No one knows, but it isn't a protocol failure."); - break; - case AlertDescription::DECRYPTION_FAILED_RESERVED: - case AlertDescription::NO_CERTIFICATE_RESERVED: - case AlertDescription::EXPORT_RESTRICTION_RESERVED: - dbgln("- No one knows, the server sent a non-compliant alert."); - break; - default: - dbgln("- No one knows."); - break; - } - - dbgln("- {}", enum_to_value((AlertDescription)m_context.critical_error)); + s_certificate_store_paths = move(paths); } -void TLSv12::set_root_certificates(Vector certificates) -{ - if (!m_context.root_certificates.is_empty()) { - dbgln("TLS warn: resetting root certificates!"); - m_context.root_certificates.clear(); - } - - for (auto& cert : certificates) { - if (!cert.is_valid()) { - dbgln("Certificate for {} is invalid, things may or may not work!", cert.subject.to_string()); - } - // FIXME: Figure out what we should do when our root certs are invalid. - - m_context.root_certificates.set(MUST(cert.subject.to_string()).to_byte_string(), cert); - } - dbgln_if(TLS_DEBUG, "{}: Set {} root certificates", this, m_context.root_certificates.size()); -} - -static bool wildcard_matches(StringView host, StringView subject) -{ - if (host == subject) - return true; - - if (subject.starts_with("*."sv)) { - auto maybe_first_dot_index = host.find('.'); - if (maybe_first_dot_index.has_value()) { - auto first_dot_index = maybe_first_dot_index.release_value(); - return wildcard_matches(host.substring_view(first_dot_index + 1), subject.substring_view(2)); - } - } - - return false; -} - -static bool certificate_subject_matches_host(Certificate const& cert, StringView host) -{ - if (wildcard_matches(host, cert.subject.common_name())) - return true; - - for (auto& san : cert.SAN) { - if (wildcard_matches(host, san)) - return true; - } - - return false; -} - -bool Context::verify_chain(StringView host) const -{ - if (!options.validate_certificates) - return true; - - Vector const* local_chain = nullptr; - if (is_server) { - dbgln("Unsupported: Server mode"); - TODO(); - } else { - local_chain = &certificates; - } - - if (local_chain->is_empty()) { - dbgln("verify_chain: Attempting to verify an empty chain"); - return false; - } - - // RFC5246 section 7.4.2: The sender's certificate MUST come first in the list. Each following certificate - // MUST directly certify the one preceding it. Because certificate validation requires that root keys be - // distributed independently, the self-signed certificate that specifies the root certificate authority MAY be - // omitted from the chain, under the assumption that the remote end must already possess it in order to validate - // it in any case. - - if (!host.is_empty()) { - auto const& first_certificate = local_chain->first(); - auto subject_matches = certificate_subject_matches_host(first_certificate, host); - if (!subject_matches) { - dbgln("verify_chain: First certificate does not match the hostname"); - return false; - } - } else { - // FIXME: The host is taken from m_context.extensions.SNI, when is this empty? - dbgln("FIXME: verify_chain called without host"); - return false; - } - - for (size_t cert_index = 0; cert_index < local_chain->size(); ++cert_index) { - auto const& cert = local_chain->at(cert_index); - - auto subject_string = MUST(cert.subject.to_string()); - auto issuer_string = MUST(cert.issuer.to_string()); - - if (!cert.is_valid()) { - dbgln("verify_chain: Certificate is not valid {}", subject_string); - return false; - } - - auto maybe_root_certificate = root_certificates.get(issuer_string.to_byte_string()); - if (maybe_root_certificate.has_value()) { - auto& root_certificate = *maybe_root_certificate; - auto verification_correct = verify_certificate_pair(cert, root_certificate); - - if (!verification_correct) { - dbgln("verify_chain: Signature inconsistent, {} was not signed by {} (root certificate)", subject_string, issuer_string); - return false; - } - - // Root certificate reached, and correctly verified, so we can stop now - return true; - } - - if (subject_string == issuer_string) { - dbgln("verify_chain: Non-root self-signed certificate"); - return options.allow_self_signed_certificates; - } - if ((cert_index + 1) >= local_chain->size()) { - dbgln("verify_chain: No trusted root certificate found before end of certificate chain"); - dbgln("verify_chain: Last certificate in chain was signed by {}", issuer_string); - return false; - } - - auto const& parent_certificate = local_chain->at(cert_index + 1); - if (issuer_string != MUST(parent_certificate.subject.to_string())) { - dbgln("verify_chain: Next certificate in the chain is not the issuer of this certificate"); - return false; - } - - if (!(parent_certificate.is_allowed_to_sign_certificate && parent_certificate.is_certificate_authority)) { - dbgln("verify_chain: {} is not marked as certificate authority", issuer_string); - return false; - } - if (parent_certificate.path_length_constraint.has_value() && cert_index > parent_certificate.path_length_constraint.value()) { - dbgln("verify_chain: Path length for certificate exceeded"); - return false; - } - - bool verification_correct = verify_certificate_pair(cert, parent_certificate); - if (!verification_correct) { - dbgln("verify_chain: Signature inconsistent, {} was not signed by {}", subject_string, issuer_string); - return false; - } - } - - // Either a root certificate is reached, or parent validation fails as the end of the local chain is reached - VERIFY_NOT_REACHED(); -} - -bool Context::verify_certificate_pair(Certificate const& subject, Certificate const& issuer) const -{ - Crypto::Hash::HashKind kind = Crypto::Hash::HashKind::Unknown; - auto identifier = subject.signature_algorithm.identifier; - - bool is_rsa = true; - - if (identifier == rsa_encryption_oid) { - kind = Crypto::Hash::HashKind::None; - } else if (identifier == rsa_md5_encryption_oid) { - kind = Crypto::Hash::HashKind::MD5; - } else if (identifier == rsa_sha1_encryption_oid) { - kind = Crypto::Hash::HashKind::SHA1; - } else if (identifier == rsa_sha256_encryption_oid) { - kind = Crypto::Hash::HashKind::SHA256; - } else if (identifier == rsa_sha384_encryption_oid) { - kind = Crypto::Hash::HashKind::SHA384; - } else if (identifier == rsa_sha512_encryption_oid) { - kind = Crypto::Hash::HashKind::SHA512; - } else if (identifier == ecdsa_with_sha256_encryption_oid) { - kind = Crypto::Hash::HashKind::SHA256; - is_rsa = false; - } else if (identifier == ecdsa_with_sha384_encryption_oid) { - kind = Crypto::Hash::HashKind::SHA384; - is_rsa = false; - } else if (identifier == ecdsa_with_sha512_encryption_oid) { - kind = Crypto::Hash::HashKind::SHA512; - is_rsa = false; - } - - if (kind == Crypto::Hash::HashKind::Unknown) { - dbgln("verify_certificate_pair: Unknown signature algorithm, expected RSA or ECDSA with SHA1/256/384/512, got OID {}", identifier); - return false; - } - - if (is_rsa) { - Crypto::PK::RSAPrivateKey dummy_private_key; - Crypto::PK::RSAPublicKey public_key_copy { issuer.public_key.rsa }; - auto rsa = Crypto::PK::RSA(public_key_copy, dummy_private_key); - auto verification_buffer_result = ByteBuffer::create_uninitialized(subject.signature_value.size()); - if (verification_buffer_result.is_error()) { - dbgln("verify_certificate_pair: Unable to allocate buffer for verification"); - return false; - } - auto verification_buffer = verification_buffer_result.release_value(); - auto verification_buffer_bytes = verification_buffer.bytes(); - rsa.verify(subject.signature_value, verification_buffer_bytes); - - ReadonlyBytes message = subject.tbs_asn1.bytes(); - auto pkcs1 = Crypto::PK::EMSA_PKCS1_V1_5(kind); - auto verification = pkcs1.verify(message, verification_buffer_bytes, subject.signature_value.size() * 8); - return verification == Crypto::VerificationConsistency::Consistent; - } - - // ECDSA hash verification: hash, then check signature against the specific curve - switch (issuer.public_key.algorithm.ec_parameters) { - case SupportedGroup::SECP256R1: { - Crypto::Hash::Manager hasher(kind); - hasher.update(subject.tbs_asn1.bytes()); - auto hash = hasher.digest(); - - Crypto::Curves::SECP256r1 curve; - auto result = curve.verify(hash.bytes(), issuer.public_key.raw_key, subject.signature_value); - if (result.is_error()) { - dbgln("verify_certificate_pair: Failed to check SECP256r1 signature {}", result.release_error()); - return false; - } - return result.value(); - } - case SupportedGroup::SECP384R1: { - Crypto::Hash::Manager hasher(kind); - hasher.update(subject.tbs_asn1.bytes()); - auto hash = hasher.digest(); - - Crypto::Curves::SECP384r1 curve; - auto result = curve.verify(hash.bytes(), issuer.public_key.raw_key, subject.signature_value); - if (result.is_error()) { - dbgln("verify_certificate_pair: Failed to check SECP384r1 signature {}", result.release_error()); - return false; - } - return result.value(); - } - case SupportedGroup::X25519: { - Crypto::Curves::Ed25519 curve; - auto result = curve.verify(issuer.public_key.raw_key, subject.signature_value, subject.tbs_asn1.bytes()); - if (!result) { - dbgln("verify_certificate_pair: Failed to check Ed25519 signature"); - return false; - } - return result; - } - default: - dbgln("verify_certificate_pair: Don't know how to verify signature for curve {}", to_underlying(issuer.public_key.algorithm.ec_parameters)); - return false; - } -} - -template -static void hmac_pseudorandom_function(Bytes output, ReadonlyBytes secret, u8 const* label, size_t label_length, ReadonlyBytes seed, ReadonlyBytes seed_b) -{ - if (!secret.size()) { - dbgln("null secret"); - return; - } - - auto append_label_seed = [&](auto& hmac) { - hmac.update(label, label_length); - hmac.update(seed); - if (seed_b.size() > 0) - hmac.update(seed_b); - }; - - HMACType hmac(secret); - append_label_seed(hmac); - - constexpr auto digest_size = hmac.digest_size(); - u8 digest[digest_size]; - auto digest_0 = Bytes { digest, digest_size }; - - digest_0.overwrite(0, hmac.digest().immutable_data(), digest_size); - - size_t index = 0; - while (index < output.size()) { - hmac.update(digest_0); - append_label_seed(hmac); - auto digest_1 = hmac.digest(); - - auto copy_size = min(digest_size, output.size() - index); - - output.overwrite(index, digest_1.immutable_data(), copy_size); - index += copy_size; - - digest_0.overwrite(0, hmac.process(digest_0).immutable_data(), digest_size); - } -} - -void TLSv12::pseudorandom_function(Bytes output, ReadonlyBytes secret, u8 const* label, size_t label_length, ReadonlyBytes seed, ReadonlyBytes seed_b) -{ - // Simplification: We only support the HMAC PRF with the hash function SHA-256 or stronger. - - // RFC 5246: "In this section, we define one PRF, based on HMAC. This PRF with the - // SHA-256 hash function is used for all cipher suites defined in this - // document and in TLS documents published prior to this document when - // TLS 1.2 is negotiated. New cipher suites MUST explicitly specify a - // PRF and, in general, SHOULD use the TLS PRF with SHA-256 or a - // stronger standard hash function." - - switch (hmac_hash()) { - case Crypto::Hash::HashKind::SHA512: - hmac_pseudorandom_function>(output, secret, label, label_length, seed, seed_b); - break; - case Crypto::Hash::HashKind::SHA384: - hmac_pseudorandom_function>(output, secret, label, label_length, seed, seed_b); - break; - case Crypto::Hash::HashKind::SHA256: - hmac_pseudorandom_function>(output, secret, label, label_length, seed, seed_b); - break; - default: - dbgln("Failed to find a suitable HMAC hash"); - VERIFY_NOT_REACHED(); - break; - } -} - -TLSv12::TLSv12(StreamVariantType stream, Options options) - : m_stream(move(stream)) -{ - m_context.options = move(options); - m_context.is_server = false; - m_context.tls_buffer = {}; - - set_root_certificates(m_context.options.root_certificates.has_value() - ? *m_context.options.root_certificates - : DefaultRootCACertificates::the().certificates()); - - setup_connection(); -} - -Vector TLSv12::parse_pem_certificate(ReadonlyBytes certificate_pem_buffer, ReadonlyBytes rsa_key) // FIXME: This should not be bound to RSA -{ - if (certificate_pem_buffer.is_empty() || rsa_key.is_empty()) { - return {}; - } - - auto decoded_certificate = Crypto::decode_pem(certificate_pem_buffer); - if (decoded_certificate.is_empty()) { - dbgln("Certificate not PEM"); - return {}; - } - - auto maybe_certificate = Certificate::parse_certificate(decoded_certificate); - if (!maybe_certificate.is_error()) { - dbgln("Invalid certificate"); - return {}; - } - - Crypto::PK::RSA rsa(rsa_key); - auto certificate = maybe_certificate.release_value(); - certificate.private_key = rsa.private_key(); - - return { move(certificate) }; -} - -static Vector s_default_ca_certificate_paths; - -void DefaultRootCACertificates::set_default_certificate_paths(Span paths) -{ - s_default_ca_certificate_paths.clear(); - s_default_ca_certificate_paths.ensure_capacity(paths.size()); - for (auto& path : paths) - s_default_ca_certificate_paths.unchecked_append(path); -} - -DefaultRootCACertificates::DefaultRootCACertificates() -{ - auto load_result = load_certificates(s_default_ca_certificate_paths); - if (load_result.is_error()) { - dbgln("Failed to load CA Certificates: {}", load_result.error()); - return; - } - - m_ca_certificates = load_result.release_value(); -} - -DefaultRootCACertificates& DefaultRootCACertificates::the() -{ - static thread_local DefaultRootCACertificates s_the; - return s_the; -} - -ErrorOr> DefaultRootCACertificates::load_certificates(Span custom_cert_paths) -{ - auto cacert_file_or_error = Core::File::open("/etc/cacert.pem"sv, Core::File::OpenMode::Read); - ByteBuffer data; - if (!cacert_file_or_error.is_error()) - data = TRY(cacert_file_or_error.value()->read_until_eof()); - - auto user_cert_path = TRY(String::formatted("{}/.config/certs.pem", Core::StandardPaths::home_directory())); - if (FileSystem::exists(user_cert_path)) { - auto user_cert_file = TRY(Core::File::open(user_cert_path, Core::File::OpenMode::Read)); - TRY(data.try_append(TRY(user_cert_file->read_until_eof()))); - } - - for (auto& custom_cert_path : custom_cert_paths) { - if (FileSystem::exists(custom_cert_path)) { - auto custom_cert_file = TRY(Core::File::open(custom_cert_path, Core::File::OpenMode::Read)); - TRY(data.try_append(TRY(custom_cert_file->read_until_eof()))); - } - } - - return TRY(parse_pem_root_certificate_authorities(data)); -} - -ErrorOr> DefaultRootCACertificates::parse_pem_root_certificate_authorities(ByteBuffer& data) -{ - Vector certificates; - - auto certs = TRY(Crypto::decode_pems(data)); - - for (auto& cert : certs) { - auto certificate_result = Certificate::parse_certificate(cert.bytes()); - if (certificate_result.is_error()) { - // FIXME: It would be nice to have more informations about the certificate we failed to parse. - // Like: Issuer, Algorithm, CN, etc - dbgln("Failed to load certificate: {}", certificate_result.error()); - continue; - } - auto certificate = certificate_result.release_value(); - if (certificate.is_certificate_authority && certificate.is_self_signed()) { - TRY(certificates.try_append(move(certificate))); - } else { - dbgln("Skipped '{}' because it is not a valid root CA", TRY(certificate.subject.to_string())); - } - } - - dbgln_if(TLS_DEBUG, "Loaded {} of {} ({:.2}%) provided CA Certificates", certificates.size(), certs.size(), (certificates.size() * 100.0) / certs.size()); - - return certificates; -} } diff --git a/Userland/Libraries/LibTLS/TLSv12.h b/Userland/Libraries/LibTLS/TLSv12.h index 806fd9ff9b0..7f57c7799ee 100644 --- a/Userland/Libraries/LibTLS/TLSv12.h +++ b/Userland/Libraries/LibTLS/TLSv12.h @@ -6,546 +6,48 @@ #pragma once -#include "Certificate.h" #include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +struct WOLFSSL_CTX; +struct WOLFSSL; namespace TLS { -inline void print_buffer(ReadonlyBytes buffer) -{ - dbgln("{:hex-dump}", buffer); -} - -inline void print_buffer(ByteBuffer const& buffer) -{ - print_buffer(buffer.bytes()); -} - -inline void print_buffer(u8 const* buffer, size_t size) -{ - print_buffer(ReadonlyBytes { buffer, size }); -} - -class Socket; - -enum class Error : i8 { - NoError = 0, - UnknownError = -1, - BrokenPacket = -2, - NotUnderstood = -3, - NoCommonCipher = -5, - UnexpectedMessage = -6, - CloseConnection = -7, - CompressionNotSupported = -8, - NotVerified = -9, - NotSafe = -10, - IntegrityCheckFailed = -11, - ErrorAlert = -12, - BrokenConnection = -13, - BadCertificate = -14, - UnsupportedCertificate = -15, - NoRenegotiation = -16, - FeatureNotSupported = -17, - DecryptionFailed = -20, - NeedMoreData = -21, - TimedOut = -22, - OutOfMemory = -23, -}; - -enum class WritePacketStage { - Initial = 0, - ClientHandshake = 1, - ServerHandshake = 2, - Finished = 3, -}; - -enum class ConnectionStatus { - Disconnected, - Negotiating, - KeyExchange, - Renegotiating, - Established, -}; - -enum ClientVerificationStaus { - Verified, - VerificationNeeded, -}; - -// 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. -// -// The cipher suite list below is ordered based on the recommendations from Mozilla. -// When changing the supported cipher suites, please consult the webpage below for -// the preferred order. -// -// https://wiki.mozilla.org/Security/Server_Side_TLS -#define ENUMERATE_CIPHERS(C) \ - C(true, CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, KeyExchangeAlgorithm::ECDHE_ECDSA, CipherAlgorithm::AES_128_GCM, Crypto::Hash::SHA256, 8, true) \ - C(true, CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, KeyExchangeAlgorithm::ECDHE_RSA, CipherAlgorithm::AES_128_GCM, Crypto::Hash::SHA256, 8, true) \ - C(true, CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, KeyExchangeAlgorithm::ECDHE_ECDSA, CipherAlgorithm::AES_256_GCM, Crypto::Hash::SHA384, 8, true) \ - C(true, CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, KeyExchangeAlgorithm::ECDHE_RSA, CipherAlgorithm::AES_256_GCM, Crypto::Hash::SHA384, 8, true) \ - C(true, CipherSuite::TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, KeyExchangeAlgorithm::DHE_RSA, CipherAlgorithm::AES_128_GCM, Crypto::Hash::SHA256, 8, true) \ - C(true, CipherSuite::TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, KeyExchangeAlgorithm::DHE_RSA, CipherAlgorithm::AES_256_GCM, Crypto::Hash::SHA384, 8, true) \ - C(true, CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, KeyExchangeAlgorithm::ECDHE_ECDSA, CipherAlgorithm::AES_128_CBC, Crypto::Hash::SHA1, 16, false) \ - C(true, CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, KeyExchangeAlgorithm::ECDHE_RSA, CipherAlgorithm::AES_128_CBC, Crypto::Hash::SHA1, 16, false) \ - C(true, CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, KeyExchangeAlgorithm::ECDHE_ECDSA, CipherAlgorithm::AES_256_CBC, Crypto::Hash::SHA1, 16, false) \ - C(true, CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, KeyExchangeAlgorithm::ECDHE_RSA, CipherAlgorithm::AES_256_CBC, Crypto::Hash::SHA1, 16, false) \ - C(true, CipherSuite::TLS_RSA_WITH_AES_128_GCM_SHA256, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_128_GCM, Crypto::Hash::SHA256, 8, true) \ - C(true, CipherSuite::TLS_RSA_WITH_AES_256_GCM_SHA384, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_256_GCM, Crypto::Hash::SHA384, 8, true) \ - C(true, CipherSuite::TLS_RSA_WITH_AES_128_CBC_SHA256, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_128_CBC, Crypto::Hash::SHA256, 16, false) \ - C(true, CipherSuite::TLS_RSA_WITH_AES_256_CBC_SHA256, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_256_CBC, Crypto::Hash::SHA256, 16, false) \ - C(true, CipherSuite::TLS_RSA_WITH_AES_128_CBC_SHA, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_128_CBC, Crypto::Hash::SHA1, 16, false) \ - C(true, CipherSuite::TLS_RSA_WITH_AES_256_CBC_SHA, KeyExchangeAlgorithm::RSA, CipherAlgorithm::AES_256_CBC, Crypto::Hash::SHA1, 16, false) - -constexpr KeyExchangeAlgorithm get_key_exchange_algorithm(CipherSuite suite) -{ - switch (suite) { -#define C(is_supported, suite, key_exchange, cipher, hash, iv_size, is_aead) \ - case suite: \ - return key_exchange; - ENUMERATE_CIPHERS(C) -#undef C - default: - return KeyExchangeAlgorithm::Invalid; - } -} - -constexpr CipherAlgorithm get_cipher_algorithm(CipherSuite suite) -{ - switch (suite) { -#define C(is_supported, suite, key_exchange, cipher, hash, iv_size, is_aead) \ - case suite: \ - return cipher; - ENUMERATE_CIPHERS(C) -#undef C - default: - return CipherAlgorithm::Invalid; - } -} - -struct Options { - static Vector default_usable_cipher_suites() - { - Vector cipher_suites; -#define C(is_supported, suite, key_exchange, cipher, hash, iv_size, is_aead) \ - if constexpr (is_supported) \ - cipher_suites.empend(suite); - ENUMERATE_CIPHERS(C) -#undef C - return cipher_suites; - } - Vector usable_cipher_suites = default_usable_cipher_suites(); - -#define OPTION_WITH_DEFAULTS(typ, name, ...) \ - static typ default_##name() \ - { \ - return typ { __VA_ARGS__ }; \ - } \ - typ name = default_##name(); \ - Options& set_##name(typ new_value)& \ - { \ - name = move(new_value); \ - return *this; \ - } \ - Options&& set_##name(typ new_value)&& \ - { \ - name = move(new_value); \ - return move(*this); \ - } - - OPTION_WITH_DEFAULTS(ProtocolVersion, version, ProtocolVersion::VERSION_1_2) - OPTION_WITH_DEFAULTS(Vector, supported_signature_algorithms, - { HashAlgorithm::SHA512, SignatureAlgorithm::RSA }, - { HashAlgorithm::SHA384, SignatureAlgorithm::RSA }, - { HashAlgorithm::SHA256, SignatureAlgorithm::RSA }, - { HashAlgorithm::SHA1, SignatureAlgorithm::RSA }, - { HashAlgorithm::SHA256, SignatureAlgorithm::ECDSA }, - { HashAlgorithm::SHA384, SignatureAlgorithm::ECDSA }, - { HashAlgorithm::INTRINSIC, SignatureAlgorithm::ED25519 }); - OPTION_WITH_DEFAULTS(Vector, elliptic_curves, - SupportedGroup::X25519, - SupportedGroup::SECP256R1, - SupportedGroup::SECP384R1, - SupportedGroup::X448) - OPTION_WITH_DEFAULTS(Vector, supported_ec_point_formats, ECPointFormat::UNCOMPRESSED) - - OPTION_WITH_DEFAULTS(bool, use_sni, true) - OPTION_WITH_DEFAULTS(bool, use_compression, false) - OPTION_WITH_DEFAULTS(bool, validate_certificates, true) - OPTION_WITH_DEFAULTS(bool, allow_self_signed_certificates, false) - OPTION_WITH_DEFAULTS(Optional>, root_certificates, ) - OPTION_WITH_DEFAULTS(Function, alert_handler, [](auto) {}) - OPTION_WITH_DEFAULTS(Function, finish_callback, [] {}) - OPTION_WITH_DEFAULTS(Function()>, certificate_provider, [] { return Vector {}; }) - OPTION_WITH_DEFAULTS(bool, enable_extended_master_secret, true) - -#undef OPTION_WITH_DEFAULTS -}; - -class SegmentedBuffer { +class WolfTLS final : public Core::Socket { public: - [[nodiscard]] size_t size() const { return m_size; } - [[nodiscard]] bool is_empty() const { return m_size == 0; } - void transfer(Bytes dest, size_t size) + WolfTLS(WOLFSSL_CTX* context, WOLFSSL* ssl, NonnullOwnPtr underlying) + : m_context(context) + , m_ssl(ssl) + , m_underlying(move(underlying)) { - VERIFY(size <= dest.size()); - size_t transferred = 0; - while (transferred < size) { - auto& buffer = m_buffers.head(); - size_t to_transfer = min(buffer.size() - m_offset_into_current_buffer, size - transferred); - memcpy(dest.offset(transferred), buffer.data() + m_offset_into_current_buffer, to_transfer); - transferred += to_transfer; - m_offset_into_current_buffer += to_transfer; - if (m_offset_into_current_buffer >= buffer.size()) { - m_buffers.dequeue(); - m_offset_into_current_buffer = 0; - } - m_size -= to_transfer; - } + m_underlying->on_ready_to_read = [this] { + if (on_ready_to_read) + on_ready_to_read(); + }; } - AK::ErrorOr try_append(ReadonlyBytes data) - { - if (Checked::addition_would_overflow(m_size, data.size())) - return AK::Error::from_errno(EOVERFLOW); + ~WolfTLS(); - m_size += data.size(); - m_buffers.enqueue(TRY(ByteBuffer::copy(data))); - return {}; - } + static void install_certificate_store_paths(Vector); + + static ErrorOr> connect(ByteString const& host, u16 port); + ErrorOr read_some(Bytes bytes) override; + ErrorOr write_some(ReadonlyBytes bytes) override; + bool is_eof() const override; + bool is_open() const override; + void close() override; + ErrorOr pending_bytes() const override; + ErrorOr can_read_without_blocking(int timeout) const override; + ErrorOr set_blocking(bool enabled) override; + ErrorOr set_close_on_exec(bool enabled) override { return m_underlying->set_close_on_exec(enabled); } + void set_notifications_enabled(bool enabled) override { m_underlying->set_notifications_enabled(enabled); } private: - size_t m_size { 0 }; - Queue m_buffers; - size_t m_offset_into_current_buffer { 0 }; + static StringView error_text(WOLFSSL*, int error_code); + + WOLFSSL_CTX* m_context { nullptr }; + WOLFSSL* m_ssl { nullptr }; + NonnullOwnPtr m_underlying; }; - -struct Context { - bool verify_chain(StringView host) const; - bool verify_certificate_pair(Certificate const& subject, Certificate const& issuer) const; - - Options options; - - u8 remote_random[32]; - u8 local_random[32]; - u8 session_id[32]; - u8 session_id_size { 0 }; - CipherSuite cipher; - bool is_server { false }; - Vector certificates; - Certificate private_key; - Vector client_certificates; - ByteBuffer master_key; - ByteBuffer premaster_key; - u8 cipher_spec_set { 0 }; - struct { - int created { 0 }; - u8 remote_mac[32]; - u8 local_mac[32]; - u8 local_iv[16]; - u8 remote_iv[16]; - u8 local_aead_iv[4]; - u8 remote_aead_iv[4]; - } crypto; - - Crypto::Hash::Manager handshake_hash; - - ByteBuffer message_buffer; - u64 remote_sequence_number { 0 }; - u64 local_sequence_number { 0 }; - - ConnectionStatus connection_status { ConnectionStatus::Disconnected }; - bool should_expect_successful_read { false }; - u8 critical_error { 0 }; - Error error_code { Error::NoError }; - - ByteBuffer tls_buffer; - - SegmentedBuffer application_buffer; - - bool is_child { false }; - - struct { - // Server Name Indicator - ByteString SNI; // I hate your existence - bool extended_master_secret { false }; - } extensions; - - u8 request_client_certificate { 0 }; - - ByteBuffer cached_handshake; - - ClientVerificationStaus client_verified { Verified }; - - bool connection_finished { false }; - bool close_notify { false }; - bool has_invoked_finish_or_error_callback { false }; - - // message flags - u8 handshake_messages[11] { 0 }; - ByteBuffer user_data; - HashMap root_certificates; - - Vector alpn; - StringView negotiated_alpn; - - size_t send_retries { 0 }; - - time_t handshake_initiation_timestamp { 0 }; - - struct { - ByteBuffer p; - ByteBuffer g; - ByteBuffer Ys; - } server_diffie_hellman_params; - - OwnPtr server_key_exchange_curve; -}; - -class TLSv12 final : public Core::Socket { -private: - Core::Socket& underlying_stream() - { - return *m_stream.visit([&](auto& stream) -> Core::Socket* { return stream; }); - } - Core::Socket const& underlying_stream() const - { - return *m_stream.visit([&](auto& stream) -> Core::Socket const* { return stream; }); - } - -public: - /// Reads into a buffer, with the maximum size being the size of the buffer. - /// The amount of bytes read can be smaller than the size of the buffer. - /// Returns either the bytes that were read, or an errno in the case of - /// failure. - virtual ErrorOr read_some(Bytes) override; - - /// Tries to write the entire contents of the buffer. It is possible for - /// less than the full buffer to be written. Returns either the amount of - /// bytes written into the stream, or an errno in the case of failure. - virtual ErrorOr write_some(ReadonlyBytes) override; - - virtual bool is_eof() const override { return m_context.application_buffer.is_empty() && (m_context.connection_finished || underlying_stream().is_eof()); } - - virtual bool is_open() const override { return is_established(); } - virtual void close() override; - - virtual ErrorOr pending_bytes() const override { return m_context.application_buffer.size(); } - virtual ErrorOr can_read_without_blocking(int = 0) const override { return !m_context.application_buffer.is_empty(); } - virtual ErrorOr set_blocking(bool block) override - { - VERIFY(!block); - return {}; - } - virtual ErrorOr set_close_on_exec(bool enabled) override { return underlying_stream().set_close_on_exec(enabled); } - - virtual void set_notifications_enabled(bool enabled) override { underlying_stream().set_notifications_enabled(enabled); } - - static ErrorOr> connect(ByteString const& host, u16 port, Options = {}); - static ErrorOr> connect(ByteString const& host, Core::Socket& underlying_stream, Options = {}); - - using StreamVariantType = Variant, Core::Socket*>; - explicit TLSv12(StreamVariantType, Options); - - bool is_established() const { return m_context.connection_status == ConnectionStatus::Established; } - - void set_sni(StringView sni) - { - if (m_context.is_server || m_context.critical_error || m_context.connection_status != ConnectionStatus::Disconnected) { - dbgln("invalid state for set_sni"); - return; - } - m_context.extensions.SNI = sni; - } - - void set_root_certificates(Vector); - - static Vector parse_pem_certificate(ReadonlyBytes certificate_pem_buffer, ReadonlyBytes key_pem_buffer); - - StringView alpn() const { return m_context.negotiated_alpn; } - - bool supports_cipher(CipherSuite suite) const - { - switch (suite) { -#define C(is_supported, suite, key_exchange, cipher, hash, iv_size, is_aead) \ - case suite: \ - return is_supported; - ENUMERATE_CIPHERS(C) -#undef C - default: - return false; - } - } - - bool supports_version(ProtocolVersion v) const - { - return v == ProtocolVersion::VERSION_1_2; - } - - void alert(AlertLevel, AlertDescription); - - Function on_tls_error; - Function on_tls_finished; - Function on_tls_certificate_request; - Function on_connected; - -private: - void setup_connection(); - - void consume(ReadonlyBytes record); - - ByteBuffer hmac_message(ReadonlyBytes buf, Optional const buf2, size_t mac_length, bool local = false); - void ensure_hmac(size_t digest_size, bool local); - - void update_packet(ByteBuffer& packet); - void update_hash(ReadonlyBytes in, size_t header_size); - - void write_packet(ByteBuffer& packet, bool immediately = false); - - ByteBuffer build_client_key_exchange(); - ByteBuffer build_server_key_exchange(); - - ByteBuffer build_hello(); - ByteBuffer build_handshake_finished(); - ByteBuffer build_certificate(); - ByteBuffer build_alert(bool critical, u8 code); - ByteBuffer build_change_cipher_spec(); - 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(); - ErrorOr read_from_socket(); - - bool check_connection_state(bool read); - void notify_client_for_app_data(); - - ssize_t handle_server_hello(ReadonlyBytes, WritePacketStage&); - 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_ecdhe_server_key_exchange(ReadonlyBytes, u8& server_public_key_length); - ssize_t handle_ecdhe_rsa_server_key_exchange(ReadonlyBytes); - ssize_t handle_ecdhe_ecdsa_server_key_exchange(ReadonlyBytes); - ssize_t handle_server_hello_done(ReadonlyBytes); - ssize_t handle_certificate_verify(ReadonlyBytes); - ssize_t handle_handshake_payload(ReadonlyBytes); - ssize_t handle_message(ReadonlyBytes); - - void pseudorandom_function(Bytes output, ReadonlyBytes secret, u8 const* label, size_t label_length, ReadonlyBytes seed, ReadonlyBytes seed_b); - - ssize_t verify_rsa_server_key_exchange(ReadonlyBytes server_key_info_buffer, ReadonlyBytes signature_buffer); - ssize_t verify_ecdsa_server_key_exchange(ReadonlyBytes server_key_info_buffer, ReadonlyBytes signature_buffer); - - size_t key_length() const - { - switch (m_context.cipher) { -#define C(is_supported, suite, key_exchange, cipher, hash, iv_size, is_aead) \ - case suite: \ - return cipher_key_size(cipher) / 8; - ENUMERATE_CIPHERS(C) -#undef C - default: - return 128 / 8; - } - } - - size_t mac_length() const - { - switch (m_context.cipher) { -#define C(is_supported, suite, key_exchange, cipher, hash, iv_size, is_aead) \ - case suite: \ - return hash ::digest_size(); - ENUMERATE_CIPHERS(C) -#undef C - default: - return Crypto::Hash::SHA256::digest_size(); - } - } - - Crypto::Hash::HashKind hmac_hash() const - { - switch (mac_length()) { - case Crypto::Hash::SHA512::DigestSize: - return Crypto::Hash::HashKind::SHA512; - case Crypto::Hash::SHA384::DigestSize: - return Crypto::Hash::HashKind::SHA384; - case Crypto::Hash::SHA256::DigestSize: - case Crypto::Hash::SHA1::DigestSize: - default: - return Crypto::Hash::HashKind::SHA256; - } - } - - size_t iv_length() const - { - switch (m_context.cipher) { -#define C(is_supported, suite, key_exchange, cipher, hash, iv_size, is_aead) \ - case suite: \ - return iv_size; - ENUMERATE_CIPHERS(C) -#undef C - default: - return 16; - } - } - - bool is_aead() const - { - switch (m_context.cipher) { -#define C(is_supported, suite, key_exchange, cipher, hash, iv_size, is_aead) \ - case suite: \ - return is_aead; - ENUMERATE_CIPHERS(C) -#undef C - default: - return false; - } - } - - bool expand_key(); - - bool compute_master_secret_from_pre_master_secret(size_t length); - - void try_disambiguate_error() const; - - bool m_eof { false }; - StreamVariantType m_stream; - Context m_context; - - OwnPtr> m_hmac_local; - OwnPtr> m_hmac_remote; - - using CipherVariant = Variant< - Empty, - Crypto::Cipher::AESCipher::CBCMode, - Crypto::Cipher::AESCipher::GCMMode>; - CipherVariant m_cipher_local {}; - CipherVariant m_cipher_remote {}; - - bool m_has_scheduled_write_flush { false }; - bool m_has_scheduled_app_data_flush { false }; - i32 m_max_wait_time_for_handshake_in_seconds { 10 }; - - RefPtr m_handshake_timeout_timer; -}; - } diff --git a/Userland/Libraries/LibWebSocket/Impl/WebSocketImplSerenity.cpp b/Userland/Libraries/LibWebSocket/Impl/WebSocketImplSerenity.cpp index 3aa0d5cf416..d8e20cf768d 100644 --- a/Userland/Libraries/LibWebSocket/Impl/WebSocketImplSerenity.cpp +++ b/Userland/Libraries/LibWebSocket/Impl/WebSocketImplSerenity.cpp @@ -44,13 +44,15 @@ void WebSocketImplSerenity::connect(ConnectionInfo const& connection_info) auto socket_result = [&]() -> ErrorOr> { auto host = TRY(connection_info.url().serialized_host()).to_byte_string(); if (connection_info.is_secure()) { - TLS::Options options; - options.set_alert_handler([this](auto) { - on_connection_error(); - }); + auto result = TLS::WolfTLS::connect(host, connection_info.url().port_or_default()); + if (result.is_error()) { + Core::deferred_invoke([this] { + on_connection_error(); + }); + return result.release_error(); + } - return TRY(Core::BufferedSocket::create( - TRY(TLS::TLSv12::connect(host, connection_info.url().port_or_default(), move(options))))); + return TRY(Core::BufferedSocket::create(result.release_value())); } return TRY(Core::BufferedTCPSocket::create( diff --git a/Userland/Libraries/LibWebSocket/WebSocket.cpp b/Userland/Libraries/LibWebSocket/WebSocket.cpp index 3720c3e99cc..6b8c31d1578 100644 --- a/Userland/Libraries/LibWebSocket/WebSocket.cpp +++ b/Userland/Libraries/LibWebSocket/WebSocket.cpp @@ -616,7 +616,8 @@ void WebSocket::fatal_error(WebSocket::Error error) void WebSocket::discard_connection() { deferred_invoke([this] { - VERIFY(m_impl); + if (!m_impl) + return; m_impl->discard_connection(); m_impl->on_connection_error = nullptr; m_impl->on_connected = nullptr; diff --git a/Userland/Services/RequestServer/CMakeLists.txt b/Userland/Services/RequestServer/CMakeLists.txt index e4a4b01dd60..09032cf394d 100644 --- a/Userland/Services/RequestServer/CMakeLists.txt +++ b/Userland/Services/RequestServer/CMakeLists.txt @@ -19,4 +19,5 @@ set(GENERATED_SOURCES ) serenity_bin(RequestServer) + target_link_libraries(RequestServer PRIVATE LibCore LibCrypto LibIPC LibHTTP LibMain LibTLS LibWebSocket LibURL LibThreading) diff --git a/Userland/Services/RequestServer/ConnectionCache.cpp b/Userland/Services/RequestServer/ConnectionCache.cpp index c7dfdd13538..5f7f8f0007c 100644 --- a/Userland/Services/RequestServer/ConnectionCache.cpp +++ b/Userland/Services/RequestServer/ConnectionCache.cpp @@ -12,7 +12,7 @@ namespace RequestServer::ConnectionCache { Threading::RWLockProtected>>>>> g_tcp_connection_cache {}; -Threading::RWLockProtected>>>>> g_tls_connection_cache {}; +Threading::RWLockProtected>>>>> g_tls_connection_cache {}; Threading::RWLockProtected> g_inferred_server_properties; void request_did_finish(URL::URL const& url, Core::Socket const* socket) @@ -121,7 +121,7 @@ void request_did_finish(URL::URL const& url, Core::Socket const* socket) } }; - if (is>(socket)) + if (is>(socket)) fire_off_next_job(g_tls_connection_cache); else if (is>(socket)) fire_off_next_job(g_tcp_connection_cache); diff --git a/Userland/Services/RequestServer/ConnectionCache.h b/Userland/Services/RequestServer/ConnectionCache.h index 8a5482c8205..7807bdb85ea 100644 --- a/Userland/Services/RequestServer/ConnectionCache.h +++ b/Userland/Services/RequestServer/ConnectionCache.h @@ -57,7 +57,6 @@ struct Proxy { struct JobData { Function start {}; Function fail {}; - Function()> provide_client_certificates {}; struct TimingInfo { #if REQUESTSERVER_DEBUG bool valid { true }; @@ -69,10 +68,9 @@ struct JobData { #endif } timing_info {}; - JobData(Function start, Function fail, Function()> provide_client_certificates, TimingInfo timing_info) + JobData(Function start, Function fail, TimingInfo timing_info) : start(move(start)) , fail(move(fail)) - , provide_client_certificates(move(provide_client_certificates)) , timing_info(move(timing_info)) { } @@ -80,7 +78,6 @@ struct JobData { JobData(JobData&& other) : start(move(other.start)) , fail(move(other.fail)) - , provide_client_certificates(move(other.provide_client_certificates)) , timing_info(move(other.timing_info)) { #if REQUESTSERVER_DEBUG @@ -106,16 +103,6 @@ struct JobData { return JobData { [job](auto& socket) { job->start(socket); }, [job](auto error) { job->fail(error); }, - [job] { - if constexpr (requires { job->on_certificate_requested; }) { - if (job->on_certificate_requested) - return job->on_certificate_requested(); - } else { - // "use" `job`, otherwise clang gets sad. - (void)job; - } - return Vector {}; - }, { #if REQUESTSERVER_DEBUG .timer = Core::ElapsedTimer::start_new(Core::TimerType::Precise), @@ -155,7 +142,7 @@ struct ConnectionKey { bool operator==(ConnectionKey const&) const = default; }; -}; +} template<> struct AK::Traits : public AK::DefaultTraits { @@ -172,7 +159,7 @@ struct InferredServerProperties { }; extern Threading::RWLockProtected>>>>> g_tcp_connection_cache; -extern Threading::RWLockProtected>>>>> g_tls_connection_cache; +extern Threading::RWLockProtected>>>>> g_tls_connection_cache; extern Threading::RWLockProtected> g_inferred_server_properties; void request_did_finish(URL::URL const&, Core::Socket const*); @@ -196,29 +183,7 @@ ErrorOr recreate_socket_if_needed(T& connection, URL::URL const& url) return {}; }; - if constexpr (IsSame) { - TLS::Options options; - options.set_alert_handler([&connection](TLS::AlertDescription alert) { - Core::NetworkJob::Error reason; - if (alert == TLS::AlertDescription::HANDSHAKE_FAILURE) - reason = Core::NetworkJob::Error::ProtocolFailed; - else if (alert == TLS::AlertDescription::DECRYPT_ERROR) - reason = Core::NetworkJob::Error::ConnectionFailed; - else - reason = Core::NetworkJob::Error::TransmissionFailed; - - if (connection.job_data->fail) - connection.job_data->fail(reason); - }); - options.set_certificate_provider([&connection]() -> Vector { - if (connection.job_data->provide_client_certificates) - return connection.job_data->provide_client_certificates(); - return {}; - }); - TRY(set_socket(TRY((connection.proxy.template tunnel(url, move(options)))))); - } else { - TRY(set_socket(TRY((connection.proxy.template tunnel(url))))); - } + TRY(set_socket(TRY((connection.proxy.template tunnel(url))))); dbgln_if(REQUESTSERVER_DEBUG, "Creating a new socket for {} -> {}", url, connection.socket.ptr()); } return {}; diff --git a/Userland/Services/RequestServer/main.cpp b/Userland/Services/RequestServer/main.cpp index b1164bf374c..8a956160ff0 100644 --- a/Userland/Services/RequestServer/main.cpp +++ b/Userland/Services/RequestServer/main.cpp @@ -26,10 +26,6 @@ ErrorOr serenity_main(Main::Arguments) TRY(Core::System::pledge("stdio inet accept thread unix rpath sendfd recvfd")); - // Ensure the certificates are read out here. - // FIXME: Allow specifying extra certificates on the command line, or in other configuration. - [[maybe_unused]] auto& certs = DefaultRootCACertificates::the(); - Core::EventLoop event_loop; // FIXME: Establish a connection to LookupServer and then drop "unix"? TRY(Core::System::unveil("/tmp/portal/lookup", "rw")); diff --git a/vcpkg.json b/vcpkg.json index b6b76ea3f38..679a4007db8 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -33,6 +33,7 @@ }, "sqlite3", "woff2", + "wolfssl", { "name": "vulkan", "platform": "!android" @@ -70,6 +71,10 @@ { "name": "woff2", "version": "1.0.2#4" + }, + { + "name": "wolfssl", + "version": "5.7.0#1" } ] }