lib: crypto: add x509 parser
authorAKASHI Takahiro <takahiro.akashi@linaro.org>
Wed, 13 Nov 2019 00:45:00 +0000 (09:45 +0900)
committerTom Rini <trini@konsulko.com>
Fri, 6 Dec 2019 21:44:20 +0000 (16:44 -0500)
Imported from linux kernel v5.3:
 x509.asn1 without changes
 x509_akid.asn1 without changes
 x509_parser.h without changes
 x509_cert_parser.c with changes marked as __UBOOT__
 x509_public_key.c with changes marked as __UBOOT__

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
lib/Kconfig
lib/Makefile
lib/crypto/Kconfig
lib/crypto/Makefile
lib/crypto/x509.asn1 [new file with mode: 0644]
lib/crypto/x509_akid.asn1 [new file with mode: 0644]
lib/crypto/x509_cert_parser.c [new file with mode: 0644]
lib/crypto/x509_parser.h [new file with mode: 0644]
lib/crypto/x509_public_key.c [new file with mode: 0644]

index 4d06f7e74fb154a61b42b80d0087fec3716ba2ff..965cf7bc039c80ae0969351c392d2efa272abdb7 100644 (file)
@@ -265,6 +265,7 @@ config AES
          present.
 
 source lib/rsa/Kconfig
+source lib/crypto/Kconfig
 
 config TPM
        bool "Trusted Platform Module (TPM) Support"
index d5bf1951ff55904e94d64d3656c8714b7b0a2448..1fb650cd90d1f34cab9eed6bba1e5798a4b3daf5 100644 (file)
@@ -18,6 +18,7 @@ obj-$(CONFIG_CMD_DHRYSTONE) += dhry/
 obj-$(CONFIG_ARCH_AT91) += at91/
 obj-$(CONFIG_OPTEE) += optee/
 obj-$(CONFIG_ASN1_DECODER) += asn1_decoder.o
+obj-y += crypto/
 
 obj-$(CONFIG_AES) += aes.o
 
index 9572ea8c87f33c230d47dd061d692cbfcdb2668c..aeb599c593acb623b89977f7ac6ab23eef0502d6 100644 (file)
@@ -27,4 +27,16 @@ config RSA_PUBLIC_KEY_PARSER
          public key data and provides the ability to instantiate a public
          key.
 
+config X509_CERTIFICATE_PARSER
+       bool "X.509 certificate parser"
+       depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+       select ASN1_DECODER
+       select ASN1_COMPILER
+       select OID_REGISTRY
+       select LIB_DATE
+       help
+         This option provides support for parsing X.509 format blobs for key
+         data and provides the ability to instantiate a crypto key from a
+         public key packet found inside the certificate.
+
 endif # ASYMMETRIC_KEY_TYPE
index 69330a9ebd2716f99a9c4ccae692fb4eab87e5eb..d7e27be568a1e941bec67e8688a319f4d52bd838 100644 (file)
@@ -19,3 +19,20 @@ rsa_public_key-y := \
 
 $(obj)/rsapubkey.asn1.o: $(obj)/rsapubkey.asn1.c $(obj)/rsapubkey.asn1.h
 $(obj)/rsa_helper.o: $(obj)/rsapubkey.asn1.h
