/*
 * Copyright (C) 2000-2016 Free Software Foundation, Inc.
 * Copyright (C) 2015-2018 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/>
 *
 */

/* Functions that relate to the TLS handshake procedure.
 */

#include "gnutls_int.h"
#include "errors.h"
#include "dh.h"
#include "debug.h"
#include "algorithms.h"
#include "cipher.h"
#include "buffers.h"
#include "mbuffers.h"
#include "kx.h"
#include "handshake.h"
#include "num.h"
#include "hash_int.h"
#include "db.h"
#include "hello_ext.h"
#include "supplemental.h"
#include "auth.h"
#include "sslv2_compat.h"
#include "auth/cert.h"
#include "constate.h"
#include "record.h"
#include "state.h"
#include "ext/pre_shared_key.h"
#include "ext/srp.h"
#include "ext/session_ticket.h"
#include "ext/status_request.h"
#include "ext/safe_renegotiation.h"
#include "auth/anon.h" /* for gnutls_anon_server_credentials_t */
#include "auth/psk.h" /* for gnutls_psk_server_credentials_t */
#include "random.h"
#include "dtls.h"
#include "secrets.h"
#include "tls13/early_data.h"
#include "tls13/session_ticket.h"
#include "locks.h"
#include "system/ktls.h"

static int check_if_null_comp_present(gnutls_session_t session, uint8_t *data,
				      int datalen);
static int handshake_client(gnutls_session_t session);
static int handshake_server(gnutls_session_t session);

static int read_server_hello(gnutls_session_t session, uint8_t *data,
			     int datalen);

static int recv_handshake_final(gnutls_session_t session, int init);
static int send_handshake_final(gnutls_session_t session, int init);

/* Empties but does not free the buffer
 */
inline static void handshake_hash_buffer_reset(gnutls_session_t session)
{
	_gnutls_buffers_log("BUF[HSK]: Emptied buffer\n");

	session->internals.handshake_hash_buffer_client_hello_len = 0;
	session->internals.handshake_hash_buffer_client_kx_len = 0;
	session->internals.handshake_hash_buffer_server_finished_len = 0;
	session->internals.handshake_hash_buffer_client_finished_len = 0;
	session->internals.handshake_hash_buffer_prev_len = 0;
	session->internals.handshake_hash_buffer.length = 0;
	session->internals.full_client_hello.length = 0;
	return;
}

static int handshake_hash_add_recvd(gnutls_session_t session,
				    gnutls_handshake_description_t recv_type,
				    uint8_t *header, uint16_t header_size,
				    uint8_t *dataptr, uint32_t datalen);

static int handshake_hash_add_sent(gnutls_session_t session,
				   gnutls_handshake_description_t type,
				   uint8_t *dataptr, uint32_t datalen);

static int recv_hello_verify_request(gnutls_session_t session, uint8_t *data,
				     int datalen);

/* Clears the handshake hash buffers and handles.
 */
void _gnutls_handshake_hash_buffers_clear(gnutls_session_t session)
{
	handshake_hash_buffer_reset(session);
	_gnutls_buffer_clear(&session->internals.handshake_hash_buffer);
	_gnutls_buffer_clear(&session->internals.full_client_hello);
}

/* Replace handshake message buffer, with the special synthetic message
 * needed by TLS1.3 when HRR is sent. */
int _gnutls13_handshake_hash_buffers_synth(gnutls_session_t session,
					   const mac_entry_st *prf,
					   unsigned client)
{
	int ret;
	uint8_t hdata[4 + MAX_HASH_SIZE];
	size_t length;

	if (client)
		length = session->internals.handshake_hash_buffer_prev_len;
	else
		length = session->internals.handshake_hash_buffer.length;

	/* calculate hash */
	hdata[0] = 254;
	_gnutls_write_uint24(prf->output_size, &hdata[1]);

	ret = gnutls_hash_fast((gnutls_digest_algorithm_t)prf->id,
			       session->internals.handshake_hash_buffer.data,
			       length, hdata + 4);
	if (ret < 0)
		return gnutls_assert_val(ret);

	handshake_hash_buffer_reset(session);

	ret = _gnutls_buffer_append_data(
		&session->internals.handshake_hash_buffer, hdata,
		prf->output_size + 4);
	if (ret < 0)
		return gnutls_assert_val(ret);

	_gnutls_buffers_log(
		"BUF[HSK]: Replaced handshake buffer with synth message (%d bytes)\n",
		prf->output_size + 4);

	return 0;
}

/* this will copy the required values for resuming to
 * internals, and to security_parameters.
 * this will keep as less data to security_parameters.
 */
static int tls12_resume_copy_required_vals(gnutls_session_t session,
					   unsigned ticket)
{
	int ret;

	/* get the new random values */
	memcpy(session->internals.resumed_security_parameters.server_random,
	       session->security_parameters.server_random, GNUTLS_RANDOM_SIZE);
	memcpy(session->internals.resumed_security_parameters.client_random,
	       session->security_parameters.client_random, GNUTLS_RANDOM_SIZE);

	/* keep the ciphersuite and compression
	 * That is because the client must see these in our
	 * hello message.
	 */
	ret = _gnutls_set_cipher_suite2(
		session, session->internals.resumed_security_parameters.cs);
	if (ret < 0)
		return gnutls_assert_val(ret);

	/* or write_compression_algorithm
	 * they are the same
	 */

	session->security_parameters.entity =
		session->internals.resumed_security_parameters.entity;

	if (session->internals.resumed_security_parameters.pversion == NULL)
		return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);

	if (_gnutls_set_current_version(
		    session, session->internals.resumed_security_parameters
				     .pversion->id) < 0)
		return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);

	session->security_parameters.client_ctype =
		session->internals.resumed_security_parameters.client_ctype;
	session->security_parameters.server_ctype =
		session->internals.resumed_security_parameters.server_ctype;

	if (!ticket) {
		memcpy(session->security_parameters.session_id,
		       session->internals.resumed_security_parameters.session_id,
		       sizeof(session->security_parameters.session_id));
		session->security_parameters.session_id_size =
			session->internals.resumed_security_parameters
				.session_id_size;
	}

	return 0;
}

void _gnutls_set_client_random(gnutls_session_t session, uint8_t *rnd)
{
	_gnutls_memory_mark_defined(session->security_parameters.client_random,
				    GNUTLS_RANDOM_SIZE);
	memcpy(session->security_parameters.client_random, rnd,
	       GNUTLS_RANDOM_SIZE);
}

static int _gnutls_gen_client_random(gnutls_session_t session)
{
	int ret;

	/* no random given, we generate. */
	if (session->internals.sc_random_set != 0) {
		_gnutls_memory_mark_defined(
			session->security_parameters.client_random,
			GNUTLS_RANDOM_SIZE);
		memcpy(session->security_parameters.client_random,
		       session->internals.resumed_security_parameters
			       .client_random,
		       GNUTLS_RANDOM_SIZE);
	} else {
		_gnutls_memory_mark_defined(
			session->security_parameters.client_random,
			GNUTLS_RANDOM_SIZE);
		ret = gnutls_rnd(GNUTLS_RND_NONCE,
				 session->security_parameters.client_random,
				 GNUTLS_RANDOM_SIZE);
		if (ret < 0) {
			_gnutls_memory_mark_undefined(
				session->security_parameters.client_random,
				GNUTLS_RANDOM_SIZE);
			return gnutls_assert_val(ret);
		}
	}

	return 0;
}

