/*
* Copyright (C) 2017-2018 Red Hat, Inc.
*
* Author: Nikos Mavrogiannopoulos
*
* This file is part of GnuTLS.
*
* The GnuTLS is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see
*
*/
/* Functions that relate to the TLS handshake procedure.
*/
#include "gnutls_int.h"
#include "errors.h"
#include "dh.h"
#include "debug.h"
#include "algorithms.h"
#include "cipher.h"
#include "buffers.h"
#include "mbuffers.h"
#include "kx.h"
#include "handshake.h"
#include "num.h"
#include "hash_int.h"
#include "db.h"
#include "hello_ext.h"
#include "supplemental.h"
#include "auth.h"
#include "sslv2_compat.h"
#include
#include "constate.h"
#include
#include
#include
#include
#include "secrets.h"
#include "tls13/hello_retry.h"
#include "tls13/encrypted_extensions.h"
#include "tls13/certificate_request.h"
#include "tls13/certificate_verify.h"
#include "tls13/certificate.h"
#include "tls13/finished.h"
#include "tls13/key_update.h"
#include "ext/pre_shared_key.h"
static int generate_hs_traffic_keys(gnutls_session_t session);
static int generate_ap_traffic_keys(gnutls_session_t session);
#define SAVE_TRANSCRIPT \
if (session->internals.flags & GNUTLS_POST_HANDSHAKE_AUTH) { \
/* If post-handshake auth is in use we need a copy of the original \
* handshake transcript */ \
memcpy( &session->internals.post_handshake_hash_buffer, \
&session->internals.handshake_hash_buffer, \
sizeof(session->internals.handshake_hash_buffer)); \
_gnutls_buffer_init(&session->internals.handshake_hash_buffer); \
}
/*
* _gnutls13_handshake_client
* This function performs the client side of the handshake of the TLS/SSL protocol.
*/
int _gnutls13_handshake_client(gnutls_session_t session)
{
int ret = 0;
switch (STATE) {
case STATE99:
case STATE100:
#ifdef TLS13_APPENDIX_D4
/* We send it before keys are generated. That works because CCS
* is always being cached and queued and not being sent directly */
ret = _gnutls_send_change_cipher_spec(session, AGAIN(STATE100));
STATE = STATE100;
IMED_RET("send change cipher spec", ret, 0);
#endif
/* fall through */
case STATE101:
ret =
generate_hs_traffic_keys(session);
STATE = STATE101;
IMED_RET("generate session keys", ret, 0);
/* fall through */
case STATE102:
ret = _gnutls13_recv_encrypted_extensions(session);
STATE = STATE102;
IMED_RET("recv encrypted extensions", ret, 0);
/* fall through */
case STATE103:
ret = _gnutls13_recv_certificate_request(session);
STATE = STATE103;
IMED_RET("recv certificate request", ret, 0);
/* fall through */
case STATE104:
ret = _gnutls13_recv_certificate(session);
STATE = STATE104;
IMED_RET("recv certificate", ret, 0);
/* fall through */
case STATE105:
ret = _gnutls13_recv_certificate_verify(session);
STATE = STATE105;
IMED_RET("recv server certificate verify", ret, 0);
/* fall through */
case STATE106:
ret = _gnutls_run_verify_callback(session, GNUTLS_CLIENT);
STATE = STATE106;
if (ret < 0)
return gnutls_assert_val(ret);
FALLTHROUGH;
case STATE107:
ret = _gnutls13_recv_finished(session);
STATE = STATE107;
IMED_RET("recv finished", ret, 0);
/* fall through */
case STATE108:
ret = _gnutls13_send_certificate(session, AGAIN(STATE108));
STATE = STATE108;
IMED_RET("send certificate", ret, 0);
/* fall through */
case STATE109:
ret = _gnutls13_send_certificate_verify(session, AGAIN(STATE109));
STATE = STATE109;
IMED_RET("send certificate verify", ret, 0);
/* fall through */
case STATE110:
ret = _gnutls13_send_finished(session, AGAIN(STATE110));
STATE = STATE110;
IMED_RET("send finished", ret, 0);
/* fall through */
case STATE111:
ret =
generate_ap_traffic_keys(session);
STATE = STATE111;
IMED_RET("generate app keys", ret, 0);
STATE = STATE0;
break;
default:
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
}
/* explicitly reset any false start flags */
session->internals.recv_state = RECV_STATE_0;
session->internals.initial_negotiation_completed = 1;
SAVE_TRANSCRIPT;
if (session->internals.resumed != RESUME_FALSE)
_gnutls_set_resumed_parameters(session);
return 0;
}
static int generate_ap_traffic_keys(gnutls_session_t session)
{
int ret;
uint8_t zero[MAX_HASH_SIZE];
ret = _tls13_derive_secret(session, DERIVED_LABEL, sizeof(DERIVED_LABEL)-1,
NULL, 0, session->key.proto.tls13.temp_secret,
session->key.proto.tls13.temp_secret);
if (ret < 0)
return gnutls_assert_val(ret);
memset(zero, 0, session->security_parameters.prf->output_size);
ret = _tls13_update_secret(session, zero, session->security_parameters.prf->output_size);
if (ret < 0)
return gnutls_assert_val(ret);
ret = _tls13_derive_secret(session, EXPORTER_MASTER_LABEL, sizeof(EXPORTER_MASTER_LABEL)-1,
session->internals.handshake_hash_buffer.data,
session->internals.handshake_hash_buffer_server_finished_len,
session->key.proto.tls13.temp_secret,
session->key.proto.tls13.ap_expkey);
if (ret < 0)
return gnutls_assert_val(ret);
_gnutls_nss_keylog_write(session, "EXPORTER_SECRET",
session->key.proto.tls13.ap_expkey,
session->security_parameters.prf->output_size);
ret = _tls13_derive_secret(session, RMS_MASTER_LABEL, sizeof(RMS_MASTER_LABEL)-1,
session->internals.handshake_hash_buffer.data,
session->internals.handshake_hash_buffer_client_finished_len,
session->key.proto.tls13.temp_secret,
session->key.proto.tls13.ap_rms);
if (ret < 0)
return gnutls_assert_val(ret);
_gnutls_epoch_bump(session);
ret = _gnutls_epoch_dup(session);
if (ret < 0)
return gnutls_assert_val(ret);
ret = _tls13_connection_state_init(session, STAGE_APP);
if (ret < 0)
return gnutls_assert_val(ret);
return 0;
}
static int generate_hs_traffic_keys(gnutls_session_t session)
{
int ret;
unsigned null_key = 0;
if (unlikely(session->key.proto.tls13.temp_secret_size == 0))
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
if ((session->security_parameters.entity == GNUTLS_CLIENT &&
(!(session->internals.hsk_flags & HSK_KEY_SHARE_RECEIVED) ||
(!(session->internals.hsk_flags & HSK_PSK_KE_MODE_DHE_PSK) &&
session->internals.resumed != RESUME_FALSE))) ||
(session->security_parameters.entity == GNUTLS_SERVER &&
!(session->internals.hsk_flags & HSK_KEY_SHARE_SENT))) {
if ((session->internals.hsk_flags & HSK_PSK_SELECTED) &&
(session->internals.hsk_flags & HSK_PSK_KE_MODE_PSK)) {
null_key = 1;
}
}
if (null_key) {
uint8_t digest[MAX_HASH_SIZE];
unsigned digest_size;
if (unlikely(session->security_parameters.prf == NULL))
return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
digest_size = session->security_parameters.prf->output_size;
memset(digest, 0, digest_size);
ret = _tls13_update_secret(session, digest, digest_size);
if (ret < 0) {
gnutls_assert();
return ret;
}
} else {
if (unlikely(session->key.key.size == 0))
return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
ret = _tls13_update_secret(session, session->key.key.data, session->key.key.size);
if (ret < 0) {
gnutls_assert();
return ret;
}
}
ret = _tls13_connection_state_init(session, STAGE_HS);
if (ret < 0) {
gnutls_assert();
return ret;
}
return 0;
}
/*
* _gnutls13_handshake_server
* This function does the server stuff of the handshake protocol.
*/
int _gnutls13_handshake_server(gnutls_session_t session)
{
int ret = 0;
switch (STATE) {
case STATE90:
ret = _gnutls13_handshake_hash_buffers_synth(session, session->security_parameters.prf, 0);
STATE = STATE90;
IMED_RET("reset handshake buffers", ret, 0);
/* fall through */
case STATE91:
ret = _gnutls13_send_hello_retry_request(session, AGAIN(STATE91));
STATE = STATE91;
IMED_RET("send hello retry request", ret, 0);
/* fall through */
case STATE92:
#ifdef TLS13_APPENDIX_D4
ret = _gnutls_send_change_cipher_spec(session, AGAIN(STATE92));
STATE = STATE92;
IMED_RET("send change cipher spec", ret, 0);
#endif
/* fall through */
case STATE93:
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 = STATE94; /* hello already parsed -> move to next state */
} else {
STATE = STATE93;
}
IMED_RET("recv client hello", ret, 0);
/* fall through */
case STATE94:
ret = _gnutls_send_server_hello(session, AGAIN(STATE94));
STATE = STATE94;
IMED_RET("send hello", ret, 0);
/* fall through */
case STATE99:
case STATE100:
#ifdef TLS13_APPENDIX_D4
/* don't send CCS twice: when HRR has already been
* sent, CCS should have followed it (see above) */
if (!(session->internals.hsk_flags & HSK_HRR_SENT)) {
ret = _gnutls_send_change_cipher_spec(session, AGAIN(STATE100));
STATE = STATE100;
IMED_RET("send change cipher spec", ret, 0);
}
#endif
/* fall through */
case STATE101:
ret =
generate_hs_traffic_keys(session);
STATE = STATE101;
IMED_RET("generate session keys", ret, 0);
/* fall through */
case STATE102:
ret = _gnutls13_send_encrypted_extensions(session, AGAIN(STATE102));
STATE = STATE102;
IMED_RET("send encrypted extensions", ret, 0);
/* fall through */
case STATE103:
ret = _gnutls13_send_certificate_request(session, AGAIN(STATE103));
STATE = STATE103;
IMED_RET("send certificate request", ret, 0);
/* fall through */
case STATE104:
ret = _gnutls13_send_certificate(session, AGAIN(STATE104));
STATE = STATE104;
IMED_RET("send certificate", ret, 0);
/* fall through */
case STATE105:
ret = _gnutls13_send_certificate_verify(session, AGAIN(STATE105));
STATE = STATE105;
IMED_RET("send certificate verify", ret, 0);
/* fall through */
case STATE106:
ret = _gnutls13_send_finished(session, AGAIN(STATE106));
STATE = STATE106;
IMED_RET("send finished", ret, 0);
/* fall through */
case STATE107:
ret = _gnutls13_recv_certificate(session);
STATE = STATE107;
IMED_RET("recv certificate", ret, 0);
/* fall through */
case STATE108:
ret = _gnutls13_recv_certificate_verify(session);
STATE = STATE108;
IMED_RET("recv certificate verify", ret, 0);
/* fall through */
case STATE109:
ret = _gnutls_run_verify_callback(session, GNUTLS_CLIENT);
STATE = STATE109;
if (ret < 0)
return gnutls_assert_val(ret);
/* fall through */
case STATE110:
ret = _gnutls13_recv_finished(session);
STATE = STATE110;
IMED_RET("recv finished", ret, 0);
/* fall through */
case STATE111:
ret =
generate_ap_traffic_keys(session);
STATE = STATE111;
IMED_RET("generate app keys", ret, 0);
if (session->internals.resumed != RESUME_FALSE)
_gnutls_set_resumed_parameters(session);
/* fall through */
case STATE112:
ret = _gnutls13_send_session_ticket(session, AGAIN(STATE112));
STATE = STATE112;
IMED_RET("send session ticket", ret, 0);
STATE = STATE0;
break;
default:
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
}
/* explicitly reset any false start flags */
session->internals.recv_state = RECV_STATE_0;
session->internals.initial_negotiation_completed = 1;
SAVE_TRANSCRIPT;
return 0;
}
/* Processes handshake messages received asynchronously after initial handshake.
*
* It is called once per message, with a read-only buffer in @buf,
* and should return success, or a fatal error code.
*/
int
_gnutls13_recv_async_handshake(gnutls_session_t session, gnutls_buffer_st *buf)
{
uint8_t type;
int ret;
size_t handshake_header_size = HANDSHAKE_HEADER_SIZE(session);
size_t length;
if (buf->length < handshake_header_size) {
gnutls_assert();
return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
}
/* The following messages are expected asynchronously after
* the handshake process is complete */
if (unlikely(session->internals.handshake_in_progress))
return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
ret = _gnutls_buffer_pop_prefix8(buf, &type, 0);
if (ret < 0)
return gnutls_assert_val(ret);
ret = _gnutls_buffer_pop_prefix24(buf, &length, 1);
if (ret < 0)
return gnutls_assert_val(ret);
ret = _gnutls_call_hook_func(session, type, GNUTLS_HOOK_PRE, 1, buf->data, buf->length);
if (ret < 0)
return gnutls_assert_val(ret);
switch(type) {
case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST:
if (!(session->security_parameters.entity == GNUTLS_CLIENT) ||
!(session->internals.flags & GNUTLS_POST_HANDSHAKE_AUTH)) {
return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
}
_gnutls_buffer_reset(&session->internals.reauth_buffer);
/* include the handshake headers in reauth buffer */
ret = _gnutls_buffer_append_data(&session->internals.reauth_buffer,
buf->data-4, buf->length+4);
if (ret < 0)
return gnutls_assert_val(ret);
/* Application is expected to handle re-authentication
* explicitly. */
return GNUTLS_E_REAUTH_REQUEST;
case GNUTLS_HANDSHAKE_KEY_UPDATE:
ret = _gnutls13_recv_key_update(session, buf);
if (ret < 0)
return gnutls_assert_val(ret);
break;
case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET:
if (session->security_parameters.entity != GNUTLS_CLIENT)
return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
ret = _gnutls13_recv_session_ticket(session, buf);
if (ret < 0)
return gnutls_assert_val(ret);
memcpy(session->internals.tls13_ticket.resumption_master_secret,
session->key.proto.tls13.ap_rms,
session->key.proto.tls13.temp_secret_size);
session->internals.tls13_ticket.prf = session->security_parameters.prf;
session->internals.hsk_flags |= HSK_TICKET_RECEIVED;
break;
default:
gnutls_assert();
return GNUTLS_E_UNEXPECTED_PACKET;
}
ret = _gnutls_call_hook_func(session, type, GNUTLS_HOOK_POST, 1, buf->data, buf->length);
if (ret < 0)
return gnutls_assert_val(ret);
return 0;
}
/**
* gnutls_session_ticket_send:
* @session: is a #gnutls_session_t type.
* @flags: must be zero
*
* Sends a fresh session ticket to the peer. This is relevant only
* in server side under TLS1.3. This function may also return %GNUTLS_E_AGAIN
* or %GNUTLS_E_INTERRUPTED.
*
* Returns: %GNUTLS_E_SUCCESS on success, or a negative error code.
**/
int gnutls_session_ticket_send(gnutls_session_t session, unsigned flags)
{
int ret = 0;
const version_entry_st *vers = get_version(session);
if (!vers->tls13_sem || session->security_parameters.entity == GNUTLS_CLIENT)
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
switch (TICKET_STATE) {
case TICKET_STATE0:
ret = _gnutls_io_write_flush(session);
TICKET_STATE = TICKET_STATE0;
if (ret < 0) {
gnutls_assert();
return ret;
}
/* fall through */
case TICKET_STATE1:
ret =
_gnutls13_send_session_ticket(session, TICKET_STATE==TICKET_STATE1?1:0);
TICKET_STATE = TICKET_STATE1;
if (ret < 0) {
gnutls_assert();
return ret;
}
break;
default:
gnutls_assert();
return GNUTLS_E_INTERNAL_ERROR;
}
TICKET_STATE = TICKET_STATE0;
return 0;
}