Blame lib/ext/supported_groups.c

Packit aea12f
/*
Packit aea12f
 * Copyright (C) 2011-2012 Free Software Foundation, Inc.
Packit aea12f
 * Copyright (C) 2017 Red Hat, Inc.
Packit aea12f
 *
Packit aea12f
 * Author: Nikos Mavrogiannopoulos
Packit aea12f
 *
Packit aea12f
 * This file is part of GnuTLS.
Packit aea12f
 *
Packit aea12f
 * The GnuTLS is free software; you can redistribute it and/or
Packit aea12f
 * modify it under the terms of the GNU Lesser General Public License
Packit aea12f
 * as published by the Free Software Foundation; either version 2.1 of
Packit aea12f
 * the License, or (at your option) any later version.
Packit aea12f
 *
Packit aea12f
 * This library is distributed in the hope that it will be useful, but
Packit aea12f
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit aea12f
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit aea12f
 * Lesser General Public License for more details.
Packit aea12f
 *
Packit aea12f
 * You should have received a copy of the GNU Lesser General Public License
Packit aea12f
 * along with this program.  If not, see <https://www.gnu.org/licenses/>
Packit aea12f
 *
Packit aea12f
 */
Packit aea12f
Packit aea12f
/* This file contains the code for the Supported Groups extension (rfc7919).
Packit aea12f
 * This extension was previously named Supported Elliptic Curves under TLS 1.2.
Packit aea12f
 */
Packit aea12f
Packit aea12f
#include "ext/supported_groups.h"
Packit aea12f
#include "str.h"
Packit aea12f
#include "num.h"
Packit aea12f
#include "auth/psk.h"
Packit aea12f
#include "auth/cert.h"
Packit aea12f
#include "auth/anon.h"
Packit aea12f
#include "algorithms.h"
Packit aea12f
#include <gnutls/gnutls.h>
Packit aea12f
Packit aea12f
Packit aea12f
static int _gnutls_supported_groups_recv_params(gnutls_session_t session,
Packit aea12f
					     const uint8_t * data,
Packit aea12f
					     size_t data_size);
Packit aea12f
static int _gnutls_supported_groups_send_params(gnutls_session_t session,
Packit aea12f
					     gnutls_buffer_st * extdata);
Packit aea12f
Packit aea12f
Packit aea12f
const hello_ext_entry_st ext_mod_supported_groups = {
Packit aea12f
	.name = "Supported Groups",
Packit aea12f
	.tls_id = 10,
Packit aea12f
	.gid = GNUTLS_EXTENSION_SUPPORTED_GROUPS,
Packit aea12f
	.parse_type = GNUTLS_EXT_TLS,
Packit aea12f
	.validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS | GNUTLS_EXT_FLAG_CLIENT_HELLO |
Packit aea12f
		    GNUTLS_EXT_FLAG_EE | GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO,
Packit aea12f
	.recv_func = _gnutls_supported_groups_recv_params,
Packit aea12f
	.send_func = _gnutls_supported_groups_send_params,
Packit aea12f
	.pack_func = NULL,
Packit aea12f
	.unpack_func = NULL,
Packit aea12f
	.deinit_func = NULL,
Packit aea12f
	.cannot_be_overriden = 1
Packit aea12f
};
Packit aea12f
Packit aea12f
Packit aea12f
static unsigned get_min_dh(gnutls_session_t session)
Packit aea12f
{
Packit aea12f
	gnutls_certificate_credentials_t cert_cred;
Packit aea12f
	gnutls_psk_server_credentials_t psk_cred;
Packit aea12f
	gnutls_anon_server_credentials_t anon_cred;
Packit aea12f
	unsigned level = 0;
Packit aea12f
Packit aea12f
	cert_cred = (gnutls_certificate_credentials_t)_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE);
Packit aea12f
	psk_cred = (gnutls_psk_server_credentials_t)_gnutls_get_cred(session, GNUTLS_CRD_PSK);
Packit aea12f
	anon_cred = (gnutls_anon_server_credentials_t)_gnutls_get_cred(session, GNUTLS_CRD_ANON);
Packit aea12f
Packit aea12f
	if (cert_cred) {
Packit aea12f
		level = cert_cred->dh_sec_param;
Packit aea12f
	} else if (psk_cred) {
Packit aea12f
		level = psk_cred->dh_sec_param;
Packit aea12f
	} else if (anon_cred) {
Packit aea12f
		level = anon_cred->dh_sec_param;
Packit aea12f
	}
Packit aea12f
Packit aea12f
	if (level)
Packit aea12f
		return gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, level);
Packit aea12f
Packit aea12f
	return 0;
