1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* Asymmetric public-key cryptography key type
3  *
4  * See Documentation/crypto/asymmetric-keys.txt
5  *
6  * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
7  * Written by David Howells (dhowells@redhat.com)
8  */
9 #ifndef __UBOOT__
10 #include <log.h>
11 #include <dm/devres.h>
12 #include <keys/asymmetric-subtype.h>
13 #include <keys/asymmetric-parser.h>
14 #endif
15 #ifdef __UBOOT__
16 #include <linux/bug.h>
17 #include <linux/compat.h>
18 #include <linux/ctype.h>
19 #include <linux/err.h>
20 #include <linux/printk.h>
21 #include <linux/string.h>
22 #else
23 #include <linux/seq_file.h>
24 #include <linux/module.h>
25 #include <linux/slab.h>
26 #include <linux/ctype.h>
27 #endif
28 #include <crypto/public_key.h>
29 #ifdef __UBOOT__
30 #include <keys/asymmetric-type.h>
31 #else
32 #include <keys/system_keyring.h>
33 #include <keys/user-type.h>
34 #include "asymmetric_keys.h"
35 #endif
36 
37 MODULE_LICENSE("GPL");
38 
39 #ifndef __UBOOT__
40 const char *const key_being_used_for[NR__KEY_BEING_USED_FOR] = {
41 	[VERIFYING_MODULE_SIGNATURE]		= "mod sig",
42 	[VERIFYING_FIRMWARE_SIGNATURE]		= "firmware sig",
43 	[VERIFYING_KEXEC_PE_SIGNATURE]		= "kexec PE sig",
44 	[VERIFYING_KEY_SIGNATURE]		= "key sig",
45 	[VERIFYING_KEY_SELF_SIGNATURE]		= "key self sig",
46 	[VERIFYING_UNSPECIFIED_SIGNATURE]	= "unspec sig",
47 };
48 EXPORT_SYMBOL_GPL(key_being_used_for);
49 
50 static LIST_HEAD(asymmetric_key_parsers);
51 static DECLARE_RWSEM(asymmetric_key_parsers_sem);
52 
53 /**
54  * find_asymmetric_key - Find a key by ID.
55  * @keyring: The keys to search.
56  * @id_0: The first ID to look for or NULL.
57  * @id_1: The second ID to look for or NULL.
58  * @partial: Use partial match if true, exact if false.
59  *
60  * Find a key in the given keyring by identifier.  The preferred identifier is
61  * the id_0 and the fallback identifier is the id_1.  If both are given, the
62  * lookup is by the former, but the latter must also match.
63  */
find_asymmetric_key(struct key * keyring,const struct asymmetric_key_id * id_0,const struct asymmetric_key_id * id_1,bool partial)64 struct key *find_asymmetric_key(struct key *keyring,
65 				const struct asymmetric_key_id *id_0,
66 				const struct asymmetric_key_id *id_1,
67 				bool partial)
68 {
69 	struct key *key;
70 	key_ref_t ref;
71 	const char *lookup;
72 	char *req, *p;
73 	int len;
74 
75 	BUG_ON(!id_0 && !id_1);
76 
77 	if (id_0) {
78 		lookup = id_0->data;
79 		len = id_0->len;
80 	} else {
81 		lookup = id_1->data;
82 		len = id_1->len;
83 	}
84 
85 	/* Construct an identifier "id:<keyid>". */
86 	p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL);
87 	if (!req)
88 		return ERR_PTR(-ENOMEM);
89 
90 	if (partial) {
91 		*p++ = 'i';
92 		*p++ = 'd';
93 	} else {
94 		*p++ = 'e';
95 		*p++ = 'x';
96 	}
97 	*p++ = ':';
98 	p = bin2hex(p, lookup, len);
99 	*p = 0;
100 
101 	pr_debug("Look up: \"%s\"\n", req);
102 
103 	ref = keyring_search(make_key_ref(keyring, 1),
104 			     &key_type_asymmetric, req, true);
105 	if (IS_ERR(ref))
106 		pr_debug("Request for key '%s' err %ld\n", req, PTR_ERR(ref));
107 	kfree(req);
108 
109 	if (IS_ERR(ref)) {
110 		switch (PTR_ERR(ref)) {
111 			/* Hide some search errors */
112 		case -EACCES:
113 		case -ENOTDIR:
114 		case -EAGAIN:
115 			return ERR_PTR(-ENOKEY);
116 		default:
117 			return ERR_CAST(ref);
118 		}
119 	}
120 
121 	key = key_ref_to_ptr(ref);
122 	if (id_0 && id_1) {
123 		const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
124 
125 		if (!kids->id[1]) {
126 			pr_debug("First ID matches, but second is missing\n");
127 			goto reject;
128 		}
129 		if (!asymmetric_key_id_same(id_1, kids->id[1])) {
130 			pr_debug("First ID matches, but second does not\n");
131 			goto reject;
132 		}
133 	}
134 
135 	pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key));
136 	return key;
137 
138 reject:
139 	key_put(key);
140 	return ERR_PTR(-EKEYREJECTED);
141 }
142 EXPORT_SYMBOL_GPL(find_asymmetric_key);
143 #endif /* !__UBOOT__ */
144 
145 /**
146  * asymmetric_key_generate_id: Construct an asymmetric key ID
147  * @val_1: First binary blob
148  * @len_1: Length of first binary blob
149  * @val_2: Second binary blob
150  * @len_2: Length of second binary blob
151  *
152  * Construct an asymmetric key ID from a pair of binary blobs.
153  */
asymmetric_key_generate_id(const void * val_1,size_t len_1,const void * val_2,size_t len_2)154 struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1,
155 						     size_t len_1,
156 						     const void *val_2,
157 						     size_t len_2)
158 {
159 	struct asymmetric_key_id *kid;
160 
161 	kid = kmalloc(sizeof(struct asymmetric_key_id) + len_1 + len_2,
162 		      GFP_KERNEL);
163 	if (!kid)
164 		return ERR_PTR(-ENOMEM);
165 	kid->len = len_1 + len_2;
166 	memcpy(kid->data, val_1, len_1);
167 	memcpy(kid->data + len_1, val_2, len_2);
168 	return kid;
169 }
170 EXPORT_SYMBOL_GPL(asymmetric_key_generate_id);
171 
172 /**
173  * asymmetric_key_id_same - Return true if two asymmetric keys IDs are the same.
174  * @kid_1, @kid_2: The key IDs to compare
175  */
asymmetric_key_id_same(const struct asymmetric_key_id * kid1,const struct asymmetric_key_id * kid2)176 bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1,
177 			    const struct asymmetric_key_id *kid2)
178 {
179 	if (!kid1 || !kid2)
180 		return false;
181 	if (kid1->len != kid2->len)
182 		return false;
183 	return memcmp(kid1->data, kid2->data, kid1->len) == 0;
184 }
185 EXPORT_SYMBOL_GPL(asymmetric_key_id_same);
186 
187 /**
188  * asymmetric_key_id_partial - Return true if two asymmetric keys IDs
189  * partially match
190  * @kid_1, @kid_2: The key IDs to compare
191  */
asymmetric_key_id_partial(const struct asymmetric_key_id * kid1,const struct asymmetric_key_id * kid2)192 bool asymmetric_key_id_partial(const struct asymmetric_key_id *kid1,
193 			       const struct asymmetric_key_id *kid2)
194 {
195 	if (!kid1 || !kid2)
196 		return false;
197 	if (kid1->len < kid2->len)
198 		return false;
199 	return memcmp(kid1->data + (kid1->len - kid2->len),
200 		      kid2->data, kid2->len) == 0;
201 }
202 EXPORT_SYMBOL_GPL(asymmetric_key_id_partial);
203 
204 #ifndef __UBOOT__
205 /**
206  * asymmetric_match_key_ids - Search asymmetric key IDs
207  * @kids: The list of key IDs to check
208  * @match_id: The key ID we're looking for
209  * @match: The match function to use
210  */
asymmetric_match_key_ids(const struct asymmetric_key_ids * kids,const struct asymmetric_key_id * match_id,bool (* match)(const struct asymmetric_key_id * kid1,const struct asymmetric_key_id * kid2))211 static bool asymmetric_match_key_ids(
212 	const struct asymmetric_key_ids *kids,
213 	const struct asymmetric_key_id *match_id,
214 	bool (*match)(const struct asymmetric_key_id *kid1,
215 		      const struct asymmetric_key_id *kid2))
216 {
217 	int i;
218 
219 	if (!kids || !match_id)
220 		return false;
221 	for (i = 0; i < ARRAY_SIZE(kids->id); i++)
222 		if (match(kids->id[i], match_id))
223 			return true;
224 	return false;
225 }
226 
227 /* helper function can be called directly with pre-allocated memory */
__asymmetric_key_hex_to_key_id(const char * id,struct asymmetric_key_id * match_id,size_t hexlen)228 inline int __asymmetric_key_hex_to_key_id(const char *id,
229 				   struct asymmetric_key_id *match_id,
230 				   size_t hexlen)
231 {
232 	match_id->len = hexlen;
233 	return hex2bin(match_id->data, id, hexlen);
234 }
235 
236 /**
237  * asymmetric_key_hex_to_key_id - Convert a hex string into a key ID.
238  * @id: The ID as a hex string.
239  */
asymmetric_key_hex_to_key_id(const char * id)240 struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id)
241 {
242 	struct asymmetric_key_id *match_id;
243 	size_t asciihexlen;
244 	int ret;
245 
246 	if (!*id)
247 		return ERR_PTR(-EINVAL);
248 	asciihexlen = strlen(id);
249 	if (asciihexlen & 1)
250 		return ERR_PTR(-EINVAL);
251 
252 	match_id = kmalloc(sizeof(struct asymmetric_key_id) + asciihexlen / 2,
253 			   GFP_KERNEL);
254 	if (!match_id)
255 		return ERR_PTR(-ENOMEM);
256 	ret = __asymmetric_key_hex_to_key_id(id, match_id, asciihexlen / 2);
257 	if (ret < 0) {
258 		kfree(match_id);
259 		return ERR_PTR(-EINVAL);
260 	}
261 	return match_id;
262 }
263 
264 /*
265  * Match asymmetric keys by an exact match on an ID.
266  */
asymmetric_key_cmp(const struct key * key,const struct key_match_data * match_data)267 static bool asymmetric_key_cmp(const struct key *key,
268 			       const struct key_match_data *match_data)
269 {
270 	const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
271 	const struct asymmetric_key_id *match_id = match_data->preparsed;
272 
273 	return asymmetric_match_key_ids(kids, match_id,
274 					asymmetric_key_id_same);
275 }
276 
277 /*
278  * Match asymmetric keys by a partial match on an IDs.
279  */
asymmetric_key_cmp_partial(const struct key * key,const struct key_match_data * match_data)280 static bool asymmetric_key_cmp_partial(const struct key *key,
281 				       const struct key_match_data *match_data)
282 {
283 	const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
284 	const struct asymmetric_key_id *match_id = match_data->preparsed;
285 
286 	return asymmetric_match_key_ids(kids, match_id,
287 					asymmetric_key_id_partial);
288 }
289 
290 /*
291  * Preparse the match criterion.  If we don't set lookup_type and cmp,
292  * the default will be an exact match on the key description.
293  *
294  * There are some specifiers for matching key IDs rather than by the key
295  * description:
296  *
297  *	"id:<id>" - find a key by partial match on any available ID
298  *	"ex:<id>" - find a key by exact match on any available ID
299  *
300  * These have to be searched by iteration rather than by direct lookup because
301  * the key is hashed according to its description.
302  */
asymmetric_key_match_preparse(struct key_match_data * match_data)303 static int asymmetric_key_match_preparse(struct key_match_data *match_data)
304 {
305 	struct asymmetric_key_id *match_id;
306 	const char *spec = match_data->raw_data;
307 	const char *id;
308 	bool (*cmp)(const struct key *, const struct key_match_data *) =
309 		asymmetric_key_cmp;
310 
311 	if (!spec || !*spec)
312 		return -EINVAL;
313 	if (spec[0] == 'i' &&
314 	    spec[1] == 'd' &&
315 	    spec[2] == ':') {
316 		id = spec + 3;
317 		cmp = asymmetric_key_cmp_partial;
318 	} else if (spec[0] == 'e' &&
319 		   spec[1] == 'x' &&
320 		   spec[2] == ':') {
321 		id = spec + 3;
322 	} else {
323 		goto default_match;
324 	}
325 
326 	match_id = asymmetric_key_hex_to_key_id(id);
327 	if (IS_ERR(match_id))
328 		return PTR_ERR(match_id);
329 
330 	match_data->preparsed = match_id;
331 	match_data->cmp = cmp;
332 	match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE;
333 	return 0;
334 
335 default_match:
336 	return 0;
337 }
338 
339 /*
340  * Free the preparsed the match criterion.
341  */
asymmetric_key_match_free(struct key_match_data * match_data)342 static void asymmetric_key_match_free(struct key_match_data *match_data)
343 {
344 	kfree(match_data->preparsed);
345 }
346 
347 /*
348  * Describe the asymmetric key
349  */
asymmetric_key_describe(const struct key * key,struct seq_file * m)350 static void asymmetric_key_describe(const struct key *key, struct seq_file *m)
351 {
352 	const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
353 	const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
354 	const struct asymmetric_key_id *kid;
355 	const unsigned char *p;
356 	int n;
357 
358 	seq_puts(m, key->description);
359 
360 	if (subtype) {
361 		seq_puts(m, ": ");
362 		subtype->describe(key, m);
363 
364 		if (kids && kids->id[1]) {
365 			kid = kids->id[1];
366 			seq_putc(m, ' ');
367 			n = kid->len;
368 			p = kid->data;
369 			if (n > 4) {
370 				p += n - 4;
371 				n = 4;
372 			}
373 			seq_printf(m, "%*phN", n, p);
374 		}
375 
376 		seq_puts(m, " [");
377 		/* put something here to indicate the key's capabilities */
378 		seq_putc(m, ']');
379 	}
380 }
381 
382 /*
383  * Preparse a asymmetric payload to get format the contents appropriately for the
384  * internal payload to cut down on the number of scans of the data performed.
385  *
386  * We also generate a proposed description from the contents of the key that
387  * can be used to name the key if the user doesn't want to provide one.
388  */
asymmetric_key_preparse(struct key_preparsed_payload * prep)389 static int asymmetric_key_preparse(struct key_preparsed_payload *prep)
390 {
391 	struct asymmetric_key_parser *parser;
392 	int ret;
393 
394 	pr_devel("==>%s()\n", __func__);
395 
396 	if (prep->datalen == 0)
397 		return -EINVAL;
398 
399 	down_read(&asymmetric_key_parsers_sem);
400 
401 	ret = -EBADMSG;
402 	list_for_each_entry(parser, &asymmetric_key_parsers, link) {
403 		pr_debug("Trying parser '%s'\n", parser->name);
404 
405 		ret = parser->parse(prep);
406 		if (ret != -EBADMSG) {
407 			pr_debug("Parser recognised the format (ret %d)\n",
408 				 ret);
409 			break;
410 		}
411 	}
412 
413 	up_read(&asymmetric_key_parsers_sem);
414 	pr_devel("<==%s() = %d\n", __func__, ret);
415 	return ret;
416 }
417 
418 /*
419  * Clean up the key ID list
420  */
asymmetric_key_free_kids(struct asymmetric_key_ids * kids)421 static void asymmetric_key_free_kids(struct asymmetric_key_ids *kids)
422 {
423 	int i;
424 
425 	if (kids) {
426 		for (i = 0; i < ARRAY_SIZE(kids->id); i++)
427 			kfree(kids->id[i]);
428 		kfree(kids);
429 	}
430 }
431 
432 /*
433  * Clean up the preparse data
434  */
asymmetric_key_free_preparse(struct key_preparsed_payload * prep)435 static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
436 {
437 	struct asymmetric_key_subtype *subtype = prep->payload.data[asym_subtype];
438 	struct asymmetric_key_ids *kids = prep->payload.data[asym_key_ids];
439 
440 	pr_devel("==>%s()\n", __func__);
441 
442 	if (subtype) {
443 		subtype->destroy(prep->payload.data[asym_crypto],
444 				 prep->payload.data[asym_auth]);
445 		module_put(subtype->owner);
446 	}
447 	asymmetric_key_free_kids(kids);
448 	kfree(prep->description);
449 }
450 
451 /*
452  * dispose of the data dangling from the corpse of a asymmetric key
453  */
asymmetric_key_destroy(struct key * key)454 static void asymmetric_key_destroy(struct key *key)
455 {
456 	struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
457 	struct asymmetric_key_ids *kids = key->payload.data[asym_key_ids];
458 	void *data = key->payload.data[asym_crypto];
459 	void *auth = key->payload.data[asym_auth];
460 
461 	key->payload.data[asym_crypto] = NULL;
462 	key->payload.data[asym_subtype] = NULL;
463 	key->payload.data[asym_key_ids] = NULL;
464 	key->payload.data[asym_auth] = NULL;
465 
466 	if (subtype) {
467 		subtype->destroy(data, auth);
468 		module_put(subtype->owner);
469 	}
470 
471 	asymmetric_key_free_kids(kids);
472 }
473 
asymmetric_restriction_alloc(key_restrict_link_func_t check,struct key * key)474 static struct key_restriction *asymmetric_restriction_alloc(
475 	key_restrict_link_func_t check,
476 	struct key *key)
477 {
478 	struct key_restriction *keyres =
479 		kzalloc(sizeof(struct key_restriction), GFP_KERNEL);
480 
481 	if (!keyres)
482 		return ERR_PTR(-ENOMEM);
483 
484 	keyres->check = check;
485 	keyres->key = key;
486 	keyres->keytype = &key_type_asymmetric;
487 
488 	return keyres;
489 }
490 
491 /*
492  * look up keyring restrict functions for asymmetric keys
493  */
asymmetric_lookup_restriction(const char * restriction)494 static struct key_restriction *asymmetric_lookup_restriction(
495 	const char *restriction)
496 {
497 	char *restrict_method;
498 	char *parse_buf;
499 	char *next;
500 	struct key_restriction *ret = ERR_PTR(-EINVAL);
501 
502 	if (strcmp("builtin_trusted", restriction) == 0)
503 		return asymmetric_restriction_alloc(
504 			restrict_link_by_builtin_trusted, NULL);
505 
506 	if (strcmp("builtin_and_secondary_trusted", restriction) == 0)
507 		return asymmetric_restriction_alloc(
508 			restrict_link_by_builtin_and_secondary_trusted, NULL);
509 
510 	parse_buf = kstrndup(restriction, PAGE_SIZE, GFP_KERNEL);
511 	if (!parse_buf)
512 		return ERR_PTR(-ENOMEM);
513 
514 	next = parse_buf;
515 	restrict_method = strsep(&next, ":");
516 
517 	if ((strcmp(restrict_method, "key_or_keyring") == 0) && next) {
518 		char *key_text;
519 		key_serial_t serial;
520 		struct key *key;
521 		key_restrict_link_func_t link_fn =
522 			restrict_link_by_key_or_keyring;
523 		bool allow_null_key = false;
524 
525 		key_text = strsep(&next, ":");
526 
527 		if (next) {
528 			if (strcmp(next, "chain") != 0)
529 				goto out;
530 
531 			link_fn = restrict_link_by_key_or_keyring_chain;
532 			allow_null_key = true;
533 		}
534 
535 		if (kstrtos32(key_text, 0, &serial) < 0)
536 			goto out;
537 
538 		if ((serial == 0) && allow_null_key) {
539 			key = NULL;
540 		} else {
541 			key = key_lookup(serial);
542 			if (IS_ERR(key)) {
543 				ret = ERR_CAST(key);
544 				goto out;
545 			}
546 		}
547 
548 		ret = asymmetric_restriction_alloc(link_fn, key);
549 		if (IS_ERR(ret))
550 			key_put(key);
551 	}
552 
553 out:
554 	kfree(parse_buf);
555 	return ret;
556 }
557 
asymmetric_key_eds_op(struct kernel_pkey_params * params,const void * in,void * out)558 int asymmetric_key_eds_op(struct kernel_pkey_params *params,
559 			  const void *in, void *out)
560 {
561 	const struct asymmetric_key_subtype *subtype;
562 	struct key *key = params->key;
563 	int ret;
564 
565 	pr_devel("==>%s()\n", __func__);
566 
567 	if (key->type != &key_type_asymmetric)
568 		return -EINVAL;
569 	subtype = asymmetric_key_subtype(key);
570 	if (!subtype ||
571 	    !key->payload.data[0])
572 		return -EINVAL;
573 	if (!subtype->eds_op)
574 		return -ENOTSUPP;
575 
576 	ret = subtype->eds_op(params, in, out);
577 
578 	pr_devel("<==%s() = %d\n", __func__, ret);
579 	return ret;
580 }
581 
asymmetric_key_verify_signature(struct kernel_pkey_params * params,const void * in,const void * in2)582 static int asymmetric_key_verify_signature(struct kernel_pkey_params *params,
583 					   const void *in, const void *in2)
584 {
585 	struct public_key_signature sig = {
586 		.s_size		= params->in2_len,
587 		.digest_size	= params->in_len,
588 		.encoding	= params->encoding,
589 		.hash_algo	= params->hash_algo,
590 		.digest		= (void *)in,
591 		.s		= (void *)in2,
592 	};
593 
594 	return verify_signature(params->key, &sig);
595 }
596 
597 struct key_type key_type_asymmetric = {
598 	.name			= "asymmetric",
599 	.preparse		= asymmetric_key_preparse,
600 	.free_preparse		= asymmetric_key_free_preparse,
601 	.instantiate		= generic_key_instantiate,
602 	.match_preparse		= asymmetric_key_match_preparse,
603 	.match_free		= asymmetric_key_match_free,
604 	.destroy		= asymmetric_key_destroy,
605 	.describe		= asymmetric_key_describe,
606 	.lookup_restriction	= asymmetric_lookup_restriction,
607 	.asym_query		= query_asymmetric_key,
608 	.asym_eds_op		= asymmetric_key_eds_op,
609 	.asym_verify_signature	= asymmetric_key_verify_signature,
610 };
611 EXPORT_SYMBOL_GPL(key_type_asymmetric);
612 
613 /**
614  * register_asymmetric_key_parser - Register a asymmetric key blob parser
615  * @parser: The parser to register
616  */
register_asymmetric_key_parser(struct asymmetric_key_parser * parser)617 int register_asymmetric_key_parser(struct asymmetric_key_parser *parser)
618 {
619 	struct asymmetric_key_parser *cursor;
620 	int ret;
621 
622 	down_write(&asymmetric_key_parsers_sem);
623 
624 	list_for_each_entry(cursor, &asymmetric_key_parsers, link) {
625 		if (strcmp(cursor->name, parser->name) == 0) {
626 			pr_err("Asymmetric key parser '%s' already registered\n",
627 			       parser->name);
628 			ret = -EEXIST;
629 			goto out;
630 		}
631 	}
632 
633 	list_add_tail(&parser->link, &asymmetric_key_parsers);
634 
635 	pr_notice("Asymmetric key parser '%s' registered\n", parser->name);
636 	ret = 0;
637 
638 out:
639 	up_write(&asymmetric_key_parsers_sem);
640 	return ret;
641 }
642 EXPORT_SYMBOL_GPL(register_asymmetric_key_parser);
643 
644 /**
645  * unregister_asymmetric_key_parser - Unregister a asymmetric key blob parser
646  * @parser: The parser to unregister
647  */
unregister_asymmetric_key_parser(struct asymmetric_key_parser * parser)648 void unregister_asymmetric_key_parser(struct asymmetric_key_parser *parser)
649 {
650 	down_write(&asymmetric_key_parsers_sem);
651 	list_del(&parser->link);
652 	up_write(&asymmetric_key_parsers_sem);
653 
654 	pr_notice("Asymmetric key parser '%s' unregistered\n", parser->name);
655 }
656 EXPORT_SYMBOL_GPL(unregister_asymmetric_key_parser);
657 
658 /*
659  * Module stuff
660  */
asymmetric_key_init(void)661 static int __init asymmetric_key_init(void)
662 {
663 	return register_key_type(&key_type_asymmetric);
664 }
665 
asymmetric_key_cleanup(void)666 static void __exit asymmetric_key_cleanup(void)
667 {
668 	unregister_key_type(&key_type_asymmetric);
669 }
670 
671 module_init(asymmetric_key_init);
672 module_exit(asymmetric_key_cleanup);
673 #endif /* !__UBOOT__ */
674