linux-crypto.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] tpm: HMAC fix and cleanup
@ 2025-07-31 21:52 Eric Biggers
  2025-07-31 21:52 ` [PATCH 1/2] tpm: Compare HMAC values in constant time Eric Biggers
  2025-07-31 21:52 ` [PATCH 2/2] tpm: Use HMAC-SHA256 library instead of open-coded HMAC Eric Biggers
  0 siblings, 2 replies; 13+ messages in thread
From: Eric Biggers @ 2025-07-31 21:52 UTC (permalink / raw)
  To: Peter Huewe, Jarkko Sakkinen, linux-integrity
  Cc: Jason Gunthorpe, James Bottomley, linux-crypto, linux-kernel,
	Eric Biggers

Patch 1 fixes the HMAC comparison in the tpm driver to be constant-time.

Patch 2 simplifies the HMAC computation in the tpm driver by using the
library API instead of an open-coded HMAC implementation.  Note that
this depends on the HMAC library API that was merged for v6.17-rc1.

Eric Biggers (2):
  tpm: Compare HMAC values in constant time
  tpm: Use HMAC-SHA256 library instead of open-coded HMAC

 drivers/char/tpm/Kconfig         |   1 +
 drivers/char/tpm/tpm2-sessions.c | 104 +++++++++----------------------
 2 files changed, 31 insertions(+), 74 deletions(-)


base-commit: d6084bb815c453de27af8071a23163a711586a6c
-- 
2.50.1


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/2] tpm: Compare HMAC values in constant time
  2025-07-31 21:52 [PATCH 0/2] tpm: HMAC fix and cleanup Eric Biggers
@ 2025-07-31 21:52 ` Eric Biggers
  2025-08-01  2:28   ` James Bottomley
  2025-07-31 21:52 ` [PATCH 2/2] tpm: Use HMAC-SHA256 library instead of open-coded HMAC Eric Biggers
  1 sibling, 1 reply; 13+ messages in thread
From: Eric Biggers @ 2025-07-31 21:52 UTC (permalink / raw)
  To: Peter Huewe, Jarkko Sakkinen, linux-integrity
  Cc: Jason Gunthorpe, James Bottomley, linux-crypto, linux-kernel,
	Eric Biggers, stable

To prevent timing attacks, HMAC value comparison needs to be constant
time.  Replace the memcmp() with the correct function, crypto_memneq().

Fixes: 1085b8276bb4 ("tpm: Add the rest of the session HMAC API")
Cc: stable@vger.kernel.org
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
---
 drivers/char/tpm/Kconfig         | 1 +
 drivers/char/tpm/tpm2-sessions.c | 6 +++---
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index dddd702b2454a..f9d8a4e966867 100644
--- a/drivers/char/tpm/Kconfig
+++ b/drivers/char/tpm/Kconfig
@@ -31,10 +31,11 @@ config TCG_TPM2_HMAC
 	bool "Use HMAC and encrypted transactions on the TPM bus"
 	default X86_64
 	select CRYPTO_ECDH
 	select CRYPTO_LIB_AESCFB
 	select CRYPTO_LIB_SHA256
+	select CRYPTO_LIB_UTILS
 	help
 	  Setting this causes us to deploy a scheme which uses request
 	  and response HMACs in addition to encryption for
 	  communicating with the TPM to prevent or detect bus snooping
 	  and interposer attacks (see tpm-security.rst).  Saying Y
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index bdb119453dfbe..5fbd62ee50903 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -69,10 +69,11 @@
 #include <linux/unaligned.h>
 #include <crypto/kpp.h>
 #include <crypto/ecdh.h>
 #include <crypto/hash.h>
 #include <crypto/hmac.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
@@ -827,16 +828,15 @@ int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
 	sha256_update(&sctx, auth->our_nonce, sizeof(auth->our_nonce));
 	sha256_update(&sctx, &auth->attrs, 1);
 	/* we're done with the rphash, so put our idea of the hmac there */
 	tpm2_hmac_final(&sctx, auth->session_key, sizeof(auth->session_key)
 			+ auth->passphrase_len, rphash);
-	if (memcmp(rphash, &buf->data[offset_s], SHA256_DIGEST_SIZE) == 0) {
-		rc = 0;
-	} else {
+	if (crypto_memneq(rphash, &buf->data[offset_s], SHA256_DIGEST_SIZE)) {
 		dev_err(&chip->dev, "TPM: HMAC check failed\n");
 		goto out;
 	}
+	rc = 0;
 
 	/* now do response decryption */
 	if (auth->attrs & TPM2_SA_ENCRYPT) {
 		/* need key and IV */
 		tpm2_KDFa(auth->session_key, SHA256_DIGEST_SIZE
-- 
2.50.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 2/2] tpm: Use HMAC-SHA256 library instead of open-coded HMAC
  2025-07-31 21:52 [PATCH 0/2] tpm: HMAC fix and cleanup Eric Biggers
  2025-07-31 21:52 ` [PATCH 1/2] tpm: Compare HMAC values in constant time Eric Biggers
