summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibTLS/Handshake.cpp
diff options
context:
space:
mode:
authorDexesTTP <dexes.ttp@gmail.com>2021-05-18 21:55:06 +0200
committerAndreas Kling <kling@serenityos.org>2021-05-19 09:18:45 +0200
commit851e254e8f197590e618364df8e01a57b7f5bc86 (patch)
tree806a3db9a8e8a5ed2b0300aa8671d93ba810a9e9 /Userland/Libraries/LibTLS/Handshake.cpp
parent6d190b299e1f80387a117104841e9382a9814bdc (diff)
downloadserenity-851e254e8f197590e618364df8e01a57b7f5bc86.zip
LibTLS: Rework method names and arrangement in cpp files
This commit only moves and renames methods. The code hasn't changed.
Diffstat (limited to 'Userland/Libraries/LibTLS/Handshake.cpp')
-rw-r--r--Userland/Libraries/LibTLS/Handshake.cpp387
1 files changed, 373 insertions, 14 deletions
diff --git a/Userland/Libraries/LibTLS/Handshake.cpp b/Userland/Libraries/LibTLS/Handshake.cpp
index 3b515a4fab..fc1402f922 100644
--- a/Userland/Libraries/LibTLS/Handshake.cpp
+++ b/Userland/Libraries/LibTLS/Handshake.cpp
@@ -4,7 +4,11 @@
* 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_PSS.h>
#include <LibTLS/TLSv12.h>
@@ -121,22 +125,17 @@ ByteBuffer TLSv12::build_hello()
return packet;
}
-ByteBuffer TLSv12::build_alert(bool critical, u8 code)
+ByteBuffer TLSv12::build_change_cipher_spec()
{
- PacketBuilder builder(MessageType::Alert, (u16)m_context.options.version);
- builder.append((u8)(critical ? AlertLevel::Critical : AlertLevel::Warning));
- builder.append(code);
-
- if (critical)
- m_context.critical_error = code;
-
+ PacketBuilder builder { MessageType::ChangeCipher, 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_finished()
+ByteBuffer TLSv12::build_handshake_finished()
{
PacketBuilder builder { MessageType::Handshake, m_context.options.version, 12 + 64 };
builder.append((u8)HandshakeType::Finished);
@@ -160,11 +159,371 @@ ByteBuffer TLSv12::build_finished()
return packet;
}
-void TLSv12::alert(AlertLevel level, AlertDescription code)
+ssize_t TLSv12::handle_handshake_finished(ReadonlyBytes buffer, WritePacketStage& write_packets)
{
- auto the_alert = build_alert(level == AlertLevel::Critical, (u8)code);
- write_packet(the_alert);
- flush();
+ 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_tls_ready_to_write)
+ on_tls_ready_to_write(*this);
+
+ 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 = 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 HelloRequest:
+ 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 ClientHello:
+ // FIXME: We only support client mode right now
+ if (m_context.is_server) {
+ VERIFY_NOT_REACHED();
+ }
+ payload_res = (i8)Error::UnexpectedMessage;
+ break;
+ case ServerHello:
+ 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 HelloVerifyRequest:
+ dbgln("unsupported: DTLS");
+ payload_res = (i8)Error::UnexpectedMessage;
+ break;
+ case CertificateMessage:
+ 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));
+ if (m_context.certificates.size()) {
+ auto it = m_context.certificates.find_if([](const auto& cert) { return cert.is_valid(); });
+
+ if (it.is_end()) {
+ // no valid certificates
+ dbgln("No valid certificates found");
+ payload_res = (i8)Error::BadCertificate;
+ m_context.critical_error = payload_res;
+ break;
+ }
+
+ // swap the first certificate with the valid one
+ if (it.index() != 0)
+ swap(m_context.certificates[0], m_context.certificates[it.index()]);
+ }
+ } else {
+ payload_res = (i8)Error::UnexpectedMessage;
+ }
+ break;
+ case ServerKeyExchange:
+ 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 CertificateRequest:
+ 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 ServerHelloDone:
+ 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 CertificateVerify:
+ 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 ClientKeyExchange:
+ 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 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: {}", type);
+ return (i8)Error::NotUnderstood;
+ }
+
+ if (type != HelloRequest) {
+ 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::UnexpectedMessage);
+ write_packet(packet);
+ break;
+ }
+ case Error::CompressionNotSupported: {
+ auto packet = build_alert(true, (u8)AlertDescription::DecompressionFailure);
+ write_packet(packet);
+ break;
+ }
+ case Error::BrokenPacket: {
+ auto packet = build_alert(true, (u8)AlertDescription::DecodeError);
+ write_packet(packet);
+ break;
+ }
+ case Error::NotVerified: {
+ auto packet = build_alert(true, (u8)AlertDescription::BadRecordMAC);
+ write_packet(packet);
+ break;
+ }
+ case Error::BadCertificate: {
+ auto packet = build_alert(true, (u8)AlertDescription::BadCertificate);
+ write_packet(packet);
+ break;
+ }
+ case Error::UnsupportedCertificate: {
+ auto packet = build_alert(true, (u8)AlertDescription::UnsupportedCertificate);
+ write_packet(packet);
+ break;
+ }
+ case Error::NoCommonCipher: {
+ auto packet = build_alert(true, (u8)AlertDescription::InsufficientSecurity);
+ write_packet(packet);
+ break;
+ }
+ case Error::NotUnderstood: {
+ auto packet = build_alert(true, (u8)AlertDescription::InternalError);
+ write_packet(packet);
+ break;
+ }
+ case Error::NoRenegotiation: {
+ auto packet = build_alert(true, (u8)AlertDescription::NoRenegotiation);
+ write_packet(packet);
+ break;
+ }
+ case Error::DecryptionFailed: {
+ auto packet = build_alert(true, (u8)AlertDescription::DecryptionFailed);
+ 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;
+}
}