You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
281 lines
11 KiB
281 lines
11 KiB
8 months ago
|
From b36e02a241389dc00ceee9abbaa3a89b731477ed Mon Sep 17 00:00:00 2001
|
||
|
From: Dan Streetman <ddstreet@ieee.org>
|
||
|
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 <openssl/bio.h>
|
||
|
# include <openssl/bn.h>
|
||
|
+# include <openssl/crypto.h>
|
||
|
# include <openssl/err.h>
|
||
|
# include <openssl/evp.h>
|
||
|
# include <openssl/opensslv.h>
|
||
|
@@ -24,6 +25,7 @@
|
||
|
# include <openssl/param_build.h>
|
||
|
# 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
|