linux-integrity.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Matthew Garrett <matthewgarrett@google.com>
To: linux-kernel@vger.kernel.org
Cc: linux-integrity@vger.kernel.org, linux-pm@vger.kernel.org,
	keyrings@vger.kernel.org, zohar@linux.ibm.com,
	jejb@linux.ibm.com, jarkko@kernel.org, corbet@lwn.net,
	rjw@rjwysocki.net, Matthew Garrett <matthewgarrett@google.com>,
	Matthew Garrett <mjg59@google.com>
Subject: [PATCH 8/9] pm: hibernate: Verify the digest encryption key
Date: Sat, 20 Feb 2021 01:32:54 +0000	[thread overview]
Message-ID: <20210220013255.1083202-9-matthewgarrett@google.com> (raw)
In-Reply-To: <20210220013255.1083202-1-matthewgarrett@google.com>

We want to ensure that the key used to encrypt the digest was created by
the kernel during hibernation. To do this we request that the TPM include
information about the value of PCR 23 at the time of key creation in the
sealed blob. On resume, we can ask the TPM to certify that the creation
data is accurate and then make sure that the PCR information in the blob
corresponds to the expected value. Since only the kernel can touch PCR
23, if an attacker generates a key themselves the value of PCR 23 will
have been different, allowing us to reject the key and boot normally
instead of resuming.

Signed-off-by: Matthew Garrett <mjg59@google.com>
---
 include/linux/tpm.h |   1 +
 kernel/power/tpm.c  | 150 +++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 148 insertions(+), 3 deletions(-)

diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index e2075e2242a0..f6970986b097 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -216,6 +216,7 @@ enum tpm2_command_codes {
 	TPM2_CC_SELF_TEST	        = 0x0143,
 	TPM2_CC_STARTUP		        = 0x0144,
 	TPM2_CC_SHUTDOWN	        = 0x0145,
+	TPM2_CC_CERTIFYCREATION	        = 0x014A,
 	TPM2_CC_NV_READ                 = 0x014E,
 	TPM2_CC_CREATE		        = 0x0153,
 	TPM2_CC_LOAD		        = 0x0157,
diff --git a/kernel/power/tpm.c b/kernel/power/tpm.c
index 953dcbdc56d8..34e6cfb98ce4 100644
--- a/kernel/power/tpm.c
+++ b/kernel/power/tpm.c
@@ -14,6 +14,12 @@ static struct tpm_digest digest = { .alg_id = TPM_ALG_SHA256,
 		   0xf1, 0x22, 0x38, 0x6c, 0x33, 0xb1, 0x14, 0xb7, 0xec, 0x05,
 		   0x5f, 0x49}};
 
