tpm2_encryptdecrypt.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. /* SPDX-License-Identifier: BSD-3-Clause */
  2. #include <assert.h>
  3. #include <errno.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_tool.h"
  12. #include "tpm2_alg_util.h"
  13. #include "tpm2_auth_util.h"
  14. #include "tpm2_options.h"
  15. #define MAX_INPUT_DATA_SIZE UINT16_MAX
  16. typedef struct tpm_encrypt_decrypt_ctx tpm_encrypt_decrypt_ctx;
  17. struct tpm_encrypt_decrypt_ctx {
  18. struct {
  19. const char *ctx_path;
  20. const char *auth_str;
  21. tpm2_loaded_object object;
  22. } encryption_key;
  23. TPMI_YES_NO is_decrypt;
  24. uint8_t input_data[MAX_INPUT_DATA_SIZE];
  25. uint16_t input_data_size;
  26. const char *input_path;
  27. char *out_file_path;
  28. uint8_t padded_block_len;
  29. bool is_padding_option_enabled;
  30. TPMI_ALG_SYM_MODE mode;
  31. struct {
  32. char *in;
  33. char *out;
  34. } iv;
  35. TPM2B_IV iv_start;
  36. char *cp_hash_path;
  37. };
  38. static tpm_encrypt_decrypt_ctx ctx = {
  39. .mode = TPM2_ALG_NULL,
  40. .input_data_size = MAX_INPUT_DATA_SIZE,
  41. .padded_block_len = TPM2_MAX_SYM_BLOCK_SIZE,
  42. .is_padding_option_enabled = false,
  43. .iv_start = { .size = sizeof(ctx.iv_start.buffer), .buffer = { 0 } },
  44. };
  45. static tool_rc readpub(ESYS_CONTEXT *ectx, TPM2B_PUBLIC **public) {
  46. return tpm2_readpublic(ectx, ctx.encryption_key.object.tr_handle,
  47. public, NULL, NULL);
  48. }
  49. static bool evaluate_pkcs7_padding_requirements(uint16_t remaining_bytes,
  50. bool expected) {
  51. if (!ctx.is_padding_option_enabled) {
  52. return false;
  53. }
  54. if (ctx.is_decrypt != expected) {
  55. return false;
  56. }
  57. /*
  58. * If no ctx.mode was specified, the default cfb was set.
  59. */
  60. if (ctx.mode != TPM2_ALG_CBC && ctx.mode != TPM2_ALG_ECB) {
  61. return false;
  62. }
  63. /*
  64. * Is last block?
  65. */
  66. if (!(remaining_bytes <= TPM2_MAX_DIGEST_BUFFER && remaining_bytes > 0)) {
  67. return false;
  68. }
  69. LOG_WARN("Processing pkcs7 padding.");
  70. return true;
  71. }
  72. static void append_pkcs7_padding_data_to_input(uint8_t *pad_data,
  73. uint16_t *in_data_size, uint16_t *remaining_bytes) {
  74. bool test_pad_reqs = evaluate_pkcs7_padding_requirements(*remaining_bytes,
  75. false);
  76. if (!test_pad_reqs) {
  77. return;
  78. }
  79. *pad_data = ctx.padded_block_len - (*in_data_size % ctx.padded_block_len);
  80. memset(&ctx.input_data[ctx.input_data_size], *pad_data, *pad_data);
  81. if (*pad_data == ctx.padded_block_len) {
  82. *remaining_bytes += *pad_data;
  83. }
  84. if (*pad_data < ctx.padded_block_len) {
  85. *remaining_bytes = *in_data_size += *pad_data;
  86. }
  87. }
  88. static void strip_pkcs7_padding_data_from_output(uint8_t *pad_data,
  89. TPM2B_MAX_BUFFER *out_data, uint16_t *remaining_bytes) {
  90. bool test_pad_reqs = evaluate_pkcs7_padding_requirements(*remaining_bytes,
  91. true);
  92. if (!test_pad_reqs) {
  93. return;
  94. }
  95. uint8_t last_block_length = ctx.padded_block_len
  96. - (out_data->size % ctx.padded_block_len);
  97. if (last_block_length != ctx.padded_block_len) {
  98. LOG_WARN("Encrypted input is not block length aligned.");
  99. }
  100. *pad_data = out_data->buffer[last_block_length - 1];
  101. out_data->size -= *pad_data;
  102. }
  103. static tool_rc encrypt_decrypt(ESYS_CONTEXT *ectx) {
  104. tool_rc rc = tool_rc_general_error;
  105. /*
  106. * try EncryptDecrypt2 first, and if the command is not supported by the TPM
  107. * fall back to EncryptDecrypt.
  108. */
  109. UINT16 data_offset = 0;
  110. bool result = true;
  111. FILE *out_file_ptr =
  112. ctx.out_file_path ? fopen(ctx.out_file_path, "wb+") : stdout;
  113. if (!out_file_ptr) {
  114. LOG_ERR("Could not open file \"%s\", error: %s", ctx.out_file_path,
  115. strerror(errno));
  116. return tool_rc_general_error;
  117. }
  118. TPM2B_MAX_BUFFER *out_data = NULL;
  119. TPM2B_MAX_BUFFER in_data;
  120. TPM2B_IV *iv_out = NULL;
  121. TPM2B_IV *iv_in = &ctx.iv_start;
  122. uint8_t pad_data = 0;
  123. uint16_t remaining_bytes = ctx.input_data_size;
  124. if (ctx.mode == TPM2_ALG_ECB) {
  125. iv_in = NULL;
  126. }
  127. if (ctx.cp_hash_path) {
  128. in_data.size = remaining_bytes;
  129. append_pkcs7_padding_data_to_input(&pad_data, &in_data.size,
  130. &remaining_bytes);
  131. memcpy(in_data.buffer, ctx.input_data, in_data.size);
  132. LOG_WARN("Calculating cpHash. Exiting without performing encryptdecrypt.");
  133. TPM2B_DIGEST cp_hash = { .size = 0 };
  134. tool_rc rc = tpm2_encryptdecrypt(ectx, &ctx.encryption_key.object,
  135. ctx.is_decrypt, ctx.mode, iv_in, &in_data, &out_data, &iv_out,
  136. &cp_hash);
  137. if (rc != tool_rc_success) {
  138. LOG_ERR("CpHash calculation failed!");
  139. fclose(out_file_ptr);
  140. return rc;
  141. }
  142. bool result = files_save_digest(&cp_hash, ctx.cp_hash_path);
  143. if (!result) {
  144. rc = tool_rc_general_error;
  145. }
  146. return rc;
  147. }
  148. while (remaining_bytes > 0) {
  149. in_data.size =
  150. remaining_bytes > TPM2_MAX_DIGEST_BUFFER ?
  151. TPM2_MAX_DIGEST_BUFFER : remaining_bytes;
  152. if (!pad_data) {
  153. append_pkcs7_padding_data_to_input(&pad_data, &in_data.size,
  154. &remaining_bytes);
  155. }
  156. memcpy(in_data.buffer, &ctx.input_data[data_offset], in_data.size);
  157. rc = tpm2_encryptdecrypt(ectx, &ctx.encryption_key.object,
  158. ctx.is_decrypt, ctx.mode, iv_in, &in_data, &out_data, &iv_out,
  159. NULL);
  160. if (rc != tool_rc_success) {
  161. goto out;
  162. }
  163. /*
  164. * Copy iv_out iv_in to use it in next loop iteration.
  165. * This copy is also output from the tool for further chaining.
  166. */
  167. if (ctx.mode != TPM2_ALG_ECB) {
  168. assert(iv_in);
  169. assert(iv_out);
  170. *iv_in = *iv_out;
  171. free(iv_out);
  172. }
  173. strip_pkcs7_padding_data_from_output(&pad_data, out_data,
  174. &remaining_bytes);
  175. result = files_write_bytes(out_file_ptr, out_data->buffer,
  176. out_data->size);
  177. free(out_data);
  178. if (!result) {
  179. LOG_ERR("Failed to save output data to file");
  180. goto out;
  181. }
  182. remaining_bytes -= in_data.size;
  183. data_offset += in_data.size;
  184. }
  185. /*
  186. * iv_in here is the copy of final iv_out from the loop above.
  187. */
  188. result =
  189. (ctx.iv.out && iv_in) ?
  190. files_save_bytes_to_file(ctx.iv.out, iv_in->buffer,
  191. iv_in->size) :
  192. true;
  193. if (!result) {
  194. goto out;
  195. }
  196. rc = tool_rc_success;
  197. out:
  198. if (out_file_ptr != stdout) {
  199. fclose(out_file_ptr);
  200. }
  201. return rc;
  202. }
  203. static void parse_iv(char *value) {
  204. ctx.iv.in = value;
  205. char *split = strchr(value, ':');
  206. if (split) {
  207. *split = '\0';
  208. split++;
  209. if (split) {
  210. ctx.iv.out = split;
  211. }
  212. }
  213. }
  214. static bool setup_alg_mode(ESYS_CONTEXT *ectx) {
  215. TPM2B_PUBLIC *public;
  216. tool_rc rc = readpub(ectx, &public);
  217. if (rc != tool_rc_success) {
  218. return false;
  219. }
  220. /*
  221. * Sym objects can have a NULL mode, which means the caller can and must determine mode.
  222. * Thus if the caller doesn't specify an algorithm, and the object has a default mode, choose it,
  223. * else choose CFB.
  224. * If the caller specifies an invalid mode, just pass it to the TPM and let it error out.
  225. */
  226. if (ctx.mode == TPM2_ALG_NULL) {
  227. TPMI_ALG_SYM_MODE objmode =
  228. public->publicArea.parameters.symDetail.sym.mode.sym;
  229. if (objmode == TPM2_ALG_NULL) {
  230. ctx.mode = TPM2_ALG_CFB;
  231. } else {
  232. ctx.mode = objmode;
  233. }
  234. }
  235. free(public);
  236. return true;
  237. }
  238. static bool on_option(char key, char *value) {
  239. switch (key) {
  240. case 'c':
  241. ctx.encryption_key.ctx_path = value;
  242. break;
  243. case 'p':
  244. ctx.encryption_key.auth_str = value;
  245. break;
  246. case 'd':
  247. ctx.is_decrypt = 1;
  248. break;
  249. case 'o':
  250. ctx.out_file_path = value;
  251. break;
  252. case 'G':
  253. ctx.mode = tpm2_alg_util_strtoalg(value, tpm2_alg_util_flags_mode);
  254. if (ctx.mode == TPM2_ALG_ERROR) {
  255. LOG_ERR("Invalid mode, got: %s", value);
  256. return false;
  257. }
  258. break;
  259. case 't':
  260. parse_iv(value);
  261. break;
  262. case 'e':
  263. ctx.is_padding_option_enabled = true;
  264. break;
  265. case 0:
  266. ctx.cp_hash_path = value;
  267. break;
  268. }
  269. return true;
  270. }
  271. static bool on_args(int argc, char *argv[]) {
  272. if (argc != 1) {
  273. LOG_ERR("Expected one input file, got: %d", argc);
  274. return false;
  275. }
  276. ctx.input_path = argv[0];
  277. return true;
  278. }
  279. static bool tpm2_tool_onstart(tpm2_options **opts) {
  280. const struct option topts[] = {
  281. { "auth", required_argument, NULL, 'p' },
  282. { "decrypt", no_argument, NULL, 'd' },
  283. { "iv", required_argument, NULL, 't' },
  284. { "mode", required_argument, NULL, 'G' },
  285. { "output", required_argument, NULL, 'o' },
  286. { "key-context", required_argument, NULL, 'c' },
  287. { "pad", no_argument, NULL, 'e' },
  288. { "cphash", required_argument, NULL, 0 },
  289. };
  290. *opts = tpm2_options_new("p:edi:o:c:G:t:", ARRAY_LEN(topts), topts,
  291. on_option, on_args, 0);
  292. return *opts != NULL;
  293. }
  294. static bool is_input_options_args_valid(void) {
  295. if (!ctx.encryption_key.ctx_path) {
  296. LOG_ERR("Expected a context file or handle, got none.");
  297. return false;
  298. }
  299. bool result = files_load_bytes_from_buffer_or_file_or_stdin(NULL,
  300. ctx.input_path, &ctx.input_data_size, ctx.input_data);
  301. if (!result) {
  302. LOG_ERR("Failed to read in the input.");
  303. return result;
  304. }
  305. if (!ctx.iv.in) {
  306. LOG_WARN("Using a weak IV, try specifying an IV");
  307. }
  308. if (ctx.iv.in) {
  309. unsigned long file_size;
  310. result = files_get_file_size_path(ctx.iv.in, &file_size);
  311. if (!result) {
  312. LOG_ERR("Could not retrieve iv file size.");
  313. return false;
  314. }
  315. if (file_size != ctx.iv_start.size) {
  316. LOG_ERR("Iv should be 16 bytes, got %lu", file_size);
  317. return false;
  318. }
  319. result = files_load_bytes_from_path(ctx.iv.in, ctx.iv_start.buffer,
  320. &ctx.iv_start.size);
  321. if (!result) {
  322. LOG_ERR("Could not load the iv from the file.");
  323. return false;
  324. }
  325. }
  326. if (ctx.cp_hash_path && ctx.input_data_size > TPM2_MAX_DIGEST_BUFFER) {
  327. LOG_ERR("Cannot calculate cpHash for buffer larger than max digest buffer.");
  328. return false;
  329. }
  330. return true;
  331. }
  332. static tool_rc tpm2_tool_onrun(ESYS_CONTEXT *ectx, tpm2_option_flags flags) {
  333. UNUSED(flags);
  334. bool retval = is_input_options_args_valid();
  335. if (!retval) {
  336. return tool_rc_option_error;
  337. }
  338. tool_rc rc = tpm2_util_object_load_auth(ectx, ctx.encryption_key.ctx_path,
  339. ctx.encryption_key.auth_str, &ctx.encryption_key.object, false,
  340. TPM2_HANDLE_ALL_W_NV);
  341. if (rc != tool_rc_success) {
  342. LOG_ERR("Invalid object key authorization");
  343. return rc;
  344. }
  345. bool result = setup_alg_mode(ectx);
  346. if (!result) {
  347. LOG_ERR("Failure to setup key mode.");
  348. return tool_rc_general_error;
  349. }
  350. return encrypt_decrypt(ectx);
  351. }
  352. static tool_rc tpm2_tool_onstop(ESYS_CONTEXT *ectx) {
  353. UNUSED(ectx);
  354. return tpm2_session_close(&ctx.encryption_key.object.session);
  355. }
  356. // Register this tool with tpm2_tool.c
  357. TPM2_TOOL_REGISTER("encryptdecrypt", tpm2_tool_onstart, tpm2_tool_onrun, tpm2_tool_onstop, NULL)