X.509: Add a crypto key parser for binary (DER) X.509 certificates

Add a crypto key parser for binary (DER) encoded X.509 certificates.  The
certificate is parsed and, if possible, the signature is verified.

An X.509 key can be added like this:

	# keyctl padd crypto bar @s </tmp/x509.cert
	15768135

and displayed like this:

	# cat /proc/keys
	00f09a47 I--Q---     1 perm 39390000     0     0 asymmetri bar: X509.RSA e9fd6d08 []

Note that this only works with binary certificates.  PEM encoded certificates
are ignored by the parser.

Note also that the X.509 key ID is not congruent with the PGP key ID, but for
the moment, they will match.

If a NULL or "" name is given to add_key(), then the parser will generate a key
description from the CertificateSerialNumber and Name fields of the
TBSCertificate:

	00aefc4e I--Q---     1 perm 39390000     0     0 asymmetri bfbc0cd76d050ea4:/C=GB/L=Cambridge/O=Red Hat/CN=kernel key: X509.RSA 0c688c7b []

Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
diff --git a/crypto/asymmetric_keys/.gitignore b/crypto/asymmetric_keys/.gitignore
new file mode 100644
index 0000000..ee328374
--- /dev/null
+++ b/crypto/asymmetric_keys/.gitignore
@@ -0,0 +1 @@
+*-asn1.[ch]
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index 561759d..6d2c2ea 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -25,4 +25,14 @@
 	help
 	  This option enables support for the RSA algorithm (PKCS#1, RFC3447).
 
+config X509_CERTIFICATE_PARSER
+	tristate "X.509 certificate parser"
+	depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+	select ASN1
+	select OID_REGISTRY
+	help
+	  This option procides 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/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index 7c92691..0727204 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -8,3 +8,20 @@
 
 obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o
 obj-$(CONFIG_PUBLIC_KEY_ALGO_RSA) += rsa.o
+
+#
+# X.509 Certificate handling
+#
+obj-$(CONFIG_X509_CERTIFICATE_PARSER) += x509_key_parser.o
+x509_key_parser-y := \
+	x509-asn1.o \
+	x509_rsakey-asn1.o \
+	x509_cert_parser.o \
+	x509_public_key.o
+
+$(obj)/x509_cert_parser.o: $(obj)/x509-asn1.h $(obj)/x509_rsakey-asn1.h
+$(obj)/x509-asn1.o: $(obj)/x509-asn1.c $(obj)/x509-asn1.h
+$(obj)/x509_rsakey-asn1.o: $(obj)/x509_rsakey-asn1.c $(obj)/x509_rsakey-asn1.h
+
+clean-files	+= x509-asn1.c x509-asn1.h
+clean-files	+= x509_rsakey-asn1.c x509_rsakey-asn1.h
diff --git a/crypto/asymmetric_keys/x509.asn1 b/crypto/asymmetric_keys/x509.asn1
new file mode 100644
index 0000000..bf32b3d
--- /dev/null
+++ b/crypto/asymmetric_keys/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,
+	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
+}
+
+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/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
new file mode 100644
index 0000000..8fcac94
--- /dev/null
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -0,0 +1,497 @@
+/* X.509 certificate parser
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "X.509: "fmt
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/oid_registry.h>
+#include "public_key.h"
+#include "x509_parser.h"
+#include "x509-asn1.h"
+#include "x509_rsakey-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 */
+	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 */
+};
+
+/*
+ * Free an X.509 certificate
+ */
+void x509_free_certificate(struct x509_certificate *cert)
+{
+	if (cert) {
+		public_key_destroy(cert->pub);
+		kfree(cert->issuer);
+		kfree(cert->subject);
+		kfree(cert->fingerprint);
+		kfree(cert->authority);
+		kfree(cert);
+	}
+}
+
+/*
+ * 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;
+	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;
+	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 public key */
+	ret = asn1_ber_decoder(&x509_rsakey_decoder, ctx,
+			       ctx->key, ctx->key_size);
+	if (ret < 0)
+		goto error_decode;
+
+	kfree(ctx);
+	return cert;
+
+error_decode:
+	kfree(ctx);
+error_no_ctx:
+	x509_free_certificate(cert);
+error_no_cert:
+	return ERR_PTR(ret);
+}
+
+/*
+ * 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: [%zu] %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 = PKEY_HASH_MD5;
+		ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+		break;
+
+	case OID_sha1WithRSAEncryption:
+		ctx->cert->sig_hash_algo = PKEY_HASH_SHA1;
+		ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+		break;
+
+	case OID_sha256WithRSAEncryption:
+		ctx->cert->sig_hash_algo = PKEY_HASH_SHA256;
+		ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+		break;
+
+	case OID_sha384WithRSAEncryption:
+		ctx->cert->sig_hash_algo = PKEY_HASH_SHA384;
+		ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+		break;
+
+	case OID_sha512WithRSAEncryption:
+		ctx->cert->sig_hash_algo = PKEY_HASH_SHA512;
+		ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+		break;
+
+	case OID_sha224WithRSAEncryption:
+		ctx->cert->sig_hash_algo = PKEY_HASH_SHA224;
+		ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+		break;
+	}
+
+	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;
+	}
+
+	ctx->cert->sig = value;
+	ctx->cert->sig_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;
+	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;
+	return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->subject, vlen);
+}
+
+/*
+ * 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;
+
+	if (ctx->last_oid != OID_rsaEncryption)
+		return -ENOPKG;
+
+	/* There seems to be an extraneous 0 byte on the front of the data */
+	ctx->cert->pkey_algo = PKEY_ALGO_RSA;
+	ctx->key = value + 1;
+	ctx->key_size = vlen - 1;
+	return 0;
+}
+
+/*
+ * Extract a RSA public key value
+ */
+int rsa_extract_mpi(void *context, size_t hdrlen,
+		    unsigned char tag,
+		    const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+	MPI mpi;
+
+	if (ctx->nr_mpi >= ARRAY_SIZE(ctx->cert->pub->mpi)) {
+		pr_err("Too many public key MPIs in certificate\n");
+		return -EBADMSG;
+	}
+
+	mpi = mpi_read_raw_data(value, vlen);
+	if (!mpi)
+		return -ENOMEM;
+
+	ctx->cert->pub->mpi[ctx->nr_mpi++] = mpi;
+	return 0;
+}
+
+/*
+ * 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;
+	const unsigned char *v = value;
+	char *f;
+	int i;
+
+	pr_debug("Extension: %u\n", ctx->last_oid);
+
+	if (ctx->last_oid == OID_subjectKeyIdentifier) {
+		/* Get hold of the key fingerprint */
+		if (vlen < 3)
+			return -EBADMSG;
+		if (v[0] != ASN1_OTS || v[1] != vlen - 2)
+			return -EBADMSG;
+		v += 2;
+		vlen -= 2;
+
+		f = kmalloc(vlen * 2 + 1, GFP_KERNEL);
+		if (!f)
+			return -ENOMEM;
+		for (i = 0; i < vlen; i++)
+			sprintf(f + i * 2, "%02x", v[i]);
+		pr_debug("fingerprint %s\n", f);
+		ctx->cert->fingerprint = f;
+		return 0;
+	}
+
+	if (ctx->last_oid == OID_authorityKeyIdentifier) {
+		/* Get hold of the CA key fingerprint */
+		if (vlen < 5)
+			return -EBADMSG;
+		if (v[0] != (ASN1_SEQ | (ASN1_CONS << 5)) ||
+		    v[1] != vlen - 2 ||
+		    v[2] != (ASN1_CONT << 6) ||
+		    v[3] != vlen - 4)
+			return -EBADMSG;
+		v += 4;
+		vlen -= 4;
+
+		f = kmalloc(vlen * 2 + 1, GFP_KERNEL);
+		if (!f)
+			return -ENOMEM;
+		for (i = 0; i < vlen; i++)
+			sprintf(f + i * 2, "%02x", v[i]);
+		pr_debug("authority   %s\n", f);
+		ctx->cert->authority = f;
+		return 0;
+	}
+
+	return 0;
+}
+
+/*
+ * Record a certificate time.
+ */
+static int x509_note_time(time_t *_time,  size_t hdrlen,
+			  unsigned char tag,
+			  const unsigned char *value, size_t vlen)
+{
+	unsigned YY, MM, DD, hh, mm, ss;
+	const unsigned char *p = value;
+
+#define dec2bin(X) ((X) - '0')
+#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;
+		YY = DD2bin(p);
+		if (YY > 50)
+			YY += 1900;
+		else
+			YY += 2000;
+	} else if (tag == ASN1_GENTIM) {
+		/* GenTime: YYYYMMDDHHMMSSZ */
+		if (vlen != 15)
+			goto unsupported_time;
+		YY = DD2bin(p) * 100 + DD2bin(p);
+	} else {
+		goto unsupported_time;
+	}
+
+	MM = DD2bin(p);
+	DD = DD2bin(p);
+	hh = DD2bin(p);
+	mm = DD2bin(p);
+	ss = DD2bin(p);
+
+	if (*p != 'Z')
+		goto unsupported_time;
+
+	*_time = mktime(YY, MM, DD, hh, mm, ss);
+	return 0;
+
+unsupported_time:
+	pr_debug("Got unsupported time [tag %02x]: '%*.*s'\n",
+		 tag, (int)vlen, (int)vlen, value);
+	return -EBADMSG;
+}
+
+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_note_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_note_time(&ctx->cert->valid_to, hdrlen, tag, value, vlen);
+}
diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h
new file mode 100644
index 0000000..635053f
--- /dev/null
+++ b/crypto/asymmetric_keys/x509_parser.h
@@ -0,0 +1,36 @@
+/* X.509 certificate parser internal definitions
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <crypto/public_key.h>
+
+struct x509_certificate {
+	struct x509_certificate *next;
+	struct public_key *pub;			/* Public key details */
+	char		*issuer;		/* Name of certificate issuer */
+	char		*subject;		/* Name of certificate subject */
+	char		*fingerprint;		/* Key fingerprint as hex */
+	char		*authority;		/* Authority key fingerprint as hex */
+	time_t		valid_from;
+	time_t		valid_to;
+	enum pkey_algo	pkey_algo : 8;		/* Public key algorithm */
+	enum pkey_algo	sig_pkey_algo : 8;	/* Signature public key algorithm */
+	enum pkey_hash_algo sig_hash_algo : 8;	/* Signature hash algorithm */
+	const void	*tbs;			/* Signed data */
+	size_t		tbs_size;		/* Size of signed data */
+	const void	*sig;			/* Signature data */
+	size_t		sig_size;		/* Size of sigature */
+};
+
+/*
+ * 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);
diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c
new file mode 100644
index 0000000..716917c
--- /dev/null
+++ b/crypto/asymmetric_keys/x509_public_key.c
@@ -0,0 +1,207 @@
+/* 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)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "X.509: "fmt
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/mpi.h>
+#include <linux/asn1_decoder.h>
+#include <keys/asymmetric-subtype.h>
+#include <keys/asymmetric-parser.h>
+#include <crypto/hash.h>
+#include "asymmetric_keys.h"
+#include "public_key.h"
+#include "x509_parser.h"
+
+static const
+struct public_key_algorithm *x509_public_key_algorithms[PKEY_ALGO__LAST] = {
+	[PKEY_ALGO_DSA]		= NULL,
+#if defined(CONFIG_PUBLIC_KEY_ALGO_RSA) || \
+	defined(CONFIG_PUBLIC_KEY_ALGO_RSA_MODULE)
+	[PKEY_ALGO_RSA]		= &RSA_public_key_algorithm,
+#endif
+};
+
+/*
+ * Check the signature on a certificate using the provided public key
+ */
+static int x509_check_signature(const struct public_key *pub,
+				const struct x509_certificate *cert)
+{
+	struct public_key_signature *sig;
+	struct crypto_shash *tfm;
+	struct shash_desc *desc;
+	size_t digest_size, desc_size;
+	int ret;
+
+	pr_devel("==>%s()\n", __func__);
+	
+	/* Allocate the hashing algorithm we're going to need and find out how
+	 * big the hash operational data will be.
+	 */
+	tfm = crypto_alloc_shash(pkey_hash_algo[cert->sig_hash_algo], 0, 0);
+	if (IS_ERR(tfm))
+		return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
+
+	desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
+	digest_size = crypto_shash_digestsize(tfm);
+
+	/* We allocate the hash operational data storage on the end of our
+	 * context data.
+	 */
+	ret = -ENOMEM;
+	sig = kzalloc(sizeof(*sig) + desc_size + digest_size, GFP_KERNEL);
+	if (!sig)
+		goto error_no_sig;
+
+	sig->pkey_hash_algo	= cert->sig_hash_algo;
+	sig->digest		= (u8 *)sig + sizeof(*sig) + desc_size;
+	sig->digest_size	= digest_size;
+
+	desc = (void *)sig + sizeof(*sig);
+	desc->tfm	= tfm;
+	desc->flags	= CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	ret = crypto_shash_init(desc);
+	if (ret < 0)
+		goto error;
+
+	ret = -ENOMEM;
+	sig->rsa.s = mpi_read_raw_data(cert->sig, cert->sig_size);
+	if (!sig->rsa.s)
+		goto error;
+
+	ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, sig->digest);
+	if (ret < 0)
+		goto error_mpi;
+
+	ret = pub->algo->verify_signature(pub, sig);
+
+	pr_debug("Cert Verification: %d\n", ret);
+
+error_mpi:
+	mpi_free(sig->rsa.s);
+error:
+	kfree(sig);
+error_no_sig:
+	crypto_free_shash(tfm);
+
+	pr_devel("<==%s() = %d\n", __func__, ret);
+	return ret;
+}
+
+/*
+ * Attempt to parse a data blob for a key as an X509 certificate.
+ */
+static int x509_key_preparse(struct key_preparsed_payload *prep)
+{
+	struct x509_certificate *cert;
+	time_t now;
+	size_t srlen, sulen;
+	char *desc = NULL;
+	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);
+	pr_devel("Cert Key Algo: %s\n", pkey_algo[cert->pkey_algo]);
+	pr_devel("Cert Valid: %lu - %lu\n", cert->valid_from, cert->valid_to);
+	pr_devel("Cert Signature: %s + %s\n",
+		 pkey_algo[cert->sig_pkey_algo],
+		 pkey_hash_algo[cert->sig_hash_algo]);
+
+	if (!cert->fingerprint || !cert->authority) {
+		pr_warn("Cert for '%s' must have SubjKeyId and AuthKeyId extensions\n",
+			cert->subject);
+		ret = -EKEYREJECTED;
+		goto error_free_cert;
+	}
+
+	now = CURRENT_TIME.tv_sec;
+	if (now < cert->valid_from) {
+		pr_warn("Cert %s is not yet valid\n", cert->fingerprint);
+		ret = -EKEYREJECTED;
+		goto error_free_cert;
+	}
+	if (now >= cert->valid_to) {
+		pr_warn("Cert %s has expired\n", cert->fingerprint);
+		ret = -EKEYEXPIRED;
+		goto error_free_cert;
+	}
+
+	cert->pub->algo = x509_public_key_algorithms[cert->pkey_algo];
+	cert->pub->id_type = PKEY_ID_X509;
+
+	/* Check the signature on the key */
+	if (strcmp(cert->fingerprint, cert->authority) == 0) {
+		ret = x509_check_signature(cert->pub, cert);
+		if (ret < 0)
+			goto error_free_cert;
+	}
+
+	/* Propose a description */
+	sulen = strlen(cert->subject);
+	srlen = strlen(cert->fingerprint);
+	ret = -ENOMEM;
+	desc = kmalloc(sulen + 2 + srlen + 1, GFP_KERNEL);
+	if (!desc)
+		goto error_free_cert;
+	memcpy(desc, cert->subject, sulen);
+	desc[sulen] = ':';
+	desc[sulen + 1] = ' ';
+	memcpy(desc + sulen + 2, cert->fingerprint, srlen);
+	desc[sulen + 2 + srlen] = 0;
+
+	/* We're pinning the module by being linked against it */
+	__module_get(public_key_subtype.owner);
+	prep->type_data[0] = &public_key_subtype;
+	prep->type_data[1] = cert->fingerprint;
+	prep->payload = cert->pub;
+	prep->description = desc;
+	prep->quotalen = 100;
+
+	/* We've finished with the certificate */
+	cert->pub = NULL;
+	cert->fingerprint = NULL;
+	desc = NULL;
+	ret = 0;
+
+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);
diff --git a/crypto/asymmetric_keys/x509_rsakey.asn1 b/crypto/asymmetric_keys/x509_rsakey.asn1
new file mode 100644
index 0000000..4ec7cc6
--- /dev/null
+++ b/crypto/asymmetric_keys/x509_rsakey.asn1
@@ -0,0 +1,4 @@
+RSAPublicKey ::= SEQUENCE {
+	modulus			INTEGER ({ rsa_extract_mpi }),	-- n
+	publicExponent		INTEGER ({ rsa_extract_mpi })	-- e
+	}