summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2018-07-19 15:52:26 +0200
committerNikos Mavrogiannopoulos <nmav@redhat.com>2018-08-03 09:18:17 +0200
commitd47111032f5b20eed70093d988741da5d0e69952 (patch)
treedb725ee0bf90d5d500a45c681bb07445574a8b86
parent5b9c6c93c680fdfa63b2854741d446ff50002510 (diff)
downloadgnutls-d47111032f5b20eed70093d988741da5d0e69952.tar.gz
tls1.3: server returns early on handshake when no cert is provided by client
Under TLS1.3 the server knows the negotiated keys early, if no client certificate is sent. In that case, the server is not only able to transmit the session ticket immediately after its finished message, but is also able to transmit data, similarly to false start. Resolves #481 Resolves #457 Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
-rw-r--r--NEWS6
-rw-r--r--doc/cha-gtls-app.texi9
-rw-r--r--lib/alert.c1
-rw-r--r--lib/constate.c75
-rw-r--r--lib/constate.h2
-rw-r--r--lib/gnutls_int.h16
-rw-r--r--lib/handshake-tls13.c188
-rw-r--r--lib/handshake.c12
-rw-r--r--lib/handshake.h15
-rw-r--r--lib/includes/gnutls/gnutls.h.in13
-rw-r--r--lib/record.c54
-rw-r--r--lib/session_pack.c3
-rw-r--r--lib/state.c3
-rw-r--r--lib/tls13/finished.c30
-rw-r--r--lib/tls13/key_update.c8
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/resume-lifetime.c2
-rw-r--r--tests/resume.c49
-rw-r--r--tests/suite/tls-fuzzer/gnutls-nocert-tls13.json17
m---------tests/suite/tls-fuzzer/tlsfuzzer0
m---------tests/suite/tls-fuzzer/tlslite-ng0
-rw-r--r--tests/tls13-early-start.c346
22 files changed, 734 insertions, 117 deletions
diff --git a/NEWS b/NEWS
index 3936b03bc8..b864a10b9d 100644
--- a/NEWS
+++ b/NEWS
@@ -11,8 +11,12 @@ See the end for copying conditions.
gnutls_certificate_set_retrieve_function() which could not handle the case where
no certificates were returned, or the callbacks were set to NULL (see #528).
+** libgnutls: gnutls_handshake() on server returns early on handshake when no
+ certificate is presented by client and the gnutls_init() flag GNUTLS_ENABLE_EARLY_START
+ is specified.
+
** API and ABI modifications:
-No changes since last version.
+GNUTLS_ENABLE_EARLY_START: Added
* Version 3.6.3 (released 2018-07-16)
diff --git a/doc/cha-gtls-app.texi b/doc/cha-gtls-app.texi
index b1573213db..0f89e8f41a 100644
--- a/doc/cha-gtls-app.texi
+++ b/doc/cha-gtls-app.texi
@@ -907,6 +907,15 @@ reduce the round-trips to a single one by taking advantage of the @ref{False Sta
TLS extension. This can be enabled by setting the @acronym{GNUTLS_ENABLE_FALSE_START}
flag on @funcref{gnutls_init}.
+Under TLS 1.3, the server side can start transmitting before the handshake
+is complete (i.e., while the client Finished message is still in flight),
+when no client certificate authentication is requested. This, unlike false
+start, is part of protocol design with no known security implications.
+It can be enabled by setting the @acronym{GNUTLS_ENABLE_EARLY_START} on
+@funcref{gnutls_init}, and the @funcref{gnutls_handshake} function will
+return early, allowing the server to send data earlier.
+
+
@node DTLS sessions
@subsection DTLS sessions
diff --git a/lib/alert.c b/lib/alert.c
index 6b19507780..5755970ca1 100644
--- a/lib/alert.c
+++ b/lib/alert.c
@@ -209,6 +209,7 @@ int gnutls_error_to_alert(int err, int *level)
case GNUTLS_E_UNEXPECTED_PACKET_LENGTH:
case GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH:
case GNUTLS_E_NO_CERTIFICATE_FOUND:
+ case GNUTLS_E_HANDSHAKE_TOO_LARGE:
ret = GNUTLS_A_DECODE_ERROR;
_level = GNUTLS_AL_FATAL;
break;
diff --git a/lib/constate.c b/lib/constate.c
index e43bad811a..3ff6b526da 100644
--- a/lib/constate.c
+++ b/lib/constate.c
@@ -262,34 +262,34 @@ _tls13_update_keys(gnutls_session_t session, hs_stage_t stage,
ret = _tls13_expand_secret(session, APPLICATION_TRAFFIC_UPDATE,
sizeof(APPLICATION_TRAFFIC_UPDATE)-1,
NULL, 0,
- session->key.proto.tls13.hs_ckey,
+ session->key.proto.tls13.ap_ckey,
session->security_parameters.prf->output_size,
- session->key.proto.tls13.hs_ckey);
+ session->key.proto.tls13.ap_ckey);
if (ret < 0)
return gnutls_assert_val(ret);
- ret = _tls13_expand_secret(session, "key", 3, NULL, 0, session->key.proto.tls13.hs_ckey, key_size, key_block);
+ ret = _tls13_expand_secret(session, "key", 3, NULL, 0, session->key.proto.tls13.ap_ckey, key_size, key_block);
if (ret < 0)
return gnutls_assert_val(ret);
- ret = _tls13_expand_secret(session, "iv", 2, NULL, 0, session->key.proto.tls13.hs_ckey, iv_size, iv_block);
+ ret = _tls13_expand_secret(session, "iv", 2, NULL, 0, session->key.proto.tls13.ap_ckey, iv_size, iv_block);
if (ret < 0)
return gnutls_assert_val(ret);
} else {
ret = _tls13_expand_secret(session, APPLICATION_TRAFFIC_UPDATE,
sizeof(APPLICATION_TRAFFIC_UPDATE)-1,
NULL, 0,
- session->key.proto.tls13.hs_skey,
+ session->key.proto.tls13.ap_skey,
session->security_parameters.prf->output_size,
- session->key.proto.tls13.hs_skey);
+ session->key.proto.tls13.ap_skey);
if (ret < 0)
return gnutls_assert_val(ret);
- ret = _tls13_expand_secret(session, "key", 3, NULL, 0, session->key.proto.tls13.hs_skey, key_size, key_block);
+ ret = _tls13_expand_secret(session, "key", 3, NULL, 0, session->key.proto.tls13.ap_skey, key_size, key_block);
if (ret < 0)
return gnutls_assert_val(ret);
- ret = _tls13_expand_secret(session, "iv", 2, NULL, 0, session->key.proto.tls13.hs_skey, iv_size, iv_block);
+ ret = _tls13_expand_secret(session, "iv", 2, NULL, 0, session->key.proto.tls13.ap_skey, iv_size, iv_block);
if (ret < 0)
return gnutls_assert_val(ret);
}
@@ -335,6 +335,7 @@ _tls13_set_keys(gnutls_session_t session, hs_stage_t stage,
const char *label;
unsigned label_size, hsk_len;
const char *keylog_label;
+ void *ckey, *skey;
int ret;
if (stage == STAGE_UPD_OURS || stage == STAGE_UPD_PEERS)
@@ -346,31 +347,33 @@ _tls13_set_keys(gnutls_session_t session, hs_stage_t stage,
label_size = sizeof(HANDSHAKE_CLIENT_TRAFFIC_LABEL)-1;
hsk_len = session->internals.handshake_hash_buffer.length;
keylog_label = "CLIENT_HANDSHAKE_TRAFFIC_SECRET";
+ ckey = session->key.proto.tls13.hs_ckey;
} else {
label = APPLICATION_CLIENT_TRAFFIC_LABEL;
label_size = sizeof(APPLICATION_CLIENT_TRAFFIC_LABEL)-1;
hsk_len = session->internals.handshake_hash_buffer_server_finished_len;
keylog_label = "CLIENT_TRAFFIC_SECRET_0";
+ ckey = session->key.proto.tls13.ap_ckey;
}
ret = _tls13_derive_secret(session, label, label_size,
session->internals.handshake_hash_buffer.data,
hsk_len,
session->key.proto.tls13.temp_secret,
- session->key.proto.tls13.hs_ckey);
+ ckey);
if (ret < 0)
return gnutls_assert_val(ret);
_gnutls_nss_keylog_write(session, keylog_label,
- session->key.proto.tls13.hs_ckey,
+ ckey,
session->security_parameters.prf->output_size);
/* client keys */
- ret = _tls13_expand_secret(session, "key", 3, NULL, 0, session->key.proto.tls13.hs_ckey, key_size, ckey_block);
+ ret = _tls13_expand_secret(session, "key", 3, NULL, 0, ckey, key_size, ckey_block);
if (ret < 0)
return gnutls_assert_val(ret);
- ret = _tls13_expand_secret(session, "iv", 2, NULL, 0, session->key.proto.tls13.hs_ckey, iv_size, civ_block);
+ ret = _tls13_expand_secret(session, "iv", 2, NULL, 0, ckey, iv_size, civ_block);
if (ret < 0)
return gnutls_assert_val(ret);
@@ -379,30 +382,32 @@ _tls13_set_keys(gnutls_session_t session, hs_stage_t stage,
label = HANDSHAKE_SERVER_TRAFFIC_LABEL;
label_size = sizeof(HANDSHAKE_SERVER_TRAFFIC_LABEL)-1;
keylog_label = "SERVER_HANDSHAKE_TRAFFIC_SECRET";
+ skey = session->key.proto.tls13.hs_skey;
} else {
label = APPLICATION_SERVER_TRAFFIC_LABEL;
label_size = sizeof(APPLICATION_SERVER_TRAFFIC_LABEL)-1;
keylog_label = "SERVER_TRAFFIC_SECRET_0";
+ skey = session->key.proto.tls13.ap_skey;
}
ret = _tls13_derive_secret(session, label, label_size,
session->internals.handshake_hash_buffer.data,
hsk_len,
session->key.proto.tls13.temp_secret,
- session->key.proto.tls13.hs_skey);
+ skey);
if (ret < 0)
return gnutls_assert_val(ret);
_gnutls_nss_keylog_write(session, keylog_label,
- session->key.proto.tls13.hs_skey,
+ skey,
session->security_parameters.prf->output_size);
- ret = _tls13_expand_secret(session, "key", 3, NULL, 0, session->key.proto.tls13.hs_skey, key_size, skey_block);
+ ret = _tls13_expand_secret(session, "key", 3, NULL, 0, skey, key_size, skey_block);
if (ret < 0)
return gnutls_assert_val(ret);
- ret = _tls13_expand_secret(session, "iv", 2, NULL, 0, session->key.proto.tls13.hs_skey, iv_size, siv_block);
+ ret = _tls13_expand_secret(session, "iv", 2, NULL, 0, skey, iv_size, siv_block);
if (ret < 0)
return gnutls_assert_val(ret);
@@ -1012,6 +1017,44 @@ int _tls13_connection_state_init(gnutls_session_t session, hs_stage_t stage)
return 0;
}
+int _tls13_read_connection_state_init(gnutls_session_t session, hs_stage_t stage)
+{
+ const uint16_t epoch_next =
+ session->security_parameters.epoch_next;
+ int ret;
+
+ ret = _gnutls_epoch_set_keys(session, epoch_next, stage);
+ if (ret < 0)
+ return ret;
+
+ _gnutls_handshake_log("HSK[%p]: TLS 1.3 set read key with cipher suite: %s\n",
+ session,
+ session->security_parameters.cs->name);
+
+ session->security_parameters.epoch_read = epoch_next;
+
+ return 0;
+}
+
+int _tls13_write_connection_state_init(gnutls_session_t session, hs_stage_t stage)
+{
+ const uint16_t epoch_next =
+ session->security_parameters.epoch_next;
+ int ret;
+
+ ret = _gnutls_epoch_set_keys(session, epoch_next, stage);
+ if (ret < 0)
+ return ret;
+
+ _gnutls_handshake_log("HSK[%p]: TLS 1.3 set write key with cipher suite: %s\n",
+ session,
+ session->security_parameters.cs->name);
+
+ session->security_parameters.epoch_write = epoch_next;
+
+ return 0;
+}
+
static int
_tls13_init_record_state(gnutls_cipher_algorithm_t algo, record_state_st *state)
{
diff --git a/lib/constate.h b/lib/constate.h
index 8a15400f5f..125a48f8f2 100644
--- a/lib/constate.h
+++ b/lib/constate.h
@@ -46,6 +46,8 @@ void _gnutls_epoch_free(gnutls_session_t session,
void _gnutls_set_resumed_parameters(gnutls_session_t session);
int _tls13_connection_state_init(gnutls_session_t session, hs_stage_t stage);
+int _tls13_read_connection_state_init(gnutls_session_t session, hs_stage_t stage);
+int _tls13_write_connection_state_init(gnutls_session_t session, hs_stage_t stage);
static inline int _gnutls_epoch_is_valid(gnutls_session_t session,
int epoch)
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index 6525282a69..c19a909225 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -269,7 +269,7 @@ typedef enum handshake_state_t { STATE0 = 0, STATE1, STATE2,
STATE90=90, STATE91, STATE92, STATE93, STATE94, STATE99=99,
STATE100=100, STATE101, STATE102, STATE103, STATE104,
STATE105, STATE106, STATE107, STATE108, STATE109, STATE110,
- STATE111, STATE112,
+ STATE111, STATE112, STATE113, STATE114,
STATE150 /* key update */
} handshake_state_t;
@@ -299,9 +299,14 @@ typedef enum heartbeat_state_t {
typedef enum recv_state_t {
RECV_STATE_0 = 0,
RECV_STATE_DTLS_RETRANSMIT,
+ /* client-side false start state */
RECV_STATE_FALSE_START_HANDLING, /* we are calling gnutls_handshake() within record_recv() */
RECV_STATE_FALSE_START, /* gnutls_record_recv() should complete the handshake */
- RECV_STATE_ASYNC_HANDSHAKE /* an incomplete async handshake message was seen */
+ /* async handshake msg state */
+ RECV_STATE_ASYNC_HANDSHAKE, /* an incomplete async handshake message was seen */
+ /* server-side early start under TLS1.3; enabled when no client cert is received */
+ RECV_STATE_EARLY_START_HANDLING, /* we are calling gnutls_handshake() within record_recv() */
+ RECV_STATE_EARLY_START /* gnutls_record_recv() should complete the handshake */
} recv_state_t;
#include "str.h"
@@ -504,8 +509,10 @@ struct gnutls_key_st {
* early_secret, client_early_traffic_secret, ... */
uint8_t temp_secret[MAX_HASH_SIZE];
unsigned temp_secret_size; /* depends on negotiated PRF size */
- uint8_t hs_ckey[MAX_HASH_SIZE]; /* client_hs_traffic_secret/client_ap_traffic_secret */
- uint8_t hs_skey[MAX_HASH_SIZE]; /* server_hs_traffic_secret/server_ap_traffic_secret */
+ uint8_t hs_ckey[MAX_HASH_SIZE]; /* client_hs_traffic_secret */
+ uint8_t hs_skey[MAX_HASH_SIZE]; /* server_hs_traffic_secret */
+ uint8_t ap_ckey[MAX_HASH_SIZE]; /* client_ap_traffic_secret */
+ uint8_t ap_skey[MAX_HASH_SIZE]; /* server_ap_traffic_secret */
uint8_t ap_expkey[MAX_HASH_SIZE]; /* exporter_master_secret */
uint8_t ap_rms[MAX_HASH_SIZE]; /* resumption_master_secret */
} tls13; /* tls1.3 */
@@ -1279,6 +1286,7 @@ typedef struct {
* server: a ticket was sent to client.
*/
#define HSK_TICKET_RECEIVED (1<<20) /* client: a session ticket was received */
+#define HSK_EARLY_START_USED (1<<21)
/* The hsk_flags are for use within the ongoing handshake;
* they are reset to zero prior to handshake start by gnutls_handshake. */
diff --git a/lib/handshake-tls13.c b/lib/handshake-tls13.c
index ffd1b1531d..a8666186c7 100644
--- a/lib/handshake-tls13.c
+++ b/lib/handshake-tls13.c
@@ -56,7 +56,8 @@
#include "tls13/key_update.h"
#include "ext/pre_shared_key.h"
-static int generate_hs_traffic_keys(gnutls_session_t session);
+static int generate_rms_keys(gnutls_session_t session);
+static int generate_and_set_hs_traffic_keys(gnutls_session_t session);
static int generate_ap_traffic_keys(gnutls_session_t session);
#define SAVE_TRANSCRIPT \
@@ -90,9 +91,9 @@ int _gnutls13_handshake_client(gnutls_session_t session)
/* fall through */
case STATE101:
ret =
- generate_hs_traffic_keys(session);
+ generate_and_set_hs_traffic_keys(session);
STATE = STATE101;
- IMED_RET("generate session keys", ret, 0);
+ IMED_RET_FATAL("generate session keys", ret, 0);
/* fall through */
case STATE102:
ret = _gnutls13_recv_encrypted_extensions(session);
@@ -141,10 +142,18 @@ int _gnutls13_handshake_client(gnutls_session_t session)
IMED_RET("send finished", ret, 0);
/* fall through */
case STATE111:
+ STATE = STATE111;
+
ret =
generate_ap_traffic_keys(session);
- STATE = STATE111;
- IMED_RET("generate app keys", ret, 0);
+ IMED_RET_FATAL("generate app keys", ret, 0);
+
+ ret = generate_rms_keys(session);
+ IMED_RET_FATAL("generate rms keys", ret, 0);
+
+ /* set traffic keys */
+ ret = _tls13_connection_state_init(session, STAGE_APP);
+ IMED_RET_FATAL("set app keys", ret, 0);
STATE = STATE0;
break;
@@ -152,6 +161,7 @@ int _gnutls13_handshake_client(gnutls_session_t session)
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
}
+
/* explicitly reset any false start flags */
session->internals.recv_state = RECV_STATE_0;
session->internals.initial_negotiation_completed = 1;
@@ -164,6 +174,58 @@ int _gnutls13_handshake_client(gnutls_session_t session)
return 0;
}
+static int generate_non_auth_rms_keys(gnutls_session_t session)
+{
+ int ret;
+ /* we simulate client finished */
+ uint8_t finished[MAX_HASH_SIZE+TLS_HANDSHAKE_HEADER_SIZE];
+ unsigned spos;
+
+ ret = _gnutls13_compute_finished(session->security_parameters.prf,
+ session->key.proto.tls13.hs_ckey,
+ &session->internals.handshake_hash_buffer,
+ finished+TLS_HANDSHAKE_HEADER_SIZE);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ spos = session->internals.handshake_hash_buffer.length;
+
+ finished[0] = GNUTLS_HANDSHAKE_FINISHED;
+ _gnutls_write_uint24(session->security_parameters.prf->output_size, finished+1);
+
+ ret = _gnutls_buffer_append_data(&session->internals.handshake_hash_buffer, finished,
+ TLS_HANDSHAKE_HEADER_SIZE+session->security_parameters.prf->output_size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _tls13_derive_secret(session, RMS_MASTER_LABEL, sizeof(RMS_MASTER_LABEL)-1,
+ session->internals.handshake_hash_buffer.data,
+ session->internals.handshake_hash_buffer.length,
+ session->key.proto.tls13.temp_secret,
+ session->key.proto.tls13.ap_rms);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ session->internals.handshake_hash_buffer.length = spos;
+
+ return 0;
+}
+
+static int generate_rms_keys(gnutls_session_t session)
+{
+ int ret;
+
+ ret = _tls13_derive_secret(session, RMS_MASTER_LABEL, sizeof(RMS_MASTER_LABEL)-1,
+ session->internals.handshake_hash_buffer.data,
+ session->internals.handshake_hash_buffer_client_finished_len,
+ session->key.proto.tls13.temp_secret,
+ session->key.proto.tls13.ap_rms);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+}
+
static int generate_ap_traffic_keys(gnutls_session_t session)
{
int ret;
@@ -192,27 +254,15 @@ static int generate_ap_traffic_keys(gnutls_session_t session)
session->key.proto.tls13.ap_expkey,
session->security_parameters.prf->output_size);
- ret = _tls13_derive_secret(session, RMS_MASTER_LABEL, sizeof(RMS_MASTER_LABEL)-1,
- session->internals.handshake_hash_buffer.data,
- session->internals.handshake_hash_buffer_client_finished_len,
- session->key.proto.tls13.temp_secret,
- session->key.proto.tls13.ap_rms);
- if (ret < 0)
- return gnutls_assert_val(ret);
-
_gnutls_epoch_bump(session);
ret = _gnutls_epoch_dup(session);
if (ret < 0)
return gnutls_assert_val(ret);
- ret = _tls13_connection_state_init(session, STAGE_APP);
- if (ret < 0)
- return gnutls_assert_val(ret);
-
return 0;
}
-static int generate_hs_traffic_keys(gnutls_session_t session)
+static int generate_and_set_hs_traffic_keys(gnutls_session_t session)
{
int ret;
unsigned null_key = 0;
@@ -268,6 +318,8 @@ static int generate_hs_traffic_keys(gnutls_session_t session)
return 0;
}
+#define TICKETS_TO_SEND 1
+
/*
* _gnutls13_handshake_server
* This function does the server stuff of the handshake protocol.
@@ -280,7 +332,7 @@ int _gnutls13_handshake_server(gnutls_session_t session)
case STATE90:
ret = _gnutls13_handshake_hash_buffers_synth(session, session->security_parameters.prf, 0);
STATE = STATE90;
- IMED_RET("reset handshake buffers", ret, 0);
+ IMED_RET_FATAL("reset handshake buffers", ret, 0);
/* fall through */
case STATE91:
ret = _gnutls13_send_hello_retry_request(session, AGAIN(STATE91));
@@ -329,9 +381,9 @@ int _gnutls13_handshake_server(gnutls_session_t session)
/* fall through */
case STATE101:
ret =
- generate_hs_traffic_keys(session);
+ generate_and_set_hs_traffic_keys(session);
STATE = STATE101;
- IMED_RET("generate session keys", ret, 0);
+ IMED_RET_FATAL("generate session keys", ret, 0);
/* fall through */
case STATE102:
ret = _gnutls13_send_encrypted_extensions(session, AGAIN(STATE102));
@@ -359,40 +411,96 @@ int _gnutls13_handshake_server(gnutls_session_t session)
IMED_RET("send finished", ret, 0);
/* fall through */
case STATE107:
+ /* At this point our sending keys should be the app keys
+ * see 4.4.4 at draft-ietf-tls-tls13-28 */
+ ret =
+ generate_ap_traffic_keys(session);
+ IMED_RET_FATAL("generate app keys", ret, 0);
+
+ /* If the session is unauthenticated, try to optimize the handshake by
+ * sending the session ticket early. */
+ if (!(session->internals.hsk_flags & (HSK_CRT_REQ_SENT|HSK_PSK_SELECTED))) {
+ STATE = STATE107;
+
+ ret = generate_non_auth_rms_keys(session);
+ IMED_RET_FATAL("generate rms keys", ret, 0);
+
+ session->internals.hsk_flags |= HSK_EARLY_START_USED;
+ _gnutls_handshake_log("HSK[%p]: unauthenticated session eligible for early start\n", session);
+ }
+
+ ret = _tls13_write_connection_state_init(session, STAGE_APP);
+ IMED_RET_FATAL("set write app keys", ret, 0);
+
+ _gnutls_handshake_log("HSK[%p]: switching early to application traffic keys\n", session);
+
+ /* fall through */
+ case STATE108:
+ if (session->internals.resumed != RESUME_FALSE)
+ _gnutls_set_resumed_parameters(session);
+
+ if (session->internals.hsk_flags & HSK_EARLY_START_USED) {
+ ret = _gnutls13_send_session_ticket(session, TICKETS_TO_SEND,
+ AGAIN(STATE108));
+
+ STATE = STATE108;
+ IMED_RET("send session ticket", ret, 0);
+
+ /* complete this phase of the handshake. We
+ * should be called again by gnutls_record_recv()
+ */
+
+ if (session->internals.flags & GNUTLS_ENABLE_EARLY_START) {
+ STATE = STATE112; /* finished */
+ gnutls_assert();
+
+ session->internals.recv_state = RECV_STATE_EARLY_START;
+ return 0;
+ }
+ }
+ /* fall through */
+ case STATE109:
ret = _gnutls13_recv_certificate(session);
- STATE = STATE107;
+ STATE = STATE109;
IMED_RET("recv certificate", ret, 0);
/* fall through */
- case STATE108:
+ case STATE110:
ret = _gnutls13_recv_certificate_verify(session);
- STATE = STATE108;
+ STATE = STATE110;
IMED_RET("recv certificate verify", ret, 0);
/* fall through */
- case STATE109:
+ case STATE111:
ret = _gnutls_run_verify_callback(session, GNUTLS_CLIENT);
- STATE = STATE109;
+ STATE = STATE111;
if (ret < 0)
return gnutls_assert_val(ret);
/* fall through */
- case STATE110:
+ case STATE112: /* can enter from STATE108 */
ret = _gnutls13_recv_finished(session);
- STATE = STATE110;
+ STATE = STATE112;
IMED_RET("recv finished", ret, 0);
/* fall through */
- case STATE111:
- ret =
- generate_ap_traffic_keys(session);
- STATE = STATE111;
- IMED_RET("generate app keys", ret, 0);
+ case STATE113:
+ /* If we did request a client certificate, then we can
+ * only send the tickets here */
+ STATE = STATE113;
+
+ if (!(session->internals.hsk_flags & HSK_EARLY_START_USED)) {
+ ret = generate_rms_keys(session);
+ IMED_RET_FATAL("generate rms keys", ret, 0);
+ }
- if (session->internals.resumed != RESUME_FALSE)
- _gnutls_set_resumed_parameters(session);
- /* fall through */
- case STATE112:
+ ret = _tls13_read_connection_state_init(session, STAGE_APP);
+ IMED_RET_FATAL("set read app keys", ret, 0);
- ret = _gnutls13_send_session_ticket(session, 1, AGAIN(STATE112));
- STATE = STATE112;
- IMED_RET("send session ticket", ret, 0);
+ /* fall through */
+ case STATE114:
+ if (!(session->internals.hsk_flags & (HSK_TLS13_TICKET_SENT|HSK_EARLY_START_USED))) {
+ ret = _gnutls13_send_session_ticket(session, TICKETS_TO_SEND,
+ AGAIN(STATE114));
+ STATE = STATE114;
+ IMED_RET("send session ticket", ret, 0);
+ }
STATE = STATE0;
break;
diff --git a/lib/handshake.c b/lib/handshake.c
index d0c0f9dc97..ba3911d160 100644
--- a/lib/handshake.c
+++ b/lib/handshake.c
@@ -2462,8 +2462,9 @@ int gnutls_rehandshake(gnutls_session_t session)
if (session->security_parameters.entity == GNUTLS_CLIENT)
return GNUTLS_E_INVALID_REQUEST;
- if (vers->tls13_sem)
+ if (vers->tls13_sem) {
return gnutls_session_key_update(session, GNUTLS_KU_PEER);
+ }
_dtls_async_timer_delete(session);
@@ -2652,9 +2653,8 @@ int gnutls_handshake(gnutls_session_t session)
}
/* clear handshake buffer */
- if (session->security_parameters.entity != GNUTLS_CLIENT ||
- !(session->internals.flags & GNUTLS_ENABLE_FALSE_START) ||
- session->internals.recv_state != RECV_STATE_FALSE_START) {
+ if (session->internals.recv_state != RECV_STATE_FALSE_START &&
+ session->internals.recv_state != RECV_STATE_EARLY_START) {
_gnutls_handshake_hash_buffers_clear(session);
@@ -2839,7 +2839,7 @@ static int handshake_client(gnutls_session_t session)
ret = _gnutls_ext_sr_verify(session);
STATE = STATE4;
- IMED_RET("recv hello", ret, 0);
+ IMED_RET_FATAL("recv hello", ret, 0);
/* fall through */
case STATE5:
if (session->security_parameters.do_recv_supplemental) {
@@ -3257,7 +3257,7 @@ static int handshake_server(gnutls_session_t session)
ret = _gnutls_ext_sr_verify(session);
STATE = STATE2;
- IMED_RET("recv hello", ret, 0);
+ IMED_RET_FATAL("recv hello", ret, 0);
/* fall through */
case STATE3:
ret = _gnutls_send_server_hello(session, AGAIN(STATE3));
diff --git a/lib/handshake.h b/lib/handshake.h
index 248d6a1896..e32de894f2 100644
--- a/lib/handshake.h
+++ b/lib/handshake.h
@@ -28,6 +28,11 @@
#include "record.h"
#include <assert.h>
+/* The following two macros are used in the handshake state machines; the first
+ * (IMED_RET) accounts for non-fatal errors and re-entry to current state, while
+ * the latter invalidates the handshake on any error (to be used by functions
+ * that are not expected to return non-fatal errors).
+ */
#define IMED_RET( str, ret, allow_alert) do { \
if (ret < 0) { \
/* EAGAIN and INTERRUPTED are always non-fatal */ \
@@ -54,6 +59,16 @@
return ret; \
} } while (0)
+#define IMED_RET_FATAL( str, ret, allow_alert) do { \
+ if (ret < 0) { \
+ gnutls_assert(); \
+ if (gnutls_error_is_fatal(ret) == 0) \
+ ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); \
+ session_invalidate(session); \
+ _gnutls_handshake_hash_buffers_clear(session); \
+ return ret; \
+ } } while (0)
+
int _gnutls_send_handshake(gnutls_session_t session, mbuffer_st * bufel,
gnutls_handshake_description_t type);
int _gnutls_recv_hello_request(gnutls_session_t session, void *data,
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index 6baebc295c..2252be0224 100644
--- a/lib/includes/gnutls/gnutls.h.in
+++ b/lib/includes/gnutls/gnutls.h.in
@@ -381,6 +381,10 @@ typedef enum {
* @GNUTLS_NO_REPLAY_PROTECTION: Disable any replay protection in DTLS. This must only be used if replay protection is achieved using other means. Since 3.2.2.
* @GNUTLS_ALLOW_ID_CHANGE: Allow the peer to replace its certificate, or change its ID during a rehandshake. This change is often used in attacks and thus prohibited by default. Since 3.5.0.
* @GNUTLS_ENABLE_FALSE_START: Enable the TLS false start on client side if the negotiated ciphersuites allow it. This will enable sending data prior to the handshake being complete, and may introduce a risk of crypto failure when combined with certain key exchanged; for that GnuTLS may not enable that option in ciphersuites that are known to be not safe for false start. Since 3.5.0.
+ * @GNUTLS_ENABLE_EARLY_START: Under TLS1.3 allow the server to return earlier than the full handshake
+ * finish; similarly to false start the handshake will be completed once data are received by the
+ * client, while the server is able to transmit sooner. This is not enabled by default as it could
+ * break certain existing server assumptions and use-cases. Since 3.6.4.
* @GNUTLS_FORCE_CLIENT_CERT: When in client side and only a single cert is specified, send that certificate irrespective of the issuers expected by the server. Since 3.5.0.
* @GNUTLS_NO_TICKETS: Flag to indicate that the session should not use resumption with session tickets.
* @GNUTLS_KEY_SHARE_TOP3: Generate key shares for the top-3 different groups which are enabled.
@@ -428,7 +432,8 @@ typedef enum {
GNUTLS_KEY_SHARE_TOP3 = (1<<13),
GNUTLS_POST_HANDSHAKE_AUTH = (1<<14),
GNUTLS_NO_AUTO_REKEY = (1<<15),
- GNUTLS_SAFE_PADDING_CHECK = (1<<16)
+ GNUTLS_SAFE_PADDING_CHECK = (1<<16),
+ GNUTLS_ENABLE_EARLY_START = (1<<17)
} gnutls_init_flags_t;
/* compatibility defines (previous versions of gnutls
@@ -1441,9 +1446,10 @@ unsigned gnutls_session_etm_status(gnutls_session_t session);
* @GNUTLS_SFLAGS_RFC7919: The RFC7919 Diffie-Hellman parameters were negotiated
* @GNUTLS_SFLAGS_HB_LOCAL_SEND: The heartbeat negotiation allows the local side to send heartbeat messages
* @GNUTLS_SFLAGS_HB_PEER_SEND: The heartbeat negotiation allows the peer to send heartbeat messages
- * @GNUTLS_SFLAGS_FALSE_START: The appdata set with gnutls_handshake_set_appdata() were sent during handshake (false start)
+ * @GNUTLS_SFLAGS_FALSE_START: False start was used in this client session.
* @GNUTLS_SFLAGS_SESSION_TICKET: A session ticket has been received by the server.
* @GNUTLS_SFLAGS_POST_HANDSHAKE_AUTH: Indicates client capability for post-handshake auth; set only on server side.
+ * @GNUTLS_SFLAGS_EARLY_START: The TLS1.3 server session returned early.
*
* Enumeration of different session parameters.
*/
@@ -1456,7 +1462,8 @@ typedef enum {
GNUTLS_SFLAGS_FALSE_START = 1<<5,
GNUTLS_SFLAGS_RFC7919 = 1<<6,
GNUTLS_SFLAGS_SESSION_TICKET = 1<<7,
- GNUTLS_SFLAGS_POST_HANDSHAKE_AUTH = 1<<8
+ GNUTLS_SFLAGS_POST_HANDSHAKE_AUTH = 1<<8,
+ GNUTLS_SFLAGS_EARLY_START = 1<<9
} gnutls_session_flags_t;
unsigned gnutls_session_get_flags(gnutls_session_t session);
diff --git a/lib/record.c b/lib/record.c
index 9b485fd293..1cc328cb93 100644
--- a/lib/record.c
+++ b/lib/record.c
@@ -1521,30 +1521,48 @@ check_session_status(gnutls_session_t session, unsigned ms)
return gnutls_assert_val(ret);
return GNUTLS_E_AGAIN;
+ case RECV_STATE_EARLY_START_HANDLING:
case RECV_STATE_FALSE_START_HANDLING:
return 1;
case RECV_STATE_FALSE_START:
/* if false start is not complete we always expect for handshake packets
* prior to anything else. */
- if (session->security_parameters.entity == GNUTLS_CLIENT &&
- (session->internals.flags & GNUTLS_ENABLE_FALSE_START)) {
- /* Attempt to complete handshake */
+ if (session->security_parameters.entity != GNUTLS_CLIENT ||
+ !(session->internals.flags & GNUTLS_ENABLE_FALSE_START))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
- session->internals.recv_state = RECV_STATE_FALSE_START_HANDLING;
- ret = gnutls_handshake(session);
- if (ret < 0) {
- /* a temp or fatal error, make sure we reset the state
- * so we can resume or temp errors */
- session->internals.recv_state = RECV_STATE_FALSE_START;
- gnutls_assert();
- return ret;
- }
+ /* Attempt to complete handshake */
- session->internals.recv_state = RECV_STATE_0;
- return 1;
- } else {
+ session->internals.recv_state = RECV_STATE_FALSE_START_HANDLING;
+ ret = gnutls_handshake(session);
+ if (ret < 0) {
+ /* a temp or fatal error, make sure we reset the state
+ * so we can resume on temp errors */
+ session->internals.recv_state = RECV_STATE_FALSE_START;
+ return gnutls_assert_val(ret);
+ }
+
+ session->internals.recv_state = RECV_STATE_0;
+ return 1;
+ case RECV_STATE_EARLY_START:
+ /* if early start is not complete we always expect for handshake packets
+ * prior to anything else. */
+ if (session->security_parameters.entity != GNUTLS_SERVER ||
+ !(session->internals.flags & GNUTLS_ENABLE_EARLY_START))
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ /* Attempt to complete handshake */
+ session->internals.recv_state = RECV_STATE_EARLY_START_HANDLING;
+ ret = gnutls_handshake(session);
+ if (ret < 0) {
+ /* a temp or fatal error, make sure we reset the state
+ * so we can resume on temp errors */
+ session->internals.recv_state = RECV_STATE_EARLY_START;
+ return gnutls_assert_val(ret);
}
+
+ session->internals.recv_state = RECV_STATE_0;
+ return 1;
case RECV_STATE_DTLS_RETRANSMIT:
ret = _dtls_retransmit(session);
if (ret < 0)
@@ -1809,7 +1827,8 @@ gnutls_record_send2(gnutls_session_t session, const void *data,
/* this is to protect buggy applications from sending unencrypted
* data. We allow sending however, if we are in false start handshake
* state. */
- if (session->internals.recv_state != RECV_STATE_FALSE_START)
+ if (session->internals.recv_state != RECV_STATE_FALSE_START &&
+ session->internals.recv_state != RECV_STATE_EARLY_START)
return gnutls_assert_val(GNUTLS_E_UNAVAILABLE_DURING_HANDSHAKE);
}
@@ -1984,7 +2003,8 @@ gnutls_record_recv(gnutls_session_t session, void *data, size_t data_size)
/* this is to protect buggy applications from sending unencrypted
* data. We allow sending however, if we are in false start handshake
* state. */
- if (session->internals.recv_state != RECV_STATE_FALSE_START)
+ if (session->internals.recv_state != RECV_STATE_FALSE_START &&
+ session->internals.recv_state != RECV_STATE_EARLY_START)
return gnutls_assert_val(GNUTLS_E_UNAVAILABLE_DURING_HANDSHAKE);
}
diff --git a/lib/session_pack.c b/lib/session_pack.c
index 615eb6c2a5..2ed04a8eeb 100644
--- a/lib/session_pack.c
+++ b/lib/session_pack.c
@@ -860,7 +860,8 @@ pack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps)
size_t cur_size;
if (session->security_parameters.epoch_read
- != session->security_parameters.epoch_write) {
+ != session->security_parameters.epoch_write &&
+ !(session->internals.hsk_flags & HSK_EARLY_START_USED)) {
gnutls_assert();
return GNUTLS_E_UNAVAILABLE_DURING_HANDSHAKE;
}
diff --git a/lib/state.c b/lib/state.c
index d01475c84a..8469339c7a 100644
--- a/lib/state.c
+++ b/lib/state.c
@@ -1351,6 +1351,9 @@ unsigned gnutls_session_get_flags(gnutls_session_t session)
flags |= GNUTLS_SFLAGS_HB_PEER_SEND;
if (session->internals.hsk_flags & HSK_FALSE_START_USED)
flags |= GNUTLS_SFLAGS_FALSE_START;
+ if ((session->internals.hsk_flags & HSK_EARLY_START_USED) &&
+ (session->internals.flags & GNUTLS_ENABLE_EARLY_START))
+ flags |= GNUTLS_SFLAGS_EARLY_START;
if (session->internals.hsk_flags & HSK_USED_FFDHE)
flags |= GNUTLS_SFLAGS_RFC7919;
if (session->internals.hsk_flags & HSK_TICKET_RECEIVED)
diff --git a/lib/tls13/finished.c b/lib/tls13/finished.c
index cb768b9739..6d88e8feed 100644
--- a/lib/tls13/finished.c
+++ b/lib/tls13/finished.c
@@ -75,10 +75,17 @@ int _gnutls13_recv_finished(gnutls_session_t session)
hash_size = session->security_parameters.prf->output_size;
- if (session->security_parameters.entity == GNUTLS_CLIENT)
- base_key = session->key.proto.tls13.hs_skey;
- else
- base_key = session->key.proto.tls13.hs_ckey;
+ if (!session->internals.initial_negotiation_completed) {
+ if (session->security_parameters.entity == GNUTLS_CLIENT)
+ base_key = session->key.proto.tls13.hs_skey;
+ else
+ base_key = session->key.proto.tls13.hs_ckey;
+ } else {
+ if (session->security_parameters.entity == GNUTLS_CLIENT)
+ base_key = session->key.proto.tls13.ap_skey;
+ else
+ base_key = session->key.proto.tls13.ap_ckey;
+ }
ret = _gnutls13_compute_finished(session->security_parameters.prf,
base_key,
@@ -133,10 +140,17 @@ int _gnutls13_send_finished(gnutls_session_t session, unsigned again)
hash_size = session->security_parameters.prf->output_size;
- if (session->security_parameters.entity == GNUTLS_CLIENT)
- base_key = session->key.proto.tls13.hs_ckey;
- else
- base_key = session->key.proto.tls13.hs_skey;
+ if (!session->internals.initial_negotiation_completed) {
+ if (session->security_parameters.entity == GNUTLS_CLIENT)
+ base_key = session->key.proto.tls13.hs_ckey;
+ else
+ base_key = session->key.proto.tls13.hs_skey;
+ } else {
+ if (session->security_parameters.entity == GNUTLS_CLIENT)
+ base_key = session->key.proto.tls13.ap_ckey;
+ else
+ base_key = session->key.proto.tls13.ap_skey;
+ }
ret = _gnutls13_compute_finished(session->security_parameters.prf,
base_key,
diff --git a/lib/tls13/key_update.c b/lib/tls13/key_update.c
index 9bbfca15e3..d9c495efdc 100644
--- a/lib/tls13/key_update.c
+++ b/lib/tls13/key_update.c
@@ -44,7 +44,13 @@ static int update_keys(gnutls_session_t session, hs_stage_t stage)
if (ret < 0)
return gnutls_assert_val(ret);
- ret = _tls13_connection_state_init(session, stage);
+ /* If we send a key update during early start, only update our
+ * write keys */
+ if (session->internals.recv_state == RECV_STATE_EARLY_START) {
+ ret = _tls13_write_connection_state_init(session, stage);
+ } else {
+ ret = _tls13_connection_state_init(session, stage);
+ }
if (ret < 0)
return gnutls_assert_val(ret);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index c870ec3605..c9f62f16f0 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -127,6 +127,8 @@ ctests += tls13/no-psk-exts
ctests += tls13/psk-dumbfw
+ctests += tls13-early-start
+
ctests += mini-record-2 simple gnutls_hmac_fast set_pkcs12_cred cert certuniqueid tls-neg-ext-key \
mpi certificate_set_x509_crl dn parse_ca x509-dn x509-dn-decode record-sizes \
hostname-check cve-2008-4989 pkcs12_s2k chainverify record-sizes-range \
diff --git a/tests/resume-lifetime.c b/tests/resume-lifetime.c
index 99c9c5b346..ad71d41059 100644
--- a/tests/resume-lifetime.c
+++ b/tests/resume-lifetime.c
@@ -66,7 +66,7 @@ static int handshake_callback(gnutls_session_t session, unsigned int htype,
{
struct hsk_st *h = gnutls_session_get_ptr(session);
- if (htype == GNUTLS_HANDSHAKE_FINISHED && incoming) {
+ if (htype == GNUTLS_HANDSHAKE_FINISHED && !incoming) {
if (h->sleep_at_finished)
virt_sec_sleep(h->sleep_at_finished);
return 0;
diff --git a/tests/resume.c b/tests/resume.c
index 891a209313..1a08e0f0bb 100644
--- a/tests/resume.c
+++ b/tests/resume.c
@@ -81,6 +81,8 @@ struct params_res {
int try_sni;
int expire_ticket;
int change_ciphersuite;
+ int early_start;
+ int no_early_start;
};
pid_t child;
@@ -119,7 +121,7 @@ struct params_res resume_tests[] = {
.first_no_ext_master = 1,
.second_no_ext_master = 0},
#endif
-#ifdef TLS13
+#if defined(TLS13)
/* only makes sense under TLS1.3 as negotiation involves a new
* handshake with different parameters */
{.desc = "try to resume from session ticket (different cipher order)",
@@ -129,6 +131,23 @@ struct params_res resume_tests[] = {
.change_ciphersuite = 1,
.expect_resume = 1},
#endif
+#if defined(TLS13) && !defined(USE_PSK)
+ {.desc = "try to resume from session ticket (early start)",
+ .enable_db = 0,
+ .enable_session_ticket_server = 1,
+ .enable_session_ticket_client = 1,
+ .early_start = 1,
+ .expect_resume = 1},
+#endif
+#if defined(TLS13) && defined(USE_PSK)
+ /* early start should no happen on PSK. */
+ {.desc = "try to resume from session ticket (early start)",
+ .enable_db = 0,
+ .enable_session_ticket_server = 1,
+ .enable_session_ticket_client = 1,
+ .no_early_start = 1,
+ .expect_resume = 1},
+#endif
{.desc = "try to resume from session ticket",
.enable_db = 0,
.enable_session_ticket_server = 1,
@@ -330,6 +349,18 @@ static void verify_server_params(gnutls_session_t session, unsigned counter, str
}
#endif
+ if (counter == 0 && params->early_start) {
+ if (!(gnutls_session_get_flags(session) & GNUTLS_SFLAGS_EARLY_START)) {
+ fail("early start did not happen on %d!\n", counter);
+ }
+ }
+
+ if (params->no_early_start) {
+ if (gnutls_session_get_flags(session) & GNUTLS_SFLAGS_EARLY_START) {
+ fail("early start did happen on %d but was not expected!\n", counter);
+ }
+ }
+
#if defined(USE_X509)
unsigned int l;
@@ -423,7 +454,6 @@ static void client(int sds[], struct params_res *params)
gnutls_global_set_log_function(tls_log_func);
gnutls_global_set_log_level(2);
}
- global_init();
#ifdef USE_PSK
gnutls_psk_allocate_client_credentials(&pskcred);
@@ -443,9 +473,7 @@ static void client(int sds[], struct params_res *params)
for (t = 0; t < SESSIONS; t++) {
int sd = sds[t];
- /* Initialize TLS session
- */
- gnutls_init(&session, GNUTLS_CLIENT);
+ assert(gnutls_init(&session, GNUTLS_CLIENT)>=0);
snprintf(prio_str, sizeof(prio_str), "%s", PRIO_STR);
@@ -663,8 +691,6 @@ static void global_stop(void)
gnutls_certificate_free_credentials(serverx509cred);
#endif
gnutls_dh_params_deinit(dh_params);
-
- gnutls_global_deinit();
}
#ifdef USE_PSK
@@ -691,6 +717,10 @@ static void server(int sds[], struct params_res *params)
gnutls_session_t session;
char buffer[MAX_BUF + 1];
gnutls_group_t pgroup;
+ unsigned iflags = GNUTLS_SERVER;
+
+ if (params->early_start || params->no_early_start)
+ iflags |= GNUTLS_ENABLE_EARLY_START;
/* this must be called once in the program, it is mostly for the server.
*/
@@ -699,8 +729,6 @@ static void server(int sds[], struct params_res *params)
gnutls_global_set_log_level(2);
}
- global_init();
-
#ifdef USE_PSK
gnutls_psk_allocate_server_credentials(&pskcred);
gnutls_psk_set_server_credentials_function(pskcred, pskfunc);
@@ -731,7 +759,7 @@ static void server(int sds[], struct params_res *params)
for (t = 0; t < SESSIONS; t++) {
int sd = sds[t];
- assert(gnutls_init(&session, GNUTLS_SERVER) >= 0);
+ assert(gnutls_init(&session, iflags) >= 0);
/* avoid calling all the priority functions, since the defaults
* are adequate.
@@ -894,7 +922,6 @@ void doit(void)
for (j = 0; j < SESSIONS; j++)
close(server_sds[j]);
client(client_sds, &resume_tests[i]);
- gnutls_global_deinit();
exit(0);
}
}
diff --git a/tests/suite/tls-fuzzer/gnutls-nocert-tls13.json b/tests/suite/tls-fuzzer/gnutls-nocert-tls13.json
index 89c8853c68..a31eec4858 100644
--- a/tests/suite/tls-fuzzer/gnutls-nocert-tls13.json
+++ b/tests/suite/tls-fuzzer/gnutls-nocert-tls13.json
@@ -43,18 +43,19 @@
{"name" : "test-tls13-version-negotiation.py",
"arguments": ["-p", "@PORT@"]},
{"name" : "test-tls13-zero-length-data.py",
- "comment" : "in these tests tlsfuzzer splits ClientHello into the first 2 bytes and the remainder, which gnutls doesn't support, last 3 related to #481",
+ "comment" : "in these tests tlsfuzzer splits ClientHello into the first 2 bytes and the remainder, which gnutls doesn't support",
"arguments": ["-p", "@PORT@",
+ "-e", "zero-length app data interleaved in handshake",
"-e", "zero-len app data with large padding during handshake",
"-e", "zero-len app data with large padding interleaved in handshake",
- "-e", "zero-len app data with padding interleaved in handshake",
- "-e", "zero-length app data during handshake",
- "-e", "zero-length app data interleaved in handshake",
- "-e", "zero-length app data with padding during handshake"]},
+ "-e", "zero-len app data with padding interleaved in handshake"]},
{"name" : "test-tls13-finished.py",
- "arguments": ["-p", "@PORT@", "-n", "5"],
- "exp_pass" : false,
- "comment" : "we do not switch the keys early enough for this test see #481"}
+ "commoent" : "the disabled tests timeout very often due to slow tls-fuzzer implementation",
+ "arguments": ["-p", "@PORT@", "-n", "5",
+ "-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"]}
]
}
]
diff --git a/tests/suite/tls-fuzzer/tlsfuzzer b/tests/suite/tls-fuzzer/tlsfuzzer
-Subproject 65af9ab3615a14c59f579085e13fe5a4557a356
+Subproject 23d66d990f1773af5701fb510a3a0ffa59beac5
diff --git a/tests/suite/tls-fuzzer/tlslite-ng b/tests/suite/tls-fuzzer/tlslite-ng
-Subproject d976188fe7fd7466dc5cf0818a4ef87e3738189
+Subproject 3029e014231ffe34445d29300f0193a4be4b6cc
diff --git a/tests/tls13-early-start.c b/tests/tls13-early-start.c
new file mode 100644
index 0000000000..6e0d7da82e
--- /dev/null
+++ b/tests/tls13-early-start.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2015-2018 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS 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
+ * 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/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* This program tests support for early start in TLS1.3 handshake */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gnutls/gnutls.h>
+#include "utils.h"
+#include "eagain-common.h"
+#include <assert.h>
+
+#define USE_CERT 1
+#define ASK_CERT 2
+
+const char *side;
+
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "%s|<%d>| %s", side, level, str);
+}
+
+#define try_ok(name, client_prio) \
+ try_with_key(name, client_prio, \
+ &server_ca3_localhost_cert, &server_ca3_key, NULL, NULL, 0)
+
+#define MSG "hello there ppl"
+
+static
+void try_with_key_fail(const char *name, const char *client_prio,
+ const gnutls_datum_t *serv_cert,
+ const gnutls_datum_t *serv_key,
+ const gnutls_datum_t *cli_cert,
+ const gnutls_datum_t *cli_key,
+ unsigned init_flags)
+{
+ int ret;
+ char buffer[256];
+ gnutls_certificate_credentials_t serverx509cred;
+ gnutls_session_t server;
+ int sret = GNUTLS_E_AGAIN;
+ /* Client stuff. */
+ gnutls_certificate_credentials_t clientx509cred;
+ gnutls_session_t client;
+ int cret = GNUTLS_E_AGAIN, version;
+ const char *err;
+
+ gnutls_global_set_log_function(tls_log_func);
+ if (debug)
+ gnutls_global_set_log_level(6);
+
+ reset_buffers();
+ /* Init server */
+ gnutls_certificate_allocate_credentials(&serverx509cred);
+
+ ret = gnutls_certificate_set_x509_key_mem(serverx509cred,
+ serv_cert, serv_key,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0)
+ fail("Could not set key/cert: %s\n", gnutls_strerror(ret));
+
+ assert(gnutls_init(&server, GNUTLS_SERVER|init_flags)>=0);
+ gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE,
+ serverx509cred);
+
+ assert(gnutls_priority_set_direct(server, client_prio, NULL) >= 0);
+
+ gnutls_transport_set_push_function(server, server_push);
+ gnutls_transport_set_pull_function(server, server_pull);
+ gnutls_transport_set_ptr(server, server);
+
+ /* Init client */
+ ret = gnutls_certificate_allocate_credentials(&clientx509cred);
+ if (ret < 0)
+ exit(1);
+
+ if (cli_cert) {
+ gnutls_certificate_set_x509_key_mem(clientx509cred,
+ cli_cert, cli_key,
+ GNUTLS_X509_FMT_PEM);
+ gnutls_certificate_server_set_request(server, GNUTLS_CERT_REQUIRE);
+ }
+
+ ret = gnutls_init(&client, GNUTLS_CLIENT);
+ if (ret < 0)
+ exit(1);
+
+ ret = gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE,
+ clientx509cred);
+ if (ret < 0)
+ exit(1);
+
+ gnutls_transport_set_push_function(client, client_push);
+ gnutls_transport_set_pull_function(client, client_pull);
+ gnutls_transport_set_ptr(client, client);
+
+ ret = gnutls_priority_set_direct(client, client_prio, &err);
+ if (ret < 0) {
+ if (ret == GNUTLS_E_INVALID_REQUEST)
+ fprintf(stderr, "Error in %s\n", err);
+ exit(1);
+ }
+
+ success("negotiating %s\n", name);
+ HANDSHAKE(client, server);
+
+ assert(!(gnutls_session_get_flags(server) & GNUTLS_SFLAGS_EARLY_START));
+ assert(!(gnutls_session_get_flags(client) & GNUTLS_SFLAGS_EARLY_START));
+
+ version = gnutls_protocol_get_version(client);
+ assert(version == GNUTLS_TLS1_3);
+
+ memset(buffer, 0, sizeof(buffer));
+ assert(gnutls_record_send(server, MSG, strlen(MSG))>=0);
+
+ ret = gnutls_record_recv(client, buffer, sizeof(buffer));
+ if (ret == 0) {
+ fail("client: Peer has closed the TLS connection\n");
+ exit(1);
+ } else if (ret < 0) {
+ fail("client: Error: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+
+ if (ret != strlen(MSG) || memcmp(MSG, buffer, ret) != 0) {
+ fail("client: Error in data received. Expected %d, got %d\n", (int)strlen(MSG), ret);
+ exit(1);
+ }
+
+ memset(buffer, 0, sizeof(buffer));
+ assert(gnutls_record_send(client, MSG, strlen(MSG))>=0);
+
+ ret = gnutls_record_recv(server, buffer, sizeof(buffer));
+ if (ret == 0) {
+ fail("server: Peer has closed the TLS connection\n");
+ } else if (ret < 0) {
+ fail("server: Error: %s\n", gnutls_strerror(ret));
+ }
+
+ if (ret != strlen(MSG) || memcmp(MSG, buffer, ret) != 0) {
+ fail("client: Error in data received. Expected %d, got %d\n", (int)strlen(MSG), ret);
+ exit(1);
+ }
+
+ gnutls_deinit(client);
+ gnutls_deinit(server);
+
+ gnutls_certificate_free_credentials(serverx509cred);
+ gnutls_certificate_free_credentials(clientx509cred);
+}
+
+static
+void try_with_key_ks(const char *name, const char *client_prio,
+ const gnutls_datum_t *serv_cert,
+ const gnutls_datum_t *serv_key,
+ const gnutls_datum_t *client_cert,
+ const gnutls_datum_t *client_key,
+ unsigned cert_flags,
+ unsigned init_flags)
+{
+ int ret;
+ char buffer[256];
+ /* Server stuff. */
+ gnutls_certificate_credentials_t serverx509cred;
+ gnutls_session_t server;
+ int sret = GNUTLS_E_AGAIN;
+ /* Client stuff. */
+ gnutls_certificate_credentials_t clientx509cred;
+ gnutls_session_t client;
+ int cret = GNUTLS_E_AGAIN, version;
+ const char *err;
+
+ /* General init. */
+ gnutls_global_set_log_function(tls_log_func);
+ if (debug)
+ gnutls_global_set_log_level(6);
+
+ reset_buffers();
+ /* Init server */
+ gnutls_certificate_allocate_credentials(&serverx509cred);
+
+ ret = gnutls_certificate_set_x509_key_mem(serverx509cred,
+ serv_cert, serv_key,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ fail("Could not set key/cert: %s\n", gnutls_strerror(ret));
+ }
+
+ assert(gnutls_init(&server, GNUTLS_SERVER|init_flags)>=0);
+ gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE,
+ serverx509cred);
+
+
+ assert(gnutls_priority_set_direct(server,
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3",
+ NULL)>=0);
+ gnutls_transport_set_push_function(server, server_push);
+ gnutls_transport_set_pull_function(server, server_pull);
+ gnutls_transport_set_ptr(server, server);
+
+ /* Init client */
+
+ ret = gnutls_certificate_allocate_credentials(&clientx509cred);
+ if (ret < 0)
+ exit(1);
+
+ if (cert_flags == USE_CERT) {
+ gnutls_certificate_set_x509_key_mem(clientx509cred,
+ client_cert, client_key,
+ GNUTLS_X509_FMT_PEM);
+ gnutls_certificate_server_set_request(server, GNUTLS_CERT_REQUIRE);
+ } else if (cert_flags == ASK_CERT) {
+ gnutls_certificate_server_set_request(server, GNUTLS_CERT_REQUEST);
+ }
+
+ ret = gnutls_init(&client, GNUTLS_CLIENT);
+ if (ret < 0)
+ exit(1);
+
+
+ ret = gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE,
+ clientx509cred);
+ if (ret < 0)
+ exit(1);
+
+ gnutls_transport_set_push_function(client, client_push);
+ gnutls_transport_set_pull_function(client, client_pull);
+ gnutls_transport_set_ptr(client, client);
+
+ ret = gnutls_priority_set_direct(client, client_prio, &err);
+ if (ret < 0) {
+ if (ret == GNUTLS_E_INVALID_REQUEST)
+ fprintf(stderr, "Error in %s\n", err);
+ exit(1);
+ }
+ success("negotiating %s\n", name);
+ HANDSHAKE(client, server);
+
+ assert(gnutls_session_get_flags(server) & GNUTLS_SFLAGS_EARLY_START);
+ assert(!(gnutls_session_get_flags(client) & GNUTLS_SFLAGS_EARLY_START));
+
+ version = gnutls_protocol_get_version(client);
+ assert(version == GNUTLS_TLS1_3);
+
+ memset(buffer, 0, sizeof(buffer));
+ assert(gnutls_record_send(server, MSG, strlen(MSG))>=0);
+
+ ret = gnutls_record_recv(client, buffer, sizeof(buffer));
+ if (ret == 0) {
+ fail("client: Peer has closed the TLS connection\n");
+ exit(1);
+ } else if (ret < 0) {
+ fail("client: Error: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+
+ if (ret != strlen(MSG) || memcmp(MSG, buffer, ret) != 0) {
+ fail("client: Error in data received. Expected %d, got %d\n", (int)strlen(MSG), ret);
+ exit(1);
+ }
+
+ memset(buffer, 0, sizeof(buffer));
+ assert(gnutls_record_send(client, MSG, strlen(MSG))>=0);
+
+ ret = gnutls_record_recv(server, buffer, sizeof(buffer));
+ if (ret == 0) {
+ fail("server: Peer has closed the TLS connection\n");
+ } else if (ret < 0) {
+ fail("server: Error: %s\n", gnutls_strerror(ret));
+ }
+
+ if (ret != strlen(MSG) || memcmp(MSG, buffer, ret) != 0) {
+ fail("client: Error in data received. Expected %d, got %d\n", (int)strlen(MSG), ret);
+ exit(1);
+ }
+
+ gnutls_bye(client, GNUTLS_SHUT_RDWR);
+ gnutls_bye(server, GNUTLS_SHUT_RDWR);
+
+ gnutls_deinit(client);
+ gnutls_deinit(server);
+
+ gnutls_certificate_free_credentials(serverx509cred);
+ gnutls_certificate_free_credentials(clientx509cred);
+}
+
+static
+void try_with_key(const char *name, const char *client_prio,
+ const gnutls_datum_t *serv_cert,
+ const gnutls_datum_t *serv_key,
+ const gnutls_datum_t *cli_cert,
+ const gnutls_datum_t *cli_key,
+ unsigned cert_flags)
+{
+ return try_with_key_ks(name, client_prio,
+ serv_cert, serv_key, cli_cert, cli_key, cert_flags, GNUTLS_ENABLE_EARLY_START);
+}
+
+#include "cert-common.h"
+
+void doit(void)
+{
+ /* TLS 1.3 no client cert: early start expected */
+ try_ok("TLS 1.3 with ffdhe2048 rsa no-cli-cert", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-FFDHE2048");
+ try_ok("TLS 1.3 with secp256r1 rsa no-cli-cert", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP256R1");
+ try_ok("TLS 1.3 with x25519 rsa no-cli-cert", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-X25519");
+
+ try_with_key_ks("TLS 1.3 with secp256r1 ecdsa no-cli-cert", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP256R1",
+ &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, NULL, NULL, 0, GNUTLS_ENABLE_EARLY_START);
+
+ /* client authentication: no early start possible */
+ try_with_key_fail("TLS 1.3 with rsa-pss cli-cert", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-KX-ALL:+ECDHE-RSA",
+ &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, &cli_ca3_rsa_pss_cert, &cli_ca3_rsa_pss_key, GNUTLS_ENABLE_EARLY_START);
+ try_with_key_fail("TLS 1.3 with rsa cli-cert", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-KX-ALL:+ECDHE-RSA",
+ &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, &cli_ca3_cert, &cli_ca3_key, GNUTLS_ENABLE_EARLY_START);
+ try_with_key_fail("TLS 1.3 with ecdsa cli-cert", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-KX-ALL:+ECDHE-RSA",
+ &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, GNUTLS_ENABLE_EARLY_START);
+
+ /* TLS 1.3 no client cert: no early start flag specified */
+ try_with_key_fail("TLS 1.3 with rsa-pss cli-cert", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-KX-ALL:+ECDHE-RSA",
+ &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, NULL, NULL, 0);
+}