diff mbox series

[v7,12/21] tpm: Add NULL primary creation

Message ID 20240213171334.30479-13-James.Bottomley@HansenPartnership.com (mailing list archive)
State New
Headers show
Series add integrity and security to TPM2 transactions | expand

Commit Message

James Bottomley Feb. 13, 2024, 5:13 p.m. UTC
The session handling code uses a "salted" session, meaning a session
whose salt is encrypted to the public part of another TPM key so an
observer cannot obtain it (and thus deduce the session keys).  This
patch creates and context saves in the tpm_chip area the primary key
of the NULL hierarchy for this purpose.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>

---
v6: split out of original HMAC patch update config name
v7: rename null seed parameters
---
 drivers/char/tpm/Kconfig         |  11 ++
 drivers/char/tpm/Makefile        |   1 +
 drivers/char/tpm/tpm.h           |  10 ++
 drivers/char/tpm/tpm2-cmd.c      |   5 +
 drivers/char/tpm/tpm2-sessions.c | 276 +++++++++++++++++++++++++++++++
 include/linux/tpm.h              |  49 ++++++
 6 files changed, 352 insertions(+)
 create mode 100644 drivers/char/tpm/tpm2-sessions.c

Comments

Jarkko Sakkinen Feb. 23, 2024, 3:51 p.m. UTC | #1
On Tue Feb 13, 2024 at 7:13 PM EET, James Bottomley wrote:
> The session handling code uses a "salted" session, meaning a session
> whose salt is encrypted to the public part of another TPM key so an
> observer cannot obtain it (and thus deduce the session keys).  This
> patch creates and context saves in the tpm_chip area the primary key
> of the NULL hierarchy for this purpose.
>
> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
>
> ---
> v6: split out of original HMAC patch update config name
> v7: rename null seed parameters
> ---
>  drivers/char/tpm/Kconfig         |  11 ++
>  drivers/char/tpm/Makefile        |   1 +
>  drivers/char/tpm/tpm.h           |  10 ++
>  drivers/char/tpm/tpm2-cmd.c      |   5 +
>  drivers/char/tpm/tpm2-sessions.c | 276 +++++++++++++++++++++++++++++++
>  include/linux/tpm.h              |  49 ++++++
>  6 files changed, 352 insertions(+)
>  create mode 100644 drivers/char/tpm/tpm2-sessions.c
>
> diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
> index 927088b2c3d3..e3c39a83171b 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 encrypted and HMACd transactions on the TPM bus"

How about simply "Use HMAC-encrypted transactions"

The details are anyway in the description.

> +	default y
> +	help
> +          Setting this causes us to deploy a scheme which uses request
          ~~~

extra spaces

> +	  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);

I'm sorry but "sessions" and "init" are the worst possible terminology I
could every pick when trying to make a function that self-documents
itself :-)

I'd like to see here well-scoped name so that it is easy to connect
this to its purpose when reviewing some other patches.


> +#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..9fc263ee02c2
> --- /dev/null
> +++ b/drivers/char/tpm/tpm2-sessions.c
> @@ -0,0 +1,276 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/*
> + * Copyright (C) 2018 James.Bottomley@HansenPartnership.com
> + *

useless extra line in the commment

> + */
> +
> +#include "tpm.h"
> +

spurious newline

> +#include <asm/unaligned.h>

spurious newline
> +
> +#include <crypto/aes.h>

spurious newline
> +
> +/* if you change to AES256, you only need change this */
> +#define AES_KEYBYTES	AES_KEYSIZE_128

AES_KEY_BYTES. Also the comment is talking about changing something
without documenting what it is. Even not having the comment at all
would be less confusing.

> +
spurious newline

> +#define AES_KEYBITS	(AES_KEYBYTES*8)

AES_KEY_BITS

Also, documentation missing.

> +

Documentation missing for "tpm2_parse_create_primary". It is a complex
function despite being not exported so definitely needs documentation.


> +static int tpm2_parse_create_primary(struct tpm_chip *chip, struct tpm_buf *buf,
> +				     u32 *nullkey)
                                          ~~~~~~~
					  null_key

> +{
> +	struct tpm_header *head = (struct tpm_header *)buf->data;
> +	off_t offset_r = TPM_HEADER_SIZE, offset_t;
> +	u16 len = TPM_HEADER_SIZE;
> +	u32 tot_len = be32_to_cpu(head->length);

no good reason to use more confusing namme "tot_len", instead of more
readable "total_len".

> +	u32 val, parm_len;
> +
> +	*nullkey = tpm_buf_read_u32(buf, &offset_r);
> +	parm_len = tpm_buf_read_u32(buf, &offset_r);
        ~~~~~~~~
	param_len

> +	/*
> +	 * parm_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
> +	 */
> +	parm_len += TPM_HEADER_SIZE;
> +
> +	if (parm_len + 8 > tot_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_NO_DA |
> +		    TPM2_OA_FIXED_TPM |
> +		    TPM2_OA_FIXED_PARENT |
> +		    TPM2_OA_SENSITIVE_DATA_ORIGIN |
> +		    TPM2_OA_USER_WITH_AUTH |
> +		    TPM2_OA_DECRYPT |
> +		    TPM2_OA_RESTRICTED))

Please make define a mask constant for these bits and use it here
instead. There's just too many and make the code really unredable.
I'd even suggesting documenting that constant with some rationale
for the mask.

Here the logic is obviously to fix the primary key to the exact
chip and make unmovable (cannot be migrated), isn't it? That would
perfectly sufficient documentation for the constant, or along the
lines.


