[PATCH 01/18] X.509: Parse RSASSA-PSS style certificates
From: Varad Gautam
Date: Tue Mar 30 2021 - 16:30:37 EST
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.gautam@xxxxxxxx>
---
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