An X.509 wrapper for a RSASSA-PSS signature contains additional
signature parameters over the PKCSv.15 encoding scheme. Extend the
x509 parser to allow parsing RSASSA-PSS encoded certificates, with
the defaults taken from RFC8017.

A certificate is rejected if the hash function used for the MGF is
different from the hash function used for signature generation,
although this is allowed in RFC8017.

References: https://tools.ietf.org/html/rfc8017#appendix-C
Signed-off-by: Varad Gautam <varad.gau...@suse.com>
---
 crypto/asymmetric_keys/Makefile           |   5 +-
 crypto/asymmetric_keys/x509_cert_parser.c | 152 ++++++++++++++++++++++
 crypto/asymmetric_keys/x509_rsassa.asn1   |  17 +++
 include/crypto/public_key.h               |   4 +
 include/linux/oid_registry.h              |   3 +
 5 files changed, 180 insertions(+), 1 deletion(-)
 create mode 100644 crypto/asymmetric_keys/x509_rsassa.asn1

diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index 28b91adba2ae..f79ed8e8ef8e 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -20,15 +20,18 @@ obj-$(CONFIG_X509_CERTIFICATE_PARSER) += x509_key_parser.o
 x509_key_parser-y := \
        x509.asn1.o \
        x509_akid.asn1.o \
+       x509_rsassa.asn1.o \
        x509_cert_parser.o \
        x509_public_key.o
 
 $(obj)/x509_cert_parser.o: \
        $(obj)/x509.asn1.h \
-       $(obj)/x509_akid.asn1.h
+       $(obj)/x509_akid.asn1.h \
+       $(obj)/x509_rsassa.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)/x509_rsassa.asn1.o: $(obj)/x509_rsassa.asn1.c $(obj)/x509_rsassa.asn1.h
 
 #
 # PKCS#8 private key handling
diff --git a/crypto/asymmetric_keys/x509_cert_parser.c 
b/crypto/asymmetric_keys/x509_cert_parser.c
index 52c9b455fc7d..3738355618cd 100644
--- a/crypto/asymmetric_keys/x509_cert_parser.c
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -15,6 +15,7 @@
 #include "x509_parser.h"
 #include "x509.asn1.h"
 #include "x509_akid.asn1.h"