static int _gnutls_set_server_random(gnutls_session_t session,
				     const version_entry_st *vers, uint8_t *rnd)
{
	const version_entry_st *max;

	_gnutls_memory_mark_defined(session->security_parameters.server_random,
				    GNUTLS_RANDOM_SIZE);
	memcpy(session->security_parameters.server_random, rnd,
	       GNUTLS_RANDOM_SIZE);

	/* check whether the server random value is set according to
	 * to TLS 1.3. p4.1.3 requirements */
	if (!IS_DTLS(session) && vers->id <= GNUTLS_TLS1_2 &&
	    have_creds_for_tls13(session)) {
		max = _gnutls_version_max(session);
		if (max->id <= GNUTLS_TLS1_2)
			return 0;

		if (vers->id == GNUTLS_TLS1_2 &&
		    memcmp(&session->security_parameters
				    .server_random[GNUTLS_RANDOM_SIZE - 8],
			   "\x44\x4F\x57\x4E\x47\x52\x44\x01", 8) == 0) {
			_gnutls_audit_log(
				session,
				"Detected downgrade to TLS 1.2 from TLS 1.3\n");
			return gnutls_assert_val(
				GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
		} else if (vers->id <= GNUTLS_TLS1_1 &&
			   memcmp(&session->security_parameters
					   .server_random[GNUTLS_RANDOM_SIZE -
							  8],
				  "\x44\x4F\x57\x4E\x47\x52\x44\x00", 8) == 0) {
			_gnutls_audit_log(
				session,
				"Detected downgrade to TLS 1.1 or earlier from TLS 1.3\n");
			return gnutls_assert_val(
				GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
		}
	}

	return 0;
}

int _gnutls_gen_server_random(gnutls_session_t session, int version)
{
	int ret;
	const version_entry_st *max;

	if (session->internals.sc_random_set != 0) {
		_gnutls_memory_mark_defined(
			session->security_parameters.server_random,
			GNUTLS_RANDOM_SIZE);
		memcpy(session->security_parameters.server_random,
		       session->internals.resumed_security_parameters
			       .server_random,
		       GNUTLS_RANDOM_SIZE);
		return 0;
	}

	max = _gnutls_version_max(session);
	if (max == NULL)
		return gnutls_assert_val(GNUTLS_E_NO_CIPHER_SUITES);

	_gnutls_memory_mark_defined(session->security_parameters.server_random,
				    GNUTLS_RANDOM_SIZE);

	if (!IS_DTLS(session) && max->id >= GNUTLS_TLS1_3 &&
	    version <= GNUTLS_TLS1_2) {
		if (version == GNUTLS_TLS1_2) {
			memcpy(&session->security_parameters
					.server_random[GNUTLS_RANDOM_SIZE - 8],
			       "\x44\x4F\x57\x4E\x47\x52\x44\x01", 8);
		} else {
			memcpy(&session->security_parameters
					.server_random[GNUTLS_RANDOM_SIZE - 8],
			       "\x44\x4F\x57\x4E\x47\x52\x44\x00", 8);
		}
		ret = gnutls_rnd(GNUTLS_RND_NONCE,
				 session->security_parameters.server_random,
				 GNUTLS_RANDOM_SIZE - 8);

	} else {
		ret = gnutls_rnd(GNUTLS_RND_NONCE,
				 session->security_parameters.server_random,
				 GNUTLS_RANDOM_SIZE);
	}

	if (ret < 0) {
		gnutls_assert();
		_gnutls_memory_mark_undefined(
			session->security_parameters.server_random,
			GNUTLS_RANDOM_SIZE);
		return ret;
	}

	return 0;
}

#ifdef ENABLE_SSL3
/* Calculate The SSL3 Finished message
 */
#define SSL3_CLIENT_MSG "CLNT"
#define SSL3_SERVER_MSG "SRVR"
#define SSL_MSG_LEN 4
static int _gnutls_ssl3_finished(gnutls_session_t session, int type,
				 uint8_t *ret, int sending)
{
	digest_hd_st td_md5;
	digest_hd_st td_sha;
	const char *mesg;
	int rc, len;

	if (sending)
		len = session->internals.handshake_hash_buffer.length;
	else
		len = session->internals.handshake_hash_buffer_prev_len;

	rc = _gnutls_hash_init(&td_sha, hash_to_entry(GNUTLS_DIG_SHA1));
	if (rc < 0)
		return gnutls_assert_val(rc);

	rc = _gnutls_hash_init(&td_md5, hash_to_entry(GNUTLS_DIG_MD5));
	if (rc < 0) {
		_gnutls_hash_deinit(&td_sha, NULL);
		return gnutls_assert_val(rc);
	}

	_gnutls_hash(&td_sha, session->internals.handshake_hash_buffer.data,
		     len);
	_gnutls_hash(&td_md5, session->internals.handshake_hash_buffer.data,
		     len);

	if (type == GNUTLS_SERVER)
		mesg = SSL3_SERVER_MSG;
	else
		mesg = SSL3_CLIENT_MSG;

	_gnutls_hash(&td_md5, mesg, SSL_MSG_LEN);
	_gnutls_hash(&td_sha, mesg, SSL_MSG_LEN);

	rc = _gnutls_mac_deinit_ssl3_handshake(
		&td_md5, ret, session->security_parameters.master_secret,
		GNUTLS_MASTER_SIZE);
	if (rc < 0) {
		_gnutls_hash_deinit(&td_md5, NULL);
		_gnutls_hash_deinit(&td_sha, NULL);
		return gnutls_assert_val(rc);
	}

	rc = _gnutls_mac_deinit_ssl3_handshake(
		&td_sha, &ret[16], session->security_parameters.master_secret,
		GNUTLS_MASTER_SIZE);
	if (rc < 0) {
		_gnutls_hash_deinit(&td_sha, NULL);
		return gnutls_assert_val(rc);
	}

	return 0;
}
#endif

/* Hash the handshake messages as required by TLS 1.0
 */
#define SERVER_MSG "server finished"
#define CLIENT_MSG "client finished"
#define TLS_MSG_LEN 15
static int _gnutls_finished(gnutls_session_t session, int type, void *ret,
			    int sending)
{
	const int siz = TLS_MSG_LEN;
	uint8_t concat[MAX_HASH_SIZE];
	size_t hash_len;
	const char *mesg;
	int rc, len, algorithm;

	if (sending)
		len = session->internals.handshake_hash_buffer.length;
	else
		len = session->internals.handshake_hash_buffer_prev_len;

	algorithm = session->security_parameters.prf->id;
	rc = _gnutls_hash_fast(algorithm,
			       session->internals.handshake_hash_buffer.data,
			       len, concat);
	if (rc < 0)
		return gnutls_assert_val(rc);

	hash_len = session->security_parameters.prf->output_size;

	if (type == GNUTLS_SERVER) {
		mesg = SERVER_MSG;
	} else {
		mesg = CLIENT_MSG;
	}

	_gnutls_memory_mark_defined(session->security_parameters.master_secret,
				    GNUTLS_MASTER_SIZE);
	rc = _gnutls_PRF(session, session->security_parameters.master_secret,
			 GNUTLS_MASTER_SIZE, mesg, siz, concat, hash_len, 12,
			 ret);
	if (rc < 0) {
		_gnutls_memory_mark_undefined(
			session->security_parameters.master_secret,
			GNUTLS_MASTER_SIZE);
	}
	return rc;
}

/* returns the 0 on success or a negative error code.
 */
int _gnutls_negotiate_version(gnutls_session_t session, uint8_t major,
			      uint8_t minor, unsigned allow_tls13)
{
	const version_entry_st *vers;
	const version_entry_st *aversion = nversion_to_entry(major, minor);

	/* if we do not support that version, unless that version is TLS 1.2;
	 * TLS 1.2 is handled separately because it is always advertised under TLS 1.3 or later */
	if (aversion == NULL ||
	    _gnutls_nversion_is_supported(session, major, minor) == 0) {
		if (aversion && aversion->id == GNUTLS_TLS1_2) {
			vers = _gnutls_version_max(session);
			if (unlikely(vers == NULL))
				return gnutls_assert_val(
					GNUTLS_E_NO_CIPHER_SUITES);

			if (vers->id >= GNUTLS_TLS1_2) {
				session->security_parameters.pversion =
					aversion;
				return 0;
			}
		}

		/* if we get an unknown/unsupported version, then fail if the version we
		 * got is too low to be supported */
		if (!_gnutls_version_is_too_high(session, major, minor))
			return gnutls_assert_val(
				GNUTLS_E_UNSUPPORTED_VERSION_PACKET);

		/* If he requested something we do not support
		 * then we send him the highest we support.
		 */
		vers = _gnutls_legacy_version_max(session);
		if (vers == NULL) {
			/* this check is not really needed.
			 */
			gnutls_assert();
			return GNUTLS_E_UNKNOWN_CIPHER_SUITE;
		}

		session->security_parameters.pversion = vers;

		return 0;
	} else {
		session->security_parameters.pversion = aversion;

		/* we do not allow TLS1.3 negotiation using this mechanism */
		if (aversion->tls13_sem && !allow_tls13) {
			vers = _gnutls_legacy_version_max(session);
			session->security_parameters.pversion = vers;
		}

		return 0;
	}
}

/* This function returns:
 *  - zero on success
 *  - GNUTLS_E_INT_RET_0 if GNUTLS_E_AGAIN || GNUTLS_E_INTERRUPTED were returned by the callback
 *  - a negative error code on other error
 */
int _gnutls_user_hello_func(gnutls_session_t session, uint8_t major,
			    uint8_t minor)
{
	int ret, sret = 0;
	const version_entry_st *vers, *old_vers;
	const version_entry_st *new_max;

	if (session->internals.user_hello_func != NULL) {
		ret = session->internals.user_hello_func(session);

		if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) {
			gnutls_assert();
			sret = GNUTLS_E_INT_RET_0;
		} else if (ret < 0) {
			gnutls_assert();
			return ret;
		}

		/* This callback is often used to switch the priority string of the
		 * server, and that includes switching version which we have already
		 * negotiated; note that this doesn't apply when resuming as the version
		 * negotiation is already complete. */
		if (!session->internals.resumed) {
			new_max = _gnutls_version_max(session);
			old_vers = get_version(session);

			if (!old_vers->tls13_sem ||
			    (new_max && !new_max->tls13_sem)) {
#if GNUTLS_TLS_VERSION_MAX != GNUTLS_TLS1_3
#error "Need to update the following logic"
#endif
				/* Here we need to renegotiate the version since the callee might
				 * have disabled some TLS versions. This logic does not cope for
				 * protocols later than TLS1.3 if they have the tls13_sem set */
				ret = _gnutls_negotiate_version(session, major,
								minor, 0);
				if (ret < 0)
					return gnutls_assert_val(ret);

				vers = get_version(session);
				if (old_vers != vers) {
					/* at this point we need to regenerate the random value to
					 * avoid the peer detecting this session as a rollback
					 * attempt. */
					ret = _gnutls_gen_server_random(
						session, vers->id);
					if (ret < 0)
						return gnutls_assert_val(ret);
				}
			}
		}
	}
	return sret;
}

/* Associates the right credential types for the session, and
 * performs sanity checks. */
static int set_auth_types(gnutls_session_t session)
{
	const version_entry_st *ver = get_version(session);
	gnutls_kx_algorithm_t kx;

	/* sanity check:
	 * we see TLS1.3 negotiated but no key share was sent */
	if (ver->tls13_sem) {
		if (unlikely(!(session->internals.hsk_flags &
			       HSK_PSK_KE_MODE_PSK) &&
			     !(session->internals.hsk_flags &
			       HSK_KEY_SHARE_RECEIVED))) {
			return gnutls_assert_val(GNUTLS_E_MISSING_EXTENSION);
		}

		/* Under TLS1.3 this returns a KX which matches the negotiated
		 * groups from the key shares; if we are resuming then the KX seen
		 * here doesn't match the original session. */
		if (!session->internals.resumed) {
			const gnutls_group_entry_st *group = get_group(session);

			if (session->internals.hsk_flags & HSK_PSK_SELECTED) {
				if (group) {
					kx = group->pk == GNUTLS_PK_DH ?
						     GNUTLS_KX_DHE_PSK :
						     GNUTLS_KX_ECDHE_PSK;
				} else {
					kx = GNUTLS_KX_PSK;
				}
			} else if (group) {
				/* Not necessarily be RSA, but just to
				 * make _gnutls_map_kx_get_cred below
				 * work.
				 */
				kx = group->pk == GNUTLS_PK_DH ?
					     GNUTLS_KX_DHE_RSA :
					     GNUTLS_KX_ECDHE_RSA;
			} else
				kx = GNUTLS_KX_UNKNOWN;
		} else
			kx = GNUTLS_KX_UNKNOWN;
	} else {
		/* TLS1.2 or earlier, kx is associated with ciphersuite */
		kx = session->security_parameters.cs->kx_algorithm;
	}

	if (kx != GNUTLS_KX_UNKNOWN) {
		session->security_parameters.server_auth_type =
			_gnutls_map_kx_get_cred(kx, 1);
		session->security_parameters.client_auth_type =
			_gnutls_map_kx_get_cred(kx, 0);
	} else if (unlikely(!session->internals.resumed)) {
		/* Here we can only arrive if something we received
		 * prevented the session from completing. */
		return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
	}

	return 0;
}

/* Read a client hello packet.
 * A client hello must be a known version client hello
 * or version 2.0 client hello (only for compatibility
 * since SSL version 2.0 is not supported).
 */
static int read_client_hello(gnutls_session_t session, uint8_t *data,
			     int datalen)
{
	uint8_t session_id_len;
	int pos = 0, ret;
	uint16_t suite_size, comp_size;
	int ext_size;
	int neg_version, sret = 0;
	int len = datalen;
	uint8_t major, minor;
	uint8_t *suite_ptr, *comp_ptr, *session_id, *ext_ptr;
	const version_entry_st *vers;

	DECR_LEN(len, 2);
	_gnutls_handshake_log("HSK[%p]: Client's version: %d.%d\n", session,
			      data[pos], data[pos + 1]);

	major = data[pos];
	minor = data[pos + 1];

	set_adv_version(session, major, minor);

	ret = _gnutls_negotiate_version(session, major, minor, 0);
	if (ret < 0)
		return gnutls_assert_val(ret);

	vers = get_version(session);
	if (vers == NULL)
		return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);

	neg_version = vers->id;

	pos += 2;

	/* Read client random value.
	 */
	DECR_LEN(len, GNUTLS_RANDOM_SIZE);
	_gnutls_set_client_random(session, &data[pos]);

	pos += GNUTLS_RANDOM_SIZE;

	ret = _gnutls_gen_server_random(session, neg_version);
	if (ret < 0)
		return gnutls_assert_val(ret);

	session->security_parameters.timestamp = gnutls_time(NULL);

	DECR_LEN(len, 1);
	session_id_len = data[pos++];

	/* RESUME SESSION
	 */
	if (session_id_len > GNUTLS_MAX_SESSION_ID_SIZE) {
		gnutls_assert();
		return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
	}
	DECR_LEN(len, session_id_len);
	session_id = &data[pos];
	pos += session_id_len;

	if (IS_DTLS(session)) {
		int cookie_size;

		DECR_LEN(len, 1);
		cookie_size = data[pos++];
		DECR_LEN(len, cookie_size);
		pos += cookie_size;
	}

	/* move forward to extensions and store other vals */
	DECR_LEN(len, 2);
	suite_size = _gnutls_read_uint16(&data[pos]);
	pos += 2;

	if (suite_size == 0 || (suite_size % 2) != 0)
		return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);

	suite_ptr = &data[pos];
	DECR_LEN(len, suite_size);
	pos += suite_size;

	DECR_LEN(len, 1);
	comp_size = data[pos++]; /* the number of compression methods */

	if (comp_size == 0)
		return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);

	comp_ptr = &data[pos];
	DECR_LEN(len, comp_size);
	pos += comp_size;

	ext_ptr = &data[pos];
	ext_size = len;

	/* Parse only the mandatory to read extensions for resumption
	 * and version negotiation. We don't want to parse any other
	 * extensions since  we don't want new extension values to override
	 * the resumed ones. */
	ret = _gnutls_parse_hello_extensions(session,
					     GNUTLS_EXT_FLAG_CLIENT_HELLO,
					     GNUTLS_EXT_VERSION_NEG, ext_ptr,
					     ext_size);
	if (ret < 0)
		return gnutls_assert_val(ret);

	ret = _gnutls_parse_hello_extensions(session,
					     GNUTLS_EXT_FLAG_CLIENT_HELLO,
					     GNUTLS_EXT_MANDATORY, ext_ptr,
					     ext_size);
	if (ret < 0)
		return gnutls_assert_val(ret);

	vers = get_version(session);
	if (unlikely(vers == NULL))
		return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);

	if (!vers->tls13_sem) {
		ret = _gnutls_server_restore_session(session, session_id,
						     session_id_len);

		if (session_id_len > 0)
			session->internals.resumption_requested = 1;

		if (ret == 0) { /* resumed using default TLS resumption! */
			ret = _gnutls_server_select_suite(session, suite_ptr,
							  suite_size, 1);
			if (ret < 0)
				return gnutls_assert_val(ret);

			ret = tls12_resume_copy_required_vals(session, 0);
			if (ret < 0)
				return gnutls_assert_val(ret);

			session->internals.resumed = true;

			return _gnutls_user_hello_func(session, major, minor);
		} else {
			ret = _gnutls_generate_session_id(
				session->security_parameters.session_id,
				&session->security_parameters.session_id_size);
			if (ret < 0)
				return gnutls_assert_val(ret);

			session->internals.resumed = false;
		}
	} else { /* TLS1.3 */
		/* we echo client's session ID - length was checked previously */
		assert(session_id_len <= GNUTLS_MAX_SESSION_ID_SIZE);
		if (session_id_len > 0)
			memcpy(session->security_parameters.session_id,
			       session_id, session_id_len);
		session->security_parameters.session_id_size = session_id_len;
	}

	/* Parse the extensions (if any)
	 *
	 * Unconditionally try to parse extensions; safe renegotiation uses them in
	 * sslv3 and higher, even though sslv3 doesn't officially support them.
	 */
	ret = _gnutls_parse_hello_extensions(session,
					     GNUTLS_EXT_FLAG_CLIENT_HELLO,
					     GNUTLS_EXT_APPLICATION, ext_ptr,
					     ext_size);
	/* len is the rest of the parsed length */
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	/* we cache this error code */
	sret = _gnutls_user_hello_func(session, major, minor);
	if (sret < 0 && sret != GNUTLS_E_INT_RET_0) {
		gnutls_assert();
		return sret;
	}

	/* Session tickets are parsed in this point */
	ret = _gnutls_parse_hello_extensions(session,
					     GNUTLS_EXT_FLAG_CLIENT_HELLO,
					     GNUTLS_EXT_TLS, ext_ptr, ext_size);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	if (session->internals.hsk_flags & HSK_EARLY_DATA_ACCEPTED) {
		const cipher_entry_st *ce;
		const mac_entry_st *me;
		record_parameters_st *params;

		ce = cipher_to_entry(
			session->internals.resumed_security_parameters.cs
				->block_algorithm);
		me = mac_to_entry(session->internals.resumed_security_parameters
					  .cs->mac_algorithm);

		ret = _gnutls_epoch_get(session, EPOCH_NEXT, &params);
		if (ret < 0) {
			return gnutls_assert_val(ret);
		}

		params->cipher = ce;
		params->mac = me;

		ret = _tls13_read_connection_state_init(session, STAGE_EARLY);
		if (ret < 0) {
			return gnutls_assert_val(ret);
		}

		_gnutls_epoch_bump(session);
		ret = _gnutls_epoch_dup(session, EPOCH_READ_CURRENT);
		if (ret < 0) {
			return gnutls_assert_val(ret);
		}
	}

	/* resumed by session_ticket extension */
	if (!vers->tls13_sem && session->internals.resumed) {
		session->internals.resumed_security_parameters
			.max_record_recv_size =
			session->security_parameters.max_record_recv_size;
		session->internals.resumed_security_parameters
			.max_record_send_size =
			session->security_parameters.max_record_send_size;

		ret = _gnutls_server_select_suite(session, suite_ptr,
						  suite_size, 1);
		if (ret < 0)
			return gnutls_assert_val(ret);

		ret = tls12_resume_copy_required_vals(session, 1);
		if (ret < 0)
			return gnutls_assert_val(ret);

		/* to indicate to the client that the current session is resumed */
		memcpy(session->security_parameters.session_id, session_id,
		       session_id_len);
		session->security_parameters.session_id_size = session_id_len;

		return 0;
	}

	/* select an appropriate cipher suite (as well as certificate)
	 */
	ret = _gnutls_server_select_suite(session, suite_ptr, suite_size, 0);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	/* Only at this point we know the version we are actually going to use
	 * ("supported_versions" extension is parsed, user_hello_func is called,
	 * legacy version negotiation is done). */
	vers = get_version(session);
	if (unlikely(vers == NULL))
		return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);

	if (_gnutls_version_priority(session, vers->id) < 0)
		return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);

	/* check if EMS is required */
	if (!vers->tls13_sem && vers->id != GNUTLS_SSL3 &&
	    vers->id != GNUTLS_DTLS0_9 &&
	    session->internals.priorities->force_ext_master_secret ==
		    EMS_REQUIRE &&
	    !session->security_parameters.ext_master_secret) {
		return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_SECURITY);
	}

	_gnutls_handshake_log("HSK[%p]: Selected version %s\n", session,
			      vers->name);

	/* select appropriate compression method */
	ret = check_if_null_comp_present(session, comp_ptr, comp_size);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	/* call extensions that are intended to be parsed after the ciphersuite/cert
	 * are known. */
	ret = _gnutls_parse_hello_extensions(session,
					     GNUTLS_EXT_FLAG_CLIENT_HELLO,
					     _GNUTLS_EXT_TLS_POST_CS, ext_ptr,
					     ext_size);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	/* Calculate TLS 1.3 Early Secret */
	if (session->security_parameters.pversion->tls13_sem &&
	    !(session->internals.hsk_flags & HSK_PSK_SELECTED)) {
		ret = _tls13_init_secret(session, NULL, 0);
		if (ret < 0)
			return gnutls_assert_val(ret);
	}

	ret = set_auth_types(session);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	return sret;
}

