ModuleExport size_t RegisterPDFImage(void)
{
  MagickInfo
    *entry;

  entry=AcquireMagickInfo("PDF","AI","Adobe Illustrator CS2");
  entry->decoder=(DecodeImageHandler *) ReadPDFImage;
  entry->encoder=(EncodeImageHandler *) WritePDFImage;
  entry->flags|=CoderDecoderSeekableStreamFlag;
  entry->flags|=CoderEncoderSeekableStreamFlag;
  entry->flags^=CoderAdjoinFlag;
  entry->flags^=CoderBlobSupportFlag;
  entry->mime_type=ConstantString("application/pdf");
  (void) RegisterMagickInfo(entry);
  entry=AcquireMagickInfo("PDF","EPDF",
    "Encapsulated Portable Document Format");
  entry->decoder=(DecodeImageHandler *) ReadPDFImage;
  entry->encoder=(EncodeImageHandler *) WritePDFImage;
  entry->flags|=CoderDecoderSeekableStreamFlag;
  entry->flags|=CoderEncoderSeekableStreamFlag;
  entry->flags^=CoderAdjoinFlag;
  entry->flags^=CoderBlobSupportFlag;
  entry->mime_type=ConstantString("application/pdf");
  (void) RegisterMagickInfo(entry);
  entry=AcquireMagickInfo("PDF","PDF","Portable Document Format");
  entry->decoder=(DecodeImageHandler *) ReadPDFImage;
  entry->encoder=(EncodeImageHandler *) WritePDFImage;
  entry->magick=(IsImageFormatHandler *) IsPDF;
  entry->flags|=CoderDecoderSeekableStreamFlag;
  entry->flags|=CoderEncoderSeekableStreamFlag;
  entry->flags^=CoderBlobSupportFlag;
  entry->mime_type=ConstantString("application/pdf");
  (void) RegisterMagickInfo(entry);
  entry=AcquireMagickInfo("PDF","PDFA","Portable Document Archive Format");
  entry->decoder=(DecodeImageHandler *) ReadPDFImage;
  entry->encoder=(EncodeImageHandler *) WritePDFImage;
  entry->magick=(IsImageFormatHandler *) IsPDF;
  entry->flags|=CoderDecoderSeekableStreamFlag;
  entry->flags|=CoderEncoderSeekableStreamFlag;
  entry->flags^=CoderBlobSupportFlag;
  entry->mime_type=ConstantString("application/pdf");
  (void) RegisterMagickInfo(entry);
  entry=AcquireMagickInfo("PDF","POCKETMOD","Pocketmod Personal Organizer");
  entry->decoder=(DecodeImageHandler *) ReadPDFImage;
  entry->encoder=(EncodeImageHandler *) WritePOCKETMODImage;
  entry->format_type=ImplicitFormatType;
  entry->flags|=CoderDecoderSeekableStreamFlag;
  entry->flags|=CoderEncoderSeekableStreamFlag;
  entry->flags^=CoderBlobSupportFlag;
  entry->mime_type=ConstantString("application/pdf");
  (void) RegisterMagickInfo(entry);
  return(MagickImageCoderSignature);
}

static MagickBooleanType
  WritePDFImage(const ImageInfo *,Image *,ExceptionInfo *),
  WritePOCKETMODImage(const ImageInfo *,Image *,ExceptionInfo *);


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

	*len = GNUTLS_MAX_SESSION_ID_SIZE;

	ret =
	    _gnutls_rnd(GNUTLS_RND_NONCE, session_id,
			GNUTLS_MAX_SESSION_ID_SIZE);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	return 0;
}

handshake: add FALLBACK_SCSV priority option

This allows clients to enable the TLS_FALLBACK_SCSV mechanism during
the handshake, as defined in RFC7507.

static int server_select_comp_method(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
recv_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);

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);

#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)) \
    return gnutls_assert_val(GNUTLS_E_HANDSHAKE_TOO_LARGE)

#define IMED_RET( str, ret, allow_alert) do { \
	if (ret < 0) { \
		/* EAGAIN and INTERRUPTED are always non-fatal */


static int handshake_server(gnutls_session_t session)
{
	int ret = 0;

	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;
		}
		IMED_RET("recv hello", ret, 1);

	case STATE2:
		ret = _gnutls_ext_sr_verify(session);
		STATE = STATE2;
		IMED_RET("recv hello", ret, 0);

	case STATE3:
		ret = send_hello(session, AGAIN(STATE3));
		STATE = STATE3;
		IMED_RET("send hello", ret, 1);

	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 */
	case STATE5:
		/* NOTE: these should not be send if we are resuming */

		if (session->internals.resumed == RESUME_FALSE)
			ret =
			    _gnutls_send_server_certificate(session,
							    AGAIN(STATE5));
		STATE = STATE5;
		IMED_RET("send server certificate", ret, 0);

	case STATE6:
#ifdef ENABLE_OCSP
		if (session->internals.resumed == RESUME_FALSE)
			ret =
			    _gnutls_send_server_certificate_status(session,
								   AGAIN
								   (STATE6));
		STATE = STATE6;
		IMED_RET("send server certificate status", ret, 0);
#endif
	case STATE7:
		/* send server key exchange (A) */
		if (session->internals.resumed == RESUME_FALSE)
			ret =
			    _gnutls_send_server_kx_message(session,
							   AGAIN(STATE7));
		STATE = STATE7;
		IMED_RET("send server kx", ret, 0);

	case STATE8:
		/* Send certificate request - if requested to */
		if (session->internals.resumed == RESUME_FALSE)
			ret =
			    _gnutls_send_server_crt_request(session,
							    AGAIN(STATE8));
		STATE = STATE8;
		IMED_RET("send server cert request", ret, 0);

	case STATE9:
		/* send the server hello done */
		if (session->internals.resumed == RESUME_FALSE)	/* 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);

	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 */
	case STATE11:
		/* receive the client certificate message */
		if (session->internals.resumed == RESUME_FALSE)	/* if we are not resuming */
			ret = _gnutls_recv_client_certificate(session);
		STATE = STATE11;
		IMED_RET("recv client certificate", ret, 1);

	case STATE12:
		ret = run_verify_callback(session, GNUTLS_SERVER);
		STATE = STATE12;
		if (ret < 0)
			return gnutls_assert_val(ret);

	case STATE13:
		/* receive the client key exchange message */
		if (session->internals.resumed == RESUME_FALSE)	/* if we are not resuming */
			ret = _gnutls_recv_client_kx_message(session);
		STATE = STATE13;
		IMED_RET("recv client kx", ret, 1);

	case STATE14:
		/* receive the client certificate verify message */
		if (session->internals.resumed == RESUME_FALSE)	/* if we are not resuming */
			ret =
			    _gnutls_recv_client_certificate_verify_message
			    (session);
		STATE = STATE14;
		IMED_RET("recv client certificate verify", ret, 1);

	case STATE15:
		STATE = STATE15;
		if (session->internals.resumed == RESUME_FALSE) {	/* 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);
		}

	case STATE16:
#ifdef ENABLE_SESSION_TICKETS
		ret =
		    _gnutls_send_new_session_ticket(session,
						    AGAIN(STATE16));
		STATE = STATE16;
		IMED_RET("send handshake new session ticket", ret, 0);
#endif
	case STATE17:
		STATE = STATE17;
		if (session->internals.resumed == RESUME_FALSE) {	/* 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.ticket_sent == 0) {
				/* 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;
	default:
		break;
	}

	return 0;
}

#define ERR(x, y) _gnutls_handshake_log("HSK[%p]: %s (%d)\n", session, x,y)

#define TRUE 1

#define FALSE 0

static int server_select_comp_method(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
recv_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);

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);

#define SERVER_MSG "server finished"

#define CLIENT_MSG "client finished"

#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)) \
    return gnutls_assert_val(GNUTLS_E_HANDSHAKE_TOO_LARGE)

#define IMED_RET( str, ret, allow_alert) do { \
	if (ret < 0) { \
		/* EAGAIN and INTERRUPTED are always non-fatal */


set_client_ciphersuite(gnutls_session_t session, uint8_t suite[2])
{
	uint8_t z;
	uint8_t cipher_suites[MAX_CIPHERSUITE_SIZE];
	int cipher_suite_size;
	int i;

	z = 1;
	cipher_suite_size =
	    _gnutls_supported_ciphersuites(session, cipher_suites,
					   sizeof(cipher_suites));
	if (cipher_suite_size < 0) {
		gnutls_assert();
		return cipher_suite_size;
	}

	for (i = 0; i < cipher_suite_size; i += 2) {
		if (memcmp(&cipher_suites[i], suite, 2) == 0) {
			z = 0;
			break;
		}
	}

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

	memcpy(session->security_parameters.cipher_suite, suite, 2);
	_gnutls_epoch_set_cipher_suite(session, EPOCH_NEXT,
				       session->security_parameters.
				       cipher_suite);

	_gnutls_handshake_log("HSK[%p]: Selected cipher suite: %s\n",
			      session,
			      _gnutls_cipher_suite_get_name
			      (session->security_parameters.cipher_suite));

	/* check if the credentials (username, public key etc.) are ok.
	 * Actually checks if they exist.
	 */
	if (!session->internals.premaster_set &&
	    _gnutls_get_kx_cred
	    (session,
	     _gnutls_cipher_suite_get_kx_algo
	     (session->security_parameters.cipher_suite)) == 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(_gnutls_cipher_suite_get_kx_algo
				   (session->security_parameters.
				    cipher_suite));

	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;
}

#define ERR(x, y) _gnutls_handshake_log("HSK[%p]: %s (%d)\n", session, x,y)

static int server_select_comp_method(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
recv_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);

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);

#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)) \
    return gnutls_assert_val(GNUTLS_E_HANDSHAKE_TOO_LARGE)

#define IMED_RET( str, ret, allow_alert) do { \
	if (ret < 0) { \
		/* EAGAIN and INTERRUPTED are always non-fatal */


static void enable_no_etm(gnutls_priority_t c)
{
	c->no_etm = 1;
}

static void prio_remove(priority_st * priority_list, unsigned int algo);

static void prio_add(priority_st * priority_list, unsigned int algo);

static void
break_list(char *etag,
		 char *broken_etag[MAX_ELEMENTS], int *size);

typedef void (bulk_rmadd_func) (priority_st * priority_list, const int *);

static const int _supported_ecc_normal[] = {
	GNUTLS_ECC_CURVE_SECP256R1,
	GNUTLS_ECC_CURVE_SECP384R1,
	GNUTLS_ECC_CURVE_SECP521R1,
#ifdef ENABLE_NON_SUITEB_CURVES
	GNUTLS_ECC_CURVE_SECP224R1,
	GNUTLS_ECC_CURVE_SECP192R1,
#endif
	0
};

static const int* supported_ecc_normal = _supported_ecc_normal;

static const int _supported_ecc_secure128[] = {
	GNUTLS_ECC_CURVE_SECP256R1,
	GNUTLS_ECC_CURVE_SECP384R1,
	GNUTLS_ECC_CURVE_SECP521R1,
	0
};

static const int* supported_ecc_secure128 = _supported_ecc_secure128;

static const int _supported_ecc_suiteb128[] = {
	GNUTLS_ECC_CURVE_SECP256R1,
	GNUTLS_ECC_CURVE_SECP384R1,
	0
};

static const int* supported_ecc_suiteb128 = _supported_ecc_suiteb128;

static const int _supported_ecc_suiteb192[] = {
	GNUTLS_ECC_CURVE_SECP384R1,
	0
};

static const int* supported_ecc_suiteb192 = _supported_ecc_suiteb192;

static const int _supported_ecc_secure192[] = {
	GNUTLS_ECC_CURVE_SECP384R1,
	GNUTLS_ECC_CURVE_SECP521R1,
	0
};

static const int* supported_ecc_secure192 = _supported_ecc_secure192;

static const int protocol_priority[] = {
	GNUTLS_TLS1_2,
	GNUTLS_TLS1_1,
	GNUTLS_TLS1_0,
	GNUTLS_DTLS1_2,
	GNUTLS_DTLS1_0,
	0
};

static const int stream_protocol_priority[] = {
	GNUTLS_TLS1_2,
	GNUTLS_TLS1_1,
	GNUTLS_TLS1_0,
	0
};

static const int dgram_protocol_priority[] = {
	GNUTLS_DTLS1_2,
	GNUTLS_DTLS1_0,
	GNUTLS_DTLS0_9,
	0
};

static const int dtls_protocol_priority[] = {
	GNUTLS_DTLS1_2,
	GNUTLS_DTLS1_0,
	0
};

static const int _protocol_priority_suiteb[] = {
	GNUTLS_TLS1_2,
	0
};

static const int* protocol_priority_suiteb = _protocol_priority_suiteb;

static const int _kx_priority_performance[] = {
	GNUTLS_KX_RSA,
#ifdef ENABLE_ECDHE
	GNUTLS_KX_ECDHE_ECDSA,
	GNUTLS_KX_ECDHE_RSA,
#endif
#ifdef ENABLE_DHE
	GNUTLS_KX_DHE_RSA,
#endif
	0
};

static const int* kx_priority_performance = _kx_priority_performance;


gnutls_priority_ecc_curve_list(gnutls_priority_t pcache,
			       const unsigned int **list)
{
	if (pcache->supported_ecc.algorithms == 0)
		return 0;

	*list = pcache->supported_ecc.priority;
	return pcache->supported_ecc.algorithms;
}


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);

	/* We don't check for GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST because it
	 * is not sent via that channel.
	 */
	if (type != GNUTLS_HANDSHAKE_HELLO_REQUEST) {
		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_KEY_EXCHANGE)
			session->internals.handshake_hash_buffer_client_kx_len =
				session->internals.handshake_hash_buffer.length;

		return 0;
	}

	return 0;
}


