tls-crypto: Share private key search between client and server
authorPascal Knecht <pascal.knecht@hsr.ch>
Tue, 10 Nov 2020 09:22:12 +0000 (10:22 +0100)
committerTobias Brunner <tobias@strongswan.org>
Fri, 12 Feb 2021 13:35:23 +0000 (14:35 +0100)
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>
src/libtls/tls_crypto.c
src/libtls/tls_crypto.h
src/libtls/tls_peer.c
src/libtls/tls_server.c

index d34a8ec..1ce2a03 100644 (file)
@@ -24,6 +24,7 @@
 #include <utils/debug.h>
 #include <plugins/plugin_feature.h>
 #include <collections/hashtable.h>
+#include <collections/array.h>
 
 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;
+}
index 0e9d454..3bd75b6 100644 (file)
@@ -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_ @}*/
index ec6b64a..3c107d5 100644 (file)
@@ -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;
        }
index 24b62fb..50051c1 100644 (file)
@@ -197,74 +197,30 @@ public_key_t *tls_find_public_key(auth_cfg_t *peer_auth)
 }
 
 /**
- * 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;
 }