[RFC PATCH] tpm: Allow the TPM2 pcr_extend HMAC capability to be disabled on boot

Mimi Zohar
2024-10-15T19:39:45Z

The initial TPM2 HMAC session capability added HMAC authentication to
each and every TPM communication making the pcr_extend performance
abysmal for HW TPMs. Further, the new CONFIG_TCG_TPM2_HMAC option was
configured by default on x86_64.

The decision to use the TPM2 HMAC session capability feature doesn't
differentiate between the critical encrypted and the non-encrypted
communication, but when configured is required for all TPM communication.

In addition, the reason to HMAC the tpm2_pcr_extend() as provided in commit
6519fea6fd37 ("tpm: add hmac checks to tpm2_pcr_extend()") was to protect
tpm2_pcr_extend() when used by "trusted keys" to lock the PCR. However,
locking the PCR is currently limited to TPM 1.2.

We can revert the commit which adds the HMAC sessions for
tpm2_pcr_extend, allow just the TPM2 pcr_extend HMAC capability to be
disabled on boot for better IMA performance, or define a generic boot
command line option to disable HMAC in general. This patch allows
disabling the HMAC for just the TPM2_pcr_extend.

Fixes: 6519fea6fd37 ("tpm: add hmac checks to tpm2_pcr_extend()")
Co-developed-by: Roberto Sassu <roberto.sassu@huawei.com>
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
---
Comment: applied and tested with/without patches in Jarkko's hmac-v5 branch -
commit 92999f9cd11f ("tpm: flush the auth session only when /dev/tpm0 is open")

.../admin-guide/kernel-parameters.txt | 5 ++
drivers/char/tpm/tpm2-cmd.c | 41 ++++++++++---
drivers/char/tpm/tpm2-sessions.c | 59 +++++++++++--------
include/linux/tpm.h | 4 ++
4 files changed, 77 insertions(+), 32 deletions(-)

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 1518343bbe22..c7811f32ba28 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -6727,6 +6727,11 @@
torture.verbose_sleep_duration= [KNL]
Duration of each verbose-printk() sleep in jiffies.

+ tpm_pcr_extend_hmac_disable [HW,TPM]
+ Disable TPM2 pcr_extend HMAC for better IMA
+ performance. By default is set to true (1).
+ Mainly needed when using a HW TPM2.
+
tpm_suspend_pcr=[HW,TPM]
Format: integer pcr id
Specify that at suspend time, the tpm driver
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index aba024cbe7c5..bac409520a72 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -14,6 +14,14 @@
#include "tpm.h"
#include <crypto/hash_info.h>

+static int __ro_after_init tpm2_pcr_extend_hmac = 1;
+static int __init tpm2_pcr_extend_hmac_setup(char *str)
+{
+ tpm2_pcr_extend_hmac = 0;
+ return 0;
+}
+__setup("tpm2_pcr_extend_hmac_disable", tpm2_pcr_extend_hmac_setup);
+
static struct tpm2_hash tpm2_hash_map[] = {
{HASH_ALGO_SHA1, TPM_ALG_SHA1},
{HASH_ALGO_SHA256, TPM_ALG_SHA256},
@@ -232,18 +240,26 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
int rc;
int i;

- rc = tpm2_start_auth_session(chip);
- if (rc)
- return rc;
+ if (tpm2_pcr_extend_hmac) {
+ rc = tpm2_start_auth_session(chip);
+ if (rc)
+ return rc;
+ }

rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
if (rc) {
- tpm2_end_auth_session(chip);
+ if (tpm2_pcr_extend_hmac)
+ tpm2_end_auth_session(chip);
return rc;
}

- tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
- tpm_buf_append_hmac_session(chip, &buf, 0, NULL, 0);
+ if (tpm2_pcr_extend_hmac) {
+ tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
+ tpm_buf_append_hmac_session(chip, &buf, 0, NULL, 0);
+ } else {
+ tpm_buf_append_handle(chip, &buf, pcr_idx, NULL);
+ tpm_buf_append_auth(chip, &buf, 0, NULL, 0);
+ }

tpm_buf_append_u32(&buf, chip->nr_allocated_banks);

@@ -253,9 +269,16 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
chip->allocated_banks[i].digest_size);
}

