diff mbox series

[U-Boot,v3,14/16] lib: crypto: add x509 parser

Message ID 20191113004502.29986-15-takahiro.akashi@linaro.org
State Accepted
Commit b4adf627d5b7bdff649d3b852eab97d6f9191111
Delegated to: Tom Rini
Headers show
Series import x509/pkcs7 parsers from linux | expand

Commit Message

AKASHI Takahiro Nov. 13, 2019, 12:45 a.m. UTC
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                   |   1 +
 lib/Makefile                  |   1 +
 lib/crypto/Kconfig            |  12 +
 lib/crypto/Makefile           |  17 +
 lib/crypto/x509.asn1          |  60 +++
 lib/crypto/x509_akid.asn1     |  35 ++
 lib/crypto/x509_cert_parser.c | 697 ++++++++++++++++++++++++++++++++++
 lib/crypto/x509_parser.h      |  57 +++
 lib/crypto/x509_public_key.c  | 292 ++++++++++++++
 9 files changed, 1172 insertions(+)
 create mode 100644 lib/crypto/x509.asn1
 create mode 100644 lib/crypto/x509_akid.asn1
 create mode 100644 lib/crypto/x509_cert_parser.c
 create mode 100644 lib/crypto/x509_parser.h
 create mode 100644 lib/crypto/x509_public_key.c

Comments

Tom Rini Dec. 6, 2019, 9:50 p.m. UTC | #1
On Wed, Nov 13, 2019 at 09:45:00AM +0900, AKASHI Takahiro wrote:

> 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>

Applied to u-boot/master, thanks!
Heinrich Schuchardt Dec. 7, 2019, 8:51 p.m. UTC | #2
On 12/6/19 10:50 PM, Tom Rini wrote:
> On Wed, Nov 13, 2019 at 09:45:00AM +0900, AKASHI Takahiro wrote:
>
>> 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>
>
> Applied to u-boot/master, thanks!
>
Hello Tom,

this patch causes a build error for rpi_0_w_defconfig with
CONFIG_X509_CERTIFICATE_PARSER=y:

In file included from include/linux/time.h:4,
                  from lib/crypto/x509_parser.h:8,
                  from lib/crypto/x509_cert_parser.c:20:
include/rtc.h:197:26: error: unknown type name ‘uchar’; did you mean
‘unchar’?
  void rtc_write8(int reg, uchar val);
                           ^~~~~
                           unchar
make[4]: *** [scripts/Makefile.build:279: lib/crypto/x509_cert_parser.o]
Error 1

The error is caused by:

include/linux/types.h:88:typedef unsigned char          unchar;

I have prepared a patch but will first run it through Gitlab:

https://gitlab.denx.de/u-boot/custodians/u-boot-efi/commit/f574992867cf40f48a381f88c1ac15a47dbe1baf

@Takahiro:
I am wondering why this did not show up in your tests. Did Tom miss a patch?

Best regards

Heinrich
Tom Rini Dec. 7, 2019, 10:34 p.m. UTC | #3
On Sat, Dec 07, 2019 at 09:51:22PM +0100, Heinrich Schuchardt wrote:
> On 12/6/19 10:50 PM, Tom Rini wrote:
> > On Wed, Nov 13, 2019 at 09:45:00AM +0900, AKASHI Takahiro wrote:
> > 
> > > 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>
> > 
> > Applied to u-boot/master, thanks!
> > 
> Hello Tom,
> 
> this patch causes a build error for rpi_0_w_defconfig with
> CONFIG_X509_CERTIFICATE_PARSER=y:
> 
> In file included from include/linux/time.h:4,
>                  from lib/crypto/x509_parser.h:8,
>                  from lib/crypto/x509_cert_parser.c:20:
> include/rtc.h:197:26: error: unknown type name ‘uchar’; did you mean
> ‘unchar’?
>  void rtc_write8(int reg, uchar val);
>                           ^~~~~
>                           unchar
> make[4]: *** [scripts/Makefile.build:279: lib/crypto/x509_cert_parser.o]
> Error 1
> 
> The error is caused by:
> 
> include/linux/types.h:88:typedef unsigned char          unchar;
> 
> I have prepared a patch but will first run it through Gitlab:
> 
> https://gitlab.denx.de/u-boot/custodians/u-boot-efi/commit/f574992867cf40f48a381f88c1ac15a47dbe1baf
> 
> @Takahiro:
> I am wondering why this did not show up in your tests. Did Tom miss a patch?

