Linux Integrity Measurement development
 help / color / mirror / Atom feed
* Re: [PATCH] KEYS: trusted: Re-orchestrate tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-07  9:28 UTC (permalink / raw)
  To: James Bottomley
  Cc: linux-integrity, Peter Huewe, Jason Gunthorpe, Mimi Zohar,
	David Howells, Paul Moore, James Morris, Serge E. Hallyn,
	open list, open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM
In-Reply-To: <aTVGiubK5EKitM9u@kernel.org>

On Sun, Dec 07, 2025 at 11:19:10AM +0200, Jarkko Sakkinen wrote:
> On Sun, Dec 07, 2025 at 03:33:17PM +0900, James Bottomley wrote:
> > On Fri, 2025-12-05 at 02:49 +0200, Jarkko Sakkinen wrote:
> > > On Fri, Dec 05, 2025 at 01:20:30AM +0200, Jarkko Sakkinen wrote:
> > > > On Fri, Dec 05, 2025 at 12:31:27AM +0200, Jarkko Sakkinen wrote:
> > > > > tpm2_load_cmd() and tpm2_unseal_cmd() use the same parent, and
> > > > > calls to
> > > > > tpm_buf_append_name() cause the exact same TPM2_ReadPublic
> > > > > command to be
> > > > > sent to the chip, causing unnecessary traffic.
> > > > > 
> > > > > 1. Export tpm2_read_public in order to make it callable from
> > > > > 'trusted_tpm2'.
> > > > > 2. Re-orchestrate tpm2_seal_trusted() and tpm2_unseal_trusted()
> > > > > in order to
> > > > >    halve the name resolutions required:
> > > > > 2a. Move tpm2_read_public() calls into trusted_tpm2.
> > > > > 2b. Pass TPM name to tpm_buf_append_name().
> > > > > 2c. Rework tpm_buf_append_name() to use the pre-resolved name.
> > > > > 
> > > > > Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> > > > 
> > > > If ASN.1 blob would contain also name of the parent then zero
> > > > tpm2_read_public() calls would be required i.e., the main
> > > > bottleneck here inherits from the limitations of the file format
> > > > itself.
> > > 
> > > Along the lines of attached patch.
> > 
> > Well firstly [6] is already being taken by the creation data proposal,
> > so this would introduce an incompatibility between the kernel and the
> > spec, but secondly, if you want something like this in the spec before
> > it goes to the IETF you really need to propose it now.
> 
> What is the mailing list for the working group, or is this still
> unclear as of today?
> 
> > 
> > The problem with this particular addition is that it would be Linux
> > Kernel specific.  All the current TSSs already do a cached read public
> > under the covers when they add the session wrappings so the user facing
> > API they expose has nowhere to insert (or easily extract) a name field
> > and thus TSS based implementations would have no incentive to either
> > output or consume this field.  That's not to say the standard can't
> > have additions for crazy or niche use cases (that's what the rsaParent
> > flag is: a one off to support a niche SUSE use case) but it would be
> > hard to persuade user implementations to do this so the kernel would
> > have to interoperate with the case where it didn't exist anyway.
> 
> It is just matter of conditionally calling tpm2_read_public(). Not
> a big deal.
> 
> > 
> > The standard use today is with permanent handles for parents, where the
> > parent is created on the fly, so the name is actually returned from
> > TPM2_CreatePrimary for this use case.  It should be a simple matter to
> > bring the kernel implementation up to doing this as well.
> 
> Intercepting TPM2_CreatePrimary does not provide full coverage, as
> it does not address persistent keys, which kernel interface does
> support.

It also weaker solution in the sense that it lacks support for secondary
keys as parents.

Name digest is universal as it scales primary keys, secondary keys both
persistent and transient.

I'm not really sure why we should use TSS implementations as reference
for anything in this space. We always want to support "superset' because
it actually creates innovation and distruption to the pre-existing
science.

BR, Jarkko



^ permalink raw reply

* [PATCH v6 0/2] Optimize tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-07 17:32 UTC (permalink / raw)
  To: linux-integrity
  Cc: tpm2, Jarkko Sakkinen, James Bottomley, Mimi Zohar, David Howells,
	Paul Moore, James Morris, Serge E. Hallyn, open list:KEYS-TRUSTED,
	open list:SECURITY SUBSYSTEM, open list

The main goal is fairly straight-forwrd here.

The aim of these patches is optimize the number of tpm2_read_public()
calls to the bare minimum. The last patch is a bit out of scope but
I included because it builds on top of this work.

Jarkko Sakkinen (2):
  KEYS: trusted: Re-orchestrate tpm2_read_public() calls
  KEYS: trusted: Store parent's name to the encoded keys

 drivers/char/tpm/tpm2-cmd.c               |  86 ++++++++-
 drivers/char/tpm/tpm2-sessions.c          | 156 ++--------------
 include/linux/tpm.h                       |   3 +-
 security/keys/trusted-keys/tpm2key.asn1   |  17 +-
 security/keys/trusted-keys/trusted_tpm2.c | 207 +++++++++++++++-------
 5 files changed, 259 insertions(+), 210 deletions(-)

-- 
2.39.5


^ permalink raw reply

* [PATCH v6 1/2] KEYS: trusted: Re-orchestrate tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-07 17:32 UTC (permalink / raw)
  To: linux-integrity
  Cc: tpm2, Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe,
	James Bottomley, Mimi Zohar, David Howells, Paul Moore,
	James Morris, Serge E. Hallyn, open list, open list:KEYS-TRUSTED,
	open list:SECURITY SUBSYSTEM
In-Reply-To: <20251207173210.93765-1-jarkko@kernel.org>

tpm2_load_cmd() and tpm2_unseal_cmd() use the same parent, and calls to
tpm_buf_append_name() cause the exact same TPM2_ReadPublic command to be
sent to the chip, causing unnecessary traffic.

1. Export tpm2_read_public in order to make it callable from
   'trusted_tpm2'.
2. Re-orchestrate tpm2_seal_trusted() and tpm2_unseal_trusted() in order to
   halve the name resolutions required:
2a. Move tpm2_read_public() calls into trusted_tpm2.
2b. Pass TPM name to tpm_buf_append_name().
2c. Rework tpm_buf_append_name() to use the pre-resolved name.

Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v6:
- No changes.
v5:
- Rebased to the mainline.
v4:
- No changes
v3:
- No changes.
v2:
- No changes.
---
 drivers/char/tpm/tpm2-cmd.c               |   3 +-
 drivers/char/tpm/tpm2-sessions.c          |  95 +++++------------
 include/linux/tpm.h                       |  10 +-
 security/keys/trusted-keys/trusted_tpm2.c | 124 ++++++++++++++--------
 4 files changed, 118 insertions(+), 114 deletions(-)

diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 2682f5ec09cb..5b04e74b6377 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -199,7 +199,8 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
 	tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
 
 	if (!disable_pcr_integrity) {
-		rc = tpm_buf_append_name(chip, buf, pcr_idx, NULL);
+		rc = tpm_buf_append_name(chip, buf, pcr_idx, (u8 *)&pcr_idx,
+					 sizeof(u32));
 		if (rc)
 			return rc;
 		tpm_buf_append_hmac_session(chip, buf, 0, NULL, 0);
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 79f27a46bd7f..6b7112a7c30c 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -136,8 +136,8 @@ struct tpm2_auth {
 	 * handle, but they are part of the session by name, which
 	 * we must compute and remember
 	 */
-	u32 name_h[AUTH_MAX_NAMES];
 	u8 name[AUTH_MAX_NAMES][2 + SHA512_DIGEST_SIZE];
+	u16 name_size_tbl[AUTH_MAX_NAMES];
 };
 
 #ifdef CONFIG_TCG_TPM2_HMAC
@@ -163,7 +163,17 @@ static int name_size(const u8 *name)
 	}
 }
 
-static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
+/**
+ * tpm2_read_public: Resolve TPM name for a handle
+ * @chip:		TPM chip to use.
+ * @handle:		TPM handle.
+ * @name:		A buffer for returning the name blob. Must have a
+ *			capacity of 'SHA512_DIGET_SIZE + 2' bytes at minimum
+ *
+ * Returns size of TPM handle name of success.
+ * Returns tpm_transmit_cmd error codes when TPM2_ReadPublic fails.
+ */
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
 {
 	u32 mso = tpm2_handle_mso(handle);
 	off_t offset = TPM_HEADER_SIZE;
@@ -212,14 +222,16 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
 	memcpy(name, &buf->data[offset], rc);
 	return name_size_alg;
 }
+EXPORT_SYMBOL_GPL(tpm2_read_public);
 #endif /* CONFIG_TCG_TPM2_HMAC */
 
 /**
- * tpm_buf_append_name() - add a handle area to the buffer
- * @chip: the TPM chip structure
- * @buf: The buffer to be appended
- * @handle: The handle to be appended
- * @name: The name of the handle (may be NULL)
+ * tpm_buf_append_name() - Append a handle and store TPM name
+ * @chip:		TPM chip to use.
+ * @buf:		TPM buffer containing the TPM command in-transit.
+ * @handle:		TPM handle to be appended.
+ * @name:		TPM name of the handle
+ * @name_size:		Size of the TPM name.
  *
  * In order to compute session HMACs, we need to know the names of the
  * objects pointed to by the handles.  For most objects, this is simply
@@ -236,15 +248,14 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
  * will be caused by an incorrect programming model and indicated by a
  * kernel message.
  *
- * Ends the authorization session on failure.
+ * Returns zero on success.
+ * Returns -EIO when the authorization area state is malformed.
  */
 int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
-			u32 handle, u8 *name)
+			u32 handle, u8 *name, u16 name_size)
 {
 #ifdef CONFIG_TCG_TPM2_HMAC
-	enum tpm2_mso_type mso = tpm2_handle_mso(handle);
 	struct tpm2_auth *auth;
-	u16 name_size_alg;
 	int slot;
 	int ret;
 #endif
@@ -269,36 +280,15 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
 	}
 	tpm_buf_append_u32(buf, handle);
 	auth->session += 4;
-
-	if (mso == TPM2_MSO_PERSISTENT ||
-	    mso == TPM2_MSO_VOLATILE ||
-	    mso == TPM2_MSO_NVRAM) {
-		if (!name) {
-			ret = tpm2_read_public(chip, handle, auth->name[slot]);
-			if (ret < 0)
-				goto err;
-
-			name_size_alg = ret;
-		}
-	} else {
-		if (name) {
-			dev_err(&chip->dev, "handle 0x%08x does not use a name\n",
-				handle);
-			ret = -EIO;
-			goto err;
-		}
-	}
-
-	auth->name_h[slot] = handle;
-	if (name)
-		memcpy(auth->name[slot], name, name_size_alg);
+	memcpy(auth->name[slot], name, name_size);
+	auth->name_size_tbl[slot] = name_size;
 #endif
 	return 0;
 
 #ifdef CONFIG_TCG_TPM2_HMAC
 err:
 	tpm2_end_auth_session(chip);
-	return tpm_ret_to_err(ret);
+	return ret;
 #endif
 }
 EXPORT_SYMBOL_GPL(tpm_buf_append_name);
@@ -606,22 +596,8 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
 	attrs = chip->cc_attrs_tbl[i];
 
 	handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
+	offset_s += handles * sizeof(u32);
 
-	/*
-	 * just check the names, it's easy to make mistakes.  This
-	 * would happen if someone added a handle via
-	 * tpm_buf_append_u32() instead of tpm_buf_append_name()
-	 */
-	for (i = 0; i < handles; i++) {
-		u32 handle = tpm_buf_read_u32(buf, &offset_s);
-
-		if (auth->name_h[i] != handle) {
-			dev_err(&chip->dev, "invalid handle 0x%08x\n", handle);
-			ret = -EIO;
-			goto err;
-		}
-	}
-	/* point offset_s to the start of the sessions */
 	val = tpm_buf_read_u32(buf, &offset_s);
 	/* point offset_p to the start of the parameters */
 	offset_p = offset_s + val;
@@ -682,23 +658,8 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
 	/* ordinal is already BE */
 	sha256_update(&sctx, (u8 *)&head->ordinal, sizeof(head->ordinal));
 	/* add the handle names */
-	for (i = 0; i < handles; i++) {
-		enum tpm2_mso_type mso = tpm2_handle_mso(auth->name_h[i]);
-
-		if (mso == TPM2_MSO_PERSISTENT ||
-		    mso == TPM2_MSO_VOLATILE ||
-		    mso == TPM2_MSO_NVRAM) {
-			ret = name_size(auth->name[i]);
-			if (ret < 0)
-				goto err;
-
-			sha256_update(&sctx, auth->name[i], ret);
-		} else {
-			__be32 h = cpu_to_be32(auth->name_h[i]);
-
-			sha256_update(&sctx, (u8 *)&h, 4);
-		}
-	}
+	for (i = 0; i < handles; i++)
+		sha256_update(&sctx, auth->name[i], auth->name_size_tbl[i]);
 	if (offset_s != tpm_buf_length(buf))
 		sha256_update(&sctx, &buf->data[offset_s],
 			      tpm_buf_length(buf) - offset_s);
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 42e2a091f43d..3f1d8c27394b 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -530,7 +530,7 @@ static inline struct tpm2_auth *tpm2_chip_auth(struct tpm_chip *chip)
 }
 
 int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
-			u32 handle, u8 *name);
+			u32 handle, u8 *name, u16 name_size);
 void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
 				 u8 attributes, u8 *passphrase,
 				 int passphraselen);
@@ -544,6 +544,7 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
 int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
 				int rc);
 void tpm2_end_auth_session(struct tpm_chip *chip);
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name);
 #else
 #include <linux/unaligned.h>
 
