From 818dc86568f8b3f7a34293884b639e72c1da573c Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 22 Apr 2020 16:03:01 +0200 Subject: [PATCH] libtls: Add TLS 1.3 implementation of tls_aead_t The key material, in particular the nonce/IV, is derived differently and the IV is also generated in a different way. Additionally, the actual content type is encrypted and there may be optional padding to mask the actual size of the encrypted data. --- src/libcharon/Android.mk | 8 +- src/libtls/Makefile.am | 2 +- src/libtls/tls_aead.h | 20 +++ src/libtls/tls_aead_seq.c | 264 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 289 insertions(+), 5 deletions(-) create mode 100644 src/libtls/tls_aead_seq.c diff --git a/src/libcharon/Android.mk b/src/libcharon/Android.mk index cefab67cf..885b2e751 100644 --- a/src/libcharon/Android.mk +++ b/src/libcharon/Android.mk @@ -220,10 +220,10 @@ ifneq ($(or $(call plugin_enabled, eap-tls), $(call plugin_enabled, eap-ttls), \ $(call plugin_enabled, eap-peap), $(call plugin_enabled, eap-tnc)),) LOCAL_C_INCLUDES += $(LOCAL_PATH)/../libtls/ LOCAL_SRC_FILES += $(addprefix ../libtls/, \ - tls_protection.c tls_compression.c tls_fragmentation.c tls_alert.c \ - tls_crypto.c tls_prf.c tls_socket.c tls_eap.c tls_cache.c tls_peer.c \ - tls_aead_expl.c tls_aead_impl.c tls_aead_null.c tls_aead.c \ - tls_server.c tls.c \ + tls_protection.c tls_compression.c tls_fragmentation.c tls_alert.c \ + tls_crypto.c tls_prf.c tls_socket.c tls_eap.c tls_cache.c tls_peer.c \ + tls_aead_expl.c tls_aead_impl.c tls_aead_null.c tls_aead_seq.c tls_aead.c \ + tls_server.c tls.c \ ) endif diff --git a/src/libtls/Makefile.am b/src/libtls/Makefile.am index 7319bd76f..2aa3b7f10 100644 --- a/src/libtls/Makefile.am +++ b/src/libtls/Makefile.am @@ -11,7 +11,7 @@ ipseclib_LTLIBRARIES = libtls.la libtls_la_SOURCES = \ tls_protection.c tls_compression.c tls_fragmentation.c tls_alert.c \ tls_crypto.c tls_prf.c tls_socket.c tls_eap.c tls_cache.c tls_peer.c \ - tls_aead_expl.c tls_aead_impl.c tls_aead_null.c tls_aead.c \ + tls_aead_expl.c tls_aead_impl.c tls_aead_null.c tls_aead_seq.c tls_aead.c \ tls_server.c tls.c libtls_la_LIBADD = \ diff --git a/src/libtls/tls_aead.h b/src/libtls/tls_aead.h index e067a13f6..3e1e01355 100644 --- a/src/libtls/tls_aead.h +++ b/src/libtls/tls_aead.h @@ -1,4 +1,7 @@ /* + * Copyright (C) 2020 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG * @@ -147,10 +150,27 @@ tls_aead_t *tls_aead_create_null(integrity_algorithm_t mac); /** * Create a tls_aead instance using real a AEAD cipher. * + * The sequence number is used as IV directly. The internally used nonce + * is derived from the key material. Used for TLS 1.2. + * * @param encr AEAD encryption algorithm * @param encr_size encryption key size, in bytes * @return TLS AEAD transform */ tls_aead_t *tls_aead_create_aead(encryption_algorithm_t encr, size_t encr_size); +/** + * Create a tls_aead instance using real a AEAD cipher and sequence number + * as IV. + * + * The sequence number XORed with a static value of at least the same length + * that is derived from the key material is used as IV. This is what TLS 1.3 + * and newer uses. + * + * @param encr AEAD encryption algorithm + * @param encr_size encryption key size, in bytes + * @return TLS AEAD transform + */ +tls_aead_t *tls_aead_create_seq(encryption_algorithm_t encr, size_t encr_size); + #endif /** TLS_AEAD_H_ @}*/ diff --git a/src/libtls/tls_aead_seq.c b/src/libtls/tls_aead_seq.c new file mode 100644 index 000000000..5b101ce72 --- /dev/null +++ b/src/libtls/tls_aead_seq.c @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2020 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "tls_aead.h" + +#include + +typedef struct private_tls_aead_t private_tls_aead_t; + +/** + * Private data of an tls_aead_t object. + */ +struct private_tls_aead_t { + + /** + * Public tls_aead_t interface. + */ + tls_aead_t public; + + /** + * AEAD transform. + */ + aead_t *aead; + + /** + * IV derived from key material. + */ + chunk_t iv; + + /** + * Size of the salt that's internally used by the AEAD implementation. + */ + size_t salt; +}; + +/** + * Additional data for AEAD (record header) + */ +typedef struct __attribute__((__packed__)) { + uint8_t type; + uint16_t version; + uint16_t length; +} sigheader_t; + +/** + * Generate the IV from the given sequence number. + */ +static bool generate_iv(private_tls_aead_t *this, uint64_t seq, chunk_t iv) +{ + if (iv.len < sizeof(uint64_t) || + iv.len < this->iv.len) + { + return FALSE; + } + memset(iv.ptr, 0, iv.len); + htoun64(iv.ptr + iv.len - sizeof(uint64_t), seq); + memxor(iv.ptr + iv.len - this->iv.len, this->iv.ptr, this->iv.len); + return TRUE; +} + +METHOD(tls_aead_t, encrypt, bool, + private_tls_aead_t *this, tls_version_t version, tls_content_type_t *type, + uint64_t seq, chunk_t *data) +{ + bio_writer_t *writer; + chunk_t assoc, encrypted, iv, padding, plain; + uint8_t icvlen; + sigheader_t hdr; + + iv = chunk_alloca(this->aead->get_iv_size(this->aead)); + if (!generate_iv(this, seq, iv)) + { + return FALSE; + } + + /* no padding for now */ + padding = chunk_empty; + icvlen = this->aead->get_icv_size(this->aead); + + writer = bio_writer_create(data->len + 1 + padding.len + icvlen); + writer->write_data(writer, *data); + writer->write_uint8(writer, *type); + writer->write_data(writer, padding); + writer->skip(writer, icvlen); + encrypted = writer->extract_buf(writer); + writer->destroy(writer); + + plain = encrypted; + plain.len -= icvlen; + + hdr.type = TLS_APPLICATION_DATA; + htoun16(&hdr.version, TLS_1_2); + htoun16(&hdr.length, encrypted.len); + + assoc = chunk_from_thing(hdr); + if (!this->aead->encrypt(this->aead, plain, assoc, iv, NULL)) + { + chunk_free(&encrypted); + return FALSE; + } + chunk_free(data); + *type = TLS_APPLICATION_DATA; + *data = encrypted; + return TRUE; +} + +METHOD(tls_aead_t, decrypt, bool, + private_tls_aead_t *this, tls_version_t version, tls_content_type_t *type, + uint64_t seq, chunk_t *data) +{ + chunk_t assoc, iv; + uint8_t icvlen; + sigheader_t hdr; + + iv = chunk_alloca(this->aead->get_iv_size(this->aead)); + if (!generate_iv(this, seq, iv)) + { + return FALSE; + } + + icvlen = this->aead->get_icv_size(this->aead); + if (data->len < icvlen) + { + return FALSE; + } + + hdr.type = TLS_APPLICATION_DATA; + htoun16(&hdr.version, TLS_1_2); + htoun16(&hdr.length, data->len); + + assoc = chunk_from_thing(hdr); + if (!this->aead->decrypt(this->aead, *data, assoc, iv, NULL)) + { + return FALSE; + } + data->len -= icvlen; + + while (data->len && !data->ptr[data->len-1]) + { /* ignore any padding */ + data->len--; + } + if (data->len < 1) + { + return FALSE; + } + *type = data->ptr[data->len-1]; + data->len--; + return TRUE; +} + +METHOD(tls_aead_t, get_mac_key_size, size_t, + private_tls_aead_t *this) +{ + return 0; +} + +METHOD(tls_aead_t, get_encr_key_size, size_t, + private_tls_aead_t *this) +{ + /* our AEAD implementations add the salt length here, so subtract it */ + return this->aead->get_key_size(this->aead) - this->salt; +} + +METHOD(tls_aead_t, get_iv_size, size_t, + private_tls_aead_t *this) +{ + /* analogous to the change above, we add the salt length here */ + return this->aead->get_iv_size(this->aead) + this->salt; +} + +METHOD(tls_aead_t, set_keys, bool, + private_tls_aead_t *this, chunk_t mac, chunk_t encr, chunk_t iv) +{ + chunk_t key, salt; + bool success; + + if (mac.len || iv.len < this->salt) + { + return FALSE; + } + + /* we have to recombine the keys as our AEAD implementations expect the + * salt as part of the key */ + chunk_clear(&this->iv); + chunk_split(iv, "ma", this->salt, &salt, iv.len - this->salt, &this->iv); + key = chunk_cata("cc", encr, salt); + success = this->aead->set_key(this->aead, key); + memwipe(key.ptr, key.len); + return success; +} + +METHOD(tls_aead_t, destroy, void, + private_tls_aead_t *this) +{ + this->aead->destroy(this->aead); + chunk_clear(&this->iv); + free(this); +} + +/* + * Described in header + */ +tls_aead_t *tls_aead_create_seq(encryption_algorithm_t encr, size_t encr_size) +{ + private_tls_aead_t *this; + size_t salt; + + switch (encr) + { + case ENCR_AES_GCM_ICV16: + case ENCR_CHACHA20_POLY1305: + salt = 4; + break; + case ENCR_AES_CCM_ICV8: + case ENCR_AES_CCM_ICV16: + salt = 3; + break; + default: + return NULL; + } + + INIT(this, + .public = { + .encrypt = _encrypt, + .decrypt = _decrypt, + .get_mac_key_size = _get_mac_key_size, + .get_encr_key_size = _get_encr_key_size, + .get_iv_size = _get_iv_size, + .set_keys = _set_keys, + .destroy = _destroy, + }, + .aead = lib->crypto->create_aead(lib->crypto, encr, encr_size, salt), + .salt = salt, + ); + + if (!this->aead) + { + free(this); + return NULL; + } + + if (this->aead->get_block_size(this->aead) != 1) + { /* TLS does not define any padding scheme for AEAD */ + destroy(this); + return NULL; + } + + return &this->public; +} -- 2.47.3