Packit aea12f
}
Packit aea12f
Packit aea12f
/*
Packit aea12f
 * In case of a server: if a SUPPORTED_GROUPS extension type is received then it stores
Packit aea12f
 * into the session security parameters the new value. The server may use gnutls_session_certificate_type_get(),
Packit aea12f
 * to access it.
Packit aea12f
 *
Packit aea12f
 * In case of a client: If supported_eccs have been specified then we send the extension.
Packit aea12f
 *
Packit aea12f
 */
Packit aea12f
static int
Packit aea12f
_gnutls_supported_groups_recv_params(gnutls_session_t session,
Packit 5407aa
				  const uint8_t * data, size_t data_size)
Packit aea12f
{
Packit aea12f
	int i;
Packit aea12f
	uint16_t len;
Packit aea12f
	const uint8_t *p = data;
Packit aea12f
	const gnutls_group_entry_st *group = NULL;
Packit aea12f
	unsigned have_ffdhe = 0;
Packit aea12f
	unsigned tls_id;
Packit aea12f
	unsigned min_dh;
Packit aea12f
	unsigned j;
Packit aea12f
	int serv_ec_idx, serv_dh_idx; /* index in server's priority listing */
Packit aea12f
	int cli_ec_pos, cli_dh_pos; /* position in listing sent by client */
Packit aea12f
Packit aea12f
	if (session->security_parameters.entity == GNUTLS_CLIENT) {
Packit aea12f
		/* A client shouldn't receive this extension in TLS1.2. It is
Packit aea12f
		 * possible to read that message under TLS1.3 as an encrypted
Packit aea12f
		 * extension. */
Packit aea12f
		return 0;
Packit aea12f
	} else {		/* SERVER SIDE - we must check if the sent supported ecc type is the right one
Packit aea12f
				 */
Packit aea12f
		if (data_size < 2)
Packit aea12f
			return
Packit aea12f
			    gnutls_assert_val
Packit aea12f
			    (GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
Packit aea12f
Packit aea12f
		DECR_LEN(data_size, 2);
Packit aea12f
		len = _gnutls_read_uint16(p);
Packit aea12f
		p += 2;
Packit aea12f
Packit aea12f
		if (len % 2 != 0)
Packit aea12f
			return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
Packit aea12f
Packit aea12f
		DECR_LEN(data_size, len);
Packit aea12f
Packit aea12f
		/* we figure what is the minimum DH allowed for this session, if any */
Packit aea12f
		min_dh = get_min_dh(session);
Packit aea12f
Packit aea12f
		serv_ec_idx = serv_dh_idx = -1;
Packit aea12f
		cli_ec_pos = cli_dh_pos = -1;
Packit aea12f
Packit aea12f
		/* This extension is being processed prior to a ciphersuite being selected,
Packit aea12f
		 * so we cannot rely on ciphersuite information. */
Packit aea12f
		for (i = 0; i < len; i += 2) {
Packit aea12f
			if (have_ffdhe == 0 && p[i] == 0x01)
Packit aea12f
				have_ffdhe = 1;
Packit aea12f
Packit aea12f
			tls_id = _gnutls_read_uint16(&p[i]);
Packit aea12f
			group = _gnutls_tls_id_to_group(tls_id);
Packit aea12f
Packit aea12f
			_gnutls_handshake_log("EXT[%p]: Received group %s (0x%x)\n", session, group?group->name:"unknown", tls_id);
Packit aea12f
			if (group == NULL)
Packit aea12f
				continue;
Packit aea12f
Packit aea12f
			if (min_dh > 0 && group->prime && group->prime->size*8 < min_dh)
Packit aea12f
				continue;
Packit aea12f
Packit aea12f
			/* we simulate _gnutls_session_supports_group, but we prioritize if
Packit aea12f
			 * %SERVER_PRECEDENCE is given */
Packit aea12f
			for (j = 0; j < session->internals.priorities->groups.size; j++) {
Packit aea12f
				if (session->internals.priorities->groups.entry[j]->id == group->id) {
Packit aea12f
					if (session->internals.priorities->server_precedence) {
Packit aea12f
						if (group->pk == GNUTLS_PK_DH) {
Packit aea12f
							if (serv_dh_idx != -1 && (int)j > serv_dh_idx)
Packit aea12f
								break;
Packit aea12f
							serv_dh_idx = j;
Packit aea12f
							cli_dh_pos = i;
Packit aea12f
						} else {
Packit aea12f
							if (serv_ec_idx != -1 && (int)j > serv_ec_idx)
Packit aea12f
								break;
Packit aea12f
							serv_ec_idx = j;
Packit aea12f
							cli_ec_pos = i;
Packit aea12f
						}
Packit aea12f
					} else {
Packit aea12f
						if (group->pk == GNUTLS_PK_DH) {
Packit aea12f
							if (cli_dh_pos != -1)
Packit aea12f
								break;
Packit aea12f
							cli_dh_pos = i;
Packit aea12f
							serv_dh_idx = j;
Packit aea12f
						} else {
Packit aea12f
							if (cli_ec_pos != -1)
Packit aea12f
								break;
Packit aea12f
							cli_ec_pos = i;
Packit aea12f
							serv_ec_idx = j;
Packit aea12f
						}
Packit aea12f
					}
Packit aea12f
					break;
Packit aea12f
				}
Packit aea12f
			}
Packit aea12f
		}
Packit aea12f
Packit aea12f
		/* serv_dh/ec_pos contain the index of the groups we want to use.
Packit aea12f
		 */
Packit aea12f
		if (serv_dh_idx != -1) {
Packit aea12f
			session->internals.cand_dh_group = session->internals.priorities->groups.entry[serv_dh_idx];
Packit aea12f
			session->internals.cand_group = session->internals.cand_dh_group;
Packit aea12f
		}
Packit aea12f
Packit aea12f
		if (serv_ec_idx != -1) {
Packit aea12f
			session->internals.cand_ec_group = session->internals.priorities->groups.entry[serv_ec_idx];
Packit aea12f
			if (session->internals.cand_group == NULL ||
Packit aea12f
			    (session->internals.priorities->server_precedence && serv_ec_idx < serv_dh_idx) ||
Packit aea12f
			    (!session->internals.priorities->server_precedence && cli_ec_pos < cli_dh_pos)) {
Packit aea12f
				session->internals.cand_group = session->internals.cand_ec_group;
Packit aea12f
			}
Packit aea12f
		}
Packit aea12f
Packit aea12f
		if (session->internals.cand_group)
Packit aea12f
			_gnutls_handshake_log("EXT[%p]: Selected group %s\n", session, session->internals.cand_group->name);
Packit aea12f
Packit aea12f
		if (have_ffdhe)
Packit aea12f
			session->internals.hsk_flags |= HSK_HAVE_FFDHE;
Packit aea12f
	}
Packit aea12f
Packit aea12f
	return 0;
Packit aea12f
}
Packit aea12f
Packit aea12f
/* returns data_size or a negative number on failure
Packit aea12f
 */
Packit aea12f
static int
Packit aea12f
_gnutls_supported_groups_send_params(gnutls_session_t session,
Packit aea12f
				  gnutls_buffer_st * extdata)
Packit aea12f
{
Packit aea12f
	unsigned len, i;
Packit aea12f
	int ret;
Packit aea12f
	uint16_t p;
Packit aea12f
Packit aea12f
	/* this extension is only being sent on client side */
Packit aea12f
	if (session->security_parameters.entity == GNUTLS_CLIENT) {
Packit aea12f
Packit aea12f
		len = session->internals.priorities->groups.size;
Packit aea12f
		if (len > 0) {
Packit aea12f
			ret =
Packit aea12f
			    _gnutls_buffer_append_prefix(extdata, 16,
Packit aea12f
							 len * 2);
Packit aea12f
			if (ret < 0)
Packit aea12f
				return gnutls_assert_val(ret);
Packit aea12f
Packit aea12f
			for (i = 0; i < len; i++) {
Packit aea12f
				p = session->internals.priorities->groups.entry[i]->tls_id;
Packit aea12f
Packit aea12f
				_gnutls_handshake_log("EXT[%p]: Sent group %s (0x%x)\n", session,
Packit aea12f
					session->internals.priorities->groups.entry[i]->name, (unsigned)p);
Packit aea12f
Packit aea12f
				ret =
Packit aea12f
				    _gnutls_buffer_append_prefix(extdata,
Packit aea12f
								 16, p);
Packit aea12f
				if (ret < 0)
Packit aea12f
					return gnutls_assert_val(ret);
Packit aea12f
			}
Packit aea12f
			return (len + 1) * 2;
Packit aea12f
		}
Packit aea12f
Packit aea12f
	}
Packit aea12f
Packit aea12f
	return 0;
Packit aea12f
}
Packit aea12f
Packit aea12f
/* Returns 0 if the given ECC curve is allowed in the current
Packit aea12f
 * session. A negative error value is returned otherwise.
Packit aea12f
 */
Packit aea12f
int
Packit aea12f
_gnutls_session_supports_group(gnutls_session_t session,
Packit aea12f
				unsigned int group)
Packit aea12f
{
Packit aea12f
	unsigned i;
Packit aea12f
Packit aea12f
	for (i = 0; i < session->internals.priorities->groups.size; i++) {
Packit aea12f
		if (session->internals.priorities->groups.entry[i]->id == group)
Packit aea12f
			return 0;
Packit aea12f
	}
Packit aea12f
Packit aea12f
	return GNUTLS_E_ECC_UNSUPPORTED_CURVE;
Packit aea12f
}