openssl: Add support for X25519 and X448
authorTobias Brunner <tobias@strongswan.org>
Thu, 15 Nov 2018 09:20:45 +0000 (10:20 +0100)
committerTobias Brunner <tobias@strongswan.org>
Fri, 30 Nov 2018 13:53:38 +0000 (14:53 +0100)
While X25519 was already added with 1.1.0a, its use would be a lot more
complicated, as the helpers like EVP_PKEY_new_raw_public_key() were only
added in 1.1.1, which also added X448.

src/libstrongswan/plugins/openssl/Makefile.am
src/libstrongswan/plugins/openssl/openssl_plugin.c
src/libstrongswan/plugins/openssl/openssl_x_diffie_hellman.c [new file with mode: 0644]
src/libstrongswan/plugins/openssl/openssl_x_diffie_hellman.h [new file with mode: 0644]

index 9287f78..f2d06a5 100644 (file)
@@ -29,7 +29,8 @@ libstrongswan_openssl_la_SOURCES = \
        openssl_pkcs12.c openssl_pkcs12.h \
        openssl_rng.c openssl_rng.h \
        openssl_hmac.c openssl_hmac.h \
-       openssl_gcm.c openssl_gcm.h
+       openssl_gcm.c openssl_gcm.h \
+       openssl_x_diffie_hellman.c openssl_x_diffie_hellman.h
 
 libstrongswan_openssl_la_LDFLAGS = -module -avoid-version
 libstrongswan_openssl_la_LIBADD  = $(OPENSSL_LIB)
index b0db932..ca1c9b3 100644 (file)
@@ -47,6 +47,7 @@
 #include "openssl_rng.h"
 #include "openssl_hmac.h"
 #include "openssl_gcm.h"
+#include "openssl_x_diffie_hellman.h"
 
 #ifndef FIPS_MODE
 #define FIPS_MODE 0