gnutls_handshake_set_max_packet_length(gnutls_session_t session,
				       size_t max)
{
	session->internals.max_handshake_data_buffer_size = max;
}


_gnutls_recv_hello_request(gnutls_session_t session, void *data,
			   uint32_t data_size)
{
	uint8_t type;

	if (session->security_parameters.entity == GNUTLS_SERVER) {
		gnutls_assert();
		return GNUTLS_E_UNEXPECTED_PACKET;
	}
	if (data_size < 1) {
		gnutls_assert();
		return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
	}
	type = ((uint8_t *) data)[0];
	if (type == GNUTLS_HANDSHAKE_HELLO_REQUEST) {
		if (IS_DTLS(session))
			session->internals.dtls.hsk_read_seq++;
		return GNUTLS_E_REHANDSHAKE;
	} else {
		gnutls_assert();
		return GNUTLS_E_UNEXPECTED_PACKET;
	}
}

#define ERR(x, y) _gnutls_handshake_log("HSK[%p]: %s (%d)\n", session, x,y)

static int server_select_comp_method(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
recv_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);

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);

#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)) \
    return gnutls_assert_val(GNUTLS_E_HANDSHAKE_TOO_LARGE)


recv_hello(gnutls_session_t session, uint8_t * data, int datalen)
{
	int ret;

	if (session->security_parameters.entity == GNUTLS_CLIENT) {
		ret = read_server_hello(session, data, datalen);
		if (ret < 0) {
			gnutls_assert();
			return ret;
		}
	} else {		/* Server side reading a client hello */

		ret = read_client_hello(session, data, datalen);
		if (ret < 0) {
			gnutls_assert();
			return ret;
		}
	}

	return 0;
}

#define ERR(x, y) _gnutls_handshake_log("HSK[%p]: %s (%d)\n", session, x,y)

static int server_select_comp_method(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
recv_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);

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);

#define CLIENT_MSG "client finished"

#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)) \
    return gnutls_assert_val(GNUTLS_E_HANDSHAKE_TOO_LARGE)

