]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
Support different hash/sig algorithms in handshake signing, including ECDSA
authorMartin Willi <martin@revosec.ch>
Thu, 2 Sep 2010 08:29:32 +0000 (10:29 +0200)
committerMartin Willi <martin@revosec.ch>
Thu, 2 Sep 2010 11:07:25 +0000 (13:07 +0200)
src/libtls/tls_crypto.c
src/libtls/tls_crypto.h
src/libtls/tls_peer.c
src/libtls/tls_server.c

index 7e5ca197d54c093f379be71e6246a13755440852..1f617956578af008a9c9a6335ea3cfd3ca179729 100644 (file)
@@ -692,36 +692,130 @@ static bool hash_handshake(private_tls_crypto_t *this, chunk_t *hash)
        return TRUE;
 }
 
-METHOD(tls_crypto_t, sign_handshake, bool,
-       private_tls_crypto_t *this, private_key_t *key, tls_writer_t *writer)
+/**
+ * Get the signature scheme from a TLS 1.2 hash/sig algorithm pair
+ */
+static signature_scheme_t hashsig_to_scheme(key_type_t type,
+                                       tls_hash_algorithm_t hash, tls_signature_algorithm_t sig)
 {
-       chunk_t sig, hash;
+       switch (sig)
+       {
+               case TLS_SIG_RSA:
+                       if (type != KEY_RSA)
+                       {
+                               return SIGN_UNKNOWN;
+                       }
+                       switch (hash)
+                       {
+                               case TLS_HASH_MD5:
+                                       return SIGN_RSA_EMSA_PKCS1_MD5;
+                               case TLS_HASH_SHA1:
+                                       return SIGN_RSA_EMSA_PKCS1_SHA1;
+                               case TLS_HASH_SHA224:
+                                       return SIGN_RSA_EMSA_PKCS1_SHA224;
+                               case TLS_HASH_SHA256:
+                                       return SIGN_RSA_EMSA_PKCS1_SHA256;
+                               case TLS_HASH_SHA384:
+                                       return SIGN_RSA_EMSA_PKCS1_SHA384;
+                               case TLS_HASH_SHA512:
+                                       return SIGN_RSA_EMSA_PKCS1_SHA512;
+                               default:
+                                       return SIGN_UNKNOWN;
+                       }
+               case TLS_SIG_ECDSA:
+                       if (type != KEY_ECDSA)
+                       {
+                               return SIGN_UNKNOWN;
+                       }
+                       switch (hash)
+                       {
+                               case TLS_HASH_SHA224:
+                                       return SIGN_ECDSA_WITH_SHA1_DER;
+                               case TLS_HASH_SHA256:
+                                       return SIGN_ECDSA_WITH_SHA256_DER;
+                               case TLS_HASH_SHA384:
+                                       return SIGN_ECDSA_WITH_SHA384_DER;
+                               case TLS_HASH_SHA512:
+                                       return SIGN_ECDSA_WITH_SHA512_DER;
+                               default:
+                                       return SIGN_UNKNOWN;
+                       }
+               default:
+                       return SIGN_UNKNOWN;
+       }
+}
 
