openssl: Mainly use EVP interface for ECDH
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).
This commit is contained in:
parent
5fdc979770
commit
74e02ff5e6
|
@ -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
|
||||
|
@ -17,10 +17,13 @@
|
|||
|
||||
#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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the shared secret.
|
||||
*/
|
||||
static bool compute_shared_key(private_openssl_ec_diffie_hellman_t *this,
|
||||
chunk_t *shared_secret)
|
||||
{
|
||||
int len;
|
||||
|
||||
*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)
|
||||
{
|
||||
chunk_free(shared_secret);
|
||||
return FALSE;
|
||||
}
|
||||
shared_secret->len = len;
|
||||
return TRUE;
|
||||
}
|
||||
#endif /* OPENSSL_VERSION_NUMBER < ... */
|
||||
|
||||
METHOD(diffie_hellman_t, set_other_public_value, bool,
|
||||
private_openssl_ec_diffie_hellman_t *this, chunk_t value)
|
||||
{
|
||||
EVP_PKEY *pub = NULL;
|
||||
|
||||
chunk_clear(&this->shared_secret);
|
||||
this->computed = FALSE;
|
||||
|
||||
if (!diffie_hellman_verify_value(this->group, value))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!chunk2ecp(this->ec_group, value, this->pub_key))
|
||||
pub = EVP_PKEY_new();
|
||||
if (!pub)
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
if (EC_KEY_set_public_key(key, pub) != 1)
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
if (EVP_PKEY_set1_EC_KEY(this->key, key) != 1)
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
ret = TRUE;
|
||||
|
||||
error:
|
||||
if (pub)
|
||||
{
|
||||
EC_POINT_free(pub);
|
||||
}
|
||||
if (priv)
|
||||
{
|
||||
BN_free(priv);
|
||||
}
|
||||
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,6 +601,39 @@ 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;
|
||||
EC_KEY *key = NULL;
|
||||
|
||||
switch (group)
|
||||
{
|
||||
case ECP_192_BIT:
|
||||
key = EC_KEY_new_by_curve_name(NID_X9_62_prime192v1);
|
||||
break;
|
||||
case ECP_224_BIT:
|
||||
key = EC_KEY_new_by_curve_name(NID_secp224r1);
|
||||
break;
|
||||
case ECP_256_BIT:
|
||||
key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||
break;
|
||||
case ECP_384_BIT:
|
||||
key = EC_KEY_new_by_curve_name(NID_secp384r1);
|
||||
break;
|
||||
case ECP_521_BIT:
|
||||
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:
|
||||
key = ec_key_new_brainpool(group);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!key)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
|
@ -571,59 +647,24 @@ openssl_ec_diffie_hellman_t *openssl_ec_diffie_hellman_create(diffie_hellman_gro
|
|||
},
|
||||
},
|
||||
.group = group,
|
||||
.ec_group = EC_GROUP_dup(EC_KEY_get0_group(key)),
|
||||
);
|
||||
|
||||
switch (group)
|
||||
{
|
||||
case ECP_192_BIT:
|
||||
this->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);
|
||||
break;
|
||||
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;
|
||||
case ECP_224_BP:
|
||||
case ECP_256_BP:
|
||||
case ECP_384_BP:
|
||||
case ECP_512_BP:
|
||||
this->key = ec_key_new_brainpool(group);
|
||||
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)
|
||||
{
|
||||
destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* generate an EC private (public) key */
|
||||
if (!EC_KEY_generate_key(this->key))
|
||||
if (!EC_KEY_generate_key(key))
|
||||
{
|
||||
EC_KEY_free(key);
|
||||
destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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 */
|
||||
|
|
Loading…
Reference in New Issue