[PATCH v12 08/10] pkcs7, x509: Add RSASSA-PSS support

David Howells posted 10 patches 3 weeks, 2 days ago
There is a newer version of this series
[PATCH v12 08/10] pkcs7, x509: Add RSASSA-PSS support
Posted by David Howells 3 weeks, 2 days ago
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     | 114 +++++-----
 crypto/asymmetric_keys/public_key.c       |  10 +
 crypto/asymmetric_keys/rsassa_params.asn1 |  25 +++
 crypto/asymmetric_keys/rsassa_parser.c    | 240 ++++++++++++++++++++++
 crypto/asymmetric_keys/rsassa_parser.h    |  25 +++
 crypto/asymmetric_keys/x509.asn1          |   2 +-
 crypto/asymmetric_keys/x509_cert_parser.c | 100 ++++-----
 crypto/asymmetric_keys/x509_parser.h      |  45 +++-
 crypto/asymmetric_keys/x509_public_key.c  |  36 +++-
 include/linux/oid_registry.h              |   2 +
 13 files changed, 503 insertions(+), 122 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..47d3c1920e8f 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");
@@ -28,14 +29,16 @@ struct pkcs7_parse_context {
 	struct x509_certificate **ppcerts;
 	unsigned long	data;			/* Start of data */
 	enum OID	last_oid;		/* Last OID encountered */
-	unsigned	x509_index;
-	unsigned	sinfo_index;
+	unsigned int	x509_index;
+	unsigned int	sinfo_index;
+	unsigned int	algo_params_size;
+	const void	*algo_params;
 	const void	*raw_serial;
-	unsigned	raw_serial_size;
-	unsigned	raw_issuer_size;
+	unsigned int	raw_serial_size;
+	unsigned int	raw_issuer_size;
 	const void	*raw_issuer;
 	const void	*raw_skid;
-	unsigned	raw_skid_size;
+	unsigned int	raw_skid_size;
 	bool		expect_skid;
 };
 
@@ -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,11 +262,21 @@ 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:
+		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";
 		break;
 	case OID_id_ecdsa_with_sha1:
 	case OID_id_ecdsa_with_sha224:
@@ -289,33 +286,36 @@ 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;
 	}