@@ -567,6 +568,13 @@ static inline int tpm_buf_check_hmac_response(struct tpm_chip *chip,
 {
 	return rc;
 }
+
+static inline int tpm2_read_public(struct tpm_chip *chip, u32 handle,
+				   void *name)
+{
+	memcpy(name, &handle, sizeof(u32));
+	return sizeof(u32);
+}
 #endif	/* CONFIG_TCG_TPM2_HMAC */
 
 #endif
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index fb76c4ea496f..0feb96cab62d 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -233,7 +233,9 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		      struct trusted_key_payload *payload,
 		      struct trusted_key_options *options)
 {
+	u8 parent_name[2 + SHA512_DIGEST_SIZE];
 	off_t offset = TPM_HEADER_SIZE;
+	u16 parent_name_size;
 	int blob_len = 0;
 	int hash;
 	u32 flags;
@@ -250,6 +252,12 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 	if (rc)
 		return rc;
 
+	rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+	if (rc < 0)
+		goto out_put;
+
+	parent_name_size = rc;
+
 	rc = tpm2_start_auth_session(chip);
 	if (rc)
 		goto out_put;
@@ -264,7 +272,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 	tpm_buf_init(buf, TPM_BUFSIZE);
 	tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
 
-	rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
+	rc = tpm_buf_append_name(chip, buf, options->keyhandle, parent_name,
+				 parent_name_size);
 	if (rc)
 		goto out_put;
 
@@ -356,48 +365,38 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 }
 
 /**
- * tpm2_load_cmd() - execute a TPM2_Load command
- *
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- * @blob_handle: returned blob handle
+ * tpm2_load_cmd() - Execute TPM2_Load
+ * @chip:		TPM chip to use.
+ * @payload:		Key data in clear text.
+ * @options:		Trusted key options.
+ * @parent_name:	A cryptographic name, i.e. a TPMT_HA blob, of the
+ *			parent key.
+ * @blob:		The decoded payload for the key.
+ * @blob_handle:	On success, will contain handle to the loaded keyedhash
+ *			blob.
  *
- * Return: 0 on success.
- *        -E2BIG on wrong payload size.
- *        -EPERM on tpm error status.
- *        < 0 error from tpm_send.
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load fails.
  */
 static int tpm2_load_cmd(struct tpm_chip *chip,
 			 struct trusted_key_payload *payload,
 			 struct trusted_key_options *options,
+			 u8 *parent_name,
+			 u16 parent_name_size,
+			 const u8 *blob,
 			 u32 *blob_handle)
 {
 	u8 *blob_ref __free(kfree) = NULL;
 	unsigned int private_len;
 	unsigned int public_len;
 	unsigned int blob_len;
-	u8 *blob, *pub;
+	const u8 *pub;
 	int rc;
 	u32 attrs;
 
-	rc = tpm2_key_decode(payload, options, &blob);
-	if (rc) {
-		/* old form */
-		blob = payload->blob;
-		payload->old_format = 1;
-	} else {
-		/* Bind for cleanup: */
-		blob_ref = blob;
-	}
-
-	/* new format carries keyhandle but old format doesn't */
-	if (!options->keyhandle)
-		return -EINVAL;
-
 	/* must be big enough for at least the two be16 size counts */
 	if (payload->blob_len < 4)
-		return -EINVAL;
+		return -E2BIG;
 
 	private_len = get_unaligned_be16(blob);
 
@@ -436,7 +435,8 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 	tpm_buf_init(buf, TPM_BUFSIZE);
 	tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
 
-	rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
+	rc = tpm_buf_append_name(chip, buf, options->keyhandle, parent_name,
+				 parent_name_size);
 	if (rc)
 		return rc;
 
@@ -464,20 +464,23 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 }
 
 /**
- * tpm2_unseal_cmd() - execute a TPM2_Unload command
+ * tpm2_unseal_cmd() - Execute TPM2_Unload
  *
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- * @blob_handle: blob handle
+ * @chip:		TPM chip to use
+ * @payload:		Key data in clear text.
+ * @options:		Trusted key options.
+ * @parent_name:	A cryptographic name, i.e. a TPMT_HA blob, of the
+ *			parent key.
+ * @blob_handle:	Handle to the loaded keyedhash blob.
  *
- * Return: 0 on success
- *         -EPERM on tpm error status
- *         < 0 error from tpm_send
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load fails.
  */
 static int tpm2_unseal_cmd(struct tpm_chip *chip,
 			   struct trusted_key_payload *payload,
 			   struct trusted_key_options *options,
+			   u8 *parent_name,
+			   u16 parent_name_size,
 			   u32 blob_handle)
 {
 	struct tpm_header *head;
@@ -497,7 +500,8 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 	tpm_buf_init(buf, TPM_BUFSIZE);
 	tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
 
-	rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
+	rc = tpm_buf_append_name(chip, buf, options->keyhandle, parent_name,
+				 parent_name_size);
 	if (rc)
 		return rc;
 
@@ -567,30 +571,60 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 }
 
 /**
- * tpm2_unseal_trusted() - unseal the payload of a trusted key
+ * tpm2_unseal_trusted() - Unseal a trusted key
+ * @chip:	TPM chip to use.
+ * @payload:	Key data in clear text.
+ * @options:	Trusted key options.
  *
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- *
- * Return: Same as with tpm_send.
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Return -EINVAL when parent's key handle has not been set.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load or TPM2_Unseal
+ * fails.
  */
 int tpm2_unseal_trusted(struct tpm_chip *chip,
 			struct trusted_key_payload *payload,
 			struct trusted_key_options *options)
 {
+	u8 *blob_ref __free(kfree) = NULL;
+	u8 parent_name[2 + SHA512_DIGEST_SIZE];
+	u16 parent_name_size;
 	u32 blob_handle;
+	u8 *blob;
 	int rc;
 
+	/*
+	 * Try to decode the provided blob as an ASN.1 blob. Assume that the
+	 * blob is in the legacy format if decoding does not end successfully.
+	 */
+	rc = tpm2_key_decode(payload, options, &blob);
+	if (rc) {
+		blob = payload->blob;
+		payload->old_format = 1;
+	} else {
+		blob_ref = blob;
+	}
+
+	if (!options->keyhandle)
+		return -EINVAL;
+
 	rc = tpm_try_get_ops(chip);
 	if (rc)
 		return rc;
 
-	rc = tpm2_load_cmd(chip, payload, options, &blob_handle);
+	rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+	if (rc < 0)
+		goto out;
+
+	parent_name_size = rc;
+
+	rc = tpm2_load_cmd(chip, payload, options, parent_name,
+			   parent_name_size, blob, &blob_handle);
 	if (rc)
 		goto out;
 
-	rc = tpm2_unseal_cmd(chip, payload, options, blob_handle);
+	rc = tpm2_unseal_cmd(chip, payload, options, parent_name,
+			     parent_name_size, blob_handle);
+
 	tpm2_flush_context(chip, blob_handle);
 
 out:
-- 
2.39.5


^ permalink raw reply related

* [PATCH v6 2/2] KEYS: trusted: Store parent's name to the encoded keys
From: Jarkko Sakkinen @ 2025-12-07 17:32 UTC (permalink / raw)
  To: linux-integrity
  Cc: tpm2, Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe,
	James Bottomley, Mimi Zohar, David Howells, Paul Moore,
	James Morris, Serge E. Hallyn, open list, open list:KEYS-TRUSTED,
	open list:SECURITY SUBSYSTEM
In-Reply-To: <20251207173210.93765-1-jarkko@kernel.org>

Extend TPMKey ASN.1 speciication [1] with an optional 'parentName'
attribute containing TPM name of the parent key (in other words, TPMT_HA
blob).

The life-cycle for trusted keys will now proceed as follows:

1. Encode parent's name to the 'paretName' during tpm2_key_encode().
2. During tpm2_unseal_trusted, read parent's name from 'parentName'. When
   the attribute is not available, fallback on doing tpm2_read_public().

In other words, in the common (i.e., not loading a legacy key blob),
tpm2_read_public() will now only happen at the time when a key is first
created.

In addition, move tpm2_read_public() to 'tpm2-cmd.c' and make its body
unconditional so that the binary format of the saved keys is not dependent
on kernel configuration.

[1] https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.txt

Cc: tpm2@lists.linux.dev
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v6:
- Addressed the issues reported by James.

  Updated tag index to '7', as '6' is reserved by "creation data proposal"
  to which I could not find a reference but presumably exists:

  https://lore.kernel.org/linux-integrity/89d90617ba9b7a5eff1d5fad6bb9773033d3c18c.camel@HansenPartnership.com/#t

  Once I become aware of the mailing list of IETF WG for TPM2 ASN.1 key
  spec, so could not cc to patch over there buth at least the conflict has
  been addressed. I can add the list ass CC one I know th address.
v5:
- Rebased to the mainline.
v4:
- No changes.
v3:
- Fixed three low-hanging fruit issues I found myself.
v2:
- A new patch.
---
 drivers/char/tpm/tpm2-cmd.c               | 83 ++++++++++++++++++++
 drivers/char/tpm/tpm2-sessions.c          | 85 ---------------------
 include/linux/tpm.h                       |  9 +--
 security/keys/trusted-keys/tpm2key.asn1   | 17 ++++-
 security/keys/trusted-keys/trusted_tpm2.c | 93 ++++++++++++++++-------
 5 files changed, 166 insertions(+), 121 deletions(-)

diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 5b04e74b6377..83a70680cfe9 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -757,3 +757,86 @@ int tpm2_find_cc(struct tpm_chip *chip, u32 cc)
 
 	return -1;
 }
+
+/*
+ * Name Size based on TPM algorithm (assumes no hash bigger than 255)
+ */
+static int name_size(const u8 *name)
+{
+	u16 hash_alg = get_unaligned_be16(name);
+
+	switch (hash_alg) {
+	case TPM_ALG_SHA1:
+		return SHA1_DIGEST_SIZE + 2;
+	case TPM_ALG_SHA256:
+		return SHA256_DIGEST_SIZE + 2;
+	case TPM_ALG_SHA384:
+		return SHA384_DIGEST_SIZE + 2;
+	case TPM_ALG_SHA512:
+		return SHA512_DIGEST_SIZE + 2;
+	default:
+		pr_warn("tpm: unsupported name algorithm: 0x%04x\n", hash_alg);
+		return -EINVAL;
+	}
+}
+
+/**
+ * tpm2_read_public: Resolve TPM name for a handle
+ * @chip:		TPM chip to use.
+ * @handle:		TPM handle.
+ * @name:		A buffer for returning the name blob. Must have a
+ *			capacity of 'SHA512_DIGET_SIZE + 2' bytes at minimum
+ *
+ * Returns size of TPM handle name of success.
+ * Returns tpm_transmit_cmd error codes when TPM2_ReadPublic fails.
+ */
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
+{
+	u32 mso = tpm2_handle_mso(handle);
+	off_t offset = TPM_HEADER_SIZE;
+	int rc, name_size_alg;
+
+	if (mso != TPM2_MSO_PERSISTENT && mso != TPM2_MSO_VOLATILE &&
+	    mso != TPM2_MSO_NVRAM) {
+		memcpy(name, &handle, sizeof(u32));
+		return sizeof(u32);
+	}
+
+	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC);
+	tpm_buf_append_u32(buf, handle);
+
+	rc = tpm_transmit_cmd(chip, buf, 0, "TPM2_ReadPublic");
+	if (rc)
+		return tpm_ret_to_err(rc);
+
+	/* Skip TPMT_PUBLIC: */
+	offset += tpm_buf_read_u16(buf, &offset);
+
+	/*
+	 * Ensure space for the length field of TPM2B_NAME and hashAlg field of
+	 * TPMT_HA (the extra four bytes).
+	 */
+	if (offset + 4 > tpm_buf_length(buf))
+		return -EIO;
+
+	rc = tpm_buf_read_u16(buf, &offset);
+	name_size_alg = name_size(&buf->data[offset]);
+
+	if (name_size_alg < 0)
+		return name_size_alg;
+
+	if (rc != name_size_alg)
+		return -EIO;
+
+	if (offset + rc > tpm_buf_length(buf))
+		return -EIO;
+
+	memcpy(name, &buf->data[offset], rc);
+	return name_size_alg;
+}
+EXPORT_SYMBOL_GPL(tpm2_read_public);
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 6b7112a7c30c..daf78c2e905f 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -140,91 +140,6 @@ struct tpm2_auth {
 	u16 name_size_tbl[AUTH_MAX_NAMES];
 };
 
-#ifdef CONFIG_TCG_TPM2_HMAC
-/*
- * Name Size based on TPM algorithm (assumes no hash bigger than 255)
- */
-static int name_size(const u8 *name)
-{
-	u16 hash_alg = get_unaligned_be16(name);
-
-	switch (hash_alg) {
-	case TPM_ALG_SHA1:
-		return SHA1_DIGEST_SIZE + 2;
-	case TPM_ALG_SHA256:
-		return SHA256_DIGEST_SIZE + 2;
-	case TPM_ALG_SHA384:
-		return SHA384_DIGEST_SIZE + 2;
-	case TPM_ALG_SHA512:
-		return SHA512_DIGEST_SIZE + 2;
-	default:
-		pr_warn("tpm: unsupported name algorithm: 0x%04x\n", hash_alg);
-		return -EINVAL;
-	}
-}
-
-/**
- * tpm2_read_public: Resolve TPM name for a handle
- * @chip:		TPM chip to use.
- * @handle:		TPM handle.
- * @name:		A buffer for returning the name blob. Must have a
- *			capacity of 'SHA512_DIGET_SIZE + 2' bytes at minimum
- *
- * Returns size of TPM handle name of success.
- * Returns tpm_transmit_cmd error codes when TPM2_ReadPublic fails.
- */
-int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
-{
-	u32 mso = tpm2_handle_mso(handle);
-	off_t offset = TPM_HEADER_SIZE;
-	int rc, name_size_alg;
-
-	if (mso != TPM2_MSO_PERSISTENT && mso != TPM2_MSO_VOLATILE &&
-	    mso != TPM2_MSO_NVRAM) {
-		memcpy(name, &handle, sizeof(u32));
-		return sizeof(u32);
-	}
-
-	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
-
-	tpm_buf_init(buf, TPM_BUFSIZE);
-	tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC);
-	tpm_buf_append_u32(buf, handle);
-
-	rc = tpm_transmit_cmd(chip, buf, 0, "TPM2_ReadPublic");
-	if (rc)
-		return tpm_ret_to_err(rc);
-
-	/* Skip TPMT_PUBLIC: */
-	offset += tpm_buf_read_u16(buf, &offset);
-
-	/*
-	 * Ensure space for the length field of TPM2B_NAME and hashAlg field of
-	 * TPMT_HA (the extra four bytes).
-	 */
-	if (offset + 4 > tpm_buf_length(buf))
-		return -EIO;
-
-	rc = tpm_buf_read_u16(buf, &offset);
-	name_size_alg = name_size(&buf->data[offset]);
-
-	if (name_size_alg < 0)
-		return name_size_alg;
-
-	if (rc != name_size_alg)
-		return -EIO;
-
-	if (offset + rc > tpm_buf_length(buf))
-		return -EIO;
-
-	memcpy(name, &buf->data[offset], rc);
-	return name_size_alg;
-}
-EXPORT_SYMBOL_GPL(tpm2_read_public);
-#endif /* CONFIG_TCG_TPM2_HMAC */
-
 /**
  * tpm_buf_append_name() - Append a handle and store TPM name
  * @chip:		TPM chip to use.
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 3f1d8c27394b..40d1ede005b0 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -478,6 +478,7 @@ 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);
 int tpm2_find_hash_alg(unsigned int crypto_id);
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name);
 
 static inline void tpm_buf_append_empty_auth(struct tpm_buf *buf, u32 handle)
 {
@@ -544,7 +545,6 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
 int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
 				int rc);
 void tpm2_end_auth_session(struct tpm_chip *chip);
-int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name);
 #else
 #include <linux/unaligned.h>
 
@@ -568,13 +568,6 @@ static inline int tpm_buf_check_hmac_response(struct tpm_chip *chip,
 {
 	return rc;
 }
-
-static inline int tpm2_read_public(struct tpm_chip *chip, u32 handle,
-				   void *name)
-{
-	memcpy(name, &handle, sizeof(u32));
-	return sizeof(u32);
-}
 #endif	/* CONFIG_TCG_TPM2_HMAC */
 
 #endif
diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trusted-keys/tpm2key.asn1
index f57f869ad600..c48ea5a14219 100644
--- a/security/keys/trusted-keys/tpm2key.asn1
+++ b/security/keys/trusted-keys/tpm2key.asn1
@@ -1,11 +1,26 @@
 ---
 --- ASN.1 for TPM 2.0 keys
 ---