/* This is to be called after sending CHANGE CIPHER SPEC packet
 * and initializing encryption. This is the first encrypted message
 * we send.
 */
int _gnutls_send_finished(gnutls_session_t session, int again)
{
	mbuffer_st *bufel;
	uint8_t *data;
	int ret;
	size_t vdata_size = 0;
	const version_entry_st *vers;

	if (again == 0) {
		bufel = _gnutls_handshake_alloc(session, MAX_VERIFY_DATA_SIZE);
		if (bufel == NULL) {
			gnutls_assert();
			return GNUTLS_E_MEMORY_ERROR;
		}
		data = _mbuffer_get_udata_ptr(bufel);

		vers = get_version(session);
		if (unlikely(vers == NULL))
			return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);

#ifdef ENABLE_SSL3
		if (vers->id == GNUTLS_SSL3) {
			ret = _gnutls_ssl3_finished(
				session, session->security_parameters.entity,
				data, 1);
			_mbuffer_set_udata_size(bufel, 36);
		} else { /* TLS 1.0+ */
#endif
			ret = _gnutls_finished(
				session, session->security_parameters.entity,
				data, 1);
			_mbuffer_set_udata_size(bufel, 12);
#ifdef ENABLE_SSL3
		}
#endif

		if (ret < 0) {
			gnutls_assert();
			return ret;
		}

		vdata_size = _mbuffer_get_udata_size(bufel);

		ret = _gnutls_ext_sr_finished(session, data, vdata_size, 0);
		if (ret < 0) {
			gnutls_assert();
			return ret;
		}

		if ((!session->internals.resumed &&
		     session->security_parameters.entity == GNUTLS_CLIENT) ||
		    (session->internals.resumed &&
		     session->security_parameters.entity == GNUTLS_SERVER)) {
			/* if we are a client not resuming - or we are a server resuming */
			_gnutls_handshake_log(
				"HSK[%p]: recording tls-unique CB (send)\n",
				session);
			memcpy(session->internals.cb_tls_unique, data,
			       vdata_size);
			session->internals.cb_tls_unique_len = vdata_size;
		}

		ret = _gnutls_send_handshake(session, bufel,
					     GNUTLS_HANDSHAKE_FINISHED);
	} else {
		ret = _gnutls_send_handshake(session, NULL,
					     GNUTLS_HANDSHAKE_FINISHED);
	}

	return ret;
}

/* This is to be called after sending our finished message. If everything
 * went fine we have negotiated a secure connection
 */
int _gnutls_recv_finished(gnutls_session_t session)
{
	uint8_t data[MAX_VERIFY_DATA_SIZE], *vrfy;
	gnutls_buffer_st buf;
	int data_size;
	int ret;
	int vrfy_size;
	const version_entry_st *vers = get_version(session);

	if (unlikely(vers == NULL))
		return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);

	ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_FINISHED, 0,
				     &buf);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	vrfy = buf.data;
	vrfy_size = buf.length;

#ifdef ENABLE_SSL3
	if (vers->id == GNUTLS_SSL3)
		data_size = 36;
	else
#endif
		data_size = 12;

	if (vrfy_size != data_size) {
		gnutls_assert();
		ret = GNUTLS_E_ERROR_IN_FINISHED_PACKET;
		goto cleanup;
	}

#ifdef ENABLE_SSL3
	if (vers->id == GNUTLS_SSL3) {
		ret = _gnutls_ssl3_finished(
			session, (session->security_parameters.entity + 1) % 2,
			data, 0);
	} else /* TLS 1.0+ */
#endif
		ret = _gnutls_finished(
			session, (session->security_parameters.entity + 1) % 2,
			data, 0);

	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
	/* When fuzzying allow to proceed without verifying the handshake
	 * consistency */
	(void)vrfy;
#warning This is unsafe for production builds

#else
	if (memcmp(vrfy, data, data_size) != 0) {
		gnutls_assert();
		ret = GNUTLS_E_ERROR_IN_FINISHED_PACKET;
		goto cleanup;
	}
#endif

	ret = _gnutls_ext_sr_finished(session, data, data_size, 1);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	if ((session->internals.resumed &&
	     session->security_parameters.entity == GNUTLS_CLIENT) ||
	    (!session->internals.resumed &&
	     session->security_parameters.entity == GNUTLS_SERVER)) {
		/* if we are a client resuming - or we are a server not resuming */
		_gnutls_handshake_log(
			"HSK[%p]: recording tls-unique CB (recv)\n", session);
		memcpy(session->internals.cb_tls_unique, data, data_size);
		session->internals.cb_tls_unique_len = data_size;
	}

cleanup:
	_gnutls_buffer_clear(&buf);

	return ret;
}

/* This selects the best supported ciphersuite from the given ones. Then
 * it adds the suite to the session and performs some checks.
 *
 * When @scsv_only is non-zero only the available SCSVs are parsed
 * and acted upon.
 */
int _gnutls_server_select_suite(gnutls_session_t session, uint8_t *data,
				unsigned int datalen, unsigned scsv_only)
{
	int ret;
	unsigned int i;
	ciphersuite_list_st peer_clist;
	const gnutls_cipher_suite_entry_st *selected;
	gnutls_kx_algorithm_t kx;
	int retval;
	const version_entry_st *vers = get_version(session);

	peer_clist.size = 0;

	for (i = 0; i < datalen; i += 2) {
		/* we support the TLS renegotiation SCSV, even if we are
		 * not under SSL 3.0, because openssl sends this SCSV
		 * on resumption unconditionally. */
		/* TLS_RENEGO_PROTECTION_REQUEST = { 0x00, 0xff } */
		if (session->internals.priorities->sr != SR_DISABLED &&
		    data[i] == GNUTLS_RENEGO_PROTECTION_REQUEST_MAJOR &&
		    data[i + 1] == GNUTLS_RENEGO_PROTECTION_REQUEST_MINOR) {
			_gnutls_handshake_log(
				"HSK[%p]: Received safe renegotiation CS\n",
				session);
			retval = _gnutls_ext_sr_recv_cs(session);
			if (retval < 0) {
				gnutls_assert();
				return retval;
			}
		}

		/* TLS_FALLBACK_SCSV */
		if (data[i] == GNUTLS_FALLBACK_SCSV_MAJOR &&
		    data[i + 1] == GNUTLS_FALLBACK_SCSV_MINOR) {
			const version_entry_st *max =
				_gnutls_version_max(session);

			_gnutls_handshake_log("HSK[%p]: Received fallback CS\n",
					      session);

			if (vers != max)
				return gnutls_assert_val(
					GNUTLS_E_INAPPROPRIATE_FALLBACK);
		} else if (!scsv_only) {
			if (peer_clist.size < MAX_CIPHERSUITE_SIZE) {
				peer_clist.entry[peer_clist.size] =
					ciphersuite_to_entry(&data[i]);
				if (peer_clist.entry[peer_clist.size] != NULL)
					peer_clist.size++;
			}
		}
	}

	if (scsv_only)
		return 0;

	ret = _gnutls_figure_common_ciphersuite(session, &peer_clist,
						&selected);
	if (ret < 0) {
		return gnutls_assert_val(ret);
	}

	_gnutls_handshake_log("HSK[%p]: Selected cipher suite: %s\n", session,
			      selected->name);

	ret = _gnutls_set_cipher_suite2(session, selected);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	if (!vers->tls13_sem) {
		/* check if the credentials (username, public key etc.) are ok
		 */
		kx = selected->kx_algorithm;
		if (_gnutls_get_kx_cred(session, kx) == NULL) {
			gnutls_assert();
			return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
		}

		/* set the mod_auth_st to the appropriate struct
		 * according to the KX algorithm. This is needed since all the
		 * handshake functions are read from there;
		 */
		session->internals.auth_struct = _gnutls_kx_auth_struct(kx);
		if (session->internals.auth_struct == NULL) {
			_gnutls_handshake_log(
				"HSK[%p]: Cannot find the appropriate handler for the KX algorithm\n",
				session);
			gnutls_assert();
			return GNUTLS_E_INTERNAL_ERROR;
		}
	}

	return 0;
}

/* This checks whether the null compression method is present.
 */
static int check_if_null_comp_present(gnutls_session_t session, uint8_t *data,
				      int datalen)
{
	int j;

	for (j = 0; j < datalen; j++) {
		if (data[j] == 0)
			return 0;
	}

	/* we were not able to find a the NULL compression
	 * algorithm
	 */
	gnutls_assert();
	return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM;
}

/* This function sends an empty handshake packet. (like hello request).
 * If the previous _gnutls_send_empty_handshake() returned
 * GNUTLS_E_AGAIN or GNUTLS_E_INTERRUPTED, then it must be called again
 * (until it returns ok), with NULL parameters.
 */
static int _gnutls_send_empty_handshake(gnutls_session_t session,
					gnutls_handshake_description_t type,
					int again)
{
	mbuffer_st *bufel;

	if (again == 0) {
		bufel = _gnutls_handshake_alloc(session, 0);
		if (bufel == NULL) {
			gnutls_assert();
			return GNUTLS_E_MEMORY_ERROR;
		}
	} else
		bufel = NULL;

	return _gnutls_send_handshake(session, bufel, type);
}

int _gnutls_call_hook_func(gnutls_session_t session,
			   gnutls_handshake_description_t type, int post,
			   unsigned incoming, const uint8_t *data,
			   unsigned data_size)
{
	gnutls_datum_t msg = { (void *)data, data_size };

	if (session->internals.h_hook != NULL) {
		if ((session->internals.h_type == type ||
		     session->internals.h_type == GNUTLS_HANDSHAKE_ANY) &&
		    (session->internals.h_post == post ||
		     session->internals.h_post == GNUTLS_HOOK_BOTH)) {
			/* internal API for testing: when we are expected to
			 * wait for GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC, we
			 * do so, but not when doing for all messages. The
			 * reason is that change cipher specs are not handshake
			 * messages, and we don't support waiting for them
			 * consistently (only sending is tracked, not receiving).
			 */
			if (type == GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC &&
			    session->internals.h_type !=
				    GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC)
				return 0;

			return session->internals.h_hook(session, type, post,
							 incoming, &msg);
		}
	}
	return 0;
}

/* Note that the "New session ticket" handshake packet behaves differently under
 * TLS 1.2 or 1.3. In 1.2 it is included in the handshake process, while in 1.3
 * it is sent asynchronously */
#define IS_ASYNC(t, v)                          \
	(t == GNUTLS_HANDSHAKE_HELLO_REQUEST || \
	 t == GNUTLS_HANDSHAKE_KEY_UPDATE ||    \
	 (t == GNUTLS_HANDSHAKE_NEW_SESSION_TICKET && v->tls13_sem))

int _gnutls_send_handshake(gnutls_session_t session, mbuffer_st *bufel,
			   gnutls_handshake_description_t type)
{
	return _gnutls_send_handshake2(session, bufel, type, 0);
}

/* This function sends a handshake message of type 'type' containing the
 * data specified here. If the previous _gnutls_send_handshake() returned
 * GNUTLS_E_AGAIN or GNUTLS_E_INTERRUPTED, then it must be called again
 * (until it returns ok), with NULL parameters.
 */
int _gnutls_send_handshake2(gnutls_session_t session, mbuffer_st *bufel,
			    gnutls_handshake_description_t type,
			    unsigned queue_only)
{
	int ret;
	uint8_t *data;
	uint32_t datasize, i_datasize;
	int pos = 0;
	const version_entry_st *vers = get_version(session);

	if (bufel == NULL) {
		/* we are resuming a previously interrupted
		 * send.
		 */
		ret = _gnutls_handshake_io_write_flush(session);
		return ret;
	}

	/* first run */
	data = _mbuffer_get_uhead_ptr(bufel);
	i_datasize = _mbuffer_get_udata_size(bufel);
	datasize = i_datasize + _mbuffer_get_uhead_size(bufel);

	data[pos++] = (uint8_t)REAL_HSK_TYPE(type);
	_gnutls_write_uint24(_mbuffer_get_udata_size(bufel), &data[pos]);
	pos += 3;

	/* Add DTLS handshake fragment headers.  The message will be
	 * fragmented later by the fragmentation sub-layer. All fields must
	 * be set properly for HMAC. The HMAC requires we pretend that the
	 * message was sent in a single fragment. */
	if (IS_DTLS(session)) {
		_gnutls_write_uint16(session->internals.dtls.hsk_write_seq++,
				     &data[pos]);
		pos += 2;

		/* Fragment offset */
		_gnutls_write_uint24(0, &data[pos]);
		pos += 3;

		/* Fragment length */
		_gnutls_write_uint24(i_datasize, &data[pos]);
		/* pos += 3; */
	}

	_gnutls_handshake_log("HSK[%p]: %s was queued [%ld bytes]\n", session,
			      _gnutls_handshake2str(type), (long)datasize);

	/* Here we keep the handshake messages in order to hash them...
	 */
	if (!IS_ASYNC(type, vers)) {
		if ((ret = handshake_hash_add_sent(session, type, data,
						   datasize)) < 0) {
			gnutls_assert();
			_mbuffer_xfree(&bufel);
			return ret;
		}
		/* If we are sending a PSK, generate early secrets here.
		 * This cannot be done in pre_shared_key.c, because it
		 * relies on transcript hash of a Client Hello. */
		if (type == GNUTLS_HANDSHAKE_CLIENT_HELLO &&
		    session->key.binders[0].prf != NULL) {
			ret = _gnutls_generate_early_secrets_for_psk(session);
			if (ret < 0) {
				gnutls_assert();
				_mbuffer_xfree(&bufel);
				return ret;
			}
		}
	}

	ret = _gnutls_call_hook_func(session, type, GNUTLS_HOOK_PRE, 0,
				     _mbuffer_get_udata_ptr(bufel),
				     _mbuffer_get_udata_size(bufel));
	if (ret < 0) {
		gnutls_assert();
		_mbuffer_xfree(&bufel);
		return ret;
	}

	session->internals.last_handshake_out = type;

	ret = _gnutls_handshake_io_cache_int(session, type, bufel);
	if (ret < 0) {
		_mbuffer_xfree(&bufel);
		gnutls_assert();
		return ret;
	}

	ret = _gnutls_call_hook_func(session, type, GNUTLS_HOOK_POST, 0,
				     _mbuffer_get_udata_ptr(bufel),
				     _mbuffer_get_udata_size(bufel));
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	if (queue_only)
		return 0;

	/* Decide when to cache and when to send */
	if (vers && vers->tls13_sem) {
		if (session->internals.initial_negotiation_completed) {
			/* we are under TLS1.3 in a re-authentication phase.
			 * we don't attempt to cache any messages */
			goto force_send;
		}

		/* The messages which are followed by another are not sent by default
		 * but are cached instead */
		switch (type) {
		case GNUTLS_HANDSHAKE_SERVER_HELLO: /* always followed by something */
		case GNUTLS_HANDSHAKE_ENCRYPTED_EXTENSIONS: /* followed by finished or cert */
		case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST: /* followed by certificate */
		case GNUTLS_HANDSHAKE_CERTIFICATE_PKT: /* this one is followed by cert verify */
		case GNUTLS_HANDSHAKE_COMPRESSED_CERTIFICATE_PKT: /* as above */
		case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY: /* followed by finished */
			ret = 0; /* cache */
			break;
		default:
			/* send this and any cached messages */
			goto force_send;
		}
	} else {
		/* The messages which are followed by another are not sent by default
		 * but are cached instead */
		switch (type) {
		case GNUTLS_HANDSHAKE_CERTIFICATE_PKT: /* this one is followed by ServerHelloDone
							 * or ClientKeyExchange always.
							 */
		case GNUTLS_HANDSHAKE_COMPRESSED_CERTIFICATE_PKT: /* as above */
		case GNUTLS_HANDSHAKE_CERTIFICATE_STATUS:
		case GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE: /* as above */
		case GNUTLS_HANDSHAKE_SERVER_HELLO: /* as above */
		case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST: /* as above */
		case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: /* followed by ChangeCipherSpec */

			/* now for client Certificate, ClientKeyExchange and
			 * CertificateVerify are always followed by ChangeCipherSpec
			 */
		case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY:
		case GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE:
			ret = 0;
			break;
		default:
			/* send this and any cached messages */
			goto force_send;
		}
	}

	return ret;

force_send:
	return _gnutls_handshake_io_write_flush(session);
}

