]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
Support for EC curve Hello extension, EC curve fallback
authorMartin Willi <martin@revosec.ch>
Fri, 3 Sep 2010 09:45:55 +0000 (11:45 +0200)
committerMartin Willi <martin@revosec.ch>
Fri, 3 Sep 2010 12:54:43 +0000 (14:54 +0200)
src/libtls/tls.c
src/libtls/tls.h
src/libtls/tls_crypto.c
src/libtls/tls_crypto.h
src/libtls/tls_peer.c
src/libtls/tls_server.c

index 87b81f1d1c24188b89e06d2081ed1942ab360cfd..0d40211eb77500430ee70c81bf00dea2d1f10166 100644 (file)
@@ -55,7 +55,10 @@ ENUM_NEXT(tls_handshake_type_names, TLS_FINISHED, TLS_FINISHED, TLS_CLIENT_KEY_E
        "Finished");
 ENUM_END(tls_handshake_type_names, TLS_FINISHED);
 
-ENUM(tls_extension_names, TLS_EXT_SIGNATURE_ALGORITHMS, TLS_EXT_SIGNATURE_ALGORITHMS,
+ENUM(tls_extension_names, TLS_EXT_ELLIPTIC_CURVES, TLS_EXT_SIGNATURE_ALGORITHMS,
+       "elliptic curves",
+       "ec point formats",
+       "(12)",
        "signature algorithms",
 );
 
index 2bed4cb366a411851f6f7e46f6c5f04829a05ba6..de0d922bbd76d9da4e43603c7c92256e66565fbb 100644 (file)
@@ -104,6 +104,10 @@ enum tls_purpose_t {
  * TLS Hello extension types.
  */
 enum tls_extension_t {
+       /** supported elliptic curves */
+       TLS_EXT_ELLIPTIC_CURVES = 10,
+       /** supported point formats */
+       TLS_EXT_EC_POINT_FORMATS = 11,
        /** supported signature algorithms */
        TLS_EXT_SIGNATURE_ALGORITHMS = 13,
 };
index e38bf83cfbc777714400c8fd25a6162fb2ee0cb7..d2d8885ca0ca843158747167195d35da50f0d49b 100644 (file)
@@ -757,6 +757,19 @@ METHOD(tls_crypto_t, get_signature_algorithms, void,
        supported->destroy(supported);
 }
 
+METHOD(tls_crypto_t, get_curves, void,
+       private_tls_crypto_t *this, tls_writer_t *writer)
+{
+       u_int16_t curves[] = {
+               htons(TLS_SECP256R1),
+               htons(TLS_SECP384R1),
+               htons(TLS_SECP521R1),
+               htons(TLS_SECP192R1),
+               htons(TLS_SECP224R1),
+       };
+       writer->write_data16(writer, chunk_from_thing(curves));
+}
+
 METHOD(tls_crypto_t, set_protection, void,
        private_tls_crypto_t *this, tls_protection_t *protection)
 {
@@ -1215,6 +1228,7 @@ tls_crypto_t *tls_crypto_create(tls_t *tls)
                        .select_cipher_suite = _select_cipher_suite,
                        .get_dh_group = _get_dh_group,
                        .get_signature_algorithms = _get_signature_algorithms,
+                       .get_curves = _get_curves,
                        .set_protection = _set_protection,
                        .append_handshake = _append_handshake,
                        .sign = _sign,
index a17e93fb16cd25553dce48e88e0cfeb36a264a16..24d1a23a2d7c1c1f9840d4d9a1db54b07d8679fc 100644 (file)
@@ -395,6 +395,13 @@ struct tls_crypto_t {
         */
        void (*get_signature_algorithms)(tls_crypto_t *this, tls_writer_t *writer);
 
+       /**
+        * Write the list of supported elliptic curves to writer.
+        *
+        * @param writer                writer to write elliptic curves to
+        */
+       void (*get_curves)(tls_crypto_t *this, tls_writer_t *writer);
+
        /**
         * Set the protection layer of the TLS stack to control it.
         *
index b31b41188ad2de823b160f5e401ba89ca5ec9ec1..7cfbe144c8698ce2ac8128af0f421d4144ae6c99 100644 (file)
@@ -709,10 +709,14 @@ static status_t send_client_hello(private_tls_peer_t *this,
        writer->write_uint8(writer, 1);
        writer->write_uint8(writer, 0);
 
-       /* signature algorithms extension */
        extensions = tls_writer_create(32);
+
        extensions->write_uint16(extensions, TLS_EXT_SIGNATURE_ALGORITHMS);
        this->crypto->get_signature_algorithms(this->crypto, extensions);
+
+       extensions->write_uint16(extensions, TLS_EXT_ELLIPTIC_CURVES);
+       this->crypto->get_curves(this->crypto, extensions);
+
        writer->write_data16(writer, extensions->get_buf(extensions));
        extensions->destroy(extensions);
 
index 48ab58b9b9f859161c456428aeb577122a4a301c..1c1c962f16a5b132ccd1f1cb1b14b96f166c3a1c 100644 (file)
@@ -124,6 +124,16 @@ struct private_tls_server_t {
         * Hash and signature algorithms supported by peer
         */
        chunk_t hashsig;
+
+       /**
+        * Elliptic curves supported by peer
+        */
+       chunk_t curves;
+
+       /**
+        * Did we receive the curves from the client?
+        */
+       bool curves_received;
 };
 
 /**
@@ -175,6 +185,13 @@ static status_t process_client_hello(private_tls_server_t *this,
                                                this->hashsig = chunk_clone(ext);
                                        }
                                        break;
+                               case TLS_EXT_ELLIPTIC_CURVES:
+                                       this->curves_received = TRUE;
+                                       if (extensions->read_data16(extensions, &ext))
+                                       {
+                                               this->curves = chunk_clone(ext);
+                                       }
+                                       break;
                                default:
                                        break;
                        }
@@ -679,6 +696,81 @@ static status_t send_certificate_request(private_tls_server_t *this,
        return NEED_MORE;
 }
 
+/**
+ * Get the TLS curve of a given EC DH group
+ */
+static tls_named_curve_t ec_group_to_curve(diffie_hellman_group_t group)
+{
+       switch (group)
+       {
+               case ECP_256_BIT:
+                       return TLS_SECP256R1;
+               case ECP_384_BIT:
+                       return TLS_SECP384R1;
+               case ECP_521_BIT:
+                       return TLS_SECP521R1;
+               case ECP_192_BIT:
+                       return TLS_SECP192R1;
+               case ECP_224_BIT:
+                       return TLS_SECP224R1;
+               default:
+                       return 0;
+       }
+}
+
+/**
+ * Check if the peer supports a given TLS EC group
+ */
+bool peer_supports_ec_group(private_tls_server_t *this,
+                                                       diffie_hellman_group_t group)
+{
+       tls_reader_t *reader;
+       u_int16_t curve, current;
+
+       if (!this->curves_received)
+       {       /* none received, assume yes */
+               return TRUE;
+       }
+       curve = ec_group_to_curve(group);
+       reader = tls_reader_create(this->curves);
+       while (reader->remaining(reader) && reader->read_uint16(reader, &current))
+       {
+               if (current == curve)
+               {
+                       reader->destroy(reader);
+                       return TRUE;
+               }
+       }
+       reader->destroy(reader);
+       return FALSE;
+}
+
+/**
+ * Try to find a group supported by both, client and server
+ */
+static bool find_supported_group(private_tls_server_t *this,
+                                                       diffie_hellman_group_t *group)
+{
+       diffie_hellman_group_t groups[] = {
+               ECP_256_BIT,
+               ECP_384_BIT,
+               ECP_521_BIT,
+               ECP_224_BIT,
+               ECP_192_BIT,
+       };
+       int i;
+
+       for (i = 0; i < countof(groups); i++)
+       {
+               if (peer_supports_ec_group(this, groups[i]))
+               {
+                       *group = groups[i];
+                       return TRUE;
+               }
+       }
+       return FALSE;
+}
+
 /**
  * Send Server key Exchange
  */
@@ -689,35 +781,22 @@ static status_t send_server_key_exchange(private_tls_server_t *this,
        diffie_hellman_params_t *params = NULL;
        chunk_t chunk;
 
-       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;
-       }
        switch (group)
        {
                case ECP_256_BIT:
-                       writer->write_uint8(writer, TLS_ECC_NAMED_CURVE);
-                       writer->write_uint16(writer, TLS_SECP256R1);
-                       break;
                case ECP_384_BIT:
-                       writer->write_uint8(writer, TLS_ECC_NAMED_CURVE);
-                       writer->write_uint16(writer, TLS_SECP384R1);
-                       break;
                case ECP_521_BIT:
-                       writer->write_uint8(writer, TLS_ECC_NAMED_CURVE);
-                       writer->write_uint16(writer, TLS_SECP521R1);
-                       break;
                case ECP_192_BIT:
-                       writer->write_uint8(writer, TLS_ECC_NAMED_CURVE);
-                       writer->write_uint16(writer, TLS_SECP192R1);
-                       break;
                case ECP_224_BIT:
+                       if (!peer_supports_ec_group(this, group) &&
+                               !find_supported_group(this, &group))
+                       {
+                               DBG1(DBG_TLS, "no EC group supported by client and server");
+                               this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE);
+                               return NEED_MORE;
+                       }
                        writer->write_uint8(writer, TLS_ECC_NAMED_CURVE);
-                       writer->write_uint16(writer, TLS_SECP224R1);
+                       writer->write_uint16(writer, ec_group_to_curve(group));
                        break;
                default:
                        /* MODP groups */
@@ -733,6 +812,14 @@ static status_t send_server_key_exchange(private_tls_server_t *this,
                        writer->write_data16(writer, params->generator);
                        break;
        }
+       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;
+       }
        this->dh->get_my_public_value(this->dh, &chunk);
        if (params)
        {
@@ -872,6 +959,7 @@ METHOD(tls_handshake_t, destroy, void,
        this->peer_auth->destroy(this->peer_auth);
        this->server_auth->destroy(this->server_auth);
        free(this->hashsig.ptr);
+       free(this->curves.ptr);
        free(this);
 }