tpm2_makecredential.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. /* SPDX-License-Identifier: BSD-3-Clause */
  2. #include <stdbool.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <openssl/rand.h>
  7. #include "files.h"
  8. #include "log.h"
  9. #include "tpm2.h"
  10. #include "tpm2_tool.h"
  11. #include "tpm2_alg_util.h"
  12. #include "tpm2_identity_util.h"
  13. #include "tpm2_options.h"
  14. #include "tpm2_openssl.h"
  15. typedef struct tpm_makecred_ctx tpm_makecred_ctx;
  16. struct tpm_makecred_ctx {
  17. TPM2B_NAME object_name;
  18. char *out_file_path;
  19. char *input_secret_data;
  20. char *public_key_path; /* path to the public portion of an object */
  21. TPM2B_PUBLIC public;
  22. TPM2B_DIGEST credential;
  23. struct {
  24. UINT8 e :1;
  25. UINT8 s :1;
  26. UINT8 n :1;
  27. UINT8 o :1;
  28. } flags;
  29. char *key_type; //type of key attempting to load, defaults to auto attempt
  30. };
  31. static tpm_makecred_ctx ctx = {
  32. .object_name = TPM2B_EMPTY_INIT,
  33. .public = TPM2B_EMPTY_INIT,
  34. .credential = TPM2B_EMPTY_INIT,
  35. };
  36. static bool write_cred_and_secret(const char *path, TPM2B_ID_OBJECT *cred,
  37. TPM2B_ENCRYPTED_SECRET *secret) {
  38. bool result = false;
  39. FILE *fp = fopen(path, "wb+");
  40. if (!fp) {
  41. LOG_ERR("Could not open file \"%s\" error: \"%s\"", path,
  42. strerror(errno));
  43. return false;
  44. }
  45. result = files_write_header(fp, 1);
  46. if (!result) {
  47. LOG_ERR("Could not write version header");
  48. goto out;
  49. }
  50. result = files_write_16(fp, cred->size);
  51. if (!result) {
  52. LOG_ERR("Could not write credential size");
  53. goto out;
  54. }
  55. result = files_write_bytes(fp, cred->credential, cred->size);
  56. if (!result) {
  57. LOG_ERR("Could not write credential data");
  58. goto out;
  59. }
  60. result = files_write_16(fp, secret->size);
  61. if (!result) {
  62. LOG_ERR("Could not write secret size");
  63. goto out;
  64. }
  65. result = files_write_bytes(fp, secret->secret, secret->size);
  66. if (!result) {
  67. LOG_ERR("Could not write secret data");
  68. goto out;
  69. }
  70. result = true;
  71. out:
  72. fclose(fp);
  73. return result;
  74. }
  75. static tool_rc make_external_credential_and_save(void) {
  76. /*
  77. * Get name_alg from the public key
  78. */
  79. TPMI_ALG_HASH name_alg = ctx.public.publicArea.nameAlg;
  80. /*
  81. * Generate and encrypt seed
  82. */
  83. TPM2B_DIGEST seed = TPM2B_TYPE_INIT(TPM2B_DIGEST, buffer);
  84. TPM2B_ENCRYPTED_SECRET encrypted_seed = TPM2B_EMPTY_INIT;
  85. unsigned char label[10] = { 'I', 'D', 'E', 'N', 'T', 'I', 'T', 'Y', 0 };
  86. bool res = tpm2_identity_util_share_secret_with_public_key(&seed,
  87. &ctx.public, label, 9, &encrypted_seed);
  88. if (!res) {
  89. LOG_ERR("Failed Seed Encryption\n");
  90. return tool_rc_general_error;
  91. }
  92. /*
  93. * Perform identity structure calculations (off of the TPM)
  94. */
  95. TPM2B_MAX_BUFFER hmac_key;
  96. TPM2B_MAX_BUFFER enc_key;
  97. tpm2_identity_util_calc_outer_integrity_hmac_key_and_dupsensitive_enc_key(
  98. &ctx.public, &ctx.object_name, &seed, &hmac_key, &enc_key);
  99. /*
  100. * The ctx.credential needs to be marshalled into struct with
  101. * both size and contents together (to be encrypted as a block)
  102. */
  103. TPM2B_MAX_BUFFER marshalled_inner_integrity = TPM2B_EMPTY_INIT;
  104. marshalled_inner_integrity.size = ctx.credential.size
  105. + sizeof(ctx.credential.size);
  106. UINT16 cred_size = ctx.credential.size;
  107. if (!tpm2_util_is_big_endian()) {
  108. cred_size = tpm2_util_endian_swap_16(cred_size);
  109. }
  110. memcpy(marshalled_inner_integrity.buffer, &cred_size, sizeof(cred_size));
  111. memcpy(&marshalled_inner_integrity.buffer[2], ctx.credential.buffer,
  112. ctx.credential.size);
  113. /*
  114. * Perform inner encryption (encIdentity) and outer HMAC (outerHMAC)
  115. */
  116. TPM2B_DIGEST outer_hmac = TPM2B_EMPTY_INIT;
  117. TPM2B_MAX_BUFFER encrypted_sensitive = TPM2B_EMPTY_INIT;
  118. tpm2_identity_util_calculate_outer_integrity(name_alg, &ctx.object_name,
  119. &marshalled_inner_integrity, &hmac_key, &enc_key,
  120. &ctx.public.publicArea.parameters.rsaDetail.symmetric,
  121. &encrypted_sensitive, &outer_hmac);
  122. /*
  123. * Package up the info to save
  124. * cred_bloc = outer_hmac || encrypted_sensitive
  125. * secret = encrypted_seed (with pubEK)
  126. */
  127. TPM2B_ID_OBJECT cred_blob = TPM2B_TYPE_INIT(TPM2B_ID_OBJECT, credential);
  128. UINT16 outer_hmac_size = outer_hmac.size;
  129. if (!tpm2_util_is_big_endian()) {
  130. outer_hmac_size = tpm2_util_endian_swap_16(outer_hmac_size);
  131. }
  132. int offset = 0;
  133. memcpy(cred_blob.credential + offset, &outer_hmac_size,
  134. sizeof(outer_hmac.size));
  135. offset += sizeof(outer_hmac.size);
  136. memcpy(cred_blob.credential + offset, outer_hmac.buffer, outer_hmac.size);
  137. offset += outer_hmac.size;
  138. //NOTE: do NOT include the encrypted_sensitive size, since it is encrypted with the blob!
  139. memcpy(cred_blob.credential + offset, encrypted_sensitive.buffer,
  140. encrypted_sensitive.size);
  141. cred_blob.size = outer_hmac.size + encrypted_sensitive.size
  142. + sizeof(outer_hmac.size);
  143. return write_cred_and_secret(ctx.out_file_path, &cred_blob,
  144. &encrypted_seed) ? tool_rc_success : tool_rc_general_error;
  145. }
  146. static tool_rc make_credential_and_save(ESYS_CONTEXT *ectx) {
  147. TPM2B_ID_OBJECT *cred_blob;
  148. TPM2B_ENCRYPTED_SECRET *secret;
  149. ESYS_TR tr_handle = ESYS_TR_NONE;
  150. tool_rc rc = tpm2_loadexternal(ectx,
  151. NULL, &ctx.public, TPM2_RH_NULL, &tr_handle);
  152. if (rc != tool_rc_success) {
  153. return rc;
  154. }
  155. rc = tpm2_makecredential(ectx, tr_handle,
  156. &ctx.credential, &ctx.object_name, &cred_blob,
  157. &secret);
  158. if (rc != tool_rc_success) {
  159. return rc;
  160. }
  161. rc = tpm2_flush_context(ectx, tr_handle);
  162. if (rc != tool_rc_success) {
  163. free(cred_blob);
  164. free(secret);
  165. return rc;
  166. }
  167. bool ret = write_cred_and_secret(ctx.out_file_path, cred_blob, secret);
  168. free(cred_blob);
  169. free(secret);
  170. return ret ? tool_rc_success : tool_rc_general_error;
  171. }
  172. static bool on_option(char key, char *value) {
  173. switch (key) {
  174. case 'u':
  175. if (ctx.flags.e) {
  176. LOG_ERR("Specify public key with **-u** or **-e**, not both");
  177. return false;
  178. }
  179. ctx.public_key_path = value;
  180. ctx.flags.e = 1;
  181. break;
  182. case 'e':
  183. if (ctx.flags.e) {
  184. LOG_ERR("Specify encryption key with **-u** or **-e**, not both");
  185. return false;
  186. }
  187. ctx.public_key_path = value;
  188. ctx.flags.e = 1;
  189. break;
  190. case 's':
  191. ctx.input_secret_data = strcmp("-", value) ? value : NULL;
  192. ctx.flags.s = 1;
  193. break;
  194. case 'n':
  195. ctx.object_name.size = BUFFER_SIZE(TPM2B_NAME, name);
  196. int q;
  197. if ((q = tpm2_util_hex_to_byte_structure(value, &ctx.object_name.size,
  198. ctx.object_name.name)) != 0) {
  199. LOG_ERR("FAILED: %d", q);
  200. return false;
  201. }
  202. ctx.flags.n = 1;
  203. break;
  204. case 'o':
  205. ctx.out_file_path = value;
  206. ctx.flags.o = 1;
  207. break;
  208. case 'G':
  209. ctx.key_type = value;
  210. break;
  211. }
  212. return true;
  213. }
  214. static bool tpm2_tool_onstart(tpm2_options **opts) {
  215. const struct option topts[] = {
  216. {"encryption-key", required_argument, NULL, 'e'},
  217. {"public", required_argument, NULL, 'u'},
  218. {"secret", required_argument, NULL, 's'},
  219. {"name", required_argument, NULL, 'n'},
  220. {"credential-blob", required_argument, NULL, 'o'},
  221. { "key-algorithm", required_argument, NULL, 'G'},
  222. };
  223. *opts = tpm2_options_new("G:u:e:s:n:o:", ARRAY_LEN(topts), topts, on_option,
  224. NULL, TPM2_OPTIONS_OPTIONAL_SAPI);
  225. return *opts != NULL;
  226. }
  227. static void set_default_TCG_EK_template(TPMI_ALG_PUBLIC alg) {
  228. switch (alg) {
  229. case TPM2_ALG_RSA:
  230. ctx.public.publicArea.parameters.rsaDetail.symmetric.algorithm =
  231. TPM2_ALG_AES;
  232. ctx.public.publicArea.parameters.rsaDetail.symmetric.keyBits.aes = 128;
  233. ctx.public.publicArea.parameters.rsaDetail.symmetric.mode.aes =
  234. TPM2_ALG_CFB;
  235. ctx.public.publicArea.parameters.rsaDetail.scheme.scheme = TPM2_ALG_NULL;
  236. ctx.public.publicArea.parameters.rsaDetail.keyBits = 2048;
  237. ctx.public.publicArea.parameters.rsaDetail.exponent = 0;
  238. ctx.public.publicArea.unique.rsa.size = 256;
  239. break;
  240. case TPM2_ALG_ECC:
  241. ctx.public.publicArea.parameters.eccDetail.symmetric.algorithm =
  242. TPM2_ALG_AES;
  243. ctx.public.publicArea.parameters.eccDetail.symmetric.keyBits.aes = 128;
  244. ctx.public.publicArea.parameters.eccDetail.symmetric.mode.sym =
  245. TPM2_ALG_CFB;
  246. ctx.public.publicArea.parameters.eccDetail.scheme.scheme = TPM2_ALG_NULL;
  247. ctx.public.publicArea.parameters.eccDetail.curveID = TPM2_ECC_NIST_P256;
  248. ctx.public.publicArea.parameters.eccDetail.kdf.scheme = TPM2_ALG_NULL;
  249. ctx.public.publicArea.unique.ecc.x.size = 32;
  250. ctx.public.publicArea.unique.ecc.y.size = 32;
  251. break;
  252. }
  253. ctx.public.publicArea.objectAttributes =
  254. TPMA_OBJECT_RESTRICTED | TPMA_OBJECT_ADMINWITHPOLICY
  255. | TPMA_OBJECT_DECRYPT | TPMA_OBJECT_FIXEDTPM
  256. | TPMA_OBJECT_FIXEDPARENT | TPMA_OBJECT_SENSITIVEDATAORIGIN;
  257. static const TPM2B_DIGEST auth_policy = {
  258. .size = 32,
  259. .buffer = {
  260. 0x83, 0x71, 0x97, 0x67, 0x44, 0x84, 0xB3, 0xF8, 0x1A, 0x90, 0xCC,
  261. 0x8D, 0x46, 0xA5, 0xD7, 0x24, 0xFD, 0x52, 0xD7, 0x6E, 0x06, 0x52,
  262. 0x0B, 0x64, 0xF2, 0xA1, 0xDA, 0x1B, 0x33, 0x14, 0x69, 0xAA
  263. }
  264. };
  265. TPM2B_DIGEST *authp = &ctx.public.publicArea.authPolicy;
  266. *authp = auth_policy;
  267. ctx.public.publicArea.nameAlg = TPM2_ALG_SHA256;
  268. }
  269. static tool_rc process_input(void) {
  270. TPMI_ALG_PUBLIC alg = TPM2_ALG_NULL;
  271. if (ctx.key_type) {
  272. LOG_WARN("Because **-G** is specified, assuming input encryption public key is in PEM format.");
  273. alg = tpm2_alg_util_from_optarg(ctx.key_type,
  274. tpm2_alg_util_flags_asymmetric);
  275. if (alg == TPM2_ALG_ERROR ||
  276. (alg != TPM2_ALG_RSA && alg != TPM2_ALG_ECC)) {
  277. LOG_ERR("Unsupported key type, got: \"%s\"", ctx.key_type);
  278. return tool_rc_general_error;
  279. }
  280. }
  281. if (ctx.public_key_path) {
  282. bool result = tpm2_openssl_load_public(ctx.public_key_path, alg,
  283. &ctx.public);
  284. if (!result) {
  285. return tool_rc_general_error;
  286. }
  287. }
  288. /*
  289. * Since it is a PEM we will fixate the key properties from TCG EK
  290. * template since we had to choose "a template".
  291. */
  292. if (ctx.key_type) {
  293. set_default_TCG_EK_template(alg);
  294. }
  295. if (!ctx.flags.s) {
  296. LOG_ERR("Specify the secret either as a file or a '-' for stdin");
  297. return tool_rc_option_error;
  298. }
  299. if (!ctx.flags.e || !ctx.flags.n || !ctx.flags.o) {
  300. LOG_ERR("Expected mandatory options e, n, o.");
  301. return tool_rc_option_error;
  302. }
  303. /*
  304. * Maximum size of the allowed secret-data size to fit in TPM2B_DIGEST
  305. */
  306. ctx.credential.size = TPM2_SHA512_DIGEST_SIZE;
  307. bool result = files_load_bytes_from_buffer_or_file_or_stdin(NULL,
  308. ctx.input_secret_data, &ctx.credential.size, ctx.credential.buffer);
  309. if (!result) {
  310. return tool_rc_general_error;
  311. }
  312. return tool_rc_success;
  313. }
  314. static tool_rc tpm2_tool_onrun(ESYS_CONTEXT *ectx, tpm2_option_flags flags) {
  315. UNUSED(flags);
  316. tool_rc rc = process_input();
  317. if (rc != tool_rc_success) {
  318. return rc;
  319. }
  320. // Run it outside of a TPM
  321. return ectx ?
  322. make_credential_and_save(ectx) :
  323. make_external_credential_and_save();
  324. }
  325. // Register this tool with tpm2_tool.c
  326. TPM2_TOOL_REGISTER("makecredential", tpm2_tool_onstart, tpm2_tool_onrun, NULL, NULL)