tpm2_loadexternal.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. /* SPDX-License-Identifier: BSD-3-Clause */
  2. #include <assert.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <openssl/rand.h>
  6. #include "files.h"
  7. #include "log.h"
  8. #include "tpm2.h"
  9. #include "tpm2_alg_util.h"
  10. #include "tpm2_attr_util.h"
  11. #include "tpm2_auth_util.h"
  12. #include "tpm2_hierarchy.h"
  13. #include "tpm2_openssl.h"
  14. #include "tpm2_tool.h"
  15. #define BASE_DEFAULT_ATTRS \
  16. (TPMA_OBJECT_DECRYPT | TPMA_OBJECT_SIGN_ENCRYPT)
  17. #define DEFAULT_NAME_ALG TPM2_ALG_SHA256
  18. typedef struct tpm_loadexternal_ctx tpm_loadexternal_ctx;
  19. struct tpm_loadexternal_ctx {
  20. char *context_file_path;
  21. TPMI_RH_HIERARCHY hierarchy_value;
  22. ESYS_TR handle;
  23. char *public_key_path; /* path to the public portion of an object */
  24. char *private_key_path; /* path to the private portion of an object */
  25. char *attrs; /* The attributes to use */
  26. char *auth; /* The password for use of the private portion */
  27. char *policy; /* a policy for use of the private portion */
  28. char *name_alg; /* name hashing algorithm */
  29. char *key_type; /* type of key attempting to load, defaults to an auto attempt */
  30. char *name_path; /* An optional path to output the loaded objects name information to */
  31. char *passin; /* an optional auth string for the input key file for OSSL */
  32. };
  33. static tpm_loadexternal_ctx ctx = {
  34. /*
  35. * default to the NULL hierarchy, as the tpm rejects loading a private
  36. * portion of an object in other hierarchies.
  37. */
  38. .hierarchy_value = TPM2_RH_NULL,
  39. };
  40. static tool_rc load_external(ESYS_CONTEXT *ectx, TPM2B_PUBLIC *pub,
  41. TPM2B_SENSITIVE *priv, bool has_priv, TPM2B_NAME **name) {
  42. uint32_t hierarchy;
  43. TSS2_RC rval = fix_esys_hierarchy(ctx.hierarchy_value, &hierarchy);
  44. if (rval != TSS2_RC_SUCCESS) {
  45. LOG_ERR("Unknown hierarchy");
  46. return tool_rc_from_tpm(rval);
  47. }
  48. tool_rc rc = tpm2_loadexternal(ectx,
  49. has_priv ? priv : NULL, pub,
  50. hierarchy, &ctx.handle);
  51. if (rc != tool_rc_success) {
  52. return rc;
  53. }
  54. return tpm2_tr_get_name(ectx, ctx.handle, name);
  55. }
  56. static bool on_option(char key, char *value) {
  57. bool result;
  58. switch (key) {
  59. case 'C':
  60. result = tpm2_util_handle_from_optarg(value, &ctx.hierarchy_value,
  61. TPM2_HANDLE_FLAGS_ALL_HIERACHIES);
  62. if (!result) {
  63. return false;
  64. }
  65. break;
  66. case 'u':
  67. ctx.public_key_path = value;
  68. break;
  69. case 'r':
  70. ctx.private_key_path = value;
  71. break;
  72. case 'c':
  73. ctx.context_file_path = value;
  74. break;
  75. case 'a':
  76. ctx.attrs = value;
  77. break;
  78. case 'p':
  79. ctx.auth = value;
  80. break;
  81. case 'L':
  82. ctx.policy = value;
  83. break;
  84. case 'g':
  85. ctx.name_alg = value;
  86. break;
  87. case 'G':
  88. ctx.key_type = value;
  89. break;
  90. case 'n':
  91. ctx.name_path = value;
  92. break;
  93. case 0:
  94. ctx.passin = value;
  95. break;
  96. }
  97. return true;
  98. }
  99. static bool tpm2_tool_onstart(tpm2_options **opts) {
  100. const struct option topts[] = {
  101. { "hierarchy", required_argument, NULL, 'C'},
  102. { "public", required_argument, NULL, 'u'},
  103. { "private", required_argument, NULL, 'r'},
  104. { "key-context", required_argument, NULL, 'c'},
  105. { "attributes", required_argument, NULL, 'a'},
  106. { "policy", required_argument, NULL, 'L'},
  107. { "auth", required_argument, NULL, 'p'},
  108. { "hash-algorithm", required_argument, NULL, 'g'},
  109. { "key-algorithm", required_argument, NULL, 'G'},
  110. { "name", required_argument, NULL, 'n'},
  111. { "passin", required_argument, NULL, 0 },
  112. };
  113. *opts = tpm2_options_new("C:u:r:c:a:p:L:g:G:n:", ARRAY_LEN(topts), topts,
  114. on_option, NULL, 0);
  115. return *opts != NULL;
  116. }
  117. static tool_rc tpm2_tool_onrun(ESYS_CONTEXT *ectx, tpm2_option_flags flags) {
  118. UNUSED(flags);
  119. if (!ctx.public_key_path && !ctx.private_key_path) {
  120. LOG_ERR("Expected either -r or -u options");
  121. return tool_rc_option_error;
  122. }
  123. if (!ctx.context_file_path) {
  124. LOG_ERR("Expected -c option");
  125. return tool_rc_option_error;
  126. }
  127. /*
  128. * We only load a TSS format for the public portion, so if
  129. * someone hands us a public file, we'll assume the TSS format when
  130. * no -G is specified.
  131. *
  132. * If they specify a private they need to tell us the type we expect.
  133. * This helps reduce auto-guess complexity, as well as future proofing
  134. * us for being able to load XOR. Ie we don't want to guess XOR or HMAC
  135. * in leui of AES or vice versa.
  136. */
  137. if (!ctx.key_type && ctx.private_key_path) {
  138. LOG_ERR("Expected key type via -G option when specifying private"
  139. " portion of object");
  140. return tool_rc_option_error;
  141. }
  142. TPMI_ALG_PUBLIC alg = TPM2_ALG_NULL;
  143. if (ctx.key_type) {
  144. alg = tpm2_alg_util_from_optarg(ctx.key_type,
  145. tpm2_alg_util_flags_asymmetric | tpm2_alg_util_flags_symmetric);
  146. if (alg == TPM2_ALG_ERROR) {
  147. LOG_ERR("Unsupported key type, got: \"%s\"", ctx.key_type);
  148. return tool_rc_general_error;
  149. }
  150. }
  151. /*
  152. * Modifying this init to anything NOT 0 requires
  153. * the memset/reinit on the case of specified -u
  154. * and found public data in private.
  155. */
  156. TPM2B_PUBLIC pub = {
  157. . size = 0,
  158. .publicArea = {
  159. .authPolicy = { .size = 0 },
  160. },
  161. };
  162. /*
  163. * set up the public attributes with a default.
  164. * This can be cleared by load_public() if a TSS
  165. * object is provided.
  166. */
  167. if (ctx.attrs) {
  168. bool result = tpm2_attr_util_obj_from_optarg(ctx.attrs,
  169. &pub.publicArea.objectAttributes);
  170. if (!result) {
  171. return tool_rc_general_error;
  172. }
  173. } else {
  174. /*
  175. * Default to the BASE attributes, but add in USER_WITH_AUTH if -p is specified
  176. * or NO -L. Where -L is a specified policy and -p is a specified password.
  177. * Truth Table:
  178. * -L -p | Result
  179. * --------------
  180. * 0 0 | 1 (set USER_WITH_AUTH)
  181. * 0 1 | 0 (don't set USER_WITH_AUTH) <-- we want this case.
  182. * 1 0 | 1
  183. * 1 1 | 1
  184. *
  185. * This is an if/then truth table, we want to execute setting USER_WITH_AUTH on
  186. * it's negation.
  187. */
  188. pub.publicArea.objectAttributes = BASE_DEFAULT_ATTRS;
  189. if (!(ctx.policy && !ctx.auth)) {
  190. pub.publicArea.objectAttributes |= TPMA_OBJECT_USERWITHAUTH;
  191. }
  192. }
  193. /*
  194. * Set the policy for public, again this can be overridden if the
  195. * object is a TSS object
  196. */
  197. if (ctx.policy) {
  198. pub.publicArea.authPolicy.size =
  199. sizeof(pub.publicArea.authPolicy.buffer);
  200. bool res = files_load_bytes_from_path(ctx.policy,
  201. pub.publicArea.authPolicy.buffer,
  202. &pub.publicArea.authPolicy.size);
  203. if (!res) {
  204. return tool_rc_general_error;
  205. }
  206. }
  207. /*
  208. * Set the name alg, again this gets wipped on a TSS object
  209. */
  210. pub.publicArea.nameAlg =
  211. ctx.name_alg ?
  212. tpm2_alg_util_from_optarg(ctx.name_alg,
  213. tpm2_alg_util_flags_hash
  214. | tpm2_alg_util_flags_misc) :
  215. DEFAULT_NAME_ALG;
  216. if (pub.publicArea.nameAlg == TPM2_ALG_ERROR) {
  217. LOG_ERR("Invalid name hashing algorithm, got: \"%s\"", ctx.name_alg);
  218. return tool_rc_general_error;
  219. }
  220. /*
  221. * Set the AUTH value for sensitive portion
  222. */
  223. TPM2B_SENSITIVE priv = {
  224. .size = 0,
  225. .sensitiveArea = {
  226. .authValue = { .size = 0 }
  227. },
  228. };
  229. /*
  230. * when nameAlg is not TPM2_ALG_NULL, seed value is needed to pass
  231. * consistency checks by TPM
  232. */
  233. TPM2B_DIGEST *seed = &priv.sensitiveArea.seedValue;
  234. seed->size = tpm2_alg_util_get_hash_size(pub.publicArea.nameAlg);
  235. if (seed->size != 0) {
  236. RAND_bytes(seed->buffer, seed->size);
  237. }
  238. tpm2_session *tmp;
  239. tool_rc tmp_rc = tpm2_auth_util_from_optarg(NULL, ctx.auth, &tmp, true);
  240. if (tmp_rc != tool_rc_success) {
  241. LOG_ERR("Invalid key authorization");
  242. return tmp_rc;
  243. }
  244. const TPM2B_AUTH *auth = tpm2_session_get_auth_value(tmp);
  245. priv.sensitiveArea.authValue = *auth;
  246. tpm2_session_close(&tmp);
  247. tpm2_openssl_load_rc load_status = lprc_error;
  248. if (ctx.private_key_path) {
  249. load_status = tpm2_openssl_load_private(ctx.private_key_path,
  250. ctx.passin, alg, &pub, &priv);
  251. if (load_status == lprc_error) {
  252. return tool_rc_general_error;
  253. }
  254. }
  255. /*
  256. * If we cannot load the public from the private and a path
  257. * is not specified for public, this is an error.
  258. *
  259. * If we loaded the public from the private and a public was
  260. * specified, this is warning. re-init public and load the
  261. * specified one.
  262. */
  263. if (!tpm2_openssl_did_load_public(load_status) && !ctx.public_key_path) {
  264. LOG_ERR("Only loaded a private key, expected public key in either"
  265. " private PEM or -r option");
  266. return tool_rc_general_error;
  267. } else if (tpm2_openssl_did_load_public(load_status)
  268. && ctx.public_key_path) {
  269. LOG_WARN("Loaded a public key from the private portion"
  270. " and a public portion was specified via -u. Defaulting"
  271. " to specified public");
  272. memset(&pub.publicArea.parameters, 0,
  273. sizeof(pub.publicArea.parameters));
  274. pub.publicArea.type = TPM2_ALG_NULL;
  275. }
  276. if (ctx.public_key_path) {
  277. bool result = tpm2_openssl_load_public(ctx.public_key_path, alg, &pub);
  278. if (!result) {
  279. return tool_rc_general_error;
  280. }
  281. }
  282. tool_rc rc = tool_rc_general_error;
  283. TPM2B_NAME *name = NULL;
  284. tmp_rc = load_external(ectx, &pub, &priv, ctx.private_key_path != NULL,
  285. &name);
  286. if (tmp_rc != tool_rc_success) {
  287. rc = tmp_rc;
  288. goto out;
  289. }
  290. assert(name);
  291. tmp_rc = files_save_tpm_context_to_path(ectx, ctx.handle,
  292. ctx.context_file_path);
  293. if (tmp_rc != tool_rc_success) {
  294. rc = tmp_rc;
  295. goto out;
  296. }
  297. tpm2_tool_output("name: ");
  298. tpm2_util_hexdump(name->name, name->size);
  299. tpm2_tool_output("\n");
  300. if (ctx.name_path) {
  301. bool result = files_save_bytes_to_file(ctx.name_path, name->name,
  302. name->size);
  303. if (!result) {
  304. goto out;
  305. }
  306. }
  307. rc = tool_rc_success;
  308. out:
  309. free(name);
  310. return rc;
  311. }
  312. // Register this tool with tpm2_tool.c
  313. TPM2_TOOL_REGISTER("loadexternal", tpm2_tool_onstart, tpm2_tool_onrun, NULL, NULL)