diff --git a/conf/options/charon.opt b/conf/options/charon.opt index d57fb4ced..df3233046 100644 --- a/conf/options/charon.opt +++ b/conf/options/charon.opt @@ -473,6 +473,24 @@ charon.tls.mac charon.tls.suites List of TLS cipher 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. + + 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.2 + Minimum TLS version to negotiate. + +charon.tls.version_max = 1.2 + 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 84a32f96f..5ee7e3714 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 * @@ -33,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"); } /** @@ -85,14 +108,16 @@ 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; while (times == -1 || times-- > 0) { - 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)); @@ -105,7 +130,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_2, TRUE); + tls = tls_socket_create(FALSE, server, client, fd, cache, min_version, + max_version, TRUE); if (!tls) { close(fd); @@ -125,8 +151,9 @@ 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) +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) { tls_socket_t *tls; int fd, cfd; @@ -162,7 +189,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, client, cfd, cache, min_version, + max_version, TRUE); if (!tls) { close(fd); @@ -207,8 +235,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); @@ -251,11 +279,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); @@ -267,8 +298,9 @@ int main(int argc, char *argv[]) { char *address = NULL; bool listen = FALSE; - int port = 0, times = -1, res; - identification_t *server, *client; + int port = 0, times = -1, res, family = AF_UNSPEC; + identification_t *server, *client = NULL; + tls_version_t min_version = TLS_SUPPORTED_MIN, max_version = TLS_SUPPORTED_MAX; tls_cache_t *cache; host_t *host; @@ -283,7 +315,13 @@ 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' }, + {"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 } }; @@ -306,6 +344,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 */ @@ -326,6 +371,37 @@ 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; + case 'm': + 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(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(tls_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; @@ -337,7 +413,7 @@ int main(int argc, char *argv[]) usage(stderr, argv[0]); return 1; } - host = host_create_from_dns(address, 0, port); + host = host_create_from_dns(address, family, port); if (!host) { DBG1(DBG_TLS, "resolving hostname %s failed", address); @@ -347,12 +423,14 @@ int main(int argc, char *argv[]) cache = tls_cache_create(100, 30); if (listen) { - res = serve(host, server, times, cache); + 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); + res = run_client(host, server, client, times, cache, min_version, + max_version); DESTROY_IF(client); } cache->destroy(cache); diff --git a/src/libcharon/Android.mk b/src/libcharon/Android.mk index cefab67cf..0fd8b57f3 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_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_peer.c tls_server.c tls.c \ ) endif diff --git a/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_soap.c b/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_soap.c index 8e69de095..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_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 b79b92763..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_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 0168b1802..1ad6165f6 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_UNSPEC, + TLS_UNSPEC, FALSE), .tnccs = (tls_t*)tnccs, .auth = auth, ); diff --git a/src/libstrongswan/crypto/diffie_hellman.c b/src/libstrongswan/crypto/diffie_hellman.c index efcfdbc74..c6c3bc65f 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. @@ -493,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; 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/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 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); diff --git a/src/libtls/Makefile.am b/src/libtls/Makefile.am index 7319bd76f..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_aead_expl.c tls_aead_impl.c tls_aead_null.c tls_aead.c \ - tls_server.c tls.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_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..df61b6100 --- /dev/null +++ b/src/libtls/tests/suites/test_hkdf.c @@ -0,0 +1,727 @@ +/* + * 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" + +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 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 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 exp_client_handshake_iv = chunk_from_chars( + 0x71,0xab,0xc2,0xca,0xe4,0xc6,0x99,0xd4,0x7c,0x60,0x02,0x68, + ); + + 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 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 exp_server_handshake_iv = chunk_from_chars( + 0x4c,0x04,0x2d,0xdc,0x12,0x0a,0x38,0xd1,0x41,0x7f,0xc8,0x15, + ); + + 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 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, + ); + + tls_hkdf_t *hkdf = tls_hkdf_create(HASH_SHA256, chunk_empty); + ck_assert(hkdf); + + hkdf->set_shared_secret(hkdf, ulfheim_ecdhe); + + /* Generate client handshake traffic 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); + + check_finished(hkdf, FALSE, exp_client_finished_key); + + /* Generate server handshake traffic 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); + + check_finished(hkdf, TRUE, exp_server_finished_key); + + hkdf->destroy(hkdf); +} +END_TEST + +START_TEST(test_ulfheim_traffic) +{ + 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,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,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,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,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( + 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 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 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 exp_client_handshake_iv = chunk_from_chars( + 0x5b,0xd3,0xc7,0x1b,0x83,0x6e,0x0b,0x76,0xbb,0x73,0x26,0x5f, + ); + + 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 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 */ + 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 */ + 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); + + /* 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 + +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 + +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); + + 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; +} diff --git a/src/libtls/tests/suites/test_socket.c b/src/libtls/tests/suites/test_socket.c index af7eaefd3..4707f5849 100644 --- a/src/libtls/tests/suites/test_socket.c +++ b/src/libtls/tests/suites/test_socket.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2020-2021 Pascal Knecht + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG * @@ -126,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 @@ -217,12 +242,77 @@ 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; - 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); @@ -230,8 +320,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); @@ -243,13 +333,36 @@ 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); } +} - 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_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 @@ -257,6 +370,7 @@ START_TEARDOWN(teardown_creds) { lib->credmgr->remove_set(lib->credmgr, &creds->set); creds->destroy(creds); + creds = NULL; } END_TEARDOWN @@ -298,7 +412,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_SUPPORTED_MIN, config->version, TRUE); ck_assert(tls != NULL); while (TRUE) @@ -374,7 +488,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_SUPPORTED_MIN, config->version, TRUE); ck_assert(tls != NULL); wr = rd = 0; @@ -406,6 +520,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 */ @@ -416,18 +548,11 @@ 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); - 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); @@ -442,6 +567,176 @@ 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_ke_groups(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; + + config = create_config(version, port, cauth); + + 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.ke_group", curve, lib->ns); + + run_echo_client(config); + + free(groups); + + shutdown(config->fd, SHUT_RDWR); + close(config->fd); + + 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 + */ +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, 5661, FALSE, _i); +} +END_TEST + +START_TEST(test_tls_13_server) +{ + test_tls_server(TLS_1_3, 5662, FALSE, _i); +} +END_TEST + +START_TEST(test_tls_13_client) +{ + test_tls_client(TLS_1_3, 5663, FALSE, _i); +} +END_TEST + +START_TEST(test_tls13_ke_groups) +{ + 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 + +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); @@ -478,44 +773,163 @@ START_TEST(test_tls10_mutual) } END_TEST +#define add_tls_test(func, version) \ + 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); + Suite *socket_suite_create() { Suite *s; TCase *tc; - int count; - - count = tls_crypto_get_supported_suites(TRUE, NULL); 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/key exchange groups"); + tcase_add_checked_fixture(tc, setup_creds, teardown_creds); + tcase_add_loop_test(tc, test_tls13_ke_groups, 0, + 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); + 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); - 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; +} + +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); + 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); + + 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); + 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; 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/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; } diff --git a/src/libtls/tests/tls_tests.h b/src/libtls/tests/tls_tests.h index 489b2ddb1..77e548e53 100644 --- a/src/libtls/tests/tls_tests.h +++ b/src/libtls/tests/tls_tests.h @@ -13,5 +13,8 @@ * for more details. */ +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) diff --git a/src/libtls/tls.c b/src/libtls/tls.c index ea39f7fd1..ae14213a0 100644 --- a/src/libtls/tls.c +++ b/src/libtls/tls.c @@ -1,4 +1,8 @@ /* + * Copyright (C) 2021 Tobias Brunner + * Copyright (C) 2020-2021 Pascal Knecht + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2010 Martin Willi * Copyright (C) 2010 revosec AG * @@ -24,14 +28,26 @@ #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_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); + +/** + * 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", @@ -40,12 +56,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 +79,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,20 +98,68 @@ 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_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", + "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); +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, +); + +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 */ @@ -107,9 +188,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 +386,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,32 +454,86 @@ METHOD(tls_t, get_peer_id, identification_t*, return this->handshake->get_peer_id(this->handshake); } -METHOD(tls_t, get_version, tls_version_t, +/** + * 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) + { /* 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); + if (version_str && + enum_from_name(tls_numeric_version_names, version_str, &version)) + { + this->version_min = version; + } + } + if (this->version_max == TLS_UNSPEC) + { /* 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); + 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) { - return this->version; + 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 version) + private_tls_t *this, tls_version_t min_version, tls_version_t max_version) { - if (version > this->version) + 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; } - switch (version) + + this->version_min = min_version; + this->version_max = max_version; + + if (min_version != TLS_UNSPEC && min_version == max_version) { - case TLS_1_0: - case TLS_1_1: - case TLS_1_2: - this->version = 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, @@ -466,7 +613,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 +623,6 @@ tls_t *tls_create(bool is_server, identification_t *server, .destroy = _destroy, }, .is_server = is_server, - .version = TLS_1_2, .application = application, .purpose = purpose, ); diff --git a/src/libtls/tls.h b/src/libtls/tls.h index f832ef50e..9de042b26 100644 --- a/src/libtls/tls.h +++ b/src/libtls/tls.h @@ -1,4 +1,8 @@ /* + * Copyright (C) 2021 Tobias Brunner + * Copyright (C) 2020-2021 Pascal Knecht + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2010 Martin Willi * Copyright (C) 2010 revosec AG * @@ -47,11 +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, }; /** @@ -59,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 */ @@ -81,6 +94,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 +106,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 +137,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 +152,48 @@ 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, + /** 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 **/ + 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, }; @@ -148,6 +207,18 @@ 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; + +/** + * 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. */ @@ -216,19 +287,29 @@ struct tls_t { identification_t* (*get_peer_id)(tls_t *this); /** - * Get the negotiated TLS/SSL version. + * Get the maximum and negotiated TLS 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); /** - * Set the negotiated TLS/SSL version. + * Get the minimum TLS version. + * + * @return min TLS version + */ + tls_version_t (*get_version_min)(tls_t *this); + + /** + * 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_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..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 * @@ -44,13 +47,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 +62,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. @@ -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_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_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; +} 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..cf5e5b579 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-2021 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 * @@ -14,9 +19,12 @@ */ #include "tls_crypto.h" +#include "tls_hkdf.h" #include #include +#include +#include ENUM_BEGIN(tls_cipher_suite_names, TLS_NULL_WITH_NULL_NULL, TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, @@ -175,9 +183,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", @@ -237,7 +253,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", @@ -249,11 +272,44 @@ 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_SHA224, TLS_SIG_ECDSA_SHA224, TLS_SIG_ECDSA_SHA1, + "RSA_PKCS1_SHA224", + "DSA_SHA224", + "ECDSA_SHA224"); +ENUM_NEXT(tls_signature_scheme_names, + 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_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_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, + "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, @@ -279,7 +335,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 +362,18 @@ ENUM(tls_named_curve_names, TLS_SECT163K1, TLS_SECP521R1, "SECP384R1", "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_CURVE448, + "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 +418,11 @@ struct private_tls_crypto_t { */ int suite_count; + /** + * HKDF for TLS 1.3 + */ + tls_hkdf_t *hkdf; + /** * Selected cipher suite */ @@ -415,216 +488,274 @@ typedef struct { integrity_algorithm_t mac; encryption_algorithm_t encr; size_t encr_size; + tls_version_t min_version; + tls_version_t max_version; } suite_algs_t; /** * 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[] = { - { 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 + /* Cipher suites of TLS 1.3: key exchange and authentication + * delegated to extensions, therefore KEY_ANY, MODP_NONE, PRF_UNDEFINED */ + { 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_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_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_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + { 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_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_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, TLS_1_3, + }, + /* Legacy TLS cipher suites */ + { TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, KEY_ECDSA, ECP_384_BIT, - HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32 + HASH_SHA384, PRF_HMAC_SHA2_384, + AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32, + TLS_1_2, 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_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_0, 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_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_RSA_WITH_AES_128_CBC_SHA, - KEY_RSA, ECP_256_BIT, + { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + KEY_ECDSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16 + AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16, + TLS_1_2, TLS_1_2, }, - { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - 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_SHA2_256_256, ENCR_AES_CBC, 16 - }, - { 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_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_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_HMAC_SHA1_160, ENCR_AES_CBC, 16, + TLS_1_0, 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_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_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_DHE_RSA_WITH_AES_128_CBC_SHA256, - KEY_RSA, MODP_3072_BIT, + { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + KEY_RSA, ECP_384_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16 + AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32, + TLS_1_0, TLS_1_2, }, - { TLS_DHE_RSA_WITH_AES_256_CBC_SHA, - KEY_RSA, MODP_3072_BIT, + { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + KEY_RSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32 + AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16, + TLS_1_2, TLS_1_2, }, - { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, - KEY_RSA, MODP_4096_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, 32 + AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16, + TLS_1_2, TLS_1_2, }, - { TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, - KEY_RSA, MODP_3072_BIT, + { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + KEY_RSA, ECP_256_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16 + 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 + 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, + { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + KEY_RSA, MODP_4096_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 16 + AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 32, + TLS_1_2, TLS_1_2, }, - { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, + { TLS_DHE_RSA_WITH_AES_256_CBC_SHA, KEY_RSA, MODP_3072_BIT, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 16 - }, - { 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_AES_CBC, 32, + 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 + AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 32, + TLS_1_2, TLS_1_2, }, - { TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + { 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, + SSL_3_0, 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_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_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_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_3DES, 0 - }, - { 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_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_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_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_RSA_WITH_AES_128_GCM_SHA256, - KEY_RSA, MODP_NONE, - HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16 + 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 + AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32, + TLS_1_2, TLS_1_2, }, - { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, + { TLS_RSA_WITH_AES_256_CBC_SHA256, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 16 + AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 32, + TLS_1_2, TLS_1_2, }, - { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256, + { TLS_RSA_WITH_AES_256_CBC_SHA, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 16 + AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32, + SSL_3_0, TLS_1_2, }, - { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, + { TLS_RSA_WITH_AES_128_GCM_SHA256, KEY_RSA, MODP_NONE, HASH_SHA256, PRF_HMAC_SHA2_256, - AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 32 + AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16, + TLS_1_2, 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_RSA_WITH_AES_128_CBC_SHA, + KEY_RSA, MODP_NONE, + HASH_SHA256, PRF_HMAC_SHA2_256, + AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16, + 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 + 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_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_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, 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_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_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_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_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 + AUTH_HMAC_SHA1_160, ENCR_NULL, 0, + SSL_3_0, TLS_1_2, }, }; @@ -693,8 +824,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 */ @@ -702,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; } } @@ -829,56 +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, "3des") && - suites[i].encr == ENCR_3DES) - { - 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; @@ -909,12 +1002,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) { @@ -972,20 +1059,50 @@ 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.ke_group", 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 */ static void filter_unsupported_suites(suite_algs_t suites[], int *count) { /* filter suite list by each algorithm */ - 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, encr), + lib->crypto->create_crypter_enumerator); + filter_suite(suites, count, offsetof(suite_algs_t, mac), + lib->crypto->create_signer_enumerator); filter_suite(suites, count, offsetof(suite_algs_t, hash), lib->crypto->create_hasher_enumerator); filter_suite(suites, count, offsetof(suite_algs_t, dh), @@ -995,16 +1112,39 @@ 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)]; - int count = countof(suite_algs), i; + tls_version_t min_version, max_version, new_min_version, new_max_version; + bool require_encryption; + int count = 0, i; - /* copy all suites */ - for (i = 0; i < count; i++) + switch (this->tls->get_purpose(this->tls)) { - suites[i] = suite_algs[i]; + 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); + + /* copy all suites appropriate for the current min/max versions */ + for (i = 0; i < countof(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) @@ -1033,17 +1173,40 @@ 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_numeric_version_names, new_min_version, + tls_numeric_version_names, new_max_version); } } METHOD(tls_crypto_t, get_cipher_suites, int, private_tls_crypto_t *this, tls_cipher_suite_t **suites) { - *suites = this->suites; + if (!this->suites) + { + build_cipher_suite_list(this); + } + if (suites) + { + *suites = this->suites; + } return this->suite_count; } @@ -1068,7 +1231,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 +1260,16 @@ 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); + } if (!this->aead_in || !this->aead_out) { DBG1(DBG_TLS, "selected TLS transforms %N-%u not supported", @@ -1124,19 +1295,32 @@ 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(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) { @@ -1179,7 +1363,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)) { @@ -1194,92 +1380,147 @@ 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 + */ +#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); + +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; +} scheme_algs_t; + /** - * Map signature schemes to TLS key types and hashes, ordered by preference + * Map TLS signature schemes, ordered by preference */ -static struct { - tls_signature_algorithm_t sig; - tls_hash_algorithm_t hash; - signature_scheme_t scheme; -} 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 }, +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 }, + 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_0, TLS_1_3 }, + { TLS_SIG_ED448, { .scheme = SIGN_ED448 }, + 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, }, + 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 }, }; +/** + * 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) + private_tls_crypto_t *this, bio_writer_t *writer, bool cert) { bio_writer_t *supported; + tls_version_t min_version, max_version; int i; supported = bio_writer_create(32); - for (i = 0; i < countof(schemes); i++) + if (!cert) { - if (schemes[i].sig == TLS_SIG_RSA && !this->rsa) + 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 ((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)) && + filter_signature_scheme_config(schemes[i].sig)) { - continue; + supported->write_uint16(supported, schemes[i].sig); } - if (schemes[i].sig == TLS_SIG_ECDSA && !this->ecdsa) - { - 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); writer->write_data16(writer, supported->get_buf(supported)); supported->destroy(supported); } /** - * 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, + bool sign) { 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++) + /* strongSwan supports only RSA_PSS_RSAE schemes for signing but can + * 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)) { - if (schemes[i].sig == sig && schemes[i].hash == hash) - { - return schemes[i].scheme; - } + continue; + } + if (schemes[i].sig == sig) + { + return &schemes[i].params; } } - return SIGN_UNKNOWN; + return NULL; } /** @@ -1287,41 +1528,67 @@ 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}, { 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, void *null, enumerator_t *orig, va_list args) { - diffie_hellman_group_t group, *out; - tls_named_curve_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; @@ -1331,8 +1598,54 @@ 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); +} + +/** + * 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, @@ -1357,7 +1670,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; @@ -1403,33 +1716,118 @@ 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). + */ +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, +); + +/** + * 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) { - 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); - 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; + 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; + } + 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) { /* 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, TRUE); + if (params && + type == key_type_from_signature_scheme(params->scheme) && + key->sign(key, params->scheme, params->params, data, &sig)) { done = TRUE; break; @@ -1442,10 +1840,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); } @@ -1477,6 +1874,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; } @@ -1490,33 +1901,52 @@ 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_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, FALSE); + 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 (!key->verify(key, scheme, NULL, data, sig)) + 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; + } + + 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)) + { + 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 { @@ -1552,6 +1982,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; } @@ -1572,7 +2016,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; @@ -1594,6 +2038,38 @@ METHOD(tls_crypto_t, calculate_finished, bool, return TRUE; } +METHOD(tls_crypto_t, calculate_finished, bool, + private_tls_crypto_t *this, bool server, chunk_t *out) +{ + chunk_t finished_key, finished_hash; + + if (!this->hkdf) + { + return FALSE; + } + if (!hash_data(this, this->handshake, &finished_hash)) + { + DBG1(DBG_TLS, "creating hash of handshake failed"); + return FALSE; + } + if (!this->hkdf->derive_finished(this->hkdf, 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); + return TRUE; +} + /** * Derive master secret from premaster, optionally save session */ @@ -1710,6 +2186,119 @@ METHOD(tls_crypto_t, derive_secrets, bool, expand_keys(this, client_random, server_random); } +/** + * Derive and configure the client/server key/IV on an AEAD using a given label. + */ +static bool derive_labeled_key(private_tls_crypto_t *this, bool server, + tls_hkdf_label_t label, tls_aead_t *aead) +{ + chunk_t key = chunk_empty, iv = chunk_empty; + bool success = FALSE; + + if (!this->hkdf->generate_secret(this->hkdf, label, this->handshake, + NULL) || + !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 key material failed"); + goto out; + } + + if (!aead->set_keys(aead, chunk_empty, key, iv)) + { + DBG1(DBG_TLS, "setting AEAD key material failed"); + goto out; + } + success = TRUE; + +out: + 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, *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; + 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) +{ + 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) +{ + 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, + 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; + } + 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) @@ -1755,10 +2344,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; } } } @@ -1776,6 +2367,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); } @@ -1799,12 +2391,17 @@ 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, .verify_handshake = _verify_handshake, + .calculate_finished_legacy = _calculate_finished_legacy, .calculate_finished = _calculate_finished, .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, @@ -1815,6 +2412,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)) { @@ -1826,6 +2425,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: @@ -1840,22 +2441,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; @@ -1866,15 +2458,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); @@ -1894,3 +2491,283 @@ int tls_crypto_get_supported_suites(bool null, tls_cipher_suite_t **out) } 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. + */ +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. + */ +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; +} + +/** + * 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); +} + +/** + * Create an enumerator over supported key types within a specific TLS range + */ +static enumerator_t *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); +} + +/** + * 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 a42e07bb3..61d143a7b 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-2021 Pascal Knecht + * Copyright (C) 2020 Méline Sieber + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2010 Martin Willi * Copyright (C) 2010 revosec AG * @@ -24,10 +29,10 @@ 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_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; @@ -43,213 +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_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_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_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA8, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA9, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAA, }; /** @@ -276,18 +290,42 @@ 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_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, + 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 @@ -327,7 +365,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 +391,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_CURVE448 = 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. @@ -398,7 +445,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); @@ -423,16 +470,19 @@ 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. * - * Enumerates over (diffie_hellman_group_t, tls_named_curve_t) + * Enumerates over (diffie_hellman_group_t, tls_named_group_t) * * @return enumerator */ @@ -454,6 +504,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. * @@ -499,13 +557,24 @@ struct tls_crypto_t { bio_reader_t *reader); /** - * Calculate the data of a TLS 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 * @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. + * + * @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 server, + chunk_t *out); /** * Derive the master secret, MAC and encryption keys. @@ -521,6 +590,29 @@ 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_keys)(tls_crypto_t *this, chunk_t shared_secret); + + /** + * Derive the application keys. + * + * @return TRUE if secret derived successfully + */ + 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. * @@ -576,9 +668,60 @@ 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); + +/** + * 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 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 + * + * @param group diffie hellman group indicator + * @return TLS group indicator + */ +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); + +/** + * Find a private key to encrypt/verify key exchange data + * + * @param min_version minimum negotiated TLS version + * @param max_version maximum negotiated TLS version + * @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_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_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: diff --git a/src/libtls/tls_fragmentation.c b/src/libtls/tls_fragmentation.c index 7a963029d..f2aa1b59d 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-through */ case TLS_APPLICATION_DATA: status = process_application(this, reader); break; @@ -353,7 +357,9 @@ 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 && + type != TLS_KEY_UPDATE && + !this->handshake->cipherspec_changed(this->handshake, FALSE)) { hs->destroy(hs); continue; diff --git a/src/libtls/tls_hkdf.c b/src/libtls/tls_hkdf.c new file mode 100644 index 000000000..c406b3ec9 --- /dev/null +++ b/src/libtls/tls_hkdf.c @@ -0,0 +1,743 @@ +/* + * Copyright (C) 2020 Tobias Brunner + * 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 struct cached_secrets_t { + chunk_t client; + chunk_t server; +} cached_secrets_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; + + /** + * 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; + + /** + * PSK used. + */ + chunk_t psk; + + /** + * PRK used. + */ + chunk_t prk; + + /** + * Handshake traffic secrets. + */ + cached_secrets_t handshake_traffic_secrets; + + /** + * Current traffic secrets. + */ + cached_secrets_t traffic_secrets; +}; + +static char *hkdf_labels[] = { + "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", +}; + +/** + * 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 secret to salt"); + return FALSE; + } + chunk_clear(prk); + if(!this->prf->allocate_bytes(this->prf, ikm, prk)) + { + DBG1(DBG_TLS, "unable to allocate PRF result"); + 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) +{ + prf_plus_t *prf_plus; + + if (!this->prf->set_key(this->prf, prk)) + { + DBG1(DBG_TLS, "unable to set PRF secret to PRK"); + return FALSE; + } + prf_plus = prf_plus_create(this->prf, TRUE, info); + if (!prf_plus || !prf_plus->allocate_bytes(prf_plus, length, okm)) + { + DBG1(DBG_TLS, "unable to allocate PRF+ result"); + DESTROY_IF(prf_plus); + return FALSE; + } + prf_plus->destroy(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 || label.len > 249 || 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); + label = chunk_cata("cc", chunk_from_str("tls13 "), label); + 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 secret, + chunk_t label, chunk_t messages, chunk_t *okm) +{ + chunk_t context; + bool success; + + if (!this->hasher->allocate_hash(this->hasher, messages, &context)) + { + return FALSE; + } + + success = expand_label(this, secret, label, context, + this->hasher->get_hash_size(this->hasher), okm); + chunk_free(&context); + 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, 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 (!psk.ptr) + { + psk = salt_zero; + } + if (!extract(this, salt_zero, psk, &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; + } +} + +/** + * 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 okm; + + 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: + if (!derive_secret(this, this->prk, chunk_from_str("derived"), + chunk_empty, &okm)) + { + DBG1(DBG_TLS, "unable to derive secret"); + return FALSE; + } + + if (!this->shared_secret.ptr) + { + DBG1(DBG_TLS, "no shared secret set"); + chunk_clear(&okm); + return FALSE; + } + + 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: + return TRUE; + default: + DBG1(DBG_TLS, "invalid HKDF phase"); + return FALSE; + } +} + +/** + * 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 okm, 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 */ + if (!derive_secret(this, this->prk, chunk_from_str("derived"), + chunk_empty, &okm)) + { + 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, 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: + return TRUE; + default: + DBG1(DBG_TLS, "invalid HKDF phase"); + return FALSE; + } +} + +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, tls_hkdf_label_t label, chunk_t messages, + chunk_t *secret) +{ + chunk_t okm; + + 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; + 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 (label == TLS_HKDF_UPD_C_TRAFFIC || label == TLS_HKDF_UPD_S_TRAFFIC) + { + chunk_t previous = this->traffic_secrets.client; + + if (label == TLS_HKDF_UPD_S_TRAFFIC) + { + previous = this->traffic_secrets.server; + } + + if (!expand_label(this, previous, chunk_from_str("traffic upd"), + chunk_empty, this->hasher->get_hash_size(this->hasher), + &okm)) + { + DBG1(DBG_TLS, "unable to update secret"); + return FALSE; + } + } + else + { + if (!derive_secret(this, this->prk, chunk_from_str(hkdf_labels[label]), + messages, &okm)) + { + DBG1(DBG_TLS, "unable to derive secret"); + return FALSE; + } + } + + 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->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->traffic_secrets.server); + this->traffic_secrets.server = chunk_clone(okm); + break; + default: + break; + } + + if (secret) + { + *secret = okm; + } + else + { + chunk_clear(&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, + cached_secrets_t *secrets, + bool server, size_t length, chunk_t *key) +{ + chunk_t result = chunk_empty, secret; + + secret = server ? secrets->server : secrets->client; + + 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; +} + +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"), + &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"), + &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"), + &this->handshake_traffic_secrets, server, + this->hasher->get_hash_size(this->hasher), + 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, 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, 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) +{ + return this->prf->set_key(this->prf, key) && + 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); + destroy_secrets(&this->handshake_traffic_secrets); + destroy_secrets(&this->traffic_secrets); + 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, "unsupported hash algorithm %N", hash_algorithm_names, + 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, + .export = _export, + .resume = _resume, + .binder = _binder, + .allocate_bytes = _allocate_bytes, + .destroy = _destroy, + }, + .phase = HKDF_PHASE_0, + .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 (!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 new file mode 100644 index 000000000..c280ebda8 --- /dev/null +++ b/src/libtls/tls_hkdf.h @@ -0,0 +1,182 @@ +/* + * 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 enum tls_hkdf_label_t tls_hkdf_label_t; +typedef struct tls_hkdf_t tls_hkdf_t; + +/** + * TLS HKDF labels + */ +enum tls_hkdf_label_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_UPD_C_TRAFFIC, + TLS_HKDF_UPD_S_TRAFFIC, +}; + +/** + * 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, tls_hkdf_label_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 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, + 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 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 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); + + /** + * 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); + + /** + * 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 + * 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 + */ + 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_ @}*/ diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 1f2439ca1..1b755ea34 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-2021 Pascal Knecht + * Copyright (C) 2020 Méline Sieber + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2010 Martin Willi * Copyright (C) 2010 revosec AG * @@ -37,6 +42,14 @@ 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, + STATE_KEY_UPDATE_REQUESTED, + STATE_KEY_UPDATE_SENT, + STATE_CERT_VERIFY_SENT, } peer_state_t; /** @@ -114,6 +127,21 @@ struct private_tls_peer_t { */ diffie_hellman_t *dh; + /** + * Requested DH group + */ + tls_named_group_t requested_curve; + + /** + * Original cipher suite in HelloRetryRequest + */ + tls_cipher_suite_t original_suite; + + /** + * Cookie extension received in HelloRetryRequest + */ + chunk_t cookie; + /** * Resuming a session? */ @@ -135,6 +163,44 @@ struct private_tls_peer_t { chunk_t cert_types; }; +/* 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. + */ +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 */ @@ -142,13 +208,15 @@ 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; + uint16_t version, cipher, key_type = 0; + bio_reader_t *extensions, *extension; + 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; - 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) || @@ -161,9 +229,87 @@ 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)); - if (!this->tls->set_version(this->tls, version)) + 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 ServerHello"); + 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_SUPPORTED_VERSIONS: + if (!extension->read_uint16(extension, &version)) + { + 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) || + (!is_retry_request && + !(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; + 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; + } + extension->destroy(extension); + } + 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", tls_version_names, version); @@ -171,18 +317,26 @@ 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) { suite = cipher; @@ -193,15 +347,168 @@ 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); 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->original_suite = suite; + 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; + + 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)) + { + 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); + } + 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) +{ + chunk_t ext = chunk_empty; + uint16_t extension_type; + + this->crypto->append_handshake(this->crypto, TLS_ENCRYPTED_EXTENSIONS, + reader->peek(reader)); + + if (!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) + { + bio_reader_t *extensions = bio_reader_create(ext); + + while (extensions->remaining(extensions)) + { + chunk_t extension_data = chunk_empty; + + if (!extensions->read_uint16(extensions, &extension_type) || + !extensions->read_data16(extensions, &extension_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) + { + 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: + /* not supported so far */ + DBG2(DBG_TLS, "ignoring unsupported %N EncryptedExtension", + tls_extension_names, extension_type); + break; + default: + DBG1(DBG_TLS, "received forbidden EncryptedExtension (%d)", + extension_type); + this->alert->add(this->alert, TLS_FATAL, + TLS_ILLEGAL_PARAMETER); + extensions->destroy(extensions); + return NEED_MORE; + } + } + extensions->destroy(extensions); + } + this->state = STATE_ENCRYPTED_EXTENSIONS_RECEIVED; + return NEED_MORE; +} + /** * Check if a server certificate is acceptable for the given server identity */ @@ -252,6 +559,16 @@ 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 (!reader->read_data24(reader, &data)) { DBG1(DBG_TLS, "certificate message header invalid"); @@ -300,6 +617,15 @@ 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, "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; @@ -307,34 +633,36 @@ static status_t process_certificate(private_tls_peer_t *this, } /** - * Find a trusted public key to encrypt/verify key exchange data + * Process CertificateVerify message */ -static public_key_t *find_public_key(private_tls_peer_t *this) +static status_t process_cert_verify(private_tls_peer_t *this, + bio_reader_t *reader) { - public_key_t *public = NULL, *current; - certificate_t *cert, *found; - enumerator_t *enumerator; - auth_cfg_t *auth; + public_key_t *public; + chunk_t msg; - cert = this->server_auth->get(this->server_auth, AUTH_HELPER_SUBJECT_CERT); - if (cert) + public = tls_find_public_key(this->server_auth); + if (!public) { - 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); + 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; } - return public; + + 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; } /** @@ -362,7 +690,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); @@ -407,10 +735,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,12 +792,12 @@ 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; } - 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); @@ -500,14 +828,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); @@ -545,55 +879,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(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) { @@ -613,6 +919,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; } @@ -634,30 +1039,104 @@ 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; - 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 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_legacy(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; + } + verify_data = chunk_from_thing(buf); } - 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; + 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_const(received, chunk_from_thing(buf))) + + 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; } - this->state = STATE_FINISHED_RECEIVED; - this->crypto->append_handshake(this->crypto, TLS_FINISHED, received); + 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 NewSessionTicket 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; +} + +/** + * 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"); + this->state = STATE_KEY_UPDATE_REQUESTED; + } return NEED_MORE; } @@ -666,56 +1145,128 @@ 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_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_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); + } + 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); + } + if (type == TLS_KEY_UPDATE) + { + return process_key_update(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); @@ -727,14 +1278,16 @@ 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; - tls_version_t version; - tls_named_curve_t curve; + 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; enumerator_t *enumerator; - int count, i; + int count, i, v; rng_t *rng; htoun32(&this->client_random, time(NULL)); @@ -750,10 +1303,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); + /* 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); + 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 */ @@ -761,7 +1325,12 @@ 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"); + 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++) { @@ -774,23 +1343,50 @@ 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); - - /* add supported Elliptic Curves, if any */ - enumerator = this->crypto->create_ec_enumerator(this->crypto); - while (enumerator->enumerate(enumerator, NULL, &curve)) + if (this->server->get_type(this->server) == ID_FQDN) { + bio_writer_t *names; + + 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); + } + + 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_ELLIPTIC_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; + } + } 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); @@ -801,19 +1397,62 @@ 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) + + if (version_max >= TLS_1_3) { - bio_writer_t *names; + 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 Server Name Indication for '%Y'", this->server); + 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); + } - 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_SIGNATURE_ALGORITHMS); + extensions->write_uint16(extensions, TLS_EXT_SIGNATURE_ALGORITHMS); + 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); + 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->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, this->dh)) + { + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + extensions->destroy(extensions); + return NEED_MORE; + } + key_share->wrap16(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)); @@ -826,42 +1465,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); } /** @@ -874,48 +1507,79 @@ 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); - if (!this->private) + version_min = this->tls->get_version_min(this->tls); + version_max = this->tls->get_version_max(this->tls); + if (!this->hashsig.len) { - DBG1(DBG_TLS, "no TLS peer certificate found for '%Y', " - "skipping client authentication", this->peer); + 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)) + { + if (!enumerator) + { + DBG1(DBG_TLS, "no common signature algorithms found"); + } + else + { + 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); + + /* 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); @@ -957,7 +1621,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); @@ -1013,15 +1677,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); @@ -1048,7 +1719,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, @@ -1071,16 +1743,34 @@ 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]; - - 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_legacy(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 + { + chunk_t 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); + return NEED_MORE; + } + + writer->write_data(writer, verify_data); + chunk_free(&verify_data); + } *type = TLS_FINISHED; this->state = STATE_FINISHED_SENT; @@ -1088,66 +1778,164 @@ 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) { - 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: + case STATE_CIPHERSPEC_CHANGED_OUT: + case STATE_FINISHED_RECEIVED: + 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); + 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); + this->state = STATE_FINISHED_SENT_KEY_SWITCHED; + 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_SENT_KEY_SWITCHED; + return INVALID_STATE; + default: + 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, 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) - { - return this->state == STATE_FINISHED_RECEIVED; + if (inbound) + { /* accept ChangeCipherSpec after ServerHello or HelloRetryRequest */ + return this->state == STATE_HELLO_RECEIVED || retrying(this); } - 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 (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; @@ -1161,11 +1949,18 @@ 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) { - return this->state == STATE_FINISHED_SENT; + 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*, @@ -1198,6 +1993,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); } diff --git a/src/libtls/tls_protection.c b/src/libtls/tls_protection.c index cea3eca14..2dcca4e2d 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,15 +99,14 @@ 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 (*type == TLS_CHANGE_CIPHER_SPEC && this->version < TLS_1_3) { - this->seq_out = 0; return status; } 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); @@ -128,11 +123,15 @@ 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; } } @@ -145,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); } diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 70d17f22c..687fd0ce2 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2020-2021 Pascal Knecht + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2010 Martin Willi * Copyright (C) 2010 revosec AG * @@ -19,6 +22,7 @@ #include #include +#include typedef struct private_tls_server_t private_tls_server_t; @@ -42,6 +46,12 @@ 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, + STATE_KEY_UPDATE_REQUESTED, + STATE_KEY_UPDATE_SENT, + STATE_FINISHED_SENT_KEY_SWITCHED, } server_state_t; /** @@ -79,11 +89,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 */ @@ -119,6 +124,11 @@ struct private_tls_server_t { */ diffie_hellman_t *dh; + /** + * Requested DH group + */ + tls_named_group_t requested_curve; + /** * Selected TLS cipher suite */ @@ -153,77 +163,191 @@ 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; }; +/** + * 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; +} + /** * 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) { + tls_version_t version_min, version_max; private_key_t *key; - key_type_t type; + auth_cfg_t *auth; + enumerator_t *enumerator; - key = lib->credmgr->get_private(lib->credmgr, KEY_ANY, this->server, - this->server_auth); - if (!key) + version_min = this->tls->get_version_min(this->tls); + version_max = this->tls->get_version_max(this->tls); + enumerator = tls_create_private_key_enumerator(version_min, version_max, + this->hashsig, this->server); + if (!enumerator) + { + DBG1(DBG_TLS, "no common signature algorithms found"); + return FALSE; + } + if (!enumerator->enumerate(enumerator, &key, &auth)) { DBG1(DBG_TLS, "no usable TLS server certificate found for '%Y'", this->server); + enumerator->destroy(enumerator); 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->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; + if (version_max >= TLS_1_3) + { + this->suite = this->crypto->select_cipher_suite(this->crypto, suites, + count, KEY_ANY); + } + else + { + this->suite = this->crypto->select_cipher_suite(this->crypto, suites, + 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 */ + this->suite = this->crypto->select_cipher_suite(this->crypto, + suites, count, + key->get_type(key)); } } - this->private = 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)); + 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; } +/** + * 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; +} + +/** + * 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. + */ +static bool retrying(private_tls_server_t *this) +{ + return this->state == STATE_INIT && this->requested_curve; +} + /** * 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, extension_type = 0; + chunk_t random, session, ciphers, versions = chunk_empty, compression; + 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; + tls_version_t original_version_max; int count, i; rng_t *rng; 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) || @@ -235,36 +359,89 @@ static status_t process_client_hello(private_tls_server_t *this, return NEED_MORE; } - if (ext.len) + /* 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); + + 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_ELLIPTIC_CURVES: - 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: + 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); + 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_data16(extension, &key_shares)) + { + 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); + + 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)); @@ -282,28 +459,77 @@ static status_t process_client_hello(private_tls_server_t *this, } rng->destroy(rng); - if (!this->tls->set_version(this->tls, version)) + original_version_max = this->tls->get_version_max(this->tls); + + 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; + } + } + + /* 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, + 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->suite) + 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 && !retrying(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 { + 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); @@ -317,16 +543,131 @@ 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 (retrying(this) && original_suite != this->suite) { - DBG1(DBG_TLS, "generating TLS session identifier failed, skipped"); + 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); + if (!rng || + !rng->allocate_bytes(rng, SESSION_ID_SIZE, &this->session)) + { + DBG1(DBG_TLS, "generating TLS session identifier failed, skipped"); + } + DESTROY_IF(rng); + } + else + { + chunk_free(&this->session); + this->session = chunk_clone(session); } - 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); } + + if (this->tls->get_version_max(this->tls) >= TLS_1_3) + { + diffie_hellman_group_t group; + tls_named_group_t curve, requesting_curve = 0; + enumerator_t *enumerator; + 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 && + peer_supports_curve(this, curve) && + !peer_offered_curve(peer_key_shares, curve, NULL)) + { + requesting_curve = 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); + this->dh = lib->crypto->create_dh(lib->crypto, group); + break; + } + } + enumerator->destroy(enumerator); + array_destroy(peer_key_shares); + + if (!this->dh) + { + if (retrying(this)) + { + DBG1(DBG_TLS, "already replied with a hello retry request"); + this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE); + return NEED_MORE; + } + + 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; + } + } + else + { + if (peer.key_share.len && + peer.curve != TLS_CURVE25519 && + peer.curve != TLS_CURVE448) + { /* classic format (see RFC 8446, section 4.2.8.2) */ + if (peer.key_share.ptr[0] != TLS_ANSI_UNCOMPRESSED) + { + DBG1(DBG_TLS, "DH point format '%N' not supported", + tls_ansi_point_format_names, peer.key_share.ptr[0]); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + peer.key_share = chunk_skip(peer.key_share, 1); + } + 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); + return NEED_MORE; + } + this->requested_curve = 0; + } + } + this->state = STATE_HELLO_RECEIVED; return NEED_MORE; } @@ -345,6 +686,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"); @@ -352,6 +703,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)) @@ -372,12 +729,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 { @@ -391,6 +742,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; @@ -469,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))) { @@ -483,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) { @@ -541,48 +905,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); - 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; - 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; } @@ -592,88 +937,198 @@ 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(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; + } + this->crypto->change_cipher(this->crypto, TRUE); } - 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; } +/** + * 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) { 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: + 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); + } + 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", + 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); @@ -681,15 +1136,63 @@ 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, diffie_hellman_t *dh) +{ + bio_writer_t *writer; + tls_named_group_t curve; + chunk_t 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, curve); + if (curve == TLS_CURVE25519 || + curve == 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) { - /* TLS version */ - writer->write_uint16(writer, this->tls->get_version(this->tls)); - writer->write_data(writer, chunk_from_thing(this->server_random)); + 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)); + + 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); @@ -700,8 +1203,75 @@ 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); + + DBG2(DBG_TLS, "sending extension: %N", + tls_extension_names, TLS_EXT_KEY_SHARE); + extensions->write_uint16(extensions, TLS_EXT_KEY_SHARE); + if (this->requested_curve) + { + 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); + } + + 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; +} + +/** + * 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; } @@ -718,6 +1288,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); @@ -730,6 +1306,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)) @@ -757,31 +1338,40 @@ static status_t send_certificate(private_tls_server_t *this, } /** - * Send Certificate Request + * Send Certificate Verify */ -static status_t send_certificate_request(private_tls_server_t *this, - tls_handshake_type_t *type, bio_writer_t *writer) +static status_t send_certificate_verify(private_tls_server_t *this, + tls_handshake_type_t *type, + bio_writer_t *writer) { - bio_writer_t *authorities, *supported; + 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; +} + +/* + * Write all available certificate authorities to output writer + */ +static void write_certificate_authorities(bio_writer_t *writer) +{ + 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(this->tls) >= TLS_1_2) - { - this->crypto->get_signature_algorithms(this->crypto, writer); - } - 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; @@ -795,6 +1385,67 @@ 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); + } + + if (this->send_certreq_authorities) + { + write_certificate_authorities(writer); + } + else + { + writer->write_data16(writer, chunk_empty); + } + } + else + { + /* certificate request context as described in RFC 8446, section 4.3.2 */ + writer->write_uint8(writer, 0); + + extensions = bio_writer_create(32); + + 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); + 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; @@ -802,61 +1453,13 @@ 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_curve_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; - 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; -} - -/** - * Check if the peer supports a given TLS curve - */ -bool peer_supports_curve(private_tls_server_t *this, tls_named_curve_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_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,12 +1484,12 @@ 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)) { - 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))) { @@ -894,7 +1497,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); } @@ -929,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), @@ -972,16 +1580,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(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; @@ -990,67 +1616,160 @@ 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) { 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: + 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: + 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: + 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); + 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; + } } } 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 or HelloRetryRequest */ + return this->state == STATE_FINISHED_SENT || + this->state == STATE_FINISHED_SENT_KEY_SWITCHED || + retrying(this); + } + 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 (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; @@ -1064,11 +1783,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*, @@ -1135,6 +1861,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; diff --git a/src/libtls/tls_socket.c b/src/libtls/tls_socket.c index d065bdb28..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, @@ -188,7 +193,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; } @@ -232,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)) @@ -264,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; @@ -292,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); @@ -308,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) { @@ -405,8 +421,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 +459,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, max_version); - return &this->public; } diff --git a/src/libtls/tls_socket.h b/src/libtls/tls_socket.h index 7924c585c..08ccbd287 100644 --- a/src/libtls/tls_socket.h +++ b/src/libtls/tls_socket.h @@ -99,17 +99,21 @@ 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 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 */ 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_ @}*/