diff options
Diffstat (limited to 'lib/tls13/finished.c')
-rw-r--r-- | lib/tls13/finished.c | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/lib/tls13/finished.c b/lib/tls13/finished.c new file mode 100644 index 0000000..ec646e6 --- /dev/null +++ b/lib/tls13/finished.c @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2017 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 <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include "errors.h" +#include "handshake.h" +#include "tls13/finished.h" +#include "mem.h" +#include "mbuffers.h" +#include "secrets.h" + +int _gnutls13_compute_finished(const mac_entry_st *prf, + const uint8_t *base_key, + gnutls_buffer_st *handshake_hash_buffer, + void *out) +{ + int ret; + uint8_t fkey[MAX_HASH_SIZE]; + uint8_t ts_hash[MAX_HASH_SIZE]; + + ret = _tls13_expand_secret2(prf, + "finished", 8, + NULL, 0, + base_key, + prf->output_size, fkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_hash_fast(MAC_TO_DIG(prf->id), + handshake_hash_buffer->data, + handshake_hash_buffer->length, + ts_hash); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_hmac_fast(prf->id, + fkey, prf->output_size, + ts_hash, prf->output_size, + out); + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; +} + +int _gnutls13_recv_finished(gnutls_session_t session) +{ + int ret; + gnutls_buffer_st buf; + uint8_t verifier[MAX_HASH_SIZE]; + const uint8_t *base_key; + unsigned hash_size; + + if (unlikely(session->security_parameters.prf == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + hash_size = session->security_parameters.prf->output_size; + + if (!session->internals.initial_negotiation_completed) { + if (session->security_parameters.entity == GNUTLS_CLIENT) + base_key = session->key.proto.tls13.hs_skey; + else + base_key = session->key.proto.tls13.hs_ckey; + } else { + if (session->security_parameters.entity == GNUTLS_CLIENT) + base_key = session->key.proto.tls13.ap_skey; + else + base_key = session->key.proto.tls13.ap_ckey; + } + + ret = _gnutls13_compute_finished(session->security_parameters.prf, + base_key, + &session->internals.handshake_hash_buffer, + verifier); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_FINISHED, 0, &buf); + if (ret < 0) + return gnutls_assert_val(ret); + + _gnutls_handshake_log("HSK[%p]: parsing finished\n", session); + + if (buf.length != hash_size) { + gnutls_assert(); + ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + goto cleanup; + } + + +#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) +# warning This is unsafe for production builds +#else + if (gnutls_memcmp(verifier, buf.data, buf.length) != 0) { + gnutls_assert(); + ret = GNUTLS_E_ERROR_IN_FINISHED_PACKET; + goto cleanup; + } +#endif + + ret = 0; +cleanup: + + _gnutls_buffer_clear(&buf); + return ret; +} + +int _gnutls13_send_finished(gnutls_session_t session, unsigned again) +{ + int ret; + uint8_t verifier[MAX_HASH_SIZE]; + mbuffer_st *bufel = NULL; + const uint8_t *base_key; + unsigned hash_size; + + if (again == 0) { + if (unlikely(session->security_parameters.prf == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + hash_size = session->security_parameters.prf->output_size; + + if (!session->internals.initial_negotiation_completed) { + if (session->security_parameters.entity == GNUTLS_CLIENT) + base_key = session->key.proto.tls13.hs_ckey; + else + base_key = session->key.proto.tls13.hs_skey; + } else { + if (session->security_parameters.entity == GNUTLS_CLIENT) + base_key = session->key.proto.tls13.ap_ckey; + else + base_key = session->key.proto.tls13.ap_skey; + } + + ret = _gnutls13_compute_finished(session->security_parameters.prf, + base_key, + &session->internals.handshake_hash_buffer, + verifier); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + _gnutls_handshake_log("HSK[%p]: sending finished\n", session); + + bufel = _gnutls_handshake_alloc(session, hash_size); + if (bufel == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + _mbuffer_set_udata_size(bufel, 0); + ret = _mbuffer_append_data(bufel, verifier, hash_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_FINISHED); + +cleanup: + _mbuffer_xfree(&bufel); + return ret; +} |