#define CHECK_SIZE(ll)                                                      \
	if ((session->internals.max_handshake_data_buffer_size > 0) &&      \
	    (((ll) + session->internals.handshake_hash_buffer.length) >     \
	     session->internals.max_handshake_data_buffer_size)) {          \
		_gnutls_debug_log(                                          \
			"Handshake buffer length is %u (max: %u)\n",        \
			(unsigned)((ll) +                                   \
				   session->internals.handshake_hash_buffer \
					   .length),                        \
			(unsigned)session->internals                        \
				.max_handshake_data_buffer_size);           \
		return gnutls_assert_val(GNUTLS_E_HANDSHAKE_TOO_LARGE);     \
	}

/* This function add the handshake headers and the
 * handshake data to the handshake hash buffers. Needed
 * for the finished messages calculations.
 */
static int handshake_hash_add_recvd(gnutls_session_t session,
				    gnutls_handshake_description_t recv_type,
				    uint8_t *header, uint16_t header_size,
				    uint8_t *dataptr, uint32_t datalen)
{
	int ret;
	const version_entry_st *vers = get_version(session);

	if (unlikely(vers == NULL))
		return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);

	if ((vers->id != GNUTLS_DTLS0_9 &&
	     recv_type == GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST) ||
	    IS_ASYNC(recv_type, vers))
		return 0;

	CHECK_SIZE(header_size + datalen);

	session->internals.handshake_hash_buffer_prev_len =
		session->internals.handshake_hash_buffer.length;

	if (vers->id != GNUTLS_DTLS0_9) {
		ret = _gnutls_buffer_append_data(
			&session->internals.handshake_hash_buffer, header,
			header_size);
		if (ret < 0)
			return gnutls_assert_val(ret);
	}
	if (datalen > 0) {
		ret = _gnutls_buffer_append_data(
			&session->internals.handshake_hash_buffer, dataptr,
			datalen);
		if (ret < 0)
			return gnutls_assert_val(ret);
	}

	/* save the size until client KX. That is because the TLS
	 * session hash is calculated up to this message.
	 */
	if (recv_type == GNUTLS_HANDSHAKE_CLIENT_HELLO)
		session->internals.handshake_hash_buffer_client_hello_len =
			session->internals.handshake_hash_buffer.length;
	if (recv_type == GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE)
		session->internals.handshake_hash_buffer_client_kx_len =
			session->internals.handshake_hash_buffer.length;
	if (recv_type == GNUTLS_HANDSHAKE_FINISHED &&
	    session->security_parameters.entity == GNUTLS_CLIENT)
		session->internals.handshake_hash_buffer_server_finished_len =
			session->internals.handshake_hash_buffer.length;
	if (recv_type == GNUTLS_HANDSHAKE_FINISHED &&
	    session->security_parameters.entity == GNUTLS_SERVER)
		session->internals.handshake_hash_buffer_client_finished_len =
			session->internals.handshake_hash_buffer.length;

	return 0;
}

/* This function will store the handshake message we sent.
 */
static int handshake_hash_add_sent(gnutls_session_t session,
				   gnutls_handshake_description_t type,
				   uint8_t *dataptr, uint32_t datalen)
{
	int ret;
	const version_entry_st *vers = get_version(session);

	if (unlikely(vers == NULL))
		return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);

	if (IS_ASYNC(type, vers))
		return 0;

	CHECK_SIZE(datalen);

	if (vers->id == GNUTLS_DTLS0_9) {
		/* Old DTLS doesn't include the header in the MAC */
		if (datalen < 12) {
			gnutls_assert();
			return GNUTLS_E_INTERNAL_ERROR;
		}
		dataptr += 12;
		datalen -= 12;

		if (datalen == 0)
			return 0;
	}

	ret = _gnutls_buffer_append_data(
		&session->internals.handshake_hash_buffer, dataptr, datalen);
	if (ret < 0)
		return gnutls_assert_val(ret);

	if (type == GNUTLS_HANDSHAKE_CLIENT_HELLO)
		session->internals.handshake_hash_buffer_client_hello_len =
			session->internals.handshake_hash_buffer.length;
	if (type == GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE)
		session->internals.handshake_hash_buffer_client_kx_len =
			session->internals.handshake_hash_buffer.length;
	if (type == GNUTLS_HANDSHAKE_FINISHED &&
	    session->security_parameters.entity == GNUTLS_SERVER)
		session->internals.handshake_hash_buffer_server_finished_len =
			session->internals.handshake_hash_buffer.length;
	if (type == GNUTLS_HANDSHAKE_FINISHED &&
	    session->security_parameters.entity == GNUTLS_CLIENT)
		session->internals.handshake_hash_buffer_client_finished_len =
			session->internals.handshake_hash_buffer.length;

	return 0;
}

/* This function will receive handshake messages of the given types,
 * and will pass the message to the right place in order to be processed.
 * E.g. for the SERVER_HELLO message (if it is expected), it will be
 * passed to _gnutls_recv_hello().
 */
int _gnutls_recv_handshake(gnutls_session_t session,
			   gnutls_handshake_description_t type,
			   unsigned int optional, gnutls_buffer_st *buf)
{
	int ret, ret2;
	handshake_buffer_st hsk;

	ret = _gnutls_handshake_io_recv_int(session, type, &hsk, optional);
	if (ret < 0) {
		if (optional != 0 &&
		    ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET) {
			if (buf)
				_gnutls_buffer_init(buf);
			return 0;
		}

		return gnutls_assert_val_fatal(ret);
	}
	session->internals.last_handshake_in = hsk.htype;

	ret = _gnutls_call_hook_func(session, hsk.htype, GNUTLS_HOOK_PRE, 1,
				     hsk.data.data, hsk.data.length);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	ret = handshake_hash_add_recvd(session, hsk.rtype, hsk.header,
				       hsk.header_size, hsk.data.data,
				       hsk.data.length);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	switch (hsk.htype) {
	case GNUTLS_HANDSHAKE_CLIENT_HELLO_V2:
	case GNUTLS_HANDSHAKE_CLIENT_HELLO:
		if (!(IS_SERVER(session))) {
			ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
			goto cleanup;
		}

#ifdef ENABLE_SSL2
		if (hsk.htype == GNUTLS_HANDSHAKE_CLIENT_HELLO_V2)
			ret = _gnutls_read_client_hello_v2(
				session, hsk.data.data, hsk.data.length);
		else
#endif
		{
			/* Reference the full ClientHello in case an extension needs it */
			ret = _gnutls_ext_set_full_client_hello(session, &hsk);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}

			ret = read_client_hello(session, hsk.data.data,
						hsk.data.length);
		}

		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}

		break;

	case GNUTLS_HANDSHAKE_SERVER_HELLO:
		if (IS_SERVER(session)) {
			ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
			goto cleanup;
		}

		ret = read_server_hello(session, hsk.data.data,
					hsk.data.length);

		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}

		break;
	case GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST:
		if (IS_SERVER(session)) {
			ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
			goto cleanup;
		}

		ret = recv_hello_verify_request(session, hsk.data.data,
						hsk.data.length);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		} else {
			/* Signal our caller we have received a verification cookie
			   and ClientHello needs to be sent again. */
			ret = 1;
		}

		break;
	case GNUTLS_HANDSHAKE_HELLO_RETRY_REQUEST: {
		/* hash buffer synth message is generated during hello retry parsing */
		gnutls_datum_t hrr = { hsk.data.data, hsk.data.length };

		if (IS_SERVER(session)) {
			ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
			goto cleanup;
		}

		ret = _gnutls13_recv_hello_retry_request(session, &hsk.data);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		} else {
			/* during hello retry parsing, we reset handshake hash buffer,
				 * re-add this message */
			ret = handshake_hash_add_recvd(session, hsk.htype,
						       hsk.header,
						       hsk.header_size,
						       hrr.data, hrr.size);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}

			/* Signal our caller we have received a retry request
				   and ClientHello needs to be sent again. */
			ret = 1;
		}

		break;
	}
	case GNUTLS_HANDSHAKE_SERVER_HELLO_DONE:
		if (hsk.data.length == 0)
			ret = 0;
		else {
			gnutls_assert();
			ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
			goto cleanup;
		}
		break;
	case GNUTLS_HANDSHAKE_CERTIFICATE_PKT:
	case GNUTLS_HANDSHAKE_COMPRESSED_CERTIFICATE_PKT:
	case GNUTLS_HANDSHAKE_CERTIFICATE_STATUS:
	case GNUTLS_HANDSHAKE_FINISHED:
	case GNUTLS_HANDSHAKE_ENCRYPTED_EXTENSIONS:
	case GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE:
	case GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE:
	case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST:
	case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY:
	case GNUTLS_HANDSHAKE_SUPPLEMENTAL:
	case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET:
	case GNUTLS_HANDSHAKE_END_OF_EARLY_DATA:
		ret = hsk.data.length;
		break;
	default:
		gnutls_assert();
		/* we shouldn't actually arrive here in any case .
		 * unexpected messages should be caught after _gnutls_handshake_io_recv_int()
		 */
		ret = GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET;
		goto cleanup;
	}

	ret2 = _gnutls_call_hook_func(session, hsk.htype, GNUTLS_HOOK_POST, 1,
				      hsk.data.data, hsk.data.length);
	if (ret2 < 0) {
		ret = ret2;
		gnutls_assert();
		goto cleanup;
	}

	if (buf) {
		*buf = hsk.data;
		return ret;
	}

cleanup:
	_gnutls_handshake_buffer_clear(&hsk);
	return ret;
}

/* This function checks if the given cipher suite is supported, and sets it
 * to the session;
 */
static int set_client_ciphersuite(gnutls_session_t session, uint8_t suite[2])
{
	unsigned j;
	int ret;
	const gnutls_cipher_suite_entry_st *selected = NULL;
	const version_entry_st *vers = get_version(session);
	gnutls_kx_algorithm_t kx;

	for (j = 0; j < session->internals.priorities->cs.size; j++) {
		if (suite[0] ==
			    session->internals.priorities->cs.entry[j]->id[0] &&
		    suite[1] ==
			    session->internals.priorities->cs.entry[j]->id[1]) {
			selected = session->internals.priorities->cs.entry[j];
			break;
		}
	}

	if (!selected) {
		gnutls_assert();
		_gnutls_handshake_log(
			"HSK[%p]: unsupported cipher suite %.2X.%.2X was negotiated\n",
			session, (unsigned int)suite[0],
			(unsigned int)suite[1]);
		return GNUTLS_E_UNKNOWN_CIPHER_SUITE;
	}

	ret = _gnutls_set_cipher_suite2(session, selected);
	if (ret < 0)
		return gnutls_assert_val(ret);

	_gnutls_handshake_log("HSK[%p]: Selected cipher suite: %s\n", session,
			      selected->name);

	/* check if the credentials (username, public key etc.) are ok.
	 * Actually checks if they exist.
	 */
	if (!vers->tls13_sem) {
		kx = selected->kx_algorithm;

		if (!session->internals.premaster_set &&
		    _gnutls_get_kx_cred(session, kx) == NULL) {
			gnutls_assert();
			return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
		}

		/* set the mod_auth_st to the appropriate struct
		 * according to the KX algorithm. This is needed since all the
		 * handshake functions are read from there;
		 */
		session->internals.auth_struct = _gnutls_kx_auth_struct(kx);

		if (session->internals.auth_struct == NULL) {
			_gnutls_handshake_log(
				"HSK[%p]: Cannot find the appropriate handler for the KX algorithm\n",
				session);
			gnutls_assert();
			return GNUTLS_E_INTERNAL_ERROR;
		}
	} else {
		if (session->internals.hsk_flags & HSK_PSK_SELECTED) {
			if (session->key.binders[0].prf->id != selected->prf) {
				_gnutls_handshake_log(
					"HSK[%p]: PRF of ciphersuite differs with the PSK identity (cs: %s, id: %s)\n",
					session, selected->name,
					session->key.binders[0].prf->name);
				gnutls_assert();
				return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
			}
		}
	}

	return 0;
}

/* This function returns 0 if we are resuming a session or -1 otherwise.
 * This also sets the variables in the session. Used only while reading a server
 * hello. Only applicable to TLS1.2 or earlier.
 */