@ 2025-07-31 21:52 ` Eric Biggers
  1 sibling, 0 replies; 13+ messages in thread
From: Eric Biggers @ 2025-07-31 21:52 UTC (permalink / raw)
  To: Peter Huewe, Jarkko Sakkinen, linux-integrity
  Cc: Jason Gunthorpe, James Bottomley, linux-crypto, linux-kernel,
	Eric Biggers

Now that there are easy-to-use HMAC-SHA256 library functions, use these
in tpm2-sessions.c instead of open-coding the HMAC algorithm.

Note that the new implementation correctly handles keys longer than 64
bytes (SHA256_BLOCK_SIZE), whereas the old implementation handled such
keys incorrectly.  But it doesn't appear that such keys were being used.

Signed-off-by: Eric Biggers <ebiggers@kernel.org>
---
 drivers/char/tpm/tpm2-sessions.c | 98 +++++++++-----------------------
 1 file changed, 27 insertions(+), 71 deletions(-)

diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 5fbd62ee50903..6d03c224e6b21 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -67,12 +67,11 @@
 #include <linux/random.h>
 #include <linux/scatterlist.h>
 #include <linux/unaligned.h>
 #include <crypto/kpp.h>
 #include <crypto/ecdh.h>
-#include <crypto/hash.h>
-#include <crypto/hmac.h>
+#include <crypto/sha2.h>
 #include <crypto/utils.h>
 
 /* maximum number of names the TPM must remember for authorization */
 #define AUTH_MAX_NAMES	3
 
@@ -383,55 +382,10 @@ EXPORT_SYMBOL_GPL(tpm_buf_append_hmac_session);
 #ifdef CONFIG_TCG_TPM2_HMAC
 
 static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy,
 			       u32 *handle, u8 *name);
 
-/*
- * It turns out the crypto hmac(sha256) is hard for us to consume
- * because it assumes a fixed key and the TPM seems to change the key
- * on every operation, so we weld the hmac init and final functions in
- * here to give it the same usage characteristics as a regular hash
- */
-static void tpm2_hmac_init(struct sha256_ctx *sctx, u8 *key, u32 key_len)
-{
-	u8 pad[SHA256_BLOCK_SIZE];
-	int i;
-
-	sha256_init(sctx);
-	for (i = 0; i < sizeof(pad); i++) {
-		if (i < key_len)
-			pad[i] = key[i];
-		else
-			pad[i] = 0;
-		pad[i] ^= HMAC_IPAD_VALUE;
-	}
-	sha256_update(sctx, pad, sizeof(pad));
-}
-
-static void tpm2_hmac_final(struct sha256_ctx *sctx, u8 *key, u32 key_len,
-			    u8 *out)
-{
-	u8 pad[SHA256_BLOCK_SIZE];
-	int i;
-
-	for (i = 0; i < sizeof(pad); i++) {
-		if (i < key_len)
-			pad[i] = key[i];
-		else
-			pad[i] = 0;
-		pad[i] ^= HMAC_OPAD_VALUE;
-	}
-
-	/* collect the final hash;  use out as temporary storage */
-	sha256_final(sctx, out);
-
-	sha256_init(sctx);
-	sha256_update(sctx, pad, sizeof(pad));
-	sha256_update(sctx, out, SHA256_DIGEST_SIZE);
-	sha256_final(sctx, out);
-}
-
 /*
  * assume hash sha256 and nonces u, v of size SHA256_DIGEST_SIZE but
  * otherwise standard tpm2_KDFa.  Note output is in bytes not bits.
  */
 static void tpm2_KDFa(u8 *key, u32 key_len, const char *label, u8 *u,
@@ -439,20 +393,20 @@ static void tpm2_KDFa(u8 *key, u32 key_len, const char *label, u8 *u,
 {
 	u32 counter = 1;
 	const __be32 bits = cpu_to_be32(bytes * 8);
 
 	while (bytes > 0) {
-		struct sha256_ctx sctx;
+		struct hmac_sha256_ctx hctx;
 		__be32 c = cpu_to_be32(counter);
 
-		tpm2_hmac_init(&sctx, key, key_len);
-		sha256_update(&sctx, (u8 *)&c, sizeof(c));
-		sha256_update(&sctx, label, strlen(label)+1);
-		sha256_update(&sctx, u, SHA256_DIGEST_SIZE);
-		sha256_update(&sctx, v, SHA256_DIGEST_SIZE);
-		sha256_update(&sctx, (u8 *)&bits, sizeof(bits));
-		tpm2_hmac_final(&sctx, key, key_len, out);
+		hmac_sha256_init_usingrawkey(&hctx, key, key_len);
+		hmac_sha256_update(&hctx, (u8 *)&c, sizeof(c));
+		hmac_sha256_update(&hctx, label, strlen(label) + 1);
+		hmac_sha256_update(&hctx, u, SHA256_DIGEST_SIZE);
+		hmac_sha256_update(&hctx, v, SHA256_DIGEST_SIZE);
+		hmac_sha256_update(&hctx, (u8 *)&bits, sizeof(bits));
+		hmac_sha256_final(&hctx, out);
 
 		bytes -= SHA256_DIGEST_SIZE;
 		counter++;
 		out += SHA256_DIGEST_SIZE;
 	}
@@ -592,10 +546,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
 	off_t offset_s = TPM_HEADER_SIZE, offset_p;
 	u8 *hmac = NULL;
 	u32 attrs;
 	u8 cphash[SHA256_DIGEST_SIZE];
 	struct sha256_ctx sctx;
+	struct hmac_sha256_ctx hctx;
 
 	if (!auth)
 		return;
 
 	/* save the command code in BE format */
@@ -703,18 +658,18 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
 		sha256_update(&sctx, &buf->data[offset_s],
 			      tpm_buf_length(buf) - offset_s);
 	sha256_final(&sctx, cphash);
 
 	/* now calculate the hmac */
-	tpm2_hmac_init(&sctx, auth->session_key, sizeof(auth->session_key)
-		       + auth->passphrase_len);
-	sha256_update(&sctx, cphash, sizeof(cphash));
-	sha256_update(&sctx, auth->our_nonce, sizeof(auth->our_nonce));
-	sha256_update(&sctx, auth->tpm_nonce, sizeof(auth->tpm_nonce));
-	sha256_update(&sctx, &auth->attrs, 1);
-	tpm2_hmac_final(&sctx, auth->session_key, sizeof(auth->session_key)
-			+ auth->passphrase_len, hmac);
+	hmac_sha256_init_usingrawkey(&hctx, auth->session_key,
+				     sizeof(auth->session_key) +
+					     auth->passphrase_len);
+	hmac_sha256_update(&hctx, cphash, sizeof(cphash));
+	hmac_sha256_update(&hctx, auth->our_nonce, sizeof(auth->our_nonce));
+	hmac_sha256_update(&hctx, auth->tpm_nonce, sizeof(auth->tpm_nonce));
+	hmac_sha256_update(&hctx, &auth->attrs, 1);
+	hmac_sha256_final(&hctx, hmac);
 }
 EXPORT_SYMBOL(tpm_buf_fill_hmac_session);
 
 /**
  * tpm_buf_check_hmac_response() - check the TPM return HMAC for correctness
@@ -750,10 +705,11 @@ int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
 	struct tpm2_auth *auth = chip->auth;
 	off_t offset_s, offset_p;
 	u8 rphash[SHA256_DIGEST_SIZE];
 	u32 attrs, cc;
 	struct sha256_ctx sctx;
+	struct hmac_sha256_ctx hctx;
 	u16 tag = be16_to_cpu(head->tag);
 	int parm_len, len, i, handles;
 
 	if (!auth)
 		return rc;
@@ -819,19 +775,19 @@ int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
 	sha256_update(&sctx, (u8 *)&auth->ordinal, sizeof(auth->ordinal));
 	sha256_update(&sctx, &buf->data[offset_p], parm_len);
 	sha256_final(&sctx, rphash);
 
 	/* now calculate the hmac */
-	tpm2_hmac_init(&sctx, auth->session_key, sizeof(auth->session_key)
-		       + auth->passphrase_len);
-	sha256_update(&sctx, rphash, sizeof(rphash));
-	sha256_update(&sctx, auth->tpm_nonce, sizeof(auth->tpm_nonce));
-	sha256_update(&sctx, auth->our_nonce, sizeof(auth->our_nonce));
-	sha256_update(&sctx, &auth->attrs, 1);
+	hmac_sha256_init_usingrawkey(&hctx, auth->session_key,
+				     sizeof(auth->session_key) +
+					     auth->passphrase_len);
+	hmac_sha256_update(&hctx, rphash, sizeof(rphash));
+	hmac_sha256_update(&hctx, auth->tpm_nonce, sizeof(auth->tpm_nonce));
+	hmac_sha256_update(&hctx, auth->our_nonce, sizeof(auth->our_nonce));
+	hmac_sha256_update(&hctx, &auth->attrs, 1);
 	/* we're done with the rphash, so put our idea of the hmac there */
-	tpm2_hmac_final(&sctx, auth->session_key, sizeof(auth->session_key)
-			+ auth->passphrase_len, rphash);
+	hmac_sha256_final(&hctx, rphash);
 	if (crypto_memneq(rphash, &buf->data[offset_s], SHA256_DIGEST_SIZE)) {
 		dev_err(&chip->dev, "TPM: HMAC check failed\n");
 		goto out;
 	}
 	rc = 0;
-- 
2.50.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/2] tpm: Compare HMAC values in constant time
  2025-07-31 21:52 ` [PATCH 1/2] tpm: Compare HMAC values in constant time Eric Biggers