+TPMPolicy ::= SEQUENCE {
+	commandCode	[0] EXPLICIT INTEGER,
+	commandPolicy	[1] EXPLICIT OCTET STRING
+}
+
+TPMAuthPolicy ::= SEQUENCE {
+	name		[0] EXPLICIT UTF8String OPTIONAL,
+	policy		[1] EXPLICIT SEQUENCE OF TPMPolicy
+}
 
 TPMKey ::= SEQUENCE {
 	type		OBJECT IDENTIFIER ({tpm2_key_type}),
 	emptyAuth	[0] EXPLICIT BOOLEAN OPTIONAL,
+	policy		[1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL,
+	secret		[2] EXPLICIT OCTET STRING OPTIONAL,
+	authPolicy	[3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL,
+	description	[4] EXPLICIT UTF8String OPTIONAL,
+	rsaParent	[5] EXPLICIT BOOLEAN OPTIONAL,
+	parentName	[7] EXPLICIT OCTET STRING OPTIONAL ({tpm2_key_parent_name}),
 	parent		INTEGER ({tpm2_key_parent}),
 	pubkey		OCTET STRING ({tpm2_key_pub}),
 	privkey		OCTET STRING ({tpm2_key_priv})
-	}
+}
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 0feb96cab62d..6ceb8cff3830 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -20,16 +20,26 @@
 
 static u32 tpm2key_oid[] = { 2, 23, 133, 10, 1, 5 };
 
+enum tpm_key_tag {
+	TPM_KEY_TAG_EMPTY_AUTH	= 0,
+	TPM_KEY_TAG_POLICY	= 1,
+	TPM_KEY_TAG_SECRET	= 2,
+	TPM_KEY_TAG_AUTH_POLICY	= 3,
+	TPM_KEY_TAG_DESCRIPTION = 4,
+	TPM_KEY_TAG_RSA_PARENT	= 5,
+	TPM_KEY_TAG_PARENT_NAME	= 7,
+};
+
 static int tpm2_key_encode(struct trusted_key_payload *payload,
 			   struct trusted_key_options *options,
-			   u8 *src, u32 len)
+			   u8 *src, u32 len, u8 *parent_name,
+			   u16 parent_name_size)
 {
 	const int SCRATCH_SIZE = PAGE_SIZE;
-	u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
-	u8 *work = scratch, *work1;
-	u8 *end_work = scratch + SCRATCH_SIZE;
-	u8 *priv, *pub;
+	u8 *end_work, *name_end;
 	u16 priv_len, pub_len;
+	u8 *work, *work1;
+	u8 *priv, *pub;
 	int ret;
 
 	priv_len = get_unaligned_be16(src) + 2;
@@ -40,23 +50,41 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
 	pub_len = get_unaligned_be16(src) + 2;
 	pub = src;
 
+	u8 *scratch __free(kfree) = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
 	if (!scratch)
 		return -ENOMEM;
 
+	work = scratch;
+	end_work = scratch + SCRATCH_SIZE;
+
 	work = asn1_encode_oid(work, end_work, tpm2key_oid,
 			       asn1_oid_len(tpm2key_oid));
 
 	if (options->blobauth_len == 0) {
-		unsigned char bool[3], *w = bool;
-		/* tag 0 is emptyAuth */
-		w = asn1_encode_boolean(w, w + sizeof(bool), true);
-		if (WARN(IS_ERR(w), "BUG: Boolean failed to encode")) {
-			ret = PTR_ERR(w);
-			goto err;
+		u8 *bool_end;
+		u8 bool[3];
+
+		bool_end = asn1_encode_boolean(&bool[0], &bool[sizeof(bool)],
+					       true);
+		if (IS_ERR(bool_end)) {
+			pr_err("BUG: Boolean failed to encode\n");
+			return PTR_ERR(bool_end);
 		}
-		work = asn1_encode_tag(work, end_work, 0, bool, w - bool);
+
+		work = asn1_encode_tag(work, end_work, TPM_KEY_TAG_EMPTY_AUTH,
+				       bool, bool_end - bool);
 	}
 
+	u8 *name_encoded __free(kfree) = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
+	if (!name_encoded)
+		return -ENOMEM;
+
+	name_end = asn1_encode_octet_string(&name_encoded[0],
+					    &name_encoded[SCRATCH_SIZE],
+					    parent_name, parent_name_size);
+	work = asn1_encode_tag(work, end_work, TPM_KEY_TAG_PARENT_NAME,
+			       name_encoded, name_end - name_encoded);
+
 	/*
 	 * Assume both octet strings will encode to a 2 byte definite length
 	 *
@@ -65,8 +93,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
 	 */
 	if (WARN(work - scratch + pub_len + priv_len + 14 > SCRATCH_SIZE,
 		 "BUG: scratch buffer is too small")) {
-		ret = -EINVAL;
-		goto err;
+		return -EINVAL;
 	}
 
 	work = asn1_encode_integer(work, end_work, options->keyhandle);
@@ -79,15 +106,10 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
 	if (IS_ERR(work1)) {
 		ret = PTR_ERR(work1);
 		pr_err("BUG: ASN.1 encoder failed with %d\n", ret);
-		goto err;
+		return ret;
 	}
 
-	kfree(scratch);
 	return work1 - payload->blob;
-
-err:
-	kfree(scratch);
-	return ret;
 }
 
 struct tpm2_key_context {
@@ -96,11 +118,13 @@ struct tpm2_key_context {
 	u32 pub_len;
 	const u8 *priv;
 	u32 priv_len;
+	const u8 *name;
+	u32 name_len;
 };
 
 static int tpm2_key_decode(struct trusted_key_payload *payload,
 			   struct trusted_key_options *options,
-			   u8 **buf)
+			   u8 **buf, u8 *parent_name, u16 *parent_name_size)
 {
 	int ret;
 	struct tpm2_key_context ctx;
@@ -127,6 +151,8 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
 	blob += ctx.priv_len;
 
 	memcpy(blob, ctx.pub, ctx.pub_len);
+	memcpy(parent_name, ctx.name, ctx.name_len);
+	*parent_name_size = ctx.name_len;
 
 	return 0;
 }
@@ -190,6 +216,16 @@ int tpm2_key_priv(void *context, size_t hdrlen,
 	return 0;
 }
 
+int tpm2_key_parent_name(void *context, size_t hdrlen, unsigned char tag,
+			 const void *value, size_t vlen)
+{
+	struct tpm2_key_context *ctx = context;
+
+	ctx->name = value;
+	ctx->name_len = vlen;
+
+	return 0;
+}
 /**
  * tpm2_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
  *
@@ -351,7 +387,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		goto out;
 	}
 
-	blob_len = tpm2_key_encode(payload, options, &buf->data[offset], blob_len);
+	blob_len = tpm2_key_encode(payload, options, &buf->data[offset],
+				   blob_len, parent_name, parent_name_size);
 	if (blob_len < 0)
 		rc = blob_len;
 
@@ -596,7 +633,7 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
 	 * Try to decode the provided blob as an ASN.1 blob. Assume that the
 	 * blob is in the legacy format if decoding does not end successfully.
 	 */
-	rc = tpm2_key_decode(payload, options, &blob);
+	rc = tpm2_key_decode(payload, options, &blob, &parent_name[0], &parent_name_size);
 	if (rc) {
 		blob = payload->blob;
 		payload->old_format = 1;
@@ -611,11 +648,13 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
 	if (rc)
 		return rc;
 
-	rc = tpm2_read_public(chip, options->keyhandle, parent_name);
-	if (rc < 0)
-		goto out;
+	if (!parent_name_size) {
+		rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+		if (rc < 0)
+			goto out;
 
-	parent_name_size = rc;
+		parent_name_size = rc;
+	}
 
 	rc = tpm2_load_cmd(chip, payload, options, parent_name,
 			   parent_name_size, blob, &blob_handle);
-- 
2.39.5


^ permalink raw reply related

* Re: [PATCH v6 2/2] KEYS: trusted: Store parent's name to the encoded keys
From: Jarkko Sakkinen @ 2025-12-07 17:57 UTC (permalink / raw)
  To: linux-integrity
  Cc: tpm2, Peter Huewe, Jason Gunthorpe, James Bottomley, Mimi Zohar,
	David Howells, Paul Moore, James Morris, Serge E. Hallyn,
	open list, open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM
In-Reply-To: <20251207173210.93765-3-jarkko@kernel.org>

On Sun, Dec 07, 2025 at 07:32:10PM +0200, Jarkko Sakkinen wrote:
> Extend TPMKey ASN.1 speciication [1] with an optional 'parentName'
> attribute containing TPM name of the parent key (in other words, TPMT_HA
> blob).
> 
> The life-cycle for trusted keys will now proceed as follows:
> 
> 1. Encode parent's name to the 'paretName' during tpm2_key_encode().
> 2. During tpm2_unseal_trusted, read parent's name from 'parentName'. When
>    the attribute is not available, fallback on doing tpm2_read_public().
> 
> In other words, in the common (i.e., not loading a legacy key blob),
> tpm2_read_public() will now only happen at the time when a key is first
> created.
> 
> In addition, move tpm2_read_public() to 'tpm2-cmd.c' and make its body
> unconditional so that the binary format of the saved keys is not dependent
> on kernel configuration.
> 
> [1] https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.txt
> 
> Cc: tpm2@lists.linux.dev
> Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>

As an alternative workaround I think the following could be possibly
done (I need to trial it first though):

1. Maintain a cache where a name gets added at the time of
   tpm2_seal_trusted(). It is from TPMT_HA to TPMT_HA mapping,
   mapping TPMT_HA of the key to the TPMT of the parent.
2. At thet time tpm2_unseal_trusted() retrieve name of thet
   parent from the cache.

Capturing TPM2_CreatePrimary would be essentially duct taping the
spec but I guess this could be more generally applicable. It neither 
addresses persistent keys nor secondary parent keys, which we *have
to support*, as the kernel interface does.

BR, Jarkko




^ permalink raw reply

* Static values for TPM 1.2 command durations
From: Jarkko Sakkinen @ 2025-12-07 18:17 UTC (permalink / raw)
  To: linux-integrity; +Cc: tpm2

Hi,

In order to translate some of the code (or data in this case) to
Trenchboot's early boot code it needs to know have some idea of
command durations.

In the case of TPM 2.0 we have a table for this, which can be
linked easily to early boot code. 

Can be come by reasonable values for TPM 1.2 command durations
that would scale to the pre-existing hardware?

BR, Jarkko

^ permalink raw reply

* Re: [PATCH v6 2/2] KEYS: trusted: Store parent's name to the encoded keys
From: Jarkko Sakkinen @ 2025-12-07 20:07 UTC (permalink / raw)
  To: linux-integrity
  Cc: tpm2, Peter Huewe, Jason Gunthorpe, James Bottomley, Mimi Zohar,
	David Howells, Paul Moore, James Morris, Serge E. Hallyn,
	open list, open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM
In-Reply-To: <aTW_-fv8B6q6TAMx@kernel.org>

On Sun, Dec 07, 2025 at 07:57:13PM +0200, Jarkko Sakkinen wrote:
> On Sun, Dec 07, 2025 at 07:32:10PM +0200, Jarkko Sakkinen wrote:
> > Extend TPMKey ASN.1 speciication [1] with an optional 'parentName'
> > attribute containing TPM name of the parent key (in other words, TPMT_HA
> > blob).
> > 
> > The life-cycle for trusted keys will now proceed as follows:
> > 
> > 1. Encode parent's name to the 'paretName' during tpm2_key_encode().
> > 2. During tpm2_unseal_trusted, read parent's name from 'parentName'. When
> >    the attribute is not available, fallback on doing tpm2_read_public().
> > 
> > In other words, in the common (i.e., not loading a legacy key blob),
> > tpm2_read_public() will now only happen at the time when a key is first
> > created.
> > 
> > In addition, move tpm2_read_public() to 'tpm2-cmd.c' and make its body
> > unconditional so that the binary format of the saved keys is not dependent
> > on kernel configuration.
> > 
> > [1] https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.txt
> > 
> > Cc: tpm2@lists.linux.dev
> > Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> 
> As an alternative workaround I think the following could be possibly
> done (I need to trial it first though):
> 
> 1. Maintain a cache where a name gets added at the time of
>    tpm2_seal_trusted(). It is from TPMT_HA to TPMT_HA mapping,
>    mapping TPMT_HA of the key to the TPMT of the parent.
> 2. At thet time tpm2_unseal_trusted() retrieve name of thet
>    parent from the cache.
> 
> Capturing TPM2_CreatePrimary would be essentially duct taping the
> spec but I guess this could be more generally applicable. It neither 
> addresses persistent keys nor secondary parent keys, which we *have
> to support*, as the kernel interface does.

I think it is better to focus on merging the first patch and continue
this in the working groups mailing list first before implementing
complex quirks to address flaws of the file format.

BR, Jarkko

^ permalink raw reply

* Re: [PATCH v6 2/2] KEYS: trusted: Store parent's name to the encoded keys
From: Jarkko Sakkinen @ 2025-12-07 21:06 UTC (permalink / raw)
  To: linux-integrity
  Cc: tpm2, Peter Huewe, Jason Gunthorpe, James Bottomley, Mimi Zohar,
	David Howells, Paul Moore, James Morris, Serge E. Hallyn,
	open list, open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM
In-Reply-To: <aTXelmqQ_y7zX-KZ@kernel.org>

On Sun, Dec 07, 2025 at 10:07:55PM +0200, Jarkko Sakkinen wrote:
> On Sun, Dec 07, 2025 at 07:57:13PM +0200, Jarkko Sakkinen wrote:
> > On Sun, Dec 07, 2025 at 07:32:10PM +0200, Jarkko Sakkinen wrote:
> > > Extend TPMKey ASN.1 speciication [1] with an optional 'parentName'
> > > attribute containing TPM name of the parent key (in other words, TPMT_HA
> > > blob).
> > > 
> > > The life-cycle for trusted keys will now proceed as follows:
> > > 
> > > 1. Encode parent's name to the 'paretName' during tpm2_key_encode().
> > > 2. During tpm2_unseal_trusted, read parent's name from 'parentName'. When
> > >    the attribute is not available, fallback on doing tpm2_read_public().
> > > 
> > > In other words, in the common (i.e., not loading a legacy key blob),
> > > tpm2_read_public() will now only happen at the time when a key is first
> > > created.
> > > 
> > > In addition, move tpm2_read_public() to 'tpm2-cmd.c' and make its body
> > > unconditional so that the binary format of the saved keys is not dependent
> > > on kernel configuration.
> > > 
> > > [1] https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.txt
> > > 
> > > Cc: tpm2@lists.linux.dev
> > > Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> > 
> > As an alternative workaround I think the following could be possibly
> > done (I need to trial it first though):
> > 
> > 1. Maintain a cache where a name gets added at the time of
> >    tpm2_seal_trusted(). It is from TPMT_HA to TPMT_HA mapping,
> >    mapping TPMT_HA of the key to the TPMT of the parent.
> > 2. At thet time tpm2_unseal_trusted() retrieve name of thet
> >    parent from the cache.
> > 
> > Capturing TPM2_CreatePrimary would be essentially duct taping the
> > spec but I guess this could be more generally applicable. It neither 
> > addresses persistent keys nor secondary parent keys, which we *have
> > to support*, as the kernel interface does.
> 
> I think it is better to focus on merging the first patch and continue
> this in the working groups mailing list first before implementing
> complex quirks to address flaws of the file format.

Storing the name of the parent to the key file has also some other
advantages such as enabling parent auto discovery, which is especially
useful with transient keys. Parent handle is not useful data as by
definition it has an ambiguous meaning.

It is neither "insecure" as child key is tied to one single parent but
at the same parent's handle is completely useless and irrelevant data.

There's nothing "niche" IMHO on storing parent's name at least, and
it is very limiting not to have it.

BR, Jarkko

^ permalink raw reply

* [PATCH v7 0/3] Optimize tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-08  5:06 UTC (permalink / raw)
  To: linux-integrity
  Cc: tpm2, Jarkko Sakkinen, James Bottomley, Mimi Zohar, David Howells,
	Paul Moore, James Morris, Serge E. Hallyn, open list:KEYS-TRUSTED,
	open list:SECURITY SUBSYSTEM, open list

The main goal is fairly straight-forwrd here.

The aim of these patches is optimize the number of tpm2_read_public() calls
to the bare minimum. 

## About dropping 'parentName' attribute for ASN.1 keys from the patch set

I wrote this section as a remainder as I have facts fresh in my mind so 
that I can return them as soon as there is working group for the ASN.1 
specification. We really need to have this in the spec.

I dropped [1] given that [2] is landing shortly to IETF draft process,
according to James Bottomley [3]. We will return to [1] as soon as draft
process is open for comments. Still, that attribute is super important,
and here is why.

This will cause a overhead as tpm2_unseal_trusted needs to do an
unnecessary (from pure technical perspective) TPM2_ReadPublic command to
acquire TPM name of the parent. This is obviously known at the time of
creation of a key but the information is not stored anywhere by the
key format.

It also aligns badly with TCG specifications as Table 6 of architecture
spec explicitly defines a reference (or name) for transient keys,
persistent keys and NV indexes to be TPM_ALG_ID concatenated together
with the hash of TPMT_PUBLIC. I.e. the file format is using exactly
the opposite what should be use as reference for keys than what it 
should use.

Other benefits are of course auto-discovery of parent for a key file,
which is nasty to do without the name pre-stored.

[1] https://lore.kernel.org/linux-integrity/20251207173210.93765-3-jarkko@kernel.org/
[2] https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.txt
[3] https://lore.kernel.org/linux-integrity/89d90617ba9b7a5eff1d5fad6bb9773033d3c18c.camel@HansenPartnership.com/

Jarkko Sakkinen (3):
  tpm2-sessions: Define TPM2_NAME_MAX_SIZE
  KEYS: trusted: Re-orchestrate tpm2_read_public() calls
  tpm2-sessions: Remove AUTH_MAX_NAMES

 drivers/char/tpm/tpm2-cmd.c               |   3 +-
 drivers/char/tpm/tpm2-sessions.c          | 116 ++++++--------------
 include/linux/tpm.h                       |  19 +++-
 security/keys/trusted-keys/trusted_tpm2.c | 124 ++++++++++++++--------
 4 files changed, 133 insertions(+), 129 deletions(-)

-- 
2.39.5


^ permalink raw reply

* [PATCH v7 1/3] tpm2-sessions: Define TPM2_NAME_MAX_SIZE
From: Jarkko Sakkinen @ 2025-12-08  5:06 UTC (permalink / raw)
  To: linux-integrity
  Cc: tpm2, Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe,
	James Bottomley, Mimi Zohar, David Howells, Paul Moore,
	James Morris, Serge E. Hallyn, open list, open list:KEYS-TRUSTED,
	open list:SECURITY SUBSYSTEM
In-Reply-To: <20251208050620.339408-1-jarkko@kernel.org>

This is somewhat cosmetic change but it does serve a purpose on tracking
the value set for the maximum length of TPM names, and to clearly states
what components it is composed of. It also anchors the value so that when
buffers are declared for this particular purpose, the same value is used
for the capacity.

Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v7:
- A new patch.
---
 drivers/char/tpm/tpm2-sessions.c | 2 +-
 include/linux/tpm.h              | 9 +++++++++
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 79f27a46bd7f..a0c88fb1804c 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -137,7 +137,7 @@ struct tpm2_auth {
 	 * we must compute and remember
 	 */
 	u32 name_h[AUTH_MAX_NAMES];
-	u8 name[AUTH_MAX_NAMES][2 + SHA512_DIGEST_SIZE];
+	u8 name[AUTH_MAX_NAMES][TPM2_MAX_NAME_SIZE];
 };
 
 #ifdef CONFIG_TCG_TPM2_HMAC
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 42e2a091f43d..b4b5bd586501 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -28,7 +28,16 @@
 #define TPM_DIGEST_SIZE		20	/* Max TPM v1.2 PCR size */
 #define TPM_BUFSIZE		4096
 
+/*
+ * SHA-512 is, as of today, the largest digest in the TCG algorithm repository.
+ */
 #define TPM2_MAX_DIGEST_SIZE	SHA512_DIGEST_SIZE
+
+/*
+ * A TPM name digest i.e., TPMT_HA, is a concatenation of TPM_ALG_ID of the
+ * name algorithm and hash of TPMT_PUBLIC.
+ */
+#define TPM2_MAX_NAME_SIZE	(TPM2_MAX_DIGEST_SIZE + 2)
 #define TPM2_MAX_PCR_BANKS	8
 
 struct tpm_chip;
-- 
2.39.5


^ permalink raw reply related

* [PATCH v7 2/3] KEYS: trusted: Re-orchestrate tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-08  5:06 UTC (permalink / raw)
  To: linux-integrity
  Cc: tpm2, Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe,
	James Bottomley, Mimi Zohar, David Howells, Paul Moore,
	James Morris, Serge E. Hallyn, open list, open list:KEYS-TRUSTED,
	open list:SECURITY SUBSYSTEM
In-Reply-To: <20251208050620.339408-1-jarkko@kernel.org>

tpm2_load_cmd() and tpm2_unseal_cmd() use the same parent, and calls to
tpm_buf_append_name() cause the exact same TPM2_ReadPublic command to be
sent to the chip, causing unnecessary traffic.

1. Export tpm2_read_public in order to make it callable from
   'trusted_tpm2'.
2. Re-orchestrate tpm2_seal_trusted() and tpm2_unseal_trusted() in order to
   halve the name resolutions required:
2a. Move tpm2_read_public() calls into trusted_tpm2.
2b. Pass TPM name to tpm_buf_append_name().
2c. Rework tpm_buf_append_name() to use the pre-resolved name.

Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v7:
- No changes of any of the versions so far.
---
 drivers/char/tpm/tpm2-cmd.c               |   3 +-
 drivers/char/tpm/tpm2-sessions.c          |  95 +++++------------
 include/linux/tpm.h                       |  10 +-
 security/keys/trusted-keys/trusted_tpm2.c | 124 ++++++++++++++--------
 4 files changed, 118 insertions(+), 114 deletions(-)

diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 2682f5ec09cb..5b04e74b6377 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -199,7 +199,8 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
 	tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
 
 	if (!disable_pcr_integrity) {
-		rc = tpm_buf_append_name(chip, buf, pcr_idx, NULL);
+		rc = tpm_buf_append_name(chip, buf, pcr_idx, (u8 *)&pcr_idx,
+					 sizeof(u32));
 		if (rc)
 			return rc;
 		tpm_buf_append_hmac_session(chip, buf, 0, NULL, 0);
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index a0c88fb1804c..0816a91134fc 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -136,8 +136,8 @@ struct tpm2_auth {
 	 * handle, but they are part of the session by name, which
 	 * we must compute and remember
 	 */
-	u32 name_h[AUTH_MAX_NAMES];
 	u8 name[AUTH_MAX_NAMES][TPM2_MAX_NAME_SIZE];
+	u16 name_size_tbl[AUTH_MAX_NAMES];
 };
 
 #ifdef CONFIG_TCG_TPM2_HMAC
@@ -163,7 +163,17 @@ static int name_size(const u8 *name)
 	}
 }
 
-static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
+/**
+ * tpm2_read_public: Resolve TPM name for a handle
+ * @chip:		TPM chip to use.
+ * @handle:		TPM handle.
+ * @name:		A buffer for returning the name blob. Must have a
+ *			capacity of 'SHA512_DIGET_SIZE + 2' bytes at minimum
+ *
+ * Returns size of TPM handle name of success.
+ * Returns tpm_transmit_cmd error codes when TPM2_ReadPublic fails.
+ */
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
 {
 	u32 mso = tpm2_handle_mso(handle);
 	off_t offset = TPM_HEADER_SIZE;
@@ -212,14 +222,16 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
 	memcpy(name, &buf->data[offset], rc);
 	return name_size_alg;
 }
+EXPORT_SYMBOL_GPL(tpm2_read_public);
 #endif /* CONFIG_TCG_TPM2_HMAC */
 
 /**
- * tpm_buf_append_name() - add a handle area to the buffer
- * @chip: the TPM chip structure
- * @buf: The buffer to be appended
- * @handle: The handle to be appended
- * @name: The name of the handle (may be NULL)
+ * tpm_buf_append_name() - Append a handle and store TPM name
+ * @chip:		TPM chip to use.
+ * @buf:		TPM buffer containing the TPM command in-transit.
+ * @handle:		TPM handle to be appended.
+ * @name:		TPM name of the handle
+ * @name_size:		Size of the TPM name.
  *
  * In order to compute session HMACs, we need to know the names of the
  * objects pointed to by the handles.  For most objects, this is simply
@@ -236,15 +248,14 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
  * will be caused by an incorrect programming model and indicated by a
  * kernel message.
  *
- * Ends the authorization session on failure.
+ * Returns zero on success.
+ * Returns -EIO when the authorization area state is malformed.
  */
 int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
-			u32 handle, u8 *name)
+			u32 handle, u8 *name, u16 name_size)
 {
 #ifdef CONFIG_TCG_TPM2_HMAC
-	enum tpm2_mso_type mso = tpm2_handle_mso(handle);
 	struct tpm2_auth *auth;
-	u16 name_size_alg;
 	int slot;
 	int ret;
 #endif
@@ -269,36 +280,15 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
 	}
 	tpm_buf_append_u32(buf, handle);
 	auth->session += 4;
