summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/char/tpm/Kconfig11
-rw-r--r--drivers/char/tpm/Makefile1
-rw-r--r--drivers/char/tpm/tpm.h10
-rw-r--r--drivers/char/tpm/tpm2-cmd.c5
-rw-r--r--drivers/char/tpm/tpm2-sessions.c322
5 files changed, 349 insertions, 0 deletions
diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index 852bb9344788..ad37b55f5ff9 100644
--- a/drivers/char/tpm/Kconfig
+++ b/drivers/char/tpm/Kconfig
@@ -27,6 +27,17 @@ menuconfig TCG_TPM
if TCG_TPM
+config TCG_TPM2_HMAC
+ bool "Use HMAC and encrypted transactions on the TPM bus"
+ default y
+ help
+ Setting this causes us to deploy a scheme which uses request
+ and response HMACs in addition to encryption for
+ communicating with the TPM to prevent or detect bus snooping
+ and interposer attacks (see tpm-security.rst). Saying Y
+ here adds some encryption overhead to all kernel to TPM
+ transactions.
+
config HW_RANDOM_TPM
bool "TPM HW Random Number Generator support"
depends on TCG_TPM && HW_RANDOM && !(TCG_TPM=y && HW_RANDOM=m)
diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index ad3594e383e1..4c695b0388f3 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -17,6 +17,7 @@ tpm-y += eventlog/tpm1.o
tpm-y += eventlog/tpm2.o
tpm-y += tpm-buf.o
+tpm-$(CONFIG_TCG_TPM2_HMAC) += tpm2-sessions.o
tpm-$(CONFIG_ACPI) += tpm_ppi.o eventlog/acpi.o
tpm-$(CONFIG_EFI) += eventlog/efi.o
tpm-$(CONFIG_OF) += eventlog/of.o
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index cbc9d1e2974d..6b8b9956ba69 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -321,4 +321,14 @@ void tpm_bios_log_setup(struct tpm_chip *chip);
void tpm_bios_log_teardown(struct tpm_chip *chip);
int tpm_dev_common_init(void);
void tpm_dev_common_exit(void);
+
+#ifdef CONFIG_TCG_TPM2_HMAC
+int tpm2_sessions_init(struct tpm_chip *chip);
+#else
+static inline int tpm2_sessions_init(struct tpm_chip *chip)
+{
+ return 0;
+}
+#endif
+
#endif
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 93545be190a5..b0e72fb563d9 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -759,6 +759,11 @@ int tpm2_auto_startup(struct tpm_chip *chip)
rc = 0;
}
+ if (rc)
+ goto out;
+
+ rc = tpm2_sessions_init(chip);
+
out:
/*
* Infineon TPM in field upgrade mode will return no data for the number
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
new file mode 100644
index 000000000000..78b94097114d
--- /dev/null
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -0,0 +1,322 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2018 James.Bottomley@HansenPartnership.com
+ *
+ */
+
+#include "tpm.h"
+#include <asm/unaligned.h>
+
+/**
+ * tpm2_parse_create_primary() - parse the data returned from TPM_CC_CREATE_PRIMARY
+ *
+ * @chip: The TPM the primary was created under
+ * @buf: The response buffer from the chip
+ * @handle: pointer to be filled in with the return handle of the primary
+ * @hierarchy: The hierarchy the primary was created for
+ *
+ * Return:
+ * * 0 - OK
+ * * -errno - A system error
+ * * TPM_RC - A TPM error
+ */
+static int tpm2_parse_create_primary(struct tpm_chip *chip, struct tpm_buf *buf,
+ u32 *handle, u32 hierarchy)
+{
+ struct tpm_header *head = (struct tpm_header *)buf->data;
+ off_t offset_r = TPM_HEADER_SIZE, offset_t;
+ u16 len = TPM_HEADER_SIZE;
+ u32 total_len = be32_to_cpu(head->length);
+ u32 val, param_len;
+
+ *handle = tpm_buf_read_u32(buf, &offset_r);
+ param_len = tpm_buf_read_u32(buf, &offset_r);
+ /*
+ * param_len doesn't include the header, but all the other
+ * lengths and offsets do, so add it to parm len to make
+ * the comparisons easier
+ */
+ param_len += TPM_HEADER_SIZE;
+
+ if (param_len + 8 > total_len)
+ return -EINVAL;
+ len = tpm_buf_read_u16(buf, &offset_r);
+ offset_t = offset_r;
+ /* now we have the public area, compute the name of the object */
+ put_unaligned_be16(TPM_ALG_SHA256, chip->null_key_name);
+ sha256(&buf->data[offset_r], len, chip->null_key_name + 2);
+
+ /* validate the public key */
+ val = tpm_buf_read_u16(buf, &offset_t);
+
+ /* key type (must be what we asked for) */
+ if (val != TPM_ALG_ECC)
+ return -EINVAL;
+ val = tpm_buf_read_u16(buf, &offset_t);
+
+ /* name algorithm */
+ if (val != TPM_ALG_SHA256)
+ return -EINVAL;
+ val = tpm_buf_read_u32(buf, &offset_t);
+
+ /* object properties */
+ if (val != TPM2_OA_TMPL)
+ return -EINVAL;
+
+ /* auth policy (empty) */
+ val = tpm_buf_read_u16(buf, &offset_t);
+ if (val != 0)
+ return -EINVAL;
+
+ /* symmetric key parameters */
+ val = tpm_buf_read_u16(buf, &offset_t);
+ if (val != TPM_ALG_AES)
+ return -EINVAL;
+
+ /* symmetric key length */
+ val = tpm_buf_read_u16(buf, &offset_t);
+ if (val != AES_KEY_BITS)
+ return -EINVAL;
+
+ /* symmetric encryption scheme */
+ val = tpm_buf_read_u16(buf, &offset_t);
+ if (val != TPM_ALG_CFB)
+ return -EINVAL;
+
+ /* signing scheme */
+ val = tpm_buf_read_u16(buf, &offset_t);
+ if (val != TPM_ALG_NULL)
+ return -EINVAL;
+
+ /* ECC Curve */
+ val = tpm_buf_read_u16(buf, &offset_t);
+ if (val != TPM2_ECC_NIST_P256)
+ return -EINVAL;
+
+ /* KDF Scheme */
+ val = tpm_buf_read_u16(buf, &offset_t);
+ if (val != TPM_ALG_NULL)
+ return -EINVAL;
+
+ /* extract public key (x and y points) */
+ val = tpm_buf_read_u16(buf, &offset_t);
+ if (val != EC_PT_SZ)
+ return -EINVAL;
+ memcpy(chip->null_ec_key_x, &buf->data[offset_t], val);
+ offset_t += val;
+ val = tpm_buf_read_u16(buf, &offset_t);
+ if (val != EC_PT_SZ)
+ return -EINVAL;
+ memcpy(chip->null_ec_key_y, &buf->data[offset_t], val);
+ offset_t += val;
+
+ /* original length of the whole TPM2B */
+ offset_r += len;
+
+ /* should have exactly consumed the TPM2B public structure */
+ if (offset_t != offset_r)
+ return -EINVAL;
+ if (offset_r > param_len)
+ return -EINVAL;
+
+ /* creation data (skip) */
+ len = tpm_buf_read_u16(buf, &offset_r);
+ offset_r += len;
+ if (offset_r > param_len)
+ return -EINVAL;
+
+ /* creation digest (must be sha256) */
+ len = tpm_buf_read_u16(buf, &offset_r);
+ offset_r += len;
+ if (len != SHA256_DIGEST_SIZE || offset_r > param_len)
+ return -EINVAL;
+
+ /* TPMT_TK_CREATION follows */
+ /* tag, must be TPM_ST_CREATION (0x8021) */
+ val = tpm_buf_read_u16(buf, &offset_r);
+ if (val != TPM2_ST_CREATION || offset_r > param_len)
+ return -EINVAL;
+
+ /* hierarchy */
+ val = tpm_buf_read_u32(buf, &offset_r);
+ if (val != hierarchy || offset_r > param_len)
+ return -EINVAL;
+
+ /* the ticket digest HMAC (might not be sha256) */
+ len = tpm_buf_read_u16(buf, &offset_r);
+ offset_r += len;
+ if (offset_r > param_len)
+ return -EINVAL;
+
+ /*
+ * finally we have the name, which is a sha256 digest plus a 2
+ * byte algorithm type
+ */
+ len = tpm_buf_read_u16(buf, &offset_r);
+ if (offset_r + len != param_len + 8)
+ return -EINVAL;
+ if (len != SHA256_DIGEST_SIZE + 2)
+ return -EINVAL;
+
+ if (memcmp(chip->null_key_name, &buf->data[offset_r],
+ SHA256_DIGEST_SIZE + 2) != 0) {
+ dev_err(&chip->dev, "NULL Seed name comparison failed\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * tpm2_create_primary() - create a primary key using a fixed P-256 template
+ *
+ * @chip: the TPM chip to create under
+ * @hierarchy: The hierarchy handle to create under
+ * @handle: The returned volatile handle on success
+ *
+ * For platforms that might not have a persistent primary, this can be
+ * used to create one quickly on the fly (it uses Elliptic Curve not
+ * RSA, so even slow TPMs can create one fast). The template uses the
+ * TCG mandated H one for non-endorsement ECC primaries, i.e. P-256
+ * elliptic curve (the only current one all TPM2s are required to
+ * have) a sha256 name hash and no policy.
+ *
+ * Return:
+ * * 0 - OK
+ * * -errno - A system error
+ * * TPM_RC - A TPM error
+ */
+static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy,
+ u32 *handle)
+{
+ int rc;
+ struct tpm_buf buf;
+ struct tpm_buf template;
+
+ rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE_PRIMARY);
+ if (rc)
+ return rc;
+
+ rc = tpm_buf_init_sized(&template);
+ if (rc) {
+ tpm_buf_destroy(&buf);
+ return rc;
+ }
+
+ /*
+ * create the template. Note: in order for userspace to
+ * verify the security of the system, it will have to create
+ * and certify this NULL primary, meaning all the template
+ * parameters will have to be identical, so conform exactly to
+ * the TCG TPM v2.0 Provisioning Guidance for the SRK ECC
+ * key H template (H has zero size unique points)
+ */
+
+ /* key type */
+ tpm_buf_append_u16(&template, TPM_ALG_ECC);
+
+ /* name algorithm */
+ tpm_buf_append_u16(&template, TPM_ALG_SHA256);
+
+ /* object properties */
+ tpm_buf_append_u32(&template, TPM2_OA_TMPL);
+
+ /* sauth policy (empty) */
+ tpm_buf_append_u16(&template, 0);
+
+ /* BEGIN parameters: key specific; for ECC*/
+
+ /* symmetric algorithm */
+ tpm_buf_append_u16(&template, TPM_ALG_AES);
+
+ /* bits for symmetric algorithm */
+ tpm_buf_append_u16(&template, AES_KEY_BITS);
+
+ /* algorithm mode (must be CFB) */
+ tpm_buf_append_u16(&template, TPM_ALG_CFB);
+
+ /* scheme (NULL means any scheme) */
+ tpm_buf_append_u16(&template, TPM_ALG_NULL);
+
+ /* ECC Curve ID */
+ tpm_buf_append_u16(&template, TPM2_ECC_NIST_P256);
+
+ /* KDF Scheme */
+ tpm_buf_append_u16(&template, TPM_ALG_NULL);
+
+ /* unique: key specific; for ECC it is two zero size points */
+ tpm_buf_append_u16(&template, 0);
+ tpm_buf_append_u16(&template, 0);
+
+ /* END parameters */
+
+ /* primary handle */
+ tpm_buf_append_u32(&buf, hierarchy);
+ tpm_buf_append_empty_auth(&buf, TPM2_RS_PW);
+
+ /* sensitive create size is 4 for two empty buffers */
+ tpm_buf_append_u16(&buf, 4);
+
+ /* sensitive create auth data (empty) */
+ tpm_buf_append_u16(&buf, 0);
+
+ /* sensitive create sensitive data (empty) */
+ tpm_buf_append_u16(&buf, 0);
+
+ /* the public template */
+ tpm_buf_append(&buf, template.data, template.length);
+ tpm_buf_destroy(&template);
+
+ /* outside info (empty) */
+ tpm_buf_append_u16(&buf, 0);
+
+ /* creation PCR (none) */
+ tpm_buf_append_u32(&buf, 0);
+
+ rc = tpm_transmit_cmd(chip, &buf, 0,
+ "attempting to create NULL primary");
+
+ if (rc == TPM2_RC_SUCCESS)
+ rc = tpm2_parse_create_primary(chip, &buf, handle, hierarchy);
+
+ tpm_buf_destroy(&buf);
+
+ return rc;
+}
+
+static int tpm2_create_null_primary(struct tpm_chip *chip)
+{
+ u32 null_key;
+ int rc;
+
+ rc = tpm2_create_primary(chip, TPM2_RH_NULL, &null_key);
+
+ if (rc == TPM2_RC_SUCCESS) {
+ unsigned int offset = 0; /* dummy offset for null key context */
+
+ rc = tpm2_save_context(chip, null_key, chip->null_key_context,
+ sizeof(chip->null_key_context), &offset);
+ tpm2_flush_context(chip, null_key);
+ }
+
+ return rc;
+}
+
+/**
+ * tpm2_sessions_init() - start of day initialization for the sessions code
+ * @chip: TPM chip
+ *
+ * Derive and context save the null primary and allocate memory in the
+ * struct tpm_chip for the authorizations.
+ */
+int tpm2_sessions_init(struct tpm_chip *chip)
+{
+ int rc;
+
+ rc = tpm2_create_null_primary(chip);
+ if (rc)
+ dev_err(&chip->dev, "TPM: security failed (NULL seed derivation): %d\n", rc);
+
+ return rc;
+}