summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@gnutls.org>2018-04-06 11:36:58 +0000
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2018-04-06 11:36:58 +0000
commit5242dc17e855eb29e0e373bbf1490dae7a6a667c (patch)
treebb77ca61d788f6f7754083b603a6122b75c53196
parenta046665a384a728253ad94122dfcbd25a52478c2 (diff)
parent7d60d1e971ac8412b2592666a121008987e23f1e (diff)
downloadgnutls-5242dc17e855eb29e0e373bbf1490dae7a6a667c.tar.gz
Merge branch 'tmp-psk-tls1.3' into 'master'
TLS1.3: add support for PSK key exchange Closes #414 and #125 See merge request gnutls/gnutls!615
-rw-r--r--doc/cha-gtls-app.texi4
-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.c35
-rw-r--r--lib/auth/psk.h11
-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/dumbfw.c8
-rw-r--r--lib/ext/key_share.c19
-rw-r--r--lib/ext/pre_shared_key.c478
-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/libgnutls.map6
-rw-r--r--lib/priority.c42
-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.c150
-rw-r--r--lib/tls13/psk_ext_parser.h52
-rw-r--r--tests/Makefile.am4
-rwxr-xr-xtests/fastopen.sh13
-rw-r--r--tests/psk-file.c151
-rw-r--r--tests/pskself.c36
-rw-r--r--tests/tls13/no-psk-exts.c258
-rw-r--r--tests/tls13/psk-ext.c170
42 files changed, 2015 insertions, 185 deletions
diff --git a/doc/cha-gtls-app.texi b/doc/cha-gtls-app.texi
index 6575120756..655046c917 100644
--- a/doc/cha-gtls-app.texi
+++ b/doc/cha-gtls-app.texi
@@ -1268,7 +1268,9 @@ GROUP-SECP256R1, GROUP-SECP384R1, GROUP-SECP521R1, GROUP-X25519,
GROUP-FFDHE2048, GROUP-FFDHE3072, GROUP-FFDHE4096 and GROUP-FFDHE8192.
Groups include both elliptic curve groups, e.g., SECP256R1, as well as
finite field groups such as FFDHE2048. Catch all which enables all groups
-from NORMAL priority is GROUP-ALL.
+from NORMAL priority is GROUP-ALL. The helper keywords GROUP-DH-ALL and
+GROUP-EC-ALL are also available, restricting the groups to finite fields
+(DH) and elliptic curves.
@item Certificate type @tab
The only option currently is CTYPE-X509. Catch all is CTYPE-ALL.
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..6968bb8057 100644
--- a/lib/auth/psk.c
+++ b/lib/auth/psk.c
@@ -26,7 +26,6 @@
#include "errors.h"
#include "auth.h"
-#include "auth.h"
#include "debug.h"
#include "num.h"
#include <auth/psk.h>
@@ -111,40 +110,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..3d204bc8c8 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 */
@@ -59,11 +63,10 @@ typedef struct psk_auth_info_st {
char hint[MAX_USERNAME_SIZE + 1];
} *psk_auth_info_t;
+typedef struct psk_auth_info_st psk_auth_info_st;
#ifdef ENABLE_PSK
-typedef struct psk_auth_info_st psk_auth_info_st;
-
int
_gnutls_set_psk_session_key(gnutls_session_t session, gnutls_datum_t * key,
gnutls_datum_t * psk2);
@@ -71,10 +74,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/dumbfw.c b/lib/ext/dumbfw.c
index 7faff65693..4e56192337 100644
--- a/lib/ext/dumbfw.c
+++ b/lib/ext/dumbfw.c
@@ -1,5 +1,6 @@
/*
- * Copyright (C) 2013 Nikos Mavrogiannopoulos
+ * Copyright (C) 2013-2018 Nikos Mavrogiannopoulos
+ * Copyright (C) 2018 Red Hat, Inc.
*
* This file is part of GnuTLS.
*
@@ -57,15 +58,16 @@ _gnutls_dumbfw_send_params(gnutls_session_t session,
int total_size = 0, ret;
uint8_t pad[257];
unsigned pad_size;
+ ssize_t len = extdata->length - sizeof(mbuffer_st);
if (session->security_parameters.entity == GNUTLS_SERVER ||
session->internals.dumbfw == 0 ||
IS_DTLS(session) != 0 ||
- (extdata->length < 256 || extdata->length >= 512)) {
+ (len < 256 || len >= 512)) {
return 0;
} else {
/* 256 <= extdata->length < 512 */
- pad_size = 512 - extdata->length;
+ pad_size = 512 - len;
memset(pad, 0, pad_size);
ret =
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..d4ea982cbb
--- /dev/null
+++ b/lib/ext/pre_shared_key.c
@@ -0,0 +1,478 @@
+/*
+ * 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;
+ psk_auth_info_t info;
+
+ ret = _gnutls13_psk_ext_parser_init(&psk_parser, data, len);
+ if (ret < 0) {
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) /* No PSKs advertised by client */
+ return 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))
+ return 0;
+
+ /* 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)
+ return gnutls_assert_val(ret);
+
+ if (_gnutls_mac_get_algo_len(prf) != binder_recvd.size ||
+ safe_memcmp(binder_value, binder_recvd.data, binder_recvd.size)) {
+ gnutls_free(key.data);
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ }
+
+ 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);
+ }
+
+ /* save the username in psk_auth_info to make it available
+ * using gnutls_psk_server_get_username() */
+ if (psk.ob_ticket_age == 0) {
+ if (psk.identity.size >= sizeof(info->username))
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+
+ ret = _gnutls_auth_info_set(session, GNUTLS_CRD_PSK, sizeof(psk_auth_info_st), 1);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ info = _gnutls_get_auth_info(session, GNUTLS_CRD_PSK);
+ assert(info != NULL);
+
+ memcpy(info->username, psk.identity.data, psk.identity.size);
+ info->username[psk.identity.size] = 0;
+ }
+
+ 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;
+
+ return 0;
+}
+
+/*
+ * 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/libgnutls.map b/lib/libgnutls.map
index 07aaf714cc..45aa9b9431 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -1218,6 +1218,7 @@ GNUTLS_3_6_3
} GNUTLS_3_6_2;
GNUTLS_FIPS140_3_4 {
+ global:
gnutls_cipher_self_test;
gnutls_pk_self_test;
gnutls_mac_self_test;
@@ -1256,6 +1257,11 @@ GNUTLS_PRIVATE_3_4 {
_gnutls_digest_exists;
_gnutls_log;
+ # PSK extension parser unit test
+ _gnutls13_psk_ext_parser_init;
+ _gnutls13_psk_ext_parser_next_psk;
+ _gnutls13_psk_ext_parser_find_binder;
+
# Internal symbols needed by gnutls-cli-debug:
_gnutls_rsa_pms_set_version;
_gnutls_record_set_default_version;
diff --git a/lib/priority.c b/lib/priority.c
index 65b3dd3d93..fef7d5f9ba 100644
--- a/lib/priority.c
+++ b/lib/priority.c
@@ -105,6 +105,22 @@ static void _clear_given_priorities(priority_st * st, const int *list)
}
}
+static const int _supported_groups_dh[] = {
+ GNUTLS_GROUP_FFDHE2048,
+ GNUTLS_GROUP_FFDHE3072,
+ GNUTLS_GROUP_FFDHE4096,
+ GNUTLS_GROUP_FFDHE8192,
+ 0
+};
+
+static const int _supported_groups_ecdh[] = {
+ GNUTLS_GROUP_SECP256R1,
+ GNUTLS_GROUP_SECP384R1,
+ GNUTLS_GROUP_SECP521R1,
+ GNUTLS_GROUP_X25519, /* draft-ietf-tls-rfc4492bis */
+ 0
+};
+
static const int _supported_groups_normal[] = {
GNUTLS_GROUP_SECP256R1,
GNUTLS_GROUP_SECP384R1,
@@ -1177,6 +1193,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 +1230,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 +1342,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;
@@ -1575,6 +1601,18 @@ gnutls_priority_init(gnutls_priority_t * priority_cache,
bulk_fn(&(*priority_cache)->
_supported_ecc,
supported_groups_normal);
+ } else if (strncasecmp
+ (&broken_list[i][1], "GROUP-DH-ALL",
+ 12) == 0) {
+ bulk_given_fn(&(*priority_cache)->
+ _supported_ecc,
+ _supported_groups_dh);
+ } else if (strncasecmp
+ (&broken_list[i][1], "GROUP-EC-ALL",
+ 12) == 0) {
+ bulk_given_fn(&(*priority_cache)->
+ _supported_ecc,
+ _supported_groups_ecdh);
} else {
if ((algo =
gnutls_group_get_id
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..9f4087773d
--- /dev/null
+++ b/lib/tls13/psk_ext_parser.c
@@ -0,0 +1,150 @@
+/*
+ * 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 "tls13/psk_ext_parser.h"
+
+/* Returns GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE when no identities
+ * are present, or >= 0, on success.
+ */
+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));
+
+ DECR_LEN(len, 2);
+ identities_len = _gnutls_read_uint16(data);
+ data += 2;
+
+ if (identities_len == 0)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ p->id_len = identities_len;
+ p->data = (unsigned char *) data;
+ p->len = len;
+
+ DECR_LEN(len, p->id_len);
+ data += p->id_len;
+
+ DECR_LEN(len, 2);
+ p->binder_len = _gnutls_read_uint16(data);
+
+ p->binder_data = p->data + p->id_len + 2;
+ DECR_LEN(len, p->binder_len);
+
+ return 0;
+}
+
+int _gnutls13_psk_ext_parser_next_psk(psk_ext_parser_st *p, struct psk_st *psk)
+{
+ if (p->id_read >= p->id_len)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+
+ /* Read a PskIdentity structure */
+ DECR_LEN(p->len, 2);
+ psk->identity.size = _gnutls_read_uint16(p->data);
+ if (psk->identity.size == 0)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+
+ p->data += 2;
+ p->id_read += 2;
+
+ psk->identity.data = (void*)p->data;
+
+ DECR_LEN(p->len, psk->identity.size);
+ p->data += psk->identity.size;
+ p->id_read += psk->identity.size;
+
+ DECR_LEN(p->len, 4);
+ psk->ob_ticket_age = _gnutls_read_uint32(p->data);
+
+ p->data += 4;
+ p->id_read += 4;
+
+ return p->next_index++;
+}
+
+/* Output is a pointer to data, which shouldn't be de-allocated. */
+int _gnutls13_psk_ext_parser_find_binder(psk_ext_parser_st *p, int psk_index,
+ gnutls_datum_t *binder_out)
+{
+ uint8_t binder_len;
+ int cur_index = 0, binder_found = 0;
+ ssize_t len;
+ const uint8_t *data;
+ ssize_t read_data = 0;
+
+ if (p == NULL || psk_index < 0 || binder_out == NULL)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ if (p->id_len == 0)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ len = p->binder_len;
+ data = p->binder_data;
+
+ if (len == 0)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ /* Start traversing the binders */
+ while (!binder_found && len > 0) {
+ DECR_LEN(len, 1);
+ binder_len = *(data);
+
+ if (binder_len == 0)
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+
+ data++;
+ read_data++;
+
+ if (cur_index == psk_index) {
+ /* We found the binder with the supplied index */
+ DECR_LEN(len, binder_len);
+ binder_out->data = (void*)data;
+ binder_out->size = binder_len;
+
+ data += binder_len;
+ read_data += binder_len;
+
+ binder_found = 1;
+ } else {
+ /* Not our binder - continue to the next one */
+ DECR_LEN(len, binder_len);
+ data += binder_len;
+ read_data += 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..2c6ab603ae
--- /dev/null
+++ b/lib/tls13/psk_ext_parser.h
@@ -0,0 +1,52 @@
+/*
+ * 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/gnutls.h>
+
+struct psk_ext_parser_st {
+ const unsigned char *data;
+ ssize_t len;
+ size_t id_len;
+ size_t id_read;
+ int next_index;
+
+ const unsigned char *binder_data;
+ ssize_t binder_len;
+};
+
+typedef struct psk_ext_parser_st psk_ext_parser_st;
+
+struct psk_st {
+ /* constant values */
+ 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
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 5abd976ff8..2536d3b8f1 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -108,6 +108,8 @@ ctests = tls13/supported_versions tls13/tls12-no-tls13-exts \
tls12-rollback-detection tls11-rollback-detection \
tls12-check-rollback-val tls11-check-rollback-val tls13/hello_random_value
+ctests += tls13/psk-ext
+
ctests += tls13/key_update
ctests += tls13/key_limits
@@ -120,6 +122,8 @@ ctests += tls13/change_cipher_spec
ctests += tls13-cipher-neg
+ctests += tls13/no-psk-exts
+
ctests += mini-record-2 simple gnutls_hmac_fast set_pkcs12_cred cert certuniqueid tls-neg-ext-key \
mpi certificate_set_x509_crl dn parse_ca x509-dn x509-dn-decode record-sizes \
hostname-check cve-2008-4989 pkcs12_s2k chainverify record-sizes-range \
diff --git a/tests/fastopen.sh b/tests/fastopen.sh
index 5d58b35028..87f3b24bba 100755
--- a/tests/fastopen.sh
+++ b/tests/fastopen.sh
@@ -48,13 +48,20 @@ SERV="${SERV} -q"
echo "Checking Fast open"
+KEY1=${srcdir}/../doc/credentials/x509/key-rsa.pem
+CERT1=${srcdir}/../doc/credentials/x509/cert-rsa.pem
+CA1=${srcdir}/../doc/credentials/x509/ca.pem
+
eval "${GETPORT}"
-launch_server $$ --echo --priority "NORMAL:+ANON-ECDH"
+launch_server $$ --echo --x509keyfile ${KEY1} --x509certfile ${CERT1}
PID=$!
wait_server ${PID}
-${VALGRIND} "${CLI}" -p "${PORT}" 127.0.0.1 --fastopen --rehandshake --priority "NORMAL:+ANON-ECDH:+ANON-DH" </dev/null >/dev/null || \
- fail ${PID} "1. rehandshake should have succeeded!"
+${VALGRIND} "${CLI}" -p "${PORT}" localhost --fastopen --priority "NORMAL:-VERS-ALL:+VERS-TLS1.2" --x509cafile ${CA1} </dev/null || \
+ fail ${PID} "1. TLS1.2 handshake should have succeeded!"
+
+${VALGRIND} "${CLI}" -p "${PORT}" localhost --fastopen --x509cafile ${CA1} </dev/null || \
+ fail ${PID} "2. handshake should have succeeded!"
kill ${PID}
diff --git a/tests/psk-file.c b/tests/psk-file.c
index ee6f8c533e..a6df3f0467 100644
--- a/tests/psk-file.c
+++ b/tests/psk-file.c
@@ -65,14 +65,13 @@ static void tls_log_func(int level, const char *str)
#define MAX_BUF 1024
#define MSG "Hello TLS"
-static void client(int sd, const char *prio, const char *user, unsigned expect_fail)
+static void client(int sd, const char *prio, const char *user, const gnutls_datum_t *key,
+ unsigned expect_hint, int expect_fail, int exp_kx)
{
- int ret, ii;
+ int ret, ii, kx;
gnutls_session_t session;
char buffer[MAX_BUF + 1];
gnutls_psk_client_credentials_t pskcred;
- /* Need to enable anonymous KX specifically. */
- const gnutls_datum_t key = { (void *) "9e32cf7786321a828ef7668f09fb35db", 32 };
const char *hint;
global_init();
@@ -83,7 +82,7 @@ static void client(int sd, const char *prio, const char *user, unsigned expect_f
side = "client";
gnutls_psk_allocate_client_credentials(&pskcred);
- gnutls_psk_set_client_credentials(pskcred, user, &key,
+ gnutls_psk_set_client_credentials(pskcred, user, key,
GNUTLS_PSK_KEY_HEX);
/* Initialize TLS session
@@ -106,7 +105,10 @@ static void client(int sd, const char *prio, const char *user, unsigned expect_f
if (ret < 0) {
if (!expect_fail)
fail("client: Handshake failed\n");
- gnutls_perror(ret);
+ if (ret != expect_fail) {
+ fail("expected cli error %d (%s), got %d (%s)\n", expect_fail, gnutls_strerror(expect_fail),
+ ret, gnutls_strerror(ret));
+ }
goto end;
} else {
if (debug)
@@ -114,10 +116,12 @@ static void client(int sd, const char *prio, const char *user, unsigned expect_f
}
/* check the hint */
- hint = gnutls_psk_client_get_hint(session);
- if (hint == NULL || strcmp(hint, "hint") != 0) {
- fail("client: hint is not the expected: %s\n", gnutls_psk_client_get_hint(session));
- goto end;
+ if (expect_hint) {
+ hint = gnutls_psk_client_get_hint(session);
+ if (hint == NULL || strcmp(hint, "hint") != 0) {
+ fail("client: hint is not the expected: %s\n", gnutls_psk_client_get_hint(session));
+ goto end;
+ }
}
gnutls_record_send(session, MSG, strlen(MSG));
@@ -133,6 +137,8 @@ static void client(int sd, const char *prio, const char *user, unsigned expect_f
goto end;
}
+ kx = gnutls_kx_get(session);
+
if (debug) {
printf("- Received %d bytes: ", ret);
for (ii = 0; ii < ret; ii++) {
@@ -143,6 +149,15 @@ static void client(int sd, const char *prio, const char *user, unsigned expect_f
gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ if (expect_fail)
+ fail("client: expected failure but connection succeeded!\n");
+
+ if (exp_kx && kx != exp_kx) {
+ fail("client: expected key exchange %s, but got %s\n",
+ gnutls_kx_get_name(exp_kx),
+ gnutls_kx_get_name(kx));
+ }
+
end:
close(sd);
@@ -159,13 +174,15 @@ static void client(int sd, const char *prio, const char *user, unsigned expect_f
#define MAX_BUF 1024
-static void server(int sd, const char *prio, const char *user, unsigned expect_fail)
+static void server(int sd, const char *prio, const char *user, int expect_fail, int exp_kx)
{
gnutls_psk_server_credentials_t server_pskcred;
- int ret;
+ int ret, kx;
gnutls_session_t session;
+ const char *pskid;
char buffer[MAX_BUF + 1];
char *psk_file = getenv("PSK_FILE");
+ char *desc;
/* this must be called once in the program
*/
@@ -197,17 +214,22 @@ static void server(int sd, const char *prio, const char *user, unsigned expect_f
gnutls_transport_set_int(session, sd);
ret = gnutls_handshake(session);
if (ret < 0) {
- close(sd);
- gnutls_deinit(session);
- gnutls_psk_free_server_credentials(server_pskcred);
+ gnutls_alert_send_appropriate(session, ret);
if (expect_fail) {
- success("server: Handshake has failed - expected (%s)\n\n",
- gnutls_strerror(ret));
+ if (ret != expect_fail) {
+ fail("expected error %d (%s), got %d (%s)\n", expect_fail,
+ gnutls_strerror(expect_fail),
+ ret, gnutls_strerror(ret));
+ }
+
+ if (debug)
+ success("server: Handshake has failed - expected (%s)\n\n",
+ gnutls_strerror(ret));
} else {
fail("server: Handshake has failed (%s)\n\n",
gnutls_strerror(ret));
}
- return;
+ goto end;
}
if (debug)
success("server: Handshake was completed\n");
@@ -235,10 +257,32 @@ static void server(int sd, const char *prio, const char *user, unsigned expect_f
strlen(buffer));
}
}
+
+ kx = gnutls_kx_get(session);
+
+ desc = gnutls_session_get_desc(session);
+ success(" - connected with: %s\n", desc);
+ gnutls_free(desc);
/* do not wait for the peer to close the connection.
*/
gnutls_bye(session, GNUTLS_SHUT_WR);
+ if (expect_fail)
+ fail("server: expected failure but connection succeeded!\n");
+
+ pskid = gnutls_psk_server_get_username(session);
+ if (pskid == NULL || strcmp(pskid, user) != 0) {
+ fail("server: username (%s), does not match expected (%s)\n",
+ pskid, user);
+ }
+
+ if (exp_kx && kx != exp_kx) {
+ fail("server: expected key exchange %s, but got %s\n",
+ gnutls_kx_get_name(exp_kx),
+ gnutls_kx_get_name(kx));
+ }
+
+ end:
close(sd);
gnutls_deinit(session);
@@ -251,13 +295,18 @@ static void server(int sd, const char *prio, const char *user, unsigned expect_f
}
static
-void run_test(const char *prio, const char *user, unsigned expect_fail)
+void run_test2(const char *prio, const char *sprio, const char *user, const gnutls_datum_t *key,
+ unsigned expect_hint, int exp_kx, int expect_fail_cli, int expect_fail_serv)
{
pid_t child;
int err;
int sockets[2];
- success("trying %s / user:%s\n", prio, user);
+ if (expect_fail_serv || expect_fail_cli) {
+ success("ntest %s (user:%s)\n", prio, user);
+ } else {
+ success("test %s (user:%s)\n", prio, user);
+ }
err = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
if (err == -1) {
@@ -277,22 +326,72 @@ void run_test(const char *prio, const char *user, unsigned expect_fail)
close(sockets[1]);
int status;
/* parent */
- server(sockets[0], prio, user, expect_fail);
+ server(sockets[0], sprio?sprio:prio, user, expect_fail_serv, exp_kx);
wait(&status);
check_wait_status(status);
} else {
close(sockets[0]);
- client(sockets[1], prio, user, expect_fail);
+ client(sockets[1], prio, user, key, expect_hint, expect_fail_cli, exp_kx);
exit(0);
}
}
+static
+void run_test_ok(const char *prio, const char *user, const gnutls_datum_t *key, unsigned expect_hint, int expect_fail)
+{
+ return run_test2(prio, NULL, user, key, expect_hint, GNUTLS_KX_PSK, expect_fail, expect_fail);
+}
+
+static
+void run_ectest_ok(const char *prio, const char *user, const gnutls_datum_t *key, unsigned expect_hint, int expect_fail)
+{
+ return run_test2(prio, NULL, user, key, expect_hint, GNUTLS_KX_ECDHE_PSK, expect_fail, expect_fail);
+}
+
+static
+void run_dhtest_ok(const char *prio, const char *user, const gnutls_datum_t *key, unsigned expect_hint, int expect_fail)
+{
+ return run_test2(prio, NULL, user, key, expect_hint, GNUTLS_KX_DHE_PSK, expect_fail, expect_fail);
+}
+
void doit(void)
{
- run_test("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+PSK", "jas", 0);
- run_test("NORMAL:-KX-ALL:+PSK", "jas", 0);
- run_test("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+PSK", "non-hex", 1);
- run_test("NORMAL:-KX-ALL:+PSK", "non-hex", 1);
+ const gnutls_datum_t key = { (void *) "9e32cf7786321a828ef7668f09fb35db", 32 };
+ const gnutls_datum_t wrong_key = { (void *) "9e31cf7786321a828ef7668f09fb35db", 32 };
+
+ run_test_ok("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+PSK", "jas", &key, 1, 0);
+ run_dhtest_ok("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+DHE-PSK", "jas", &key, 1, 0);
+ run_ectest_ok("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+ECDHE-PSK", "jas", &key, 1, 0);
+ run_test2("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+PSK", NULL, "unknown", &key, 1, 0, GNUTLS_E_FATAL_ALERT_RECEIVED, GNUTLS_E_DECRYPTION_FAILED);
+ run_test2("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+PSK", NULL, "jas", &wrong_key, 1, 0, GNUTLS_E_FATAL_ALERT_RECEIVED, GNUTLS_E_DECRYPTION_FAILED);
+ run_test2("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+PSK", NULL, "non-hex", &key, 1, 0, GNUTLS_E_FATAL_ALERT_RECEIVED, GNUTLS_E_KEYFILE_ERROR);
+
+ run_test_ok("NORMAL:-KX-ALL:+PSK", "jas", &key, 1, 0);
+ run_test2("NORMAL:+PSK", NULL, "unknown", &key, 1, 0, GNUTLS_E_FATAL_ALERT_RECEIVED, GNUTLS_E_DECRYPTION_FAILED);
+ run_test2("NORMAL:+PSK", NULL, "jas", &wrong_key, 1, 0, GNUTLS_E_FATAL_ALERT_RECEIVED, GNUTLS_E_DECRYPTION_FAILED);
+ run_test2("NORMAL:-KX-ALL:+PSK", NULL, "non-hex", &key, 1, 0, GNUTLS_E_FATAL_ALERT_RECEIVED, GNUTLS_E_KEYFILE_ERROR);
+
+ run_dhtest_ok("NORMAL:-VERS-ALL:+VERS-TLS1.3:+DHE-PSK:-GROUP-EC-ALL", "jas", &key, 0, 0);
+ run_test_ok("NORMAL:-VERS-ALL:+VERS-TLS1.3:+PSK", "jas", &key, 0, 0);
+
+ /* test priorities of DHE-PSK and PSK */
+ run_ectest_ok("NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+DHE-PSK:+PSK:-GROUP-DH-ALL", "jas", &key, 0, 0);
+ run_test_ok("NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+PSK:+DHE-PSK:-GROUP-DH-ALL", "jas", &key, 0, 0);
+ run_test2("NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+DHE-PSK:+PSK:-GROUP-DH-ALL",
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+PSK:+DHE-PSK:%SERVER_PRECEDENCE:-GROUP-DH-ALL",
+ "jas", &key, 0, GNUTLS_KX_PSK, 0, 0);
+ /* try with PRF that doesn't match binder (SHA256) */
+ run_test2("NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-256-GCM:+PSK:+DHE-PSK", NULL, "jas", &key, 0, 0, GNUTLS_E_FATAL_ALERT_RECEIVED, GNUTLS_E_INSUFFICIENT_SECURITY);
+ /* try with no groups and PSK */
+ run_test_ok("NORMAL:-VERS-ALL:+VERS-TLS1.3:+PSK:-GROUP-ALL", "jas", &key, 0, 0);
+ /* try without any groups but DHE-PSK */
+ run_test2("NORMAL:-VERS-ALL:+VERS-TLS1.3:+DHE-PSK:-GROUP-ALL", "NORMAL:-VERS-ALL:+VERS-TLS1.3:+DHE-PSK:+PSK", "jas", &key, 0, 0, GNUTLS_E_FATAL_ALERT_RECEIVED, GNUTLS_E_NO_COMMON_KEY_SHARE);
+ run_test2("NORMAL:-VERS-ALL:+VERS-TLS1.3:+DHE-PSK:-GROUP-ALL", "NORMAL:-VERS-ALL:+VERS-TLS1.3:+DHE-PSK:+PSK:-GROUP-ALL", "jas", &key, 0, 0, GNUTLS_E_FATAL_ALERT_RECEIVED, GNUTLS_E_NO_COMMON_KEY_SHARE);
+
+ /* if user invalid we continue without PSK */
+ run_test2("NORMAL:-VERS-ALL:+VERS-TLS1.3:+PSK:+DHE-PSK", NULL, "non-hex", &key, 0, 0, GNUTLS_E_FATAL_ALERT_RECEIVED, GNUTLS_E_INSUFFICIENT_CREDENTIALS);
+ run_test2("NORMAL:-VERS-ALL:+VERS-TLS1.3:+PSK:+DHE-PSK", NULL, "unknown", &key, 0, 0, GNUTLS_E_FATAL_ALERT_RECEIVED, GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ run_test2("NORMAL:-VERS-ALL:+VERS-TLS1.3:+PSK:+DHE-PSK", NULL, "jas", &wrong_key, 0, 0, GNUTLS_E_FATAL_ALERT_RECEIVED, GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
}
#endif /* _WIN32 */
diff --git a/tests/pskself.c b/tests/pskself.c
index 293cb59561..9580abe86d 100644
--- a/tests/pskself.c
+++ b/tests/pskself.c
@@ -64,7 +64,7 @@ static void tls_log_func(int level, const char *str)
#define MAX_BUF 1024
#define MSG "Hello TLS"
-static void client(int sd, const char *prio)
+static void client(int sd, const char *prio, unsigned exp_hint)
{
int ret, ii;
gnutls_session_t session;
@@ -112,10 +112,12 @@ static void client(int sd, const char *prio)
}
/* check the hint */
- hint = gnutls_psk_client_get_hint(session);
- if (hint == NULL || strcmp(hint, "hint") != 0) {
- fail("client: hint is not the expected: %s\n", gnutls_psk_client_get_hint(session));
- goto end;
+ if (exp_hint) {
+ hint = gnutls_psk_client_get_hint(session);
+ if (hint == NULL || strcmp(hint, "hint") != 0) {
+ fail("client: hint is not the expected: %s\n", gnutls_psk_client_get_hint(session));
+ goto end;
+ }
}
gnutls_record_send(session, MSG, strlen(MSG));
@@ -274,12 +276,14 @@ char buffer[MAX_BUF + 1];
}
static
-void run_test(const char *prio)
+void run_test(const char *prio, unsigned exp_hint)
{
pid_t child;
int err;
int sockets[2];
+ success("trying with %s\n", prio);
+
err = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
if (err == -1) {
perror("socketpair");
@@ -303,7 +307,7 @@ void run_test(const char *prio)
check_wait_status(status);
} else {
close(sockets[0]);
- client(sockets[1], prio);
+ client(sockets[1], prio, exp_hint);
exit(0);
}
}
@@ -312,13 +316,19 @@ void doit(void)
{
generate_dh_params();
- run_test("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+PSK");
- run_test("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+ECDHE-PSK");
- run_test("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+DHE-PSK");
+ run_test("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+PSK", 1);
+ run_test("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+ECDHE-PSK", 1);
+ run_test("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+DHE-PSK", 1);
+
+ run_test("NORMAL:-VERS-ALL:+VERS-TLS1.3:+PSK", 0);
+ run_test("NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-FFDHE2048:+DHE-PSK", 0);
+ run_test("NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP256R1:+ECDHE-PSK", 0);
+ /* the follow should work once we support PSK without DH */
+ run_test("NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+PSK", 0);
- run_test("NORMAL:-KX-ALL:+PSK");
- run_test("NORMAL:-KX-ALL:+ECDHE-PSK");
- run_test("NORMAL:-KX-ALL:+DHE-PSK");
+ run_test("NORMAL:-KX-ALL:+PSK", 1);
+ run_test("NORMAL:-KX-ALL:+ECDHE-PSK", 1);
+ run_test("NORMAL:-KX-ALL:+DHE-PSK", 1);
gnutls_dh_params_deinit(dh_params);
}
diff --git a/tests/tls13/no-psk-exts.c b/tests/tls13/no-psk-exts.c
new file mode 100644
index 0000000000..b70c667822
--- /dev/null
+++ b/tests/tls13/no-psk-exts.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2017-2018 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS 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
+ * 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/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main()
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+#include <signal.h>
+
+#include "cert-common.h"
+#include "tls13/ext-parse.h"
+#include "utils.h"
+
+/* This program tests whether a connection without the PSK priority
+ * options, will contain PSK extensions */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+#define MAX_BUF 1024
+
+static void client(int fd)
+{
+ int ret;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_psk_client_credentials_t psk_cred;
+ gnutls_session_t session;
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_psk_allocate_client_credentials(&psk_cred);
+
+ /* Initialize TLS session
+ */
+ gnutls_init(&session, GNUTLS_CLIENT);
+
+ gnutls_handshake_set_timeout(session, 20 * 1000);
+
+ ret = gnutls_priority_set_direct(session, "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2:+VERS-TLS1.0", NULL);
+ if (ret < 0)
+ fail("cannot set TLS 1.3 priorities\n");
+
+ /* put the anonymous credentials to the current session
+ */
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+ gnutls_credentials_set(session, GNUTLS_CRD_PSK, psk_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ /* Perform the TLS handshake
+ */
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ /* try if gnutls_reauth() would fail as expected */
+ ret = gnutls_reauth(session, 0);
+ if (ret != GNUTLS_E_INVALID_REQUEST)
+ fail("server: gnutls_reauth did not fail as expected: %s", gnutls_strerror(ret));
+
+ close(fd);
+
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+ gnutls_psk_free_client_credentials(psk_cred);
+
+ gnutls_global_deinit();
+}
+
+static unsigned server_hello_ok = 0;
+
+#define TLS_EXT_PSK 41
+#define TLS_EXT_PSK_KE 45
+
+static int hellos_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ if (htype == GNUTLS_HANDSHAKE_SERVER_HELLO && post == GNUTLS_HOOK_POST) {
+ if (find_server_extension(msg, TLS_EXT_PSK_KE, NULL, NULL)) {
+ fail("PSK KE extension seen on server (illegal)!\n");
+ }
+ if (find_server_extension(msg, TLS_EXT_PSK, NULL, NULL)) {
+ fail("PSK extension seen on server (illegal)!\n");
+ }
+ server_hello_ok = 1;
+
+ return GNUTLS_E_INTERRUPTED;
+ }
+
+ if (htype != GNUTLS_HANDSHAKE_CLIENT_HELLO || post != GNUTLS_HOOK_PRE)
+ return 0;
+
+ if (find_client_extension(msg, TLS_EXT_PSK, NULL, NULL))
+ fail("PSK extension seen in client hello with no PSK!\n");
+
+ if (find_client_extension(msg, TLS_EXT_PSK_KE, NULL, NULL))
+ fail("PSK extension seen in client hello with no PSK!\n");
+
+ return 0;
+}
+
+static void server(int fd)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+
+ /* this must be called once in the program
+ */
+ global_init();
+ memset(buffer, 0, sizeof(buffer));
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM);
+
+ gnutls_init(&session, GNUTLS_SERVER);
+
+ gnutls_handshake_set_timeout(session, 20 * 1000);
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_ANY,
+ GNUTLS_HOOK_BOTH,
+ hellos_callback);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ gnutls_priority_set_direct(session, "NORMAL:+VERS-TLS1.3", NULL);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ if (ret == GNUTLS_E_INTERRUPTED) { /* expected */
+ break;
+ }
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+
+ if (server_hello_ok == 0) {
+ fail("server: did not verify the server hello contents\n");
+ }
+
+ close(fd);
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: client/server hello were verified\n");
+}
+
+static void ch_handler(int sig)
+{
+ int status;
+ wait(&status);
+ check_wait_status(status);
+ return;
+}
+
+void doit(void)
+{
+ int fd[2];
+ int ret;
+ pid_t child;
+
+ signal(SIGCHLD, ch_handler);
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ /* parent */
+ close(fd[1]);
+ server(fd[0]);
+ kill(child, SIGTERM);
+ } else {
+ close(fd[0]);
+ client(fd[1]);
+ exit(0);
+ }
+}
+
+#endif /* _WIN32 */
diff --git a/tests/tls13/psk-ext.c b/tests/tls13/psk-ext.c
new file mode 100644
index 0000000000..3d5c5956ed
--- /dev/null
+++ b/tests/tls13/psk-ext.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2016, 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS 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
+ * 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/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string.h>
+#include <gnutls/gnutls.h>
+#include <stdint.h>
+#include "../lib/tls13/psk_ext_parser.h"
+
+#include "utils.h"
+
+/* Tests the PSK-extension decoding part */
+
+static void decode(const char *test_name, const gnutls_datum_t *raw, const gnutls_datum_t *id,
+ const gnutls_datum_t *b, unsigned idx, int res)
+{
+ int ret;
+ psk_ext_parser_st p;
+ struct psk_st psk;
+ gnutls_datum_t binder;
+ unsigned found = 0;
+
+ ret = _gnutls13_psk_ext_parser_init(&p, raw->data, raw->size);
+ if (ret < 0) {
+ if (res == ret) /* expected */
+ return;
+ fail("%s: _gnutls13_psk_ext_parser_init: %d/%s\n", test_name, ret, gnutls_strerror(ret));
+ exit(1);
+ }
+
+ while ((ret = _gnutls13_psk_ext_parser_next_psk(&p, &psk)) >= 0) {
+ if (ret == (int)idx) {
+ if (psk.identity.size == id->size && memcmp(psk.identity.data, id->data, id->size) == 0) {
+ if (debug)
+ success("%s: found id\n", test_name);
+ found = 1;
+ break;
+ } else {
+ fail("%s: did not found identity on index %d\n", test_name, idx);
+ }
+ }
+ }
+
+ if (found == 0)
+ fail("%s: did not found identity!\n", test_name);
+
+ ret = _gnutls13_psk_ext_parser_find_binder(&p, idx, &binder);
+ if (ret < 0) {
+ fail("%s: could not extract binder: %s\n", test_name, gnutls_strerror(ret));
+ }
+
+ if (binder.size != b->size || memcmp(binder.data, b->data, b->size) != 0) {
+ hexprint(binder.data, binder.size);
+ fail("%s: did not match binder on index %d\n", test_name, idx);
+ }
+
+ return;
+}
+
+struct decode_tests_st {
+ const char *name;
+ gnutls_datum_t psk;
+ unsigned idx; /* the ID index */
+ gnutls_datum_t id;
+ gnutls_datum_t binder;
+ int res;
+};
+
+struct decode_tests_st decode_tests[] = {
+ {
+ .name = "single PSK",
+ .psk = { (unsigned char*)"\x00\x0a\x00\x04\x6e\x6d\x61\x76\x00\x00\x00\x00\x00\x21\x20\xc4\xda\xe5\x7e\x05\x59\xf7\xae\x9b\xba\x90\xd2\x6e\x12\x68\xf6\xc1\xc7\xb9\x7e\xdc\xed\x9e\x67\x4e\xa5\x91\x2d\x7c\xb4\xf0\xab", 47},
+ .id = { (unsigned char*)"nmav", 4 },
+ .binder = { (unsigned char*)"\xc4\xda\xe5\x7e\x05\x59\xf7\xae\x9b\xba\x90\xd2\x6e\x12\x68\xf6\xc1\xc7\xb9\x7e\xdc\xed\x9e\x67\x4e\xa5\x91\x2d\x7c\xb4\xf0\xab", 32 },
+ .idx = 0,
+ .res = 0
+ },
+ {
+ .name = "multiple psks id0",
+ .psk = { (unsigned char*)"\x00\x20\x00\x04\x70\x73\x6b\x31\x00\x00\x00\x00"
+ "\x00\x06\x70\x73\x6b\x69\x64\x00\x00\x00\x00\x00"
+ "\x00\x04\x74\x65\x73\x74\x00\x00\x00\x00\x00\x63"
+ "\x20\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x01\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x20\x71\x83\x89\x3d\xcc"
+ "\x46\xad\x83\x18\x98\x59\x46\x0b\xb2\x51\x24\x53"
+ "\x41\xb4\x35\x04\x22\x90\x02\xac\x5e\xc1\xe7\xbc"
+ "\xca\x52\x16", 135},
+ .id = { (unsigned char*)"psk1", 4 },
+ .binder = { (unsigned char*)"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32},
+ .idx = 0,
+ .res = 0
+ },
+ {
+ .name = "multiple psks id1",
+ .psk = { (unsigned char*)"\x00\x20\x00\x04\x70\x73\x6b\x31\x00\x00\x00\x00"
+ "\x00\x06\x70\x73\x6b\x69\x64\x00\x00\x00\x00\x00"
+ "\x00\x04\x74\x65\x73\x74\x00\x00\x00\x00\x00\x63"
+ "\x20\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x01\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x20\x71\x83\x89\x3d\xcc"
+ "\x46\xad\x83\x18\x98\x59\x46\x0b\xb2\x51\x24\x53"
+ "\x41\xb4\x35\x04\x22\x90\x02\xac\x5e\xc1\xe7\xbc"
+ "\xca\x52\x16", 135},
+ .id = { (unsigned char*)"pskid", 6 },
+ .binder = { (unsigned char*)"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32},
+ .idx = 1,
+ .res = 0
+ },
+ {
+ .name = "multiple psks id2",
+ .psk = { (unsigned char*)"\x00\x20\x00\x04\x70\x73\x6b\x31\x00\x00\x00\x00"
+ "\x00\x06\x70\x73\x6b\x69\x64\x00\x00\x00\x00\x00"
+ "\x00\x04\x74\x65\x73\x74\x00\x00\x00\x00\x00\x63"
+ "\x20\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x01\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x20\x71\x83\x89\x3d\xcc"
+ "\x46\xad\x83\x18\x98\x59\x46\x0b\xb2\x51\x24\x53"
+ "\x41\xb4\x35\x04\x22\x90\x02\xac\x5e\xc1\xe7\xbc"
+ "\xca\x52\x16", 135},
+ .id = { (unsigned char*)"test", 4 },
+ .binder = { (unsigned char*)"\x71\x83\x89\x3d\xcc\x46\xad\x83\x18\x98\x59\x46\x0b\xb2\x51\x24\x53\x41\xb4\x35\x04\x22\x90\x02\xac\x5e\xc1\xe7\xbc\xca\x52\x16", 32},
+ .idx = 2,
+ .res = 0
+ }
+};
+
+void doit(void)
+{
+ unsigned i;
+
+ for (i=0;i<sizeof(decode_tests)/sizeof(decode_tests[0]);i++) {
+ decode(decode_tests[i].name, &decode_tests[i].psk, &decode_tests[i].id,
+ &decode_tests[i].binder, decode_tests[i].idx, decode_tests[i].res);
+ }
+}
+