+
+	ctx->algo_params = NULL;
+	ctx->algo_params_size = 0;
 	return 0;
 }
 
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..b80720fa94be
--- /dev/null
+++ b/crypto/asymmetric_keys/rsassa_parser.c
@@ -0,0 +1,240 @@
+// 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) "RSASSA-PSS: "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 = {};
+	long ret;
+
+	struct rsassa_parameters *rsassa __free(kfree) =
+		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)
+{
+	const char *mf, *mh;
+
+	if (!sig_params || !sig_params_size) {
+		pr_debug("sig algo without parameters\n");
+		return -EBADMSG;
+	}
+
+	struct rsassa_parameters *rsassa __free(rsassa_params_free) =
+		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..a4b848628e37 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
@@ -60,12 +39,11 @@ EXPORT_SYMBOL_GPL(x509_free_certificate);
  */
 struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
 {
-	struct x509_certificate *cert __free(x509_free_certificate) = NULL;
-	struct x509_parse_context *ctx __free(kfree) = NULL;
 	struct asymmetric_key_id *kid;
 	long ret;
 
-	cert = kzalloc(sizeof(struct x509_certificate), GFP_KERNEL);
+	struct x509_certificate *cert __free(x509_free_certificate) =
+		kzalloc(sizeof(struct x509_certificate), GFP_KERNEL);
 	if (!cert)
 		return ERR_PTR(-ENOMEM);
 	cert->pub = kzalloc(sizeof(struct public_key), GFP_KERNEL);
@@ -74,7 +52,9 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
 	cert->sig = kzalloc(sizeof(struct public_key_signature), GFP_KERNEL);
 	if (!cert->sig)
 		return ERR_PTR(-ENOMEM);
-	ctx = kzalloc(sizeof(struct x509_parse_context), GFP_KERNEL);
+
+	struct x509_parse_context *ctx __free(kfree) =
+		kzalloc(sizeof(struct x509_parse_context), GFP_KERNEL);
 	if (!ctx)
 		return ERR_PTR(-ENOMEM);
 
@@ -104,15 +84,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 +126,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 +159,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 +191,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 +252,19 @@ int x509_note_sig_algo(void *context, size_t hdrlen, unsigned char tag,
 		goto ml_dsa;
 	}
 
+rsassa_pss:
+	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 +321,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 +476,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 +504,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) {
+			ctx->key_params = ctx->algo_params;
+			ctx->key_params_size = ctx->algo_params_size;
+			ctx->algo_params = NULL;
+			ctx->algo_params_size = 0;
+
+			struct rsassa_parameters *params __free(rsassa_params_free) =
+				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 +563,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..578de49c37bc 100644
--- a/crypto/asymmetric_keys/x509_parser.h
+++ b/crypto/asymmetric_keys/x509_parser.h
@@ -22,18 +22,16 @@ struct x509_certificate {
 	time64_t	valid_from;
 	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 */
+	unsigned int	tbs_size;		/* Size of signed data */
 	const void	*raw_serial;		/* Raw serial number in ASN.1 */
-	unsigned	raw_serial_size;
-	unsigned	raw_issuer_size;
+	unsigned int	raw_serial_size;
+	unsigned int	raw_issuer_size;
 	const void	*raw_issuer;		/* Raw issuer name in ASN.1 */
 	const void	*raw_subject;		/* Raw subject name in ASN.1 */
-	unsigned	raw_subject_size;
-	unsigned	raw_skid_size;
+	unsigned int	raw_subject_size;
+	unsigned int	raw_skid_size;
 	const void	*raw_skid;		/* Raw subjectKeyId in ASN.1 */
-	unsigned	index;
+	unsigned int	index;
 	bool		seen;			/* Infinite recursion prevention */
 	bool		verified;
 	bool		self_signed;		/* T if self-signed (check unsupported_sig too) */
@@ -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 int	raw_akid_size;
+	const void	*raw_akid;		/* Raw authorityKeyId in ASN.1 */
+	const void	*akid_raw_issuer;	/* Raw directoryName in authorityKeyId */
+	unsigned int	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 2243add11d48..4490cfa368a3 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;
 		}
@@ -149,13 +171,12 @@ int x509_check_for_self_signed(struct x509_certificate *cert)
  */
 static int x509_key_preparse(struct key_preparsed_payload *prep)
 {
-	struct x509_certificate *cert __free(x509_free_certificate) = NULL;
-	struct asymmetric_key_ids *kids __free(kfree) = NULL;
 	char *p, *desc __free(kfree) = NULL;
 	const char *q;
 	size_t srlen, sulen;
 
-	cert = x509_cert_parse(prep->data, prep->datalen);
+	struct x509_certificate *cert __free(x509_free_certificate) =
+		x509_cert_parse(prep->data, prep->datalen);
 	if (IS_ERR(cert))
 		return PTR_ERR(cert);
 
@@ -198,7 +219,8 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
 	p = bin2hex(p, q, srlen);
 	*p = 0;
 
-	kids = kmalloc(sizeof(struct asymmetric_key_ids), GFP_KERNEL);
+	struct asymmetric_key_ids *kids __free(kfree) =
+		kmalloc(sizeof(struct asymmetric_key_ids), GFP_KERNEL);
 	if (!kids)
 		return -ENOMEM;
 	kids->id[0] = cert->id;
diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h
index ebce402854de..7fe168f54a6c 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 */
Re: [PATCH v12 08/10] pkcs7, x509: Add RSASSA-PSS support
Posted by Ignat Korchagin 2 weeks, 4 days ago
Hi David,

On Thu, Jan 15, 2026 at 9:52 PM David Howells <dhowells@redhat.com> wrote:
>
> 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     | 114 +++++-----
>  crypto/asymmetric_keys/public_key.c       |  10 +
>  crypto/asymmetric_keys/rsassa_params.asn1 |  25 +++
>  crypto/asymmetric_keys/rsassa_parser.c    | 240 ++++++++++++++++++++++
>  crypto/asymmetric_keys/rsassa_parser.h    |  25 +++
>  crypto/asymmetric_keys/x509.asn1          |   2 +-
>  crypto/asymmetric_keys/x509_cert_parser.c | 100 ++++-----
>  crypto/asymmetric_keys/x509_parser.h      |  45 +++-
>  crypto/asymmetric_keys/x509_public_key.c  |  36 +++-
>  include/linux/oid_registry.h              |   2 +
>  13 files changed, 503 insertions(+), 122 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..47d3c1920e8f 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");
> @@ -28,14 +29,16 @@ struct pkcs7_parse_context {
>         struct x509_certificate **ppcerts;
>         unsigned long   data;                   /* Start of data */
>         enum OID        last_oid;               /* Last OID encountered */
> -       unsigned        x509_index;
> -       unsigned        sinfo_index;
> +       unsigned int    x509_index;
> +       unsigned int    sinfo_index;
> +       unsigned int    algo_params_size;
> +       const void      *algo_params;
>         const void      *raw_serial;
> -       unsigned        raw_serial_size;
> -       unsigned        raw_issuer_size;
> +       unsigned int    raw_serial_size;
> +       unsigned int    raw_issuer_size;
>         const void      *raw_issuer;
>         const void      *raw_skid;
> -       unsigned        raw_skid_size;
> +       unsigned int    raw_skid_size;
>         bool            expect_skid;
>  };
>
> @@ -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,11 +262,21 @@ 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:
> +               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";
>                 break;
>         case OID_id_ecdsa_with_sha1:
>         case OID_id_ecdsa_with_sha224:
> @@ -289,33 +286,36 @@ 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;
>         }
> +
> +       ctx->algo_params = NULL;
> +       ctx->algo_params_size = 0;
>         return 0;
>  }
>
> 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..b80720fa94be
> --- /dev/null
> +++ b/crypto/asymmetric_keys/rsassa_parser.c
> @@ -0,0 +1,240 @@
> +// 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) "RSASSA-PSS: "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 = {};
> +       long ret;
> +
> +       struct rsassa_parameters *rsassa __free(kfree) =

Did you mean to use the newly added rsassa_params_free() here?

> +               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)
> +{
> +       const char *mf, *mh;
> +
> +       if (!sig_params || !sig_params_size) {
> +               pr_debug("sig algo without parameters\n");
> +               return -EBADMSG;
> +       }
> +
> +       struct rsassa_parameters *rsassa __free(rsassa_params_free) =
> +               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))

