diff mbox series

[v7,13/21] tpm: Add HMAC session start and end functions

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

Commit Message

James Bottomley Feb. 13, 2024, 5:13 p.m. UTC
Add session  based HMAC  authentication plus parameter  decryption and
response encryption  using AES. The  basic design is to  segregate all
the nasty crypto, hash and hmac code into tpm2-sessions.c and export a
usable API.  The API first of all starts off by gaining a session with
tpm2_start_auth_session() which  initiates a session with  the TPM and
allocates  an  opaque  tpm2_auth   structure  to  handle  the  session
parameters.  The  design is that  session use will be  single threaded
from start to finish under the ops lock, so the tpm2_auth structure is
stored in struct tpm2_chip to simpify the externally visible API.

The session can be ended with tpm2_end_auth_session() which is
designed only to be used in error legs.  Ordinarily the further
session API (future patches) will end or continue the session
appropriately without having to call this.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> # crypto API parts

---

v6: split into new patch, update config variable
v7: add tpm2_ prefix and memzero_explicit
---
 drivers/char/tpm/Kconfig         |   3 +
 drivers/char/tpm/tpm-buf.c       |   1 +
 drivers/char/tpm/tpm-chip.c      |   3 +
 drivers/char/tpm/tpm2-sessions.c | 385 +++++++++++++++++++++++++++++++
 include/linux/tpm.h              |  34 +++
 5 files changed, 426 insertions(+)

Comments

Jarkko Sakkinen Feb. 23, 2024, 5:02 p.m. UTC | #1
On Tue Feb 13, 2024 at 7:13 PM EET, James Bottomley wrote:
> Add session  based HMAC  authentication plus parameter  decryption and
> response encryption  using AES. The  basic design is to  segregate all
> the nasty crypto, hash and hmac code into tpm2-sessions.c and export a
> usable API.  The API first of all starts off by gaining a session with
> tpm2_start_auth_session() which  initiates a session with  the TPM and
> allocates  an  opaque  tpm2_auth   structure  to  handle  the  session
> parameters.  The  design is that  session use will be  single threaded
> from start to finish under the ops lock, so the tpm2_auth structure is
> stored in struct tpm2_chip to simpify the externally visible API.
>
> The session can be ended with tpm2_end_auth_session() which is
> designed only to be used in error legs.  Ordinarily the further
> session API (future patches) will end or continue the session
> appropriately without having to call this.
>
> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
> Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> # crypto API parts
>
> ---
>
> v6: split into new patch, update config variable
> v7: add tpm2_ prefix and memzero_explicit
> ---
>  drivers/char/tpm/Kconfig         |   3 +
>  drivers/char/tpm/tpm-buf.c       |   1 +
>  drivers/char/tpm/tpm-chip.c      |   3 +
>  drivers/char/tpm/tpm2-sessions.c | 385 +++++++++++++++++++++++++++++++
>  include/linux/tpm.h              |  34 +++
>  5 files changed, 426 insertions(+)
>
> diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
> index e3c39a83171b..086cb8588493 100644
> --- a/drivers/char/tpm/Kconfig
> +++ b/drivers/char/tpm/Kconfig
> @@ -30,6 +30,9 @@ if TCG_TPM
>  config TCG_TPM2_HMAC
>  	bool "Use encrypted and HMACd transactions on the TPM bus"
>  	default y
> +	select CRYPTO_ECDH
> +	select CRYPTO_LIB_AESCFB
> +	select CRYPTO_LIB_SHA256
>  	help
>            Setting this causes us to deploy a scheme which uses request
>  	  and response HMACs in addition to encryption for
> diff --git a/drivers/char/tpm/tpm-buf.c b/drivers/char/tpm/tpm-buf.c
> index bb81180495d1..274130398569 100644
> --- a/drivers/char/tpm/tpm-buf.c
> +++ b/drivers/char/tpm/tpm-buf.c
> @@ -44,6 +44,7 @@ void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal)
>  	head->tag = cpu_to_be16(tag);
>  	head->length = cpu_to_be32(sizeof(*head));
>  	head->ordinal = cpu_to_be32(ordinal);
> +	buf->handles = 0;
>  }
>  EXPORT_SYMBOL_GPL(tpm_buf_reset);
>  
> diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
> index 42b1062e33cd..d93937326b2e 100644
> --- a/drivers/char/tpm/tpm-chip.c
> +++ b/drivers/char/tpm/tpm-chip.c
> @@ -275,6 +275,9 @@ static void tpm_dev_release(struct device *dev)
>  	kfree(chip->work_space.context_buf);
>  	kfree(chip->work_space.session_buf);
>  	kfree(chip->allocated_banks);
> +#ifdef CONFIG_TCG_TPM2_HMAC
> +	kfree(chip->auth);
> +#endif
>  	kfree(chip);
>  }
>  
> diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
> index 9fc263ee02c2..c8792a6b8bd4 100644
> --- a/drivers/char/tpm/tpm2-sessions.c
> +++ b/drivers/char/tpm/tpm2-sessions.c
> @@ -3,18 +3,399 @@
>  /*
>   * Copyright (C) 2018 James.Bottomley@HansenPartnership.com
>   *
> + * Cryptographic helper routines for handling TPM2 sessions for
> + * authorization HMAC and request response encryption.
> + *
> + * The idea is to ensure that every TPM command is HMAC protected by a
> + * session, meaning in-flight tampering would be detected and in
> + * addition all sensitive inputs and responses should be encrypted.
> + *
> + * The basic way this works is to use a TPM feature called salted
> + * sessions where a random secret used in session construction is
> + * encrypted to the public part of a known TPM key.  The problem is we
> + * have no known keys, so initially a primary Elliptic Curve key is
> + * derived from the NULL seed (we use EC because most TPMs generate
> + * these keys much faster than RSA ones).  The curve used is NIST_P256
> + * because that's now mandated to be present in 'TCG TPM v2.0
> + * Provisioning Guidance'
> + *
> + * Threat problems: the initial TPM2_CreatePrimary is not (and cannot
> + * be) session protected, so a clever Man in the Middle could return a
> + * public key they control to this command and from there intercept
> + * and decode all subsequent session based transactions.  The kernel
> + * cannot mitigate this threat but, after boot, userspace can get
> + * proof this has not happened by asking the TPM to certify the NULL
> + * key.  This certification would chain back to the TPM Endorsement
> + * Certificate and prove the NULL seed primary had not been tampered
> + * with and thus all sessions must have been cryptographically secure.
> + * To assist with this, the initial NULL seed public key name is made
> + * available in a sysfs file.
> + *
> + * Use of these functions:
> + *
> + * The design is all the crypto, hash and hmac gunk is confined in this
> + * file and never needs to be seen even by the kernel internal user.  To
> + * the user there's an init function tpm2_sessions_init() that needs to
> + * be called once per TPM which generates the NULL seed primary key.
> + *
> + * These are the usage functions:
> + *
> + * tpm2_start_auth_session() which allocates the opaque auth structure
> + *	and gets a session from the TPM.  This must be called before
> + *	any of the following functions.  The session is protected by a
> + *	session_key which is derived from a random salt value
> + *	encrypted to the NULL seed.
> + * tpm2_end_auth_session() kills the session and frees the resources.
> + *	Under normal operation this function is done by
> + *	tpm_buf_check_hmac_response(), so this is only to be used on
> + *	error legs where the latter is not executed.
>   */
>  
>  #include "tpm.h"
>  
> +#include <linux/random.h>
> +#include <linux/scatterlist.h>
> +
>  #include <asm/unaligned.h>
>  
>  #include <crypto/aes.h>
> +#include <crypto/kpp.h>
> +#include <crypto/ecdh.h>
> +#include <crypto/hash.h>
> +#include <crypto/hmac.h>
>  
>  /* if you change to AES256, you only need change this */
>  #define AES_KEYBYTES	AES_KEYSIZE_128
>  
>  #define AES_KEYBITS	(AES_KEYBYTES*8)
> +#define AUTH_MAX_NAMES	3
> +
> +/*
> + * This is the structure that carries all the auth information (like
> + * session handle, nonces, session key and auth) from use to use it is
> + * designed to be opaque to anything outside.
> + */
> +struct tpm2_auth {
> +	u32 handle;
> +	/*
> +	 * This has two meanings: before tpm_buf_fill_hmac_session()
> +	 * it marks the offset in the buffer of the start of the
> +	 * sessions (i.e. after all the handles).  Once the buffer has
> +	 * been filled it markes the session number of our auth
> +	 * session so we can find it again in the response buffer.
> +	 *
> +	 * The two cases are distinguished because the first offset
> +	 * must always be greater than TPM_HEADER_SIZE and the second
> +	 * must be less than or equal to 5.
> +	 */
> +	u32 session;
> +	/*
> +	 * the size here is variable and set by the size of our_nonce
> +	 * which must be between 16 and the name hash length. we set
> +	 * the maximum sha256 size for the greatest protection
> +	 */
> +	u8 our_nonce[SHA256_DIGEST_SIZE];
> +	u8 tpm_nonce[SHA256_DIGEST_SIZE];
> +	/*
> +	 * the salt is only used across the session command/response
           ~
	   T
 

> +	 * after that it can be used as a scratch area
                                                     ~
						     a.

> +	 */
> +	union {
> +		u8 salt[EC_PT_SZ];
> +		/* scratch for key + IV */
> +		u8 scratch[AES_KEYBYTES + AES_BLOCK_SIkkkZE];
> +	};
> +	u8 session_key[SHA256_DIGEST_SIZE];
> +};
> +
> +/*
> + * It turns out the crypto hmac(sha256) is hard for us to consume
> + * because it assumes a fixed key and the TPM seems to change the key
> + * on every operation, so we weld the hmac init and final functions in
> + * here to give it the same usage characteristics as a regular hash
> + */
> +static void hmac_init(struct sha256_state *sctx, u8 *key, int keylen)

