summaryrefslogtreecommitdiff
path: root/Libraries/LibTLS
diff options
context:
space:
mode:
authorAnotherTest <ali.mpfard@gmail.com>2020-11-13 01:59:36 +0330
committerAndreas Kling <kling@serenityos.org>2020-11-14 10:18:54 +0100
commit117274663390395d9ac990b1b69a08f4a8eafba5 (patch)
treed57c5b00f91e257576b4883982a8e15b1d9039cb /Libraries/LibTLS
parentd3c52cef86573f6845ba8e860da2c24831e89cde (diff)
downloadserenity-117274663390395d9ac990b1b69a08f4a8eafba5.zip
LibTLS: Add support for AEAD cipher suites
And integrate AES-GCM.
Diffstat (limited to 'Libraries/LibTLS')
-rw-r--r--Libraries/LibTLS/Exchange.cpp40
-rw-r--r--Libraries/LibTLS/Handshake.cpp3
-rw-r--r--Libraries/LibTLS/Record.cpp262
-rw-r--r--Libraries/LibTLS/TLSv12.h31
4 files changed, 252 insertions, 84 deletions
diff --git a/Libraries/LibTLS/Exchange.cpp b/Libraries/LibTLS/Exchange.cpp
index 9a1d888426..41151a0bc4 100644
--- a/Libraries/LibTLS/Exchange.cpp
+++ b/Libraries/LibTLS/Exchange.cpp
@@ -35,6 +35,8 @@ bool TLSv12::expand_key()
u8 key[192]; // soooooooo many constants
auto key_buffer = ByteBuffer::wrap(key, 192);
+ auto is_aead = this->is_aead();
+
if (m_context.master_key.size() == 0) {
dbg() << "expand_key() with empty master key";
return false;
@@ -52,10 +54,14 @@ bool TLSv12::expand_key()
ByteBuffer::wrap(m_context.local_random, 32));
size_t offset = 0;
- memcpy(m_context.crypto.local_mac, key + offset, mac_size);
- offset += mac_size;
- memcpy(m_context.crypto.remote_mac, key + offset, mac_size);
- offset += mac_size;
+ if (is_aead) {
+ iv_size = 4; // Explicit IV size.
+ } else {
+ memcpy(m_context.crypto.local_mac, key + offset, mac_size);
+ offset += mac_size;
+ memcpy(m_context.crypto.remote_mac, key + offset, mac_size);
+ offset += mac_size;
+ }
auto client_key = key + offset;
offset += key_size;
@@ -75,17 +81,27 @@ bool TLSv12::expand_key()
print_buffer(client_iv, iv_size);
dbg() << "server iv";
print_buffer(server_iv, iv_size);
- dbg() << "client mac key";
- print_buffer(m_context.crypto.local_mac, mac_size);
- dbg() << "server mac key";
- print_buffer(m_context.crypto.remote_mac, mac_size);
+ if (!is_aead) {
+ dbg() << "client mac key";
+ print_buffer(m_context.crypto.local_mac, mac_size);
+ dbg() << "server mac key";
+ print_buffer(m_context.crypto.remote_mac, mac_size);
+ }
#endif
- memcpy(m_context.crypto.local_iv, client_iv, iv_size);
- memcpy(m_context.crypto.remote_iv, server_iv, iv_size);
+ if (is_aead) {
+ memcpy(m_context.crypto.local_aead_iv, client_iv, iv_size);
+ memcpy(m_context.crypto.remote_aead_iv, server_iv, iv_size);
- m_aes_local = make<Crypto::Cipher::AESCipher::CBCMode>(ByteBuffer::wrap(client_key, key_size), key_size * 8, Crypto::Cipher::Intent::Encryption, Crypto::Cipher::PaddingMode::RFC5246);
- m_aes_remote = make<Crypto::Cipher::AESCipher::CBCMode>(ByteBuffer::wrap(server_key, key_size), key_size * 8, Crypto::Cipher::Intent::Decryption, Crypto::Cipher::PaddingMode::RFC5246);
+ m_aes_local.gcm = make<Crypto::Cipher::AESCipher::GCMMode>(ByteBuffer::wrap(client_key, key_size), key_size * 8, Crypto::Cipher::Intent::Encryption, Crypto::Cipher::PaddingMode::RFC5246);
+ m_aes_remote.gcm = make<Crypto::Cipher::AESCipher::GCMMode>(ByteBuffer::wrap(server_key, key_size), key_size * 8, Crypto::Cipher::Intent::Decryption, Crypto::Cipher::PaddingMode::RFC5246);
+ } else {
+ memcpy(m_context.crypto.local_iv, client_iv, iv_size);
+ memcpy(m_context.crypto.remote_iv, server_iv, iv_size);
+
+ m_aes_local.cbc = make<Crypto::Cipher::AESCipher::CBCMode>(ByteBuffer::wrap(client_key, key_size), key_size * 8, Crypto::Cipher::Intent::Encryption, Crypto::Cipher::PaddingMode::RFC5246);
+ m_aes_remote.cbc = make<Crypto::Cipher::AESCipher::CBCMode>(ByteBuffer::wrap(server_key, key_size), key_size * 8, Crypto::Cipher::Intent::Decryption, Crypto::Cipher::PaddingMode::RFC5246);
+ }
m_context.crypto.created = 1;
diff --git a/Libraries/LibTLS/Handshake.cpp b/Libraries/LibTLS/Handshake.cpp
index 832fe820b5..8e2e819ed9 100644
--- a/Libraries/LibTLS/Handshake.cpp
+++ b/Libraries/LibTLS/Handshake.cpp
@@ -73,11 +73,12 @@ ByteBuffer TLSv12::build_hello()
}
// Ciphers
- builder.append((u16)(4 * sizeof(u16)));
+ builder.append((u16)(5 * sizeof(u16)));
builder.append((u16)CipherSuite::RSA_WITH_AES_128_CBC_SHA256);
builder.append((u16)CipherSuite::RSA_WITH_AES_256_CBC_SHA256);
builder.append((u16)CipherSuite::RSA_WITH_AES_128_CBC_SHA);
builder.append((u16)CipherSuite::RSA_WITH_AES_256_CBC_SHA);
+ builder.append((u16)CipherSuite::RSA_WITH_AES_128_GCM_SHA256);
// we don't like compression
builder.append((u8)1);
diff --git a/Libraries/LibTLS/Record.cpp b/Libraries/LibTLS/Record.cpp
index 7350ae09d5..338528c130 100644
--- a/Libraries/LibTLS/Record.cpp
+++ b/Libraries/LibTLS/Record.cpp
@@ -26,6 +26,7 @@
#include <AK/Endian.h>
+#include <AK/MemoryStream.h>
#include <LibCore/Timer.h>
#include <LibCrypto/ASN1/DER.h>
#include <LibCrypto/PK/Code/EMSA_PSS.h>
@@ -68,14 +69,23 @@ void TLSv12::update_packet(ByteBuffer& packet)
}
}
if (m_context.cipher_spec_set && m_context.crypto.created) {
- size_t length = packet.size() - header_size + mac_length();
- auto block_size = m_aes_local->cipher().block_size();
- // If the length is already a multiple a block_size,
- // an entire block of padding is added.
- // In short, we _never_ have no padding.
- size_t padding = block_size - length % block_size;
- length += padding;
- size_t mac_size = mac_length();
+ size_t length = packet.size() - header_size;
+ size_t block_size, padding, mac_size;
+
+ if (!is_aead()) {
+ block_size = m_aes_local.cbc->cipher().block_size();
+ // If the length is already a multiple a block_size,
+ // an entire block of padding is added.
+ // In short, we _never_ have no padding.
+ padding = block_size - length % block_size;
+ length += padding;
+ mac_size = mac_length();
+ length += mac_size;
+ } else {
+ block_size = m_aes_local.gcm->cipher().block_size();
+ padding = 0;
+ mac_size = 0; // AEAD provides its own authentication scheme.
+ }
if (m_context.crypto.created == 1) {
// `buffer' will continue to be encrypted
@@ -83,41 +93,93 @@ void TLSv12::update_packet(ByteBuffer& packet)
size_t buffer_position = 0;
auto iv_size = iv_length();
- // We need enough space for a header, iv_length bytes of IV and whatever the packet contains
- auto ct = ByteBuffer::create_uninitialized(length + header_size + iv_size);
-
- // copy the header over
- ct.overwrite(0, packet.data(), header_size - 2);
-
// copy the packet, sans the header
buffer.overwrite(buffer_position, packet.offset_pointer(header_size), packet.size() - header_size);
buffer_position += packet.size() - header_size;
- // get the appropricate HMAC value for the entire packet
- auto mac = hmac_message(packet, {}, mac_size, true);
+ ByteBuffer ct;
+
+ if (is_aead()) {
+ // We need enough space for a header, the data, a tag, and the IV
+ ct = ByteBuffer::create_uninitialized(length + header_size + iv_size + 16);
+
+ // copy the header over
+ ct.overwrite(0, packet.data(), header_size - 2);
+
+ // AEAD AAD (13)
+ // Seq. no (8)
+ // content type (1)
+ // version (2)
+ // length (2)
+ u8 aad[13];
+ Bytes aad_bytes { aad, 13 };
+ OutputMemoryStream aad_stream { aad_bytes };
+
+ u64 seq_no = AK::convert_between_host_and_network_endian(m_context.local_sequence_number);
+ u16 len = AK::convert_between_host_and_network_endian((u16)(packet.size() - header_size));
+
+ aad_stream.write({ &seq_no, sizeof(seq_no) });
+ aad_stream.write(packet.bytes().slice(0, 3)); // content-type + version
+ aad_stream.write({ &len, sizeof(len) }); // length
+ ASSERT(aad_stream.is_end());
+
+ // AEAD IV (12)
+ // IV (4)
+ // (Nonce) (8)
+ // -- Our GCM impl takes 16 bytes
+ // zero (4)
+ u8 iv[16];
+ Bytes iv_bytes { iv, 16 };
+ Bytes { m_context.crypto.local_aead_iv, 4 }.copy_to(iv_bytes);
+ AK::fill_with_random(iv_bytes.offset(4), 8);
+ memset(iv_bytes.offset(12), 0, 4);
+
+ // write the random part of the iv out
+ iv_bytes.slice(4, 8).copy_to(ct.bytes().slice(header_size));
+
+ // Write the encrypted data and the tag
+ m_aes_local.gcm->encrypt(
+ packet.bytes().slice(header_size, length),
+ ct.bytes().slice(header_size + 8, length),
+ iv_bytes,
+ aad_bytes,
+ ct.bytes().slice(header_size + 8 + length, 16));
+
+ ASSERT(header_size + 8 + length + 16 == ct.size());
+
+ } else {
+ // We need enough space for a header, iv_length bytes of IV and whatever the packet contains
+ ct = ByteBuffer::create_uninitialized(length + header_size + iv_size);
+
+ // copy the header over
+ ct.overwrite(0, packet.data(), header_size - 2);
- // write the MAC
- buffer.overwrite(buffer_position, mac.data(), mac.size());
- buffer_position += mac.size();
+ // get the appropricate HMAC value for the entire packet
+ auto mac = hmac_message(packet, {}, mac_size, true);
- // Apply the padding (a packet MUST always be padded)
- memset(buffer.offset_pointer(buffer_position), padding - 1, padding);
- buffer_position += padding;
+ // write the MAC
+ buffer.overwrite(buffer_position, mac.data(), mac.size());
+ buffer_position += mac.size();
- ASSERT(buffer_position == buffer.size());
+ // Apply the padding (a packet MUST always be padded)
+ memset(buffer.offset_pointer(buffer_position), padding - 1, padding);
+ buffer_position += padding;
- auto iv = ByteBuffer::create_uninitialized(iv_size);
- AK::fill_with_random(iv.data(), iv.size());
+ ASSERT(buffer_position == buffer.size());
- // write it into the ciphertext portion of the message
- ct.overwrite(header_size, iv.data(), iv.size());
+ auto iv = ByteBuffer::create_uninitialized(iv_size);
+ AK::fill_with_random(iv.data(), iv.size());
- ASSERT(header_size + iv_size + length == ct.size());
- ASSERT(length % block_size == 0);
+ // write it into the ciphertext portion of the message
+ ct.overwrite(header_size, iv.data(), iv.size());
- // get a block to encrypt into
- auto view = ct.bytes().slice(header_size + iv_size, length);
- m_aes_local->encrypt(buffer, view, iv);
+ ASSERT(header_size + iv_size + length == ct.size());
+ ASSERT(length % block_size == 0);
+
+ // get a block to encrypt into
+ auto view = ct.bytes().slice(header_size + iv_size, length);
+ m_aes_local.cbc->encrypt(buffer, view, iv);
+ }
// store the correct ciphertext length into the packet
u16 ct_length = (u16)ct.size() - header_size;
@@ -211,50 +273,116 @@ ssize_t TLSv12::handle_message(const ByteBuffer& buffer)
print_buffer(buffer.slice_view(header_size, length));
#endif
- ASSERT(m_aes_remote);
- auto iv_size = iv_length();
+ if (is_aead()) {
+ ASSERT(m_aes_remote.gcm);
+
+ if (length < 24) {
+ dbg() << "Invalid packet length";
+ auto packet = build_alert(true, (u8)AlertDescription::DecryptError);
+ write_packet(packet);
+ return (i8)Error::BrokenPacket;
+ }
+
+ auto packet_length = length - iv_length() - 16;
+ auto payload = plain.bytes();
+ auto decrypted = ByteBuffer::create_uninitialized(packet_length);
+
+ // AEAD AAD (13)
+ // Seq. no (8)
+ // content type (1)
+ // version (2)
+ // length (2)
+ u8 aad[13];
+ Bytes aad_bytes { aad, 13 };
+ OutputMemoryStream aad_stream { aad_bytes };
+
+ u64 seq_no = AK::convert_between_host_and_network_endian(m_context.remote_sequence_number);
+ u16 len = AK::convert_between_host_and_network_endian((u16)packet_length);
+
+ aad_stream.write({ &seq_no, sizeof(seq_no) }); // Sequence number
+ aad_stream.write(buffer.bytes().slice(0, header_size - 2)); // content-type + version
+ aad_stream.write({ &len, sizeof(u16) });
+ ASSERT(aad_stream.is_end());
+
+ auto nonce = payload.slice(0, iv_length());
+ payload = payload.slice(iv_length());
+
+ // AEAD IV (12)
+ // IV (4)
+ // (Nonce) (8)
+ // -- Our GCM impl takes 16 bytes
+ // zero (4)
+ u8 iv[16];
+ Bytes iv_bytes { iv, 16 };
+ Bytes { m_context.crypto.remote_aead_iv, 4 }.copy_to(iv_bytes);
+ nonce.copy_to(iv_bytes.slice(4));
+ memset(iv_bytes.offset(12), 0, 4);
+
+ auto ciphertext = payload.slice(0, payload.size() - 16);
+ auto tag = payload.slice(ciphertext.size());
+
+ auto consistency = m_aes_remote.gcm->decrypt(
+ ciphertext,
+ decrypted,
+ iv_bytes,
+ aad_bytes,
+ tag);
+
+ if (consistency != Crypto::VerificationConsistency::Consistent) {
+ dbg() << "integrity check failed (tag length " << tag.size() << ")";
+ auto packet = build_alert(true, (u8)AlertDescription::BadRecordMAC);
+ write_packet(packet);
+
+ return (i8)Error::IntegrityCheckFailed;
+ }
+
+ plain = decrypted;
+ } else {
+ ASSERT(m_aes_remote.cbc);
+ auto iv_size = iv_length();
- auto decrypted = m_aes_remote->create_aligned_buffer(length - iv_size);
- auto iv = buffer.slice_view(header_size, iv_size);
+ auto decrypted = m_aes_remote.cbc->create_aligned_buffer(length - iv_size);
+ auto iv = buffer.slice_view(header_size, iv_size);
- Bytes decrypted_span = decrypted;
- m_aes_remote->decrypt(buffer.bytes().slice(header_size + iv_size, length - iv_size), decrypted_span, iv);
+ Bytes decrypted_span = decrypted;
+ m_aes_remote.cbc->decrypt(buffer.bytes().slice(header_size + iv_size, length - iv_size), decrypted_span, iv);
- length = decrypted_span.size();
+ length = decrypted_span.size();
#ifdef TLS_DEBUG
- dbg() << "Decrypted: ";
- print_buffer(decrypted);
+ dbg() << "Decrypted: ";
+ print_buffer(decrypted);
#endif
- auto mac_size = mac_length();
- if (length < mac_size) {
- dbg() << "broken packet";
- auto packet = build_alert(true, (u8)AlertDescription::DecryptError);
- write_packet(packet);
- return (i8)Error::BrokenPacket;
- }
-
- length -= mac_size;
-
- const u8* message_hmac = decrypted_span.offset(length);
- u8 temp_buf[5];
- memcpy(temp_buf, buffer.offset_pointer(0), 3);
- *(u16*)(temp_buf + 3) = AK::convert_between_host_and_network_endian(length);
- auto hmac = hmac_message({ temp_buf, 5 }, decrypted_span.slice(0, length), mac_size);
- auto message_mac = ByteBuffer::wrap(const_cast<u8*>(message_hmac), mac_size);
- if (hmac != message_mac) {
- dbg() << "integrity check failed (mac length " << mac_size << ")";
- dbg() << "mac received:";
- print_buffer(message_mac);
- dbg() << "mac computed:";
- print_buffer(hmac);
- auto packet = build_alert(true, (u8)AlertDescription::BadRecordMAC);
- write_packet(packet);
+ auto mac_size = mac_length();
+ if (length < mac_size) {
+ dbg() << "broken packet";
+ auto packet = build_alert(true, (u8)AlertDescription::DecryptError);
+ write_packet(packet);
+ return (i8)Error::BrokenPacket;
+ }
- return (i8)Error::IntegrityCheckFailed;
+ length -= mac_size;
+
+ const u8* message_hmac = decrypted_span.offset(length);
+ u8 temp_buf[5];
+ memcpy(temp_buf, buffer.offset_pointer(0), 3);
+ *(u16*)(temp_buf + 3) = AK::convert_between_host_and_network_endian(length);
+ auto hmac = hmac_message({ temp_buf, 5 }, decrypted_span.slice(0, length), mac_size);
+ auto message_mac = ByteBuffer::wrap(const_cast<u8*>(message_hmac), mac_size);
+ if (hmac != message_mac) {
+ dbg() << "integrity check failed (mac length " << mac_size << ")";
+ dbg() << "mac received:";
+ print_buffer(message_mac);
+ dbg() << "mac computed:";
+ print_buffer(hmac);
+ auto packet = build_alert(true, (u8)AlertDescription::BadRecordMAC);
+ write_packet(packet);
+
+ return (i8)Error::IntegrityCheckFailed;
+ }
+ plain = decrypted.slice(0, length);
}
- plain = decrypted.slice(0, length);
}
m_context.remote_sequence_number++;
diff --git a/Libraries/LibTLS/TLSv12.h b/Libraries/LibTLS/TLSv12.h
index 3e2788dd61..0ffd44ca54 100644
--- a/Libraries/LibTLS/TLSv12.h
+++ b/Libraries/LibTLS/TLSv12.h
@@ -218,6 +218,8 @@ struct Context {
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;
@@ -296,7 +298,11 @@ public:
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;
+ 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
@@ -423,7 +429,22 @@ private:
case CipherSuite::AES_256_GCM_SHA384:
case CipherSuite::RSA_WITH_AES_128_GCM_SHA256:
case CipherSuite::RSA_WITH_AES_256_GCM_SHA384:
- return 12;
+ 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;
}
}
@@ -440,8 +461,10 @@ private:
OwnPtr<Crypto::Authentication::HMAC<Crypto::Hash::Manager>> m_hmac_local;
OwnPtr<Crypto::Authentication::HMAC<Crypto::Hash::Manager>> m_hmac_remote;
- OwnPtr<Crypto::Cipher::AESCipher::CBCMode> m_aes_local;
- OwnPtr<Crypto::Cipher::AESCipher::CBCMode> m_aes_remote;
+ struct {
+ OwnPtr<Crypto::Cipher::AESCipher::CBCMode> cbc;
+ OwnPtr<Crypto::Cipher::AESCipher::GCMMode> gcm;
+ } m_aes_local, m_aes_remote;
bool m_has_scheduled_write_flush { false };
i32 m_max_wait_time_for_handshake_in_seconds { 10 };