Skip to content

Commit

Permalink
FAPI: Enable importing of private OSSL keys.
Browse files Browse the repository at this point in the history
A private OSSL PEM key now can be imported under a parent key in
the FAPI keystore.
Integration tests where OSSL RSA and ECC keys are imported are
added.

Signed-off-by: Juergen Repp <[email protected]>
  • Loading branch information
JuergenReppSIT committed Dec 9, 2023
1 parent 182b027 commit 7e81217
Show file tree
Hide file tree
Showing 6 changed files with 482 additions and 11 deletions.
18 changes: 18 additions & 0 deletions Makefile-test.am
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,8 @@ FAPI_TESTS_INTEGRATION = \
test/integration/fapi-get-esys-blobs.fint \
test/integration/fapi-get-random.fint \
test/integration/fapi-platform-certificates.fint \
test/integration/fapi-import-ossl-key-ecc.fint \
test/integration/fapi-import-ossl-key-rsa.fint \
test/integration/fapi-key-create-sign.fint \
test/integration/fapi-key-create-he-sign.fint \
test/integration/fapi-key-create-primary-sign.fint \
Expand Down Expand Up @@ -2445,6 +2447,22 @@ test_integration_fapi_duplicate_fint_SOURCES = \
test/integration/fapi-duplicate.int.c \
test/integration/main-fapi.c test/integration/test-fapi.h

test_integration_fapi_import_ossl_key_ecc_fint_CFLAGS = $(TESTS_CFLAGS) \
-DFAPI_PROFILE=\"P_ECC\"
test_integration_fapi_import_ossl_key_ecc_fint_LDADD = $(TESTS_LDADD)
test_integration_fapi_import_ossl_key_ecc_fint_LDFLAGS = $(TESTS_LDFLAGS)
test_integration_fapi_import_ossl_key_ecc_fint_SOURCES = \
test/integration/fapi-import-ossl-key.int.c \
test/integration/main-fapi.c test/integration/test-fapi.h

test_integration_fapi_import_ossl_key_rsa_fint_CFLAGS = $(TESTS_CFLAGS) \
-DFAPI_PROFILE=\"P_RSA\"
test_integration_fapi_import_ossl_key_rsa_fint_LDADD = $(TESTS_LDADD)
test_integration_fapi_import_ossl_key_rsa_fint_LDFLAGS = $(TESTS_LDFLAGS)
test_integration_fapi_import_ossl_key_rsa_fint_SOURCES = \
test/integration/fapi-import-ossl-key.int.c \
test/integration/main-fapi.c test/integration/test-fapi.h

test_integration_fapi_pcr_test_fint_CFLAGS = $(TESTS_CFLAGS)
test_integration_fapi_pcr_test_fint_LDADD = $(TESTS_LDADD)
test_integration_fapi_pcr_test_fint_LDFLAGS = $(TESTS_LDFLAGS)
Expand Down
105 changes: 95 additions & 10 deletions src/tss2-fapi/api/Fapi_Import.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
Expand All @@ -169,6 +171,9 @@ Fapi_Import_Async(
command->jso_string = NULL;
strdup_check(command->out_path, path, r, cleanup_error);
memset(&command->object, 0, sizeof(IFAPI_OBJECT));
memset(&command->public_templ, 0, sizeof(IFAPI_KEY_TEMPLATE));
command->ossl_priv = NULL;
command->profile = NULL;
extPubKey->pem_ext_public = NULL;

if (strncmp(importData, IFAPI_PEM_PUBLIC_STRING,
Expand Down Expand Up @@ -202,8 +207,48 @@ 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) {

r = ifapi_non_tpm_mode_init(context);
goto_if_error(r, "Initialize Import in none TPM mode", cleanup_error);

/* If the async state automata of FAPI shall be tested, then we must not set
the timeouts of ESYS to blocking mode.
During testing, the mssim tcti will ensure multiple re-invocations.
Usually however the synchronous invocations of FAPI shall instruct ESYS
to block until a result is available. */
#ifndef TEST_FAPI_ASYNC
r = Esys_SetTimeout(context->esys, TSS2_TCTI_TIMEOUT_BLOCK);
goto_if_error_reset_state(r, "Set Timeout to blocking", cleanup_error);
#endif /* TEST_FAPI_ASYNC */

r = ifapi_session_init(context);
goto_if_error(r, "Initialize Import", cleanup_error);

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);
Expand Down Expand Up @@ -368,6 +413,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);
Expand All @@ -393,6 +440,7 @@ Fapi_Import_Finish(
char *profile_name = context->loadKey.path_list->str;
r = ifapi_profiles_get(&context->profiles, profile_name,
&context->cmd.ImportKey.profile);

goto_if_error_reset_state(r, "Retrieving profile data", error_cleanup);

if (object->misc.key.public.publicArea.type == TPM2_ALG_RSA)
Expand All @@ -413,6 +461,39 @@ 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_reset_state(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_reset_state(r, "Fapi marshalling sensitive data of OSSL key failed.",
error_cleanup);

r = Tss2_MU_UINT16_Marshal(marshalled_sensitive_size,
&keyTree->duplicate.buffer[0], sizeof(uint16_t),
&marshalled_length);
goto_if_error_reset_state(r, "Fapi marshalling size of sensitive date failed.",
error_cleanup);

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);
Expand Down Expand Up @@ -550,14 +631,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;
Expand All @@ -580,7 +663,9 @@ Fapi_Import_Finish(
newObject->misc.key.policyInstance = NULL;
newObject->misc.key.description = NULL;
newObject->misc.key.certificate = NULL;
r = ifapi_get_profile_sig_scheme(&context->profiles.default_profile,
r = ifapi_get_profile_sig_scheme(context->cmd.ImportKey.profile ?
context->cmd.ImportKey.profile :
&context->profiles.default_profile,
&keyTree->public.publicArea,
&newObject->misc.key.signing_scheme);
goto_if_error(r, "Get signing scheme.", error_cleanup);
Expand Down
188 changes: 188 additions & 0 deletions src/tss2-fapi/fapi_crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Loading

0 comments on commit 7e81217

Please sign in to comment.