tpm_*

there's too many hmac sites in the kernel so it would be nice to git
grep these easily in the future.

> +{
> +	u8 pad[SHA256_BLOCK_SIZE];
> +	int i;
> +
> +	sha256_init(sctx);
> +	for (i = 0; i < sizeof(pad); i++) {
> +		if (i < keylen)
> +			pad[i] = key[i];
> +		else
> +			pad[i] = 0;
> +		pad[i] ^= HMAC_IPAD_VALUE;
> +	}
> +	sha256_update(sctx, pad, sizeof(pad));
> +}
> +
> +static void hmac_final(struct sha256_state *sctx, u8 *key, int keylen, u8 *out)

tpm_*

> +{
> +	u8 pad[SHA256_BLOCK_SIZE];
> +	int i;
> +
> +	for (i = 0; i < sizeof(pad); i++) {
> +		if (i < keylen)
> +			pad[i] = key[i];
> +		else
> +			pad[i] = 0;
> +		pad[i] ^= HMAC_OPAD_VALUE;
> +	}
> +
> +	/* collect the final hash;  use out as temporary storage */
> +	sha256_final(sctx, out);
> +
> +	sha256_init(sctx);
> +	sha256_update(sctx, pad, sizeof(pad));
> +	sha256_update(sctx, out, SHA256_DIGEST_SIZE);
> +	sha256_final(sctx, out);
> +}
> +
> +/*
> + * assume hash sha256 and nonces u, v of size SHA256_DIGEST_SIZE but
      ~
      A

> + * otherwise standard KDFa.  Note output is in bytes not bits.
> + */
> +static void KDFa(u8 *key, int keylen, const char *label, u8 *u,
                                 ~~~~~~
				 key_len


tpm_* prefix

> +		 u8 *v, int bytes, u8 *out)

Why bytes is a signed type? Does it need to be multiple of
something?

> +{
> +	u32 counter;
> +	const __be32 bits = cpu_to_be32(bytes * 8);
> +
> +	for (counter = 1; bytes > 0; bytes -= SHA256_DIGEST_SIZE, counter++,
> +		     out += SHA256_DIGEST_SIZE) {

Let's open code this:

	counter = 1;
	while (bytes > 0) {
		/* ... */

		bytes -= SHA256_DIGEST_SIZE;
		out += SHA256_DIGEST_SIZE;
		counter++;
	}

I'd figure that this requires one index, not three. Others
have linear relation? The for-loop construc is too cryptic.

> +		struct sha256_state sctx;
> +		__be32 c = cpu_to_be32(counter);
> +
> +		hmac_init(&sctx, key, keylen);
> +		sha256_update(&sctx, (u8 *)&c, sizeof(c));
> +		sha256_update(&sctx, label, strlen(label)+1);
> +		sha256_update(&sctx, u, SHA256_DIGEST_SIZE);
> +		sha256_update(&sctx, v, SHA256_DIGEST_SIZE);
> +		sha256_update(&sctx, (u8 *)&bits, sizeof(bits));
> +		hmac_final(&sctx, key, keylen, out);
> +	}
> +}
> +
> +/*
> + * Somewhat of a bastardization of the real KDFe.  We're assuming
> + * we're working with known point sizes for the input parameters and
> + * the hash algorithm is fixed at sha256.  Because we know that the
> + * point size is 32 bytes like the hash size, there's no need to loop
> + * in this KDF.
> + */
> +static void KDFe(u8 z[EC_PT_SZ], const char *str, u8 *pt_u, u8 *pt_v,
> +		 u8 *keyout)
 		     ~~~~~~~
		     the previous function had 'out'. why the name
		     change?

tpm_*

> +{
> +	struct sha256_state sctx;
> +	/*
> +	 * this should be an iterative counter, but because we know
> +	 *  we're only taking 32 bytes for the point using a sha256
> +	 *  hash which is also 32 bytes, there's only one loop
> +	 */
> +	__be32 c = cpu_to_be32(1);
> +
> +	sha256_init(&sctx);
> +	/* counter (BE) */
> +	sha256_update(&sctx, (u8 *)&c, sizeof(c));
> +	/* secret value */
> +	sha256_update(&sctx, z, EC_PT_SZ);
> +	/* string including trailing zero */
> +	sha256_update(&sctx, str, strlen(str)+1);
> +	sha256_update(&sctx, pt_u, EC_PT_SZ);
> +	sha256_update(&sctx, pt_v, EC_PT_SZ);
> +	sha256_final(&sctx, keyout);
> +}
> +
> +static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip)
> +{
> +	struct crypto_kpp *kpp;
> +	struct kpp_request *req;
> +	struct scatterlist s[2], d[1];
> +	struct ecdh p = {0};
> +	u8 encoded_key[EC_PT_SZ], *x, *y;
> +	unsigned int buf_len;

i'd prefer the reverse christmas tree order

> +
> +	/* secret is two sized points */
> +	tpm_buf_append_u16(buf, (EC_PT_SZ + 2)*2);
> +	/*
> +	 * we cheat here and append uninitialized data to form
> +	 * the points.  All we care about is getting the two
> +	 * co-ordinate pointers, which will be used to overwrite
> +	 * the uninitialized data
> +	 */
> +	tpm_buf_append_u16(buf, EC_PT_SZ);
> +	x = &buf->data[tpm_buf_length(buf)];
> +	tpm_buf_append(buf, encoded_key, EC_PT_SZ);
> +	tpm_buf_append_u16(buf, EC_PT_SZ);
> +	y = &buf->data[tpm_buf_length(buf)];
> +	tpm_buf_append(buf, encoded_key, EC_PT_SZ);
> +	sg_init_table(s, 2);
> +	sg_set_buf(&s[0], x, EC_PT_SZ);
> +	sg_set_buf(&s[1], y, EC_PT_SZ);
> +
> +	kpp = crypto_alloc_kpp("ecdh-nist-p256", CRYPTO_ALG_INTERNAL, 0);
> +	if (IS_ERR(kpp)) {
> +		dev_err(&chip->dev, "crypto ecdh allocation failed\n");
> +		return;
> +	}
> +
> +	buf_len = crypto_ecdh_key_len(&p);
> +	if (sizeof(encoded_key) < buf_len) {
> +		dev_err(&chip->dev, "salt buffer too small needs %d\n",
> +			buf_len);
> +		goto out;
> +	}
> +	crypto_ecdh_encode_key(encoded_key, buf_len, &p);
> +	/* this generates a random private key */
> +	crypto_kpp_set_secret(kpp, encoded_key, buf_len);
> +
> +	/* salt is now the public point of this private key */
> +	req = kpp_request_alloc(kpp, GFP_KERNEL);
> +	if (!req)
> +		goto out;
> +	kpp_request_set_input(req, NULL, 0);
> +	kpp_request_set_output(req, s, EC_PT_SZ*2);
> +	crypto_kpp_generate_public_key(req);
> +	/*
> +	 * we're not done: now we have to compute the shared secret
> +	 * which is our private key multiplied by the tpm_key public
> +	 * point, we actually only take the x point and discard the y
> +	 * point and feed it through KDFe to get the final secret salt
> +	 */
> +	sg_set_buf(&s[0], chip->null_ec_key_x, EC_PT_SZ);
> +	sg_set_buf(&s[1], chip->null_ec_key_y, EC_PT_SZ);
> +	kpp_request_set_input(req, s, EC_PT_SZ*2);
> +	sg_init_one(d, chip->auth->salt, EC_PT_SZ);
> +	kpp_request_set_output(req, d, EC_PT_SZ);
> +	crypto_kpp_compute_shared_secret(req);
> +	kpp_request_free(req);
> +
> +	/*
> +	 * pass the shared secret through KDFe for salt. Note salt
> +	 * area is used both for input shared secret and output salt.
> +	 * This works because KDFe fully consumes the secret before it
> +	 * writes the salt
> +	 */
> +	KDFe(chip->auth->salt, "SECRET", x, chip->null_ec_key_x,
> +	     chip->auth->salt);

empty line missing before the label

> + out:
> +	crypto_free_kpp(kpp);
> +}
> +/**
> + * tpm2_end_auth_session() - kill the allocated auth session
> + * @chip: the TPM chip structure
> + *
> + * ends the session started by tpm2_start_auth_session and frees all
> + * the resources.  Under normal conditions,
> + * tpm_buf_check_hmac_response() will correctly end the session if
> + * required, so this function is only for use in error legs that will
> + * bypass the normal invocation of tpm_buf_check_hmac_response().
> + */
> +void tpm2_end_auth_session(struct tpm_chip *chip)
> +{
> +	tpm2_flush_context(chip, chip->auth->handle);
> +	memzero_explicit(chip->auth, sizeof(*chip->auth));
> +}
> +EXPORT_SYMBOL(tpm2_end_auth_session);
> +
> +static int tpm2_parse_start_auth_session(struct tpm2_auth *auth,
> +					 struct tpm_buf *buf)
> +{
> +	struct tpm_header *head = (struct tpm_header *)buf->data;
> +	u32 tot_len = be32_to_cpu(head->length);
> +	off_t offset = TPM_HEADER_SIZE;
> +	u32 val;
> +
> +	/* we're starting after the header so adjust the length */
> +	tot_len -= TPM_HEADER_SIZE;
> +
> +	/* should have handle plus nonce */
> +	if (tot_len != 4 + 2 + sizeof(auth->tpm_nonce))
> +		return -EINVAL;
> +
> +	auth->handle = tpm_buf_read_u32(buf, &offset);
> +	val = tpm_buf_read_u16(buf, &offset);
> +	if (val != sizeof(auth->tpm_nonce))
> +		return -EINVAL;
> +	memcpy(auth->tpm_nonce, &buf->data[offset], sizeof(auth->tpm_nonce));
> +	/* now compute the session key from the nonces */
> +	KDFa(auth->salt, sizeof(auth->salt), "ATH", auth->tpm_nonce,
> +	     auth->our_nonce, sizeof(auth->session_key), auth->session_key);
> +
> +	return 0;
> +}
> +
> +/**
> + * tpm2_start_auth_session() - create a HMAC authentication session with the TPM
> + * @chip: the TPM chip structure to create the session with
> + *
> + * This function loads the NULL seed from its saved context and starts
> + * an authentication session on the null seed, fills in the
> + * @chip->auth structure to contain all the session details necessary
> + * for performing the HMAC, encrypt and decrypt operations and
> + * returns.  The NULL seed is flushed before this function returns.
> + *
> + * Return: zero on success or actual error encountered.
> + */
> +int tpm2_start_auth_session(struct tpm_chip *chip)
> +{
> +	struct tpm_buf buf;
> +	struct tpm2_auth *auth = chip->auth;
> +	int rc;
> +	unsigned int offset = 0; /* dummy offset for null seed context */

What does the comment is trying to say to me? Should be
in its own line.

> +	u32 nullkey;
> +
> +	rc = tpm2_load_context(chip, chip->null_key_context, &offset,
> +			       &nullkey);
> +	if (rc)
> +		goto out;
> +
> +	auth->session = TPM_HEADER_SIZE;
> +
> +	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_START_AUTH_SESS);
> +	if (rc)
> +		goto out;
> +
> +	/* salt key handle */
> +	tpm_buf_append_u32(&buf, nullkey);
> +	/* bind key handle */
> +	tpm_buf_append_u32(&buf, TPM2_RH_NULL);
> +	/* nonce caller */
> +	get_random_bytes(auth->our_nonce, sizeof(auth->our_nonce));
> +	tpm_buf_append_u16(&buf, sizeof(auth->our_nonce));
> +	tpm_buf_append(&buf, auth->our_nonce, sizeof(auth->our_nonce));
> +
> +	/* append encrypted salt and squirrel away unencrypted in auth */
> +	tpm_buf_append_salt(&buf, chip);
> +	/* session type (HMAC, audit or policy) */
> +	tpm_buf_append_u8(&buf, TPM2_SE_HMAC);
> +
> +	/* symmetric encryption parameters */
> +	/* symmetric algorithm */
> +	tpm_buf_append_u16(&buf, TPM_ALG_AES);
> +	/* bits for symmetric algorithm */
> +	tpm_buf_append_u16(&buf, AES_KEYBITS);
> +	/* symmetric algorithm mode (must be CFB) */
> +	tpm_buf_append_u16(&buf, TPM_ALG_CFB);
> +	/* hash algorithm for session */
> +	tpm_buf_append_u16(&buf, TPM_ALG_SHA256);
> +
> +	rc = tpm_transmit_cmd(chip, &buf, 0, "start auth session");
> +	tpm2_flush_context(chip, nullkey);
> +
> +	if (rc == TPM2_RC_SUCCESS)
> +		rc = tpm2_parse_start_auth_session(auth, &buf);
> +
> +	tpm_buf_destroy(&buf);
> +
> +	if (rc)
> +		goto out;
> +
> + out:
> +	return rc;
> +}
> +EXPORT_SYMBOL(tpm2_start_auth_session);
>  
>  static int tpm2_parse_create_primary(struct tpm_chip *chip, struct tpm_buf *buf,
>  				     u32 *nullkey)
		

