Merge branch 'openssl-3.0'

This provides compatibility changes for OpenSSL 3.0.
This commit is contained in:
Tobias Brunner 2022-04-14 19:06:12 +02:00
commit 9aa7e126b2
26 changed files with 1768 additions and 1211 deletions

View File

@ -87,7 +87,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
test: [ botan, wolfssl, openssl, gcrypt ]
test: [ botan, wolfssl, openssl, openssl-3, gcrypt ]
leak-detective: [ no, yes ]
env:
LEAK_DETECTIVE: ${{ matrix.leak-detective || 'no' }}

View File

@ -1480,7 +1480,6 @@ ADD_PLUGIN([acert], [s charon])
ADD_PLUGIN([pubkey], [s charon pki cmd aikgen])
ADD_PLUGIN([pkcs1], [s charon scepclient pki scripts manager medsrv attest nm cmd aikgen fuzz])
ADD_PLUGIN([pkcs7], [s charon scepclient pki scripts nm cmd])
ADD_PLUGIN([pkcs8], [s charon scepclient pki scripts manager medsrv attest nm cmd])
ADD_PLUGIN([pkcs12], [s charon scepclient pki scripts cmd])
ADD_PLUGIN([pgp], [s charon])
ADD_PLUGIN([dnskey], [s charon pki])
@ -1493,6 +1492,7 @@ ADD_PLUGIN([openssl], [s charon scepclient pki scripts manager meds
ADD_PLUGIN([wolfssl], [s charon scepclient pki scripts manager medsrv attest nm cmd aikgen])
ADD_PLUGIN([gcrypt], [s charon scepclient pki scripts manager medsrv attest nm cmd aikgen])
ADD_PLUGIN([botan], [s charon scepclient pki scripts manager medsrv attest nm cmd aikgen])
ADD_PLUGIN([pkcs8], [s charon scepclient pki scripts manager medsrv attest nm cmd])
ADD_PLUGIN([af-alg], [s charon scepclient pki scripts medsrv attest nm cmd aikgen])
ADD_PLUGIN([fips-prf], [s charon nm cmd])
ADD_PLUGIN([gmp], [s charon scepclient pki scripts manager medsrv attest nm cmd aikgen fuzz])

View File

@ -89,6 +89,46 @@ build_tss2()
cd -
}
build_openssl()
{
SSL_REV=3.0.2
SSL_PKG=openssl-$SSL_REV
SSL_DIR=$DEPS_BUILD_DIR/$SSL_PKG
SSL_SRC=https://www.openssl.org/source/$SSL_PKG.tar.gz
SSL_INS=$DEPS_PREFIX/ssl
SSL_OPT="shared no-tls no-dtls no-ssl3 no-zlib no-comp no-idea no-psk no-srp
no-stdio no-tests enable-rfc3779 enable-ec_nistp_64_gcc_128"
if test -d "$SSL_DIR"; then
return
fi
# insist on compiling with gcc and debug information as symbols are otherwise not found
if test "$LEAK_DETECTIVE" = "yes"; then
SSL_OPT="$SSL_OPT CC=gcc -d"
fi
echo "$ build_openssl()"
curl -L $SSL_SRC | tar xz -C $DEPS_BUILD_DIR &&
cd $SSL_DIR &&
./config --prefix=$SSL_INS --openssldir=$SSL_INS --libdir=lib $SSL_OPT &&
make -j4 >/dev/null &&
sudo make install_sw >/dev/null &&
sudo ldconfig || exit $?
cd -
}
use_custom_openssl()
{
CFLAGS="$CFLAGS -I$DEPS_PREFIX/ssl/include"
export LDFLAGS="$LDFLAGS -L$DEPS_PREFIX/ssl/lib"
export LD_LIBRARY_PATH="$DEPS_PREFIX/ssl/lib:$LD_LIBRARY_PATH"
if test "$1" = "build-deps"; then
build_openssl
fi
}
: ${BUILD_DIR=$PWD}
: ${DEPS_BUILD_DIR=$BUILD_DIR/..}
: ${DEPS_PREFIX=/usr/local}
@ -114,6 +154,10 @@ openssl*)
CONFIG="--disable-defaults --enable-pki --enable-openssl --enable-pem"
export TESTS_PLUGINS="test-vectors pem openssl!"
DEPS="libssl-dev"
if test "$TEST" = "openssl-3"; then
DEPS=""
use_custom_openssl $1
fi
;;
gcrypt)
CONFIG="--disable-defaults --enable-pki --enable-gcrypt --enable-pkcs1 --enable-pkcs8"
@ -184,6 +228,7 @@ all|coverage|sonarcloud)
build_wolfssl
build_tss2
fi
use_custom_openssl $1
;;
win*)
CONFIG="--disable-defaults --enable-svc --enable-ikev2

View File

@ -19,6 +19,7 @@ libstrongswan_openssl_la_SOURCES = \
openssl_plugin.h openssl_plugin.c \
openssl_util.c openssl_util.h \
openssl_crypter.c openssl_crypter.h \
openssl_engine.c openssl_engine.h \
openssl_hasher.c openssl_hasher.h \
openssl_sha1_prf.c openssl_sha1_prf.h \
openssl_diffie_hellman.c openssl_diffie_hellman.h \

View File

@ -15,8 +15,6 @@
#include <openssl/opensslv.h>
#if OPENSSL_VERSION_NUMBER >= 0x1000100fL
#include "openssl_aead.h"
#include <openssl/evp.h>
@ -363,5 +361,3 @@ aead_t *openssl_aead_create(encryption_algorithm_t algo,
return &this->public;
}
#endif /* OPENSSL_VERSION_NUMBER */

View File

@ -18,9 +18,15 @@
#ifndef OPENSSL_NO_DH
#include <openssl/evp.h>
#include <openssl/bn.h>
#include <openssl/dh.h>
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
#include <openssl/param_build.h>
#include <openssl/core_names.h>
#endif
#include "openssl_diffie_hellman.h"
#include "openssl_util.h"
@ -49,6 +55,17 @@ struct private_openssl_diffie_hellman_t {
*/
diffie_hellman_group_t group;
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
/**
* Diffie Hellman key
*/
EVP_PKEY *key;
/**
* Other public value
*/
EVP_PKEY *pub;
#else
/**
* Diffie Hellman object
*/
@ -58,21 +75,35 @@ struct private_openssl_diffie_hellman_t {
* Other public value
*/
BIGNUM *pub_key;
#endif
/**
* Shared secret
*/
chunk_t shared_secret;
/**
* True if shared secret is computed
*/
bool computed;
};
METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t,
private_openssl_diffie_hellman_t *this)
{
return this->group;
}
METHOD(diffie_hellman_t, get_my_public_value, bool,
private_openssl_diffie_hellman_t *this, chunk_t *value)
{
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
chunk_t pub;
pub.len = EVP_PKEY_get1_encoded_public_key(this->key, &pub.ptr);
if (pub.len != 0)
{
*value = chunk_clone(pub);
OPENSSL_free(pub.ptr);
return value->len != 0;
}
return FALSE;
#else
const BIGNUM *pubkey;
*value = chunk_alloc(DH_size(this->dh));
@ -80,49 +111,148 @@ METHOD(diffie_hellman_t, get_my_public_value, bool,
DH_get0_key(this->dh, &pubkey, NULL);
BN_bn2bin(pubkey, value->ptr + value->len - BN_num_bytes(pubkey));
return TRUE;
#endif
}
METHOD(diffie_hellman_t, get_shared_secret, bool,
private_openssl_diffie_hellman_t *this, chunk_t *secret)
{
if (!this->computed)
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
if (!this->shared_secret.len &&
!openssl_compute_shared_key(this->key, this->pub, &this->shared_secret))
{
DBG1(DBG_LIB, "DH shared secret computation failed");
return FALSE;
}
/* shared secret should requires a len according the DH group */
*secret = chunk_alloc(DH_size(this->dh));
memset(secret->ptr, 0, secret->len);
memcpy(secret->ptr + secret->len - this->shared_secret.len,
this->shared_secret.ptr, this->shared_secret.len);
*secret = chunk_clone(this->shared_secret);
return TRUE;
#else
int len;
if (!this->shared_secret.len)
{
this->shared_secret = chunk_alloc(DH_size(this->dh));
memset(this->shared_secret.ptr, 0xFF, this->shared_secret.len);
len = DH_compute_key(this->shared_secret.ptr, this->pub_key, this->dh);
if (len < 0)
{
DBG1(DBG_LIB, "DH shared secret computation failed");
chunk_clear(&this->shared_secret);
return FALSE;
}
this->shared_secret.len = len;
}
/* shared secret requires a length according to the DH group */
*secret = chunk_copy_pad(chunk_alloc(DH_size(this->dh)),
this->shared_secret, 0);
return TRUE;
#endif
}
METHOD(diffie_hellman_t, set_other_public_value, bool,
private_openssl_diffie_hellman_t *this, chunk_t value)
{
int len;
if (!diffie_hellman_verify_value(this->group, value))
{
return FALSE;
}
BN_bin2bn(value.ptr, value.len, this->pub_key);
chunk_clear(&this->shared_secret);
this->shared_secret.ptr = malloc(DH_size(this->dh));
memset(this->shared_secret.ptr, 0xFF, this->shared_secret.len);
len = DH_compute_key(this->shared_secret.ptr, this->pub_key, this->dh);
if (len < 0)
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
if (!this->pub)
{
DBG1(DBG_LIB, "DH shared secret computation failed");
this->pub = EVP_PKEY_new();
}
if (EVP_PKEY_copy_parameters(this->pub, this->key) <= 0 ||
EVP_PKEY_set1_encoded_public_key(this->pub, value.ptr, value.len) <= 0)
{
DBG1(DBG_LIB, "DH public value is malformed");
return FALSE;
}
this->shared_secret.len = len;
this->computed = TRUE;
#else
if (!BN_bin2bn(value.ptr, value.len, this->pub_key))
{
return FALSE;
}
#endif
chunk_clear(&this->shared_secret);
return TRUE;
}
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
/**
* Calculate the public key for the given private key and DH parameters.
* Setting only the private key and generating the public key interanlly is
* not supported anymore with OpenSSL 3.0.0.
*/
static BIGNUM *calculate_public_key(BIGNUM *priv, const BIGNUM *g,
const BIGNUM *p)
{
BN_CTX *ctx = BN_CTX_new();
BIGNUM *pub = BN_new();
BN_set_flags(priv, BN_FLG_CONSTTIME);
/* pub = g^priv mod p */
if (!ctx || ! pub || !BN_mod_exp(pub, g, priv, p, ctx))
{
BN_free(pub);
pub = NULL;
}
BN_CTX_free(ctx);
return pub;
}
METHOD(diffie_hellman_t, set_private_value, bool,
private_openssl_diffie_hellman_t *this, chunk_t value)
{
BIGNUM *priv, *g = NULL, *p = NULL, *pub = NULL;
OSSL_PARAM_BLD *bld = NULL;
OSSL_PARAM *params = NULL;
EVP_PKEY *key = NULL;
EVP_PKEY_CTX *ctx = NULL;
bool ret = FALSE;
priv = BN_bin2bn(value.ptr, value.len, NULL);
if (EVP_PKEY_get_bn_param(this->key, OSSL_PKEY_PARAM_FFC_G, &g) <= 0 ||
EVP_PKEY_get_bn_param(this->key, OSSL_PKEY_PARAM_FFC_P, &p) <= 0)
{
goto error;
}
pub = calculate_public_key(priv, g, p);
bld = OSSL_PARAM_BLD_new();
if (pub && bld &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_G, g) &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_P, p) &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, priv) &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PUB_KEY, pub))
{
params = OSSL_PARAM_BLD_to_param(bld);
}
ctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL);
if (!params || !ctx ||
EVP_PKEY_fromdata_init(ctx) <= 0 ||
EVP_PKEY_fromdata(ctx, &key, EVP_PKEY_KEYPAIR, params) <= 0)
{
goto error;
}
EVP_PKEY_free(this->key);
this->key = key;
ret = TRUE;
error:
EVP_PKEY_CTX_free(ctx);
OSSL_PARAM_free(params);
OSSL_PARAM_BLD_free(bld);
BN_free(pub);
BN_free(p);
BN_free(g);
BN_free(priv);
return ret;
}
#else /* OPENSSL_VERSION_NUMBER */
METHOD(diffie_hellman_t, set_private_value, bool,
private_openssl_diffie_hellman_t *this, chunk_t value)
{
@ -136,56 +266,23 @@ METHOD(diffie_hellman_t, set_private_value, bool,
return FALSE;
}
chunk_clear(&this->shared_secret);
this->computed = FALSE;
return DH_generate_key(this->dh);
}
return FALSE;
}
METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t,
private_openssl_diffie_hellman_t *this)
{
return this->group;
}
/**
* Lookup the modulus in modulo table
*/
static status_t set_modulus(private_openssl_diffie_hellman_t *this)
{
BIGNUM *p, *g;
diffie_hellman_params_t *params = diffie_hellman_get_params(this->group);
if (!params)
{
return NOT_FOUND;
}
p = BN_bin2bn(params->prime.ptr, params->prime.len, NULL);
g = BN_bin2bn(params->generator.ptr, params->generator.len, NULL);
if (!DH_set0_pqg(this->dh, p, NULL, g))
{
return FAILED;
}
if (params->exp_len != params->prime.len)
{
#if defined(OPENSSL_IS_BORINGSSL) && \
(!defined(BORINGSSL_API_VERSION) || BORINGSSL_API_VERSION < 11)
this->dh->priv_length = params->exp_len * 8;
#else
if (!DH_set_length(this->dh, params->exp_len * 8))
{
return FAILED;
}
#endif
}
return SUCCESS;
}
#endif /* OPENSSL_VERSION_NUMBER */
METHOD(diffie_hellman_t, destroy, void,
private_openssl_diffie_hellman_t *this)
{
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
EVP_PKEY_free(this->key);
EVP_PKEY_free(this->pub);
#else
BN_clear_free(this->pub_key);
DH_free(this->dh);
#endif
chunk_clear(&this->shared_secret);
free(this);
}
@ -197,7 +294,8 @@ openssl_diffie_hellman_t *openssl_diffie_hellman_create(
diffie_hellman_group_t group, ...)
{
private_openssl_diffie_hellman_t *this;
const BIGNUM *privkey;
BIGNUM *g, *p;
int priv_len = 0;
INIT(this,
.public = {
@ -210,50 +308,105 @@ openssl_diffie_hellman_t *openssl_diffie_hellman_create(
.destroy = _destroy,
},
},
.group = group,
);
this->dh = DH_new();
if (!this->dh)
{
free(this);
return NULL;
}
this->group = group;
this->computed = FALSE;
this->pub_key = BN_new();
this->shared_secret = chunk_empty;
if (group == MODP_CUSTOM)
{
chunk_t g, p;
chunk_t g_chunk, p_chunk;
VA_ARGS_GET(group, g, p);
if (!DH_set0_pqg(this->dh, BN_bin2bn(p.ptr, p.len, NULL), NULL,
BN_bin2bn(g.ptr, g.len, NULL)))
{
destroy(this);
return NULL;
}
VA_ARGS_GET(group, g_chunk, p_chunk);
g = BN_bin2bn(g_chunk.ptr, g_chunk.len, NULL);
p = BN_bin2bn(p_chunk.ptr, p_chunk.len, NULL);
}
else
{
/* find a modulus according to group */
if (set_modulus(this) != SUCCESS)
diffie_hellman_params_t *params = diffie_hellman_get_params(group);
if (!params)
{
destroy(this);
return NULL;
}
g = BN_bin2bn(params->generator.ptr, params->generator.len, NULL);
p = BN_bin2bn(params->prime.ptr, params->prime.len, NULL);
if (params->exp_len != params->prime.len)
{
priv_len = params->exp_len * 8;
}
}
/* generate my public and private values */
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
OSSL_PARAM_BLD *bld;
OSSL_PARAM *params = NULL;
EVP_PKEY_CTX *ctx;
/* if we abandoned MODP_CUSTOM, we could set OSSL_PKEY_PARAM_GROUP_NAME,
* which wouldn't require the first ctx/key for the parameters */
bld = OSSL_PARAM_BLD_new();
if (bld &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_G, g) &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_P, p) &&
(!priv_len ||
OSSL_PARAM_BLD_push_int(bld, OSSL_PKEY_PARAM_DH_PRIV_LEN, priv_len)))
{
params = OSSL_PARAM_BLD_to_param(bld);
}
OSSL_PARAM_BLD_free(bld);
BN_free(g);
BN_free(p);
ctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL);
if (!params || !ctx ||
EVP_PKEY_fromdata_init(ctx) <= 0 ||
EVP_PKEY_fromdata(ctx, &this->key, EVP_PKEY_KEY_PARAMETERS, params) <= 0)
{
EVP_PKEY_CTX_free(ctx);
OSSL_PARAM_free(params);
destroy(this);
return NULL;
}
OSSL_PARAM_free(params);
EVP_PKEY_CTX_free(ctx);
ctx = EVP_PKEY_CTX_new(this->key, NULL);
if (!ctx ||
EVP_PKEY_keygen_init(ctx) <= 0 ||
EVP_PKEY_generate(ctx, &this->key) <= 0)
{
EVP_PKEY_CTX_free(ctx);
destroy(this);
return NULL;
}
EVP_PKEY_CTX_free(ctx);
#else /* OPENSSL_VERSION_NUMBER */
this->dh = DH_new();
this->pub_key = BN_new();
if (!DH_set0_pqg(this->dh, p, NULL, g))
{
BN_free(g);
BN_free(p);
destroy(this);
return NULL;
}
if (priv_len)
{
#if defined(OPENSSL_IS_BORINGSSL) && \
(!defined(BORINGSSL_API_VERSION) || BORINGSSL_API_VERSION < 11)
this->dh->priv_length = priv_len;
#else
if (!DH_set_length(this->dh, priv_len))
{
destroy(this);
return NULL;
}
#endif
}
if (!DH_generate_key(this->dh))
{
destroy(this);
return NULL;
}
DH_get0_key(this->dh, NULL, &privkey);
DBG2(DBG_LIB, "size of DH secret exponent: %d bits", BN_num_bits(privkey));
#endif /* OPENSSL_VERSION_NUMBER */
return &this->public;
}

View File

@ -23,6 +23,15 @@
#if OPENSSL_VERSION_NUMBER < 0x1010000fL
#include <openssl/bn.h>
#elif OPENSSL_VERSION_NUMBER >= 0x30000000L
#include <openssl/bn.h>
#include <openssl/param_build.h>
#include <openssl/core_names.h>
#endif
#if OPENSSL_VERSION_NUMBER < 0x30000000L
#define EVP_PKEY_set1_encoded_public_key EVP_PKEY_set1_tls_encodedpoint
#define EVP_PKEY_get1_encoded_public_key EVP_PKEY_get1_tls_encodedpoint
#endif
#include "openssl_ec_diffie_hellman.h"
@ -51,6 +60,11 @@ struct private_openssl_ec_diffie_hellman_t {
*/
EVP_PKEY *key;
/**
* Public key provided by peer
*/
EVP_PKEY *pub;
/**
* EC group
*/
@ -186,50 +200,35 @@ error:
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;
}
pub = EVP_PKEY_new();
if (!pub)
if (!this->pub)
{
goto error;
this->pub = EVP_PKEY_new();
}
#if OPENSSL_VERSION_NUMBER < 0x1010000fL
if (!chunk2ecp(this->ec_group, value, pub))
if (!chunk2ecp(this->ec_group, value, this->pub))
{
DBG1(DBG_LIB, "ECDH public value is malformed");
goto error;
return FALSE;
}
#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)
if (EVP_PKEY_copy_parameters(this->pub, this->key) <= 0 ||
EVP_PKEY_set1_encoded_public_key(this->pub, value.ptr, value.len) <= 0)
{
DBG1(DBG_LIB, "ECDH public value is malformed");
goto error;
return FALSE;
}
#endif
if (!openssl_compute_shared_key(this->key, pub, &this->shared_secret))
{
DBG1(DBG_LIB, "ECDH shared secret computation failed");
goto error;
}
this->computed = TRUE;
error:
EVP_PKEY_free(pub);
return this->computed;
chunk_clear(&this->shared_secret);
return TRUE;
}
METHOD(diffie_hellman_t, get_my_public_value, bool,
@ -242,7 +241,7 @@ METHOD(diffie_hellman_t, get_my_public_value, bool,
/* 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);
pub.len = EVP_PKEY_get1_encoded_public_key(this->key, &pub.ptr);
if (pub.len != 0)
{
*value = chunk_clone(chunk_skip(pub, 1));
@ -253,59 +252,13 @@ METHOD(diffie_hellman_t, get_my_public_value, bool,
#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;
priv = BN_bin2bn(value.ptr, value.len, NULL);
if (!priv)
{
goto error;
}
pub = EC_POINT_new(this->ec_group);
if (!pub)
{
goto error;
}
if (EC_POINT_mul(this->ec_group, pub, priv, NULL, NULL, NULL) != 1)
{
goto error;
}
key = EC_KEY_new();
if (!key || !EC_KEY_set_group(key, this->ec_group))
{
goto error;
}
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:
EC_POINT_free(pub);
BN_free(priv);
EC_KEY_free(key);
return ret;
}
METHOD(diffie_hellman_t, get_shared_secret, bool,
private_openssl_ec_diffie_hellman_t *this, chunk_t *secret)
{
if (!this->computed)
if (!this->shared_secret.len &&
!openssl_compute_shared_key(this->key, this->pub, &this->shared_secret))
{
DBG1(DBG_LIB, "ECDH shared secret computation failed");
return FALSE;
}
*secret = chunk_clone(this->shared_secret);
@ -318,15 +271,6 @@ METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t,
return this->group;
}
METHOD(diffie_hellman_t, destroy, void,
private_openssl_ec_diffie_hellman_t *this)
{
EC_GROUP_free(this->ec_group);
EVP_PKEY_free(this->key);
chunk_clear(&this->shared_secret);
free(this);
}
/*
* Described in header
*/
@ -360,21 +304,151 @@ int openssl_ecdh_group_to_nid(diffie_hellman_group_t group)
}
}
/**
* Parse the given private key as BIGNUM and calculate the corresponding public
* key as EC_POINT.
*/
static bool get_keypair(EC_GROUP *group, chunk_t value, BIGNUM **priv,
EC_POINT **pub)
{
*priv = BN_bin2bn(value.ptr, value.len, NULL);
*pub = EC_POINT_new(group);
return *priv && *pub &&
EC_POINT_mul(group, *pub, *priv, NULL, NULL, NULL) == 1;
}
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
/**
* Convert the given EC_POINT to a chunk.
*/
static bool ecp2chunk(EC_GROUP *group, EC_POINT *point, chunk_t *chunk)
{
BN_CTX *ctx = BN_CTX_new();
if (ctx)
{
chunk->len = EC_POINT_point2buf(group, point,
POINT_CONVERSION_UNCOMPRESSED,
&chunk->ptr, ctx);
}
BN_CTX_free(ctx);
return chunk->len;
}
METHOD(diffie_hellman_t, set_private_value, bool,
private_openssl_ec_diffie_hellman_t *this, chunk_t value)
{
BIGNUM *priv = NULL;
EC_POINT *pub = NULL;
chunk_t pub_chunk = chunk_empty;
const char *name;
OSSL_PARAM_BLD *bld;
OSSL_PARAM *params = NULL;
EVP_PKEY_CTX *ctx;
EVP_PKEY *key = NULL;
bool ret = FALSE;
if (!get_keypair(this->ec_group, value, &priv, &pub) ||
!ecp2chunk(this->ec_group, pub, &pub_chunk))
{
EC_POINT_free(pub);
BN_free(priv);
return FALSE;
}
EC_POINT_free(pub);
name = OSSL_EC_curve_nid2name(openssl_ecdh_group_to_nid(this->group));
bld = OSSL_PARAM_BLD_new();
if (name && bld &&
OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME,
(char*)name, 0) &&
OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY,
pub_chunk.ptr, pub_chunk.len) &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, priv))
{
params = OSSL_PARAM_BLD_to_param(bld);
}
OSSL_PARAM_BLD_free(bld);
chunk_free(&pub_chunk);
BN_free(priv);
ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
if (params && ctx &&
EVP_PKEY_fromdata_init(ctx) > 0 &&
EVP_PKEY_fromdata(ctx, &key, EVP_PKEY_KEYPAIR, params) > 0)
{
EVP_PKEY_free(this->key);
this->key = key;
ret = TRUE;
}
EVP_PKEY_CTX_free(ctx);
OSSL_PARAM_free(params);
return ret;
}
#else /* OPENSSL_VERSION_NUMBER */
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;
if (!get_keypair(this->ec_group, value, &priv, &pub))
{
goto error;
}
key = EC_KEY_new();
if (!key || !EC_KEY_set_group(key, this->ec_group))
{
goto error;
}
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:
EC_POINT_free(pub);
BN_free(priv);
EC_KEY_free(key);
return ret;
}
#endif /* OPENSSL_VERSION_NUMBER */
METHOD(diffie_hellman_t, destroy, void,
private_openssl_ec_diffie_hellman_t *this)
{
EC_GROUP_free(this->ec_group);
EVP_PKEY_free(this->key);
EVP_PKEY_free(this->pub);
chunk_clear(&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;
EC_KEY *key = NULL;
int curve;
curve = openssl_ecdh_group_to_nid(group);
if (curve)
{
key = EC_KEY_new_by_curve_name(curve);
}
if (!key)
if (!curve)
{
return NULL;
}
@ -391,17 +465,25 @@ 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)),
);
/* generate an EC private (public) key */
if (!EC_KEY_generate_key(key))
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
this->ec_group = EC_GROUP_new_by_curve_name(curve);
this->key = EVP_EC_gen(OSSL_EC_curve_nid2name(curve));
if (!this->key)
{
destroy(this);
return NULL;
}
#else
EC_KEY *key = EC_KEY_new_by_curve_name(curve);
if (!key || !EC_KEY_generate_key(key))
{
EC_KEY_free(key);
destroy(this);
return NULL;
}
this->ec_group = EC_GROUP_dup(EC_KEY_get0_group(key));
this->key = EVP_PKEY_new();
if (!this->key || !EVP_PKEY_assign_EC_KEY(this->key, key))
{
@ -409,6 +491,8 @@ openssl_ec_diffie_hellman_t *openssl_ec_diffie_hellman_create(diffie_hellman_gro
destroy(this);
return NULL;
}
#endif
return &this->public;
}

View File

@ -46,7 +46,7 @@ struct private_openssl_ec_private_key_t {
/**
* EC key object
*/
EC_KEY *ec;
EVP_PKEY *key;
/**
* TRUE if the key is from an OpenSSL ENGINE and might not be readable
@ -59,25 +59,78 @@ struct private_openssl_ec_private_key_t {
refcount_t ref;
};
/* from ec public key */
bool openssl_ec_fingerprint(EC_KEY *ec, cred_encoding_type_t type, chunk_t *fp);
/* from openssl_ec_public_key */
bool openssl_check_ec_key_curve(EVP_PKEY *key, int nid_curve);
/**
* Build a DER encoded signature as in RFC 3279
*/
static bool build_der_signature(private_openssl_ec_private_key_t *this,
int nid_hash, chunk_t data, chunk_t *signature)
{
EVP_MD_CTX *ctx;
const EVP_MD *md;
md = EVP_get_digestbynid(nid_hash);
if (!md)
{
return FALSE;
}
*signature = chunk_alloc(EVP_PKEY_size(this->key));
ctx = EVP_MD_CTX_create();
if (!ctx ||
EVP_DigestSignInit(ctx, NULL, md, NULL, this->key) <= 0 ||
EVP_DigestSignUpdate(ctx, data.ptr, data.len) <= 0 ||
EVP_DigestSignFinal(ctx, signature->ptr, &signature->len) != 1)
{
chunk_free(signature);
EVP_MD_CTX_destroy(ctx);
return FALSE;
}
EVP_MD_CTX_destroy(ctx);
return TRUE;
}
/**
* Build a signature as in RFC 4754
*/
static bool build_signature(private_openssl_ec_private_key_t *this,
chunk_t hash, chunk_t *signature)
int nid_hash, chunk_t data, chunk_t *signature)
{
const BIGNUM *r, *s;
EVP_PKEY_CTX *ctx;
ECDSA_SIG *sig;
const BIGNUM *r, *s;
const u_char *p;
chunk_t der_sig;
bool built = FALSE;
sig = ECDSA_do_sign(hash.ptr, hash.len, this->ec);
if (!nid_hash)
{ /* EVP_DigestSign*() has issues with NULL EVP_MD */
der_sig = chunk_alloc(EVP_PKEY_size(this->key));
ctx = EVP_PKEY_CTX_new(this->key, NULL);
if (!ctx ||
EVP_PKEY_sign_init(ctx) <= 0 ||
EVP_PKEY_sign(ctx, der_sig.ptr, &der_sig.len, data.ptr, data.len) <= 0)
{
chunk_free(&der_sig);
EVP_PKEY_CTX_free(ctx);
return FALSE;
}
EVP_PKEY_CTX_free(ctx);
}
else if (!build_der_signature(this, nid_hash, data, &der_sig))
{
return FALSE;
}
/* extract r and s from the DER-encoded signature */
p = der_sig.ptr;
sig = d2i_ECDSA_SIG(NULL, &p, der_sig.len);
chunk_free(&der_sig);
if (sig)
{
ECDSA_SIG_get0(sig, &r, &s);
/* concatenate BNs r/s to a signature chunk */
built = openssl_bn_cat(EC_FIELD_ELEMENT_LEN(EC_KEY_get0_group(this->ec)),
built = openssl_bn_cat((EVP_PKEY_bits(this->key) + 7) / 8,
r, s, signature);
ECDSA_SIG_free(sig);
}
@ -91,62 +144,13 @@ static bool build_curve_signature(private_openssl_ec_private_key_t *this,
signature_scheme_t scheme, int nid_hash,
int nid_curve, chunk_t data, chunk_t *signature)
{
const EC_GROUP *my_group;
EC_GROUP *req_group;
chunk_t hash;
bool built;
req_group = EC_GROUP_new_by_curve_name(nid_curve);
if (!req_group)
if (!openssl_check_ec_key_curve(this->key, nid_curve))
{
DBG1(DBG_LIB, "signature scheme %N not supported in EC (required curve "
"not supported)", signature_scheme_names, scheme);
return FALSE;
}
my_group = EC_KEY_get0_group(this->ec);
if (EC_GROUP_cmp(my_group, req_group, NULL) != 0)
{
DBG1(DBG_LIB, "signature scheme %N not supported by private key",
DBG1(DBG_LIB, "signature scheme %N not supported by key",
signature_scheme_names, scheme);
return FALSE;
}
EC_GROUP_free(req_group);
if (!openssl_hash_chunk(nid_hash, data, &hash))
{
return FALSE;
}
built = build_signature(this, hash, signature);
chunk_free(&hash);
return built;
}
/**
* Build a DER encoded signature as in RFC 3279
*/
static bool build_der_signature(private_openssl_ec_private_key_t *this,
int hash_nid, chunk_t data, chunk_t *signature)
{
chunk_t hash, sig;
int siglen = 0;
bool built;
if (!openssl_hash_chunk(hash_nid, data, &hash))
{
return FALSE;
}
sig = chunk_alloc(ECDSA_size(this->ec));
built = ECDSA_sign(0, hash.ptr, hash.len, sig.ptr, &siglen, this->ec) == 1;
sig.len = siglen;
if (built)
{
*signature = sig;
}
else
{
free(sig.ptr);
}
free(hash.ptr);
return built;
return build_signature(this, nid_hash, data, signature);
}
METHOD(private_key_t, sign, bool,
@ -156,7 +160,7 @@ METHOD(private_key_t, sign, bool,
switch (scheme)
{
case SIGN_ECDSA_WITH_NULL:
return build_signature(this, data, signature);
return build_signature(this, 0, data, signature);
case SIGN_ECDSA_WITH_SHA1_DER:
return build_der_signature(this, NID_sha1, data, signature);
case SIGN_ECDSA_WITH_SHA256_DER:
@ -192,7 +196,7 @@ METHOD(private_key_t, decrypt, bool,
METHOD(private_key_t, get_keysize, int,
private_openssl_ec_private_key_t *this)
{
return EC_GROUP_get_degree(EC_KEY_get0_group(this->ec));
return EVP_PKEY_bits(this->key);
}
METHOD(private_key_t, get_type, key_type_t,
@ -206,12 +210,8 @@ METHOD(private_key_t, get_public_key, public_key_t*,
{
public_key_t *public;
chunk_t key;
u_char *p;
key = chunk_alloc(i2d_EC_PUBKEY(this->ec, NULL));
p = key.ptr;
i2d_EC_PUBKEY(this->ec, &p);
key = openssl_i2chunk(PUBKEY, this->key);
public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ECDSA,
BUILD_BLOB_ASN1_DER, key, BUILD_END);
free(key.ptr);
@ -222,20 +222,17 @@ METHOD(private_key_t, get_fingerprint, bool,
private_openssl_ec_private_key_t *this, cred_encoding_type_t type,
chunk_t *fingerprint)
{
return openssl_ec_fingerprint(this->ec, type, fingerprint);
return openssl_fingerprint(this->key, type, fingerprint);
}
METHOD(private_key_t, get_encoding, bool,
private_openssl_ec_private_key_t *this, cred_encoding_type_t type,
chunk_t *encoding)
{
u_char *p;
if (this->engine)
{
return FALSE;
}
switch (type)
{
case PRIVKEY_ASN1_DER:
@ -243,9 +240,7 @@ METHOD(private_key_t, get_encoding, bool,
{
bool success = TRUE;
*encoding = chunk_alloc(i2d_ECPrivateKey(this->ec, NULL));
p = encoding->ptr;
i2d_ECPrivateKey(this->ec, &p);
*encoding = openssl_i2chunk(PrivateKey, this->key);
if (type == PRIVKEY_PEM)
{
@ -275,10 +270,10 @@ METHOD(private_key_t, destroy, void,
{
if (ref_put(&this->ref))
{
if (this->ec)
if (this->key)
{
lib->encoding->clear_cache(lib->encoding, this->ec);
EC_KEY_free(this->ec);
lib->encoding->clear_cache(lib->encoding, this->key);
EVP_PKEY_free(this->key);
}
free(this);
}
@ -287,7 +282,7 @@ METHOD(private_key_t, destroy, void,
/**
* Internal generic constructor
*/
static private_openssl_ec_private_key_t *create_empty(void)
static private_openssl_ec_private_key_t *create_internal(EVP_PKEY *key)
{
private_openssl_ec_private_key_t *this;
@ -309,6 +304,7 @@ static private_openssl_ec_private_key_t *create_empty(void)
},
},
.ref = 1,
.key = key,
);
return this;
@ -320,16 +316,13 @@ static private_openssl_ec_private_key_t *create_empty(void)
private_key_t *openssl_ec_private_key_create(EVP_PKEY *key, bool engine)
{
private_openssl_ec_private_key_t *this;
EC_KEY *ec;
ec = EVP_PKEY_get1_EC_KEY(key);
EVP_PKEY_free(key);
if (!ec)
if (EVP_PKEY_base_id(key) != EVP_PKEY_EC)
{
EVP_PKEY_free(key);
return NULL;
}
this = create_empty();
this->ec = ec;
this = create_internal(key);
this->engine = engine;
return &this->public.key;
}
@ -341,6 +334,7 @@ openssl_ec_private_key_t *openssl_ec_private_key_gen(key_type_t type,
va_list args)
{
private_openssl_ec_private_key_t *this;
EVP_PKEY *key = NULL;
u_int key_size = 0;
while (TRUE)
@ -361,32 +355,58 @@ openssl_ec_private_key_t *openssl_ec_private_key_gen(key_type_t type,
{
return NULL;
}
this = create_empty();
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
switch (key_size)
{
case 256:
this->ec = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
key = EVP_EC_gen("P-256");
break;
case 384:
this->ec = EC_KEY_new_by_curve_name(NID_secp384r1);
key = EVP_EC_gen("P-384");
break;
case 521:
this->ec = EC_KEY_new_by_curve_name(NID_secp521r1);
key = EVP_EC_gen("P-521");
break;
default:
DBG1(DBG_LIB, "EC private key size %d not supported", key_size);
destroy(this);
return NULL;
}
if (EC_KEY_generate_key(this->ec) != 1)
#else /* OPENSSL_VERSION_NUMBER */
EC_KEY *ec;
switch (key_size)
{
case 256:
ec = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
break;
case 384:
ec = EC_KEY_new_by_curve_name(NID_secp384r1);
break;
case 521:
ec = EC_KEY_new_by_curve_name(NID_secp521r1);
break;
default:
DBG1(DBG_LIB, "EC private key size %d not supported", key_size);
return NULL;
}
if (ec && EC_KEY_generate_key(ec) == 1)
{
key = EVP_PKEY_new();
if (!EVP_PKEY_assign_EC_KEY(key, ec))
{
EC_KEY_free(ec);
EVP_PKEY_free(key);
key = NULL;
}
}
#endif /* OPENSSL_VERSION_NUMBER */
if (!key)
{
DBG1(DBG_LIB, "EC private key generation failed", key_size);
destroy(this);
return NULL;
}
/* encode as a named curve key (no parameters), uncompressed public key */
EC_KEY_set_asn1_flag(this->ec, OPENSSL_EC_NAMED_CURVE);
EC_KEY_set_conv_form(this->ec, POINT_CONVERSION_UNCOMPRESSED);
this = create_internal(key);
return &this->public;
}
@ -397,7 +417,8 @@ openssl_ec_private_key_t *openssl_ec_private_key_load(key_type_t type,
va_list args)
{
private_openssl_ec_private_key_t *this;
chunk_t par = chunk_empty, key = chunk_empty;
chunk_t par = chunk_empty, blob = chunk_empty;
EVP_PKEY *key = NULL;
while (TRUE)
{
@ -407,7 +428,7 @@ openssl_ec_private_key_t *openssl_ec_private_key_load(key_type_t type,
par = va_arg(args, chunk_t);
continue;
case BUILD_BLOB_ASN1_DER:
key = va_arg(args, chunk_t);
blob = va_arg(args, chunk_t);
continue;
case BUILD_END:
break;
@ -417,36 +438,46 @@ openssl_ec_private_key_t *openssl_ec_private_key_load(key_type_t type,
break;
}
this = create_empty();
if (par.ptr)
{
this->ec = d2i_ECParameters(NULL, (const u_char**)&par.ptr, par.len);
if (!this->ec)
/* for OpenSSL 3, the combination of d2i_KeyParams/d2i_PrivateKey, which
* are intended to replace the functions below, does currently not work
* because OpenSSL does not pass the internal EC_KEY that stores the
* parameters from the first call to the call that parses the private
* key. however, since parsing PKCS#8 is the only use case for this and
* OpenSSL 3 parses this format directly, there isn't really any need
* for it anyway */
#if OPENSSL_VERSION_NUMBER < 0x30000000L
EC_KEY *ec;
ec = d2i_ECParameters(NULL, (const u_char**)&par.ptr, par.len);
if (ec && d2i_ECPrivateKey(&ec, (const u_char**)&blob.ptr, blob.len))
{
goto error;
key = EVP_PKEY_new();
if (!EVP_PKEY_assign_EC_KEY(key, ec))
{
EC_KEY_free(ec);
EVP_PKEY_free(key);
key = NULL;
}
}
if (!d2i_ECPrivateKey(&this->ec, (const u_char**)&key.ptr, key.len))
else
{
goto error;
EC_KEY_free(ec);
}
#endif
}
else
{
this->ec = d2i_ECPrivateKey(NULL, (const u_char**)&key.ptr, key.len);
if (!this->ec)
{
goto error;
}
key = d2i_PrivateKey(EVP_PKEY_EC, NULL, (const u_char**)&blob.ptr,
blob.len);
}
if (!EC_KEY_check_key(this->ec))
{
goto error;
}
return &this->public;
error:
destroy(this);
return NULL;
if (!key)
{
return NULL;
}
this = create_internal(key);
return &this->public;
}
#endif /* OPENSSL_NO_ECDSA */

View File

@ -27,6 +27,10 @@
#include <openssl/ecdsa.h>
#include <openssl/x509.h>
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
#include <openssl/core_names.h>
#endif
#if OPENSSL_VERSION_NUMBER < 0x10100000L
OPENSSL_KEY_FALLBACK(ECDSA_SIG, r, s)
#endif
@ -45,7 +49,7 @@ struct private_openssl_ec_public_key_t {
/**
* EC key object
*/
EC_KEY *ec;
EVP_PKEY *key;
/**
* reference counter
@ -53,14 +57,48 @@ struct private_openssl_ec_public_key_t {
refcount_t ref;
};
/**
* Verification of a DER encoded signature as in RFC 3279
*/
static bool verify_der_signature(private_openssl_ec_public_key_t *this,
int nid_hash, chunk_t data, chunk_t signature)
{
EVP_MD_CTX *ctx;
const EVP_MD *md;
/* remove any preceding 0-bytes from signature */
while (signature.len && signature.ptr[0] == 0x00)
{
signature = chunk_skip(signature, 1);
}
md = EVP_get_digestbynid(nid_hash);
if (!md)
{
return FALSE;
}
ctx = EVP_MD_CTX_create();
if (!ctx ||
EVP_DigestVerifyInit(ctx, NULL, md, NULL, this->key) <= 0 ||
EVP_DigestVerifyUpdate(ctx, data.ptr, data.len) <= 0 ||
EVP_DigestVerifyFinal(ctx, signature.ptr, signature.len) != 1)
{
EVP_MD_CTX_destroy(ctx);
return FALSE;
}
EVP_MD_CTX_destroy(ctx);
return TRUE;
}
/**
* Verification of a signature as in RFC 4754
*/
static bool verify_signature(private_openssl_ec_public_key_t *this,
chunk_t hash, chunk_t signature)
int nid_hash, chunk_t data, chunk_t signature)
{
EVP_PKEY_CTX *ctx;
BIGNUM *r, *s;
ECDSA_SIG *sig;
chunk_t der_sig;
bool valid = FALSE;
sig = ECDSA_SIG_new();
@ -77,13 +115,73 @@ static bool verify_signature(private_openssl_ec_public_key_t *this,
}
if (ECDSA_SIG_set0(sig, r, s))
{
valid = (ECDSA_do_verify(hash.ptr, hash.len, sig, this->ec) == 1);
der_sig = openssl_i2chunk(ECDSA_SIG, sig);
if (!nid_hash)
{ /* EVP_DigestVerify*() has issues with NULL EVP_MD */
ctx = EVP_PKEY_CTX_new(this->key, NULL);
valid = ctx && EVP_PKEY_verify_init(ctx) > 0 &&
EVP_PKEY_verify(ctx, der_sig.ptr, der_sig.len,
data.ptr, data.len) > 0;
EVP_PKEY_CTX_free(ctx);
}
else
{
valid = verify_der_signature(this, nid_hash, data, der_sig);
}
chunk_free(&der_sig);
}
ECDSA_SIG_free(sig);
}
return valid;
}
/**
* Check that the given key's curve matches a specific one. Also used by
* private key.
*/
bool openssl_check_ec_key_curve(EVP_PKEY *key, int nid_curve)
{
EC_GROUP *req_group, *my_group = NULL;
bool matches = FALSE;
req_group = EC_GROUP_new_by_curve_name(nid_curve);
if (!req_group)
{
goto error;
}
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
char name[BUF_LEN];
OSSL_PARAM params[] = {
OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, name, sizeof(name)),
OSSL_PARAM_END,
};
if (!EVP_PKEY_get_group_name(key, name, sizeof(name), NULL))
{
goto error;
}
my_group = EC_GROUP_new_from_params(params, NULL, NULL);
#elif OPENSSL_VERSION_NUMBER >= 0x1010000fL
EC_KEY *ec = EVP_PKEY_get0_EC_KEY(key);
my_group = EC_GROUP_dup(EC_KEY_get0_group(ec));
#else
EC_KEY *ec = EVP_PKEY_get1_EC_KEY(key);
my_group = EC_GROUP_dup(EC_KEY_get0_group(ec));
EC_KEY_free(ec);
#endif
if (EC_GROUP_cmp(my_group, req_group, NULL) == 0)
{
matches = TRUE;
}
error:
EC_GROUP_free(my_group);
EC_GROUP_free(req_group);
return matches;
}
/**
* Verify a RFC 4754 signature for a specified curve and hash algorithm
*/
@ -91,56 +189,13 @@ static bool verify_curve_signature(private_openssl_ec_public_key_t *this,
signature_scheme_t scheme, int nid_hash,
int nid_curve, chunk_t data, chunk_t signature)
{
const EC_GROUP *my_group;
EC_GROUP *req_group;
chunk_t hash;
bool valid;
req_group = EC_GROUP_new_by_curve_name(nid_curve);
if (!req_group)
if (!openssl_check_ec_key_curve(this->key, nid_curve))
{
DBG1(DBG_LIB, "signature scheme %N not supported in EC (required curve "
"not supported)", signature_scheme_names, scheme);
return FALSE;
}
my_group = EC_KEY_get0_group(this->ec);
if (EC_GROUP_cmp(my_group, req_group, NULL) != 0)
{
DBG1(DBG_LIB, "signature scheme %N not supported by private key",
DBG1(DBG_LIB, "signature scheme %N not supported by key",
signature_scheme_names, scheme);
return FALSE;
}
EC_GROUP_free(req_group);
if (!openssl_hash_chunk(nid_hash, data, &hash))
{
return FALSE;
}
valid = verify_signature(this, hash, signature);
chunk_free(&hash);
return valid;
}
/**
* Verification of a DER encoded signature as in RFC 3279
*/
static bool verify_der_signature(private_openssl_ec_public_key_t *this,
int nid_hash, chunk_t data, chunk_t signature)
{
chunk_t hash;
bool valid = FALSE;
/* remove any preceding 0-bytes from signature */
while (signature.len && signature.ptr[0] == 0x00)
{
signature = chunk_skip(signature, 1);
}
if (openssl_hash_chunk(nid_hash, data, &hash))
{
valid = ECDSA_verify(0, hash.ptr, hash.len,
signature.ptr, signature.len, this->ec) == 1;
free(hash.ptr);
}
return valid;
return verify_signature(this, nid_hash, data, signature);
}
METHOD(public_key_t, get_type, key_type_t,
@ -164,7 +219,7 @@ METHOD(public_key_t, verify, bool,
case SIGN_ECDSA_WITH_SHA512_DER:
return verify_der_signature(this, NID_sha512, data, signature);
case SIGN_ECDSA_WITH_NULL:
return verify_signature(this, data, signature);
return verify_signature(this, 0, data, signature);
case SIGN_ECDSA_256:
return verify_curve_signature(this, scheme, NID_sha256,
NID_X9_62_prime256v1, data, signature);
@ -192,56 +247,14 @@ METHOD(public_key_t, encrypt, bool,
METHOD(public_key_t, get_keysize, int,
private_openssl_ec_public_key_t *this)
{
return EC_GROUP_get_degree(EC_KEY_get0_group(this->ec));
}
/**
* Calculate fingerprint from a EC_KEY, also used in ec private key.
*/
bool openssl_ec_fingerprint(EC_KEY *ec, cred_encoding_type_t type, chunk_t *fp)
{
hasher_t *hasher;
chunk_t key;
u_char *p;
if (lib->encoding->get_cache(lib->encoding, type, ec, fp))
{
return TRUE;
}
switch (type)
{
case KEYID_PUBKEY_SHA1:
key = chunk_alloc(i2o_ECPublicKey(ec, NULL));
p = key.ptr;
i2o_ECPublicKey(ec, &p);
break;
case KEYID_PUBKEY_INFO_SHA1:
key = chunk_alloc(i2d_EC_PUBKEY(ec, NULL));
p = key.ptr;
i2d_EC_PUBKEY(ec, &p);
break;
default:
return FALSE;
}
hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
if (!hasher || !hasher->allocate_hash(hasher, key, fp))
{
DBG1(DBG_LIB, "SHA1 hash algorithm not supported, fingerprinting failed");
DESTROY_IF(hasher);
free(key.ptr);
return FALSE;
}
hasher->destroy(hasher);
free(key.ptr);
lib->encoding->cache(lib->encoding, type, ec, *fp);
return TRUE;
return EVP_PKEY_bits(this->key);
}
METHOD(public_key_t, get_fingerprint, bool,
private_openssl_ec_public_key_t *this, cred_encoding_type_t type,
chunk_t *fingerprint)
{
return openssl_ec_fingerprint(this->ec, type, fingerprint);
return openssl_fingerprint(this->key, type, fingerprint);
}
METHOD(public_key_t, get_encoding, bool,
@ -249,11 +262,8 @@ METHOD(public_key_t, get_encoding, bool,
chunk_t *encoding)
{
bool success = TRUE;
u_char *p;
*encoding = chunk_alloc(i2d_EC_PUBKEY(this->ec, NULL));
p = encoding->ptr;
i2d_EC_PUBKEY(this->ec, &p);
*encoding = openssl_i2chunk(PUBKEY, this->key);
if (type != PUBKEY_SPKI_ASN1_DER)
{
@ -279,21 +289,45 @@ METHOD(public_key_t, destroy, void,
{
if (ref_put(&this->ref))
{
if (this->ec)
if (this->key)
{
lib->encoding->clear_cache(lib->encoding, this->ec);
EC_KEY_free(this->ec);
lib->encoding->clear_cache(lib->encoding, this->key);
EVP_PKEY_free(this->key);
}
free(this);
}
}
/**
* Generic private constructor
* See header.
*/
static private_openssl_ec_public_key_t *create_empty()
openssl_ec_public_key_t *openssl_ec_public_key_load(key_type_t type,
va_list args)
{
private_openssl_ec_public_key_t *this;
chunk_t blob = chunk_empty;
EVP_PKEY *key;
while (TRUE)
{
switch (va_arg(args, builder_part_t))
{
case BUILD_BLOB_ASN1_DER:
blob = va_arg(args, chunk_t);
continue;
case BUILD_END:
break;
default:
return NULL;
}
break;
}
key = d2i_PUBKEY(NULL, (const u_char**)&blob.ptr, blob.len);
if (!key || EVP_PKEY_base_id(key) != EVP_PKEY_EC)
{
EVP_PKEY_free(key);
return NULL;
}
INIT(this,
.public = {
@ -311,47 +345,9 @@ static private_openssl_ec_public_key_t *create_empty()
},
},
.ref = 1,
.key = key,
);
return this;
}
/**
* See header.
*/
openssl_ec_public_key_t *openssl_ec_public_key_load(key_type_t type,
va_list args)
{
private_openssl_ec_public_key_t *this;
chunk_t blob = chunk_empty;
if (type != KEY_ECDSA)
{
return NULL;
}
while (TRUE)
{
switch (va_arg(args, builder_part_t))
{
case BUILD_BLOB_ASN1_DER:
blob = va_arg(args, chunk_t);
continue;
case BUILD_END:
break;
default:
return NULL;
}
break;
}
this = create_empty();
this->ec = d2i_EC_PUBKEY(NULL, (const u_char**)&blob.ptr, blob.len);
if (!this->ec)
{
destroy(this);
return NULL;
}
return &this->public;
}
#endif /* OPENSSL_NO_ECDSA */
#endif /* OPENSSL_NO_ECDSA */

View File

@ -0,0 +1,214 @@
/*
* Copyright (C) 2008-2018 Tobias Brunner
* Copyright (C) 2008 Martin Willi
* 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.
*/
/* the ENGINE API has been deprecated with OpenSSL 3.0 (the provider API should
* be used instead) */
#define OPENSSL_SUPPRESS_DEPRECATED
#include <openssl/opensslv.h>
#include <openssl/opensslconf.h>
#include "openssl_engine.h"
#if !defined(OPENSSL_NO_ENGINE) && \
(OPENSSL_VERSION_NUMBER < 0x30000000L || !defined(OPENSSL_NO_DEPRECATED))
#include <openssl/engine.h>
#include "openssl_ec_private_key.h"
#include "openssl_ed_private_key.h"
#include "openssl_rsa_private_key.h"
/**
* Login to engine with a PIN specified for a keyid
*/
static bool login(ENGINE *engine, chunk_t keyid)
{
enumerator_t *enumerator;
shared_key_t *shared;
identification_t *id;
chunk_t key;
char pin[64];
bool found = FALSE, success = FALSE;
id = identification_create_from_encoding(ID_KEY_ID, keyid);
enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
SHARED_PIN, id, NULL);
while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
{
found = TRUE;
key = shared->get_key(shared);
if (snprintf(pin, sizeof(pin),
"%.*s", (int)key.len, key.ptr) >= sizeof(pin))
{
continue;
}
if (ENGINE_ctrl_cmd_string(engine, "PIN", pin, 0))
{
success = TRUE;
break;
}
else
{
DBG1(DBG_CFG, "setting PIN on engine failed");
}
}
enumerator->destroy(enumerator);
id->destroy(id);
if (!found)
{
DBG1(DBG_CFG, "no PIN found for %#B", &keyid);
}
return success;
}
/*
* Described in header
*/
private_key_t *openssl_private_key_connect(key_type_t type, va_list args)
{
char *engine_id = NULL;
char keyname[BUF_LEN];
chunk_t keyid = chunk_empty;
EVP_PKEY *key;
ENGINE *engine;
int slot = -1;
while (TRUE)
{
switch (va_arg(args, builder_part_t))
{
case BUILD_PKCS11_KEYID:
keyid = va_arg(args, chunk_t);
continue;
case BUILD_PKCS11_SLOT:
slot = va_arg(args, int);
continue;
case BUILD_PKCS11_MODULE:
engine_id = va_arg(args, char*);
continue;
case BUILD_END:
break;
default:
return NULL;
}
break;
}
if (!keyid.len)
{
return NULL;
}
memset(keyname, 0, sizeof(keyname));
if (slot != -1)
{
snprintf(keyname, sizeof(keyname), "%d:", slot);
}
if (sizeof(keyname) - strlen(keyname) <= keyid.len * 2 + 1)
{
return NULL;
}
chunk_to_hex(keyid, keyname + strlen(keyname), FALSE);
if (!engine_id)
{
engine_id = lib->settings->get_str(lib->settings,
"%s.plugins.openssl.engine_id", "pkcs11", lib->ns);
}
engine = ENGINE_by_id(engine_id);
if (!engine)
{
DBG2(DBG_LIB, "engine '%s' is not available", engine_id);
return NULL;
}
if (!ENGINE_init(engine))
{
DBG1(DBG_LIB, "failed to initialize engine '%s'", engine_id);
ENGINE_free(engine);
return NULL;
}
ENGINE_free(engine);
if (!login(engine, keyid))
{
DBG1(DBG_LIB, "login to engine '%s' failed", engine_id);
ENGINE_finish(engine);
return NULL;
}
key = ENGINE_load_private_key(engine, keyname, NULL, NULL);
ENGINE_finish(engine);
if (!key)
{
DBG1(DBG_LIB, "failed to load private key with ID '%s' from "
"engine '%s'", keyname, engine_id);
return NULL;
}
switch (EVP_PKEY_base_id(key))
{
#ifndef OPENSSL_NO_RSA
case EVP_PKEY_RSA:
return openssl_rsa_private_key_create(key, TRUE);
#endif
#ifndef OPENSSL_NO_ECDSA
case EVP_PKEY_EC:
return openssl_ec_private_key_create(key, TRUE);
#endif
#if OPENSSL_VERSION_NUMBER >= 0x1010100fL && !defined(OPENSSL_NO_EC)
case EVP_PKEY_ED25519:
case EVP_PKEY_ED448:
return openssl_ed_private_key_create(key, TRUE);
#endif /* OPENSSL_VERSION_NUMBER */
default:
EVP_PKEY_free(key);
break;
}
return NULL;
}
/*
* Described in header
*/
void openssl_engine_deinit()
{
#if OPENSSL_VERSION_NUMBER < 0x10100000L
ENGINE_cleanup();
#endif
}
/*
* Described in header
*/
void openssl_engine_init()
{
#if OPENSSL_VERSION_NUMBER < 0x10100000L
/* activate support for hardware accelerators */
ENGINE_load_builtin_engines();
ENGINE_register_all_complete();
#endif
}
#else /* OPENSSL_NO_ENGINE */
private_key_t *openssl_private_key_connect(key_type_t type, va_list args)
{
return NULL;
}
void openssl_engine_deinit() {}
void openssl_engine_init() {}
#endif /* OPENSSL_NO_ENGINE */

View File

@ -0,0 +1,53 @@
/*
* Copyright (C) 2022 Tobias Brunner, codelabs GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Compatibility code for legacy ENGINE support.
*
* @defgroup openssl_engine openssl_engine
* @{ @ingroup openssl_p
*/
#ifndef OPENSSL_ENGINE_H_
#define OPENSSL_ENGINE_H_
#include <credentials/keys/private_key.h>
/**
* Load a private key from a token/ENGINE.
*
* @param type key type to load
* @param args build arguments
*/
private_key_t *openssl_private_key_connect(key_type_t type, va_list args);
/**
* Initialize ENGINE support.
*/
void openssl_engine_init();
/**
* Deinitialize ENGINE support.
*/
void openssl_engine_deinit();
#endif /** OPENSSL_ENGINE_H_ @}*/

View File

@ -42,6 +42,10 @@
#include <openssl/evp.h>
#include <openssl/hmac.h>
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
#include <openssl/core_names.h>
#endif
#include "openssl_hmac.h"
#include <crypto/mac.h>
@ -60,6 +64,19 @@ struct private_mac_t {
*/
mac_t public;
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
/**
* HMAC context
*/
EVP_MAC_CTX *hmac;
/**
* Base context because EVP_MAC_init() does not reset the internal state if
* no key is passed, so the above is a copy that's replaced with every
* reset that does not change the key
*/
EVP_MAC_CTX *hmac_base;
#else
/**
* Hasher to use
*/
@ -69,6 +86,7 @@ struct private_mac_t {
* Current HMAC context
*/
HMAC_CTX *hmac;
#endif
#if OPENSSL_VERSION_NUMBER < 0x10100000L
/**
@ -84,16 +102,20 @@ struct private_mac_t {
*/
static bool reset(private_mac_t *this, chunk_t key)
{
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
if (!key.len || EVP_MAC_init(this->hmac_base, key.ptr, key.len, NULL))
{
EVP_MAC_CTX_free(this->hmac);
this->hmac = EVP_MAC_CTX_dup(this->hmac_base);
return TRUE;
}
#else
if (HMAC_Init_ex(this->hmac, key.ptr, key.len, this->hasher, NULL))
{
return TRUE;
}
return FALSE;
#else /* OPENSSL_VERSION_NUMBER < 1.0 */
HMAC_Init_ex(this->hmac, key.ptr, key.len, this->hasher, NULL);
return TRUE;
#endif
return FALSE;
}
METHOD(mac_t, set_key, bool,
@ -104,46 +126,62 @@ METHOD(mac_t, set_key, bool,
* use a lengthy string in case there is a limit in FIPS-mode */
key = chunk_from_str("00000000000000000000000000000000");
}
return reset(this, key);
}
METHOD(mac_t, get_mac, bool,
private_mac_t *this, chunk_t data, uint8_t *out)
{
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
if (!HMAC_Update(this->hmac, data.ptr, data.len))
if (!reset(this, key))
{
return FALSE;
}
if (out == NULL)
{
return TRUE;
}
if (!HMAC_Final(this->hmac, out, NULL))
{
return FALSE;
}
#else /* OPENSSL_VERSION_NUMBER < 1.0 */
HMAC_Update(this->hmac, data.ptr, data.len);
if (out == NULL)
{
return TRUE;
}
HMAC_Final(this->hmac, out, NULL);
#endif
return reset(this, chunk_empty);
return TRUE;
}
METHOD(mac_t, get_mac_size, size_t,
private_mac_t *this)
{
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
return EVP_MAC_CTX_get_mac_size(this->hmac);
#else
return EVP_MD_size(this->hasher);
#endif
}
METHOD(mac_t, get_mac, bool,
private_mac_t *this, chunk_t data, uint8_t *out)
{
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
if (!EVP_MAC_update(this->hmac, data.ptr, data.len))
{
return FALSE;
}
#else
if (!HMAC_Update(this->hmac, data.ptr, data.len))
{
return FALSE;
}
#endif
if (!out)
{
return TRUE;
}
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
if (!EVP_MAC_final(this->hmac, out, NULL, get_mac_size(this)))
{
return FALSE;
}
#else
if (!HMAC_Final(this->hmac, out, NULL))
{
return FALSE;
}
#endif
return reset(this, chunk_empty);
}
METHOD(mac_t, destroy, void,
private_mac_t *this)
{
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
EVP_MAC_CTX_free(this->hmac_base);
EVP_MAC_CTX_free(this->hmac);
#elif OPENSSL_VERSION_NUMBER >= 0x10100000L
HMAC_CTX_free(this->hmac);
#else
HMAC_CTX_cleanup(&this->hmac_ctx);
@ -172,21 +210,41 @@ static mac_t *hmac_create(hash_algorithm_t algo)
.set_key = _set_key,
.destroy = _destroy,
},
.hasher = EVP_get_digestbyname(name),
);
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
OSSL_PARAM params[] = {
OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, name, 0),
OSSL_PARAM_END,
};
EVP_MAC *mac = EVP_MAC_fetch(NULL, "HMAC", NULL);
if (!mac)
{
free(this);
return NULL;
}
this->hmac_base = EVP_MAC_CTX_new(mac);
EVP_MAC_free(mac);
if (!this->hmac_base || !EVP_MAC_CTX_set_params(this->hmac_base, params))
{
free(this);
return NULL;
}
#else /* OPENSSL_VERSION_NUMBER */
this->hasher = EVP_get_digestbyname(name);
if (!this->hasher)
{
free(this);
return NULL;
}
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
this->hmac = HMAC_CTX_new();
#else
HMAC_CTX_init(&this->hmac_ctx);
this->hmac = &this->hmac_ctx;
#endif
#endif /* OPENSSL_VERSION_NUMBER */
/* make sure the underlying hash algorithm is supported */
if (!set_key(this, chunk_empty))

View File

@ -25,9 +25,6 @@
#include <openssl/conf.h>
#include <openssl/rand.h>
#include <openssl/crypto.h>
#ifndef OPENSSL_NO_ENGINE
#include <openssl/engine.h>
#endif
#ifndef OPENSSL_NO_ECDH
#include <openssl/ec.h>
#endif
@ -38,6 +35,7 @@
#include "openssl_plugin.h"
#include "openssl_util.h"
#include "openssl_crypter.h"
#include "openssl_engine.h"
#include "openssl_hasher.h"
#include "openssl_sha1_prf.h"
#include "openssl_diffie_hellman.h"
@ -160,42 +158,27 @@ static thread_value_t *cleanup;
*/
static void cleanup_thread(void *arg)
{
#if OPENSSL_VERSION_NUMBER >= 0x1000000fL
CRYPTO_THREADID tid;
CRYPTO_THREADID_set_numeric(&tid, (u_long)(uintptr_t)arg);
ERR_remove_thread_state(&tid);
#else
ERR_remove_state((u_long)(uintptr_t)arg);
#endif
}
/**
* Thread-ID callback function
* Callback for thread ID
*/
static u_long id_function(void)
static void threadid_function(CRYPTO_THREADID *threadid)
{
u_long id;
/* ensure the thread ID is never zero, otherwise OpenSSL might try to
* acquire locks recursively */
id = 1 + (u_long)thread_current_id();
/* cleanup a thread's state later if OpenSSL interacted with it */
cleanup->set(cleanup, (void*)(uintptr_t)id);
return id;
CRYPTO_THREADID_set_numeric(threadid, id);
}
#if OPENSSL_VERSION_NUMBER >= 0x1000000fL
/**
* Callback for thread ID
*/
static void threadid_function(CRYPTO_THREADID *threadid)
{
CRYPTO_THREADID_set_numeric(threadid, id_function());
}
#endif /* OPENSSL_VERSION_NUMBER */
/**
* initialize OpenSSL for multi-threaded use
*/
@ -205,14 +188,9 @@ static void threading_init()
cleanup = thread_value_create(cleanup_thread);
#if OPENSSL_VERSION_NUMBER >= 0x1000000fL
CRYPTO_THREADID_set_callback(threadid_function);
#else
CRYPTO_set_id_callback(id_function);
#endif
CRYPTO_set_locking_callback(locking_function);
CRYPTO_set_dynlock_create_callback(create_function);
CRYPTO_set_dynlock_lock_callback(lock_function);
CRYPTO_set_dynlock_destroy_callback(destroy_function);
@ -336,157 +314,6 @@ static private_key_t *openssl_private_key_load(key_type_t type, va_list args)
return NULL;
}
#ifndef OPENSSL_NO_ENGINE
/**
* Login to engine with a PIN specified for a keyid
*/
static bool login(ENGINE *engine, chunk_t keyid)
{
enumerator_t *enumerator;
shared_key_t *shared;
identification_t *id;
chunk_t key;
char pin[64];
bool found = FALSE, success = FALSE;
id = identification_create_from_encoding(ID_KEY_ID, keyid);
enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
SHARED_PIN, id, NULL);
while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
{
found = TRUE;
key = shared->get_key(shared);
if (snprintf(pin, sizeof(pin),
"%.*s", (int)key.len, key.ptr) >= sizeof(pin))
{
continue;
}
if (ENGINE_ctrl_cmd_string(engine, "PIN", pin, 0))
{
success = TRUE;
break;
}
else
{
DBG1(DBG_CFG, "setting PIN on engine failed");
}
}
enumerator->destroy(enumerator);
id->destroy(id);
if (!found)
{
DBG1(DBG_CFG, "no PIN found for %#B", &keyid);
}
return success;
}
#endif /* OPENSSL_NO_ENGINE */
/**
* Load private key via engine
*/
static private_key_t *openssl_private_key_connect(key_type_t type,
va_list args)
{
#ifndef OPENSSL_NO_ENGINE
char *engine_id = NULL;
char keyname[BUF_LEN];
chunk_t keyid = chunk_empty;
EVP_PKEY *key;
ENGINE *engine;
int slot = -1;
while (TRUE)
{
switch (va_arg(args, builder_part_t))
{
case BUILD_PKCS11_KEYID:
keyid = va_arg(args, chunk_t);
continue;
case BUILD_PKCS11_SLOT:
slot = va_arg(args, int);
continue;
case BUILD_PKCS11_MODULE:
engine_id = va_arg(args, char*);
continue;
case BUILD_END:
break;
default:
return NULL;
}
break;
}
if (!keyid.len)
{
return NULL;
}
memset(keyname, 0, sizeof(keyname));
if (slot != -1)
{
snprintf(keyname, sizeof(keyname), "%d:", slot);
}
if (sizeof(keyname) - strlen(keyname) <= keyid.len * 2 + 1)
{
return NULL;
}
chunk_to_hex(keyid, keyname + strlen(keyname), FALSE);
if (!engine_id)
{
engine_id = lib->settings->get_str(lib->settings,
"%s.plugins.openssl.engine_id", "pkcs11", lib->ns);
}
engine = ENGINE_by_id(engine_id);
if (!engine)
{
DBG2(DBG_LIB, "engine '%s' is not available", engine_id);
return NULL;
}
if (!ENGINE_init(engine))
{
DBG1(DBG_LIB, "failed to initialize engine '%s'", engine_id);
ENGINE_free(engine);
return NULL;
}
ENGINE_free(engine);
if (!login(engine, keyid))
{
DBG1(DBG_LIB, "login to engine '%s' failed", engine_id);
ENGINE_finish(engine);
return NULL;
}
key = ENGINE_load_private_key(engine, keyname, NULL, NULL);
ENGINE_finish(engine);
if (!key)
{
DBG1(DBG_LIB, "failed to load private key with ID '%s' from "
"engine '%s'", keyname, engine_id);
return NULL;
}
switch (EVP_PKEY_base_id(key))
{
#ifndef OPENSSL_NO_RSA
case EVP_PKEY_RSA:
return openssl_rsa_private_key_create(key, TRUE);
#endif
#ifndef OPENSSL_NO_ECDSA
case EVP_PKEY_EC:
return openssl_ec_private_key_create(key, TRUE);
#endif
#if OPENSSL_VERSION_NUMBER >= 0x1010100fL && !defined(OPENSSL_NO_EC)
case EVP_PKEY_ED25519:
case EVP_PKEY_ED448:
return openssl_ed_private_key_create(key, TRUE);
#endif /* OPENSSL_VERSION_NUMBER */
default:
EVP_PKEY_free(key);
break;
}
#endif /* OPENSSL_NO_ENGINE */
return NULL;
}
METHOD(plugin_t, get_name, char*,
private_openssl_plugin_t *this)
{
@ -615,7 +442,8 @@ METHOD(plugin_t, get_features, int,
PLUGIN_PROVIDE(XOF, XOF_SHAKE_128),
PLUGIN_PROVIDE(XOF, XOF_SHAKE_256),
#endif
#ifndef OPENSSL_NO_SHA1
#if !defined(OPENSSL_NO_SHA1) && \
(OPENSSL_VERSION_NUMBER < 0x30000000L || !defined(OPENSSL_NO_DEPRECATED))
/* keyed sha1 hasher (aka prf) */
PLUGIN_REGISTER(PRF, openssl_sha1_prf_create),
PLUGIN_PROVIDE(PRF, PRF_KEYED_SHA1),
@ -662,7 +490,7 @@ METHOD(plugin_t, get_features, int,
PLUGIN_PROVIDE(KDF, KDF_PRF_PLUS),
#endif
#endif /* OPENSSL_NO_HMAC */
#if (OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_AES)) || \
#if (!defined(OPENSSL_NO_AES)) || \
(OPENSSL_VERSION_NUMBER >= 0x1010000fL && !defined(OPENSSL_NO_CHACHA))
/* AEAD (AES GCM since 1.0.1, ChaCha20-Poly1305 since 1.1.0) */
PLUGIN_REGISTER(AEAD, openssl_aead_create),
@ -722,10 +550,8 @@ METHOD(plugin_t, get_features, int,
/* signature/encryption schemes */
PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_RSA_EMSA_PKCS1_NULL),
PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_NULL),
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_RSA_EMSA_PSS),
PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PSS),
#endif
#ifndef OPENSSL_NO_SHA1
PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_RSA_EMSA_PKCS1_SHA1),
PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_SHA1),
@ -886,6 +712,7 @@ METHOD(plugin_t, get_features, int,
METHOD(plugin_t, destroy, void,
private_openssl_plugin_t *this)
{
/* OpenSSL 1.1.0 cleans up itself at exit and while OPENSSL_cleanup() exists we
* can't call it as we couldn't re-initialize the library (as required by the
* unit tests and the Android app) */
@ -895,9 +722,7 @@ METHOD(plugin_t, destroy, void,
OBJ_cleanup();
#endif
EVP_cleanup();
#ifndef OPENSSL_NO_ENGINE
ENGINE_cleanup();
#endif /* OPENSSL_NO_ENGINE */
openssl_engine_deinit();
CRYPTO_cleanup_all_ex_data();
threading_cleanup();
ERR_free_strings();
@ -981,11 +806,7 @@ plugin_t *openssl_plugin_create()
OPENSSL_config(NULL);
#endif
OpenSSL_add_all_algorithms();
#ifndef OPENSSL_NO_ENGINE
/* activate support for hardware accelerators */
ENGINE_load_builtin_engines();
ENGINE_register_all_complete();
#endif /* OPENSSL_NO_ENGINE */
openssl_engine_init();
#endif /* OPENSSL_VERSION_NUMBER */
#if OPENSSL_VERSION_NUMBER >= 0x30000000L

View File

@ -31,6 +31,11 @@
#include <openssl/evp.h>
#include <openssl/rsa.h>
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
#include <openssl/param_build.h>
#include <openssl/core_names.h>
#endif
/**
* Public exponent to use for key generation.
*/
@ -41,6 +46,7 @@ OPENSSL_KEY_FALLBACK(RSA, key, n, e, d)
OPENSSL_KEY_FALLBACK(RSA, factors, p, q)
OPENSSL_KEY_FALLBACK(RSA, crt_params, dmp1, dmq1, iqmp)
#define BN_secure_new() BN_new()
#define BN_CTX_secure_new() BN_CTX_new()
#endif
typedef struct private_openssl_rsa_private_key_t private_openssl_rsa_private_key_t;
@ -55,9 +61,9 @@ struct private_openssl_rsa_private_key_t {
openssl_rsa_private_key_t public;
/**
* RSA object from OpenSSL
* RSA key object
*/
RSA *rsa;
EVP_PKEY *key;
/**
* TRUE if the key is from an OpenSSL ENGINE and might not be readable
@ -71,9 +77,7 @@ struct private_openssl_rsa_private_key_t {
};
/* implemented in rsa public key */
bool openssl_rsa_fingerprint(RSA *rsa, cred_encoding_type_t type, chunk_t *fp);
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
bool openssl_rsa_fingerprint(EVP_PKEY *key, cred_encoding_type_t type, chunk_t *fp);
/**
* Build RSA signature
@ -84,20 +88,14 @@ static bool build_signature(private_openssl_rsa_private_key_t *this,
{
EVP_PKEY_CTX *pctx = NULL;
EVP_MD_CTX *mctx = NULL;
EVP_PKEY *key;
bool success = FALSE;
mctx = EVP_MD_CTX_create();
key = EVP_PKEY_new();
if (!mctx || !key)
if (!mctx)
{
goto error;
return FALSE;
}
if (!EVP_PKEY_set1_RSA(key, this->rsa))
{
goto error;
}
if (EVP_DigestSignInit(mctx, &pctx, md, NULL, key) <= 0)
if (EVP_DigestSignInit(mctx, &pctx, md, NULL, this->key) <= 0)
{
goto error;
}
@ -118,17 +116,31 @@ static bool build_signature(private_openssl_rsa_private_key_t *this,
success = (EVP_DigestSignFinal(mctx, sig->ptr, &sig->len) == 1);
error:
if (key)
{
EVP_PKEY_free(key);
}
if (mctx)
{
EVP_MD_CTX_destroy(mctx);
}
EVP_MD_CTX_destroy(mctx);
return success;
}
/**
* Build an EMSA PKCS1 signature without hashing
*/
static bool build_plain_signature(private_openssl_rsa_private_key_t *this,
chunk_t data, chunk_t *sig)
{
EVP_PKEY_CTX *ctx;
ctx = EVP_PKEY_CTX_new(this->key, NULL);
if (!ctx ||
EVP_PKEY_sign_init(ctx) <= 0 ||
EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0 ||
EVP_PKEY_sign(ctx, sig->ptr, &sig->len, data.ptr, data.len) <= 0)
{
EVP_PKEY_CTX_free(ctx);
return FALSE;
}
EVP_PKEY_CTX_free(ctx);
return TRUE;
}
/**
* Build an EMSA PKCS1 signature described in PKCS#1
*/
@ -137,12 +149,11 @@ static bool build_emsa_pkcs1_signature(private_openssl_rsa_private_key_t *this,
{
const EVP_MD *md;
*sig = chunk_alloc(RSA_size(this->rsa));
*sig = chunk_alloc(EVP_PKEY_size(this->key));
if (type == NID_undef)
{
if (RSA_private_encrypt(data.len, data.ptr, sig->ptr, this->rsa,
RSA_PKCS1_PADDING) == sig->len)
if (build_plain_signature(this, data, sig))
{
return TRUE;
}
@ -173,7 +184,7 @@ static bool build_emsa_pss_signature(private_openssl_rsa_private_key_t *this,
return FALSE;
}
*sig = chunk_alloc(RSA_size(this->rsa));
*sig = chunk_alloc(EVP_PKEY_size(this->key));
md = openssl_get_md(params->hash);
if (md && build_signature(this, md, params, data, sig))
@ -184,80 +195,6 @@ static bool build_emsa_pss_signature(private_openssl_rsa_private_key_t *this,
return FALSE;
}
#else /* OPENSSL_VERSION_NUMBER < 1.0 */
/**
* Build an EMSA PKCS1 signature described in PKCS#1
*/
static bool build_emsa_pkcs1_signature(private_openssl_rsa_private_key_t *this,
int type, chunk_t data, chunk_t *sig)
{
bool success = FALSE;
*sig = chunk_alloc(RSA_size(this->rsa));
if (type == NID_undef)
{
if (RSA_private_encrypt(data.len, data.ptr, sig->ptr, this->rsa,
RSA_PKCS1_PADDING) == sig->len)
{
success = TRUE;
}
}
else
{
EVP_MD_CTX *ctx = NULL;
EVP_PKEY *key = NULL;
const EVP_MD *hasher;
u_int len;
hasher = EVP_get_digestbynid(type);
if (!hasher)
{
goto error;
}
ctx = EVP_MD_CTX_create();
key = EVP_PKEY_new();
if (!ctx || !key)
{
goto error;
}
if (!EVP_PKEY_set1_RSA(key, this->rsa))
{
goto error;
}
if (!EVP_SignInit_ex(ctx, hasher, NULL))
{
goto error;
}
if (!EVP_SignUpdate(ctx, data.ptr, data.len))
{
goto error;
}
if (EVP_SignFinal(ctx, sig->ptr, &len, key))
{
success = TRUE;
}
error:
if (key)
{
EVP_PKEY_free(key);
}
if (ctx)
{
EVP_MD_CTX_destroy(ctx);
}
}
if (!success)
{
free(sig->ptr);
}
return success;
}
#endif /* OPENSSL_VERSION_NUMBER < 1.0 */
METHOD(private_key_t, get_type, key_type_t,
private_openssl_rsa_private_key_t *this)
{
@ -294,10 +231,8 @@ METHOD(private_key_t, sign, bool,
return build_emsa_pkcs1_signature(this, NID_sha1, data, signature);
case SIGN_RSA_EMSA_PKCS1_MD5:
return build_emsa_pkcs1_signature(this, NID_md5, data, signature);
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
case SIGN_RSA_EMSA_PSS:
return build_emsa_pss_signature(this, params, data, signature);
#endif
default:
DBG1(DBG_LIB, "signature scheme %N not supported in RSA",
signature_scheme_names, scheme);
@ -310,7 +245,6 @@ METHOD(private_key_t, decrypt, bool,
void *params, chunk_t crypto, chunk_t *plain)
{
EVP_PKEY_CTX *ctx = NULL;
EVP_PKEY *evp_key = NULL;
chunk_t label = chunk_empty;
hash_algorithm_t hash_alg = HASH_UNKNOWN;
size_t len;
@ -349,23 +283,11 @@ METHOD(private_key_t, decrypt, bool,
return FALSE;
}
evp_key = EVP_PKEY_new();
if (!evp_key)
{
DBG1(DBG_LIB, "could not create EVP key");
goto error;
}
if (EVP_PKEY_set1_RSA(evp_key, this->rsa) <= 0)
{
DBG1(DBG_LIB, "could not set EVP key to RSA key");
goto error;
}
ctx = EVP_PKEY_CTX_new(evp_key, NULL);
ctx = EVP_PKEY_CTX_new(this->key, NULL);
if (!ctx)
{
DBG1(DBG_LIB, "could not create EVP context");
goto error;
return FALSE;
}
if (EVP_PKEY_decrypt_init(ctx) <= 0)
@ -410,7 +332,7 @@ METHOD(private_key_t, decrypt, bool,
}
/* determine maximum plaintext size */
len = RSA_size(this->rsa);
len = EVP_PKEY_size(this->key);
decrypted = malloc(len);
/* decrypt data */
@ -424,33 +346,23 @@ METHOD(private_key_t, decrypt, bool,
success = TRUE;
error:
if (ctx)
{
EVP_PKEY_CTX_free(ctx);
}
if (evp_key)
{
EVP_PKEY_free(evp_key);
}
EVP_PKEY_CTX_free(ctx);
return success;
}
METHOD(private_key_t, get_keysize, int,
private_openssl_rsa_private_key_t *this)
{
return RSA_size(this->rsa) * 8;
return EVP_PKEY_bits(this->key);
}
METHOD(private_key_t, get_public_key, public_key_t*,
private_openssl_rsa_private_key_t *this)
{
chunk_t enc;
public_key_t *key;
u_char *p;
chunk_t enc;
enc = chunk_alloc(i2d_RSAPublicKey(this->rsa, NULL));
p = enc.ptr;
i2d_RSAPublicKey(this->rsa, &p);
enc = openssl_i2chunk(PublicKey, this->key);
key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA,
BUILD_BLOB_ASN1_DER, enc, BUILD_END);
free(enc.ptr);
@ -461,15 +373,13 @@ METHOD(private_key_t, get_fingerprint, bool,
private_openssl_rsa_private_key_t *this, cred_encoding_type_t type,
chunk_t *fingerprint)
{
return openssl_rsa_fingerprint(this->rsa, type, fingerprint);
return openssl_rsa_fingerprint(this->key, type, fingerprint);
}
METHOD(private_key_t, get_encoding, bool,
private_openssl_rsa_private_key_t *this, cred_encoding_type_t type,
chunk_t *encoding)
{
u_char *p;
if (this->engine)
{
return FALSE;
@ -481,9 +391,7 @@ METHOD(private_key_t, get_encoding, bool,
{
bool success = TRUE;
*encoding = chunk_alloc(i2d_RSAPrivateKey(this->rsa, NULL));
p = encoding->ptr;
i2d_RSAPrivateKey(this->rsa, &p);
*encoding = openssl_i2chunk(PrivateKey, this->key);
if (type == PRIVKEY_PEM)
{
@ -513,10 +421,10 @@ METHOD(private_key_t, destroy, void,
{
if (ref_put(&this->ref))
{
if (this->rsa)
if (this->key)
{
lib->encoding->clear_cache(lib->encoding, this->rsa);
RSA_free(this->rsa);
lib->encoding->clear_cache(lib->encoding, this->key);
EVP_PKEY_free(this->key);
}
free(this);
}
@ -525,7 +433,7 @@ METHOD(private_key_t, destroy, void,
/**
* Internal generic constructor
*/
static private_openssl_rsa_private_key_t *create_empty()
static private_openssl_rsa_private_key_t *create_internal(EVP_PKEY *key)
{
private_openssl_rsa_private_key_t *this;
@ -547,6 +455,7 @@ static private_openssl_rsa_private_key_t *create_empty()
},
},
.ref = 1,
.key = key,
);
return this;
@ -559,9 +468,9 @@ openssl_rsa_private_key_t *openssl_rsa_private_key_gen(key_type_t type,
va_list args)
{
private_openssl_rsa_private_key_t *this;
EVP_PKEY *key = NULL;
u_int key_size = 0;
RSA *rsa = NULL;
BIGNUM *e = NULL;
BIGNUM *e;
while (TRUE)
{
@ -583,29 +492,54 @@ openssl_rsa_private_key_t *openssl_rsa_private_key_gen(key_type_t type,
}
e = BN_new();
if (!e || !BN_set_word(e, PUBLIC_EXPONENT))
{
goto error;
}
rsa = RSA_new();
if (!rsa || !RSA_generate_key_ex(rsa, key_size, e, NULL))
{
goto error;
}
this = create_empty();
this->rsa = rsa;
BN_free(e);
return &this->public;
error:
if (e)
{
BN_free(e);
return NULL;
}
if (rsa)
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
/* EVP_RSA_gen() does not allow specifying the public exponent, the default
* value is the same, but let's still use this more flexible approach */
EVP_PKEY_CTX *ctx;
ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
if (!ctx ||
EVP_PKEY_keygen_init(ctx) <= 0 ||
EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, key_size) <= 0 ||
EVP_PKEY_CTX_set1_rsa_keygen_pubexp(ctx, e) <= 0 ||
EVP_PKEY_keygen(ctx, &key) <= 0)
{
EVP_PKEY_CTX_free(ctx);
return NULL;
}
EVP_PKEY_CTX_free(ctx);
#else /* OPENSSL_VERSION_NUMBER */
RSA *rsa = RSA_new();
if (RSA_generate_key_ex(rsa, key_size, e, NULL))
{
key = EVP_PKEY_new();
if (!EVP_PKEY_assign_RSA(key, rsa))
{
RSA_free(rsa);
EVP_PKEY_free(key);
key = NULL;
}
}
else
{
RSA_free(rsa);
}
return NULL;
#endif /* OPENSSL_VERSION_NUMBER */
if (!key)
{
BN_free(e);
return NULL;
}
this = create_internal(key);
BN_free(e);
return &this->public;
}
/*
@ -614,16 +548,13 @@ error:
private_key_t *openssl_rsa_private_key_create(EVP_PKEY *key, bool engine)
{
private_openssl_rsa_private_key_t *this;
RSA *rsa;
rsa = EVP_PKEY_get1_RSA(key);
EVP_PKEY_free(key);
if (!rsa)
if (EVP_PKEY_base_id(key) != EVP_PKEY_RSA)
{
EVP_PKEY_free(key);
return NULL;
}
this = create_empty();
this->rsa = rsa;
this = create_internal(key);
this->engine = engine;
return &this->public.key;
}
@ -632,19 +563,13 @@ private_key_t *openssl_rsa_private_key_create(EVP_PKEY *key, bool engine)
* Recover the primes from n, e and d using the algorithm described in
* Appendix C of NIST SP 800-56B.
*/
static bool calculate_pq(BIGNUM *n, BIGNUM *e, BIGNUM *d,
BIGNUM **p, BIGNUM **q)
static bool calculate_pq(BN_CTX *ctx, BIGNUM *n, BIGNUM *e, BIGNUM *d,
BIGNUM *p, BIGNUM *q)
{
BN_CTX *ctx;
BIGNUM *k, *r, *g, *y, *n1, *x;
int i, t, j;
bool success = FALSE;
ctx = BN_CTX_new();
if (!ctx)
{
return FALSE;
}
BN_CTX_start(ctx);
k = BN_CTX_get(ctx);
r = BN_CTX_get(ctx);
@ -685,7 +610,7 @@ static bool calculate_pq(BIGNUM *n, BIGNUM *e, BIGNUM *d,
}
for (i = 0; i < 100; i++)
{ /* generate random integer g in [0, n-1] */
if (!BN_pseudo_rand_range(g, n))
if (!BN_rand_range(g, n))
{
goto error;
}
@ -730,25 +655,19 @@ done:
{
goto error;
}
*p = BN_secure_new();
if (!BN_gcd(*p, y, n, ctx))
if (!BN_gcd(p, y, n, ctx))
{
BN_clear_free(*p);
goto error;
}
/* q = n/p */
*q = BN_secure_new();
if (!BN_div(*q, NULL, n, *p, ctx))
if (!BN_div(q, NULL, n, p, ctx))
{
BN_clear_free(*p);
BN_clear_free(*q);
goto error;
}
success = TRUE;
error:
BN_CTX_end(ctx);
BN_CTX_free(ctx);
return success;
}
@ -756,65 +675,31 @@ error:
* Calculates dp = d (mod p-1) or dq = d (mod q-1) for the Chinese remainder
* algorithm.
*/
static BIGNUM *dmodpq1(BIGNUM *d, BIGNUM *pq)
static bool dmodpq1(BN_CTX *ctx, BIGNUM *d, BIGNUM *pq, BIGNUM *res)
{
BN_CTX *ctx;
BIGNUM *res = NULL, *pq1;
BIGNUM *pq1;
ctx = BN_CTX_new();
if (!ctx)
{
return NULL;
}
BN_CTX_start(ctx);
pq1 = BN_CTX_get(ctx);
/* p|q - 1 */
if (!BN_sub(pq1, pq, BN_value_one()))
/* p|q - 1
* d (mod p|q -1) */
if (!BN_sub(pq1, pq, BN_value_one()) ||
!BN_mod(res, d, pq1, ctx))
{
goto error;
BN_CTX_end(ctx);
return FALSE;
}
/* d (mod p|q -1) */
res = BN_secure_new();
if (!BN_mod(res, d, pq1, ctx))
{
BN_clear_free(res);
res = NULL;
goto error;
}
error:
BN_CTX_end(ctx);
BN_CTX_free(ctx);
return res;
return TRUE;
}
/**
* Calculates qinv = q^-1 (mod p) for the Chinese remainder algorithm.
*/
static BIGNUM *qinv(BIGNUM *q, BIGNUM *p)
static bool qinv(BN_CTX *ctx, BIGNUM *q, BIGNUM *p, BIGNUM *res)
{
BN_CTX *ctx;
BIGNUM *res = NULL;
ctx = BN_CTX_new();
if (!ctx)
{
return NULL;
}
BN_CTX_start(ctx);
/* q^-1 (mod p) */
res = BN_secure_new();
if (!BN_mod_inverse(res, q, p, ctx))
{
BN_clear_free(res);
res = NULL;
goto error;
}
error:
BN_CTX_end(ctx);
BN_CTX_free(ctx);
return res;
return BN_mod_inverse(res, q, p, ctx);
}
/*
@ -824,6 +709,7 @@ openssl_rsa_private_key_t *openssl_rsa_private_key_load(key_type_t type,
va_list args)
{
private_openssl_rsa_private_key_t *this;
EVP_PKEY *key = NULL;
chunk_t blob, n, e, d, p, q, exp1, exp2, coeff;
blob = n = e = d = p = q = exp1 = exp2 = coeff = chunk_empty;
@ -866,79 +752,126 @@ openssl_rsa_private_key_t *openssl_rsa_private_key_load(key_type_t type,
break;
}
this = create_empty();
if (blob.ptr)
{
this->rsa = d2i_RSAPrivateKey(NULL, (const u_char**)&blob.ptr, blob.len);
if (this->rsa && RSA_check_key(this->rsa) == 1)
{
return &this->public;
}
key = d2i_PrivateKey(EVP_PKEY_RSA, NULL, (const u_char**)&blob.ptr,
blob.len);
}
else if (n.ptr && e.ptr && d.ptr)
{
BIGNUM *bn_n, *bn_e, *bn_d, *bn_p, *bn_q;
BIGNUM *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL;
BN_CTX *ctx;
BIGNUM *bn_n, *bn_e, *bn_d, *bn_p, *bn_q, *dmp1, *dmq1, *iqmp;
this->rsa = RSA_new();
bn_n = BN_bin2bn((const u_char*)n.ptr, n.len, NULL);
bn_e = BN_bin2bn((const u_char*)e.ptr, e.len, NULL);
bn_d = BN_bin2bn((const u_char*)d.ptr, d.len, NULL);
if (!RSA_set0_key(this->rsa, bn_n, bn_e, bn_d))
ctx = BN_CTX_secure_new();
if (!ctx)
{
goto error;
}
BN_CTX_start(ctx);
bn_n = BN_CTX_get(ctx);
bn_e = BN_CTX_get(ctx);
bn_d = BN_CTX_get(ctx);
bn_p = BN_CTX_get(ctx);
bn_q = BN_CTX_get(ctx);
dmp1 = BN_CTX_get(ctx);
dmq1 = BN_CTX_get(ctx);
iqmp = BN_CTX_get(ctx);
bn_n = BN_bin2bn((const u_char*)n.ptr, n.len, bn_n);
bn_e = BN_bin2bn((const u_char*)e.ptr, e.len, bn_e);
bn_d = BN_bin2bn((const u_char*)d.ptr, d.len, bn_d);
if (p.ptr && q.ptr)
{
bn_p = BN_bin2bn((const u_char*)p.ptr, p.len, NULL);
bn_q = BN_bin2bn((const u_char*)q.ptr, q.len, NULL);
bn_p = BN_bin2bn((const u_char*)p.ptr, p.len, bn_p);
bn_q = BN_bin2bn((const u_char*)q.ptr, q.len, bn_q);
}
else
{
if (!calculate_pq(bn_n, bn_e, bn_d, &bn_p, &bn_q))
{
goto error;
}
}
if (!RSA_set0_factors(this->rsa, bn_p, bn_q))
else if (!calculate_pq(ctx, bn_n, bn_e, bn_d, bn_p, bn_q))
{
goto error;
}
if (exp1.ptr)
{
dmp1 = BN_bin2bn((const u_char*)exp1.ptr, exp1.len, NULL);
dmp1 = BN_bin2bn((const u_char*)exp1.ptr, exp1.len, dmp1);
}
else
else if (!dmodpq1(ctx, bn_d, bn_p, dmp1))
{
dmp1 = dmodpq1(bn_d, bn_p);
goto error;
}
if (exp2.ptr)
{
dmq1 = BN_bin2bn((const u_char*)exp2.ptr, exp2.len, NULL);
dmq1 = BN_bin2bn((const u_char*)exp2.ptr, exp2.len, dmq1);
}
else
else if (!dmodpq1(ctx, bn_d, bn_q, dmq1))
{
dmq1 = dmodpq1(bn_d, bn_q);
goto error;
}
if (coeff.ptr)
{
iqmp = BN_bin2bn((const u_char*)coeff.ptr, coeff.len, NULL);
iqmp = BN_bin2bn((const u_char*)coeff.ptr, coeff.len, iqmp);
}
else
else if (!qinv(ctx, bn_q, bn_p, iqmp))
{
iqmp = qinv(bn_q, bn_p);
goto error;
}
if (RSA_set0_crt_params(this->rsa, dmp1, dmq1, iqmp) &&
RSA_check_key(this->rsa) == 1)
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
OSSL_PARAM_BLD *bld;
OSSL_PARAM *params = NULL;
EVP_PKEY_CTX *pctx;
bld = OSSL_PARAM_BLD_new();
if (bld &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, bn_n) &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, bn_e) &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_D, bn_d) &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR1, bn_p) &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR2, bn_q) &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_EXPONENT1, dmp1) &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_EXPONENT2, dmq1) &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_COEFFICIENT1, iqmp))
{
return &this->public;
params = OSSL_PARAM_BLD_to_param(bld);
}
}
OSSL_PARAM_BLD_free(bld);
pctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL);
if (!params || !pctx ||
EVP_PKEY_fromdata_init(pctx) <= 0 ||
EVP_PKEY_fromdata(pctx, &key, EVP_PKEY_KEYPAIR, params) <= 0)
{
key = NULL;
}
EVP_PKEY_CTX_free(pctx);
OSSL_PARAM_free(params);
#else /* OPENSSL_VERSION_NUMBER */
RSA *rsa = RSA_new();
if (!RSA_set0_key(rsa, BN_dup(bn_n), BN_dup(bn_e), BN_dup(bn_d)) ||
!RSA_set0_factors(rsa, BN_dup(bn_p), BN_dup(bn_q)) ||
!RSA_set0_crt_params(rsa, BN_dup(dmp1), BN_dup(dmq1), BN_dup(iqmp)) ||
RSA_check_key(rsa) <= 0)
{
RSA_free(rsa);
goto error;
}
key = EVP_PKEY_new();
if (!EVP_PKEY_assign_RSA(key, rsa))
{
RSA_free(rsa);
EVP_PKEY_free(key);
key = NULL;
}
#endif /* OPENSSL_VERSION_NUMBER */
error:
destroy(this);
return NULL;
BN_CTX_end(ctx);
BN_CTX_free(ctx);
}
if (!key)
{
return NULL;
}
this = create_internal(key);
return &this->public;
}
#endif /* OPENSSL_NO_RSA */

View File

@ -31,6 +31,11 @@
#include <openssl/rsa.h>
#include <openssl/x509.h>
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
#include <openssl/param_build.h>
#include <openssl/core_names.h>
#endif
#if OPENSSL_VERSION_NUMBER < 0x10100000L
OPENSSL_KEY_FALLBACK(RSA, key, n, e, d)
#endif
@ -47,9 +52,9 @@ struct private_openssl_rsa_public_key_t {
openssl_rsa_public_key_t public;
/**
* RSA object from OpenSSL
* RSA key object
*/
RSA *rsa;
EVP_PKEY *key;
/**
* reference counter
@ -57,9 +62,6 @@ struct private_openssl_rsa_public_key_t {
refcount_t ref;
};
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
/**
* Verify RSA signature
*/
@ -69,8 +71,7 @@ static bool verify_signature(private_openssl_rsa_public_key_t *this,
{
EVP_PKEY_CTX *pctx = NULL;
EVP_MD_CTX *mctx = NULL;
EVP_PKEY *key;
int rsa_size = RSA_size(this->rsa);
int rsa_size = EVP_PKEY_size(this->key);
bool valid = FALSE;
/* OpenSSL expects a signature of exactly RSA size (no leading 0x00) */
@ -80,16 +81,11 @@ static bool verify_signature(private_openssl_rsa_public_key_t *this,
}
mctx = EVP_MD_CTX_create();
key = EVP_PKEY_new();
if (!mctx || !key)
if (!mctx)
{
goto error;
return FALSE;
}
if (!EVP_PKEY_set1_RSA(key, this->rsa))
{
goto error;
}
if (EVP_DigestVerifyInit(mctx, &pctx, md, NULL, key) <= 0)
if (EVP_DigestVerifyInit(mctx, &pctx, md, NULL, this->key) <= 0)
{
goto error;
}
@ -110,14 +106,7 @@ static bool verify_signature(private_openssl_rsa_public_key_t *this,
valid = (EVP_DigestVerifyFinal(mctx, signature.ptr, signature.len) == 1);
error:
if (key)
{
EVP_PKEY_free(key);
}
if (mctx)
{
EVP_MD_CTX_destroy(mctx);
}
EVP_MD_CTX_destroy(mctx);
return valid;
}
@ -128,7 +117,7 @@ static bool verify_plain_signature(private_openssl_rsa_public_key_t *this,
chunk_t data, chunk_t signature)
{
char *buf;
int len, rsa_size = RSA_size(this->rsa);
size_t rsa_size = EVP_PKEY_size(this->key);
bool valid = FALSE;
/* OpenSSL expects a signature of exactly RSA size (no leading 0x00) */
@ -136,14 +125,39 @@ static bool verify_plain_signature(private_openssl_rsa_public_key_t *this,
{
signature = chunk_skip(signature, signature.len - rsa_size);
}
#if defined(OPENSSL_IS_BORINGSSL) && \
(!defined(BORINGSSL_API_VERSION) || BORINGSSL_API_VERSION < 10)
RSA *rsa = EVP_PKEY_get1_RSA(this->key);
int len;
buf = malloc(rsa_size);
len = RSA_public_decrypt(signature.len, signature.ptr, buf, this->rsa,
len = RSA_public_decrypt(signature.len, signature.ptr, buf, rsa,
RSA_PKCS1_PADDING);
if (len != -1)
{
valid = chunk_equals_const(data, chunk_create(buf, len));
}
RSA_free(rsa);
#else
EVP_PKEY_CTX *ctx;
size_t len = rsa_size;
ctx = EVP_PKEY_CTX_new(this->key, NULL);
if (!ctx ||
EVP_PKEY_verify_recover_init(ctx) <= 0 ||
EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
{
EVP_PKEY_CTX_free(ctx);
return FALSE;
}
buf = malloc(rsa_size);
if (EVP_PKEY_verify_recover(ctx, buf, &len, signature.ptr, signature.len) > 0)
{
valid = chunk_equals_const(data, chunk_create(buf, len));
}
free(buf);
EVP_PKEY_CTX_free(ctx);
#endif
return valid;
}
@ -180,85 +194,6 @@ static bool verify_emsa_pss_signature(private_openssl_rsa_public_key_t *this,
return md && verify_signature(this, md, params, data, signature);
}
#else /* OPENSSL_VERSION_NUMBER < 1.0 */
/**
* Verification of an EMSA PKCS1 signature described in PKCS#1
*/
static bool verify_emsa_pkcs1_signature(private_openssl_rsa_public_key_t *this,
int type, chunk_t data, chunk_t signature)
{
bool valid = FALSE;
int rsa_size = RSA_size(this->rsa);
/* OpenSSL expects a signature of exactly RSA size (no leading 0x00) */
if (signature.len > rsa_size)
{
signature = chunk_skip(signature, signature.len - rsa_size);
}
if (type == NID_undef)
{
char *buf;
int len;
buf = malloc(rsa_size);
len = RSA_public_decrypt(signature.len, signature.ptr, buf, this->rsa,
RSA_PKCS1_PADDING);
if (len != -1)
{
valid = chunk_equals_const(data, chunk_create(buf, len));
}
free(buf);
}
else
{
EVP_MD_CTX *ctx;
EVP_PKEY *key;
const EVP_MD *hasher;
hasher = EVP_get_digestbynid(type);
if (!hasher)
{
return FALSE;
}
ctx = EVP_MD_CTX_create();
key = EVP_PKEY_new();
if (!ctx || !key)
{
goto error;
}
if (!EVP_PKEY_set1_RSA(key, this->rsa))
{
goto error;
}
if (!EVP_VerifyInit_ex(ctx, hasher, NULL))
{
goto error;
}
if (!EVP_VerifyUpdate(ctx, data.ptr, data.len))
{
goto error;
}
valid = (EVP_VerifyFinal(ctx, signature.ptr, signature.len, key) == 1);
error:
if (key)
{
EVP_PKEY_free(key);
}
if (ctx)
{
EVP_MD_CTX_destroy(ctx);
}
}
return valid;
}
#endif /* OPENSSL_VERSION_NUMBER < 1.0 */
METHOD(public_key_t, get_type, key_type_t,
private_openssl_rsa_public_key_t *this)
{
@ -295,10 +230,8 @@ METHOD(public_key_t, verify, bool,
return verify_emsa_pkcs1_signature(this, NID_sha1, data, signature);
case SIGN_RSA_EMSA_PKCS1_MD5:
return verify_emsa_pkcs1_signature(this, NID_md5, data, signature);
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
case SIGN_RSA_EMSA_PSS:
return verify_emsa_pss_signature(this, params, data, signature);
#endif
default:
DBG1(DBG_LIB, "signature scheme %N not supported in RSA",
signature_scheme_names, scheme);
@ -311,7 +244,6 @@ METHOD(public_key_t, encrypt, bool,
void *params, chunk_t plain, chunk_t *crypto)
{
EVP_PKEY_CTX *ctx = NULL;
EVP_PKEY *evp_key = NULL;
chunk_t label = chunk_empty;
hash_algorithm_t hash_alg = HASH_UNKNOWN;
size_t len;
@ -350,23 +282,11 @@ METHOD(public_key_t, encrypt, bool,
return FALSE;
}
evp_key = EVP_PKEY_new();
if (!evp_key)
{
DBG1(DBG_LIB, "could not create EVP key");
goto error;
}
if (EVP_PKEY_set1_RSA(evp_key, this->rsa) <= 0)
{
DBG1(DBG_LIB, "could not set EVP key to RSA key");
goto error;
}
ctx = EVP_PKEY_CTX_new(evp_key, NULL);
ctx = EVP_PKEY_CTX_new(this->key, NULL);
if (!ctx)
{
DBG1(DBG_LIB, "could not create EVP context");
goto error;
return FALSE;
}
if (EVP_PKEY_encrypt_init(ctx) <= 0)
@ -411,7 +331,7 @@ METHOD(public_key_t, encrypt, bool,
}
/* determine maximum ciphertext size */
len = RSA_size(this->rsa);
len = EVP_PKEY_size(this->key);
encrypted = malloc(len);
/* decrypt data */
@ -425,78 +345,78 @@ METHOD(public_key_t, encrypt, bool,
success = TRUE;
error:
if (ctx)
{
EVP_PKEY_CTX_free(ctx);
}
if (evp_key)
{
EVP_PKEY_free(evp_key);
}
EVP_PKEY_CTX_free(ctx);
return success;
}
METHOD(public_key_t, get_keysize, int,
private_openssl_rsa_public_key_t *this)
{
return RSA_size(this->rsa) * 8;
return EVP_PKEY_bits(this->key);
}
/**
* Get n and e of the given RSA key (allocated).
*/
static bool get_n_and_e(EVP_PKEY *key, chunk_t *n, chunk_t *e)
{
const BIGNUM *cbn_n, *cbn_e;
BIGNUM *bn_n = NULL, *bn_e = NULL;
bool success = FALSE;
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
if (EVP_PKEY_get_bn_param(key, OSSL_PKEY_PARAM_RSA_N, &bn_n) <= 0 ||
EVP_PKEY_get_bn_param(key, OSSL_PKEY_PARAM_RSA_E, &bn_e) <= 0)
{
goto error;
}
cbn_n = bn_n;
cbn_e = bn_e;
#elif OPENSSL_VERSION_NUMBER >= 0x1010000fL
RSA *rsa = EVP_PKEY_get0_RSA(key);
RSA_get0_key(rsa, &cbn_n, &cbn_e, NULL);
#else
RSA *rsa = EVP_PKEY_get1_RSA(key);
RSA_get0_key(rsa, &cbn_n, &cbn_e, NULL);
RSA_free(rsa);
#endif
*n = *e = chunk_empty;
if (!openssl_bn2chunk(cbn_n, n) ||
!openssl_bn2chunk(cbn_e, e))
{
chunk_free(n);
chunk_free(e);
goto error;
}
success = TRUE;
error:
BN_free(bn_n);
BN_free(bn_e);
return success;
}
/**
* Calculate fingerprint from a RSA key, also used in rsa private key.
*/
bool openssl_rsa_fingerprint(RSA *rsa, cred_encoding_type_t type, chunk_t *fp)
bool openssl_rsa_fingerprint(EVP_PKEY *key, cred_encoding_type_t type, chunk_t *fp)
{
hasher_t *hasher;
chunk_t key;
u_char *p;
if (!openssl_fingerprint(key, type, fp))
{
chunk_t n = chunk_empty, e = chunk_empty;
bool success = FALSE;
if (lib->encoding->get_cache(lib->encoding, type, rsa, fp))
{
return TRUE;
}
switch (type)
{
case KEYID_PUBKEY_SHA1:
key = chunk_alloc(i2d_RSAPublicKey(rsa, NULL));
p = key.ptr;
i2d_RSAPublicKey(rsa, &p);
break;
case KEYID_PUBKEY_INFO_SHA1:
key = chunk_alloc(i2d_RSA_PUBKEY(rsa, NULL));
p = key.ptr;
i2d_RSA_PUBKEY(rsa, &p);
break;
default:
if (get_n_and_e(key, &n, &e))
{
const BIGNUM *bn_n, *bn_e;
chunk_t n = chunk_empty, e = chunk_empty;
bool success = FALSE;
RSA_get0_key(rsa, &bn_n, &bn_e, NULL);
if (openssl_bn2chunk(bn_n, &n) &&
openssl_bn2chunk(bn_e, &e))
{
success = lib->encoding->encode(lib->encoding, type, rsa, fp,
success = lib->encoding->encode(lib->encoding, type, key, fp,
CRED_PART_RSA_MODULUS, n,
CRED_PART_RSA_PUB_EXP, e, CRED_PART_END);
}
chunk_free(&n);
chunk_free(&e);
return success;
}
chunk_free(&n);
chunk_free(&e);
return success;
}
hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
if (!hasher || !hasher->allocate_hash(hasher, key, fp))
{
DBG1(DBG_LIB, "SHA1 hash algorithm not supported, fingerprinting failed");
DESTROY_IF(hasher);
free(key.ptr);
return FALSE;
}
free(key.ptr);
hasher->destroy(hasher);
lib->encoding->cache(lib->encoding, type, rsa, *fp);
return TRUE;
}
@ -504,7 +424,7 @@ METHOD(public_key_t, get_fingerprint, bool,
private_openssl_rsa_public_key_t *this, cred_encoding_type_t type,
chunk_t *fingerprint)
{
return openssl_rsa_fingerprint(this->rsa, type, fingerprint);
return openssl_rsa_fingerprint(this->key, type, fingerprint);
}
METHOD(public_key_t, get_encoding, bool,
@ -512,16 +432,13 @@ METHOD(public_key_t, get_encoding, bool,
chunk_t *encoding)
{
bool success = FALSE;
u_char *p;
switch (type)
{
case PUBKEY_SPKI_ASN1_DER:
case PUBKEY_PEM:
{
*encoding = chunk_alloc(i2d_RSA_PUBKEY(this->rsa, NULL));
p = encoding->ptr;
i2d_RSA_PUBKEY(this->rsa, &p);
*encoding = openssl_i2chunk(PUBKEY, this->key);
success = TRUE;
if (type == PUBKEY_PEM)
@ -537,19 +454,14 @@ METHOD(public_key_t, get_encoding, bool,
}
case PUBKEY_ASN1_DER:
{
*encoding = chunk_alloc(i2d_RSAPublicKey(this->rsa, NULL));
p = encoding->ptr;
i2d_RSAPublicKey(this->rsa, &p);
*encoding = openssl_i2chunk(PublicKey, this->key);
return TRUE;
}
default:
{
const BIGNUM *bn_n, *bn_e;
chunk_t n = chunk_empty, e = chunk_empty;
RSA_get0_key(this->rsa, &bn_n, &bn_e, NULL);
if (openssl_bn2chunk(bn_n, &n) &&
openssl_bn2chunk(bn_e, &e))
if (get_n_and_e(this->key, &n, &e))
{
success = lib->encoding->encode(lib->encoding, type, NULL,
encoding, CRED_PART_RSA_MODULUS, n,
@ -574,10 +486,10 @@ METHOD(public_key_t, destroy, void,
{
if (ref_put(&this->ref))
{
if (this->rsa)
if (this->key)
{
lib->encoding->clear_cache(lib->encoding, this->rsa);
RSA_free(this->rsa);
lib->encoding->clear_cache(lib->encoding, this->key);
EVP_PKEY_free(this->key);
}
free(this);
}
@ -586,7 +498,7 @@ METHOD(public_key_t, destroy, void,
/**
* Generic private constructor
*/
static private_openssl_rsa_public_key_t *create_empty()
static private_openssl_rsa_public_key_t *create_internal(EVP_PKEY *key)
{
private_openssl_rsa_public_key_t *this;
@ -606,6 +518,7 @@ static private_openssl_rsa_public_key_t *create_empty()
},
},
.ref = 1,
.key = key,
);
return this;
@ -618,6 +531,7 @@ openssl_rsa_public_key_t *openssl_rsa_public_key_load(key_type_t type,
va_list args)
{
private_openssl_rsa_public_key_t *this;
EVP_PKEY *key = NULL;
chunk_t blob, n, e;
n = e = blob = chunk_empty;
@ -642,41 +556,98 @@ openssl_rsa_public_key_t *openssl_rsa_public_key_load(key_type_t type,
break;
}
this = create_empty();
if (blob.ptr)
{
switch (type)
{
case KEY_ANY:
this->rsa = d2i_RSA_PUBKEY(NULL, (const u_char**)&blob.ptr,
blob.len);
key = d2i_PUBKEY(NULL, (const u_char**)&blob.ptr, blob.len);
if (key && EVP_PKEY_base_id(key) != EVP_PKEY_RSA)
{
EVP_PKEY_free(key);
key = NULL;
}
break;
case KEY_RSA:
this->rsa = d2i_RSAPublicKey(NULL, (const u_char**)&blob.ptr,
blob.len);
#if defined(OPENSSL_IS_BORINGSSL) && \
(!defined(BORINGSSL_API_VERSION) || BORINGSSL_API_VERSION < 10)
{
RSA *rsa = d2i_RSAPublicKey(NULL, (const u_char**)&blob.ptr,
blob.len);
key = EVP_PKEY_new();
if (!key || !EVP_PKEY_assign_RSA(key, rsa))
{
RSA_free(rsa);
EVP_PKEY_free(key);
key = NULL;
}
}
#else
key = d2i_PublicKey(EVP_PKEY_RSA, NULL, (const u_char**)&blob.ptr,
blob.len);
#endif
break;
default:
break;
}
if (this->rsa)
{
return &this->public;
}
}
else if (n.ptr && e.ptr && type == KEY_RSA)
{
BIGNUM *bn_n, *bn_e;
this->rsa = RSA_new();
bn_n = BN_bin2bn((const u_char*)n.ptr, n.len, NULL);
bn_e = BN_bin2bn((const u_char*)e.ptr, e.len, NULL);
if (RSA_set0_key(this->rsa, bn_n, bn_e, NULL))
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
OSSL_PARAM_BLD *bld;
OSSL_PARAM *params = NULL;
EVP_PKEY_CTX *ctx;
bld = OSSL_PARAM_BLD_new();
if (bld &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, bn_n) &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, bn_e))
{
return &this->public;
params = OSSL_PARAM_BLD_to_param(bld);
}
OSSL_PARAM_BLD_free(bld);
BN_free(bn_n);
BN_free(bn_e);
ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL);
if (!params || !ctx ||
EVP_PKEY_fromdata_init(ctx) <= 0 ||
EVP_PKEY_fromdata(ctx, &key, EVP_PKEY_PUBLIC_KEY, params) <= 0)
{
key = NULL;
}
EVP_PKEY_CTX_free(ctx);
OSSL_PARAM_free(params);
#else /* OPENSSL_VERSION_NUMBER */
RSA *rsa = RSA_new();
if (RSA_set0_key(rsa, bn_n, bn_e, NULL))
{
key = EVP_PKEY_new();
if (!key || !EVP_PKEY_assign_RSA(key, rsa))
{
RSA_free(rsa);
EVP_PKEY_free(key);
key = NULL;
}
}
else
{
RSA_free(rsa);
}
#endif /* OPENSSL_VERSION_NUMBER */
}
destroy(this);
return NULL;
if (!key)
{
return NULL;
}
this = create_internal(key);
return &this->public;
}
#endif /* OPENSSL_NO_RSA */

View File

@ -13,9 +13,15 @@
* for more details.
*/
/* direct access to the state and the SHA1_* API have been deprecated with
* OpenSSL 3, so at some point this won't work anymore */
#define OPENSSL_SUPPRESS_DEPRECATED
#include <openssl/opensslv.h>
#include <openssl/opensslconf.h>
#ifndef OPENSSL_NO_SHA1
#if !defined(OPENSSL_NO_SHA1) && \
(OPENSSL_VERSION_NUMBER < 0x30000000L || !defined(OPENSSL_NO_DEPRECATED))
#include "openssl_sha1_prf.h"
@ -43,14 +49,10 @@ struct private_openssl_sha1_prf_t {
METHOD(prf_t, get_bytes, bool,
private_openssl_sha1_prf_t *this, chunk_t seed, uint8_t *bytes)
{
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
if (!SHA1_Update(&this->ctx, seed.ptr, seed.len))
{
return FALSE;
}
#else /* OPENSSL_VERSION_NUMBER < 1.0 */
SHA1_Update(&this->ctx, seed.ptr, seed.len);
#endif
if (bytes)
{
@ -92,14 +94,10 @@ METHOD(prf_t, get_key_size, size_t,
METHOD(prf_t, set_key, bool,
private_openssl_sha1_prf_t *this, chunk_t key)
{
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
if (!SHA1_Init(&this->ctx))
{
return FALSE;
}
#else /* OPENSSL_VERSION_NUMBER < 1.0 */
SHA1_Init(&this->ctx);
#endif
if (key.len % 4)
{
@ -162,4 +160,4 @@ openssl_sha1_prf_t *openssl_sha1_prf_create(pseudo_random_function_t algo)
return &this->public;
}
#endif /* OPENSSL_NO_SHA1 */
#endif /* !OPENSSL_NO_SHA1 && SHA_LONG */

View File

@ -22,6 +22,11 @@
#include <openssl/evp.h>
#include <openssl/x509.h>
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
/* for EVP_PKEY_CTX_set_dh_pad */
#include <openssl/dh.h>
#endif
/* these were added with 1.1.0 when ASN1_OBJECT was made opaque */
#if OPENSSL_VERSION_NUMBER < 0x10100000L
#define OBJ_get0_data(o) ((o)->data)
@ -48,6 +53,14 @@ bool openssl_compute_shared_key(EVP_PKEY *priv, EVP_PKEY *pub, chunk_t *shared)
goto error;
}
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
if (EVP_PKEY_base_id(priv) == EVP_PKEY_DH &&
EVP_PKEY_CTX_set_dh_pad(ctx, 1) <= 0)
{
goto error;
}
#endif
if (EVP_PKEY_derive_set_peer(ctx, pub) <= 0)
{
goto error;
@ -62,6 +75,7 @@ bool openssl_compute_shared_key(EVP_PKEY *priv, EVP_PKEY *pub, chunk_t *shared)
if (EVP_PKEY_derive(ctx, shared->ptr, &shared->len) <= 0)
{
chunk_clear(shared);
goto error;
}
@ -72,6 +86,48 @@ error:
return success;
}
/*
* Described in header
*/
bool openssl_fingerprint(EVP_PKEY *key, cred_encoding_type_t type, chunk_t *fp)
{
hasher_t *hasher;
chunk_t enc;
u_char *p;
if (lib->encoding->get_cache(lib->encoding, type, key, fp))
{
return TRUE;
}
switch (type)
{
case KEYID_PUBKEY_SHA1:
enc = chunk_alloc(i2d_PublicKey(key, NULL));
p = enc.ptr;
i2d_PublicKey(key, &p);
break;
case KEYID_PUBKEY_INFO_SHA1:
enc = chunk_alloc(i2d_PUBKEY(key, NULL));
p = enc.ptr;
i2d_PUBKEY(key, &p);
break;
default:
return FALSE;
}
hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
if (!hasher || !hasher->allocate_hash(hasher, enc, fp))
{
DBG1(DBG_LIB, "SHA1 not supported, fingerprinting failed");
DESTROY_IF(hasher);
free(enc.ptr);
return FALSE;
}
free(enc.ptr);
hasher->destroy(hasher);
lib->encoding->cache(lib->encoding, type, key, *fp);
return TRUE;
}
/**
* Described in header.
*/

View File

@ -46,6 +46,16 @@
*/
bool openssl_compute_shared_key(EVP_PKEY *priv, EVP_PKEY *pub, chunk_t *shared);
/**
* Calculate a fingerprint from the given key (cached under it).
*
* @param key key object
* @param type encoding type
* @param fp allocated fingerprint
* @return TRUE on success, FALSE otherwise
*/
bool openssl_fingerprint(EVP_PKEY *key, cred_encoding_type_t type, chunk_t *fp);
/**
* Creates a hash of a given type of a chunk of data.
*

View File

@ -45,15 +45,15 @@ struct private_diffie_hellman_t {
*/
EVP_PKEY *key;
/**
* Public key provided by peer
*/
EVP_PKEY *pub;
/**
* Shared secret
*/
chunk_t shared_secret;
/**
* True if shared secret is computed
*/
bool computed;
};
/**
@ -75,33 +75,21 @@ static int map_key_type(diffie_hellman_group_t group)
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)
EVP_PKEY_free(this->pub);
this->pub = EVP_PKEY_new_raw_public_key(map_key_type(this->group), NULL,
value.ptr, value.len);
if (!this->pub)
{
DBG1(DBG_LIB, "%N public value is malformed",
diffie_hellman_group_names, this->group);
return FALSE;
}
chunk_clear(&this->shared_secret);
if (!openssl_compute_shared_key(this->key, 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;
}
@ -141,8 +129,11 @@ METHOD(diffie_hellman_t, set_private_value, bool,
METHOD(diffie_hellman_t, get_shared_secret, bool,
private_diffie_hellman_t *this, chunk_t *secret)
{
if (!this->computed)
if (!this->shared_secret.len &&
!openssl_compute_shared_key(this->key, this->pub, &this->shared_secret))
{
DBG1(DBG_LIB, "%N shared secret computation failed",
diffie_hellman_group_names, this->group);
return FALSE;
}
*secret = chunk_clone(this->shared_secret);
@ -159,6 +150,7 @@ METHOD(diffie_hellman_t, destroy, void,
private_diffie_hellman_t *this)
{
EVP_PKEY_free(this->key);
EVP_PKEY_free(this->pub);
chunk_clear(&this->shared_secret);
free(this);
}

View File

@ -124,7 +124,8 @@ end:
* Try to decrypt the given blob with multiple passwords using the given
* pkcs5 object.
*/
static private_key_t *decrypt_private_key(pkcs5_t *pkcs5, chunk_t blob)
static private_key_t *decrypt_private_key(key_type_t type, pkcs5_t *pkcs5,
chunk_t blob)
{
enumerator_t *enumerator;
shared_key_t *shared;
@ -140,7 +141,15 @@ static private_key_t *decrypt_private_key(pkcs5_t *pkcs5, chunk_t blob)
{
continue;
}
private_key = parse_private_key(decrypted);
/* do a quick check to validate whether the password was correct */
if (!is_asn1(decrypted))
{
chunk_clear(&decrypted);
continue;
}
private_key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
type, BUILD_BLOB_ASN1_DER,
decrypted, BUILD_END);
if (private_key)
{
chunk_clear(&decrypted);
@ -169,7 +178,7 @@ static const asn1Object_t encryptedPKIObjects[] = {
* Load an encrypted private key from an ASN.1 encoded blob
* Schemes per PKCS#5 (RFC 2898)
*/
static private_key_t *parse_encrypted_private_key(chunk_t blob)
static private_key_t *parse_encrypted_private_key(key_type_t type, chunk_t blob)
{
asn1_parser_t *parser;
chunk_t object;
@ -195,7 +204,7 @@ static private_key_t *parse_encrypted_private_key(chunk_t blob)
}
case EPKINFO_ENCRYPTED_DATA:
{
key = decrypt_private_key(pkcs5, object);
key = decrypt_private_key(type, pkcs5, object);
break;
}
}
@ -230,7 +239,7 @@ private_key_t *pkcs8_private_key_load(key_type_t type, va_list args)
break;
}
/* we don't know whether it is encrypted or not, try both ways */
key = parse_encrypted_private_key(blob);
key = parse_encrypted_private_key(type, blob);
if (!key)
{
key = parse_private_key(blob);

View File

@ -177,6 +177,7 @@ END_TEST
static struct {
chunk_t key;
chunk_t pkcs8;
chunk_t spki;
chunk_t pub;
chunk_t fp_pk;
chunk_t fp_spki;
@ -252,6 +253,14 @@ static struct {
0x4c,0xa9,0xa2,0x42,0xbe,0xdd,0xdb,0xf7,0xd3,0x28,0x07,0x10,0x88,0x53,0x15,0xb2,
0x4f,0xb5,0x9d,0x47,0x9b,0xd6,0xc8,0xfe,0x5b,0xa2,0xd7,0xe1,0x13,0xca,0x0b,0xce,
0x7a,0xed,0xa2,0x3e,0xd5,0x9b,0xb8,0x8b,0x4f,0x02,0x03,0x01,0x00,0x01),
chunk_from_chars(
0x30,0x68,0x02,0x61,0x00,0xd1,0x5d,0x98,0x97,0x95,0x98,0x19,0x87,0x20,0x3f,0x10,
0xb0,0x05,0x36,0x1e,0x1b,0xcd,0xc8,0x93,0x66,0xd7,0x43,0xed,0x84,0xb0,0x3e,0x96,
0xd3,0xe7,0x27,0x0e,0xc0,0xba,0xdf,0x7e,0x32,0x05,0xd3,0x08,0xd6,0x44,0xd5,0x01,
0x2b,0x3e,0x5d,0xc0,0x37,0xae,0x4f,0xe0,0xea,0x8d,0x2c,0x42,0x4c,0xa9,0xa2,0x42,
0xbe,0xdd,0xdb,0xf7,0xd3,0x28,0x07,0x10,0x88,0x53,0x15,0xb2,0x4f,0xb5,0x9d,0x47,
0x9b,0xd6,0xc8,0xfe,0x5b,0xa2,0xd7,0xe1,0x13,0xca,0x0b,0xce,0x7a,0xed,0xa2,0x3e,
0xd5,0x9b,0xb8,0x8b,0x4f,0x02,0x03,0x01,0x00,0x01),
chunk_from_chars(
0x06,0xad,0x82,0xc3,0x58,0x22,0xbb,0x79,0xb5,0xfc,0x48,0xdb,0xa0,0x3c,0x39,0x60,
0x00,0x85,0x06,0xca),
@ -351,6 +360,16 @@ static struct {
0xb8,0x9d,0x07,0x84,0x03,0x68,0x6b,0x9f,0xbf,0xe5,0xd8,0x14,0x2a,0xe0,0xef,0xbd,
0x1a,0x61,0x0d,0x3a,0xc8,0x67,0xcd,0x99,0x90,0xe3,0xe6,0x52,0x83,0x02,0x03,0x01,
0x00,0x01),
chunk_from_chars(
0x30,0x81,0x89,0x02,0x81,0x81,0x00,0xc0,0xbd,0x48,0x83,0xbc,0xea,0x0b,0x32,0x06,
0x4b,0xf5,0x10,0x54,0x1b,0xba,0x88,0xc4,0x10,0x7e,0x47,0xec,0x0e,0xf9,0xb4,0xcf,
0x9a,0x02,0xc6,0xb3,0xaf,0x35,0xc8,0xaf,0x78,0x1a,0xbc,0x37,0x1a,0x25,0x7a,0x37,
0x24,0x73,0x53,0x9a,0xf0,0x44,0x64,0x5b,0x6b,0x64,0x4c,0xfa,0x83,0x3a,0x0f,0x77,
0x5d,0x7b,0x21,0xa2,0x25,0x00,0x11,0xae,0x72,0x36,0x35,0xd9,0x0d,0xef,0x5a,0xdd,
0x98,0x35,0x49,0xaf,0x44,0xa0,0x33,0x29,0xc0,0xca,0xf5,0x6f,0xfe,0xc1,0x06,0x4c,
0x80,0x9a,0x54,0xbe,0x46,0x1a,0x96,0xb1,0xf3,0x29,0xb8,0x9d,0x07,0x84,0x03,0x68,
0x6b,0x9f,0xbf,0xe5,0xd8,0x14,0x2a,0xe0,0xef,0xbd,0x1a,0x61,0x0d,0x3a,0xc8,0x67,
0xcd,0x99,0x90,0xe3,0xe6,0x52,0x83,0x02,0x03,0x01,0x00,0x01),
chunk_from_chars(
0xda,0xab,0x50,0x22,0x4b,0x4f,0x3b,0xd0,0x82,0xc4,0xa4,0x14,0x06,0x64,0x0b,0x6f,
0xad,0xbc,0x69,0xc0),
@ -491,6 +510,20 @@ static struct {
0x91,0xe2,0xfc,0x7b,0xea,0xb0,0x89,0x24,0xaa,0x00,0x29,0x8c,0x26,0x7c,0x94,0x54,
0x74,0xe4,0x11,0xa8,0x04,0x6f,0x40,0xeb,0xaf,0xed,0xac,0x75,0x33,0x02,0x03,0x01,
0x00,0x01),
chunk_from_chars(
0x30,0x81,0xc9,0x02,0x81,0xc1,0x00,0xba,0xe3,0x37,0x93,0x7e,0x42,0x13,0x3c,0xba,
0x41,0xc1,0x7b,0xf0,0xcc,0x7a,0x44,0xc6,0x54,0xc8,0x77,0x01,0x70,0x2f,0x6e,0x4a,
0xcf,0x2d,0x07,0xab,0x01,0xc0,0x43,0xab,0x8d,0x33,0xb3,0xd4,0xeb,0xe3,0x90,0xf6,
0x01,0x03,0x75,0x03,0x1d,0xe8,0x06,0x40,0x15,0xfa,0x96,0x0b,0xd5,0x26,0x64,0xea,
0x55,0x82,0x16,0x7b,0xd5,0x1e,0xaa,0x08,0xc7,0x30,0x1a,0x59,0xf8,0xd9,0xe3,0x9e,
0x89,0xd9,0x92,0x2c,0x32,0x79,0x0e,0xb3,0x25,0xbc,0x1d,0x7c,0x59,0xde,0x05,0x47,
0x8f,0x61,0x77,0xf5,0x4f,0xed,0x82,0x2c,0xf8,0x2a,0x3e,0x02,0xf3,0xc0,0x15,0x51,
0xde,0x05,0xc4,0xfc,0x80,0x91,0xae,0x06,0x1b,0xd7,0x39,0x8e,0x9a,0x6d,0xb3,0x2f,
0xb0,0xd0,0xc8,0x96,0xa6,0x88,0xb3,0x17,0xca,0x58,0xbe,0x38,0x2c,0x64,0x35,0x5a,
0x29,0xb7,0xf8,0x74,0x3d,0xbb,0xec,0x90,0x01,0x04,0x64,0x3d,0x38,0x0f,0x87,0xce,
0xd7,0xfc,0xd2,0x96,0x93,0x31,0x85,0x0d,0x2d,0xa5,0x91,0xe2,0xfc,0x7b,0xea,0xb0,
0x89,0x24,0xaa,0x00,0x29,0x8c,0x26,0x7c,0x94,0x54,0x74,0xe4,0x11,0xa8,0x04,0x6f,
0x40,0xeb,0xaf,0xed,0xac,0x75,0x33,0x02,0x03,0x01,0x00,0x01),
chunk_from_chars(
0x21,0x00,0x8c,0xe1,0x78,0x25,0x67,0x19,0xb7,0xd0,0xcb,0x13,0x01,0x7a,0xa3,0x71,
0x67,0x46,0x96,0xf1),
@ -671,6 +704,24 @@ static struct {
0xde,0x54,0x7d,0x95,0xd6,0x4e,0x58,0x12,0x06,0x60,0x22,0x33,0xf2,0x19,0x67,0x65,
0xdd,0xf3,0x42,0xb5,0x00,0x51,0x35,0xe5,0x62,0x4d,0x90,0x44,0xfb,0x7f,0x5b,0xb5,
0xe5,0x02,0x03,0x01,0x00,0x01),
chunk_from_chars(
0x30,0x82,0x01,0x0a,0x02,0x82,0x01,0x01,0x00,0xba,0xbf,0x27,0x0b,0x22,0x59,0xd8,
0x6f,0xff,0x26,0x5d,0x41,0x3d,0xb0,0x94,0x58,0x5d,0xc0,0x46,0xb6,0x77,0xa9,0x78,
0x10,0x6d,0xe9,0xbf,0xca,0x6f,0x04,0xe1,0xda,0x85,0x12,0x1e,0xe0,0xa6,0xc7,0xa2,
0x71,0x04,0x8b,0x6e,0x84,0xf9,0x86,0x2b,0xeb,0x72,0x01,0x72,0xc8,0x0a,0x83,0xa6,
0xf7,0xc0,0xd6,0x76,0x1d,0x28,0x38,0xb5,0x7e,0x6c,0x8c,0x6a,0x13,0xf4,0xf1,0x7f,
0xf2,0x79,0xae,0x73,0xba,0x1a,0x3f,0x30,0x65,0xb6,0x23,0xa7,0x94,0x34,0x29,0x87,
0xce,0x06,0x99,0xee,0x85,0x10,0xce,0x08,0xe2,0x8d,0xd5,0x47,0xf3,0xc8,0xf0,0x18,
0x41,0xc0,0x59,0x66,0x06,0xda,0xb6,0x18,0xd2,0xa3,0xa0,0xbd,0x3a,0x90,0x7f,0x37,
0x39,0xdf,0x98,0x55,0xa2,0x19,0x5e,0x37,0xbc,0x86,0xf3,0x02,0xf8,0x68,0x49,0x53,
0xf2,0x4b,0x3d,0x7a,0xe3,0x1d,0xa4,0x15,0x10,0xa6,0xce,0x8c,0xb8,0xfd,0x95,0x54,
0xa2,0x50,0xa2,0xd9,0x35,0x12,0x56,0xae,0xbc,0x51,0x33,0x6d,0xb8,0x63,0x7c,0x26,
0xab,0x19,0x01,0xa5,0xda,0xfa,0x4b,0xb6,0x57,0xd3,0x4b,0xdd,0xc0,0x62,0xc5,0x05,
0xb7,0xc3,0x2e,0x1f,0x17,0xc8,0x09,0x87,0x12,0x37,0x21,0xd7,0x7a,0x53,0xb0,0x47,
0x60,0xa2,0xb5,0x23,0x3b,0x99,0xdf,0xea,0x8b,0x94,0xea,0x9d,0x53,0x5d,0x02,0x52,
0xf7,0x29,0xfb,0x63,0xb0,0xff,0x27,0x5e,0xde,0x54,0x7d,0x95,0xd6,0x4e,0x58,0x12,
0x06,0x60,0x22,0x33,0xf2,0x19,0x67,0x65,0xdd,0xf3,0x42,0xb5,0x00,0x51,0x35,0xe5,
0x62,0x4d,0x90,0x44,0xfb,0x7f,0x5b,0xb5,0xe5,0x02,0x03,0x01,0x00,0x01),
chunk_from_chars(
0x4f,0xe8,0x82,0xee,0xaa,0x2c,0x7b,0x3f,0x3a,0xf1,0xb4,0xe1,0xe3,0x85,0xd4,0xb1,
0xb4,0x34,0x5c,0x2d),
@ -708,9 +759,17 @@ START_TEST(test_load)
pubkey = privkey->get_public_key(privkey);
ck_assert(pubkey != NULL);
ck_assert(pubkey->get_encoding(pubkey, PUBKEY_SPKI_ASN1_DER, &encoding));
ck_assert_chunk_eq(keys[_i].pub, encoding);
ck_assert_chunk_eq(keys[_i].spki, encoding);
chunk_free(&encoding);
if (!pubkey->get_encoding(pubkey, PUBKEY_ASN1_DER, &encoding))
{
warn("PUBKEY_ASN1_DER encoding not supported, ignored");
}
else
{
ck_assert_chunk_eq(keys[_i].pub, encoding);
chunk_free(&encoding);
}
ck_assert(pubkey->get_fingerprint(pubkey, KEYID_PUBKEY_SHA1, &fp));
ck_assert_chunk_eq(keys[_i].fp_pk, fp);
ck_assert(pubkey->get_fingerprint(pubkey, KEYID_PUBKEY_INFO_SHA1, &fp));

View File

@ -245,23 +245,29 @@ Suite *stream_suite_create()
{
Suite *s;
TCase *tc;
int count = countof(services);
if (getenv("TESTS_NO_IPV6"))
{
count--;
}
s = suite_create("stream");
tc = tcase_create("sync");
tcase_add_loop_test(tc, test_sync, 0, countof(services));
tcase_add_loop_test(tc, test_sync, 0, count);
suite_add_tcase(s, tc);
tc = tcase_create("async");
tcase_add_loop_test(tc, test_async, 0, countof(services));
tcase_add_loop_test(tc, test_async, 0, count);
suite_add_tcase(s, tc);
tc = tcase_create("all");
tcase_add_loop_test(tc, test_all, 0, countof(services));
tcase_add_loop_test(tc, test_all, 0, count);
suite_add_tcase(s, tc);
tc = tcase_create("concurrency");
tcase_add_loop_test(tc, test_concurrency, 0, countof(services));
tcase_add_loop_test(tc, test_concurrency, 0, count);
suite_add_tcase(s, tc);
return s;

View File

@ -456,23 +456,45 @@ static void collect_failure_info(array_t *failures, char *name, int i)
array_insert(failures, -1, &failure);
}
/**
* Context data to collect warnings
*/
typedef struct {
char *name;
int i;
array_t *warnings;
} warning_ctx_t;
/**
* Callback to collect warnings
*/
CALLBACK(warning_cb, void,
warning_ctx_t *ctx, const char *msg, const char *file, const int line)
{
failure_t warning = {
.name = ctx->name,
.i = ctx->i,
.file = file,
.line = line,
};
strncpy(warning.msg, msg, sizeof(warning.msg) - 1);
warning.msg[sizeof(warning.msg)-1] = 0;
array_insert(ctx->warnings, -1, &warning);
}
/**
* Collect warning information, add failure_t to array
*/
static bool collect_warning_info(array_t *warnings, char *name, int i)
{
failure_t warning = {
warning_ctx_t ctx = {
.name = name,
.i = i,
.warnings = warnings,
};
warning.line = test_warning_get(warning.msg, sizeof(warning.msg),
&warning.file);
if (warning.line)
{
array_insert(warnings, -1, &warning);
}
return warning.line;
return test_warnings_get(warning_cb, &ctx);
}
/**

View File

@ -50,19 +50,26 @@ static backtrace_t *failure_backtrace;
static bool worker_failed;
/**
* Warning message buf
* Warning information
*/
static char warning_buf[4096];
typedef struct {
/** Warning message */
char msg[BUF_LEN];
/** Source file warning was issued */
const char *file;
/** Line of source warning was issued */
int line;
} warning_info_t;
/**
* Source file warning was issued
* Warnings that occurred
*/
static const char *warning_file;
static warning_info_t warnings[3];
/**
* Line of source file warning was issued
* Current warning index
*/
static int warning_line;
static int warning_idx = -1;
/**
* See header.
@ -442,11 +449,16 @@ void test_warn_msg(const char *file, int line, char *fmt, ...)
{
va_list args;
if (++warning_idx >= countof(warnings))
{
return;
}
va_start(args, fmt);
vsnprintf(warning_buf, sizeof(warning_buf), fmt, args);
warning_line = line;
warning_file = file;
vsnprintf(warnings[warning_idx].msg, sizeof(warnings[warning_idx].msg),
fmt, args);
va_end(args);
warnings[warning_idx].file = file;
warnings[warning_idx].line = line;
}
/**
@ -479,20 +491,22 @@ int test_failure_get(char *msg, int len, const char **file)
/**
* See header.
*/
int test_warning_get(char *msg, int len, const char **file)
bool test_warnings_get(void (*cb)(void *ctx, const char *msg, const char *file,
const int line), void *ctx)
{
int line = warning_line;
int i;
if (!line)
if (warning_idx < 0)
{
return 0;
return FALSE;
}
for (i = 0; i <= warning_idx && i < countof(warnings); i++)
{
cb(ctx, warnings[i].msg, warnings[i].file, warnings[i].line);
}
strncpy(msg, warning_buf, len - 1);
msg[len - 1] = 0;
*file = warning_file;
/* reset state */
warning_line = 0;
return line;
warning_idx = -1;
return TRUE;
}
/**

View File

@ -219,6 +219,18 @@ void test_setup_timeout(int s);
*/
int test_failure_get(char *msg, int len, const char **file);
/**
* Get info about warnings if any were issued during the test. Resets the
* warning state.
*
* @param cb callback that receives a custom context object, message,
* source file and line of each warning
* @param ctx context object
* @return TRUE if any warnings were issued
*/
bool test_warnings_get(void (*cb)(void *ctx, const char *msg, const char *file,
const int line), void *ctx);
/**
* Get info about a warning if one was issued during the test. Resets the
* warning state.

View File

@ -597,6 +597,29 @@ static char *whitelist[] = {
"RAND_DRBG_get0_master",
"RAND_DRBG_get0_private",
"RAND_DRBG_get0_public",
/* OpenSSL 3.0 caches even more static stuff */
"ERR_set_debug",
"ERR_set_error",
"EVP_DigestSignInit",
"EVP_DigestVerifyInit",
"EVP_PKEY_encrypt_init",
"EVP_PKEY_decrypt_init",
"EVP_PKEY_derive_init",
"EVP_PKEY_sign_init",
"EVP_ASYM_CIPHER_fetch",
"EVP_CIPHER_fetch",
"EVP_KDF_fetch",
"EVP_KEYEXCH_fetch",
"EVP_KEYMGMT_fetch",
"EVP_MAC_fetch",
"EVP_MD_fetch",
"EVP_SIGNATURE_fetch",
"OSSL_DECODER_do_all_provided",
"OSSL_ENCODER_do_all_provided",
"OSSL_PROVIDER_try_load",
"OSSL_PROVIDER_load",
"RAND_get0_private",
"RAND_get0_public",
/* We get this via libcurl and OpenSSL 1.1.1 */
"CRYPTO_get_ex_new_index",
/* OpenSSL libssl */