tpm2_getrandom.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. /* SPDX-License-Identifier: BSD-3-Clause */
  2. #include <errno.h>
  3. #include <inttypes.h>
  4. #include <stdbool.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include "files.h"
  9. #include "log.h"
  10. #include "tpm2.h"
  11. #include "tpm2_capability.h"
  12. #include "tpm2_tool.h"
  13. #include "tpm2_alg_util.h"
  14. #include "tpm2_util.h"
  15. typedef struct tpm_random_ctx tpm_random_ctx;
  16. #define MAX_AUX_SESSIONS 3
  17. #define MAX_SESSIONS 3
  18. struct tpm_random_ctx {
  19. /*
  20. * Input options
  21. */
  22. UINT16 num_of_bytes;
  23. bool force;
  24. bool hex;
  25. /*
  26. * Outputs
  27. */
  28. char *output_file;
  29. TPM2B_DIGEST *random_bytes;
  30. /*
  31. * Parameter hashes
  32. */
  33. const char *cp_hash_path;
  34. TPM2B_DIGEST cp_hash;
  35. const char *rp_hash_path;
  36. TPM2B_DIGEST rp_hash;
  37. TPMI_ALG_HASH parameter_hash_algorithm;
  38. bool is_command_dispatch;
  39. /*
  40. * Aux Sessions
  41. */
  42. uint8_t aux_session_cnt;
  43. tpm2_session *aux_session[MAX_AUX_SESSIONS];
  44. const char *aux_session_path[MAX_AUX_SESSIONS];
  45. ESYS_TR aux_session_handle[MAX_AUX_SESSIONS];
  46. };
  47. static tpm_random_ctx ctx = {
  48. .aux_session_handle[0] = ESYS_TR_NONE,
  49. .aux_session_handle[1] = ESYS_TR_NONE,
  50. .aux_session_handle[2] = ESYS_TR_NONE,
  51. .parameter_hash_algorithm = TPM2_ALG_ERROR,
  52. };
  53. static tool_rc get_random(ESYS_CONTEXT *ectx) {
  54. /*
  55. * 1. TPM2_CC_<command> OR Retrieve cpHash
  56. */
  57. tool_rc rc = tpm2_getrandom(ectx, ctx.num_of_bytes, &ctx.random_bytes,
  58. &ctx.cp_hash, &ctx.rp_hash, ctx.aux_session_handle[0],
  59. ctx.aux_session_handle[1], ctx.aux_session_handle[2],
  60. ctx.parameter_hash_algorithm);
  61. if (rc != tool_rc_success) {
  62. LOG_ERR("Failed getrandom");
  63. }
  64. return rc;
  65. }
  66. static tool_rc process_outputs(void) {
  67. /*
  68. * 1. Outputs that do not require TPM2_CC_<command> dispatch
  69. */
  70. bool is_file_op_success = true;
  71. if (ctx.cp_hash_path) {
  72. is_file_op_success = files_save_digest(&ctx.cp_hash, ctx.cp_hash_path);
  73. if (!is_file_op_success) {
  74. return tool_rc_general_error;
  75. }
  76. }
  77. if (!ctx.is_command_dispatch) {
  78. return tool_rc_success;
  79. }
  80. /*
  81. * 2. Outputs generated after TPM2_CC_<command> dispatch
  82. */
  83. /* ensure we got the expected number of bytes unless force is set */
  84. tool_rc rc = tool_rc_success;
  85. if (!ctx.force && ctx.random_bytes->size != ctx.num_of_bytes) {
  86. LOG_ERR("Got %"PRIu16" bytes, expected: %"PRIu16"\n"
  87. "Lower your requested amount or"
  88. " use --force to override this behavior",
  89. ctx.random_bytes->size, ctx.num_of_bytes);
  90. rc = tool_rc_general_error;
  91. goto out_skip_output_file;
  92. }
  93. /*
  94. * Either open an output file, or if stdout, do nothing as -Q
  95. * was specified.
  96. */
  97. FILE *out = stdout;
  98. if (ctx.output_file) {
  99. out = fopen(ctx.output_file, "wb+");
  100. if (!out) {
  101. LOG_ERR("Could not open output file \"%s\", error: %s",
  102. ctx.output_file, strerror(errno));
  103. rc = tool_rc_general_error;
  104. goto out;
  105. }
  106. } else if (!output_enabled) {
  107. goto out;
  108. }
  109. if (ctx.hex) {
  110. tpm2_util_print_tpm2b2(out, ctx.random_bytes);
  111. goto out;
  112. }
  113. is_file_op_success = files_write_bytes(out, ctx.random_bytes->buffer,
  114. ctx.random_bytes->size);
  115. if (!is_file_op_success) {
  116. rc = tool_rc_general_error;
  117. goto out;
  118. }
  119. if(ctx.rp_hash_path) {
  120. is_file_op_success = files_save_digest(&ctx.rp_hash, ctx.rp_hash_path);
  121. if (!is_file_op_success) {
  122. rc = tool_rc_general_error;
  123. }
  124. }
  125. out:
  126. if (out && out != stdout) {
  127. fclose(out);
  128. }
  129. out_skip_output_file:
  130. if (!ctx.cp_hash_path) {
  131. free(ctx.random_bytes);
  132. }
  133. return rc;
  134. }
  135. static tool_rc get_max_random(ESYS_CONTEXT *ectx, UINT32 *value) {
  136. TPMS_CAPABILITY_DATA *cap_data = NULL;
  137. tool_rc rc = tpm2_capability_get(ectx, TPM2_CAP_TPM_PROPERTIES,
  138. TPM2_PT_FIXED, TPM2_MAX_TPM_PROPERTIES, &cap_data);
  139. if (rc != tool_rc_success) {
  140. return rc;
  141. }
  142. UINT32 i;
  143. for (i = 0; i < cap_data->data.tpmProperties.count; i++) {
  144. TPMS_TAGGED_PROPERTY *p = &cap_data->data.tpmProperties.tpmProperty[i];
  145. if (p->property == TPM2_PT_MAX_DIGEST) {
  146. *value = p->value;
  147. free(cap_data);
  148. return tool_rc_success;
  149. }
  150. }
  151. LOG_ERR("TPM does not have property TPM2_PT_MAX_DIGEST");
  152. free(cap_data);
  153. return tool_rc_general_error;
  154. }
  155. static tool_rc process_inputs(ESYS_CONTEXT *ectx) {
  156. /*
  157. * 1. Object and auth initializations
  158. */
  159. /*
  160. * 1.a Add the new-auth values to be set for the object.
  161. */
  162. /*
  163. * 1.b Add object names and their auth sessions
  164. * Note: Old-auth value is ignored when calculating cpHash.
  165. */
  166. /*
  167. * 2. Restore auxiliary sessions
  168. */
  169. tool_rc rc = tpm2_util_aux_sessions_setup(ectx, ctx.aux_session_cnt,
  170. ctx.aux_session_path, ctx.aux_session_handle, ctx.aux_session);
  171. if (rc != tool_rc_success) {
  172. return rc;
  173. }
  174. /*
  175. * 3. Command specific initializations
  176. */
  177. /*
  178. * Error if bytes requested is bigger than max hash size, which is what TPMs
  179. * should bound their requests by and always have available per the spec.
  180. *
  181. * Per 16.1 of:
  182. * - https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-3-Commands-01.38.pdf
  183. *
  184. * Allow the force flag to override this behavior.
  185. */
  186. if (!ctx.force) {
  187. UINT32 max = 0;
  188. rc = get_max_random(ectx, &max);
  189. if (rc != tool_rc_success) {
  190. return rc;
  191. }
  192. if (ctx.num_of_bytes > max) {
  193. LOG_ERR("TPM getrandom is bounded by max hash size, which is: "
  194. "%"PRIu32"\n"
  195. "Please lower your request (preferred) and try again or"
  196. " use --force (advanced)", max);
  197. return tool_rc_general_error;
  198. }
  199. }
  200. /*
  201. * 4. Configuration for calculating the pHash
  202. */
  203. /*
  204. * 4.a Determine pHash length and alg
  205. */
  206. tpm2_session *all_sessions[MAX_SESSIONS] = {
  207. ctx.aux_session[0],
  208. ctx.aux_session[1],
  209. ctx.aux_session[2]
  210. };
  211. const char **cphash_path = ctx.cp_hash_path ? &ctx.cp_hash_path : 0;
  212. const char **rphash_path = ctx.rp_hash_path ? &ctx.rp_hash_path : 0;
  213. ctx.parameter_hash_algorithm = tpm2_util_calculate_phash_algorithm(ectx,
  214. cphash_path, &ctx.cp_hash, rphash_path, &ctx.rp_hash, all_sessions);
  215. /*
  216. * 4.b Determine if TPM2_CC_<command> is to be dispatched
  217. * !rphash && !cphash [Y]
  218. * !rphash && cphash [N]
  219. * rphash && !cphash [Y]
  220. * rphash && cphash [Y]
  221. */
  222. ctx.is_command_dispatch = (ctx.cp_hash_path && !ctx.rp_hash_path) ?
  223. false : true;
  224. return rc;
  225. }
  226. static bool on_option(char key, char *value) {
  227. UNUSED(key);
  228. switch (key) {
  229. case 'f':
  230. ctx.force = true;
  231. break;
  232. case 'o':
  233. ctx.output_file = value;
  234. break;
  235. case 0:
  236. ctx.hex = true;
  237. break;
  238. case 1:
  239. ctx.cp_hash_path = value;
  240. break;
  241. case 2:
  242. ctx.rp_hash_path = value;
  243. break;
  244. case 'S':
  245. ctx.aux_session_path[ctx.aux_session_cnt] = value;
  246. if (ctx.aux_session_cnt < MAX_AUX_SESSIONS) {
  247. ctx.aux_session_cnt++;
  248. } else {
  249. LOG_ERR("Specify a max of 3 sessions");
  250. return false;
  251. }
  252. break;
  253. /* no default */
  254. }
  255. return true;
  256. }
  257. static bool on_args(int argc, char **argv) {
  258. if (argc > 1) {
  259. LOG_ERR("Only supports one SIZE octets, got: %d", argc);
  260. return false;
  261. }
  262. bool result = tpm2_util_string_to_uint16(argv[0], &ctx.num_of_bytes);
  263. if (!result) {
  264. LOG_ERR("Error converting size to a number, got: \"%s\".", argv[0]);
  265. return false;
  266. }
  267. return true;
  268. }
  269. static bool tpm2_tool_onstart(tpm2_options **opts) {
  270. const struct option topts[] = {
  271. { "output", required_argument, NULL, 'o' },
  272. { "force", required_argument, NULL, 'f' },
  273. { "hex", no_argument, NULL, 0 },
  274. { "session", required_argument, NULL, 'S' },
  275. { "cphash", required_argument, NULL, 1 },
  276. { "rphash", required_argument, NULL, 2 }
  277. };
  278. *opts = tpm2_options_new("S:o:f", ARRAY_LEN(topts), topts, on_option, on_args,
  279. 0);
  280. return *opts != NULL;
  281. }
  282. static tool_rc tpm2_tool_onrun(ESYS_CONTEXT *ectx, tpm2_option_flags flags) {
  283. UNUSED(flags);
  284. /*
  285. * 1. Process options
  286. */
  287. /*
  288. * 2. Process inputs
  289. */
  290. tool_rc rc = process_inputs(ectx);
  291. if (rc != tool_rc_success) {
  292. return rc;
  293. }
  294. /*
  295. * 3. TPM2_CC_<command> call
  296. */
  297. rc = get_random(ectx);
  298. if (rc != tool_rc_success) {
  299. return rc;
  300. }
  301. /*
  302. * 4. Process outputs
  303. */
  304. return process_outputs();
  305. }
  306. static tool_rc tpm2_tool_onstop(ESYS_CONTEXT *ectx) {
  307. UNUSED(ectx);
  308. /*
  309. * 1. Free objects
  310. */
  311. /*
  312. * 2. Close authorization sessions
  313. */
  314. /*
  315. * 3. Close auxiliary sessions
  316. */
  317. tool_rc rc = tool_rc_success;
  318. tool_rc tmp_rc = tool_rc_success;
  319. size_t i = 0;
  320. for(i = 0; i < ctx.aux_session_cnt; i++) {
  321. if (ctx.aux_session_path[i]) {
  322. tmp_rc = tpm2_session_close(&ctx.aux_session[i]);
  323. }
  324. if (tmp_rc != tool_rc_success) {
  325. rc = tmp_rc;
  326. }
  327. }
  328. return rc;
  329. }
  330. // Register this tool with tpm2_tool.c
  331. TPM2_TOOL_REGISTER("getrandom", tpm2_tool_onstart, tpm2_tool_onrun,
  332. tpm2_tool_onstop, NULL)