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 /lib/tls13 | |
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 'lib/tls13')
-rw-r--r-- | lib/tls13/anti_replay.c | 221 | ||||
-rw-r--r-- | lib/tls13/anti_replay.h | 26 |
2 files changed, 247 insertions, 0 deletions
diff --git a/lib/tls13/anti_replay.c b/lib/tls13/anti_replay.c new file mode 100644 index 0000000000..5ae9926afd --- /dev/null +++ b/lib/tls13/anti_replay.c @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2018 Red Hat, Inc. + * + * Author: Daiki Ueno + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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/> + * + */ + +#include "gnutls_int.h" +#include "db.h" +#include "system.h" +#include "tls13/anti_replay.h" + +/* The default time window in milliseconds; RFC8446 suggests the order + * of ten seconds is sufficient for the clients on the Internet. */ +#define DEFAULT_WINDOW_MS 10000 + +struct gnutls_anti_replay_st { + uint32_t window; + struct timespec start_time; +}; + +/** + * gnutls_anti_replay_init: + * @anti_replay: is a pointer to #gnutls_anti_replay_t type + * + * This function will allocate and initialize the @anti_replay context + * to be usable for detect replay attacks. The context can then be + * attached to a @gnutls_session_t with + * gnutls_anti_replay_enable(). + * + * Returns: Zero or a negative error code on error. + * + * Since: 3.6.5 + **/ +int +gnutls_anti_replay_init(gnutls_anti_replay_t *anti_replay) +{ + *anti_replay = gnutls_calloc(1, sizeof(struct gnutls_anti_replay_st)); + if (!*anti_replay) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + (*anti_replay)->window = DEFAULT_WINDOW_MS; + + gnutls_gettime(&(*anti_replay)->start_time); + + return 0; +} + +/** + * gnutls_anti_replay_set_window: + * @anti_replay: is a #gnutls_anti_replay_t type. + * @window: is the time window recording ClientHello, in milliseconds + * + * Sets the time window used for ClientHello recording. In order to + * protect against replay attacks, the server records ClientHello + * messages within this time period from the last update, and + * considers it a replay when a ClientHello outside of the period; if + * a ClientHello arrives within this period, the server checks the + * database and detects duplicates. + * + * For the details of the algorithm, see RFC 8446, section 8.2. + * + * Since: 3.6.5 + */ +void +gnutls_anti_replay_set_window(gnutls_anti_replay_t anti_replay, + unsigned int window) +{ + anti_replay->window = window; +} + +/** + * gnutls_anti_replay_deinit: + * @anti_replay: is a #gnutls_anti_replay type + * + * This function will deinitialize all resources occupied by the given + * anti-replay context. + * + * Since: 3.6.5 + **/ +void +gnutls_anti_replay_deinit(gnutls_anti_replay_t anti_replay) +{ + gnutls_free(anti_replay); +} + +/** + * gnutls_anti_replay_enable: + * @session: is a #gnutls_session_t type. + * @anti_replay: is a #gnutls_anti_replay_t type. + * + * Request that the server should use anti-replay mechanism. + * + * Since: 3.6.5 + **/ +void +gnutls_anti_replay_enable(gnutls_session_t session, + gnutls_anti_replay_t anti_replay) +{ + if (unlikely(session->security_parameters.entity != GNUTLS_SERVER)) { + gnutls_assert(); + return; + } + + session->internals.anti_replay = anti_replay; +} + +int +_gnutls_anti_replay_check(gnutls_session_t session, + uint32_t client_ticket_age, + struct timespec *ticket_creation_time, + gnutls_datum_t *id) +{ + gnutls_anti_replay_t anti_replay = session->internals.anti_replay; + struct timespec now; + uint32_t server_ticket_age, diff; + gnutls_datum_t key = { NULL, 0 }; + gnutls_datum_t entry = { NULL, 0 }; + unsigned char key_buffer[MAX_HASH_SIZE + 12]; + unsigned char entry_buffer[12]; /* magic + timestamp + expire_time */ + unsigned char *p; + int ret; + + if (unlikely(id->size > MAX_HASH_SIZE)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + gnutls_gettime(&now); + server_ticket_age = timespec_sub_ms(&now, ticket_creation_time); + + /* It shouldn't be possible that the server's view of ticket + * age is smaller than the client's view. + */ + if (unlikely(server_ticket_age < client_ticket_age)) + return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + + /* If ticket is created before recording has started, discard + * reject early data. + */ + if (_gnutls_timespec_cmp(ticket_creation_time, + &anti_replay->start_time) < 0) { + _gnutls_handshake_log("anti_replay: ticket is created before recording has started\n"); + return gnutls_assert_val(GNUTLS_E_EARLY_DATA_REJECTED); + } + + /* If certain amount of time (window) has elapsed, rollover + * the recording. + */ + diff = timespec_sub_ms(&now, &anti_replay->start_time); + if (diff > anti_replay->window) + gnutls_gettime(&anti_replay->start_time); + + /* If expected_arrival_time is out of window, reject early + * data. + */ + if (server_ticket_age - client_ticket_age > anti_replay->window) { + _gnutls_handshake_log("anti_replay: server ticket age: %u, client ticket age: %u\n", + server_ticket_age, + client_ticket_age); + return gnutls_assert_val(GNUTLS_E_EARLY_DATA_REJECTED); + } + + /* Check if the ClientHello is stored in the database. + */ + if (!session->internals.db_add_func) + return gnutls_assert_val(GNUTLS_E_EARLY_DATA_REJECTED); + + /* Create a key for database lookup, prefixing window start + * time to ID. Note that this shouldn't clash with session ID + * used in TLS 1.2, because such IDs are 32 octets, while here + * the key becomes 44+ octets. + */ + p = key_buffer; + _gnutls_write_uint32((uint64_t) anti_replay->start_time.tv_sec >> 32, p); + p += 4; + _gnutls_write_uint32(anti_replay->start_time.tv_sec & 0xFFFFFFFF, p); + p += 4; + _gnutls_write_uint32(anti_replay->start_time.tv_nsec, p); + p += 4; + memcpy(p, id->data, id->size); + p += id->size; + key.data = key_buffer; + key.size = p - key_buffer; + + /* Create an entry to be stored on database if the lookup + * failed. This is formatted so that + * gnutls_db_entry_is_expired() work. + */ + p = entry_buffer; + _gnutls_write_uint32(PACKED_SESSION_MAGIC, p); + p += 4; + _gnutls_write_uint32(now.tv_sec, p); + p += 4; + _gnutls_write_uint32(anti_replay->window / 1000, p); + p += 4; + entry.data = entry_buffer; + entry.size = p - entry_buffer; + + ret = session->internals.db_add_func(session->internals.db_ptr, + key, entry); + if (ret < 0) { + _gnutls_handshake_log("anti_replay: duplicate ClientHello found\n"); + return gnutls_assert_val(GNUTLS_E_EARLY_DATA_REJECTED); + } + + return 0; +} diff --git a/lib/tls13/anti_replay.h b/lib/tls13/anti_replay.h new file mode 100644 index 0000000000..e44186c910 --- /dev/null +++ b/lib/tls13/anti_replay.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2018 Red Hat, Inc. + * + * Author: Daiki Ueno + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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/> + * + */ + +int _gnutls_anti_replay_check(gnutls_session_t session, + uint32_t client_ticket_age, + struct timespec *ticket_creation_time, + gnutls_datum_t *id); |