@ 2025-08-01  2:28   ` James Bottomley
  2025-08-01  3:02     ` Eric Biggers
  0 siblings, 1 reply; 13+ messages in thread
From: James Bottomley @ 2025-08-01  2:28 UTC (permalink / raw)
  To: Eric Biggers, Peter Huewe, Jarkko Sakkinen, linux-integrity
  Cc: Jason Gunthorpe, linux-crypto, linux-kernel, stable

On Thu, 2025-07-31 at 14:52 -0700, Eric Biggers wrote:
> To prevent timing attacks, HMAC value comparison needs to be constant
> time.  Replace the memcmp() with the correct function,
> crypto_memneq().

Um, OK, I'm all for more security but how could there possibly be a
timing attack in the hmac final comparison code?  All it's doing is
seeing if the HMAC the TPM returns matches the calculated one.  Beyond
this calculation, there's nothing secret about the HMAC key.

Regards,

James


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/2] tpm: Compare HMAC values in constant time
  2025-08-01  2:28   ` James Bottomley
@ 2025-08-01  3:02     ` Eric Biggers
  2025-08-01 11:36       ` James Bottomley
  0 siblings, 1 reply; 13+ messages in thread
From: Eric Biggers @ 2025-08-01  3:02 UTC (permalink / raw)
  To: James Bottomley
  Cc: Peter Huewe, Jarkko Sakkinen, linux-integrity, Jason Gunthorpe,
	linux-crypto, linux-kernel, stable

On Thu, Jul 31, 2025 at 10:28:49PM -0400, James Bottomley wrote:
> On Thu, 2025-07-31 at 14:52 -0700, Eric Biggers wrote:
> > To prevent timing attacks, HMAC value comparison needs to be constant
> > time.  Replace the memcmp() with the correct function,
> > crypto_memneq().
> 
> Um, OK, I'm all for more security but how could there possibly be a
> timing attack in the hmac final comparison code?  All it's doing is
> seeing if the HMAC the TPM returns matches the calculated one.  Beyond
> this calculation, there's nothing secret about the HMAC key.

I'm not sure I understand your question.  Timing attacks on MAC
validation are a well-known issue that can allow a valid MAC to be
guessed without knowing the key.  Whether it's practical in this
particular case for some architecture+compiler+kconfig combination is
another question, but there's no reason not to use the constant-time
comparison function that solves this problem.

Is your claim that in this case the key is public, so the MAC really
just serves as a checksum (and thus the wrong primitive is being used)?

- Eric

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/2] tpm: Compare HMAC values in constant time
  2025-08-01  3:02     ` Eric Biggers
