Linux Integrity Measurement development
 help / color / mirror / Atom feed
* [PATCH] tpm: Use kfree_sensitive() to free auth session in tpm_dev_release()
From: Gunnar Kudrjavets @ 2026-04-09 17:20 UTC (permalink / raw)
  To: peterhuewe, jarkko
  Cc: jgg, James.Bottomley, ardb, linux-integrity, linux-kernel,
	Justinien Bouron

tpm_dev_release() uses plain kfree() to free chip->auth, which contains
sensitive cryptographic material including HMAC session keys, nonces,
and passphrase data (struct tpm2_auth).

Every other code path that frees this structure uses kfree_sensitive()
to zero the memory before releasing it: both tpm2_end_auth_session()
and tpm_buf_check_hmac_response() do so. The tpm_dev_release() path
is the only one that does not, leaving key material in freed slab
memory until it is eventually overwritten.

Use kfree_sensitive() for consistency with the rest of the driver and
to ensure session keys are scrubbed during device teardown.

Fixes: 699e3efd6c64 ("tpm: Add HMAC session start and end functions")
Signed-off-by: Gunnar Kudrjavets <gunnarku@amazon.com>
Reviewed-by: Justinien Bouron <jbouron@amazon.com>
---
 drivers/char/tpm/tpm-chip.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index 082b910ddf0d..17d9d71774ec 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -247,7 +247,7 @@ static void tpm_dev_release(struct device *dev)
 	kfree(chip->work_space.context_buf);
 	kfree(chip->work_space.session_buf);
 #ifdef CONFIG_TCG_TPM2_HMAC
-	kfree(chip->auth);
+	kfree_sensitive(chip->auth);
 #endif
 	kfree(chip);
 }

base-commit: 03e5553f5fb99cb47c315e167a604a9c69e6f724
-- 
2.47.3


^ permalink raw reply related

* [PATCH v3] KEYS: trusted: Debugging as a feature
From: Jarkko Sakinen @ 2026-04-09 16:07 UTC (permalink / raw)
  To: linux-integrity, keyrings
  Cc: Jarkko Sakkinen, Srish Srinivasan, Nayna Jain, James Bottomley,
	Mimi Zohar, David Howells, Paul Moore, James Morris,
	Serge E. Hallyn, Ahmad Fatoum, Pengutronix Kernel Team,
	linux-kernel, linux-security-module

From: Jarkko Sakkinen <jarkko@kernel.org>

TPM_DEBUG, and other similar flags, are a non-standard way to specify a
feature in Linux kernel. Introduce CONFIG_TRUSTED_KEYS_DEBUG for trusted
keys, and use it to replace these ad-hoc feature flags.

Given that trusted keys debug dumps can contain sensitive data, harden the
feature as follows:

1. In the Kconfig description postulate that pr_debug() statements must be
   used.
2. Use pr_debug() statements in TPM 1.x driver to print the protocol dump.
3. Require trusted.debug=1 on the kernel command line (default: 0) to
   activate dumps at runtime, even when CONFIG_TRUSTED_KEYS_DEBUG=y.

Traces, when actually needed, can be easily enabled by providing
trusted.dyndbg='+p' and trusted.debug=1 in the kernel command-line.

Cc: Srish Srinivasan <ssrish@linux.ibm.com>
Reported-by: Nayna Jain <nayna@linux.ibm.com>
Closes: https://lore.kernel.org/all/7f8b8478-5cd8-4d97-bfd0-341fd5cf10f9@linux.ibm.com/
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v3:
- Add kernel-command line option for enabling the traces.
- Add safety information to the Kconfig entry.
v2:
- Implement for all trusted keys backends.
- Add HAVE_TRUSTED_KEYS_DEBUG as it is a good practice despite full
  coverage.
---
 include/keys/trusted-type.h               | 21 ++++++-----
 security/keys/trusted-keys/Kconfig        | 23 ++++++++++++
 security/keys/trusted-keys/trusted_caam.c |  7 ++--
 security/keys/trusted-keys/trusted_core.c |  6 ++++
 security/keys/trusted-keys/trusted_tpm1.c | 44 +++++++++++++----------
 5 files changed, 71 insertions(+), 30 deletions(-)

diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
index 03527162613f..9f9940482da4 100644
--- a/include/keys/trusted-type.h
+++ b/include/keys/trusted-type.h
@@ -83,18 +83,21 @@ struct trusted_key_source {
 
 extern struct key_type key_type_trusted;
 
-#define TRUSTED_DEBUG 0
+#ifdef CONFIG_TRUSTED_KEYS_DEBUG
+extern bool trusted_debug;
 
-#if TRUSTED_DEBUG
 static inline void dump_payload(struct trusted_key_payload *p)
 {
-	pr_info("key_len %d\n", p->key_len);
-	print_hex_dump(KERN_INFO, "key ", DUMP_PREFIX_NONE,
-		       16, 1, p->key, p->key_len, 0);
-	pr_info("bloblen %d\n", p->blob_len);
-	print_hex_dump(KERN_INFO, "blob ", DUMP_PREFIX_NONE,
-		       16, 1, p->blob, p->blob_len, 0);
-	pr_info("migratable %d\n", p->migratable);
+	if (!trusted_debug)
+		return;
+
+	pr_debug("key_len %d\n", p->key_len);
+	print_hex_dump_debug("key ", DUMP_PREFIX_NONE,
+			     16, 1, p->key, p->key_len, 0);
+	pr_debug("bloblen %d\n", p->blob_len);
+	print_hex_dump_debug("blob ", DUMP_PREFIX_NONE,
+			     16, 1, p->blob, p->blob_len, 0);
+	pr_debug("migratable %d\n", p->migratable);
 }
 #else
 static inline void dump_payload(struct trusted_key_payload *p)
diff --git a/security/keys/trusted-keys/Kconfig b/security/keys/trusted-keys/Kconfig
index 9e00482d886a..c1ae7db1f612 100644
--- a/security/keys/trusted-keys/Kconfig
+++ b/security/keys/trusted-keys/Kconfig
@@ -1,10 +1,29 @@
 config HAVE_TRUSTED_KEYS
 	bool
 
+config HAVE_TRUSTED_KEYS_DEBUG
+	bool
+
+config TRUSTED_KEYS_DEBUG
+	bool "Debug trusted keys"
+	depends on HAVE_TRUSTED_KEYS_DEBUG
+	default n
+	help
+	  Trusted keys backends and core code that support debug traces can
+	  opt-in that feature here. Traces must only use debug level output, as
+	  sensitive data may pass by. In the kernel-command line traces can be
+	  enabled via trusted.dyndbg='+p'.
+
+	  SAFETY: Debug dumps are inactive at runtime until trusted.debug=1 is
+	  set on the kernel command-line. Use at your utmost consideration when
+	  enabling this feature on a production build. The general advice is not
+	  to do this.
+
 config TRUSTED_KEYS_TPM
 	bool "TPM-based trusted keys"
 	depends on TCG_TPM >= TRUSTED_KEYS
 	default y
+	select HAVE_TRUSTED_KEYS_DEBUG
 	select CRYPTO_HASH_INFO
 	select CRYPTO_LIB_SHA1
 	select CRYPTO_LIB_UTILS
@@ -23,6 +42,7 @@ config TRUSTED_KEYS_TEE
 	bool "TEE-based trusted keys"
 	depends on TEE >= TRUSTED_KEYS
 	default y
+	select HAVE_TRUSTED_KEYS_DEBUG
 	select HAVE_TRUSTED_KEYS
 	help
 	  Enable use of the Trusted Execution Environment (TEE) as trusted
@@ -33,6 +53,7 @@ config TRUSTED_KEYS_CAAM
 	depends on CRYPTO_DEV_FSL_CAAM_JR >= TRUSTED_KEYS
 	select CRYPTO_DEV_FSL_CAAM_BLOB_GEN
 	default y
+	select HAVE_TRUSTED_KEYS_DEBUG
 	select HAVE_TRUSTED_KEYS
 	help
 	  Enable use of NXP's Cryptographic Accelerator and Assurance Module
@@ -42,6 +63,7 @@ config TRUSTED_KEYS_DCP
 	bool "DCP-based trusted keys"
 	depends on CRYPTO_DEV_MXS_DCP >= TRUSTED_KEYS
 	default y
+	select HAVE_TRUSTED_KEYS_DEBUG
 	select HAVE_TRUSTED_KEYS
 	help
 	  Enable use of NXP's DCP (Data Co-Processor) as trusted key backend.
@@ -50,6 +72,7 @@ config TRUSTED_KEYS_PKWM
 	bool "PKWM-based trusted keys"
 	depends on PSERIES_PLPKS >= TRUSTED_KEYS
 	default y
+	select HAVE_TRUSTED_KEYS_DEBUG
 	select HAVE_TRUSTED_KEYS
 	help
 	  Enable use of IBM PowerVM Key Wrapping Module (PKWM) as a trusted key backend.
diff --git a/security/keys/trusted-keys/trusted_caam.c b/security/keys/trusted-keys/trusted_caam.c
index 601943ce0d60..6a33dbf2a7f5 100644
--- a/security/keys/trusted-keys/trusted_caam.c
+++ b/security/keys/trusted-keys/trusted_caam.c
@@ -28,10 +28,13 @@ static const match_table_t key_tokens = {
 	{opt_err, NULL}
 };
 
-#ifdef CAAM_DEBUG
+#ifdef CONFIG_TRUSTED_KEYS_DEBUG
 static inline void dump_options(const struct caam_pkey_info *pkey_info)
 {
-	pr_info("key encryption algo %d\n", pkey_info->key_enc_algo);
+	if (!trusted_debug)
+		return;
+
+	pr_debug("key encryption algo %d\n", pkey_info->key_enc_algo);
 }
 #else
 static inline void dump_options(const struct caam_pkey_info *pkey_info)
diff --git a/security/keys/trusted-keys/trusted_core.c b/security/keys/trusted-keys/trusted_core.c
index 9046123d94de..9ce2459d14b4 100644
--- a/security/keys/trusted-keys/trusted_core.c
+++ b/security/keys/trusted-keys/trusted_core.c
@@ -31,6 +31,12 @@ static char *trusted_rng = "default";
 module_param_named(rng, trusted_rng, charp, 0);
 MODULE_PARM_DESC(rng, "Select trusted key RNG");
 
+#ifdef CONFIG_TRUSTED_KEYS_DEBUG
+bool trusted_debug;
+module_param_named(debug, trusted_debug, bool, 0);
+MODULE_PARM_DESC(debug, "Enable trusted keys debug traces (default: 0)");
+#endif
+
 static char *trusted_key_source;
 module_param_named(source, trusted_key_source, charp, 0);
 MODULE_PARM_DESC(source, "Select trusted keys source (tpm, tee, caam, dcp or pkwm)");
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index c865c97aa1b4..b9fa2b4205cf 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -46,38 +46,44 @@ enum {
 	SRK_keytype = 4
 };
 
-#define TPM_DEBUG 0
-
-#if TPM_DEBUG
+#ifdef CONFIG_TRUSTED_KEYS_DEBUG
 static inline void dump_options(struct trusted_key_options *o)
 {
-	pr_info("sealing key type %d\n", o->keytype);
-	pr_info("sealing key handle %0X\n", o->keyhandle);
-	pr_info("pcrlock %d\n", o->pcrlock);
-	pr_info("pcrinfo %d\n", o->pcrinfo_len);
-	print_hex_dump(KERN_INFO, "pcrinfo ", DUMP_PREFIX_NONE,
-		       16, 1, o->pcrinfo, o->pcrinfo_len, 0);
+	if (!trusted_debug)
+		return;
+
+	pr_debug("sealing key type %d\n", o->keytype);
+	pr_debug("sealing key handle %0X\n", o->keyhandle);
+	pr_debug("pcrlock %d\n", o->pcrlock);
+	pr_debug("pcrinfo %d\n", o->pcrinfo_len);
+	print_hex_dump_debug("pcrinfo ", DUMP_PREFIX_NONE,
+			     16, 1, o->pcrinfo, o->pcrinfo_len, 0);
 }
 
 static inline void dump_sess(struct osapsess *s)
 {
-	print_hex_dump(KERN_INFO, "trusted-key: handle ", DUMP_PREFIX_NONE,
-		       16, 1, &s->handle, 4, 0);
-	pr_info("secret:\n");
-	print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE,
-		       16, 1, &s->secret, SHA1_DIGEST_SIZE, 0);
-	pr_info("trusted-key: enonce:\n");
-	print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE,
-		       16, 1, &s->enonce, SHA1_DIGEST_SIZE, 0);
+	if (!trusted_debug)
+		return;
+
+	print_hex_dump_debug("trusted-key: handle ", DUMP_PREFIX_NONE,
+			     16, 1, &s->handle, 4, 0);
+	pr_debug("secret:\n");
+	print_hex_dump_debug("", DUMP_PREFIX_NONE,
+			     16, 1, &s->secret, SHA1_DIGEST_SIZE, 0);
+	pr_debug("trusted-key: enonce:\n");
+	print_hex_dump_debug("", DUMP_PREFIX_NONE,
+			     16, 1, &s->enonce, SHA1_DIGEST_SIZE, 0);
 }
 
 static inline void dump_tpm_buf(unsigned char *buf)
 {
 	int len;
 
-	pr_info("\ntpm buffer\n");
+	if (!trusted_debug)
+		return;
+	pr_debug("\ntpm buffer\n");
 	len = LOAD32(buf, TPM_SIZE_OFFSET);
-	print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, buf, len, 0);
+	print_hex_dump_debug("", DUMP_PREFIX_NONE, 16, 1, buf, len, 0);
 }
 #else
 static inline void dump_options(struct trusted_key_options *o)
-- 
2.39.5


^ permalink raw reply related

* Re: [PATCH v2] KEYS: trusted: Debugging as a feature
From: Nayna Jain @ 2026-04-09  0:41 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: linux-integrity, keyrings, Srish Srinivasan, James Bottomley,
	Mimi Zohar, David Howells, Paul Moore, James Morris,
	Serge E. Hallyn, Ahmad Fatoum, Pengutronix Kernel Team, open list,
	open list:SECURITY SUBSYSTEM
In-Reply-To: <adYRURAJfNCu0FYB@kernel.org>


