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_ @}*/