Blob Blame History Raw
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