/*
* Copyright (C) 2001-2015 Free Software Foundation, Inc.
* Copyright (C) 2015 Nikos Mavrogiannopoulos
*
* 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
*
*/
/* This file contains certificate authentication functions to be exported in the
* API which did not fit elsewhere.
*/
#include "gnutls_int.h"
#include
#include
#include
#include
#include "errors.h"
#include
#include
#include
#include
/* ANON & DHE */
#if defined(ENABLE_DHE) || defined(ENABLE_ANON)
/**
* gnutls_dh_set_prime_bits:
* @session: is a #gnutls_session_t type.
* @bits: is the number of bits
*
* This function sets the number of bits, for use in a Diffie-Hellman
* key exchange. This is used both in DH ephemeral and DH anonymous
* cipher suites. This will set the minimum size of the prime that
* will be used for the handshake.
*
* In the client side it sets the minimum accepted number of bits. If
* a server sends a prime with less bits than that
* %GNUTLS_E_DH_PRIME_UNACCEPTABLE will be returned by the handshake.
*
* Note that this function will warn via the audit log for value that
* are believed to be weak.
*
* The function has no effect in server side.
*
* Note that since 3.1.7 this function is deprecated. The minimum
* number of bits is set by the priority string level.
* Also this function must be called after gnutls_priority_set_direct()
* or the set value may be overridden by the selected priority options.
*
*
**/
void gnutls_dh_set_prime_bits(gnutls_session_t session, unsigned int bits)
{
if (bits < gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, GNUTLS_SEC_PARAM_WEAK)
&& bits != 0)
_gnutls_audit_log(session,
"Note that the security level of the Diffie-Hellman key exchange has been lowered to %u bits and this may allow decryption of the session data\n",
bits);
session->internals.priorities.dh_prime_bits = bits;
}
/**
* gnutls_dh_get_group:
* @session: is a gnutls session
* @raw_gen: will hold the generator.
* @raw_prime: will hold the prime.
*
* This function will return the group parameters used in the last
* Diffie-Hellman key exchange with the peer. These are the prime and
* the generator used. This function should be used for both
* anonymous and ephemeral Diffie-Hellman. The output parameters must
* be freed with gnutls_free().
*
* Note, that the prime and generator are exported as non-negative
* integers and may include a leading zero byte.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
* an error code is returned.
**/
int
gnutls_dh_get_group(gnutls_session_t session,
gnutls_datum_t * raw_gen, gnutls_datum_t * raw_prime)
{
dh_info_st *dh;
int ret;
anon_auth_info_t anon_info;
cert_auth_info_t cert_info;
psk_auth_info_t psk_info;
switch (gnutls_auth_get_type(session)) {
case GNUTLS_CRD_ANON:
anon_info = _gnutls_get_auth_info(session, GNUTLS_CRD_ANON);
if (anon_info == NULL)
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
dh = &anon_info->dh;
break;
case GNUTLS_CRD_PSK:
psk_info = _gnutls_get_auth_info(session, GNUTLS_CRD_PSK);
if (psk_info == NULL)
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
dh = &psk_info->dh;
break;
case GNUTLS_CRD_CERTIFICATE:
cert_info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
if (cert_info == NULL)
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
dh = &cert_info->dh;
break;
default:
gnutls_assert();
return GNUTLS_E_INVALID_REQUEST;
}
ret = _gnutls_set_datum(raw_prime, dh->prime.data, dh->prime.size);
if (ret < 0) {
gnutls_assert();
return ret;
}
ret =
_gnutls_set_datum(raw_gen, dh->generator.data,
dh->generator.size);
if (ret < 0) {
gnutls_assert();
_gnutls_free_datum(raw_prime);
return ret;
}
return 0;
}
/**
* gnutls_dh_get_pubkey:
* @session: is a gnutls session
* @raw_key: will hold the public key.
*
* This function will return the peer's public key used in the last
* Diffie-Hellman key exchange. This function should be used for both
* anonymous and ephemeral Diffie-Hellman. The output parameters must
* be freed with gnutls_free().
*
* Note, that public key is exported as non-negative
* integer and may include a leading zero byte.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
* an error code is returned.
**/
int
gnutls_dh_get_pubkey(gnutls_session_t session, gnutls_datum_t * raw_key)
{
dh_info_st *dh;
anon_auth_info_t anon_info;
cert_auth_info_t cert_info;
psk_auth_info_t psk_info;
switch (gnutls_auth_get_type(session)) {
case GNUTLS_CRD_ANON:
{
anon_info = _gnutls_get_auth_info(session, GNUTLS_CRD_ANON);
if (anon_info == NULL)
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
dh = &anon_info->dh;
break;
}
case GNUTLS_CRD_PSK:
{
psk_info = _gnutls_get_auth_info(session, GNUTLS_CRD_PSK);
if (psk_info == NULL)
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
dh = &psk_info->dh;
break;
}
case GNUTLS_CRD_CERTIFICATE:
{
cert_info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
if (cert_info == NULL)
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
dh = &cert_info->dh;
break;
}
default:
gnutls_assert();
return GNUTLS_E_INVALID_REQUEST;
}
return _gnutls_set_datum(raw_key, dh->public_key.data,
dh->public_key.size);
}
/**
* gnutls_dh_get_secret_bits:
* @session: is a gnutls session
*
* This function will return the bits used in the last Diffie-Hellman
* key exchange with the peer. Should be used for both anonymous and
* ephemeral Diffie-Hellman.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
* an error code is returned.
**/
int gnutls_dh_get_secret_bits(gnutls_session_t session)
{
switch (gnutls_auth_get_type(session)) {
case GNUTLS_CRD_ANON:
{
anon_auth_info_t info;
info = _gnutls_get_auth_info(session, GNUTLS_CRD_ANON);
if (info == NULL)
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
return info->dh.secret_bits;
}
case GNUTLS_CRD_PSK:
{
psk_auth_info_t info;
info = _gnutls_get_auth_info(session, GNUTLS_CRD_PSK);
if (info == NULL)
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
return info->dh.secret_bits;
}
case GNUTLS_CRD_CERTIFICATE:
{
cert_auth_info_t info;
info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
if (info == NULL)
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
return info->dh.secret_bits;
}
default:
gnutls_assert();
return GNUTLS_E_INVALID_REQUEST;
}
}
static int mpi_buf2bits(gnutls_datum_t * mpi_buf)
{
bigint_t mpi;
int rc;
rc = _gnutls_mpi_init_scan_nz(&mpi, mpi_buf->data, mpi_buf->size);
if (rc) {
gnutls_assert();
return rc;
}
rc = _gnutls_mpi_get_nbits(mpi);
_gnutls_mpi_release(&mpi);
return rc;
}
/**
* gnutls_dh_get_prime_bits:
* @session: is a gnutls session
*
* This function will return the bits of the prime used in the last
* Diffie-Hellman key exchange with the peer. Should be used for both
* anonymous and ephemeral Diffie-Hellman. Note that some ciphers,
* like RSA and DSA without DHE, do not use a Diffie-Hellman key
* exchange, and then this function will return 0.
*
* Returns: The Diffie-Hellman bit strength is returned, or 0 if no
* Diffie-Hellman key exchange was done, or a negative error code on
* failure.
**/
int gnutls_dh_get_prime_bits(gnutls_session_t session)
{
dh_info_st *dh;
switch (gnutls_auth_get_type(session)) {
case GNUTLS_CRD_ANON:
{
anon_auth_info_t info;
info = _gnutls_get_auth_info(session, GNUTLS_CRD_ANON);
if (info == NULL)
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
dh = &info->dh;
break;
}
case GNUTLS_CRD_PSK:
{
psk_auth_info_t info;
info = _gnutls_get_auth_info(session, GNUTLS_CRD_PSK);
if (info == NULL)
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
dh = &info->dh;
break;
}
case GNUTLS_CRD_CERTIFICATE:
{
cert_auth_info_t info;
info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
if (info == NULL)
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
dh = &info->dh;
break;
}
default:
gnutls_assert();
return GNUTLS_E_INVALID_REQUEST;
}
if(dh->prime.size == 0)
return 0;
return mpi_buf2bits(&dh->prime);
}
/**
* gnutls_dh_get_peers_public_bits:
* @session: is a gnutls session
*
* Get the Diffie-Hellman public key bit size. Can be used for both
* anonymous and ephemeral Diffie-Hellman.
*
* Returns: The public key bit size used in the last Diffie-Hellman
* key exchange with the peer, or a negative error code in case of error.
**/
int gnutls_dh_get_peers_public_bits(gnutls_session_t session)
{
dh_info_st *dh;
switch (gnutls_auth_get_type(session)) {
case GNUTLS_CRD_ANON:
{
anon_auth_info_t info;
info = _gnutls_get_auth_info(session, GNUTLS_CRD_ANON);
if (info == NULL)
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
dh = &info->dh;
break;
}
case GNUTLS_CRD_PSK:
{
psk_auth_info_t info;
info = _gnutls_get_auth_info(session, GNUTLS_CRD_PSK);
if (info == NULL)
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
dh = &info->dh;
break;
}
case GNUTLS_CRD_CERTIFICATE:
{
cert_auth_info_t info;
info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
if (info == NULL)
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
dh = &info->dh;
break;
}
default:
gnutls_assert();
return GNUTLS_E_INVALID_REQUEST;
}
return mpi_buf2bits(&dh->public_key);
}
#endif /* DH */