summaryrefslogtreecommitdiffstats
path: root/lib/tls13/finished.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tls13/finished.c')
-rw-r--r--lib/tls13/finished.c183
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;
+}