From patchwork Sat Feb 20 01:32:54 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matthew Garrett X-Patchwork-Id: 12096519 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id F025BC43381 for ; Sat, 20 Feb 2021 01:35:51 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D81CF64E77 for ; Sat, 20 Feb 2021 01:35:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230019AbhBTBfh (ORCPT ); Fri, 19 Feb 2021 20:35:37 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44362 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230021AbhBTBfM (ORCPT ); Fri, 19 Feb 2021 20:35:12 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3322AC061A2B for ; Fri, 19 Feb 2021 17:33:20 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id u1so8818051ybu.14 for ; Fri, 19 Feb 2021 17:33:20 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=Le2xy86M2g7vFjnuX/RXj2Z9OeFij6CrkPXndbL+3YM=; b=mUmzPMLQ2EhM5ohqQmvRX3zzjTI4HWgSEU8OF6ChUOqqCnyRmUWuS7Fg85iVRVC6D0 tlS1Qf9auwUtImh2HiRyyIF9zKTBlptAOolf0FoHdiiUmi0OW/MbiiVc5aFxLp0Ixvg1 cFeCJz9zZMvDoZq8E4OJNEyCKW06WEsbbi1WGGYwxmWGValJTlOySL2JTQ+lyT9DZEcG H2uY8kjDRo7fTGbtIHU0o77/si0NRfV4D/hT8to4ifU0A4zu8NUO1F1mRowKzzBwornS TsK6HQUR5c3H2o4BOGqBx1XATTaJyYRtgg6nD/nS2R0cTSOvXH5a/+N4HO8Uwo08O0Ik vzfQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=Le2xy86M2g7vFjnuX/RXj2Z9OeFij6CrkPXndbL+3YM=; b=AXFxqVMiKwkYjCDMRZlntzXN91aUjaZhniGUHf9yY5jBoUSOESvYzOpkp2/zQ7/a3M 1kF4A/IXevN02R7m5DlTDFc2hRl5STBfvZMnYMyDIBRqtyToF3yuU/V90BwyW0mEi7dV wr6nstSdZ/nMrnhsKRpfSh4CA1nzEuy4k8+fIfYZ6mCQlwxTE844+kmSmUAM5awsjpUU wTLngfAfdZ96NT+J2dzt9y638YvxYq4k6xJzg2IlzaStkuCxRo2phl+qxfXq1n/pqvkd Zdp4hl+ychII5ebh5DjDC3apKMqwbNy8B4ba1zimeSCfY1kTgwyIIXO7pnYo0VacRYt4 PhXg== X-Gm-Message-State: AOAM530RtBkJtVUC+iHbBYb+ehd9VtXopR1Z8H+oYsZvrYxkdNz/a8oK hloaLqSQJlLqpS0nIV3czQwb11yY1uRs4YXdhBX+HQ== X-Google-Smtp-Source: ABdhPJz+znzmydksCXNSz0qRUgqJOeUsiHxIZRQcS35YoWwljFjdXh5jNWJo2zOzCx4tGEmcAtw9+Y046tcbzA8xOA1QIg== Sender: "matthewgarrett via sendgmr" X-Received: from matthewgarrett-tmp.c.googlers.com ([fda3:e722:ac3:10:7f:e700:c0a8:1081]) (user=matthewgarrett job=sendgmr) by 2002:a25:a0c9:: with SMTP id i9mr15696415ybm.479.1613784799467; Fri, 19 Feb 2021 17:33:19 -0800 (PST) Date: Sat, 20 Feb 2021 01:32:54 +0000 In-Reply-To: <20210220013255.1083202-1-matthewgarrett@google.com> Message-Id: <20210220013255.1083202-9-matthewgarrett@google.com> Mime-Version: 1.0 References: <20210220013255.1083202-1-matthewgarrett@google.com> X-Mailer: git-send-email 2.30.0.617.g56c4b15f3c-goog Subject: [PATCH 8/9] pm: hibernate: Verify the digest encryption key From: Matthew Garrett 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 , Matthew Garrett Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org 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 --- 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);