* [ima-evm-utils PATCH 0/5] Add support for ML-DSA signing and verification
@ 2026-04-06 0:08 Stefan Berger
2026-04-06 0:08 ` [ima-evm-utils PATCH 1/5] checkpatch: Remove warning when function name is found in output string Stefan Berger
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: Stefan Berger @ 2026-04-06 0:08 UTC (permalink / raw)
To: linux-integrity; +Cc: zohar, roberto.sassu, ebiggers, coxu, Stefan Berger
This series adds support for ML-DSA signing and verification for EVM and
IMA using ML-DSA in pure mode and IMA's sigv3 signature format. Evmctl
will prevent users from trying to use sigv2 signatures with ML-DSA and
therefore ML-DSA requires the usage of --v3. Test cases for IMA and EVM
sigv3 are added using ML-DSA-44 (2420 bytes) and ML-DSA-65 (3309 bytes)
type of keys since those are more likely to fit into xattrs on most
filesystems than the even larger ML-DSA-87 signatures (4627 bytes).
Stefan
Stefan Berger (5):
checkpatch: Remove warning when function name is found in output
string
Set size of xattr_value to MAX_SIGNATURE_SIZE
Support signing with ML-DSA keys when OpenSSL >=3.5 is available
examples: Implement script to create ML-DSA-65 CA and signing keys
test: Add tests for signing and verifying with ML-DSA keys
README | 3 +-
examples/ima-gen-local-ca-mldsa65.sh | 29 +++
examples/ima-genkey-mldsa65.sh | 34 ++++
examples/ima-genkey-mldsa87.sh | 34 ++++
scripts/checkpatch.pl | 14 +-
src/evmctl.c | 6 +-
src/imaevm.h | 5 +-
src/libimaevm.c | 258 +++++++++++++++++++++++++--
tests/gen-keys.sh | 22 +++
tests/sign_verify.test | 40 ++++-
10 files changed, 417 insertions(+), 28 deletions(-)
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
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [ima-evm-utils PATCH 1/5] checkpatch: Remove warning when function name is found in output string
2026-04-06 0:08 [ima-evm-utils PATCH 0/5] Add support for ML-DSA signing and verification Stefan Berger
@ 2026-04-06 0:08 ` Stefan Berger
2026-04-06 0:08 ` [ima-evm-utils PATCH 2/5] Set size of xattr_value to MAX_SIGNATURE_SIZE Stefan Berger
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Stefan Berger @ 2026-04-06 0:08 UTC (permalink / raw)
To: linux-integrity; +Cc: zohar, roberto.sassu, ebiggers, coxu, Stefan Berger
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 [flat|nested] 6+ messages in thread
* [ima-evm-utils PATCH 2/5] Set size of xattr_value to MAX_SIGNATURE_SIZE
2026-04-06 0:08 [ima-evm-utils PATCH 0/5] Add support for ML-DSA signing and verification Stefan Berger
2026-04-06 0:08 ` [ima-evm-utils PATCH 1/5] checkpatch: Remove warning when function name is found in output string Stefan Berger
@ 2026-04-06 0:08 ` Stefan Berger
2026-04-06 0:08 ` [ima-evm-utils PATCH 3/5] Support signing with ML-DSA keys when OpenSSL >=3.5 is available Stefan Berger
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Stefan Berger @ 2026-04-06 0:08 UTC (permalink / raw)
To: linux-integrity; +Cc: zohar, roberto.sassu, ebiggers, coxu, Stefan Berger
Set the size of an xattr_value that can be read to MAX_SIGNATURE_SIZE
so that ML-DSA keys can also be read once enabled (and MAX_SIGNATURE_SIZE
gets a larger value).
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 de67178..c8da495 100644
--- a/src/evmctl.c
+++ b/src/evmctl.c
@@ -347,7 +347,7 @@ static int calc_evm_hash(const char *file, const char *hash_algo,
EVP_MD_CTX *pctx;
unsigned int mdlen;
char **xattrname;
- char xattr_value[1024];
+ char xattr_value[MAX_SIGNATURE_SIZE];
char list[1024];
ssize_t list_size;
char uuid[16];
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [ima-evm-utils PATCH 3/5] Support signing with ML-DSA keys when OpenSSL >=3.5 is available
2026-04-06 0:08 [ima-evm-utils PATCH 0/5] Add support for ML-DSA signing and verification Stefan Berger
2026-04-06 0:08 ` [ima-evm-utils PATCH 1/5] checkpatch: Remove warning when function name is found in output string Stefan Berger
2026-04-06 0:08 ` [ima-evm-utils PATCH 2/5] Set size of xattr_value to MAX_SIGNATURE_SIZE Stefan Berger
@ 2026-04-06 0:08 ` Stefan Berger
2026-04-06 0:08 ` [ima-evm-utils PATCH 4/5] examples: Implement script to create ML-DSA-65 CA and signing keys Stefan Berger
2026-04-06 0:08 ` [ima-evm-utils PATCH 5/5] test: Add tests for signing and verifying with ML-DSA keys Stefan Berger
4 siblings, 0 replies; 6+ messages in thread
From: Stefan Berger @ 2026-04-06 0:08 UTC (permalink / raw)
To: linux-integrity; +Cc: zohar, roberto.sassu, ebiggers, coxu, Stefan Berger
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 [flat|nested] 6+ messages in thread
* [ima-evm-utils PATCH 4/5] examples: Implement script to create ML-DSA-65 CA and signing keys
2026-04-06 0:08 [ima-evm-utils PATCH 0/5] Add support for ML-DSA signing and verification Stefan Berger
` (2 preceding siblings ...)
2026-04-06 0:08 ` [ima-evm-utils PATCH 3/5] Support signing with ML-DSA keys when OpenSSL >=3.5 is available Stefan Berger
@ 2026-04-06 0:08 ` Stefan Berger
2026-04-06 0:08 ` [ima-evm-utils PATCH 5/5] test: Add tests for signing and verifying with ML-DSA keys Stefan Berger
4 siblings, 0 replies; 6+ messages in thread
From: Stefan Berger @ 2026-04-06 0:08 UTC (permalink / raw)
To: linux-integrity; +Cc: zohar, roberto.sassu, ebiggers, coxu, Stefan Berger
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 [flat|nested] 6+ messages in thread
* [ima-evm-utils PATCH 5/5] test: Add tests for signing and verifying with ML-DSA keys
2026-04-06 0:08 [ima-evm-utils PATCH 0/5] Add support for ML-DSA signing and verification Stefan Berger
` (3 preceding siblings ...)
2026-04-06 0:08 ` [ima-evm-utils PATCH 4/5] examples: Implement script to create ML-DSA-65 CA and signing keys Stefan Berger
@ 2026-04-06 0:08 ` Stefan Berger
4 siblings, 0 replies; 6+ messages in thread
From: Stefan Berger @ 2026-04-06 0:08 UTC (permalink / raw)
To: linux-integrity; +Cc: zohar, roberto.sassu, ebiggers, coxu, Stefan Berger
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 [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-04-06 0:08 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-06 0:08 [ima-evm-utils PATCH 0/5] Add support for ML-DSA signing and verification Stefan Berger
2026-04-06 0:08 ` [ima-evm-utils PATCH 1/5] checkpatch: Remove warning when function name is found in output string Stefan Berger
2026-04-06 0:08 ` [ima-evm-utils PATCH 2/5] Set size of xattr_value to MAX_SIGNATURE_SIZE Stefan Berger
2026-04-06 0:08 ` [ima-evm-utils PATCH 3/5] Support signing with ML-DSA keys when OpenSSL >=3.5 is available Stefan Berger
2026-04-06 0:08 ` [ima-evm-utils PATCH 4/5] examples: Implement script to create ML-DSA-65 CA and signing keys Stefan Berger
2026-04-06 0:08 ` [ima-evm-utils PATCH 5/5] test: Add tests for signing and verifying with ML-DSA keys Stefan Berger
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox