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 1bool 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;}// UINT32result = 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);// UINT64result = files_write_64(stream, context->sequence);if (!result) {LOG_ERR("Could not write sequence");goto out;}// U16 LENGTHresult = files_write_16(stream, context->contextBlob.size);if (!result) {LOG_ERR("Could not write contextBob size");goto out;}// BYTE[] contextBlobresult = 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函数全部分析完了。