2010-01-25 09:44:35 +00:00
|
|
|
/*
|
2020-08-25 14:14:54 +00:00
|
|
|
* Copyright (C) 2020 Tobias Brunner
|
|
|
|
* Copyright (C) 2020 Pascal Knecht
|
|
|
|
* Copyright (C) 2020 Méline Sieber
|
|
|
|
* HSR Hochschule fuer Technik Rapperswil
|
|
|
|
*
|
2010-01-25 09:44:35 +00:00
|
|
|
* Copyright (C) 2010 Martin Willi
|
|
|
|
* Copyright (C) 2010 revosec AG
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
|
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
|
|
|
*
|
|
|
|
* This program 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 General Public License
|
|
|
|
* for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "tls_peer.h"
|
|
|
|
|
2012-10-16 14:03:21 +00:00
|
|
|
#include <utils/debug.h>
|
2010-08-31 16:07:38 +00:00
|
|
|
#include <credentials/certificates/x509.h>
|
2010-01-25 09:44:35 +00:00
|
|
|
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
typedef struct private_tls_peer_t private_tls_peer_t;
|
|
|
|
|
2010-01-25 11:23:59 +00:00
|
|
|
typedef enum {
|
|
|
|
STATE_INIT,
|
|
|
|
STATE_HELLO_SENT,
|
2010-02-09 11:37:29 +00:00
|
|
|
STATE_HELLO_RECEIVED,
|
2010-01-25 16:31:55 +00:00
|
|
|
STATE_HELLO_DONE,
|
2010-02-03 18:53:40 +00:00
|
|
|
STATE_CERT_SENT,
|
2010-02-09 11:37:29 +00:00
|
|
|
STATE_CERT_RECEIVED,
|
2010-09-02 17:26:19 +00:00
|
|
|
STATE_KEY_EXCHANGE_RECEIVED,
|
2010-02-09 11:37:29 +00:00
|
|
|
STATE_CERTREQ_RECEIVED,
|
2010-02-03 18:53:40 +00:00
|
|
|
STATE_KEY_EXCHANGE_SENT,
|
|
|
|
STATE_VERIFY_SENT,
|
2010-02-05 10:50:29 +00:00
|
|
|
STATE_CIPHERSPEC_CHANGED_OUT,
|
2010-02-03 18:53:40 +00:00
|
|
|
STATE_FINISHED_SENT,
|
2010-02-05 10:50:29 +00:00
|
|
|
STATE_CIPHERSPEC_CHANGED_IN,
|
2011-12-31 00:41:56 +00:00
|
|
|
STATE_FINISHED_RECEIVED,
|
2020-05-21 07:37:38 +00:00
|
|
|
/* new states in TLS 1.3 */
|
|
|
|
STATE_HELLORETRYREQ_RECEIVED,
|
|
|
|
STATE_ENCRYPTED_EXTENSIONS_RECEIVED,
|
|
|
|
STATE_CERT_VERIFY_RECEIVED,
|
|
|
|
STATE_FINISHED_SENT_KEY_SWITCHED,
|
|
|
|
|
2010-01-25 11:23:59 +00:00
|
|
|
} peer_state_t;
|
|
|
|
|
2010-01-25 09:44:35 +00:00
|
|
|
/**
|
|
|
|
* Private data of an tls_peer_t object.
|
|
|
|
*/
|
|
|
|
struct private_tls_peer_t {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Public tls_peer_t interface.
|
|
|
|
*/
|
|
|
|
tls_peer_t public;
|
2010-01-25 11:21:57 +00:00
|
|
|
|
2010-01-25 16:31:55 +00:00
|
|
|
/**
|
|
|
|
* TLS stack
|
|
|
|
*/
|
|
|
|
tls_t *tls;
|
|
|
|
|
2010-01-25 11:21:57 +00:00
|
|
|
/**
|
|
|
|
* TLS crypto context
|
|
|
|
*/
|
|
|
|
tls_crypto_t *crypto;
|
2010-01-25 11:23:59 +00:00
|
|
|
|
2010-08-23 12:22:38 +00:00
|
|
|
/**
|
|
|
|
* TLS alert handler
|
|
|
|
*/
|
|
|
|
tls_alert_t *alert;
|
|
|
|
|
2010-02-03 18:53:40 +00:00
|
|
|
/**
|
2010-08-24 09:34:43 +00:00
|
|
|
* Peer identity, NULL for no client authentication
|
2010-02-03 18:53:40 +00:00
|
|
|
*/
|
|
|
|
identification_t *peer;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Server identity
|
|
|
|
*/
|
|
|
|
identification_t *server;
|
|
|
|
|
2010-01-25 11:23:59 +00:00
|
|
|
/**
|
|
|
|
* State we are in
|
|
|
|
*/
|
|
|
|
peer_state_t state;
|
2010-02-03 18:53:40 +00:00
|
|
|
|
2014-03-25 09:50:51 +00:00
|
|
|
/**
|
|
|
|
* TLS version we offered in hello
|
|
|
|
*/
|
|
|
|
tls_version_t hello_version;
|
|
|
|
|
2010-02-04 10:17:48 +00:00
|
|
|
/**
|
|
|
|
* Hello random data selected by client
|
|
|
|
*/
|
|
|
|
char client_random[32];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Hello random data selected by server
|
|
|
|
*/
|
|
|
|
char server_random[32];
|
|
|
|
|
2010-02-03 18:53:40 +00:00
|
|
|
/**
|
|
|
|
* Auth helper for peer authentication
|
|
|
|
*/
|
|
|
|
auth_cfg_t *peer_auth;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Auth helper for server authentication
|
|
|
|
*/
|
|
|
|
auth_cfg_t *server_auth;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Peer private key
|
|
|
|
*/
|
|
|
|
private_key_t *private;
|
2010-09-02 08:29:32 +00:00
|
|
|
|
2010-09-02 17:26:19 +00:00
|
|
|
/**
|
|
|
|
* DHE exchange
|
|
|
|
*/
|
|
|
|
diffie_hellman_t *dh;
|
|
|
|
|
2020-08-25 15:23:45 +00:00
|
|
|
/**
|
|
|
|
* Requested DH group
|
|
|
|
*/
|
|
|
|
tls_named_group_t requested_curve;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Cookie extension received in HelloRetryRequest
|
|
|
|
*/
|
|
|
|
chunk_t cookie;
|
|
|
|
|
2011-12-31 00:41:56 +00:00
|
|
|
/**
|
|
|
|
* Resuming a session?
|
|
|
|
*/
|
|
|
|
bool resume;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* TLS session identifier
|
|
|
|
*/
|
|
|
|
chunk_t session;
|
|
|
|
|
2010-09-02 08:29:32 +00:00
|
|
|
/**
|
|
|
|
* List of server-supported hashsig algorithms
|
|
|
|
*/
|
|
|
|
chunk_t hashsig;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* List of server-supported client certificate types
|
|
|
|
*/
|
|
|
|
chunk_t cert_types;
|
2010-01-25 09:44:35 +00:00
|
|
|
};
|
|
|
|
|
2020-08-25 15:23:45 +00:00
|
|
|
/**
|
|
|
|
* Verify the DH group/key type requested by the server is valid.
|
|
|
|
*/
|
|
|
|
static bool verify_requested_key_type(private_tls_peer_t *this,
|
|
|
|
uint16_t key_type)
|
|
|
|
{
|
|
|
|
enumerator_t *enumerator;
|
|
|
|
diffie_hellman_group_t group, found = MODP_NONE;
|
|
|
|
tls_named_group_t curve;
|
|
|
|
|
|
|
|
enumerator = this->crypto->create_ec_enumerator(this->crypto);
|
|
|
|
while (enumerator->enumerate(enumerator, &group, &curve))
|
|
|
|
{
|
|
|
|
if (key_type == curve)
|
|
|
|
{
|
|
|
|
found = group;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
|
|
|
|
if (found == MODP_NONE)
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "server requested key exchange we didn't propose");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if (this->dh->get_dh_group(this->dh) == found)
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "server requested key exchange we already use");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2010-01-25 16:31:55 +00:00
|
|
|
/**
|
|
|
|
* Process a server hello message
|
|
|
|
*/
|
2010-02-01 10:25:44 +00:00
|
|
|
static status_t process_server_hello(private_tls_peer_t *this,
|
2011-05-31 13:46:30 +00:00
|
|
|
bio_reader_t *reader)
|
2010-01-25 16:31:55 +00:00
|
|
|
{
|
2016-03-22 12:22:01 +00:00
|
|
|
uint8_t compression;
|
2020-08-25 15:23:45 +00:00
|
|
|
uint16_t version, cipher, key_type = 0;
|
2020-08-25 14:14:54 +00:00
|
|
|
bio_reader_t *extensions, *extension;
|
2020-08-25 15:23:45 +00:00
|
|
|
chunk_t msg, random, session, ext = chunk_empty, key_share = chunk_empty;
|
|
|
|
chunk_t cookie = chunk_empty;
|
2011-12-31 00:41:56 +00:00
|
|
|
tls_cipher_suite_t suite = 0;
|
2020-08-25 15:23:45 +00:00
|
|
|
bool is_retry_request;
|
2010-02-01 10:25:44 +00:00
|
|
|
|
2020-08-25 15:23:45 +00:00
|
|
|
msg = reader->peek(reader);
|
2010-02-01 10:25:44 +00:00
|
|
|
if (!reader->read_uint16(reader, &version) ||
|
2010-02-04 10:17:48 +00:00
|
|
|
!reader->read_data(reader, sizeof(this->server_random), &random) ||
|
2010-02-01 10:25:44 +00:00
|
|
|
!reader->read_data8(reader, &session) ||
|
|
|
|
!reader->read_uint16(reader, &cipher) ||
|
|
|
|
!reader->read_uint8(reader, &compression) ||
|
|
|
|
(reader->remaining(reader) && !reader->read_data16(reader, &ext)))
|
2010-01-25 16:31:55 +00:00
|
|
|
{
|
2010-08-20 13:57:47 +00:00
|
|
|
DBG1(DBG_TLS, "received invalid ServerHello");
|
2010-08-23 12:22:38 +00:00
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
|
|
|
|
return NEED_MORE;
|
2010-02-01 10:25:44 +00:00
|
|
|
}
|
2010-02-04 10:17:48 +00:00
|
|
|
|
2020-08-25 15:23:45 +00:00
|
|
|
is_retry_request = chunk_equals_const(random, tls_hello_retry_request_magic);
|
|
|
|
|
2010-02-04 10:17:48 +00:00
|
|
|
memcpy(this->server_random, random.ptr, sizeof(this->server_random));
|
|
|
|
|
2020-08-25 14:14:54 +00:00
|
|
|
extensions = bio_reader_create(ext);
|
|
|
|
while (extensions->remaining(extensions))
|
2020-05-21 07:37:38 +00:00
|
|
|
{
|
2020-08-25 14:14:54 +00:00
|
|
|
uint16_t extension_type;
|
|
|
|
chunk_t extension_data;
|
2020-05-21 07:37:38 +00:00
|
|
|
|
2020-08-25 14:14:54 +00:00
|
|
|
if (!extensions->read_uint16(extensions, &extension_type) ||
|
|
|
|
!extensions->read_data16(extensions, &extension_data))
|
2020-05-21 07:37:38 +00:00
|
|
|
{
|
2020-08-25 14:14:54 +00:00
|
|
|
DBG1(DBG_TLS, "invalid extension in ServerHello");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
|
|
|
|
extensions->destroy(extensions);
|
|
|
|
return NEED_MORE;
|
2020-05-21 07:37:38 +00:00
|
|
|
}
|
2020-08-25 14:14:54 +00:00
|
|
|
extension = bio_reader_create(extension_data);
|
2020-05-21 07:37:38 +00:00
|
|
|
switch (extension_type)
|
|
|
|
{
|
|
|
|
case TLS_EXT_SUPPORTED_VERSIONS:
|
2020-08-25 14:14:54 +00:00
|
|
|
if (!extension->read_uint16(extension, &version))
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "invalid %N extension", tls_extension_names,
|
|
|
|
extension_type);
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
|
|
|
|
extensions->destroy(extensions);
|
|
|
|
extension->destroy(extension);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
2020-05-21 07:37:38 +00:00
|
|
|
break;
|
|
|
|
case TLS_EXT_KEY_SHARE:
|
2020-08-25 14:14:54 +00:00
|
|
|
if (!extension->read_uint16(extension, &key_type) ||
|
2020-08-25 15:23:45 +00:00
|
|
|
(!is_retry_request &&
|
2020-08-26 14:08:26 +00:00
|
|
|
!(extension->read_data16(extension, &key_share) &&
|
|
|
|
key_share.len)))
|
2020-05-21 07:37:38 +00:00
|
|
|
{
|
2020-08-25 14:14:54 +00:00
|
|
|
DBG1(DBG_TLS, "invalid %N extension", tls_extension_names,
|
|
|
|
extension_type);
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
|
|
|
|
extensions->destroy(extensions);
|
|
|
|
extension->destroy(extension);
|
|
|
|
return NEED_MORE;
|
2020-05-21 07:37:38 +00:00
|
|
|
}
|
|
|
|
break;
|
2020-08-25 15:23:45 +00:00
|
|
|
case TLS_EXT_COOKIE:
|
|
|
|
if (!extension->read_data16(extension, &cookie))
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "invalid %N extension", tls_extension_names,
|
|
|
|
extension_type);
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
|
|
|
|
extensions->destroy(extensions);
|
|
|
|
extension->destroy(extension);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
2020-05-21 07:37:38 +00:00
|
|
|
default:
|
2020-08-25 14:14:54 +00:00
|
|
|
break;
|
2020-05-21 07:37:38 +00:00
|
|
|
}
|
2020-08-25 14:14:54 +00:00
|
|
|
extension->destroy(extension);
|
2020-05-21 07:37:38 +00:00
|
|
|
}
|
2020-08-25 14:14:54 +00:00
|
|
|
extensions->destroy(extensions);
|
2020-05-21 07:37:38 +00:00
|
|
|
|
2020-08-26 12:37:59 +00:00
|
|
|
if (!this->tls->set_version(this->tls, version, version))
|
2010-02-01 10:25:44 +00:00
|
|
|
{
|
2010-08-20 14:08:59 +00:00
|
|
|
DBG1(DBG_TLS, "negotiated version %N not supported",
|
|
|
|
tls_version_names, version);
|
2010-08-23 12:22:38 +00:00
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_PROTOCOL_VERSION);
|
|
|
|
return NEED_MORE;
|
2010-01-25 16:31:55 +00:00
|
|
|
}
|
2011-12-31 00:41:56 +00:00
|
|
|
|
2020-05-21 07:37:38 +00:00
|
|
|
if (this->tls->get_version_max(this->tls) < TLS_1_3)
|
2010-02-04 10:17:48 +00:00
|
|
|
{
|
2020-05-21 07:37:38 +00:00
|
|
|
if (chunk_equals(this->session, session))
|
2011-12-31 00:41:56 +00:00
|
|
|
{
|
2020-05-21 07:37:38 +00:00
|
|
|
suite = this->crypto->resume_session(this->crypto, session,
|
|
|
|
this->server, chunk_from_thing
|
|
|
|
(this->client_random),
|
|
|
|
chunk_from_thing
|
|
|
|
(this->server_random));
|
|
|
|
if (suite)
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "resumed %N using suite %N",
|
|
|
|
tls_version_names, version, tls_cipher_suite_names, suite);
|
|
|
|
this->resume = TRUE;
|
|
|
|
}
|
2011-12-31 00:41:56 +00:00
|
|
|
}
|
2020-05-21 07:37:38 +00:00
|
|
|
DESTROY_IF(this->dh);
|
|
|
|
this->dh = NULL;
|
2011-12-31 00:41:56 +00:00
|
|
|
}
|
2020-08-25 14:14:54 +00:00
|
|
|
|
2011-12-31 00:41:56 +00:00
|
|
|
if (!suite)
|
|
|
|
{
|
|
|
|
suite = cipher;
|
|
|
|
if (!this->crypto->select_cipher_suite(this->crypto, &suite, 1, KEY_ANY))
|
|
|
|
{
|
2018-05-29 16:27:16 +00:00
|
|
|
DBG1(DBG_TLS, "received TLS cipher suite %N unacceptable",
|
2011-12-31 00:41:56 +00:00
|
|
|
tls_cipher_suite_names, suite);
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
DBG1(DBG_TLS, "negotiated %N using suite %N",
|
|
|
|
tls_version_names, version, tls_cipher_suite_names, suite);
|
|
|
|
free(this->session.ptr);
|
|
|
|
this->session = chunk_clone(session);
|
2010-02-04 10:17:48 +00:00
|
|
|
}
|
2020-05-21 07:37:38 +00:00
|
|
|
|
2020-08-25 15:23:45 +00:00
|
|
|
if (is_retry_request)
|
|
|
|
{
|
|
|
|
if (!this->crypto->hash_handshake(this->crypto, NULL))
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "failed to hash handshake messages");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this->crypto->append_handshake(this->crypto, TLS_SERVER_HELLO, msg);
|
|
|
|
|
|
|
|
if (is_retry_request)
|
|
|
|
{
|
|
|
|
if (key_type)
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "server requests key exchange with %N",
|
|
|
|
tls_named_group_names, key_type);
|
|
|
|
}
|
|
|
|
else if (cookie.len)
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "server requests retry with cookie");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "invalid retry request received");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_ILLEGAL_PARAMETER);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
if (this->requested_curve || this->cookie.len)
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "already replied to previous retry request");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
if (key_type && !verify_requested_key_type(this, key_type))
|
|
|
|
{
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_ILLEGAL_PARAMETER);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
|
|
|
|
DESTROY_IF(this->dh);
|
|
|
|
this->dh = NULL;
|
|
|
|
this->requested_curve = key_type;
|
|
|
|
this->cookie = chunk_clone(cookie);
|
|
|
|
this->state = STATE_INIT;
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
|
2020-08-26 14:08:26 +00:00
|
|
|
if (this->tls->get_version_max(this->tls) >= TLS_1_3)
|
2020-05-21 07:37:38 +00:00
|
|
|
{
|
2020-08-25 11:22:04 +00:00
|
|
|
chunk_t shared_secret = chunk_empty;
|
2020-05-21 07:37:38 +00:00
|
|
|
|
2020-08-26 14:08:26 +00:00
|
|
|
if (key_share.len &&
|
|
|
|
key_type != TLS_CURVE25519 &&
|
|
|
|
key_type != TLS_CURVE448)
|
|
|
|
{ /* classic format (see RFC 8446, section 4.2.8.2) */
|
|
|
|
if (key_share.ptr[0] != TLS_ANSI_UNCOMPRESSED)
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "DH point format '%N' not supported",
|
|
|
|
tls_ansi_point_format_names, key_share.ptr[0]);
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
key_share = chunk_skip(key_share, 1);
|
|
|
|
}
|
|
|
|
if (!key_share.len ||
|
|
|
|
!this->dh->set_other_public_value(this->dh, key_share) ||
|
2020-08-25 11:22:04 +00:00
|
|
|
!this->dh->get_shared_secret(this->dh, &shared_secret) ||
|
|
|
|
!this->crypto->derive_handshake_keys(this->crypto, shared_secret))
|
2020-05-21 07:37:38 +00:00
|
|
|
{
|
2020-08-25 11:22:04 +00:00
|
|
|
DBG1(DBG_TLS, "DH key derivation failed");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE);
|
|
|
|
chunk_clear(&shared_secret);
|
|
|
|
return NEED_MORE;
|
2020-05-21 07:37:38 +00:00
|
|
|
}
|
2020-08-25 11:22:04 +00:00
|
|
|
chunk_clear(&shared_secret);
|
2020-05-21 07:37:38 +00:00
|
|
|
|
2020-08-25 11:22:04 +00:00
|
|
|
this->crypto->change_cipher(this->crypto, TRUE);
|
|
|
|
this->crypto->change_cipher(this->crypto, FALSE);
|
2020-05-21 07:37:38 +00:00
|
|
|
}
|
|
|
|
|
2010-02-09 11:37:29 +00:00
|
|
|
this->state = STATE_HELLO_RECEIVED;
|
2010-02-01 10:25:44 +00:00
|
|
|
return NEED_MORE;
|
2010-01-25 16:31:55 +00:00
|
|
|
}
|
|
|
|
|
2020-05-21 07:37:38 +00:00
|
|
|
/**
|
2020-08-25 14:14:54 +00:00
|
|
|
* Process a server encrypted extensions message
|
|
|
|
*/
|
2020-05-21 07:37:38 +00:00
|
|
|
static status_t process_encrypted_extensions(private_tls_peer_t *this,
|
|
|
|
bio_reader_t *reader)
|
|
|
|
{
|
|
|
|
chunk_t ext = chunk_empty;
|
2020-08-25 14:14:54 +00:00
|
|
|
uint16_t extension_type;
|
2020-05-21 07:37:38 +00:00
|
|
|
|
2020-08-25 14:14:54 +00:00
|
|
|
this->crypto->append_handshake(this->crypto, TLS_ENCRYPTED_EXTENSIONS,
|
|
|
|
reader->peek(reader));
|
2020-05-21 07:37:38 +00:00
|
|
|
|
2020-08-28 06:54:49 +00:00
|
|
|
if (!reader->read_data16(reader, &ext))
|
2020-05-21 07:37:38 +00:00
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "received invalid EncryptedExtensions");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
2020-08-25 14:14:54 +00:00
|
|
|
if (ext.len)
|
2020-05-21 07:37:38 +00:00
|
|
|
{
|
2020-08-25 14:14:54 +00:00
|
|
|
bio_reader_t *extensions = bio_reader_create(ext);
|
2020-05-21 07:37:38 +00:00
|
|
|
|
2020-08-25 14:14:54 +00:00
|
|
|
while (extensions->remaining(extensions))
|
2020-05-21 07:37:38 +00:00
|
|
|
{
|
2020-08-25 14:14:54 +00:00
|
|
|
chunk_t extension_data = chunk_empty;
|
|
|
|
|
|
|
|
if (!extensions->read_uint16(extensions, &extension_type) ||
|
|
|
|
!extensions->read_data16(extensions, &extension_data))
|
2020-05-21 07:37:38 +00:00
|
|
|
{
|
2020-08-25 14:14:54 +00:00
|
|
|
DBG1(DBG_TLS, "invalid extension in EncryptedExtensions");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
|
|
|
|
extensions->destroy(extensions);
|
|
|
|
return NEED_MORE;
|
2020-05-21 07:37:38 +00:00
|
|
|
}
|
|
|
|
switch (extension_type)
|
|
|
|
{
|
|
|
|
case TLS_EXT_SERVER_NAME:
|
|
|
|
case TLS_EXT_MAX_FRAGMENT_LENGTH:
|
|
|
|
case TLS_EXT_SUPPORTED_GROUPS:
|
|
|
|
case TLS_EXT_USE_SRTP:
|
|
|
|
case TLS_EXT_HEARTBEAT:
|
|
|
|
case TLS_EXT_APPLICATION_LAYER_PROTOCOL_NEGOTIATION:
|
|
|
|
case TLS_SERVER_CERTIFICATE_TYPE:
|
2020-08-25 14:14:54 +00:00
|
|
|
/* not supported so far */
|
|
|
|
DBG2(DBG_TLS, "ignoring unsupported %N EncryptedExtension",
|
|
|
|
tls_extension_names, extension_type);
|
2020-05-21 07:37:38 +00:00
|
|
|
break;
|
|
|
|
default:
|
2020-08-25 14:14:54 +00:00
|
|
|
DBG1(DBG_TLS, "received forbidden EncryptedExtension (%d)",
|
|
|
|
extension_type);
|
2020-05-21 07:37:38 +00:00
|
|
|
this->alert->add(this->alert, TLS_FATAL,
|
|
|
|
TLS_ILLEGAL_PARAMETER);
|
2020-08-25 14:14:54 +00:00
|
|
|
extensions->destroy(extensions);
|
2020-05-21 07:37:38 +00:00
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
}
|
2020-08-25 14:14:54 +00:00
|
|
|
extensions->destroy(extensions);
|
2020-05-21 07:37:38 +00:00
|
|
|
}
|
2020-08-25 14:14:54 +00:00
|
|
|
this->state = STATE_ENCRYPTED_EXTENSIONS_RECEIVED;
|
2020-05-21 07:37:38 +00:00
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
|
2010-08-31 16:07:38 +00:00
|
|
|
/**
|
|
|
|
* Check if a server certificate is acceptable for the given server identity
|
|
|
|
*/
|
|
|
|
static bool check_certificate(private_tls_peer_t *this, certificate_t *cert)
|
|
|
|
{
|
|
|
|
identification_t *id;
|
|
|
|
|
|
|
|
if (cert->has_subject(cert, this->server))
|
|
|
|
{
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
id = cert->get_subject(cert);
|
|
|
|
if (id->matches(id, this->server))
|
|
|
|
{
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
if (cert->get_type(cert) == CERT_X509)
|
|
|
|
{
|
|
|
|
x509_t *x509 = (x509_t*)cert;
|
|
|
|
enumerator_t *enumerator;
|
|
|
|
|
|
|
|
enumerator = x509->create_subjectAltName_enumerator(x509);
|
|
|
|
while (enumerator->enumerate(enumerator, &id))
|
|
|
|
{
|
|
|
|
if (id->matches(id, this->server))
|
|
|
|
{
|
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
}
|
|
|
|
DBG1(DBG_TLS, "server certificate does not match to '%Y'", this->server);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2010-01-25 16:31:55 +00:00
|
|
|
/**
|
|
|
|
* Process a Certificate message
|
|
|
|
*/
|
2010-02-01 10:25:44 +00:00
|
|
|
static status_t process_certificate(private_tls_peer_t *this,
|
2011-05-31 13:46:30 +00:00
|
|
|
bio_reader_t *reader)
|
2010-01-25 16:31:55 +00:00
|
|
|
{
|
2010-02-01 10:25:44 +00:00
|
|
|
certificate_t *cert;
|
2011-05-31 13:46:30 +00:00
|
|
|
bio_reader_t *certs;
|
2010-02-01 10:25:44 +00:00
|
|
|
chunk_t data;
|
2010-02-03 18:53:40 +00:00
|
|
|
bool first = TRUE;
|
|
|
|
|
2010-02-05 13:25:38 +00:00
|
|
|
this->crypto->append_handshake(this->crypto,
|
|
|
|
TLS_CERTIFICATE, reader->peek(reader));
|
2010-01-25 16:31:55 +00:00
|
|
|
|
2020-05-21 07:37:38 +00:00
|
|
|
if (this->tls->get_version_max(this->tls) > TLS_1_2)
|
|
|
|
{
|
|
|
|
if (!reader->read_data8(reader, &data))
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "certificate request context invalid");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
}
|
2010-02-01 10:25:44 +00:00
|
|
|
if (!reader->read_data24(reader, &data))
|
|
|
|
{
|
2010-08-23 12:22:38 +00:00
|
|
|
DBG1(DBG_TLS, "certificate message header invalid");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
|
|
|
|
return NEED_MORE;
|
2010-02-01 10:25:44 +00:00
|
|
|
}
|
2011-05-31 13:46:30 +00:00
|
|
|
certs = bio_reader_create(data);
|
2010-02-01 10:25:44 +00:00
|
|
|
while (certs->remaining(certs))
|
|
|
|
{
|
|
|
|
if (!certs->read_data24(certs, &data))
|
2010-01-25 16:31:55 +00:00
|
|
|
{
|
2010-08-23 12:22:38 +00:00
|
|
|
DBG1(DBG_TLS, "certificate message invalid");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
|
2010-02-01 10:25:44 +00:00
|
|
|
certs->destroy(certs);
|
2010-08-23 12:22:38 +00:00
|
|
|
return NEED_MORE;
|
2010-01-25 16:31:55 +00:00
|
|
|
}
|
2010-02-01 10:25:44 +00:00
|
|
|
cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
|
2010-02-03 18:53:40 +00:00
|
|
|
BUILD_BLOB_ASN1_DER, data, BUILD_END);
|
2010-02-01 10:25:44 +00:00
|
|
|
if (cert)
|
2010-01-25 16:31:55 +00:00
|
|
|
{
|
2010-02-03 18:53:40 +00:00
|
|
|
if (first)
|
|
|
|
{
|
2010-08-31 16:07:38 +00:00
|
|
|
if (!check_certificate(this, cert))
|
|
|
|
{
|
|
|
|
cert->destroy(cert);
|
|
|
|
certs->destroy(certs);
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_ACCESS_DENIED);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
2010-02-03 18:53:40 +00:00
|
|
|
this->server_auth->add(this->server_auth,
|
2010-02-09 17:19:25 +00:00
|
|
|
AUTH_HELPER_SUBJECT_CERT, cert);
|
2010-08-20 13:57:47 +00:00
|
|
|
DBG1(DBG_TLS, "received TLS server certificate '%Y'",
|
2010-02-03 18:53:40 +00:00
|
|
|
cert->get_subject(cert));
|
|
|
|
first = FALSE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-08-20 13:57:47 +00:00
|
|
|
DBG1(DBG_TLS, "received TLS intermediate certificate '%Y'",
|
2010-02-03 18:53:40 +00:00
|
|
|
cert->get_subject(cert));
|
|
|
|
this->server_auth->add(this->server_auth,
|
2010-02-09 17:19:25 +00:00
|
|
|
AUTH_HELPER_IM_CERT, cert);
|
2010-02-03 18:53:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-08-20 13:57:47 +00:00
|
|
|
DBG1(DBG_TLS, "parsing TLS certificate failed, skipped");
|
2010-08-23 12:22:38 +00:00
|
|
|
this->alert->add(this->alert, TLS_WARNING, TLS_BAD_CERTIFICATE);
|
2010-01-25 16:31:55 +00:00
|
|
|
}
|
2020-05-21 07:37:38 +00:00
|
|
|
if (this->tls->get_version_max(this->tls) > TLS_1_2)
|
|
|
|
{
|
|
|
|
if (!certs->read_data16(certs, &data))
|
|
|
|
{
|
2020-08-25 14:11:59 +00:00
|
|
|
DBG1(DBG_TLS, "failed to read extensions of CertificateEntry");
|
2020-05-21 07:37:38 +00:00
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
}
|
2010-01-25 16:31:55 +00:00
|
|
|
}
|
2010-02-01 10:25:44 +00:00
|
|
|
certs->destroy(certs);
|
2010-02-09 11:37:29 +00:00
|
|
|
this->state = STATE_CERT_RECEIVED;
|
2010-01-25 16:31:55 +00:00
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
|
2010-09-02 17:26:19 +00:00
|
|
|
/**
|
|
|
|
* Find a trusted public key to encrypt/verify key exchange data
|
|
|
|
*/
|
|
|
|
static public_key_t *find_public_key(private_tls_peer_t *this)
|
|
|
|
{
|
|
|
|
public_key_t *public = NULL, *current;
|
2015-02-20 10:29:02 +00:00
|
|
|
certificate_t *cert, *found;
|
2010-09-02 17:26:19 +00:00
|
|
|
enumerator_t *enumerator;
|
|
|
|
auth_cfg_t *auth;
|
|
|
|
|
|
|
|
cert = this->server_auth->get(this->server_auth, AUTH_HELPER_SUBJECT_CERT);
|
|
|
|
if (cert)
|
|
|
|
{
|
|
|
|
enumerator = lib->credmgr->create_public_enumerator(lib->credmgr,
|
2015-10-26 14:35:23 +00:00
|
|
|
KEY_ANY, cert->get_subject(cert),
|
|
|
|
this->server_auth, TRUE);
|
2010-09-02 17:26:19 +00:00
|
|
|
while (enumerator->enumerate(enumerator, ¤t, &auth))
|
|
|
|
{
|
2015-02-20 10:29:02 +00:00
|
|
|
found = auth->get(auth, AUTH_RULE_SUBJECT_CERT);
|
|
|
|
if (found && cert->equals(cert, found))
|
|
|
|
{
|
|
|
|
public = current->get_ref(current);
|
|
|
|
this->server_auth->merge(this->server_auth, auth, FALSE);
|
|
|
|
break;
|
|
|
|
}
|
2010-09-02 17:26:19 +00:00
|
|
|
}
|
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
}
|
|
|
|
return public;
|
|
|
|
}
|
|
|
|
|
2020-08-25 14:17:27 +00:00
|
|
|
/**
|
|
|
|
* Process CertificateVerify message
|
|
|
|
*/
|
|
|
|
static status_t process_cert_verify(private_tls_peer_t *this,
|
|
|
|
bio_reader_t *reader)
|
|
|
|
{
|
|
|
|
public_key_t *public;
|
|
|
|
chunk_t msg;
|
|
|
|
|
|
|
|
public = find_public_key(this);
|
|
|
|
if (!public)
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "no TLS public key found for server '%Y'", this->server);
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_CERTIFICATE_UNKNOWN);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
|
|
|
|
msg = reader->peek(reader);
|
|
|
|
if (!this->crypto->verify_handshake(this->crypto, public, reader))
|
|
|
|
{
|
|
|
|
public->destroy(public);
|
|
|
|
DBG1(DBG_TLS, "signature verification failed");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_BAD_CERTIFICATE);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
public->destroy(public);
|
|
|
|
|
|
|
|
this->crypto->append_handshake(this->crypto, TLS_CERTIFICATE_VERIFY, msg);
|
|
|
|
this->state = STATE_CERT_VERIFY_RECEIVED;
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
|
2010-09-02 17:26:19 +00:00
|
|
|
/**
|
|
|
|
* Process a Key Exchange message using MODP Diffie Hellman
|
|
|
|
*/
|
|
|
|
static status_t process_modp_key_exchange(private_tls_peer_t *this,
|
2011-05-31 13:46:30 +00:00
|
|
|
bio_reader_t *reader)
|
2010-09-02 17:26:19 +00:00
|
|
|
{
|
|
|
|
chunk_t prime, generator, pub, chunk;
|
|
|
|
public_key_t *public;
|
|
|
|
|
|
|
|
chunk = reader->peek(reader);
|
|
|
|
if (!reader->read_data16(reader, &prime) ||
|
|
|
|
!reader->read_data16(reader, &generator) ||
|
|
|
|
!reader->read_data16(reader, &pub))
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "received invalid Server Key Exchange");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
2015-05-20 08:56:23 +00:00
|
|
|
/* reject (export) DH groups using primes smaller than 1024 bit */
|
|
|
|
if (prime.len < 1024 / 8)
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "short DH prime received (%zu bytes)", prime.len);
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
2010-09-02 17:26:19 +00:00
|
|
|
public = find_public_key(this);
|
|
|
|
if (!public)
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "no TLS public key found for server '%Y'", this->server);
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_CERTIFICATE_UNKNOWN);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
|
|
|
|
chunk.len = 2 + prime.len + 2 + generator.len + 2 + pub.len;
|
|
|
|
chunk = chunk_cat("ccc", chunk_from_thing(this->client_random),
|
|
|
|
chunk_from_thing(this->server_random), chunk);
|
|
|
|
if (!this->crypto->verify(this->crypto, public, reader, chunk))
|
|
|
|
{
|
|
|
|
public->destroy(public);
|
|
|
|
free(chunk.ptr);
|
|
|
|
DBG1(DBG_TLS, "verifying DH parameters failed");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_BAD_CERTIFICATE);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
public->destroy(public);
|
|
|
|
free(chunk.ptr);
|
|
|
|
|
|
|
|
this->dh = lib->crypto->create_dh(lib->crypto, MODP_CUSTOM,
|
|
|
|
generator, prime);
|
|
|
|
if (!this->dh)
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "custom DH parameters not supported");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
2015-03-23 12:09:32 +00:00
|
|
|
if (!this->dh->set_other_public_value(this->dh, pub))
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "applying DH public value failed");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
2010-09-02 17:26:19 +00:00
|
|
|
|
|
|
|
this->state = STATE_KEY_EXCHANGE_RECEIVED;
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
|
2010-09-03 15:05:39 +00:00
|
|
|
/**
|
|
|
|
* Get the EC group for a TLS named curve
|
|
|
|
*/
|
|
|
|
static diffie_hellman_group_t curve_to_ec_group(private_tls_peer_t *this,
|
2020-05-21 07:37:38 +00:00
|
|
|
tls_named_group_t curve)
|
2010-09-03 15:05:39 +00:00
|
|
|
{
|
|
|
|
diffie_hellman_group_t group;
|
2020-05-21 07:37:38 +00:00
|
|
|
tls_named_group_t current;
|
2010-09-03 15:05:39 +00:00
|
|
|
enumerator_t *enumerator;
|
|
|
|
|
|
|
|
enumerator = this->crypto->create_ec_enumerator(this->crypto);
|
|
|
|
while (enumerator->enumerate(enumerator, &group, ¤t))
|
|
|
|
{
|
|
|
|
if (current == curve)
|
|
|
|
{
|
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
return group;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
return 0;
|
|
|
|
}
|
2010-09-03 09:00:07 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Process a Key Exchange message using EC Diffie Hellman
|
|
|
|
*/
|
|
|
|
static status_t process_ec_key_exchange(private_tls_peer_t *this,
|
2011-05-31 13:46:30 +00:00
|
|
|
bio_reader_t *reader)
|
2010-09-03 09:00:07 +00:00
|
|
|
{
|
|
|
|
diffie_hellman_group_t group;
|
|
|
|
public_key_t *public;
|
2016-03-22 12:22:01 +00:00
|
|
|
uint8_t type;
|
|
|
|
uint16_t curve;
|
2010-09-03 09:00:07 +00:00
|
|
|
chunk_t pub, chunk;
|
|
|
|
|
|
|
|
chunk = reader->peek(reader);
|
|
|
|
if (!reader->read_uint8(reader, &type))
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "received invalid Server Key Exchange");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
if (type != TLS_ECC_NAMED_CURVE)
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "ECDH curve type %N not supported",
|
|
|
|
tls_ecc_curve_type_names, type);
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
if (!reader->read_uint16(reader, &curve) ||
|
2010-09-06 13:31:32 +00:00
|
|
|
!reader->read_data8(reader, &pub) || pub.len == 0)
|
2010-09-03 09:00:07 +00:00
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "received invalid Server Key Exchange");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
2010-09-03 15:05:39 +00:00
|
|
|
|
|
|
|
group = curve_to_ec_group(this, curve);
|
|
|
|
if (!group)
|
2010-09-03 09:00:07 +00:00
|
|
|
{
|
2010-09-03 15:05:39 +00:00
|
|
|
DBG1(DBG_TLS, "ECDH curve %N not supported",
|
2020-05-21 07:37:38 +00:00
|
|
|
tls_named_group_names, curve);
|
2010-09-03 15:05:39 +00:00
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE);
|
|
|
|
return NEED_MORE;
|
2010-09-03 09:00:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public = find_public_key(this);
|
|
|
|
if (!public)
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "no TLS public key found for server '%Y'", this->server);
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_CERTIFICATE_UNKNOWN);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
|
|
|
|
chunk.len = 4 + pub.len;
|
|
|
|
chunk = chunk_cat("ccc", chunk_from_thing(this->client_random),
|
|
|
|
chunk_from_thing(this->server_random), chunk);
|
|
|
|
if (!this->crypto->verify(this->crypto, public, reader, chunk))
|
|
|
|
{
|
|
|
|
public->destroy(public);
|
|
|
|
free(chunk.ptr);
|
|
|
|
DBG1(DBG_TLS, "verifying DH parameters failed");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_BAD_CERTIFICATE);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
public->destroy(public);
|
|
|
|
free(chunk.ptr);
|
|
|
|
|
|
|
|
this->dh = lib->crypto->create_dh(lib->crypto, group);
|
|
|
|
if (!this->dh)
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "DH group %N not supported",
|
|
|
|
diffie_hellman_group_names, group);
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
2010-09-06 13:31:32 +00:00
|
|
|
|
2020-08-26 13:23:53 +00:00
|
|
|
if (group != CURVE_25519 &&
|
|
|
|
group != CURVE_448)
|
|
|
|
{ /* classic ECPoint format (see RFC 8422, section 5.4.1) */
|
|
|
|
if (pub.ptr[0] != TLS_ANSI_UNCOMPRESSED)
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "DH point format '%N' not supported",
|
|
|
|
tls_ansi_point_format_names, pub.ptr[0]);
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
pub = chunk_skip(pub, 1);
|
2010-09-06 13:31:32 +00:00
|
|
|
}
|
2020-08-26 13:23:53 +00:00
|
|
|
|
|
|
|
if (!this->dh->set_other_public_value(this->dh, pub))
|
2015-03-23 12:09:32 +00:00
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "applying DH public value failed");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
2010-09-03 09:00:07 +00:00
|
|
|
|
|
|
|
this->state = STATE_KEY_EXCHANGE_RECEIVED;
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
|
2010-09-02 17:26:19 +00:00
|
|
|
/**
|
|
|
|
* Process a Server Key Exchange
|
|
|
|
*/
|
|
|
|
static status_t process_key_exchange(private_tls_peer_t *this,
|
2011-05-31 13:46:30 +00:00
|
|
|
bio_reader_t *reader)
|
2010-09-02 17:26:19 +00:00
|
|
|
{
|
|
|
|
diffie_hellman_group_t group;
|
|
|
|
|
|
|
|
this->crypto->append_handshake(this->crypto,
|
|
|
|
TLS_SERVER_KEY_EXCHANGE, reader->peek(reader));
|
|
|
|
|
|
|
|
group = this->crypto->get_dh_group(this->crypto);
|
2010-09-03 14:22:49 +00:00
|
|
|
if (group == MODP_NONE)
|
2010-09-02 17:26:19 +00:00
|
|
|
{
|
2010-09-03 14:22:49 +00:00
|
|
|
DBG1(DBG_TLS, "received Server Key Exchange, but not required "
|
|
|
|
"for current suite");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
if (diffie_hellman_group_is_ec(group))
|
|
|
|
{
|
|
|
|
return process_ec_key_exchange(this, reader);
|
2010-09-02 17:26:19 +00:00
|
|
|
}
|
2010-09-03 14:22:49 +00:00
|
|
|
return process_modp_key_exchange(this, reader);
|
2010-09-02 17:26:19 +00:00
|
|
|
}
|
|
|
|
|
2010-01-25 16:31:55 +00:00
|
|
|
/**
|
2010-08-24 09:34:43 +00:00
|
|
|
* Process a Certificate Request message
|
2010-01-25 16:31:55 +00:00
|
|
|
*/
|
2011-05-31 13:46:30 +00:00
|
|
|
static status_t process_certreq(private_tls_peer_t *this, bio_reader_t *reader)
|
2010-01-25 16:31:55 +00:00
|
|
|
{
|
2010-02-01 10:25:44 +00:00
|
|
|
chunk_t types, hashsig, data;
|
2011-05-31 13:46:30 +00:00
|
|
|
bio_reader_t *authorities;
|
2010-01-25 16:31:55 +00:00
|
|
|
identification_t *id;
|
2010-02-03 18:53:40 +00:00
|
|
|
certificate_t *cert;
|
|
|
|
|
2010-08-24 09:34:43 +00:00
|
|
|
if (!this->peer)
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "server requested a certificate, but client "
|
|
|
|
"authentication disabled");
|
|
|
|
}
|
2010-02-05 13:25:38 +00:00
|
|
|
this->crypto->append_handshake(this->crypto,
|
|
|
|
TLS_CERTIFICATE_REQUEST, reader->peek(reader));
|
2010-01-25 16:31:55 +00:00
|
|
|
|
2010-02-01 10:25:44 +00:00
|
|
|
if (!reader->read_data8(reader, &types))
|
2010-01-25 16:31:55 +00:00
|
|
|
{
|
2010-08-23 12:22:38 +00:00
|
|
|
DBG1(DBG_TLS, "certreq message header invalid");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
|
|
|
|
return NEED_MORE;
|
2010-01-25 16:31:55 +00:00
|
|
|
}
|
2010-09-02 08:29:32 +00:00
|
|
|
this->cert_types = chunk_clone(types);
|
2020-05-21 07:37:38 +00:00
|
|
|
if (this->tls->get_version_max(this->tls) >= TLS_1_2)
|
2010-01-25 16:31:55 +00:00
|
|
|
{
|
2010-02-01 10:25:44 +00:00
|
|
|
if (!reader->read_data16(reader, &hashsig))
|
2010-01-25 16:31:55 +00:00
|
|
|
{
|
2010-08-23 12:22:38 +00:00
|
|
|
DBG1(DBG_TLS, "certreq message invalid");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
|
|
|
|
return NEED_MORE;
|
2010-01-25 16:31:55 +00:00
|
|
|
}
|
2010-09-02 08:29:32 +00:00
|
|
|
this->hashsig = chunk_clone(hashsig);
|
2010-01-25 16:31:55 +00:00
|
|
|
}
|
2010-02-01 10:25:44 +00:00
|
|
|
if (!reader->read_data16(reader, &data))
|
2010-01-25 16:31:55 +00:00
|
|
|
{
|
2010-08-23 12:22:38 +00:00
|
|
|
DBG1(DBG_TLS, "certreq message invalid");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
|
|
|
|
return NEED_MORE;
|
2010-01-25 16:31:55 +00:00
|
|
|
}
|
2011-05-31 13:46:30 +00:00
|
|
|
authorities = bio_reader_create(data);
|
2010-02-01 10:25:44 +00:00
|
|
|
while (authorities->remaining(authorities))
|
2010-01-25 16:31:55 +00:00
|
|
|
{
|
2010-02-01 10:25:44 +00:00
|
|
|
if (!authorities->read_data16(authorities, &data))
|
2010-01-25 16:31:55 +00:00
|
|
|
{
|
2010-08-23 12:22:38 +00:00
|
|
|
DBG1(DBG_TLS, "certreq message invalid");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
|
2010-02-01 10:25:44 +00:00
|
|
|
authorities->destroy(authorities);
|
2010-08-23 12:22:38 +00:00
|
|
|
return NEED_MORE;
|
2010-01-25 16:31:55 +00:00
|
|
|
}
|
2011-04-14 17:54:02 +00:00
|
|
|
if (this->peer)
|
2010-02-03 18:53:40 +00:00
|
|
|
{
|
2011-04-14 17:54:02 +00:00
|
|
|
id = identification_create_from_encoding(ID_DER_ASN1_DN, data);
|
|
|
|
cert = lib->credmgr->get_cert(lib->credmgr,
|
|
|
|
CERT_X509, KEY_ANY, id, TRUE);
|
|
|
|
if (cert)
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "received TLS cert request for '%Y", id);
|
|
|
|
this->peer_auth->add(this->peer_auth, AUTH_RULE_CA_CERT, cert);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "received TLS cert request for unknown CA '%Y'", id);
|
|
|
|
}
|
|
|
|
id->destroy(id);
|
2010-02-03 18:53:40 +00:00
|
|
|
}
|
2010-01-25 16:31:55 +00:00
|
|
|
}
|
2010-02-01 10:25:44 +00:00
|
|
|
authorities->destroy(authorities);
|
2010-02-09 11:37:29 +00:00
|
|
|
this->state = STATE_CERTREQ_RECEIVED;
|
2010-01-25 16:31:55 +00:00
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
|
2010-02-03 18:53:40 +00:00
|
|
|
/**
|
|
|
|
* Process Hello Done message
|
|
|
|
*/
|
|
|
|
static status_t process_hello_done(private_tls_peer_t *this,
|
2011-05-31 13:46:30 +00:00
|
|
|
bio_reader_t *reader)
|
2010-02-03 18:53:40 +00:00
|
|
|
{
|
2010-02-05 13:25:38 +00:00
|
|
|
this->crypto->append_handshake(this->crypto,
|
|
|
|
TLS_SERVER_HELLO_DONE, reader->peek(reader));
|
2010-02-03 18:53:40 +00:00
|
|
|
this->state = STATE_HELLO_DONE;
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
|
2010-02-05 10:50:29 +00:00
|
|
|
/**
|
|
|
|
* Process finished message
|
|
|
|
*/
|
2011-05-31 13:46:30 +00:00
|
|
|
static status_t process_finished(private_tls_peer_t *this, bio_reader_t *reader)
|
2010-02-05 10:50:29 +00:00
|
|
|
{
|
2020-05-21 07:37:38 +00:00
|
|
|
chunk_t received, verify_data;
|
2020-08-25 14:25:37 +00:00
|
|
|
u_char buf[12];
|
2010-02-05 11:27:52 +00:00
|
|
|
|
2020-05-21 07:37:38 +00:00
|
|
|
if (this->tls->get_version_max(this->tls) < TLS_1_3)
|
2010-02-05 11:27:52 +00:00
|
|
|
{
|
2020-05-21 07:37:38 +00:00
|
|
|
if (!reader->read_data(reader, sizeof(buf), &received))
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "received server finished too short");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
2020-08-25 14:52:20 +00:00
|
|
|
if (!this->crypto->calculate_finished_legacy(this->crypto,
|
|
|
|
"server finished", buf))
|
2020-05-21 07:37:38 +00:00
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "calculating server finished failed");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
2020-08-25 14:25:37 +00:00
|
|
|
verify_data = chunk_from_thing(buf);
|
2010-02-05 11:27:52 +00:00
|
|
|
}
|
2020-05-21 07:37:38 +00:00
|
|
|
else
|
2010-02-05 11:27:52 +00:00
|
|
|
{
|
2020-08-25 14:25:37 +00:00
|
|
|
received = reader->peek(reader);
|
|
|
|
if (!this->crypto->calculate_finished(this->crypto, TRUE, &verify_data))
|
2020-05-21 07:37:38 +00:00
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "calculating server finished failed");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
2010-02-05 11:27:52 +00:00
|
|
|
}
|
2011-12-31 00:41:56 +00:00
|
|
|
|
2020-08-25 14:25:37 +00:00
|
|
|
if (!chunk_equals_const(received, verify_data))
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "received server finished invalid");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (verify_data.ptr != buf)
|
|
|
|
{
|
|
|
|
chunk_free(&verify_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
this->crypto->append_handshake(this->crypto, TLS_FINISHED, received);
|
|
|
|
this->state = STATE_FINISHED_RECEIVED;
|
2010-02-05 11:27:52 +00:00
|
|
|
return NEED_MORE;
|
2010-02-05 10:50:29 +00:00
|
|
|
}
|
|
|
|
|
2020-05-21 07:37:38 +00:00
|
|
|
/**
|
2020-08-25 14:25:37 +00:00
|
|
|
* Process NewSessionTicket message
|
2020-05-21 07:37:38 +00:00
|
|
|
*/
|
|
|
|
static status_t process_new_session_ticket(private_tls_peer_t *this,
|
|
|
|
bio_reader_t *reader)
|
|
|
|
{
|
|
|
|
uint32_t ticket_lifetime, ticket_age_add;
|
|
|
|
chunk_t ticket_nonce, ticket, extensions;
|
|
|
|
|
|
|
|
if (!reader->read_uint32(reader, &ticket_lifetime) ||
|
|
|
|
!reader->read_uint32(reader, &ticket_age_add) ||
|
|
|
|
!reader->read_data8(reader, &ticket_nonce) ||
|
|
|
|
!reader->read_data16(reader, &ticket) ||
|
|
|
|
!reader->read_data16(reader, &extensions))
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "received invalid NewSessionTicket");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
|
2010-01-25 09:44:35 +00:00
|
|
|
METHOD(tls_handshake_t, process, status_t,
|
2011-05-31 13:46:30 +00:00
|
|
|
private_tls_peer_t *this, tls_handshake_type_t type, bio_reader_t *reader)
|
2010-01-25 09:44:35 +00:00
|
|
|
{
|
2010-02-09 11:37:29 +00:00
|
|
|
tls_handshake_type_t expected;
|
|
|
|
|
2020-05-21 07:37:38 +00:00
|
|
|
if (this->tls->get_version_max(this->tls) < TLS_1_3)
|
2010-01-25 16:31:55 +00:00
|
|
|
{
|
2020-05-21 07:37:38 +00:00
|
|
|
switch (this->state)
|
|
|
|
{
|
|
|
|
case STATE_HELLO_SENT:
|
|
|
|
if (type == TLS_SERVER_HELLO)
|
|
|
|
{
|
|
|
|
return process_server_hello(this, reader);
|
|
|
|
}
|
|
|
|
expected = TLS_SERVER_HELLO;
|
|
|
|
break;
|
|
|
|
case STATE_HELLO_RECEIVED:
|
|
|
|
if (type == TLS_CERTIFICATE)
|
|
|
|
{
|
|
|
|
return process_certificate(this, reader);
|
|
|
|
}
|
|
|
|
expected = TLS_CERTIFICATE;
|
|
|
|
break;
|
|
|
|
case STATE_CERT_RECEIVED:
|
|
|
|
if (type == TLS_SERVER_KEY_EXCHANGE)
|
|
|
|
{
|
|
|
|
return process_key_exchange(this, reader);
|
|
|
|
}
|
|
|
|
/* fall through since TLS_SERVER_KEY_EXCHANGE is optional */
|
|
|
|
case STATE_KEY_EXCHANGE_RECEIVED:
|
|
|
|
if (type == TLS_CERTIFICATE_REQUEST)
|
|
|
|
{
|
|
|
|
return process_certreq(this, reader);
|
|
|
|
}
|
|
|
|
/* no cert request, server does not want to authenticate us */
|
|
|
|
DESTROY_IF(this->peer);
|
|
|
|
this->peer = NULL;
|
|
|
|
/* fall through since TLS_CERTIFICATE_REQUEST is optional */
|
|
|
|
case STATE_CERTREQ_RECEIVED:
|
|
|
|
if (type == TLS_SERVER_HELLO_DONE)
|
|
|
|
{
|
|
|
|
return process_hello_done(this, reader);
|
|
|
|
}
|
|
|
|
expected = TLS_SERVER_HELLO_DONE;
|
|
|
|
break;
|
|
|
|
case STATE_CIPHERSPEC_CHANGED_IN:
|
|
|
|
if (type == TLS_FINISHED)
|
|
|
|
{
|
|
|
|
return process_finished(this, reader);
|
|
|
|
}
|
|
|
|
expected = TLS_FINISHED;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
DBG1(DBG_TLS, "TLS %N not expected in current state",
|
|
|
|
tls_handshake_type_names, type);
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch (this->state)
|
|
|
|
{
|
|
|
|
case STATE_HELLO_SENT:
|
|
|
|
if (type == TLS_SERVER_HELLO)
|
|
|
|
{
|
|
|
|
return process_server_hello(this, reader);
|
|
|
|
}
|
|
|
|
expected = TLS_SERVER_HELLO;
|
|
|
|
break;
|
|
|
|
case STATE_CIPHERSPEC_CHANGED_IN:
|
|
|
|
case STATE_HELLO_RECEIVED:
|
|
|
|
if (type == TLS_ENCRYPTED_EXTENSIONS)
|
|
|
|
{
|
|
|
|
return process_encrypted_extensions(this, reader);
|
|
|
|
}
|
|
|
|
expected = TLS_ENCRYPTED_EXTENSIONS;
|
|
|
|
break;
|
|
|
|
case STATE_ENCRYPTED_EXTENSIONS_RECEIVED:
|
|
|
|
if (type == TLS_CERTIFICATE)
|
|
|
|
{
|
|
|
|
return process_certificate(this, reader);
|
|
|
|
}
|
|
|
|
expected = TLS_CERTIFICATE;
|
|
|
|
break;
|
|
|
|
case STATE_CERT_RECEIVED:
|
|
|
|
if (type == TLS_CERTIFICATE_VERIFY)
|
|
|
|
{
|
|
|
|
return process_cert_verify(this, reader);
|
|
|
|
}
|
|
|
|
expected = TLS_CERTIFICATE_VERIFY;
|
|
|
|
break;
|
|
|
|
case STATE_CERT_VERIFY_RECEIVED:
|
|
|
|
if (type == TLS_FINISHED)
|
|
|
|
{
|
|
|
|
return process_finished(this, reader);
|
|
|
|
}
|
|
|
|
expected = TLS_FINISHED;
|
|
|
|
break;
|
|
|
|
case STATE_FINISHED_RECEIVED:
|
|
|
|
return NEED_MORE;
|
|
|
|
case STATE_FINISHED_SENT_KEY_SWITCHED:
|
|
|
|
if (type == TLS_NEW_SESSION_TICKET)
|
|
|
|
{
|
|
|
|
return process_new_session_ticket(this, reader);
|
|
|
|
}
|
|
|
|
expected = TLS_NEW_SESSION_TICKET;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
DBG1(DBG_TLS, "TLS %N not expected in current state",
|
|
|
|
tls_handshake_type_names, type);
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
2010-01-25 16:31:55 +00:00
|
|
|
}
|
2010-08-20 13:57:47 +00:00
|
|
|
DBG1(DBG_TLS, "TLS %N expected, but received %N",
|
2010-02-09 11:37:29 +00:00
|
|
|
tls_handshake_type_names, expected, tls_handshake_type_names, type);
|
2010-08-23 12:22:38 +00:00
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE);
|
|
|
|
return NEED_MORE;
|
2010-01-25 09:44:35 +00:00
|
|
|
}
|
|
|
|
|
2010-01-25 11:23:59 +00:00
|
|
|
/**
|
|
|
|
* Send a client hello
|
|
|
|
*/
|
2010-02-09 11:37:29 +00:00
|
|
|
static status_t send_client_hello(private_tls_peer_t *this,
|
2020-08-25 14:18:27 +00:00
|
|
|
tls_handshake_type_t *type,
|
|
|
|
bio_writer_t *writer)
|
2010-01-25 11:23:59 +00:00
|
|
|
{
|
2010-08-05 07:51:05 +00:00
|
|
|
tls_cipher_suite_t *suites;
|
2020-08-25 14:18:27 +00:00
|
|
|
bio_writer_t *extensions, *curves = NULL, *versions, *key_share;
|
2020-05-21 07:37:38 +00:00
|
|
|
tls_version_t version_max, version_min;
|
2020-08-25 14:18:27 +00:00
|
|
|
diffie_hellman_group_t group;
|
|
|
|
tls_named_group_t curve, selected_curve = 0;
|
2010-09-03 15:05:39 +00:00
|
|
|
enumerator_t *enumerator;
|
2020-05-21 07:37:38 +00:00
|
|
|
int count, i, v;
|
2010-01-25 11:23:59 +00:00
|
|
|
rng_t *rng;
|
2020-05-21 07:37:38 +00:00
|
|
|
chunk_t pub;
|
2010-01-25 11:23:59 +00:00
|
|
|
|
2010-02-04 10:17:48 +00:00
|
|
|
htoun32(&this->client_random, time(NULL));
|
2010-01-25 11:23:59 +00:00
|
|
|
rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
|
2012-06-25 14:04:40 +00:00
|
|
|
if (!rng ||
|
|
|
|
!rng->get_bytes(rng, sizeof(this->client_random) - 4,
|
|
|
|
this->client_random + 4))
|
2010-01-25 11:23:59 +00:00
|
|
|
{
|
2012-06-25 14:04:40 +00:00
|
|
|
DBG1(DBG_TLS, "failed to generate client random");
|
2010-08-23 12:22:38 +00:00
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
|
2012-06-25 14:04:40 +00:00
|
|
|
DESTROY_IF(rng);
|
2010-08-23 12:22:38 +00:00
|
|
|
return NEED_MORE;
|
2010-01-25 11:23:59 +00:00
|
|
|
}
|
2010-02-01 14:12:18 +00:00
|
|
|
rng->destroy(rng);
|
|
|
|
|
2020-05-21 07:37:38 +00:00
|
|
|
/* TLS version_max in handshake protocol */
|
|
|
|
version_max = this->tls->get_version_max(this->tls);
|
|
|
|
version_min = this->tls->get_version_min(this->tls);
|
|
|
|
if (version_max < TLS_1_3)
|
|
|
|
{
|
|
|
|
this->hello_version = version_max;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this->hello_version = TLS_1_2;
|
|
|
|
}
|
|
|
|
writer->write_uint16(writer, this->hello_version);
|
2010-02-04 10:17:48 +00:00
|
|
|
writer->write_data(writer, chunk_from_thing(this->client_random));
|
2010-08-05 07:51:05 +00:00
|
|
|
|
2011-12-31 00:41:56 +00:00
|
|
|
/* session identifier */
|
|
|
|
this->session = this->crypto->get_session(this->crypto, this->server);
|
|
|
|
writer->write_data8(writer, this->session);
|
2010-02-01 14:12:18 +00:00
|
|
|
|
2010-08-05 07:51:05 +00:00
|
|
|
/* add TLS cipher suites */
|
|
|
|
count = this->crypto->get_cipher_suites(this->crypto, &suites);
|
2010-02-01 14:12:18 +00:00
|
|
|
writer->write_uint16(writer, count * 2);
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
2010-08-05 07:51:05 +00:00
|
|
|
writer->write_uint16(writer, suites[i]);
|
2010-02-01 14:12:18 +00:00
|
|
|
}
|
2010-08-05 07:51:05 +00:00
|
|
|
|
2010-02-01 14:12:18 +00:00
|
|
|
/* NULL compression only */
|
|
|
|
writer->write_uint8(writer, 1);
|
|
|
|
writer->write_uint8(writer, 0);
|
|
|
|
|
2011-05-31 13:46:30 +00:00
|
|
|
extensions = bio_writer_create(32);
|
2010-09-03 09:45:55 +00:00
|
|
|
|
2020-05-21 07:37:38 +00:00
|
|
|
if (this->server->get_type(this->server) == ID_FQDN)
|
|
|
|
{
|
|
|
|
bio_writer_t *names;
|
2010-09-03 09:45:55 +00:00
|
|
|
|
2020-05-21 07:37:38 +00:00
|
|
|
DBG2(DBG_TLS, "sending extension: Server Name Indication for '%Y'",
|
|
|
|
this->server);
|
|
|
|
names = bio_writer_create(8);
|
|
|
|
names->write_uint8(names, TLS_NAME_TYPE_HOST_NAME);
|
|
|
|
names->write_data16(names, this->server->get_encoding(this->server));
|
|
|
|
names->wrap16(names);
|
|
|
|
extensions->write_uint16(extensions, TLS_EXT_SERVER_NAME);
|
|
|
|
extensions->write_data16(extensions, names->get_buf(names));
|
|
|
|
names->destroy(names);
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG2(DBG_TLS, "sending extension: %N",
|
|
|
|
tls_extension_names, TLS_EXT_SUPPORTED_GROUPS);
|
2010-09-03 15:05:39 +00:00
|
|
|
enumerator = this->crypto->create_ec_enumerator(this->crypto);
|
2020-08-25 14:18:27 +00:00
|
|
|
while (enumerator->enumerate(enumerator, &group, &curve))
|
2010-09-03 15:05:39 +00:00
|
|
|
{
|
2020-08-25 15:23:45 +00:00
|
|
|
if (this->requested_curve && this->requested_curve != curve)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2010-09-03 15:05:39 +00:00
|
|
|
if (!curves)
|
|
|
|
{
|
2020-05-21 07:37:38 +00:00
|
|
|
extensions->write_uint16(extensions, TLS_EXT_SUPPORTED_GROUPS);
|
2011-05-31 13:46:30 +00:00
|
|
|
curves = bio_writer_create(16);
|
2010-09-03 15:05:39 +00:00
|
|
|
}
|
2020-08-25 14:18:27 +00:00
|
|
|
if (!this->dh)
|
|
|
|
{
|
|
|
|
this->dh = lib->crypto->create_dh(lib->crypto, group);
|
|
|
|
if (!this->dh)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
selected_curve = curve;
|
|
|
|
}
|
2010-09-03 15:05:39 +00:00
|
|
|
curves->write_uint16(curves, curve);
|
|
|
|
}
|
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
if (curves)
|
|
|
|
{
|
2012-11-28 09:18:28 +00:00
|
|
|
curves->wrap16(curves);
|
2010-09-03 15:05:39 +00:00
|
|
|
extensions->write_data16(extensions, curves->get_buf(curves));
|
|
|
|
curves->destroy(curves);
|
2010-09-06 16:51:38 +00:00
|
|
|
|
|
|
|
/* if we support curves, add point format extension */
|
|
|
|
extensions->write_uint16(extensions, TLS_EXT_EC_POINT_FORMATS);
|
|
|
|
extensions->write_uint16(extensions, 2);
|
|
|
|
extensions->write_uint8(extensions, 1);
|
|
|
|
extensions->write_uint8(extensions, TLS_EC_POINT_UNCOMPRESSED);
|
2010-09-03 15:05:39 +00:00
|
|
|
}
|
2020-05-21 07:37:38 +00:00
|
|
|
|
2020-08-25 14:18:27 +00:00
|
|
|
if (version_max >= TLS_1_3)
|
2011-04-14 17:42:32 +00:00
|
|
|
{
|
2020-08-25 14:18:27 +00:00
|
|
|
DBG2(DBG_TLS, "sending extension: %N",
|
|
|
|
tls_extension_names, TLS_EXT_SUPPORTED_VERSIONS);
|
|
|
|
extensions->write_uint16(extensions, TLS_EXT_SUPPORTED_VERSIONS);
|
|
|
|
versions = bio_writer_create(0);
|
|
|
|
for (v = version_max; v >= version_min; v--)
|
|
|
|
{
|
|
|
|
versions->write_uint16(versions, v);
|
|
|
|
}
|
|
|
|
versions->wrap8(versions);
|
|
|
|
extensions->write_data16(extensions, versions->get_buf(versions));
|
|
|
|
versions->destroy(versions);
|
2020-05-21 07:37:38 +00:00
|
|
|
}
|
2011-04-14 17:42:32 +00:00
|
|
|
|
2020-08-25 15:23:45 +00:00
|
|
|
if (this->cookie.len)
|
|
|
|
{
|
|
|
|
DBG2(DBG_TLS, "sending extension: %N",
|
|
|
|
tls_extension_names, TLS_EXT_COOKIE);
|
|
|
|
extensions->write_uint16(extensions, TLS_EXT_COOKIE);
|
|
|
|
extensions->write_uint16(extensions, this->cookie.len + 2);
|
|
|
|
extensions->write_data16(extensions, this->cookie);
|
|
|
|
chunk_free(&this->cookie);
|
|
|
|
}
|
|
|
|
|
2020-05-21 07:37:38 +00:00
|
|
|
DBG2(DBG_TLS, "sending extension: %N",
|
|
|
|
tls_extension_names, TLS_EXT_SIGNATURE_ALGORITHMS);
|
|
|
|
extensions->write_uint16(extensions, TLS_EXT_SIGNATURE_ALGORITHMS);
|
|
|
|
this->crypto->get_signature_algorithms(this->crypto, extensions);
|
2011-04-14 17:42:32 +00:00
|
|
|
|
2020-08-25 14:18:27 +00:00
|
|
|
if (this->dh)
|
2020-05-21 07:37:38 +00:00
|
|
|
{
|
2020-08-25 14:18:27 +00:00
|
|
|
DBG2(DBG_TLS, "sending extension: %N",
|
|
|
|
tls_extension_names, TLS_EXT_KEY_SHARE);
|
|
|
|
if (!this->dh->get_my_public_value(this->dh, &pub))
|
|
|
|
{
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
|
|
|
|
extensions->destroy(extensions);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
extensions->write_uint16(extensions, TLS_EXT_KEY_SHARE);
|
|
|
|
key_share = bio_writer_create(pub.len + 6);
|
|
|
|
key_share->write_uint16(key_share, selected_curve);
|
2020-08-26 14:08:26 +00:00
|
|
|
if (selected_curve == TLS_CURVE25519 ||
|
|
|
|
selected_curve == TLS_CURVE448)
|
|
|
|
{
|
|
|
|
key_share->write_data16(key_share, pub);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ /* classic format (see RFC 8446, section 4.2.8.2) */
|
|
|
|
key_share->write_uint16(key_share, pub.len + 1);
|
|
|
|
key_share->write_uint8(key_share, TLS_ANSI_UNCOMPRESSED);
|
|
|
|
key_share->write_data(key_share, pub);
|
|
|
|
}
|
2020-08-25 14:18:27 +00:00
|
|
|
key_share->wrap16(key_share);
|
|
|
|
extensions->write_data16(extensions, key_share->get_buf(key_share));
|
|
|
|
key_share->destroy(key_share);
|
|
|
|
free(pub.ptr);
|
2011-04-14 17:42:32 +00:00
|
|
|
}
|
2010-09-03 09:45:55 +00:00
|
|
|
|
2010-09-02 17:19:17 +00:00
|
|
|
writer->write_data16(writer, extensions->get_buf(extensions));
|
|
|
|
extensions->destroy(extensions);
|
|
|
|
|
2010-02-01 14:12:18 +00:00
|
|
|
*type = TLS_CLIENT_HELLO;
|
2010-01-25 16:31:55 +00:00
|
|
|
this->state = STATE_HELLO_SENT;
|
2010-02-05 13:25:38 +00:00
|
|
|
this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
|
2010-02-03 18:53:40 +00:00
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
|
2010-09-02 08:29:32 +00:00
|
|
|
/**
|
|
|
|
* Find a private key suitable to sign Certificate Verify
|
|
|
|
*/
|
|
|
|
static private_key_t *find_private_key(private_tls_peer_t *this)
|
|
|
|
{
|
|
|
|
private_key_t *key = NULL;
|
2011-05-31 13:46:30 +00:00
|
|
|
bio_reader_t *reader;
|
2010-09-02 08:29:32 +00:00
|
|
|
key_type_t type;
|
2016-03-22 12:22:01 +00:00
|
|
|
uint8_t cert;
|
2010-09-02 08:29:32 +00:00
|
|
|
|
|
|
|
if (!this->peer)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-05-31 13:46:30 +00:00
|
|
|
reader = bio_reader_create(this->cert_types);
|
2010-09-02 08:29:32 +00:00
|
|
|
while (reader->remaining(reader) && reader->read_uint8(reader, &cert))
|
|
|
|
{
|
|
|
|
switch (cert)
|
|
|
|
{
|
|
|
|
case TLS_RSA_SIGN:
|
|
|
|
type = KEY_RSA;
|
|
|
|
break;
|
|
|
|
case TLS_ECDSA_SIGN:
|
|
|
|
type = KEY_ECDSA;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
key = lib->credmgr->get_private(lib->credmgr, type,
|
|
|
|
this->peer, this->peer_auth);
|
|
|
|
if (key)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
reader->destroy(reader);
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
2010-02-03 18:53:40 +00:00
|
|
|
/**
|
|
|
|
* Send Certificate
|
|
|
|
*/
|
|
|
|
static status_t send_certificate(private_tls_peer_t *this,
|
2011-05-31 13:46:30 +00:00
|
|
|
tls_handshake_type_t *type, bio_writer_t *writer)
|
2010-02-03 18:53:40 +00:00
|
|
|
{
|
|
|
|
enumerator_t *enumerator;
|
|
|
|
certificate_t *cert;
|
|
|
|
auth_rule_t rule;
|
2011-05-31 13:46:30 +00:00
|
|
|
bio_writer_t *certs;
|
2010-02-03 18:53:40 +00:00
|
|
|
chunk_t data;
|
|
|
|
|
2010-09-02 08:29:32 +00:00
|
|
|
this->private = find_private_key(this);
|
2010-02-03 18:53:40 +00:00
|
|
|
if (!this->private)
|
|
|
|
{
|
2011-04-14 18:00:54 +00:00
|
|
|
DBG1(DBG_TLS, "no TLS peer certificate found for '%Y', "
|
|
|
|
"skipping client authentication", this->peer);
|
2013-02-28 10:39:55 +00:00
|
|
|
this->peer->destroy(this->peer);
|
2011-04-14 18:00:54 +00:00
|
|
|
this->peer = NULL;
|
2010-02-03 18:53:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* generate certificate payload */
|
2011-05-31 13:46:30 +00:00
|
|
|
certs = bio_writer_create(256);
|
2011-04-14 18:00:54 +00:00
|
|
|
if (this->peer)
|
2010-02-03 18:53:40 +00:00
|
|
|
{
|
2011-04-14 18:00:54 +00:00
|
|
|
cert = this->peer_auth->get(this->peer_auth, AUTH_RULE_SUBJECT_CERT);
|
|
|
|
if (cert)
|
2010-02-03 18:53:40 +00:00
|
|
|
{
|
2010-03-19 17:55:23 +00:00
|
|
|
if (cert->get_encoding(cert, CERT_ASN1_DER, &data))
|
|
|
|
{
|
2011-04-14 18:00:54 +00:00
|
|
|
DBG1(DBG_TLS, "sending TLS peer certificate '%Y'",
|
2010-03-19 17:55:23 +00:00
|
|
|
cert->get_subject(cert));
|
|
|
|
certs->write_data24(certs, data);
|
|
|
|
free(data.ptr);
|
|
|
|
}
|
2010-02-03 18:53:40 +00:00
|
|
|
}
|
2011-04-14 18:00:54 +00:00
|
|
|
enumerator = this->peer_auth->create_enumerator(this->peer_auth);
|
|
|
|
while (enumerator->enumerate(enumerator, &rule, &cert))
|
|
|
|
{
|
|
|
|
if (rule == AUTH_RULE_IM_CERT)
|
|
|
|
{
|
|
|
|
if (cert->get_encoding(cert, CERT_ASN1_DER, &data))
|
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "sending TLS intermediate certificate '%Y'",
|
|
|
|
cert->get_subject(cert));
|
|
|
|
certs->write_data24(certs, data);
|
|
|
|
free(data.ptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
enumerator->destroy(enumerator);
|
2010-02-03 18:53:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
writer->write_data24(writer, certs->get_buf(certs));
|
|
|
|
certs->destroy(certs);
|
|
|
|
|
|
|
|
*type = TLS_CERTIFICATE;
|
|
|
|
this->state = STATE_CERT_SENT;
|
2010-02-05 13:25:38 +00:00
|
|
|
this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
|
2010-02-03 18:53:40 +00:00
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-09-02 17:26:19 +00:00
|
|
|
* Send client key exchange, using premaster encryption
|
2010-02-03 18:53:40 +00:00
|
|
|
*/
|
2010-09-02 17:26:19 +00:00
|
|
|
static status_t send_key_exchange_encrypt(private_tls_peer_t *this,
|
2011-05-31 13:46:30 +00:00
|
|
|
tls_handshake_type_t *type, bio_writer_t *writer)
|
2010-02-03 18:53:40 +00:00
|
|
|
{
|
2010-09-02 17:26:19 +00:00
|
|
|
public_key_t *public;
|
2010-02-03 18:53:40 +00:00
|
|
|
rng_t *rng;
|
|
|
|
char premaster[48];
|
|
|
|
chunk_t encrypted;
|
|
|
|
|
|
|
|
rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
|
2012-06-25 14:04:40 +00:00
|
|
|
if (!rng || !rng->get_bytes(rng, sizeof(premaster) - 2, premaster + 2))
|
2010-02-03 18:53:40 +00:00
|
|
|
{
|
2012-06-25 14:04:40 +00:00
|
|
|
DBG1(DBG_TLS, "failed to generate TLS premaster secret");
|
2010-08-25 16:24:27 +00:00
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
|
2012-06-25 14:04:40 +00:00
|
|
|
DESTROY_IF(rng);
|
2010-09-02 17:26:19 +00:00
|
|
|
return NEED_MORE;
|
2010-02-03 18:53:40 +00:00
|
|
|
}
|
|
|
|
rng->destroy(rng);
|
2014-03-25 09:50:51 +00:00
|
|
|
htoun16(premaster, this->hello_version);
|
2010-02-03 18:53:40 +00:00
|
|
|
|
2012-07-06 07:28:25 +00:00
|
|
|
if (!this->crypto->derive_secrets(this->crypto, chunk_from_thing(premaster),
|
|
|
|
this->session, this->server,
|
|
|
|
chunk_from_thing(this->client_random),
|
|
|
|
chunk_from_thing(this->server_random)))
|
|
|
|
{
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
2010-02-04 10:17:48 +00:00
|
|
|
|
2010-09-02 17:26:19 +00:00
|
|
|
public = find_public_key(this);
|
2010-02-03 18:53:40 +00:00
|
|
|
if (!public)
|
|
|
|
{
|
2010-08-20 13:57:47 +00:00
|
|
|
DBG1(DBG_TLS, "no TLS public key found for server '%Y'", this->server);
|
2010-08-25 16:24:27 +00:00
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_CERTIFICATE_UNKNOWN);
|
2010-09-02 17:26:19 +00:00
|
|
|
return NEED_MORE;
|
2010-02-03 18:53:40 +00:00
|
|
|
}
|
2010-08-10 12:38:44 +00:00
|
|
|
if (!public->encrypt(public, ENCRYPT_RSA_PKCS1,
|
|
|
|
chunk_from_thing(premaster), &encrypted))
|
2010-02-03 18:53:40 +00:00
|
|
|
{
|
|
|
|
public->destroy(public);
|
2010-08-20 13:57:47 +00:00
|
|
|
DBG1(DBG_TLS, "encrypting TLS premaster secret failed");
|
2010-08-25 16:24:27 +00:00
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_BAD_CERTIFICATE);
|
2010-09-02 17:26:19 +00:00
|
|
|
return NEED_MORE;
|
2010-02-03 18:53:40 +00:00
|
|
|
}
|
|
|
|
public->destroy(public);
|
|
|
|
|
|
|
|
writer->write_data16(writer, encrypted);
|
|
|
|
free(encrypted.ptr);
|
|
|
|
|
|
|
|
*type = TLS_CLIENT_KEY_EXCHANGE;
|
|
|
|
this->state = STATE_KEY_EXCHANGE_SENT;
|
2010-02-05 13:25:38 +00:00
|
|
|
this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
|
2010-02-03 18:53:40 +00:00
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
|
2010-09-02 17:26:19 +00:00
|
|
|
/**
|
|
|
|
* Send client key exchange, using DHE exchange
|
|
|
|
*/
|
|
|
|
static status_t send_key_exchange_dhe(private_tls_peer_t *this,
|
2011-05-31 13:46:30 +00:00
|
|
|
tls_handshake_type_t *type, bio_writer_t *writer)
|
2010-09-02 17:26:19 +00:00
|
|
|
{
|
|
|
|
chunk_t premaster, pub;
|
|
|
|
|
2015-03-23 09:54:24 +00:00
|
|
|
if (!this->dh->get_shared_secret(this->dh, &premaster))
|
2010-09-02 17:26:19 +00:00
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "calculating premaster from DH failed");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
2012-07-06 07:28:25 +00:00
|
|
|
if (!this->crypto->derive_secrets(this->crypto, premaster,
|
|
|
|
this->session, this->server,
|
|
|
|
chunk_from_thing(this->client_random),
|
|
|
|
chunk_from_thing(this->server_random)))
|
|
|
|
{
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
|
|
|
|
chunk_clear(&premaster);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
2010-09-02 17:26:19 +00:00
|
|
|
chunk_clear(&premaster);
|
|
|
|
|
2015-03-23 10:37:27 +00:00
|
|
|
if (!this->dh->get_my_public_value(this->dh, &pub))
|
|
|
|
{
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
2020-08-26 13:23:53 +00:00
|
|
|
switch (this->dh->get_dh_group(this->dh))
|
2010-09-03 09:00:07 +00:00
|
|
|
{
|
2020-08-26 13:23:53 +00:00
|
|
|
case MODP_CUSTOM:
|
|
|
|
writer->write_data16(writer, pub);
|
|
|
|
break;
|
|
|
|
case CURVE_25519:
|
|
|
|
case CURVE_448:
|
|
|
|
/* ECPoint uses an 8-bit length header only */
|
|
|
|
writer->write_data8(writer, pub);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* classic ECPoint format (see RFC 8422, section 5.4.1) */
|
|
|
|
writer->write_uint8(writer, pub.len + 1);
|
|
|
|
writer->write_uint8(writer, TLS_ANSI_UNCOMPRESSED);
|
|
|
|
writer->write_data(writer, pub);
|
|
|
|
break;
|
2010-09-03 09:00:07 +00:00
|
|
|
}
|
2010-09-02 17:26:19 +00:00
|
|
|
free(pub.ptr);
|
|
|
|
|
|
|
|
*type = TLS_CLIENT_KEY_EXCHANGE;
|
|
|
|
this->state = STATE_KEY_EXCHANGE_SENT;
|
|
|
|
this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Send client key exchange, depending on suite
|
|
|
|
*/
|
|
|
|
static status_t send_key_exchange(private_tls_peer_t *this,
|
2011-05-31 13:46:30 +00:00
|
|
|
tls_handshake_type_t *type, bio_writer_t *writer)
|
2010-09-02 17:26:19 +00:00
|
|
|
{
|
|
|
|
if (this->dh)
|
|
|
|
{
|
|
|
|
return send_key_exchange_dhe(this, type, writer);
|
|
|
|
}
|
|
|
|
return send_key_exchange_encrypt(this, type, writer);
|
|
|
|
}
|
|
|
|
|
2010-02-03 18:53:40 +00:00
|
|
|
/**
|
|
|
|
* Send certificate verify
|
|
|
|
*/
|
|
|
|
static status_t send_certificate_verify(private_tls_peer_t *this,
|
2011-05-31 13:46:30 +00:00
|
|
|
tls_handshake_type_t *type, bio_writer_t *writer)
|
2010-02-03 18:53:40 +00:00
|
|
|
{
|
2010-02-05 13:25:38 +00:00
|
|
|
if (!this->private ||
|
2010-09-02 08:29:32 +00:00
|
|
|
!this->crypto->sign_handshake(this->crypto, this->private,
|
|
|
|
writer, this->hashsig))
|
2010-02-03 18:53:40 +00:00
|
|
|
{
|
2010-08-20 13:57:47 +00:00
|
|
|
DBG1(DBG_TLS, "creating TLS Certificate Verify signature failed");
|
2010-08-25 16:24:27 +00:00
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
|
2010-09-02 17:26:19 +00:00
|
|
|
return NEED_MORE;
|
2010-02-03 18:53:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
*type = TLS_CERTIFICATE_VERIFY;
|
|
|
|
this->state = STATE_VERIFY_SENT;
|
2010-02-05 13:25:38 +00:00
|
|
|
this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
|
2010-02-03 18:53:40 +00:00
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Send Finished
|
|
|
|
*/
|
|
|
|
static status_t send_finished(private_tls_peer_t *this,
|
2011-05-31 13:46:30 +00:00
|
|
|
tls_handshake_type_t *type, bio_writer_t *writer)
|
2010-02-03 18:53:40 +00:00
|
|
|
{
|
2020-05-21 07:37:38 +00:00
|
|
|
if (this->tls->get_version_max(this->tls) < TLS_1_3)
|
2010-02-04 10:17:48 +00:00
|
|
|
{
|
2020-05-21 07:37:38 +00:00
|
|
|
char buf[12];
|
|
|
|
|
2020-08-25 14:52:20 +00:00
|
|
|
if (!this->crypto->calculate_finished_legacy(this->crypto,
|
|
|
|
"client finished", buf))
|
2020-05-21 07:37:38 +00:00
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "calculating client finished data failed");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
|
|
|
|
writer->write_data(writer, chunk_from_thing(buf));
|
2010-02-04 10:17:48 +00:00
|
|
|
}
|
2020-05-21 07:37:38 +00:00
|
|
|
else
|
|
|
|
{
|
2020-08-25 14:25:37 +00:00
|
|
|
chunk_t verify_data;
|
|
|
|
|
2020-08-25 14:52:20 +00:00
|
|
|
if (!this->crypto->calculate_finished(this->crypto, FALSE, &verify_data))
|
2020-05-21 07:37:38 +00:00
|
|
|
{
|
|
|
|
DBG1(DBG_TLS, "calculating client finished data failed");
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
2010-02-04 10:17:48 +00:00
|
|
|
|
2020-05-21 07:37:38 +00:00
|
|
|
writer->write_data(writer, verify_data);
|
2020-08-25 14:25:37 +00:00
|
|
|
chunk_free(&verify_data);
|
2020-05-21 07:37:38 +00:00
|
|
|
}
|
2010-02-04 10:17:48 +00:00
|
|
|
|
2010-02-03 18:53:40 +00:00
|
|
|
*type = TLS_FINISHED;
|
|
|
|
this->state = STATE_FINISHED_SENT;
|
2010-02-05 13:25:38 +00:00
|
|
|
this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
|
2010-01-25 11:23:59 +00:00
|
|
|
return NEED_MORE;
|
|
|
|
}
|
|
|
|
|
2010-01-25 09:44:35 +00:00
|
|
|
METHOD(tls_handshake_t, build, status_t,
|
2011-05-31 13:46:30 +00:00
|
|
|
private_tls_peer_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
|
2010-01-25 09:44:35 +00:00
|
|
|
{
|
2020-05-21 07:37:38 +00:00
|
|
|
if (this->tls->get_version_max(this->tls) < TLS_1_3)
|
2010-01-25 11:23:59 +00:00
|
|
|
{
|
2020-05-21 07:37:38 +00:00
|
|
|
switch (this->state)
|
|
|
|
{
|
|
|
|
case STATE_INIT:
|
|
|
|
return send_client_hello(this, type, writer);
|
|
|
|
case STATE_HELLO_DONE:
|
|
|
|
if (this->peer)
|
|
|
|
{
|
|
|
|
return send_certificate(this, type, writer);
|
|
|
|
}
|
|
|
|
/* otherwise fall through to next state */
|
|
|
|
case STATE_CERT_SENT:
|
|
|
|
return send_key_exchange(this, type, writer);
|
|
|
|
case STATE_KEY_EXCHANGE_SENT:
|
|
|
|
if (this->peer)
|
|
|
|
{
|
|
|
|
return send_certificate_verify(this, type, writer);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return INVALID_STATE;
|
|
|
|
}
|
|
|
|
case STATE_CIPHERSPEC_CHANGED_OUT:
|
|
|
|
return send_finished(this, type, writer);
|
|
|
|
default:
|
2010-08-06 14:24:56 +00:00
|
|
|
return INVALID_STATE;
|
2020-05-21 07:37:38 +00:00
|
|
|
}
|
2010-01-25 11:23:59 +00:00
|
|
|
}
|
2020-05-21 07:37:38 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
switch (this->state)
|
|
|
|
{
|
|
|
|
case STATE_INIT:
|
|
|
|
return send_client_hello(this, type, writer);
|
|
|
|
case STATE_HELLO_DONE:
|
|
|
|
case STATE_CIPHERSPEC_CHANGED_OUT:
|
|
|
|
case STATE_FINISHED_RECEIVED:
|
|
|
|
return send_finished(this, type, writer);
|
|
|
|
case STATE_FINISHED_SENT:
|
2020-08-25 11:22:04 +00:00
|
|
|
if (!this->crypto->derive_app_keys(this->crypto))
|
|
|
|
{
|
|
|
|
this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
|
|
|
|
return NEED_MORE;
|
|
|
|
}
|
2020-05-21 07:37:38 +00:00
|
|
|
this->crypto->change_cipher(this->crypto, TRUE);
|
|
|
|
this->crypto->change_cipher(this->crypto, FALSE);
|
|
|
|
this->state = STATE_FINISHED_SENT_KEY_SWITCHED;
|
|
|
|
return SUCCESS;
|
|
|
|
case STATE_FINISHED_SENT_KEY_SWITCHED:
|
|
|
|
return SUCCESS;
|
|
|
|
default:
|
|
|
|
return INVALID_STATE;
|
|
|
|
}
|
|
|
|
}
|
2020-08-25 15:23:45 +00:00
|
|
|
}
|
2020-05-21 07:37:38 +00:00
|
|
|
|
2020-08-25 15:23:45 +00:00
|
|
|
/**
|
|
|
|
* Check if we are currently retrying to connect to the server.
|
|
|
|
*/
|
|
|
|
static bool retrying(private_tls_peer_t *this)
|
|
|
|
{
|
|
|
|
return this->state == STATE_INIT && (this->requested_curve || this->cookie.len);
|
2010-01-25 09:44:35 +00:00
|
|
|
}
|
|
|
|
|
2010-02-03 18:53:40 +00:00
|
|
|
METHOD(tls_handshake_t, cipherspec_changed, bool,
|
2011-12-30 17:29:11 +00:00
|
|
|
private_tls_peer_t *this, bool inbound)
|
2010-02-03 18:53:40 +00:00
|
|
|
{
|
2020-05-21 07:37:38 +00:00
|
|
|
if (this->tls->get_version_max(this->tls) < TLS_1_3)
|
2010-02-03 18:53:40 +00:00
|
|
|
{
|
2020-05-21 07:37:38 +00:00
|
|
|
if (inbound)
|
2011-12-31 00:41:56 +00:00
|
|
|
{
|
2020-05-21 07:37:38 +00:00
|
|
|
if (this->resume)
|
|
|
|
{
|
|
|
|
return this->state == STATE_HELLO_RECEIVED;
|
|
|
|
}
|
|
|
|
return this->state == STATE_FINISHED_SENT;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (this->resume)
|
|
|
|
{
|
|
|
|
return this->state == STATE_FINISHED_RECEIVED;
|
|
|
|
}
|
|
|
|
if (this->peer)
|
|
|
|
{
|
|
|
|
return this->state == STATE_VERIFY_SENT;
|
|
|
|
}
|
|
|
|
return this->state == STATE_KEY_EXCHANGE_SENT;
|
|
|
|
|
2011-12-31 00:41:56 +00:00
|
|
|
}
|
2011-12-30 17:29:11 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-05-21 07:37:38 +00:00
|
|
|
if (inbound)
|
2020-08-25 15:23:45 +00:00
|
|
|
{ /* accept ChangeCipherSpec after ServerHello or HelloRetryRequest */
|
|
|
|
return this->state == STATE_HELLO_RECEIVED || retrying(this);
|
2011-12-31 00:41:56 +00:00
|
|
|
}
|
2020-05-21 07:37:38 +00:00
|
|
|
else
|
2011-12-30 17:29:11 +00:00
|
|
|
{
|
2020-05-21 07:37:38 +00:00
|
|
|
return FALSE;
|
2011-12-30 17:29:11 +00:00
|
|
|
}
|
2010-02-03 18:53:40 +00:00
|
|
|
}
|
2020-05-21 07:37:38 +00:00
|
|
|
|
2010-02-03 18:53:40 +00:00
|
|
|
}
|
|
|
|
|
2011-12-30 17:29:11 +00:00
|
|
|
METHOD(tls_handshake_t, change_cipherspec, void,
|
|
|
|
private_tls_peer_t *this, bool inbound)
|
2010-02-03 18:53:40 +00:00
|
|
|
{
|
2020-05-21 07:37:38 +00:00
|
|
|
if (this->tls->get_version_max(this->tls) < TLS_1_3)
|
|
|
|
{
|
|
|
|
this->crypto->change_cipher(this->crypto, inbound);
|
|
|
|
}
|
|
|
|
|
2020-08-25 15:23:45 +00:00
|
|
|
if (retrying(this))
|
|
|
|
{ /* servers might send a ChangeCipherSpec after a HelloRetryRequest,
|
|
|
|
* which should not cause any state changes */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-12-30 17:29:11 +00:00
|
|
|
if (inbound)
|
2010-02-05 10:50:29 +00:00
|
|
|
{
|
|
|
|
this->state = STATE_CIPHERSPEC_CHANGED_IN;
|
|
|
|
}
|
2011-12-30 17:29:11 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
this->state = STATE_CIPHERSPEC_CHANGED_OUT;
|
|
|
|
}
|
2010-02-03 18:53:40 +00:00
|
|
|
}
|
|
|
|
|
2010-08-12 21:56:44 +00:00
|
|
|
METHOD(tls_handshake_t, finished, bool,
|
|
|
|
private_tls_peer_t *this)
|
|
|
|
{
|
2020-05-21 07:37:38 +00:00
|
|
|
if (this->tls->get_version_max(this->tls) < TLS_1_3)
|
2011-12-31 00:41:56 +00:00
|
|
|
{
|
2020-05-21 07:37:38 +00:00
|
|
|
if (this->resume)
|
|
|
|
{
|
2020-08-25 14:32:40 +00:00
|
|
|
return this->state == STATE_FINISHED_SENT;
|
2020-05-21 07:37:38 +00:00
|
|
|
}
|
|
|
|
return this->state == STATE_FINISHED_RECEIVED;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return this->state == STATE_FINISHED_SENT_KEY_SWITCHED;
|
2011-12-31 00:41:56 +00:00
|
|
|
}
|
2010-08-12 21:56:44 +00:00
|
|
|
}
|
|
|
|
|
2013-02-28 10:39:55 +00:00
|
|
|
METHOD(tls_handshake_t, get_peer_id, identification_t*,
|
|
|
|
private_tls_peer_t *this)
|
|
|
|
{
|
|
|
|
return this->peer;
|
|
|
|
}
|
|
|
|
|
|
|
|
METHOD(tls_handshake_t, get_server_id, identification_t*,
|
|
|
|
private_tls_peer_t *this)
|
|
|
|
{
|
|
|
|
return this->server;
|
|
|
|
}
|
|
|
|
|
2015-01-29 10:13:42 +00:00
|
|
|
METHOD(tls_handshake_t, get_auth, auth_cfg_t*,
|
|
|
|
private_tls_peer_t *this)
|
|
|
|
{
|
|
|
|
return this->server_auth;
|
|
|
|
}
|
|
|
|
|
2010-01-25 09:44:35 +00:00
|
|
|
METHOD(tls_handshake_t, destroy, void,
|
|
|
|
private_tls_peer_t *this)
|
|
|
|
{
|
2010-02-03 18:53:40 +00:00
|
|
|
DESTROY_IF(this->private);
|
2010-09-02 17:26:19 +00:00
|
|
|
DESTROY_IF(this->dh);
|
2013-02-28 10:39:55 +00:00
|
|
|
DESTROY_IF(this->peer);
|
|
|
|
this->server->destroy(this->server);
|
2010-02-03 18:53:40 +00:00
|
|
|
this->peer_auth->destroy(this->peer_auth);
|
|
|
|
this->server_auth->destroy(this->server_auth);
|
2010-09-02 08:29:32 +00:00
|
|
|
free(this->hashsig.ptr);
|
|
|
|
free(this->cert_types.ptr);
|
2011-12-31 00:41:56 +00:00
|
|
|
free(this->session.ptr);
|
2020-08-25 15:23:45 +00:00
|
|
|
free(this->cookie.ptr);
|
2010-01-25 09:44:35 +00:00
|
|
|
free(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* See header
|
|
|
|
*/
|
2010-08-23 12:22:38 +00:00
|
|
|
tls_peer_t *tls_peer_create(tls_t *tls, tls_crypto_t *crypto, tls_alert_t *alert,
|
2010-02-03 18:53:40 +00:00
|
|
|
identification_t *peer, identification_t *server)
|
2010-01-25 09:44:35 +00:00
|
|
|
{
|
|
|
|
private_tls_peer_t *this;
|
|
|
|
|
|
|
|
INIT(this,
|
2010-08-18 10:15:03 +00:00
|
|
|
.public = {
|
|
|
|
.handshake = {
|
|
|
|
.process = _process,
|
|
|
|
.build = _build,
|
|
|
|
.cipherspec_changed = _cipherspec_changed,
|
|
|
|
.change_cipherspec = _change_cipherspec,
|
|
|
|
.finished = _finished,
|
2013-02-28 10:39:55 +00:00
|
|
|
.get_peer_id = _get_peer_id,
|
|
|
|
.get_server_id = _get_server_id,
|
2015-01-29 10:13:42 +00:00
|
|
|
.get_auth = _get_auth,
|
2010-08-18 10:15:03 +00:00
|
|
|
.destroy = _destroy,
|
|
|
|
},
|
2010-01-25 09:44:35 +00:00
|
|
|
},
|
2010-01-25 11:23:59 +00:00
|
|
|
.state = STATE_INIT,
|
2010-01-25 16:31:55 +00:00
|
|
|
.tls = tls,
|
2010-01-25 11:21:57 +00:00
|
|
|
.crypto = crypto,
|
2010-08-23 12:22:38 +00:00
|
|
|
.alert = alert,
|
2013-02-28 10:39:55 +00:00
|
|
|
.peer = peer ? peer->clone(peer) : NULL,
|
|
|
|
.server = server->clone(server),
|
2010-02-03 18:53:40 +00:00
|
|
|
.peer_auth = auth_cfg_create(),
|
|
|
|
.server_auth = auth_cfg_create(),
|
2010-01-25 09:44:35 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
return &this->public;
|
|
|
|
}
|