]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
openssl: Mainly use EVP interface for ECDH
authorTobias Brunner <tobias@strongswan.org>
Tue, 1 Dec 2020 10:45:05 +0000 (11:45 +0100)
committerTobias Brunner <tobias@strongswan.org>
Wed, 20 Jan 2021 16:53:35 +0000 (17:53 +0100)
Functions like ECDH_compute_key() will be removed with OpenSSL 3 (which
will require additional changes as other functions will be deprecated or
removed too).

src/libstrongswan/plugins/openssl/openssl_ec_diffie_hellman.c

index 6f58c2ceb027c7169eaacc6a8733b4a1915c9d1c..180eb0ab2957721eabdb4ae115fad3d90b59e5fd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2013 Tobias Brunner
+ * Copyright (C) 2008-2021 Tobias Brunner
  * HSR Hochschule fuer Technik Rapperswil
  *
  * This program is free software; you can redistribute it and/or modify it
 
 #ifndef OPENSSL_NO_EC
 
-#include <openssl/bn.h>
+#include <openssl/evp.h>
 #include <openssl/ec.h>
 #include <openssl/objects.h>
+
+#if OPENSSL_VERSION_NUMBER < 0x1010000fL || defined(OPENSSL_IS_BORINGSSL)
 #include <openssl/bn.h>
+#endif
 
 #include "openssl_ec_diffie_hellman.h"
 #include "openssl_util.h"
@@ -46,17 +49,12 @@ struct private_openssl_ec_diffie_hellman_t {
        /**
         * EC private (public) key
         */
-       EC_KEY *key;
+       EVP_PKEY *key;
 
        /**
         * EC group
         */
-       const EC_GROUP *ec_group;
-
-       /**
-        * Other public key
-        */
-       EC_POINT *pub_key;
+       EC_GROUP *ec_group;
 
        /**
         * Shared secret
@@ -69,12 +67,15 @@ struct private_openssl_ec_diffie_hellman_t {
        bool computed;
 };
 
+#if OPENSSL_VERSION_NUMBER < 0x1010000fL || defined(OPENSSL_IS_BORINGSSL)
 /**
- * Convert a chunk to an EC_POINT (which must already exist). The x and y
+ * Convert a chunk to an EC_POINT and set it on the given key. The x and y
  * coordinates of the point have to be concatenated in the chunk.
  */
-static bool chunk2ecp(const EC_GROUP *group, chunk_t chunk, EC_POINT *point)
+static bool chunk2ecp(const EC_GROUP *group, chunk_t chunk, EVP_PKEY *key)
 {
+       EC_POINT *point = NULL;
+       EC_KEY *pub = NULL;
        BN_CTX *ctx;
        BIGNUM *x, *y;
        bool ret = FALSE;
@@ -98,7 +99,8 @@ static bool chunk2ecp(const EC_GROUP *group, chunk_t chunk, EC_POINT *point)
                goto error;
        }
 
-       if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx))
+       point = EC_POINT_new(group);
+       if (!point || !EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx))
        {
                goto error;
        }
@@ -108,20 +110,40 @@ static bool chunk2ecp(const EC_GROUP *group, chunk_t chunk, EC_POINT *point)
                goto error;
        }
 
+       pub = EC_KEY_new();
+       if (!pub || !EC_KEY_set_group(pub, group))
+       {
+               goto error;
+       }
+
+       if (EC_KEY_set_public_key(pub, point) != 1)
+       {
+               goto error;
+       }
+
+       if (EVP_PKEY_set1_EC_KEY(key, pub) != 1)
+       {
+               goto error;
+       }
+
        ret = TRUE;
+
 error:
+       EC_POINT_clear_free(point);
+       EC_KEY_free(pub);
        BN_CTX_end(ctx);
        BN_CTX_free(ctx);
        return ret;
 }
 
 /**
- * Convert an EC_POINT to a chunk by concatenating the x and y coordinates of
- * the point. This function allocates memory for the chunk.
+ * Convert a key to a chunk by concatenating the x and y coordinates of
+ * the underlying EC point. This function allocates memory for the chunk.
  */
-static bool ecp2chunk(const EC_GROUP *group, const EC_POINT *point,
-                                         chunk_t *chunk)
+static bool ecp2chunk(const EC_GROUP *group, EVP_PKEY *key, chunk_t *chunk)
 {
+       EC_KEY *ec_key = NULL;
+       const EC_POINT *point;
        BN_CTX *ctx;
        BIGNUM *x, *y;
        bool ret = FALSE;
@@ -140,7 +162,9 @@ static bool ecp2chunk(const EC_GROUP *group, const EC_POINT *point,
                goto error;
        }
 
-       if (!EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx))
+       ec_key = EVP_PKEY_get1_EC_KEY(key);
+       point = EC_KEY_get0_public_key(ec_key);
+       if (!point || !EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx))
        {
                goto error;
        }
