blob: 74ab9bcd7cbefe0c85b7fac018b98aaa2a3090f4 [file] [log] [blame]
/* SPDX-License-Identifier: BSD-2-Clause */
/*******************************************************************************
* Copyright 2018-2019, Fraunhofer SIT sponsored by Infineon Technologies AG
* All rights reserved.
*******************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <ctype.h>
#include <dirent.h>
#include "tss2_mu.h"
#include "fapi_util.h"
#include "fapi_crypto.h"
#include "ifapi_helpers.h"
#include "ifapi_json_serialize.h"
#include "ifapi_json_deserialize.h"
#include "tpm_json_deserialize.h"
#include "fapi_policy.h"
#include "ifapi_policyutil_execute.h"
#define LOGMODULE fapi
#include "util/log.h"
#include "util/aux_util.h"
/** State machine for flushing objects.
*
* @param[in] context The FAPI_CONTEXT.
* @param[in] handle of the object to be flushed.
*
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
* @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
* this function needs to be called again.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
* operation already pending.
*/
TSS2_RC
ifapi_flush_object(FAPI_CONTEXT *context, ESYS_TR handle)
{
TSS2_RC r = TSS2_RC_SUCCESS;
if (handle == ESYS_TR_NONE)
return r;
switch (context->flush_object_state) {
statecase(context->flush_object_state, FLUSH_INIT);
r = Esys_FlushContext_Async(context->esys, handle);
return_if_error(r, "Flush Object");
fallthrough;
statecase(context->flush_object_state, WAIT_FOR_FLUSH);
r = Esys_FlushContext_Finish(context->esys);
if (base_rc(r) == TSS2_BASE_RC_TRY_AGAIN)
return TSS2_FAPI_RC_TRY_AGAIN;
return_if_error(r, "FlushContext");
context->flush_object_state = FLUSH_INIT;
return TSS2_RC_SUCCESS;
statecasedefault(context->flush_object_state);
}
}
/** Preparation for getting a session handle.
*
* The corresponding async call be executed and a session secret for encryption
* TPM2B parameters will be created.
*
* @param[in] esys The ESYS_CONTEXT.
* @param[in] saltkey The key which will be used for the encryption of the session
* secret.
* @param[in] profile The FAPI profile will be used to adjust the sessions symmetric
* parameters.
* @param[in] hashAlg The hash algorithm used for the session.
*
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
*/
TSS2_RC
ifapi_get_session_async(ESYS_CONTEXT *esys, ESYS_TR saltkey, const IFAPI_PROFILE *profile,
TPMI_ALG_HASH hashAlg)
{
TSS2_RC r;
r = Esys_StartAuthSession_Async(esys, saltkey,
ESYS_TR_NONE,
ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
NULL,
TPM2_SE_HMAC, &profile->session_symmetric,
hashAlg);
return_if_error(r, "Creating session.");
return TSS2_RC_SUCCESS;
}
/** Call for getting a session handle and adjust session parameters.
*
* @param[in] esys The ESYS_CONTEXT.
* @param[out] session The session handle.
* @param[in] flags The flags to adjust the session attributes.
*
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
*/
TSS2_RC
ifapi_get_session_finish(ESYS_CONTEXT *esys, ESYS_TR *session,
TPMA_SESSION flags)
{
TSS2_RC r;
TPMA_SESSION sessionAttributes = 0;
/* Check whether authorization callback is defined */
r = Esys_StartAuthSession_Finish(esys, session);
if (r != TSS2_RC_SUCCESS)
return r;
sessionAttributes |= flags;
sessionAttributes |= TPMA_SESSION_CONTINUESESSION;
r = Esys_TRSess_SetAttributes(esys, *session, sessionAttributes,
0xff);
return_if_error(r, "Set session attributes.");
return TSS2_RC_SUCCESS;
}
/** Get the digest size of the policy of a FAPI object.
*
* @param[in] object The object with the correspodning policy.
*
* @retval The size of policy digest.
* @retval 0 if The object does not have a policy.
*/
static size_t
policy_digest_size(IFAPI_OBJECT *object)
{
switch (object->objectType) {
case IFAPI_KEY_OBJ:
return object->misc.key.public.publicArea.authPolicy.size;
case IFAPI_NV_OBJ:
return object->misc.nv.public.nvPublic.authPolicy.size;
case IFAPI_HIERARCHY_OBJ:
return object->misc.hierarchy.authPolicy.size;
default:
return 0;
}
}
/** Add a object together with size as first element to a linked list.
*
* This function can e.g. used to add byte arrays together with their size
* to a linked list.
*
* @param[in] object The object to be added.
* @param[in] size The size of the object to be added.
* @param[in,out] object_list The linked list to be extended.
*
* @retval TSS2_RC_SUCCESS if the object was added.
* @retval TSS2_FAPI_RC_MEMORY If memory for the list extension cannot
* be allocated.
*/
static TSS2_RC
push_object_with_size_to_list(void *object, size_t size, NODE_OBJECT_T **object_list)
{
TSS2_RC r;
r = push_object_to_list(object, object_list);
return_if_error(r, "Push object with size.");
(*object_list)->size = size;
return TSS2_RC_SUCCESS;
}
/** Initialize and expand the linked list representing a FAPI key path.
*
* From a passed key path the explicit key path will be determined. The
* profile and the hierarchy will be added if necessary and the extension
* is possible.
*
* @param[in] context_profile The profile used for extension of no profile is
* part of the path.
* @param[in] ipath The implicit pathname which has to be extended.
* @param[out] list_node1 The linked list for the passed key path without
* extensions.
* @param[out] current_list_node The current node in the list list_node1,
* which represent the tail not processed.
* @param[out] result The part of the new list which had been extended
* without the tail not processed.
*
* @retval TSS2_RC_SUCCESS: If the initialization was successful.
* @retval TSS2_FAPI_RC_BAD_VALUE If an invalid path was passed.
* @retval TSS2_FAPI_RC_MEMORY: if not enough memory can be allocated.
* @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context
* or contains illegal characters.
*/
static TSS2_RC
init_explicit_key_path(
const char *context_profile,
const char *ipath,
NODE_STR_T **list_node1,
NODE_STR_T **current_list_node,
NODE_STR_T **result)
{
*list_node1 = split_string(ipath, IFAPI_FILE_DELIM);
NODE_STR_T *list_node = *list_node1;
char const *profile;
char *hierarchy;
TSS2_RC r = TSS2_RC_SUCCESS;
*result = NULL;
if (list_node == NULL) {
LOG_ERROR("Invalid path");
free_string_list(*list_node1);
return TSS2_FAPI_RC_BAD_VALUE;
}
/* Processing of the profile. */
if (strncmp("P_", list_node->str, 2) == 0) {
profile = list_node->str;
list_node = list_node->next;
} else {
profile = context_profile;
}
/* Create the initial node of the linked list. */
*result = init_string_list(profile);
if (*result == NULL) {
free_string_list(*list_node1);
LOG_ERROR("Out of memory");
return TSS2_FAPI_RC_MEMORY;
}
if (list_node == NULL) {
/* Storage hierarchy will be used as default. */
hierarchy = "HS";
} else {
if (strcmp(list_node->str, "HN") == 0) {
hierarchy = list_node->str;
list_node = list_node->next;
} else if (strcmp(list_node->str, "HS") == 0 ||
strcmp(list_node->str, "HE") == 0 ||
strcmp(list_node->str, "HN") == 0) {
hierarchy = list_node->str;
list_node = list_node->next;
} else if (strcmp(list_node->str, "EK") == 0) {
/* The hierarchy for an endorsement key will be added. */
hierarchy = "HE";
} else if (list_node->str != NULL &&
strcmp(list_node->str, "SRK") == 0) {
/* The storage hierachy will be added. */
hierarchy = "HS";
} else {
LOG_ERROR("Hierarchy cannot be determined.");
r = TSS2_FAPI_RC_BAD_PATH;
goto error;
}
}
/* Add the used hierarchy to the linked list. */
if (!add_string_to_list(*result, hierarchy)) {
LOG_ERROR("Out of memory");
r = TSS2_FAPI_RC_MEMORY;
goto error;
}
if (list_node == NULL) {
goto_error(r, TSS2_FAPI_RC_BAD_PATH, "Explicit path can't be determined.",
error);
}
/* Add the primary directory to the linked list. */
if (!add_string_to_list(*result, list_node->str)) {
LOG_ERROR("Out of memory");
r = TSS2_FAPI_RC_MEMORY;
goto error;
}
if (strcmp(hierarchy, "HS") == 0 && strcmp(list_node->str, "EK") == 0) {
LOG_ERROR("Key EK cannot be create in the storage hierarchy.");
r = TSS2_FAPI_RC_BAD_PATH;
goto error;
}
if (strcmp(hierarchy, "HE") == 0 && strcmp(list_node->str, "SRK") == 0) {
LOG_ERROR("Key EK cannot be create in the endorsement hierarchy.");
r = TSS2_FAPI_RC_BAD_PATH;
goto error;
}
if (strcmp(hierarchy, "HN") == 0 &&
(strcmp(list_node->str, "SRK") == 0 || strcmp(list_node->str, "EK") == 0)) {
LOG_ERROR("Key EK and SRK cannot be created in NULL hierarchy.");
r = TSS2_FAPI_RC_BAD_PATH;
goto error;
}
/* Return the rest of the path. */
*current_list_node = list_node->next;
return TSS2_RC_SUCCESS;
error:
free_string_list(*result);
*result = NULL;
free_string_list(*list_node1);
*list_node1 = NULL;
return r;
}
/** Free first object of a linked list.
*
* Note: Referenced objects of the list have to be freed before.
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
*/
static TSS2_RC
pop_object_from_list(FAPI_CONTEXT *context, NODE_OBJECT_T **object_list)
{
return_if_null(*object_list, "Pop from list.", TSS2_FAPI_RC_BAD_REFERENCE);
NODE_OBJECT_T *head = *object_list;
NODE_OBJECT_T *next = head->next;
*object_list = next;
ifapi_free_object(context, (void *)&head->object);
free(head);
return TSS2_RC_SUCCESS;
}
/** Get relative path of a FAPI object.
*
* @param[in] object The internal FAPI object.
*
* @retval The relative path of the object.
* @retval NULL if no path is available.
*/
const char *
ifapi_get_object_path(IFAPI_OBJECT *object)
{
if (object->rel_path)
return object->rel_path;
/* For hierarchies the path might not be set during reading
from keystore. */
if (object->objectType == IFAPI_HIERARCHY_OBJ) {
switch (object->handle) {
case ESYS_TR_RH_NULL:
return "/HN";
case ESYS_TR_RH_OWNER:
return "/HS";
case ESYS_TR_RH_ENDORSEMENT:
return "/HE";
case ESYS_TR_RH_LOCKOUT:
return "/LOCKOUT";
}
}
return NULL;
}
/** Set authorization value for a primary key to be created.
*
* The callback which provides the auth value must be defined.
*
* @param[in,out] context The FAPI_CONTEXT.
* @param[in] object The auth value will be assigned to this object.
* @param[in,out] inSensitive The sensitive data to store the auth value.
*
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN If the callback for getting
* the auth value is not defined.
*/
TSS2_RC
ifapi_set_auth_primary(
FAPI_CONTEXT *context,
IFAPI_OBJECT *object,
TPMS_SENSITIVE_CREATE *inSensitive)
{
TSS2_RC r;
const char *auth = NULL;
const char *obj_path;
memset(inSensitive, 0, sizeof(TPMS_SENSITIVE_CREATE));
if (!object->misc.key.with_auth) {
return TSS2_RC_SUCCESS;
}
obj_path = ifapi_get_object_path(object);
/* Check whether callback is defined. */
if (context->callbacks.auth) {
r = context->callbacks.auth(obj_path, object->misc.key.description,
&auth, context->callbacks.authData);
return_if_error(r, "AuthCallback");
if (auth != NULL) {
inSensitive->userAuth.size = strlen(auth);
memcpy(&inSensitive->userAuth.buffer[0], auth,
inSensitive->userAuth.size);
}
return TSS2_RC_SUCCESS;
}
SAFE_FREE(auth);
return_error( TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN, "Authorization callback not defined.");
}
/** Set authorization value for a FAPI object.
*
* The callback which provides the auth value must be defined.
*
* @param[in,out] context The FAPI_CONTEXT.
* @param[in] auth_object The auth value will be assigned to this object.
* @param[in] description The description will be passed to the callback
* which delivers the auth value.
*
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN If the callback for getting
* the auth value is not defined.
*/
TSS2_RC
ifapi_set_auth(
FAPI_CONTEXT *context,
IFAPI_OBJECT *auth_object,
const char *description)
{
TSS2_RC r;
const char *auth = NULL;
TPM2B_AUTH authValue = {.size = 0,.buffer = {0} };
const char *obj_path;
obj_path = ifapi_get_object_path(auth_object);
/* Check whether callback is defined. */
if (context->callbacks.auth) {
r = context->callbacks.auth(obj_path, description, &auth,
context->callbacks.authData);
return_if_error(r, "policyAuthCallback");
if (auth != NULL) {
authValue.size = strlen(auth);
memcpy(&authValue.buffer[0], auth, authValue.size);
}
/* Store auth value in the ESYS object. */
r = Esys_TR_SetAuth(context->esys, auth_object->handle, &authValue);
return_if_error(r, "Set auth value.");
if (auth_object->objectType == IFAPI_HIERARCHY_OBJ)
auth_object->misc.hierarchy.authorized = true;
return TSS2_RC_SUCCESS;
}
SAFE_FREE(auth);
return_error( TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN, "Authorization callback not defined.");
}
/** Preparation for getting a free handle after a start handle number.
*
* @param[in] fctx The FAPI_CONTEXT.
* @param[in] handle The start value for handle search.
*
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
*/
TSS2_RC
ifapi_get_free_handle_async(FAPI_CONTEXT *fctx, TPM2_HANDLE *handle)
{
TSS2_RC r = Esys_GetCapability_Async(fctx->esys,
ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
TPM2_CAP_HANDLES, *handle, 1);
return_if_error(r, "GetCapability");
return r;
}
/** Execution of get capability until a free handle is found.
*
* The get capability method is called until a free handle is found
* or the max number of trials passe to the function is exeeded.
*
* @param[in] fctx The FAPI_CONTEXT.
* @param[out] handle The free handle.
* @param[in] max The maximal number of trials.
*
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_FAPI_RC_NV_TOO_SMALL if too many NV handles are defined.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
* @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
* this function needs to be called again.
*/
TSS2_RC
ifapi_get_free_handle_finish(FAPI_CONTEXT *fctx, TPM2_HANDLE *handle,
TPM2_HANDLE max)
{
TPMI_YES_NO moreData;
TPMS_CAPABILITY_DATA *capabilityData = NULL;
TSS2_RC r = Esys_GetCapability_Finish(fctx->esys,
&moreData, &capabilityData);
if (base_rc(r) == TSS2_BASE_RC_TRY_AGAIN)
return TSS2_FAPI_RC_TRY_AGAIN;
return_if_error(r, "GetCapability");
if (capabilityData->data.handles.count == 0 ||
capabilityData->data.handles.handle[0] != *handle) {
SAFE_FREE(capabilityData);
return TSS2_RC_SUCCESS;
}
SAFE_FREE(capabilityData);
*handle += 1;
if (*handle > max) {
return_error(TSS2_FAPI_RC_NV_TOO_SMALL, "No NV index free.");
}
r = ifapi_get_free_handle_async(fctx, handle);
return_if_error(r, "GetCapability");
return TSS2_FAPI_RC_TRY_AGAIN;
}
/** Create a linked list of directories in the key store.
*
* If the absolute path in key store is not defined the list will
* be extended if possible.
*
* @param[out] keystore The used keystore.
* @param[in] ipath The implicit pathname, which might be extended.
* @param[out] The linked list of directories in the explicit pathname.
*
* @retval TSS2_RC_SUCCESS If the keystore can be initialized.
* @retval TSS2_FAPI_RC_IO_ERROR If the user part of the keystore can't be
* initialized.
* @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated.
* @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
* the function.
* @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context
* or contains illegal characters.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
* during authorization.
*/
static TSS2_RC
get_explicit_key_path(
IFAPI_KEYSTORE *keystore,
const char *ipath,
NODE_STR_T **result)
{
NODE_STR_T *list_node1 = NULL;
NODE_STR_T *list_node = NULL;
/* Extend the first part of the list if necessary. */
TSS2_RC r = init_explicit_key_path(keystore->defaultprofile, ipath,
&list_node1, &list_node, result);
goto_if_error(r, "init_explicit_key_path", error);
/* Extend the list with the tail of the initial unmodified list. */
while (list_node != NULL) {
if (!add_string_to_list(*result, list_node->str)) {
LOG_ERROR("Out of memory");
r = TSS2_FAPI_RC_MEMORY;
goto error;
}
list_node = list_node->next;
}
free_string_list(list_node1);
return TSS2_RC_SUCCESS;
error:
if (*result)
free_string_list(*result);
if (list_node1)
free_string_list(list_node1);
return r;
}
/** Prepare the creation of a primary key.
*
* Depending on the parameters the creation of an endorsement or storage root key
* will be prepared.
*
* @param[in] context The FAPI_CONTEXT.
* @param[in] ktype The type of key TSS2_EK or TSS2_SRK.
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_FAPI_RC_BAD_VALUE if a wrong type was passed.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
* @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
* this function needs to be called again.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
* operation already pending.
* @retval TSS2_FAPI_RC_IO_ERROR if an error occurred while accessing the
* object store.
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
* during authorization.
* @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
* @retval TSS2_FAPI_RC_NOT_PROVISIONED FAPI was not provisioned.
* @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context
* or contains illegal characters.
*/
TSS2_RC
ifapi_init_primary_async(FAPI_CONTEXT *context, TSS2_KEY_TYPE ktype)
{
TSS2_RC r;
TPMS_POLICY *policy;
r = TSS2_RC_SUCCESS;
if (ktype == TSS2_EK) {
/* Values set according to EK credential profile. */
if (context->cmd.Provision.public_templ.public.publicArea.type == TPM2_ALG_RSA) {
context->cmd.Provision.public_templ.public.publicArea.unique.rsa.size = 256;
} else if (context->cmd.Provision.public_templ.public.publicArea.type == TPM2_ALG_ECC) {
context->cmd.Provision.public_templ.public.publicArea.unique.ecc.x.size = 32;
context->cmd.Provision.public_templ.public.publicArea.unique.ecc.y.size = 32;
}
policy = context->profiles.default_profile.ek_policy;
} else if (ktype == TSS2_SRK) {
policy = context->profiles.default_profile.srk_policy;
} else {
return_error(TSS2_FAPI_RC_BAD_VALUE,
"Invalid key type. Only EK or SRK allowed");
}
if (policy) {
/* Duplicate policy to prevent profile policy from cleanup. */
policy = ifapi_copy_policy(policy);
return_if_null(policy, "Out of memory.", TSS2_FAPI_RC_MEMORY);
r = ifapi_calculate_tree(context, NULL, /**< no path needed */
policy,
context->profiles.default_profile.nameAlg,
&context->cmd.Provision.digest_idx,
&context->cmd.Provision.hash_size);
if (r) {
LOG_ERROR("Policy calculation");
SAFE_FREE(policy);
return r;
}
context->cmd.Provision.public_templ.public.publicArea.authPolicy.size =
context->cmd.Provision.hash_size;
memcpy(&context->cmd.Provision.public_templ.public.publicArea.authPolicy.buffer[0],
&policy->policyDigests.digests[context->policy.digest_idx].digest,
context->cmd.Provision.hash_size);
}
context->createPrimary.pkey_object.policy = policy;
context->createPrimary.pkey_object.objectType = IFAPI_KEY_OBJ;
memset(&context->cmd.Provision.inSensitive, 0, sizeof(TPM2B_SENSITIVE_CREATE));
memset(&context->cmd.Provision.outsideInfo, 0, sizeof(TPM2B_DATA));
memset(&context->cmd.Provision.creationPCR, 0, sizeof(TPML_PCR_SELECTION));
context->primary_state = PRIMARY_AUTHORIZE_HIERARCHY;
return r;
}
/** Finalize the creation of a primary key.
*
* Depending on the parameters the creation of an endorsement key or a storage root key
* will be finalized. The created object with the all information needed by FAPI will
* be stored in the FAPI context.
*
* @param[in] context The FAPI_CONTEXT.
* @param[in] ktype The type of key TSS2_EK or TSS2_SRK.
* @param[in,out] hierarchy The hiearchy used for primary creation.
*
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_FAPI_RC_TRY_AGAIN if the execution cannot be completed.
* @retval TSS2_FAPI_RC_BAD_VALUE if a wrong type was passed.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
* @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
* is not set.
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
* operation already pending.
* @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
* during authorization.
* @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
* @retval TSS2_FAPI_RC_IO_ERROR if an error occured while accessing the
* object store.
* @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
* was not successful.
*/
TSS2_RC
ifapi_init_primary_finish(FAPI_CONTEXT *context, TSS2_KEY_TYPE ktype, IFAPI_OBJECT *hierarchy)
{
TSS2_RC r;
ESYS_TR primaryHandle;
TPM2B_PUBLIC *outPublic = NULL;
TPM2B_CREATION_DATA *creationData = NULL;
TPM2B_DIGEST *creationHash = NULL;
TPMT_TK_CREATION *creationTicket = NULL;
IFAPI_KEY *pkey = &context->createPrimary.pkey_object.misc.key;
NODE_STR_T *k_sub_path = NULL;
ESYS_TR auth_session;
switch (context->primary_state) {
statecase(context->primary_state, PRIMARY_AUTHORIZE_HIERARCHY);
if (hierarchy->misc.hierarchy.with_auth == TPM2_YES || policy_digest_size(hierarchy)) {
r = ifapi_authorize_object(context, hierarchy, &auth_session);
FAPI_SYNC(r, "Authorize hierarchy.", error_cleanup);
} else {
auth_session = context->session1;
}
r = Esys_CreatePrimary_Async(context->esys, hierarchy->handle,
(auth_session == ESYS_TR_NONE) ?
ESYS_TR_PASSWORD : auth_session,
ESYS_TR_NONE, ESYS_TR_NONE,
&context->cmd.Provision.inSensitive,
&context->cmd.Provision.public_templ.public,
&context->cmd.Provision.outsideInfo,
&context->cmd.Provision.creationPCR);
goto_if_error_reset_state(r, "CreatePrimary", error_cleanup);
fallthrough;
statecase(context->primary_state, PRIMARY_WAIT_FOR_PRIMARY);
r = Esys_CreatePrimary_Finish(context->esys,
&primaryHandle, &outPublic, &creationData, &creationHash,
&creationTicket);
if (base_rc(r) == TSS2_BASE_RC_TRY_AGAIN)
return TSS2_FAPI_RC_TRY_AGAIN;
/* Retry with authorization callback after trial with null auth */
if (number_rc(r) == TPM2_RC_BAD_AUTH
&& hierarchy->misc.hierarchy.with_auth == TPM2_NO) {
char *description;
r = ifapi_get_description(hierarchy, &description);
return_if_error(r, "Get description");
r = ifapi_set_auth(context, hierarchy, description);
SAFE_FREE(description);
goto_if_error_reset_state(r, "CreatePrimary", error_cleanup);
r = Esys_CreatePrimary_Async(context->esys, hierarchy->handle,
(context->session1 == ESYS_TR_NONE) ?
ESYS_TR_PASSWORD : context->session1,
ESYS_TR_NONE, ESYS_TR_NONE,
&context->cmd.Provision.inSensitive,
&context->cmd.Provision.public_templ.public,
&context->cmd.Provision.outsideInfo,
&context->cmd.Provision.creationPCR);
goto_if_error_reset_state(r, "CreatePrimary", error_cleanup);
if (ktype == TSS2_EK) {
context->state = PROVISION_AUTH_EK_AUTH_SENT;
} else {
context->state = PROVISION_AUTH_SRK_AUTH_SENT;
}
hierarchy->misc.hierarchy.with_auth = TPM2_YES;
return TSS2_FAPI_RC_TRY_AGAIN;
} else {
goto_if_error_reset_state(r, "FAPI Provision", error_cleanup);
}
/* Set EK or SRK handle in context. */
if (ktype == TSS2_EK) {
context->ek_handle = primaryHandle;
} else if (ktype == TSS2_SRK) {
context->srk_handle = primaryHandle;
} else {
return_error(TSS2_FAPI_RC_BAD_VALUE,
"Invalid key type. Only EK or SRK allowed");
}
/* Prepare serialization of pkey to key store. */
SAFE_FREE(pkey->serialization.buffer);
r = Esys_TR_Serialize(context->esys, primaryHandle, &pkey->serialization.buffer,
&pkey->serialization.size);
goto_if_error(r, "Error serialize esys object", error_cleanup);
r = ifapi_get_name(&outPublic->publicArea, &pkey->name);
goto_if_error(r, "Get primary name", error_cleanup);
pkey->public = *outPublic;
pkey->policyInstance = NULL;
pkey->creationData = *creationData;
pkey->creationTicket = *creationTicket;
pkey->description = NULL;
pkey->certificate = NULL;
/* Cleanup unused information */
SAFE_FREE(outPublic);
SAFE_FREE(creationData);
SAFE_FREE(creationHash);
SAFE_FREE(creationTicket);
if (pkey->public.publicArea.type == TPM2_ALG_RSA)
pkey->signing_scheme = context->profiles.default_profile.rsa_signing_scheme;
else
pkey->signing_scheme = context->profiles.default_profile.ecc_signing_scheme;
context->createPrimary.pkey_object.handle = primaryHandle;
SAFE_FREE(pkey->serialization.buffer);
return TSS2_RC_SUCCESS;
statecasedefault(context->primary_state);
}
error_cleanup:
ifapi_cleanup_ifapi_object(&context->createPrimary.pkey_object);
free_string_list(k_sub_path);
SAFE_FREE(pkey->serialization.buffer);
ifapi_cleanup_ifapi_object(&context->createPrimary.pkey_object);
return r;
}
/** Prepare the loading of a primary key from key store.
*
* The asynchronous loading or the key from keystore will be prepared and
* the path will be stored in the FAPI context.
*
* @param[in] context The FAPI_CONTEXT.
* @param[in] path The FAPI path of the primary key.
*
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_FAPI_RC_BAD_VALUE if a wrong type was passed.
* @retval TSS2_FAPI_RC_IO_ERROR if an I/O error was encountered.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND if the file does not exist.
* @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated for path names.
* @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
* @retval TSS2_FAPI_RC_NOT_PROVISIONED FAPI was not provisioned.
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
* @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context
* or contains illegal characters.
*/
TSS2_RC
ifapi_load_primary_async(FAPI_CONTEXT *context, char *path)
{
TSS2_RC r;
memset(&context->createPrimary.pkey_object, 0, sizeof(IFAPI_OBJECT));
context->createPrimary.path = path;
r = ifapi_keystore_load_async(&context->keystore, &context->io, path);
return_if_error2(r, "Could not open: %s", path);
context->primary_state = PRIMARY_READ_KEY;
return TSS2_RC_SUCCESS;
}
/** State machine to finalize the loading of a primary key from key store.
*
* The asynchronous loading or the key from keystore will be finalized.
* Afterwards the hierarchy object, which will be used for authorization will
* be loaded and the ESAPI functions for primary generation will be called
* if the primary is not persistent.
*
* @param[in] context The FAPI_CONTEXT.
* @param[out] handle The object handle of the primary key.
*
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_FAPI_RC_BAD_VALUE if a wrong type was passed.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND if the hierarchy file does not exist.
* @retval TSS2_FAPI_RC_IO_ERROR if an I/O error was encountered.
* @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated for path names.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
* @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
* this function needs to be called again.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
* operation already pending.
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
* @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
* @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
* is not set.
* @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
* @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
* was not successful.
* @retval TSS2_FAPI_RC_NOT_PROVISIONED FAPI was not provisioned.
* @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context
* or contains illegal characters.
*/
TSS2_RC
ifapi_load_primary_finish(FAPI_CONTEXT *context, ESYS_TR *handle)
{
TSS2_RC r;
IFAPI_OBJECT *hierarchy = &context->createPrimary.hierarchy;
TPM2B_PUBLIC *outPublic = NULL;
TPM2B_CREATION_DATA *creationData = NULL;
TPM2B_DIGEST *creationHash = NULL;
TPMT_TK_CREATION *creationTicket = NULL;
IFAPI_OBJECT *pkey_object = &context->createPrimary.pkey_object;
IFAPI_KEY *pkey = &context->createPrimary.pkey_object.misc.key;
TPMS_CAPABILITY_DATA **capabilityData = &context->createPrimary.capabilityData;
TPMI_YES_NO moreData;
ESYS_TR auth_session = ESYS_TR_NONE; /* Initialized due to scanbuild */
LOG_TRACE("call");
switch (context->primary_state) {
statecase(context->primary_state, PRIMARY_READ_KEY);
/* Read the primary key from keystore. */
r = ifapi_keystore_load_finish(&context->keystore, &context->io,
pkey_object);
return_try_again(r);
return_if_error(r, "read_finish failed");
r = ifapi_initialize_object(context->esys, pkey_object);
goto_if_error_reset_state(r, "Initialize key object", error_cleanup);
/* Check whether a persistent key was loaded.
In this case the handle has already been set. */
if (pkey_object->handle != ESYS_TR_NONE) {
if (pkey->creationTicket.hierarchy == TPM2_RH_EK) {
context->ek_persistent = true;
} else {
context->srk_persistent = true;
}
/* It has to be checked whether the persistent handle exists. */
context->primary_state = PRIMARY_VERIFY_PERSISTENT;
return TSS2_FAPI_RC_TRY_AGAIN;
}
else {
if (pkey->creationTicket.hierarchy == TPM2_RH_EK) {
context->ek_persistent = false;
} else {
context->srk_persistent = false;
}
}
fallthrough;
statecase(context->primary_state, PRIMARY_READ_HIERARCHY);
/* The hierarchy object ussed for auth_session will be loaded from key store. */
if (pkey->creationTicket.hierarchy == TPM2_RH_EK) {
r = ifapi_keystore_load_async(&context->keystore, &context->io, "/HE");
return_if_error2(r, "Could not open hierarchy /HE");
} else if (pkey->creationTicket.hierarchy == TPM2_RH_NULL) {
r = ifapi_keystore_load_async(&context->keystore, &context->io, "/HN");
return_if_error2(r, "Could not open hierarchy /HN");
} else {
r = ifapi_keystore_load_async(&context->keystore, &context->io, "/HS");
return_if_error2(r, "Could not open hierarchy /HS");
}
fallthrough;
statecase(context->primary_state, PRIMARY_READ_HIERARCHY_FINISH);
r = ifapi_keystore_load_finish(&context->keystore, &context->io, hierarchy);
return_try_again(r);
return_if_error(r, "read_finish failed");
r = ifapi_initialize_object(context->esys, hierarchy);
goto_if_error_reset_state(r, "Initialize hierarchy object", error_cleanup);
if (pkey->creationTicket.hierarchy == TPM2_RH_EK) {
hierarchy->handle = ESYS_TR_RH_ENDORSEMENT;
} else if (pkey->creationTicket.hierarchy == TPM2_RH_NULL) {
hierarchy->handle = ESYS_TR_RH_NULL;
} else {
hierarchy->handle = ESYS_TR_RH_OWNER;
}
fallthrough;
statecase(context->primary_state, PRIMARY_AUTHORIZE_HIERARCHY);
/* The asynchronous authorization of the hierarchy needed for primary. */
r = ifapi_authorize_object(context, hierarchy, &auth_session);
FAPI_SYNC(r, "Authorize hierarchy.", error_cleanup);
memset(&context->createPrimary.inSensitive, 0, sizeof(TPM2B_SENSITIVE_CREATE));
memset(&context->createPrimary.outsideInfo, 0, sizeof(TPM2B_DATA));
memset(&context->createPrimary.creationPCR, 0, sizeof(TPML_PCR_SELECTION));
fallthrough;
statecase(context->primary_state, PRIMARY_GET_AUTH_VALUE);
/* Get the auth value to be stored in inSensitive */
r = ifapi_set_auth_primary(context, pkey_object,
&context->createPrimary.inSensitive.sensitive);
return_try_again(r);
goto_if_error_reset_state(r, "Get auth value for primary", error_cleanup);
/* Prepare primary creation. */
TPM2B_PUBLIC public = pkey->public;
memset(&public.publicArea.unique, 0, sizeof(TPMU_PUBLIC_ID));
r = Esys_CreatePrimary_Async(context->esys, hierarchy->handle,
auth_session, ESYS_TR_NONE, ESYS_TR_NONE,
&context->createPrimary.inSensitive,
&public,
&context->createPrimary.outsideInfo,
&context->createPrimary.creationPCR);
return_if_error(r, "CreatePrimary");
fallthrough;
statecase(context->primary_state, PRIMARY_HAUTH_SENT);
if (context->createPrimary.handle) {
*handle = context->createPrimary.handle;
context->primary_state = PRIMARY_CREATED;
return TSS2_FAPI_RC_TRY_AGAIN;
} else {
r = Esys_CreatePrimary_Finish(context->esys,
&pkey_object->handle, &outPublic,
&creationData, &creationHash,
&creationTicket);
return_try_again(r);
goto_if_error_reset_state(r, "FAPI regenerate primary", error_cleanup);
}
*handle = pkey_object->handle;
context->primary_state = PRIMARY_INIT;
break;
statecase(context->primary_state, PRIMARY_VERIFY_PERSISTENT);
/* Check the TPM capabilities for the persistent handle. */
r = Esys_GetCapability_Async(context->esys,
ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
TPM2_CAP_HANDLES,
pkey_object->misc.key.persistent_handle, 1);
goto_if_error(r, "Esys_GetCapability_Async", error_cleanup);
fallthrough;
statecase(context->primary_state, PRIMARY_GET_CAP);
r = Esys_GetCapability_Finish(context->esys, &moreData, capabilityData);
return_try_again(r);
goto_if_error_reset_state(r, "GetCapablity_Finish", error_cleanup);
/* Check whether the persistent handle exists. */
if ((*capabilityData)->data.handles.count != 0 &&
(*capabilityData)->data.handles.handle[0] ==
pkey_object->misc.key.persistent_handle) {
/* Persistent handle found. */
SAFE_FREE(*capabilityData);
*handle = pkey_object->handle;
break;
}
goto_error(r, TSS2_FAPI_RC_KEY_NOT_FOUND ,
"The persistent handle 0x%x does not exist. "
"The TPM state and the keystore state do not match."
, error_cleanup, pkey_object->misc.key.persistent_handle);
fallthrough;
statecasedefault(context->primary_state);
}
SAFE_FREE(outPublic);
SAFE_FREE(creationData);
SAFE_FREE(creationHash);
SAFE_FREE(creationTicket);
ifapi_cleanup_ifapi_object(&context->createPrimary.hierarchy);
return TSS2_RC_SUCCESS;
error_cleanup:
SAFE_FREE(*capabilityData);
SAFE_FREE(outPublic);
SAFE_FREE(creationData);
SAFE_FREE(creationHash);
SAFE_FREE(creationTicket);
ifapi_cleanup_ifapi_object(&context->createPrimary.hierarchy);
ifapi_cleanup_ifapi_object(&context->createPrimary.pkey_object);
return r;
}
/** Prepare session for FAPI command execution.
*
* It will be checked whether the context of FAPI and ESAPI is initialized
* and whether no other FAPI command session is running.
* Also some handle variables in the context are initialized.
*
* @param[in] context The FAPI_CONTEXT.
*
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_FAPI_RC_BAD_REFERENCE if the context is not initialized.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE If a FAPI command session is active.
* @retval TSS2_FAPI_RC_NO_TPM if the ESAPI context is not initialized.
*/
TSS2_RC
ifapi_session_init(FAPI_CONTEXT *context)
{
LOG_TRACE("call");
return_if_null(context, "No context", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(context->esys, "No context", TSS2_FAPI_RC_NO_TPM);
if (context->state != _FAPI_STATE_INIT) {
return_error(TSS2_FAPI_RC_BAD_SEQUENCE, "Invalid State");
}
context->session1 = ESYS_TR_NONE;
context->session2 = ESYS_TR_NONE;
context->policy.session = ESYS_TR_NONE;
context->srk_handle = ESYS_TR_NONE;
return TSS2_RC_SUCCESS;
}
/** Prepare session for FAPI command execution without TPM.
*
* It will be checked whether the context of FAPI is initialized
* and whether no other FAPI command session is running.
* Also some handle variables in the context are initialized.
*
* @param[in] context The FAPI_CONTEXT.
*
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_FAPI_RC_BAD_REFERENCE if the context is not initialized.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE If a FAPI command session is active.
*/
TSS2_RC
ifapi_non_tpm_mode_init(FAPI_CONTEXT *context)
{
LOG_TRACE("call");
return_if_null(context, "No context", TSS2_FAPI_RC_BAD_REFERENCE);
if (context->state != _FAPI_STATE_INIT) {
return_error(TSS2_FAPI_RC_BAD_SEQUENCE, "Invalid State");
}
context->session1 = ESYS_TR_NONE;
context->session2 = ESYS_TR_NONE;
context->policy.session = ESYS_TR_NONE;
context->srk_handle = ESYS_TR_NONE;
return TSS2_RC_SUCCESS;
}
/** Cleanup FAPI sessions in error cases.
*
* The uses sessions and the SRK (if not persistent) will be flushed
* non asynchronous in error cases.
*
* @param[in,out] context The FAPI_CONTEXT.
*/
void
ifapi_session_clean(FAPI_CONTEXT *context)
{
if (context->policy_session && context->policy_session != ESYS_TR_NONE) {
Esys_FlushContext(context->esys, context->policy_session);
}
if (context->session1 != ESYS_TR_NONE) {
if (Esys_FlushContext(context->esys, context->session1) != TSS2_RC_SUCCESS) {
LOG_ERROR("Cleanup session failed.");
}
context->session1 = ESYS_TR_NONE;
}
if (context->session2 != ESYS_TR_NONE) {
if (Esys_FlushContext(context->esys, context->session2) != TSS2_RC_SUCCESS) {
LOG_ERROR("Cleanup session failed.");
context->session2 = ESYS_TR_NONE;
}
}
if (!context->srk_persistent && context->srk_handle != ESYS_TR_NONE) {
if (Esys_FlushContext(context->esys, context->srk_handle) != TSS2_RC_SUCCESS) {
LOG_ERROR("Cleanup Policy Session failed.");
}
context->srk_handle = ESYS_TR_NONE;
}
context->srk_persistent = false;
}
/** State machine for asynchronous cleanup of a FAPI session.
*
* Used sessions and the SRK will be flushed.
*
* @param[in] context The FAPI_CONTEXT storing the used handles.
*
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
* @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
* this function needs to be called again.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
* operation already pending.
*/
TSS2_RC
ifapi_cleanup_session(FAPI_CONTEXT *context)
{
TSS2_RC r;
/* Policy sessions were closed after successful execution. */
context->policy_session = ESYS_TR_NONE;
switch (context->cleanup_state) {
statecase(context->cleanup_state, CLEANUP_INIT);
if (context->session1 != ESYS_TR_NONE) {
r = Esys_FlushContext_Async(context->esys, context->session1);
try_again_or_error(r, "Flush session.");
}
fallthrough;
statecase(context->cleanup_state, CLEANUP_SESSION1);
if (context->session1 != ESYS_TR_NONE) {
r = Esys_FlushContext_Finish(context->esys);
try_again_or_error(r, "Flush session.");
}
context->session1 = ESYS_TR_NONE;
if (context->session2 != ESYS_TR_NONE) {
r = Esys_FlushContext_Async(context->esys, context->session2);
try_again_or_error(r, "Flush session.");
}
fallthrough;
statecase(context->cleanup_state, CLEANUP_SESSION2);
if (context->session2 != ESYS_TR_NONE) {
r = Esys_FlushContext_Finish(context->esys);
try_again_or_error(r, "Flush session.");
}
context->session2 = ESYS_TR_NONE;
if (!context->srk_persistent && context->srk_handle != ESYS_TR_NONE) {
r = Esys_FlushContext_Async(context->esys, context->srk_handle);
try_again_or_error(r, "Flush SRK.");
}
fallthrough;
statecase(context->cleanup_state, CLEANUP_SRK);
if (!context->srk_persistent && context->srk_handle != ESYS_TR_NONE) {
r = Esys_FlushContext_Finish(context->esys);
try_again_or_error(r, "Flush SRK.");
context->srk_handle = ESYS_TR_NONE;
context->srk_persistent = false;
}
context->cleanup_state = CLEANUP_INIT;
return TSS2_RC_SUCCESS;
statecasedefault(context->state);
}
}
/** Cleanup primary keys in error cases (non asynchronous).
*
* @param[in] context The FAPI_CONTEXT storing the used handles.
*
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
*/
void
ifapi_primary_clean(FAPI_CONTEXT *context)
{
if (!context->srk_persistent && context->srk_handle != ESYS_TR_NONE) {
if (Esys_FlushContext(context->esys, context->srk_handle) != TSS2_RC_SUCCESS) {
LOG_ERROR("Cleanup session failed.");
}
context->srk_handle = ESYS_TR_NONE;
}
if (!context->ek_persistent && context->ek_handle != ESYS_TR_NONE) {
if (Esys_FlushContext(context->esys, context->ek_handle) != TSS2_RC_SUCCESS) {
LOG_ERROR("Cleanup EK failed.");
}
context->ek_handle = ESYS_TR_NONE;
}
context->srk_persistent = false;
}
/** Prepare the session creation of a FAPI command.
*
* The initial state of the state machine for session creation will be determined.
* Depending of the session_flags creation of a primary for the encryption of
* the session secret can be adjusted.
* The session passed session attributes will be used for the ESAPI command
* Esys_TRSess_SetAttributes.
*
* @param[in] context The FAPI_CONTEXT storing the used handles.
* @param[in] session_flags The flags to adjust used session and encryption
* key. With IFAPI_SESSION1 and IFAPI_SESSION2 the session creation
* for sesion1 and session2 can be activated, IFAPI_SESSION_GENEK
* triggers the creation of the primary for session secret encryption.
* @param[in] attribute_flags1 The attributes used for session1.
* @param[in] attribute_flags2 The attributes used for session2.
*
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND if the hierarchy file or the primary key file
* does not exist.
* @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated for path names.
* of the primary.
* @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
* @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
* the function.
* @retval TSS2_FAPI_RC_IO_ERROR if an error occurred while accessing the
* object store.
* @retval TSS2_FAPI_RC_NOT_PROVISIONED FAPI was not provisioned.
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
* @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context
* or contains illegal characters.
*/
TSS2_RC
ifapi_get_sessions_async(FAPI_CONTEXT *context,
IFAPI_SESSION_TYPE session_flags,
TPMA_SESSION attribute_flags1,
TPMA_SESSION attribute_flags2)
{
TSS2_RC r;
LOG_TRACE("call");
context->session_flags = session_flags;
context->session1_attribute_flags = attribute_flags1;
context->session2_attribute_flags = attribute_flags2;
char *file = NULL;
if (!(session_flags & IFAPI_SESSION_GENEK)) {
context->srk_handle = ESYS_TR_NONE;
context->session_state = SESSION_CREATE_SESSION;
return TSS2_RC_SUCCESS;
}
context->primary_state = PRIMARY_INIT;
r = ifapi_asprintf(&file, "%s%s", context->config.profile_name,
IFAPI_SRK_KEY_PATH);
goto_if_error(r, "Error ifapi_asprintf", error_cleanup);
r = ifapi_load_primary_async(context, file);
return_if_error_reset_state(r, "Load EK");
free(file);
context->session_state = SESSION_WAIT_FOR_PRIMARY;
return TSS2_RC_SUCCESS;
error_cleanup:
SAFE_FREE(file);
return r;
}
/** State machine for the session creation of a FAPI command.
*
* The sessions needed for a FAPI command will be created. If needed also the
* primary key for session encryption will be created.
*
* @param[in] context The FAPI_CONTEXT storing the used handles.
* @param[in] profile The FAPI profile will be used to adjust session parameters.
* @param[in] hash_alg The hash algorithm used for the session.
*
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_FAPI_RC_IO_ERROR if an I/O error was encountered.
* @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated for path names.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
* @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
* this function needs to be called again.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
* operation already pending.
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
* @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
* the function.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
* during authorization.
* @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
* @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
* is not set.
* @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
* @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
* was not successful.
* @retval TSS2_FAPI_RC_NOT_PROVISIONED FAPI was not provisioned.
* @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context
* or contains illegal characters.
*/
TSS2_RC
ifapi_get_sessions_finish(
FAPI_CONTEXT *context,
const IFAPI_PROFILE *profile,
TPMI_ALG_HASH hash_alg)
{
TSS2_RC r;
switch (context->session_state) {
statecase(context->session_state, SESSION_WAIT_FOR_PRIMARY);
LOG_TRACE("**STATE** SESSION_WAIT_FOR_PRIMARY");
r = ifapi_load_primary_finish(context, &context->srk_handle);
return_try_again(r);
ifapi_cleanup_ifapi_object(&context->createPrimary.pkey_object);
return_if_error(r, "Load primary.");
fallthrough;
statecase(context->session_state, SESSION_CREATE_SESSION);
LOG_TRACE("**STATE** SESSION_CREATE_SESSION");
if (!(context->session_flags & IFAPI_SESSION1)) {
LOG_TRACE("finished");
return TSS2_RC_SUCCESS;
}
/* Initializing the first session for the caller */
r = ifapi_get_session_async(context->esys, context->srk_handle, profile,
hash_alg);
return_if_error_reset_state(r, "Create FAPI session async");
fallthrough;
statecase(context->session_state, SESSION_WAIT_FOR_SESSION1);
LOG_TRACE("**STATE** SESSION_WAIT_FOR_SESSION1");
r = ifapi_get_session_finish(context->esys, &context->session1,
context->session1_attribute_flags);
return_try_again(r);
return_if_error_reset_state(r, "Create FAPI session finish");
if (!(context->session_flags & IFAPI_SESSION2)) {
LOG_TRACE("finished");
return TSS2_RC_SUCCESS;
}
/* Initializing the second session for the caller */
r = ifapi_get_session_async(context->esys, context->srk_handle, profile,
profile->nameAlg);
return_if_error_reset_state(r, "Create FAPI session async");
fallthrough;
statecase(context->session_state, SESSION_WAIT_FOR_SESSION2);
LOG_TRACE("**STATE** SESSION_WAIT_FOR_SESSION2");
r = ifapi_get_session_finish(context->esys, &context->session2,
context->session2_attribute_flags);
return_try_again(r);
return_if_error_reset_state(r, "Create FAPI session finish");
break;
statecasedefault(context->session_state);
}
return TSS2_RC_SUCCESS;
}
/** Merge profile already stored in FAPI context into a NV object template.
*
* The defaults for NV creation which are stored in the FAPI default profile
* will be merged in the passed templates default values.
*
* @param[in] context The FAPI_CONTEXT with the default profile.
* @param[in] template The template with the default values for the NV object.
*
* @retval TSS2_RC_SUCCESS on success.
*/
TSS2_RC
ifapi_merge_profile_into_nv_template(
FAPI_CONTEXT *context,
IFAPI_NV_TEMPLATE *template)
{
const TPMA_NV extend_mask = TPM2_NT_EXTEND << TPMA_NV_TPM2_NT_SHIFT;
const TPMA_NV counter_mask = TPM2_NT_COUNTER << TPMA_NV_TPM2_NT_SHIFT;
const TPMA_NV bitfield_mask = TPM2_NT_BITS << TPMA_NV_TPM2_NT_SHIFT;
const IFAPI_PROFILE *profile = &context->profiles.default_profile;
size_t hash_size;
template->public.nameAlg = profile->nameAlg;
if ((template->public.attributes & extend_mask) == extend_mask) {
/* The size of the NV ram to be extended must be read from the
profile */
hash_size = ifapi_hash_get_digest_size(profile->nameAlg);
template->public.dataSize = hash_size;
} else if ((template->public.attributes & counter_mask) == counter_mask ||
(template->public.attributes & bitfield_mask) == bitfield_mask) {
/* For bit fields and counters only size 8 is possible */
template->public.dataSize = 8;
} else {
/* For normal NV ram the passed size will be used. */
template->public.dataSize = context->nv_cmd.numBytes;
}
return TSS2_RC_SUCCESS;
}
/** Merge profile already stored in FAPI context into a key template.
*
* The defaults for key creation which are stored in the FAPI default profile
* will be merged in the passed templates default values.
*
* @param[in] profile The profile which will be used to adjust the template.
* @param[in] template The template with the default values for the key object.
*
* @retval TSS2_RC_SUCCESS on success.
*/
TSS2_RC
ifapi_merge_profile_into_template(
const IFAPI_PROFILE *profile,
IFAPI_KEY_TEMPLATE *template)
{
/* Merge profile parameters */
template->public.publicArea.type = profile->type;
template->public.publicArea.nameAlg = profile->nameAlg;
if (profile->type == TPM2_ALG_RSA) {
template->public.publicArea.parameters.rsaDetail.keyBits = profile->keyBits;
template->public.publicArea.parameters.rsaDetail.exponent = profile->exponent;
} else if (profile->type == TPM2_ALG_ECC) {
template->public.publicArea.parameters.eccDetail.curveID = profile->curveID;
template->public.publicArea.parameters.eccDetail.kdf.scheme = TPM2_ALG_NULL;
}
/* Set remaining parameters depending on key type */
if (template->public.publicArea.objectAttributes & TPMA_OBJECT_RESTRICTED) {
if (template->public.publicArea.objectAttributes & TPMA_OBJECT_DECRYPT) {
template->public.publicArea.parameters.asymDetail.symmetric =
profile->sym_parameters;
} else {
template->public.publicArea.parameters.asymDetail.symmetric.algorithm =
TPM2_ALG_NULL;
}
if (profile->type == TPM2_ALG_RSA) {
if (template->public.publicArea.objectAttributes & TPMA_OBJECT_SIGN_ENCRYPT) {
template->public.publicArea.parameters.rsaDetail.scheme.scheme =
profile->rsa_signing_scheme.scheme;
memcpy(&template->public.publicArea.parameters.rsaDetail.scheme.details,
&profile->rsa_signing_scheme.details, sizeof(TPMU_ASYM_SCHEME));
} else {
template->public.publicArea.parameters.rsaDetail.scheme.scheme = TPM2_ALG_NULL;
}
} else if (profile->type == TPM2_ALG_ECC) {
if (template->public.publicArea.objectAttributes & TPMA_OBJECT_SIGN_ENCRYPT) {
template->public.publicArea.parameters.eccDetail.scheme.scheme =
profile->ecc_signing_scheme.scheme;
memcpy(&template->public.publicArea.parameters.eccDetail.scheme.details,
&profile->ecc_signing_scheme.details, sizeof(TPMU_ASYM_SCHEME));
} else {
template->public.publicArea.parameters.eccDetail.scheme.scheme = TPM2_ALG_NULL;
}
} else {
template->public.publicArea.parameters.asymDetail.scheme.scheme = TPM2_ALG_NULL;
}
} else {
/* Non restricted key */
template->public.publicArea.parameters.asymDetail.symmetric.algorithm =
TPM2_ALG_NULL;
template->public.publicArea.parameters.asymDetail.scheme.scheme = TPM2_ALG_NULL;
}
return TSS2_RC_SUCCESS;
}
/** Convert absolute path to FAPI path which can be used as parameter for FAPI commands.
*
* Function converts the absolute path to a FAPI path.
*
* @param[in] keystore The used keystore.
* @param[out] path FAPI key path.
*/
static void
full_path_to_fapi_path(IFAPI_KEYSTORE *keystore, char *path)
{
unsigned int start_pos, end_pos, i;
const unsigned int path_length = strlen(path);
size_t keystore_length = strlen(keystore->userdir);
char fapi_path_delim;
start_pos = 0;
/* Check key store part of the path */
if (strncmp(&path[0], keystore->userdir, keystore_length) == 0) {
start_pos = strlen(keystore->userdir);
} else {
keystore_length = strlen(keystore->systemdir);
if (strncmp(&path[0], keystore->systemdir, keystore_length) == 0)
start_pos = strlen(keystore->systemdir);
}
if (!start_pos)
return;
/* Shift FAPI path to the beginning. */
end_pos = path_length - start_pos;
memmove(&path[0], &path[start_pos], end_pos);
size_t ip = 0;
size_t lp = strlen(path);
/* Special handling for // */
while (ip < lp) {
if (strncmp(&path[ip], "//", 2) == 0) {
memmove(&path[ip], &path[ip + 1], lp - ip);
lp -= 1;
} else {
ip += 1;
}
}
/* Special handling for policy path were the name of the object file
is part of the path. */
if (ifapi_path_type_p(path, IFAPI_POLICY_PATH))
fapi_path_delim = '.';
else
fapi_path_delim = IFAPI_FILE_DELIM_CHAR;
for (i = end_pos - 1; i > 0; i--) {
if (path[i] == fapi_path_delim) {
path[i] = '\0';
break;
}
}
}
/** Asynchronous preparation for loading a key and parent keys.
*
* The key loading is prepared. The pathname will be extended if possible and
* a linked list with the directories will be created.
*
* @param[in,out] context The FAPI_CONTEXT.
* @param[in] keyPath the key path without the parent directories
* of the key store. (e.g. HE/EK, HS/SRK/mykey)
*
* @retval TSS2_RC_SUCCESS If the preparation is successful.
* @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated for path names.
* @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
* the function.
* @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context
* or contains illegal characters.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
* during authorization.
*/
TSS2_RC
ifapi_load_keys_async(FAPI_CONTEXT *context, char const *keyPath)
{
TSS2_RC r;
NODE_STR_T *path_list;
size_t path_length;
char *fapi_key_path = NULL;
LOG_TRACE("Load key: %s", keyPath);
fapi_key_path = strdup(keyPath);
check_oom(fapi_key_path);
full_path_to_fapi_path(&context->keystore, fapi_key_path);
r = get_explicit_key_path(&context->keystore, fapi_key_path, &path_list);
SAFE_FREE(fapi_key_path);
return_if_error(r, "Compute explicit path.");
context->loadKey.path_list = path_list;
path_length = ifapi_path_length(path_list);
r = ifapi_load_key_async(context, path_length);
goto_if_error(r, "Load key async.", error);
return TSS2_RC_SUCCESS;
error:
free_string_list( context->loadKey.path_list);
return r;
}
/** Asynchronous preparation for loading of the parent keys.
*
* The key loading is prepared. The pathname will be extended if possible and
* a linked list with the directories will be created.
*
* @param[in,out] context The FAPI_CONTEXT.
* @param[in] keyPath the key path without the parent directories
* of the key store. (e.g. HE/EK, HS/SRK/mykey)
*
* @retval TSS2_RC_SUCCESS If the preparation is successful.
* @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated for path names.
* @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
* the function.
* @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context
* or contains illegal characters.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
* during authorization.
*/
TSS2_RC
ifapi_load_parent_keys_async(FAPI_CONTEXT *context, char const *keyPath)
{
TSS2_RC r;
NODE_STR_T *path_list;
size_t path_length;
char *fapi_key_path = NULL;
LOG_TRACE("Load key: %s", keyPath);
fapi_key_path = strdup(keyPath);
check_oom(fapi_key_path);
full_path_to_fapi_path(&context->keystore, fapi_key_path);
r = get_explicit_key_path(&context->keystore, fapi_key_path, &path_list);
SAFE_FREE(fapi_key_path);
goto_if_error(r, "Compute explicit path.", error);
context->loadKey.path_list = path_list;
path_length = ifapi_path_length(path_list);
r = ifapi_load_key_async(context, path_length - 1);
return_if_error(r, "Load key async.");
return TSS2_RC_SUCCESS;
error:
free_string_list(context->loadKey.path_list);
return r;
}
/** Asynchronous finish function for loading a key.
*
* @param[in,out] context The FAPI_CONTEXT.
* @param[in] flush_parent If false the parent of the key to be loaded
* will not be flushed.
* @param[out] handle The ESYS handle of the key.
* @param[out] key_object The object which will be used for the
* authorization of the loaded key.
* @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
* this function needs to be called again.
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
* @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
* the function.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
* operation already pending.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
* during authorization.
* @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
* @retval TSS2_FAPI_RC_IO_ERROR if an error occurred while accessing the
* object store.
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
* @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
* is not set.
* @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
* @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
* was not successful.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
* @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context
* or contains illegal characters.
* @retval TSS2_FAPI_RC_NOT_PROVISIONED FAPI was not provisioned.
*/
TSS2_RC
ifapi_load_keys_finish(
FAPI_CONTEXT *context,
bool flush_parent,
ESYS_TR *handle,
IFAPI_OBJECT **key_object)
{
TSS2_RC r;
r = ifapi_load_key_finish(context, flush_parent);
if (r == TSS2_FAPI_RC_TRY_AGAIN)
return r;
goto_if_error(r, "Load keys", error);
*handle = context->loadKey.auth_object.handle;
/* The current authorization object is the last key loaded and
will be used. */
*key_object = &context->loadKey.auth_object;
free_string_list(context->loadKey.path_list);
return TSS2_RC_SUCCESS;
error:
free_string_list(context->loadKey.path_list);
return r;
}
/** Initialize state machine for loading a key.
*
* @param[in,out] context for storing all state information.
* @param[in] position the position of the key in path list stored in
* context->loadKey.path_list.
*
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated for path names.
*/
TSS2_RC
ifapi_load_key_async(FAPI_CONTEXT *context, size_t position)
{
context->loadKey.state = LOAD_KEY_GET_PATH;
context->loadKey.position = position;
context->loadKey.key_list = NULL;
context->loadKey.parent_handle = ESYS_TR_NONE;
return TSS2_RC_SUCCESS;
}
/** State machine for loading a key.
*
* A stack with all sup keys will be created and decremented during
* the loading auf all keys.
* The object of the loaded key will be stored in:
* context->loadKey.auth_object
*
* @param[in,out] context for storing all state information.
* @param[in] flush_parent If flush_parent is false parent is
only flushed if a new parent is available.
*
* @retval TSS2_RC_SUCCESS If the loading of the key was successful.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
* @retval TSS2_FAPI_RC_GENERAL_FAILURE If an internal error occurs, which is
* not covered by other return codes.
* @retval TSS2_FAPI_RC_BAD_VALUE If wrong values are detected during execution.
* @retval TSS2_FAPI_RC_IO_ERROR If an error occurs during access to the policy
* store.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND If an object needed for loading or
* authentication was not found.
* @retval TSS2_FAPI_RC_POLICY_UNKNOWN If policy search for a certain policy digest was
* not successful.
* @retval TPM2_RC_BAD_AUTH If the authentication for an object needed for loading
* fails.
* @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a needed authorization callback
* is not defined.
* @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
* this function needs to be called again.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
* operation already pending.
* @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
* @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
* @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context
* or contains illegal characters.
* @retval TSS2_FAPI_RC_NOT_PROVISIONED FAPI was not provisioned.
*/
TSS2_RC
ifapi_load_key_finish(FAPI_CONTEXT *context, bool flush_parent)
{
TSS2_RC r;
NODE_STR_T *path_list = context->loadKey.path_list;
size_t *position = &context->loadKey.position;
IFAPI_OBJECT *key_object = NULL;
IFAPI_KEY *key = NULL;
ESYS_TR auth_session;
switch (context->loadKey.state) {
statecase(context->loadKey.state, LOAD_KEY_GET_PATH);
context->loadKey.key_path = NULL;
/* Compute path name of key to be loaded. */
r = ifapi_path_string_n(&context->loadKey.key_path, NULL, path_list, NULL,
*position);
LOG_TRACE("Load path %s", context->loadKey.key_path);
return_if_error(r, "Compute key path.");
context->loadKey.key_object = ifapi_allocate_object(context);
goto_if_null2(context->loadKey.key_object, "Allocating key", r,
TSS2_FAPI_RC_MEMORY, error_cleanup);
goto_if_null2(context->loadKey.key_path, "Invalid path", r,
TSS2_FAPI_RC_GENERAL_FAILURE,
error_cleanup); /**< to avoid scan-build errors. */
/* Prepare key loading. */
r = ifapi_keystore_load_async(&context->keystore, &context->io,
context->loadKey.key_path);
return_if_error2(r, "Could not open: %s", context->loadKey.key_path);
fallthrough;
statecase(context->loadKey.state, LOAD_KEY_READ_KEY);
goto_if_null2(context->loadKey.key_path, "Invalid path", r,
TSS2_FAPI_RC_GENERAL_FAILURE,
error_cleanup); /**< to avoid scan-build errors. */
key = &context->loadKey.key_object->misc.key;
r = ifapi_keystore_load_finish(&context->keystore, &context->io,
context->loadKey.key_object);
if (r != TSS2_RC_SUCCESS) {
ifapi_cleanup_ifapi_object(context->loadKey.key_object);
}
return_try_again(r);
return_if_error(r, "read_finish failed");
if (context->loadKey.key_object->objectType != IFAPI_KEY_OBJ)
goto_error(r, TSS2_FAPI_RC_BAD_PATH, "%s is no key", error_cleanup,
context->loadKey.key_path);
r = ifapi_initialize_object(context->esys, context->loadKey.key_object);
goto_if_error_reset_state(r, "Initialize key object", error_cleanup);
SAFE_FREE(context->loadKey.key_path);
context->loadKey.handle = context->loadKey.key_object->handle;
if (context->loadKey.handle != ESYS_TR_NONE) {
/* Persistent key could be desearialized keys can be loaded */
r = ifapi_copy_ifapi_key_object(&context->loadKey.auth_object,
context->loadKey.key_object);
goto_if_error(r, "Could not copy key object", error_cleanup);
ifapi_cleanup_ifapi_object(context->loadKey.key_object);
context->loadKey.state = LOAD_KEY_LOAD_KEY;
return TSS2_FAPI_RC_TRY_AGAIN;
}
if (key->private.size == 0) {
/* Create a deep copy of the primary key */
r = ifapi_copy_ifapi_key_object(&context->createPrimary.pkey_object,
context->loadKey.key_object);
goto_if_error(r, "Could not copy primary key", error_cleanup);
ifapi_cleanup_ifapi_key(key);
context->primary_state = PRIMARY_READ_HIERARCHY;
context->loadKey.state = LOAD_KEY_WAIT_FOR_PRIMARY;
return TSS2_FAPI_RC_TRY_AGAIN;
}
IFAPI_OBJECT * copyToPush = malloc(sizeof(IFAPI_OBJECT));
goto_if_null(copyToPush, "Out of memory", TSS2_FAPI_RC_MEMORY, error_cleanup);
r = ifapi_copy_ifapi_key_object(copyToPush, context->loadKey.key_object);
if (r) {
free(copyToPush);
LOG_ERROR("Could not create a copy to push");
goto error_cleanup;
}
/* Add object to the list of keys to be loaded. */
r = push_object_to_list(copyToPush, &context->loadKey.key_list);
if (r) {
free(copyToPush);
LOG_ERROR("Out of memory");
goto error_cleanup;
}
ifapi_cleanup_ifapi_object(context->loadKey.key_object);
*position -= 1;
context->loadKey.state = LOAD_KEY_GET_PATH;
return TSS2_FAPI_RC_TRY_AGAIN;
statecase(context->loadKey.state, LOAD_KEY_LOAD_KEY);
if (!(context->loadKey.key_list)) {
LOG_TRACE("All keys loaded.");
return TSS2_RC_SUCCESS;
}
/* if flush_parent is false parent is only flushed if a new parent
is available */
if (!flush_parent && context->loadKey.parent_handle != ESYS_TR_NONE) {
r = Esys_FlushContext(context->esys, context->loadKey.parent_handle);
goto_if_error_reset_state(r, "Flush object", error_cleanup);
}
fallthrough;
statecase(context->loadKey.state, LOAD_KEY_AUTHORIZE);
key_object = context->loadKey.key_list->object;
key = &key_object->misc.key;
r = ifapi_authorize_object(context, &context->loadKey.auth_object, &auth_session);
FAPI_SYNC(r, "Authorize key.", error_cleanup);
/* Store parent handle in context for usage in ChangeAuth if not persistent */
context->loadKey.parent_handle = context->loadKey.handle;
if (context->loadKey.auth_object.misc.key.persistent_handle)
context->loadKey.parent_handle_persistent = true;
else
context->loadKey.parent_handle_persistent = false;
TPM2B_PRIVATE private;
private.size = key->private.size;
memcpy(&private.buffer[0], key->private.buffer, key->private.size);
r = Esys_Load_Async(context->esys, context->loadKey.handle,
auth_session,
ESYS_TR_NONE, ESYS_TR_NONE,
&private, &key->public);
goto_if_error(r, "Load async", error_cleanup);
fallthrough;
statecase(context->loadKey.state, LOAD_KEY_AUTH);
r = Esys_Load_Finish(context->esys, &context->loadKey.handle);
return_try_again(r);
goto_if_error_reset_state(r, "Load", error_cleanup);
/* The current parent is flushed if not prohibited by flush parent */
if (flush_parent && context->loadKey.auth_object.objectType == IFAPI_KEY_OBJ &&
! context->loadKey.auth_object.misc.key.persistent_handle) {
r = Esys_FlushContext(context->esys, context->loadKey.auth_object.handle);
goto_if_error_reset_state(r, "Flush object", error_cleanup);
}
LOG_TRACE("New key used as auth object.");
ifapi_cleanup_ifapi_object(&context->loadKey.auth_object);
r = ifapi_copy_ifapi_key_object(&context->loadKey.auth_object,
context->loadKey.key_list->object);
goto_if_error(r, "Could not copy loaded key", error_cleanup);
context->loadKey.auth_object.handle = context->loadKey.handle;
IFAPI_OBJECT *top_obj = context->loadKey.key_list->object;
ifapi_cleanup_ifapi_object(top_obj);
SAFE_FREE(context->loadKey.key_list->object);
r = pop_object_from_list(context, &context->loadKey.key_list);
goto_if_error_reset_state(r, "Pop key failed.", error_cleanup);
if (context->loadKey.key_list) {
/* Object can be cleaned if it's not the last */
ifapi_free_object(context, &top_obj);
}
context->loadKey.state = LOAD_KEY_LOAD_KEY;
return TSS2_FAPI_RC_TRY_AGAIN;
statecase(context->loadKey.state, LOAD_KEY_WAIT_FOR_PRIMARY);
r = ifapi_load_primary_finish(context, &context->loadKey.handle);
return_try_again(r);
goto_if_error(r, "CreatePrimary", error_cleanup);
/* Save the primary object for authorization */
r = ifapi_copy_ifapi_key_object(&context->loadKey.auth_object,
&context->createPrimary.pkey_object);
goto_if_error(r, "Could not copy primary key", error_cleanup);
if (context->loadKey.key_list) {
context->loadKey.state = LOAD_KEY_LOAD_KEY;
return TSS2_FAPI_RC_TRY_AGAIN;
} else {
LOG_TRACE("success");
ifapi_cleanup_ifapi_object(context->loadKey.key_object);
return TSS2_RC_SUCCESS;
}
break;
statecasedefault(context->loadKey.state);
}
error_cleanup:
if (context->loadKey.handle && context->loadKey.handle != ESYS_TR_NONE &&
context->loadKey.key_object->misc.key.persistent_handle) {
Esys_FlushContext(context->esys, context->loadKey.handle);
}
ifapi_free_object_list(context->loadKey.key_list);
ifapi_cleanup_ifapi_object(context->loadKey.key_object);
SAFE_FREE(context->loadKey.key_path);
return r;
}
/** Get the name alg corresponding to a FAPI object.
*
* @param[in] context The context with the default profile.
* @param[in] object The object to be checked.
* @retval TPMI_ALG_HASH The hash algorithm.
* @retval 0 If no name alg can be assigned to the object.
*/
static size_t
get_name_alg(FAPI_CONTEXT *context, IFAPI_OBJECT *object)
{
switch (object->objectType) {
case IFAPI_KEY_OBJ:
return object->misc.key.public.publicArea.nameAlg;
case IFAPI_NV_OBJ:
return object->misc.nv.public.nvPublic.nameAlg;
case IFAPI_HIERARCHY_OBJ:
return context->profiles.default_profile.nameAlg;
default:
return 0;
}
}
/** Check whether policy session has to be flushed.
*
* Policy sessions with cleared continue session flag are not flushed in error
* cases. Therefore the return code will be checked and if a policy session was
* used the session will be flushed if the command was not executed successfully.
*
* @param[in,out] context for storing all state information.
* @param[in] session the session to be checked whether flush is needed.
* @param[in] r The return code of the command using the session.
*/
void
ifapi_flush_policy_session(FAPI_CONTEXT *context, ESYS_TR session, TSS2_RC r)
{
if (session != context->session1) {
/* A policy session was used instead auf the default session. */
if (r != TSS2_RC_SUCCESS) {
Esys_FlushContext(context->esys, session);
}
}
}
/** State machine to authorize a key, a NV object of a hierarchy.
*
* @param[in,out] context for storing all state information.
* @param[in] object The FAPI object.
* @param[out] session The session which can be used for object authorization.
*
* @retval TSS2_RC_SUCCESS If the authorization is successful
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
* @retval TSS2_FAPI_RC_BAD_VALUE If wrong values are detected during execution.
* @retval TSS2_FAPI_RC_IO_ERROR If an error occurs during access to the policy
* store.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND If a policy for a certain path was not found.
* @retval TSS2_FAPI_RC_POLICY_UNKNOWN If policy search for a certain policy digest was
* not successful.
* @retval TPM2_RC_BAD_AUTH If the authentication for an object needed for the policy
* execution fails.
* @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a needed authorization callback
is not defined.
* @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
* this function needs to be called again.
* @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
* @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
*/
TSS2_RC
ifapi_authorize_object(FAPI_CONTEXT *context, IFAPI_OBJECT *object, ESYS_TR *session)
{
TSS2_RC r;
TPMI_YES_NO auth_required;
LOG_DEBUG("Authorize object: %x", object->handle);
switch (object->authorization_state) {
statecase(object->authorization_state, AUTH_INIT)
LOG_TRACE("**STATE** AUTH_INIT");
if (!policy_digest_size(object)) {
/* No policy used authorization callbacks have to be called if necessary. */
if (object_with_auth(object)) {
/* Check whether hierarchy was already authorized. */
if (object->objectType != IFAPI_HIERARCHY_OBJ ||
!object->misc.hierarchy.authorized) {
char *description = NULL;
r = ifapi_get_description(object, &description);
return_if_error(r, "Get description");
r = ifapi_set_auth(context, object, description);
SAFE_FREE(description);
return_if_error(r, "Set auth value");
}
}
/* No policy session needed current fapi session can be used */
if (context->session1 && context->session1 != ESYS_TR_NONE)
*session = context->session1;
else
/* Use password session if session1 had not been created */
*session = ESYS_TR_PASSWORD;
break;
}
/* Save current object to be authorized in context. */
context->current_auth_object = object;
r = ifapi_policyutil_execute_prepare(context, get_name_alg(context, object),
object->policy);
return_if_error(r, "Prepare policy execution.");
/* Next state will switch from prev context to next context. */
context->policy.util_current_policy = context->policy.util_current_policy->prev;
object->authorization_state = AUTH_EXEC_POLICY;
fallthrough;
statecase(object->authorization_state, AUTH_EXEC_POLICY)
*session = ESYS_TR_NONE;
r = ifapi_policyutil_execute(context, session);
if (r == TSS2_FAPI_RC_TRY_AGAIN)
return r;
return_if_error(r, "Execute policy.");
r = Esys_TRSess_GetAuthRequired(context->esys, *session,
&auth_required);
return_if_error(r, "GetAuthRequired");
/* Check whether PolicyCommand requiring authorization was executed */
if (auth_required == TPM2_YES) {
char* description;
r = ifapi_get_description(object, &description);
return_if_error(r, "Get description");
r = ifapi_set_auth(context, object, description);
SAFE_FREE(description);
goto_if_error(r, "Set auth value", error);
}
/* Clear continue session flag, so policy session will be flushed after authorization */
r = Esys_TRSess_SetAttributes(context->esys, *session, 0, TPMA_SESSION_CONTINUESESSION);
goto_if_error(r, "Esys_TRSess_SetAttributes", error);
break;
general_failure(object->authorization_state)
}
object->authorization_state = AUTH_INIT;
return TSS2_RC_SUCCESS;
error:
/* No policy call was executed session can be flushed */
Esys_FlushContext(context->esys, *session);
return r;
}
/** State machine to write data to the NV ram of the TPM.
*
* The NV object will be read from object store and the data will be
* written by one, or more than one if necessary, ESAPI calls to the NV ram of
* the TPM.
* The sub context nv_cmd will be prepared:
* - data The buffer for the data which has to be written
* - offset The current offset for writing
* - numBytes The number of bytes which have to be written.
*
* @param[in,out] context for storing all state information.
* @param[in] nvPath The fapi path of the NV object.
* @param[in] param_offset The offset in the NV memory (will be stored in context).
* @param[in] data The pointer to the data to be written.
* @param[in] size The number of bytes to be written.
*
* @retval TSS2_RC_SUCCESS If data can be written.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
* @retval TSS2_FAPI_RC_BAD_VALUE If wrong values are detected during execution.
* @retval TSS2_FAPI_RC_GENERAL_FAILURE If an internal error occurs, which is
+ not covered by other return codes.
* @retval TSS2_FAPI_RC_IO_ERROR If an error occurs during access to the object
* store.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND The nv object or an object needed for
* authentication was not found.
* @retval TSS2_FAPI_RC_POLICY_UNKNOWN If policy search for a certain policy digest was
* not successful.
* @retval TPM2_RC_BAD_AUTH If the authentication for an object needed for the
* command execution fails.
* @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a needed authorization callback
* is not defined.
* @retval TSS2_FAPI_RC_BAD_PATH if a path is used in inappropriate context
* or contains illegal characters.
* @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
* this function needs to be called again.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
* operation already pending.
* @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
* @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
* @retval TSS2_FAPI_RC_NOT_PROVISIONED FAPI was not provisioned.
*/
TSS2_RC
ifapi_nv_write(
FAPI_CONTEXT *context,
char *nvPath,
size_t param_offset,
uint8_t const *data,
size_t size)
{
TSS2_RC r = TSS2_RC_SUCCESS;
ESYS_TR auth_index;
ESYS_TR nv_index = context->nv_cmd.esys_handle;
IFAPI_OBJECT *object = &context->nv_cmd.nv_object;
IFAPI_OBJECT *auth_object = &context->nv_cmd.auth_object;
TPM2B_MAX_NV_BUFFER *aux_data = (TPM2B_MAX_NV_BUFFER *)&context->aux_data;
char *nv_file_name = NULL;
ESYS_TR auth_session;
switch (context->nv_cmd.nv_write_state) {
statecase(context->nv_cmd.nv_write_state, NV2_WRITE_INIT);
memset(&context->nv_cmd.nv_object, 0, sizeof(IFAPI_OBJECT));
context->nv_cmd.nvPath = nvPath;
context->nv_cmd.offset = param_offset;
context->nv_cmd.numBytes = size;
context->nv_cmd.data = data;
if (context->nv_cmd.numBytes > context->nv_buffer_max)
aux_data->size = context->nv_buffer_max;
else
aux_data->size = context->nv_cmd.numBytes;
context->nv_cmd.data_idx = 0;
/* Use calloc to ensure zero padding for write buffer. */
context->nv_cmd.write_data = calloc(size, 1);
goto_if_null2(context->nv_cmd.write_data, "Out of memory.", r,
TSS2_FAPI_RC_MEMORY,
error_cleanup);
memcpy(context->nv_cmd.write_data, data, size);
memcpy(&aux_data->buffer[0], &context->nv_cmd.data[0], aux_data->size);
/* Prepare reading of the key from keystore. */
r = ifapi_keystore_load_async(&context->keystore, &context->io,
context->nv_cmd.nvPath);
return_if_error2(r, "Could not open: %s", context->nv_cmd.nvPath);
fallthrough;
statecase(context->nv_cmd.nv_write_state, NV2_WRITE_READ);
r = ifapi_keystore_load_finish(&context->keystore, &context->io, object);
return_try_again(r);
return_if_error(r, "read_finish failed");
if (object->objectType != IFAPI_NV_OBJ)
goto_error(r, TSS2_FAPI_RC_BAD_PATH, "%s is no NV object.", error_cleanup,
context->nv_cmd.nvPath);
r = ifapi_initialize_object(context->esys, object);
goto_if_error_reset_state(r, "Initialize NV object", error_cleanup);
/* Store object info in context */
nv_index = context->nv_cmd.nv_object.handle;
context->nv_cmd.esys_handle = nv_index;
context->nv_cmd.nv_obj = object->misc.nv;
/* Determine the object which will be uses for authorization. */
if (object->misc.nv.public.nvPublic.attributes & TPMA_NV_PPWRITE) {
ifapi_init_hierarchy_object(auth_object, ESYS_TR_RH_PLATFORM);
auth_index = ESYS_TR_RH_PLATFORM;
} else {
if (object->misc.nv.public.nvPublic.attributes & TPMA_NV_OWNERWRITE) {
ifapi_init_hierarchy_object(auth_object, ESYS_TR_RH_OWNER);
auth_index = ESYS_TR_RH_OWNER;
} else {
auth_index = nv_index;
}
*auth_object = *object;
}
context->nv_cmd.auth_index = auth_index;
/* Get A session for authorizing the NV write operation. */
r = ifapi_get_sessions_async(context, IFAPI_SESSION_GENEK | IFAPI_SESSION1,
TPMA_SESSION_DECRYPT, 0);
goto_if_error(r, "Create sessions", error_cleanup);
fallthrough;
statecase(context->nv_cmd.nv_write_state, NV2_WRITE_WAIT_FOR_SESSSION);
r = ifapi_get_sessions_finish(context, &context->profiles.default_profile,
object->misc.nv.public.nvPublic.nameAlg);
return_try_again(r);
goto_if_error_reset_state(r, " FAPI create session", error_cleanup);
fallthrough;
statecase(context->nv_cmd.nv_write_state, NV2_WRITE_AUTHORIZE);
r = ifapi_authorize_object(context, auth_object, &auth_session);
FAPI_SYNC(r, "Authorize NV object.", error_cleanup);
/* Prepare the writing to NV ram. */
r = Esys_NV_Write_Async(context->esys,
context->nv_cmd.auth_index,
nv_index,
auth_session,
context->session2,
ESYS_TR_NONE,
aux_data,
context->nv_cmd.data_idx);
goto_if_error_reset_state(r, " Fapi_NvWrite_Async", error_cleanup);
if (!(object->misc.nv.public.nvPublic.attributes & TPMA_NV_NO_DA))
context->nv_cmd.nv_write_state = NV2_WRITE_AUTH_SENT;
else
context->nv_cmd.nv_write_state = NV2_WRITE_NULL_AUTH_SENT;
context->nv_cmd.bytesRequested = aux_data->size;
fallthrough;
case NV2_WRITE_AUTH_SENT:
case NV2_WRITE_NULL_AUTH_SENT:
r = Esys_NV_Write_Finish(context->esys);
return_try_again(r);
if (number_rc(r) == TPM2_RC_BAD_AUTH) {
if (context->nv_cmd.nv_write_state == NV2_WRITE_NULL_AUTH_SENT) {
IFAPI_OBJECT *auth_object = &context->nv_cmd.auth_object;
char *description;
r = ifapi_get_description(auth_object, &description);
return_if_error(r, "Get description");
r = ifapi_set_auth(context, auth_object, description);
SAFE_FREE(description);
goto_if_error_reset_state(r, " Fapi_NvWrite_Finish", error_cleanup);
/* Prepare the writing to NV ram. */
r = Esys_NV_Write_Async(context->esys,
context->nv_cmd.auth_index,
nv_index,
(!context->policy.session
|| context->policy.session == ESYS_TR_NONE) ? context->session1 :
context->policy.session,
context->session2,
ESYS_TR_NONE,
aux_data,
context->nv_cmd.data_idx);
goto_if_error_reset_state(r, "FAPI NV_Write_Async", error_cleanup);
context->nv_cmd.nv_write_state = NV2_WRITE_AUTH_SENT;
return TSS2_FAPI_RC_TRY_AGAIN;
}
}
goto_if_error_reset_state(r, "FAPI NV_Write_Finish", error_cleanup);
context->nv_cmd.numBytes -= context->nv_cmd.bytesRequested;
if (context->nv_cmd.numBytes > 0) {
/* Increment data idx with number of transmitted bytes. */
context->nv_cmd.data_idx += aux_data->size;
if (context->nv_cmd.numBytes > context->nv_buffer_max)
aux_data->size = context->nv_buffer_max;
else
aux_data->size = context->nv_cmd.numBytes;
memcpy(&aux_data->buffer[0],
&context->nv_cmd.write_data[context->nv_cmd.data_idx],
aux_data->size);
statecase(context->nv_cmd.nv_write_state, NV2_WRITE_AUTHORIZE2);
r = ifapi_authorize_object(context, auth_object, &auth_session);
FAPI_SYNC(r, "Authorize NV object.", error_cleanup);
/* Prepare the writing to NV ram */
r = Esys_NV_Write_Async(context->esys,
context->nv_cmd.auth_index,
nv_index,
auth_session,
context->session2,
ESYS_TR_NONE,
aux_data,
context->nv_cmd.data_idx);
goto_if_error_reset_state(r, "FAPI NV_Write", error_cleanup);
context->nv_cmd.bytesRequested = aux_data->size;
context->nv_cmd.nv_write_state = NV2_WRITE_AUTH_SENT;
return TSS2_FAPI_RC_TRY_AGAIN;
}
fallthrough;
statecase(context->nv_cmd.nv_write_state, NV2_WRITE_WRITE_PREPARE);
/* Set written bit in keystore */
context->nv_cmd.nv_object.misc.nv.public.nvPublic.attributes |= TPMA_NV_WRITTEN;
/* Perform esys serialization if necessary */
r = ifapi_esys_serialize_object(context->esys, &context->nv_cmd.nv_object);
goto_if_error(r, "Prepare serialization", error_cleanup);
/* Start writing the NV object to the key store */
r = ifapi_keystore_store_async(&context->keystore, &context->io,
context->nv_cmd.nvPath,
&context->nv_cmd.nv_object);
goto_if_error_reset_state(r, "Could not open: %s", error_cleanup,
context->nv_cmd.nvPath);
context->nv_cmd.nv_write_state = NV2_WRITE_WRITE;
fallthrough;
statecase(context->nv_cmd.nv_write_state, NV2_WRITE_WRITE);
/* Finish writing the NV object to the key store */
r = ifapi_keystore_store_finish(&context->keystore, &context->io);
return_try_again(r);
return_if_error_reset_state(r, "write_finish failed");
LOG_DEBUG("success");
r = TSS2_RC_SUCCESS;
context->nv_cmd.nv_write_state = NV2_WRITE_INIT;
break;
statecasedefault(context->nv_cmd.nv_write_state);
}
error_cleanup:
SAFE_FREE(nv_file_name);
SAFE_FREE(context->nv_cmd.write_data);
return r;
}
/** State machine to read data from the NV ram of the TPM.
*
* Context nv_cmd has to be prepared before the call of this function:
* - auth_index The ESAPI handle of the authorization object.
* - numBytes The number of bytes which should be read.
* - esys_handle The ESAPI handle of the NV object.
*
* @param[in,out] context for storing all state information.
* @param[out] data the data fetched from TPM.
* @param[in,out] size The number of bytes requested and fetched.
*
* @retval TSS2_RC_SUCCESS If the data was read successfully.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
* @retval TSS2_FAPI_RC_BAD_VALUE If wrong values are detected during execution.
* @retval TSS2_FAPI_RC_GENERAL_FAILURE If an internal error occurs, which is
+ not covered by other return codes.
* @retval TSS2_FAPI_RC_IO_ERROR If an error occurs during access to the object
* store.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND If a policy for a certain path was not found.
* @retval TSS2_FAPI_RC_POLICY_UNKNOWN If policy search for a certain policy digest was
* not successful.
* @retval TPM2_RC_BAD_AUTH If the authentication for an object needed for the
* execution fails.
* @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a needed authorization callback
* is not defined.
* @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
* this function needs to be called again.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
* operation already pending.
* @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
* @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
*/
TSS2_RC
ifapi_nv_read(
FAPI_CONTEXT *context,
uint8_t **data,
size_t *size)
{
TSS2_RC r;
UINT16 aux_size;
TPM2B_MAX_NV_BUFFER *aux_data;
UINT16 bytesRequested = context->nv_cmd.bytesRequested;
size_t *numBytes = &context->nv_cmd.numBytes;
ESYS_TR nv_index = context->nv_cmd.esys_handle;
IFAPI_OBJECT *auth_object = &context->nv_cmd.auth_object;
ESYS_TR session;
switch (context->nv_cmd.nv_read_state) {
statecase(context->nv_cmd.nv_read_state, NV_READ_INIT);
LOG_TRACE("NV_READ_INIT");
context->nv_cmd.rdata = NULL;
fallthrough;
statecase(context->nv_cmd.nv_read_state, NV_READ_AUTHORIZE);
LOG_TRACE("NV_READ_AUTHORIZE");
r = ifapi_authorize_object(context, auth_object, &session);
FAPI_SYNC(r, "Authorize NV object.", error_cleanup);
if (*numBytes > context->nv_buffer_max)
aux_size = context->nv_buffer_max;
else
aux_size = *numBytes;
/* Prepare the reading from NV ram. */
r = Esys_NV_Read_Async(context->esys,
context->nv_cmd.auth_index,
nv_index,
session,
ESYS_TR_NONE,
ESYS_TR_NONE,
aux_size,
0);
goto_if_error_reset_state(r, " Fapi_NvRead_Async", error_cleanup);
context->nv_cmd.nv_read_state = NV_READ_AUTH_SENT;
context->nv_cmd.bytesRequested = aux_size;
return TSS2_FAPI_RC_TRY_AGAIN;
statecase(context->nv_cmd.nv_read_state, NV_READ_AUTH_SENT);
LOG_TRACE("NV_READ_NULL_AUTH_SENT");
if (context->nv_cmd.rdata == NULL) {
/* Allocate the data buffer if not already initialized. */
LOG_TRACE("Allocate %zu bytes", *numBytes);
context->nv_cmd.rdata = malloc(*numBytes);
}
*data = context->nv_cmd.rdata;
goto_if_null(*data, "Malloc failed", TSS2_FAPI_RC_MEMORY, error_cleanup);
r = Esys_NV_Read_Finish(context->esys, &aux_data);
if (base_rc(r) == TSS2_BASE_RC_TRY_AGAIN)
return TSS2_FAPI_RC_TRY_AGAIN;
if (context->nv_cmd.auth_index == ESYS_TR_RH_OWNER &&
number_rc(r) == TPM2_RC_BAD_AUTH &&
auth_object->misc.hierarchy.with_auth == TPM2_NO) {
/* NULL auth failed, password was used for owner hierarchy, try again. */
auth_object->misc.hierarchy.with_auth = TPM2_YES;
context->nv_cmd.nv_read_state = NV_READ_AUTHORIZE;
return TSS2_FAPI_RC_TRY_AGAIN;
}
goto_if_error_reset_state(r, "FAPI NV_Read_Finish", error_cleanup);
if (aux_data->size < bytesRequested)
*numBytes = 0;
else
*numBytes -= aux_data->size;
memcpy(*data + context->nv_cmd.data_idx, &aux_data->buffer[0],
aux_data->size);
context->nv_cmd.data_idx += aux_data->size;
free(aux_data);
if (*numBytes > 0) {
statecase(context->nv_cmd.nv_read_state, NV_READ_AUTHORIZE2);
r = ifapi_authorize_object(context, auth_object, &session);
FAPI_SYNC(r, "Authorize NV object.", error_cleanup);
/* The reading of the NV data is not completed. The next
reading will be prepared. */
if (*numBytes > context->nv_buffer_max)
aux_size = context->nv_buffer_max;
else
aux_size = *numBytes;
r = Esys_NV_Read_Async(context->esys,
context->nv_cmd.auth_index,
nv_index,
session,
ESYS_TR_NONE,
ESYS_TR_NONE,
aux_size,
context->nv_cmd.data_idx);
goto_if_error_reset_state(r, "FAPI NV_Read", error_cleanup);
context->nv_cmd.bytesRequested = aux_size;
context->nv_cmd.nv_read_state = NV_READ_AUTH_SENT;
return TSS2_FAPI_RC_TRY_AGAIN;
} else {
*size = context->nv_cmd.data_idx;
context->nv_cmd.nv_read_state = NV_READ_INIT;
LOG_DEBUG("success");
r = TSS2_RC_SUCCESS;
break;
}
statecasedefault(context->nv_cmd.nv_read_state);
}
error_cleanup:
return r;
}
#define min(X,Y) (X>Y)?Y:X
/** State machine to retrieve random data from TPM.
*
* If the buffer size exceeds the maximum size, several ESAPI calls are made.
*
* @param[in,out] context for storing all state information.
* @param[in] numBytes Number of random bytes to be computed.
* @param[out] data The random data.
*
* @retval TSS2_RC_SUCCESS If random data can be computed.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
* @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
* the function.
* @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
* this function needs to be called again.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
* operation already pending.
*/
TSS2_RC
ifapi_get_random(FAPI_CONTEXT *context, size_t numBytes, uint8_t **data)
{
TSS2_RC r;
TPM2B_DIGEST *aux_data = NULL;
switch (context->get_random_state) {
statecase(context->get_random_state, GET_RANDOM_INIT);
context->get_random.numBytes = numBytes;
context->get_random.data = calloc(context->get_random.numBytes, 1);
context->get_random.idx = 0;
return_if_null(context->get_random.data, "FAPI out of memory.",
TSS2_FAPI_RC_MEMORY);
/* Prepare the creation of random data. */
r = Esys_GetRandom_Async(context->esys,
context->session1,
ESYS_TR_NONE, ESYS_TR_NONE,
min(context->get_random.numBytes, sizeof(TPMU_HA)));
goto_if_error_reset_state(r, "FAPI GetRandom", error_cleanup);
fallthrough;
statecase(context->get_random_state, GET_RANDOM_SENT);
r = Esys_GetRandom_Finish(context->esys, &aux_data);
return_try_again(r);
goto_if_error_reset_state(r, "FAPI GetRandom_Finish", error_cleanup);
if (aux_data -> size > context->get_random.numBytes) {
goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "TPM returned too many bytes",
error_cleanup);
}
/* Save created random data. */
memcpy(context->get_random.data + context->get_random.idx, &aux_data->buffer[0],
aux_data->size);
context->get_random.numBytes -= aux_data->size;
context->get_random.idx += aux_data->size;
Esys_Free(aux_data);
aux_data = NULL;
if (context->get_random.numBytes > 0) {
/* Continue creaion of random data if needed. */
r = Esys_GetRandom_Async(context->esys, context->session1, ESYS_TR_NONE,
ESYS_TR_NONE, min(context->get_random.numBytes, sizeof(TPMU_HA)));
goto_if_error_reset_state(r, "FAPI GetRandom", error_cleanup);
return TSS2_FAPI_RC_TRY_AGAIN;
}
break;
statecasedefault(context->get_random_state);
}
*data = context->get_random.data;
LOG_DEBUG("success");
context->get_random_state = GET_RANDOM_INIT;
return TSS2_RC_SUCCESS;
error_cleanup:
if (aux_data)
Esys_Free(aux_data);
context->get_random_state = GET_RANDOM_INIT;
if (context->get_random.data != NULL)
SAFE_FREE(context->get_random.data);
return r;
}
/** Load a key and initialize profile and session for ESAPI execution.
*
* This state machine prepares the session for key loading. Some
* session related parameters will be taken from profile.
*
* @param[in,out] context The FAPI_CONTEXT.
* @param[in] keyPath the key path without the parent directories
* of the key store. (e.g. HE/EK, HS/SRK/mykey)
* @param[out] key_object The callee allocated internal representation
* of a key object.
*
* @retval TSS2_RC_SUCCESS If the key was loaded successfully.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
* @retval TSS2_FAPI_RC_GENERAL_FAILURE If an internal error occurs, which is
* not covered by other return codes.
* @retval TSS2_FAPI_RC_BAD_VALUE If wrong values are detected during execution.
* @retval TSS2_FAPI_RC_IO_ERROR If an error occurs during access to the object
* store.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND If a policy or key was not found.
* @retval TSS2_FAPI_RC_POLICY_UNKNOWN If policy search for a certain policy digest was
* not successful.
* @retval TPM2_RC_BAD_AUTH If the authentication for an object needed for policy
* execution fails.
* @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a needed authorization callback
is not defined.
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
* @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
* this function needs to be called again.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
* operation already pending.
* @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
* @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
* @retval TSS2_FAPI_RC_NOT_PROVISIONED FAPI was not provisioned.
* @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context
* or contains illegal characters.
*/
TSS2_RC
ifapi_load_key(
FAPI_CONTEXT *context,
char const *keyPath,
IFAPI_OBJECT **key_object)
{
TSS2_RC r;
const IFAPI_PROFILE *profile;
return_if_null(keyPath, "Bad reference for key path.",
TSS2_FAPI_RC_BAD_REFERENCE);
switch (context->Key_Sign.state) {
statecase(context->Key_Sign.state, SIGN_INIT);
context->Key_Sign.keyPath = keyPath;
/* Prepare the session creation. */
r = ifapi_get_sessions_async(context,
IFAPI_SESSION_GENEK | IFAPI_SESSION1,
TPMA_SESSION_DECRYPT, 0);
goto_if_error_reset_state(r, "Create sessions", error_cleanup);
fallthrough;
statecase(context->Key_Sign.state, SIGN_WAIT_FOR_SESSION);
r = ifapi_profiles_get(&context->profiles, context->Key_Sign.keyPath, &profile);
goto_if_error_reset_state(r, "Reading profile data", error_cleanup);
r = ifapi_get_sessions_finish(context, profile, profile->nameAlg);
return_try_again(r);
goto_if_error_reset_state(r, " FAPI create session", error_cleanup);
/* Prepare the key loading. */
r = ifapi_load_keys_async(context, context->Key_Sign.keyPath);
goto_if_error(r, "Load keys.", error_cleanup);
fallthrough;
statecase(context->Key_Sign.state, SIGN_WAIT_FOR_KEY);
r = ifapi_load_keys_finish(context, IFAPI_FLUSH_PARENT,
&context->Key_Sign.handle,
key_object);
return_try_again(r);
goto_if_error_reset_state(r, " Load key.", error_cleanup);
context->Key_Sign.state = SIGN_INIT;
break;
statecasedefault(context->Key_Sign.state);
}
error_cleanup:
return r;
}
/** State machine for signing operation.
*
* The key used for signing will be authorized and the signing of the passed data
* will be executed.
*
* @param[in,out] context The FAPI_CONTEXT.
* @param[in] sig_key_object The Fapi key object which will be used to
* sign the passed digest.
* @param[in] padding is the padding algorithm used. Possible values are RSA_SSA,
* RSA_PPSS (case insensitive). padding MAY be NULL.
* @param[in] digest is the data to be signed, already hashed.
* digest MUST NOT be NULL.
* @param[out] tpm_signature returns the signature in binary form (DER format).
* tpm_signature MUST NOT be NULL (callee-allocated).
* @param[out] publicKey is the public key of the signing key in PEM format.
* publicKey is callee allocated and MAY be NULL.
* @param[out] certificate is the certificate associated with the signing key
* in PEM format. certificate MAY be NULL.
*
* @retval TSS2_RC_SUCCESS If the signing was successful.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
* @retval TSS2_FAPI_RC_GENERAL_FAILURE If an internal error occurs, which is
* not covered by other return codes.
* @retval TSS2_FAPI_RC_BAD_VALUE If wrong values are detected during execution.
* @retval TSS2_FAPI_RC_IO_ERROR If an error occurs during access to the policy
* store.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND If a policy for a certain path was not found.
* @retval TSS2_FAPI_RC_POLICY_UNKNOWN If policy search for a certain policy digest was
* not successful.
* @retval TSS2_FAPI_RC_BAD_TEMPLATE In a invalid policy is loaded during execution.
* @retval TPM2_RC_BAD_AUTH If the authentication for an object needed for policy
* execution fails.
* @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a needed authorization callback
* is not defined.
* @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
* this function needs to be called again.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
* operation already pending.
* @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
* @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
*/
TSS2_RC
ifapi_key_sign(
FAPI_CONTEXT *context,
IFAPI_OBJECT *sig_key_object,
char const *padding,
TPM2B_DIGEST *digest,
TPMT_SIGNATURE **tpm_signature,
char **publicKey,
char **certificate)
{
TSS2_RC r;
TPMT_SIG_SCHEME sig_scheme;
ESYS_TR session;
TPMT_TK_HASHCHECK hash_validation = {
.tag = TPM2_ST_HASHCHECK,
.hierarchy = TPM2_RH_OWNER,
};
memset(&hash_validation.digest, 0, sizeof(TPM2B_DIGEST));
switch (context->Key_Sign.state) {
statecase(context->Key_Sign.state, SIGN_INIT);
sig_key_object = context->Key_Sign.key_object;
r = ifapi_authorize_object(context, sig_key_object, &session);
FAPI_SYNC(r, "Authorize signature key.", cleanup);
context->policy.session = session;
r = ifapi_get_sig_scheme(context, sig_key_object, padding, digest, &sig_scheme);
goto_if_error(r, "Get signature scheme", cleanup);
/* Prepare the signing operation. */
r = Esys_Sign_Async(context->esys,
context->Key_Sign.handle,
session,
ESYS_TR_NONE, ESYS_TR_NONE,
digest,
&sig_scheme,
&hash_validation);
goto_if_error(r, "Error: Sign", cleanup);
fallthrough;
statecase(context->Key_Sign.state, SIGN_AUTH_SENT);
context->Key_Sign.signature = NULL;
r = Esys_Sign_Finish(context->esys,
&context->Key_Sign.signature);
return_try_again(r);
ifapi_flush_policy_session(context, context->policy.session, r);
goto_if_error(r, "Error: Sign", cleanup);
/* Prepare the flushing of the signing key. */
if (!sig_key_object->misc.key.persistent_handle) {
r = Esys_FlushContext_Async(context->esys, context->Key_Sign.handle);
goto_if_error(r, "Error: FlushContext", cleanup);
}
fallthrough;
statecase(context->Key_Sign.state, SIGN_WAIT_FOR_FLUSH);
if (!sig_key_object->misc.key.persistent_handle) {
r = Esys_FlushContext_Finish(context->esys);
return_try_again(r);
goto_if_error(r, "Error: Sign", cleanup);
}
int pem_size;
if (publicKey) {
/* Convert internal key object to PEM format. */
r = ifapi_pub_pem_key_from_tpm(&sig_key_object->misc.key.public,
publicKey,
&pem_size);
goto_if_error(r, "Conversion pub key to PEM failed", cleanup);
}
context->Key_Sign.handle = ESYS_TR_NONE;
*tpm_signature = context->Key_Sign.signature;
if (certificate) {
if (context->Key_Sign.key_object->misc.key.certificate) {
*certificate = strdup(context->Key_Sign.key_object->misc.key.certificate);
goto_if_null(*certificate, "Out of memory.",
TSS2_FAPI_RC_MEMORY, cleanup);
} else {
strdup_check(*certificate, "", r, cleanup);
}
}
context->Key_Sign.state = SIGN_INIT;
LOG_TRACE("success");
r = TSS2_RC_SUCCESS;
break;
statecasedefault(context->Key_Sign.state);
}
cleanup:
if (context->Key_Sign.handle != ESYS_TR_NONE)
Esys_FlushContext(context->esys, context->Key_Sign.handle);
ifapi_cleanup_ifapi_object(context->Key_Sign.key_object);
return r;
}
/** Get json encoding for FAPI object.
*
* A json representation which can be used for exporting of a FAPI object will
* be created.
*
* @param[in] context The FAPI_CONTEXT.
* @param[in] object The object to be serialized.
* @param[out] json_string The json string created by the deserialzation
* function (callee-allocated).
*
* @retval TSS2_RC_SUCCESS If the serialization was successful.
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
* @retval TSS2_FAPI_RC_BAD_VALUE If wrong values are detected during
* serialization.
* @retval TSS2_FAPI_RC_BAD_REFERENCE If a NULL pointer was passed for
* the object.
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
*/
TSS2_RC
ifapi_get_json(FAPI_CONTEXT *context, IFAPI_OBJECT *object, char **json_string)
{
TSS2_RC r = TSS2_RC_SUCCESS;
json_object *jso = NULL;
/* Perform esys serialization if necessary */
r = ifapi_esys_serialize_object(context->esys, object);
goto_if_error(r, "Prepare serialization", cleanup);
r = ifapi_json_IFAPI_OBJECT_serialize(object, &jso);
return_if_error(r, "Serialize duplication object");
*json_string = strdup(json_object_to_json_string_ext(jso,
JSON_C_TO_STRING_PRETTY));
goto_if_null2(*json_string, "Converting json to string", r, TSS2_FAPI_RC_MEMORY,
cleanup);
cleanup:
if (jso)
json_object_put(jso);
return r;
}
/** Serialize persistent objects into buffer of keystore object.
*
* NV objects and persistent keys will serialized via the ESYS API to
* enable reconstruction durinng loading from keystore.
*
* @param[in] ectx The ESAPI context.
* @param[in,out] object The nv object or the key.
* @retval TSS2_RC_SUCCESS if the function call was a success.
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occured.
*/
TSS2_RC
ifapi_esys_serialize_object(ESYS_CONTEXT *ectx, IFAPI_OBJECT *object)
{
TSS2_RC r = TSS2_RC_SUCCESS;
IFAPI_KEY *key_object = NULL;
IFAPI_NV *nv_object;
switch (object->objectType) {
case IFAPI_NV_OBJ:
nv_object = &object->misc.nv;
if (nv_object->serialization.buffer != NULL) {
/* Cleanup old buffer */
Fapi_Free(nv_object->serialization.buffer);
nv_object->serialization.buffer = NULL;
}
r = Esys_TR_Serialize(ectx, object->handle,
&nv_object->serialization.buffer,
&nv_object->serialization.size);
return_if_error(r, "Error serialize esys object");
break;
case IFAPI_KEY_OBJ:
key_object = &object->misc.key;
key_object->serialization.size = 0;
if (key_object->serialization.buffer != NULL) {
/* Cleanup old buffer */
Fapi_Free(key_object->serialization.buffer);
key_object->serialization.buffer = NULL;
}
if (object->handle != ESYS_TR_NONE && key_object->persistent_handle) {
key_object->serialization.buffer = NULL;
r = Esys_TR_Serialize(ectx, object->handle,
&key_object->serialization.buffer,
&key_object->serialization.size);
return_if_error(r, "Error serialize esys object");
}
break;
default:
/* Nothing to be done */
break;
}
return TSS2_RC_SUCCESS;
}
/** Initialize the part of an IFAPI_OBJECT which is not serialized.
*
* For persistent objects the correspodning ESYS object will be created.
*
* @param[in,out] ectx The ESYS context.
* @param[out] object the deserialzed binary object.
* @retval TSS2_RC_SUCCESS if the function call was a success.
* @retval TSS2_FAPI_RC_BAD_VALUE if the json object can't be deserialized.
*/
TSS2_RC
ifapi_initialize_object(
ESYS_CONTEXT *ectx,
IFAPI_OBJECT *object)
{
TSS2_RC r = TSS2_RC_SUCCESS;
ESYS_TR handle;
switch (object->objectType) {
case IFAPI_NV_OBJ:
if (object->misc.nv.serialization.size > 0) {
r = Esys_TR_Deserialize(ectx, &object->misc.nv.serialization.buffer[0],
object->misc.nv.serialization.size, &handle);
goto_if_error(r, "Error dserialize esys object", cleanup);
} else {
handle = ESYS_TR_NONE;
}
object->authorization_state = AUTH_INIT;
object->handle = handle;
break;
case IFAPI_KEY_OBJ:
if (object->misc.key.serialization.size > 0) {
r = Esys_TR_Deserialize(ectx, &object->misc.key.serialization.buffer[0],
object->misc.key.serialization.size, &handle);
goto_if_error(r, "Error deserialize esys object", cleanup);
} else {
handle = ESYS_TR_NONE;
}
object->authorization_state = AUTH_INIT;
object->handle = handle;
break;
default:
object->authorization_state = AUTH_INIT;
break;
}
return r;
cleanup:
SAFE_FREE(object->policy);
return r;
}
/** Prepare key creation with an auth value.
*
* The auth value will be copied int the FAPI context for later use in key creation.
*
* @param[in,out] context The FAPI_CONTEXT.
* @param[in] keyPath the key path without the parent directories
* of the key store. (e.g. HE/EK, HS/SRK/mykey)
* @param[in] policyPath identifies the policy to be associated with the new key.
* policyPath MAY be NULL. If policyPath is NULL then no policy will
* be associated with the key.
* @param[in] authValue The authentication value of the key.
*
* @retval TSS2_RC_SUCCESS If the preparation was successful.
* @retval TSS2_FAPI_RC_PATH_ALREADY_EXISTS If the object with does already exist in
* keystore.
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
* @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
* the function.
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
* @retval TSS2_FAPI_RC_NO_TPM if FAPI was initialized in no-TPM-mode via its
* config file.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
* operation already pending.
* @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context
* or contains illegal characters.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
* during authorization.
*/
TSS2_RC
ifapi_key_create_prepare_auth(
FAPI_CONTEXT *context,
char const *keyPath,
char const *policyPath,
char const *authValue)
{
TSS2_RC r;
memset(&context->cmd.Key_Create.inSensitive, 0, sizeof(TPM2B_SENSITIVE_CREATE));
if (authValue) {
/* Copy the auth value */
if (strlen(authValue) > sizeof(TPMU_HA)) {
return_error(TSS2_FAPI_RC_BAD_VALUE, "Password too long.");
}
memcpy(&context->cmd.Key_Create.inSensitive.sensitive.userAuth.buffer,
authValue, strlen(authValue));
context->cmd.Key_Create.inSensitive.sensitive.userAuth.size = strlen(authValue);
}
context->cmd.Key_Create.gen_sensitive_random = false;
context->cmd.Key_Create.inSensitive.sensitive.data.size = 0;
r = ifapi_key_create_prepare(context, keyPath, policyPath);
return r;
}
/** Prepare key creation with an auth value and sensitive data.
*
* The auth value and the sensitive data will be copied int the FAPI context
* for later use in key creation.
*
* @param[in,out] context The FAPI_CONTEXT.
* @param[in] keyPath the key path without the parent directories
* of the key store. (e.g. HE/EK, HS/SRK/mykey)
* @param[in] policyPath identifies the policy to be associated with the new key.
* policyPath MAY be NULL. If policyPath is NULL then no policy will
* be associated with the key.
* @param[in] dataSize The size of the sensitive data.
* @param[in] authValue The authentication value of the key.
* @param[in] data The sensitive data.
*
* @retval TSS2_RC_SUCCESS If the preparation was successful.
* @retval TSS2_FAPI_RC_PATH_ALREADY_EXISTS If the object with does already exist in
* keystore.
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
* @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
* the function.
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
* @retval TSS2_FAPI_RC_NO_TPM if FAPI was initialized in no-TPM-mode via its
* config file.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
* operation already pending.
* @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context
* or contains illegal characters.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
* during authorization.
*/
TSS2_RC
ifapi_key_create_prepare_sensitive(
FAPI_CONTEXT *context,
char const *keyPath,
char const *policyPath,
size_t dataSize,
char const *authValue,
uint8_t const *data)
{
TSS2_RC r;
memset(&context->cmd.Key_Create.inSensitive, 0, sizeof(TPM2B_SENSITIVE_CREATE));
if (dataSize > sizeof(context->cmd.Key_Create.inSensitive.sensitive.data.buffer)
|| dataSize == 0) {
return_error(TSS2_FAPI_RC_BAD_VALUE, "Data too big or equal zero.");
}
if (data) {
/* Copy the sensitive data */
context->cmd.Key_Create.gen_sensitive_random = false;
memcpy(&context->cmd.Key_Create.inSensitive.sensitive.data.buffer,
data, dataSize);
} else {
context->cmd.Key_Create.gen_sensitive_random = true;
}
context->cmd.Key_Create.inSensitive.sensitive.data.size = dataSize;
if (authValue) {
/* Copy the auth value. */
if (strlen(authValue) > sizeof(TPMU_HA)) {
return_error(TSS2_FAPI_RC_BAD_VALUE, "Password too long.");
}
memcpy(&context->cmd.Key_Create.inSensitive.sensitive.userAuth.buffer,
authValue, strlen(authValue));
context->cmd.Key_Create.inSensitive.sensitive.userAuth.size = strlen(authValue);
}
r = ifapi_key_create_prepare(context, keyPath, policyPath);
return r;
}
/** Prepare key creation if possible.
*
* It will be checked whether the object already exists in key store and the FAPI context
* will be initialized appropriate for key creation.
*
* @param[in,out] context The FAPI_CONTEXT.
* @param[in] keyPath the key path without the parent directories
* of the key store. (e.g. HE/EK, HS/SRK/mykey)
* @param[in] policyPath identifies the policy to be associated with the new key.
* policyPath MAY be NULL. If policyPath is NULL then no policy will
* be associated with the key.
*
* @retval TSS2_RC_SUCCESS If the preparation was successful.
* @retval TSS2_FAPI_RC_PATH_ALREADY_EXISTS If the object with does already exist in
* keystore.
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
* @retval TSS2_FAPI_RC_NO_TPM if FAPI was initialized in no-TPM-mode via its
* config file.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
* operation already pending.
* @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
* the function.
* @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context
* or contains illegal characters.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
* during authorization.
*/
TSS2_RC
ifapi_key_create_prepare(
FAPI_CONTEXT *context,
char const *keyPath,
char const *policyPath)
{
TSS2_RC r;
IFAPI_OBJECT *object = &context->cmd.Key_Create.object;
NODE_STR_T *path_list = NULL;
LOG_TRACE("call");
r = ifapi_session_init(context);
return_if_error(r, "Initialize Key_Create");
/* First check whether an existing object would be overwritten */
r = ifapi_keystore_check_overwrite(&context->keystore, &context->io,
keyPath);
return_if_error2(r, "Check overwrite %s", keyPath);
context->srk_handle = 0;
/* Clear the memory used for the the new key object */
memset(&context->cmd.Key_Create.outsideInfo, 0, sizeof(TPM2B_DATA));
memset(&context->cmd.Key_Create.creationPCR, 0, sizeof(TPML_PCR_SELECTION));
memset(object, 0, sizeof(IFAPI_OBJECT));
strdup_check(context->cmd.Key_Create.policyPath, policyPath, r, error);
strdup_check(context->cmd.Key_Create.keyPath, keyPath, r, error);
r = get_explicit_key_path(&context->keystore, keyPath, &path_list);
return_if_error(r, "Compute explicit path.");
context->loadKey.path_list = path_list;
char *file;
r = ifapi_path_string(&file, NULL, path_list, NULL);
goto_if_error(r, "Compute explicit path.", error);
LOG_DEBUG("Explicit key path: %s", file);
free(file);
context->cmd.Key_Create.state = KEY_CREATE_INIT;
return TSS2_RC_SUCCESS;
error:
free_string_list(path_list);
return r;
}
/** State machine for key creation.
*
* The function for the preparation of the key have to called before the state machine can
* be activated. The linked list for the used directories must be available in the
* FAPI context.
* It will be checked whether the object already exists in key store and the FAPI context
* will be initialized appropriate for key creation.
*
* @param[in,out] context The FAPI_CONTEXT.
* @param[in] template The template which defines the key attributes and whether the
* key will be persistent.
*
* @retval TSS2_RC_SUCCESS If the key could be generated.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
* @retval TSS2_FAPI_RC_GENERAL_FAILURE If an internal error occurs, which is
* not covered by other return codes.
* @retval TSS2_FAPI_RC_BAD_VALUE If wrong values are detected during execution.
* @retval TSS2_FAPI_RC_IO_ERROR If an error occurs during access to the policy
* store.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND If an object needed for creation or
authentication was not found.
* @retval TSS2_FAPI_RC_POLICY_UNKNOWN If policy search for a certain policy digest was
* not successful.
* @retval TPM2_RC_BAD_AUTH If the authentication for an object needed for creation
* fails.
* @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a needed authorization callback
* is not defined.
* @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
* this function needs to be called again.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
* operation already pending.
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
* @retval TSS2_FAPI_RC_NOT_PROVISIONED FAPI was not provisioned.
* @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
* @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context
* or contains illegal characters.
* @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
* @retval TSS2_FAPI_RC_PATH_ALREADY_EXISTS if the object already exists in object store.
*/
TSS2_RC
ifapi_key_create(
FAPI_CONTEXT *context,
IFAPI_KEY_TEMPLATE *template)
{
TSS2_RC r;
size_t path_length;
NODE_STR_T *path_list = context->loadKey.path_list;
TPM2B_PUBLIC *outPublic = NULL;
TPM2B_PRIVATE *outPrivate = NULL;
TPM2B_CREATION_DATA *creationData = NULL;
TPM2B_DIGEST *creationHash = NULL;
TPMT_TK_CREATION *creationTicket = NULL;
IFAPI_OBJECT *object = &context->cmd.Key_Create.object;
IFAPI_OBJECT *hierarchy = &context->cmd.Key_Create.hierarchy;
ESYS_TR auth_session;
uint8_t *random_data = NULL;
LOG_TRACE("call");
switch (context->cmd.Key_Create.state) {
statecase(context->cmd.Key_Create.state, KEY_CREATE_INIT);
context->cmd.Key_Create.public_templ = *template;
context->loadKey.auth_object.handle = ESYS_TR_NONE;
/* Profile name is first element of the explicit path list */
char *profile_name = context->loadKey.path_list->str;
r = ifapi_profiles_get(&context->profiles, profile_name, &context->cmd.Key_Create.profile);
goto_if_error_reset_state(r, "Retrieving profile data", error_cleanup);
if (context->cmd.Key_Create.inSensitive.sensitive.data.size > 0) {
/* A keyed hash object sealing sensitive data will be created */
context->cmd.Key_Create.public_templ.public.publicArea.type = TPM2_ALG_KEYEDHASH;
context->cmd.Key_Create.public_templ.public.publicArea.nameAlg =
context->cmd.Key_Create.profile->nameAlg;
context->cmd.Key_Create.public_templ.public.publicArea.parameters.keyedHashDetail.scheme.scheme =
TPM2_ALG_NULL;
} else {
r = ifapi_merge_profile_into_template(context->cmd.Key_Create.profile,
&context->cmd.Key_Create.public_templ);
goto_if_error_reset_state(r, "Merge profile", error_cleanup);
}
if (context->cmd.Key_Create.policyPath
&& strcmp(context->cmd.Key_Create.policyPath, "") != 0)
context->cmd.Key_Create.state = KEY_CREATE_CALCULATE_POLICY;
/* else jump over to KEY_CREATE_WAIT_FOR_SESSION below */
/* FALLTHRU */
case KEY_CREATE_CALCULATE_POLICY:
if (context->cmd.Key_Create.state == KEY_CREATE_CALCULATE_POLICY) {
r = ifapi_calculate_tree(context, context->cmd.Key_Create.policyPath,
&context->policy.policy,
context->cmd.Key_Create.public_templ.public.publicArea.nameAlg,
&context->policy.digest_idx,
&context->policy.hash_size);
return_try_again(r);
goto_if_error2(r, "Calculate policy tree %s", error_cleanup,
context->cmd.Key_Create.policyPath);
/* Store the calculated policy in the key object */
object->policy = calloc(1, sizeof(TPMS_POLICY));
return_if_null(object->policy, "Out of memory",
TSS2_FAPI_RC_MEMORY);
*(object->policy) = context->policy.policy;
context->cmd.Key_Create.public_templ.public.publicArea.authPolicy.size =
context->policy.hash_size;
memcpy(&context->cmd.Key_Create.public_templ.public.publicArea.authPolicy.buffer[0],
&context->policy.policy.policyDigests.digests[context->policy.digest_idx].digest,
context->policy.hash_size);
}
r = ifapi_get_sessions_async(context,
IFAPI_SESSION_GENEK | IFAPI_SESSION1,
TPMA_SESSION_ENCRYPT | TPMA_SESSION_DECRYPT, 0);
goto_if_error_reset_state(r, "Create sessions", error_cleanup);
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_WAIT_FOR_SESSION);
LOG_TRACE("KEY_CREATE_WAIT_FOR_SESSION");
r = ifapi_get_sessions_finish(context, context->cmd.Key_Create.profile,
context->cmd.Key_Create.profile->nameAlg);
return_try_again(r);
goto_if_error_reset_state(r, " FAPI create session", error_cleanup);
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_WAIT_FOR_RANDOM);
if (context->cmd.Key_Create.gen_sensitive_random) {
r = ifapi_get_random(context,
context->cmd.Key_Create.inSensitive.sensitive.data.size,
&random_data);
return_try_again(r);
goto_if_error_reset_state(r, "FAPI GetRandom", error_cleanup);
/* Copy the sensitive data */
memcpy(&context->cmd.Key_Create.inSensitive.sensitive.data.buffer,
random_data,
context->cmd.Key_Create.inSensitive.sensitive.data.size);
SAFE_FREE(random_data);
}
path_length = ifapi_path_length(path_list);
r = ifapi_load_key_async(context, path_length - 1);
goto_if_error(r, "LoadKey async", error_cleanup);
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_WAIT_FOR_PARENT);
LOG_TRACE("KEY_CREATE_WAIT_FOR_PARENT");
r = ifapi_load_key_finish(context, IFAPI_FLUSH_PARENT);
return_try_again(r);
goto_if_error(r, "LoadKey finish", error_cleanup);
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_WAIT_FOR_AUTHORIZATION);
r = ifapi_authorize_object(context, &context->loadKey.auth_object, &auth_session);
FAPI_SYNC(r, "Authorize key.", error_cleanup);
r = Esys_Create_Async(context->esys, context->loadKey.auth_object.handle,
auth_session,
ESYS_TR_NONE, ESYS_TR_NONE,
&context->cmd.Key_Create.inSensitive,
&context->cmd.Key_Create.public_templ.public,
&context->cmd.Key_Create.outsideInfo,
&context->cmd.Key_Create.creationPCR);
goto_if_error(r, "Create_Async", error_cleanup);
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_AUTH_SENT);
r = Esys_Create_Finish(context->esys, &outPrivate, &outPublic, &creationData,
&creationHash, &creationTicket);
try_again_or_error_goto(r, "Key create finish", error_cleanup);
/* Prepare object for serialization */
object->system = context->cmd.Key_Create.public_templ.system;
object->objectType = IFAPI_KEY_OBJ;
object->misc.key.public = *outPublic;
object->misc.key.private.size = outPrivate->size;
object->misc.key.private.buffer = calloc(1, outPrivate->size);
goto_if_null2( object->misc.key.private.buffer, "Out of memory.", r,
TSS2_FAPI_RC_MEMORY, error_cleanup);
object->misc.key.private.buffer = memcpy(&object->misc.key.private.buffer[0],
&outPrivate->buffer[0], outPrivate->size);
object->misc.key.policyInstance = NULL;
object->misc.key.creationData = *creationData;
object->misc.key.creationTicket = *creationTicket;
object->misc.key.description = NULL;
object->misc.key.certificate = NULL;
SAFE_FREE(creationData);
SAFE_FREE(creationTicket);
SAFE_FREE(creationHash);
if (context->cmd.Key_Create.inSensitive.sensitive.userAuth.size > 0)
object->misc.key.with_auth = TPM2_YES;
else
object->misc.key.with_auth = TPM2_NO;;
r = ifapi_get_name(&outPublic->publicArea, &object->misc.key.name);
goto_if_error(r, "Get key name", error_cleanup);
SAFE_FREE(outPrivate);
SAFE_FREE(outPublic);
if (object->misc.key.public.publicArea.type == TPM2_ALG_RSA)
object->misc.key.signing_scheme = context->cmd.Key_Create.profile->rsa_signing_scheme;
else
object->misc.key.signing_scheme = context->cmd.Key_Create.profile->ecc_signing_scheme;
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_WAIT_FOR_LOAD_AUTHORIZATION);
if (template->persistent_handle) {
r = ifapi_authorize_object(context, &context->loadKey.auth_object, &auth_session);
FAPI_SYNC(r, "Authorize key.", error_cleanup);
TPM2B_PRIVATE private;
private.size = object->misc.key.private.size;
memcpy(&private.buffer[0], &object->misc.key.private.buffer[0],
private.size);
r = Esys_Load_Async(context->esys, context->loadKey.handle,
auth_session,
ESYS_TR_NONE, ESYS_TR_NONE,
&private,
&object->misc.key.public);
goto_if_error(r, "Load key.", error_cleanup);
}
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_WAIT_FOR_KEY);
if (template->persistent_handle) {
r = Esys_Load_Finish(context->esys, &context->loadKey.handle);
return_try_again(r);
goto_if_error_reset_state(r, "Load", error_cleanup);
}
/* Prepare Flushing of key used for authorization */
if (!context->loadKey.auth_object.misc.key.persistent_handle) {
r = Esys_FlushContext_Async(context->esys, context->loadKey.auth_object.handle);
goto_if_error(r, "Flush parent", error_cleanup);
}
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_FLUSH1);
if (!context->loadKey.auth_object.misc.key.persistent_handle) {
r = Esys_FlushContext_Finish(context->esys);
try_again_or_error_goto(r, "Flush context", error_cleanup);
ifapi_cleanup_ifapi_object(&context->loadKey.auth_object);
}
if (template->persistent_handle) {
r = ifapi_keystore_load_async(&context->keystore, &context->io, "/HS");
return_if_error2(r, "Could not open hierarchy /HS");
}
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_WAIT_FOR_HIERARCHY);
if (template->persistent_handle) {
r = ifapi_keystore_load_finish(&context->keystore, &context->io, hierarchy);
return_try_again(r);
return_if_error(r, "read_finish failed");
r = ifapi_initialize_object(context->esys, hierarchy);
goto_if_error_reset_state(r, "Initialize hierarchy object", error_cleanup);
hierarchy->handle = ESYS_TR_RH_OWNER;
}
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_AUTHORIZE_HIERARCHY);
if (template->persistent_handle) {
r = ifapi_authorize_object(context, hierarchy, &auth_session);
FAPI_SYNC(r, "Authorize hierarchy.", error_cleanup);
object->misc.key.persistent_handle = template->persistent_handle;
/* Prepare making the loaded key permanent. */
r = Esys_EvictControl_Async(context->esys, hierarchy->handle,
context->loadKey.handle,
auth_session, ESYS_TR_NONE,
ESYS_TR_NONE,
object->misc.key.persistent_handle);
goto_if_error(r, "Error Esys EvictControl", error_cleanup);
ifapi_cleanup_ifapi_object(hierarchy);
}
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_WAIT_FOR_EVICT_CONTROL);
if (template->persistent_handle) {
/* Prepare making the loaded key permanent. */
r = Esys_EvictControl_Finish(context->esys, &object->handle);
return_try_again(r);
goto_if_error(r, "Evict control failed", error_cleanup);
}
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_FLUSH2);
/* Flush the key which was evicted. */
if (template->persistent_handle) {
r = ifapi_flush_object(context, context->loadKey.handle);
return_try_again(r);
goto_if_error(r, "Flush key", error_cleanup);
}
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_WRITE_PREPARE);
if (template->persistent_handle) {
/* Compute the serialization, which will be used for the
reconstruction of the key object. */
SAFE_FREE(object->misc.key.serialization.buffer);
r = Esys_TR_Serialize(context->esys, object->handle,
&object->misc.key.serialization.buffer,
&object->misc.key.serialization.size);
goto_if_error(r, "Serialize object", error_cleanup);
}
/* Perform esys serialization if necessary */
r = ifapi_esys_serialize_object(context->esys, object);
goto_if_error(r, "Prepare serialization", error_cleanup);
/* Check whether object already exists in key store.*/
r = ifapi_keystore_object_does_not_exist(&context->keystore,
context->cmd.Key_Create.keyPath,
object);
goto_if_error_reset_state(r, "Could not write: %s", error_cleanup,
context->cmd.Key_Create.keyPath);
/* Start writing the object to the key store */
r = ifapi_keystore_store_async(&context->keystore, &context->io,
context->cmd.Key_Create.keyPath, object);
goto_if_error_reset_state(r, "Could not open: %s", error_cleanup,
context->cmd.Key_Create.keyPath);
ifapi_cleanup_ifapi_object(object);
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_WRITE);
/* Finish writing the key to the key store */
r = ifapi_keystore_store_finish(&context->keystore, &context->io);
return_try_again(r);
return_if_error_reset_state(r, "write_finish failed");
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_CLEANUP);
r = ifapi_cleanup_session(context);
try_again_or_error_goto(r, "Cleanup", error_cleanup);
context->cmd.Key_Create.state = KEY_CREATE_INIT;
r = TSS2_RC_SUCCESS;
break;
statecasedefault(context->cmd.Key_Create.state);
}
cleanup:
free_string_list(context->loadKey.path_list);
SAFE_FREE(outPublic);
SAFE_FREE(outPrivate);
SAFE_FREE(creationData);
SAFE_FREE(creationHash);
SAFE_FREE(creationTicket);
SAFE_FREE(context->cmd.Key_Create.policyPath);
SAFE_FREE(context->cmd.Key_Create.keyPath);
SAFE_FREE(random_data);
ifapi_cleanup_ifapi_object(object);
ifapi_session_clean(context);
if (template->persistent_handle)
ifapi_cleanup_ifapi_object(hierarchy);
return r;
error_cleanup:
if (template->persistent_handle)
ifapi_cleanup_ifapi_object(hierarchy);
if (context->loadKey.auth_object.handle != ESYS_TR_NONE &&
!context->loadKey.auth_object.misc.key.persistent_handle) {
Esys_FlushContext(context->esys, context->loadKey.auth_object.handle);
}
goto cleanup;
}
/** Get signature scheme for key.
*
* If padding is passed the scheme will be derived from paddint otherwise
* the scheme form object will be used.
*
* @param[in] context The FAPI_CONTEXT.
* @param[in] object The internal FAPI object of the key.
* @param[in] padding The strings RSA_SSA or RSA_PSS will be converted
* into the TSS constants used for the signing scheme.
* @param[in] digest The digest size will be used to determine the hashalg
* for the signature scheme.
* @param[out] sig_scheme The computed signature scheme.
*
* @retval TSS2_FAPI_RC_BAD_VALUE If the digest size is not appropriate.
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
*/
TSS2_RC
ifapi_get_sig_scheme(
FAPI_CONTEXT *context,
IFAPI_OBJECT *object,
char const *padding,
TPM2B_DIGEST *digest,
TPMT_SIG_SCHEME *sig_scheme)
{
TPMI_ALG_HASH hash_alg;
TSS2_RC r;
if (padding) {
/* Get hash algorithm from digest size */
r = ifapi_get_hash_alg_for_size(digest->size, &hash_alg);
return_if_error2(r, "Invalid digest size.");
/* Use scheme object from context */
if (strcasecmp("RSA_SSA", padding) == 0) {
context->Key_Sign.scheme.scheme = TPM2_ALG_RSASSA;
context->Key_Sign.scheme.details.rsassa.hashAlg = hash_alg;
}
if (strcasecmp("RSA_PSS", padding) == 0) {
context->Key_Sign.scheme.scheme = TPM2_ALG_RSAPSS;
context->Key_Sign.scheme.details.rsapss.hashAlg = hash_alg;
}
*sig_scheme = context->Key_Sign.scheme;
return TSS2_RC_SUCCESS;
} else {
/* Use scheme defined for object */
*sig_scheme = object->misc.key.signing_scheme;
/* Get hash algorithm from digest size */
r = ifapi_get_hash_alg_for_size(digest->size, &hash_alg);
return_if_error2(r, "Invalid digest size.");
sig_scheme->details.any.hashAlg = hash_alg;
return TSS2_RC_SUCCESS;
}
}
/** State machine for changing the hierarchy authorization.
*
* First it will be tried to set the auth value of the hierarchy with a
* "null" authorization. If this trial is not successful it will be tried to
* authorize the hierarchy via a callback.
* If an not null auth value is passed with_auth is set to yes for the
* object otherwise to no. So for later authorizations it will be clear
* whether null authorization is possible or not.
*
* @param[in] context The FAPI_CONTEXT.
* @param[in] handle The ESAPI handle of the hierarchy.
* @param[in,out] hierarchy_object The internal FAPI representation of a
* hierarchy.
* @param[in] newAuthValue The new authorization for the hierarchy.
*
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
* @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
* this function needs to be called again.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
* operation already pending.
* @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
* is not set.
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
* @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occured.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
* during authorization.
* @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
* @retval TSS2_FAPI_RC_IO_ERROR if an error occured while accessing the
* object store.
* @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
* was not successful.
*/
TSS2_RC
ifapi_change_auth_hierarchy(
FAPI_CONTEXT *context,
ESYS_TR handle,
IFAPI_OBJECT *hierarchy_object,
TPM2B_AUTH *newAuthValue)
{
TSS2_RC r;
ESYS_TR auth_session;
switch (context->hierarchy_state) {
statecase(context->hierarchy_state, HIERARCHY_CHANGE_AUTH_INIT);
if (hierarchy_object->misc.hierarchy.with_auth == TPM2_YES ||
policy_digest_size(hierarchy_object)) {
r = ifapi_authorize_object(context, hierarchy_object, &auth_session);
FAPI_SYNC(r, "Authorize hierarchy.", error);
} else {
auth_session = context->session1;
}
r = Esys_HierarchyChangeAuth_Async(context->esys,
handle,
(auth_session
&& auth_session != ESYS_TR_NONE) ?
auth_session : ESYS_TR_PASSWORD,
ESYS_TR_NONE, ESYS_TR_NONE,
newAuthValue);
return_if_error(r, "HierarchyChangeAuth");
fallthrough;
statecase(context->hierarchy_state, HIERARCHY_CHANGE_AUTH_NULL_AUTH_SENT);
r = Esys_HierarchyChangeAuth_Finish(context->esys);
return_try_again(r);
if (number_rc(r) == TPM2_RC_BAD_AUTH &&
hierarchy_object->misc.hierarchy.with_auth == TPM2_NO) {
/* Retry after NULL authorization was not successful */
char *description;
r = ifapi_get_description(hierarchy_object, &description);
return_if_error(r, "Get description");
r = ifapi_set_auth(context, hierarchy_object, description);
SAFE_FREE(description);
return_if_error(r, "HierarchyChangeAuth");
r = Esys_HierarchyChangeAuth_Async(context->esys,
handle,
(context->session1
&& context->session1 != ESYS_TR_NONE) ?
context->session1 : ESYS_TR_PASSWORD,
ESYS_TR_NONE, ESYS_TR_NONE,
newAuthValue);
return_if_error(r, "HierarchyChangeAuth");
return TSS2_FAPI_RC_TRY_AGAIN;
}
return_if_error(r, "HierarchyChangeAuth");
if (newAuthValue->size > 0)
hierarchy_object->misc.hierarchy.with_auth = TPM2_YES;
else
hierarchy_object->misc.hierarchy.with_auth = TPM2_NO;
context->hierarchy_state = HIERARCHY_CHANGE_AUTH_INIT;
return r;
statecasedefault(context->hierarchy_state);
}
error:
return r;
}
/** State machine for changing the policy of a hierarchy.
*
* Based on a passed policy the policy digest will be computed.
* First it will be tried to set the policy of the hierarchy with a
* "null" authorization. If this trial is not successful it will be tried to
* authorize the hierarchy via a callback.
* If an not null auth value is passed with_auth is set to yes for the
* object otherwise to no. So for later authorizations it will be clear
* whether null authorization is possible or not.
*
* @param[in] context The FAPI_CONTEXT.
* @param[in] handle The ESAPI handle of the hierarchy.
* @param[in,out] hierarchy_object The internal FAPI representation of a
* hierarchy.
* @param[in] policy The new policy assigned to the hierarchy.
*
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
* @retval TSS2_FAPI_RC_GENERAL_FAILURE If an internal error occurs, which is
* not covered by other return codes.
* @retval TSS2_FAPI_RC_BAD_VALUE If wrong values are detected during policy calculation.
* @retval TSS2_FAPI_RC_IO_ERROR If an error occurs during access to the policy
* store.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND If an object needed for policy calculation was
* not found.
* @retval TSS2_FAPI_RC_POLICY_UNKNOWN If policy search for a certain policy digest was
* not successful.
* @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
* this function needs to be called again.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
* operation already pending.
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
* @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
* @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
* is not set.
* @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
* @retval TSS2_FAPI_RC_NOT_PROVISIONED FAPI was not provisioned.
* @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context
* or contains illegal characters.
*/
TSS2_RC
ifapi_change_policy_hierarchy(
FAPI_CONTEXT *context,
ESYS_TR handle,
IFAPI_OBJECT *hierarchy_object,
TPMS_POLICY *policy)
{
TSS2_RC r;
ESYS_TR auth_session;
switch (context->hierarchy_policy_state) {
statecase(context->hierarchy_policy_state, HIERARCHY_CHANGE_POLICY_INIT);
if ((! policy || ! policy->policy) && !hierarchy_object->policy) {
/* No policy will be used for hierarchy */
return TSS2_RC_SUCCESS;
}
fallthrough;
statecase(context->hierarchy_policy_state, HIERARCHY_CHANGE_POLICY_AUTHORIZE);
if (hierarchy_object->misc.hierarchy.with_auth == TPM2_YES ||
policy_digest_size(hierarchy_object)) {
r = ifapi_authorize_object(context, hierarchy_object, &auth_session);
FAPI_SYNC(r, "Authorize hierarchy.", error);
} else {
auth_session = context->session1;
}
if (policy) {
context->policy.state = POLICY_INIT;
/* Calculate the policy digest which will be used as hierarchy policy. */
r = ifapi_calculate_tree(context, NULL, /**< no path needed */
policy,
context->profiles.default_profile.nameAlg,
&context->cmd.Provision.digest_idx,
&context->cmd.Provision.hash_size);
goto_if_error(r, "Policy calculation", error);
/* Policy data will be stored in the provisioning context. */
context->cmd.Provision.policy_digest.size = context->cmd.Provision.hash_size;
memcpy(&context->cmd.Provision.policy_digest.buffer[0],
&policy
->policyDigests.digests[context->cmd.Provision.digest_idx].digest,
context->cmd.Provision.hash_size);
hierarchy_object->policy = policy;
hierarchy_object->misc.hierarchy.authPolicy = context->cmd.Provision.policy_digest;
} else {
/* No policy will be used for this hierarchy. */
context->cmd.Provision.policy_digest.size = 0;
ifapi_cleanup_policy(hierarchy_object->policy);
SAFE_FREE(hierarchy_object->policy);
hierarchy_object->policy = NULL;
hierarchy_object->misc.hierarchy.with_auth = TPM2_NO;
hierarchy_object->misc.hierarchy.authPolicy.size = 0;
}
/* Prepare the setting of the policy. */
r = Esys_SetPrimaryPolicy_Async(context->esys, handle,
(auth_session
&& auth_session != ESYS_TR_NONE) ?
auth_session : ESYS_TR_PASSWORD,
ESYS_TR_NONE, ESYS_TR_NONE,
&context->cmd.Provision.policy_digest,
context->cmd.Provision.policy_digest.size ?
context->profiles.default_profile.nameAlg :
TPM2_ALG_NULL);
return_if_error(r, "Esys_SetPrimaryPolicy_Async");
fallthrough;
statecase(context->hierarchy_policy_state, HIERARCHY_CHANGE_POLICY_NULL_AUTH_SENT);
r = Esys_SetPrimaryPolicy_Finish(context->esys);
return_try_again(r);
if (number_rc(r) == TPM2_RC_BAD_AUTH &&
hierarchy_object->misc.hierarchy.with_auth == TPM2_NO) {
/* Retry after NULL authorization was not successful */
char *description;
r = ifapi_get_description(hierarchy_object, &description);
return_if_error(r, "Get description");
r = ifapi_set_auth(context, hierarchy_object, description);
SAFE_FREE(description);
return_if_error(r, "HierarchyChangePolicy");
r = Esys_SetPrimaryPolicy_Async(context->esys, handle,
(context->session1
&& context->session1 != ESYS_TR_NONE) ?
context->session1 : ESYS_TR_PASSWORD,
ESYS_TR_NONE, ESYS_TR_NONE,
&context->cmd.Provision.policy_digest,
context->profiles.default_profile.nameAlg);
return_if_error(r, "Esys_SetPrimaryPolicy_Async");
return TSS2_FAPI_RC_TRY_AGAIN;
}
return_if_error(r, "Set primary policy");
break;
statecasedefault(context->hierarchy_policy_state);
}
error:
return r;
}
/** Allocate ifapi object and store the result in a linked list.
*
* Allocated ifapi objects will be recorded in the context.
*
* @param[in,out] context The FAPI_CONTEXT.
*
* @retval The allocated ifapi object.
* @retval NULL if the object cannot be allocated.
*/
IFAPI_OBJECT
*ifapi_allocate_object(FAPI_CONTEXT *context)
{
NODE_OBJECT_T *node = calloc(1, sizeof(NODE_OBJECT_T));
if (!node)
return NULL;
node->object = calloc(1, sizeof(IFAPI_OBJECT));
if (!node->object) {
free(node);
return NULL;
}
node->next = context->object_list;
context->object_list = node;
return (IFAPI_OBJECT *) node->object;
}
/** Free all ifapi objects stored in the context.
*
* @param[in,out] context The FAPI_CONTEXT.
*/
void
ifapi_free_objects(FAPI_CONTEXT *context)
{
NODE_OBJECT_T *free_node;
NODE_OBJECT_T *node = context->object_list;
while (node) {
free(node->object);
free_node = node;
node = node->next;
free(free_node);
}
}
/** Free ifapi a object stored in the context.
*
* @param[in,out] context The FAPI_CONTEXT.
* @param[in,out] object The object which should be removed from the
* the linked list stored in context.
*/
void
ifapi_free_object(FAPI_CONTEXT *context, IFAPI_OBJECT **object)
{
NODE_OBJECT_T *node;
NODE_OBJECT_T **update_ptr;
for (node = context->object_list,
update_ptr = &context->object_list;
node != NULL;
update_ptr = &node->next, node = node->next) {
if (node->object == object) {
*update_ptr = node->next;
SAFE_FREE(node->object);
SAFE_FREE(node);
*object = NULL;
return;
}
}
}
#define ADD_CAPABILITY_INFO(capability, field, subfield, max_count, property_count) \
if (context->cmd.GetInfo.fetched_data->data.capability.count > max_count - property_count) { \
context->cmd.GetInfo.fetched_data->data.capability.count = max_count - property_count; \
} \
\
memmove(&context->cmd.GetInfo.capability_data->data.capability.field[property_count], \
context->cmd.GetInfo.fetched_data->data.capability.field, \
context->cmd.GetInfo.fetched_data->data.capability.count \
* sizeof(context->cmd.GetInfo.fetched_data->data.capability.field[0])); \
property_count += context->cmd.GetInfo.fetched_data->data.capability.count; \
\
context->cmd.GetInfo.capability_data->data.capability.count = property_count; \
\
if (more_data && property_count < count \
&& context->cmd.GetInfo.fetched_data->data.capability.count) { \
context->cmd.GetInfo.property \
= context->cmd.GetInfo.capability_data->data. \
capability.field[property_count - 1]subfield + 1; \
} else { \
more_data = false; \
}
/** Prepare the receiving of capability data.
*
* @param[in,out] context The FAPI_CONTEXT.
*
* @retval TSS2_RC_SUCCESS.
*/
TPM2_RC
ifapi_capability_init(FAPI_CONTEXT *context)
{
context->cmd.GetInfo.capability_data = NULL;
context->cmd.GetInfo.fetched_data = NULL;
return TSS2_RC_SUCCESS;
}
/** State machine for receiving TPM capability information.
*
* The state machine shares the state with the FAPI function Fapi_GetInfo.
* context->state == GET_INFO_GET_CAP_MORE signals that more capability data can
* be retrieved.
*
* @param[in,out] context The FAPI_CONTEXT.
* @param[in] capability The capability to be retrieved.
* @param[in] count The maximal number of items that should be retrieved.
* @param[out] capability_data The retrieved capability information.
*
* @retval TSS2_RC_SUCCESS If all capability data is retrieved.
* @retval TSS2_FAPI_RC_TRY_AGAIN if more capability data is available.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
* @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
* the function.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
* operation already pending.
*/
TPM2_RC
ifapi_capability_get(FAPI_CONTEXT *context, TPM2_CAP capability,
UINT32 count, TPMS_CAPABILITY_DATA **capability_data) {
TPMI_YES_NO more_data;
TSS2_RC r = TSS2_RC_SUCCESS;
ESYS_CONTEXT *ectx = context->esys;
switch (context->state) {
statecase(context->state, GET_INFO_GET_CAP);
/* fetch capability info */
context->cmd.GetInfo.fetched_data = NULL;
context->cmd.GetInfo.capability_data = NULL;
fallthrough;
statecase(context->state, GET_INFO_GET_CAP_MORE);
r = Esys_GetCapability_Async(ectx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
capability, context->cmd.GetInfo.property,
count - context->cmd.GetInfo.property_count);
goto_if_error(r, "Error GetCapability", error_cleanup);
fallthrough;
statecase(context->state, GET_INFO_WAIT_FOR_CAP);
r = Esys_GetCapability_Finish(ectx, &more_data, &context->cmd.GetInfo.fetched_data);
return_try_again(r);
goto_if_error(r, "Error GetCapability", error_cleanup);
LOG_TRACE("GetCapability: capability: 0x%x, property: 0x%x", capability,
context->cmd.GetInfo.property);
if (context->cmd.GetInfo.fetched_data->capability != capability) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
"TPM returned different capability than requested: 0x%x != 0x%x",
error_cleanup,
context->cmd.GetInfo.fetched_data->capability, capability);
}
if (context->cmd.GetInfo.capability_data == NULL) {
/* reuse the TPM's result structure */
context->cmd.GetInfo.capability_data = context->cmd.GetInfo.fetched_data;
if (!more_data) {
/* there won't be another iteration of the loop, just return the result unmodified */
*capability_data = context->cmd.GetInfo.capability_data;
return TPM2_RC_SUCCESS;
}
}
/* append the TPM's results to the initial structure, as long as there is still space left */
switch (capability) {
case TPM2_CAP_ALGS:
ADD_CAPABILITY_INFO(algorithms, algProperties, .alg,
TPM2_MAX_CAP_ALGS,
context->cmd.GetInfo.property_count);
break;
case TPM2_CAP_HANDLES:
ADD_CAPABILITY_INFO(handles, handle,,
TPM2_MAX_CAP_HANDLES,
context->cmd.GetInfo.property_count);
break;
case TPM2_CAP_COMMANDS:
ADD_CAPABILITY_INFO(command, commandAttributes,,
TPM2_MAX_CAP_CC,
context->cmd.GetInfo.property_count);
/* workaround because tpm2-tss does not implement attribute commandIndex for TPMA_CC */
context->cmd.GetInfo.property &= TPMA_CC_COMMANDINDEX_MASK;
break;
case TPM2_CAP_PP_COMMANDS:
ADD_CAPABILITY_INFO(ppCommands, commandCodes,,
TPM2_MAX_CAP_CC,
context->cmd.GetInfo.property_count);
break;
case TPM2_CAP_AUDIT_COMMANDS:
ADD_CAPABILITY_INFO(auditCommands, commandCodes,,
TPM2_MAX_CAP_CC,
context->cmd.GetInfo.property_count);
break;
case TPM2_CAP_PCRS:
ADD_CAPABILITY_INFO(assignedPCR, pcrSelections, .hash,
TPM2_NUM_PCR_BANKS,
context->cmd.GetInfo.property_count);
break;
case TPM2_CAP_TPM_PROPERTIES:
ADD_CAPABILITY_INFO(tpmProperties, tpmProperty, .property,
TPM2_MAX_TPM_PROPERTIES,
context->cmd.GetInfo.property_count);
break;
case TPM2_CAP_PCR_PROPERTIES:
ADD_CAPABILITY_INFO(pcrProperties, pcrProperty, .tag,
TPM2_MAX_PCR_PROPERTIES,
context->cmd.GetInfo.property_count);
break;
case TPM2_CAP_ECC_CURVES:
ADD_CAPABILITY_INFO(eccCurves, eccCurves,,
TPM2_MAX_ECC_CURVES,
context->cmd.GetInfo.property_count);
break;
case TPM2_CAP_VENDOR_PROPERTY:
ADD_CAPABILITY_INFO(intelPttProperty, property,,
TPM2_MAX_PTT_PROPERTIES,
context->cmd.GetInfo.property_count);
break;
default:
LOG_ERROR("Unsupported capability: 0x%x\n", capability);
if (context->cmd.GetInfo.fetched_data != context->cmd.GetInfo.capability_data) {
free(context->cmd.GetInfo.fetched_data);
}
free(context->cmd.GetInfo.capability_data);
*capability_data = NULL;
return TSS2_FAPI_RC_BAD_VALUE;
}
if (context->cmd.GetInfo.fetched_data != context->cmd.GetInfo.capability_data) {
free(context->cmd.GetInfo.fetched_data);
}
*capability_data = context->cmd.GetInfo.capability_data;
break;
statecasedefault(context->state);
}
if (more_data) {
context->state = GET_INFO_GET_CAP_MORE;
return TSS2_FAPI_RC_TRY_AGAIN;
} else {
context->state = _FAPI_STATE_INIT;
return TSS2_RC_SUCCESS;
}
error_cleanup:
context->state = _FAPI_STATE_INIT;
SAFE_FREE(context->cmd.GetInfo.capability_data);
SAFE_FREE(context->cmd.GetInfo.fetched_data);
return r;
}
/** Get certificates stored in NV ram.
*
* The NV handles in the certificate range are determined. The corresponding
* certificates are read out and stored in a linked list.
*
* @param[in,out] context The FAPI_CONTEXT. The sub context for NV reading
* will be used.
* @param[in] min_handle The first possible handle in the handle range.
* @param[in] max_handle Maximal handle to filter out the handles not in the
* handle range for certificates.
* @param[out] cert_list The callee allocates linked list of certificates.
*
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
*
* @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
* this function needs to be called again.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
* operation already pending.
* @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
* is not set.
* @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
* @retval TSS2_FAPI_RC_IO_ERROR if an error occurred while accessing the
* object store.
* @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
* was not successful.
* @retval TSS2_FAPI_RC_NOT_PROVISIONED FAPI was not provisioned.
* @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
* @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context
* or contains illegal characters.
* @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
* the function.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
* during authorization.
*/
TSS2_RC
ifapi_get_certificates(
FAPI_CONTEXT *context,
UINT32 min_handle,
UINT32 max_handle,
NODE_OBJECT_T **cert_list)
{
TSS2_RC r;
TPMI_YES_NO moreData;
uint8_t *cert_data;
size_t cert_size;
context->cmd.Provision.cert_nv_idx = MIN_EK_CERT_HANDLE;
switch (context->get_cert_state) {
statecase(context->get_cert_state, GET_CERT_INIT);
*cert_list = NULL;
context->cmd.Provision.capabilityData = NULL;
context->cmd.Provision.cert_idx = 0;
/* Prepare the reading of the capability handles in the certificate range */
r = Esys_GetCapability_Async(context->esys,
ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
TPM2_CAP_HANDLES, min_handle,
TPM2_MAX_CAP_HANDLES);
goto_if_error(r, "Esys_GetCapability_Async", error);
fallthrough;
statecase(context->get_cert_state, GET_CERT_WAIT_FOR_GET_CAP);
r = Esys_GetCapability_Finish(context->esys, &moreData,
&context->cmd.Provision.capabilityData);
try_again_or_error(r, "GetCapablity_Finish");
if (!context->cmd.Provision.capabilityData ||
context->cmd.Provision.capabilityData->data.handles.count == 0) {
*cert_list = NULL;
SAFE_FREE(context->cmd.Provision.capabilityData);
return TSS2_RC_SUCCESS;
}
context->cmd.Provision.cert_count =
context->cmd.Provision.capabilityData->data.handles.count;
/* Filter out NV handles beyond the EK cert range */
for (size_t i = 0; i < context->cmd.Provision.cert_count; i++) {
if (context->cmd.Provision.capabilityData->data.handles.handle[i] > max_handle) {
context->cmd.Provision.cert_count = i;
break;
}
}
fallthrough;
statecase(context->get_cert_state, GET_CERT_GET_CERT_NV);
goto_if_null(context->cmd.Provision.capabilityData,
"capabilityData is null", TSS2_FAPI_RC_MEMORY, error);
context->cmd.Provision.cert_nv_idx
= context->cmd.Provision.capabilityData
->data.handles.handle[context->cmd.Provision.cert_idx];
ifapi_init_hierarchy_object(&context->nv_cmd.auth_object,
TPM2_RH_OWNER);
r = Esys_TR_FromTPMPublic_Async(context->esys,
context->cmd.Provision.cert_nv_idx,
ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE);
goto_if_error_reset_state(r, "Esys_TR_FromTPMPublic_Async", error);
fallthrough;
statecase(context->get_cert_state, GET_CERT_GET_CERT_NV_FINISH);
r = Esys_TR_FromTPMPublic_Finish(context->esys,
&context->cmd.Provision.esys_nv_cert_handle);
try_again_or_error_goto(r, "TR_FromTPMPublic_Finish", error);
/* Read public to get size of certificate */
r = Esys_NV_ReadPublic_Async(context->esys,
context->cmd.Provision.esys_nv_cert_handle,
ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE);
goto_if_error_reset_state(r, "Esys_NV_ReadPublic_Async", error);
fallthrough;
statecase(context->get_cert_state, GET_CERT_GET_CERT_READ_PUBLIC);
r = Esys_NV_ReadPublic_Finish(context->esys,
&context->cmd.Provision.nvPublic,
NULL);
try_again_or_error_goto(r, "Error: nv read public", error);
/* TPMA_NV_NO_DA is set for NV certificate */
context->nv_cmd.nv_object.misc.nv.public.nvPublic.attributes = TPMA_NV_NO_DA;
r = ifapi_keystore_load_async(&context->keystore, &context->io, "/HS");
goto_if_error_reset_state(r, "Could not open hierarchy /HS", error);
fallthrough;
statecase(context->get_cert_state, GET_CERT_GET_CERT_READ_HIERARCHY);
r = ifapi_keystore_load_finish(&context->keystore, &context->io,
&context->nv_cmd.auth_object);
try_again_or_error_goto(r, "read_finish failed", error);
/* Prepare context for nv read */
r = ifapi_initialize_object(context->esys, &context->nv_cmd.auth_object);
goto_if_error_reset_state(r, "Initialize hierarchy object", error);
context->nv_cmd.auth_object.handle = ESYS_TR_RH_OWNER;
context->nv_cmd.data_idx = 0;
context->nv_cmd.auth_index = ESYS_TR_RH_OWNER;
context->nv_cmd.numBytes = context->cmd.Provision.nvPublic->nvPublic.dataSize;
context->nv_cmd.esys_handle = context->cmd.Provision.esys_nv_cert_handle;
context->nv_cmd.offset = 0;
context->cmd.Provision.pem_cert = NULL;
context->session1 = ESYS_TR_PASSWORD;
context->session2 = ESYS_TR_NONE;
context->nv_cmd.nv_read_state = NV_READ_INIT;
memset(&context->nv_cmd.nv_object, 0, sizeof(IFAPI_OBJECT));
SAFE_FREE(context->cmd.Provision.nvPublic);
fallthrough;
statecase(context->get_cert_state, GET_CERT_READ_CERT);
r = ifapi_nv_read(context, &cert_data, &cert_size);
try_again_or_error_goto(r, " FAPI NV_Read", error);
context->cmd.Provision.cert_idx += 1;
/* Add cert to list */
if (context->cmd.Provision.cert_idx == context->cmd.Provision.cert_count) {
context->get_cert_state = GET_CERT_GET_CERT_NV;
r = push_object_with_size_to_list(cert_data, cert_size, cert_list);
goto_if_error(r, "Store certificate in list.", error);
ifapi_cleanup_ifapi_object(&context->nv_cmd.auth_object);
SAFE_FREE(context->cmd.Provision.capabilityData);
return TSS2_RC_SUCCESS;
} else {
context->get_cert_state = GET_CERT_GET_CERT_NV;
return TSS2_FAPI_RC_TRY_AGAIN;
}
break;
statecasedefault(context->get_cert_state);
}
error:
SAFE_FREE(context->cmd.Provision.nvPublic);
SAFE_FREE(context->cmd.Provision.capabilityData);
ifapi_cleanup_ifapi_object(&context->nv_cmd.auth_object);
ifapi_free_object_list(*cert_list);
return r;
}
/** Get description of an internal FAPI object.
*
* @param[in] object The object with the description.
* @param[out] description The callee allocated description.
*
* @retval TSS2_RC_SUCCESS If a copy of the description can be returned
* or if no description exists.
* @retval TSS2_FAPI_RC_MEMORY in the copy cannot be allocated.
*/
TSS2_RC
ifapi_get_description(IFAPI_OBJECT *object, char **description)
{
char *obj_description = NULL;
switch (object->objectType) {
case IFAPI_KEY_OBJ:
obj_description = object->misc.key.description;
break;
case IFAPI_NV_OBJ:
obj_description = object->misc.nv.description;
break;
case IFAPI_HIERARCHY_OBJ:
if (object->misc.hierarchy.description)
obj_description = object->misc.hierarchy.description;
else if (object->handle == ESYS_TR_RH_OWNER)
obj_description = "Owner Hierarchy";
else if (object->handle == ESYS_TR_RH_ENDORSEMENT)
obj_description = "Endorsement Hierarchy";
else if (object->handle == ESYS_TR_RH_LOCKOUT)
obj_description = "Lockout Hierarchy";
else if (object->handle == ESYS_TR_RH_NULL)
obj_description = "Null Hierarchy";
else
obj_description = "Hierarchy";
break;
default:
*description = NULL;
return TSS2_RC_SUCCESS;
}
if (obj_description) {
*description = strdup(obj_description);
check_oom(*description);
} else {
*description = NULL;
}
return TSS2_RC_SUCCESS;
}
/** Set description of an internal FAPI object.
*
* @param[in,out] object The object with the description.
* @param[in] description The description char strint or NULL.
*/
void
ifapi_set_description(IFAPI_OBJECT *object, char *description)
{
switch (object->objectType) {
case IFAPI_KEY_OBJ:
SAFE_FREE(object->misc.key.description);
object->misc.key.description = description;
break;
case IFAPI_NV_OBJ:
SAFE_FREE(object->misc.nv.description);
object->misc.nv.description = description;
break;
case IFAPI_HIERARCHY_OBJ:
SAFE_FREE(object->misc.hierarchy.description);
object->misc.hierarchy.description = description;
break;
default:
LOG_WARNING("Description can't be set");
break;
}
}
/** Determine key properties (primary, null hierarchy).
*
* It will be checked whether a path is the path of a primary key,
* and whether it's a key in null hiearchy
*
* @param[in,out] context The FAPI_CONTEXT.
* @param[in] key_path the key path.
* @param[out] is_primary if key path is the path of a primary.
* @param[out] in_null_hierarchy if key is a null hierarchy key.
*
* @retval TSS2_RC_SUCCESS If the preparation is successful.
* @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated for path names.
* @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
* the function.
* @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context
* or contains illegal characters.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
* during authorization.
*/
TSS2_RC
ifapi_get_key_properties(
FAPI_CONTEXT *context,
char const *key_path,
bool *is_primary,
bool *in_null_hierarchy)
{
TSS2_RC r;
NODE_STR_T *path_list = NULL;
size_t path_length;
const char *hierarchy;
LOG_TRACE("Check primary: %s", key_path);
*is_primary = false;
*in_null_hierarchy = false;
r = get_explicit_key_path(&context->keystore, key_path, &path_list);
return_if_error(r, "Compute explicit path.");
path_length = ifapi_path_length(path_list);
if (path_length == 3) {
if (strncmp("P_", path_list->str, 2) == 0) {
hierarchy = path_list->next->str;
if (strcmp(hierarchy,"HS") == 0 ||
strcmp(hierarchy,"HE") == 0 ||
strcmp(hierarchy,"HN") == 0) {
*is_primary = true;
}
}
}
if (path_length >= 3) {
hierarchy = path_list->next->str;
if (strcmp(hierarchy,"HN") == 0)
*in_null_hierarchy = true;
}
free_string_list(path_list);
return TSS2_RC_SUCCESS;
}
/** Creation of a primary key.
*
* Depending on the flags stored in the context the creation of a primary
* key will be prepared.
*
* @param[in] context The FAPI_CONTEXT.
* @param[in] template The template which defines the key attributes and whether the
* key will be persistent.
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_FAPI_RC_BAD_VALUE if a wrong type was passed.
* @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
* @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
* this function needs to be called again.
* @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous
* operation already pending.
* @retval TSS2_FAPI_RC_IO_ERROR if an error occurred while accessing the
* object store.
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
* @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
* during authorization.
* @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
* @retval TSS2_FAPI_RC_NOT_PROVISIONED FAPI was not provisioned.
* @retval TSS2_FAPI_RC_BAD_PATH if the path is used in inappropriate context
* or contains illegal characters.
* @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
* is not set.
* @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
* @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
* was not successful.
* @retval TSS2_FAPI_RC_PATH_ALREADY_EXISTS if the object already exists in object store.
*/
TSS2_RC
ifapi_create_primary(
FAPI_CONTEXT *context,
IFAPI_KEY_TEMPLATE *template)
{
TSS2_RC r;
ESYS_TR auth_session;
TPM2B_PUBLIC *outPublic = NULL;
TPM2B_CREATION_DATA *creationData = NULL;
TPM2B_DIGEST *creationHash = NULL;
TPMT_TK_CREATION *creationTicket = NULL;
IFAPI_OBJECT *object = &context->cmd.Key_Create.object;
IFAPI_OBJECT *hierarchy = &context->cmd.Key_Create.hierarchy;
IFAPI_KEY *pkey = &context->cmd.Key_Create.object.misc.key;
const char *hierarchy_path;
const char *profile_name;
/* Compute policy */
switch (context->cmd.Key_Create.state) {
statecase(context->cmd.Key_Create.state, KEY_CREATE_PRIMARY_INIT);
profile_name = context->loadKey.path_list->str;
r = ifapi_profiles_get(&context->profiles, profile_name,
&context->cmd.Key_Create.profile);
goto_if_error_reset_state(r, "Retrieving profile data", error_cleanup);
context->cmd.Key_Create.public_templ = *template;
r = ifapi_merge_profile_into_template(context->cmd.Key_Create.profile,
&context->cmd.Key_Create.public_templ);
goto_if_error_reset_state(r, "Merge profile", error_cleanup);
/* Persistent keys are not allowd in the NULL hierarchy. */
if (template->persistent_handle) {
hierarchy_path = context->loadKey.path_list->next->str;
if (strcmp(hierarchy_path, "HN") == 0)
goto_error_reset_state(r, TSS2_FAPI_RC_BAD_VALUE,
"Persistent keys are not possible in the NULL "
"hierarchy.", error_cleanup);
}
if (context->cmd.Key_Create.policyPath
&& strcmp(context->cmd.Key_Create.policyPath, "") != 0)
context->cmd.Key_Create.state = KEY_CREATE_PRIMARY_CALCULATE_POLICY;
/* else jump over to KEY_CREATE_PRIMARY_WAIT_FOR_SESSION below */
/* FALLTHRU */
case KEY_CREATE_PRIMARY_CALCULATE_POLICY:
if (context->cmd.Key_Create.state == KEY_CREATE_PRIMARY_CALCULATE_POLICY) {
r = ifapi_calculate_tree(context, context->cmd.Key_Create.policyPath,
&context->policy.policy,
context->cmd.Key_Create.public_templ.public.publicArea.nameAlg,
&context->policy.digest_idx,
&context->policy.hash_size);
return_try_again(r);
goto_if_error2(r, "Calculate policy tree %s", error_cleanup,
context->cmd.Key_Create.policyPath);
/* Store the calculated policy in the key object */
object->policy = calloc(1, sizeof(TPMS_POLICY));
return_if_null(object->policy, "Out of memory",
TSS2_FAPI_RC_MEMORY);
*(object->policy) = context->policy.policy;
context->cmd.Key_Create.public_templ.public.publicArea.authPolicy.size =
context->policy.hash_size;
memcpy(&context->cmd.Key_Create.public_templ.public.publicArea.authPolicy.buffer[0],
&context->policy.policy.policyDigests.digests[context->policy.digest_idx].digest,
context->policy.hash_size);
}
r = ifapi_get_sessions_async(context,
IFAPI_SESSION_GENEK | IFAPI_SESSION1,
TPMA_SESSION_ENCRYPT | TPMA_SESSION_DECRYPT, 0);
goto_if_error_reset_state(r, "Create sessions", error_cleanup);
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_PRIMARY_WAIT_FOR_SESSION);
r = ifapi_get_sessions_finish(context, &context->profiles.default_profile,
context->profiles.default_profile.nameAlg);
return_try_again(r);
goto_if_error(r, "Create FAPI session.", error_cleanup);
hierarchy_path = context->loadKey.path_list->next->str;
r = ifapi_keystore_load_async(&context->keystore, &context->io, hierarchy_path);
free_string_list(context->loadKey.path_list);
context->loadKey.path_list = NULL;
return_if_error2(r, "Could not open hierarchy /%s", hierarchy_path);
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_PRIMARY_WAIT_FOR_HIERARCHY);
r = ifapi_keystore_load_finish(&context->keystore, &context->io, hierarchy);
return_try_again(r);
return_if_error(r, "read_finish failed");
r = ifapi_initialize_object(context->esys, hierarchy);
goto_if_error_reset_state(r, "Initialize hierarchy object", error_cleanup);
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_PRIMARY_WAIT_FOR_AUTHORIZE1);
r = ifapi_authorize_object(context, hierarchy, &auth_session);
FAPI_SYNC(r, "Authorize hierarchy.", error_cleanup);
r = Esys_CreatePrimary_Async(context->esys, hierarchy->handle,
auth_session,
ESYS_TR_NONE, ESYS_TR_NONE,
&context->cmd.Key_Create.inSensitive,
&context->cmd.Key_Create.public_templ.public,
&context->cmd.Key_Create.outsideInfo,
&context->cmd.Key_Create.creationPCR);
goto_if_error_reset_state(r, "Prepare create primary", error_cleanup);
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_PRIMARY_WAIT_FOR_PRIMARY);
r = Esys_CreatePrimary_Finish(context->esys,
&context->cmd.Key_Create.handle,
&outPublic, &creationData, &creationHash,
&creationTicket);
try_again_or_error_goto(r, "Create primary.", error_cleanup);
/* Prepare object for serialization */
object->system = context->cmd.Key_Create.public_templ.system;
object->objectType = IFAPI_KEY_OBJ;
object->misc.key.public = *outPublic;
object->misc.key.private.size = 0;
object->misc.key.private.buffer = NULL;
object->misc.key.policyInstance = NULL;
object->misc.key.creationData = *creationData;
object->misc.key.creationTicket = *creationTicket;
object->misc.key.description = NULL;
object->misc.key.certificate = NULL;
object->misc.key.reset_count = context->init_time.clockInfo.resetCount;
SAFE_FREE(pkey->serialization.buffer);
r = Esys_TR_Serialize(context->esys, context->cmd.Key_Create.handle,
&pkey->serialization.buffer, &pkey->serialization.size);
goto_if_error(r, "Error serialize esys object", error_cleanup);
SAFE_FREE(creationData);
SAFE_FREE(creationTicket);
SAFE_FREE(creationHash);
if (context->cmd.Key_Create.inSensitive.sensitive.userAuth.size > 0)
object->misc.key.with_auth = TPM2_YES;
else
object->misc.key.with_auth = TPM2_NO;;
r = ifapi_get_name(&outPublic->publicArea, &object->misc.key.name);
goto_if_error(r, "Get key name", error_cleanup);
if (object->misc.key.public.publicArea.type == TPM2_ALG_RSA)
object->misc.key.signing_scheme = context->cmd.Key_Create.profile->rsa_signing_scheme;
else
object->misc.key.signing_scheme = context->cmd.Key_Create.profile->ecc_signing_scheme;
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_PRIMARY_WAIT_FOR_AUTHORIZE2);
if (template->persistent_handle) {
r = ifapi_authorize_object(context, hierarchy, &auth_session);
FAPI_SYNC(r, "Authorize hierarchy.", error_cleanup);
/* Prepare making the created primary permanent. */
r = Esys_EvictControl_Async(context->esys, hierarchy->handle,
context->cmd.Key_Create.handle,
auth_session, ESYS_TR_NONE,
ESYS_TR_NONE,
object->misc.key.persistent_handle);
goto_if_error(r, "Error Esys EvictControl", error_cleanup);
}
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_PRIMARY_WAIT_FOR_EVICT_CONTROL);
if (template->persistent_handle) {
/* Prepare making the loaded key permanent. */
r = Esys_EvictControl_Finish(context->esys, &object->handle);
return_try_again(r);
goto_if_error(r, "Evict control failed", error_cleanup);
}
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_PRIMARY_FLUSH);
/* Flush the primary key. */
r = ifapi_flush_object(context, context->cmd.Key_Create.handle);
return_try_again(r);
goto_if_error(r, "Flush key", error_cleanup);
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_PRIMARY_WRITE_PREPARE);
SAFE_FREE(outPublic);
/* Perform esys serialization */
r = ifapi_esys_serialize_object(context->esys, object);
goto_if_error(r, "Prepare serialization", error_cleanup);
/* Check whether object already exists in key store.*/
r = ifapi_keystore_object_does_not_exist(&context->keystore,
context->cmd.Key_Create.keyPath,
object);
goto_if_error_reset_state(r, "Could not write: %s", error_cleanup,
context->cmd.Key_Create.keyPath);
/* Start writing the object to the key store */
r = ifapi_keystore_store_async(&context->keystore, &context->io,
context->cmd.Key_Create.keyPath, object);
goto_if_error_reset_state(r, "Could not open: %s", error_cleanup,
context->cmd.Key_Create.keyPath);
ifapi_cleanup_ifapi_object(object);
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_PRIMARY_WRITE);
/* Finish writing the key to the key store */
r = ifapi_keystore_store_finish(&context->keystore, &context->io);
return_try_again(r);
return_if_error_reset_state(r, "write_finish failed");
fallthrough;
statecase(context->cmd.Key_Create.state, KEY_CREATE_PRIMARY_CLEANUP);
r = ifapi_cleanup_session(context);
try_again_or_error_goto(r, "Cleanup", error_cleanup);
context->cmd.Key_Create.state = KEY_CREATE_INIT;
break;
statecasedefault(context->cmd.Key_Create.state);
}
free_string_list(context->loadKey.path_list);
ifapi_cleanup_ifapi_object(hierarchy);
SAFE_FREE(outPublic);
SAFE_FREE(creationData);
SAFE_FREE(creationHash);
SAFE_FREE(creationTicket);
SAFE_FREE(context->cmd.Key_Create.policyPath);
SAFE_FREE(context->cmd.Key_Create.keyPath);
ifapi_cleanup_ifapi_object(object);
ifapi_session_clean(context);
return TSS2_RC_SUCCESS;
error_cleanup:
SAFE_FREE(outPublic);
SAFE_FREE(creationData);
SAFE_FREE(creationHash);
SAFE_FREE(creationTicket);
SAFE_FREE(context->cmd.Key_Create.policyPath);
SAFE_FREE(context->cmd.Key_Create.keyPath);
free_string_list(context->loadKey.path_list);
ifapi_cleanup_ifapi_object(hierarchy);
return r;
}