Blame SOURCES/0015-openssl-Implement-EVP_PKEY-based-key-export.patch

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