> +		return -EINVAL;
> +	/* auth policy (empty) */
> +	val = tpm_buf_read_u16(buf, &offset_t);
> +	if (val != 0)
> +		return -EINVAL;

I'd add empty line betwen each of these checks to make it more
readable.

> +	/* 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_KEYBITS)
> +		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;
> +	/* x point */
> +	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 > parm_len)
> +		return -EINVAL;
> +	/* creation data (skip) */
> +	len = tpm_buf_read_u16(buf, &offset_r);
> +	offset_r += len;
> +	if (offset_r > parm_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 > parm_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 > parm_len)
> +		return -EINVAL;
> +	/* hierarchy (must be NULL) */
> +	val = tpm_buf_read_u32(buf, &offset_r);
> +	if (val != TPM2_RH_NULL || offset_r > parm_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 > parm_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 != parm_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;
> +}
>

documentation missing:

> +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
> +	 */
> +
> +	/* 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_NO_DA |
> +			   TPM2_OA_FIXED_TPM |
> +			   TPM2_OA_FIXED_PARENT |
> +			   TPM2_OA_SENSITIVE_DATA_ORIGIN |
> +			   TPM2_OA_USER_WITH_AUTH |
> +			   TPM2_OA_DECRYPT |
> +			   TPM2_OA_RESTRICTED);
> +	/* 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, 128);
> +	/* 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 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);
> +
> +	tpm_buf_destroy(&buf);
> +
> +	return rc;
> +}
> +
> +static int tpm2_create_null_primary(struct tpm_chip *chip)
> +{
> +	u32 nullkey;
            ~~~~~~~
	    null_key
   

> +	int rc;
> +
> +	rc = tpm2_create_primary(chip, TPM2_RH_NULL, &nullkey);
> +
> +	if (rc == TPM2_RC_SUCCESS) {
> +		unsigned int offset = 0; /* dummy offset for null key context */
> +
> +		rc = tpm2_save_context(chip, nullkey, chip->null_key_context,
> +				       sizeof(chip->null_key_context), &offset);
> +		tpm2_flush_context(chip, nullkey);
> +	}
> +
> +	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.

isn't this exactly for HMAC authorization at least in the patch set
scope? plural confuses me here.

