summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaiki Ueno <dueno@redhat.com>2018-07-16 11:30:05 +0200
committerDaiki Ueno <dueno@redhat.com>2018-07-24 14:43:16 +0200
commit9d1f2253d1181213ea3fcc9357e7c6e181f3feef (patch)
tree770d93e1e699e4e53d9756d843b38b8c1c86cc1b
parent1debc409d3f751fcf72da37ee919a1fe8cb435e4 (diff)
downloadgnutls-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.am2
-rw-r--r--doc/manpages/Makefile.am1
-rw-r--r--lib/ext/Makefile.am3
-rw-r--r--lib/ext/early_data.c100
-rw-r--r--lib/ext/early_data.h30
-rw-r--r--lib/gnutls_int.h11
-rw-r--r--lib/hello_ext.c2
-rw-r--r--lib/includes/gnutls/gnutls.h.in2
-rw-r--r--lib/libgnutls.map6
-rw-r--r--lib/record.c24
-rw-r--r--lib/state.c9
-rw-r--r--lib/tls13/session_ticket.c44
-rw-r--r--symbols.last2
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