diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index cd1406f9b14a..ac18b7f64a77 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_X509_CERTIFICATE_PARSER) += x509_key_parser.o
 x509_key_parser-y := \
 	x509-asn1.o \
 	x509_akid-asn1.o \
+	x509_extusage-asn1.o \
 	x509_rsakey-asn1.o \
 	x509_cert_parser.o \
 	x509_public_key.o
@@ -23,13 +24,16 @@ x509_key_parser-y := \
 $(obj)/x509_cert_parser.o: \
 	$(obj)/x509-asn1.h \
 	$(obj)/x509_akid-asn1.h \
+	$(obj)/x509_extusage-asn1.h \
 	$(obj)/x509_rsakey-asn1.h
 $(obj)/x509-asn1.o: $(obj)/x509-asn1.c $(obj)/x509-asn1.h
 $(obj)/x509_akid-asn1.o: $(obj)/x509_akid-asn1.c $(obj)/x509_akid-asn1.h
+$(obj)/x509_extusage-asn1.o: $(obj)/x509_extusage-asn1.c $(obj)/x509_extusage-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_akid-asn1.c x509_akid-asn1.h
+clean-files	+= x509_extusage-asn1.c x509_extusage-asn1.h
 clean-files	+= x509_rsakey-asn1.c x509_rsakey-asn1.h
 
 #
diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c
index bcbbbd794e1d..9047175fe818 100644
--- a/crypto/asymmetric_keys/asymmetric_type.c
+++ b/crypto/asymmetric_keys/asymmetric_type.c
@@ -246,7 +246,8 @@ static void asymmetric_key_describe(const struct key *key, struct seq_file *m)
 		}
 
 		seq_puts(m, " [");
-		/* put something here to indicate the key's capabilities */
+		if (subtype->describe_caps)
+			subtype->describe_caps(key, m);
 		seq_putc(m, ']');
 	}
 }
diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
index 2f6e4fb1a1ea..7790f58d00bf 100644
--- a/crypto/asymmetric_keys/public_key.c
+++ b/crypto/asymmetric_keys/public_key.c
@@ -42,6 +42,16 @@ const char *const pkey_id_type_name[PKEY_ID_TYPE__LAST] = {
 };
 EXPORT_SYMBOL_GPL(pkey_id_type_name);
 
+static const char *const public_key_restrictions[NR__PKEY_USAGE_RESTRICTION] = {
+	[PKEY_USAGE_NOT_SPECIFIED]		= "unrestricted",
+	[PKEY_RESTRICTED_USAGE]			= "unspecified",
+	[PKEY_RESTRICTED_TO_OTHER]		= "other use",
+	[PKEY_RESTRICTED_TO_MODULE_SIGNING]	= "module sig",
+	[PKEY_RESTRICTED_TO_FIRMWARE_SIGNING]	= "firmware sig",
+	[PKEY_RESTRICTED_TO_KEXEC_SIGNING]	= "kexec sig",
+	[PKEY_RESTRICTED_TO_KEY_SIGNING]	= "key sig",
+};
+
 /*
  * Provide a part of a description of the key for /proc/keys.
  */
@@ -56,6 +66,18 @@ static void public_key_describe(const struct key *asymmetric_key,
 }
 
 /*
+ * Describe capabilities/restrictions of the key for /proc/keys.
+ */
+static void public_key_describe_caps(const struct key *asymmetric_key,
+				     struct seq_file *m)
+{
+	struct public_key *key = asymmetric_key->payload.data;
+
+	if (key)
+		seq_puts(m, public_key_restrictions[key->usage_restriction]);
+}
+
+/*
  * Destroy a public key algorithm key.
  */
 void public_key_destroy(void *payload)
@@ -123,6 +145,7 @@ struct asymmetric_key_subtype public_key_subtype = {
 	.name			= "public_key",
 	.name_len		= sizeof("public_key") - 1,
 	.describe		= public_key_describe,
+	.describe_caps		= public_key_describe_caps,
 	.destroy		= public_key_destroy,
 	.verify_signature	= public_key_verify_signature_2,
 };
diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
index 6c130dd56f35..4d9e9a7f8fd2 100644
--- a/crypto/asymmetric_keys/x509_cert_parser.c
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -19,6 +19,7 @@
 #include "x509_parser.h"
 #include "x509-asn1.h"
 #include "x509_akid-asn1.h"
+#include "x509_extusage-asn1.h"
 #include "x509_rsakey-asn1.h"
 
 struct x509_parse_context {
@@ -40,6 +41,8 @@ struct x509_parse_context {
 	const void	*raw_akid;		/* Raw authorityKeyId in ASN.1 */
 	const void	*akid_raw_issuer;	/* Raw directoryName in authorityKeyId */
 	unsigned	akid_raw_issuer_size;
+	unsigned	raw_extusage_size;
+	const void	*raw_extusage;		/* Raw extKeyUsage in ASN.1 */
 };
 
 /*
@@ -91,6 +94,20 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
 	if (ret < 0)
 		goto error_decode;
 
+	/* Decode the extended key usage information */
+	if (ctx->raw_extusage) {
+		pr_devel("EXTUSAGE: %u %*phN\n",
+			 ctx->raw_extusage_size, ctx->raw_extusage_size,
+			 ctx->raw_extusage);
+		ret = asn1_ber_decoder(&x509_extusage_decoder, ctx,
+				       ctx->raw_extusage,
+				       ctx->raw_extusage_size);
+		if (ret < 0) {
+			pr_warn("Couldn't decode extKeyUsage\n");
+			goto error_decode;
+		}
+	}
+
 	/* Decode the AuthorityKeyIdentifier */
 	if (ctx->raw_akid) {
 		pr_devel("AKID: %u %*phN\n",
@@ -471,6 +488,14 @@ int x509_process_extension(void *context, size_t hdrlen,
 		return 0;
 	}
 
+	if (ctx->last_oid == OID_extKeyUsage) {
+		/* Get hold of the extended key usage information */
+		ctx->raw_extusage = v;
+		ctx->raw_extusage_size = vlen;
+		ctx->cert->pub->usage_restriction = PKEY_RESTRICTED_USAGE;
+		return 0;
+	}
+
 	return 0;
 }
 
@@ -605,3 +630,50 @@ int x509_akid_note_serial(void *context, size_t hdrlen,
 	ctx->cert->akid_id = kid;
 	return 0;
 }
+
+/*
+ * Note restriction to a purpose
+ */
+int x509_extusage_note_purpose(void *context, size_t hdrlen,
+			       unsigned char tag,
+			       const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+	enum pkey_usage_restriction restriction;
+	char buffer[50];
+	enum OID oid;
+
+	sprint_oid(value, vlen, buffer, sizeof(buffer));
+	pr_debug("ExtUsage: %s\n", buffer);
+
+	oid = look_up_OID(value, vlen);
+	if (oid == OID__NR) {
+		pr_debug("Unknown extension: [%lu] %s\n",
+			 (unsigned long)value - ctx->data, buffer);
+		return 0;
+	}
+
+	switch (oid) {
+	case OID_firmwareSigningOnlyKey:
+		restriction = PKEY_RESTRICTED_TO_FIRMWARE_SIGNING;
+		break;
+	case OID_moduleSigningOnlyKey:
+		restriction = PKEY_RESTRICTED_TO_MODULE_SIGNING;
+		break;
+	case OID_kexecSigningOnlyKey:
+		restriction = PKEY_RESTRICTED_TO_KEXEC_SIGNING;
+		break;
+	default:
+		restriction = PKEY_RESTRICTED_TO_OTHER;
+		break;
+	}
+
+	if (ctx->cert->pub->usage_restriction != PKEY_RESTRICTED_USAGE) {
+		pr_warn("Rejecting certificate with multiple restrictions\n");
+		return -EKEYREJECTED;
+	}
+
+	ctx->cert->pub->usage_restriction = restriction;
+	pr_debug("usage restriction %u\n", restriction);
+	return 0;
+}
diff --git a/crypto/asymmetric_keys/x509_extusage.asn1 b/crypto/asymmetric_keys/x509_extusage.asn1
new file mode 100644
index 000000000000..ffe12ae644b5
--- /dev/null
+++ b/crypto/asymmetric_keys/x509_extusage.asn1
@@ -0,0 +1,3 @@
+ExtKeyUsageSyntax ::= SEQUENCE OF KeyPurposeId
+
+KeyPurposeId ::= OBJECT IDENTIFIER ({ x509_extusage_note_purpose })
diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
index fda097e079a4..d15c74cb1f98 100644
--- a/include/crypto/public_key.h
+++ b/include/crypto/public_key.h
@@ -37,6 +37,17 @@ enum pkey_id_type {
 	PKEY_ID_TYPE__LAST
 };
 
+enum pkey_usage_restriction {
+	PKEY_USAGE_NOT_SPECIFIED,
+	PKEY_RESTRICTED_USAGE,
+	PKEY_RESTRICTED_TO_OTHER,
+	PKEY_RESTRICTED_TO_MODULE_SIGNING,
+	PKEY_RESTRICTED_TO_FIRMWARE_SIGNING,
+	PKEY_RESTRICTED_TO_KEXEC_SIGNING,
+	PKEY_RESTRICTED_TO_KEY_SIGNING,
+	NR__PKEY_USAGE_RESTRICTION
+};
+
 extern const char *const pkey_id_type_name[PKEY_ID_TYPE__LAST];
 
 /*
@@ -52,6 +63,7 @@ struct public_key {
 #define PKEY_CAN_DECRYPT	0x02
 #define PKEY_CAN_SIGN		0x04
 #define PKEY_CAN_VERIFY		0x08
+	enum pkey_usage_restriction usage_restriction : 8;
 	enum pkey_algo pkey_algo : 8;
 	enum pkey_id_type id_type : 8;
 	union {
diff --git a/include/keys/asymmetric-subtype.h b/include/keys/asymmetric-subtype.h
index 4b840e822209..b6a47f09ef2b 100644
--- a/include/keys/asymmetric-subtype.h
+++ b/include/keys/asymmetric-subtype.h
@@ -31,6 +31,9 @@ struct asymmetric_key_subtype {
 	/* Describe a key of this subtype for /proc/keys */
 	void (*describe)(const struct key *key, struct seq_file *m);
 
+	/* Describe capabilities/restrictions of a key of this subtype */
+	void (*describe_caps)(const struct key *key, struct seq_file *m);
+
 	/* Destroy a key of this subtype */
 	void (*destroy)(void *payload);
 
diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h
index db8d382699b5..46b3b10e2dc1 100644
--- a/include/linux/oid_registry.h
+++ b/include/linux/oid_registry.h
@@ -90,6 +90,9 @@ enum OID {
 
 	/* Signing */
 	OID_firmwareName,		/* 1.3.6.1.4.1.2312.99.1 */
+	OID_firmwareSigningOnlyKey,	/* 1.3.6.1.4.1.2312.99.2 */
+	OID_moduleSigningOnlyKey,	/* 1.3.6.1.4.1.2312.99.3 */
+	OID_kexecSigningOnlyKey,	/* 1.3.6.1.4.1.2312.99.4 */
 
 	OID__NR
 };
diff --git a/kernel/Makefile b/kernel/Makefile
index 2d03e870ba8d..1b1aea6fe655 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -239,6 +239,7 @@ x509.genkey:
 	@echo >>x509.genkey "keyUsage=digitalSignature"
 	@echo >>x509.genkey "subjectKeyIdentifier=hash"
 	@echo >>x509.genkey "authorityKeyIdentifier=keyid"
+	@echo >>x509.genkey "extendedKeyUsage=critical,1.3.6.1.4.1.2312.99.3"
 endif
 
 $(eval $(call config_filename,MODULE_SIG_KEY))
diff --git a/lib/oid_registry.c b/lib/oid_registry.c
index 318f382a010d..41df9b4a5fe7 100644
--- a/lib/oid_registry.c
+++ b/lib/oid_registry.c
@@ -115,6 +115,7 @@ int sprint_oid(const void *data, size_t datasize, char *buffer, size_t bufsize)
 	size_t ret;
 	int count;
 
+	buffer[0] = 0;
 	if (v >= end)
 		return -EBADMSG;
 
