From e34d393d7c73a58b3bf6fccbe88ea1eedfe1d7b7 Mon Sep 17 00:00:00 2001 From: Petr Gotthard 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 --- 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 #include #include +#if OPENSSL_VERSION_NUMBER < 0x30000000L +#include +#else +#include +#include +#include +#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