static int client_check_if_resuming(gnutls_session_t session,
				    uint8_t *session_id, int session_id_len)
{
	char buf[2 * GNUTLS_MAX_SESSION_ID_SIZE + 1];
	int ret;

	_gnutls_handshake_log("HSK[%p]: SessionID length: %d\n", session,
			      session_id_len);
	_gnutls_handshake_log("HSK[%p]: SessionID: %s\n", session,
			      _gnutls_bin2hex(session_id, session_id_len, buf,
					      sizeof(buf), NULL));

	if ((session->internals.resumption_requested != 0 ||
	     session->internals.premaster_set != 0) &&
	    session_id_len > 0 &&
	    session->internals.resumed_security_parameters.session_id_size ==
		    session_id_len &&
	    memcmp(session_id,
		   session->internals.resumed_security_parameters.session_id,
		   session_id_len) == 0) {
		/* resume session */
		memcpy(session->internals.resumed_security_parameters
			       .server_random,
		       session->security_parameters.server_random,
		       GNUTLS_RANDOM_SIZE);
		memcpy(session->internals.resumed_security_parameters
			       .client_random,
		       session->security_parameters.client_random,
		       GNUTLS_RANDOM_SIZE);

		ret = _gnutls_set_cipher_suite2(
			session,
			session->internals.resumed_security_parameters.cs);
		if (ret < 0) {
			gnutls_assert();
			goto no_resume;
		}

		session->internals.resumed = true; /* we are resuming */

		return 0;
	} else {
	no_resume:
		/* keep the new session id */
		session->internals.resumed = false; /* we are not resuming */
		return -1;
	}
}

/* This function reads and parses the server hello handshake message.
 * This function also restores resumed parameters if we are resuming a
 * session.
 */
static int read_server_hello(gnutls_session_t session, uint8_t *data,
			     int datalen)
{
	uint8_t session_id_len = 0;
	uint8_t *session_id;
	uint8_t *cs_pos, *comp_pos, *srandom_pos;
	uint8_t major, minor;
	int pos = 0;
	int ret;
	int len = datalen;
	unsigned ext_parse_flag = 0;
	const version_entry_st *vers, *saved_vers;

	if (datalen < GNUTLS_RANDOM_SIZE + 2) {
		gnutls_assert();
		return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
	}

	_gnutls_handshake_log("HSK[%p]: Server's version: %d.%d\n", session,
			      data[pos], data[pos + 1]);

	DECR_LEN(len, 2);
	major = data[pos];
	minor = data[pos + 1];

	saved_vers = get_version(
		session); /* will be non-null if HRR has been received */

	vers = nversion_to_entry(major, minor);
	if (unlikely(vers == NULL))
		return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);

	if (vers->tls13_sem) /* that shouldn't have been negotiated here */
		return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);

	if (_gnutls_set_current_version(session, vers->id) < 0)
		return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);

	pos += 2;

	DECR_LEN(len, GNUTLS_RANDOM_SIZE);
	srandom_pos = &data[pos];
	pos += GNUTLS_RANDOM_SIZE;

	/* Read session ID
	 */
	DECR_LEN(len, 1);
	session_id_len = data[pos++];

	if (len < session_id_len ||
	    session_id_len > GNUTLS_MAX_SESSION_ID_SIZE) {
		gnutls_assert();
		return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
	}
	DECR_LEN(len, session_id_len);
	session_id = &data[pos];
	pos += session_id_len;

	DECR_LEN(len, 2);
	cs_pos = &data[pos];
	pos += 2;

	/* move to compression
	 */
	DECR_LEN(len, 1);
	comp_pos = &data[pos];
	pos++;

	/* parse extensions to figure version */
	ret = _gnutls_parse_hello_extensions(
		session,
		GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO |
			GNUTLS_EXT_FLAG_TLS13_SERVER_HELLO,
		GNUTLS_EXT_VERSION_NEG, &data[pos], len);
	if (ret < 0)
		return gnutls_assert_val(ret);

	vers = get_version(session);
	if (unlikely(vers == NULL))
		return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);
	if (vers->tls13_sem) {
		if (major != 0x03 || minor != 0x03)
			return gnutls_assert_val(
				GNUTLS_E_UNSUPPORTED_VERSION_PACKET);
	}

	if (_gnutls_nversion_is_supported(session, vers->major, vers->minor) ==
	    0)
		return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);

	/* set server random - done after final version is selected */
	ret = _gnutls_set_server_random(session, vers, srandom_pos);
	if (ret < 0)
		return gnutls_assert_val(ret);

	/* reset keys and binders if we are not using TLS 1.3 */
	if (!vers->tls13_sem) {
		gnutls_memset(&session->key.proto.tls13, 0,
			      sizeof(session->key.proto.tls13));
		reset_binders(session);
	}

	/* check if we are resuming and set the appropriate
	 * values;
	 */
	if (!vers->tls13_sem && client_check_if_resuming(session, session_id,
							 session_id_len) == 0) {
		ret = _gnutls_parse_hello_extensions(
			session, GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO,
			GNUTLS_EXT_MANDATORY, &data[pos], len);
		if (ret < 0)
			return gnutls_assert_val(ret);

		return 0;
	} else {
		session->security_parameters.session_id_size = session_id_len;
		if (session_id_len > 0)
			memcpy(session->security_parameters.session_id,
			       session_id, session_id_len);
	}

	/* Check if the given cipher suite is supported and copy
	 * it to the session.
	 */
	ret = set_client_ciphersuite(session, cs_pos);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	if (session->internals.hsk_flags & HSK_HRR_RECEIVED) {
		/* check if ciphersuite matches */
		if (memcmp(cs_pos, session->internals.hrr_cs, 2) != 0)
			return gnutls_assert_val(
				GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);

		/* check if HRR version matches this version */
		if (vers != saved_vers)
			return gnutls_assert_val(
				GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
	}

	if (*comp_pos != 0)
		return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);

	if (vers->tls13_sem)
		ext_parse_flag |= GNUTLS_EXT_FLAG_TLS13_SERVER_HELLO;
	else
		ext_parse_flag |= GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO;

	/* Parse extensions in order.
	 */
	ret = _gnutls_parse_hello_extensions(
		session, ext_parse_flag, GNUTLS_EXT_MANDATORY, &data[pos], len);
	if (ret < 0)
		return gnutls_assert_val(ret);

	if (!vers->tls13_sem) {
		/* check if EtM is required */
		if (session->internals.priorities->force_etm &&
		    !session->security_parameters.etm) {
			const cipher_entry_st *cipher =
				cipher_to_entry(session->security_parameters.cs
							->block_algorithm);
			if (_gnutls_cipher_type(cipher) == CIPHER_BLOCK)
				return gnutls_assert_val(
					GNUTLS_E_UNWANTED_ALGORITHM);
		}

		/* check if EMS is required */
		if (vers->id != GNUTLS_SSL3 && vers->id != GNUTLS_DTLS0_9 &&
		    session->internals.priorities->force_ext_master_secret ==
			    EMS_REQUIRE &&
		    !session->security_parameters.ext_master_secret) {
			return gnutls_assert_val(
				GNUTLS_E_INSUFFICIENT_SECURITY);
		}
	}

	ret = _gnutls_parse_hello_extensions(session, ext_parse_flag,
					     GNUTLS_EXT_APPLICATION, &data[pos],
					     len);
	if (ret < 0)
		return gnutls_assert_val(ret);

	ret = _gnutls_parse_hello_extensions(session, ext_parse_flag,
					     GNUTLS_EXT_TLS, &data[pos], len);
	if (ret < 0)
		return gnutls_assert_val(ret);

	ret = _gnutls_parse_hello_extensions(session, ext_parse_flag,
					     _GNUTLS_EXT_TLS_POST_CS,
					     &data[pos], len);
	if (ret < 0)
		return gnutls_assert_val(ret);

	/* Calculate TLS 1.3 Early Secret */
	if (vers->tls13_sem &&
	    !(session->internals.hsk_flags & HSK_PSK_SELECTED)) {
		ret = _tls13_init_secret(session, NULL, 0);
		if (ret < 0)
			return gnutls_assert_val(ret);
	}

	ret = set_auth_types(session);
	if (ret < 0)
		return gnutls_assert_val(ret);

	session->internals.hsk_flags |= HSK_SERVER_HELLO_RECEIVED;

	return 0;
}

/* This function copies the appropriate compression methods, to a locally allocated buffer
 * Needed in hello messages. Returns the new data length.
 */
static int append_null_comp(gnutls_session_t session, gnutls_buffer_st *cdata)
{
	uint8_t compression_methods[2] = { 0x01, 0x00 };
	size_t init_length = cdata->length;
	int ret;

	ret = _gnutls_buffer_append_data(cdata, compression_methods, 2);
	if (ret < 0)
		return gnutls_assert_val(ret);

	ret = cdata->length - init_length;

	return ret;
}

/* This function sends the client hello handshake message.
 */
static int send_client_hello(gnutls_session_t session, int again)
{
	mbuffer_st *bufel = NULL;
	int type;
	int ret = 0;
	const version_entry_st *hver, *min_ver, *max_ver;
	uint8_t tver[2];
	gnutls_buffer_st extdata;
	bool rehandshake = false;
	bool resuming = false;
	unsigned add_sr_scsv = 0;
	uint8_t *session_id =
		session->internals.resumed_security_parameters.session_id;
	uint8_t session_id_len =
		session->internals.resumed_security_parameters.session_id_size;

	if (again == 0) {
		/* note that rehandshake is different than resuming
		 */
		if (session->internals.initial_negotiation_completed)
			rehandshake = true;

		ret = _gnutls_buffer_init_handshake_mbuffer(&extdata);
		if (ret < 0)
			return gnutls_assert_val(ret);

		/* if we are resuming a session then we set the
		 * version number to the previously established.
		 */
		if (session->internals.resumption_requested == 0 &&
		    session->internals.premaster_set == 0) {
			if (rehandshake) /* already negotiated version thus version_max == negotiated version */
				hver = get_version(session);
			else /* new handshake. just get the max */
				hver = _gnutls_legacy_version_max(session);
		} else {
			/* we are resuming a session */
			resuming = true;

			hver = session->internals.resumed_security_parameters
				       .pversion;

			if (hver && hver->tls13_sem)
				hver = _gnutls_legacy_version_max(session);
		}

		if (hver == NULL) {
			gnutls_assert();
			if (session->internals.flags & INT_FLAG_NO_TLS13)
				ret = GNUTLS_E_INSUFFICIENT_CREDENTIALS;
			else
				ret = GNUTLS_E_NO_PRIORITIES_WERE_SET;
			goto cleanup;
		}

		if (unlikely(session->internals.default_hello_version[0] !=
			     0)) {
			tver[0] = session->internals.default_hello_version[0];
			tver[1] = session->internals.default_hello_version[1];
		} else {
			tver[0] = hver->major;
			tver[1] = hver->minor;
		}
		ret = _gnutls_buffer_append_data(&extdata, tver, 2);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}
		_gnutls_handshake_log("HSK[%p]: Adv. version: %u.%u\n", session,
				      (unsigned)tver[0], (unsigned)tver[1]);

		min_ver = _gnutls_version_lowest(session);
		max_ver = _gnutls_version_max(session);
		if (min_ver == NULL || max_ver == NULL) {
			gnutls_assert();
			ret = GNUTLS_E_NO_PRIORITIES_WERE_SET;
			goto cleanup;
		}

		/* if we are replying to an HRR the version is already negotiated */
		if (!(session->internals.hsk_flags & HSK_HRR_RECEIVED) ||
		    !get_version(session)) {
			/* Set the version we advertised as maximum
			 * (RSA uses it). */
			set_adv_version(session, hver->major, hver->minor);
			if (_gnutls_set_current_version(session, hver->id) <
			    0) {
				ret = gnutls_assert_val(
					GNUTLS_E_UNSUPPORTED_VERSION_PACKET);
				goto cleanup;
			}
		}

		if (session->internals.priorities->min_record_version != 0) {
			/* Advertise the lowest supported (SSL 3.0) record packet
			 * version in record packets during the handshake.
			 * That is to avoid confusing implementations
			 * that do not support TLS 1.2 and don't know
			 * how 3,3 version of record packets look like.
			 */
			set_default_version(session, min_ver);
		} else {
			set_default_version(session, hver);
		}

		/* In order to know when this session was initiated.
		 */
		session->security_parameters.timestamp = gnutls_time(NULL);

		/* Generate random data
		 */
		if (!(session->internals.hsk_flags & HSK_HRR_RECEIVED) &&
		    !(IS_DTLS(session) &&
		      session->internals.dtls.hsk_hello_verify_requests != 0)) {
			ret = _gnutls_gen_client_random(session);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}
		}

		ret = _gnutls_buffer_append_data(
			&extdata, session->security_parameters.client_random,
			GNUTLS_RANDOM_SIZE);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}

#ifdef TLS13_APPENDIX_D4
		if (max_ver->tls13_sem &&
		    session->internals.priorities->tls13_compat_mode &&
		    !resuming) {
			if (!(session->internals.hsk_flags &
			      HSK_HRR_RECEIVED)) {
				/* Under TLS1.3 we generate a random session ID to make
				 * the TLS1.3 session look like a resumed TLS1.2 session */
				ret = _gnutls_generate_session_id(
					session->security_parameters.session_id,
					&session->security_parameters
						 .session_id_size);
				if (ret < 0) {
					gnutls_assert();
					goto cleanup;
				}
			}
			session_id = session->security_parameters.session_id;
			session_id_len =
				session->security_parameters.session_id_size;
		}
#endif

		/* Copy the Session ID - if any
		 */
		ret = _gnutls_buffer_append_data_prefix(&extdata, 8, session_id,
							session_id_len);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}

		/* Copy the DTLS cookie
		 */
		if (IS_DTLS(session)) {
			ret = _gnutls_buffer_append_data_prefix(
				&extdata, 8,
				session->internals.dtls.dcookie.data,
				session->internals.dtls.dcookie.size);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}
			_gnutls_free_datum(&session->internals.dtls.dcookie);
		}

		/* Copy the ciphersuites.
		 */
#ifdef ENABLE_SSL3
		/* If using SSLv3 Send TLS_RENEGO_PROTECTION_REQUEST SCSV for MITM
		 * prevention on initial negotiation (but not renegotiation; that's
		 * handled with the RI extension below).
		 */
		if (!session->internals.initial_negotiation_completed &&
		    session->security_parameters.entity == GNUTLS_CLIENT &&
		    (hver->id == GNUTLS_SSL3 &&
		     session->internals.priorities->no_extensions != 0)) {
			add_sr_scsv = 1;
		}
#endif
		ret = _gnutls_get_client_ciphersuites(session, &extdata,
						      min_ver, add_sr_scsv);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}

		/* Copy the compression methods.
		 */
		ret = append_null_comp(session, &extdata);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}

		/* Generate and copy TLS extensions.
		 */
		if (session->internals.priorities->no_extensions == 0) {
			if (_gnutls_version_has_extensions(hver)) {
				type = GNUTLS_EXT_ANY;
			} else {
				type = GNUTLS_EXT_MANDATORY;
			}

			ret = _gnutls_gen_hello_extensions(
				session, &extdata, GNUTLS_EXT_FLAG_CLIENT_HELLO,
				type);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}
		}

		bufel = _gnutls_buffer_to_mbuffer(&extdata);
	}

	ret = _gnutls_send_handshake(session, bufel,
				     GNUTLS_HANDSHAKE_CLIENT_HELLO);

	if (session->internals.hsk_flags & HSK_EARLY_DATA_IN_FLIGHT) {
		const cipher_entry_st *ce;
		const mac_entry_st *me;
		record_parameters_st *params;

		ce = cipher_to_entry(
			session->internals.resumed_security_parameters.cs
				->block_algorithm);
		me = mac_to_entry(session->internals.resumed_security_parameters
					  .cs->mac_algorithm);

		ret = _gnutls_epoch_get(session, EPOCH_NEXT, &params);
		if (ret < 0) {
			return gnutls_assert_val(ret);
		}

		params->cipher = ce;
		params->mac = me;

		ret = _tls13_write_connection_state_init(session, STAGE_EARLY);
		if (ret < 0) {
			return gnutls_assert_val(ret);
		}

		_gnutls_epoch_bump(session);
		ret = _gnutls_epoch_dup(session, EPOCH_WRITE_CURRENT);
		if (ret < 0) {
			return gnutls_assert_val(ret);
		}

		ret = _gnutls13_send_early_data(session);
		if (ret < 0) {
			return gnutls_assert_val(ret);
		}
	}

	return ret;

cleanup:
	_gnutls_buffer_clear(&extdata);
	return ret;
}

int _gnutls_send_server_hello(gnutls_session_t session, int again)
{
	mbuffer_st *bufel = NULL;
	gnutls_buffer_st buf;
	int ret;
	uint8_t session_id_len = session->security_parameters.session_id_size;
	char tmpbuf[2 * GNUTLS_MAX_SESSION_ID_SIZE + 1];
	const version_entry_st *vers;
	uint8_t vbytes[2];
	unsigned extflag = 0;
	gnutls_ext_parse_type_t etype;

	_gnutls_buffer_init(&buf);

	if (again == 0) {
		vers = get_version(session);
		if (unlikely(vers == NULL ||
			     session->security_parameters.cs == NULL))
			return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);

		if (vers->tls13_sem) {
			vbytes[0] = 0x03; /* TLS1.2 */
			vbytes[1] = 0x03;
			extflag |= GNUTLS_EXT_FLAG_TLS13_SERVER_HELLO;
		} else {
			vbytes[0] = vers->major;
			vbytes[1] = vers->minor;
			extflag |= GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO;
		}

		ret = _gnutls_buffer_init_handshake_mbuffer(&buf);
		if (ret < 0) {
			gnutls_assert();
			goto fail;
		}

		ret = _gnutls_buffer_append_data(&buf, vbytes, 2);
		if (ret < 0) {
			gnutls_assert();
			goto fail;
		}

		ret = _gnutls_buffer_append_data(
			&buf, session->security_parameters.server_random,
			GNUTLS_RANDOM_SIZE);
		if (ret < 0) {
			gnutls_assert();
			goto fail;
		}

		ret = _gnutls_buffer_append_data_prefix(
			&buf, 8, session->security_parameters.session_id,
			session_id_len);
		if (ret < 0) {
			gnutls_assert();
			goto fail;
		}

		_gnutls_handshake_log(
			"HSK[%p]: SessionID: %s\n", session,
			_gnutls_bin2hex(session->security_parameters.session_id,
					session_id_len, tmpbuf, sizeof(tmpbuf),
					NULL));

		ret = _gnutls_buffer_append_data(
			&buf, session->security_parameters.cs->id, 2);
		if (ret < 0) {
			gnutls_assert();
			goto fail;
		}

		/* compression */
		ret = _gnutls_buffer_append_prefix(&buf, 8, 0);
		if (ret < 0) {
			gnutls_assert();
			goto fail;
		}

		if (!vers->tls13_sem && session->internals.resumed)
			etype = GNUTLS_EXT_MANDATORY;
		else
			etype = GNUTLS_EXT_ANY;
		ret = _gnutls_gen_hello_extensions(session, &buf, extflag,
						   etype);
		if (ret < 0) {
			gnutls_assert();
			goto fail;
		}

		if (vers->tls13_sem) {
			/* Under TLS1.3, the session ID is used for different purposes than
			 * the TLS1.0 session ID. Ensure that there is an internally set
			 * value which the server will see on the original and resumed sessions */
			ret = _gnutls_generate_session_id(
				session->security_parameters.session_id,
				&session->security_parameters.session_id_size);
			if (ret < 0) {
				gnutls_assert();
				goto fail;
			}
		}

		bufel = _gnutls_buffer_to_mbuffer(&buf);
	}

	ret = _gnutls_send_handshake(session, bufel,
				     GNUTLS_HANDSHAKE_SERVER_HELLO);

fail:
	_gnutls_buffer_clear(&buf);
	return ret;
}

static int recv_hello_verify_request(gnutls_session_t session, uint8_t *data,
				     int datalen)
{
	ssize_t len = datalen;
	size_t pos = 0;
	uint8_t cookie_len;
	unsigned int nb_verifs;
	int ret;

	if (!IS_DTLS(session)) {
		gnutls_assert();
		return GNUTLS_E_UNEXPECTED_PACKET;
	}

	nb_verifs = ++session->internals.dtls.hsk_hello_verify_requests;
	if (nb_verifs >= MAX_HANDSHAKE_HELLO_VERIFY_REQUESTS) {
		/* The server is either buggy, malicious or changing cookie
		   secrets _way_ too fast. */
		gnutls_assert();
		return GNUTLS_E_UNEXPECTED_PACKET;
	}

	DECR_LEN(len, 2);
	pos += 2;

	DECR_LEN(len, 1);
	cookie_len = data[pos];
	pos++;

	if (cookie_len > DTLS_MAX_COOKIE_SIZE) {
		gnutls_assert();
		return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
	}

	DECR_LEN(len, cookie_len);

	gnutls_free(session->internals.dtls.dcookie.data);
	ret = _gnutls_set_datum(&session->internals.dtls.dcookie, &data[pos],
				cookie_len);
	if (ret < 0)
		return gnutls_assert_val(ret);

	if (len != 0) {
		gnutls_assert();
		return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
	}

	/* reset handshake hash buffers */
	handshake_hash_buffer_reset(session);
	/* reset extensions used in previous hello */
	session->internals.used_exts = 0;

	return 0;
}

/* The packets in gnutls_handshake (it's more broad than original TLS handshake)
 *
 *     Client					       Server
 *
 *     ClientHello		  -------->
 *				  <--------	 ServerHello
 *
 *						    Certificate*
 *					      ServerKeyExchange*
 *				  <--------   CertificateRequest*
 *
 *				  <--------      ServerHelloDone
 *     Certificate*
 *     ClientKeyExchange
 *     CertificateVerify*
 *     [ChangeCipherSpec]
 *     Finished		     -------->
 *						NewSessionTicket
 *					      [ChangeCipherSpec]
 *				  <--------	     Finished
 *
 * (*): means optional packet.
 */

/* Handshake when resumming session:
 *      Client						Server
 *
 *      ClientHello		   -------->
 *						      ServerHello
 *					       [ChangeCipherSpec]
 *				   <--------	     Finished
 *     [ChangeCipherSpec]
 *     Finished		      -------->
 *
 */

/**
 * gnutls_rehandshake:
 * @session: is a #gnutls_session_t type.
 *
 * This function can only be called in server side, and
 * instructs a TLS 1.2 or earlier client to renegotiate
 * parameters (perform a handshake), by sending a
 * hello request message.
 *
 * If this function succeeds, the calling application
 * should call gnutls_record_recv() until %GNUTLS_E_REHANDSHAKE
 * is returned to clear any pending data. If the %GNUTLS_E_REHANDSHAKE
 * error code is not seen, then the handshake request was
 * not followed by the peer (the TLS protocol does not require
 * the client to do, and such compliance should be handled
 * by the application protocol).
 *
 * Once the %GNUTLS_E_REHANDSHAKE error code is seen, the
 * calling application should proceed to calling
 * gnutls_handshake() to negotiate the new
 * parameters.
 *
 * If the client does not wish to renegotiate parameters he
 * may reply with an alert message, and in that case the return code seen
 * by subsequent gnutls_record_recv() will be
 * %GNUTLS_E_WARNING_ALERT_RECEIVED with the specific alert being
 * %GNUTLS_A_NO_RENEGOTIATION.  A client may also choose to ignore
 * this request.
 *
 * Under TLS 1.3 this function is equivalent to gnutls_session_key_update()
 * with the %GNUTLS_KU_PEER flag. In that case subsequent calls to
 * gnutls_record_recv() will not return %GNUTLS_E_REHANDSHAKE, and
 * calls to gnutls_handshake() in server side are a no-op.
 *
 * This function always fails with %GNUTLS_E_INVALID_REQUEST when
 * called in client side.
 *
 * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
 **/
int gnutls_rehandshake(gnutls_session_t session)
{
	int ret;
	const version_entry_st *vers = get_version(session);

	/* only server sends that handshake packet */
	if (session->security_parameters.entity == GNUTLS_CLIENT)
		return GNUTLS_E_INVALID_REQUEST;

	if (vers->tls13_sem) {
		return gnutls_session_key_update(session, GNUTLS_KU_PEER);
	}

	_dtls_async_timer_delete(session);

	ret = _gnutls_send_empty_handshake(
		session, GNUTLS_HANDSHAKE_HELLO_REQUEST, AGAIN(STATE50));
	STATE = STATE50;

	if (ret < 0) {
		gnutls_assert();
		return ret;
	}
	STATE = STATE0;

	return 0;
}

/* This function checks whether the error code should be treated fatal
 * or not, and also does the necessary state transition.  In
 * particular, in the case of a rehandshake abort it resets the
 * handshake's internal state.
 */
inline static int _gnutls_abort_handshake(gnutls_session_t session, int ret)
{
	switch (ret) {
	case GNUTLS_E_WARNING_ALERT_RECEIVED:
		if (gnutls_alert_get(session) == GNUTLS_A_NO_RENEGOTIATION) {
			/* The server always toleretes a "no_renegotiation" alert. */
			if (session->security_parameters.entity ==
			    GNUTLS_SERVER) {
				STATE = STATE0;
				return ret;
			}

			/* The client should tolerete a "no_renegotiation" alert only if:
			 * - the initial handshake has completed, or
			 * - a Server Hello is not yet received
			 */
			if (session->internals.initial_negotiation_completed ||
			    !(session->internals.hsk_flags &
			      HSK_SERVER_HELLO_RECEIVED)) {
				STATE = STATE0;
				return ret;
			}

			return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
		}
		return ret;
	case GNUTLS_E_GOT_APPLICATION_DATA:
		STATE = STATE0;
		return ret;
	default:
		return ret;
	}
}

static int _gnutls_send_supplemental(gnutls_session_t session, int again)
{
	mbuffer_st *bufel = NULL;
	int ret = 0;

	_gnutls_debug_log("EXT[%p]: Sending supplemental data\n", session);

	if (!again) {
		gnutls_buffer_st buf;
		ret = _gnutls_buffer_init_handshake_mbuffer(&buf);
		if (ret < 0)
			return gnutls_assert_val(ret);

		ret = _gnutls_gen_supplemental(session, &buf);
		if (ret < 0) {
			gnutls_assert();
			_gnutls_buffer_clear(&buf);
			return ret;
		}

		bufel = _gnutls_buffer_to_mbuffer(&buf);
	}

	return _gnutls_send_handshake(session, bufel,
				      GNUTLS_HANDSHAKE_SUPPLEMENTAL);
}

static int _gnutls_recv_supplemental(gnutls_session_t session)
{
	gnutls_buffer_st buf;
	int ret;

	_gnutls_debug_log("EXT[%p]: Expecting supplemental data\n", session);

	ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_SUPPLEMENTAL, 1,
				     &buf);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	ret = _gnutls_parse_supplemental(session, buf.data, buf.length);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

cleanup:
	_gnutls_buffer_clear(&buf);

	return ret;
}

/**
 * gnutls_handshake:
 * @session: is a #gnutls_session_t type.
 *
 * This function performs the handshake of the TLS/SSL protocol, and
 * initializes the TLS session parameters.
 *
 * The non-fatal errors expected by this function are:
 * %GNUTLS_E_INTERRUPTED, %GNUTLS_E_AGAIN,
 * %GNUTLS_E_WARNING_ALERT_RECEIVED. When this function is called
 * for re-handshake under TLS 1.2 or earlier, the non-fatal error code
 * %GNUTLS_E_GOT_APPLICATION_DATA may also be returned.
 *
 * The former two interrupt the handshake procedure due to the transport
 * layer being interrupted, and the latter because of a "warning" alert that
 * was sent by the peer (it is always a good idea to check any
 * received alerts). On these non-fatal errors call this function again,
 * until it returns 0; cf.  gnutls_record_get_direction() and
 * gnutls_error_is_fatal(). In DTLS sessions the non-fatal error
 * %GNUTLS_E_LARGE_PACKET is also possible, and indicates that
 * the MTU should be adjusted.
 *
 * When this function is called by a server after a rehandshake request
 * under TLS 1.2 or earlier the %GNUTLS_E_GOT_APPLICATION_DATA error code indicates
 * that some data were pending prior to peer initiating the handshake.
 * Under TLS 1.3 this function when called after a successful handshake, is a no-op
 * and always succeeds in server side; in client side this function is
 * equivalent to gnutls_session_key_update() with %GNUTLS_KU_PEER flag.
 *
 * This function handles both full and abbreviated TLS handshakes (resumption).
 * For abbreviated handshakes, in client side, the gnutls_session_set_data()
 * should be called prior to this function to set parameters from a previous session.
 * In server side, resumption is handled by either setting a DB back-end, or setting
 * up keys for session tickets.
 *
 * Returns: %GNUTLS_E_SUCCESS on a successful handshake, otherwise a negative error code.
 **/