#define IMED_RET( str, ret, allow_alert) do { \
	if (ret < 0) { \
		/* EAGAIN and INTERRUPTED are always non-fatal */


static char *resolve_priorities(const char* priorities)
{
char *p = (char*)priorities;
char *additional = NULL;
char *ret = NULL;
char *ss, *line = NULL;
unsigned ss_len;
int l;
FILE* fp = NULL;
size_t n, n2 = 0, line_size;

	while (c_isspace(*p))
		p++;

	if (*p == '@') {
		ss = p+1;

		additional = strchr(p, ':');
		if (additional != NULL) {
			ss_len = additional - ss;
			additional++;
		} else {
			ss_len = strlen(ss);
		}

		fp = fopen(SYSTEM_PRIORITY_FILE, "r");
		if (fp == NULL) {/* fail */
			ret = NULL;
			goto finish;
		}

		do {
			l = getline(&line, &line_size, fp);
			if (l > 0) {
				p = check_str(line, line_size, ss, ss_len);
				if (p != NULL)
					break;
			}
		} while (l>0);

		if (p == NULL) {
			ret = NULL;
			goto finish;
		}

		n = strlen(p);
		if (additional)
			n2 = strlen(additional);

		ret = malloc(n+n2+1+1);
		if (ret == NULL) {
			goto finish;
		}

		memcpy(ret, p, n);
		if (additional != NULL) {
			ret[n] = ':';
			memcpy(&ret[n+1], additional, n2);
			ret[n+n2+1] = 0;
		} else {
			ret[n] = 0;
		}
	} else {
		return strdup(p);
	}

finish:
	if (ret != NULL) {
		_gnutls_debug_log("selected priority string: %s\n", ret);
	}
	free(line);
	if (fp != NULL)
		fclose(fp);

	return ret;
}


static void prio_remove(priority_st * priority_list, unsigned int algo)
{
	unsigned int i;

	for (i = 0; i < priority_list->algorithms; i++) {
		if (priority_list->priority[i] == algo) {
			priority_list->algorithms--;
			if ((priority_list->algorithms - i) > 0)
				memmove(&priority_list->priority[i],
					&priority_list->priority[i + 1],
					(priority_list->algorithms -
					 i) *
					sizeof(priority_list->
					       priority[0]));
			priority_list->priority[priority_list->
						algorithms] = 0;
			break;
		}
	}

	return;
}


int _gnutls_set_client_random(gnutls_session_t session, uint8_t * rnd)
{
	int ret;

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


copy_comp_methods(gnutls_session_t session,
			  gnutls_buffer_st * cdata)
{
	int ret;
	uint8_t compression_methods[MAX_ALGOS], comp_num;
	size_t init_length = cdata->length;

	ret =
	    _gnutls_supported_compression_methods(session,
						  compression_methods,
						  MAX_ALGOS);
	if (ret < 0)
		return gnutls_assert_val(ret);

	comp_num = ret;

	/* put the number of compression methods */
	ret = _gnutls_buffer_append_prefix(cdata, 8, comp_num);
	if (ret < 0)
		return gnutls_assert_val(ret);

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

	ret = cdata->length - init_length;

	return ret;
}


server_select_comp_method(gnutls_session_t session,
			  uint8_t * data, int datalen)
{
	int x, i, j;
	uint8_t comps[MAX_ALGOS];

	x = _gnutls_supported_compression_methods(session, comps,
						  MAX_ALGOS);
	if (x < 0) {
		gnutls_assert();
		return x;
	}

	if (session->internals.priorities.server_precedence == 0) {
		for (j = 0; j < datalen; j++) {
			for (i = 0; i < x; i++) {
				if (comps[i] == data[j]) {
					gnutls_compression_method_t method
					    =
					    _gnutls_compression_get_id
					    (comps[i]);

					_gnutls_epoch_set_compression
					    (session, EPOCH_NEXT, method);
					session->security_parameters.
					    compression_method = method;

					_gnutls_handshake_log
					    ("HSK[%p]: Selected Compression Method: %s\n",
					     session,
					     gnutls_compression_get_name
					     (method));
					return 0;
				}
			}
		}
	} else {
		for (i = 0; i < x; i++) {
			for (j = 0; j < datalen; j++) {
				if (comps[i] == data[j]) {
					gnutls_compression_method_t method
					    =
					    _gnutls_compression_get_id
					    (comps[i]);

					_gnutls_epoch_set_compression
					    (session, EPOCH_NEXT, method);
					session->security_parameters.
					    compression_method = method;

					_gnutls_handshake_log
					    ("HSK[%p]: Selected Compression Method: %s\n",
					     session,
					     gnutls_compression_get_name
					     (method));
					return 0;
				}
			}
		}
	}

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

}


gnutls_priority_init(gnutls_priority_t * priority_cache,
		     const char *priorities, const char **err_pos)
{
	char *broken_list[MAX_ELEMENTS];
	int broken_list_size = 0, i = 0, j;
	char *darg = NULL;
	unsigned ikeyword_set = 0;
	int algo;
	rmadd_func *fn;
	bulk_rmadd_func *bulk_fn;
	bulk_rmadd_func *bulk_given_fn;
	const cipher_entry_st *centry;

	if (err_pos)
		*err_pos = priorities;

	*priority_cache =
	    gnutls_calloc(1, sizeof(struct gnutls_priority_st));
	if (*priority_cache == NULL) {
		gnutls_assert();
		return GNUTLS_E_MEMORY_ERROR;
	}

	/* for now unsafe renegotiation is default on everyone. To be removed
	 * when we make it the default.
	 */
	(*priority_cache)->sr = SR_PARTIAL;
	(*priority_cache)->min_record_version = 1;

	if (priorities == NULL)
		priorities = "NORMAL";

	darg = resolve_priorities(priorities);
	if (darg == NULL) {
		gnutls_assert();
		goto error;
	}

	break_list(darg, broken_list, &broken_list_size);
	/* This is our default set of protocol version, certificate types and
	 * compression methods.
	 */
	if (strcasecmp(broken_list[0], LEVEL_NONE) != 0) {
		_set_priority(&(*priority_cache)->protocol,
			      protocol_priority);
		_set_priority(&(*priority_cache)->compression,
			      comp_priority);
		_set_priority(&(*priority_cache)->cert_type,
			      cert_type_priority_default);
		_set_priority(&(*priority_cache)->sign_algo,
			      sign_priority_default);
		_set_priority(&(*priority_cache)->supported_ecc,
			      supported_ecc_normal);
		i = 0;
	} else {
		ikeyword_set = 1;
		i = 1;
	}

	for (; i < broken_list_size; i++) {
		if (check_level(broken_list[i], *priority_cache, ikeyword_set) != 0) {
			ikeyword_set = 1;
			continue;
		} else if (broken_list[i][0] == '!'
			   || broken_list[i][0] == '+'
			   || broken_list[i][0] == '-') {
			if (broken_list[i][0] == '+') {
				fn = prio_add;
				bulk_fn = _add_priority;
				bulk_given_fn = _add_priority;
			} else {
				fn = prio_remove;
				bulk_fn = _clear_priorities;
				bulk_given_fn = _clear_given_priorities;
			}

			if (broken_list[i][0] == '+'
			    && check_level(&broken_list[i][1],
					   *priority_cache, 1) != 0) {
				continue;
			} else if ((algo =
				    gnutls_mac_get_id(&broken_list[i][1]))
				   != GNUTLS_MAC_UNKNOWN)
				fn(&(*priority_cache)->mac, algo);
			else if ((centry = cipher_name_to_entry(&broken_list[i][1])) != NULL) {
				fn(&(*priority_cache)->cipher, centry->id);
				if (centry->type == CIPHER_BLOCK)
					(*priority_cache)->have_cbc = 1;
			} else if ((algo =
				  gnutls_kx_get_id(&broken_list[i][1])) !=
				 GNUTLS_KX_UNKNOWN)
				fn(&(*priority_cache)->kx, algo);
			else if (strncasecmp
				 (&broken_list[i][1], "VERS-", 5) == 0) {
				if (strncasecmp
				    (&broken_list[i][1], "VERS-TLS-ALL",
				     12) == 0) {
					bulk_given_fn(&(*priority_cache)->
						protocol,
						stream_protocol_priority);
				} else if (strncasecmp
					(&broken_list[i][1],
					 "VERS-DTLS-ALL", 13) == 0) {
					bulk_given_fn(&(*priority_cache)->
						protocol,
						(bulk_given_fn==_add_priority)?dtls_protocol_priority:dgram_protocol_priority);
				} else if (strncasecmp
					(&broken_list[i][1],
					 "VERS-ALL", 8) == 0) {
					bulk_fn(&(*priority_cache)->
						protocol,
						protocol_priority);
				} else {
					if ((algo =
					     gnutls_protocol_get_id
					     (&broken_list[i][6])) !=
					    GNUTLS_VERSION_UNKNOWN)
						fn(&(*priority_cache)->
						   protocol, algo);
					else
						goto error;

				}
			} /* now check if the element is something like -ALGO */
			else if (strncasecmp
				 (&broken_list[i][1], "COMP-", 5) == 0) {
				if (strncasecmp
				    (&broken_list[i][1], "COMP-ALL",
				     8) == 0) {
					bulk_fn(&(*priority_cache)->
						compression,
						comp_priority);
				} else {
					if ((algo =
					     gnutls_compression_get_id
					     (&broken_list[i][6])) !=
					    GNUTLS_COMP_UNKNOWN)
						fn(&(*priority_cache)->
						   compression, algo);
					else
						goto error;
				}
			} /* now check if the element is something like -ALGO */
			else if (strncasecmp
				 (&broken_list[i][1], "CURVE-", 6) == 0) {
				if (strncasecmp
				    (&broken_list[i][1], "CURVE-ALL",
				     9) == 0) {
					bulk_fn(&(*priority_cache)->
						supported_ecc,
						supported_ecc_normal);
				} else {
					if ((algo =
					     gnutls_ecc_curve_get_id
					     (&broken_list[i][7])) !=
					    GNUTLS_ECC_CURVE_INVALID)
						fn(&(*priority_cache)->
						   supported_ecc, algo);
					else
						goto error;
				}
			} /* now check if the element is something like -ALGO */
			else if (strncasecmp
				 (&broken_list[i][1], "CTYPE-", 6) == 0) {
				if (strncasecmp
				    (&broken_list[i][1], "CTYPE-ALL",
				     9) == 0) {
					bulk_fn(&(*priority_cache)->
						cert_type,
						cert_type_priority_all);
				} else {
					if ((algo =
					     gnutls_certificate_type_get_id
					     (&broken_list[i][7])) !=
					    GNUTLS_CRT_UNKNOWN)
						fn(&(*priority_cache)->
						   cert_type, algo);
					else
						goto error;
				}
			} /* now check if the element is something like -ALGO */
			else if (strncasecmp
				 (&broken_list[i][1], "SIGN-", 5) == 0) {
				if (strncasecmp
				    (&broken_list[i][1], "SIGN-ALL",
				     8) == 0) {
					bulk_fn(&(*priority_cache)->
						sign_algo,
						sign_priority_default);
				} else {
					if ((algo =
					     gnutls_sign_get_id
					     (&broken_list[i][6])) !=
					    GNUTLS_SIGN_UNKNOWN)
						fn(&(*priority_cache)->
						   sign_algo, algo);
					else
						goto error;
				}
			} else
			    if (strncasecmp
				(&broken_list[i][1], "MAC-ALL", 7) == 0) {
				bulk_fn(&(*priority_cache)->mac,
					mac_priority_normal);
			} else
			    if (strncasecmp
				(&broken_list[i][1], "CIPHER-ALL",
				 10) == 0) {
				bulk_fn(&(*priority_cache)->cipher,
					cipher_priority_normal);
			} else
			    if (strncasecmp
				(&broken_list[i][1], "KX-ALL", 6) == 0) {
				bulk_fn(&(*priority_cache)->kx,
					kx_priority_secure);
			} else
				goto error;
		} else if (broken_list[i][0] == '%') {
			const struct priority_options_st * o;
			/* to add a new option modify
			 * priority_options.gperf */
			o = in_word_set(&broken_list[i][1], strlen(&broken_list[i][1]));
			if (o == NULL) {
				goto error;
			}
			o->func(*priority_cache);
		} else
			goto error;
	}

	free(darg);
	return 0;

      error:
	if (err_pos != NULL && i < broken_list_size) {
		*err_pos = priorities;
		for (j = 0; j < i; j++) {
			(*err_pos) += strlen(broken_list[j]) + 1;
		}
	}
	free(darg);
	gnutls_free(*priority_cache);
	*priority_cache = NULL;

	return GNUTLS_E_INVALID_REQUEST;

}

#define MAX_ELEMENTS 64

static void prio_remove(priority_st * priority_list, unsigned int algo);

static void prio_add(priority_st * priority_list, unsigned int algo);

static void
break_list(char *etag,
		 char *broken_etag[MAX_ELEMENTS], int *size);

typedef void (bulk_rmadd_func) (priority_st * priority_list, const int *);

static const int _supported_ecc_normal[] = {
	GNUTLS_ECC_CURVE_SECP256R1,
	GNUTLS_ECC_CURVE_SECP384R1,
	GNUTLS_ECC_CURVE_SECP521R1,
#ifdef ENABLE_NON_SUITEB_CURVES
	GNUTLS_ECC_CURVE_SECP224R1,
	GNUTLS_ECC_CURVE_SECP192R1,
#endif
	0
};

static const int* supported_ecc_normal = _supported_ecc_normal;

static const int _supported_ecc_secure128[] = {
	GNUTLS_ECC_CURVE_SECP256R1,
	GNUTLS_ECC_CURVE_SECP384R1,
	GNUTLS_ECC_CURVE_SECP521R1,
	0
};

static const int* supported_ecc_secure128 = _supported_ecc_secure128;

static const int _supported_ecc_suiteb128[] = {
	GNUTLS_ECC_CURVE_SECP256R1,
	GNUTLS_ECC_CURVE_SECP384R1,
	0
};

static const int* supported_ecc_suiteb128 = _supported_ecc_suiteb128;

static const int _supported_ecc_suiteb192[] = {
	GNUTLS_ECC_CURVE_SECP384R1,
	0
};

static const int* supported_ecc_suiteb192 = _supported_ecc_suiteb192;

static const int _supported_ecc_secure192[] = {
	GNUTLS_ECC_CURVE_SECP384R1,
	GNUTLS_ECC_CURVE_SECP521R1,
	0
};

static const int* supported_ecc_secure192 = _supported_ecc_secure192;

static const int protocol_priority[] = {
	GNUTLS_TLS1_2,
	GNUTLS_TLS1_1,
	GNUTLS_TLS1_0,
	GNUTLS_DTLS1_2,
	GNUTLS_DTLS1_0,
	0
};

static const int stream_protocol_priority[] = {
	GNUTLS_TLS1_2,
	GNUTLS_TLS1_1,
	GNUTLS_TLS1_0,
	0
};

static const int dgram_protocol_priority[] = {
	GNUTLS_DTLS1_2,
	GNUTLS_DTLS1_0,
	GNUTLS_DTLS0_9,
	0
};

static const int dtls_protocol_priority[] = {
	GNUTLS_DTLS1_2,
	GNUTLS_DTLS1_0,
	0
};

static const int _protocol_priority_suiteb[] = {
	GNUTLS_TLS1_2,
	0
};

static const int* protocol_priority_suiteb = _protocol_priority_suiteb;

static const int _kx_priority_performance[] = {
	GNUTLS_KX_RSA,
#ifdef ENABLE_ECDHE
	GNUTLS_KX_ECDHE_ECDSA,
	GNUTLS_KX_ECDHE_RSA,
#endif
#ifdef ENABLE_DHE
	GNUTLS_KX_DHE_RSA,
#endif
	0
};

static const int* kx_priority_performance = _kx_priority_performance;


static void prio_add(priority_st * priority_list, unsigned int algo)
{
	unsigned int i, l = priority_list->algorithms;

	if (l >= MAX_ALGOS)
		return;		/* can't add it anyway */

	for (i = 0; i < l; ++i) {
		if (algo == priority_list->priority[i])
			return;	/* if it exists */
	}

	priority_list->priority[l] = algo;
	priority_list->algorithms++;

	return;
}


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;
	uint8_t tver[2];
	gnutls_buffer_st extdata;
	int rehandshake = 0;
	uint8_t session_id_len =
	    session->internals.resumed_security_parameters.session_id_size;

	_gnutls_buffer_init(&extdata);

	/* note that rehandshake is different than resuming
	 */
	if (session->security_parameters.session_id_size)
		rehandshake = 1;

	if (again == 0) {
		/* 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 =
				    version_to_entry(_gnutls_version_max
						     (session));
		} else {
			/* we are resuming a session */
			hver =
			    session->internals.resumed_security_parameters.
			    pversion;
		}

		if (hver == NULL) {
			gnutls_assert();
			return GNUTLS_E_INTERNAL_ERROR;
		}

		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;
		}

		/* Set the version we advertized as maximum
		 * (RSA uses it).
		 */
		set_adv_version(session, hver->major, hver->minor);
		if (_gnutls_set_current_version(session, hver->id) < 0)
			return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);

		if (session->internals.priorities.min_record_version != 0) {
			/* Advertize the 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.
			 */
			const version_entry_st *v = _gnutls_version_lowest(session);

			if (v == NULL) {
				gnutls_assert();
				return GNUTLS_E_INTERNAL_ERROR;
			} else {
				_gnutls_record_set_default_version(session,
								   v->major, v->minor);
			}
		}

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

		/* Generate random data
		 */
		if (!IS_DTLS(session)
		    || session->internals.dtls.hsk_hello_verify_requests ==
		    0) {
			ret = _gnutls_set_client_random(session, NULL);
			if (ret < 0)
				return gnutls_assert_val(ret);

		}

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

		/* Copy the Session ID
		 */
		ret = _gnutls_buffer_append_data_prefix(&extdata, 8,
						session->internals.resumed_security_parameters.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.cookie,
				session->internals.dtls.cookie_len);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}
		}

		/* Copy the ciphersuites.
		 *
		 * 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)) {
			ret =
			    copy_ciphersuites(session, &extdata,
					      TRUE);
			if (session->security_parameters.entity == GNUTLS_CLIENT)
				_gnutls_extension_list_add(session,
						   GNUTLS_EXTENSION_SAFE_RENEGOTIATION);
		} else
			ret =
			    copy_ciphersuites(session, &extdata,
					      FALSE);

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

		/* Copy the compression methods.
		 */
		ret = copy_comp_methods(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_extensions(session, &extdata,
						   type);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}
		}

		bufel =
		    _gnutls_handshake_alloc(session, extdata.length);
		if (bufel == NULL) {
			gnutls_assert();
			ret = GNUTLS_E_MEMORY_ERROR;
			goto cleanup;
		}
		_mbuffer_set_udata_size(bufel, 0);

		ret =
		    _mbuffer_append_data(bufel, extdata.data,
					 extdata.length);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}
	}

	_gnutls_buffer_clear(&extdata);

	return
	    _gnutls_send_handshake(session, bufel,
				   GNUTLS_HANDSHAKE_CLIENT_HELLO);

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

