All of lore.kernel.org
 help / color / mirror / Atom feed
From: David Howells <dhowells@redhat.com>
To: Lukas Wunner <lukas@wunner.de>, Ignat Korchagin <ignat@cloudflare.com>
Cc: David Howells <dhowells@redhat.com>,
	Jarkko Sakkinen <jarkko@kernel.org>,
	Herbert Xu <herbert@gondor.apana.org.au>,
	Eric Biggers <ebiggers@kernel.org>,
	Luis Chamberlain <mcgrof@kernel.org>,
	Petr Pavlu <petr.pavlu@suse.com>,
	Daniel Gomez <da.gomez@kernel.org>,
	Sami Tolvanen <samitolvanen@google.com>,
	"Jason A . Donenfeld" <Jason@zx2c4.com>,
	Ard Biesheuvel <ardb@kernel.org>,
	Stephan Mueller <smueller@chronox.de>,
	linux-crypto@vger.kernel.org, keyrings@vger.kernel.org,
	linux-modules@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH v11 7/8] pkcs7, x509: Add RSASSA-PSS support
Date: Mon,  5 Jan 2026 15:21:32 +0000	[thread overview]
Message-ID: <20260105152145.1801972-8-dhowells@redhat.com> (raw)
In-Reply-To: <20260105152145.1801972-1-dhowells@redhat.com>

Add support for RSASSA-PSS keys and signatures to the PKCS#7 and X.509
implementations.  This requires adding support for algorithm parameters for
keys and signatures as RSASSA-PSS needs metadata.  The ASN.1 encoded data
is converted into a printable key=value list string and passed to the
verification code.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Lukas Wunner <lukas@wunner.de>
cc: Ignat Korchagin <ignat@cloudflare.com>
cc: Herbert Xu <herbert@gondor.apana.org.au>
cc: keyrings@vger.kernel.org
cc: linux-crypto@vger.kernel.org
---
 crypto/asymmetric_keys/Makefile           |  12 +-
 crypto/asymmetric_keys/mgf1_params.asn1   |  12 ++
 crypto/asymmetric_keys/pkcs7.asn1         |   2 +-
 crypto/asymmetric_keys/pkcs7_parser.c     | 113 ++++++-----
 crypto/asymmetric_keys/public_key.c       |  10 +
 crypto/asymmetric_keys/rsassa_params.asn1 |  25 +++
 crypto/asymmetric_keys/rsassa_parser.c    | 233 ++++++++++++++++++++++
 crypto/asymmetric_keys/rsassa_parser.h    |  25 +++
 crypto/asymmetric_keys/x509.asn1          |   2 +-
 crypto/asymmetric_keys/x509_cert_parser.c |  96 +++++----
 crypto/asymmetric_keys/x509_parser.h      |  33 ++-
 crypto/asymmetric_keys/x509_public_key.c  |  28 ++-
 include/linux/oid_registry.h              |   2 +
 13 files changed, 490 insertions(+), 103 deletions(-)
 create mode 100644 crypto/asymmetric_keys/mgf1_params.asn1
 create mode 100644 crypto/asymmetric_keys/rsassa_params.asn1
 create mode 100644 crypto/asymmetric_keys/rsassa_parser.c
 create mode 100644 crypto/asymmetric_keys/rsassa_parser.h

diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index bc65d3b98dcb..c5aed382ee8a 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -21,7 +21,11 @@ x509_key_parser-y := \
 	x509_akid.asn1.o \
 	x509_cert_parser.o \
 	x509_loader.o \
-	x509_public_key.o
+	x509_public_key.o \
+	rsassa_params.asn1.o \
+	rsassa_parser.o \
+	mgf1_params.asn1.o
+
 obj-$(CONFIG_FIPS_SIGNATURE_SELFTEST) += x509_selftest.o
 x509_selftest-y += selftest.o
 x509_selftest-$(CONFIG_FIPS_SIGNATURE_SELFTEST_RSA) += selftest_rsa.o
@@ -31,8 +35,14 @@ $(obj)/x509_cert_parser.o: \
 	$(obj)/x509.asn1.h \
 	$(obj)/x509_akid.asn1.h
 
+$(obj)/rsassa_parser.o: \
+	$(obj)/rsassa_params.asn1.h \
+	$(obj)/mgf1_params.asn1.h
+
 $(obj)/x509.asn1.o: $(obj)/x509.asn1.c $(obj)/x509.asn1.h
 $(obj)/x509_akid.asn1.o: $(obj)/x509_akid.asn1.c $(obj)/x509_akid.asn1.h
+$(obj)/rsassa_params.asn1.o: $(obj)/rsassa_params.asn1.c $(obj)/rsassa_params.asn1.h
+$(obj)/mgf1_params.asn1.o: $(obj)/mgf1_params.asn1.c $(obj)/mgf1_params.asn1.h
 
 #
 # PKCS#8 private key handling
