From 2e33d70c69f490b069603196caafb4cada3015a1 Mon Sep 17 00:00:00 2001 From: Juergen Repp Date: Tue, 5 Dec 2023 21:04:14 +0100 Subject: [PATCH] FAPI: Enable importing of private OSSL keys. A private OSSL PEM key now can be imported under a parent key in the FAPI keystore. Signed-off-by: Juergen Repp --- src/tss2-fapi/api/Fapi_Import.c | 79 ++++++++++++-- src/tss2-fapi/fapi_crypto.c | 188 ++++++++++++++++++++++++++++++++ src/tss2-fapi/fapi_crypto.h | 9 ++ src/tss2-fapi/fapi_int.h | 7 +- 4 files changed, 273 insertions(+), 10 deletions(-) diff --git a/src/tss2-fapi/api/Fapi_Import.c b/src/tss2-fapi/api/Fapi_Import.c index 06ef64f0c..f867a7ce7 100644 --- a/src/tss2-fapi/api/Fapi_Import.c +++ b/src/tss2-fapi/api/Fapi_Import.c @@ -19,6 +19,7 @@ #include "fapi_int.h" #include "fapi_util.h" #include "tss2_esys.h" +#include "tss2_mu.h" #include "ifapi_json_deserialize.h" #include "ifapi_policy_json_deserialize.h" #include "tpm_json_deserialize.h" @@ -148,6 +149,7 @@ Fapi_Import_Async( json_object *jso2; size_t pos = 0; TPMS_POLICY policy = { 0 }; + TPMA_OBJECT *attributes; /* Check for NULL parameters */ check_not_null(context); @@ -202,8 +204,31 @@ Fapi_Import_Async( context->state = IMPORT_KEY_WRITE_OBJECT_PREPARE; - } else if (strcmp(importData, IFAPI_PEM_PRIVATE_KEY) == 0) { - goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "Invalid importData.", cleanup_error); + } else if (strncmp(importData, IFAPI_PEM_PRIVATE_KEY, + sizeof(IFAPI_PEM_PRIVATE_KEY) - 1) == 0 || + strncmp(importData, IFAPI_PEM_ECC_PRIVATE_KEY, + sizeof(IFAPI_PEM_ECC_PRIVATE_KEY) - 1) == 0 || + strncmp(importData, IFAPI_PEM_RSA_PRIVATE_KEY, + sizeof(IFAPI_PEM_RSA_PRIVATE_KEY) - 1) == 0) { + attributes = &context->cmd.ImportKey.public_templ.public.publicArea.objectAttributes; + r = ifapi_profiles_get(&context->profiles, path, &context->cmd.ImportKey.profile); + goto_if_error2(r, "Get profile for path: %s", cleanup_error, path); + + r = ifapi_merge_profile_into_template(context->cmd.ImportKey.profile, + &context->cmd.ImportKey.public_templ); + goto_if_error(r, "Merge profile", cleanup_error); + + *attributes = TPMA_OBJECT_SIGN_ENCRYPT | TPMA_OBJECT_DECRYPT | TPMA_OBJECT_USERWITHAUTH; + context->cmd.ImportKey.ossl_priv = importData; + + /* Create session for key loading. */ + r = ifapi_get_sessions_async(context, + IFAPI_SESSION_GEN_SRK | IFAPI_SESSION1, + TPMA_SESSION_DECRYPT, 0); + goto_if_error_reset_state(r, "Create sessions", cleanup_error); + + context->state = IMPORT_WAIT_FOR_SESSION; + return TSS2_RC_SUCCESS; } else { r = ifapi_non_tpm_mode_init(context); @@ -368,6 +393,8 @@ Fapi_Import_Finish( TSS2_RC r; ESYS_TR session; + size_t marshalled_sensitive_size = 0; + size_t marshalled_length = 0; /* Check for NULL parameters */ check_not_null(context); @@ -413,6 +440,38 @@ Fapi_Import_Finish( goto_if_error(r, "LoadKey finish", error_cleanup); context->loadKey.auth_object = *auth_object; + + /* Copy private OSSL PEM key to key tree. */ + if (command->ossl_priv) { + command->parent_object = &context->loadKey.auth_object; + command->public_templ.public.publicArea.nameAlg = + command->parent_object->misc.key.public.publicArea.nameAlg; + r = ifapi_openssl_load_private(command->ossl_priv, + NULL, + NULL, + &context->cmd.ImportKey.public_templ.public, + &keyTree->public, + &command->sensitive); + + goto_if_error(r, "Fapi load OSSL Key.", error_cleanup); + + + r = Tss2_MU_TPMT_SENSITIVE_Marshal(&command->sensitive.sensitiveArea, + &keyTree->duplicate.buffer[sizeof(uint16_t)], + TPM2_MAX_DIGEST_BUFFER, + &marshalled_sensitive_size); + goto_if_error(r, "Fapi marshalling sensitive data of OSSL key failed."); + + r = Tss2_MU_UINT16_Marshal(marshalled_sensitive_size, + &keyTree->duplicate.buffer[0], sizeof(uint16_t), + &marshalled_length); + goto_if_error(r, "Fapi marshalling size of sensitive date failed."); + + keyTree->duplicate.size = marshalled_sensitive_size + sizeof(uint16_t); + context->state = IMPORT_KEY_AUTHORIZE_PARENT; + return TSS2_FAPI_RC_TRY_AGAIN; + } + fallthrough; statecase(context->state, IMPORT_WAIT_FOR_AUTHORIZATION); @@ -550,14 +609,16 @@ Fapi_Import_Finish( r = Esys_Import_Finish(context->esys, &command->private); try_again_or_error_goto(r, "Import", error_cleanup); - /* Concatenate keyname and parent path */ - char* ipath = NULL; - r = ifapi_asprintf(&ipath, "%s%s%s", command->parent_path, - IFAPI_FILE_DELIM, command->out_path); - goto_if_error(r, "Out of memory.", error_cleanup); + if (!command->ossl_priv) { + /* Concatenate keyname and parent path */ + char* ipath = NULL; + r = ifapi_asprintf(&ipath, "%s%s%s", command->parent_path, + IFAPI_FILE_DELIM, command->out_path); + goto_if_error(r, "Out of memory.", error_cleanup); - SAFE_FREE(command->out_path); - command->out_path = ipath; + SAFE_FREE(command->out_path); + command->out_path = ipath; + } context->state = IMPORT_KEY_WAIT_FOR_FLUSH; fallthrough; diff --git a/src/tss2-fapi/fapi_crypto.c b/src/tss2-fapi/fapi_crypto.c index f603d8629..e5c13ffc3 100644 --- a/src/tss2-fapi/fapi_crypto.c +++ b/src/tss2-fapi/fapi_crypto.c @@ -2201,3 +2201,191 @@ ifapi_rsa_encrypt(const char *pem_key, OSSL_FREE(ctx, EVP_PKEY_CTX); return r; } + +static bool +load_private_RSA_from_key(EVP_PKEY *key, + TPM2B_SENSITIVE *priv) { + + 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_ERROR("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_ERROR("Could not read private key"); + goto out; + } +#endif + + TPMT_SENSITIVE *sa = &priv->sensitiveArea; + + sa->sensitiveType = TPM2_ALG_RSA; + + TPM2B_PRIVATE_KEY_RSA *pkr = &sa->sensitive.rsa; + + unsigned priv_bytes = BN_num_bytes(p); + if (priv_bytes > sizeof(pkr->buffer)) { + LOG_ERROR("Expected prime \"d\" to be less than or equal to %zu," + " got: %u", sizeof(pkr->buffer), priv_bytes); + goto out; + } + + pkr->size = priv_bytes; + + int success = BN_bn2bin(p, pkr->buffer); + if (!success) { + ERR_print_errors_fp(stderr); + LOG_ERROR("Could not copy private exponent \"d\""); + goto out; + } + 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; +} + +static TSS2_RC +load_RSA_key(EVP_PKEY *key, + TPM2B_PUBLIC *pub, + TPM2B_SENSITIVE *priv) { + TSS2_RC rc = TSS2_RC_SUCCESS; + + bool loaded_priv = load_private_RSA_from_key(key, priv); + if (!loaded_priv) { + return TSS2_FAPI_RC_GENERAL_FAILURE; + } + + rc = get_rsa_tpm2b_public_from_evp(key, pub); + if (rc) { + goto out; + } +out: + EVP_PKEY_free(key); + return rc; +} + +static bool +load_private_ECC_from_key(EVP_PKEY *key, + TPM2B_SENSITIVE *priv) { + bool result = false; + /* + * private data + */ + priv->sensitiveArea.sensitiveType = TPM2_ALG_ECC; + + 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_ERROR("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_ERROR("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_ERROR("Expected ECC private portion to be less than or equal to %zu," + " got: %u", sizeof(p->buffer), priv_bytes); + goto out; + } + + p->size = BN_bn2binpad(b, p->buffer, priv_bytes); + if (p->size != priv_bytes) { + goto out; + } + 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 TSS2_RC +load_ECC_key(EVP_PKEY *key, + TPM2B_PUBLIC *pub, + TPM2B_SENSITIVE *priv) { + TSS2_RC rc = TSS2_RC_SUCCESS; + + if (!load_private_ECC_from_key(key, priv)) { + rc = TSS2_FAPI_RC_GENERAL_FAILURE; + goto out; + } + rc = get_ecc_tpm2b_public_from_evp(key, pub); + if (rc) { + goto out; + } +out: + EVP_PKEY_free(key); + return rc; +} + +TSS2_RC +ifapi_openssl_load_private(const char *pem_key, + const char *passin, + const char *object_auth, + TPM2B_PUBLIC *template, + TPM2B_PUBLIC *pub, + TPM2B_SENSITIVE *priv) { + *pub = *template; + BIO *bio = NULL; + EVP_PKEY *key = NULL; + TSS2_RC rc = TSS2_RC_SUCCESS; + (void)object_auth; + + /* Create a key from PEM string. */ + bio = BIO_new_mem_buf(pem_key, -1); + + if (!bio) { + LOG_ERROR("Error creating BIO."); + return TSS2_FAPI_RC_GENERAL_FAILURE; + } + + key = PEM_read_bio_PrivateKey(bio,NULL,NULL, (void *) passin); + + if (!key) { + LOG_ERROR("Creation of key from PEM string failed."); + return TSS2_FAPI_RC_GENERAL_FAILURE; + } + + switch (template->publicArea.type) { + case TPM2_ALG_RSA: + rc = load_RSA_key(key, pub, priv); + break; + case TPM2_ALG_ECC: + rc = load_ECC_key(key, pub, priv); + break; + default: + LOG_ERROR("Cannot handle algorithm, got: 0x%x", template->publicArea.type); + rc = TSS2_FAPI_RC_GENERAL_FAILURE; + } + return rc; +} diff --git a/src/tss2-fapi/fapi_crypto.h b/src/tss2-fapi/fapi_crypto.h index 33b61e128..4360b9cdb 100644 --- a/src/tss2-fapi/fapi_crypto.h +++ b/src/tss2-fapi/fapi_crypto.h @@ -132,4 +132,13 @@ ifapi_rsa_encrypt( uint8_t **cipherText, size_t *cipherTextSize); +TSS2_RC +ifapi_openssl_load_private( + const char *pem_key, + const char *passin, + const char *object_auth, + TPM2B_PUBLIC *template, + TPM2B_PUBLIC *pub, + TPM2B_SENSITIVE *priv); + #endif /* FAPI_CRYPTO_H */ diff --git a/src/tss2-fapi/fapi_int.h b/src/tss2-fapi/fapi_int.h index a9a542420..19133856b 100644 --- a/src/tss2-fapi/fapi_int.h +++ b/src/tss2-fapi/fapi_int.h @@ -69,7 +69,9 @@ typedef UINT8 IFAPI_SESSION_TYPE; #define IFAPI_PUB_KEY_DIR "ext" #define IFAPI_POLICY_DIR "policy" #define IFAPI_PEM_PUBLIC_STRING "-----BEGIN PUBLIC KEY-----" -#define IFAPI_PEM_PRIVATE_KEY "-----PRIVATE KEY-----" +#define IFAPI_PEM_PRIVATE_KEY "----BEGIN PRIVATE KEY-----" +#define IFAPI_PEM_RSA_PRIVATE_KEY "-----BEGIN RSA PRIVATE KEY-----" +#define IFAPI_PEM_ECC_PRIVATE_KEY "-----BEGIN EC PRIVATE KEY-----" #define IFAPI_JSON_TAG_POLICY "policy" #define IFAPI_JSON_TAG_OBJECT_TYPE "objectType" #define IFAPI_JSON_TAG_DUPLICATE "public_parent" @@ -716,6 +718,9 @@ typedef struct { TPM2B_PRIVATE *private; char *jso_string; const IFAPI_PROFILE *profile; + IFAPI_KEY_TEMPLATE public_templ; /**< The template for the keys public data */ + const char *ossl_priv; /**< Private OSSL PEM key to be import. */ + TPM2B_SENSITIVE sensitive; /**< The sensitive part of an OSSL key. */ } IFAPI_ImportKey;