Only sandbox enables any of this out of the box, which is why I've taken
it at this point in the window.  It's otherwise neutral and as good as
it was going to get prior to inclusion somewhere for wider testing.
Which, thanks for doing.
AKASHI Takahiro Dec. 9, 2019, 12:59 a.m. UTC | #4
On Sat, Dec 07, 2019 at 05:34:07PM -0500, Tom Rini wrote:
> On Sat, Dec 07, 2019 at 09:51:22PM +0100, Heinrich Schuchardt wrote:
> > On 12/6/19 10:50 PM, Tom Rini wrote:
> > > On Wed, Nov 13, 2019 at 09:45:00AM +0900, AKASHI Takahiro wrote:
> > > 
> > > > 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>
> > > 
> > > Applied to u-boot/master, thanks!
> > > 
> > Hello Tom,
> > 
> > this patch causes a build error for rpi_0_w_defconfig with
> > CONFIG_X509_CERTIFICATE_PARSER=y:
> > 
> > In file included from include/linux/time.h:4,
> >                  from lib/crypto/x509_parser.h:8,
> >                  from lib/crypto/x509_cert_parser.c:20:
> > include/rtc.h:197:26: error: unknown type name ‘uchar’; did you mean
> > ‘unchar’?
> >  void rtc_write8(int reg, uchar val);
> >                           ^~~~~
> >                           unchar
> > make[4]: *** [scripts/Makefile.build:279: lib/crypto/x509_cert_parser.o]
> > Error 1
> > 
> > The error is caused by:
> > 
> > include/linux/types.h:88:typedef unsigned char          unchar;
> > 
> > I have prepared a patch but will first run it through Gitlab:
> > 
> > https://gitlab.denx.de/u-boot/custodians/u-boot-efi/commit/f574992867cf40f48a381f88c1ac15a47dbe1baf
> > 
> > @Takahiro:
> > I am wondering why this did not show up in your tests. Did Tom miss a patch?

Thank you for catching this, Heinrich.

> Only sandbox enables any of this out of the box, which is why I've taken
> it at this point in the window.  It's otherwise neutral and as good as
> it was going to get prior to inclusion somewhere for wider testing.
> Which, thanks for doing.

Yeah, more strictly speaking, my x509 test is enabled by default
due to "default y" if UNIT_TEST is also enabled.
So if pytest is run on Travis CI, x509 test can also be exercised
via pytest's ut driver, but pytest is run on limited number of
architectures on Travis CI and UNIT_TEST is enabled only on sandbox
for now.

Thanks,
-Takahiro Akashi


> -- 
> Tom
diff mbox series

Patch

diff --git a/lib/Kconfig b/lib/Kconfig
index 0431cfbd21f8..051ffce23b42 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -265,6 +265,7 @@  config AES
 	  present.
 
 source lib/rsa/Kconfig
+source lib/crypto/Kconfig
 
 config TPM
 	bool "Trusted Platform Module (TPM) Support"
diff --git a/lib/Makefile b/lib/Makefile
index 9b8305e3190f..0fb1710f5aca 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -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
 
diff --git a/lib/crypto/Kconfig b/lib/crypto/Kconfig
index 9572ea8c87f3..aeb599c593ac 100644
--- a/lib/crypto/Kconfig
+++ b/lib/crypto/Kconfig
@@ -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
diff --git a/lib/crypto/Makefile b/lib/crypto/Makefile
index 69330a9ebd27..d7e27be568a1 100644
--- a/lib/crypto/Makefile
+++ b/lib/crypto/Makefile
@@ -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
index 000000000000..5c9f4e4a5231
--- /dev/null
+++ b/lib/crypto/x509.asn1
@@ -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
index 000000000000..1a33231a75a8
--- /dev/null
+++ b/lib/crypto/x509_akid.asn1
@@ -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
index 000000000000..e6d2a426a0bc
--- /dev/null
+++ b/lib/crypto/x509_cert_parser.c
@@ -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
index 000000000000..c233f136fb35
--- /dev/null
+++ b/lib/crypto/x509_parser.h
@@ -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
index 000000000000..04bdb672b496
--- /dev/null
+++ b/lib/crypto/x509_public_key.c
@@ -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");