@ 2025-08-01 11:36       ` James Bottomley
  2025-08-01 17:11         ` Eric Biggers
  0 siblings, 1 reply; 13+ messages in thread
From: James Bottomley @ 2025-08-01 11:36 UTC (permalink / raw)
  To: Eric Biggers
  Cc: Peter Huewe, Jarkko Sakkinen, linux-integrity, Jason Gunthorpe,
	linux-crypto, linux-kernel, stable

On Thu, 2025-07-31 at 20:02 -0700, Eric Biggers wrote:
> On Thu, Jul 31, 2025 at 10:28:49PM -0400, James Bottomley wrote:
> > On Thu, 2025-07-31 at 14:52 -0700, Eric Biggers wrote:
> > > To prevent timing attacks, HMAC value comparison needs to be
> > > constant time.  Replace the memcmp() with the correct function,
> > > crypto_memneq().
> > 
> > Um, OK, I'm all for more security but how could there possibly be a
> > timing attack in the hmac final comparison code?  All it's doing is
> > seeing if the HMAC the TPM returns matches the calculated one. 
> > Beyond this calculation, there's nothing secret about the HMAC key.
> 
> I'm not sure I understand your question.  Timing attacks on MAC
> validation are a well-known issue that can allow a valid MAC to be
> guessed without knowing the key.  Whether it's practical in this
> particular case for some architecture+compiler+kconfig combination is
> another question, but there's no reason not to use the constant-time
> comparison function that solves this problem.
> 
> Is your claim that in this case the key is public, so the MAC really
> just serves as a checksum (and thus the wrong primitive is being
> used)?

The keys used for TPM HMAC calculations are all derived from a shared
secret and updating parameters making them one time ones which are
never reused, so there's no benefit to an attacker working out after
the fact what the key was.

Regards,

