aboutsummaryrefslogtreecommitdiffstats
path: root/crypto/asymmetric_keys
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2012-09-24 12:11:48 -0400
committerRusty Russell <rusty@rustcorp.com.au>2012-10-07 23:20:22 -0400
commitc26fd69fa00916a31a47f5f096fd7be924106df8 (patch)
tree842075a43c3587ab0a93212b7f96563f616d6c33 /crypto/asymmetric_keys
parente1045992949160b56309b730b8bdc428f2f8b69e (diff)
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>
Diffstat (limited to 'crypto/asymmetric_keys')
-rw-r--r--crypto/asymmetric_keys/.gitignore1
-rw-r--r--crypto/asymmetric_keys/Kconfig10
-rw-r--r--crypto/asymmetric_keys/Makefile17
-rw-r--r--crypto/asymmetric_keys/x509.asn160
-rw-r--r--crypto/asymmetric_keys/x509_cert_parser.c497
-rw-r--r--crypto/asymmetric_keys/x509_parser.h36
-rw-r--r--crypto/asymmetric_keys/x509_public_key.c207
-rw-r--r--crypto/asymmetric_keys/x509_rsakey.asn14
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
28config 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
28endif # ASYMMETRIC_KEY_TYPE 38endif # 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
9obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o 9obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o
10obj-$(CONFIG_PUBLIC_KEY_ALGO_RSA) += rsa.o 10obj-$(CONFIG_PUBLIC_KEY_ALGO_RSA) += rsa.o
11
12#
13# X.509 Certificate handling
14#
15obj-$(CONFIG_X509_CERTIFICATE_PARSER) += x509_key_parser.o
16x509_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
26clean-files += x509-asn1.c x509-asn1.h
27clean-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 @@
1Certificate ::= SEQUENCE {
2 tbsCertificate TBSCertificate ({ x509_note_tbs_certificate }),
3 signatureAlgorithm AlgorithmIdentifier,
4 signature BIT STRING ({ x509_note_signature })
5 }
6
7TBSCertificate ::= 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
20Version ::= INTEGER
21CertificateSerialNumber ::= INTEGER
22
23AlgorithmIdentifier ::= SEQUENCE {
24 algorithm OBJECT IDENTIFIER ({ x509_note_OID }),
25 parameters ANY OPTIONAL
26}
27
28Name ::= SEQUENCE OF RelativeDistinguishedName
29
30RelativeDistinguishedName ::= SET OF AttributeValueAssertion
31
32AttributeValueAssertion ::= SEQUENCE {
33 attributeType OBJECT IDENTIFIER ({ x509_note_OID }),
34 attributeValue ANY ({ x509_extract_name_segment })
35 }
36
37Validity ::= SEQUENCE {
38 notBefore Time ({ x509_note_not_before }),
39 notAfter Time ({ x509_note_not_after })
40 }
41
42Time ::= CHOICE {
43 utcTime UTCTime,
44 generalTime GeneralizedTime
45 }
46
47SubjectPublicKeyInfo ::= SEQUENCE {
48 algorithm AlgorithmIdentifier,
49 subjectPublicKey BIT STRING ({ x509_extract_key_data })
50 }
51
52UniqueIdentifier ::= BIT STRING
53
54Extensions ::= SEQUENCE OF Extension
55
56Extension ::= 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
22struct 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 */
42void 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 */
57struct 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
91error_decode:
92 kfree(ctx);
93error_no_ctx:
94 x509_free_certificate(cert);
95error_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 */
103int 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 */
123int 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 */
140int 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 */
192int 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 */
214int 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 */
243static 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
303single_component:
304 buffer = kmalloc(namesize + 1, GFP_KERNEL);
305 if (!buffer)
306 return -ENOMEM;
307 memcpy(buffer, name, namesize);
308 buffer[namesize] = 0;
309
310done:
311 *_name = buffer;
312 ctx->cn_size = 0;
313 ctx->o_size = 0;
314 ctx->email_size = 0;
315 return 0;
316}
317
318int 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
326int 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 */
337int 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 */
356int 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 */
379int 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 */
437static 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
477unsupported_time:
478 pr_debug("Got unsupported time [tag %02x]: '%*.*s'\n",
479 tag, (int)vlen, (int)vlen, value);
480 return -EBADMSG;
481}
482
483int 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
491int 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
14struct 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 */
35extern void x509_free_certificate(struct x509_certificate *cert);
36extern 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
26static const
27struct 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 */
38static 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
92error_mpi:
93 mpi_free(sig->rsa.s);
94error:
95 kfree(sig);
96error_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 */
106static 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
182error_free_cert:
183 x509_free_certificate(cert);
184 return ret;
185}
186
187static 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 */
196static int __init x509_key_init(void)
197{
198 return register_asymmetric_key_parser(&x509_key_parser);
199}
200
201static void __exit x509_key_exit(void)
202{
203 unregister_asymmetric_key_parser(&x509_key_parser);
204}
205
206module_init(x509_key_init);
207module_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 @@
1RSAPublicKey ::= SEQUENCE {
2 modulus INTEGER ({ rsa_extract_mpi }), -- n
3 publicExponent INTEGER ({ rsa_extract_mpi }) -- e
4 }