tpm2_print.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. /* SPDX-License-Identifier: BSD-3-Clause */
  2. #include <inttypes.h>
  3. #include <stdbool.h>
  4. #include <stdio.h>
  5. #include <string.h>
  6. #include "files.h"
  7. #include "log.h"
  8. #include "tpm2_alg_util.h"
  9. #include "tpm2_tool.h"
  10. #include "tpm2_util.h"
  11. typedef bool (*print_fn)(FILE *f);
  12. typedef struct tpm2_print_ctx tpm2_print_ctx;
  13. struct tpm2_print_ctx {
  14. struct {
  15. const char *path;
  16. print_fn handler;
  17. } file;
  18. };
  19. static tpm2_print_ctx ctx;
  20. static void print_clock_info(TPMS_CLOCK_INFO *clock_info, size_t indent_count) {
  21. print_yaml_indent(indent_count);
  22. tpm2_tool_output("clock: %"PRIu64"\n", clock_info->clock);
  23. print_yaml_indent(indent_count);
  24. tpm2_tool_output("resetCount: %"PRIu32"\n", clock_info->resetCount);
  25. print_yaml_indent(indent_count);
  26. tpm2_tool_output("restartCount: %"PRIu32"\n", clock_info->restartCount);
  27. print_yaml_indent(indent_count);
  28. tpm2_tool_output("safe: %u\n", clock_info->safe);
  29. }
  30. static bool print_TPMS_QUOTE_INFO(TPMS_QUOTE_INFO *info, size_t indent_count) {
  31. print_yaml_indent(indent_count);
  32. tpm2_tool_output("pcrSelect:\n");
  33. print_yaml_indent(indent_count + 1);
  34. tpm2_tool_output("count: %"PRIu32"\n", info->pcrSelect.count);
  35. print_yaml_indent(indent_count + 1);
  36. tpm2_tool_output("pcrSelections:\n");
  37. // read TPML_PCR_SELECTION array (of size count)
  38. UINT32 i;
  39. for (i = 0; i < info->pcrSelect.count; ++i) {
  40. print_yaml_indent(indent_count + 2);
  41. tpm2_tool_output("%"PRIu32":\n", i);
  42. // print hash type (TPMI_ALG_HASH)
  43. const char* const hash_name = tpm2_alg_util_algtostr(
  44. info->pcrSelect.pcrSelections[i].hash,
  45. tpm2_alg_util_flags_hash);
  46. if (!hash_name) {
  47. LOG_ERR("Invalid hash type in quote");
  48. return false;
  49. }
  50. print_yaml_indent(indent_count + 3);
  51. tpm2_tool_output("hash: %"PRIu16" (%s)\n",
  52. info->pcrSelect.pcrSelections[i].hash,
  53. hash_name);
  54. print_yaml_indent(indent_count + 3);
  55. tpm2_tool_output("sizeofSelect: %"PRIu8"\n",
  56. info->pcrSelect.pcrSelections[i].sizeofSelect);
  57. // print PCR selection in hex
  58. print_yaml_indent(indent_count + 3);
  59. tpm2_tool_output("pcrSelect: ");
  60. tpm2_util_hexdump((BYTE *)&info->pcrSelect.pcrSelections[i].pcrSelect,
  61. info->pcrSelect.pcrSelections[i].sizeofSelect);
  62. tpm2_tool_output("\n");
  63. }
  64. // print digest in hex (a TPM2B object)
  65. print_yaml_indent(indent_count);
  66. tpm2_tool_output("pcrDigest: ");
  67. tpm2_util_print_tpm2b(&info->pcrDigest);
  68. tpm2_tool_output("\n");
  69. return true;
  70. }
  71. static bool print_TPMS_ATTEST(FILE* fd) {
  72. TPMS_ATTEST attest = { 0 };
  73. bool res = files_load_attest_file(fd, ctx.file.path, &attest);
  74. if (!res) {
  75. LOG_ERR("Could not parse TPMS_ATTEST file: \"%s\"", ctx.file.path);
  76. return false;
  77. }
  78. tpm2_tool_output("magic: ");
  79. /* dump these in TPM endianess (big-endian) */
  80. typeof(attest.magic) be_magic = tpm2_util_hton_32(attest.magic);
  81. tpm2_util_hexdump((const UINT8*) &be_magic,
  82. sizeof(attest.magic));
  83. tpm2_tool_output("\n");
  84. // check magic
  85. if (attest.magic != TPM2_GENERATED_VALUE) {
  86. LOG_ERR("Bad magic, got: 0x%x, expected: 0x%x",
  87. attest.magic, TPM2_GENERATED_VALUE);
  88. return false;
  89. }
  90. tpm2_tool_output("type: ");
  91. /* dump these in TPM endianess (big-endian) */
  92. typeof(attest.type) be_type = tpm2_util_hton_16(attest.type);
  93. tpm2_util_hexdump((const UINT8*) &be_type,
  94. sizeof(attest.type));
  95. tpm2_tool_output("\n");
  96. tpm2_tool_output("qualifiedSigner: ");
  97. tpm2_util_print_tpm2b(&attest.qualifiedSigner);
  98. tpm2_tool_output("\n");
  99. tpm2_tool_output("extraData: ");
  100. tpm2_util_print_tpm2b(&attest.extraData);
  101. tpm2_tool_output("\n");
  102. tpm2_tool_output("clockInfo:\n");
  103. print_clock_info(&attest.clockInfo, 1);
  104. tpm2_tool_output("firmwareVersion: ");
  105. tpm2_util_hexdump((BYTE *)&attest.firmwareVersion,
  106. sizeof(attest.firmwareVersion));
  107. tpm2_tool_output("\n");
  108. switch (attest.type) {
  109. case TPM2_ST_ATTEST_QUOTE:
  110. tpm2_tool_output("attested:\n");
  111. print_yaml_indent(1);
  112. tpm2_tool_output("quote:\n");
  113. return print_TPMS_QUOTE_INFO(&attest.attested.quote, 2);
  114. break;
  115. default:
  116. LOG_ERR("Cannot print unsupported type 0x%" PRIx16, attest.type);
  117. return false;
  118. }
  119. /* Should be unreachable */
  120. return false;
  121. }
  122. static bool print_TPMS_CONTEXT(FILE *fstream) {
  123. /*
  124. * Reading the TPMS_CONTEXT structure to disk, format:
  125. * TPM2.0-TOOLS HEADER
  126. * U32 hierarchy
  127. * U32 savedHandle
  128. * U64 sequence
  129. * U16 contextBlobLength
  130. * BYTE[] contextBlob
  131. */
  132. UINT32 version;
  133. TPMS_CONTEXT context;
  134. bool result = files_read_header(fstream, &version);
  135. if (!result) {
  136. LOG_WARN("The loaded tpm context does not appear to be in the proper "
  137. "format, assuming old format.");
  138. rewind(fstream);
  139. result = files_read_bytes(fstream, (UINT8 *) &context, sizeof(context));
  140. if (!result) {
  141. LOG_ERR("Could not load tpm context file");
  142. goto out;
  143. } else {
  144. goto print_context;
  145. }
  146. }
  147. result = files_read_32(fstream, &context.hierarchy);
  148. if (!result) {
  149. LOG_ERR("Error reading hierarchy!");
  150. goto out;
  151. }
  152. result = files_read_32(fstream, &context.savedHandle);
  153. if (!result) {
  154. LOG_ERR("Error reading savedHandle!");
  155. goto out;
  156. }
  157. result = files_read_64(fstream, &context.sequence);
  158. if (!result) {
  159. LOG_ERR("Error reading sequence!");
  160. goto out;
  161. }
  162. result = files_read_16(fstream, &context.contextBlob.size);
  163. if (!result) {
  164. LOG_ERR("Error reading contextBlob.size!");
  165. goto out;
  166. }
  167. if (context.contextBlob.size > sizeof(context.contextBlob.buffer)) {
  168. LOG_ERR("Size mismatch found on contextBlob, got %"PRIu16" expected "
  169. "less than or equal to %zu", context.contextBlob.size,
  170. sizeof(context.contextBlob.buffer));
  171. result = false;
  172. goto out;
  173. }
  174. result = files_read_bytes(fstream, context.contextBlob.buffer,
  175. context.contextBlob.size);
  176. if (!result) {
  177. LOG_ERR("Error reading contextBlob.size!");
  178. goto out;
  179. }
  180. print_context:
  181. tpm2_tool_output("version: %d\n", version);
  182. const char *hierarchy;
  183. switch (context.hierarchy) {
  184. case TPM2_RH_OWNER:
  185. hierarchy = "owner";
  186. break;
  187. case TPM2_RH_PLATFORM:
  188. hierarchy = "platform";
  189. break;
  190. case TPM2_RH_ENDORSEMENT:
  191. hierarchy = "endorsement";
  192. break;
  193. case TPM2_RH_NULL:
  194. default:
  195. hierarchy = "null";
  196. break;
  197. }
  198. tpm2_tool_output("hierarchy: %s\n", hierarchy);
  199. tpm2_tool_output("handle: 0x%X (%u)\n", context.savedHandle,
  200. context.savedHandle);
  201. tpm2_tool_output("sequence: %"PRIu64"\n", context.sequence);
  202. tpm2_tool_output("contextBlob: \n");
  203. tpm2_tool_output("\tsize: %d\n", context.contextBlob.size);
  204. result = true;
  205. out:
  206. return result;
  207. }
  208. static bool print_TPMT_PUBLIC(FILE *fstream) {
  209. TPMT_PUBLIC public = { 0 };
  210. bool res = files_load_template_file(fstream, ctx.file.path, &public);
  211. if (!res) {
  212. return res;
  213. }
  214. tpm2_util_tpmt_public_to_yaml(&public, NULL);
  215. return true;
  216. }
  217. static bool print_TPM2B_PUBLIC(FILE *fstream) {
  218. TPM2B_PUBLIC public = { 0 };
  219. bool res = files_load_public_file(fstream, ctx.file.path, &public);
  220. if (!res) {
  221. return res;
  222. }
  223. tpm2_util_public_to_yaml(&public, NULL);
  224. return true;
  225. }
  226. #define ADD_HANDLER(type) { .name = #type, .fn = print_##type }
  227. static bool handle_type(const char *name) {
  228. static const struct {
  229. const char *name;
  230. print_fn fn;
  231. } handlers[] = {
  232. ADD_HANDLER(TPMS_ATTEST),
  233. ADD_HANDLER(TPMS_CONTEXT),
  234. ADD_HANDLER(TPM2B_PUBLIC),
  235. ADD_HANDLER(TPMT_PUBLIC)
  236. };
  237. size_t i;
  238. for (i=0; i < ARRAY_LEN(handlers); i++) {
  239. if (!strcmp(name, handlers[i].name)) {
  240. ctx.file.handler = handlers[i].fn;
  241. return true;
  242. }
  243. }
  244. LOG_ERR("Unknown file type, got: \"%s\"", name);
  245. return false;
  246. }
  247. static bool on_option(char key, char *value) {
  248. switch (key) {
  249. case 't':
  250. return handle_type(value);
  251. case 'i':
  252. ctx.file.path = value;
  253. break;
  254. default:
  255. LOG_ERR("Invalid option %c", key);
  256. return false;
  257. }
  258. return true;
  259. }
  260. static bool on_arg(int argc, char *argv[]) {
  261. if (argc != 1) {
  262. LOG_ERR("Expected single file path argument");
  263. return false;
  264. }
  265. ctx.file.path = argv[0];
  266. return true;
  267. }
  268. static bool tpm2_tool_onstart(tpm2_options **opts) {
  269. static const struct option topts[] = {
  270. { "type", required_argument, NULL, 't' },
  271. };
  272. *opts = tpm2_options_new("t:", ARRAY_LEN(topts), topts, on_option, on_arg,
  273. TPM2_OPTIONS_NO_SAPI);
  274. return *opts != NULL;
  275. }
  276. static tool_rc tpm2_tool_onrun(ESYS_CONTEXT *ectx, tpm2_option_flags flags) {
  277. UNUSED(ectx);
  278. UNUSED(flags);
  279. FILE* fd = stdin;
  280. if (!ctx.file.handler) {
  281. /*
  282. * TODO: This could be automated by each
  283. * type having an interrogation function. If it passes,
  284. * then use the associated handler. For now, make -t
  285. * mandatory.
  286. */
  287. LOG_ERR("Must specify -t/--type");
  288. return tool_rc_general_error;
  289. }
  290. if (ctx.file.path) {
  291. LOG_INFO("Reading from file %s", ctx.file.path);
  292. fd = fopen(ctx.file.path, "rb");
  293. if (!fd) {
  294. LOG_ERR("Could not open file %s", ctx.file.path);
  295. return tool_rc_general_error;
  296. }
  297. } else {
  298. LOG_INFO("Reading from stdin");
  299. }
  300. bool res = ctx.file.handler(fd);
  301. LOG_INFO("Read %ld bytes from file %s", ftell(fd), ctx.file.path);
  302. if (fd != stdin) {
  303. fclose(fd);
  304. }
  305. return res ? tool_rc_success : tool_rc_general_error;
  306. }
  307. // Register this tool with tpm2_tool.c
  308. TPM2_TOOL_REGISTER("print", tpm2_tool_onstart, tpm2_tool_onrun, NULL, NULL)