From ba2bcdd8826341bb55937a6544acda080b1d1ac3 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 22 Apr 2020 15:44:31 +0200 Subject: [PATCH 001/107] libtls: Allow tls_aead_t to change the content type The actual content type is encrypted with TLS 1.3, the type in the record header is always Application Data. --- src/libtls/tls_aead.c | 8 ++++---- src/libtls/tls_aead.h | 8 ++++---- src/libtls/tls_aead_expl.c | 8 ++++---- src/libtls/tls_aead_impl.c | 8 ++++---- src/libtls/tls_aead_null.c | 8 ++++---- src/libtls/tls_protection.c | 4 ++-- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/libtls/tls_aead.c b/src/libtls/tls_aead.c index f1daa6f45..e0c7d3be3 100644 --- a/src/libtls/tls_aead.c +++ b/src/libtls/tls_aead.c @@ -51,7 +51,7 @@ typedef struct __attribute__((__packed__)) { } sigheader_t; METHOD(tls_aead_t, encrypt, bool, - private_tls_aead_t *this, tls_version_t version, tls_content_type_t type, + private_tls_aead_t *this, tls_version_t version, tls_content_type_t *type, uint64_t seq, chunk_t *data) { chunk_t assoc, encrypted, iv, plain; @@ -74,7 +74,7 @@ METHOD(tls_aead_t, encrypt, bool, plain = chunk_skip(encrypted, iv.len); plain.len -= icvlen; - hdr.type = type; + hdr.type = *type; htoun64(&hdr.seq, seq); htoun16(&hdr.version, version); htoun16(&hdr.length, plain.len); @@ -91,7 +91,7 @@ METHOD(tls_aead_t, encrypt, bool, } METHOD(tls_aead_t, decrypt, bool, - private_tls_aead_t *this, tls_version_t version, tls_content_type_t type, + private_tls_aead_t *this, tls_version_t version, tls_content_type_t *type, uint64_t seq, chunk_t *data) { chunk_t assoc, iv; @@ -111,7 +111,7 @@ METHOD(tls_aead_t, decrypt, bool, return FALSE; } - hdr.type = type; + hdr.type = *type; htoun64(&hdr.seq, seq); htoun16(&hdr.version, version); htoun16(&hdr.length, data->len - icvlen); diff --git a/src/libtls/tls_aead.h b/src/libtls/tls_aead.h index 389a498a5..e067a13f6 100644 --- a/src/libtls/tls_aead.h +++ b/src/libtls/tls_aead.h @@ -44,13 +44,13 @@ struct tls_aead_t { * gets updated to the IV for the next record. * * @param version TLS version - * @param type TLS content type + * @param type TLS content type (may be changed) * @param seq record sequence number * @param data data to encrypt, encryption result * @return TRUE if successfully encrypted */ bool (*encrypt)(tls_aead_t *this, tls_version_t version, - tls_content_type_t type, uint64_t seq, chunk_t *data); + tls_content_type_t *type, uint64_t seq, chunk_t *data); /** * Decrypt and verify a TLS record. @@ -59,13 +59,13 @@ struct tls_aead_t { * length, decryption is done inline. * * @param version TLS version - * @param type TLS content type + * @param type TLS content type (may be changed) * @param seq record sequence number * @param data data to decrypt, decrypted result * @return TRUE if successfully decrypted */ bool (*decrypt)(tls_aead_t *this, tls_version_t version, - tls_content_type_t type, uint64_t seq, chunk_t *data); + tls_content_type_t *type, uint64_t seq, chunk_t *data); /** * Get the authentication key size. diff --git a/src/libtls/tls_aead_expl.c b/src/libtls/tls_aead_expl.c index 201c9bcf8..9a2c41160 100644 --- a/src/libtls/tls_aead_expl.c +++ b/src/libtls/tls_aead_expl.c @@ -56,14 +56,14 @@ typedef struct __attribute__((__packed__)) { } sigheader_t; METHOD(tls_aead_t, encrypt, bool, - private_tls_aead_t *this, tls_version_t version, tls_content_type_t type, + private_tls_aead_t *this, tls_version_t version, tls_content_type_t *type, uint64_t seq, chunk_t *data) { chunk_t assoc, mac, padding, iv; uint8_t bs, padlen; sigheader_t hdr; - hdr.type = type; + hdr.type = *type; htoun64(&hdr.seq, seq); htoun16(&hdr.version, version); htoun16(&hdr.length, data->len); @@ -99,7 +99,7 @@ METHOD(tls_aead_t, encrypt, bool, } METHOD(tls_aead_t, decrypt, bool, - private_tls_aead_t *this, tls_version_t version, tls_content_type_t type, + private_tls_aead_t *this, tls_version_t version, tls_content_type_t *type, uint64_t seq, chunk_t *data) { chunk_t assoc, mac, iv; @@ -144,7 +144,7 @@ METHOD(tls_aead_t, decrypt, bool, mac = chunk_skip(*data, data->len - bs); data->len -= bs; - hdr.type = type; + hdr.type = *type; htoun64(&hdr.seq, seq); htoun16(&hdr.version, version); htoun16(&hdr.length, data->len); diff --git a/src/libtls/tls_aead_impl.c b/src/libtls/tls_aead_impl.c index 8f83cb456..1b0ec86ab 100644 --- a/src/libtls/tls_aead_impl.c +++ b/src/libtls/tls_aead_impl.c @@ -55,13 +55,13 @@ typedef struct __attribute__((__packed__)) { 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) + tls_content_type_t *type, uint64_t seq, chunk_t *data) { chunk_t assoc, mac, padding; uint8_t bs, padlen; sigheader_t hdr; - hdr.type = type; + hdr.type = *type; htoun64(&hdr.seq, seq); htoun16(&hdr.version, version); htoun16(&hdr.length, data->len); @@ -95,7 +95,7 @@ METHOD(tls_aead_t, encrypt, bool, 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) + tls_content_type_t *type, uint64_t seq, chunk_t *data) { chunk_t assoc, mac, iv; uint8_t bs, padlen; @@ -135,7 +135,7 @@ METHOD(tls_aead_t, decrypt, bool, mac = chunk_skip(*data, data->len - bs); data->len -= bs; - hdr.type = type; + hdr.type = *type; htoun64(&hdr.seq, seq); htoun16(&hdr.version, version); htoun16(&hdr.length, data->len); diff --git a/src/libtls/tls_aead_null.c b/src/libtls/tls_aead_null.c index cb4c10633..0f929333a 100644 --- a/src/libtls/tls_aead_null.c +++ b/src/libtls/tls_aead_null.c @@ -45,12 +45,12 @@ typedef struct __attribute__((__packed__)) { 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) + tls_content_type_t *type, uint64_t seq, chunk_t *data) { chunk_t assoc, mac; sigheader_t hdr; - hdr.type = type; + hdr.type = *type; htoun64(&hdr.seq, seq); htoun16(&hdr.version, version); htoun16(&hdr.length, data->len); @@ -67,7 +67,7 @@ METHOD(tls_aead_t, encrypt, bool, 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) + tls_content_type_t *type, uint64_t seq, chunk_t *data) { chunk_t assoc, mac; sigheader_t hdr; @@ -80,7 +80,7 @@ METHOD(tls_aead_t, decrypt, bool, mac = chunk_skip(*data, data->len - mac.len); data->len -= mac.len; - hdr.type = type; + hdr.type = *type; htoun64(&hdr.seq, seq); htoun16(&hdr.version, version); htoun16(&hdr.length, data->len); diff --git a/src/libtls/tls_protection.c b/src/libtls/tls_protection.c index cea3eca14..1666d664a 100644 --- a/src/libtls/tls_protection.c +++ b/src/libtls/tls_protection.c @@ -76,7 +76,7 @@ METHOD(tls_protection_t, process, status_t, if (this->aead_in) { if (!this->aead_in->decrypt(this->aead_in, this->version, - type, this->seq_in, &data)) + &type, this->seq_in, &data)) { DBG1(DBG_TLS, "TLS record decryption failed"); this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC); @@ -111,7 +111,7 @@ METHOD(tls_protection_t, build, status_t, if (this->aead_out) { if (!this->aead_out->encrypt(this->aead_out, this->version, - *type, this->seq_out, data)) + type, this->seq_out, data)) { DBG1(DBG_TLS, "TLS record encryption failed"); chunk_free(data); From 818dc86568f8b3f7a34293884b639e72c1da573c Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 22 Apr 2020 16:03:01 +0200 Subject: [PATCH 002/107] 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; +} From 3d83d348f454d1ec0b1d833e98edc80286c5691e Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 22 Apr 2020 16:27:00 +0200 Subject: [PATCH 003/107] libtls: Add support to run unit tests with a custom plugin list --- src/libtls/tests/tls_tests.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/libtls/tests/tls_tests.c b/src/libtls/tests/tls_tests.c index 3f22f9c82..87af48e70 100644 --- a/src/libtls/tests/tls_tests.c +++ b/src/libtls/tests/tls_tests.c @@ -35,8 +35,13 @@ static bool test_runner_init(bool init) { if (init) { - plugin_loader_add_plugindirs(PLUGINDIR, PLUGINS); - if (!lib->plugins->load(lib->plugins, PLUGINS)) + char *plugins; + + plugins = getenv("TESTS_PLUGINS") ?: + lib->settings->get_str(lib->settings, + "tests.load", PLUGINS); + plugin_loader_add_plugindirs(PLUGINDIR, plugins); + if (!lib->plugins->load(lib->plugins, plugins)) { return FALSE; } From 02d740551256a2aa29d27c76dbdcf741e1b85cf5 Mon Sep 17 00:00:00 2001 From: Pascal K Date: Thu, 21 May 2020 07:57:14 +0200 Subject: [PATCH 004/107] libtls: Implement HKDF for TLS 1.3 TLS 1.3 uses HMAC-based Extract-and-Expand Key Derivation Function (HKDF) as defined in RFC 5869 to compute traffic secrets. Co-authored-by: bytinbit --- src/libcharon/Android.mk | 4 +- src/libtls/Makefile.am | 8 +- src/libtls/tests/Makefile.am | 1 + src/libtls/tests/suites/test_hkdf.c | 439 ++++++++++++++++++++++++ src/libtls/tests/tls_tests.h | 1 + src/libtls/tls_hkdf.c | 511 ++++++++++++++++++++++++++++ src/libtls/tls_hkdf.h | 128 +++++++ 7 files changed, 1086 insertions(+), 6 deletions(-) create mode 100644 src/libtls/tests/suites/test_hkdf.c create mode 100644 src/libtls/tls_hkdf.c create mode 100644 src/libtls/tls_hkdf.h diff --git a/src/libcharon/Android.mk b/src/libcharon/Android.mk index 885b2e751..0fd8b57f3 100644 --- a/src/libcharon/Android.mk +++ b/src/libcharon/Android.mk @@ -221,9 +221,9 @@ ifneq ($(or $(call plugin_enabled, eap-tls), $(call plugin_enabled, eap-ttls), \ 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_crypto.c tls_prf.c tls_hkdf.c tls_socket.c tls_eap.c tls_cache.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 \ + tls_peer.c tls_server.c tls.c \ ) endif diff --git a/src/libtls/Makefile.am b/src/libtls/Makefile.am index 2aa3b7f10..db31b8042 100644 --- a/src/libtls/Makefile.am +++ b/src/libtls/Makefile.am @@ -10,9 +10,9 @@ AM_LDFLAGS = \ 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_crypto.c tls_prf.c tls_hkdf.c tls_socket.c tls_eap.c tls_cache.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 + tls_peer.c tls_server.c tls.c libtls_la_LIBADD = \ $(top_builddir)/src/libstrongswan/libstrongswan.la @@ -25,8 +25,8 @@ if USE_DEV_HEADERS tls_includedir = ${dev_headers}/tls nobase_tls_include_HEADERS = \ tls_protection.h tls_compression.h tls_fragmentation.h tls_alert.h \ - tls_crypto.h tls_prf.h tls_socket.h tls_eap.h tls_cache.h tls_peer.h \ - tls_server.h tls_handshake.h tls_application.h tls_aead.h tls.h + tls_crypto.h tls_prf.h tls_hkdf.h tls_socket.h tls_eap.h tls_cache.h \ + tls_peer.h tls_server.h tls_handshake.h tls_application.h tls_aead.h tls.h endif SUBDIRS = . tests diff --git a/src/libtls/tests/Makefile.am b/src/libtls/tests/Makefile.am index 456383f02..2220ff4a6 100644 --- a/src/libtls/tests/Makefile.am +++ b/src/libtls/tests/Makefile.am @@ -3,6 +3,7 @@ TESTS = tls_tests check_PROGRAMS = $(TESTS) tls_tests_SOURCES = \ + suites/test_hkdf.c \ suites/test_socket.c \ suites/test_suites.c \ tls_tests.h tls_tests.c diff --git a/src/libtls/tests/suites/test_hkdf.c b/src/libtls/tests/suites/test_hkdf.c new file mode 100644 index 000000000..529c6d540 --- /dev/null +++ b/src/libtls/tests/suites/test_hkdf.c @@ -0,0 +1,439 @@ +/* + * Copyright (C) 2020 Pascal Knecht + * Copyright (C) 2020 Méline Sieber + * HSR Hochschule fuer Technik Rapperswil + * + * 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 + +#include "tls_hkdf.h" + +START_TEST(test_ulfheim_handshake) +{ + chunk_t handshake = chunk_from_chars( + /* Client Hello */ + 0x01, 0x00, 0x00, 0xc6, 0x03, 0x03, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0xe0, + 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, + 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x00, + 0x06, 0x13, 0x01, 0x13, 0x02, 0x13, 0x03, 0x01, + 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x18, 0x00, + 0x16, 0x00, 0x00, 0x13, 0x65, 0x78, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x2e, 0x75, 0x6c, 0x66, 0x68, + 0x65, 0x69, 0x6d, 0x2e, 0x6e, 0x65, 0x74, 0x00, + 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, + 0x17, 0x00, 0x18, 0x00, 0x0d, 0x00, 0x14, 0x00, + 0x12, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, 0x05, + 0x03, 0x08, 0x05, 0x05, 0x01, 0x08, 0x06, 0x06, + 0x01, 0x02, 0x01, 0x00, 0x33, 0x00, 0x26, 0x00, + 0x24, 0x00, 0x1d, 0x00, 0x20, 0x35, 0x80, 0x72, + 0xd6, 0x36, 0x58, 0x80, 0xd1, 0xae, 0xea, 0x32, + 0x9a, 0xdf, 0x91, 0x21, 0x38, 0x38, 0x51, 0xed, + 0x21, 0xa2, 0x8e, 0x3b, 0x75, 0xe9, 0x65, 0xd0, + 0xd2, 0xcd, 0x16, 0x62, 0x54, 0x00, 0x2d, 0x00, + 0x02, 0x01, 0x01, 0x00, 0x2b, 0x00, 0x03, 0x02, + 0x03, 0x04, + /* Server Hello */ + 0x02, 0x00, 0x00, 0x76, 0x03, 0x03, 0x70, 0x71, + 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, + 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x20, 0xe0, + 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, + 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x13, + 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x24, + 0x00, 0x1d, 0x00, 0x20, 0x9f, 0xd7, 0xad, 0x6d, + 0xcf, 0xf4, 0x29, 0x8d, 0xd3, 0xf9, 0x6d, 0x5b, + 0x1b, 0x2a, 0xf9, 0x10, 0xa0, 0x53, 0x5b, 0x14, + 0x88, 0xd7, 0xf8, 0xfa, 0xbb, 0x34, 0x9a, 0x98, + 0x28, 0x80, 0xb6, 0x15, 0x00, 0x2b, 0x00, 0x02, + 0x03, 0x04, + ); + + chunk_t ecdhe = chunk_from_chars( + 0xdf, 0x4a, 0x29, 0x1b, 0xaa, 0x1e, 0xb7, 0xcf, + 0xa6, 0x93, 0x4b, 0x29, 0xb4, 0x74, 0xba, 0xad, + 0x26, 0x97, 0xe2, 0x9f, 0x1f, 0x92, 0x0d, 0xcc, + 0x77, 0xc8, 0xa0, 0xa0, 0x88, 0x44, 0x76, 0x24, + ); + + chunk_t expected_client_handshake_traffic_secret = chunk_from_chars( + 0xff, 0x0e, 0x5b, 0x96, 0x52, 0x91, 0xc6, 0x08, + 0xc1, 0xe8, 0xcd, 0x26, 0x7e, 0xef, 0xc0, 0xaf, + 0xcc, 0x5e, 0x98, 0xa2, 0x78, 0x63, 0x73, 0xf0, + 0xdb, 0x47, 0xb0, 0x47, 0x86, 0xd7, 0x2a, 0xea, + ); + + chunk_t expected_server_handshake_traffic_secret = chunk_from_chars( + 0xa2, 0x06, 0x72, 0x65, 0xe7, 0xf0, 0x65, 0x2a, + 0x92, 0x3d, 0x5d, 0x72, 0xab, 0x04, 0x67, 0xc4, + 0x61, 0x32, 0xee, 0xb9, 0x68, 0xb6, 0xa3, 0x2d, + 0x31, 0x1c, 0x80, 0x58, 0x68, 0x54, 0x88, 0x14, + ); + + chunk_t expected_client_handshake_key = chunk_from_chars( + 0x71, 0x54, 0xf3, 0x14, 0xe6, 0xbe, 0x7d, 0xc0, + 0x08, 0xdf, 0x2c, 0x83, 0x2b, 0xaa, 0x1d, 0x39, + ); + + chunk_t expected_client_handshake_iv = chunk_from_chars( + 0x71, 0xab, 0xc2, 0xca, 0xe4, 0xc6, 0x99, 0xd4, + 0x7c, 0x60, 0x02, 0x68, + ); + + chunk_t expected_server_handshake_key = chunk_from_chars( + 0x84, 0x47, 0x80, 0xa7, 0xac, 0xad, 0x9f, 0x98, + 0x0f, 0xa2, 0x5c, 0x11, 0x4e, 0x43, 0x40, 0x2a, + ); + + chunk_t expected_server_handshake_iv = chunk_from_chars( + 0x4c, 0x04, 0x2d, 0xdc, 0x12, 0x0a, 0x38, 0xd1, + 0x41, 0x7f, 0xc8, 0x15, + ); + + chunk_t expected_client_finished_key = chunk_from_chars( + 0x7c, 0x60, 0xf8, 0xd6, 0x34, 0x6f, 0x4a, 0x96, + 0x91, 0xd2, 0xae, 0x64, 0x5a, 0x78, 0x85, 0xe0, + 0x10, 0x4a, 0xdf, 0xf9, 0x8e, 0xba, 0x98, 0x1c, + 0xa2, 0xf9, 0x9e, 0xf6, 0x2b, 0xdd, 0x8f, 0xaa, + ); + + chunk_t expected_server_finished_key = chunk_from_chars( + 0xea, 0x84, 0xab, 0xd2, 0xad, 0xa0, 0xb5, 0xc6, + 0x4c, 0x08, 0x07, 0xa3, 0x26, 0xb6, 0xfd, 0x94, + 0xa9, 0x59, 0x7e, 0x39, 0xca, 0x62, 0x10, 0x60, + 0x7c, 0x0d, 0x3c, 0x8c, 0x76, 0x68, 0x65, 0x71, + ); + + chunk_t c_secret, c_key, c_iv, s_secret, s_key, s_iv, c_finished, + s_finished; + + tls_hkdf_t *hkdf = tls_hkdf_create(HASH_SHA256, chunk_empty); + hkdf->set_shared_secret(hkdf, ecdhe); + + /* Generate client handshake traffic secret */ + ck_assert(hkdf->generate_secret(hkdf, TLS_HKDF_C_HS_TRAFFIC, handshake, + &c_secret)); + ck_assert_chunk_eq(expected_client_handshake_traffic_secret, c_secret); + + ck_assert(hkdf->derive_key(hkdf, FALSE, 16, &c_key)); + ck_assert_chunk_eq(expected_client_handshake_key, c_key); + + ck_assert(hkdf->derive_iv(hkdf, FALSE, 12, &c_iv)); + ck_assert_chunk_eq(expected_client_handshake_iv, c_iv); + + ck_assert(hkdf->derive_finished(hkdf, TRUE, &c_finished)); + ck_assert_chunk_eq(expected_client_finished_key, c_finished); + + /* Generate server handshake traffic secret */ + ck_assert(hkdf->generate_secret(hkdf, TLS_HKDF_S_HS_TRAFFIC, handshake, + &s_secret)); + ck_assert_chunk_eq(expected_server_handshake_traffic_secret, s_secret); + + ck_assert(hkdf->derive_key(hkdf, TRUE, 16, &s_key)); + ck_assert_chunk_eq(expected_server_handshake_key, s_key); + + ck_assert(hkdf->derive_iv(hkdf, TRUE, 12, &s_iv)); + ck_assert_chunk_eq(expected_server_handshake_iv, s_iv); + + ck_assert(hkdf->derive_finished(hkdf, TRUE, &s_finished)); + ck_assert_chunk_eq(expected_server_finished_key, s_finished); + + hkdf->destroy(hkdf); + + chunk_free(&c_secret); + chunk_free(&c_key); + chunk_free(&c_iv); + chunk_free(&s_secret); + chunk_free(&s_key); + chunk_free(&s_iv); + chunk_free(&c_finished); + chunk_free(&s_finished); +} +END_TEST + +START_TEST(test_ulfheim_traffic) +{ + chunk_t handshake = chunk_from_chars( + /* Client Hello */ + 0x01, 0x00, 0x00, 0xc6, 0x03, 0x03, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0xe0, + 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, + 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x00, + 0x06, 0x13, 0x01, 0x13, 0x02, 0x13, 0x03, 0x01, + 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x18, 0x00, + 0x16, 0x00, 0x00, 0x13, 0x65, 0x78, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x2e, 0x75, 0x6c, 0x66, 0x68, + 0x65, 0x69, 0x6d, 0x2e, 0x6e, 0x65, 0x74, 0x00, + 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, + 0x17, 0x00, 0x18, 0x00, 0x0d, 0x00, 0x14, 0x00, + 0x12, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, 0x05, + 0x03, 0x08, 0x05, 0x05, 0x01, 0x08, 0x06, 0x06, + 0x01, 0x02, 0x01, 0x00, 0x33, 0x00, 0x26, 0x00, + 0x24, 0x00, 0x1d, 0x00, 0x20, 0x35, 0x80, 0x72, + 0xd6, 0x36, 0x58, 0x80, 0xd1, 0xae, 0xea, 0x32, + 0x9a, 0xdf, 0x91, 0x21, 0x38, 0x38, 0x51, 0xed, + 0x21, 0xa2, 0x8e, 0x3b, 0x75, 0xe9, 0x65, 0xd0, + 0xd2, 0xcd, 0x16, 0x62, 0x54, 0x00, 0x2d, 0x00, + 0x02, 0x01, 0x01, 0x00, 0x2b, 0x00, 0x03, 0x02, + 0x03, 0x04, + /* Server Hello */ + 0x02, 0x00, 0x00, 0x76, 0x03, 0x03, 0x70, 0x71, + 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, + 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x20, 0xe0, + 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, + 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x13, + 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x24, + 0x00, 0x1d, 0x00, 0x20, 0x9f, 0xd7, 0xad, 0x6d, + 0xcf, 0xf4, 0x29, 0x8d, 0xd3, 0xf9, 0x6d, 0x5b, + 0x1b, 0x2a, 0xf9, 0x10, 0xa0, 0x53, 0x5b, 0x14, + 0x88, 0xd7, 0xf8, 0xfa, 0xbb, 0x34, 0x9a, 0x98, + 0x28, 0x80, 0xb6, 0x15, 0x00, 0x2b, 0x00, 0x02, + 0x03, 0x04, + /* Server Encrypted Extension */ + 0x08, 0x00, 0x00, 0x02, 0x00, 0x00, + /* Server Certificate */ + 0x0b, 0x00, 0x03, 0x2e, 0x00, 0x00, 0x03, 0x2a, + 0x00, 0x03, 0x25, 0x30, 0x82, 0x03, 0x21, 0x30, + 0x82, 0x02, 0x09, 0xa0, 0x03, 0x02, 0x01, 0x02, + 0x02, 0x08, 0x15, 0x5a, 0x92, 0xad, 0xc2, 0x04, + 0x8f, 0x90, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, + 0x00, 0x30, 0x22, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0a, 0x45, 0x78, 0x61, 0x6d, 0x70, + 0x6c, 0x65, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, + 0x0d, 0x31, 0x38, 0x31, 0x30, 0x30, 0x35, 0x30, + 0x31, 0x33, 0x38, 0x31, 0x37, 0x5a, 0x17, 0x0d, + 0x31, 0x39, 0x31, 0x30, 0x30, 0x35, 0x30, 0x31, + 0x33, 0x38, 0x31, 0x37, 0x5a, 0x30, 0x2b, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x1c, 0x30, 0x1a, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x13, 0x65, + 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x75, + 0x6c, 0x66, 0x68, 0x65, 0x69, 0x6d, 0x2e, 0x6e, + 0x65, 0x74, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, + 0x01, 0x01, 0x00, 0xc4, 0x80, 0x36, 0x06, 0xba, + 0xe7, 0x47, 0x6b, 0x08, 0x94, 0x04, 0xec, 0xa7, + 0xb6, 0x91, 0x04, 0x3f, 0xf7, 0x92, 0xbc, 0x19, + 0xee, 0xfb, 0x7d, 0x74, 0xd7, 0xa8, 0x0d, 0x00, + 0x1e, 0x7b, 0x4b, 0x3a, 0x4a, 0xe6, 0x0f, 0xe8, + 0xc0, 0x71, 0xfc, 0x73, 0xe7, 0x02, 0x4c, 0x0d, + 0xbc, 0xf4, 0xbd, 0xd1, 0x1d, 0x39, 0x6b, 0xba, + 0x70, 0x46, 0x4a, 0x13, 0xe9, 0x4a, 0xf8, 0x3d, + 0xf3, 0xe1, 0x09, 0x59, 0x54, 0x7b, 0xc9, 0x55, + 0xfb, 0x41, 0x2d, 0xa3, 0x76, 0x52, 0x11, 0xe1, + 0xf3, 0xdc, 0x77, 0x6c, 0xaa, 0x53, 0x37, 0x6e, + 0xca, 0x3a, 0xec, 0xbe, 0xc3, 0xaa, 0xb7, 0x3b, + 0x31, 0xd5, 0x6c, 0xb6, 0x52, 0x9c, 0x80, 0x98, + 0xbc, 0xc9, 0xe0, 0x28, 0x18, 0xe2, 0x0b, 0xf7, + 0xf8, 0xa0, 0x3a, 0xfd, 0x17, 0x04, 0x50, 0x9e, + 0xce, 0x79, 0xbd, 0x9f, 0x39, 0xf1, 0xea, 0x69, + 0xec, 0x47, 0x97, 0x2e, 0x83, 0x0f, 0xb5, 0xca, + 0x95, 0xde, 0x95, 0xa1, 0xe6, 0x04, 0x22, 0xd5, + 0xee, 0xbe, 0x52, 0x79, 0x54, 0xa1, 0xe7, 0xbf, + 0x8a, 0x86, 0xf6, 0x46, 0x6d, 0x0d, 0x9f, 0x16, + 0x95, 0x1a, 0x4c, 0xf7, 0xa0, 0x46, 0x92, 0x59, + 0x5c, 0x13, 0x52, 0xf2, 0x54, 0x9e, 0x5a, 0xfb, + 0x4e, 0xbf, 0xd7, 0x7a, 0x37, 0x95, 0x01, 0x44, + 0xe4, 0xc0, 0x26, 0x87, 0x4c, 0x65, 0x3e, 0x40, + 0x7d, 0x7d, 0x23, 0x07, 0x44, 0x01, 0xf4, 0x84, + 0xff, 0xd0, 0x8f, 0x7a, 0x1f, 0xa0, 0x52, 0x10, + 0xd1, 0xf4, 0xf0, 0xd5, 0xce, 0x79, 0x70, 0x29, + 0x32, 0xe2, 0xca, 0xbe, 0x70, 0x1f, 0xdf, 0xad, + 0x6b, 0x4b, 0xb7, 0x11, 0x01, 0xf4, 0x4b, 0xad, + 0x66, 0x6a, 0x11, 0x13, 0x0f, 0xe2, 0xee, 0x82, + 0x9e, 0x4d, 0x02, 0x9d, 0xc9, 0x1c, 0xdd, 0x67, + 0x16, 0xdb, 0xb9, 0x06, 0x18, 0x86, 0xed, 0xc1, + 0xba, 0x94, 0x21, 0x02, 0x03, 0x01, 0x00, 0x01, + 0xa3, 0x52, 0x30, 0x50, 0x30, 0x0e, 0x06, 0x03, + 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, + 0x03, 0x02, 0x05, 0xa0, 0x30, 0x1d, 0x06, 0x03, + 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, + 0x02, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x03, 0x01, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, + 0x89, 0x4f, 0xde, 0x5b, 0xcc, 0x69, 0xe2, 0x52, + 0xcf, 0x3e, 0xa3, 0x00, 0xdf, 0xb1, 0x97, 0xb8, + 0x1d, 0xe1, 0xc1, 0x46, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, + 0x59, 0x16, 0x45, 0xa6, 0x9a, 0x2e, 0x37, 0x79, + 0xe4, 0xf6, 0xdd, 0x27, 0x1a, 0xba, 0x1c, 0x0b, + 0xfd, 0x6c, 0xd7, 0x55, 0x99, 0xb5, 0xe7, 0xc3, + 0x6e, 0x53, 0x3e, 0xff, 0x36, 0x59, 0x08, 0x43, + 0x24, 0xc9, 0xe7, 0xa5, 0x04, 0x07, 0x9d, 0x39, + 0xe0, 0xd4, 0x29, 0x87, 0xff, 0xe3, 0xeb, 0xdd, + 0x09, 0xc1, 0xcf, 0x1d, 0x91, 0x44, 0x55, 0x87, + 0x0b, 0x57, 0x1d, 0xd1, 0x9b, 0xdf, 0x1d, 0x24, + 0xf8, 0xbb, 0x9a, 0x11, 0xfe, 0x80, 0xfd, 0x59, + 0x2b, 0xa0, 0x39, 0x8c, 0xde, 0x11, 0xe2, 0x65, + 0x1e, 0x61, 0x8c, 0xe5, 0x98, 0xfa, 0x96, 0xe5, + 0x37, 0x2e, 0xef, 0x3d, 0x24, 0x8a, 0xfd, 0xe1, + 0x74, 0x63, 0xeb, 0xbf, 0xab, 0xb8, 0xe4, 0xd1, + 0xab, 0x50, 0x2a, 0x54, 0xec, 0x00, 0x64, 0xe9, + 0x2f, 0x78, 0x19, 0x66, 0x0d, 0x3f, 0x27, 0xcf, + 0x20, 0x9e, 0x66, 0x7f, 0xce, 0x5a, 0xe2, 0xe4, + 0xac, 0x99, 0xc7, 0xc9, 0x38, 0x18, 0xf8, 0xb2, + 0x51, 0x07, 0x22, 0xdf, 0xed, 0x97, 0xf3, 0x2e, + 0x3e, 0x93, 0x49, 0xd4, 0xc6, 0x6c, 0x9e, 0xa6, + 0x39, 0x6d, 0x74, 0x44, 0x62, 0xa0, 0x6b, 0x42, + 0xc6, 0xd5, 0xba, 0x68, 0x8e, 0xac, 0x3a, 0x01, + 0x7b, 0xdd, 0xfc, 0x8e, 0x2c, 0xfc, 0xad, 0x27, + 0xcb, 0x69, 0xd3, 0xcc, 0xdc, 0xa2, 0x80, 0x41, + 0x44, 0x65, 0xd3, 0xae, 0x34, 0x8c, 0xe0, 0xf3, + 0x4a, 0xb2, 0xfb, 0x9c, 0x61, 0x83, 0x71, 0x31, + 0x2b, 0x19, 0x10, 0x41, 0x64, 0x1c, 0x23, 0x7f, + 0x11, 0xa5, 0xd6, 0x5c, 0x84, 0x4f, 0x04, 0x04, + 0x84, 0x99, 0x38, 0x71, 0x2b, 0x95, 0x9e, 0xd6, + 0x85, 0xbc, 0x5c, 0x5d, 0xd6, 0x45, 0xed, 0x19, + 0x90, 0x94, 0x73, 0x40, 0x29, 0x26, 0xdc, 0xb4, + 0x0e, 0x34, 0x69, 0xa1, 0x59, 0x41, 0xe8, 0xe2, + 0xcc, 0xa8, 0x4b, 0xb6, 0x08, 0x46, 0x36, 0xa0, + 0x00, 0x00, + /* Server Certificate Verify */ + 0x0f, 0x00, 0x01, 0x04, 0x08, 0x04, 0x01, 0x00, + 0x17, 0xfe, 0xb5, 0x33, 0xca, 0x6d, 0x00, 0x7d, + 0x00, 0x58, 0x25, 0x79, 0x68, 0x42, 0x4b, 0xbc, + 0x3a, 0xa6, 0x90, 0x9e, 0x9d, 0x49, 0x55, 0x75, + 0x76, 0xa5, 0x20, 0xe0, 0x4a, 0x5e, 0xf0, 0x5f, + 0x0e, 0x86, 0xd2, 0x4f, 0xf4, 0x3f, 0x8e, 0xb8, + 0x61, 0xee, 0xf5, 0x95, 0x22, 0x8d, 0x70, 0x32, + 0xaa, 0x36, 0x0f, 0x71, 0x4e, 0x66, 0x74, 0x13, + 0x92, 0x6e, 0xf4, 0xf8, 0xb5, 0x80, 0x3b, 0x69, + 0xe3, 0x55, 0x19, 0xe3, 0xb2, 0x3f, 0x43, 0x73, + 0xdf, 0xac, 0x67, 0x87, 0x06, 0x6d, 0xcb, 0x47, + 0x56, 0xb5, 0x45, 0x60, 0xe0, 0x88, 0x6e, 0x9b, + 0x96, 0x2c, 0x4a, 0xd2, 0x8d, 0xab, 0x26, 0xba, + 0xd1, 0xab, 0xc2, 0x59, 0x16, 0xb0, 0x9a, 0xf2, + 0x86, 0x53, 0x7f, 0x68, 0x4f, 0x80, 0x8a, 0xef, + 0xee, 0x73, 0x04, 0x6c, 0xb7, 0xdf, 0x0a, 0x84, + 0xfb, 0xb5, 0x96, 0x7a, 0xca, 0x13, 0x1f, 0x4b, + 0x1c, 0xf3, 0x89, 0x79, 0x94, 0x03, 0xa3, 0x0c, + 0x02, 0xd2, 0x9c, 0xbd, 0xad, 0xb7, 0x25, 0x12, + 0xdb, 0x9c, 0xec, 0x2e, 0x5e, 0x1d, 0x00, 0xe5, + 0x0c, 0xaf, 0xcf, 0x6f, 0x21, 0x09, 0x1e, 0xbc, + 0x4f, 0x25, 0x3c, 0x5e, 0xab, 0x01, 0xa6, 0x79, + 0xba, 0xea, 0xbe, 0xed, 0xb9, 0xc9, 0x61, 0x8f, + 0x66, 0x00, 0x6b, 0x82, 0x44, 0xd6, 0x62, 0x2a, + 0xaa, 0x56, 0x88, 0x7c, 0xcf, 0xc6, 0x6a, 0x0f, + 0x38, 0x51, 0xdf, 0xa1, 0x3a, 0x78, 0xcf, 0xf7, + 0x99, 0x1e, 0x03, 0xcb, 0x2c, 0x3a, 0x0e, 0xd8, + 0x7d, 0x73, 0x67, 0x36, 0x2e, 0xb7, 0x80, 0x5b, + 0x00, 0xb2, 0x52, 0x4f, 0xf2, 0x98, 0xa4, 0xda, + 0x48, 0x7c, 0xac, 0xde, 0xaf, 0x8a, 0x23, 0x36, + 0xc5, 0x63, 0x1b, 0x3e, 0xfa, 0x93, 0x5b, 0xb4, + 0x11, 0xe7, 0x53, 0xca, 0x13, 0xb0, 0x15, 0xfe, + 0xc7, 0xe4, 0xa7, 0x30, 0xf1, 0x36, 0x9f, 0x9e, + /* Server Handshake Finish */ + 0x14, 0x00, 0x00, 0x20, 0xea, 0x6e, 0xe1, 0x76, + 0xdc, 0xcc, 0x4a, 0xf1, 0x85, 0x9e, 0x9e, 0x4e, + 0x93, 0xf7, 0x97, 0xea, 0xc9, 0xa7, 0x8c, 0xe4, + 0x39, 0x30, 0x1e, 0x35, 0x27, 0x5a, 0xd4, 0x3f, + 0x3c, 0xdd, 0xbd, 0xe3, + ); + + chunk_t ecdhe = chunk_from_chars( + 0xdf, 0x4a, 0x29, 0x1b, 0xaa, 0x1e, 0xb7, 0xcf, + 0xa6, 0x93, 0x4b, 0x29, 0xb4, 0x74, 0xba, 0xad, + 0x26, 0x97, 0xe2, 0x9f, 0x1f, 0x92, 0x0d, 0xcc, + 0x77, 0xc8, 0xa0, 0xa0, 0x88, 0x44, 0x76, 0x24, + ); + + chunk_t expected_client_application_key = chunk_from_chars( + 0x49, 0x13, 0x4b, 0x95, 0x32, 0x8f, 0x27, 0x9f, + 0x01, 0x83, 0x86, 0x05, 0x89, 0xac, 0x67, 0x07, + ); + + chunk_t expected_client_application_iv = chunk_from_chars( + 0xbc, 0x4d, 0xd5, 0xf7, 0xb9, 0x8a, 0xcf, 0xf8, + 0x54, 0x66, 0x26, 0x1d, + ); + + chunk_t expected_server_application_key = chunk_from_chars( + 0x0b, 0x6d, 0x22, 0xc8, 0xff, 0x68, 0x09, 0x7e, + 0xa8, 0x71, 0xc6, 0x72, 0x07, 0x37, 0x73, 0xbf, + ); + + chunk_t expected_server_application_iv = chunk_from_chars( + 0x1b, 0x13, 0xdd, 0x9f, 0x8d, 0x8f, 0x17, 0x09, + 0x1d, 0x34, 0xb3, 0x49, + ); + + chunk_t c_key, c_iv, s_key, s_iv; + + tls_hkdf_t *hkdf = tls_hkdf_create(HASH_SHA256, chunk_empty); + hkdf->set_shared_secret(hkdf, ecdhe); + + /* Generate client application traffic secret */ + ck_assert(hkdf->generate_secret(hkdf, TLS_HKDF_C_AP_TRAFFIC, handshake, NULL)); + + ck_assert(hkdf->derive_key(hkdf, FALSE, 16, &c_key)); + ck_assert_chunk_eq(expected_client_application_key, c_key); + + ck_assert(hkdf->derive_iv(hkdf, FALSE, 12, &c_iv)); + ck_assert_chunk_eq(expected_client_application_iv, c_iv); + + /* Generate server application traffic secret */ + ck_assert(hkdf->generate_secret(hkdf, TLS_HKDF_S_AP_TRAFFIC, handshake, NULL)); + + ck_assert(hkdf->derive_key(hkdf, TRUE, 16, &s_key)); + ck_assert_chunk_eq(expected_server_application_key, s_key); + + ck_assert(hkdf->derive_iv(hkdf, TRUE, 12, &s_iv)); + ck_assert_chunk_eq(expected_server_application_iv, s_iv); + + hkdf->destroy(hkdf); + + chunk_free(&c_key); + chunk_free(&c_iv); + chunk_free(&s_key); + chunk_free(&s_iv); +} +END_TEST + +Suite *hkdf_suite_create() +{ + Suite *s; + TCase *tc; + + s = suite_create("HKDF TLS 1.3"); + + tc = tcase_create("Ulfheim Keys"); + tcase_add_test(tc, test_ulfheim_handshake); + tcase_add_test(tc, test_ulfheim_traffic); + suite_add_tcase(s, tc); + + return s; +} diff --git a/src/libtls/tests/tls_tests.h b/src/libtls/tests/tls_tests.h index 489b2ddb1..b0d0d57a5 100644 --- a/src/libtls/tests/tls_tests.h +++ b/src/libtls/tests/tls_tests.h @@ -13,5 +13,6 @@ * for more details. */ +TEST_SUITE(hkdf_suite_create) TEST_SUITE(socket_suite_create) TEST_SUITE(suites_suite_create) diff --git a/src/libtls/tls_hkdf.c b/src/libtls/tls_hkdf.c new file mode 100644 index 000000000..d0c44f558 --- /dev/null +++ b/src/libtls/tls_hkdf.c @@ -0,0 +1,511 @@ +/* + * Copyright (C) 2020 Pascal Knecht + * Copyright (C) 2020 Méline Sieber + * HSR Hochschule fuer Technik Rapperswil + * + * 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_hkdf.h" + +#include +#include + +typedef struct private_tls_hkdf_t private_tls_hkdf_t; + +typedef enum hkdf_phase { + HKDF_PHASE_0, + HKDF_PHASE_1, + HKDF_PHASE_2, + HKDF_PHASE_3, +} hkdf_phase; + +struct private_tls_hkdf_t { + + /** + * Public tls_hkdf_t interface. + */ + struct tls_hkdf_t public; + + /** + * Phase we are in. + */ + hkdf_phase phase; + + /** + * Hash algorithm used. + */ + hash_algorithm_t hash_algorithm; + + /** + * Pseudorandom function used. + */ + prf_t *prf; + + /** + * Hasher used. + */ + hasher_t *hasher; + + /** + * (EC)DHE as IKM to switch from phase 1 to phase 2 + */ + chunk_t shared_secret; + + /** + * IKM used. + */ + chunk_t ikm; + + /** + * PRK used. + */ + chunk_t prk; + + /** + * OKM used. + */ + chunk_t okm; + + /** + * Current implementation needs a copy of derived secrets to calculate the + * proper finished key. + */ + chunk_t client_traffic_secret; + chunk_t server_traffic_secret; +}; + +static char *hkdf_labels[] = { + "tls13 ext binder", + "tls13 res binder", + "tls13 c e traffic", + "tls13 e exp master", + "tls13 c hs traffic", + "tls13 s hs traffic", + "tls13 c ap traffic", + "tls13 s ap traffic", + "tls13 exp master", + "tls13 res master", +}; + +/** + * Step 1: Extract, as defined in RFC 5869, section 2.2: + * HKDF-Extract(salt, IKM) -> PRK + */ +static bool extract(private_tls_hkdf_t *this, chunk_t salt, chunk_t ikm, + chunk_t *prk) +{ + if (!this->prf->set_key(this->prf, salt)) + { + DBG1(DBG_TLS, "unable to set PRF salt"); + return FALSE; + } + chunk_clear(prk); + if(!this->prf->allocate_bytes(this->prf, ikm, prk)) + { + DBG1(DBG_TLS, "unable to allocate PRF space"); + return FALSE; + } + + DBG4(DBG_TLS, "PRK: %B", prk); + + return TRUE; +} + +/** + * Step 2: Expand as defined in RFC 5869, section 2.3: + * HKDF-Expand(PRK, info, L) -> OKM + */ +static bool expand(private_tls_hkdf_t *this, chunk_t prk, chunk_t info, + size_t length, chunk_t *okm) +{ + if (!this->prf->set_key(this->prf, prk)) + { + DBG1(DBG_TLS, "unable to set PRF PRK"); + return FALSE; + } + prf_plus_t *prf_plus = prf_plus_create(this->prf, TRUE, info); + chunk_clear(&this->okm); + if (!prf_plus || !prf_plus->allocate_bytes(prf_plus, length, okm)) + { + DBG1(DBG_TLS, "unable to allocate PRF plus space"); + DESTROY_IF(prf_plus); + chunk_clear(okm); + return FALSE; + } + DESTROY_IF(prf_plus); + + DBG4(DBG_TLS, "OKM: %B", okm); + + return TRUE; +} + +/** + * Expand-Label as defined in RFC 8446, section 7.1: + * HKDF-Expand-Label(Secret, Label, Context, Length) -> OKM + */ +static bool expand_label(private_tls_hkdf_t *this, chunk_t secret, + chunk_t label, chunk_t context, uint16_t length, + chunk_t *key) +{ + bool success; + + if (label.len < 7 || label.len > 255 || context.len > 255) + { + return FALSE; + } + + /* HKDFLabel as defined in RFC 8446, section 7.1 */ + bio_writer_t *writer = bio_writer_create(0); + writer->write_uint16(writer, length); + writer->write_data8(writer, label); + writer->write_data8(writer, context); + + success = expand(this, secret, writer->get_buf(writer), length, key); + writer->destroy(writer); + return success; +} + +/** + * Derive-Secret as defined in RFC 8446, section 7.1: + * Derive-Secret(Secret, Label, Message) -> OKM + */ +static bool derive_secret(private_tls_hkdf_t *this, chunk_t label, + chunk_t messages) +{ + bool success; + chunk_t context; + + if (!this->hasher || + !this->hasher->allocate_hash(this->hasher, messages, &context)) + { + DBG1(DBG_TLS, "%N not supported", hash_algorithm_names, + this->hash_algorithm); + return FALSE; + } + + success = expand_label(this, this->prk, label, context, + this->hasher->get_hash_size(this->hasher), + &this->okm); + chunk_free(&context); + return success; +} + +static bool move_to_phase_1(private_tls_hkdf_t *this) +{ + chunk_t salt_zero; + + switch (this->phase) + { + case HKDF_PHASE_0: + salt_zero = chunk_alloca(this->hasher->get_hash_size(this->hasher)); + chunk_copy_pad(salt_zero, chunk_empty, 0); + if (!extract(this, salt_zero, this->ikm, &this->prk)) + { + DBG1(DBG_TLS, "unable to extract PRK"); + return FALSE; + } + this->phase = HKDF_PHASE_1; + return TRUE; + case HKDF_PHASE_1: + return TRUE; + default: + DBG1(DBG_TLS, "invalid HKDF phase"); + return FALSE; + } +} + +static bool move_to_phase_2(private_tls_hkdf_t *this) +{ + chunk_t derived; + + switch (this->phase) + { + case HKDF_PHASE_0: + if (!move_to_phase_1(this)) + { + DBG1(DBG_TLS, "unable to move to phase 1"); + return FALSE; + } + /* fall-through */ + case HKDF_PHASE_1: + derived = chunk_from_str("tls13 derived"); + if (!derive_secret(this, derived, chunk_empty)) + { + DBG1(DBG_TLS, "unable to derive secret"); + return FALSE; + } + + if (!this->shared_secret.ptr) + { + DBG1(DBG_TLS, "no shared secret set"); + return FALSE; + } + else + { + chunk_clear(&this->ikm); + this->ikm = chunk_clone(this->shared_secret); + } + + if (!extract(this, this->okm, this->ikm, &this->prk)) + { + DBG1(DBG_TLS, "unable extract PRK"); + return FALSE; + } + this->phase = HKDF_PHASE_2; + return TRUE; + case HKDF_PHASE_2: + return TRUE; + default: + DBG1(DBG_TLS, "invalid HKDF phase"); + return FALSE; + } +} + +static bool move_to_phase_3(private_tls_hkdf_t *this) +{ + chunk_t derived, ikm_zero; + + switch (this->phase) + { + case HKDF_PHASE_0: + case HKDF_PHASE_1: + if (!move_to_phase_2(this)) + { + DBG1(DBG_TLS, "unable to move to phase 2"); + return FALSE; + } + /* fall-through */ + case HKDF_PHASE_2: + /* prepare okm for next extract */ + derived = chunk_from_str("tls13 derived"); + if (!derive_secret(this, derived, chunk_empty)) + { + DBG1(DBG_TLS, "unable to derive secret"); + return FALSE; + } + + ikm_zero = chunk_alloca(this->hasher->get_hash_size(this->hasher)); + chunk_copy_pad(ikm_zero, chunk_empty, 0); + if (!extract(this, this->okm, ikm_zero, &this->prk)) + { + DBG1(DBG_TLS, "unable extract PRK"); + return FALSE; + } + this->phase = HKDF_PHASE_3; + return TRUE; + case HKDF_PHASE_3: + return TRUE; + default: + DBG1(DBG_TLS, "invalid HKDF phase"); + return FALSE; + } +} + +static void return_secret(private_tls_hkdf_t *this, chunk_t key, + chunk_t *secret) +{ + *secret = chunk_alloc(key.len); + chunk_copy_pad(*secret, key, 0); +} + +static bool get_shared_label_keys(private_tls_hkdf_t *this, chunk_t label, + bool is_server, size_t length, chunk_t *key) +{ + chunk_t result, secret; + + if (is_server) + { + secret = chunk_clone(this->server_traffic_secret); + } + else + { + secret = chunk_clone(this->client_traffic_secret); + } + + if (!expand_label(this, secret, label, chunk_empty, length, &result)) + { + DBG1(DBG_TLS, "unable to derive secret"); + chunk_clear(&secret); + chunk_clear(&result); + return FALSE; + } + + if (key) + { + return_secret(this, result, key); + } + + chunk_clear(&secret); + chunk_clear(&result); + return TRUE; +} + +METHOD(tls_hkdf_t, set_shared_secret, void, + private_tls_hkdf_t *this, chunk_t shared_secret) +{ + this->shared_secret = chunk_clone(shared_secret); +} + +METHOD(tls_hkdf_t, generate_secret, bool, + private_tls_hkdf_t *this, enum tls_hkdf_labels_t label, chunk_t messages, + chunk_t *secret) +{ + switch (label) + { + case TLS_HKDF_EXT_BINDER: + case TLS_HKDF_RES_BINDER: + case TLS_HKDF_C_E_TRAFFIC: + case TLS_HKDF_E_EXP_MASTER: + if (!move_to_phase_1(this)) + { + DBG1(DBG_TLS, "unable to move to phase 1"); + return FALSE; + } + break; + case TLS_HKDF_C_HS_TRAFFIC: + case TLS_HKDF_S_HS_TRAFFIC: + if (!move_to_phase_2(this)) + { + DBG1(DBG_TLS, "unable to move to phase 2"); + return FALSE; + } + break; + case TLS_HKDF_C_AP_TRAFFIC: + case TLS_HKDF_S_AP_TRAFFIC: + case TLS_HKDF_EXP_MASTER: + case TLS_HKDF_RES_MASTER: + if (!move_to_phase_3(this)) + { + DBG1(DBG_TLS, "unable to move to phase 3"); + return FALSE; + } + break; + default: + DBG1(DBG_TLS, "invalid HKDF label"); + return FALSE; + } + + if (!derive_secret(this, chunk_from_str(hkdf_labels[label]), messages)) + { + DBG1(DBG_TLS, "unable to derive secret"); + return FALSE; + } + + if (label == TLS_HKDF_C_HS_TRAFFIC || label == TLS_HKDF_C_AP_TRAFFIC) + { + chunk_clear(&this->client_traffic_secret); + this->client_traffic_secret = chunk_clone(this->okm); + } + + if (label == TLS_HKDF_S_HS_TRAFFIC || label == TLS_HKDF_S_AP_TRAFFIC) + { + chunk_clear(&this->server_traffic_secret); + this->server_traffic_secret = chunk_clone(this->okm); + } + + if (secret) + { + return_secret(this, this->okm, secret); + } + + return TRUE; +} + +METHOD(tls_hkdf_t, derive_key, bool, + private_tls_hkdf_t *this, bool is_server, size_t length, chunk_t *key) +{ + return get_shared_label_keys(this, chunk_from_str("tls13 key"), is_server, + length, key); +} + +METHOD(tls_hkdf_t, derive_iv, bool, + private_tls_hkdf_t *this, bool is_server, size_t length, chunk_t *iv) +{ + return get_shared_label_keys(this, chunk_from_str("tls13 iv"), is_server, + length, iv); +} + +METHOD(tls_hkdf_t, derive_finished, bool, + private_tls_hkdf_t *this, bool is_server, chunk_t *finished) +{ + return get_shared_label_keys(this, chunk_from_str("tls13 finished"), + is_server, + this->hasher->get_hash_size(this->hasher), + finished); +} + +METHOD(tls_hkdf_t, destroy, void, + private_tls_hkdf_t *this) +{ + chunk_free(&this->ikm); + chunk_clear(&this->prk); + chunk_clear(&this->shared_secret); + chunk_clear(&this->okm); + chunk_clear(&this->client_traffic_secret); + chunk_clear(&this->server_traffic_secret); + DESTROY_IF(this->prf); + DESTROY_IF(this->hasher); + free(this); +} + +tls_hkdf_t *tls_hkdf_create(hash_algorithm_t hash_algorithm, chunk_t psk) +{ + private_tls_hkdf_t *this; + pseudo_random_function_t prf_algorithm; + + switch (hash_algorithm) + { + case HASH_SHA256: + prf_algorithm = PRF_HMAC_SHA2_256; + break; + case HASH_SHA384: + prf_algorithm = PRF_HMAC_SHA2_384; + break; + default: + DBG1(DBG_TLS, "not supported hash algorithm"); + return NULL; + } + + INIT(this, + .public = { + .set_shared_secret = _set_shared_secret, + .generate_secret = _generate_secret, + .derive_key = _derive_key, + .derive_iv = _derive_iv, + .derive_finished = _derive_finished, + .destroy = _destroy, + }, + .phase = HKDF_PHASE_0, + .hash_algorithm = hash_algorithm, + .prf = lib->crypto->create_prf(lib->crypto, prf_algorithm), + .hasher = lib->crypto->create_hasher(lib->crypto, hash_algorithm), + ); + + if (!psk.ptr) + { + this->ikm = chunk_alloc(this->hasher->get_hash_size(this->hasher)); + chunk_copy_pad(this->ikm, chunk_empty, 0); + } + else + { + this->ikm = chunk_clone(psk); + } + + if (!this->prf || !this->hasher) + { + DBG1(DBG_TLS, "unable to initialise HKDF"); + destroy(this); + return NULL; + } + + return &this->public; +} diff --git a/src/libtls/tls_hkdf.h b/src/libtls/tls_hkdf.h new file mode 100644 index 000000000..a820b0298 --- /dev/null +++ b/src/libtls/tls_hkdf.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2020 Pascal Knecht + * Copyright (C) 2020 Méline Sieber + * HSR Hochschule fuer Technik Rapperswil + * + * 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. + */ + +/** + * @defgroup tls_hkdf tls_hkdf + * @{ @ingroup libtls + */ + +#ifndef TLS_HKDF_H_ +#define TLS_HKDF_H_ + +#include +#include + +typedef struct tls_hkdf_t tls_hkdf_t; + +/** + * TLS HKDF labels + */ +enum tls_hkdf_labels_t { + TLS_HKDF_EXT_BINDER, + TLS_HKDF_RES_BINDER, + TLS_HKDF_C_E_TRAFFIC, + TLS_HKDF_E_EXP_MASTER, + TLS_HKDF_C_HS_TRAFFIC, + TLS_HKDF_S_HS_TRAFFIC, + TLS_HKDF_C_AP_TRAFFIC, + TLS_HKDF_S_AP_TRAFFIC, + TLS_HKDF_EXP_MASTER, + TLS_HKDF_RES_MASTER, +}; + +/** + * TLS HKDF helper functions. + */ +struct tls_hkdf_t { + + /** + * Set the (EC)DHE shared secret of this connection. + * + * @param shared_secret input key material to use + */ + void (*set_shared_secret)(tls_hkdf_t *this, chunk_t shared_secret); + + /** + * Allocate secret of the requested label. + * + * Space for returned secret is allocated and must be freed by the caller. + * + * @param label HKDF label of requested secret + * @param messages handshake messages + * @param secret secret will be written into this chunk, if used + * @return TRUE if secrets derived successfully + */ + bool (*generate_secret)(tls_hkdf_t *this, enum tls_hkdf_labels_t label, + chunk_t messages, chunk_t *secret); + + /** + * Allocate traffic encryption key bytes. + * + * Key used to encrypt traffic data as defined in RFC 8446, section 7.3. + * Space for returned secret is allocated and must be freed by the caller. + * + * @param is_server TRUE if server, FALSE if client derives secret + * @param length key length, in bytes + * @param key secret will be written into this chunk + * @return TRUE if secrets derived successfully + */ + bool (*derive_key)(tls_hkdf_t *this, bool is_server, size_t length, + chunk_t *key); + + /** + * Allocate traffic IV bytes. + * + * IV used to encrypt traffic data as defined in RFC 8446, section 7.3. + * Space for returned secret is allocated and must be freed by the caller. + * + * @param is_server TRUE if server, FALSE if client derives secret + * @param length key length, in bytes + * @param iv IV will be written into this chunk + * @return TRUE if secrets derived successfully + */ + bool (*derive_iv)(tls_hkdf_t *this, bool is_server, size_t length, + chunk_t *iv); + + /** + * Allocate finished key bytes. + * + * Key used to compute Finished messages as defined in RFC 8446, + * section 4.4.4. Space for returned secret is allocated and must be freed + * by the caller. + * + * @param is_server TRUE if server, FALSE if client derives secret + * @param finished key will be written into this chunk + * @return TRUE if secrets derived successfully + */ + bool (*derive_finished)(tls_hkdf_t *this, bool is_server, + chunk_t *finished); + + /** + * Destroy a tls_hkdf_t + */ + void (*destroy)(tls_hkdf_t *this); +}; + +/** + * Create a tls_hkdf instance. + * + * @param hash_algorithm hash algorithm to use + * @param psk Pre shared key if available otherwise NULL + * @return TLS HKDF helper + */ +tls_hkdf_t *tls_hkdf_create(hash_algorithm_t hash_algorithm, chunk_t psk); + +#endif /** TLS_HKDF_H_ @}*/ From 7a2b02667c30cb3d3dbffc57ebca6466608492cd Mon Sep 17 00:00:00 2001 From: bytinbit Date: Thu, 21 May 2020 09:37:38 +0200 Subject: [PATCH 005/107] libtls: Implement TLS 1.3 handshake on client-side The code is a minimal handshake with the HelloRetryRequest message implementation missing. Can be tested with an OpenSSL server running TLS 1.3. The server must be at least version 1.1.1 (September 2018). Co-authored-by: ryru --- scripts/tls_test.c | 31 +- src/libtls/tls.c | 114 +++-- src/libtls/tls.h | 60 ++- src/libtls/tls_alert.c | 21 +- src/libtls/tls_alert.h | 9 + src/libtls/tls_crypto.c | 593 +++++++++++++++++++++++--- src/libtls/tls_crypto.h | 51 ++- src/libtls/tls_fragmentation.c | 9 +- src/libtls/tls_peer.c | 738 ++++++++++++++++++++++++++------- src/libtls/tls_protection.c | 29 +- src/libtls/tls_server.c | 26 +- 11 files changed, 1398 insertions(+), 283 deletions(-) diff --git a/scripts/tls_test.c b/scripts/tls_test.c index 84a32f96f..316c98625 100644 --- a/scripts/tls_test.c +++ b/scripts/tls_test.c @@ -92,6 +92,7 @@ static int run_client(host_t *host, identification_t *server, while (times == -1 || times-- > 0) { + /* Open IPv4 socket */ fd = socket(AF_INET, SOCK_STREAM, 0); if (fd == -1) { @@ -101,11 +102,21 @@ static int run_client(host_t *host, identification_t *server, if (connect(fd, host->get_sockaddr(host), *host->get_sockaddr_len(host)) == -1) { - DBG1(DBG_TLS, "connecting to %#H failed: %s", host, strerror(errno)); - close(fd); - return 1; + /* Check if the socket should use IPv6 instead */ + fd = socket(AF_INET6, SOCK_STREAM, 0); + { + if (connect(fd, host->get_sockaddr(host), + *host->get_sockaddr_len(host)) == -1) + { + DBG1(DBG_TLS, "connecting to %#H failed: %s", host, strerror(errno)); + close(fd); + return 1; + } + } + } - tls = tls_socket_create(FALSE, server, client, fd, cache, TLS_1_2, TRUE); + tls = tls_socket_create(FALSE, server, client, fd, cache, + TLS_1_3, TRUE); if (!tls) { close(fd); @@ -337,11 +348,17 @@ int main(int argc, char *argv[]) usage(stderr, argv[0]); return 1; } - host = host_create_from_dns(address, 0, port); + /* Try to connect with IPv4 first*/ + host = host_create_from_dns(address, AF_INET, port); if (!host) { - DBG1(DBG_TLS, "resolving hostname %s failed", address); - return 1; + /* If it fails, try IPv6*/ + host = host_create_from_dns(address, AF_INET6, port); + if (!host) + { + DBG1(DBG_TLS, "resolving hostname %s failed", address); + return 1; + } } server = identification_create_from_string(address); cache = tls_cache_create(100, 30); diff --git a/src/libtls/tls.c b/src/libtls/tls.c index ea39f7fd1..4a461c2cc 100644 --- a/src/libtls/tls.c +++ b/src/libtls/tls.c @@ -26,12 +26,13 @@ ENUM_BEGIN(tls_version_names, SSL_2_0, SSL_2_0, "SSLv2"); -ENUM_NEXT(tls_version_names, SSL_3_0, TLS_1_2, SSL_2_0, +ENUM_NEXT(tls_version_names, SSL_3_0, TLS_1_3, SSL_2_0, "SSLv3", "TLS 1.0", "TLS 1.1", - "TLS 1.2"); -ENUM_END(tls_version_names, TLS_1_2); + "TLS 1.2", + "TLS 1.3"); +ENUM_END(tls_version_names, TLS_1_3); ENUM(tls_content_type_names, TLS_CHANGE_CIPHER_SPEC, TLS_APPLICATION_DATA, "ChangeCipherSpec", @@ -40,12 +41,22 @@ ENUM(tls_content_type_names, TLS_CHANGE_CIPHER_SPEC, TLS_APPLICATION_DATA, "ApplicationData", ); -ENUM_BEGIN(tls_handshake_type_names, TLS_HELLO_REQUEST, TLS_SERVER_HELLO, - "HelloRequest", - "ClientHello", - "ServerHello"); +ENUM_BEGIN(tls_handshake_type_names, TLS_HELLO_REQUEST, TLS_HELLO_REQUEST, + "HelloRequest"); ENUM_NEXT(tls_handshake_type_names, - TLS_CERTIFICATE, TLS_CLIENT_KEY_EXCHANGE, TLS_SERVER_HELLO, + TLS_CLIENT_HELLO, TLS_HELLO_RETRY_REQUEST, TLS_HELLO_REQUEST, + "ClientHello", + "ServerHello", + "HelloVerifyRequest", + "NewSessionTicket", + "EndOfEarlyData", + "HelloRetryRequest"); +ENUM_NEXT(tls_handshake_type_names, + TLS_ENCRYPTED_EXTENSIONS, TLS_ENCRYPTED_EXTENSIONS, + TLS_HELLO_RETRY_REQUEST, + "EncryptedExtensions"); +ENUM_NEXT(tls_handshake_type_names, + TLS_CERTIFICATE, TLS_CLIENT_KEY_EXCHANGE, TLS_ENCRYPTED_EXTENSIONS, "Certificate", "ServerKeyExchange", "CertificateRequest", @@ -53,9 +64,16 @@ ENUM_NEXT(tls_handshake_type_names, "CertificateVerify", "ClientKeyExchange"); ENUM_NEXT(tls_handshake_type_names, - TLS_FINISHED, TLS_FINISHED, TLS_CLIENT_KEY_EXCHANGE, - "Finished"); -ENUM_END(tls_handshake_type_names, TLS_FINISHED); + TLS_FINISHED, TLS_KEY_UPDATE, TLS_CLIENT_KEY_EXCHANGE, + "Finished", + "CertificateUrl", + "CertificateStatus", + "SupplementalData", + "KeyUpdate"); +ENUM_NEXT(tls_handshake_type_names, + TLS_MESSAGE_HASH, TLS_MESSAGE_HASH, TLS_KEY_UPDATE, + "MessageHash"); +ENUM_END(tls_handshake_type_names, TLS_MESSAGE_HASH); ENUM_BEGIN(tls_extension_names, TLS_EXT_SERVER_NAME, TLS_EXT_STATUS_REQUEST, "server name", @@ -65,17 +83,42 @@ ENUM_BEGIN(tls_extension_names, TLS_EXT_SERVER_NAME, TLS_EXT_STATUS_REQUEST, "truncated hmac", "status request"); ENUM_NEXT(tls_extension_names, - TLS_EXT_ELLIPTIC_CURVES, TLS_EXT_EC_POINT_FORMATS, + TLS_EXT_SUPPORTED_GROUPS, TLS_EXT_EC_POINT_FORMATS, TLS_EXT_STATUS_REQUEST, - "elliptic curves", + "supported groups", "ec point formats"); ENUM_NEXT(tls_extension_names, - TLS_EXT_SIGNATURE_ALGORITHMS, TLS_EXT_SIGNATURE_ALGORITHMS, + TLS_EXT_SIGNATURE_ALGORITHMS, + TLS_EXT_APPLICATION_LAYER_PROTOCOL_NEGOTIATION, TLS_EXT_EC_POINT_FORMATS, - "signature algorithms"); + "signature algorithms", + "use rtp", + "heartbeat", + "application layer protocol negotiation"); +ENUM_NEXT(tls_extension_names, + TLS_CLIENT_CERTIFICATE_TYPE, TLS_SERVER_CERTIFICATE_TYPE, + TLS_EXT_APPLICATION_LAYER_PROTOCOL_NEGOTIATION, + "client certificate type", + "server certificate type"); +ENUM_NEXT(tls_extension_names, + TLS_EXT_PRE_SHARED_KEY, TLS_EXT_PSK_KEY_EXCHANGE_MODES, + TLS_SERVER_CERTIFICATE_TYPE, + "pre-shared key", + "early data", + "supported versions", + "cookie", + "psk key exchange modes"); +ENUM_NEXT(tls_extension_names, + TLS_EXT_CERTIFICATE_AUTHORITIES, TLS_EXT_KEY_SHARE, + TLS_EXT_PSK_KEY_EXCHANGE_MODES, + "certificate authorities", + "oid filters", + "post-handshake auth", + "signature algorithms cert", + "key-share"); ENUM_NEXT(tls_extension_names, TLS_EXT_RENEGOTIATION_INFO, TLS_EXT_RENEGOTIATION_INFO, - TLS_EXT_SIGNATURE_ALGORITHMS, + TLS_EXT_KEY_SHARE, "renegotiation info"); ENUM_END(tls_extension_names, TLS_EXT_RENEGOTIATION_INFO); @@ -107,9 +150,14 @@ struct private_tls_t { bool is_server; /** - * Negotiated TLS version + * Negotiated TLS version and maximum supported TLS version */ - tls_version_t version; + tls_version_t version_max; + + /** + * Minimal supported TLS version + */ + tls_version_t version_min; /** * TLS stack purpose, as given to constructor @@ -300,7 +348,14 @@ METHOD(tls_t, build, status_t, { case NEED_MORE: record.type = type; - htoun16(&record.version, this->version); + if (this->version_max < TLS_1_3) + { + htoun16(&record.version, this->version_max); + } + else + { + htoun16(&record.version, TLS_1_2); + } htoun16(&record.length, data.len); this->output = chunk_cat("mcm", this->output, chunk_from_thing(record), data); @@ -361,16 +416,22 @@ METHOD(tls_t, get_peer_id, identification_t*, return this->handshake->get_peer_id(this->handshake); } -METHOD(tls_t, get_version, tls_version_t, +METHOD(tls_t, get_version_max, tls_version_t, private_tls_t *this) { - return this->version; + return this->version_max; +} + +METHOD(tls_t, get_version_min, tls_version_t, + private_tls_t *this) +{ + return this->version_min; } METHOD(tls_t, set_version, bool, private_tls_t *this, tls_version_t version) { - if (version > this->version) + if (version > this->version_max) { return FALSE; } @@ -379,7 +440,8 @@ METHOD(tls_t, set_version, bool, case TLS_1_0: case TLS_1_1: case TLS_1_2: - this->version = version; + case TLS_1_3: + this->version_max = version; this->protection->set_version(this->protection, version); return TRUE; case SSL_2_0: @@ -466,7 +528,8 @@ tls_t *tls_create(bool is_server, identification_t *server, .is_server = _is_server, .get_server_id = _get_server_id, .get_peer_id = _get_peer_id, - .get_version = _get_version, + .get_version_max = _get_version_max, + .get_version_min = _get_version_min, .set_version = _set_version, .get_purpose = _get_purpose, .is_complete = _is_complete, @@ -475,7 +538,8 @@ tls_t *tls_create(bool is_server, identification_t *server, .destroy = _destroy, }, .is_server = is_server, - .version = TLS_1_2, + .version_max = TLS_1_3, + .version_min = TLS_1_0, .application = application, .purpose = purpose, ); diff --git a/src/libtls/tls.h b/src/libtls/tls.h index f832ef50e..4509e05a3 100644 --- a/src/libtls/tls.h +++ b/src/libtls/tls.h @@ -52,6 +52,7 @@ enum tls_version_t { TLS_1_0 = 0x0301, TLS_1_1 = 0x0302, TLS_1_2 = 0x0303, + TLS_1_3 = 0x0304, }; /** @@ -81,6 +82,11 @@ enum tls_handshake_type_t { TLS_HELLO_REQUEST = 0, TLS_CLIENT_HELLO = 1, TLS_SERVER_HELLO = 2, + TLS_HELLO_VERIFY_REQUEST = 3, + TLS_NEW_SESSION_TICKET = 4, + TLS_END_OF_EARLY_DATA = 5, + TLS_HELLO_RETRY_REQUEST = 6, + TLS_ENCRYPTED_EXTENSIONS = 8, TLS_CERTIFICATE = 11, TLS_SERVER_KEY_EXCHANGE = 12, TLS_CERTIFICATE_REQUEST = 13, @@ -88,6 +94,11 @@ enum tls_handshake_type_t { TLS_CERTIFICATE_VERIFY = 15, TLS_CLIENT_KEY_EXCHANGE = 16, TLS_FINISHED = 20, + TLS_CERTIFICATE_URL = 21, + TLS_CERTIFICATE_STATUS = 22, + TLS_SUPPLEMENTAL_DATA = 23, + TLS_KEY_UPDATE = 24, + TLS_MESSAGE_HASH = 254, }; /** @@ -114,7 +125,7 @@ enum tls_purpose_t { }; /** - * TLS Hello extension types. + * TLS Handshake extension types. */ enum tls_extension_t { /** Server name the client wants to talk to */ @@ -129,12 +140,42 @@ enum tls_extension_t { TLS_EXT_TRUNCATED_HMAC = 4, /** list of OCSP responders the client trusts */ TLS_EXT_STATUS_REQUEST = 5, - /** list of supported elliptic curves */ - TLS_EXT_ELLIPTIC_CURVES = 10, + /** list of supported groups, in legacy tls: elliptic curves */ + TLS_EXT_SUPPORTED_GROUPS = 10, /** supported point formats */ TLS_EXT_EC_POINT_FORMATS = 11, /** list supported signature algorithms */ TLS_EXT_SIGNATURE_ALGORITHMS = 13, + /** indicate usage of Datagram Transport Layer Security (DTLS) */ + TLS_EXT_USE_SRTP = 14, + /** indicate usage of heartbeat */ + TLS_EXT_HEARTBEAT = 15, + /** indicate usage of application-layer protocol negotiation */ + TLS_EXT_APPLICATION_LAYER_PROTOCOL_NEGOTIATION = 16, + /** exchange raw public key, client side*/ + TLS_CLIENT_CERTIFICATE_TYPE = 19, + /** exchange raw public key, server side*/ + TLS_SERVER_CERTIFICATE_TYPE = 20, + /** negotiate identity of the psk **/ + TLS_EXT_PRE_SHARED_KEY = 41, + /** send data in 0-RTT when psk is used and early data is allowed **/ + TLS_EXT_EARLY_DATA = 42, + /** negotiate supported tls versions **/ + TLS_EXT_SUPPORTED_VERSIONS = 43, + /** identify client **/ + TLS_EXT_COOKIE = 44, + /** psk modes supported by the client **/ + TLS_EXT_PSK_KEY_EXCHANGE_MODES = 45, + /** indicate supported ca's by endpoint **/ + TLS_EXT_CERTIFICATE_AUTHORITIES = 47, + /** provide oid/value pairs to match client's certificate **/ + TLS_EXT_OID_FILTERS = 48, + /** willing to perform post-handshake authentication **/ + TLS_EXT_POST_HANDSHAKE_AUTH = 49, + /** list supported signature algorithms to verify certificates **/ + TLS_EXT_SIGNATURE_ALGORITHMS_CERT = 50, + /** list endpoint's cryptographic parameters **/ + TLS_EXT_KEY_SHARE = 51, /** cryptographic binding for RFC 5746 renegotiation indication */ TLS_EXT_RENEGOTIATION_INFO = 65281, }; @@ -216,11 +257,18 @@ struct tls_t { identification_t* (*get_peer_id)(tls_t *this); /** - * Get the negotiated TLS/SSL version. + * Get the maximum and negotiated TLS/SSL version. * - * @return negotiated TLS version + * @return max and negotiated TLS version */ - tls_version_t (*get_version)(tls_t *this); + tls_version_t (*get_version_max)(tls_t *this); + + /** + * Get the minimum TLS/SSL version. + * + * @return min TLS version + */ + tls_version_t (*get_version_min)(tls_t *this); /** * Set the negotiated TLS/SSL version. diff --git a/src/libtls/tls_alert.c b/src/libtls/tls_alert.c index 69570e9c9..a9cd67165 100644 --- a/src/libtls/tls_alert.c +++ b/src/libtls/tls_alert.c @@ -63,19 +63,34 @@ ENUM_NEXT(tls_alert_desc_names, TLS_INTERNAL_ERROR, TLS_INTERNAL_ERROR, TLS_INSUFFICIENT_SECURITY, "internal error", ); +ENUM_NEXT(tls_alert_desc_names, TLS_INAPPROPRIATE_FALLBACK, + TLS_INAPPROPRIATE_FALLBACK, TLS_INTERNAL_ERROR, + "inappropriate fallback", +); ENUM_NEXT(tls_alert_desc_names, TLS_USER_CANCELED, TLS_USER_CANCELED, - TLS_INTERNAL_ERROR, + TLS_INAPPROPRIATE_FALLBACK, "user canceled", ); ENUM_NEXT(tls_alert_desc_names, TLS_NO_RENEGOTIATION, TLS_NO_RENEGOTIATION, TLS_USER_CANCELED, "no renegotiation", ); -ENUM_NEXT(tls_alert_desc_names, TLS_UNSUPPORTED_EXTENSION, TLS_UNSUPPORTED_EXTENSION, +ENUM_NEXT(tls_alert_desc_names, TLS_MISSING_EXTENSION, TLS_CERTIFICATE_REQUIRED, TLS_NO_RENEGOTIATION, + "missing extensions", "unsupported extension", + "certificate unobtainable", + "recognized name", + "bad certificate status response", + "bad certificate hash value", + "unknown psk identity", + "certificate required", ); -ENUM_END(tls_alert_desc_names, TLS_UNSUPPORTED_EXTENSION); +ENUM_NEXT(tls_alert_desc_names, TLS_NO_APPLICATION_PROTOCOL, + TLS_NO_APPLICATION_PROTOCOL, TLS_CERTIFICATE_REQUIRED, + "no application protocol" +); +ENUM_END(tls_alert_desc_names, TLS_NO_APPLICATION_PROTOCOL); typedef struct private_tls_alert_t private_tls_alert_t; diff --git a/src/libtls/tls_alert.h b/src/libtls/tls_alert.h index 8ce50f83d..053501357 100644 --- a/src/libtls/tls_alert.h +++ b/src/libtls/tls_alert.h @@ -61,9 +61,18 @@ enum tls_alert_desc_t { TLS_PROTOCOL_VERSION = 70, TLS_INSUFFICIENT_SECURITY = 71, TLS_INTERNAL_ERROR = 80, + TLS_INAPPROPRIATE_FALLBACK = 86, TLS_USER_CANCELED = 90, TLS_NO_RENEGOTIATION = 100, + TLS_MISSING_EXTENSION = 109, TLS_UNSUPPORTED_EXTENSION = 110, + TLS_CERTIFICATE_UNOBTAINABLE = 111, + TLS_RECOGNIZED_NAME = 112, + TLS_BAD_CERTIFICATE_STATUS_RESPONSE = 113, + TLS_BAD_CERTIFICATE_HASH_VALUE = 114, + TLS_UNKNOWN_PSK_IDENTITY = 115, + TLS_CERTIFICATE_REQUIRED = 116, + TLS_NO_APPLICATION_PROTOCOL = 120, }; /** diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index ebadb91c8..6b4651393 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -14,6 +14,7 @@ */ #include "tls_crypto.h" +#include "tls_hkdf.h" #include #include @@ -175,9 +176,17 @@ ENUM_NEXT(tls_cipher_suite_names, TLS_EMPTY_RENEGOTIATION_INFO_SCSV, TLS_EMPTY_RENEGOTIATION_INFO_SCSV, TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256, "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"); +ENUM_NEXT(tls_cipher_suite_names, TLS_AES_128_GCM_SHA256, + TLS_AES_128_CCM_8_SHA256, + TLS_EMPTY_RENEGOTIATION_INFO_SCSV, + "TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_CHACHA20_POLY1305_SHA256", + "TLS_AES_128_CCM_SHA256", + "TLS_AES_128_CCM_8_SHA256"); ENUM_NEXT(tls_cipher_suite_names, TLS_ECDH_ECDSA_WITH_NULL_SHA, TLS_ECDHE_PSK_WITH_NULL_SHA384, - TLS_EMPTY_RENEGOTIATION_INFO_SCSV, + TLS_AES_128_CCM_8_SHA256, "TLS_ECDH_ECDSA_WITH_NULL_SHA", "TLS_ECDH_ECDSA_WITH_RC4_128_SHA", "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", @@ -279,7 +288,7 @@ ENUM(tls_ecc_curve_type_names, TLS_ECC_EXPLICIT_PRIME, TLS_ECC_NAMED_CURVE, "NAMED_CURVE", ); -ENUM(tls_named_curve_names, TLS_SECT163K1, TLS_SECP521R1, +ENUM_BEGIN(tls_named_group_names, TLS_SECT163K1, TLS_SECP521R1, "SECT163K1", "SECT163R1", "SECT163R2", @@ -306,6 +315,18 @@ ENUM(tls_named_curve_names, TLS_SECT163K1, TLS_SECP521R1, "SECP384R1", "SECP521R1", ); +ENUM_NEXT(tls_named_group_names, TLS_CURVE25519, TLS_CURVE_448, TLS_SECP521R1, + "CURVE25519", + "CURVE448", +); +ENUM_NEXT(tls_named_group_names, TLS_FFDHE2048, TLS_FFDHE8192, TLS_CURVE_448, + "FFDHE2048", + "FFDHE3072", + "FFDHE4096", + "FFDHE6144", + "FFDHE8192", +); +ENUM_END(tls_named_group_names, TLS_FFDHE8192); ENUM(tls_ansi_point_format_names, TLS_ANSI_COMPRESSED, TLS_ANSI_HYBRID_Y, "compressed", @@ -350,6 +371,11 @@ struct private_tls_crypto_t { */ int suite_count; + /** + * HKDF for TLS 1.3 + */ + tls_hkdf_t *hkdf; + /** * Selected cipher suite */ @@ -365,6 +391,35 @@ struct private_tls_crypto_t { */ bool ecdsa; + /** + * MD5 supported? + */ + bool md5; + + /** + * SHA1 supported? + */ + bool sha1; + /** + * SHA224 supported? + */ + bool sha224; + + /* + * SHA256 supported? + */ + bool sha256; + + /** + * SHA384 supported? + */ + bool sha384; + + /** + * SHA512 supported? + */ + bool sha512; + /** * TLS context */ @@ -415,216 +470,291 @@ typedef struct { integrity_algorithm_t mac; encryption_algorithm_t encr; size_t encr_size; + tls_version_t tls_version; } suite_algs_t; /** * Mapping suites to a set of algorithms */ static suite_algs_t suite_algs[] = { + /* Cipher suites of TLS 1.3: key exchange and authentication + * delegated to extensions, therefore KEY_ANY, MODP_NONE, PRF_UNDEFINED */ + { TLS_AES_128_GCM_SHA256, + KEY_ANY, MODP_NONE, + HASH_SHA256, PRF_UNDEFINED, + AUTH_HMAC_SHA2_256_256, ENCR_AES_GCM_ICV16, 16, + TLS_1_3, + }, + { TLS_AES_256_GCM_SHA384, + KEY_ANY, MODP_NONE, + HASH_SHA384, PRF_UNDEFINED, + AUTH_HMAC_SHA2_384_384, ENCR_AES_GCM_ICV16, 32, + TLS_1_3, + }, + { TLS_CHACHA20_POLY1305_SHA256, + KEY_ANY, MODP_NONE, + HASH_SHA256, PRF_UNDEFINED, + AUTH_HMAC_SHA2_256_256, ENCR_CHACHA20_POLY1305, 16, + TLS_1_3, + }, + { TLS_AES_128_CCM_SHA256, + KEY_ANY, MODP_NONE, + HASH_SHA256, PRF_UNDEFINED, + AUTH_HMAC_SHA2_256_256, ENCR_AES_CCM_ICV16, 16, + TLS_1_3, + }, + { TLS_AES_128_CCM_8_SHA256, + KEY_ANY, MODP_NONE, + HASH_SHA256, PRF_UNDEFINED, + AUTH_HMAC_SHA2_256_256, ENCR_AES_CCM_ICV8, 16, + TLS_1_3, + }, + /* Legacy TLS cipher suites */ { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, KEY_ECDSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16 + AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16, + TLS_1_2, }, { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, KEY_ECDSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16 + AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16, + TLS_1_2, }, { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, KEY_ECDSA, ECP_384_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32 + AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32, + TLS_1_2, }, { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, KEY_ECDSA, ECP_384_BIT, HASH_SHA384, PRF_HMAC_SHA2_384, - AUTH_HMAC_SHA2_384_384, ENCR_AES_CBC, 32 + AUTH_HMAC_SHA2_384_384, ENCR_AES_CBC, 32, + TLS_1_2, }, { TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, KEY_ECDSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16 + AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16, + TLS_1_2, }, { TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, KEY_ECDSA, ECP_384_BIT, HASH_SHA384, PRF_HMAC_SHA2_384, - AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32 + AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32, + TLS_1_2, }, { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, KEY_RSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16 + AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16, + TLS_1_2, }, { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, KEY_RSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16 + AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16, + TLS_1_2, }, { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, KEY_RSA, ECP_384_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32 + AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32, + TLS_1_2, }, { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, KEY_RSA, ECP_384_BIT, HASH_SHA384, PRF_HMAC_SHA2_384, - AUTH_HMAC_SHA2_384_384, ENCR_AES_CBC, 32 + AUTH_HMAC_SHA2_384_384, ENCR_AES_CBC, 32, + TLS_1_2, }, { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, KEY_RSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16 + AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16, + TLS_1_2, }, { TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, KEY_RSA, ECP_384_BIT, HASH_SHA384, PRF_HMAC_SHA2_384, - AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32 + AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32, + TLS_1_2, }, { TLS_DHE_RSA_WITH_AES_128_CBC_SHA, KEY_RSA, MODP_2048_BIT, HASH_SHA256,PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16 + AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16, + TLS_1_2, }, { TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, KEY_RSA, MODP_3072_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16 + AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16, + TLS_1_2, }, { TLS_DHE_RSA_WITH_AES_256_CBC_SHA, KEY_RSA, MODP_3072_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32 + AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32, + TLS_1_2, }, { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, KEY_RSA, MODP_4096_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 32 + AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 32, + TLS_1_2, }, { TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, KEY_RSA, MODP_3072_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16 + AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16, + TLS_1_2, }, { TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, KEY_RSA, MODP_4096_BIT, HASH_SHA384, PRF_HMAC_SHA2_384, - AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32 + AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32, + TLS_1_2, }, { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, KEY_RSA, MODP_2048_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 16 + AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 16, + TLS_1_2, }, { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, KEY_RSA, MODP_3072_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 16 + AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 16, + TLS_1_2, }, { TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, KEY_RSA, MODP_3072_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 32 + AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 32, + TLS_1_2, }, { TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256, KEY_RSA, MODP_4096_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 32 + AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 32, + TLS_1_2, }, { TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, KEY_RSA, MODP_2048_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_3DES, 0 + AUTH_HMAC_SHA1_160, ENCR_3DES, 0, + TLS_1_2, }, { TLS_RSA_WITH_AES_128_CBC_SHA, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16 + AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16, + TLS_1_2, }, { TLS_RSA_WITH_AES_128_CBC_SHA256, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16 + AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16, + TLS_1_2, }, { TLS_RSA_WITH_AES_256_CBC_SHA, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32 + AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32, + TLS_1_2, }, { TLS_RSA_WITH_AES_256_CBC_SHA256, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 32 + AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 32, + TLS_1_2, }, { TLS_RSA_WITH_AES_128_GCM_SHA256, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16 + AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16, + TLS_1_2, }, { TLS_RSA_WITH_AES_256_GCM_SHA384, KEY_RSA, MODP_NONE, HASH_SHA384, PRF_HMAC_SHA2_384, - AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32 + AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32, + TLS_1_2, }, { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 16 + AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 16, + TLS_1_2, }, { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 16 + AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 16, + TLS_1_2, }, { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 32 + AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 32, + TLS_1_2, }, { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 32 + AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 32, + TLS_1_2, }, { TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, KEY_ECDSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_3DES, 0 + AUTH_HMAC_SHA1_160, ENCR_3DES, 0, + TLS_1_2, }, { TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, KEY_RSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_3DES, 0 + AUTH_HMAC_SHA1_160, ENCR_3DES, 0, + TLS_1_2, }, { TLS_RSA_WITH_3DES_EDE_CBC_SHA, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_3DES, 0 + AUTH_HMAC_SHA1_160, ENCR_3DES, 0, + TLS_1_2, }, { TLS_ECDHE_ECDSA_WITH_NULL_SHA, KEY_ECDSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_NULL, 0 + AUTH_HMAC_SHA1_160, ENCR_NULL, 0, + TLS_1_2, }, { TLS_ECDHE_RSA_WITH_NULL_SHA, KEY_ECDSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_NULL, 0 + AUTH_HMAC_SHA1_160, ENCR_NULL, 0, + TLS_1_2, }, { TLS_RSA_WITH_NULL_SHA, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_NULL, 0 + AUTH_HMAC_SHA1_160, ENCR_NULL, 0, + TLS_1_2, }, { TLS_RSA_WITH_NULL_SHA256, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA2_256_256, ENCR_NULL, 0 + AUTH_HMAC_SHA2_256_256, ENCR_NULL, 0, + TLS_1_2, }, { TLS_RSA_WITH_NULL_MD5, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_MD5_128, ENCR_NULL, 0 + AUTH_HMAC_MD5_128, ENCR_NULL, 0, + TLS_1_2, }, }; @@ -978,14 +1108,17 @@ static void filter_specific_config_suites(private_tls_crypto_t *this, static void filter_unsupported_suites(suite_algs_t suites[], int *count) { /* filter suite list by each algorithm */ + if (suites->tls_version < TLS_1_3) + { + filter_suite(suites, count, offsetof(suite_algs_t, encr), + lib->crypto->create_aead_enumerator); + filter_suite(suites, count, offsetof(suite_algs_t, prf), + lib->crypto->create_prf_enumerator); + } filter_suite(suites, count, offsetof(suite_algs_t, encr), lib->crypto->create_crypter_enumerator); - filter_suite(suites, count, offsetof(suite_algs_t, encr), - lib->crypto->create_aead_enumerator); filter_suite(suites, count, offsetof(suite_algs_t, mac), lib->crypto->create_signer_enumerator); - filter_suite(suites, count, offsetof(suite_algs_t, prf), - lib->crypto->create_prf_enumerator); filter_suite(suites, count, offsetof(suite_algs_t, hash), lib->crypto->create_hasher_enumerator); filter_suite(suites, count, offsetof(suite_algs_t, dh), @@ -1068,7 +1201,7 @@ static bool create_null(private_tls_crypto_t *this, suite_algs_t *algs) */ static bool create_traditional(private_tls_crypto_t *this, suite_algs_t *algs) { - if (this->tls->get_version(this->tls) < TLS_1_1) + if (this->tls->get_version_max(this->tls) < TLS_1_1) { this->aead_in = tls_aead_create_implicit(algs->mac, algs->encr, algs->encr_size); @@ -1097,8 +1230,20 @@ static bool create_traditional(private_tls_crypto_t *this, suite_algs_t *algs) */ static bool create_aead(private_tls_crypto_t *this, suite_algs_t *algs) { - this->aead_in = tls_aead_create_aead(algs->encr, algs->encr_size); - this->aead_out = tls_aead_create_aead(algs->encr, algs->encr_size); + if (this->tls->get_version_max(this->tls) < TLS_1_3) + { + this->aead_in = tls_aead_create_aead(algs->encr, algs->encr_size); + this->aead_out = tls_aead_create_aead(algs->encr, algs->encr_size); + } + else + { + this->aead_in = tls_aead_create_seq(algs->encr, algs->encr_size); + this->aead_out = tls_aead_create_seq(algs->encr, algs->encr_size); + + this->protection->set_cipher(this->protection, TRUE, this->aead_in); + this->protection->set_cipher(this->protection, FALSE, this->aead_out); + + } if (!this->aead_in || !this->aead_out) { DBG1(DBG_TLS, "selected TLS transforms %N-%u not supported", @@ -1125,18 +1270,30 @@ static bool create_ciphers(private_tls_crypto_t *this, suite_algs_t *algs) { destroy_aeads(this); DESTROY_IF(this->prf); - if (this->tls->get_version(this->tls) < TLS_1_2) + if (this->tls->get_version_max(this->tls) < TLS_1_3) { - this->prf = tls_prf_create_10(); + if (this->tls->get_version_max(this->tls) < TLS_1_2) + { + this->prf = tls_prf_create_10(); + } + else + { + this->prf = tls_prf_create_12(algs->prf); + } + if (!this->prf) + { + DBG1(DBG_TLS, "selected TLS PRF not supported"); + return FALSE; + } } else { - this->prf = tls_prf_create_12(algs->prf); - } - if (!this->prf) - { - DBG1(DBG_TLS, "selected TLS PRF not supported"); - return FALSE; + this->hkdf = tls_hkdf_create(algs->hash, chunk_empty); + if (!this->hkdf) + { + DBG1(DBG_TLS, "TLS HKDF creation unsuccessful"); + return FALSE; + } } if (algs->encr == ENCR_NULL) { @@ -1245,6 +1402,30 @@ METHOD(tls_crypto_t, get_signature_algorithms, void, { continue; } + if (schemes[i].hash == TLS_HASH_MD5 && !this->md5) + { + continue; + } + if (schemes[i].hash == TLS_HASH_SHA1 && !this->sha1) + { + continue; + } + if (schemes[i].hash == TLS_HASH_SHA224 && !this->sha224) + { + continue; + } + if (schemes[i].hash == TLS_HASH_SHA256 && !this->sha256) + { + continue; + } + if (schemes[i].hash == TLS_HASH_SHA384 && !this->sha384) + { + continue; + } + if (schemes[i].hash == TLS_HASH_SHA512 && !this->sha512) + { + continue; + } if (!lib->plugins->has_feature(lib->plugins, PLUGIN_PROVIDE(PUBKEY_VERIFY, schemes[i].scheme))) { @@ -1287,7 +1468,7 @@ static signature_scheme_t hashsig_to_scheme(key_type_t type, */ static struct { diffie_hellman_group_t group; - tls_named_curve_t curve; + tls_named_group_t curve; } curves[] = { { ECP_256_BIT, TLS_SECP256R1}, { ECP_384_BIT, TLS_SECP384R1}, @@ -1300,7 +1481,7 @@ CALLBACK(group_filter, bool, void *null, enumerator_t *orig, va_list args) { diffie_hellman_group_t group, *out; - tls_named_curve_t *curve; + tls_named_group_t *curve; char *plugin; int i; @@ -1357,7 +1538,7 @@ METHOD(tls_crypto_t, append_handshake, void, */ static bool hash_data(private_tls_crypto_t *this, chunk_t data, chunk_t *hash) { - if (this->tls->get_version(this->tls) >= TLS_1_2) + if (this->tls->get_version_max(this->tls) >= TLS_1_2) { hasher_t *hasher; suite_algs_t *alg; @@ -1407,7 +1588,7 @@ METHOD(tls_crypto_t, sign, bool, private_tls_crypto_t *this, private_key_t *key, bio_writer_t *writer, chunk_t data, chunk_t hashsig) { - if (this->tls->get_version(this->tls) >= TLS_1_2) + if (this->tls->get_version_max(this->tls) >= TLS_1_2) { const chunk_t hashsig_def = chunk_from_chars( TLS_HASH_SHA1, TLS_SIG_RSA, TLS_HASH_SHA1, TLS_SIG_ECDSA); @@ -1490,7 +1671,64 @@ METHOD(tls_crypto_t, verify, bool, private_tls_crypto_t *this, public_key_t *key, bio_reader_t *reader, chunk_t data) { - if (this->tls->get_version(this->tls) >= TLS_1_2) + if (this->tls->get_version_max(this->tls) == TLS_1_3) + { + signature_scheme_t scheme = SIGN_UNKNOWN; + uint8_t hash, alg; + chunk_t sig, transcript_hash, static_sig_data_all; + + chunk_t static_sig_data = chunk_from_chars( + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x54, 0x4c, 0x53, 0x20, 0x31, 0x2e, 0x33, 0x2c, + 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x69, 0x66, + 0x79, 0x00, + ); + + if (!reader->read_uint8(reader, &hash) || + !reader->read_uint8(reader, &alg) || + !reader->read_data16(reader, &sig)) + { + DBG1(DBG_TLS, "received invalid signature"); + return FALSE; + } + + if (!hash_data(this, data, &transcript_hash)) + { + DBG1(DBG_TLS, "Unable to hash"); + return FALSE; + } + + static_sig_data_all = chunk_cat("cc", static_sig_data, transcript_hash); + scheme = hashsig_to_scheme(key->get_type(key), hash, alg); + if (scheme == SIGN_UNKNOWN) + { + DBG1(DBG_TLS, "signature algorithms %N/%N not supported", + tls_hash_algorithm_names, hash, + tls_signature_algorithm_names, alg); + return FALSE; + } + if (!key->verify(key, scheme, NULL, static_sig_data_all, sig)) + { + DBG1(DBG_TLS, "verification of signature failed"); + return FALSE; + } + DBG2(DBG_TLS, + "verified signature with %N/%N", + tls_hash_algorithm_names, + hash, + tls_signature_algorithm_names, + alg); + } + else if (this->tls->get_version_max(this->tls) == TLS_1_2) { signature_scheme_t scheme = SIGN_UNKNOWN; uint8_t hash, alg; @@ -1594,6 +1832,42 @@ METHOD(tls_crypto_t, calculate_finished, bool, return TRUE; } +METHOD(tls_crypto_t, calculate_finished_tls13, bool, + private_tls_crypto_t *this, bool is_server, chunk_t *out) +{ + chunk_t finished_key, finished_hash; + prf_t *prf; + + this->hkdf->derive_finished(this->hkdf, is_server, &finished_key); + if (!hash_data(this, this->handshake, &finished_hash)) + { + DBG1(DBG_TLS, "creating hash of handshake failed"); + } + + if (this->suite == TLS_AES_256_GCM_SHA384) + { + prf = lib->crypto->create_prf(lib->crypto, PRF_HMAC_SHA2_384); + } + else + { + prf = lib->crypto->create_prf(lib->crypto, PRF_HMAC_SHA2_256); + } + if(!prf->set_key(prf, finished_key) || + !prf->allocate_bytes(prf, finished_hash, out)) + { + DBG1(DBG_TLS, "setting key or generating hash for HMAC failed"); + chunk_clear(&finished_key); + chunk_clear(&finished_hash); + prf->destroy(prf); + return FALSE; + } + + chunk_clear(&finished_key); + chunk_clear(&finished_hash); + prf->destroy(prf); + return TRUE; +} + /** * Derive master secret from premaster, optionally save session */ @@ -1710,6 +1984,150 @@ METHOD(tls_crypto_t, derive_secrets, bool, expand_keys(this, client_random, server_random); } +METHOD(tls_crypto_t, derive_handshake_secret, bool, + private_tls_crypto_t *this, chunk_t shared_secret) +{ + chunk_t c_key, c_iv, s_key, s_iv; + + this->hkdf->set_shared_secret(this->hkdf, shared_secret); + + /* client key material */ + if (!this->hkdf->generate_secret(this->hkdf, TLS_HKDF_C_HS_TRAFFIC, + this->handshake, NULL) || + !this->hkdf->derive_key(this->hkdf, FALSE, + this->aead_out-> + get_encr_key_size(this->aead_out), &c_key) || + !this->hkdf->derive_iv(this->hkdf, FALSE, + this->aead_out-> + get_iv_size(this->aead_out), &c_iv)) + { + DBG1(DBG_TLS, "deriving client key material failed"); + chunk_clear(&c_key); + chunk_clear(&c_iv); + return FALSE; + } + + /* server key material */ + if (!this->hkdf->generate_secret(this->hkdf, TLS_HKDF_S_HS_TRAFFIC, + this->handshake, NULL) || + !this->hkdf->derive_key(this->hkdf, TRUE, this->aead_in-> + get_encr_key_size(this->aead_in), &s_key) || + !this->hkdf->derive_iv(this->hkdf, TRUE, this->aead_in-> + get_iv_size(this->aead_in), &s_iv)) + { + DBG1(DBG_TLS, "deriving server key material failed"); + chunk_clear(&c_key); + chunk_clear(&c_iv); + chunk_clear(&s_key); + chunk_clear(&s_iv); + return FALSE; + } + + if (this->tls->is_server(this->tls)) + { + if (!this->aead_in->set_keys(this->aead_in, chunk_empty, s_key, s_iv) || + !this->aead_out->set_keys(this->aead_out, chunk_empty, c_key, c_iv)) + { + DBG1(DBG_TLS, "setting aead server key material failed"); + chunk_clear(&c_key); + chunk_clear(&c_iv); + chunk_clear(&s_key); + chunk_clear(&s_iv); + return FALSE; + } + } + else + { + if (!this->aead_in->set_keys(this->aead_in, chunk_empty, s_key, s_iv) || + !this->aead_out->set_keys(this->aead_out, chunk_empty, c_key, c_iv)) + { + DBG1(DBG_TLS, "setting aead client key material failed"); + chunk_clear(&c_key); + chunk_clear(&c_iv); + chunk_clear(&s_key); + chunk_clear(&s_iv); + return FALSE; + } + } + + chunk_clear(&c_key); + chunk_clear(&c_iv); + chunk_clear(&s_key); + chunk_clear(&s_iv); + return TRUE; +} + +METHOD(tls_crypto_t, derive_app_secret, bool, + private_tls_crypto_t *this) +{ + chunk_t c_key, c_iv, s_key, s_iv; + + /* client key material */ + if (!this->hkdf->generate_secret(this->hkdf, TLS_HKDF_C_AP_TRAFFIC, + this->handshake, NULL) || + !this->hkdf->derive_key(this->hkdf, FALSE, + this->aead_out-> + get_encr_key_size(this->aead_out), &c_key) || + !this->hkdf->derive_iv(this->hkdf, FALSE, + this->aead_out-> + get_iv_size(this->aead_out), &c_iv)) + { + DBG1(DBG_TLS, "deriving client key material failed"); + chunk_clear(&c_key); + chunk_clear(&c_iv); + return FALSE; + } + + /* server key material */ + if (!this->hkdf->generate_secret(this->hkdf, TLS_HKDF_S_AP_TRAFFIC, + this->handshake, NULL) || + !this->hkdf->derive_key(this->hkdf, TRUE, this->aead_in-> + get_encr_key_size(this->aead_in), &s_key) || + !this->hkdf->derive_iv(this->hkdf, TRUE, this->aead_in-> + get_iv_size(this->aead_in), &s_iv)) + { + DBG1(DBG_TLS, "deriving server key material failed"); + chunk_clear(&c_key); + chunk_clear(&c_iv); + chunk_clear(&s_key); + chunk_clear(&s_iv); + return FALSE; + } + + if (this->tls->is_server(this->tls)) + { + if (!this->aead_in->set_keys(this->aead_in, chunk_empty, s_key, s_iv) || + !this->aead_out->set_keys(this->aead_out, chunk_empty, c_key, c_iv)) + { + DBG1(DBG_TLS, "setting aead server key material failed"); + chunk_clear(&c_key); + chunk_clear(&c_iv); + chunk_clear(&s_key); + chunk_clear(&s_iv); + return FALSE; + } + } + else + { + if (!this->aead_in->set_keys(this->aead_in, chunk_empty, s_key, s_iv) || + !this->aead_out->set_keys(this->aead_out, chunk_empty, c_key, c_iv)) + { + DBG1(DBG_TLS, "setting aead client key material failed"); + chunk_clear(&c_key); + chunk_clear(&c_iv); + chunk_clear(&s_key); + chunk_clear(&s_iv); + return FALSE; + } + } + + chunk_clear(&c_key); + chunk_clear(&c_iv); + chunk_clear(&s_key); + chunk_clear(&s_iv); + return TRUE; +} + METHOD(tls_crypto_t, resume_session, tls_cipher_suite_t, private_tls_crypto_t *this, chunk_t session, identification_t *id, chunk_t client_random, chunk_t server_random) @@ -1776,6 +2194,7 @@ METHOD(tls_crypto_t, destroy, void, free(this->handshake.ptr); free(this->msk.ptr); DESTROY_IF(this->prf); + DESTROY_IF(this->hkdf); free(this->suites); free(this); } @@ -1789,6 +2208,8 @@ tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache) enumerator_t *enumerator; credential_type_t type; int subtype; + int hash_algorithm; + const char *plugin; INIT(this, .public = { @@ -1804,7 +2225,10 @@ tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache) .sign_handshake = _sign_handshake, .verify_handshake = _verify_handshake, .calculate_finished = _calculate_finished, + .calculate_finished_tls13 = _calculate_finished_tls13, .derive_secrets = _derive_secrets, + .derive_handshake_secret = _derive_handshake_secret, + .derive_app_secret = _derive_app_secret, .resume_session = _resume_session, .get_session = _get_session, .change_cipher = _change_cipher, @@ -1835,6 +2259,49 @@ tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache) } enumerator->destroy(enumerator); + enumerator = lib->crypto->create_hasher_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &hash_algorithm, &plugin)) + { + switch (hash_algorithm) + { + case TLS_HASH_MD5: + if (tls->get_version_max(tls) < TLS_1_3) + { + this->md5 = TRUE; + } + else + { + this->md5 = FALSE; + } + break; + case TLS_HASH_SHA1: + this->sha1 = TRUE; + break; + case TLS_HASH_SHA224: + if (tls->get_version_max(tls) < TLS_1_3) + { + this->sha224 = TRUE; + } + else + { + this->sha224 = FALSE; + } + break; + case TLS_HASH_SHA384: + this->sha384 = TRUE; + break; + case TLS_HASH_SHA256: + this->sha256 = TRUE; + break; + case TLS_HASH_SHA512: + this->sha512 = TRUE; + break; + default: + continue; + } + } + enumerator->destroy(enumerator); + switch (tls->get_purpose(tls)) { case TLS_PURPOSE_EAP_TLS: diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h index a42e07bb3..a1390e349 100644 --- a/src/libtls/tls_crypto.h +++ b/src/libtls/tls_crypto.h @@ -27,7 +27,7 @@ typedef enum tls_hash_algorithm_t tls_hash_algorithm_t; typedef enum tls_signature_algorithm_t tls_signature_algorithm_t; typedef enum tls_client_certificate_type_t tls_client_certificate_type_t; typedef enum tls_ecc_curve_type_t tls_ecc_curve_type_t; -typedef enum tls_named_curve_t tls_named_curve_t; +typedef enum tls_named_group_t tls_named_group_t; typedef enum tls_ansi_point_format_t tls_ansi_point_format_t; typedef enum tls_ec_point_format_t tls_ec_point_format_t; @@ -191,6 +191,12 @@ enum tls_cipher_suite_t { TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF, + TLS_AES_128_GCM_SHA256 = 0x1301, + TLS_AES_256_GCM_SHA384 = 0x1302, + TLS_CHACHA20_POLY1305_SHA256 = 0x1303, + TLS_AES_128_CCM_SHA256 = 0x1304, + TLS_AES_128_CCM_8_SHA256 = 0x1305, + TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xC001, TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 0xC002, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC003, @@ -327,7 +333,7 @@ extern enum_name_t *tls_ecc_curve_type_names; /** * TLS Named Curve identifiers */ -enum tls_named_curve_t { +enum tls_named_group_t { TLS_SECT163K1 = 1, TLS_SECT163R1 = 2, TLS_SECT163R2 = 3, @@ -353,12 +359,21 @@ enum tls_named_curve_t { TLS_SECP256R1 = 23, TLS_SECP384R1 = 24, TLS_SECP521R1 = 25, + + /* TLS 1.3: new ecdhe, dhe groups */ + TLS_CURVE25519 = 29, + TLS_CURVE_448 = 30, + TLS_FFDHE2048 = 256, + TLS_FFDHE3072 = 257, + TLS_FFDHE4096 = 258, + TLS_FFDHE6144 = 259, + TLS_FFDHE8192 = 260, }; /** - * Enum names for tls_named_curve_t + * Enum names for tls_named_group_t */ -extern enum_name_t *tls_named_curve_names; +extern enum_name_t *tls_named_group_names; /** * EC Point format, ANSI X9.62. @@ -432,7 +447,7 @@ struct tls_crypto_t { /** * Create an enumerator over supported ECDH groups. * - * Enumerates over (diffie_hellman_group_t, tls_named_curve_t) + * Enumerates over (diffie_hellman_group_t, tls_named_group_t) * * @return enumerator */ @@ -499,7 +514,7 @@ struct tls_crypto_t { bio_reader_t *reader); /** - * Calculate the data of a TLS finished message. + * Calculate the data of a legacyTLS finished message. * * @param label ASCII label to use for calculation * @param out buffer to write finished data to @@ -507,6 +522,15 @@ struct tls_crypto_t { */ bool (*calculate_finished)(tls_crypto_t *this, char *label, char out[12]); + /** + * Calculate the data of a TLS finished message. + * + * @param out buffer to write finished data to + * @return TRUE if calculation successful + */ + bool (*calculate_finished_tls13)(tls_crypto_t *this, bool is_server, + chunk_t *out); + /** * Derive the master secret, MAC and encryption keys. * @@ -521,6 +545,21 @@ struct tls_crypto_t { chunk_t session, identification_t *id, chunk_t client_random, chunk_t server_random); + /** + * Derive the handshake keys. + * + * @param shared_secret input key material + * @return TRUE if secret derived successfully + */ + bool (*derive_handshake_secret)(tls_crypto_t *this, chunk_t shared_secret); + + /** + * Derive the application keys. + * + * @return TRUE if secret derived successfully + */ + bool (*derive_app_secret)(tls_crypto_t *this); + /** * Try to resume a TLS session, derive key material. * diff --git a/src/libtls/tls_fragmentation.c b/src/libtls/tls_fragmentation.c index 7a963029d..f74cf59bf 100644 --- a/src/libtls/tls_fragmentation.c +++ b/src/libtls/tls_fragmentation.c @@ -292,7 +292,11 @@ METHOD(tls_fragmentation_t, process, status_t, break; case TLS_HANDSHAKE: status = process_handshake(this, reader); - break; + if (!this->handshake->finished(this->handshake)) + { + break; + } + /* fall */ case TLS_APPLICATION_DATA: status = process_application(this, reader); break; @@ -353,7 +357,8 @@ static status_t build_handshake(private_tls_fragmentation_t *this) msg->write_data24(msg, hs->get_buf(hs)); DBG2(DBG_TLS, "sending TLS %N handshake (%u bytes)", tls_handshake_type_names, type, hs->get_buf(hs).len); - if (!this->handshake->cipherspec_changed(this->handshake, FALSE)) + if (type != TLS_FINISHED && + !this->handshake->cipherspec_changed(this->handshake, FALSE)) { hs->destroy(hs); continue; diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 1f2439ca1..471ba49f7 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -37,6 +37,12 @@ typedef enum { STATE_FINISHED_SENT, STATE_CIPHERSPEC_CHANGED_IN, STATE_FINISHED_RECEIVED, + /* new states in TLS 1.3 */ + STATE_HELLORETRYREQ_RECEIVED, + STATE_ENCRYPTED_EXTENSIONS_RECEIVED, + STATE_CERT_VERIFY_RECEIVED, + STATE_FINISHED_SENT_KEY_SWITCHED, + } peer_state_t; /** @@ -143,8 +149,11 @@ static status_t process_server_hello(private_tls_peer_t *this, { uint8_t compression; uint16_t version, cipher; - chunk_t random, session, ext = chunk_empty; + chunk_t random, session, ext = chunk_empty, ext_key_share = chunk_empty; tls_cipher_suite_t suite = 0; + int offset = 0; + uint16_t extension_type, extension_length; + uint16_t key_type, key_length; this->crypto->append_handshake(this->crypto, TLS_SERVER_HELLO, reader->peek(reader)); @@ -163,6 +172,51 @@ static status_t process_server_hello(private_tls_peer_t *this, memcpy(this->server_random, random.ptr, sizeof(this->server_random)); + bio_reader_t *extension_reader = bio_reader_create(ext); + + /* parse extension to decide which tls version of the state machine we + * want to go + */ + while (offset < ext.len) + { + chunk_t extension_payload = chunk_empty; + + extension_reader->read_uint16(extension_reader, &extension_type); + extension_reader->read_uint16(extension_reader, &extension_length); + offset += extension_length + 4; + + if (!extension_reader->read_data(extension_reader, + extension_length, + &extension_payload)) + { + DBG2(DBG_TLS, "unable to read extension payload data"); + } + + bio_reader_t *ext_payload_reader = bio_reader_create(extension_payload); + + switch (extension_type) + { + case TLS_EXT_SUPPORTED_VERSIONS: + ext_payload_reader->read_uint16(ext_payload_reader, &version); + break; + + case TLS_EXT_KEY_SHARE: + ext_payload_reader->read_uint16(ext_payload_reader, &key_type); + ext_payload_reader->read_uint16(ext_payload_reader, &key_length); + if (!ext_payload_reader->read_data(ext_payload_reader, + key_length, + &ext_key_share)) + { + DBG2(DBG_TLS, "no valid key share found in extension"); + } + break; + default: + continue; + } + ext_payload_reader->destroy(ext_payload_reader); + } + extension_reader->destroy(extension_reader); + if (!this->tls->set_version(this->tls, version)) { DBG1(DBG_TLS, "negotiated version %N not supported", @@ -171,17 +225,24 @@ static status_t process_server_hello(private_tls_peer_t *this, return NEED_MORE; } - if (chunk_equals(this->session, session)) + if (this->tls->get_version_max(this->tls) < TLS_1_3) { - suite = this->crypto->resume_session(this->crypto, session, this->server, - chunk_from_thing(this->client_random), - chunk_from_thing(this->server_random)); - if (suite) + if (chunk_equals(this->session, session)) { - DBG1(DBG_TLS, "resumed %N using suite %N", - tls_version_names, version, tls_cipher_suite_names, suite); - this->resume = TRUE; + suite = this->crypto->resume_session(this->crypto, session, + this->server, chunk_from_thing + (this->client_random), + chunk_from_thing + (this->server_random)); + if (suite) + { + DBG1(DBG_TLS, "resumed %N using suite %N", + tls_version_names, version, tls_cipher_suite_names, suite); + this->resume = TRUE; + } } + DESTROY_IF(this->dh); + this->dh = NULL; } if (!suite) { @@ -198,10 +259,101 @@ static status_t process_server_hello(private_tls_peer_t *this, free(this->session.ptr); this->session = chunk_clone(session); } + + if (this->tls->get_version_max(this->tls) == TLS_1_3) + { + chunk_t shared_secret; + + if (key_type != CURVE_25519 && + !this->dh->set_other_public_value(this->dh, ext_key_share)) + { + DBG2(DBG_TLS, "server key share unable to save"); + } + if (!this->dh->get_shared_secret(this->dh, &shared_secret)) + { + DBG2(DBG_TLS, "No shared secret key found"); + } + + if (!this->crypto->derive_handshake_secret(this->crypto, shared_secret)) + { + DBG2(DBG_TLS, "derive handshake traffic secret failed"); + } + } + this->state = STATE_HELLO_RECEIVED; return NEED_MORE; } +/** +* Process a server encrypted extensions message +*/ +static status_t process_encrypted_extensions(private_tls_peer_t *this, + bio_reader_t *reader) +{ + uint16_t length; + chunk_t ext = chunk_empty; + int offset = 0; + uint16_t extension_type, extension_length; + + this->crypto->append_handshake(this->crypto, + TLS_ENCRYPTED_EXTENSIONS, reader->peek(reader)); + + + if (!reader->read_uint16(reader, &length) || + (reader->remaining(reader) && !reader->read_data16(reader, &ext))) + { + DBG1(DBG_TLS, "received invalid EncryptedExtensions"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + if (ext.len == 0) + { + this->state = STATE_ENCRYPTED_EXTENSIONS_RECEIVED; + return NEED_MORE; + } + else + { + bio_reader_t *extension_reader = bio_reader_create(ext); + + while (offset < ext.len) + { + chunk_t extension_payload = chunk_empty; + extension_reader->read_uint16(extension_reader, &extension_type); + extension_reader->read_uint16(extension_reader, &extension_length); + offset += extension_length + 4; + + if (!extension_reader->read_data(extension_reader, + extension_length, + &extension_payload)) + { + DBG2(DBG_TLS, "unable to read extension payload data"); + } + switch (extension_type) + { + /* fall through because not supported so far */ + case TLS_EXT_SERVER_NAME: + case TLS_EXT_MAX_FRAGMENT_LENGTH: + case TLS_EXT_SUPPORTED_GROUPS: + case TLS_EXT_USE_SRTP: + case TLS_EXT_HEARTBEAT: + case TLS_EXT_APPLICATION_LAYER_PROTOCOL_NEGOTIATION: + case TLS_SERVER_CERTIFICATE_TYPE: + this->state = STATE_ENCRYPTED_EXTENSIONS_RECEIVED; + extension_reader->destroy(extension_reader); + break; + default: + DBG1(DBG_TLS, "received forbidden EncryptedExtensions"); + this->alert->add(this->alert, TLS_FATAL, + TLS_ILLEGAL_PARAMETER); + extension_reader->destroy(extension_reader); + return NEED_MORE; + } + } + + } + return NEED_MORE; +} + /** * Check if a server certificate is acceptable for the given server identity */ @@ -252,6 +404,20 @@ static status_t process_certificate(private_tls_peer_t *this, this->crypto->append_handshake(this->crypto, TLS_CERTIFICATE, reader->peek(reader)); + if (this->tls->get_version_max(this->tls) > TLS_1_2) + { + if (!reader->read_data8(reader, &data)) + { + DBG1(DBG_TLS, "certificate request context invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + if (data.len > 0) + { + DBG1(DBG_TLS, "certificate request context available," + "but CertificateRequest not received"); + } + } if (!reader->read_data24(reader, &data)) { DBG1(DBG_TLS, "certificate message header invalid"); @@ -300,12 +466,69 @@ static status_t process_certificate(private_tls_peer_t *this, DBG1(DBG_TLS, "parsing TLS certificate failed, skipped"); this->alert->add(this->alert, TLS_WARNING, TLS_BAD_CERTIFICATE); } + if (this->tls->get_version_max(this->tls) > TLS_1_2) + { + if (!certs->read_data16(certs, &data)) + { + DBG1(DBG_TLS, "reading extension field of certificate failed", + &data); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + break; + } } certs->destroy(certs); this->state = STATE_CERT_RECEIVED; return NEED_MORE; } +/** + * Process Certificate verify + */ +static status_t process_cert_verify(private_tls_peer_t *this, + bio_reader_t *reader) +{ + enumerator_t *enumerator; + public_key_t *public; + auth_cfg_t *auth; + bio_reader_t *sig; + bool verified = FALSE; + + enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, + KEY_ANY, this->server, + this->server_auth, TRUE); + while (enumerator->enumerate(enumerator, &public, &auth)) + { + sig = bio_reader_create(reader->peek(reader)); + verified = this->crypto->verify_handshake(this->crypto, public, sig); + sig->destroy(sig); + if (verified) + { + this->server_auth->merge(this->server_auth, auth, FALSE); + break; + } + DBG1(DBG_TLS, "signature verification failed, trying another key"); + } + enumerator->destroy(enumerator); + + if (!verified) + { + DBG1(DBG_TLS, "no trusted certificate found for '%Y' to verify TLS peer", + this->server); + this->server->destroy(this->server); + this->peer = NULL; + this->state = STATE_KEY_EXCHANGE_RECEIVED; + } + else + { + this->state = STATE_CERT_VERIFY_RECEIVED; + } + this->crypto->append_handshake(this->crypto, + TLS_CERTIFICATE_VERIFY, reader->peek(reader)); + return NEED_MORE; +} + /** * Find a trusted public key to encrypt/verify key exchange data */ @@ -407,10 +630,10 @@ static status_t process_modp_key_exchange(private_tls_peer_t *this, * Get the EC group for a TLS named curve */ static diffie_hellman_group_t curve_to_ec_group(private_tls_peer_t *this, - tls_named_curve_t curve) + tls_named_group_t curve) { diffie_hellman_group_t group; - tls_named_curve_t current; + tls_named_group_t current; enumerator_t *enumerator; enumerator = this->crypto->create_ec_enumerator(this->crypto); @@ -464,7 +687,7 @@ static status_t process_ec_key_exchange(private_tls_peer_t *this, if (!group) { DBG1(DBG_TLS, "ECDH curve %N not supported", - tls_named_curve_names, curve); + tls_named_group_names, curve); this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); return NEED_MORE; } @@ -569,7 +792,7 @@ static status_t process_certreq(private_tls_peer_t *this, bio_reader_t *reader) return NEED_MORE; } this->cert_types = chunk_clone(types); - if (this->tls->get_version(this->tls) >= TLS_1_2) + if (this->tls->get_version_max(this->tls) >= TLS_1_2) { if (!reader->read_data16(reader, &hashsig)) { @@ -634,26 +857,54 @@ static status_t process_hello_done(private_tls_peer_t *this, */ static status_t process_finished(private_tls_peer_t *this, bio_reader_t *reader) { - chunk_t received; + chunk_t received, verify_data; char buf[12]; + uint32_t hash_length; - if (!reader->read_data(reader, sizeof(buf), &received)) + if (this->tls->get_version_max(this->tls) < TLS_1_3) { - DBG1(DBG_TLS, "received server finished too short"); - this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); - return NEED_MORE; + if (!reader->read_data(reader, sizeof(buf), &received)) + { + DBG1(DBG_TLS, "received server finished too short"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + if (!this->crypto->calculate_finished(this->crypto, "server finished", + buf)) + { + DBG1(DBG_TLS, "calculating server finished failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + if (!chunk_equals_const(received, chunk_from_thing(buf))) + { + DBG1(DBG_TLS, "received server finished invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR); + return NEED_MORE; + } } - if (!this->crypto->calculate_finished(this->crypto, "server finished", buf)) + else { - DBG1(DBG_TLS, "calculating server finished failed"); - this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); - return NEED_MORE; - } - if (!chunk_equals_const(received, chunk_from_thing(buf))) - { - DBG1(DBG_TLS, "received server finished invalid"); - this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR); - return NEED_MORE; + hash_length = reader->remaining(reader); + if (!reader->read_data(reader, hash_length, &received)) + { + DBG1(DBG_TLS, "received server finished too short"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + if (!this->crypto->calculate_finished_tls13(this->crypto, true, + &verify_data)) + { + DBG1(DBG_TLS, "calculating server finished failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + if (!chunk_equals(received, verify_data)) + { + DBG1(DBG_TLS, "received server finished invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR); + return NEED_MORE; + } } this->state = STATE_FINISHED_RECEIVED; this->crypto->append_handshake(this->crypto, TLS_FINISHED, received); @@ -661,61 +912,142 @@ static status_t process_finished(private_tls_peer_t *this, bio_reader_t *reader) return NEED_MORE; } +/** + * Process New Session Ticket message + */ +static status_t process_new_session_ticket(private_tls_peer_t *this, + bio_reader_t *reader) +{ + uint32_t ticket_lifetime, ticket_age_add; + chunk_t ticket_nonce, ticket, extensions; + + if (!reader->read_uint32(reader, &ticket_lifetime) || + !reader->read_uint32(reader, &ticket_age_add) || + !reader->read_data8(reader, &ticket_nonce) || + !reader->read_data16(reader, &ticket) || + !reader->read_data16(reader, &extensions)) + { + DBG1(DBG_TLS, "received invalid NewSessionTicket"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + return NEED_MORE; +} + METHOD(tls_handshake_t, process, status_t, private_tls_peer_t *this, tls_handshake_type_t type, bio_reader_t *reader) { tls_handshake_type_t expected; - switch (this->state) + if (this->tls->get_version_max(this->tls) < TLS_1_3) { - case STATE_HELLO_SENT: - if (type == TLS_SERVER_HELLO) - { - return process_server_hello(this, reader); - } - expected = TLS_SERVER_HELLO; - break; - case STATE_HELLO_RECEIVED: - if (type == TLS_CERTIFICATE) - { - return process_certificate(this, reader); - } - expected = TLS_CERTIFICATE; - break; - case STATE_CERT_RECEIVED: - if (type == TLS_SERVER_KEY_EXCHANGE) - { - return process_key_exchange(this, reader); - } - /* fall through since TLS_SERVER_KEY_EXCHANGE is optional */ - case STATE_KEY_EXCHANGE_RECEIVED: - if (type == TLS_CERTIFICATE_REQUEST) - { - return process_certreq(this, reader); - } - /* no cert request, server does not want to authenticate us */ - DESTROY_IF(this->peer); - this->peer = NULL; - /* fall through since TLS_CERTIFICATE_REQUEST is optional */ - case STATE_CERTREQ_RECEIVED: - if (type == TLS_SERVER_HELLO_DONE) - { - return process_hello_done(this, reader); - } - expected = TLS_SERVER_HELLO_DONE; - break; - case STATE_CIPHERSPEC_CHANGED_IN: - if (type == TLS_FINISHED) - { - return process_finished(this, reader); - } - expected = TLS_FINISHED; - break; - default: - DBG1(DBG_TLS, "TLS %N not expected in current state", - tls_handshake_type_names, type); - this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE); - return NEED_MORE; + switch (this->state) + { + case STATE_HELLO_SENT: + if (type == TLS_SERVER_HELLO) + { + return process_server_hello(this, reader); + } + expected = TLS_SERVER_HELLO; + break; + case STATE_HELLO_RECEIVED: + if (type == TLS_CERTIFICATE) + { + return process_certificate(this, reader); + } + expected = TLS_CERTIFICATE; + break; + case STATE_CERT_RECEIVED: + if (type == TLS_SERVER_KEY_EXCHANGE) + { + return process_key_exchange(this, reader); + } + /* fall through since TLS_SERVER_KEY_EXCHANGE is optional */ + case STATE_KEY_EXCHANGE_RECEIVED: + if (type == TLS_CERTIFICATE_REQUEST) + { + return process_certreq(this, reader); + } + /* no cert request, server does not want to authenticate us */ + DESTROY_IF(this->peer); + this->peer = NULL; + /* fall through since TLS_CERTIFICATE_REQUEST is optional */ + case STATE_CERTREQ_RECEIVED: + if (type == TLS_SERVER_HELLO_DONE) + { + return process_hello_done(this, reader); + } + expected = TLS_SERVER_HELLO_DONE; + break; + case STATE_CIPHERSPEC_CHANGED_IN: + if (type == TLS_FINISHED) + { + return process_finished(this, reader); + } + expected = TLS_FINISHED; + break; + default: + DBG1(DBG_TLS, "TLS %N not expected in current state", + tls_handshake_type_names, type); + this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE); + return NEED_MORE; + } + } + else + { + switch (this->state) + { + case STATE_HELLO_SENT: + if (type == TLS_SERVER_HELLO) + { + return process_server_hello(this, reader); + } + expected = TLS_SERVER_HELLO; + break; + case STATE_CIPHERSPEC_CHANGED_IN: + case STATE_HELLO_RECEIVED: + if (type == TLS_ENCRYPTED_EXTENSIONS) + { + return process_encrypted_extensions(this, reader); + } + expected = TLS_ENCRYPTED_EXTENSIONS; + break; + case STATE_ENCRYPTED_EXTENSIONS_RECEIVED: + if (type == TLS_CERTIFICATE) + { + return process_certificate(this, reader); + } + expected = TLS_CERTIFICATE; + break; + case STATE_CERT_RECEIVED: + if (type == TLS_CERTIFICATE_VERIFY) + { + return process_cert_verify(this, reader); + } + expected = TLS_CERTIFICATE_VERIFY; + break; + case STATE_CERT_VERIFY_RECEIVED: + if (type == TLS_FINISHED) + { + return process_finished(this, reader); + } + expected = TLS_FINISHED; + break; + case STATE_FINISHED_RECEIVED: + return NEED_MORE; + case STATE_FINISHED_SENT_KEY_SWITCHED: + if (type == TLS_NEW_SESSION_TICKET) + { + return process_new_session_ticket(this, reader); + } + expected = TLS_NEW_SESSION_TICKET; + break; + default: + DBG1(DBG_TLS, "TLS %N not expected in current state", + tls_handshake_type_names, type); + this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE); + return NEED_MORE; + } } DBG1(DBG_TLS, "TLS %N expected, but received %N", tls_handshake_type_names, expected, tls_handshake_type_names, type); @@ -731,11 +1063,13 @@ static status_t send_client_hello(private_tls_peer_t *this, { tls_cipher_suite_t *suites; bio_writer_t *extensions, *curves = NULL; - tls_version_t version; - tls_named_curve_t curve; + tls_version_t version_max, version_min; + tls_named_group_t curve; enumerator_t *enumerator; - int count, i; + int count, i, v; rng_t *rng; + chunk_t pub; + uint8_t nof_tls_versions; htoun32(&this->client_random, time(NULL)); rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); @@ -750,10 +1084,21 @@ static status_t send_client_hello(private_tls_peer_t *this, } rng->destroy(rng); - /* TLS version */ - version = this->tls->get_version(this->tls); - this->hello_version = version; - writer->write_uint16(writer, version); + /* client key generation */ + this->dh = lib->crypto->create_dh(lib->crypto, CURVE_25519); + + /* TLS version_max in handshake protocol */ + version_max = this->tls->get_version_max(this->tls); + version_min = this->tls->get_version_min(this->tls); + if (version_max < TLS_1_3) + { + this->hello_version = version_max; + } + else + { + this->hello_version = TLS_1_2; + } + writer->write_uint16(writer, this->hello_version); writer->write_data(writer, chunk_from_thing(this->client_random)); /* session identifier */ @@ -774,16 +1119,29 @@ static status_t send_client_hello(private_tls_peer_t *this, extensions = bio_writer_create(32); - extensions->write_uint16(extensions, TLS_EXT_SIGNATURE_ALGORITHMS); - this->crypto->get_signature_algorithms(this->crypto, extensions); + if (this->server->get_type(this->server) == ID_FQDN) + { + bio_writer_t *names; - /* add supported Elliptic Curves, if any */ + DBG2(DBG_TLS, "sending extension: Server Name Indication for '%Y'", + this->server); + names = bio_writer_create(8); + names->write_uint8(names, TLS_NAME_TYPE_HOST_NAME); + names->write_data16(names, this->server->get_encoding(this->server)); + names->wrap16(names); + extensions->write_uint16(extensions, TLS_EXT_SERVER_NAME); + extensions->write_data16(extensions, names->get_buf(names)); + names->destroy(names); + } + + DBG2(DBG_TLS, "sending extension: %N", + tls_extension_names, TLS_EXT_SUPPORTED_GROUPS); enumerator = this->crypto->create_ec_enumerator(this->crypto); while (enumerator->enumerate(enumerator, NULL, &curve)) { if (!curves) { - extensions->write_uint16(extensions, TLS_EXT_ELLIPTIC_CURVES); + extensions->write_uint16(extensions, TLS_EXT_SUPPORTED_GROUPS); curves = bio_writer_create(16); } curves->write_uint16(curves, curve); @@ -791,6 +1149,10 @@ static status_t send_client_hello(private_tls_peer_t *this, enumerator->destroy(enumerator); if (curves) { + if (version_max == TLS_1_3) + { + curves->write_uint16(curves, TLS_CURVE25519); + } curves->wrap16(curves); extensions->write_data16(extensions, curves->get_buf(curves)); curves->destroy(curves); @@ -801,21 +1163,39 @@ static status_t send_client_hello(private_tls_peer_t *this, extensions->write_uint8(extensions, 1); extensions->write_uint8(extensions, TLS_EC_POINT_UNCOMPRESSED); } - if (this->server->get_type(this->server) == ID_FQDN) + + DBG2(DBG_TLS, "sending extension: %N", + tls_extension_names, TLS_EXT_SUPPORTED_VERSIONS); + nof_tls_versions = (version_max - version_min + 1)*2; + extensions->write_uint16(extensions, TLS_EXT_SUPPORTED_VERSIONS); + extensions->write_uint16(extensions, nof_tls_versions+1); + extensions->write_uint8(extensions, nof_tls_versions); + for (v = version_max; v >= version_min; v--) { - bio_writer_t *names; - - DBG2(DBG_TLS, "sending Server Name Indication for '%Y'", this->server); - - names = bio_writer_create(8); - names->write_uint8(names, TLS_NAME_TYPE_HOST_NAME); - names->write_data16(names, this->server->get_encoding(this->server)); - names->wrap16(names); - extensions->write_uint16(extensions, TLS_EXT_SERVER_NAME); - extensions->write_data16(extensions, names->get_buf(names)); - names->destroy(names); + extensions->write_uint16(extensions, v); + nof_tls_versions += 2; } + DBG2(DBG_TLS, "sending extension: %N", + tls_extension_names, TLS_EXT_SIGNATURE_ALGORITHMS); + extensions->write_uint16(extensions, TLS_EXT_SIGNATURE_ALGORITHMS); + this->crypto->get_signature_algorithms(this->crypto, extensions); + + DBG2(DBG_TLS, "sending extension: %N", + tls_extension_names, TLS_EXT_KEY_SHARE); + if (!this->dh->get_my_public_value(this->dh, &pub)) + { + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + extensions->write_uint16(extensions, TLS_EXT_KEY_SHARE); + extensions->write_uint16(extensions, pub.len+6); + extensions->write_uint16(extensions, pub.len+4); + extensions->write_uint16(extensions, TLS_CURVE25519); + extensions->write_uint16(extensions, pub.len); + extensions->write_data(extensions, pub); + free(pub.ptr); + writer->write_data16(writer, extensions->get_buf(extensions)); extensions->destroy(extensions); @@ -1071,16 +1451,33 @@ static status_t send_certificate_verify(private_tls_peer_t *this, static status_t send_finished(private_tls_peer_t *this, tls_handshake_type_t *type, bio_writer_t *writer) { - char buf[12]; + chunk_t verify_data; - if (!this->crypto->calculate_finished(this->crypto, "client finished", buf)) + if (this->tls->get_version_max(this->tls) < TLS_1_3) { - DBG1(DBG_TLS, "calculating client finished data failed"); - this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); - return NEED_MORE; - } + char buf[12]; - writer->write_data(writer, chunk_from_thing(buf)); + if (!this->crypto->calculate_finished(this->crypto, "client finished", buf)) + { + DBG1(DBG_TLS, "calculating client finished data failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + + writer->write_data(writer, chunk_from_thing(buf)); + } + else + { + if (!this->crypto->calculate_finished_tls13(this->crypto, false, + &verify_data)) + { + DBG1(DBG_TLS, "calculating client finished data failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + + writer->write_data(writer, verify_data); + } *type = TLS_FINISHED; this->state = STATE_FINISHED_SENT; @@ -1091,63 +1488,112 @@ static status_t send_finished(private_tls_peer_t *this, METHOD(tls_handshake_t, build, status_t, private_tls_peer_t *this, tls_handshake_type_t *type, bio_writer_t *writer) { - switch (this->state) + if (this->tls->get_version_max(this->tls) < TLS_1_3) { - case STATE_INIT: - return send_client_hello(this, type, writer); - case STATE_HELLO_DONE: - if (this->peer) - { - return send_certificate(this, type, writer); - } - /* otherwise fall through to next state */ - case STATE_CERT_SENT: - return send_key_exchange(this, type, writer); - case STATE_KEY_EXCHANGE_SENT: - if (this->peer) - { - return send_certificate_verify(this, type, writer); - } - else - { + switch (this->state) + { + case STATE_INIT: + return send_client_hello(this, type, writer); + case STATE_HELLO_DONE: + if (this->peer) + { + return send_certificate(this, type, writer); + } + /* otherwise fall through to next state */ + case STATE_CERT_SENT: + return send_key_exchange(this, type, writer); + case STATE_KEY_EXCHANGE_SENT: + if (this->peer) + { + return send_certificate_verify(this, type, writer); + } + else + { + return INVALID_STATE; + } + case STATE_CIPHERSPEC_CHANGED_OUT: + return send_finished(this, type, writer); + default: return INVALID_STATE; - } - case STATE_CIPHERSPEC_CHANGED_OUT: - return send_finished(this, type, writer); - default: - return INVALID_STATE; + } } + else + { + switch (this->state) + { + case STATE_INIT: + return send_client_hello(this, type, writer); + case STATE_HELLO_DONE: + /* otherwise fall through to next state */ + case STATE_CIPHERSPEC_CHANGED_OUT: + case STATE_FINISHED_RECEIVED: + /* fall through since legacy TLS and TLS 1.3 + * expect the same message */ + return send_finished(this, type, writer); + case STATE_FINISHED_SENT: + this->crypto->derive_app_secret(this->crypto); + this->crypto->change_cipher(this->crypto, TRUE); + this->crypto->change_cipher(this->crypto, FALSE); + this->state = STATE_FINISHED_SENT_KEY_SWITCHED; + return SUCCESS; + case STATE_FINISHED_SENT_KEY_SWITCHED: + return SUCCESS; + default: + return INVALID_STATE; + } + } + } METHOD(tls_handshake_t, cipherspec_changed, bool, private_tls_peer_t *this, bool inbound) { - if (inbound) + if (this->tls->get_version_max(this->tls) < TLS_1_3) { - if (this->resume) + if (inbound) { - return this->state == STATE_HELLO_RECEIVED; + if (this->resume) + { + return this->state == STATE_HELLO_RECEIVED; + } + return this->state == STATE_FINISHED_SENT; + } + else + { + if (this->resume) + { + return this->state == STATE_FINISHED_RECEIVED; + } + if (this->peer) + { + return this->state == STATE_VERIFY_SENT; + } + return this->state == STATE_KEY_EXCHANGE_SENT; + } - return this->state == STATE_FINISHED_SENT; } else { - if (this->resume) + if (inbound) { - return this->state == STATE_FINISHED_RECEIVED; + return this->state == STATE_HELLO_RECEIVED; } - if (this->peer) + else { - return this->state == STATE_VERIFY_SENT; + return FALSE; } - return this->state == STATE_KEY_EXCHANGE_SENT; } + } METHOD(tls_handshake_t, change_cipherspec, void, private_tls_peer_t *this, bool inbound) { - this->crypto->change_cipher(this->crypto, inbound); + if (this->tls->get_version_max(this->tls) < TLS_1_3) + { + this->crypto->change_cipher(this->crypto, inbound); + } + if (inbound) { this->state = STATE_CIPHERSPEC_CHANGED_IN; @@ -1161,11 +1607,19 @@ METHOD(tls_handshake_t, change_cipherspec, void, METHOD(tls_handshake_t, finished, bool, private_tls_peer_t *this) { - if (this->resume) + if (this->tls->get_version_max(this->tls) < TLS_1_3) { + if (this->resume) + { return this->state == STATE_FINISHED_SENT; + } + + return this->state == STATE_FINISHED_RECEIVED; + } + else + { + return this->state == STATE_FINISHED_SENT_KEY_SWITCHED; } - return this->state == STATE_FINISHED_RECEIVED; } METHOD(tls_handshake_t, get_peer_id, identification_t*, diff --git a/src/libtls/tls_protection.c b/src/libtls/tls_protection.c index 1666d664a..d286e179a 100644 --- a/src/libtls/tls_protection.c +++ b/src/libtls/tls_protection.c @@ -73,23 +73,19 @@ METHOD(tls_protection_t, process, status_t, return NEED_MORE; } - if (this->aead_in) + if (this-> version < TLS_1_3 || + type == TLS_APPLICATION_DATA) { - if (!this->aead_in->decrypt(this->aead_in, this->version, - &type, this->seq_in, &data)) + if (this->aead_in) { - DBG1(DBG_TLS, "TLS record decryption failed"); - this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC); - return NEED_MORE; + if (!this->aead_in->decrypt(this->aead_in, this->version, + &type, this->seq_in, &data)) + { + DBG1(DBG_TLS, "TLS record decryption failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC); + return NEED_MORE; + } } - } - - if (type == TLS_CHANGE_CIPHER_SPEC) - { - this->seq_in = 0; - } - else - { this->seq_in++; } return this->compression->process(this->compression, type, data); @@ -103,9 +99,8 @@ METHOD(tls_protection_t, build, status_t, status = this->compression->build(this->compression, type, data); if (status == NEED_MORE) { - if (*type == TLS_CHANGE_CIPHER_SPEC) + if (this-> version < TLS_1_3 && *type == TLS_CHANGE_CIPHER_SPEC) { - this->seq_out = 0; return status; } if (this->aead_out) @@ -129,10 +124,12 @@ METHOD(tls_protection_t, set_cipher, void, if (inbound) { this->aead_in = aead; + this->seq_in = 0; } else { this->aead_out = aead; + this->seq_out = 0; } } diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 70d17f22c..9714df0f6 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -256,7 +256,7 @@ static status_t process_client_hello(private_tls_server_t *this, case TLS_EXT_SIGNATURE_ALGORITHMS: this->hashsig = chunk_clone(ext); break; - case TLS_EXT_ELLIPTIC_CURVES: + case TLS_EXT_SUPPORTED_GROUPS: this->curves_received = TRUE; this->curves = chunk_clone(ext); break; @@ -299,7 +299,7 @@ static status_t process_client_hello(private_tls_server_t *this, this->session = chunk_clone(session); this->resume = TRUE; DBG1(DBG_TLS, "resumed %N using suite %N", - tls_version_names, this->tls->get_version(this->tls), + tls_version_names, this->tls->get_version_max(this->tls), tls_cipher_suite_names, this->suite); } else @@ -324,7 +324,7 @@ static status_t process_client_hello(private_tls_server_t *this, } DESTROY_IF(rng); DBG1(DBG_TLS, "negotiated %N using suite %N", - tls_version_names, this->tls->get_version(this->tls), + tls_version_names, this->tls->get_version_max(this->tls), tls_cipher_suite_names, this->suite); } this->state = STATE_HELLO_RECEIVED; @@ -688,7 +688,7 @@ static status_t send_server_hello(private_tls_server_t *this, tls_handshake_type_t *type, bio_writer_t *writer) { /* TLS version */ - writer->write_uint16(writer, this->tls->get_version(this->tls)); + writer->write_uint16(writer, this->tls->get_version_max(this->tls)); writer->write_data(writer, chunk_from_thing(this->server_random)); /* session identifier if we have one */ @@ -774,7 +774,7 @@ static status_t send_certificate_request(private_tls_server_t *this, supported->write_uint8(supported, TLS_ECDSA_SIGN); writer->write_data8(writer, supported->get_buf(supported)); supported->destroy(supported); - if (this->tls->get_version(this->tls) >= TLS_1_2) + if (this->tls->get_version_max(this->tls) >= TLS_1_2) { this->crypto->get_signature_algorithms(this->crypto, writer); } @@ -805,11 +805,11 @@ static status_t send_certificate_request(private_tls_server_t *this, /** * Get the TLS curve of a given EC DH group */ -static tls_named_curve_t ec_group_to_curve(private_tls_server_t *this, - diffie_hellman_group_t group) +static tls_named_group_t ec_group_to_curve(private_tls_server_t *this, + diffie_hellman_group_t group) { diffie_hellman_group_t current; - tls_named_curve_t curve; + tls_named_group_t curve; enumerator_t *enumerator; enumerator = this->crypto->create_ec_enumerator(this->crypto); @@ -828,7 +828,7 @@ static tls_named_curve_t ec_group_to_curve(private_tls_server_t *this, /** * Check if the peer supports a given TLS curve */ -bool peer_supports_curve(private_tls_server_t *this, tls_named_curve_t curve) +bool peer_supports_curve(private_tls_server_t *this, tls_named_group_t curve) { bio_reader_t *reader; uint16_t current; @@ -854,9 +854,9 @@ bool peer_supports_curve(private_tls_server_t *this, tls_named_curve_t curve) * Try to find a curve supported by both, client and server */ static bool find_supported_curve(private_tls_server_t *this, - tls_named_curve_t *curve) + tls_named_group_t *curve) { - tls_named_curve_t current; + tls_named_group_t current; enumerator_t *enumerator; enumerator = this->crypto->create_ec_enumerator(this->crypto); @@ -881,7 +881,7 @@ static status_t send_server_key_exchange(private_tls_server_t *this, diffie_hellman_group_t group) { diffie_hellman_params_t *params = NULL; - tls_named_curve_t curve; + tls_named_group_t curve; chunk_t chunk; if (diffie_hellman_group_is_ec(group)) @@ -894,7 +894,7 @@ static status_t send_server_key_exchange(private_tls_server_t *this, this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); return NEED_MORE; } - DBG2(DBG_TLS, "selected ECDH group %N", tls_named_curve_names, curve); + DBG2(DBG_TLS, "selected ECDH group %N", tls_named_group_names, curve); writer->write_uint8(writer, TLS_ECC_NAMED_CURVE); writer->write_uint16(writer, curve); } From 4099035a0ce9584b2c196460e015301a34d1ca32 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Aug 2020 13:09:55 +0200 Subject: [PATCH 006/107] tls-test: Make address family configurable and simplify DNS/socket handling --- scripts/tls_test.c | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/scripts/tls_test.c b/scripts/tls_test.c index 316c98625..c47c1cae1 100644 --- a/scripts/tls_test.c +++ b/scripts/tls_test.c @@ -92,8 +92,8 @@ static int run_client(host_t *host, identification_t *server, while (times == -1 || times-- > 0) { - /* Open IPv4 socket */ - fd = socket(AF_INET, SOCK_STREAM, 0); + DBG2(DBG_TLS, "connecting to %#H", host); + fd = socket(host->get_family(host), SOCK_STREAM, 0); if (fd == -1) { DBG1(DBG_TLS, "opening socket failed: %s", strerror(errno)); @@ -102,18 +102,9 @@ static int run_client(host_t *host, identification_t *server, if (connect(fd, host->get_sockaddr(host), *host->get_sockaddr_len(host)) == -1) { - /* Check if the socket should use IPv6 instead */ - fd = socket(AF_INET6, SOCK_STREAM, 0); - { - if (connect(fd, host->get_sockaddr(host), - *host->get_sockaddr_len(host)) == -1) - { - DBG1(DBG_TLS, "connecting to %#H failed: %s", host, strerror(errno)); - close(fd); - return 1; - } - } - + DBG1(DBG_TLS, "connecting to %#H failed: %s", host, strerror(errno)); + close(fd); + return 1; } tls = tls_socket_create(FALSE, server, client, fd, cache, TLS_1_3, TRUE); @@ -278,7 +269,7 @@ int main(int argc, char *argv[]) { char *address = NULL; bool listen = FALSE; - int port = 0, times = -1, res; + int port = 0, times = -1, res, family = AF_UNSPEC; identification_t *server, *client; tls_cache_t *cache; host_t *host; @@ -295,6 +286,8 @@ int main(int argc, char *argv[]) {"cert", required_argument, NULL, 'x' }, {"key", required_argument, NULL, 'k' }, {"times", required_argument, NULL, 't' }, + {"ipv4", no_argument, NULL, '4' }, + {"ipv6", no_argument, NULL, '6' }, {"debug", required_argument, NULL, 'd' }, {0,0,0,0 } }; @@ -337,6 +330,12 @@ int main(int argc, char *argv[]) case 'd': tls_level = atoi(optarg); continue; + case '4': + family = AF_INET; + continue; + case '6': + family = AF_INET6; + continue; default: usage(stderr, argv[0]); return 1; @@ -348,17 +347,11 @@ int main(int argc, char *argv[]) usage(stderr, argv[0]); return 1; } - /* Try to connect with IPv4 first*/ - host = host_create_from_dns(address, AF_INET, port); + host = host_create_from_dns(address, family, port); if (!host) { - /* If it fails, try IPv6*/ - host = host_create_from_dns(address, AF_INET6, port); - if (!host) - { - DBG1(DBG_TLS, "resolving hostname %s failed", address); - return 1; - } + DBG1(DBG_TLS, "resolving hostname %s failed", address); + return 1; } server = identification_create_from_string(address); cache = tls_cache_create(100, 30); From ba3c90ded16b122163ccc7baf85cf803d7c2c8a6 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Aug 2020 16:32:40 +0200 Subject: [PATCH 007/107] libtls: Some code style fixes --- src/libtls/tls_fragmentation.c | 2 +- src/libtls/tls_peer.c | 3 +-- src/libtls/tls_protection.c | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libtls/tls_fragmentation.c b/src/libtls/tls_fragmentation.c index f74cf59bf..483613fff 100644 --- a/src/libtls/tls_fragmentation.c +++ b/src/libtls/tls_fragmentation.c @@ -296,7 +296,7 @@ METHOD(tls_fragmentation_t, process, status_t, { break; } - /* fall */ + /* fall-through */ case TLS_APPLICATION_DATA: status = process_application(this, reader); break; diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 471ba49f7..dea39fb16 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -1611,9 +1611,8 @@ METHOD(tls_handshake_t, finished, bool, { if (this->resume) { - return this->state == STATE_FINISHED_SENT; + return this->state == STATE_FINISHED_SENT; } - return this->state == STATE_FINISHED_RECEIVED; } else diff --git a/src/libtls/tls_protection.c b/src/libtls/tls_protection.c index d286e179a..3d0ec489b 100644 --- a/src/libtls/tls_protection.c +++ b/src/libtls/tls_protection.c @@ -73,7 +73,7 @@ METHOD(tls_protection_t, process, status_t, return NEED_MORE; } - if (this-> version < TLS_1_3 || + if (this->version < TLS_1_3 || type == TLS_APPLICATION_DATA) { if (this->aead_in) @@ -99,7 +99,7 @@ METHOD(tls_protection_t, build, status_t, status = this->compression->build(this->compression, type, data); if (status == NEED_MORE) { - if (this-> version < TLS_1_3 && *type == TLS_CHANGE_CIPHER_SPEC) + if (*type == TLS_CHANGE_CIPHER_SPEC && this->version < TLS_1_3) { return status; } From b7ea969b32416445671a2ce0b4e374511f87e230 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 26 Aug 2020 12:18:06 +0200 Subject: [PATCH 008/107] tls-crypto: Use correct key length for ChaCha20/Poly1305 --- src/libtls/tls_crypto.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 6b4651393..a48393f38 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -494,7 +494,7 @@ static suite_algs_t suite_algs[] = { { TLS_CHACHA20_POLY1305_SHA256, KEY_ANY, MODP_NONE, HASH_SHA256, PRF_UNDEFINED, - AUTH_HMAC_SHA2_256_256, ENCR_CHACHA20_POLY1305, 16, + AUTH_HMAC_SHA2_256_256, ENCR_CHACHA20_POLY1305, 32, TLS_1_3, }, { TLS_AES_128_CCM_SHA256, From 436571b2f049d45c00d974e51a12d397eb28bf53 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Aug 2020 13:47:00 +0200 Subject: [PATCH 009/107] tls-crypto: Correctly filter cipher suites based on PRF algorithms The previous check operated on the first array element. --- src/libtls/tls_crypto.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index a48393f38..311299bea 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -823,8 +823,12 @@ static void filter_suite(suite_algs_t suites[], int *count, int offset, } } if (current.prf && current.prf != suites[i].prf) - { /* skip, PRF does not match */ - continue; + { + if (suites[i].prf != PRF_UNDEFINED) + { + /* skip, PRF does not match nor is it undefined */ + continue; + } } if (current.hash && current.hash != suites[i].hash) { /* skip, hash does not match */ @@ -1108,13 +1112,10 @@ static void filter_specific_config_suites(private_tls_crypto_t *this, static void filter_unsupported_suites(suite_algs_t suites[], int *count) { /* filter suite list by each algorithm */ - if (suites->tls_version < TLS_1_3) - { - filter_suite(suites, count, offsetof(suite_algs_t, encr), - lib->crypto->create_aead_enumerator); - filter_suite(suites, count, offsetof(suite_algs_t, prf), - lib->crypto->create_prf_enumerator); - } + filter_suite(suites, count, offsetof(suite_algs_t, encr), + lib->crypto->create_aead_enumerator); + filter_suite(suites, count, offsetof(suite_algs_t, prf), + lib->crypto->create_prf_enumerator); filter_suite(suites, count, offsetof(suite_algs_t, encr), lib->crypto->create_crypter_enumerator); filter_suite(suites, count, offsetof(suite_algs_t, mac), From 281766c5e6604a2d6c7885a4769bd67838f99d37 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Aug 2020 13:54:08 +0200 Subject: [PATCH 010/107] tls-crypto: Filter TLS cipher suites by min/max version There is no point proposing legacy (or future) cipher suites depending on the proposed TLS versions. It was actually possible to negotiate and use cipher suites only defined for TLS 1.2 with earlier TLS versions. --- src/libtls/tests/suites/test_socket.c | 20 ++-- src/libtls/tls_crypto.c | 136 ++++++++++++++------------ src/libtls/tls_crypto.h | 4 +- 3 files changed, 88 insertions(+), 72 deletions(-) diff --git a/src/libtls/tests/suites/test_socket.c b/src/libtls/tests/suites/test_socket.c index af7eaefd3..a637096cd 100644 --- a/src/libtls/tests/suites/test_socket.c +++ b/src/libtls/tests/suites/test_socket.c @@ -426,8 +426,7 @@ static void test_tls(tls_version_t version, uint16_t port, bool cauth, u_int i) start_echo_server(config); - count = tls_crypto_get_supported_suites(TRUE, &suites); - + count = tls_crypto_get_supported_suites(TRUE, version, &suites); ck_assert(i < count); snprintf(suite, sizeof(suite), "%N", tls_cipher_suite_names, suites[i]); lib->settings->set_str(lib->settings, "%s.tls.suites", suite, lib->ns); @@ -482,40 +481,41 @@ Suite *socket_suite_create() { Suite *s; TCase *tc; - int count; - count = tls_crypto_get_supported_suites(TRUE, NULL); +#define add_tls_test(func, version) \ + tcase_add_loop_test(tc, func, 0, \ + tls_crypto_get_supported_suites(TRUE, version, NULL)); s = suite_create("socket"); tc = tcase_create("TLS 1.2/anon"); tcase_add_checked_fixture(tc, setup_creds, teardown_creds); - tcase_add_loop_test(tc, test_tls12, 0, count); + add_tls_test(test_tls12, TLS_1_2); suite_add_tcase(s, tc); tc = tcase_create("TLS 1.2/mutl"); tcase_add_checked_fixture(tc, setup_creds, teardown_creds); - tcase_add_loop_test(tc, test_tls12_mutual, 0, count); + add_tls_test(test_tls12_mutual, TLS_1_2); suite_add_tcase(s, tc); tc = tcase_create("TLS 1.1/anon"); tcase_add_checked_fixture(tc, setup_creds, teardown_creds); - tcase_add_loop_test(tc, test_tls11, 0, count); + add_tls_test(test_tls11, TLS_1_1); suite_add_tcase(s, tc); tc = tcase_create("TLS 1.1/mutl"); tcase_add_checked_fixture(tc, setup_creds, teardown_creds); - tcase_add_loop_test(tc, test_tls11_mutual, 0, count); + add_tls_test(test_tls11_mutual, TLS_1_1); suite_add_tcase(s, tc); tc = tcase_create("TLS 1.0/anon"); tcase_add_checked_fixture(tc, setup_creds, teardown_creds); - tcase_add_loop_test(tc, test_tls10, 0, count); + add_tls_test(test_tls10, TLS_1_0); suite_add_tcase(s, tc); tc = tcase_create("TLS 1.0/mutl"); tcase_add_checked_fixture(tc, setup_creds, teardown_creds); - tcase_add_loop_test(tc, test_tls10_mutual, 0, count); + add_tls_test(test_tls10_mutual, TLS_1_0); suite_add_tcase(s, tc); return s; diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 311299bea..0d296b521 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -470,7 +470,8 @@ typedef struct { integrity_algorithm_t mac; encryption_algorithm_t encr; size_t encr_size; - tls_version_t tls_version; + tls_version_t min_version; + tls_version_t max_version; } suite_algs_t; /** @@ -483,278 +484,278 @@ static suite_algs_t suite_algs[] = { KEY_ANY, MODP_NONE, HASH_SHA256, PRF_UNDEFINED, AUTH_HMAC_SHA2_256_256, ENCR_AES_GCM_ICV16, 16, - TLS_1_3, + TLS_1_3, TLS_1_3, }, { TLS_AES_256_GCM_SHA384, KEY_ANY, MODP_NONE, HASH_SHA384, PRF_UNDEFINED, AUTH_HMAC_SHA2_384_384, ENCR_AES_GCM_ICV16, 32, - TLS_1_3, + TLS_1_3, TLS_1_3, }, { TLS_CHACHA20_POLY1305_SHA256, KEY_ANY, MODP_NONE, HASH_SHA256, PRF_UNDEFINED, AUTH_HMAC_SHA2_256_256, ENCR_CHACHA20_POLY1305, 32, - TLS_1_3, + TLS_1_3, TLS_1_3, }, { TLS_AES_128_CCM_SHA256, - KEY_ANY, MODP_NONE, - HASH_SHA256, PRF_UNDEFINED, - AUTH_HMAC_SHA2_256_256, ENCR_AES_CCM_ICV16, 16, - TLS_1_3, + KEY_ANY, MODP_NONE, + HASH_SHA256, PRF_UNDEFINED, + AUTH_HMAC_SHA2_256_256, ENCR_AES_CCM_ICV16, 16, + TLS_1_3, TLS_1_3, }, { TLS_AES_128_CCM_8_SHA256, - KEY_ANY, MODP_NONE, - HASH_SHA256, PRF_UNDEFINED, - AUTH_HMAC_SHA2_256_256, ENCR_AES_CCM_ICV8, 16, - TLS_1_3, + KEY_ANY, MODP_NONE, + HASH_SHA256, PRF_UNDEFINED, + AUTH_HMAC_SHA2_256_256, ENCR_AES_CCM_ICV8, 16, + TLS_1_3, TLS_1_3, }, /* Legacy TLS cipher suites */ { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, KEY_ECDSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16, - TLS_1_2, + TLS_1_0, TLS_1_2, }, { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, KEY_ECDSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16, - TLS_1_2, + TLS_1_2, TLS_1_2, }, { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, KEY_ECDSA, ECP_384_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32, - TLS_1_2, + TLS_1_0, TLS_1_2, }, { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, KEY_ECDSA, ECP_384_BIT, HASH_SHA384, PRF_HMAC_SHA2_384, AUTH_HMAC_SHA2_384_384, ENCR_AES_CBC, 32, - TLS_1_2, + TLS_1_2, TLS_1_2, }, { TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, KEY_ECDSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16, - TLS_1_2, + TLS_1_2, TLS_1_2, }, { TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, KEY_ECDSA, ECP_384_BIT, HASH_SHA384, PRF_HMAC_SHA2_384, AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32, - TLS_1_2, + TLS_1_2, TLS_1_2, }, { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, KEY_RSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16, - TLS_1_2, + TLS_1_0, TLS_1_2, }, { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, KEY_RSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16, - TLS_1_2, + TLS_1_2, TLS_1_2, }, { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, KEY_RSA, ECP_384_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32, - TLS_1_2, + TLS_1_0, TLS_1_2, }, { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, KEY_RSA, ECP_384_BIT, HASH_SHA384, PRF_HMAC_SHA2_384, AUTH_HMAC_SHA2_384_384, ENCR_AES_CBC, 32, - TLS_1_2, + TLS_1_2, TLS_1_2, }, { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, KEY_RSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16, - TLS_1_2, + TLS_1_2, TLS_1_2, }, { TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, KEY_RSA, ECP_384_BIT, HASH_SHA384, PRF_HMAC_SHA2_384, AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32, - TLS_1_2, + TLS_1_2, TLS_1_2, }, { TLS_DHE_RSA_WITH_AES_128_CBC_SHA, KEY_RSA, MODP_2048_BIT, HASH_SHA256,PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16, - TLS_1_2, + SSL_3_0, TLS_1_2, }, { TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, KEY_RSA, MODP_3072_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16, - TLS_1_2, + TLS_1_2, TLS_1_2, }, { TLS_DHE_RSA_WITH_AES_256_CBC_SHA, KEY_RSA, MODP_3072_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32, - TLS_1_2, + SSL_3_0, TLS_1_2, }, { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, KEY_RSA, MODP_4096_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 32, - TLS_1_2, + TLS_1_2, TLS_1_2, }, { TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, KEY_RSA, MODP_3072_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16, - TLS_1_2, + TLS_1_2, TLS_1_2, }, { TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, KEY_RSA, MODP_4096_BIT, HASH_SHA384, PRF_HMAC_SHA2_384, AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32, - TLS_1_2, + TLS_1_2, TLS_1_2, }, { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, KEY_RSA, MODP_2048_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 16, - TLS_1_2, + SSL_3_0, TLS_1_2, }, { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, KEY_RSA, MODP_3072_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 16, - TLS_1_2, + TLS_1_2, TLS_1_2, }, { TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, KEY_RSA, MODP_3072_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 32, - TLS_1_2, + SSL_3_0, TLS_1_2, }, { TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256, KEY_RSA, MODP_4096_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 32, - TLS_1_2, + TLS_1_2, TLS_1_2, }, { TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, KEY_RSA, MODP_2048_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_3DES, 0, - TLS_1_2, + SSL_3_0, TLS_1_2, }, { TLS_RSA_WITH_AES_128_CBC_SHA, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16, - TLS_1_2, + SSL_3_0, TLS_1_2, }, { TLS_RSA_WITH_AES_128_CBC_SHA256, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16, - TLS_1_2, + TLS_1_2, TLS_1_2, }, { TLS_RSA_WITH_AES_256_CBC_SHA, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32, - TLS_1_2, + SSL_3_0, TLS_1_2, }, { TLS_RSA_WITH_AES_256_CBC_SHA256, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 32, - TLS_1_2, + TLS_1_2, TLS_1_2, }, { TLS_RSA_WITH_AES_128_GCM_SHA256, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16, - TLS_1_2, + TLS_1_2, TLS_1_2, }, { TLS_RSA_WITH_AES_256_GCM_SHA384, KEY_RSA, MODP_NONE, HASH_SHA384, PRF_HMAC_SHA2_384, AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32, - TLS_1_2, + TLS_1_2, TLS_1_2, }, { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 16, - TLS_1_2, + SSL_3_0, TLS_1_2, }, { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 16, - TLS_1_2, + TLS_1_2, TLS_1_2, }, { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 32, - TLS_1_2, + SSL_3_0, TLS_1_2, }, { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 32, - TLS_1_2, + TLS_1_2, TLS_1_2, }, { TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, KEY_ECDSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_3DES, 0, - TLS_1_2, + TLS_1_0, TLS_1_2, }, { TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, KEY_RSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_3DES, 0, - TLS_1_2, + TLS_1_0, TLS_1_2, }, { TLS_RSA_WITH_3DES_EDE_CBC_SHA, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_3DES, 0, - TLS_1_2, + SSL_3_0, TLS_1_2, }, { TLS_ECDHE_ECDSA_WITH_NULL_SHA, KEY_ECDSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_NULL, 0, - TLS_1_2, + TLS_1_0, TLS_1_2, }, { TLS_ECDHE_RSA_WITH_NULL_SHA, KEY_ECDSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_NULL, 0, - TLS_1_2, + TLS_1_0, TLS_1_2, }, { TLS_RSA_WITH_NULL_SHA, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_NULL, 0, - TLS_1_2, + SSL_3_0, TLS_1_2, }, { TLS_RSA_WITH_NULL_SHA256, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA2_256_256, ENCR_NULL, 0, - TLS_1_2, + TLS_1_2, TLS_1_2, }, { TLS_RSA_WITH_NULL_MD5, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_MD5_128, ENCR_NULL, 0, - TLS_1_2, + SSL_2_0, TLS_1_2, }, }; @@ -1133,12 +1134,20 @@ static void build_cipher_suite_list(private_tls_crypto_t *this, bool require_encryption) { suite_algs_t suites[countof(suite_algs)]; - int count = countof(suite_algs), i; + tls_version_t min_version, max_version; + int count = 0, i; - /* copy all suites */ - for (i = 0; i < count; i++) + min_version = this->tls->get_version_min(this->tls); + max_version = this->tls->get_version_max(this->tls); + + /* copy all suites appropriate for the current min/max versions */ + for (i = 0; i < countof(suite_algs); i++) { - suites[i] = suite_algs[i]; + if (suite_algs[i].min_version <= max_version && + suite_algs[i].max_version >= min_version) + { + suites[count++] = suite_algs[i]; + } } if (require_encryption) @@ -2334,15 +2343,20 @@ tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache) /** * See header. */ -int tls_crypto_get_supported_suites(bool null, tls_cipher_suite_t **out) +int tls_crypto_get_supported_suites(bool null, tls_version_t version, + tls_cipher_suite_t **out) { suite_algs_t suites[countof(suite_algs)]; - int count = countof(suite_algs), i; + int count = 0, i; /* initialize copy of suite list */ - for (i = 0; i < count; i++) + for (i = 0; i < countof(suite_algs); i++) { - suites[i] = suite_algs[i]; + if (suite_algs[i].min_version <= version && + suite_algs[i].max_version >= version) + { + suites[count++] = suite_algs[i]; + } } filter_unsupported_suites(suites, &count); diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h index a1390e349..045190656 100644 --- a/src/libtls/tls_crypto.h +++ b/src/libtls/tls_crypto.h @@ -615,9 +615,11 @@ tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache); * Get a list of all supported TLS cipher suites. * * @param null include supported NULL encryption suites + * @param version TLS version * @param suites pointer to allocated suites array, to free(), or NULL * @return number of suites supported */ -int tls_crypto_get_supported_suites(bool null, tls_cipher_suite_t **suites); +int tls_crypto_get_supported_suites(bool null, tls_version_t version, + tls_cipher_suite_t **suites); #endif /** TLS_CRYPTO_H_ @}*/ From 43c8f950a7bce88c8fc36948137dbe969f770744 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Aug 2020 15:46:29 +0200 Subject: [PATCH 011/107] tls-crypto: Delay instantiation of cipher suites This way we can take into account the version set via setter on tls_t. --- src/libtls/tls_crypto.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 0d296b521..bef8184ee 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1130,13 +1130,28 @@ static void filter_unsupported_suites(suite_algs_t suites[], int *count) /** * Initialize the cipher suite list */ -static void build_cipher_suite_list(private_tls_crypto_t *this, - bool require_encryption) +static void build_cipher_suite_list(private_tls_crypto_t *this) { suite_algs_t suites[countof(suite_algs)]; tls_version_t min_version, max_version; + bool require_encryption; int count = 0, i; + switch (this->tls->get_purpose(this->tls)) + { + case TLS_PURPOSE_EAP_TLS: + case TLS_PURPOSE_GENERIC_NULLOK: + require_encryption = FALSE; + break; + case TLS_PURPOSE_EAP_PEAP: + case TLS_PURPOSE_EAP_TTLS: + case TLS_PURPOSE_GENERIC: + require_encryption = TRUE; + break; + default: + return; + } + min_version = this->tls->get_version_min(this->tls); max_version = this->tls->get_version_max(this->tls); @@ -1186,6 +1201,10 @@ static void build_cipher_suite_list(private_tls_crypto_t *this, METHOD(tls_crypto_t, get_cipher_suites, int, private_tls_crypto_t *this, tls_cipher_suite_t **suites) { + if (!this->suites) + { + build_cipher_suite_list(this); + } *suites = this->suites; return this->suite_count; } @@ -1337,6 +1356,11 @@ METHOD(tls_crypto_t, select_cipher_suite, tls_cipher_suite_t, suite_algs_t *algs; int i, j; + if (!this->suites) + { + build_cipher_suite_list(this); + } + for (i = 0; i < this->suite_count; i++) { for (j = 0; j < count; j++) @@ -2317,22 +2341,13 @@ tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache) case TLS_PURPOSE_EAP_TLS: /* MSK PRF ASCII constant label according to EAP-TLS RFC 5216 */ this->msk_label = "client EAP encryption"; - build_cipher_suite_list(this, FALSE); break; case TLS_PURPOSE_EAP_PEAP: this->msk_label = "client EAP encryption"; - build_cipher_suite_list(this, TRUE); break; case TLS_PURPOSE_EAP_TTLS: /* MSK PRF ASCII constant label according to EAP-TTLS RFC 5281 */ this->msk_label = "ttls keying material"; - build_cipher_suite_list(this, TRUE); - break; - case TLS_PURPOSE_GENERIC: - build_cipher_suite_list(this, TRUE); - break; - case TLS_PURPOSE_GENERIC_NULLOK: - build_cipher_suite_list(this, FALSE); break; default: break; From 53ba0801ac69d9134321cca50200a20712f65ed3 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Aug 2020 13:14:05 +0200 Subject: [PATCH 012/107] tls-crypto: Simplify hash algorithm handling --- src/libtls/tls_crypto.c | 101 ++++------------------------------------ 1 file changed, 9 insertions(+), 92 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index bef8184ee..fea04a9eb 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1,4 +1,9 @@ /* + * Copyright (C) 2020 Tobias Brunner + * Copyright (C) 2020 Pascal Knecht + * Copyright (C) 2020 Méline Sieber + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2010-2014 Martin Willi * Copyright (C) 2010-2014 revosec AG * @@ -391,35 +396,6 @@ struct private_tls_crypto_t { */ bool ecdsa; - /** - * MD5 supported? - */ - bool md5; - - /** - * SHA1 supported? - */ - bool sha1; - /** - * SHA224 supported? - */ - bool sha224; - - /* - * SHA256 supported? - */ - bool sha256; - - /** - * SHA384 supported? - */ - bool sha384; - - /** - * SHA512 supported? - */ - bool sha512; - /** * TLS context */ @@ -1422,9 +1398,11 @@ METHOD(tls_crypto_t, get_signature_algorithms, void, private_tls_crypto_t *this, bio_writer_t *writer) { bio_writer_t *supported; + tls_version_t version; int i; supported = bio_writer_create(32); + version = this->tls->get_version_max(this->tls); for (i = 0; i < countof(schemes); i++) { @@ -1436,27 +1414,11 @@ METHOD(tls_crypto_t, get_signature_algorithms, void, { continue; } - if (schemes[i].hash == TLS_HASH_MD5 && !this->md5) + if (schemes[i].hash == TLS_HASH_MD5 && version >= TLS_1_3) { continue; } - if (schemes[i].hash == TLS_HASH_SHA1 && !this->sha1) - { - continue; - } - if (schemes[i].hash == TLS_HASH_SHA224 && !this->sha224) - { - continue; - } - if (schemes[i].hash == TLS_HASH_SHA256 && !this->sha256) - { - continue; - } - if (schemes[i].hash == TLS_HASH_SHA384 && !this->sha384) - { - continue; - } - if (schemes[i].hash == TLS_HASH_SHA512 && !this->sha512) + if (schemes[i].hash == TLS_HASH_SHA224 && version >= TLS_1_3) { continue; } @@ -2242,8 +2204,6 @@ tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache) enumerator_t *enumerator; credential_type_t type; int subtype; - int hash_algorithm; - const char *plugin; INIT(this, .public = { @@ -2293,49 +2253,6 @@ tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache) } enumerator->destroy(enumerator); - enumerator = lib->crypto->create_hasher_enumerator(lib->crypto); - while (enumerator->enumerate(enumerator, &hash_algorithm, &plugin)) - { - switch (hash_algorithm) - { - case TLS_HASH_MD5: - if (tls->get_version_max(tls) < TLS_1_3) - { - this->md5 = TRUE; - } - else - { - this->md5 = FALSE; - } - break; - case TLS_HASH_SHA1: - this->sha1 = TRUE; - break; - case TLS_HASH_SHA224: - if (tls->get_version_max(tls) < TLS_1_3) - { - this->sha224 = TRUE; - } - else - { - this->sha224 = FALSE; - } - break; - case TLS_HASH_SHA384: - this->sha384 = TRUE; - break; - case TLS_HASH_SHA256: - this->sha256 = TRUE; - break; - case TLS_HASH_SHA512: - this->sha512 = TRUE; - break; - default: - continue; - } - } - enumerator->destroy(enumerator); - switch (tls->get_purpose(tls)) { case TLS_PURPOSE_EAP_TLS: From 3101120c75ef14530d034614c7ae353066912bba Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Aug 2020 13:16:52 +0200 Subject: [PATCH 013/107] tls-crypto: Enumerate x25519/448 and rename constant for consistency --- src/libtls/tls_crypto.c | 6 ++++-- src/libtls/tls_crypto.h | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index fea04a9eb..77fc15309 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -320,11 +320,11 @@ ENUM_BEGIN(tls_named_group_names, TLS_SECT163K1, TLS_SECP521R1, "SECP384R1", "SECP521R1", ); -ENUM_NEXT(tls_named_group_names, TLS_CURVE25519, TLS_CURVE_448, TLS_SECP521R1, +ENUM_NEXT(tls_named_group_names, TLS_CURVE25519, TLS_CURVE448, TLS_SECP521R1, "CURVE25519", "CURVE448", ); -ENUM_NEXT(tls_named_group_names, TLS_FFDHE2048, TLS_FFDHE8192, TLS_CURVE_448, +ENUM_NEXT(tls_named_group_names, TLS_FFDHE2048, TLS_FFDHE8192, TLS_CURVE448, "FFDHE2048", "FFDHE3072", "FFDHE4096", @@ -1471,6 +1471,8 @@ static struct { { ECP_521_BIT, TLS_SECP521R1}, { ECP_224_BIT, TLS_SECP224R1}, { ECP_192_BIT, TLS_SECP192R1}, + { CURVE_25519, TLS_CURVE25519}, + { CURVE_448, TLS_CURVE448}, }; CALLBACK(group_filter, bool, diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h index 045190656..5cd007241 100644 --- a/src/libtls/tls_crypto.h +++ b/src/libtls/tls_crypto.h @@ -362,7 +362,7 @@ enum tls_named_group_t { /* TLS 1.3: new ecdhe, dhe groups */ TLS_CURVE25519 = 29, - TLS_CURVE_448 = 30, + TLS_CURVE448 = 30, TLS_FFDHE2048 = 256, TLS_FFDHE3072 = 257, TLS_FFDHE4096 = 258, From 8495138d4a8a0175e4c1cad1dbe883be49ecf8e6 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 26 Aug 2020 15:23:53 +0200 Subject: [PATCH 014/107] tls-peer: Support x25519/448 for TLS 1.2 These DH groups don't use the point format prefix (RFC 8422 deprecated any other format anyway). Since they are enumerated now, they can also be used by servers for TLS 1.2. --- src/libtls/tls_peer.c | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index dea39fb16..870c75573 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -723,14 +723,20 @@ static status_t process_ec_key_exchange(private_tls_peer_t *this, return NEED_MORE; } - if (pub.ptr[0] != TLS_ANSI_UNCOMPRESSED) - { - DBG1(DBG_TLS, "DH point format '%N' not supported", - tls_ansi_point_format_names, pub.ptr[0]); - this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); - return NEED_MORE; + if (group != CURVE_25519 && + group != CURVE_448) + { /* classic ECPoint format (see RFC 8422, section 5.4.1) */ + if (pub.ptr[0] != TLS_ANSI_UNCOMPRESSED) + { + DBG1(DBG_TLS, "DH point format '%N' not supported", + tls_ansi_point_format_names, pub.ptr[0]); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + pub = chunk_skip(pub, 1); } - if (!this->dh->set_other_public_value(this->dh, chunk_skip(pub, 1))) + + if (!this->dh->set_other_public_value(this->dh, pub)) { DBG1(DBG_TLS, "applying DH public value failed"); this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); @@ -1393,15 +1399,22 @@ static status_t send_key_exchange_dhe(private_tls_peer_t *this, this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); return NEED_MORE; } - if (this->dh->get_dh_group(this->dh) == MODP_CUSTOM) + switch (this->dh->get_dh_group(this->dh)) { - writer->write_data16(writer, pub); - } - else - { /* ECP uses 8bit length header only, but a point format */ - writer->write_uint8(writer, pub.len + 1); - writer->write_uint8(writer, TLS_ANSI_UNCOMPRESSED); - writer->write_data(writer, pub); + case MODP_CUSTOM: + writer->write_data16(writer, pub); + break; + case CURVE_25519: + case CURVE_448: + /* ECPoint uses an 8-bit length header only */ + writer->write_data8(writer, pub); + break; + default: + /* classic ECPoint format (see RFC 8422, section 5.4.1) */ + writer->write_uint8(writer, pub.len + 1); + writer->write_uint8(writer, TLS_ANSI_UNCOMPRESSED); + writer->write_data(writer, pub); + break; } free(pub.ptr); From fff1974012df309b0483021941557f3ddf4155c7 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Aug 2020 13:35:54 +0200 Subject: [PATCH 015/107] tls-hkdf: Make labels enum a proper type --- src/libtls/tls_hkdf.c | 2 +- src/libtls/tls_hkdf.h | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libtls/tls_hkdf.c b/src/libtls/tls_hkdf.c index d0c44f558..682dd2fb5 100644 --- a/src/libtls/tls_hkdf.c +++ b/src/libtls/tls_hkdf.c @@ -356,7 +356,7 @@ METHOD(tls_hkdf_t, set_shared_secret, void, } METHOD(tls_hkdf_t, generate_secret, bool, - private_tls_hkdf_t *this, enum tls_hkdf_labels_t label, chunk_t messages, + private_tls_hkdf_t *this, tls_hkdf_label_t label, chunk_t messages, chunk_t *secret) { switch (label) diff --git a/src/libtls/tls_hkdf.h b/src/libtls/tls_hkdf.h index a820b0298..2f56a04f1 100644 --- a/src/libtls/tls_hkdf.h +++ b/src/libtls/tls_hkdf.h @@ -25,12 +25,13 @@ #include #include +typedef enum tls_hkdf_label_t tls_hkdf_label_t; typedef struct tls_hkdf_t tls_hkdf_t; /** * TLS HKDF labels */ -enum tls_hkdf_labels_t { +enum tls_hkdf_label_t { TLS_HKDF_EXT_BINDER, TLS_HKDF_RES_BINDER, TLS_HKDF_C_E_TRAFFIC, @@ -65,7 +66,7 @@ struct tls_hkdf_t { * @param secret secret will be written into this chunk, if used * @return TRUE if secrets derived successfully */ - bool (*generate_secret)(tls_hkdf_t *this, enum tls_hkdf_labels_t label, + bool (*generate_secret)(tls_hkdf_t *this, tls_hkdf_label_t label, chunk_t messages, chunk_t *secret); /** From 2921f43705f4b0113643ff8b9baf49a6bcc2355b Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Aug 2020 13:22:04 +0200 Subject: [PATCH 016/107] tls-crypto: Simplify handshake/application key derivation and rename methods Also consistently change the ciphers outside of tls_crypto_t and simplify key derivation in tls_peer_t and fix a memory leak. --- src/libtls/tls_crypto.c | 195 +++++++++++++--------------------------- src/libtls/tls_crypto.h | 11 ++- src/libtls/tls_peer.c | 32 +++---- 3 files changed, 85 insertions(+), 153 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 77fc15309..0fbc51ec2 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1244,10 +1244,6 @@ static bool create_aead(private_tls_crypto_t *this, suite_algs_t *algs) { this->aead_in = tls_aead_create_seq(algs->encr, algs->encr_size); this->aead_out = tls_aead_create_seq(algs->encr, algs->encr_size); - - this->protection->set_cipher(this->protection, TRUE, this->aead_in); - this->protection->set_cipher(this->protection, FALSE, this->aead_out); - } if (!this->aead_in || !this->aead_out) { @@ -1982,148 +1978,79 @@ METHOD(tls_crypto_t, derive_secrets, bool, expand_keys(this, client_random, server_random); } -METHOD(tls_crypto_t, derive_handshake_secret, bool, - private_tls_crypto_t *this, chunk_t shared_secret) +/** + * Derive and configure the keys/IVs using the given labels. + */ +static bool derive_labeled_keys(private_tls_crypto_t *this, + tls_hkdf_label_t client_label, + tls_hkdf_label_t server_label) { - chunk_t c_key, c_iv, s_key, s_iv; - - this->hkdf->set_shared_secret(this->hkdf, shared_secret); - - /* client key material */ - if (!this->hkdf->generate_secret(this->hkdf, TLS_HKDF_C_HS_TRAFFIC, - this->handshake, NULL) || - !this->hkdf->derive_key(this->hkdf, FALSE, - this->aead_out-> - get_encr_key_size(this->aead_out), &c_key) || - !this->hkdf->derive_iv(this->hkdf, FALSE, - this->aead_out-> - get_iv_size(this->aead_out), &c_iv)) - { - DBG1(DBG_TLS, "deriving client key material failed"); - chunk_clear(&c_key); - chunk_clear(&c_iv); - return FALSE; - } - - /* server key material */ - if (!this->hkdf->generate_secret(this->hkdf, TLS_HKDF_S_HS_TRAFFIC, - this->handshake, NULL) || - !this->hkdf->derive_key(this->hkdf, TRUE, this->aead_in-> - get_encr_key_size(this->aead_in), &s_key) || - !this->hkdf->derive_iv(this->hkdf, TRUE, this->aead_in-> - get_iv_size(this->aead_in), &s_iv)) - { - DBG1(DBG_TLS, "deriving server key material failed"); - chunk_clear(&c_key); - chunk_clear(&c_iv); - chunk_clear(&s_key); - chunk_clear(&s_iv); - return FALSE; - } + chunk_t c_key = chunk_empty, c_iv = chunk_empty; + chunk_t s_key = chunk_empty, s_iv = chunk_empty; + tls_aead_t *aead_c = this->aead_out, *aead_s = this->aead_in; + bool success = FALSE; if (this->tls->is_server(this->tls)) { - if (!this->aead_in->set_keys(this->aead_in, chunk_empty, s_key, s_iv) || - !this->aead_out->set_keys(this->aead_out, chunk_empty, c_key, c_iv)) - { - DBG1(DBG_TLS, "setting aead server key material failed"); - chunk_clear(&c_key); - chunk_clear(&c_iv); - chunk_clear(&s_key); - chunk_clear(&s_iv); - return FALSE; - } - } - else - { - if (!this->aead_in->set_keys(this->aead_in, chunk_empty, s_key, s_iv) || - !this->aead_out->set_keys(this->aead_out, chunk_empty, c_key, c_iv)) - { - DBG1(DBG_TLS, "setting aead client key material failed"); - chunk_clear(&c_key); - chunk_clear(&c_iv); - chunk_clear(&s_key); - chunk_clear(&s_iv); - return FALSE; - } + aead_c = this->aead_in; + aead_s = this->aead_out; } + if (!this->hkdf->generate_secret(this->hkdf, client_label, this->handshake, + NULL) || + !this->hkdf->derive_key(this->hkdf, FALSE, + this->aead_out->get_encr_key_size(this->aead_out), + &c_key) || + !this->hkdf->derive_iv(this->hkdf, FALSE, + this->aead_out->get_iv_size(this->aead_out), + &c_iv)) + { + DBG1(DBG_TLS, "deriving client key material failed"); + goto out; + } + + if (!this->hkdf->generate_secret(this->hkdf, server_label, this->handshake, + NULL) || + !this->hkdf->derive_key(this->hkdf, TRUE, + this->aead_in->get_encr_key_size(this->aead_in), + &s_key) || + !this->hkdf->derive_iv(this->hkdf, TRUE, + this->aead_in->get_iv_size(this->aead_in), + &s_iv)) + { + DBG1(DBG_TLS, "deriving server key material failed"); + goto out; + } + + if (!aead_c->set_keys(aead_c, chunk_empty, c_key, c_iv) || + !aead_s->set_keys(aead_s, chunk_empty, s_key, s_iv)) + { + DBG1(DBG_TLS, "setting AEAD key material failed"); + goto out; + } + success = TRUE; + +out: chunk_clear(&c_key); chunk_clear(&c_iv); chunk_clear(&s_key); chunk_clear(&s_iv); - return TRUE; + return success; } -METHOD(tls_crypto_t, derive_app_secret, bool, +METHOD(tls_crypto_t, derive_handshake_keys, bool, + private_tls_crypto_t *this, chunk_t shared_secret) +{ + this->hkdf->set_shared_secret(this->hkdf, shared_secret); + return derive_labeled_keys(this, TLS_HKDF_C_HS_TRAFFIC, + TLS_HKDF_S_HS_TRAFFIC); +} + +METHOD(tls_crypto_t, derive_app_keys, bool, private_tls_crypto_t *this) { - chunk_t c_key, c_iv, s_key, s_iv; - - /* client key material */ - if (!this->hkdf->generate_secret(this->hkdf, TLS_HKDF_C_AP_TRAFFIC, - this->handshake, NULL) || - !this->hkdf->derive_key(this->hkdf, FALSE, - this->aead_out-> - get_encr_key_size(this->aead_out), &c_key) || - !this->hkdf->derive_iv(this->hkdf, FALSE, - this->aead_out-> - get_iv_size(this->aead_out), &c_iv)) - { - DBG1(DBG_TLS, "deriving client key material failed"); - chunk_clear(&c_key); - chunk_clear(&c_iv); - return FALSE; - } - - /* server key material */ - if (!this->hkdf->generate_secret(this->hkdf, TLS_HKDF_S_AP_TRAFFIC, - this->handshake, NULL) || - !this->hkdf->derive_key(this->hkdf, TRUE, this->aead_in-> - get_encr_key_size(this->aead_in), &s_key) || - !this->hkdf->derive_iv(this->hkdf, TRUE, this->aead_in-> - get_iv_size(this->aead_in), &s_iv)) - { - DBG1(DBG_TLS, "deriving server key material failed"); - chunk_clear(&c_key); - chunk_clear(&c_iv); - chunk_clear(&s_key); - chunk_clear(&s_iv); - return FALSE; - } - - if (this->tls->is_server(this->tls)) - { - if (!this->aead_in->set_keys(this->aead_in, chunk_empty, s_key, s_iv) || - !this->aead_out->set_keys(this->aead_out, chunk_empty, c_key, c_iv)) - { - DBG1(DBG_TLS, "setting aead server key material failed"); - chunk_clear(&c_key); - chunk_clear(&c_iv); - chunk_clear(&s_key); - chunk_clear(&s_iv); - return FALSE; - } - } - else - { - if (!this->aead_in->set_keys(this->aead_in, chunk_empty, s_key, s_iv) || - !this->aead_out->set_keys(this->aead_out, chunk_empty, c_key, c_iv)) - { - DBG1(DBG_TLS, "setting aead client key material failed"); - chunk_clear(&c_key); - chunk_clear(&c_iv); - chunk_clear(&s_key); - chunk_clear(&s_iv); - return FALSE; - } - } - - chunk_clear(&c_key); - chunk_clear(&c_iv); - chunk_clear(&s_key); - chunk_clear(&s_iv); - return TRUE; + return derive_labeled_keys(this, TLS_HKDF_C_AP_TRAFFIC, + TLS_HKDF_S_AP_TRAFFIC); } METHOD(tls_crypto_t, resume_session, tls_cipher_suite_t, @@ -2223,8 +2150,8 @@ tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache) .calculate_finished = _calculate_finished, .calculate_finished_tls13 = _calculate_finished_tls13, .derive_secrets = _derive_secrets, - .derive_handshake_secret = _derive_handshake_secret, - .derive_app_secret = _derive_app_secret, + .derive_handshake_keys = _derive_handshake_keys, + .derive_app_keys = _derive_app_keys, .resume_session = _resume_session, .get_session = _get_session, .change_cipher = _change_cipher, diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h index 5cd007241..f980095eb 100644 --- a/src/libtls/tls_crypto.h +++ b/src/libtls/tls_crypto.h @@ -1,4 +1,9 @@ /* + * Copyright (C) 2020 Tobias Brunner + * Copyright (C) 2020 Pascal Knecht + * Copyright (C) 2020 Méline Sieber + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2010 Martin Willi * Copyright (C) 2010 revosec AG * @@ -514,7 +519,7 @@ struct tls_crypto_t { bio_reader_t *reader); /** - * Calculate the data of a legacyTLS finished message. + * Calculate the data of a legacy TLS finished message. * * @param label ASCII label to use for calculation * @param out buffer to write finished data to @@ -551,14 +556,14 @@ struct tls_crypto_t { * @param shared_secret input key material * @return TRUE if secret derived successfully */ - bool (*derive_handshake_secret)(tls_crypto_t *this, chunk_t shared_secret); + bool (*derive_handshake_keys)(tls_crypto_t *this, chunk_t shared_secret); /** * Derive the application keys. * * @return TRUE if secret derived successfully */ - bool (*derive_app_secret)(tls_crypto_t *this); + bool (*derive_app_keys)(tls_crypto_t *this); /** * Try to resume a TLS session, derive key material. diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 870c75573..96ce37447 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -262,22 +262,21 @@ static status_t process_server_hello(private_tls_peer_t *this, if (this->tls->get_version_max(this->tls) == TLS_1_3) { - chunk_t shared_secret; + chunk_t shared_secret = chunk_empty; - if (key_type != CURVE_25519 && - !this->dh->set_other_public_value(this->dh, ext_key_share)) + if (!this->dh->set_other_public_value(this->dh, ext_key_share) || + !this->dh->get_shared_secret(this->dh, &shared_secret) || + !this->crypto->derive_handshake_keys(this->crypto, shared_secret)) { - DBG2(DBG_TLS, "server key share unable to save"); - } - if (!this->dh->get_shared_secret(this->dh, &shared_secret)) - { - DBG2(DBG_TLS, "No shared secret key found"); + DBG1(DBG_TLS, "DH key derivation failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); + chunk_clear(&shared_secret); + return NEED_MORE; } + chunk_clear(&shared_secret); - if (!this->crypto->derive_handshake_secret(this->crypto, shared_secret)) - { - DBG2(DBG_TLS, "derive handshake traffic secret failed"); - } + this->crypto->change_cipher(this->crypto, TRUE); + this->crypto->change_cipher(this->crypto, FALSE); } this->state = STATE_HELLO_RECEIVED; @@ -1537,14 +1536,15 @@ METHOD(tls_handshake_t, build, status_t, case STATE_INIT: return send_client_hello(this, type, writer); case STATE_HELLO_DONE: - /* otherwise fall through to next state */ case STATE_CIPHERSPEC_CHANGED_OUT: case STATE_FINISHED_RECEIVED: - /* fall through since legacy TLS and TLS 1.3 - * expect the same message */ return send_finished(this, type, writer); case STATE_FINISHED_SENT: - this->crypto->derive_app_secret(this->crypto); + if (!this->crypto->derive_app_keys(this->crypto)) + { + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } this->crypto->change_cipher(this->crypto, TRUE); this->crypto->change_cipher(this->crypto, FALSE); this->state = STATE_FINISHED_SENT_KEY_SWITCHED; From de983a3cb90d632438960c1b80966d3560d1f85f Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Aug 2020 15:58:46 +0200 Subject: [PATCH 017/107] tls-crypto: Simplify signature creation/verification --- src/libtls/tls_crypto.c | 94 ++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 58 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 0fbc51ec2..a2375b3f5 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1578,6 +1578,26 @@ static bool hash_data(private_tls_crypto_t *this, chunk_t data, chunk_t *hash) return TRUE; } +/** + * TLS 1.3 static part of the data the server signs (64 spaces followed by the + * context string "TLS 1.3, server CertificateVerify" and a 0 byte). + */ +static chunk_t tls13_sig_data_server = chunk_from_chars( + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x54, 0x4c, 0x53, 0x20, 0x31, 0x2e, 0x33, 0x2c, + 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x69, 0x66, + 0x79, 0x00, +); + METHOD(tls_crypto_t, sign, bool, private_tls_crypto_t *this, private_key_t *key, bio_writer_t *writer, chunk_t data, chunk_t hashsig) @@ -1665,64 +1685,7 @@ METHOD(tls_crypto_t, verify, bool, private_tls_crypto_t *this, public_key_t *key, bio_reader_t *reader, chunk_t data) { - if (this->tls->get_version_max(this->tls) == TLS_1_3) - { - signature_scheme_t scheme = SIGN_UNKNOWN; - uint8_t hash, alg; - chunk_t sig, transcript_hash, static_sig_data_all; - - chunk_t static_sig_data = chunk_from_chars( - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x54, 0x4c, 0x53, 0x20, 0x31, 0x2e, 0x33, 0x2c, - 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, - 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x69, 0x66, - 0x79, 0x00, - ); - - if (!reader->read_uint8(reader, &hash) || - !reader->read_uint8(reader, &alg) || - !reader->read_data16(reader, &sig)) - { - DBG1(DBG_TLS, "received invalid signature"); - return FALSE; - } - - if (!hash_data(this, data, &transcript_hash)) - { - DBG1(DBG_TLS, "Unable to hash"); - return FALSE; - } - - static_sig_data_all = chunk_cat("cc", static_sig_data, transcript_hash); - scheme = hashsig_to_scheme(key->get_type(key), hash, alg); - if (scheme == SIGN_UNKNOWN) - { - DBG1(DBG_TLS, "signature algorithms %N/%N not supported", - tls_hash_algorithm_names, hash, - tls_signature_algorithm_names, alg); - return FALSE; - } - if (!key->verify(key, scheme, NULL, static_sig_data_all, sig)) - { - DBG1(DBG_TLS, "verification of signature failed"); - return FALSE; - } - DBG2(DBG_TLS, - "verified signature with %N/%N", - tls_hash_algorithm_names, - hash, - tls_signature_algorithm_names, - alg); - } - else if (this->tls->get_version_max(this->tls) == TLS_1_2) + if (this->tls->get_version_max(this->tls) >= TLS_1_2) { signature_scheme_t scheme = SIGN_UNKNOWN; uint8_t hash, alg; @@ -1743,8 +1706,23 @@ METHOD(tls_crypto_t, verify, bool, tls_signature_algorithm_names, alg); return FALSE; } + if (this->tls->get_version_max(this->tls) == TLS_1_3) + { + chunk_t transcript_hash; + + if (!hash_data(this, data, &transcript_hash)) + { + DBG1(DBG_TLS, "Unable to create transcript hash"); + return FALSE; + } + + data = chunk_cata("cm", tls13_sig_data_server, transcript_hash); + } if (!key->verify(key, scheme, NULL, data, sig)) { + DBG1(DBG_TLS, "signature verification with %N/%N failed", + tls_hash_algorithm_names, hash, tls_signature_algorithm_names, + alg); return FALSE; } DBG2(DBG_TLS, "verified signature with %N/%N", From 6a0ee0c23c3ac6a0cf9cd87dcb2a3ca6d91ef51a Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Aug 2020 16:01:27 +0200 Subject: [PATCH 018/107] tls-hkdf: Cleanups and refactorings The main refactoring is how secrets (PSK/DH) are handled. --- src/libtls/tls_hkdf.c | 204 +++++++++++++++++++++++++----------------- src/libtls/tls_hkdf.h | 2 +- 2 files changed, 124 insertions(+), 82 deletions(-) diff --git a/src/libtls/tls_hkdf.c b/src/libtls/tls_hkdf.c index 682dd2fb5..4da511bfe 100644 --- a/src/libtls/tls_hkdf.c +++ b/src/libtls/tls_hkdf.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2020 Tobias Brunner * Copyright (C) 2020 Pascal Knecht * Copyright (C) 2020 Méline Sieber * HSR Hochschule fuer Technik Rapperswil @@ -40,11 +41,6 @@ struct private_tls_hkdf_t { */ hkdf_phase phase; - /** - * Hash algorithm used. - */ - hash_algorithm_t hash_algorithm; - /** * Pseudorandom function used. */ @@ -61,9 +57,9 @@ struct private_tls_hkdf_t { chunk_t shared_secret; /** - * IKM used. + * PSK used. */ - chunk_t ikm; + chunk_t psk; /** * PRK used. @@ -105,13 +101,13 @@ static bool extract(private_tls_hkdf_t *this, chunk_t salt, chunk_t ikm, { if (!this->prf->set_key(this->prf, salt)) { - DBG1(DBG_TLS, "unable to set PRF salt"); + DBG1(DBG_TLS, "unable to set PRF secret to salt"); return FALSE; } chunk_clear(prk); if(!this->prf->allocate_bytes(this->prf, ikm, prk)) { - DBG1(DBG_TLS, "unable to allocate PRF space"); + DBG1(DBG_TLS, "unable to allocate PRF result"); return FALSE; } @@ -127,21 +123,23 @@ static bool extract(private_tls_hkdf_t *this, chunk_t salt, chunk_t ikm, static bool expand(private_tls_hkdf_t *this, chunk_t prk, chunk_t info, size_t length, chunk_t *okm) { + prf_plus_t *prf_plus; + if (!this->prf->set_key(this->prf, prk)) { - DBG1(DBG_TLS, "unable to set PRF PRK"); + DBG1(DBG_TLS, "unable to set PRF secret to PRK"); return FALSE; } - prf_plus_t *prf_plus = prf_plus_create(this->prf, TRUE, info); - chunk_clear(&this->okm); + prf_plus = prf_plus_create(this->prf, TRUE, info); + chunk_clear(okm); if (!prf_plus || !prf_plus->allocate_bytes(prf_plus, length, okm)) { - DBG1(DBG_TLS, "unable to allocate PRF plus space"); + DBG1(DBG_TLS, "unable to allocate PRF+ result"); DESTROY_IF(prf_plus); chunk_clear(okm); return FALSE; } - DESTROY_IF(prf_plus); + prf_plus->destroy(prf_plus); DBG4(DBG_TLS, "OKM: %B", okm); @@ -181,14 +179,11 @@ static bool expand_label(private_tls_hkdf_t *this, chunk_t secret, static bool derive_secret(private_tls_hkdf_t *this, chunk_t label, chunk_t messages) { - bool success; chunk_t context; + bool success; - if (!this->hasher || - !this->hasher->allocate_hash(this->hasher, messages, &context)) + if (!this->hasher->allocate_hash(this->hasher, messages, &context)) { - DBG1(DBG_TLS, "%N not supported", hash_algorithm_names, - this->hash_algorithm); return FALSE; } @@ -199,16 +194,38 @@ static bool derive_secret(private_tls_hkdf_t *this, chunk_t label, return success; } +/** + * Move to phase 1 (Early Secret) + * + * 0 + * | + * v + * PSK -> HKDF-Extract = Early Secret + * | + * +-----> Derive-Secret(., "ext binder" | "res binder", "") + * | = binder_key + * | + * +-----> Derive-Secret(., "c e traffic", ClientHello) + * | = client_early_traffic_secret + * | + * +-----> Derive-Secret(., "e exp master", ClientHello) + * | = early_exporter_master_secret + * v + */ static bool move_to_phase_1(private_tls_hkdf_t *this) { - chunk_t salt_zero; + chunk_t salt_zero, psk = this->psk; switch (this->phase) { case HKDF_PHASE_0: salt_zero = chunk_alloca(this->hasher->get_hash_size(this->hasher)); chunk_copy_pad(salt_zero, chunk_empty, 0); - if (!extract(this, salt_zero, this->ikm, &this->prk)) + if (!psk.ptr) + { + psk = salt_zero; + } + if (!extract(this, salt_zero, psk, &this->prk)) { DBG1(DBG_TLS, "unable to extract PRK"); return FALSE; @@ -223,6 +240,23 @@ static bool move_to_phase_1(private_tls_hkdf_t *this) } } +/** + * Move to phase 2 (Handshake Secret) + * + * Derive-Secret(., "derived", "") + * | + * v + * (EC)DHE -> HKDF-Extract = Handshake Secret + * | + * +-----> Derive-Secret(., "c hs traffic", + * | ClientHello...ServerHello) + * | = client_handshake_traffic_secret + * | + * +-----> Derive-Secret(., "s hs traffic", + * | ClientHello...ServerHello) + * | = server_handshake_traffic_secret + * v + */ static bool move_to_phase_2(private_tls_hkdf_t *this) { chunk_t derived; @@ -249,13 +283,8 @@ static bool move_to_phase_2(private_tls_hkdf_t *this) DBG1(DBG_TLS, "no shared secret set"); return FALSE; } - else - { - chunk_clear(&this->ikm); - this->ikm = chunk_clone(this->shared_secret); - } - if (!extract(this, this->okm, this->ikm, &this->prk)) + if (!extract(this, this->okm, this->shared_secret, &this->prk)) { DBG1(DBG_TLS, "unable extract PRK"); return FALSE; @@ -270,6 +299,30 @@ static bool move_to_phase_2(private_tls_hkdf_t *this) } } +/** + * Move to phase 3 (Master Secret) + * + * Derive-Secret(., "derived", "") + * | + * v + * 0 -> HKDF-Extract = Master Secret + * | + * +-----> Derive-Secret(., "c ap traffic", + * | ClientHello...server Finished) + * | = client_application_traffic_secret_0 + * | + * +-----> Derive-Secret(., "s ap traffic", + * | ClientHello...server Finished) + * | = server_application_traffic_secret_0 + * | + * +-----> Derive-Secret(., "exp master", + * | ClientHello...server Finished) + * | = exporter_master_secret + * | + * +-----> Derive-Secret(., "res master", + * ClientHello...client Finished) + * = resumption_master_secret + */ static bool move_to_phase_3(private_tls_hkdf_t *this) { chunk_t derived, ikm_zero; @@ -310,45 +363,6 @@ static bool move_to_phase_3(private_tls_hkdf_t *this) } } -static void return_secret(private_tls_hkdf_t *this, chunk_t key, - chunk_t *secret) -{ - *secret = chunk_alloc(key.len); - chunk_copy_pad(*secret, key, 0); -} - -static bool get_shared_label_keys(private_tls_hkdf_t *this, chunk_t label, - bool is_server, size_t length, chunk_t *key) -{ - chunk_t result, secret; - - if (is_server) - { - secret = chunk_clone(this->server_traffic_secret); - } - else - { - secret = chunk_clone(this->client_traffic_secret); - } - - if (!expand_label(this, secret, label, chunk_empty, length, &result)) - { - DBG1(DBG_TLS, "unable to derive secret"); - chunk_clear(&secret); - chunk_clear(&result); - return FALSE; - } - - if (key) - { - return_secret(this, result, key); - } - - chunk_clear(&secret); - chunk_clear(&result); - return TRUE; -} - METHOD(tls_hkdf_t, set_shared_secret, void, private_tls_hkdf_t *this, chunk_t shared_secret) { @@ -414,9 +428,37 @@ METHOD(tls_hkdf_t, generate_secret, bool, if (secret) { - return_secret(this, this->okm, secret); + *secret = chunk_clone(this->okm); + } + return TRUE; +} + +/** + * Derive keys/IVs from the current traffic secrets. + */ +static bool get_shared_label_keys(private_tls_hkdf_t *this, chunk_t label, + bool is_server, size_t length, chunk_t *key) +{ + chunk_t result = chunk_empty, secret; + + secret = is_server ? this->server_traffic_secret + : this->client_traffic_secret; + + if (!expand_label(this, secret, label, chunk_empty, length, &result)) + { + DBG1(DBG_TLS, "unable to derive labeled secret"); + chunk_clear(&result); + return FALSE; } + if (key) + { + *key = result; + } + else + { + chunk_clear(&result); + } return TRUE; } @@ -446,7 +488,7 @@ METHOD(tls_hkdf_t, derive_finished, bool, METHOD(tls_hkdf_t, destroy, void, private_tls_hkdf_t *this) { - chunk_free(&this->ikm); + chunk_clear(&this->psk); chunk_clear(&this->prk); chunk_clear(&this->shared_secret); chunk_clear(&this->okm); @@ -471,7 +513,8 @@ tls_hkdf_t *tls_hkdf_create(hash_algorithm_t hash_algorithm, chunk_t psk) prf_algorithm = PRF_HMAC_SHA2_384; break; default: - DBG1(DBG_TLS, "not supported hash algorithm"); + DBG1(DBG_TLS, "unsupported hash algorithm %N", hash_algorithm_names, + hash_algorithm); return NULL; } @@ -485,27 +528,26 @@ tls_hkdf_t *tls_hkdf_create(hash_algorithm_t hash_algorithm, chunk_t psk) .destroy = _destroy, }, .phase = HKDF_PHASE_0, - .hash_algorithm = hash_algorithm, + .psk = psk.ptr ? chunk_clone(psk) : chunk_empty, .prf = lib->crypto->create_prf(lib->crypto, prf_algorithm), .hasher = lib->crypto->create_hasher(lib->crypto, hash_algorithm), ); - if (!psk.ptr) - { - this->ikm = chunk_alloc(this->hasher->get_hash_size(this->hasher)); - chunk_copy_pad(this->ikm, chunk_empty, 0); - } - else - { - this->ikm = chunk_clone(psk); - } - if (!this->prf || !this->hasher) { + if (!this->prf) + { + DBG1(DBG_TLS, "%N not supported", pseudo_random_function_names, + prf_algorithm); + } + if (!this->hasher) + { + DBG1(DBG_TLS, "%N not supported", hash_algorithm_names, + hash_algorithm); + } DBG1(DBG_TLS, "unable to initialise HKDF"); destroy(this); return NULL; } - return &this->public; } diff --git a/src/libtls/tls_hkdf.h b/src/libtls/tls_hkdf.h index 2f56a04f1..7debf062d 100644 --- a/src/libtls/tls_hkdf.h +++ b/src/libtls/tls_hkdf.h @@ -77,7 +77,7 @@ struct tls_hkdf_t { * * @param is_server TRUE if server, FALSE if client derives secret * @param length key length, in bytes - * @param key secret will be written into this chunk + * @param key key will be written into this chunk * @return TRUE if secrets derived successfully */ bool (*derive_key)(tls_hkdf_t *this, bool is_server, size_t length, From a9f661f52a5a5e002a9b3fe1e66f2e77c9049ba3 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Aug 2020 16:44:17 +0200 Subject: [PATCH 019/107] tls-hkdf: Add helper method to allocate data from the internal PRF --- src/libtls/tls_hkdf.c | 9 +++++++++ src/libtls/tls_hkdf.h | 13 +++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/libtls/tls_hkdf.c b/src/libtls/tls_hkdf.c index 4da511bfe..566916a23 100644 --- a/src/libtls/tls_hkdf.c +++ b/src/libtls/tls_hkdf.c @@ -485,6 +485,14 @@ METHOD(tls_hkdf_t, derive_finished, bool, finished); } +METHOD(tls_hkdf_t, allocate_bytes, bool, + private_tls_hkdf_t *this, chunk_t key, chunk_t seed, + chunk_t *out) +{ + return this->prf->set_key(this->prf, key) && + this->prf->allocate_bytes(this->prf, seed, out); +} + METHOD(tls_hkdf_t, destroy, void, private_tls_hkdf_t *this) { @@ -525,6 +533,7 @@ tls_hkdf_t *tls_hkdf_create(hash_algorithm_t hash_algorithm, chunk_t psk) .derive_key = _derive_key, .derive_iv = _derive_iv, .derive_finished = _derive_finished, + .allocate_bytes = _allocate_bytes, .destroy = _destroy, }, .phase = HKDF_PHASE_0, diff --git a/src/libtls/tls_hkdf.h b/src/libtls/tls_hkdf.h index 7debf062d..5a8a77f5b 100644 --- a/src/libtls/tls_hkdf.h +++ b/src/libtls/tls_hkdf.h @@ -111,6 +111,19 @@ struct tls_hkdf_t { bool (*derive_finished)(tls_hkdf_t *this, bool is_server, chunk_t *finished); + /** + * Use the internal PRF to allocate data (mainly for the finished message + * where the key is from derive_finished() and the seed is the transcript + * hash). + * + * @param key key to use with the PRF + * @param seed seed to use with the PRF + * @param out output from the PRF (allocated) + * @return TRUE if output was generated + */ + bool (*allocate_bytes)(tls_hkdf_t *this, chunk_t key, chunk_t seed, + chunk_t *out); + /** * Destroy a tls_hkdf_t */ From f116a4823f620f77b498d9c1a07db08d9b7c39c3 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Aug 2020 16:49:52 +0200 Subject: [PATCH 020/107] tls-crypto: Use internal PRF of tls-hkdf to generate finished message Also adds additional checks. --- src/libtls/tls_crypto.c | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index a2375b3f5..2fed6604d 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1808,35 +1808,31 @@ METHOD(tls_crypto_t, calculate_finished_tls13, bool, private_tls_crypto_t *this, bool is_server, chunk_t *out) { chunk_t finished_key, finished_hash; - prf_t *prf; - this->hkdf->derive_finished(this->hkdf, is_server, &finished_key); + if (!this->hkdf) + { + return FALSE; + } if (!hash_data(this, this->handshake, &finished_hash)) { DBG1(DBG_TLS, "creating hash of handshake failed"); - } - - if (this->suite == TLS_AES_256_GCM_SHA384) - { - prf = lib->crypto->create_prf(lib->crypto, PRF_HMAC_SHA2_384); - } - else - { - prf = lib->crypto->create_prf(lib->crypto, PRF_HMAC_SHA2_256); - } - if(!prf->set_key(prf, finished_key) || - !prf->allocate_bytes(prf, finished_hash, out)) - { - DBG1(DBG_TLS, "setting key or generating hash for HMAC failed"); - chunk_clear(&finished_key); - chunk_clear(&finished_hash); - prf->destroy(prf); return FALSE; } - + if (!this->hkdf->derive_finished(this->hkdf, is_server, &finished_key)) + { + DBG1(DBG_TLS, "generating finished key failed"); + chunk_clear(&finished_hash); + return FALSE; + } + if (!this->hkdf->allocate_bytes(this->hkdf, finished_key, finished_hash, out)) + { + DBG1(DBG_TLS, "generating finished HMAC failed"); + chunk_clear(&finished_key); + chunk_clear(&finished_hash); + return FALSE; + } chunk_clear(&finished_key); chunk_clear(&finished_hash); - prf->destroy(prf); return TRUE; } From 2e1c0a277656e55f2e273c80691baddea55912aa Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Aug 2020 16:52:20 +0200 Subject: [PATCH 021/107] tls-crypto: Rename methods to calculate finished message Instead of the version number use "legacy" for the one for earlier TLS versions. --- src/libtls/tls_crypto.c | 6 +++--- src/libtls/tls_crypto.h | 7 ++++--- src/libtls/tls_peer.c | 12 ++++++------ src/libtls/tls_server.c | 6 ++++-- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 2fed6604d..494a6930a 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1782,7 +1782,7 @@ METHOD(tls_crypto_t, verify_handshake, bool, return verify(this, key, reader, this->handshake); } -METHOD(tls_crypto_t, calculate_finished, bool, +METHOD(tls_crypto_t, calculate_finished_legacy, bool, private_tls_crypto_t *this, char *label, char out[12]) { chunk_t seed; @@ -1804,7 +1804,7 @@ METHOD(tls_crypto_t, calculate_finished, bool, return TRUE; } -METHOD(tls_crypto_t, calculate_finished_tls13, bool, +METHOD(tls_crypto_t, calculate_finished, bool, private_tls_crypto_t *this, bool is_server, chunk_t *out) { chunk_t finished_key, finished_hash; @@ -2121,8 +2121,8 @@ tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache) .verify = _verify, .sign_handshake = _sign_handshake, .verify_handshake = _verify_handshake, + .calculate_finished_legacy = _calculate_finished_legacy, .calculate_finished = _calculate_finished, - .calculate_finished_tls13 = _calculate_finished_tls13, .derive_secrets = _derive_secrets, .derive_handshake_keys = _derive_handshake_keys, .derive_app_keys = _derive_app_keys, diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h index f980095eb..3f48cfe13 100644 --- a/src/libtls/tls_crypto.h +++ b/src/libtls/tls_crypto.h @@ -525,7 +525,8 @@ struct tls_crypto_t { * @param out buffer to write finished data to * @return TRUE if calculation successful */ - bool (*calculate_finished)(tls_crypto_t *this, char *label, char out[12]); + bool (*calculate_finished_legacy)(tls_crypto_t *this, char *label, + char out[12]); /** * Calculate the data of a TLS finished message. @@ -533,8 +534,8 @@ struct tls_crypto_t { * @param out buffer to write finished data to * @return TRUE if calculation successful */ - bool (*calculate_finished_tls13)(tls_crypto_t *this, bool is_server, - chunk_t *out); + bool (*calculate_finished)(tls_crypto_t *this, bool is_server, + chunk_t *out); /** * Derive the master secret, MAC and encryption keys. diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 96ce37447..dfc15cc05 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -874,8 +874,8 @@ static status_t process_finished(private_tls_peer_t *this, bio_reader_t *reader) this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); return NEED_MORE; } - if (!this->crypto->calculate_finished(this->crypto, "server finished", - buf)) + if (!this->crypto->calculate_finished_legacy(this->crypto, + "server finished", buf)) { DBG1(DBG_TLS, "calculating server finished failed"); this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); @@ -897,7 +897,7 @@ static status_t process_finished(private_tls_peer_t *this, bio_reader_t *reader) this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); return NEED_MORE; } - if (!this->crypto->calculate_finished_tls13(this->crypto, true, + if (!this->crypto->calculate_finished(this->crypto, true, &verify_data)) { DBG1(DBG_TLS, "calculating server finished failed"); @@ -1469,7 +1469,8 @@ static status_t send_finished(private_tls_peer_t *this, { char buf[12]; - if (!this->crypto->calculate_finished(this->crypto, "client finished", buf)) + if (!this->crypto->calculate_finished_legacy(this->crypto, + "client finished", buf)) { DBG1(DBG_TLS, "calculating client finished data failed"); this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); @@ -1480,8 +1481,7 @@ static status_t send_finished(private_tls_peer_t *this, } else { - if (!this->crypto->calculate_finished_tls13(this->crypto, false, - &verify_data)) + if (!this->crypto->calculate_finished(this->crypto, FALSE, &verify_data)) { DBG1(DBG_TLS, "calculating client finished data failed"); this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 9714df0f6..3e97d3799 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -601,7 +601,8 @@ static status_t process_finished(private_tls_server_t *this, this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); return NEED_MORE; } - if (!this->crypto->calculate_finished(this->crypto, "client finished", buf)) + if (!this->crypto->calculate_finished_legacy(this->crypto, + "client finished", buf)) { DBG1(DBG_TLS, "calculating client finished failed"); this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); @@ -974,7 +975,8 @@ static status_t send_finished(private_tls_server_t *this, { char buf[12]; - if (!this->crypto->calculate_finished(this->crypto, "server finished", buf)) + if (!this->crypto->calculate_finished_legacy(this->crypto, + "server finished", buf)) { DBG1(DBG_TLS, "calculating server finished data failed"); this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); From 4c40a3d3f06c945bf23d571c84e9cd6b44bc3175 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Aug 2020 16:11:59 +0200 Subject: [PATCH 022/107] tls-peer: Fix parsing of intermediate CA certificates --- src/libtls/tls_peer.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index dfc15cc05..6bec0219b 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -469,12 +469,10 @@ static status_t process_certificate(private_tls_peer_t *this, { if (!certs->read_data16(certs, &data)) { - DBG1(DBG_TLS, "reading extension field of certificate failed", - &data); + DBG1(DBG_TLS, "failed to read extensions of CertificateEntry"); this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); return NEED_MORE; } - break; } } certs->destroy(certs); From f0ed5f9125fbb8da4f6cd01ab7120ccb6328455c Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 28 Aug 2020 08:54:49 +0200 Subject: [PATCH 023/107] tls-peer: Fix parsing of encrypted extensions --- src/libtls/tls_peer.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 6bec0219b..a75a8f037 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -289,7 +289,6 @@ static status_t process_server_hello(private_tls_peer_t *this, static status_t process_encrypted_extensions(private_tls_peer_t *this, bio_reader_t *reader) { - uint16_t length; chunk_t ext = chunk_empty; int offset = 0; uint16_t extension_type, extension_length; @@ -297,9 +296,7 @@ static status_t process_encrypted_extensions(private_tls_peer_t *this, this->crypto->append_handshake(this->crypto, TLS_ENCRYPTED_EXTENSIONS, reader->peek(reader)); - - if (!reader->read_uint16(reader, &length) || - (reader->remaining(reader) && !reader->read_data16(reader, &ext))) + if (!reader->read_data16(reader, &ext)) { DBG1(DBG_TLS, "received invalid EncryptedExtensions"); this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); From c78b2bee5dc054cd0fadc4230b70270f9e264ce6 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Aug 2020 16:14:54 +0200 Subject: [PATCH 024/107] tls-peer: Refactor parsing of TLS extensions Also adds proper error handling. --- src/libtls/tls_peer.c | 131 +++++++++++++++++++++--------------------- 1 file changed, 66 insertions(+), 65 deletions(-) diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index a75a8f037..f150dca04 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -1,4 +1,9 @@ /* + * Copyright (C) 2020 Tobias Brunner + * Copyright (C) 2020 Pascal Knecht + * Copyright (C) 2020 Méline Sieber + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2010 Martin Willi * Copyright (C) 2010 revosec AG * @@ -148,12 +153,10 @@ static status_t process_server_hello(private_tls_peer_t *this, bio_reader_t *reader) { uint8_t compression; - uint16_t version, cipher; - chunk_t random, session, ext = chunk_empty, ext_key_share = chunk_empty; + uint16_t version, cipher, key_type; + bio_reader_t *extensions, *extension; + chunk_t random, session, ext = chunk_empty, key_share = chunk_empty; tls_cipher_suite_t suite = 0; - int offset = 0; - uint16_t extension_type, extension_length; - uint16_t key_type, key_length; this->crypto->append_handshake(this->crypto, TLS_SERVER_HELLO, reader->peek(reader)); @@ -172,50 +175,52 @@ static status_t process_server_hello(private_tls_peer_t *this, memcpy(this->server_random, random.ptr, sizeof(this->server_random)); - bio_reader_t *extension_reader = bio_reader_create(ext); - - /* parse extension to decide which tls version of the state machine we - * want to go - */ - while (offset < ext.len) + extensions = bio_reader_create(ext); + while (extensions->remaining(extensions)) { - chunk_t extension_payload = chunk_empty; + uint16_t extension_type; + chunk_t extension_data; - extension_reader->read_uint16(extension_reader, &extension_type); - extension_reader->read_uint16(extension_reader, &extension_length); - offset += extension_length + 4; - - if (!extension_reader->read_data(extension_reader, - extension_length, - &extension_payload)) + if (!extensions->read_uint16(extensions, &extension_type) || + !extensions->read_data16(extensions, &extension_data)) { - DBG2(DBG_TLS, "unable to read extension payload data"); + DBG1(DBG_TLS, "invalid extension in ServerHello"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + extensions->destroy(extensions); + return NEED_MORE; } - - bio_reader_t *ext_payload_reader = bio_reader_create(extension_payload); - + extension = bio_reader_create(extension_data); switch (extension_type) { case TLS_EXT_SUPPORTED_VERSIONS: - ext_payload_reader->read_uint16(ext_payload_reader, &version); - break; - - case TLS_EXT_KEY_SHARE: - ext_payload_reader->read_uint16(ext_payload_reader, &key_type); - ext_payload_reader->read_uint16(ext_payload_reader, &key_length); - if (!ext_payload_reader->read_data(ext_payload_reader, - key_length, - &ext_key_share)) + if (!extension->read_uint16(extension, &version)) { - DBG2(DBG_TLS, "no valid key share found in extension"); + DBG1(DBG_TLS, "invalid %N extension", tls_extension_names, + extension_type); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + extensions->destroy(extensions); + extension->destroy(extension); + return NEED_MORE; + } + break; + case TLS_EXT_KEY_SHARE: + if (!extension->read_uint16(extension, &key_type) || + !extension->read_data16(extension, &key_share)) + { + DBG1(DBG_TLS, "invalid %N extension", tls_extension_names, + extension_type); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + extensions->destroy(extensions); + extension->destroy(extension); + return NEED_MORE; } break; default: - continue; + break; } - ext_payload_reader->destroy(ext_payload_reader); + extension->destroy(extension); } - extension_reader->destroy(extension_reader); + extensions->destroy(extensions); if (!this->tls->set_version(this->tls, version)) { @@ -244,6 +249,7 @@ static status_t process_server_hello(private_tls_peer_t *this, DESTROY_IF(this->dh); this->dh = NULL; } + if (!suite) { suite = cipher; @@ -264,7 +270,7 @@ static status_t process_server_hello(private_tls_peer_t *this, { chunk_t shared_secret = chunk_empty; - if (!this->dh->set_other_public_value(this->dh, ext_key_share) || + if (!this->dh->set_other_public_value(this->dh, key_share) || !this->dh->get_shared_secret(this->dh, &shared_secret) || !this->crypto->derive_handshake_keys(this->crypto, shared_secret)) { @@ -284,17 +290,16 @@ static status_t process_server_hello(private_tls_peer_t *this, } /** -* Process a server encrypted extensions message -*/ + * Process a server encrypted extensions message + */ static status_t process_encrypted_extensions(private_tls_peer_t *this, bio_reader_t *reader) { chunk_t ext = chunk_empty; - int offset = 0; - uint16_t extension_type, extension_length; + uint16_t extension_type; - this->crypto->append_handshake(this->crypto, - TLS_ENCRYPTED_EXTENSIONS, reader->peek(reader)); + this->crypto->append_handshake(this->crypto, TLS_ENCRYPTED_EXTENSIONS, + reader->peek(reader)); if (!reader->read_data16(reader, &ext)) { @@ -302,31 +307,24 @@ static status_t process_encrypted_extensions(private_tls_peer_t *this, this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); return NEED_MORE; } - if (ext.len == 0) + if (ext.len) { - this->state = STATE_ENCRYPTED_EXTENSIONS_RECEIVED; - return NEED_MORE; - } - else - { - bio_reader_t *extension_reader = bio_reader_create(ext); + bio_reader_t *extensions = bio_reader_create(ext); - while (offset < ext.len) + while (extensions->remaining(extensions)) { - chunk_t extension_payload = chunk_empty; - extension_reader->read_uint16(extension_reader, &extension_type); - extension_reader->read_uint16(extension_reader, &extension_length); - offset += extension_length + 4; + chunk_t extension_data = chunk_empty; - if (!extension_reader->read_data(extension_reader, - extension_length, - &extension_payload)) + if (!extensions->read_uint16(extensions, &extension_type) || + !extensions->read_data16(extensions, &extension_data)) { - DBG2(DBG_TLS, "unable to read extension payload data"); + DBG1(DBG_TLS, "invalid extension in EncryptedExtensions"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + extensions->destroy(extensions); + return NEED_MORE; } switch (extension_type) { - /* fall through because not supported so far */ case TLS_EXT_SERVER_NAME: case TLS_EXT_MAX_FRAGMENT_LENGTH: case TLS_EXT_SUPPORTED_GROUPS: @@ -334,19 +332,22 @@ static status_t process_encrypted_extensions(private_tls_peer_t *this, case TLS_EXT_HEARTBEAT: case TLS_EXT_APPLICATION_LAYER_PROTOCOL_NEGOTIATION: case TLS_SERVER_CERTIFICATE_TYPE: - this->state = STATE_ENCRYPTED_EXTENSIONS_RECEIVED; - extension_reader->destroy(extension_reader); + /* not supported so far */ + DBG2(DBG_TLS, "ignoring unsupported %N EncryptedExtension", + tls_extension_names, extension_type); break; default: - DBG1(DBG_TLS, "received forbidden EncryptedExtensions"); + DBG1(DBG_TLS, "received forbidden EncryptedExtension (%d)", + extension_type); this->alert->add(this->alert, TLS_FATAL, TLS_ILLEGAL_PARAMETER); - extension_reader->destroy(extension_reader); + extensions->destroy(extensions); return NEED_MORE; } } - + extensions->destroy(extensions); } + this->state = STATE_ENCRYPTED_EXTENSIONS_RECEIVED; return NEED_MORE; } From bfa3178836e3aa62f6a266b12358d4601a416a18 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Aug 2020 16:17:27 +0200 Subject: [PATCH 025/107] tls-peer: Use existing code to verify certificate and signature --- src/libtls/tls_peer.c | 78 ++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 46 deletions(-) diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index f150dca04..4cba0bba4 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -478,52 +478,6 @@ static status_t process_certificate(private_tls_peer_t *this, return NEED_MORE; } -/** - * Process Certificate verify - */ -static status_t process_cert_verify(private_tls_peer_t *this, - bio_reader_t *reader) -{ - enumerator_t *enumerator; - public_key_t *public; - auth_cfg_t *auth; - bio_reader_t *sig; - bool verified = FALSE; - - enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, - KEY_ANY, this->server, - this->server_auth, TRUE); - while (enumerator->enumerate(enumerator, &public, &auth)) - { - sig = bio_reader_create(reader->peek(reader)); - verified = this->crypto->verify_handshake(this->crypto, public, sig); - sig->destroy(sig); - if (verified) - { - this->server_auth->merge(this->server_auth, auth, FALSE); - break; - } - DBG1(DBG_TLS, "signature verification failed, trying another key"); - } - enumerator->destroy(enumerator); - - if (!verified) - { - DBG1(DBG_TLS, "no trusted certificate found for '%Y' to verify TLS peer", - this->server); - this->server->destroy(this->server); - this->peer = NULL; - this->state = STATE_KEY_EXCHANGE_RECEIVED; - } - else - { - this->state = STATE_CERT_VERIFY_RECEIVED; - } - this->crypto->append_handshake(this->crypto, - TLS_CERTIFICATE_VERIFY, reader->peek(reader)); - return NEED_MORE; -} - /** * Find a trusted public key to encrypt/verify key exchange data */ @@ -555,6 +509,38 @@ static public_key_t *find_public_key(private_tls_peer_t *this) return public; } +/** + * Process CertificateVerify message + */ +static status_t process_cert_verify(private_tls_peer_t *this, + bio_reader_t *reader) +{ + public_key_t *public; + chunk_t msg; + + public = find_public_key(this); + if (!public) + { + DBG1(DBG_TLS, "no TLS public key found for server '%Y'", this->server); + this->alert->add(this->alert, TLS_FATAL, TLS_CERTIFICATE_UNKNOWN); + return NEED_MORE; + } + + msg = reader->peek(reader); + if (!this->crypto->verify_handshake(this->crypto, public, reader)) + { + public->destroy(public); + DBG1(DBG_TLS, "signature verification failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_BAD_CERTIFICATE); + return NEED_MORE; + } + public->destroy(public); + + this->crypto->append_handshake(this->crypto, TLS_CERTIFICATE_VERIFY, msg); + this->state = STATE_CERT_VERIFY_RECEIVED; + return NEED_MORE; +} + /** * Process a Key Exchange message using MODP Diffie Hellman */ From 44cda40d58c585ee4444eee10d32f0d11f9fcd1c Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Aug 2020 17:21:02 +0200 Subject: [PATCH 026/107] tls-peer: Simply ignore certificate request context This SHALL be zero length for server authentication anyway. --- src/libtls/tls_peer.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 4cba0bba4..fa81ee7e9 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -409,11 +409,6 @@ static status_t process_certificate(private_tls_peer_t *this, this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); return NEED_MORE; } - if (data.len > 0) - { - DBG1(DBG_TLS, "certificate request context available," - "but CertificateRequest not received"); - } } if (!reader->read_data24(reader, &data)) { From de31646a09a82d1feb0d8a6ea119d5f9020c022c Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Aug 2020 16:25:37 +0200 Subject: [PATCH 027/107] tls-peer: Refactor sending/processing finished message Also fixes leaks. --- src/libtls/tls_peer.c | 50 +++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index fa81ee7e9..8b81e67e8 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -840,8 +840,7 @@ static status_t process_hello_done(private_tls_peer_t *this, static status_t process_finished(private_tls_peer_t *this, bio_reader_t *reader) { chunk_t received, verify_data; - char buf[12]; - uint32_t hash_length; + u_char buf[12]; if (this->tls->get_version_max(this->tls) < TLS_1_3) { @@ -858,44 +857,38 @@ static status_t process_finished(private_tls_peer_t *this, bio_reader_t *reader) this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); return NEED_MORE; } - if (!chunk_equals_const(received, chunk_from_thing(buf))) - { - DBG1(DBG_TLS, "received server finished invalid"); - this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR); - return NEED_MORE; - } + verify_data = chunk_from_thing(buf); } else { - hash_length = reader->remaining(reader); - if (!reader->read_data(reader, hash_length, &received)) - { - DBG1(DBG_TLS, "received server finished too short"); - this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); - return NEED_MORE; - } - if (!this->crypto->calculate_finished(this->crypto, true, - &verify_data)) + received = reader->peek(reader); + if (!this->crypto->calculate_finished(this->crypto, TRUE, &verify_data)) { DBG1(DBG_TLS, "calculating server finished failed"); this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); return NEED_MORE; } - if (!chunk_equals(received, verify_data)) - { - DBG1(DBG_TLS, "received server finished invalid"); - this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR); - return NEED_MORE; - } } - this->state = STATE_FINISHED_RECEIVED; - this->crypto->append_handshake(this->crypto, TLS_FINISHED, received); + if (!chunk_equals_const(received, verify_data)) + { + DBG1(DBG_TLS, "received server finished invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR); + return NEED_MORE; + } + + if (verify_data.ptr != buf) + { + chunk_free(&verify_data); + } + + this->crypto->append_handshake(this->crypto, TLS_FINISHED, received); + this->state = STATE_FINISHED_RECEIVED; return NEED_MORE; } /** - * Process New Session Ticket message + * Process NewSessionTicket message */ static status_t process_new_session_ticket(private_tls_peer_t *this, bio_reader_t *reader) @@ -1440,8 +1433,6 @@ static status_t send_certificate_verify(private_tls_peer_t *this, static status_t send_finished(private_tls_peer_t *this, tls_handshake_type_t *type, bio_writer_t *writer) { - chunk_t verify_data; - if (this->tls->get_version_max(this->tls) < TLS_1_3) { char buf[12]; @@ -1458,6 +1449,8 @@ static status_t send_finished(private_tls_peer_t *this, } else { + chunk_t verify_data; + if (!this->crypto->calculate_finished(this->crypto, FALSE, &verify_data)) { DBG1(DBG_TLS, "calculating client finished data failed"); @@ -1466,6 +1459,7 @@ static status_t send_finished(private_tls_peer_t *this, } writer->write_data(writer, verify_data); + chunk_free(&verify_data); } *type = TLS_FINISHED; From 851b605e214efa06a40de74bb1ee1d9dc9962964 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Aug 2020 16:18:27 +0200 Subject: [PATCH 028/107] tls-peer: Refactor writing of extensions and use less hard-coded DH group Note that this breaks connecting to many TLS 1.3 servers until we support HelloRetryRequest as we now send a key_share for ECP_256 while still proposing other groups, so many servers request to use CURVE_25519. --- src/libtls/tls_peer.c | 77 ++++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 8b81e67e8..8efd6ef83 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -1034,17 +1034,18 @@ METHOD(tls_handshake_t, process, status_t, * Send a client hello */ static status_t send_client_hello(private_tls_peer_t *this, - tls_handshake_type_t *type, bio_writer_t *writer) + tls_handshake_type_t *type, + bio_writer_t *writer) { tls_cipher_suite_t *suites; - bio_writer_t *extensions, *curves = NULL; + bio_writer_t *extensions, *curves = NULL, *versions, *key_share; tls_version_t version_max, version_min; - tls_named_group_t curve; + diffie_hellman_group_t group; + tls_named_group_t curve, selected_curve = 0; enumerator_t *enumerator; int count, i, v; rng_t *rng; chunk_t pub; - uint8_t nof_tls_versions; htoun32(&this->client_random, time(NULL)); rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); @@ -1059,9 +1060,6 @@ static status_t send_client_hello(private_tls_peer_t *this, } rng->destroy(rng); - /* client key generation */ - this->dh = lib->crypto->create_dh(lib->crypto, CURVE_25519); - /* TLS version_max in handshake protocol */ version_max = this->tls->get_version_max(this->tls); version_min = this->tls->get_version_min(this->tls); @@ -1112,22 +1110,27 @@ static status_t send_client_hello(private_tls_peer_t *this, DBG2(DBG_TLS, "sending extension: %N", tls_extension_names, TLS_EXT_SUPPORTED_GROUPS); enumerator = this->crypto->create_ec_enumerator(this->crypto); - while (enumerator->enumerate(enumerator, NULL, &curve)) + while (enumerator->enumerate(enumerator, &group, &curve)) { if (!curves) { extensions->write_uint16(extensions, TLS_EXT_SUPPORTED_GROUPS); curves = bio_writer_create(16); } + if (!this->dh) + { + this->dh = lib->crypto->create_dh(lib->crypto, group); + if (!this->dh) + { + continue; + } + selected_curve = curve; + } curves->write_uint16(curves, curve); } enumerator->destroy(enumerator); if (curves) { - if (version_max == TLS_1_3) - { - curves->write_uint16(curves, TLS_CURVE25519); - } curves->wrap16(curves); extensions->write_data16(extensions, curves->get_buf(curves)); curves->destroy(curves); @@ -1139,16 +1142,19 @@ static status_t send_client_hello(private_tls_peer_t *this, extensions->write_uint8(extensions, TLS_EC_POINT_UNCOMPRESSED); } - DBG2(DBG_TLS, "sending extension: %N", - tls_extension_names, TLS_EXT_SUPPORTED_VERSIONS); - nof_tls_versions = (version_max - version_min + 1)*2; - extensions->write_uint16(extensions, TLS_EXT_SUPPORTED_VERSIONS); - extensions->write_uint16(extensions, nof_tls_versions+1); - extensions->write_uint8(extensions, nof_tls_versions); - for (v = version_max; v >= version_min; v--) + if (version_max >= TLS_1_3) { - extensions->write_uint16(extensions, v); - nof_tls_versions += 2; + DBG2(DBG_TLS, "sending extension: %N", + tls_extension_names, TLS_EXT_SUPPORTED_VERSIONS); + extensions->write_uint16(extensions, TLS_EXT_SUPPORTED_VERSIONS); + versions = bio_writer_create(0); + for (v = version_max; v >= version_min; v--) + { + versions->write_uint16(versions, v); + } + versions->wrap8(versions); + extensions->write_data16(extensions, versions->get_buf(versions)); + versions->destroy(versions); } DBG2(DBG_TLS, "sending extension: %N", @@ -1156,20 +1162,25 @@ static status_t send_client_hello(private_tls_peer_t *this, extensions->write_uint16(extensions, TLS_EXT_SIGNATURE_ALGORITHMS); this->crypto->get_signature_algorithms(this->crypto, extensions); - DBG2(DBG_TLS, "sending extension: %N", - tls_extension_names, TLS_EXT_KEY_SHARE); - if (!this->dh->get_my_public_value(this->dh, &pub)) + if (this->dh) { - this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); - return NEED_MORE; + DBG2(DBG_TLS, "sending extension: %N", + tls_extension_names, TLS_EXT_KEY_SHARE); + if (!this->dh->get_my_public_value(this->dh, &pub)) + { + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + extensions->destroy(extensions); + return NEED_MORE; + } + extensions->write_uint16(extensions, TLS_EXT_KEY_SHARE); + key_share = bio_writer_create(pub.len + 6); + key_share->write_uint16(key_share, selected_curve); + key_share->write_data16(key_share, pub); + key_share->wrap16(key_share); + extensions->write_data16(extensions, key_share->get_buf(key_share)); + key_share->destroy(key_share); + free(pub.ptr); } - extensions->write_uint16(extensions, TLS_EXT_KEY_SHARE); - extensions->write_uint16(extensions, pub.len+6); - extensions->write_uint16(extensions, pub.len+4); - extensions->write_uint16(extensions, TLS_CURVE25519); - extensions->write_uint16(extensions, pub.len); - extensions->write_data(extensions, pub); - free(pub.ptr); writer->write_data16(writer, extensions->get_buf(extensions)); extensions->destroy(extensions); From 64e63c68c86049c4979df405d6e362e8c9f20598 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Aug 2020 17:13:17 +0200 Subject: [PATCH 029/107] tls-crypto: Destroy HKDF instance if keys are derived multiple times This will be the case during a retry. --- src/libtls/tls_crypto.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 494a6930a..04fe4ef0f 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1270,6 +1270,7 @@ static void destroy_aeads(private_tls_crypto_t *this) static bool create_ciphers(private_tls_crypto_t *this, suite_algs_t *algs) { destroy_aeads(this); + DESTROY_IF(this->hkdf); DESTROY_IF(this->prf); if (this->tls->get_version_max(this->tls) < TLS_1_3) { From 2271d67f07a8b3c9ee93d5b8b592067e179ccf99 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Aug 2020 17:17:55 +0200 Subject: [PATCH 030/107] tls-crypto: Add method to hash handshake data and use result as initial transcript This is used for HelloRetryRequest. --- src/libtls/tls_crypto.c | 25 +++++++++++++++++++++++++ src/libtls/tls_crypto.h | 8 ++++++++ 2 files changed, 33 insertions(+) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 04fe4ef0f..39adcaebf 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1579,6 +1579,30 @@ static bool hash_data(private_tls_crypto_t *this, chunk_t data, chunk_t *hash) return TRUE; } +METHOD(tls_crypto_t, hash_handshake, bool, + private_tls_crypto_t *this, chunk_t *out) +{ + chunk_t hash; + + if (!hash_data(this, this->handshake, &hash)) + { + return FALSE; + } + + chunk_free(&this->handshake); + append_handshake(this, TLS_MESSAGE_HASH, hash); + + if (out) + { + *out = hash; + } + else + { + free(hash.ptr); + } + return TRUE; +} + /** * TLS 1.3 static part of the data the server signs (64 spaces followed by the * context string "TLS 1.3, server CertificateVerify" and a 0 byte). @@ -2118,6 +2142,7 @@ tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache) .create_ec_enumerator = _create_ec_enumerator, .set_protection = _set_protection, .append_handshake = _append_handshake, + .hash_handshake = _hash_handshake, .sign = _sign, .verify = _verify, .sign_handshake = _sign_handshake, diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h index 3f48cfe13..d8d7ebe72 100644 --- a/src/libtls/tls_crypto.h +++ b/src/libtls/tls_crypto.h @@ -474,6 +474,14 @@ struct tls_crypto_t { void (*append_handshake)(tls_crypto_t *this, tls_handshake_type_t type, chunk_t data); + /** + * Hash the stored handshake data and store it. It is optionally returned + * so it could be sent in a cookie extension. + * + * @param hash optionally returned hash (allocated) + */ + bool (*hash_handshake)(tls_crypto_t *this, chunk_t *hash); + /** * Sign a blob of data, append signature to writer. * From 2b6565c236dc7b4d4bfb967f7b1db34c834501d4 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Aug 2020 17:23:45 +0200 Subject: [PATCH 031/107] tls-peer: Handle HelloRetryRequest Adds support to handle retries with different DH group and/or a cookie extension. --- src/libtls/tls.c | 7 ++ src/libtls/tls.h | 6 ++ src/libtls/tls_peer.c | 149 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 154 insertions(+), 8 deletions(-) diff --git a/src/libtls/tls.c b/src/libtls/tls.c index 4a461c2cc..30f4786d5 100644 --- a/src/libtls/tls.c +++ b/src/libtls/tls.c @@ -122,6 +122,13 @@ ENUM_NEXT(tls_extension_names, "renegotiation info"); ENUM_END(tls_extension_names, TLS_EXT_RENEGOTIATION_INFO); +chunk_t tls_hello_retry_request_magic = chunk_from_chars( + 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, + 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91, + 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, + 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C, +); + /** * TLS record */ diff --git a/src/libtls/tls.h b/src/libtls/tls.h index 4509e05a3..005497f5a 100644 --- a/src/libtls/tls.h +++ b/src/libtls/tls.h @@ -189,6 +189,12 @@ enum tls_name_type_t { */ extern enum_name_t *tls_extension_names; +/** + * Magic value (SHA-256 of "HelloRetryRequest") for Random to differentiate + * ServerHello from HelloRetryRequest. + */ +extern chunk_t tls_hello_retry_request_magic; + /** * A bottom-up driven TLS stack, suitable for EAP implementations. */ diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 8efd6ef83..bc1857f69 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -125,6 +125,16 @@ struct private_tls_peer_t { */ diffie_hellman_t *dh; + /** + * Requested DH group + */ + tls_named_group_t requested_curve; + + /** + * Cookie extension received in HelloRetryRequest + */ + chunk_t cookie; + /** * Resuming a session? */ @@ -146,6 +156,40 @@ struct private_tls_peer_t { chunk_t cert_types; }; +/** + * Verify the DH group/key type requested by the server is valid. + */ +static bool verify_requested_key_type(private_tls_peer_t *this, + uint16_t key_type) +{ + enumerator_t *enumerator; + diffie_hellman_group_t group, found = MODP_NONE; + tls_named_group_t curve; + + enumerator = this->crypto->create_ec_enumerator(this->crypto); + while (enumerator->enumerate(enumerator, &group, &curve)) + { + if (key_type == curve) + { + found = group; + break; + } + } + enumerator->destroy(enumerator); + + if (found == MODP_NONE) + { + DBG1(DBG_TLS, "server requested key exchange we didn't propose"); + return FALSE; + } + if (this->dh->get_dh_group(this->dh) == found) + { + DBG1(DBG_TLS, "server requested key exchange we already use"); + return FALSE; + } + return TRUE; +} + /** * Process a server hello message */ @@ -153,14 +197,14 @@ static status_t process_server_hello(private_tls_peer_t *this, bio_reader_t *reader) { uint8_t compression; - uint16_t version, cipher, key_type; + uint16_t version, cipher, key_type = 0; bio_reader_t *extensions, *extension; - chunk_t random, session, ext = chunk_empty, key_share = chunk_empty; + chunk_t msg, random, session, ext = chunk_empty, key_share = chunk_empty; + chunk_t cookie = chunk_empty; tls_cipher_suite_t suite = 0; + bool is_retry_request; - this->crypto->append_handshake(this->crypto, - TLS_SERVER_HELLO, reader->peek(reader)); - + msg = reader->peek(reader); if (!reader->read_uint16(reader, &version) || !reader->read_data(reader, sizeof(this->server_random), &random) || !reader->read_data8(reader, &session) || @@ -173,6 +217,8 @@ static status_t process_server_hello(private_tls_peer_t *this, return NEED_MORE; } + is_retry_request = chunk_equals_const(random, tls_hello_retry_request_magic); + memcpy(this->server_random, random.ptr, sizeof(this->server_random)); extensions = bio_reader_create(ext); @@ -205,7 +251,8 @@ static status_t process_server_hello(private_tls_peer_t *this, break; case TLS_EXT_KEY_SHARE: if (!extension->read_uint16(extension, &key_type) || - !extension->read_data16(extension, &key_share)) + (!is_retry_request && + !extension->read_data16(extension, &key_share))) { DBG1(DBG_TLS, "invalid %N extension", tls_extension_names, extension_type); @@ -215,6 +262,16 @@ static status_t process_server_hello(private_tls_peer_t *this, return NEED_MORE; } break; + case TLS_EXT_COOKIE: + if (!extension->read_data16(extension, &cookie)) + { + DBG1(DBG_TLS, "invalid %N extension", tls_extension_names, + extension_type); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + extensions->destroy(extensions); + extension->destroy(extension); + return NEED_MORE; + } default: break; } @@ -266,6 +323,54 @@ static status_t process_server_hello(private_tls_peer_t *this, this->session = chunk_clone(session); } + if (is_retry_request) + { + if (!this->crypto->hash_handshake(this->crypto, NULL)) + { + DBG1(DBG_TLS, "failed to hash handshake messages"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + } + this->crypto->append_handshake(this->crypto, TLS_SERVER_HELLO, msg); + + if (is_retry_request) + { + if (key_type) + { + DBG1(DBG_TLS, "server requests key exchange with %N", + tls_named_group_names, key_type); + } + else if (cookie.len) + { + DBG1(DBG_TLS, "server requests retry with cookie"); + } + else + { + DBG1(DBG_TLS, "invalid retry request received"); + this->alert->add(this->alert, TLS_FATAL, TLS_ILLEGAL_PARAMETER); + return NEED_MORE; + } + if (this->requested_curve || this->cookie.len) + { + DBG1(DBG_TLS, "already replied to previous retry request"); + this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE); + return NEED_MORE; + } + if (key_type && !verify_requested_key_type(this, key_type)) + { + this->alert->add(this->alert, TLS_FATAL, TLS_ILLEGAL_PARAMETER); + return NEED_MORE; + } + + DESTROY_IF(this->dh); + this->dh = NULL; + this->requested_curve = key_type; + this->cookie = chunk_clone(cookie); + this->state = STATE_INIT; + return NEED_MORE; + } + if (this->tls->get_version_max(this->tls) == TLS_1_3) { chunk_t shared_secret = chunk_empty; @@ -1112,6 +1217,10 @@ static status_t send_client_hello(private_tls_peer_t *this, enumerator = this->crypto->create_ec_enumerator(this->crypto); while (enumerator->enumerate(enumerator, &group, &curve)) { + if (this->requested_curve && this->requested_curve != curve) + { + continue; + } if (!curves) { extensions->write_uint16(extensions, TLS_EXT_SUPPORTED_GROUPS); @@ -1157,6 +1266,16 @@ static status_t send_client_hello(private_tls_peer_t *this, versions->destroy(versions); } + if (this->cookie.len) + { + DBG2(DBG_TLS, "sending extension: %N", + tls_extension_names, TLS_EXT_COOKIE); + extensions->write_uint16(extensions, TLS_EXT_COOKIE); + extensions->write_uint16(extensions, this->cookie.len + 2); + extensions->write_data16(extensions, this->cookie); + chunk_free(&this->cookie); + } + DBG2(DBG_TLS, "sending extension: %N", tls_extension_names, TLS_EXT_SIGNATURE_ALGORITHMS); extensions->write_uint16(extensions, TLS_EXT_SIGNATURE_ALGORITHMS); @@ -1537,7 +1656,14 @@ METHOD(tls_handshake_t, build, status_t, return INVALID_STATE; } } +} +/** + * Check if we are currently retrying to connect to the server. + */ +static bool retrying(private_tls_peer_t *this) +{ + return this->state == STATE_INIT && (this->requested_curve || this->cookie.len); } METHOD(tls_handshake_t, cipherspec_changed, bool, @@ -1570,8 +1696,8 @@ METHOD(tls_handshake_t, cipherspec_changed, bool, else { if (inbound) - { - return this->state == STATE_HELLO_RECEIVED; + { /* accept ChangeCipherSpec after ServerHello or HelloRetryRequest */ + return this->state == STATE_HELLO_RECEIVED || retrying(this); } else { @@ -1589,6 +1715,12 @@ METHOD(tls_handshake_t, change_cipherspec, void, this->crypto->change_cipher(this->crypto, inbound); } + if (retrying(this)) + { /* servers might send a ChangeCipherSpec after a HelloRetryRequest, + * which should not cause any state changes */ + return; + } + if (inbound) { this->state = STATE_CIPHERSPEC_CHANGED_IN; @@ -1646,6 +1778,7 @@ METHOD(tls_handshake_t, destroy, void, free(this->hashsig.ptr); free(this->cert_types.ptr); free(this->session.ptr); + free(this->cookie.ptr); free(this); } From c4576a1f5789804c272439dd12626aecd3623fc7 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 26 Aug 2020 14:37:59 +0200 Subject: [PATCH 032/107] tls: Allow setting both minimum and maximum TLS versions This allows to increase the initial minimum version and also prevents sending a list of versions during retries when 1.3 was already negotiated. --- src/libtls/tls.c | 28 +++++++++++++--------------- src/libtls/tls.h | 15 +++++++++------ src/libtls/tls_peer.c | 2 +- src/libtls/tls_server.c | 2 +- src/libtls/tls_socket.c | 2 +- 5 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/libtls/tls.c b/src/libtls/tls.c index 30f4786d5..fc313e08e 100644 --- a/src/libtls/tls.c +++ b/src/libtls/tls.c @@ -436,26 +436,24 @@ METHOD(tls_t, get_version_min, tls_version_t, } METHOD(tls_t, set_version, bool, - private_tls_t *this, tls_version_t version) + private_tls_t *this, tls_version_t min_version, tls_version_t max_version) { - if (version > this->version_max) + if (min_version < this->version_min || + max_version > this->version_max || + min_version > max_version || + min_version < TLS_1_0) { return FALSE; } - switch (version) + + this->version_min = min_version; + this->version_max = max_version; + + if (min_version == max_version) { - case TLS_1_0: - case TLS_1_1: - case TLS_1_2: - case TLS_1_3: - this->version_max = version; - this->protection->set_version(this->protection, version); - return TRUE; - case SSL_2_0: - case SSL_3_0: - default: - return FALSE; + this->protection->set_version(this->protection, max_version); } + return TRUE; } METHOD(tls_t, get_purpose, tls_purpose_t, @@ -545,8 +543,8 @@ tls_t *tls_create(bool is_server, identification_t *server, .destroy = _destroy, }, .is_server = is_server, - .version_max = TLS_1_3, .version_min = TLS_1_0, + .version_max = TLS_1_3, .application = application, .purpose = purpose, ); diff --git a/src/libtls/tls.h b/src/libtls/tls.h index 005497f5a..b8903af5d 100644 --- a/src/libtls/tls.h +++ b/src/libtls/tls.h @@ -263,26 +263,29 @@ struct tls_t { identification_t* (*get_peer_id)(tls_t *this); /** - * Get the maximum and negotiated TLS/SSL version. + * Get the maximum and negotiated TLS version. * * @return max and negotiated TLS version */ tls_version_t (*get_version_max)(tls_t *this); /** - * Get the minimum TLS/SSL version. + * Get the minimum TLS version. * * @return min TLS version */ tls_version_t (*get_version_min)(tls_t *this); /** - * Set the negotiated TLS/SSL version. + * Set the initial minimum/maximum TLS version, or set both to the same + * value once negotiated. * - * @param version negotiated TLS version - * @return TRUE if version acceptable + * @param min_version minimum (or negotiated) TLS version + * @param max_version maximum (or negotiated) TLS version + * @return TRUE if version(s) acceptable */ - bool (*set_version)(tls_t *this, tls_version_t version); + bool (*set_version)(tls_t *this, tls_version_t min_version, + tls_version_t max_version); /** * Get the purpose of this TLS stack instance. diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index bc1857f69..50326d3f1 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -279,7 +279,7 @@ static status_t process_server_hello(private_tls_peer_t *this, } extensions->destroy(extensions); - if (!this->tls->set_version(this->tls, version)) + if (!this->tls->set_version(this->tls, version, version)) { DBG1(DBG_TLS, "negotiated version %N not supported", tls_version_names, version); diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 3e97d3799..f0bbb1a59 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -282,7 +282,7 @@ static status_t process_client_hello(private_tls_server_t *this, } rng->destroy(rng); - if (!this->tls->set_version(this->tls, version)) + if (!this->tls->set_version(this->tls, version, version)) { DBG1(DBG_TLS, "negotiated version %N not supported", tls_version_names, version); diff --git a/src/libtls/tls_socket.c b/src/libtls/tls_socket.c index d065bdb28..100475c7d 100644 --- a/src/libtls/tls_socket.c +++ b/src/libtls/tls_socket.c @@ -447,7 +447,7 @@ tls_socket_t *tls_socket_create(bool is_server, identification_t *server, free(this); return NULL; } - this->tls->set_version(this->tls, max_version); + this->tls->set_version(this->tls, TLS_1_0, max_version); return &this->public; } From a7f2818832a18ec5965ab7634610e5e997d0c20d Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 26 Aug 2020 14:40:51 +0200 Subject: [PATCH 033/107] tls-socket: Allow configuring both minimum and maximum TLS versions --- scripts/tls_test.c | 5 +++-- src/libcharon/plugins/tnc_ifmap/tnc_ifmap_soap.c | 2 +- src/libpttls/pt_tls_client.c | 2 +- src/libpttls/pt_tls_server.c | 3 ++- src/libtls/tests/suites/test_socket.c | 4 ++-- src/libtls/tls_socket.c | 10 +++++----- src/libtls/tls_socket.h | 6 ++++-- 7 files changed, 18 insertions(+), 14 deletions(-) diff --git a/scripts/tls_test.c b/scripts/tls_test.c index c47c1cae1..5e9e95462 100644 --- a/scripts/tls_test.c +++ b/scripts/tls_test.c @@ -106,7 +106,7 @@ static int run_client(host_t *host, identification_t *server, close(fd); return 1; } - tls = tls_socket_create(FALSE, server, client, fd, cache, + tls = tls_socket_create(FALSE, server, client, fd, cache, TLS_1_0, TLS_1_3, TRUE); if (!tls) { @@ -164,7 +164,8 @@ static int serve(host_t *host, identification_t *server, } DBG1(DBG_TLS, "%#H connected", host); - tls = tls_socket_create(TRUE, server, NULL, cfd, cache, TLS_1_2, TRUE); + tls = tls_socket_create(TRUE, server, NULL, cfd, cache, TLS_1_0, + TLS_1_2, TRUE); if (!tls) { close(fd); diff --git a/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_soap.c b/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_soap.c index 8e69de095..ab8c7273b 100644 --- a/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_soap.c +++ b/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_soap.c @@ -877,7 +877,7 @@ static bool soap_init(private_tnc_ifmap_soap_t *this) /* open TLS socket */ this->tls = tls_socket_create(FALSE, server_id, client_id, this->fd, - NULL, TLS_1_2, FALSE); + NULL, TLS_1_0, TLS_1_2, FALSE); if (!this->tls) { DBG1(DBG_TNC, "creating TLS socket failed"); diff --git a/src/libpttls/pt_tls_client.c b/src/libpttls/pt_tls_client.c index b79b92763..59842368f 100644 --- a/src/libpttls/pt_tls_client.c +++ b/src/libpttls/pt_tls_client.c @@ -85,7 +85,7 @@ static bool make_connection(private_pt_tls_client_t *this) } this->tls = tls_socket_create(FALSE, this->server, this->client, fd, - NULL, TLS_1_2, FALSE); + NULL, TLS_1_0, TLS_1_2, FALSE); if (!this->tls) { close(fd); diff --git a/src/libpttls/pt_tls_server.c b/src/libpttls/pt_tls_server.c index 0168b1802..4c484fbb8 100644 --- a/src/libpttls/pt_tls_server.c +++ b/src/libpttls/pt_tls_server.c @@ -532,7 +532,8 @@ pt_tls_server_t *pt_tls_server_create(identification_t *server, int fd, .destroy = _destroy, }, .state = PT_TLS_SERVER_VERSION, - .tls = tls_socket_create(TRUE, server, NULL, fd, NULL, TLS_1_2, FALSE), + .tls = tls_socket_create(TRUE, server, NULL, fd, NULL, TLS_1_0, TLS_1_2, + FALSE), .tnccs = (tls_t*)tnccs, .auth = auth, ); diff --git a/src/libtls/tests/suites/test_socket.c b/src/libtls/tests/suites/test_socket.c index a637096cd..a5f80e59e 100644 --- a/src/libtls/tests/suites/test_socket.c +++ b/src/libtls/tests/suites/test_socket.c @@ -298,7 +298,7 @@ static job_requeue_t serve_echo(echo_server_config_t *config) } tls = tls_socket_create(TRUE, server, client, cfd, NULL, - config->version, TRUE); + TLS_1_0, config->version, TRUE); ck_assert(tls != NULL); while (TRUE) @@ -374,7 +374,7 @@ static void run_echo_client(echo_server_config_t *config) ck_assert(connect(fd, host->get_sockaddr(host), *host->get_sockaddr_len(host)) != -1); tls = tls_socket_create(FALSE, server, client, fd, NULL, - config->version, TRUE); + TLS_1_0, config->version, TRUE); ck_assert(tls != NULL); wr = rd = 0; diff --git a/src/libtls/tls_socket.c b/src/libtls/tls_socket.c index 100475c7d..f29a369f1 100644 --- a/src/libtls/tls_socket.c +++ b/src/libtls/tls_socket.c @@ -405,8 +405,9 @@ METHOD(tls_socket_t, destroy, void, * See header */ tls_socket_t *tls_socket_create(bool is_server, identification_t *server, - identification_t *peer, int fd, tls_cache_t *cache, - tls_version_t max_version, bool nullok) + identification_t *peer, int fd, + tls_cache_t *cache, tls_version_t min_version, + tls_version_t max_version, bool nullok) { private_tls_socket_t *this; tls_purpose_t purpose; @@ -442,12 +443,11 @@ tls_socket_t *tls_socket_create(bool is_server, identification_t *server, this->tls = tls_create(is_server, server, peer, purpose, &this->app.application, cache); - if (!this->tls) + if (!this->tls || + !this->tls->set_version(this->tls, min_version, max_version)) { free(this); return NULL; } - this->tls->set_version(this->tls, TLS_1_0, max_version); - return &this->public; } diff --git a/src/libtls/tls_socket.h b/src/libtls/tls_socket.h index 7924c585c..2026cba41 100644 --- a/src/libtls/tls_socket.h +++ b/src/libtls/tls_socket.h @@ -104,12 +104,14 @@ struct tls_socket_t { * @param peer client identity, NULL for no client authentication * @param fd socket to read/write from * @param cache session cache to use, or NULL + * @param min_version minimum TLS version to negotiate * @param max_version maximum TLS version to negotiate * @param nullok accept NULL encryption ciphers * @return TLS socket wrapper */ tls_socket_t *tls_socket_create(bool is_server, identification_t *server, - identification_t *peer, int fd, tls_cache_t *cache, - tls_version_t max_version, bool nullok); + identification_t *peer, int fd, + tls_cache_t *cache, tls_version_t min_version, + tls_version_t max_version, bool nullok); #endif /** TLS_SOCKET_H_ @}*/ From 8e35b1f1a57cf5ac73a517828910da3e5d73f515 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 26 Aug 2020 15:00:30 +0200 Subject: [PATCH 034/107] tls-test: Add options to configure TLS versions --- scripts/tls_test.c | 54 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/scripts/tls_test.c b/scripts/tls_test.c index 5e9e95462..4e3a1ccfa 100644 --- a/scripts/tls_test.c +++ b/scripts/tls_test.c @@ -85,7 +85,8 @@ static identification_t *find_client_id() * Client routine */ static int run_client(host_t *host, identification_t *server, - identification_t *client, int times, tls_cache_t *cache) + identification_t *client, int times, tls_cache_t *cache, + tls_version_t min_version, tls_version_t max_version) { tls_socket_t *tls; int fd, res; @@ -106,8 +107,8 @@ static int run_client(host_t *host, identification_t *server, close(fd); return 1; } - tls = tls_socket_create(FALSE, server, client, fd, cache, TLS_1_0, - TLS_1_3, TRUE); + tls = tls_socket_create(FALSE, server, client, fd, cache, min_version, + max_version, TRUE); if (!tls) { close(fd); @@ -128,7 +129,8 @@ static int run_client(host_t *host, identification_t *server, * Server routine */ static int serve(host_t *host, identification_t *server, - int times, tls_cache_t *cache) + int times, tls_cache_t *cache, tls_version_t min_version, + tls_version_t max_version) { tls_socket_t *tls; int fd, cfd; @@ -164,8 +166,8 @@ static int serve(host_t *host, identification_t *server, } DBG1(DBG_TLS, "%#H connected", host); - tls = tls_socket_create(TRUE, server, NULL, cfd, cache, TLS_1_0, - TLS_1_2, TRUE); + tls = tls_socket_create(TRUE, server, NULL, cfd, cache, min_version, + max_version, TRUE); if (!tls) { close(fd); @@ -266,12 +268,22 @@ static void init() atexit(cleanup); } +/** + * Used to parse TLS versions + */ +ENUM(numeric_version_names, TLS_1_0, TLS_1_3, + "1.0", + "1.1", + "1.2", + "1.3"); + int main(int argc, char *argv[]) { char *address = NULL; bool listen = FALSE; int port = 0, times = -1, res, family = AF_UNSPEC; identification_t *server, *client; + tls_version_t min_version = TLS_1_0, max_version = TLS_1_3; tls_cache_t *cache; host_t *host; @@ -289,6 +301,9 @@ int main(int argc, char *argv[]) {"times", required_argument, NULL, 't' }, {"ipv4", no_argument, NULL, '4' }, {"ipv6", no_argument, NULL, '6' }, + {"min-version", required_argument, NULL, 'm' }, + {"max-version", required_argument, NULL, 'M' }, + {"version", required_argument, NULL, 'v' }, {"debug", required_argument, NULL, 'd' }, {0,0,0,0 } }; @@ -337,6 +352,28 @@ int main(int argc, char *argv[]) case '6': family = AF_INET6; continue; + case 'm': + if (!enum_from_name(numeric_version_names, optarg, &min_version)) + { + fprintf(stderr, "unknown minimum TLS version: %s\n", optarg); + return 1; + } + continue; + case 'M': + if (!enum_from_name(numeric_version_names, optarg, &max_version)) + { + fprintf(stderr, "unknown maximum TLS version: %s\n", optarg); + return 1; + } + continue; + case 'v': + if (!enum_from_name(numeric_version_names, optarg, &min_version)) + { + fprintf(stderr, "unknown TLS version: %s\n", optarg); + return 1; + } + max_version = min_version; + continue; default: usage(stderr, argv[0]); return 1; @@ -358,12 +395,13 @@ int main(int argc, char *argv[]) cache = tls_cache_create(100, 30); if (listen) { - res = serve(host, server, times, cache); + res = serve(host, server, times, cache, min_version, max_version); } else { client = find_client_id(); - res = run_client(host, server, client, times, cache); + res = run_client(host, server, client, times, cache, min_version, + max_version); DESTROY_IF(client); } cache->destroy(cache); From d532d6c7bc8ba5d746cb522d9633c11671a956bb Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 26 Aug 2020 16:08:26 +0200 Subject: [PATCH 035/107] tls-peer: Correctly handle classic ECDH key-share format Similar to TLS 1.2 but uses a 16-bit length header. --- src/libtls/tls_peer.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 50326d3f1..95fee05fe 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -252,7 +252,8 @@ static status_t process_server_hello(private_tls_peer_t *this, case TLS_EXT_KEY_SHARE: if (!extension->read_uint16(extension, &key_type) || (!is_retry_request && - !extension->read_data16(extension, &key_share))) + !(extension->read_data16(extension, &key_share) && + key_share.len))) { DBG1(DBG_TLS, "invalid %N extension", tls_extension_names, extension_type); @@ -371,11 +372,25 @@ static status_t process_server_hello(private_tls_peer_t *this, return NEED_MORE; } - if (this->tls->get_version_max(this->tls) == TLS_1_3) + if (this->tls->get_version_max(this->tls) >= TLS_1_3) { chunk_t shared_secret = chunk_empty; - if (!this->dh->set_other_public_value(this->dh, key_share) || + if (key_share.len && + key_type != TLS_CURVE25519 && + key_type != TLS_CURVE448) + { /* classic format (see RFC 8446, section 4.2.8.2) */ + if (key_share.ptr[0] != TLS_ANSI_UNCOMPRESSED) + { + DBG1(DBG_TLS, "DH point format '%N' not supported", + tls_ansi_point_format_names, key_share.ptr[0]); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + key_share = chunk_skip(key_share, 1); + } + if (!key_share.len || + !this->dh->set_other_public_value(this->dh, key_share) || !this->dh->get_shared_secret(this->dh, &shared_secret) || !this->crypto->derive_handshake_keys(this->crypto, shared_secret)) { @@ -1294,7 +1309,17 @@ static status_t send_client_hello(private_tls_peer_t *this, extensions->write_uint16(extensions, TLS_EXT_KEY_SHARE); key_share = bio_writer_create(pub.len + 6); key_share->write_uint16(key_share, selected_curve); - key_share->write_data16(key_share, pub); + if (selected_curve == TLS_CURVE25519 || + selected_curve == TLS_CURVE448) + { + key_share->write_data16(key_share, pub); + } + else + { /* classic format (see RFC 8446, section 4.2.8.2) */ + key_share->write_uint16(key_share, pub.len + 1); + key_share->write_uint8(key_share, TLS_ANSI_UNCOMPRESSED); + key_share->write_data(key_share, pub); + } key_share->wrap16(key_share); extensions->write_data16(extensions, key_share->get_buf(key_share)); key_share->destroy(key_share); From 09fbaad6bd712b5480d9c1d8a9bfa766171b783d Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 26 Aug 2020 17:42:21 +0200 Subject: [PATCH 036/107] tls-socket: Don't fail reading if sending data failed If data is processed that eventually includes a TLS close notify, build() will fail after a close notify has been sent in turn. However, propagating that error immediately when reading prevented ever returning the data already processed before the close notify was received. --- src/libtls/tls_socket.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libtls/tls_socket.c b/src/libtls/tls_socket.c index f29a369f1..7745988fb 100644 --- a/src/libtls/tls_socket.c +++ b/src/libtls/tls_socket.c @@ -188,7 +188,11 @@ static bool exchange(private_tls_socket_t *this, bool wr, bool block) case SUCCESS: return TRUE; default: - return FALSE; + if (wr) + { + return FALSE; + } + break; } break; } From b51c1d468eaba77e12ab7fcddabc524d47fcbb45 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 27 Aug 2020 11:50:00 +0200 Subject: [PATCH 037/107] tls-hkdf: Support updating client/server traffic secrets --- src/libtls/tls_hkdf.c | 58 ++++++++++++++++++++++++++++++++++--------- src/libtls/tls_hkdf.h | 2 ++ 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/src/libtls/tls_hkdf.c b/src/libtls/tls_hkdf.c index 566916a23..97ba35638 100644 --- a/src/libtls/tls_hkdf.c +++ b/src/libtls/tls_hkdf.c @@ -403,27 +403,61 @@ METHOD(tls_hkdf_t, generate_secret, bool, return FALSE; } break; + case TLS_HKDF_UPD_C_TRAFFIC: + case TLS_HKDF_UPD_S_TRAFFIC: + if (this->phase != HKDF_PHASE_3) + { + DBG1(DBG_TLS, "unable to update traffic keys"); + return FALSE; + } + break; default: DBG1(DBG_TLS, "invalid HKDF label"); return FALSE; } - if (!derive_secret(this, chunk_from_str(hkdf_labels[label]), messages)) + if (label == TLS_HKDF_UPD_C_TRAFFIC || label == TLS_HKDF_UPD_S_TRAFFIC) { - DBG1(DBG_TLS, "unable to derive secret"); - return FALSE; + chunk_t previous = this->client_traffic_secret; + + if (label == TLS_HKDF_UPD_S_TRAFFIC) + { + previous = this->server_traffic_secret; + } + + if (!expand_label(this, previous, chunk_from_str("tls13 traffic upd"), + chunk_empty, this->hasher->get_hash_size(this->hasher), + &this->okm)) + { + DBG1(DBG_TLS, "unable to update secret"); + return FALSE; + } + } + else + { + if (!derive_secret(this, chunk_from_str(hkdf_labels[label]), messages)) + { + DBG1(DBG_TLS, "unable to derive secret"); + return FALSE; + } } - if (label == TLS_HKDF_C_HS_TRAFFIC || label == TLS_HKDF_C_AP_TRAFFIC) + switch (label) { - chunk_clear(&this->client_traffic_secret); - this->client_traffic_secret = chunk_clone(this->okm); - } - - if (label == TLS_HKDF_S_HS_TRAFFIC || label == TLS_HKDF_S_AP_TRAFFIC) - { - chunk_clear(&this->server_traffic_secret); - this->server_traffic_secret = chunk_clone(this->okm); + case TLS_HKDF_C_HS_TRAFFIC: + case TLS_HKDF_C_AP_TRAFFIC: + case TLS_HKDF_UPD_C_TRAFFIC: + chunk_clear(&this->client_traffic_secret); + this->client_traffic_secret = chunk_clone(this->okm); + break; + case TLS_HKDF_S_HS_TRAFFIC: + case TLS_HKDF_S_AP_TRAFFIC: + case TLS_HKDF_UPD_S_TRAFFIC: + chunk_clear(&this->server_traffic_secret); + this->server_traffic_secret = chunk_clone(this->okm); + break; + default: + break; } if (secret) diff --git a/src/libtls/tls_hkdf.h b/src/libtls/tls_hkdf.h index 5a8a77f5b..352d8e399 100644 --- a/src/libtls/tls_hkdf.h +++ b/src/libtls/tls_hkdf.h @@ -42,6 +42,8 @@ enum tls_hkdf_label_t { TLS_HKDF_S_AP_TRAFFIC, TLS_HKDF_EXP_MASTER, TLS_HKDF_RES_MASTER, + TLS_HKDF_UPD_C_TRAFFIC, + TLS_HKDF_UPD_S_TRAFFIC, }; /** From 1466d4da254f767a56b4b79c0409e2892b46ff7f Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 27 Aug 2020 11:51:00 +0200 Subject: [PATCH 038/107] tls-crypto: Add method to update application traffic keys --- src/libtls/tls_crypto.c | 85 ++++++++++++++++++++++------------------- src/libtls/tls_crypto.h | 8 ++++ 2 files changed, 53 insertions(+), 40 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 39adcaebf..0aab3616d 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1978,51 +1978,26 @@ METHOD(tls_crypto_t, derive_secrets, bool, } /** - * Derive and configure the keys/IVs using the given labels. + * Derive and configure the client/server key/IV on an AEAD using a given label. */ -static bool derive_labeled_keys(private_tls_crypto_t *this, - tls_hkdf_label_t client_label, - tls_hkdf_label_t server_label) +static bool derive_labeled_key(private_tls_crypto_t *this, bool server, + tls_hkdf_label_t label, tls_aead_t *aead) { - chunk_t c_key = chunk_empty, c_iv = chunk_empty; - chunk_t s_key = chunk_empty, s_iv = chunk_empty; - tls_aead_t *aead_c = this->aead_out, *aead_s = this->aead_in; + chunk_t key = chunk_empty, iv = chunk_empty; bool success = FALSE; - if (this->tls->is_server(this->tls)) - { - aead_c = this->aead_in; - aead_s = this->aead_out; - } - - if (!this->hkdf->generate_secret(this->hkdf, client_label, this->handshake, + if (!this->hkdf->generate_secret(this->hkdf, label, this->handshake, NULL) || - !this->hkdf->derive_key(this->hkdf, FALSE, - this->aead_out->get_encr_key_size(this->aead_out), - &c_key) || - !this->hkdf->derive_iv(this->hkdf, FALSE, - this->aead_out->get_iv_size(this->aead_out), - &c_iv)) + !this->hkdf->derive_key(this->hkdf, server, + aead->get_encr_key_size(aead), &key) || + !this->hkdf->derive_iv(this->hkdf, server, + aead->get_iv_size(aead), &iv)) { - DBG1(DBG_TLS, "deriving client key material failed"); + DBG1(DBG_TLS, "deriving key material failed"); goto out; } - if (!this->hkdf->generate_secret(this->hkdf, server_label, this->handshake, - NULL) || - !this->hkdf->derive_key(this->hkdf, TRUE, - this->aead_in->get_encr_key_size(this->aead_in), - &s_key) || - !this->hkdf->derive_iv(this->hkdf, TRUE, - this->aead_in->get_iv_size(this->aead_in), - &s_iv)) - { - DBG1(DBG_TLS, "deriving server key material failed"); - goto out; - } - - if (!aead_c->set_keys(aead_c, chunk_empty, c_key, c_iv) || - !aead_s->set_keys(aead_s, chunk_empty, s_key, s_iv)) + if (!aead->set_keys(aead, chunk_empty, key, iv)) { DBG1(DBG_TLS, "setting AEAD key material failed"); goto out; @@ -2030,13 +2005,29 @@ static bool derive_labeled_keys(private_tls_crypto_t *this, success = TRUE; out: - chunk_clear(&c_key); - chunk_clear(&c_iv); - chunk_clear(&s_key); - chunk_clear(&s_iv); + chunk_clear(&key); + chunk_clear(&iv); return success; } +/** + * Derive and configure the keys/IVs using the given labels. + */ +static bool derive_labeled_keys(private_tls_crypto_t *this, + tls_hkdf_label_t client_label, + tls_hkdf_label_t server_label) +{ + tls_aead_t *aead_c = this->aead_out, *aead_s = this->aead_in; + + if (this->tls->is_server(this->tls)) + { + aead_c = this->aead_in; + aead_s = this->aead_out; + } + return derive_labeled_key(this, FALSE, client_label, aead_c) && + derive_labeled_key(this, TRUE, server_label, aead_s); +} + METHOD(tls_crypto_t, derive_handshake_keys, bool, private_tls_crypto_t *this, chunk_t shared_secret) { @@ -2052,6 +2043,19 @@ METHOD(tls_crypto_t, derive_app_keys, bool, TLS_HKDF_S_AP_TRAFFIC); } +METHOD(tls_crypto_t, update_app_keys, bool, + private_tls_crypto_t *this, bool inbound) +{ + tls_hkdf_label_t label = TLS_HKDF_UPD_C_TRAFFIC; + + if (this->tls->is_server(this->tls) != inbound) + { + label = TLS_HKDF_UPD_S_TRAFFIC; + } + return derive_labeled_key(this, label == TLS_HKDF_UPD_S_TRAFFIC, label, + inbound ? this->aead_in : this->aead_out); +} + METHOD(tls_crypto_t, resume_session, tls_cipher_suite_t, private_tls_crypto_t *this, chunk_t session, identification_t *id, chunk_t client_random, chunk_t server_random) @@ -2152,6 +2156,7 @@ tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache) .derive_secrets = _derive_secrets, .derive_handshake_keys = _derive_handshake_keys, .derive_app_keys = _derive_app_keys, + .update_app_keys = _update_app_keys, .resume_session = _resume_session, .get_session = _get_session, .change_cipher = _change_cipher, diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h index d8d7ebe72..de6e56bad 100644 --- a/src/libtls/tls_crypto.h +++ b/src/libtls/tls_crypto.h @@ -574,6 +574,14 @@ struct tls_crypto_t { */ bool (*derive_app_keys)(tls_crypto_t *this); + /** + * Update the application keys. + * + * @param inbound whether to update the in- or outbound keys + * @return TRUE if secret derived successfully + */ + bool (*update_app_keys)(tls_crypto_t *this, bool inbound); + /** * Try to resume a TLS session, derive key material. * From bfcb49b3935b38f580c34451268483025bc3fba7 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 27 Aug 2020 11:51:58 +0200 Subject: [PATCH 039/107] tls-peer: Add support to handle KeyUpdate message We currently don't support switching our own keys and sending the message if requested by the server. --- src/libtls/tls_peer.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 95fee05fe..a63b9fc5b 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -1029,6 +1029,36 @@ static status_t process_new_session_ticket(private_tls_peer_t *this, return NEED_MORE; } +/** + * Process KeyUpdate message + */ +static status_t process_key_update(private_tls_peer_t *this, + bio_reader_t *reader) +{ + uint8_t update_requested; + + if (!reader->read_uint8(reader, &update_requested) || + update_requested > 1) + { + DBG1(DBG_TLS, "received invalid KeyUpdate"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + + if (!this->crypto->update_app_keys(this->crypto, TRUE)) + { + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + this->crypto->change_cipher(this->crypto, TRUE); + + if (update_requested) + { + DBG1(DBG_TLS, "server requested KeyUpdate, currently not supported"); + } + return NEED_MORE; +} + METHOD(tls_handshake_t, process, status_t, private_tls_peer_t *this, tls_handshake_type_t type, bio_reader_t *reader) { @@ -1135,6 +1165,10 @@ METHOD(tls_handshake_t, process, status_t, { return process_new_session_ticket(this, reader); } + if (type == TLS_KEY_UPDATE) + { + return process_key_update(this, reader); + } expected = TLS_NEW_SESSION_TICKET; break; default: From 27360d34076c5d780990d409c468790b9bbbe37c Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 28 Aug 2020 11:56:36 +0200 Subject: [PATCH 040/107] tls-socket: Change how EOF of the underlying socket is handled With the previous code, there was an issue when replying to TLS 1.3 post-handshake messages. In this case, SUCCESS is eventually returned from build(), however, no actual data has been received so in_done is 0. This was interpreted as EOF, plain_eof was set to TRUE and no further data was read from the socket afterwards. Returning SUCCESS from build() if the exchange is initiated by write_(), as is the case with the finished reply, never was a problem because there the return value of 0 is not interpreted as EOF. --- src/libtls/tls_socket.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/libtls/tls_socket.c b/src/libtls/tls_socket.c index 7745988fb..8b427dd1a 100644 --- a/src/libtls/tls_socket.c +++ b/src/libtls/tls_socket.c @@ -101,6 +101,11 @@ struct private_tls_socket_t { * Underlying OS socket */ int fd; + + /** + * Whether the socket returned EOF + */ + bool eof; }; METHOD(tls_application_t, process, status_t, @@ -236,6 +241,7 @@ static bool exchange(private_tls_socket_t *this, bool wr, bool block) } if (in == 0) { /* EOF */ + this->eof = TRUE; return TRUE; } switch (this->tls->process(this->tls, buf, in)) @@ -268,11 +274,20 @@ METHOD(tls_socket_t, read_, ssize_t, } return cache; } + if (this->eof) + { + return 0; + } this->app.in.ptr = buf; this->app.in.len = len; this->app.in_done = 0; if (exchange(this, FALSE, block)) { + if (!this->app.in_done && !this->eof) + { + errno = EWOULDBLOCK; + return -1; + } return this->app.in_done; } return -1; @@ -296,13 +311,13 @@ METHOD(tls_socket_t, splice, bool, { char buf[PLAIN_BUF_SIZE], *pos; ssize_t in, out; - bool old, plain_eof = FALSE, crypto_eof = FALSE; + bool old, crypto_eof = FALSE; struct pollfd pfd[] = { { .fd = this->fd, .events = POLLIN, }, { .fd = rfd, .events = POLLIN, }, }; - while (!plain_eof && !crypto_eof) + while (!this->eof && !crypto_eof) { old = thread_cancelability(TRUE); in = poll(pfd, countof(pfd), -1); @@ -312,14 +327,11 @@ METHOD(tls_socket_t, splice, bool, DBG1(DBG_TLS, "TLS select error: %s", strerror(errno)); return FALSE; } - while (!plain_eof && pfd[0].revents & (POLLIN | POLLHUP | POLLNVAL)) + while (!this->eof && pfd[0].revents & (POLLIN | POLLHUP | POLLNVAL)) { in = read_(this, buf, sizeof(buf), FALSE); switch (in) { - case 0: - plain_eof = TRUE; - break; case -1: if (errno != EWOULDBLOCK) { From e02f19e3c6a6463f5d799e54e23788e76be63a58 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 28 Aug 2020 10:23:43 +0200 Subject: [PATCH 041/107] tls-peer: Support answering KeyUpdate requests --- src/libtls/tls_fragmentation.c | 1 + src/libtls/tls_peer.c | 31 ++++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/libtls/tls_fragmentation.c b/src/libtls/tls_fragmentation.c index 483613fff..f2aa1b59d 100644 --- a/src/libtls/tls_fragmentation.c +++ b/src/libtls/tls_fragmentation.c @@ -358,6 +358,7 @@ static status_t build_handshake(private_tls_fragmentation_t *this) DBG2(DBG_TLS, "sending TLS %N handshake (%u bytes)", tls_handshake_type_names, type, hs->get_buf(hs).len); if (type != TLS_FINISHED && + type != TLS_KEY_UPDATE && !this->handshake->cipherspec_changed(this->handshake, FALSE)) { hs->destroy(hs); diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index a63b9fc5b..04c007289 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -47,6 +47,8 @@ typedef enum { STATE_ENCRYPTED_EXTENSIONS_RECEIVED, STATE_CERT_VERIFY_RECEIVED, STATE_FINISHED_SENT_KEY_SWITCHED, + STATE_KEY_UPDATE_REQUESTED, + STATE_KEY_UPDATE_SENT, } peer_state_t; @@ -1054,7 +1056,8 @@ static status_t process_key_update(private_tls_peer_t *this, if (update_requested) { - DBG1(DBG_TLS, "server requested KeyUpdate, currently not supported"); + DBG1(DBG_TLS, "server requested KeyUpdate"); + this->state = STATE_KEY_UPDATE_REQUESTED; } return NEED_MORE; } @@ -1657,6 +1660,21 @@ static status_t send_finished(private_tls_peer_t *this, return NEED_MORE; } +/** + * Send KeyUpdate message + */ +static status_t send_key_update(private_tls_peer_t *this, + tls_handshake_type_t *type, bio_writer_t *writer) +{ + *type = TLS_KEY_UPDATE; + + /* we currently only send this as reply, so we never request an update */ + writer->write_uint8(writer, 0); + + this->state = STATE_KEY_UPDATE_SENT; + return NEED_MORE; +} + METHOD(tls_handshake_t, build, status_t, private_tls_peer_t *this, tls_handshake_type_t *type, bio_writer_t *writer) { @@ -1709,6 +1727,17 @@ METHOD(tls_handshake_t, build, status_t, this->crypto->change_cipher(this->crypto, FALSE); this->state = STATE_FINISHED_SENT_KEY_SWITCHED; return SUCCESS; + case STATE_KEY_UPDATE_REQUESTED: + return send_key_update(this, type, writer); + case STATE_KEY_UPDATE_SENT: + if (!this->crypto->update_app_keys(this->crypto, FALSE)) + { + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + this->crypto->change_cipher(this->crypto, FALSE); + this->state = STATE_FINISHED_SENT_KEY_SWITCHED; + return SUCCESS; case STATE_FINISHED_SENT_KEY_SWITCHED: return SUCCESS; default: From 1b593e1d4868eaf33bf0b826e2ea433dd3b615a9 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 28 Aug 2020 08:59:37 +0200 Subject: [PATCH 042/107] tls-crypto: Add support for RSA-PSS signatures PKCS#1 v1.5 signatures are not defined for use with TLS 1.3 (they can only appear in certificates, we now send a signature_algorithms_cert extension to indicate support for them). So for RSA certificates, we must support RSA-PSS signatures. There are two sets of schemes, that are differentiated by the type of RSA key used for the signature, one is for classic RSA keys (rsaEncryption OID), which can also be used with PKCS#1 when using TLS 1.2, the other is for RSA-PSS keys (RSASSA-PSS OID), which are not yet commonly used (and can't be generated by our pki tool). According to the RFC, PSS must also be supported for TLS 1.2 if the schemes are included in the signature_algorithms extension (e.g. OpenSSL does not use PKCS#1 v1.5 anymore if PSS is proposed). This changes how these schemes are stored and enumerated (they are not treated as combination of hash algo and key type anymore). Legacy schemes (MD5/SHA-1) are removed. --- src/libtls/tls_crypto.c | 211 ++++++++++++++++++++++++---------------- src/libtls/tls_crypto.h | 43 +++++--- src/libtls/tls_peer.c | 6 +- src/libtls/tls_server.c | 2 +- 4 files changed, 167 insertions(+), 95 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 0aab3616d..906c99baa 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -263,11 +263,42 @@ ENUM(tls_hash_algorithm_names, TLS_HASH_NONE, TLS_HASH_SHA512, "SHA512", ); -ENUM(tls_signature_algorithm_names, TLS_SIG_RSA, TLS_SIG_ECDSA, - "RSA", - "DSA", - "ECDSA", +ENUM_BEGIN(tls_signature_scheme_names, + TLS_SIG_RSA_PKCS1_SHA1, TLS_SIG_RSA_PKCS1_SHA1, + "RSA_PKCS1_SHA1"); +ENUM_NEXT(tls_signature_scheme_names, + TLS_SIG_ECDSA_SHA1, TLS_SIG_ECDSA_SHA1, TLS_SIG_RSA_PKCS1_SHA1, + "ECDSA_SHA1"); +ENUM_NEXT(tls_signature_scheme_names, + TLS_SIG_RSA_PKCS1_SHA256, TLS_SIG_RSA_PKCS1_SHA256, TLS_SIG_ECDSA_SHA1, + "RSA_PKCS1_SHA256"); +ENUM_NEXT(tls_signature_scheme_names, + TLS_SIG_ECDSA_SHA256, TLS_SIG_ECDSA_SHA256, TLS_SIG_RSA_PKCS1_SHA256, + "ECDSA_SHA256"); +ENUM_NEXT(tls_signature_scheme_names, + TLS_SIG_RSA_PKCS1_SHA384, TLS_SIG_RSA_PKCS1_SHA384, TLS_SIG_ECDSA_SHA256, + "RSA_PKCS1_SHA384"); +ENUM_NEXT(tls_signature_scheme_names, + TLS_SIG_ECDSA_SHA384, TLS_SIG_ECDSA_SHA384, TLS_SIG_RSA_PKCS1_SHA384, + "ECDSA_SHA384"); +ENUM_NEXT(tls_signature_scheme_names, + TLS_SIG_RSA_PKCS1_SHA512, TLS_SIG_RSA_PKCS1_SHA512, TLS_SIG_ECDSA_SHA384, + "RSA_PKCS1_SHA512"); +ENUM_NEXT(tls_signature_scheme_names, + TLS_SIG_ECDSA_SHA512, TLS_SIG_ECDSA_SHA512, TLS_SIG_RSA_PKCS1_SHA512, + "ECDSA_SHA512"); +ENUM_NEXT(tls_signature_scheme_names, + TLS_SIG_RSA_PSS_RSAE_SHA256, TLS_SIG_RSA_PSS_PSS_SHA512, TLS_SIG_ECDSA_SHA512, + "RSA_PSS_RSAE_SHA256", + "RSA_PSS_RSAE_SHA384", + "RSA_PSS_RSAE_SHA512", + "ED25519", + "ED448", + "RSA_PSS_PSS_SHA256", + "RSA_PSS_PSS_SHA384", + "RSA_PSS_PSS_SHA512", ); +ENUM_END(tls_signature_scheme_names, TLS_SIG_RSA_PSS_PSS_SHA512); ENUM_BEGIN(tls_client_certificate_type_names, TLS_RSA_SIGN, TLS_DSS_EPHEMERAL_DH, @@ -1372,60 +1403,84 @@ METHOD(tls_crypto_t, get_dh_group, diffie_hellman_group_t, } /** - * Map signature schemes to TLS key types and hashes, ordered by preference + * Parameters for RSA/PSS signature schemes + */ +#define PSS_PARAMS(bits) static rsa_pss_params_t pss_params_sha##bits = { \ + .hash = HASH_SHA##bits, \ + .mgf1_hash = HASH_SHA##bits, \ + .salt_len = HASH_SIZE_SHA##bits, \ +} + +PSS_PARAMS(256); +PSS_PARAMS(384); +PSS_PARAMS(512); + +/** + * Map TLS signature schemes, ordered by preference */ static struct { - tls_signature_algorithm_t sig; - tls_hash_algorithm_t hash; - signature_scheme_t scheme; + tls_signature_scheme_t sig; + signature_params_t params; + /* min/max versions for use in CertificateVerify */ + tls_version_t min_version; + tls_version_t max_version; } schemes[] = { - { TLS_SIG_ECDSA, TLS_HASH_SHA256, SIGN_ECDSA_WITH_SHA256_DER }, - { TLS_SIG_ECDSA, TLS_HASH_SHA384, SIGN_ECDSA_WITH_SHA384_DER }, - { TLS_SIG_ECDSA, TLS_HASH_SHA512, SIGN_ECDSA_WITH_SHA512_DER }, - { TLS_SIG_ECDSA, TLS_HASH_SHA1, SIGN_ECDSA_WITH_SHA1_DER }, - { TLS_SIG_RSA, TLS_HASH_SHA256, SIGN_RSA_EMSA_PKCS1_SHA2_256 }, - { TLS_SIG_RSA, TLS_HASH_SHA384, SIGN_RSA_EMSA_PKCS1_SHA2_384 }, - { TLS_SIG_RSA, TLS_HASH_SHA512, SIGN_RSA_EMSA_PKCS1_SHA2_512 }, - { TLS_SIG_RSA, TLS_HASH_SHA224, SIGN_RSA_EMSA_PKCS1_SHA2_224 }, - { TLS_SIG_RSA, TLS_HASH_SHA1, SIGN_RSA_EMSA_PKCS1_SHA1 }, - { TLS_SIG_RSA, TLS_HASH_MD5, SIGN_RSA_EMSA_PKCS1_MD5 }, + { TLS_SIG_ECDSA_SHA256, { .scheme = SIGN_ECDSA_WITH_SHA256_DER }, + TLS_1_0, TLS_1_3 }, + { TLS_SIG_ECDSA_SHA384, { .scheme = SIGN_ECDSA_WITH_SHA384_DER }, + TLS_1_0, TLS_1_3 }, + { TLS_SIG_ECDSA_SHA512, { .scheme = SIGN_ECDSA_WITH_SHA512_DER }, + TLS_1_0, TLS_1_3 }, + { TLS_SIG_ED25519, { .scheme = SIGN_ED25519 }, + TLS_1_3, TLS_1_3 }, + { TLS_SIG_ED448, { .scheme = SIGN_ED448 }, + TLS_1_3, TLS_1_3 }, + { TLS_SIG_RSA_PSS_RSAE_SHA256, { .scheme = SIGN_RSA_EMSA_PSS, .params = &pss_params_sha256, }, + TLS_1_2, TLS_1_3 }, + { TLS_SIG_RSA_PSS_RSAE_SHA384, { .scheme = SIGN_RSA_EMSA_PSS, .params = &pss_params_sha384, }, + TLS_1_2, TLS_1_3 }, + { TLS_SIG_RSA_PSS_RSAE_SHA512, { .scheme = SIGN_RSA_EMSA_PSS, .params = &pss_params_sha512, }, + TLS_1_2, TLS_1_3 }, + /* the parameters for the next three should actually be taken from the + * public key, we currently don't have an API for that, so assume defaults */ + { TLS_SIG_RSA_PSS_PSS_SHA256, { .scheme = SIGN_RSA_EMSA_PSS, .params = &pss_params_sha256, }, + TLS_1_2, TLS_1_3 }, + { TLS_SIG_RSA_PSS_PSS_SHA384, { .scheme = SIGN_RSA_EMSA_PSS, .params = &pss_params_sha384, }, + TLS_1_2, TLS_1_3 }, + { TLS_SIG_RSA_PSS_PSS_SHA512, { .scheme = SIGN_RSA_EMSA_PSS, .params = &pss_params_sha512, }, + TLS_1_2, TLS_1_3 }, + { TLS_SIG_RSA_PKCS1_SHA256, { .scheme = SIGN_RSA_EMSA_PKCS1_SHA2_256 }, + TLS_1_0, TLS_1_2 }, + { TLS_SIG_RSA_PKCS1_SHA384, { .scheme = SIGN_RSA_EMSA_PKCS1_SHA2_384 }, + TLS_1_0, TLS_1_2 }, + { TLS_SIG_RSA_PKCS1_SHA512, { .scheme = SIGN_RSA_EMSA_PKCS1_SHA2_512 }, + TLS_1_0, TLS_1_2 }, }; METHOD(tls_crypto_t, get_signature_algorithms, void, - private_tls_crypto_t *this, bio_writer_t *writer) + private_tls_crypto_t *this, bio_writer_t *writer, bool cert) { bio_writer_t *supported; - tls_version_t version; + tls_version_t min_version, max_version; int i; supported = bio_writer_create(32); - version = this->tls->get_version_max(this->tls); + + if (!cert) + { + min_version = this->tls->get_version_min(this->tls); + max_version = this->tls->get_version_max(this->tls); + } for (i = 0; i < countof(schemes); i++) { - if (schemes[i].sig == TLS_SIG_RSA && !this->rsa) + if ((cert || (schemes[i].min_version <= max_version && + schemes[i].max_version >= min_version)) && + lib->plugins->has_feature(lib->plugins, + PLUGIN_PROVIDE(PUBKEY_VERIFY, schemes[i].params.scheme))) { - continue; + supported->write_uint16(supported, schemes[i].sig); } - if (schemes[i].sig == TLS_SIG_ECDSA && !this->ecdsa) - { - continue; - } - if (schemes[i].hash == TLS_HASH_MD5 && version >= TLS_1_3) - { - continue; - } - if (schemes[i].hash == TLS_HASH_SHA224 && version >= TLS_1_3) - { - continue; - } - if (!lib->plugins->has_feature(lib->plugins, - PLUGIN_PROVIDE(PUBKEY_VERIFY, schemes[i].scheme))) - { - continue; - } - supported->write_uint8(supported, schemes[i].hash); - supported->write_uint8(supported, schemes[i].sig); } supported->wrap16(supported); @@ -1434,26 +1489,20 @@ METHOD(tls_crypto_t, get_signature_algorithms, void, } /** - * Get the signature scheme from a TLS 1.2 hash/sig algorithm pair + * Get the signature parameters from a TLS signature scheme */ -static signature_scheme_t hashsig_to_scheme(key_type_t type, - tls_hash_algorithm_t hash, - tls_signature_algorithm_t sig) +static signature_params_t *params_for_scheme(tls_signature_scheme_t sig) { int i; - if ((sig == TLS_SIG_RSA && type == KEY_RSA) || - (sig == TLS_SIG_ECDSA && type == KEY_ECDSA)) + for (i = 0; i < countof(schemes); i++) { - for (i = 0; i < countof(schemes); i++) + if (schemes[i].sig == sig) { - if (schemes[i].sig == sig && schemes[i].hash == hash) - { - return schemes[i].scheme; - } + return &schemes[i].params; } } - return SIGN_UNKNOWN; + return NULL; } /** @@ -1629,11 +1678,12 @@ METHOD(tls_crypto_t, sign, bool, { if (this->tls->get_version_max(this->tls) >= TLS_1_2) { - const chunk_t hashsig_def = chunk_from_chars( - TLS_HASH_SHA1, TLS_SIG_RSA, TLS_HASH_SHA1, TLS_SIG_ECDSA); - signature_scheme_t scheme; + /* fallback to SHA1/RSA and SHA1/ECDSA */ + const chunk_t hashsig_def = chunk_from_chars(0x02, 0x01, 0x02, 0x03); + signature_params_t *params; + key_type_t type; + uint16_t scheme; bio_reader_t *reader; - uint8_t hash, alg; chunk_t sig; bool done = FALSE; @@ -1641,15 +1691,16 @@ METHOD(tls_crypto_t, sign, bool, { /* fallback if none given */ hashsig = hashsig_def; } + type = key->get_type(key); reader = bio_reader_create(hashsig); while (reader->remaining(reader) >= 2) { - if (reader->read_uint8(reader, &hash) && - reader->read_uint8(reader, &alg)) + if (reader->read_uint16(reader, &scheme)) { - scheme = hashsig_to_scheme(key->get_type(key), hash, alg); - if (scheme != SIGN_UNKNOWN && - key->sign(key, scheme, NULL, data, &sig)) + params = params_for_scheme(scheme); + if (params && + type == key_type_from_signature_scheme(params->scheme) && + key->sign(key, params->scheme, params->params, data, &sig)) { done = TRUE; break; @@ -1662,10 +1713,9 @@ METHOD(tls_crypto_t, sign, bool, DBG1(DBG_TLS, "none of the proposed hash/sig algorithms supported"); return FALSE; } - DBG2(DBG_TLS, "created signature with %N/%N", - tls_hash_algorithm_names, hash, tls_signature_algorithm_names, alg); - writer->write_uint8(writer, hash); - writer->write_uint8(writer, alg); + DBG2(DBG_TLS, "created signature with %N", tls_signature_scheme_names, + scheme); + writer->write_uint16(writer, scheme); writer->write_data16(writer, sig); free(sig.ptr); } @@ -1712,23 +1762,21 @@ METHOD(tls_crypto_t, verify, bool, { if (this->tls->get_version_max(this->tls) >= TLS_1_2) { - signature_scheme_t scheme = SIGN_UNKNOWN; - uint8_t hash, alg; + signature_params_t *params; + uint16_t scheme; chunk_t sig; - if (!reader->read_uint8(reader, &hash) || - !reader->read_uint8(reader, &alg) || + if (!reader->read_uint16(reader, &scheme) || !reader->read_data16(reader, &sig)) { DBG1(DBG_TLS, "received invalid signature"); return FALSE; } - scheme = hashsig_to_scheme(key->get_type(key), hash, alg); - if (scheme == SIGN_UNKNOWN) + params = params_for_scheme(scheme); + if (!params) { - DBG1(DBG_TLS, "signature algorithms %N/%N not supported", - tls_hash_algorithm_names, hash, - tls_signature_algorithm_names, alg); + DBG1(DBG_TLS, "signature algorithms %N not supported", + tls_signature_scheme_names, scheme); return FALSE; } if (this->tls->get_version_max(this->tls) == TLS_1_3) @@ -1743,15 +1791,14 @@ METHOD(tls_crypto_t, verify, bool, data = chunk_cata("cm", tls13_sig_data_server, transcript_hash); } - if (!key->verify(key, scheme, NULL, data, sig)) + if (!key->verify(key, params->scheme, params->params, data, sig)) { - DBG1(DBG_TLS, "signature verification with %N/%N failed", - tls_hash_algorithm_names, hash, tls_signature_algorithm_names, - alg); + DBG1(DBG_TLS, "signature verification with %N failed", + tls_signature_scheme_names, scheme); return FALSE; } - DBG2(DBG_TLS, "verified signature with %N/%N", - tls_hash_algorithm_names, hash, tls_signature_algorithm_names, alg); + DBG2(DBG_TLS, "verified signature with %N", + tls_signature_scheme_names, scheme); } else { diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h index de6e56bad..b5dd4f930 100644 --- a/src/libtls/tls_crypto.h +++ b/src/libtls/tls_crypto.h @@ -29,7 +29,7 @@ typedef struct tls_crypto_t tls_crypto_t; typedef enum tls_cipher_suite_t tls_cipher_suite_t; typedef enum tls_hash_algorithm_t tls_hash_algorithm_t; -typedef enum tls_signature_algorithm_t tls_signature_algorithm_t; +typedef enum tls_signature_scheme_t tls_signature_scheme_t; typedef enum tls_client_certificate_type_t tls_client_certificate_type_t; typedef enum tls_ecc_curve_type_t tls_ecc_curve_type_t; typedef enum tls_named_group_t tls_named_group_t; @@ -287,18 +287,36 @@ enum tls_hash_algorithm_t { extern enum_name_t *tls_hash_algorithm_names; /** - * TLS SignatureAlgorithm identifiers + * TLS SignatureScheme identifiers */ -enum tls_signature_algorithm_t { - TLS_SIG_RSA = 1, - TLS_SIG_DSA = 2, - TLS_SIG_ECDSA = 3, +enum tls_signature_scheme_t { + /* legacy schemes compatible with TLS 1.2 (first byte is the hash algorithm, + * second the key type) */ + TLS_SIG_RSA_PKCS1_SHA1 = 0x0201, + TLS_SIG_ECDSA_SHA1 = 0x0203, + TLS_SIG_RSA_PKCS1_SHA256 = 0x0401, + TLS_SIG_ECDSA_SHA256 = 0x0403, + TLS_SIG_RSA_PKCS1_SHA384 = 0x0501, + TLS_SIG_ECDSA_SHA384 = 0x0503, + TLS_SIG_RSA_PKCS1_SHA512 = 0x0601, + TLS_SIG_ECDSA_SHA512 = 0x0603, + /* RSASSA-PSS for public keys with OID rsaEncryption */ + TLS_SIG_RSA_PSS_RSAE_SHA256 = 0x0804, + TLS_SIG_RSA_PSS_RSAE_SHA384 = 0x0805, + TLS_SIG_RSA_PSS_RSAE_SHA512 = 0x0806, + /* EdDSA */ + TLS_SIG_ED25519 = 0x0807, + TLS_SIG_ED448 = 0x0808, + /* RSASSA-PSS for public keys with OID RSASSA-PSS */ + TLS_SIG_RSA_PSS_PSS_SHA256 = 0x0809, + TLS_SIG_RSA_PSS_PSS_SHA384 = 0x080a, + TLS_SIG_RSA_PSS_PSS_SHA512 = 0x080b, }; /** - * Enum names for tls_signature_algorithm_t + * Enum names for tls_signature_scheme_t */ -extern enum_name_t *tls_signature_algorithm_names; +extern enum_name_t *tls_signature_scheme_names; /** * TLS ClientCertificateType @@ -443,11 +461,14 @@ struct tls_crypto_t { diffie_hellman_group_t (*get_dh_group)(tls_crypto_t *this); /** - * Write the list of supported hash/sig algorithms to writer. + * Write the list of supported signature schemes, either for certificates + * or for CertificateVerify messages, to writer. * - * @param writer writer to write supported hash/sig algorithms + * @param writer writer to write supported signature schemes + * @param cert TRUE to return signature schemes supported in certs */ - void (*get_signature_algorithms)(tls_crypto_t *this, bio_writer_t *writer); + void (*get_signature_algorithms)(tls_crypto_t *this, bio_writer_t *writer, + bool cert); /** * Create an enumerator over supported ECDH groups. diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 04c007289..b5d5bef71 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -1331,7 +1331,11 @@ static status_t send_client_hello(private_tls_peer_t *this, DBG2(DBG_TLS, "sending extension: %N", tls_extension_names, TLS_EXT_SIGNATURE_ALGORITHMS); extensions->write_uint16(extensions, TLS_EXT_SIGNATURE_ALGORITHMS); - this->crypto->get_signature_algorithms(this->crypto, extensions); + this->crypto->get_signature_algorithms(this->crypto, extensions, FALSE); + DBG2(DBG_TLS, "sending extension: %N", + tls_extension_names, TLS_EXT_SIGNATURE_ALGORITHMS_CERT); + extensions->write_uint16(extensions, TLS_EXT_SIGNATURE_ALGORITHMS_CERT); + this->crypto->get_signature_algorithms(this->crypto, extensions, TRUE); if (this->dh) { diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index f0bbb1a59..8675dc20e 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -777,7 +777,7 @@ static status_t send_certificate_request(private_tls_server_t *this, supported->destroy(supported); if (this->tls->get_version_max(this->tls) >= TLS_1_2) { - this->crypto->get_signature_algorithms(this->crypto, writer); + this->crypto->get_signature_algorithms(this->crypto, writer, TRUE); } authorities = bio_writer_create(64); From 4e2b8f9c7139215b05a4e02921ef2dd660c12bdb Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 31 Aug 2020 17:08:07 +0200 Subject: [PATCH 043/107] tls-hkdf: Store OKM in local variables --- src/libtls/tls_hkdf.c | 47 +++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/libtls/tls_hkdf.c b/src/libtls/tls_hkdf.c index 97ba35638..8ec55b858 100644 --- a/src/libtls/tls_hkdf.c +++ b/src/libtls/tls_hkdf.c @@ -66,11 +66,6 @@ struct private_tls_hkdf_t { */ chunk_t prk; - /** - * OKM used. - */ - chunk_t okm; - /** * Current implementation needs a copy of derived secrets to calculate the * proper finished key. @@ -131,12 +126,10 @@ static bool expand(private_tls_hkdf_t *this, chunk_t prk, chunk_t info, return FALSE; } prf_plus = prf_plus_create(this->prf, TRUE, info); - chunk_clear(okm); if (!prf_plus || !prf_plus->allocate_bytes(prf_plus, length, okm)) { DBG1(DBG_TLS, "unable to allocate PRF+ result"); DESTROY_IF(prf_plus); - chunk_clear(okm); return FALSE; } prf_plus->destroy(prf_plus); @@ -177,7 +170,7 @@ static bool expand_label(private_tls_hkdf_t *this, chunk_t secret, * Derive-Secret(Secret, Label, Message) -> OKM */ static bool derive_secret(private_tls_hkdf_t *this, chunk_t label, - chunk_t messages) + chunk_t messages, chunk_t *okm) { chunk_t context; bool success; @@ -188,8 +181,7 @@ static bool derive_secret(private_tls_hkdf_t *this, chunk_t label, } success = expand_label(this, this->prk, label, context, - this->hasher->get_hash_size(this->hasher), - &this->okm); + this->hasher->get_hash_size(this->hasher), okm); chunk_free(&context); return success; } @@ -259,7 +251,7 @@ static bool move_to_phase_1(private_tls_hkdf_t *this) */ static bool move_to_phase_2(private_tls_hkdf_t *this) { - chunk_t derived; + chunk_t derived, okm; switch (this->phase) { @@ -272,7 +264,7 @@ static bool move_to_phase_2(private_tls_hkdf_t *this) /* fall-through */ case HKDF_PHASE_1: derived = chunk_from_str("tls13 derived"); - if (!derive_secret(this, derived, chunk_empty)) + if (!derive_secret(this, derived, chunk_empty, &okm)) { DBG1(DBG_TLS, "unable to derive secret"); return FALSE; @@ -281,14 +273,17 @@ static bool move_to_phase_2(private_tls_hkdf_t *this) if (!this->shared_secret.ptr) { DBG1(DBG_TLS, "no shared secret set"); + chunk_clear(&okm); return FALSE; } - if (!extract(this, this->okm, this->shared_secret, &this->prk)) + if (!extract(this, okm, this->shared_secret, &this->prk)) { DBG1(DBG_TLS, "unable extract PRK"); + chunk_clear(&okm); return FALSE; } + chunk_clear(&okm); this->phase = HKDF_PHASE_2; return TRUE; case HKDF_PHASE_2: @@ -325,7 +320,7 @@ static bool move_to_phase_2(private_tls_hkdf_t *this) */ static bool move_to_phase_3(private_tls_hkdf_t *this) { - chunk_t derived, ikm_zero; + chunk_t derived, okm, ikm_zero; switch (this->phase) { @@ -340,7 +335,7 @@ static bool move_to_phase_3(private_tls_hkdf_t *this) case HKDF_PHASE_2: /* prepare okm for next extract */ derived = chunk_from_str("tls13 derived"); - if (!derive_secret(this, derived, chunk_empty)) + if (!derive_secret(this, derived, chunk_empty, &okm)) { DBG1(DBG_TLS, "unable to derive secret"); return FALSE; @@ -348,11 +343,13 @@ static bool move_to_phase_3(private_tls_hkdf_t *this) ikm_zero = chunk_alloca(this->hasher->get_hash_size(this->hasher)); chunk_copy_pad(ikm_zero, chunk_empty, 0); - if (!extract(this, this->okm, ikm_zero, &this->prk)) + if (!extract(this, okm, ikm_zero, &this->prk)) { DBG1(DBG_TLS, "unable extract PRK"); + chunk_clear(&okm); return FALSE; } + chunk_clear(&okm); this->phase = HKDF_PHASE_3; return TRUE; case HKDF_PHASE_3: @@ -373,6 +370,8 @@ METHOD(tls_hkdf_t, generate_secret, bool, private_tls_hkdf_t *this, tls_hkdf_label_t label, chunk_t messages, chunk_t *secret) { + chunk_t okm; + switch (label) { case TLS_HKDF_EXT_BINDER: @@ -427,7 +426,7 @@ METHOD(tls_hkdf_t, generate_secret, bool, if (!expand_label(this, previous, chunk_from_str("tls13 traffic upd"), chunk_empty, this->hasher->get_hash_size(this->hasher), - &this->okm)) + &okm)) { DBG1(DBG_TLS, "unable to update secret"); return FALSE; @@ -435,7 +434,8 @@ METHOD(tls_hkdf_t, generate_secret, bool, } else { - if (!derive_secret(this, chunk_from_str(hkdf_labels[label]), messages)) + if (!derive_secret(this, chunk_from_str(hkdf_labels[label]), messages, + &okm)) { DBG1(DBG_TLS, "unable to derive secret"); return FALSE; @@ -448,13 +448,13 @@ METHOD(tls_hkdf_t, generate_secret, bool, case TLS_HKDF_C_AP_TRAFFIC: case TLS_HKDF_UPD_C_TRAFFIC: chunk_clear(&this->client_traffic_secret); - this->client_traffic_secret = chunk_clone(this->okm); + this->client_traffic_secret = chunk_clone(okm); break; case TLS_HKDF_S_HS_TRAFFIC: case TLS_HKDF_S_AP_TRAFFIC: case TLS_HKDF_UPD_S_TRAFFIC: chunk_clear(&this->server_traffic_secret); - this->server_traffic_secret = chunk_clone(this->okm); + this->server_traffic_secret = chunk_clone(okm); break; default: break; @@ -462,7 +462,11 @@ METHOD(tls_hkdf_t, generate_secret, bool, if (secret) { - *secret = chunk_clone(this->okm); + *secret = okm; + } + else + { + chunk_clear(&okm); } return TRUE; } @@ -533,7 +537,6 @@ METHOD(tls_hkdf_t, destroy, void, chunk_clear(&this->psk); chunk_clear(&this->prk); chunk_clear(&this->shared_secret); - chunk_clear(&this->okm); chunk_clear(&this->client_traffic_secret); chunk_clear(&this->server_traffic_secret); DESTROY_IF(this->prf); From 3e89d26e1c84204a552cfd6a06cbd396f6dfe881 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 1 Sep 2020 18:35:02 +0200 Subject: [PATCH 044/107] tls-hkdf: Pass secret to derive_secret() explicitly --- src/libtls/tls_hkdf.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libtls/tls_hkdf.c b/src/libtls/tls_hkdf.c index 8ec55b858..41b1d5f14 100644 --- a/src/libtls/tls_hkdf.c +++ b/src/libtls/tls_hkdf.c @@ -169,8 +169,8 @@ static bool expand_label(private_tls_hkdf_t *this, chunk_t secret, * Derive-Secret as defined in RFC 8446, section 7.1: * Derive-Secret(Secret, Label, Message) -> OKM */ -static bool derive_secret(private_tls_hkdf_t *this, chunk_t label, - chunk_t messages, chunk_t *okm) +static bool derive_secret(private_tls_hkdf_t *this, chunk_t secret, + chunk_t label, chunk_t messages, chunk_t *okm) { chunk_t context; bool success; @@ -180,7 +180,7 @@ static bool derive_secret(private_tls_hkdf_t *this, chunk_t label, return FALSE; } - success = expand_label(this, this->prk, label, context, + success = expand_label(this, secret, label, context, this->hasher->get_hash_size(this->hasher), okm); chunk_free(&context); return success; @@ -264,7 +264,7 @@ static bool move_to_phase_2(private_tls_hkdf_t *this) /* fall-through */ case HKDF_PHASE_1: derived = chunk_from_str("tls13 derived"); - if (!derive_secret(this, derived, chunk_empty, &okm)) + if (!derive_secret(this, this->prk, derived, chunk_empty, &okm)) { DBG1(DBG_TLS, "unable to derive secret"); return FALSE; @@ -335,7 +335,7 @@ static bool move_to_phase_3(private_tls_hkdf_t *this) case HKDF_PHASE_2: /* prepare okm for next extract */ derived = chunk_from_str("tls13 derived"); - if (!derive_secret(this, derived, chunk_empty, &okm)) + if (!derive_secret(this, this->prk, derived, chunk_empty, &okm)) { DBG1(DBG_TLS, "unable to derive secret"); return FALSE; @@ -434,8 +434,8 @@ METHOD(tls_hkdf_t, generate_secret, bool, } else { - if (!derive_secret(this, chunk_from_str(hkdf_labels[label]), messages, - &okm)) + if (!derive_secret(this, this->prk, chunk_from_str(hkdf_labels[label]), + messages, &okm)) { DBG1(DBG_TLS, "unable to derive secret"); return FALSE; From 86cda1a3c064a8480649882b07cd85e48556ac20 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 1 Sep 2020 18:48:41 +0200 Subject: [PATCH 045/107] tls-hkdf: Handle label expansion inside expand_label() We have to expand arbitrary external labels the same way when exporting key material. --- src/libtls/tls_hkdf.c | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/libtls/tls_hkdf.c b/src/libtls/tls_hkdf.c index 41b1d5f14..be5d57a3d 100644 --- a/src/libtls/tls_hkdf.c +++ b/src/libtls/tls_hkdf.c @@ -75,16 +75,16 @@ struct private_tls_hkdf_t { }; static char *hkdf_labels[] = { - "tls13 ext binder", - "tls13 res binder", - "tls13 c e traffic", - "tls13 e exp master", - "tls13 c hs traffic", - "tls13 s hs traffic", - "tls13 c ap traffic", - "tls13 s ap traffic", - "tls13 exp master", - "tls13 res master", + "ext binder", + "res binder", + "c e traffic", + "e exp master", + "c hs traffic", + "s hs traffic", + "c ap traffic", + "s ap traffic", + "exp master", + "res master", }; /** @@ -149,7 +149,7 @@ static bool expand_label(private_tls_hkdf_t *this, chunk_t secret, { bool success; - if (label.len < 7 || label.len > 255 || context.len > 255) + if (!label.len || label.len > 249 || context.len > 255) { return FALSE; } @@ -157,6 +157,7 @@ static bool expand_label(private_tls_hkdf_t *this, chunk_t secret, /* HKDFLabel as defined in RFC 8446, section 7.1 */ bio_writer_t *writer = bio_writer_create(0); writer->write_uint16(writer, length); + label = chunk_cata("cc", chunk_from_str("tls13 "), label); writer->write_data8(writer, label); writer->write_data8(writer, context); @@ -251,7 +252,7 @@ static bool move_to_phase_1(private_tls_hkdf_t *this) */ static bool move_to_phase_2(private_tls_hkdf_t *this) { - chunk_t derived, okm; + chunk_t okm; switch (this->phase) { @@ -263,8 +264,8 @@ static bool move_to_phase_2(private_tls_hkdf_t *this) } /* fall-through */ case HKDF_PHASE_1: - derived = chunk_from_str("tls13 derived"); - if (!derive_secret(this, this->prk, derived, chunk_empty, &okm)) + if (!derive_secret(this, this->prk, chunk_from_str("derived"), + chunk_empty, &okm)) { DBG1(DBG_TLS, "unable to derive secret"); return FALSE; @@ -320,7 +321,7 @@ static bool move_to_phase_2(private_tls_hkdf_t *this) */ static bool move_to_phase_3(private_tls_hkdf_t *this) { - chunk_t derived, okm, ikm_zero; + chunk_t okm, ikm_zero; switch (this->phase) { @@ -334,8 +335,8 @@ static bool move_to_phase_3(private_tls_hkdf_t *this) /* fall-through */ case HKDF_PHASE_2: /* prepare okm for next extract */ - derived = chunk_from_str("tls13 derived"); - if (!derive_secret(this, this->prk, derived, chunk_empty, &okm)) + if (!derive_secret(this, this->prk, chunk_from_str("derived"), + chunk_empty, &okm)) { DBG1(DBG_TLS, "unable to derive secret"); return FALSE; @@ -424,7 +425,7 @@ METHOD(tls_hkdf_t, generate_secret, bool, previous = this->server_traffic_secret; } - if (!expand_label(this, previous, chunk_from_str("tls13 traffic upd"), + if (!expand_label(this, previous, chunk_from_str("traffic upd"), chunk_empty, this->hasher->get_hash_size(this->hasher), &okm)) { @@ -503,21 +504,21 @@ static bool get_shared_label_keys(private_tls_hkdf_t *this, chunk_t label, METHOD(tls_hkdf_t, derive_key, bool, private_tls_hkdf_t *this, bool is_server, size_t length, chunk_t *key) { - return get_shared_label_keys(this, chunk_from_str("tls13 key"), is_server, + return get_shared_label_keys(this, chunk_from_str("key"), is_server, length, key); } METHOD(tls_hkdf_t, derive_iv, bool, private_tls_hkdf_t *this, bool is_server, size_t length, chunk_t *iv) { - return get_shared_label_keys(this, chunk_from_str("tls13 iv"), is_server, + return get_shared_label_keys(this, chunk_from_str("iv"), is_server, length, iv); } METHOD(tls_hkdf_t, derive_finished, bool, private_tls_hkdf_t *this, bool is_server, chunk_t *finished) { - return get_shared_label_keys(this, chunk_from_str("tls13 finished"), + return get_shared_label_keys(this, chunk_from_str("finished"), is_server, this->hasher->get_hash_size(this->hasher), finished); From d2fe921cf5f5fd7940495891afe108d26c1c1ca7 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 1 Sep 2020 18:50:47 +0200 Subject: [PATCH 046/107] tls-hkdf: Add method that allows exporting key material --- src/libtls/tls_hkdf.c | 49 +++++++++++++++++++++++++++++++++++++++++++ src/libtls/tls_hkdf.h | 13 ++++++++++++ 2 files changed, 62 insertions(+) diff --git a/src/libtls/tls_hkdf.c b/src/libtls/tls_hkdf.c index be5d57a3d..d31a41d79 100644 --- a/src/libtls/tls_hkdf.c +++ b/src/libtls/tls_hkdf.c @@ -524,6 +524,54 @@ METHOD(tls_hkdf_t, derive_finished, bool, finished); } +METHOD(tls_hkdf_t, export, bool, + private_tls_hkdf_t *this, char *label, chunk_t context, + chunk_t messages, size_t length, chunk_t *key) +{ + chunk_t exporter_master, exporter, hash = chunk_empty; + + if (this->phase != HKDF_PHASE_3) + { + DBG1(DBG_TLS, "unable to export key material"); + return FALSE; + } + + /** + * Export key material according to RFC 8446, section 7.5: + * + * TLS-Exporter(label, context_value, key_length) = + * HKDF-Expand-Label(Derive-Secret(Secret, label, ""), + * "exporter", Hash(context_value), key_length) + */ + if (!generate_secret(this, TLS_HKDF_EXP_MASTER, messages, &exporter_master)) + { + DBG1(DBG_TLS, "unable to derive exporter master secret"); + return FALSE; + } + + if (!derive_secret(this, exporter_master, chunk_from_str(label), + chunk_empty, &exporter)) + { + DBG1(DBG_TLS, "unable to derive exporter secret"); + chunk_clear(&exporter_master); + return FALSE; + } + chunk_clear(&exporter_master); + + if (!this->hasher->allocate_hash(this->hasher, context, &hash) || + !expand_label(this, exporter, chunk_from_str("exporter"), hash, + length, key)) + { + DBG1(DBG_TLS, "unable to expand key material"); + chunk_clear(&exporter); + chunk_free(&hash); + return FALSE; + } + chunk_clear(&exporter); + chunk_free(&hash); + return TRUE; +} + METHOD(tls_hkdf_t, allocate_bytes, bool, private_tls_hkdf_t *this, chunk_t key, chunk_t seed, chunk_t *out) @@ -571,6 +619,7 @@ tls_hkdf_t *tls_hkdf_create(hash_algorithm_t hash_algorithm, chunk_t psk) .derive_key = _derive_key, .derive_iv = _derive_iv, .derive_finished = _derive_finished, + .export = _export, .allocate_bytes = _allocate_bytes, .destroy = _destroy, }, diff --git a/src/libtls/tls_hkdf.h b/src/libtls/tls_hkdf.h index 352d8e399..2b2bf3e03 100644 --- a/src/libtls/tls_hkdf.h +++ b/src/libtls/tls_hkdf.h @@ -113,6 +113,19 @@ struct tls_hkdf_t { bool (*derive_finished)(tls_hkdf_t *this, bool is_server, chunk_t *finished); + /** + * Export key material. + * + * @param label exporter label + * @param context optional context + * @param messages handshake messages + * @param length key length, in bytes + * @param key exported key material + * @return TRUE if key material successfully exported + */ + bool (*export)(tls_hkdf_t *this, char *label, chunk_t context, + chunk_t messages, size_t length, chunk_t *key); + /** * Use the internal PRF to allocate data (mainly for the finished message * where the key is from derive_finished() and the seed is the transcript From 121ac4b9e37e9bc3721bb66a6e9e56a80235f988 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 1 Sep 2020 18:51:32 +0200 Subject: [PATCH 047/107] tls-crypto: Generate MSK for TLS 1.3 We generate material for both MSK and EMSK even though we only need the former. Because HKDF-Expand-Label(), on which the export functionality is based, encodes the requested key length, we have to allocate the same number of bytes as e.g. FreeRADIUS does (i.e. if we only request 64 bytes, those won't be the same as the first 64 bytes after requesting 128 bytes). Unfortunately, key derivation for TLS-based methods is currently not standardized for TLS 1.3. There is a draft [1], which defines a scheme that's different from previous versions (instead of individual label strings it uses a single one and passes the EAP type/code as context value to TLS-Export()). The current code is compatible to FreeRADIUS 3.0.x, which doesn't implement it according to that draft yet (there are unreleased changes for EAP-TLS, not for the other methods, but these only switch the label, no context value is passed). In a separate draft for EAP-TLS [2] there is an altogether different scheme defined in the latest version (label combined with EAP method, no context and separate derivation for MSK and EMSK). So this is a mess and we will have to change this later with the inevitable compatibility issues (we should definitely disable TLS 1.3 by default). [1] https://tools.ietf.org/html/draft-ietf-emu-tls-eap-types [2] https://tools.ietf.org/html/draft-ietf-emu-eap-tls13 --- src/libtls/tls_crypto.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 906c99baa..cb2141f8b 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -2086,8 +2086,26 @@ METHOD(tls_crypto_t, derive_handshake_keys, bool, METHOD(tls_crypto_t, derive_app_keys, bool, private_tls_crypto_t *this) { - return derive_labeled_keys(this, TLS_HKDF_C_AP_TRAFFIC, - TLS_HKDF_S_AP_TRAFFIC); + if (!derive_labeled_keys(this, TLS_HKDF_C_AP_TRAFFIC, + TLS_HKDF_S_AP_TRAFFIC)) + { + return FALSE; + } + + /* EAP-MSK */ + if (this->msk_label) + { + /* because the length is encoded when expanding key material, we + * request the same number of bytes as FreeRADIUS (the first 64 for + * the MSK, the next for the EMSK, which we just ignore) */ + if (!this->hkdf->export(this->hkdf, this->msk_label, chunk_empty, + this->handshake, 128, &this->msk)) + { + return FALSE; + } + this->msk.len = 64; + } + return TRUE; } METHOD(tls_crypto_t, update_app_keys, bool, From 00a6280aabc3dacd9737aa2c970f8c997bfd8aab Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 1 Sep 2020 18:59:17 +0200 Subject: [PATCH 048/107] tls-peer: Return INVALID_STATE after changing TLS 1.3 keys Even though we return from build(), we are not actually sending a response, so we can't return NEED_MORE (would send an invalid ClientHello message) and if we return SUCCESS, the EAP layer treats this as failure (there is a comment in eap_authenticator_t about client methods never returning SUCCESS from process()). Instead we return INVALID_STATE, which allows tls_t.build() to exit from the build() loop immediately and send the already generated Finished message. --- src/libtls/tls_peer.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index b5d5bef71..002b84c7a 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -1730,7 +1730,7 @@ METHOD(tls_handshake_t, build, status_t, this->crypto->change_cipher(this->crypto, TRUE); this->crypto->change_cipher(this->crypto, FALSE); this->state = STATE_FINISHED_SENT_KEY_SWITCHED; - return SUCCESS; + return INVALID_STATE; case STATE_KEY_UPDATE_REQUESTED: return send_key_update(this, type, writer); case STATE_KEY_UPDATE_SENT: @@ -1741,9 +1741,7 @@ METHOD(tls_handshake_t, build, status_t, } this->crypto->change_cipher(this->crypto, FALSE); this->state = STATE_FINISHED_SENT_KEY_SWITCHED; - return SUCCESS; - case STATE_FINISHED_SENT_KEY_SWITCHED: - return SUCCESS; + return INVALID_STATE; default: return INVALID_STATE; } From 06aad98ff0aa55c39ba80fe829bc765af154c736 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 3 Sep 2020 15:56:14 +0200 Subject: [PATCH 049/107] tls-test: Make plugin list configurable via environment variable --- scripts/tls_test.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/tls_test.c b/scripts/tls_test.c index 4e3a1ccfa..f95494683 100644 --- a/scripts/tls_test.c +++ b/scripts/tls_test.c @@ -256,11 +256,14 @@ static void cleanup() */ static void init() { + char *plugins; + library_init(NULL, "tls_test"); dbg = dbg_tls; - lib->plugins->load(lib->plugins, PLUGINS); + plugins = getenv("PLUGINS") ?: PLUGINS; + lib->plugins->load(lib->plugins, plugins); creds = mem_cred_create(); lib->credmgr->add_set(lib->credmgr, &creds->set); From f920125304d2799c9c270f27bcaca457c19b3926 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Thu, 3 Sep 2020 21:53:52 +0200 Subject: [PATCH 050/107] tls-peer: Don't initiate TLS connection if no cipher suites are supported If zero cipher suites are left after all filters, tls-peer does not try to establish a connection to the server anymore. --- src/libtls/tls_peer.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 002b84c7a..8e75eecf5 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -1237,6 +1237,12 @@ static status_t send_client_hello(private_tls_peer_t *this, /* add TLS cipher suites */ count = this->crypto->get_cipher_suites(this->crypto, &suites); + if (count <= 0) + { + DBG1(DBG_TLS, "no supported TLS cipher suite available"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } writer->write_uint16(writer, count * 2); for (i = 0; i < count; i++) { From 8a6edc08a45830842f3946562fe23fcb37863e01 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Fri, 4 Sep 2020 18:29:43 +0200 Subject: [PATCH 051/107] tls-crypto: Check if TLS versions and cipher suites match Only suggest TLS versions of supported cipher suites. For instance, do not suggest TLS 1.3 if none of its cipher suites (requiring GCM/CCM or ChaPoly) are available. --- src/libtls/tls_crypto.c | 18 +++++++++++++++++- src/libtls/tls_peer.c | 4 +++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index cb2141f8b..06fd9229f 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1140,7 +1140,7 @@ static void filter_unsupported_suites(suite_algs_t suites[], int *count) static void build_cipher_suite_list(private_tls_crypto_t *this) { suite_algs_t suites[countof(suite_algs)]; - tls_version_t min_version, max_version; + tls_version_t min_version, max_version, new_min_version, new_max_version; bool require_encryption; int count = 0, i; @@ -1198,10 +1198,26 @@ static void build_cipher_suite_list(private_tls_crypto_t *this) this->suites = malloc(sizeof(tls_cipher_suite_t) * count); DBG2(DBG_TLS, "%d supported TLS cipher suites:", count); + new_min_version = max_version; + new_max_version = min_version; for (i = 0; i < count; i++) { DBG2(DBG_TLS, " %N", tls_cipher_suite_names, suites[i].suite); this->suites[i] = suites[i].suite; + + /* set TLS min/max versions appropriate to the final cipher suites */ + new_max_version = max(new_max_version, suites[i].max_version); + new_min_version = min(new_min_version, suites[i].min_version); + } + new_max_version = min(new_max_version, max_version); + new_min_version = max(new_min_version, min_version); + + if (min_version != new_min_version || max_version != new_max_version) + { + this->tls->set_version(this->tls, new_min_version, new_max_version); + DBG2(DBG_TLS, "TLS min/max %N/%N according to the cipher suites", + tls_version_names, new_min_version, + tls_version_names, new_max_version); } } diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 8e75eecf5..26792f930 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -1217,6 +1217,9 @@ static status_t send_client_hello(private_tls_peer_t *this, } rng->destroy(rng); + /* determine supported suites before the versions as they might change */ + count = this->crypto->get_cipher_suites(this->crypto, &suites); + /* TLS version_max in handshake protocol */ version_max = this->tls->get_version_max(this->tls); version_min = this->tls->get_version_min(this->tls); @@ -1236,7 +1239,6 @@ static status_t send_client_hello(private_tls_peer_t *this, writer->write_data8(writer, this->session); /* add TLS cipher suites */ - count = this->crypto->get_cipher_suites(this->crypto, &suites); if (count <= 0) { DBG1(DBG_TLS, "no supported TLS cipher suite available"); From 06424efa171f8a2fc339ff7a30463824f7af35c5 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 19 Nov 2020 14:40:30 +0100 Subject: [PATCH 052/107] tls-server: Determine supported/configured suites and versions early If we don't do this, we might negotiate a TLS version for which we don't have any suites configured, so that the cipher suite negotiation subsequently fails. --- src/libtls/tls_crypto.c | 10 ++++------ src/libtls/tls_crypto.h | 2 +- src/libtls/tls_server.c | 4 ++++ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 06fd9229f..f24713de1 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1228,7 +1228,10 @@ METHOD(tls_crypto_t, get_cipher_suites, int, { build_cipher_suite_list(this); } - *suites = this->suites; + if (suites) + { + *suites = this->suites; + } return this->suite_count; } @@ -1376,11 +1379,6 @@ METHOD(tls_crypto_t, select_cipher_suite, tls_cipher_suite_t, suite_algs_t *algs; int i, j; - if (!this->suites) - { - build_cipher_suite_list(this); - } - for (i = 0; i < this->suite_count; i++) { for (j = 0; j < count; j++) diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h index b5dd4f930..958b7db29 100644 --- a/src/libtls/tls_crypto.h +++ b/src/libtls/tls_crypto.h @@ -436,7 +436,7 @@ struct tls_crypto_t { /** * Get a list of supported TLS cipher suites. * - * @param suites list of suites, points to internal data + * @param suites optional list of suites, points to internal data * @return number of suites returned */ int (*get_cipher_suites)(tls_crypto_t *this, tls_cipher_suite_t **suites); diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 8675dc20e..990365ff1 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -235,6 +235,10 @@ static status_t process_client_hello(private_tls_server_t *this, return NEED_MORE; } + /* before we do anything version-related, determine our supported suites + * as that might change the min./max. versions */ + this->crypto->get_cipher_suites(this->crypto, NULL); + if (ext.len) { extensions = bio_reader_create(ext); From d107198fcba3880a6f576205a7f9b4fadc2e80b0 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Fri, 4 Sep 2020 19:36:40 +0200 Subject: [PATCH 053/107] tls-crypto: Rework cipher suite preference order The reworked list follows the order of modern browsers such as Firefox. The new order prefers more secure ciphers over weaker ones. --- src/libtls/tls_crypto.c | 258 ++++++++++++++++++---------------------- 1 file changed, 114 insertions(+), 144 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index f24713de1..a7ad738e6 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -483,22 +483,34 @@ typedef struct { /** * Mapping suites to a set of algorithms + * + * The order represents the descending preference of cipher suites and follows + * this rule set: + * + * 1. TLS 1.3 > Legacy TLS + * 2. AES > CAMELLIA > NULL + * 3. AES256 > AES128 + * 4. GCM > CBC + * 5. ECDHE > DHE > NULL + * 6. ECDSA > RSA + * 7. SHA384 > SHA256 > SHA1 + * */ static suite_algs_t suite_algs[] = { /* Cipher suites of TLS 1.3: key exchange and authentication * delegated to extensions, therefore KEY_ANY, MODP_NONE, PRF_UNDEFINED */ - { TLS_AES_128_GCM_SHA256, - KEY_ANY, MODP_NONE, - HASH_SHA256, PRF_UNDEFINED, - AUTH_HMAC_SHA2_256_256, ENCR_AES_GCM_ICV16, 16, - TLS_1_3, TLS_1_3, - }, { TLS_AES_256_GCM_SHA384, KEY_ANY, MODP_NONE, HASH_SHA384, PRF_UNDEFINED, AUTH_HMAC_SHA2_384_384, ENCR_AES_GCM_ICV16, 32, TLS_1_3, TLS_1_3, }, + { TLS_AES_128_GCM_SHA256, + KEY_ANY, MODP_NONE, + HASH_SHA256, PRF_UNDEFINED, + AUTH_HMAC_SHA2_256_256, ENCR_AES_GCM_ICV16, 16, + TLS_1_3, TLS_1_3, + }, { TLS_CHACHA20_POLY1305_SHA256, KEY_ANY, MODP_NONE, HASH_SHA256, PRF_UNDEFINED, @@ -518,16 +530,16 @@ static suite_algs_t suite_algs[] = { TLS_1_3, TLS_1_3, }, /* Legacy TLS cipher suites */ - { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - KEY_ECDSA, ECP_256_BIT, - HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16, - TLS_1_0, TLS_1_2, + { TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + KEY_ECDSA, ECP_384_BIT, + HASH_SHA384, PRF_HMAC_SHA2_384, + AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32, + TLS_1_2, TLS_1_2, }, - { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - KEY_ECDSA, ECP_256_BIT, - HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16, + { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + KEY_ECDSA, ECP_384_BIT, + HASH_SHA384, PRF_HMAC_SHA2_384, + AUTH_HMAC_SHA2_384_384, ENCR_AES_CBC, 32, TLS_1_2, TLS_1_2, }, { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, @@ -536,34 +548,34 @@ static suite_algs_t suite_algs[] = { AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32, TLS_1_0, TLS_1_2, }, - { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, - KEY_ECDSA, ECP_384_BIT, - HASH_SHA384, PRF_HMAC_SHA2_384, - AUTH_HMAC_SHA2_384_384, ENCR_AES_CBC, 32, - TLS_1_2, TLS_1_2, - }, { TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, KEY_ECDSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16, TLS_1_2, TLS_1_2, }, - { TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - KEY_ECDSA, ECP_384_BIT, - HASH_SHA384, PRF_HMAC_SHA2_384, - AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32, + { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + KEY_ECDSA, ECP_256_BIT, + HASH_SHA256, PRF_HMAC_SHA2_256, + AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16, TLS_1_2, TLS_1_2, }, - { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - KEY_RSA, ECP_256_BIT, + { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + KEY_ECDSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16, TLS_1_0, TLS_1_2, }, - { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - KEY_RSA, ECP_256_BIT, - HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16, + { TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + KEY_RSA, ECP_384_BIT, + HASH_SHA384, PRF_HMAC_SHA2_384, + AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32, + TLS_1_2, TLS_1_2, + }, + { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + KEY_RSA, ECP_384_BIT, + HASH_SHA384, PRF_HMAC_SHA2_384, + AUTH_HMAC_SHA2_384_384, ENCR_AES_CBC, 32, TLS_1_2, TLS_1_2, }, { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, @@ -572,34 +584,34 @@ static suite_algs_t suite_algs[] = { AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32, TLS_1_0, TLS_1_2, }, - { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, - KEY_RSA, ECP_384_BIT, - HASH_SHA384, PRF_HMAC_SHA2_384, - AUTH_HMAC_SHA2_384_384, ENCR_AES_CBC, 32, - TLS_1_2, TLS_1_2, - }, { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, KEY_RSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16, TLS_1_2, TLS_1_2, }, - { TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - KEY_RSA, ECP_384_BIT, + { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + KEY_RSA, ECP_256_BIT, + HASH_SHA256, PRF_HMAC_SHA2_256, + AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16, + TLS_1_2, TLS_1_2, + }, + { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + KEY_RSA, ECP_256_BIT, + HASH_SHA256, PRF_HMAC_SHA2_256, + AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16, + TLS_1_0, TLS_1_2, + }, + { TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, + KEY_RSA, MODP_4096_BIT, HASH_SHA384, PRF_HMAC_SHA2_384, AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32, TLS_1_2, TLS_1_2, }, - { TLS_DHE_RSA_WITH_AES_128_CBC_SHA, - KEY_RSA, MODP_2048_BIT, - HASH_SHA256,PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16, - SSL_3_0, TLS_1_2, - }, - { TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, - KEY_RSA, MODP_3072_BIT, + { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + KEY_RSA, MODP_4096_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16, + AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 32, TLS_1_2, TLS_1_2, }, { TLS_DHE_RSA_WITH_AES_256_CBC_SHA, @@ -608,34 +620,10 @@ static suite_algs_t suite_algs[] = { AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32, SSL_3_0, TLS_1_2, }, - { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + { TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256, KEY_RSA, MODP_4096_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 32, - TLS_1_2, TLS_1_2, - }, - { TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, - KEY_RSA, MODP_3072_BIT, - HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16, - TLS_1_2, TLS_1_2, - }, - { TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, - KEY_RSA, MODP_4096_BIT, - HASH_SHA384, PRF_HMAC_SHA2_384, - AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32, - TLS_1_2, TLS_1_2, - }, - { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, - KEY_RSA, MODP_2048_BIT, - HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 16, - SSL_3_0, TLS_1_2, - }, - { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, - KEY_RSA, MODP_3072_BIT, - HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 16, + AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 32, TLS_1_2, TLS_1_2, }, { TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, @@ -644,28 +632,46 @@ static suite_algs_t suite_algs[] = { AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 32, SSL_3_0, TLS_1_2, }, - { TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256, - KEY_RSA, MODP_4096_BIT, + { TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + KEY_RSA, MODP_3072_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 32, + AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16, TLS_1_2, TLS_1_2, }, - { TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, - KEY_RSA, MODP_2048_BIT, + { TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + KEY_RSA, MODP_3072_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_3DES, 0, - SSL_3_0, TLS_1_2, + AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16, + TLS_1_2, TLS_1_2, }, - { TLS_RSA_WITH_AES_128_CBC_SHA, - KEY_RSA, MODP_NONE, - HASH_SHA256, PRF_HMAC_SHA2_256, + { TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + KEY_RSA, MODP_2048_BIT, + HASH_SHA256,PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16, SSL_3_0, TLS_1_2, }, - { TLS_RSA_WITH_AES_128_CBC_SHA256, + { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, + KEY_RSA, MODP_3072_BIT, + HASH_SHA256, PRF_HMAC_SHA2_256, + AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 16, + TLS_1_2, TLS_1_2, + }, + { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, + KEY_RSA, MODP_2048_BIT, + HASH_SHA256, PRF_HMAC_SHA2_256, + AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 16, + SSL_3_0, TLS_1_2, + }, + { TLS_RSA_WITH_AES_256_GCM_SHA384, + KEY_RSA, MODP_NONE, + HASH_SHA384, PRF_HMAC_SHA2_384, + AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32, + TLS_1_2, TLS_1_2, + }, + { TLS_RSA_WITH_AES_256_CBC_SHA256, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16, + AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 32, TLS_1_2, TLS_1_2, }, { TLS_RSA_WITH_AES_256_CBC_SHA, @@ -674,40 +680,22 @@ static suite_algs_t suite_algs[] = { AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32, SSL_3_0, TLS_1_2, }, - { TLS_RSA_WITH_AES_256_CBC_SHA256, - KEY_RSA, MODP_NONE, - HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 32, - TLS_1_2, TLS_1_2, - }, { TLS_RSA_WITH_AES_128_GCM_SHA256, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16, TLS_1_2, TLS_1_2, }, - { TLS_RSA_WITH_AES_256_GCM_SHA384, + { TLS_RSA_WITH_AES_128_CBC_SHA256, KEY_RSA, MODP_NONE, - HASH_SHA384, PRF_HMAC_SHA2_384, - AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32, + HASH_SHA256, PRF_HMAC_SHA2_256, + AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16, TLS_1_2, TLS_1_2, }, - { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, + { TLS_RSA_WITH_AES_128_CBC_SHA, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 16, - SSL_3_0, TLS_1_2, - }, - { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256, - KEY_RSA, MODP_NONE, - HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 16, - TLS_1_2, TLS_1_2, - }, - { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, - KEY_RSA, MODP_NONE, - HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 32, + AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16, SSL_3_0, TLS_1_2, }, { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256, @@ -716,22 +704,22 @@ static suite_algs_t suite_algs[] = { AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 32, TLS_1_2, TLS_1_2, }, - { TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, - KEY_ECDSA, ECP_256_BIT, - HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_3DES, 0, - TLS_1_0, TLS_1_2, - }, - { TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - KEY_RSA, ECP_256_BIT, - HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_3DES, 0, - TLS_1_0, TLS_1_2, - }, - { TLS_RSA_WITH_3DES_EDE_CBC_SHA, + { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_3DES, 0, + AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 32, + SSL_3_0, TLS_1_2, + }, + { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256, + KEY_RSA, MODP_NONE, + HASH_SHA256, PRF_HMAC_SHA2_256, + AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 16, + TLS_1_2, TLS_1_2, + }, + { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, + KEY_RSA, MODP_NONE, + HASH_SHA256, PRF_HMAC_SHA2_256, + AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 16, SSL_3_0, TLS_1_2, }, { TLS_ECDHE_ECDSA_WITH_NULL_SHA, @@ -746,23 +734,17 @@ static suite_algs_t suite_algs[] = { AUTH_HMAC_SHA1_160, ENCR_NULL, 0, TLS_1_0, TLS_1_2, }, - { TLS_RSA_WITH_NULL_SHA, - KEY_RSA, MODP_NONE, - HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_NULL, 0, - SSL_3_0, TLS_1_2, - }, { TLS_RSA_WITH_NULL_SHA256, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA2_256_256, ENCR_NULL, 0, TLS_1_2, TLS_1_2, }, - { TLS_RSA_WITH_NULL_MD5, + { TLS_RSA_WITH_NULL_SHA, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_MD5_128, ENCR_NULL, 0, - SSL_2_0, TLS_1_2, + AUTH_HMAC_SHA1_160, ENCR_NULL, 0, + SSL_3_0, TLS_1_2, }, }; @@ -1013,12 +995,6 @@ static void filter_cipher_config_suites(private_tls_crypto_t *this, suites[remaining++] = suites[i]; break; } - if (strcaseeq(token, "3des") && - suites[i].encr == ENCR_3DES) - { - suites[remaining++] = suites[i]; - break; - } if (strcaseeq(token, "null") && suites[i].encr == ENCR_NULL) { @@ -1051,12 +1027,6 @@ static void filter_mac_config_suites(private_tls_crypto_t *this, enumerator = enumerator_create_token(config, ",", " "); while (enumerator->enumerate(enumerator, &token)) { - if (strcaseeq(token, "md5") && - suites[i].mac == AUTH_HMAC_MD5_128) - { - suites[remaining++] = suites[i]; - break; - } if (strcaseeq(token, "sha1") && suites[i].mac == AUTH_HMAC_SHA1_160) { From f81c04e9e3a0eab7f35f94eed7c6511eb247e105 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Tue, 15 Sep 2020 16:08:08 +0200 Subject: [PATCH 054/107] libtls: Add missing cipher suite and TLS extension constants - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 - TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 - Encrypt then MAC - Extended master secret - Session ticket and also add missing suites in the unit test. --- src/libtls/tests/suites/test_suites.c | 11 + src/libtls/tls.c | 14 +- src/libtls/tls.h | 9 + src/libtls/tls_crypto.c | 9 +- src/libtls/tls_crypto.h | 417 +++++++++++++------------- 5 files changed, 251 insertions(+), 209 deletions(-) diff --git a/src/libtls/tests/suites/test_suites.c b/src/libtls/tests/suites/test_suites.c index f8ae12eb3..52ed9a63a 100644 --- a/src/libtls/tests/suites/test_suites.c +++ b/src/libtls/tests/suites/test_suites.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2020 Pascal Knecht + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG * @@ -170,6 +173,11 @@ START_TEST(test_cipher_names) CHECK_NAME(TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256); CHECK_NAME(TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256); CHECK_NAME(TLS_EMPTY_RENEGOTIATION_INFO_SCSV); + CHECK_NAME(TLS_AES_128_GCM_SHA256); + CHECK_NAME(TLS_AES_256_GCM_SHA384); + CHECK_NAME(TLS_CHACHA20_POLY1305_SHA256); + CHECK_NAME(TLS_AES_128_CCM_SHA256); + CHECK_NAME(TLS_AES_128_CCM_8_SHA256); CHECK_NAME(TLS_ECDH_ECDSA_WITH_NULL_SHA); CHECK_NAME(TLS_ECDH_ECDSA_WITH_RC4_128_SHA); CHECK_NAME(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA); @@ -229,6 +237,9 @@ START_TEST(test_cipher_names) CHECK_NAME(TLS_ECDHE_PSK_WITH_NULL_SHA); CHECK_NAME(TLS_ECDHE_PSK_WITH_NULL_SHA256); CHECK_NAME(TLS_ECDHE_PSK_WITH_NULL_SHA384); + CHECK_NAME(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256); + CHECK_NAME(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256); + CHECK_NAME(TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256); } END_TEST diff --git a/src/libtls/tls.c b/src/libtls/tls.c index fc313e08e..52e06a446 100644 --- a/src/libtls/tls.c +++ b/src/libtls/tls.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2020 Pascal Knecht + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2010 Martin Willi * Copyright (C) 2010 revosec AG * @@ -101,8 +104,17 @@ ENUM_NEXT(tls_extension_names, "client certificate type", "server certificate type"); ENUM_NEXT(tls_extension_names, - TLS_EXT_PRE_SHARED_KEY, TLS_EXT_PSK_KEY_EXCHANGE_MODES, + TLS_EXT_ENCRYPT_THEN_MAC, TLS_EXT_EXTENDED_MASTER_SECRET, TLS_SERVER_CERTIFICATE_TYPE, + "encrypt-then-mac", + "extended master secret"); +ENUM_NEXT(tls_extension_names, + TLS_EXT_SESSION_TICKET, TLS_EXT_SESSION_TICKET, + TLS_EXT_EXTENDED_MASTER_SECRET, + "session ticket"); +ENUM_NEXT(tls_extension_names, + TLS_EXT_PRE_SHARED_KEY, TLS_EXT_PSK_KEY_EXCHANGE_MODES, + TLS_EXT_SESSION_TICKET, "pre-shared key", "early data", "supported versions", diff --git a/src/libtls/tls.h b/src/libtls/tls.h index b8903af5d..c85c9c452 100644 --- a/src/libtls/tls.h +++ b/src/libtls/tls.h @@ -1,4 +1,7 @@ /* + * Copyright (C) 2020 Pascal Knecht + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2010 Martin Willi * Copyright (C) 2010 revosec AG * @@ -156,6 +159,12 @@ enum tls_extension_t { TLS_CLIENT_CERTIFICATE_TYPE = 19, /** exchange raw public key, server side*/ TLS_SERVER_CERTIFICATE_TYPE = 20, + /** use encrypt-then-MAC security mechanism RFC 7366 */ + TLS_EXT_ENCRYPT_THEN_MAC = 22, + /** bind master secret to handshake data RFC 7627 */ + TLS_EXT_EXTENDED_MASTER_SECRET = 23, + /** session resumption without server-side state RFC 5077 */ + TLS_EXT_SESSION_TICKET = 35, /** negotiate identity of the psk **/ TLS_EXT_PRE_SHARED_KEY = 41, /** send data in 0-RTT when psk is used and early data is allowed **/ diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index a7ad738e6..f6d88a451 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -251,7 +251,14 @@ ENUM_NEXT(tls_cipher_suite_names, TLS_ECDH_ECDSA_WITH_NULL_SHA, "TLS_ECDHE_PSK_WITH_NULL_SHA", "TLS_ECDHE_PSK_WITH_NULL_SHA256", "TLS_ECDHE_PSK_WITH_NULL_SHA384"); -ENUM_END(tls_cipher_suite_names, TLS_ECDHE_PSK_WITH_NULL_SHA384); +ENUM_NEXT(tls_cipher_suite_names, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_PSK_WITH_NULL_SHA384, + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256"); +ENUM_END(tls_cipher_suite_names, TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256); + ENUM(tls_hash_algorithm_names, TLS_HASH_NONE, TLS_HASH_SHA512, "NONE", diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h index 958b7db29..8324b2243 100644 --- a/src/libtls/tls_crypto.h +++ b/src/libtls/tls_crypto.h @@ -48,219 +48,222 @@ typedef enum tls_ec_point_format_t tls_ec_point_format_t; * TLS cipher suites */ enum tls_cipher_suite_t { - TLS_NULL_WITH_NULL_NULL = 0x0000, - TLS_RSA_WITH_NULL_MD5 = 0x0001, - TLS_RSA_WITH_NULL_SHA = 0x0002, - TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003, - TLS_RSA_WITH_RC4_128_MD5 = 0x0004, - TLS_RSA_WITH_RC4_128_SHA = 0x0005, - TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x0006, - TLS_RSA_WITH_IDEA_CBC_SHA = 0x0007, - TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0008, - TLS_RSA_WITH_DES_CBC_SHA = 0x0009, - TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A, - TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x000B, - TLS_DH_DSS_WITH_DES_CBC_SHA = 0x000C, - TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x000D, - TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x000E, - TLS_DH_RSA_WITH_DES_CBC_SHA = 0x000F, - TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x0010, - TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0011, - TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x0012, - TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013, - TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0014, - TLS_DHE_RSA_WITH_DES_CBC_SHA = 0x0015, - TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016, - TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x0017, - TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018, - TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0x0019, - TLS_DH_anon_WITH_DES_CBC_SHA = 0x001A, - TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x001B, + TLS_NULL_WITH_NULL_NULL = 0x0000, + TLS_RSA_WITH_NULL_MD5 = 0x0001, + TLS_RSA_WITH_NULL_SHA = 0x0002, + TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003, + TLS_RSA_WITH_RC4_128_MD5 = 0x0004, + TLS_RSA_WITH_RC4_128_SHA = 0x0005, + TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x0006, + TLS_RSA_WITH_IDEA_CBC_SHA = 0x0007, + TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0008, + TLS_RSA_WITH_DES_CBC_SHA = 0x0009, + TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A, + TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x000B, + TLS_DH_DSS_WITH_DES_CBC_SHA = 0x000C, + TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x000D, + TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x000E, + TLS_DH_RSA_WITH_DES_CBC_SHA = 0x000F, + TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x0010, + TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0011, + TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x0012, + TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013, + TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0014, + TLS_DHE_RSA_WITH_DES_CBC_SHA = 0x0015, + TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016, + TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x0017, + TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018, + TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0x0019, + TLS_DH_anon_WITH_DES_CBC_SHA = 0x001A, + TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x001B, - TLS_KRB5_WITH_DES_CBC_SHA = 0x001E, - TLS_KRB5_WITH_3DES_EDE_CBC_SHA = 0x001F, - TLS_KRB5_WITH_RC4_128_SHA = 0x0020, - TLS_KRB5_WITH_IDEA_CBC_SHA = 0x0021, - TLS_KRB5_WITH_DES_CBC_MD5 = 0x0022, - TLS_KRB5_WITH_3DES_EDE_CBC_MD5 = 0x0023, - TLS_KRB5_WITH_RC4_128_MD5 = 0x0024, - TLS_KRB5_WITH_IDEA_CBC_MD5 = 0x0025, - TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA = 0x0026, - TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA = 0x0027, - TLS_KRB5_EXPORT_WITH_RC4_40_SHA = 0x0028, - TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 = 0x0029, - TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 = 0x002A, - TLS_KRB5_EXPORT_WITH_RC4_40_MD5 = 0x002B, - TLS_PSK_WITH_NULL_SHA = 0x002C, - TLS_DHE_PSK_WITH_NULL_SHA = 0x002D, - TLS_RSA_PSK_WITH_NULL_SHA = 0x002E, - TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F, - TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x0030, - TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x0031, - TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032, - TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033, - TLS_DH_anon_WITH_AES_128_CBC_SHA = 0x0034, - TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035, - TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x0036, - TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x0037, - TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038, - TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039, - TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x003A, - TLS_RSA_WITH_NULL_SHA256 = 0x003B, - TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C, - TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D, - TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x003E, - TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x003F, - TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x0040, - TLS_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0041, - TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0042, - TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0043, - TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0044, - TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0045, - TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA = 0x0046, + TLS_KRB5_WITH_DES_CBC_SHA = 0x001E, + TLS_KRB5_WITH_3DES_EDE_CBC_SHA = 0x001F, + TLS_KRB5_WITH_RC4_128_SHA = 0x0020, + TLS_KRB5_WITH_IDEA_CBC_SHA = 0x0021, + TLS_KRB5_WITH_DES_CBC_MD5 = 0x0022, + TLS_KRB5_WITH_3DES_EDE_CBC_MD5 = 0x0023, + TLS_KRB5_WITH_RC4_128_MD5 = 0x0024, + TLS_KRB5_WITH_IDEA_CBC_MD5 = 0x0025, + TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA = 0x0026, + TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA = 0x0027, + TLS_KRB5_EXPORT_WITH_RC4_40_SHA = 0x0028, + TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 = 0x0029, + TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 = 0x002A, + TLS_KRB5_EXPORT_WITH_RC4_40_MD5 = 0x002B, + TLS_PSK_WITH_NULL_SHA = 0x002C, + TLS_DHE_PSK_WITH_NULL_SHA = 0x002D, + TLS_RSA_PSK_WITH_NULL_SHA = 0x002E, + TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F, + TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x0030, + TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x0031, + TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033, + TLS_DH_anon_WITH_AES_128_CBC_SHA = 0x0034, + TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035, + TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x0036, + TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x0037, + TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039, + TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x003A, + TLS_RSA_WITH_NULL_SHA256 = 0x003B, + TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C, + TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D, + TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x003E, + TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x003F, + TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x0040, + TLS_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0041, + TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0042, + TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0043, + TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0044, + TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0045, + TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA = 0x0046, - TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067, - TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x0068, - TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x0069, - TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x006A, - TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B, - TLS_DH_anon_WITH_AES_128_CBC_SHA256 = 0x006C, - TLS_DH_anon_WITH_AES_256_CBC_SHA256 = 0x006D, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067, + TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x0068, + TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x0069, + TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x006A, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B, + TLS_DH_anon_WITH_AES_128_CBC_SHA256 = 0x006C, + TLS_DH_anon_WITH_AES_256_CBC_SHA256 = 0x006D, - TLS_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0084, - TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0085, - TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0086, - TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0087, - TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0088, - TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA = 0x0089, - TLS_PSK_WITH_RC4_128_SHA = 0x008A, - TLS_PSK_WITH_3DES_EDE_CBC_SHA = 0x008B, - TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C, - TLS_PSK_WITH_AES_256_CBC_SHA = 0x008D, - TLS_DHE_PSK_WITH_RC4_128_SHA = 0x008E, - TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = 0x008F, - TLS_DHE_PSK_WITH_AES_128_CBC_SHA = 0x0090, - TLS_DHE_PSK_WITH_AES_256_CBC_SHA = 0x0091, - TLS_RSA_PSK_WITH_RC4_128_SHA = 0x0092, - TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA = 0x0093, - TLS_RSA_PSK_WITH_AES_128_CBC_SHA = 0x0094, - TLS_RSA_PSK_WITH_AES_256_CBC_SHA = 0x0095, - TLS_RSA_WITH_SEED_CBC_SHA = 0x0096, - TLS_DH_DSS_WITH_SEED_CBC_SHA = 0x0097, - TLS_DH_RSA_WITH_SEED_CBC_SHA = 0x0098, - TLS_DHE_DSS_WITH_SEED_CBC_SHA = 0x0099, - TLS_DHE_RSA_WITH_SEED_CBC_SHA = 0x009A, - TLS_DH_anon_WITH_SEED_CBC_SHA = 0x009B, - TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C, - TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D, - TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E, - TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F, - TLS_DH_RSA_WITH_AES_128_GCM_SHA256 = 0x00A0, - TLS_DH_RSA_WITH_AES_256_GCM_SHA384 = 0x00A1, - TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2, - TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3, - TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = 0x00A4, - TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = 0x00A5, - TLS_DH_anon_WITH_AES_128_GCM_SHA256 = 0x00A6, - TLS_DH_anon_WITH_AES_256_GCM_SHA384 = 0x00A7, - TLS_PSK_WITH_AES_128_GCM_SHA256 = 0x00A8, - TLS_PSK_WITH_AES_256_GCM_SHA384 = 0x00A9, - TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 = 0x00AA, - TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 = 0x00AB, - TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 = 0x00AC, - TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 = 0x00AD, - TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE, - TLS_PSK_WITH_AES_256_CBC_SHA384 = 0x00AF, - TLS_PSK_WITH_NULL_SHA256 = 0x00B0, - TLS_PSK_WITH_NULL_SHA384 = 0x00B1, - TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 = 0x00B2, - TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 = 0x00B3, - TLS_DHE_PSK_WITH_NULL_SHA256 = 0x00B4, - TLS_DHE_PSK_WITH_NULL_SHA384 = 0x00B5, - TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 = 0x00B6, - TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 = 0x00B7, - TLS_RSA_PSK_WITH_NULL_SHA256 = 0x00B8, - TLS_RSA_PSK_WITH_NULL_SHA384 = 0x00B9, - TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BA, - TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BB, - TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BC, - TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BD, - TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BE, - TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BF, - TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C0, - TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C1, - TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C2, - TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C3, - TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C4, - TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C5, + TLS_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0084, + TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0085, + TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0086, + TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0087, + TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0088, + TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA = 0x0089, + TLS_PSK_WITH_RC4_128_SHA = 0x008A, + TLS_PSK_WITH_3DES_EDE_CBC_SHA = 0x008B, + TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C, + TLS_PSK_WITH_AES_256_CBC_SHA = 0x008D, + TLS_DHE_PSK_WITH_RC4_128_SHA = 0x008E, + TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = 0x008F, + TLS_DHE_PSK_WITH_AES_128_CBC_SHA = 0x0090, + TLS_DHE_PSK_WITH_AES_256_CBC_SHA = 0x0091, + TLS_RSA_PSK_WITH_RC4_128_SHA = 0x0092, + TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA = 0x0093, + TLS_RSA_PSK_WITH_AES_128_CBC_SHA = 0x0094, + TLS_RSA_PSK_WITH_AES_256_CBC_SHA = 0x0095, + TLS_RSA_WITH_SEED_CBC_SHA = 0x0096, + TLS_DH_DSS_WITH_SEED_CBC_SHA = 0x0097, + TLS_DH_RSA_WITH_SEED_CBC_SHA = 0x0098, + TLS_DHE_DSS_WITH_SEED_CBC_SHA = 0x0099, + TLS_DHE_RSA_WITH_SEED_CBC_SHA = 0x009A, + TLS_DH_anon_WITH_SEED_CBC_SHA = 0x009B, + TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C, + TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D, + TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E, + TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F, + TLS_DH_RSA_WITH_AES_128_GCM_SHA256 = 0x00A0, + TLS_DH_RSA_WITH_AES_256_GCM_SHA384 = 0x00A1, + TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2, + TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3, + TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = 0x00A4, + TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = 0x00A5, + TLS_DH_anon_WITH_AES_128_GCM_SHA256 = 0x00A6, + TLS_DH_anon_WITH_AES_256_GCM_SHA384 = 0x00A7, + TLS_PSK_WITH_AES_128_GCM_SHA256 = 0x00A8, + TLS_PSK_WITH_AES_256_GCM_SHA384 = 0x00A9, + TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 = 0x00AA, + TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 = 0x00AB, + TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 = 0x00AC, + TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 = 0x00AD, + TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE, + TLS_PSK_WITH_AES_256_CBC_SHA384 = 0x00AF, + TLS_PSK_WITH_NULL_SHA256 = 0x00B0, + TLS_PSK_WITH_NULL_SHA384 = 0x00B1, + TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 = 0x00B2, + TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 = 0x00B3, + TLS_DHE_PSK_WITH_NULL_SHA256 = 0x00B4, + TLS_DHE_PSK_WITH_NULL_SHA384 = 0x00B5, + TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 = 0x00B6, + TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 = 0x00B7, + TLS_RSA_PSK_WITH_NULL_SHA256 = 0x00B8, + TLS_RSA_PSK_WITH_NULL_SHA384 = 0x00B9, + TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BA, + TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BB, + TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BC, + TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BD, + TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BE, + TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BF, + TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C0, + TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C1, + TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C2, + TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C3, + TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C4, + TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C5, - TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF, + TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF, - TLS_AES_128_GCM_SHA256 = 0x1301, - TLS_AES_256_GCM_SHA384 = 0x1302, - TLS_CHACHA20_POLY1305_SHA256 = 0x1303, - TLS_AES_128_CCM_SHA256 = 0x1304, - TLS_AES_128_CCM_8_SHA256 = 0x1305, + TLS_AES_128_GCM_SHA256 = 0x1301, + TLS_AES_256_GCM_SHA384 = 0x1302, + TLS_CHACHA20_POLY1305_SHA256 = 0x1303, + TLS_AES_128_CCM_SHA256 = 0x1304, + TLS_AES_128_CCM_8_SHA256 = 0x1305, - TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xC001, - TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 0xC002, - TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC003, - TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = 0xC004, - TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = 0xC005, - TLS_ECDHE_ECDSA_WITH_NULL_SHA = 0xC006, - TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007, - TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC008, - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009, - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A, - TLS_ECDH_RSA_WITH_NULL_SHA = 0xC00B, - TLS_ECDH_RSA_WITH_RC4_128_SHA = 0xC00C, - TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = 0xC00D, - TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = 0xC00E, - TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = 0xC00F, - TLS_ECDHE_RSA_WITH_NULL_SHA = 0xC010, - TLS_ECDHE_RSA_WITH_RC4_128_SHA = 0xC011, - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = 0xC012, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013, - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014, - TLS_ECDH_anon_WITH_NULL_SHA = 0xC015, - TLS_ECDH_anon_WITH_RC4_128_SHA = 0xC016, - TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA = 0xC017, - TLS_ECDH_anon_WITH_AES_128_CBC_SHA = 0xC018, - TLS_ECDH_anon_WITH_AES_256_CBC_SHA = 0xC019, - TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 0xC01A, - TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 0xC01B, - TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = 0xC01C, - TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0xC01D, - TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 0xC01E, - TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = 0xC01F, - TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 0xC020, - TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 0xC021, - TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = 0xC022, - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023, - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024, - TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC025, - TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC026, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027, - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028, - TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = 0xC029, - TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = 0xC02A, - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B, - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C, - TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02D, - TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02E, - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F, - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030, - TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = 0xC031, - TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = 0xC032, - TLS_ECDHE_PSK_WITH_RC4_128_SHA = 0xC033, - TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA = 0xC034, - TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA = 0xC035, - TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA = 0xC036, - TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 = 0xC037, - TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 = 0xC038, - TLS_ECDHE_PSK_WITH_NULL_SHA = 0xC039, - TLS_ECDHE_PSK_WITH_NULL_SHA256 = 0xC03A, - TLS_ECDHE_PSK_WITH_NULL_SHA384 = 0xC03B + TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xC001, + TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 0xC002, + TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC003, + TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = 0xC004, + TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = 0xC005, + TLS_ECDHE_ECDSA_WITH_NULL_SHA = 0xC006, + TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007, + TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC008, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A, + TLS_ECDH_RSA_WITH_NULL_SHA = 0xC00B, + TLS_ECDH_RSA_WITH_RC4_128_SHA = 0xC00C, + TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = 0xC00D, + TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = 0xC00E, + TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = 0xC00F, + TLS_ECDHE_RSA_WITH_NULL_SHA = 0xC010, + TLS_ECDHE_RSA_WITH_RC4_128_SHA = 0xC011, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = 0xC012, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014, + TLS_ECDH_anon_WITH_NULL_SHA = 0xC015, + TLS_ECDH_anon_WITH_RC4_128_SHA = 0xC016, + TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA = 0xC017, + TLS_ECDH_anon_WITH_AES_128_CBC_SHA = 0xC018, + TLS_ECDH_anon_WITH_AES_256_CBC_SHA = 0xC019, + TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 0xC01A, + TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 0xC01B, + TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = 0xC01C, + TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0xC01D, + TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 0xC01E, + TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = 0xC01F, + TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 0xC020, + TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 0xC021, + TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = 0xC022, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024, + TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC025, + TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC026, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028, + TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = 0xC029, + TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = 0xC02A, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C, + TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02D, + TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02E, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030, + TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = 0xC031, + TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = 0xC032, + TLS_ECDHE_PSK_WITH_RC4_128_SHA = 0xC033, + TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA = 0xC034, + TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA = 0xC035, + TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA = 0xC036, + TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 = 0xC037, + TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 = 0xC038, + TLS_ECDHE_PSK_WITH_NULL_SHA = 0xC039, + TLS_ECDHE_PSK_WITH_NULL_SHA256 = 0xC03A, + TLS_ECDHE_PSK_WITH_NULL_SHA384 = 0xC03B, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA8, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA9, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAA, }; /** From 6549adb6088e2eb4b17ba4d19b917e4c2d13824f Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Wed, 16 Sep 2020 08:54:54 +0200 Subject: [PATCH 055/107] tls-crypto: Fix invalid signature algorithm list building List building also added an additional length field which is required by client-side TLS extensions but not for server-side certificate request extension. Now the method only returns a list of supported signature algorithms and the implementation is responsible to add additional length fields. Fixes: 07f826af673d ("Fixed encoding of TLS extensions (elliptic_curves and signature_algorithms)") --- src/libtls/tls_crypto.c | 1 - src/libtls/tls_peer.c | 16 +++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index f6d88a451..b9be2c04a 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1474,7 +1474,6 @@ METHOD(tls_crypto_t, get_signature_algorithms, void, } } - supported->wrap16(supported); writer->write_data16(writer, supported->get_buf(supported)); supported->destroy(supported); } diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 26792f930..4e5c2cc03 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -49,7 +49,6 @@ typedef enum { STATE_FINISHED_SENT_KEY_SWITCHED, STATE_KEY_UPDATE_REQUESTED, STATE_KEY_UPDATE_SENT, - } peer_state_t; /** @@ -532,6 +531,7 @@ static status_t process_certificate(private_tls_peer_t *this, return NEED_MORE; } } + if (!reader->read_data24(reader, &data)) { DBG1(DBG_TLS, "certificate message header invalid"); @@ -1195,7 +1195,7 @@ static status_t send_client_hello(private_tls_peer_t *this, bio_writer_t *writer) { tls_cipher_suite_t *suites; - bio_writer_t *extensions, *curves = NULL, *versions, *key_share; + bio_writer_t *extensions, *curves = NULL, *versions, *key_share, *signatures; tls_version_t version_max, version_min; diffie_hellman_group_t group; tls_named_group_t curve, selected_curve = 0; @@ -1339,11 +1339,18 @@ static status_t send_client_hello(private_tls_peer_t *this, DBG2(DBG_TLS, "sending extension: %N", tls_extension_names, TLS_EXT_SIGNATURE_ALGORITHMS); extensions->write_uint16(extensions, TLS_EXT_SIGNATURE_ALGORITHMS); - this->crypto->get_signature_algorithms(this->crypto, extensions, FALSE); + signatures = bio_writer_create(32); + this->crypto->get_signature_algorithms(this->crypto, signatures, FALSE); + extensions->write_data16(extensions, signatures->get_buf(signatures)); + signatures->destroy(signatures); + DBG2(DBG_TLS, "sending extension: %N", tls_extension_names, TLS_EXT_SIGNATURE_ALGORITHMS_CERT); extensions->write_uint16(extensions, TLS_EXT_SIGNATURE_ALGORITHMS_CERT); - this->crypto->get_signature_algorithms(this->crypto, extensions, TRUE); + signatures = bio_writer_create(32); + this->crypto->get_signature_algorithms(this->crypto, signatures, TRUE); + extensions->write_data16(extensions, signatures->get_buf(signatures)); + signatures->destroy(signatures); if (this->dh) { @@ -1802,7 +1809,6 @@ METHOD(tls_handshake_t, cipherspec_changed, bool, return FALSE; } } - } METHOD(tls_handshake_t, change_cipherspec, void, From 328d7af6d2ed029f35e218df6085400792968fa4 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Wed, 16 Sep 2020 14:10:57 +0200 Subject: [PATCH 056/107] tls-crypto: Rename parameter to be more consistent Also add missing parameter in documentation of calculate_finished method. --- src/libtls/tls_crypto.c | 4 ++-- src/libtls/tls_crypto.h | 3 ++- src/libtls/tls_hkdf.c | 10 +++++----- src/libtls/tls_hkdf.h | 4 ++-- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index b9be2c04a..eb5f3b96b 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1867,7 +1867,7 @@ METHOD(tls_crypto_t, calculate_finished_legacy, bool, } METHOD(tls_crypto_t, calculate_finished, bool, - private_tls_crypto_t *this, bool is_server, chunk_t *out) + private_tls_crypto_t *this, bool server, chunk_t *out) { chunk_t finished_key, finished_hash; @@ -1880,7 +1880,7 @@ METHOD(tls_crypto_t, calculate_finished, bool, DBG1(DBG_TLS, "creating hash of handshake failed"); return FALSE; } - if (!this->hkdf->derive_finished(this->hkdf, is_server, &finished_key)) + if (!this->hkdf->derive_finished(this->hkdf, server, &finished_key)) { DBG1(DBG_TLS, "generating finished key failed"); chunk_clear(&finished_hash); diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h index 8324b2243..354329b8a 100644 --- a/src/libtls/tls_crypto.h +++ b/src/libtls/tls_crypto.h @@ -563,10 +563,11 @@ struct tls_crypto_t { /** * Calculate the data of a TLS finished message. * + * @param server Whether the server or client finish message is calculated * @param out buffer to write finished data to * @return TRUE if calculation successful */ - bool (*calculate_finished)(tls_crypto_t *this, bool is_server, + bool (*calculate_finished)(tls_crypto_t *this, bool server, chunk_t *out); /** diff --git a/src/libtls/tls_hkdf.c b/src/libtls/tls_hkdf.c index d31a41d79..b03efa0bd 100644 --- a/src/libtls/tls_hkdf.c +++ b/src/libtls/tls_hkdf.c @@ -476,12 +476,12 @@ METHOD(tls_hkdf_t, generate_secret, bool, * Derive keys/IVs from the current traffic secrets. */ static bool get_shared_label_keys(private_tls_hkdf_t *this, chunk_t label, - bool is_server, size_t length, chunk_t *key) + bool server, size_t length, chunk_t *key) { chunk_t result = chunk_empty, secret; - secret = is_server ? this->server_traffic_secret - : this->client_traffic_secret; + secret = server ? this->server_traffic_secret + : this->client_traffic_secret; if (!expand_label(this, secret, label, chunk_empty, length, &result)) { @@ -516,10 +516,10 @@ METHOD(tls_hkdf_t, derive_iv, bool, } METHOD(tls_hkdf_t, derive_finished, bool, - private_tls_hkdf_t *this, bool is_server, chunk_t *finished) + private_tls_hkdf_t *this, bool server, chunk_t *finished) { return get_shared_label_keys(this, chunk_from_str("finished"), - is_server, + server, this->hasher->get_hash_size(this->hasher), finished); } diff --git a/src/libtls/tls_hkdf.h b/src/libtls/tls_hkdf.h index 2b2bf3e03..2ce3d038a 100644 --- a/src/libtls/tls_hkdf.h +++ b/src/libtls/tls_hkdf.h @@ -106,11 +106,11 @@ struct tls_hkdf_t { * section 4.4.4. Space for returned secret is allocated and must be freed * by the caller. * - * @param is_server TRUE if server, FALSE if client derives secret + * @param server Whether the client or server finish key is derived * @param finished key will be written into this chunk * @return TRUE if secrets derived successfully */ - bool (*derive_finished)(tls_hkdf_t *this, bool is_server, + bool (*derive_finished)(tls_hkdf_t *this, bool server, chunk_t *finished); /** From 7fbe2e27ecf63d24b4fe4daa5d92bd6f59f4805e Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Tue, 8 Sep 2020 15:58:14 +0200 Subject: [PATCH 057/107] tls-server: TLS 1.3 support for TLS server implementation Add basic support to establish a TLS 1.3 connection with a client capable of the same protocol version. --- src/libtls/tests/suites/test_socket.c | 25 + src/libtls/tls_crypto.c | 22 + src/libtls/tls_server.c | 772 +++++++++++++++++++------- 3 files changed, 624 insertions(+), 195 deletions(-) diff --git a/src/libtls/tests/suites/test_socket.c b/src/libtls/tests/suites/test_socket.c index a5f80e59e..5682dad88 100644 --- a/src/libtls/tests/suites/test_socket.c +++ b/src/libtls/tests/suites/test_socket.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2020 Pascal Knecht + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG * @@ -441,6 +444,18 @@ static void test_tls(tls_version_t version, uint16_t port, bool cauth, u_int i) free(config); } +START_TEST(test_tls13) +{ + test_tls(TLS_1_3, 5669, FALSE, _i); +} +END_TEST + +START_TEST(test_tls13_mutual) +{ + test_tls(TLS_1_3, 5670, TRUE, _i); +} +END_TEST + START_TEST(test_tls12) { test_tls(TLS_1_2, 5671, FALSE, _i); @@ -488,6 +503,16 @@ Suite *socket_suite_create() s = suite_create("socket"); + tc = tcase_create("TLS 1.3/anon"); + tcase_add_checked_fixture(tc, setup_creds, teardown_creds); + add_tls_test(test_tls13, TLS_1_3); + suite_add_tcase(s, tc); + + tc = tcase_create("TLS 1.3/mutl"); + tcase_add_checked_fixture(tc, setup_creds, teardown_creds); + add_tls_test(test_tls13_mutual, TLS_1_3); + suite_add_tcase(s, tc); + tc = tcase_create("TLS 1.2/anon"); tcase_add_checked_fixture(tc, setup_creds, teardown_creds); add_tls_test(test_tls12, TLS_1_2); diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index eb5f3b96b..52140f672 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1487,6 +1487,16 @@ static signature_params_t *params_for_scheme(tls_signature_scheme_t sig) for (i = 0; i < countof(schemes); i++) { + /* strongSwan supports only RSA_PSS_RSAE schemes for signing but can + * verify public keys in rsaEncryption as well as rsassaPss encoding. + * Current implementation does not distinguish between signing and + * verifying. */ + if (sig == TLS_SIG_RSA_PSS_PSS_SHA256 || + sig == TLS_SIG_RSA_PSS_PSS_SHA384 || + sig == TLS_SIG_RSA_PSS_PSS_SHA512) + { + continue; + } if (schemes[i].sig == sig) { return &schemes[i].params; @@ -1677,6 +1687,18 @@ METHOD(tls_crypto_t, sign, bool, chunk_t sig; bool done = FALSE; + if (this->tls->get_version_max(this->tls) >= TLS_1_3) + { + chunk_t transcript_hash; + + if (!hash_data(this, data, &transcript_hash)) + { + DBG1(DBG_TLS, "unable to create transcript hash"); + return FALSE; + } + data = chunk_cata("cm", tls13_sig_data_server, transcript_hash); + } + if (!hashsig.len) { /* fallback if none given */ hashsig = hashsig_def; diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 990365ff1..c85825208 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2020 Pascal Knecht + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2010 Martin Willi * Copyright (C) 2010 revosec AG * @@ -42,6 +45,9 @@ typedef enum { STATE_FINISHED_RECEIVED, STATE_CIPHERSPEC_CHANGED_OUT, STATE_FINISHED_SENT, + /* new states in TLS 1.3 */ + STATE_ENCRYPTED_EXTENSIONS_SENT, + STATE_CERT_VERIFY_SENT, } server_state_t; /** @@ -119,6 +125,11 @@ struct private_tls_server_t { */ diffie_hellman_t *dh; + /** + * Requested DH group + */ + tls_named_group_t requested_curve; + /** * Selected TLS cipher suite */ @@ -172,50 +183,96 @@ static bool select_suite_and_key(private_tls_server_t *this, this->server); return FALSE; } - this->suite = this->crypto->select_cipher_suite(this->crypto, - suites, count, key->get_type(key)); - if (!this->suite) - { /* no match for this key, try to find another type */ - if (key->get_type(key) == KEY_ECDSA) - { - type = KEY_RSA; - } - else - { - type = KEY_ECDSA; - } - key->destroy(key); - this->suite = this->crypto->select_cipher_suite(this->crypto, - suites, count, type); + if (this->tls->get_version_max(this->tls) >= TLS_1_3) + { + /* currently no support to derive key based on client supported + * signature schemes */ + this->suite = this->crypto->select_cipher_suite(this->crypto, suites, + count, KEY_ANY); if (!this->suite) { DBG1(DBG_TLS, "received cipher suites unacceptable"); return FALSE; } - this->server_auth->destroy(this->server_auth); - this->server_auth = auth_cfg_create(); - key = lib->credmgr->get_private(lib->credmgr, type, this->server, - this->server_auth); - if (!key) - { - DBG1(DBG_TLS, "received cipher suites unacceptable"); - return FALSE; + } + else + { + this->suite = this->crypto->select_cipher_suite(this->crypto, suites, + count, + key->get_type(key)); + if (!this->suite) + { /* no match for this key, try to find another type */ + if (key->get_type(key) == KEY_ECDSA) + { + type = KEY_RSA; + } + else + { + type = KEY_ECDSA; + } + key->destroy(key); + + this->suite = this->crypto->select_cipher_suite(this->crypto, suites, + count, type); + if (!this->suite) + { + DBG1(DBG_TLS, "received cipher suites unacceptable"); + return FALSE; + } + this->server_auth->destroy(this->server_auth); + this->server_auth = auth_cfg_create(); + key = lib->credmgr->get_private(lib->credmgr, type, this->server, + this->server_auth); + if (!key) + { + DBG1(DBG_TLS, "received cipher suites unacceptable"); + return FALSE; + } } } this->private = key; return TRUE; } +/** + * Check if the peer supports a given TLS curve + */ +static bool peer_supports_curve(private_tls_server_t *this, + tls_named_group_t curve) +{ + bio_reader_t *reader; + uint16_t current; + + if (!this->curves_received) + { /* none received, assume yes */ + return TRUE; + } + reader = bio_reader_create(this->curves); + while (reader->remaining(reader) && reader->read_uint16(reader, ¤t)) + { + if (current == curve) + { + reader->destroy(reader); + return TRUE; + } + } + reader->destroy(reader); + return FALSE; +} + /** * Process client hello message */ static status_t process_client_hello(private_tls_server_t *this, bio_reader_t *reader) { - uint16_t version, extension; - chunk_t random, session, ciphers, compression, ext = chunk_empty; - bio_reader_t *extensions; + uint16_t legacy_version = 0, version = 0, key_share_length, key_type = 0; + uint16_t extension_type = 0; + chunk_t random, session, ciphers, versions = chunk_empty, compression; + chunk_t ext = chunk_empty, key_share = chunk_empty; + chunk_t extension_data = chunk_empty; + bio_reader_t *extensions, *extension; tls_cipher_suite_t *suites; int count, i; rng_t *rng; @@ -223,7 +280,7 @@ static status_t process_client_hello(private_tls_server_t *this, this->crypto->append_handshake(this->crypto, TLS_CLIENT_HELLO, reader->peek(reader)); - if (!reader->read_uint16(reader, &version) || + if (!reader->read_uint16(reader, &legacy_version) || !reader->read_data(reader, sizeof(this->client_random), &random) || !reader->read_data8(reader, &session) || !reader->read_data16(reader, &ciphers) || @@ -239,37 +296,63 @@ static status_t process_client_hello(private_tls_server_t *this, * as that might change the min./max. versions */ this->crypto->get_cipher_suites(this->crypto, NULL); - if (ext.len) + extensions = bio_reader_create(ext); + while (extensions->remaining(extensions)) { - extensions = bio_reader_create(ext); - while (extensions->remaining(extensions)) + if (!extensions->read_uint16(extensions, &extension_type) || + !extensions->read_data16(extensions, &extension_data)) { - if (!extensions->read_uint16(extensions, &extension) || - !extensions->read_data16(extensions, &ext)) - { - DBG1(DBG_TLS, "received invalid ClientHello Extensions"); - this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); - extensions->destroy(extensions); - return NEED_MORE; - } - DBG2(DBG_TLS, "received TLS '%N' extension", - tls_extension_names, extension); - DBG3(DBG_TLS, "%B", &ext); - switch (extension) - { - case TLS_EXT_SIGNATURE_ALGORITHMS: - this->hashsig = chunk_clone(ext); - break; - case TLS_EXT_SUPPORTED_GROUPS: - this->curves_received = TRUE; - this->curves = chunk_clone(ext); - break; - default: - break; - } + DBG1(DBG_TLS, "received invalid ClientHello Extensions"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + extensions->destroy(extensions); + return NEED_MORE; } - extensions->destroy(extensions); + extension = bio_reader_create(extension_data); + DBG2(DBG_TLS, "received TLS '%N' extension", + tls_extension_names, extension_type); + DBG3(DBG_TLS, "%B", &extension_data); + switch (extension_type) + { + case TLS_EXT_SIGNATURE_ALGORITHMS: + this->hashsig = chunk_clone(extension_data); + break; + case TLS_EXT_SUPPORTED_GROUPS: + this->curves_received = TRUE; + this->curves = chunk_clone(extension_data); + break; + case TLS_EXT_SUPPORTED_VERSIONS: + if (!extension->read_data8(extension, &versions)) + { + DBG1(DBG_TLS, "invalid %N extension", + tls_extension_names, extension_type); + this->alert->add(this->alert, TLS_FATAL, + TLS_DECODE_ERROR); + extensions->destroy(extensions); + extension->destroy(extension); + return NEED_MORE; + } + break; + case TLS_EXT_KEY_SHARE: + if (!extension->read_uint16(extension, &key_share_length) || + !extension->read_uint16(extension, &key_type) || + !extension->read_data16(extension, &key_share) || + !key_share.len) + { + DBG1(DBG_TLS, "invalid %N extension", + tls_extension_names, extension_type); + this->alert->add(this->alert, TLS_FATAL, + TLS_DECODE_ERROR); + extensions->destroy(extensions); + extension->destroy(extension); + return NEED_MORE; + } + break; + default: + break; + } + extension->destroy(extension); } + extensions->destroy(extensions); memcpy(this->client_random, random.ptr, sizeof(this->client_random)); @@ -286,18 +369,48 @@ static status_t process_client_hello(private_tls_server_t *this, } rng->destroy(rng); - if (!this->tls->set_version(this->tls, version, version)) + if (versions.len) { - DBG1(DBG_TLS, "negotiated version %N not supported", - tls_version_names, version); + bio_reader_t *client_versions; + + client_versions = bio_reader_create(versions); + while (client_versions->remaining(client_versions)) + { + if (client_versions->read_uint16(client_versions, &version)) + { + if (this->tls->set_version(this->tls, version, version)) + { + this->client_version = version; + break; + } + } + } + client_versions->destroy(client_versions); + } + else + { + version = legacy_version; + if (this->tls->set_version(this->tls, version, version)) + { + this->client_version = version; + } + } + if (!this->client_version) + { + DBG1(DBG_TLS, "proposed version %N not supported", tls_version_names, + version); this->alert->add(this->alert, TLS_FATAL, TLS_PROTOCOL_VERSION); return NEED_MORE; } - this->client_version = version; - this->suite = this->crypto->resume_session(this->crypto, session, this->peer, - chunk_from_thing(this->client_random), - chunk_from_thing(this->server_random)); + if (this->tls->get_version_max(this->tls) < TLS_1_3) + { + this->suite = this->crypto->resume_session(this->crypto, session, + this->peer, + chunk_from_thing(this->client_random), + chunk_from_thing(this->server_random)); + } + if (this->suite) { this->session = chunk_clone(session); @@ -321,16 +434,74 @@ static status_t process_client_hello(private_tls_server_t *this, this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); return NEED_MORE; } - rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG); - if (!rng || !rng->allocate_bytes(rng, SESSION_ID_SIZE, &this->session)) + if (this->tls->get_version_max(this->tls) < TLS_1_3) { - DBG1(DBG_TLS, "generating TLS session identifier failed, skipped"); + rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG); + if (!rng || + !rng->allocate_bytes(rng, SESSION_ID_SIZE, &this->session)) + { + DBG1(DBG_TLS, "generating TLS session identifier failed, skipped"); + } + DESTROY_IF(rng); + } + else + { + this->session = chunk_clone(session); } - DESTROY_IF(rng); DBG1(DBG_TLS, "negotiated %N using suite %N", tls_version_names, this->tls->get_version_max(this->tls), tls_cipher_suite_names, this->suite); } + + if (this->tls->get_version_max(this->tls) >= TLS_1_3) + { + diffie_hellman_group_t group; + tls_named_group_t curve; + enumerator_t *enumerator; + chunk_t shared_secret = chunk_empty; + + enumerator = this->crypto->create_ec_enumerator(this->crypto); + while (enumerator->enumerate(enumerator, &group, &curve)) + { + if (curve == key_type && peer_supports_curve(this, curve)) + { + this->dh = lib->crypto->create_dh(lib->crypto, group); + break; + } + } + enumerator->destroy(enumerator); + if (!this->dh) + { /* simply fail since HelloRetryRequest is not currently implemented */ + DBG1(DBG_TLS, "key_type is not within supported group"); + this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); + return NEED_MORE; + } + + if (key_share.len && + key_type != TLS_CURVE25519 && + key_type != TLS_CURVE448) + { /* classic format (see RFC 8446, section 4.2.8.2) */ + if (key_share.ptr[0] != TLS_ANSI_UNCOMPRESSED) + { + DBG1(DBG_TLS, "DH point format '%N' not supported", + tls_ansi_point_format_names, key_share.ptr[0]); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + key_share = chunk_skip(key_share, 1); + } + if (!key_share.len || + !this->dh->set_other_public_value(this->dh, key_share)) + { + DBG1(DBG_TLS, "DH key derivation failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); + chunk_clear(&shared_secret); + return NEED_MORE; + } + chunk_clear(&shared_secret); + this->requested_curve = key_type; + } + this->state = STATE_HELLO_RECEIVED; return NEED_MORE; } @@ -596,29 +767,57 @@ static status_t process_cert_verify(private_tls_server_t *this, static status_t process_finished(private_tls_server_t *this, bio_reader_t *reader) { - chunk_t received; - char buf[12]; + chunk_t received, verify_data; + u_char buf[12]; - if (!reader->read_data(reader, sizeof(buf), &received)) + if (this->tls->get_version_max(this->tls) < TLS_1_3) { - DBG1(DBG_TLS, "received client finished too short"); - this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); - return NEED_MORE; + if (!reader->read_data(reader, sizeof(buf), &received)) + { + DBG1(DBG_TLS, "received client finished too short"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + if (!this->crypto->calculate_finished_legacy(this->crypto, + "client finished", buf)) + { + DBG1(DBG_TLS, "calculating client finished failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + verify_data = chunk_from_thing(buf); } - if (!this->crypto->calculate_finished_legacy(this->crypto, - "client finished", buf)) + else { - DBG1(DBG_TLS, "calculating client finished failed"); - this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); - return NEED_MORE; + received = reader->peek(reader); + if (!this->crypto->calculate_finished(this->crypto, FALSE, &verify_data)) + { + DBG1(DBG_TLS, "calculating client finished failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + + if (!this->crypto->derive_app_keys(this->crypto)) + { + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + this->crypto->change_cipher(this->crypto, TRUE); + this->crypto->change_cipher(this->crypto, FALSE); } - if (!chunk_equals_const(received, chunk_from_thing(buf))) + + if (!chunk_equals_const(received, verify_data)) { DBG1(DBG_TLS, "received client finished invalid"); this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR); return NEED_MORE; } + if (verify_data.ptr != buf) + { + chunk_free(&verify_data); + } + this->crypto->append_handshake(this->crypto, TLS_FINISHED, received); this->state = STATE_FINISHED_RECEIVED; return NEED_MORE; @@ -629,56 +828,86 @@ METHOD(tls_handshake_t, process, status_t, { tls_handshake_type_t expected; - switch (this->state) + if (this->tls->get_version_max(this->tls) < TLS_1_3) { - case STATE_INIT: - if (type == TLS_CLIENT_HELLO) - { - return process_client_hello(this, reader); - } - expected = TLS_CLIENT_HELLO; - break; - case STATE_HELLO_DONE: - if (type == TLS_CERTIFICATE) - { - return process_certificate(this, reader); - } - if (this->peer) - { - expected = TLS_CERTIFICATE; + switch (this->state) + { + case STATE_INIT: + if (type == TLS_CLIENT_HELLO) + { + return process_client_hello(this, reader); + } + expected = TLS_CLIENT_HELLO; break; - } - /* otherwise fall through to next state */ - case STATE_CERT_RECEIVED: - if (type == TLS_CLIENT_KEY_EXCHANGE) - { - return process_key_exchange(this, reader); - } - expected = TLS_CLIENT_KEY_EXCHANGE; - break; - case STATE_KEY_EXCHANGE_RECEIVED: - if (type == TLS_CERTIFICATE_VERIFY) - { - return process_cert_verify(this, reader); - } - if (this->peer) - { - expected = TLS_CERTIFICATE_VERIFY; + case STATE_HELLO_DONE: + if (type == TLS_CERTIFICATE) + { + return process_certificate(this, reader); + } + if (this->peer) + { + expected = TLS_CERTIFICATE; + break; + } + /* otherwise fall through to next state */ + case STATE_CERT_RECEIVED: + if (type == TLS_CLIENT_KEY_EXCHANGE) + { + return process_key_exchange(this, reader); + } + expected = TLS_CLIENT_KEY_EXCHANGE; break; - } - return INVALID_STATE; - case STATE_CIPHERSPEC_CHANGED_IN: - if (type == TLS_FINISHED) - { - return process_finished(this, reader); - } - expected = TLS_FINISHED; - break; - default: - DBG1(DBG_TLS, "TLS %N not expected in current state", - tls_handshake_type_names, type); - this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE); - return NEED_MORE; + case STATE_KEY_EXCHANGE_RECEIVED: + if (type == TLS_CERTIFICATE_VERIFY) + { + return process_cert_verify(this, reader); + } + if (this->peer) + { + expected = TLS_CERTIFICATE_VERIFY; + break; + } + return INVALID_STATE; + case STATE_CIPHERSPEC_CHANGED_IN: + if (type == TLS_FINISHED) + { + return process_finished(this, reader); + } + expected = TLS_FINISHED; + break; + default: + DBG1(DBG_TLS, "TLS %N not expected in current state", + tls_handshake_type_names, type); + this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE); + return NEED_MORE; + } + } + else + { + switch (this->state) + { + case STATE_INIT: + if (type == TLS_CLIENT_HELLO) + { + return process_client_hello(this, reader); + } + expected = TLS_CLIENT_HELLO; + break; + case STATE_CIPHERSPEC_CHANGED_IN: + case STATE_FINISHED_SENT: + if (type == TLS_FINISHED) + { + return process_finished(this, reader); + } + return NEED_MORE; + case STATE_FINISHED_RECEIVED: + return INVALID_STATE; + default: + DBG1(DBG_TLS, "TLS %N not expected in current state", + tls_handshake_type_names, type); + this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE); + return NEED_MORE; + } } DBG1(DBG_TLS, "TLS %N expected, but received %N", tls_handshake_type_names, expected, tls_handshake_type_names, type); @@ -692,8 +921,12 @@ METHOD(tls_handshake_t, process, status_t, static status_t send_server_hello(private_tls_server_t *this, tls_handshake_type_t *type, bio_writer_t *writer) { - /* TLS version */ - writer->write_uint16(writer, this->tls->get_version_max(this->tls)); + bio_writer_t *extensions, *key_share; + tls_version_t version = this->tls->get_version_max(this->tls); + chunk_t pub; + + /* cap legacy version at TLS 1.2 for middlebox compatibility */ + writer->write_uint16(writer, min(TLS_1_2, version)); writer->write_data(writer, chunk_from_thing(this->server_random)); /* session identifier if we have one */ @@ -705,12 +938,88 @@ static status_t send_server_hello(private_tls_server_t *this, /* NULL compression only */ writer->write_uint8(writer, 0); + if (version >= TLS_1_3) + { + extensions = bio_writer_create(32); + + DBG2(DBG_TLS, "sending extension: %N", + tls_extension_names, TLS_EXT_SUPPORTED_VERSIONS); + extensions->write_uint16(extensions, TLS_EXT_SUPPORTED_VERSIONS); + extensions->write_uint16(extensions, 2); + extensions->write_uint16(extensions, version); + + if (this->dh) + { + tls_named_group_t selected_curve = this->requested_curve; + + DBG2(DBG_TLS, "sending extension: %N", + tls_extension_names, TLS_EXT_KEY_SHARE); + if (!this->dh->get_my_public_value(this->dh, &pub)) + { + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + extensions->destroy(extensions); + return NEED_MORE; + } + extensions->write_uint16(extensions, TLS_EXT_KEY_SHARE); + key_share = bio_writer_create(pub.len + 6); + key_share->write_uint16(key_share, selected_curve); + if (selected_curve == TLS_CURVE25519 || + selected_curve == TLS_CURVE448) + { + key_share->write_data16(key_share, pub); + } + else + { /* classic format (see RFC 8446, section 4.2.8.2) */ + key_share->write_uint16(key_share, pub.len + 1); + key_share->write_uint8(key_share, TLS_ANSI_UNCOMPRESSED); + key_share->write_data(key_share, pub); + } + extensions->write_data16(extensions, key_share->get_buf(key_share)); + key_share->destroy(key_share); + free(pub.ptr); + } + + writer->write_data16(writer, extensions->get_buf(extensions)); + extensions->destroy(extensions); + } + *type = TLS_SERVER_HELLO; this->state = STATE_HELLO_SENT; this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); return NEED_MORE; } +/** + * Send encrypted extensions message + */ +static status_t send_encrypted_extensions(private_tls_server_t *this, + tls_handshake_type_t *type, + bio_writer_t *writer) +{ + chunk_t shared_secret = chunk_empty; + + if (!this->dh->get_shared_secret(this->dh, &shared_secret) || + !this->crypto->derive_handshake_keys(this->crypto, shared_secret)) + { + DBG1(DBG_TLS, "DH key derivation failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); + chunk_clear(&shared_secret); + return NEED_MORE; + } + chunk_clear(&shared_secret); + + this->crypto->change_cipher(this->crypto, TRUE); + this->crypto->change_cipher(this->crypto, FALSE); + + /* currently no extensions are supported */ + writer->write_uint16(writer, 0); + + *type = TLS_ENCRYPTED_EXTENSIONS; + this->state = STATE_ENCRYPTED_EXTENSIONS_SENT; + this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); + return NEED_MORE; +} + /** * Send Certificate */ @@ -723,6 +1032,12 @@ static status_t send_certificate(private_tls_server_t *this, bio_writer_t *certs; chunk_t data; + /* certificate request context as described in RFC 8446, section 4.4.2 */ + if (this->tls->get_version_max(this->tls) > TLS_1_2) + { + writer->write_uint8(writer, 0); + } + /* generate certificate payload */ certs = bio_writer_create(256); cert = this->server_auth->get(this->server_auth, AUTH_RULE_SUBJECT_CERT); @@ -735,6 +1050,11 @@ static status_t send_certificate(private_tls_server_t *this, certs->write_data24(certs, data); free(data.ptr); } + /* extensions see RFC 8446, section 4.4.2 */ + if (this->tls->get_version_max(this->tls) > TLS_1_2) + { + certs->write_uint16(certs, 0); + } } enumerator = this->server_auth->create_enumerator(this->server_auth); while (enumerator->enumerate(enumerator, &rule, &cert)) @@ -761,6 +1081,27 @@ static status_t send_certificate(private_tls_server_t *this, return NEED_MORE; } +/** + * Send Certificate Verify + */ +static status_t send_certificate_verify(private_tls_server_t *this, + tls_handshake_type_t *type, + bio_writer_t *writer) +{ + if (!this->crypto->sign_handshake(this->crypto, this->private, writer, + this->hashsig)) + { + DBG1(DBG_TLS, "signature generation failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + + *type = TLS_CERTIFICATE_VERIFY; + this->state = STATE_CERT_VERIFY_SENT; + this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); + return NEED_MORE; +} + /** * Send Certificate Request */ @@ -830,36 +1171,11 @@ static tls_named_group_t ec_group_to_curve(private_tls_server_t *this, return 0; } -/** - * Check if the peer supports a given TLS curve - */ -bool peer_supports_curve(private_tls_server_t *this, tls_named_group_t curve) -{ - bio_reader_t *reader; - uint16_t current; - - if (!this->curves_received) - { /* none received, assume yes */ - return TRUE; - } - reader = bio_reader_create(this->curves); - while (reader->remaining(reader) && reader->read_uint16(reader, ¤t)) - { - if (current == curve) - { - reader->destroy(reader); - return TRUE; - } - } - reader->destroy(reader); - return FALSE; -} - /** * Try to find a curve supported by both, client and server */ static bool find_supported_curve(private_tls_server_t *this, - tls_named_group_t *curve) + tls_named_group_t *curve) { tls_named_group_t current; enumerator_t *enumerator; @@ -977,17 +1293,34 @@ static status_t send_hello_done(private_tls_server_t *this, static status_t send_finished(private_tls_server_t *this, tls_handshake_type_t *type, bio_writer_t *writer) { - char buf[12]; - - if (!this->crypto->calculate_finished_legacy(this->crypto, - "server finished", buf)) + if (this->tls->get_version_max(this->tls) < TLS_1_3) { - DBG1(DBG_TLS, "calculating server finished data failed"); - this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); - return FAILED; - } + char buf[12]; - writer->write_data(writer, chunk_from_thing(buf)); + if (!this->crypto->calculate_finished_legacy(this->crypto, + "server finished", buf)) + { + DBG1(DBG_TLS, "calculating server finished data failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return FAILED; + } + + writer->write_data(writer, chunk_from_thing(buf)); + } + else + { + chunk_t verify_data; + + if (!this->crypto->calculate_finished(this->crypto, TRUE, &verify_data)) + { + DBG1(DBG_TLS, "calculating server finished data failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + + writer->write_data(writer, verify_data); + chunk_free(&verify_data); + } *type = TLS_FINISHED; this->state = STATE_FINISHED_SENT; @@ -1001,62 +1334,104 @@ METHOD(tls_handshake_t, build, status_t, { diffie_hellman_group_t group; - switch (this->state) + if (this->tls->get_version_max(this->tls) < TLS_1_3) { - case STATE_HELLO_RECEIVED: - return send_server_hello(this, type, writer); - case STATE_HELLO_SENT: - return send_certificate(this, type, writer); - case STATE_CERT_SENT: - group = this->crypto->get_dh_group(this->crypto); - if (group) - { - return send_server_key_exchange(this, type, writer, group); - } - /* otherwise fall through to next state */ - case STATE_KEY_EXCHANGE_SENT: - return send_certificate_request(this, type, writer); - case STATE_CERTREQ_SENT: - return send_hello_done(this, type, writer); - case STATE_CIPHERSPEC_CHANGED_OUT: - return send_finished(this, type, writer); - case STATE_FINISHED_SENT: - return INVALID_STATE; - default: - return INVALID_STATE; + switch (this->state) + { + case STATE_HELLO_RECEIVED: + return send_server_hello(this, type, writer); + case STATE_HELLO_SENT: + return send_certificate(this, type, writer); + case STATE_CERT_SENT: + group = this->crypto->get_dh_group(this->crypto); + if (group) + { + return send_server_key_exchange(this, type, writer, group); + } + /* otherwise fall through to next state */ + case STATE_KEY_EXCHANGE_SENT: + return send_certificate_request(this, type, writer); + case STATE_CERTREQ_SENT: + return send_hello_done(this, type, writer); + case STATE_CIPHERSPEC_CHANGED_OUT: + return send_finished(this, type, writer); + case STATE_FINISHED_SENT: + return INVALID_STATE; + default: + return INVALID_STATE; + } + } + else + { + switch (this->state) + { + case STATE_HELLO_RECEIVED: + return send_server_hello(this, type, writer); + case STATE_HELLO_SENT: + case STATE_CIPHERSPEC_CHANGED_OUT: + return send_encrypted_extensions(this, type, writer); + case STATE_ENCRYPTED_EXTENSIONS_SENT: + return send_certificate(this, type, writer); + case STATE_CERT_SENT: + return send_certificate_verify(this, type, writer); + case STATE_CERT_VERIFY_SENT: + return send_finished(this, type, writer); + case STATE_FINISHED_SENT: + return INVALID_STATE; + default: + return INVALID_STATE; + } } } METHOD(tls_handshake_t, cipherspec_changed, bool, private_tls_server_t *this, bool inbound) { - if (inbound) + if (this->tls->get_version_max(this->tls) < TLS_1_3) { - if (this->resume) + if (inbound) { - return this->state == STATE_FINISHED_SENT; + if (this->resume) + { + return this->state == STATE_FINISHED_SENT; + } + if (this->peer) + { + return this->state == STATE_CERT_VERIFY_RECEIVED; + } + return this->state == STATE_KEY_EXCHANGE_RECEIVED; } - if (this->peer) + else { - return this->state == STATE_CERT_VERIFY_RECEIVED; + if (this->resume) + { + return this->state == STATE_HELLO_SENT; + } + return this->state == STATE_FINISHED_RECEIVED; } - return this->state == STATE_KEY_EXCHANGE_RECEIVED; + return FALSE; } else { - if (this->resume) + if (inbound) + { /* accept ChangeCipherSpec after ServerFinish */ + return this->state == STATE_FINISHED_SENT; + } + else { return this->state == STATE_HELLO_SENT; } - return this->state == STATE_FINISHED_RECEIVED; } - return FALSE; } METHOD(tls_handshake_t, change_cipherspec, void, private_tls_server_t *this, bool inbound) { - this->crypto->change_cipher(this->crypto, inbound); + if (this->tls->get_version_max(this->tls) < TLS_1_3) + { + this->crypto->change_cipher(this->crypto, inbound); + } + if (inbound) { this->state = STATE_CIPHERSPEC_CHANGED_IN; @@ -1070,11 +1445,18 @@ METHOD(tls_handshake_t, change_cipherspec, void, METHOD(tls_handshake_t, finished, bool, private_tls_server_t *this) { - if (this->resume) + if (this->tls->get_version_max(this->tls) < TLS_1_3) + { + if (this->resume) + { + return this->state == STATE_FINISHED_RECEIVED; + } + return this->state == STATE_FINISHED_SENT; + } + else { return this->state == STATE_FINISHED_RECEIVED; } - return this->state == STATE_FINISHED_SENT; } METHOD(tls_handshake_t, get_peer_id, identification_t*, From 5c4cb40e476ea776a94d7bfde825e88ed1c036af Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Mon, 21 Sep 2020 22:19:34 +0200 Subject: [PATCH 058/107] tls-server: Refactor writing of key share extensions Client and server now share the same code to write this extension. --- src/libtls/tls_peer.c | 26 +++++--------- src/libtls/tls_server.c | 78 ++++++++++++++++++++++++----------------- 2 files changed, 53 insertions(+), 51 deletions(-) diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 4e5c2cc03..7d6c1ff7b 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -157,6 +157,10 @@ struct private_tls_peer_t { chunk_t cert_types; }; +/* Implemented in tls_server.c */ +bool tls_write_key_share(bio_writer_t **key_share, tls_named_group_t group, + diffie_hellman_t *dh); + /** * Verify the DH group/key type requested by the server is valid. */ @@ -1202,7 +1206,6 @@ static status_t send_client_hello(private_tls_peer_t *this, enumerator_t *enumerator; int count, i, v; rng_t *rng; - chunk_t pub; htoun32(&this->client_random, time(NULL)); rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); @@ -1352,34 +1355,21 @@ static status_t send_client_hello(private_tls_peer_t *this, extensions->write_data16(extensions, signatures->get_buf(signatures)); signatures->destroy(signatures); - if (this->dh) + if (this->tls->get_version_max(this->tls) >= TLS_1_3 && + this->dh) { DBG2(DBG_TLS, "sending extension: %N", tls_extension_names, TLS_EXT_KEY_SHARE); - if (!this->dh->get_my_public_value(this->dh, &pub)) + extensions->write_uint16(extensions, TLS_EXT_KEY_SHARE); + if (!tls_write_key_share(&key_share, selected_curve, this->dh)) { this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); extensions->destroy(extensions); return NEED_MORE; } - extensions->write_uint16(extensions, TLS_EXT_KEY_SHARE); - key_share = bio_writer_create(pub.len + 6); - key_share->write_uint16(key_share, selected_curve); - if (selected_curve == TLS_CURVE25519 || - selected_curve == TLS_CURVE448) - { - key_share->write_data16(key_share, pub); - } - else - { /* classic format (see RFC 8446, section 4.2.8.2) */ - key_share->write_uint16(key_share, pub.len + 1); - key_share->write_uint8(key_share, TLS_ANSI_UNCOMPRESSED); - key_share->write_data(key_share, pub); - } key_share->wrap16(key_share); extensions->write_data16(extensions, key_share->get_buf(key_share)); key_share->destroy(key_share); - free(pub.ptr); } writer->write_data16(writer, extensions->get_buf(extensions)); diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index c85825208..fb897cf0e 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -340,8 +340,7 @@ static status_t process_client_hello(private_tls_server_t *this, { DBG1(DBG_TLS, "invalid %N extension", tls_extension_names, extension_type); - this->alert->add(this->alert, TLS_FATAL, - TLS_DECODE_ERROR); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); extensions->destroy(extensions); extension->destroy(extension); return NEED_MORE; @@ -915,15 +914,46 @@ METHOD(tls_handshake_t, process, status_t, return NEED_MORE; } +/** + * Write public key into key share extension + */ +bool tls_write_key_share(bio_writer_t **key_share, tls_named_group_t group, + diffie_hellman_t *dh) +{ + bio_writer_t *writer; + chunk_t pub; + + if (!dh || !dh->get_my_public_value(dh, &pub)) + { + return FALSE; + } + *key_share = writer = bio_writer_create(pub.len + 7); + writer->write_uint16(writer, group); + if (group == TLS_CURVE25519 || + group == TLS_CURVE448) + { + writer->write_data16(writer, pub); + } + else + { /* classic format (see RFC 8446, section 4.2.8.2) */ + writer->write_uint16(writer, pub.len + 1); + writer->write_uint8(writer, TLS_ANSI_UNCOMPRESSED); + writer->write_data(writer, pub); + } + free(pub.ptr); + return TRUE; +} + /** * Send ServerHello message */ static status_t send_server_hello(private_tls_server_t *this, tls_handshake_type_t *type, bio_writer_t *writer) { - bio_writer_t *extensions, *key_share; - tls_version_t version = this->tls->get_version_max(this->tls); - chunk_t pub; + bio_writer_t *key_share, *extensions; + tls_version_t version; + + version = this->tls->get_version_max(this->tls); /* cap legacy version at TLS 1.2 for middlebox compatibility */ writer->write_uint16(writer, min(TLS_1_2, version)); @@ -948,36 +978,18 @@ static status_t send_server_hello(private_tls_server_t *this, extensions->write_uint16(extensions, 2); extensions->write_uint16(extensions, version); - if (this->dh) - { - tls_named_group_t selected_curve = this->requested_curve; + DBG2(DBG_TLS, "sending extension: %N", + tls_extension_names, TLS_EXT_KEY_SHARE); + extensions->write_uint16(extensions, TLS_EXT_KEY_SHARE); - DBG2(DBG_TLS, "sending extension: %N", - tls_extension_names, TLS_EXT_KEY_SHARE); - if (!this->dh->get_my_public_value(this->dh, &pub)) - { - this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); - extensions->destroy(extensions); - return NEED_MORE; - } - extensions->write_uint16(extensions, TLS_EXT_KEY_SHARE); - key_share = bio_writer_create(pub.len + 6); - key_share->write_uint16(key_share, selected_curve); - if (selected_curve == TLS_CURVE25519 || - selected_curve == TLS_CURVE448) - { - key_share->write_data16(key_share, pub); - } - else - { /* classic format (see RFC 8446, section 4.2.8.2) */ - key_share->write_uint16(key_share, pub.len + 1); - key_share->write_uint8(key_share, TLS_ANSI_UNCOMPRESSED); - key_share->write_data(key_share, pub); - } - extensions->write_data16(extensions, key_share->get_buf(key_share)); - key_share->destroy(key_share); - free(pub.ptr); + if (!tls_write_key_share(&key_share, this->requested_curve, this->dh)) + { + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + extensions->destroy(extensions); + return NEED_MORE; } + extensions->write_data16(extensions, key_share->get_buf(key_share)); + key_share->destroy(key_share); writer->write_data16(writer, extensions->get_buf(extensions)); extensions->destroy(extensions); From ab70f68cf99c40a2c695a534d42c47c4d1730b5b Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Sun, 27 Sep 2020 16:43:08 +0200 Subject: [PATCH 059/107] tls-server: Support KeyUpdate requests and answers --- src/libtls/tls_server.c | 62 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index fb897cf0e..48344a177 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -48,6 +48,8 @@ typedef enum { /* new states in TLS 1.3 */ STATE_ENCRYPTED_EXTENSIONS_SENT, STATE_CERT_VERIFY_SENT, + STATE_KEY_UPDATE_REQUESTED, + STATE_KEY_UPDATE_SENT, } server_state_t; /** @@ -822,6 +824,37 @@ static status_t process_finished(private_tls_server_t *this, return NEED_MORE; } +/** + * Process KeyUpdate message + */ +static status_t process_key_update(private_tls_server_t *this, + bio_reader_t *reader) +{ + uint8_t update_requested; + + if (!reader->read_uint8(reader, &update_requested) || + update_requested > 1) + { + DBG1(DBG_TLS, "received invalid KeyUpdate"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + + if (!this->crypto->update_app_keys(this->crypto, TRUE)) + { + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + this->crypto->change_cipher(this->crypto, TRUE); + + if (update_requested) + { + DBG1(DBG_TLS, "client requested KeyUpdate"); + this->state = STATE_KEY_UPDATE_REQUESTED; + } + return NEED_MORE; +} + METHOD(tls_handshake_t, process, status_t, private_tls_server_t *this, tls_handshake_type_t type, bio_reader_t *reader) { @@ -900,6 +933,10 @@ METHOD(tls_handshake_t, process, status_t, } return NEED_MORE; case STATE_FINISHED_RECEIVED: + if (type == TLS_KEY_UPDATE) + { + return process_key_update(this, reader); + } return INVALID_STATE; default: DBG1(DBG_TLS, "TLS %N not expected in current state", @@ -1341,6 +1378,21 @@ static status_t send_finished(private_tls_server_t *this, return NEED_MORE; } +/** + * Send KeyUpdate message + */ +static status_t send_key_update(private_tls_server_t *this, + tls_handshake_type_t *type, bio_writer_t *writer) +{ + *type = TLS_KEY_UPDATE; + + /* we currently only send this as reply, so we never request an update */ + writer->write_uint8(writer, 0); + + this->state = STATE_KEY_UPDATE_SENT; + return NEED_MORE; +} + METHOD(tls_handshake_t, build, status_t, private_tls_server_t *this, tls_handshake_type_t *type, bio_writer_t *writer) { @@ -1390,6 +1442,16 @@ METHOD(tls_handshake_t, build, status_t, return send_finished(this, type, writer); case STATE_FINISHED_SENT: return INVALID_STATE; + case STATE_KEY_UPDATE_REQUESTED: + return send_key_update(this, type, writer); + case STATE_KEY_UPDATE_SENT: + if (!this->crypto->update_app_keys(this->crypto, FALSE)) + { + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + this->crypto->change_cipher(this->crypto, FALSE); + this->state = STATE_FINISHED_RECEIVED; default: return INVALID_STATE; } From 066ac8809c3c38ab5b0a175183ec83497c4d2802 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Wed, 30 Sep 2020 14:57:14 +0200 Subject: [PATCH 060/107] tls-crypto: Generalizing DH group to TLS group mapping This simplifies writing the key share extension as the TLS group does not have to be cached. --- src/libtls/tls_crypto.c | 17 ++++++++++++++++ src/libtls/tls_crypto.h | 8 ++++++++ src/libtls/tls_peer.c | 11 ++++------- src/libtls/tls_server.c | 44 ++++++++++++----------------------------- 4 files changed, 42 insertions(+), 38 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 52140f672..16aed5c8c 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -2319,3 +2319,20 @@ int tls_crypto_get_supported_suites(bool null, tls_version_t version, } return count; } + +/** + * See header. + */ +tls_named_group_t tls_ec_group_to_curve(diffie_hellman_group_t group) +{ + int i; + + for (i = 0; i < countof(curves); i++) + { + if (curves[i].group == group) + { + return curves[i].curve; + } + } + return 0; +} diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h index 354329b8a..784f66167 100644 --- a/src/libtls/tls_crypto.h +++ b/src/libtls/tls_crypto.h @@ -669,4 +669,12 @@ tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache); int tls_crypto_get_supported_suites(bool null, tls_version_t version, tls_cipher_suite_t **suites); +/** + * Get the TLS curve of a given EC DH group + * + * @param group diffie hellman group indicator + * @return TLS group indicator + */ +tls_named_group_t tls_ec_group_to_curve(diffie_hellman_group_t group); + #endif /** TLS_CRYPTO_H_ @}*/ diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 7d6c1ff7b..4da8914f4 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -158,8 +158,7 @@ struct private_tls_peer_t { }; /* Implemented in tls_server.c */ -bool tls_write_key_share(bio_writer_t **key_share, tls_named_group_t group, - diffie_hellman_t *dh); +bool tls_write_key_share(bio_writer_t **key_share, diffie_hellman_t *dh); /** * Verify the DH group/key type requested by the server is valid. @@ -1202,7 +1201,7 @@ static status_t send_client_hello(private_tls_peer_t *this, bio_writer_t *extensions, *curves = NULL, *versions, *key_share, *signatures; tls_version_t version_max, version_min; diffie_hellman_group_t group; - tls_named_group_t curve, selected_curve = 0; + tls_named_group_t curve; enumerator_t *enumerator; int count, i, v; rng_t *rng; @@ -1296,7 +1295,6 @@ static status_t send_client_hello(private_tls_peer_t *this, { continue; } - selected_curve = curve; } curves->write_uint16(curves, curve); } @@ -1355,13 +1353,12 @@ static status_t send_client_hello(private_tls_peer_t *this, extensions->write_data16(extensions, signatures->get_buf(signatures)); signatures->destroy(signatures); - if (this->tls->get_version_max(this->tls) >= TLS_1_3 && - this->dh) + if (this->tls->get_version_max(this->tls) >= TLS_1_3) { DBG2(DBG_TLS, "sending extension: %N", tls_extension_names, TLS_EXT_KEY_SHARE); extensions->write_uint16(extensions, TLS_EXT_KEY_SHARE); - if (!tls_write_key_share(&key_share, selected_curve, this->dh)) + if (!tls_write_key_share(&key_share, this->dh)) { this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); extensions->destroy(extensions); diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 48344a177..e6ef207b2 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -954,20 +954,25 @@ METHOD(tls_handshake_t, process, status_t, /** * Write public key into key share extension */ -bool tls_write_key_share(bio_writer_t **key_share, tls_named_group_t group, - diffie_hellman_t *dh) +bool tls_write_key_share(bio_writer_t **key_share, diffie_hellman_t *dh) { bio_writer_t *writer; + tls_named_group_t curve; chunk_t pub; - if (!dh || !dh->get_my_public_value(dh, &pub)) + if (!dh) + { + return FALSE; + } + curve = tls_ec_group_to_curve(dh->get_dh_group(dh)); + if (!curve || !dh->get_my_public_value(dh, &pub)) { return FALSE; } *key_share = writer = bio_writer_create(pub.len + 7); - writer->write_uint16(writer, group); - if (group == TLS_CURVE25519 || - group == TLS_CURVE448) + writer->write_uint16(writer, curve); + if (curve == TLS_CURVE25519 || + curve == TLS_CURVE448) { writer->write_data16(writer, pub); } @@ -1019,7 +1024,7 @@ static status_t send_server_hello(private_tls_server_t *this, tls_extension_names, TLS_EXT_KEY_SHARE); extensions->write_uint16(extensions, TLS_EXT_KEY_SHARE); - if (!tls_write_key_share(&key_share, this->requested_curve, this->dh)) + if (!tls_write_key_share(&key_share, this->dh)) { this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); extensions->destroy(extensions); @@ -1197,29 +1202,6 @@ static status_t send_certificate_request(private_tls_server_t *this, return NEED_MORE; } -/** - * Get the TLS curve of a given EC DH group - */ -static tls_named_group_t ec_group_to_curve(private_tls_server_t *this, - diffie_hellman_group_t group) -{ - diffie_hellman_group_t current; - tls_named_group_t curve; - enumerator_t *enumerator; - - enumerator = this->crypto->create_ec_enumerator(this->crypto); - while (enumerator->enumerate(enumerator, ¤t, &curve)) - { - if (current == group) - { - enumerator->destroy(enumerator); - return curve; - } - } - enumerator->destroy(enumerator); - return 0; -} - /** * Try to find a curve supported by both, client and server */ @@ -1256,7 +1238,7 @@ static status_t send_server_key_exchange(private_tls_server_t *this, if (diffie_hellman_group_is_ec(group)) { - curve = ec_group_to_curve(this, group); + curve = tls_ec_group_to_curve(group); if (!curve || (!peer_supports_curve(this, curve) && !find_supported_curve(this, &curve))) { From 9514aa2dccddf7551b547e6db4964f0e04716e2a Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 27 Jan 2021 14:35:37 +0100 Subject: [PATCH 061/107] diffie-hellman: Add enum names that match proposal keywords --- src/libstrongswan/crypto/diffie_hellman.c | 39 +++++++++++++++++++ src/libstrongswan/crypto/diffie_hellman.h | 5 +++ .../tests/suites/test_proposal.c | 30 ++++++++++++++ 3 files changed, 74 insertions(+) diff --git a/src/libstrongswan/crypto/diffie_hellman.c b/src/libstrongswan/crypto/diffie_hellman.c index efcfdbc74..173882bce 100644 --- a/src/libstrongswan/crypto/diffie_hellman.c +++ b/src/libstrongswan/crypto/diffie_hellman.c @@ -57,6 +57,45 @@ ENUM_NEXT(diffie_hellman_group_names, MODP_CUSTOM, MODP_CUSTOM, NH_128_BIT, "MODP_CUSTOM"); ENUM_END(diffie_hellman_group_names, MODP_CUSTOM); +ENUM_BEGIN(diffie_hellman_group_names_short, MODP_NONE, MODP_1024_BIT, + "modpnone", + "modp768", + "modp1024"); +ENUM_NEXT(diffie_hellman_group_names_short, MODP_1536_BIT, MODP_1536_BIT, MODP_1024_BIT, + "modp1536"); +ENUM_NEXT(diffie_hellman_group_names_short, MODP_2048_BIT, ECP_521_BIT, MODP_1536_BIT, + "modp2048", + "modp3072", + "modp4096", + "modp6144", + "modp8192", + "ecp256", + "ecp384", + "ecp521"); +ENUM_NEXT(diffie_hellman_group_names_short, MODP_1024_160, CURVE_448, ECP_521_BIT, + "modp1024s160", + "modp2048s224", + "modp2048s256", + "ecp192", + "ecp224", + "ecp224bp", + "ecp256bp", + "ecp384bp", + "ecp512bp", + "curve25519", + "curve448"); +ENUM_NEXT(diffie_hellman_group_names_short, MODP_NULL, MODP_NULL, CURVE_448, + "modpnull"); +ENUM_NEXT(diffie_hellman_group_names_short, NTRU_112_BIT, NTRU_256_BIT, MODP_NULL, + "ntru112", + "ntru128", + "ntru192", + "ntru256"); +ENUM_NEXT(diffie_hellman_group_names_short, NH_128_BIT, NH_128_BIT, NTRU_256_BIT, + "newhope128"); +ENUM_NEXT(diffie_hellman_group_names_short, MODP_CUSTOM, MODP_CUSTOM, NH_128_BIT, + "modpcustom"); +ENUM_END(diffie_hellman_group_names_short, MODP_CUSTOM); /** * List of known diffie hellman group parameters. diff --git a/src/libstrongswan/crypto/diffie_hellman.h b/src/libstrongswan/crypto/diffie_hellman.h index ce01bb346..f9459e77a 100644 --- a/src/libstrongswan/crypto/diffie_hellman.h +++ b/src/libstrongswan/crypto/diffie_hellman.h @@ -82,6 +82,11 @@ enum diffie_hellman_group_t { */ extern enum_name_t *diffie_hellman_group_names; +/** + * enum names for diffie_hellman_group_t (matching proposal keywords). + */ +extern enum_name_t *diffie_hellman_group_names_short; + /** * Implementation of the Diffie-Hellman algorithm, as in RFC2631. */ diff --git a/src/libstrongswan/tests/suites/test_proposal.c b/src/libstrongswan/tests/suites/test_proposal.c index c323119ea..783b2ec96 100644 --- a/src/libstrongswan/tests/suites/test_proposal.c +++ b/src/libstrongswan/tests/suites/test_proposal.c @@ -17,6 +17,32 @@ #include +START_TEST(test_dh_group_mapping) +{ + enum_name_t *e = diffie_hellman_group_names_short; + diffie_hellman_group_t group; + const proposal_token_t *token; + char *name; + + do + { + for (group = e->first; group <= e->last; group++) + { + if (group == MODP_CUSTOM) + { /* can't be configured */ + continue; + } + name = e->names[group - e->first]; + token = lib->proposal->get_token(lib->proposal, name); + ck_assert_msg(token, "%s can't be mapped", name); + ck_assert_int_eq(token->type, DIFFIE_HELLMAN_GROUP); + ck_assert_int_eq(token->algorithm, group); + } + } + while ((e = e->next)); +} +END_TEST + static struct { protocol_id_t proto; char *proposal; @@ -456,6 +482,10 @@ Suite *proposal_suite_create() s = suite_create("proposal"); + tc = tcase_create("proposal keywords"); + tcase_add_test(tc, test_dh_group_mapping); + suite_add_tcase(s, tc); + tc = tcase_create("create_from_string"); tcase_add_loop_test(tc, test_create_from_string, 0, countof(create_data)); suite_add_tcase(s, tc); From e53bee9dbea3c0d64253914da1d4b36165dce595 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Sat, 26 Sep 2020 22:19:16 +0200 Subject: [PATCH 062/107] tls-crypto: Add support to configure DH groups to use And add new test cases to test TLS 1.3 connections for each supported DH group. --- src/libtls/tests/suites/test_socket.c | 49 +++++++++++ src/libtls/tls_crypto.c | 120 ++++++++++++++++++++++---- src/libtls/tls_crypto.h | 8 ++ 3 files changed, 160 insertions(+), 17 deletions(-) diff --git a/src/libtls/tests/suites/test_socket.c b/src/libtls/tests/suites/test_socket.c index 5682dad88..c183df5ec 100644 --- a/src/libtls/tests/suites/test_socket.c +++ b/src/libtls/tests/suites/test_socket.c @@ -444,6 +444,49 @@ static void test_tls(tls_version_t version, uint16_t port, bool cauth, u_int i) free(config); } +/** + * TLS curve test wrapper function + */ +static void test_tls_curves(tls_version_t version, uint16_t port, bool cauth, + u_int i) +{ + echo_server_config_t *config; + diffie_hellman_group_t *groups; + char curve[128]; + int count; + + INIT(config, + .version = version, + .addr = "127.0.0.1", + .port = port, + .cauth = cauth, + .data = chunk_from_chars(0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08), + ); + + start_echo_server(config); + + count = tls_crypto_get_supported_groups(&groups); + ck_assert(i < count); + snprintf(curve, sizeof(curve), "%N", diffie_hellman_group_names_short, + groups[i]); + lib->settings->set_str(lib->settings, "%s.tls.curve", curve, lib->ns); + + run_echo_client(config); + + free(groups); + + shutdown(config->fd, SHUT_RDWR); + close(config->fd); + + free(config); +} + +START_TEST(test_tls13_curves) +{ + test_tls_curves(TLS_1_3, 5668, FALSE, _i); +} +END_TEST + START_TEST(test_tls13) { test_tls(TLS_1_3, 5669, FALSE, _i); @@ -503,6 +546,12 @@ Suite *socket_suite_create() s = suite_create("socket"); + tc = tcase_create("TLS 1.3/curves"); + tcase_add_checked_fixture(tc, setup_creds, teardown_creds); + tcase_add_loop_test(tc, test_tls13_curves, 0, + tls_crypto_get_supported_groups(NULL)); + suite_add_tcase(s, tc); + tc = tcase_create("TLS 1.3/anon"); tcase_add_checked_fixture(tc, setup_creds, teardown_creds); add_tls_test(test_tls13, TLS_1_3); diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 16aed5c8c..7c43a570c 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1091,6 +1091,35 @@ static void filter_specific_config_suites(private_tls_crypto_t *this, } } +/** + * Filter key exchange curves by curve user config + */ +static bool filter_curve_config(tls_named_group_t curve) +{ + enumerator_t *enumerator; + char *token, *config; + + config = lib->settings->get_str(lib->settings, "%s.tls.curve", NULL, lib->ns); + if (config) + { + enumerator = enumerator_create_token(config, ",", " "); + while (enumerator->enumerate(enumerator, &token)) + { + const proposal_token_t *tok; + + tok = lib->proposal->get_token(lib->proposal, token); + if (tok != NULL && tok->type == DIFFIE_HELLMAN_GROUP && + curve == tls_ec_group_to_curve(tok->algorithm)) + { + enumerator->destroy(enumerator); + return TRUE; + } + } + enumerator->destroy(enumerator); + } + return !config; +} + /** * Filter out unsupported suites on given suite array */ @@ -1524,29 +1553,52 @@ static struct { CALLBACK(group_filter, bool, void *null, enumerator_t *orig, va_list args) { - diffie_hellman_group_t group, *out; - tls_named_group_t *curve; + diffie_hellman_group_t group, *group_out; + tls_named_group_t curve, *curve_out; char *plugin; - int i; - VA_ARGS_VGET(args, out, curve); + VA_ARGS_VGET(args, group_out, curve_out); while (orig->enumerate(orig, &group, &plugin)) { - for (i = 0; i < countof(curves); i++) + curve = tls_ec_group_to_curve(group); + if (curve) { - if (curves[i].group == group) + if (group_out) { - if (out) - { - *out = curves[i].group; - } - if (curve) - { - *curve = curves[i].curve; - } - return TRUE; + *group_out = group; } + if (curve_out) + { + *curve_out = curve; + } + return TRUE; + } + } + return FALSE; +} + +CALLBACK(config_filter, bool, + void *null, enumerator_t *orig, va_list args) +{ + diffie_hellman_group_t group, *group_out; + tls_named_group_t curve, *curve_out; + + VA_ARGS_VGET(args, group_out, curve_out); + + while (orig->enumerate(orig, &group, &curve)) + { + if (filter_curve_config(curve)) + { + if (group_out) + { + *group_out = group; + } + if (curve_out) + { + *curve_out = curve; + } + return TRUE; } } return FALSE; @@ -1556,8 +1608,10 @@ METHOD(tls_crypto_t, create_ec_enumerator, enumerator_t*, private_tls_crypto_t *this) { return enumerator_create_filter( - lib->crypto->create_dh_enumerator(lib->crypto), - group_filter, NULL, NULL); + enumerator_create_filter( + lib->crypto->create_dh_enumerator(lib->crypto), + group_filter, NULL, NULL), + config_filter, NULL, NULL); } METHOD(tls_crypto_t, set_protection, void, @@ -2320,6 +2374,38 @@ int tls_crypto_get_supported_suites(bool null, tls_version_t version, return count; } +/** + * See header. + */ +int tls_crypto_get_supported_groups(diffie_hellman_group_t **out) +{ + enumerator_t *enumerator; + diffie_hellman_group_t groups[countof(curves)]; + diffie_hellman_group_t group; + tls_named_group_t curve; + int count = 0, i; + + enumerator = enumerator_create_filter( + lib->crypto->create_dh_enumerator(lib->crypto), + group_filter, NULL, NULL); + + while (enumerator->enumerate(enumerator, &group, &curve)) + { + groups[count++] = group; + } + enumerator->destroy(enumerator); + + if (out) + { + *out = calloc(count, sizeof(diffie_hellman_group_t)); + for (i = 0; i < count; i++) + { + (*out)[i] = groups[i]; + } + } + return count; +} + /** * See header. */ diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h index 784f66167..3d230e919 100644 --- a/src/libtls/tls_crypto.h +++ b/src/libtls/tls_crypto.h @@ -669,6 +669,14 @@ tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache); int tls_crypto_get_supported_suites(bool null, tls_version_t version, tls_cipher_suite_t **suites); +/** + * Get a list of all supported TLS DH groups. + * + * @param groups pointer to allocated DH group array, to free(), or NULL + * @return number of curves supported + */ +int tls_crypto_get_supported_groups(diffie_hellman_group_t **groups); + /** * Get the TLS curve of a given EC DH group * From c3e71324682a14ffb25c54566bc09d610562ce9b Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Sat, 26 Sep 2020 13:17:43 +0200 Subject: [PATCH 063/107] tls-server: Support HelloRetryRequest (HRR) Adds support to request and handle retries with a different DH group. Only the first key share extension sent by the client is currently considered, so this might result in protocol errors if the server requests a group for which the client already sent a key share. --- src/libtls/tls_server.c | 131 ++++++++++++++++++++++++++++++---------- 1 file changed, 98 insertions(+), 33 deletions(-) diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index e6ef207b2..fa90091a1 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -263,6 +263,14 @@ static bool peer_supports_curve(private_tls_server_t *this, return FALSE; } +/** + * Check if client is currently retrying to connect to the server. + */ +static bool retrying(private_tls_server_t *this) +{ + return this->state == STATE_INIT && this->requested_curve; +} + /** * Process client hello message */ @@ -316,9 +324,11 @@ static status_t process_client_hello(private_tls_server_t *this, switch (extension_type) { case TLS_EXT_SIGNATURE_ALGORITHMS: + chunk_free(&this->hashsig); this->hashsig = chunk_clone(extension_data); break; case TLS_EXT_SUPPORTED_GROUPS: + chunk_free(&this->curves); this->curves_received = TRUE; this->curves = chunk_clone(extension_data); break; @@ -457,50 +467,82 @@ static status_t process_client_hello(private_tls_server_t *this, if (this->tls->get_version_max(this->tls) >= TLS_1_3) { diffie_hellman_group_t group; - tls_named_group_t curve; + tls_named_group_t curve, requesting_curve = 0; enumerator_t *enumerator; chunk_t shared_secret = chunk_empty; enumerator = this->crypto->create_ec_enumerator(this->crypto); while (enumerator->enumerate(enumerator, &group, &curve)) { + if (!requesting_curve && + curve != key_type && + peer_supports_curve(this, curve)) + { + requesting_curve = curve; + } if (curve == key_type && peer_supports_curve(this, curve)) { + DBG1(DBG_TLS, "using key exchange %N", + tls_named_group_names, curve); this->dh = lib->crypto->create_dh(lib->crypto, group); break; } } enumerator->destroy(enumerator); - if (!this->dh) - { /* simply fail since HelloRetryRequest is not currently implemented */ - DBG1(DBG_TLS, "key_type is not within supported group"); - this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); - return NEED_MORE; - } - if (key_share.len && - key_type != TLS_CURVE25519 && - key_type != TLS_CURVE448) - { /* classic format (see RFC 8446, section 4.2.8.2) */ - if (key_share.ptr[0] != TLS_ANSI_UNCOMPRESSED) + if (!this->dh) + { + if (retrying(this)) { - DBG1(DBG_TLS, "DH point format '%N' not supported", - tls_ansi_point_format_names, key_share.ptr[0]); + DBG1(DBG_TLS, "already replied with a hello retry request"); + this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE); + return NEED_MORE; + } + + /* TODO: process all client offered key shares, currently only the + * first key share extensions is processed other offered key shares + * are ignored. */ + if (!requesting_curve) + { + DBG1(DBG_TLS, "no mutual supported group in client hello"); + this->alert->add(this->alert, TLS_FATAL, TLS_ILLEGAL_PARAMETER); + return NEED_MORE; + } + this->requested_curve = requesting_curve; + + if (!this->crypto->hash_handshake(this->crypto, NULL)) + { + DBG1(DBG_TLS, "failed to hash handshake messages"); this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); return NEED_MORE; } - key_share = chunk_skip(key_share, 1); } - if (!key_share.len || - !this->dh->set_other_public_value(this->dh, key_share)) + else { - DBG1(DBG_TLS, "DH key derivation failed"); - this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); + if (key_share.len && + key_type != TLS_CURVE25519 && + key_type != TLS_CURVE448) + { /* classic format (see RFC 8446, section 4.2.8.2) */ + if (key_share.ptr[0] != TLS_ANSI_UNCOMPRESSED) + { + DBG1(DBG_TLS, "DH point format '%N' not supported", + tls_ansi_point_format_names, key_share.ptr[0]); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + key_share = chunk_skip(key_share, 1); + } + if (!key_share.len || + !this->dh->set_other_public_value(this->dh, key_share)) + { + DBG1(DBG_TLS, "DH key derivation failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); + chunk_clear(&shared_secret); + return NEED_MORE; + } chunk_clear(&shared_secret); - return NEED_MORE; + this->requested_curve = 0; } - chunk_clear(&shared_secret); - this->requested_curve = key_type; } this->state = STATE_HELLO_RECEIVED; @@ -999,7 +1041,15 @@ static status_t send_server_hello(private_tls_server_t *this, /* cap legacy version at TLS 1.2 for middlebox compatibility */ writer->write_uint16(writer, min(TLS_1_2, version)); - writer->write_data(writer, chunk_from_thing(this->server_random)); + + if (this->requested_curve) + { + writer->write_data(writer, tls_hello_retry_request_magic); + } + else + { + writer->write_data(writer, chunk_from_thing(this->server_random)); + } /* session identifier if we have one */ writer->write_data8(writer, this->session); @@ -1023,22 +1073,31 @@ static status_t send_server_hello(private_tls_server_t *this, DBG2(DBG_TLS, "sending extension: %N", tls_extension_names, TLS_EXT_KEY_SHARE); extensions->write_uint16(extensions, TLS_EXT_KEY_SHARE); - - if (!tls_write_key_share(&key_share, this->dh)) + if (this->requested_curve) { - this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); - extensions->destroy(extensions); - return NEED_MORE; + DBG1(DBG_TLS, "requesting key exchange with %N", + tls_named_group_names, this->requested_curve); + extensions->write_uint16(extensions, 2); + extensions->write_uint16(extensions, this->requested_curve); + } + else + { + if (!tls_write_key_share(&key_share, this->dh)) + { + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + extensions->destroy(extensions); + return NEED_MORE; + } + extensions->write_data16(extensions, key_share->get_buf(key_share)); + key_share->destroy(key_share); } - extensions->write_data16(extensions, key_share->get_buf(key_share)); - key_share->destroy(key_share); writer->write_data16(writer, extensions->get_buf(extensions)); extensions->destroy(extensions); } *type = TLS_SERVER_HELLO; - this->state = STATE_HELLO_SENT; + this->state = this->requested_curve ? STATE_INIT : STATE_HELLO_SENT; this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); return NEED_MORE; } @@ -1470,8 +1529,8 @@ METHOD(tls_handshake_t, cipherspec_changed, bool, else { if (inbound) - { /* accept ChangeCipherSpec after ServerFinish */ - return this->state == STATE_FINISHED_SENT; + { /* accept ChangeCipherSpec after ServerFinish or HelloRetryRequest */ + return this->state == STATE_FINISHED_SENT || retrying(this); } else { @@ -1488,6 +1547,12 @@ METHOD(tls_handshake_t, change_cipherspec, void, this->crypto->change_cipher(this->crypto, inbound); } + if (retrying(this)) + { /* client might send a ChangeCipherSpec after a HelloRetryRequest and + * before a new ClientHello which should not cause any state changes */ + return; + } + if (inbound) { this->state = STATE_CIPHERSPEC_CHANGED_IN; From 64e2506f5b24435021db00fae9e6c1b604d4bb84 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Tue, 29 Sep 2020 14:15:19 +0200 Subject: [PATCH 064/107] libtls: Add TLS version negotiation test cases These client to server negotiation test cases are implemented: - TLS 1.0 => TLS 1.3 - TLS 1.1 => TLS 1.3 - TLS 1.2 => TLS 1.3 - TLS 1.3 => TLS 1.3 - TLS 1.3 => TLS 1.0 - TLS 1.3 => TLS 1.1 - TLS 1.3 => TLS 1.2 - TLS 1.3 => TLS 1.3 - TLS 1.0 => TLS 1.2 - TLS 1.1 => TLS 1.2 - TLS 1.2 => TLS 1.2 - TLS 1.3 => TLS 1.2 --- src/libtls/tests/suites/test_socket.c | 118 +++++++++++++++++++++++--- 1 file changed, 104 insertions(+), 14 deletions(-) diff --git a/src/libtls/tests/suites/test_socket.c b/src/libtls/tests/suites/test_socket.c index c183df5ec..9be77de26 100644 --- a/src/libtls/tests/suites/test_socket.c +++ b/src/libtls/tests/suites/test_socket.c @@ -409,6 +409,24 @@ static void run_echo_client(echo_server_config_t *config) server->destroy(server); } +/** + * Create server/peer configuration + */ +static echo_server_config_t *create_config(tls_version_t version, uint16_t port, + bool cauth) +{ + echo_server_config_t *config; + + INIT(config, + .version = version, + .addr = "127.0.0.1", + .port = port, + .cauth = cauth, + .data = chunk_from_chars(0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08), + ); + return config; +} + /** * Common test wrapper function for different test variants */ @@ -419,13 +437,7 @@ static void test_tls(tls_version_t version, uint16_t port, bool cauth, u_int i) char suite[128]; int count; - INIT(config, - .version = version, - .addr = "127.0.0.1", - .port = port, - .cauth = cauth, - .data = chunk_from_chars(0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08), - ); + config = create_config(version, port, cauth); start_echo_server(config); @@ -455,13 +467,7 @@ static void test_tls_curves(tls_version_t version, uint16_t port, bool cauth, char curve[128]; int count; - INIT(config, - .version = version, - .addr = "127.0.0.1", - .port = port, - .cauth = cauth, - .data = chunk_from_chars(0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08), - ); + config = create_config(version, port, cauth); start_echo_server(config); @@ -481,6 +487,72 @@ static void test_tls_curves(tls_version_t version, uint16_t port, bool cauth, free(config); } +/** + * TLS server version test wrapper function + */ +static void test_tls_server(tls_version_t version, uint16_t port, bool cauth, + u_int i) +{ + echo_server_config_t *client, *server; + + client = create_config(i, port, cauth); + server = create_config(version, port, cauth); + + start_echo_server(server); + + run_echo_client(client); + + shutdown(client->fd, SHUT_RDWR); + close(client->fd); + shutdown(server->fd, SHUT_RDWR); + close(server->fd); + + free(client); + free(server); +} + +/** + * TLS client version test wrapper function + */ +static void test_tls_client(tls_version_t version, uint16_t port, bool cauth, + u_int i) +{ + echo_server_config_t *client, *server; + + client = create_config(version, port, cauth); + server = create_config(i, port, cauth); + + start_echo_server(server); + + run_echo_client(client); + + shutdown(client->fd, SHUT_RDWR); + close(client->fd); + shutdown(server->fd, SHUT_RDWR); + close(server->fd); + + free(client); + free(server); +} + +START_TEST(test_tls_12_server) +{ + test_tls_server(TLS_1_2, 5665, FALSE, _i); +} +END_TEST + +START_TEST(test_tls_13_server) +{ + test_tls_server(TLS_1_3, 5666, FALSE, _i); +} +END_TEST + +START_TEST(test_tls_13_client) +{ + test_tls_client(TLS_1_3, 5667, FALSE, _i); +} +END_TEST + START_TEST(test_tls13_curves) { test_tls_curves(TLS_1_3, 5668, FALSE, _i); @@ -544,8 +616,26 @@ Suite *socket_suite_create() tcase_add_loop_test(tc, func, 0, \ tls_crypto_get_supported_suites(TRUE, version, NULL)); +#define add_tls_versions_test(func, from, to) \ + tcase_add_loop_test(tc, func, from, to+1); + s = suite_create("socket"); + tc = tcase_create("TLS [1.0..1.3] client to TLS 1.3 server"); + tcase_add_checked_fixture(tc, setup_creds, teardown_creds); + add_tls_versions_test(test_tls_13_server, TLS_1_0, TLS_1_3); + suite_add_tcase(s, tc); + + tc = tcase_create("TLS 1.3 client to TLS [1.0..1.3] server"); + tcase_add_checked_fixture(tc, setup_creds, teardown_creds); + add_tls_versions_test(test_tls_13_client, TLS_1_0, TLS_1_3); + suite_add_tcase(s, tc); + + tc = tcase_create("TLS [1.0..1.3] client to TLS 1.2 server"); + tcase_add_checked_fixture(tc, setup_creds, teardown_creds); + add_tls_versions_test(test_tls_12_server, TLS_1_0, TLS_1_3); + suite_add_tcase(s, tc); + tc = tcase_create("TLS 1.3/curves"); tcase_add_checked_fixture(tc, setup_creds, teardown_creds); tcase_add_loop_test(tc, test_tls13_curves, 0, From 3767a4a6558d96ebae710ee200b6cda1e82a7ec9 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Fri, 2 Oct 2020 18:11:45 +0200 Subject: [PATCH 065/107] tls-server: Support multiple client key shares A client can send one or multiple key shares from which the server picks one it supports (checked in its preferred order). A retry is requested if none of the key shares are supported. --- src/libtls/tls_server.c | 91 +++++++++++++++++++++++++++++++---------- 1 file changed, 70 insertions(+), 21 deletions(-) diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index fa90091a1..3d114d589 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -22,6 +22,7 @@ #include #include +#include typedef struct private_tls_server_t private_tls_server_t; @@ -263,6 +264,38 @@ static bool peer_supports_curve(private_tls_server_t *this, return FALSE; } +/** + * TLS 1.3 key exchange key share + */ +typedef struct { + uint16_t curve; + chunk_t key_share; +} key_share_t; + +/** + * Check if peer sent a key share of a given TLS named DH group + */ +static bool peer_offered_curve(array_t *key_shares, tls_named_group_t curve, + key_share_t *out) +{ + key_share_t peer; + int i; + + for (i = 0; i < array_count(key_shares); i++) + { + array_get(key_shares, i, &peer); + if (curve == peer.curve) + { + if (out) + { + *out = peer; + } + return TRUE; + } + } + return FALSE; +} + /** * Check if client is currently retrying to connect to the server. */ @@ -277,10 +310,10 @@ static bool retrying(private_tls_server_t *this) static status_t process_client_hello(private_tls_server_t *this, bio_reader_t *reader) { - uint16_t legacy_version = 0, version = 0, key_share_length, key_type = 0; - uint16_t extension_type = 0; + uint16_t legacy_version = 0, version = 0, extension_type = 0; chunk_t random, session, ciphers, versions = chunk_empty, compression; - chunk_t ext = chunk_empty, key_share = chunk_empty; + chunk_t ext = chunk_empty, key_shares = chunk_empty; + key_share_t peer = {0}; chunk_t extension_data = chunk_empty; bio_reader_t *extensions, *extension; tls_cipher_suite_t *suites; @@ -345,10 +378,7 @@ static status_t process_client_hello(private_tls_server_t *this, } break; case TLS_EXT_KEY_SHARE: - if (!extension->read_uint16(extension, &key_share_length) || - !extension->read_uint16(extension, &key_type) || - !extension->read_data16(extension, &key_share) || - !key_share.len) + if (!extension->read_data16(extension, &key_shares)) { DBG1(DBG_TLS, "invalid %N extension", tls_extension_names, extension_type); @@ -470,17 +500,38 @@ static status_t process_client_hello(private_tls_server_t *this, tls_named_group_t curve, requesting_curve = 0; enumerator_t *enumerator; chunk_t shared_secret = chunk_empty; + array_t *peer_key_shares; + + peer_key_shares = array_create(sizeof(key_share_t), 1); + extension = bio_reader_create(key_shares); + while (extension->remaining(extension)) + { + if (!extension->read_uint16(extension, &peer.curve) || + !extension->read_data16(extension, &peer.key_share) || + !peer.key_share.len) + { + DBG1(DBG_TLS, "invalid %N extension", + tls_extension_names, extension_type); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + extension->destroy(extension); + array_destroy(peer_key_shares); + return NEED_MORE; + } + array_insert(peer_key_shares, ARRAY_TAIL, &peer); + } + extension->destroy(extension); enumerator = this->crypto->create_ec_enumerator(this->crypto); while (enumerator->enumerate(enumerator, &group, &curve)) { if (!requesting_curve && - curve != key_type && - peer_supports_curve(this, curve)) + peer_supports_curve(this, curve) && + !peer_offered_curve(peer_key_shares, curve, NULL)) { requesting_curve = curve; } - if (curve == key_type && peer_supports_curve(this, curve)) + if (peer_supports_curve(this, curve) && + peer_offered_curve(peer_key_shares, curve, &peer)) { DBG1(DBG_TLS, "using key exchange %N", tls_named_group_names, curve); @@ -489,6 +540,7 @@ static status_t process_client_hello(private_tls_server_t *this, } } enumerator->destroy(enumerator); + array_destroy(peer_key_shares); if (!this->dh) { @@ -499,9 +551,6 @@ static status_t process_client_hello(private_tls_server_t *this, return NEED_MORE; } - /* TODO: process all client offered key shares, currently only the - * first key share extensions is processed other offered key shares - * are ignored. */ if (!requesting_curve) { DBG1(DBG_TLS, "no mutual supported group in client hello"); @@ -519,21 +568,21 @@ static status_t process_client_hello(private_tls_server_t *this, } else { - if (key_share.len && - key_type != TLS_CURVE25519 && - key_type != TLS_CURVE448) + if (peer.key_share.len && + peer.curve != TLS_CURVE25519 && + peer.curve != TLS_CURVE448) { /* classic format (see RFC 8446, section 4.2.8.2) */ - if (key_share.ptr[0] != TLS_ANSI_UNCOMPRESSED) + if (peer.key_share.ptr[0] != TLS_ANSI_UNCOMPRESSED) { DBG1(DBG_TLS, "DH point format '%N' not supported", - tls_ansi_point_format_names, key_share.ptr[0]); + tls_ansi_point_format_names, peer.key_share.ptr[0]); this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); return NEED_MORE; } - key_share = chunk_skip(key_share, 1); + peer.key_share = chunk_skip(peer.key_share, 1); } - if (!key_share.len || - !this->dh->set_other_public_value(this->dh, key_share)) + if (!peer.key_share.len || + !this->dh->set_other_public_value(this->dh, peer.key_share)) { DBG1(DBG_TLS, "DH key derivation failed"); this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); From 0dfe1590b67f05b2ca8234b61f86838bdbb0ce22 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Fri, 9 Oct 2020 19:14:11 +0200 Subject: [PATCH 066/107] tls-server: Fix invalid signature algorithm and supported groups parsing The extension's content length field was wrongly added to the content data. Fixes: 06109c4717 ("Implemented "signature algorithm" hello extension") --- src/libtls/tls_server.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 3d114d589..07a1ca326 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -357,10 +357,28 @@ static status_t process_client_hello(private_tls_server_t *this, switch (extension_type) { case TLS_EXT_SIGNATURE_ALGORITHMS: + if (!extension->read_data16(extension, &extension_data)) + { + DBG1(DBG_TLS, "invalid %N extension", + tls_extension_names, extension_type); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + extensions->destroy(extensions); + extension->destroy(extension); + return NEED_MORE; + } chunk_free(&this->hashsig); this->hashsig = chunk_clone(extension_data); break; case TLS_EXT_SUPPORTED_GROUPS: + if (!extension->read_data16(extension, &extension_data)) + { + DBG1(DBG_TLS, "invalid %N extension", + tls_extension_names, extension_type); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + extensions->destroy(extensions); + extension->destroy(extension); + return NEED_MORE; + } chunk_free(&this->curves); this->curves_received = TRUE; this->curves = chunk_clone(extension_data); @@ -370,8 +388,7 @@ static status_t process_client_hello(private_tls_server_t *this, { DBG1(DBG_TLS, "invalid %N extension", tls_extension_names, extension_type); - this->alert->add(this->alert, TLS_FATAL, - TLS_DECODE_ERROR); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); extensions->destroy(extensions); extension->destroy(extension); return NEED_MORE; From 91c9e4d576e5c4e6154d1d41eeca5acaf925dc14 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Sat, 10 Oct 2020 17:10:37 +0200 Subject: [PATCH 067/107] tls-crypto: Add missing signature scheme constants Some peers, e.g. Firefox, send a wide range of signature algorithms. To prevent numeric identifiers in the log these algorithms are added here. --- src/libtls/tls_crypto.c | 24 +++++++++++++----------- src/libtls/tls_crypto.h | 6 ++++++ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 7c43a570c..86fafb4dd 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -277,22 +277,24 @@ ENUM_NEXT(tls_signature_scheme_names, TLS_SIG_ECDSA_SHA1, TLS_SIG_ECDSA_SHA1, TLS_SIG_RSA_PKCS1_SHA1, "ECDSA_SHA1"); ENUM_NEXT(tls_signature_scheme_names, - TLS_SIG_RSA_PKCS1_SHA256, TLS_SIG_RSA_PKCS1_SHA256, TLS_SIG_ECDSA_SHA1, - "RSA_PKCS1_SHA256"); + TLS_SIG_RSA_PKCS1_SHA224, TLS_SIG_ECDSA_SHA224, TLS_SIG_ECDSA_SHA1, + "RSA_PKCS1_SHA224", + "DSA_SHA224", + "ECDSA_SHA224"); ENUM_NEXT(tls_signature_scheme_names, - TLS_SIG_ECDSA_SHA256, TLS_SIG_ECDSA_SHA256, TLS_SIG_RSA_PKCS1_SHA256, + TLS_SIG_RSA_PKCS1_SHA256, TLS_SIG_ECDSA_SHA256, TLS_SIG_ECDSA_SHA224, + "RSA_PKCS1_SHA256", + "DSA_SHA256", "ECDSA_SHA256"); ENUM_NEXT(tls_signature_scheme_names, - TLS_SIG_RSA_PKCS1_SHA384, TLS_SIG_RSA_PKCS1_SHA384, TLS_SIG_ECDSA_SHA256, - "RSA_PKCS1_SHA384"); -ENUM_NEXT(tls_signature_scheme_names, - TLS_SIG_ECDSA_SHA384, TLS_SIG_ECDSA_SHA384, TLS_SIG_RSA_PKCS1_SHA384, + TLS_SIG_RSA_PKCS1_SHA384, TLS_SIG_ECDSA_SHA384, TLS_SIG_ECDSA_SHA256, + "RSA_PKCS1_SHA384", + "DSA_SHA384", "ECDSA_SHA384"); ENUM_NEXT(tls_signature_scheme_names, - TLS_SIG_RSA_PKCS1_SHA512, TLS_SIG_RSA_PKCS1_SHA512, TLS_SIG_ECDSA_SHA384, - "RSA_PKCS1_SHA512"); -ENUM_NEXT(tls_signature_scheme_names, - TLS_SIG_ECDSA_SHA512, TLS_SIG_ECDSA_SHA512, TLS_SIG_RSA_PKCS1_SHA512, + TLS_SIG_RSA_PKCS1_SHA512, TLS_SIG_ECDSA_SHA512, TLS_SIG_ECDSA_SHA384, + "RSA_PKCS1_SHA512", + "DSA_SHA512", "ECDSA_SHA512"); ENUM_NEXT(tls_signature_scheme_names, TLS_SIG_RSA_PSS_RSAE_SHA256, TLS_SIG_RSA_PSS_PSS_SHA512, TLS_SIG_ECDSA_SHA512, diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h index 3d230e919..9da07ce75 100644 --- a/src/libtls/tls_crypto.h +++ b/src/libtls/tls_crypto.h @@ -297,11 +297,17 @@ enum tls_signature_scheme_t { * second the key type) */ TLS_SIG_RSA_PKCS1_SHA1 = 0x0201, TLS_SIG_ECDSA_SHA1 = 0x0203, + TLS_SIG_RSA_PKCS1_SHA224 = 0x0301, + TLS_SIG_DSA_SHA224 = 0x0302, + TLS_SIG_ECDSA_SHA224 = 0x0303, TLS_SIG_RSA_PKCS1_SHA256 = 0x0401, + TLS_SIG_DSA_SHA256 = 0x0402, TLS_SIG_ECDSA_SHA256 = 0x0403, TLS_SIG_RSA_PKCS1_SHA384 = 0x0501, + TLS_SIG_DSA_SHA384 = 0x0502, TLS_SIG_ECDSA_SHA384 = 0x0503, TLS_SIG_RSA_PKCS1_SHA512 = 0x0601, + TLS_SIG_DSA_SHA512 = 0x0602, TLS_SIG_ECDSA_SHA512 = 0x0603, /* RSASSA-PSS for public keys with OID rsaEncryption */ TLS_SIG_RSA_PSS_RSAE_SHA256 = 0x0804, From e7313962808c0cf8c2a21386d7e5fe2c7f4bbe7e Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Fri, 16 Oct 2020 09:28:49 +0200 Subject: [PATCH 068/107] tls-server: Check if peer sent hash and signature algorithms It is mandatory to receive the `signature_algorithms` extension when a server is authenticating with a certificate, which is always the case. --- src/libtls/tls_server.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 07a1ca326..4957cba89 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -412,6 +412,14 @@ static status_t process_client_hello(private_tls_server_t *this, } extensions->destroy(extensions); + if (this->tls->get_version_max(this->tls) >= TLS_1_3 && !this->hashsig.len) + { + DBG1(DBG_TLS, "no %N extension received", tls_extension_names, + TLS_MISSING_EXTENSION); + this->alert->add(this->alert, TLS_FATAL, TLS_MISSING_EXTENSION); + return NEED_MORE; + } + memcpy(this->client_random, random.ptr, sizeof(this->client_random)); htoun32(&this->server_random, time(NULL)); From 06112f3fe26413585c832b50e36b7d91d0e96f6e Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Tue, 13 Oct 2020 13:54:38 +0200 Subject: [PATCH 069/107] tls-crypto: Distinguish between signing and verifying signature schemes strongSwan supports RSA_PSS_RSAE schemes for signing but does not differentiate between rsaEncryption and rsassaPss encoding. Thus RSA_PSS_PSS schemes are only used for verifying signatures. --- src/libtls/tls_crypto.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 86fafb4dd..fc74d31f2 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1512,19 +1512,18 @@ METHOD(tls_crypto_t, get_signature_algorithms, void, /** * Get the signature parameters from a TLS signature scheme */ -static signature_params_t *params_for_scheme(tls_signature_scheme_t sig) +static signature_params_t *params_for_scheme(tls_signature_scheme_t sig, + bool sign) { int i; for (i = 0; i < countof(schemes); i++) { /* strongSwan supports only RSA_PSS_RSAE schemes for signing but can - * verify public keys in rsaEncryption as well as rsassaPss encoding. - * Current implementation does not distinguish between signing and - * verifying. */ - if (sig == TLS_SIG_RSA_PSS_PSS_SHA256 || - sig == TLS_SIG_RSA_PSS_PSS_SHA384 || - sig == TLS_SIG_RSA_PSS_PSS_SHA512) + * verify public keys in rsaEncryption as well as rsassaPss encoding. */ + if (sign && (sig == TLS_SIG_RSA_PSS_PSS_SHA256 || + sig == TLS_SIG_RSA_PSS_PSS_SHA384 || + sig == TLS_SIG_RSA_PSS_PSS_SHA512)) { continue; } @@ -1765,7 +1764,7 @@ METHOD(tls_crypto_t, sign, bool, { if (reader->read_uint16(reader, &scheme)) { - params = params_for_scheme(scheme); + params = params_for_scheme(scheme, TRUE); if (params && type == key_type_from_signature_scheme(params->scheme) && key->sign(key, params->scheme, params->params, data, &sig)) @@ -1840,7 +1839,7 @@ METHOD(tls_crypto_t, verify, bool, DBG1(DBG_TLS, "received invalid signature"); return FALSE; } - params = params_for_scheme(scheme); + params = params_for_scheme(scheme, FALSE); if (!params) { DBG1(DBG_TLS, "signature algorithms %N not supported", From 9803fb82f448c7eae03aa4505b0f24a26a8f1948 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Mon, 12 Oct 2020 18:58:53 +0200 Subject: [PATCH 070/107] tls-server: Consider supported signature algorithms when selecting key/certificate This won't work if the client doesn't send a `signature_algorithms` extension. But since the default is SHA1/RSA, most will send it to at least announce stronger hash algorithms if not ECDSA. --- src/libtls/tls_crypto.c | 85 ++++++++++++++++++++++++++++++++ src/libtls/tls_crypto.h | 20 ++++++++ src/libtls/tls_server.c | 106 +++++++++++++++++++++++++++------------- 3 files changed, 177 insertions(+), 34 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index fc74d31f2..cb4ed9c53 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -23,6 +23,7 @@ #include #include +#include ENUM_BEGIN(tls_cipher_suite_names, TLS_NULL_WITH_NULL_NULL, TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, @@ -2423,3 +2424,87 @@ tls_named_group_t tls_ec_group_to_curve(diffie_hellman_group_t group) } return 0; } + +/** + * See header. + */ +key_type_t tls_signature_scheme_to_key_type(tls_signature_scheme_t sig) +{ + int i; + + for (i = 0; i < countof(schemes); i++) + { + if (schemes[i].sig == sig) + { + return key_type_from_signature_scheme(schemes[i].params.scheme); + } + } + return 0; +} + +/** + * Hashtable hash function + */ +static u_int hash_key_type(key_type_t *type) +{ + return chunk_hash(chunk_from_thing(*type)); +} + +/** + * Hashtable equals function + */ +static bool equals_key_type(key_type_t *key1, key_type_t *key2) +{ + return *key1 == *key2; +} + +CALLBACK(filter_key_types, bool, + void *data, enumerator_t *orig, va_list args) +{ + key_type_t *key_type, *out; + + VA_ARGS_VGET(args, out); + + if (orig->enumerate(orig, NULL, &key_type)) + { + *out = *key_type; + return TRUE; + } + return FALSE; +} + +CALLBACK(destroy_key_types, void, + hashtable_t *ht) +{ + ht->destroy_function(ht, (void*)free); +} + +/* + * See header. + */ +enumerator_t *tls_get_supported_key_types(tls_version_t min_version, + tls_version_t max_version) +{ + hashtable_t *ht; + key_type_t *type, lookup; + int i; + + ht = hashtable_create((hashtable_hash_t)hash_key_type, + (hashtable_equals_t)equals_key_type, 4); + for (i = 0; i < countof(schemes); i++) + { + if (schemes[i].min_version <= max_version && + schemes[i].max_version >= min_version) + { + lookup = key_type_from_signature_scheme(schemes[i].params.scheme); + if (!ht->get(ht, &lookup)) + { + type = malloc_thing(key_type_t); + *type = lookup; + ht->put(ht, type, type); + } + } + } + return enumerator_create_filter(ht->create_enumerator(ht), + filter_key_types, ht, destroy_key_types); +} diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h index 9da07ce75..0e9d454f7 100644 --- a/src/libtls/tls_crypto.h +++ b/src/libtls/tls_crypto.h @@ -691,4 +691,24 @@ int tls_crypto_get_supported_groups(diffie_hellman_group_t **groups); */ tls_named_group_t tls_ec_group_to_curve(diffie_hellman_group_t group); +/** + * Get the key type from a TLS signature scheme + * + * @param sig TLS signature algorithm scheme + * @return type of a key + */ +key_type_t tls_signature_scheme_to_key_type(tls_signature_scheme_t sig); + +/** + * Create an enumerator over supported key types within a specific TLS version range + * + * Enumerates over key_type_t + * + * @param min_version minimum negotiated TLS version + * @param max_version maximum negotiated TLS version + * @return hashtable of key types + */ +enumerator_t *tls_get_supported_key_types(tls_version_t min_version, + tls_version_t max_version); + #endif /** TLS_CRYPTO_H_ @}*/ diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 4957cba89..f5e15615b 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -169,71 +169,109 @@ struct private_tls_server_t { bool curves_received; }; +/** + * Create an array of an intersection of server and peer supported key types + */ +static array_t *create_common_key_types(chunk_t hashsig, + tls_version_t version_min, + tls_version_t version_max) +{ + array_t *key_types; + enumerator_t *enumerator; + key_type_t v, lookup; + uint16_t sig_scheme; + + key_types = array_create(sizeof(key_type_t), 8); + enumerator = tls_get_supported_key_types(version_min, version_max); + while (enumerator->enumerate(enumerator, &v)) + { + bio_reader_t *reader; + + reader = bio_reader_create(hashsig); + while (reader->remaining(reader) && + reader->read_uint16(reader, &sig_scheme)) + { + lookup = tls_signature_scheme_to_key_type(sig_scheme); + if (v == lookup) + { + array_insert(key_types, ARRAY_TAIL, &lookup); + break; + } + } + reader->destroy(reader); + } + enumerator->destroy(enumerator); + return key_types; +} + /** * Find a cipher suite and a server key */ static bool select_suite_and_key(private_tls_server_t *this, tls_cipher_suite_t *suites, int count) { + array_t *key_types; + tls_version_t version_min, version_max; private_key_t *key; key_type_t type; - key = lib->credmgr->get_private(lib->credmgr, KEY_ANY, this->server, - this->server_auth); + version_min = this->tls->get_version_min(this->tls); + version_max = this->tls->get_version_max(this->tls); + key_types = create_common_key_types(this->hashsig, version_min, version_max); + if (!array_count(key_types)) + { + DBG1(DBG_TLS, "no common signature algorithms found"); + array_destroy(key_types); + return FALSE; + } + while (array_remove(key_types, ARRAY_HEAD, &type)) + { + key = lib->credmgr->get_private(lib->credmgr, type, this->server, + this->server_auth); + if (key) + { + break; + } + } if (!key) { DBG1(DBG_TLS, "no usable TLS server certificate found for '%Y'", this->server); + array_destroy(key_types); return FALSE; } - if (this->tls->get_version_max(this->tls) >= TLS_1_3) + if (version_max >= TLS_1_3) { - /* currently no support to derive key based on client supported - * signature schemes */ this->suite = this->crypto->select_cipher_suite(this->crypto, suites, count, KEY_ANY); - if (!this->suite) - { - DBG1(DBG_TLS, "received cipher suites unacceptable"); - return FALSE; - } } else { this->suite = this->crypto->select_cipher_suite(this->crypto, suites, - count, - key->get_type(key)); - if (!this->suite) - { /* no match for this key, try to find another type */ - if (key->get_type(key) == KEY_ECDSA) - { - type = KEY_RSA; - } - else - { - type = KEY_ECDSA; - } - key->destroy(key); - - this->suite = this->crypto->select_cipher_suite(this->crypto, suites, - count, type); - if (!this->suite) - { - DBG1(DBG_TLS, "received cipher suites unacceptable"); - return FALSE; - } + count, type); + while (!this->suite && array_remove(key_types, ARRAY_HEAD, &type)) + { /* find a key and cipher suite for one of the remaining key types */ + DESTROY_IF(key); this->server_auth->destroy(this->server_auth); this->server_auth = auth_cfg_create(); key = lib->credmgr->get_private(lib->credmgr, type, this->server, this->server_auth); - if (!key) + if (key) { - DBG1(DBG_TLS, "received cipher suites unacceptable"); - return FALSE; + this->suite = this->crypto->select_cipher_suite(this->crypto, + suites, count, + type); } } } + array_destroy(key_types); + if (!this->suite || !key) + { + DBG1(DBG_TLS, "received cipher suites or signature schemes unacceptable"); + return FALSE; + } + DBG1(DBG_TLS, "using key of type %N", key_type_names, key->get_type(key)); this->private = key; return TRUE; } From 34f66ce6cb43b288f5ef4f5dd62d4246b82b2b1a Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Fri, 23 Oct 2020 16:17:16 +0200 Subject: [PATCH 071/107] tls-crypto: Support EdDSA keys with ECDSA cipher suites Ed25519 and Ed448 are now usable in all TLS versions for connections that use an ECDSA cipher suite, as per RFC 8422. --- src/libtls/tls_crypto.c | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index cb4ed9c53..2d0475274 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1397,7 +1397,9 @@ METHOD(tls_crypto_t, select_cipher_suite, tls_cipher_suite_t, algs = find_suite(this->suites[i]); if (algs) { - if (key == KEY_ANY || key == algs->key) + if (key == KEY_ANY || key == algs->key || + (algs->key == KEY_ECDSA && key == KEY_ED25519) || + (algs->key == KEY_ECDSA && key == KEY_ED448)) { if (create_ciphers(this, algs)) { @@ -1455,9 +1457,9 @@ static struct { { TLS_SIG_ECDSA_SHA512, { .scheme = SIGN_ECDSA_WITH_SHA512_DER }, TLS_1_0, TLS_1_3 }, { TLS_SIG_ED25519, { .scheme = SIGN_ED25519 }, - TLS_1_3, TLS_1_3 }, + TLS_1_0, TLS_1_3 }, { TLS_SIG_ED448, { .scheme = SIGN_ED448 }, - TLS_1_3, TLS_1_3 }, + TLS_1_0, TLS_1_3 }, { TLS_SIG_RSA_PSS_RSAE_SHA256, { .scheme = SIGN_RSA_EMSA_PSS, .params = &pss_params_sha256, }, TLS_1_2, TLS_1_3 }, { TLS_SIG_RSA_PSS_RSAE_SHA384, { .scheme = SIGN_RSA_EMSA_PSS, .params = &pss_params_sha384, }, @@ -1815,6 +1817,20 @@ METHOD(tls_crypto_t, sign, bool, } DBG2(DBG_TLS, "created signature with SHA1/ECDSA"); break; + case KEY_ED25519: + if (!key->sign(key, SIGN_ED25519, NULL, data, &sig)) + { + return FALSE; + } + DBG2(DBG_TLS, "created signature with Ed25519"); + break; + case KEY_ED448: + if (!key->sign(key, SIGN_ED448, NULL, data, &sig)) + { + return FALSE; + } + DBG2(DBG_TLS, "created signature with Ed448"); + break; default: return FALSE; } @@ -1902,6 +1918,20 @@ METHOD(tls_crypto_t, verify, bool, } DBG2(DBG_TLS, "verified signature with SHA1/ECDSA"); break; + case KEY_ED25519: + if (!key->verify(key, SIGN_ED25519, NULL, data, sig)) + { + return FALSE; + } + DBG2(DBG_TLS, "verified signature with Ed25519"); + break; + case KEY_ED448: + if (!key->verify(key, SIGN_ED448, NULL, data, sig)) + { + return FALSE; + } + DBG2(DBG_TLS, "verified signature with Ed448"); + break; default: return FALSE; } @@ -2300,6 +2330,8 @@ tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache) .cache = cache, ); + /* FIXME: EDDSA keys are currently treated like ECDSA keys. A cleaner + * separation would be welcome. */ enumerator = lib->creds->create_builder_enumerator(lib->creds); while (enumerator->enumerate(enumerator, &type, &subtype)) { @@ -2311,6 +2343,8 @@ tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache) this->rsa = TRUE; break; case KEY_ECDSA: + case KEY_ED25519: + case KEY_ED448: this->ecdsa = TRUE; break; default: From 5e579ebe8f6b81786f21251349a1481a85d78ea2 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Fri, 23 Oct 2020 16:01:03 +0200 Subject: [PATCH 072/107] tls-test: Load keys of any type Only RSA keys were possible until now. --- scripts/tls_test.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/tls_test.c b/scripts/tls_test.c index f95494683..321bacb49 100644 --- a/scripts/tls_test.c +++ b/scripts/tls_test.c @@ -1,4 +1,8 @@ /* + * Copyright (C) 2020 Pascal Knecht + * Copyright (C) 2020 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2010 Martin Willi * Copyright (C) 2010 revosec AG * @@ -212,8 +216,8 @@ static bool load_key(char *filename) { private_key_t *key; - key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_RSA, - BUILD_FROM_FILE, filename, BUILD_END); + key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY, + BUILD_FROM_FILE, filename, BUILD_END); if (!key) { DBG1(DBG_TLS, "loading key from '%s' failed", filename); From 0aaf1242d9db78f1f0d051192efe81789a25dd89 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Fri, 23 Oct 2020 22:56:03 +0200 Subject: [PATCH 073/107] libtls: Add unit tests for Ed25519 and Ed448 keys TLS 1.0 to TLS 1.3 socket connection tests with each key type. --- src/libtls/tests/suites/test_socket.c | 148 +++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 4 deletions(-) diff --git a/src/libtls/tests/suites/test_socket.c b/src/libtls/tests/suites/test_socket.c index 9be77de26..de1b51682 100644 --- a/src/libtls/tests/suites/test_socket.c +++ b/src/libtls/tests/suites/test_socket.c @@ -129,6 +129,28 @@ static char ecdsa[] = { 0xb1,0x47,0xc8,0xf6,0x18,0xbb,0x97, }; +/** + * Ed25519 private key + * pki --gen --type ed25519 + */ +static char ed25519[] = { + 0x30,0x2e,0x02,0x01,0x00,0x30,0x05,0x06,0x03,0x2b,0x65,0x70,0x04,0x22,0x04,0x20, + 0x70,0x4b,0xca,0x70,0x1c,0xb1,0x75,0xf1,0xed,0xde,0xf1,0x99,0x8e,0x11,0x32,0x2d, + 0x76,0x41,0x01,0x6c,0xd7,0xbc,0x79,0xa2,0x06,0x15,0x7e,0x44,0x28,0xf2,0x49,0x61, +}; + +/** + * Ed448 private key + * pki --gen --type ed448 + */ +static char ed448[] = { + 0x30,0x47,0x02,0x01,0x00,0x30,0x05,0x06,0x03,0x2b,0x65,0x71,0x04,0x3b,0x04,0x39, + 0xcf,0xb9,0xbe,0x75,0xdf,0x76,0x8b,0x9a,0xdf,0x56,0x52,0x5d,0x37,0x7d,0xb0,0xa8, + 0x1f,0x19,0x44,0x30,0xbd,0x9d,0x0e,0xff,0x5f,0xc8,0xc3,0xec,0x60,0xd5,0xf6,0xa4, + 0x3d,0x1f,0x0e,0xb5,0x65,0x6e,0xe6,0x62,0x4d,0xb7,0xe7,0x5c,0x88,0x7b,0xe8,0xdd, + 0x02,0xd9,0x0f,0xaa,0xe7,0xfb,0x05,0x14,0x41, +}; + /** * TLS certificate for RSA key * pki --self --in rsa.key --dn "C=CH, O=strongSwan, CN=tls-rsa" --san 127.0.0.1 @@ -220,7 +242,68 @@ static char ecdsa_crt[] = { 0xac,0x36,0x08,0x14,0x29, }; -START_SETUP(setup_creds) +/** + * TLS certificate for Ed25519 key + * pki --self --in ed25519.key --dn "C=CH, O=strongSwan, CN=tls-ed25519" \ + * --san 127.0.0.1 + */ +static char ed25519_crt[] = { + 0x30,0x82,0x01,0x38,0x30,0x81,0xeb,0xa0,0x03,0x02,0x01,0x02,0x02,0x08,0x46,0x41, + 0x12,0x0d,0xa4,0xbd,0x00,0x11,0x30,0x05,0x06,0x03,0x2b,0x65,0x70,0x30,0x38,0x31, + 0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x43,0x48,0x31,0x13,0x30,0x11, + 0x06,0x03,0x55,0x04,0x0a,0x13,0x0a,0x73,0x74,0x72,0x6f,0x6e,0x67,0x53,0x77,0x61, + 0x6e,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x03,0x13,0x0b,0x74,0x6c,0x73,0x2d, + 0x65,0x64,0x32,0x35,0x35,0x31,0x39,0x30,0x1e,0x17,0x0d,0x32,0x30,0x31,0x30,0x32, + 0x32,0x31,0x34,0x30,0x31,0x30,0x30,0x5a,0x17,0x0d,0x32,0x33,0x31,0x30,0x32,0x32, + 0x31,0x34,0x30,0x31,0x30,0x30,0x5a,0x30,0x38,0x31,0x0b,0x30,0x09,0x06,0x03,0x55, + 0x04,0x06,0x13,0x02,0x43,0x48,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0a,0x13, + 0x0a,0x73,0x74,0x72,0x6f,0x6e,0x67,0x53,0x77,0x61,0x6e,0x31,0x14,0x30,0x12,0x06, + 0x03,0x55,0x04,0x03,0x13,0x0b,0x74,0x6c,0x73,0x2d,0x65,0x64,0x32,0x35,0x35,0x31, + 0x39,0x30,0x2a,0x30,0x05,0x06,0x03,0x2b,0x65,0x70,0x03,0x21,0x00,0xbd,0xbd,0xd2, + 0x9d,0x90,0x2c,0x4f,0xb0,0x0d,0x88,0xe9,0x92,0xba,0x59,0x91,0x6c,0x0a,0x30,0xc3, + 0x8e,0x7d,0x0d,0x55,0x67,0xf7,0xb0,0x37,0x39,0xfa,0x05,0x61,0xc3,0xa3,0x13,0x30, + 0x11,0x30,0x0f,0x06,0x03,0x55,0x1d,0x11,0x04,0x08,0x30,0x06,0x87,0x04,0x7f,0x00, + 0x00,0x01,0x30,0x05,0x06,0x03,0x2b,0x65,0x70,0x03,0x41,0x00,0x04,0xc6,0x12,0x57, + 0xfa,0x69,0x74,0xd2,0x3a,0x7d,0x1b,0x23,0xde,0x64,0x08,0xa8,0x05,0x75,0xd3,0x15, + 0xfb,0xd4,0x46,0xe0,0x4a,0x59,0x48,0x8e,0xee,0x4e,0x4d,0x72,0xbf,0xbc,0xdb,0x36, + 0xda,0x39,0x23,0x9a,0x06,0x88,0xee,0x63,0xe5,0xb4,0x23,0xf9,0xa9,0x80,0x41,0x99, + 0x3d,0x3f,0xb5,0x39,0x72,0x4b,0x62,0x86,0x4e,0x85,0x61,0x0b, +}; + +/** + * TLS certificate for Ed448 key + * pki --self --in ed448.key --dn "C=CH, O=strongSwan, CN=tls-ed448" \ + * --san 127.0.0.1 + */ +static char ed448_crt[] = { + 0x30,0x82,0x01,0x80,0x30,0x82,0x01,0x00,0xa0,0x03,0x02,0x01,0x02,0x02,0x08,0x71, + 0xa9,0xa0,0xdd,0x5b,0xee,0xa0,0x5c,0x30,0x05,0x06,0x03,0x2b,0x65,0x71,0x30,0x36, + 0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x43,0x48,0x31,0x13,0x30, + 0x11,0x06,0x03,0x55,0x04,0x0a,0x13,0x0a,0x73,0x74,0x72,0x6f,0x6e,0x67,0x53,0x77, + 0x61,0x6e,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x03,0x13,0x09,0x74,0x6c,0x73, + 0x2d,0x65,0x64,0x34,0x34,0x38,0x30,0x1e,0x17,0x0d,0x32,0x30,0x31,0x30,0x32,0x33, + 0x32,0x30,0x34,0x34,0x30,0x35,0x5a,0x17,0x0d,0x32,0x33,0x31,0x30,0x32,0x33,0x32, + 0x30,0x34,0x34,0x30,0x35,0x5a,0x30,0x36,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04, + 0x06,0x13,0x02,0x43,0x48,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0a,0x13,0x0a, + 0x73,0x74,0x72,0x6f,0x6e,0x67,0x53,0x77,0x61,0x6e,0x31,0x12,0x30,0x10,0x06,0x03, + 0x55,0x04,0x03,0x13,0x09,0x74,0x6c,0x73,0x2d,0x65,0x64,0x34,0x34,0x38,0x30,0x43, + 0x30,0x05,0x06,0x03,0x2b,0x65,0x71,0x03,0x3a,0x00,0xdd,0x5e,0x19,0xc8,0x67,0xa9, + 0x93,0x53,0x5f,0x26,0xca,0x6f,0x2f,0xdf,0x9e,0x0f,0x48,0xb6,0x60,0x3e,0x56,0xa5, + 0xaf,0xe8,0xf9,0x3f,0xe5,0x1c,0xeb,0xf2,0xf4,0x84,0xd1,0x48,0xa9,0xb1,0x92,0x6b, + 0xa1,0x4d,0x47,0x86,0x8e,0xf9,0xcc,0xd2,0x58,0xd4,0x6f,0x8c,0x76,0x59,0xf4,0x77, + 0x59,0xc8,0x00,0xa3,0x13,0x30,0x11,0x30,0x0f,0x06,0x03,0x55,0x1d,0x11,0x04,0x08, + 0x30,0x06,0x87,0x04,0x7f,0x00,0x00,0x01,0x30,0x05,0x06,0x03,0x2b,0x65,0x71,0x03, + 0x73,0x00,0xc6,0xf0,0x4e,0xf2,0x41,0xe0,0xb4,0xa0,0x0a,0x9f,0x73,0x67,0xcb,0x89, + 0x97,0xf0,0x3a,0xfe,0x53,0xb2,0x1b,0x6c,0x37,0x24,0xbe,0x9e,0x2b,0x50,0x0c,0x98, + 0xb3,0x15,0x65,0x79,0x37,0xd6,0xc2,0x92,0x89,0x96,0xf3,0x5f,0x2d,0x70,0xa5,0x49, + 0xdd,0x7d,0x12,0x3d,0x17,0x28,0xd2,0x56,0x25,0xab,0x80,0x89,0x4d,0x6a,0xfa,0x32, + 0x6a,0x16,0x7f,0xd2,0x12,0xb3,0x73,0xf5,0xe7,0x1f,0x89,0x0b,0x5e,0x05,0xbe,0x69, + 0xb8,0x11,0x59,0xf9,0xdc,0x9c,0xca,0x68,0xe3,0x1f,0x7a,0x43,0x1a,0x72,0xfa,0x1f, + 0x07,0xe0,0x58,0xc2,0x3a,0x18,0xb3,0x01,0x68,0x90,0x40,0x05,0x7d,0x35,0x1a,0x3f, + 0xdc,0xc8,0x00,0x00 +}; + +static void setup_credentials(chunk_t key_data, chunk_t cert_data) { private_key_t *key; certificate_t *cert; @@ -233,8 +316,8 @@ START_SETUP(setup_creds) { creds->add_key(creds, key); } - key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ECDSA, - BUILD_BLOB, chunk_from_thing(ecdsa), BUILD_END); + key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY, + BUILD_BLOB, key_data, BUILD_END); if (key) { creds->add_key(creds, key); @@ -246,7 +329,7 @@ START_SETUP(setup_creds) creds->add_cert(creds, TRUE, cert); } cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, - BUILD_BLOB, chunk_from_thing(ecdsa_crt), BUILD_END); + BUILD_BLOB, cert_data, BUILD_END); if (cert) { creds->add_cert(creds, TRUE, cert); @@ -254,6 +337,23 @@ START_SETUP(setup_creds) lib->credmgr->add_set(lib->credmgr, &creds->set); } + +START_SETUP(setup_creds) +{ + setup_credentials(chunk_from_thing(ecdsa), chunk_from_thing(ecdsa_crt)); +} +END_SETUP + +START_SETUP(setup_ed25519_creds) +{ + setup_credentials(chunk_from_thing(ed25519), chunk_from_thing(ed25519_crt)); +} +END_SETUP + +START_SETUP(setup_ed448_creds) +{ + setup_credentials(chunk_from_thing(ed448), chunk_from_thing(ed448_crt)); +} END_SETUP START_TEARDOWN(teardown_creds) @@ -682,5 +782,45 @@ Suite *socket_suite_create() add_tls_test(test_tls10_mutual, TLS_1_0); suite_add_tcase(s, tc); + tc = tcase_create("TLS 1.3/ed25519"); + tcase_add_checked_fixture(tc, setup_ed25519_creds, teardown_creds); + add_tls_test(test_tls13, TLS_1_3); + suite_add_tcase(s, tc); + + tc = tcase_create("TLS 1.2/ed25519"); + tcase_add_checked_fixture(tc, setup_ed25519_creds, teardown_creds); + add_tls_test(test_tls12, TLS_1_2); + suite_add_tcase(s, tc); + + tc = tcase_create("TLS 1.1/ed25519"); + tcase_add_checked_fixture(tc, setup_ed25519_creds, teardown_creds); + add_tls_test(test_tls11, TLS_1_1); + suite_add_tcase(s, tc); + + tc = tcase_create("TLS 1.0/ed25519"); + tcase_add_checked_fixture(tc, setup_ed25519_creds, teardown_creds); + add_tls_test(test_tls10, TLS_1_0); + suite_add_tcase(s, tc); + + tc = tcase_create("TLS 1.3/ed448"); + tcase_add_checked_fixture(tc, setup_ed448_creds, teardown_creds); + add_tls_test(test_tls13, TLS_1_3); + suite_add_tcase(s, tc); + + tc = tcase_create("TLS 1.2/ed448"); + tcase_add_checked_fixture(tc, setup_ed448_creds, teardown_creds); + add_tls_test(test_tls12, TLS_1_2); + suite_add_tcase(s, tc); + + tc = tcase_create("TLS 1.1/ed448"); + tcase_add_checked_fixture(tc, setup_ed448_creds, teardown_creds); + add_tls_test(test_tls11, TLS_1_1); + suite_add_tcase(s, tc); + + tc = tcase_create("TLS 1.0/ed448"); + tcase_add_checked_fixture(tc, setup_ed448_creds, teardown_creds); + add_tls_test(test_tls10, TLS_1_0); + suite_add_tcase(s, tc); + return s; } From 534a7816467ca17adc41cf88402c69c674f9f752 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Sun, 8 Nov 2020 15:34:39 +0100 Subject: [PATCH 074/107] tls-hkdf: Always use correct base key to derive finished message The cached traffic secrets change once the application traffic secrets are derived, but we must always use the correct base key to derive the finished message, which are the handshake traffic secrets (RFC 8446, section 4.4). --- src/libtls/tls_hkdf.c | 61 +++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/src/libtls/tls_hkdf.c b/src/libtls/tls_hkdf.c index b03efa0bd..002d481f1 100644 --- a/src/libtls/tls_hkdf.c +++ b/src/libtls/tls_hkdf.c @@ -22,6 +22,11 @@ typedef struct private_tls_hkdf_t private_tls_hkdf_t; +typedef struct cached_secrets_t { + chunk_t client; + chunk_t server; +} cached_secrets_t; + typedef enum hkdf_phase { HKDF_PHASE_0, HKDF_PHASE_1, @@ -67,11 +72,14 @@ struct private_tls_hkdf_t { chunk_t prk; /** - * Current implementation needs a copy of derived secrets to calculate the - * proper finished key. + * Handshake traffic secrets. */ - chunk_t client_traffic_secret; - chunk_t server_traffic_secret; + cached_secrets_t handshake_traffic_secrets; + + /** + * Current traffic secrets. + */ + cached_secrets_t traffic_secrets; }; static char *hkdf_labels[] = { @@ -418,11 +426,11 @@ METHOD(tls_hkdf_t, generate_secret, bool, if (label == TLS_HKDF_UPD_C_TRAFFIC || label == TLS_HKDF_UPD_S_TRAFFIC) { - chunk_t previous = this->client_traffic_secret; + chunk_t previous = this->traffic_secrets.client; if (label == TLS_HKDF_UPD_S_TRAFFIC) { - previous = this->server_traffic_secret; + previous = this->traffic_secrets.server; } if (!expand_label(this, previous, chunk_from_str("traffic upd"), @@ -446,16 +454,22 @@ METHOD(tls_hkdf_t, generate_secret, bool, switch (label) { case TLS_HKDF_C_HS_TRAFFIC: + chunk_clear(&this->handshake_traffic_secrets.client); + this->handshake_traffic_secrets.client = chunk_clone(okm); + /* fall-through */ case TLS_HKDF_C_AP_TRAFFIC: case TLS_HKDF_UPD_C_TRAFFIC: - chunk_clear(&this->client_traffic_secret); - this->client_traffic_secret = chunk_clone(okm); + chunk_clear(&this->traffic_secrets.client); + this->traffic_secrets.client = chunk_clone(okm); break; case TLS_HKDF_S_HS_TRAFFIC: + chunk_clear(&this->handshake_traffic_secrets.server); + this->handshake_traffic_secrets.server = chunk_clone(okm); + /* fall-through */ case TLS_HKDF_S_AP_TRAFFIC: case TLS_HKDF_UPD_S_TRAFFIC: - chunk_clear(&this->server_traffic_secret); - this->server_traffic_secret = chunk_clone(okm); + chunk_clear(&this->traffic_secrets.server); + this->traffic_secrets.server = chunk_clone(okm); break; default: break; @@ -476,12 +490,12 @@ METHOD(tls_hkdf_t, generate_secret, bool, * Derive keys/IVs from the current traffic secrets. */ static bool get_shared_label_keys(private_tls_hkdf_t *this, chunk_t label, + cached_secrets_t *secrets, bool server, size_t length, chunk_t *key) { chunk_t result = chunk_empty, secret; - secret = server ? this->server_traffic_secret - : this->client_traffic_secret; + secret = server ? secrets->server : secrets->client; if (!expand_label(this, secret, label, chunk_empty, length, &result)) { @@ -504,22 +518,22 @@ static bool get_shared_label_keys(private_tls_hkdf_t *this, chunk_t label, METHOD(tls_hkdf_t, derive_key, bool, private_tls_hkdf_t *this, bool is_server, size_t length, chunk_t *key) { - return get_shared_label_keys(this, chunk_from_str("key"), is_server, - length, key); + return get_shared_label_keys(this, chunk_from_str("key"), + &this->traffic_secrets, is_server, length, key); } METHOD(tls_hkdf_t, derive_iv, bool, private_tls_hkdf_t *this, bool is_server, size_t length, chunk_t *iv) { - return get_shared_label_keys(this, chunk_from_str("iv"), is_server, - length, iv); + return get_shared_label_keys(this, chunk_from_str("iv"), + &this->traffic_secrets, is_server, length, iv); } METHOD(tls_hkdf_t, derive_finished, bool, private_tls_hkdf_t *this, bool server, chunk_t *finished) { return get_shared_label_keys(this, chunk_from_str("finished"), - server, + &this->handshake_traffic_secrets, server, this->hasher->get_hash_size(this->hasher), finished); } @@ -580,14 +594,23 @@ METHOD(tls_hkdf_t, allocate_bytes, bool, this->prf->allocate_bytes(this->prf, seed, out); } +/** + * Clean up secrets + */ +static void destroy_secrets(cached_secrets_t *secrets) +{ + chunk_clear(&secrets->client); + chunk_clear(&secrets->server); +} + METHOD(tls_hkdf_t, destroy, void, private_tls_hkdf_t *this) { chunk_clear(&this->psk); chunk_clear(&this->prk); chunk_clear(&this->shared_secret); - chunk_clear(&this->client_traffic_secret); - chunk_clear(&this->server_traffic_secret); + destroy_secrets(&this->handshake_traffic_secrets); + destroy_secrets(&this->traffic_secrets); DESTROY_IF(this->prf); DESTROY_IF(this->hasher); free(this); From 6b23543abd857faef64d5e7131091381b5614c43 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Wed, 28 Oct 2020 21:54:09 +0100 Subject: [PATCH 075/107] tls-crypto: Move AEAD ownership to the protection layer This separates key derivation from key switching. --- src/libtls/tls_crypto.c | 20 +++++++++++++++++++- src/libtls/tls_protection.c | 4 ++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 2d0475274..78d9a5003 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -2162,8 +2162,17 @@ static bool derive_labeled_keys(private_tls_crypto_t *this, tls_hkdf_label_t client_label, tls_hkdf_label_t server_label) { - tls_aead_t *aead_c = this->aead_out, *aead_s = this->aead_in; + tls_aead_t *aead_c, *aead_s; + suite_algs_t *algs; + algs = find_suite(this->suite); + destroy_aeads(this); + if (!create_aead(this, algs)) + { + return FALSE; + } + aead_c = this->aead_out; + aead_s = this->aead_in; if (this->tls->is_server(this->tls)) { aead_c = this->aead_in; @@ -2209,8 +2218,15 @@ METHOD(tls_crypto_t, derive_app_keys, bool, METHOD(tls_crypto_t, update_app_keys, bool, private_tls_crypto_t *this, bool inbound) { + suite_algs_t *algs; tls_hkdf_label_t label = TLS_HKDF_UPD_C_TRAFFIC; + algs = find_suite(this->suite); + destroy_aeads(this); + if (!create_aead(this, algs)) + { + return FALSE; + } if (this->tls->is_server(this->tls) != inbound) { label = TLS_HKDF_UPD_S_TRAFFIC; @@ -2264,10 +2280,12 @@ METHOD(tls_crypto_t, change_cipher, void, if (inbound) { this->protection->set_cipher(this->protection, TRUE, this->aead_in); + this->aead_in = NULL; } else { this->protection->set_cipher(this->protection, FALSE, this->aead_out); + this->aead_out = NULL; } } } diff --git a/src/libtls/tls_protection.c b/src/libtls/tls_protection.c index 3d0ec489b..2dcca4e2d 100644 --- a/src/libtls/tls_protection.c +++ b/src/libtls/tls_protection.c @@ -123,11 +123,13 @@ METHOD(tls_protection_t, set_cipher, void, { if (inbound) { + DESTROY_IF(this->aead_in); this->aead_in = aead; this->seq_in = 0; } else { + DESTROY_IF(this->aead_out); this->aead_out = aead; this->seq_out = 0; } @@ -142,6 +144,8 @@ METHOD(tls_protection_t, set_version, void, METHOD(tls_protection_t, destroy, void, private_tls_protection_t *this) { + DESTROY_IF(this->aead_in); + DESTROY_IF(this->aead_out); free(this); } From 4635f348fa9d92b599e743cb95b6a8b7ea2677c4 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Sun, 1 Nov 2020 17:01:14 +0100 Subject: [PATCH 076/107] tls-server: Share trusted public key search between client and server --- src/libtls/tls_peer.c | 43 +++++++---------------------------------- src/libtls/tls_server.c | 31 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 36 deletions(-) diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 4da8914f4..ec6b64a55 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -159,6 +159,7 @@ struct private_tls_peer_t { /* Implemented in tls_server.c */ bool tls_write_key_share(bio_writer_t **key_share, diffie_hellman_t *dh); +public_key_t *tls_find_public_key(auth_cfg_t *peer_auth); /** * Verify the DH group/key type requested by the server is valid. @@ -598,37 +599,6 @@ static status_t process_certificate(private_tls_peer_t *this, return NEED_MORE; } -/** - * Find a trusted public key to encrypt/verify key exchange data - */ -static public_key_t *find_public_key(private_tls_peer_t *this) -{ - public_key_t *public = NULL, *current; - certificate_t *cert, *found; - enumerator_t *enumerator; - auth_cfg_t *auth; - - cert = this->server_auth->get(this->server_auth, AUTH_HELPER_SUBJECT_CERT); - if (cert) - { - enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, - KEY_ANY, cert->get_subject(cert), - this->server_auth, TRUE); - while (enumerator->enumerate(enumerator, ¤t, &auth)) - { - found = auth->get(auth, AUTH_RULE_SUBJECT_CERT); - if (found && cert->equals(cert, found)) - { - public = current->get_ref(current); - this->server_auth->merge(this->server_auth, auth, FALSE); - break; - } - } - enumerator->destroy(enumerator); - } - return public; -} - /** * Process CertificateVerify message */ @@ -638,10 +608,11 @@ static status_t process_cert_verify(private_tls_peer_t *this, public_key_t *public; chunk_t msg; - public = find_public_key(this); + public = tls_find_public_key(this->server_auth); if (!public) { - DBG1(DBG_TLS, "no TLS public key found for server '%Y'", this->server); + DBG1(DBG_TLS, "no trusted certificate found for '%Y' to verify TLS server", + this->server); this->alert->add(this->alert, TLS_FATAL, TLS_CERTIFICATE_UNKNOWN); return NEED_MORE; } @@ -686,7 +657,7 @@ static status_t process_modp_key_exchange(private_tls_peer_t *this, this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); return NEED_MORE; } - public = find_public_key(this); + public = tls_find_public_key(this->server_auth); if (!public) { DBG1(DBG_TLS, "no TLS public key found for server '%Y'", this->server); @@ -793,7 +764,7 @@ static status_t process_ec_key_exchange(private_tls_peer_t *this, return NEED_MORE; } - public = find_public_key(this); + public = tls_find_public_key(this->server_auth); if (!public) { DBG1(DBG_TLS, "no TLS public key found for server '%Y'", this->server); @@ -1510,7 +1481,7 @@ static status_t send_key_exchange_encrypt(private_tls_peer_t *this, return NEED_MORE; } - public = find_public_key(this); + public = tls_find_public_key(this->server_auth); if (!public) { DBG1(DBG_TLS, "no TLS public key found for server '%Y'", this->server); diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index f5e15615b..1a87d6834 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -169,6 +169,37 @@ struct private_tls_server_t { bool curves_received; }; +/** + * Find a trusted public key to encrypt/verify key exchange data + */ +public_key_t *tls_find_public_key(auth_cfg_t *peer_auth) +{ + public_key_t *public = NULL, *current; + certificate_t *cert, *found; + enumerator_t *enumerator; + auth_cfg_t *auth; + + cert = peer_auth->get(peer_auth, AUTH_HELPER_SUBJECT_CERT); + if (cert) + { + enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, + KEY_ANY, cert->get_subject(cert), + peer_auth, TRUE); + while (enumerator->enumerate(enumerator, ¤t, &auth)) + { + found = auth->get(auth, AUTH_RULE_SUBJECT_CERT); + if (found && cert->equals(cert, found)) + { + public = current->get_ref(current); + peer_auth->merge(peer_auth, auth, FALSE); + break; + } + } + enumerator->destroy(enumerator); + } + return public; +} + /** * Create an array of an intersection of server and peer supported key types */ From 4bba89fff3dfd2dfa510e6a0ad38a905fa7f96f0 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Wed, 4 Nov 2020 12:22:58 +0100 Subject: [PATCH 077/107] tls-server: Make CertificateRequest conditional in old TLS versions The server implementation now only sends a CertificateRequest message if it has identity information to verify client certificates. --- src/libtls/tls_server.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 1a87d6834..07978b3f1 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -88,11 +88,6 @@ struct private_tls_server_t { */ identification_t *peer; - /** - * Is it acceptable if we couldn't verify the peer certificate? - */ - bool peer_auth_optional; - /** * State we are in */ @@ -733,12 +728,6 @@ static status_t process_certificate(private_tls_server_t *this, DBG1(DBG_TLS, "received TLS peer certificate '%Y'", cert->get_subject(cert)); first = FALSE; - if (this->peer == NULL) - { /* apply identity to authenticate */ - this->peer = cert->get_subject(cert); - this->peer = this->peer->clone(this->peer); - this->peer_auth_optional = TRUE; - } } else { @@ -928,11 +917,6 @@ static status_t process_cert_verify(private_tls_server_t *this, { DBG1(DBG_TLS, "no trusted certificate found for '%Y' to verify TLS peer", this->peer); - if (!this->peer_auth_optional) - { /* client authentication is required */ - this->alert->add(this->alert, TLS_FATAL, TLS_CERTIFICATE_UNKNOWN); - return NEED_MORE; - } /* reset peer identity, we couldn't authenticate it */ this->peer->destroy(this->peer); this->peer = NULL; @@ -1598,7 +1582,11 @@ METHOD(tls_handshake_t, build, status_t, } /* otherwise fall through to next state */ case STATE_KEY_EXCHANGE_SENT: - return send_certificate_request(this, type, writer); + if (this->peer) + { + return send_certificate_request(this, type, writer); + } + /* otherwise fall through to next state */ case STATE_CERTREQ_SENT: return send_hello_done(this, type, writer); case STATE_CIPHERSPEC_CHANGED_OUT: From dc49d457a289f5686975b1784bceda4f5f209cbf Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Wed, 4 Nov 2020 13:07:49 +0100 Subject: [PATCH 078/107] tls-server: Terminate connection if peer certificate is required but not sent This change mainly affects legacy TLS versions because TLS 1.3 connections are terminated by the server once the peer does not send a CertificateVerify message next to its empty Certificate message. --- src/libtls/tls_server.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 07978b3f1..ce3714ed1 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -708,6 +708,12 @@ static status_t process_certificate(private_tls_server_t *this, return NEED_MORE; } certs = bio_reader_create(data); + if (!certs->remaining(certs)) + { + DBG1(DBG_TLS, "no certificate sent by peer"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } while (certs->remaining(certs)) { if (!certs->read_data24(certs, &data)) From d2fc9b0961c6963397aa28b42d9562d2aa944c28 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Fri, 30 Oct 2020 15:15:30 +0100 Subject: [PATCH 079/107] tls-server: Mutual authentication support for TLS 1.3 This commit also addresses the side effect that additional messages have an influence on the derivation of the application traffic secrets. Therefore, key derivation is relocated after the server finished message has been sent, so the additional messages from the client (Certificate, CertificateVerify) don't affect the key derivation. Only the outbound key is switched there, the inbound key remains in use until the client's finished message has been processed. --- src/libtls/tests/suites/test_socket.c | 11 -- src/libtls/tls_crypto.c | 29 +++- src/libtls/tls_server.c | 186 ++++++++++++++++++-------- 3 files changed, 159 insertions(+), 67 deletions(-) diff --git a/src/libtls/tests/suites/test_socket.c b/src/libtls/tests/suites/test_socket.c index de1b51682..01883b5d1 100644 --- a/src/libtls/tests/suites/test_socket.c +++ b/src/libtls/tests/suites/test_socket.c @@ -665,12 +665,6 @@ START_TEST(test_tls13) } END_TEST -START_TEST(test_tls13_mutual) -{ - test_tls(TLS_1_3, 5670, TRUE, _i); -} -END_TEST - START_TEST(test_tls12) { test_tls(TLS_1_2, 5671, FALSE, _i); @@ -747,11 +741,6 @@ Suite *socket_suite_create() add_tls_test(test_tls13, TLS_1_3); suite_add_tcase(s, tc); - tc = tcase_create("TLS 1.3/mutl"); - tcase_add_checked_fixture(tc, setup_creds, teardown_creds); - add_tls_test(test_tls13_mutual, TLS_1_3); - suite_add_tcase(s, tc); - tc = tcase_create("TLS 1.2/anon"); tcase_add_checked_fixture(tc, setup_creds, teardown_creds); add_tls_test(test_tls12, TLS_1_2); diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 78d9a5003..d34a8ecee 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1730,6 +1730,26 @@ static chunk_t tls13_sig_data_server = chunk_from_chars( 0x79, 0x00, ); +/** + * TLS 1.3 static part of the data the peer signs (64 spaces followed by the + * context string "TLS 1.3, client CertificateVerify" and a 0 byte). + */ +static chunk_t tls13_sig_data_client = chunk_from_chars( + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x54, 0x4c, 0x53, 0x20, 0x31, 0x2e, 0x33, 0x2c, + 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x69, 0x66, + 0x79, 0x00, +); + METHOD(tls_crypto_t, sign, bool, private_tls_crypto_t *this, private_key_t *key, bio_writer_t *writer, chunk_t data, chunk_t hashsig) @@ -1873,7 +1893,14 @@ METHOD(tls_crypto_t, verify, bool, return FALSE; } - data = chunk_cata("cm", tls13_sig_data_server, transcript_hash); + if (this->tls->is_server(this->tls)) + { + data = chunk_cata("cm", tls13_sig_data_client, transcript_hash); + } + else + { + data = chunk_cata("cm", tls13_sig_data_server, transcript_hash); + } } if (!key->verify(key, params->scheme, params->params, data, sig)) { diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index ce3714ed1..24b62fb90 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -51,6 +51,7 @@ typedef enum { STATE_CERT_VERIFY_SENT, STATE_KEY_UPDATE_REQUESTED, STATE_KEY_UPDATE_SENT, + STATE_FINISHED_SENT_KEY_SWITCHED, } server_state_t; /** @@ -701,6 +702,16 @@ static status_t process_certificate(private_tls_server_t *this, this->crypto->append_handshake(this->crypto, TLS_CERTIFICATE, reader->peek(reader)); + if (this->tls->get_version_max(this->tls) > TLS_1_2) + { + if (!reader->read_data8(reader, &data)) + { + DBG1(DBG_TLS, "certificate request context invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + } + if (!reader->read_data24(reader, &data)) { DBG1(DBG_TLS, "certificate message header invalid"); @@ -747,6 +758,15 @@ static status_t process_certificate(private_tls_server_t *this, DBG1(DBG_TLS, "parsing TLS certificate failed, skipped"); this->alert->add(this->alert, TLS_WARNING, TLS_BAD_CERTIFICATE); } + if (this->tls->get_version_max(this->tls) > TLS_1_2) + { + if (!certs->read_data16(certs, &data)) + { + DBG1(DBG_TLS, "failed to read extensions of CertificateEntry"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + } } certs->destroy(certs); this->state = STATE_CERT_RECEIVED; @@ -897,43 +917,29 @@ static status_t process_key_exchange(private_tls_server_t *this, static status_t process_cert_verify(private_tls_server_t *this, bio_reader_t *reader) { - bool verified = FALSE; - enumerator_t *enumerator; public_key_t *public; - auth_cfg_t *auth; - bio_reader_t *sig; + chunk_t msg; - enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, - KEY_ANY, this->peer, this->peer_auth, TRUE); - while (enumerator->enumerate(enumerator, &public, &auth)) - { - sig = bio_reader_create(reader->peek(reader)); - verified = this->crypto->verify_handshake(this->crypto, public, sig); - sig->destroy(sig); - if (verified) - { - this->peer_auth->merge(this->peer_auth, auth, FALSE); - break; - } - DBG1(DBG_TLS, "signature verification failed, trying another key"); - } - enumerator->destroy(enumerator); - - if (!verified) + public = tls_find_public_key(this->peer_auth); + if (!public) { DBG1(DBG_TLS, "no trusted certificate found for '%Y' to verify TLS peer", this->peer); - /* reset peer identity, we couldn't authenticate it */ - this->peer->destroy(this->peer); - this->peer = NULL; - this->state = STATE_KEY_EXCHANGE_RECEIVED; + this->alert->add(this->alert, TLS_FATAL, TLS_CERTIFICATE_UNKNOWN); + return NEED_MORE; } - else + + msg = reader->peek(reader); + if (!this->crypto->verify_handshake(this->crypto, public, reader)) { - this->state = STATE_CERT_VERIFY_RECEIVED; + public->destroy(public); + DBG1(DBG_TLS, "signature verification failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR); + return NEED_MORE; } - this->crypto->append_handshake(this->crypto, - TLS_CERTIFICATE_VERIFY, reader->peek(reader)); + public->destroy(public); + this->state = STATE_CERT_VERIFY_RECEIVED; + this->crypto->append_handshake(this->crypto, TLS_CERTIFICATE_VERIFY, msg); return NEED_MORE; } @@ -972,14 +978,7 @@ static status_t process_finished(private_tls_server_t *this, this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); return NEED_MORE; } - - if (!this->crypto->derive_app_keys(this->crypto)) - { - this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); - return NEED_MORE; - } this->crypto->change_cipher(this->crypto, TRUE); - this->crypto->change_cipher(this->crypto, FALSE); } if (!chunk_equals_const(received, verify_data)) @@ -1102,6 +1101,29 @@ METHOD(tls_handshake_t, process, status_t, break; case STATE_CIPHERSPEC_CHANGED_IN: case STATE_FINISHED_SENT: + case STATE_FINISHED_SENT_KEY_SWITCHED: + if (type == TLS_CERTIFICATE) + { + return process_certificate(this, reader); + } + if (this->peer) + { + expected = TLS_CERTIFICATE; + break; + } + /* otherwise fall through to next state */ + case STATE_CERT_RECEIVED: + if (type == TLS_CERTIFICATE_VERIFY) + { + return process_cert_verify(this, reader); + } + if (this->peer) + { + expected = TLS_CERTIFICATE_VERIFY; + break; + } + /* otherwise fall through to next state */ + case STATE_CERT_VERIFY_RECEIVED: if (type == TLS_FINISHED) { return process_finished(this, reader); @@ -1348,32 +1370,20 @@ static status_t send_certificate_verify(private_tls_server_t *this, return NEED_MORE; } -/** - * Send Certificate Request +/* + * Write all available certificate authorities to output writer */ -static status_t send_certificate_request(private_tls_server_t *this, - tls_handshake_type_t *type, bio_writer_t *writer) +static void write_certificate_authorities(bio_writer_t *writer) { - bio_writer_t *authorities, *supported; + bio_writer_t *authorities; enumerator_t *enumerator; certificate_t *cert; x509_t *x509; identification_t *id; - supported = bio_writer_create(4); - /* we propose both RSA and ECDSA */ - supported->write_uint8(supported, TLS_RSA_SIGN); - supported->write_uint8(supported, TLS_ECDSA_SIGN); - writer->write_data8(writer, supported->get_buf(supported)); - supported->destroy(supported); - if (this->tls->get_version_max(this->tls) >= TLS_1_2) - { - this->crypto->get_signature_algorithms(this->crypto, writer, TRUE); - } - authorities = bio_writer_create(64); - enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr, - CERT_X509, KEY_RSA, NULL, TRUE); + enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr, CERT_X509, + KEY_RSA, NULL, TRUE); while (enumerator->enumerate(enumerator, &cert)) { x509 = (x509_t*)cert; @@ -1387,6 +1397,56 @@ static status_t send_certificate_request(private_tls_server_t *this, enumerator->destroy(enumerator); writer->write_data16(writer, authorities->get_buf(authorities)); authorities->destroy(authorities); +} + +/** + * Send Certificate Request + */ +static status_t send_certificate_request(private_tls_server_t *this, + tls_handshake_type_t *type, + bio_writer_t *writer) +{ + bio_writer_t *authorities, *supported, *extensions; + + if (this->tls->get_version_max(this->tls) < TLS_1_3) + { + supported = bio_writer_create(4); + /* we propose both RSA and ECDSA */ + supported->write_uint8(supported, TLS_RSA_SIGN); + supported->write_uint8(supported, TLS_ECDSA_SIGN); + writer->write_data8(writer, supported->get_buf(supported)); + supported->destroy(supported); + if (this->tls->get_version_max(this->tls) >= TLS_1_2) + { + this->crypto->get_signature_algorithms(this->crypto, writer, TRUE); + } + + write_certificate_authorities(writer); + } + else + { + /* certificate request context as described in RFC 8446, section 4.3.2 */ + writer->write_uint8(writer, 0); + + extensions = bio_writer_create(32); + DBG2(DBG_TLS, "sending extension: %N", + tls_extension_names, TLS_EXT_CERTIFICATE_AUTHORITIES); + authorities = bio_writer_create(64); + write_certificate_authorities(authorities); + extensions->write_uint16(extensions, TLS_EXT_CERTIFICATE_AUTHORITIES); + extensions->write_data16(extensions, authorities->get_buf(authorities)); + authorities->destroy(authorities); + + DBG2(DBG_TLS, "sending extension: %N", + tls_extension_names, TLS_EXT_SIGNATURE_ALGORITHMS); + extensions->write_uint16(extensions, TLS_EXT_SIGNATURE_ALGORITHMS); + supported = bio_writer_create(32); + this->crypto->get_signature_algorithms(this->crypto, supported, TRUE); + extensions->write_data16(extensions, supported->get_buf(supported)); + supported->destroy(supported); + writer->write_data16(writer, extensions->get_buf(extensions)); + extensions->destroy(extensions); + } *type = TLS_CERTIFICATE_REQUEST; this->state = STATE_CERTREQ_SENT; @@ -1613,12 +1673,26 @@ METHOD(tls_handshake_t, build, status_t, case STATE_CIPHERSPEC_CHANGED_OUT: return send_encrypted_extensions(this, type, writer); case STATE_ENCRYPTED_EXTENSIONS_SENT: + if (this->peer) + { + return send_certificate_request(this, type, writer); + } + /* otherwise fall through to next state */ + case STATE_CERTREQ_SENT: return send_certificate(this, type, writer); case STATE_CERT_SENT: return send_certificate_verify(this, type, writer); case STATE_CERT_VERIFY_SENT: return send_finished(this, type, writer); case STATE_FINISHED_SENT: + if (!this->crypto->derive_app_keys(this->crypto)) + { + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + /* inbound key switches after process client finished message */ + this->crypto->change_cipher(this->crypto, FALSE); + this->state = STATE_FINISHED_SENT_KEY_SWITCHED; return INVALID_STATE; case STATE_KEY_UPDATE_REQUESTED: return send_key_update(this, type, writer); @@ -1667,7 +1741,9 @@ METHOD(tls_handshake_t, cipherspec_changed, bool, { if (inbound) { /* accept ChangeCipherSpec after ServerFinish or HelloRetryRequest */ - return this->state == STATE_FINISHED_SENT || retrying(this); + return this->state == STATE_FINISHED_SENT || + this->state == STATE_FINISHED_SENT_KEY_SWITCHED || + retrying(this); } else { From 299cc800949c7d0c33dff577e7ecc52bb9939156 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Sun, 1 Nov 2020 16:39:00 +0100 Subject: [PATCH 080/107] tls-test: Add support to require/verify client certificates Also add detailed usage output with description of all options. --- scripts/tls_test.c | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/scripts/tls_test.c b/scripts/tls_test.c index 321bacb49..0040e23a3 100644 --- a/scripts/tls_test.c +++ b/scripts/tls_test.c @@ -37,8 +37,27 @@ static void usage(FILE *out, char *cmd) { fprintf(out, "usage:\n"); - fprintf(out, " %s --connect
--port [--key ]+ [--times ]\n", cmd); - fprintf(out, " %s --listen
--port --key [--cert ]+ [--times ]\n", cmd); + fprintf(out, " %s --connect
--port [--key ] [--cacert ]+ [--times ]\n", cmd); + fprintf(out, " %s --listen
--port --key --cert [--cacert ]+ [--times ]\n", cmd); + fprintf(out, "\n"); + fprintf(out, "options:\n"); + fprintf(out, " --help print help and exit\n"); + fprintf(out, " --connect
connect to a server on dns name or ip address\n"); + fprintf(out, " --listen
listen on dns name or ip address\n"); + fprintf(out, " --port specify the port to use\n"); + fprintf(out, " --cert certificate to authenticate itself\n"); + fprintf(out, " --key private key to authenticate itself\n"); + fprintf(out, " --cacert certificate to verify other peer\n"); + fprintf(out, " --times specify the amount of repeated connection establishments\n"); + fprintf(out, " --ipv4 use IPv4\n"); + fprintf(out, " --ipv6 use IPv6\n"); + fprintf(out, " --min-version specify the minimum TLS version, supported versions:\n"); + fprintf(out, " 1.0 (default), 1.1, 1.2 and 1.3\n"); + fprintf(out, " --max-version specify the maximum TLS version, supported versions:\n"); + fprintf(out, " 1.0, 1.1, 1.2 and 1.3 (default)\n"); + fprintf(out, " --version set one specific TLS version to use, supported versions:\n"); + fprintf(out, " 1.0, 1.1, 1.2 and 1.3\n"); + fprintf(out, " --debug set debug level, default is 1\n"); } /** @@ -132,7 +151,7 @@ static int run_client(host_t *host, identification_t *server, /** * Server routine */ -static int serve(host_t *host, identification_t *server, +static int serve(host_t *host, identification_t *server, identification_t *client, int times, tls_cache_t *cache, tls_version_t min_version, tls_version_t max_version) { @@ -170,7 +189,7 @@ static int serve(host_t *host, identification_t *server, } DBG1(DBG_TLS, "%#H connected", host); - tls = tls_socket_create(TRUE, server, NULL, cfd, cache, min_version, + tls = tls_socket_create(TRUE, server, client, cfd, cache, min_version, max_version, TRUE); if (!tls) { @@ -289,7 +308,7 @@ int main(int argc, char *argv[]) char *address = NULL; bool listen = FALSE; int port = 0, times = -1, res, family = AF_UNSPEC; - identification_t *server, *client; + identification_t *server, *client = NULL; tls_version_t min_version = TLS_1_0, max_version = TLS_1_3; tls_cache_t *cache; host_t *host; @@ -305,6 +324,7 @@ int main(int argc, char *argv[]) {"port", required_argument, NULL, 'p' }, {"cert", required_argument, NULL, 'x' }, {"key", required_argument, NULL, 'k' }, + {"cacert", required_argument, NULL, 'f' }, {"times", required_argument, NULL, 't' }, {"ipv4", no_argument, NULL, '4' }, {"ipv6", no_argument, NULL, '6' }, @@ -333,6 +353,13 @@ int main(int argc, char *argv[]) return 1; } continue; + case 'f': + if (!load_certificate(optarg)) + { + return 1; + } + client = identification_create_from_encoding(ID_ANY, chunk_empty); + continue; case 'l': listen = TRUE; /* fall */ @@ -402,10 +429,11 @@ int main(int argc, char *argv[]) cache = tls_cache_create(100, 30); if (listen) { - res = serve(host, server, times, cache, min_version, max_version); + res = serve(host, server, client, times, cache, min_version, max_version); } else { + DESTROY_IF(client); client = find_client_id(); res = run_client(host, server, client, times, cache, min_version, max_version); From d8e42a3d4e3ca24647da8f06c38c5ddc139f1340 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Tue, 10 Nov 2020 10:22:12 +0100 Subject: [PATCH 081/107] tls-crypto: Share private key search between client and server This way the client also properly considers the TLS version and the signature schemes supported by the server. Co-authored-by: Tobias Brunner --- src/libtls/tls_crypto.c | 115 ++++++++++++++++++++++++++++++++++++++-- src/libtls/tls_crypto.h | 15 +++--- src/libtls/tls_peer.c | 30 +++++++++-- src/libtls/tls_server.c | 85 +++++++---------------------- 4 files changed, 166 insertions(+), 79 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index d34a8ecee..1ce2a03e6 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -24,6 +24,7 @@ #include #include #include +#include ENUM_BEGIN(tls_cipher_suite_names, TLS_NULL_WITH_NULL_NULL, TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, @@ -2558,10 +2559,10 @@ CALLBACK(destroy_key_types, void, ht->destroy_function(ht, (void*)free); } -/* - * See header. +/** + * Create an enumerator over supported key types within a specific TLS range */ -enumerator_t *tls_get_supported_key_types(tls_version_t min_version, +static enumerator_t *get_supported_key_types(tls_version_t min_version, tls_version_t max_version) { hashtable_t *ht; @@ -2587,3 +2588,111 @@ enumerator_t *tls_get_supported_key_types(tls_version_t min_version, return enumerator_create_filter(ht->create_enumerator(ht), filter_key_types, ht, destroy_key_types); } + +/** + * Create an array of an intersection of server and peer supported key types + */ +static array_t *create_common_key_types(enumerator_t *enumerator, chunk_t hashsig) +{ + array_t *key_types; + key_type_t v, lookup; + uint16_t sig_scheme; + + key_types = array_create(sizeof(key_type_t), 8); + while (enumerator->enumerate(enumerator, &v)) + { + bio_reader_t *reader; + + reader = bio_reader_create(hashsig); + while (reader->remaining(reader) && + reader->read_uint16(reader, &sig_scheme)) + { + lookup = tls_signature_scheme_to_key_type(sig_scheme); + if (v == lookup) + { + array_insert(key_types, ARRAY_TAIL, &lookup); + break; + } + } + reader->destroy(reader); + } + return key_types; +} + +typedef struct { + enumerator_t public; + array_t *key_types; + identification_t *peer; + private_key_t *key; + auth_cfg_t *auth; +} private_key_enumerator_t; + +METHOD(enumerator_t, private_key_enumerate, bool, + private_key_enumerator_t *this, va_list args) +{ + key_type_t type; + auth_cfg_t **auth_out; + private_key_t **key_out; + + VA_ARGS_VGET(args, key_out, auth_out); + + DESTROY_IF(this->key); + DESTROY_IF(this->auth); + this->auth = auth_cfg_create(); + + while (array_remove(this->key_types, ARRAY_HEAD, &type)) + { + this->key = lib->credmgr->get_private(lib->credmgr, type, this->peer, + this->auth); + if (this->key) + { + *key_out = this->key; + if (auth_out) + { + *auth_out = this->auth; + } + return TRUE; + } + } + return FALSE; +} + +METHOD(enumerator_t, private_key_destroy, void, + private_key_enumerator_t *this) +{ + DESTROY_IF(this->key); + DESTROY_IF(this->auth); + array_destroy(this->key_types); + free(this); +} + +/** + * See header. + */ +enumerator_t *tls_create_private_key_enumerator(tls_version_t min_version, + tls_version_t max_version, + chunk_t hashsig, + identification_t *peer) +{ + private_key_enumerator_t *enumerator; + enumerator_t *key_types; + + key_types = get_supported_key_types(min_version, max_version); + + INIT(enumerator, + .public = { + .enumerate = enumerator_enumerate_default, + .venumerate = _private_key_enumerate, + .destroy = _private_key_destroy, + }, + .key_types = create_common_key_types(key_types, hashsig), + .peer = peer, + ); + key_types->destroy(key_types); + + if (!array_count(enumerator->key_types)) + { + return NULL; + } + return &enumerator->public; +} diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h index 0e9d454f7..3bd75b64e 100644 --- a/src/libtls/tls_crypto.h +++ b/src/libtls/tls_crypto.h @@ -700,15 +700,18 @@ tls_named_group_t tls_ec_group_to_curve(diffie_hellman_group_t group); key_type_t tls_signature_scheme_to_key_type(tls_signature_scheme_t sig); /** - * Create an enumerator over supported key types within a specific TLS version range - * - * Enumerates over key_type_t + * Find a private key to encrypt/verify key exchange data * * @param min_version minimum negotiated TLS version * @param max_version maximum negotiated TLS version - * @return hashtable of key types + * @param hashsig hash and signature algorithms supported by other peer + * @param peer this peer identification + * @return enumerator over private keys, + * NULL in case no common signature scheme */ -enumerator_t *tls_get_supported_key_types(tls_version_t min_version, - tls_version_t max_version); +enumerator_t *tls_create_private_key_enumerator(tls_version_t min_version, + tls_version_t max_version, + chunk_t hashsig, + identification_t *peer); #endif /** TLS_CRYPTO_H_ @}*/ diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index ec6b64a55..3c107d58c 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -1398,13 +1398,37 @@ static status_t send_certificate(private_tls_peer_t *this, certificate_t *cert; auth_rule_t rule; bio_writer_t *certs; + private_key_t *key; + auth_cfg_t *auth; chunk_t data; + tls_version_t version_min, version_max; - this->private = find_private_key(this); + version_min = this->tls->get_version_min(this->tls); + version_max = this->tls->get_version_max(this->tls); + if (version_max > TLS_1_1) + { + enumerator = tls_create_private_key_enumerator(version_min, version_max, + this->hashsig, this->peer); + if (!enumerator) + { + DBG1(DBG_TLS, "no common signature algorithms found"); + return FALSE; + } + if (enumerator->enumerate(enumerator, &key, &auth)) + { + this->private = key->get_ref(key); + this->peer_auth->merge(this->peer_auth, auth, FALSE); + } + enumerator->destroy(enumerator); + } + else + { + this->private = find_private_key(this); + } if (!this->private) { - DBG1(DBG_TLS, "no TLS peer certificate found for '%Y', " - "skipping client authentication", this->peer); + DBG1(DBG_TLS, "no usable TLS client certificate found for '%Y'", + this->peer); this->peer->destroy(this->peer); this->peer = NULL; } diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 24b62fb90..50051c143 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -196,75 +196,31 @@ public_key_t *tls_find_public_key(auth_cfg_t *peer_auth) return public; } -/** - * Create an array of an intersection of server and peer supported key types - */ -static array_t *create_common_key_types(chunk_t hashsig, - tls_version_t version_min, - tls_version_t version_max) -{ - array_t *key_types; - enumerator_t *enumerator; - key_type_t v, lookup; - uint16_t sig_scheme; - - key_types = array_create(sizeof(key_type_t), 8); - enumerator = tls_get_supported_key_types(version_min, version_max); - while (enumerator->enumerate(enumerator, &v)) - { - bio_reader_t *reader; - - reader = bio_reader_create(hashsig); - while (reader->remaining(reader) && - reader->read_uint16(reader, &sig_scheme)) - { - lookup = tls_signature_scheme_to_key_type(sig_scheme); - if (v == lookup) - { - array_insert(key_types, ARRAY_TAIL, &lookup); - break; - } - } - reader->destroy(reader); - } - enumerator->destroy(enumerator); - return key_types; -} - /** * Find a cipher suite and a server key */ static bool select_suite_and_key(private_tls_server_t *this, tls_cipher_suite_t *suites, int count) { - array_t *key_types; tls_version_t version_min, version_max; private_key_t *key; - key_type_t type; + auth_cfg_t *auth; + enumerator_t *enumerator; version_min = this->tls->get_version_min(this->tls); version_max = this->tls->get_version_max(this->tls); - key_types = create_common_key_types(this->hashsig, version_min, version_max); - if (!array_count(key_types)) + enumerator = tls_create_private_key_enumerator(version_min, version_max, + this->hashsig, this->server); + if (!enumerator) { DBG1(DBG_TLS, "no common signature algorithms found"); - array_destroy(key_types); return FALSE; } - while (array_remove(key_types, ARRAY_HEAD, &type)) - { - key = lib->credmgr->get_private(lib->credmgr, type, this->server, - this->server_auth); - if (key) - { - break; - } - } - if (!key) + if (!enumerator->enumerate(enumerator, &key, &auth)) { DBG1(DBG_TLS, "no usable TLS server certificate found for '%Y'", this->server); - array_destroy(key_types); + enumerator->destroy(enumerator); return FALSE; } @@ -276,30 +232,25 @@ static bool select_suite_and_key(private_tls_server_t *this, else { this->suite = this->crypto->select_cipher_suite(this->crypto, suites, - count, type); - while (!this->suite && array_remove(key_types, ARRAY_HEAD, &type)) + count, key->get_type(key)); + while (!this->suite && + enumerator->enumerate(enumerator, &key, &auth)) { /* find a key and cipher suite for one of the remaining key types */ - DESTROY_IF(key); - this->server_auth->destroy(this->server_auth); - this->server_auth = auth_cfg_create(); - key = lib->credmgr->get_private(lib->credmgr, type, this->server, - this->server_auth); - if (key) - { - this->suite = this->crypto->select_cipher_suite(this->crypto, - suites, count, - type); - } + this->suite = this->crypto->select_cipher_suite(this->crypto, + suites, count, + key->get_type(key)); } } - array_destroy(key_types); - if (!this->suite || !key) + if (!this->suite) { DBG1(DBG_TLS, "received cipher suites or signature schemes unacceptable"); + enumerator->destroy(enumerator); return FALSE; } DBG1(DBG_TLS, "using key of type %N", key_type_names, key->get_type(key)); - this->private = key; + this->private = key->get_ref(key); + this->server_auth->merge(this->server_auth, auth, FALSE); + enumerator->destroy(enumerator); return TRUE; } From d41d8b00392f0c232c45072db448bed6c9b444db Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Tue, 17 Nov 2020 17:54:55 +0100 Subject: [PATCH 082/107] tls-peer: Use private key enumeration also in TLS versions < 1.2 Until now, key selection was based on tls_client_certificate_type_t and now uses a simple mapping from these types to tls_signature_scheme_t. --- src/libtls/tls_peer.c | 67 +++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 37 deletions(-) diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 3c107d58c..1f9e27077 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -1350,42 +1350,36 @@ static status_t send_client_hello(private_tls_peer_t *this, } /** - * Find a private key suitable to sign Certificate Verify + * Convert certificate types to signature schemes so TLS version <= 1.1 can use + * the same private key enumeration as newer TLS versions. */ -static private_key_t *find_private_key(private_tls_peer_t *this) +static void convert_cert_types(private_tls_peer_t *this) { - private_key_t *key = NULL; bio_reader_t *reader; - key_type_t type; - uint8_t cert; + bio_writer_t *writer; + uint8_t type; - if (!this->peer) - { - return NULL; - } reader = bio_reader_create(this->cert_types); - while (reader->remaining(reader) && reader->read_uint8(reader, &cert)) + writer = bio_writer_create(0); + while (reader->remaining(reader) && reader->read_uint8(reader, &type)) { - switch (cert) + /* each certificate type is mapped to one signature scheme, which is not + * ideal but serves our needs in legacy TLS versions */ + switch (type) { case TLS_RSA_SIGN: - type = KEY_RSA; + writer->write_uint16(writer, TLS_SIG_RSA_PKCS1_SHA256); break; case TLS_ECDSA_SIGN: - type = KEY_ECDSA; + writer->write_uint16(writer, TLS_SIG_ECDSA_SHA256); break; default: continue; } - key = lib->credmgr->get_private(lib->credmgr, type, - this->peer, this->peer_auth); - if (key) - { - break; - } } reader->destroy(reader); - return key; + this->hashsig = writer->extract_buf(writer); + writer->destroy(writer); } /** @@ -1405,33 +1399,32 @@ static status_t send_certificate(private_tls_peer_t *this, version_min = this->tls->get_version_min(this->tls); version_max = this->tls->get_version_max(this->tls); - if (version_max > TLS_1_1) + if (!this->hashsig.len) + { + convert_cert_types(this); + } + enumerator = tls_create_private_key_enumerator(version_min, version_max, + this->hashsig, this->peer); + if (!enumerator || !enumerator->enumerate(enumerator, &key, &auth)) { - enumerator = tls_create_private_key_enumerator(version_min, version_max, - this->hashsig, this->peer); if (!enumerator) { DBG1(DBG_TLS, "no common signature algorithms found"); - return FALSE; } - if (enumerator->enumerate(enumerator, &key, &auth)) + else { - this->private = key->get_ref(key); - this->peer_auth->merge(this->peer_auth, auth, FALSE); + DBG1(DBG_TLS, "no usable TLS client certificate found for '%Y'", + this->peer); } - enumerator->destroy(enumerator); - } - else - { - this->private = find_private_key(this); - } - if (!this->private) - { - DBG1(DBG_TLS, "no usable TLS client certificate found for '%Y'", - this->peer); this->peer->destroy(this->peer); this->peer = NULL; } + else + { + this->private = key->get_ref(key); + this->peer_auth->merge(this->peer_auth, auth, FALSE); + } + DESTROY_IF(enumerator); /* generate certificate payload */ certs = bio_writer_create(256); From 2d933f318b24545d8fde479eb87661e52a1c68e7 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Tue, 10 Nov 2020 14:44:51 +0100 Subject: [PATCH 083/107] tls-peer: Derive application traffic keys after server finished message The inbound key is used right away, the outbound key only after the client finished message has been sent. --- src/libtls/tls_peer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 1f9e27077..c9da4e260 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -1710,14 +1710,14 @@ METHOD(tls_handshake_t, build, status_t, case STATE_HELLO_DONE: case STATE_CIPHERSPEC_CHANGED_OUT: case STATE_FINISHED_RECEIVED: - return send_finished(this, type, writer); - case STATE_FINISHED_SENT: if (!this->crypto->derive_app_keys(this->crypto)) { this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); return NEED_MORE; } this->crypto->change_cipher(this->crypto, TRUE); + return send_finished(this, type, writer); + case STATE_FINISHED_SENT: this->crypto->change_cipher(this->crypto, FALSE); this->state = STATE_FINISHED_SENT_KEY_SWITCHED; return INVALID_STATE; From 9ef46cfaf9176c4ec288ea59bcdb783e22c13d09 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Tue, 10 Nov 2020 14:50:59 +0100 Subject: [PATCH 084/107] tls-peer: Mutual authentication support for TLS 1.3 --- src/libtls/tests/suites/test_socket.c | 11 ++ src/libtls/tls_crypto.c | 11 +- src/libtls/tls_peer.c | 210 +++++++++++++++++++------- 3 files changed, 176 insertions(+), 56 deletions(-) diff --git a/src/libtls/tests/suites/test_socket.c b/src/libtls/tests/suites/test_socket.c index 01883b5d1..de1b51682 100644 --- a/src/libtls/tests/suites/test_socket.c +++ b/src/libtls/tests/suites/test_socket.c @@ -665,6 +665,12 @@ START_TEST(test_tls13) } END_TEST +START_TEST(test_tls13_mutual) +{ + test_tls(TLS_1_3, 5670, TRUE, _i); +} +END_TEST + START_TEST(test_tls12) { test_tls(TLS_1_2, 5671, FALSE, _i); @@ -741,6 +747,11 @@ Suite *socket_suite_create() add_tls_test(test_tls13, TLS_1_3); suite_add_tcase(s, tc); + tc = tcase_create("TLS 1.3/mutl"); + tcase_add_checked_fixture(tc, setup_creds, teardown_creds); + add_tls_test(test_tls13_mutual, TLS_1_3); + suite_add_tcase(s, tc); + tc = tcase_create("TLS 1.2/anon"); tcase_add_checked_fixture(tc, setup_creds, teardown_creds); add_tls_test(test_tls12, TLS_1_2); diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 1ce2a03e6..e8126c000 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1775,7 +1775,14 @@ METHOD(tls_crypto_t, sign, bool, DBG1(DBG_TLS, "unable to create transcript hash"); return FALSE; } - data = chunk_cata("cm", tls13_sig_data_server, transcript_hash); + if (this->tls->is_server(this->tls)) + { + data = chunk_cata("cm", tls13_sig_data_server, transcript_hash); + } + else + { + data = chunk_cata("cm", tls13_sig_data_client, transcript_hash); + } } if (!hashsig.len) @@ -1884,7 +1891,7 @@ METHOD(tls_crypto_t, verify, bool, tls_signature_scheme_names, scheme); return FALSE; } - if (this->tls->get_version_max(this->tls) == TLS_1_3) + if (this->tls->get_version_max(this->tls) >= TLS_1_3) { chunk_t transcript_hash; diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index c9da4e260..987bd5152 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -49,6 +49,7 @@ typedef enum { STATE_FINISHED_SENT_KEY_SWITCHED, STATE_KEY_UPDATE_REQUESTED, STATE_KEY_UPDATE_SENT, + STATE_CERT_VERIFY_SENT, } peer_state_t; /** @@ -846,55 +847,27 @@ static status_t process_key_exchange(private_tls_peer_t *this, } /** - * Process a Certificate Request message + * Read all available certificate authorities from the given reader */ -static status_t process_certreq(private_tls_peer_t *this, bio_reader_t *reader) +static bool read_certificate_authorities(private_tls_peer_t *this, + bio_reader_t *reader) { - chunk_t types, hashsig, data; + chunk_t data; bio_reader_t *authorities; identification_t *id; certificate_t *cert; - if (!this->peer) - { - DBG1(DBG_TLS, "server requested a certificate, but client " - "authentication disabled"); - } - this->crypto->append_handshake(this->crypto, - TLS_CERTIFICATE_REQUEST, reader->peek(reader)); - - if (!reader->read_data8(reader, &types)) - { - DBG1(DBG_TLS, "certreq message header invalid"); - this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); - return NEED_MORE; - } - this->cert_types = chunk_clone(types); - if (this->tls->get_version_max(this->tls) >= TLS_1_2) - { - if (!reader->read_data16(reader, &hashsig)) - { - DBG1(DBG_TLS, "certreq message invalid"); - this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); - return NEED_MORE; - } - this->hashsig = chunk_clone(hashsig); - } if (!reader->read_data16(reader, &data)) { - DBG1(DBG_TLS, "certreq message invalid"); - this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); - return NEED_MORE; + return FALSE; } authorities = bio_reader_create(data); while (authorities->remaining(authorities)) { if (!authorities->read_data16(authorities, &data)) { - DBG1(DBG_TLS, "certreq message invalid"); - this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); authorities->destroy(authorities); - return NEED_MORE; + return FALSE; } if (this->peer) { @@ -914,6 +887,105 @@ static status_t process_certreq(private_tls_peer_t *this, bio_reader_t *reader) } } authorities->destroy(authorities); + return TRUE; +} + +/** + * Process a Certificate Request message + */ +static status_t process_certreq(private_tls_peer_t *this, bio_reader_t *reader) +{ + chunk_t types, hashsig, context, ext; + bio_reader_t *extensions, *extension; + + if (!this->peer) + { + DBG1(DBG_TLS, "server requested a certificate, but client " + "authentication disabled"); + } + this->crypto->append_handshake(this->crypto, + TLS_CERTIFICATE_REQUEST, reader->peek(reader)); + + if (this->tls->get_version_max(this->tls) < TLS_1_3) + { + if (!reader->read_data8(reader, &types)) + { + DBG1(DBG_TLS, "certreq message header invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + this->cert_types = chunk_clone(types); + if (this->tls->get_version_max(this->tls) >= TLS_1_2) + { + if (!reader->read_data16(reader, &hashsig)) + { + DBG1(DBG_TLS, "certreq message invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + this->hashsig = chunk_clone(hashsig); + } + + if (!read_certificate_authorities(this, reader)) + { + DBG1(DBG_TLS, "certreq message invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + } + else + { + /* certificate request context as described in RFC 8446, section 4.3.2 */ + reader->read_data8(reader, &context); + + reader->read_data16(reader, &ext); + extensions = bio_reader_create(ext); + while (extensions->remaining(extensions)) + { + uint16_t extension_type; + chunk_t extension_data; + + if (!extensions->read_uint16(extensions, &extension_type) || + !extensions->read_data16(extensions, &extension_data)) + { + DBG1(DBG_TLS, "invalid extension in CertificateRequest"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + extensions->destroy(extensions); + return NEED_MORE; + } + extension = bio_reader_create(extension_data); + switch (extension_type) + { + case TLS_EXT_SIGNATURE_ALGORITHMS: + if (!extension->read_data16(extension, &extension_data)) + { + DBG1(DBG_TLS, "invalid %N extension", + tls_extension_names, extension_type); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + extension->destroy(extension); + extensions->destroy(extensions); + return NEED_MORE; + } + chunk_free(&this->hashsig); + this->hashsig = chunk_clone(extension_data); + break; + case TLS_EXT_CERTIFICATE_AUTHORITIES: + if (!read_certificate_authorities(this, extension)) + { + DBG1(DBG_TLS, "certificate request message invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + extension->destroy(extension); + extensions->destroy(extensions); + return NEED_MORE; + } + break; + default: + break; + } + extension->destroy(extension); + } + extensions->destroy(extensions); + } this->state = STATE_CERTREQ_RECEIVED; return NEED_MORE; } @@ -1115,6 +1187,15 @@ METHOD(tls_handshake_t, process, status_t, expected = TLS_ENCRYPTED_EXTENSIONS; break; case STATE_ENCRYPTED_EXTENSIONS_RECEIVED: + if (type == TLS_CERTIFICATE_REQUEST) + { + return process_certreq(this, reader); + } + /* no cert request, server does not want to authenticate us */ + DESTROY_IF(this->peer); + this->peer = NULL; + /* otherwise fall through to next state */ + case STATE_CERTREQ_RECEIVED: if (type == TLS_CERTIFICATE) { return process_certificate(this, reader); @@ -1426,37 +1507,45 @@ static status_t send_certificate(private_tls_peer_t *this, } DESTROY_IF(enumerator); + /* certificate request context as described in RFC 8446, section 4.4.2 */ + if (this->tls->get_version_max(this->tls) > TLS_1_2) + { + writer->write_uint8(writer, 0); + } + /* generate certificate payload */ certs = bio_writer_create(256); - if (this->peer) + cert = this->peer_auth->get(this->peer_auth, AUTH_RULE_SUBJECT_CERT); + if (cert) { - cert = this->peer_auth->get(this->peer_auth, AUTH_RULE_SUBJECT_CERT); - if (cert) + if (cert->get_encoding(cert, CERT_ASN1_DER, &data)) + { + DBG1(DBG_TLS, "sending TLS client certificate '%Y'", + cert->get_subject(cert)); + certs->write_data24(certs, data); + free(data.ptr); + } + /* extensions see RFC 8446, section 4.4.2 */ + if (this->tls->get_version_max(this->tls) > TLS_1_2) + { + certs->write_uint16(certs, 0); + } + } + enumerator = this->peer_auth->create_enumerator(this->peer_auth); + while (enumerator->enumerate(enumerator, &rule, &cert)) + { + if (rule == AUTH_RULE_IM_CERT) { if (cert->get_encoding(cert, CERT_ASN1_DER, &data)) { - DBG1(DBG_TLS, "sending TLS peer certificate '%Y'", + DBG1(DBG_TLS, "sending TLS intermediate certificate '%Y'", cert->get_subject(cert)); certs->write_data24(certs, data); free(data.ptr); } } - enumerator = this->peer_auth->create_enumerator(this->peer_auth); - while (enumerator->enumerate(enumerator, &rule, &cert)) - { - if (rule == AUTH_RULE_IM_CERT) - { - if (cert->get_encoding(cert, CERT_ASN1_DER, &data)) - { - DBG1(DBG_TLS, "sending TLS intermediate certificate '%Y'", - cert->get_subject(cert)); - certs->write_data24(certs, data); - free(data.ptr); - } - } - } - enumerator->destroy(enumerator); } + enumerator->destroy(enumerator); writer->write_data24(writer, certs->get_buf(certs)); certs->destroy(certs); @@ -1596,7 +1685,8 @@ static status_t send_key_exchange(private_tls_peer_t *this, * Send certificate verify */ static status_t send_certificate_verify(private_tls_peer_t *this, - tls_handshake_type_t *type, bio_writer_t *writer) + tls_handshake_type_t *type, + bio_writer_t *writer) { if (!this->private || !this->crypto->sign_handshake(this->crypto, this->private, @@ -1716,6 +1806,18 @@ METHOD(tls_handshake_t, build, status_t, return NEED_MORE; } this->crypto->change_cipher(this->crypto, TRUE); + if (this->peer) + { + return send_certificate(this, type, writer); + } + /* otherwise fall through to next state */ + case STATE_CERT_SENT: + if (this->peer) + { + return send_certificate_verify(this, type, writer); + } + /* otherwise fall through to next state */ + case STATE_VERIFY_SENT: return send_finished(this, type, writer); case STATE_FINISHED_SENT: this->crypto->change_cipher(this->crypto, FALSE); From 3e535c31b467cd392d78041e5bd6aa1f48a34912 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Sun, 22 Nov 2020 21:30:45 +0100 Subject: [PATCH 085/107] tls-hkdf: Implement resumption key generation --- src/libtls/tls_hkdf.c | 41 +++++++++++++++++++++++++++++++++++++++++ src/libtls/tls_hkdf.h | 11 +++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/libtls/tls_hkdf.c b/src/libtls/tls_hkdf.c index 002d481f1..0a29b6855 100644 --- a/src/libtls/tls_hkdf.c +++ b/src/libtls/tls_hkdf.c @@ -586,6 +586,46 @@ METHOD(tls_hkdf_t, export, bool, return TRUE; } +METHOD(tls_hkdf_t, resume, bool, + private_tls_hkdf_t *this, chunk_t messages, chunk_t nonce, chunk_t *key) +{ + chunk_t resumption_master; + + if (this->phase != HKDF_PHASE_3) + { + DBG1(DBG_TLS, "unable to generate resumption key material"); + return FALSE; + } + if (!nonce.len) + { + DBG1(DBG_TLS, "no nonce provided"); + return FALSE; + } + + /** + * PSK associated with the ticket according to RFC 8446, section 4.6.1 + * + * HKDF-Expand-Label(resumption_master_secret, + * "resumption", ticket_nonce, Hash.length) + */ + if (!generate_secret(this, TLS_HKDF_RES_MASTER, messages, + &resumption_master)) + { + DBG1(DBG_TLS, "unable to derive resumption master secret"); + return FALSE; + } + + if (!expand_label(this, resumption_master, chunk_from_str("resumption"), + nonce, this->hasher->get_hash_size(this->hasher), key)) + { + chunk_clear(&resumption_master); + DBG1(DBG_TLS, "unable to expand key material"); + return FALSE; + } + chunk_clear(&resumption_master); + return TRUE; +} + METHOD(tls_hkdf_t, allocate_bytes, bool, private_tls_hkdf_t *this, chunk_t key, chunk_t seed, chunk_t *out) @@ -643,6 +683,7 @@ tls_hkdf_t *tls_hkdf_create(hash_algorithm_t hash_algorithm, chunk_t psk) .derive_iv = _derive_iv, .derive_finished = _derive_finished, .export = _export, + .resume = _resume, .allocate_bytes = _allocate_bytes, .destroy = _destroy, }, diff --git a/src/libtls/tls_hkdf.h b/src/libtls/tls_hkdf.h index 2ce3d038a..38e8de82a 100644 --- a/src/libtls/tls_hkdf.h +++ b/src/libtls/tls_hkdf.h @@ -126,6 +126,17 @@ struct tls_hkdf_t { bool (*export)(tls_hkdf_t *this, char *label, chunk_t context, chunk_t messages, size_t length, chunk_t *key); + /** + * Generate resumption PSKs. + * + * @param messages handshake messages + * @param nonce nonce to use for this PSK + * @param psk generated PSK + * @return TRUE if PSK successfully generated + */ + bool (*resume)(tls_hkdf_t *this, chunk_t messages, chunk_t nonce, + chunk_t *psk); + /** * Use the internal PRF to allocate data (mainly for the finished message * where the key is from derive_finished() and the seed is the transcript From 7797c058d9eae4458d438e8859f83e194e760be1 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Fri, 4 Dec 2020 19:01:00 +0100 Subject: [PATCH 086/107] tls-hkdf: Implement binder PSK generation --- src/libtls/tls_hkdf.c | 30 ++++++++++++++++++++++++++++++ src/libtls/tls_hkdf.h | 14 ++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/libtls/tls_hkdf.c b/src/libtls/tls_hkdf.c index 0a29b6855..c406b3ec9 100644 --- a/src/libtls/tls_hkdf.c +++ b/src/libtls/tls_hkdf.c @@ -626,6 +626,35 @@ METHOD(tls_hkdf_t, resume, bool, return TRUE; } +METHOD(tls_hkdf_t, binder, bool, + private_tls_hkdf_t *this, chunk_t seed, chunk_t *out) +{ + chunk_t binder_key, finished_key; + + if (!generate_secret(this, TLS_HKDF_RES_BINDER, chunk_empty, &binder_key)) + { + DBG1(DBG_TLS, "unable to derive binder key"); + return FALSE; + } + + if (!expand_label(this, binder_key, chunk_from_str("finished"), chunk_empty, + this->hasher->get_hash_size(this->hasher), &finished_key)) + { + chunk_clear(&binder_key); + return FALSE; + } + chunk_clear(&binder_key); + + if (!this->prf->set_key(this->prf, finished_key) || + !this->prf->allocate_bytes(this->prf, seed, out)) + { + chunk_clear(&finished_key); + return FALSE; + } + chunk_clear(&finished_key); + return TRUE; +} + METHOD(tls_hkdf_t, allocate_bytes, bool, private_tls_hkdf_t *this, chunk_t key, chunk_t seed, chunk_t *out) @@ -684,6 +713,7 @@ tls_hkdf_t *tls_hkdf_create(hash_algorithm_t hash_algorithm, chunk_t psk) .derive_finished = _derive_finished, .export = _export, .resume = _resume, + .binder = _binder, .allocate_bytes = _allocate_bytes, .destroy = _destroy, }, diff --git a/src/libtls/tls_hkdf.h b/src/libtls/tls_hkdf.h index 38e8de82a..c280ebda8 100644 --- a/src/libtls/tls_hkdf.h +++ b/src/libtls/tls_hkdf.h @@ -137,6 +137,20 @@ struct tls_hkdf_t { bool (*resume)(tls_hkdf_t *this, chunk_t messages, chunk_t nonce, chunk_t *psk); + /** + * Generate a PSK binder. + * + * @note The transcript hash is built of the partial ClientHello message up + * to and including the PreSharedKey extension's identities field, excluding + * the actual binders (their length is included in that of the extension(s) + * and message, though), as per RFC 8446, section 4.2.11.2. + * + * @param seed transcript-hash of client_hello to seed the PRF + * @param psk_binder generated psk binder + * @return TRUE if output was generated + */ + bool (*binder)(tls_hkdf_t *this, chunk_t seed, chunk_t *psk_binder); + /** * Use the internal PRF to allocate data (mainly for the finished message * where the key is from derive_finished() and the seed is the transcript From 9389fef78a2831cd30d72a14d8155e960c0c4f15 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Sun, 22 Nov 2020 21:37:37 +0100 Subject: [PATCH 087/107] test-hkdf: Add two test cases and restructure all tests RFC 8448 contains multiple TLS 1.3 message traces, this commit adds two new test cases focusing on key derivation: - Simple 1-RTT Handshake - Resumed 0-RTT Handshake Additionally, the whole test suite is restructured and duplicate code is removed and consolidated. --- src/libtls/tests/suites/test_hkdf.c | 972 ++++++++++++++++++---------- 1 file changed, 630 insertions(+), 342 deletions(-) diff --git a/src/libtls/tests/suites/test_hkdf.c b/src/libtls/tests/suites/test_hkdf.c index 529c6d540..df61b6100 100644 --- a/src/libtls/tests/suites/test_hkdf.c +++ b/src/libtls/tests/suites/test_hkdf.c @@ -18,408 +18,691 @@ #include "tls_hkdf.h" +static chunk_t ulfheim_ecdhe = chunk_from_chars( + 0xdf,0x4a,0x29,0x1b,0xaa,0x1e,0xb7,0xcf,0xa6,0x93,0x4b,0x29,0xb4,0x74,0xba,0xad, + 0x26,0x97,0xe2,0x9f,0x1f,0x92,0x0d,0xcc,0x77,0xc8,0xa0,0xa0,0x88,0x44,0x76,0x24, +); + +static chunk_t ulfheim_client_server_hello = chunk_from_chars( + /* Client Hello */ + 0x01,0x00,0x00,0xc6,0x03,0x03,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, + 0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19, + 0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8, + 0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, + 0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff,0x00,0x06,0x13,0x01,0x13,0x02,0x13,0x03,0x01, + 0x00,0x00,0x77,0x00,0x00,0x00,0x18,0x00,0x16,0x00,0x00,0x13,0x65,0x78,0x61,0x6d, + 0x70,0x6c,0x65,0x2e,0x75,0x6c,0x66,0x68,0x65,0x69,0x6d,0x2e,0x6e,0x65,0x74,0x00, + 0x0a,0x00,0x08,0x00,0x06,0x00,0x1d,0x00,0x17,0x00,0x18,0x00,0x0d,0x00,0x14,0x00, + 0x12,0x04,0x03,0x08,0x04,0x04,0x01,0x05,0x03,0x08,0x05,0x05,0x01,0x08,0x06,0x06, + 0x01,0x02,0x01,0x00,0x33,0x00,0x26,0x00,0x24,0x00,0x1d,0x00,0x20,0x35,0x80,0x72, + 0xd6,0x36,0x58,0x80,0xd1,0xae,0xea,0x32,0x9a,0xdf,0x91,0x21,0x38,0x38,0x51,0xed, + 0x21,0xa2,0x8e,0x3b,0x75,0xe9,0x65,0xd0,0xd2,0xcd,0x16,0x62,0x54,0x00,0x2d,0x00, + 0x02,0x01,0x01,0x00,0x2b,0x00,0x03,0x02,0x03,0x04, + /* Server Hello */ + 0x02,0x00,0x00,0x76,0x03,0x03,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79, + 0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89, + 0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,0x20,0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8, + 0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, + 0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff,0x13,0x01,0x00,0x00,0x2e,0x00,0x33,0x00,0x24, + 0x00,0x1d,0x00,0x20,0x9f,0xd7,0xad,0x6d,0xcf,0xf4,0x29,0x8d,0xd3,0xf9,0x6d,0x5b, + 0x1b,0x2a,0xf9,0x10,0xa0,0x53,0x5b,0x14,0x88,0xd7,0xf8,0xfa,0xbb,0x34,0x9a,0x98, + 0x28,0x80,0xb6,0x15,0x00,0x2b,0x00,0x02,0x03,0x04, +); + +static chunk_t ulfheim_server_data = chunk_from_chars( + /* Server Encrypted Extension */ + 0x08,0x00,0x00,0x02,0x00,0x00, + /* Server Certificate */ + 0x0b,0x00,0x03,0x2e,0x00,0x00,0x03,0x2a,0x00,0x03,0x25,0x30,0x82,0x03,0x21,0x30, + 0x82,0x02,0x09,0xa0,0x03,0x02,0x01,0x02,0x02,0x08,0x15,0x5a,0x92,0xad,0xc2,0x04, + 0x8f,0x90,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b,0x05, + 0x00,0x30,0x22,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53, + 0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0a,0x13,0x0a,0x45,0x78,0x61,0x6d,0x70, + 0x6c,0x65,0x20,0x43,0x41,0x30,0x1e,0x17,0x0d,0x31,0x38,0x31,0x30,0x30,0x35,0x30, + 0x31,0x33,0x38,0x31,0x37,0x5a,0x17,0x0d,0x31,0x39,0x31,0x30,0x30,0x35,0x30,0x31, + 0x33,0x38,0x31,0x37,0x5a,0x30,0x2b,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06, + 0x13,0x02,0x55,0x53,0x31,0x1c,0x30,0x1a,0x06,0x03,0x55,0x04,0x03,0x13,0x13,0x65, + 0x78,0x61,0x6d,0x70,0x6c,0x65,0x2e,0x75,0x6c,0x66,0x68,0x65,0x69,0x6d,0x2e,0x6e, + 0x65,0x74,0x30,0x82,0x01,0x22,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d, + 0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0f,0x00,0x30,0x82,0x01,0x0a,0x02,0x82, + 0x01,0x01,0x00,0xc4,0x80,0x36,0x06,0xba,0xe7,0x47,0x6b,0x08,0x94,0x04,0xec,0xa7, + 0xb6,0x91,0x04,0x3f,0xf7,0x92,0xbc,0x19,0xee,0xfb,0x7d,0x74,0xd7,0xa8,0x0d,0x00, + 0x1e,0x7b,0x4b,0x3a,0x4a,0xe6,0x0f,0xe8,0xc0,0x71,0xfc,0x73,0xe7,0x02,0x4c,0x0d, + 0xbc,0xf4,0xbd,0xd1,0x1d,0x39,0x6b,0xba,0x70,0x46,0x4a,0x13,0xe9,0x4a,0xf8,0x3d, + 0xf3,0xe1,0x09,0x59,0x54,0x7b,0xc9,0x55,0xfb,0x41,0x2d,0xa3,0x76,0x52,0x11,0xe1, + 0xf3,0xdc,0x77,0x6c,0xaa,0x53,0x37,0x6e,0xca,0x3a,0xec,0xbe,0xc3,0xaa,0xb7,0x3b, + 0x31,0xd5,0x6c,0xb6,0x52,0x9c,0x80,0x98,0xbc,0xc9,0xe0,0x28,0x18,0xe2,0x0b,0xf7, + 0xf8,0xa0,0x3a,0xfd,0x17,0x04,0x50,0x9e,0xce,0x79,0xbd,0x9f,0x39,0xf1,0xea,0x69, + 0xec,0x47,0x97,0x2e,0x83,0x0f,0xb5,0xca,0x95,0xde,0x95,0xa1,0xe6,0x04,0x22,0xd5, + 0xee,0xbe,0x52,0x79,0x54,0xa1,0xe7,0xbf,0x8a,0x86,0xf6,0x46,0x6d,0x0d,0x9f,0x16, + 0x95,0x1a,0x4c,0xf7,0xa0,0x46,0x92,0x59,0x5c,0x13,0x52,0xf2,0x54,0x9e,0x5a,0xfb, + 0x4e,0xbf,0xd7,0x7a,0x37,0x95,0x01,0x44,0xe4,0xc0,0x26,0x87,0x4c,0x65,0x3e,0x40, + 0x7d,0x7d,0x23,0x07,0x44,0x01,0xf4,0x84,0xff,0xd0,0x8f,0x7a,0x1f,0xa0,0x52,0x10, + 0xd1,0xf4,0xf0,0xd5,0xce,0x79,0x70,0x29,0x32,0xe2,0xca,0xbe,0x70,0x1f,0xdf,0xad, + 0x6b,0x4b,0xb7,0x11,0x01,0xf4,0x4b,0xad,0x66,0x6a,0x11,0x13,0x0f,0xe2,0xee,0x82, + 0x9e,0x4d,0x02,0x9d,0xc9,0x1c,0xdd,0x67,0x16,0xdb,0xb9,0x06,0x18,0x86,0xed,0xc1, + 0xba,0x94,0x21,0x02,0x03,0x01,0x00,0x01,0xa3,0x52,0x30,0x50,0x30,0x0e,0x06,0x03, + 0x55,0x1d,0x0f,0x01,0x01,0xff,0x04,0x04,0x03,0x02,0x05,0xa0,0x30,0x1d,0x06,0x03, + 0x55,0x1d,0x25,0x04,0x16,0x30,0x14,0x06,0x08,0x2b,0x06,0x01,0x05,0x05,0x07,0x03, + 0x02,0x06,0x08,0x2b,0x06,0x01,0x05,0x05,0x07,0x03,0x01,0x30,0x1f,0x06,0x03,0x55, + 0x1d,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0x89,0x4f,0xde,0x5b,0xcc,0x69,0xe2,0x52, + 0xcf,0x3e,0xa3,0x00,0xdf,0xb1,0x97,0xb8,0x1d,0xe1,0xc1,0x46,0x30,0x0d,0x06,0x09, + 0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b,0x05,0x00,0x03,0x82,0x01,0x01,0x00, + 0x59,0x16,0x45,0xa6,0x9a,0x2e,0x37,0x79,0xe4,0xf6,0xdd,0x27,0x1a,0xba,0x1c,0x0b, + 0xfd,0x6c,0xd7,0x55,0x99,0xb5,0xe7,0xc3,0x6e,0x53,0x3e,0xff,0x36,0x59,0x08,0x43, + 0x24,0xc9,0xe7,0xa5,0x04,0x07,0x9d,0x39,0xe0,0xd4,0x29,0x87,0xff,0xe3,0xeb,0xdd, + 0x09,0xc1,0xcf,0x1d,0x91,0x44,0x55,0x87,0x0b,0x57,0x1d,0xd1,0x9b,0xdf,0x1d,0x24, + 0xf8,0xbb,0x9a,0x11,0xfe,0x80,0xfd,0x59,0x2b,0xa0,0x39,0x8c,0xde,0x11,0xe2,0x65, + 0x1e,0x61,0x8c,0xe5,0x98,0xfa,0x96,0xe5,0x37,0x2e,0xef,0x3d,0x24,0x8a,0xfd,0xe1, + 0x74,0x63,0xeb,0xbf,0xab,0xb8,0xe4,0xd1,0xab,0x50,0x2a,0x54,0xec,0x00,0x64,0xe9, + 0x2f,0x78,0x19,0x66,0x0d,0x3f,0x27,0xcf,0x20,0x9e,0x66,0x7f,0xce,0x5a,0xe2,0xe4, + 0xac,0x99,0xc7,0xc9,0x38,0x18,0xf8,0xb2,0x51,0x07,0x22,0xdf,0xed,0x97,0xf3,0x2e, + 0x3e,0x93,0x49,0xd4,0xc6,0x6c,0x9e,0xa6,0x39,0x6d,0x74,0x44,0x62,0xa0,0x6b,0x42, + 0xc6,0xd5,0xba,0x68,0x8e,0xac,0x3a,0x01,0x7b,0xdd,0xfc,0x8e,0x2c,0xfc,0xad,0x27, + 0xcb,0x69,0xd3,0xcc,0xdc,0xa2,0x80,0x41,0x44,0x65,0xd3,0xae,0x34,0x8c,0xe0,0xf3, + 0x4a,0xb2,0xfb,0x9c,0x61,0x83,0x71,0x31,0x2b,0x19,0x10,0x41,0x64,0x1c,0x23,0x7f, + 0x11,0xa5,0xd6,0x5c,0x84,0x4f,0x04,0x04,0x84,0x99,0x38,0x71,0x2b,0x95,0x9e,0xd6, + 0x85,0xbc,0x5c,0x5d,0xd6,0x45,0xed,0x19,0x90,0x94,0x73,0x40,0x29,0x26,0xdc,0xb4, + 0x0e,0x34,0x69,0xa1,0x59,0x41,0xe8,0xe2,0xcc,0xa8,0x4b,0xb6,0x08,0x46,0x36,0xa0, + 0x00,0x00, + /* Server Certificate Verify */ + 0x0f,0x00,0x01,0x04,0x08,0x04,0x01,0x00,0x17,0xfe,0xb5,0x33,0xca,0x6d,0x00,0x7d, + 0x00,0x58,0x25,0x79,0x68,0x42,0x4b,0xbc,0x3a,0xa6,0x90,0x9e,0x9d,0x49,0x55,0x75, + 0x76,0xa5,0x20,0xe0,0x4a,0x5e,0xf0,0x5f,0x0e,0x86,0xd2,0x4f,0xf4,0x3f,0x8e,0xb8, + 0x61,0xee,0xf5,0x95,0x22,0x8d,0x70,0x32,0xaa,0x36,0x0f,0x71,0x4e,0x66,0x74,0x13, + 0x92,0x6e,0xf4,0xf8,0xb5,0x80,0x3b,0x69,0xe3,0x55,0x19,0xe3,0xb2,0x3f,0x43,0x73, + 0xdf,0xac,0x67,0x87,0x06,0x6d,0xcb,0x47,0x56,0xb5,0x45,0x60,0xe0,0x88,0x6e,0x9b, + 0x96,0x2c,0x4a,0xd2,0x8d,0xab,0x26,0xba,0xd1,0xab,0xc2,0x59,0x16,0xb0,0x9a,0xf2, + 0x86,0x53,0x7f,0x68,0x4f,0x80,0x8a,0xef,0xee,0x73,0x04,0x6c,0xb7,0xdf,0x0a,0x84, + 0xfb,0xb5,0x96,0x7a,0xca,0x13,0x1f,0x4b,0x1c,0xf3,0x89,0x79,0x94,0x03,0xa3,0x0c, + 0x02,0xd2,0x9c,0xbd,0xad,0xb7,0x25,0x12,0xdb,0x9c,0xec,0x2e,0x5e,0x1d,0x00,0xe5, + 0x0c,0xaf,0xcf,0x6f,0x21,0x09,0x1e,0xbc,0x4f,0x25,0x3c,0x5e,0xab,0x01,0xa6,0x79, + 0xba,0xea,0xbe,0xed,0xb9,0xc9,0x61,0x8f,0x66,0x00,0x6b,0x82,0x44,0xd6,0x62,0x2a, + 0xaa,0x56,0x88,0x7c,0xcf,0xc6,0x6a,0x0f,0x38,0x51,0xdf,0xa1,0x3a,0x78,0xcf,0xf7, + 0x99,0x1e,0x03,0xcb,0x2c,0x3a,0x0e,0xd8,0x7d,0x73,0x67,0x36,0x2e,0xb7,0x80,0x5b, + 0x00,0xb2,0x52,0x4f,0xf2,0x98,0xa4,0xda,0x48,0x7c,0xac,0xde,0xaf,0x8a,0x23,0x36, + 0xc5,0x63,0x1b,0x3e,0xfa,0x93,0x5b,0xb4,0x11,0xe7,0x53,0xca,0x13,0xb0,0x15,0xfe, + 0xc7,0xe4,0xa7,0x30,0xf1,0x36,0x9f,0x9e, + /* Server Handshake Finish */ + 0x14,0x00,0x00,0x20,0xea,0x6e,0xe1,0x76,0xdc,0xcc,0x4a,0xf1,0x85,0x9e,0x9e,0x4e, + 0x93,0xf7,0x97,0xea,0xc9,0xa7,0x8c,0xe4,0x39,0x30,0x1e,0x35,0x27,0x5a,0xd4,0x3f, + 0x3c,0xdd,0xbd,0xe3, +); + +static void check_secret(tls_hkdf_t *hkdf, tls_hkdf_label_t label, chunk_t data, chunk_t exp_secret) +{ + chunk_t secret; + + ck_assert(hkdf->generate_secret(hkdf, label, data, &secret)); + ck_assert_chunk_eq(exp_secret, secret); + + chunk_free(&secret); +} + +static void check_secret_key_iv(tls_hkdf_t *hkdf, tls_hkdf_label_t label, + chunk_t data, bool is_server, chunk_t exp_secret, + int key_length, int iv_length, chunk_t exp_key, + chunk_t exp_iv) +{ + chunk_t key, iv; + + check_secret(hkdf, label, data, exp_secret); + + ck_assert(hkdf->derive_key(hkdf, is_server, key_length, &key)); + ck_assert_chunk_eq(exp_key, key); + + ck_assert(hkdf->derive_iv(hkdf, is_server, iv_length, &iv)); + ck_assert_chunk_eq(exp_iv, iv); + + chunk_free(&key); + chunk_free(&iv); +} + +static void check_finished(tls_hkdf_t *hkdf, bool is_server, chunk_t exp_finished) +{ + chunk_t finished; + + ck_assert(hkdf->derive_finished(hkdf, is_server, &finished)); + ck_assert_chunk_eq(exp_finished, finished); + + chunk_free(&finished); +} + +static void check_resumption(tls_hkdf_t *hkdf, chunk_t data, chunk_t exp_resume) +{ + chunk_t nonce, resume; + + nonce = chunk_from_chars(0x00,0x00); + ck_assert(hkdf->resume(hkdf, data, nonce, &resume)); + ck_assert_chunk_eq(exp_resume, resume); + + chunk_free(&resume); +} + START_TEST(test_ulfheim_handshake) { - chunk_t handshake = chunk_from_chars( - /* Client Hello */ - 0x01, 0x00, 0x00, 0xc6, 0x03, 0x03, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, - 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, - 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, - 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0xe0, - 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, - 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, - 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x00, - 0x06, 0x13, 0x01, 0x13, 0x02, 0x13, 0x03, 0x01, - 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x18, 0x00, - 0x16, 0x00, 0x00, 0x13, 0x65, 0x78, 0x61, 0x6d, - 0x70, 0x6c, 0x65, 0x2e, 0x75, 0x6c, 0x66, 0x68, - 0x65, 0x69, 0x6d, 0x2e, 0x6e, 0x65, 0x74, 0x00, - 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, - 0x17, 0x00, 0x18, 0x00, 0x0d, 0x00, 0x14, 0x00, - 0x12, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, 0x05, - 0x03, 0x08, 0x05, 0x05, 0x01, 0x08, 0x06, 0x06, - 0x01, 0x02, 0x01, 0x00, 0x33, 0x00, 0x26, 0x00, - 0x24, 0x00, 0x1d, 0x00, 0x20, 0x35, 0x80, 0x72, - 0xd6, 0x36, 0x58, 0x80, 0xd1, 0xae, 0xea, 0x32, - 0x9a, 0xdf, 0x91, 0x21, 0x38, 0x38, 0x51, 0xed, - 0x21, 0xa2, 0x8e, 0x3b, 0x75, 0xe9, 0x65, 0xd0, - 0xd2, 0xcd, 0x16, 0x62, 0x54, 0x00, 0x2d, 0x00, - 0x02, 0x01, 0x01, 0x00, 0x2b, 0x00, 0x03, 0x02, - 0x03, 0x04, - /* Server Hello */ - 0x02, 0x00, 0x00, 0x76, 0x03, 0x03, 0x70, 0x71, - 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, - 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, - 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, - 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x20, 0xe0, - 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, - 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, - 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x13, - 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x24, - 0x00, 0x1d, 0x00, 0x20, 0x9f, 0xd7, 0xad, 0x6d, - 0xcf, 0xf4, 0x29, 0x8d, 0xd3, 0xf9, 0x6d, 0x5b, - 0x1b, 0x2a, 0xf9, 0x10, 0xa0, 0x53, 0x5b, 0x14, - 0x88, 0xd7, 0xf8, 0xfa, 0xbb, 0x34, 0x9a, 0x98, - 0x28, 0x80, 0xb6, 0x15, 0x00, 0x2b, 0x00, 0x02, - 0x03, 0x04, + chunk_t exp_client_handshake_traffic_secret = chunk_from_chars( + 0xff,0x0e,0x5b,0x96,0x52,0x91,0xc6,0x08,0xc1,0xe8,0xcd,0x26,0x7e,0xef,0xc0,0xaf, + 0xcc,0x5e,0x98,0xa2,0x78,0x63,0x73,0xf0,0xdb,0x47,0xb0,0x47,0x86,0xd7,0x2a,0xea, ); - chunk_t ecdhe = chunk_from_chars( - 0xdf, 0x4a, 0x29, 0x1b, 0xaa, 0x1e, 0xb7, 0xcf, - 0xa6, 0x93, 0x4b, 0x29, 0xb4, 0x74, 0xba, 0xad, - 0x26, 0x97, 0xe2, 0x9f, 0x1f, 0x92, 0x0d, 0xcc, - 0x77, 0xc8, 0xa0, 0xa0, 0x88, 0x44, 0x76, 0x24, + chunk_t exp_client_handshake_key = chunk_from_chars( + 0x71,0x54,0xf3,0x14,0xe6,0xbe,0x7d,0xc0,0x08,0xdf,0x2c,0x83,0x2b,0xaa,0x1d,0x39, ); - chunk_t expected_client_handshake_traffic_secret = chunk_from_chars( - 0xff, 0x0e, 0x5b, 0x96, 0x52, 0x91, 0xc6, 0x08, - 0xc1, 0xe8, 0xcd, 0x26, 0x7e, 0xef, 0xc0, 0xaf, - 0xcc, 0x5e, 0x98, 0xa2, 0x78, 0x63, 0x73, 0xf0, - 0xdb, 0x47, 0xb0, 0x47, 0x86, 0xd7, 0x2a, 0xea, + chunk_t exp_client_handshake_iv = chunk_from_chars( + 0x71,0xab,0xc2,0xca,0xe4,0xc6,0x99,0xd4,0x7c,0x60,0x02,0x68, ); - chunk_t expected_server_handshake_traffic_secret = chunk_from_chars( - 0xa2, 0x06, 0x72, 0x65, 0xe7, 0xf0, 0x65, 0x2a, - 0x92, 0x3d, 0x5d, 0x72, 0xab, 0x04, 0x67, 0xc4, - 0x61, 0x32, 0xee, 0xb9, 0x68, 0xb6, 0xa3, 0x2d, - 0x31, 0x1c, 0x80, 0x58, 0x68, 0x54, 0x88, 0x14, + chunk_t exp_server_handshake_traffic_secret = chunk_from_chars( + 0xa2,0x06,0x72,0x65,0xe7,0xf0,0x65,0x2a,0x92,0x3d,0x5d,0x72,0xab,0x04,0x67,0xc4, + 0x61,0x32,0xee,0xb9,0x68,0xb6,0xa3,0x2d,0x31,0x1c,0x80,0x58,0x68,0x54,0x88,0x14, ); - chunk_t expected_client_handshake_key = chunk_from_chars( - 0x71, 0x54, 0xf3, 0x14, 0xe6, 0xbe, 0x7d, 0xc0, - 0x08, 0xdf, 0x2c, 0x83, 0x2b, 0xaa, 0x1d, 0x39, + chunk_t exp_server_handshake_key = chunk_from_chars( + 0x84,0x47,0x80,0xa7,0xac,0xad,0x9f,0x98,0x0f,0xa2,0x5c,0x11,0x4e,0x43,0x40,0x2a, ); - chunk_t expected_client_handshake_iv = chunk_from_chars( - 0x71, 0xab, 0xc2, 0xca, 0xe4, 0xc6, 0x99, 0xd4, - 0x7c, 0x60, 0x02, 0x68, + chunk_t exp_server_handshake_iv = chunk_from_chars( + 0x4c,0x04,0x2d,0xdc,0x12,0x0a,0x38,0xd1,0x41,0x7f,0xc8,0x15, ); - chunk_t expected_server_handshake_key = chunk_from_chars( - 0x84, 0x47, 0x80, 0xa7, 0xac, 0xad, 0x9f, 0x98, - 0x0f, 0xa2, 0x5c, 0x11, 0x4e, 0x43, 0x40, 0x2a, + chunk_t exp_client_finished_key = chunk_from_chars( + 0x7c,0x60,0xf8,0xd6,0x34,0x6f,0x4a,0x96,0x91,0xd2,0xae,0x64,0x5a,0x78,0x85,0xe0, + 0x10,0x4a,0xdf,0xf9,0x8e,0xba,0x98,0x1c,0xa2,0xf9,0x9e,0xf6,0x2b,0xdd,0x8f,0xaa, ); - chunk_t expected_server_handshake_iv = chunk_from_chars( - 0x4c, 0x04, 0x2d, 0xdc, 0x12, 0x0a, 0x38, 0xd1, - 0x41, 0x7f, 0xc8, 0x15, + chunk_t exp_server_finished_key = chunk_from_chars( + 0xea,0x84,0xab,0xd2,0xad,0xa0,0xb5,0xc6,0x4c,0x08,0x07,0xa3,0x26,0xb6,0xfd,0x94, + 0xa9,0x59,0x7e,0x39,0xca,0x62,0x10,0x60,0x7c,0x0d,0x3c,0x8c,0x76,0x68,0x65,0x71, ); - chunk_t expected_client_finished_key = chunk_from_chars( - 0x7c, 0x60, 0xf8, 0xd6, 0x34, 0x6f, 0x4a, 0x96, - 0x91, 0xd2, 0xae, 0x64, 0x5a, 0x78, 0x85, 0xe0, - 0x10, 0x4a, 0xdf, 0xf9, 0x8e, 0xba, 0x98, 0x1c, - 0xa2, 0xf9, 0x9e, 0xf6, 0x2b, 0xdd, 0x8f, 0xaa, - ); - - chunk_t expected_server_finished_key = chunk_from_chars( - 0xea, 0x84, 0xab, 0xd2, 0xad, 0xa0, 0xb5, 0xc6, - 0x4c, 0x08, 0x07, 0xa3, 0x26, 0xb6, 0xfd, 0x94, - 0xa9, 0x59, 0x7e, 0x39, 0xca, 0x62, 0x10, 0x60, - 0x7c, 0x0d, 0x3c, 0x8c, 0x76, 0x68, 0x65, 0x71, - ); - - chunk_t c_secret, c_key, c_iv, s_secret, s_key, s_iv, c_finished, - s_finished; - tls_hkdf_t *hkdf = tls_hkdf_create(HASH_SHA256, chunk_empty); - hkdf->set_shared_secret(hkdf, ecdhe); + ck_assert(hkdf); + + hkdf->set_shared_secret(hkdf, ulfheim_ecdhe); /* Generate client handshake traffic secret */ - ck_assert(hkdf->generate_secret(hkdf, TLS_HKDF_C_HS_TRAFFIC, handshake, - &c_secret)); - ck_assert_chunk_eq(expected_client_handshake_traffic_secret, c_secret); + check_secret_key_iv(hkdf, TLS_HKDF_C_HS_TRAFFIC, ulfheim_client_server_hello, + FALSE, exp_client_handshake_traffic_secret, 16, 12, + exp_client_handshake_key, exp_client_handshake_iv); - ck_assert(hkdf->derive_key(hkdf, FALSE, 16, &c_key)); - ck_assert_chunk_eq(expected_client_handshake_key, c_key); - - ck_assert(hkdf->derive_iv(hkdf, FALSE, 12, &c_iv)); - ck_assert_chunk_eq(expected_client_handshake_iv, c_iv); - - ck_assert(hkdf->derive_finished(hkdf, TRUE, &c_finished)); - ck_assert_chunk_eq(expected_client_finished_key, c_finished); + check_finished(hkdf, FALSE, exp_client_finished_key); /* Generate server handshake traffic secret */ - ck_assert(hkdf->generate_secret(hkdf, TLS_HKDF_S_HS_TRAFFIC, handshake, - &s_secret)); - ck_assert_chunk_eq(expected_server_handshake_traffic_secret, s_secret); + check_secret_key_iv(hkdf, TLS_HKDF_S_HS_TRAFFIC, ulfheim_client_server_hello, + TRUE, exp_server_handshake_traffic_secret, 16, 12, + exp_server_handshake_key, exp_server_handshake_iv); - ck_assert(hkdf->derive_key(hkdf, TRUE, 16, &s_key)); - ck_assert_chunk_eq(expected_server_handshake_key, s_key); - - ck_assert(hkdf->derive_iv(hkdf, TRUE, 12, &s_iv)); - ck_assert_chunk_eq(expected_server_handshake_iv, s_iv); - - ck_assert(hkdf->derive_finished(hkdf, TRUE, &s_finished)); - ck_assert_chunk_eq(expected_server_finished_key, s_finished); + check_finished(hkdf, TRUE, exp_server_finished_key); hkdf->destroy(hkdf); - - chunk_free(&c_secret); - chunk_free(&c_key); - chunk_free(&c_iv); - chunk_free(&s_secret); - chunk_free(&s_key); - chunk_free(&s_iv); - chunk_free(&c_finished); - chunk_free(&s_finished); } END_TEST START_TEST(test_ulfheim_traffic) { - chunk_t handshake = chunk_from_chars( - /* Client Hello */ - 0x01, 0x00, 0x00, 0xc6, 0x03, 0x03, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, - 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, - 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, - 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0xe0, - 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, - 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, - 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x00, - 0x06, 0x13, 0x01, 0x13, 0x02, 0x13, 0x03, 0x01, - 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x18, 0x00, - 0x16, 0x00, 0x00, 0x13, 0x65, 0x78, 0x61, 0x6d, - 0x70, 0x6c, 0x65, 0x2e, 0x75, 0x6c, 0x66, 0x68, - 0x65, 0x69, 0x6d, 0x2e, 0x6e, 0x65, 0x74, 0x00, - 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, - 0x17, 0x00, 0x18, 0x00, 0x0d, 0x00, 0x14, 0x00, - 0x12, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, 0x05, - 0x03, 0x08, 0x05, 0x05, 0x01, 0x08, 0x06, 0x06, - 0x01, 0x02, 0x01, 0x00, 0x33, 0x00, 0x26, 0x00, - 0x24, 0x00, 0x1d, 0x00, 0x20, 0x35, 0x80, 0x72, - 0xd6, 0x36, 0x58, 0x80, 0xd1, 0xae, 0xea, 0x32, - 0x9a, 0xdf, 0x91, 0x21, 0x38, 0x38, 0x51, 0xed, - 0x21, 0xa2, 0x8e, 0x3b, 0x75, 0xe9, 0x65, 0xd0, - 0xd2, 0xcd, 0x16, 0x62, 0x54, 0x00, 0x2d, 0x00, - 0x02, 0x01, 0x01, 0x00, 0x2b, 0x00, 0x03, 0x02, - 0x03, 0x04, - /* Server Hello */ - 0x02, 0x00, 0x00, 0x76, 0x03, 0x03, 0x70, 0x71, - 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, - 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, - 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, - 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x20, 0xe0, - 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, - 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, - 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x13, - 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x24, - 0x00, 0x1d, 0x00, 0x20, 0x9f, 0xd7, 0xad, 0x6d, - 0xcf, 0xf4, 0x29, 0x8d, 0xd3, 0xf9, 0x6d, 0x5b, - 0x1b, 0x2a, 0xf9, 0x10, 0xa0, 0x53, 0x5b, 0x14, - 0x88, 0xd7, 0xf8, 0xfa, 0xbb, 0x34, 0x9a, 0x98, - 0x28, 0x80, 0xb6, 0x15, 0x00, 0x2b, 0x00, 0x02, - 0x03, 0x04, + chunk_t exp_client_application_traffic_secret = chunk_from_chars( + 0xb8,0x82,0x22,0x31,0xc1,0xd6,0x76,0xec,0xca,0x1c,0x11,0xff,0xf6,0x59,0x42,0x80, + 0x31,0x4d,0x03,0xa4,0xe9,0x1c,0xf1,0xaf,0x7f,0xe7,0x3f,0x8f,0x7b,0xe2,0xc1,0x1b, + ); + + chunk_t exp_client_application_key = chunk_from_chars( + 0x49,0x13,0x4b,0x95,0x32,0x8f,0x27,0x9f,0x01,0x83,0x86,0x05,0x89,0xac,0x67,0x07, + ); + + chunk_t exp_client_application_iv = chunk_from_chars( + 0xbc,0x4d,0xd5,0xf7,0xb9,0x8a,0xcf,0xf8,0x54,0x66,0x26,0x1d, + ); + + chunk_t exp_server_application_traffic_secret = chunk_from_chars( + 0x3f,0xc3,0x5e,0xa7,0x06,0x93,0x06,0x9a,0x27,0x79,0x56,0xaf,0xa2,0x3b,0x8f,0x45, + 0x43,0xce,0x68,0xac,0x59,0x5f,0x2a,0xac,0xe0,0x5c,0xd7,0xa1,0xc9,0x20,0x23,0xd5, + ); + + chunk_t exp_server_application_key = chunk_from_chars( + 0x0b,0x6d,0x22,0xc8,0xff,0x68,0x09,0x7e,0xa8,0x71,0xc6,0x72,0x07,0x37,0x73,0xbf, + ); + + chunk_t exp_server_application_iv = chunk_from_chars( + 0x1b,0x13,0xdd,0x9f,0x8d,0x8f,0x17,0x09,0x1d,0x34,0xb3,0x49, + ); + + chunk_t hs_data; + + tls_hkdf_t *hkdf = tls_hkdf_create(HASH_SHA256, chunk_empty); + ck_assert(hkdf); + + hkdf->set_shared_secret(hkdf, ulfheim_ecdhe); + + /* Generate client application traffic secret */ + hs_data = chunk_cata("cc", ulfheim_client_server_hello, ulfheim_server_data); + check_secret_key_iv(hkdf, TLS_HKDF_C_AP_TRAFFIC, hs_data, FALSE, + exp_client_application_traffic_secret, 16, 12, + exp_client_application_key, exp_client_application_iv); + + /* Generate server application traffic secret */ + check_secret_key_iv(hkdf, TLS_HKDF_S_AP_TRAFFIC, hs_data, TRUE, + exp_server_application_traffic_secret, 16, 12, + exp_server_application_key, exp_server_application_iv); + + hkdf->destroy(hkdf); +} +END_TEST + +START_TEST(test_rfc8448_simple_1_rtt_handshake) +{ + chunk_t client_hello = chunk_from_chars( + 0x01,0x00,0x00,0xc0,0x03,0x03,0xcb,0x34,0xec,0xb1,0xe7,0x81,0x63,0xba,0x1c,0x38, + 0xc6,0xda,0xcb,0x19,0x6a,0x6d,0xff,0xa2,0x1a,0x8d,0x99,0x12,0xec,0x18,0xa2,0xef, + 0x62,0x83,0x02,0x4d,0xec,0xe7,0x00,0x00,0x06,0x13,0x01,0x13,0x03,0x13,0x02,0x01, + 0x00,0x00,0x91,0x00,0x00,0x00,0x0b,0x00,0x09,0x00,0x00,0x06,0x73,0x65,0x72,0x76, + 0x65,0x72,0xff,0x01,0x00,0x01,0x00,0x00,0x0a,0x00,0x14,0x00,0x12,0x00,0x1d,0x00, + 0x17,0x00,0x18,0x00,0x19,0x01,0x00,0x01,0x01,0x01,0x02,0x01,0x03,0x01,0x04,0x00, + 0x23,0x00,0x00,0x00,0x33,0x00,0x26,0x00,0x24,0x00,0x1d,0x00,0x20,0x99,0x38,0x1d, + 0xe5,0x60,0xe4,0xbd,0x43,0xd2,0x3d,0x8e,0x43,0x5a,0x7d,0xba,0xfe,0xb3,0xc0,0x6e, + 0x51,0xc1,0x3c,0xae,0x4d,0x54,0x13,0x69,0x1e,0x52,0x9a,0xaf,0x2c,0x00,0x2b,0x00, + 0x03,0x02,0x03,0x04,0x00,0x0d,0x00,0x20,0x00,0x1e,0x04,0x03,0x05,0x03,0x06,0x03, + 0x02,0x03,0x08,0x04,0x08,0x05,0x08,0x06,0x04,0x01,0x05,0x01,0x06,0x01,0x02,0x01, + 0x04,0x02,0x05,0x02,0x06,0x02,0x02,0x02,0x00,0x2d,0x00,0x02,0x01,0x01,0x00,0x1c, + 0x00,0x02,0x40,0x01, + ); + + chunk_t server_hello = chunk_from_chars( + 0x02,0x00,0x00,0x56,0x03,0x03,0xa6,0xaf,0x06,0xa4,0x12,0x18,0x60,0xdc,0x5e,0x6e, + 0x60,0x24,0x9c,0xd3,0x4c,0x95,0x93,0x0c,0x8a,0xc5,0xcb,0x14,0x34,0xda,0xc1,0x55, + 0x77,0x2e,0xd3,0xe2,0x69,0x28,0x00,0x13,0x01,0x00,0x00,0x2e,0x00,0x33,0x00,0x24, + 0x00,0x1d,0x00,0x20,0xc9,0x82,0x88,0x76,0x11,0x20,0x95,0xfe,0x66,0x76,0x2b,0xdb, + 0xf7,0xc6,0x72,0xe1,0x56,0xd6,0xcc,0x25,0x3b,0x83,0x3d,0xf1,0xdd,0x69,0xb1,0xb0, + 0x4e,0x75,0x1f,0x0f,0x00,0x2b,0x00,0x02,0x03,0x04, + ); + + chunk_t server_data = chunk_from_chars( /* Server Encrypted Extension */ - 0x08, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x08,0x00,0x00,0x24,0x00,0x22,0x00,0x0a,0x00,0x14,0x00,0x12,0x00,0x1d,0x00,0x17, + 0x00,0x18,0x00,0x19,0x01,0x00,0x01,0x01,0x01,0x02,0x01,0x03,0x01,0x04,0x00,0x1c, + 0x00,0x02,0x40,0x01,0x00,0x00,0x00,0x00, /* Server Certificate */ - 0x0b, 0x00, 0x03, 0x2e, 0x00, 0x00, 0x03, 0x2a, - 0x00, 0x03, 0x25, 0x30, 0x82, 0x03, 0x21, 0x30, - 0x82, 0x02, 0x09, 0xa0, 0x03, 0x02, 0x01, 0x02, - 0x02, 0x08, 0x15, 0x5a, 0x92, 0xad, 0xc2, 0x04, - 0x8f, 0x90, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, - 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, - 0x00, 0x30, 0x22, 0x31, 0x0b, 0x30, 0x09, 0x06, - 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, - 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, - 0x0a, 0x13, 0x0a, 0x45, 0x78, 0x61, 0x6d, 0x70, - 0x6c, 0x65, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, - 0x0d, 0x31, 0x38, 0x31, 0x30, 0x30, 0x35, 0x30, - 0x31, 0x33, 0x38, 0x31, 0x37, 0x5a, 0x17, 0x0d, - 0x31, 0x39, 0x31, 0x30, 0x30, 0x35, 0x30, 0x31, - 0x33, 0x38, 0x31, 0x37, 0x5a, 0x30, 0x2b, 0x31, - 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, - 0x13, 0x02, 0x55, 0x53, 0x31, 0x1c, 0x30, 0x1a, - 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x13, 0x65, - 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x75, - 0x6c, 0x66, 0x68, 0x65, 0x69, 0x6d, 0x2e, 0x6e, - 0x65, 0x74, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, - 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, - 0x01, 0x01, 0x00, 0xc4, 0x80, 0x36, 0x06, 0xba, - 0xe7, 0x47, 0x6b, 0x08, 0x94, 0x04, 0xec, 0xa7, - 0xb6, 0x91, 0x04, 0x3f, 0xf7, 0x92, 0xbc, 0x19, - 0xee, 0xfb, 0x7d, 0x74, 0xd7, 0xa8, 0x0d, 0x00, - 0x1e, 0x7b, 0x4b, 0x3a, 0x4a, 0xe6, 0x0f, 0xe8, - 0xc0, 0x71, 0xfc, 0x73, 0xe7, 0x02, 0x4c, 0x0d, - 0xbc, 0xf4, 0xbd, 0xd1, 0x1d, 0x39, 0x6b, 0xba, - 0x70, 0x46, 0x4a, 0x13, 0xe9, 0x4a, 0xf8, 0x3d, - 0xf3, 0xe1, 0x09, 0x59, 0x54, 0x7b, 0xc9, 0x55, - 0xfb, 0x41, 0x2d, 0xa3, 0x76, 0x52, 0x11, 0xe1, - 0xf3, 0xdc, 0x77, 0x6c, 0xaa, 0x53, 0x37, 0x6e, - 0xca, 0x3a, 0xec, 0xbe, 0xc3, 0xaa, 0xb7, 0x3b, - 0x31, 0xd5, 0x6c, 0xb6, 0x52, 0x9c, 0x80, 0x98, - 0xbc, 0xc9, 0xe0, 0x28, 0x18, 0xe2, 0x0b, 0xf7, - 0xf8, 0xa0, 0x3a, 0xfd, 0x17, 0x04, 0x50, 0x9e, - 0xce, 0x79, 0xbd, 0x9f, 0x39, 0xf1, 0xea, 0x69, - 0xec, 0x47, 0x97, 0x2e, 0x83, 0x0f, 0xb5, 0xca, - 0x95, 0xde, 0x95, 0xa1, 0xe6, 0x04, 0x22, 0xd5, - 0xee, 0xbe, 0x52, 0x79, 0x54, 0xa1, 0xe7, 0xbf, - 0x8a, 0x86, 0xf6, 0x46, 0x6d, 0x0d, 0x9f, 0x16, - 0x95, 0x1a, 0x4c, 0xf7, 0xa0, 0x46, 0x92, 0x59, - 0x5c, 0x13, 0x52, 0xf2, 0x54, 0x9e, 0x5a, 0xfb, - 0x4e, 0xbf, 0xd7, 0x7a, 0x37, 0x95, 0x01, 0x44, - 0xe4, 0xc0, 0x26, 0x87, 0x4c, 0x65, 0x3e, 0x40, - 0x7d, 0x7d, 0x23, 0x07, 0x44, 0x01, 0xf4, 0x84, - 0xff, 0xd0, 0x8f, 0x7a, 0x1f, 0xa0, 0x52, 0x10, - 0xd1, 0xf4, 0xf0, 0xd5, 0xce, 0x79, 0x70, 0x29, - 0x32, 0xe2, 0xca, 0xbe, 0x70, 0x1f, 0xdf, 0xad, - 0x6b, 0x4b, 0xb7, 0x11, 0x01, 0xf4, 0x4b, 0xad, - 0x66, 0x6a, 0x11, 0x13, 0x0f, 0xe2, 0xee, 0x82, - 0x9e, 0x4d, 0x02, 0x9d, 0xc9, 0x1c, 0xdd, 0x67, - 0x16, 0xdb, 0xb9, 0x06, 0x18, 0x86, 0xed, 0xc1, - 0xba, 0x94, 0x21, 0x02, 0x03, 0x01, 0x00, 0x01, - 0xa3, 0x52, 0x30, 0x50, 0x30, 0x0e, 0x06, 0x03, - 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, - 0x03, 0x02, 0x05, 0xa0, 0x30, 0x1d, 0x06, 0x03, - 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, - 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, - 0x02, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, - 0x07, 0x03, 0x01, 0x30, 0x1f, 0x06, 0x03, 0x55, - 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, - 0x89, 0x4f, 0xde, 0x5b, 0xcc, 0x69, 0xe2, 0x52, - 0xcf, 0x3e, 0xa3, 0x00, 0xdf, 0xb1, 0x97, 0xb8, - 0x1d, 0xe1, 0xc1, 0x46, 0x30, 0x0d, 0x06, 0x09, - 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, - 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, - 0x59, 0x16, 0x45, 0xa6, 0x9a, 0x2e, 0x37, 0x79, - 0xe4, 0xf6, 0xdd, 0x27, 0x1a, 0xba, 0x1c, 0x0b, - 0xfd, 0x6c, 0xd7, 0x55, 0x99, 0xb5, 0xe7, 0xc3, - 0x6e, 0x53, 0x3e, 0xff, 0x36, 0x59, 0x08, 0x43, - 0x24, 0xc9, 0xe7, 0xa5, 0x04, 0x07, 0x9d, 0x39, - 0xe0, 0xd4, 0x29, 0x87, 0xff, 0xe3, 0xeb, 0xdd, - 0x09, 0xc1, 0xcf, 0x1d, 0x91, 0x44, 0x55, 0x87, - 0x0b, 0x57, 0x1d, 0xd1, 0x9b, 0xdf, 0x1d, 0x24, - 0xf8, 0xbb, 0x9a, 0x11, 0xfe, 0x80, 0xfd, 0x59, - 0x2b, 0xa0, 0x39, 0x8c, 0xde, 0x11, 0xe2, 0x65, - 0x1e, 0x61, 0x8c, 0xe5, 0x98, 0xfa, 0x96, 0xe5, - 0x37, 0x2e, 0xef, 0x3d, 0x24, 0x8a, 0xfd, 0xe1, - 0x74, 0x63, 0xeb, 0xbf, 0xab, 0xb8, 0xe4, 0xd1, - 0xab, 0x50, 0x2a, 0x54, 0xec, 0x00, 0x64, 0xe9, - 0x2f, 0x78, 0x19, 0x66, 0x0d, 0x3f, 0x27, 0xcf, - 0x20, 0x9e, 0x66, 0x7f, 0xce, 0x5a, 0xe2, 0xe4, - 0xac, 0x99, 0xc7, 0xc9, 0x38, 0x18, 0xf8, 0xb2, - 0x51, 0x07, 0x22, 0xdf, 0xed, 0x97, 0xf3, 0x2e, - 0x3e, 0x93, 0x49, 0xd4, 0xc6, 0x6c, 0x9e, 0xa6, - 0x39, 0x6d, 0x74, 0x44, 0x62, 0xa0, 0x6b, 0x42, - 0xc6, 0xd5, 0xba, 0x68, 0x8e, 0xac, 0x3a, 0x01, - 0x7b, 0xdd, 0xfc, 0x8e, 0x2c, 0xfc, 0xad, 0x27, - 0xcb, 0x69, 0xd3, 0xcc, 0xdc, 0xa2, 0x80, 0x41, - 0x44, 0x65, 0xd3, 0xae, 0x34, 0x8c, 0xe0, 0xf3, - 0x4a, 0xb2, 0xfb, 0x9c, 0x61, 0x83, 0x71, 0x31, - 0x2b, 0x19, 0x10, 0x41, 0x64, 0x1c, 0x23, 0x7f, - 0x11, 0xa5, 0xd6, 0x5c, 0x84, 0x4f, 0x04, 0x04, - 0x84, 0x99, 0x38, 0x71, 0x2b, 0x95, 0x9e, 0xd6, - 0x85, 0xbc, 0x5c, 0x5d, 0xd6, 0x45, 0xed, 0x19, - 0x90, 0x94, 0x73, 0x40, 0x29, 0x26, 0xdc, 0xb4, - 0x0e, 0x34, 0x69, 0xa1, 0x59, 0x41, 0xe8, 0xe2, - 0xcc, 0xa8, 0x4b, 0xb6, 0x08, 0x46, 0x36, 0xa0, - 0x00, 0x00, + 0x0b,0x00,0x01,0xb9,0x00,0x00,0x01,0xb5,0x00,0x01,0xb0,0x30,0x82,0x01,0xac,0x30, + 0x82,0x01,0x15,0xa0,0x03,0x02,0x01,0x02,0x02,0x01,0x02,0x30,0x0d,0x06,0x09,0x2a, + 0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b,0x05,0x00,0x30,0x0e,0x31,0x0c,0x30,0x0a, + 0x06,0x03,0x55,0x04,0x03,0x13,0x03,0x72,0x73,0x61,0x30,0x1e,0x17,0x0d,0x31,0x36, + 0x30,0x37,0x33,0x30,0x30,0x31,0x32,0x33,0x35,0x39,0x5a,0x17,0x0d,0x32,0x36,0x30, + 0x37,0x33,0x30,0x30,0x31,0x32,0x33,0x35,0x39,0x5a,0x30,0x0e,0x31,0x0c,0x30,0x0a, + 0x06,0x03,0x55,0x04,0x03,0x13,0x03,0x72,0x73,0x61,0x30,0x81,0x9f,0x30,0x0d,0x06, + 0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x01,0x05,0x00,0x03,0x81,0x8d,0x00, + 0x30,0x81,0x89,0x02,0x81,0x81,0x00,0xb4,0xbb,0x49,0x8f,0x82,0x79,0x30,0x3d,0x98, + 0x08,0x36,0x39,0x9b,0x36,0xc6,0x98,0x8c,0x0c,0x68,0xde,0x55,0xe1,0xbd,0xb8,0x26, + 0xd3,0x90,0x1a,0x24,0x61,0xea,0xfd,0x2d,0xe4,0x9a,0x91,0xd0,0x15,0xab,0xbc,0x9a, + 0x95,0x13,0x7a,0xce,0x6c,0x1a,0xf1,0x9e,0xaa,0x6a,0xf9,0x8c,0x7c,0xed,0x43,0x12, + 0x09,0x98,0xe1,0x87,0xa8,0x0e,0xe0,0xcc,0xb0,0x52,0x4b,0x1b,0x01,0x8c,0x3e,0x0b, + 0x63,0x26,0x4d,0x44,0x9a,0x6d,0x38,0xe2,0x2a,0x5f,0xda,0x43,0x08,0x46,0x74,0x80, + 0x30,0x53,0x0e,0xf0,0x46,0x1c,0x8c,0xa9,0xd9,0xef,0xbf,0xae,0x8e,0xa6,0xd1,0xd0, + 0x3e,0x2b,0xd1,0x93,0xef,0xf0,0xab,0x9a,0x80,0x02,0xc4,0x74,0x28,0xa6,0xd3,0x5a, + 0x8d,0x88,0xd7,0x9f,0x7f,0x1e,0x3f,0x02,0x03,0x01,0x00,0x01,0xa3,0x1a,0x30,0x18, + 0x30,0x09,0x06,0x03,0x55,0x1d,0x13,0x04,0x02,0x30,0x00,0x30,0x0b,0x06,0x03,0x55, + 0x1d,0x0f,0x04,0x04,0x03,0x02,0x05,0xa0,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86, + 0xf7,0x0d,0x01,0x01,0x0b,0x05,0x00,0x03,0x81,0x81,0x00,0x85,0xaa,0xd2,0xa0,0xe5, + 0xb9,0x27,0x6b,0x90,0x8c,0x65,0xf7,0x3a,0x72,0x67,0x17,0x06,0x18,0xa5,0x4c,0x5f, + 0x8a,0x7b,0x33,0x7d,0x2d,0xf7,0xa5,0x94,0x36,0x54,0x17,0xf2,0xea,0xe8,0xf8,0xa5, + 0x8c,0x8f,0x81,0x72,0xf9,0x31,0x9c,0xf3,0x6b,0x7f,0xd6,0xc5,0x5b,0x80,0xf2,0x1a, + 0x03,0x01,0x51,0x56,0x72,0x60,0x96,0xfd,0x33,0x5e,0x5e,0x67,0xf2,0xdb,0xf1,0x02, + 0x70,0x2e,0x60,0x8c,0xca,0xe6,0xbe,0xc1,0xfc,0x63,0xa4,0x2a,0x99,0xbe,0x5c,0x3e, + 0xb7,0x10,0x7c,0x3c,0x54,0xe9,0xb9,0xeb,0x2b,0xd5,0x20,0x3b,0x1c,0x3b,0x84,0xe0, + 0xa8,0xb2,0xf7,0x59,0x40,0x9b,0xa3,0xea,0xc9,0xd9,0x1d,0x40,0x2d,0xcc,0x0c,0xc8, + 0xf8,0x96,0x12,0x29,0xac,0x91,0x87,0xb4,0x2b,0x4d,0xe1,0x00,0x00, /* Server Certificate Verify */ - 0x0f, 0x00, 0x01, 0x04, 0x08, 0x04, 0x01, 0x00, - 0x17, 0xfe, 0xb5, 0x33, 0xca, 0x6d, 0x00, 0x7d, - 0x00, 0x58, 0x25, 0x79, 0x68, 0x42, 0x4b, 0xbc, - 0x3a, 0xa6, 0x90, 0x9e, 0x9d, 0x49, 0x55, 0x75, - 0x76, 0xa5, 0x20, 0xe0, 0x4a, 0x5e, 0xf0, 0x5f, - 0x0e, 0x86, 0xd2, 0x4f, 0xf4, 0x3f, 0x8e, 0xb8, - 0x61, 0xee, 0xf5, 0x95, 0x22, 0x8d, 0x70, 0x32, - 0xaa, 0x36, 0x0f, 0x71, 0x4e, 0x66, 0x74, 0x13, - 0x92, 0x6e, 0xf4, 0xf8, 0xb5, 0x80, 0x3b, 0x69, - 0xe3, 0x55, 0x19, 0xe3, 0xb2, 0x3f, 0x43, 0x73, - 0xdf, 0xac, 0x67, 0x87, 0x06, 0x6d, 0xcb, 0x47, - 0x56, 0xb5, 0x45, 0x60, 0xe0, 0x88, 0x6e, 0x9b, - 0x96, 0x2c, 0x4a, 0xd2, 0x8d, 0xab, 0x26, 0xba, - 0xd1, 0xab, 0xc2, 0x59, 0x16, 0xb0, 0x9a, 0xf2, - 0x86, 0x53, 0x7f, 0x68, 0x4f, 0x80, 0x8a, 0xef, - 0xee, 0x73, 0x04, 0x6c, 0xb7, 0xdf, 0x0a, 0x84, - 0xfb, 0xb5, 0x96, 0x7a, 0xca, 0x13, 0x1f, 0x4b, - 0x1c, 0xf3, 0x89, 0x79, 0x94, 0x03, 0xa3, 0x0c, - 0x02, 0xd2, 0x9c, 0xbd, 0xad, 0xb7, 0x25, 0x12, - 0xdb, 0x9c, 0xec, 0x2e, 0x5e, 0x1d, 0x00, 0xe5, - 0x0c, 0xaf, 0xcf, 0x6f, 0x21, 0x09, 0x1e, 0xbc, - 0x4f, 0x25, 0x3c, 0x5e, 0xab, 0x01, 0xa6, 0x79, - 0xba, 0xea, 0xbe, 0xed, 0xb9, 0xc9, 0x61, 0x8f, - 0x66, 0x00, 0x6b, 0x82, 0x44, 0xd6, 0x62, 0x2a, - 0xaa, 0x56, 0x88, 0x7c, 0xcf, 0xc6, 0x6a, 0x0f, - 0x38, 0x51, 0xdf, 0xa1, 0x3a, 0x78, 0xcf, 0xf7, - 0x99, 0x1e, 0x03, 0xcb, 0x2c, 0x3a, 0x0e, 0xd8, - 0x7d, 0x73, 0x67, 0x36, 0x2e, 0xb7, 0x80, 0x5b, - 0x00, 0xb2, 0x52, 0x4f, 0xf2, 0x98, 0xa4, 0xda, - 0x48, 0x7c, 0xac, 0xde, 0xaf, 0x8a, 0x23, 0x36, - 0xc5, 0x63, 0x1b, 0x3e, 0xfa, 0x93, 0x5b, 0xb4, - 0x11, 0xe7, 0x53, 0xca, 0x13, 0xb0, 0x15, 0xfe, - 0xc7, 0xe4, 0xa7, 0x30, 0xf1, 0x36, 0x9f, 0x9e, + 0x0f,0x00,0x00,0x84,0x08,0x04,0x00,0x80,0x5a,0x74,0x7c,0x5d,0x88,0xfa,0x9b,0xd2, + 0xe5,0x5a,0xb0,0x85,0xa6,0x10,0x15,0xb7,0x21,0x1f,0x82,0x4c,0xd4,0x84,0x14,0x5a, + 0xb3,0xff,0x52,0xf1,0xfd,0xa8,0x47,0x7b,0x0b,0x7a,0xbc,0x90,0xdb,0x78,0xe2,0xd3, + 0x3a,0x5c,0x14,0x1a,0x07,0x86,0x53,0xfa,0x6b,0xef,0x78,0x0c,0x5e,0xa2,0x48,0xee, + 0xaa,0xa7,0x85,0xc4,0xf3,0x94,0xca,0xb6,0xd3,0x0b,0xbe,0x8d,0x48,0x59,0xee,0x51, + 0x1f,0x60,0x29,0x57,0xb1,0x54,0x11,0xac,0x02,0x76,0x71,0x45,0x9e,0x46,0x44,0x5c, + 0x9e,0xa5,0x8c,0x18,0x1e,0x81,0x8e,0x95,0xb8,0xc3,0xfb,0x0b,0xf3,0x27,0x84,0x09, + 0xd3,0xbe,0x15,0x2a,0x3d,0xa5,0x04,0x3e,0x06,0x3d,0xda,0x65,0xcd,0xf5,0xae,0xa2, + 0x0d,0x53,0xdf,0xac,0xd4,0x2f,0x74,0xf3, /* Server Handshake Finish */ - 0x14, 0x00, 0x00, 0x20, 0xea, 0x6e, 0xe1, 0x76, - 0xdc, 0xcc, 0x4a, 0xf1, 0x85, 0x9e, 0x9e, 0x4e, - 0x93, 0xf7, 0x97, 0xea, 0xc9, 0xa7, 0x8c, 0xe4, - 0x39, 0x30, 0x1e, 0x35, 0x27, 0x5a, 0xd4, 0x3f, - 0x3c, 0xdd, 0xbd, 0xe3, + 0x14,0x00,0x00,0x20,0x9b,0x9b,0x14,0x1d,0x90,0x63,0x37,0xfb,0xd2,0xcb,0xdc,0xe7, + 0x1d,0xf4,0xde,0xda,0x4a,0xb4,0x2c,0x30,0x95,0x72,0xcb,0x7f,0xff,0xee,0x54,0x54, + 0xb7,0x8f,0x07,0x18, + ); + + chunk_t client_finished = chunk_from_chars( + 0x14,0x00,0x00,0x20,0xa8,0xec,0x43,0x6d,0x67,0x76,0x34,0xae,0x52,0x5a,0xc1,0xfc, + 0xeb,0xe1,0x1a,0x03,0x9e,0xc1,0x76,0x94,0xfa,0xc6,0xe9,0x85,0x27,0xb6,0x42,0xf2, + 0xed,0xd5,0xce,0x61, ); chunk_t ecdhe = chunk_from_chars( - 0xdf, 0x4a, 0x29, 0x1b, 0xaa, 0x1e, 0xb7, 0xcf, - 0xa6, 0x93, 0x4b, 0x29, 0xb4, 0x74, 0xba, 0xad, - 0x26, 0x97, 0xe2, 0x9f, 0x1f, 0x92, 0x0d, 0xcc, - 0x77, 0xc8, 0xa0, 0xa0, 0x88, 0x44, 0x76, 0x24, + 0x8b,0xd4,0x05,0x4f,0xb5,0x5b,0x9d,0x63,0xfd,0xfb,0xac,0xf9,0xf0,0x4b,0x9f,0x0d, + 0x35,0xe6,0xd6,0x3f,0x53,0x75,0x63,0xef,0xd4,0x62,0x72,0x90,0x0f,0x89,0x49,0x2d, ); - chunk_t expected_client_application_key = chunk_from_chars( - 0x49, 0x13, 0x4b, 0x95, 0x32, 0x8f, 0x27, 0x9f, - 0x01, 0x83, 0x86, 0x05, 0x89, 0xac, 0x67, 0x07, + chunk_t exp_client_handshake_traffic_secret = chunk_from_chars( + 0xb3,0xed,0xdb,0x12,0x6e,0x06,0x7f,0x35,0xa7,0x80,0xb3,0xab,0xf4,0x5e,0x2d,0x8f, + 0x3b,0x1a,0x95,0x07,0x38,0xf5,0x2e,0x96,0x00,0x74,0x6a,0x0e,0x27,0xa5,0x5a,0x21, ); - chunk_t expected_client_application_iv = chunk_from_chars( - 0xbc, 0x4d, 0xd5, 0xf7, 0xb9, 0x8a, 0xcf, 0xf8, - 0x54, 0x66, 0x26, 0x1d, + chunk_t exp_client_handshake_key = chunk_from_chars( + 0xdb,0xfa,0xa6,0x93,0xd1,0x76,0x2c,0x5b,0x66,0x6a,0xf5,0xd9,0x50,0x25,0x8d,0x01, ); - chunk_t expected_server_application_key = chunk_from_chars( - 0x0b, 0x6d, 0x22, 0xc8, 0xff, 0x68, 0x09, 0x7e, - 0xa8, 0x71, 0xc6, 0x72, 0x07, 0x37, 0x73, 0xbf, + chunk_t exp_client_handshake_iv = chunk_from_chars( + 0x5b,0xd3,0xc7,0x1b,0x83,0x6e,0x0b,0x76,0xbb,0x73,0x26,0x5f, ); - chunk_t expected_server_application_iv = chunk_from_chars( - 0x1b, 0x13, 0xdd, 0x9f, 0x8d, 0x8f, 0x17, 0x09, - 0x1d, 0x34, 0xb3, 0x49, + chunk_t exp_server_handshake_traffic_secret = chunk_from_chars( + 0xb6,0x7b,0x7d,0x69,0x0c,0xc1,0x6c,0x4e,0x75,0xe5,0x42,0x13,0xcb,0x2d,0x37,0xb4, + 0xe9,0xc9,0x12,0xbc,0xde,0xd9,0x10,0x5d,0x42,0xbe,0xfd,0x59,0xd3,0x91,0xad,0x38, ); - chunk_t c_key, c_iv, s_key, s_iv; + chunk_t exp_server_handshake_key = chunk_from_chars( + 0x3f,0xce,0x51,0x60,0x09,0xc2,0x17,0x27,0xd0,0xf2,0xe4,0xe8,0x6e,0xe4,0x03,0xbc, + ); + + chunk_t exp_server_handshake_iv = chunk_from_chars( + 0x5d,0x31,0x3e,0xb2,0x67,0x12,0x76,0xee,0x13,0x00,0x0b,0x30, + ); + + chunk_t exp_client_finished_key = chunk_from_chars( + 0xb8,0x0a,0xd0,0x10,0x15,0xfb,0x2f,0x0b,0xd6,0x5f,0xf7,0xd4,0xda,0x5d,0x6b,0xf8, + 0x3f,0x84,0x82,0x1d,0x1f,0x87,0xfd,0xc7,0xd3,0xc7,0x5b,0x5a,0x7b,0x42,0xd9,0xc4, + ); + + chunk_t exp_server_finished_key = chunk_from_chars( + 0x00,0x8d,0x3b,0x66,0xf8,0x16,0xea,0x55,0x9f,0x96,0xb5,0x37,0xe8,0x85,0xc3,0x1f, + 0xc0,0x68,0xbf,0x49,0x2c,0x65,0x2f,0x01,0xf2,0x88,0xa1,0xd8,0xcd,0xc1,0x9f,0xc8, + ); + + chunk_t exp_client_application_traffic_secret = chunk_from_chars( + 0x9e,0x40,0x64,0x6c,0xe7,0x9a,0x7f,0x9d,0xc0,0x5a,0xf8,0x88,0x9b,0xce,0x65,0x52, + 0x87,0x5a,0xfa,0x0b,0x06,0xdf,0x00,0x87,0xf7,0x92,0xeb,0xb7,0xc1,0x75,0x04,0xa5, + ); + + chunk_t exp_client_application_key = chunk_from_chars( + 0x17,0x42,0x2d,0xda,0x59,0x6e,0xd5,0xd9,0xac,0xd8,0x90,0xe3,0xc6,0x3f,0x50,0x51, + ); + + chunk_t exp_client_application_iv = chunk_from_chars( + 0x5b,0x78,0x92,0x3d,0xee,0x08,0x57,0x90,0x33,0xe5,0x23,0xd9, + ); + + chunk_t exp_server_application_traffic_secret = chunk_from_chars( + 0xa1,0x1a,0xf9,0xf0,0x55,0x31,0xf8,0x56,0xad,0x47,0x11,0x6b,0x45,0xa9,0x50,0x32, + 0x82,0x04,0xb4,0xf4,0x4b,0xfb,0x6b,0x3a,0x4b,0x4f,0x1f,0x3f,0xcb,0x63,0x16,0x43, + ); + + chunk_t exp_server_application_key = chunk_from_chars( + 0x9f,0x02,0x28,0x3b,0x6c,0x9c,0x07,0xef,0xc2,0x6b,0xb9,0xf2,0xac,0x92,0xe3,0x56, + ); + + chunk_t exp_server_application_iv = chunk_from_chars( + 0xcf,0x78,0x2b,0x88,0xdd,0x83,0x54,0x9a,0xad,0xf1,0xe9,0x84, + ); + + chunk_t exp_generated_resumption_secret = chunk_from_chars( + 0x4e,0xcd,0x0e,0xb6,0xec,0x3b,0x4d,0x87,0xf5,0xd6,0x02,0x8f,0x92,0x2c,0xa4,0xc5, + 0x85,0x1a,0x27,0x7f,0xd4,0x13,0x11,0xc9,0xe6,0x2d,0x2c,0x94,0x92,0xe1,0xc4,0xf3, + ); + + chunk_t hs_data; tls_hkdf_t *hkdf = tls_hkdf_create(HASH_SHA256, chunk_empty); + ck_assert(hkdf); + hkdf->set_shared_secret(hkdf, ecdhe); + /* Generate client handshake traffic secret */ + hs_data = chunk_cata("cc", client_hello, server_hello); + check_secret_key_iv(hkdf, TLS_HKDF_C_HS_TRAFFIC, hs_data, FALSE, + exp_client_handshake_traffic_secret, 16, 12, + exp_client_handshake_key, + exp_client_handshake_iv); + check_finished(hkdf, FALSE, exp_client_finished_key); + + /* Generate server handshake traffic secret */ + check_secret_key_iv(hkdf, TLS_HKDF_S_HS_TRAFFIC, hs_data, TRUE, + exp_server_handshake_traffic_secret, 16, 12, + exp_server_handshake_key, + exp_server_handshake_iv); + check_finished(hkdf, TRUE, exp_server_finished_key); + /* Generate client application traffic secret */ - ck_assert(hkdf->generate_secret(hkdf, TLS_HKDF_C_AP_TRAFFIC, handshake, NULL)); - - ck_assert(hkdf->derive_key(hkdf, FALSE, 16, &c_key)); - ck_assert_chunk_eq(expected_client_application_key, c_key); - - ck_assert(hkdf->derive_iv(hkdf, FALSE, 12, &c_iv)); - ck_assert_chunk_eq(expected_client_application_iv, c_iv); + hs_data = chunk_cata("cc", hs_data, server_data); + check_secret_key_iv(hkdf, TLS_HKDF_C_AP_TRAFFIC, hs_data, FALSE, + exp_client_application_traffic_secret, 16, 12, + exp_client_application_key, + exp_client_application_iv); /* Generate server application traffic secret */ - ck_assert(hkdf->generate_secret(hkdf, TLS_HKDF_S_AP_TRAFFIC, handshake, NULL)); + check_secret_key_iv(hkdf, TLS_HKDF_S_AP_TRAFFIC, hs_data, TRUE, + exp_server_application_traffic_secret, 16, 12, + exp_server_application_key, + exp_server_application_iv); - ck_assert(hkdf->derive_key(hkdf, TRUE, 16, &s_key)); - ck_assert_chunk_eq(expected_server_application_key, s_key); - - ck_assert(hkdf->derive_iv(hkdf, TRUE, 12, &s_iv)); - ck_assert_chunk_eq(expected_server_application_iv, s_iv); + /* Generating resumption master secret */ + hs_data = chunk_cata("cc", hs_data, client_finished); + check_resumption(hkdf, hs_data, exp_generated_resumption_secret); hkdf->destroy(hkdf); +} +END_TEST - chunk_free(&c_key); - chunk_free(&c_iv); - chunk_free(&s_key); - chunk_free(&s_iv); +START_TEST(test_rfc8448_resumed_0_rtt_handshake) +{ + chunk_t client_hello = chunk_from_chars( + 0x01,0x00,0x01,0xfc,0x03,0x03,0x1b,0xc3,0xce,0xb6,0xbb,0xe3,0x9c,0xff,0x93,0x83, + 0x55,0xb5,0xa5,0x0a,0xdb,0x6d,0xb2,0x1b,0x7a,0x6a,0xf6,0x49,0xd7,0xb4,0xbc,0x41, + 0x9d,0x78,0x76,0x48,0x7d,0x95,0x00,0x00,0x06,0x13,0x01,0x13,0x03,0x13,0x02,0x01, + 0x00,0x01,0xcd,0x00,0x00,0x00,0x0b,0x00,0x09,0x00,0x00,0x06,0x73,0x65,0x72,0x76, + 0x65,0x72,0xff,0x01,0x00,0x01,0x00,0x00,0x0a,0x00,0x14,0x00,0x12,0x00,0x1d,0x00, + 0x17,0x00,0x18,0x00,0x19,0x01,0x00,0x01,0x01,0x01,0x02,0x01,0x03,0x01,0x04,0x00, + 0x33,0x00,0x26,0x00,0x24,0x00,0x1d,0x00,0x20,0xe4,0xff,0xb6,0x8a,0xc0,0x5f,0x8d, + 0x96,0xc9,0x9d,0xa2,0x66,0x98,0x34,0x6c,0x6b,0xe1,0x64,0x82,0xba,0xdd,0xda,0xfe, + 0x05,0x1a,0x66,0xb4,0xf1,0x8d,0x66,0x8f,0x0b,0x00,0x2a,0x00,0x00,0x00,0x2b,0x00, + 0x03,0x02,0x03,0x04,0x00,0x0d,0x00,0x20,0x00,0x1e,0x04,0x03,0x05,0x03,0x06,0x03, + 0x02,0x03,0x08,0x04,0x08,0x05,0x08,0x06,0x04,0x01,0x05,0x01,0x06,0x01,0x02,0x01, + 0x04,0x02,0x05,0x02,0x06,0x02,0x02,0x02,0x00,0x2d,0x00,0x02,0x01,0x01,0x00,0x1c, + 0x00,0x02,0x40,0x01,0x00,0x15,0x00,0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x29,0x00,0xdd,0x00,0xb8,0x00,0xb2,0x2c,0x03,0x5d,0x82,0x93,0x59,0xee,0x5f,0xf7, + 0xaf,0x4e,0xc9,0x00,0x00,0x00,0x00,0x26,0x2a,0x64,0x94,0xdc,0x48,0x6d,0x2c,0x8a, + 0x34,0xcb,0x33,0xfa,0x90,0xbf,0x1b,0x00,0x70,0xad,0x3c,0x49,0x88,0x83,0xc9,0x36, + 0x7c,0x09,0xa2,0xbe,0x78,0x5a,0xbc,0x55,0xcd,0x22,0x60,0x97,0xa3,0xa9,0x82,0x11, + 0x72,0x83,0xf8,0x2a,0x03,0xa1,0x43,0xef,0xd3,0xff,0x5d,0xd3,0x6d,0x64,0xe8,0x61, + 0xbe,0x7f,0xd6,0x1d,0x28,0x27,0xdb,0x27,0x9c,0xce,0x14,0x50,0x77,0xd4,0x54,0xa3, + 0x66,0x4d,0x4e,0x6d,0xa4,0xd2,0x9e,0xe0,0x37,0x25,0xa6,0xa4,0xda,0xfc,0xd0,0xfc, + 0x67,0xd2,0xae,0xa7,0x05,0x29,0x51,0x3e,0x3d,0xa2,0x67,0x7f,0xa5,0x90,0x6c,0x5b, + 0x3f,0x7d,0x8f,0x92,0xf2,0x28,0xbd,0xa4,0x0d,0xda,0x72,0x14,0x70,0xf9,0xfb,0xf2, + 0x97,0xb5,0xae,0xa6,0x17,0x64,0x6f,0xac,0x5c,0x03,0x27,0x2e,0x97,0x07,0x27,0xc6, + 0x21,0xa7,0x91,0x41,0xef,0x5f,0x7d,0xe6,0x50,0x5e,0x5b,0xfb,0xc3,0x88,0xe9,0x33, + 0x43,0x69,0x40,0x93,0x93,0x4a,0xe4,0xd3,0x57,0xfa,0xd6,0xaa,0xcb, + ); + + chunk_t client_hello_hash = chunk_from_chars( + 0x63,0x22,0x4b,0x2e,0x45,0x73,0xf2,0xd3,0x45,0x4c,0xa8,0x4b,0x9d,0x00,0x9a,0x04, + 0xf6,0xbe,0x9e,0x05,0x71,0x1a,0x83,0x96,0x47,0x3a,0xef,0xa0,0x1e,0x92,0x4a,0x14, + ); + + chunk_t server_hello = chunk_from_chars( + 0x02,0x00,0x00,0x5c,0x03,0x03,0x3c,0xcf,0xd2,0xde,0xc8,0x90,0x22,0x27,0x63,0x47, + 0x2a,0xe8,0x13,0x67,0x77,0xc9,0xd7,0x35,0x87,0x77,0xbb,0x66,0xe9,0x1e,0xa5,0x12, + 0x24,0x95,0xf5,0x59,0xea,0x2d,0x00,0x13,0x01,0x00,0x00,0x34,0x00,0x29,0x00,0x02, + 0x00,0x00,0x00,0x33,0x00,0x24,0x00,0x1d,0x00,0x20,0x12,0x17,0x61,0xee,0x42,0xc3, + 0x33,0xe1,0xb9,0xe7,0x7b,0x60,0xdd,0x57,0xc2,0x05,0x3c,0xd9,0x45,0x12,0xab,0x47, + 0xf1,0x15,0xe8,0x6e,0xff,0x50,0x94,0x2c,0xea,0x31,0x00,0x2b,0x00,0x02,0x03,0x04, + ); + + chunk_t encrypted_extension = chunk_from_chars( + 0x08,0x00,0x00,0x28,0x00,0x26,0x00,0x0a,0x00,0x14,0x00,0x12,0x00,0x1d,0x00,0x17, + 0x00,0x18,0x00,0x19,0x01,0x00,0x01,0x01,0x01,0x02,0x01,0x03,0x01,0x04,0x00,0x1c, + 0x00,0x02,0x40,0x01,0x00,0x00,0x00,0x00,0x00,0x2a,0x00,0x00, + ); + + chunk_t server_finished = chunk_from_chars( + 0x14,0x00,0x00,0x20,0x48,0xd3,0xe0,0xe1,0xb3,0xd9,0x07,0xc6,0xac,0xff,0x14,0x5e, + 0x16,0x09,0x03,0x88,0xc7,0x7b,0x05,0xc0,0x50,0xb6,0x34,0xab,0x1a,0x88,0xbb,0xd0, + 0xdd,0x1a,0x34,0xb2, + ); + + chunk_t end_of_early_data = chunk_from_chars( + 0x05,0x00,0x00,0x00, + ); + + chunk_t client_finished = chunk_from_chars( + 0x14,0x00,0x00,0x20,0x72,0x30,0xa9,0xc9,0x52,0xc2,0x5c,0xd6,0x13,0x8f,0xc5,0xe6, + 0x62,0x83,0x08,0xc4,0x1c,0x53,0x35,0xdd,0x81,0xb9,0xf9,0x6b,0xce,0xa5,0x0f,0xd3, + 0x2b,0xda,0x41,0x6d, + ); + + chunk_t psk = chunk_from_chars( + 0x4e,0xcd,0x0e,0xb6,0xec,0x3b,0x4d,0x87,0xf5,0xd6,0x02,0x8f,0x92,0x2c,0xa4,0xc5, + 0x85,0x1a,0x27,0x7f,0xd4,0x13,0x11,0xc9,0xe6,0x2d,0x2c,0x94,0x92,0xe1,0xc4,0xf3, + ); + + chunk_t ecdhe = chunk_from_chars( + 0xf4,0x41,0x94,0x75,0x6f,0xf9,0xec,0x9d,0x25,0x18,0x06,0x35,0xd6,0x6e,0xa6,0x82, + 0x4c,0x6a,0xb3,0xbf,0x17,0x99,0x77,0xbe,0x37,0xf7,0x23,0x57,0x0e,0x7c,0xcb,0x2e, + ); + + chunk_t exp_psk_binder = chunk_from_chars( + 0x3a,0xdd,0x4f,0xb2,0xd8,0xfd,0xf8,0x22,0xa0,0xca,0x3c,0xf7,0x67,0x8e,0xf5,0xe8, + 0x8d,0xae,0x99,0x01,0x41,0xc5,0x92,0x4d,0x57,0xbb,0x6f,0xa3,0x1b,0x9e,0x5f,0x9d, + ); + + chunk_t exp_early_exporter_master_secret = chunk_from_chars( + 0xb2,0x02,0x68,0x66,0x61,0x09,0x37,0xd7,0x42,0x3e,0x5b,0xe9,0x08,0x62,0xcc,0xf2, + 0x4c,0x0e,0x60,0x91,0x18,0x6d,0x34,0xf8,0x12,0x08,0x9f,0xf5,0xbe,0x2e,0xf7,0xdf, + ); + + chunk_t exp_client_handshake_traffic_secret = chunk_from_chars( + 0x2f,0xaa,0xc0,0x8f,0x85,0x1d,0x35,0xfe,0xa3,0x60,0x4f,0xcb,0x4d,0xe8,0x2d,0xc6, + 0x2c,0x9b,0x16,0x4a,0x70,0x97,0x4d,0x04,0x62,0xe2,0x7f,0x1a,0xb2,0x78,0x70,0x0f, + ); + + chunk_t exp_client_handshake_key = chunk_from_chars( + 0xb1,0x53,0x08,0x06,0xf4,0xad,0xfe,0xac,0x83,0xf1,0x41,0x30,0x32,0xbb,0xfa,0x82, + ); + + chunk_t exp_client_handshake_iv = chunk_from_chars( + 0xeb,0x50,0xc1,0x6b,0xe7,0x65,0x4a,0xbf,0x99,0xdd,0x06,0xd9, + ); + + chunk_t exp_server_handshake_traffic_secret = chunk_from_chars( + 0xfe,0x92,0x7a,0xe2,0x71,0x31,0x2e,0x8b,0xf0,0x27,0x5b,0x58,0x1c,0x54,0xee,0xf0, + 0x20,0x45,0x0d,0xc4,0xec,0xff,0xaa,0x05,0xa1,0xa3,0x5d,0x27,0x51,0x8e,0x78,0x03, + ); + + chunk_t exp_server_handshake_key = chunk_from_chars( + 0x27,0xc6,0xbd,0xc0,0xa3,0xdc,0xea,0x39,0xa4,0x73,0x26,0xd7,0x9b,0xc9,0xe4,0xee, + ); + + chunk_t exp_server_handshake_iv = chunk_from_chars( + 0x95,0x69,0xec,0xdd,0x4d,0x05,0x36,0x70,0x5e,0x9e,0xf7,0x25, + ); + + chunk_t exp_server_finished = chunk_from_chars( + 0x4b,0xb7,0x4c,0xae,0x7a,0x5d,0xc8,0x91,0x46,0x04,0xc0,0xbf,0xbe,0x2f,0x0c,0x06, + 0x23,0x96,0x88,0x39,0x22,0xbe,0xc8,0xa1,0x5e,0x2a,0x9b,0x53,0x2a,0x5d,0x39,0x2c, + + ); + + chunk_t exp_client_finished = chunk_from_chars( + 0x5a,0xce,0x39,0x4c,0x26,0x98,0x0d,0x58,0x12,0x43,0xf6,0x27,0xd1,0x15,0x0a,0xe2, + 0x7e,0x37,0xfa,0x52,0x36,0x4e,0x0a,0x7f,0x20,0xac,0x68,0x6d,0x09,0xcd,0x0e,0x8e, + ); + + chunk_t exp_client_application_traffic_secret = chunk_from_chars( + 0x2a,0xbb,0xf2,0xb8,0xe3,0x81,0xd2,0x3d,0xbe,0xbe,0x1d,0xd2,0xa7,0xd1,0x6a,0x8b, + 0xf4,0x84,0xcb,0x49,0x50,0xd2,0x3f,0xb7,0xfb,0x7f,0xa8,0x54,0x70,0x62,0xd9,0xa1, + ); + + chunk_t exp_client_application_key = chunk_from_chars( + 0x3c,0xf1,0x22,0xf3,0x01,0xc6,0x35,0x8c,0xa7,0x98,0x95,0x53,0x25,0x0e,0xfd,0x72, + ); + + chunk_t exp_client_application_iv = chunk_from_chars( + 0xab,0x1a,0xec,0x26,0xaa,0x78,0xb8,0xfc,0x11,0x76,0xb9,0xac, + ); + + chunk_t exp_server_application_traffic_secret = chunk_from_chars( + 0xcc,0x21,0xf1,0xbf,0x8f,0xeb,0x7d,0xd5,0xfa,0x50,0x5b,0xd9,0xc4,0xb4,0x68,0xa9, + 0x98,0x4d,0x55,0x4a,0x99,0x3d,0xc4,0x9e,0x6d,0x28,0x55,0x98,0xfb,0x67,0x26,0x91, + ); + + chunk_t exp_server_application_key = chunk_from_chars( + 0xe8,0x57,0xc6,0x90,0xa3,0x4c,0x5a,0x91,0x29,0xd8,0x33,0x61,0x96,0x84,0xf9,0x5e + ); + + chunk_t exp_server_application_iv = chunk_from_chars( + 0x06,0x85,0xd6,0xb5,0x61,0xaa,0xb9,0xef,0x10,0x13,0xfa,0xf9, + ); + + chunk_t exp_exporter_master_secret = chunk_from_chars( + 0x3f,0xd9,0x3d,0x4f,0xfd,0xdc,0x98,0xe6,0x4b,0x14,0xdd,0x10,0x7a,0xed,0xf8,0xee, + 0x4a,0xdd,0x23,0xf4,0x51,0x0f,0x58,0xa4,0x59,0x2d,0x0b,0x20,0x1b,0xee,0x56,0xb4, + ); + + chunk_t exp_resumption_master_secret = chunk_from_chars( + 0x5e,0x95,0xbd,0xf1,0xf8,0x90,0x05,0xea,0x2e,0x9a,0xa0,0xba,0x85,0xe7,0x28,0xe3, + 0xc1,0x9c,0x5f,0xe0,0xc6,0x99,0xe3,0xf5,0xbe,0xe5,0x9f,0xae,0xbd,0x0b,0x54,0x06, + ); + + chunk_t hs_data, psk_binder; + + tls_hkdf_t *hkdf = tls_hkdf_create(HASH_SHA256, psk); + ck_assert(hkdf); + + ck_assert(hkdf->binder(hkdf, client_hello_hash, &psk_binder)); + ck_assert_chunk_eq(exp_psk_binder, psk_binder); + + /* PSK binder is wrapped first with 0x20 and then with 0x00,0x21 length bytes*/ + hs_data = chunk_cata("ccc", client_hello, chunk_from_chars(0x00,0x21,0x20), + psk_binder); + check_secret(hkdf, TLS_HKDF_E_EXP_MASTER, hs_data, exp_early_exporter_master_secret); + + hkdf->set_shared_secret(hkdf, ecdhe); + + /* Generate client handshake traffic secret */ + hs_data = chunk_cata("cc", hs_data, server_hello); + check_secret_key_iv(hkdf, TLS_HKDF_C_HS_TRAFFIC, hs_data, FALSE, + exp_client_handshake_traffic_secret, 16, 12, + exp_client_handshake_key, + exp_client_handshake_iv); + + /* Generate sever handshake traffic secret */ + check_secret_key_iv(hkdf, TLS_HKDF_S_HS_TRAFFIC, hs_data, TRUE, + exp_server_handshake_traffic_secret, 16, 12, + exp_server_handshake_key, + exp_server_handshake_iv); + + check_finished(hkdf, TRUE, exp_server_finished); + check_finished(hkdf, FALSE, exp_client_finished); + + /* Generate client application traffic secret */ + hs_data = chunk_cata("ccc", hs_data, encrypted_extension, server_finished); + check_secret_key_iv(hkdf, TLS_HKDF_C_AP_TRAFFIC, hs_data, FALSE, + exp_client_application_traffic_secret, 16, 12, + exp_client_application_key, + exp_client_application_iv); + + /* Generate server application traffic secret */ + check_secret_key_iv(hkdf, TLS_HKDF_S_AP_TRAFFIC, hs_data, TRUE, + exp_server_application_traffic_secret, 16, 12, + exp_server_application_key, + exp_server_application_iv); + + check_secret(hkdf, TLS_HKDF_EXP_MASTER, hs_data, exp_exporter_master_secret); + + hs_data = chunk_cata("ccc", hs_data, end_of_early_data, client_finished); + check_secret(hkdf, TLS_HKDF_RES_MASTER, hs_data, exp_resumption_master_secret); + + hkdf->destroy(hkdf); + chunk_free(&psk_binder); } END_TEST @@ -435,5 +718,10 @@ Suite *hkdf_suite_create() tcase_add_test(tc, test_ulfheim_traffic); suite_add_tcase(s, tc); + tc = tcase_create("RFC 8448"); + tcase_add_test(tc, test_rfc8448_simple_1_rtt_handshake); + tcase_add_test(tc, test_rfc8448_resumed_0_rtt_handshake); + suite_add_tcase(s, tc); + return s; } From 663969ddf7856f7f92c27ed78accc5bdeb303215 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 13 Jan 2021 14:40:40 +0100 Subject: [PATCH 088/107] libtls: Make min/max TLS version configurable Except for the tls_test tool, the versions now default to those configured in strongswan.conf. --- conf/options/charon.opt | 6 ++ scripts/tls_test.c | 20 ++--- .../plugins/tnc_ifmap/tnc_ifmap_soap.c | 2 +- src/libpttls/pt_tls_client.c | 2 +- src/libpttls/pt_tls_server.c | 4 +- src/libtls/tests/suites/test_socket.c | 4 +- src/libtls/tls.c | 75 +++++++++++++++++-- src/libtls/tls.h | 9 +++ src/libtls/tls_socket.h | 6 +- 9 files changed, 99 insertions(+), 29 deletions(-) diff --git a/conf/options/charon.opt b/conf/options/charon.opt index d57fb4ced..eb835e88e 100644 --- a/conf/options/charon.opt +++ b/conf/options/charon.opt @@ -473,6 +473,12 @@ charon.tls.mac charon.tls.suites List of TLS cipher suites. +charon.tls.version_min = 1.0 + Minimum TLS version to negotiate. + +charon.tls.version_max = 1.3 + Maximum TLS version to negotiate. + charon.user Name of the user the daemon changes to after startup. diff --git a/scripts/tls_test.c b/scripts/tls_test.c index 0040e23a3..5ee7e3714 100644 --- a/scripts/tls_test.c +++ b/scripts/tls_test.c @@ -294,22 +294,13 @@ static void init() atexit(cleanup); } -/** - * Used to parse TLS versions - */ -ENUM(numeric_version_names, TLS_1_0, TLS_1_3, - "1.0", - "1.1", - "1.2", - "1.3"); - int main(int argc, char *argv[]) { char *address = NULL; bool listen = FALSE; int port = 0, times = -1, res, family = AF_UNSPEC; identification_t *server, *client = NULL; - tls_version_t min_version = TLS_1_0, max_version = TLS_1_3; + tls_version_t min_version = TLS_SUPPORTED_MIN, max_version = TLS_SUPPORTED_MAX; tls_cache_t *cache; host_t *host; @@ -387,21 +378,24 @@ int main(int argc, char *argv[]) family = AF_INET6; continue; case 'm': - if (!enum_from_name(numeric_version_names, optarg, &min_version)) + if (!enum_from_name(tls_numeric_version_names, optarg, + &min_version)) { fprintf(stderr, "unknown minimum TLS version: %s\n", optarg); return 1; } continue; case 'M': - if (!enum_from_name(numeric_version_names, optarg, &max_version)) + if (!enum_from_name(tls_numeric_version_names, optarg, + &max_version)) { fprintf(stderr, "unknown maximum TLS version: %s\n", optarg); return 1; } continue; case 'v': - if (!enum_from_name(numeric_version_names, optarg, &min_version)) + if (!enum_from_name(tls_numeric_version_names, optarg, + &min_version)) { fprintf(stderr, "unknown TLS version: %s\n", optarg); return 1; diff --git a/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_soap.c b/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_soap.c index ab8c7273b..4937f32a2 100644 --- a/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_soap.c +++ b/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_soap.c @@ -877,7 +877,7 @@ static bool soap_init(private_tnc_ifmap_soap_t *this) /* open TLS socket */ this->tls = tls_socket_create(FALSE, server_id, client_id, this->fd, - NULL, TLS_1_0, TLS_1_2, FALSE); + NULL, TLS_UNSPEC, TLS_UNSPEC, FALSE); if (!this->tls) { DBG1(DBG_TNC, "creating TLS socket failed"); diff --git a/src/libpttls/pt_tls_client.c b/src/libpttls/pt_tls_client.c index 59842368f..b7fc4c70d 100644 --- a/src/libpttls/pt_tls_client.c +++ b/src/libpttls/pt_tls_client.c @@ -85,7 +85,7 @@ static bool make_connection(private_pt_tls_client_t *this) } this->tls = tls_socket_create(FALSE, this->server, this->client, fd, - NULL, TLS_1_0, TLS_1_2, FALSE); + NULL, TLS_UNSPEC, TLS_UNSPEC, FALSE); if (!this->tls) { close(fd); diff --git a/src/libpttls/pt_tls_server.c b/src/libpttls/pt_tls_server.c index 4c484fbb8..1ad6165f6 100644 --- a/src/libpttls/pt_tls_server.c +++ b/src/libpttls/pt_tls_server.c @@ -532,8 +532,8 @@ pt_tls_server_t *pt_tls_server_create(identification_t *server, int fd, .destroy = _destroy, }, .state = PT_TLS_SERVER_VERSION, - .tls = tls_socket_create(TRUE, server, NULL, fd, NULL, TLS_1_0, TLS_1_2, - FALSE), + .tls = tls_socket_create(TRUE, server, NULL, fd, NULL, TLS_UNSPEC, + TLS_UNSPEC, FALSE), .tnccs = (tls_t*)tnccs, .auth = auth, ); diff --git a/src/libtls/tests/suites/test_socket.c b/src/libtls/tests/suites/test_socket.c index de1b51682..ee470e280 100644 --- a/src/libtls/tests/suites/test_socket.c +++ b/src/libtls/tests/suites/test_socket.c @@ -401,7 +401,7 @@ static job_requeue_t serve_echo(echo_server_config_t *config) } tls = tls_socket_create(TRUE, server, client, cfd, NULL, - TLS_1_0, config->version, TRUE); + TLS_SUPPORTED_MIN, config->version, TRUE); ck_assert(tls != NULL); while (TRUE) @@ -477,7 +477,7 @@ static void run_echo_client(echo_server_config_t *config) ck_assert(connect(fd, host->get_sockaddr(host), *host->get_sockaddr_len(host)) != -1); tls = tls_socket_create(FALSE, server, client, fd, NULL, - TLS_1_0, config->version, TRUE); + TLS_SUPPORTED_MIN, config->version, TRUE); ck_assert(tls != NULL); wr = rd = 0; diff --git a/src/libtls/tls.c b/src/libtls/tls.c index 52e06a446..4cb68ba29 100644 --- a/src/libtls/tls.c +++ b/src/libtls/tls.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2021 Tobias Brunner * Copyright (C) 2020 Pascal Knecht * HSR Hochschule fuer Technik Rapperswil * @@ -27,7 +28,9 @@ #include "tls_server.h" #include "tls_peer.h" -ENUM_BEGIN(tls_version_names, SSL_2_0, SSL_2_0, +ENUM_BEGIN(tls_version_names, TLS_UNSPEC, TLS_UNSPEC, + "TLS UNSPEC"); +ENUM_NEXT(tls_version_names, SSL_2_0, SSL_2_0, TLS_UNSPEC, "SSLv2"); ENUM_NEXT(tls_version_names, SSL_3_0, TLS_1_3, SSL_2_0, "SSLv3", @@ -37,6 +40,15 @@ ENUM_NEXT(tls_version_names, SSL_3_0, TLS_1_3, SSL_2_0, "TLS 1.3"); ENUM_END(tls_version_names, TLS_1_3); +/** + * Only supported versions are mapped + */ +ENUM(tls_numeric_version_names, TLS_SUPPORTED_MIN, TLS_SUPPORTED_MAX, + "1.0", + "1.1", + "1.2", + "1.3"); + ENUM(tls_content_type_names, TLS_CHANGE_CIPHER_SPEC, TLS_APPLICATION_DATA, "ChangeCipherSpec", "Alert", @@ -435,25 +447,74 @@ METHOD(tls_t, get_peer_id, identification_t*, return this->handshake->get_peer_id(this->handshake); } +/** + * Determine the min/max versions + */ +static void determine_versions(private_tls_t *this) +{ + tls_version_t version; + char *version_str; + + if (this->version_min == TLS_UNSPEC) + { + this->version_min = TLS_SUPPORTED_MIN; + + version_str = lib->settings->get_str(lib->settings, "%s.tls.version_min", + NULL, lib->ns); + if (version_str && + enum_from_name(tls_numeric_version_names, version_str, &version)) + { + this->version_min = version; + } + } + if (this->version_max == TLS_UNSPEC) + { + this->version_max = TLS_SUPPORTED_MAX; + + version_str = lib->settings->get_str(lib->settings, "%s.tls.version_max", + NULL, lib->ns); + if (version_str && + enum_from_name(tls_numeric_version_names, version_str, &version)) + { + this->version_max = version; + } + } + if (this->version_max < this->version_min) + { + this->version_min = this->version_max; + } +} + METHOD(tls_t, get_version_max, tls_version_t, private_tls_t *this) { + determine_versions(this); return this->version_max; } METHOD(tls_t, get_version_min, tls_version_t, private_tls_t *this) { + determine_versions(this); return this->version_min; } METHOD(tls_t, set_version, bool, private_tls_t *this, tls_version_t min_version, tls_version_t max_version) { - if (min_version < this->version_min || - max_version > this->version_max || - min_version > max_version || - min_version < TLS_1_0) + if (min_version == TLS_UNSPEC) + { + min_version = this->version_min; + } + if (max_version == TLS_UNSPEC) + { + max_version = this->version_max; + } + if ((this->version_min != TLS_UNSPEC && min_version < this->version_min) || + (this->version_max != TLS_UNSPEC && max_version > this->version_max) || + (min_version != TLS_UNSPEC && min_version < TLS_SUPPORTED_MIN) || + (max_version != TLS_UNSPEC && max_version > TLS_SUPPORTED_MAX) || + min_version > max_version) { return FALSE; } @@ -461,7 +522,7 @@ METHOD(tls_t, set_version, bool, this->version_min = min_version; this->version_max = max_version; - if (min_version == max_version) + if (min_version != TLS_UNSPEC && min_version == max_version) { this->protection->set_version(this->protection, max_version); } @@ -555,8 +616,6 @@ tls_t *tls_create(bool is_server, identification_t *server, .destroy = _destroy, }, .is_server = is_server, - .version_min = TLS_1_0, - .version_max = TLS_1_3, .application = application, .purpose = purpose, ); diff --git a/src/libtls/tls.h b/src/libtls/tls.h index c85c9c452..5a11f703d 100644 --- a/src/libtls/tls.h +++ b/src/libtls/tls.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2021 Tobias Brunner * Copyright (C) 2020 Pascal Knecht * HSR Hochschule fuer Technik Rapperswil * @@ -50,12 +51,15 @@ typedef struct tls_t tls_t; * TLS/SSL version numbers */ enum tls_version_t { + TLS_UNSPEC = 0, SSL_2_0 = 0x0200, SSL_3_0 = 0x0300, TLS_1_0 = 0x0301, + TLS_SUPPORTED_MIN = TLS_1_0, TLS_1_1 = 0x0302, TLS_1_2 = 0x0303, TLS_1_3 = 0x0304, + TLS_SUPPORTED_MAX = TLS_1_3, }; /** @@ -63,6 +67,11 @@ enum tls_version_t { */ extern enum_name_t *tls_version_names; +/** + * Simple, numeric enum names for tls_version_t (only supported versions) + */ +extern enum_name_t *tls_numeric_version_names; + /** * TLS higher level content type */ diff --git a/src/libtls/tls_socket.h b/src/libtls/tls_socket.h index 2026cba41..08ccbd287 100644 --- a/src/libtls/tls_socket.h +++ b/src/libtls/tls_socket.h @@ -99,13 +99,15 @@ struct tls_socket_t { /** * Create a tls_socket instance. * + * Use TLS_UNSPEC to default to the configured min/max version. + * * @param is_server TRUE to act as TLS server * @param server server identity * @param peer client identity, NULL for no client authentication * @param fd socket to read/write from * @param cache session cache to use, or NULL - * @param min_version minimum TLS version to negotiate - * @param max_version maximum TLS version to negotiate + * @param min_version minimum TLS version to negotiate or TLS_UNSPEC + * @param max_version maximum TLS version to negotiate or TLS_UNSPEC * @param nullok accept NULL encryption ciphers * @return TLS socket wrapper */ From 92aef122c3faca04d09387663f267212ffd26ca8 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 13 Jan 2021 15:26:33 +0100 Subject: [PATCH 089/107] libtls: Reduce default max version to 1.2 Using TLS 1.3 with various EAP methods is not yet fully standardized, so we don't enable it by default yet. --- conf/options/charon.opt | 2 +- src/libtls/tls.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/conf/options/charon.opt b/conf/options/charon.opt index eb835e88e..fb69bd7a0 100644 --- a/conf/options/charon.opt +++ b/conf/options/charon.opt @@ -476,7 +476,7 @@ charon.tls.suites charon.tls.version_min = 1.0 Minimum TLS version to negotiate. -charon.tls.version_max = 1.3 +charon.tls.version_max = 1.2 Maximum TLS version to negotiate. charon.user diff --git a/src/libtls/tls.c b/src/libtls/tls.c index 4cb68ba29..8b9911a87 100644 --- a/src/libtls/tls.c +++ b/src/libtls/tls.c @@ -468,8 +468,8 @@ static void determine_versions(private_tls_t *this) } } if (this->version_max == TLS_UNSPEC) - { - this->version_max = TLS_SUPPORTED_MAX; + { /* default to TLS 1.2 until 1.3 is stable for use in EAP */ + this->version_max = TLS_1_2; version_str = lib->settings->get_str(lib->settings, "%s.tls.version_max", NULL, lib->ns); From 8cf3998f1a95f7890379262df0d568c071418a8f Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 14 Jan 2021 15:11:13 +0100 Subject: [PATCH 090/107] tls-crypto: Only log modified TLS versions if successfully set If no cipher suites are available, the new versions are the previous values but reversed (i.e. the versions were not changed but we still ended up with a log message saying "TLS min/max TLS 1.3/TLS 1.0 ..."). Also switched to using the numeric version names to avoid the repeated "TLS" prefix. --- src/libtls/tls_crypto.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index e8126c000..23c36374e 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1222,12 +1222,12 @@ static void build_cipher_suite_list(private_tls_crypto_t *this) new_max_version = min(new_max_version, max_version); new_min_version = max(new_min_version, min_version); - if (min_version != new_min_version || max_version != new_max_version) + if ((min_version != new_min_version || max_version != new_max_version) && + this->tls->set_version(this->tls, new_min_version, new_max_version)) { - this->tls->set_version(this->tls, new_min_version, new_max_version); DBG2(DBG_TLS, "TLS min/max %N/%N according to the cipher suites", - tls_version_names, new_min_version, - tls_version_names, new_max_version); + tls_numeric_version_names, new_min_version, + tls_numeric_version_names, new_max_version); } } From 7b64880a8c5a2ff5cd1f0827d81465f9b6064055 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 14 Jan 2021 16:14:47 +0100 Subject: [PATCH 091/107] tls-peer: Don't log anything if we are not sending supported groups --- src/libtls/tls_peer.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 987bd5152..9d797f34b 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -1326,8 +1326,6 @@ static status_t send_client_hello(private_tls_peer_t *this, names->destroy(names); } - DBG2(DBG_TLS, "sending extension: %N", - tls_extension_names, TLS_EXT_SUPPORTED_GROUPS); enumerator = this->crypto->create_ec_enumerator(this->crypto); while (enumerator->enumerate(enumerator, &group, &curve)) { @@ -1351,8 +1349,12 @@ static status_t send_client_hello(private_tls_peer_t *this, curves->write_uint16(curves, curve); } enumerator->destroy(enumerator); + if (curves) { + DBG2(DBG_TLS, "sending extension: %N", + tls_extension_names, TLS_EXT_SUPPORTED_GROUPS); + curves->wrap16(curves); extensions->write_data16(extensions, curves->get_buf(curves)); curves->destroy(curves); From a00ace0fe2bebd35fe868d070f7fe465f6650964 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 14 Jan 2021 15:45:34 +0100 Subject: [PATCH 092/107] libtls: Only run socket tests with EdDSA keys if they are supported ECDSA support is currently required to run the tests because ECDSA cipher suites are not filtered when determining the supported cipher suites. Also required are ECDH groups. --- src/libtls/tests/suites/test_socket.c | 30 ++++++++++++++++++++++----- src/libtls/tests/tls_tests.h | 2 ++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/libtls/tests/suites/test_socket.c b/src/libtls/tests/suites/test_socket.c index ee470e280..e9f7d8321 100644 --- a/src/libtls/tests/suites/test_socket.c +++ b/src/libtls/tests/suites/test_socket.c @@ -707,11 +707,6 @@ START_TEST(test_tls10_mutual) } END_TEST -Suite *socket_suite_create() -{ - Suite *s; - TCase *tc; - #define add_tls_test(func, version) \ tcase_add_loop_test(tc, func, 0, \ tls_crypto_get_supported_suites(TRUE, version, NULL)); @@ -719,6 +714,11 @@ Suite *socket_suite_create() #define add_tls_versions_test(func, from, to) \ tcase_add_loop_test(tc, func, from, to+1); +Suite *socket_suite_create() +{ + Suite *s; + TCase *tc; + s = suite_create("socket"); tc = tcase_create("TLS [1.0..1.3] client to TLS 1.3 server"); @@ -782,6 +782,16 @@ Suite *socket_suite_create() add_tls_test(test_tls10_mutual, TLS_1_0); suite_add_tcase(s, tc); + return s; +} + +Suite *socket_suite_ed25519_create() +{ + Suite *s; + TCase *tc; + + s = suite_create("socket ed25519"); + tc = tcase_create("TLS 1.3/ed25519"); tcase_add_checked_fixture(tc, setup_ed25519_creds, teardown_creds); add_tls_test(test_tls13, TLS_1_3); @@ -802,6 +812,16 @@ Suite *socket_suite_create() add_tls_test(test_tls10, TLS_1_0); suite_add_tcase(s, tc); + return s; +} + +Suite *socket_suite_ed448_create() +{ + Suite *s; + TCase *tc; + + s = suite_create("socket ed448"); + tc = tcase_create("TLS 1.3/ed448"); tcase_add_checked_fixture(tc, setup_ed448_creds, teardown_creds); add_tls_test(test_tls13, TLS_1_3); diff --git a/src/libtls/tests/tls_tests.h b/src/libtls/tests/tls_tests.h index b0d0d57a5..77e548e53 100644 --- a/src/libtls/tests/tls_tests.h +++ b/src/libtls/tests/tls_tests.h @@ -15,4 +15,6 @@ TEST_SUITE(hkdf_suite_create) TEST_SUITE(socket_suite_create) +TEST_SUITE_DEPEND(socket_suite_ed25519_create, PRIVKEY_GEN, KEY_ED25519) +TEST_SUITE_DEPEND(socket_suite_ed448_create, PRIVKEY_GEN, KEY_ED448) TEST_SUITE(suites_suite_create) From 083f38259c79ee5d1ac443bbfd0146c1fa03978e Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 14 Jan 2021 18:02:00 +0100 Subject: [PATCH 093/107] tls-eap: Conclude EAP method also after processing packets With TLS 1.3, the server sends its Finished message first, so the session is complete after processing the client's Finished message, without having to send anything else (in particular no acknowledgement as the last message from the client is no fragment). --- src/libtls/tls_eap.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libtls/tls_eap.c b/src/libtls/tls_eap.c index 12d5aed53..196cf92fe 100644 --- a/src/libtls/tls_eap.c +++ b/src/libtls/tls_eap.c @@ -394,6 +394,10 @@ METHOD(tls_eap_t, process, status_t, switch (status) { case INVALID_STATE: + if (this->tls->is_complete(this->tls)) + { + return SUCCESS; + } *out = create_ack(this); return NEED_MORE; case FAILED: From a4a128bd2fa66067170a06b3c83e64c024493641 Mon Sep 17 00:00:00 2001 From: Shmulik Ladkani Date: Fri, 15 Jan 2021 14:45:34 +0100 Subject: [PATCH 094/107] tls-server: Optionally omit CAs in CertificateRequest messages Usually, the DNs of all loaded CA certificates are included in the CertificateRequest messages sent by the server. Alas, certain EAP-TLS clients fail to process this message if the list is too long, returning the fatal TLS alert 'illegal parameter'. This new option allows configuring whether CAs are included or an empty list is sent (TLS 1.2), or the certificate_authorities extension is omitted (TLS 1.3). The list only serves as hint/constraint for clients during certificate selection, they still have to provide a certificate but are free to select any one they have available. Closes strongswan/strongswan#187. --- conf/options/charon.opt | 6 ++++++ src/libtls/tls_server.c | 35 +++++++++++++++++++++++++++-------- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/conf/options/charon.opt b/conf/options/charon.opt index fb69bd7a0..dd972649b 100644 --- a/conf/options/charon.opt +++ b/conf/options/charon.opt @@ -473,6 +473,12 @@ charon.tls.mac charon.tls.suites List of TLS cipher suites. +charon.tls.send_certreq_authorities = yes + Whether to include CAs in a server's CertificateRequest message. + + Whether to include CAs in a server's CertificateRequest message. May be + disabled if clients can't handle a long list of CAs. + charon.tls.version_min = 1.0 Minimum TLS version to negotiate. diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 50051c143..eb3edf81f 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -163,6 +163,11 @@ struct private_tls_server_t { * Did we receive the curves from the client? */ bool curves_received; + + /** + * Whether to include CAs in CertificateRequest messages + */ + bool send_certreq_authorities; }; /** @@ -1372,7 +1377,14 @@ static status_t send_certificate_request(private_tls_server_t *this, this->crypto->get_signature_algorithms(this->crypto, writer, TRUE); } - write_certificate_authorities(writer); + if (this->send_certreq_authorities) + { + write_certificate_authorities(writer); + } + else + { + writer->write_data16(writer, chunk_empty); + } } else { @@ -1380,13 +1392,17 @@ static status_t send_certificate_request(private_tls_server_t *this, writer->write_uint8(writer, 0); extensions = bio_writer_create(32); - DBG2(DBG_TLS, "sending extension: %N", - tls_extension_names, TLS_EXT_CERTIFICATE_AUTHORITIES); - authorities = bio_writer_create(64); - write_certificate_authorities(authorities); - extensions->write_uint16(extensions, TLS_EXT_CERTIFICATE_AUTHORITIES); - extensions->write_data16(extensions, authorities->get_buf(authorities)); - authorities->destroy(authorities); + + if (this->send_certreq_authorities) + { + DBG2(DBG_TLS, "sending extension: %N", + tls_extension_names, TLS_EXT_CERTIFICATE_AUTHORITIES); + authorities = bio_writer_create(64); + write_certificate_authorities(authorities); + extensions->write_uint16(extensions, TLS_EXT_CERTIFICATE_AUTHORITIES); + extensions->write_data16(extensions, authorities->get_buf(authorities)); + authorities->destroy(authorities); + } DBG2(DBG_TLS, "sending extension: %N", tls_extension_names, TLS_EXT_SIGNATURE_ALGORITHMS); @@ -1808,6 +1824,9 @@ tls_server_t *tls_server_create(tls_t *tls, .state = STATE_INIT, .peer_auth = auth_cfg_create(), .server_auth = auth_cfg_create(), + .send_certreq_authorities = lib->settings->get_bool(lib->settings, + "%s.tls.send_certreq_authorities", + TRUE, lib->ns), ); return &this->public; From dc9f6c68dfbfe5b8d6413b76c5bb6b1353edabf6 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Sun, 17 Jan 2021 16:33:02 +0100 Subject: [PATCH 095/107] libtls: Add downgrade protection for TLS 1.3 and TLS 1.2 Section 4.1.3 in RFC 8446 defines a new downgrade protection mechanism that also affects TLS 1.2. --- src/libtls/tls.c | 9 ++++++++- src/libtls/tls.h | 8 +++++++- src/libtls/tls_peer.c | 20 +++++++++++++++++++- src/libtls/tls_server.c | 20 +++++++++++++++++++- 4 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/libtls/tls.c b/src/libtls/tls.c index 8b9911a87..da45f4b99 100644 --- a/src/libtls/tls.c +++ b/src/libtls/tls.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2021 Tobias Brunner - * Copyright (C) 2020 Pascal Knecht + * Copyright (C) 2020-2021 Pascal Knecht * HSR Hochschule fuer Technik Rapperswil * * Copyright (C) 2010 Martin Willi @@ -153,6 +153,13 @@ chunk_t tls_hello_retry_request_magic = chunk_from_chars( 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C, ); +chunk_t tls_downgrade_protection_tls11 = chunk_from_chars( + 0x44, 0x4F, 0x57, 0x4E, 0x47, 0x52, 0x44, 0x00, +); +chunk_t tls_downgrade_protection_tls12 = chunk_from_chars( + 0x44, 0x4F, 0x57, 0x4E, 0x47, 0x52, 0x44, 0x01, +); + /** * TLS record */ diff --git a/src/libtls/tls.h b/src/libtls/tls.h index 5a11f703d..9de042b26 100644 --- a/src/libtls/tls.h +++ b/src/libtls/tls.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2021 Tobias Brunner - * Copyright (C) 2020 Pascal Knecht + * Copyright (C) 2020-2021 Pascal Knecht * HSR Hochschule fuer Technik Rapperswil * * Copyright (C) 2010 Martin Willi @@ -213,6 +213,12 @@ extern enum_name_t *tls_extension_names; */ extern chunk_t tls_hello_retry_request_magic; +/** + * Magic values for downgrade protection (see RFC 8446, section 4.1.3) + */ +extern chunk_t tls_downgrade_protection_tls11; +extern chunk_t tls_downgrade_protection_tls12; + /** * A bottom-up driven TLS stack, suitable for EAP implementations. */ diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 9d797f34b..dac3acad8 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2020 Tobias Brunner - * Copyright (C) 2020 Pascal Knecht + * Copyright (C) 2020-2021 Pascal Knecht * Copyright (C) 2020 Méline Sieber * HSR Hochschule fuer Technik Rapperswil * @@ -208,6 +208,7 @@ static status_t process_server_hello(private_tls_peer_t *this, chunk_t msg, random, session, ext = chunk_empty, key_share = chunk_empty; chunk_t cookie = chunk_empty; tls_cipher_suite_t suite = 0; + tls_version_t version_max; bool is_retry_request; msg = reader->peek(reader); @@ -286,6 +287,23 @@ static status_t process_server_hello(private_tls_peer_t *this, } extensions->destroy(extensions); + /* downgrade protection (see RFC 8446, section 4.1.3) */ + version_max = this->tls->get_version_max(this->tls); + if ((version_max == TLS_1_3 && version < TLS_1_3) || + (version_max == TLS_1_2 && version < TLS_1_2)) + { + chunk_t server_random_end = chunk_create(&this->server_random[24], 8); + + if (chunk_equals(server_random_end, tls_downgrade_protection_tls11) || + chunk_equals(server_random_end, tls_downgrade_protection_tls12)) + { + DBG1(DBG_TLS, "server random indicates downgrade attack to %N", + tls_version_names, version); + this->alert->add(this->alert, TLS_FATAL, TLS_ILLEGAL_PARAMETER); + return NEED_MORE; + } + } + if (!this->tls->set_version(this->tls, version, version)) { DBG1(DBG_TLS, "negotiated version %N not supported", diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index eb3edf81f..275323183 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Pascal Knecht + * Copyright (C) 2020-2021 Pascal Knecht * HSR Hochschule fuer Technik Rapperswil * * Copyright (C) 2010 Martin Willi @@ -338,6 +338,7 @@ static status_t process_client_hello(private_tls_server_t *this, chunk_t extension_data = chunk_empty; bio_reader_t *extensions, *extension; tls_cipher_suite_t *suites; + tls_version_t original_version_max; int count, i; rng_t *rng; @@ -456,6 +457,8 @@ static status_t process_client_hello(private_tls_server_t *this, } rng->destroy(rng); + original_version_max = this->tls->get_version_max(this->tls); + if (versions.len) { bio_reader_t *client_versions; @@ -482,6 +485,21 @@ static status_t process_client_hello(private_tls_server_t *this, this->client_version = version; } } + + /* downgrade protection (see RFC 8446, section 4.1.3) */ + if ((original_version_max == TLS_1_3 && version < TLS_1_3) || + (original_version_max == TLS_1_2 && version < TLS_1_2)) + { + chunk_t downgrade_protection = tls_downgrade_protection_tls11; + + if (version == TLS_1_2) + { + downgrade_protection = tls_downgrade_protection_tls12; + } + memcpy(&this->server_random[24], downgrade_protection.ptr, + downgrade_protection.len); + } + if (!this->client_version) { DBG1(DBG_TLS, "proposed version %N not supported", tls_version_names, From 111e9071687d9df0002f9c06db0d6df4db4b82b1 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 22 Jan 2021 09:41:22 +0100 Subject: [PATCH 096/107] tls-server: Remove unused variable --- src/libtls/tls_server.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 275323183..6562fc4f9 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -563,7 +563,6 @@ static status_t process_client_hello(private_tls_server_t *this, diffie_hellman_group_t group; tls_named_group_t curve, requesting_curve = 0; enumerator_t *enumerator; - chunk_t shared_secret = chunk_empty; array_t *peer_key_shares; peer_key_shares = array_create(sizeof(key_share_t), 1); @@ -650,10 +649,8 @@ static status_t process_client_hello(private_tls_server_t *this, { DBG1(DBG_TLS, "DH key derivation failed"); this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); - chunk_clear(&shared_secret); return NEED_MORE; } - chunk_clear(&shared_secret); this->requested_curve = 0; } } From ab226b3927f87ad1a9cc947368f3c7f797654925 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 22 Jan 2021 10:06:05 +0100 Subject: [PATCH 097/107] tls-server: Select cipher suite also when handling HelloRetryRequest This was previously treated like a resumption, which it is clearly not. Also added a check that verifies that the same cipher suite is selected during the retry, as per RFC 8446, section 4.1.4. --- src/libtls/tls_server.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 6562fc4f9..376a1933a 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -253,7 +253,9 @@ static bool select_suite_and_key(private_tls_server_t *this, return FALSE; } DBG1(DBG_TLS, "using key of type %N", key_type_names, key->get_type(key)); + DESTROY_IF(this->private); this->private = key->get_ref(key); + this->server_auth->purge(this->server_auth, FALSE); this->server_auth->merge(this->server_auth, auth, FALSE); enumerator->destroy(enumerator); return TRUE; @@ -516,7 +518,7 @@ static status_t process_client_hello(private_tls_server_t *this, chunk_from_thing(this->server_random)); } - if (this->suite) + if (this->suite && !retrying(this)) { this->session = chunk_clone(session); this->resume = TRUE; @@ -526,6 +528,8 @@ static status_t process_client_hello(private_tls_server_t *this, } else { + tls_cipher_suite_t original_suite = this->suite; + count = ciphers.len / sizeof(uint16_t); suites = alloca(count * sizeof(tls_cipher_suite_t)); DBG2(DBG_TLS, "received %d TLS cipher suites:", count); @@ -539,6 +543,14 @@ static status_t process_client_hello(private_tls_server_t *this, this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); return NEED_MORE; } + if (retrying(this) && original_suite != this->suite) + { + DBG1(DBG_TLS, "selected %N instead of %N during retry", + tls_cipher_suite_names, this->suite, tls_cipher_suite_names, + original_suite); + this->alert->add(this->alert, TLS_FATAL, TLS_ILLEGAL_PARAMETER); + return NEED_MORE; + } if (this->tls->get_version_max(this->tls) < TLS_1_3) { rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG); @@ -551,6 +563,7 @@ static status_t process_client_hello(private_tls_server_t *this, } else { + chunk_free(&this->session); this->session = chunk_clone(session); } DBG1(DBG_TLS, "negotiated %N using suite %N", From 3abcbf82b5fb83730784e607999bd3df0d19aca5 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 22 Jan 2021 10:19:57 +0100 Subject: [PATCH 098/107] tls-peer: Verify server selects the same cipher suite after HelloRetryRequest This is as per RFC 8446, section 4.1.4. --- src/libtls/tls_peer.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index dac3acad8..1b755ea34 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -132,6 +132,11 @@ struct private_tls_peer_t { */ tls_named_group_t requested_curve; + /** + * Original cipher suite in HelloRetryRequest + */ + tls_cipher_suite_t original_suite; + /** * Cookie extension received in HelloRetryRequest */ @@ -342,6 +347,14 @@ static status_t process_server_hello(private_tls_peer_t *this, this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); return NEED_MORE; } + if (this->original_suite && this->original_suite != suite) + { + DBG1(DBG_TLS, "server selected %N instead of %N after retry", + tls_cipher_suite_names, suite, tls_cipher_suite_names, + this->original_suite); + this->alert->add(this->alert, TLS_FATAL, TLS_ILLEGAL_PARAMETER); + return NEED_MORE; + } DBG1(DBG_TLS, "negotiated %N using suite %N", tls_version_names, version, tls_cipher_suite_names, suite); free(this->session.ptr); @@ -390,6 +403,7 @@ static status_t process_server_hello(private_tls_peer_t *this, DESTROY_IF(this->dh); this->dh = NULL; + this->original_suite = suite; this->requested_curve = key_type; this->cookie = chunk_clone(cookie); this->state = STATE_INIT; From a60e248b0d26f77f05be669559ef714c429172d7 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 27 Jan 2021 14:40:22 +0100 Subject: [PATCH 099/107] libtls: Increase default min version to 1.2 The older versions are generally considered deprecated (there is an Internet-Draft that aims to do that formally). --- conf/options/charon.opt | 2 +- src/libtls/tls.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/conf/options/charon.opt b/conf/options/charon.opt index dd972649b..868ee1d05 100644 --- a/conf/options/charon.opt +++ b/conf/options/charon.opt @@ -479,7 +479,7 @@ charon.tls.send_certreq_authorities = yes Whether to include CAs in a server's CertificateRequest message. May be disabled if clients can't handle a long list of CAs. -charon.tls.version_min = 1.0 +charon.tls.version_min = 1.2 Minimum TLS version to negotiate. charon.tls.version_max = 1.2 diff --git a/src/libtls/tls.c b/src/libtls/tls.c index da45f4b99..ae14213a0 100644 --- a/src/libtls/tls.c +++ b/src/libtls/tls.c @@ -463,8 +463,8 @@ static void determine_versions(private_tls_t *this) char *version_str; if (this->version_min == TLS_UNSPEC) - { - this->version_min = TLS_SUPPORTED_MIN; + { /* default to TLS 1.2 as older versions are considered deprecated */ + this->version_min = TLS_1_2; version_str = lib->settings->get_str(lib->settings, "%s.tls.version_min", NULL, lib->ns); From e5b6565730600656c7806e7c5b543172f56f22f0 Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Mon, 18 Jan 2021 18:11:55 +0100 Subject: [PATCH 100/107] tls-crypto: Rename DH group/key exchange method config option TLS key exchange methods are now configured with `ke_group`. --- conf/options/charon.opt | 3 +++ src/libtls/tests/suites/test_socket.c | 16 ++++++++-------- src/libtls/tls_crypto.c | 5 +++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/conf/options/charon.opt b/conf/options/charon.opt index 868ee1d05..f76ef44f3 100644 --- a/conf/options/charon.opt +++ b/conf/options/charon.opt @@ -473,6 +473,9 @@ charon.tls.mac charon.tls.suites List of TLS cipher suites. +charon.tls.ke_group + List of TLS key exchange groups. + charon.tls.send_certreq_authorities = yes Whether to include CAs in a server's CertificateRequest message. diff --git a/src/libtls/tests/suites/test_socket.c b/src/libtls/tests/suites/test_socket.c index e9f7d8321..f15889319 100644 --- a/src/libtls/tests/suites/test_socket.c +++ b/src/libtls/tests/suites/test_socket.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Pascal Knecht + * Copyright (C) 2020-2021 Pascal Knecht * HSR Hochschule fuer Technik Rapperswil * * Copyright (C) 2014 Martin Willi @@ -559,8 +559,8 @@ static void test_tls(tls_version_t version, uint16_t port, bool cauth, u_int i) /** * TLS curve test wrapper function */ -static void test_tls_curves(tls_version_t version, uint16_t port, bool cauth, - u_int i) +static void test_tls_ke_groups(tls_version_t version, uint16_t port, bool cauth, + u_int i) { echo_server_config_t *config; diffie_hellman_group_t *groups; @@ -575,7 +575,7 @@ static void test_tls_curves(tls_version_t version, uint16_t port, bool cauth, ck_assert(i < count); snprintf(curve, sizeof(curve), "%N", diffie_hellman_group_names_short, groups[i]); - lib->settings->set_str(lib->settings, "%s.tls.curve", curve, lib->ns); + lib->settings->set_str(lib->settings, "%s.tls.ke_group", curve, lib->ns); run_echo_client(config); @@ -653,9 +653,9 @@ START_TEST(test_tls_13_client) } END_TEST -START_TEST(test_tls13_curves) +START_TEST(test_tls13_ke_groups) { - test_tls_curves(TLS_1_3, 5668, FALSE, _i); + test_tls_ke_groups(TLS_1_3, 5668, FALSE, _i); } END_TEST @@ -736,9 +736,9 @@ Suite *socket_suite_create() add_tls_versions_test(test_tls_12_server, TLS_1_0, TLS_1_3); suite_add_tcase(s, tc); - tc = tcase_create("TLS 1.3/curves"); + tc = tcase_create("TLS 1.3/key exchange groups"); tcase_add_checked_fixture(tc, setup_creds, teardown_creds); - tcase_add_loop_test(tc, test_tls13_curves, 0, + tcase_add_loop_test(tc, test_tls13_ke_groups, 0, tls_crypto_get_supported_groups(NULL)); suite_add_tcase(s, tc); diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 23c36374e..1693a18b6 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2020 Tobias Brunner - * Copyright (C) 2020 Pascal Knecht + * Copyright (C) 2020-2021 Pascal Knecht * Copyright (C) 2020 Méline Sieber * HSR Hochschule fuer Technik Rapperswil * @@ -1103,7 +1103,8 @@ static bool filter_curve_config(tls_named_group_t curve) enumerator_t *enumerator; char *token, *config; - config = lib->settings->get_str(lib->settings, "%s.tls.curve", NULL, lib->ns); + config = lib->settings->get_str(lib->settings, "%s.tls.ke_group", NULL, + lib->ns); if (config) { enumerator = enumerator_create_token(config, ",", " "); From e3757300ebdf77404ad2c44d48aa04fe74be79fa Mon Sep 17 00:00:00 2001 From: Pascal Knecht Date: Mon, 1 Feb 2021 21:41:56 +0100 Subject: [PATCH 101/107] tls-crypto: Add signature scheme config file filter And add signature scheme unit tests. --- conf/options/charon.opt | 3 + src/libtls/tests/suites/test_socket.c | 104 ++++++++++++++++++++++++-- src/libtls/tls_crypto.c | 83 ++++++++++++++++++-- src/libtls/tls_crypto.h | 12 ++- 4 files changed, 188 insertions(+), 14 deletions(-) diff --git a/conf/options/charon.opt b/conf/options/charon.opt index f76ef44f3..df3233046 100644 --- a/conf/options/charon.opt +++ b/conf/options/charon.opt @@ -476,6 +476,9 @@ charon.tls.suites charon.tls.ke_group List of TLS key exchange groups. +charon.tls.signature + List of TLS signature schemes. + charon.tls.send_certreq_authorities = yes Whether to include CAs in a server's CertificateRequest message. diff --git a/src/libtls/tests/suites/test_socket.c b/src/libtls/tests/suites/test_socket.c index f15889319..4707f5849 100644 --- a/src/libtls/tests/suites/test_socket.c +++ b/src/libtls/tests/suites/test_socket.c @@ -308,7 +308,11 @@ static void setup_credentials(chunk_t key_data, chunk_t cert_data) private_key_t *key; certificate_t *cert; - creds = mem_cred_create(); + if (!creds) + { + creds = mem_cred_create(); + lib->credmgr->add_set(lib->credmgr, &creds->set); + } key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_RSA, BUILD_BLOB, chunk_from_thing(rsa), BUILD_END); @@ -334,8 +338,6 @@ static void setup_credentials(chunk_t key_data, chunk_t cert_data) { creds->add_cert(creds, TRUE, cert); } - - lib->credmgr->add_set(lib->credmgr, &creds->set); } START_SETUP(setup_creds) @@ -356,10 +358,19 @@ START_SETUP(setup_ed448_creds) } END_SETUP +START_SETUP(setup_all_creds) +{ + setup_credentials(chunk_from_thing(ecdsa), chunk_from_thing(ecdsa_crt)); + setup_credentials(chunk_from_thing(ed25519), chunk_from_thing(ed25519_crt)); + setup_credentials(chunk_from_thing(ed448), chunk_from_thing(ed448_crt)); +} +END_SETUP + START_TEARDOWN(teardown_creds) { lib->credmgr->remove_set(lib->credmgr, &creds->set); creds->destroy(creds); + creds = NULL; } END_TEARDOWN @@ -587,6 +598,37 @@ static void test_tls_ke_groups(tls_version_t version, uint16_t port, bool cauth, free(config); } +/** + * TLS signature test wrapper function + */ +static void test_tls_signature_schemes(tls_version_t version, uint16_t port, + bool cauth, u_int i) +{ + echo_server_config_t *config; + tls_signature_scheme_t *schemes; + char signature[128]; + int count; + + config = create_config(version, port, cauth); + + start_echo_server(config); + + count = tls_crypto_get_supported_signatures(version, &schemes); + ck_assert(i < count); + snprintf(signature, sizeof(signature), "%N", tls_signature_scheme_names, + schemes[i]); + lib->settings->set_str(lib->settings, "%s.tls.signature", signature, lib->ns); + + run_echo_client(config); + + free(schemes); + + shutdown(config->fd, SHUT_RDWR); + close(config->fd); + + free(config); +} + /** * TLS server version test wrapper function */ @@ -637,25 +679,49 @@ static void test_tls_client(tls_version_t version, uint16_t port, bool cauth, START_TEST(test_tls_12_server) { - test_tls_server(TLS_1_2, 5665, FALSE, _i); + test_tls_server(TLS_1_2, 5661, FALSE, _i); } END_TEST START_TEST(test_tls_13_server) { - test_tls_server(TLS_1_3, 5666, FALSE, _i); + test_tls_server(TLS_1_3, 5662, FALSE, _i); } END_TEST START_TEST(test_tls_13_client) { - test_tls_client(TLS_1_3, 5667, FALSE, _i); + test_tls_client(TLS_1_3, 5663, FALSE, _i); } END_TEST START_TEST(test_tls13_ke_groups) { - test_tls_ke_groups(TLS_1_3, 5668, FALSE, _i); + test_tls_ke_groups(TLS_1_3, 5664, FALSE, _i); +} +END_TEST + +START_TEST(test_tls13_signature_schemes) +{ + test_tls_signature_schemes(TLS_1_3, 5665, FALSE, _i); +} +END_TEST + +START_TEST(test_tls12_signature_schemes) +{ + test_tls_signature_schemes(TLS_1_2, 5666, FALSE, _i); +} +END_TEST + +START_TEST(test_tls11_signature_schemes) +{ + test_tls_signature_schemes(TLS_1_1, 5667, FALSE, _i); +} +END_TEST + +START_TEST(test_tls10_signature_schemes) +{ + test_tls_signature_schemes(TLS_1_0, 5668, FALSE, _i); } END_TEST @@ -742,6 +808,30 @@ Suite *socket_suite_create() tls_crypto_get_supported_groups(NULL)); suite_add_tcase(s, tc); + tc = tcase_create("TLS 1.3/signature schemes"); + tcase_add_checked_fixture(tc, setup_all_creds, teardown_creds); + tcase_add_loop_test(tc, test_tls13_signature_schemes, 0, + tls_crypto_get_supported_signatures(TLS_1_3, NULL)); + suite_add_tcase(s, tc); + + tc = tcase_create("TLS 1.2/signature schemes"); + tcase_add_checked_fixture(tc, setup_all_creds, teardown_creds); + tcase_add_loop_test(tc, test_tls12_signature_schemes, 0, + tls_crypto_get_supported_signatures(TLS_1_2, NULL)); + suite_add_tcase(s, tc); + + tc = tcase_create("TLS 1.1/signature schemes"); + tcase_add_checked_fixture(tc, setup_all_creds, teardown_creds); + tcase_add_loop_test(tc, test_tls11_signature_schemes, 0, + tls_crypto_get_supported_signatures(TLS_1_1, NULL)); + suite_add_tcase(s, tc); + + tc = tcase_create("TLS 1.0/signature schemes"); + tcase_add_checked_fixture(tc, setup_all_creds, teardown_creds); + tcase_add_loop_test(tc, test_tls10_signature_schemes, 0, + tls_crypto_get_supported_signatures(TLS_1_0, NULL)); + suite_add_tcase(s, tc); + tc = tcase_create("TLS 1.3/anon"); tcase_add_checked_fixture(tc, setup_creds, teardown_creds); add_tls_test(test_tls13, TLS_1_3); diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 1693a18b6..07d5ce714 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1442,16 +1442,18 @@ PSS_PARAMS(256); PSS_PARAMS(384); PSS_PARAMS(512); -/** - * Map TLS signature schemes, ordered by preference - */ -static struct { +typedef struct { tls_signature_scheme_t sig; signature_params_t params; /* min/max versions for use in CertificateVerify */ tls_version_t min_version; tls_version_t max_version; -} schemes[] = { +} scheme_algs_t; + +/** + * Map TLS signature schemes, ordered by preference + */ +static scheme_algs_t schemes[] = { { TLS_SIG_ECDSA_SHA256, { .scheme = SIGN_ECDSA_WITH_SHA256_DER }, TLS_1_0, TLS_1_3 }, { TLS_SIG_ECDSA_SHA384, { .scheme = SIGN_ECDSA_WITH_SHA384_DER }, @@ -1484,6 +1486,35 @@ static struct { TLS_1_0, TLS_1_2 }, }; +/** + * Filter signature scheme config + */ +static bool filter_signature_scheme_config(tls_signature_scheme_t signature) +{ + enumerator_t *enumerator; + char *token, *config; + + config = lib->settings->get_str(lib->settings, "%s.tls.signature", NULL, + lib->ns); + if (config) + { + enumerator = enumerator_create_token(config, ",", " "); + while (enumerator->enumerate(enumerator, &token)) + { + tls_signature_scheme_t sig; + + if (enum_from_name(tls_signature_scheme_names, token, &sig) && + sig == signature) + { + enumerator->destroy(enumerator); + return TRUE; + } + } + enumerator->destroy(enumerator); + } + return !config; +} + METHOD(tls_crypto_t, get_signature_algorithms, void, private_tls_crypto_t *this, bio_writer_t *writer, bool cert) { @@ -1504,7 +1535,8 @@ METHOD(tls_crypto_t, get_signature_algorithms, void, if ((cert || (schemes[i].min_version <= max_version && schemes[i].max_version >= min_version)) && lib->plugins->has_feature(lib->plugins, - PLUGIN_PROVIDE(PUBKEY_VERIFY, schemes[i].params.scheme))) + PLUGIN_PROVIDE(PUBKEY_VERIFY, schemes[i].params.scheme)) && + filter_signature_scheme_config(schemes[i].sig)) { supported->write_uint16(supported, schemes[i].sig); } @@ -2496,6 +2528,45 @@ int tls_crypto_get_supported_groups(diffie_hellman_group_t **out) return count; } +/** + * See header. + */ +int tls_crypto_get_supported_signatures(tls_version_t version, + tls_signature_scheme_t **out) +{ + scheme_algs_t sigs[countof(schemes)]; + int count = 0, i; + + /* initialize copy of signature scheme list */ + for (i = 0; i < countof(schemes); i++) + { + /* only RSA_PSS_RSAE schemes supported for signing and verifying */ + if (schemes[i].sig == TLS_SIG_RSA_PSS_PSS_SHA256 || + schemes[i].sig == TLS_SIG_RSA_PSS_PSS_SHA384 || + schemes[i].sig == TLS_SIG_RSA_PSS_PSS_SHA512) + { + continue; + } + if (schemes[i].min_version <= version && + schemes[i].max_version >= version && + lib->plugins->has_feature(lib->plugins, + PLUGIN_PROVIDE(PUBKEY_VERIFY, schemes[i].params.scheme))) + { + sigs[count++] = schemes[i]; + } + } + + if (out) + { + *out = calloc(count, sizeof(tls_signature_scheme_t)); + for (i = 0; i < count; i++) + { + (*out)[i] = sigs[i].sig; + } + } + return count; +} + /** * See header. */ diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h index 3bd75b64e..61d143a7b 100644 --- a/src/libtls/tls_crypto.h +++ b/src/libtls/tls_crypto.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2020 Tobias Brunner - * Copyright (C) 2020 Pascal Knecht + * Copyright (C) 2020-2021 Pascal Knecht * Copyright (C) 2020 Méline Sieber * HSR Hochschule fuer Technik Rapperswil * @@ -683,6 +683,16 @@ int tls_crypto_get_supported_suites(bool null, tls_version_t version, */ int tls_crypto_get_supported_groups(diffie_hellman_group_t **groups); +/** + * Get a list of all supported TLS signature schemes. + * + * @param version TLS version + * @param schemes pointer to allocated signature array, to free(), or NULL + * @return number of signature schemes supported + */ +int tls_crypto_get_supported_signatures(tls_version_t version, + tls_signature_scheme_t **schemes); + /** * Get the TLS curve of a given EC DH group * From 85bde019d31cc55f49a63de7325078f74503b620 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 11 Feb 2021 17:00:05 +0100 Subject: [PATCH 102/107] diffie-hellman: Classify x25519 and x448 as ECDH methods --- src/libstrongswan/crypto/diffie_hellman.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libstrongswan/crypto/diffie_hellman.c b/src/libstrongswan/crypto/diffie_hellman.c index 173882bce..c6c3bc65f 100644 --- a/src/libstrongswan/crypto/diffie_hellman.c +++ b/src/libstrongswan/crypto/diffie_hellman.c @@ -532,6 +532,8 @@ bool diffie_hellman_group_is_ec(diffie_hellman_group_t group) case ECP_256_BP: case ECP_384_BP: case ECP_512_BP: + case CURVE_25519: + case CURVE_448: return TRUE; default: return FALSE; From 311405c34d5a943f4b302c5ef13727a15e23d93f Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 11 Feb 2021 17:09:04 +0100 Subject: [PATCH 103/107] tls-crypto: Don't filter suites with specific ECDH group if any is available Since DH groups (or with TLS < 1.3 curves) are negotiated separately, it doesn't matter which one is listed in the cipher suite as any one could be used. --- src/libtls/tls_crypto.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 07d5ce714..d7faa4e25 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -837,8 +837,10 @@ static void filter_suite(suite_algs_t suites[], int *count, int offset, } if (current.dh && current.dh != suites[i].dh) { - if (suites[i].dh != MODP_NONE) - { /* skip DH group, does not match nor NONE */ + if (suites[i].dh != MODP_NONE && + !(diffie_hellman_group_is_ec(current.dh) && + diffie_hellman_group_is_ec(suites[i].dh))) + { /* skip DH group, does not match nor NONE nor both ECDH */ continue; } } From f77ecf0728f6e7a71e61afe78c7849dec6cce9e9 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 11 Feb 2021 17:46:17 +0100 Subject: [PATCH 104/107] tls-crypto: Fallback to any supported ECDH group If the default group listed in the cipher suite is not supported, we try to use any other supported group (the groups are negotiated separately so we are not locked in to a specific group). --- src/libtls/tls_crypto.c | 58 ++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index d7faa4e25..ed1eea36b 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1418,19 +1418,6 @@ METHOD(tls_crypto_t, select_cipher_suite, tls_cipher_suite_t, return 0; } -METHOD(tls_crypto_t, get_dh_group, diffie_hellman_group_t, - private_tls_crypto_t *this) -{ - suite_algs_t *algs; - - algs = find_suite(this->suite); - if (algs) - { - return algs->dh; - } - return MODP_NONE; -} - /** * Parameters for RSA/PSS signature schemes */ @@ -1629,6 +1616,7 @@ CALLBACK(config_filter, bool, while (orig->enumerate(orig, &group, &curve)) { if (filter_curve_config(curve)) + { if (group_out) { @@ -1654,6 +1642,50 @@ METHOD(tls_crypto_t, create_ec_enumerator, enumerator_t*, config_filter, NULL, NULL); } +/** + * Check if the given ECDH group is supported or return the first one we + * actually do support. + */ +static diffie_hellman_group_t supported_ec_group(private_tls_crypto_t *this, + diffie_hellman_group_t orig) +{ + diffie_hellman_group_t current, first = MODP_NONE; + enumerator_t *enumerator; + + enumerator = create_ec_enumerator(this); + while (enumerator->enumerate(enumerator, ¤t, NULL)) + { + if (current == orig) + { + enumerator->destroy(enumerator); + return orig; + } + else if (first == MODP_NONE) + { + first = current; + } + } + enumerator->destroy(enumerator); + return first; +} + +METHOD(tls_crypto_t, get_dh_group, diffie_hellman_group_t, + private_tls_crypto_t *this) +{ + suite_algs_t *algs; + + algs = find_suite(this->suite); + if (algs) + { + if (diffie_hellman_group_is_ec(algs->dh)) + { + return supported_ec_group(this, algs->dh); + } + return algs->dh; + } + return MODP_NONE; +} + METHOD(tls_crypto_t, set_protection, void, private_tls_crypto_t *this, tls_protection_t *protection) { From 966a26eaa21e89f8adff8fa72f223ee14845e7c0 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 11 Feb 2021 17:47:55 +0100 Subject: [PATCH 105/107] tls-server: Support x25519/448 for TLS 1.2 --- src/libtls/tls_server.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 376a1933a..687fd0ce2 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -829,12 +829,14 @@ static status_t process_key_exchange_dhe(private_tls_server_t *this, bio_reader_t *reader) { chunk_t premaster, pub; + diffie_hellman_group_t group; bool ec; this->crypto->append_handshake(this->crypto, TLS_CLIENT_KEY_EXCHANGE, reader->peek(reader)); - ec = diffie_hellman_group_is_ec(this->dh->get_dh_group(this->dh)); + group = this->dh->get_dh_group(this->dh); + ec = diffie_hellman_group_is_ec(group); if ((ec && !reader->read_data8(reader, &pub)) || (!ec && (!reader->read_data16(reader, &pub) || pub.len == 0))) { @@ -843,7 +845,9 @@ static status_t process_key_exchange_dhe(private_tls_server_t *this, return NEED_MORE; } - if (ec) + if (ec && + group != CURVE_25519 && + group != CURVE_448) { if (pub.ptr[0] != TLS_ANSI_UNCOMPRESSED) { @@ -1528,12 +1532,17 @@ static status_t send_server_key_exchange(private_tls_server_t *this, { writer->write_data16(writer, chunk); } - else + else if (group != CURVE_25519 && + group != CURVE_448) { /* ECP uses 8bit length header only, but a point format */ writer->write_uint8(writer, chunk.len + 1); writer->write_uint8(writer, TLS_ANSI_UNCOMPRESSED); writer->write_data(writer, chunk); } + else + { /* ECPoint uses an 8-bit length header only */ + writer->write_data8(writer, chunk); + } free(chunk.ptr); chunk = chunk_cat("ccc", chunk_from_thing(this->client_random), From 8b2b5a647bfffd2f76b3103cd5d4fab86d68001d Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 11 Feb 2021 18:06:49 +0100 Subject: [PATCH 106/107] proposal: Add aliases for AES-GCM/CCM without explicit ICV length These are mapped to use the default, maximum ICV length of 16 bytes. --- .../crypto/proposal/proposal_keywords_static.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libstrongswan/crypto/proposal/proposal_keywords_static.txt b/src/libstrongswan/crypto/proposal/proposal_keywords_static.txt index ba3fb5233..41c40ef4e 100644 --- a/src/libstrongswan/crypto/proposal/proposal_keywords_static.txt +++ b/src/libstrongswan/crypto/proposal/proposal_keywords_static.txt @@ -45,36 +45,42 @@ aes128ccm12, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV12, 128 aes128ccm96, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV12, 128 aes128ccm16, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV16, 128 aes128ccm128, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV16, 128 +aes128ccm, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV16, 128 aes192ccm8, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV8, 192 aes192ccm64, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV8, 192 aes192ccm12, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV12, 192 aes192ccm96, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV12, 192 aes192ccm16, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV16, 192 aes192ccm128, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV16, 192 +aes192ccm, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV16, 192 aes256ccm8, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV8, 256 aes256ccm64, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV8, 256 aes256ccm12, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV12, 256 aes256ccm96, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV12, 256 aes256ccm16, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV16, 256 aes256ccm128, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV16, 256 +aes256ccm, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV16, 256 aes128gcm8, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV8, 128 aes128gcm64, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV8, 128 aes128gcm12, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV12, 128 aes128gcm96, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV12, 128 aes128gcm16, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV16, 128 aes128gcm128, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV16, 128 +aes128gcm, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV16, 128 aes192gcm8, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV8, 192 aes192gcm64, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV8, 192 aes192gcm12, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV12, 192 aes192gcm96, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV12, 192 aes192gcm16, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV16, 192 aes192gcm128, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV16, 192 +aes192gcm, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV16, 192 aes256gcm8, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV8, 256 aes256gcm64, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV8, 256 aes256gcm12, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV12, 256 aes256gcm96, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV12, 256 aes256gcm16, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV16, 256 aes256gcm128, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV16, 256 +aes256gcm, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV16, 256 aes128gmac, ENCRYPTION_ALGORITHM, ENCR_NULL_AUTH_AES_GMAC, 128 aes192gmac, ENCRYPTION_ALGORITHM, ENCR_NULL_AUTH_AES_GMAC, 192 aes256gmac, ENCRYPTION_ALGORITHM, ENCR_NULL_AUTH_AES_GMAC, 256 From 74b9ba7cdb3500fe788b92c2e58409e87e3345ae Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 11 Feb 2021 18:10:56 +0100 Subject: [PATCH 107/107] tls-crypto: Simplify and extend cipher config filter This way we automatically can filter for newer algorithms (e.g. chacha20poly1305). --- src/libtls/tls_crypto.c | 50 +++++------------------------------------ 1 file changed, 6 insertions(+), 44 deletions(-) diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index ed1eea36b..cf5e5b579 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -966,50 +966,12 @@ static void filter_cipher_config_suites(private_tls_crypto_t *this, enumerator = enumerator_create_token(config, ",", " "); while (enumerator->enumerate(enumerator, &token)) { - if (strcaseeq(token, "aes128") && - suites[i].encr == ENCR_AES_CBC && - suites[i].encr_size == 16) - { - suites[remaining++] = suites[i]; - break; - } - if (strcaseeq(token, "aes256") && - suites[i].encr == ENCR_AES_CBC && - suites[i].encr_size == 32) - { - suites[remaining++] = suites[i]; - break; - } - if (strcaseeq(token, "aes128gcm") && - suites[i].encr == ENCR_AES_GCM_ICV16 && - suites[i].encr_size == 16) - { - suites[remaining++] = suites[i]; - break; - } - if (strcaseeq(token, "aes256gcm") && - suites[i].encr == ENCR_AES_GCM_ICV16 && - suites[i].encr_size == 32) - { - suites[remaining++] = suites[i]; - break; - } - if (strcaseeq(token, "camellia128") && - suites[i].encr == ENCR_CAMELLIA_CBC && - suites[i].encr_size == 16) - { - suites[remaining++] = suites[i]; - break; - } - if (strcaseeq(token, "camellia256") && - suites[i].encr == ENCR_CAMELLIA_CBC && - suites[i].encr_size == 32) - { - suites[remaining++] = suites[i]; - break; - } - if (strcaseeq(token, "null") && - suites[i].encr == ENCR_NULL) + const proposal_token_t *tok; + + tok = lib->proposal->get_token(lib->proposal, token); + if (tok != NULL && tok->type == ENCRYPTION_ALGORITHM && + suites[i].encr == tok->algorithm && + (!tok->keysize || suites[i].encr_size == tok->keysize / 8)) { suites[remaining++] = suites[i]; break;