diff options
-rw-r--r-- | lib/algorithms/protocols.c | 1 | ||||
-rw-r--r-- | lib/crypto-backend.h | 4 | ||||
-rw-r--r-- | lib/errors.c | 2 | ||||
-rw-r--r-- | lib/ext/Makefile.am | 2 | ||||
-rw-r--r-- | lib/ext/key_share.c | 665 | ||||
-rw-r--r-- | lib/ext/key_share.h | 30 | ||||
-rw-r--r-- | lib/extensions.c | 2 | ||||
-rw-r--r-- | lib/gnutls_int.h | 10 | ||||
-rw-r--r-- | lib/handshake.c | 28 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 2 | ||||
-rw-r--r-- | lib/nettle/pk.c | 12 | ||||
-rw-r--r-- | lib/pk.h | 3 | ||||
-rw-r--r-- | lib/state.c | 5 |
13 files changed, 754 insertions, 12 deletions
diff --git a/lib/algorithms/protocols.c b/lib/algorithms/protocols.c index 74a41f981a..3937800cc3 100644 --- a/lib/algorithms/protocols.c +++ b/lib/algorithms/protocols.c @@ -102,6 +102,7 @@ static const version_entry_st sup_versions[] = { .obsolete = 0, .only_extension = 1, .post_handshake_auth = 1, + .key_shares = 1, .false_start = 0, /* doesn't make sense */ .tls_sig_sem = 1 }, diff --git a/lib/crypto-backend.h b/lib/crypto-backend.h index 75f4326da4..d57b1d1135 100644 --- a/lib/crypto-backend.h +++ b/lib/crypto-backend.h @@ -357,9 +357,11 @@ typedef struct gnutls_crypto_pk { int (*pk_fixup_private_params) (gnutls_pk_algorithm_t, gnutls_direction_t, gnutls_pk_params_st *); +#define PK_DERIVE_TLS13 1 int (*derive) (gnutls_pk_algorithm_t, gnutls_datum_t * out, const gnutls_pk_params_st * priv, - const gnutls_pk_params_st * pub); + const gnutls_pk_params_st * pub, + unsigned int flags); int (*curve_exists) (gnutls_ecc_curve_t); /* true/false */ } gnutls_crypto_pk_st; diff --git a/lib/errors.c b/lib/errors.c index b5213707a3..ea444c3e12 100644 --- a/lib/errors.c +++ b/lib/errors.c @@ -420,6 +420,8 @@ static const gnutls_error_entry error_entries[] = { GNUTLS_E_INCOMPATIBLE_SIG_WITH_KEY), ERROR_ENTRY(N_("One of the involved algorithms has insufficient security level."), GNUTLS_E_INSUFFICIENT_SECURITY), + ERROR_ENTRY(N_("No common key share with peer."), + GNUTLS_E_NO_COMMON_KEY_SHARE), {NULL, NULL, 0} }; diff --git a/lib/ext/Makefile.am b/lib/ext/Makefile.am index a97f204dce..211694778d 100644 --- a/lib/ext/Makefile.am +++ b/lib/ext/Makefile.am @@ -42,7 +42,7 @@ libgnutls_ext_la_SOURCES = max_record.c \ status_request.h status_request.c dumbfw.c dumbfw.h \ ext_master_secret.c ext_master_secret.h etm.h etm.c \ supported_versions.c supported_versions.h \ - post_handshake.c post_handshake.h + post_handshake.c post_handshake.h key_share.c key_share.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 new file mode 100644 index 0000000000..71593e83b5 --- /dev/null +++ b/lib/ext/key_share.c @@ -0,0 +1,665 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +/* This file contains the code the Key Share TLS 1.3 extension. + */ + +#include "gnutls_int.h" +#include "errors.h" +#include "num.h" +#include <ext/ecc.h> +#include <state.h> +#include <num.h> +#include <algorithms.h> +#include "auth/psk.h" +#include "auth/cert.h" +#include "auth/anon.h" +#include "../ecc.h" +#include "../algorithms.h" +#include "pk.h" + +static int key_share_recv_params(gnutls_session_t session, + const uint8_t * data, + size_t data_size); +static int key_share_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata); + +const extension_entry_st ext_mod_key_share = { + .name = "Key Share", + .type = GNUTLS_EXTENSION_KEY_SHARE, + .parse_type = _GNUTLS_EXT_TLS_POST_CS, + + .recv_func = key_share_recv_params, + .send_func = key_share_send_params, + .pack_func = NULL, + .unpack_func = NULL, + .deinit_func = NULL, + .cannot_be_overriden = 1 +}; + +/* + * Generates key exchange parameters, and stores them in + * session->key.kshare_*_params. + * + * struct { + * NamedGroup group; + * opaque key_exchange<1..2^16-1>; + * } KeyShareEntry; + * + */ +static int client_gen_key_share(gnutls_session_t session, const gnutls_group_entry_st *group, gnutls_buffer_st *extdata) +{ + gnutls_datum_t tmp = {NULL, 0}; + int ret; + + if (group->pk != GNUTLS_PK_EC && group->pk != GNUTLS_PK_ECDH_X25519 && + group->pk != GNUTLS_PK_DH) { + _gnutls_debug_log("Cannot send key share for group %s!\n", group->name); + return GNUTLS_E_INT_RET_0; + } + + _gnutls_handshake_log("EXT[%p]: sending key share for %s\n", session, group->name); + + ret = + _gnutls_buffer_append_prefix(extdata, 16, group->tls_id); + if (ret < 0) + return gnutls_assert_val(ret); + + if (group->pk == GNUTLS_PK_EC) { + gnutls_pk_params_release(&session->key.kshare_ecdh_params); + gnutls_pk_params_init(&session->key.kshare_ecdh_params); + + ret = _gnutls_pk_generate_keys(group->pk, group->curve, + &session->key.kshare_ecdh_params, 1); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_ecc_ansi_x962_export(group->curve, + session->key.kshare_ecdh_params.params[ECC_X], + session->key.kshare_ecdh_params.params[ECC_Y], + &tmp); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + _gnutls_buffer_append_data_prefix(extdata, 16, tmp.data, tmp.size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + session->key.kshare_ecdh_params.algo = group->pk; + session->key.kshare_ecdh_params.curve = group->curve; + + ret = 0; + + } else if (group->pk == GNUTLS_PK_ECDH_X25519) { + gnutls_pk_params_release(&session->key.kshare_ecdhx_params); + gnutls_pk_params_init(&session->key.kshare_ecdhx_params); + + ret = _gnutls_pk_generate_keys(group->pk, group->curve, + &session->key.kshare_ecdhx_params, 1); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + _gnutls_buffer_append_data_prefix(extdata, 16, + session->key.kshare_ecdhx_params.raw_pub.data, + session->key.kshare_ecdhx_params.raw_pub.size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + session->key.kshare_ecdhx_params.algo = group->pk; + session->key.kshare_ecdhx_params.curve = group->curve; + + ret = 0; + + } else if (group->pk == GNUTLS_PK_DH) { + /* we need to initialize the group parameters first */ + gnutls_pk_params_release(&session->key.kshare_dh_params); + gnutls_pk_params_init(&session->key.kshare_dh_params); + + ret = _gnutls_mpi_init_scan_nz(&session->key.kshare_dh_params.params[DH_G], + group->generator->data, group->generator->size); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_mpi_init_scan_nz(&session->key.kshare_dh_params.params[DH_P], + group->prime->data, group->prime->size); + if (ret < 0) + return gnutls_assert_val(ret); + + session->key.kshare_dh_params.algo = group->pk; + session->key.kshare_dh_params.qbits = *group->q_bits; + session->key.kshare_dh_params.params_nr = 3; /* empty q */ + + ret = _gnutls_pk_generate_keys(group->pk, 0, &session->key.kshare_dh_params, 1); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + _gnutls_buffer_append_prefix(extdata, 16, group->prime->size); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_buffer_append_fixed_mpi(extdata, session->key.kshare_dh_params.params[DH_Y], + group->prime->size); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = 0; + } + + cleanup: + gnutls_free(tmp.data); + return ret; +} + +/* + * Sends server key exchange parameters + * + */ +static int server_gen_key_share(gnutls_session_t session, const gnutls_group_entry_st *group, gnutls_buffer_st *extdata) +{ + gnutls_datum_t tmp = {NULL, 0}; + int ret; + + if (group->pk != GNUTLS_PK_EC && group->pk != GNUTLS_PK_ECDH_X25519 && + group->pk != GNUTLS_PK_DH) { + _gnutls_debug_log("Cannot send key share for group %s!\n", group->name); + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + } + + _gnutls_handshake_log("EXT[%p]: sending key share for %s\n", session, group->name); + + ret = + _gnutls_buffer_append_prefix(extdata, 16, group->tls_id); + if (ret < 0) + return gnutls_assert_val(ret); + + if (group->pk == GNUTLS_PK_EC) { + ret = _gnutls_ecc_ansi_x962_export(group->curve, + session->key.kshare_ecdh_params.params[ECC_X], + session->key.kshare_ecdh_params.params[ECC_Y], + &tmp); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + _gnutls_buffer_append_data_prefix(extdata, 16, tmp.data, tmp.size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + + } else if (group->pk == GNUTLS_PK_ECDH_X25519) { + ret = + _gnutls_buffer_append_data_prefix(extdata, 16, + session->key.kshare_ecdhx_params.raw_pub.data, + session->key.kshare_ecdhx_params.raw_pub.size); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = 0; + + } else if (group->pk == GNUTLS_PK_DH) { + ret = + _gnutls_buffer_append_prefix(extdata, 16, group->prime->size); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_buffer_append_fixed_mpi(extdata, session->key.kshare_dh_params.params[DH_Y], + group->prime->size); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = 0; + } + + cleanup: + gnutls_free(tmp.data); + return ret; +} + +/* Generates shared key and stores it in session->key.key + */ +static int +server_use_key_share(gnutls_session_t session, const gnutls_group_entry_st *group, + const uint8_t * data, size_t data_size) +{ + const gnutls_ecc_curve_entry_st *curve; + int ret; + + if (group->pk == GNUTLS_PK_EC) { + gnutls_pk_params_st pub; + + gnutls_pk_params_release(&session->key.kshare_ecdh_params); + gnutls_pk_params_init(&session->key.kshare_ecdh_params); + + curve = _gnutls_ecc_curve_get_params(group->curve); + + gnutls_pk_params_init(&pub); + + if (curve->size*2+1 != data_size) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + /* generate our key */ + ret = _gnutls_pk_generate_keys(curve->pk, curve->id, &session->key.kshare_ecdh_params, 1); + if (ret < 0) + return gnutls_assert_val(ret); + + /* read the public key */ + ret = _gnutls_ecc_ansi_x962_import(data, data_size, + &pub.params[ECC_X], + &pub.params[ECC_Y]); + if (ret < 0) + return gnutls_assert_val(ret); + + pub.algo = group->pk; + pub.curve = curve->id; + pub.params_nr = 2; + + /* generate shared */ + ret = _gnutls_pk_derive_tls13(curve->pk, &session->key.key, &session->key.kshare_ecdh_params, &pub); + gnutls_pk_params_release(&pub); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + ret = 0; + + } else if (group->pk == GNUTLS_PK_ECDH_X25519) { + gnutls_pk_params_st pub; + + gnutls_pk_params_release(&session->key.kshare_ecdhx_params); + gnutls_pk_params_init(&session->key.kshare_ecdhx_params); + + curve = _gnutls_ecc_curve_get_params(group->curve); + + if (curve->size != data_size) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + /* generate our key */ + ret = _gnutls_pk_generate_keys(curve->pk, curve->id, &session->key.kshare_ecdhx_params, 1); + if (ret < 0) + return gnutls_assert_val(ret); + + /* read the public key and generate shared */ + gnutls_pk_params_init(&pub); + + pub.algo = group->pk; + pub.curve = curve->id; + + pub.raw_pub.data = (void*)data; + pub.raw_pub.size = data_size; + + /* We don't mask the MSB in the final byte as required + * by RFC7748. This will be done internally by nettle 3.3 or later. + */ + ret = _gnutls_pk_derive_tls13(curve->pk, &session->key.key, &session->key.kshare_ecdhx_params, &pub); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + ret = 0; + + } else if (group->pk == GNUTLS_PK_DH) { + gnutls_pk_params_st pub; + + /* we need to initialize the group parameters first */ + gnutls_pk_params_release(&session->key.kshare_dh_params); + gnutls_pk_params_init(&session->key.kshare_dh_params); + + if (data_size != group->prime->size) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + /* set group params */ + ret = _gnutls_mpi_init_scan_nz(&session->key.kshare_dh_params.params[DH_G], + group->generator->data, group->generator->size); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_mpi_init_scan_nz(&session->key.kshare_dh_params.params[DH_P], + group->prime->data, group->prime->size); + if (ret < 0) + return gnutls_assert_val(ret); + + session->key.kshare_dh_params.algo = GNUTLS_PK_DH; + session->key.kshare_dh_params.qbits = *group->q_bits; + session->key.kshare_dh_params.params_nr = 3; /* empty q */ + + /* generate our keys */ + ret = _gnutls_pk_generate_keys(group->pk, 0, &session->key.kshare_dh_params, 1); + if (ret < 0) + return gnutls_assert_val(ret); + + /* read the public key and generate shared */ + gnutls_pk_params_init(&pub); + + ret = _gnutls_mpi_init_scan_nz(&pub.params[DH_Y], + data, data_size); + if (ret < 0) + return gnutls_assert_val(ret); + + pub.algo = group->pk; + + /* generate shared key */ + ret = _gnutls_pk_derive_tls13(GNUTLS_PK_DH, &session->key.key, &session->key.kshare_dh_params, &pub); + _gnutls_mpi_release(pub.params[DH_Y]); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = 0; + } else { + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + } + + _gnutls_debug_log("EXT[%p]: server generated %s shared key\n", session, group->name); + + return ret; +} + +/* Generates shared key and stores it in session->key.key + */ +static int +client_use_key_share(gnutls_session_t session, const gnutls_group_entry_st *group, + const uint8_t * data, size_t data_size) +{ + const gnutls_ecc_curve_entry_st *curve; + int ret; + + if (group->pk == GNUTLS_PK_EC) { + gnutls_pk_params_st pub; + + curve = _gnutls_ecc_curve_get_params(group->curve); + + gnutls_pk_params_init(&pub); + + if (curve->size*2+1 != data_size) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + /* read the server's public key */ + ret = _gnutls_ecc_ansi_x962_import(data, data_size, + &pub.params[ECC_X], + &pub.params[ECC_Y]); + if (ret < 0) + return gnutls_assert_val(ret); + + pub.algo = group->pk; + pub.curve = curve->id; + pub.params_nr = 2; + + /* generate shared key */ + ret = _gnutls_pk_derive_tls13(curve->pk, &session->key.key, &session->key.kshare_ecdh_params, &pub); + gnutls_pk_params_release(&pub); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + ret = 0; + + } else if (group->pk == GNUTLS_PK_ECDH_X25519) { + gnutls_pk_params_st pub; + + curve = _gnutls_ecc_curve_get_params(group->curve); + + if (curve->size != data_size) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + /* read the public key and generate shared */ + gnutls_pk_params_init(&pub); + + pub.algo = group->pk; + pub.curve = curve->id; + + pub.raw_pub.data = (void*)data; + pub.raw_pub.size = data_size; + + /* We don't mask the MSB in the final byte as required + * by RFC7748. This will be done internally by nettle 3.3 or later. + */ + ret = _gnutls_pk_derive_tls13(curve->pk, &session->key.key, &session->key.kshare_ecdhx_params, &pub); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + ret = 0; + + } else if (group->pk == GNUTLS_PK_DH) { + gnutls_pk_params_st pub; + + if (data_size != group->prime->size) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + /* read the public key and generate shared */ + gnutls_pk_params_init(&pub); + + ret = _gnutls_mpi_init_scan_nz(&pub.params[DH_Y], + data, data_size); + if (ret < 0) + return gnutls_assert_val(ret); + + pub.algo = group->pk; + + /* generate shared key */ + ret = _gnutls_pk_derive_tls13(GNUTLS_PK_DH, &session->key.key, &session->key.kshare_dh_params, &pub); + _gnutls_mpi_release(pub.params[DH_Y]); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = 0; + } else { + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + } + + _gnutls_debug_log("EXT[%p]: client generated %s shared key\n", session, group->name); + + return ret; +} + +static int +key_share_recv_params(gnutls_session_t session, + const uint8_t * data, size_t _data_size) +{ + int ret; + ssize_t data_size = _data_size; + ssize_t size; + unsigned gid, used_share = 0; + const version_entry_st *ver; + const gnutls_group_entry_st *group, *sgroup = NULL; + + if (session->security_parameters.entity == GNUTLS_SERVER) { + ver = get_version(session); + if (ver == NULL || ver->key_shares == 0) + return gnutls_assert_val(0); + + DECR_LEN(data_size, 2); + size = _gnutls_read_uint16(data); + data += 2; + + if (data_size != size) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + while(data_size > 0) { + DECR_LEN(data_size, 2); + gid = _gnutls_read_uint16(data); + data += 2; + + DECR_LEN(data_size, 2); + size = _gnutls_read_uint16(data); + data += 2; + + DECR_LEN(data_size, size); + + /* at this point we have already negotiated a group; + * find the group's share. */ + group = _gnutls_tls_id_to_group(gid); + + if (group != NULL) + _gnutls_handshake_log("EXT[%p]: Received key share for %s\n", session, group->name); + + if (group != NULL) { + if (group == session->internals.cand_ec_group) + sgroup = group; + else if (group == session->internals.cand_dh_group) + sgroup = group; + } + + if (sgroup == NULL) { + data += size; + continue; + } + + _gnutls_session_group_set(session, sgroup); + _gnutls_handshake_log("EXT[%p]: Selected group %s\n", session, sgroup->name); + + ret = server_use_key_share(session, sgroup, data, size); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + used_share = 1; + break; + } + + if (used_share == 0) { + /* we signal for hello-retry-request */ + return gnutls_assert_val(GNUTLS_E_NO_COMMON_KEY_SHARE); + } + + } else { /* Client */ + ver = get_version(session); + if (unlikely(ver == NULL || ver->key_shares == 0)) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + DECR_LEN(data_size, 2); + gid = _gnutls_read_uint16(data); + data += 2; + + DECR_LEN(data_size, 2); + size = _gnutls_read_uint16(data); + data+=2; + + if (data_size != size) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + group = _gnutls_tls_id_to_group(gid); + if (group == NULL) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + /* check if we support it */ + ret = _gnutls_session_supports_group(session, group->id); + if (ret < 0) { + _gnutls_handshake_log("EXT[%p]: received share for %s which is disabled\n", session, group->name); + return gnutls_assert_val(ret); + } + + _gnutls_session_group_set(session, group); + + ret = client_use_key_share(session, group, data, size); + if (ret < 0) + return gnutls_assert_val(ret); + } + + return 0; +} + +#define MAX_GROUPS 3 +/* returns data_size or a negative number on failure + */ +static int +key_share_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + unsigned i; + int ret; + unsigned char *lengthp; + unsigned int cur_length; + gnutls_pk_algorithm_t selected_groups[MAX_GROUPS]; + unsigned int generated = 0; + const gnutls_group_entry_st *group; + const version_entry_st *ver; + + /* this extension is only being sent on client side */ + if (session->security_parameters.entity == GNUTLS_CLIENT) { + ver = _gnutls_version_max(session); + if (unlikely(ver == NULL || ver->key_shares == 0)) + return gnutls_assert_val(0); + + /* write the total length later */ + lengthp = &extdata->data[extdata->length]; + + ret = + _gnutls_buffer_append_prefix(extdata, 16, 0); + if (ret < 0) + return gnutls_assert_val(ret); + + cur_length = extdata->length; + + /* generate key shares for out top-3 groups + * if they are of different PK type. */ + for (i=0;i<session->internals.priorities->groups.size;i++) { + group = session->internals.priorities->groups.entry[i]; + + if (generated == 1 && group->pk == selected_groups[0]) + continue; + else if (generated == 2 && (group->pk == selected_groups[1] || group->pk == selected_groups[0])) + continue; + + selected_groups[generated] = group->pk; + + ret = client_gen_key_share(session, group, extdata); + if (ret == GNUTLS_E_INT_RET_0) + continue; /* no key share for this algorithm */ + if (ret < 0) + return gnutls_assert_val(ret); + + generated++; + + if (generated >= MAX_GROUPS) + break; + } + + /* copy actual length */ + _gnutls_write_uint16(extdata->length - cur_length, lengthp); + + } else { /* server */ + ver = get_version(session); + if (unlikely(ver == NULL || ver->key_shares == 0)) + return gnutls_assert_val(0); + + group = get_group(session); + if (unlikely(group == NULL)) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + ret = server_gen_key_share(session, group, extdata); + if (ret < 0) + return gnutls_assert_val(ret); + } + + return 0; +} + diff --git a/lib/ext/key_share.h b/lib/ext/key_share.h new file mode 100644 index 0000000000..11d6345d00 --- /dev/null +++ b/lib/ext/key_share.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +#ifndef EXT_KEY_SHARE_H +#define EXT_KEY_SHARE_H + +#include <extensions.h> + +extern const extension_entry_st ext_mod_key_share; + +#endif diff --git a/lib/extensions.c b/lib/extensions.c index 3dcfb30e2d..7c3e829681 100644 --- a/lib/extensions.c +++ b/lib/extensions.c @@ -45,6 +45,7 @@ #include <ext/srtp.h> #include <ext/alpn.h> #include <ext/dumbfw.h> +#include <ext/key_share.h> #include <ext/etm.h> #include <num.h> @@ -77,6 +78,7 @@ static extension_entry_st const *extfunc[MAX_EXT_TYPES+1] = { &ext_mod_supported_ecc, &ext_mod_supported_ecc_pf, &ext_mod_sig, + &ext_mod_key_share, #ifdef ENABLE_DTLS_SRTP &ext_mod_srtp, #endif diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 3679369f84..b16a98d1d8 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -285,6 +285,7 @@ typedef enum extensions_t { GNUTLS_EXTENSION_ETM = 22, GNUTLS_EXTENSION_EXT_MASTER_SECRET = 23, GNUTLS_EXTENSION_SESSION_TICKET = 35, + GNUTLS_EXTENSION_KEY_SHARE = 40, GNUTLS_EXTENSION_SUPPORTED_VERSIONS = 43, GNUTLS_EXTENSION_POST_HANDSHAKE = 49, GNUTLS_EXTENSION_SAFE_RENEGOTIATION = 65281 /* aka: 0xff01 */ @@ -396,8 +397,14 @@ typedef struct auth_cred_st { } auth_cred_st; struct gnutls_key_st { + /* TLS 1.3 key share exchange */ + gnutls_pk_params_st kshare_ecdh_params; + gnutls_pk_params_st kshare_ecdhx_params; + gnutls_pk_params_st kshare_dh_params; + /* For ECDH KX */ gnutls_pk_params_st ecdh_params; /* private part */ + /* public part */ bigint_t ecdh_x; bigint_t ecdh_y; @@ -434,8 +441,8 @@ struct gnutls_key_st { uint8_t crypt_algo; auth_cred_st *cred; /* used to specify keys/certificates etc */ - }; + typedef struct gnutls_key_st gnutls_key_st; struct pin_info_st { @@ -524,6 +531,7 @@ typedef struct { bool false_start; /* That version can be used with false start */ bool only_extension; /* negotiated only with an extension */ bool post_handshake_auth; /* Supports the TLS 1.3 post handshake auth */ + bool key_shares; /* TLS 1.3 key share key exchange */ /* * TLS versions modify the semantics of signature algorithms. This number * is there to distinguish signature algorithms semantics between versions diff --git a/lib/handshake.c b/lib/handshake.c index 36ecc11440..5c15d8dc4a 100644 --- a/lib/handshake.c +++ b/lib/handshake.c @@ -1595,15 +1595,31 @@ read_server_hello(gnutls_session_t session, pos++; } - /* Parse extensions. + /* Parse extensions in order. */ ret = - _gnutls_parse_extensions(session, GNUTLS_EXT_ANY, &data[pos], + _gnutls_parse_extensions(session, GNUTLS_EXT_MANDATORY, &data[pos], len); - if (ret < 0) { - gnutls_assert(); - return ret; - } + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + _gnutls_parse_extensions(session, GNUTLS_EXT_APPLICATION, &data[pos], + len); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + _gnutls_parse_extensions(session, GNUTLS_EXT_TLS, &data[pos], + len); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + _gnutls_parse_extensions(session, _GNUTLS_EXT_TLS_POST_CS, &data[pos], + len); + if (ret < 0) + return gnutls_assert_val(ret); return ret; } diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index ec6af62906..9245431642 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -2967,6 +2967,8 @@ void gnutls_fips140_set_mode(gnutls_fips_mode_t mode, unsigned flags); #define GNUTLS_E_PK_INVALID_PUBKEY_PARAMS -420 #define GNUTLS_E_PK_NO_VALIDATION_PARAMS -421 +#define GNUTLS_E_NO_COMMON_KEY_SHARE -420 + #define GNUTLS_E_UNIMPLEMENTED_FEATURE -1250 /* Internal errors of the library; will never be returned diff --git a/lib/nettle/pk.c b/lib/nettle/pk.c index 0ecd14f5c3..64f17d80c3 100644 --- a/lib/nettle/pk.c +++ b/lib/nettle/pk.c @@ -200,7 +200,8 @@ ecc_shared_secret(struct ecc_scalar *private_key, static int _wrap_nettle_pk_derive(gnutls_pk_algorithm_t algo, gnutls_datum_t * out, const gnutls_pk_params_st * priv, - const gnutls_pk_params_st * pub) + const gnutls_pk_params_st * pub, + unsigned int flags) { int ret; @@ -249,7 +250,14 @@ static int _wrap_nettle_pk_derive(gnutls_pk_algorithm_t algo, goto dh_cleanup; } - ret = _gnutls_mpi_dprint(k, out); + if (flags & PK_DERIVE_TLS13) { + ret = + _gnutls_mpi_dprint_size(k, out, + (bits+7)/8); + } else { + ret = _gnutls_mpi_dprint(k, out); + } + if (ret < 0) { gnutls_assert(); goto dh_cleanup; @@ -32,7 +32,8 @@ extern gnutls_crypto_pk_st _gnutls_pk_ops; #define _gnutls_pk_verify( algo, data, sig, params, sign_params) _gnutls_pk_ops.verify( algo, data, sig, params, sign_params) #define _gnutls_pk_verify_priv_params( algo, params) _gnutls_pk_ops.verify_priv_params( algo, params) #define _gnutls_pk_verify_pub_params( algo, params) _gnutls_pk_ops.verify_pub_params( algo, params) -#define _gnutls_pk_derive( algo, out, pub, priv) _gnutls_pk_ops.derive( algo, out, pub, priv) +#define _gnutls_pk_derive( algo, out, pub, priv) _gnutls_pk_ops.derive( algo, out, pub, priv, 0) +#define _gnutls_pk_derive_tls13( algo, out, pub, priv) _gnutls_pk_ops.derive( algo, out, pub, priv, PK_DERIVE_TLS13) #define _gnutls_pk_generate_keys( algo, bits, params, temporal) _gnutls_pk_ops.generate_keys( algo, bits, params, temporal) #define _gnutls_pk_generate_params( algo, bits, priv) _gnutls_pk_ops.generate_params( algo, bits, priv) #define _gnutls_pk_hash_algorithm( pk, sig, params, hash) _gnutls_pk_ops.hash_algorithm(pk, sig, params, hash) diff --git a/lib/state.c b/lib/state.c index 2056dd2438..f762fc7481 100644 --- a/lib/state.c +++ b/lib/state.c @@ -153,6 +153,11 @@ static void deinit_keys(gnutls_session_t session) { gnutls_pk_params_release(&session->key.ecdh_params); gnutls_pk_params_release(&session->key.dh_params); + + gnutls_pk_params_release(&session->key.kshare_ecdhx_params); + gnutls_pk_params_release(&session->key.kshare_ecdh_params); + gnutls_pk_params_release(&session->key.kshare_dh_params); + zrelease_temp_mpi_key(&session->key.ecdh_x); zrelease_temp_mpi_key(&session->key.ecdh_y); _gnutls_free_temp_key_datum(&session->key.ecdhx); |