From 2d8a12b7fb089287d110ea64e5888fda4298548a Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Fri, 6 Oct 2017 09:05:20 +0200 Subject: key share: added flags to gnutls_init() to modify its default behavior That way the application can adjust the range of keys generated during client hello attempting to guess the server's algorithm. Applications are intentionally not given the option to select the algorithm in the key share, but rather chose from the prioritized list of groups, to avoid a disconnect between the prioritized groups, and the key share sent. Relates #284 Signed-off-by: Nikos Mavrogiannopoulos --- lib/ext/key_share.c | 49 ++++++++++++++++++++++++----------------- lib/includes/gnutls/gnutls.h.in | 18 ++++++++++++++- 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/lib/ext/key_share.c b/lib/ext/key_share.c index 411b10e9bf..2b31c95b4a 100644 --- a/lib/ext/key_share.c +++ b/lib/ext/key_share.c @@ -627,7 +627,6 @@ key_share_recv_params(gnutls_session_t session, return 0; } -#define MAX_GROUPS 3 /* returns data_size or a negative number on failure */ static int @@ -638,7 +637,6 @@ key_share_send_params(gnutls_session_t session, 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; @@ -669,29 +667,40 @@ key_share_send_params(gnutls_session_t session, return gnutls_assert_val(GNUTLS_E_NO_COMMON_KEY_SHARE); if (ret < 0) return gnutls_assert_val(ret); - } else - /* generate key shares for out top-3 groups - * if they are of different PK type. */ - for (i=0;iinternals.priorities->groups.size;i++) { - group = session->internals.priorities->groups.entry[i]; + } else { + gnutls_pk_algorithm_t selected_groups[3]; + unsigned max_groups = 2; /* GNUTLS_KEY_SHARE_TOP2 */ - if (generated == 1 && group->pk == selected_groups[0]) - continue; - else if (generated == 2 && (group->pk == selected_groups[1] || group->pk == selected_groups[0])) - continue; + if (session->internals.flags & GNUTLS_KEY_SHARE_TOP) + max_groups = 1; + else if (session->internals.flags & GNUTLS_KEY_SHARE_TOP3) + max_groups = 3; - selected_groups[generated] = group->pk; + assert(max_groups <= sizeof(selected_groups)/sizeof(selected_groups[0])); - 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); + /* generate key shares for out top-(max_groups) 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; - generated++; + 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); - if (generated >= MAX_GROUPS) - break; + generated++; + + if (generated >= max_groups) + break; + } } /* copy actual length */ diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index 0a9fbe07bd..ef37573265 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -361,10 +361,23 @@ typedef enum { * @GNUTLS_ENABLE_FALSE_START: Enable the TLS false start on client side if the negotiated ciphersuites allow it. This will enable sending data prior to the handshake being complete, and may introduce a risk of crypto failure when combined with certain key exchanged; for that GnuTLS may not enable that option in ciphersuites that are known to be not safe for false start. Since 3.5.0. * @GNUTLS_FORCE_CLIENT_CERT: When in client side and only a single cert is specified, send that certificate irrespective of the issuers expected by the server. Since 3.5.0. * @GNUTLS_NO_TICKETS: Flag to indicate that the session should not use resumption with session tickets. + * @GNUTLS_KEY_SHARE_TOP3: Generate key shared for the top-3 different groups which are enabled. + * That is, as each group is associated with a key type (EC, finite field, x25519), generate + * three keys using %GNUTLS_PK_DH, %GNUTLS_PK_EC, %GNUTLS_PK_ECDH_X25519 if all of them are enabled. + * @GNUTLS_KEY_SHARE_TOP2: Generate key shared for the top-2 different groups which are enabled. + * For example (ECDH + x25519). This is the default. + * @GNUTLS_KEY_SHARE_TOP: Generate key shared for the first group which is enabled. + * For example x25519. This option is the most performant for client (less CPU spent + * generating keys), but if the server doesn't support the advertized option it may + * result to more roundtrips needed to discover the server's choice. * * Enumeration of different flags for gnutls_init() function. All the flags * can be combined except @GNUTLS_SERVER and @GNUTLS_CLIENT which are mutually * exclusive. + * + * The key share options relate to the TLS 1.3 key share extension + * which is a speculative key generation expecting that the server + * would support the generated key. */ typedef enum { GNUTLS_SERVER = 1, @@ -377,7 +390,10 @@ typedef enum { GNUTLS_ALLOW_ID_CHANGE = (1<<7), GNUTLS_ENABLE_FALSE_START = (1<<8), GNUTLS_FORCE_CLIENT_CERT = (1<<9), - GNUTLS_NO_TICKETS = (1<<10) + GNUTLS_NO_TICKETS = (1<<10), + GNUTLS_KEY_SHARE_TOP = (1<<11), + GNUTLS_KEY_SHARE_TOP2 = (1<<12), + GNUTLS_KEY_SHARE_TOP3 = (1<<13) } gnutls_init_flags_t; /* compatibility defines (previous versions of gnutls -- cgit v1.2.1