|
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 |
}
|