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