+#include "x509_rsassa.asn1.h"
 
 struct x509_parse_context {
        struct x509_certificate *cert;          /* Certificate being 
constructed */
@@ -38,6 +39,8 @@ struct x509_parse_context {
        const void      *raw_akid;              /* Raw authorityKeyId in ASN.1 
*/
        const void      *akid_raw_issuer;       /* Raw directoryName in 
authorityKeyId */
        unsigned        akid_raw_issuer_size;
+       const void      *raw_sig_params;        /* Signature 
AlgorithmIdentifier.parameters */
+       unsigned        raw_sig_params_size;
 };
 
 /*
@@ -101,6 +104,15 @@ struct x509_certificate *x509_cert_parse(const void *data, 
size_t datalen)
                }
        }
 
+       if (strcmp(ctx->cert->sig->encoding, "pss") == 0) {
+               pr_devel("rsa enc=pss hash=%s mgf=%s mgf_hash=%s salt=0x%x 
tf=0x%x\n",
+                        ctx->cert->sig->hash_algo,
+                        ctx->cert->sig->mgf,
+                        ctx->cert->sig->mgf_hash_algo,
+                        ctx->cert->sig->salt_length,
+                        ctx->cert->sig->trailer_field);
+       }
+
        ret = -ENOMEM;
        cert->pub->key = kmemdup(ctx->key, ctx->key_size, GFP_KERNEL);
        if (!cert->pub->key)
@@ -194,6 +206,7 @@ int x509_note_pkey_algo(void *context, size_t hdrlen,
                        const void *value, size_t vlen)
 {
        struct x509_parse_context *ctx = context;
+       int ret = 0;
 
        pr_debug("PubKey Algo: %u\n", ctx->last_oid);
 
@@ -238,6 +251,39 @@ int x509_note_pkey_algo(void *context, size_t hdrlen,
        case OID_SM2_with_SM3:
                ctx->cert->sig->hash_algo = "sm3";
                goto sm2;
+
+       case OID_rsassaPSS:
+               /* For rsassaPSS, the hash algorithm is packed as a mandatory
+                * parameter in AlgorithmIdentifier.parameters.
+                */
+               if (ctx->raw_sig_params == NULL && ctx->raw_sig_params_size != 
1)
+                       return -EBADMSG;
+
+               ctx->cert->sig->pkey_algo = "rsa";
+               ctx->cert->sig->encoding = "pss";
+               ctx->algo_oid = ctx->last_oid;
+               if (ctx->raw_sig_params) {
+                       ret = asn1_ber_decoder(&x509_rsassa_decoder, ctx,
+                                              ctx->raw_sig_params,
+                                              ctx->raw_sig_params_size);
+                       if (ret < 0)
+                               return ret;
+               }
+
+               /* Fill in RSASSA-PSS-params defaults if left out. */
+               if (!ctx->cert->sig->hash_algo)
+                       ctx->cert->sig->hash_algo = "sha1";
+               if (!ctx->cert->sig->mgf)
+                       ctx->cert->sig->mgf = "mgf1";
+               if (!ctx->cert->sig->mgf_hash_algo)
+                       ctx->cert->sig->mgf_hash_algo = "sha1";
+               ctx->cert->sig->trailer_field = 0xbc;
+
+               /* Reject if digest and mgf use different hashalgs. */
+               if (strcmp(ctx->cert->sig->hash_algo, 
ctx->cert->sig->mgf_hash_algo) != 0)
+                       return -ENOPKG;
+
+               return 0;
        }
 
 rsa_pkcs1:
@@ -439,6 +485,18 @@ int x509_note_params(void *context, size_t hdrlen,
 {
        struct x509_parse_context *ctx = context;
 
+       if (ctx->last_oid == OID_rsassaPSS && !ctx->raw_sig_params) {
+               /* Stash AlgorithmIdentifier.parameters for RSASSA-PSS. */
+               ctx->raw_sig_params_size = vlen + hdrlen;
+               if (ctx->raw_sig_params_size) {
+                       ctx->raw_sig_params = value - hdrlen;
+               } else {
+                       ctx->raw_sig_params = NULL;
+                       ctx->raw_sig_params_size = 1;
+               }
+               return 0;
+       }
+
        /*
         * AlgorithmIdentifier is used three times in the x509, we should skip
         * first and ignore third, using second one which is after subject and
@@ -705,3 +763,97 @@ int x509_akid_note_serial(void *context, size_t hdrlen,
        ctx->cert->sig->auth_ids[0] = kid;
        return 0;
 }
+
+int x509_note_hash_algo(void *context, size_t hdrlen,
+                       unsigned char tag,
+                       const void *value, size_t vlen)
+{
+       struct x509_parse_context *ctx = context;
+       const char **ptr = NULL;
+
+       if (ctx->last_oid != OID_rsassaPSS)
+               return -EBADMSG;
+
+       if (ctx->cert->sig->mgf)
+               ptr = &ctx->cert->sig->mgf_hash_algo;
+       else
+               ptr = &ctx->cert->sig->hash_algo;
+
+       switch (look_up_OID(value, vlen)) {
+       case OID_sha224:
+               *ptr = "sha224";
+               break;
+       case OID_sha256:
+               *ptr = "sha256";
+               break;
+       case OID_sha384:
+               *ptr = "sha384";
+               break;
+       case OID_sha512:
+               *ptr = "sha512";
+               break;
+       case OID_sha1:
+       default:
+               *ptr = "sha1";
+               break;
+       }
+
+       return 0;
+}
+
+int x509_note_hash_algo_params(void *context, size_t hdrlen,
+                              unsigned char tag,
+                              const void *value, size_t vlen)
+{
+       return -EOPNOTSUPP;
+}
+
+int x509_note_mgf(void *context, size_t hdrlen,
+                 unsigned char tag,
+                 const void *value, size_t vlen)
+{
+       struct x509_parse_context *ctx = context;
+
+       if (ctx->last_oid != OID_rsassaPSS)
+               return -EBADMSG;
+
+       /* RFC8017 PKCS1MGFAlgorithms */
+       if (look_up_OID(value, vlen) != OID_mgf1)
+               return -EINVAL;
+
+       ctx->cert->sig->mgf = "mgf1";
+
+       return 0;
+}
+
+int x509_note_salt_length(void *context, size_t hdrlen,
+                         unsigned char tag,
+                         const void *value, size_t vlen)
+{
+       struct x509_parse_context *ctx = context;
+
+       if (ctx->last_oid != OID_rsassaPSS)
+               return -EBADMSG;
+
+       if (!value || !vlen || vlen > sizeof(ctx->cert->sig->salt_length))
+               return -EINVAL;
+
+       ctx->cert->sig->salt_length = (vlen == 2) ?
+               be16_to_cpu(*((u16 *) value)) : *((u8 *) value);
+
+       return 0;
+}
+
+int x509_note_trailer_field(void *context, size_t hdrlen,
+                           unsigned char tag,
+                           const void *value, size_t vlen)
+{
+       struct x509_parse_context *ctx = context;
+
+       if (ctx->last_oid != OID_rsassaPSS)
+               return -EBADMSG;
+
+       /* trailerField 0xbc per RFC8017 A.2.3 regardless of if
+        * specified. */
+       return 0;
+}
diff --git a/crypto/asymmetric_keys/x509_rsassa.asn1 
b/crypto/asymmetric_keys/x509_rsassa.asn1
new file mode 100644
index 000000000000..e524b978856d
--- /dev/null
+++ b/crypto/asymmetric_keys/x509_rsassa.asn1
@@ -0,0 +1,17 @@
+-- RFC8017
+RSASSA-PSS-params ::= SEQUENCE {
+       hashAlgorithm      [0] HashAlgorithm DEFAULT,
+       maskGenAlgorithm   [1] MaskGenAlgorithm DEFAULT,
+       saltLength         [2] INTEGER DEFAULT ({ x509_note_salt_length }),
+       trailerField       [3] INTEGER DEFAULT ({ x509_note_trailer_field })
+}
+
+HashAlgorithm ::= SEQUENCE {
+       algorithm               OBJECT IDENTIFIER ({ x509_note_hash_algo }),
+       parameters              ANY OPTIONAL ({ x509_note_hash_algo_params })
+}
+
+MaskGenAlgorithm ::= SEQUENCE {
+       mgf     OBJECT IDENTIFIER ({ x509_note_mgf }),
+       parameters      HashAlgorithm
+}
diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
index 47accec68cb0..f36834c8bb13 100644
--- a/include/crypto/public_key.h
+++ b/include/crypto/public_key.h
@@ -46,6 +46,10 @@ struct public_key_signature {
        const char *encoding;
        const void *data;
        unsigned int data_size;
+       const char *mgf;
+       const char *mgf_hash_algo;
+       u16 salt_length;
+       u16 trailer_field;
 };
 
 extern void public_key_signature_free(struct public_key_signature *sig);
diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h
index 4462ed2c18cd..c247adc8a41e 100644
--- a/include/linux/oid_registry.h
+++ b/include/linux/oid_registry.h
@@ -113,6 +113,9 @@ enum OID {
        OID_SM2_with_SM3,               /* 1.2.156.10197.1.501 */
        OID_sm3WithRSAEncryption,       /* 1.2.156.10197.1.504 */
 
+       OID_mgf1,                       /* 1.2.840.113549.1.1.8 */
+       OID_rsassaPSS,                  /* 1.2.840.113549.1.1.10 */
+
        OID__NR
 };
 
-- 
2.30.2

Reply via email to