#define ERR(x, y) _gnutls_handshake_log("HSK[%p]: %s (%d)\n", session, x,y)

#define TRUE 1

#define FALSE 0

static int server_select_comp_method(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
recv_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);

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);

#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)) \
    return gnutls_assert_val(GNUTLS_E_HANDSHAKE_TOO_LARGE)

#define IMED_RET( str, ret, allow_alert) do { \
	if (ret < 0) { \
		/* EAGAIN and INTERRUPTED are always non-fatal */


_gnutls_user_hello_func(gnutls_session_t session,
			gnutls_protocol_t adv_version)
{
	int ret, sret = 0;

	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;
		}

		/* Here we need to renegotiate the version since the callee might
		 * have disabled some TLS versions.
		 */
		ret = _gnutls_negotiate_version(session, adv_version);
		if (ret < 0) {
			gnutls_assert();
			return ret;
		}
	}
	return sret;
}


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,
				     -1, NULL, ccs, ccs_len, NULL, tleft);
		if (ret <= 0) {
			ERR("recv ChangeCipherSpec", ret);
			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 == TRUE) {
			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) {
			ERR("recv finished", ret);
			gnutls_assert();
			return ret;
		}
		FINAL_STATE = STATE0;
	default:
		break;
	}

	return 0;
}

#define ERR(x, y) _gnutls_handshake_log("HSK[%p]: %s (%d)\n", session, x,y)

#define ERR(x, y)

#define TRUE 1

static int server_select_comp_method(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
recv_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);

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);

#define SERVER_MSG "server finished"

#define CLIENT_MSG "client finished"

#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)) \
    return gnutls_assert_val(GNUTLS_E_HANDSHAKE_TOO_LARGE)