What if this was simply tpm2_create_primary?


> @@ -271,6 +652,10 @@ int tpm2_sessions_init(struct tpm_chip *chip)
>  	if (rc)
>  		dev_err(&chip->dev, "TPM: security failed (NULL seed derivation): %d\n", rc);
>  
> +	chip->auth = kmalloc(sizeof(*chip->auth), GFP_KERNEL);
> +	if (!chip->auth)
> +		return -ENOMEM;
> +
>  	return rc;
>  }
>  EXPORT_SYMBOL(tpm2_sessions_init);
> diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> index 3060ab794afb..444f0a83682a 100644
> --- a/include/linux/tpm.h
> +++ b/include/linux/tpm.h
> @@ -30,6 +30,14 @@
>  struct tpm_chip;
>  struct trusted_key_payload;
>  struct trusted_key_options;
> +/* opaque structure, holds auth session parameters like the session key */
> +struct tpm2_auth;
> +
> +enum tpm2_session_types {
> +	TPM2_SE_HMAC	= 0x00,
> +	TPM2_SE_POLICY	= 0x01,
> +	TPM2_SE_TRIAL	= 0x02,
> +};
>  
>  /* if you add a new hash to this, increment TPM_MAX_HASHES below */
>  enum tpm_algorithms {
> @@ -199,6 +207,7 @@ struct tpm_chip {
>  	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];
> +	struct tpm2_auth *auth;
>  #endif
>  };
>  
> @@ -262,6 +271,7 @@ enum tpm2_command_codes {
>  	TPM2_CC_CONTEXT_LOAD	        = 0x0161,
>  	TPM2_CC_CONTEXT_SAVE	        = 0x0162,
>  	TPM2_CC_FLUSH_CONTEXT	        = 0x0165,
> +	TPM2_CC_START_AUTH_SESS		= 0x0176,
>  	TPM2_CC_VERIFY_SIGNATURE        = 0x0177,
>  	TPM2_CC_GET_CAPABILITY	        = 0x017A,
>  	TPM2_CC_GET_RANDOM	        = 0x017B,
> @@ -345,20 +355,30 @@ struct tpm_buf {
>  	u32 flags;
>  	u32 length;
>  	u8 *data;
> +	u8 handles;
>  };
>  
>  enum tpm2_object_attributes {
>  	TPM2_OA_FIXED_TPM		= BIT(1),
> +	TPM2_OA_ST_CLEAR		= BIT(2),
>  	TPM2_OA_FIXED_PARENT		= BIT(4),
>  	TPM2_OA_SENSITIVE_DATA_ORIGIN	= BIT(5),
>  	TPM2_OA_USER_WITH_AUTH		= BIT(6),
> +	TPM2_OA_ADMIN_WITH_POLICY	= BIT(7),
>  	TPM2_OA_NO_DA			= BIT(10),
> +	TPM2_OA_ENCRYPTED_DUPLICATION	= BIT(11),
>  	TPM2_OA_RESTRICTED		= BIT(16),
>  	TPM2_OA_DECRYPT			= BIT(17),
> +	TPM2_OA_SIGN			= BIT(18),
>  };
>  
>  enum tpm2_session_attributes {
>  	TPM2_SA_CONTINUE_SESSION	= BIT(0),
> +	TPM2_SA_AUDIT_EXCLUSIVE		= BIT(1),
> +	TPM2_SA_AUDIT_RESET		= BIT(3),
> +	TPM2_SA_DECRYPT			= BIT(5),
> +	TPM2_SA_ENCRYPT			= BIT(6),
> +	TPM2_SA_AUDIT			= BIT(7),
>  };
>  
>  struct tpm2_hash {
> @@ -449,4 +469,18 @@ static inline void tpm_buf_append_empty_auth(struct tpm_buf *buf, u32 handle)
>  {
>  }
>  #endif
> +#ifdef CONFIG_TCG_TPM2_HMAC
> +
> +int tpm2_start_auth_session(struct tpm_chip *chip);
> +void tpm2_end_auth_session(struct tpm_chip *chip);
> +#else
> +static inline int tpm2_start_auth_session(struct tpm_chip *chip)
> +{
> +	return 0;
> +}
> +static inline void tpm2_end_auth_session(struct tpm_chip *chip)
> +{
> +}
> +#endif	/* CONFIG_TCG_TPM2_HMAC */
> +
>  #endif


BR, Jarkko
James Bottomley April 29, 2024, 8:11 p.m. UTC | #2
On Fri, 2024-02-23 at 19:02 +0200, Jarkko Sakkinen wrote:
> On Tue Feb 13, 2024 at 7:13 PM EET, James Bottomley wrote:
[...]
> >  static int tpm2_parse_create_primary(struct tpm_chip *chip, struct
> > tpm_buf *buf,
> >                                      u32 *nullkey)
>                 
> 
> What if this was simply tpm2_create_primary?

It would cause a massive code block surrounded by an if, which would
make the function somewhat less readable.

James
diff mbox series

Patch

diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index e3c39a83171b..086cb8588493 100644
--- a/drivers/char/tpm/Kconfig
+++ b/drivers/char/tpm/Kconfig
@@ -30,6 +30,9 @@  if TCG_TPM
 config TCG_TPM2_HMAC
 	bool "Use encrypted and HMACd transactions on the TPM bus"
 	default y
+	select CRYPTO_ECDH
+	select CRYPTO_LIB_AESCFB
+	select CRYPTO_LIB_SHA256
 	help
           Setting this causes us to deploy a scheme which uses request
 	  and response HMACs in addition to encryption for
diff --git a/drivers/char/tpm/tpm-buf.c b/drivers/char/tpm/tpm-buf.c
index bb81180495d1..274130398569 100644
--- a/drivers/char/tpm/tpm-buf.c
+++ b/drivers/char/tpm/tpm-buf.c
@@ -44,6 +44,7 @@  void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal)
 	head->tag = cpu_to_be16(tag);
 	head->length = cpu_to_be32(sizeof(*head));
 	head->ordinal = cpu_to_be32(ordinal);
+	buf->handles = 0;
 }
 EXPORT_SYMBOL_GPL(tpm_buf_reset);
 
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index 42b1062e33cd..d93937326b2e 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -275,6 +275,9 @@  static void tpm_dev_release(struct device *dev)
 	kfree(chip->work_space.context_buf);
 	kfree(chip->work_space.session_buf);
 	kfree(chip->allocated_banks);