So you use plain kfree() in one instance and this custom free
definition in another. We should probably pick one. What is the idea
behind this custom rsassa_params_free(), if it just calls into
kfree()?

> 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..a4b848628e37 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
> @@ -60,12 +39,11 @@ EXPORT_SYMBOL_GPL(x509_free_certificate);
>   */
>  struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
>  {
> -       struct x509_certificate *cert __free(x509_free_certificate) = NULL;
> -       struct x509_parse_context *ctx __free(kfree) = NULL;

Thank you for fixing this.

>         struct asymmetric_key_id *kid;
>         long ret;
>
> -       cert = kzalloc(sizeof(struct x509_certificate), GFP_KERNEL);
> +       struct x509_certificate *cert __free(x509_free_certificate) =
> +               kzalloc(sizeof(struct x509_certificate), GFP_KERNEL);
>         if (!cert)
>                 return ERR_PTR(-ENOMEM);
>         cert->pub = kzalloc(sizeof(struct public_key), GFP_KERNEL);
> @@ -74,7 +52,9 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
>         cert->sig = kzalloc(sizeof(struct public_key_signature), GFP_KERNEL);
>         if (!cert->sig)
>                 return ERR_PTR(-ENOMEM);
> -       ctx = kzalloc(sizeof(struct x509_parse_context), GFP_KERNEL);
> +
> +       struct x509_parse_context *ctx __free(kfree) =
> +               kzalloc(sizeof(struct x509_parse_context), GFP_KERNEL);
>         if (!ctx)
>                 return ERR_PTR(-ENOMEM);
>
> @@ -104,15 +84,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 +126,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 +159,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 +191,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 +252,19 @@ int x509_note_sig_algo(void *context, size_t hdrlen, unsigned char tag,
>                 goto ml_dsa;
>         }
>
> +rsassa_pss:
> +       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 +321,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 +476,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 +504,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) {
> +                       ctx->key_params = ctx->algo_params;
> +                       ctx->key_params_size = ctx->algo_params_size;
> +                       ctx->algo_params = NULL;
> +                       ctx->algo_params_size = 0;
> +
> +                       struct rsassa_parameters *params __free(rsassa_params_free) =
> +                               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 +563,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..578de49c37bc 100644
> --- a/crypto/asymmetric_keys/x509_parser.h
> +++ b/crypto/asymmetric_keys/x509_parser.h
> @@ -22,18 +22,16 @@ struct x509_certificate {
>         time64_t        valid_from;
>         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 */
> +       unsigned int    tbs_size;               /* Size of signed data */
>         const void      *raw_serial;            /* Raw serial number in ASN.1 */
> -       unsigned        raw_serial_size;
> -       unsigned        raw_issuer_size;
> +       unsigned int    raw_serial_size;
> +       unsigned int    raw_issuer_size;
>         const void      *raw_issuer;            /* Raw issuer name in ASN.1 */
>         const void      *raw_subject;           /* Raw subject name in ASN.1 */
> -       unsigned        raw_subject_size;
> -       unsigned        raw_skid_size;
> +       unsigned int    raw_subject_size;
> +       unsigned int    raw_skid_size;
>         const void      *raw_skid;              /* Raw subjectKeyId in ASN.1 */
> -       unsigned        index;
> +       unsigned int    index;
>         bool            seen;                   /* Infinite recursion prevention */
>         bool            verified;
>         bool            self_signed;            /* T if self-signed (check unsupported_sig too) */
> @@ -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 int    raw_akid_size;
> +       const void      *raw_akid;              /* Raw authorityKeyId in ASN.1 */
> +       const void      *akid_raw_issuer;       /* Raw directoryName in authorityKeyId */
> +       unsigned int    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 2243add11d48..4490cfa368a3 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;
>                 }
> @@ -149,13 +171,12 @@ int x509_check_for_self_signed(struct x509_certificate *cert)
>   */
>  static int x509_key_preparse(struct key_preparsed_payload *prep)
>  {
> -       struct x509_certificate *cert __free(x509_free_certificate) = NULL;
> -       struct asymmetric_key_ids *kids __free(kfree) = NULL;
>         char *p, *desc __free(kfree) = NULL;
>         const char *q;
>         size_t srlen, sulen;
>
> -       cert = x509_cert_parse(prep->data, prep->datalen);
> +       struct x509_certificate *cert __free(x509_free_certificate) =
> +               x509_cert_parse(prep->data, prep->datalen);
>         if (IS_ERR(cert))
>                 return PTR_ERR(cert);
>
> @@ -198,7 +219,8 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
>         p = bin2hex(p, q, srlen);
>         *p = 0;
>
> -       kids = kmalloc(sizeof(struct asymmetric_key_ids), GFP_KERNEL);
> +       struct asymmetric_key_ids *kids __free(kfree) =
> +               kmalloc(sizeof(struct asymmetric_key_ids), GFP_KERNEL);
>         if (!kids)
>                 return -ENOMEM;
>         kids->id[0] = cert->id;
> diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h
> index ebce402854de..7fe168f54a6c 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 */
>

Ignat
Re: [PATCH v12 08/10] pkcs7, x509: Add RSASSA-PSS support
Posted by David Howells 2 weeks, 4 days ago
Ignat Korchagin <ignat@cloudflare.com> wrote:

> > +       struct rsassa_parameters *rsassa __free(kfree) =
> 
> Did you mean to use the newly added rsassa_params_free() here?

Err, yes, I should have done.

> > +DEFINE_FREE(rsassa_params_free,  struct rsassa_parameters*, rsassa_params_free(_T))
> 
> So you use plain kfree() in one instance and this custom free definition in
> another.  We should probably pick one.

Yeah, both should have used the custom one.

> What is the idea behind this custom rsassa_params_free(), if it just calls
> into kfree()?

In case in future something freeable is added to it, it seems like a good idea
to have a destructor[*].  Other mask functions are theoretically possible, for
example.

[*] (Since we seem to be reinventing C++ ;-)

David