summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2017-09-21 16:40:43 +0200
committerNikos Mavrogiannopoulos <nmav@redhat.com>2017-11-14 15:00:32 +0100
commit133885bd4bbc8e39e65eeaa86b070691f6cb0e3d (patch)
treefaedff24a17250a29c6a123fcff514fb9f4858e6
parent105b2c3d92fb23102ce2354a71133d486bd8c12f (diff)
downloadgnutls-133885bd4bbc8e39e65eeaa86b070691f6cb0e3d.tar.gz
handshake: send hello retry request when no key share matches
Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
-rw-r--r--lib/Makefile.am1
-rw-r--r--lib/alert.c1
-rw-r--r--lib/constate.c17
-rw-r--r--lib/ext/key_share.c29
-rw-r--r--lib/gnutls_int.h2
-rw-r--r--lib/handshake-tls13.c36
-rw-r--r--lib/handshake.c63
-rw-r--r--lib/handshake.h4
-rw-r--r--lib/hello_ext.c4
-rw-r--r--lib/state.c2
-rw-r--r--lib/tls13/hello_retry.c83
-rw-r--r--lib/tls13/hello_retry.h23
12 files changed, 243 insertions, 22 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 808e1bd350..1a8c82b89c 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -92,6 +92,7 @@ COBJECTS += tls13/encrypted_extensions.c tls13/encrypted_extensions.h \
tls13/certificate_verify.c tls13/certificate_verify.h \
tls13-sig.c tls13-sig.h \
tls13/finished.c tls13/finished.h \
+ tls13/hello_retry.c tls13/hello_retry.h \
tls13/session_ticket.c tls13/session_ticket.h \
tls13/certificate.c tls13/certificate.h
diff --git a/lib/alert.c b/lib/alert.c
index b692ed6f9c..aec54615cf 100644
--- a/lib/alert.c
+++ b/lib/alert.c
@@ -218,6 +218,7 @@ int gnutls_error_to_alert(int err, int *level)
case GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER:
case GNUTLS_E_ILLEGAL_SRP_USERNAME:
case GNUTLS_E_PK_INVALID_PUBKEY:
+ case GNUTLS_E_NO_COMMON_KEY_SHARE:
ret = GNUTLS_A_ILLEGAL_PARAMETER;
_level = GNUTLS_AL_FATAL;
break;
diff --git a/lib/constate.c b/lib/constate.c
index 45a4bdc1df..ae9279ca3e 100644
--- a/lib/constate.c
+++ b/lib/constate.c
@@ -354,18 +354,27 @@ _gnutls_set_cipher_suite2(gnutls_session_t session,
const mac_entry_st *mac_algo;
record_parameters_st *params;
int ret;
+ const version_entry_st *ver = get_version(session);
ret = _gnutls_epoch_get(session, EPOCH_NEXT, &params);
if (ret < 0)
return gnutls_assert_val(ret);
- if (params->initialized
- || params->cipher != NULL || params->mac != NULL)
- return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
-
cipher_algo = cipher_to_entry(cs->block_algorithm);
mac_algo = mac_to_entry(cs->mac_algorithm);
+ if (ver->tls13_sem && (session->internals.hsk_flags & HSK_HRR_SENT)) {
+ if (params->initialized && (params->cipher != cipher_algo ||
+ params->mac != mac_algo || cs != session->security_parameters.cs))
+ 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);
+ }
+
if (_gnutls_cipher_is_ok(cipher_algo) == 0
|| _gnutls_mac_is_ok(mac_algo) == 0)
return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM);
diff --git a/lib/ext/key_share.c b/lib/ext/key_share.c
index 321364e727..72bec9f55d 100644
--- a/lib/ext/key_share.c
+++ b/lib/ext/key_share.c
@@ -652,13 +652,30 @@ key_share_send_params(gnutls_session_t session,
if (unlikely(ver == NULL || ver->key_shares == 0))
return gnutls_assert_val(0);
- group = get_group(session);
- if (unlikely(group == NULL))
- return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ if (_gnutls_ext_get_msg(session) == GNUTLS_EXT_FLAG_HRR) {
+ if (session->internals.cand_ec_group != NULL)
+ group = session->internals.cand_ec_group;
+ else
+ group = session->internals.cand_dh_group;
+ if (group == NULL)
+ return gnutls_assert_val(GNUTLS_E_NO_COMMON_KEY_SHARE);
+
+ _gnutls_session_group_set(session, group);
+
+ _gnutls_handshake_log("EXT[%p]: requesting retry with group %s\n", session, group->name);
+ ret =
+ _gnutls_buffer_append_prefix(extdata, 16, group->tls_id);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ } else {
+ group = get_group(session);
+ if (unlikely(group == NULL))
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
- ret = server_gen_key_share(session, group, extdata);
- if (ret < 0)
- return gnutls_assert_val(ret);
+ ret = server_gen_key_share(session, group, extdata);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
}
return 0;
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index 465379fdca..fae3fe6320 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -247,6 +247,7 @@ typedef enum handshake_state_t { STATE0 = 0, STATE1, STATE2,
STATE15, STATE16, STATE17, STATE18, STATE19,
STATE20 = 20, STATE21, STATE22,
STATE30 = 30, STATE31, STATE40 = 40, STATE41, STATE50 = 50,
+ STATE90=90, STATE91, STATE92, STATE93,
STATE100=100, STATE101, STATE102, STATE103, STATE104,
STATE105, STATE106, STATE107, STATE108, STATE109, STATE110
} handshake_state_t;
@@ -1109,6 +1110,7 @@ typedef struct {
#define HSK_CRT_VRFY_EXPECTED 1
#define HSK_CRT_SENT (1<<1)
#define HSK_CRT_ASKED (1<<2)
+#define HSK_HRR_SENT (1<<3)
unsigned hsk_flags; /* TLS1.3 only */
unsigned crt_requested; /* 1 if client auth was requested (i.e., client cert).
diff --git a/lib/handshake-tls13.c b/lib/handshake-tls13.c
index 9b45d6f729..87a8766389 100644
--- a/lib/handshake-tls13.c
+++ b/lib/handshake-tls13.c
@@ -47,6 +47,7 @@
#include <random.h>
#include <dtls.h>
#include "secrets.h"
+#include "tls13/hello_retry.h"
#include "tls13/encrypted_extensions.h"
#include "tls13/certificate_request.h"
#include "tls13/certificate_verify.h"
@@ -168,7 +169,7 @@ static int generate_hs_traffic_keys(gnutls_session_t session)
{
int ret;
- if (unlikely(session->key.key.size == 0))
+ if (unlikely(session->key.key.size == 0 || session->key.temp_secret_size == 0))
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
ret = _tls13_update_secret(session, session->key.key.data, session->key.key.size);
@@ -195,6 +196,39 @@ int _gnutls13_handshake_server(gnutls_session_t session)
int ret = 0;
switch (STATE) {
+ case STATE90:
+ ret = _gnutls13_handshake_hash_buffers_synth(session);
+ STATE = STATE90;
+ IMED_RET("reset handshake buffers", ret, 0);
+ /* fall through */
+ case STATE91:
+ ret = _gnutls13_send_hello_retry_request(session, AGAIN(STATE91));
+ STATE = STATE91;
+ IMED_RET("send hello retry request", ret, 0);
+ /* fall through */
+ case STATE92:
+ ret =
+ _gnutls_recv_handshake(session,
+ GNUTLS_HANDSHAKE_CLIENT_HELLO,
+ 0, NULL);
+ STATE = STATE92;
+
+ if (ret == GNUTLS_E_INT_RET_0) {
+ /* this is triggered by post_client_hello, and instructs the
+ * handshake to proceed but be put on hold */
+ ret = GNUTLS_E_INTERRUPTED;
+ STATE = STATE100; /* hello already parsed -> move on */
+ } else {
+ STATE = STATE92;
+ }
+
+ IMED_RET("recv client hello", ret, 0);
+ /* fall through */
+ case STATE93:
+ ret = _gnutls_send_server_hello(session, AGAIN(STATE93));
+ STATE = STATE93;
+ IMED_RET("send hello", ret, 0);
+ /* fall through */
case STATE100:
ret =
generate_hs_traffic_keys(session);
diff --git a/lib/handshake.c b/lib/handshake.c
index da0f41cc05..bf2b029db3 100644
--- a/lib/handshake.c
+++ b/lib/handshake.c
@@ -74,13 +74,13 @@ send_handshake_final(gnutls_session_t session, int init);
/* Empties but does not free the buffer
*/
-static inline void
-handshake_hash_buffer_empty(gnutls_session_t session)
+inline static void
+handshake_hash_buffer_reset(gnutls_session_t session)
{
-
_gnutls_buffers_log("BUF[HSK]: Emptied buffer\n");
- session->internals.used_exts = 0;
+ session->internals.handshake_hash_buffer_client_kx_len = 0;
+ session->internals.handshake_hash_buffer_server_finished_len = 0;
session->internals.handshake_hash_buffer_prev_len = 0;
session->internals.handshake_hash_buffer.length = 0;
return;
@@ -106,12 +106,43 @@ recv_hello_verify_request(gnutls_session_t session,
*/
void _gnutls_handshake_hash_buffers_clear(gnutls_session_t session)
{
- session->internals.handshake_hash_buffer_prev_len = 0;
- session->internals.handshake_hash_buffer_client_kx_len = 0;
- session->internals.handshake_hash_buffer_server_finished_len = 0;
+ handshake_hash_buffer_reset(session);
_gnutls_buffer_clear(&session->internals.handshake_hash_buffer);
}
+/* 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 ret;
+ uint8_t hdata[4+MAX_HASH_SIZE];
+
+ /* calculate hash */
+ hdata[0] = 254;
+ _gnutls_write_uint24(session->security_parameters.prf->output_size, &hdata[1]);
+
+ ret = gnutls_hash_fast((gnutls_digest_algorithm_t)session->security_parameters.prf->id,
+ session->internals.handshake_hash_buffer.data,
+ session->internals.handshake_hash_buffer.length,
+ hdata+4);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ handshake_hash_buffer_reset(session);
+
+ ret =
+ _gnutls_buffer_append_data(&session->internals.
+ handshake_hash_buffer,
+ hdata, session->security_parameters.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);
+
+ return 0;
+}
+
/* this will copy the required values for resuming to
* internals, and to security_parameters.
* this will keep as less data to security_parameters.
@@ -1883,7 +1914,7 @@ static int send_client_hello(gnutls_session_t session, int again)
return ret;
}
-static int send_server_hello(gnutls_session_t session, int again)
+int _gnutls_send_server_hello(gnutls_session_t session, int again)
{
mbuffer_st *bufel = NULL;
gnutls_buffer_st buf;
@@ -2046,7 +2077,9 @@ recv_hello_verify_request(gnutls_session_t session,
}
/* reset handshake hash buffers */
- handshake_hash_buffer_empty(session);
+ handshake_hash_buffer_reset(session);
+ /* reset extensions used in previous hello */
+ session->internals.used_exts = 0;
return 0;
}
@@ -2841,7 +2874,8 @@ static int handshake_server(gnutls_session_t session)
const version_entry_st *ver;
reset:
- if (STATE >= STATE100)
+
+ if (STATE >= STATE90)
return _gnutls13_handshake_server(session);
switch (STATE) {
@@ -2859,6 +2893,13 @@ static int handshake_server(gnutls_session_t session)
} else {
STATE = STATE1;
}
+
+ if (ret == GNUTLS_E_NO_COMMON_KEY_SHARE) {
+ STATE = STATE90;
+ session->internals.hsk_flags |= HSK_HRR_SENT;
+ goto reset;
+ }
+
IMED_RET("recv hello", ret, 1);
/* fall through */
case STATE2:
@@ -2868,7 +2909,7 @@ static int handshake_server(gnutls_session_t session)
IMED_RET("recv hello", ret, 0);
/* fall through */
case STATE3:
- ret = send_server_hello(session, AGAIN(STATE3));
+ ret = _gnutls_send_server_hello(session, AGAIN(STATE3));
STATE = STATE3;
IMED_RET("send hello", ret, 1);
diff --git a/lib/handshake.h b/lib/handshake.h
index 79ac19eb7a..1af8fbc19c 100644
--- a/lib/handshake.h
+++ b/lib/handshake.h
@@ -64,6 +64,8 @@ int _gnutls_generate_session_id(uint8_t * session_id, uint8_t * len);
int _gnutls_gen_server_random(gnutls_session_t session, int version);
void _gnutls_set_client_random(gnutls_session_t session, uint8_t * rnd);
+int _gnutls_send_server_hello(gnutls_session_t session, int again);
+
int _gnutls_find_pk_algos_in_ciphersuites(uint8_t * data, int datalen);
int _gnutls_server_select_suite(gnutls_session_t session, uint8_t * data,
unsigned int datalen, unsigned int scsv_only);
@@ -75,6 +77,8 @@ 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);
+
#define STATE session->internals.handshake_state
#define FINAL_STATE session->internals.handshake_final_state
/* This returns true if we have got there
diff --git a/lib/hello_ext.c b/lib/hello_ext.c
index 55bfd6df64..e22f72fca8 100644
--- a/lib/hello_ext.c
+++ b/lib/hello_ext.c
@@ -266,6 +266,10 @@ int hello_ext_send(void *_ctx, gnutls_buffer_st *buf)
p->name, (int)p->tls_id,
ext_msg_validity_to_str(ctx->msg));
return 0;
+ } else {
+ _gnutls_handshake_log("EXT[%p]: Preparing extension (%s/%d) for '%s'\n", session,
+ p->name, (int)p->tls_id,
+ ext_msg_validity_to_str(ctx->msg));
}
/* ensure we don't send something twice (i.e, overriden extensions in
diff --git a/lib/state.c b/lib/state.c
index 2cedce6198..14ce443b5a 100644
--- a/lib/state.c
+++ b/lib/state.c
@@ -222,6 +222,8 @@ static void handshake_internal_state_clear1(gnutls_session_t session)
session->internals.have_ffdhe = 0;
session->internals.cand_ec_group = 0;
session->internals.cand_dh_group = 0;
+
+ session->internals.hsk_flags = 0;
}
/* This function will clear all the variables in internals
diff --git a/lib/tls13/hello_retry.c b/lib/tls13/hello_retry.c
new file mode 100644
index 0000000000..30d0a2a8e1
--- /dev/null
+++ b/lib/tls13/hello_retry.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser 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/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include "hello_ext.h"
+#include "handshake.h"
+#include "tls13/hello_retry.h"
+#include "auth/cert.h"
+#include "mbuffers.h"
+
+int _gnutls13_send_hello_retry_request(gnutls_session_t session, unsigned again)
+{
+ int ret;
+ mbuffer_st *bufel = NULL;
+ gnutls_buffer_st buf;
+ const version_entry_st *ver;
+
+ if (again == 0) {
+ ver = get_version(session);
+ if (unlikely(ver == NULL || session->security_parameters.cs == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ ret = _gnutls_buffer_init_handshake_mbuffer(&buf);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_buffer_append_data(&buf, &ver->major, 1);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_buffer_append_data(&buf, &ver->minor, 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_buffer_append_data(&buf, session->security_parameters.cs->id, 2);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_gen_hello_extensions(session, &buf,
+ GNUTLS_EXT_FLAG_HRR,
+ GNUTLS_EXT_ANY);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* reset extensions sent by this session to allow re-sending them */
+ session->internals.used_exts = 0;
+
+ bufel = _gnutls_buffer_to_mbuffer(&buf);
+ }
+
+ return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_HELLO_RETRY_REQUEST);
+
+ cleanup:
+ _gnutls_buffer_clear(&buf);
+ return ret;
+}
+
diff --git a/lib/tls13/hello_retry.h b/lib/tls13/hello_retry.h
new file mode 100644
index 0000000000..1f21f8b089
--- /dev/null
+++ b/lib/tls13/hello_retry.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser 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/>
+ *
+ */
+
+int _gnutls13_send_hello_retry_request(gnutls_session_t session, unsigned again);