Add a supplementary information parameter to the asymmetric key signature
verification API, in particular crypto_sig_verify() and sig_alg::verify.
This takes the form of a printable string containing of key=val elements.
This is needed as some algorithms require additional metadata
(e.g. RSASSA-PSS) and this extra metadata is included in the X.509
certificates and PKCS#7 messages. Furthermore, keyctl(KEYCTL_PKEY_VERIFY)
already allows for this to be passed to the kernel, as do the _SIGN,
_ENCRYPT and _DECRYPT keyctls.
Signed-off-by: David Howells <dhowells@redhat.com>
Reviewed-by: Ignat Korchagin <ignat@cloudflare.com>
cc: Herbert Xu <herbert@gondor.apana.org.au>
cc: "David S. Miller" <davem@davemloft.net>
cc: Lukas Wunner <lukas@wunner.de>
cc: keyrings@vger.kernel.org
cc: linux-crypto@vger.kernel.org
---
crypto/asymmetric_keys/asymmetric_type.c | 1 +
crypto/asymmetric_keys/public_key.c | 2 +-
crypto/asymmetric_keys/signature.c | 1 +
crypto/ecdsa-p1363.c | 5 +++--
crypto/ecdsa-x962.c | 5 +++--
crypto/ecdsa.c | 3 ++-
crypto/ecrdsa.c | 3 ++-
crypto/mldsa.c | 3 ++-
crypto/rsassa-pkcs1.c | 3 ++-
crypto/sig.c | 3 ++-
crypto/testmgr.c | 2 +-
crypto/testmgr.h | 1 +
include/crypto/public_key.h | 1 +
include/crypto/sig.h | 9 ++++++---
14 files changed, 28 insertions(+), 14 deletions(-)
diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c
index 348966ea2175..dad4f0edfa25 100644
--- a/crypto/asymmetric_keys/asymmetric_type.c
+++ b/crypto/asymmetric_keys/asymmetric_type.c
@@ -596,6 +596,7 @@ static int asymmetric_key_verify_signature(struct kernel_pkey_params *params,
.digest_size = params->in_len,
.encoding = params->encoding,
.hash_algo = params->hash_algo,
+ .info = params->info,
.digest = (void *)in,
.s = (void *)in2,
};
diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
index ed6b4b5ae4ef..61dc4f626620 100644
--- a/crypto/asymmetric_keys/public_key.c
+++ b/crypto/asymmetric_keys/public_key.c
@@ -433,7 +433,7 @@ int public_key_verify_signature(const struct public_key *pkey,
goto error_free_key;
ret = crypto_sig_verify(tfm, sig->s, sig->s_size,
- sig->digest, sig->digest_size);
+ sig->digest, sig->digest_size, sig->info);
error_free_key:
kfree_sensitive(key);
diff --git a/crypto/asymmetric_keys/signature.c b/crypto/asymmetric_keys/signature.c
index bea01cf27d0a..30ba50eb44af 100644
--- a/crypto/asymmetric_keys/signature.c
+++ b/crypto/asymmetric_keys/signature.c
@@ -30,6 +30,7 @@ void public_key_signature_free(struct public_key_signature *sig)
kfree(sig->s);
if (sig->digest_free)
kfree(sig->digest);
+ kfree(sig->info);
kfree(sig);
}
}
diff --git a/crypto/ecdsa-p1363.c b/crypto/ecdsa-p1363.c
index e0c55c64711c..fa987dba1213 100644
--- a/crypto/ecdsa-p1363.c
+++ b/crypto/ecdsa-p1363.c
@@ -18,7 +18,8 @@ struct ecdsa_p1363_ctx {
static int ecdsa_p1363_verify(struct crypto_sig *tfm,
const void *src, unsigned int slen,
- const void *digest, unsigned int dlen)
+ const void *digest, unsigned int dlen,
+ const char *info)
{
struct ecdsa_p1363_ctx *ctx = crypto_sig_ctx(tfm);
unsigned int keylen = DIV_ROUND_UP_POW2(crypto_sig_keysize(ctx->child),
@@ -32,7 +33,7 @@ static int ecdsa_p1363_verify(struct crypto_sig *tfm,
ecc_digits_from_bytes(src, keylen, sig.r, ndigits);
ecc_digits_from_bytes(src + keylen, keylen, sig.s, ndigits);
- return crypto_sig_verify(ctx->child, &sig, sizeof(sig), digest, dlen);
+ return crypto_sig_verify(ctx->child, &sig, sizeof(sig), digest, dlen, info);
}
static unsigned int ecdsa_p1363_key_size(struct crypto_sig *tfm)
diff --git a/crypto/ecdsa-x962.c b/crypto/ecdsa-x962.c
index ee71594d10a0..5d7f1078989c 100644
--- a/crypto/ecdsa-x962.c
+++ b/crypto/ecdsa-x962.c
@@ -75,7 +75,8 @@ int ecdsa_get_signature_s(void *context, size_t hdrlen, unsigned char tag,
static int ecdsa_x962_verify(struct crypto_sig *tfm,
const void *src, unsigned int slen,
- const void *digest, unsigned int dlen)
+ const void *digest, unsigned int dlen,
+ const char *info)
{
struct ecdsa_x962_ctx *ctx = crypto_sig_ctx(tfm);
struct ecdsa_x962_signature_ctx sig_ctx;
@@ -89,7 +90,7 @@ static int ecdsa_x962_verify(struct crypto_sig *tfm,
return err;
return crypto_sig_verify(ctx->child, &sig_ctx.sig, sizeof(sig_ctx.sig),
- digest, dlen);
+ digest, dlen, info);
}
static unsigned int ecdsa_x962_key_size(struct crypto_sig *tfm)
diff --git a/crypto/ecdsa.c b/crypto/ecdsa.c
index ce8e4364842f..144fd6b9168b 100644
--- a/crypto/ecdsa.c
+++ b/crypto/ecdsa.c
@@ -65,7 +65,8 @@ static int _ecdsa_verify(struct ecc_ctx *ctx, const u64 *hash, const u64 *r, con
*/
static int ecdsa_verify(struct crypto_sig *tfm,
const void *src, unsigned int slen,
- const void *digest, unsigned int dlen)
+ const void *digest, unsigned int dlen,
+ const char *info)
{
struct ecc_ctx *ctx = crypto_sig_ctx(tfm);
size_t bufsize = ctx->curve->g.ndigits * sizeof(u64);
diff --git a/crypto/ecrdsa.c b/crypto/ecrdsa.c
index 2c0602f0cd40..59f2d5bb3be4 100644
--- a/crypto/ecrdsa.c
+++ b/crypto/ecrdsa.c
@@ -69,7 +69,8 @@ static const struct ecc_curve *get_curve_by_oid(enum OID oid)
static int ecrdsa_verify(struct crypto_sig *tfm,
const void *src, unsigned int slen,
- const void *digest, unsigned int dlen)
+ const void *digest, unsigned int dlen,
+ const char *info)
{
struct ecrdsa_ctx *ctx = crypto_sig_ctx(tfm);
unsigned int ndigits = dlen / sizeof(u64);
diff --git a/crypto/mldsa.c b/crypto/mldsa.c
index 2146c774b5ca..ba071d030ab0 100644
--- a/crypto/mldsa.c
+++ b/crypto/mldsa.c
@@ -25,7 +25,8 @@ static int crypto_mldsa_sign(struct crypto_sig *tfm,
static int crypto_mldsa_verify(struct crypto_sig *tfm,
const void *sig, unsigned int sig_len,
- const void *msg, unsigned int msg_len)
+ const void *msg, unsigned int msg_len,
+ const char *info)
{
const struct crypto_mldsa_ctx *ctx = crypto_sig_ctx(tfm);
diff --git a/crypto/rsassa-pkcs1.c b/crypto/rsassa-pkcs1.c
index 94fa5e9600e7..6283050e609a 100644
--- a/crypto/rsassa-pkcs1.c
+++ b/crypto/rsassa-pkcs1.c
@@ -215,7 +215,8 @@ static int rsassa_pkcs1_sign(struct crypto_sig *tfm,
static int rsassa_pkcs1_verify(struct crypto_sig *tfm,
const void *src, unsigned int slen,
- const void *digest, unsigned int dlen)
+ const void *digest, unsigned int dlen,
+ const char *info)
{
struct sig_instance *inst = sig_alg_instance(tfm);
struct rsassa_pkcs1_inst_ctx *ictx = sig_instance_ctx(inst);
diff --git a/crypto/sig.c b/crypto/sig.c
index beba745b6405..c56fea3a53ae 100644
--- a/crypto/sig.c
+++ b/crypto/sig.c
@@ -92,7 +92,8 @@ static int sig_default_sign(struct crypto_sig *tfm,
static int sig_default_verify(struct crypto_sig *tfm,
const void *src, unsigned int slen,
- const void *dst, unsigned int dlen)
+ const void *dst, unsigned int dlen,
+ const char *info)
{
return -ENOSYS;
}
diff --git a/crypto/testmgr.c b/crypto/testmgr.c
index 5df204d9c9dd..51f76b15f134 100644
--- a/crypto/testmgr.c
+++ b/crypto/testmgr.c
@@ -3969,7 +3969,7 @@ static int test_sig_one(struct crypto_sig *tfm, const struct sig_testvec *vecs)
* (which does not require a private key)
*/
err = crypto_sig_verify(tfm, vecs->c, vecs->c_size,
- vecs->m, vecs->m_size);
+ vecs->m, vecs->m_size, vecs->verify_info);
if (err) {
pr_err("alg: sig: verify test failed: err %d\n", err);
return err;
diff --git a/crypto/testmgr.h b/crypto/testmgr.h
index 1a3329e1c325..305adad2f2d0 100644
--- a/crypto/testmgr.h
+++ b/crypto/testmgr.h
@@ -146,6 +146,7 @@ struct akcipher_testvec {
struct sig_testvec {
const unsigned char *key;
+ const unsigned char *verify_info;
const unsigned char *params;
const unsigned char *m;
const unsigned char *c;
diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
index 68899a49cd0d..b6f2f2218aae 100644
--- a/include/crypto/public_key.h
+++ b/include/crypto/public_key.h
@@ -48,6 +48,7 @@ struct public_key_signature {
u32 digest_size; /* Number of bytes in digest */
bool digest_free; /* T if digest needs freeing */
bool algo_does_hash; /* Public key algo does its own hashing */
+ char *info; /* Supplementary parameters */
const char *pkey_algo;
const char *hash_algo;
const char *encoding;
diff --git a/include/crypto/sig.h b/include/crypto/sig.h
index fa6dafafab3f..885fa6487780 100644
--- a/include/crypto/sig.h
+++ b/include/crypto/sig.h
@@ -56,7 +56,8 @@ struct sig_alg {
void *dst, unsigned int dlen);
int (*verify)(struct crypto_sig *tfm,
const void *src, unsigned int slen,
- const void *digest, unsigned int dlen);
+ const void *digest, unsigned int dlen,
+ const char *info);
int (*set_pub_key)(struct crypto_sig *tfm,
const void *key, unsigned int keylen);
int (*set_priv_key)(struct crypto_sig *tfm,
@@ -209,16 +210,18 @@ static inline int crypto_sig_sign(struct crypto_sig *tfm,
* @slen: source length
* @digest: digest
* @dlen: digest length
+ * @info: Additional parameters as a set of k=v
*
* Return: zero on verification success; error code in case of error.
*/
static inline int crypto_sig_verify(struct crypto_sig *tfm,
const void *src, unsigned int slen,
- const void *digest, unsigned int dlen)
+ const void *digest, unsigned int dlen,
+ const char *info)
{
struct sig_alg *alg = crypto_sig_alg(tfm);
- return alg->verify(tfm, src, slen, digest, dlen);
+ return alg->verify(tfm, src, slen, digest, dlen, info);
}
/**
On Thu, Jan 15, 2026 at 09:50:48PM +0000, David Howells wrote: > Add a supplementary information parameter to the asymmetric key signature > verification API, in particular crypto_sig_verify() and sig_alg::verify. > This takes the form of a printable string containing of key=val elements. As I'm sure you're aware, C has native support for function parameters. No need to serialize to a string on the caller side and then deserialize in the callee. This is yet another example of a case where trying to fit different algorithms into a generic API doesn't work well. We should just have a library API for each signature algorithm, with each algorithm taking the parameters it needs. - Eric
On Tue, Jan 20, 2026 at 02:03:21PM -0800, Eric Biggers wrote: > On Thu, Jan 15, 2026 at 09:50:48PM +0000, David Howells wrote: > > Add a supplementary information parameter to the asymmetric key signature > > verification API, in particular crypto_sig_verify() and sig_alg::verify. > > This takes the form of a printable string containing of key=val elements. > > As I'm sure you're aware, C has native support for function parameters. > No need to serialize to a string on the caller side and then deserialize > in the callee. > > This is yet another example of a case where trying to fit different > algorithms into a generic API doesn't work well. > > We should just have a library API for each signature algorithm, with > each algorithm taking the parameters it needs. This would be a great idea IMHO. I'm looking into renewing my TPM2 asymmetric keys patch set some day, and e.g., for that such primitives would be more ergonomic fit. > > - Eric BR, Jarkko
Eric Biggers <ebiggers@kernel.org> wrote: > As I'm sure you're aware, C has native support for function parameters. And we have a syscall interface to honour that takes a parameter string *for this very purpose*. It just wasn't threaded into the akcipher API. > This is yet another example of a case where trying to fit different > algorithms into a generic API doesn't work well. Well enough. > We should just have a library API for each signature algorithm, with > each algorithm taking the parameters it needs. No, we shouldn't. We have a library that allows demand loadable modules. We should use that. David
On Tue, Jan 20, 2026 at 11:39:46PM +0000, David Howells wrote: > Eric Biggers <ebiggers@kernel.org> wrote: > > > As I'm sure you're aware, C has native support for function parameters. > > And we have a syscall interface to honour that takes a parameter string *for > this very purpose*. It just wasn't threaded into the akcipher API. This seems to be more of a bug than a feature, though. It seems the actual goals of this patchset are to add ML-DSA and RSASSA-PSS support to kernel module signing. But because of how the code is organized, as a side effect it ended up extending the KEYCTL_PKEY_* UAPIs as well. Linux's UAPI stability guarantee holds for these UAPIs; anything that we add to them, including these ad-hoc and undocumented parameter strings, will likely have to be supported forever. Unless these keyctl UAPI extensions are well-justified and come with documentation and tests, we should just hold off on them for now. What's the hurry? BTW, we got hit by this when there was an attempt to remove SHA-1 support from module signing. Due to the design defect where the signing is also exposed through KEYCTL_PKEY_*, it caused a UAPI regression as well and had to be reverted. - Eric
© 2016 - 2026 Red Hat, Inc.