]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
tls1.3: server returns early on handshake when no cert is provided by client
authorNikos Mavrogiannopoulos <nmav@redhat.com>
Thu, 19 Jul 2018 13:52:26 +0000 (15:52 +0200)
committerNikos Mavrogiannopoulos <nmav@redhat.com>
Fri, 3 Aug 2018 07:18:17 +0000 (09:18 +0200)
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>
22 files changed:
NEWS
doc/cha-gtls-app.texi
lib/alert.c
lib/constate.c
lib/constate.h
lib/gnutls_int.h
lib/handshake-tls13.c
lib/handshake.c
lib/handshake.h
lib/includes/gnutls/gnutls.h.in
lib/record.c
lib/session_pack.c
lib/state.c
lib/tls13/finished.c
lib/tls13/key_update.c
tests/Makefile.am
tests/resume-lifetime.c
tests/resume.c
tests/suite/tls-fuzzer/gnutls-nocert-tls13.json
tests/suite/tls-fuzzer/tlsfuzzer
tests/suite/tls-fuzzer/tlslite-ng
tests/tls13-early-start.c [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 3936b03bc8437337f6933bba4a0cdcea0f4805e5..b864a10b9dcecb5e8a2df7214d6049d4a418ac0d 100644 (file)
--- 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)
index b1573213dba252dc46dd05dc3990db01e3bb287b..0f89e8f41a4adeb9010d1760769440517a0653d6 100644 (file)
@@ -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
 
index 6b19507780bde5e3b8a519e95ba5c8915a9ce7e8..5755970ca1fa884dc3925bf5eaa18d99b57889a4 100644 (file)
@@ -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;
index e43bad811ab2befc3da8fca4d22692c5d13b0579..3ff6b526da8be99b1794c27178dec80ac85efca0 100644 (file)
@@ -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)
 {
index 8a15400f5f055473ed3dc4d0d1ef7c706d3c22a8..125a48f8f296904929759e4fab1370ee20343ef0 100644 (file)
@@ -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)
index 6525282a6903e21a190a132744c4f77cadc5444a..c19a909225f4c707196b64c76e7c33daf75a2594 100644 (file)
@@ -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. */
index ffd1b1531d925e06abb143cc63ec8cf3ed548573..a8666186c79350e717900ecc4d387a73f9f9abfa 100644 (file)
@@ -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;
index d0c0f9dc9710c5033f9b2e1bb42c39d0d3760d8c..ba3911d160eb98bdf0415f4617b179130efe55cb 100644 (file)
@@ -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));
index 248d6a18962b93f1e86fd778f854fd53ae86a533..e32de894f2a85162e0fecb38fc16c0d985ba70d5 100644 (file)
 #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 */ \
                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,
index 6baebc295c754dd86ef2576a49756cb261123e08..2252be02242c270b3b9889c871f937943bb0578c 100644 (file)
@@ -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);
index 9b485fd2932fecfd85c66c68a8e72a47abbac048..1cc328cb93ffff655345df8b1b15cf9834995e1b 100644 (file)
@@ -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);
        }
 
index 615eb6c2a53574d3d92a9d57ae881c0a0d475ef2..2ed04a8eebe911e5cc2cbc4f86ada6e5a37dcba1 100644 (file)
@@ -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;
        }
index d01475c84aac7cf0024432052a275f6dcba0c946..8469339c7a5f0324929dd23c784ab6cb571a5bc2 100644 (file)
@@ -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)
index cb768b97399584134aa31837901731e23a6e2fcf..6d88e8feedfaf60337ee400b5c8f72049d78830f 100644 (file)
@@ -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,
index 9bbfca15e312cf9d83b717e7c017d0d5a5220d81..d9c495efdc55728a795a93c949a5b65d8aa15775 100644 (file)
@@ -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);
 
index c870ec36058bf989a456eff5d1dc62f84ba0eede..c9f62f16f0ce337cf2e9a4114c644bfa6952a767 100644 (file)
@@ -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 \
index 99c9c5b346f30d16c3f8363be436b41b5227390a..ad71d410597fd8d77a6c4f813129010e333c4ed2 100644 (file)
@@ -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;
index 891a209313869a356447a033e93d200f6fb24e1f..1a08e0f0bbc55becb261f9a0b7224ebe1b673721 100644 (file)
@@ -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)",
@@ -128,6 +130,23 @@ struct params_res resume_tests[] = {
         .enable_session_ticket_client = 1,
         .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,
@@ -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);
                }
        }
index 89c8853c68a10be68a5771de30823cf61b1036e0..a31eec4858547526440a8e470f724b373ef4f63c 100644 (file)
         {"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"]}
      ]
     }
 ]
index 65af9ab3615a14c59f579085e13fe5a4557a356c..23d66d990f1773af5701fb510a3a0ffa59beac5f 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 65af9ab3615a14c59f579085e13fe5a4557a356c
+Subproject commit 23d66d990f1773af5701fb510a3a0ffa59beac5f
index d976188fe7fd7466dc5cf0818a4ef87e37381897..3029e014231ffe34445d29300f0193a4be4b6ccd 160000 (submodule)
@@ -1 +1 @@
-Subproject commit d976188fe7fd7466dc5cf0818a4ef87e37381897
+Subproject commit 3029e014231ffe34445d29300f0193a4be4b6ccd
diff --git a/tests/tls13-early-start.c b/tests/tls13-early-start.c
new file mode 100644 (file)
index 0000000..6e0d7da
--- /dev/null
@@ -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);
+}