diff options
author | David Howells <dhowells@redhat.com> | 2018-10-09 12:47:46 -0400 |
---|---|---|
committer | James Morris <james.morris@microsoft.com> | 2018-10-26 04:30:46 -0400 |
commit | 3c58b2362ba828ee2970c66c6a6fd7b04fde4413 (patch) | |
tree | 842096971b747ef531d6b4d3767ac85f993eb83f | |
parent | c08fed73712620eb0a19244dbbbbdf00edbe5e47 (diff) |
KEYS: Implement PKCS#8 RSA Private Key parser [ver #2]
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>
Tested-by: Marcel Holtmann <marcel@holtmann.org>
Reviewed-by: Marcel Holtmann <marcel@holtmann.org>
Reviewed-by: Denis Kenzior <denkenz@gmail.com>
Tested-by: Denis Kenzior <denkenz@gmail.com>
Signed-off-by: James Morris <james.morris@microsoft.com>
-rw-r--r-- | Documentation/crypto/asymmetric-keys.txt | 2 | ||||
-rw-r--r-- | crypto/asymmetric_keys/Kconfig | 10 | ||||
-rw-r--r-- | crypto/asymmetric_keys/Makefile | 13 | ||||
-rw-r--r-- | crypto/asymmetric_keys/pkcs8.asn1 | 24 | ||||
-rw-r--r-- | crypto/asymmetric_keys/pkcs8_parser.c | 184 |
5 files changed, 233 insertions, 0 deletions
diff --git a/Documentation/crypto/asymmetric-keys.txt b/Documentation/crypto/asymmetric-keys.txt index deb656ef008b..8763866b11cf 100644 --- a/Documentation/crypto/asymmetric-keys.txt +++ b/Documentation/crypto/asymmetric-keys.txt | |||
@@ -248,6 +248,8 @@ Examples of blob formats for which parsers could be implemented include: | |||
248 | - X.509 ASN.1 stream. | 248 | - X.509 ASN.1 stream. |
249 | - Pointer to TPM key. | 249 | - Pointer to TPM key. |
250 | - Pointer to UEFI key. | 250 | - Pointer to UEFI key. |
251 | - PKCS#8 private key [RFC 5208]. | ||
252 | - PKCS#5 encrypted private key [RFC 2898]. | ||
251 | 253 | ||
252 | During key instantiation each parser in the list is tried until one doesn't | 254 | During key instantiation each parser in the list is tried until one doesn't |
253 | return -EBADMSG. | 255 | return -EBADMSG. |
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index f3702e533ff4..66a7dad7ed3d 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig | |||
@@ -31,6 +31,16 @@ config X509_CERTIFICATE_PARSER | |||
31 | data and provides the ability to instantiate a crypto key from a | 31 | data and provides the ability to instantiate a crypto key from a |
32 | public key packet found inside the certificate. | 32 | public key packet found inside the certificate. |
33 | 33 | ||
34 | config PKCS8_PRIVATE_KEY_PARSER | ||
35 | tristate "PKCS#8 private key parser" | ||
36 | depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE | ||
37 | select ASN1 | ||
38 | select OID_REGISTRY | ||
39 | help | ||
40 | This option provides support for parsing PKCS#8 format blobs for | ||
41 | private key data and provides the ability to instantiate a crypto key | ||
42 | from that data. | ||
43 | |||
34 | config PKCS7_MESSAGE_PARSER | 44 | config PKCS7_MESSAGE_PARSER |
35 | tristate "PKCS#7 message parser" | 45 | tristate "PKCS#7 message parser" |
36 | depends on X509_CERTIFICATE_PARSER | 46 | depends on X509_CERTIFICATE_PARSER |
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index d4b2e1b2dc65..c38424f55b08 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile | |||
@@ -30,6 +30,19 @@ $(obj)/x509.asn1.o: $(obj)/x509.asn1.c $(obj)/x509.asn1.h | |||
30 | $(obj)/x509_akid.asn1.o: $(obj)/x509_akid.asn1.c $(obj)/x509_akid.asn1.h | 30 | $(obj)/x509_akid.asn1.o: $(obj)/x509_akid.asn1.c $(obj)/x509_akid.asn1.h |
31 | 31 | ||
32 | # | 32 | # |
33 | # PKCS#8 private key handling | ||
34 | # | ||
35 | obj-$(CONFIG_PKCS8_PRIVATE_KEY_PARSER) += pkcs8_key_parser.o | ||
36 | pkcs8_key_parser-y := \ | ||
37 | pkcs8.asn1.o \ | ||
38 | pkcs8_parser.o | ||
39 | |||
40 | $(obj)/pkcs8_parser.o: $(obj)/pkcs8.asn1.h | ||
41 | $(obj)/pkcs8-asn1.o: $(obj)/pkcs8.asn1.c $(obj)/pkcs8.asn1.h | ||
42 | |||
43 | clean-files += pkcs8.asn1.c pkcs8.asn1.h | ||
44 | |||
45 | # | ||
33 | # PKCS#7 message handling | 46 | # PKCS#7 message handling |
34 | # | 47 | # |
35 | obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o | 48 | obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.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 @@ | |||
1 | -- | ||
2 | -- This is the unencrypted variant | ||
3 | -- | ||
4 | PrivateKeyInfo ::= SEQUENCE { | ||
5 | version Version, | ||
6 | privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, | ||
7 | privateKey PrivateKey, | ||
8 | attributes [0] IMPLICIT Attributes OPTIONAL | ||
9 | } | ||
10 | |||
11 | Version ::= INTEGER ({ pkcs8_note_version }) | ||
12 | |||
13 | PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier ({ pkcs8_note_algo }) | ||
14 | |||
15 | PrivateKey ::= OCTET STRING ({ pkcs8_note_key }) | ||
16 | |||
17 | Attributes ::= SET OF Attribute | ||
18 | |||
19 | Attribute ::= ANY | ||
20 | |||
21 | AlgorithmIdentifier ::= SEQUENCE { | ||
22 | algorithm OBJECT IDENTIFIER ({ pkcs8_note_OID }), | ||
23 | parameters ANY OPTIONAL | ||
24 | } | ||
diff --git a/crypto/asymmetric_keys/pkcs8_parser.c b/crypto/asymmetric_keys/pkcs8_parser.c new file mode 100644 index 000000000000..5f6a7ecc9765 --- /dev/null +++ b/crypto/asymmetric_keys/pkcs8_parser.c | |||
@@ -0,0 +1,184 @@ | |||
1 | /* PKCS#8 Private Key parser [RFC 5208]. | ||
2 | * | ||
3 | * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public Licence | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the Licence, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #define pr_fmt(fmt) "PKCS8: "fmt | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/export.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/err.h> | ||
18 | #include <linux/oid_registry.h> | ||
19 | #include <keys/asymmetric-subtype.h> | ||
20 | #include <keys/asymmetric-parser.h> | ||
21 | #include <crypto/public_key.h> | ||
22 | #include "pkcs8.asn1.h" | ||
23 | |||
24 | struct pkcs8_parse_context { | ||
25 | struct public_key *pub; | ||
26 | unsigned long data; /* Start of data */ | ||
27 | enum OID last_oid; /* Last OID encountered */ | ||
28 | enum OID algo_oid; /* Algorithm OID */ | ||
29 | u32 key_size; | ||
30 | const void *key; | ||
31 | }; | ||
32 | |||
33 | /* | ||
34 | * Note an OID when we find one for later processing when we know how to | ||
35 | * interpret it. | ||
36 | */ | ||
37 | int pkcs8_note_OID(void *context, size_t hdrlen, | ||
38 | unsigned char tag, | ||
39 | const void *value, size_t vlen) | ||
40 | { | ||
41 | struct pkcs8_parse_context *ctx = context; | ||
42 | |||
43 | ctx->last_oid = look_up_OID(value, vlen); | ||
44 | if (ctx->last_oid == OID__NR) { | ||
45 | char buffer[50]; | ||
46 | |||
47 | sprint_oid(value, vlen, buffer, sizeof(buffer)); | ||
48 | pr_info("Unknown OID: [%lu] %s\n", | ||
49 | (unsigned long)value - ctx->data, buffer); | ||
50 | } | ||
51 | return 0; | ||
52 | } | ||
53 | |||
54 | /* | ||
55 | * Note the version number of the ASN.1 blob. | ||
56 | */ | ||
57 | int pkcs8_note_version(void *context, size_t hdrlen, | ||
58 | unsigned char tag, | ||
59 | const void *value, size_t vlen) | ||
60 | { | ||
61 | if (vlen != 1 || ((const u8 *)value)[0] != 0) { | ||
62 | pr_warn("Unsupported PKCS#8 version\n"); | ||
63 | return -EBADMSG; | ||
64 | } | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | /* | ||
69 | * Note the public algorithm. | ||
70 | */ | ||
71 | int pkcs8_note_algo(void *context, size_t hdrlen, | ||
72 | unsigned char tag, | ||
73 | const void *value, size_t vlen) | ||
74 | { | ||
75 | struct pkcs8_parse_context *ctx = context; | ||
76 | |||
77 | if (ctx->last_oid != OID_rsaEncryption) | ||
78 | return -ENOPKG; | ||
79 | |||
80 | ctx->pub->pkey_algo = "rsa"; | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | /* | ||
85 | * Note the key data of the ASN.1 blob. | ||
86 | */ | ||
87 | int pkcs8_note_key(void *context, size_t hdrlen, | ||
88 | unsigned char tag, | ||
89 | const void *value, size_t vlen) | ||
90 | { | ||
91 | struct pkcs8_parse_context *ctx = context; | ||
92 | |||
93 | ctx->key = value; | ||
94 | ctx->key_size = vlen; | ||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | /* | ||
99 | * Parse a PKCS#8 private key blob. | ||
100 | */ | ||
101 | static struct public_key *pkcs8_parse(const void *data, size_t datalen) | ||
102 | { | ||
103 | struct pkcs8_parse_context ctx; | ||
104 | struct public_key *pub; | ||
105 | long ret; | ||
106 | |||
107 | memset(&ctx, 0, sizeof(ctx)); | ||
108 | |||
109 | ret = -ENOMEM; | ||
110 | ctx.pub = kzalloc(sizeof(struct public_key), GFP_KERNEL); | ||
111 | if (!ctx.pub) | ||
112 | goto error; | ||
113 | |||
114 | ctx.data = (unsigned long)data; | ||
115 | |||
116 | /* Attempt to decode the private key */ | ||
117 | ret = asn1_ber_decoder(&pkcs8_decoder, &ctx, data, datalen); | ||
118 | if (ret < 0) | ||
119 | goto error_decode; | ||
120 | |||
121 | ret = -ENOMEM; | ||
122 | pub = ctx.pub; | ||
123 | pub->key = kmemdup(ctx.key, ctx.key_size, GFP_KERNEL); | ||
124 | if (!pub->key) | ||
125 | goto error_decode; | ||
126 | |||
127 | pub->keylen = ctx.key_size; | ||
128 | pub->key_is_private = true; | ||
129 | return pub; | ||
130 | |||
131 | error_decode: | ||
132 | kfree(ctx.pub); | ||
133 | error: | ||
134 | return ERR_PTR(ret); | ||
135 | } | ||
136 | |||
137 | /* | ||
138 | * Attempt to parse a data blob for a key as a PKCS#8 private key. | ||
139 | */ | ||
140 | static int pkcs8_key_preparse(struct key_preparsed_payload *prep) | ||
141 | { | ||
142 | struct public_key *pub; | ||
143 | |||
144 | pub = pkcs8_parse(prep->data, prep->datalen); | ||
145 | if (IS_ERR(pub)) | ||
146 | return PTR_ERR(pub); | ||
147 | |||
148 | pr_devel("Cert Key Algo: %s\n", pub->pkey_algo); | ||
149 | pub->id_type = "PKCS8"; | ||
150 | |||
151 | /* We're pinning the module by being linked against it */ | ||
152 | __module_get(public_key_subtype.owner); | ||
153 | prep->payload.data[asym_subtype] = &public_key_subtype; | ||
154 | prep->payload.data[asym_key_ids] = NULL; | ||
155 | prep->payload.data[asym_crypto] = pub; | ||
156 | prep->payload.data[asym_auth] = NULL; | ||
157 | prep->quotalen = 100; | ||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | static struct asymmetric_key_parser pkcs8_key_parser = { | ||
162 | .owner = THIS_MODULE, | ||
163 | .name = "pkcs8", | ||
164 | .parse = pkcs8_key_preparse, | ||
165 | }; | ||
166 | |||
167 | /* | ||
168 | * Module stuff | ||
169 | */ | ||
170 | static int __init pkcs8_key_init(void) | ||
171 | { | ||
172 | return register_asymmetric_key_parser(&pkcs8_key_parser); | ||
173 | } | ||
174 | |||
175 | static void __exit pkcs8_key_exit(void) | ||
176 | { | ||
177 | unregister_asymmetric_key_parser(&pkcs8_key_parser); | ||
178 | } | ||
179 | |||
180 | module_init(pkcs8_key_init); | ||
181 | module_exit(pkcs8_key_exit); | ||
182 | |||
183 | MODULE_DESCRIPTION("PKCS#8 certificate parser"); | ||
184 | MODULE_LICENSE("GPL"); | ||