diff options
-rw-r--r-- | lib/algorithms.h | 3 | ||||
-rw-r--r-- | lib/ext/key_share.c | 37 | ||||
-rw-r--r-- | lib/gnutls_int.h | 4 | ||||
-rw-r--r-- | lib/handshake-tls13.c | 2 | ||||
-rw-r--r-- | lib/handshake.c | 81 | ||||
-rw-r--r-- | lib/handshake.h | 8 | ||||
-rw-r--r-- | lib/record.c | 5 | ||||
-rw-r--r-- | lib/state.c | 2 | ||||
-rw-r--r-- | lib/tls13/hello_retry.c | 67 |
9 files changed, 192 insertions, 17 deletions
diff --git a/lib/algorithms.h b/lib/algorithms.h index e96689208c..556bc33901 100644 --- a/lib/algorithms.h +++ b/lib/algorithms.h @@ -43,6 +43,9 @@ #define TLS_SIGN_AID_UNKNOWN {{255, 255}, 0} #define HAVE_UNKNOWN_SIGAID(aid) ((aid)->id[0] == 255 && (aid)->id[1] == 255) +#define CS_INVALID_MAJOR 0x00 +#define CS_INVALID_MINOR 0x00 + /* Functions for version handling. */ const version_entry_st *version_to_entry(gnutls_protocol_t version); const version_entry_st *nversion_to_entry(uint8_t major, uint8_t minor); diff --git a/lib/ext/key_share.c b/lib/ext/key_share.c index 72bec9f55d..5e82916de3 100644 --- a/lib/ext/key_share.c +++ b/lib/ext/key_share.c @@ -556,6 +556,32 @@ key_share_recv_params(gnutls_session_t session, if (unlikely(ver == NULL || ver->key_shares == 0)) return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + if (_gnutls_ext_get_msg(session) == GNUTLS_EXT_FLAG_HRR) { + if (unlikely(!(session->internals.hsk_flags & HSK_HRR_RECEIVED))) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + DECR_LEN(data_size, 2); + gid = _gnutls_read_uint16(data); + + group = _gnutls_tls_id_to_group(gid); + if (group == NULL) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + _gnutls_handshake_log("EXT[%p]: HRR key share with %s\n", session, group->name); + + /* check if we support it */ + ret = _gnutls_session_supports_group(session, group->id); + if (ret < 0) { + _gnutls_handshake_log("EXT[%p]: received share for %s which is disabled\n", session, group->name); + return gnutls_assert_val(ret); + } + + _gnutls_session_group_set(session, group); + + return 0; + } + /* else */ + DECR_LEN(data_size, 2); gid = _gnutls_read_uint16(data); data += 2; @@ -620,6 +646,17 @@ key_share_send_params(gnutls_session_t session, cur_length = extdata->length; + if (session->internals.hsk_flags & HSK_HRR_RECEIVED) { /* we know the group */ + group = get_group(session); + if (unlikely(group == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + ret = client_gen_key_share(session, group, extdata); + if (ret == GNUTLS_E_INT_RET_0) + return gnutls_assert_val(GNUTLS_E_NO_COMMON_KEY_SHARE); + if (ret < 0) + return gnutls_assert_val(ret); + } else /* generate key shares for out top-3 groups * if they are of different PK type. */ for (i=0;i<session->internals.priorities->groups.size;i++) { diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index ccbdce8f1c..c86f303bdb 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -1111,6 +1111,7 @@ typedef struct { #define HSK_CRT_SENT (1<<1) #define HSK_CRT_ASKED (1<<2) #define HSK_HRR_SENT (1<<3) +#define HSK_HRR_RECEIVED (1<<4) unsigned hsk_flags; /* TLS1.3 only */ unsigned crt_requested; /* 1 if client auth was requested (i.e., client cert). @@ -1203,6 +1204,9 @@ typedef struct { const gnutls_group_entry_st *cand_ec_group; const gnutls_group_entry_st *cand_dh_group; + /* the ciphersuite received in HRR */ + uint8_t hrr_cs[2]; + /* If you add anything here, check _gnutls_handshake_internal_state_clear(). */ } internals_st; diff --git a/lib/handshake-tls13.c b/lib/handshake-tls13.c index 87a8766389..5776d310c4 100644 --- a/lib/handshake-tls13.c +++ b/lib/handshake-tls13.c @@ -197,7 +197,7 @@ int _gnutls13_handshake_server(gnutls_session_t session) switch (STATE) { case STATE90: - ret = _gnutls13_handshake_hash_buffers_synth(session); + ret = _gnutls13_handshake_hash_buffers_synth(session, session->security_parameters.prf, 0); STATE = STATE90; IMED_RET("reset handshake buffers", ret, 0); /* fall through */ diff --git a/lib/handshake.c b/lib/handshake.c index 2a041ec611..22b984050b 100644 --- a/lib/handshake.c +++ b/lib/handshake.c @@ -88,18 +88,18 @@ handshake_hash_buffer_reset(gnutls_session_t session) static int handshake_hash_add_recvd(gnutls_session_t session, - gnutls_handshake_description_t recv_type, - uint8_t * header, uint16_t header_size, - uint8_t * dataptr, uint32_t datalen); + gnutls_handshake_description_t recv_type, + uint8_t * header, uint16_t header_size, + uint8_t * dataptr, uint32_t datalen); static int handshake_hash_add_sent(gnutls_session_t session, - gnutls_handshake_description_t type, - uint8_t * dataptr, uint32_t datalen); + gnutls_handshake_description_t type, + uint8_t * dataptr, uint32_t datalen); static int recv_hello_verify_request(gnutls_session_t session, - uint8_t * data, int datalen); + uint8_t * data, int datalen); /* Clears the handshake hash buffers and handles. @@ -112,19 +112,26 @@ void _gnutls_handshake_hash_buffers_clear(gnutls_session_t session) /* Replace handshake message buffer, with the special synthetic message * needed by TLS1.3 when HRR is sent. */ -int _gnutls13_handshake_hash_buffers_synth(gnutls_session_t session) +int _gnutls13_handshake_hash_buffers_synth(gnutls_session_t session, + const mac_entry_st *prf, + unsigned client) { int ret; uint8_t hdata[4+MAX_HASH_SIZE]; + size_t length; + + if (client) + length = session->internals.handshake_hash_buffer_prev_len; + else + length = session->internals.handshake_hash_buffer.length; /* calculate hash */ hdata[0] = 254; - _gnutls_write_uint24(session->security_parameters.prf->output_size, &hdata[1]); + _gnutls_write_uint24(prf->output_size, &hdata[1]); - ret = gnutls_hash_fast((gnutls_digest_algorithm_t)session->security_parameters.prf->id, + ret = gnutls_hash_fast((gnutls_digest_algorithm_t)prf->id, session->internals.handshake_hash_buffer.data, - session->internals.handshake_hash_buffer.length, - hdata+4); + length, hdata+4); if (ret < 0) return gnutls_assert_val(ret); @@ -133,12 +140,12 @@ int _gnutls13_handshake_hash_buffers_synth(gnutls_session_t session) ret = _gnutls_buffer_append_data(&session->internals. handshake_hash_buffer, - hdata, session->security_parameters.prf->output_size+4); + hdata, prf->output_size+4); if (ret < 0) return gnutls_assert_val(ret); _gnutls_buffers_log("BUF[HSK]: Replaced handshake buffer with synth message (%d bytes)\n", - session->security_parameters.prf->output_size+4); + prf->output_size+4); return 0; } @@ -1201,7 +1208,7 @@ handshake_hash_add_recvd(gnutls_session_t session, if ((vers->id != GNUTLS_DTLS0_9 && recv_type == GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST) || - recv_type == GNUTLS_HANDSHAKE_HELLO_REQUEST) + recv_type == GNUTLS_HANDSHAKE_HELLO_REQUEST) return 0; CHECK_SIZE(header_size + datalen); @@ -1378,6 +1385,34 @@ _gnutls_recv_handshake(gnutls_session_t session, } break; + case GNUTLS_HANDSHAKE_HELLO_RETRY_REQUEST: { + /* hash buffer synth message is generated during hello retry parsing */ + gnutls_datum_t hrr = {hsk.data.data, hsk.data.length}; + ret = + _gnutls13_recv_hello_retry_request(session, + &hsk.data); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } else { + /* during hello retry parsing, we reset handshake hash buffer, + * re-add this message */ + ret = handshake_hash_add_recvd(session, hsk.htype, + hsk.header, hsk.header_size, + hrr.data, + hrr.size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + /* Signal our caller we have received a retry request + and ClientHello needs to be sent again. */ + ret = 1; + } + + break; + } case GNUTLS_HANDSHAKE_SERVER_HELLO_DONE: if (hsk.data.length == 0) ret = 0; @@ -1646,6 +1681,12 @@ read_server_hello(gnutls_session_t session, } pos += 2; + if (session->internals.hsk_flags & HSK_HRR_RECEIVED) { + /* check if ciphersuite matches */ + if (memcmp(session->security_parameters.cs->id, session->internals.hrr_cs, 2) != 0) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + } + if (vers->tls13_sem) { /* TLS 1.3 Early Secret */ ret = _tls13_init_secret(session, NULL, 0); @@ -2483,6 +2524,18 @@ static int handshake_client(gnutls_session_t session) STATE = STATE0; return 1; } + } else { + ret = + _gnutls_recv_handshake(session, + GNUTLS_HANDSHAKE_HELLO_RETRY_REQUEST, + 1, NULL); + STATE = STATE2; + IMED_RET("recv hello retry", ret, 1); + + if (ret == 1) { + STATE = STATE0; + return 1; + } } /* fall through */ case STATE3: diff --git a/lib/handshake.h b/lib/handshake.h index 1af8fbc19c..fb944925e9 100644 --- a/lib/handshake.h +++ b/lib/handshake.h @@ -77,7 +77,9 @@ int _gnutls_user_hello_func(gnutls_session_t session, void _gnutls_handshake_hash_buffers_clear(gnutls_session_t session); -int _gnutls13_handshake_hash_buffers_synth(gnutls_session_t session); +int _gnutls13_handshake_hash_buffers_synth(gnutls_session_t session, + const mac_entry_st *prf, + unsigned client); #define STATE session->internals.handshake_state #define FINAL_STATE session->internals.handshake_final_state @@ -128,6 +130,10 @@ int _gnutls13_handshake_client(gnutls_session_t session); int _gnutls13_handshake_server(gnutls_session_t session); int +_gnutls13_recv_hello_retry_request(gnutls_session_t session, + gnutls_buffer_st *buf); + +int _gnutls13_recv_async_handshake(gnutls_session_t session, gnutls_buffer_st *buf); #endif diff --git a/lib/record.c b/lib/record.c index 039ce0f140..83c8427c41 100644 --- a/lib/record.c +++ b/lib/record.c @@ -685,6 +685,7 @@ record_check_version(gnutls_session_t session, if (!IS_DTLS(session)) { if (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO || + htype == GNUTLS_HANDSHAKE_HELLO_RETRY_REQUEST || htype == GNUTLS_HANDSHAKE_SERVER_HELLO) { if (version[0] != 3) { gnutls_assert(); @@ -853,7 +854,9 @@ record_add_to_buffers(gnutls_session_t session, * reasons). Otherwise it is an unexpected packet */ if (type == GNUTLS_ALERT - || ((htype == GNUTLS_HANDSHAKE_SERVER_HELLO || htype == GNUTLS_HANDSHAKE_CLIENT_HELLO) + || ((htype == GNUTLS_HANDSHAKE_SERVER_HELLO || + htype == GNUTLS_HANDSHAKE_CLIENT_HELLO || + htype == GNUTLS_HANDSHAKE_HELLO_RETRY_REQUEST) && type == GNUTLS_HANDSHAKE)) { /* even if data is unexpected put it into the buffer */ _gnutls_record_buffer_put(session, recv->type, diff --git a/lib/state.c b/lib/state.c index 3d45f4f566..f25d047d8f 100644 --- a/lib/state.c +++ b/lib/state.c @@ -224,6 +224,8 @@ static void handshake_internal_state_clear1(gnutls_session_t session) session->internals.cand_dh_group = 0; session->internals.hsk_flags = 0; + session->internals.hrr_cs[0] = CS_INVALID_MAJOR; + session->internals.hrr_cs[1] = CS_INVALID_MINOR; } /* This function will clear all the variables in internals diff --git a/lib/tls13/hello_retry.c b/lib/tls13/hello_retry.c index 30d0a2a8e1..59f965cd57 100644 --- a/lib/tls13/hello_retry.c +++ b/lib/tls13/hello_retry.c @@ -81,3 +81,70 @@ int _gnutls13_send_hello_retry_request(gnutls_session_t session, unsigned again) return ret; } +int +_gnutls13_recv_hello_retry_request(gnutls_session_t session, + gnutls_buffer_st *buf) +{ + const version_entry_st *vers; + int ret; + uint8_t tmp[2]; + const gnutls_cipher_suite_entry_st *cs; + const mac_entry_st *prf; + + /* only under TLS 1.3 */ + if (IS_DTLS(session)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + if (session->internals.hsk_flags & HSK_HRR_RECEIVED) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); + + session->internals.hsk_flags |= HSK_HRR_RECEIVED; + + ret = _gnutls_buffer_pop_data(buf, tmp, 2); + if (ret < 0) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + vers = nversion_to_entry(tmp[0], tmp[1]); + if (unlikely(vers == NULL)) + return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET); + + if (_gnutls_version_is_supported(session, vers->id) == 0) + return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET); + + if (_gnutls_set_current_version(session, vers->id) < 0) + return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET); + + ret = _gnutls_buffer_pop_data(buf, tmp, 2); + if (ret < 0) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + cs = ciphersuite_to_entry(tmp); + if (unlikely(cs == NULL)) + return gnutls_assert_val(GNUTLS_E_UNKNOWN_CIPHER_SUITE); + + _gnutls_handshake_log("EXT[%p]: Hello Retry Request with %s\n", session, cs->name); + + memcpy(session->internals.hrr_cs, cs->id, 2); + + prf = mac_to_entry(cs->prf); + if (unlikely(prf == NULL)) + return gnutls_assert_val(GNUTLS_E_UNKNOWN_CIPHER_SUITE); + + ret = _gnutls13_handshake_hash_buffers_synth(session, prf, 1); + if (ret < 0) + return gnutls_assert_val(ret); + + if (buf->length <= 2) { + /* no extensions present */ + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH); + } + + ret = _gnutls_parse_hello_extensions(session, GNUTLS_EXT_FLAG_HRR, GNUTLS_EXT_ANY, + buf->data, buf->length); + if (ret < 0) + return gnutls_assert_val(ret); + + session->internals.used_exts = 0; + + return 0; +} |