接前一篇文章:tpm2-tools源码分析之tpm2_load.c(4)
上一篇文章分析完了tpm2_load.c中的tpm2_tool_onrun函数的第2个函数process_inputs。本文分析第3个函数load。
先看一下调用该函数的代码片段:
/*
* 3. TPM2_CC_<command> call
*/
rc = load(ectx);
if (rc != tool_rc_success) {
return rc;
}
load函数的源码如下:
static tool_rc load(ESYS_CONTEXT *ectx) {
/*
* If a tssprivkey was specified, load the private and public from the
* parsed TSSPEM file.
*/
TPM2B_PRIVATE *to_load_priv =
ctx.is_tss_pem ? &tpm2_util_object_tsspem_priv : &ctx.object.private;
TPM2B_PUBLIC *to_load_pub =
ctx.is_tss_pem ? &tpm2_util_object_tsspem_pub : &ctx.object.public;
return tpm2_load(ectx, &ctx.parent.object, to_load_priv, to_load_pub,
&ctx.object.handle, &ctx.cp_hash, ctx.parameter_hash_algorithm);
}
ctx.is_tss_pem的取值在《tpm2-tools源码分析之tpm2_load.c(3)》中进行了分析。此处是根据ctx.is_tss_pem的值确定tp_load_priv指针的指向。如果ctx.is_tss_pem为True,则指向tpm2_util_object_tsspem_priv;如果ctx.is_tss_pem为False,则指向&ctx.object.private。
对于to_load_pub指针来说,也是同样的机制。
tpm2_util_object_tsspem_priv和tpm2_util_object_tsspem_pub在lib/object.c中定义,代码如下:
TPM2B_PRIVATE tpm2_util_object_tsspem_priv = { 0 };
TPM2B_PUBLIC tpm2_util_object_tsspem_pub = { 0 };
以上代码实际上就是判断公私钥是从PEM中来还是从文件中来。
tpm2_util_object_tsspem_priv和tpm2_util_object_tsspem_pub是在lib/object.c中的tpm2_util_object_load_tsspem函数中被赋值的,该函数代码如下:
static tool_rc tpm2_util_object_load_tsspem(ESYS_CONTEXT *ctx,
const char *objectstr, tpm2_loaded_object *outobject) {
/*
* fetch out the various parts of the PEM file using openssl API
*/
tool_rc rc = tpm2_util_object_fetch_priv_pub_from_tpk(objectstr,
&tpm2_util_object_tsspem_pub, &tpm2_util_object_tsspem_priv);
if (rc != tool_rc_success) {
LOG_ERR("Unable to fetch public/private portions of TSS PRIVKEY");
return rc;
}
uint64_t val;
rc = tpm2_util_object_fetch_parent_from_tpk(objectstr, &val);
if (rc != tool_rc_success) {
return rc;
}
bool is_persistent_parent = (val != TPM2_RH_OWNER && val != 0);
if (!is_persistent_parent) {
ESYS_TR obj_parent = ESYS_TR_NONE;
rc = tpm2_util_object_setup_primary(ctx, &obj_parent);
if (rc != tool_rc_success) {
LOG_ERR("Unable to create parent using createprimary");
return rc;
}
outobject->tr_handle = obj_parent;
rc = Esys_TR_GetTpmHandle(ctx, obj_parent,
&outobject->handle);
if (rc != TSS2_RC_SUCCESS) {
LOG_ERR("Unable to fetch TPM handle from ESYS TR handle");
return rc;
}
} else {
ESYS_TR tr_parent = ESYS_TR_NONE;
rc = tpm2_from_tpm_public(ctx, val, ESYS_TR_NONE, ESYS_TR_NONE,
ESYS_TR_NONE, &tr_parent);
if (rc != tool_rc_success) {
LOG_ERR("Unable to fetch TR Handle for persistent parent");
return rc;
}
outobject->tr_handle = tr_parent;
}
return rc;
}
回到load函数中。tpm2_load函数是load函数的核心函数,实际上也是tpm2_load命令的核心函数,其在lib/tpm2.c中,代码如下:
tool_rc tpm2_load(ESYS_CONTEXT *esys_context, tpm2_loaded_object *parentobj,
const TPM2B_PRIVATE *in_private, const TPM2B_PUBLIC *in_public,
ESYS_TR *object_handle, TPM2B_DIGEST *cp_hash,
TPMI_ALG_HASH parameter_hash_algorithm) {
ESYS_TR parent_object_session_handle = ESYS_TR_NONE;
tool_rc rc = tpm2_auth_util_get_shandle(esys_context, parentobj->tr_handle,
parentobj->session, &parent_object_session_handle);
if (rc != tool_rc_success) {
LOG_ERR("Failed to get parent object session handle");
return rc;
}
if (cp_hash && cp_hash->size) {
/*
* Need sys_context to be able to calculate CpHash
*/
TSS2_SYS_CONTEXT *sys_context = 0;
rc = tpm2_getsapicontext(esys_context, &sys_context);
if(rc != tool_rc_success) {
LOG_ERR("Failed to acquire SAPI context.");
return rc;
}
TSS2_RC rval = Tss2_Sys_Load_Prepare(sys_context, parentobj->handle,
in_private, in_public);
if (rval != TPM2_RC_SUCCESS) {
LOG_PERR(Tss2_Sys_Load_Prepare, rval);
return tool_rc_general_error;
}
TPM2B_NAME *name1 = 0;
rc = tpm2_tr_get_name(esys_context, parentobj->tr_handle,
&name1);
if (rc != tool_rc_success) {
goto tpm2_load_free_name1;
}
rc = tpm2_sapi_getcphash(sys_context, name1, 0, 0,
parameter_hash_algorithm, cp_hash);
/*
* Exit here without making the ESYS call since we just need the cpHash
*/
tpm2_load_free_name1:
Esys_Free(name1);
goto tpm2_load_skip_esapi_call;
}
TSS2_RC rval = Esys_Load(esys_context, parentobj->tr_handle,
parent_object_session_handle, ESYS_TR_NONE, ESYS_TR_NONE, in_private,
in_public, object_handle);
if (rval != TPM2_RC_SUCCESS) {
LOG_PERR(Eys_Load, rval);
return tool_rc_from_tpm(rval);
}
tpm2_load_skip_esapi_call:
return rc;
}
1)tpm2_auth_util_get_shandle
tpm2_auth_util_get_shandle函数在lib/tpm2_auth_util.c中,代码如下:
tool_rc tpm2_auth_util_get_shandle(ESYS_CONTEXT *ectx, ESYS_TR object,
tpm2_session *session, ESYS_TR *out) {
*out = tpm2_session_get_handle(session);
const TPM2B_AUTH *auth = tpm2_session_get_auth_value(session);
return tpm2_tr_set_auth(ectx, object, auth);
}
2)tpm2_getsapicontext
tpm2_getsapicontext函数在lib/tpm2.c中,代码如下:
tool_rc tpm2_getsapicontext(ESYS_CONTEXT *esys_context,
TSS2_SYS_CONTEXT **sys_context) {
TSS2_RC rval = Esys_GetSysContext(esys_context, sys_context);
if (rval != TPM2_RC_SUCCESS) {
LOG_PERR(Esys_GetSysContext, rval);
return tool_rc_from_tpm(rval);
}
return tool_rc_success;
}
3)Tss2_Sys_Load_Prepare
Tss2_Sys_Load_Prepare函数的实现在tpm2-tools源码路径下是找不到的,其是在tpm2-tss源码中声明和实现的。声明是在tpm2-tss/include/tss2/tss2_sys.h中,实现是在tpm2-tss/src/tss2-sys/api/Tss2_Sys_Load.c中。由于不是本系列重点,在此不作深入讲解。只给出函数声明,如下所示:
TSS2_RC Tss2_Sys_Load_Prepare(
TSS2_SYS_CONTEXT *sysContext,
TPMI_DH_OBJECT parentHandle,
const TPM2B_PRIVATE *inPrivate,
const TPM2B_PUBLIC *inPublic
);
4)tpm2_tr_get_name
tpm2_tr_get_name函数在lib/tpm2.c中,代码如下:
tool_rc tpm2_tr_get_name(ESYS_CONTEXT *esys_context, ESYS_TR handle,
TPM2B_NAME **name) {
TSS2_RC rval = Esys_TR_GetName(esys_context, handle, name);
if (rval != TSS2_RC_SUCCESS) {
LOG_PERR(Esys_TR_GetName, rval);
return tool_rc_from_tpm(rval);
}
return tool_rc_success;
}
5)tpm2_sapi_getcphash
tpm2_sapi_getcphash函数在lib/tpm2.c中,代码如下:
tool_rc tpm2_sapi_getcphash(TSS2_SYS_CONTEXT *sys_context,
const TPM2B_NAME *name1, const TPM2B_NAME *name2, const TPM2B_NAME *name3,
TPMI_ALG_HASH halg, TPM2B_DIGEST *cp_hash) {
uint8_t command_code[4];
TSS2_RC rval = Tss2_Sys_GetCommandCode(sys_context, &command_code[0]);
if (rval != TPM2_RC_SUCCESS) {
LOG_PERR(Tss2_Sys_GetCommandCode, rval);
return tool_rc_general_error;
}
const uint8_t *command_parameters;
size_t command_parameters_size;
rval = Tss2_Sys_GetCpBuffer(sys_context, &command_parameters_size,
&command_parameters);
if (rval != TPM2_RC_SUCCESS) {
LOG_PERR(Tss2_Sys_GetCpBuffer, rval);
return tool_rc_general_error;
}
uint16_t to_hash_len = sizeof(command_code) + command_parameters_size;
to_hash_len += name1 ? name1->size : 0;
to_hash_len += name2 ? name2->size : 0;
to_hash_len += name3 ? name3->size : 0;
uint8_t *to_hash = malloc(to_hash_len);
if (!to_hash) {
LOG_ERR("oom");
return tool_rc_general_error;
}
//Command-Code
memcpy(to_hash, command_code, sizeof(command_code));
uint16_t offset = sizeof(command_code);
//Names
if (name1) {
memcpy(to_hash + offset, name1->name, name1->size);
offset += name1->size;
}
if (name2) {
memcpy(to_hash + offset, name2->name, name2->size);
offset += name2->size;
}
if (name3) {
memcpy(to_hash + offset, name3->name, name3->size);
offset += name3->size;
}
//CpBuffer
memcpy(to_hash + offset, command_parameters, command_parameters_size);
//cpHash
tool_rc rc = tool_rc_success;
bool result = tpm2_openssl_hash_compute_data(halg, to_hash, to_hash_len,
cp_hash);
free(to_hash);
if (!result) {
LOG_ERR("Failed cpHash digest calculation.");
rc = tool_rc_general_error;
}
return rc;
}
6)Esys_Load
Esys_Load函数在tpm2-tss/src/tss2-esys/api/Esys_Load.c中实现。由于不是本系列重点,在此不作深入讲解。只给出函数声明和部分函数注释,如下所示:
/** One-Call function for TPM2_Load
*
* This function invokes the TPM2_Load command in a one-call
* variant. This means the function will block until the TPM response is
* available. All input parameters are const. The memory for non-simple output
* parameters is allocated by the function implementation.
……
*/
TSS2_RC
Esys_Load(
ESYS_CONTEXT *esysContext,
ESYS_TR parentHandle,
ESYS_TR shandle1,
ESYS_TR shandle2,
ESYS_TR shandle3,
const TPM2B_PRIVATE *inPrivate,
const TPM2B_PUBLIC *inPublic, ESYS_TR *objectHandle)