@@ -150,68 +174,89 @@ static bool ecp2chunk(const EC_GROUP *group, const EC_POINT *point,
                goto error;
        }
 
-       ret = TRUE;
+       ret = chunk->len != 0;
 error:
+       EC_KEY_free(ec_key);
        BN_CTX_end(ctx);
        BN_CTX_free(ctx);
        return ret;
 }
+#endif /* OPENSSL_VERSION_NUMBER < ... */
 
-/**
- * Compute the shared secret.
- */
-static bool compute_shared_key(private_openssl_ec_diffie_hellman_t *this,
-                                                          chunk_t *shared_secret)
+METHOD(diffie_hellman_t, set_other_public_value, bool,
+       private_openssl_ec_diffie_hellman_t *this, chunk_t value)
 {
-       int len;
+       EVP_PKEY *pub = NULL;
+
+       chunk_clear(&this->shared_secret);
+       this->computed = FALSE;
 
-       *shared_secret = chunk_alloc(EC_FIELD_ELEMENT_LEN(this->ec_group));
-       len = ECDH_compute_key(shared_secret->ptr, shared_secret->len,
-                                                  this->pub_key, this->key, NULL);
-       if (len <= 0)
+       if (!diffie_hellman_verify_value(this->group, value))
        {
-               chunk_free(shared_secret);
                return FALSE;
        }
-       shared_secret->len = len;
-       return TRUE;
-}
 
