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; 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); + long ret; + + rsassa = kzalloc(sizeof(*rsassa), GFP_KERNEL); + if (!rsassa) + return ERR_PTR(-ENOMEM); + + ctx.rsassa = rsassa; + ctx.data = (unsigned long)data; + + /* Attempt to decode the parameters */ + ret = asn1_ber_decoder(&rsassa_params_decoder, &ctx, data, datalen); + if (ret < 0) { + pr_debug("RSASSA parse failed %ld\n", ret); + return ERR_PTR(ret); + } + + return no_free_ptr(rsassa); +} + +/* + * Note an OID when we find one for later processing when we know how + * to interpret it. + */ +int rsassa_note_OID(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct rsassa_parse_context *ctx = context; + + ctx->last_oid = look_up_OID(value, vlen); + if (ctx->last_oid == OID__NR) { + char buffer[56]; + sprint_oid(value, vlen, buffer, sizeof(buffer)); + pr_debug("Unknown OID: %s\n", buffer); + } + return 0; +} + +/* + * Parse trailerField. We only accept trailerFieldBC. +*/ +int rsassa_note_trailer(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + if (vlen != 1 || *(u8 *)value != 0x01) { + pr_debug("Unknown trailerField\n"); + return -EINVAL; + } + return 0; +} + +int rsassa_note_hash_algo(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct rsassa_parse_context *ctx = context; + + ctx->rsassa->hash_algo = ctx->last_oid; + pr_debug("HASH-ALGO %u %u\n", ctx->rsassa->hash_algo, ctx->params_len); + ctx->params = NULL; + return 0; +} + +int rsassa_note_maskgen_algo(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct rsassa_parse_context *ctx = context; + int ret; + + ctx->rsassa->maskgen_algo = ctx->last_oid; + pr_debug("MGF-ALGO %u %u\n", ctx->rsassa->maskgen_algo, ctx->params_len); + + switch (ctx->rsassa->maskgen_algo) { + case OID_id_mgf1: + if (!vlen) { + pr_debug("MGF1 missing parameters\n"); + return -EBADMSG; + } + + ret = asn1_ber_decoder(&mgf1_params_decoder, ctx, + ctx->params, ctx->params_len); + if (ret < 0) { + pr_debug("MGF1 parse failed %d\n", ret); + return ret; + } + ctx->rsassa->maskgen_hash = ctx->mgf1_last_oid; + break; + + default: + pr_debug("Unsupported MaskGenAlgorithm %d\n", ret); + return -ENOPKG; + } + + ctx->params = NULL; + return 0; +} + +int rsassa_note_salt_length(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct rsassa_parse_context *ctx = context; + u32 salt_len = 0; + + if (!vlen) { + pr_debug("Salt len bad integer\n"); + return -EBADMSG; + } + if (vlen > 4) { + pr_debug("Salt len too long %zu\n", vlen); + return -EBADMSG; + } + if (((u8 *)value)[0] & 0x80) { + pr_debug("Salt len negative\n"); + return -EBADMSG; + } + + for (size_t i = 0; i < vlen; i++) { + salt_len <<= 8; + salt_len |= ((u8 *)value)[i]; + } + + ctx->rsassa->salt_len = salt_len; + pr_debug("Salt-Len %u\n", salt_len); + return 0; +} + +/* + * Extract arbitrary parameters. + */ +int rsassa_note_params(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct rsassa_parse_context *ctx = context; + + ctx->params = value - hdrlen; + ctx->params_len = vlen + hdrlen; + return 0; +} + +/* + * Note an OID when we find one for later processing when we know how to + * interpret it. + */ +int mgf1_note_OID(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct rsassa_parse_context *ctx = context; + + ctx->mgf1_last_oid = look_up_OID(value, vlen); + if (ctx->mgf1_last_oid == OID__NR) { + char buffer[56]; + sprint_oid(value, vlen, buffer, sizeof(buffer)); + pr_debug("Unknown MGF1 OID: %s\n", buffer); + } + return 0; +} + +/* + * Parse the signature parameter block and generate a suitable info string from + * it. + */ +int rsassa_parse_sig_params(struct public_key_signature *sig, + const u8 *sig_params, unsigned int sig_params_size) +{ + struct rsassa_parameters *rsassa __free(rsassa_params_free) = NULL; + const char *mf, *mh; + + rsassa = rsassa_params_parse(sig_params, sig_params_size); + if (IS_ERR(rsassa)) + return PTR_ERR(rsassa); + + sig->hash_algo = oid_to_hash(rsassa->hash_algo); + if (!sig->hash_algo) { + pr_notice("Unsupported hash: %u\n", rsassa->hash_algo); + return -ENOPKG; + } + + switch (rsassa->maskgen_algo) { + case OID_id_mgf1: + mf = "mgf1"; + break; + default: + pr_notice("Unsupported maskgen algo: %u\n", rsassa->maskgen_algo); + return -ENOPKG; + } + + mh = oid_to_hash(rsassa->maskgen_hash); + if (!mh) { + pr_notice("Unsupported MGF1 hash: %u\n", rsassa->maskgen_hash); + return -ENOPKG; + } + + sig->info = kasprintf(GFP_KERNEL, "sighash=%s pss_mask=%s,%s pss_salt=%u", + sig->hash_algo, mf, mh, rsassa->salt_len); + if (!sig->info) + return -ENOMEM; + pr_debug("Info string: %s\n", sig->info); + return 0; +} diff --git a/crypto/asymmetric_keys/rsassa_parser.h b/crypto/asymmetric_keys/rsassa_parser.h new file mode 100644 index 000000000000..b80401a3de8f --- /dev/null +++ b/crypto/asymmetric_keys/rsassa_parser.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* RSASSA-PSS parameter parsing context + * + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. + * Written by David Howells ([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]; 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 */
