diff options
Diffstat (limited to 'src/lib/crypto/ec_ossl.cpp')
-rw-r--r-- | src/lib/crypto/ec_ossl.cpp | 181 |
1 files changed, 164 insertions, 17 deletions
diff --git a/src/lib/crypto/ec_ossl.cpp b/src/lib/crypto/ec_ossl.cpp index 6974b4c..46c4677 100644 --- a/src/lib/crypto/ec_ossl.cpp +++ b/src/lib/crypto/ec_ossl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, [Ribose Inc](https://www.ribose.com). + * Copyright (c) 2021, 2023 [Ribose Inc](https://www.ribose.com). * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, @@ -36,6 +36,10 @@ #include <openssl/objects.h> #include <openssl/err.h> #include <openssl/ec.h> +#if defined(CRYPTO_BACKEND_OPENSSL3) +#include <openssl/core_names.h> +#include <openssl/param_build.h> +#endif static bool ec_is_raw_key(const pgp_curve_t curve) @@ -61,26 +65,34 @@ ec_generate_pkey(const pgp_pubkey_alg_t alg_id, const pgp_curve_t curve) } int nid = OBJ_sn2nid(ec_desc->openssl_name); if (nid == NID_undef) { + /* LCOV_EXCL_START */ RNP_LOG("Unknown SN: %s", ec_desc->openssl_name); return NULL; + /* LCOV_EXCL_END */ } bool raw = ec_is_raw_key(curve); EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(raw ? nid : EVP_PKEY_EC, NULL); if (!ctx) { + /* LCOV_EXCL_START */ RNP_LOG("Failed to create ctx: %lu", ERR_peek_last_error()); return NULL; + /* LCOV_EXCL_END */ } EVP_PKEY *pkey = NULL; if (EVP_PKEY_keygen_init(ctx) <= 0) { + /* LCOV_EXCL_START */ RNP_LOG("Failed to init keygen: %lu", ERR_peek_last_error()); goto done; + /* LCOV_EXCL_END */ } if (!raw && (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, nid) <= 0)) { + /* LCOV_EXCL_START */ RNP_LOG("Failed to set curve nid: %lu", ERR_peek_last_error()); goto done; + /* LCOV_EXCL_END */ } if (EVP_PKEY_keygen(ctx, &pkey) <= 0) { - RNP_LOG("EC keygen failed: %lu", ERR_peek_last_error()); + RNP_LOG("EC keygen failed: %lu", ERR_peek_last_error()); // LCOV_EXCL_LINE } done: EVP_PKEY_CTX_free(ctx); @@ -94,8 +106,10 @@ ec_write_raw_seckey(EVP_PKEY *pkey, pgp_ec_key_t *key) static_assert(sizeof(key->x.mpi) > 32, "mpi is too small."); key->x.len = sizeof(key->x.mpi); if (EVP_PKEY_get_raw_private_key(pkey, key->x.mpi, &key->x.len) <= 0) { + /* LCOV_EXCL_START */ RNP_LOG("Failed get raw private key: %lu", ERR_peek_last_error()); return false; + /* LCOV_EXCL_END */ } assert(key->x.len == 32); if (EVP_PKEY_id(pkey) == EVP_PKEY_X25519) { @@ -107,6 +121,30 @@ ec_write_raw_seckey(EVP_PKEY *pkey, pgp_ec_key_t *key) return true; } +static bool +ec_write_seckey(EVP_PKEY *pkey, pgp_mpi_t &key) +{ +#if defined(CRYPTO_BACKEND_OPENSSL3) + rnp::bn x; + return EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, x.ptr()) && + bn2mpi(x.get(), &key); +#else + const bignum_t *x = NULL; + const EC_KEY * ec = EVP_PKEY_get0_EC_KEY(pkey); + if (!ec) { + /* LCOV_EXCL_START */ + RNP_LOG("Failed to retrieve EC key: %lu", ERR_peek_last_error()); + return false; + /* LCOV_EXCL_END */ + } + x = EC_KEY_get0_private_key(ec); + if (!x) { + return false; + } + return bn2mpi(x, &key); +#endif +} + rnp_result_t ec_generate(rnp::RNG * rng, pgp_ec_key_t * key, @@ -125,24 +163,19 @@ ec_generate(rnp::RNG * rng, EVP_PKEY_free(pkey); return ret; } - const EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey); - if (!ec) { - RNP_LOG("Failed to retrieve EC key: %lu", ERR_peek_last_error()); - goto done; - } if (!ec_write_pubkey(pkey, key->p, curve)) { + /* LCOV_EXCL_START */ RNP_LOG("Failed to write pubkey."); goto done; + /* LCOV_EXCL_END */ } - const bignum_t *x; - x = EC_KEY_get0_private_key(ec); - if (!x) { - ret = RNP_ERROR_BAD_STATE; + if (!ec_write_seckey(pkey, key->x)) { + /* LCOV_EXCL_START */ + RNP_LOG("Failed to write seckey."); goto done; + /* LCOV_EXCL_END */ } - if (bn2mpi(x, &key->x)) { - ret = RNP_SUCCESS; - } + ret = RNP_SUCCESS; done: EVP_PKEY_free(pkey); return ret; @@ -161,7 +194,7 @@ ec_load_raw_key(const pgp_mpi_t &keyp, const pgp_mpi_t *keyx, int nid) EVP_PKEY *evpkey = EVP_PKEY_new_raw_public_key(nid, NULL, &keyp.mpi[1], mpi_bytes(&keyp) - 1); if (!evpkey) { - RNP_LOG("Failed to load public key: %lu", ERR_peek_last_error()); + RNP_LOG("Failed to load public key: %lu", ERR_peek_last_error()); // LCOV_EXCL_LINE } return evpkey; } @@ -189,10 +222,68 @@ ec_load_raw_key(const pgp_mpi_t &keyp, const pgp_mpi_t *keyx, int nid) evpkey = EVP_PKEY_new_raw_private_key(nid, NULL, prkey.data(), 32); } if (!evpkey) { - RNP_LOG("Failed to load private key: %lu", ERR_peek_last_error()); + RNP_LOG("Failed to load private key: %lu", ERR_peek_last_error()); // LCOV_EXCL_LINE + } + return evpkey; +} + +#if defined(CRYPTO_BACKEND_OPENSSL3) +static OSSL_PARAM * +ec_build_params(const pgp_mpi_t &p, bignum_t *x, const char *curve) +{ + OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new(); + if (!bld) { + return NULL; + } + if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME, curve, 0) || + !OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, p.mpi, p.len) || + (x && !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, x))) { + /* LCOV_EXCL_START */ + OSSL_PARAM_BLD_free(bld); + return NULL; + /* LCOV_EXCL_END */ + } + OSSL_PARAM *param = OSSL_PARAM_BLD_to_param(bld); + OSSL_PARAM_BLD_free(bld); + return param; +} + +static EVP_PKEY * +ec_load_key_openssl3(const pgp_mpi_t & keyp, + const pgp_mpi_t * keyx, + const ec_curve_desc_t *curv_desc) +{ + rnp::bn x(keyx ? mpi2bn(keyx) : NULL); + OSSL_PARAM *params = ec_build_params(keyp, x.get(), curv_desc->openssl_name); + if (!params) { + /* LCOV_EXCL_START */ + RNP_LOG("failed to build ec params"); + return NULL; + /* LCOV_EXCL_END */ + } + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); + if (!ctx) { + /* LCOV_EXCL_START */ + RNP_LOG("failed to create ec context"); + OSSL_PARAM_free(params); + return NULL; + /* LCOV_EXCL_END */ } + EVP_PKEY *evpkey = NULL; + if ((EVP_PKEY_fromdata_init(ctx) != 1) || + (EVP_PKEY_fromdata( + ctx, &evpkey, keyx ? EVP_PKEY_KEYPAIR : EVP_PKEY_PUBLIC_KEY, params) != 1)) { + /* LCOV_EXCL_START */ + RNP_LOG("failed to create ec key from data"); + /* Some version of OpenSSL may leave evpkey non-NULL after failure, so let's be safe */ + evpkey = NULL; + /* LCOV_EXCL_END */ + } + OSSL_PARAM_free(params); + EVP_PKEY_CTX_free(ctx); return evpkey; } +#endif EVP_PKEY * ec_load_key(const pgp_mpi_t &keyp, const pgp_mpi_t *keyx, pgp_curve_t curve) @@ -208,20 +299,27 @@ ec_load_key(const pgp_mpi_t &keyp, const pgp_mpi_t *keyx, pgp_curve_t curve) } int nid = OBJ_sn2nid(curv_desc->openssl_name); if (nid == NID_undef) { + /* LCOV_EXCL_START */ RNP_LOG("Unknown SN: %s", curv_desc->openssl_name); return NULL; + /* LCOV_EXCL_END */ } /* EdDSA and X25519 keys are loaded in a different way */ if (ec_is_raw_key(curve)) { return ec_load_raw_key(keyp, keyx, nid); } +#if defined(CRYPTO_BACKEND_OPENSSL3) + return ec_load_key_openssl3(keyp, keyx, curv_desc); +#else EC_KEY *ec = EC_KEY_new_by_curve_name(nid); if (!ec) { + /* LCOV_EXCL_START */ RNP_LOG("Failed to create EC key with group %d (%s): %s", nid, curv_desc->openssl_name, ERR_reason_error_string(ERR_peek_last_error())); return NULL; + /* LCOV_EXCL_END */ } bool res = false; @@ -229,22 +327,30 @@ ec_load_key(const pgp_mpi_t &keyp, const pgp_mpi_t *keyx, pgp_curve_t curve) EVP_PKEY *pkey = NULL; EC_POINT *p = EC_POINT_new(EC_KEY_get0_group(ec)); if (!p) { + /* LCOV_EXCL_START */ RNP_LOG("Failed to allocate point: %lu", ERR_peek_last_error()); goto done; + /* LCOV_EXCL_END */ } if (EC_POINT_oct2point(EC_KEY_get0_group(ec), p, keyp.mpi, keyp.len, NULL) <= 0) { + /* LCOV_EXCL_START */ RNP_LOG("Failed to decode point: %lu", ERR_peek_last_error()); goto done; + /* LCOV_EXCL_END */ } if (EC_KEY_set_public_key(ec, p) <= 0) { + /* LCOV_EXCL_START */ RNP_LOG("Failed to set public key: %lu", ERR_peek_last_error()); goto done; + /* LCOV_EXCL_END */ } pkey = EVP_PKEY_new(); if (!pkey) { + /* LCOV_EXCL_START */ RNP_LOG("EVP_PKEY allocation failed: %lu", ERR_peek_last_error()); goto done; + /* LCOV_EXCL_END */ } if (!keyx) { res = true; @@ -253,12 +359,16 @@ ec_load_key(const pgp_mpi_t &keyp, const pgp_mpi_t *keyx, pgp_curve_t curve) x = mpi2bn(keyx); if (!x) { + /* LCOV_EXCL_START */ RNP_LOG("allocation failed"); goto done; + /* LCOV_EXCL_END */ } if (EC_KEY_set_private_key(ec, x) <= 0) { + /* LCOV_EXCL_START */ RNP_LOG("Failed to set secret key: %lu", ERR_peek_last_error()); goto done; + /* LCOV_EXCL_END */ } res = true; done: @@ -273,6 +383,7 @@ done: pkey = NULL; } return pkey; +#endif } rnp_result_t @@ -296,14 +407,18 @@ ec_validate_key(const pgp_ec_key_t &key, bool secret) rnp_result_t ret = RNP_ERROR_GENERIC; EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(evpkey, NULL); if (!ctx) { + /* LCOV_EXCL_START */ RNP_LOG("Context allocation failed: %lu", ERR_peek_last_error()); goto done; + /* LCOV_EXCL_END */ } int res; res = secret ? EVP_PKEY_check(ctx) : EVP_PKEY_public_check(ctx); if (res < 0) { + /* LCOV_EXCL_START */ auto err = ERR_peek_last_error(); RNP_LOG("EC key check failed: %lu (%s)", err, ERR_reason_error_string(err)); + /* LCOV_EXCL_END */ } if (res > 0) { ret = RNP_SUCCESS; @@ -321,29 +436,61 @@ ec_write_pubkey(EVP_PKEY *pkey, pgp_mpi_t &mpi, pgp_curve_t curve) /* EdDSA and X25519 keys are saved in a different way */ mpi.len = sizeof(mpi.mpi) - 1; if (EVP_PKEY_get_raw_public_key(pkey, &mpi.mpi[1], &mpi.len) <= 0) { + /* LCOV_EXCL_START */ RNP_LOG("Failed get raw public key: %lu", ERR_peek_last_error()); return false; + /* LCOV_EXCL_END */ } assert(mpi.len == 32); mpi.mpi[0] = 0x40; mpi.len++; return true; } +#if defined(CRYPTO_BACKEND_OPENSSL3) + const ec_curve_desc_t *ec_desc = get_curve_desc(curve); + if (!ec_desc) { + return false; + } + size_t flen = BITS_TO_BYTES(ec_desc->bitlen); + rnp::bn qx; + rnp::bn qy; + + /* OpenSSL before 3.0.9 by default uses compressed point for OSSL_PKEY_PARAM_PUB_KEY so use + * this approach */ + bool res = EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_X, qx.ptr()) && + EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, qy.ptr()); + if (!res) { + return false; + } + /* Compose uncompressed point in mpi */ + size_t xlen = qx.bytes(); + size_t ylen = qy.bytes(); + assert((xlen <= flen) && (ylen <= flen)); + memset(mpi.mpi, 0, sizeof(mpi.mpi)); + mpi.mpi[0] = 0x04; + mpi.len = 2 * flen + 1; + return qx.bin(&mpi.mpi[1 + flen - xlen]) && qy.bin(&mpi.mpi[1 + 2 * flen - ylen]); +#else const EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey); if (!ec) { + /* LCOV_EXCL_START */ RNP_LOG("Failed to retrieve EC key: %lu", ERR_peek_last_error()); return false; + /* LCOV_EXCL_END */ } const EC_POINT *p = EC_KEY_get0_public_key(ec); if (!p) { + /* LCOV_EXCL_START */ RNP_LOG("Null point: %lu", ERR_peek_last_error()); return false; + /* LCOV_EXCL_END */ } /* call below adds leading zeroes if needed */ mpi.len = EC_POINT_point2oct( EC_KEY_get0_group(ec), p, POINT_CONVERSION_UNCOMPRESSED, mpi.mpi, sizeof(mpi.mpi), NULL); if (!mpi.len) { - RNP_LOG("Failed to encode public key: %lu", ERR_peek_last_error()); + RNP_LOG("Failed to encode public key: %lu", ERR_peek_last_error()); // LCOV_EXCL_LINE } return mpi.len; +#endif } |