tpm2_sign.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. /* SPDX-License-Identifier: BSD-3-Clause */
  2. #include <stdbool.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include "files.h"
  6. #include "log.h"
  7. #include "tpm2.h"
  8. #include "tpm2_tool.h"
  9. #include "tpm2_alg_util.h"
  10. #include "tpm2_convert.h"
  11. #include "tpm2_hash.h"
  12. #include "tpm2_options.h"
  13. typedef struct tpm_sign_ctx tpm_sign_ctx;
  14. struct tpm_sign_ctx {
  15. TPMT_TK_HASHCHECK validation;
  16. struct {
  17. const char *ctx_path;
  18. const char *auth_str;
  19. tpm2_loaded_object object;
  20. } signing_key;
  21. TPMI_ALG_HASH halg;
  22. TPMI_ALG_SIG_SCHEME sig_scheme;
  23. TPMT_SIG_SCHEME in_scheme;
  24. TPM2B_DIGEST *digest;
  25. char *output_path;
  26. BYTE *msg;
  27. UINT16 length;
  28. char *input_file;
  29. tpm2_convert_sig_fmt sig_format;
  30. struct {
  31. UINT8 d :1;
  32. UINT8 t :1;
  33. UINT8 o :1;
  34. } flags;
  35. char *cp_hash_path;
  36. char *commit_index;
  37. };
  38. static tpm_sign_ctx ctx = {
  39. .halg = TPM2_ALG_NULL,
  40. .sig_scheme = TPM2_ALG_NULL
  41. };
  42. static tool_rc sign_and_save(ESYS_CONTEXT *ectx) {
  43. TPMT_SIGNATURE *signature;
  44. bool result;
  45. if (ctx.cp_hash_path) {
  46. TPM2B_DIGEST cp_hash = { .size = 0 };
  47. tool_rc rc = tpm2_sign(ectx, &ctx.signing_key.object, ctx.digest,
  48. &ctx.in_scheme, &ctx.validation, &signature, &cp_hash);
  49. if (rc != tool_rc_success) {
  50. return rc;
  51. }
  52. bool result = files_save_digest(&cp_hash, ctx.cp_hash_path);
  53. if (!result) {
  54. rc = tool_rc_general_error;
  55. }
  56. return rc;
  57. }
  58. tool_rc rc = tpm2_sign(ectx, &ctx.signing_key.object, ctx.digest,
  59. &ctx.in_scheme, &ctx.validation, &signature, NULL);
  60. if (rc != tool_rc_success) {
  61. goto out;
  62. }
  63. result = tpm2_convert_sig_save(signature, ctx.sig_format, ctx.output_path);
  64. if (!result) {
  65. rc = tool_rc_general_error;
  66. goto out;
  67. }
  68. rc = tool_rc_success;
  69. out:
  70. free(signature);
  71. return rc;
  72. }
  73. static tool_rc init(ESYS_CONTEXT *ectx) {
  74. /*
  75. * Set signature scheme for key type, or validate chosen scheme is
  76. * allowed for key type.
  77. */
  78. tool_rc rc = tpm2_alg_util_get_signature_scheme(ectx,
  79. ctx.signing_key.object.tr_handle, &ctx.halg, ctx.sig_scheme,
  80. &ctx.in_scheme);
  81. if (rc != tool_rc_success) {
  82. LOG_ERR("Invalid signature scheme for key type!");
  83. return rc;
  84. }
  85. if (ctx.in_scheme.scheme != TPM2_ALG_ECDAA && ctx.commit_index) {
  86. LOG_ERR("Commit counter is only applicable in an ECDAA scheme.");
  87. return tool_rc_option_error;
  88. }
  89. if (ctx.in_scheme.scheme == TPM2_ALG_ECDAA && ctx.commit_index) {
  90. bool result = tpm2_util_string_to_uint16(ctx.commit_index,
  91. &ctx.in_scheme.details.ecdaa.count);
  92. if (!result) {
  93. return tool_rc_general_error;
  94. }
  95. }
  96. if (ctx.in_scheme.scheme == TPM2_ALG_ECDAA &&
  97. ctx.sig_format != signature_format_tss) {
  98. LOG_ERR("Only TSS signature format is possible with ECDAA scheme");
  99. return tool_rc_option_error;
  100. }
  101. if (ctx.cp_hash_path && ctx.output_path) {
  102. LOG_ERR("Cannot output signature when calculating cpHash");
  103. return tool_rc_option_error;
  104. }
  105. if (!ctx.signing_key.ctx_path) {
  106. LOG_ERR("Expected option c");
  107. return tool_rc_option_error;
  108. }
  109. if (!ctx.flags.o && !ctx.cp_hash_path) {
  110. LOG_ERR("Expected option o");
  111. return tool_rc_option_error;
  112. }
  113. if (!ctx.flags.d && ctx.flags.t) {
  114. LOG_WARN("Ignoring the specified validation ticket since no TPM "
  115. "calculated digest specified.");
  116. }
  117. /*
  118. * Applicable when input data is not a digest, rather the message to sign.
  119. * A digest is calculated first in this case.
  120. */
  121. if (!ctx.flags.d) {
  122. FILE *input = ctx.input_file ? fopen(ctx.input_file, "rb") : stdin;
  123. if (!input) {
  124. LOG_ERR("Could not open file \"%s\"", ctx.input_file);
  125. return tool_rc_general_error;
  126. }
  127. TPMT_TK_HASHCHECK *temp_validation_ticket;
  128. rc = tpm2_hash_file(ectx, ctx.halg, TPM2_RH_OWNER, input, &ctx.digest,
  129. &temp_validation_ticket);
  130. if (input != stdin) {
  131. fclose(input);
  132. }
  133. if (rc != tool_rc_success) {
  134. LOG_ERR("Could not hash input");
  135. }
  136. ctx.validation = *temp_validation_ticket;
  137. free(temp_validation_ticket);
  138. /*
  139. * we don't need to perform the digest, just read it
  140. */
  141. return rc;
  142. }
  143. /*
  144. * else process it as a pre-computed digest
  145. */
  146. ctx.digest = malloc(sizeof(TPM2B_DIGEST));
  147. if (!ctx.digest) {
  148. LOG_ERR("oom");
  149. return tool_rc_general_error;
  150. }
  151. ctx.digest->size = sizeof(ctx.digest->buffer);
  152. bool result = files_load_bytes_from_buffer_or_file_or_stdin(NULL,
  153. ctx.input_file, &ctx.digest->size, ctx.digest->buffer);
  154. if (!result) {
  155. return tool_rc_general_error;
  156. }
  157. /*
  158. * Applicable to un-restricted signing keys
  159. * NOTE: When digests without tickets are specified for restricted keys,
  160. * the sign operation will fail.
  161. */
  162. if (ctx.flags.d && !ctx.flags.t) {
  163. ctx.validation.tag = TPM2_ST_HASHCHECK;
  164. ctx.validation.hierarchy = TPM2_RH_NULL;
  165. memset(&ctx.validation.digest, 0, sizeof(ctx.validation.digest));
  166. }
  167. return tool_rc_success;
  168. }
  169. static bool on_option(char key, char *value) {
  170. switch (key) {
  171. case 'c':
  172. ctx.signing_key.ctx_path = value;
  173. break;
  174. case 'p':
  175. ctx.signing_key.auth_str = value;
  176. break;
  177. case 'g':
  178. ctx.halg = tpm2_alg_util_from_optarg(value, tpm2_alg_util_flags_hash);
  179. if (ctx.halg == TPM2_ALG_ERROR) {
  180. LOG_ERR("Could not convert to number or lookup algorithm, got: "
  181. "\"%s\"", value);
  182. return false;
  183. }
  184. break;
  185. case 's': {
  186. ctx.sig_scheme = tpm2_alg_util_from_optarg(value,
  187. tpm2_alg_util_flags_sig);
  188. if (ctx.sig_scheme == TPM2_ALG_ERROR) {
  189. LOG_ERR("Unknown signing scheme, got: \"%s\"", value);
  190. return false;
  191. }
  192. }
  193. break;
  194. case 'd':
  195. ctx.flags.d = 1;
  196. break;
  197. case 't': {
  198. bool result = files_load_validation(value, &ctx.validation);
  199. if (!result) {
  200. return false;
  201. }
  202. ctx.flags.t = 1;
  203. }
  204. break;
  205. case 'o':
  206. ctx.output_path = value;
  207. ctx.flags.o = 1;
  208. break;
  209. case 0:
  210. ctx.cp_hash_path = value;
  211. break;
  212. case 1:
  213. ctx.commit_index = value;
  214. break;
  215. case 'f':
  216. ctx.sig_format = tpm2_convert_sig_fmt_from_optarg(value);
  217. if (ctx.sig_format == signature_format_err) {
  218. return false;
  219. }
  220. /* no default */
  221. }
  222. return true;
  223. }
  224. static bool on_args(int argc, char *argv[]) {
  225. if (argc != 1) {
  226. LOG_ERR("Expected one input file, got: %d", argc);
  227. return false;
  228. }
  229. ctx.input_file = argv[0];
  230. return true;
  231. }
  232. static bool tpm2_tool_onstart(tpm2_options **opts) {
  233. static const struct option topts[] = {
  234. { "auth", required_argument, NULL, 'p' },
  235. { "hash-algorithm", required_argument, NULL, 'g' },
  236. { "scheme", required_argument, NULL, 's' },
  237. { "digest", no_argument, NULL, 'd' },
  238. { "signature", required_argument, NULL, 'o' },
  239. { "ticket", required_argument, NULL, 't' },
  240. { "key-context", required_argument, NULL, 'c' },
  241. { "format", required_argument, NULL, 'f' },
  242. { "cphash", required_argument, NULL, 0 },
  243. { "commit-index", required_argument, NULL, 1 },
  244. };
  245. *opts = tpm2_options_new("p:g:dt:o:c:f:s:", ARRAY_LEN(topts), topts,
  246. on_option, on_args, 0);
  247. return *opts != NULL;
  248. }
  249. static tool_rc tpm2_tool_onrun(ESYS_CONTEXT *ectx, tpm2_option_flags flags) {
  250. UNUSED(flags);
  251. tool_rc rc = tpm2_util_object_load_auth(ectx, ctx.signing_key.ctx_path,
  252. ctx.signing_key.auth_str, &ctx.signing_key.object, false,
  253. TPM2_HANDLE_ALL_W_NV);
  254. if (rc != tool_rc_success) {
  255. LOG_ERR("Invalid key authorization");
  256. return rc;
  257. }
  258. rc = init(ectx);
  259. if (rc != tool_rc_success) {
  260. return rc;
  261. }
  262. return sign_and_save(ectx);
  263. }
  264. static tool_rc tpm2_tool_onstop(ESYS_CONTEXT *ectx) {
  265. UNUSED(ectx);
  266. return tpm2_session_close(&ctx.signing_key.object.session);
  267. }
  268. static void tpm2_tool_onexit(void) {
  269. if (ctx.digest) {
  270. free(ctx.digest);
  271. }
  272. free(ctx.msg);
  273. }
  274. // Register this tool with tpm2_tool.c
  275. TPM2_TOOL_REGISTER("sign", tpm2_tool_onstart, tpm2_tool_onrun, tpm2_tool_onstop, tpm2_tool_onexit)