summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2018-07-19 15:52:26 +0200
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2018-07-23 21:14:13 +0200
commitf6663ac055afa2bf428951609696061f1bbc6757 (patch)
treedb25a6d45fcdbc7aac104950d4d2c63105776eb2
parent8376b7cb404c1f3994cb19735269a67f6bc5e5d3 (diff)
downloadgnutls-tmp-handshake-return-early.tar.gz
tls1.3: server returns early on handshake when no cert is provided by clienttmp-handshake-return-early
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--lib/constate.c75
-rw-r--r--lib/constate.h2
-rw-r--r--lib/gnutls_int.h16
-rw-r--r--lib/handshake-tls13.c157
-rw-r--r--lib/handshake.c8
-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.c29
-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
18 files changed, 659 insertions, 106 deletions
diff --git a/lib/constate.c b/lib/constate.c
index 8f15990ad6..7761f4e4d8 100644
--- a/lib/constate.c
+++ b/lib/constate.c
@@ -256,34 +256,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);
}
@@ -329,6 +329,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)
@@ -340,31 +341,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);
@@ -373,30 +376,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);
@@ -1006,6 +1011,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..ba22962ccf 100644
--- a/lib/handshake-tls13.c
+++ b/lib/handshake-tls13.c
@@ -56,7 +56,7 @@
#include "tls13/key_update.h"
#include "ext/pre_shared_key.h"
-static int generate_hs_traffic_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,7 +90,7 @@ 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);
/* fall through */
@@ -141,17 +141,23 @@ 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);
+ /* set traffic keys */
+ ret = _tls13_connection_state_init(session, STAGE_APP);
+ IMED_RET("set app keys", ret, 0);
+
STATE = STATE0;
break;
default:
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;
@@ -192,11 +198,43 @@ 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 ((session->internals.hsk_flags & HSK_EARLY_START_USED) &&
+ session->security_parameters.entity == GNUTLS_SERVER) {
+ /* 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);
+
+ session->internals.handshake_hash_buffer.length = spos;
+ } else {
+ 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);
@@ -205,14 +243,10 @@ static int generate_ap_traffic_keys(gnutls_session_t 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 +302,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.
@@ -329,7 +365,7 @@ 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);
/* fall through */
@@ -359,40 +395,95 @@ 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 */
+ if (!(session->internals.hsk_flags & HSK_CRT_REQ_SENT)) {
+ STATE = STATE107;
+
+ session->internals.hsk_flags |= HSK_EARLY_START_USED;
+
+ ret =
+ generate_ap_traffic_keys(session);
+ IMED_RET("generate app keys", ret, 0);
+
+ ret = _tls13_write_connection_state_init(session, STAGE_APP);
+ IMED_RET("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 we didn't request a client certificate we can send tickets now
+ * and return early */
+ 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 = STATE111; /* 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 STATE107 */
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 = _tls13_read_connection_state_init(session, STAGE_APP);
+ IMED_RET("set read app keys", ret, 0);
+ } else {
+ ret =
+ generate_ap_traffic_keys(session);
+ IMED_RET("generate app keys", ret, 0);
- if (session->internals.resumed != RESUME_FALSE)
- _gnutls_set_resumed_parameters(session);
+ /* set traffic keys */
+ ret = _tls13_connection_state_init(session, STAGE_APP);
+ IMED_RET("set app keys", ret, 0);
+ }
/* fall through */
- case STATE112:
-
- ret = _gnutls13_send_session_ticket(session, 1, AGAIN(STATE112));
- STATE = STATE112;
- IMED_RET("send session ticket", ret, 0);
+ case STATE114:
+ if (!(session->internals.hsk_flags & 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 5feaed99fd..7bf590751e 100644
--- a/lib/handshake.c
+++ b/lib/handshake.c
@@ -2460,8 +2460,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);
@@ -2650,9 +2651,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);
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index 52e9727486..4ad71ba5dd 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 f6d89ab6b3..2c044620b9 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..4e169a2b40 100644
--- a/tests/resume.c
+++ b/tests/resume.c
@@ -81,6 +81,7 @@ struct params_res {
int try_sni;
int expire_ticket;
int change_ciphersuite;
+ int early_start;
};
pid_t child;
@@ -128,6 +129,12 @@ struct params_res resume_tests[] = {
.enable_session_ticket_client = 1,
.change_ciphersuite = 1,
.expect_resume = 1},
+ {.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
{.desc = "try to resume from session ticket",
.enable_db = 0,
@@ -330,6 +337,12 @@ static void verify_server_params(gnutls_session_t session, unsigned counter, str
}
#endif
+ if (params->early_start) {
+ if (!(gnutls_session_get_flags(session) & GNUTLS_SFLAGS_EARLY_START)) {
+ fail("early start did not happen!\n");
+ }
+ }
+
#if defined(USE_X509)
unsigned int l;
@@ -423,7 +436,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 +455,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 +673,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 +699,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)
+ iflags |= GNUTLS_ENABLE_EARLY_START;
/* this must be called once in the program, it is mostly for the server.
*/
@@ -699,8 +711,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 +741,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 +904,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..5c6ba9122e 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"}
+ "comment" : "in these tests tlsfuzzer seems to bail due to NewSessionTicket",
+ "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 cebce5c72e6feac052670fc2d8cece2c9d4b37e
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);
+}