[PATCH v3 7/7] tpm: Send only one TPM command per hwrng request

Jarkko Sakkinen
2025-12-10T14:40:23Z

hwrng framework does not have a enforce that all bytes requested need to be
read. By enforcing such a requirement internally, TPM driver can cause
unpredicatability in latency, as a single tpm_get_random() call can result
multiple TPM commands.

Especially, when TCG_TPM2_HMAC is enabled, roundtrips with the TPM should
be capped to exactly one.

Add a @wait parameter to enforce this behavior and set it to true at the
call sites TPM 1.2 keys. At the call sites of hwrng, set @wait to false.

Cc: David S. Miller <davem@davemloft.net>
Cc: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
drivers/char/tpm/tpm-chip.c | 2 +-
drivers/char/tpm/tpm-interface.c | 11 +++++++++--
include/linux/tpm.h | 2 +-
security/keys/trusted-keys/trusted_tpm1.c | 8 ++++----
4 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index 082b910ddf0d..8fca4373e2df 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -494,7 +494,7 @@ static int tpm_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait)
{
struct tpm_chip *chip = container_of(rng, struct tpm_chip, hwrng);

- return tpm_get_random(chip, data, max);
+ return tpm_get_random(chip, data, max, false);
}

static bool tpm_is_hwrng_enabled(struct tpm_chip *chip)
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index ab52a1cb0a78..5cc2bbabd57a 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -604,9 +604,11 @@ static int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max)
* @chip: A &tpm_chip instance. Whenset to %NULL, the default chip is used.
* @out: Destination buffer for the acquired random bytes.
* @max: The maximum number of bytes to write to @out.
+ * @wait: Set to true when all of the @max bytes need to be acquired.
*
* Iterates pulling more bytes from TPM up until all of the @max bytes have been
- * received.
+ * received, when @wait it sets true. Otherwise, the queries for @max bytes from
+ * TPM exactly once, and returns the bytes that were received.
*
* Returns the number of random bytes read on success.
* Returns -EINVAL when @out is NULL, or @max is not between zero and
@@ -614,7 +616,7 @@ static int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max)
* Returns tpm_transmit_cmd() error codes when the TPM command results an
* error.
*/
-int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max)
+int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max, bool wait)
{
u32 num_bytes = max;
u8 *out_ptr = out;
@@ -647,6 +649,11 @@ int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max)
if (rc < 0)
goto err;

+ if (!wait) {
+ total = rc;
+ break;
+ }
+
out_ptr += rc;
total += rc;
num_bytes -= rc;
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index e68995df8796..177833d6b965 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -485,7 +485,7 @@ extern int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
struct tpm_digest *digest);
extern int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
struct tpm_digest *digests);
-extern int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max);
+int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max, bool wait);
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);
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index 759c1ecb0435..f36f6a0b533f 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -361,7 +361,7 @@ static int osap(struct tpm_buf *tb, struct osapsess *s,
unsigned char ononce[TPM_NONCE_SIZE];
int ret;

- ret = tpm_get_random(chip, ononce, TPM_NONCE_SIZE);
+ ret = tpm_get_random(chip, ononce, TPM_NONCE_SIZE, true);
if (ret < 0)
return ret;

@@ -454,7 +454,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype,
memcpy(td->xorwork + SHA1_DIGEST_SIZE, sess.enonce, SHA1_DIGEST_SIZE);
sha1(td->xorwork, SHA1_DIGEST_SIZE * 2, td->xorhash);

- ret = tpm_get_random(chip, td->nonceodd, TPM_NONCE_SIZE);
+ ret = tpm_get_random(chip, td->nonceodd, TPM_NONCE_SIZE, true);
if (ret < 0)
goto out;

@@ -565,7 +565,7 @@ static int tpm_unseal(struct tpm_buf *tb,
}

ordinal = htonl(TPM_ORD_UNSEAL);
- ret = tpm_get_random(chip, nonceodd, TPM_NONCE_SIZE);
+ ret = tpm_get_random(chip, nonceodd, TPM_NONCE_SIZE, true);
if (ret < 0)
return ret;

@@ -938,7 +938,7 @@ static int trusted_tpm_unseal(struct trusted_key_payload *p, char *datablob)

static int trusted_tpm_get_random(unsigned char *key, size_t key_len)
{
- return tpm_get_random(chip, key, key_len);
+ return tpm_get_random(chip, key, key_len, true);
}

static int __init init_digests(void)
--
2.52.0