James


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/2] tpm: Compare HMAC values in constant time
  2025-08-01 11:36       ` James Bottomley
@ 2025-08-01 17:11         ` Eric Biggers
  2025-08-01 18:03           ` James Bottomley
  0 siblings, 1 reply; 13+ messages in thread
From: Eric Biggers @ 2025-08-01 17:11 UTC (permalink / raw)
  To: James Bottomley
  Cc: Peter Huewe, Jarkko Sakkinen, linux-integrity, Jason Gunthorpe,
	linux-crypto, linux-kernel, stable

On Fri, Aug 01, 2025 at 07:36:02AM -0400, James Bottomley wrote:
> On Thu, 2025-07-31 at 20:02 -0700, Eric Biggers wrote:
> > On Thu, Jul 31, 2025 at 10:28:49PM -0400, James Bottomley wrote:
> > > On Thu, 2025-07-31 at 14:52 -0700, Eric Biggers wrote:
> > > > To prevent timing attacks, HMAC value comparison needs to be
> > > > constant time.  Replace the memcmp() with the correct function,
> > > > crypto_memneq().
> > > 
> > > Um, OK, I'm all for more security but how could there possibly be a
> > > timing attack in the hmac final comparison code?  All it's doing is
> > > seeing if the HMAC the TPM returns matches the calculated one. 
> > > Beyond this calculation, there's nothing secret about the HMAC key.
> > 
> > I'm not sure I understand your question.  Timing attacks on MAC
> > validation are a well-known issue that can allow a valid MAC to be
> > guessed without knowing the key.  Whether it's practical in this
> > particular case for some architecture+compiler+kconfig combination is
> > another question, but there's no reason not to use the constant-time
> > comparison function that solves this problem.
> > 
> > Is your claim that in this case the key is public, so the MAC really
> > just serves as a checksum (and thus the wrong primitive is being
> > used)?
> 
> The keys used for TPM HMAC calculations are all derived from a shared
> secret and updating parameters making them one time ones which are
> never reused, so there's no benefit to an attacker working out after
> the fact what the key was.

MAC timing attacks forge MACs; they don't leak the key.

It's true that such attacks don't work with one-time keys.  But here
it's not necessarily a one-time key.  E.g., tpm2_get_random() sets a
key, then authenticates multiple messages using that key.

I guses I'm struggling to understand the point of your comments.  Even
if in a follow-up message you're finally able to present a correct
argument for why memcmp() is okay, it's clearly subtle enough that we
should just use crypto_memneq() anyway, just like everywhere else in the
kernel that validates MACs.  If you're worried about performance, you
shouldn't be: it's a negligible difference that is far outweighed by all
the optimizations I've been making to lib/crypto/.

- Eric

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/2] tpm: Compare HMAC values in constant time
  2025-08-01 17:11         ` Eric Biggers
@ 2025-08-01 18:03           ` James Bottomley
  2025-08-01 18:40             ` Eric Biggers
  0 siblings, 1 reply; 13+ messages in thread
From: James Bottomley @ 2025-08-01 18:03 UTC (permalink / raw)
  To: Eric Biggers
  Cc: Peter Huewe, Jarkko Sakkinen, linux-integrity, Jason Gunthorpe,
	linux-crypto, linux-kernel, stable

On Fri, 2025-08-01 at 10:11 -0700, Eric Biggers wrote:
> On Fri, Aug 01, 2025 at 07:36:02AM -0400, James Bottomley wrote:
> > On Thu, 2025-07-31 at 20:02 -0700, Eric Biggers wrote:
> > > On Thu, Jul 31, 2025 at 10:28:49PM -0400, James Bottomley wrote:
> > > > On Thu, 2025-07-31 at 14:52 -0700, Eric Biggers wrote:
> > > > > To prevent timing attacks, HMAC value comparison needs to be
> > > > > constant time.  Replace the memcmp() with the correct
> > > > > function, crypto_memneq().
> > > > 
> > > > Um, OK, I'm all for more security but how could there possibly
> > > > be a timing attack in the hmac final comparison code?  All it's
> > > > doing is seeing if the HMAC the TPM returns matches the
> > > > calculated one.  Beyond this calculation, there's nothing
> > > > secret about the HMAC key.
> > > 
> > > I'm not sure I understand your question.  Timing attacks on MAC
> > > validation are a well-known issue that can allow a valid MAC to
> > > be guessed without knowing the key.  Whether it's practical in
> > > this particular case for some architecture+compiler+kconfig
> > > combination is another question, but there's no reason not to use
> > > the constant-time comparison function that solves this problem.
> > > 
> > > Is your claim that in this case the key is public, so the MAC
> > > really just serves as a checksum (and thus the wrong primitive is
> > > being used)?
> > 
> > The keys used for TPM HMAC calculations are all derived from a
> > shared secret and updating parameters making them one time ones
> > which are never reused, so there's no benefit to an attacker
> > working out after the fact what the key was.
> 
> MAC timing attacks forge MACs; they don't leak the key.

> It's true that such attacks don't work with one-time keys.  But here
> it's not necessarily a one-time key.  E.g., tpm2_get_random() sets a
> key, then authenticates multiple messages using that key.

The nonces come one from us and one from the TPM.  I think ours doesn't
change if the session is continued although it could, whereas the TPM
one does, so the HMAC key is different for every communication of a
continued session.

> I guses I'm struggling to understand the point of your comments.

Your commit message, still quoted above, begins "To prevent timing
attacks ..." but I still don't think there are any viable timing
attacks against this code.  However, that statement gives the idea that
it's fixing a crypto vulnerablility and thus is going to excite the AI
based CVE producers.

>   Even if in a follow-up message you're finally able to present a
> correct argument for why memcmp() is okay, it's clearly subtle enough
> that we should just use crypto_memneq() anyway, just like everywhere
> else in the kernel that validates MACs.  If you're worried about
> performance, you shouldn't be: it's a negligible difference that is
> far outweighed by all the optimizations I've been making to
> lib/crypto/.

So if you change the justification to something like "crypto people
would like to update hmac compares to be constant time everywhere to
avoid having to check individual places for correctness" I think I'd be
happy.

Regards,

James


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/2] tpm: Compare HMAC values in constant time
  2025-08-01 18:03           ` James Bottomley
@ 2025-08-01 18:40             ` Eric Biggers
  2025-08-01 18:53               ` James Bottomley
  0 siblings, 1 reply; 13+ messages in thread
From: Eric Biggers @ 2025-08-01 18:40 UTC (permalink / raw)
  To: James Bottomley
  Cc: Peter Huewe, Jarkko Sakkinen, linux-integrity, Jason Gunthorpe,
	linux-crypto, linux-kernel, stable

On Fri, Aug 01, 2025 at 02:03:47PM -0400, James Bottomley wrote:
> On Fri, 2025-08-01 at 10:11 -0700, Eric Biggers wrote:
> > On Fri, Aug 01, 2025 at 07:36:02AM -0400, James Bottomley wrote:
> > > On Thu, 2025-07-31 at 20:02 -0700, Eric Biggers wrote:
> > > > On Thu, Jul 31, 2025 at 10:28:49PM -0400, James Bottomley wrote:
> > > > > On Thu, 2025-07-31 at 14:52 -0700, Eric Biggers wrote:
> > > > > > To prevent timing attacks, HMAC value comparison needs to be
> > > > > > constant time.  Replace the memcmp() with the correct
> > > > > > function, crypto_memneq().
> > > > > 
> > > > > Um, OK, I'm all for more security but how could there possibly
> > > > > be a timing attack in the hmac final comparison code?  All it's
> > > > > doing is seeing if the HMAC the TPM returns matches the
> > > > > calculated one.  Beyond this calculation, there's nothing
> > > > > secret about the HMAC key.
> > > > 
> > > > I'm not sure I understand your question.  Timing attacks on MAC
> > > > validation are a well-known issue that can allow a valid MAC to
> > > > be guessed without knowing the key.  Whether it's practical in
> > > > this particular case for some architecture+compiler+kconfig
> > > > combination is another question, but there's no reason not to use
> > > > the constant-time comparison function that solves this problem.
> > > > 
> > > > Is your claim that in this case the key is public, so the MAC
> > > > really just serves as a checksum (and thus the wrong primitive is
> > > > being used)?
> > > 
> > > The keys used for TPM HMAC calculations are all derived from a
> > > shared secret and updating parameters making them one time ones
> > > which are never reused, so there's no benefit to an attacker
> > > working out after the fact what the key was.
> > 
> > MAC timing attacks forge MACs; they don't leak the key.
> 
> > It's true that such attacks don't work with one-time keys.  But here
> > it's not necessarily a one-time key.  E.g., tpm2_get_random() sets a
> > key, then authenticates multiple messages using that key.
> 
> The nonces come one from us and one from the TPM.  I think ours doesn't
> change if the session is continued although it could, whereas the TPM
> one does, so the HMAC key is different for every communication of a
> continued session.

Again, tpm2_get_random() sets a HMAC key once and then uses it multiple
times.

> > I guses I'm struggling to understand the point of your comments.
> 
> Your commit message, still quoted above, begins "To prevent timing
> attacks ..." but I still don't think there are any viable timing
> attacks against this code.  However, that statement gives the idea that
> it's fixing a crypto vulnerablility and thus is going to excite the AI
> based CVE producers.
> 
> >   Even if in a follow-up message you're finally able to present a
> > correct argument for why memcmp() is okay, it's clearly subtle enough
> > that we should just use crypto_memneq() anyway, just like everywhere
> > else in the kernel that validates MACs.  If you're worried about
> > performance, you shouldn't be: it's a negligible difference that is
> > far outweighed by all the optimizations I've been making to
> > lib/crypto/.
> 
> So if you change the justification to something like "crypto people
> would like to update hmac compares to be constant time everywhere to
> avoid having to check individual places for correctness" I think I'd be
> happy.

Sure, provided that memcmp() is actually secure here.  So far, it hasn't
been particularly convincing when each argument you've given for it
being secure has been incorrect.

But I do see that each call to tpm_buf_check_hmac_response() is paired
with a call to tpm_buf_append_hmac_session() which generates a fresh
nonce.  That nonce is then sent to the other endpoint (the one that
claims to be a TPM) and then implicitly becomes part of the response
message (but is not explicitly transmitted back in it).  That may be the
real reason: messages are guaranteed to not be repeated, so a MAC timing
attack can't be done.  Do you agree that is the actual reason?

- Eric

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/2] tpm: Compare HMAC values in constant time
  2025-08-01 18:40             ` Eric Biggers
@ 2025-08-01 18:53               ` James Bottomley
  2025-08-01 19:03                 ` Eric Biggers
  0 siblings, 1 reply; 13+ messages in thread
From: James Bottomley @ 2025-08-01 18:53 UTC (permalink / raw)
  To: Eric Biggers
  Cc: Peter Huewe, Jarkko Sakkinen, linux-integrity, Jason Gunthorpe,
	linux-crypto, linux-kernel, stable

On Fri, 2025-08-01 at 11:40 -0700, Eric Biggers wrote:
> On Fri, Aug 01, 2025 at 02:03:47PM -0400, James Bottomley wrote:
> > On Fri, 2025-08-01 at 10:11 -0700, Eric Biggers wrote:
[...]
> > > It's true that such attacks don't work with one-time keys.  But
> > > here it's not necessarily a one-time key.  E.g.,
> > > tpm2_get_random() sets a key, then authenticates multiple
> > > messages using that key.
> > 
> > The nonces come one from us and one from the TPM.  I think ours
> > doesn't change if the session is continued although it could,
> > whereas the TPM one does, so the HMAC key is different for every
> > communication of a continued session.
> 
> Again, tpm2_get_random() sets a HMAC key once and then uses it
> multiple times.

No it doesn't.  If you actually read the code, you'd find it does what
I say above.  Specifically  tpm_buf_fill_hmac_session() which is called
inside that loop recalculates the hmac key from the nonces.  This
recalculated key is what is used in tpm_buf_check_hmac_response(), and
which is where the new tpm nonce is collected for the next iteration.

Regards,

James


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/2] tpm: Compare HMAC values in constant time
  2025-08-01 18:53               ` James Bottomley
@ 2025-08-01 19:03                 ` Eric Biggers
  2025-08-01 19:20                   ` James Bottomley
  0 siblings, 1 reply; 13+ messages in thread
From: Eric Biggers @ 2025-08-01 19:03 UTC (permalink / raw)
  To: James Bottomley
  Cc: Peter Huewe, Jarkko Sakkinen, linux-integrity, Jason Gunthorpe,
	linux-crypto, linux-kernel, stable

On Fri, Aug 01, 2025 at 02:53:09PM -0400, James Bottomley wrote:
> On Fri, 2025-08-01 at 11:40 -0700, Eric Biggers wrote:
> > On Fri, Aug 01, 2025 at 02:03:47PM -0400, James Bottomley wrote:
> > > On Fri, 2025-08-01 at 10:11 -0700, Eric Biggers wrote:
> [...]
> > > > It's true that such attacks don't work with one-time keys.  But
> > > > here it's not necessarily a one-time key.  E.g.,
> > > > tpm2_get_random() sets a key, then authenticates multiple
> > > > messages using that key.
> > > 
> > > The nonces come one from us and one from the TPM.  I think ours
> > > doesn't change if the session is continued although it could,
> > > whereas the TPM one does, so the HMAC key is different for every
> > > communication of a continued session.
> > 
> > Again, tpm2_get_random() sets a HMAC key once and then uses it
> > multiple times.
> 
> No it doesn't.  If you actually read the code, you'd find it does what
> I say above.  Specifically  tpm_buf_fill_hmac_session() which is called
> inside that loop recalculates the hmac key from the nonces.  This
> recalculated key is what is used in tpm_buf_check_hmac_response(), and
> which is where the new tpm nonce is collected for the next iteration.

tpm_buf_fill_hmac_session() computes a HMAC value, but it doesn't modify
the HMAC key.  tpm2_parse_start_auth_session() is the only place where
the HMAC key is changed.  You may be confusing HMAC values with keys.

- Eric

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/2] tpm: Compare HMAC values in constant time
  2025-08-01 19:03                 ` Eric Biggers
@ 2025-08-01 19:20                   ` James Bottomley
  2025-08-01 20:14                     ` Eric Biggers
  0 siblings, 1 reply; 13+ messages in thread
From: James Bottomley @ 2025-08-01 19:20 UTC (permalink / raw)
  To: Eric Biggers
  Cc: Peter Huewe, Jarkko Sakkinen, linux-integrity, Jason Gunthorpe,
	linux-crypto, linux-kernel, stable

On Fri, 2025-08-01 at 12:03 -0700, Eric Biggers wrote:
> On Fri, Aug 01, 2025 at 02:53:09PM -0400, James Bottomley wrote:
> > On Fri, 2025-08-01 at 11:40 -0700, Eric Biggers wrote:
> > > On Fri, Aug 01, 2025 at 02:03:47PM -0400, James Bottomley wrote:
> > > > On Fri, 2025-08-01 at 10:11 -0700, Eric Biggers wrote:
> > [...]
> > > > > It's true that such attacks don't work with one-time keys. 
> > > > > But here it's not necessarily a one-time key.  E.g.,
> > > > > tpm2_get_random() sets a key, then authenticates multiple
> > > > > messages using that key.
> > > > 
> > > > The nonces come one from us and one from the TPM.  I think ours
> > > > doesn't change if the session is continued although it could,
> > > > whereas the TPM one does, so the HMAC key is different for
> > > > every communication of a continued session.
> > > 
> > > Again, tpm2_get_random() sets a HMAC key once and then uses it
> > > multiple times.
> > 
> > No it doesn't.  If you actually read the code, you'd find it does
> > what I say above.  Specifically  tpm_buf_fill_hmac_session() which
> > is called inside that loop recalculates the hmac key from the
> > nonces.  This recalculated key is what is used in
> > tpm_buf_check_hmac_response(), and which is where the new tpm nonce
> > is collected for the next
> > iteration.
> 
> tpm_buf_fill_hmac_session() computes a HMAC value, but it doesn't
> modify the HMAC key.  tpm2_parse_start_auth_session() is the only
> place where the HMAC key is changed.  You may be confusing HMAC
> values with keys.

