diff options
author | Daiki Ueno <ueno@gnu.org> | 2021-04-25 07:49:32 +0000 |
---|---|---|
committer | Daiki Ueno <ueno@gnu.org> | 2021-04-25 07:49:32 +0000 |
commit | eaa5ef3addfcfb22ffa7c0e0fcb52960c2abbe64 (patch) | |
tree | 0b0d6a598908c5b127e917897d0812f6096ef8d7 | |
parent | 2e8bfb2844514a7fe0487104bf112530d93cf9e4 (diff) | |
parent | 0e1f1f0558e733c39a55172fecbb42c96abc31d3 (diff) | |
download | gnutls-eaa5ef3addfcfb22ffa7c0e0fcb52960c2abbe64.tar.gz |
Merge branch 'wip/dueno/earlydata' into 'master'
handshake: fix timing of sending early data
Closes #1146
See merge request gnutls/gnutls!1416
-rw-r--r-- | NEWS | 5 | ||||
-rw-r--r-- | lib/cipher.c | 6 | ||||
-rw-r--r-- | lib/constate.c | 46 | ||||
-rw-r--r-- | lib/handshake-tls13.c | 92 | ||||
-rw-r--r-- | lib/handshake.c | 70 | ||||
-rw-r--r-- | lib/record.c | 2 | ||||
-rw-r--r-- | lib/session_pack.c | 14 | ||||
-rw-r--r-- | tests/tls13-early-data.c | 16 | ||||
-rw-r--r-- | tests/tls13/prf-early.c | 8 |
9 files changed, 176 insertions, 83 deletions
@@ -14,6 +14,11 @@ See the end for copying conditions. This can be enabled with --enable-afalg configure option, when libkcapi package is installed (#308). +** libgnutls: Fixed timing of early data exchange. Previously, the client was + sending early data after receiving Server Hello, which not only negates the + benefit of 0-RTT, but also works under certain assumptions hold (e.g., the + same ciphersuite is selected in initial and resumption handshake) (#1146). + * Version 3.7.1 (released 2021-03-10) ** libgnutls: Fixed potential use-after-free in sending "key_share" diff --git a/lib/cipher.c b/lib/cipher.c index 90ab1d3a9b..28eafbe188 100644 --- a/lib/cipher.c +++ b/lib/cipher.c @@ -85,7 +85,11 @@ _gnutls_encrypt(gnutls_session_t session, content_type_t type, record_parameters_st *params) { gnutls_datum_t plaintext; - const version_entry_st *vers = get_version(session); + const version_entry_st *vers = + (session->internals.hsk_flags & HSK_EARLY_DATA_IN_FLIGHT) && + !IS_SERVER(session) ? + session->internals.resumed_security_parameters.pversion : + get_version(session); int ret; plaintext.data = (uint8_t *) data; diff --git a/lib/constate.c b/lib/constate.c index fc56a7569a..ffa343000e 100644 --- a/lib/constate.c +++ b/lib/constate.c @@ -336,11 +336,19 @@ _tls13_set_early_keys(gnutls_session_t session, return GNUTLS_E_INVALID_REQUEST; } - ret = _tls13_expand_secret(session, "key", 3, NULL, 0, session->key.proto.tls13.e_ckey, key_size, key_block); + ret = _tls13_expand_secret2(session->internals. + resumed_security_parameters.prf, + "key", 3, NULL, 0, + session->key.proto.tls13.e_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.e_ckey, iv_size, iv_block); + ret = _tls13_expand_secret2(session->internals. + resumed_security_parameters.prf, + "iv", 2, NULL, 0, + session->key.proto.tls13.e_ckey, + iv_size, iv_block); if (ret < 0) return gnutls_assert_val(ret); @@ -592,10 +600,19 @@ _gnutls_set_cipher_suite2(gnutls_session_t session, return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); return 0; - } else { - if (params->initialized - || params->cipher != NULL || params->mac != NULL) - return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + } + + /* The params shouldn't have been initialized at this point, unless we + * are doing trial encryption/decryption of early data. + */ + if (unlikely + (!((session->internals.hsk_flags & HSK_EARLY_DATA_IN_FLIGHT && + !IS_SERVER(session)) || + (session->internals.hsk_flags & HSK_EARLY_DATA_ACCEPTED && + IS_SERVER(session))) && + (params->initialized + || params->cipher != NULL || params->mac != NULL))) { + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); } if (_gnutls_cipher_is_ok(cipher_algo) == 0 @@ -655,7 +672,10 @@ int _gnutls_epoch_set_keys(gnutls_session_t session, uint16_t epoch, hs_stage_t int key_size; record_parameters_st *params; int ret; - const version_entry_st *ver = get_version(session); + const version_entry_st *ver = + stage == STAGE_EARLY && !IS_SERVER(session) ? + session->internals.resumed_security_parameters.pversion : + get_version(session); if (unlikely(ver == NULL)) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); @@ -1200,12 +1220,18 @@ int _tls13_read_connection_state_init(gnutls_session_t session, hs_stage_t stage session->security_parameters.epoch_next; int ret; + if (unlikely(stage == STAGE_EARLY && !IS_SERVER(session))) { + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + } + 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, + stage == STAGE_EARLY ? + session->internals.resumed_security_parameters.cs->name : session->security_parameters.cs->name); session->security_parameters.epoch_read = epoch_next; @@ -1223,12 +1249,18 @@ int _tls13_write_connection_state_init(gnutls_session_t session, hs_stage_t stag session->security_parameters.epoch_next; int ret; + if (unlikely(stage == STAGE_EARLY && IS_SERVER(session))) { + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + } + 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, + stage == STAGE_EARLY ? + session->internals.resumed_security_parameters.cs->name : session->security_parameters.cs->name); session->security_parameters.epoch_write = epoch_next; diff --git a/lib/handshake-tls13.c b/lib/handshake-tls13.c index 9687707a32..d9071e2b31 100644 --- a/lib/handshake-tls13.c +++ b/lib/handshake-tls13.c @@ -84,7 +84,9 @@ int _gnutls13_handshake_client(gnutls_session_t session) case STATE99: case STATE100: #ifdef TLS13_APPENDIX_D4 - if (session->internals.priorities->tls13_compat_mode) { + if (session->internals.priorities->tls13_compat_mode && + /* Key change is indicated by sending an EndOfEarlyData below */ + !(session->internals.hsk_flags & HSK_EARLY_DATA_IN_FLIGHT)) { /* We send it before keys are generated. That works because CCS * is always being cached and queued and not being sent directly */ ret = _gnutls_send_change_cipher_spec(session, AGAIN(STATE100)); @@ -94,26 +96,7 @@ int _gnutls13_handshake_client(gnutls_session_t session) #endif FALLTHROUGH; case STATE101: - /* Note that we check IN_FLIGHT, not ACCEPTED - * here. This is because the client sends early data - * speculatively. */ - if (session->internals.hsk_flags & HSK_EARLY_DATA_IN_FLIGHT) { - ret = _tls13_write_connection_state_init(session, STAGE_EARLY); - if (ret == 0) { - _gnutls_epoch_bump(session); - ret = _gnutls_epoch_dup(session, EPOCH_WRITE_CURRENT); - } - STATE = STATE101; - IMED_RET_FATAL("set early traffic keys", ret, 0); - } - FALLTHROUGH; - case STATE102: - ret = _gnutls13_send_early_data(session); - STATE = STATE102; - IMED_RET("send early data", ret, 0); - FALLTHROUGH; - case STATE103: - STATE = STATE103; + STATE = STATE101; ret = generate_hs_traffic_keys(session); /* Note that we check IN_FLIGHT, not ACCEPTED * here. This is because the client sends early data @@ -125,40 +108,40 @@ int _gnutls13_handshake_client(gnutls_session_t session) ret = _tls13_connection_state_init(session, STAGE_HS); IMED_RET_FATAL("set hs traffic keys", ret, 0); FALLTHROUGH; - case STATE104: + case STATE102: ret = _gnutls13_recv_encrypted_extensions(session); - STATE = STATE104; + STATE = STATE102; IMED_RET("recv encrypted extensions", ret, 0); FALLTHROUGH; - case STATE105: + case STATE103: ret = _gnutls13_recv_certificate_request(session); - STATE = STATE105; + STATE = STATE103; IMED_RET("recv certificate request", ret, 0); FALLTHROUGH; - case STATE106: + case STATE104: ret = _gnutls13_recv_certificate(session); - STATE = STATE106; + STATE = STATE104; IMED_RET("recv certificate", ret, 0); FALLTHROUGH; - case STATE107: + case STATE105: ret = _gnutls13_recv_certificate_verify(session); - STATE = STATE107; + STATE = STATE105; IMED_RET("recv server certificate verify", ret, 0); FALLTHROUGH; - case STATE108: + case STATE106: ret = _gnutls_run_verify_callback(session, GNUTLS_CLIENT); - STATE = STATE108; + STATE = STATE106; if (ret < 0) return gnutls_assert_val(ret); FALLTHROUGH; - case STATE109: + case STATE107: ret = _gnutls13_recv_finished(session); - STATE = STATE109; + STATE = STATE107; IMED_RET("recv finished", ret, 0); FALLTHROUGH; - case STATE110: - ret = _gnutls13_send_end_of_early_data(session, AGAIN(STATE110)); - STATE = STATE110; + case STATE108: + ret = _gnutls13_send_end_of_early_data(session, AGAIN(STATE108)); + STATE = STATE108; IMED_RET("send end of early data", ret, 0); /* Note that we check IN_FLIGHT, not ACCEPTED @@ -170,23 +153,23 @@ int _gnutls13_handshake_client(gnutls_session_t session) IMED_RET_FATAL("set hs traffic key after sending early data", ret, 0); } FALLTHROUGH; - case STATE111: - ret = _gnutls13_send_certificate(session, AGAIN(STATE111)); - STATE = STATE111; + case STATE109: + ret = _gnutls13_send_certificate(session, AGAIN(STATE109)); + STATE = STATE109; IMED_RET("send certificate", ret, 0); FALLTHROUGH; - case STATE112: - ret = _gnutls13_send_certificate_verify(session, AGAIN(STATE112)); - STATE = STATE112; + case STATE110: + ret = _gnutls13_send_certificate_verify(session, AGAIN(STATE110)); + STATE = STATE110; IMED_RET("send certificate verify", ret, 0); FALLTHROUGH; - case STATE113: - ret = _gnutls13_send_finished(session, AGAIN(STATE113)); - STATE = STATE113; + case STATE111: + ret = _gnutls13_send_finished(session, AGAIN(STATE111)); + STATE = STATE111; IMED_RET("send finished", ret, 0); FALLTHROUGH; - case STATE114: - STATE = STATE114; + case STATE112: + STATE = STATE112; ret = generate_ap_traffic_keys(session); @@ -430,22 +413,13 @@ int _gnutls13_handshake_server(gnutls_session_t session) FALLTHROUGH; case STATE101: STATE = STATE101; - if (session->internals.hsk_flags & HSK_EARLY_DATA_ACCEPTED) { - ret = _tls13_read_connection_state_init(session, STAGE_EARLY); - if (ret == 0) { - _gnutls_epoch_bump(session); - ret = _gnutls_epoch_dup(session, EPOCH_READ_CURRENT); - } - IMED_RET_FATAL("set early traffic keys", ret, 0); - ret = generate_hs_traffic_keys(session); - IMED_RET_FATAL("generate hs traffic keys", ret, 0); + ret = generate_hs_traffic_keys(session); + IMED_RET_FATAL("generate hs traffic keys", ret, 0); + if (session->internals.hsk_flags & HSK_EARLY_DATA_ACCEPTED) { ret = _tls13_write_connection_state_init(session, STAGE_HS); } else { - ret = generate_hs_traffic_keys(session); - IMED_RET_FATAL("generate hs traffic keys", ret, 0); - ret = _tls13_connection_state_init(session, STAGE_HS); } IMED_RET_FATAL("set hs traffic keys", ret, 0); diff --git a/lib/handshake.c b/lib/handshake.c index c30703ccbd..1153eac85d 100644 --- a/lib/handshake.c +++ b/lib/handshake.c @@ -55,6 +55,7 @@ #include <random.h> #include <dtls.h> #include "secrets.h" +#include "tls13/early_data.h" #include "tls13/session_ticket.h" #include "locks.h" #ifdef HAVE_VALGRIND_MEMCHECK_H @@ -788,6 +789,38 @@ read_client_hello(gnutls_session_t session, uint8_t * data, return ret; } + if (session->internals.hsk_flags & HSK_EARLY_DATA_ACCEPTED) { + const cipher_entry_st *ce; + const mac_entry_st *me; + record_parameters_st *params; + + ce = cipher_to_entry(session->internals. + resumed_security_parameters. + cs->block_algorithm); + me = mac_to_entry(session->internals. + resumed_security_parameters. + cs->mac_algorithm); + + ret = _gnutls_epoch_get(session, EPOCH_NEXT, ¶ms); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + params->cipher = ce; + params->mac = me; + + ret = _tls13_read_connection_state_init(session, STAGE_EARLY); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + _gnutls_epoch_bump(session); + ret = _gnutls_epoch_dup(session, EPOCH_READ_CURRENT); + if (ret < 0) { + return gnutls_assert_val(ret); + } + } + /* resumed by session_ticket extension */ if (!vers->tls13_sem && session->internals.resumed) { session->internals.resumed_security_parameters. @@ -2309,6 +2342,43 @@ static int send_client_hello(gnutls_session_t session, int again) ret = _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_CLIENT_HELLO); + if (session->internals.hsk_flags & HSK_EARLY_DATA_IN_FLIGHT) { + const cipher_entry_st *ce; + const mac_entry_st *me; + record_parameters_st *params; + + ce = cipher_to_entry(session->internals. + resumed_security_parameters. + cs->block_algorithm); + me = mac_to_entry(session->internals. + resumed_security_parameters. + cs->mac_algorithm); + + ret = _gnutls_epoch_get(session, EPOCH_NEXT, ¶ms); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + params->cipher = ce; + params->mac = me; + + ret = _tls13_write_connection_state_init(session, STAGE_EARLY); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + _gnutls_epoch_bump(session); + ret = _gnutls_epoch_dup(session, EPOCH_WRITE_CURRENT); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + ret = _gnutls13_send_early_data(session); + if (ret < 0) { + return gnutls_assert_val(ret); + } + } + return ret; cleanup: diff --git a/lib/record.c b/lib/record.c index cd9df80520..860b9897d6 100644 --- a/lib/record.c +++ b/lib/record.c @@ -2120,7 +2120,7 @@ ssize_t gnutls_record_send_early_data(gnutls_session_t session, * @data: the buffer that the data will be read into * @data_size: the number of requested bytes * - * This function can be used by a searver to retrieve data sent early + * This function can be used by a server to retrieve data sent early * in the handshake processes when resuming a session. This is used * to implement a zero-roundtrip (0-RTT) mode. It has the same * semantics as gnutls_record_recv(). diff --git a/lib/session_pack.c b/lib/session_pack.c index a6d11c4cfc..1f3bc4740c 100644 --- a/lib/session_pack.c +++ b/lib/session_pack.c @@ -911,11 +911,11 @@ pack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps) BUFFER_APPEND_NUM(ps, session->security_parameters.client_ctype); BUFFER_APPEND_NUM(ps, session->security_parameters.server_ctype); + BUFFER_APPEND(ps, session->security_parameters.cs->id, 2); + /* if we are under TLS 1.3 do not pack keys or params negotiated using an extension * they are not necessary */ if (!session->security_parameters.pversion->tls13_sem) { - BUFFER_APPEND(ps, session->security_parameters.cs->id, 2); - BUFFER_APPEND_PFX1(ps, session->security_parameters.master_secret, GNUTLS_MASTER_SIZE); BUFFER_APPEND_PFX1(ps, session->security_parameters.client_random, @@ -1026,12 +1026,12 @@ unpack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps) session->internals.resumed_security_parameters. server_ctype); - if (!session->internals.resumed_security_parameters.pversion->tls13_sem) { - BUFFER_POP(ps, cs, 2); - session->internals.resumed_security_parameters.cs = ciphersuite_to_entry(cs); - if (session->internals.resumed_security_parameters.cs == NULL) - return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + BUFFER_POP(ps, cs, 2); + session->internals.resumed_security_parameters.cs = ciphersuite_to_entry(cs); + if (session->internals.resumed_security_parameters.cs == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + if (!session->internals.resumed_security_parameters.pversion->tls13_sem) { /* master secret */ ret = _gnutls_buffer_pop_datum_prefix8(ps, &t); if (ret < 0) { diff --git a/tests/tls13-early-data.c b/tests/tls13-early-data.c index 4235e12e2a..8091572dfa 100644 --- a/tests/tls13-early-data.c +++ b/tests/tls13-early-data.c @@ -70,11 +70,19 @@ static void client_log_func(int level, const char *str) /* A very basic TLS client. */ -#define SESSIONS 3 #define MAX_BUF 1024 #define MSG "Hello TLS" #define EARLY_MSG "Hello TLS, it's early" -#define PRIORITY "NORMAL:-VERS-ALL:+VERS-TLS1.3" + +/* This test makes connection 3 times with different ciphersuites: first with + * TLS_AES_128_GCM_SHA256, then TLS_AES_256_GCM_SHA384 two times. The reason + * for doing this is to check that the early data is encrypted with the + * ciphersuite selected during the initial handshake, not the resuming + * handshakes. + */ +#define SESSIONS 3 +#define TLS13_AES_128_GCM "NORMAL:-VERS-ALL:+VERS-TLS1.3:+AES-128-GCM" +#define TLS13_AES_256_GCM "NORMAL:-VERS-ALL:+VERS-TLS1.3:+AES-256-GCM" static const gnutls_datum_t hrnd = {(void*)"\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32}; @@ -120,7 +128,7 @@ static void client(int sds[], const char *data, size_t size, size_t maxsize) int sd = sds[t]; assert(gnutls_init(&session, GNUTLS_CLIENT)>=0); - assert(gnutls_priority_set_direct(session, PRIORITY, NULL)>=0); + assert(gnutls_priority_set_direct(session, t == 0 ? TLS13_AES_128_GCM : TLS13_AES_256_GCM, NULL)>=0); gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred); @@ -289,7 +297,7 @@ static void server(int sds[], const char *data, size_t size, size_t maxsize) assert(gnutls_init(&session, GNUTLS_SERVER|GNUTLS_ENABLE_EARLY_DATA)>=0); - assert(gnutls_priority_set_direct(session, PRIORITY, NULL)>=0); + assert(gnutls_priority_set_direct(session, t == 0 ? TLS13_AES_128_GCM : TLS13_AES_256_GCM, NULL)>=0); gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred); diff --git a/tests/tls13/prf-early.c b/tests/tls13/prf-early.c index 51dd7a3f76..3d9462848a 100644 --- a/tests/tls13/prf-early.c +++ b/tests/tls13/prf-early.c @@ -123,10 +123,10 @@ static void dump(const char *name, const uint8_t *data, unsigned data_size) } \ } -#define KEY_EXP_VALUE "\x7f\x9a\x62\x64\x5e\x90\xa4\x19\x6f\xbf\x7b\x4e\x98\x63\x29\xb0\x46\xa2\x2a\x47\x94\x6a\x78\xdc\x6e\xea\x90\x13\x9d\xd4\xd1\x20\x02\x04" -#define HELLO_VALUE "\x38\x40\x8c\x0d\x53\xe5\xd2\xe8\x66\xb4\x46\xce\x32\x85\xd5\x02\x3a\x4f\x81\x3c\x9e\x1b\x4a\x53\x73\x22\xad\xf2\x11\xc6\x45" -#define CONTEXT_VALUE "\xf6\x95\x60\x0d\x51\x9e\x1a\x40\xb2\x9e\xb0\x48\x55\xfe\x64\xf8\xa0\x26\x31\xd8\xb1\x66\xf3\x10\x62\x32\x26\x52\x9e\x63\x49" -#define NULL_CONTEXT_VALUE "\xb1\x80\x8c\xb3\xc2\xa9\x06\x88\xb7\xc2\xed\xd4\x5f\x1c\xad\x0b\xb2\x1f\xa9\xe2\xc6\x37\xd3\x52\x73\x1b\xf5\x3b\x92\x61\x08" +#define KEY_EXP_VALUE "\xec\xc2\x4a\x6b\x07\x89\xd9\x19\xd9\x73\x6d\xd0\x00\x73\xc9\x7a\xd7\x92\xef\x56\x91\x61\xb4\xff\x5f\xef\x81\xc1\x98\x68\x4e\xdf\xd7\x7e" +#define HELLO_VALUE "\x4f\x85\x33\x64\x48\xff\x0d\x8b\xd5\x50\x0f\x97\x91\x5b\x7d\x8d\xc9\x05\x91\x45\x4f\xb9\x4b\x4b\xbc\xbf\x58\x84\x1a\x46\xe3" +#define CONTEXT_VALUE "\x11\x8d\x85\xa8\x91\xe5\x50\x75\x44\x88\x69\xaf\x95\x9a\xb0\x29\xd4\xae\xcd\x11\xcb\x1d\x29\x7c\xe6\x24\xd4\x7c\x95\xdb\x5c" +#define NULL_CONTEXT_VALUE "\x56\x99\x41\x73\x5e\x73\x34\x7f\x3d\x69\x9f\xc0\x3b\x8b\x86\x33\xc6\xc3\x97\x46\x61\x62\x3f\x55\xab\x39\x60\xa5\xeb\xfe\x37" static int handshake_callback_called; |