123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550 |
- /*
- * Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #pragma once
- #include "Certificate.h"
- #include <AK/IPv4Address.h>
- #include <AK/Queue.h>
- #include <AK/WeakPtr.h>
- #include <LibCore/Notifier.h>
- #include <LibCore/Socket.h>
- #include <LibCore/Timer.h>
- #include <LibCrypto/Authentication/HMAC.h>
- #include <LibCrypto/BigInt/UnsignedBigInteger.h>
- #include <LibCrypto/Cipher/AES.h>
- #include <LibCrypto/Curves/EllipticCurve.h>
- #include <LibCrypto/Hash/HashManager.h>
- #include <LibCrypto/PK/RSA.h>
- #include <LibTLS/CipherSuite.h>
- #include <LibTLS/TLSPacketBuilder.h>
- 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<CipherSuite> default_usable_cipher_suites()
- {
- Vector<CipherSuite> 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<CipherSuite> 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<SignatureAndHashAlgorithm>, 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<SupportedGroup>, elliptic_curves,
- SupportedGroup::X25519,
- SupportedGroup::SECP256R1,
- SupportedGroup::SECP384R1,
- SupportedGroup::X448)
- OPTION_WITH_DEFAULTS(Vector<ECPointFormat>, 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<Vector<Certificate>>, root_certificates, )
- OPTION_WITH_DEFAULTS(Function<void(AlertDescription)>, alert_handler, [](auto) {})
- OPTION_WITH_DEFAULTS(Function<void()>, finish_callback, [] {})
- OPTION_WITH_DEFAULTS(Function<Vector<Certificate>()>, certificate_provider, [] { return Vector<Certificate> {}; })
- OPTION_WITH_DEFAULTS(bool, enable_extended_master_secret, true)
- #undef OPTION_WITH_DEFAULTS
- };
- class SegmentedBuffer {
- 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)
- {
- 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;
- }
- }
- AK::ErrorOr<void> try_append(ReadonlyBytes data)
- {
- if (Checked<size_t>::addition_would_overflow(m_size, data.size()))
- return AK::Error::from_errno(EOVERFLOW);
- m_size += data.size();
- m_buffers.enqueue(TRY(ByteBuffer::copy(data)));
- return {};
- }
- private:
- size_t m_size { 0 };
- Queue<ByteBuffer> m_buffers;
- size_t m_offset_into_current_buffer { 0 };
- };
- 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<Certificate> certificates;
- Certificate private_key;
- Vector<Certificate> 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 };
- 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<ByteString, Certificate> root_certificates;
- Vector<ByteString> 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<Crypto::Curves::EllipticCurve> 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<Bytes> 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<size_t> 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<size_t> pending_bytes() const override { return m_context.application_buffer.size(); }
- virtual ErrorOr<bool> can_read_without_blocking(int = 0) const override { return !m_context.application_buffer.is_empty(); }
- virtual ErrorOr<void> set_blocking(bool block) override
- {
- VERIFY(!block);
- return {};
- }
- virtual ErrorOr<void> 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<NonnullOwnPtr<TLSv12>> connect(ByteString const& host, u16 port, Options = {});
- static ErrorOr<NonnullOwnPtr<TLSv12>> connect(ByteString const& host, Core::Socket& underlying_stream, Options = {});
- using StreamVariantType = Variant<OwnPtr<Core::Socket>, 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<Certificate>);
- static Vector<Certificate> 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<void(AlertDescription)> on_tls_error;
- Function<void()> on_tls_finished;
- Function<void(TLSv12&)> on_tls_certificate_request;
- Function<void()> on_connected;
- private:
- void setup_connection();
- void consume(ReadonlyBytes record);
- ByteBuffer hmac_message(ReadonlyBytes buf, Optional<ReadonlyBytes> 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<bool> flush();
- void write_into_socket();
- ErrorOr<void> 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<Crypto::Authentication::HMAC<Crypto::Hash::Manager>> m_hmac_local;
- OwnPtr<Crypto::Authentication::HMAC<Crypto::Hash::Manager>> 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<Core::Timer> m_handshake_timeout_timer;
- };
- }
|