Is this simply a semantic quibble about what gets called a key?  For
each TPM command we compute a cphash across all the command parameters
(and for each return a rphash).  This hash then forms a
hmac(session_key, cphash | our_nonce | tpm_nonce | attrs).  The point
being that although session_key is fixed across the session, the
our_nonce and tpm_nonce can change with every iteration.  Since the
cphash is over the ciphertext, it's the only bit you get to vary with a
chosen ciphertext attack, so the other parameters effectively key the
hmac.

Regards,

James


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/2] tpm: Compare HMAC values in constant time
  2025-08-01 19:20                   ` James Bottomley
@ 2025-08-01 20:14                     ` Eric Biggers
  0 siblings, 0 replies; 13+ messages in thread
From: Eric Biggers @ 2025-08-01 20:14 UTC (permalink / raw)
  To: James Bottomley
  Cc: Peter Huewe, Jarkko Sakkinen, linux-integrity, Jason Gunthorpe,
	linux-crypto, linux-kernel, stable

On Fri, Aug 01, 2025 at 03:20:52PM -0400, James Bottomley wrote:
> On Fri, 2025-08-01 at 12:03 -0700, Eric Biggers wrote:
> > On Fri, Aug 01, 2025 at 02:53:09PM -0400, James Bottomley wrote:
> > > On Fri, 2025-08-01 at 11:40 -0700, Eric Biggers wrote:
> > > > On Fri, Aug 01, 2025 at 02:03:47PM -0400, James Bottomley wrote:
> > > > > On Fri, 2025-08-01 at 10:11 -0700, Eric Biggers wrote:
> > > [...]
> > > > > > It's true that such attacks don't work with one-time keys. 
> > > > > > But here it's not necessarily a one-time key.  E.g.,
> > > > > > tpm2_get_random() sets a key, then authenticates multiple
> > > > > > messages using that key.
> > > > > 
> > > > > The nonces come one from us and one from the TPM.  I think ours
> > > > > doesn't change if the session is continued although it could,
> > > > > whereas the TPM one does, so the HMAC key is different for
> > > > > every communication of a continued session.
> > > > 
> > > > Again, tpm2_get_random() sets a HMAC key once and then uses it
> > > > multiple times.
> > > 
> > > No it doesn't.  If you actually read the code, you'd find it does
> > > what I say above.  Specifically  tpm_buf_fill_hmac_session() which
> > > is called inside that loop recalculates the hmac key from the
> > > nonces.  This recalculated key is what is used in
> > > tpm_buf_check_hmac_response(), and which is where the new tpm nonce
> > > is collected for the next
> > > iteration.
> > 
> > tpm_buf_fill_hmac_session() computes a HMAC value, but it doesn't
> > modify the HMAC key.  tpm2_parse_start_auth_session() is the only
> > place where the HMAC key is changed.  You may be confusing HMAC
> > values with keys.
> 
> Is this simply a semantic quibble about what gets called a key?  For
> each TPM command we compute a cphash across all the command parameters
> (and for each return a rphash).  This hash then forms a
> hmac(session_key, cphash | our_nonce | tpm_nonce | attrs).  The point
> being that although session_key is fixed across the session, the
> our_nonce and tpm_nonce can change with every iteration.  Since the
> cphash is over the ciphertext, it's the only bit you get to vary with a
> chosen ciphertext attack, so the other parameters effectively key the
> hmac.

No, it's not "simply a semantic quibble".  You're just wrong.

As I said earlier, our_nonce (which is not a key) does appear to make
MAC timing attacks not possible.  All the other fields appear to be
attacker-controlled, contrary to what you're claiming above.

Anyway, point taken: I'll drop the Fixes and Cc stable from the commit,
and include my own analysis of why MAC timing attacks don't appear to be
possible with this protocol.  Everything else in this thread has just
been a pointless distraction.

- Eric

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2025-08-01 20:15 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-31 21:52 [PATCH 0/2] tpm: HMAC fix and cleanup Eric Biggers
2025-07-31 21:52 ` [PATCH 1/2] tpm: Compare HMAC values in constant time Eric Biggers
2025-08-01  2:28   ` James Bottomley
2025-08-01  3:02     ` Eric Biggers
2025-08-01 11:36       ` James Bottomley
2025-08-01 17:11         ` Eric Biggers
2025-08-01 18:03           ` James Bottomley
2025-08-01 18:40             ` Eric Biggers
2025-08-01 18:53               ` James Bottomley
2025-08-01 19:03                 ` Eric Biggers
2025-08-01 19:20                   ` James Bottomley
2025-08-01 20:14                     ` Eric Biggers
2025-07-31 21:52 ` [PATCH 2/2] tpm: Use HMAC-SHA256 library instead of open-coded HMAC Eric Biggers

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).