From: Pascal Knecht Date: Tue, 10 Nov 2020 09:22:12 +0000 (+0100) Subject: tls-crypto: Share private key search between client and server X-Git-Tag: 5.9.2rc1~23^2~26 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d8e42a3d4e3ca24647da8f06c38c5ddc139f1340;p=thirdparty%2Fstrongswan.git 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 --- diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index d34a8ecee3..1ce2a03e6a 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -24,6 +24,7 @@ #include #include #include +#include ENUM_BEGIN(tls_cipher_suite_names, TLS_NULL_WITH_NULL_NULL, TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, @@ -2558,10 +2559,10 @@ CALLBACK(destroy_key_types, void, 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) { 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), 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; +} diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h index 0e9d454f73..3bd75b64ee 100644 --- a/src/libtls/tls_crypto.h +++ b/src/libtls/tls_crypto.h @@ -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); /** - * Create an enumerator over supported key types within a specific TLS version range - * - * Enumerates over key_type_t + * Find a private key to encrypt/verify key exchange data * * @param min_version minimum 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, - tls_version_t max_version); +enumerator_t *tls_create_private_key_enumerator(tls_version_t min_version, + tls_version_t max_version, + chunk_t hashsig, + identification_t *peer); #endif /** TLS_CRYPTO_H_ @}*/ diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index ec6b64a551..3c107d58ca 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -1398,13 +1398,37 @@ static status_t send_certificate(private_tls_peer_t *this, certificate_t *cert; auth_rule_t rule; bio_writer_t *certs; + private_key_t *key; + auth_cfg_t *auth; 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) { - DBG1(DBG_TLS, "no TLS peer certificate found for '%Y', " - "skipping client authentication", this->peer); + DBG1(DBG_TLS, "no usable TLS client certificate found for '%Y'", + this->peer); this->peer->destroy(this->peer); this->peer = NULL; } diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 24b62fb906..50051c143f 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -196,75 +196,31 @@ public_key_t *tls_find_public_key(auth_cfg_t *peer_auth) 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 */ static bool select_suite_and_key(private_tls_server_t *this, tls_cipher_suite_t *suites, int count) { - array_t *key_types; tls_version_t version_min, version_max; 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_max = this->tls->get_version_max(this->tls); - key_types = create_common_key_types(this->hashsig, version_min, version_max); - if (!array_count(key_types)) + enumerator = tls_create_private_key_enumerator(version_min, version_max, + this->hashsig, this->server); + if (!enumerator) { DBG1(DBG_TLS, "no common signature algorithms found"); - array_destroy(key_types); return FALSE; } - while (array_remove(key_types, ARRAY_HEAD, &type)) - { - key = lib->credmgr->get_private(lib->credmgr, type, this->server, - this->server_auth); - if (key) - { - break; - } - } - if (!key) + if (!enumerator->enumerate(enumerator, &key, &auth)) { DBG1(DBG_TLS, "no usable TLS server certificate found for '%Y'", this->server); - array_destroy(key_types); + enumerator->destroy(enumerator); return FALSE; } @@ -276,30 +232,25 @@ static bool select_suite_and_key(private_tls_server_t *this, else { this->suite = this->crypto->select_cipher_suite(this->crypto, suites, - count, type); - while (!this->suite && array_remove(key_types, ARRAY_HEAD, &type)) + count, key->get_type(key)); + while (!this->suite && + enumerator->enumerate(enumerator, &key, &auth)) { /* find a key and cipher suite for one of the remaining key types */ - DESTROY_IF(key); - this->server_auth->destroy(this->server_auth); - this->server_auth = auth_cfg_create(); - 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); - } + this->suite = this->crypto->select_cipher_suite(this->crypto, + suites, count, + key->get_type(key)); } } - array_destroy(key_types); - if (!this->suite || !key) + if (!this->suite) { DBG1(DBG_TLS, "received cipher suites or signature schemes unacceptable"); + enumerator->destroy(enumerator); return FALSE; } 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; }