diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 9ac3ef098..a807ef98b 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -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' }} diff --git a/configure.ac b/configure.ac index 04a1c5ce3..de77dc373 100644 --- a/configure.ac +++ b/configure.ac @@ -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]) diff --git a/scripts/test.sh b/scripts/test.sh index c397e00ec..b1e352033 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -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 diff --git a/src/libstrongswan/plugins/openssl/Makefile.am b/src/libstrongswan/plugins/openssl/Makefile.am index 4df4a81b4..979bd8886 100644 --- a/src/libstrongswan/plugins/openssl/Makefile.am +++ b/src/libstrongswan/plugins/openssl/Makefile.am @@ -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 \ diff --git a/src/libstrongswan/plugins/openssl/openssl_aead.c b/src/libstrongswan/plugins/openssl/openssl_aead.c index 9262cfb23..4d1aa7cd4 100644 --- a/src/libstrongswan/plugins/openssl/openssl_aead.c +++ b/src/libstrongswan/plugins/openssl/openssl_aead.c @@ -15,8 +15,6 @@ #include -#if OPENSSL_VERSION_NUMBER >= 0x1000100fL - #include "openssl_aead.h" #include @@ -363,5 +361,3 @@ aead_t *openssl_aead_create(encryption_algorithm_t algo, return &this->public; } - -#endif /* OPENSSL_VERSION_NUMBER */ diff --git a/src/libstrongswan/plugins/openssl/openssl_diffie_hellman.c b/src/libstrongswan/plugins/openssl/openssl_diffie_hellman.c index bee56a425..01b0fa0ea 100644 --- a/src/libstrongswan/plugins/openssl/openssl_diffie_hellman.c +++ b/src/libstrongswan/plugins/openssl/openssl_diffie_hellman.c @@ -18,9 +18,15 @@ #ifndef OPENSSL_NO_DH +#include #include #include +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include +#include +#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; } diff --git a/src/libstrongswan/plugins/openssl/openssl_ec_diffie_hellman.c b/src/libstrongswan/plugins/openssl/openssl_ec_diffie_hellman.c index d591b0517..35969654e 100644 --- a/src/libstrongswan/plugins/openssl/openssl_ec_diffie_hellman.c +++ b/src/libstrongswan/plugins/openssl/openssl_ec_diffie_hellman.c @@ -23,6 +23,15 @@ #if OPENSSL_VERSION_NUMBER < 0x1010000fL #include +#elif OPENSSL_VERSION_NUMBER >= 0x30000000L +#include +#include +#include +#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; } diff --git a/src/libstrongswan/plugins/openssl/openssl_ec_private_key.c b/src/libstrongswan/plugins/openssl/openssl_ec_private_key.c index 0c208be7e..54c20dcb9 100644 --- a/src/libstrongswan/plugins/openssl/openssl_ec_private_key.c +++ b/src/libstrongswan/plugins/openssl/openssl_ec_private_key.c @@ -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 */ diff --git a/src/libstrongswan/plugins/openssl/openssl_ec_public_key.c b/src/libstrongswan/plugins/openssl/openssl_ec_public_key.c index 79ab82db5..7b529719b 100644 --- a/src/libstrongswan/plugins/openssl/openssl_ec_public_key.c +++ b/src/libstrongswan/plugins/openssl/openssl_ec_public_key.c @@ -27,6 +27,10 @@ #include #include +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include +#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 */ diff --git a/src/libstrongswan/plugins/openssl/openssl_engine.c b/src/libstrongswan/plugins/openssl/openssl_engine.c new file mode 100644 index 000000000..a4b2cba5f --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_engine.c @@ -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 . + * + * 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 +#include + +#include "openssl_engine.h" + +#if !defined(OPENSSL_NO_ENGINE) && \ + (OPENSSL_VERSION_NUMBER < 0x30000000L || !defined(OPENSSL_NO_DEPRECATED)) + +#include + +#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 */ diff --git a/src/libstrongswan/plugins/openssl/openssl_engine.h b/src/libstrongswan/plugins/openssl/openssl_engine.h new file mode 100644 index 000000000..d25d12321 --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_engine.h @@ -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 + +/** + * 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_ @}*/ diff --git a/src/libstrongswan/plugins/openssl/openssl_hmac.c b/src/libstrongswan/plugins/openssl/openssl_hmac.c index d186134e2..e4fe7fa76 100644 --- a/src/libstrongswan/plugins/openssl/openssl_hmac.c +++ b/src/libstrongswan/plugins/openssl/openssl_hmac.c @@ -42,6 +42,10 @@ #include #include +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include +#endif + #include "openssl_hmac.h" #include @@ -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)) diff --git a/src/libstrongswan/plugins/openssl/openssl_plugin.c b/src/libstrongswan/plugins/openssl/openssl_plugin.c index c21292b09..77422f8be 100644 --- a/src/libstrongswan/plugins/openssl/openssl_plugin.c +++ b/src/libstrongswan/plugins/openssl/openssl_plugin.c @@ -25,9 +25,6 @@ #include #include #include -#ifndef OPENSSL_NO_ENGINE -#include -#endif #ifndef OPENSSL_NO_ECDH #include #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 diff --git a/src/libstrongswan/plugins/openssl/openssl_rsa_private_key.c b/src/libstrongswan/plugins/openssl/openssl_rsa_private_key.c index 15a4ead3d..f8334cba9 100644 --- a/src/libstrongswan/plugins/openssl/openssl_rsa_private_key.c +++ b/src/libstrongswan/plugins/openssl/openssl_rsa_private_key.c @@ -31,6 +31,11 @@ #include #include +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include +#include +#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 */ diff --git a/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c b/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c index d5c9eed1e..c5a1eb725 100644 --- a/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c +++ b/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c @@ -31,6 +31,11 @@ #include #include +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include +#include +#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 */ diff --git a/src/libstrongswan/plugins/openssl/openssl_sha1_prf.c b/src/libstrongswan/plugins/openssl/openssl_sha1_prf.c index 8371bc17f..fe68f0c17 100644 --- a/src/libstrongswan/plugins/openssl/openssl_sha1_prf.c +++ b/src/libstrongswan/plugins/openssl/openssl_sha1_prf.c @@ -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 #include -#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 */ diff --git a/src/libstrongswan/plugins/openssl/openssl_util.c b/src/libstrongswan/plugins/openssl/openssl_util.c index 55fe80ae8..fce8d29a2 100644 --- a/src/libstrongswan/plugins/openssl/openssl_util.c +++ b/src/libstrongswan/plugins/openssl/openssl_util.c @@ -22,6 +22,11 @@ #include #include +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +/* for EVP_PKEY_CTX_set_dh_pad */ +#include +#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. */ diff --git a/src/libstrongswan/plugins/openssl/openssl_util.h b/src/libstrongswan/plugins/openssl/openssl_util.h index 0b5562c4e..150eebe20 100644 --- a/src/libstrongswan/plugins/openssl/openssl_util.h +++ b/src/libstrongswan/plugins/openssl/openssl_util.h @@ -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. * diff --git a/src/libstrongswan/plugins/openssl/openssl_x_diffie_hellman.c b/src/libstrongswan/plugins/openssl/openssl_x_diffie_hellman.c index 3d41eb95a..ad36b3dd4 100644 --- a/src/libstrongswan/plugins/openssl/openssl_x_diffie_hellman.c +++ b/src/libstrongswan/plugins/openssl/openssl_x_diffie_hellman.c @@ -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); } diff --git a/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c b/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c index 1d7a48fb2..9916def19 100644 --- a/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c +++ b/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c @@ -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); diff --git a/src/libstrongswan/tests/suites/test_rsa.c b/src/libstrongswan/tests/suites/test_rsa.c index 0ab9dc5c9..33a223d57 100644 --- a/src/libstrongswan/tests/suites/test_rsa.c +++ b/src/libstrongswan/tests/suites/test_rsa.c @@ -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)); diff --git a/src/libstrongswan/tests/suites/test_stream.c b/src/libstrongswan/tests/suites/test_stream.c index 899306af2..d273cd057 100644 --- a/src/libstrongswan/tests/suites/test_stream.c +++ b/src/libstrongswan/tests/suites/test_stream.c @@ -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; diff --git a/src/libstrongswan/tests/test_runner.c b/src/libstrongswan/tests/test_runner.c index 67fe80f7f..6abdfd0c1 100644 --- a/src/libstrongswan/tests/test_runner.c +++ b/src/libstrongswan/tests/test_runner.c @@ -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); } /** diff --git a/src/libstrongswan/tests/test_suite.c b/src/libstrongswan/tests/test_suite.c index 412d9fbf6..1149e1357 100644 --- a/src/libstrongswan/tests/test_suite.c +++ b/src/libstrongswan/tests/test_suite.c @@ -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; } /** diff --git a/src/libstrongswan/tests/test_suite.h b/src/libstrongswan/tests/test_suite.h index a35e9ec63..e0629356b 100644 --- a/src/libstrongswan/tests/test_suite.h +++ b/src/libstrongswan/tests/test_suite.h @@ -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. diff --git a/src/libstrongswan/utils/leak_detective.c b/src/libstrongswan/utils/leak_detective.c index 0476353ad..5e3be5acc 100644 --- a/src/libstrongswan/utils/leak_detective.c +++ b/src/libstrongswan/utils/leak_detective.c @@ -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 */