On 4/8/26 4:26 AM, Jarkko Sakkinen wrote:
> On Mon, Apr 06, 2026 at 10:42:00PM -0400, Nayna Jain wrote:
>> On 3/24/26 7:00 AM, Jarkko Sakkinen wrote:
>>> TPM_DEBUG, and other similar flags, are a non-standard way to specify a
>>> feature in Linux kernel.  Introduce CONFIG_TRUSTED_KEYS_DEBUG for
>>> trusted keys, and use it to replace these ad-hoc feature flags.
>>>
>>> Given that trusted keys debug dumps can contain sensitive data, harden
>>> the feature as follows:
>>>
>>> 1. In the Kconfig description postulate that pr_debug() statements must be
>>>      used.
>>> 2. Use pr_debug() statements in TPM 1.x driver to print the protocol dump.
>>>
>>> Traces, when actually needed, can be easily enabled by providing
>>> trusted.dyndbg='+p' in the kernel command-line.
>>>
>>> Cc: Srish Srinivasan <ssrish@linux.ibm.com>
>>> Reported-by: Nayna Jain <nayna@linux.ibm.com>
>>> Closes: https://lore.kernel.org/all/7f8b8478-5cd8-4d97-bfd0-341fd5cf10f9@linux.ibm.com/
>>> Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
>>> ---
>>> v2:
>>> - Implement for all trusted keys backends.
>>> - Add HAVE_TRUSTED_KEYS_DEBUG as it is a good practice despite full
>>>     coverage.
>>> ---
>>>    include/keys/trusted-type.h               | 18 +++++-------
>>>    security/keys/trusted-keys/Kconfig        | 19 ++++++++++++
>>>    security/keys/trusted-keys/trusted_caam.c |  4 +--
>>>    security/keys/trusted-keys/trusted_tpm1.c | 36 +++++++++++------------
>>>    4 files changed, 46 insertions(+), 31 deletions(-)
>>>
>>> diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
>>> index 03527162613f..620a1f890b6b 100644
>>> --- a/include/keys/trusted-type.h
>>> +++ b/include/keys/trusted-type.h
>>> @@ -83,18 +83,16 @@ struct trusted_key_source {
>>>    extern struct key_type key_type_trusted;
>>> -#define TRUSTED_DEBUG 0
>>> -
>>> -#if TRUSTED_DEBUG
>>> +#ifdef CONFIG_TRUSTED_KEYS_DEBUG
>>>    static inline void dump_payload(struct trusted_key_payload *p)
>>>    {
>>> -	pr_info("key_len %d\n", p->key_len);
>>> -	print_hex_dump(KERN_INFO, "key ", DUMP_PREFIX_NONE,
>>> -		       16, 1, p->key, p->key_len, 0);
>>> -	pr_info("bloblen %d\n", p->blob_len);
>>> -	print_hex_dump(KERN_INFO, "blob ", DUMP_PREFIX_NONE,
>>> -		       16, 1, p->blob, p->blob_len, 0);
>>> -	pr_info("migratable %d\n", p->migratable);
>>> +	pr_debug("key_len %d\n", p->key_len);
>>> +	print_hex_dump_debug("key ", DUMP_PREFIX_NONE,
>>> +			     16, 1, p->key, p->key_len, 0);
>>> +	pr_debug("bloblen %d\n", p->blob_len);
>>> +	print_hex_dump_debug("blob ", DUMP_PREFIX_NONE,
>>> +			     16, 1, p->blob, p->blob_len, 0);
>>> +	pr_debug("migratable %d\n", p->migratable);
>>>    }
>>>    #else
>>>    static inline void dump_payload(struct trusted_key_payload *p)
>>> diff --git a/security/keys/trusted-keys/Kconfig b/security/keys/trusted-keys/Kconfig
>>> index 9e00482d886a..2ad9ba0e03f1 100644
>>> --- a/security/keys/trusted-keys/Kconfig
>>> +++ b/security/keys/trusted-keys/Kconfig
>>> @@ -1,10 +1,25 @@
>>>    config HAVE_TRUSTED_KEYS
>>>    	bool
>>> +config HAVE_TRUSTED_KEYS_DEBUG
>>> +	bool
>>> +
>>> +config TRUSTED_KEYS_DEBUG
>>> +	bool "Debug trusted keys"
>>> +	depends on HAVE_TRUSTED_KEYS_DEBUG
>>> +	default n
>>> +	help
>>> +	  Trusted keys backends and core code that support debug dumps
>>> +	  can opt-in that feature here. Dumps must only use DEBUG
>>> +	  level output, as sensitive data may pass by. In the
>>> +	  kernel-command line traces can be enabled via
>>> +	  trusted.dyndbg='+p'.
>> Would it be good idea to add an explicit note/warning:
>>
>>
>> NOTE: This option is intended for debugging purposes only. Do not enable on
>> production systems as debug output may expose sensitive cryptographic
>> material.
>> If you are unsure, say N.
>>
>> Apart from this, looks good to me.
>>
>> Reviewed-by: Nayna Jain <nayna@linux.ibm.com>
> Thank, I'll add your tag but would you mind quickly screening v3 again
> where I add "trusted.debug=0|1". And yes, your suggestion about extra
> warning makes sense.
Sure Jarkko.. However, I don't see v3 version in my inbox or in 
linux-integrity. Or you are about to post it soon.
>
> Let's make this safe as possible. Mistakes do happen... and then those
> measures pay off :-)
Yes agree.
>
> BR, Jarkko

Thanks & Regards,

     - Nayna


^ permalink raw reply

* [PATCH v2 0/2] Add support for ML-DSA signature for EVM and IMA
From: Stefan Berger @ 2026-04-08 17:41 UTC (permalink / raw)
  To: linux-integrity, linux-security-module
  Cc: linux-kernel, zohar, roberto.sassu, ebiggers, Stefan Berger

Based on IMA sigv3 type of signatures, add support for ML-DSA signature
for EVM and IMA. Use the existing ML-DSA hashless signing mode (pure mode).

   Stefan

v2:
  - Dropped 1/3
  - Using "none" as hash_algo in 2/2

Stefan Berger (2):
  integrity: Refactor asymmetric_verify for reusability
  integrity: Add support for sigv3 verification using ML-DSA keys

 security/integrity/digsig_asymmetric.c | 126 +++++++++++++++++++++----
 1 file changed, 107 insertions(+), 19 deletions(-)


base-commit: 82bbd447199ff1441031d2eaf9afe041550cf525
-- 
2.53.0


^ permalink raw reply

* [PATCH v2 2/2] integrity: Add support for sigv3 verification using ML-DSA keys
From: Stefan Berger @ 2026-04-08 17:41 UTC (permalink / raw)
  To: linux-integrity, linux-security-module
  Cc: linux-kernel, zohar, roberto.sassu, ebiggers, Stefan Berger
In-Reply-To: <20260408174154.139606-1-stefanb@linux.ibm.com>

Add support for sigv3 signature verification using ML-DSA in pure mode.
When a sigv3 signature is verified, first check whether the key to use
for verification is an ML-DSA key and therefore uses a hashless signature
verification scheme. The hashless signature verification method uses the
ima_file_id structure directly for signature verification rather than
its digest.

Suggested-by: Eric Biggers <ebiggers@kernel.org>
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>

---
v2: Set hash_algo in public_key_signature to "none"
---
 security/integrity/digsig_asymmetric.c | 84 ++++++++++++++++++++++++--
 1 file changed, 79 insertions(+), 5 deletions(-)

diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
index e29ed73f15cd..c80cb2b117a6 100644
--- a/security/integrity/digsig_asymmetric.c
+++ b/security/integrity/digsig_asymmetric.c
@@ -190,17 +190,91 @@ static int calc_file_id_hash(enum evm_ima_xattr_type type,
 	return rc;
 }
 
+/*
+ * asymmetric_verify_v3_hashless - Use hashless signature verification on sigv3
+ * @key: The key to use for signature verification
+ * @pk: The associated public key
+ * @encoding: The encoding the key type uses
+ * @sig: The signature
+ * @siglen: The length of the xattr signature
+ * @algo: The hash algorithm
+ * @digest: The file digest
+ *
+ * Create an ima_file_id structure and use it for signature verification
+ * directly. This can be used for ML-DSA in pure mode for example.
+ */
+static int asymmetric_verify_v3_hashless(struct key *key,
+					 const struct public_key *pk,
+					 const char *encoding,
+					 const char *sig, int siglen,
+					 u8 algo,
+					 const u8 *digest)
+{
+	struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig;
+	struct ima_file_id file_id = {
+		.hash_type = hdr->type,
+		.hash_algorithm = algo,
+	};
+	size_t digest_size = hash_digest_size[algo];
+	struct public_key_signature pks = {
+		.m = (u8 *)&file_id,
+		.m_size = sizeof(file_id) - (HASH_MAX_DIGESTSIZE - digest_size),
+		.s = hdr->sig,
+		.s_size = siglen - sizeof(*hdr),
+		.pkey_algo = pk->pkey_algo,
+		.hash_algo = "none",
+		.encoding = encoding,
+	};
+	int ret;
+
+	if (hdr->type != IMA_VERITY_DIGSIG &&
+	    hdr->type != EVM_IMA_XATTR_DIGSIG &&
+	    hdr->type != EVM_XATTR_PORTABLE_DIGSIG)
+		return -EINVAL;
+
+	if (pks.s_size != be16_to_cpu(hdr->sig_size))
+		return -EBADMSG;
+
+	memcpy(file_id.hash, digest, digest_size);
+
+	ret = verify_signature(key, &pks);
+	pr_debug("%s() = %d\n", __func__, ret);
+	return ret;
+}
+
 int asymmetric_verify_v3(struct key *keyring, const char *sig, int siglen,
 			 const char *data, int datalen, u8 algo)
 {
 	struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig;
 	struct ima_max_digest_data hash;
+	const struct public_key *pk;
+	struct key *key;
 	int rc;
 
-	rc = calc_file_id_hash(hdr->type, algo, data, &hash);
-	if (rc)
-		return -EINVAL;
+	if (siglen <= sizeof(*hdr))
+		return -EBADMSG;
+
+	key = request_asymmetric_key(keyring, be32_to_cpu(hdr->keyid));
+	if (IS_ERR(key))
+		return PTR_ERR(key);
 
-	return asymmetric_verify(keyring, sig, siglen, hash.digest,
-				 hash.hdr.length);
+	pk = asymmetric_key_public_key(key);
+	if (!strncmp(pk->pkey_algo, "mldsa", 5)) {
+		rc = asymmetric_verify_v3_hashless(key, pk, "raw",
+						   sig, siglen, algo, data);
+	} else {
+		rc = calc_file_id_hash(hdr->type, algo, data, &hash);
+		if (rc) {
+			rc = -EINVAL;
+			goto err_exit;
+		}
+
+		rc = asymmetric_verify_common(key, pk, sig, siglen, hash.digest,
+					      hash.hdr.length);
+	}
+
+err_exit:
+	key_put(key);
+
+	return rc;
 }
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 1/2] integrity: Refactor asymmetric_verify for reusability
From: Stefan Berger @ 2026-04-08 17:41 UTC (permalink / raw)
  To: linux-integrity, linux-security-module
  Cc: linux-kernel, zohar, roberto.sassu, ebiggers, Stefan Berger
In-Reply-To: <20260408174154.139606-1-stefanb@linux.ibm.com>

Refactor asymmetric_verify for reusability. Have it call
asymmetric_verify_common with the signature verification key and the
public_key structure as parameters. sigv3 support for ML-DSA will need to
check the public key type first to decide how to do the signature
verification and therefore will have these parameters available for
calling asymmetric_verify_common.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 security/integrity/digsig_asymmetric.c | 42 +++++++++++++++++---------
 1 file changed, 28 insertions(+), 14 deletions(-)

diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
index 6e68ec3becbd..e29ed73f15cd 100644
--- a/security/integrity/digsig_asymmetric.c
+++ b/security/integrity/digsig_asymmetric.c
@@ -79,18 +79,15 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid)
 	return key;
 }
 
-int asymmetric_verify(struct key *keyring, const char *sig,
-		      int siglen, const char *data, int datalen)
+static int asymmetric_verify_common(const struct key *key,
+				    const struct public_key *pk,
+				    const char *sig, int siglen,
+				    const char *data, int datalen)
 {
-	struct public_key_signature pks;
 	struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig;
-	const struct public_key *pk;
-	struct key *key;
+	struct public_key_signature pks;
 	int ret;
 
-	if (siglen <= sizeof(*hdr))
-		return -EBADMSG;
-
 	siglen -= sizeof(*hdr);
 
 	if (siglen != be16_to_cpu(hdr->sig_size))
@@ -99,15 +96,10 @@ int asymmetric_verify(struct key *keyring, const char *sig,
 	if (hdr->hash_algo >= HASH_ALGO__LAST)
 		return -ENOPKG;
 
-	key = request_asymmetric_key(keyring, be32_to_cpu(hdr->keyid));
-	if (IS_ERR(key))
-		return PTR_ERR(key);
-
 	memset(&pks, 0, sizeof(pks));
 
 	pks.hash_algo = hash_algo_name[hdr->hash_algo];
 
-	pk = asymmetric_key_public_key(key);
 	pks.pkey_algo = pk->pkey_algo;
 	if (!strcmp(pk->pkey_algo, "rsa")) {
 		pks.encoding = "pkcs1";
@@ -127,11 +119,33 @@ int asymmetric_verify(struct key *keyring, const char *sig,
 	pks.s_size = siglen;
 	ret = verify_signature(key, &pks);
 out:
-	key_put(key);
 	pr_debug("%s() = %d\n", __func__, ret);
 	return ret;
 }
 
+int asymmetric_verify(struct key *keyring, const char *sig,
+		      int siglen, const char *data, int datalen)
+{
+	struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig;
+	const struct public_key *pk;
+	struct key *key;
+	int ret;
+
+	if (siglen <= sizeof(*hdr))
+		return -EBADMSG;
+
+	key = request_asymmetric_key(keyring, be32_to_cpu(hdr->keyid));
+	if (IS_ERR(key))
+		return PTR_ERR(key);
+	pk = asymmetric_key_public_key(key);
+
+	ret = asymmetric_verify_common(key, pk, sig, siglen, data, datalen);
+
+	key_put(key);
+
+	return ret;
+}
+
 /*
  * calc_file_id_hash - calculate the hash of the ima_file_id struct data
  * @type: xattr type [enum evm_ima_xattr_type]
-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH 1/3] crypto: public_key: Remove check for valid hash_algo for ML-DSA keys
From: Stefan Berger @ 2026-04-08 17:25 UTC (permalink / raw)
  To: Eric Biggers
  Cc: linux-integrity, linux-security-module, linux-kernel, zohar,
	roberto.sassu, David Howells, Lukas Wunner, Ignat Korchagin,
	keyrings, linux-crypto
In-Reply-To: <20260406165350.GD2971@sol>



On 4/6/26 12:53 PM, Eric Biggers wrote:
> On Sun, Apr 05, 2026 at 07:12:22PM -0400, Stefan Berger wrote:
>> Remove the check for the hash_algo since ML-DSA is only used in pure mode
>> and there is no relevance of a hash_algo for the input data.
>>
>> Cc: David Howells <dhowells@redhat.com>
>> Cc: Lukas Wunner <lukas@wunner.de>
>> Cc: Ignat Korchagin <ignat@linux.win>
>> Cc: keyrings@vger.kernel.org
>> Cc: linux-crypto@vger.kernel.org
>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>> ---
>>   crypto/asymmetric_keys/public_key.c | 5 -----
>>   1 file changed, 5 deletions(-)
>>
>> diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
>> index 09a0b83d5d77..df6918a77ab8 100644
>> --- a/crypto/asymmetric_keys/public_key.c
>> +++ b/crypto/asymmetric_keys/public_key.c
>> @@ -147,11 +147,6 @@ software_key_determine_akcipher(const struct public_key *pkey,
>>   		   strcmp(pkey->pkey_algo, "mldsa87") == 0) {
>>   		if (strcmp(encoding, "raw") != 0)
>>   			return -EINVAL;
>> -		if (!hash_algo)
>> -			return -EINVAL;
>> -		if (strcmp(hash_algo, "none") != 0 &&
>> -		    strcmp(hash_algo, "sha512") != 0)
>> -			return -EINVAL;
> 
> Does this broaden which hash algorithms are accepted for CMS signatures
> that use ML-DSA and contain signed attributes?

Right... dropping this patch and using the "none" route now.

> 
> - Eric
> 


^ permalink raw reply

* Re: [PATCH] tpm2-sessions: Fix missing tpm_buf_destroy() in tpm2_read_public()
From: Paul Menzel @ 2026-04-08 17:01 UTC (permalink / raw)
  To: Gunnar Kudrjavets
  Cc: peterhuewe, jarkko, jgg, noodles, linux-integrity, linux-kernel,
	Justinien Bouron
In-Reply-To: <20260408164359.24968-1-gunnarku@amazon.com>

Dear Gunnar,


Thank you for your patch.

Am 08.04.26 um 18:43 schrieb Gunnar Kudrjavets:
> tpm2_read_public() calls tpm_buf_init() but fails to call
> tpm_buf_destroy() on two exit paths, leaking a page allocation:
> 
> 1. When name_size() returns an error (unrecognized hash algorithm),
>     the function returns directly without destroying the buffer.
> 
> 2. On the success path, the buffer is never destroyed before
>     returning.
> 
> All other error paths in the function correctly call
> tpm_buf_destroy() before returning.
> 
> Fix both by adding the missing tpm_buf_destroy() calls.
> 
> Fixes: bda1cbf73c6e ("tpm2-sessions: Fix tpm2_read_public range checks")
> Signed-off-by: Gunnar Kudrjavets <gunnarku@amazon.com>
> Reviewed-by: Justinien Bouron <jbouron@amazon.com>
> ---
>   drivers/char/tpm/tpm2-sessions.c | 5 ++++-
>   1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
> index 09df6353ef04..f7c6c043fef4 100644
> --- a/drivers/char/tpm/tpm2-sessions.c
> +++ b/drivers/char/tpm/tpm2-sessions.c
> @@ -203,8 +203,10 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
>   	rc = tpm_buf_read_u16(&buf, &offset);
>   	name_size_alg = name_size(&buf.data[offset]);
>   
> -	if (name_size_alg < 0)
> +	if (name_size_alg < 0) {
> +		tpm_buf_destroy(&buf);
>   		return name_size_alg;
> +	}
>   
>   	if (rc != name_size_alg) {
>   		tpm_buf_destroy(&buf);
> @@ -217,6 +219,7 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
>   	}
>   
>   	memcpy(name, &buf.data[offset], rc);
> +	tpm_buf_destroy(&buf);
>   	return name_size_alg;
>   }
>   #endif /* CONFIG_TCG_TPM2_HMAC */
> 
> base-commit: 03e5553f5fb99cb47c315e167a604a9c69e6f724

Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>


Kind regards,

Paul

^ permalink raw reply

* [PATCH] tpm2-sessions: Fix missing tpm_buf_destroy() in tpm2_read_public()
From: Gunnar Kudrjavets @ 2026-04-08 16:43 UTC (permalink / raw)
  To: peterhuewe, jarkko
  Cc: jgg, noodles, gunnarku, linux-integrity, linux-kernel,
	Justinien Bouron

tpm2_read_public() calls tpm_buf_init() but fails to call
tpm_buf_destroy() on two exit paths, leaking a page allocation:

1. When name_size() returns an error (unrecognized hash algorithm),
   the function returns directly without destroying the buffer.

2. On the success path, the buffer is never destroyed before
   returning.

All other error paths in the function correctly call
tpm_buf_destroy() before returning.

Fix both by adding the missing tpm_buf_destroy() calls.

Fixes: bda1cbf73c6e ("tpm2-sessions: Fix tpm2_read_public range checks")
Signed-off-by: Gunnar Kudrjavets <gunnarku@amazon.com>
Reviewed-by: Justinien Bouron <jbouron@amazon.com>
---
 drivers/char/tpm/tpm2-sessions.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 09df6353ef04..f7c6c043fef4 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -203,8 +203,10 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
 	rc = tpm_buf_read_u16(&buf, &offset);
 	name_size_alg = name_size(&buf.data[offset]);
 
-	if (name_size_alg < 0)
+	if (name_size_alg < 0) {
+		tpm_buf_destroy(&buf);
 		return name_size_alg;
+	}
 
 	if (rc != name_size_alg) {
 		tpm_buf_destroy(&buf);
@@ -217,6 +219,7 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
 	}
 
 	memcpy(name, &buf.data[offset], rc);
+	tpm_buf_destroy(&buf);
 	return name_size_alg;
 }
 #endif /* CONFIG_TCG_TPM2_HMAC */

base-commit: 03e5553f5fb99cb47c315e167a604a9c69e6f724
-- 
2.47.3


^ permalink raw reply related

* Re: [PATCH] tpm: Fix auth session leak in tpm2_get_random() error path
From: Jarkko Sakkinen @ 2026-04-08  9:00 UTC (permalink / raw)
  To: Gunnar Kudrjavets
  Cc: peterhuewe, jgg, noodles, linux-integrity, linux-kernel,
	Justinien Bouron
In-Reply-To: <20260402181156.29396-1-gunnarku@amazon.com>

On Thu, Apr 02, 2026 at 06:11:39PM +0000, Gunnar Kudrjavets wrote:
> When tpm_buf_fill_hmac_session() fails inside the do-while loop in
> tpm2_get_random(), the function returns directly after destroying the
> buffer, without ending the auth session via tpm2_end_auth_session().
> 
> This leaks the TPM auth session resource. All other error paths within
> the loop correctly reach the 'out' label which calls both
> tpm_buf_destroy() and tpm2_end_auth_session().
> 
> Fix this by replacing the early return with a goto to the existing 'out'
> label, which already handles both cleanup operations. The redundant
> tpm_buf_destroy() call is removed since 'out' takes care of it.
> 
> Fixes: 6e9722e9a7bf ("tpm2-sessions: Fix out of range indexing in name_size")
> Signed-off-by: Gunnar Kudrjavets <gunnarku@amazon.com>
> Reviewed-by: Justinien Bouron <jbouron@amazon.com>
> ---
>  drivers/char/tpm/tpm2-cmd.c | 6 ++----
>  1 file changed, 2 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
> index e00f668f8c84..b11e6fa8b740 100644
> --- a/drivers/char/tpm/tpm2-cmd.c
> +++ b/drivers/char/tpm/tpm2-cmd.c
> @@ -295,10 +295,8 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
>  		}
>  		tpm_buf_append_u16(&buf, num_bytes);
>  		err = tpm_buf_fill_hmac_session(chip, &buf);
> -		if (err) {
> -			tpm_buf_destroy(&buf);
> -			return err;
> -		}
> +		if (err)
> +			goto out;
>  
>  		err = tpm_transmit_cmd(chip, &buf,
>  				       offsetof(struct tpm2_get_random_out,
> 
> base-commit: 7f2a32c0e87814f0e7852b17fa9f10321f882c36
> -- 
> 2.47.3
> 

Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>

BR, Jarkko

^ permalink raw reply

* Re: [PATCH 4/4] tpm: Move TPM common base definitions to the command header
From: Jarkko Sakkinen @ 2026-04-08  8:45 UTC (permalink / raw)
  To: Alec Brown
  Cc: linux-kernel@vger.kernel.org, linux-integrity@vger.kernel.org,
	peterhuewe@gmx.de, jarkko.sakkinen@iki.fi, jgg@ziepe.ca,
	Ross Philipson, dpsmith@apertussolutions.com, Daniel Kiper,
	Kanth Ghatraju, trenchboot-devel@googlegroups.com,
	ardb@kernel.org
In-Reply-To: <IA1PR10MB68309FDA0D5450AB2FC0B5BFBC56A@IA1PR10MB6830.namprd10.prod.outlook.com>

On Thu, Mar 26, 2026 at 07:54:36PM +0000, Alec Brown wrote:
> On Mon, Mar 23, 2026 at 1:42 AM, Jarkko Sakkinen <jarkko@kernel.org> wrote:
> > On Tue, Mar 17, 2026 at 04:03:35PM +0000, Alec Brown wrote:
> > > From: Ross Philipson <ross.philipson@oracle.com>
> > >
> > > From: Ross Philipson <ross.philipson@oracle.com>
> > >
> > > These are top level definitions shared by both TPM 1 and 2 family
> > > chips. This includes core definitions like TPM localities, common
> > > crypto algorithm IDs, and the base TPM command header.
> > >
> > > Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
> > > Signed-off-by: Ross Philipson <ross.philipson@oracle.com>
> > > Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
> > > ---
> > >  include/linux/tpm.h         | 50 +--------------------
> > >  include/linux/tpm_command.h | 89
> > > +++++++++++++++++++++++++++++++++++++
> > >  2 files changed, 90 insertions(+), 49 deletions(-)
> > >
> > > diff --git a/include/linux/tpm.h b/include/linux/tpm.h index
> > > 92957452f7a7..a282b7045a24 100644
> > > --- a/include/linux/tpm.h
> > > +++ b/include/linux/tpm.h
> > > @@ -27,49 +27,12 @@
> > >
> > >  #include "tpm_command.h"
> > >
> > > -#define TPM_DIGEST_SIZE 20	/* Max TPM v1.2 PCR size */
> > > -
> > > -#define TPM2_MAX_DIGEST_SIZE	SHA512_DIGEST_SIZE
> > > -#define TPM2_MAX_PCR_BANKS	8
> > > -
> > >  struct tpm_chip;
> > >  struct trusted_key_payload;
> > >  struct trusted_key_options;
> > >  /* opaque structure, holds auth session parameters like the session
> > > key */  struct tpm2_auth;
> > >
> > > -/* if you add a new hash to this, increment TPM_MAX_HASHES below */
> > > -enum tpm_algorithms {
> > > -	TPM_ALG_ERROR		= 0x0000,
> > > -	TPM_ALG_SHA1		= 0x0004,
> > > -	TPM_ALG_AES		= 0x0006,
> > > -	TPM_ALG_KEYEDHASH	= 0x0008,
> > > -	TPM_ALG_SHA256		= 0x000B,
> > > -	TPM_ALG_SHA384		= 0x000C,
> > > -	TPM_ALG_SHA512		= 0x000D,
> > > -	TPM_ALG_NULL		= 0x0010,
> > > -	TPM_ALG_SM3_256		= 0x0012,
> > > -	TPM_ALG_ECC		= 0x0023,
> > > -	TPM_ALG_CFB		= 0x0043,
> > > -};
> > > -
> > > -/*
> > > - * maximum number of hashing algorithms a TPM can have.  This is
> > > - * basically a count of every hash in tpm_algorithms above
> > > - */
> > > -#define TPM_MAX_HASHES	5
> > > -
> > > -struct tpm_digest {
> > > -	u16 alg_id;
> > > -	u8 digest[TPM2_MAX_DIGEST_SIZE];
> > > -} __packed;
> > > -
> > > -struct tpm_bank_info {
> > > -	u16 alg_id;
> > > -	u16 digest_size;
> > > -	u16 crypto_id;
> > > -};
> > > -
> > >  enum TPM_OPS_FLAGS {
> > >  	TPM_OPS_AUTO_STARTUP = BIT(0),
> > >  };
> > > @@ -127,7 +90,7 @@ struct tpm_chip_seqops {
> > >  	const struct seq_operations *seqops;  };
> > >
> > > -/* fixed define for the curve we use which is NIST_P256 */
> > > +/* Fixed define for the curve we use which is NIST_P256 */
> > >  #define EC_PT_SZ	32
> > >
> > >  /*
> > > @@ -209,8 +172,6 @@ struct tpm_chip {
> > >  #endif
> > >  };
> > >
> > > -#define TPM_HEADER_SIZE		10
> > > -
> > >  static inline enum tpm2_mso_type tpm2_handle_mso(u32 handle)  {
> > >  	return handle >> 24;
> > > @@ -239,15 +200,6 @@ enum tpm_chip_flags {
> > >
> > >  #define to_tpm_chip(d) container_of(d, struct tpm_chip, dev)
> > >
> > > -struct tpm_header {
> > > -	__be16 tag;
> > > -	__be32 length;
> > > -	union {
> > > -		__be32 ordinal;
> > > -		__be32 return_code;
> > > -	};
> > > -} __packed;
> > > -
> > >  enum tpm_buf_flags {
> > >  	/* the capacity exceeded: */
> > >  	TPM_BUF_OVERFLOW	= BIT(0),
> > > diff --git a/include/linux/tpm_command.h b/include/linux/tpm_command.h
> > > index ee76fcd5ecef..25a247254140 100644
> > > --- a/include/linux/tpm_command.h
> > > +++ b/include/linux/tpm_command.h
> > > @@ -431,4 +431,93 @@ struct tpm2_context {
> > >  	__be16 blob_size;
> > >  } __packed;
> > >
> > > +/************************************************/
> > > +/* TPM Common Defs                              */
> > > +/************************************************/


One nit I just noticed here: let's use kernel standard block comments.

E.g.

/*
 * Common definitions for TPM.
 */


> > > +
> > > +#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)
> > > +
> > > +/*
> > > + * Fixed define for the size of a name.  This is actually HASHALG
> > > +size
> > > + * plus 2, so 32 for SHA256
> > > + */
> > > +#define TPM2_NULL_NAME_SIZE	34
> > > +
> > > +/*
> > > + * The maximum number of PCR banks.
> > > + */
> > > +#define TPM2_MAX_PCR_BANKS	8
> > > +
> > > +/* If you add a new hash to this, increment TPM_MAX_HASHES below */
> > > +enum tpm_algorithms {
> > > +	TPM_ALG_ERROR		= 0x0000,
> > > +	TPM_ALG_SHA1		= 0x0004,
> > > +	TPM_ALG_AES		= 0x0006,
> > > +	TPM_ALG_KEYEDHASH	= 0x0008,
> > > +	TPM_ALG_SHA256		= 0x000B,
> > > +	TPM_ALG_SHA384		= 0x000C,
> > > +	TPM_ALG_SHA512		= 0x000D,
> > > +	TPM_ALG_NULL		= 0x0010,
> > > +	TPM_ALG_SM3_256		= 0x0012,
> > > +	TPM_ALG_ECC		= 0x0023,
> > > +	TPM_ALG_CFB		= 0x0043,
> > > +};
> > > +
> > > +/*
> > > + * The locality (0 - 4) for a TPM, as defined in section 3.2 of the
> > > + * Client Platform Profile Specification.
> > > + */
> > > +enum tpm_localities {
> > > +	TPM_LOCALITY_0		= 0, /* Static RTM */
> > > +	TPM_LOCALITY_1		= 1, /* Dynamic OS */
> > > +	TPM_LOCALITY_2		= 2, /* DRTM Environment */
> > > +	TPM_LOCALITY_3		= 3, /* Aux Components */
> > > +	TPM_LOCALITY_4		= 4, /* CPU DRTM Establishment */
> > > +	TPM_MAX_LOCALITY	= TPM_LOCALITY_4
> > > +};
> > > +
> > > +/*
> > > + * Structure to represent active PCR algorithm banks usable by the
> > > + * TPM chip.
> > > + */
> > > +struct tpm_bank_info {
> > > +	u16 alg_id;
> > > +	u16 digest_size;
> > > +	u16 crypto_id;
> > > +};
> > > +
> > > +/*
> > > + * Maximum number of hashing algorithms a TPM can have.  This is
> > > + * basically a count of every hash in tpm_algorithms above  */
> > > +#define TPM_MAX_HASHES		5
> > > +
> > > +struct tpm_digest {
> > > +	u16 alg_id;
> > > +	u8 digest[TPM2_MAX_DIGEST_SIZE];
> > > +} __packed;
> > > +
> > > +#define TPM_HEADER_SIZE		10
> > > +
> > > +struct tpm_header {
> > > +	__be16 tag;
> > > +	__be32 length;
> > > +	union {
> > > +		__be32 ordinal;
> > > +		__be32 return_code;
> > > +	};
> > > +} __packed;
> > > +
> > >  #endif
> > > --
> > > 2.47.3
> > >
> > 
> > Yep, all looks great and clean to me but exactly for that reason
> > this needs to the truth serum :-)
> > 
> > BR, Jarkko
> 
> Thanks for taking a look! Glad to hear the patches are looking good!
> 
> Alec Brown 

BR, Jarkko

^ permalink raw reply

* Re: [PATCH v3] tpm: i2c: atmel: fix block comment formatting
From: Jarkko Sakkinen @ 2026-04-08  8:38 UTC (permalink / raw)
  To: Ethan Luna
  Cc: peterhuewe, jgg, nicolas.ferre, claudiu.beznea, linux-integrity,
	linux-arm-kernel, linux-kernel
In-Reply-To: <20260324154354.6268-1-trunixcodes@zohomail.com>

On Tue, Mar 24, 2026 at 08:39:49AM -0700, Ethan Luna wrote:
> Multiple block comments in tpm_i2c_atmel.c placed the closing '*/' on the
> same line as the comment text. This violates the kernel's preferred
> comment style, which requires the closing delimiter to appear on its
> line.
> 
> Fix the formatting to improve readability and resolve checkpatch
> warnings.
> 
> Signed-off-by: Ethan Luna <trunixcodes@zohomail.com>
> ---
> 
> V1 -> V2: Fixed block comment formatting consistently across all multi-line comments
> V2 -> V3: Fixed trailing whitespaces consistently across all multi-line comments
> 
> v1: https://lore.kernel.org/all/20260322193112.27010-1-trunixcodes@zohomail.com/
> v2: https://lore.kernel.org/all/20260323134200.7766-1-trunixcodes@zohomail.com/
> 
>  drivers/char/tpm/tpm_i2c_atmel.c | 34 +++++++++++++++++++++-----------
>  1 file changed, 23 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c
> index 4f229656a8e2..9fd73049821f 100644
> --- a/drivers/char/tpm/tpm_i2c_atmel.c
> +++ b/drivers/char/tpm/tpm_i2c_atmel.c
> @@ -31,9 +31,11 @@
>  
>  struct priv_data {
>  	size_t len;
> -	/* This is the amount we read on the first try. 25 was chosen to fit a
> +	/*
> +	 * This is the amount we read on the first try. 25 was chosen to fit a
>  	 * fair number of read responses in the buffer so a 2nd retry can be
> -	 * avoided in small message cases. */
> +	 * avoided in small message cases.
> +	 */
>  	u8 buffer[sizeof(struct tpm_header) + 25];
>  };
>  
> @@ -58,7 +60,9 @@ static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz,
>  	if (status < 0)
>  		return status;
>  
> -	/* The upper layer does not support incomplete sends. */
> +	/*
> +	 * The upper layer does not support incomplete sends.
> +	 */
>  	if (status != len)
>  		return -E2BIG;
>  
> @@ -76,9 +80,11 @@ static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
>  	if (priv->len == 0)
>  		return -EIO;
>  
> -	/* Get the message size from the message header, if we didn't get the
> +	/*
> +	 * Get the message size from the message header, if we didn't get the
>  	 * whole message in read_status then we need to re-read the
> -	 * message. */
> +	 * message.
> +	 */
>  	expected_len = be32_to_cpu(hdr->length);
>  	if (expected_len > count)
>  		return -ENOMEM;
> @@ -111,15 +117,19 @@ static u8 i2c_atmel_read_status(struct tpm_chip *chip)
>  	struct i2c_client *client = to_i2c_client(chip->dev.parent);
>  	int rc;
>  
> -	/* The TPM fails the I2C read until it is ready, so we do the entire
> +	/*
> +	 * The TPM fails the I2C read until it is ready, so we do the entire
>  	 * transfer here and buffer it locally. This way the common code can
> -	 * properly handle the timeouts. */
> +	 * properly handle the timeouts.
> +	 */
>  	priv->len = 0;
>  	memset(priv->buffer, 0, sizeof(priv->buffer));
>  
>  
> -	/* Once the TPM has completed the command the command remains readable
> -	 * until another command is issued. */
> +	/*
> +	 * Once the TPM has completed the command the command remains readable
> +	 * until another command is issued.
> +	 */
>  	rc = i2c_master_recv(client, priv->buffer, sizeof(priv->buffer));
>  	dev_dbg(&chip->dev,
>  		"%s: sts=%d", __func__, rc);
> @@ -172,9 +182,11 @@ static int i2c_atmel_probe(struct i2c_client *client)
>  
>  	dev_set_drvdata(&chip->dev, priv);
>  
> -	/* There is no known way to probe for this device, and all version
> +	/*
> +	 * There is no known way to probe for this device, and all version
>  	 * information seems to be read via TPM commands. Thus we rely on the
> -	 * TPM startup process in the common code to detect the device. */
> +	 * TPM startup process in the common code to detect the device.
> +	 */
>  
>  	return tpm_chip_register(chip);
>  }
> -- 
> 2.53.0
> 

Thank you.

Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>

Applied.

BR, Jarkko

^ permalink raw reply

* Re: [RFC 2/4] tpm_crb: Add new wrapper function to invoke start method
From: Jarkko Sakkinen @ 2026-04-08  8:36 UTC (permalink / raw)
  To: Arun Menon; +Cc: linux-kernel, linux-integrity, Jason Gunthorpe, Peter Huewe
In-Reply-To: <acKG6EQ_tXO8eqfj@fedora>

On Tue, Mar 24, 2026 at 06:13:20PM +0530, Arun Menon wrote:
> 
> On Tue, Mar 24, 2026 at 12:42:19PM +0200, Jarkko Sakkinen wrote:
> > On Tue, Mar 24, 2026 at 12:48:01PM +0530, Arun Menon wrote:
> > > - Extract start invocation code into a separate function called
> > >   crb_trigger_tpm. This will be helpful in repeatedly calling the
> > >   start method with different bits set to toggle between start,
> > >   nextChunk or crbRspRetry behaviours.
> > > - Based on the bit set, we signal the TPM to consume
> > >   contents of the command/response buffer.
> > > - No functional change intended.
> > 
> > Please write a proper commit message, which is not a task list.
> 
> Yes, I shall do that. Thank you for taking a look.

Yeah, and now is good time to improve cosmetic details like this.

I rather take closer look at code when I can run it as I'm not LLM
coding agent, I need to run to understand cannot hallucinate it :-)

