tools: add ability to specify creation data and pcrs

Add a --creation-data <pcrs> option that specifies a possibly empty
list of PCRs to be embedded in the greation data stored in the key
file.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
diff --git a/src/include/tpm2-common.h b/src/include/tpm2-common.h
index ba27525..043bd69 100644
--- a/src/include/tpm2-common.h
+++ b/src/include/tpm2-common.h
@@ -157,4 +157,8 @@
 TPM_RC tpm2_pcrs_get_digests(TSS_CONTEXT *tssContext,
 			     TPML_PCR_SELECTION pcrread, TPM_ALG_ID hash_alg,
 			     DIGEST_2B *pcrDigest, TPML_DIGEST_VALUES *dv);
+int tpm2_check_creation_pcrs(TSS_CONTEXT *tssContext,
+			     TPML_PCR_SELECTION *creation_pcrs,
+			     TPM2B_CREATION_DATA *creationData,
+			     TPML_DIGEST_VALUES **pcrValues);
 #endif
diff --git a/src/libcommon/tpm2-common.c b/src/libcommon/tpm2-common.c
index 2b31daa..7ec59f0 100644
--- a/src/libcommon/tpm2-common.c
+++ b/src/libcommon/tpm2-common.c
@@ -3540,6 +3540,29 @@
 	return 0;
 }
 
+/* returns true if the creation pcrs don't match the current values */
+int tpm2_check_creation_pcrs(TSS_CONTEXT *tssContext,
+			     TPML_PCR_SELECTION *creation_pcrs,
+			     TPM2B_CREATION_DATA *creationData,
+			     TPML_DIGEST_VALUES **pcrValues)
+{
+	int count = tpm2_count_pcrs(creation_pcrs);
+	DIGEST_2B d;
+
+	if (!creation_pcrs)
+		return 0;
+
+	*pcrValues = malloc(sizeof((*pcrValues)->count) +
+			    count*sizeof((*pcrValues)->digests[0]));
+	(*pcrValues)->count = 0;
+	tpm2_pcrs_get_digests(tssContext, *creation_pcrs,
+			      name_alg, &d, *pcrValues);
+	if (memcmp(d.buffer, VAL_2B(creationData->creationData.pcrDigest, buffer),
+		   VAL_2B(creationData->creationData.pcrDigest, size)) != 0) {
+		return 1;
+	}
+	return 0;
+}
 static __attribute__((constructor)) void tpm2_init(void)
 {
 	int fd;
diff --git a/src/tools/create_tpm2_key.c b/src/tools/create_tpm2_key.c
index 14518e2..6aa0462 100644
--- a/src/tools/create_tpm2_key.c
+++ b/src/tools/create_tpm2_key.c
@@ -32,6 +32,7 @@
 #define OPT_LOCALITY 0x1fc
 #define OPT_SECRET 0x1fb
 #define OPT_DESCRIPTION 0x1fa
+#define OPT_CREATION 0x1f9
 
 static struct option long_options[] = {
 	{"auth", 0, 0, 'a'},
@@ -55,6 +56,7 @@
 	{"restricted", 0, 0, OPT_RESTRICTED },
 	{"secret", 1, 0, OPT_SECRET },
 	{"description", 1, 0, OPT_DESCRIPTION},
+	{"creation-data", 1, 0, OPT_CREATION},
 	/*
 	 * The option --deprecated allows us to create old format keys
 	 * for the purposes of testing.  It should never be used in
@@ -114,6 +116,8 @@
 		"\t                              Identified by <handle>.\n"
 		"\t--description <desc>          Specify a description string to be\n"
 		"\t                              embedded in the key file\n"
+		"\t--creation-data <pcrs>        request creation data with the given\n"
+		"\t                              pcr list"
 		"\n"
 		"Report bugs to " PACKAGE_BUGREPORT "\n",
 		argv0);
@@ -492,8 +496,12 @@
 	int restricted = 0;
 	char *parent_str = NULL;
 	TPML_PCR_SELECTION pcr_lock = { 0 };
+	TPML_PCR_SELECTION *creation_pcrs = NULL, tmp_tpml = { 0 };
 	int has_policy = 0, has_locality = 0;
 	UINT8 locality = 0;
+	TPM2B_CREATION_DATA creationData;
+	TPMT_TK_CREATION creationTicket;
+	TPML_DIGEST_VALUES *pcrValues = NULL;
 
 	OpenSSL_add_all_digests();
 	/* may be needed to decrypt the key */
@@ -599,6 +607,10 @@
 			case OPT_DESCRIPTION:
 				description = optarg;
 				break;
+			case OPT_CREATION:
+				tpm2_get_pcr_lock(&tmp_tpml, optarg);
+				creation_pcrs = &tmp_tpml;
+				break;
 			default:
 				printf("Unknown option '%c'\n", c);
 				usage(argv[0]);
@@ -626,6 +638,11 @@
 		key_size = 2048;
 	}
 
+	if (wrap && creation_pcrs) {
+		fprintf(stderr, "creation data can't be added to wrapped keys\n");
+		usage(argv[0]);
+	}
+
 	if (rsa == 1 && ecc != TPM_ECC_NONE) {
 		fprintf(stderr, "Cannot specify both --rsa and --ecc\n");
 		exit(1);
@@ -947,7 +964,8 @@
 
 		rc = tpm2_Create(tssContext, phandle, &inSensitive,
 				 &objectPublic, &outPrivate, &outPublic,
-				 authHandle, parent_auth, NULL, NULL, NULL);
+				 authHandle, parent_auth, creation_pcrs,
+				 &creationData, &creationTicket);
 
 		if (rc) {
 			reason = "TPM2_Create";
@@ -956,6 +974,13 @@
 			goto out_flush;
 		}
 
+		if (tpm2_check_creation_pcrs(tssContext, creation_pcrs,
+					     &creationData, &pcrValues)) {
+			rc = NOT_TPM_ERROR;
+			reason = "creation data PCR mismatch";
+			goto out_flush;
+		}
+
 		pub = &outPublic;
 		priv = &outPrivate;
 	}
@@ -978,7 +1003,7 @@
 			   privkey, privkey_len,
 			   auth == NULL && secret_handle == 0, parent, sk,
 			   version, enc_secret, description,
-			   NULL, NULL, NULL);
+			   &creationData, &creationTicket, pcrValues);
 	tpm2_free_policy(sk);
 
 	exit(0);
diff --git a/src/tools/seal_tpm2_data.c b/src/tools/seal_tpm2_data.c
index 33cf07b..4b37f5e 100644
--- a/src/tools/seal_tpm2_data.c
+++ b/src/tools/seal_tpm2_data.c
@@ -25,6 +25,7 @@
 #define OPT_LOCALITY 0x1fc
 #define OPT_SECRET 0x1fb
 #define OPT_DESCRIPTION 0x1fa
+#define OPT_CREATION 0x1f9
 
 static struct option long_options[] = {
 	{"auth", 0, 0, 'a'},
@@ -43,6 +44,7 @@
 	{"name-scheme", 1, 0, 'n'},
 	{"import", 1, 0, 'i'},
 	{"description", 1, 0, OPT_DESCRIPTION },
+	{"creation-data", 1, 0, OPT_CREATION},
 	{0, 0, 0, 0}
 };
 
@@ -97,6 +99,8 @@
 		"\t-c, --policy                  Specify a policy for unsealing the data\n"
 		"\t--description <desc>          Specify a description string to be\n"
 		"\t                              embedded in the key file\n"
+		"\t--creation-data <pcrs>        request creation data with the given\n"
+		"\t                              pcr list"
 		"\n"
 		"\n"
 		"Report bugs to " PACKAGE_BUGREPORT "\n",
@@ -158,6 +162,10 @@
 	char *signed_policy = NULL;
 	ENCRYPTED_SECRET_2B secret, *enc_secret = NULL;
 	int has_locality = 0, locality = 0;
+	TPML_PCR_SELECTION *creation_pcrs = NULL, tmp_tpml = { 0 };
+	TPM2B_CREATION_DATA creationData;
+	TPMT_TK_CREATION creationTicket;
+	TPML_DIGEST_VALUES *pcrValues = NULL;
 
 	pcr_lock.count = 0;
 
@@ -239,6 +247,10 @@
 		case OPT_DESCRIPTION:
 			description = optarg;
 			break;
+		case OPT_CREATION:
+			tpm2_get_pcr_lock(&tmp_tpml, optarg);
+			creation_pcrs = &tmp_tpml;
+			break;
 		default:
 			printf("Unknown option '%c'\n", c);
 			usage(argv[0]);
@@ -267,6 +279,11 @@
 		exit(1);
 	}
 
+	if (import && creation_pcrs) {
+		fprintf(stderr, "cannot get creation data when creating importable\n");
+		exit(1);
+	}
+
 	if (has_locality && locality == 0) {
 		fprintf(stderr, "zero is an illegal locality bitmap\n");
 		exit(1);
@@ -439,7 +456,7 @@
 
 	rc = tpm2_Create(tssContext, phandle, &inSensitive, &inPublic,
 			 &outPrivate, &outPublic, authHandle, parent_auth,
-			 NULL, NULL, NULL);
+			 creation_pcrs, &creationData, &creationTicket);
 
 	if (rc) {
 		reason = "TPM2_Create";
@@ -448,6 +465,13 @@
 		goto out_flush;
 	}
 
+	if (tpm2_check_creation_pcrs(tssContext, creation_pcrs,
+				     &creationData, &pcrValues)) {
+		rc = NOT_TPM_ERROR;
+		reason = "creation data PCR mismatch";
+		goto out_flush;
+	}
+
 	parent = tpm2_handle_ext(tssContext, parent);
 
  write_file:
@@ -465,7 +489,7 @@
 			   privkey, privkey_len,
 			   data_auth == NULL && secret_handle == 0,
 			   parent, sk, 2, enc_secret, description,
-			   NULL, NULL, NULL);
+			   &creationData, &creationTicket, pcrValues);
 
  out_flush:
 	if (tssContext)