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