+#ifdef CONFIG_TCG_TPM2_HMAC
+	kfree(chip->auth);
+#endif
 	kfree(chip);
 }
 
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 9fc263ee02c2..c8792a6b8bd4 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -3,18 +3,399 @@ 
 /*
  * Copyright (C) 2018 James.Bottomley@HansenPartnership.com
  *
+ * Cryptographic helper routines for handling TPM2 sessions for
+ * authorization HMAC and request response encryption.
+ *
+ * The idea is to ensure that every TPM command is HMAC protected by a
+ * session, meaning in-flight tampering would be detected and in
+ * addition all sensitive inputs and responses should be encrypted.
+ *
+ * The basic way this works is to use a TPM feature called salted
+ * sessions where a random secret used in session construction is
+ * encrypted to the public part of a known TPM key.  The problem is we
+ * have no known keys, so initially a primary Elliptic Curve key is
+ * derived from the NULL seed (we use EC because most TPMs generate
+ * these keys much faster than RSA ones).  The curve used is NIST_P256
+ * because that's now mandated to be present in 'TCG TPM v2.0
+ * Provisioning Guidance'
+ *
+ * Threat problems: the initial TPM2_CreatePrimary is not (and cannot
+ * be) session protected, so a clever Man in the Middle could return a
+ * public key they control to this command and from there intercept
+ * and decode all subsequent session based transactions.  The kernel
+ * cannot mitigate this threat but, after boot, userspace can get
+ * proof this has not happened by asking the TPM to certify the NULL
+ * key.  This certification would chain back to the TPM Endorsement
+ * Certificate and prove the NULL seed primary had not been tampered
+ * with and thus all sessions must have been cryptographically secure.
+ * To assist with this, the initial NULL seed public key name is made
+ * available in a sysfs file.
+ *
+ * Use of these functions:
+ *
+ * The design is all the crypto, hash and hmac gunk is confined in this
+ * file and never needs to be seen even by the kernel internal user.  To
+ * the user there's an init function tpm2_sessions_init() that needs to
+ * be called once per TPM which generates the NULL seed primary key.
+ *
+ * These are the usage functions:
+ *
+ * tpm2_start_auth_session() which allocates the opaque auth structure
+ *	and gets a session from the TPM.  This must be called before
+ *	any of the following functions.  The session is protected by a
+ *	session_key which is derived from a random salt value
+ *	encrypted to the NULL seed.
+ * tpm2_end_auth_session() kills the session and frees the resources.
+ *	Under normal operation this function is done by
+ *	tpm_buf_check_hmac_response(), so this is only to be used on
+ *	error legs where the latter is not executed.
  */
 
 #include "tpm.h"
 
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+
 #include <asm/unaligned.h>
 
 #include <crypto/aes.h>
+#include <crypto/kpp.h>
+#include <crypto/ecdh.h>
+#include <crypto/hash.h>
+#include <crypto/hmac.h>
 
 /* if you change to AES256, you only need change this */
 #define AES_KEYBYTES	AES_KEYSIZE_128
 
 #define AES_KEYBITS	(AES_KEYBYTES*8)
+#define AUTH_MAX_NAMES	3
+
+/*
+ * This is the structure that carries all the auth information (like
+ * session handle, nonces, session key and auth) from use to use it is
+ * designed to be opaque to anything outside.
+ */
+struct tpm2_auth {
+	u32 handle;
+	/*
+	 * This has two meanings: before tpm_buf_fill_hmac_session()
+	 * it marks the offset in the buffer of the start of the
+	 * sessions (i.e. after all the handles).  Once the buffer has
+	 * been filled it markes the session number of our auth
+	 * session so we can find it again in the response buffer.
+	 *
+	 * The two cases are distinguished because the first offset
+	 * must always be greater than TPM_HEADER_SIZE and the second
+	 * must be less than or equal to 5.
+	 */
+	u32 session;
+	/*
+	 * the size here is variable and set by the size of our_nonce
+	 * which must be between 16 and the name hash length. we set
+	 * the maximum sha256 size for the greatest protection
+	 */
+	u8 our_nonce[SHA256_DIGEST_SIZE];
+	u8 tpm_nonce[SHA256_DIGEST_SIZE];
+	/*
+	 * the salt is only used across the session command/response
+	 * after that it can be used as a scratch area
+	 */
+	union {
+		u8 salt[EC_PT_SZ];
+		/* scratch for key + IV */
+		u8 scratch[AES_KEYBYTES + AES_BLOCK_SIZE];
+	};
+	u8 session_key[SHA256_DIGEST_SIZE];
+};
+
+/*
+ * It turns out the crypto hmac(sha256) is hard for us to consume
+ * because it assumes a fixed key and the TPM seems to change the key
+ * on every operation, so we weld the hmac init and final functions in
+ * here to give it the same usage characteristics as a regular hash
+ */
+static void hmac_init(struct sha256_state *sctx, u8 *key, int keylen)
+{
+	u8 pad[SHA256_BLOCK_SIZE];
+	int i;
+
+	sha256_init(sctx);
+	for (i = 0; i < sizeof(pad); i++) {
+		if (i < keylen)
+			pad[i] = key[i];
+		else
+			pad[i] = 0;
+		pad[i] ^= HMAC_IPAD_VALUE;
+	}
+	sha256_update(sctx, pad, sizeof(pad));
+}
+
+static void hmac_final(struct sha256_state *sctx, u8 *key, int keylen, u8 *out)
+{
+	u8 pad[SHA256_BLOCK_SIZE];
+	int i;
+
+	for (i = 0; i < sizeof(pad); i++) {
+		if (i < keylen)
+			pad[i] = key[i];
+		else
+			pad[i] = 0;
+		pad[i] ^= HMAC_OPAD_VALUE;
+	}
+
+	/* collect the final hash;  use out as temporary storage */
+	sha256_final(sctx, out);
+
+	sha256_init(sctx);
+	sha256_update(sctx, pad, sizeof(pad));
+	sha256_update(sctx, out, SHA256_DIGEST_SIZE);
+	sha256_final(sctx, out);
+}
+
+/*
+ * assume hash sha256 and nonces u, v of size SHA256_DIGEST_SIZE but
+ * otherwise standard KDFa.  Note output is in bytes not bits.
+ */
+static void KDFa(u8 *key, int keylen, const char *label, u8 *u,
+		 u8 *v, int bytes, u8 *out)
+{
+	u32 counter;
+	const __be32 bits = cpu_to_be32(bytes * 8);
+
+	for (counter = 1; bytes > 0; bytes -= SHA256_DIGEST_SIZE, counter++,
+		     out += SHA256_DIGEST_SIZE) {
+		struct sha256_state sctx;
+		__be32 c = cpu_to_be32(counter);
+
+		hmac_init(&sctx, key, keylen);
+		sha256_update(&sctx, (u8 *)&c, sizeof(c));
+		sha256_update(&sctx, label, strlen(label)+1);
+		sha256_update(&sctx, u, SHA256_DIGEST_SIZE);
+		sha256_update(&sctx, v, SHA256_DIGEST_SIZE);
+		sha256_update(&sctx, (u8 *)&bits, sizeof(bits));
+		hmac_final(&sctx, key, keylen, out);
+	}
+}
+
+/*
+ * Somewhat of a bastardization of the real KDFe.  We're assuming
+ * we're working with known point sizes for the input parameters and
+ * the hash algorithm is fixed at sha256.  Because we know that the
+ * point size is 32 bytes like the hash size, there's no need to loop
+ * in this KDF.
+ */
+static void KDFe(u8 z[EC_PT_SZ], const char *str, u8 *pt_u, u8 *pt_v,
+		 u8 *keyout)
+{
+	struct sha256_state sctx;
+	/*
+	 * this should be an iterative counter, but because we know
+	 *  we're only taking 32 bytes for the point using a sha256
+	 *  hash which is also 32 bytes, there's only one loop
+	 */
+	__be32 c = cpu_to_be32(1);
+
+	sha256_init(&sctx);
+	/* counter (BE) */
+	sha256_update(&sctx, (u8 *)&c, sizeof(c));
+	/* secret value */
+	sha256_update(&sctx, z, EC_PT_SZ);
+	/* string including trailing zero */
+	sha256_update(&sctx, str, strlen(str)+1);
+	sha256_update(&sctx, pt_u, EC_PT_SZ);
+	sha256_update(&sctx, pt_v, EC_PT_SZ);
+	sha256_final(&sctx, keyout);
+}
+
+static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip)
+{
+	struct crypto_kpp *kpp;
+	struct kpp_request *req;
+	struct scatterlist s[2], d[1];
+	struct ecdh p = {0};
+	u8 encoded_key[EC_PT_SZ], *x, *y;
+	unsigned int buf_len;
+
+	/* secret is two sized points */
+	tpm_buf_append_u16(buf, (EC_PT_SZ + 2)*2);
+	/*
+	 * we cheat here and append uninitialized data to form
+	 * the points.  All we care about is getting the two
+	 * co-ordinate pointers, which will be used to overwrite
+	 * the uninitialized data
+	 */
+	tpm_buf_append_u16(buf, EC_PT_SZ);
+	x = &buf->data[tpm_buf_length(buf)];
+	tpm_buf_append(buf, encoded_key, EC_PT_SZ);
+	tpm_buf_append_u16(buf, EC_PT_SZ);
+	y = &buf->data[tpm_buf_length(buf)];
+	tpm_buf_append(buf, encoded_key, EC_PT_SZ);
+	sg_init_table(s, 2);
+	sg_set_buf(&s[0], x, EC_PT_SZ);
+	sg_set_buf(&s[1], y, EC_PT_SZ);
+
+	kpp = crypto_alloc_kpp("ecdh-nist-p256", CRYPTO_ALG_INTERNAL, 0);
+	if (IS_ERR(kpp)) {
+		dev_err(&chip->dev, "crypto ecdh allocation failed\n");
+		return;
+	}
+
+	buf_len = crypto_ecdh_key_len(&p);
+	if (sizeof(encoded_key) < buf_len) {
+		dev_err(&chip->dev, "salt buffer too small needs %d\n",
+			buf_len);
+		goto out;
+	}
+	crypto_ecdh_encode_key(encoded_key, buf_len, &p);
+	/* this generates a random private key */
+	crypto_kpp_set_secret(kpp, encoded_key, buf_len);
+
+	/* salt is now the public point of this private key */
+	req = kpp_request_alloc(kpp, GFP_KERNEL);
+	if (!req)
+		goto out;
+	kpp_request_set_input(req, NULL, 0);
+	kpp_request_set_output(req, s, EC_PT_SZ*2);
+	crypto_kpp_generate_public_key(req);
+	/*
+	 * we're not done: now we have to compute the shared secret
+	 * which is our private key multiplied by the tpm_key public
+	 * point, we actually only take the x point and discard the y
+	 * point and feed it through KDFe to get the final secret salt
+	 */
+	sg_set_buf(&s[0], chip->null_ec_key_x, EC_PT_SZ);
+	sg_set_buf(&s[1], chip->null_ec_key_y, EC_PT_SZ);
+	kpp_request_set_input(req, s, EC_PT_SZ*2);
+	sg_init_one(d, chip->auth->salt, EC_PT_SZ);
+	kpp_request_set_output(req, d, EC_PT_SZ);
+	crypto_kpp_compute_shared_secret(req);
+	kpp_request_free(req);
+
+	/*
+	 * pass the shared secret through KDFe for salt. Note salt
+	 * area is used both for input shared secret and output salt.
+	 * This works because KDFe fully consumes the secret before it
+	 * writes the salt
+	 */
+	KDFe(chip->auth->salt, "SECRET", x, chip->null_ec_key_x,
+	     chip->auth->salt);
+ out:
+	crypto_free_kpp(kpp);
+}
+/**
+ * tpm2_end_auth_session() - kill the allocated auth session
+ * @chip: the TPM chip structure
+ *
+ * ends the session started by tpm2_start_auth_session and frees all
+ * the resources.  Under normal conditions,
+ * tpm_buf_check_hmac_response() will correctly end the session if
+ * required, so this function is only for use in error legs that will
+ * bypass the normal invocation of tpm_buf_check_hmac_response().
+ */
+void tpm2_end_auth_session(struct tpm_chip *chip)
+{
+	tpm2_flush_context(chip, chip->auth->handle);
+	memzero_explicit(chip->auth, sizeof(*chip->auth));
+}
+EXPORT_SYMBOL(tpm2_end_auth_session);
+
+static int tpm2_parse_start_auth_session(struct tpm2_auth *auth,
+					 struct tpm_buf *buf)
+{
+	struct tpm_header *head = (struct tpm_header *)buf->data;
+	u32 tot_len = be32_to_cpu(head->length);
+	off_t offset = TPM_HEADER_SIZE;
+	u32 val;
+
+	/* we're starting after the header so adjust the length */
+	tot_len -= TPM_HEADER_SIZE;
+
+	/* should have handle plus nonce */
+	if (tot_len != 4 + 2 + sizeof(auth->tpm_nonce))
+		return -EINVAL;
+
+	auth->handle = tpm_buf_read_u32(buf, &offset);
+	val = tpm_buf_read_u16(buf, &offset);
+	if (val != sizeof(auth->tpm_nonce))
+		return -EINVAL;
+	memcpy(auth->tpm_nonce, &buf->data[offset], sizeof(auth->tpm_nonce));
+	/* now compute the session key from the nonces */
+	KDFa(auth->salt, sizeof(auth->salt), "ATH", auth->tpm_nonce,
+	     auth->our_nonce, sizeof(auth->session_key), auth->session_key);
+
+	return 0;
+}
+
+/**
+ * tpm2_start_auth_session() - create a HMAC authentication session with the TPM
+ * @chip: the TPM chip structure to create the session with
+ *
+ * This function loads the NULL seed from its saved context and starts
+ * an authentication session on the null seed, fills in the
+ * @chip->auth structure to contain all the session details necessary
+ * for performing the HMAC, encrypt and decrypt operations and
+ * returns.  The NULL seed is flushed before this function returns.
+ *
+ * Return: zero on success or actual error encountered.
+ */
+int tpm2_start_auth_session(struct tpm_chip *chip)
+{
+	struct tpm_buf buf;
+	struct tpm2_auth *auth = chip->auth;
+	int rc;
+	unsigned int offset = 0; /* dummy offset for null seed context */
+	u32 nullkey;
+
+	rc = tpm2_load_context(chip, chip->null_key_context, &offset,
+			       &nullkey);
+	if (rc)
+		goto out;
+
+	auth->session = TPM_HEADER_SIZE;
+
+	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_START_AUTH_SESS);
+	if (rc)
+		goto out;
+
+	/* salt key handle */
+	tpm_buf_append_u32(&buf, nullkey);
+	/* bind key handle */
+	tpm_buf_append_u32(&buf, TPM2_RH_NULL);
+	/* nonce caller */
+	get_random_bytes(auth->our_nonce, sizeof(auth->our_nonce));
+	tpm_buf_append_u16(&buf, sizeof(auth->our_nonce));
+	tpm_buf_append(&buf, auth->our_nonce, sizeof(auth->our_nonce));
+
+	/* append encrypted salt and squirrel away unencrypted in auth */
+	tpm_buf_append_salt(&buf, chip);
+	/* session type (HMAC, audit or policy) */
+	tpm_buf_append_u8(&buf, TPM2_SE_HMAC);
+
+	/* symmetric encryption parameters */
+	/* symmetric algorithm */
+	tpm_buf_append_u16(&buf, TPM_ALG_AES);
+	/* bits for symmetric algorithm */
+	tpm_buf_append_u16(&buf, AES_KEYBITS);
+	/* symmetric algorithm mode (must be CFB) */
+	tpm_buf_append_u16(&buf, TPM_ALG_CFB);
+	/* hash algorithm for session */
+	tpm_buf_append_u16(&buf, TPM_ALG_SHA256);
+
+	rc = tpm_transmit_cmd(chip, &buf, 0, "start auth session");
+	tpm2_flush_context(chip, nullkey);
+
+	if (rc == TPM2_RC_SUCCESS)
+		rc = tpm2_parse_start_auth_session(auth, &buf);
+
+	tpm_buf_destroy(&buf);
+
+	if (rc)
+		goto out;
+
+ out:
+	return rc;
+}
+EXPORT_SYMBOL(tpm2_start_auth_session);
 
 static int tpm2_parse_create_primary(struct tpm_chip *chip, struct tpm_buf *buf,
 				     u32 *nullkey)