int gnutls_handshake(gnutls_session_t session)
{
	const version_entry_st *vers = get_version(session);
	int ret;

	if (unlikely(session->internals.initial_negotiation_completed)) {
		if (vers->tls13_sem) {
			if (session->security_parameters.entity ==
			    GNUTLS_CLIENT) {
				return gnutls_session_key_update(
					session, GNUTLS_KU_PEER);
			} else {
				/* This is a no-op for a server under TLS 1.3, as
				 * a server has already called gnutls_rehandshake()
				 * which performed a key update.
				 */
				return 0;
			}
		}
	}

	if (STATE == STATE0) {
		unsigned int tmo_ms;
		struct timespec *end;
		struct timespec *start;

		/* first call */
		if (session->internals.priorities == NULL ||
		    session->internals.priorities->cs.size == 0)
			return gnutls_assert_val(
				GNUTLS_E_NO_PRIORITIES_WERE_SET);

		ret = _gnutls_epoch_setup_next(session, 0, NULL);
		if (ret < 0)
			return gnutls_assert_val(ret);

		session->internals.used_exts = 0;
		session->internals.hsk_flags = 0;
		session->internals.handshake_in_progress = 1;
		session->internals.vc_status = -1;
		gnutls_gettime(&session->internals.handshake_start_time);

		tmo_ms = session->internals.handshake_timeout_ms;
		end = &session->internals.handshake_abs_timeout;
		start = &session->internals.handshake_start_time;

		if (tmo_ms && end->tv_sec == 0 && end->tv_nsec == 0) {
			end->tv_sec = start->tv_sec +
				      (start->tv_nsec + tmo_ms * 1000000LL) /
					      1000000000LL;
			end->tv_nsec = (start->tv_nsec + tmo_ms * 1000000LL) %
				       1000000000LL;
		}

#ifdef ENABLE_KTLS
		if (_gnutls_config_is_ktls_enabled()) {
			if ((session->internals.pull_func &&
			     session->internals.pull_func != system_read) ||
			    session->internals.push_func) {
				_gnutls_audit_log(
					session, "Not enabling KTLS with "
						 "custom pull/push function\n");
			} else {
				_gnutls_ktls_enable(session);
			}
		}
#endif
	}

	if (session->internals.recv_state == RECV_STATE_FALSE_START) {
		session_invalidate(session);
		return gnutls_assert_val(GNUTLS_E_HANDSHAKE_DURING_FALSE_START);
	}

	if (session->security_parameters.entity == GNUTLS_CLIENT) {
		do {
			ret = handshake_client(session);
		} while (ret == 1);
	} else {
		ret = handshake_server(session);
	}

	if (ret < 0) {
		return _gnutls_abort_handshake(session, ret);
	}

	/* clear handshake buffer */
	if (session->internals.recv_state != RECV_STATE_FALSE_START &&
	    session->internals.recv_state != RECV_STATE_EARLY_START) {
		_gnutls_handshake_hash_buffers_clear(session);

		if (IS_DTLS(session) == 0) {
			_gnutls_handshake_io_buffer_clear(session);
		} else {
			_dtls_async_timer_init(session);
		}

		_gnutls_handshake_internal_state_clear(session);

		_gnutls_buffer_clear(&session->internals.record_presend_buffer);

		_gnutls_epoch_bump(session);
	}

	/* Give an estimation of the round-trip under TLS1.3, used by gnutls_session_get_data2() */
	if (!IS_SERVER(session) && vers->tls13_sem) {
		struct timespec handshake_finish_time;
		gnutls_gettime(&handshake_finish_time);

		if (!(session->internals.hsk_flags & HSK_HRR_RECEIVED)) {
			session->internals.ertt =
				timespec_sub_ms(
					&handshake_finish_time,
					&session->internals
						 .handshake_start_time) /
				2;
		} else {
			session->internals.ertt =
				timespec_sub_ms(
					&handshake_finish_time,
					&session->internals
						 .handshake_start_time) /
				4;
		}
	}

#ifdef ENABLE_KTLS
	if (IS_KTLS_ENABLED(session, GNUTLS_KTLS_DUPLEX)) {
		ret = _gnutls_ktls_set_keys(session, GNUTLS_KTLS_DUPLEX);
		if (ret < 0) {
			/* no need to invalidate the session as keys were not set */
			session->internals.ktls_enabled = 0;
			_gnutls_audit_log(
				session,
				"disabling KTLS: failed to set keys\n");
		}
	}
#endif

	return 0;
}

/**
 * gnutls_handshake_set_timeout:
 * @session: is a #gnutls_session_t type.
 * @ms: is a timeout value in milliseconds
 *
 * This function sets the timeout for the TLS handshake process
 * to the provided value. Use an @ms value of zero to disable
 * timeout, or %GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT for a reasonable
 * default value. For the DTLS protocol, the more detailed
 * gnutls_dtls_set_timeouts() is provided.
 *
 * This function requires to set a pull timeout callback. See
 * gnutls_transport_set_pull_timeout_function().
 *
 * Since: 3.1.0
 **/
void gnutls_handshake_set_timeout(gnutls_session_t session, unsigned int ms)
{
	if (ms == GNUTLS_INDEFINITE_TIMEOUT) {
		session->internals.handshake_timeout_ms = 0;
		return;
	}

	if (ms == GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT)
		ms = DEFAULT_HANDSHAKE_TIMEOUT_MS;

	if (IS_DTLS(session)) {
		gnutls_dtls_set_timeouts(session, DTLS_RETRANS_TIMEOUT, ms);
		return;
	}

	session->internals.handshake_timeout_ms = ms;
}

/* Runs the certificate verification callback.
 * side is the side that we verify the certificate
 * from (either GNUTLS_CLIENT or GNUTLS_SERVER).
 */
int _gnutls_run_verify_callback(gnutls_session_t session, unsigned int side)
{
	gnutls_certificate_credentials_t cred;
	int ret, type;

	if (session->internals.hsk_flags & HSK_PSK_SELECTED)
		return 0;

	cred = (gnutls_certificate_credentials_t)_gnutls_get_cred(
		session, GNUTLS_CRD_CERTIFICATE);

	if (side == GNUTLS_CLIENT)
		type = gnutls_auth_server_get_type(session);
	else
		type = gnutls_auth_client_get_type(session);

	if (type != GNUTLS_CRD_CERTIFICATE)
		return 0;

	/* verify whether the certificate of the peer remained the same
	 * as with any previous handshakes */
	if (cred != NULL) {
		ret = _gnutls_check_if_cert_hash_is_same(session, cred);
		if (ret < 0) {
			return gnutls_assert_val(ret);
		}
	}

	if (cred != NULL &&
	    (cred->verify_callback != NULL ||
	     session->internals.verify_callback != NULL) &&
	    (session->security_parameters.entity == GNUTLS_CLIENT ||
	     session->internals.send_cert_req != GNUTLS_CERT_IGNORE)) {
		if (session->internals.verify_callback)
			ret = session->internals.verify_callback(session);
		else
			ret = cred->verify_callback(session);
		if (ret < -1)
			return gnutls_assert_val(ret);
		else if (ret != 0)
			return gnutls_assert_val(GNUTLS_E_CERTIFICATE_ERROR);
	}

	return 0;
}

static bool can_send_false_start(gnutls_session_t session)
{
	const version_entry_st *vers;

	vers = get_version(session);
	if (unlikely(vers == NULL || !vers->false_start))
		return 0;

	if (session->internals.selected_cert_list != NULL)
		return 0;

	if (!_gnutls_kx_allows_false_start(session))
		return 0;

	return 1;
}

/*
 * handshake_client
 * This function performs the client side of the handshake of the TLS/SSL protocol.
 */
static int handshake_client(gnutls_session_t session)
{
	int ret = 0;
	const version_entry_st *ver;

reset:
	if (STATE >= STATE99)
		return _gnutls13_handshake_client(session);

	switch (STATE) {
	case STATE0:
	case STATE1:
		ret = send_client_hello(session, AGAIN(STATE1));
		STATE = STATE1;
		IMED_RET("send hello", ret, 1);
		FALLTHROUGH;
	case STATE2:
		if (IS_DTLS(session)) {
			ret = _gnutls_recv_handshake(
				session, GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST,
				1, NULL);
			STATE = STATE2;
			IMED_RET("recv hello verify", ret, 1);

			if (ret == 1) {
				STATE = STATE0;
				return 1;
			}
		} else {
			ret = _gnutls_recv_handshake(
				session, GNUTLS_HANDSHAKE_HELLO_RETRY_REQUEST,
				1, NULL);
			STATE = STATE2;
			IMED_RET("recv hello retry", ret, 1);

			if (ret == 1) {
				STATE = STATE0;
				return 1;
			}
		}
		FALLTHROUGH;
	case STATE3:
		/* receive the server hello */
		ret = _gnutls_recv_handshake(
			session, GNUTLS_HANDSHAKE_SERVER_HELLO, 0, NULL);
		STATE = STATE3;
		IMED_RET("recv hello", ret, 1);
		FALLTHROUGH;
	case STATE4:
		ver = get_version(session);
		if (ver->tls13_sem) { /* TLS 1.3 state machine */
			STATE = STATE99;
			goto reset;
		}

		ret = _gnutls_ext_sr_verify(session);
		STATE = STATE4;
		IMED_RET_FATAL("recv hello", ret, 0);
		FALLTHROUGH;
	case STATE5:
		if (session->security_parameters.do_recv_supplemental) {
			ret = _gnutls_recv_supplemental(session);
			STATE = STATE5;
			IMED_RET("recv supplemental", ret, 1);
		}
		FALLTHROUGH;
	case STATE6:
		/* RECV CERTIFICATE */
		if (!session->internals.resumed) /* if we are not resuming */
			ret = _gnutls_recv_server_certificate(session);
		STATE = STATE6;
		IMED_RET("recv server certificate", ret, 1);
		FALLTHROUGH;
	case STATE7:
#ifdef ENABLE_OCSP
		/* RECV CERTIFICATE STATUS */
		if (!session->internals.resumed) /* if we are not resuming */
			ret = _gnutls_recv_server_certificate_status(session);
		STATE = STATE7;
		IMED_RET("recv server certificate", ret, 1);
#endif
		FALLTHROUGH;
	case STATE8:
		ret = _gnutls_run_verify_callback(session, GNUTLS_CLIENT);
		STATE = STATE8;
		if (ret < 0)
			return gnutls_assert_val(ret);

		FALLTHROUGH;
	case STATE9:
		/* receive the server key exchange */
		if (!session->internals.resumed) /* if we are not resuming */
			ret = _gnutls_recv_server_kx_message(session);
		STATE = STATE9;
		IMED_RET("recv server kx message", ret, 1);
		FALLTHROUGH;
	case STATE10:
		/* receive the server certificate request - if any
		 */

		if (!session->internals.resumed) /* if we are not resuming */
			ret = _gnutls_recv_server_crt_request(session);
		STATE = STATE10;
		IMED_RET("recv server certificate request message", ret, 1);
		FALLTHROUGH;
	case STATE11:
		/* receive the server hello done */
		if (!session->internals.resumed) /* if we are not resuming */
			ret = _gnutls_recv_handshake(
				session, GNUTLS_HANDSHAKE_SERVER_HELLO_DONE, 0,
				NULL);
		STATE = STATE11;
		IMED_RET("recv server hello done", ret, 1);
		FALLTHROUGH;
	case STATE12:
		if (session->security_parameters.do_send_supplemental) {
			ret = _gnutls_send_supplemental(session,
							AGAIN(STATE12));
			STATE = STATE12;
			IMED_RET("send supplemental", ret, 0);
		}
		FALLTHROUGH;
	case STATE13:
		/* send our certificate - if any and if requested
		 */
		if (!session->internals.resumed) /* if we are not resuming */
			ret = _gnutls_send_client_certificate(session,
							      AGAIN(STATE13));
		STATE = STATE13;
		IMED_RET("send client certificate", ret, 0);
		FALLTHROUGH;
	case STATE14:
		if (!session->internals.resumed) /* if we are not resuming */
			ret = _gnutls_send_client_kx_message(session,
							     AGAIN(STATE14));
		STATE = STATE14;
		IMED_RET("send client kx", ret, 0);
		FALLTHROUGH;
	case STATE15:
		/* send client certificate verify */
		if (!session->internals.resumed) /* if we are not resuming */
			ret = _gnutls_send_client_certificate_verify(
				session, AGAIN(STATE15));
		STATE = STATE15;
		IMED_RET("send client certificate verify", ret, 1);
		FALLTHROUGH;
	case STATE16:
		STATE = STATE16;
		if (!session->internals.resumed) {
			ret = send_handshake_final(session, true);
			IMED_RET("send handshake final 2", ret, 1);
		} else {
			ret = _gnutls_recv_new_session_ticket(session);
			IMED_RET("recv handshake new session ticket", ret, 1);
		}
		FALLTHROUGH;
	case STATE17:
		STATE = STATE17;
		if (!session->internals.resumed &&
		    (session->internals.flags & GNUTLS_ENABLE_FALSE_START) &&
		    can_send_false_start(session)) {
			session->internals.hsk_flags |= HSK_FALSE_START_USED;
			session->internals.recv_state = RECV_STATE_FALSE_START;
			/* complete this phase of the handshake. We
			 * should be called again by gnutls_record_recv()
			 */
			STATE = STATE18;
			gnutls_assert();

			return 0;
		}
		FALLTHROUGH;
	case STATE18:
		STATE = STATE18;

		if (!session->internals.resumed) {
			ret = _gnutls_recv_new_session_ticket(session);
			IMED_RET("recv handshake new session ticket", ret, 1);
		} else {
			ret = recv_handshake_final(session, true);
			IMED_RET("recv handshake final", ret, 1);
		}
		FALLTHROUGH;
	case STATE19:
		STATE = STATE19;
		if (!session->internals.resumed) {
			ret = recv_handshake_final(session, false);
			IMED_RET("recv handshake final 2", ret, 1);
		} else {
			ret = send_handshake_final(session, false);
			IMED_RET("send handshake final", ret, 1);
		}

		STATE = STATE0;
		FALLTHROUGH;
	default:
		break;
	}

	/* explicitly reset any false start flags */
	gnutls_mutex_lock(&session->internals.post_negotiation_lock);
	session->internals.initial_negotiation_completed = 1;
	session->internals.recv_state = RECV_STATE_0;
	gnutls_mutex_unlock(&session->internals.post_negotiation_lock);

	return 0;
}

/* This function is to be called if the handshake was successfully
 * completed. This sends a Change Cipher Spec packet to the peer.
 */