+
+#
+# X.509 Certificate handling
+#
+obj-$(CONFIG_X509_CERTIFICATE_PARSER) += x509_key_parser.o
+x509_key_parser-y := \
+       x509.asn1.o \
+       x509_akid.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.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
diff --git a/lib/crypto/x509.asn1 b/lib/crypto/x509.asn1
new file mode 100644 (file)
index 0000000..5c9f4e4
--- /dev/null
@@ -0,0 +1,60 @@
+Certificate ::= SEQUENCE {
+       tbsCertificate          TBSCertificate ({ x509_note_tbs_certificate }),
+       signatureAlgorithm      AlgorithmIdentifier,
+       signature               BIT STRING ({ x509_note_signature })
+       }
+
+TBSCertificate ::= SEQUENCE {
+       version           [ 0 ] Version DEFAULT,
+       serialNumber            CertificateSerialNumber ({ x509_note_serial }),
+       signature               AlgorithmIdentifier ({ x509_note_pkey_algo }),
+       issuer                  Name ({ x509_note_issuer }),
+       validity                Validity,
+       subject                 Name ({ x509_note_subject }),
+       subjectPublicKeyInfo    SubjectPublicKeyInfo,
+       issuerUniqueID    [ 1 ] IMPLICIT UniqueIdentifier OPTIONAL,
+       subjectUniqueID   [ 2 ] IMPLICIT UniqueIdentifier OPTIONAL,
+       extensions        [ 3 ] Extensions OPTIONAL
+       }
+
+Version ::= INTEGER
+CertificateSerialNumber ::= INTEGER
+
+AlgorithmIdentifier ::= SEQUENCE {
+       algorithm               OBJECT IDENTIFIER ({ x509_note_OID }),
+       parameters              ANY OPTIONAL ({ x509_note_params })
+}
+
+Name ::= SEQUENCE OF RelativeDistinguishedName
+
+RelativeDistinguishedName ::= SET OF AttributeValueAssertion
+
+AttributeValueAssertion ::= SEQUENCE {
+       attributeType           OBJECT IDENTIFIER ({ x509_note_OID }),
+       attributeValue          ANY ({ x509_extract_name_segment })
+       }
+
+Validity ::= SEQUENCE {
+       notBefore               Time ({ x509_note_not_before }),
+       notAfter                Time ({ x509_note_not_after })
+       }
+
+Time ::= CHOICE {
+       utcTime                 UTCTime,
+       generalTime             GeneralizedTime
+       }
+
+SubjectPublicKeyInfo ::= SEQUENCE {
+       algorithm               AlgorithmIdentifier,
+       subjectPublicKey        BIT STRING ({ x509_extract_key_data })
+       }
+
+UniqueIdentifier ::= BIT STRING
+
+Extensions ::= SEQUENCE OF Extension
+
+Extension ::= SEQUENCE {
+       extnid                  OBJECT IDENTIFIER ({ x509_note_OID }),
+       critical                BOOLEAN DEFAULT,
+       extnValue               OCTET STRING ({ x509_process_extension })
+       }
diff --git a/lib/crypto/x509_akid.asn1 b/lib/crypto/x509_akid.asn1
new file mode 100644 (file)
index 0000000..1a33231
--- /dev/null
@@ -0,0 +1,35 @@
+-- X.509 AuthorityKeyIdentifier
+-- rfc5280 section 4.2.1.1
+
+AuthorityKeyIdentifier ::= SEQUENCE {
+       keyIdentifier                   [0] IMPLICIT KeyIdentifier              OPTIONAL,
+       authorityCertIssuer             [1] IMPLICIT GeneralNames               OPTIONAL,
+       authorityCertSerialNumber       [2] IMPLICIT CertificateSerialNumber    OPTIONAL
+       }
+
+KeyIdentifier ::= OCTET STRING ({ x509_akid_note_kid })
+
+CertificateSerialNumber ::= INTEGER ({ x509_akid_note_serial })
+
+GeneralNames ::= SEQUENCE OF GeneralName
+
+GeneralName ::= CHOICE {
+       otherName                       [0] ANY,
+       rfc822Name                      [1] IA5String,
+       dNSName                         [2] IA5String,
+       x400Address                     [3] ANY,
+       directoryName                   [4] Name ({ x509_akid_note_name }),
+       ediPartyName                    [5] ANY,
+       uniformResourceIdentifier       [6] IA5String,
+       iPAddress                       [7] OCTET STRING,
+       registeredID                    [8] OBJECT IDENTIFIER
+       }
+
+Name ::= SEQUENCE OF RelativeDistinguishedName
+
+RelativeDistinguishedName ::= SET OF AttributeValueAssertion
+
+AttributeValueAssertion ::= SEQUENCE {
+       attributeType           OBJECT IDENTIFIER ({ x509_note_OID }),
+       attributeValue          ANY ({ x509_extract_name_segment })
+       }
diff --git a/lib/crypto/x509_cert_parser.c b/lib/crypto/x509_cert_parser.c
new file mode 100644 (file)
index 0000000..e6d2a42
--- /dev/null
@@ -0,0 +1,697 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* X.509 certificate parser
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#define pr_fmt(fmt) "X.509: "fmt
+#include <linux/kernel.h>
+#ifndef __UBOOT__
+#include <linux/export.h>
+#include <linux/slab.h>
+#endif
+#include <linux/err.h>
+#include <linux/oid_registry.h>
+#ifdef __UBOOT__
+#include <linux/string.h>
+#endif
+#include <crypto/public_key.h>
+#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      *cert_start;            /* Start of cert content */
+       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;               /* Public key algorithm */
+       enum OID        last_oid;               /* Last OID encountered */
+       enum OID        algo_oid;               /* Algorithm OID */
+       unsigned char   nr_mpi;                 /* Number of MPIs stored */
+       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;
+};
+
+/*
+ * Free an X.509 certificate
+ */
+void x509_free_certificate(struct x509_certificate *cert)
+{
+       if (cert) {
+               public_key_free(cert->pub);
+               public_key_signature_free(cert->sig);
+               kfree(cert->issuer);
+               kfree(cert->subject);
+               kfree(cert->id);
+               kfree(cert->skid);
+               kfree(cert);
+       }
+}
+EXPORT_SYMBOL_GPL(x509_free_certificate);
+
+/*
+ * Parse an X.509 certificate
+ */
+struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
+{
+       struct x509_certificate *cert;
+       struct x509_parse_context *ctx;
+       struct asymmetric_key_id *kid;
+       long ret;
+
+       ret = -ENOMEM;
+       cert = kzalloc(sizeof(struct x509_certificate), GFP_KERNEL);
+       if (!cert)
+               goto error_no_cert;
+       cert->pub = kzalloc(sizeof(struct public_key), GFP_KERNEL);
+       if (!cert->pub)
+               goto error_no_ctx;
+       cert->sig = kzalloc(sizeof(struct public_key_signature), GFP_KERNEL);
+       if (!cert->sig)
+               goto error_no_ctx;
+       ctx = kzalloc(sizeof(struct x509_parse_context), GFP_KERNEL);
+       if (!ctx)
+               goto error_no_ctx;
+
+       ctx->cert = cert;
+       ctx->data = (unsigned long)data;
+
+       /* Attempt to decode the certificate */
+       ret = asn1_ber_decoder(&x509_decoder, ctx, data, datalen);
+       if (ret < 0)
+               goto error_decode;
+
+       /* Decode the AuthorityKeyIdentifier */
+       if (ctx->raw_akid) {
+               pr_devel("AKID: %u %*phN\n",
+                        ctx->raw_akid_size, ctx->raw_akid_size, ctx->raw_akid);
+               ret = asn1_ber_decoder(&x509_akid_decoder, ctx,
+                                      ctx->raw_akid, ctx->raw_akid_size);
+               if (ret < 0) {
+                       pr_warn("Couldn't decode AuthKeyIdentifier\n");
+                       goto error_decode;
+               }
+       }
+
+       ret = -ENOMEM;
+       cert->pub->key = kmemdup(ctx->key, ctx->key_size, GFP_KERNEL);
+       if (!cert->pub->key)
+               goto error_decode;
+
+       cert->pub->keylen = ctx->key_size;
+
+       cert->pub->params = kmemdup(ctx->params, ctx->params_size, GFP_KERNEL);
+       if (!cert->pub->params)
+               goto error_decode;
+
+       cert->pub->paramlen = ctx->params_size;
+       cert->pub->algo = ctx->key_algo;
+
+       /* Grab the signature bits */
+       ret = x509_get_sig_params(cert);
+       if (ret < 0)
+               goto error_decode;
+
+       /* Generate cert issuer + serial number key ID */
+       kid = asymmetric_key_generate_id(cert->raw_serial,
+                                        cert->raw_serial_size,
+                                        cert->raw_issuer,
+                                        cert->raw_issuer_size);
+       if (IS_ERR(kid)) {
+               ret = PTR_ERR(kid);
+               goto error_decode;
+       }
+       cert->id = kid;
+
+#ifndef __UBOOT__
+       /* Detect self-signed certificates */
+       ret = x509_check_for_self_signed(cert);
+       if (ret < 0)
+               goto error_decode;
+#endif
+
+       kfree(ctx);
+       return cert;
+
+error_decode:
+       kfree(ctx);
+error_no_ctx:
+       x509_free_certificate(cert);
+error_no_cert:
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(x509_cert_parse);
+
+/*
+ * Note an OID when we find one for later processing when we know how
+ * to interpret it.
+ */
+int x509_note_OID(void *context, size_t hdrlen,
+            unsigned char tag,
+            const void *value, size_t vlen)
+{
+       struct x509_parse_context *ctx = context;
+
+       ctx->last_oid = look_up_OID(value, vlen);
+       if (ctx->last_oid == OID__NR) {
+               char buffer[50];
+               sprint_oid(value, vlen, buffer, sizeof(buffer));
+               pr_debug("Unknown OID: [%lu] %s\n",
+                        (unsigned long)value - ctx->data, buffer);
+       }
+       return 0;
+}
+
+/*
+ * Save the position of the TBS data so that we can check the signature over it
+ * later.
+ */
+int x509_note_tbs_certificate(void *context, size_t hdrlen,
+                             unsigned char tag,
+                             const void *value, size_t vlen)
+{
+       struct x509_parse_context *ctx = context;
+
+       pr_debug("x509_note_tbs_certificate(,%zu,%02x,%ld,%zu)!\n",
+                hdrlen, tag, (unsigned long)value - ctx->data, vlen);
+
+       ctx->cert->tbs = value - hdrlen;
+       ctx->cert->tbs_size = vlen + hdrlen;
+       return 0;
+}
+
+/*
+ * Record the public key algorithm
+ */
+int x509_note_pkey_algo(void *context, size_t hdrlen,
+                       unsigned char tag,
+                       const void *value, size_t vlen)
+{
+       struct x509_parse_context *ctx = context;
+
+       pr_debug("PubKey Algo: %u\n", ctx->last_oid);
+
+       switch (ctx->last_oid) {
+       case OID_md2WithRSAEncryption:
+       case OID_md3WithRSAEncryption:
+       default:
+               return -ENOPKG; /* Unsupported combination */
+
+       case OID_md4WithRSAEncryption:
+               ctx->cert->sig->hash_algo = "md4";
+               goto rsa_pkcs1;
+
+       case OID_sha1WithRSAEncryption:
+               ctx->cert->sig->hash_algo = "sha1";
+               goto rsa_pkcs1;
+
+       case OID_sha256WithRSAEncryption:
+               ctx->cert->sig->hash_algo = "sha256";
+               goto rsa_pkcs1;
+
+       case OID_sha384WithRSAEncryption:
+               ctx->cert->sig->hash_algo = "sha384";
+               goto rsa_pkcs1;
+
+       case OID_sha512WithRSAEncryption:
+               ctx->cert->sig->hash_algo = "sha512";
+               goto rsa_pkcs1;
+
+       case OID_sha224WithRSAEncryption:
+               ctx->cert->sig->hash_algo = "sha224";
+               goto rsa_pkcs1;
+
+       case OID_gost2012Signature256:
+               ctx->cert->sig->hash_algo = "streebog256";
+               goto ecrdsa;
+
+       case OID_gost2012Signature512:
+               ctx->cert->sig->hash_algo = "streebog512";
+               goto ecrdsa;
+       }
+
+rsa_pkcs1:
+       ctx->cert->sig->pkey_algo = "rsa";
+       ctx->cert->sig->encoding = "pkcs1";
+       ctx->algo_oid = ctx->last_oid;
+       return 0;
+ecrdsa:
+       ctx->cert->sig->pkey_algo = "ecrdsa";
+       ctx->cert->sig->encoding = "raw";
+       ctx->algo_oid = ctx->last_oid;
+       return 0;
+}
+
+/*
+ * Note the whereabouts and type of the signature.
+ */
+int x509_note_signature(void *context, size_t hdrlen,
+                       unsigned char tag,
+                       const void *value, size_t vlen)
+{
+       struct x509_parse_context *ctx = context;
+
+       pr_debug("Signature type: %u size %zu\n", ctx->last_oid, vlen);
+
+       if (ctx->last_oid != ctx->algo_oid) {
+               pr_warn("Got cert with pkey (%u) and sig (%u) algorithm OIDs\n",
+                       ctx->algo_oid, ctx->last_oid);
+               return -EINVAL;
+       }
+
+       if (strcmp(ctx->cert->sig->pkey_algo, "rsa") == 0 ||
+           strcmp(ctx->cert->sig->pkey_algo, "ecrdsa") == 0) {
+               /* Discard the BIT STRING metadata */
+               if (vlen < 1 || *(const u8 *)value != 0)
+                       return -EBADMSG;
+
+               value++;
+               vlen--;
+       }
+
+       ctx->cert->raw_sig = value;
+       ctx->cert->raw_sig_size = vlen;
+       return 0;
+}
+
+/*
+ * Note the certificate serial number
+ */
+int x509_note_serial(void *context, size_t hdrlen,
+                    unsigned char tag,
+                    const void *value, size_t vlen)
+{
+       struct x509_parse_context *ctx = context;
+       ctx->cert->raw_serial = value;
+       ctx->cert->raw_serial_size = vlen;
+       return 0;
+}
+
+/*
+ * Note some of the name segments from which we'll fabricate a name.
+ */
+int x509_extract_name_segment(void *context, size_t hdrlen,
+                             unsigned char tag,
+                             const void *value, size_t vlen)
+{
+       struct x509_parse_context *ctx = context;
+
+       switch (ctx->last_oid) {
+       case OID_commonName:
+               ctx->cn_size = vlen;
+               ctx->cn_offset = (unsigned long)value - ctx->data;
+               break;
+       case OID_organizationName:
+               ctx->o_size = vlen;
+               ctx->o_offset = (unsigned long)value - ctx->data;
+               break;
+       case OID_email_address:
+               ctx->email_size = vlen;
+               ctx->email_offset = (unsigned long)value - ctx->data;
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+/*
+ * Fabricate and save the issuer and subject names
+ */
+static int x509_fabricate_name(struct x509_parse_context *ctx, size_t hdrlen,
+                              unsigned char tag,
+                              char **_name, size_t vlen)
+{
+       const void *name, *data = (const void *)ctx->data;
+       size_t namesize;
+       char *buffer;
+
+       if (*_name)
+               return -EINVAL;
+
+       /* Empty name string if no material */
+       if (!ctx->cn_size && !ctx->o_size && !ctx->email_size) {
+               buffer = kmalloc(1, GFP_KERNEL);
+               if (!buffer)
+                       return -ENOMEM;
+               buffer[0] = 0;
+               goto done;
+       }
+
+       if (ctx->cn_size && ctx->o_size) {
+               /* Consider combining O and CN, but use only the CN if it is
+                * prefixed by the O, or a significant portion thereof.
+                */
+               namesize = ctx->cn_size;
+               name = data + ctx->cn_offset;
+               if (ctx->cn_size >= ctx->o_size &&
+                   memcmp(data + ctx->cn_offset, data + ctx->o_offset,
+                          ctx->o_size) == 0)
+                       goto single_component;
+               if (ctx->cn_size >= 7 &&
+                   ctx->o_size >= 7 &&
+                   memcmp(data + ctx->cn_offset, data + ctx->o_offset, 7) == 0)
+                       goto single_component;
+
+               buffer = kmalloc(ctx->o_size + 2 + ctx->cn_size + 1,
+                                GFP_KERNEL);
+               if (!buffer)
+                       return -ENOMEM;
+
+               memcpy(buffer,
+                      data + ctx->o_offset, ctx->o_size);
+               buffer[ctx->o_size + 0] = ':';
+               buffer[ctx->o_size + 1] = ' ';
+               memcpy(buffer + ctx->o_size + 2,
+                      data + ctx->cn_offset, ctx->cn_size);
+               buffer[ctx->o_size + 2 + ctx->cn_size] = 0;
+               goto done;
+
+       } else if (ctx->cn_size) {
+               namesize = ctx->cn_size;
+               name = data + ctx->cn_offset;
+       } else if (ctx->o_size) {
+               namesize = ctx->o_size;
+               name = data + ctx->o_offset;
+       } else {
+               namesize = ctx->email_size;
+               name = data + ctx->email_offset;
+       }
+
+single_component:
+       buffer = kmalloc(namesize + 1, GFP_KERNEL);
+       if (!buffer)
+               return -ENOMEM;
+       memcpy(buffer, name, namesize);
+       buffer[namesize] = 0;
+
+done:
+       *_name = buffer;
+       ctx->cn_size = 0;
+       ctx->o_size = 0;
+       ctx->email_size = 0;
+       return 0;
+}
+
+int x509_note_issuer(void *context, size_t hdrlen,
+                    unsigned char tag,
+                    const void *value, size_t vlen)
+{
+       struct x509_parse_context *ctx = context;
+       ctx->cert->raw_issuer = value;
+       ctx->cert->raw_issuer_size = vlen;
+       return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->issuer, vlen);
+}
+
+int x509_note_subject(void *context, size_t hdrlen,
+                     unsigned char tag,
+                     const void *value, size_t vlen)
+{
+       struct x509_parse_context *ctx = context;
+       ctx->cert->raw_subject = value;
+       ctx->cert->raw_subject_size = vlen;
+       return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->subject, vlen);
+}
+
+/*
+ * Extract the parameters for the public key
+ */
+int x509_note_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;
+       return 0;
+}
+
+/*
+ * Extract the data for the public key algorithm
+ */
+int x509_extract_key_data(void *context, size_t hdrlen,
+                         unsigned char tag,
+                         const void *value, size_t vlen)
+{
+       struct x509_parse_context *ctx = context;
+
+       ctx->key_algo = ctx->last_oid;
+       if (ctx->last_oid == OID_rsaEncryption)
+               ctx->cert->pub->pkey_algo = "rsa";
+       else if (ctx->last_oid == OID_gost2012PKey256 ||
+                ctx->last_oid == OID_gost2012PKey512)
+               ctx->cert->pub->pkey_algo = "ecrdsa";
+       else
+               return -ENOPKG;
+
+       /* Discard the BIT STRING metadata */
+       if (vlen < 1 || *(const u8 *)value != 0)
+               return -EBADMSG;
+       ctx->key = value + 1;
+       ctx->key_size = vlen - 1;
+       return 0;
+}
+
+/* The keyIdentifier in AuthorityKeyIdentifier SEQUENCE is tag(CONT,PRIM,0) */
+#define SEQ_TAG_KEYID (ASN1_CONT << 6)
+
+/*
+ * Process certificate extensions that are used to qualify the certificate.
+ */
+int x509_process_extension(void *context, size_t hdrlen,
+                          unsigned char tag,
+                          const void *value, size_t vlen)
+{
+       struct x509_parse_context *ctx = context;
+       struct asymmetric_key_id *kid;
+       const unsigned char *v = value;
+
+       pr_debug("Extension: %u\n", ctx->last_oid);
+
+       if (ctx->last_oid == OID_subjectKeyIdentifier) {
+               /* Get hold of the key fingerprint */
+               if (ctx->cert->skid || vlen < 3)
+                       return -EBADMSG;
+               if (v[0] != ASN1_OTS || v[1] != vlen - 2)
+                       return -EBADMSG;
+               v += 2;
+               vlen -= 2;
+
+               ctx->cert->raw_skid_size = vlen;
+               ctx->cert->raw_skid = v;
+               kid = asymmetric_key_generate_id(v, vlen, "", 0);
+               if (IS_ERR(kid))
+                       return PTR_ERR(kid);
+               ctx->cert->skid = kid;
+               pr_debug("subjkeyid %*phN\n", kid->len, kid->data);
+               return 0;
+       }
+
+       if (ctx->last_oid == OID_authorityKeyIdentifier) {
+               /* Get hold of the CA key fingerprint */
+               ctx->raw_akid = v;
+               ctx->raw_akid_size = vlen;
+               return 0;
+       }
+
+       return 0;
+}
+
+/**
+ * x509_decode_time - Decode an X.509 time ASN.1 object
+ * @_t: The time to fill in
+ * @hdrlen: The length of the object header
+ * @tag: The object tag
+ * @value: The object value
+ * @vlen: The size of the object value
+ *
+ * Decode an ASN.1 universal time or generalised time field into a struct the
+ * kernel can handle and check it for validity.  The time is decoded thus:
+ *
+ *     [RFC5280 ยง4.1.2.5]
+ *     CAs conforming to this profile MUST always encode certificate validity
+ *     dates through the year 2049 as UTCTime; certificate validity dates in
+ *     2050 or later MUST be encoded as GeneralizedTime.  Conforming
+ *     applications MUST be able to process validity dates that are encoded in
+ *     either UTCTime or GeneralizedTime.
+ */
+int x509_decode_time(time64_t *_t,  size_t hdrlen,
+                    unsigned char tag,
+                    const unsigned char *value, size_t vlen)
+{
+       static const unsigned char month_lengths[] = { 31, 28, 31, 30, 31, 30,
+                                                      31, 31, 30, 31, 30, 31 };
+       const unsigned char *p = value;
+       unsigned year, mon, day, hour, min, sec, mon_len;
+
+#define dec2bin(X) ({ unsigned char x = (X) - '0'; if (x > 9) goto invalid_time; x; })
+#define DD2bin(P) ({ unsigned x = dec2bin(P[0]) * 10 + dec2bin(P[1]); P += 2; x; })
+
+       if (tag == ASN1_UNITIM) {
+               /* UTCTime: YYMMDDHHMMSSZ */
+               if (vlen != 13)
+                       goto unsupported_time;
+               year = DD2bin(p);
+               if (year >= 50)
+                       year += 1900;
+               else
+                       year += 2000;
+       } else if (tag == ASN1_GENTIM) {
+               /* GenTime: YYYYMMDDHHMMSSZ */
+               if (vlen != 15)
+                       goto unsupported_time;
+               year = DD2bin(p) * 100 + DD2bin(p);
+               if (year >= 1950 && year <= 2049)
+                       goto invalid_time;
+       } else {
+               goto unsupported_time;
+       }
+
+       mon  = DD2bin(p);
+       day = DD2bin(p);
+       hour = DD2bin(p);
+       min  = DD2bin(p);
+       sec  = DD2bin(p);
+
+       if (*p != 'Z')
+               goto unsupported_time;
+
+       if (year < 1970 ||
+           mon < 1 || mon > 12)
+               goto invalid_time;
+
+       mon_len = month_lengths[mon - 1];
+       if (mon == 2) {
+               if (year % 4 == 0) {
+                       mon_len = 29;
+                       if (year % 100 == 0) {
+                               mon_len = 28;
+                               if (year % 400 == 0)
+                                       mon_len = 29;
+                       }
+               }
+       }
+
+       if (day < 1 || day > mon_len ||
+           hour > 24 || /* ISO 8601 permits 24:00:00 as midnight tomorrow */
+           min > 59 ||
+           sec > 60) /* ISO 8601 permits leap seconds [X.680 46.3] */
+               goto invalid_time;
+
+       *_t = mktime64(year, mon, day, hour, min, sec);
+       return 0;
+
+unsupported_time:
+       pr_debug("Got unsupported time [tag %02x]: '%*phN'\n",
+                tag, (int)vlen, value);
+       return -EBADMSG;
+invalid_time:
+       pr_debug("Got invalid time [tag %02x]: '%*phN'\n",
+                tag, (int)vlen, value);
+       return -EBADMSG;
+}
+EXPORT_SYMBOL_GPL(x509_decode_time);
+
+int x509_note_not_before(void *context, size_t hdrlen,
+                        unsigned char tag,
+                        const void *value, size_t vlen)
+{
+       struct x509_parse_context *ctx = context;
+       return x509_decode_time(&ctx->cert->valid_from, hdrlen, tag, value, vlen);
+}
+
+int x509_note_not_after(void *context, size_t hdrlen,
+                       unsigned char tag,
+                       const void *value, size_t vlen)
+{
+       struct x509_parse_context *ctx = context;
+       return x509_decode_time(&ctx->cert->valid_to, hdrlen, tag, value, vlen);
+}
+
+/*
+ * Note a key identifier-based AuthorityKeyIdentifier
+ */
+int x509_akid_note_kid(void *context, size_t hdrlen,
+                      unsigned char tag,
+                      const void *value, size_t vlen)
+{
+       struct x509_parse_context *ctx = context;
+       struct asymmetric_key_id *kid;
+
+       pr_debug("AKID: keyid: %*phN\n", (int)vlen, value);
+
+       if (ctx->cert->sig->auth_ids[1])
+               return 0;
+
+       kid = asymmetric_key_generate_id(value, vlen, "", 0);
+       if (IS_ERR(kid))
+               return PTR_ERR(kid);
+       pr_debug("authkeyid %*phN\n", kid->len, kid->data);
+       ctx->cert->sig->auth_ids[1] = kid;
+       return 0;
+}
+
+/*
+ * Note a directoryName in an AuthorityKeyIdentifier
+ */
+int x509_akid_note_name(void *context, size_t hdrlen,
+                       unsigned char tag,
+                       const void *value, size_t vlen)
+{
+       struct x509_parse_context *ctx = context;
+
+       pr_debug("AKID: name: %*phN\n", (int)vlen, value);
+
+       ctx->akid_raw_issuer = value;
+       ctx->akid_raw_issuer_size = vlen;
+       return 0;
+}
+
+/*
+ * Note a serial number in an AuthorityKeyIdentifier
+ */
+int x509_akid_note_serial(void *context, size_t hdrlen,
+                         unsigned char tag,
+                         const void *value, size_t vlen)
+{
+       struct x509_parse_context *ctx = context;
+       struct asymmetric_key_id *kid;
+
+       pr_debug("AKID: serial: %*phN\n", (int)vlen, value);
+
+       if (!ctx->akid_raw_issuer || ctx->cert->sig->auth_ids[0])
+               return 0;
+
+       kid = asymmetric_key_generate_id(value,
+                                        vlen,
+                                        ctx->akid_raw_issuer,
+                                        ctx->akid_raw_issuer_size);
+       if (IS_ERR(kid))
+               return PTR_ERR(kid);
+
+       pr_debug("authkeyid %*phN\n", kid->len, kid->data);
+       ctx->cert->sig->auth_ids[0] = kid;
+       return 0;
+}
diff --git a/lib/crypto/x509_parser.h b/lib/crypto/x509_parser.h
new file mode 100644 (file)
index 0000000..c233f13
--- /dev/null
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* X.509 certificate parser internal definitions
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#include <linux/time.h>
+#include <crypto/public_key.h>
+#include <keys/asymmetric-type.h>
+
+struct x509_certificate {
+       struct x509_certificate *next;
+       struct x509_certificate *signer;        /* Certificate that signed this one */
+       struct public_key *pub;                 /* Public key details */
+       struct public_key_signature *sig;       /* Signature parameters */
+       char            *issuer;                /* Name of certificate issuer */
+       char            *subject;               /* Name of certificate subject */
+       struct asymmetric_key_id *id;           /* Issuer + Serial number */
+       struct asymmetric_key_id *skid;         /* Subject + subjectKeyId (optional) */
+       time64_t        valid_from;
+       time64_t        valid_to;
+       const void      *tbs;                   /* Signed data */
+       unsigned        tbs_size;               /* Size of signed data */
+       unsigned        raw_sig_size;           /* Size of sigature */
+       const void      *raw_sig;               /* Signature data */
+       const void      *raw_serial;            /* Raw serial number in ASN.1 */
+       unsigned        raw_serial_size;
+       unsigned        raw_issuer_size;
+       const void      *raw_issuer;            /* Raw issuer name in ASN.1 */
+       const void      *raw_subject;           /* Raw subject name in ASN.1 */
+       unsigned        raw_subject_size;
+       unsigned        raw_skid_size;
+       const void      *raw_skid;              /* Raw subjectKeyId in ASN.1 */
+       unsigned        index;
+       bool            seen;                   /* Infinite recursion prevention */
+       bool            verified;
+       bool            self_signed;            /* T if self-signed (check unsupported_sig too) */
+       bool            unsupported_key;        /* T if key uses unsupported crypto */
+       bool            unsupported_sig;        /* T if signature uses unsupported crypto */
+       bool            blacklisted;
+};
+
+/*
+ * x509_cert_parser.c
+ */
+extern void x509_free_certificate(struct x509_certificate *cert);
+extern struct x509_certificate *x509_cert_parse(const void *data, size_t datalen);
+extern int x509_decode_time(time64_t *_t,  size_t hdrlen,
+                           unsigned char tag,
+                           const unsigned char *value, size_t vlen);
+
+/*
+ * x509_public_key.c
+ */
+extern int x509_get_sig_params(struct x509_certificate *cert);
+extern int x509_check_for_self_signed(struct x509_certificate *cert);
diff --git a/lib/crypto/x509_public_key.c b/lib/crypto/x509_public_key.c
new file mode 100644 (file)
index 0000000..04bdb67
--- /dev/null
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Instantiate a public key crypto key from an X.509 Certificate
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#define pr_fmt(fmt) "X.509: "fmt
+#ifdef __UBOOT__
+#include <common.h>
+#include <linux/compat.h>
+#include <linux/errno.h>
+#else
+#include <linux/module.h>
+#endif
+#include <linux/kernel.h>
+#ifndef __UBOOT__
+#include <linux/slab.h>
+#include <keys/asymmetric-subtype.h>
+#include <keys/asymmetric-parser.h>
+#include <keys/system_keyring.h>
+#include <crypto/hash.h>
+#include "asymmetric_keys.h"
+#endif
+#include "x509_parser.h"
+
+/*
+ * 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)
+{
+       struct public_key_signature *sig = cert->sig;
+#ifndef __UBOOT__
+       struct crypto_shash *tfm;
+       struct shash_desc *desc;
+       size_t desc_size;
+#endif
+       int ret;
+
+       pr_devel("==>%s()\n", __func__);
+
+       if (!cert->pub->pkey_algo)
+               cert->unsupported_key = true;
+
+       if (!sig->pkey_algo)
+               cert->unsupported_sig = true;
+
+       /* We check the hash if we can - even if we can't then verify it */
+       if (!sig->hash_algo) {
+               cert->unsupported_sig = true;
+               return 0;
+       }
+
+       sig->s = kmemdup(cert->raw_sig, cert->raw_sig_size, GFP_KERNEL);
+       if (!sig->s)
+               return -ENOMEM;
+
+       sig->s_size = cert->raw_sig_size;
+
+#ifdef __UBOOT__
+       /*
+        * Note:
+        * This part (filling sig->digest) should be implemented if
+        * x509_check_for_self_signed() is enabled x509_cert_parse().
+        * Currently, this check won't affect UEFI secure boot.
+        */
+       ret = 0;
+#else
+       /* Allocate the hashing algorithm we're going to need and find out how
+        * big the hash operational data will be.
+        */
+       tfm = crypto_alloc_shash(sig->hash_algo, 0, 0);
+       if (IS_ERR(tfm)) {
+               if (PTR_ERR(tfm) == -ENOENT) {
+                       cert->unsupported_sig = true;
+                       return 0;
+               }
+               return PTR_ERR(tfm);
+       }
+
+       desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
+       sig->digest_size = crypto_shash_digestsize(tfm);
+
+       ret = -ENOMEM;
+       sig->digest = kmalloc(sig->digest_size, GFP_KERNEL);
+       if (!sig->digest)
+               goto error;
+
+       desc = kzalloc(desc_size, GFP_KERNEL);
+       if (!desc)
+               goto error;
+
+       desc->tfm = tfm;
+
+       ret = crypto_shash_digest(desc, cert->tbs, cert->tbs_size, sig->digest);
+       if (ret < 0)
+               goto error_2;
+
+       ret = is_hash_blacklisted(sig->digest, sig->digest_size, "tbs");
+       if (ret == -EKEYREJECTED) {
+               pr_err("Cert %*phN is blacklisted\n",
+                      sig->digest_size, sig->digest);
+               cert->blacklisted = true;
+               ret = 0;
+       }
+
+error_2:
+       kfree(desc);
+error:
+       crypto_free_shash(tfm);
+#endif /* __UBOOT__ */
+       pr_devel("<==%s() = %d\n", __func__, ret);
+       return ret;
+}
+
+#ifndef __UBOOT__
+/*
+ * Check for self-signedness in an X.509 cert and if found, check the signature
+ * immediately if we can.
+ */
+int x509_check_for_self_signed(struct x509_certificate *cert)
+{
+       int ret = 0;
+
+       pr_devel("==>%s()\n", __func__);
+
+       if (cert->raw_subject_size != cert->raw_issuer_size ||
+           memcmp(cert->raw_subject, cert->raw_issuer,
+                  cert->raw_issuer_size) != 0)
+               goto not_self_signed;
+
+       if (cert->sig->auth_ids[0] || cert->sig->auth_ids[1]) {
+               /* If the AKID is present it may have one or two parts.  If
+                * both are supplied, both must match.
+                */
+               bool a = asymmetric_key_id_same(cert->skid, cert->sig->auth_ids[1]);
+               bool b = asymmetric_key_id_same(cert->id, cert->sig->auth_ids[0]);
+
+               if (!a && !b)
+                       goto not_self_signed;
+
+               ret = -EKEYREJECTED;
+               if (((a && !b) || (b && !a)) &&
+                   cert->sig->auth_ids[0] && cert->sig->auth_ids[1])
+                       goto out;
+       }
+
+       ret = -EKEYREJECTED;
+       if (strcmp(cert->pub->pkey_algo, cert->sig->pkey_algo) != 0)
+               goto out;
+
+       ret = public_key_verify_signature(cert->pub, cert->sig);
+       if (ret < 0) {
+               if (ret == -ENOPKG) {
+                       cert->unsupported_sig = true;
+                       ret = 0;
+               }
+               goto out;
+       }
+
+       pr_devel("Cert Self-signature verified");
+       cert->self_signed = true;
+
+out:
+       pr_devel("<==%s() = %d\n", __func__, ret);
+       return ret;
+
+not_self_signed:
+       pr_devel("<==%s() = 0 [not]\n", __func__);
+       return 0;
+}
+
+/*
+ * Attempt to parse a data blob for a key as an X509 certificate.
+ */
+static int x509_key_preparse(struct key_preparsed_payload *prep)
+{
+       struct asymmetric_key_ids *kids;
+       struct x509_certificate *cert;
+       const char *q;
+       size_t srlen, sulen;
+       char *desc = NULL, *p;
+       int ret;
+
+       cert = x509_cert_parse(prep->data, prep->datalen);
+       if (IS_ERR(cert))
+               return PTR_ERR(cert);
+
+       pr_devel("Cert Issuer: %s\n", cert->issuer);
+       pr_devel("Cert Subject: %s\n", cert->subject);
+
+       if (cert->unsupported_key) {
+               ret = -ENOPKG;
+               goto error_free_cert;
+       }
+
+       pr_devel("Cert Key Algo: %s\n", cert->pub->pkey_algo);
+       pr_devel("Cert Valid period: %lld-%lld\n", cert->valid_from, cert->valid_to);
+
+       cert->pub->id_type = "X509";
+
+       if (cert->unsupported_sig) {
+               public_key_signature_free(cert->sig);
+               cert->sig = NULL;
+       } else {
+               pr_devel("Cert Signature: %s + %s\n",
+                        cert->sig->pkey_algo, cert->sig->hash_algo);
+       }
+
+       /* Don't permit addition of blacklisted keys */
+       ret = -EKEYREJECTED;
+       if (cert->blacklisted)
+               goto error_free_cert;
+
+       /* Propose a description */
+       sulen = strlen(cert->subject);
+       if (cert->raw_skid) {
+               srlen = cert->raw_skid_size;
+               q = cert->raw_skid;
+       } else {
+               srlen = cert->raw_serial_size;
+               q = cert->raw_serial;
+       }
+
+       ret = -ENOMEM;
+       desc = kmalloc(sulen + 2 + srlen * 2 + 1, GFP_KERNEL);
+       if (!desc)
+               goto error_free_cert;
+       p = memcpy(desc, cert->subject, sulen);
+       p += sulen;
+       *p++ = ':';
+       *p++ = ' ';
+       p = bin2hex(p, q, srlen);
+       *p = 0;
+
+       kids = kmalloc(sizeof(struct asymmetric_key_ids), GFP_KERNEL);
+       if (!kids)
+               goto error_free_desc;
+       kids->id[0] = cert->id;
+       kids->id[1] = cert->skid;
+
+       /* We're pinning the module by being linked against it */
+       __module_get(public_key_subtype.owner);
+       prep->payload.data[asym_subtype] = &public_key_subtype;
+       prep->payload.data[asym_key_ids] = kids;
+       prep->payload.data[asym_crypto] = cert->pub;
+       prep->payload.data[asym_auth] = cert->sig;
+       prep->description = desc;
+       prep->quotalen = 100;
+
+       /* We've finished with the certificate */
+       cert->pub = NULL;
+       cert->id = NULL;
+       cert->skid = NULL;
+       cert->sig = NULL;
+       desc = NULL;
+       ret = 0;
+
+error_free_desc:
+       kfree(desc);
+error_free_cert:
+       x509_free_certificate(cert);
+       return ret;
+}
+
+static struct asymmetric_key_parser x509_key_parser = {
+       .owner  = THIS_MODULE,
+       .name   = "x509",
+       .parse  = x509_key_preparse,
+};
+
+/*
+ * Module stuff
+ */
+static int __init x509_key_init(void)
+{
+       return register_asymmetric_key_parser(&x509_key_parser);
+}
+
+static void __exit x509_key_exit(void)
+{
+       unregister_asymmetric_key_parser(&x509_key_parser);
+}
+
+module_init(x509_key_init);
+module_exit(x509_key_exit);
+#endif /* !__UBOOT__ */
+
+MODULE_DESCRIPTION("X.509 certificate parser");
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_LICENSE("GPL");