-
-	if (mso == TPM2_MSO_PERSISTENT ||
-	    mso == TPM2_MSO_VOLATILE ||
-	    mso == TPM2_MSO_NVRAM) {
-		if (!name) {
-			ret = tpm2_read_public(chip, handle, auth->name[slot]);
-			if (ret < 0)
-				goto err;
-
-			name_size_alg = ret;
-		}
-	} else {
-		if (name) {
-			dev_err(&chip->dev, "handle 0x%08x does not use a name\n",
-				handle);
-			ret = -EIO;
-			goto err;
-		}
-	}
-
-	auth->name_h[slot] = handle;
-	if (name)
-		memcpy(auth->name[slot], name, name_size_alg);
+	memcpy(auth->name[slot], name, name_size);
+	auth->name_size_tbl[slot] = name_size;
 #endif
 	return 0;
 
 #ifdef CONFIG_TCG_TPM2_HMAC
 err:
 	tpm2_end_auth_session(chip);
-	return tpm_ret_to_err(ret);
+	return ret;
 #endif
 }
 EXPORT_SYMBOL_GPL(tpm_buf_append_name);
@@ -606,22 +596,8 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
 	attrs = chip->cc_attrs_tbl[i];
 
 	handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
+	offset_s += handles * sizeof(u32);
 
-	/*
-	 * just check the names, it's easy to make mistakes.  This
-	 * would happen if someone added a handle via
-	 * tpm_buf_append_u32() instead of tpm_buf_append_name()
-	 */
-	for (i = 0; i < handles; i++) {
-		u32 handle = tpm_buf_read_u32(buf, &offset_s);
-
-		if (auth->name_h[i] != handle) {
-			dev_err(&chip->dev, "invalid handle 0x%08x\n", handle);
-			ret = -EIO;
-			goto err;
-		}
-	}
-	/* point offset_s to the start of the sessions */
 	val = tpm_buf_read_u32(buf, &offset_s);
 	/* point offset_p to the start of the parameters */
 	offset_p = offset_s + val;
@@ -682,23 +658,8 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
 	/* ordinal is already BE */
 	sha256_update(&sctx, (u8 *)&head->ordinal, sizeof(head->ordinal));
 	/* add the handle names */
-	for (i = 0; i < handles; i++) {
-		enum tpm2_mso_type mso = tpm2_handle_mso(auth->name_h[i]);
-
-		if (mso == TPM2_MSO_PERSISTENT ||
-		    mso == TPM2_MSO_VOLATILE ||
-		    mso == TPM2_MSO_NVRAM) {
-			ret = name_size(auth->name[i]);
-			if (ret < 0)
-				goto err;
-
-			sha256_update(&sctx, auth->name[i], ret);
-		} else {
-			__be32 h = cpu_to_be32(auth->name_h[i]);
-
-			sha256_update(&sctx, (u8 *)&h, 4);
-		}
-	}
+	for (i = 0; i < handles; i++)
+		sha256_update(&sctx, auth->name[i], auth->name_size_tbl[i]);
 	if (offset_s != tpm_buf_length(buf))
 		sha256_update(&sctx, &buf->data[offset_s],
 			      tpm_buf_length(buf) - offset_s);
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index b4b5bd586501..db716841973f 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -539,7 +539,7 @@ static inline struct tpm2_auth *tpm2_chip_auth(struct tpm_chip *chip)
 }
 
 int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
-			u32 handle, u8 *name);
+			u32 handle, u8 *name, u16 name_size);
 void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
 				 u8 attributes, u8 *passphrase,
 				 int passphraselen);
@@ -553,6 +553,7 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
 int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
 				int rc);
 void tpm2_end_auth_session(struct tpm_chip *chip);
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name);
 #else
 #include <linux/unaligned.h>
 
@@ -576,6 +577,13 @@ static inline int tpm_buf_check_hmac_response(struct tpm_chip *chip,
 {
 	return rc;
 }
+
+static inline int tpm2_read_public(struct tpm_chip *chip, u32 handle,
+				   void *name)
+{
+	memcpy(name, &handle, sizeof(u32));
+	return sizeof(u32);
+}
 #endif	/* CONFIG_TCG_TPM2_HMAC */
 
 #endif
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index fb76c4ea496f..b05808c39d9d 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -233,7 +233,9 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		      struct trusted_key_payload *payload,
 		      struct trusted_key_options *options)
 {
+	u8 parent_name[TPM2_MAX_NAME_SIZE];
 	off_t offset = TPM_HEADER_SIZE;
+	u16 parent_name_size;
 	int blob_len = 0;
 	int hash;
 	u32 flags;
@@ -250,6 +252,12 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 	if (rc)
 		return rc;
 
+	rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+	if (rc < 0)
+		goto out_put;
+
+	parent_name_size = rc;
+
 	rc = tpm2_start_auth_session(chip);
 	if (rc)
 		goto out_put;
@@ -264,7 +272,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 	tpm_buf_init(buf, TPM_BUFSIZE);
 	tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
 
-	rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
+	rc = tpm_buf_append_name(chip, buf, options->keyhandle, parent_name,
+				 parent_name_size);
 	if (rc)
 		goto out_put;
 
@@ -356,48 +365,38 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 }
 
 /**
- * tpm2_load_cmd() - execute a TPM2_Load command
- *
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- * @blob_handle: returned blob handle
+ * tpm2_load_cmd() - Execute TPM2_Load
+ * @chip:		TPM chip to use.
+ * @payload:		Key data in clear text.
+ * @options:		Trusted key options.
+ * @parent_name:	A cryptographic name, i.e. a TPMT_HA blob, of the
+ *			parent key.
+ * @blob:		The decoded payload for the key.
+ * @blob_handle:	On success, will contain handle to the loaded keyedhash
+ *			blob.
  *
- * Return: 0 on success.
- *        -E2BIG on wrong payload size.
- *        -EPERM on tpm error status.
- *        < 0 error from tpm_send.
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load fails.
  */
 static int tpm2_load_cmd(struct tpm_chip *chip,
 			 struct trusted_key_payload *payload,
 			 struct trusted_key_options *options,
+			 u8 *parent_name,
+			 u16 parent_name_size,
+			 const u8 *blob,
 			 u32 *blob_handle)
 {
 	u8 *blob_ref __free(kfree) = NULL;
 	unsigned int private_len;
 	unsigned int public_len;
 	unsigned int blob_len;
-	u8 *blob, *pub;
+	const u8 *pub;
 	int rc;
 	u32 attrs;
 
-	rc = tpm2_key_decode(payload, options, &blob);
-	if (rc) {
-		/* old form */
-		blob = payload->blob;
-		payload->old_format = 1;
-	} else {
-		/* Bind for cleanup: */
-		blob_ref = blob;
-	}
-
-	/* new format carries keyhandle but old format doesn't */
-	if (!options->keyhandle)
-		return -EINVAL;
-
 	/* must be big enough for at least the two be16 size counts */
 	if (payload->blob_len < 4)
-		return -EINVAL;
+		return -E2BIG;
 
 	private_len = get_unaligned_be16(blob);
 
@@ -436,7 +435,8 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 	tpm_buf_init(buf, TPM_BUFSIZE);
 	tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
 
-	rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
+	rc = tpm_buf_append_name(chip, buf, options->keyhandle, parent_name,
+				 parent_name_size);
 	if (rc)
 		return rc;
 
@@ -464,20 +464,23 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 }
 
 /**
- * tpm2_unseal_cmd() - execute a TPM2_Unload command
+ * tpm2_unseal_cmd() - Execute TPM2_Unload
  *
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- * @blob_handle: blob handle
+ * @chip:		TPM chip to use
+ * @payload:		Key data in clear text.
+ * @options:		Trusted key options.
+ * @parent_name:	A cryptographic name, i.e. a TPMT_HA blob, of the
+ *			parent key.
+ * @blob_handle:	Handle to the loaded keyedhash blob.
  *
- * Return: 0 on success
- *         -EPERM on tpm error status
- *         < 0 error from tpm_send
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load fails.
  */
 static int tpm2_unseal_cmd(struct tpm_chip *chip,
 			   struct trusted_key_payload *payload,
 			   struct trusted_key_options *options,
+			   u8 *parent_name,
+			   u16 parent_name_size,
 			   u32 blob_handle)
 {
 	struct tpm_header *head;
@@ -497,7 +500,8 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 	tpm_buf_init(buf, TPM_BUFSIZE);
 	tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
 
-	rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
+	rc = tpm_buf_append_name(chip, buf, options->keyhandle, parent_name,
+				 parent_name_size);
 	if (rc)
 		return rc;
 
@@ -567,30 +571,60 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 }
 
 /**
- * tpm2_unseal_trusted() - unseal the payload of a trusted key
+ * tpm2_unseal_trusted() - Unseal a trusted key
+ * @chip:	TPM chip to use.
+ * @payload:	Key data in clear text.
+ * @options:	Trusted key options.
  *
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- *
- * Return: Same as with tpm_send.
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Return -EINVAL when parent's key handle has not been set.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load or TPM2_Unseal
+ * fails.
  */
 int tpm2_unseal_trusted(struct tpm_chip *chip,
 			struct trusted_key_payload *payload,
 			struct trusted_key_options *options)
 {
+	u8 *blob_ref __free(kfree) = NULL;
+	u8 parent_name[TPM2_MAX_NAME_SIZE];
+	u16 parent_name_size;
 	u32 blob_handle;
+	u8 *blob;
 	int rc;
 
+	/*
+	 * Try to decode the provided blob as an ASN.1 blob. Assume that the
+	 * blob is in the legacy format if decoding does not end successfully.
+	 */
+	rc = tpm2_key_decode(payload, options, &blob);
+	if (rc) {
+		blob = payload->blob;
+		payload->old_format = 1;
+	} else {
+		blob_ref = blob;
+	}
+
+	if (!options->keyhandle)
+		return -EINVAL;
+
 	rc = tpm_try_get_ops(chip);
 	if (rc)
 		return rc;
 
-	rc = tpm2_load_cmd(chip, payload, options, &blob_handle);
+	rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+	if (rc < 0)
+		goto out;
+
+	parent_name_size = rc;
+
+	rc = tpm2_load_cmd(chip, payload, options, parent_name,
+			   parent_name_size, blob, &blob_handle);
 	if (rc)
 		goto out;
 
-	rc = tpm2_unseal_cmd(chip, payload, options, blob_handle);
+	rc = tpm2_unseal_cmd(chip, payload, options, parent_name,
+			     parent_name_size, blob_handle);
+
 	tpm2_flush_context(chip, blob_handle);
 
 out:
-- 
2.39.5


^ permalink raw reply related

* [PATCH v7 3/3] tpm2-sessions: Remove AUTH_MAX_NAMES
From: Jarkko Sakkinen @ 2025-12-08  5:06 UTC (permalink / raw)
  To: linux-integrity
  Cc: tpm2, Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe,
	James Bottomley, Mimi Zohar, David Howells, Paul Moore,
	James Morris, Serge E. Hallyn, open list, open list:KEYS-TRUSTED,
	open list:SECURITY SUBSYSTEM
In-Reply-To: <20251208050620.339408-1-jarkko@kernel.org>

In all of the call sites only one session is ever append. Thus, reduce
AUTH_MAX_NAMES, which leads into removing constant completely.

Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v7:
- A new patch.
---
 drivers/char/tpm/tpm2-sessions.c | 31 +++++++++++--------------------
 1 file changed, 11 insertions(+), 20 deletions(-)

diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 0816a91134fc..62a200ae72d7 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -72,9 +72,6 @@
 #include <crypto/sha2.h>
 #include <crypto/utils.h>
 
-/* maximum number of names the TPM must remember for authorization */
-#define AUTH_MAX_NAMES	3
-
 #define AES_KEY_BYTES	AES_KEYSIZE_128
 #define AES_KEY_BITS	(AES_KEY_BYTES*8)
 
@@ -136,8 +133,8 @@ struct tpm2_auth {
 	 * handle, but they are part of the session by name, which
 	 * we must compute and remember
 	 */
-	u8 name[AUTH_MAX_NAMES][TPM2_MAX_NAME_SIZE];
-	u16 name_size_tbl[AUTH_MAX_NAMES];
+	u8 name[TPM2_MAX_NAME_SIZE];
+	u16 name_size;
 };
 
 #ifdef CONFIG_TCG_TPM2_HMAC
@@ -254,11 +251,14 @@ EXPORT_SYMBOL_GPL(tpm2_read_public);
 int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
 			u32 handle, u8 *name, u16 name_size)
 {
-#ifdef CONFIG_TCG_TPM2_HMAC
 	struct tpm2_auth *auth;
-	int slot;
 	int ret;
-#endif
+
+	if (tpm_buf_length(buf) != TPM_HEADER_SIZE) {
+		dev_err(&chip->dev, "too many handles\n");
+		ret = -EIO;
+		goto err;
+	}
 
 	if (!tpm2_chip_auth(chip)) {
 		tpm_buf_append_handle(buf, handle);
@@ -266,12 +266,6 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
 	}
 
 #ifdef CONFIG_TCG_TPM2_HMAC
-	slot = (tpm_buf_length(buf) - TPM_HEADER_SIZE) / 4;
-	if (slot >= AUTH_MAX_NAMES) {
-		dev_err(&chip->dev, "too many handles\n");
-		ret = -EIO;
-		goto err;
-	}
 	auth = chip->auth;
 	if (auth->session != tpm_buf_length(buf)) {
 		dev_err(&chip->dev, "session state malformed");
@@ -280,16 +274,14 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
 	}
 	tpm_buf_append_u32(buf, handle);
 	auth->session += 4;
-	memcpy(auth->name[slot], name, name_size);
-	auth->name_size_tbl[slot] = name_size;
+	memcpy(auth->name, name, name_size);
+	auth->name_size = name_size;
 #endif
 	return 0;
 
-#ifdef CONFIG_TCG_TPM2_HMAC
 err:
 	tpm2_end_auth_session(chip);
 	return ret;
-#endif
 }
 EXPORT_SYMBOL_GPL(tpm_buf_append_name);
 
@@ -658,8 +650,7 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
 	/* ordinal is already BE */
 	sha256_update(&sctx, (u8 *)&head->ordinal, sizeof(head->ordinal));
 	/* add the handle names */
-	for (i = 0; i < handles; i++)
-		sha256_update(&sctx, auth->name[i], auth->name_size_tbl[i]);
+	sha256_update(&sctx, auth->name, auth->name_size);
 	if (offset_s != tpm_buf_length(buf))
 		sha256_update(&sctx, &buf->data[offset_s],
 			      tpm_buf_length(buf) - offset_s);
-- 
2.39.5


^ permalink raw reply related

* Re: [PATCH v7 0/3] Optimize tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-08  5:15 UTC (permalink / raw)
  To: linux-integrity
  Cc: tpm2, James Bottomley, Mimi Zohar, David Howells, Paul Moore,
	James Morris, Serge E. Hallyn, open list:KEYS-TRUSTED,
	open list:SECURITY SUBSYSTEM, open list
In-Reply-To: <20251208050620.339408-1-jarkko@kernel.org>

On Mon, Dec 08, 2025 at 07:06:16AM +0200, Jarkko Sakkinen wrote:
> The main goal is fairly straight-forwrd here.
> 
> The aim of these patches is optimize the number of tpm2_read_public() calls
> to the bare minimum. 
> 
> ## About dropping 'parentName' attribute for ASN.1 keys from the patch set
> 
> I wrote this section as a remainder as I have facts fresh in my mind so 
> that I can return them as soon as there is working group for the ASN.1 
> specification. We really need to have this in the spec.
> 
> I dropped [1] given that [2] is landing shortly to IETF draft process,
> according to James Bottomley [3]. We will return to [1] as soon as draft
> process is open for comments. Still, that attribute is super important,
> and here is why.
> 
> This will cause a overhead as tpm2_unseal_trusted needs to do an
> unnecessary (from pure technical perspective) TPM2_ReadPublic command to
> acquire TPM name of the parent. This is obviously known at the time of
> creation of a key but the information is not stored anywhere by the
> key format.
> 
> It also aligns badly with TCG specifications as Table 6 of architecture
> spec explicitly defines a reference (or name) for transient keys,
> persistent keys and NV indexes to be TPM_ALG_ID concatenated together
> with the hash of TPMT_PUBLIC. I.e. the file format is using exactly
> the opposite what should be use as reference for keys than what it 
> should use.
> 
> Other benefits are of course auto-discovery of parent for a key file,
> which is nasty to do without the name pre-stored.

Right and TPMT_HA is calculated everytime when a trusted key is
created, and right after that the already calculated data is simply
thrown into dumpster. And we are talking about spec compliant way
to refer other keys here, and only 66 bytes of extra payload.

I don't get it. And neither do I get how anyone would want to fix 
this issue with TPM2_CreatePrimary interception.

BR, Jarkko

^ permalink raw reply

* Re: [PATCH 1/1] IMA event log trimming
From: Roberto Sassu @ 2025-12-08  9:40 UTC (permalink / raw)
  To: James Bottomley, steven chen, linux-integrity
  Cc: zohar, roberto.sassu, dmitry.kasatkin, eric.snowberg, paul,
	jmorris, serge, linux-security-module, anirudhve, gregorylumen,
	nramas, sushring
In-Reply-To: <34d739c2cf15baf78dff5acb7ae3ddd7ad47f219.camel@HansenPartnership.com>

