From: David Howells <dhowells@redhat.com>
To: mathew.j.martineau@linux.intel.com, tadeusz.struk@intel.com
Cc: linux-kernel@vger.kernel.org, dhowells@redhat.com,
linux-security-module@vger.kernel.org, keyrings@vger.kernel.org,
linux-crypto@vger.kernel.org, dwmw2@infradead.org
Subject: [RFC PATCH 8/8] KEYS: Implement PKCS#8 RSA Private Key parser [ver 3]
Date: Wed, 11 May 2016 15:22:52 +0100 [thread overview]
Message-ID: <20160511142252.4743.21903.stgit@warthog.procyon.org.uk> (raw)
In-Reply-To: <20160511142152.4743.14414.stgit@warthog.procyon.org.uk>
Implement PKCS#8 RSA Private Key format [RFC 5208] parser for the
asymmetric key type. For the moment, this will only support unencrypted
DER blobs. PEM and decryption can be added later.
PKCS#8 keys can be loaded like this:
openssl pkcs8 -in private_key.pem -topk8 -nocrypt -outform DER | \
keyctl padd asymmetric foo @s
Signed-off-by: David Howells <dhowells@redhat.com>
---
Documentation/crypto/asymmetric-keys.txt | 2
crypto/asymmetric_keys/Kconfig | 10 ++
crypto/asymmetric_keys/Makefile | 13 ++
crypto/asymmetric_keys/pkcs8.asn1 | 24 ++++
crypto/asymmetric_keys/pkcs8_parser.c | 184 ++++++++++++++++++++++++++++++
5 files changed, 233 insertions(+)
create mode 100644 crypto/asymmetric_keys/pkcs8.asn1
create mode 100644 crypto/asymmetric_keys/pkcs8_parser.c
diff --git a/Documentation/crypto/asymmetric-keys.txt b/Documentation/crypto/asymmetric-keys.txt
index 93a071714de7..45e50c5695bf 100644
--- a/Documentation/crypto/asymmetric-keys.txt
+++ b/Documentation/crypto/asymmetric-keys.txt
@@ -254,6 +254,8 @@ Examples of blob formats for which parsers could be implemented include:
- X.509 ASN.1 stream.
- Pointer to TPM key.
- Pointer to UEFI key.
+ - PKCS#8 private key [RFC 5208].
+ - PKCS#5 encrypted private key [RFC 2898].
During key instantiation each parser in the list is tried until one doesn't
return -EBADMSG.
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index e28e912000a7..c145bdf88381 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -29,6 +29,16 @@ config X509_CERTIFICATE_PARSER
data and provides the ability to instantiate a crypto key from a
public key packet found inside the certificate.
+config PKCS8_PRIVATE_KEY_PARSER
+ tristate "PKCS#8 private key parser"
+ depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+ select ASN1
+ select OID_REGISTRY
+ help
+ This option provides support for parsing PKCS#8 format blobs for
+ private key data and provides the ability to instantiate a crypto key
+ from that data.
+
config PKCS7_MESSAGE_PARSER
tristate "PKCS#7 message parser"
depends on X509_CERTIFICATE_PARSER
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index 6516855bec18..417035a53e98 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -34,6 +34,19 @@ clean-files += x509_akid-asn1.c x509_akid-asn1.h
#
# PKCS#7 message handling
#
+obj-$(CONFIG_PKCS8_PRIVATE_KEY_PARSER) += pkcs8_key_parser.o
+pkcs8_key_parser-y := \
+ pkcs8-asn1.o \
+ pkcs8_parser.o
+
+$(obj)/pkcs8_parser.o: $(obj)/pkcs8-asn1.h
+$(obj)/pkcs8-asn1.o: $(obj)/pkcs8-asn1.c $(obj)/pkcs8-asn1.h
+
+clean-files += pkcs8-asn1.c pkcs8-asn1.h
+
+#
+# PKCS#7 message handling
+#
obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o
pkcs7_message-y := \
pkcs7-asn1.o \
diff --git a/crypto/asymmetric_keys/pkcs8.asn1 b/crypto/asymmetric_keys/pkcs8.asn1
new file mode 100644
index 000000000000..702c41a3c713
--- /dev/null
+++ b/crypto/asymmetric_keys/pkcs8.asn1
@@ -0,0 +1,24 @@
+--
+-- This is the unencrypted variant
+--
+PrivateKeyInfo ::= SEQUENCE {
+ version Version,
+ privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
+ privateKey PrivateKey,
+ attributes [0] IMPLICIT Attributes OPTIONAL
+}
+
+Version ::= INTEGER ({ pkcs8_note_version })
+
+PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier ({ pkcs8_note_algo })
+
+PrivateKey ::= OCTET STRING ({ pkcs8_note_key })
+
+Attributes ::= SET OF Attribute
+
+Attribute ::= ANY
+
+AlgorithmIdentifier ::= SEQUENCE {
+ algorithm OBJECT IDENTIFIER ({ pkcs8_note_OID }),
+ parameters ANY OPTIONAL
+}
diff --git a/crypto/asymmetric_keys/pkcs8_parser.c b/crypto/asymmetric_keys/pkcs8_parser.c
new file mode 100644
index 000000000000..26a73c0b7eef
--- /dev/null
+++ b/crypto/asymmetric_keys/pkcs8_parser.c
@@ -0,0 +1,184 @@
+/* PKCS#8 Private Key parser [RFC 5208].
+ *
+ * Copyright (C) 2016 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) "PKCS8: "fmt
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/oid_registry.h>
+#include <keys/asymmetric-subtype.h>
+#include <keys/asymmetric-parser.h>
+#include <crypto/public_key.h>
+#include "pkcs8-asn1.h"
+
+struct pkcs8_parse_context {
+ struct public_key *pub;
+ unsigned long data; /* Start of data */
+ enum OID last_oid; /* Last OID encountered */
+ enum OID algo_oid; /* Algorithm OID */
+ u32 key_size;
+ const void *key;
+};
+
+/*
+ * Note an OID when we find one for later processing when we know how to
+ * interpret it.
+ */
+int pkcs8_note_OID(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs8_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_info("Unknown OID: [%lu] %s\n",
+ (unsigned long)value - ctx->data, buffer);
+ }
+ return 0;
+}
+
+/*
+ * Note the version number of the ASN.1 blob.
+ */
+int pkcs8_note_version(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ if (vlen != 1 || ((const u8 *)value)[0] != 0) {
+ pr_warn("Unsupported PKCS#8 version\n");
+ return -EBADMSG;
+ }
+ return 0;
+}
+
+/*
+ * Note the public algorithm.
+ */
+int pkcs8_note_algo(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs8_parse_context *ctx = context;
+
+ if (ctx->last_oid != OID_rsaEncryption)
+ return -ENOPKG;
+
+ ctx->pub->pkey_algo = "rsa";
+ return 0;
+}
+
+/*
+ * Note the key data of the ASN.1 blob.
+ */
+int pkcs8_note_key(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs8_parse_context *ctx = context;
+
+ ctx->key = value;
+ ctx->key_size = vlen;
+ return 0;
+}
+
+/*
+ * Parse a PKCS#8 private key blob.
+ */
+static struct public_key *pkcs8_parse(const void *data, size_t datalen)
+{
+ struct pkcs8_parse_context ctx;
+ struct public_key *pub;
+ long ret;
+
+ memset(&ctx, 0, sizeof(ctx));
+
+ ret = -ENOMEM;
+ ctx.pub = kzalloc(sizeof(struct public_key), GFP_KERNEL);
+ if (!ctx.pub)
+ goto error;
+
+ ctx.data = (unsigned long)data;
+
+ /* Attempt to decode the private key */
+ ret = asn1_ber_decoder(&pkcs8_decoder, &ctx, data, datalen);
+ if (ret < 0)
+ goto error_decode;
+
+ ret = -ENOMEM;
+ pub = ctx.pub;
+ pub->key = kmemdup(ctx.key, ctx.key_size, GFP_KERNEL);
+ if (!pub->key)
+ goto error_decode;
+
+ pub->keylen = ctx.key_size;
+ pub->key_is_private = true;
+ return pub;
+
+error_decode:
+ kfree(ctx.pub);
+error:
+ return ERR_PTR(ret);
+}
+
+/*
+ * Attempt to parse a data blob for a key as a PKCS#8 private key.
+ */
+static int pkcs8_key_preparse(struct key_preparsed_payload *prep)
+{
+ struct public_key *pub;
+
+ pub = pkcs8_parse(prep->data, prep->datalen);
+ if (IS_ERR(pub))
+ return PTR_ERR(pub);
+
+ pr_devel("Cert Key Algo: %s\n", pub->pkey_algo);
+ pub->id_type = "PKCS8";
+
+ /* 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] = NULL;
+ prep->payload.data[asym_crypto] = pub;
+ prep->payload.data[asym_auth] = NULL;
+ prep->quotalen = 100;
+ return 0;
+}
+
+static struct asymmetric_key_parser pkcs8_key_parser = {
+ .owner = THIS_MODULE,
+ .name = "pkcs8",
+ .parse = pkcs8_key_preparse,
+};
+
+/*
+ * Module stuff
+ */
+static int __init pkcs8_key_init(void)
+{
+ return register_asymmetric_key_parser(&pkcs8_key_parser);
+}
+
+static void __exit pkcs8_key_exit(void)
+{
+ unregister_asymmetric_key_parser(&pkcs8_key_parser);
+}
+
+module_init(pkcs8_key_init);
+module_exit(pkcs8_key_exit);
+
+MODULE_DESCRIPTION("PKCS#8 certificate parser");
+MODULE_LICENSE("GPL");
next prev parent reply other threads:[~2016-05-11 14:22 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-05-11 14:21 [RFC PATCH 0/8] KEYS: keyctl operations for asymmetric keys [ver 3] David Howells
2016-05-11 14:22 ` [RFC PATCH 1/8] KEYS: Provide key type operations for asymmetric key ops " David Howells
2016-05-11 14:22 ` [RFC PATCH 2/8] KEYS: Provide keyctls to drive the new key type ops for asymmetric keys " David Howells
2016-05-11 22:17 ` Mat Martineau
2016-05-12 10:16 ` David Howells
2016-05-12 11:04 ` David Woodhouse
2016-05-11 14:22 ` [RFC PATCH 3/8] KEYS: Provide missing asymmetric key subops for new key type ops " David Howells
2016-05-11 14:22 ` [RFC PATCH 4/8] KEYS: Make the X.509 and PKCS7 parsers supply the sig encoding type " David Howells
2016-05-11 14:22 ` [RFC PATCH 5/8] KEYS: Provide software public key query function " David Howells
2016-05-11 23:50 ` Mat Martineau
2016-05-12 0:17 ` Tadeusz Struk
2016-05-12 10:19 ` David Howells
2016-05-12 17:01 ` Mat Martineau
2016-05-11 14:22 ` [RFC PATCH 6/8] KEYS: Allow the public_key struct to hold a private key " David Howells
2016-05-11 14:22 ` [RFC PATCH 7/8] KEYS: Implement encrypt, decrypt and sign for software asymmetric " David Howells
2016-05-11 14:22 ` David Howells [this message]
2016-05-11 19:11 ` [RFC PATCH 8/8] KEYS: Implement PKCS#8 RSA Private Key parser " David Woodhouse
2016-05-12 0:09 ` Mat Martineau
2016-05-12 10:20 ` David Howells
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20160511142252.4743.21903.stgit@warthog.procyon.org.uk \
--to=dhowells@redhat.com \
--cc=dwmw2@infradead.org \
--cc=keyrings@vger.kernel.org \
--cc=linux-crypto@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=mathew.j.martineau@linux.intel.com \
--cc=tadeusz.struk@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox