tpm2_create.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. /* SPDX-License-Identifier: BSD-3-Clause */
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include "files.h"
  5. #include "log.h"
  6. #include "pcr.h"
  7. #include "tpm2.h"
  8. #include "tpm2_tool.h"
  9. #include "tpm2_alg_util.h"
  10. #include "tpm2_auth_util.h"
  11. #include "tpm2_options.h"
  12. typedef struct tpm_create_ctx tpm_create_ctx;
  13. #define MAX_AUX_SESSIONS 2
  14. #define MAX_SESSIONS 3
  15. struct tpm_create_ctx {
  16. /*
  17. * Inputs
  18. */
  19. struct {
  20. const char *ctx_path;
  21. const char *auth_str;
  22. tpm2_loaded_object object;
  23. } parent;
  24. struct {
  25. char *sealed_data;
  26. char *auth_str;
  27. TPM2B_SENSITIVE_CREATE sensitive;
  28. TPM2B_PUBLIC in_public;
  29. TPM2B_DATA outside_info;
  30. TPML_PCR_SELECTION creation_pcr;
  31. char *outside_info_data;
  32. char *alg;
  33. char *attrs;
  34. char *name_alg;
  35. char *policy;
  36. bool is_object_alg_specified;
  37. bool is_sealing_input_specified;
  38. /*
  39. * Outputs
  40. */
  41. char *public_path;
  42. TPM2B_PUBLIC *out_public;
  43. const char *ctx_path;
  44. char *private_path;
  45. TPM2B_PRIVATE *out_private;
  46. char *creation_data_file;
  47. TPM2B_CREATION_DATA *creation_data;
  48. char *creation_hash_file;
  49. TPM2B_DIGEST *creation_hash;
  50. char *creation_ticket_file;
  51. TPMT_TK_CREATION *creation_ticket;
  52. char *template_data_path;
  53. ESYS_TR object_handle;
  54. } object;
  55. bool is_createloaded;
  56. /*
  57. * Parameter hashes
  58. */
  59. const char *cp_hash_path;
  60. TPM2B_DIGEST cp_hash;
  61. const char *rp_hash_path;
  62. TPM2B_DIGEST rp_hash;
  63. TPMI_ALG_HASH parameter_hash_algorithm;
  64. bool is_command_dispatch;
  65. /*
  66. * Aux sessions
  67. */
  68. uint8_t aux_session_cnt;
  69. tpm2_session *aux_session[MAX_AUX_SESSIONS];
  70. const char *aux_session_path[MAX_AUX_SESSIONS];
  71. ESYS_TR aux_session_handle[MAX_AUX_SESSIONS];
  72. };
  73. #define DEFAULT_KEY_ALG "rsa2048"
  74. static tpm_create_ctx ctx = {
  75. .object = { .alg = DEFAULT_KEY_ALG },
  76. .object.creation_pcr = { .count = 0 },
  77. .aux_session_handle[0] = ESYS_TR_NONE,
  78. .aux_session_handle[1] = ESYS_TR_NONE,
  79. .object.object_handle = ESYS_TR_NONE,
  80. .cp_hash.size = 0,
  81. .is_command_dispatch = true,
  82. .object.outside_info.size = 0,
  83. .parameter_hash_algorithm = TPM2_ALG_ERROR,
  84. };
  85. static bool load_outside_info(TPM2B_DATA *outside_info) {
  86. outside_info->size = sizeof(outside_info->buffer);
  87. return tpm2_util_bin_from_hex_or_file(ctx.object.outside_info_data,
  88. &outside_info->size, outside_info->buffer);
  89. }
  90. static tool_rc create(ESYS_CONTEXT *ectx) {
  91. /*
  92. * 1. TPM2_CC_<command> OR Retrieve cpHash
  93. */
  94. /* TPM2_CC_CreateLoaded */
  95. if (ctx.is_createloaded) {
  96. size_t offset = 0;
  97. TPM2B_TEMPLATE template = { .size = 0 };
  98. tool_rc tmp_rc = tpm2_mu_tpmt_public_marshal(
  99. &ctx.object.in_public.publicArea, &template.buffer[0],
  100. sizeof(TPMT_PUBLIC), &offset);
  101. if (tmp_rc != tool_rc_success) {
  102. return tmp_rc;
  103. }
  104. template.size = offset;
  105. tmp_rc = tpm2_create_loaded(ectx, &ctx.parent.object,
  106. &ctx.object.sensitive, &template, &ctx.object.object_handle,
  107. &ctx.object.out_private, &ctx.object.out_public, &ctx.cp_hash,
  108. &ctx.rp_hash, ctx.parameter_hash_algorithm,
  109. ctx.aux_session_handle[0], ctx.aux_session_handle[1]);
  110. if (tmp_rc != tool_rc_success) {
  111. return tmp_rc;
  112. }
  113. }
  114. /* TPM2_CC_Create */
  115. if (!ctx.is_createloaded) {
  116. /*
  117. * Outside data is optional. If not specified default to 0
  118. */
  119. bool result = ctx.object.outside_info_data ?
  120. load_outside_info(&ctx.object.outside_info) : true;
  121. if (!result) {
  122. return tool_rc_general_error;
  123. }
  124. tool_rc tmp_rc = tpm2_create(ectx, &ctx.parent.object,
  125. &ctx.object.sensitive, &ctx.object.in_public,
  126. &ctx.object.outside_info, &ctx.object.creation_pcr,
  127. &ctx.object.out_private, &ctx.object.out_public,
  128. &ctx.object.creation_data, &ctx.object.creation_hash,
  129. &ctx.object.creation_ticket, &ctx.cp_hash, &ctx.rp_hash,
  130. ctx.parameter_hash_algorithm, ctx.aux_session_handle[0],
  131. ctx.aux_session_handle[1]);
  132. if (tmp_rc != tool_rc_success) {
  133. return tmp_rc;
  134. }
  135. }
  136. return tool_rc_success;
  137. }
  138. #define DEFAULT_ATTRS \
  139. TPMA_OBJECT_DECRYPT|TPMA_OBJECT_SIGN_ENCRYPT|TPMA_OBJECT_FIXEDTPM \
  140. |TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN \
  141. |TPMA_OBJECT_USERWITHAUTH
  142. static void setup_attributes(TPMA_OBJECT *attrs) {
  143. if (ctx.object.is_sealing_input_specified && !ctx.object.attrs) {
  144. *attrs &= ~TPMA_OBJECT_SIGN_ENCRYPT;
  145. *attrs &= ~TPMA_OBJECT_DECRYPT;
  146. *attrs &= ~TPMA_OBJECT_SENSITIVEDATAORIGIN;
  147. }
  148. if (!ctx.object.is_sealing_input_specified && !ctx.object.attrs &&
  149. !strncmp("hmac", ctx.object.alg, 4)) {
  150. *attrs &= ~TPMA_OBJECT_DECRYPT;
  151. }
  152. if (!ctx.object.attrs && ctx.object.policy && !ctx.object.auth_str) {
  153. *attrs &= ~TPMA_OBJECT_USERWITHAUTH;
  154. }
  155. }
  156. static tool_rc process_output(ESYS_CONTEXT *ectx) {
  157. /*
  158. * 1. Outputs that do not require TPM2_CC_<command> dispatch
  159. */
  160. bool is_file_op_success = true;
  161. if (ctx.is_createloaded && ctx.object.template_data_path) {
  162. is_file_op_success = files_save_template(
  163. &ctx.object.in_public.publicArea, ctx.object.template_data_path);
  164. if (!is_file_op_success) {
  165. LOG_ERR("Could not save public template to file.");
  166. return tool_rc_general_error;
  167. }
  168. }
  169. if (ctx.cp_hash_path) {
  170. is_file_op_success = files_save_digest(&ctx.cp_hash, ctx.cp_hash_path);
  171. if (!is_file_op_success) {
  172. LOG_ERR("Failed to save cp hash");
  173. return tool_rc_general_error;
  174. }
  175. }
  176. if (!ctx.is_command_dispatch) {
  177. return tool_rc_success;
  178. }
  179. /*
  180. * 2. Outputs generated after TPM2_CC_<command> dispatch
  181. */
  182. /* TPM2_CC_Create outputs */
  183. tool_rc rc = tool_rc_success;
  184. if (!ctx.is_createloaded && ctx.object.creation_data_file &&
  185. ctx.object.creation_data->size) {
  186. is_file_op_success = files_save_creation_data(ctx.object.creation_data,
  187. ctx.object.creation_data_file);
  188. if (!is_file_op_success) {
  189. LOG_ERR("Failed saving creation data.");
  190. rc = tool_rc_general_error;
  191. goto create_out;
  192. }
  193. }
  194. if (!ctx.is_createloaded && ctx.object.creation_ticket_file &&
  195. ctx.object.creation_ticket->digest.size) {
  196. is_file_op_success = files_save_creation_ticket(
  197. ctx.object.creation_ticket, ctx.object.creation_ticket_file);
  198. if (!is_file_op_success) {
  199. LOG_ERR("Failed saving creation ticket.");
  200. rc = tool_rc_general_error;
  201. goto create_out;
  202. }
  203. }
  204. if (!ctx.is_createloaded && ctx.object.creation_hash_file &&
  205. ctx.object.creation_hash->size) {
  206. is_file_op_success = files_save_digest(ctx.object.creation_hash,
  207. ctx.object.creation_hash_file);
  208. if (!is_file_op_success) {
  209. LOG_ERR("Failed saving creation hash.");
  210. rc = tool_rc_general_error;
  211. }
  212. }
  213. create_out:
  214. free(ctx.object.creation_data);
  215. free(ctx.object.creation_hash);
  216. free(ctx.object.creation_ticket);
  217. if (rc != tool_rc_success) {
  218. return rc;
  219. }
  220. /* Common- TPM2_CC_Create/ TPM2_CC_CreateLoaded outputs*/
  221. tpm2_util_public_to_yaml(ctx.object.out_public, NULL);
  222. if (ctx.object.public_path) {
  223. is_file_op_success = files_save_public(ctx.object.out_public,
  224. ctx.object.public_path);
  225. if (!is_file_op_success) {
  226. rc = tool_rc_general_error;
  227. goto out;
  228. }
  229. }
  230. if (ctx.object.private_path) {
  231. is_file_op_success = files_save_private(ctx.object.out_private,
  232. ctx.object.private_path);
  233. if (!is_file_op_success) {
  234. rc = tool_rc_general_error;
  235. goto out;
  236. }
  237. }
  238. if (ctx.object.ctx_path) {
  239. rc = files_save_tpm_context_to_path(ectx, ctx.object.object_handle,
  240. ctx.object.ctx_path);
  241. if (rc != tool_rc_success) {
  242. goto out;
  243. }
  244. }
  245. if (ctx.rp_hash_path) {
  246. is_file_op_success = files_save_digest(&ctx.rp_hash, ctx.rp_hash_path);
  247. if (!is_file_op_success) {
  248. rc = tool_rc_general_error;
  249. }
  250. }
  251. out:
  252. free(ctx.object.out_private);
  253. free(ctx.object.out_public);
  254. return rc;
  255. }
  256. static tool_rc process_inputs(ESYS_CONTEXT *ectx) {
  257. /*
  258. * 1. Object and auth initializations
  259. */
  260. /*
  261. * 1.a Add the new-auth values to be set for the object.
  262. */
  263. tpm2_session *tmp;
  264. tool_rc rc = tpm2_auth_util_from_optarg(NULL, ctx.object.auth_str, &tmp,
  265. true);
  266. if (rc != tool_rc_success) {
  267. LOG_ERR("Invalid key authorization");
  268. return rc;
  269. }
  270. TPM2B_AUTH const *auth = tpm2_session_get_auth_value(tmp);
  271. ctx.object.sensitive.sensitive.userAuth = *auth;
  272. tpm2_session_close(&tmp);
  273. /*
  274. * 1.b Add object names and their auth sessions
  275. */
  276. rc = tpm2_util_object_load_auth(ectx, ctx.parent.ctx_path,
  277. ctx.parent.auth_str, &ctx.parent.object, false, TPM2_HANDLE_ALL_W_NV);
  278. if (rc != tool_rc_success) {
  279. return rc;
  280. }
  281. /*
  282. * 2. Restore auxiliary sessions
  283. */
  284. rc = tpm2_util_aux_sessions_setup(ectx, ctx.aux_session_cnt,
  285. ctx.aux_session_path, ctx.aux_session_handle, ctx.aux_session);
  286. if (rc != tool_rc_success) {
  287. return rc;
  288. }
  289. /*
  290. * 3. Command specific initializations
  291. */
  292. /* Setup attributes */
  293. TPMA_OBJECT attrs = DEFAULT_ATTRS;
  294. setup_attributes(&attrs);
  295. /* Initialize object */
  296. rc = tpm2_alg_util_public_init(ctx.object.alg, ctx.object.name_alg,
  297. ctx.object.attrs, ctx.object.policy, attrs, &ctx.object.in_public);
  298. if (rc != tool_rc_success) {
  299. return rc;
  300. }
  301. /* Check object validitity */
  302. if (ctx.object.is_sealing_input_specified &&
  303. ctx.object.in_public.publicArea.type != TPM2_ALG_KEYEDHASH) {
  304. LOG_ERR("Only TPM2_ALG_KEYEDHASH algorithm is allowed when sealing data");
  305. return tool_rc_general_error;
  306. }
  307. /* Check command type */
  308. if ((ctx.object.ctx_path || ctx.object.template_data_path) &&
  309. (!ctx.object.creation_data_file && !ctx.object.creation_ticket_file &&
  310. !ctx.object.creation_hash_file)) {
  311. ctx.is_createloaded = true;
  312. }
  313. /*
  314. * 4. Configuration for calculating the pHash
  315. */
  316. /*
  317. * 4.a Determine pHash length and alg
  318. */
  319. tpm2_session *all_sessions[MAX_SESSIONS] = {
  320. ctx.parent.object.session,
  321. ctx.aux_session[0],
  322. ctx.aux_session[1]
  323. };
  324. const char **cphash_path = ctx.cp_hash_path ? &ctx.cp_hash_path : 0;
  325. const char **rphash_path = ctx.rp_hash_path ? &ctx.rp_hash_path : 0;
  326. ctx.parameter_hash_algorithm = tpm2_util_calculate_phash_algorithm(ectx,
  327. cphash_path, &ctx.cp_hash, rphash_path, &ctx.rp_hash, all_sessions);
  328. /*
  329. * 4.b Determine if TPM2_CC_<command> is to be dispatched
  330. * !rphash && !cphash [Y]
  331. * !rphash && cphash [N]
  332. * rphash && !cphash [Y]
  333. * rphash && cphash [Y]
  334. */
  335. ctx.is_command_dispatch = (ctx.cp_hash_path && !ctx.rp_hash_path) ?
  336. false : true;
  337. return rc;
  338. }
  339. static tool_rc check_options(void) {
  340. if (!ctx.parent.ctx_path) {
  341. LOG_ERR("Must specify parent object via -C.");
  342. return tool_rc_option_error;
  343. }
  344. if (ctx.object.is_sealing_input_specified && ctx.object.is_object_alg_specified) {
  345. LOG_ERR("Cannot specify -G and -i together.");
  346. return tool_rc_option_error;
  347. }
  348. if (ctx.cp_hash_path && !ctx.rp_hash_path &&
  349. (ctx.object.public_path || ctx.object.private_path ||
  350. ctx.object.creation_data_file || ctx.object.creation_hash_file ||
  351. ctx.object.creation_ticket_file || ctx.object.ctx_path)) {
  352. LOG_ERR("CpHash Error: Cannot specify pub, priv, creation - data, hash, ticket");
  353. return tool_rc_option_error;
  354. }
  355. return tool_rc_success;
  356. }
  357. static bool load_sensitive(void) {
  358. ctx.object.sensitive.sensitive.data.size = BUFFER_SIZE(
  359. typeof(ctx.object.sensitive.sensitive.data), buffer);
  360. return files_load_bytes_from_buffer_or_file_or_stdin(NULL,
  361. ctx.object.sealed_data, &ctx.object.sensitive.sensitive.data.size,
  362. ctx.object.sensitive.sensitive.data.buffer);
  363. }
  364. static bool on_option(char key, char *value) {
  365. switch (key) {
  366. case 'P':
  367. ctx.parent.auth_str = value;
  368. break;
  369. case 'p':
  370. ctx.object.auth_str = value;
  371. break;
  372. case 'g':
  373. ctx.object.name_alg = value;
  374. break;
  375. case 'G':
  376. ctx.object.alg = value;
  377. ctx.object.is_object_alg_specified = true;
  378. break;
  379. case 'a':
  380. ctx.object.attrs = value;
  381. break;
  382. case 'i':
  383. ctx.object.sealed_data = strcmp("-", value) ? value : NULL;
  384. ctx.object.alg = "keyedhash";
  385. ctx.object.is_sealing_input_specified = true;
  386. bool res = load_sensitive();
  387. if (!res) {
  388. return false;
  389. }
  390. break;
  391. case 'L':
  392. ctx.object.policy = value;
  393. break;
  394. case 'u':
  395. ctx.object.public_path = value;
  396. break;
  397. case 'r':
  398. ctx.object.private_path = value;
  399. break;
  400. case 'C':
  401. ctx.parent.ctx_path = value;
  402. break;
  403. case 'c':
  404. ctx.object.ctx_path = value;
  405. break;
  406. case 0:
  407. ctx.object.creation_data_file = value;
  408. break;
  409. case 1:
  410. ctx.object.template_data_path = value;
  411. break;
  412. case 't':
  413. ctx.object.creation_ticket_file = value;
  414. break;
  415. case 'd':
  416. ctx.object.creation_hash_file = value;
  417. break;
  418. case 'q':
  419. ctx.object.outside_info_data = value;
  420. break;
  421. case 'l':
  422. if (!pcr_parse_selections(value, &ctx.object.creation_pcr)) {
  423. LOG_ERR("Could not parse pcr selections, got: \"%s\"", value);
  424. return false;
  425. }
  426. break;
  427. case 2:
  428. ctx.cp_hash_path = value;
  429. break;
  430. case 3:
  431. ctx.rp_hash_path = value;
  432. break;
  433. case 'S':
  434. ctx.aux_session_path[ctx.aux_session_cnt] = value;
  435. if (ctx.aux_session_cnt < MAX_AUX_SESSIONS) {
  436. ctx.aux_session_cnt++;
  437. } else {
  438. LOG_ERR("Specify a max of 3 sessions");
  439. return false;
  440. }
  441. break;
  442. };
  443. return true;
  444. }
  445. static bool tpm2_tool_onstart(tpm2_options **opts) {
  446. static struct option topts[] = {
  447. { "parent-auth", required_argument, NULL, 'P' },
  448. { "key-auth", required_argument, NULL, 'p' },
  449. { "hash-algorithm", required_argument, NULL, 'g' },
  450. { "key-algorithm", required_argument, NULL, 'G' },
  451. { "attributes", required_argument, NULL, 'a' },
  452. { "sealing-input", required_argument, NULL, 'i' },
  453. { "policy", required_argument, NULL, 'L' },
  454. { "public", required_argument, NULL, 'u' },
  455. { "private", required_argument, NULL, 'r' },
  456. { "parent-context", required_argument, NULL, 'C' },
  457. { "key-context", required_argument, NULL, 'c' },
  458. { "creation-data", required_argument, NULL, 0 },
  459. { "template-data", required_argument, NULL, 1 },
  460. { "creation-ticket",required_argument, NULL, 't' },
  461. { "creation-hash", required_argument, NULL, 'd' },
  462. { "outside-info", required_argument, NULL, 'q' },
  463. { "pcr-list", required_argument, NULL, 'l' },
  464. { "cphash", required_argument, NULL, 2 },
  465. { "rphash", required_argument, NULL, 3 },
  466. { "session", required_argument, NULL, 'S' },
  467. };
  468. *opts = tpm2_options_new("P:p:g:G:a:i:L:u:r:C:c:t:d:q:l:S:",
  469. ARRAY_LEN(topts), topts, on_option, NULL, 0);
  470. return *opts != NULL;
  471. }
  472. static tool_rc tpm2_tool_onrun(ESYS_CONTEXT *ectx, tpm2_option_flags flags) {
  473. UNUSED(flags);
  474. /*
  475. * 1. Process options
  476. */
  477. tool_rc rc = check_options();
  478. if (rc != tool_rc_success) {
  479. return rc;
  480. }
  481. /*
  482. * 2. Process inputs
  483. */
  484. rc = process_inputs(ectx);
  485. if (rc != tool_rc_success) {
  486. return rc;
  487. }
  488. /*
  489. * 3. TPM2_CC_<command> call
  490. */
  491. rc = create(ectx);
  492. if (rc != tool_rc_success) {
  493. return rc;
  494. }
  495. /*
  496. * 4. Process outputs
  497. */
  498. return process_output(ectx);
  499. }
  500. static tool_rc tpm2_tool_onstop(ESYS_CONTEXT *ectx) {
  501. UNUSED(ectx);
  502. /*
  503. * 1. Free objects
  504. */
  505. /*
  506. * 2. Close authorization sessions
  507. */
  508. tool_rc rc = tool_rc_success;
  509. tool_rc tmp_rc = tpm2_session_close(&ctx.parent.object.session);
  510. if (tmp_rc != tool_rc_success) {
  511. rc = tmp_rc;
  512. }
  513. /*
  514. * 3. Close auxiliary sessions
  515. */
  516. size_t i = 0;
  517. for(i = 0; i < ctx.aux_session_cnt; i++) {
  518. if (ctx.aux_session_path[i]) {
  519. tmp_rc = tpm2_session_close(&ctx.aux_session[i]);
  520. if (tmp_rc != tool_rc_success) {
  521. rc = tmp_rc;
  522. }
  523. }
  524. }
  525. return rc;
  526. }
  527. // Register this tool with tpm2_tool.c
  528. TPM2_TOOL_REGISTER("create", tpm2_tool_onstart, tpm2_tool_onrun,
  529. tpm2_tool_onstop, NULL)