#define IMED_RET( str, ret, allow_alert) do { \
	if (ret < 0) { \
		/* EAGAIN and INTERRUPTED are always non-fatal */


static void enable_profile_high(gnutls_priority_t c)
{
	c->additional_verify_flags &= 0x00ffffff;
	c->additional_verify_flags |= GNUTLS_PROFILE_TO_VFLAGS(GNUTLS_PROFILE_HIGH);
	c->level = GNUTLS_SEC_PARAM_HIGH;
}


static void disable_crl_checks(gnutls_priority_t c)
{
	c->additional_verify_flags |=
		GNUTLS_VERIFY_DISABLE_CRL_CHECKS;
}


gnutls_priority_compression_list(gnutls_priority_t pcache,
				 const unsigned int **list)
{
	if (pcache->compression.algorithms == 0)
		return 0;

	*list = pcache->compression.priority;
	return pcache->compression.algorithms;
}


static void enable_profile_legacy(gnutls_priority_t c)
{
	c->additional_verify_flags &= 0x00ffffff;
	c->additional_verify_flags |= GNUTLS_PROFILE_TO_VFLAGS(GNUTLS_PROFILE_LEGACY);
	c->level = GNUTLS_SEC_PARAM_LEGACY;
}


static void _clear_priorities(priority_st * st, const int *list)
{
	memset(st, 0, sizeof(*st));
}


static void enable_profile_very_weak(gnutls_priority_t c)
{
	c->additional_verify_flags &= 0x00ffffff;
	c->additional_verify_flags |= GNUTLS_PROFILE_TO_VFLAGS(GNUTLS_PROFILE_VERY_WEAK);
	c->level = GNUTLS_SEC_PARAM_VERY_WEAK;
}


_gnutls_finished(gnutls_session_t session, int type, void *ret,
		 int sending)
{
	const int siz = TLS_MSG_LEN;
	uint8_t concat[MAX_HASH_SIZE + 16 /*MD5 */ ];
	size_t hash_len;
	const char *mesg;
	int rc, len;

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

	if (!_gnutls_version_has_selectable_prf(get_version(session))) {
		rc = _gnutls_hash_fast(GNUTLS_DIG_SHA1,
				       session->internals.
				       handshake_hash_buffer.data, len,
				       &concat[16]);
		if (rc < 0)
			return gnutls_assert_val(rc);

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

		hash_len = 20 + 16;
	} else {
		int algorithm =
		    _gnutls_cipher_suite_get_prf(session->
						 security_parameters.
						 cipher_suite);

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

		hash_len =
		    _gnutls_hash_get_algo_len(mac_to_entry(algorithm));
	}

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

	return _gnutls_PRF(session,
			   session->security_parameters.master_secret,
			   GNUTLS_MASTER_SIZE, mesg, siz, concat, hash_len,
			   12, ret);
}

#define ERR(x, y) _gnutls_handshake_log("HSK[%p]: %s (%d)\n", session, x,y)

static int server_select_comp_method(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
recv_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);

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);

#define SERVER_MSG "server finished"

#define CLIENT_MSG "client finished"

#define TLS_MSG_LEN 15

#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)) \
    return gnutls_assert_val(GNUTLS_E_HANDSHAKE_TOO_LARGE)

#define IMED_RET( str, ret, allow_alert) do { \
	if (ret < 0) { \
		/* EAGAIN and INTERRUPTED are always non-fatal */


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;
	gnutls_protocol_t adv_version;
	int neg_version, sret = 0;
	int len = datalen;
	uint8_t *suite_ptr, *comp_ptr, *session_id;

	DECR_LEN(len, 2);

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

	adv_version = _gnutls_version_get(data[pos], data[pos + 1]);

	set_adv_version(session, data[pos], data[pos + 1]);
	pos += 2;

	neg_version = _gnutls_negotiate_version(session, adv_version);
	if (neg_version < 0) {
		gnutls_assert();
		return neg_version;
	}

	_gnutls_handshake_log("HSK[%p]: Selected version %s\n",
		     session, gnutls_protocol_get_name(neg_version));

	/* Read client random value.
	 */
	DECR_LEN(len, GNUTLS_RANDOM_SIZE);
	ret = _gnutls_set_client_random(session, &data[pos]);
	if (ret < 0)
		return gnutls_assert_val(ret);

	pos += GNUTLS_RANDOM_SIZE;

	ret = _gnutls_set_server_random(session, NULL);
	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;
	}

	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! */
		/* Parse only the safe renegotiation extension
		 * We don't want to parse any other extensions since
		 * we don't want new extension values to overwrite the
		 * resumed ones.
		 */

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

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

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

		ret =
		    _gnutls_parse_extensions(session, GNUTLS_EXT_MANDATORY,
					     &data[pos], len);
		if (ret < 0) {
			gnutls_assert();
			return ret;
		}

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

		session->internals.resumed = RESUME_TRUE;

		return _gnutls_user_hello_func(session, adv_version);
	} else {
		_gnutls_generate_session_id(session->security_parameters.
					    session_id,
					    &session->security_parameters.
					    session_id_size);

		session->internals.resumed = RESUME_FALSE;
	}

	/* Remember ciphersuites for later
	 */
	DECR_LEN(len, 2);
	suite_size = _gnutls_read_uint16(&data[pos]);
	pos += 2;

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

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

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

	/* 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_extensions(session, GNUTLS_EXT_APPLICATION,
				       &data[pos], len);
	/* 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, adv_version);
	if (sret < 0 && sret != GNUTLS_E_INT_RET_0) {
		gnutls_assert();
		return sret;
	}

	ret = _gnutls_parse_extensions(session, GNUTLS_EXT_MANDATORY,
				       &data[pos], len);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	ret =
	    _gnutls_parse_extensions(session, GNUTLS_EXT_TLS, &data[pos],
				     len);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	/* resumed by session_ticket extension */
	if (session->internals.resumed != RESUME_FALSE) {
		/* to indicate the client that the current session is resumed */
		memcpy(session->internals.resumed_security_parameters.
		       session_id, session_id, session_id_len);
		session->internals.resumed_security_parameters.
		    session_id_size = session_id_len;

		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 = resume_copy_required_values(session);
		if (ret < 0)
			return gnutls_assert_val(ret);

		return 0;
	}

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

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

	return sret;
}


static void enable_safe_renegotiation(gnutls_priority_t c)
{
	c->sr = SR_SAFE;

}


static void disable_wildcards(gnutls_priority_t c)
{
	c->additional_verify_flags |= GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS;
}


static void enable_latest_record_version(gnutls_priority_t c)
{
	c->min_record_version = 0;
}


gnutls_priority_certificate_type_list(gnutls_priority_t pcache,
				      const unsigned int **list)
{
	if (pcache->cert_type.algorithms == 0)
		return 0;

	*list = pcache->cert_type.priority;
	return pcache->cert_type.algorithms;
}


static void _clear_given_priorities(priority_st * st, const int *list)
{
	unsigned i;

	for (i=0;list[i]!=0;i++) {
		prio_remove(st, list[i]);
	}
}


int _gnutls_set_server_random(gnutls_session_t session, uint8_t * rnd)
{
	int ret;

	if (rnd != NULL)
		memcpy(session->security_parameters.server_random, rnd,
		       GNUTLS_RANDOM_SIZE);
	else {
		/* no random given, we generate. */
		if (session->internals.sc_random_set != 0) {
			memcpy(session->security_parameters.server_random,
			       session->internals.
			       resumed_security_parameters.server_random,
			       GNUTLS_RANDOM_SIZE);
		} else {
			ret =
			    create_tls_random(session->
					      security_parameters.
					      server_random);
			if (ret < 0)
				return gnutls_assert_val(ret);
		}
	}
	return 0;
}


static int handshake_client(gnutls_session_t session)
{
	int ret = 0;

#ifdef HANDSHAKE_DEBUG
	char buf[64];

	if (session->internals.resumed_security_parameters.
	    session_id_size > 0)
		_gnutls_handshake_log("HSK[%p]: Ask to resume: %s\n",
				      session,
				      _gnutls_bin2hex(session->internals.
						      resumed_security_parameters.
						      session_id,
						      session->internals.
						      resumed_security_parameters.
						      session_id_size, buf,
						      sizeof(buf), NULL));
#endif

	switch (STATE) {
	case STATE0:
	case STATE1:
		ret = send_hello(session, AGAIN(STATE1));
		STATE = STATE1;
		IMED_RET("send hello", ret, 1);

	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;
			}
		}
	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);

	case STATE4:
		ret = _gnutls_ext_sr_verify(session);
		STATE = STATE4;
		IMED_RET("recv hello", ret, 0);

	case STATE5:
		if (session->security_parameters.do_recv_supplemental) {
			ret = _gnutls_recv_supplemental(session);
			STATE = STATE5;
			IMED_RET("recv supplemental", ret, 1);
		}

	case STATE6:
		/* RECV CERTIFICATE */
		if (session->internals.resumed == RESUME_FALSE)	/* if we are not resuming */
			ret = _gnutls_recv_server_certificate(session);
		STATE = STATE6;
		IMED_RET("recv server certificate", ret, 1);

	case STATE7:
#ifdef ENABLE_OCSP
		/* RECV CERTIFICATE STATUS */
		if (session->internals.resumed == RESUME_FALSE)	/* if we are not resuming */
			ret =
			    _gnutls_recv_server_certificate_status
			    (session);
		STATE = STATE7;
		IMED_RET("recv server certificate", ret, 1);
