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