From: Nikos Mavrogiannopoulos Date: Wed, 8 Oct 2014 12:09:30 +0000 (+0200) Subject: Added support for the extended master secret calculation X-Git-Tag: gnutls_3_4_0~818 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=105b694ef48402f5285eda3f95e2e64e3571610e;p=thirdparty%2Fgnutls.git Added support for the extended master secret calculation That is performed implicitly unless GNUTLS_NO_EXTENSIONS is specified. The implementation follows draft-ietf-tls-session-hash-02. --- diff --git a/lib/ext/Makefile.am b/lib/ext/Makefile.am index b532f7d6bc..9b7012bed2 100644 --- a/lib/ext/Makefile.am +++ b/lib/ext/Makefile.am @@ -39,8 +39,8 @@ libgnutls_ext_la_SOURCES = max_record.c cert_type.c \ max_record.h cert_type.h server_name.h srp.h \ session_ticket.h signature.h safe_renegotiation.h \ session_ticket.c srp.c ecc.c ecc.h heartbeat.c heartbeat.h \ - status_request.h status_request.c \ - dumbfw.c dumbfw.h + status_request.h status_request.c dumbfw.c dumbfw.h \ + ext_master_secret.c ext_master_secret.h if ENABLE_ALPN libgnutls_ext_la_SOURCES += alpn.c alpn.h diff --git a/lib/ext/ext_master_secret.c b/lib/ext/ext_master_secret.c new file mode 100644 index 0000000000..0fc5d28331 --- /dev/null +++ b/lib/ext/ext_master_secret.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2014 Red Hat, 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 + * + */ + +/* This file contains the code for the Max Record Size TLS extension. + */ + +#include "gnutls_int.h" +#include "gnutls_errors.h" +#include "gnutls_num.h" +#include +#include + +static int _gnutls_ext_master_secret_recv_params(gnutls_session_t session, + const uint8_t * data, + size_t data_size); +static int _gnutls_ext_master_secret_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata); + +extension_entry_st ext_mod_ext_master_secret = { + .name = "EXT MASTER SECRET", + .type = GNUTLS_EXTENSION_EXT_MASTER_SECRET, + .parse_type = GNUTLS_EXT_TLS, + + .recv_func = _gnutls_ext_master_secret_recv_params, + .send_func = _gnutls_ext_master_secret_send_params, + .pack_func = NULL, + .unpack_func = NULL, + .deinit_func = NULL +}; + +/* + * In case of a server: if an EXT_MASTER_SECRET extension type is received then it + * sets a flag into the session security parameters. + * + */ +static int +_gnutls_ext_master_secret_recv_params(gnutls_session_t session, + const uint8_t * data, size_t _data_size) +{ + ssize_t data_size = _data_size; + + if (session->internals.try_ext_master_secret == 0) { + return 0; + } + + if (data_size != 0) { + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + } + session->security_parameters.ext_master_secret = 1; + + return 0; +} + +/* returns data_size or a negative number on failure + */ +static int +_gnutls_ext_master_secret_send_params(gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + if (session->internals.try_ext_master_secret == 0) { + return 0; + } + + /* this function sends the client extension data */ + if (session->security_parameters.entity == GNUTLS_CLIENT) { + return GNUTLS_E_INT_RET_0; + } else { /* server side */ + if (session->security_parameters.ext_master_secret != 0) + return GNUTLS_E_INT_RET_0; + } + + return 0; +} + +/** + * gnutls_session_ext_master_secret_status: + * @session: is a #gnutls_session_t structure. + * + * Get the status of the extended master secret extension negotiation. + * This is in accordance to draft-ietf-tls-session-hash-01 + * + * Returns: Non-zero if the negotiation was successful or zero otherwise. + **/ +unsigned gnutls_session_ext_master_secret_status(gnutls_session_t session) +{ + return session->security_parameters.ext_master_secret; +} + diff --git a/lib/ext/ext_master_secret.h b/lib/ext/ext_master_secret.h new file mode 100644 index 0000000000..2cdb29538b --- /dev/null +++ b/lib/ext/ext_master_secret.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 Red Hat, 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 + * + */ + +#ifndef EXT_EXT_MASTER_SECRET_H +#define EXT_EXT_MASTER_SECRET_H + +#include + +extern extension_entry_st ext_mod_ext_master_secret; + +#endif diff --git a/lib/gnutls_constate.c b/lib/gnutls_constate.c index 97c538d34d..e9b9678f42 100644 --- a/lib/gnutls_constate.c +++ b/lib/gnutls_constate.c @@ -388,6 +388,7 @@ int _gnutls_epoch_set_keys(gnutls_session_t session, uint16_t epoch) dst->cert_type = src->cert_type; \ dst->compression_method = src->compression_method; \ dst->timestamp = src->timestamp; \ + dst->ext_master_secret = src->ext_master_secret; \ dst->max_record_recv_size = src->max_record_recv_size; \ dst->max_record_send_size = src->max_record_send_size diff --git a/lib/gnutls_extensions.c b/lib/gnutls_extensions.c index 36ac43e8ee..e8cf539f21 100644 --- a/lib/gnutls_extensions.c +++ b/lib/gnutls_extensions.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -311,6 +312,10 @@ int _gnutls_ext_init(void) if (ret != GNUTLS_E_SUCCESS) return ret; + ret = _gnutls_ext_register(&ext_mod_ext_master_secret); + if (ret != GNUTLS_E_SUCCESS) + return ret; + #ifdef ENABLE_OCSP ret = _gnutls_ext_register(&ext_mod_status_request); if (ret != GNUTLS_E_SUCCESS) diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c index 28e4a7eafe..9ddcfbce3e 100644 --- a/lib/gnutls_handshake.c +++ b/lib/gnutls_handshake.c @@ -3571,3 +3571,69 @@ gnutls_handshake_get_last_out(gnutls_session_t session) { return session->internals.last_handshake_out; } + +/* This returns the session hash as in draft-ietf-tls-session-hash-02. + * + * FIXME: It duplicates some of the actions in _gnutls_handshake_sign_crt_vrfy*. + * See whether they can be merged. + */ +int _gnutls_handshake_get_session_hash(gnutls_session_t session, gnutls_datum_t *shash) +{ + const version_entry_st *ver = get_version(session); + int ret; + const mac_entry_st *me; + uint8_t concat[2*MAX_HASH_SIZE]; + digest_hd_st td_md5; + digest_hd_st td_sha; + + if (unlikely(ver == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + if (_gnutls_version_has_selectable_prf(ver)) { /* TLS 1.2+ */ + gnutls_mac_algorithm_t prf; + + prf = _gnutls_cipher_suite_get_prf(session->security_parameters.cipher_suite); + if (prf == GNUTLS_MAC_UNKNOWN) + return gnutls_assert_val(GNUTLS_E_UNKNOWN_PK_ALGORITHM); + + me = mac_to_entry(prf); + + ret = + _gnutls_hash_fast((gnutls_digest_algorithm_t)me->id, + session->internals.handshake_hash_buffer. + data, + session->internals.handshake_hash_buffer. + length, concat); + if (ret < 0) + return gnutls_assert_val(ret); + + return _gnutls_set_datum(shash, concat, me->output_size); + } else { + ret = _gnutls_hash_init(&td_sha, hash_to_entry(GNUTLS_DIG_SHA1)); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + _gnutls_hash(&td_sha, + session->internals.handshake_hash_buffer.data, + session->internals.handshake_hash_buffer.length); + + _gnutls_hash_deinit(&td_sha, &concat[16]); + + ret = + _gnutls_hash_init(&td_md5, + hash_to_entry(GNUTLS_DIG_MD5)); + if (ret < 0) + return gnutls_assert_val(ret); + + _gnutls_hash(&td_md5, + session->internals.handshake_hash_buffer.data, + session->internals.handshake_hash_buffer. + length); + + _gnutls_hash_deinit(&td_md5, concat); + + return _gnutls_set_datum(shash, concat, 36); + } +} diff --git a/lib/gnutls_handshake.h b/lib/gnutls_handshake.h index 88a4169b6d..65b190238d 100644 --- a/lib/gnutls_handshake.h +++ b/lib/gnutls_handshake.h @@ -71,4 +71,6 @@ inline static int handshake_remaining_time(gnutls_session_t session) return 0; } +int _gnutls_handshake_get_session_hash(gnutls_session_t session, gnutls_datum_t *shash); + #endif diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 106cd73b25..c6e541fe1e 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -285,8 +285,8 @@ typedef enum extensions_t { GNUTLS_EXTENSION_HEARTBEAT = 15, GNUTLS_EXTENSION_ALPN = 16, GNUTLS_EXTENSION_DUMBFW = 21, + GNUTLS_EXTENSION_EXT_MASTER_SECRET = 23, GNUTLS_EXTENSION_SESSION_TICKET = 35, - GNUTLS_EXTENSION_NEW_RECORD_PADDING = 48015, /* aka: 0xbeaf */ GNUTLS_EXTENSION_SAFE_RENEGOTIATION = 65281 /* aka: 0xff01 */ } extensions_t; @@ -529,10 +529,6 @@ typedef struct { * session. */ -/* if you add anything in Security_Parameters struct, then - * also modify CPY_COMMON in gnutls_constate.c. - */ - /* Note that the security parameters structure is set up after the * handshake has finished. The only value you may depend on while * the handshake is in progress is the cipher suite value. @@ -576,9 +572,15 @@ typedef struct { /* Holds the signature algorithm used in this session - If any */ gnutls_sign_algorithm_t server_sign_algo; gnutls_sign_algorithm_t client_sign_algo; - - /* FIXME: The following are not saved in the session storage - * for session resumption. + + /* Whether the master secret negotiation will be according to + * draft-ietf-tls-session-hash-01 + */ + uint8_t ext_master_secret; + + /* Note: if you add anything in Security_Parameters struct, then + * also modify CPY_COMMON in gnutls_constate.c, and gnutls_session_pack.c, + * in order to save it in the session storage. */ /* Used by extensions that enable supplemental data: Which ones @@ -994,6 +996,7 @@ typedef struct { bool sc_random_set; bool no_replay_protection; /* DTLS replay protection */ + bool try_ext_master_secret; /* whether to try negotiating the ext master secret */ /* If you add anything here, check _gnutls_handshake_internal_state_clear(). */ diff --git a/lib/gnutls_kx.c b/lib/gnutls_kx.c index 04e7a69593..d6d41a6ea8 100644 --- a/lib/gnutls_kx.c +++ b/lib/gnutls_kx.c @@ -70,6 +70,10 @@ send_handshake(gnutls_session_t session, uint8_t * data, size_t size, #define MASTER_SECRET "master secret" #define MASTER_SECRET_SIZE (sizeof(MASTER_SECRET)-1) + +#define EXT_MASTER_SECRET "extended master secret" +#define EXT_MASTER_SECRET_SIZE (sizeof(EXT_MASTER_SECRET)-1) + static int generate_normal_master(gnutls_session_t session, gnutls_datum_t *, int); @@ -114,39 +118,55 @@ generate_normal_master(gnutls_session_t session, server_random, 32, buf, sizeof(buf), NULL)); - if (get_num_version(session) == GNUTLS_SSL3) { + if (session->security_parameters.ext_master_secret == 0) { uint8_t rnd[2 * GNUTLS_RANDOM_SIZE + 1]; - memcpy(rnd, session->security_parameters.client_random, GNUTLS_RANDOM_SIZE); memcpy(&rnd[GNUTLS_RANDOM_SIZE], session->security_parameters.server_random, GNUTLS_RANDOM_SIZE); - ret = - _gnutls_ssl3_generate_random(premaster->data, - premaster->size, rnd, - 2 * GNUTLS_RANDOM_SIZE, - GNUTLS_MASTER_SIZE, - session->security_parameters. - master_secret); - + if (get_num_version(session) == GNUTLS_SSL3) { + ret = + _gnutls_ssl3_generate_random(premaster->data, + premaster->size, rnd, + 2 * GNUTLS_RANDOM_SIZE, + GNUTLS_MASTER_SIZE, + session->security_parameters. + master_secret); + } else { + ret = + _gnutls_PRF(session, premaster->data, premaster->size, + MASTER_SECRET, MASTER_SECRET_SIZE, + rnd, 2 * GNUTLS_RANDOM_SIZE, + GNUTLS_MASTER_SIZE, + session->security_parameters. + master_secret); + } } else { - uint8_t rnd[2 * GNUTLS_RANDOM_SIZE + 1]; - - memcpy(rnd, session->security_parameters.client_random, - GNUTLS_RANDOM_SIZE); - memcpy(&rnd[GNUTLS_RANDOM_SIZE], - session->security_parameters.server_random, - GNUTLS_RANDOM_SIZE); + gnutls_datum_t shash = {NULL, 0}; - ret = - _gnutls_PRF(session, premaster->data, premaster->size, - MASTER_SECRET, MASTER_SECRET_SIZE, - rnd, 2 * GNUTLS_RANDOM_SIZE, - GNUTLS_MASTER_SIZE, - session->security_parameters. - master_secret); + /* draft-ietf-tls-session-hash-02 */ + ret = _gnutls_handshake_get_session_hash(session, &shash); + if (ret < 0) + return gnutls_assert_val(ret); + if (get_num_version(session) == GNUTLS_SSL3) { + ret = + _gnutls_ssl3_generate_random(premaster->data, + premaster->size, shash.data, shash.size, + GNUTLS_MASTER_SIZE, + session->security_parameters. + master_secret); + } else { + ret = + _gnutls_PRF(session, premaster->data, premaster->size, + EXT_MASTER_SECRET, EXT_MASTER_SECRET_SIZE, + shash.data, shash.size, + GNUTLS_MASTER_SIZE, + session->security_parameters. + master_secret); + } + gnutls_free(shash.data); } if (!keep_premaster) diff --git a/lib/gnutls_session_pack.c b/lib/gnutls_session_pack.c index 7f4111d1a6..5d63a1a0a8 100644 --- a/lib/gnutls_session_pack.c +++ b/lib/gnutls_session_pack.c @@ -768,6 +768,8 @@ pack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps) session->security_parameters.server_sign_algo); BUFFER_APPEND_NUM(ps, session->security_parameters.client_sign_algo); + BUFFER_APPEND_NUM(ps, + session->security_parameters.ext_master_secret); _gnutls_write_uint32(ps->length - cur_size, ps->data + size_offset); @@ -855,6 +857,9 @@ unpack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps) BUFFER_POP_NUM(ps, session->internals.resumed_security_parameters. client_sign_algo); + BUFFER_POP_NUM(ps, + session->internals.resumed_security_parameters. + ext_master_secret); if (session->internals.resumed_security_parameters. max_record_recv_size == 0 diff --git a/lib/gnutls_state.c b/lib/gnutls_state.c index a05c77e7ff..ee2cccbc7c 100644 --- a/lib/gnutls_state.c +++ b/lib/gnutls_state.c @@ -413,6 +413,9 @@ int gnutls_init(gnutls_session_t * session, unsigned int flags) #endif } + if (!(flags & GNUTLS_NO_EXTENSIONS)) + (*session)->internals.try_ext_master_secret = 1; + if (flags & GNUTLS_NO_REPLAY_PROTECTION) (*session)->internals.no_replay_protection = 1; diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index fbc26f3960..0647802d67 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -1022,6 +1022,7 @@ int gnutls_heartbeat_allowed(gnutls_session_t session, unsigned int type); /* Safe renegotiation */ int gnutls_safe_renegotiation_status(gnutls_session_t session); +unsigned gnutls_session_ext_master_secret_status(gnutls_session_t session); /** * gnutls_supplemental_data_format_type_t: diff --git a/lib/libgnutls.map b/lib/libgnutls.map index a081b5c88e..f53e06fff2 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1048,6 +1048,7 @@ GNUTLS_3_1_0 { gnutls_x509_crq_get_signature_algorithm; gnutls_privkey_import_ext3; gnutls_record_discard_queued; + gnutls_session_ext_master_secret_status; } GNUTLS_3_0_0; GNUTLS_FIPS140 {