summaryrefslogtreecommitdiffstats
path: root/src/lib/crypto/ec_ossl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/crypto/ec_ossl.cpp')
-rw-r--r--src/lib/crypto/ec_ossl.cpp181
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
}