summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorDaiki Ueno <dueno@redhat.com>2018-10-19 17:52:48 +0200
committerDaiki Ueno <dueno@redhat.com>2018-11-12 14:08:45 +0100
commit79f2f1cf5b91491be5f0e3486c416594ec522b25 (patch)
treeeec06e9a96e5c64449e3469c477fc1d332953d12 /tests
parent8ada9c280c9044644dfad1f234e3da32f0df86a0 (diff)
downloadgnutls-79f2f1cf5b91491be5f0e3486c416594ec522b25.tar.gz
TLS 1.3: implement anti-replay measure using ClientHello recording
This implements ClientHello recording outlined in section 8.2 of RFC 8446. Signed-off-by: Daiki Ueno <dueno@redhat.com>
Diffstat (limited to 'tests')
-rw-r--r--tests/Makefile.am13
-rw-r--r--tests/tls13-early-data.c133
-rw-r--r--tests/tls13/anti_replay.c145
3 files changed, 277 insertions, 14 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am
index b3fee01e00..1ccba11028 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -109,7 +109,8 @@ ctests = tls13/supported_versions tls13/tls12-no-tls13-exts \
tls13/cookie tls13/key_share tls13/prf tls13/post-handshake-with-cert-ticket \
tls12-rollback-detection tls11-rollback-detection \
tls12-check-rollback-val tls11-check-rollback-val tls13/hello_random_value \
- tls13/post-handshake-with-psk tls13/post-handshake-with-cert-auto
+ tls13/post-handshake-with-psk tls13/post-handshake-with-cert-auto \
+ tls13/anti_replay
ctests += tls13/hello_retry_request
@@ -425,6 +426,16 @@ name_constraints_merge_CPPFLAGS = $(AM_CPPFLAGS) \
-I$(top_builddir)/gl \
$(NETTLE_CFLAGS)
+murmur3_CPPFLAGS = $(AM_CPPFLAGS) \
+ -I$(top_srcdir)/gl \
+ -I$(top_builddir)/gl \
+ $(NETTLE_CFLAGS)
+
+tls13_anti_replay_CPPFLAGS = $(AM_CPPFLAGS) \
+ -I$(top_srcdir)/gl \
+ -I$(top_builddir)/gl \
+ $(NETTLE_CFLAGS)
+
dist_check_SCRIPTS = rfc2253-escape-test rsa-md5-collision/rsa-md5-collision.sh systemkey.sh
if !WINDOWS
diff --git a/tests/tls13-early-data.c b/tests/tls13-early-data.c
index 1189eb10c1..f23aec77fa 100644
--- a/tests/tls13-early-data.c
+++ b/tests/tls13-early-data.c
@@ -43,12 +43,14 @@ int main(void)
#include <arpa/inet.h>
#include <unistd.h>
#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
#include <gnutls/dtls.h>
#include <signal.h>
#include <assert.h>
#include "cert-common.h"
#include "utils.h"
+#include "virt-time.h"
/* This program tests the robustness of record sending with padding.
*/
@@ -73,6 +75,25 @@ static void client_log_func(int level, const char *str)
#define EARLY_MSG "Hello TLS, it's early"
#define PRIORITY "NORMAL:-VERS-ALL:+VERS-TLS1.3"
+static const
+gnutls_datum_t hrnd = {(void*)"\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32};
+
+static int gnutls_rnd_works;
+
+int __attribute__ ((visibility ("protected")))
+gnutls_rnd(gnutls_rnd_level_t level, void *data, size_t len)
+{
+ gnutls_rnd_works = 1;
+
+ memset(data, 0xff, len);
+
+ /* Flip the first byte to avoid infinite loop in the RSA
+ * blinding code of Nettle */
+ if (len > 0)
+ memset(data, 0x0, 1);
+ return 0;
+}
+
static void client(int sds[])
{
int ret;
@@ -87,6 +108,11 @@ static void client(int sds[])
gnutls_global_set_log_level(7);
}
+ /* Generate the same ob_ticket_age value, which affects the
+ * binder calculation.
+ */
+ virt_time_init();
+
gnutls_certificate_allocate_credentials(&x509_cred);
for (t = 0; t < SESSIONS; t++) {
@@ -102,6 +128,7 @@ static void client(int sds[])
if (t > 0) {
assert(gnutls_session_set_data(session, session_data.data, session_data.size) >= 0);
assert(gnutls_record_send_early_data(session, EARLY_MSG, sizeof(EARLY_MSG)) >= 0);
+ assert(gnutls_handshake_set_random(session, &hrnd) >= 0);
}
/* Perform the TLS handshake
@@ -130,7 +157,7 @@ static void client(int sds[])
fail("client: Getting resume data failed\n");
}
- if (t == 1) {
+ if (t > 0) {
if (!gnutls_session_is_resumed(session)) {
fail("client: session_is_resumed error (%d)\n", t);
}
@@ -166,6 +193,55 @@ static void client(int sds[])
static pid_t child;
+#define MAX_CLIENT_HELLO_RECORDED 10
+
+struct storage_st {
+ gnutls_datum_t entries[MAX_CLIENT_HELLO_RECORDED];
+ size_t num_entries;
+};
+
+static int
+storage_add(void *ptr, gnutls_datum_t key, gnutls_datum_t value)
+{
+ struct storage_st *storage = ptr;
+ gnutls_datum_t *datum;
+ size_t i;
+
+ for (i = 0; i < storage->num_entries; i++) {
+ if (key.size == storage->entries[i].size &&
+ memcmp(storage->entries[i].data, key.data, key.size) == 0) {
+ return GNUTLS_E_DB_ENTRY_EXISTS;
+ }
+ }
+
+ /* If the maximum number of ClientHello exceeded, reject early
+ * data until next time.
+ */
+ if (storage->num_entries == MAX_CLIENT_HELLO_RECORDED)
+ return GNUTLS_E_DB_ERROR;
+
+ datum = &storage->entries[storage->num_entries];
+ datum->data = gnutls_malloc(key.size);
+ if (!datum->data)
+ return GNUTLS_E_MEMORY_ERROR;
+ memcpy(datum->data, key.data, key.size);
+ datum->size = key.size;
+
+ storage->num_entries++;
+
+ return 0;
+}
+
+static void
+storage_clear(struct storage_st *storage)
+{
+ size_t i;
+
+ for (i = 0; i < storage->num_entries; i++)
+ gnutls_free(storage->entries[i].data);
+ storage->num_entries = 0;
+}
+
static void server(int sds[])
{
int ret;
@@ -173,12 +249,15 @@ static void server(int sds[])
gnutls_session_t session;
gnutls_certificate_credentials_t x509_cred;
gnutls_datum_t session_ticket_key = { NULL, 0 };
+ struct storage_st storage;
+ gnutls_anti_replay_t anti_replay;
int t;
/* this must be called once in the program
*/
global_init();
memset(buffer, 0, sizeof(buffer));
+ memset(&storage, 0, sizeof(storage));
if (debug) {
gnutls_global_set_log_function(server_log_func);
@@ -192,6 +271,10 @@ static void server(int sds[])
gnutls_session_ticket_key_generate(&session_ticket_key);
+ ret = gnutls_anti_replay_init(&anti_replay);
+ if (ret < 0)
+ fail("server: failed to initialize anti-replay\n");
+
for (t = 0; t < SESSIONS; t++) {
int sd = sds[t];
@@ -204,6 +287,10 @@ static void server(int sds[])
gnutls_session_ticket_enable_server(session,
&session_ticket_key);
+ gnutls_db_set_add_function(session, storage_add);
+ gnutls_db_set_ptr(session, &storage);
+ gnutls_anti_replay_enable(session, anti_replay);
+
gnutls_transport_set_int(session, sd);
do {
@@ -220,23 +307,39 @@ static void server(int sds[])
if (debug)
success("server: Handshake was completed\n");
- if (t == 1) {
+ if (t > 0) {
if (!gnutls_session_is_resumed(session)) {
fail("server: session_is_resumed error (%d)\n", t);
}
- if (!(gnutls_session_get_flags(session) & GNUTLS_SFLAGS_EARLY_DATA)) {
- fail("server: early data is not received (%d)\n", t);
- }
-
- ret = gnutls_record_recv_early_data(session, buffer, sizeof(buffer));
- if (ret < 0) {
- fail("server: failed to retrieve early data: %s\n",
- gnutls_strerror(ret));
+ /* as we reuse the same ticket twice, expect
+ * early data only on the first resumption */
+ if (t == 1) {
+ if (gnutls_rnd_works) {
+ if (!(gnutls_session_get_flags(session) & GNUTLS_SFLAGS_EARLY_DATA)) {
+ fail("server: early data is not received (%d)\n", t);
+ }
+ } else {
+ success("server: gnutls_rnd() could not be overridden, skip checking replay (%d)\n", t);
+ }
+
+ ret = gnutls_record_recv_early_data(session, buffer, sizeof(buffer));
+ if (ret < 0) {
+ fail("server: failed to retrieve early data: %s\n",
+ gnutls_strerror(ret));
+ }
+
+ if (ret != sizeof(EARLY_MSG) || memcmp(buffer, EARLY_MSG, ret))
+ fail("server: early data mismatch\n");
+ } else {
+ if (gnutls_rnd_works) {
+ if (gnutls_session_get_flags(session) & GNUTLS_SFLAGS_EARLY_DATA) {
+ fail("server: early data is not rejected (%d)\n", t);
+ }
+ } else {
+ success("server: gnutls_rnd() could not be overridden, skip checking replay (%d)\n", t);
+ }
}
-
- if (ret != sizeof(EARLY_MSG) || memcmp(buffer, EARLY_MSG, ret))
- fail("server: early data mismatch\n");
}
/* see the Getting peer's information example */
@@ -271,6 +374,10 @@ static void server(int sds[])
gnutls_deinit(session);
}
+ gnutls_anti_replay_deinit(anti_replay);
+
+ storage_clear(&storage);
+
gnutls_free(session_ticket_key.data);
gnutls_certificate_free_credentials(x509_cred);
diff --git a/tests/tls13/anti_replay.c b/tests/tls13/anti_replay.c
new file mode 100644
index 0000000000..090dcabbdb
--- /dev/null
+++ b/tests/tls13/anti_replay.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2015-2018 Red Hat, Inc.
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdint.h>
+
+#include "utils.h"
+#include "virt-time.h"
+#include "../../lib/tls13/anti_replay.h"
+#include "../../lib/system.h"
+
+#define MAX_CLIENT_HELLO_RECORDED 10
+
+struct storage_st {
+ gnutls_datum_t entries[MAX_CLIENT_HELLO_RECORDED];
+ size_t num_entries;
+};
+
+static int
+storage_add(void *ptr, gnutls_datum_t key, gnutls_datum_t value)
+{
+ struct storage_st *storage = ptr;
+ gnutls_datum_t *datum;
+ size_t i;
+
+ for (i = 0; i < storage->num_entries; i++) {
+ if (key.size == storage->entries[i].size &&
+ memcmp(storage->entries[i].data, key.data, key.size) == 0) {
+ return GNUTLS_E_DB_ENTRY_EXISTS;
+ }
+ }
+
+ /* If the maximum number of ClientHello exceeded, reject early
+ * data until next time.
+ */
+ if (storage->num_entries == MAX_CLIENT_HELLO_RECORDED)
+ return GNUTLS_E_DB_ERROR;
+
+ datum = &storage->entries[storage->num_entries];
+ datum->data = gnutls_malloc(key.size);
+ if (!datum->data)
+ return GNUTLS_E_MEMORY_ERROR;
+ memcpy(datum->data, key.data, key.size);
+ datum->size = key.size;
+
+ storage->num_entries++;
+
+ return 0;
+}
+
+static void
+storage_clear(struct storage_st *storage)
+{
+ size_t i;
+
+ for (i = 0; i < storage->num_entries; i++)
+ gnutls_free(storage->entries[i].data);
+ storage->num_entries = 0;
+}
+
+void doit(void)
+{
+ gnutls_anti_replay_t anti_replay;
+ gnutls_datum_t key = { (unsigned char *) "\xFF\xFF\xFF\xFF", 4 };
+ struct timespec creation_time;
+ struct storage_st storage;
+ gnutls_session_t session;
+ int ret;
+
+ virt_time_init();
+ memset(&storage, 0, sizeof(storage));
+
+ /* server_ticket_age < client_ticket_age */
+ ret = gnutls_anti_replay_init(&anti_replay);
+ assert(ret == 0);
+ gnutls_anti_replay_set_window(anti_replay, 10000);
+ gnutls_init(&session, GNUTLS_SERVER);
+ gnutls_db_set_add_function(session, storage_add);
+ gnutls_db_set_ptr(session, &storage);
+ gnutls_anti_replay_enable(session, anti_replay);
+ mygettime(&creation_time);
+ ret = _gnutls_anti_replay_check(session, 10000, &creation_time, &key);
+ if (ret != GNUTLS_E_ILLEGAL_PARAMETER)
+ fail("error is not returned, while server_ticket_age < client_ticket_age\n");
+ gnutls_deinit(session);
+ gnutls_anti_replay_deinit(anti_replay);
+ storage_clear(&storage);
+
+ /* server_ticket_age - client_ticket_age > window */
+ ret = gnutls_anti_replay_init(&anti_replay);
+ assert(ret == 0);
+ gnutls_anti_replay_set_window(anti_replay, 10000);
+ gnutls_init(&session, GNUTLS_SERVER);
+ gnutls_db_set_add_function(session, storage_add);
+ gnutls_db_set_ptr(session, &storage);
+ gnutls_anti_replay_enable(session, anti_replay);
+ mygettime(&creation_time);
+ virt_sec_sleep(30);
+ ret = _gnutls_anti_replay_check(session, 10000, &creation_time, &key);
+ if (ret != GNUTLS_E_EARLY_DATA_REJECTED)
+ fail("early data is NOT rejected, while freshness check fails\n");
+ gnutls_deinit(session);
+ gnutls_anti_replay_deinit(anti_replay);
+ storage_clear(&storage);
+
+ /* server_ticket_age - client_ticket_age < window */
+ ret = gnutls_anti_replay_init(&anti_replay);
+ assert(ret == 0);
+ gnutls_anti_replay_set_window(anti_replay, 10000);
+ gnutls_init(&session, GNUTLS_SERVER);
+ gnutls_db_set_add_function(session, storage_add);
+ gnutls_db_set_ptr(session, &storage);
+ gnutls_anti_replay_enable(session, anti_replay);
+ mygettime(&creation_time);
+ virt_sec_sleep(15);
+ ret = _gnutls_anti_replay_check(session, 10000, &creation_time, &key);
+ if (ret != 0)
+ fail("early data is rejected, while freshness check succeeds\n");
+ ret = _gnutls_anti_replay_check(session, 10000, &creation_time, &key);
+ if (ret != GNUTLS_E_EARLY_DATA_REJECTED)
+ fail("early data is NOT rejected for a duplicate key\n");
+ gnutls_deinit(session);
+ gnutls_anti_replay_deinit(anti_replay);
+ storage_clear(&storage);
+}