From 68c21a22c45cfe6ea80f542dc8ef3a9b84c1498b Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Mon, 16 Jul 2018 11:30:05 +0200 Subject: TLS 1.3: ignore "early_data" extension As 0-RTT is still not implemented in GnuTLS, the server responds with 1-RTT, by skipping decryption failure up to max_early_data_size, as suggested in 4.2.10 Early Data Detection. Resolves #512 Signed-off-by: Daiki Ueno --- NEWS | 1 + doc/Makefile.am | 2 + doc/manpages/Makefile.am | 1 + lib/ext/Makefile.am | 3 +- lib/ext/early_data.c | 102 ++++++++++++++++++++++++++++++++++++++++ lib/ext/early_data.h | 30 ++++++++++++ lib/gnutls_int.h | 10 ++++ lib/hello_ext.c | 2 + lib/includes/gnutls/gnutls.h.in | 2 + lib/libgnutls.map | 6 +++ lib/record.c | 38 +++++++++++++++ lib/state.c | 8 ++++ lib/tls13/session_ticket.c | 12 ++++- symbols.last | 2 + 14 files changed, 216 insertions(+), 3 deletions(-) create mode 100644 lib/ext/early_data.c create mode 100644 lib/ext/early_data.h diff --git a/NEWS b/NEWS index b864a10b9d..1482a60ef5 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,7 @@ See the end for copying conditions. ** API and ABI modifications: GNUTLS_ENABLE_EARLY_START: Added +gnutls_record_set_max_early_data_size: Added * Version 3.6.3 (released 2018-07-16) diff --git a/doc/Makefile.am b/doc/Makefile.am index de4a7711b4..5d6cd0c1bc 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1812,6 +1812,8 @@ FUNCS += functions/gnutls_record_send2 FUNCS += functions/gnutls_record_send2.short FUNCS += functions/gnutls_record_send_range FUNCS += functions/gnutls_record_send_range.short +FUNCS += functions/gnutls_record_set_max_early_data_size +FUNCS += functions/gnutls_record_set_max_early_data_size.short FUNCS += functions/gnutls_record_set_max_size FUNCS += functions/gnutls_record_set_max_size.short FUNCS += functions/gnutls_record_set_state diff --git a/doc/manpages/Makefile.am b/doc/manpages/Makefile.am index 6a802077b0..b4dc4ae8c9 100644 --- a/doc/manpages/Makefile.am +++ b/doc/manpages/Makefile.am @@ -701,6 +701,7 @@ APIMANS += gnutls_record_recv_seq.3 APIMANS += gnutls_record_send.3 APIMANS += gnutls_record_send2.3 APIMANS += gnutls_record_send_range.3 +APIMANS += gnutls_record_set_max_early_data_size.3 APIMANS += gnutls_record_set_max_size.3 APIMANS += gnutls_record_set_state.3 APIMANS += gnutls_record_set_timeout.3 diff --git a/lib/ext/Makefile.am b/lib/ext/Makefile.am index 626d9bae96..cbd68c8588 100644 --- a/lib/ext/Makefile.am +++ b/lib/ext/Makefile.am @@ -46,7 +46,8 @@ libgnutls_ext_la_SOURCES = max_record.c \ cookie.c cookie.h \ psk_ke_modes.c psk_ke_modes.h pre_shared_key.c pre_shared_key.h \ supported_groups.c supported_groups.h \ - ec_point_formats.c ec_point_formats.h + ec_point_formats.c ec_point_formats.h \ + early_data.c early_data.h if ENABLE_ALPN libgnutls_ext_la_SOURCES += alpn.c alpn.h diff --git a/lib/ext/early_data.c b/lib/ext/early_data.c new file mode 100644 index 0000000000..daa0d8fcb7 --- /dev/null +++ b/lib/ext/early_data.c @@ -0,0 +1,102 @@ +/* + * 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 + * + */ + +/* This file contains the code for the Early Data TLS 1.3 extension. + */ + +#include "gnutls_int.h" +#include "errors.h" +#include "num.h" +#include "hello_ext_lib.h" +#include + +static int early_data_recv_params(gnutls_session_t session, + const uint8_t * data, + size_t data_size); +static int early_data_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata); + +const hello_ext_entry_st ext_mod_early_data = { + .name = "Early Data", + .tls_id = 42, + .gid = GNUTLS_EXTENSION_EARLY_DATA, + .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_CLIENT_HELLO, + .parse_type = GNUTLS_EXT_MANDATORY, /* force parsing prior to EXT_TLS extensions */ + .recv_func = early_data_recv_params, + .send_func = early_data_send_params, + .pack_func = NULL, + .unpack_func = NULL, + .deinit_func = _gnutls_hello_ext_default_deinit, + .cannot_be_overriden = 0 +}; + +static int +early_data_recv_params(gnutls_session_t session, + const uint8_t * data, size_t _data_size) +{ + const version_entry_st *vers = get_version(session); + if (!vers || !vers->tls13_sem) + return gnutls_assert_val(0); + if (session->security_parameters.entity == GNUTLS_SERVER) + session->internals.hsk_flags |= HSK_EARLY_DATA_IN_FLIGHT; + + return 0; +} + +/* returns data_size or a negative number on failure + */ +static int +early_data_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + return 0; +} + +/** + * gnutls_record_set_max_early_data_size: + * @session: is a #gnutls_session_t type. + * @size: is the new size + * + * This function sets the maximum early data size in this connection. + * This property can only be set to servers. The client may be + * provided with the maximum allowed size through the "early_data" + * extension of the NewSessionTicket handshake message. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. + * + * Since: 3.6.4 + **/ +int +gnutls_record_set_max_early_data_size(gnutls_session_t session, + size_t size) +{ + if (session->security_parameters.entity == GNUTLS_CLIENT) + return GNUTLS_E_INVALID_REQUEST; + + if (size > UINT32_MAX) + return GNUTLS_E_INVALID_REQUEST; + + session->security_parameters.max_early_data_size = (uint32_t) size; + + return 0; +} diff --git a/lib/ext/early_data.h b/lib/ext/early_data.h new file mode 100644 index 0000000000..dece833efd --- /dev/null +++ b/lib/ext/early_data.h @@ -0,0 +1,30 @@ +/* + * 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 + * + */ + +#ifndef EXT_EARLY_DATA_H +#define EXT_EARLY_DATA_H + +#include + +extern const hello_ext_entry_st ext_mod_early_data; + +#endif diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index c19a909225..fc60f7cc3e 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -193,6 +193,7 @@ typedef enum record_send_state_t { #define IS_DTLS(session) (session->internals.transport == GNUTLS_DGRAM) #define DEFAULT_MAX_RECORD_SIZE 16384 +#define DEFAULT_MAX_EARLY_DATA_SIZE 16384 #define TLS_RECORD_HEADER_SIZE 5 #define DTLS_RECORD_HEADER_SIZE (TLS_RECORD_HEADER_SIZE+8) #define RECORD_HEADER_SIZE(session) (IS_DTLS(session) ? DTLS_RECORD_HEADER_SIZE : TLS_RECORD_HEADER_SIZE) @@ -338,6 +339,7 @@ typedef enum extensions_t { GNUTLS_EXTENSION_SAFE_RENEGOTIATION, GNUTLS_EXTENSION_SERVER_NAME, GNUTLS_EXTENSION_COOKIE, + GNUTLS_EXTENSION_EARLY_DATA, GNUTLS_EXTENSION_PSK_KE_MODES, /* * pre_shared_key and dumbfw must always be the last extensions, @@ -749,6 +751,10 @@ typedef struct { */ uint16_t max_record_send_size; uint16_t max_record_recv_size; + + /* The maximum amount of early data */ + uint32_t max_early_data_size; + /* holds the negotiated certificate type */ gnutls_certificate_type_t cert_type; @@ -1287,6 +1293,7 @@ typedef struct { */ #define HSK_TICKET_RECEIVED (1<<20) /* client: a session ticket was received */ #define HSK_EARLY_START_USED (1<<21) +#define HSK_EARLY_DATA_IN_FLIGHT (1<<22) /* server: early_data extension was seen in ClientHello */ /* The hsk_flags are for use within the ongoing handshake; * they are reset to zero prior to handshake start by gnutls_handshake. */ @@ -1388,6 +1395,9 @@ typedef struct { tls13_ticket_t tls13_ticket; + /* the amount of early data received so far */ + uint32_t early_data_received; + /* If you add anything here, check _gnutls_handshake_internal_state_clear(). */ } internals_st; diff --git a/lib/hello_ext.c b/lib/hello_ext.c index f72afe77fd..ac0fc1ba03 100644 --- a/lib/hello_ext.c +++ b/lib/hello_ext.c @@ -51,6 +51,7 @@ #include #include #include +#include #include "extv.h" #include @@ -82,6 +83,7 @@ static hello_ext_entry_st const *extfunc[MAX_EXT_TYPES+1] = { [GNUTLS_EXTENSION_SIGNATURE_ALGORITHMS] = &ext_mod_sig, [GNUTLS_EXTENSION_KEY_SHARE] = &ext_mod_key_share, [GNUTLS_EXTENSION_COOKIE] = &ext_mod_cookie, + [GNUTLS_EXTENSION_EARLY_DATA] = &ext_mod_early_data, #ifdef ENABLE_DTLS_SRTP [GNUTLS_EXTENSION_SRTP] = &ext_mod_srtp, #endif diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index 2252be0224..984febe83b 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -1384,6 +1384,8 @@ ssize_t gnutls_record_set_max_size(gnutls_session_t session, size_t size); size_t gnutls_record_check_pending(gnutls_session_t session); size_t gnutls_record_check_corked(gnutls_session_t session); +int gnutls_record_set_max_early_data_size(gnutls_session_t session, size_t size); + void gnutls_session_force_valid(gnutls_session_t session); int gnutls_prf(gnutls_session_t session, diff --git a/lib/libgnutls.map b/lib/libgnutls.map index b3208dc875..4a43c07093 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1237,6 +1237,12 @@ GNUTLS_3_6_3 gnutls_priority_init2; } GNUTLS_3_6_2; +GNUTLS_3_6_4 +{ + global: + gnutls_record_set_max_early_data_size; +} GNUTLS_3_6_3; + GNUTLS_FIPS140_3_4 { global: gnutls_cipher_self_test; diff --git a/lib/record.c b/lib/record.c index 1cc328cb93..4589765524 100644 --- a/lib/record.c +++ b/lib/record.c @@ -1334,6 +1334,44 @@ _gnutls_recv_in_buffers(gnutls_session_t session, content_type_t type, _mbuffer_head_remove_bytes(&session->internals.record_recv_buffer, record.header_size + record.length); + + /* FIXME: as 0-RTT is not implemented yet, when early data is + * indicated, skip decryption failure up to + * max_early_data_size. Otherwise, if the record is properly + * decrypted, treat it as the start of client's second flight. + * + * This implements the first way suggested in 4.2.10 of + * draft-ietf-tls-tls13-28. + */ + if (unlikely(session->internals.hsk_flags & HSK_EARLY_DATA_IN_FLIGHT)) { + if (record.type == GNUTLS_APPLICATION_DATA && + (ret < 0 || + /* early data must always be encrypted, treat it + * as decryption failure if otherwise */ + record_params->cipher->id == GNUTLS_CIPHER_NULL)) { + if (record.length > + session->security_parameters.max_early_data_size - + session->internals.early_data_received) { + _gnutls_record_log + ("REC[%p]: max_early_data_size exceeded\n", + session); + ret = GNUTLS_E_UNEXPECTED_PACKET; + goto sanity_check_error; + } + + _gnutls_record_log("REC[%p]: Discarded early data[%u] due to invalid decryption, length: %u\n", + session, + (unsigned int) + _gnutls_uint64touint32(packet_sequence), + (unsigned int) + record.length); + session->internals.early_data_received += record.length; + goto discard; + } else { + session->internals.hsk_flags &= ~HSK_EARLY_DATA_IN_FLIGHT; + } + } + if (ret < 0) { gnutls_assert(); _gnutls_audit_log(session, diff --git a/lib/state.c b/lib/state.c index 8469339c7a..ab0021a64e 100644 --- a/lib/state.c +++ b/lib/state.c @@ -276,6 +276,7 @@ void _gnutls_handshake_internal_state_clear(gnutls_session_t session) session->internals.tfo.connect_addrlen = 0; session->internals.tfo.connect_only = 0; + session->internals.early_data_received = 0; } /** @@ -354,6 +355,13 @@ int gnutls_init(gnutls_session_t * session, unsigned int flags) (*session)->security_parameters.max_record_send_size = DEFAULT_MAX_RECORD_SIZE; + /* set the default early data size for TLS + */ + if ((*session)->security_parameters.entity == GNUTLS_SERVER) { + (*session)->security_parameters.max_early_data_size = + DEFAULT_MAX_EARLY_DATA_SIZE; + } + /* everything else not initialized here is initialized * as NULL or 0. This is why calloc is used. */ diff --git a/lib/tls13/session_ticket.c b/lib/tls13/session_ticket.c index 184c0ac271..8087ba7a8b 100644 --- a/lib/tls13/session_ticket.c +++ b/lib/tls13/session_ticket.c @@ -27,6 +27,7 @@ #include "mbuffers.h" #include "ext/pre_shared_key.h" #include "ext/session_ticket.h" +#include "ext/early_data.h" #include "auth/cert.h" #include "tls13/session_ticket.h" #include "session_pack.h" @@ -329,7 +330,14 @@ cleanup: static int parse_nst_extension(void *ctx, unsigned tls_id, const unsigned char *data, unsigned data_size) { - /* ignore all extensions */ + gnutls_session_t session = ctx; + if (tls_id == ext_mod_early_data.tls_id) { + uint32_t size; + if (data_size < 4) + return gnutls_assert_val(GNUTLS_E_TLS_PACKET_DECODING_ERROR); + size = _gnutls_read_uint32(data); + session->security_parameters.max_early_data_size = size; + } return 0; } @@ -382,7 +390,7 @@ int _gnutls13_recv_session_ticket(gnutls_session_t session, gnutls_buffer_st *bu return gnutls_assert_val(ret); /* Extensions */ - ret = _gnutls_extv_parse(NULL, parse_nst_extension, buf->data, buf->length); + ret = _gnutls_extv_parse(session, parse_nst_extension, buf->data, buf->length); if (ret < 0) return gnutls_assert_val(ret); diff --git a/symbols.last b/symbols.last index cf609caf43..66b2400794 100644 --- a/symbols.last +++ b/symbols.last @@ -2,6 +2,7 @@ GNUTLS_3_4@GNUTLS_3_4 GNUTLS_3_6_0@GNUTLS_3_6_0 GNUTLS_3_6_2@GNUTLS_3_6_2 GNUTLS_3_6_3@GNUTLS_3_6_3 +GNUTLS_3_6_4@GNUTLS_3_6_4 _gnutls_global_init_skip@GNUTLS_3_4 gnutls_aead_cipher_decrypt@GNUTLS_3_4 gnutls_aead_cipher_deinit@GNUTLS_3_4 @@ -680,6 +681,7 @@ gnutls_record_recv_seq@GNUTLS_3_4 gnutls_record_send2@GNUTLS_3_6_3 gnutls_record_send@GNUTLS_3_4 gnutls_record_send_range@GNUTLS_3_4 +gnutls_record_set_max_early_data_size@GNUTLS_3_6_4 gnutls_record_set_max_size@GNUTLS_3_4 gnutls_record_set_state@GNUTLS_3_4 gnutls_record_set_timeout@GNUTLS_3_4 -- cgit v1.2.1 From 66bab376625e350a3aa05ac12803377bc717c196 Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Thu, 2 Aug 2018 16:59:27 +0200 Subject: tlsfuzzer: update to the latest version Also enable test-tls13-0rtt-garbage.py. Signed-off-by: Daiki Ueno --- tests/suite/tls-fuzzer/gnutls-nocert-tls13.json | 4 +++- tests/suite/tls-fuzzer/tlsfuzzer | 2 +- tests/suite/tls-fuzzer/tlslite-ng | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/suite/tls-fuzzer/gnutls-nocert-tls13.json b/tests/suite/tls-fuzzer/gnutls-nocert-tls13.json index a31eec4858..db351652bb 100644 --- a/tests/suite/tls-fuzzer/gnutls-nocert-tls13.json +++ b/tests/suite/tls-fuzzer/gnutls-nocert-tls13.json @@ -55,7 +55,9 @@ "-e", "padding - cipher TLS_AES_128_GCM_SHA256, pad_byte 0, pad_left 0, pad_right 16777183", "-e", "padding - cipher TLS_AES_256_GCM_SHA384, pad_byte 0, pad_left 0, pad_right 16777167"]}, {"name" : "test-tls13-count-tickets.py", - "arguments": ["-p", "@PORT@", "-t", "1"]} + "arguments": ["-p", "@PORT@", "-t", "1"]}, + {"name" : "test-tls13-0rtt-garbage.py", + "arguments": ["-p", "@PORT@"]} ] } ] diff --git a/tests/suite/tls-fuzzer/tlsfuzzer b/tests/suite/tls-fuzzer/tlsfuzzer index 23d66d990f..4433cc402c 160000 --- a/tests/suite/tls-fuzzer/tlsfuzzer +++ b/tests/suite/tls-fuzzer/tlsfuzzer @@ -1 +1 @@ -Subproject commit 23d66d990f1773af5701fb510a3a0ffa59beac5f +Subproject commit 4433cc402c3902750c2e25848d28463df9bc6663 diff --git a/tests/suite/tls-fuzzer/tlslite-ng b/tests/suite/tls-fuzzer/tlslite-ng index 3029e01423..02852484e5 160000 --- a/tests/suite/tls-fuzzer/tlslite-ng +++ b/tests/suite/tls-fuzzer/tlslite-ng @@ -1 +1 @@ -Subproject commit 3029e014231ffe34445d29300f0193a4be4b6ccd +Subproject commit 02852484e5ca9ea5e0c69d525ff45a3a999b386b -- cgit v1.2.1