diff --git a/crypto/asymmetric_keys/mgf1_params.asn1 b/crypto/asymmetric_keys/mgf1_params.asn1
new file mode 100644
index 000000000000..c3bc4643e72c
--- /dev/null
+++ b/crypto/asymmetric_keys/mgf1_params.asn1
@@ -0,0 +1,12 @@
+-- SPDX-License-Identifier: BSD-3-Clause
+--
+-- Copyright (C) 2009 IETF Trust and the persons identified as authors
+-- of the code
+--
+--
+-- https://datatracker.ietf.org/doc/html/rfc4055 Section 6.
+
+AlgorithmIdentifier ::= SEQUENCE {
+	algorithm	OBJECT IDENTIFIER ({ mgf1_note_OID }),
+	parameters	ANY OPTIONAL
+}
diff --git a/crypto/asymmetric_keys/pkcs7.asn1 b/crypto/asymmetric_keys/pkcs7.asn1
index 28e1f4a41c14..03c2248f23bc 100644
--- a/crypto/asymmetric_keys/pkcs7.asn1
+++ b/crypto/asymmetric_keys/pkcs7.asn1
@@ -124,7 +124,7 @@ UnauthenticatedAttribute ::= SEQUENCE {
 
 DigestEncryptionAlgorithmIdentifier ::= SEQUENCE {
 	algorithm		OBJECT IDENTIFIER ({ pkcs7_note_OID }),
-	parameters		ANY OPTIONAL
+	parameters		ANY OPTIONAL ({ pkcs7_sig_note_algo_params })
 }
 
 EncryptedDigest ::= OCTET STRING ({ pkcs7_sig_note_signature })
diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c
index 90c36fe1b5ed..81996b60c1f1 100644
--- a/crypto/asymmetric_keys/pkcs7_parser.c
+++ b/crypto/asymmetric_keys/pkcs7_parser.c
@@ -14,6 +14,7 @@
 #include <linux/oid_registry.h>
 #include <crypto/public_key.h>
 #include "pkcs7_parser.h"
+#include "rsassa_parser.h"
 #include "pkcs7.asn1.h"
 
 MODULE_DESCRIPTION("PKCS#7 parser");
@@ -30,6 +31,8 @@ struct pkcs7_parse_context {
 	enum OID	last_oid;		/* Last OID encountered */
 	unsigned	x509_index;
 	unsigned	sinfo_index;
+	unsigned	algo_params_size;
+	const void	*algo_params;
 	const void	*raw_serial;
 	unsigned	raw_serial_size;
 	unsigned	raw_issuer_size;
@@ -225,45 +228,29 @@ int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen,
 			       const void *value, size_t vlen)
 {
 	struct pkcs7_parse_context *ctx = context;
+	const char *algo;
 
-	switch (ctx->last_oid) {
-	case OID_sha1:
-		ctx->sinfo->sig->hash_algo = "sha1";
-		break;
-	case OID_sha256:
-		ctx->sinfo->sig->hash_algo = "sha256";
-		break;
-	case OID_sha384:
-		ctx->sinfo->sig->hash_algo = "sha384";
-		break;
-	case OID_sha512:
-		ctx->sinfo->sig->hash_algo = "sha512";
-		break;
-	case OID_sha224:
-		ctx->sinfo->sig->hash_algo = "sha224";
-		break;
-	case OID_sm3:
-		ctx->sinfo->sig->hash_algo = "sm3";
-		break;
-	case OID_gost2012Digest256:
-		ctx->sinfo->sig->hash_algo = "streebog256";
-		break;
-	case OID_gost2012Digest512:
-		ctx->sinfo->sig->hash_algo = "streebog512";
-		break;
-	case OID_sha3_256:
-		ctx->sinfo->sig->hash_algo = "sha3-256";
-		break;
-	case OID_sha3_384:
-		ctx->sinfo->sig->hash_algo = "sha3-384";
-		break;
-	case OID_sha3_512:
-		ctx->sinfo->sig->hash_algo = "sha3-512";
-		break;
-	default:
-		printk("Unsupported digest algo: %u\n", ctx->last_oid);
+	algo = oid_to_hash(ctx->last_oid);
+	if (!algo) {
+		pr_notice("Unsupported digest algo: %u\n", ctx->last_oid);
 		return -ENOPKG;
 	}
+
+	ctx->sinfo->sig->hash_algo = algo;
+	return 0;
+}
+
+/*
+ * Note the parameters for the signature.
+ */
+int pkcs7_sig_note_algo_params(void *context, size_t hdrlen,
+			       unsigned char tag,
+			       const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	ctx->algo_params = value - hdrlen;
+	ctx->algo_params_size = vlen + hdrlen;
 	return 0;
 }
 
@@ -275,12 +262,16 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,
 			     const void *value, size_t vlen)
 {
 	struct pkcs7_parse_context *ctx = context;
+	struct public_key_signature *sig = ctx->sinfo->sig;
+	int err;
 
 	switch (ctx->last_oid) {
 	case OID_rsaEncryption:
-		ctx->sinfo->sig->pkey_algo = "rsa";
-		ctx->sinfo->sig->encoding = "pkcs1";
+		sig->pkey_algo = "rsa";
+		sig->encoding = "pkcs1";
 		break;
+	case OID_id_rsassa_pss:
+		goto rsassa_pss;
 	case OID_id_ecdsa_with_sha1:
 	case OID_id_ecdsa_with_sha224:
 	case OID_id_ecdsa_with_sha256:
@@ -289,34 +280,52 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,
 	case OID_id_ecdsa_with_sha3_256:
 	case OID_id_ecdsa_with_sha3_384:
 	case OID_id_ecdsa_with_sha3_512:
-		ctx->sinfo->sig->pkey_algo = "ecdsa";
-		ctx->sinfo->sig->encoding = "x962";
+		sig->pkey_algo = "ecdsa";
+		sig->encoding = "x962";
 		break;
 	case OID_gost2012PKey256:
 	case OID_gost2012PKey512:
-		ctx->sinfo->sig->pkey_algo = "ecrdsa";
-		ctx->sinfo->sig->encoding = "raw";
+		sig->pkey_algo = "ecrdsa";
+		sig->encoding = "raw";
 		break;
 	case OID_id_ml_dsa_44:
-		ctx->sinfo->sig->pkey_algo = "mldsa44";
-		ctx->sinfo->sig->encoding = "raw";
-		ctx->sinfo->sig->algo_does_hash = true;
+		sig->pkey_algo = "mldsa44";
+		sig->encoding = "raw";
+		sig->algo_does_hash = true;
 		break;
 	case OID_id_ml_dsa_65:
-		ctx->sinfo->sig->pkey_algo = "mldsa65";
-		ctx->sinfo->sig->encoding = "raw";
-		ctx->sinfo->sig->algo_does_hash = true;
+		sig->pkey_algo = "mldsa65";
+		sig->encoding = "raw";
+		sig->algo_does_hash = true;
 		break;
 	case OID_id_ml_dsa_87:
-		ctx->sinfo->sig->pkey_algo = "mldsa87";
-		ctx->sinfo->sig->encoding = "raw";
-		ctx->sinfo->sig->algo_does_hash = true;
+		sig->pkey_algo = "mldsa87";
+		sig->encoding = "raw";
+		sig->algo_does_hash = true;
 		break;
 	default:
-		printk("Unsupported pkey algo: %u\n", ctx->last_oid);
+		pr_notice("Unsupported pkey algo: %u\n", ctx->last_oid);
 		return -ENOPKG;
 	}
+
+out:
+	ctx->algo_params = NULL;
+	ctx->algo_params_size = 0;
 	return 0;
+
+rsassa_pss:
+	if (!ctx->algo_params || !ctx->algo_params_size) {
+		pr_debug("RSASSA-PSS sig algo without parameters\n");
+		return -EBADMSG;
+	}
+
+	err = rsassa_parse_sig_params(sig, ctx->algo_params, ctx->algo_params_size);
+	if (err < 0)
+		return err;
+
+	sig->pkey_algo = "rsa";
+	sig->encoding = "emsa-pss";
+	goto out;
 }
 
 /*
diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
index 61dc4f626620..13a5616becaa 100644
--- a/crypto/asymmetric_keys/public_key.c
+++ b/crypto/asymmetric_keys/public_key.c
@@ -100,6 +100,16 @@ software_key_determine_akcipher(const struct public_key *pkey,
 			}
 			return n >= CRYPTO_MAX_ALG_NAME ? -EINVAL : 0;
 		}
+		if (strcmp(encoding, "emsa-pss") == 0) {
+			if (op != kernel_pkey_sign &&
+			    op != kernel_pkey_verify)
+				return -EINVAL;
+			*sig = true;
+			if (!hash_algo)
+				hash_algo = "none";
+			n = snprintf(alg_name, CRYPTO_MAX_ALG_NAME, "rsassa-pss");
+			return n >= CRYPTO_MAX_ALG_NAME ? -EINVAL : 0;
+		}
 		if (strcmp(encoding, "raw") != 0)
 			return -EINVAL;
 		/*
diff --git a/crypto/asymmetric_keys/rsassa_params.asn1 b/crypto/asymmetric_keys/rsassa_params.asn1
new file mode 100644
index 000000000000..95a4e5f0dcd5
--- /dev/null
+++ b/crypto/asymmetric_keys/rsassa_params.asn1
@@ -0,0 +1,25 @@
+-- SPDX-License-Identifier: BSD-3-Clause
+--
+-- Copyright (C) 2009 IETF Trust and the persons identified as authors
+-- of the code
+--
+--
+-- https://datatracker.ietf.org/doc/html/rfc4055 Section 6.
+
+RSASSA-PSS-params ::= SEQUENCE {
+	hashAlgorithm      [0] HashAlgorithm,
+	maskGenAlgorithm   [1] MaskGenAlgorithm,
+	saltLength         [2] INTEGER ({ rsassa_note_salt_length }),
+	trailerField       [3] TrailerField OPTIONAL
+}
+
+TrailerField ::= INTEGER ({ rsassa_note_trailer })
+-- { trailerFieldBC(1) }
+
+HashAlgorithm ::= AlgorithmIdentifier ({ rsassa_note_hash_algo })
+MaskGenAlgorithm ::= AlgorithmIdentifier ({ rsassa_note_maskgen_algo })
+
+AlgorithmIdentifier ::= SEQUENCE {
+	algorithm	OBJECT IDENTIFIER ({ rsassa_note_OID }),
+	parameters	ANY OPTIONAL ({ rsassa_note_params })
+}
diff --git a/crypto/asymmetric_keys/rsassa_parser.c b/crypto/asymmetric_keys/rsassa_parser.c
new file mode 100644
index 000000000000..8c598517f785
--- /dev/null
+++ b/crypto/asymmetric_keys/rsassa_parser.c
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* RSASSA-PSS ASN.1 parameter parser
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#define pr_fmt(fmt) "RSAPSS: "fmt
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/asn1.h>
+#include <crypto/hash.h>
+#include <crypto/hash_info.h>
+#include <crypto/public_key.h>
+#include "x509_parser.h"
+#include "rsassa_parser.h"
+#include "rsassa_params.asn1.h"
+#include "mgf1_params.asn1.h"
+
+struct rsassa_parse_context {
+	struct rsassa_parameters *rsassa;	/* The parsed parameters */
+	unsigned long	data;			/* Start of data */
+	const void	*params;		/* Algo parameters */
+	unsigned int	params_len;		/* Length of algo parameters */
+	enum OID	last_oid;		/* Last OID encountered */
+	enum OID	mgf1_last_oid;		/* Last OID encountered in MGF1 */
+};
+
+/*
+ * Parse an RSASSA parameter block.
+ */
+struct rsassa_parameters *rsassa_params_parse(const void *data, size_t datalen)
+{
+	struct rsassa_parse_context ctx = {};
+	struct rsassa_parameters *rsassa __free(kfree);
+	long ret;
+
+	rsassa = kzalloc(sizeof(*rsassa), GFP_KERNEL);
+	if (!rsassa)
+		return ERR_PTR(-ENOMEM);
+
+	ctx.rsassa = rsassa;
+	ctx.data = (unsigned long)data;
+
+	/* Attempt to decode the parameters */
+	ret = asn1_ber_decoder(&rsassa_params_decoder, &ctx, data, datalen);
+	if (ret < 0) {
+		pr_debug("RSASSA parse failed %ld\n", ret);
+		return ERR_PTR(ret);
+	}
+
+	return no_free_ptr(rsassa);
+}
+
+/*
+ * Note an OID when we find one for later processing when we know how
+ * to interpret it.
+ */
+int rsassa_note_OID(void *context, size_t hdrlen, unsigned char tag,
+		    const void *value, size_t vlen)
+{
+	struct rsassa_parse_context *ctx = context;
+
+	ctx->last_oid = look_up_OID(value, vlen);
+	if (ctx->last_oid == OID__NR) {
+		char buffer[56];
+		sprint_oid(value, vlen, buffer, sizeof(buffer));
+		pr_debug("Unknown OID: %s\n", buffer);
+	}
+	return 0;
+}
+
+/*
+ * Parse trailerField.  We only accept trailerFieldBC.
+*/
+int rsassa_note_trailer(void *context, size_t hdrlen, unsigned char tag,
+			const void *value, size_t vlen)
+{
+	if (vlen != 1 || *(u8 *)value != 0x01) {
+		pr_debug("Unknown trailerField\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+int rsassa_note_hash_algo(void *context, size_t hdrlen, unsigned char tag,
+			  const void *value, size_t vlen)
+{
+	struct rsassa_parse_context *ctx = context;
+
+	ctx->rsassa->hash_algo = ctx->last_oid;
+	pr_debug("HASH-ALGO %u %u\n", ctx->rsassa->hash_algo, ctx->params_len);
+	ctx->params = NULL;
+	return 0;
+}
+
+int rsassa_note_maskgen_algo(void *context, size_t hdrlen, unsigned char tag,
+			     const void *value, size_t vlen)
+{
+	struct rsassa_parse_context *ctx = context;
+	int ret;
+
+	ctx->rsassa->maskgen_algo = ctx->last_oid;
+	pr_debug("MGF-ALGO %u %u\n", ctx->rsassa->maskgen_algo, ctx->params_len);
+
+	switch (ctx->rsassa->maskgen_algo) {
+	case OID_id_mgf1:
+		if (!vlen) {
+			pr_debug("MGF1 missing parameters\n");
+			return -EBADMSG;
+		}
+
+		ret = asn1_ber_decoder(&mgf1_params_decoder, ctx,
+				       ctx->params, ctx->params_len);
+		if (ret < 0) {
+			pr_debug("MGF1 parse failed %d\n", ret);
+			return ret;
+		}
+		ctx->rsassa->maskgen_hash = ctx->mgf1_last_oid;
+		break;
+
+	default:
+		pr_debug("Unsupported MaskGenAlgorithm %d\n", ret);
+		return -ENOPKG;
+	}
+
+	ctx->params = NULL;
+	return 0;
+}
+
+int rsassa_note_salt_length(void *context, size_t hdrlen, unsigned char tag,
+			    const void *value, size_t vlen)
+{
+	struct rsassa_parse_context *ctx = context;
+	u32 salt_len = 0;
+
+	if (!vlen) {
+		pr_debug("Salt len bad integer\n");
+		return -EBADMSG;
+	}
+	if (vlen > 4) {
+		pr_debug("Salt len too long %zu\n", vlen);
+		return -EBADMSG;
+	}
+	if (((u8 *)value)[0] & 0x80) {
+		pr_debug("Salt len negative\n");
+		return -EBADMSG;
+	}
+
+	for (size_t i = 0; i < vlen; i++) {
+		salt_len <<= 8;
+		salt_len |= ((u8 *)value)[i];
+	}
+
+	ctx->rsassa->salt_len = salt_len;
+	pr_debug("Salt-Len %u\n", salt_len);
+	return 0;
+}
+
+/*
+ * Extract arbitrary parameters.
+ */
+int rsassa_note_params(void *context, size_t hdrlen, unsigned char tag,
+		       const void *value, size_t vlen)
+{
+	struct rsassa_parse_context *ctx = context;
+
+	ctx->params	= value - hdrlen;
+	ctx->params_len	= vlen + hdrlen;
+	return 0;
+}
+
+/*
+ * Note an OID when we find one for later processing when we know how to
+ * interpret it.
+ */
+int mgf1_note_OID(void *context, size_t hdrlen, unsigned char tag,
+		  const void *value, size_t vlen)
+{
+	struct rsassa_parse_context *ctx = context;
+
+	ctx->mgf1_last_oid = look_up_OID(value, vlen);
+	if (ctx->mgf1_last_oid == OID__NR) {
+		char buffer[56];
+		sprint_oid(value, vlen, buffer, sizeof(buffer));
+		pr_debug("Unknown MGF1 OID: %s\n", buffer);
+	}
+	return 0;
+}
+
+/*
+ * Parse the signature parameter block and generate a suitable info string from
+ * it.
+ */
+int rsassa_parse_sig_params(struct public_key_signature *sig,
+			    const u8 *sig_params, unsigned int sig_params_size)
+{
+	struct rsassa_parameters *rsassa __free(rsassa_params_free) = NULL;
+	const char *mf, *mh;
+
+	rsassa = rsassa_params_parse(sig_params, sig_params_size);
+	if (IS_ERR(rsassa))
+		return PTR_ERR(rsassa);
+
+	sig->hash_algo = oid_to_hash(rsassa->hash_algo);
+	if (!sig->hash_algo) {
+		pr_notice("Unsupported hash: %u\n", rsassa->hash_algo);
+		return -ENOPKG;
+	}
+
+	switch (rsassa->maskgen_algo) {
+	case OID_id_mgf1:
+		mf = "mgf1";
+		break;
+	default:
+		pr_notice("Unsupported maskgen algo: %u\n", rsassa->maskgen_algo);
+		return -ENOPKG;
+	}
+
+	mh = oid_to_hash(rsassa->maskgen_hash);
+	if (!mh) {
+		pr_notice("Unsupported MGF1 hash: %u\n", rsassa->maskgen_hash);
+		return -ENOPKG;
+	}
+
+	sig->info = kasprintf(GFP_KERNEL, "sighash=%s pss_mask=%s,%s pss_salt=%u",
+			      sig->hash_algo, mf, mh, rsassa->salt_len);
+	if (!sig->info)
+		return -ENOMEM;
+	pr_debug("Info string: %s\n", sig->info);
+	return 0;
+}
diff --git a/crypto/asymmetric_keys/rsassa_parser.h b/crypto/asymmetric_keys/rsassa_parser.h
new file mode 100644
index 000000000000..b80401a3de8f
--- /dev/null
+++ b/crypto/asymmetric_keys/rsassa_parser.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* RSASSA-PSS parameter parsing context
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#include <linux/oid_registry.h>
+
+struct rsassa_parameters {
+	enum OID	hash_algo;		/* Hash algorithm identifier */
+	enum OID	maskgen_algo;		/* Mask gen algorithm identifier */
+	enum OID	maskgen_hash;		/* Mask gen hash algorithm identifier */
+	u32		salt_len;
+};
+
+struct rsassa_parameters *rsassa_params_parse(const void *data, size_t datalen);
+int rsassa_parse_sig_params(struct public_key_signature *sig,
+			    const u8 *sig_params, unsigned int sig_params_size);
+
+static inline void rsassa_params_free(struct rsassa_parameters *params)
+{
+	kfree(params);
+}
+DEFINE_FREE(rsassa_params_free,  struct rsassa_parameters*, rsassa_params_free(_T))
diff --git a/crypto/asymmetric_keys/x509.asn1 b/crypto/asymmetric_keys/x509.asn1
index feb9573cacce..453b72eba1fe 100644
--- a/crypto/asymmetric_keys/x509.asn1
+++ b/crypto/asymmetric_keys/x509.asn1
@@ -29,7 +29,7 @@ CertificateSerialNumber ::= INTEGER
 
 AlgorithmIdentifier ::= SEQUENCE {
 	algorithm		OBJECT IDENTIFIER ({ x509_note_OID }),
-	parameters		ANY OPTIONAL ({ x509_note_params })
+	parameters		ANY OPTIONAL ({ x509_note_algo_id_params })
 }
 
 Name ::= SEQUENCE OF RelativeDistinguishedName
diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
index 5ab5b4e5f1b4..6c431bf181f2 100644
--- a/crypto/asymmetric_keys/x509_cert_parser.c
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -15,28 +15,7 @@
 #include "x509_parser.h"
 #include "x509.asn1.h"
 #include "x509_akid.asn1.h"
-
-struct x509_parse_context {
-	struct x509_certificate	*cert;		/* Certificate being constructed */
-	unsigned long	data;			/* Start of data */
-	const void	*key;			/* Key data */
-	size_t		key_size;		/* Size of key data */
-	const void	*params;		/* Key parameters */
-	size_t		params_size;		/* Size of key parameters */
-	enum OID	key_algo;		/* Algorithm used by the cert's key */
-	enum OID	last_oid;		/* Last OID encountered */
-	enum OID	sig_algo;		/* Algorithm used to sign the cert */
-	u8		o_size;			/* Size of organizationName (O) */
-	u8		cn_size;		/* Size of commonName (CN) */
-	u8		email_size;		/* Size of emailAddress */
-	u16		o_offset;		/* Offset of organizationName (O) */
-	u16		cn_offset;		/* Offset of commonName (CN) */
-	u16		email_offset;		/* Offset of emailAddress */
-	unsigned	raw_akid_size;
-	const void	*raw_akid;		/* Raw authorityKeyId in ASN.1 */
-	const void	*akid_raw_issuer;	/* Raw directoryName in authorityKeyId */
-	unsigned	akid_raw_issuer_size;
-};
+#include "rsassa_parser.h"
 
 /*
  * Free an X.509 certificate
@@ -104,15 +83,15 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
 
 	cert->pub->keylen = ctx->key_size;
 
-	cert->pub->params = kmemdup(ctx->params, ctx->params_size, GFP_KERNEL);
+	cert->pub->params = kmemdup(ctx->key_params, ctx->key_params_size, GFP_KERNEL);
 	if (!cert->pub->params)
 		return ERR_PTR(-ENOMEM);
 
-	cert->pub->paramlen = ctx->params_size;
+	cert->pub->paramlen = ctx->key_params_size;
 	cert->pub->algo = ctx->key_algo;
 
 	/* Grab the signature bits */
-	ret = x509_get_sig_params(cert);
+	ret = x509_get_sig_params(cert, ctx);
 	if (ret < 0)
 		return ERR_PTR(ret);
 
@@ -146,7 +125,7 @@ int x509_note_OID(void *context, size_t hdrlen,
 
 	ctx->last_oid = look_up_OID(value, vlen);
 	if (ctx->last_oid == OID__NR) {
-		char buffer[50];
+		char buffer[56];
 		sprint_oid(value, vlen, buffer, sizeof(buffer));
 		pr_debug("Unknown OID: [%lu] %s\n",
 			 (unsigned long)value - ctx->data, buffer);
@@ -179,6 +158,7 @@ int x509_note_sig_algo(void *context, size_t hdrlen, unsigned char tag,
 		       const void *value, size_t vlen)
 {
 	struct x509_parse_context *ctx = context;
+	int err;
 
 	pr_debug("PubKey Algo: %u\n", ctx->last_oid);
 
@@ -210,6 +190,9 @@ int x509_note_sig_algo(void *context, size_t hdrlen, unsigned char tag,
 		ctx->cert->sig->hash_algo = "sha1";
 		goto ecdsa;
 
+	case OID_id_rsassa_pss:
+		goto rsassa_pss;
+
 	case OID_id_rsassa_pkcs1_v1_5_with_sha3_256:
 		ctx->cert->sig->hash_algo = "sha3-256";
 		goto rsa_pkcs1;
@@ -268,6 +251,24 @@ int x509_note_sig_algo(void *context, size_t hdrlen, unsigned char tag,
 		goto ml_dsa;
 	}
 
+rsassa_pss:
+	if (!ctx->algo_params || !ctx->algo_params_size) {
+		pr_debug("RSASSA-PSS sig algo without parameters\n");
+		return -EBADMSG;
+	}
+
+	err = rsassa_parse_sig_params(ctx->cert->sig,
+				      ctx->algo_params, ctx->algo_params_size);
+	if (err < 0)
+		return err;
+
+	ctx->cert->sig->pkey_algo = "rsa";
+	ctx->cert->sig->encoding = "emsa-pss";
+	ctx->sig_algo = ctx->last_oid;
+	ctx->algo_params = NULL;
+	ctx->algo_params_size = 0;
+	return 0;
+
 rsa_pkcs1:
 	ctx->cert->sig->pkey_algo = "rsa";
 	ctx->cert->sig->encoding = "pkcs1";
@@ -324,8 +325,8 @@ int x509_note_signature(void *context, size_t hdrlen,
 		vlen--;
 	}
 
-	ctx->cert->raw_sig = value;
-	ctx->cert->raw_sig_size = vlen;
+	ctx->sig = value;
+	ctx->sig_size = vlen;
 	return 0;
 }
 
@@ -479,23 +480,16 @@ int x509_note_subject(void *context, size_t hdrlen,
 }
 
 /*
- * Extract the parameters for the public key
+ * Extract the parameters for an AlgorithmIdentifier.
  */
-int x509_note_params(void *context, size_t hdrlen,
-		     unsigned char tag,
-		     const void *value, size_t vlen)
+int x509_note_algo_id_params(void *context, size_t hdrlen,
+			     unsigned char tag,
+			     const void *value, size_t vlen)
 {
 	struct x509_parse_context *ctx = context;
 
-	/*
-	 * AlgorithmIdentifier is used three times in the x509, we should skip
-	 * first and ignore third, using second one which is after subject and
-	 * before subjectPublicKey.
-	 */
-	if (!ctx->cert->raw_subject || ctx->key)
-		return 0;
-	ctx->params = value - hdrlen;
-	ctx->params_size = vlen + hdrlen;
+	ctx->algo_params = value - hdrlen;
+	ctx->algo_params_size = vlen + hdrlen;
 	return 0;
 }
 
@@ -514,12 +508,28 @@ int x509_extract_key_data(void *context, size_t hdrlen,
 	case OID_rsaEncryption:
 		ctx->cert->pub->pkey_algo = "rsa";
 		break;
+	case OID_id_rsassa_pss:
+		/* Parameters are optional for the key itself. */
+		if (ctx->algo_params_size) {
+			struct rsassa_parameters *params __free(rsassa_params_free) = NULL;
+			ctx->key_params = ctx->algo_params;
+			ctx->key_params_size = ctx->algo_params_size;
+			ctx->algo_params = NULL;
+			ctx->algo_params_size = 0;
+
+			params = rsassa_params_parse(ctx->key_params, ctx->key_params_size);
+			if (IS_ERR(params))
+				return PTR_ERR(params);
+			break;
+		}
+		ctx->cert->pub->pkey_algo = "rsa";
+		break;
 	case OID_gost2012PKey256:
 	case OID_gost2012PKey512:
 		ctx->cert->pub->pkey_algo = "ecrdsa";
 		break;
 	case OID_id_ecPublicKey:
-		if (parse_OID(ctx->params, ctx->params_size, &oid) != 0)
+		if (parse_OID(ctx->algo_params, ctx->algo_params_size, &oid) != 0)
 			return -EBADMSG;
 
 		switch (oid) {
@@ -557,6 +567,8 @@ int x509_extract_key_data(void *context, size_t hdrlen,
 		return -EBADMSG;
 	ctx->key = value + 1;
 	ctx->key_size = vlen - 1;
+	ctx->algo_params = NULL;
+	ctx->algo_params_size = 0;
 	return 0;
 }
 
diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h
index 0688c222806b..be2e1f6cb9f5 100644
--- a/crypto/asymmetric_keys/x509_parser.h
+++ b/crypto/asymmetric_keys/x509_parser.h
@@ -23,8 +23,6 @@ struct x509_certificate {
 	time64_t	valid_to;
 	const void	*tbs;			/* Signed data */
 	unsigned	tbs_size;		/* Size of signed data */
-	unsigned	raw_sig_size;		/* Size of signature */
-	const void	*raw_sig;		/* Signature data */
 	const void	*raw_serial;		/* Raw serial number in ASN.1 */
 	unsigned	raw_serial_size;
 	unsigned	raw_issuer_size;
@@ -41,6 +39,34 @@ struct x509_certificate {
 	bool		blacklisted;
 };
 
+struct x509_parse_context {
+	struct x509_certificate	*cert;		/* Certificate being constructed */
+	unsigned long	data;			/* Start of data */
+	const void	*key;			/* Key data */
+	size_t		key_size;		/* Size of key data */
+	const void	*algo_params;		/* AlgorithmIdentifier: parameters */
+	size_t		algo_params_size;	/* AlgorithmIdentifier: parameters size */
+	const void	*key_params;		/* Key parameters */
+	size_t		key_params_size;	/* Size of key parameters */
+	const void	*sig_params;		/* Signature parameters */
+	unsigned int	sig_params_size;	/* Size of sig parameters */
+	unsigned int	sig_size;		/* Size of signature */
+	const void	*sig;			/* Signature data */
+	enum OID	key_algo;		/* Algorithm used by the cert's key */
+	enum OID	last_oid;		/* Last OID encountered */
+	enum OID	sig_algo;		/* Algorithm used to sign the cert */
+	u8		o_size;			/* Size of organizationName (O) */
+	u8		cn_size;		/* Size of commonName (CN) */
+	u8		email_size;		/* Size of emailAddress */
+	u16		o_offset;		/* Offset of organizationName (O) */
+	u16		cn_offset;		/* Offset of commonName (CN) */
+	u16		email_offset;		/* Offset of emailAddress */
+	unsigned	raw_akid_size;
+	const void	*raw_akid;		/* Raw authorityKeyId in ASN.1 */
+	const void	*akid_raw_issuer;	/* Raw directoryName in authorityKeyId */
+	unsigned	akid_raw_issuer_size;
+};
+
 /*
  * x509_cert_parser.c
  */
@@ -55,5 +81,6 @@ extern int x509_decode_time(time64_t *_t,  size_t hdrlen,
 /*
  * x509_public_key.c
  */
-extern int x509_get_sig_params(struct x509_certificate *cert);
+extern const char *oid_to_hash(enum OID oid);
+extern int x509_get_sig_params(struct x509_certificate *cert, struct x509_parse_context *parse);
 extern int x509_check_for_self_signed(struct x509_certificate *cert);
diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c
index 12e3341e806b..b2f8542accc4 100644
--- a/crypto/asymmetric_keys/x509_public_key.c
+++ b/crypto/asymmetric_keys/x509_public_key.c
@@ -17,11 +17,32 @@
 #include "asymmetric_keys.h"
 #include "x509_parser.h"
 
+/*
+ * Translate OIDs to hash algorithm names.
+ */
+const char *oid_to_hash(enum OID oid)
+{
+	switch (oid) {
+	case OID_sha1:			return "sha1";
+	case OID_sha256:		return "sha256";
+	case OID_sha384:		return "sha384";
+	case OID_sha512:		return "sha512";
+	case OID_sha224:		return "sha224";
+	case OID_sm3:			return "sm3";
+	case OID_gost2012Digest256:	return "streebog256";
+	case OID_gost2012Digest512:	return "streebog512";
+	case OID_sha3_256:		return "sha3-256";
+	case OID_sha3_384:		return "sha3-384";
+	case OID_sha3_512:		return "sha3-512";
+	default:			return NULL;
+	}
+}
+
 /*
  * Set up the signature parameters in an X.509 certificate.  This involves
  * digesting the signed data and extracting the signature.
  */
-int x509_get_sig_params(struct x509_certificate *cert)
+int x509_get_sig_params(struct x509_certificate *cert, struct x509_parse_context *parse)
 {
 	struct public_key_signature *sig = cert->sig;
 	struct crypto_shash *tfm;
@@ -31,11 +52,11 @@ int x509_get_sig_params(struct x509_certificate *cert)
 
 	pr_devel("==>%s()\n", __func__);
 
-	sig->s = kmemdup(cert->raw_sig, cert->raw_sig_size, GFP_KERNEL);
+	sig->s = kmemdup(parse->sig, parse->sig_size, GFP_KERNEL);
 	if (!sig->s)
 		return -ENOMEM;
 
-	sig->s_size = cert->raw_sig_size;
+	sig->s_size = parse->sig_size;
 
 	/* Allocate the hashing algorithm we're going to need and find out how
 	 * big the hash operational data will be.
@@ -43,6 +64,7 @@ int x509_get_sig_params(struct x509_certificate *cert)
 	tfm = crypto_alloc_shash(sig->hash_algo, 0, 0);
 	if (IS_ERR(tfm)) {
 		if (PTR_ERR(tfm) == -ENOENT) {
+			pr_debug("Unsupported hash %s\n", sig->hash_algo);
 			cert->unsupported_sig = true;
 			return 0;
 		}
diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h
index 30821a6a4f72..d546ea7999b9 100644
--- a/include/linux/oid_registry.h
+++ b/include/linux/oid_registry.h
@@ -31,6 +31,8 @@ enum OID {
 	/* PKCS#1 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1)} */
 	OID_rsaEncryption,		/* 1.2.840.113549.1.1.1 */
 	OID_sha1WithRSAEncryption,	/* 1.2.840.113549.1.1.5 */
+	OID_id_mgf1,			/* 1.2.840.113549.1.1.8 */
+	OID_id_rsassa_pss,		/* 1.2.840.113549.1.1.10 */
 	OID_sha256WithRSAEncryption,	/* 1.2.840.113549.1.1.11 */
 	OID_sha384WithRSAEncryption,	/* 1.2.840.113549.1.1.12 */
 	OID_sha512WithRSAEncryption,	/* 1.2.840.113549.1.1.13 */


  parent reply	other threads:[~2026-01-05 15:22 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-01-05 15:21 [PATCH v11 0/8] x509, pkcs7, crypto: Add ML-DSA and RSASSA-PSS signing David Howells
2026-01-05 15:21 ` [PATCH v11 1/8] crypto: Add ML-DSA crypto_sig support David Howells
2026-01-05 15:21 ` [PATCH v11 2/8] pkcs7: Allow the signing algo to calculate the digest itself David Howells
2026-01-05 20:19   ` Ignat Korchagin
2026-01-07 13:53     ` David Howells
2026-01-07 13:59       ` Ignat Korchagin
2026-01-08 12:59       ` David Howells
2026-01-05 15:21 ` [PATCH v11 3/8] pkcs7, x509: Add ML-DSA support David Howells
2026-01-06  8:02   ` Eric Biggers
2026-01-06  8:22     ` Eric Biggers
2026-01-06  9:37     ` David Howells
2026-01-08 14:38     ` David Howells
2026-01-05 15:21 ` [PATCH v11 4/8] modsign: Enable ML-DSA module signing David Howells
2026-01-06  8:10   ` Eric Biggers
2026-01-05 15:21 ` [PATCH v11 5/8] crypto: Add supplementary info param to asymmetric key signature verification David Howells
2026-01-07 14:23   ` Ignat Korchagin
2026-01-05 15:21 ` [PATCH v11 6/8] crypto: Add RSASSA-PSS support David Howells
2026-01-07 16:24   ` Ignat Korchagin
2026-01-08 11:29     ` David Howells
2026-01-08 13:15   ` Jarkko Sakkinen
2026-01-08 14:39     ` David Howells
2026-01-05 15:21 ` David Howells [this message]
2026-01-07 16:36   ` [PATCH v11 7/8] pkcs7, x509: " Ignat Korchagin
2026-01-08 11:53     ` David Howells
2026-01-05 15:21 ` [PATCH v11 8/8] modsign: Enable RSASSA-PSS module signing David Howells
2026-01-07 16:38   ` Ignat Korchagin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260105152145.1801972-8-dhowells@redhat.com \
    --to=dhowells@redhat.com \
    --cc=Jason@zx2c4.com \
    --cc=ardb@kernel.org \
    --cc=da.gomez@kernel.org \
    --cc=ebiggers@kernel.org \
    --cc=herbert@gondor.apana.org.au \
    --cc=ignat@cloudflare.com \
    --cc=jarkko@kernel.org \
    --cc=keyrings@vger.kernel.org \
    --cc=linux-crypto@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-modules@vger.kernel.org \
    --cc=lukas@wunner.de \
    --cc=mcgrof@kernel.org \
    --cc=petr.pavlu@suse.com \
    --cc=samitolvanen@google.com \
    --cc=smueller@chronox.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.