diff options
Diffstat (limited to 'Libraries/LibTLS')
-rw-r--r-- | Libraries/LibTLS/Exchange.cpp | 40 | ||||
-rw-r--r-- | Libraries/LibTLS/Handshake.cpp | 3 | ||||
-rw-r--r-- | Libraries/LibTLS/Record.cpp | 262 | ||||
-rw-r--r-- | Libraries/LibTLS/TLSv12.h | 31 |
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 }; |