> + */
> +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;
> +}
> +EXPORT_SYMBOL(tpm2_sessions_init);
> diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> index 6be263509e81..3060ab794afb 100644
> --- a/include/linux/tpm.h
> +++ b/include/linux/tpm.h
> @@ -35,12 +35,15 @@ struct trusted_key_options;
>  enum tpm_algorithms {
>  	TPM_ALG_ERROR		= 0x0000,
>  	TPM_ALG_SHA1		= 0x0004,
> +	TPM_ALG_AES		= 0x0006,
>  	TPM_ALG_KEYEDHASH	= 0x0008,
>  	TPM_ALG_SHA256		= 0x000B,
>  	TPM_ALG_SHA384		= 0x000C,
>  	TPM_ALG_SHA512		= 0x000D,
>  	TPM_ALG_NULL		= 0x0010,
>  	TPM_ALG_SM3_256		= 0x0012,
> +	TPM_ALG_ECC		= 0x0023,
> +	TPM_ALG_CFB		= 0x0043,
>  };
>  
>  /*
> @@ -49,6 +52,11 @@ enum tpm_algorithms {
>   */
>  #define TPM_MAX_HASHES	5
>  
> +enum tpm2_curves {
> +	TPM2_ECC_NONE		= 0x0000,
> +	TPM2_ECC_NIST_P256	= 0x0003,
> +};
> +
>  struct tpm_digest {
>  	u16 alg_id;
>  	u8 digest[TPM_MAX_DIGEST_SIZE];
> @@ -116,6 +124,20 @@ struct tpm_chip_seqops {
>  	const struct seq_operations *seqops;
>  };
>  
> +/* fixed define for the curve we use which is NIST_P256 */
> +#define EC_PT_SZ	32
> +
> +/*
> + * fixed define for the size of a name.  This is actually HASHALG size
> + * plus 2, so 32 for SHA256
> + */
> +#define TPM2_NAME_SIZE	34
> +
> +/*
> + * The maximum size for an object context
> + */
> +#define TPM2_MAX_CONTEXT_SIZE 4096
> +
>  struct tpm_chip {
>  	struct device dev;
>  	struct device devs;
> @@ -170,6 +192,14 @@ struct tpm_chip {
>  
>  	/* active locality */
>  	int locality;
> +
> +#ifdef CONFIG_TCG_TPM2_HMAC
> +	/* details for communication security via sessions */
> +	u8 null_key_context[TPM2_MAX_CONTEXT_SIZE]; /* context for NULL seed */

	/* Saved context of the null seed: */

I like the description on top because they suffer less alignment
issues and can have more verbose explanation. "context for NULL
seed" is good as no comment at all./

> +	u8 null_key_name[TPM2_NAME_SIZE];	 /* name of NULL seed */

	/* Digest of the public area: */

This is not totally accurate but way more documenting than the
current description, which is tautology, i.e. you are saying that
"name is name" in the comment leaving the reader questioning what
the heck is name.

Not totally accurate i.e. missing concatenated alg identifier but
good enough...


> +	u8 null_ec_key_x[EC_PT_SZ];
> +	u8 null_ec_key_y[EC_PT_SZ];
> +#endif

this is now very nice clean and udnerstandable:

I'd prefer tho this formatting:

	/* Name of the NULL seed: */
	u8 null_key_name[TPM2_NAME_SIZE];


>  };
>  
>  #define TPM_HEADER_SIZE		10
> @@ -194,6 +224,7 @@ enum tpm2_timeouts {
>  enum tpm2_structures {
>  	TPM2_ST_NO_SESSIONS	= 0x8001,
>  	TPM2_ST_SESSIONS	= 0x8002,
> +	TPM2_ST_CREATION	= 0x8021,
>  };
>  
>  /* Indicates from what layer of the software stack the error comes from */
> @@ -243,6 +274,7 @@ enum tpm2_command_codes {
>  };
>  
>  enum tpm2_permanent_handles {
> +	TPM2_RH_NULL		= 0x40000007,
>  	TPM2_RS_PW		= 0x40000009,
>  };
>  
> @@ -318,7 +350,11 @@ struct tpm_buf {
>  enum tpm2_object_attributes {
>  	TPM2_OA_FIXED_TPM		= BIT(1),
>  	TPM2_OA_FIXED_PARENT		= BIT(4),
> +	TPM2_OA_SENSITIVE_DATA_ORIGIN	= BIT(5),
>  	TPM2_OA_USER_WITH_AUTH		= BIT(6),
> +	TPM2_OA_NO_DA			= BIT(10),
> +	TPM2_OA_RESTRICTED		= BIT(16),
> +	TPM2_OA_DECRYPT			= BIT(17),
>  };
>  

Here would be a great place to declarate aforementioned mask.

>  enum tpm2_session_attributes {
> @@ -373,6 +409,15 @@ extern int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
>  extern int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max);
>  extern struct tpm_chip *tpm_default_chip(void);
>  void tpm2_flush_context(struct tpm_chip *chip, u32 handle);

please add empty line here.

> +static inline void tpm_buf_append_empty_auth(struct tpm_buf *buf, u32 handle)
> +{
> +	/* simple authorization for empty auth */
> +	tpm_buf_append_u32(buf, 9);		/* total length of auth */
> +	tpm_buf_append_u32(buf, handle);
> +	tpm_buf_append_u16(buf, 0);		/* nonce len */
> +	tpm_buf_append_u8(buf, 0);		/* attributes */
> +	tpm_buf_append_u16(buf, 0);		/* hmac len */
> +}
>  #else
>  static inline int tpm_is_tpm2(struct tpm_chip *chip)
>  {
> @@ -399,5 +444,9 @@ static inline struct tpm_chip *tpm_default_chip(void)
>  {
>  	return NULL;
>  }
> +
> +static inline void tpm_buf_append_empty_auth(struct tpm_buf *buf, u32 handle)
> +{
> +}
>  #endif
>  #endif

I skipped first eleven patches because they are completed. This patch
will be finished once all the numerous style issues have been fixed.
Approach is totally correct...

BR, Jarkko
Gabríel Arthúr Pétursson March 30, 2024, 6:48 p.m. UTC | #2
On Tue, 2024-02-13 at 12:13 -0500, James Bottomley wrote:
> +	/* unique: key specific; for ECC it is two points */
> +	tpm_buf_append_u16(&template, 0);
> +	tpm_buf_append_u16(&template, 0);

Shouldn't the points be 32 bytes each in size, filled with zeros?

The TCP TPM 2.0 Provisioning Guidance defines the SRK Template as a
diff on top of Template L-2 (for ECC keys) as defined in the EK
Credential Profile 2.0 document.

Template L-2 calls for the X and Y points to be of 32 bytes each,
filled with zeros. The Provisioning Guidance does not call for zero-
sized points.

For example, let's create an ECC Endorsement Key using the standard
template then print its name:

   tpm2_createek -G ecc -c /dev/null -u ./ek.pub
   tpm2_loadexternal -c n -u ./ek.pub

Equivalently using tpm2_createprimary:

   perl -e 'print "\0"x64' | tpm2_createprimary -C e -o ./ek.pub -G ecc -a 'fixedtpm|fixedparent|sensitivedataorigin|adminwithpolicy|restricted|decrypt' -L 837197674484b3f81a90cc8d46a5d724fd52d76e06520b64f2a1da1b331469aa -u -
   tpm2_loadexternal -c n -u ./ek.pub

You'll find that the key's public modulus matches that of the EK
Certificate imprinted by the manufacturer, indicating we got the
template correct.

To generate a standard SRK key, the TCG TPM2 Provisioning Guidance
states we should:

	1. set userWithAuth,
	2. clear adminWithPolicy
	3. set noDA, and
	4. clear the authorization policy.

There's no mention of alterations to the unique field.

Let's also create the key in the null hierarchy:

   perl -e 'print "\0"x64' | tpm2_createprimary -C n -o ./null.pub -G ecc -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda|restricted|decrypt' -u -
   tpm2_loadexternal -c n -u ./null.pub

The name does not match the kernel's name for the null key.
Jarkko Sakkinen March 31, 2024, 4 p.m. UTC | #3
On Sat Mar 30, 2024 at 8:48 PM EET, Gabríel Arthúr Pétursson wrote:
> On Tue, 2024-02-13 at 12:13 -0500, James Bottomley wrote:
> > +	/* unique: key specific; for ECC it is two points */
> > +	tpm_buf_append_u16(&template, 0);
> > +	tpm_buf_append_u16(&template, 0);
>
> Shouldn't the points be 32 bytes each in size, filled with zeros?
>
> The TCP TPM 2.0 Provisioning Guidance defines the SRK Template as a
> diff on top of Template L-2 (for ECC keys) as defined in the EK
> Credential Profile 2.0 document.
>
> Template L-2 calls for the X and Y points to be of 32 bytes each,
> filled with zeros. The Provisioning Guidance does not call for zero-
> sized points.
>
> For example, let's create an ECC Endorsement Key using the standard
> template then print its name:
>
>    tpm2_createek -G ecc -c /dev/null -u ./ek.pub
>    tpm2_loadexternal -c n -u ./ek.pub
>
> Equivalently using tpm2_createprimary:
>
>    perl -e 'print "\0"x64' | tpm2_createprimary -C e -o ./ek.pub -G ecc -a 'fixedtpm|fixedparent|sensitivedataorigin|adminwithpolicy|restricted|decrypt' -L 837197674484b3f81a90cc8d46a5d724fd52d76e06520b64f2a1da1b331469aa -u -
>    tpm2_loadexternal -c n -u ./ek.pub
>
> You'll find that the key's public modulus matches that of the EK
> Certificate imprinted by the manufacturer, indicating we got the
> template correct.
>
> To generate a standard SRK key, the TCG TPM2 Provisioning Guidance
> states we should:
>
> 	1. set userWithAuth,
> 	2. clear adminWithPolicy
> 	3. set noDA, and
> 	4. clear the authorization policy.
>
> There's no mention of alterations to the unique field.
>
> Let's also create the key in the null hierarchy:
>
>    perl -e 'print "\0"x64' | tpm2_createprimary -C n -o ./null.pub -G ecc -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda|restricted|decrypt' -u -
>    tpm2_loadexternal -c n -u ./null.pub
>
> The name does not match the kernel's name for the null key.

Null key is not provisioned, what is the motivation here?

Not saying no, just asking for details...

There's couple of things that lack in this patch set ATM:

1. Neither kselftest additions nor not even proper testing
   instructions. Why 21 patches and zero tests? How one should
   decide when the work is "complete"?
2. I still don't know what version of QEMU I should patch to
   test corner cases from an URL, which I cannot recall :-)
   Highlights the first issue.

So for the time being the patch set is NAK just because we lack
clear definition of done here. I revisit it only when I know how
to test it.

BR, Jarkko
Jarkko Sakkinen March 31, 2024, 4:09 p.m. UTC | #4
On Sun Mar 31, 2024 at 7:00 PM EEST, Jarkko Sakkinen wrote:
> On Sat Mar 30, 2024 at 8:48 PM EET, Gabríel Arthúr Pétursson wrote:
> > On Tue, 2024-02-13 at 12:13 -0500, James Bottomley wrote:
> > > +	/* unique: key specific; for ECC it is two points */
> > > +	tpm_buf_append_u16(&template, 0);
> > > +	tpm_buf_append_u16(&template, 0);
> >
> > Shouldn't the points be 32 bytes each in size, filled with zeros?
> >
> > The TCP TPM 2.0 Provisioning Guidance defines the SRK Template as a
> > diff on top of Template L-2 (for ECC keys) as defined in the EK
> > Credential Profile 2.0 document.
> >
> > Template L-2 calls for the X and Y points to be of 32 bytes each,
> > filled with zeros. The Provisioning Guidance does not call for zero-
> > sized points.
> >
> > For example, let's create an ECC Endorsement Key using the standard
> > template then print its name:
> >
> >    tpm2_createek -G ecc -c /dev/null -u ./ek.pub
> >    tpm2_loadexternal -c n -u ./ek.pub
> >
> > Equivalently using tpm2_createprimary:
> >
> >    perl -e 'print "\0"x64' | tpm2_createprimary -C e -o ./ek.pub -G ecc -a 'fixedtpm|fixedparent|sensitivedataorigin|adminwithpolicy|restricted|decrypt' -L 837197674484b3f81a90cc8d46a5d724fd52d76e06520b64f2a1da1b331469aa -u -
> >    tpm2_loadexternal -c n -u ./ek.pub
> >
> > You'll find that the key's public modulus matches that of the EK
> > Certificate imprinted by the manufacturer, indicating we got the
> > template correct.
> >
> > To generate a standard SRK key, the TCG TPM2 Provisioning Guidance
> > states we should:
> >
> > 	1. set userWithAuth,
> > 	2. clear adminWithPolicy
> > 	3. set noDA, and
> > 	4. clear the authorization policy.
> >
> > There's no mention of alterations to the unique field.
> >
> > Let's also create the key in the null hierarchy:
> >
> >    perl -e 'print "\0"x64' | tpm2_createprimary -C n -o ./null.pub -G ecc -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda|restricted|decrypt' -u -
> >    tpm2_loadexternal -c n -u ./null.pub
> >
> > The name does not match the kernel's name for the null key.
>
> Null key is not provisioned, what is the motivation here?
>
> Not saying no, just asking for details...
>
> There's couple of things that lack in this patch set ATM:
>
> 1. Neither kselftest additions nor not even proper testing
>    instructions. Why 21 patches and zero tests? How one should
>    decide when the work is "complete"?
> 2. I still don't know what version of QEMU I should patch to
>    test corner cases from an URL, which I cannot recall :-)
>    Highlights the first issue.
>
> So for the time being the patch set is NAK just because we lack
> clear definition of done here. I revisit it only when I know how
> to test it.

Your feedback was great because it highlights the issue that the
excepted behaviour is non-existent at this point.

BR, Jarkko
Gabríel Arthúr Pétursson March 31, 2024, 4:52 p.m. UTC | #5
On Sun, 2024-03-31 at 19:00 +0300, Jarkko Sakkinen wrote:
> Null key is not provisioned, what is the motivation here?

The terms here, to create a key and to provision a key, are almost
interchangeable. The latter merely implies the additional step of
saving the key to NVRAM and making it available through a persistent
handle.

The template, aside from defining what kind of key we want to create,
is fed into the TPM's KDF used to generate the key. Different template,
different key.

Userspace will want to recreate the same key the kernel has, thus must
use the same template. Which template shall be used then?

+	/*
+	 * 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
+	 */

The TPM specifications have a standardized set of templates for the
Endorsement Keys, and a recommendation on a template to
create/provision the shared SRK.

Why not use that one then, above something else? We are required to use
a template for key generation after all, might as well pick the one
most fitting from the standards.

That's at least my understanding of the author's motivation.

> So for the time being the patch set is NAK just because we lack
> clear definition of done here. I revisit it only when I know how
> to test it.

I want to note that I'm not affiliated with the patches' author. I'm
merely an outside observer who has taken interest in the proposed
changes. Wanted to share my thoughts.
Jarkko Sakkinen April 1, 2024, 12:57 p.m. UTC | #6
On Sun Mar 31, 2024 at 7:52 PM EEST, Gabríel Arthúr Pétursson wrote:
> On Sun, 2024-03-31 at 19:00 +0300, Jarkko Sakkinen wrote:
> > Null key is not provisioned, what is the motivation here?
>
> The terms here, to create a key and to provision a key, are almost
> interchangeable. The latter merely implies the additional step of
> saving the key to NVRAM and making it available through a persistent
> handle.
>
> The template, aside from defining what kind of key we want to create,
> is fed into the TPM's KDF used to generate the key. Different template,
> different key.
>
> Userspace will want to recreate the same key the kernel has, thus must
> use the same template. Which template shall be used then?

I don't disagree with you, nor did when sending the first response :-)

Downstream seems to break that guide tho. E.g. Ubuntu uses 0x80100001
NV index for storage key unsealing the key slot with PCR values. I did
some time ago a quick VM install of Ubuntu just to see how it uses TPM2.

I got:

$ sudo tpm2_getcap handles-persistent
- 0x81000001
- 0x81010001

0x81010001 is according to provisioning guide EK handle but Ubuntu uses
it for completely different purpose.

What I would like to understand when it comes to the provisioning guide
is how important it is in reality? I have no information at this point
is e.g. systemd-boot going to follow it but would like to know.


>
> +	/*
> +	 * 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
> +	 */
>
> The TPM specifications have a standardized set of templates for the
> Endorsement Keys, and a recommendation on a template to
> create/provision the shared SRK.
>
> Why not use that one then, above something else? We are required to use
> a template for key generation after all, might as well pick the one
> most fitting from the standards.
>
> That's at least my understanding of the author's motivation.

Yeah, not necessarily disagree with this or I'm somewhat aligned to this
view. Ubuntu's architecture confuses me so would like to understand the
magnitude of the provisioning guide.

>
> > So for the time being the patch set is NAK just because we lack
> > clear definition of done here. I revisit it only when I know how
> > to test it.
>
> I want to note that I'm not affiliated with the patches' author. I'm
> merely an outside observer who has taken interest in the proposed
> changes. Wanted to share my thoughts.

BR, Jarkko
Jarkko Sakkinen April 1, 2024, 1:04 p.m. UTC | #7
On Mon Apr 1, 2024 at 3:57 PM EEST, Jarkko Sakkinen wrote:
> > > So for the time being the patch set is NAK just because we lack
> > > clear definition of done here. I revisit it only when I know how
> > > to test it.
> >
> > I want to note that I'm not affiliated with the patches' author. I'm
> > merely an outside observer who has taken interest in the proposed
> > changes. Wanted to share my thoughts.

I.e. thanks a lot for the feedback. It makes sense to I'm just confused
overally of the provisioning topic as one big player (Canonical) in the
cloud seems to break that guide, unless I missed something.

It would interesting to get some feedback on this from Ubuntu developers
but I don't know who has worked on TPM2 boot feature on that side.

BR, Jarkko
James Bottomley April 1, 2024, 2:19 p.m. UTC | #8
On Sat, 2024-03-30 at 18:48 +0000, Gabríel Arthúr Pétursson wrote:
> On Tue, 2024-02-13 at 12:13 -0500, James Bottomley wrote:
> > +       /* unique: key specific; for ECC it is two points */
> > +       tpm_buf_append_u16(&template, 0);
> > +       tpm_buf_append_u16(&template, 0);
> 
> Shouldn't the points be 32 bytes each in size, filled with zeros?
> 
> The TCP TPM 2.0 Provisioning Guidance defines the SRK Template as a
> diff on top of Template L-2 (for ECC keys) as defined in the EK
> Credential Profile 2.0 document.
> 
> Template L-2 calls for the X and Y points to be of 32 bytes each,
> filled with zeros. The Provisioning Guidance does not call for zero-
> sized points.
> 
> For example, let's create an ECC Endorsement Key using the standard
> template then print its name:
> 
>    tpm2_createek -G ecc -c /dev/null -u ./ek.pub
>    tpm2_loadexternal -c n -u ./ek.pub
> 
> Equivalently using tpm2_createprimary:
> 
>    perl -e 'print "\0"x64' | tpm2_createprimary -C e -o ./ek.pub -G
> ecc -a
> 'fixedtpm|fixedparent|sensitivedataorigin|adminwithpolicy|restricted|
> decrypt' -L
> 837197674484b3f81a90cc8d46a5d724fd52d76e06520b64f2a1da1b331469aa -u -
>    tpm2_loadexternal -c n -u ./ek.pub
> 
> You'll find that the key's public modulus matches that of the EK
> Certificate imprinted by the manufacturer, indicating we got the
> template correct.
> 
> To generate a standard SRK key, the TCG TPM2 Provisioning Guidance
> states we should:
> 
>         1. set userWithAuth,
>         2. clear adminWithPolicy
>         3. set noDA, and
>         4. clear the authorization policy.
> 
> There's no mention of alterations to the unique field.
> 
> Let's also create the key in the null hierarchy:
> 
>    perl -e 'print "\0"x64' | tpm2_createprimary -C n -o ./null.pub -G
> ecc -a
> 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda|restricte
> d|decrypt' -u -
>    tpm2_loadexternal -c n -u ./null.pub
> 
> The name does not match the kernel's name for the null key.

So this seems to be a fairly global problem.  If you look at
openssl_tpm2_engine, openconnect and gnutls, they all formulate the ECC
primary template this way (zero size points).  They all look to have
inherited it from the IBM TSS:

https://github.com/kgoldman/ibmtss/blob/master/utils/objecttemplates.c#L272-L275

Ironically, even the Intel TSS based openssl engine and the provider
seem to define it this way as well, and they definitely didn't get it
from the IBM TSS:

https://github.com/tpm2-software/tpm2-tss-engine/blob/master/src/tpm2-tss-engine-common.h#L149-L152

The problem is if we change it now, it would break all the current keys
with primary handles (which is pretty much all of them thanks to almost
no-one following the provisioning guidance about persistent handles).

So I'm not really sure how to solve this.  At the moment the kernel
doesn't use permanent handles for keys, but it should and it should
follow what all of the industry is doing for interoperability (i.e.
zero size points), which means the NULL primary should also follow it.

James
James Bottomley April 1, 2024, 4:55 p.m. UTC | #9
On Mon, 2024-04-01 at 10:19 -0400, James Bottomley wrote:
> So I'm not really sure how to solve this.  At the moment the kernel
> doesn't use permanent handles for keys, but it should and it should
> follow what all of the industry is doing for interoperability (i.e.
> zero size points), which means the NULL primary should also follow
> it.

Actually, it turns out this is already solved by the TCG.  The template
we're using is the correct one (zero size points).  Apparently they
regretted their earlier decision to zero fill and issued this guidance:

   2.2.1.2.2 EK Template
   
   An EK Template is stored in an NV Index as a TPMT_PUBLIC structure
   marshaled as described in the TPM 2.0 Library Specification [1]. The
   default EK Templates are defined in annex B. The EK Template NV Index
   MUST be Populated if non-default values are used. It SHOULD be Absent
   if default values are used.
   
   The EK Template unique field buffer size(s) SHOULD be zero.
   
But since they can't revoke the previous guidance, we now have two
templates defined: the L one which has the old n bytes of zeros and the
new (and recommended) H one which has zero size unique field.

https://trustedcomputinggroup.org/resource/http-trustedcomputinggroup-org-wp-content-uploads-tcg-ek-credential-profile-v-2-5-r2_published-pdf/

So in other words, we're doing the later correct thing and there's no
problem.  I'll update the ASN.1 draft

https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html

to state that we MUST use the H template to remove any ambiguity

James
Jarkko Sakkinen April 1, 2024, 8:54 p.m. UTC | #10
On Mon Apr 1, 2024 at 7:55 PM EEST, James Bottomley wrote:
> On Mon, 2024-04-01 at 10:19 -0400, James Bottomley wrote:
> > So I'm not really sure how to solve this.  At the moment the kernel
> > doesn't use permanent handles for keys, but it should and it should
> > follow what all of the industry is doing for interoperability (i.e.
> > zero size points), which means the NULL primary should also follow
> > it.
>
> Actually, it turns out this is already solved by the TCG.  The template
> we're using is the correct one (zero size points).  Apparently they
> regretted their earlier decision to zero fill and issued this guidance:
>
>    2.2.1.2.2 EK Template
>    
>    An EK Template is stored in an NV Index as a TPMT_PUBLIC structure
>    marshaled as described in the TPM 2.0 Library Specification [1]. The
>    default EK Templates are defined in annex B. The EK Template NV Index
>    MUST be Populated if non-default values are used. It SHOULD be Absent
>    if default values are used.
>    
>    The EK Template unique field buffer size(s) SHOULD be zero.
>    
> But since they can't revoke the previous guidance, we now have two
> templates defined: the L one which has the old n bytes of zeros and the
> new (and recommended) H one which has zero size unique field.
>
> https://trustedcomputinggroup.org/resource/http-trustedcomputinggroup-org-wp-content-uploads-tcg-ek-credential-profile-v-2-5-r2_published-pdf/
>
> So in other words, we're doing the later correct thing and there's no
> problem.  I'll update the ASN.1 draft
>
> https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html

First time I'm seeing this document or URL.

>
> to state that we MUST use the H template to remove any ambiguity
>
> James

BR, Jarkko
Jarkko Sakkinen April 1, 2024, 8:59 p.m. UTC | #11
On Mon Apr 1, 2024 at 11:54 PM EEST, Jarkko Sakkinen wrote:
> On Mon Apr 1, 2024 at 7:55 PM EEST, James Bottomley wrote:
> > On Mon, 2024-04-01 at 10:19 -0400, James Bottomley wrote:
> > > So I'm not really sure how to solve this.  At the moment the kernel
> > > doesn't use permanent handles for keys, but it should and it should
> > > follow what all of the industry is doing for interoperability (i.e.
> > > zero size points), which means the NULL primary should also follow
> > > it.
> >
> > Actually, it turns out this is already solved by the TCG.  The template
> > we're using is the correct one (zero size points).  Apparently they
> > regretted their earlier decision to zero fill and issued this guidance:
> >
> >    2.2.1.2.2 EK Template
> >    
> >    An EK Template is stored in an NV Index as a TPMT_PUBLIC structure
> >    marshaled as described in the TPM 2.0 Library Specification [1]. The
> >    default EK Templates are defined in annex B. The EK Template NV Index
> >    MUST be Populated if non-default values are used. It SHOULD be Absent
> >    if default values are used.
> >    
> >    The EK Template unique field buffer size(s) SHOULD be zero.
> >    
> > But since they can't revoke the previous guidance, we now have two
> > templates defined: the L one which has the old n bytes of zeros and the
> > new (and recommended) H one which has zero size unique field.
> >
> > https://trustedcomputinggroup.org/resource/http-trustedcomputinggroup-org-wp-content-uploads-tcg-ek-credential-profile-v-2-5-r2_published-pdf/
> >
> > So in other words, we're doing the later correct thing and there's no
> > problem.  I'll update the ASN.1 draft
> >
> > https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html
>
> First time I'm seeing this document or URL.

Anyway, only thing that we align with is the latest in kernel
documentation, outside URL's are ignored. I.e. the legit ref
is trusted-encrypted.rst.

BR, Jarkko
Ken Goldman April 2, 2024, 7:30 p.m. UTC | #12
On 3/31/2024 12:52 PM, Gabríel Arthúr Pétursson wrote:
> The TPM specifications have a standardized set of templates for the
> Endorsement Keys, and a recommendation on a template to
> create/provision the shared SRK.

The original TCG guidance document for an SRK used arrays of zeros for 
the unique field.

This was either a holdover from TPM 1.2, where arrays were 20 bytes,
or a misinterpretation of text that said: NULL.

The reality is that it's a TPM2B, and the size(s) can be zero.

The answer for the EK is different. It has to use the TCG
standard.  The EK is not a 'guidance document'.
Jarkko Sakkinen April 3, 2024, 3:43 p.m. UTC | #13
On Tue Apr 2, 2024 at 10:30 PM EEST, Ken Goldman wrote:
> On 3/31/2024 12:52 PM, Gabríel Arthúr Pétursson wrote:
> > The TPM specifications have a standardized set of templates for the
> > Endorsement Keys, and a recommendation on a template to
> > create/provision the shared SRK.
>
> The original TCG guidance document for an SRK used arrays of zeros for 
> the unique field.
>
> This was either a holdover from TPM 1.2, where arrays were 20 bytes,
> or a misinterpretation of text that said: NULL.
>
> The reality is that it's a TPM2B, and the size(s) can be zero.
>
> The answer for the EK is different. It has to use the TCG
> standard.  The EK is not a 'guidance document'.

Does anyone follow TCG's provisioning guide? I.e. is it implemented
"in the industry"? I'm just trying to understand the real value of
this document.

BR, Jarkko
diff mbox series

Patch

diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index 927088b2c3d3..e3c39a83171b 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 encrypted and HMACd 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..9fc263ee02c2
--- /dev/null
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -0,0 +1,276 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2018 James.Bottomley@HansenPartnership.com
+ *
+ */
+
+#include "tpm.h"
+
+#include <asm/unaligned.h>
+
+#include <crypto/aes.h>
+
+/* if you change to AES256, you only need change this */
+#define AES_KEYBYTES	AES_KEYSIZE_128
+
+#define AES_KEYBITS	(AES_KEYBYTES*8)
+
+static int tpm2_parse_create_primary(struct tpm_chip *chip, struct tpm_buf *buf,
+				     u32 *nullkey)
+{
+	struct tpm_header *head = (struct tpm_header *)buf->data;
+	off_t offset_r = TPM_HEADER_SIZE, offset_t;
+	u16 len = TPM_HEADER_SIZE;
+	u32 tot_len = be32_to_cpu(head->length);
+	u32 val, parm_len;
+
+	*nullkey = tpm_buf_read_u32(buf, &offset_r);
+	parm_len = tpm_buf_read_u32(buf, &offset_r);
+	/*
+	 * parm_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
+	 */
+	parm_len += TPM_HEADER_SIZE;
+
+	if (parm_len + 8 > tot_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_NO_DA |
+		    TPM2_OA_FIXED_TPM |
+		    TPM2_OA_FIXED_PARENT |
+		    TPM2_OA_SENSITIVE_DATA_ORIGIN |
+		    TPM2_OA_USER_WITH_AUTH |
+		    TPM2_OA_DECRYPT |
+		    TPM2_OA_RESTRICTED))
+		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_KEYBITS)
+		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;
+	/* x point */
+	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 > parm_len)
+		return -EINVAL;
+	/* creation data (skip) */
+	len = tpm_buf_read_u16(buf, &offset_r);
+	offset_r += len;
+	if (offset_r > parm_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 > parm_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 > parm_len)
+		return -EINVAL;
+	/* hierarchy (must be NULL) */
+	val = tpm_buf_read_u32(buf, &offset_r);
+	if (val != TPM2_RH_NULL || offset_r > parm_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 > parm_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 != parm_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;
+}
+
+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
+	 */
+
+	/* 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_NO_DA |
+			   TPM2_OA_FIXED_TPM |
+			   TPM2_OA_FIXED_PARENT |
+			   TPM2_OA_SENSITIVE_DATA_ORIGIN |
+			   TPM2_OA_USER_WITH_AUTH |
+			   TPM2_OA_DECRYPT |
+			   TPM2_OA_RESTRICTED);
+	/* 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, 128);
+	/* 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 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);
+
+	tpm_buf_destroy(&buf);
+
+	return rc;
+}
+
+static int tpm2_create_null_primary(struct tpm_chip *chip)
+{
+	u32 nullkey;
+	int rc;
+
+	rc = tpm2_create_primary(chip, TPM2_RH_NULL, &nullkey);
+
+	if (rc == TPM2_RC_SUCCESS) {
+		unsigned int offset = 0; /* dummy offset for null key context */
+
+		rc = tpm2_save_context(chip, nullkey, chip->null_key_context,
+				       sizeof(chip->null_key_context), &offset);
+		tpm2_flush_context(chip, nullkey);
+	}
+
+	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;
+}
+EXPORT_SYMBOL(tpm2_sessions_init);
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 6be263509e81..3060ab794afb 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -35,12 +35,15 @@  struct trusted_key_options;
 enum tpm_algorithms {
 	TPM_ALG_ERROR		= 0x0000,
 	TPM_ALG_SHA1		= 0x0004,
+	TPM_ALG_AES		= 0x0006,
 	TPM_ALG_KEYEDHASH	= 0x0008,
 	TPM_ALG_SHA256		= 0x000B,
 	TPM_ALG_SHA384		= 0x000C,
 	TPM_ALG_SHA512		= 0x000D,
 	TPM_ALG_NULL		= 0x0010,
 	TPM_ALG_SM3_256		= 0x0012,
+	TPM_ALG_ECC		= 0x0023,
+	TPM_ALG_CFB		= 0x0043,
 };
 
 /*
@@ -49,6 +52,11 @@  enum tpm_algorithms {
  */
 #define TPM_MAX_HASHES	5
 