#endif
	case STATE8:
		ret = run_verify_callback(session, GNUTLS_CLIENT);
		STATE = STATE8;
		if (ret < 0)
			return gnutls_assert_val(ret);
	case STATE9:
		/* receive the server key exchange */
		if (session->internals.resumed == RESUME_FALSE)	/* if we are not resuming */
			ret = _gnutls_recv_server_kx_message(session);
		STATE = STATE9;
		IMED_RET("recv server kx message", ret, 1);

	case STATE10:
		/* receive the server certificate request - if any
		 */

		if (session->internals.resumed == RESUME_FALSE)	/* if we are not resuming */
			ret = _gnutls_recv_server_crt_request(session);
		STATE = STATE10;
		IMED_RET("recv server certificate request message", ret,
			 1);

	case STATE11:
		/* receive the server hello done */
		if (session->internals.resumed == RESUME_FALSE)	/* 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);

	case STATE12:
		if (session->security_parameters.do_send_supplemental) {
			ret =
			    _gnutls_send_supplemental(session,
						      AGAIN(STATE12));
			STATE = STATE12;
			IMED_RET("send supplemental", ret, 0);
		}

	case STATE13:
		/* send our certificate - if any and if requested
		 */
		if (session->internals.resumed == RESUME_FALSE)	/* if we are not resuming */
			ret =
			    _gnutls_send_client_certificate(session,
							    AGAIN
							    (STATE13));
		STATE = STATE13;
		IMED_RET("send client certificate", ret, 0);

	case STATE14:
		if (session->internals.resumed == RESUME_FALSE)	/* if we are not resuming */
			ret =
			    _gnutls_send_client_kx_message(session,
							   AGAIN(STATE14));
		STATE = STATE14;
		IMED_RET("send client kx", ret, 0);

	case STATE15:
		/* send client certificate verify */
		if (session->internals.resumed == RESUME_FALSE)	/* if we are not resuming */
			ret =
			    _gnutls_send_client_certificate_verify(session,
								   AGAIN
								   (STATE15));
		STATE = STATE15;
		IMED_RET("send client certificate verify", ret, 1);

	case STATE16:
		STATE = STATE16;
		if (session->internals.resumed == RESUME_FALSE) {
			ret = send_handshake_final(session, TRUE);
			IMED_RET("send handshake final 2", ret, 1);
#ifdef ENABLE_SESSION_TICKETS
		} else {
			ret = _gnutls_recv_new_session_ticket(session);
			IMED_RET("recv handshake new session ticket", ret,
				 1);
#endif
		}

	case STATE17:
		STATE = STATE17;
		if (session->internals.resumed == RESUME_FALSE) {
#ifdef ENABLE_SESSION_TICKETS
			ret = _gnutls_recv_new_session_ticket(session);
			IMED_RET("recv handshake new session ticket", ret,
				 1);
#endif
		} else {
			ret = recv_handshake_final(session, TRUE);
			IMED_RET("recv handshake final", ret, 1);
		}

	case STATE18:
		STATE = STATE18;
		if (session->internals.resumed == RESUME_FALSE) {
			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;
	default:
		break;
	}

	return 0;
}


static void enable_ssl3_record_version(gnutls_priority_t c)
{
	c->min_record_version = 1;
}


int _gnutls_handshake_get_session_hash(gnutls_session_t session, gnutls_datum_t *shash)
{
	const version_entry_st *ver = get_version(session);
	int ret;
	const mac_entry_st *me;
	uint8_t concat[2*MAX_HASH_SIZE];
	digest_hd_st td_md5;
	digest_hd_st td_sha;

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

	if (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);
	}

	if (_gnutls_version_has_selectable_prf(ver)) { /* TLS 1.2+ */
		gnutls_mac_algorithm_t prf;

		prf = _gnutls_cipher_suite_get_prf(session->security_parameters.cipher_suite);
		if (prf == GNUTLS_MAC_UNKNOWN)
			return gnutls_assert_val(GNUTLS_E_UNKNOWN_PK_ALGORITHM);

		me = mac_to_entry(prf);

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

		return _gnutls_set_datum(shash, concat, me->output_size);
	} else {
		ret = _gnutls_hash_init(&td_sha, hash_to_entry(GNUTLS_DIG_SHA1));
		if (ret < 0) {
			gnutls_assert();
			return ret;
		}

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

		_gnutls_hash_deinit(&td_sha, &concat[16]);

		ret =
		    _gnutls_hash_init(&td_md5,
				      hash_to_entry(GNUTLS_DIG_MD5));
		if (ret < 0)
			return gnutls_assert_val(ret);

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

		_gnutls_hash_deinit(&td_md5, concat);

		return _gnutls_set_datum(shash, concat, 36);
	}
}


handshake_hash_buffer_empty(gnutls_session_t session)
{

	_gnutls_buffers_log("BUF[HSK]: Emptied buffer\n");

	session->internals.handshake_hash_buffer_prev_len = 0;
	session->internals.handshake_hash_buffer.length = 0;
	return;
}


static ssize_t 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, 1);
		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_handshake_io_cache_int(session,
						   GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC,
						   bufel);
		if (ret < 0) {
			_mbuffer_xfree(&bufel);
			return gnutls_assert_val(ret);
		}

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

	return 0;
}


_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;
}

#define ERR(x, y) _gnutls_handshake_log("HSK[%p]: %s (%d)\n", session, x,y)

static int server_select_comp_method(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
recv_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);

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);

#define SSL3_CLIENT_MSG "CLNT"

#define SSL3_SERVER_MSG "SRVR"

#define SSL_MSG_LEN 4

#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)) \
    return gnutls_assert_val(GNUTLS_E_HANDSHAKE_TOO_LARGE)

#define IMED_RET( str, ret, allow_alert) do { \
	if (ret < 0) { \
		/* EAGAIN and INTERRUPTED are always non-fatal */


_gnutls_send_handshake(gnutls_session_t session, mbuffer_st * bufel,
		       gnutls_handshake_description_t type)
{
	int ret;
	uint8_t *data;
	uint32_t datasize, i_datasize;
	int pos = 0;

	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) 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 (type != GNUTLS_HANDSHAKE_HELLO_REQUEST)
		if ((ret =
		     handshake_hash_add_sent(session, type, data,
						     datasize)) < 0) {
			gnutls_assert();
			_mbuffer_xfree(&bufel);
			return ret;
		}

	ret = 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 = 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;
	}

	switch (type) {
	case GNUTLS_HANDSHAKE_CERTIFICATE_PKT:	/* this one is followed by ServerHelloDone
						 * or ClientKeyExchange always.
						 */
	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 cached messages */
		ret = _gnutls_handshake_io_write_flush(session);
		break;
	}

	return ret;
}


static void enable_profile_low(gnutls_priority_t c)
{
	c->additional_verify_flags &= 0x00ffffff;
	c->additional_verify_flags |= GNUTLS_PROFILE_TO_VFLAGS(GNUTLS_PROFILE_LOW);
	c->level = GNUTLS_SEC_PARAM_LOW;
}


_gnutls_abort_handshake(gnutls_session_t session, int ret)
{
	if (((ret == GNUTLS_E_WARNING_ALERT_RECEIVED) &&
	     (gnutls_alert_get(session) == GNUTLS_A_NO_RENEGOTIATION))
	    || ret == GNUTLS_E_GOT_APPLICATION_DATA)
		return 0;

	/* this doesn't matter */
	return GNUTLS_E_INTERNAL_ERROR;
}


gnutls_handshake_set_timeout(gnutls_session_t session, unsigned int ms)
{
	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;
}


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 = send_change_cipher_spec(session, FAGAIN(STATE1));
		FINAL_STATE = STATE0;

		if (ret < 0) {
			ERR("send ChangeCipherSpec", ret);
			gnutls_assert();
			return ret;
		}
		/* Initialize the connection session (start encryption) - in case of client
		 */
		if (init == TRUE) {
			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) {
			ERR("send Finished", ret);
			gnutls_assert();
			return ret;
		}

		FINAL_STATE = STATE0;
	default:
		break;
	}

	return 0;
}


static int send_hello(gnutls_session_t session, int again)
{
	int ret;

	if (session->security_parameters.entity == GNUTLS_CLIENT) {
		ret = send_client_hello(session, again);

	} else {		/* SERVER */
		ret = send_server_hello(session, again);
	}

	return ret;
}


static int create_tls_random(uint8_t * dst)
{
	uint32_t tim;
	int ret;

	/* Use weak random numbers for the most of the
	 * buffer except for the first 4 that are the
	 * system's time.
	 */

	tim = gnutls_time(NULL);
	/* generate server random value */
	_gnutls_write_uint32(tim, dst);

	ret =
	    _gnutls_rnd(GNUTLS_RND_NONCE, &dst[3], GNUTLS_RANDOM_SIZE - 3);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	return 0;
}

static int server_select_comp_method(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
recv_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);

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);

#define SERVER_MSG "server finished"

#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)) \
    return gnutls_assert_val(GNUTLS_E_HANDSHAKE_TOO_LARGE)

#define IMED_RET( str, ret, allow_alert) do { \
	if (ret < 0) { \
		/* EAGAIN and INTERRUPTED are always non-fatal */


static void enable_stateless_compression(gnutls_priority_t c)
{
	c->stateless_compression = 1;
}


_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 = 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.htype,
				       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:
	case GNUTLS_HANDSHAKE_SERVER_HELLO:
		if (hsk.htype == GNUTLS_HANDSHAKE_CLIENT_HELLO_V2)
			ret =
			    _gnutls_read_client_hello_v2(session,
							 hsk.data.data,
							 hsk.data.length);
		else
			ret =
			    recv_hello(session, hsk.data.data,
					hsk.data.length);

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

		break;
	case GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST:
		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_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_CERTIFICATE_STATUS:
	case GNUTLS_HANDSHAKE_FINISHED:
	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:
		ret = hsk.data.length;
		break;
	default:
		gnutls_assert();
		/* we shouldn't actually arrive here in any case .
		 * unexpected messages should be catched after _gnutls_handshake_io_recv_int()
		 */
		ret = GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET;
		goto cleanup;
	}

	ret2 = 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;
}


