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