-METHOD(diffie_hellman_t, set_other_public_value, bool,
-       private_openssl_ec_diffie_hellman_t *this, chunk_t value)
-{
-       if (!diffie_hellman_verify_value(this->group, value))
+       pub = EVP_PKEY_new();
+       if (!pub)
        {
-               return FALSE;
+               goto error;
        }
 
-       if (!chunk2ecp(this->ec_group, value, this->pub_key))
+#if OPENSSL_VERSION_NUMBER < 0x1010000fL || defined(OPENSSL_IS_BORINGSSL)
+       if (!chunk2ecp(this->ec_group, value, pub))
        {
                DBG1(DBG_LIB, "ECDH public value is malformed");
-               return FALSE;
+               goto error;
        }
+#else
+       /* OpenSSL expects the pubkey in the format specified in section 2.3.4 of
+        * SECG SEC 1, i.e. prefixed with 0x04 to indicate an uncompressed point */
+       value = chunk_cata("cc", chunk_from_chars(0x04), value);
+       if (EVP_PKEY_copy_parameters(pub, this->key) <= 0 ||
+               EVP_PKEY_set1_tls_encodedpoint(pub, value.ptr, value.len) <= 0)
+       {
+               DBG1(DBG_LIB, "ECDH public value is malformed");
+               goto error;
+       }
+#endif
 
-       chunk_clear(&this->shared_secret);
-
-       if (!compute_shared_key(this, &this->shared_secret)) {
+       if (!openssl_compute_shared_key(this->key, pub, &this->shared_secret))
+       {
                DBG1(DBG_LIB, "ECDH shared secret computation failed");
-               return FALSE;
+               goto error;
        }
-
        this->computed = TRUE;
-       return TRUE;
+
+error:
+       EVP_PKEY_free(pub);
+       return this->computed;
 }
 
 METHOD(diffie_hellman_t, get_my_public_value, bool,
-       private_openssl_ec_diffie_hellman_t *this,chunk_t *value)
+       private_openssl_ec_diffie_hellman_t *this, chunk_t *value)
 {
-       ecp2chunk(this->ec_group, EC_KEY_get0_public_key(this->key), value);
-       return TRUE;
+#if OPENSSL_VERSION_NUMBER < 0x1010000fL || defined(OPENSSL_IS_BORINGSSL)
+       return ecp2chunk(this->ec_group, this->key, value);
+#else
+       chunk_t pub;
+
+       /* OpenSSL returns the pubkey in the format specified in section 2.3.4 of
+        * SECG SEC 1, i.e. prefixed with 0x04 to indicate an uncompressed point */
+       pub.len = EVP_PKEY_get1_tls_encodedpoint(this->key, &pub.ptr);
+       if (pub.len != 0)
+       {
+               *value = chunk_clone(chunk_skip(pub, 1));
+               chunk_free(&pub);
+               return value->len != 0;
+       }
+       return FALSE;
+#endif
 }
 
 METHOD(diffie_hellman_t, set_private_value, bool,
        private_openssl_ec_diffie_hellman_t *this, chunk_t value)
 {
+       EC_KEY *key = NULL;
        EC_POINT *pub = NULL;
        BIGNUM *priv = NULL;
        bool ret = FALSE;
@@ -221,7 +266,7 @@ METHOD(diffie_hellman_t, set_private_value, bool,
        {
                goto error;
        }
-       pub = EC_POINT_new(EC_KEY_get0_group(this->key));
+       pub = EC_POINT_new(this->ec_group);
        if (!pub)
        {
                goto error;
@@ -230,25 +275,29 @@ METHOD(diffie_hellman_t, set_private_value, bool,
        {
                goto error;
        }
-       if (EC_KEY_set_private_key(this->key, priv) != 1)
+       key = EC_KEY_new();
+       if (!key || !EC_KEY_set_group(key, this->ec_group))
        {
                goto error;
        }
-       if (EC_KEY_set_public_key(this->key, pub) != 1)
+       if (EC_KEY_set_private_key(key, priv) != 1)
        {
                goto error;
        }
-       ret = TRUE;
-
-error:
-       if (pub)
+       if (EC_KEY_set_public_key(key, pub) != 1)
        {
-               EC_POINT_free(pub);
+               goto error;
        }
-       if (priv)
+       if (EVP_PKEY_set1_EC_KEY(this->key, key) != 1)
        {
-               BN_free(priv);
+               goto error;
        }
+       ret = TRUE;
+
+error:
+       EC_POINT_free(pub);
+       BN_free(priv);
+       EC_KEY_free(key);
        return ret;
 }
 
@@ -272,14 +321,8 @@ METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t,
 METHOD(diffie_hellman_t, destroy, void,
        private_openssl_ec_diffie_hellman_t *this)
 {
-       if (this->pub_key)
-       {
-               EC_POINT_clear_free(this->pub_key);
-       }
-       if (this->key)
-       {
-               EC_KEY_free(this->key);
-       }
+       EC_GROUP_free(this->ec_group);
+       EVP_PKEY_free(this->key);
        chunk_clear(&this->shared_secret);
        free(this);
 }
@@ -558,72 +601,70 @@ static EC_KEY *ec_key_new_brainpool(diffie_hellman_group_t group)
 openssl_ec_diffie_hellman_t *openssl_ec_diffie_hellman_create(diffie_hellman_group_t group)
 {
        private_openssl_ec_diffie_hellman_t *this;
-
-       INIT(this,
-               .public = {
-                       .dh = {
-                               .get_shared_secret = _get_shared_secret,
-                               .set_other_public_value = _set_other_public_value,
-                               .get_my_public_value = _get_my_public_value,
-                               .set_private_value = _set_private_value,
-                               .get_dh_group = _get_dh_group,
-                               .destroy = _destroy,
-                       },
-               },
-               .group = group,
-       );
+       EC_KEY *key = NULL;
 
        switch (group)
        {
                case ECP_192_BIT:
-                       this->key = EC_KEY_new_by_curve_name(NID_X9_62_prime192v1);
+                       key = EC_KEY_new_by_curve_name(NID_X9_62_prime192v1);
                        break;
                case ECP_224_BIT:
-                       this->key = EC_KEY_new_by_curve_name(NID_secp224r1);
+                       key = EC_KEY_new_by_curve_name(NID_secp224r1);
                        break;
                case ECP_256_BIT:
-                       this->key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+                       key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
                        break;
                case ECP_384_BIT:
-                       this->key = EC_KEY_new_by_curve_name(NID_secp384r1);
+                       key = EC_KEY_new_by_curve_name(NID_secp384r1);
                        break;
                case ECP_521_BIT:
-                       this->key = EC_KEY_new_by_curve_name(NID_secp521r1);
+                       key = EC_KEY_new_by_curve_name(NID_secp521r1);
                        break;
                case ECP_224_BP:
                case ECP_256_BP:
                case ECP_384_BP:
                case ECP_512_BP:
-                       this->key = ec_key_new_brainpool(group);
+                       key = ec_key_new_brainpool(group);
                        break;
                default:
-                       this->key = NULL;
                        break;
        }
 
-       if (!this->key)
+       if (!key)
        {
-               free(this);
                return NULL;
        }
 
-       /* caching the EC group */
-       this->ec_group = EC_KEY_get0_group(this->key);
+       INIT(this,
+               .public = {
+                       .dh = {
+                               .get_shared_secret = _get_shared_secret,
+                               .set_other_public_value = _set_other_public_value,
+                               .get_my_public_value = _get_my_public_value,
+                               .set_private_value = _set_private_value,
+                               .get_dh_group = _get_dh_group,
+                               .destroy = _destroy,
+                       },
+               },
+               .group = group,
+               .ec_group = EC_GROUP_dup(EC_KEY_get0_group(key)),
+       );
 
-       this->pub_key = EC_POINT_new(this->ec_group);
-       if (!this->pub_key)
+       /* generate an EC private (public) key */
+       if (!EC_KEY_generate_key(key))
        {
+               EC_KEY_free(key);
                destroy(this);
                return NULL;
        }
 
-       /* generate an EC private (public) key */
-       if (!EC_KEY_generate_key(this->key))
+       this->key = EVP_PKEY_new();
+       if (!this->key || !EVP_PKEY_assign_EC_KEY(this->key, key))
        {
+               EC_KEY_free(key);
                destroy(this);
                return NULL;
        }
-
        return &this->public;
 }
 #endif /* OPENSSL_NO_EC */