@@ -594,7 +595,14 @@ METHOD(plugin_t, get_features, int,
                        PLUGIN_PROVIDE(DH, ECP_384_BP),
                        PLUGIN_PROVIDE(DH, ECP_512_BP),
                        PLUGIN_PROVIDE(DH, ECP_224_BP),
-#endif
+#if OPENSSL_VERSION_NUMBER >= 0x1010100fL
+               PLUGIN_REGISTER(DH, openssl_x_diffie_hellman_create),
+                       /* available since 1.1.0a, but we require 1.1.1 features */
+                       PLUGIN_PROVIDE(DH, CURVE_25519),
+                       /* available since 1.1.1 */
+                       PLUGIN_PROVIDE(DH, CURVE_448),
+#endif /* OPENSSL_VERSION_NUMBER */
+#endif /* OPENSSL_NO_ECDH */
 #ifndef OPENSSL_NO_DH
                /* MODP DH groups */
                PLUGIN_REGISTER(DH, openssl_diffie_hellman_create),
diff --git a/src/libstrongswan/plugins/openssl/openssl_x_diffie_hellman.c b/src/libstrongswan/plugins/openssl/openssl_x_diffie_hellman.c
new file mode 100644 (file)
index 0000000..37943f5
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2018 Tobias Brunner
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include <openssl/evp.h>
+
+/* basic support for X25519 was added with 1.1.0a, but we require features (e.g.
+ * to load the keys) that were only added with 1.1.1 */
+#if OPENSSL_VERSION_NUMBER >= 0x1010100fL && !defined(OPENSSL_NO_ECDH)
+
+#include "openssl_x_diffie_hellman.h"
+
+#include <utils/debug.h>
+
+typedef struct private_diffie_hellman_t private_diffie_hellman_t;
+
+/**
+ * Private data
+ */
+struct private_diffie_hellman_t {
+       /**
+        * Public interface.
+        */
+       diffie_hellman_t public;
+
+       /**
+        * Diffie Hellman group number.
+        */
+       diffie_hellman_group_t group;
+
+       /**
+        * Private (public) key
+        */
+       EVP_PKEY *key;
+
+       /**
+        * Shared secret
+        */
+       chunk_t shared_secret;
+
+       /**
+        * True if shared secret is computed
+        */
+       bool computed;
+};
+
+/**
+ * Map a DH group to a key type
+ */
+static int map_key_type(diffie_hellman_group_t group)
+{
+       switch (group)
+       {
+               case CURVE_25519:
+                       return EVP_PKEY_X25519;
+               case CURVE_448:
+                       return EVP_PKEY_X448;
+               default:
+                       return 0;
+       }
+}
+
+/**
+ * Compute the shared secret
+ */
+static bool compute_shared_key(private_diffie_hellman_t *this, EVP_PKEY *pub,
+                                                          chunk_t *shared_secret)
+{
+       EVP_PKEY_CTX *ctx;
+       bool success = FALSE;
+
+       ctx = EVP_PKEY_CTX_new(this->key, NULL);
+       if (!ctx)
+       {
+               return FALSE;
+       }
+
+       if (EVP_PKEY_derive_init(ctx) <= 0)
+       {
+               goto error;
+       }
+
+       if (EVP_PKEY_derive_set_peer(ctx, pub) <= 0)
+       {
+               goto error;
+       }
+
+       if (EVP_PKEY_derive(ctx, NULL, &shared_secret->len) <= 0)
+       {
+               goto error;
+       }
+
+       *shared_secret = chunk_alloc(shared_secret->len);
+
+       if (EVP_PKEY_derive(ctx, shared_secret->ptr, &shared_secret->len) <= 0)
+       {
+               goto error;
+       }
+
+       success = TRUE;
+
+error:
+       EVP_PKEY_CTX_free(ctx);
+       return success;
+}
+
+METHOD(diffie_hellman_t, set_other_public_value, bool,
+       private_diffie_hellman_t *this, chunk_t value)
+{
+       EVP_PKEY *pub;
+
+       if (!diffie_hellman_verify_value(this->group, value))
+       {
+               return FALSE;
+       }
+
+       pub =  EVP_PKEY_new_raw_public_key(map_key_type(this->group), NULL,
+                                       value.ptr, value.len);
+       if (!pub)
+       {
+               DBG1(DBG_LIB, "%N public value is malformed",
+                        diffie_hellman_group_names, this->group);
+               return FALSE;
+       }
+
+       chunk_clear(&this->shared_secret);
+
+       if (!compute_shared_key(this, pub, &this->shared_secret))
+       {
+               DBG1(DBG_LIB, "%N shared secret computation failed",
+                        diffie_hellman_group_names, this->group);
+               EVP_PKEY_free(pub);
+               return FALSE;
+       }
+       this->computed = TRUE;
+       EVP_PKEY_free(pub);
+       return TRUE;
+}
+
+METHOD(diffie_hellman_t, get_my_public_value, bool,
+       private_diffie_hellman_t *this, chunk_t *value)
+{
+       size_t len;
+
+       if (!EVP_PKEY_get_raw_public_key(this->key, NULL, &len))
+       {
+               return FALSE;
+       }
+
+       *value = chunk_alloc(len);
+
+       if (!EVP_PKEY_get_raw_public_key(this->key, value->ptr, &value->len))
+       {
+               chunk_free(value);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+METHOD(diffie_hellman_t, set_private_value, bool,
+       private_diffie_hellman_t *this, chunk_t value)
+{
+       EVP_PKEY_free(this->key);
+       this->key = EVP_PKEY_new_raw_private_key(map_key_type(this->group), NULL,
+                                                                                        value.ptr, value.len);
+       if (!this->key)
+       {
+               return FALSE;
+       }
+       return TRUE;
+}
+
+METHOD(diffie_hellman_t, get_shared_secret, bool,
+       private_diffie_hellman_t *this, chunk_t *secret)
+{
+       if (!this->computed)
+       {
+               return FALSE;
+       }
+       *secret = chunk_clone(this->shared_secret);
+       return TRUE;
+}
+
+METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t,
+       private_diffie_hellman_t *this)
+{
+       return this->group;
+}
+
+METHOD(diffie_hellman_t, destroy, void,
+       private_diffie_hellman_t *this)
+{
+       EVP_PKEY_free(this->key);
+       chunk_clear(&this->shared_secret);
+       free(this);
+}
+
+/*
+ * Described in header
+ */
+diffie_hellman_t *openssl_x_diffie_hellman_create(diffie_hellman_group_t group)
+{
+       private_diffie_hellman_t *this;
+       EVP_PKEY_CTX *ctx = NULL;
+       EVP_PKEY *key = NULL;
+
+       switch (group)
+       {
+               case CURVE_25519:
+                       ctx = EVP_PKEY_CTX_new_id(NID_X25519, NULL);
+                       break;
+               case CURVE_448:
+                       ctx = EVP_PKEY_CTX_new_id(NID_X448, NULL);
+                       break;
+               default:
+                       break;
+       }
+
+       if (!ctx ||
+               EVP_PKEY_keygen_init(ctx) <= 0 ||
+               EVP_PKEY_keygen(ctx, &key) <= 0)
+       {
+               DBG1(DBG_LIB, "generating key for %N failed",
+                        diffie_hellman_group_names, group);
+               EVP_PKEY_CTX_free(ctx);
+               return NULL;
+       }
+       EVP_PKEY_CTX_free(ctx);
+
+       INIT(this,
+               .public = {
+                       .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,
+               .key = key,
+       );
+       return &this->public;
+}
+
+#endif /* OPENSSL_NO_ECDH */
diff --git a/src/libstrongswan/plugins/openssl/openssl_x_diffie_hellman.h b/src/libstrongswan/plugins/openssl/openssl_x_diffie_hellman.h
new file mode 100644 (file)
index 0000000..e28f38d
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 Tobias Brunner
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * Implementation of the X25519/X448 Diffie-Hellman algorithm using OpenSSL.
+ *
+ * @defgroup openssl_x_diffie_hellman openssl_x_diffie_hellman
+ * @{ @ingroup openssl_p
+ */
+
+#ifndef OPENSSL_X_DIFFIE_HELLMAN_H_
+#define OPENSSL_X_DIFFIE_HELLMAN_H_
+
+#include <library.h>
+
+/**
+ * Creates a new diffie_hellman_t object.
+ *
+ * @param group                        Diffie Hellman group number to use
+ * @return                             object, NULL if not supported
+ */
+diffie_hellman_t *openssl_x_diffie_hellman_create(diffie_hellman_group_t group);
+
+#endif /** OPENSSL_X_DIFFIE_HELLMAN_H_ @}*/
+