summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnder Juaristi <a@juaristi.eus>2018-03-22 08:59:56 +0100
committerNikos Mavrogiannopoulos <nmav@redhat.com>2018-04-06 13:28:55 +0200
commit921cee23b4c7ee5d4e4537431e7fb1e9411be2d6 (patch)
tree3b1b423ea33220f41c49d7d5322fd505c4dfb55d
parenta046665a384a728253ad94122dfcbd25a52478c2 (diff)
downloadgnutls-921cee23b4c7ee5d4e4537431e7fb1e9411be2d6.tar.gz
Added support for out-of-band Pre-shared keys under TLS1.3
That adds support for pre-shared keys with and without Diffie-Hellman key exchange. That's a modified version of initial Ander's patch. Resolves #414 Resolves #125 Signed-off-by: Ander Juaristi <a@juaristi.eus> Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.org>
-rw-r--r--lib/Makefile.am3
-rw-r--r--lib/algorithms.h9
-rw-r--r--lib/algorithms/ciphersuites.c14
-rw-r--r--lib/auth/psk.c34
-rw-r--r--lib/auth/psk.h8
-rw-r--r--lib/auth/psk_passwd.c35
-rw-r--r--lib/auth/psk_passwd.h7
-rw-r--r--lib/errors.c6
-rw-r--r--lib/ext/Makefile.am3
-rw-r--r--lib/ext/key_share.c19
-rw-r--r--lib/ext/pre_shared_key.c470
-rw-r--r--lib/ext/pre_shared_key.h18
-rw-r--r--lib/ext/psk_ke_modes.c180
-rw-r--r--lib/ext/psk_ke_modes.h8
-rw-r--r--lib/gnutls_int.h51
-rw-r--r--lib/handshake-tls13.c42
-rw-r--r--lib/handshake.c68
-rw-r--r--lib/hello_ext.c47
-rw-r--r--lib/hello_ext.h16
-rw-r--r--lib/includes/gnutls/gnutls.h.in6
-rw-r--r--lib/priority.c14
-rw-r--r--lib/psk.c9
-rw-r--r--lib/secrets.c69
-rw-r--r--lib/secrets.h16
-rw-r--r--lib/session.c15
-rw-r--r--lib/state.c25
-rw-r--r--lib/tls13/certificate.c6
-rw-r--r--lib/tls13/certificate_request.c6
-rw-r--r--lib/tls13/certificate_verify.c3
-rw-r--r--lib/tls13/finished.c96
-rw-r--r--lib/tls13/finished.h5
-rw-r--r--lib/tls13/psk_ext_parser.c159
-rw-r--r--lib/tls13/psk_ext_parser.h48
33 files changed, 1379 insertions, 136 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index ea03f39724..64c7110d85 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -97,7 +97,8 @@ COBJECTS += tls13/encrypted_extensions.c tls13/encrypted_extensions.h \
tls13/hello_retry.c tls13/hello_retry.h \
tls13/session_ticket.c tls13/session_ticket.h \
tls13/certificate.c tls13/certificate.h \
- tls13/post_handshake.c
+ tls13/post_handshake.c \
+ tls13/psk_ext_parser.c tls13/psk_ext_parser.h
if ENABLE_PKCS11
COBJECTS += pkcs11.c pkcs11x.c pkcs11_privkey.c pkcs11_write.c pkcs11_secret.c \
diff --git a/lib/algorithms.h b/lib/algorithms.h
index 495b20d439..468a0c8e96 100644
--- a/lib/algorithms.h
+++ b/lib/algorithms.h
@@ -444,6 +444,15 @@ static inline int _gnutls_kx_is_ecc(gnutls_kx_algorithm_t kx)
return 0;
}
+static inline int _gnutls_kx_is_psk(gnutls_kx_algorithm_t kx)
+{
+ if (kx == GNUTLS_KX_PSK || kx == GNUTLS_KX_DHE_PSK ||
+ kx == GNUTLS_KX_ECDHE_PSK || kx == GNUTLS_KX_RSA_PSK)
+ return 1;
+
+ return 0;
+}
+
static inline int _gnutls_kx_is_dhe(gnutls_kx_algorithm_t kx)
{
if (kx == GNUTLS_KX_DHE_RSA || kx == GNUTLS_KX_DHE_DSS ||
diff --git a/lib/algorithms/ciphersuites.c b/lib/algorithms/ciphersuites.c
index 063363b5bf..ef31a05afd 100644
--- a/lib/algorithms/ciphersuites.c
+++ b/lib/algorithms/ciphersuites.c
@@ -1482,6 +1482,13 @@ _gnutls_figure_common_ciphersuite(gnutls_session_t session,
if (!kx_is_ok(session, kx, cred_type, &sgroup))
continue;
+ /* if we have selected PSK, we need a ciphersuites which matches
+ * the selected binder */
+ if (session->internals.hsk_flags & HSK_PSK_SELECTED) {
+ if (session->key.proto.tls13.binder_prf->id != session->internals.priorities->cs.entry[j]->prf)
+ continue;
+ }
+
if (cred_type == GNUTLS_CRD_CERTIFICATE) {
ret = _gnutls_server_select_cert(session, peer_clist->entry[i]);
if (ret < 0) {
@@ -1520,6 +1527,13 @@ _gnutls_figure_common_ciphersuite(gnutls_session_t session,
if (!kx_is_ok(session, kx, cred_type, &sgroup))
break;
+ /* if we have selected PSK, we need a ciphersuites which matches
+ * the selected binder */
+ if (session->internals.hsk_flags & HSK_PSK_SELECTED) {
+ if (session->key.proto.tls13.binder_prf->id != session->internals.priorities->cs.entry[j]->prf)
+ break;
+ }
+
if (cred_type == GNUTLS_CRD_CERTIFICATE) {
ret = _gnutls_server_select_cert(session, peer_clist->entry[i]);
if (ret < 0) {
diff --git a/lib/auth/psk.c b/lib/auth/psk.c
index e9bb701f33..a2e7d9d9ea 100644
--- a/lib/auth/psk.c
+++ b/lib/auth/psk.c
@@ -111,40 +111,6 @@ _gnutls_set_psk_session_key(gnutls_session_t session,
return ret;
}
-/* returns the username and they key for the PSK session.
- * Free is non (0) if they have to be freed.
- */
-int _gnutls_find_psk_key(gnutls_session_t session,
- gnutls_psk_client_credentials_t cred,
- gnutls_datum_t * username, gnutls_datum_t * key,
- int *free)
-{
- char *user_p;
- int ret;
-
- *free = 0;
-
- if (cred->username.data != NULL && cred->key.data != NULL) {
- username->data = cred->username.data;
- username->size = cred->username.size;
- key->data = cred->key.data;
- key->size = cred->key.size;
- } else if (cred->get_function != NULL) {
- ret = cred->get_function(session, &user_p, key);
- if (ret)
- return gnutls_assert_val(ret);
-
- username->data = (uint8_t *) user_p;
- username->size = strlen(user_p);
-
- *free = 1;
- } else
- return
- gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
-
- return 0;
-}
-
/* Generates the PSK client key exchange
*
diff --git a/lib/auth/psk.h b/lib/auth/psk.h
index 8cde1f7159..783d4f99ad 100644
--- a/lib/auth/psk.h
+++ b/lib/auth/psk.h
@@ -30,6 +30,8 @@ typedef struct gnutls_psk_client_credentials_st {
gnutls_datum_t username;
gnutls_datum_t key;
gnutls_psk_client_credentials_function *get_function;
+ /* TLS 1.3 - The HMAC algorithm to use to compute the binder values */
+ const mac_entry_st *binder_algo;
} psk_client_credentials_st;
typedef struct gnutls_psk_server_credentials_st {
@@ -50,6 +52,8 @@ typedef struct gnutls_psk_server_credentials_st {
/* Identity hint. */
char *hint;
+ /* TLS 1.3 - HMAC algorithm for the binder values */
+ const mac_entry_st *binder_algo;
} psk_server_cred_st;
/* these structures should not use allocated data */
@@ -71,10 +75,6 @@ int _gnutls_gen_psk_server_kx(gnutls_session_t session,
gnutls_buffer_st * data);
int _gnutls_gen_psk_client_kx(gnutls_session_t, gnutls_buffer_st *);
-int _gnutls_find_psk_key(gnutls_session_t session,
- gnutls_psk_client_credentials_t cred,
- gnutls_datum_t * username, gnutls_datum_t * key,
- int *free);
#else
#define _gnutls_set_psk_session_key(x,y,z) GNUTLS_E_UNIMPLEMENTED_FEATURE
diff --git a/lib/auth/psk_passwd.c b/lib/auth/psk_passwd.c
index 1e1898cb8d..dfaac2bea2 100644
--- a/lib/auth/psk_passwd.c
+++ b/lib/auth/psk_passwd.c
@@ -24,8 +24,6 @@
#include "gnutls_int.h"
-#ifdef ENABLE_PSK
-
#include "x509_b64.h"
#include "errors.h"
#include <auth/psk_passwd.h>
@@ -202,5 +200,36 @@ cleanup:
}
+/* returns the username and they key for the PSK session.
+ * Free is non (0) if they have to be freed.
+ */
+int _gnutls_find_psk_key(gnutls_session_t session,
+ gnutls_psk_client_credentials_t cred,
+ gnutls_datum_t * username, gnutls_datum_t * key,
+ int *free)
+{
+ char *user_p;
+ int ret;
+
+ *free = 0;
-#endif /* ENABLE PSK */
+ if (cred->username.data != NULL && cred->key.data != NULL) {
+ username->data = cred->username.data;
+ username->size = cred->username.size;
+ key->data = cred->key.data;
+ key->size = cred->key.size;
+ } else if (cred->get_function != NULL) {
+ ret = cred->get_function(session, &user_p, key);
+ if (ret)
+ return gnutls_assert_val(ret);
+
+ username->data = (uint8_t *) user_p;
+ username->size = strlen(user_p);
+
+ *free = 1;
+ } else
+ return
+ gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
+
+ return 0;
+}
diff --git a/lib/auth/psk_passwd.h b/lib/auth/psk_passwd.h
index f09df621d5..8fe7ae4b02 100644
--- a/lib/auth/psk_passwd.h
+++ b/lib/auth/psk_passwd.h
@@ -20,10 +20,11 @@
*
*/
-#ifdef ENABLE_PSK
-
/* this is locally allocated. It should be freed using the provided function */
int _gnutls_psk_pwd_find_entry(gnutls_session_t, char *username,
gnutls_datum_t * key);
-#endif /* ENABLE_SRP */
+int _gnutls_find_psk_key(gnutls_session_t session,
+ gnutls_psk_client_credentials_t cred,
+ gnutls_datum_t * username, gnutls_datum_t * key,
+ int *free);
diff --git a/lib/errors.c b/lib/errors.c
index 16ade63749..1e9a64922b 100644
--- a/lib/errors.c
+++ b/lib/errors.c
@@ -134,7 +134,7 @@ static const gnutls_error_entry error_entries[] = {
GNUTLS_E_UNIMPLEMENTED_FEATURE),
ERROR_ENTRY(N_("Insufficient credentials for that request."),
GNUTLS_E_INSUFFICIENT_CREDENTIALS),
- ERROR_ENTRY(N_("Error in password file."), GNUTLS_E_SRP_PWD_ERROR),
+ ERROR_ENTRY(N_("Error in password/key file."), GNUTLS_E_SRP_PWD_ERROR),
ERROR_ENTRY(N_("Wrong padding in PKCS1 packet."),
GNUTLS_E_PKCS1_WRONG_PAD),
ERROR_ENTRY(N_("The session or certificate has expired."),
@@ -148,7 +148,7 @@ static const gnutls_error_entry error_entries[] = {
GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR),
ERROR_ENTRY(N_("Base64 encoding error."),
GNUTLS_E_BASE64_ENCODING_ERROR),
- ERROR_ENTRY(N_("Parsing error in password file."),
+ ERROR_ENTRY(N_("Parsing error in password/key file."),
GNUTLS_E_SRP_PWD_PARSING_ERROR),
ERROR_ENTRY(N_("The requested data were not available."),
GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE),
@@ -260,7 +260,7 @@ static const gnutls_error_entry error_entries[] = {
ERROR_ENTRY(N_("The SRP username supplied is illegal."),
GNUTLS_E_ILLEGAL_SRP_USERNAME),
- ERROR_ENTRY(N_("The SRP username supplied is unknown."),
+ ERROR_ENTRY(N_("The username supplied is unknown."),
GNUTLS_E_UNKNOWN_SRP_USERNAME),
ERROR_ENTRY(N_("The OpenPGP fingerprint is not supported."),
diff --git a/lib/ext/Makefile.am b/lib/ext/Makefile.am
index 63d94760bb..89d2389be9 100644
--- a/lib/ext/Makefile.am
+++ b/lib/ext/Makefile.am
@@ -43,7 +43,8 @@ libgnutls_ext_la_SOURCES = max_record.c \
ext_master_secret.c ext_master_secret.h etm.h etm.c \
supported_versions.c supported_versions.h \
post_handshake.c post_handshake.h key_share.c key_share.h \
- cookie.c cookie.h
+ cookie.c cookie.h \
+ psk_ke_modes.c psk_ke_modes.h pre_shared_key.c pre_shared_key.h
if ENABLE_ALPN
libgnutls_ext_la_SOURCES += alpn.c alpn.h
diff --git a/lib/ext/key_share.c b/lib/ext/key_share.c
index f9403df838..871ff08ceb 100644
--- a/lib/ext/key_share.c
+++ b/lib/ext/key_share.c
@@ -506,6 +506,13 @@ key_share_recv_params(gnutls_session_t session,
if (data_size != size)
return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+ /* if we do PSK without DH ignore that share */
+ if ((session->internals.hsk_flags & HSK_PSK_SELECTED) &&
+ (session->internals.hsk_flags & HSK_PSK_KE_MODE_PSK)) {
+ reset_cand_groups(session);
+ return 0;
+ }
+
while(data_size > 0) {
DECR_LEN(data_size, 2);
gid = _gnutls_read_uint16(data);
@@ -554,8 +561,9 @@ key_share_recv_params(gnutls_session_t session,
* In cases (2,3) the error is translated to illegal
* parameter alert.
*/
- if (used_share == 0)
+ if (used_share == 0) {
return gnutls_assert_val(GNUTLS_E_NO_COMMON_KEY_SHARE);
+ }
} else { /* Client */
ver = get_version(session);
@@ -611,6 +619,7 @@ key_share_recv_params(gnutls_session_t session,
}
_gnutls_session_group_set(session, group);
+ session->internals.hsk_flags |= HSK_KEY_SHARE_RECEIVED;
ret = client_use_key_share(session, group, data, size);
if (ret < 0)
@@ -718,6 +727,11 @@ key_share_send_params(gnutls_session_t session,
if (ret < 0)
return gnutls_assert_val(ret);
} else {
+ /* if we are negotiating PSK without DH, do not send a key share */
+ if ((session->internals.hsk_flags & HSK_PSK_SELECTED) &&
+ (session->internals.hsk_flags & HSK_PSK_KE_MODE_PSK))
+ return gnutls_assert_val(0);
+
group = get_group(session);
if (unlikely(group == NULL))
return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
@@ -726,8 +740,9 @@ key_share_send_params(gnutls_session_t session,
if (ret < 0)
return gnutls_assert_val(ret);
}
+
+ session->internals.hsk_flags |= HSK_KEY_SHARE_SENT;
}
return 0;
}
-
diff --git a/lib/ext/pre_shared_key.c b/lib/ext/pre_shared_key.c
new file mode 100644
index 0000000000..02c2288528
--- /dev/null
+++ b/lib/ext/pre_shared_key.c
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2017-2018 Free Software Foundation, Inc.
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * Author: Ander Juaristi, 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 "auth/psk.h"
+#include "secrets.h"
+#include "tls13/psk_ext_parser.h"
+#include "tls13/finished.h"
+#include "auth/psk_passwd.h"
+#include <ext/pre_shared_key.h>
+#include <assert.h>
+
+typedef struct {
+ uint16_t selected_identity;
+} psk_ext_st;
+
+static int
+compute_binder_key(const mac_entry_st *prf,
+ const uint8_t *key, size_t keylen,
+ void *out)
+{
+ int ret;
+ char label[] = "ext_binder";
+ size_t label_len = sizeof(label) - 1;
+ uint8_t tmp_key[MAX_HASH_SIZE];
+
+ /* Compute HKDF-Extract(0, psk) */
+ ret = _tls13_init_secret2(prf, key, keylen, tmp_key);
+ if (ret < 0)
+ return ret;
+
+ /* Compute Derive-Secret(secret, label, transcript_hash) */
+ ret = _tls13_derive_secret2(prf,
+ label, label_len,
+ NULL, 0,
+ tmp_key,
+ out);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int
+compute_psk_binder(unsigned entity,
+ const mac_entry_st *prf, unsigned binders_length, unsigned hash_size,
+ int exts_length, int ext_offset,
+ const gnutls_datum_t *psk, const gnutls_datum_t *client_hello,
+ void *out)
+{
+ int ret;
+ unsigned extensions_len_pos;
+ gnutls_buffer_st handshake_buf;
+ uint8_t binder_key[MAX_HASH_SIZE];
+
+ _gnutls_buffer_init(&handshake_buf);
+
+ if (entity == GNUTLS_CLIENT) {
+ ret = gnutls_buffer_append_data(&handshake_buf,
+ (const void *) client_hello->data,
+ client_hello->size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ /* This is a ClientHello message */
+ handshake_buf.data[0] = GNUTLS_HANDSHAKE_CLIENT_HELLO;
+
+ /*
+ * At this point we have not yet added the binders to the ClientHello,
+ * but we have to overwrite the size field, pretending as if binders
+ * of the correct length were present.
+ */
+ _gnutls_write_uint24(handshake_buf.length + binders_length - 2, &handshake_buf.data[1]);
+ _gnutls_write_uint16(handshake_buf.length + binders_length - ext_offset,
+ &handshake_buf.data[ext_offset]);
+
+ extensions_len_pos = handshake_buf.length - exts_length - 2;
+ _gnutls_write_uint16(exts_length + binders_length + 2,
+ &handshake_buf.data[extensions_len_pos]);
+ } else {
+ gnutls_buffer_append_data(&handshake_buf,
+ (const void *) client_hello->data,
+ client_hello->size - binders_length - 3);
+ }
+
+ ret = compute_binder_key(prf,
+ psk->data, psk->size,
+ binder_key);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ ret = _gnutls13_compute_finished(prf,
+ binder_key, hash_size,
+ &handshake_buf,
+ out);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ ret = 0;
+error:
+ _gnutls_buffer_clear(&handshake_buf);
+ return ret;
+}
+
+static int
+client_send_params(gnutls_session_t session,
+ gnutls_buffer_t extdata,
+ const gnutls_psk_client_credentials_t cred)
+{
+ int ret, ext_offset = 0;
+ uint8_t binder_value[MAX_HASH_SIZE];
+ size_t length, pos;
+ gnutls_datum_t username = {NULL, 0}, key = {NULL, 0}, client_hello;
+ const mac_entry_st *prf = cred->binder_algo;
+ unsigned hash_size = _gnutls_mac_get_algo_len(prf);
+ int free_data;
+
+ if (prf == NULL || hash_size == 0 || hash_size > 255)
+ return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
+
+ /* Credentials but no username set - this extension is not applicable */
+ if (!_gnutls_have_psk_credentials(cred))
+ return 0;
+
+ ret = _gnutls_find_psk_key(session, cred, &username, &key, &free_data);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (username.size == 0 || username.size > UINT16_MAX) {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_PASSWORD);
+ goto cleanup;
+ }
+
+ /* placeholder to be filled later */
+ pos = extdata->length;
+ ret = _gnutls_buffer_append_prefix(extdata, 16, 0);
+ if (ret < 0) {
+ gnutls_assert_val(ret);
+ goto cleanup;
+ }
+
+ if ((ret = _gnutls_buffer_append_data_prefix(extdata, 16,
+ username.data, username.size)) < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Now append the ticket age, which is always zero for out-of-band PSKs */
+ if ((ret = _gnutls_buffer_append_prefix(extdata, 32, 0)) < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ /* Total length appended is the length of the data, plus six octets */
+ length = (username.size + 6);
+
+ _gnutls_write_uint16(length, &extdata->data[pos]);
+
+ ext_offset = _gnutls_ext_get_extensions_offset(session);
+
+ /* Compute the binders. extdata->data points to the start
+ * of this client hello. */
+ assert(extdata->length >= sizeof(mbuffer_st));
+ assert(ext_offset >= (ssize_t)sizeof(mbuffer_st));
+ ext_offset -= sizeof(mbuffer_st);
+ client_hello.data = extdata->data+sizeof(mbuffer_st);
+ client_hello.size = extdata->length-sizeof(mbuffer_st);
+
+ ret = compute_psk_binder(GNUTLS_CLIENT, prf,
+ hash_size+1, hash_size, extdata->length-pos,
+ ext_offset, &key, &client_hello,
+ binder_value);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Associate the selected pre-shared key with the session */
+ session->key.psk.data = key.data;
+ session->key.psk.size = key.size;
+ session->key.psk_needs_free = free_data;
+ key.data = NULL;
+ session->key.proto.tls13.binder_prf = prf;
+
+ /* Now append the binders */
+ ret = _gnutls_buffer_append_prefix(extdata, 16, hash_size+1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Add the size of the binder (we only have one) */
+ ret = _gnutls_buffer_append_data_prefix(extdata, 8, binder_value, hash_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ if (free_data) {
+ _gnutls_free_datum(&username);
+ _gnutls_free_temp_key_datum(&key);
+ }
+ return ret;
+}
+
+static int
+server_send_params(gnutls_session_t session, gnutls_buffer_t extdata)
+{
+ int ret;
+
+ if (!(session->internals.hsk_flags & HSK_PSK_SELECTED))
+ return 0;
+
+ ret = _gnutls_buffer_append_prefix(extdata, 16,
+ session->key.proto.tls13.psk_index);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 2;
+}
+
+static int server_recv_params(gnutls_session_t session,
+ const unsigned char *data, size_t len,
+ const gnutls_psk_server_credentials_t pskcred)
+{
+ int ret;
+ const mac_entry_st *prf;
+ gnutls_datum_t full_client_hello;
+ uint8_t binder_value[MAX_HASH_SIZE];
+ int psk_index = -1;
+ gnutls_datum_t binder_recvd = { NULL, 0 };
+ gnutls_datum_t key;
+ unsigned hash_size;
+ psk_ext_parser_st psk_parser;
+ struct psk_st psk;
+
+ ret = _gnutls13_psk_ext_parser_init(&psk_parser, data, len);
+ if (ret == 0) {
+ /* No PSKs advertised by client */
+ return 0;
+ } else if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ while ((ret = _gnutls13_psk_ext_parser_next_psk(&psk_parser, &psk)) >= 0) {
+ if (psk.ob_ticket_age == 0) {
+ /* _gnutls_psk_pwd_find_entry() expects 0-terminated identities */
+ if (psk.identity.size > 0 && psk.identity.size <= MAX_USERNAME_SIZE) {
+ char identity_str[psk.identity.size + 1];
+
+ memcpy(identity_str, psk.identity.data, psk.identity.size);
+ identity_str[psk.identity.size] = 0;
+
+ ret = _gnutls_psk_pwd_find_entry(session, identity_str, &key);
+ if (ret == 0)
+ psk_index = ret;
+ }
+ }
+ }
+
+ if (psk_index < 0)
+ return 0;
+
+ ret = _gnutls13_psk_ext_parser_find_binder(&psk_parser, psk_index,
+ &binder_recvd);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* Get full ClientHello */
+ if (!_gnutls_ext_get_full_client_hello(session, &full_client_hello)) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ /* Compute the binder value for this PSK */
+ prf = pskcred->binder_algo;
+ hash_size = prf->output_size;
+ ret = compute_psk_binder(GNUTLS_SERVER, prf, hash_size, hash_size, 0, 0,
+ &key, &full_client_hello,
+ binder_value);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (_gnutls_mac_get_algo_len(prf) != binder_recvd.size ||
+ safe_memcmp(binder_value, binder_recvd.data, binder_recvd.size)) {
+ gnutls_free(key.data);
+ ret = gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ goto cleanup;
+ }
+
+ if (session->internals.hsk_flags & HSK_PSK_KE_MODE_DHE_PSK)
+ _gnutls_handshake_log("EXT[%p]: Selected DHE-PSK mode\n", session);
+ else {
+ reset_cand_groups(session);
+ _gnutls_handshake_log("EXT[%p]: Selected PSK mode\n", session);
+ }
+
+ session->internals.hsk_flags |= HSK_PSK_SELECTED;
+
+ /* Reference the selected pre-shared key */
+ session->key.psk.data = key.data;
+ session->key.psk.size = key.size;
+ session->key.psk_needs_free = 1;
+
+ session->key.proto.tls13.psk_index = psk_index;
+ session->key.proto.tls13.binder_prf = prf;
+
+ ret = 0;
+ cleanup:
+ _gnutls_free_datum(&binder_recvd);
+
+ return ret;
+}
+
+/*
+ * Return values for this function:
+ * - 0 : Not applicable.
+ * - >0 : Ok. Return size of extension data.
+ * - GNUTLS_E_INT_RET_0 : Size of extension data is zero.
+ * - <0 : There's been an error.
+ *
+ * In the client, generates the PskIdentity and PskBinderEntry messages.
+ *
+ * PskIdentity identities<7..2^16-1>;
+ * PskBinderEntry binders<33..2^16-1>;
+ *
+ * struct {
+ * opaque identity<1..2^16-1>;
+ * uint32 obfuscated_ticket_age;
+ * } PskIdentity;
+ *
+ * opaque PskBinderEntry<32..255>;
+ *
+ * The server sends the selected identity, which is a zero-based index
+ * of the PSKs offered by the client:
+ *
+ * struct {
+ * uint16 selected_identity;
+ * } PreSharedKeyExtension;
+ */
+static int _gnutls_psk_send_params(gnutls_session_t session,
+ gnutls_buffer_t extdata)
+{
+ gnutls_psk_client_credentials_t cred = NULL;
+ const version_entry_st *vers;
+
+ if (session->security_parameters.entity == GNUTLS_CLIENT) {
+ vers = _gnutls_version_max(session);
+
+ if (!vers || !vers->tls13_sem)
+ return 0;
+
+ if (session->internals.hsk_flags & HSK_PSK_KE_MODES_SENT) {
+ cred = (gnutls_psk_client_credentials_t)
+ _gnutls_get_cred(session, GNUTLS_CRD_PSK);
+ /* If there are no PSK credentials, this extension is not applicable,
+ * so we return zero. */
+ if (cred == NULL || !session->internals.priorities->have_psk)
+ return 0;
+
+ return client_send_params(session, extdata, cred);
+ } else {
+ return 0;
+ }
+ } else {
+ vers = get_version(session);
+
+ if (!vers || !vers->tls13_sem)
+ return 0;
+
+ cred = (gnutls_psk_client_credentials_t)
+ _gnutls_get_cred(session, GNUTLS_CRD_PSK);
+ if (cred == NULL || !session->internals.priorities->have_psk)
+ return 0;
+
+ if (session->internals.hsk_flags & HSK_PSK_KE_MODES_RECEIVED)
+ return server_send_params(session, extdata);
+ else
+ return 0;
+ }
+}
+
+/*
+ * Return values for this function:
+ * - 0 : Not applicable.
+ * - >0 : Ok. Return size of extension data.
+ * - <0 : There's been an error.
+ */
+static int _gnutls_psk_recv_params(gnutls_session_t session,
+ const unsigned char *data, size_t len)
+{
+ gnutls_psk_server_credentials_t pskcred;
+ const version_entry_st *vers = get_version(session);
+
+ if (!vers || !vers->tls13_sem)
+ return 0;
+
+ if (session->security_parameters.entity == GNUTLS_CLIENT) {
+ if (session->internals.hsk_flags & HSK_PSK_KE_MODES_SENT) {
+ uint16_t selected_identity = _gnutls_read_uint16(data);
+
+ if (selected_identity == 0) {
+ _gnutls_handshake_log("EXT[%p]: Selected PSK mode\n", session);
+ session->internals.hsk_flags |= HSK_PSK_SELECTED;
+ }
+ return 0;
+ } else {
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
+ }
+ } else {
+ if (session->internals.hsk_flags & HSK_PSK_KE_MODES_RECEIVED) {
+ if (session->internals.hsk_flags & HSK_PSK_KE_MODE_INVALID) {
+ /* We received a "psk_ke_modes" extension, but with a value we don't support */
+ return 0;
+ }
+
+ pskcred = (gnutls_psk_server_credentials_t)
+ _gnutls_get_cred(session, GNUTLS_CRD_PSK);
+
+ /* If there are no PSK credentials, this extension is not applicable,
+ * so we return zero. */
+ if (pskcred == NULL)
+ return 0;
+
+ return server_recv_params(session, data, len, pskcred);
+ } else {
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
+ }
+ }
+}
+
+const hello_ext_entry_st ext_pre_shared_key = {
+ .name = "Pre Shared Key",
+ .tls_id = 41,
+ .gid = GNUTLS_EXTENSION_PRE_SHARED_KEY,
+ .parse_type = GNUTLS_EXT_TLS,
+ .validity = GNUTLS_EXT_FLAG_CLIENT_HELLO | GNUTLS_EXT_FLAG_TLS13_SERVER_HELLO,
+ .send_func = _gnutls_psk_send_params,
+ .recv_func = _gnutls_psk_recv_params
+};
diff --git a/lib/ext/pre_shared_key.h b/lib/ext/pre_shared_key.h
new file mode 100644
index 0000000000..25dd159f6e
--- /dev/null
+++ b/lib/ext/pre_shared_key.h
@@ -0,0 +1,18 @@
+#ifndef EXT_PRE_SHARED_KEY_H
+#define EXT_PRE_SHARED_KEY_H
+
+#include "auth/psk.h"
+#include <hello_ext.h>
+
+extern const hello_ext_entry_st ext_pre_shared_key;
+
+inline static
+unsigned _gnutls_have_psk_credentials(const gnutls_psk_client_credentials_t cred)
+{
+ if (cred->get_function || cred->username.data)
+ return 1;
+ else
+ return 0;
+}
+
+#endif
diff --git a/lib/ext/psk_ke_modes.c b/lib/ext/psk_ke_modes.c
new file mode 100644
index 0000000000..c6aef3bda8
--- /dev/null
+++ b/lib/ext/psk_ke_modes.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2017 Free Software Foundation, Inc.
+ *
+ * Author: Ander Juaristi
+ *
+ * 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 "ext/psk_ke_modes.h"
+#include "ext/pre_shared_key.h"
+#include <assert.h>
+
+#define PSK_KE 0
+#define PSK_DHE_KE 1
+
+/*
+ * We only support ECDHE-authenticated PSKs.
+ * The client just sends a "psk_key_exchange_modes" extension
+ * with the value one.
+ */
+static int
+psk_ke_modes_send_params(gnutls_session_t session,
+ gnutls_buffer_t extdata)
+{
+ int ret;
+ gnutls_psk_client_credentials_t cred;
+ const version_entry_st *vers;
+ uint8_t data[2];
+ unsigned pos, i;
+ unsigned have_dhpsk = 0;
+ unsigned have_psk = 0;
+
+ /* Server doesn't send psk_key_exchange_modes */
+ if (session->security_parameters.entity == GNUTLS_SERVER ||
+ !session->internals.priorities->have_psk)
+ return 0;
+
+ cred = (gnutls_psk_client_credentials_t)
+ _gnutls_get_cred(session, GNUTLS_CRD_PSK);
+ if (cred == NULL || _gnutls_have_psk_credentials(cred) == 0)
+ return 0;
+
+ vers = _gnutls_version_max(session);
+ if (!vers || !vers->tls13_sem)
+ return 0;
+
+ pos = 0;
+ for (i=0;i<session->internals.priorities->_kx.algorithms;i++) {
+ if (session->internals.priorities->_kx.priority[i] == GNUTLS_KX_PSK && !have_psk) {
+ assert(pos <= 1);
+ data[pos++] = PSK_KE;
+ session->internals.hsk_flags |= HSK_PSK_KE_MODE_PSK;
+ have_psk = 1;
+ } else if ((session->internals.priorities->_kx.priority[i] == GNUTLS_KX_DHE_PSK ||
+ session->internals.priorities->_kx.priority[i] == GNUTLS_KX_ECDHE_PSK) && !have_dhpsk) {
+ assert(pos <= 1);
+ data[pos++] = PSK_DHE_KE;
+ session->internals.hsk_flags |= HSK_PSK_KE_MODE_DHE_PSK;
+ have_dhpsk = 1;
+ }
+
+ if (have_psk && have_dhpsk)
+ break;
+ }
+
+ ret = _gnutls_buffer_append_data_prefix(extdata, 8, data, pos);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ session->internals.hsk_flags |= HSK_PSK_KE_MODES_SENT;
+
+ return 0;
+}
+
+#define MAX_POS INT_MAX
+
+/*
+ * Since we only support ECDHE-authenticated PSKs, the server
+ * just verifies that a "psk_key_exchange_modes" extension was received,
+ * and that it contains the value one.
+ */
+static int
+psk_ke_modes_recv_params(gnutls_session_t session,
+ const unsigned char *data, size_t _len)
+{
+ uint8_t ke_modes_len;
+ ssize_t len = _len;
+ const version_entry_st *vers = get_version(session);
+ gnutls_psk_server_credentials_t cred;
+ int dhpsk_pos = MAX_POS;
+ int psk_pos = MAX_POS;
+ int cli_psk_pos = MAX_POS;
+ int cli_dhpsk_pos = MAX_POS;
+ unsigned i;
+
+ /* Server doesn't send psk_key_exchange_modes */
+ if (session->security_parameters.entity == GNUTLS_CLIENT)
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
+
+ if (!vers || !vers->tls13_sem)
+ return 0;
+
+ cred = (gnutls_psk_server_credentials_t)_gnutls_get_cred(session, GNUTLS_CRD_PSK);
+ if (cred == NULL)
+ return 0;
+
+ DECR_LEN(len, 1);
+ ke_modes_len = *(data++);
+
+ for (i=0;i<session->internals.priorities->_kx.algorithms;i++) {
+ if (session->internals.priorities->_kx.priority[i] == GNUTLS_KX_PSK && psk_pos == MAX_POS) {
+ psk_pos = i;
+ } else if ((session->internals.priorities->_kx.priority[i] == GNUTLS_KX_DHE_PSK ||
+ session->internals.priorities->_kx.priority[i] == GNUTLS_KX_ECDHE_PSK) &&
+ dhpsk_pos == MAX_POS) {
+ dhpsk_pos = i;
+ }
+
+ if (dhpsk_pos != MAX_POS && psk_pos != MAX_POS)
+ break;
+ }
+
+ if (session->internals.priorities->groups.size == 0 && psk_pos == MAX_POS)
+ return gnutls_assert_val(0);
+
+ for (i=0;i<ke_modes_len;i++) {
+ if (data[i] == PSK_DHE_KE)
+ cli_dhpsk_pos = i;
+ if (data[i] == PSK_KE)
+ cli_psk_pos = i;
+
+ if (cli_psk_pos != MAX_POS && cli_dhpsk_pos != MAX_POS)
+ break;
+ }
+
+ if (session->internals.priorities->server_precedence) {
+ if (dhpsk_pos != MAX_POS && cli_dhpsk_pos != MAX_POS && dhpsk_pos < psk_pos)
+ session->internals.hsk_flags |= HSK_PSK_KE_MODE_DHE_PSK;
+ else if (psk_pos != MAX_POS && cli_psk_pos != MAX_POS && psk_pos < dhpsk_pos)
+ session->internals.hsk_flags |= HSK_PSK_KE_MODE_PSK;
+ } else {
+ if (dhpsk_pos != MAX_POS && cli_dhpsk_pos != MAX_POS && cli_dhpsk_pos < cli_psk_pos)
+ session->internals.hsk_flags |= HSK_PSK_KE_MODE_DHE_PSK;
+ else if (psk_pos != MAX_POS && cli_psk_pos != MAX_POS && cli_psk_pos < cli_dhpsk_pos)
+ session->internals.hsk_flags |= HSK_PSK_KE_MODE_PSK;
+ }
+
+ if ((session->internals.hsk_flags & HSK_PSK_KE_MODE_PSK) ||
+ (session->internals.hsk_flags & HSK_PSK_KE_MODE_DHE_PSK)) {
+ return 0;
+ } else {
+ session->internals.hsk_flags |= HSK_PSK_KE_MODE_INVALID;
+ return 0;
+ }
+}
+
+const hello_ext_entry_st ext_psk_ke_modes = {
+ .name = "PSK Key Exchange Modes",
+ .tls_id = 45,
+ .gid = GNUTLS_EXTENSION_PSK_KE_MODES,
+ .parse_type = GNUTLS_EXT_TLS,
+ .validity = GNUTLS_EXT_FLAG_CLIENT_HELLO | GNUTLS_EXT_FLAG_TLS13_SERVER_HELLO,
+ .send_func = psk_ke_modes_send_params,
+ .recv_func = psk_ke_modes_recv_params
+};
diff --git a/lib/ext/psk_ke_modes.h b/lib/ext/psk_ke_modes.h
new file mode 100644
index 0000000000..bd06139ff5
--- /dev/null
+++ b/lib/ext/psk_ke_modes.h
@@ -0,0 +1,8 @@
+#ifndef EXT_PSK_KE_MODES_H
+#define EXT_PSK_KE_MODES_H
+
+#include <hello_ext.h>
+
+extern const hello_ext_entry_st ext_psk_ke_modes;
+
+#endif
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index e926b3d0fe..cae9d7aec7 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -325,7 +325,13 @@ typedef enum extensions_t {
GNUTLS_EXTENSION_SAFE_RENEGOTIATION,
GNUTLS_EXTENSION_SERVER_NAME,
GNUTLS_EXTENSION_COOKIE,
- GNUTLS_EXTENSION_DUMBFW, /* this must always be the last */
+ GNUTLS_EXTENSION_PSK_KE_MODES,
+ /*
+ * pre_shared_key and dumbfw must always be the last extensions,
+ * in that order
+ */
+ GNUTLS_EXTENSION_PRE_SHARED_KEY,
+ GNUTLS_EXTENSION_DUMBFW,
GNUTLS_EXTENSION_MAX /* not real extension - used for iterators */
} extensions_t;
@@ -449,7 +455,7 @@ typedef struct mbuffer_head_st {
typedef struct auth_cred_st {
gnutls_credentials_type_t algorithm;
- /* the type of credentials depends on algorithm
+ /* the type of credentials depends on algorithm
*/
void *credentials;
struct auth_cred_st *next;
@@ -463,9 +469,21 @@ struct gnutls_key_st {
gnutls_pk_params_st dh_params;
} kshare;
- /* The union contents depend on the negotiated protocol */
+ /* The union contents depend on the negotiated protocol.
+ * It should not contain any values which are allocated
+ * prior to protocol negotiation, as it would be impossible
+ * to deinitialize.
+ */
union {
struct {
+ /*
+ * 0-based index of the selected PSK.
+ * This only applies if the HSK_PSK_SELECTED flag is set in internals.hsk_flags,
+ * which signals a PSK has indeed been selected.
+ */
+ unsigned psk_index;
+ const struct mac_entry_st *binder_prf;
+
/* the current (depending on state) secret, can be
* early_secret, client_early_traffic_secret, ... */
uint8_t temp_secret[MAX_HASH_SIZE];
@@ -507,6 +525,10 @@ struct gnutls_key_st {
} tls12; /* from ssl3.0 to tls12 */
} proto;
+ /* Pre-shared key in use (if any); temporary storage */
+ gnutls_datum_t psk;
+ unsigned psk_needs_free;
+
/* TLS pre-master key; applies to 1.2 and 1.3 */
gnutls_datum_t key;
@@ -849,6 +871,7 @@ struct gnutls_priority_st {
bool allow_server_key_usage_violation; /* for test suite purposes only */
bool no_tickets;
bool have_cbc;
+ bool have_psk;
unsigned int additional_verify_flags;
/* TLS_FALLBACK_SCSV */
@@ -1181,6 +1204,9 @@ typedef struct {
/* it is a copy of the handshake hash buffer if post handshake is used */
gnutls_buffer_st post_handshake_hash_buffer;
+/* When either of PSK or DHE-PSK is received */
+#define HSK_PSK_KE_MODES_RECEIVED (HSK_PSK_KE_MODE_PSK|HSK_PSK_KE_MODE_DHE_PSK|HSK_PSK_KE_MODE_INVALID)
+
#define HSK_CRT_VRFY_EXPECTED 1
#define HSK_CRT_SENT (1<<1)
#define HSK_CRT_ASKED (1<<2)
@@ -1192,10 +1218,25 @@ typedef struct {
#define HSK_FALSE_START_USED (1<<8) /* TLS1.2 only */
#define HSK_HAVE_FFDHE (1<<9) /* whether the peer has advertized at least an FFDHE group */
#define HSK_USED_FFDHE (1<<10) /* whether ffdhe was actually negotiated and used */
+#define HSK_PSK_KE_MODES_SENT (1<<11)
+#define HSK_PSK_KE_MODE_PSK (1<<12) /* client: whether PSK without DH is allowed,
+ * server: whether PSK without DH is selected. */
+#define HSK_PSK_KE_MODE_INVALID (1<<13) /* server: no compatible PSK modes were seen */
+#define HSK_PSK_KE_MODE_DHE_PSK (1<<14) /* server: whether PSK with DH is selected
+ * client: whether PSK with DH is allowed
+ */
+#define HSK_PSK_SELECTED (1<<15)
+#define HSK_KEY_SHARE_SENT (1<<16) /* server: key share was sent to client */
+#define HSK_KEY_SHARE_RECEIVED (1<<17) /* client: key share was received */
+
/* The hsk_flags are for use within the ongoing handshake;
* they are reset to zero prior to handshake start by gnutls_handshake. */
unsigned hsk_flags;
time_t last_key_update;
+ /* Read-only pointer to the full ClientHello message */
+ gnutls_buffer_st full_client_hello;
+ /* The offset at which extensions start in the ClientHello buffer */
+ int extensions_offset;
gnutls_buffer_st hb_local_data;
gnutls_buffer_st hb_remote_data;
@@ -1289,6 +1330,10 @@ typedef struct {
/* Maximum number of epochs we keep around. */
#define MAX_EPOCH_INDEX 4
+#define reset_cand_groups(session) \
+ session->internals.cand_ec_group = session->internals.cand_dh_group = \
+ session->internals.cand_group = NULL
+
struct gnutls_session_int {
security_parameters_st security_parameters;
record_parameters_st *record_parameters[MAX_EPOCH_INDEX];
diff --git a/lib/handshake-tls13.c b/lib/handshake-tls13.c
index edb6e80574..de14cf106e 100644
--- a/lib/handshake-tls13.c
+++ b/lib/handshake-tls13.c
@@ -204,14 +204,46 @@ static int generate_ap_traffic_keys(gnutls_session_t session)
static int generate_hs_traffic_keys(gnutls_session_t session)
{
int ret;
+ unsigned null_key = 0;
- if (unlikely(session->key.key.size == 0 || session->key.proto.tls13.temp_secret_size == 0))
+ if (unlikely(session->key.proto.tls13.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);
- if (ret < 0) {
- gnutls_assert();
- return ret;
+ if ((session->security_parameters.entity == GNUTLS_CLIENT &&
+ !(session->internals.hsk_flags & HSK_KEY_SHARE_RECEIVED)) ||
+ (session->security_parameters.entity == GNUTLS_SERVER &&
+ !(session->internals.hsk_flags & HSK_KEY_SHARE_SENT))) {
+
+ if ((session->internals.hsk_flags & HSK_PSK_SELECTED) &&
+ (session->internals.hsk_flags & HSK_PSK_KE_MODE_PSK)) {
+ null_key = 1;
+ }
+ }
+
+ if (null_key) {
+ uint8_t digest[MAX_HASH_SIZE];
+ unsigned digest_size;
+
+ if (unlikely(session->security_parameters.prf == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ digest_size = session->security_parameters.prf->output_size;
+ memset(digest, 0, digest_size);
+
+ ret = _tls13_update_secret(session, digest, digest_size);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ } else {
+ if (unlikely(session->key.key.size == 0))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ ret = _tls13_update_secret(session, session->key.key.data, session->key.key.size);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
}
ret = _tls13_connection_state_init(session, STAGE_HS);
diff --git a/lib/handshake.c b/lib/handshake.c
index 955fd5dd08..5d2bf9b852 100644
--- a/lib/handshake.c
+++ b/lib/handshake.c
@@ -83,6 +83,7 @@ handshake_hash_buffer_reset(gnutls_session_t session)
session->internals.handshake_hash_buffer_server_finished_len = 0;
session->internals.handshake_hash_buffer_prev_len = 0;
session->internals.handshake_hash_buffer.length = 0;
+ session->internals.full_client_hello.length = 0;
return;
}
@@ -108,6 +109,7 @@ void _gnutls_handshake_hash_buffers_clear(gnutls_session_t session)
{
handshake_hash_buffer_reset(session);
_gnutls_buffer_clear(&session->internals.handshake_hash_buffer);
+ _gnutls_buffer_clear(&session->internals.full_client_hello);
}
/* Replace handshake message buffer, with the special synthetic message
@@ -1409,8 +1411,15 @@ _gnutls_recv_handshake(gnutls_session_t session,
hsk.data.length);
else
#endif
+ {
+ /* Reference the full ClientHello in case an extension needs it */
+ ret = _gnutls_ext_set_full_client_hello(session, &hsk);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
ret = read_client_hello(session, hsk.data.data,
hsk.data.length);
+ }
if (ret < 0) {
gnutls_assert();
@@ -1581,6 +1590,15 @@ set_client_ciphersuite(gnutls_session_t session, uint8_t suite[2])
gnutls_assert();
return GNUTLS_E_INTERNAL_ERROR;
}
+ } else {
+ if (session->internals.hsk_flags & HSK_PSK_SELECTED) {
+ if (session->key.proto.tls13.binder_prf->id != selected->prf) {
+ _gnutls_handshake_log
+ ("HSK[%p]: PRF of ciphersuite differs with the PSK identity (cs: %s, id: %s)\n",
+ session, selected->name, session->key.proto.tls13.binder_prf->name);
+ gnutls_assert();
+ }
+ }
}
return 0;
@@ -1659,6 +1677,8 @@ read_server_hello(gnutls_session_t session,
int len = datalen;
unsigned ext_parse_flag = 0;
const version_entry_st *vers, *saved_vers;
+ const uint8_t *psk = NULL;
+ size_t psk_size = 0;
if (datalen < GNUTLS_RANDOM_SIZE+2) {
gnutls_assert();
@@ -1832,6 +1852,28 @@ read_server_hello(gnutls_session_t session,
if (ret < 0)
return gnutls_assert_val(ret);
+ if (vers->tls13_sem) {
+ /* TLS 1.3 Early Secret */
+ if (session->internals.hsk_flags & HSK_PSK_SELECTED) {
+ psk = session->key.psk.data;
+ psk_size = session->key.psk.size;
+ }
+
+ ret = _tls13_init_secret(session, psk, psk_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _tls13_derive_secret(session, DERIVED_LABEL, sizeof(DERIVED_LABEL)-1,
+ NULL, 0, session->key.proto.tls13.temp_secret,
+ session->key.proto.tls13.temp_secret);
+ if (ret < 0)
+ gnutls_assert();
+ }
+
+cleanup:
+
return ret;
}
@@ -2081,6 +2123,8 @@ int _gnutls_send_server_hello(gnutls_session_t session, int again)
const version_entry_st *vers;
uint8_t vbytes[2];
unsigned extflag = 0;
+ const uint8_t *psk = NULL;
+ size_t psk_size = 0;
_gnutls_buffer_init(&buf);
@@ -2091,9 +2135,16 @@ int _gnutls_send_server_hello(gnutls_session_t session, int again)
if (vers->tls13_sem) {
/* TLS 1.3 Early Secret */
- ret = _tls13_init_secret(session, NULL, 0);
- if (ret < 0)
- return gnutls_assert_val(ret);
+ if (session->internals.hsk_flags & HSK_PSK_SELECTED) {
+ psk = session->key.psk.data;
+ psk_size = session->key.psk.size;
+ }
+
+ ret = _tls13_init_secret(session, psk, psk_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
vbytes[0] = 0x03; /* TLS1.2 */
vbytes[1] = 0x03;
@@ -2105,8 +2156,10 @@ int _gnutls_send_server_hello(gnutls_session_t session, int again)
}
ret = _gnutls_buffer_init_handshake_mbuffer(&buf);
- if (ret < 0)
- return gnutls_assert_val(ret);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
ret = _gnutls_buffer_append_data(&buf, vbytes, 2);
if (ret < 0) {
@@ -2180,7 +2233,7 @@ int _gnutls_send_server_hello(gnutls_session_t session, int again)
_gnutls_send_handshake(session, bufel,
GNUTLS_HANDSHAKE_SERVER_HELLO);
- fail:
+fail:
_gnutls_buffer_clear(&buf);
return ret;
}
@@ -2580,6 +2633,9 @@ int _gnutls_run_verify_callback(gnutls_session_t session, unsigned int side)
gnutls_certificate_credentials_t cred;
int ret, type;
+ if (session->internals.hsk_flags & HSK_PSK_SELECTED)
+ return 0;
+
cred =
(gnutls_certificate_credentials_t) _gnutls_get_cred(session,
GNUTLS_CRD_CERTIFICATE);
diff --git a/lib/hello_ext.c b/lib/hello_ext.c
index 7fe8e1056c..57583231a3 100644
--- a/lib/hello_ext.c
+++ b/lib/hello_ext.c
@@ -1,6 +1,6 @@
/*
- * Copyright (C) 2001-2016 Free Software Foundation, Inc.
- * Copyright (C) 2015-2017 Red Hat, Inc.
+ * Copyright (C) 2001-2018 Free Software Foundation, Inc.
+ * Copyright (C) 2015-2018 Red Hat, Inc.
*
* Author: Nikos Mavrogiannopoulos, Simon Josefsson
*
@@ -46,6 +46,8 @@
#include <ext/alpn.h>
#include <ext/dumbfw.h>
#include <ext/key_share.h>
+#include <ext/pre_shared_key.h>
+#include <ext/psk_ke_modes.h>
#include <ext/etm.h>
#include <ext/cookie.h>
#include "extv.h"
@@ -87,6 +89,8 @@ static hello_ext_entry_st const *extfunc[MAX_EXT_TYPES+1] = {
#ifdef ENABLE_ALPN
[GNUTLS_EXTENSION_ALPN] = &ext_mod_alpn,
#endif
+ [GNUTLS_EXTENSION_PSK_KE_MODES] = &ext_psk_ke_modes,
+ [GNUTLS_EXTENSION_PRE_SHARED_KEY] = &ext_pre_shared_key,
/* This must be the last extension registered.
*/
[GNUTLS_EXTENSION_DUMBFW] = &ext_mod_dumbfw,
@@ -335,9 +339,9 @@ int hello_ext_send(void *_ctx, gnutls_buffer_st *buf)
int
_gnutls_gen_hello_extensions(gnutls_session_t session,
- gnutls_buffer_st * buf,
- gnutls_ext_flags_t msg,
- gnutls_ext_parse_type_t parse_type)
+ gnutls_buffer_st * buf,
+ gnutls_ext_flags_t msg,
+ gnutls_ext_parse_type_t parse_type)
{
int pos, ret;
size_t i;
@@ -352,6 +356,7 @@ _gnutls_gen_hello_extensions(gnutls_session_t session,
return gnutls_assert_val(ret);
pos = ret;
+ _gnutls_ext_set_extensions_offset(session, pos);
for (i=0; i < session->internals.rexts_size; i++) {
ctx.ext = &session->internals.rexts[i];
@@ -481,6 +486,38 @@ int _gnutls_hello_ext_pack(gnutls_session_t session, gnutls_buffer_st *packed)
return 0;
}
+int _gnutls_ext_set_full_client_hello(gnutls_session_t session,
+ handshake_buffer_st *recv_buf)
+{
+ int ret;
+ gnutls_buffer_st *buf = &session->internals.full_client_hello;
+
+ _gnutls_buffer_clear(buf);
+
+ if ((ret = _gnutls_buffer_append_prefix(buf, 8, recv_buf->htype)) < 0)
+ return gnutls_assert_val(ret);
+ if ((ret = _gnutls_buffer_append_prefix(buf, 24, recv_buf->data.length)) < 0)
+ return gnutls_assert_val(ret);
+ if ((ret = _gnutls_buffer_append_data(buf, recv_buf->data.data, recv_buf->data.length)) < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+}
+
+unsigned _gnutls_ext_get_full_client_hello(gnutls_session_t session,
+ gnutls_datum_t *d)
+{
+ gnutls_buffer_st *buf = &session->internals.full_client_hello;
+
+ if (!buf->length)
+ return 0;
+
+ d->data = buf->data;
+ d->size = buf->length;
+
+ return 1;
+}
+
static void
_gnutls_ext_set_resumed_session_data(gnutls_session_t session,
extensions_t id,
diff --git a/lib/hello_ext.h b/lib/hello_ext.h
index 53e1d5eede..f0fcf056c7 100644
--- a/lib/hello_ext.h
+++ b/lib/hello_ext.h
@@ -63,6 +63,22 @@ inline static void _gnutls_ext_set_msg(gnutls_session_t session, gnutls_ext_flag
session->internals.ext_msg = msg;
}
+inline static void _gnutls_ext_set_extensions_offset(gnutls_session_t session,
+ int offset)
+{
+ session->internals.extensions_offset = offset;
+}
+
+inline static int _gnutls_ext_get_extensions_offset(gnutls_session_t session)
+{
+ return session->internals.extensions_offset;
+}
+
+int _gnutls_ext_set_full_client_hello(gnutls_session_t session,
+ handshake_buffer_st *recv_buf);
+unsigned _gnutls_ext_get_full_client_hello(gnutls_session_t session,
+ gnutls_datum_t *datum);
+
/* for session packing */
int _gnutls_hello_ext_pack(gnutls_session_t session, gnutls_buffer_st * packed);
int _gnutls_hello_ext_unpack(gnutls_session_t session,
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index 8a00423336..cf7c61866c 100644
--- a/lib/includes/gnutls/gnutls.h.in
+++ b/lib/includes/gnutls/gnutls.h.in
@@ -2867,7 +2867,8 @@ void gnutls_fips140_set_mode(gnutls_fips_mode_t mode, unsigned flags);
#define GNUTLS_E_AGAIN -28
#define GNUTLS_E_EXPIRED -29
#define GNUTLS_E_DB_ERROR -30
-#define GNUTLS_E_SRP_PWD_ERROR -31
+#define GNUTLS_E_SRP_PWD_ERROR GNUTLS_E_KEYFILE_ERROR
+#define GNUTLS_E_KEYFILE_ERROR -31
#define GNUTLS_E_INSUFFICIENT_CREDENTIALS -32
#define GNUTLS_E_INSUFICIENT_CREDENTIALS GNUTLS_E_INSUFFICIENT_CREDENTIALS /* for backwards compatibility only */
#define GNUTLS_E_INSUFFICIENT_CRED GNUTLS_E_INSUFFICIENT_CREDENTIALS
@@ -2916,7 +2917,8 @@ void gnutls_fips140_set_mode(gnutls_fips_mode_t mode, unsigned flags);
#define GNUTLS_E_PK_SIG_VERIFY_FAILED -89
#define GNUTLS_E_ILLEGAL_SRP_USERNAME -90
-#define GNUTLS_E_SRP_PWD_PARSING_ERROR -91
+#define GNUTLS_E_SRP_PWD_PARSING_ERROR GNUTLS_E_KEYFILE_PARSING_ERROR
+#define GNUTLS_E_KEYFILE_PARSING_ERROR -91
#define GNUTLS_E_NO_TEMPORARY_DH_PARAMS -93
/* For certificate and key stuff
diff --git a/lib/priority.c b/lib/priority.c
index 65b3dd3d93..25f7ebab37 100644
--- a/lib/priority.c
+++ b/lib/priority.c
@@ -1177,6 +1177,7 @@ static int set_ciphersuite_list(gnutls_priority_t priority_cache)
const version_entry_st *tlsmin = NULL;
const version_entry_st *dtlsmin = NULL;
unsigned have_tls13 = 0;
+ unsigned have_psk = 0;
priority_cache->cs.size = 0;
priority_cache->sigalg.size = 0;
@@ -1213,9 +1214,18 @@ static int set_ciphersuite_list(gnutls_priority_t priority_cache)
if ((!tlsmax || !tlsmin) && (!dtlsmax || !dtlsmin))
return gnutls_assert_val(GNUTLS_E_NO_PRIORITIES_WERE_SET);
+ for (i = 0; i < priority_cache->_kx.algorithms; i++) {
+ if (_gnutls_kx_is_psk(priority_cache->_kx.priority[i])) {
+ have_psk = 1;
+ break;
+ }
+ }
+
+ priority_cache->have_psk = have_psk;
+
/* if we are have TLS1.3+ do not enable any key exchange algorithms,
* the protocol doesn't require any. */
- if (tlsmin && tlsmin->tls13_sem) {
+ if (tlsmin && tlsmin->tls13_sem && !have_psk) {
if (!dtlsmin || (dtlsmin && dtlsmin->tls13_sem))
priority_cache->_kx.algorithms = 0;
}
@@ -1316,7 +1326,7 @@ static int set_ciphersuite_list(gnutls_priority_t priority_cache)
return gnutls_assert_val(GNUTLS_E_NO_PRIORITIES_WERE_SET);
/* when TLS 1.3 is available we must have groups set */
- if (tlsmax && tlsmax->id >= GNUTLS_TLS1_3 && priority_cache->groups.size == 0)
+ if (!have_psk && tlsmax && tlsmax->id >= GNUTLS_TLS1_3 && priority_cache->groups.size == 0)
return gnutls_assert_val(GNUTLS_E_NO_PRIORITIES_WERE_SET);
return 0;
diff --git a/lib/psk.c b/lib/psk.c
index 34083c8de7..1d5d21d62b 100644
--- a/lib/psk.c
+++ b/lib/psk.c
@@ -67,6 +67,8 @@ gnutls_psk_allocate_client_credentials(gnutls_psk_client_credentials_t *
if (*sc == NULL)
return GNUTLS_E_MEMORY_ERROR;
+ /* TLS 1.3 - Default binder HMAC algorithm is SHA-256 */
+ (*sc)->binder_algo = _gnutls_mac_to_entry(GNUTLS_MAC_SHA256);
return 0;
}
@@ -182,6 +184,8 @@ gnutls_psk_allocate_server_credentials(gnutls_psk_server_credentials_t *
if (*sc == NULL)
return GNUTLS_E_MEMORY_ERROR;
+ /* TLS 1.3 - Default binder HMAC algorithm is SHA-256 */
+ (*sc)->binder_algo = _gnutls_mac_to_entry(GNUTLS_MAC_SHA256);
return 0;
}
@@ -343,7 +347,10 @@ const char *gnutls_psk_server_get_username(gnutls_session_t session)
* username to use. This should only be called in case of PSK
* authentication and in case of a client.
*
- * Returns: the identity hint of the peer, or %NULL in case of an error.
+ * Note: there is no hint in TLS 1.3, so this function will return %NULL
+ * if TLS 1.3 has been negotiated.
+ *
+ * Returns: the identity hint of the peer, or %NULL in case of an error or if TLS 1.3 is being used.
*
* Since: 2.4.0
**/
diff --git a/lib/secrets.c b/lib/secrets.c
index 73402f9e60..fed5198ae6 100644
--- a/lib/secrets.c
+++ b/lib/secrets.c
@@ -32,24 +32,36 @@
/* HKDF-Extract(0,0) or HKDF-Extract(0, PSK) */
int _tls13_init_secret(gnutls_session_t session, const uint8_t *psk, size_t psk_size)
{
+ session->key.proto.tls13.temp_secret_size = session->security_parameters.prf->output_size;
+
+ return _tls13_init_secret2(session->security_parameters.prf,
+ psk, psk_size,
+ session->key.proto.tls13.temp_secret);
+}
+
+int _tls13_init_secret2(const mac_entry_st *prf,
+ const uint8_t *psk, size_t psk_size,
+ void *out)
+{
char buf[128];
- session->key.proto.tls13.temp_secret_size = session->security_parameters.prf->output_size;
+ if (unlikely(prf == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
/* when no PSK, use the zero-value */
if (psk == NULL) {
- psk_size = session->key.proto.tls13.temp_secret_size;
+ psk_size = prf->output_size;
if (unlikely(psk_size >= sizeof(buf)))
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
memset(buf, 0, psk_size);
- psk = (uint8_t*)buf;
+ psk = (uint8_t*) buf;
}
- return gnutls_hmac_fast(session->security_parameters.prf->id,
+ return gnutls_hmac_fast(prf->id,
"", 0,
psk, psk_size,
- session->key.proto.tls13.temp_secret);
+ out);
}
/* HKDF-Extract(Prev-Secret, key) */
@@ -62,7 +74,7 @@ int _tls13_update_secret(gnutls_session_t session, const uint8_t *key, size_t ke
}
/* Derive-Secret(Secret, Label, Messages) */
-int _tls13_derive_secret(gnutls_session_t session,
+int _tls13_derive_secret2(const mac_entry_st *prf,
const char *label, unsigned label_size,
const uint8_t *tbh, size_t tbh_size,
const uint8_t secret[MAX_HASH_SIZE],
@@ -70,21 +82,39 @@ int _tls13_derive_secret(gnutls_session_t session,
{
uint8_t digest[MAX_HASH_SIZE];
int ret;
- unsigned digest_size = session->security_parameters.prf->output_size;
+ unsigned digest_size;
+ if (unlikely(prf == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
if (unlikely(label_size >= sizeof(digest)))
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
- ret = gnutls_hash_fast((gnutls_digest_algorithm_t)session->security_parameters.prf->id,
+ digest_size = prf->output_size;
+ ret = gnutls_hash_fast((gnutls_digest_algorithm_t) prf->id,
tbh, tbh_size, digest);
if (ret < 0)
return gnutls_assert_val(ret);
- return _tls13_expand_secret(session, label, label_size, digest, digest_size, secret, digest_size, out);
+ return _tls13_expand_secret2(prf, label, label_size, digest, digest_size, secret, digest_size, out);
+}
+
+/* Derive-Secret(Secret, Label, Messages) */
+int _tls13_derive_secret(gnutls_session_t session,
+ const char *label, unsigned label_size,
+ const uint8_t *tbh, size_t tbh_size,
+ const uint8_t secret[MAX_HASH_SIZE],
+ void *out)
+{
+ if (unlikely(session->security_parameters.prf == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ return _tls13_derive_secret2(session->security_parameters.prf, label, label_size, tbh, tbh_size,
+ secret,
+ out);
}
/* HKDF-Expand-Label(Secret, Label, HashValue, Length) */
-int _tls13_expand_secret(gnutls_session_t session,
+int _tls13_expand_secret2(const mac_entry_st *prf,
const char *label, unsigned label_size,
const uint8_t *msg, size_t msg_size,
const uint8_t secret[MAX_HASH_SIZE],
@@ -119,7 +149,7 @@ int _tls13_expand_secret(gnutls_session_t session,
goto cleanup;
}
- switch(session->security_parameters.prf->id) {
+ switch (prf->id) {
case GNUTLS_MAC_SHA256:{
struct hmac_sha256_ctx ctx;
@@ -160,3 +190,20 @@ int _tls13_expand_secret(gnutls_session_t session,
_gnutls_buffer_clear(&str);
return ret;
}
+
+int _tls13_expand_secret(gnutls_session_t session,
+ const char *label, unsigned label_size,
+ const uint8_t *msg, size_t msg_size,
+ const uint8_t secret[MAX_CIPHER_KEY_SIZE],
+ unsigned out_size,
+ void *out)
+{
+ if (unlikely(session->security_parameters.prf == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ return _tls13_expand_secret2(session->security_parameters.prf,
+ label, label_size,
+ msg, msg_size, secret,
+ out_size, out);
+}
+
diff --git a/lib/secrets.h b/lib/secrets.h
index 0dcdcf7c9c..92255ceaa6 100644
--- a/lib/secrets.h
+++ b/lib/secrets.h
@@ -22,14 +22,25 @@
#ifndef SECRETS_H
#define SECRETS_H
+#include "gnutls_int.h"
int _tls13_init_secret(gnutls_session_t session, const uint8_t *psk, size_t psk_size);
int _tls13_update_secret(gnutls_session_t session, const uint8_t *key, size_t key_size);
+
+int _tls13_init_secret2(const mac_entry_st *prf,
+ const uint8_t *psk, size_t psk_size,
+ void *out);
+
int _tls13_derive_secret(gnutls_session_t session,
const char *label, unsigned label_size,
const uint8_t *msg, size_t msg_size,
const uint8_t secret[MAX_HASH_SIZE],
void *out /* of enough length to hold PRF MAC */);
+int _tls13_derive_secret2(const mac_entry_st *prf,
+ const char *label, unsigned label_size,
+ const uint8_t *tbh, size_t tbh_size,
+ const uint8_t secret[MAX_CIPHER_KEY_SIZE],
+ void *out);
int _tls13_expand_secret(gnutls_session_t session,
const char *label, unsigned label_size,
@@ -37,5 +48,10 @@ int _tls13_expand_secret(gnutls_session_t session,
const uint8_t secret[MAX_HASH_SIZE],
unsigned out_size,
void *out);
+int _tls13_expand_secret2(const mac_entry_st *prf,
+ const char *label, unsigned label_size,
+ const uint8_t *msg, size_t msg_size,
+ const uint8_t secret[MAX_CIPHER_KEY_SIZE],
+ unsigned out_size, void *out);
#endif
diff --git a/lib/session.c b/lib/session.c
index 6c2671d70e..3e29c15292 100644
--- a/lib/session.c
+++ b/lib/session.c
@@ -306,7 +306,18 @@ char *gnutls_session_get_desc(gnutls_session_t session)
sign_str = gnutls_sign_get_name(sign_algo);
if (kx == 0 && ver->tls13_sem) { /* TLS 1.3 */
- if (group && sign_str) {
+ if (session->internals.hsk_flags & HSK_PSK_SELECTED) {
+ if (group) {
+ if (group->pk == GNUTLS_PK_DH)
+ snprintf(kx_name, sizeof(kx_name), "(DHE-PSK-%s)",
+ group_name);
+ else
+ snprintf(kx_name, sizeof(kx_name), "(ECDHE-PSK-%s)",
+ group_name);
+ } else {
+ snprintf(kx_name, sizeof(kx_name), "(PSK)");
+ }
+ } else if (group && sign_str) {
if (group->curve)
snprintf(kx_name, sizeof(kx_name), "(ECDHE-%s)-(%s)",
group_name, sign_str);
@@ -346,7 +357,7 @@ char *gnutls_session_get_desc(gnutls_session_t session)
type = gnutls_certificate_type_get(session);
- if (type == GNUTLS_CRT_X509)
+ if (type == GNUTLS_CRT_X509 || type == GNUTLS_CRT_UNKNOWN)
snprintf(proto_name, sizeof(proto_name), "%s",
gnutls_protocol_get_name(get_num_version
(session)));
diff --git a/lib/state.c b/lib/state.c
index 708f7649c7..1062c446bf 100644
--- a/lib/state.c
+++ b/lib/state.c
@@ -119,11 +119,22 @@ gnutls_kx_algorithm_t gnutls_kx_get(gnutls_session_t session)
const version_entry_st *ver = get_version(session);
const gnutls_group_entry_st *group = get_group(session);
- if (ver->tls13_sem && group) {
- if (group->curve)
- return GNUTLS_KX_ECDHE_RSA;
- else
- return GNUTLS_KX_DHE_RSA;
+ if (ver->tls13_sem) {
+ if (session->internals.hsk_flags & HSK_PSK_SELECTED) {
+ if (group) {
+ if (group->pk == GNUTLS_PK_DH)
+ return GNUTLS_KX_DHE_PSK;
+ else
+ return GNUTLS_KX_ECDHE_PSK;
+ } else {
+ return GNUTLS_KX_PSK;
+ }
+ } else if (group) {
+ if (group->pk == GNUTLS_PK_DH)
+ return GNUTLS_KX_DHE_RSA;
+ else
+ return GNUTLS_KX_ECDHE_RSA;
+ }
}
}
@@ -207,6 +218,8 @@ static void deinit_keys(gnutls_session_t session)
sizeof(session->key.proto.tls13.hs_skey));
}
+ if (session->key.psk_needs_free)
+ _gnutls_free_temp_key_datum(&session->key.psk);
_gnutls_free_temp_key_datum(&session->key.key);
}
@@ -279,7 +292,7 @@ void _gnutls_handshake_internal_state_clear(gnutls_session_t session)
int gnutls_init(gnutls_session_t * session, unsigned int flags)
{
int ret;
-
+
FAIL_IF_LIB_ERROR;
*session = gnutls_calloc(1, sizeof(struct gnutls_session_int));
diff --git a/lib/tls13/certificate.c b/lib/tls13/certificate.c
index ad05f372c5..d4abd58702 100644
--- a/lib/tls13/certificate.c
+++ b/lib/tls13/certificate.c
@@ -38,6 +38,9 @@ int _gnutls13_recv_certificate(gnutls_session_t session)
gnutls_buffer_st buf;
unsigned optional = 0;
+ if (session->internals.hsk_flags & HSK_PSK_SELECTED)
+ return 0;
+
if (session->security_parameters.entity == GNUTLS_SERVER) {
/* if we didn't request a certificate, there will not be any */
if (session->internals.send_cert_req == 0)
@@ -194,6 +197,9 @@ int _gnutls13_send_certificate(gnutls_session_t session, unsigned again)
gnutls_certificate_credentials_t cred;
if (again == 0) {
+ if (session->internals.hsk_flags & HSK_PSK_SELECTED)
+ return 0;
+
cred = (gnutls_certificate_credentials_t)
_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE);
if (cred == NULL) {
diff --git a/lib/tls13/certificate_request.c b/lib/tls13/certificate_request.c
index 4e7c104afb..293cc38dcf 100644
--- a/lib/tls13/certificate_request.c
+++ b/lib/tls13/certificate_request.c
@@ -187,6 +187,9 @@ int _gnutls13_recv_certificate_request(gnutls_session_t session)
int ret;
gnutls_buffer_st buf;
+ if (session->internals.hsk_flags & HSK_PSK_SELECTED)
+ return 0;
+
if (unlikely(session->security_parameters.entity != GNUTLS_CLIENT))
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
@@ -246,6 +249,9 @@ int _gnutls13_send_certificate_request(gnutls_session_t session, unsigned again)
if (again == 0) {
unsigned char rnd[12];
+ if (session->internals.hsk_flags & HSK_PSK_SELECTED)
+ return 0;
+
if (session->internals.send_cert_req == 0)
return 0;
diff --git a/lib/tls13/certificate_verify.c b/lib/tls13/certificate_verify.c
index 995e0c6058..33318ca1cf 100644
--- a/lib/tls13/certificate_verify.c
+++ b/lib/tls13/certificate_verify.c
@@ -142,6 +142,9 @@ int _gnutls13_send_certificate_verify(gnutls_session_t session, unsigned again)
const gnutls_sign_entry_st *se;
if (again == 0) {
+ if (session->internals.hsk_flags & HSK_PSK_SELECTED)
+ return 0;
+
ret = _gnutls_get_selected_cert(session, &apr_cert_list,
&apr_cert_list_length, &apr_pkey);
if (ret < 0)
diff --git a/lib/tls13/finished.c b/lib/tls13/finished.c
index 9286f328f6..c28d24a19d 100644
--- a/lib/tls13/finished.c
+++ b/lib/tls13/finished.c
@@ -28,40 +28,63 @@
#include "mbuffers.h"
#include "secrets.h"
-int _gnutls13_recv_finished(gnutls_session_t session)
+int _gnutls13_compute_finished(const mac_entry_st *prf,
+ const uint8_t *base_key,
+ unsigned hash_size,
+ gnutls_buffer_st *handshake_hash_buffer,
+ void *out)
{
int ret;
- gnutls_buffer_st buf;
uint8_t fkey[MAX_HASH_SIZE];
uint8_t ts_hash[MAX_HASH_SIZE];
- uint8_t verifier[MAX_HASH_SIZE];
- const uint8_t *base_key;
- unsigned 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;
-
- ret = _tls13_expand_secret(session, "finished", 8, NULL, 0, base_key,
+ ret = _tls13_expand_secret2(prf,
+ "finished", 8,
+ NULL, 0,
+ base_key,
hash_size, fkey);
if (ret < 0)
return gnutls_assert_val(ret);
- ret = gnutls_hash_fast(session->security_parameters.prf->id,
- session->internals.handshake_hash_buffer.data,
- /* we haven't yet processed the finished message */
- session->internals.handshake_hash_buffer.length,
+ ret = gnutls_hash_fast(prf->id,
+ handshake_hash_buffer->data,
+ handshake_hash_buffer->length,
ts_hash);
- if (ret < 0) {
- gnutls_assert();
- goto cleanup;
- }
+ if (ret < 0)
+ return gnutls_assert_val(ret);
- ret = gnutls_hmac_fast(session->security_parameters.prf->id,
+ ret = gnutls_hmac_fast(prf->id,
fkey, hash_size,
ts_hash, hash_size,
- verifier);
+ out);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+}
+
+int _gnutls13_recv_finished(gnutls_session_t session)
+{
+ int ret;
+ gnutls_buffer_st buf;
+ uint8_t verifier[MAX_HASH_SIZE];
+ const uint8_t *base_key;
+ unsigned hash_size;
+
+ if (unlikely(session->security_parameters.prf == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ 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;
+
+ ret = _gnutls13_compute_finished(session->security_parameters.prf,
+ base_key, hash_size,
+ &session->internals.handshake_hash_buffer,
+ verifier);
if (ret < 0) {
gnutls_assert();
goto cleanup;
@@ -96,37 +119,26 @@ cleanup:
int _gnutls13_send_finished(gnutls_session_t session, unsigned again)
{
int ret;
- uint8_t fkey[MAX_HASH_SIZE];
- uint8_t ts_hash[MAX_HASH_SIZE];
uint8_t verifier[MAX_HASH_SIZE];
mbuffer_st *bufel = NULL;
const uint8_t *base_key;
- unsigned hash_size = session->security_parameters.prf->output_size;
+ unsigned hash_size;
if (again == 0) {
+ if (unlikely(session->security_parameters.prf == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ 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;
- ret = _tls13_expand_secret(session, "finished", 8, NULL, 0, base_key,
- hash_size, fkey);
- if (ret < 0)
- return gnutls_assert_val(ret);
-
- ret = gnutls_hash_fast(session->security_parameters.prf->id,
- session->internals.handshake_hash_buffer.data,
- session->internals.handshake_hash_buffer.length,
- ts_hash);
- if (ret < 0) {
- gnutls_assert();
- goto cleanup;
- }
-
- ret = gnutls_hmac_fast(session->security_parameters.prf->id,
- fkey, hash_size,
- ts_hash, hash_size,
- verifier);
+ ret = _gnutls13_compute_finished(session->security_parameters.prf,
+ base_key, hash_size,
+ &session->internals.handshake_hash_buffer,
+ verifier);
if (ret < 0) {
gnutls_assert();
goto cleanup;
diff --git a/lib/tls13/finished.h b/lib/tls13/finished.h
index fc49c2e63f..2e732e7493 100644
--- a/lib/tls13/finished.h
+++ b/lib/tls13/finished.h
@@ -20,5 +20,10 @@
*
*/
+int _gnutls13_compute_finished(const mac_entry_st *prf,
+ const uint8_t *base_key,
+ unsigned hash_size,
+ gnutls_buffer_st *handshake_hash_buffer,
+ void *out);
int _gnutls13_recv_finished(gnutls_session_t session);
int _gnutls13_send_finished(gnutls_session_t session, unsigned again);
diff --git a/lib/tls13/psk_ext_parser.c b/lib/tls13/psk_ext_parser.c
new file mode 100644
index 0000000000..7dd1427cf3
--- /dev/null
+++ b/lib/tls13/psk_ext_parser.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2017-2018 Free Software Foundation, Inc.
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * Author: Ander Juaristi, 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 "tls13/psk_ext_parser.h"
+
+
+static int advance_to_end_of_object(psk_ext_parser_st *p)
+{
+ size_t adv;
+
+ /* Advance the pointer to the end of the current object */
+ if (p->obj_read < p->obj_len) {
+ adv = p->obj_len - p->obj_read;
+ DECR_LEN(p->len, adv);
+ p->data += adv;
+ }
+
+ return 0;
+}
+
+int _gnutls13_psk_ext_parser_init(psk_ext_parser_st *p,
+ const unsigned char *data, size_t _len)
+{
+ uint16_t identities_len;
+ ssize_t len = _len;
+
+ if (!p || !data || !len)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ memset(p, 0, sizeof(*p));
+
+ identities_len = _gnutls_read_uint16(data);
+
+ if (identities_len > 0) {
+ DECR_LEN(len, 2);
+ data += 2;
+
+ p->obj_len = identities_len;
+ p->data = (unsigned char *) data;
+ p->len = len;
+ }
+
+ return identities_len;
+}
+
+int _gnutls13_psk_ext_parser_next_psk(psk_ext_parser_st *p, struct psk_st *psk)
+{
+ if (p->obj_read >= p->obj_len)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+
+ /* Read a PskIdentity structure */
+ psk->identity.size = _gnutls_read_uint16(p->data);
+ if (psk->identity.size == 0)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+
+ DECR_LEN(p->len, 2);
+ p->data += 2;
+ p->obj_read += 2;
+
+ psk->identity.data = p->data;
+
+ DECR_LEN(p->len, psk->identity.size);
+ p->data += psk->identity.size;
+ p->obj_read += psk->identity.size;
+
+ psk->ob_ticket_age = _gnutls_read_uint32(p->data);
+ DECR_LEN(p->len, 4);
+ p->data += 4;
+ p->obj_read += 4;
+
+ return p->next_index++;
+}
+
+int _gnutls13_psk_ext_parser_find_binder(psk_ext_parser_st *p, int psk_index,
+ gnutls_datum_t *binder_out)
+{
+ uint16_t binders_len;
+ uint8_t binder_len;
+ int cur_index = 0, binder_found = 0;
+
+ if (p == NULL || psk_index < 0 || binder_out == NULL)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ if (p->obj_len == 0)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ /* Place the pointer at the start of the binders */
+ if (advance_to_end_of_object(p) < 0)
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+
+ DECR_LEN(p->len, 2);
+ binders_len = _gnutls_read_uint16(p->data);
+ if (binders_len > 0) {
+ p->data += 2;
+
+ p->obj_len = binders_len;
+ p->obj_read = 0;
+ } else {
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+ }
+
+ /* Start traversing the binders */
+ while (!binder_found && p->len > 0) {
+ DECR_LEN(p->len, 1);
+ binder_len = *(p->data);
+
+ if (binder_len == 0)
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+
+ p->data++;
+ p->obj_read++;
+
+ if (cur_index == psk_index) {
+ /* We found the binder with the supplied index */
+ binder_out->data = gnutls_malloc(binder_len);
+ if (!binder_out->data)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ binder_out->size = binder_len;
+ DECR_LEN(p->len, binder_len);
+ memcpy(binder_out->data, p->data, binder_len);
+ p->data += binder_len;
+ p->obj_read += binder_len;
+
+ binder_found = 1;
+ } else {
+ /* Not our binder - continue to the next one */
+ DECR_LEN(p->len, binder_len);
+ p->data += binder_len;
+ p->obj_read += binder_len;
+
+ cur_index++;
+ }
+ }
+
+ if (binder_found)
+ return 0;
+ else
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+}
diff --git a/lib/tls13/psk_ext_parser.h b/lib/tls13/psk_ext_parser.h
new file mode 100644
index 0000000000..14f3c89186
--- /dev/null
+++ b/lib/tls13/psk_ext_parser.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 Free Software Foundation, Inc.
+ *
+ * Author: Ander Juaristi
+ *
+ * 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/>
+ *
+ */
+
+#ifndef PSK_PARSER_H
+#define PSK_PARSER_H
+#include "gnutls_int.h"
+
+struct psk_ext_parser_st {
+ unsigned char *data;
+ ssize_t len;
+ size_t obj_len;
+ size_t obj_read;
+ int next_index;
+};
+
+typedef struct psk_ext_parser_st psk_ext_parser_st;
+
+struct psk_st {
+ gnutls_datum_t identity;
+ uint32_t ob_ticket_age;
+};
+
+int _gnutls13_psk_ext_parser_init(psk_ext_parser_st *p,
+ const unsigned char *data, size_t len);
+int _gnutls13_psk_ext_parser_next_psk(psk_ext_parser_st *p, struct psk_st *psk);
+int _gnutls13_psk_ext_parser_find_binder(psk_ext_parser_st *p, int psk_index,
+ gnutls_datum_t *binder_out);
+
+#endif