On Fri, 2025-12-05 at 13:30 -0500, James Bottomley wrote:
> On Fri, 2025-12-05 at 10:30 +0100, Roberto Sassu wrote:
> > On Tue, 2025-12-02 at 15:28 -0800, steven chen wrote:
> > > This patch is for trimming N entries of the IMA event logs as well
> > > as cleaning the hash table.
> > > 
> > > It provides a userspace interface ima_trim_log that can be used to
> > > input number N to let kernel to trim N entries of IMA event logs.
> > > When read this interface, it returns number of entries trimmed last
> > > tim.
> > 
> > High-level comments:
> > - It does not offer the possibility to keep the hash table
> > - There is no coordination between taking a snapshot and the readers
> > of
> >   the measurements list (I think it is necessary, since reading is
> >   based on *pos, which contains the entries read until a given point;
> >   if there is a truncate in the middle of the read, *pos would still
> >   refer to the non-truncated list and the next read will skip some
> >   measurement entries)
> 
> Rather than designing the interface absent use cases, could we give use
> cases first so we know it fits?  I really only have one: the keylime
> agent, but I believe it's pattern, which would be get TPM quote of
> logging PCRs, certify quote and then trim the log up to the point the
> quote was obtained will be a common pattern (at least I don't think
> anyone would trim the log without quoting it).  If you're trimming
> based on a quote, you know the end hash of all the PCRs and you want to
> trim up to that.  Since the log is fixed, you can work out what the
> index offset is, but it does seem a bit suboptimal to have to compute
> that, which is why I think trimming by end PCR hash is useful.  The
> main point in all of this is, I think, that you don't really get to
> decide which point in the log the TPM will quote: it always quotes it's
> current PCR values, so no-one who cares about log quotes can select the
> number of entries first, they all have to see what PCR values the quote
> returns.
> 
> > - While trimming per se is ok, I like more the idea of staging
> > changes and letting the user delete the staged measurements list
> > later
> 
> I'm not averse to this, but keylime won't care ... it gets the quote
> and the log and it will verify the log before it will get the agent to
> issue a trim.  Is there a use case that would actually need this
> behaviour?
> 
> I also think having the base PCRs stored in the kernel after the trim
> (in a way that's easily consumed, like a non measured log entry), thus
> allowing verification from the log up to any current quote is useful. 
> The reason I think it's useful is the attestation token one: I can see
> the keylime verifier giving an I've verified the log up to here and all
> the entries look good token that contains the base PCRs and if there
> are quite a few of these floating around (especially if the log isn't
> always trimmed) I could see the kernel base PCR storage being used to
> ask which is the relevant attestation token I should use to verify the
> rest of the log (or even used to detect nefarious trimming designed to
> hide records).

I have the impression that none the functionality you cited has to be
implemented in the kernel, because the only component one can trust to
verify the integrity of the IMA measurements list is the TPM. Whether
either the kernel or user space retain the measurements is irrelevant.

I believe that the only role of the kernel is to get rid of the
measurements entries as fast as possible (the kernel would act more
like a buffer).

This was actually the intent of my original proposal in
https://github.com/linux-integrity/linux/issues/1 . The idea of staging
(was snapshotting, but Mimi thinks the term is not accurate) is simply
to detach the entire IMA measurement list as fast as possible. Further
read and delete after staging is done without interfering with new
measurements (sure, the detaching of the hash table is not yet as
efficient as I hoped).

Anything that you said about verifying the measurements up to a certain
TPM quote can be implemented in user space without a decrease in
security. I don't see the need to synchronize the trim with the
verification in the kernel.

Roberto


^ permalink raw reply

* [PATCH] ima_selinux: Fix requirements
From: Petr Vorel @ 2025-12-08 14:13 UTC (permalink / raw)
  To: ltp; +Cc: Petr Vorel, Mimi Zohar, linux-integrity, selinux

"measure func=CRITICAL_DATA label=selinux" in selinux.policy LTP example
policy is a subset of "measure func=CRITICAL_DATA" loaded by
ima_policy=critical_data kernel command line option.

Therefore ima_selinux.sh require only one of them, not both.

Reported-by: Mimi Zohar <zohar@linux.ibm.com>
Signed-off-by: Petr Vorel <pvorel@suse.cz>
---
Thanks Mimi!

Kind regards,
Petr

 testcases/kernel/security/integrity/ima/tests/ima_selinux.sh | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/testcases/kernel/security/integrity/ima/tests/ima_selinux.sh b/testcases/kernel/security/integrity/ima/tests/ima_selinux.sh
index 1a0de21efd..e64a7739f9 100755
--- a/testcases/kernel/security/integrity/ima/tests/ima_selinux.sh
+++ b/testcases/kernel/security/integrity/ima/tests/ima_selinux.sh
@@ -18,13 +18,12 @@ TST_SETUP="setup"
 TST_MIN_KVER="5.12"
 
 REQUIRED_POLICY_CONTENT='selinux.policy'
+REQUIRED_BUILTIN_POLICY='critical_data'
 
 setup()
 {
 	SELINUX_DIR=$(tst_get_selinux_dir)
 	[ "$SELINUX_DIR" ] || tst_brk TCONF "SELinux is not enabled"
-
-	require_ima_policy_cmdline "critical_data"
 }
 
 # Format of the measured SELinux state data.
-- 
2.51.0


^ permalink raw reply related

* [PATCH] KEYS: trusted: Fix overwrite of keyhandle parameter
From: Jarkko Sakkinen @ 2025-12-08 14:54 UTC (permalink / raw)
  To: linux-integrity
  Cc: Jarkko Sakkinen, stable, James Bottomley, Mimi Zohar,
	David Howells, Paul Moore, James Morris, Serge E. Hallyn,
	open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM, open list

tpm2_key_decode() overrides the explicit keyhandle parameter, which can
lead to problems, if the loaded parent handle does not match the handle
stored to the key file. This can easily happen as handle by definition
is an ambiguous attribute.

Cc: stable@vger.kernel.org # v5.13+
Fixes: f2219745250f ("security: keys: trusted: use ASN.1 TPM2 key format for the blobs")
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
 security/keys/trusted-keys/trusted_tpm2.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index fb76c4ea496f..950684e54c71 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -121,7 +121,9 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
 		return -ENOMEM;
 
 	*buf = blob;
-	options->keyhandle = ctx.parent;
+
+	if (!options->keyhandle)
+		options->keyhandle = ctx.parent;
 
 	memcpy(blob, ctx.priv, ctx.priv_len);
 	blob += ctx.priv_len;
-- 
2.39.5


^ permalink raw reply related

* Re: [PATCH 1/1] IMA event log trimming
From: Gregory Lumen @ 2025-12-08 17:21 UTC (permalink / raw)
  To: Roberto Sassu
  Cc: James Bottomley, steven chen, linux-integrity, zohar,
	roberto.sassu, dmitry.kasatkin, eric.snowberg, paul, jmorris,
	serge, linux-security-module, anirudhve, nramas, sushring
In-Reply-To: <1ca00e3238e804db9280abf8655364c2662754ca.camel@huaweicloud.com>

[-- Attachment #1: Type: text/plain, Size: 1701 bytes --]

> Rather than designing the interface absent use cases, could we give use
> cases first so we know it fits?

I would also like to request that we include operational considerations in 
the interface design; specifically, which additional signals can or should 
be made available to UM to assist in diagnosing log validation failures. 
This seems called for as any form of log trimming will introduce a new 
potential cause for validation failures (unexpected trimming).

With the proposed changes, the only signal available to system operators 
is the validation failure itself, with no signal that could be used to 
determine if the failure was the result of an unexpected trim or a loss of 
synchronization between the log and the PCRs (either through an unexpected 
PCR extend, or tampering with the measurement list). Any of these may 
indicate malicious activity, but they may also result from system 
configuration issues that operators would need to diagnose and resolve.

Tracking and exposing either the total number of trimmed measurements or 
the most recent trimmed-to PCR values by the kernel would allow system 
operators to determine whether a failure was caused by unexpected trimming 
or integrity issues. (Storing the PCR values also enables validation of 
the retained measurements even after an unexpected trim, though I’m unsure 
how often that signal would prove useful.)

Neither approach appears to add any additional attack surface beyond 
raising the likelihood of incorrectly or insecurely implemented UM 
attestation agents. Though that risk (and the additional kernel 
complexity) should be weighed against the value of the additional 
diagnostic signals.

-Gregory Lumen

^ permalink raw reply

* Re: [PATCH 1/1] IMA event log trimming
From: Roberto Sassu @ 2025-12-08 17:50 UTC (permalink / raw)
  To: Gregory Lumen
  Cc: James Bottomley, steven chen, linux-integrity, zohar,
	roberto.sassu, dmitry.kasatkin, eric.snowberg, paul, jmorris,
	serge, linux-security-module, anirudhve, nramas, sushring
In-Reply-To: <862fa081-df51-084-ae2c-efa0eae0ca7e@linux.microsoft.com>

On Mon, 2025-12-08 at 09:21 -0800, Gregory Lumen wrote:
> > Rather than designing the interface absent use cases, could we give use
> > cases first so we know it fits?
> 
> I would also like to request that we include operational considerations in 
> the interface design; specifically, which additional signals can or should 
> be made available to UM to assist in diagnosing log validation failures. 
> This seems called for as any form of log trimming will introduce a new 
> potential cause for validation failures (unexpected trimming).
> 
> With the proposed changes, the only signal available to system operators 
> is the validation failure itself, with no signal that could be used to 
> determine if the failure was the result of an unexpected trim or a loss of 
> synchronization between the log and the PCRs (either through an unexpected 
> PCR extend, or tampering with the measurement list). Any of these may 
> indicate malicious activity, but they may also result from system 
> configuration issues that operators would need to diagnose and resolve.

I don't agree with this. User space can keep a persistent state, and
resume from where it was interrupted. There is no risk of loss of data,
if user space deletes staged measurements only after the read is
complete.

The same comment applies for coordination between different user space
agents. It would not be too hard to expose an interface managed by a
new user space component, providing similar data as IMA (even showing
the full measurements list without its users being aware of the
snapshots). The other agents can fetch the data from the new interface.

And also, I don't see the problem in developing more complex user space
programs which link the measurement entries to PCR values, and anything
that is desirable.

And if from the kernel perspective all we need to do is a list replace,
for me that is the most efficient solution.

Roberto

> Tracking and exposing either the total number of trimmed measurements or 
> the most recent trimmed-to PCR values by the kernel would allow system 
> operators to determine whether a failure was caused by unexpected trimming 
> or integrity issues. (Storing the PCR values also enables validation of 
> the retained measurements even after an unexpected trim, though I’m unsure 
> how often that signal would prove useful.)
> 
> Neither approach appears to add any additional attack surface beyond 
> raising the likelihood of incorrectly or insecurely implemented UM 
> attestation agents. Though that risk (and the additional kernel 
> complexity) should be weighed against the value of the additional 
> diagnostic signals.
> 
> -Gregory Lumen


^ permalink raw reply

* Re: [PATCH] KEYS: trusted: Fix overwrite of keyhandle parameter
From: Jarkko Sakkinen @ 2025-12-08 18:56 UTC (permalink / raw)
  To: linux-integrity
  Cc: stable, James Bottomley, Mimi Zohar, David Howells, Paul Moore,
	James Morris, Serge E. Hallyn, open list:KEYS-TRUSTED,
	open list:SECURITY SUBSYSTEM, open list
In-Reply-To: <20251208145436.21519-1-jarkko@kernel.org>

On Mon, Dec 08, 2025 at 04:54:35PM +0200, Jarkko Sakkinen wrote:
> tpm2_key_decode() overrides the explicit keyhandle parameter, which can
> lead to problems, if the loaded parent handle does not match the handle
> stored to the key file. This can easily happen as handle by definition
> is an ambiguous attribute.
> 
> Cc: stable@vger.kernel.org # v5.13+
> Fixes: f2219745250f ("security: keys: trusted: use ASN.1 TPM2 key format for the blobs")
> Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>

What this means in practice is that sometimes you need either to:

1. Binary patch the key file.
2. Decompose/compose a key file

BR, Jarkko

^ permalink raw reply

* Re: [PATCH 1/1] IMA event log trimming
From: James Bottomley @ 2025-12-08 22:17 UTC (permalink / raw)
  To: Roberto Sassu, steven chen, linux-integrity
  Cc: zohar, roberto.sassu, dmitry.kasatkin, eric.snowberg, paul,
	jmorris, serge, linux-security-module, anirudhve, gregorylumen,
	nramas, sushring
In-Reply-To: <1ca00e3238e804db9280abf8655364c2662754ca.camel@huaweicloud.com>

On Mon, 2025-12-08 at 10:40 +0100, Roberto Sassu wrote:
> I have the impression that none the functionality you cited has to be
> implemented in the kernel, because the only component one can trust
> to verify the integrity of the IMA measurements list is the TPM.
> Whether either the kernel or user space retain the measurements is
> irrelevant.

That's correct, I'm not advocating moving quoting into the kernel.  Co-
ordinating the trim with where the quote gets you to is phenomenally
useful.  While you could theoretically store any mismatch in userspace,
having two locations for the log makes it more error prone.

> I believe that the only role of the kernel is to get rid of the
> measurements entries as fast as possible (the kernel would act more
> like a buffer).

I wouldn't say that, I'd say to get rid of measurements that the user
has indicated are of no further use.

> This was actually the intent of my original proposal in
> https://github.com/linux-integrity/linux/issues/1 . The idea of
> staging (was snapshotting, but Mimi thinks the term is not accurate)
> is simply to detach the entire IMA measurement list as fast as
> possible. Further read and delete after staging is done without
> interfering with new measurements (sure, the detaching of the hash
> table is not yet as efficient as I hoped).

From the application point of view, offloading the log and random
points is a bit more work because now the log collector has to be
modified to look in multiple locations and we'd also need an agreement
of where those locations are and how the log is sequenced in a naming
scheme so it's the same for every distribution.  If the application is
in charge of trimming the log at a particular point, collection remains
the same (it can simply be the existing in-kernel location), so we
don't need a cross distro agreement, and the trim can simply be added
as an extra function.

Regards,

James


^ permalink raw reply

* [PATCH] KEYS: trusted: Don't use 'buf->handles'
From: Jarkko Sakkinen @ 2025-12-09  7:11 UTC (permalink / raw)
  To: linux-integrity
  Cc: Jarkko Sakkinen, James Bottomley, Mimi Zohar, David Howells,
	Paul Moore, James Morris, Serge E. Hallyn, open list:KEYS-TRUSTED,
	open list:SECURITY SUBSYSTEM, open list

tpm2_unseal_trusted() deduces number of handles in run-time even tho the
expected value is known at compile time. Address the issue.

Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
 security/keys/trusted-keys/trusted_tpm2.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 9074ae1a5896..e78061ee2d99 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -491,7 +491,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 		if (tpm2_chip_auth(chip)) {
 			tpm_buf_append_hmac_session(chip, buf, TPM2_SA_ENCRYPT, NULL, 0);
 		} else  {
-			offset = buf->handles * 4 + TPM_HEADER_SIZE;
+			offset = TPM_HEADER_SIZE + 2 * sizeof(u32);
 			head = (struct tpm_header *)buf->data;
 			if (tpm_buf_length(buf) == offset)
 				head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
-- 
2.39.5


^ permalink raw reply related

* [PATCH 0/4] Streamline TPM2 HMAC sessions
From: Jarkko Sakkinen @ 2025-12-09  7:38 UTC (permalink / raw)
  To: linux-integrity; +Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, open list

Since we cannot at this point cache names of the keys given limitations
of the ASN.1 file format, I'll start a fresh patch set. Let's fixup what
we can right now.

This patch set addresses two major issues in the feature:

1. Dynamic resolution without gain. All kernel sites have at most single
   handle to authorize. Even if this changes some day this is how it is
   as of today and we definitely do not want to dictate the future but
   instead downscale code to the metrics that we have as of today.
2. Eliminate at least one unnnecessary tpm2_read_public() call.

Jarkko Sakkinen (4):
  tpm2-sessions: Define TPM2_NAME_MAX_SIZE
  KEYS: trusted: Re-orchestrate tpm2_read_public() calls
  tpm2-sessions: Remove AUTH_MAX_NAMES
  tpm-buf: Remove tpm_buf_append_handle

 drivers/char/tpm/tpm-buf.c                |  25 -----
 drivers/char/tpm/tpm2-cmd.c               |   7 +-
 drivers/char/tpm/tpm2-sessions.c          | 120 +++++++--------------
 include/linux/tpm.h                       |  20 +++-
 security/keys/trusted-keys/trusted_tpm2.c | 126 ++++++++++++++--------
 5 files changed, 138 insertions(+), 160 deletions(-)

-- 
2.52.0


^ permalink raw reply

* [PATCH 1/4] tpm2-sessions: Define TPM2_NAME_MAX_SIZE
From: Jarkko Sakkinen @ 2025-12-09  7:38 UTC (permalink / raw)
  To: linux-integrity; +Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, open list
In-Reply-To: <20251209073903.767518-1-jarkko@kernel.org>

This is somewhat cosmetic change but it does serve a purpose on tracking
the value set for the maximum length of TPM names, and to clearly states
what components it is composed of. It also anchors the value so that when
buffers are declared for this particular purpose, the same value is used
for the capacity.

Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
 drivers/char/tpm/tpm2-sessions.c | 2 +-
 include/linux/tpm.h              | 9 +++++++++
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 79f27a46bd7f..a0c88fb1804c 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -137,7 +137,7 @@ struct tpm2_auth {
 	 * we must compute and remember
 	 */
 	u32 name_h[AUTH_MAX_NAMES];
-	u8 name[AUTH_MAX_NAMES][2 + SHA512_DIGEST_SIZE];
+	u8 name[AUTH_MAX_NAMES][TPM2_MAX_NAME_SIZE];
 };
 
 #ifdef CONFIG_TCG_TPM2_HMAC
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 42e2a091f43d..b4b5bd586501 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -28,7 +28,16 @@
 #define TPM_DIGEST_SIZE		20	/* Max TPM v1.2 PCR size */
 #define TPM_BUFSIZE		4096
 
+/*
+ * SHA-512 is, as of today, the largest digest in the TCG algorithm repository.
+ */
 #define TPM2_MAX_DIGEST_SIZE	SHA512_DIGEST_SIZE
+
+/*
+ * A TPM name digest i.e., TPMT_HA, is a concatenation of TPM_ALG_ID of the
+ * name algorithm and hash of TPMT_PUBLIC.
+ */
+#define TPM2_MAX_NAME_SIZE	(TPM2_MAX_DIGEST_SIZE + 2)
 #define TPM2_MAX_PCR_BANKS	8
 
 struct tpm_chip;
-- 
2.52.0


^ permalink raw reply related

* [PATCH 2/4] KEYS: trusted: Re-orchestrate tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-09  7:39 UTC (permalink / raw)
  To: linux-integrity
  Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, James Bottomley,
	Mimi Zohar, David Howells, Paul Moore, James Morris,
	Serge E. Hallyn, open list, open list:KEYS-TRUSTED,
	open list:SECURITY SUBSYSTEM
In-Reply-To: <20251209073903.767518-1-jarkko@kernel.org>

tpm2_load_cmd() and tpm2_unseal_cmd() use the same parent, and calls to
tpm_buf_append_name() cause the exact same TPM2_ReadPublic command to be
sent to the chip, causing unnecessary traffic.

1. Export tpm2_read_public in order to make it callable from
   'trusted_tpm2'.
2. Re-orchestrate tpm2_seal_trusted() and tpm2_unseal_trusted() in order to
   halve the name resolutions required:
2a. Move tpm2_read_public() calls into trusted_tpm2.
2b. Pass TPM name to tpm_buf_append_name().
2c. Rework tpm_buf_append_name() to use the pre-resolved name.

Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
 drivers/char/tpm/tpm2-cmd.c               |   3 +-
 drivers/char/tpm/tpm2-sessions.c          |  95 +++++------------
 include/linux/tpm.h                       |  10 +-
 security/keys/trusted-keys/trusted_tpm2.c | 124 ++++++++++++++--------
 4 files changed, 118 insertions(+), 114 deletions(-)

diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 2682f5ec09cb..5b04e74b6377 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -199,7 +199,8 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
 	tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
 
 	if (!disable_pcr_integrity) {
-		rc = tpm_buf_append_name(chip, buf, pcr_idx, NULL);
+		rc = tpm_buf_append_name(chip, buf, pcr_idx, (u8 *)&pcr_idx,
+					 sizeof(u32));
 		if (rc)
 			return rc;
 		tpm_buf_append_hmac_session(chip, buf, 0, NULL, 0);
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index a0c88fb1804c..0816a91134fc 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -136,8 +136,8 @@ struct tpm2_auth {
 	 * handle, but they are part of the session by name, which
 	 * we must compute and remember
 	 */
-	u32 name_h[AUTH_MAX_NAMES];
 	u8 name[AUTH_MAX_NAMES][TPM2_MAX_NAME_SIZE];
+	u16 name_size_tbl[AUTH_MAX_NAMES];
 };
 
 #ifdef CONFIG_TCG_TPM2_HMAC
@@ -163,7 +163,17 @@ static int name_size(const u8 *name)
 	}
 }
 
-static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
+/**
+ * tpm2_read_public: Resolve TPM name for a handle
+ * @chip:		TPM chip to use.
+ * @handle:		TPM handle.
+ * @name:		A buffer for returning the name blob. Must have a
+ *			capacity of 'SHA512_DIGET_SIZE + 2' bytes at minimum
+ *
+ * Returns size of TPM handle name of success.
+ * Returns tpm_transmit_cmd error codes when TPM2_ReadPublic fails.
+ */
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
 {
 	u32 mso = tpm2_handle_mso(handle);
 	off_t offset = TPM_HEADER_SIZE;
@@ -212,14 +222,16 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
 	memcpy(name, &buf->data[offset], rc);
 	return name_size_alg;
 }
+EXPORT_SYMBOL_GPL(tpm2_read_public);
 #endif /* CONFIG_TCG_TPM2_HMAC */
 
 /**
- * tpm_buf_append_name() - add a handle area to the buffer
- * @chip: the TPM chip structure
- * @buf: The buffer to be appended
- * @handle: The handle to be appended
- * @name: The name of the handle (may be NULL)
+ * tpm_buf_append_name() - Append a handle and store TPM name
+ * @chip:		TPM chip to use.
+ * @buf:		TPM buffer containing the TPM command in-transit.
+ * @handle:		TPM handle to be appended.
+ * @name:		TPM name of the handle
+ * @name_size:		Size of the TPM name.
  *
  * In order to compute session HMACs, we need to know the names of the
  * objects pointed to by the handles.  For most objects, this is simply
@@ -236,15 +248,14 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
  * will be caused by an incorrect programming model and indicated by a
  * kernel message.
  *
- * Ends the authorization session on failure.
+ * Returns zero on success.
+ * Returns -EIO when the authorization area state is malformed.
  */
 int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
-			u32 handle, u8 *name)
+			u32 handle, u8 *name, u16 name_size)
 {
 #ifdef CONFIG_TCG_TPM2_HMAC
-	enum tpm2_mso_type mso = tpm2_handle_mso(handle);
 	struct tpm2_auth *auth;
-	u16 name_size_alg;
 	int slot;
 	int ret;
 #endif
@@ -269,36 +280,15 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
 	}
 	tpm_buf_append_u32(buf, handle);
 	auth->session += 4;