- tpm_buf_fill_hmac_session(chip, &buf);
- rc = tpm_transmit_cmd(chip, &buf, 0, "attempting extend a PCR value");
- rc = tpm_buf_check_hmac_response(chip, &buf, rc);
+ if (tpm2_pcr_extend_hmac) {
+ tpm_buf_fill_hmac_session(chip, &buf);
+ rc = tpm_transmit_cmd(chip, &buf, 0,
+ "attempting extend a PCR value");
+ rc = tpm_buf_check_hmac_response(chip, &buf, rc);
+ } else {
+ rc = tpm_transmit_cmd(chip, &buf, 0,
+ "attempting extend a PCR value");
+ }
+

tpm_buf_destroy(&buf);

diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index de860773eead..fae56dfe0d92 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -205,6 +205,14 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, char *name)
}
#endif /* CONFIG_TCG_TPM2_HMAC */

+void tpm_buf_append_handle(struct tpm_chip *chip, struct tpm_buf *buf,
+ u32 handle, u8 *name)
+{
+ tpm_buf_append_u32(buf, handle);
+ /* count the number of handles in the upper bits of flags */
+ buf->handles++;
+}
+
/**
* tpm_buf_append_name() - add a handle area to the buffer
* @chip: the TPM chip structure
@@ -237,9 +245,7 @@ void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
#endif

if (!tpm2_chip_auth(chip)) {
- tpm_buf_append_u32(buf, handle);
- /* count the number of handles in the upper bits of flags */
- buf->handles++;
+ tpm_buf_append_handle(chip, buf, handle, name);
return;
}

@@ -272,6 +278,31 @@ void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
}
EXPORT_SYMBOL_GPL(tpm_buf_append_name);

+void tpm_buf_append_auth(struct tpm_chip *chip, struct tpm_buf *buf,
+ u8 attributes, u8 *passphrase, int passphrase_len)
+{
+ /* offset tells us where the sessions area begins */
+ int offset = buf->handles * 4 + TPM_HEADER_SIZE;
+ u32 len = 9 + passphrase_len;
+
+ if (tpm_buf_length(buf) != offset) {
+ /* not the first session so update the existing length */
+ len += get_unaligned_be32(&buf->data[offset]);
+ put_unaligned_be32(len, &buf->data[offset]);
+ } else {
+ tpm_buf_append_u32(buf, len);
+ }
+ /* auth handle */
+ tpm_buf_append_u32(buf, TPM2_RS_PW);
+ /* nonce */
+ tpm_buf_append_u16(buf, 0);
+ /* attributes */
+ tpm_buf_append_u8(buf, 0);
+ /* passphrase */
+ tpm_buf_append_u16(buf, passphrase_len);
+ tpm_buf_append(buf, passphrase, passphrase_len);
+}
+
/**
* tpm_buf_append_hmac_session() - Append a TPM session element
* @chip: the TPM chip structure
@@ -309,26 +340,8 @@ void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
#endif

if (!tpm2_chip_auth(chip)) {
- /* offset tells us where the sessions area begins */
- int offset = buf->handles * 4 + TPM_HEADER_SIZE;
- u32 len = 9 + passphrase_len;
-
- if (tpm_buf_length(buf) != offset) {
- /* not the first session so update the existing length */
- len += get_unaligned_be32(&buf->data[offset]);
- put_unaligned_be32(len, &buf->data[offset]);
- } else {
- tpm_buf_append_u32(buf, len);
- }
- /* auth handle */
- tpm_buf_append_u32(buf, TPM2_RS_PW);
- /* nonce */
- tpm_buf_append_u16(buf, 0);
- /* attributes */
- tpm_buf_append_u8(buf, 0);
- /* passphrase */
- tpm_buf_append_u16(buf, passphrase_len);
- tpm_buf_append(buf, passphrase, passphrase_len);
+ tpm_buf_append_auth(chip, buf, attributes, passphrase,
+ passphrase_len);
return;
}

diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 28a932aa0416..d30bb1c114f1 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -504,9 +504,13 @@ static inline struct tpm2_auth *tpm2_chip_auth(struct tpm_chip *chip)

void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
u32 handle, u8 *name);
+void tpm_buf_append_handle(struct tpm_chip *chip, struct tpm_buf *buf,
+ u32 handle, u8 *name);
void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
u8 attributes, u8 *passphrase,
int passphraselen);
+void tpm_buf_append_auth(struct tpm_chip *chip, struct tpm_buf *buf,
+ u8 attributes, u8 *passphrase, int passphraselen);
static inline void tpm_buf_append_hmac_session_opt(struct tpm_chip *chip,
struct tpm_buf *buf,
u8 attributes,
--
2.47.0