/* * 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::Invalid; dbgln("No supported cipher could be agreed upon"); return (i8)Error::NoCommonCipher; } m_context.cipher = cipher; dbgln_if(TLS_DEBUG, "Cipher: {}", (u16)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 = (HandshakeExtension)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 {}", (u16)extension_type, extension_length); if (buffer.size() - res < extension_length) return (i8)Error::NeedMoreData; if (extension_type == HandshakeExtension::ServerName) { // 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)(*(const u8*)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::HostName) 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 = String { (const char*)buffer.offset_pointer(res), sni_name_length }; res += sni_name_length; dbgln("SNI host_name: {}", m_context.extensions.SNI); } } else if (extension_type == HandshakeExtension::ApplicationLayerProtocolNegotiation && 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) { const u8* 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; String alpn_str { (const char*)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 == HandshakeExtension::SignatureAlgorithms) { 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 == HandshakeExtension::ECPointFormats) { // RFC8422 section 5.2: A server that selects an ECC cipher suite in response to a ClientHello message // including a Supported Point Formats Extension appends this extension (along with others) to its // ServerHello message, enumerating the point formats it can parse. The Supported Point Formats Extension, // when used, MUST contain the value 0 (uncompressed) as one of the items in the list of point formats. // // The current implementation only supports uncompressed points, and the server is required to support // uncompressed points. Therefore, this extension can be safely ignored as it should always inform us // that the server supports uncompressed points. res += extension_length; } else { dbgln("Encountered unknown extension {} with length {}", (u16)extension_type, extension_length); res += extension_length; } } 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::ECDH_ECDSA: case KeyExchangeAlgorithm::ECDH_RSA: case KeyExchangeAlgorithm::ECDHE_ECDSA: 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_rsa_server_key_exchange(ReadonlyBytes buffer) { if (buffer.size() < 7) return (i8)Error::NeedMoreData; auto curve_type = buffer[3]; if (curve_type != (u8)ECCurveType::NamedCurve) 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 ((NamedCurve)curve) { case NamedCurve::x25519: m_context.server_key_exchange_curve = make(); break; case NamedCurve::x448: m_context.server_key_exchange_curve = make(); break; case NamedCurve::secp256r1: m_context.server_key_exchange_curve = make(); break; default: return (i8)Error::NotUnderstood; } auto 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); } 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 = signature_buffer[1]; if (signature_algorithm != (u8)SignatureAlgorithm::RSA) { dbgln("verify_rsa_server_key_exchange failed: Signature algorithm is not RSA, 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_rsa_server_key_exchange failed: Attempting to verify signature without certificates"); return (i8)Error::NotSafe; } auto certificate_public_key = m_context.certificates.first().public_key; Crypto::PK::RSAPrivateKey dummy_private_key; auto rsa = Crypto::PK::RSA(certificate_public_key, 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; } }