BR, Jarkko

^ permalink raw reply

* Re: [RFC 0/4] tpm_crb: Add command and response buffer chunking support
From: Jarkko Sakkinen @ 2026-04-08  8:34 UTC (permalink / raw)
  To: Arun Menon; +Cc: linux-kernel, linux-integrity, Jason Gunthorpe, Peter Huewe
In-Reply-To: <acKGZ33ItHvMkQ_5@fedora>

On Tue, Mar 24, 2026 at 06:11:11PM +0530, Arun Menon wrote:
> Hi Jarkko,
> 
> On Tue, Mar 24, 2026 at 12:41:26PM +0200, Jarkko Sakkinen wrote:
> > On Tue, Mar 24, 2026 at 12:47:59PM +0530, Arun Menon wrote:
> > > The new version of TCG TPM v185 (currently under review [1]) supports
> > > sending data/commands in chunks for the CRB (Command Response Buffer)
> > > interface. This is in line with the initiative to support PQC algorithms.
> > > 
> > > This series implements the logic to send and receive larger TPM
> > > cmd/rsp between the linux guest and the TPM backend in chunks.
> > > Currently, the TPM CRB driver is limited by the physical size of the
> > > MMIO window. When userspace attempts to send a payload that exceeds this
> > > size, the driver rejects it.
> > > 
> > > This series introduces chunking support. The driver now checks the CRB
> > > interface capability for CRB_INTF_CAP_CRB_CHUNK. If supported by the
> > > backend, the driver will slice oversized commands into MMIO-sized
> > > chunks, signalling the backend via CRB_START_NEXT_CHUNK, and finalizing
> > > with CRB_START_INVOKE. Responses are also read back in a similar chunked
> > > manner.
> > > 
> > > If the backend does not support chunking, the driver retains its legacy
> > > behaviour and enforces the standard size limits.
> > > 
> > > This feature also requires the QEMU to interpret the data in chunks and
> > > forward it to the TPM backend and subsequently dispatch the TPM response
> > > in chunks back to the linux guest. This is implemented in [2]
> > > 
> > > [1] https://trustedcomputinggroup.org/wp-content/uploads/PC-Client-Specific-Platform-TPM-Profile-for-TPM-2p0-v1p07_rc1_121225.pdf
> > > [2] https://lore.kernel.org/qemu-devel/20260319135316.37412-1-armenon@redhat.com/
> > > 
> > > Arun Menon (4):
> > >   tpm_crb: Add definition of TPM CRB chunking fields
> > >   tpm_crb: Add new wrapper function to invoke start method
> > >   tpm_crb: Implement command and response chunking logic
> > >   tpm: Increase TPM_BUFSIZE to 64kB for chunking support
> > > 
> > >  drivers/char/tpm/tpm.h     |   2 +-
> > >  drivers/char/tpm/tpm_crb.c | 194 ++++++++++++++++++++++++++-----------
> > >  2 files changed, 137 insertions(+), 59 deletions(-)
> > > 
> > > -- 
> > > 2.53.0
> > > 
> > 
> > When QEMU has the feature available?
> 
> The QEMU patches are in review at the moment,
> here is the link: https://lore.kernel.org/qemu-devel/20260319135316.37412-1-armenon@redhat.com/
> Hoping to have them merged soon.

Right, and additional question: what about swtpm?

For both, to give detailed review, good enough is their main branch
(i.e. as long as upstream accepts them I can use them).

> 
> > 
> > BR, Jarkko
> > 
> 
> Regards,
> Arun Menon
> 

BR, Jarkko

^ permalink raw reply

* Re: [PATCH v2] KEYS: trusted: Debugging as a feature
From: Jarkko Sakkinen @ 2026-04-08  8:29 UTC (permalink / raw)
  To: Nayna Jain
  Cc: linux-integrity, keyrings, Srish Srinivasan, James Bottomley,
	Mimi Zohar, David Howells, Paul Moore, James Morris,
	Serge E. Hallyn, Ahmad Fatoum, Pengutronix Kernel Team, open list,
	open list:SECURITY SUBSYSTEM
In-Reply-To: <adYRURAJfNCu0FYB@kernel.org>

On Wed, Apr 08, 2026 at 11:27:01AM +0300, Jarkko Sakkinen wrote:
> On Mon, Apr 06, 2026 at 10:42:00PM -0400, Nayna Jain wrote:
> > 
> > On 3/24/26 7:00 AM, Jarkko Sakkinen wrote:
> > > TPM_DEBUG, and other similar flags, are a non-standard way to specify a
> > > feature in Linux kernel.  Introduce CONFIG_TRUSTED_KEYS_DEBUG for
> > > trusted keys, and use it to replace these ad-hoc feature flags.
> > > 
> > > Given that trusted keys debug dumps can contain sensitive data, harden
> > > the feature as follows:
> > > 
> > > 1. In the Kconfig description postulate that pr_debug() statements must be
> > >     used.
> > > 2. Use pr_debug() statements in TPM 1.x driver to print the protocol dump.
> > > 
> > > Traces, when actually needed, can be easily enabled by providing
> > > trusted.dyndbg='+p' in the kernel command-line.
> > > 
> > > Cc: Srish Srinivasan <ssrish@linux.ibm.com>
> > > Reported-by: Nayna Jain <nayna@linux.ibm.com>
> > > Closes: https://lore.kernel.org/all/7f8b8478-5cd8-4d97-bfd0-341fd5cf10f9@linux.ibm.com/
> > > Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> > > ---
> > > v2:
> > > - Implement for all trusted keys backends.
> > > - Add HAVE_TRUSTED_KEYS_DEBUG as it is a good practice despite full
> > >    coverage.
> > > ---
> > >   include/keys/trusted-type.h               | 18 +++++-------
> > >   security/keys/trusted-keys/Kconfig        | 19 ++++++++++++
> > >   security/keys/trusted-keys/trusted_caam.c |  4 +--
> > >   security/keys/trusted-keys/trusted_tpm1.c | 36 +++++++++++------------
> > >   4 files changed, 46 insertions(+), 31 deletions(-)
> > > 
> > > diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
> > > index 03527162613f..620a1f890b6b 100644
> > > --- a/include/keys/trusted-type.h
> > > +++ b/include/keys/trusted-type.h
> > > @@ -83,18 +83,16 @@ struct trusted_key_source {
> > >   extern struct key_type key_type_trusted;
> > > -#define TRUSTED_DEBUG 0
> > > -
> > > -#if TRUSTED_DEBUG
> > > +#ifdef CONFIG_TRUSTED_KEYS_DEBUG
> > >   static inline void dump_payload(struct trusted_key_payload *p)
> > >   {
> > > -	pr_info("key_len %d\n", p->key_len);
> > > -	print_hex_dump(KERN_INFO, "key ", DUMP_PREFIX_NONE,
> > > -		       16, 1, p->key, p->key_len, 0);
> > > -	pr_info("bloblen %d\n", p->blob_len);
> > > -	print_hex_dump(KERN_INFO, "blob ", DUMP_PREFIX_NONE,
> > > -		       16, 1, p->blob, p->blob_len, 0);
> > > -	pr_info("migratable %d\n", p->migratable);
> > > +	pr_debug("key_len %d\n", p->key_len);
> > > +	print_hex_dump_debug("key ", DUMP_PREFIX_NONE,
> > > +			     16, 1, p->key, p->key_len, 0);
> > > +	pr_debug("bloblen %d\n", p->blob_len);
> > > +	print_hex_dump_debug("blob ", DUMP_PREFIX_NONE,
> > > +			     16, 1, p->blob, p->blob_len, 0);
> > > +	pr_debug("migratable %d\n", p->migratable);
> > >   }
> > >   #else
> > >   static inline void dump_payload(struct trusted_key_payload *p)
> > > diff --git a/security/keys/trusted-keys/Kconfig b/security/keys/trusted-keys/Kconfig
> > > index 9e00482d886a..2ad9ba0e03f1 100644
> > > --- a/security/keys/trusted-keys/Kconfig
> > > +++ b/security/keys/trusted-keys/Kconfig
> > > @@ -1,10 +1,25 @@
> > >   config HAVE_TRUSTED_KEYS
> > >   	bool
> > > +config HAVE_TRUSTED_KEYS_DEBUG
> > > +	bool
> > > +
> > > +config TRUSTED_KEYS_DEBUG
> > > +	bool "Debug trusted keys"
> > > +	depends on HAVE_TRUSTED_KEYS_DEBUG
> > > +	default n
> > > +	help
> > > +	  Trusted keys backends and core code that support debug dumps
> > > +	  can opt-in that feature here. Dumps must only use DEBUG
> > > +	  level output, as sensitive data may pass by. In the
> > > +	  kernel-command line traces can be enabled via
> > > +	  trusted.dyndbg='+p'.
> > 
> > Would it be good idea to add an explicit note/warning:
> > 
> > 
> > NOTE: This option is intended for debugging purposes only. Do not enable on
> > production systems as debug output may expose sensitive cryptographic
> > material.
> > If you are unsure, say N.
> > 
> > Apart from this, looks good to me.
> > 
> > Reviewed-by: Nayna Jain <nayna@linux.ibm.com>
> 
> Thank, I'll add your tag but would you mind quickly screening v3 again
> where I add "trusted.debug=0|1". And yes, your suggestion about extra
> warning makes sense.
> 
> Let's make this safe as possible. Mistakes do happen... and then those
> measures pay off :-)

E.g., in 2026 world perfectly realistic scenario is "agentic devops
team" (unfortunately), which might debug trusted keys issue, and leave
debug flag on. Thus, both warning you suggested and also boot option
for good measure do actually leverage risks involved.

BR, Jarkko

^ permalink raw reply

* Re: [PATCH v2] KEYS: trusted: Debugging as a feature
From: Jarkko Sakkinen @ 2026-04-08  8:26 UTC (permalink / raw)
  To: Nayna Jain
  Cc: linux-integrity, keyrings, Srish Srinivasan, James Bottomley,
	Mimi Zohar, David Howells, Paul Moore, James Morris,
	Serge E. Hallyn, Ahmad Fatoum, Pengutronix Kernel Team, open list,
	open list:SECURITY SUBSYSTEM
In-Reply-To: <afc489d2-a62f-4604-8e56-219311b46516@linux.ibm.com>

