From b36e02a241389dc00ceee9abbaa3a89b731477ed Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Tue, 4 Jul 2023 18:52:59 -0400 Subject: [PATCH] openssl: add ecc_pkey_new(), ecc_pkey_from_curve_x_y(), ecc_pkey_to_curve_x_y() Add function to create openssl pkey from ECC curve and point, and function to get curve id and x/y point from existing ECC pkey. Also add function to create new ECC key for specified curve. Also add DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO() to handle case when func() is a macro, not a function symbol; specifically in this case it is used for OPENSSL_free() which is a macro. (cherry picked from commit 900e73f80e87df2295faabd66f66d42c973d8ad6) Related: RHEL-16182 --- src/basic/macro.h | 9 ++ src/shared/openssl-util.c | 193 ++++++++++++++++++++++++++++++++++++++ src/shared/openssl-util.h | 8 ++ 3 files changed, 210 insertions(+) diff --git a/src/basic/macro.h b/src/basic/macro.h index 9c36683ef9..9cb7ae5077 100644 --- a/src/basic/macro.h +++ b/src/basic/macro.h @@ -359,6 +359,15 @@ static inline int __coverity_check_and_return__(int condition) { } \ } +/* When func() doesn't return the appropriate type, and is also a macro, set variable to empty afterwards. */ +#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO(type, func, empty) \ + static inline void func##p(type *p) { \ + if (*p != (empty)) { \ + func(*p); \ + *p = (empty); \ + } \ + } + #define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope) \ scope type *name##_ref(type *p) { \ if (!p) \ diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c index c02440495d..c3f8f91eb3 100644 --- a/src/shared/openssl-util.c +++ b/src/shared/openssl-util.c @@ -271,6 +271,199 @@ int rsa_pkey_new(size_t bits, EVP_PKEY **ret) { return 0; } +/* Generate ECC public key from provided curve ID and x/y points. */ +int ecc_pkey_from_curve_x_y( + int curve_id, + const void *x, + size_t x_size, + const void *y, + size_t y_size, + EVP_PKEY **ret) { + + assert(x); + assert(y); + assert(ret); + + _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); + if (!ctx) + return log_oom_debug(); + + _cleanup_(BN_freep) BIGNUM *bn_x = BN_bin2bn(x, x_size, NULL), *bn_y = BN_bin2bn(y, y_size, NULL); + if (!bn_x || !bn_y) + return log_oom_debug(); + + _cleanup_(EC_GROUP_freep) EC_GROUP *group = EC_GROUP_new_by_curve_name(curve_id); + if (!group) + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "ECC curve id %d not supported.", curve_id); + + _cleanup_(EC_POINT_freep) EC_POINT *point = EC_POINT_new(group); + if (!point) + return log_oom_debug(); + + if (!EC_POINT_set_affine_coordinates(group, point, bn_x, bn_y, NULL)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set ECC coordinates."); + +#if OPENSSL_VERSION_MAJOR >= 3 + if (EVP_PKEY_fromdata_init(ctx) <= 0) + return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Failed to initialize EVP_PKEY_CTX."); + + _cleanup_(OSSL_PARAM_BLD_freep) OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new(); + if (!bld) + return log_oom_debug(); + + if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME, (char*) OSSL_EC_curve_nid2name(curve_id), 0)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to add ECC OSSL_PKEY_PARAM_GROUP_NAME."); + + _cleanup_(OPENSSL_freep) void *pbuf = NULL; + size_t pbuf_len = 0; + pbuf_len = EC_POINT_point2buf(group, point, POINT_CONVERSION_UNCOMPRESSED, (unsigned char**) &pbuf, NULL); + if (pbuf_len == 0) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert ECC point to buffer."); + + if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, pbuf, pbuf_len)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to add ECC OSSL_PKEY_PARAM_PUB_KEY."); + + _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(bld); + if (!params) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to build ECC OSSL_PARAM."); + + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL; + if (EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0) + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "Failed to create ECC EVP_PKEY."); +#else + _cleanup_(EC_KEY_freep) EC_KEY *eckey = EC_KEY_new(); + if (!eckey) + return log_oom_debug(); + + if (!EC_KEY_set_group(eckey, group)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set ECC group."); + + if (!EC_KEY_set_public_key(eckey, point)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set ECC point."); + + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = EVP_PKEY_new(); + if (!pkey) + return log_oom_debug(); + + if (!EVP_PKEY_assign_EC_KEY(pkey, eckey)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to assign ECC key."); + /* pkey owns this now, don't free */ + TAKE_PTR(eckey); +#endif + + *ret = TAKE_PTR(pkey); + + return 0; +} + +int ecc_pkey_to_curve_x_y( + const EVP_PKEY *pkey, + int *ret_curve_id, + void **ret_x, + size_t *ret_x_size, + void **ret_y, + size_t *ret_y_size) { + + _cleanup_(BN_freep) BIGNUM *bn_x = NULL, *bn_y = NULL; + int curve_id; + + assert(pkey); + +#if OPENSSL_VERSION_MAJOR >= 3 + size_t name_size; + if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0, &name_size)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get ECC group name size."); + + _cleanup_free_ char *name = malloc(name_size + 1); + if (!name) + return log_oom_debug(); + + if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, name, name_size + 1, NULL)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get ECC group name."); + + curve_id = OBJ_sn2nid(name); + if (curve_id == NID_undef) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get ECC curve id."); + + if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_X, &bn_x)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get ECC point x."); + + if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, &bn_y)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get ECC point y."); +#else + const EC_KEY *eckey = EVP_PKEY_get0_EC_KEY((EVP_PKEY*) pkey); + if (!eckey) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get EC_KEY."); + + const EC_GROUP *group = EC_KEY_get0_group(eckey); + if (!group) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get EC_GROUP."); + + curve_id = EC_GROUP_get_curve_name(group); + if (curve_id == NID_undef) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get ECC curve id."); + + const EC_POINT *point = EC_KEY_get0_public_key(eckey); + if (!point) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get EC_POINT."); + + bn_x = BN_new(); + bn_y = BN_new(); + if (!bn_x || !bn_y) + return log_oom_debug(); + + if (!EC_POINT_get_affine_coordinates(group, point, bn_x, bn_y, NULL)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get ECC x/y."); +#endif + + size_t x_size = BN_num_bytes(bn_x), y_size = BN_num_bytes(bn_y); + _cleanup_free_ void *x = malloc(x_size), *y = malloc(y_size); + if (!x || !y) + return log_oom_debug(); + + assert(BN_bn2bin(bn_x, x) == (int) x_size); + assert(BN_bn2bin(bn_y, y) == (int) y_size); + + if (ret_curve_id) + *ret_curve_id = curve_id; + if (ret_x) + *ret_x = TAKE_PTR(x); + if (ret_x_size) + *ret_x_size = x_size; + if (ret_y) + *ret_y = TAKE_PTR(y); + if (ret_y_size) + *ret_y_size = y_size; + + return 0; +} + +/* Generate a new ECC key for the specified ECC curve id. */ +int ecc_pkey_new(int curve_id, EVP_PKEY **ret) { + assert(ret); + + _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); + if (!ctx) + return log_oom_debug(); + + if (EVP_PKEY_keygen_init(ctx) <= 0) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to initialize EVP_PKEY_CTX."); + + if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, curve_id) <= 0) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set ECC curve %d.", curve_id); + + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL; + if (EVP_PKEY_keygen(ctx, &pkey) <= 0) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to generate ECC key."); + + *ret = TAKE_PTR(pkey); + + return 0; +} + int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size) { _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX* m = NULL; _cleanup_free_ void *d = NULL, *h = NULL; diff --git a/src/shared/openssl-util.h b/src/shared/openssl-util.h index b9aacfd276..90158f589b 100644 --- a/src/shared/openssl-util.h +++ b/src/shared/openssl-util.h @@ -9,6 +9,7 @@ #if HAVE_OPENSSL # include # include +# include # include # include # include @@ -24,6 +25,7 @@ # include # endif +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO(void*, OPENSSL_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(X509_NAME*, X509_NAME_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_PKEY_CTX*, EVP_PKEY_CTX_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_CIPHER_CTX*, EVP_CIPHER_CTX_free, NULL); @@ -65,6 +67,12 @@ int rsa_pkey_from_n_e(const void *n, size_t n_size, const void *e, size_t e_size int rsa_pkey_to_n_e(const EVP_PKEY *pkey, void **ret_n, size_t *ret_n_size, void **ret_e, size_t *ret_e_size); +int ecc_pkey_from_curve_x_y(int curve_id, const void *x, size_t x_size, const void *y, size_t y_size, EVP_PKEY **ret); + +int ecc_pkey_to_curve_x_y(const EVP_PKEY *pkey, int *ret_curve_id, void **ret_x, size_t *ret_x_size, void **ret_y, size_t *ret_y_size); + +int ecc_pkey_new(int curve_id, EVP_PKEY **ret); + int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size); #else