@@ -271,6 +652,10 @@  int tpm2_sessions_init(struct tpm_chip *chip)
 	if (rc)
 		dev_err(&chip->dev, "TPM: security failed (NULL seed derivation): %d\n", rc);
 
+	chip->auth = kmalloc(sizeof(*chip->auth), GFP_KERNEL);
+	if (!chip->auth)
+		return -ENOMEM;
+
 	return rc;
 }
 EXPORT_SYMBOL(tpm2_sessions_init);
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 3060ab794afb..444f0a83682a 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -30,6 +30,14 @@ 
 struct tpm_chip;
 struct trusted_key_payload;
 struct trusted_key_options;
+/* opaque structure, holds auth session parameters like the session key */
+struct tpm2_auth;
+
+enum tpm2_session_types {
+	TPM2_SE_HMAC	= 0x00,
+	TPM2_SE_POLICY	= 0x01,
+	TPM2_SE_TRIAL	= 0x02,
+};
 
 /* if you add a new hash to this, increment TPM_MAX_HASHES below */
 enum tpm_algorithms {
@@ -199,6 +207,7 @@  struct tpm_chip {
 	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];
+	struct tpm2_auth *auth;
 #endif
 };
 
@@ -262,6 +271,7 @@  enum tpm2_command_codes {
 	TPM2_CC_CONTEXT_LOAD	        = 0x0161,
 	TPM2_CC_CONTEXT_SAVE	        = 0x0162,
 	TPM2_CC_FLUSH_CONTEXT	        = 0x0165,
+	TPM2_CC_START_AUTH_SESS		= 0x0176,
 	TPM2_CC_VERIFY_SIGNATURE        = 0x0177,
 	TPM2_CC_GET_CAPABILITY	        = 0x017A,
 	TPM2_CC_GET_RANDOM	        = 0x017B,
@@ -345,20 +355,30 @@  struct tpm_buf {
 	u32 flags;
 	u32 length;
 	u8 *data;
+	u8 handles;
 };
 
 enum tpm2_object_attributes {
 	TPM2_OA_FIXED_TPM		= BIT(1),
+	TPM2_OA_ST_CLEAR		= BIT(2),
 	TPM2_OA_FIXED_PARENT		= BIT(4),
 	TPM2_OA_SENSITIVE_DATA_ORIGIN	= BIT(5),
 	TPM2_OA_USER_WITH_AUTH		= BIT(6),
+	TPM2_OA_ADMIN_WITH_POLICY	= BIT(7),
 	TPM2_OA_NO_DA			= BIT(10),
+	TPM2_OA_ENCRYPTED_DUPLICATION	= BIT(11),
 	TPM2_OA_RESTRICTED		= BIT(16),
 	TPM2_OA_DECRYPT			= BIT(17),
+	TPM2_OA_SIGN			= BIT(18),
 };
 
 enum tpm2_session_attributes {
 	TPM2_SA_CONTINUE_SESSION	= BIT(0),
+	TPM2_SA_AUDIT_EXCLUSIVE		= BIT(1),
+	TPM2_SA_AUDIT_RESET		= BIT(3),
+	TPM2_SA_DECRYPT			= BIT(5),
+	TPM2_SA_ENCRYPT			= BIT(6),
+	TPM2_SA_AUDIT			= BIT(7),
 };
 
 struct tpm2_hash {
@@ -449,4 +469,18 @@  static inline void tpm_buf_append_empty_auth(struct tpm_buf *buf, u32 handle)
 {
 }
 #endif
+#ifdef CONFIG_TCG_TPM2_HMAC
+
+int tpm2_start_auth_session(struct tpm_chip *chip);
+void tpm2_end_auth_session(struct tpm_chip *chip);
+#else
+static inline int tpm2_start_auth_session(struct tpm_chip *chip)
+{
+	return 0;
+}
+static inline void tpm2_end_auth_session(struct tpm_chip *chip)
+{
+}
+#endif	/* CONFIG_TCG_TPM2_HMAC */
+
 #endif