On Mon, Apr 06, 2026 at 10:42:00PM -0400, Nayna Jain wrote:
> 
> On 3/24/26 7:00 AM, Jarkko Sakkinen wrote:
> > TPM_DEBUG, and other similar flags, are a non-standard way to specify a
> > feature in Linux kernel.  Introduce CONFIG_TRUSTED_KEYS_DEBUG for
> > trusted keys, and use it to replace these ad-hoc feature flags.
> > 
> > Given that trusted keys debug dumps can contain sensitive data, harden
> > the feature as follows:
> > 
> > 1. In the Kconfig description postulate that pr_debug() statements must be
> >     used.
> > 2. Use pr_debug() statements in TPM 1.x driver to print the protocol dump.
> > 
> > Traces, when actually needed, can be easily enabled by providing
> > trusted.dyndbg='+p' in the kernel command-line.
> > 
> > Cc: Srish Srinivasan <ssrish@linux.ibm.com>
> > Reported-by: Nayna Jain <nayna@linux.ibm.com>
> > Closes: https://lore.kernel.org/all/7f8b8478-5cd8-4d97-bfd0-341fd5cf10f9@linux.ibm.com/
> > Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> > ---
> > v2:
> > - Implement for all trusted keys backends.
> > - Add HAVE_TRUSTED_KEYS_DEBUG as it is a good practice despite full
> >    coverage.
> > ---
> >   include/keys/trusted-type.h               | 18 +++++-------
> >   security/keys/trusted-keys/Kconfig        | 19 ++++++++++++
> >   security/keys/trusted-keys/trusted_caam.c |  4 +--
> >   security/keys/trusted-keys/trusted_tpm1.c | 36 +++++++++++------------
> >   4 files changed, 46 insertions(+), 31 deletions(-)
> > 
> > diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
> > index 03527162613f..620a1f890b6b 100644
> > --- a/include/keys/trusted-type.h
> > +++ b/include/keys/trusted-type.h
> > @@ -83,18 +83,16 @@ struct trusted_key_source {
> >   extern struct key_type key_type_trusted;
> > -#define TRUSTED_DEBUG 0
> > -
> > -#if TRUSTED_DEBUG
> > +#ifdef CONFIG_TRUSTED_KEYS_DEBUG
> >   static inline void dump_payload(struct trusted_key_payload *p)
> >   {
> > -	pr_info("key_len %d\n", p->key_len);
> > -	print_hex_dump(KERN_INFO, "key ", DUMP_PREFIX_NONE,
> > -		       16, 1, p->key, p->key_len, 0);
> > -	pr_info("bloblen %d\n", p->blob_len);
> > -	print_hex_dump(KERN_INFO, "blob ", DUMP_PREFIX_NONE,
> > -		       16, 1, p->blob, p->blob_len, 0);
> > -	pr_info("migratable %d\n", p->migratable);
> > +	pr_debug("key_len %d\n", p->key_len);
> > +	print_hex_dump_debug("key ", DUMP_PREFIX_NONE,
> > +			     16, 1, p->key, p->key_len, 0);
> > +	pr_debug("bloblen %d\n", p->blob_len);
> > +	print_hex_dump_debug("blob ", DUMP_PREFIX_NONE,
> > +			     16, 1, p->blob, p->blob_len, 0);
> > +	pr_debug("migratable %d\n", p->migratable);
> >   }
> >   #else
> >   static inline void dump_payload(struct trusted_key_payload *p)
> > diff --git a/security/keys/trusted-keys/Kconfig b/security/keys/trusted-keys/Kconfig
> > index 9e00482d886a..2ad9ba0e03f1 100644
> > --- a/security/keys/trusted-keys/Kconfig
> > +++ b/security/keys/trusted-keys/Kconfig
> > @@ -1,10 +1,25 @@
> >   config HAVE_TRUSTED_KEYS
> >   	bool
> > +config HAVE_TRUSTED_KEYS_DEBUG
> > +	bool
> > +
> > +config TRUSTED_KEYS_DEBUG
> > +	bool "Debug trusted keys"
> > +	depends on HAVE_TRUSTED_KEYS_DEBUG
> > +	default n
> > +	help
> > +	  Trusted keys backends and core code that support debug dumps
> > +	  can opt-in that feature here. Dumps must only use DEBUG
> > +	  level output, as sensitive data may pass by. In the
> > +	  kernel-command line traces can be enabled via
> > +	  trusted.dyndbg='+p'.
> 
> Would it be good idea to add an explicit note/warning:
> 
> 
> NOTE: This option is intended for debugging purposes only. Do not enable on
> production systems as debug output may expose sensitive cryptographic
> material.
> If you are unsure, say N.
> 
> Apart from this, looks good to me.
> 
> Reviewed-by: Nayna Jain <nayna@linux.ibm.com>

Thank, I'll add your tag but would you mind quickly screening v3 again
where I add "trusted.debug=0|1". And yes, your suggestion about extra
warning makes sense.

Let's make this safe as possible. Mistakes do happen... and then those
measures pay off :-)

BR, Jarkko

^ permalink raw reply

* Re: [PATCH v2] KEYS: trusted: Debugging as a feature
From: Jarkko Sakkinen @ 2026-04-08  8:24 UTC (permalink / raw)
  To: Srish Srinivasan
  Cc: linux-integrity, keyrings, Nayna Jain, James Bottomley,
	Mimi Zohar, David Howells, Paul Moore, James Morris,
	Serge E. Hallyn, Ahmad Fatoum, Pengutronix Kernel Team, open list,
	open list:SECURITY SUBSYSTEM
In-Reply-To: <0ce8d850-9ca7-4327-a6be-d1cb84925915@linux.ibm.com>

On Thu, Mar 26, 2026 at 10:34:58PM +0530, Srish Srinivasan wrote:
> 
> On 3/24/26 4:30 PM, Jarkko Sakkinen wrote:
> > TPM_DEBUG, and other similar flags, are a non-standard way to specify a
> > feature in Linux kernel.  Introduce CONFIG_TRUSTED_KEYS_DEBUG for
> > trusted keys, and use it to replace these ad-hoc feature flags.
> > 
> > Given that trusted keys debug dumps can contain sensitive data, harden
> > the feature as follows:
> > 
> > 1. In the Kconfig description postulate that pr_debug() statements must be
> >     used.
> > 2. Use pr_debug() statements in TPM 1.x driver to print the protocol dump.
> > 
> > Traces, when actually needed, can be easily enabled by providing
> > trusted.dyndbg='+p' in the kernel command-line.
> > 
> > Cc: Srish Srinivasan <ssrish@linux.ibm.com>
> > Reported-by: Nayna Jain <nayna@linux.ibm.com>
> > Closes: https://lore.kernel.org/all/7f8b8478-5cd8-4d97-bfd0-341fd5cf10f9@linux.ibm.com/
> > Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> 
> 
> Tested on PKWM and emulated TPM backends.
> 
> Tested-by: Srish Srinivasan <ssrish@linux.ibm.com>

Thank you!

As it is uprised as a feature (like it should as ad-hoc compilation
flags are harmful), this also requires a boot flag so that "I know
what I'm doing" is addressed.

I'll send one more round with a flag 'trusted.debug=0|1'. These extra
steps protect production kernels for reasonable measure.

BR, Jarkko

^ permalink raw reply

* Re: [PATCH v5 2/3] ima: trim N IMA event log records
From: Roberto Sassu @ 2026-04-07 16:19 UTC (permalink / raw)
  To: steven chen, linux-integrity
  Cc: zohar, roberto.sassu, dmitry.kasatkin, eric.snowberg, corbet,
	serge, paul, jmorris, linux-security-module, anirudhve,
	gregorylumen, nramas, sushring, linux-doc
In-Reply-To: <20260401172956.4581-3-chenste@linux.microsoft.com>

On Wed, 2026-04-01 at 10:29 -0700, steven chen wrote:
> Trim N entries of the IMA event logs. Do not clean the hash table.

The very first change of this patch is the kernel option
ima_flush_htable option that I introduced for my use case.

At the bottom of this patch you actually check the ima_flush_htable
boolean, and delete the measurements entries without disconnecting them
from the hash table, so the digest lookup is done on freed memory.

Next, you duplicated my changes regarding the measurements list
counter. But instead of removing the old counter from the hash table,
you keep incrementing both, but use the new one.

In ima_log_trim_open(), you use again my duplicated code to manage
exclusive write/concurrent read scheme for the measurement interfaces.
However, for read, if the process does not have CAP_SYS_ADMIN it falls
back calling _ima_measurements_open(). Not sure it was intended.

And, in ima_log_trim_release(), you check again CAP_SYS_ADMIN which is
redundant, you would not reach this code if the same requirements were
not met at open time. You also return an error on close().

In ima_log_trim_write(), you do manual string to number conversion for
your first number and use kstrtoul() for the second.

The measurements lists and the associated counter are atomically
updated in ima_add_digest_entry(), but not atomically accessed in
ima_delete_event_log(). Also, the measurements list is traversed
without _rcu variant or lock.

While this trimming scheme aims at minimizing the kernel space and user
space delay, it also introduces the following problem. If two agents
perform a TPM quote that include a different number of entries, there
is no guarantee that the one willing to trim less entries wins. Which
means that, one agent could end up not seeing the most recent entries,
as they were already trimmed by the other agent.

My solution is not affected by this problem, since there will be only
one process collecting all the measurements in user space and exposing
them to the agents.

Also, I didn't understand why T and ima_measure_users have to be
preserved on soft reboots. Especially ima_measure_users reflects the
state of open files for a particular kernel, but on soft reboot a new
kernel is booted.

I personally will not endorse a solution based on the ima_trim_log
interface. I could accept trimming N even more efficiently than we
currently do with a lockless walk to determine the cutting position in
ima_queue_stage(), so that we don't need to splice back entries to the
measurement list. This would be a replacement of patch 11 in my patch
set, but this would be as far as I would like to go.

Roberto

> The values saved in hash table were already used.
> 
> Provide a userspace interface ima_trim_log:
> When read this interface, it returns total number T of entries trimmed
> since system boot up.
> When write to this interface need to provide two numbers T:N to let
> kernel to trim N entries of IMA event logs.
> 
> Kernel measurement list lock time performance improvement by not
> clean the hash table.
> 
> when kernel get log trim request T:N
>  - Get the T, compare with the total trimmed number
>  - if equal, then do trim N and change T to T+N
>  - else return error
> 
> Signed-off-by: steven chen <chenste@linux.microsoft.com>
> ---
>  .../admin-guide/kernel-parameters.txt         |   4 +
>  security/integrity/ima/ima.h                  |   4 +-
>  security/integrity/ima/ima_fs.c               | 198 +++++++++++++++++-
>  security/integrity/ima/ima_kexec.c            |   2 +-
>  security/integrity/ima/ima_queue.c            |  96 +++++++++
>  5 files changed, 296 insertions(+), 8 deletions(-)
> 
> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
> index e92c0056e4e0..cd1a1d0bf0e2 100644
> --- a/Documentation/admin-guide/kernel-parameters.txt
> +++ b/Documentation/admin-guide/kernel-parameters.txt
> @@ -2197,6 +2197,10 @@
>  			Use the canonical format for the binary runtime
>  			measurements, instead of host native format.
>  
> +	ima_flush_htable  [IMA]
> +			Flush the measurement list hash table when trim all
> +			or a part of it for deletion.
> +
>  	ima_hash=	[IMA]
>  			Format: { md5 | sha1 | rmd160 | sha256 | sha384
>  				   | sha512 | ... }
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index e3d71d8d56e3..5cbee3a295a0 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -243,11 +243,13 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
>  				   const void *payload, size_t plen,
>  				   unsigned long flags, bool create);
>  #endif
> -
> +extern atomic_long_t ima_number_entries;
>  #ifdef CONFIG_IMA_KEXEC
>  void ima_measure_kexec_event(const char *event_name);
> +long ima_delete_event_log(long req_val);
>  #else
>  static inline void ima_measure_kexec_event(const char *event_name) {}
> +static inline long ima_delete_event_log(long req_val) { return 0; }
>  #endif
>  
>  /*
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index 87045b09f120..8e26e0f34311 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -21,6 +21,9 @@
>  #include <linux/rcupdate.h>
>  #include <linux/parser.h>
>  #include <linux/vmalloc.h>
> +#include <linux/ktime.h>
> +#include <linux/timekeeping.h>
> +#include <linux/ima.h>
>  
>  #include "ima.h"
>  
> @@ -38,6 +41,17 @@ __setup("ima_canonical_fmt", default_canonical_fmt_setup);
>  
>  static int valid_policy = 1;
>  
> +#define IMA_LOG_TRIM_REQ_NUM_LENGTH 15
> +#define IMA_LOG_TRIM_REQ_TOTAL_LENGTH 32
> +atomic_long_t ima_number_entries = ATOMIC_LONG_INIT(0);
> +static long trimcount;
> +/* mutex protects atomicity of trimming measurement list
> + * and also protects atomicity the measurement list read
> + * write operation.
> + */
> +static DEFINE_MUTEX(ima_measure_lock);
> +static long ima_measure_users;
> +
>  static ssize_t ima_show_htable_value(char __user *buf, size_t count,
>  				     loff_t *ppos, atomic_long_t *val)
>  {
> @@ -64,8 +78,7 @@ static ssize_t ima_show_measurements_count(struct file *filp,
>  					   char __user *buf,
>  					   size_t count, loff_t *ppos)
>  {
> -	return ima_show_htable_value(buf, count, ppos, &ima_htable.len);
> -
> +	return ima_show_htable_value(buf, count, ppos, &ima_number_entries);
>  }
>  
>  static const struct file_operations ima_measurements_count_ops = {
> @@ -202,16 +215,77 @@ static const struct seq_operations ima_measurments_seqops = {
>  	.show = ima_measurements_show
>  };
>  
> +/*
> + * _ima_measurements_open - open the IMA measurements file
> + * @inode: inode of the file being opened
> + * @file: file being opened
> + * @seq_ops: sequence operations for the file
> + *
> + * Returns 0 on success, or negative error code.
> + * Implements mutual exclusion between readers and writer
> + * of the measurements file. Multiple readers are allowed,
> + * but writer get exclusive access only no other readers/writers.
> + * Readers is not allowed when there is a writer.
> + */
> +static int _ima_measurements_open(struct inode *inode, struct file *file,
> +				  const struct seq_operations *seq_ops)
> +{
> +	bool write = !!(file->f_mode & FMODE_WRITE);
> +	int ret;
> +
> +	if (write && !capable(CAP_SYS_ADMIN))
> +		return -EPERM;
> +
> +	mutex_lock(&ima_measure_lock);
> +	if ((write && ima_measure_users != 0) ||
> +	    (!write && ima_measure_users < 0)) {
> +		mutex_unlock(&ima_measure_lock);
> +		return -EBUSY;
> +	}
> +
> +	ret = seq_open(file, seq_ops);
> +	if (ret < 0) {
> +		mutex_unlock(&ima_measure_lock);
> +		return ret;
> +	}
> +
> +	if (write)
> +		ima_measure_users--;
> +	else
> +		ima_measure_users++;
> +
> +	mutex_unlock(&ima_measure_lock);
> +	return ret;
> +}
> +
>  static int ima_measurements_open(struct inode *inode, struct file *file)
>  {
> -	return seq_open(file, &ima_measurments_seqops);
> +	return _ima_measurements_open(inode, file, &ima_measurments_seqops);
> +}
> +
> +static int ima_measurements_release(struct inode *inode, struct file *file)
> +{
> +	bool write = !!(file->f_mode & FMODE_WRITE);
> +	int ret;
> +
> +	mutex_lock(&ima_measure_lock);
> +	ret = seq_release(inode, file);
> +	if (!ret) {
> +		if (!write)
> +			ima_measure_users--;
> +		else
> +			ima_measure_users++;
> +	}
> +
> +	mutex_unlock(&ima_measure_lock);
> +	return ret;
>  }
>  
>  static const struct file_operations ima_measurements_ops = {
>  	.open = ima_measurements_open,
>  	.read = seq_read,
>  	.llseek = seq_lseek,
> -	.release = seq_release,
> +	.release = ima_measurements_release,
>  };
>  
>  void ima_print_digest(struct seq_file *m, u8 *digest, u32 size)
> @@ -279,14 +353,114 @@ static const struct seq_operations ima_ascii_measurements_seqops = {
>  
>  static int ima_ascii_measurements_open(struct inode *inode, struct file *file)
>  {
> -	return seq_open(file, &ima_ascii_measurements_seqops);
> +	return _ima_measurements_open(inode, file, &ima_ascii_measurements_seqops);
>  }
>  
>  static const struct file_operations ima_ascii_measurements_ops = {
>  	.open = ima_ascii_measurements_open,
>  	.read = seq_read,
>  	.llseek = seq_lseek,
> -	.release = seq_release,
> +	.release = ima_measurements_release,
> +};
> +
> +static int ima_log_trim_open(struct inode *inode, struct file *file)
> +{
> +	bool write = !!(file->f_mode & FMODE_WRITE);
> +
> +	if (!write && capable(CAP_SYS_ADMIN))
> +		return 0;
> +	else if (!capable(CAP_SYS_ADMIN))
> +		return -EPERM;
> +
> +	return _ima_measurements_open(inode, file, &ima_measurments_seqops);
> +}
> +
> +static ssize_t ima_log_trim_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
> +{
> +	char tmpbuf[IMA_LOG_TRIM_REQ_NUM_LENGTH];
> +	ssize_t len;
> +
> +	len = scnprintf(tmpbuf, sizeof(tmpbuf), "%li\n", trimcount);
> +	return simple_read_from_buffer(buf, size, ppos, tmpbuf, len);
> +}
> +
> +static ssize_t ima_log_trim_write(struct file *file,
> +				  const char __user *buf, size_t datalen, loff_t *ppos)
> +{
> +	char tmpbuf[IMA_LOG_TRIM_REQ_TOTAL_LENGTH];
> +	char *p = tmpbuf;
> +	long count, ret, val = 0, max = LONG_MAX;
> +
> +	if (*ppos > 0 || datalen > IMA_LOG_TRIM_REQ_TOTAL_LENGTH || datalen < 2) {
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	if (copy_from_user(tmpbuf, buf, datalen) != 0) {
> +		ret = -EFAULT;
> +		goto out;
> +	}
> +
> +	p = tmpbuf;
> +
> +	while (*p && *p != ':') {
> +		if (!isdigit((unsigned char)*p))
> +			return -EINVAL;
> +
> +		/* digit value */
> +		int d = *p - '0';
> +
> +		/* overflow check: val * 10 + d > max -> (val > (max - d) / 10) */
> +		if (val > (max - d) / 10)
> +			return -ERANGE;
> +
> +		val = val * 10 + d;
> +		p++;
> +	}
> +
> +	if (*p != ':')
> +		return -EINVAL;
> +
> +	/* verify trim count matches */
> +	if (val != trimcount)
> +		return -EINVAL;
> +
> +	p++; /* skip ':' */
> +	ret = kstrtoul(p, 0, &count);
> +
> +	if (ret < 0)
> +		goto out;
> +
> +	ret = ima_delete_event_log(count);
> +
> +	if (ret < 0)
> +		goto out;
> +
> +	trimcount += ret;
> +
> +	ret = datalen;
> +out:
> +	return ret;
> +}
> +
> +static int ima_log_trim_release(struct inode *inode, struct file *file)
> +{
> +	bool write = !!(file->f_mode & FMODE_WRITE);
> +
> +	if (!write && capable(CAP_SYS_ADMIN))
> +		return 0;
> +	else if (!capable(CAP_SYS_ADMIN))
> +		return -EPERM;
> +
> +	return ima_measurements_release(inode, file);
> +}
> +
> +static const struct file_operations ima_log_trim_ops = {
> +	.open = ima_log_trim_open,
> +	.read = ima_log_trim_read,
> +	.write = ima_log_trim_write,
> +	.llseek = generic_file_llseek,
> +	.release = ima_log_trim_release
>  };
>  
>  static ssize_t ima_read_policy(char *path)
> @@ -528,6 +702,18 @@ int __init ima_fs_init(void)
>  		goto out;
>  	}
>  
> +	if (IS_ENABLED(CONFIG_IMA_LOG_TRIMMING)) {
> +		dentry = securityfs_create_file("ima_trim_log",
> +						S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP,
> +						ima_dir, NULL, &ima_log_trim_ops);
> +		if (IS_ERR(dentry)) {
> +			ret = PTR_ERR(dentry);
> +			goto out;
> +		}
> +	}
> +
> +	trimcount = 0;
> +
>  	dentry = securityfs_create_file("runtime_measurements_count",
>  				   S_IRUSR | S_IRGRP, ima_dir, NULL,
>  				   &ima_measurements_count_ops);
> diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
> index 7362f68f2d8b..bee997683e03 100644
> --- a/security/integrity/ima/ima_kexec.c
> +++ b/security/integrity/ima/ima_kexec.c
> @@ -41,7 +41,7 @@ void ima_measure_kexec_event(const char *event_name)
>  	int n;
>  
>  	buf_size = ima_get_binary_runtime_size();
> -	len = atomic_long_read(&ima_htable.len);
> +	len = atomic_long_read(&ima_number_entries);
>  
>  	n = scnprintf(ima_kexec_event, IMA_KEXEC_EVENT_LEN,
>  		      "kexec_segment_size=%lu;ima_binary_runtime_size=%lu;"
> diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
> index 590637e81ad1..07225e19b9b5 100644
> --- a/security/integrity/ima/ima_queue.c
> +++ b/security/integrity/ima/ima_queue.c
> @@ -22,6 +22,14 @@
>  
>  #define AUDIT_CAUSE_LEN_MAX 32
>  
> +bool ima_flush_htable;
> +static int __init ima_flush_htable_setup(char *str)
> +{
> +	ima_flush_htable = true;
> +	return 1;
> +}
> +__setup("ima_flush_htable", ima_flush_htable_setup);
> +
>  /* pre-allocated array of tpm_digest structures to extend a PCR */
>  static struct tpm_digest *digests;
>  
> @@ -114,6 +122,7 @@ static int ima_add_digest_entry(struct ima_template_entry *entry,
>  	list_add_tail_rcu(&qe->later, &ima_measurements);
>  
>  	atomic_long_inc(&ima_htable.len);
> +	atomic_long_inc(&ima_number_entries);
>  	if (update_htable) {
>  		key = ima_hash_key(entry->digests[ima_hash_algo_idx].digest);
>  		hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]);
> @@ -220,6 +229,93 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
>  	return result;
>  }
>  
> +/**
> + * ima_delete_event_log - delete IMA event entry
> + * @num_records: number of records to delete
> + *
> + * delete num_records entries off the measurement list.
> + * Returns num_records, or negative error code.
> + */
> +long ima_delete_event_log(long num_records)
> +{
> +	long len, cur = num_records, tmp_len = 0;
> +	struct ima_queue_entry *qe, *qe_tmp;
> +	LIST_HEAD(ima_measurements_to_delete);
> +	struct list_head *list_ptr;
> +
> +	if (!IS_ENABLED(CONFIG_IMA_LOG_TRIMMING))
> +		return -EOPNOTSUPP;
> +
> +	if (num_records <= 0)
> +		return num_records;
> +
> +	list_ptr = &ima_measurements;
> +
> +	len = atomic_long_read(&ima_number_entries);
> +
> +	if (num_records <= len) {
> +		list_for_each_entry(qe, list_ptr, later) {
> +			if (cur > 0) {
> +				tmp_len += get_binary_runtime_size(qe->entry);
> +				--cur;
> +			}
> +			if (cur == 0) {
> +				qe_tmp = qe;
> +				break;
> +			}
> +		}
> +	}
> +	else {
> +		return -ENOENT;
> +	}
> +
> +
> +	mutex_lock(&ima_extend_list_mutex);
> +	len = atomic_long_read(&ima_number_entries);
> +
> +	if (num_records == len) {
> +		list_replace(&ima_measurements, &ima_measurements_to_delete);
> +		INIT_LIST_HEAD(&ima_measurements);
> +		atomic_long_set(&ima_number_entries, 0);
> +		list_ptr = &ima_measurements_to_delete;
> +	}
> +	else {
> +		__list_cut_position(&ima_measurements_to_delete, &ima_measurements,
> +				    &qe_tmp->later);
> +		atomic_long_sub(num_records, &ima_number_entries);
> +		if (IS_ENABLED(CONFIG_IMA_KEXEC))
> +			binary_runtime_size -= tmp_len;
> +	}
> +
> +	mutex_unlock(&ima_extend_list_mutex);
> +
> +	if (ima_flush_htable)
> +		synchronize_rcu();
> +
> +	list_for_each_entry_safe(qe, qe_tmp, &ima_measurements_to_delete, later) {
> +		/*
> +		 * Ok because after list delete qe is only accessed by
> +		 * ima_lookup_digest_entry().
> +		 */
> +		for (int i = 0; i < qe->entry->template_desc->num_fields; i++) {
> +			kfree(qe->entry->template_data[i].data);
> +			qe->entry->template_data[i].data = NULL;
> +			qe->entry->template_data[i].len = 0;
> +		}
> +
> +		list_del(&qe->later);
> +
> +		/* No leak if !ima_flush_htable, referenced by ima_htable. */
> +		if (ima_flush_htable) {
> +			kfree(qe->entry->digests);
> +			kfree(qe->entry);
> +			kfree(qe);
> +		}
> +	}
> +
> +	return num_records;
> +}
> +
>  int ima_restore_measurement_entry(struct ima_template_entry *entry)
>  {
>  	int result = 0;


^ permalink raw reply

* [PATCH] evm: zero-initialize the evm_xattrs read buffer
From: Pengpeng Hou @ 2026-04-07  6:09 UTC (permalink / raw)
  To: Mimi Zohar, Roberto Sassu
  Cc: Dmitry Kasatkin, Eric Snowberg, Paul Moore, James Morris,
	Serge Hallyn, linux-integrity, linux-security-module,
	linux-kernel, pengpeng

evm_read_xattrs() allocates size + 1 bytes, fills them from the list of
enabled xattrs and then passes strlen(temp) to simple_read_from_buffer().
When no configured xattrs are enabled, the fill loop stores nothing and
temp[0] remains uninitialized, so strlen() reads beyond initialized
memory.

Use kzalloc() so the empty-list case stays a valid empty C string.

Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
 security/integrity/evm/evm_secfs.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c
index acd840461902..03d376fa36c2 100644
--- a/security/integrity/evm/evm_secfs.c
+++ b/security/integrity/evm/evm_secfs.c
@@ -145,7 +145,7 @@ static ssize_t evm_read_xattrs(struct file *filp, char __user *buf,
 		size += strlen(xattr->name) + 1;
 	}
 
-	temp = kmalloc(size + 1, GFP_KERNEL);
+	temp = kzalloc(size + 1, GFP_KERNEL);
 	if (!temp) {
 		mutex_unlock(&xattr_list_mutex);
 		return -ENOMEM;
-- 
2.50.1 (Apple Git-155)



^ permalink raw reply related

* Re: [PATCH v2] KEYS: trusted: Debugging as a feature
From: Nayna Jain @ 2026-04-07  2:42 UTC (permalink / raw)
  To: Jarkko Sakkinen, linux-integrity
  Cc: keyrings, Srish Srinivasan, James Bottomley, Mimi Zohar,
	David Howells, Paul Moore, James Morris, Serge E. Hallyn,
	Ahmad Fatoum, Pengutronix Kernel Team, open list,
	open list:SECURITY SUBSYSTEM
In-Reply-To: <20260324110043.67248-1-jarkko@kernel.org>


On 3/24/26 7:00 AM, Jarkko Sakkinen wrote:
> TPM_DEBUG, and other similar flags, are a non-standard way to specify a
> feature in Linux kernel.  Introduce CONFIG_TRUSTED_KEYS_DEBUG for
> trusted keys, and use it to replace these ad-hoc feature flags.
>
> Given that trusted keys debug dumps can contain sensitive data, harden
> the feature as follows:
>
> 1. In the Kconfig description postulate that pr_debug() statements must be
>     used.
> 2. Use pr_debug() statements in TPM 1.x driver to print the protocol dump.
>
> Traces, when actually needed, can be easily enabled by providing
> trusted.dyndbg='+p' in the kernel command-line.
>
> Cc: Srish Srinivasan <ssrish@linux.ibm.com>
> Reported-by: Nayna Jain <nayna@linux.ibm.com>
> Closes: https://lore.kernel.org/all/7f8b8478-5cd8-4d97-bfd0-341fd5cf10f9@linux.ibm.com/
> Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> ---
> v2:
> - Implement for all trusted keys backends.
> - Add HAVE_TRUSTED_KEYS_DEBUG as it is a good practice despite full
>    coverage.
> ---
>   include/keys/trusted-type.h               | 18 +++++-------
>   security/keys/trusted-keys/Kconfig        | 19 ++++++++++++
>   security/keys/trusted-keys/trusted_caam.c |  4 +--
>   security/keys/trusted-keys/trusted_tpm1.c | 36 +++++++++++------------
>   4 files changed, 46 insertions(+), 31 deletions(-)
>
> diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
> index 03527162613f..620a1f890b6b 100644
> --- a/include/keys/trusted-type.h
> +++ b/include/keys/trusted-type.h
> @@ -83,18 +83,16 @@ struct trusted_key_source {
>   
>   extern struct key_type key_type_trusted;
>   
> -#define TRUSTED_DEBUG 0
> -
> -#if TRUSTED_DEBUG
> +#ifdef CONFIG_TRUSTED_KEYS_DEBUG
>   static inline void dump_payload(struct trusted_key_payload *p)
>   {
> -	pr_info("key_len %d\n", p->key_len);
> -	print_hex_dump(KERN_INFO, "key ", DUMP_PREFIX_NONE,
> -		       16, 1, p->key, p->key_len, 0);
> -	pr_info("bloblen %d\n", p->blob_len);
> -	print_hex_dump(KERN_INFO, "blob ", DUMP_PREFIX_NONE,
> -		       16, 1, p->blob, p->blob_len, 0);
> -	pr_info("migratable %d\n", p->migratable);
> +	pr_debug("key_len %d\n", p->key_len);
> +	print_hex_dump_debug("key ", DUMP_PREFIX_NONE,
> +			     16, 1, p->key, p->key_len, 0);
> +	pr_debug("bloblen %d\n", p->blob_len);
> +	print_hex_dump_debug("blob ", DUMP_PREFIX_NONE,
> +			     16, 1, p->blob, p->blob_len, 0);
> +	pr_debug("migratable %d\n", p->migratable);
>   }
>   #else
>   static inline void dump_payload(struct trusted_key_payload *p)
> diff --git a/security/keys/trusted-keys/Kconfig b/security/keys/trusted-keys/Kconfig
> index 9e00482d886a..2ad9ba0e03f1 100644
> --- a/security/keys/trusted-keys/Kconfig
> +++ b/security/keys/trusted-keys/Kconfig
> @@ -1,10 +1,25 @@
>   config HAVE_TRUSTED_KEYS
>   	bool
>   
> +config HAVE_TRUSTED_KEYS_DEBUG
> +	bool
> +
> +config TRUSTED_KEYS_DEBUG
> +	bool "Debug trusted keys"
> +	depends on HAVE_TRUSTED_KEYS_DEBUG
> +	default n
> +	help
> +	  Trusted keys backends and core code that support debug dumps
> +	  can opt-in that feature here. Dumps must only use DEBUG
> +	  level output, as sensitive data may pass by. In the
> +	  kernel-command line traces can be enabled via
> +	  trusted.dyndbg='+p'.

Would it be good idea to add an explicit note/warning:


NOTE: This option is intended for debugging purposes only. Do not enable 
on production systems as debug output may expose sensitive cryptographic 
material.
If you are unsure, say N.

Apart from this, looks good to me.

Reviewed-by: Nayna Jain <nayna@linux.ibm.com>


^ permalink raw reply

* Re: [PATCH 1/3] crypto: public_key: Remove check for valid hash_algo for ML-DSA keys
From: Eric Biggers @ 2026-04-06 16:53 UTC (permalink / raw)
  To: Stefan Berger
  Cc: linux-integrity, linux-security-module, linux-kernel, zohar,
	roberto.sassu, David Howells, Lukas Wunner, Ignat Korchagin,
	keyrings, linux-crypto
In-Reply-To: <20260405231224.4008298-2-stefanb@linux.ibm.com>

On Sun, Apr 05, 2026 at 07:12:22PM -0400, Stefan Berger wrote:
> Remove the check for the hash_algo since ML-DSA is only used in pure mode
> and there is no relevance of a hash_algo for the input data.
> 
> Cc: David Howells <dhowells@redhat.com>
> Cc: Lukas Wunner <lukas@wunner.de>
> Cc: Ignat Korchagin <ignat@linux.win>
> Cc: keyrings@vger.kernel.org
> Cc: linux-crypto@vger.kernel.org
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> ---
>  crypto/asymmetric_keys/public_key.c | 5 -----
>  1 file changed, 5 deletions(-)
> 
> diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
> index 09a0b83d5d77..df6918a77ab8 100644
> --- a/crypto/asymmetric_keys/public_key.c
> +++ b/crypto/asymmetric_keys/public_key.c
> @@ -147,11 +147,6 @@ software_key_determine_akcipher(const struct public_key *pkey,
>  		   strcmp(pkey->pkey_algo, "mldsa87") == 0) {
>  		if (strcmp(encoding, "raw") != 0)
>  			return -EINVAL;
> -		if (!hash_algo)
> -			return -EINVAL;
> -		if (strcmp(hash_algo, "none") != 0 &&
> -		    strcmp(hash_algo, "sha512") != 0)
> -			return -EINVAL;

Does this broaden which hash algorithms are accepted for CMS signatures
that use ML-DSA and contain signed attributes?

- Eric

^ permalink raw reply

* [ima-evm-utils PATCH 4/5] examples: Implement script to create ML-DSA-65 CA and signing keys
From: Stefan Berger @ 2026-04-06  0:08 UTC (permalink / raw)
  To: linux-integrity; +Cc: zohar, roberto.sassu, ebiggers, coxu, Stefan Berger
In-Reply-To: <20260406000810.4013201-1-stefanb@linux.ibm.com>

ima-gen-local-ca-mldsa65.sh creates a CA with an ML-DSA-65 key and
ima-genkey-mldsa65.sh creates an ML-DSA-65 IMA file signing key along with
its certificate.

Also add a script for creating an ML-DSA-87 IMA file signing key. This key
type is good for local testing with the largest possible signature.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 examples/ima-gen-local-ca-mldsa65.sh | 29 ++++++++++++++++++++++++
 examples/ima-genkey-mldsa65.sh       | 34 ++++++++++++++++++++++++++++
 examples/ima-genkey-mldsa87.sh       | 34 ++++++++++++++++++++++++++++
 3 files changed, 97 insertions(+)
 create mode 100755 examples/ima-gen-local-ca-mldsa65.sh
 create mode 100755 examples/ima-genkey-mldsa65.sh
 create mode 100755 examples/ima-genkey-mldsa87.sh

diff --git a/examples/ima-gen-local-ca-mldsa65.sh b/examples/ima-gen-local-ca-mldsa65.sh
new file mode 100755
index 0000000..e2b54cd
--- /dev/null
+++ b/examples/ima-gen-local-ca-mldsa65.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+GENKEY=ima-local-ca.genkey
+
+cat << __EOF__ >$GENKEY
+[ req ]
+distinguished_name = req_distinguished_name
+prompt = no
+string_mask = utf8only
+x509_extensions = v3_ca
+
+[ req_distinguished_name ]
+O = IMA-CA
+CN = IMA/EVM certificate signing key
+emailAddress = ca@ima-ca
+
+[ v3_ca ]
+basicConstraints=CA:TRUE
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid:always,issuer
+keyUsage = cRLSign, keyCertSign
+__EOF__
+
+openssl req -new -x509 -utf8 -sha256 -days 3650 -batch -config $GENKEY \
+		-outform DER -out ima-local-ca.x509 -keyout ima-local-ca.priv \
+		-newkey mldsa65
+
+openssl x509 -inform DER -in ima-local-ca.x509 -out ima-local-ca.pem
diff --git a/examples/ima-genkey-mldsa65.sh b/examples/ima-genkey-mldsa65.sh
new file mode 100755
index 0000000..b1aaf41
--- /dev/null
+++ b/examples/ima-genkey-mldsa65.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+GENKEY=ima.genkey
+
+cat << __EOF__ >$GENKEY
+[ req ]
+distinguished_name = req_distinguished_name
+prompt = no
+string_mask = utf8only
+x509_extensions = v3_usr
+
+[ req_distinguished_name ]
+O = `hostname`
+CN = `whoami` signing key
+emailAddress = `whoami`@`hostname`
+
+[ v3_usr ]
+basicConstraints=critical,CA:FALSE
+#basicConstraints=CA:FALSE
+keyUsage=digitalSignature
+#keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+extendedKeyUsage=critical,codeSigning
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid
+#authorityKeyIdentifier=keyid,issuer
+__EOF__
+
+openssl req -new -nodes -utf8 -sha256 -days 365 -batch -config $GENKEY \
+		-out csr_ima.pem -keyout privkey_ima.pem \
+		-newkey mldsa65
+openssl x509 -req -in csr_ima.pem -days 365 -extfile $GENKEY -extensions v3_usr \
+		-CA ima-local-ca.pem -CAkey ima-local-ca.priv -CAcreateserial \
+		-outform DER -out x509_ima.der
diff --git a/examples/ima-genkey-mldsa87.sh b/examples/ima-genkey-mldsa87.sh
new file mode 100755
index 0000000..347ff91
--- /dev/null
+++ b/examples/ima-genkey-mldsa87.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+GENKEY=ima.genkey
+
+cat << __EOF__ >$GENKEY
+[ req ]
+distinguished_name = req_distinguished_name
+prompt = no
+string_mask = utf8only
+x509_extensions = v3_usr
+
+[ req_distinguished_name ]
+O = `hostname`
+CN = `whoami` signing key
+emailAddress = `whoami`@`hostname`
+
+[ v3_usr ]
+basicConstraints=critical,CA:FALSE
+#basicConstraints=CA:FALSE
+keyUsage=digitalSignature
+#keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+extendedKeyUsage=critical,codeSigning
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid
+#authorityKeyIdentifier=keyid,issuer
+__EOF__
+
+openssl req -new -nodes -utf8 -sha256 -days 365 -batch -config $GENKEY \
+		-out csr_ima.pem -keyout privkey_ima.pem \
+		-newkey mldsa87
+openssl x509 -req -in csr_ima.pem -days 365 -extfile $GENKEY -extensions v3_usr \
+		-CA ima-local-ca.pem -CAkey ima-local-ca.priv -CAcreateserial \
+		-outform DER -out x509_ima.der
-- 
2.53.0


^ permalink raw reply related

* [ima-evm-utils PATCH 3/5] Support signing with ML-DSA keys when OpenSSL >=3.5 is available
From: Stefan Berger @ 2026-04-06  0:08 UTC (permalink / raw)
  To: linux-integrity; +Cc: zohar, roberto.sassu, ebiggers, coxu, Stefan Berger
In-Reply-To: <20260406000810.4013201-1-stefanb@linux.ibm.com>

OpenSSL >= v3.5.0 supports signing with ML-DSA-44/65/87. Add support for
it to the imaevm_create_sigv3 library function. Since the ML-DSA signatures
require a lot more space for the signature now, increase the size of the
array where the signatures are stored. The following are the sizes of
ML-DSA signatures by key type:

- ML-DSA-44: 2420
- ML-DSA-65: 3309
- ML-DSA-87: 4627

Prevent signature V2 from being created with any other key types than
'RSA', 'EC', 'GOST' (ECRDSA), or 'SM2'.

In the functions that created a v2 signature, only RSA, ECDSA, and ECRDSA
signatures are created and they can easily work with the old buffer size of
less than 1024 bytes.

The size available for extended attributes may be smaller than what is
required by the ML-DSA signature size, and therefore may not be possible
to store for example ML-DSA-87 signatures (depends on type of filesystem).
Nevertheless, extend the MAX_SIGNATURE_SIZE to the required size of
ML-DSA-87 and display an error if writing the signature of a size larger
than 4k did not work.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 README          |   3 +-
 src/evmctl.c    |   4 +
 src/imaevm.h    |   5 +-
 src/libimaevm.c | 258 ++++++++++++++++++++++++++++++++++++++++++++++--
 4 files changed, 258 insertions(+), 12 deletions(-)

diff --git a/README b/README
index 34dfddf..1b0be4c 100644
--- a/README
+++ b/README
@@ -153,7 +153,8 @@ in the kernel (since kernel 3.9). CONFIG_INTEGRITY_ASYMMETRIC_KEYS must be enabl
 
 For v2 and v3 signatures x509 certificate (containing the public key) could be appended to the
 private key (they both are in PEM format) to automatically extract keyid from its Subject
-Key Identifier (SKID).
+Key Identifier (SKID). v3 signatures can be created with the --v3 option. This signature format
+is required for signing with ML-DSA keys.
 
 Integrity keyrings
 ----------------
diff --git a/src/evmctl.c b/src/evmctl.c
index c8da495..44b52f6 100644
--- a/src/evmctl.c
+++ b/src/evmctl.c
@@ -617,6 +617,10 @@ static int sign_evm(const char *file, char *hash_algo, const char *key)
 		if (err < 0) {
 			log_errno_reset(LOG_ERR, "Setting EVM xattr failed: %s",
 					file);
+			if (len >= 4096)
+				log_err("The signature with %zu bytes is likely too large for the file "
+					"extended attribute. Consider using a different key type.\n",
+					len);
 			return err;
 		}
 	}
diff --git a/src/imaevm.h b/src/imaevm.h
index 5a8441b..a098ee3 100644
--- a/src/imaevm.h
+++ b/src/imaevm.h
@@ -74,8 +74,11 @@ typedef struct ossl_provider_st OSSL_PROVIDER;
 #define	DATA_SIZE	4096
 #define SHA1_HASH_LEN   20
 
+#define ML_DSA_87_SIGNATURE_SIZE	4627
+
 #define MAX_DIGEST_SIZE		64
-#define MAX_SIGNATURE_SIZE	1024
+#define MAX_SIGNATURE_SIZE	(1 + sizeof(struct signature_v2_hdr) + \
+				 ML_DSA_87_SIGNATURE_SIZE)
 
 /*
  * The maximum template data size is dependent on the template format. For
diff --git a/src/libimaevm.c b/src/libimaevm.c
index 49bfb62..d26074e 100644
--- a/src/libimaevm.c
+++ b/src/libimaevm.c
@@ -37,6 +37,9 @@
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
 #include <openssl/err.h>
+#if OPENSSL_VERSION_NUMBER >= 0x30000000
+#include <openssl/core_names.h>
+#endif
 
 #if CONFIG_IMA_EVM_ENGINE
 #include <openssl/engine.h>
@@ -81,8 +84,25 @@ struct libimaevm_params imaevm_params = {
 	.hash_algo = DEFAULT_HASH_ALGO,
 };
 
+#define HASH_MAX_DIGESTSIZE 64	/* kernel HASH_MAX_DIGESTSIZE is 64 bytes */
+
+struct ima_file_id {
+	__u8 hash_type;		/* xattr type [enum evm_ima_xattr_type] */
+	__u8 hash_algorithm;	/* Digest algorithm [enum hash_algo] */
+	__u8 hash[HASH_MAX_DIGESTSIZE];
+} __packed;
+
 static void __attribute__ ((constructor)) libinit(void);
 
+/* RSA, ECDSA, ECDRSA, and SM2 all use hashes for signing */
+static inline bool keytype_uses_hash_for_signing(const char *keytype)
+{
+	return strcmp("RSA", keytype) == 0 ||
+		strcmp("EC", keytype) == 0 ||
+		strncmp("GOST2012_", keytype, 9) == 0 ||
+		strcmp("SM2", keytype) == 0;
+}
+
 void imaevm_do_hexdump(FILE *fp, const void *ptr, int len, bool newline)
 {
 	int i;
@@ -460,6 +480,77 @@ void init_public_keys(const char *keyfiles)
 	imaevm_init_public_keys(keyfiles, &g_public_keys);
 }
 
+#if OPENSSL_VERSION_NUMBER >= 0x30500000
+static int verify_hashless(EVP_PKEY *pkey,
+			   const char *algo,
+			   const unsigned char *hash, int hash_size,
+			   unsigned char *sig, int siglen,
+			   enum evm_ima_xattr_type type,
+			   const char *file)
+{
+	struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig;
+	struct ima_file_id file_id = { .hash_type = type, };
+	const char *keytype = EVP_PKEY_get0_type_name(pkey);
+	uint8_t *data = (uint8_t *)&file_id;
+	EVP_SIGNATURE *sig_alg = NULL;
+	unsigned int unused;
+	EVP_PKEY_CTX *ctx;
+	int hash_algo;
+	const char *st;
+	int ret = -1;
+
+	if (!algo) {
+		log_err("Hash algorithm unspecified\n");
+		return -EINVAL;
+	}
+
+	hash_algo = imaevm_get_hash_algo(algo);
+	if (hash_algo < 0) {
+		log_err("Hash algorithm %s not supported\n", algo);
+		return -EINVAL;
+	}
+	file_id.hash_algorithm = hash_algo;
+
+	memcpy(file_id.hash, hash, hash_size);
+	unused = HASH_MAX_DIGESTSIZE - hash_size;
+
+	st = "EVP_PKEY_CTX_new";
+	ctx = EVP_PKEY_CTX_new(pkey, NULL);
+	if (!ctx)
+		goto err;
+	st = "EVP_SIGNATURE_fetch";
+	sig_alg = EVP_SIGNATURE_fetch(NULL, keytype, NULL);
+	if (!sig_alg)
+		goto err;
+	st = "EVP_PKEY_verify_message_init";
+	if (!EVP_PKEY_verify_message_init(ctx, sig_alg, NULL))
+		goto err;
+	st = "EVP_PKEY_verify";
+	ret = EVP_PKEY_verify(ctx, sig + sizeof(*hdr),
+			      siglen - sizeof(*hdr),
+			      data, sizeof(file_id) - unused);
+	if (ret == 1) {
+		ret = 0;
+	} else if (ret == 0) {
+		log_err("%s: verification failed: %d (%s)\n",
+			file, ret, ERR_reason_error_string(ERR_get_error()));
+		output_openssl_errors();
+		ret = 1;
+	}
+err:
+	if (ret < 0 || ret > 1) {
+		log_err("%s: verification failed: %d (%s) in %s\n",
+			file, ret, ERR_reason_error_string(ERR_peek_error()),
+			st);
+		output_openssl_errors();
+		ret = -1;
+	}
+	EVP_SIGNATURE_free(sig_alg);
+	EVP_PKEY_CTX_free(ctx);
+	return ret;
+}
+#endif
+
 /*
  * Verify a signature, prefixed with the signature_v2_hdr, either based
  * directly or indirectly on the file data hash.
@@ -577,6 +668,28 @@ static int verify_hash_v3(struct public_key_entry *public_keys,
 {
 	unsigned char sigv3_hash[MAX_DIGEST_SIZE];
 	int ret;
+#if OPENSSL_VERSION_NUMBER >= 0x30500000
+	struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)(sig + 1);
+	const char *keytype;
+	EVP_PKEY *pkey; // do not free here
+
+	pkey = find_keyid(public_keys, hdr->keyid);
+	if (!pkey) {
+		uint32_t keyid = hdr->keyid;
+
+		if (imaevm_params.verbose > LOG_INFO)
+			log_info("%s: verification failed: unknown keyid %x\n",
+				 file, __be32_to_cpup(&keyid));
+		return -1;
+	}
+
+	keytype = EVP_PKEY_get0_type_name(pkey);
+
+	if (keytype && !keytype_uses_hash_for_signing(keytype))
+		return verify_hashless(pkey, hash_algo,
+				       hash, size, sig + 1, siglen - 1,
+				       sig[0], file);
+#endif
 
 	ret = calc_hash_sigv3(sig[0], hash_algo, hash, sigv3_hash);
 	if (ret < 0)
@@ -587,14 +700,6 @@ static int verify_hash_v3(struct public_key_entry *public_keys,
 				  size, sig + 1, siglen - 1);
 }
 
-#define HASH_MAX_DIGESTSIZE 64	/* kernel HASH_MAX_DIGESTSIZE is 64 bytes */
-
-struct ima_file_id {
-	__u8 hash_type;		/* xattr type [enum evm_ima_xattr_type] */
-	__u8 hash_algorithm;	/* Digest algorithm [enum hash_algo] */
-	__u8 hash[HASH_MAX_DIGESTSIZE];
-} __packed;
-
 /*
  * Calculate the signature format version 3 hash based on the portion
  * of the ima_file_id structure used, not the entire structure.
@@ -1292,7 +1397,7 @@ out:
 #endif /* CONFIG_SIGV1 */
 
 /*
- * @sig is assumed to be of (MAX_SIGNATURE_SIZE - 1) size
+ * @sig is assumed to be of at least (1024 - 1) size
  * Return: -1 signing error, >0 length of signature
  */
 static int sign_hash_v2(const char *algo, const unsigned char *hash,
@@ -1306,6 +1411,7 @@ static int sign_hash_v2(const char *algo, const unsigned char *hash,
 	EVP_PKEY *pkey;
 	char name[20];
 	EVP_PKEY_CTX *ctx = NULL;
+	const char *keytype;
 	const EVP_MD *md;
 	size_t sigsize;
 	const char *st;
@@ -1337,6 +1443,15 @@ static int sign_hash_v2(const char *algo, const unsigned char *hash,
 	if (!pkey)
 		return -1;
 
+#if OPENSSL_VERSION_NUMBER >= 0x30000000
+	keytype = EVP_PKEY_get0_type_name(pkey);
+	/* if it's neither an RSA, EC(R)DSA, nor SM2 key then it cannot be used here */
+	if (keytype && !keytype_uses_hash_for_signing(keytype)) {
+		log_err("sign_hash_v2: Cannot use '%s' type of key\n", keytype);
+		return -1;
+	}
+#endif
+
 	hdr = (struct signature_v2_hdr *)sig;
 	hdr->version = (uint8_t) DIGSIG_VERSION_2;
 
@@ -1379,7 +1494,7 @@ static int sign_hash_v2(const char *algo, const unsigned char *hash,
 	if (!EVP_PKEY_CTX_set_signature_md(ctx, md))
 		goto err;
 	st = "EVP_PKEY_sign";
-	sigsize = MAX_SIGNATURE_SIZE - sizeof(struct signature_v2_hdr) - 1;
+	sigsize = 1024 - sizeof(struct signature_v2_hdr) - 1;
 	if (!EVP_PKEY_sign(ctx, hdr->sig, &sigsize, hash, size))
 		goto err;
 	len = (int)sigsize;
@@ -1451,6 +1566,106 @@ int imaevm_signhash(const char *hashalgo, const unsigned char *hash, int size,
 			    access_info, keyid);
 }
 
+#if OPENSSL_VERSION_NUMBER >= 0x30500000
+static int create_sigv3_hashless(EVP_PKEY *pkey, const char *algo,
+				 const unsigned char *hash, int hash_size,
+				 unsigned char **sig, size_t siglen,
+				 enum evm_ima_xattr_type type,
+				 const struct imaevm_ossl_access *access_info,
+				 uint32_t keyid)
+{
+	const char *keytype = EVP_PKEY_get0_type_name(pkey);
+	struct ima_file_id file_id = { .hash_type = type, };
+	uint8_t *data = (uint8_t *)&file_id;
+	EVP_SIGNATURE *sig_alg = NULL;
+	struct signature_v2_hdr *hdr;
+	EVP_PKEY_CTX *ctx = NULL;
+	bool allocated = false;
+	unsigned int unused;
+	size_t slen = -1;
+	const char *st;
+	int hash_algo;
+	char name[20];
+
+	hash_algo = imaevm_get_hash_algo(algo);
+	if (hash_algo < 0) {
+		log_err("Hash algorithm %s not supported\n", algo);
+		return -EINVAL;
+	}
+	file_id.hash_algorithm = hash_algo;
+	memcpy(file_id.hash, hash, hash_size);
+	unused = HASH_MAX_DIGESTSIZE - hash_size;
+
+	st = "EVP_PKEY_CTX_new";
+	ctx = EVP_PKEY_CTX_new(pkey, NULL);
+	if (!ctx)
+		goto err;
+	st = "EVP_SIGNATURE_fetch";
+	sig_alg = EVP_SIGNATURE_fetch(NULL, keytype, NULL);
+	if (!sig_alg)
+		goto err;
+	st = "EVP_PKEY_sign_init";
+	if (!EVP_PKEY_sign_message_init(ctx, sig_alg, NULL))
+		goto err;
+	st = "EVP_PKEY_sign";
+	/* query for size of signature */
+	if (!EVP_PKEY_sign(ctx, NULL, &slen,
+			   data, sizeof(file_id) - unused)) {
+		slen = -1;
+		goto err;
+	}
+
+	if (*sig) {
+		if (siglen < (1 + sizeof(*hdr) + slen)) {
+			siglen = -1;
+			goto err;
+		}
+	} else {
+		*sig = malloc(1 + sizeof(*hdr) + slen);
+		if (!*sig) {
+			siglen = -1;
+			goto err;
+		}
+		allocated = true;
+	}
+	hdr = (struct signature_v2_hdr *)(*sig + 1);
+
+	if (!EVP_PKEY_sign(ctx, hdr->sig, &slen,
+			   data, sizeof(file_id) - unused)) {
+		slen = -1;
+		goto err;
+	}
+
+	(*sig)[0] = type;
+	hdr->version = DIGSIG_VERSION_3;
+	hdr->hash_algo = hash_algo;
+	if (keyid)
+		keyid = htonl(keyid);
+	else
+		calc_keyid_v2(&keyid, name, pkey);
+	hdr->keyid = keyid;
+	hdr->sig_size = __cpu_to_be16(slen);
+
+	siglen = 1 + sizeof(*hdr) + slen;
+	log_info("evm/ima signature: %zu bytes\n", siglen);
+
+err:
+	EVP_SIGNATURE_free(sig_alg);
+	EVP_PKEY_CTX_free(ctx);
+	if (slen == -1) {
+		if (allocated) {
+			free(*sig);
+			*sig = NULL;
+		}
+		log_err("create_sigv3_mldsa signing failed: (%s) in %s\n",
+			ERR_reason_error_string(ERR_peek_error()), st);
+		output_openssl_errors();
+		return -1;
+	}
+	return siglen;
+}
+#endif
+
 /*
  * Create a v3 signature given a file hash
  *
@@ -1482,6 +1697,29 @@ int imaevm_create_sigv3(const char *hash_algo, const unsigned char *hash, int si
 	/* buffer capable of holding (more than) RSA-4096 signature; */
 	unsigned char sigbuf[1024];
 	int len, slen, err;
+#if OPENSSL_VERSION_NUMBER >= 0x30500000
+	const char *keytype;
+	EVP_PKEY *pkey;
+
+	if (access_info) {
+		err = check_ossl_access(access_info);
+		if (err)
+			return err;
+	}
+	pkey = read_priv_pkey(keyfile, keypass, access_info, keyid);
+	if (!pkey)
+		return -1;
+
+	keytype = EVP_PKEY_get0_type_name(pkey);
+	if (keytype && strncmp("ML-DSA-", keytype, 7) == 0) {
+		slen = create_sigv3_hashless(pkey, hash_algo, hash, size,
+					     sig, siglen,
+					     xattr_type, access_info, keyid);
+		EVP_PKEY_free(pkey);
+		return slen;
+	}
+	EVP_PKEY_free(pkey);
+#endif
 
 	len = calc_hash_sigv3(xattr_type, hash_algo, hash, sigv3_hash);
 	if (len < 0 || len == 1) {
-- 
2.53.0


^ permalink raw reply related

* [ima-evm-utils PATCH 5/5] test: Add tests for signing and verifying with ML-DSA keys
From: Stefan Berger @ 2026-04-06  0:08 UTC (permalink / raw)
  To: linux-integrity; +Cc: zohar, roberto.sassu, ebiggers, coxu, Stefan Berger
In-Reply-To: <20260406000810.4013201-1-stefanb@linux.ibm.com>

Create ML-DSA-44 & ML-DSA-65 keys if ML-DSA-44 can be created with the
installed version of OpenSSL. Add test cases for signing and verifying with
these types of keys.

Do not test with ML-DSA-87 keys since the signatures they create may be
too large for some filesystems' xattrs. On Btrfs for example it would be
possible to store the large signatures.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 tests/gen-keys.sh      | 22 ++++++++++++++++++++++
 tests/sign_verify.test | 40 ++++++++++++++++++++++++++++++++--------
 2 files changed, 54 insertions(+), 8 deletions(-)

diff --git a/tests/gen-keys.sh b/tests/gen-keys.sh
index db0189a..13e6d77 100755
--- a/tests/gen-keys.sh
+++ b/tests/gen-keys.sh
@@ -148,6 +148,28 @@ if [ -x /opt/openssl3/bin/openssl ]; then
   done)
 fi
 
+# If creating mldsa44 key works, create all ML-DSA sizes
+if openssl genpkey -algorithm mldsa44 &>/dev/null; then
+  for mldsa in mldsa44 mldsa65; do
+    if [ "$1" = clean ] || [ "$1" = force ]; then
+      rm -f test-$mldsa.cer test-$mldsa.key test-$mldsa.pub
+    fi
+    if [ "$1" = clean ]; then
+      continue
+    fi
+    if [ ! -e test-$mldsa.key ]; then
+      log openssl req -verbose -new -nodes -utf8 -sha256 -days 10000 -batch -x509 \
+        -config test-ca.conf \
+        -newkey "$mldsa" \
+        -out test-$mldsa.cer -outform DER \
+        -keyout test-$mldsa.key
+      if [ -s test-$mldsa.key ]; then
+        log openssl pkey -in test-$mldsa.key -out test-$mldsa.pub -pubout
+      fi
+    fi
+  done
+fi
+
 # This script leaves test-ca.conf, *.cer, *.pub, *.key files for sing/verify tests.
 # They are never deleted except by `make distclean'.
 
diff --git a/tests/sign_verify.test b/tests/sign_verify.test
index 9319123..2b94b91 100755
--- a/tests/sign_verify.test
+++ b/tests/sign_verify.test
@@ -166,8 +166,15 @@ check_sign() {
     fi
 
     # Can openssl sign with this digest and key?
-    cmd="openssl dgst $OPENSSL_ENGINE $OPENSSL_KEYFORM -$ALG -sign $key -hex $FILE"
-    echo - "$cmd"
+    case "${KEY:0:10}" in
+    "test-mldsa")
+      cmd="openssl pkeyutl -sign -inkey $key -in $FILE"
+      echo >> "$FILE" # need at least 1 byte in the file for signing to work
+      ;;
+    *)
+      cmd="openssl dgst $OPENSSL_ENGINE $OPENSSL_KEYFORM -$ALG -sign $key -hex $FILE"
+      ;;
+    esac
     if ! $cmd >/dev/null; then
       echo "${CYAN}$ALG ($key) test is skipped (openssl is unable to sign)$NORM"
       return "$SKIP"
@@ -216,11 +223,21 @@ check_sign() {
   if [[ "$OPTS" =~ "--v3" ]]; then
     # In case of v3 signatures we need to create ima_file_id now.
     # All data for it can be found in PREFIX and by hashing $FILE.
-    echo -en "\x${PREFIX:2:2}\x${PREFIX:6:2}" > "$FILE.tmp"
+    echo -en "\x${PREFIX:2:2}\x${PREFIX:6:2}" > "$FILE.ima_file_id"
     # shellcheck disable=SC2086
-    openssl dgst $OPENSSL_ENGINE $OPENSSL_KEYFORM -"$ALG" -binary "$FILE" >> "$FILE.tmp"
-    cmd="openssl dgst $OPENSSL_ENGINE $OPENSSL_KEYFORM -$ALG -verify ${verifykey} \
-	  -signature $FILE.sig2 $FILE.tmp"
+    openssl dgst $OPENSSL_ENGINE $OPENSSL_KEYFORM -"$ALG" -binary "$FILE" \
+        >> "$FILE.ima_file_id"
+
+    case "${KEY:0:10}" in
+    "test-mldsa")
+      # ML-DSA does not accept a hash algorithm on command line
+      cmd="openssl dgst $OPENSSL_ENGINE $OPENSSL_KEYFORM       -verify ${verifykey} \
+	    -signature $FILE.sig2 $FILE.ima_file_id"
+      ;;
+    *)
+      cmd="openssl dgst $OPENSSL_ENGINE $OPENSSL_KEYFORM -$ALG -verify ${verifykey} \
+	    -signature $FILE.sig2 $FILE.ima_file_id"
+    esac
     sigver=3
   else
     cmd="openssl dgst $OPENSSL_ENGINE $OPENSSL_KEYFORM -$ALG -verify ${verifykey} \
@@ -232,11 +249,11 @@ check_sign() {
     color_red_on_failure
     echo "Signature v${sigver} verification with openssl is failed."
     color_restore
-    rm "$FILE.sig2" "$FILE.tmp"
+    rm "$FILE.sig2" "$FILE.ima_file_id"
     return "$FAIL"
   fi
 
-  rm "$FILE.sig2" "$FILE.tmp"
+  rm "$FILE.sig2" "$FILE.ima_file_id"
   return "$OK"
 }
 
@@ -424,6 +441,13 @@ sign_verify  prime256v1 sha256   0x030304:K:004[345678] --v3
 sign_verify  prime256v1 sha384   0x030305:K:004[345678] --v3
 sign_verify  prime256v1 sha512   0x030306:K:004[345678] --v3
 
+sign_verify  mldsa44    sha256   0x030304:K:0974 --v3
+sign_verify  mldsa44    sha384   0x030305:K:0974 --v3
+sign_verify  mldsa44    sha512   0x030306:K:0974 --v3
+sign_verify  mldsa65    sha256   0x030304:K:0ced --v3
+sign_verify  mldsa65    sha384   0x030305:K:0ced --v3
+sign_verify  mldsa65    sha512   0x030306:K:0ced --v3
+
 # If openssl 3.0 is installed, test the SM2/3 algorithm combination
 ssl_major_version=$(openssl version | sed -n 's/^OpenSSL \([^\.]\).*/\1/p')
 if [ "${ssl_major_version}" = 3 ]; then
-- 
2.53.0


^ permalink raw reply related

* [ima-evm-utils PATCH 1/5] checkpatch: Remove warning when function name is found in output string
From: Stefan Berger @ 2026-04-06  0:08 UTC (permalink / raw)
  To: linux-integrity; +Cc: zohar, roberto.sassu, ebiggers, coxu, Stefan Berger
In-Reply-To: <20260406000810.4013201-1-stefanb@linux.ibm.com>

Remove the following type of warning:

WARNING: Prefer using '"%s...", __func__' to using 'create_sigv3_mldsa', \
   this function's name, in a string

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 scripts/checkpatch.pl | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 3d22bf8..f9553be 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -6285,13 +6285,13 @@ sub process {
 # This does not work very well for -f --file checking as it depends on patch
 # context providing the function name or a single line form for in-file
 # function declarations
-		if ($line =~ /^\+.*$String/ &&
-		    defined($context_function) &&
-		    get_quoted_string($line, $rawline) =~ /\b$context_function\b/ &&
-		    length(get_quoted_string($line, $rawline)) != (length($context_function) + 2)) {
-			WARN("EMBEDDED_FUNCTION_NAME",
-			     "Prefer using '\"%s...\", __func__' to using '$context_function', this function's name, in a string\n" . $herecurr);
-		}
+#		if ($line =~ /^\+.*$String/ &&
+#		    defined($context_function) &&
+#		    get_quoted_string($line, $rawline) =~ /\b$context_function\b/ &&
+#		    length(get_quoted_string($line, $rawline)) != (length($context_function) + 2)) {
+#			WARN("EMBEDDED_FUNCTION_NAME",
+#			     "Prefer using '\"%s...\", __func__' to using '$context_function', this function's name, in a string\n" . $herecurr);
+#		}
 
 # check for unnecessary function tracing like uses
 # This does not use $logFunctions because there are many instances like
-- 
2.53.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