tpm2-tools/0014-openssl-Implement-EVP_PKEY-based-key-import.patch
Štěpán Horáček d8b5733ac7 tpm2-tools: Fix segfault and add support for OpenSSL 3
The segfault was caused by calling tpm2 command on ppc64le without any
additional arguments.

Resolves: rhbz#1989617

Signed-off-by: Štěpán Horáček <shoracek@redhat.com>
2021-10-05 12:20:18 +02:00

556 lines
16 KiB
Diff

From 7852dfbda959aa326de7dcd341f6e4808d918e15 Mon Sep 17 00:00:00 2001
From: Petr Gotthard <petr.gotthard@centrum.cz>
Date: Sun, 15 Aug 2021 14:01:06 +0200
Subject: [PATCH 13/17] openssl: Implement EVP_PKEY based key import
The `RSA_KEY` and `EC_KEY` are not publicly available in OpenSSL 3.0 and
the generic `EVP_PKEY` must be used instead.
Since import of raw keys still requires access to the internal structures
the OpenSSL 3.0 introduced a completely new approach to access key internals.
Signed-off-by: Petr Gotthard <petr.gotthard@centrum.cz>
---
lib/tpm2_openssl.c | 297 ++++++++++++++++++++++++++++-----------------
lib/tpm2_openssl.h | 12 --
2 files changed, 184 insertions(+), 125 deletions(-)
diff --git a/lib/tpm2_openssl.c b/lib/tpm2_openssl.c
index ea1d6c3b..036127c3 100644
--- a/lib/tpm2_openssl.c
+++ b/lib/tpm2_openssl.c
@@ -6,7 +6,11 @@
#include <string.h>
#include <openssl/pem.h>
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
#include <openssl/rand.h>
+#else
+#include <openssl/core_names.h>
+#endif
#include "files.h"
#include "log.h"
@@ -516,8 +520,9 @@ static bool handle_ossl_pass(const char *passin, char **pass) {
return pfn(passin, pass);
}
-static bool load_public_RSA_from_key(RSA *k, TPM2B_PUBLIC *pub) {
+static bool load_public_RSA_from_key(EVP_PKEY *key, TPM2B_PUBLIC *pub) {
+ bool result = false;
TPMT_PUBLIC *pt = &pub->publicArea;
pt->type = TPM2_ALG_RSA;
@@ -532,11 +537,33 @@ static bool load_public_RSA_from_key(RSA *k, TPM2B_PUBLIC *pub) {
sym->keyBits.sym = 0;
sym->mode.sym = TPM2_ALG_NULL;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
const BIGNUM *n; /* modulus */
const BIGNUM *e; /* public key exponent */
+ RSA *k = EVP_PKEY_get0_RSA(key);
+ if (!k) {
+ LOG_ERR("Could not retrieve RSA key");
+ goto out;
+ }
+
RSA_get0_key(k, &n, &e, NULL);
+#else
+ BIGNUM *n = NULL; /* modulus */
+ BIGNUM *e = NULL; /* public key exponent */
+
+ int rc = EVP_PKEY_get_bn_param(key, OSSL_PKEY_PARAM_RSA_N, &n);
+ if (!rc) {
+ LOG_ERR("Could not read public modulus N");
+ goto out;
+ }
+ rc = EVP_PKEY_get_bn_param(key, OSSL_PKEY_PARAM_RSA_E, &e);
+ if (!rc) {
+ LOG_ERR("Could not read public exponent E");
+ goto out;
+ }
+#endif
/*
* The size of the modulus is the key size in RSA, store this as the
* keyBits in the RSA details.
@@ -549,7 +576,7 @@ static bool load_public_RSA_from_key(RSA *k, TPM2B_PUBLIC *pub) {
break;
default:
LOG_ERR("RSA key-size %u is not supported", rdetail->keyBits);
- return false;
+ goto out;
}
/* copy the modulus to the unique RSA field */
@@ -557,47 +584,25 @@ static bool load_public_RSA_from_key(RSA *k, TPM2B_PUBLIC *pub) {
int success = BN_bn2bin(n, pt->unique.rsa.buffer);
if (!success) {
LOG_ERR("Could not copy public modulus N");
- return false;
- }
-
- /*Make sure that we can fit the exponent into a UINT32 */
- unsigned e_size = BN_num_bytes(e);
- if (e_size > sizeof(rdetail->exponent)) {
- LOG_ERR(
- "Exponent is too big. Got %d expected less than or equal to %zu",
- e_size, sizeof(rdetail->exponent));
- return false;
- }
-
- /*
- * Copy the exponent into the field.
- * Returns 1 on success false on error.
- */
- return BN_bn2bin(e, (unsigned char *) &rdetail->exponent);
-}
-
-static RSA *tpm2_openssl_get_public_RSA_from_pem(FILE *f, const char *path) {
-
- /*
- * Public PEM files appear in two formats:
- * 1. PEM format, read with PEM_read_RSA_PUBKEY
- * 2. PKCS#1 format, read with PEM_read_RSAPublicKey
- *
- * See:
- * - https://stackoverflow.com/questions/7818117/why-i-cant-read-openssl-generated-rsa-pub-key-with-pem-read-rsapublickey
- */
- RSA *pub = PEM_read_RSA_PUBKEY(f, NULL, NULL, NULL);
- if (!pub) {
- pub = PEM_read_RSAPublicKey(f, NULL, NULL, NULL);
+ goto out;
}
- if (!pub) {
- ERR_print_errors_fp(stderr);
- LOG_ERR("Reading public PEM file \"%s\" failed", path);
- return NULL;
+ unsigned long exp = BN_get_word(e);
+ if (exp == 0xffffffffL) {
+ LOG_ERR("Could not copy public exponent E");
+ goto out;
}
+ rdetail->exponent = exp;
- return pub;
+ result = true;
+out:
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ /* k,n,e point to internal structrues and must not be freed after use */
+#else
+ BN_free(n);
+ BN_free(e);
+#endif
+ return result;
}
static bool load_public_RSA_from_pem(FILE *f, const char *path,
@@ -611,15 +616,19 @@ static bool load_public_RSA_from_pem(FILE *f, const char *path,
* See:
* - https://stackoverflow.com/questions/7818117/why-i-cant-read-openssl-generated-rsa-pub-key-with-pem-read-rsapublickey
*/
- RSA *k = tpm2_openssl_get_public_RSA_from_pem(f, path);
+ EVP_PKEY *k = PEM_read_PUBKEY(f, NULL, NULL, NULL);
if (!k) {
- /* tpm2_openssl_get_public_RSA_from_pem() should already log errors */
+ ERR_print_errors_fp(stderr);
+ LOG_ERR("Reading public PEM file \"%s\" failed", path);
return false;
}
- bool result = load_public_RSA_from_key(k, pub);
+ bool result = false;
+ if (EVP_PKEY_base_id(k) == EVP_PKEY_RSA) {
+ result = load_public_RSA_from_key(k, pub);
+ }
- RSA_free(k);
+ EVP_PKEY_free(k);
return result;
}
@@ -682,39 +691,41 @@ int tpm2_ossl_curve_to_nid(TPMI_ECC_CURVE curve) {
return -1;
}
-static bool load_public_ECC_from_key(EC_KEY *k, TPM2B_PUBLIC *pub) {
+static bool load_public_ECC_from_key(EVP_PKEY *key, TPM2B_PUBLIC *pub) {
+ BIGNUM *y = NULL;
+ BIGNUM *x = NULL;
+ int nid;
+ unsigned keysize;
bool result = false;
- BIGNUM *y = BN_new();
- BIGNUM *x = BN_new();
- if (!x || !y) {
- LOG_ERR("oom");
- goto out;
- }
-
/*
* Set the algorithm type
*/
pub->publicArea.type = TPM2_ALG_ECC;
+ TPMS_ECC_PARMS *pp = &pub->publicArea.parameters.eccDetail;
/*
- * Get the curve type
+ * Get the curve type and the public key (X and Y)
*/
- const EC_GROUP *group = EC_KEY_get0_group(k);
- int nid = EC_GROUP_get_curve_name(group);
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ EC_KEY *k = EVP_PKEY_get0_EC_KEY(key);
+ if (!k) {
+ LOG_ERR("Could not retrieve ECC key");
+ goto out;
+ }
- TPMS_ECC_PARMS *pp = &pub->publicArea.parameters.eccDetail;
- TPM2_ECC_CURVE curve_id = ossl_nid_to_curve(nid); // Not sure what lines up with NIST 256...
- if (curve_id == TPM2_ALG_ERROR) {
+ y = BN_new();
+ x = BN_new();
+ if (!x || !y) {
+ LOG_ERR("oom");
goto out;
}
- pp->curveID = curve_id;
+ const EC_GROUP *group = EC_KEY_get0_group(k);
+ nid = EC_GROUP_get_curve_name(group);
+ keysize = (EC_GROUP_get_degree(group) + 7) / 8;
- /*
- * Set the unique data to the public key.
- */
const EC_POINT *point = EC_KEY_get0_public_key(k);
int ret = EC_POINT_get_affine_coordinates_tss(group, point, x, y, NULL);
@@ -722,6 +733,39 @@ static bool load_public_ECC_from_key(EC_KEY *k, TPM2B_PUBLIC *pub) {
LOG_ERR("Could not get X and Y affine coordinates");
goto out;
}
+#else
+ char curve_name[80];
+
+ int rc = EVP_PKEY_get_utf8_string_param(key, OSSL_PKEY_PARAM_GROUP_NAME,
+ curve_name, sizeof(curve_name), NULL);
+ if (!rc) {
+ LOG_ERR("Could not read ECC curve name");
+ goto out;
+ }
+ nid = OBJ_txt2nid(curve_name);
+ keysize = (EVP_PKEY_bits(key) + 7) / 8;
+
+ rc = EVP_PKEY_get_bn_param(key, OSSL_PKEY_PARAM_EC_PUB_X, &x);
+ if (!rc) {
+ LOG_ERR("Could not read public X coordinate");
+ goto out;
+ }
+
+ rc = EVP_PKEY_get_bn_param(key, OSSL_PKEY_PARAM_EC_PUB_Y, &y);
+ if (!rc) {
+ LOG_ERR("Could not read public Y coordinate");
+ goto out;
+ }
+#endif
+
+ /*
+ * Set the curve type
+ */
+ TPM2_ECC_CURVE curve_id = ossl_nid_to_curve(nid); // Not sure what lines up with NIST 256...
+ if (curve_id == TPM2_ALG_ERROR) {
+ goto out;
+ }
+ pp->curveID = curve_id;
/*
* Copy the X and Y coordinate data into the ECC unique field,
@@ -730,28 +774,26 @@ static bool load_public_ECC_from_key(EC_KEY *k, TPM2B_PUBLIC *pub) {
TPM2B_ECC_PARAMETER *X = &pub->publicArea.unique.ecc.x;
TPM2B_ECC_PARAMETER *Y = &pub->publicArea.unique.ecc.y;
- unsigned x_size = (EC_GROUP_get_degree(group) + 7) / 8;
- if (x_size > sizeof(X->buffer)) {
+ if (keysize > sizeof(X->buffer)) {
LOG_ERR("X coordinate is too big. Got %u expected less than or equal to"
- " %zu", x_size, sizeof(X->buffer));
+ " %zu", keysize, sizeof(X->buffer));
goto out;
}
- unsigned y_size = (EC_GROUP_get_degree(group) + 7) / 8;
- if (y_size > sizeof(Y->buffer)) {
+ if (keysize > sizeof(Y->buffer)) {
LOG_ERR("X coordinate is too big. Got %u expected less than or equal to"
- " %zu", y_size, sizeof(Y->buffer));
+ " %zu", keysize, sizeof(Y->buffer));
goto out;
}
- X->size = BN_bn2binpad(x, X->buffer, x_size);
- if (X->size != x_size) {
+ X->size = BN_bn2binpad(x, X->buffer, keysize);
+ if (X->size != keysize) {
LOG_ERR("Error converting X point BN to binary");
goto out;
}
- Y->size = BN_bn2binpad(y, Y->buffer, y_size);
- if (Y->size != y_size) {
+ Y->size = BN_bn2binpad(y, Y->buffer, keysize);
+ if (Y->size != keysize) {
LOG_ERR("Error converting Y point BN to binary");
goto out;
}
@@ -771,43 +813,28 @@ static bool load_public_ECC_from_key(EC_KEY *k, TPM2B_PUBLIC *pub) {
sym->mode.sym = TPM2_ALG_NULL;
result = true;
-
out:
- if (x) {
- BN_free(x);
- }
- if (y) {
- BN_free(y);
- }
-
+ BN_free(x);
+ BN_free(y);
return result;
}
-EC_KEY *tpm2_openssl_get_public_ECC_from_pem(FILE *f, const char *path) {
-
- EC_KEY *pub = PEM_read_EC_PUBKEY(f, NULL, NULL, NULL);
- if (!pub) {
- ERR_print_errors_fp(stderr);
- LOG_ERR("Reading public PEM file \"%s\" failed", path);
- return NULL;
- }
-
- return pub;
-}
-
static bool load_public_ECC_from_pem(FILE *f, const char *path,
TPM2B_PUBLIC *pub) {
- EC_KEY *k = tpm2_openssl_get_public_ECC_from_pem(f, path);
+ EVP_PKEY *k = PEM_read_PUBKEY(f, NULL, NULL, NULL);
if (!k) {
ERR_print_errors_fp(stderr);
LOG_ERR("Reading PEM file \"%s\" failed", path);
return false;
}
- bool result = load_public_ECC_from_key(k, pub);
+ bool result = false;
+ if (EVP_PKEY_base_id(k) == EVP_PKEY_EC) {
+ result = load_public_ECC_from_key(k, pub);
+ }
- EC_KEY_free(k);
+ EVP_PKEY_free(k);
return result;
}
@@ -852,11 +879,27 @@ static bool load_public_AES_from_file(FILE *f, const char *path,
return tpm2_util_calc_unique(name_alg, key, seed, unique);
}
-static bool load_private_RSA_from_key(RSA *k, TPM2B_SENSITIVE *priv) {
+static bool load_private_RSA_from_key(EVP_PKEY *key, TPM2B_SENSITIVE *priv) {
- const BIGNUM *p; /* the private key exponent */
+ bool result = false;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ const BIGNUM *p = NULL; /* the private key exponent */
+ RSA *k = EVP_PKEY_get0_RSA(key);
+ if (!k) {
+ LOG_ERR("Could not retrieve RSA key");
+ goto out;
+ }
RSA_get0_factors(k, &p, NULL);
+#else
+ BIGNUM *p = NULL; /* the private key exponent */
+
+ int rc = EVP_PKEY_get_bn_param(key, OSSL_PKEY_PARAM_RSA_FACTOR1, &p);
+ if (!rc) {
+ LOG_ERR("Could not read private key");
+ goto out;
+ }
+#endif
TPMT_SENSITIVE *sa = &priv->sensitiveArea;
@@ -868,7 +911,7 @@ static bool load_private_RSA_from_key(RSA *k, TPM2B_SENSITIVE *priv) {
if (priv_bytes > sizeof(pkr->buffer)) {
LOG_ERR("Expected prime \"d\" to be less than or equal to %zu,"
" got: %u", sizeof(pkr->buffer), priv_bytes);
- return false;
+ goto out;
}
pkr->size = priv_bytes;
@@ -877,10 +920,16 @@ static bool load_private_RSA_from_key(RSA *k, TPM2B_SENSITIVE *priv) {
if (!success) {
ERR_print_errors_fp(stderr);
LOG_ERR("Could not copy private exponent \"d\"");
- return false;
+ goto out;
}
-
- return true;
+ result = true;
+out:
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ /* k,p point to internal structrues and must not be freed after use */
+#else
+ BN_free(p);
+#endif
+ return result;
}
bool tpm2_openssl_load_public(const char *path, TPMI_ALG_PUBLIC alg,
@@ -912,8 +961,9 @@ bool tpm2_openssl_load_public(const char *path, TPMI_ALG_PUBLIC alg,
return result;
}
-static bool load_private_ECC_from_key(EC_KEY *k, TPM2B_SENSITIVE *priv) {
+static bool load_private_ECC_from_key(EVP_PKEY *key, TPM2B_SENSITIVE *priv) {
+ bool result = false;
/*
* private data
*/
@@ -921,22 +971,45 @@ static bool load_private_ECC_from_key(EC_KEY *k, TPM2B_SENSITIVE *priv) {
TPM2B_ECC_PARAMETER *p = &priv->sensitiveArea.sensitive.ecc;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ EC_KEY *k = EVP_PKEY_get0_EC_KEY(key);
+ if (!k) {
+ LOG_ERR("Could not retrieve ECC key");
+ goto out;
+ }
+
const EC_GROUP *group = EC_KEY_get0_group(k);
const BIGNUM *b = EC_KEY_get0_private_key(k);
-
unsigned priv_bytes = (EC_GROUP_get_degree(group) + 7) / 8;
+#else
+ BIGNUM *b = NULL; /* the private key exponent */
+
+ int rc = EVP_PKEY_get_bn_param(key, OSSL_PKEY_PARAM_PRIV_KEY, &b);
+ if (!rc) {
+ LOG_ERR("Could not read ECC private key");
+ goto out;
+ }
+ unsigned priv_bytes = (EVP_PKEY_bits(key) + 7) / 8;
+#endif
+
if (priv_bytes > sizeof(p->buffer)) {
LOG_ERR("Expected ECC private portion to be less than or equal to %zu,"
" got: %u", sizeof(p->buffer), priv_bytes);
- return false;
+ goto out;
}
p->size = BN_bn2binpad(b, p->buffer, priv_bytes);
if (p->size != priv_bytes) {
- return false;
+ goto out;
}
-
- return true;
+ result = true;
+out:
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ /* k,b point to internal structrues and must not be freed after use */
+#else
+ BN_free(b);
+#endif
+ return result;
}
static tpm2_openssl_load_rc load_private_ECC_from_pem(FILE *f, const char *path,
@@ -950,8 +1023,7 @@ static tpm2_openssl_load_rc load_private_ECC_from_pem(FILE *f, const char *path,
return lprc_error;
}
- EC_KEY *k = PEM_read_ECPrivateKey(f, NULL,
- NULL, (void *) pass);
+ EVP_PKEY *k = PEM_read_PrivateKey(f, NULL, NULL, (void *) pass);
free(pass);
if (!k) {
ERR_print_errors_fp(stderr);
@@ -976,14 +1048,14 @@ static tpm2_openssl_load_rc load_private_ECC_from_pem(FILE *f, const char *path,
rc |= lprc_public;
out:
- EC_KEY_free(k);
+ EVP_PKEY_free(k);
return rc;
}
static tpm2_openssl_load_rc load_private_RSA_from_pem(FILE *f, const char *path,
const char *passin, TPM2B_PUBLIC *pub, TPM2B_SENSITIVE *priv) {
- RSA *k = NULL;
+ EVP_PKEY *k = NULL;
tpm2_openssl_load_rc rc = lprc_error;
@@ -993,8 +1065,7 @@ static tpm2_openssl_load_rc load_private_RSA_from_pem(FILE *f, const char *path,
return lprc_error;
}
- k = PEM_read_RSAPrivateKey(f, NULL,
- NULL, (void *) pass);
+ k = PEM_read_PrivateKey(f, NULL, NULL, (void *) pass);
free(pass);
if (!k) {
ERR_print_errors_fp(stderr);
@@ -1016,7 +1087,7 @@ static tpm2_openssl_load_rc load_private_RSA_from_pem(FILE *f, const char *path,
rc |= lprc_public;
}
out:
- RSA_free(k);
+ EVP_PKEY_free(k);
return rc;
}
diff --git a/lib/tpm2_openssl.h b/lib/tpm2_openssl.h
index b757baa5..5579ae45 100644
--- a/lib/tpm2_openssl.h
+++ b/lib/tpm2_openssl.h
@@ -196,18 +196,6 @@ tpm2_openssl_load_rc tpm2_openssl_load_private(const char *path,
bool tpm2_openssl_load_public(const char *path, TPMI_ALG_PUBLIC alg,
TPM2B_PUBLIC *pub);
-/**
- * Retrieves a public portion of an ECC key from a PEM file.
- *
- * @param f
- * The FILE object that is open for reading the path.
- * @param path
- * The path to load from.
- * @return
- * The public structure.
- */
-EC_KEY* tpm2_openssl_get_public_ECC_from_pem(FILE *f, const char *path);
-
/**
* Maps an ECC curve to an openssl nid value.
* @param curve
--
2.31.1