-
-	if (mso == TPM2_MSO_PERSISTENT ||
-	    mso == TPM2_MSO_VOLATILE ||
-	    mso == TPM2_MSO_NVRAM) {
-		if (!name) {
-			ret = tpm2_read_public(chip, handle, auth->name[slot]);
-			if (ret < 0)
-				goto err;
-
-			name_size_alg = ret;
-		}
-	} else {
-		if (name) {
-			dev_err(&chip->dev, "handle 0x%08x does not use a name\n",
-				handle);
-			ret = -EIO;
-			goto err;
-		}
-	}
-
-	auth->name_h[slot] = handle;
-	if (name)
-		memcpy(auth->name[slot], name, name_size_alg);
+	memcpy(auth->name[slot], name, name_size);
+	auth->name_size_tbl[slot] = name_size;
 #endif
 	return 0;
 
 #ifdef CONFIG_TCG_TPM2_HMAC
 err:
 	tpm2_end_auth_session(chip);
-	return tpm_ret_to_err(ret);
+	return ret;
 #endif
 }
 EXPORT_SYMBOL_GPL(tpm_buf_append_name);
@@ -606,22 +596,8 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
 	attrs = chip->cc_attrs_tbl[i];
 
 	handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
+	offset_s += handles * sizeof(u32);
 
-	/*
-	 * just check the names, it's easy to make mistakes.  This
-	 * would happen if someone added a handle via
-	 * tpm_buf_append_u32() instead of tpm_buf_append_name()
-	 */
-	for (i = 0; i < handles; i++) {
-		u32 handle = tpm_buf_read_u32(buf, &offset_s);
-
-		if (auth->name_h[i] != handle) {
-			dev_err(&chip->dev, "invalid handle 0x%08x\n", handle);
-			ret = -EIO;
-			goto err;
-		}
-	}
-	/* point offset_s to the start of the sessions */
 	val = tpm_buf_read_u32(buf, &offset_s);
 	/* point offset_p to the start of the parameters */
 	offset_p = offset_s + val;
@@ -682,23 +658,8 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
 	/* ordinal is already BE */
 	sha256_update(&sctx, (u8 *)&head->ordinal, sizeof(head->ordinal));
 	/* add the handle names */
-	for (i = 0; i < handles; i++) {
-		enum tpm2_mso_type mso = tpm2_handle_mso(auth->name_h[i]);
-
-		if (mso == TPM2_MSO_PERSISTENT ||
-		    mso == TPM2_MSO_VOLATILE ||
-		    mso == TPM2_MSO_NVRAM) {
-			ret = name_size(auth->name[i]);
-			if (ret < 0)
-				goto err;
-
-			sha256_update(&sctx, auth->name[i], ret);
-		} else {
-			__be32 h = cpu_to_be32(auth->name_h[i]);
-
-			sha256_update(&sctx, (u8 *)&h, 4);
-		}
-	}
+	for (i = 0; i < handles; i++)
+		sha256_update(&sctx, auth->name[i], auth->name_size_tbl[i]);
 	if (offset_s != tpm_buf_length(buf))
 		sha256_update(&sctx, &buf->data[offset_s],
 			      tpm_buf_length(buf) - offset_s);
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index b4b5bd586501..db716841973f 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -539,7 +539,7 @@ static inline struct tpm2_auth *tpm2_chip_auth(struct tpm_chip *chip)
 }
 
 int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
-			u32 handle, u8 *name);
+			u32 handle, u8 *name, u16 name_size);
 void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
 				 u8 attributes, u8 *passphrase,
 				 int passphraselen);
@@ -553,6 +553,7 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
 int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
 				int rc);
 void tpm2_end_auth_session(struct tpm_chip *chip);
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name);
 #else
 #include <linux/unaligned.h>
 
@@ -576,6 +577,13 @@ static inline int tpm_buf_check_hmac_response(struct tpm_chip *chip,
 {
 	return rc;
 }
+
+static inline int tpm2_read_public(struct tpm_chip *chip, u32 handle,
+				   void *name)
+{
+	memcpy(name, &handle, sizeof(u32));
+	return sizeof(u32);
+}
 #endif	/* CONFIG_TCG_TPM2_HMAC */
 
 #endif
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 9074ae1a5896..7756aa839d81 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -203,7 +203,9 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		      struct trusted_key_payload *payload,
 		      struct trusted_key_options *options)
 {
+	u8 parent_name[TPM2_MAX_NAME_SIZE];
 	off_t offset = TPM_HEADER_SIZE;
+	u16 parent_name_size;
 	int blob_len = 0;
 	int hash;
 	u32 flags;
@@ -220,6 +222,12 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 	if (rc)
 		return rc;
 
+	rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+	if (rc < 0)
+		goto out_put;
+
+	parent_name_size = rc;
+
 	rc = tpm2_start_auth_session(chip);
 	if (rc)
 		goto out_put;
@@ -234,7 +242,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 	tpm_buf_init(buf, TPM_BUFSIZE);
 	tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
 
-	rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
+	rc = tpm_buf_append_name(chip, buf, options->keyhandle, parent_name,
+				 parent_name_size);
 	if (rc)
 		goto out_put;
 
@@ -326,48 +335,38 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 }
 
 /**
- * tpm2_load_cmd() - execute a TPM2_Load command
- *
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- * @blob_handle: returned blob handle
+ * tpm2_load_cmd() - Execute TPM2_Load
+ * @chip:		TPM chip to use.
+ * @payload:		Key data in clear text.
+ * @options:		Trusted key options.
+ * @parent_name:	A cryptographic name, i.e. a TPMT_HA blob, of the
+ *			parent key.
+ * @blob:		The decoded payload for the key.
+ * @blob_handle:	On success, will contain handle to the loaded keyedhash
+ *			blob.
  *
- * Return: 0 on success.
- *        -E2BIG on wrong payload size.
- *        -EPERM on tpm error status.
- *        < 0 error from tpm_send.
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load fails.
  */
 static int tpm2_load_cmd(struct tpm_chip *chip,
 			 struct trusted_key_payload *payload,
 			 struct trusted_key_options *options,
+			 u8 *parent_name,
+			 u16 parent_name_size,
+			 const u8 *blob,
 			 u32 *blob_handle)
 {
 	u8 *blob_ref __free(kfree) = NULL;
 	unsigned int private_len;
 	unsigned int public_len;
 	unsigned int blob_len;
-	u8 *blob, *pub;
+	const u8 *pub;
 	int rc;
 	u32 attrs;
 
-	rc = tpm2_key_decode(payload, options, &blob);
-	if (rc) {
-		/* old form */
-		blob = payload->blob;
-		payload->old_format = 1;
-	} else {
-		/* Bind for cleanup: */
-		blob_ref = blob;
-	}
-
-	/* new format carries keyhandle but old format doesn't */
-	if (!options->keyhandle)
-		return -EINVAL;
-
 	/* must be big enough for at least the two be16 size counts */
 	if (payload->blob_len < 4)
-		return -EINVAL;
+		return -E2BIG;
 
 	private_len = get_unaligned_be16(blob);
 
@@ -406,7 +405,8 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 	tpm_buf_init(buf, TPM_BUFSIZE);
 	tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
 
-	rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
+	rc = tpm_buf_append_name(chip, buf, options->keyhandle, parent_name,
+				 parent_name_size);
 	if (rc)
 		return rc;
 
@@ -434,20 +434,23 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 }
 
 /**
- * tpm2_unseal_cmd() - execute a TPM2_Unload command
+ * tpm2_unseal_cmd() - Execute TPM2_Unload
  *
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- * @blob_handle: blob handle
+ * @chip:		TPM chip to use
+ * @payload:		Key data in clear text.
+ * @options:		Trusted key options.
+ * @parent_name:	A cryptographic name, i.e. a TPMT_HA blob, of the
+ *			parent key.
+ * @blob_handle:	Handle to the loaded keyedhash blob.
  *
- * Return: 0 on success
- *         -EPERM on tpm error status
- *         < 0 error from tpm_send
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load fails.
  */
 static int tpm2_unseal_cmd(struct tpm_chip *chip,
 			   struct trusted_key_payload *payload,
 			   struct trusted_key_options *options,
+			   u8 *parent_name,
+			   u16 parent_name_size,
 			   u32 blob_handle)
 {
 	struct tpm_header *head;
@@ -467,7 +470,8 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 	tpm_buf_init(buf, TPM_BUFSIZE);
 	tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
 
-	rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
+	rc = tpm_buf_append_name(chip, buf, options->keyhandle, parent_name,
+				 parent_name_size);
 	if (rc)
 		return rc;
 
@@ -534,30 +538,60 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 }
 
 /**
- * tpm2_unseal_trusted() - unseal the payload of a trusted key
+ * tpm2_unseal_trusted() - Unseal a trusted key
+ * @chip:	TPM chip to use.
+ * @payload:	Key data in clear text.
+ * @options:	Trusted key options.
  *
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- *
- * Return: Same as with tpm_send.
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Return -EINVAL when parent's key handle has not been set.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load or TPM2_Unseal
+ * fails.
  */
 int tpm2_unseal_trusted(struct tpm_chip *chip,
 			struct trusted_key_payload *payload,
 			struct trusted_key_options *options)
 {
+	u8 *blob_ref __free(kfree) = NULL;
+	u8 parent_name[TPM2_MAX_NAME_SIZE];
+	u16 parent_name_size;
 	u32 blob_handle;
+	u8 *blob;
 	int rc;
 
+	/*
+	 * Try to decode the provided blob as an ASN.1 blob. Assume that the
+	 * blob is in the legacy format if decoding does not end successfully.
+	 */
+	rc = tpm2_key_decode(payload, options, &blob);
+	if (rc) {
+		blob = payload->blob;
+		payload->old_format = 1;
+	} else {
+		blob_ref = blob;
+	}
+
+	if (!options->keyhandle)
+		return -EINVAL;
+
 	rc = tpm_try_get_ops(chip);
 	if (rc)
 		return rc;
 
-	rc = tpm2_load_cmd(chip, payload, options, &blob_handle);
+	rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+	if (rc < 0)
+		goto out;
+
+	parent_name_size = rc;
+
+	rc = tpm2_load_cmd(chip, payload, options, parent_name,
+			   parent_name_size, blob, &blob_handle);
 	if (rc)
 		goto out;
 
-	rc = tpm2_unseal_cmd(chip, payload, options, blob_handle);
+	rc = tpm2_unseal_cmd(chip, payload, options, parent_name,
+			     parent_name_size, blob_handle);
+
 	tpm2_flush_context(chip, blob_handle);
 
 out:
-- 
2.52.0


^ permalink raw reply related

* [PATCH 3/4] tpm2-sessions: Remove AUTH_MAX_NAMES
From: Jarkko Sakkinen @ 2025-12-09  7:39 UTC (permalink / raw)
  To: linux-integrity; +Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, open list
In-Reply-To: <20251209073903.767518-1-jarkko@kernel.org>

In all of the call sites only one session is ever append. Thus, reduce
AUTH_MAX_NAMES, which leads into removing constant completely.

Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
 drivers/char/tpm/tpm2-sessions.c | 31 +++++++++++--------------------
 1 file changed, 11 insertions(+), 20 deletions(-)

diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 0816a91134fc..62a200ae72d7 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -72,9 +72,6 @@
 #include <crypto/sha2.h>
 #include <crypto/utils.h>
 
-/* maximum number of names the TPM must remember for authorization */
-#define AUTH_MAX_NAMES	3
-
 #define AES_KEY_BYTES	AES_KEYSIZE_128
 #define AES_KEY_BITS	(AES_KEY_BYTES*8)
 
@@ -136,8 +133,8 @@ struct tpm2_auth {
 	 * handle, but they are part of the session by name, which
 	 * we must compute and remember
 	 */
-	u8 name[AUTH_MAX_NAMES][TPM2_MAX_NAME_SIZE];
-	u16 name_size_tbl[AUTH_MAX_NAMES];
+	u8 name[TPM2_MAX_NAME_SIZE];
+	u16 name_size;
 };
 
 #ifdef CONFIG_TCG_TPM2_HMAC
@@ -254,11 +251,14 @@ EXPORT_SYMBOL_GPL(tpm2_read_public);
 int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
 			u32 handle, u8 *name, u16 name_size)
 {
-#ifdef CONFIG_TCG_TPM2_HMAC
 	struct tpm2_auth *auth;
-	int slot;
 	int ret;
-#endif
+
+	if (tpm_buf_length(buf) != TPM_HEADER_SIZE) {
+		dev_err(&chip->dev, "too many handles\n");
+		ret = -EIO;
+		goto err;
+	}
 
 	if (!tpm2_chip_auth(chip)) {
 		tpm_buf_append_handle(buf, handle);
@@ -266,12 +266,6 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
 	}
 
 #ifdef CONFIG_TCG_TPM2_HMAC
-	slot = (tpm_buf_length(buf) - TPM_HEADER_SIZE) / 4;
-	if (slot >= AUTH_MAX_NAMES) {
-		dev_err(&chip->dev, "too many handles\n");
-		ret = -EIO;
-		goto err;
-	}
 	auth = chip->auth;
 	if (auth->session != tpm_buf_length(buf)) {
 		dev_err(&chip->dev, "session state malformed");
@@ -280,16 +274,14 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
 	}
 	tpm_buf_append_u32(buf, handle);
 	auth->session += 4;
-	memcpy(auth->name[slot], name, name_size);
-	auth->name_size_tbl[slot] = name_size;
+	memcpy(auth->name, name, name_size);
+	auth->name_size = name_size;
 #endif
 	return 0;
 
-#ifdef CONFIG_TCG_TPM2_HMAC
 err:
 	tpm2_end_auth_session(chip);
 	return ret;
-#endif
 }
 EXPORT_SYMBOL_GPL(tpm_buf_append_name);
 
@@ -658,8 +650,7 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
 	/* ordinal is already BE */
 	sha256_update(&sctx, (u8 *)&head->ordinal, sizeof(head->ordinal));
 	/* add the handle names */
-	for (i = 0; i < handles; i++)
-		sha256_update(&sctx, auth->name[i], auth->name_size_tbl[i]);
+	sha256_update(&sctx, auth->name, auth->name_size);
 	if (offset_s != tpm_buf_length(buf))
 		sha256_update(&sctx, &buf->data[offset_s],
 			      tpm_buf_length(buf) - offset_s);
-- 
2.52.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox