/* * Copyright (c) 2020, Ali Mohammad Pur * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include "Certificate.h" #include #include #include #include #include #include #include #include #include #include #include namespace TLS { inline void print_buffer(ReadonlyBytes buffer) { StringBuilder builder(buffer.size() * 2); for (auto b : buffer) builder.appendff("{:02x} ", b); dbgln("{}", builder.string_view()); } inline void print_buffer(const ByteBuffer& buffer) { print_buffer(buffer.bytes()); } inline void print_buffer(const u8* buffer, size_t size) { print_buffer(ReadonlyBytes { buffer, size }); } class Socket; enum class CipherSuite { Invalid = 0, AES_128_GCM_SHA256 = 0x1301, AES_256_GCM_SHA384 = 0x1302, AES_128_CCM_SHA256 = 0x1304, AES_128_CCM_8_SHA256 = 0x1305, // We support these RSA_WITH_AES_128_CBC_SHA = 0x002F, RSA_WITH_AES_256_CBC_SHA = 0x0035, RSA_WITH_AES_128_CBC_SHA256 = 0x003C, RSA_WITH_AES_256_CBC_SHA256 = 0x003D, // TODO RSA_WITH_AES_128_GCM_SHA256 = 0x009C, RSA_WITH_AES_256_GCM_SHA384 = 0x009D, }; #define ENUMERATE_ALERT_DESCRIPTIONS \ ENUMERATE_ALERT_DESCRIPTION(CloseNotify, 0) \ ENUMERATE_ALERT_DESCRIPTION(UnexpectedMessage, 10) \ ENUMERATE_ALERT_DESCRIPTION(BadRecordMAC, 20) \ ENUMERATE_ALERT_DESCRIPTION(DecryptionFailed, 21) \ ENUMERATE_ALERT_DESCRIPTION(RecordOverflow, 22) \ ENUMERATE_ALERT_DESCRIPTION(DecompressionFailure, 30) \ ENUMERATE_ALERT_DESCRIPTION(HandshakeFailure, 40) \ ENUMERATE_ALERT_DESCRIPTION(NoCertificate, 41) \ ENUMERATE_ALERT_DESCRIPTION(BadCertificate, 42) \ ENUMERATE_ALERT_DESCRIPTION(UnsupportedCertificate, 43) \ ENUMERATE_ALERT_DESCRIPTION(CertificateRevoked, 44) \ ENUMERATE_ALERT_DESCRIPTION(CertificateExpired, 45) \ ENUMERATE_ALERT_DESCRIPTION(CertificateUnknown, 46) \ ENUMERATE_ALERT_DESCRIPTION(IllegalParameter, 47) \ ENUMERATE_ALERT_DESCRIPTION(UnknownCA, 48) \ ENUMERATE_ALERT_DESCRIPTION(AccessDenied, 49) \ ENUMERATE_ALERT_DESCRIPTION(DecodeError, 50) \ ENUMERATE_ALERT_DESCRIPTION(DecryptError, 51) \ ENUMERATE_ALERT_DESCRIPTION(ExportRestriction, 60) \ ENUMERATE_ALERT_DESCRIPTION(ProtocolVersion, 70) \ ENUMERATE_ALERT_DESCRIPTION(InsufficientSecurity, 71) \ ENUMERATE_ALERT_DESCRIPTION(InternalError, 80) \ ENUMERATE_ALERT_DESCRIPTION(InappropriateFallback, 86) \ ENUMERATE_ALERT_DESCRIPTION(UserCanceled, 90) \ ENUMERATE_ALERT_DESCRIPTION(NoRenegotiation, 100) \ ENUMERATE_ALERT_DESCRIPTION(UnsupportedExtension, 110) \ ENUMERATE_ALERT_DESCRIPTION(NoError, 255) enum class AlertDescription : u8 { #define ENUMERATE_ALERT_DESCRIPTION(name, value) name = value, ENUMERATE_ALERT_DESCRIPTIONS #undef ENUMERATE_ALERT_DESCRIPTION }; constexpr static const char* alert_name(AlertDescription descriptor) { #define ENUMERATE_ALERT_DESCRIPTION(name, value) \ case AlertDescription::name: \ return #name; switch (descriptor) { ENUMERATE_ALERT_DESCRIPTIONS } return "Unknown"; #undef ENUMERATE_ALERT_DESCRIPTION } 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, }; enum class AlertLevel : u8 { Warning = 0x01, Critical = 0x02 }; enum HandshakeType { HelloRequest = 0x00, ClientHello = 0x01, ServerHello = 0x02, HelloVerifyRequest = 0x03, CertificateMessage = 0x0b, ServerKeyExchange = 0x0c, CertificateRequest = 0x0d, ServerHelloDone = 0x0e, CertificateVerify = 0x0f, ClientKeyExchange = 0x10, Finished = 0x14 }; enum class HandshakeExtension : u16 { ServerName = 0x00, ApplicationLayerProtocolNegotiation = 0x10, SignatureAlgorithms = 0x0d, }; enum class NameType : u8 { HostName = 0x00, }; enum class WritePacketStage { Initial = 0, ClientHandshake = 1, ServerHandshake = 2, Finished = 3, }; enum class ConnectionStatus { Disconnected, Negotiating, KeyExchange, Renegotiating, Established, }; enum ClientVerificationStaus { Verified, VerificationNeeded, }; struct Options { #define OPTION_WITH_DEFAULTS(typ, name, ...) \ static typ default_##name() { return typ { __VA_ARGS__ }; } \ typ name = default_##name(); OPTION_WITH_DEFAULTS(Vector, usable_cipher_suites, CipherSuite::RSA_WITH_AES_128_CBC_SHA256, CipherSuite::RSA_WITH_AES_256_CBC_SHA256, CipherSuite::RSA_WITH_AES_128_CBC_SHA, CipherSuite::RSA_WITH_AES_256_CBC_SHA, CipherSuite::RSA_WITH_AES_128_GCM_SHA256) OPTION_WITH_DEFAULTS(Version, version, Version::V12) OPTION_WITH_DEFAULTS(bool, use_sni, true) OPTION_WITH_DEFAULTS(bool, use_compression, false) OPTION_WITH_DEFAULTS(bool, validate_certificates, true) #undef OPTION_WITH_DEFAULTS }; struct Context { String to_string() const; bool verify() const; bool verify_chain() const; static void print_file(const StringView& fname); 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 }; u8 critical_error { 0 }; Error error_code { Error::NoError }; ByteBuffer tls_buffer; ByteBuffer application_buffer; bool is_child { false }; struct { // Server Name Indicator String SNI; // I hate your existence } extensions; u8 request_client_certificate { 0 }; ByteBuffer cached_handshake; ClientVerificationStaus client_verified { Verified }; bool connection_finished { false }; // message flags u8 handshake_messages[11] { 0 }; ByteBuffer user_data; Vector root_ceritificates; Vector alpn; StringView negotiated_alpn; size_t send_retries { 0 }; time_t handshake_initiation_timestamp { 0 }; }; class TLSv12 : public Core::Socket { C_OBJECT(TLSv12) public: ByteBuffer& write_buffer() { return m_context.tls_buffer; } bool is_established() const { return m_context.connection_status == ConnectionStatus::Established; } virtual bool connect(const String&, int) override; void set_sni(const 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; } Optional parse_asn1(ReadonlyBytes, bool client_cert = false) const; bool load_certificates(ReadonlyBytes pem_buffer); bool load_private_key(ReadonlyBytes pem_buffer); void set_root_certificates(Vector); bool add_client_key(ReadonlyBytes certificate_pem_buffer, ReadonlyBytes key_pem_buffer); bool add_client_key(Certificate certificate) { m_context.client_certificates.append(move(certificate)); return true; } ByteBuffer finish_build(); const StringView& alpn() const { return m_context.negotiated_alpn; } void add_alpn(const StringView& alpn); bool has_alpn(const StringView& alpn) const; bool supports_cipher(CipherSuite suite) const { return suite == CipherSuite::RSA_WITH_AES_128_CBC_SHA256 || suite == CipherSuite::RSA_WITH_AES_256_CBC_SHA256 || suite == CipherSuite::RSA_WITH_AES_128_CBC_SHA || suite == CipherSuite::RSA_WITH_AES_256_CBC_SHA || suite == CipherSuite::RSA_WITH_AES_128_GCM_SHA256; } bool supports_version(Version v) const { return v == Version::V12; } Optional read(); ByteBuffer read(size_t max_size); bool write(ReadonlyBytes); void alert(AlertLevel, AlertDescription); bool can_read_line() const { return m_context.application_buffer.size() && memchr(m_context.application_buffer.data(), '\n', m_context.application_buffer.size()); } bool can_read() const { return m_context.application_buffer.size() > 0; } String read_line(size_t max_size); Function on_tls_ready_to_read; Function on_tls_ready_to_write; Function on_tls_error; Function on_tls_connected; Function on_tls_finished; Function on_tls_certificate_request; private: explicit TLSv12(Core::Object* parent, Options = {}); virtual bool common_connect(const struct sockaddr*, socklen_t) override; void consume(ReadonlyBytes record); ByteBuffer hmac_message(const ReadonlyBytes& buf, const Optional 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); ByteBuffer build_client_key_exchange(); ByteBuffer build_server_key_exchange(); ByteBuffer build_hello(); ByteBuffer build_finished(); ByteBuffer build_certificate(); ByteBuffer build_done(); ByteBuffer build_alert(bool critical, u8 code); ByteBuffer build_change_cipher_spec(); ByteBuffer build_verify_request(); void build_random(PacketBuilder&); bool flush(); void write_into_socket(); void read_from_socket(); bool check_connection_state(bool read); ssize_t handle_hello(ReadonlyBytes, WritePacketStage&); ssize_t handle_finished(ReadonlyBytes, WritePacketStage&); ssize_t handle_certificate(ReadonlyBytes); ssize_t handle_server_key_exchange(ReadonlyBytes); ssize_t handle_server_hello_done(ReadonlyBytes); ssize_t handle_verify(ReadonlyBytes); ssize_t handle_payload(ReadonlyBytes); ssize_t handle_message(ReadonlyBytes); ssize_t handle_random(ReadonlyBytes); size_t asn1_length(ReadonlyBytes, size_t* octets); void pseudorandom_function(Bytes output, ReadonlyBytes secret, const u8* label, size_t label_length, ReadonlyBytes seed, ReadonlyBytes seed_b); size_t key_length() const { switch (m_context.cipher) { case CipherSuite::AES_128_CCM_8_SHA256: case CipherSuite::AES_128_CCM_SHA256: case CipherSuite::AES_128_GCM_SHA256: case CipherSuite::Invalid: case CipherSuite::RSA_WITH_AES_128_CBC_SHA256: case CipherSuite::RSA_WITH_AES_128_CBC_SHA: case CipherSuite::RSA_WITH_AES_128_GCM_SHA256: default: return 128 / 8; case CipherSuite::AES_256_GCM_SHA384: case CipherSuite::RSA_WITH_AES_256_CBC_SHA: case CipherSuite::RSA_WITH_AES_256_CBC_SHA256: case CipherSuite::RSA_WITH_AES_256_GCM_SHA384: return 256 / 8; } } size_t mac_length() const { switch (m_context.cipher) { case CipherSuite::RSA_WITH_AES_128_CBC_SHA: case CipherSuite::RSA_WITH_AES_256_CBC_SHA: return Crypto::Hash::SHA1::digest_size(); case CipherSuite::AES_256_GCM_SHA384: case CipherSuite::RSA_WITH_AES_256_GCM_SHA384: return Crypto::Hash::SHA512::digest_size(); case CipherSuite::AES_128_CCM_8_SHA256: case CipherSuite::AES_128_CCM_SHA256: case CipherSuite::AES_128_GCM_SHA256: case CipherSuite::Invalid: case CipherSuite::RSA_WITH_AES_128_CBC_SHA256: case CipherSuite::RSA_WITH_AES_128_GCM_SHA256: case CipherSuite::RSA_WITH_AES_256_CBC_SHA256: default: return Crypto::Hash::SHA256::digest_size(); } } size_t iv_length() const { switch (m_context.cipher) { case CipherSuite::AES_128_CCM_8_SHA256: case CipherSuite::AES_128_CCM_SHA256: case CipherSuite::Invalid: case CipherSuite::RSA_WITH_AES_128_CBC_SHA256: case CipherSuite::RSA_WITH_AES_128_CBC_SHA: case CipherSuite::RSA_WITH_AES_256_CBC_SHA256: case CipherSuite::RSA_WITH_AES_256_CBC_SHA: default: return 16; case CipherSuite::AES_128_GCM_SHA256: case CipherSuite::AES_256_GCM_SHA384: case CipherSuite::RSA_WITH_AES_128_GCM_SHA256: case CipherSuite::RSA_WITH_AES_256_GCM_SHA384: return 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. } } bool is_aead() const { switch (m_context.cipher) { case CipherSuite::AES_128_GCM_SHA256: case CipherSuite::AES_256_GCM_SHA384: case CipherSuite::RSA_WITH_AES_128_GCM_SHA256: case CipherSuite::RSA_WITH_AES_256_GCM_SHA384: return true; default: return false; } } bool expand_key(); bool compute_master_secret(size_t length); Optional verify_chain_and_get_matching_certificate(const StringView& host) const; void try_disambiguate_error() const; Context m_context; OwnPtr> m_hmac_local; OwnPtr> m_hmac_remote; struct { OwnPtr cbc; OwnPtr gcm; } m_aes_local, m_aes_remote; bool m_has_scheduled_write_flush { false }; i32 m_max_wait_time_for_handshake_in_seconds { 10 }; RefPtr m_handshake_timeout_timer; }; }