From 57cb4c8d2c809525897fc1ab8cba8d05ecdbc775 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 15 Nov 2018 10:20:45 +0100 Subject: [PATCH] openssl: Add support for X25519 and X448 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 | 3 +- .../plugins/openssl/openssl_plugin.c | 10 +- .../openssl/openssl_x_diffie_hellman.c | 256 ++++++++++++++++++ .../openssl/openssl_x_diffie_hellman.h | 37 +++ 4 files changed, 304 insertions(+), 2 deletions(-) create mode 100644 src/libstrongswan/plugins/openssl/openssl_x_diffie_hellman.c create mode 100644 src/libstrongswan/plugins/openssl/openssl_x_diffie_hellman.h diff --git a/src/libstrongswan/plugins/openssl/Makefile.am b/src/libstrongswan/plugins/openssl/Makefile.am index 9287f788a4..f2d06a5d13 100644 --- a/src/libstrongswan/plugins/openssl/Makefile.am +++ b/src/libstrongswan/plugins/openssl/Makefile.am @@ -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) diff --git a/src/libstrongswan/plugins/openssl/openssl_plugin.c b/src/libstrongswan/plugins/openssl/openssl_plugin.c index b0db9325f2..ca1c9b369a 100644 --- a/src/libstrongswan/plugins/openssl/openssl_plugin.c +++ b/src/libstrongswan/plugins/openssl/openssl_plugin.c @@ -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 index 0000000000..37943f5bf6 --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_x_diffie_hellman.c @@ -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 . + * + * 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 + +/* 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 + +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 index 0000000000..e28f38d15f --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_x_diffie_hellman.h @@ -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 . + * + * 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 + +/** + * 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_ @}*/ + -- 2.47.3