/*
* Copyright (C) 2011-2012 Free Software Foundation, 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
*
*/
#include "gnutls_int.h"
#include
#include "errors.h"
#include
#include "state.h"
#include "c-strcase.h"
extern mod_auth_st rsa_auth_struct;
extern mod_auth_st dhe_rsa_auth_struct;
extern mod_auth_st ecdhe_rsa_auth_struct;
extern mod_auth_st ecdhe_psk_auth_struct;
extern mod_auth_st ecdhe_ecdsa_auth_struct;
extern mod_auth_st dhe_dss_auth_struct;
extern mod_auth_st anon_auth_struct;
extern mod_auth_st anon_ecdh_auth_struct;
extern mod_auth_st srp_auth_struct;
extern mod_auth_st psk_auth_struct;
extern mod_auth_st dhe_psk_auth_struct;
extern mod_auth_st rsa_psk_auth_struct;
extern mod_auth_st srp_rsa_auth_struct;
extern mod_auth_st srp_dss_auth_struct;
extern mod_auth_st vko_gost_auth_struct;
/* Cred type mappings to KX algorithms
* The mappings are not 1-1. Some KX such as SRP_RSA require
* more than one credentials type.
*/
typedef struct {
gnutls_kx_algorithm_t algorithm;
gnutls_credentials_type_t client_type;
gnutls_credentials_type_t server_type; /* The type of credentials a server
* needs to set */
} gnutls_cred_map;
static const gnutls_cred_map cred_mappings[] = {
{GNUTLS_KX_ECDHE_RSA, GNUTLS_CRD_CERTIFICATE,
GNUTLS_CRD_CERTIFICATE},
{GNUTLS_KX_ECDHE_ECDSA, GNUTLS_CRD_CERTIFICATE,
GNUTLS_CRD_CERTIFICATE},
{GNUTLS_KX_RSA, GNUTLS_CRD_CERTIFICATE, GNUTLS_CRD_CERTIFICATE},
{GNUTLS_KX_DHE_DSS, GNUTLS_CRD_CERTIFICATE,
GNUTLS_CRD_CERTIFICATE},
{GNUTLS_KX_DHE_RSA, GNUTLS_CRD_CERTIFICATE,
GNUTLS_CRD_CERTIFICATE},
{GNUTLS_KX_ECDHE_PSK, GNUTLS_CRD_PSK, GNUTLS_CRD_PSK},
{GNUTLS_KX_PSK, GNUTLS_CRD_PSK, GNUTLS_CRD_PSK},
{GNUTLS_KX_DHE_PSK, GNUTLS_CRD_PSK, GNUTLS_CRD_PSK},
{GNUTLS_KX_RSA_PSK, GNUTLS_CRD_PSK, GNUTLS_CRD_CERTIFICATE},
{GNUTLS_KX_SRP, GNUTLS_CRD_SRP, GNUTLS_CRD_SRP},
{GNUTLS_KX_SRP_RSA, GNUTLS_CRD_SRP, GNUTLS_CRD_CERTIFICATE},
{GNUTLS_KX_SRP_DSS, GNUTLS_CRD_SRP, GNUTLS_CRD_CERTIFICATE},
{GNUTLS_KX_ANON_DH, GNUTLS_CRD_ANON, GNUTLS_CRD_ANON},
{GNUTLS_KX_ANON_ECDH, GNUTLS_CRD_ANON, GNUTLS_CRD_ANON},
{GNUTLS_KX_VKO_GOST_12, GNUTLS_CRD_CERTIFICATE, GNUTLS_CRD_CERTIFICATE},
{0, 0, 0}
};
#define GNUTLS_KX_MAP_LOOP(b) \
const gnutls_cred_map *p; \
for(p = cred_mappings; p->algorithm != 0; p++) { b ; }
struct gnutls_kx_algo_entry {
const char *name;
gnutls_kx_algorithm_t algorithm;
mod_auth_st *auth_struct;
bool needs_dh_params;
bool false_start;
};
typedef struct gnutls_kx_algo_entry gnutls_kx_algo_entry;
static const gnutls_kx_algo_entry _gnutls_kx_algorithms[] = {
#ifdef ENABLE_ECDHE
{"ECDHE-RSA", GNUTLS_KX_ECDHE_RSA, &ecdhe_rsa_auth_struct, 0, 1},
{"ECDHE-ECDSA", GNUTLS_KX_ECDHE_ECDSA, &ecdhe_ecdsa_auth_struct,
0, 1},
#endif
{"RSA", GNUTLS_KX_RSA, &rsa_auth_struct, 0, 0},
#ifdef ENABLE_DHE
{"DHE-RSA", GNUTLS_KX_DHE_RSA, &dhe_rsa_auth_struct, 1, 1},
{"DHE-DSS", GNUTLS_KX_DHE_DSS, &dhe_dss_auth_struct, 1, 1},
#endif
#ifdef ENABLE_PSK
{"PSK", GNUTLS_KX_PSK, &psk_auth_struct, 0, 0},
{"RSA-PSK", GNUTLS_KX_RSA_PSK, &rsa_psk_auth_struct, 0, 0},
#ifdef ENABLE_DHE
{"DHE-PSK", GNUTLS_KX_DHE_PSK, &dhe_psk_auth_struct,
1 /* needs DHE params */, 0},
#endif
#ifdef ENABLE_ECDHE
{"ECDHE-PSK", GNUTLS_KX_ECDHE_PSK, &ecdhe_psk_auth_struct, 0, 0},
#endif
#endif
#ifdef ENABLE_SRP
{"SRP-DSS", GNUTLS_KX_SRP_DSS, &srp_dss_auth_struct, 0, 0},
{"SRP-RSA", GNUTLS_KX_SRP_RSA, &srp_rsa_auth_struct, 0, 0},
{"SRP", GNUTLS_KX_SRP, &srp_auth_struct, 0, 0},
#endif
#if defined(ENABLE_ANON) && defined(ENABLE_DHE)
{"ANON-DH", GNUTLS_KX_ANON_DH, &anon_auth_struct, 1, 0},
#endif
#if defined(ENABLE_ANON) && defined(ENABLE_ECDHE)
{"ANON-ECDH", GNUTLS_KX_ANON_ECDH, &anon_ecdh_auth_struct, 0, 0},
#endif
#ifdef ENABLE_GOST
{"VKO-GOST-12", GNUTLS_KX_VKO_GOST_12, &vko_gost_auth_struct, 0, 0},
#endif
/* for deprecated and legacy algorithms no longer supported, use
* GNUTLS_KX_INVALID as an entry. This will make them available
* as priority strings, but they will be a no-op.
*/
{"RSA-EXPORT", GNUTLS_KX_INVALID, NULL, 0, 0},
{0, 0, 0, 0, 0}
};
#define GNUTLS_KX_LOOP(b) \
const gnutls_kx_algo_entry *p; \
for(p = _gnutls_kx_algorithms; p->name != NULL; p++) { b ; }
#define GNUTLS_KX_ALG_LOOP(a) \
GNUTLS_KX_LOOP( if(p->algorithm == algorithm) { a; break; } )
/* Key EXCHANGE functions */
mod_auth_st *_gnutls_kx_auth_struct(gnutls_kx_algorithm_t algorithm)
{
mod_auth_st *ret = NULL;
GNUTLS_KX_ALG_LOOP(ret = p->auth_struct);
return ret;
}
/**
* gnutls_kx_get_name:
* @algorithm: is a key exchange algorithm
*
* Convert a #gnutls_kx_algorithm_t value to a string.
*
* Returns: a pointer to a string that contains the name of the
* specified key exchange algorithm, or %NULL.
**/
const char *gnutls_kx_get_name(gnutls_kx_algorithm_t algorithm)
{
const char *ret = NULL;
/* avoid prefix */
GNUTLS_KX_ALG_LOOP(ret = p->name);
return ret;
}
/**
* gnutls_kx_get_id:
* @name: is a KX name
*
* Convert a string to a #gnutls_kx_algorithm_t value. The names are
* compared in a case insensitive way.
*
* Returns: an id of the specified KX algorithm, or %GNUTLS_KX_UNKNOWN
* on error.
**/
gnutls_kx_algorithm_t gnutls_kx_get_id(const char *name)
{
gnutls_kx_algorithm_t ret = GNUTLS_KX_UNKNOWN;
GNUTLS_KX_LOOP(
if (c_strcasecmp(p->name, name) == 0 && (int)p->algorithm != GNUTLS_KX_INVALID) {
ret = p->algorithm;
break;
}
);
return ret;
}
/* As with gnutls_kx_get_id(), but it returns all known
* key exchange algorithms (even legacy), with GNUTLS_KX_INVALID
* value.
*/
int _gnutls_kx_get_id(const char *name)
{
gnutls_kx_algorithm_t ret = GNUTLS_KX_UNKNOWN;
GNUTLS_KX_LOOP(
if (c_strcasecmp(p->name, name) == 0) {
ret = p->algorithm;
break;
}
);
return ret;
}
/**
* gnutls_kx_list:
*
* Get a list of supported key exchange algorithms.
*
* This function is not thread safe.
*
* Returns: a (0)-terminated list of #gnutls_kx_algorithm_t integers
* indicating the available key exchange algorithms.
**/
const gnutls_kx_algorithm_t *gnutls_kx_list(void)
{
static gnutls_kx_algorithm_t supported_kxs[MAX_ALGOS] = { 0 };
if (supported_kxs[0] == 0) {
int i = 0;
GNUTLS_KX_LOOP(supported_kxs[i++] = p->algorithm);
supported_kxs[i++] = 0;
}
return supported_kxs;
}
int _gnutls_kx_is_ok(gnutls_kx_algorithm_t algorithm)
{
ssize_t ret = -1;
GNUTLS_KX_ALG_LOOP(ret = p->algorithm);
if (ret >= 0)
ret = 0;
else
ret = 1;
return ret;
}
bool _gnutls_kx_allows_false_start(gnutls_session_t session)
{
unsigned algorithm = session->security_parameters.cs->kx_algorithm;
bool needs_dh = 0;
int bits;
ssize_t ret = 0;
GNUTLS_KX_ALG_LOOP(ret = p->false_start; needs_dh = p->needs_dh_params);
if (ret != 0) {
const gnutls_group_entry_st *e;
e = get_group(session);
#if defined(ENABLE_DHE) || defined(ENABLE_ANON)
if (needs_dh != 0) {
bits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, GNUTLS_SEC_PARAM_HIGH);
/* check whether sizes are sufficient */
if (e && e->prime) {
if (e->prime->size*8 < (unsigned)bits)
ret = 0;
} else if (gnutls_dh_get_prime_bits(session) < bits)
ret = 0;
} else
#endif
if (algorithm == GNUTLS_KX_ECDHE_RSA || algorithm == GNUTLS_KX_ECDHE_ECDSA) {
bits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_EC, GNUTLS_SEC_PARAM_HIGH);
if (e != NULL && gnutls_ecc_curve_get_size(e->curve) * 8 < bits)
ret = 0;
}
}
return ret;
}
bool _gnutls_kx_needs_dh_params(gnutls_kx_algorithm_t algorithm)
{
ssize_t ret = 0;
GNUTLS_KX_ALG_LOOP(ret = p->needs_dh_params);
return ret;
}
/* Returns the credentials type required for this
* Key exchange method.
*/
gnutls_credentials_type_t
_gnutls_map_kx_get_cred(gnutls_kx_algorithm_t algorithm, int server)
{
gnutls_credentials_type_t ret = -1;
if (server) {
GNUTLS_KX_MAP_LOOP(if (p->algorithm == algorithm) ret =
p->server_type);
} else {
GNUTLS_KX_MAP_LOOP(if (p->algorithm == algorithm) ret =
p->client_type);
}
return ret;
}