/*
* Copyright (C) 2017 Free Software Foundation, Inc.
*
* Author: Ander Juaristi
*
* 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
*
*/
#include "gnutls_int.h"
#include "ext/psk_ke_modes.h"
#include "ext/pre_shared_key.h"
#include
#define PSK_KE 0
#define PSK_DHE_KE 1
static int
psk_ke_modes_send_params(gnutls_session_t session,
gnutls_buffer_t extdata)
{
int ret;
const version_entry_st *vers;
uint8_t data[2];
unsigned pos, i;
unsigned have_dhpsk = 0;
unsigned have_psk = 0;
/* Server doesn't send psk_key_exchange_modes */
if (session->security_parameters.entity == GNUTLS_SERVER)
return 0;
/* If session ticket is disabled and no PSK key exchange is
* enabled, don't send the extension */
if ((session->internals.flags & GNUTLS_NO_TICKETS) &&
!session->internals.priorities->have_psk)
return 0;
vers = _gnutls_version_max(session);
if (!vers || !vers->tls13_sem)
return 0;
/* We send the list prioritized according to our preferences as a convention
* (used throughout the protocol), even if the protocol doesn't mandate that
* for this particular message. That way we can keep the TLS 1.2 semantics/
* prioritization when negotiating PSK or DHE-PSK. Receiving servers would
* very likely respect our prioritization if they parse the message serially. */
pos = 0;
for (i=0;iinternals.priorities->_kx.num_priorities;i++) {
if (session->internals.priorities->_kx.priorities[i] == GNUTLS_KX_PSK && !have_psk) {
assert(pos <= 1);
data[pos++] = PSK_KE;
session->internals.hsk_flags |= HSK_PSK_KE_MODE_PSK;
have_psk = 1;
} else if ((session->internals.priorities->_kx.priorities[i] == GNUTLS_KX_DHE_PSK ||
session->internals.priorities->_kx.priorities[i] == GNUTLS_KX_ECDHE_PSK) && !have_dhpsk) {
assert(pos <= 1);
data[pos++] = PSK_DHE_KE;
session->internals.hsk_flags |= HSK_PSK_KE_MODE_DHE_PSK;
have_dhpsk = 1;
}
if (have_psk && have_dhpsk)
break;
}
/* For session resumption we need to send at least one */
if (pos == 0) {
if (session->internals.flags & GNUTLS_NO_TICKETS)
return 0;
data[pos++] = PSK_DHE_KE;
data[pos++] = PSK_KE;
session->internals.hsk_flags |= HSK_PSK_KE_MODE_DHE_PSK;
session->internals.hsk_flags |= HSK_PSK_KE_MODE_PSK;
}
ret = _gnutls_buffer_append_data_prefix(extdata, 8, data, pos);
if (ret < 0)
return gnutls_assert_val(ret);
session->internals.hsk_flags |= HSK_PSK_KE_MODES_SENT;
return 0;
}
#define MAX_POS INT_MAX
/*
* Since we only support ECDHE-authenticated PSKs, the server
* just verifies that a "psk_key_exchange_modes" extension was received,
* and that it contains the value one.
*/
static int
psk_ke_modes_recv_params(gnutls_session_t session,
const unsigned char *data, size_t len)
{
uint8_t ke_modes_len;
const version_entry_st *vers = get_version(session);
gnutls_psk_server_credentials_t cred;
int dhpsk_pos = MAX_POS;
int psk_pos = MAX_POS;
int cli_psk_pos = MAX_POS;
int cli_dhpsk_pos = MAX_POS;
unsigned i;
/* Client doesn't receive psk_key_exchange_modes */
if (session->security_parameters.entity == GNUTLS_CLIENT)
return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
/* we set hsk_flags to HSK_PSK_KE_MODE_INVALID on failure to ensure that
* when we parse the pre-shared key extension we detect PSK_KE_MODES as
* received. */
if (!vers || !vers->tls13_sem) {
session->internals.hsk_flags |= HSK_PSK_KE_MODE_INVALID;
return gnutls_assert_val(0);
}
cred = (gnutls_psk_server_credentials_t)_gnutls_get_cred(session, GNUTLS_CRD_PSK);
if (cred == NULL && (session->internals.flags & GNUTLS_NO_TICKETS)) {
session->internals.hsk_flags |= HSK_PSK_KE_MODE_INVALID;
return gnutls_assert_val(0);
}
DECR_LEN(len, 1);
ke_modes_len = *(data++);
for (i=0;iinternals.priorities->_kx.num_priorities;i++) {
if (session->internals.priorities->_kx.priorities[i] == GNUTLS_KX_PSK && psk_pos == MAX_POS) {
psk_pos = i;
} else if ((session->internals.priorities->_kx.priorities[i] == GNUTLS_KX_DHE_PSK ||
session->internals.priorities->_kx.priorities[i] == GNUTLS_KX_ECDHE_PSK) &&
dhpsk_pos == MAX_POS) {
dhpsk_pos = i;
}
if (dhpsk_pos != MAX_POS && psk_pos != MAX_POS)
break;
}
if (psk_pos == MAX_POS && dhpsk_pos == MAX_POS) {
if (!(session->internals.flags & GNUTLS_NO_TICKETS))
dhpsk_pos = 0;
else if (session->internals.priorities->groups.size == 0)
return gnutls_assert_val(0);
}
for (i=0;iinternals.priorities->server_precedence) {
if (dhpsk_pos != MAX_POS && cli_dhpsk_pos != MAX_POS && dhpsk_pos < psk_pos)
session->internals.hsk_flags |= HSK_PSK_KE_MODE_DHE_PSK;
else if (psk_pos != MAX_POS && cli_psk_pos != MAX_POS && psk_pos < dhpsk_pos)
session->internals.hsk_flags |= HSK_PSK_KE_MODE_PSK;
} else {
if (dhpsk_pos != MAX_POS && cli_dhpsk_pos != MAX_POS && cli_dhpsk_pos < cli_psk_pos)
session->internals.hsk_flags |= HSK_PSK_KE_MODE_DHE_PSK;
else if (psk_pos != MAX_POS && cli_psk_pos != MAX_POS && cli_psk_pos < cli_dhpsk_pos)
session->internals.hsk_flags |= HSK_PSK_KE_MODE_PSK;
}
if ((session->internals.hsk_flags & HSK_PSK_KE_MODE_PSK) ||
(session->internals.hsk_flags & HSK_PSK_KE_MODE_DHE_PSK)) {
return 0;
} else {
session->internals.hsk_flags |= HSK_PSK_KE_MODE_INVALID;
return gnutls_assert_val(0);
}
}
const hello_ext_entry_st ext_mod_psk_ke_modes = {
.name = "PSK Key Exchange Modes",
.tls_id = 45,
.gid = GNUTLS_EXTENSION_PSK_KE_MODES,
.client_parse_point = GNUTLS_EXT_TLS,
.server_parse_point = GNUTLS_EXT_TLS,
.validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_CLIENT_HELLO | GNUTLS_EXT_FLAG_TLS13_SERVER_HELLO,
.send_func = psk_ke_modes_send_params,
.recv_func = psk_ke_modes_recv_params
};