Hi David,

On Mon, Jan 5, 2026 at 3:23 PM David Howells <[email protected]> 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 <[email protected]>
> cc: Lukas Wunner <[email protected]>
> cc: Ignat Korchagin <[email protected]>
> cc: Herbert Xu <[email protected]>
> cc: [email protected]
> cc: [email protected]
> ---
>  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;

I really don't like this. Is it possible to factor this out to a
separate function and just call here? Should the factored function
even be part of the implementation somehow?

>         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 ([email protected])
> + */
> +
> +#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);

Declare when initialised

> +       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;

Declare when initialised or just flip the two declarations

> +       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 ([email protected])
> + */
> +
> +#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];

I've seen this elsewhere in the crypto code (namely in ECC) but is it
generally a good idea to declare long buffers on the stack?

>                 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 */
>

Ignat

Reply via email to