diff options
Diffstat (limited to '')
-rw-r--r-- | lib/dh-session.c | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/lib/dh-session.c b/lib/dh-session.c new file mode 100644 index 0000000..accc71c --- /dev/null +++ b/lib/dh-session.c @@ -0,0 +1,393 @@ +/* + * 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 <https://www.gnu.org/licenses/> + * + */ + +/* This file contains certificate authentication functions to be exported in the + * API which did not fit elsewhere. + */ + +#include "gnutls_int.h" +#include <auth/srp_kx.h> +#include <auth/anon.h> +#include <auth/cert.h> +#include <auth/psk.h> +#include "errors.h" +#include <auth.h> +#include <state.h> +#include <datum.h> +#include <algorithms.h> + +/* 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.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 */ + |