tpm2-tools源码分析之tpm2_load.c(6)
接前一篇文章:tpm2-tools源码分析之tpm2_load.c(5)
上一篇文章分析完了tpm2_load.c中的tpm2_tool_onrun函数的第3个函数load。本文分析第4个函数process_output。
先看一下调用该函数的代码片段:
/*
* 4. Process outputs
*/
return process_output(ectx);
process_output函数源码如下:
static tool_rc process_output(ESYS_CONTEXT *ectx) {
UNUSED(ectx);
/*
* 1. Outputs that do not require TPM2_CC_<command> dispatch
*/
bool is_file_op_success = true;
if (ctx.cp_hash_path) {
is_file_op_success = files_save_digest(&ctx.cp_hash, ctx.cp_hash_path);
if (!is_file_op_success) {
return tool_rc_general_error;
}
}
tool_rc rc = tool_rc_success;
if (!ctx.is_command_dispatch) {
return rc;
}
/*
* 2. Outputs generated after TPM2_CC_<command> dispatch
*/
TPM2B_NAME *name;
rc = tpm2_tr_get_name(ectx, ctx.object.handle, &name);
if (rc != tool_rc_success) {
return rc;
}
if (ctx.namepath) {
bool result = files_save_bytes_to_file(ctx.namepath, name->name,
name->size);
free(name);
if (!result) {
return tool_rc_general_error;
}
} else {
tpm2_tool_output("name: ");
tpm2_util_print_tpm2b(name);
tpm2_tool_output("\n");
free(name);
}
return files_save_tpm_context_to_path(ectx, ctx.object.handle,
ctx.contextpath);
}
1)files_save_digest
files_save_digest函数在lib/files.c中实现,但是需要做转换。代码如下:
#define SAVE_TYPE(type, name) \
bool files_save_##name(type *name, const char *path) { \
\
size_t offset = 0; \
UINT8 buffer[sizeof(*name)]; \
TSS2_RC rc = Tss2_MU_##type##_Marshal(name, buffer, sizeof(buffer), &offset); \
if (rc != TSS2_RC_SUCCESS) { \
LOG_ERR("Error serializing "str(name)" structure: 0x%x", rc); \
return false; \
} \
\
return files_save_bytes_to_file(path, buffer, offset); \
}
根据上述宏定义,可以推断出files_save_digest函数对应的宏为:
SAVE_TYPE(TPM2B_DIGEST, digest)
对应的源码为:
bool files_save_digest(TPM2B_DIGEST *digest, const char *path) {
size_t offset = 0;
UINT8 buffer[sizeof(*digest)];
TSS2_RC rc = Tss2_MU_TPM2B_DIGEST_Marshal(digest, buffer, sizeof(buffer), &offset);
if (rc != TSS2_RC_SUCCESS) {
LOG_ERR("Error serializing "str(digest)" structure: 0x%x", rc);
return false;
}
return files_save_bytes_to_file(path, buffer, offset);
}
TPM2B_DIGEST在/usr/local/include/tss2/tss2_tpm2_types.h中定义,代码如下:
/* Definition of TPM2B_DIGEST Structure */
typedef struct TPM2B_DIGEST TPM2B_DIGEST;
struct TPM2B_DIGEST {
UINT16 size;
BYTE buffer[sizeof(TPMU_HA)];
};
TPMU_HA同样在/usr/local/include/tss2/tss2_tpm2_types.h中定义,代码如下:
/* Definition of TPMU_HA Union <INOUT S> */
typedef union TPMU_HA TPMU_HA;
union TPMU_HA {
BYTE sha [TPM2_SHA_DIGEST_SIZE]; /* TPM2_ALG_SHA */
BYTE sha1[TPM2_SHA1_DIGEST_SIZE];
BYTE sha256[TPM2_SHA256_DIGEST_SIZE];
BYTE sha384[TPM2_SHA384_DIGEST_SIZE];
BYTE sha512[TPM2_SHA512_DIGEST_SIZE];
BYTE sm3_256[TPM2_SM3_256_DIGEST_SIZE];
};
files_save_bytes_to_file函数在lib/files.c中,代码如下:
bool files_save_bytes_to_file(const char *path, UINT8 *buf, UINT16 size) {
if (!buf) {
return false;
}
if (!path && !output_enabled) {
return true;
}
FILE *fp = path ? fopen(path, "wb+") : stdout;
if (!fp) {
LOG_ERR("Could not open file \"%s\", error: %s", path, strerror(errno));
return false;
}
bool result = files_write_bytes(fp, buf, size);
if (!result) {
LOG_ERR("Could not write data to file \"%s\"", path ? path : "<stdout>");
}
if (fp != stdout) {
fclose(fp);
}
return result;
}
代码的意思一目了然,做常规检查后,以二进制读写方式打开文件,然后向打开的文件写入buf中的内容,即Tss2_MU_TPM2B_DIGEST_Marshal函数针对摘要生成的哈希值,最后关闭文件。
Tss2_MU_TPM2B_DIGEST_Marshal函数在tpm2-tss/src/tss2-mu/tpm2b-types.c中,代码如下(需要做一些转换):
TPM2B_MARSHAL (TPM2B_DIGEST);
#define TPM2B_MARSHAL(type) \
TSS2_RC Tss2_MU_##type##_Marshal(type const *src, uint8_t buffer[], \
size_t buffer_size, size_t *offset) \
{ \
size_t local_offset = 0; \
TSS2_RC rc; \
\
if (src == NULL) { \
LOG_WARNING("src param is NULL"); \
return TSS2_MU_RC_BAD_REFERENCE; \
} \
if (offset != NULL) { \
LOG_DEBUG("offset non-NULL, initial value: %zu", *offset); \
local_offset = *offset; \
} \
if (buffer == NULL && offset == NULL) { \
LOG_WARNING("buffer and offset parameter are NULL"); \
return TSS2_MU_RC_BAD_REFERENCE; \
} else if (buffer == NULL && offset != NULL) { \
*offset += sizeof(src->size) + src->size; \
LOG_TRACE("buffer NULL and offset non-NULL, updating offset to %zu", \
*offset); \
return TSS2_RC_SUCCESS; \
} else if (buffer_size < local_offset || \
buffer_size - local_offset < (sizeof(src->size) + src->size)) { \
LOG_DEBUG( \
"buffer_size: %zu with offset: %zu are insufficient for object " \
"of size %zu", \
buffer_size, \
local_offset, \
sizeof(src->size) + src->size); \
return TSS2_MU_RC_INSUFFICIENT_BUFFER; \
} else if ((sizeof(type) - sizeof(src->size)) < src->size) { \
LOG_WARNING(\
"size: %u for buffer of " #type " is larger than max length" \
" of buffer: %zu", \
src->size, \
(sizeof(type) - sizeof(src->size))); \
return TSS2_MU_RC_BAD_SIZE; \
} \
\
LOG_DEBUG(\
"Marshalling " #type " from 0x%" PRIxPTR " to buffer 0x%" PRIxPTR \
" at index 0x%zx, buffer size %zu, object size %u", \
(uintptr_t)&src, \
(uintptr_t)buffer, \
local_offset, \
buffer_size, \
src->size); \
\
rc = Tss2_MU_UINT16_Marshal(src->size, buffer, buffer_size, &local_offset); \
if (rc) \
return rc; \
\
if (src->size) { \
memcpy(&buffer[local_offset], ((TPM2B *)src)->buffer, src->size); \
local_offset += src->size; \
} \
\
if (offset != NULL) { \
*offset = local_offset; \
LOG_DEBUG("offset parameter non-NULL, updated to %zu", *offset); \
} \
\
return TSS2_RC_SUCCESS; \
}
2)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;
}
3)files_save_bytes_to_file
files_save_bytes_to_file在上边1)中已经分析过了,此处不再赘述。
4)tpm2_tool_output
tpm2_tool_output函数在lib/tpm2_tool_output.h中,代码如下:
/**
* prints output to stdout respecting the quiet option.
* Ie when quiet, don't print.
* @param fmt
* The format specifier, ala printf.
* @param ...
* The varargs, just like printf.
*/
#define tpm2_tool_output(fmt, ...) \
do { \
if (output_enabled) { \
printf(fmt, ##__VA_ARGS__); \
} \
} while (0)
#endif
5)tpm2_util_print_tpm2b
tpm2_util_print_tpm2b函数在lib/tpm2_util.h中,代码如下:
/**
* Prints a TPM2B as a hex dump respecting the -Q option
* to stdout.
*
* @param buffer the TPM2B to print.
*/
#define tpm2_util_print_tpm2b(b) _tpm2_util_print_tpm2b((TPM2B *)b)
static inline void _tpm2_util_print_tpm2b(TPM2B *buffer) {
return tpm2_util_hexdump(buffer->buffer, buffer->size);
}
tpm2_util_hexdump函数在lib/tpm2_util.c中,代码如下:
void tpm2_util_hexdump(const BYTE *data, size_t len) {
if (!output_enabled) {
return;
}
tpm2_util_hexdump2(stdout, data, len);
}
tpm2_util_hexdump2函数就在上边,代码如下:
void tpm2_util_hexdump2(FILE *f, const BYTE *data, size_t len) {
size_t i;
for (i = 0; i < len; i++) {
fprintf(f, "%02x", data[i]);
}
}
别看函数不少,实际上功能很简单,就是打印出name的十六进制数据。
6)files_save_tpm_context_to_path
files_save_tpm_context_to_path函数同样在lib/files.c中,代码如下:
tool_rc files_save_tpm_context_to_path(ESYS_CONTEXT *context, ESYS_TR handle,
const char *path) {
FILE *f = fopen(path, "w+b");
if (!f) {
LOG_ERR("Error opening file \"%s\" due to error: %s", path,
strerror(errno));
return tool_rc_general_error;
}
tool_rc rc = files_save_tpm_context_to_file(context, handle, f);
fclose(f);
return rc;
}
files_save_tpm_context_to_file函数就在上边,代码如下:
tool_rc files_save_tpm_context_to_file(ESYS_CONTEXT *ectx, ESYS_TR handle,
FILE *stream) {
TPMS_CONTEXT *context = NULL;
tool_rc rc = tpm2_context_save(ectx, handle, &context);
if (rc != tool_rc_success) {
return rc;
}
bool result = files_save_context(context, stream);
free(context);
return result ? tool_rc_success : tool_rc_general_error;
}
tpm2_context_save函数在lib/tpm2.c中,代码如下:
tool_rc tpm2_context_save(ESYS_CONTEXT *esys_context, ESYS_TR save_handle,
TPMS_CONTEXT **context) {
TSS2_RC rval = Esys_ContextSave(esys_context, save_handle, context);
if (rval != TSS2_RC_SUCCESS) {
LOG_PERR(Esys_ContextSave, rval);
return tool_rc_from_tpm(rval);
}
return tool_rc_success;
}
files_save_context函数在lib/files.c中,代码如下:
/*
* Current version to write TPMS_CONTEXT to disk.
*/
#define CONTEXT_VERSION 1
bool files_save_context(TPMS_CONTEXT *context, FILE *stream) {
/*
* Saving the TPMS_CONTEXT structure to disk, format:
* TPM2.0-TOOLS HEADER
* U32 hierarchy
* U32 savedHandle
* U64 sequence
* U16 contextBlobLength
* BYTE[] contextBlob
*/
bool result = files_write_header(stream, CONTEXT_VERSION);
if (!result) {
LOG_ERR("Could not write context file header");
goto out;
}
// UINT32
result = files_write_32(stream, context->hierarchy);
if (!result) {
LOG_ERR("Could not write hierarchy");
goto out;
}
result = files_write_32(stream, context->savedHandle);
if (!result) {
LOG_ERR("Could not write savedHandle");
goto out;
}
LOG_INFO("Save TPMS_CONTEXT->savedHandle: 0x%x", context->savedHandle);
// UINT64
result = files_write_64(stream, context->sequence);
if (!result) {
LOG_ERR("Could not write sequence");
goto out;
}
// U16 LENGTH
result = files_write_16(stream, context->contextBlob.size);
if (!result) {
LOG_ERR("Could not write contextBob size");
goto out;
}
// BYTE[] contextBlob
result = files_write_bytes(stream, context->contextBlob.buffer,
context->contextBlob.size);
if (!result) {
LOG_ERR("Could not write contextBlob buffer");
}
/* result is set by file_write_bytes() */
out:
return result;
}
这里要列出一下TPMS_CONTEXT的定义,在/usr/local/include/tss2/tss2_tpm2_types.h中,如下:
/* Definition of TPMS_CONTEXT Structure */
typedef struct TPMS_CONTEXT TPMS_CONTEXT;
struct TPMS_CONTEXT {
UINT64 sequence; /* the sequence number of the context. NOTE Transient object contexts and session contexts used different counters. */
TPMI_DH_CONTEXT savedHandle; /* a handle indicating if the context is a session object or sequence objectSee Context Handle Values */
TPMI_RH_HIERARCHY hierarchy; /* the hierarchy of the context */
TPM2B_CONTEXT_DATA contextBlob; /* the context data and integrity HMAC */
};
至此,process_output函数的整体流程就基本分析完了。也就意味着tpm2_tool_onrun函数全部分析完了。