接前一篇文章:tpm2-tools源码分析之tpm2_getrandom.c(2)
本文对tpm2_getrandom.c中的tpm2_tool_onrun函数进行详细解析。
先再次贴出该函数源码:
static tool_rc tpm2_tool_onrun(ESYS_CONTEXT *ectx, tpm2_option_flags flags) {
UNUSED(flags);
/*
* 1. Process options
*/
/*
* 2. Process inputs
*/
tool_rc rc = process_inputs(ectx);
if (rc != tool_rc_success) {
return rc;
}
/*
* 3. TPM2_CC_<command> call
*/
rc = get_random(ectx);
if (rc != tool_rc_success) {
return rc;
}
/*
* 4. Process outputs
*/
return process_outputs();
}
根据tpm2_tool_onrun函数中的注释,此函数分为4个步骤。不过第1个步骤没有任何函数和动作,因此实际上调用了3个函数:process_inputs、ger_random、process_outputs。实际上这个流程也是一个通用流程。
(1)process_inputs函数
process_inputs函数的作用是输入的处理。它在同文件(tpm2_getrrandom.c)中,代码如下:
process_inputs
这个函数又分为以下几个步骤:
1)对象和授权的初始化
由于tpm2_getrandom不需要授权,因此这一步骤实际上不需要。
2)恢复辅助会话
调用tpm2_util_aux_sessions_setup函数恢复辅助会话。对应代码片段如下:
tool_rc rc = tpm2_util_aux_sessions_setup(ectx, ctx.aux_session_cnt,
ctx.aux_session_path, ctx.aux_session_handle, ctx.aux_session);
if (rc != tool_rc_success) {
return rc;
}
tpm2_util_aux_sessions_setup函数在lib/tpm2_util.c中,代码如下:
#define MAX_SESSION_CNT 3
tool_rc tpm2_util_aux_sessions_setup(ESYS_CONTEXT *ectx, uint8_t session_cnt,
const char **session_path, ESYS_TR *session_handle,
tpm2_session **session) {
/*
* If no aux sessions were specified, simply return.
*/
if (!session_cnt) {
return tool_rc_success;
}
if (session_cnt > MAX_SESSION_CNT) {
LOG_ERR("A max of 3 sessions allowed");
return tool_rc_general_error;
}
uint8_t session_idx = 0;
for (session_idx = 0; session_idx < (session_cnt); session_idx++) {
if (session_path[session_idx]) {
tool_rc rc = tpm2_session_restore(ectx,
session_path[session_idx], false, &session[session_idx]);
if (rc != tool_rc_success) {
LOG_ERR("Could not restore aux-session #%s",
session_path[session_idx]);
return rc;
}
session_handle[session_idx] =
tpm2_session_get_handle(session[session_idx]);
}
}
return tool_rc_success;
}
对于lib库中的代码,后续会有专门的文章进行分析,在此不作重点分析。
再来重点说一下ctx。ctx在同文件(tools/tpm2_getrandom.c)中定义并初始化,代码如下:
static tpm_random_ctx ctx = {
.aux_session_handle[0] = ESYS_TR_NONE,
.aux_session_handle[1] = ESYS_TR_NONE,
.aux_session_handle[2] = ESYS_TR_NONE,
.parameter_hash_algorithm = TPM2_ALG_ERROR,
};
ctx是tpm_random_ctx结构的成员,该结构也在同文件中定义,代码如下:
typedef struct tpm_random_ctx tpm_random_ctx;
#define MAX_AUX_SESSIONS 3
#define MAX_SESSIONS 3
struct tpm_random_ctx {
/*
* Input options
*/
UINT16 num_of_bytes;
bool force;
bool hex;
/*
* Outputs
*/
char *output_file;
TPM2B_DIGEST *random_bytes;
/*
* Parameter hashes
*/
const char *cp_hash_path;
TPM2B_DIGEST cp_hash;
const char *rp_hash_path;
TPM2B_DIGEST rp_hash;
TPMI_ALG_HASH parameter_hash_algorithm;
bool is_command_dispatch;
/*
* Aux Sessions
*/
uint8_t aux_session_cnt;
tpm2_session *aux_session[MAX_AUX_SESSIONS];
const char *aux_session_path[MAX_AUX_SESSIONS];
ESYS_TR aux_session_handle[MAX_AUX_SESSIONS];
};
3)特定于命令的初始化
对应代码片段如下:
/*
* Error if bytes requested is bigger than max hash size, which is what TPMs
* should bound their requests by and always have available per the spec.
*
* Per 16.1 of:
* - https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-3-Commands-01.38.pdf
*
* Allow the force flag to override this behavior.
*/
if (!ctx.force) {
UINT32 max = 0;
rc = get_max_random(ectx, &max);
if (rc != tool_rc_success) {
return rc;
}
if (ctx.num_of_bytes > max) {
LOG_ERR("TPM getrandom is bounded by max hash size, which is: "
"%"PRIu32"\n"
"Please lower your request (preferred) and try again or"
" use --force (advanced)", max);
return tool_rc_general_error;
}
}
这段代码的意思是:如果命令参数选项中并未指定-f即强制选项,则需要检查并确保请求的大小在TPM的哈希大小限制内。最大字节数通过get_max_random函数获得,该函数在同文件(tools/tpm2_getrandom.c)中,代码如下:
static tool_rc get_max_random(ESYS_CONTEXT *ectx, UINT32 *value) {
TPMS_CAPABILITY_DATA *cap_data = NULL;
tool_rc rc = tpm2_capability_get(ectx, TPM2_CAP_TPM_PROPERTIES,
TPM2_PT_FIXED, TPM2_MAX_TPM_PROPERTIES, &cap_data);
if (rc != tool_rc_success) {
return rc;
}
UINT32 i;
for (i = 0; i < cap_data->data.tpmProperties.count; i++) {
TPMS_TAGGED_PROPERTY *p = &cap_data->data.tpmProperties.tpmProperty[i];
if (p->property == TPM2_PT_MAX_DIGEST) {
*value = p->value;
free(cap_data);
return tool_rc_success;
}
}
LOG_ERR("TPM does not have property TPM2_PT_MAX_DIGEST");
free(cap_data);
return tool_rc_general_error;
}
可见,get_max_random函数的核心是调用了tpm2_capability_get函数。tpm2_capability_get函数在lib/tpm2_capability.c中,代码如下:
tool_rc tpm2_capability_get(ESYS_CONTEXT *ectx, TPM2_CAP capability,
UINT32 property, UINT32 count,
TPMS_CAPABILITY_DATA **capability_data) {
return tpm2_capability_get_ex(ectx, capability,
property, count, false, capability_data);
}
4)用于计算pHash的配置
此步骤又分为2个子步骤:
4.a)确定哈希和算法
对应代码片断如下:
/*
* 4.a Determine pHash length and alg
*/
tpm2_session *all_sessions[MAX_SESSIONS] = {
ctx.aux_session[0],
ctx.aux_session[1],
ctx.aux_session[2]
};
const char **cphash_path = ctx.cp_hash_path ? &ctx.cp_hash_path : 0;
const char **rphash_path = ctx.rp_hash_path ? &ctx.rp_hash_path : 0;
ctx.parameter_hash_algorithm = tpm2_util_calculate_phash_algorithm(ectx,
cphash_path, &ctx.cp_hash, rphash_path, &ctx.rp_hash, all_sessions);
4.b)确定哈希和算法
对应代码片断如下:
/*
* 4.b Determine if TPM2_CC_<command> is to be dispatched
* !rphash && !cphash [Y]
* !rphash && cphash [N]
* rphash && !cphash [Y]
* rphash && cphash [Y]
*/
ctx.is_command_dispatch = (ctx.cp_hash_path && !ctx.rp_hash_path) ?
false : true;
这一段代码就对应上一篇文章中提到的“当cphash被选择,而rphash选项没有被选择,则该工具不会实际执行命令,它只是返回一个cpHash。
至此,tpm2_tool_onrun函数的第1个函数process_inputs函数就分析完了。下一篇文章起分析余下的函数。