+/* sha256(sha256(empty_pcr | digest)) */
+static char expected_digest[] = {0x2f, 0x96, 0xf2, 0x1b, 0x70, 0xa9, 0xe8,
+	0x42, 0x25, 0x8e, 0x66, 0x07, 0xbe, 0xbc, 0xe3, 0x1f, 0x2c, 0x84, 0x4a,
+	0x3f, 0x85, 0x17, 0x31, 0x47, 0x9a, 0xa5, 0x53, 0xbb, 0x23, 0x0c, 0x32,
+	0xf3};
+
 struct skcipher_def {
 	struct scatterlist sg;
 	struct crypto_skcipher *tfm;
@@ -21,6 +27,39 @@ struct skcipher_def {
 	struct crypto_wait wait;
 };
 
+static int sha256_data(char *buf, int size, char *output)
+{
+	struct crypto_shash *tfm;
+	struct shash_desc *desc;
+	int ret;
+
+	tfm = crypto_alloc_shash("sha256", 0, 0);
+	if (IS_ERR(tfm))
+		return PTR_ERR(tfm);
+
+	desc = kmalloc(sizeof(struct shash_desc) +
+			       crypto_shash_descsize(tfm), GFP_KERNEL);
+	if (!desc) {
+		crypto_free_shash(tfm);
+		return -ENOMEM;
+	}
+
+	desc->tfm = tfm;
+	ret = crypto_shash_init(desc);
+	if (ret != 0) {
+		crypto_free_shash(tfm);
+		kfree(desc);
+		return ret;
+	}
+
+	crypto_shash_update(desc, buf, size);
+	crypto_shash_final(desc, output);
+	crypto_free_shash(desc->tfm);
+	kfree(desc);
+
+	return 0;
+}
+
 static int swsusp_enc_dec(struct trusted_key_payload *payload, char *buf,
 			  int enc)
 {
@@ -86,6 +125,58 @@ static int swsusp_enc_dec(struct trusted_key_payload *payload, char *buf,
 	return ret;
 }
 
+static int tpm_certify_creationdata(struct tpm_chip *chip,
+				    struct trusted_key_payload *payload)
+{
+	struct tpm_header *head;
+	struct tpm_buf buf;
+	int rc;
+
+	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CERTIFYCREATION);
+	if (rc)
+		return rc;
+
+	/* Use TPM_RH_NULL for signHandle */
+	tpm_buf_append_u32(&buf, 0x40000007);
+
+	/* Object handle */
+	tpm_buf_append_u32(&buf, payload->blob_handle);
+
+	/* Auth */
+	tpm_buf_append_u32(&buf, 9);
+	tpm_buf_append_u32(&buf, TPM2_RS_PW);
+	tpm_buf_append_u16(&buf, 0);
+	tpm_buf_append_u8(&buf, 0);
+	tpm_buf_append_u16(&buf, 0);
+
+	/* Qualifying data */
+	tpm_buf_append_u16(&buf, 0);
+
+	/* Creation data hash */
+	tpm_buf_append_u16(&buf, payload->creation_hash_len);
+	tpm_buf_append(&buf, payload->creation_hash,
+		       payload->creation_hash_len);
+
+	/* signature scheme */
+	tpm_buf_append_u16(&buf, TPM_ALG_NULL);
+
+	/* creation ticket */
+	tpm_buf_append(&buf, payload->tk, payload->tk_len);
+
+	rc = tpm_send(chip, buf.data, tpm_buf_length(&buf));
+	if (rc)
+		goto out;
+
+	head = (struct tpm_header *)buf.data;
+
+	if (head->return_code != 0)
+		rc = -EINVAL;
+out:
+	tpm_buf_destroy(&buf);
+
+	return rc;
+}
+
 int swsusp_encrypt_digest(struct swsusp_header *header)
 {
 	const struct cred *cred = current_cred();
@@ -95,7 +186,7 @@ int swsusp_encrypt_digest(struct swsusp_header *header)
 	struct key *key;
 	int ret, i;
 
-	char *keyinfo = "new\t32\tkeyhandle=0x81000001";
+	char *keyinfo = "new\t32\tkeyhandle=0x81000001\tcreationpcrs=0x00800000";
 
 	chip = tpm_default_chip();
 
@@ -164,6 +255,7 @@ int swsusp_decrypt_digest(struct swsusp_header *header)
 	char *keytemplate = "load\t%s\tkeyhandle=0x81000001";
 	struct trusted_key_payload *payload;
 	struct tpm_digest *digests = NULL;
+	char certhash[SHA256_DIGEST_SIZE];
 	char *blobstring = NULL;
 	char *keyinfo = NULL;
 	struct tpm_chip *chip;
@@ -184,8 +276,10 @@ int swsusp_decrypt_digest(struct swsusp_header *header)
 
 	digests = kcalloc(chip->nr_allocated_banks, sizeof(struct tpm_digest),
 			  GFP_KERNEL);
-	if (!digests)
+	if (!digests) {
+		ret = -ENOMEM;
 		goto reset;
+	}
 
 	for (i = 0; i <= chip->nr_allocated_banks; i++) {
 		digests[i].alg_id = chip->allocated_banks[i].alg_id;
@@ -227,8 +321,58 @@ int swsusp_decrypt_digest(struct swsusp_header *header)
 
 	payload = key->payload.data[0];
 
-	ret = swsusp_enc_dec(payload, header->digest, 0);
+	ret = sha256_data(payload->creation, payload->creation_len, certhash);
+	if (ret < 0)
+		goto out;
+
+	if (memcmp(payload->creation_hash, certhash, SHA256_DIGEST_SIZE) != 0) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = tpm_certify_creationdata(chip, payload);
+	if (ret != 0) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* We now know that the creation data is authentic - parse it */
+
+	/* TPML_PCR_SELECTION.count */
+	if (be32_to_cpu(*(int *)payload->creation) != 1) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (be16_to_cpu(*(u16 *)&payload->creation[4]) != TPM_ALG_SHA256) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (*(char *)&payload->creation[6] != 3) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* PCR 23 selected */
+	if (be32_to_cpu(*(int *)&payload->creation[6]) != 0x03000080) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (be16_to_cpu(*(u16 *)&payload->creation[10]) !=
+	    SHA256_DIGEST_SIZE) {
+		ret = -EINVAL;
+		goto out;
+	}
 
+	if (memcmp(&payload->creation[12], expected_digest,
+		   SHA256_DIGEST_SIZE) != 0) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = swsusp_enc_dec(payload, header->digest, 0);
 out:
 	key_revoke(key);
 	key_put(key);
-- 
2.30.0.617.g56c4b15f3c-goog


  parent reply	other threads:[~2021-02-20  1:35 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-02-20  1:32 [PATCH 0/9] Enable hibernation when Lockdown is enabled Matthew Garrett
2021-02-20  1:32 ` [PATCH 1/9] tpm: Add support for in-kernel resetting of PCRs Matthew Garrett
2021-02-20  2:52   ` Jarkko Sakkinen
2021-02-20  1:32 ` [PATCH 2/9] tpm: Allow PCR 23 to be restricted to kernel-only use Matthew Garrett
2021-02-20  3:02   ` Jarkko Sakkinen
2021-02-24 17:12   ` Jarkko Sakkinen
2021-02-24 18:00   ` James Bottomley
2021-02-28  7:59     ` Matthew Garrett
2021-02-20  1:32 ` [PATCH 3/9] security: keys: trusted: Parse out individual components of the key blob Matthew Garrett
2021-02-20  3:05   ` Jarkko Sakkinen
2021-02-22  7:36     ` Matthew Garrett
2021-02-24 17:22       ` Jarkko Sakkinen
2021-02-20  1:32 ` [PATCH 4/9] security: keys: trusted: Store the handle of a loaded key Matthew Garrett
2021-02-20  3:06   ` Jarkko Sakkinen
2021-02-20  1:32 ` [PATCH 5/9] security: keys: trusted: Allow storage of PCR values in creation data Matthew Garrett
2021-02-20  3:09   ` Jarkko Sakkinen
2021-02-21 19:44     ` Ben Boeckel
2021-02-22  7:41     ` Matthew Garrett
2021-02-20  1:32 ` [PATCH 6/9] pm: hibernate: Optionally store and verify a hash of the image Matthew Garrett
2021-05-05 18:18   ` Evan Green
2021-02-20  1:32 ` [PATCH 7/9] pm: hibernate: Optionally use TPM-backed keys to protect image integrity Matthew Garrett
2021-02-20  2:20   ` Randy Dunlap
2021-02-22  7:41     ` Matthew Garrett
2021-02-20  1:32 ` Matthew Garrett [this message]
2021-02-20  1:32 ` [PATCH 9/9] pm: hibernate: seal the encryption key with a PCR policy Matthew Garrett
2021-05-04 21:56 ` [PATCH 0/9] Enable hibernation when Lockdown is enabled Evan Green
2021-05-05  3:18   ` Jarkko Sakkinen
2021-05-05  3:19     ` Jarkko Sakkinen
2021-05-05 18:02       ` Evan Green

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210220013255.1083202-9-matthewgarrett@google.com \
    --to=matthewgarrett@google.com \
    --cc=corbet@lwn.net \
    --cc=jarkko@kernel.org \
    --cc=jejb@linux.ibm.com \
    --cc=keyrings@vger.kernel.org \
    --cc=linux-integrity@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=mjg59@google.com \
    --cc=rjw@rjwysocki.net \
    --cc=zohar@linux.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).