copy_ciphersuites(gnutls_session_t session,
		  gnutls_buffer_st * cdata, int add_scsv)
{
	int ret;
	uint8_t cipher_suites[MAX_CIPHERSUITE_SIZE + 4]; /* allow space for SCSV */
	int cipher_suites_size;
	size_t init_length = cdata->length;

	ret =
	    _gnutls_supported_ciphersuites(session, cipher_suites,
					   sizeof(cipher_suites) - 2);
	if (ret < 0)
		return gnutls_assert_val(ret);

	/* Here we remove any ciphersuite that does not conform
	 * the certificate requested, or to the
	 * authentication requested (eg SRP).
	 */
	ret =
	    _gnutls_remove_unwanted_ciphersuites(session, cipher_suites,
						 ret, NULL, 0);
	if (ret < 0)
		return gnutls_assert_val(ret);

	/* If no cipher suites were enabled.
	 */
	if (ret == 0)
		return
		    gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);

	cipher_suites_size = ret;
	if (add_scsv) {
		cipher_suites[cipher_suites_size] = 0x00;
		cipher_suites[cipher_suites_size + 1] = 0xff;
		cipher_suites_size += 2;

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

	if (session->internals.priorities.fallback) {
		cipher_suites[cipher_suites_size] =
			GNUTLS_FALLBACK_SCSV_MAJOR;
		cipher_suites[cipher_suites_size + 1] =
			GNUTLS_FALLBACK_SCSV_MINOR;
		cipher_suites_size += 2;
	}

	ret =
	    _gnutls_buffer_append_data_prefix(cdata, 16, cipher_suites,
					      cipher_suites_size);
	if (ret < 0)
		return gnutls_assert_val(ret);

	ret = cdata->length - init_length;

	return ret;
}


static int resume_copy_required_values(gnutls_session_t session)
{
	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.
	 */
	memcpy(session->security_parameters.cipher_suite,
	       session->internals.resumed_security_parameters.cipher_suite,
	       2);
	session->security_parameters.compression_method =
	    session->internals.resumed_security_parameters.
	    compression_method;

	ret = _gnutls_epoch_set_cipher_suite(session, EPOCH_NEXT,
					     session->internals.
					     resumed_security_parameters.
					     cipher_suite);
	if (ret < 0)
		return gnutls_assert_val(ret);

	ret = _gnutls_epoch_set_compression(session, EPOCH_NEXT,
					    session->internals.
					    resumed_security_parameters.
					    compression_method);
	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.cert_type =
	    session->internals.resumed_security_parameters.cert_type;

	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;
}


    static int 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))
			return session->internals.h_hook(session, type,
							 post, incoming, &msg);
	}
	return 0;
}


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];

	_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);

		memcpy(session->security_parameters.cipher_suite,
			session->internals.resumed_security_parameters.cipher_suite, 2);
                session->security_parameters.compression_method =
		        session->internals.resumed_security_parameters.compression_method;

		_gnutls_epoch_set_cipher_suite
		    (session, EPOCH_NEXT,
		     session->internals.resumed_security_parameters.
		     cipher_suite);
		_gnutls_epoch_set_compression(session, EPOCH_NEXT,
					      session->internals.
					      resumed_security_parameters.
					      compression_method);

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

		return 0;
	} else {
		/* keep the new session id */
		session->internals.resumed = RESUME_FALSE;	/* we are not resuming */
		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);
		}

		return -1;
	}
}


static void enable_profile_ultra(gnutls_priority_t c)
{
	c->additional_verify_flags &= 0x00ffffff;
	c->additional_verify_flags |= GNUTLS_PROFILE_TO_VFLAGS(GNUTLS_PROFILE_ULTRA);
	c->level = GNUTLS_SEC_PARAM_ULTRA;
}


static void enable_dumbfw(gnutls_priority_t c)
{
	c->dumbfw = 1;
}


gnutls_handshake_get_last_in(gnutls_session_t session)
{
	return session->internals.last_handshake_in;
}


void _gnutls_priority_update_fips(void)
{
	cipher_priority_performance = cipher_priority_performance_fips;
	cipher_priority_normal = cipher_priority_normal_fips;
	mac_priority_normal = mac_priority_normal_fips;
}

static void prio_remove(priority_st * priority_list, unsigned int algo);

static void prio_add(priority_st * priority_list, unsigned int algo);

static void
break_list(char *etag,
		 char *broken_etag[MAX_ELEMENTS], int *size);

typedef void (bulk_rmadd_func) (priority_st * priority_list, const int *);

static const int cipher_priority_performance_fips[] = {
	GNUTLS_CIPHER_AES_128_GCM,
	GNUTLS_CIPHER_AES_256_GCM,
	GNUTLS_CIPHER_AES_128_CBC,
	GNUTLS_CIPHER_AES_256_CBC,
	GNUTLS_CIPHER_AES_128_CCM,
	GNUTLS_CIPHER_AES_256_CCM,
	GNUTLS_CIPHER_3DES_CBC,
	0
};

static const int cipher_priority_normal_fips[] = {
	GNUTLS_CIPHER_AES_128_GCM,
	GNUTLS_CIPHER_AES_256_GCM,
	GNUTLS_CIPHER_AES_128_CBC,
	GNUTLS_CIPHER_AES_256_CBC,
	GNUTLS_CIPHER_AES_128_CCM,
	GNUTLS_CIPHER_AES_256_CCM,
	GNUTLS_CIPHER_3DES_CBC,
	0
};

static const int mac_priority_normal_fips[] = {
	GNUTLS_MAC_SHA1,
	GNUTLS_MAC_SHA256,
	GNUTLS_MAC_SHA384,
	GNUTLS_MAC_AEAD,
	0
};

static const int *cipher_priority_performance = _cipher_priority_performance_default;

static const int *cipher_priority_normal = _cipher_priority_normal_default;

static const int *mac_priority_normal = mac_priority_normal_default;

typedef void (rmadd_func) (priority_st * priority_list, unsigned int alg);


set_client_comp_method(gnutls_session_t session,
			       uint8_t comp_method)
{
	int comp_methods_num;
	uint8_t compression_methods[MAX_ALGOS];
	int id = _gnutls_compression_get_id(comp_method);
	int i;

	_gnutls_handshake_log
	    ("HSK[%p]: Selected compression method: %s (%d)\n", session,
	     gnutls_compression_get_name(id), (int) comp_method);

	comp_methods_num = _gnutls_supported_compression_methods(session,
								 compression_methods,
								 MAX_ALGOS);
	if (comp_methods_num < 0) {
		gnutls_assert();
		return comp_methods_num;
	}

	for (i = 0; i < comp_methods_num; i++) {
		if (compression_methods[i] == comp_method) {
			comp_methods_num = 0;
			break;
		}
	}

	if (comp_methods_num != 0) {
		gnutls_assert();
		return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM;
	}

	session->security_parameters.compression_method = id;
	_gnutls_epoch_set_compression(session, EPOCH_NEXT, id);

	return 0;
}


static void enable_no_extensions(gnutls_priority_t c)
{
	c->no_extensions = 1;
}


gnutls_priority_kx_list(gnutls_priority_t pcache,
			const unsigned int **list)
{
	if (pcache->kx.algorithms == 0)
		return 0;

	*list = pcache->kx.priority;
	return pcache->kx.algorithms;
}


static void enable_profile_suiteb128(gnutls_priority_t c)
{
	c->additional_verify_flags &= 0x00ffffff;
	c->additional_verify_flags |= GNUTLS_PROFILE_TO_VFLAGS(GNUTLS_PROFILE_SUITEB128);
	c->level = GNUTLS_SEC_PARAM_HIGH;
}


static int run_verify_callback(gnutls_session_t session, unsigned int side)
{
	gnutls_certificate_credentials_t cred;
	int ret, type;

	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;

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

	return 0;
}


static void enable_verify_allow_rsa_md5(gnutls_priority_t c)
{
	c->additional_verify_flags |=
	    GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5;
}


void _gnutls_handshake_hash_buffers_clear(gnutls_session_t session)
{
	session->internals.handshake_hash_buffer_prev_len = 0;
	session->internals.handshake_hash_buffer_client_kx_len = 0;
	_gnutls_buffer_clear(&session->internals.handshake_hash_buffer);
}


static void enable_profile_suiteb192(gnutls_priority_t c)
{
	c->additional_verify_flags &= 0x00ffffff;
	c->additional_verify_flags |= GNUTLS_PROFILE_TO_VFLAGS(GNUTLS_PROFILE_SUITEB192);
	c->level = GNUTLS_SEC_PARAM_ULTRA;
}


