diff options
author | Daiki Ueno <dueno@redhat.com> | 2018-07-16 11:30:05 +0200 |
---|---|---|
committer | Daiki Ueno <dueno@redhat.com> | 2018-07-24 14:43:16 +0200 |
commit | 9d1f2253d1181213ea3fcc9357e7c6e181f3feef (patch) | |
tree | 770d93e1e699e4e53d9756d843b38b8c1c86cc1b | |
parent | 1debc409d3f751fcf72da37ee919a1fe8cb435e4 (diff) | |
download | gnutls-tmp-skip-zero-rtt.tar.gz |
TLS 1.3: ignore "early_data" extensiontmp-skip-zero-rtt
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.
Signed-off-by: Daiki Ueno <dueno@redhat.com>
-rw-r--r-- | doc/Makefile.am | 2 | ||||
-rw-r--r-- | doc/manpages/Makefile.am | 1 | ||||
-rw-r--r-- | lib/ext/Makefile.am | 3 | ||||
-rw-r--r-- | lib/ext/early_data.c | 100 | ||||
-rw-r--r-- | lib/ext/early_data.h | 30 | ||||
-rw-r--r-- | lib/gnutls_int.h | 11 | ||||
-rw-r--r-- | lib/hello_ext.c | 2 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 2 | ||||
-rw-r--r-- | lib/libgnutls.map | 6 | ||||
-rw-r--r-- | lib/record.c | 24 | ||||
-rw-r--r-- | lib/state.c | 9 | ||||
-rw-r--r-- | lib/tls13/session_ticket.c | 44 | ||||
-rw-r--r-- | symbols.last | 2 |
13 files changed, 231 insertions, 5 deletions
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..7c0380700b --- /dev/null +++ b/lib/ext/early_data.c @@ -0,0 +1,100 @@ +/* + * 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/> + * + */ + +/* 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 <ext/early_data.h> + +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 | + GNUTLS_EXT_FLAG_IGNORE_CLIENT_REQUEST, + .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) +{ + if (session->security_parameters.entity == GNUTLS_SERVER) + session->internals.early_data_indicated = 1; + + 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 <http://www.gnu.org/licenses/> + * + */ + +#ifndef EXT_EARLY_DATA_H +#define EXT_EARLY_DATA_H + +#include <hello_ext.h> + +extern const hello_ext_entry_st ext_mod_early_data; + +#endif diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 6525282a69..00ad814fd1 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) @@ -333,6 +334,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, @@ -742,6 +744,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; @@ -1380,6 +1386,11 @@ typedef struct { tls13_ticket_t tls13_ticket; + /* server: whether early_data extension was seen in ClientHello */ + bool early_data_indicated; + /* 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 a3027130a6..81510ebb83 100644 --- a/lib/hello_ext.c +++ b/lib/hello_ext.c @@ -51,6 +51,7 @@ #include <ext/psk_ke_modes.h> #include <ext/etm.h> #include <ext/cookie.h> +#include <ext/early_data.h> #include "extv.h" #include <num.h> @@ -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 52e9727486..e418abb039 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -1379,6 +1379,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 9b485fd293..ad3913dbf8 100644 --- a/lib/record.c +++ b/lib/record.c @@ -1340,7 +1340,31 @@ _gnutls_recv_in_buffers(gnutls_session_t session, content_type_t type, "Discarded message[%u] due to invalid decryption\n", (unsigned int) _gnutls_uint64touint32(packet_sequence)); + + /* as 0-RTT is not implemented yet, when early data is + * indicated, skip decryption failure up to + * max_early_data_size. */ + if (session->internals.early_data_indicated && + record.type == GNUTLS_APPLICATION_DATA) { + 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; + } + + session->internals.early_data_received += record.length; + goto discard; + } + goto sanity_check_error; + } else if (session->internals.early_data_indicated) { + /* as 0-RTT is not implemented yet, when the early + * data is indicated and the record is properly + * decrypted, treat it as the start of client's second + * flight. */ + session->internals.early_data_indicated = 0; } if (IS_DTLS(session)) { diff --git a/lib/state.c b/lib/state.c index d01475c84a..e3417e6c39 100644 --- a/lib/state.c +++ b/lib/state.c @@ -276,6 +276,8 @@ 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_indicated = 0; + session->internals.early_data_received = 0; } /** @@ -354,6 +356,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..2f8a472b2f 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" @@ -227,12 +228,26 @@ generate_session_ticket(gnutls_session_t session, tls13_ticket_t *ticket) return 0; } +static int +append_early_data_extension(void *ctx, gnutls_buffer_st *buf) +{ + gnutls_session_t session = ctx; + int ret; + + ret = _gnutls_buffer_append_prefix(buf, 32, session->security_parameters.max_early_data_size); + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; +} + int _gnutls13_send_session_ticket(gnutls_session_t session, unsigned nr, unsigned again) { int ret = 0; mbuffer_st *bufel = NULL; gnutls_buffer_st buf; tls13_ticket_t ticket; + unsigned init_pos; unsigned i; /* Client does not send a NewSessionTicket */ @@ -294,13 +309,27 @@ int _gnutls13_send_session_ticket(gnutls_session_t session, unsigned nr, unsigne goto cleanup; } - ret = _gnutls_buffer_append_prefix(&buf, 16, 0); + _gnutls_free_datum(&ticket.ticket); + + ret = _gnutls_extv_append_init(&buf); if (ret < 0) { gnutls_assert(); goto cleanup; } + init_pos = ret; - _gnutls_free_datum(&ticket.ticket); + ret = _gnutls_extv_append(&buf, ext_mod_early_data.tls_id, session, + (extv_append_func)append_early_data_extension); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_extv_append_final(&buf, init_pos); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } bufel = _gnutls_buffer_to_mbuffer(&buf); @@ -329,7 +358,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 +418,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 |