diff options
author | Daiki Ueno <dueno@redhat.com> | 2018-10-19 17:52:48 +0200 |
---|---|---|
committer | Daiki Ueno <dueno@redhat.com> | 2018-11-12 14:08:45 +0100 |
commit | 79f2f1cf5b91491be5f0e3486c416594ec522b25 (patch) | |
tree | eec06e9a96e5c64449e3469c477fc1d332953d12 /tests | |
parent | 8ada9c280c9044644dfad1f234e3da32f0df86a0 (diff) | |
download | gnutls-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.am | 13 | ||||
-rw-r--r-- | tests/tls13-early-data.c | 133 | ||||
-rw-r--r-- | tests/tls13/anti_replay.c | 145 |
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); +} |