static int send_server_hello(gnutls_session_t session, int again)
{
	mbuffer_st *bufel = NULL;
	uint8_t *data = NULL;
	gnutls_buffer_st extdata;
	int pos = 0;
	int datalen, ret = 0;
	uint8_t comp;
	uint8_t session_id_len =
	    session->security_parameters.session_id_size;
	char buf[2 * GNUTLS_MAX_SESSION_ID_SIZE + 1];
	const version_entry_st *vers;

	_gnutls_buffer_init(&extdata);

	if (again == 0) {
		datalen = 2 + session_id_len + 1 + GNUTLS_RANDOM_SIZE + 3;
		ret =
		    _gnutls_gen_extensions(session, &extdata,
					   (session->internals.resumed ==
					    RESUME_TRUE) ?
					   GNUTLS_EXT_MANDATORY :
					   GNUTLS_EXT_ANY);
		if (ret < 0) {
			gnutls_assert();
			goto fail;
		}

		bufel =
		    _gnutls_handshake_alloc(session,
					    datalen + extdata.length);
		if (bufel == NULL) {
			gnutls_assert();
			ret = GNUTLS_E_MEMORY_ERROR;
			goto fail;
		}
		data = _mbuffer_get_udata_ptr(bufel);

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

		data[pos++] = vers->major;
		data[pos++] = vers->minor;

		memcpy(&data[pos],
		       session->security_parameters.server_random,
		       GNUTLS_RANDOM_SIZE);
		pos += GNUTLS_RANDOM_SIZE;

		data[pos++] = session_id_len;
		if (session_id_len > 0) {
			memcpy(&data[pos],
			       session->security_parameters.session_id,
			       session_id_len);
		}
		pos += session_id_len;

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

		memcpy(&data[pos],
		       session->security_parameters.cipher_suite, 2);
		pos += 2;

		comp =
		    _gnutls_compression_get_num(session->
						security_parameters.
						compression_method);
		data[pos++] = comp;

		if (extdata.length > 0) {
			memcpy(&data[pos], extdata.data, extdata.length);
		}
	}

	ret =
	    _gnutls_send_handshake(session, bufel,
				   GNUTLS_HANDSHAKE_SERVER_HELLO);

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


gnutls_priority_mac_list(gnutls_priority_t pcache,
			 const unsigned int **list)
{
	if (pcache->mac.algorithms == 0)
		return 0;

	*list = pcache->mac.priority;
	return pcache->mac.algorithms;
}


static void enable_no_ext_master_secret(gnutls_priority_t c)
{
	c->no_ext_master_secret = 1;
}


void gnutls_priority_deinit(gnutls_priority_t priority_cache)
{
	gnutls_free(priority_cache);
}

static void prio_remove(priority_st * priority_list, unsigned int algo);

static void prio_add(priority_st * priority_list, unsigned int algo);

static void
break_list(char *etag,
		 char *broken_etag[MAX_ELEMENTS], int *size);

typedef void (bulk_rmadd_func) (priority_st * priority_list, const int *);

typedef void (rmadd_func) (priority_st * priority_list, unsigned int alg);

#define SET_PROFILE(to_set) \
	profile = GNUTLS_VFLAGS_TO_PROFILE(priority_cache->additional_verify_flags); \
	if (profile == 0 || profile > to_set) { \
		priority_cache->additional_verify_flags &= ~GNUTLS_VFLAGS_PROFILE_MASK; \
		priority_cache->additional_verify_flags |= GNUTLS_PROFILE_TO_VFLAGS(to_set); \
	}

#define SET_LEVEL(to_set) \
		if (priority_cache->level == 0 || (unsigned)priority_cache->level > (unsigned)to_set) \
			priority_cache->level = to_set


static void enable_partial_safe_renegotiation(gnutls_priority_t c)
{
	c->sr = SR_PARTIAL;
}


gnutls_priority_protocol_list(gnutls_priority_t pcache,
			      const unsigned int **list)
{
	if (pcache->protocol.algorithms == 0)
		return 0;

	*list = pcache->protocol.priority;
	return pcache->protocol.algorithms;
}


gnutls_priority_string_list(unsigned iter, unsigned int flags)
{
	if (flags & GNUTLS_PRIORITY_LIST_INIT_KEYWORDS) {
		if (iter >= (sizeof(pgroups)/sizeof(pgroups[0]))-1)
			return NULL;
		return pgroups[iter].name;
	} else if (flags & GNUTLS_PRIORITY_LIST_SPECIAL) {
		if (iter >= (sizeof(wordlist)/sizeof(wordlist[0]))-1)
			return NULL;
		return wordlist[iter].name;
	}
	return NULL;
}


gnutls_priority_set_direct(gnutls_session_t session,
			   const char *priorities, const char **err_pos)
{
	gnutls_priority_t prio;
	int ret;

	ret = gnutls_priority_init(&prio, priorities, err_pos);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	ret = gnutls_priority_set(session, prio);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	gnutls_priority_deinit(prio);

	return 0;
}


int gnutls_handshake(gnutls_session_t session)
{
	int ret;
	record_parameters_st *params;

	/* sanity check. Verify that there are priorities setup.
	 */

	if (STATE == STATE0) {
		/* first call */
		if (session->internals.priorities.protocol.algorithms == 0)
			return gnutls_assert_val(GNUTLS_E_NO_PRIORITIES_WERE_SET);

		session->internals.handshake_in_progress = 1;
		gettime(&session->internals.handshake_start_time);
		if (session->internals.handshake_timeout_ms &&
		    session->internals.handshake_endtime == 0)
			    session->internals.handshake_endtime = session->internals.handshake_start_time.tv_sec +
			    	session->internals.handshake_timeout_ms / 1000;
	}

	ret =
	    _gnutls_epoch_get(session,
			      session->security_parameters.epoch_next,
			      &params);
	if (ret < 0) {
		/* We assume the epoch is not allocated if _gnutls_epoch_get fails. */
		ret =
		    _gnutls_epoch_alloc(session,
					session->security_parameters.
					epoch_next, NULL);
		if (ret < 0)
			return gnutls_assert_val(ret);
	}

	if (session->security_parameters.entity == GNUTLS_CLIENT) {
		do {
			ret = handshake_client(session);
		} while (ret == 1);
	} else {
		ret = handshake_server(session);
	}
	if (ret < 0) {
		/* In the case of a rehandshake abort
		 * we should reset the handshake's internal state.
		 */
		if (_gnutls_abort_handshake(session, ret) == 0)
			STATE = STATE0;

		return ret;
	}

	/* clear handshake buffer */
	_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);

	session->security_parameters.epoch_next++;

	return 0;
}


static void dummy_func(gnutls_priority_t c)
{
}


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

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

	if (again)
		ret =
		    _gnutls_send_handshake(session, NULL,
					   GNUTLS_HANDSHAKE_SUPPLEMENTAL);
	else {
		gnutls_buffer_st buf;
		_gnutls_buffer_init(&buf);

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

		bufel =
		    _gnutls_handshake_alloc(session,
					    buf.length);
		if (bufel == NULL) {
			gnutls_assert();
			return GNUTLS_E_MEMORY_ERROR;
		}

		_mbuffer_set_udata(bufel, buf.data, buf.length);
		_gnutls_buffer_clear(&buf);

		ret = _gnutls_send_handshake(session, bufel,
					     GNUTLS_HANDSHAKE_SUPPLEMENTAL);
	}

	return ret;
}


static void disable_safe_renegotiation(gnutls_priority_t c)
{
	c->sr = SR_DISABLED;
}


static void enable_profile_medium(gnutls_priority_t c)
{
	c->additional_verify_flags &= 0x00ffffff;
	c->additional_verify_flags |= GNUTLS_PROFILE_TO_VFLAGS(GNUTLS_PROFILE_MEDIUM);
	c->level = GNUTLS_SEC_PARAM_MEDIUM;
}


_gnutls_negotiate_version(gnutls_session_t session,
			  gnutls_protocol_t adv_version)
{
	int ret;

	/* if we do not support that version  */
	if (_gnutls_version_is_supported(session, adv_version) == 0) {
		/* If he requested something we do not support
		 * then we send him the highest we support.
		 */
		ret = _gnutls_version_max(session);
		if (ret == GNUTLS_VERSION_UNKNOWN) {
			/* this check is not really needed.
			 */
			gnutls_assert();
			return GNUTLS_E_UNKNOWN_CIPHER_SUITE;
		}
	} else {
		ret = adv_version;
	}

	if (_gnutls_set_current_version(session, ret) < 0)
		return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);

	return ret;
}


int gnutls_rehandshake(gnutls_session_t session)
{
	int ret;

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

	_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;
}

#define ERR(x, y) _gnutls_handshake_log("HSK[%p]: %s (%d)\n", session, x,y)

static int server_select_comp_method(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
recv_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);

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);

#define SERVER_MSG "server finished"

#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)) \
    return gnutls_assert_val(GNUTLS_E_HANDSHAKE_TOO_LARGE)

#define IMED_RET( str, ret, allow_alert) do { \
	if (ret < 0) { \
		/* EAGAIN and INTERRUPTED are always non-fatal */


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;
}


static void enable_fallback_scsv(gnutls_priority_t c)
{
	c->fallback = 1;
}


inline static void _add_priority(priority_st * st, const int *list)
{
	int num, i, j, init;

	init = i = st->algorithms;

	for (num = 0; list[num] != 0; ++num) {
		if (i + 1 > MAX_ALGOS) {
			return;
		}

		for (j = 0; j < init; j++) {
			if (st->priority[j] == (unsigned) list[num]) {
				break;
			}
		}

		if (j == init) {
			st->priority[i++] = list[num];
			st->algorithms++;
		}
	}

	return;
}


static void enable_no_tickets(gnutls_priority_t c)
{
	c->no_tickets = 1;
}