From fc1a31d54b324625cca413583abc43406d70e930 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 22 May 2008 11:39:17 +0000 Subject: [PATCH] added ECDH with OpenSSL (see RFC 4753) --- src/charon/config/proposal.c | 12 + src/libstrongswan/crypto/diffie_hellman.c | 9 +- src/libstrongswan/crypto/diffie_hellman.h | 8 +- src/libstrongswan/plugins/openssl/Makefile.am | 3 +- .../plugins/openssl/openssl_ec_diffie_hellman.c | 360 +++++++++++++++++++++ .../plugins/openssl/openssl_ec_diffie_hellman.h | 48 +++ src/libstrongswan/plugins/openssl/openssl_plugin.c | 11 + 7 files changed, 446 insertions(+), 5 deletions(-) create mode 100644 src/libstrongswan/plugins/openssl/openssl_ec_diffie_hellman.c create mode 100644 src/libstrongswan/plugins/openssl/openssl_ec_diffie_hellman.h diff --git a/src/charon/config/proposal.c b/src/charon/config/proposal.c index 71bb3a2..45d0108 100644 --- a/src/charon/config/proposal.c +++ b/src/charon/config/proposal.c @@ -766,6 +766,18 @@ static status_t add_string_algo(private_proposal_t *this, chunk_t alg) { add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_8192_BIT, 0); } + else if (strncmp(alg.ptr, "ecp256", alg.len) == 0) + { + add_algorithm(this, DIFFIE_HELLMAN_GROUP, ECP_256_BIT, 0); + } + else if (strncmp(alg.ptr, "ecp384", alg.len) == 0) + { + add_algorithm(this, DIFFIE_HELLMAN_GROUP, ECP_384_BIT, 0); + } + else if (strncmp(alg.ptr, "ecp521", alg.len) == 0) + { + add_algorithm(this, DIFFIE_HELLMAN_GROUP, ECP_521_BIT, 0); + } else { return FAILED; diff --git a/src/libstrongswan/crypto/diffie_hellman.c b/src/libstrongswan/crypto/diffie_hellman.c index 9227087..a915074 100644 --- a/src/libstrongswan/crypto/diffie_hellman.c +++ b/src/libstrongswan/crypto/diffie_hellman.c @@ -24,11 +24,14 @@ ENUM_BEGIN(diffie_hellman_group_names, MODP_NONE, MODP_1024_BIT, "MODP_1024_BIT"); ENUM_NEXT(diffie_hellman_group_names, MODP_1536_BIT, MODP_1536_BIT, MODP_1024_BIT, "MODP_1536_BIT"); -ENUM_NEXT(diffie_hellman_group_names, MODP_2048_BIT, MODP_8192_BIT, MODP_1536_BIT, +ENUM_NEXT(diffie_hellman_group_names, MODP_2048_BIT, ECP_521_BIT, MODP_1536_BIT, "MODP_2048_BIT", "MODP_3072_BIT", "MODP_4096_BIT", "MODP_6144_BIT", - "MODP_8192_BIT"); -ENUM_END(diffie_hellman_group_names, MODP_8192_BIT); + "MODP_8192_BIT" + "ECP_256_BIT", + "ECP_384_BIT", + "ECP_521_BIT"); +ENUM_END(diffie_hellman_group_names, ECP_521_BIT); diff --git a/src/libstrongswan/crypto/diffie_hellman.h b/src/libstrongswan/crypto/diffie_hellman.h index 07e475b..53d89c3 100644 --- a/src/libstrongswan/crypto/diffie_hellman.h +++ b/src/libstrongswan/crypto/diffie_hellman.h @@ -34,6 +34,8 @@ typedef struct diffie_hellman_t diffie_hellman_t; * * The modulus (or group) to use for a Diffie-Hellman calculation. * See IKEv2 RFC 3.3.2 and RFC 3526. + * + * ECP groups are defined in RFC 4753. */ enum diffie_hellman_group_t { MODP_NONE = 0, @@ -44,7 +46,11 @@ enum diffie_hellman_group_t { MODP_3072_BIT = 15, MODP_4096_BIT = 16, MODP_6144_BIT = 17, - MODP_8192_BIT = 18 + MODP_8192_BIT = 18, + ECP_256_BIT = 19, + ECP_384_BIT = 20, + ECP_521_BIT = 21, + }; /** diff --git a/src/libstrongswan/plugins/openssl/Makefile.am b/src/libstrongswan/plugins/openssl/Makefile.am index 4f0cb86..b2bb03f 100644 --- a/src/libstrongswan/plugins/openssl/Makefile.am +++ b/src/libstrongswan/plugins/openssl/Makefile.am @@ -10,7 +10,8 @@ libstrongswan_openssl_la_SOURCES = openssl_plugin.h openssl_plugin.c \ openssl_hasher.c openssl_hasher.h \ openssl_diffie_hellman.c openssl_diffie_hellman.h \ openssl_rsa_private_key.c openssl_rsa_private_key.h \ - openssl_rsa_public_key.c openssl_rsa_public_key.h + openssl_rsa_public_key.c openssl_rsa_public_key.h \ + openssl_ec_diffie_hellman.c openssl_ec_diffie_hellman.h libstrongswan_openssl_la_LDFLAGS = -module libstrongswan_openssl_la_LIBADD = -lssl diff --git a/src/libstrongswan/plugins/openssl/openssl_ec_diffie_hellman.c b/src/libstrongswan/plugins/openssl/openssl_ec_diffie_hellman.c new file mode 100644 index 0000000..0aeaf6b --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_ec_diffie_hellman.c @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2008 Tobias Brunner + * 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. + * + * $Id$ + */ + +#include +#include + +#include "openssl_ec_diffie_hellman.h" + +#include + +#define COORD_LEN(group) ((EC_GROUP_get_degree(group) + 7) / 8) + +typedef struct private_openssl_ec_diffie_hellman_t private_openssl_ec_diffie_hellman_t; + +/** + * Private data of an openssl_ec_diffie_hellman_t object. + */ +struct private_openssl_ec_diffie_hellman_t { + /** + * Public openssl_ec_diffie_hellman_t interface. + */ + openssl_ec_diffie_hellman_t public; + + /** + * Diffie Hellman group number. + */ + u_int16_t group; + + /** + * EC private (public) key + */ + EC_KEY *key; + + /** + * EC group + */ + const EC_GROUP *ec_group; + + /** + * Other public key + */ + EC_POINT *pub_key; + + /** + * Shared secret + */ + chunk_t shared_secret; + + /** + * True if shared secret is computed + */ + bool computed; +}; + +/** + * Convert a chunk to an EC_POINT (which must already exist). 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) +{ + BN_CTX *ctx; + BIGNUM *x, *y; + int coord_len; + bool ret = FALSE; + + ctx = BN_CTX_new(); + if (!ctx) + { + return FALSE; + } + + BN_CTX_start(ctx); + x = BN_CTX_get(ctx); + y = BN_CTX_get(ctx); + if (!x || !y) + { + goto error; + } + + coord_len = COORD_LEN(group); + if (chunk.len != coord_len * 2) + { + goto error; + } + + if (!BN_bin2bn(chunk.ptr, coord_len, x) || + !BN_bin2bn(chunk.ptr + coord_len, coord_len, y)) + { + goto error; + } + + if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) + { + goto error; + } + + ret = TRUE; +error: + 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. + */ +static bool ecp2chunk(const EC_GROUP *group, const EC_POINT *point, chunk_t *chunk) +{ + BN_CTX *ctx; + BIGNUM *x, *y; + int coord_len, offset; + bool ret = FALSE; + + ctx = BN_CTX_new(); + if (!ctx) + { + return FALSE; + } + + BN_CTX_start(ctx); + x = BN_CTX_get(ctx); + y = BN_CTX_get(ctx); + if (!x || !y) + { + goto error; + } + + if (!EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx)) + { + goto error; + } + + coord_len = COORD_LEN(group); + chunk->len = coord_len * 2; + chunk->ptr = malloc(chunk->len); + memset(chunk->ptr, 0, chunk->len); + + offset = coord_len - BN_num_bytes(x); + if (!BN_bn2bin(x, chunk->ptr + offset)) + { + chunk_free(chunk); + goto error; + } + + offset = coord_len - BN_num_bytes(y); + if (!BN_bn2bin(y, chunk->ptr + coord_len + offset)) + { + chunk_free(chunk); + goto error; + } + + ret = TRUE; +error: + BN_CTX_end(ctx); + BN_CTX_free(ctx); + return ret; +} + +/** + * Compute the shared secret. + * + * We cannot use the function ECDH_compute_key() because that returns only the + * x coordinate of the shared secret point (which is defined, for instance, in + * 'NIST SP 800-56A'). + * However, we need both coordinates as RFC 4753 says: "The Diffie-Hellman + * public value is obtained by concatenating the x and y values. The format + * of the Diffie-Hellman shared secret value is the same as that of the + * Diffie-Hellman public value." + */ +static bool compute_shared_key(private_openssl_ec_diffie_hellman_t *this, chunk_t *shared_secret) +{ + const BIGNUM *priv_key; + EC_POINT *secret; + bool ret = FALSE; + + priv_key = EC_KEY_get0_private_key(this->key); + if (!priv_key) + { + goto error; + } + + secret = EC_POINT_new(this->ec_group); + if (!secret) + { + goto error; + } + + if (!EC_POINT_mul(this->ec_group, secret, NULL, this->pub_key, priv_key, NULL)) + { + goto error; + } + + if (!ecp2chunk(this->ec_group, secret, shared_secret)) + { + goto error; + } + + ret = TRUE; +error: + if (secret) + { + EC_POINT_clear_free(secret); + } + return ret; +} + +/** + * Implementation of openssl_ec_diffie_hellman_t.set_other_public_value. + */ +static void set_other_public_value(private_openssl_ec_diffie_hellman_t *this, chunk_t value) +{ + if (!chunk2ecp(this->ec_group, value, this->pub_key)) + { + DBG1("ECDH public value is malformed"); + return; + } + + chunk_free(&this->shared_secret); + + if (!compute_shared_key(this, &this->shared_secret)) { + DBG1("ECDH shared secret computation failed"); + return; + } + + this->computed = TRUE; +} + +/** + * Implementation of openssl_ec_diffie_hellman_t.get_other_public_value. + */ +static status_t get_other_public_value(private_openssl_ec_diffie_hellman_t *this, + chunk_t *value) +{ + if (!this->computed) + { + return FAILED; + } + + if (!ecp2chunk(this->ec_group, this->pub_key, value)) + { + return FAILED; + } + return SUCCESS; +} + +/** + * Implementation of openssl_ec_diffie_hellman_t.get_my_public_value. + */ +static void get_my_public_value(private_openssl_ec_diffie_hellman_t *this,chunk_t *value) +{ + ecp2chunk(this->ec_group, EC_KEY_get0_public_key(this->key), value); +} + +/** + * Implementation of openssl_ec_diffie_hellman_t.get_shared_secret. + */ +static status_t get_shared_secret(private_openssl_ec_diffie_hellman_t *this, chunk_t *secret) +{ + if (!this->computed) + { + return FAILED; + } + *secret = chunk_clone(this->shared_secret); + return SUCCESS; +} + +/** + * Implementation of openssl_ec_diffie_hellman_t.get_dh_group. + */ +static diffie_hellman_group_t get_dh_group(private_openssl_ec_diffie_hellman_t *this) +{ + return this->group; +} + +/** + * Implementation of openssl_ec_diffie_hellman_t.destroy. + */ +static void destroy(private_openssl_ec_diffie_hellman_t *this) +{ + EC_POINT_clear_free(this->pub_key); + EC_KEY_free(this->key); + chunk_free(&this->shared_secret); + free(this); +} + +/* + * Described in header. + */ +openssl_ec_diffie_hellman_t *openssl_ec_diffie_hellman_create(diffie_hellman_group_t group) +{ + private_openssl_ec_diffie_hellman_t *this = malloc_thing(private_openssl_ec_diffie_hellman_t); + + this->public.dh.get_shared_secret = (status_t (*)(diffie_hellman_t *, chunk_t *)) get_shared_secret; + this->public.dh.set_other_public_value = (void (*)(diffie_hellman_t *, chunk_t )) set_other_public_value; + this->public.dh.get_other_public_value = (status_t (*)(diffie_hellman_t *, chunk_t *)) get_other_public_value; + this->public.dh.get_my_public_value = (void (*)(diffie_hellman_t *, chunk_t *)) get_my_public_value; + this->public.dh.get_dh_group = (diffie_hellman_group_t (*)(diffie_hellman_t *)) get_dh_group; + this->public.dh.destroy = (void (*)(diffie_hellman_t *)) destroy; + + switch (group) + { + case ECP_256_BIT: + this->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); + break; + case ECP_521_BIT: + this->key = EC_KEY_new_by_curve_name(NID_secp521r1); + break; + default: + this->key = NULL; + break; + } + + if (!this->key) + { + free(this); + return NULL; + } + + /* caching the EC group */ + this->ec_group = EC_KEY_get0_group(this->key); + + this->pub_key = EC_POINT_new(this->ec_group); + if (!this->pub_key) + { + free(this); + return NULL; + } + + /* generate an EC private (public) key */ + if (!EC_KEY_generate_key(this->key)) + { + free(this); + return NULL; + } + + this->group = group; + this->computed = FALSE; + + this->shared_secret = chunk_empty; + + return &this->public; +} diff --git a/src/libstrongswan/plugins/openssl/openssl_ec_diffie_hellman.h b/src/libstrongswan/plugins/openssl/openssl_ec_diffie_hellman.h new file mode 100644 index 0000000..69d60c6 --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_ec_diffie_hellman.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 Tobias Brunner + * 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. + */ + +/** + * @defgroup openssl_ec_diffie_hellman openssl_ec_diffie_hellman + * @{ @ingroup openssl_p + */ + +#ifndef OPENSSL_EC_DIFFIE_HELLMAN_H_ +#define OPENSSL_EC_DIFFIE_HELLMAN_H_ + +typedef struct openssl_ec_diffie_hellman_t openssl_ec_diffie_hellman_t; + +#include + +/** + * Implementation of the EC Diffie-Hellman algorithm using OpenSSL. + */ +struct openssl_ec_diffie_hellman_t { + + /** + * Implements diffie_hellman_t interface. + */ + diffie_hellman_t dh; +}; + +/** + * Creates a new openssl_ec_diffie_hellman_t object. + * + * @param group EC Diffie Hellman group number to use + * @return openssl_ec_diffie_hellman_t object, NULL if not supported + */ +openssl_ec_diffie_hellman_t *openssl_ec_diffie_hellman_create(diffie_hellman_group_t group); + +#endif /*OPENSSL_EC_DIFFIE_HELLMAN_H_ @}*/ + diff --git a/src/libstrongswan/plugins/openssl/openssl_plugin.c b/src/libstrongswan/plugins/openssl/openssl_plugin.c index c888c2f..7e57c7a 100644 --- a/src/libstrongswan/plugins/openssl/openssl_plugin.c +++ b/src/libstrongswan/plugins/openssl/openssl_plugin.c @@ -23,6 +23,7 @@ #include "openssl_crypter.h" #include "openssl_hasher.h" #include "openssl_diffie_hellman.h" +#include "openssl_ec_diffie_hellman.h" #include "openssl_rsa_private_key.h" #include "openssl_rsa_public_key.h" @@ -50,6 +51,8 @@ static void destroy(private_openssl_plugin_t *this) (hasher_constructor_t)openssl_hasher_create); lib->crypto->remove_dh(lib->crypto, (dh_constructor_t)openssl_diffie_hellman_create); + lib->crypto->remove_dh(lib->crypto, + (dh_constructor_t)openssl_ec_diffie_hellman_create); lib->creds->remove_builder(lib->creds, (builder_constructor_t)openssl_rsa_private_key_builder); lib->creds->remove_builder(lib->creds, @@ -121,6 +124,14 @@ plugin_t *plugin_create() lib->crypto->add_dh(lib->crypto, MODP_8192_BIT, (dh_constructor_t)openssl_diffie_hellman_create); + /* ec diffie hellman */ + lib->crypto->add_dh(lib->crypto, ECP_256_BIT, + (dh_constructor_t)openssl_ec_diffie_hellman_create); + lib->crypto->add_dh(lib->crypto, ECP_384_BIT, + (dh_constructor_t)openssl_ec_diffie_hellman_create); + lib->crypto->add_dh(lib->crypto, ECP_521_BIT, + (dh_constructor_t)openssl_ec_diffie_hellman_create); + /* rsa */ lib->creds->add_builder(lib->creds, CRED_PRIVATE_KEY, KEY_RSA, (builder_constructor_t)openssl_rsa_private_key_builder); -- 2.7.4