From e34d393d7c73a58b3bf6fccbe88ea1eedfe1d7b7 Mon Sep 17 00:00:00 2001
From: Petr Gotthard <petr.gotthard@centrum.cz>
Date: Sun, 15 Aug 2021 15:58:28 +0200
Subject: [PATCH 14/17] openssl: Implement EVP_PKEY based key export
The `RSA_KEY` and `EC_KEY` are not publicly available in OpenSSL 3.0 and
the generic `EVP_PKEY` must be used instead.
Signed-off-by: Petr Gotthard <petr.gotthard@centrum.cz>
---
lib/tpm2_convert.c | 319 ++++++++++++++++++++++++++++++++-------------
lib/tpm2_convert.h | 20 +++
2 files changed, 245 insertions(+), 94 deletions(-)
diff --git a/lib/tpm2_convert.c b/lib/tpm2_convert.c
index cc1c18ab..986fc1a7 100644
--- a/lib/tpm2_convert.c
+++ b/lib/tpm2_convert.c
@@ -9,6 +9,13 @@
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/pem.h>
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+#include <openssl/rsa.h>
+#else
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/param_build.h>
+#endif
#include "files.h"
#include "log.h"
@@ -70,36 +77,49 @@ bool tpm2_convert_pubkey_save(TPM2B_PUBLIC *public,
return false;
}
-static bool convert_pubkey_RSA(TPMT_PUBLIC *public,
- tpm2_convert_pubkey_fmt format, BIO *bio) {
+EVP_PKEY *convert_pubkey_RSA(TPMT_PUBLIC *public) {
- bool ret = false;
- RSA *ssl_rsa_key = NULL;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ RSA *rsa_key = NULL;
+#else
+ OSSL_PARAM_BLD *build = NULL;
+ OSSL_PARAM *params = NULL;
+ EVP_PKEY_CTX *ctx = NULL;
+#endif
BIGNUM *e = NULL, *n = NULL;
+ EVP_PKEY *pkey = NULL;
UINT32 exponent = (public->parameters).rsaDetail.exponent;
if (exponent == 0) {
exponent = 0x10001;
}
- // OpenSSL expects this in network byte order
- exponent = tpm2_util_hton_32(exponent);
- ssl_rsa_key = RSA_new();
- if (!ssl_rsa_key) {
- print_ssl_error("Failed to allocate OpenSSL RSA structure");
+ n = BN_bin2bn(public->unique.rsa.buffer, public->unique.rsa.size, NULL);
+ if (!n) {
+ print_ssl_error("Failed to convert data to SSL internal format");
goto error;
}
- e = BN_bin2bn((void*) &exponent, sizeof(exponent), NULL);
- n = BN_bin2bn(public->unique.rsa.buffer, public->unique.rsa.size,
- NULL);
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ rsa_key = RSA_new();
+ if (!rsa_key) {
+ print_ssl_error("Failed to allocate OpenSSL RSA structure");
+ goto error;
+ }
- if (!n || !e) {
+ e = BN_new();
+ if (!e) {
+ print_ssl_error("Failed to convert data to SSL internal format");
+ goto error;
+ }
+ int rc = BN_set_word(e, exponent);
+ if (!rc) {
print_ssl_error("Failed to convert data to SSL internal format");
goto error;
}
- if (!RSA_set0_key(ssl_rsa_key, n, e, NULL)) {
+ rc = RSA_set0_key(rsa_key, n, e, NULL);
+ if (!rc) {
print_ssl_error("Failed to set RSA modulus and exponent components");
goto error;
}
@@ -107,162 +127,273 @@ static bool convert_pubkey_RSA(TPMT_PUBLIC *public,
/* modulus and exponent components are now owned by the RSA struct */
n = e = NULL;
- int ssl_res = 0;
+ pkey = EVP_PKEY_new();
+ if (!pkey) {
+ print_ssl_error("Failed to allocate OpenSSL EVP structure");
+ goto error;
+ }
- switch (format) {
- case pubkey_format_pem:
- ssl_res = PEM_write_bio_RSA_PUBKEY(bio, ssl_rsa_key);
- break;
- case pubkey_format_der:
- ssl_res = i2d_RSA_PUBKEY_bio(bio, ssl_rsa_key);
- break;
- default:
- LOG_ERR("Invalid OpenSSL target format %d encountered", format);
+ rc = EVP_PKEY_assign_RSA(pkey, rsa_key);
+ if (!rc) {
+ print_ssl_error("Failed to set OpenSSL EVP structure");
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ goto error;
+ }
+ /* rsa key is now owner by the EVP_PKEY struct */
+ rsa_key = NULL;
+#else
+ build = OSSL_PARAM_BLD_new();
+ if (!build) {
+ print_ssl_error("Failed to allocate OpenSSL parameters");
goto error;
}
- if (ssl_res <= 0) {
- print_ssl_error("OpenSSL public key conversion failed");
+ int rc = OSSL_PARAM_BLD_push_BN(build, OSSL_PKEY_PARAM_RSA_N, n);
+ if (!rc) {
+ print_ssl_error("Failed to set RSA modulus");
goto error;
}
- ret = true;
+ rc = OSSL_PARAM_BLD_push_uint32(build, OSSL_PKEY_PARAM_RSA_E, exponent);
+ if (!rc) {
+ print_ssl_error("Failed to set RSA exponent");
+ goto error;
+ }
- error: if (n) {
- BN_free(n);
+ params = OSSL_PARAM_BLD_to_param(build);
+ if (!params) {
+ print_ssl_error("Failed to build OpenSSL parameters");
+ goto error;
}
- if (e) {
- BN_free(e);
+
+ ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL);
+ if (!ctx) {
+ print_ssl_error("Failed to allocate RSA key context");
+ goto error;
}
- if (ssl_rsa_key) {
- RSA_free(ssl_rsa_key);
+
+ rc = EVP_PKEY_fromdata_init(ctx);
+ if (rc <= 0) {
+ print_ssl_error("Failed to initialize RSA key creation");
+ goto error;
}
- return ret;
+ rc = EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params);
+ if (rc <= 0) {
+ print_ssl_error("Failed to create a RSA public key");
+ goto error;
+ }
+#endif
+error:
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ RSA_free(rsa_key);
+#else
+ EVP_PKEY_CTX_free(ctx);
+ OSSL_PARAM_free(params);
+ OSSL_PARAM_BLD_free(build);
+#endif
+ BN_free(n);
+ BN_free(e);
+ return pkey;
}
-static bool convert_pubkey_ECC(TPMT_PUBLIC *public,
- tpm2_convert_pubkey_fmt format, BIO *bio) {
+EVP_PKEY *convert_pubkey_ECC(TPMT_PUBLIC *public) {
BIGNUM *x = NULL;
BIGNUM *y = NULL;
- EC_KEY *key = NULL;
EC_POINT *point = NULL;
- const EC_GROUP *group = NULL;
-
- bool result = false;
+ EC_GROUP *group = NULL;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ EC_KEY *ec_key = NULL;
+#else
+ OSSL_PARAM_BLD *build = NULL;
+ OSSL_PARAM *params = NULL;
+ EVP_PKEY_CTX *ctx = NULL;
+ unsigned char *puboct = NULL;
+ size_t bsize;
+#endif
+ EVP_PKEY *pkey = NULL;
TPMS_ECC_PARMS *tpm_ecc = &public->parameters.eccDetail;
TPMS_ECC_POINT *tpm_point = &public->unique.ecc;
+ /*
+ * Set the affine coordinates for the point
+ */
+ x = BN_bin2bn(tpm_point->x.buffer, tpm_point->x.size, NULL);
+ if (!x) {
+ print_ssl_error("Could not convert x coordinate to BN");
+ goto out;
+ }
+
+ y = BN_bin2bn(tpm_point->y.buffer, tpm_point->y.size, NULL);
+ if (!y) {
+ print_ssl_error("Could not convert y coordinate to BN");
+ goto out;
+ }
+
int nid = tpm2_ossl_curve_to_nid(tpm_ecc->curveID);
if (nid < 0) {
- return false;
+ goto out;
}
/*
- * Create an empty EC key by the NID
+ * Create a new point in the group, which is the public key.
*/
- key = EC_KEY_new_by_curve_name(nid);
- if (!key) {
- print_ssl_error("Failed to create EC key from nid");
- return false;
- }
-
- group = EC_KEY_get0_group(key);
+ group = EC_GROUP_new_by_curve_name(nid);
if (!group) {
print_ssl_error("EC key missing group");
goto out;
}
- /*
- * Create a new point in the group, which is the public key.
- */
point = EC_POINT_new(group);
+ int rc = EC_POINT_set_affine_coordinates_tss(group, point, x, y, NULL);
+ if (!rc) {
+ print_ssl_error("Could not set affine coordinates");
+ goto out;
+ }
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
/*
- * Set the affine coordinates for the point
+ * Create an empty EC key by the NID
*/
- x = BN_bin2bn(tpm_point->x.buffer, tpm_point->x.size, NULL);
- if (!x) {
- print_ssl_error("Could not convert x coordinate to BN");
+ ec_key = EC_KEY_new_by_curve_name(nid);
+ if (!ec_key) {
+ print_ssl_error("Failed to create EC key from nid");
+ return false;
+ }
+
+ rc = EC_KEY_set_public_key(ec_key, point);
+ if (!rc) {
+ print_ssl_error("Could not set point as public key portion");
goto out;
}
- y = BN_bin2bn(tpm_point->y.buffer, tpm_point->y.size, NULL);
- if (!y) {
- print_ssl_error("Could not convert y coordinate to BN");
+ if ((pkey = EVP_PKEY_new()) == NULL) {
+ print_ssl_error("Failed to allocate OpenSSL EVP structure");
goto out;
}
- int rc = EC_POINT_set_affine_coordinates_tss(group, point, x, y, NULL);
+ rc = EVP_PKEY_assign_EC_KEY(pkey, ec_key);
if (!rc) {
- print_ssl_error("Could not set affine coordinates");
+ print_ssl_error("Failed to set OpenSSL EVP structure");
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ goto out;
+ }
+ /* rsa key is now owner by the EVP_PKEY struct */
+ ec_key = NULL;
+#else
+ build = OSSL_PARAM_BLD_new();
+ if (!build) {
+ print_ssl_error("Failed to allocate OpenSSL parameters");
goto out;
}
- rc = EC_KEY_set_public_key(key, point);
+ rc = OSSL_PARAM_BLD_push_utf8_string(build, OSSL_PKEY_PARAM_GROUP_NAME,
+ (char *)OBJ_nid2sn(nid), 0);
if (!rc) {
- print_ssl_error("Could not set point as public key portion");
+ print_ssl_error("Failed to set the EC group name");
goto out;
}
- int ssl_res = 0;
-
- switch (format) {
- case pubkey_format_pem:
- ssl_res = PEM_write_bio_EC_PUBKEY(bio, key);
- break;
- case pubkey_format_der:
- ssl_res = i2d_EC_PUBKEY_bio(bio, key);
- break;
- default:
- LOG_ERR("Invalid OpenSSL target format %d encountered", format);
+ bsize = EC_POINT_point2buf(group, point,
+ POINT_CONVERSION_COMPRESSED,
+ &puboct, NULL);
+ if (bsize == 0) {
+ print_ssl_error("Failed compress the EC public key");
goto out;
}
- if (ssl_res <= 0) {
- print_ssl_error("OpenSSL public key conversion failed");
+ rc = OSSL_PARAM_BLD_push_octet_string(build, OSSL_PKEY_PARAM_PUB_KEY,
+ puboct, bsize);
+ if (!rc) {
+ print_ssl_error("Failed set the EC public key");
goto out;
}
- result = true;
-
-out:
- if (x) {
- BN_free(x);
+ params = OSSL_PARAM_BLD_to_param(build);
+ if (!params) {
+ print_ssl_error("Failed to build OpenSSL parameters");
+ goto out;
}
- if (y) {
- BN_free(y);
+
+ ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
+ if (!ctx) {
+ print_ssl_error("Failed to allocate EC key context");
+ goto out;
}
- if (point) {
- EC_POINT_free(point);
+
+ rc = EVP_PKEY_fromdata_init(ctx);
+ if (rc <= 0) {
+ print_ssl_error("Failed to initialize EC key creation");
+ goto out;
}
- if (key) {
- EC_KEY_free(key);
+
+ rc = EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params);
+ if (rc <= 0) {
+ print_ssl_error("Failed to create a EC public key");
+ goto out;
}
+#endif
- return result;
+out:
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ EC_KEY_free(ec_key);
+#else
+ EVP_PKEY_CTX_free(ctx);
+ OSSL_PARAM_free(params);
+ OSSL_PARAM_BLD_free(build);
+ OPENSSL_free(puboct);
+#endif
+ EC_POINT_free(point);
+ EC_GROUP_free(group);
+ BN_free(x);
+ BN_free(y);
+ return pkey;
}
static bool tpm2_convert_pubkey_bio(TPMT_PUBLIC *public,
tpm2_convert_pubkey_fmt format, BIO *bio) {
- bool result = false;
+ EVP_PKEY *pubkey = NULL;
+ int ssl_res = 0;
switch (public->type) {
case TPM2_ALG_RSA:
- result = convert_pubkey_RSA(public, format, bio);
+ pubkey = convert_pubkey_RSA(public);
break;
case TPM2_ALG_ECC:
- result = convert_pubkey_ECC(public, format, bio);
+ pubkey = convert_pubkey_ECC(public);
break;
default:
- LOG_ERR(
- "Unsupported key type for requested output format. Only RSA is supported.");
+ LOG_ERR("Unsupported key type for requested output format.");
}
- return result;
+ if (pubkey == NULL)
+ return false;
+
+ switch (format) {
+ case pubkey_format_pem:
+ ssl_res = PEM_write_bio_PUBKEY(bio, pubkey);
+ break;
+ case pubkey_format_der:
+ ssl_res = i2d_PUBKEY_bio(bio, pubkey);
+ break;
+ default:
+ LOG_ERR("Invalid OpenSSL target format %d encountered", format);
+ }
+
+ EVP_PKEY_free(pubkey);
+
+ if (ssl_res <= 0) {
+ print_ssl_error("OpenSSL public key conversion failed");
+ return false;
+ }
+
+ return true;
}
static bool tpm2_convert_pubkey_ssl(TPMT_PUBLIC *public,
diff --git a/lib/tpm2_convert.h b/lib/tpm2_convert.h
index f8c82155..bddb455d 100644
--- a/lib/tpm2_convert.h
+++ b/lib/tpm2_convert.h
@@ -45,6 +45,26 @@ tpm2_convert_pubkey_fmt tpm2_convert_pubkey_fmt_from_optarg(const char *label);
bool tpm2_convert_pubkey_save(TPM2B_PUBLIC *public,
tpm2_convert_pubkey_fmt format, const char *path);
+/**
+ * Converts the given RSA public key structure into the EVP_PKEY.
+ *
+ * @param public
+ * TPM2 public key structure structure.
+ * @return
+ * OpenSSL key structure, or NULL on error.
+ */
+EVP_PKEY *convert_pubkey_RSA(TPMT_PUBLIC *public);
+
+/**
+ * Converts the given ECC public key structure into the EVP_PKEY.
+ *
+ * @param public
+ * TPM2 public key structure structure.
+ * @return
+ * OpenSSL key structure, or NULL on error.
+ */
+EVP_PKEY *convert_pubkey_ECC(TPMT_PUBLIC *public);
+
/**
* Parses the given command line signature format option string and returns
* the corresponding signature_format enum value.
--
2.31.1