summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@gnutls.org>2018-08-03 14:20:11 +0000
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2018-08-03 14:20:11 +0000
commite7e6bb2377c78d5d146ef57dd03f087dbb9d0243 (patch)
treedb725ee0bf90d5d500a45c681bb07445574a8b86
parentb2f18aeeb8aee0eb62d6ce430dd0c848ccc2bc63 (diff)
parentd47111032f5b20eed70093d988741da5d0e69952 (diff)
downloadgnutls-e7e6bb2377c78d5d146ef57dd03f087dbb9d0243.tar.gz
Merge branch 'tmp-handshake-return-early' into 'master'
tls1.3: server returns early on handshake when no cert is provided by client Closes #481 and #457 See merge request gnutls/gnutls!711
-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--src/serv.c15
-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
23 files changed, 743 insertions, 123 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/src/serv.c b/src/serv.c
index ed7015ae45..0edb176f59 100644
--- a/src/serv.c
+++ b/src/serv.c
@@ -138,7 +138,7 @@ LIST_TYPE_DECLARE(listener_item, char *http_request; char *http_response;
int listen_socket; int fd;
gnutls_session_t tls_session;
int handshake_ok;
- int no_close;
+ int close_ok;
time_t start;
);
@@ -156,7 +156,7 @@ static void listener_free(listener_item * j)
free(j->http_request);
free(j->http_response);
if (j->fd >= 0) {
- if (j->no_close == 0)
+ if (j->close_ok)
gnutls_bye(j->tls_session, GNUTLS_SHUT_WR);
shutdown(j->fd, 2);
close(j->fd);
@@ -1281,7 +1281,7 @@ static void retry_handshake(listener_item *j)
do {
ret = gnutls_alert_send_appropriate(j->tls_session, r);
} while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
- j->no_close = 1;
+ j->close_ok = 0;
} else if (r == 0) {
if (gnutls_session_is_resumed(j->tls_session) != 0 && verbose != 0)
printf("*** This is a resumed session\n");
@@ -1299,14 +1299,16 @@ static void retry_handshake(listener_item *j)
print_info(j->tls_session, verbose, verbose);
}
+ j->close_ok = 1;
j->handshake_ok = 1;
}
}
static void try_rehandshake(listener_item *j)
{
-int r, ret;
+ int r, ret;
fprintf(stderr, "*** Received hello message\n");
+
do {
r = gnutls_handshake(j->tls_session);
} while (r == GNUTLS_E_INTERRUPTED || r == GNUTLS_E_AGAIN);
@@ -1318,6 +1320,7 @@ int r, ret;
fprintf(stderr, "Error in rehandshake: %s\n", gnutls_strerror(r));
j->http_state = HTTP_STATE_CLOSING;
} else {
+ j->close_ok = 1;
j->http_state = HTTP_STATE_REQUEST;
}
}
@@ -1425,7 +1428,7 @@ static void tcp_server(const char *name, int port)
(j->tls_session, accept_fd);
set_read_funcs(j->tls_session);
j->handshake_ok = 0;
- j->no_close = 0;
+ j->close_ok = 0;
if (verbose != 0) {
ctt = ctime(&tt);
@@ -1479,7 +1482,7 @@ static void tcp_server(const char *name, int port)
ret = gnutls_alert_send_appropriate(j->tls_session, r);
} while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
GERR(r);
- j->no_close = 1;
+ j->close_ok = 0;
}
}
} else {
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);
+}