+enum tpm2_curves {
+	TPM2_ECC_NONE		= 0x0000,
+	TPM2_ECC_NIST_P256	= 0x0003,
+};
+
 struct tpm_digest {
 	u16 alg_id;
 	u8 digest[TPM_MAX_DIGEST_SIZE];
@@ -116,6 +124,20 @@  struct tpm_chip_seqops {
 	const struct seq_operations *seqops;
 };
 
+/* fixed define for the curve we use which is NIST_P256 */
+#define EC_PT_SZ	32
+
+/*
+ * fixed define for the size of a name.  This is actually HASHALG size
+ * plus 2, so 32 for SHA256
+ */
+#define TPM2_NAME_SIZE	34
+
+/*
+ * The maximum size for an object context
+ */
+#define TPM2_MAX_CONTEXT_SIZE 4096
+
 struct tpm_chip {
 	struct device dev;
 	struct device devs;
@@ -170,6 +192,14 @@  struct tpm_chip {
 
 	/* active locality */
 	int locality;
+
+#ifdef CONFIG_TCG_TPM2_HMAC
+	/* details for communication security via sessions */
+	u8 null_key_context[TPM2_MAX_CONTEXT_SIZE]; /* context for NULL seed */
+	u8 null_key_name[TPM2_NAME_SIZE];	 /* name of NULL seed */
+	u8 null_ec_key_x[EC_PT_SZ];
+	u8 null_ec_key_y[EC_PT_SZ];
+#endif
 };
 
 #define TPM_HEADER_SIZE		10
@@ -194,6 +224,7 @@  enum tpm2_timeouts {
 enum tpm2_structures {
 	TPM2_ST_NO_SESSIONS	= 0x8001,
 	TPM2_ST_SESSIONS	= 0x8002,
+	TPM2_ST_CREATION	= 0x8021,
 };
 
 /* Indicates from what layer of the software stack the error comes from */
@@ -243,6 +274,7 @@  enum tpm2_command_codes {
 };
 
 enum tpm2_permanent_handles {
+	TPM2_RH_NULL		= 0x40000007,
 	TPM2_RS_PW		= 0x40000009,
 };
 
@@ -318,7 +350,11 @@  struct tpm_buf {
 enum tpm2_object_attributes {
 	TPM2_OA_FIXED_TPM		= BIT(1),
 	TPM2_OA_FIXED_PARENT		= BIT(4),
+	TPM2_OA_SENSITIVE_DATA_ORIGIN	= BIT(5),
 	TPM2_OA_USER_WITH_AUTH		= BIT(6),
+	TPM2_OA_NO_DA			= BIT(10),
+	TPM2_OA_RESTRICTED		= BIT(16),
+	TPM2_OA_DECRYPT			= BIT(17),
 };
 
 enum tpm2_session_attributes {
@@ -373,6 +409,15 @@  extern int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
 extern int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max);
 extern struct tpm_chip *tpm_default_chip(void);
 void tpm2_flush_context(struct tpm_chip *chip, u32 handle);
+static inline void tpm_buf_append_empty_auth(struct tpm_buf *buf, u32 handle)
+{
+	/* simple authorization for empty auth */
+	tpm_buf_append_u32(buf, 9);		/* total length of auth */
+	tpm_buf_append_u32(buf, handle);
+	tpm_buf_append_u16(buf, 0);		/* nonce len */
+	tpm_buf_append_u8(buf, 0);		/* attributes */
+	tpm_buf_append_u16(buf, 0);		/* hmac len */
+}
 #else
 static inline int tpm_is_tpm2(struct tpm_chip *chip)
 {
@@ -399,5 +444,9 @@  static inline struct tpm_chip *tpm_default_chip(void)
 {
 	return NULL;
 }
+
+static inline void tpm_buf_append_empty_auth(struct tpm_buf *buf, u32 handle)
+{
+}
 #endif
 #endif