
This will verify that the signature of the ephemeral key used in the DHE and ECDHE key exchanges is actually generated by the server. This verification is done using the first certificate provided by the server, however the validity of this certificate is not checked here. Instead this code expects the validity to be checked earlier by `TLSv12::handle_certificate`.
394 lines
17 KiB
C++
394 lines
17 KiB
C++
/*
|
|
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
|
|
* Copyright (c) 2022, Michiel Visser <opensource@webmichiel.nl>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <AK/Debug.h>
|
|
#include <AK/Endian.h>
|
|
#include <AK/Random.h>
|
|
|
|
#include <LibCore/Timer.h>
|
|
#include <LibCrypto/ASN1/DER.h>
|
|
#include <LibCrypto/PK/Code/EMSA_PKCS1_V1_5.h>
|
|
#include <LibCrypto/PK/Code/EMSA_PSS.h>
|
|
#include <LibTLS/TLSv12.h>
|
|
|
|
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<Version>(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<CipherSuite>(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)
|
|
{
|
|
auto x25519_key_size_bytes = named_curve_key_size(NamedCurve::x25519) / 8;
|
|
if (buffer.size() < x25519_key_size_bytes + 7)
|
|
return (i8)Error::NeedMoreData;
|
|
|
|
auto curve_type = buffer[3];
|
|
if (curve_type != (u8)ECCurveType::NamedCurve)
|
|
return (i8)Error::NotUnderstood;
|
|
|
|
auto curve = AK::convert_between_host_and_network_endian(ByteReader::load16(buffer.offset_pointer(4)));
|
|
if (curve != (u16)NamedCurve::x25519)
|
|
return (i8)Error::NotUnderstood;
|
|
|
|
auto server_public_key_length = buffer[6];
|
|
if (server_public_key_length != x25519_key_size_bytes)
|
|
return (i8)Error::NotUnderstood;
|
|
|
|
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<Crypto::Hash::Manager>(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;
|
|
}
|
|
}
|