summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/algorithms/protocols.c1
-rw-r--r--lib/crypto-backend.h4
-rw-r--r--lib/errors.c2
-rw-r--r--lib/ext/Makefile.am2
-rw-r--r--lib/ext/key_share.c665
-rw-r--r--lib/ext/key_share.h30
-rw-r--r--lib/extensions.c2
-rw-r--r--lib/gnutls_int.h10
-rw-r--r--lib/handshake.c28
-rw-r--r--lib/includes/gnutls/gnutls.h.in2
-rw-r--r--lib/nettle/pk.c12
-rw-r--r--lib/pk.h3
-rw-r--r--lib/state.c5
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;
diff --git a/lib/pk.h b/lib/pk.h
index f7f2652c85..7ff76d12df 100644
--- a/lib/pk.h
+++ b/lib/pk.h
@@ -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);