diff options
Diffstat (limited to 'crypto')
-rw-r--r-- | crypto/asymmetric_keys/.gitignore | 1 | ||||
-rw-r--r-- | crypto/asymmetric_keys/Kconfig | 10 | ||||
-rw-r--r-- | crypto/asymmetric_keys/Makefile | 17 | ||||
-rw-r--r-- | crypto/asymmetric_keys/x509.asn1 | 60 | ||||
-rw-r--r-- | crypto/asymmetric_keys/x509_cert_parser.c | 497 | ||||
-rw-r--r-- | crypto/asymmetric_keys/x509_parser.h | 36 | ||||
-rw-r--r-- | crypto/asymmetric_keys/x509_public_key.c | 207 | ||||
-rw-r--r-- | crypto/asymmetric_keys/x509_rsakey.asn1 | 4 |
8 files changed, 832 insertions, 0 deletions
diff --git a/crypto/asymmetric_keys/.gitignore b/crypto/asymmetric_keys/.gitignore new file mode 100644 index 000000000000..ee328374dba8 --- /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 561759d6a65f..6d2c2ea12559 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig | |||
@@ -25,4 +25,14 @@ config PUBLIC_KEY_ALGO_RSA | |||
25 | help | 25 | help |
26 | This option enables support for the RSA algorithm (PKCS#1, RFC3447). | 26 | This option enables support for the RSA algorithm (PKCS#1, RFC3447). |
27 | 27 | ||
28 | config X509_CERTIFICATE_PARSER | ||
29 | tristate "X.509 certificate parser" | ||
30 | depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE | ||
31 | select ASN1 | ||
32 | select OID_REGISTRY | ||
33 | help | ||
34 | This option procides support for parsing X.509 format blobs for key | ||
35 | data and provides the ability to instantiate a crypto key from a | ||
36 | public key packet found inside the certificate. | ||
37 | |||
28 | endif # ASYMMETRIC_KEY_TYPE | 38 | endif # ASYMMETRIC_KEY_TYPE |
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index 7c92691a45eb..0727204aab68 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile | |||
@@ -8,3 +8,20 @@ asymmetric_keys-y := asymmetric_type.o signature.o | |||
8 | 8 | ||
9 | obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o | 9 | obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o |
10 | obj-$(CONFIG_PUBLIC_KEY_ALGO_RSA) += rsa.o | 10 | obj-$(CONFIG_PUBLIC_KEY_ALGO_RSA) += rsa.o |
11 | |||
12 | # | ||
13 | # X.509 Certificate handling | ||
14 | # | ||
15 | obj-$(CONFIG_X509_CERTIFICATE_PARSER) += x509_key_parser.o | ||
16 | x509_key_parser-y := \ | ||
17 | x509-asn1.o \ | ||
18 | x509_rsakey-asn1.o \ | ||
19 | x509_cert_parser.o \ | ||
20 | x509_public_key.o | ||
21 | |||
22 | $(obj)/x509_cert_parser.o: $(obj)/x509-asn1.h $(obj)/x509_rsakey-asn1.h | ||
23 | $(obj)/x509-asn1.o: $(obj)/x509-asn1.c $(obj)/x509-asn1.h | ||
24 | $(obj)/x509_rsakey-asn1.o: $(obj)/x509_rsakey-asn1.c $(obj)/x509_rsakey-asn1.h | ||
25 | |||
26 | clean-files += x509-asn1.c x509-asn1.h | ||
27 | 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 000000000000..bf32b3dff088 --- /dev/null +++ b/crypto/asymmetric_keys/x509.asn1 | |||
@@ -0,0 +1,60 @@ | |||
1 | Certificate ::= SEQUENCE { | ||
2 | tbsCertificate TBSCertificate ({ x509_note_tbs_certificate }), | ||
3 | signatureAlgorithm AlgorithmIdentifier, | ||
4 | signature BIT STRING ({ x509_note_signature }) | ||
5 | } | ||
6 | |||
7 | TBSCertificate ::= SEQUENCE { | ||
8 | version [ 0 ] Version DEFAULT, | ||
9 | serialNumber CertificateSerialNumber, | ||
10 | signature AlgorithmIdentifier ({ x509_note_pkey_algo }), | ||
11 | issuer Name ({ x509_note_issuer }), | ||
12 | validity Validity, | ||
13 | subject Name ({ x509_note_subject }), | ||
14 | subjectPublicKeyInfo SubjectPublicKeyInfo, | ||
15 | issuerUniqueID [ 1 ] IMPLICIT UniqueIdentifier OPTIONAL, | ||
16 | subjectUniqueID [ 2 ] IMPLICIT UniqueIdentifier OPTIONAL, | ||
17 | extensions [ 3 ] Extensions OPTIONAL | ||
18 | } | ||
19 | |||
20 | Version ::= INTEGER | ||
21 | CertificateSerialNumber ::= INTEGER | ||
22 | |||
23 | AlgorithmIdentifier ::= SEQUENCE { | ||
24 | algorithm OBJECT IDENTIFIER ({ x509_note_OID }), | ||
25 | parameters ANY OPTIONAL | ||
26 | } | ||
27 | |||
28 | Name ::= SEQUENCE OF RelativeDistinguishedName | ||
29 | |||
30 | RelativeDistinguishedName ::= SET OF AttributeValueAssertion | ||
31 | |||
32 | AttributeValueAssertion ::= SEQUENCE { | ||
33 | attributeType OBJECT IDENTIFIER ({ x509_note_OID }), | ||
34 | attributeValue ANY ({ x509_extract_name_segment }) | ||
35 | } | ||
36 | |||
37 | Validity ::= SEQUENCE { | ||
38 | notBefore Time ({ x509_note_not_before }), | ||
39 | notAfter Time ({ x509_note_not_after }) | ||
40 | } | ||
41 | |||
42 | Time ::= CHOICE { | ||
43 | utcTime UTCTime, | ||
44 | generalTime GeneralizedTime | ||
45 | } | ||
46 | |||
47 | SubjectPublicKeyInfo ::= SEQUENCE { | ||
48 | algorithm AlgorithmIdentifier, | ||
49 | subjectPublicKey BIT STRING ({ x509_extract_key_data }) | ||
50 | } | ||
51 | |||
52 | UniqueIdentifier ::= BIT STRING | ||
53 | |||
54 | Extensions ::= SEQUENCE OF Extension | ||
55 | |||
56 | Extension ::= SEQUENCE { | ||
57 | extnid OBJECT IDENTIFIER ({ x509_note_OID }), | ||
58 | critical BOOLEAN DEFAULT, | ||
59 | extnValue OCTET STRING ({ x509_process_extension }) | ||
60 | } | ||
diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c new file mode 100644 index 000000000000..8fcac9493b7a --- /dev/null +++ b/crypto/asymmetric_keys/x509_cert_parser.c | |||
@@ -0,0 +1,497 @@ | |||
1 | /* X.509 certificate parser | ||
2 | * | ||
3 | * Copyright (C) 2012 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) "X.509: "fmt | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <linux/err.h> | ||
16 | #include <linux/oid_registry.h> | ||
17 | #include "public_key.h" | ||
18 | #include "x509_parser.h" | ||
19 | #include "x509-asn1.h" | ||
20 | #include "x509_rsakey-asn1.h" | ||
21 | |||
22 | struct x509_parse_context { | ||
23 | struct x509_certificate *cert; /* Certificate being constructed */ | ||
24 | unsigned long data; /* Start of data */ | ||
25 | const void *cert_start; /* Start of cert content */ | ||
26 | const void *key; /* Key data */ | ||
27 | size_t key_size; /* Size of key data */ | ||
28 | enum OID last_oid; /* Last OID encountered */ | ||
29 | enum OID algo_oid; /* Algorithm OID */ | ||
30 | unsigned char nr_mpi; /* Number of MPIs stored */ | ||
31 | u8 o_size; /* Size of organizationName (O) */ | ||
32 | u8 cn_size; /* Size of commonName (CN) */ | ||
33 | u8 email_size; /* Size of emailAddress */ | ||
34 | u16 o_offset; /* Offset of organizationName (O) */ | ||
35 | u16 cn_offset; /* Offset of commonName (CN) */ | ||
36 | u16 email_offset; /* Offset of emailAddress */ | ||
37 | }; | ||
38 | |||
39 | /* | ||
40 | * Free an X.509 certificate | ||
41 | */ | ||
42 | void x509_free_certificate(struct x509_certificate *cert) | ||
43 | { | ||
44 | if (cert) { | ||
45 | public_key_destroy(cert->pub); | ||
46 | kfree(cert->issuer); | ||
47 | kfree(cert->subject); | ||
48 | kfree(cert->fingerprint); | ||
49 | kfree(cert->authority); | ||
50 | kfree(cert); | ||
51 | } | ||
52 | } | ||
53 | |||
54 | /* | ||
55 | * Parse an X.509 certificate | ||
56 | */ | ||
57 | struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) | ||
58 | { | ||
59 | struct x509_certificate *cert; | ||
60 | struct x509_parse_context *ctx; | ||
61 | long ret; | ||
62 | |||
63 | ret = -ENOMEM; | ||
64 | cert = kzalloc(sizeof(struct x509_certificate), GFP_KERNEL); | ||
65 | if (!cert) | ||
66 | goto error_no_cert; | ||
67 | cert->pub = kzalloc(sizeof(struct public_key), GFP_KERNEL); | ||
68 | if (!cert->pub) | ||
69 | goto error_no_ctx; | ||
70 | ctx = kzalloc(sizeof(struct x509_parse_context), GFP_KERNEL); | ||
71 | if (!ctx) | ||
72 | goto error_no_ctx; | ||
73 | |||
74 | ctx->cert = cert; | ||
75 | ctx->data = (unsigned long)data; | ||
76 | |||
77 | /* Attempt to decode the certificate */ | ||
78 | ret = asn1_ber_decoder(&x509_decoder, ctx, data, datalen); | ||
79 | if (ret < 0) | ||
80 | goto error_decode; | ||
81 | |||
82 | /* Decode the public key */ | ||
83 | ret = asn1_ber_decoder(&x509_rsakey_decoder, ctx, | ||
84 | ctx->key, ctx->key_size); | ||
85 | if (ret < 0) | ||
86 | goto error_decode; | ||
87 | |||
88 | kfree(ctx); | ||
89 | return cert; | ||
90 | |||
91 | error_decode: | ||
92 | kfree(ctx); | ||
93 | error_no_ctx: | ||
94 | x509_free_certificate(cert); | ||
95 | error_no_cert: | ||
96 | return ERR_PTR(ret); | ||
97 | } | ||
98 | |||
99 | /* | ||
100 | * Note an OID when we find one for later processing when we know how | ||
101 | * to interpret it. | ||
102 | */ | ||
103 | int x509_note_OID(void *context, size_t hdrlen, | ||
104 | unsigned char tag, | ||
105 | const void *value, size_t vlen) | ||
106 | { | ||
107 | struct x509_parse_context *ctx = context; | ||
108 | |||
109 | ctx->last_oid = look_up_OID(value, vlen); | ||
110 | if (ctx->last_oid == OID__NR) { | ||
111 | char buffer[50]; | ||
112 | sprint_oid(value, vlen, buffer, sizeof(buffer)); | ||
113 | pr_debug("Unknown OID: [%zu] %s\n", | ||
114 | (unsigned long)value - ctx->data, buffer); | ||
115 | } | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | /* | ||
120 | * Save the position of the TBS data so that we can check the signature over it | ||
121 | * later. | ||
122 | */ | ||
123 | int x509_note_tbs_certificate(void *context, size_t hdrlen, | ||
124 | unsigned char tag, | ||
125 | const void *value, size_t vlen) | ||
126 | { | ||
127 | struct x509_parse_context *ctx = context; | ||
128 | |||
129 | pr_debug("x509_note_tbs_certificate(,%zu,%02x,%ld,%zu)!\n", | ||
130 | hdrlen, tag, (unsigned long)value - ctx->data, vlen); | ||
131 | |||
132 | ctx->cert->tbs = value - hdrlen; | ||
133 | ctx->cert->tbs_size = vlen + hdrlen; | ||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | /* | ||
138 | * Record the public key algorithm | ||
139 | */ | ||
140 | int x509_note_pkey_algo(void *context, size_t hdrlen, | ||
141 | unsigned char tag, | ||
142 | const void *value, size_t vlen) | ||
143 | { | ||
144 | struct x509_parse_context *ctx = context; | ||
145 | |||
146 | pr_debug("PubKey Algo: %u\n", ctx->last_oid); | ||
147 | |||
148 | switch (ctx->last_oid) { | ||
149 | case OID_md2WithRSAEncryption: | ||
150 | case OID_md3WithRSAEncryption: | ||
151 | default: | ||
152 | return -ENOPKG; /* Unsupported combination */ | ||
153 | |||
154 | case OID_md4WithRSAEncryption: | ||
155 | ctx->cert->sig_hash_algo = PKEY_HASH_MD5; | ||
156 | ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; | ||
157 | break; | ||
158 | |||
159 | case OID_sha1WithRSAEncryption: | ||
160 | ctx->cert->sig_hash_algo = PKEY_HASH_SHA1; | ||
161 | ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; | ||
162 | break; | ||
163 | |||
164 | case OID_sha256WithRSAEncryption: | ||
165 | ctx->cert->sig_hash_algo = PKEY_HASH_SHA256; | ||
166 | ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; | ||
167 | break; | ||
168 | |||
169 | case OID_sha384WithRSAEncryption: | ||
170 | ctx->cert->sig_hash_algo = PKEY_HASH_SHA384; | ||
171 | ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; | ||
172 | break; | ||
173 | |||
174 | case OID_sha512WithRSAEncryption: | ||
175 | ctx->cert->sig_hash_algo = PKEY_HASH_SHA512; | ||
176 | ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; | ||
177 | break; | ||
178 | |||
179 | case OID_sha224WithRSAEncryption: | ||
180 | ctx->cert->sig_hash_algo = PKEY_HASH_SHA224; | ||
181 | ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; | ||
182 | break; | ||
183 | } | ||
184 | |||
185 | ctx->algo_oid = ctx->last_oid; | ||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | /* | ||
190 | * Note the whereabouts and type of the signature. | ||
191 | */ | ||
192 | int x509_note_signature(void *context, size_t hdrlen, | ||
193 | unsigned char tag, | ||
194 | const void *value, size_t vlen) | ||
195 | { | ||
196 | struct x509_parse_context *ctx = context; | ||
197 | |||
198 | pr_debug("Signature type: %u size %zu\n", ctx->last_oid, vlen); | ||
199 | |||
200 | if (ctx->last_oid != ctx->algo_oid) { | ||
201 | pr_warn("Got cert with pkey (%u) and sig (%u) algorithm OIDs\n", | ||
202 | ctx->algo_oid, ctx->last_oid); | ||
203 | return -EINVAL; | ||
204 | } | ||
205 | |||
206 | ctx->cert->sig = value; | ||
207 | ctx->cert->sig_size = vlen; | ||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | /* | ||
212 | * Note some of the name segments from which we'll fabricate a name. | ||
213 | */ | ||
214 | int x509_extract_name_segment(void *context, size_t hdrlen, | ||
215 | unsigned char tag, | ||
216 | const void *value, size_t vlen) | ||
217 | { | ||
218 | struct x509_parse_context *ctx = context; | ||
219 | |||
220 | switch (ctx->last_oid) { | ||
221 | case OID_commonName: | ||
222 | ctx->cn_size = vlen; | ||
223 | ctx->cn_offset = (unsigned long)value - ctx->data; | ||
224 | break; | ||
225 | case OID_organizationName: | ||
226 | ctx->o_size = vlen; | ||
227 | ctx->o_offset = (unsigned long)value - ctx->data; | ||
228 | break; | ||
229 | case OID_email_address: | ||
230 | ctx->email_size = vlen; | ||
231 | ctx->email_offset = (unsigned long)value - ctx->data; | ||
232 | break; | ||
233 | default: | ||
234 | break; | ||
235 | } | ||
236 | |||
237 | return 0; | ||
238 | } | ||
239 | |||
240 | /* | ||
241 | * Fabricate and save the issuer and subject names | ||
242 | */ | ||
243 | static int x509_fabricate_name(struct x509_parse_context *ctx, size_t hdrlen, | ||
244 | unsigned char tag, | ||
245 | char **_name, size_t vlen) | ||
246 | { | ||
247 | const void *name, *data = (const void *)ctx->data; | ||
248 | size_t namesize; | ||
249 | char *buffer; | ||
250 | |||
251 | if (*_name) | ||
252 | return -EINVAL; | ||
253 | |||
254 | /* Empty name string if no material */ | ||
255 | if (!ctx->cn_size && !ctx->o_size && !ctx->email_size) { | ||
256 | buffer = kmalloc(1, GFP_KERNEL); | ||
257 | if (!buffer) | ||
258 | return -ENOMEM; | ||
259 | buffer[0] = 0; | ||
260 | goto done; | ||
261 | } | ||
262 | |||
263 | if (ctx->cn_size && ctx->o_size) { | ||
264 | /* Consider combining O and CN, but use only the CN if it is | ||
265 | * prefixed by the O, or a significant portion thereof. | ||
266 | */ | ||
267 | namesize = ctx->cn_size; | ||
268 | name = data + ctx->cn_offset; | ||
269 | if (ctx->cn_size >= ctx->o_size && | ||
270 | memcmp(data + ctx->cn_offset, data + ctx->o_offset, | ||
271 | ctx->o_size) == 0) | ||
272 | goto single_component; | ||
273 | if (ctx->cn_size >= 7 && | ||
274 | ctx->o_size >= 7 && | ||
275 | memcmp(data + ctx->cn_offset, data + ctx->o_offset, 7) == 0) | ||
276 | goto single_component; | ||
277 | |||
278 | buffer = kmalloc(ctx->o_size + 2 + ctx->cn_size + 1, | ||
279 | GFP_KERNEL); | ||
280 | if (!buffer) | ||
281 | return -ENOMEM; | ||
282 | |||
283 | memcpy(buffer, | ||
284 | data + ctx->o_offset, ctx->o_size); | ||
285 | buffer[ctx->o_size + 0] = ':'; | ||
286 | buffer[ctx->o_size + 1] = ' '; | ||
287 | memcpy(buffer + ctx->o_size + 2, | ||
288 | data + ctx->cn_offset, ctx->cn_size); | ||
289 | buffer[ctx->o_size + 2 + ctx->cn_size] = 0; | ||
290 | goto done; | ||
291 | |||
292 | } else if (ctx->cn_size) { | ||
293 | namesize = ctx->cn_size; | ||
294 | name = data + ctx->cn_offset; | ||
295 | } else if (ctx->o_size) { | ||
296 | namesize = ctx->o_size; | ||
297 | name = data + ctx->o_offset; | ||
298 | } else { | ||
299 | namesize = ctx->email_size; | ||
300 | name = data + ctx->email_offset; | ||
301 | } | ||
302 | |||
303 | single_component: | ||
304 | buffer = kmalloc(namesize + 1, GFP_KERNEL); | ||
305 | if (!buffer) | ||
306 | return -ENOMEM; | ||
307 | memcpy(buffer, name, namesize); | ||
308 | buffer[namesize] = 0; | ||
309 | |||
310 | done: | ||
311 | *_name = buffer; | ||
312 | ctx->cn_size = 0; | ||
313 | ctx->o_size = 0; | ||
314 | ctx->email_size = 0; | ||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | int x509_note_issuer(void *context, size_t hdrlen, | ||
319 | unsigned char tag, | ||
320 | const void *value, size_t vlen) | ||
321 | { | ||
322 | struct x509_parse_context *ctx = context; | ||
323 | return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->issuer, vlen); | ||
324 | } | ||
325 | |||
326 | int x509_note_subject(void *context, size_t hdrlen, | ||
327 | unsigned char tag, | ||
328 | const void *value, size_t vlen) | ||
329 | { | ||
330 | struct x509_parse_context *ctx = context; | ||
331 | return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->subject, vlen); | ||
332 | } | ||
333 | |||
334 | /* | ||
335 | * Extract the data for the public key algorithm | ||
336 | */ | ||
337 | int x509_extract_key_data(void *context, size_t hdrlen, | ||
338 | unsigned char tag, | ||
339 | const void *value, size_t vlen) | ||
340 | { | ||
341 | struct x509_parse_context *ctx = context; | ||
342 | |||
343 | if (ctx->last_oid != OID_rsaEncryption) | ||
344 | return -ENOPKG; | ||
345 | |||
346 | /* There seems to be an extraneous 0 byte on the front of the data */ | ||
347 | ctx->cert->pkey_algo = PKEY_ALGO_RSA; | ||
348 | ctx->key = value + 1; | ||
349 | ctx->key_size = vlen - 1; | ||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | /* | ||
354 | * Extract a RSA public key value | ||
355 | */ | ||
356 | int rsa_extract_mpi(void *context, size_t hdrlen, | ||
357 | unsigned char tag, | ||
358 | const void *value, size_t vlen) | ||
359 | { | ||
360 | struct x509_parse_context *ctx = context; | ||
361 | MPI mpi; | ||
362 | |||
363 | if (ctx->nr_mpi >= ARRAY_SIZE(ctx->cert->pub->mpi)) { | ||
364 | pr_err("Too many public key MPIs in certificate\n"); | ||
365 | return -EBADMSG; | ||
366 | } | ||
367 | |||
368 | mpi = mpi_read_raw_data(value, vlen); | ||
369 | if (!mpi) | ||
370 | return -ENOMEM; | ||
371 | |||
372 | ctx->cert->pub->mpi[ctx->nr_mpi++] = mpi; | ||
373 | return 0; | ||
374 | } | ||
375 | |||
376 | /* | ||
377 | * Process certificate extensions that are used to qualify the certificate. | ||
378 | */ | ||
379 | int x509_process_extension(void *context, size_t hdrlen, | ||
380 | unsigned char tag, | ||
381 | const void *value, size_t vlen) | ||
382 | { | ||
383 | struct x509_parse_context *ctx = context; | ||
384 | const unsigned char *v = value; | ||
385 | char *f; | ||
386 | int i; | ||
387 | |||
388 | pr_debug("Extension: %u\n", ctx->last_oid); | ||
389 | |||
390 | if (ctx->last_oid == OID_subjectKeyIdentifier) { | ||
391 | /* Get hold of the key fingerprint */ | ||
392 | if (vlen < 3) | ||
393 | return -EBADMSG; | ||
394 | if (v[0] != ASN1_OTS || v[1] != vlen - 2) | ||
395 | return -EBADMSG; | ||
396 | v += 2; | ||
397 | vlen -= 2; | ||
398 | |||
399 | f = kmalloc(vlen * 2 + 1, GFP_KERNEL); | ||
400 | if (!f) | ||
401 | return -ENOMEM; | ||
402 | for (i = 0; i < vlen; i++) | ||
403 | sprintf(f + i * 2, "%02x", v[i]); | ||
404 | pr_debug("fingerprint %s\n", f); | ||
405 | ctx->cert->fingerprint = f; | ||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | if (ctx->last_oid == OID_authorityKeyIdentifier) { | ||
410 | /* Get hold of the CA key fingerprint */ | ||
411 | if (vlen < 5) | ||
412 | return -EBADMSG; | ||
413 | if (v[0] != (ASN1_SEQ | (ASN1_CONS << 5)) || | ||
414 | v[1] != vlen - 2 || | ||
415 | v[2] != (ASN1_CONT << 6) || | ||
416 | v[3] != vlen - 4) | ||
417 | return -EBADMSG; | ||
418 | v += 4; | ||
419 | vlen -= 4; | ||
420 | |||
421 | f = kmalloc(vlen * 2 + 1, GFP_KERNEL); | ||
422 | if (!f) | ||
423 | return -ENOMEM; | ||
424 | for (i = 0; i < vlen; i++) | ||
425 | sprintf(f + i * 2, "%02x", v[i]); | ||
426 | pr_debug("authority %s\n", f); | ||
427 | ctx->cert->authority = f; | ||
428 | return 0; | ||
429 | } | ||
430 | |||
431 | return 0; | ||
432 | } | ||
433 | |||
434 | /* | ||
435 | * Record a certificate time. | ||
436 | */ | ||
437 | static int x509_note_time(time_t *_time, size_t hdrlen, | ||
438 | unsigned char tag, | ||
439 | const unsigned char *value, size_t vlen) | ||
440 | { | ||
441 | unsigned YY, MM, DD, hh, mm, ss; | ||
442 | const unsigned char *p = value; | ||
443 | |||
444 | #define dec2bin(X) ((X) - '0') | ||
445 | #define DD2bin(P) ({ unsigned x = dec2bin(P[0]) * 10 + dec2bin(P[1]); P += 2; x; }) | ||
446 | |||
447 | if (tag == ASN1_UNITIM) { | ||
448 | /* UTCTime: YYMMDDHHMMSSZ */ | ||
449 | if (vlen != 13) | ||
450 | goto unsupported_time; | ||
451 | YY = DD2bin(p); | ||
452 | if (YY > 50) | ||
453 | YY += 1900; | ||
454 | else | ||
455 | YY += 2000; | ||
456 | } else if (tag == ASN1_GENTIM) { | ||
457 | /* GenTime: YYYYMMDDHHMMSSZ */ | ||
458 | if (vlen != 15) | ||
459 | goto unsupported_time; | ||
460 | YY = DD2bin(p) * 100 + DD2bin(p); | ||
461 | } else { | ||
462 | goto unsupported_time; | ||
463 | } | ||
464 | |||
465 | MM = DD2bin(p); | ||
466 | DD = DD2bin(p); | ||
467 | hh = DD2bin(p); | ||
468 | mm = DD2bin(p); | ||
469 | ss = DD2bin(p); | ||
470 | |||
471 | if (*p != 'Z') | ||
472 | goto unsupported_time; | ||
473 | |||
474 | *_time = mktime(YY, MM, DD, hh, mm, ss); | ||
475 | return 0; | ||
476 | |||
477 | unsupported_time: | ||
478 | pr_debug("Got unsupported time [tag %02x]: '%*.*s'\n", | ||
479 | tag, (int)vlen, (int)vlen, value); | ||
480 | return -EBADMSG; | ||
481 | } | ||
482 | |||
483 | int x509_note_not_before(void *context, size_t hdrlen, | ||
484 | unsigned char tag, | ||
485 | const void *value, size_t vlen) | ||
486 | { | ||
487 | struct x509_parse_context *ctx = context; | ||
488 | return x509_note_time(&ctx->cert->valid_from, hdrlen, tag, value, vlen); | ||
489 | } | ||
490 | |||
491 | int x509_note_not_after(void *context, size_t hdrlen, | ||
492 | unsigned char tag, | ||
493 | const void *value, size_t vlen) | ||
494 | { | ||
495 | struct x509_parse_context *ctx = context; | ||
496 | return x509_note_time(&ctx->cert->valid_to, hdrlen, tag, value, vlen); | ||
497 | } | ||
diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h new file mode 100644 index 000000000000..635053f7e962 --- /dev/null +++ b/crypto/asymmetric_keys/x509_parser.h | |||
@@ -0,0 +1,36 @@ | |||
1 | /* X.509 certificate parser internal definitions | ||
2 | * | ||
3 | * Copyright (C) 2012 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 | #include <crypto/public_key.h> | ||
13 | |||
14 | struct x509_certificate { | ||
15 | struct x509_certificate *next; | ||
16 | struct public_key *pub; /* Public key details */ | ||
17 | char *issuer; /* Name of certificate issuer */ | ||
18 | char *subject; /* Name of certificate subject */ | ||
19 | char *fingerprint; /* Key fingerprint as hex */ | ||
20 | char *authority; /* Authority key fingerprint as hex */ | ||
21 | time_t valid_from; | ||
22 | time_t valid_to; | ||
23 | enum pkey_algo pkey_algo : 8; /* Public key algorithm */ | ||
24 | enum pkey_algo sig_pkey_algo : 8; /* Signature public key algorithm */ | ||
25 | enum pkey_hash_algo sig_hash_algo : 8; /* Signature hash algorithm */ | ||
26 | const void *tbs; /* Signed data */ | ||
27 | size_t tbs_size; /* Size of signed data */ | ||
28 | const void *sig; /* Signature data */ | ||
29 | size_t sig_size; /* Size of sigature */ | ||
30 | }; | ||
31 | |||
32 | /* | ||
33 | * x509_cert_parser.c | ||
34 | */ | ||
35 | extern void x509_free_certificate(struct x509_certificate *cert); | ||
36 | 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 000000000000..716917ce0907 --- /dev/null +++ b/crypto/asymmetric_keys/x509_public_key.c | |||
@@ -0,0 +1,207 @@ | |||
1 | /* Instantiate a public key crypto key from an X.509 Certificate | ||
2 | * | ||
3 | * Copyright (C) 2012 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) "X.509: "fmt | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/err.h> | ||
17 | #include <linux/mpi.h> | ||
18 | #include <linux/asn1_decoder.h> | ||
19 | #include <keys/asymmetric-subtype.h> | ||
20 | #include <keys/asymmetric-parser.h> | ||
21 | #include <crypto/hash.h> | ||
22 | #include "asymmetric_keys.h" | ||
23 | #include "public_key.h" | ||
24 | #include "x509_parser.h" | ||
25 | |||
26 | static const | ||
27 | struct public_key_algorithm *x509_public_key_algorithms[PKEY_ALGO__LAST] = { | ||
28 | [PKEY_ALGO_DSA] = NULL, | ||
29 | #if defined(CONFIG_PUBLIC_KEY_ALGO_RSA) || \ | ||
30 | defined(CONFIG_PUBLIC_KEY_ALGO_RSA_MODULE) | ||
31 | [PKEY_ALGO_RSA] = &RSA_public_key_algorithm, | ||
32 | #endif | ||
33 | }; | ||
34 | |||
35 | /* | ||
36 | * Check the signature on a certificate using the provided public key | ||
37 | */ | ||
38 | static int x509_check_signature(const struct public_key *pub, | ||
39 | const struct x509_certificate *cert) | ||
40 | { | ||
41 | struct public_key_signature *sig; | ||
42 | struct crypto_shash *tfm; | ||
43 | struct shash_desc *desc; | ||
44 | size_t digest_size, desc_size; | ||
45 | int ret; | ||
46 | |||
47 | pr_devel("==>%s()\n", __func__); | ||
48 | |||
49 | /* Allocate the hashing algorithm we're going to need and find out how | ||
50 | * big the hash operational data will be. | ||
51 | */ | ||
52 | tfm = crypto_alloc_shash(pkey_hash_algo[cert->sig_hash_algo], 0, 0); | ||
53 | if (IS_ERR(tfm)) | ||
54 | return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); | ||
55 | |||
56 | desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); | ||
57 | digest_size = crypto_shash_digestsize(tfm); | ||
58 | |||
59 | /* We allocate the hash operational data storage on the end of our | ||
60 | * context data. | ||
61 | */ | ||
62 | ret = -ENOMEM; | ||
63 | sig = kzalloc(sizeof(*sig) + desc_size + digest_size, GFP_KERNEL); | ||
64 | if (!sig) | ||
65 | goto error_no_sig; | ||
66 | |||
67 | sig->pkey_hash_algo = cert->sig_hash_algo; | ||
68 | sig->digest = (u8 *)sig + sizeof(*sig) + desc_size; | ||
69 | sig->digest_size = digest_size; | ||
70 | |||
71 | desc = (void *)sig + sizeof(*sig); | ||
72 | desc->tfm = tfm; | ||
73 | desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; | ||
74 | |||
75 | ret = crypto_shash_init(desc); | ||
76 | if (ret < 0) | ||
77 | goto error; | ||
78 | |||
79 | ret = -ENOMEM; | ||
80 | sig->rsa.s = mpi_read_raw_data(cert->sig, cert->sig_size); | ||
81 | if (!sig->rsa.s) | ||
82 | goto error; | ||
83 | |||
84 | ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, sig->digest); | ||
85 | if (ret < 0) | ||
86 | goto error_mpi; | ||
87 | |||
88 | ret = pub->algo->verify_signature(pub, sig); | ||
89 | |||
90 | pr_debug("Cert Verification: %d\n", ret); | ||
91 | |||
92 | error_mpi: | ||
93 | mpi_free(sig->rsa.s); | ||
94 | error: | ||
95 | kfree(sig); | ||
96 | error_no_sig: | ||
97 | crypto_free_shash(tfm); | ||
98 | |||
99 | pr_devel("<==%s() = %d\n", __func__, ret); | ||
100 | return ret; | ||
101 | } | ||
102 | |||
103 | /* | ||
104 | * Attempt to parse a data blob for a key as an X509 certificate. | ||
105 | */ | ||
106 | static int x509_key_preparse(struct key_preparsed_payload *prep) | ||
107 | { | ||
108 | struct x509_certificate *cert; | ||
109 | time_t now; | ||
110 | size_t srlen, sulen; | ||
111 | char *desc = NULL; | ||
112 | int ret; | ||
113 | |||
114 | cert = x509_cert_parse(prep->data, prep->datalen); | ||
115 | if (IS_ERR(cert)) | ||
116 | return PTR_ERR(cert); | ||
117 | |||
118 | pr_devel("Cert Issuer: %s\n", cert->issuer); | ||
119 | pr_devel("Cert Subject: %s\n", cert->subject); | ||
120 | pr_devel("Cert Key Algo: %s\n", pkey_algo[cert->pkey_algo]); | ||
121 | pr_devel("Cert Valid: %lu - %lu\n", cert->valid_from, cert->valid_to); | ||
122 | pr_devel("Cert Signature: %s + %s\n", | ||
123 | pkey_algo[cert->sig_pkey_algo], | ||
124 | pkey_hash_algo[cert->sig_hash_algo]); | ||
125 | |||
126 | if (!cert->fingerprint || !cert->authority) { | ||
127 | pr_warn("Cert for '%s' must have SubjKeyId and AuthKeyId extensions\n", | ||
128 | cert->subject); | ||
129 | ret = -EKEYREJECTED; | ||
130 | goto error_free_cert; | ||
131 | } | ||
132 | |||
133 | now = CURRENT_TIME.tv_sec; | ||
134 | if (now < cert->valid_from) { | ||
135 | pr_warn("Cert %s is not yet valid\n", cert->fingerprint); | ||
136 | ret = -EKEYREJECTED; | ||
137 | goto error_free_cert; | ||
138 | } | ||
139 | if (now >= cert->valid_to) { | ||
140 | pr_warn("Cert %s has expired\n", cert->fingerprint); | ||
141 | ret = -EKEYEXPIRED; | ||
142 | goto error_free_cert; | ||
143 | } | ||
144 | |||
145 | cert->pub->algo = x509_public_key_algorithms[cert->pkey_algo]; | ||
146 | cert->pub->id_type = PKEY_ID_X509; | ||
147 | |||
148 | /* Check the signature on the key */ | ||
149 | if (strcmp(cert->fingerprint, cert->authority) == 0) { | ||
150 | ret = x509_check_signature(cert->pub, cert); | ||
151 | if (ret < 0) | ||
152 | goto error_free_cert; | ||
153 | } | ||
154 | |||
155 | /* Propose a description */ | ||
156 | sulen = strlen(cert->subject); | ||
157 | srlen = strlen(cert->fingerprint); | ||
158 | ret = -ENOMEM; | ||
159 | desc = kmalloc(sulen + 2 + srlen + 1, GFP_KERNEL); | ||
160 | if (!desc) | ||
161 | goto error_free_cert; | ||
162 | memcpy(desc, cert->subject, sulen); | ||
163 | desc[sulen] = ':'; | ||
164 | desc[sulen + 1] = ' '; | ||
165 | memcpy(desc + sulen + 2, cert->fingerprint, srlen); | ||
166 | desc[sulen + 2 + srlen] = 0; | ||
167 | |||
168 | /* We're pinning the module by being linked against it */ | ||
169 | __module_get(public_key_subtype.owner); | ||
170 | prep->type_data[0] = &public_key_subtype; | ||
171 | prep->type_data[1] = cert->fingerprint; | ||
172 | prep->payload = cert->pub; | ||
173 | prep->description = desc; | ||
174 | prep->quotalen = 100; | ||
175 | |||
176 | /* We've finished with the certificate */ | ||
177 | cert->pub = NULL; | ||
178 | cert->fingerprint = NULL; | ||
179 | desc = NULL; | ||
180 | ret = 0; | ||
181 | |||
182 | error_free_cert: | ||
183 | x509_free_certificate(cert); | ||
184 | return ret; | ||
185 | } | ||
186 | |||
187 | static struct asymmetric_key_parser x509_key_parser = { | ||
188 | .owner = THIS_MODULE, | ||
189 | .name = "x509", | ||
190 | .parse = x509_key_preparse, | ||
191 | }; | ||
192 | |||
193 | /* | ||
194 | * Module stuff | ||
195 | */ | ||
196 | static int __init x509_key_init(void) | ||
197 | { | ||
198 | return register_asymmetric_key_parser(&x509_key_parser); | ||
199 | } | ||
200 | |||
201 | static void __exit x509_key_exit(void) | ||
202 | { | ||
203 | unregister_asymmetric_key_parser(&x509_key_parser); | ||
204 | } | ||
205 | |||
206 | module_init(x509_key_init); | ||
207 | 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 000000000000..4ec7cc6532c1 --- /dev/null +++ b/crypto/asymmetric_keys/x509_rsakey.asn1 | |||
@@ -0,0 +1,4 @@ | |||
1 | RSAPublicKey ::= SEQUENCE { | ||
2 | modulus INTEGER ({ rsa_extract_mpi }), -- n | ||
3 | publicExponent INTEGER ({ rsa_extract_mpi }) -- e | ||
4 | } | ||