ssize_t _gnutls_send_change_cipher_spec(gnutls_session_t session, int again)
{
	uint8_t *data;
	mbuffer_st *bufel;
	int ret;
	const version_entry_st *vers;

	if (again == 0) {
		bufel = _gnutls_handshake_alloc(session,
						3); /* max for DTLS0.9 */
		if (bufel == NULL)
			return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);

		vers = get_version(session);
		if (unlikely(vers == NULL))
			return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);

		if (vers->id == GNUTLS_DTLS0_9)
			_mbuffer_set_uhead_size(bufel, 3);
		else
			_mbuffer_set_uhead_size(bufel, 1);
		_mbuffer_set_udata_size(bufel, 0);

		data = _mbuffer_get_uhead_ptr(bufel);

		data[0] = 1;
		if (vers->id == GNUTLS_DTLS0_9) {
			_gnutls_write_uint16(
				session->internals.dtls.hsk_write_seq,
				&data[1]);
			session->internals.dtls.hsk_write_seq++;
		}

		ret = _gnutls_call_hook_func(
			session, GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC,
			GNUTLS_HOOK_PRE, 0, data, 1);
		if (ret < 0) {
			_mbuffer_xfree(&bufel);
			return gnutls_assert_val(ret);
		}

		ret = _gnutls_handshake_io_cache_int(
			session, GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC, bufel);
		if (ret < 0) {
			_mbuffer_xfree(&bufel);
			return gnutls_assert_val(ret);
		}

		ret = _gnutls_call_hook_func(
			session, GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC,
			GNUTLS_HOOK_POST, 0, data, 1);
		if (ret < 0) {
			return gnutls_assert_val(ret);
		}

		/* under TLS 1.3, CCS may be immediately followed by
		 * receiving ClientHello thus cannot be cached */
		if (vers->tls13_sem) {
			ret = _gnutls_handshake_io_write_flush(session);
			if (ret < 0)
				return gnutls_assert_val(ret);
		}

		_gnutls_handshake_log("REC[%p]: Sent ChangeCipherSpec\n",
				      session);
	}

	return 0;
}

/* This function sends the final handshake packets and initializes connection
 */
static int send_handshake_final(gnutls_session_t session, int init)
{
	int ret = 0;

	/* Send the CHANGE CIPHER SPEC PACKET */

	switch (FINAL_STATE) {
	case STATE0:
	case STATE1:
		ret = _gnutls_send_change_cipher_spec(session, FAGAIN(STATE1));
		FINAL_STATE = STATE0;

		if (ret < 0) {
			gnutls_assert();
			return ret;
		}
		/* Initialize the connection session (start encryption) - in case of client
		 */
		if (init) {
			ret = _gnutls_connection_state_init(session);
			if (ret < 0) {
				gnutls_assert();
				return ret;
			}
		}

		ret = _gnutls_write_connection_state_init(session);
		if (ret < 0) {
			gnutls_assert();
			return ret;
		}

		FALLTHROUGH;
	case STATE2:
		/* send the finished message */
		ret = _gnutls_send_finished(session, FAGAIN(STATE2));
		FINAL_STATE = STATE2;
		if (ret < 0) {
			gnutls_assert();
			return ret;
		}

		FINAL_STATE = STATE0;
		FALLTHROUGH;
	default:
		break;
	}

	return 0;
}

/* This function receives the final handshake packets
 * And executes the appropriate function to initialize the
 * read session.
 */
static int recv_handshake_final(gnutls_session_t session, int init)
{
	int ret = 0;
	uint8_t ccs[3];
	unsigned int ccs_len = 1;
	unsigned int tleft;
	const version_entry_st *vers;

	ret = handshake_remaining_time(session);
	if (ret < 0)
		return gnutls_assert_val(ret);
	tleft = ret;

	switch (FINAL_STATE) {
	case STATE0:
	case STATE30:
		FINAL_STATE = STATE30;

		/* This is the last flight and peer cannot be sure
		 * we have received it unless we notify him. So we
		 * wait for a message and retransmit if needed. */
		if (IS_DTLS(session) && !_dtls_is_async(session) &&
		    (gnutls_record_check_pending(session) +
		     record_check_unprocessed(session)) == 0) {
			ret = _dtls_wait_and_retransmit(session);
			if (ret < 0)
				return gnutls_assert_val(ret);
		}

		vers = get_version(session);
		if (unlikely(vers == NULL))
			return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);

		if (vers->id == GNUTLS_DTLS0_9)
			ccs_len = 3;

		ret = _gnutls_recv_int(session, GNUTLS_CHANGE_CIPHER_SPEC, ccs,
				       ccs_len, NULL, tleft);
		if (ret <= 0) {
			gnutls_assert();
			return (ret < 0) ? ret : GNUTLS_E_UNEXPECTED_PACKET;
		}

		if (vers->id == GNUTLS_DTLS0_9)
			session->internals.dtls.hsk_read_seq++;

		/* Initialize the connection session (start encryption) - in case of server */
		if (init) {
			ret = _gnutls_connection_state_init(session);
			if (ret < 0) {
				gnutls_assert();
				return ret;
			}
		}

		ret = _gnutls_read_connection_state_init(session);
		if (ret < 0) {
			gnutls_assert();
			return ret;
		}
		FALLTHROUGH;
	case STATE31:
		FINAL_STATE = STATE31;

		if (IS_DTLS(session) && !_dtls_is_async(session) &&
		    (gnutls_record_check_pending(session) +
		     record_check_unprocessed(session)) == 0) {
			ret = _dtls_wait_and_retransmit(session);
			if (ret < 0)
				return gnutls_assert_val(ret);
		}

		ret = _gnutls_recv_finished(session);
		if (ret < 0) {
			gnutls_assert();
			return ret;
		}
		FINAL_STATE = STATE0;
		FALLTHROUGH;
	default:
		break;
	}

	return 0;
}

/*
 * handshake_server
 * This function does the server stuff of the handshake protocol.
 */
static int handshake_server(gnutls_session_t session)
{
	int ret = 0;
	const version_entry_st *ver;

reset:

	if (STATE >= STATE90)
		return _gnutls13_handshake_server(session);

	switch (STATE) {
	case STATE0:
	case STATE1:
		ret = _gnutls_recv_handshake(
			session, GNUTLS_HANDSHAKE_CLIENT_HELLO, 0, NULL);
		if (ret == GNUTLS_E_INT_RET_0) {
			/* this is triggered by post_client_hello, and instructs the
			 * handshake to proceed but be put on hold */
			ret = GNUTLS_E_INTERRUPTED;
			STATE = STATE2; /* hello already parsed -> move on */
		} else {
			STATE = STATE1;
		}

		if (ret == GNUTLS_E_NO_COMMON_KEY_SHARE) {
			STATE = STATE90;
			session->internals.hsk_flags |= HSK_HRR_SENT;
			goto reset;
		}

		IMED_RET("recv hello", ret, 1);
		FALLTHROUGH;
	case STATE2:

		ret = _gnutls_ext_sr_verify(session);
		STATE = STATE2;
		IMED_RET_FATAL("recv hello", ret, 0);
		FALLTHROUGH;
	case STATE3:
		ret = _gnutls_send_server_hello(session, AGAIN(STATE3));
		STATE = STATE3;
		IMED_RET("send hello", ret, 1);

		ver = get_version(session);
		if (ver->tls13_sem) { /* TLS 1.3 state machine */
			STATE = STATE99;
			goto reset;
		}

		FALLTHROUGH;
	case STATE4:
		if (session->security_parameters.do_send_supplemental) {
			ret = _gnutls_send_supplemental(session, AGAIN(STATE4));
			STATE = STATE4;
			IMED_RET("send supplemental data", ret, 0);
		}
		/* SEND CERTIFICATE + KEYEXCHANGE + CERTIFICATE_REQUEST */
		FALLTHROUGH;
	case STATE5:
		/* NOTE: these should not be send if we are resuming */

		if (!session->internals.resumed)
			ret = _gnutls_send_server_certificate(session,
							      AGAIN(STATE5));
		STATE = STATE5;
		IMED_RET("send server certificate", ret, 0);
		FALLTHROUGH;
	case STATE6:
#ifdef ENABLE_OCSP
		if (!session->internals.resumed)
			ret = _gnutls_send_server_certificate_status(
				session, AGAIN(STATE6));
		STATE = STATE6;
		IMED_RET("send server certificate status", ret, 0);
#endif
		FALLTHROUGH;
	case STATE7:
		/* send server key exchange (A) */
		if (!session->internals.resumed)
			ret = _gnutls_send_server_kx_message(session,
							     AGAIN(STATE7));
		STATE = STATE7;
		IMED_RET("send server kx", ret, 0);
		FALLTHROUGH;
	case STATE8:
		/* Send certificate request - if requested to */
		if (!session->internals.resumed)
			ret = _gnutls_send_server_crt_request(session,
							      AGAIN(STATE8));
		STATE = STATE8;
		IMED_RET("send server cert request", ret, 0);
		FALLTHROUGH;
	case STATE9:
		/* send the server hello done */
		if (!session->internals.resumed) /* if we are not resuming */
			ret = _gnutls_send_empty_handshake(
				session, GNUTLS_HANDSHAKE_SERVER_HELLO_DONE,
				AGAIN(STATE9));
		STATE = STATE9;
		IMED_RET("send server hello done", ret, 1);
		FALLTHROUGH;
	case STATE10:
		if (session->security_parameters.do_recv_supplemental) {
			ret = _gnutls_recv_supplemental(session);
			STATE = STATE10;
			IMED_RET("recv client supplemental", ret, 1);
		}
		/* RECV CERTIFICATE + KEYEXCHANGE + CERTIFICATE_VERIFY */
		FALLTHROUGH;
	case STATE11:
		/* receive the client certificate message */
		if (!session->internals.resumed) /* if we are not resuming */
			ret = _gnutls_recv_client_certificate(session);
		STATE = STATE11;
		IMED_RET("recv client certificate", ret, 1);
		FALLTHROUGH;
	case STATE12:
		ret = _gnutls_run_verify_callback(session, GNUTLS_SERVER);
		STATE = STATE12;
		if (ret < 0)
			return gnutls_assert_val(ret);
		FALLTHROUGH;
	case STATE13:
		/* receive the client key exchange message */
		if (!session->internals.resumed) /* if we are not resuming */
			ret = _gnutls_recv_client_kx_message(session);
		STATE = STATE13;
		IMED_RET("recv client kx", ret, 1);
		FALLTHROUGH;
	case STATE14:
		/* receive the client certificate verify message */
		if (!session->internals.resumed) /* if we are not resuming */
			ret = _gnutls_recv_client_certificate_verify_message(
				session);
		STATE = STATE14;
		IMED_RET("recv client certificate verify", ret, 1);
		FALLTHROUGH;
	case STATE15:
		STATE = STATE15;
		if (!session->internals.resumed) { /* if we are not resuming */
			ret = recv_handshake_final(session, true);
			IMED_RET("recv handshake final", ret, 1);
		} else {
			ret = send_handshake_final(session, true);
			IMED_RET("send handshake final 2", ret, 1);
		}
		FALLTHROUGH;
	case STATE16:
		ret = _gnutls_send_new_session_ticket(session, AGAIN(STATE16));
		STATE = STATE16;
		IMED_RET("send handshake new session ticket", ret, 0);
		FALLTHROUGH;
	case STATE17:
		STATE = STATE17;
		if (!session->internals.resumed) { /* if we are not resuming */
			ret = send_handshake_final(session, false);
			IMED_RET("send handshake final", ret, 1);

			if (session->security_parameters.entity ==
				    GNUTLS_SERVER &&
			    !(session->internals.hsk_flags &
			      HSK_TLS12_TICKET_SENT)) {
				/* if no ticket, save session data */
				_gnutls_server_register_current_session(
					session);
			}
		} else {
			ret = recv_handshake_final(session, false);
			IMED_RET("recv handshake final 2", ret, 1);
		}

		STATE = STATE0;
		FALLTHROUGH;
	default:
		break;
	}

	/* no lock of post_negotiation_lock is required here as this is not run
	 * after handshake */
	session->internals.initial_negotiation_completed = 1;

	return _gnutls_check_id_for_change(session);
}

int _gnutls_generate_session_id(uint8_t *session_id, uint8_t *len)
{
	int ret;

	*len = GNUTLS_DEF_SESSION_ID_SIZE;

	ret = gnutls_rnd(GNUTLS_RND_NONCE, session_id,
			 GNUTLS_DEF_SESSION_ID_SIZE);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	return 0;
}

/**
 * gnutls_handshake_set_max_packet_length:
 * @session: is a #gnutls_session_t type.
 * @max: is the maximum number.
 *
 * This function will set the maximum size of all handshake messages.
 * Handshakes over this size are rejected with
 * %GNUTLS_E_HANDSHAKE_TOO_LARGE error code.  The default value is
 * 128kb which is typically large enough.  Set this to 0 if you do not
 * want to set an upper limit.
 *
 * The reason for restricting the handshake message sizes are to
 * limit Denial of Service attacks.
 *
 * Note that the maximum handshake size was increased to 128kb
 * from 48kb in GnuTLS 3.5.5.
 **/
void gnutls_handshake_set_max_packet_length(gnutls_session_t session,
					    size_t max)
{
	session->internals.max_handshake_data_buffer_size = max;
}

/**
 * gnutls_handshake_get_last_in:
 * @session: is a #gnutls_session_t type.
 *
 * This function is only useful to check where the last performed
 * handshake failed.  If the previous handshake succeed or was not
 * performed at all then no meaningful value will be returned.
 *
 * Check %gnutls_handshake_description_t in gnutls.h for the
 * available handshake descriptions.
 *
 * Returns: the last handshake message type received, a
 * %gnutls_handshake_description_t.
 **/
gnutls_handshake_description_t
gnutls_handshake_get_last_in(gnutls_session_t session)
{
	return session->internals.last_handshake_in;
}

/**
 * gnutls_handshake_get_last_out:
 * @session: is a #gnutls_session_t type.
 *
 * This function is only useful to check where the last performed
 * handshake failed.  If the previous handshake succeed or was not
 * performed at all then no meaningful value will be returned.
 *
 * Check %gnutls_handshake_description_t in gnutls.h for the
 * available handshake descriptions.
 *
 * Returns: the last handshake message type sent, a
 * %gnutls_handshake_description_t.
 **/
gnutls_handshake_description_t
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.
 */
int _gnutls_handshake_get_session_hash(gnutls_session_t session,
				       gnutls_datum_t *shash)
{
	const version_entry_st *ver = get_version(session);
	int ret;
	uint8_t concat[2 * MAX_HASH_SIZE];

	if (unlikely(ver == NULL))
		return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);

	if (session->internals.handshake_hash_buffer_client_kx_len == 0 ||
	    (session->internals.handshake_hash_buffer.length <
	     session->internals.handshake_hash_buffer_client_kx_len)) {
		return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
	}

	ret = _gnutls_hash_fast(
		(gnutls_digest_algorithm_t)session->security_parameters.prf->id,
		session->internals.handshake_hash_buffer.data,
		session->internals.handshake_hash_buffer_client_kx_len, concat);
	if (ret < 0)
		return gnutls_assert_val(ret);

	return _gnutls_set_datum(shash, concat,
				 session->security_parameters.prf->output_size);
}
