tls-crypto: Share private key search between client and server
This way the client also properly considers the TLS version and the signature schemes supported by the server. Co-authored-by: Tobias Brunner <tobias@strongswan.org>
This commit is contained in:
parent
299cc80094
commit
d8e42a3d4e
|
@ -24,6 +24,7 @@
|
||||||
#include <utils/debug.h>
|
#include <utils/debug.h>
|
||||||
#include <plugins/plugin_feature.h>
|
#include <plugins/plugin_feature.h>
|
||||||
#include <collections/hashtable.h>
|
#include <collections/hashtable.h>
|
||||||
|
#include <collections/array.h>
|
||||||
|
|
||||||
ENUM_BEGIN(tls_cipher_suite_names, TLS_NULL_WITH_NULL_NULL,
|
ENUM_BEGIN(tls_cipher_suite_names, TLS_NULL_WITH_NULL_NULL,
|
||||||
TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
|
TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
@ -2558,10 +2559,10 @@ CALLBACK(destroy_key_types, void,
|
||||||
ht->destroy_function(ht, (void*)free);
|
ht->destroy_function(ht, (void*)free);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* See header.
|
* Create an enumerator over supported key types within a specific TLS range
|
||||||
*/
|
*/
|
||||||
enumerator_t *tls_get_supported_key_types(tls_version_t min_version,
|
static enumerator_t *get_supported_key_types(tls_version_t min_version,
|
||||||
tls_version_t max_version)
|
tls_version_t max_version)
|
||||||
{
|
{
|
||||||
hashtable_t *ht;
|
hashtable_t *ht;
|
||||||
|
@ -2587,3 +2588,111 @@ enumerator_t *tls_get_supported_key_types(tls_version_t min_version,
|
||||||
return enumerator_create_filter(ht->create_enumerator(ht),
|
return enumerator_create_filter(ht->create_enumerator(ht),
|
||||||
filter_key_types, ht, destroy_key_types);
|
filter_key_types, ht, destroy_key_types);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an array of an intersection of server and peer supported key types
|
||||||
|
*/
|
||||||
|
static array_t *create_common_key_types(enumerator_t *enumerator, chunk_t hashsig)
|
||||||
|
{
|
||||||
|
array_t *key_types;
|
||||||
|
key_type_t v, lookup;
|
||||||
|
uint16_t sig_scheme;
|
||||||
|
|
||||||
|
key_types = array_create(sizeof(key_type_t), 8);
|
||||||
|
while (enumerator->enumerate(enumerator, &v))
|
||||||
|
{
|
||||||
|
bio_reader_t *reader;
|
||||||
|
|
||||||
|
reader = bio_reader_create(hashsig);
|
||||||
|
while (reader->remaining(reader) &&
|
||||||
|
reader->read_uint16(reader, &sig_scheme))
|
||||||
|
{
|
||||||
|
lookup = tls_signature_scheme_to_key_type(sig_scheme);
|
||||||
|
if (v == lookup)
|
||||||
|
{
|
||||||
|
array_insert(key_types, ARRAY_TAIL, &lookup);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reader->destroy(reader);
|
||||||
|
}
|
||||||
|
return key_types;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
enumerator_t public;
|
||||||
|
array_t *key_types;
|
||||||
|
identification_t *peer;
|
||||||
|
private_key_t *key;
|
||||||
|
auth_cfg_t *auth;
|
||||||
|
} private_key_enumerator_t;
|
||||||
|
|
||||||
|
METHOD(enumerator_t, private_key_enumerate, bool,
|
||||||
|
private_key_enumerator_t *this, va_list args)
|
||||||
|
{
|
||||||
|
key_type_t type;
|
||||||
|
auth_cfg_t **auth_out;
|
||||||
|
private_key_t **key_out;
|
||||||
|
|
||||||
|
VA_ARGS_VGET(args, key_out, auth_out);
|
||||||
|
|
||||||
|
DESTROY_IF(this->key);
|
||||||
|
DESTROY_IF(this->auth);
|
||||||
|
this->auth = auth_cfg_create();
|
||||||
|
|
||||||
|
while (array_remove(this->key_types, ARRAY_HEAD, &type))
|
||||||
|
{
|
||||||
|
this->key = lib->credmgr->get_private(lib->credmgr, type, this->peer,
|
||||||
|
this->auth);
|
||||||
|
if (this->key)
|
||||||
|
{
|
||||||
|
*key_out = this->key;
|
||||||
|
if (auth_out)
|
||||||
|
{
|
||||||
|
*auth_out = this->auth;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
METHOD(enumerator_t, private_key_destroy, void,
|
||||||
|
private_key_enumerator_t *this)
|
||||||
|
{
|
||||||
|
DESTROY_IF(this->key);
|
||||||
|
DESTROY_IF(this->auth);
|
||||||
|
array_destroy(this->key_types);
|
||||||
|
free(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See header.
|
||||||
|
*/
|
||||||
|
enumerator_t *tls_create_private_key_enumerator(tls_version_t min_version,
|
||||||
|
tls_version_t max_version,
|
||||||
|
chunk_t hashsig,
|
||||||
|
identification_t *peer)
|
||||||
|
{
|
||||||
|
private_key_enumerator_t *enumerator;
|
||||||
|
enumerator_t *key_types;
|
||||||
|
|
||||||
|
key_types = get_supported_key_types(min_version, max_version);
|
||||||
|
|
||||||
|
INIT(enumerator,
|
||||||
|
.public = {
|
||||||
|
.enumerate = enumerator_enumerate_default,
|
||||||
|
.venumerate = _private_key_enumerate,
|
||||||
|
.destroy = _private_key_destroy,
|
||||||
|
},
|
||||||
|
.key_types = create_common_key_types(key_types, hashsig),
|
||||||
|
.peer = peer,
|
||||||
|
);
|
||||||
|
key_types->destroy(key_types);
|
||||||
|
|
||||||
|
if (!array_count(enumerator->key_types))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return &enumerator->public;
|
||||||
|
}
|
||||||
|
|
|
@ -700,15 +700,18 @@ tls_named_group_t tls_ec_group_to_curve(diffie_hellman_group_t group);
|
||||||
key_type_t tls_signature_scheme_to_key_type(tls_signature_scheme_t sig);
|
key_type_t tls_signature_scheme_to_key_type(tls_signature_scheme_t sig);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an enumerator over supported key types within a specific TLS version range
|
* Find a private key to encrypt/verify key exchange data
|
||||||
*
|
|
||||||
* Enumerates over key_type_t
|
|
||||||
*
|
*
|
||||||
* @param min_version minimum negotiated TLS version
|
* @param min_version minimum negotiated TLS version
|
||||||
* @param max_version maximum negotiated TLS version
|
* @param max_version maximum negotiated TLS version
|
||||||
* @return hashtable of key types
|
* @param hashsig hash and signature algorithms supported by other peer
|
||||||
|
* @param peer this peer identification
|
||||||
|
* @return enumerator over private keys,
|
||||||
|
* NULL in case no common signature scheme
|
||||||
*/
|
*/
|
||||||
enumerator_t *tls_get_supported_key_types(tls_version_t min_version,
|
enumerator_t *tls_create_private_key_enumerator(tls_version_t min_version,
|
||||||
tls_version_t max_version);
|
tls_version_t max_version,
|
||||||
|
chunk_t hashsig,
|
||||||
|
identification_t *peer);
|
||||||
|
|
||||||
#endif /** TLS_CRYPTO_H_ @}*/
|
#endif /** TLS_CRYPTO_H_ @}*/
|
||||||
|
|
|
@ -1398,13 +1398,37 @@ static status_t send_certificate(private_tls_peer_t *this,
|
||||||
certificate_t *cert;
|
certificate_t *cert;
|
||||||
auth_rule_t rule;
|
auth_rule_t rule;
|
||||||
bio_writer_t *certs;
|
bio_writer_t *certs;
|
||||||
|
private_key_t *key;
|
||||||
|
auth_cfg_t *auth;
|
||||||
chunk_t data;
|
chunk_t data;
|
||||||
|
tls_version_t version_min, version_max;
|
||||||
|
|
||||||
this->private = find_private_key(this);
|
version_min = this->tls->get_version_min(this->tls);
|
||||||
|
version_max = this->tls->get_version_max(this->tls);
|
||||||
|
if (version_max > TLS_1_1)
|
||||||
|
{
|
||||||
|
enumerator = tls_create_private_key_enumerator(version_min, version_max,
|
||||||
|
this->hashsig, this->peer);
|
||||||
|
if (!enumerator)
|
||||||
|
{
|
||||||
|
DBG1(DBG_TLS, "no common signature algorithms found");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (enumerator->enumerate(enumerator, &key, &auth))
|
||||||
|
{
|
||||||
|
this->private = key->get_ref(key);
|
||||||
|
this->peer_auth->merge(this->peer_auth, auth, FALSE);
|
||||||
|
}
|
||||||
|
enumerator->destroy(enumerator);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->private = find_private_key(this);
|
||||||
|
}
|
||||||
if (!this->private)
|
if (!this->private)
|
||||||
{
|
{
|
||||||
DBG1(DBG_TLS, "no TLS peer certificate found for '%Y', "
|
DBG1(DBG_TLS, "no usable TLS client certificate found for '%Y'",
|
||||||
"skipping client authentication", this->peer);
|
this->peer);
|
||||||
this->peer->destroy(this->peer);
|
this->peer->destroy(this->peer);
|
||||||
this->peer = NULL;
|
this->peer = NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -196,75 +196,31 @@ public_key_t *tls_find_public_key(auth_cfg_t *peer_auth)
|
||||||
return public;
|
return public;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an array of an intersection of server and peer supported key types
|
|
||||||
*/
|
|
||||||
static array_t *create_common_key_types(chunk_t hashsig,
|
|
||||||
tls_version_t version_min,
|
|
||||||
tls_version_t version_max)
|
|
||||||
{
|
|
||||||
array_t *key_types;
|
|
||||||
enumerator_t *enumerator;
|
|
||||||
key_type_t v, lookup;
|
|
||||||
uint16_t sig_scheme;
|
|
||||||
|
|
||||||
key_types = array_create(sizeof(key_type_t), 8);
|
|
||||||
enumerator = tls_get_supported_key_types(version_min, version_max);
|
|
||||||
while (enumerator->enumerate(enumerator, &v))
|
|
||||||
{
|
|
||||||
bio_reader_t *reader;
|
|
||||||
|
|
||||||
reader = bio_reader_create(hashsig);
|
|
||||||
while (reader->remaining(reader) &&
|
|
||||||
reader->read_uint16(reader, &sig_scheme))
|
|
||||||
{
|
|
||||||
lookup = tls_signature_scheme_to_key_type(sig_scheme);
|
|
||||||
if (v == lookup)
|
|
||||||
{
|
|
||||||
array_insert(key_types, ARRAY_TAIL, &lookup);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reader->destroy(reader);
|
|
||||||
}
|
|
||||||
enumerator->destroy(enumerator);
|
|
||||||
return key_types;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a cipher suite and a server key
|
* Find a cipher suite and a server key
|
||||||
*/
|
*/
|
||||||
static bool select_suite_and_key(private_tls_server_t *this,
|
static bool select_suite_and_key(private_tls_server_t *this,
|
||||||
tls_cipher_suite_t *suites, int count)
|
tls_cipher_suite_t *suites, int count)
|
||||||
{
|
{
|
||||||
array_t *key_types;
|
|
||||||
tls_version_t version_min, version_max;
|
tls_version_t version_min, version_max;
|
||||||
private_key_t *key;
|
private_key_t *key;
|
||||||
key_type_t type;
|
auth_cfg_t *auth;
|
||||||
|
enumerator_t *enumerator;
|
||||||
|
|
||||||
version_min = this->tls->get_version_min(this->tls);
|
version_min = this->tls->get_version_min(this->tls);
|
||||||
version_max = this->tls->get_version_max(this->tls);
|
version_max = this->tls->get_version_max(this->tls);
|
||||||
key_types = create_common_key_types(this->hashsig, version_min, version_max);
|
enumerator = tls_create_private_key_enumerator(version_min, version_max,
|
||||||
if (!array_count(key_types))
|
this->hashsig, this->server);
|
||||||
|
if (!enumerator)
|
||||||
{
|
{
|
||||||
DBG1(DBG_TLS, "no common signature algorithms found");
|
DBG1(DBG_TLS, "no common signature algorithms found");
|
||||||
array_destroy(key_types);
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
while (array_remove(key_types, ARRAY_HEAD, &type))
|
if (!enumerator->enumerate(enumerator, &key, &auth))
|
||||||
{
|
|
||||||
key = lib->credmgr->get_private(lib->credmgr, type, this->server,
|
|
||||||
this->server_auth);
|
|
||||||
if (key)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!key)
|
|
||||||
{
|
{
|
||||||
DBG1(DBG_TLS, "no usable TLS server certificate found for '%Y'",
|
DBG1(DBG_TLS, "no usable TLS server certificate found for '%Y'",
|
||||||
this->server);
|
this->server);
|
||||||
array_destroy(key_types);
|
enumerator->destroy(enumerator);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,30 +232,25 @@ static bool select_suite_and_key(private_tls_server_t *this,
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this->suite = this->crypto->select_cipher_suite(this->crypto, suites,
|
this->suite = this->crypto->select_cipher_suite(this->crypto, suites,
|
||||||
count, type);
|
count, key->get_type(key));
|
||||||
while (!this->suite && array_remove(key_types, ARRAY_HEAD, &type))
|
while (!this->suite &&
|
||||||
|
enumerator->enumerate(enumerator, &key, &auth))
|
||||||
{ /* find a key and cipher suite for one of the remaining key types */
|
{ /* find a key and cipher suite for one of the remaining key types */
|
||||||
DESTROY_IF(key);
|
this->suite = this->crypto->select_cipher_suite(this->crypto,
|
||||||
this->server_auth->destroy(this->server_auth);
|
suites, count,
|
||||||
this->server_auth = auth_cfg_create();
|
key->get_type(key));
|
||||||
key = lib->credmgr->get_private(lib->credmgr, type, this->server,
|
|
||||||
this->server_auth);
|
|
||||||
if (key)
|
|
||||||
{
|
|
||||||
this->suite = this->crypto->select_cipher_suite(this->crypto,
|
|
||||||
suites, count,
|
|
||||||
type);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
array_destroy(key_types);
|
if (!this->suite)
|
||||||
if (!this->suite || !key)
|
|
||||||
{
|
{
|
||||||
DBG1(DBG_TLS, "received cipher suites or signature schemes unacceptable");
|
DBG1(DBG_TLS, "received cipher suites or signature schemes unacceptable");
|
||||||
|
enumerator->destroy(enumerator);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
DBG1(DBG_TLS, "using key of type %N", key_type_names, key->get_type(key));
|
DBG1(DBG_TLS, "using key of type %N", key_type_names, key->get_type(key));
|
||||||
this->private = key;
|
this->private = key->get_ref(key);
|
||||||
|
this->server_auth->merge(this->server_auth, auth, FALSE);
|
||||||
|
enumerator->destroy(enumerator);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue