* Re: [PATCH] integrity: Allow sigv3 verification on EVM_XATTR_PORTABLE_DIGSIG
From: Mimi Zohar @ 2026-03-25 14:56 UTC (permalink / raw)
To: Stefan Berger, linux-integrity; +Cc: linux-kernel, ebiggers, roberto.sassu
In-Reply-To: <20260325001051.754093-1-stefanb@linux.ibm.com>
On Tue, 2026-03-24 at 20:10 -0400, Stefan Berger wrote:
> Allow sigv3 verification on EVM_XATTR_PORTABLE_DIGSIG on RSA, ECDSA,
> ECRDSA, and SM2 signatures.
>
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Thanks, Stefan.
IMA makes an exception allowing an EVM signature in lieu of an IMA signature,
when there is no IMA signature. If the IMA policy rule requires an IMA sigv3
type signature, then EVM should also require a sigv3 type signature.
Currently any EVM signature type suffices.
Testing results:
# EVM v2 type signature, with IMA hash (improperly succeeds)
evmctl sign --imahash -o --hashalgo sha256 --key <key> filename-v2
# EVM v2 type signature, with IMA v2 type signature (fails properly)
evmctl sign --imasig -o --hashalgo sha256 --key f<key> filename-v2-imasig
# EVM v3 type signature, with IMA file hash (succeeds)
evmctl sign --v3 --imahash -o --hashalgo sha256 --key <key> filename-v3
# EVM v3 type signature, with IMA v3 type signature (succeeds)
evmctl sign --v3 --imasig -o --hashalgo sha256 --key <key> filename-v3-imasig
Mimi
> ---
> security/integrity/digsig_asymmetric.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
> index 6b21b9bf829e..6e68ec3becbd 100644
> --- a/security/integrity/digsig_asymmetric.c
> +++ b/security/integrity/digsig_asymmetric.c
> @@ -154,7 +154,8 @@ static int calc_file_id_hash(enum evm_ima_xattr_type type,
> size_t file_id_size;
> int rc;
>
> - if (type != IMA_VERITY_DIGSIG && type != EVM_IMA_XATTR_DIGSIG)
> + if (type != IMA_VERITY_DIGSIG && type != EVM_IMA_XATTR_DIGSIG &&
> + type != EVM_XATTR_PORTABLE_DIGSIG)
> return -EINVAL;
>
> tfm = crypto_alloc_shash(hash_algo_name[algo], 0, 0);
>
> base-commit: 3fa0dea50084da8279fb4b1175b3a9b4fc16f052
^ permalink raw reply
* Re: [PATCH 0/3] ima: add regular file data hash support for sigv3
From: Stefan Berger @ 2026-03-25 0:15 UTC (permalink / raw)
To: Mimi Zohar, linux-integrity; +Cc: Eric Biggers
In-Reply-To: <20260324203929.2475782-1-zohar@linux.ibm.com>
On 3/24/26 4:39 PM, Mimi Zohar wrote:
> IMA signature version 3 (sigv3) support was introduced to avoid file
> signature ambiguity. Instead of directly signing a raw fs-verity hash,
> IMA signs the hash of ima_file_id structure, containing the type of
> signature, the hash algorithm, and the hash.
>
> Pure ML-DSA calculates and signs the hash directly rather than a
> pre-hashed digest. To avoid ML-DSA having to re-calculate the file data
> hash, Eric Biggers suggested signing the smaller ima_file_id structure.
>
> This patch set adds the sigv3 support for regular file data hashes. A
> subsequent patch set will add the ML-DSA support.
>
> Mimi Zohar (3):
> ima: Define asymmetric_verify_v3() to verify IMA sigv3 signatures
> ima: add regular file data hash signature version 3 support
> ima: add support to require IMA sigv3 signatures
>
> Documentation/ABI/testing/ima_policy | 10 ++--
> security/integrity/digsig.c | 8 +--
> security/integrity/digsig_asymmetric.c | 58 +++++++++++++++++++++
> security/integrity/evm/evm_main.c | 3 +-
> security/integrity/ima/ima.h | 1 +
> security/integrity/ima/ima_appraise.c | 72 ++++++++------------------
> security/integrity/ima/ima_policy.c | 22 ++++----
> security/integrity/integrity.h | 14 ++++-
> 8 files changed, 115 insertions(+), 73 deletions(-)
>
> --
> 2.53.0
>
Series:
Tested-by: Stefan Berger <stefanb@linux.ibm.com>
^ permalink raw reply
* [PATCH] integrity: Allow sigv3 verification on EVM_XATTR_PORTABLE_DIGSIG
From: Stefan Berger @ 2026-03-25 0:10 UTC (permalink / raw)
To: linux-integrity
Cc: linux-kernel, ebiggers, zohar, roberto.sassu, Stefan Berger
Allow sigv3 verification on EVM_XATTR_PORTABLE_DIGSIG on RSA, ECDSA,
ECRDSA, and SM2 signatures.
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
security/integrity/digsig_asymmetric.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
index 6b21b9bf829e..6e68ec3becbd 100644
--- a/security/integrity/digsig_asymmetric.c
+++ b/security/integrity/digsig_asymmetric.c
@@ -154,7 +154,8 @@ static int calc_file_id_hash(enum evm_ima_xattr_type type,
size_t file_id_size;
int rc;
- if (type != IMA_VERITY_DIGSIG && type != EVM_IMA_XATTR_DIGSIG)
+ if (type != IMA_VERITY_DIGSIG && type != EVM_IMA_XATTR_DIGSIG &&
+ type != EVM_XATTR_PORTABLE_DIGSIG)
return -EINVAL;
tfm = crypto_alloc_shash(hash_algo_name[algo], 0, 0);
base-commit: 3fa0dea50084da8279fb4b1175b3a9b4fc16f052
--
2.53.0
^ permalink raw reply related
* [ima-evm-utils: PATCH 1/8] Implement imaevm_create_sigv3 for creating V3 signatures
From: Stefan Berger @ 2026-03-24 22:03 UTC (permalink / raw)
To: linux-integrity; +Cc: zohar, roberto.sassu, Stefan Berger
In-Reply-To: <20260324220314.743709-1-stefanb@linux.ibm.com>
Implement imaevm_create_sigv3 that creates v3 signatures. This function
will now also allocate a buffer if the caller did not provide one.
Further, it will write the full signature into the signature buffer,
including the leading xattr type byte.
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
src/imaevm.h | 7 +++++
src/libimaevm.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 72 insertions(+), 3 deletions(-)
diff --git a/src/imaevm.h b/src/imaevm.h
index 3b720d5..5a8441b 100644
--- a/src/imaevm.h
+++ b/src/imaevm.h
@@ -282,4 +282,11 @@ int imaevm_hash_algo_from_sig(unsigned char *sig);
const char *imaevm_hash_algo_by_id(int algo);
int calc_hash_sigv3(enum evm_ima_xattr_type type, const char *algo, const unsigned char *in_hash, unsigned char *out_hash);
+int imaevm_create_sigv3(const char *hash_algo, const unsigned char *hash, int size,
+ const char *keyfile, const char *keypass,
+ unsigned char **sig, size_t siglen, long sigflags,
+ enum evm_ima_xattr_type xattr_type,
+ const struct imaevm_ossl_access *access_info,
+ uint32_t keyid);
+
#endif
diff --git a/src/libimaevm.c b/src/libimaevm.c
index d8d5dbc..7c78432 100644
--- a/src/libimaevm.c
+++ b/src/libimaevm.c
@@ -605,7 +605,7 @@ struct ima_file_id {
int calc_hash_sigv3(enum evm_ima_xattr_type type, const char *algo,
const unsigned char *in_hash, unsigned char *out_hash)
{
- struct ima_file_id file_id = { .hash_type = IMA_VERITY_DIGSIG };
+ struct ima_file_id file_id = { .hash_type = type };
uint8_t *data = (uint8_t *) &file_id;
const EVP_MD *md;
@@ -622,8 +622,9 @@ int calc_hash_sigv3(enum evm_ima_xattr_type type, const char *algo,
int hash_size;
unsigned int unused;
- if (type != IMA_VERITY_DIGSIG) {
- log_err("Only fsverity supports signature format v3 (sigv3)\n");
+ if (type != IMA_VERITY_DIGSIG &&
+ type != EVM_IMA_XATTR_DIGSIG) {
+ log_err("Only fsverity and IMA/EVM support signature format v3 (sigv3)\n");
return -EINVAL;
}
@@ -1449,6 +1450,67 @@ int imaevm_signhash(const char *hashalgo, const unsigned char *hash, int size,
access_info, keyid);
}
+/*
+ * Create a v3 signature given a file hash
+ *
+ * @hash_algo: The hash algorithm to use for hashing
+ * @hash: The file hash
+ * @size: The size of the file hash
+ * @sig: A pointer to signature buffer pointer; if pointing to NULL, then
+ * this function will allocate a buffer large enough for the signature
+ * @siglen: Size of the given signature buffer; if it is too small then
+ * an error will be returned
+ * @sigflag: Flags related to the signature
+ * @xattr_type: Type of xattr that will be written; needed for creating
+ * ima_file_id structure
+ * @access_info: Needed in case an engine or provider is used
+ * @keyid: The key id to use
+ *
+ * Note: This function behaves slightly different than older signature creation
+ * functions because it already writes the xattr type to offset 0 in the
+ * signature buffer.
+ */
+int imaevm_create_sigv3(const char *hash_algo, const unsigned char *hash, int size,
+ const char *keyfile, const char *keypass,
+ unsigned char **sig, size_t siglen, long sigflags,
+ enum evm_ima_xattr_type xattr_type,
+ const struct imaevm_ossl_access *access_info,
+ uint32_t keyid)
+{
+ unsigned char sigv3_hash[MAX_DIGEST_SIZE];
+ /* buffer capable of holding (more than) RSA-4096 signature; */
+ unsigned char sigbuf[1024];
+ int len, slen, err;
+
+ len = calc_hash_sigv3(xattr_type, hash_algo, hash, sigv3_hash);
+ if (len < 0 || len == 1) {
+ log_err("Failure to calculate v3 file hash\n");
+ return len;
+ }
+ assert(len <= sizeof(sigv3_hash));
+
+ err = imaevm_signhash(hash_algo, sigv3_hash, len, keyfile, keypass,
+ &sigbuf[1], sigflags, access_info, keyid);
+ /* err holds error or signature length */
+ if (err < 0)
+ return err;
+ slen = 1 + err; /* will prepend xattr type */
+
+ if (!*sig) {
+ *sig = malloc(slen);
+ if (!*sig)
+ return -1;
+ } else if (siglen < slen) {
+ /* provided buffer is too small */
+ return -1;
+ }
+
+ sigbuf[0] = xattr_type;
+ sigbuf[1] = DIGSIG_VERSION_3;
+ memcpy(*sig, sigbuf, slen);
+
+ return slen;
+}
int sign_hash(const char *hashalgo, const unsigned char *hash, int size,
const char *keyfile, const char *keypass, unsigned char *sig)
--
2.53.0
^ permalink raw reply related
* [ima-evm-utils: PATCH 7/8] Allow verification of EVM_XATTR_PORTABLE_DIGSIG with sigv3
From: Stefan Berger @ 2026-03-24 22:03 UTC (permalink / raw)
To: linux-integrity; +Cc: zohar, roberto.sassu, Stefan Berger
In-Reply-To: <20260324220314.743709-1-stefanb@linux.ibm.com>
To enable sigv3 signature verification for EVM portable signatures, allow
signature verification on EVM_XATTR_PORTABLE_DIGSIG type of xattrs
with sigv3.
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
src/evmctl.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/evmctl.c b/src/evmctl.c
index 0a38aa7..de67178 100644
--- a/src/evmctl.c
+++ b/src/evmctl.c
@@ -995,7 +995,7 @@ static int verify_evm(struct public_key_entry *public_keys, const char *file)
}
if (sig[0] == EVM_XATTR_PORTABLE_DIGSIG) {
- if (sig[1] != DIGSIG_VERSION_2) {
+ if (sig[1] != DIGSIG_VERSION_2 && sig[1] != DIGSIG_VERSION_3) {
log_err("Portable sig: invalid type\n");
return -1;
}
--
2.53.0
^ permalink raw reply related
* [ima-evm-utils: PATCH 8/8] Allow hashing for sigv3 on EVM_XATTR_PORTABLE_DIGSIG
From: Stefan Berger @ 2026-03-24 22:03 UTC (permalink / raw)
To: linux-integrity; +Cc: zohar, roberto.sassu, Stefan Berger
In-Reply-To: <20260324220314.743709-1-stefanb@linux.ibm.com>
To enable sigv3 for EVM portable signatures, enable hashing for sigv3 for
EVM_XATTR_PORTABLE_DIGSIG.
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
src/libimaevm.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/libimaevm.c b/src/libimaevm.c
index 7c78432..49bfb62 100644
--- a/src/libimaevm.c
+++ b/src/libimaevm.c
@@ -623,7 +623,8 @@ int calc_hash_sigv3(enum evm_ima_xattr_type type, const char *algo,
unsigned int unused;
if (type != IMA_VERITY_DIGSIG &&
- type != EVM_IMA_XATTR_DIGSIG) {
+ type != EVM_IMA_XATTR_DIGSIG &&
+ type != EVM_XATTR_PORTABLE_DIGSIG) {
log_err("Only fsverity and IMA/EVM support signature format v3 (sigv3)\n");
return -EINVAL;
}
--
2.53.0
^ permalink raw reply related
* [ima-evm-utils: PATCH 4/8] Support v3 IMA and EVM file signatures with --v3 option.
From: Stefan Berger @ 2026-03-24 22:03 UTC (permalink / raw)
To: linux-integrity; +Cc: zohar, roberto.sassu, Stefan Berger
In-Reply-To: <20260324220314.743709-1-stefanb@linux.ibm.com>
Enable both IMA and EVM file signatures with a new --v3 option that sets
the previously introduced global variable that states which signature
version to use.
Similarly, introduce a --v2 option for users to (already) choose old V2
type of signatures.
Update the README with the dump of the evmctl help screen and mention
v3 signature format that is expected for Linux 7.1.
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
README | 6 ++++--
src/evmctl.c | 10 ++++++++++
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/README b/README
index 815b555..34dfddf 100644
--- a/README
+++ b/README
@@ -84,6 +84,8 @@ OPTIONS
--provider p preload OpenSSL provider (such as: pkcs11)
--ignore-violations ignore ToMToU measurement violations
--hmackey path to symmetric key (default: /etc/keys/evm-key-plain)
+ --v2 create V2 signatures; this is the default
+ --v3 create V3 signatures; this requires Linux 7.1 or later
-v increase verbosity level
-h, --help display this help and exit
@@ -139,7 +141,7 @@ evmctl '--smack' options enables that.
Key and signature formats
-------------------------
-Linux integrity subsystem supports two type of signature and respectively two
+Linux integrity subsystem supports three types of signature and respectively two
key formats.
First key format (v1) is pure RSA key encoded in PEM a format and uses own signature
@@ -149,7 +151,7 @@ for signing and importing the key.
Second key format uses X509 DER encoded public key certificates and uses asymmetric key support
in the kernel (since kernel 3.9). CONFIG_INTEGRITY_ASYMMETRIC_KEYS must be enabled (default).
-For v2 signatures x509 certificate (containing the public key) could be appended to the
+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).
diff --git a/src/evmctl.c b/src/evmctl.c
index aab5af9..2843ff8 100644
--- a/src/evmctl.c
+++ b/src/evmctl.c
@@ -3017,6 +3017,8 @@ static void usage(void)
#ifdef DEBUG
" --hmackey path to symmetric key (default: /etc/keys/evm-key-plain)\n"
#endif
+ " --v2 create V2 signatures; this is the default\n"
+ " --v3 create V3 signatures; this requires Linux 7.1 or later\n"
" -v increase verbosity level\n"
" -h, --help display this help and exit\n"
"\n"
@@ -3092,6 +3094,8 @@ static struct option opts[] = {
#if CONFIG_IMA_EVM_PROVIDER
{"provider", 1, 0, 149},
#endif
+ {"v2", 0, 0, 150},
+ {"v3", 0, 0, 151},
{}
};
@@ -3370,6 +3374,12 @@ int main(int argc, char *argv[])
access_info.type = IMAEVM_OSSL_ACCESS_TYPE_PROVIDER;
break;
#endif
+ case 150: /* --v2 */
+ g_signature_version = SIGNATURE_V2;
+ break;
+ case 151: /* --v3 */
+ g_signature_version = SIGNATURE_V3;
+ break;
case '?':
exit(1);
break;
--
2.53.0
^ permalink raw reply related
* [ima-evm-utils: PATCH 3/8] Implement support for EVM signatures V3 signing scheme
From: Stefan Berger @ 2026-03-24 22:03 UTC (permalink / raw)
To: linux-integrity; +Cc: zohar, roberto.sassu, Stefan Berger
In-Reply-To: <20260324220314.743709-1-stefanb@linux.ibm.com>
Add support for creating EVM signatures with the V3 signing scheme.
Implement the SIGNATURE_v3 case where necessary for EVM.
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
src/evmctl.c | 45 +++++++++++++++++++++++++++++++--------------
1 file changed, 31 insertions(+), 14 deletions(-)
diff --git a/src/evmctl.c b/src/evmctl.c
index 8b44ee0..aab5af9 100644
--- a/src/evmctl.c
+++ b/src/evmctl.c
@@ -564,8 +564,10 @@ out:
static int sign_evm(const char *file, char *hash_algo, const char *key)
{
- unsigned char hash[MAX_DIGEST_SIZE];
unsigned char sig[MAX_SIGNATURE_SIZE];
+ unsigned char hash[MAX_DIGEST_SIZE];
+ enum evm_ima_xattr_type xattr_type;
+ unsigned char *psig;
size_t len;
int err;
@@ -575,22 +577,37 @@ static int sign_evm(const char *file, char *hash_algo, const char *key)
len = (size_t)err;
assert(len <= sizeof(hash));
- err = imaevm_signhash(hash_algo, hash, len, key, g_keypass,
- sig + 1, sigflags, &access_info, imaevm_keyid);
- if (err <= 1)
- return err;
- len = (size_t)err;
- assert(len <= sizeof(sig));
-
- /* add header */
- len++;
if (evm_portable)
- sig[0] = EVM_XATTR_PORTABLE_DIGSIG;
+ xattr_type = EVM_XATTR_PORTABLE_DIGSIG;
else
- sig[0] = EVM_IMA_XATTR_DIGSIG;
+ xattr_type = EVM_IMA_XATTR_DIGSIG;
- if (evm_immutable)
- sig[1] = 3; /* immutable signature version */
+ switch (g_signature_version) {
+ case SIGNATURE_V3:
+ psig = sig;
+ err = imaevm_create_sigv3(hash_algo, hash, len, key, g_keypass,
+ &psig, sizeof(sig), sigflags,
+ xattr_type, &access_info,
+ imaevm_keyid);
+ if (err <= 1)
+ return err;
+ len = (size_t)err;
+ assert(len <= sizeof(sig));
+ break;
+ case SIGNATURE_V2:
+ err = imaevm_signhash(hash_algo, hash, len, key, g_keypass,
+ sig + 1, sigflags, &access_info, imaevm_keyid);
+ if (err <= 1)
+ return err;
+ len = (size_t)err;
+ assert(len <= sizeof(sig));
+ /* add header */
+ len++;
+ sig[0] = xattr_type;
+ if (evm_immutable)
+ sig[1] = 3; /* immutable signature version */
+ break;
+ }
if (sigdump || imaevm_params.verbose >= LOG_INFO)
imaevm_hexdump(sig, len);
--
2.53.0
^ permalink raw reply related
* [ima-evm-utils: PATCH 5/8] Use imaevm_create_sigv3 for fsverity signature creation
From: Stefan Berger @ 2026-03-24 22:03 UTC (permalink / raw)
To: linux-integrity; +Cc: zohar, roberto.sassu, Stefan Berger
In-Reply-To: <20260324220314.743709-1-stefanb@linux.ibm.com>
Convert the code that built the fsverity signature with V3 signing scheme
to use the new imaevm_create_sigv3 function.
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
src/evmctl.c | 24 +++++++++---------------
1 file changed, 9 insertions(+), 15 deletions(-)
diff --git a/src/evmctl.c b/src/evmctl.c
index 2843ff8..0a38aa7 100644
--- a/src/evmctl.c
+++ b/src/evmctl.c
@@ -828,7 +828,6 @@ static int cmd_sign_ima(struct command *cmd)
*/
static int cmd_sign_hash(struct command *cmd)
{
- unsigned char sigv3_hash[MAX_DIGEST_SIZE];
unsigned char sig[MAX_SIGNATURE_SIZE];
unsigned char hash[MAX_DIGEST_SIZE];
size_t algolen = 0;
@@ -840,7 +839,6 @@ static int cmd_sign_hash(struct command *cmd)
const char *key;
char algo[7]; /* Current maximum fsverity hash algo name length */
ssize_t len;
- int ret;
key = imaevm_params.keyfile ? : "/etc/keys/privkey_evm.pem";
@@ -888,19 +886,15 @@ static int cmd_sign_hash(struct command *cmd)
assert(hashlen / 2 <= sizeof(hash));
hex2bin(hash, hashp, hashlen / 2);
- ret = calc_hash_sigv3(IMA_VERITY_DIGSIG, algo, hash,
- sigv3_hash);
- if (ret < 0 || ret == 1) {
- log_info("Failure to calculate fs-verity hash\n");
- continue;
- }
-
- siglen = imaevm_signhash(algo, sigv3_hash, hashlen / 2,
- key, g_keypass, sig + 1, sigflags,
- &access_info, imaevm_keyid);
-
- sig[0] = IMA_VERITY_DIGSIG;
- sig[1] = DIGSIG_VERSION_3; /* sigv3 */
+ psig = sig;
+ siglen = imaevm_create_sigv3(algo, hash,
+ hashlen / 2, key, g_keypass,
+ &psig, sizeof(sig), sigflags,
+ IMA_VERITY_DIGSIG,
+ &access_info, imaevm_keyid);
+ if (siglen <= 1)
+ return siglen;
+ assert(siglen <= (int)sizeof(sig));
} else {
/* Parse the shaXsum output */
token = strpbrk(line, " \t");
--
2.53.0
^ permalink raw reply related
* [ima-evm-utils: PATCH 0/8] Add sigv3 support for IMA and EVM and all key types
From: Stefan Berger @ 2026-03-24 22:03 UTC (permalink / raw)
To: linux-integrity; +Cc: zohar, roberto.sassu, Stefan Berger
This series of patches adds sigv3 support for IMA and EVM for all currently
supported key types (RSA, ECDSA, ECRDSA, SM2). evmctl gets a --v3 option
for creating the sigv3 signatures and also a --v2 option for creating the
old sigv2 signatures. --v2 is still the default.
Some test cases are duplicated to test with --v3.
Regards,
Stefan
Stefan Berger (8):
Implement imaevm_create_sigv3 for creating V3 signatures
Implement support for IMA signatures V3 signing scheme
Implement support for EVM signatures V3 signing scheme
Support v3 IMA and EVM file signatures with --v3 option.
Use imaevm_create_sigv3 for fsverity signature creation
tests: Add new --v3 option to sign_verify tests
Allow verification of EVM_XATTR_PORTABLE_DIGSIG with sigv3
Allow hashing for sigv3 on EVM_XATTR_PORTABLE_DIGSIG
README | 6 +-
src/evmctl.c | 170 ++++++++++++++++++++++++++++-------------
src/imaevm.h | 7 ++
src/libimaevm.c | 69 ++++++++++++++++-
tests/sign_verify.test | 31 ++++++--
5 files changed, 221 insertions(+), 62 deletions(-)
--
2.53.0
^ permalink raw reply
* [ima-evm-utils: PATCH 6/8] tests: Add new --v3 option to sign_verify tests
From: Stefan Berger @ 2026-03-24 22:03 UTC (permalink / raw)
To: linux-integrity; +Cc: zohar, roberto.sassu, Stefan Berger
In-Reply-To: <20260324220314.743709-1-stefanb@linux.ibm.com>
Add the new --v3 option to the sign_verify test cases.
For --v3, adjust openssl signature verification to build an ima_file_id
structure in a file that is then used for signature verification rather
than the plain file (as before).
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
tests/sign_verify.test | 31 +++++++++++++++++++++++++------
1 file changed, 25 insertions(+), 6 deletions(-)
diff --git a/tests/sign_verify.test b/tests/sign_verify.test
index c94de24..9319123 100755
--- a/tests/sign_verify.test
+++ b/tests/sign_verify.test
@@ -128,7 +128,7 @@ check_sign() {
# OPTS (additional options for evmctl),
# FILE (working file to sign).
local "$@"
- local key verifykey
+ local key verifykey sigver
local FILE=${FILE:-$ALG.txt}
# Normalize key filename if it's not a pkcs11 URI
@@ -213,18 +213,30 @@ check_sign() {
verifykey=${key}
fi
- cmd="openssl dgst $OPENSSL_ENGINE $OPENSSL_KEYFORM -$ALG -verify ${verifykey} \
- -signature $FILE.sig2 $FILE"
+ 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"
+ # 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"
+ sigver=3
+ else
+ cmd="openssl dgst $OPENSSL_ENGINE $OPENSSL_KEYFORM -$ALG -verify ${verifykey} \
+ -signature $FILE.sig2 $FILE"
+ sigver=2
+ fi
echo - "$cmd"
if ! $cmd; then
color_red_on_failure
- echo "Signature v2 verification with openssl is failed."
+ echo "Signature v${sigver} verification with openssl is failed."
color_restore
- rm "$FILE.sig2"
+ rm "$FILE.sig2" "$FILE.tmp"
return "$FAIL"
fi
- rm "$FILE.sig2"
+ rm "$FILE.sig2" "$FILE.tmp"
return "$OK"
}
@@ -390,6 +402,9 @@ sign_verify rsa1024 sha384 0x030205:K:0080
sign_verify rsa1024 sha512 0x030206:K:0080
sign_verify rsa1024 rmd160 0x030203:K:0080
+sign_verify rsa1024 sha384 0x030305:K:0080 --v3
+sign_verify rsa1024 sha512 0x030306:K:0080 --v3
+
# Test v2 signatures with ECDSA
# Signature length is typically 0x34-0x38 bytes long, very rarely 0x33
sign_verify prime192v1 sha1 0x030202:K:003[345678]
@@ -405,6 +420,10 @@ sign_verify prime256v1 sha256 0x030204:K:004[345678]
sign_verify prime256v1 sha384 0x030205:K:004[345678]
sign_verify prime256v1 sha512 0x030206:K:004[345678]
+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
+
# 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 2/8] Implement support for IMA signatures V3 signing scheme
From: Stefan Berger @ 2026-03-24 22:03 UTC (permalink / raw)
To: linux-integrity; +Cc: zohar, roberto.sassu, Stefan Berger
In-Reply-To: <20260324220314.743709-1-stefanb@linux.ibm.com>
Add support for creating IMA signatures with the V3 signing scheme.
Introduce a global variable that states which signing scheme to
use and for now set it to SIGNATURE_V2. Implement the SIGNATURE_V3
case where necessary for IMA.
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
src/evmctl.c | 87 ++++++++++++++++++++++++++++++++++++++++------------
1 file changed, 67 insertions(+), 20 deletions(-)
diff --git a/src/evmctl.c b/src/evmctl.c
index 7c940fa..8b44ee0 100644
--- a/src/evmctl.c
+++ b/src/evmctl.c
@@ -123,6 +123,13 @@ static bool hwtpm;
static char *g_hash_algo = DEFAULT_HASH_ALGO;
static char *g_keypass;
+enum signature_version {
+ SIGNATURE_V2 = 2,
+ SIGNATURE_V3,
+};
+
+static enum signature_version g_signature_version = SIGNATURE_V2;
+
#define HMAC_FLAG_NO_UUID 0x0001
#define HMAC_FLAG_CAPS_SET 0x0002
@@ -652,6 +659,7 @@ static int sign_ima(const char *file, char *hash_algo, const char *key)
{
unsigned char hash[MAX_DIGEST_SIZE];
unsigned char sig[MAX_SIGNATURE_SIZE];
+ unsigned char *psig;
size_t len;
int err;
@@ -661,16 +669,35 @@ static int sign_ima(const char *file, char *hash_algo, const char *key)
len = (size_t)err;
assert(len <= sizeof(hash));
- err = imaevm_signhash(hash_algo, hash, len, key, g_keypass,
- sig + 1, sigflags, &access_info, imaevm_keyid);
- if (err <= 1)
- return err;
- len = (size_t)err;
- assert(len < sizeof(sig));
-
- /* add header */
- len++;
- sig[0] = EVM_IMA_XATTR_DIGSIG;
+ switch (g_signature_version) {
+ case SIGNATURE_V3:
+ psig = sig;
+ err = imaevm_create_sigv3(hash_algo, hash, len, key, g_keypass,
+ &psig, sizeof(sig), sigflags,
+ EVM_IMA_XATTR_DIGSIG, &access_info,
+ imaevm_keyid);
+ if (err <= 1)
+ return err;
+ len = (size_t)err;
+ assert(len <= sizeof(sig));
+ break;
+ case SIGNATURE_V2:
+ err = imaevm_signhash(hash_algo, hash, len, key, g_keypass,
+ sig + 1, sigflags, &access_info,
+ imaevm_keyid);
+ if (err <= 1)
+ return err;
+ len = (size_t)err;
+ assert(len < sizeof(sig));
+ /* add header */
+ len++;
+ sig[0] = EVM_IMA_XATTR_DIGSIG;
+ break;
+ default:
+ log_err("Internal error: Unsupported signature version: %d\n",
+ g_signature_version);
+ return -1;
+ }
if (sigdump || imaevm_params.verbose >= LOG_INFO)
imaevm_hexdump(sig, len);
@@ -791,6 +818,7 @@ static int cmd_sign_hash(struct command *cmd)
size_t hashlen = 0;
int siglen;
char *line = NULL, *token, *hashp;
+ unsigned char *psig;
size_t line_len = 0;
const char *key;
char algo[7]; /* Current maximum fsverity hash algo name length */
@@ -863,20 +891,39 @@ static int cmd_sign_hash(struct command *cmd)
assert(hashlen / 2 <= sizeof(hash));
hex2bin(hash, line, hashlen / 2);
- siglen = imaevm_signhash(g_hash_algo, hash,
- hashlen / 2, key, g_keypass,
- sig + 1, sigflags,
- &access_info, imaevm_keyid);
- sig[0] = EVM_IMA_XATTR_DIGSIG;
+ switch (g_signature_version) {
+ case SIGNATURE_V3:
+ psig = sig;
+ siglen = imaevm_create_sigv3(g_hash_algo, hash,
+ hashlen / 2, key, g_keypass,
+ &psig, sizeof(sig), sigflags,
+ EVM_IMA_XATTR_DIGSIG,
+ &access_info, imaevm_keyid);
+ if (siglen <= 1)
+ return siglen;
+ assert(siglen <= (int)sizeof(sig));
+ break;
+ case SIGNATURE_V2:
+ siglen = imaevm_signhash(g_hash_algo, hash,
+ hashlen / 2, key, g_keypass,
+ sig + 1, sigflags,
+ &access_info, imaevm_keyid);
+ if (siglen <= 1)
+ return siglen;
+ assert(siglen < (int)sizeof(sig));
+ siglen++;
+ sig[0] = EVM_IMA_XATTR_DIGSIG;
+ break;
+ default:
+ log_err("Internal error: Unsupported signature version: %d\n",
+ g_signature_version);
+ return -1;
+ }
}
- if (siglen <= 1)
- return siglen;
- assert(siglen < (int)sizeof(sig));
-
fwrite(line, len, 1, stdout);
fprintf(stdout, " ");
- bin2hex(sig, siglen + 1, stdout);
+ bin2hex(sig, siglen, stdout);
fprintf(stdout, "\n");
}
--
2.53.0
^ permalink raw reply related
* [ima-evm-utils: PATCH] shellcheck: Fix shellcheck v0.11.0 issues detected in fsverity.test
From: Stefan Berger @ 2026-03-24 21:26 UTC (permalink / raw)
To: linux-integrity; +Cc: zohar, roberto.sassu, Stefan Berger
Fix the following issue detected by shellcheck:
if [ "x$(id -u)" != "x0" ]; then
^---------^ SC2268 (style): Avoid x-prefix in comparisons as it no \
longer serves a purpose.
Also fix several of the following types of issues by disabling the
shellcheck error. Shellcheck does not recognize the invocation of functions
when they are indirectly invoked when they are passed as parameter to
another function.
__skip() { return "$SKIP"; }
^-- SC2329 (info): This function is never invoked. Check usage (or \
ignored if invoked indirectly).
Also see: https://www.shellcheck.net/wiki/SC2329
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
tests/kernel/fsverity.test | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/tests/kernel/fsverity.test b/tests/kernel/fsverity.test
index 734d490..9c0f381 100755
--- a/tests/kernel/fsverity.test
+++ b/tests/kernel/fsverity.test
@@ -52,6 +52,7 @@ _require dd mkfs blkid e2fsck tune2fs evmctl setfattr
trap '_report_exit_and_cleanup _cleanup_env cleanup' SIGINT SIGTERM EXIT
+# shellcheck disable=SC2329
cleanup() {
if [ -e "$TST_MNT" ]; then
if [ "$LOOPBACK_MOUNTED" -eq 1 ]; then
@@ -220,6 +221,7 @@ load_policy_rule() {
return 0
}
+# shellcheck disable=SC2329
create_file() {
local test=$1
local type=$2
@@ -236,6 +238,7 @@ create_file() {
chmod a+x "$TST_FILE"
}
+# shellcheck disable=SC2329
measure-verity() {
local test=$1
local verity="${2:-disabled}"
@@ -297,6 +300,7 @@ measure-verity() {
return "$error"
}
+# shellcheck disable=SC2329
measure-ima() {
local test=$1
local digest_filename
@@ -375,7 +379,7 @@ if ! evmctl --help | grep -q veritysig ; then
exit "$SKIP"
fi
-if [ "x$(id -u)" != "x0" ]; then
+if [ "$(id -u)" != "0" ]; then
echo "${CYAN}SKIP: Must be root to execute this test${NORM}"
exit "$SKIP"
fi
@@ -403,6 +407,7 @@ create_loopback_file ext4
# Commit 989dc72511f7 ("ima: define a new template field named 'd-ngv2' and
# templates") introduced ima-ngv2 and ima-sigv2 in linux-5.19.
+# shellcheck disable=SC2329
__skip() { return "$SKIP"; }
# IMA policy rule using the ima-ngv2 template
--
2.53.0
^ permalink raw reply related
* [ima-evm-utils: PATCH 0/2] Fix memory leaks in libimaevm
From: Stefan Berger @ 2026-03-24 21:23 UTC (permalink / raw)
To: linux-integrity; +Cc: zohar, roberto.sassu, Stefan Berger
This series fixes two memory leaks in libimaevm.
Stefan
Stefan Berger (2):
Use EVP_PKEY_free to free a key of public_key_entry
Avoid memory leak if public_keys is NULL
src/libimaevm.c | 21 ++++++++++-----------
1 file changed, 10 insertions(+), 11 deletions(-)
--
2.53.0
^ permalink raw reply
* [ima-evm-utils: PATCH 2/2] Avoid memory leak if public_keys is NULL
From: Stefan Berger @ 2026-03-24 21:23 UTC (permalink / raw)
To: linux-integrity; +Cc: zohar, roberto.sassu, Stefan Berger
In-Reply-To: <20260324212342.739685-1-stefanb@linux.ibm.com>
Avoid a memory leak of a public_key_entry if the passed public_keys
pointer is NULL because in this case the entry is lost. For this particular
case to work we would need public keys to be passed in as
'struct public_key_entry **public_keys' so that '*public_keys = entry'
could be assign. However, this change would propagate all the way to the
API of the library and we don't want to change existing functions'
signature.
This change should not have any noticeable side-effect since the resolved
case did not work before but the newly allocated entry was lost.
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
src/libimaevm.c | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/src/libimaevm.c b/src/libimaevm.c
index 6512ee5..d8d5dbc 100644
--- a/src/libimaevm.c
+++ b/src/libimaevm.c
@@ -378,17 +378,16 @@ static EVP_PKEY *find_keyid(struct public_key_entry *public_keys,
tail = entry;
}
- /* add unknown keys to list */
- entry = calloc(1, sizeof(struct public_key_entry));
- if (!entry) {
- perror("calloc");
- return 0;
- }
- entry->keyid = keyid;
- if (tail)
+ /* add unknown keys to tail of list */
+ if (tail) {
+ entry = calloc(1, sizeof(struct public_key_entry));
+ if (!entry) {
+ perror("calloc");
+ return 0;
+ }
+ entry->keyid = keyid;
tail->next = entry;
- else
- public_keys = entry;
+ }
log_err("key %d: %x (unknown keyid)\n", i, __be32_to_cpup(&keyid));
return 0;
}
--
2.53.0
^ permalink raw reply related
* [ima-evm-utils: PATCH 1/2] Use EVP_PKEY_free to free a key of public_key_entry
From: Stefan Berger @ 2026-03-24 21:23 UTC (permalink / raw)
To: linux-integrity; +Cc: zohar, roberto.sassu, Stefan Berger
In-Reply-To: <20260324212342.739685-1-stefanb@linux.ibm.com>
Use EVP_PKEY_free to fix a memory leak that occurs when using free() on the
key field of public_key_entry that is a pointer to an EVP_PKEY.
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
src/libimaevm.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/libimaevm.c b/src/libimaevm.c
index 3b268d5..6512ee5 100644
--- a/src/libimaevm.c
+++ b/src/libimaevm.c
@@ -400,7 +400,7 @@ void imaevm_free_public_keys(struct public_key_entry *public_keys)
while (entry) {
next = entry->next;
if (entry->key)
- free(entry->key);
+ EVP_PKEY_free(entry->key);
free(entry);
entry = next;
}
--
2.53.0
^ permalink raw reply related
* [PATCH 2/3] ima: add regular file data hash signature version 3 support
From: Mimi Zohar @ 2026-03-24 20:39 UTC (permalink / raw)
To: linux-integrity; +Cc: Mimi Zohar, Eric Biggers, Stefan Berger
In-Reply-To: <20260324203929.2475782-1-zohar@linux.ibm.com>
Instead of directly verifying the signature of a file data hash,
signature v3 verifies the signature of the ima_file_id structure
containing the file data hash.
To disambiguate the signature usage, the ima_file_id structure also
includes the hash algorithm and the type of data (e.g. regular file
hash or fs-verity root hash).
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
---
security/integrity/digsig_asymmetric.c | 2 +-
security/integrity/ima/ima_appraise.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
index dc5313746609..6b21b9bf829e 100644
--- a/security/integrity/digsig_asymmetric.c
+++ b/security/integrity/digsig_asymmetric.c
@@ -154,7 +154,7 @@ static int calc_file_id_hash(enum evm_ima_xattr_type type,
size_t file_id_size;
int rc;
- if (type != IMA_VERITY_DIGSIG)
+ if (type != IMA_VERITY_DIGSIG && type != EVM_IMA_XATTR_DIGSIG)
return -EINVAL;
tfm = crypto_alloc_shash(hash_algo_name[algo], 0, 0);
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 5b42307ac254..8f182d808b09 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -297,7 +297,7 @@ static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint,
}
sig = (typeof(sig))xattr_value;
- if (sig->version >= 3) {
+ if (sig->version > 3) {
*cause = "invalid-signature-version";
*status = INTEGRITY_FAIL;
break;
--
2.53.0
^ permalink raw reply related
* [PATCH 3/3] ima: add support to require IMA sigv3 signatures
From: Mimi Zohar @ 2026-03-24 20:39 UTC (permalink / raw)
To: linux-integrity; +Cc: Mimi Zohar, Eric Biggers, Stefan Berger
In-Reply-To: <20260324203929.2475782-1-zohar@linux.ibm.com>
Defining a policy rule with the "appraise_type=imasig" option allows
either v2 or v3 signatures. Defining an IMA appraise rule with the
"appraise_type=sigv3" option requires a file sigv3 signature.
Define a new appraise type: IMA_SIGV3_REQUIRED
Example: appraise func=BPRM_CHECK appraise_type=sigv3
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
---
Documentation/ABI/testing/ima_policy | 10 ++++++----
security/integrity/ima/ima.h | 1 +
security/integrity/ima/ima_appraise.c | 7 +++++++
security/integrity/ima/ima_policy.c | 22 ++++++++++------------
4 files changed, 24 insertions(+), 16 deletions(-)
diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy
index d4b3696a9efb..19258471b7b2 100644
--- a/Documentation/ABI/testing/ima_policy
+++ b/Documentation/ABI/testing/ima_policy
@@ -53,10 +53,7 @@ Description:
where 'imasig' is the original or the signature
format v2.
where 'modsig' is an appended signature,
- where 'sigv3' is the signature format v3. (Currently
- limited to fsverity digest based signatures
- stored in security.ima xattr. Requires
- specifying "digest_type=verity" first.)
+ where 'sigv3' is the signature format v3.
appraise_flag:= [check_blacklist] (deprecated)
Setting the check_blacklist flag is no longer necessary.
@@ -186,6 +183,11 @@ Description:
appraise func=BPRM_CHECK digest_type=verity \
appraise_type=sigv3
+ Example of a regular IMA file hash 'appraise' rule requiring
+ signature version 3 format stored in security.ima xattr.
+
+ appraise func=BPRM_CHECK appraise_type=sigv3
+
All of these policy rules could, for example, be constrained
either based on a filesystem's UUID (fsuuid) or based on LSM
labels.
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 0eea02ff04df..69e9bf0b82c6 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -145,6 +145,7 @@ struct ima_kexec_hdr {
#define IMA_DIGSIG_REQUIRED 0x01000000
#define IMA_PERMIT_DIRECTIO 0x02000000
#define IMA_NEW_FILE 0x04000000
+#define IMA_SIGV3_REQUIRED 0x08000000
#define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000
#define IMA_MODSIG_ALLOWED 0x20000000
#define IMA_CHECK_BLACKLIST 0x40000000
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 8f182d808b09..de963b9f3634 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -302,6 +302,13 @@ static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint,
*status = INTEGRITY_FAIL;
break;
}
+
+ if ((iint->flags & IMA_SIGV3_REQUIRED) && sig->version != 3) {
+ *cause = "IMA-sigv3-required";
+ *status = INTEGRITY_FAIL;
+ break;
+ }
+
rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
(const char *)xattr_value,
xattr_len,
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index bf2d7ba4c14a..f7f940a76922 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -1298,7 +1298,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
IMA_GID | IMA_EGID |
IMA_FGROUP | IMA_DIGSIG_REQUIRED |
IMA_PERMIT_DIRECTIO | IMA_VALIDATE_ALGOS |
- IMA_CHECK_BLACKLIST | IMA_VERITY_REQUIRED))
+ IMA_CHECK_BLACKLIST | IMA_VERITY_REQUIRED |
+ IMA_SIGV3_REQUIRED))
return false;
break;
@@ -1833,9 +1834,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
break;
case Opt_digest_type:
ima_log_string(ab, "digest_type", args[0].from);
- if (entry->flags & IMA_DIGSIG_REQUIRED)
- result = -EINVAL;
- else if ((strcmp(args[0].from, "verity")) == 0)
+ if ((strcmp(args[0].from, "verity")) == 0)
entry->flags |= IMA_VERITY_REQUIRED;
else
result = -EINVAL;
@@ -1849,14 +1848,13 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
else
entry->flags |= IMA_DIGSIG_REQUIRED | IMA_CHECK_BLACKLIST;
} else if (strcmp(args[0].from, "sigv3") == 0) {
- /* Only fsverity supports sigv3 for now */
- if (entry->flags & IMA_VERITY_REQUIRED)
- entry->flags |= IMA_DIGSIG_REQUIRED | IMA_CHECK_BLACKLIST;
- else
- result = -EINVAL;
+ entry->flags |= IMA_SIGV3_REQUIRED |
+ IMA_DIGSIG_REQUIRED |
+ IMA_CHECK_BLACKLIST;
} else if (IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG) &&
strcmp(args[0].from, "imasig|modsig") == 0) {
- if (entry->flags & IMA_VERITY_REQUIRED)
+ if ((entry->flags & IMA_VERITY_REQUIRED) ||
+ (entry->flags & IMA_SIGV3_REQUIRED))
result = -EINVAL;
else
entry->flags |= IMA_DIGSIG_REQUIRED |
@@ -1941,7 +1939,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
/* d-ngv2 template field recommended for unsigned fs-verity digests */
if (!result && entry->action == MEASURE &&
- entry->flags & IMA_VERITY_REQUIRED) {
+ (entry->flags & IMA_VERITY_REQUIRED)) {
template_desc = entry->template ? entry->template :
ima_template_desc_current();
check_template_field(template_desc, "d-ngv2",
@@ -2309,7 +2307,7 @@ int ima_policy_show(struct seq_file *m, void *v)
if (entry->template)
seq_printf(m, "template=%s ", entry->template->name);
if (entry->flags & IMA_DIGSIG_REQUIRED) {
- if (entry->flags & IMA_VERITY_REQUIRED)
+ if (entry->flags & IMA_SIGV3_REQUIRED)
seq_puts(m, "appraise_type=sigv3 ");
else if (entry->flags & IMA_MODSIG_ALLOWED)
seq_puts(m, "appraise_type=imasig|modsig ");
--
2.53.0
^ permalink raw reply related
* [PATCH 0/3] ima: add regular file data hash support for sigv3
From: Mimi Zohar @ 2026-03-24 20:39 UTC (permalink / raw)
To: linux-integrity; +Cc: Mimi Zohar, Eric Biggers, Stefan Berger
IMA signature version 3 (sigv3) support was introduced to avoid file
signature ambiguity. Instead of directly signing a raw fs-verity hash,
IMA signs the hash of ima_file_id structure, containing the type of
signature, the hash algorithm, and the hash.
Pure ML-DSA calculates and signs the hash directly rather than a
pre-hashed digest. To avoid ML-DSA having to re-calculate the file data
hash, Eric Biggers suggested signing the smaller ima_file_id structure.
This patch set adds the sigv3 support for regular file data hashes. A
subsequent patch set will add the ML-DSA support.
Mimi Zohar (3):
ima: Define asymmetric_verify_v3() to verify IMA sigv3 signatures
ima: add regular file data hash signature version 3 support
ima: add support to require IMA sigv3 signatures
Documentation/ABI/testing/ima_policy | 10 ++--
security/integrity/digsig.c | 8 +--
security/integrity/digsig_asymmetric.c | 58 +++++++++++++++++++++
security/integrity/evm/evm_main.c | 3 +-
security/integrity/ima/ima.h | 1 +
security/integrity/ima/ima_appraise.c | 72 ++++++++------------------
security/integrity/ima/ima_policy.c | 22 ++++----
security/integrity/integrity.h | 14 ++++-
8 files changed, 115 insertions(+), 73 deletions(-)
--
2.53.0
^ permalink raw reply
* [PATCH 1/3] ima: Define asymmetric_verify_v3() to verify IMA sigv3 signatures
From: Mimi Zohar @ 2026-03-24 20:39 UTC (permalink / raw)
To: linux-integrity; +Cc: Mimi Zohar, Eric Biggers, Stefan Berger
In-Reply-To: <20260324203929.2475782-1-zohar@linux.ibm.com>
Define asymmetric_verify_v3() to calculate the hash of the struct
ima_file_id, before calling asymmetric_verify() to verify the
signature.
Move and update the existing calc_file_id_hash() function with a
simpler, self contained version. In addition to the existing hash
data and hash data length arguments, also pass the hash algorithm.
Suggested-by: Stefan Berger <stefanb@linux.ibm.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
---
security/integrity/digsig.c | 8 ++--
security/integrity/digsig_asymmetric.c | 58 ++++++++++++++++++++++++
security/integrity/evm/evm_main.c | 3 +-
security/integrity/ima/ima_appraise.c | 63 ++++++--------------------
security/integrity/integrity.h | 14 +++++-
5 files changed, 90 insertions(+), 56 deletions(-)
diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c
index 75c684cce370..1ed686154d7a 100644
--- a/security/integrity/digsig.c
+++ b/security/integrity/digsig.c
@@ -59,7 +59,7 @@ static struct key *integrity_keyring_from_id(const unsigned int id)
}
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
- const char *digest, int digestlen)
+ const char *digest, int digestlen, u8 algo)
{
struct key *keyring;
@@ -76,9 +76,11 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
return digsig_verify(keyring, sig + 1, siglen - 1, digest,
digestlen);
case 2: /* regular file data hash based signature */
- case 3: /* struct ima_file_id data based signature */
return asymmetric_verify(keyring, sig, siglen, digest,
- digestlen);
+ digestlen);
+ case 3: /* struct ima_file_id data based signature */
+ return asymmetric_verify_v3(keyring, sig, siglen, digest,
+ digestlen, algo);
}
return -EOPNOTSUPP;
diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
index 87be85f477d1..dc5313746609 100644
--- a/security/integrity/digsig_asymmetric.c
+++ b/security/integrity/digsig_asymmetric.c
@@ -131,3 +131,61 @@ int asymmetric_verify(struct key *keyring, const char *sig,
pr_debug("%s() = %d\n", __func__, ret);
return ret;
}
+
+/*
+ * calc_file_id_hash - calculate the hash of the ima_file_id struct data
+ * @type: xattr type [enum evm_ima_xattr_type]
+ * @algo: hash algorithm [enum hash_algo]
+ * @digest: pointer to the digest to be hashed
+ * @hash: (out) pointer to the hash
+ *
+ * IMA signature version 3 disambiguates the data that is signed by
+ * indirectly signing the hash of the ima_file_id structure data.
+ *
+ * Return 0 on success, error code otherwise.
+ */
+static int calc_file_id_hash(enum evm_ima_xattr_type type,
+ enum hash_algo algo, const u8 *digest,
+ struct ima_max_digest_data *hash)
+{
+ struct ima_file_id file_id = {.hash_type = type, .hash_algorithm = algo};
+ size_t digest_size = hash_digest_size[algo];
+ struct crypto_shash *tfm;
+ size_t file_id_size;
+ int rc;
+
+ if (type != IMA_VERITY_DIGSIG)
+ return -EINVAL;
+
+ tfm = crypto_alloc_shash(hash_algo_name[algo], 0, 0);
+ if (IS_ERR(tfm))
+ return PTR_ERR(tfm);
+
+ memcpy(file_id.hash, digest, digest_size);
+
+ /* Calculate the ima_file_id struct hash on the portion used. */
+ file_id_size = sizeof(file_id) - (HASH_MAX_DIGESTSIZE - digest_size);
+
+ hash->hdr.algo = algo;
+ hash->hdr.length = digest_size;
+ rc = crypto_shash_tfm_digest(tfm, (const u8 *)&file_id, file_id_size,
+ hash->digest);
+
+ crypto_free_shash(tfm);
+ return rc;
+}
+
+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;
+ int rc;
+
+ rc = calc_file_id_hash(hdr->type, algo, data, &hash);
+ if (rc)
+ return -EINVAL;
+
+ return asymmetric_verify(keyring, sig, siglen, hash.digest,
+ hash.hdr.length);
+}
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 1b0089b4b796..b15d9d933b84 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -266,7 +266,8 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
break;
rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM,
(const char *)xattr_data, xattr_len,
- digest.digest, digest.hdr.length);
+ digest.digest, digest.hdr.length,
+ digest.hdr.algo);
if (!rc) {
if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG) {
if (iint)
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 0d41d102626a..5b42307ac254 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -234,40 +234,6 @@ int ima_read_xattr(struct dentry *dentry,
return ret;
}
-/*
- * calc_file_id_hash - calculate the hash of the ima_file_id struct data
- * @type: xattr type [enum evm_ima_xattr_type]
- * @algo: hash algorithm [enum hash_algo]
- * @digest: pointer to the digest to be hashed
- * @hash: (out) pointer to the hash
- *
- * IMA signature version 3 disambiguates the data that is signed by
- * indirectly signing the hash of the ima_file_id structure data.
- *
- * Signing the ima_file_id struct is currently only supported for
- * IMA_VERITY_DIGSIG type xattrs.
- *
- * Return 0 on success, error code otherwise.
- */
-static int calc_file_id_hash(enum evm_ima_xattr_type type,
- enum hash_algo algo, const u8 *digest,
- struct ima_digest_data *hash)
-{
- struct ima_file_id file_id = {
- .hash_type = IMA_VERITY_DIGSIG, .hash_algorithm = algo};
- unsigned int unused = HASH_MAX_DIGESTSIZE - hash_digest_size[algo];
-
- if (type != IMA_VERITY_DIGSIG)
- return -EINVAL;
-
- memcpy(file_id.hash, digest, hash_digest_size[algo]);
-
- hash->algo = algo;
- hash->length = hash_digest_size[algo];
-
- return ima_calc_buffer_hash(&file_id, sizeof(file_id) - unused, hash);
-}
-
/*
* xattr_verify - verify xattr digest or signature
*
@@ -279,7 +245,6 @@ static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint,
struct evm_ima_xattr_data *xattr_value, int xattr_len,
enum integrity_status *status, const char **cause)
{
- struct ima_max_digest_data hash;
struct signature_v2_hdr *sig;
int rc = -EINVAL, hash_start = 0;
int mask;
@@ -341,7 +306,8 @@ static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint,
(const char *)xattr_value,
xattr_len,
iint->ima_hash->digest,
- iint->ima_hash->length);
+ iint->ima_hash->length,
+ iint->ima_hash->algo);
if (rc == -EOPNOTSUPP) {
*status = INTEGRITY_UNKNOWN;
break;
@@ -352,7 +318,9 @@ static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint,
(const char *)xattr_value,
xattr_len,
iint->ima_hash->digest,
- iint->ima_hash->length);
+ iint->ima_hash->length,
+ iint->ima_hash->algo);
+
if (rc) {
*cause = "invalid-signature";
*status = INTEGRITY_FAIL;
@@ -378,21 +346,16 @@ static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint,
break;
}
- rc = calc_file_id_hash(IMA_VERITY_DIGSIG, iint->ima_hash->algo,
- iint->ima_hash->digest,
- container_of(&hash.hdr,
- struct ima_digest_data, hdr));
- if (rc) {
- *cause = "sigv3-hashing-error";
- *status = INTEGRITY_FAIL;
- break;
- }
-
rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
(const char *)xattr_value,
- xattr_len, hash.digest,
- hash.hdr.length);
- if (rc) {
+ xattr_len,
+ iint->ima_hash->digest,
+ iint->ima_hash->length,
+ iint->ima_hash->algo);
+ if (rc == -EOPNOTSUPP) {
+ *status = INTEGRITY_UNKNOWN;
+ break;
+ } else if (rc) {
*cause = "invalid-verity-signature";
*status = INTEGRITY_FAIL;
} else {
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 4636629533af..0c581c03c5da 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -131,7 +131,7 @@ struct modsig;
#ifdef CONFIG_INTEGRITY_SIGNATURE
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
- const char *digest, int digestlen);
+ const char *digest, int digestlen, u8 algo);
int integrity_modsig_verify(unsigned int id, const struct modsig *modsig);
int __init integrity_init_keyring(const unsigned int id);
@@ -142,7 +142,8 @@ int __init integrity_load_cert(const unsigned int id, const char *source,
static inline int integrity_digsig_verify(const unsigned int id,
const char *sig, int siglen,
- const char *digest, int digestlen)
+ const char *digest, int digestlen,
+ u8 algo)
{
return -EOPNOTSUPP;
}
@@ -170,12 +171,21 @@ static inline int __init integrity_load_cert(const unsigned int id,
#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
int asymmetric_verify(struct key *keyring, const char *sig,
int siglen, const char *data, int datalen);
+int asymmetric_verify_v3(struct key *keyring, const char *sig,
+ int siglen, const char *data, int datalen, u8 algo);
#else
static inline int asymmetric_verify(struct key *keyring, const char *sig,
int siglen, const char *data, int datalen)
{
return -EOPNOTSUPP;
}
+
+static inline int asymmetric_verify_v3(struct key *keyring,
+ const char *sig, int siglen,
+ const char *data, int datalen, u8 algo)
+{
+ return -EOPNOTSUPP;
+}
#endif
#ifdef CONFIG_IMA_APPRAISE_MODSIG
--
2.53.0
^ permalink raw reply related
* [RFC v2 5/5] tpm: tis_i2c: Use local 4KB buffer to limit memory usage
From: Arun Menon @ 2026-03-24 18:12 UTC (permalink / raw)
To: linux-kernel
Cc: Jarkko Sakkinen, linux-integrity, Peter Huewe, Jason Gunthorpe,
Arun Menon
In-Reply-To: <20260324181244.17741-1-armenon@redhat.com>
The global increase of TPM_BUFSIZE to 8KB is necessary to support
Post-Quantum Cryptography (PQC) payloads. However, applying this increase
to the tpm_tis_i2c driver is unnecessary and wasteful due to physical
transport limitations as pointed out in [1]
This commit introduces a local buffer limit that is used in the i2c
driver.
[1] https://sashiko.dev/#/patchset/20260324071803.324774-1-armenon%40redhat.com?patch=8319
Signed-off-by: Arun Menon <armenon@redhat.com>
---
drivers/char/tpm/tpm_tis_i2c.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/char/tpm/tpm_tis_i2c.c b/drivers/char/tpm/tpm_tis_i2c.c
index 6cd07dd34507e..db19d459ea1e1 100644
--- a/drivers/char/tpm/tpm_tis_i2c.c
+++ b/drivers/char/tpm/tpm_tis_i2c.c
@@ -54,6 +54,8 @@
#define TPM_INTF_CAPABILITY_ZERO 0x0FFFF000
#define TPM_I2C_INTERFACE_CAPABILITY_ZERO 0x80000000
+#define TPM_I2C_BUFSIZE 4096
+
struct tpm_tis_i2c_phy {
struct tpm_tis_data priv;
struct i2c_client *i2c_client;
@@ -232,7 +234,7 @@ static int tpm_tis_i2c_write_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
int ret;
u16 wrote = 0;
- if (len > TPM_BUFSIZE - 1)
+ if (len > TPM_I2C_BUFSIZE - 1)
return -EIO;
phy->io_buf[0] = reg;
@@ -339,7 +341,7 @@ static int tpm_tis_i2c_probe(struct i2c_client *dev)
if (!phy)
return -ENOMEM;
- phy->io_buf = devm_kzalloc(&dev->dev, TPM_BUFSIZE, GFP_KERNEL);
+ phy->io_buf = devm_kzalloc(&dev->dev, TPM_I2C_BUFSIZE, GFP_KERNEL);
if (!phy->io_buf)
return -ENOMEM;
--
2.53.0
^ permalink raw reply related
* [RFC v2 4/5] tpm: Increase TPM_BUFSIZE to 8kB for chunking support
From: Arun Menon @ 2026-03-24 18:12 UTC (permalink / raw)
To: linux-kernel
Cc: Jarkko Sakkinen, linux-integrity, Peter Huewe, Jason Gunthorpe,
Arun Menon
In-Reply-To: <20260324181244.17741-1-armenon@redhat.com>
The size of the command is checked against TPM_BUFSIZE early on before
even sending it to the backend. We therefore need to increase the
TPM_BUFSIZE to allow support for larger commands.
For now, 8KB seems sufficient for ML-KEM and ML-DSA algorithms and it is
also order-1 safe.
Signed-off-by: Arun Menon <armenon@redhat.com>
---
drivers/char/tpm/tpm.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 87d68ddf270a7..26c3765fbd732 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -33,7 +33,7 @@
#endif
#define TPM_MINOR 224 /* officially assigned */
-#define TPM_BUFSIZE 4096
+#define TPM_BUFSIZE 8192
#define TPM_NUM_DEVICES 65536
#define TPM_RETRY 50
--
2.53.0
^ permalink raw reply related
* [RFC v2 3/5] tpm_crb: Implement command and response chunking logic
From: Arun Menon @ 2026-03-24 18:12 UTC (permalink / raw)
To: linux-kernel
Cc: Jarkko Sakkinen, linux-integrity, Peter Huewe, Jason Gunthorpe,
Arun Menon
In-Reply-To: <20260324181244.17741-1-armenon@redhat.com>
With the introduction of support for Post Quantum Cryptography
algorithms in TPM, the commands and responses will grow in size.
Some TPMs have a physical hardware memory window (MMIO) that is
smaller than the commands we need to send. Therefore this commit
implements the core logic of sending/receiving data in chunks.
Instead of sending the whole command at once, the driver now sends it in
small chunks. After each chunk, it signals the TPM using a nextChunk
signal, and waits for the TPM to consume the data. Once the final piece
is delivered, the driver signals the TPM to begin execution by toggling
the start invoke bit. We use the same logic in reverse to read large
responses from the TPM.
This allows the driver to handle large payloads even when the hardware
interface has limited memory. This kernel-side support corresponds to
the backend implementation in QEMU [1]. QEMU reassembles the chunks
before passing them to the TPM emulator.
[1] https://lore.kernel.org/qemu-devel/20260319135316.37412-1-armenon@redhat.com/
Signed-off-by: Arun Menon <armenon@redhat.com>
---
drivers/char/tpm/tpm_crb.c | 155 +++++++++++++++++++++++++++----------
1 file changed, 114 insertions(+), 41 deletions(-)
diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c
index 922bcf7a69ad5..a97fc5e9927e3 100644
--- a/drivers/char/tpm/tpm_crb.c
+++ b/drivers/char/tpm/tpm_crb.c
@@ -104,11 +104,13 @@ struct crb_priv {
u8 __iomem *cmd;
u8 __iomem *rsp;
u32 cmd_size;
+ u32 rsp_size;
u32 smc_func_id;
u32 __iomem *pluton_start_addr;
u32 __iomem *pluton_reply_addr;
u8 ffa_flags;
u8 ffa_attributes;
+ bool chunking_supported;
};
struct tpm2_crb_smc {
@@ -368,38 +370,6 @@ static u8 crb_status(struct tpm_chip *chip)
return sts;
}
-static int crb_recv(struct tpm_chip *chip, u8 *buf, size_t count)
-{
- struct crb_priv *priv = dev_get_drvdata(&chip->dev);
- unsigned int expected;
-
- /* A sanity check that the upper layer wants to get at least the header
- * as that is the minimum size for any TPM response.
- */
- if (count < TPM_HEADER_SIZE)
- return -EIO;
-
- /* If this bit is set, according to the spec, the TPM is in
- * unrecoverable condition.
- */
- if (ioread32(&priv->regs_t->ctrl_sts) & CRB_CTRL_STS_ERROR)
- return -EIO;
-
- /* Read the first 8 bytes in order to get the length of the response.
- * We read exactly a quad word in order to make sure that the remaining
- * reads will be aligned.
- */
- memcpy_fromio(buf, priv->rsp, 8);
-
- expected = be32_to_cpup((__be32 *)&buf[2]);
- if (expected > count || expected < TPM_HEADER_SIZE)
- return -EIO;
-
- memcpy_fromio(&buf[8], &priv->rsp[8], expected - 8);
-
- return expected;
-}
-
static int crb_do_acpi_start(struct tpm_chip *chip)
{
union acpi_object *obj;
@@ -474,6 +444,8 @@ static int crb_trigger_tpm(struct tpm_chip *chip, u32 start_cmd)
static int crb_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, size_t len)
{
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
+ size_t offset = 0;
+ size_t chunk_size;
int rc = 0;
/* Zero the cancel register so that the next command will not get
@@ -481,7 +453,7 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, size_t len)
*/
iowrite32(0, &priv->regs_t->ctrl_cancel);
- if (len > priv->cmd_size) {
+ if (len > priv->cmd_size && !priv->chunking_supported) {
dev_err(&chip->dev, "invalid command count value %zd %d\n",
len, priv->cmd_size);
return -E2BIG;
@@ -491,18 +463,108 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, size_t len)
if (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON)
__crb_cmd_ready(&chip->dev, priv, chip->locality);
- memcpy_toio(priv->cmd, buf, len);
+ while (offset < len) {
+ chunk_size = min_t(size_t, len - offset, priv->cmd_size);
- /* Make sure that cmd is populated before issuing start. */
- wmb();
-
- rc = crb_trigger_tpm(chip, CRB_START_INVOKE);
- if (rc)
- return rc;
+ if (chunk_size == 0)
+ break;
+ memcpy_toio(priv->cmd, buf + offset, chunk_size);
+ offset += chunk_size;
+
+ /* Make sure that cmd is populated before issuing start. */
+ wmb();
+ if (offset < len) {
+ rc = crb_trigger_tpm(chip, CRB_START_NEXT_CHUNK);
+ if (rc)
+ return rc;
+ if (!crb_wait_for_reg_32(&priv->regs_t->ctrl_start,
+ CRB_START_NEXT_CHUNK, 0, TPM2_TIMEOUT_C)) {
+ dev_err(&chip->dev,
+ "Timeout waiting for backend to consume chunk\n");
+ return -ETIME;
+ }
+ } else {
+ rc = crb_trigger_tpm(chip, CRB_START_INVOKE);
+ if (rc)
+ return rc;
+ }
+ }
return crb_try_pluton_doorbell(priv, false);
}
+static int crb_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+ struct crb_priv *priv = dev_get_drvdata(&chip->dev);
+ unsigned int expected;
+ size_t offset = 0;
+ size_t chunk_size;
+ size_t first_read;
+ int rc;
+
+ /* A sanity check that the upper layer wants to get at least the header
+ * as that is the minimum size for any TPM response.
+ */
+ if (count < TPM_HEADER_SIZE)
+ return -EIO;
+
+ /* If this bit is set, according to the spec, the TPM is in
+ * unrecoverable condition.
+ */
+ if (ioread32(&priv->regs_t->ctrl_sts) & CRB_CTRL_STS_ERROR)
+ return -EIO;
+
+ /* Read the first 8 bytes in order to get the length of the response.
+ * We read exactly a quad word in order to make sure that the remaining
+ * reads will be aligned.
+ */
+ memcpy_fromio(buf, priv->rsp, 8);
+
+ expected = be32_to_cpup((__be32 *)&buf[2]);
+ if (expected > count || expected < TPM_HEADER_SIZE)
+ return -EIO;
+
+ /*
+ * Set chunk_size by comparing the size of the buffer that the upper layer has
+ * allocated (count) to the hardware tpm limit (priv->rsp_size).
+ * This is to prevent buffer overflow while writing to buf.
+ */
+ chunk_size = min_t(size_t, count, priv->rsp_size);
+ if (chunk_size < 8)
+ return -EIO;
+
+ /*
+ * Compare the actual size of the response we found in the header to the chunk_size.
+ */
+ first_read = min_t(size_t, expected, chunk_size);
+
+ memcpy_fromio(&buf[8], &priv->rsp[8], first_read - 8);
+ offset = first_read;
+
+ while (offset < expected) {
+ if (!priv->chunking_supported) {
+ dev_err(&chip->dev, "Response larger than MMIO and chunking not supported\n");
+ return -EIO;
+ }
+
+ rc = crb_trigger_tpm(chip, CRB_START_NEXT_CHUNK);
+ if (rc)
+ return rc;
+
+ if (!crb_wait_for_reg_32(&priv->regs_t->ctrl_start,
+ CRB_START_NEXT_CHUNK, 0, TPM2_TIMEOUT_C)) {
+ dev_err(&chip->dev, "Timeout waiting for backend response\n");
+ return -ETIME;
+ }
+
+ chunk_size = min_t(size_t, expected - offset, priv->rsp_size);
+ memcpy_fromio(buf + offset, priv->rsp, chunk_size);
+ offset += chunk_size;
+ }
+
+ return expected;
+}
+
static void crb_cancel(struct tpm_chip *chip)
{
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
@@ -727,6 +789,15 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
goto out;
}
+ if (priv->regs_h) {
+ u32 intf_id = ioread32((u32 __iomem *)&priv->regs_h->intf_id);
+
+ if (intf_id & CRB_INTF_CAP_CRB_CHUNK) {
+ priv->chunking_supported = true;
+ dev_info(dev, "CRB Chunking is supported by backend\n");
+ }
+ }
+
memcpy_fromio(&__rsp_pa, &priv->regs_t->ctrl_rsp_pa, 8);
rsp_pa = le64_to_cpu(__rsp_pa);
rsp_size = ioread32(&priv->regs_t->ctrl_rsp_size);
@@ -764,8 +835,10 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
priv->rsp = priv->cmd;
out:
- if (!ret)
+ if (!ret) {
priv->cmd_size = cmd_size;
+ priv->rsp_size = rsp_size;
+ }
__crb_go_idle(dev, priv, 0);
--
2.53.0
^ permalink raw reply related
* [RFC v2 2/5] tpm_crb: Add new wrapper function to invoke start method
From: Arun Menon @ 2026-03-24 18:12 UTC (permalink / raw)
To: linux-kernel
Cc: Jarkko Sakkinen, linux-integrity, Peter Huewe, Jason Gunthorpe,
Arun Menon
In-Reply-To: <20260324181244.17741-1-armenon@redhat.com>
The current implementation handles different platform start methods
(ACPI, ARM SMC, and ARM FFA) directly within crb_send(), but it is
limited to triggering the CRB_START_INVOKE bit.
To support cmd/rsp chunking, the driver must be able to send other
control bits, like CRB_START_NEXT_CHUNK, using these same
platform-specific paths.
By moving this logic into a new helper function, crb_trigger_tpm(),
the driver can now send any required control bit across all supported
platforms. This prepares the driver for the upcoming chunking support.
No functional change is intended.
Signed-off-by: Arun Menon <armenon@redhat.com>
---
drivers/char/tpm/tpm_crb.c | 50 ++++++++++++++++++++------------------
1 file changed, 27 insertions(+), 23 deletions(-)
diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c
index 67c0061d4cab7..922bcf7a69ad5 100644
--- a/drivers/char/tpm/tpm_crb.c
+++ b/drivers/char/tpm/tpm_crb.c
@@ -445,6 +445,32 @@ static int tpm_crb_smc_start(struct device *dev, unsigned long func_id)
}
#endif
+static int crb_trigger_tpm(struct tpm_chip *chip, u32 start_cmd)
+{
+ struct crb_priv *priv = dev_get_drvdata(&chip->dev);
+ int rc = 0;
+ /* The reason for the extra quirk is that the PTT in 4th Gen Core CPUs
+ * report only ACPI start but in practice seems to require both
+ * CRB start, hence invoking CRB start method if hid == MSFT0101.
+ */
+ if (priv->sm == ACPI_TPM2_COMMAND_BUFFER ||
+ priv->sm == ACPI_TPM2_MEMORY_MAPPED ||
+ !strcmp(priv->hid, "MSFT0101"))
+ iowrite32(start_cmd, &priv->regs_t->ctrl_start);
+ if (priv->sm == ACPI_TPM2_START_METHOD ||
+ priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD)
+ rc = crb_do_acpi_start(chip);
+ if (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC) {
+ iowrite32(start_cmd, &priv->regs_t->ctrl_start);
+ rc = tpm_crb_smc_start(&chip->dev, priv->smc_func_id);
+ }
+ if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) {
+ iowrite32(start_cmd, &priv->regs_t->ctrl_start);
+ rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_COMMAND, chip->locality);
+ }
+ return rc;
+}
+
static int crb_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, size_t len)
{
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
@@ -470,29 +496,7 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, size_t len)
/* Make sure that cmd is populated before issuing start. */
wmb();
- /* The reason for the extra quirk is that the PTT in 4th Gen Core CPUs
- * report only ACPI start but in practice seems to require both
- * CRB start, hence invoking CRB start method if hid == MSFT0101.
- */
- if (priv->sm == ACPI_TPM2_COMMAND_BUFFER ||
- priv->sm == ACPI_TPM2_MEMORY_MAPPED ||
- !strcmp(priv->hid, "MSFT0101"))
- iowrite32(CRB_START_INVOKE, &priv->regs_t->ctrl_start);
-
- if (priv->sm == ACPI_TPM2_START_METHOD ||
- priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD)
- rc = crb_do_acpi_start(chip);
-
- if (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC) {
- iowrite32(CRB_START_INVOKE, &priv->regs_t->ctrl_start);
- rc = tpm_crb_smc_start(&chip->dev, priv->smc_func_id);
- }
-
- if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) {
- iowrite32(CRB_START_INVOKE, &priv->regs_t->ctrl_start);
- rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_COMMAND, chip->locality);
- }
-
+ rc = crb_trigger_tpm(chip, CRB_START_INVOKE);
if (rc)
return rc;
--
2.53.0
^ permalink raw reply related
* [RFC v2 1/5] tpm_crb: Add register definitions of TPM CRB chunking fields
From: Arun Menon @ 2026-03-24 18:12 UTC (permalink / raw)
To: linux-kernel
Cc: Jarkko Sakkinen, linux-integrity, Peter Huewe, Jason Gunthorpe,
Arun Menon
In-Reply-To: <20260324181244.17741-1-armenon@redhat.com>
Post-quantum cryptographic (PQC) algorithms can require buffer sizes that
exceed the physical capacity of the TPM's Command/Response Buffer (CRB).
To support these larger payloads, the TPM 2.0 CRB specification [1]
allows for data chunking when the physical MMIO window is smaller than
the required buffer size.
To support this protocol, the TPM driver must be able to detect the
chunking capability, and signal the backend using specific start
method flags, also known as the control area start register bits.
As per sections 6.4.2.2 and 6.5.3.9 of the specification document [1]
Add 2 new bit flags to the existing enum crb_start and add the
capability bit.
- CRB_INTF_CAP_CRB_CHUNK: A capability bit used to detect if the backend
supports chunking.
- CRB_START_NEXT_CHUNK: A control bit to signal the TPM to consume the
current command buffer, or to get the next chunk from the response
buffer.
- CRB_START_RESP_RETRY: A control bit to signal retransmission of a
response buffer.
[1] https://trustedcomputinggroup.org/wp-content/uploads/PC-Client-Specific-Platform-TPM-Profile-for-TPM-2p0-v1p07_rc1_121225.pdf
Signed-off-by: Arun Menon <armenon@redhat.com>
---
drivers/char/tpm/tpm_crb.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c
index 6c25305c256ef..67c0061d4cab7 100644
--- a/drivers/char/tpm/tpm_crb.c
+++ b/drivers/char/tpm/tpm_crb.c
@@ -56,12 +56,18 @@ enum crb_ctrl_sts {
enum crb_start {
CRB_START_INVOKE = BIT(0),
+ CRB_START_RESP_RETRY = BIT(1),
+ CRB_START_NEXT_CHUNK = BIT(2),
};
enum crb_cancel {
CRB_CANCEL_INVOKE = BIT(0),
};
+enum crb_intf {
+ CRB_INTF_CAP_CRB_CHUNK = BIT(10),
+};
+
struct crb_regs_head {
u32 loc_state;
u32 reserved1;
--
2.53.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox