summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/algorithms.h3
-rw-r--r--lib/ext/key_share.c37
-rw-r--r--lib/gnutls_int.h4
-rw-r--r--lib/handshake-tls13.c2
-rw-r--r--lib/handshake.c81
-rw-r--r--lib/handshake.h8
-rw-r--r--lib/record.c5
-rw-r--r--lib/state.c2
-rw-r--r--lib/tls13/hello_retry.c67
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;
+}