+METHOD(tls_crypto_t, sign_handshake, bool,
+       private_tls_crypto_t *this, private_key_t *key, tls_writer_t *writer,
+       chunk_t hashsig)
+{
        if (this->tls->get_version(this->tls) >= TLS_1_2)
        {
-               /* TODO: use supported algorithms instead of fixed SHA1/RSA */
-               if (!key->sign(key, SIGN_RSA_EMSA_PKCS1_SHA1, this->handshake, &sig))
+               signature_scheme_t scheme;
+               tls_reader_t *reader;
+               u_int8_t hash, alg;
+               chunk_t sig;
+               bool done = FALSE;
+
+               reader = tls_reader_create(hashsig);
+               while (reader->remaining(reader) >= 2)
                {
+                       if (reader->read_uint8(reader, &hash) &&
+                               reader->read_uint8(reader, &alg))
+                       {
+                               scheme = hashsig_to_scheme(key->get_type(key), hash, alg);
+                               if (scheme != SIGN_UNKNOWN &&
+                                       key->sign(key, scheme, this->handshake, &sig))
+                               {
+                                       done = TRUE;
+                                       break;
+                               }
+                       }
+               }
+               reader->destroy(reader);
+               if (!done)
+               {
+                       DBG1(DBG_TLS, "none of the proposed hash/sig algorithms supported");
                        return FALSE;
                }
-               writer->write_uint8(writer, 2);
-               writer->write_uint8(writer, 1);
+               DBG2(DBG_TLS, "signed handshake data with %N/%N",
+                        tls_hash_algorithm_names, hash, tls_signature_algorithm_names, alg);
+               writer->write_uint8(writer, hash);
+               writer->write_uint8(writer, alg);
                writer->write_data16(writer, sig);
                free(sig.ptr);
        }
        else
        {
-               if (!hash_handshake(this, &hash))
-               {
-                       return FALSE;
-               }
-               if (!key->sign(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, &sig))
+               chunk_t sig, hash;
+
+               switch (key->get_type(key))
                {
-                       free(hash.ptr);
-                       return FALSE;
+                       case KEY_RSA:
+                               if (!hash_handshake(this, &hash))
+                               {
+                                       return FALSE;
+                               }
+                               if (!key->sign(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, &sig))
+                               {
+                                       free(hash.ptr);
+                                       return FALSE;
+                               }
+                               DBG2(DBG_TLS, "signed handshake data with SHA1+MD5/RSA");
+                               free(hash.ptr);
+                               break;
+                       case KEY_ECDSA:
+                               if (!key->sign(key, SIGN_ECDSA_WITH_SHA1_DER,
+                                                          this->handshake, &sig))
+                               {
+                                       return FALSE;
+                               }
+                               DBG2(DBG_TLS, "signed handshake data with SHA1/ECDSA");
+                               break;
+                       default:
+                               return FALSE;
                }
                writer->write_data16(writer, sig);
-               free(hash.ptr);
                free(sig.ptr);
        }
        return TRUE;
@@ -732,6 +826,7 @@ METHOD(tls_crypto_t, verify_handshake, bool,
 {
        if (this->tls->get_version(this->tls) >= TLS_1_2)
        {
+               signature_scheme_t scheme = SIGN_UNKNOWN;
                u_int8_t hash, alg;
                chunk_t sig;
 
@@ -742,12 +837,20 @@ METHOD(tls_crypto_t, verify_handshake, bool,
                        DBG1(DBG_TLS, "received invalid Certificate Verify");
                        return FALSE;
                }
-               /* TODO: map received hash/sig alg to signature scheme */
-               if (hash != 2 || alg != 1 ||
-                       !key->verify(key, SIGN_RSA_EMSA_PKCS1_SHA1, this->handshake, sig))
+               scheme = hashsig_to_scheme(key->get_type(key), hash, alg);
+               if (scheme == SIGN_UNKNOWN)
                {
+                       DBG1(DBG_TLS, "Certificate Verify algorithms %N/%N not supported",
+                                tls_hash_algorithm_names, hash,
+                                tls_signature_algorithm_names, alg);
                        return FALSE;
                }
+               if (!key->verify(key, scheme, this->handshake, sig))
+               {
+                       return FALSE;
+               }
+               DBG2(DBG_TLS, "verified handshake data with %N/%N",
+                        tls_hash_algorithm_names, hash, tls_signature_algorithm_names, alg);
        }
        else
        {
@@ -758,16 +861,33 @@ METHOD(tls_crypto_t, verify_handshake, bool,
                        DBG1(DBG_TLS, "received invalid Certificate Verify");
                        return FALSE;
                }
-               if (!hash_handshake(this, &hash))
-               {
-                       return FALSE;
-               }
-               if (!key->verify(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, sig))
+               switch (key->get_type(key))
                {
-                       free(hash.ptr);
-                       return FALSE;
+                       case KEY_RSA:
+                               if (!hash_handshake(this, &hash))
+                               {
+                                       return FALSE;
+                               }
+                               if (!key->verify(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, sig))
+                               {
+                                       free(hash.ptr);
+                                       return FALSE;
+                               }
+                               DBG2(DBG_TLS, "verified handshake data with SHA1+MD5/RSA");
+                               free(hash.ptr);
+                               break;
+                       case KEY_ECDSA:
+                               if (!key->verify(key, SIGN_ECDSA_WITH_SHA1_DER,
+                                                                this->handshake, sig))
+                               {
+                                       free(hash.ptr);
+                                       return FALSE;
+                               }
+                               DBG2(DBG_TLS, "verified handshake data with SHA1/ECDSA");
+                               break;
+                       default:
+                               return FALSE;
                }
-               free(hash.ptr);
        }
        return TRUE;
 }
index 4ae456f38c6ac2102cd0e1d6e617abff0c87302b..be4005dad040cc42a2717ee1d2bd81939d3fee7c 100644 (file)
@@ -349,10 +349,11 @@ struct tls_crypto_t {
         *
         * @param key                   private key to use for signature
         * @param writer                TLS writer to write signature to
+        * @param hashsig               list of TLS1.2 hash/sig algorithms to select from
         * @return                              TRUE if signature create successfully
         */
        bool (*sign_handshake)(tls_crypto_t *this, private_key_t *key,
-                                                  tls_writer_t *writer);
+                                                  tls_writer_t *writer, chunk_t hashsig);
 
        /**
         * Verify the signature over handshake data using a given public key.
index a7b23f76d11a7dbcb15262f27f50b4912721e21e..3ee77d501063339ce1fe7a6631aa83eaf7ff9aa5 100644 (file)
@@ -102,6 +102,16 @@ struct private_tls_peer_t {
         * Peer private key
         */
        private_key_t *private;
+
+       /**
+        * List of server-supported hashsig algorithms
+        */
+       chunk_t hashsig;
+
+       /**
+        * List of server-supported client certificate types
+        */
+       chunk_t cert_types;
 };
 
 /**
@@ -283,6 +293,7 @@ static status_t process_certreq(private_tls_peer_t *this, tls_reader_t *reader)
                this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
                return NEED_MORE;
        }
+       this->cert_types = chunk_clone(types);
        if (this->tls->get_version(this->tls) >= TLS_1_2)
        {
                if (!reader->read_data16(reader, &hashsig))
@@ -291,7 +302,7 @@ static status_t process_certreq(private_tls_peer_t *this, tls_reader_t *reader)
                        this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
                        return NEED_MORE;
                }
-               /* TODO: store supported hashsig algorithms */
+               this->hashsig = chunk_clone(hashsig);
        }
        if (!reader->read_data16(reader, &data))
        {
@@ -475,6 +486,45 @@ static status_t send_client_hello(private_tls_peer_t *this,
        return NEED_MORE;
 }
 
+/**
+ * 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;
+       tls_reader_t *reader;
+       key_type_t type;
+       u_int8_t cert;
+
+       if (!this->peer)
+       {
+               return NULL;
+       }
+       reader = tls_reader_create(this->cert_types);
+       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;
+}
+
 /**
  * Send Certificate
  */
@@ -487,11 +537,7 @@ static status_t send_certificate(private_tls_peer_t *this,
        tls_writer_t *certs;
        chunk_t data;
 
-       if (this->peer)
-       {
-               this->private = lib->credmgr->get_private(lib->credmgr,
-                                                                               KEY_ANY, this->peer, this->peer_auth);
-       }
+       this->private = find_private_key(this);
        if (!this->private)
        {
                DBG1(DBG_TLS, "no TLS peer certificate found for '%Y'", this->peer);
@@ -611,7 +657,8 @@ static status_t send_certificate_verify(private_tls_peer_t *this,
                                                        tls_handshake_type_t *type, tls_writer_t *writer)
 {
        if (!this->private ||
-               !this->crypto->sign_handshake(this->crypto, this->private, writer))
+               !this->crypto->sign_handshake(this->crypto, this->private,
+                                                                         writer, this->hashsig))
        {
                DBG1(DBG_TLS, "creating TLS Certificate Verify signature failed");
                this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
@@ -715,6 +762,8 @@ METHOD(tls_handshake_t, destroy, void,
        DESTROY_IF(this->private);
        this->peer_auth->destroy(this->peer_auth);
        this->server_auth->destroy(this->server_auth);
+       free(this->hashsig.ptr);
+       free(this->cert_types.ptr);
        free(this);
 }
 
index 47e12a854f096e0837aec6eb8a5afe2d4d9579fa..e8b96d9f005e99434a7af235feef9c0df6ac405f 100644 (file)
@@ -499,6 +499,69 @@ static status_t send_certificate(private_tls_server_t *this,
        return NEED_MORE;
 }
 
+/**
+ * Create a list of supported certificate types and hash/sig algorithms
+ */
+static void get_supported_algorithms(private_tls_server_t *this,
+                                                                        tls_writer_t *writer)
+{
+       tls_writer_t *supported;
+
+       supported = tls_writer_create(4);
+       /* we propose both RSA and ECDSA */
+       supported->write_uint8(supported, TLS_RSA_SIGN);
+       supported->write_uint8(supported, TLS_ECDSA_SIGN);
+       writer->write_data8(writer, supported->get_buf(supported));
+       supported->destroy(supported);
+
+       if (this->tls->get_version(this->tls) >= TLS_1_2)
+       {
+               enumerator_t *enumerator;
+               hash_algorithm_t alg;
+               tls_hash_algorithm_t hash;
+
+               supported = tls_writer_create(32);
+               enumerator = lib->crypto->create_hasher_enumerator(lib->crypto);
+               while (enumerator->enumerate(enumerator, &alg))
+               {
+                       switch (alg)
+                       {
+                               case HASH_MD5:
+                                       hash = TLS_HASH_MD5;
+                                       break;
+                               case HASH_SHA1:
+                                       hash = TLS_HASH_SHA1;
+                                       break;
+                               case HASH_SHA224:
+                                       hash = TLS_HASH_SHA224;
+                                       break;
+                               case HASH_SHA256:
+                                       hash = TLS_HASH_SHA256;
+                                       break;
+                               case HASH_SHA384:
+                                       hash = TLS_HASH_SHA384;
+                                       break;
+                               case HASH_SHA512:
+                                       hash = TLS_HASH_SHA512;
+                                       break;
+                               default:
+                                       continue;
+                       }
+                       supported->write_uint8(supported, hash);
+                       supported->write_uint8(supported, TLS_SIG_RSA);
+                       if (alg != HASH_MD5 && alg != HASH_SHA224)
+                       {
+                               supported->write_uint8(supported, hash);
+                               supported->write_uint8(supported, TLS_SIG_ECDSA);
+                       }
+               }
+               enumerator->destroy(enumerator);
+
+               writer->write_data16(writer, supported->get_buf(supported));
+               supported->destroy(supported);
+       }
+}
+
 /**
  * Send Certificate Request
  */
@@ -511,13 +574,7 @@ static status_t send_certificate_request(private_tls_server_t *this,
        x509_t *x509;
        identification_t *id;
 
-       /* currently only RSA signatures are supported */
-       writer->write_data8(writer, chunk_from_chars(1));
-       if (this->tls->get_version(this->tls) >= TLS_1_2)
-       {
-               /* enforce RSA with SHA1 signatures */
-               writer->write_data16(writer, chunk_from_chars(2, 1));
-       }
+       get_supported_algorithms(this, writer);
 
        authorities = tls_writer_create(64);
        enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,