]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
TLS 1.3: ignore "early_data" extension
authorDaiki Ueno <dueno@redhat.com>
Mon, 16 Jul 2018 09:30:05 +0000 (11:30 +0200)
committerDaiki Ueno <dueno@redhat.com>
Mon, 6 Aug 2018 08:51:58 +0000 (10:51 +0200)
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 <dueno@redhat.com>
14 files changed:
NEWS
doc/Makefile.am
doc/manpages/Makefile.am
lib/ext/Makefile.am
lib/ext/early_data.c [new file with mode: 0644]
lib/ext/early_data.h [new file with mode: 0644]
lib/gnutls_int.h
lib/hello_ext.c
lib/includes/gnutls/gnutls.h.in
lib/libgnutls.map
lib/record.c
lib/state.c
lib/tls13/session_ticket.c
symbols.last

diff --git a/NEWS b/NEWS
index b864a10b9dcecb5e8a2df7214d6049d4a418ac0d..1482a60ef576f20cd01ba65ec114941ae90f3662 100644 (file)
--- 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)
index de4a7711b43c6cc1865400a4f3f2c7c4ac77b07f..5d6cd0c1bc1a5beb4faa396e7ff020d41e3d8435 100644 (file)
@@ -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
index 6a802077b03ae5031dcc1c4c3a4281d74687f538..b4dc4ae8c932aabf1c704bd26c8346c2f6937b08 100644 (file)
@@ -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
index 626d9bae968be256255d6ab0a7b0c910ee74b056..cbd68c85882eff27daad0e7c302e96fd9495debd 100644 (file)
@@ -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 (file)
index 0000000..daa0d8f
--- /dev/null
@@ -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 <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,
+       .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 (file)
index 0000000..dece833
--- /dev/null
@@ -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
index c19a909225f4c707196b64c76e7c33daf75a2594..fc60f7cc3e838e1a4a49f799bfdc727dc1102f6b 100644 (file)
@@ -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;
index f72afe77fd35d20ec7bcc12122652b423f700a82..ac0fc1ba03aa2ce05c0b95c8569747f7e646745a 100644 (file)
@@ -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
index 2252be02242c270b3b9889c871f937943bb0578c..984febe83ba1ef8abd9e478f153e59eb3e079609 100644 (file)
@@ -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,
index b3208dc875f84bc941f933f45e5ab2f0119dd54b..4a43c0709366c59012acf4a5fa8eb00aaf7f32a9 100644 (file)
@@ -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;
index 1cc328cb93ffff655345df8b1b15cf9834995e1b..45897655246c2dc3d3108f524cb68b7f4025a4bb 100644 (file)
@@ -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,
index 8469339c7a5f0324929dd23c784ab6cb571a5bc2..ab0021a64ed925a986f72b0299f5bc822c488a1a 100644 (file)
@@ -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.
         */
index 184c0ac271961c83f88a62b7ec0da38319ef3d47..8087ba7a8b9068f631ae8d8bf576b934744bbeb4 100644 (file)
@@ -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);
 
index cf609caf43699d21b52b2ba9d0cf25b4a031a916..66b240079465e07770156451297d1c8e305e5932 100644 (file)
@@ -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