blob: 36c4f1bdb5214c718bab68be94922a146f710e8d [file] [log] [blame]
Herbert Xucce9e062006-08-21 21:08:13 +10001/*
2 * Cryptographic API for algorithms (i.e., low-level API).
3 *
4 * Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
10 *
11 */
12
Herbert Xu6bfd4802006-09-21 11:39:29 +100013#include <linux/err.h>
Herbert Xucce9e062006-08-21 21:08:13 +100014#include <linux/errno.h>
15#include <linux/init.h>
16#include <linux/kernel.h>
Herbert Xu4cc77202006-08-06 21:16:34 +100017#include <linux/list.h>
Herbert Xucce9e062006-08-21 21:08:13 +100018#include <linux/module.h>
19#include <linux/string.h>
20
21#include "internal.h"
22
Herbert Xu4cc77202006-08-06 21:16:34 +100023static LIST_HEAD(crypto_template_list);
24
Herbert Xu492e2b62006-09-21 11:35:17 +100025void crypto_larval_error(const char *name, u32 type, u32 mask)
Herbert Xu28259822006-08-06 21:23:26 +100026{
27 struct crypto_alg *alg;
28
29 down_read(&crypto_alg_sem);
Herbert Xu492e2b62006-09-21 11:35:17 +100030 alg = __crypto_alg_lookup(name, type, mask);
Herbert Xu28259822006-08-06 21:23:26 +100031 up_read(&crypto_alg_sem);
32
33 if (alg) {
34 if (crypto_is_larval(alg)) {
35 struct crypto_larval *larval = (void *)alg;
36 complete(&larval->completion);
37 }
38 crypto_mod_put(alg);
39 }
40}
41EXPORT_SYMBOL_GPL(crypto_larval_error);
42
Herbert Xucce9e062006-08-21 21:08:13 +100043static inline int crypto_set_driver_name(struct crypto_alg *alg)
44{
45 static const char suffix[] = "-generic";
46 char *driver_name = alg->cra_driver_name;
47 int len;
48
49 if (*driver_name)
50 return 0;
51
52 len = strlcpy(driver_name, alg->cra_name, CRYPTO_MAX_ALG_NAME);
53 if (len + sizeof(suffix) > CRYPTO_MAX_ALG_NAME)
54 return -ENAMETOOLONG;
55
56 memcpy(driver_name + len, suffix, sizeof(suffix));
57 return 0;
58}
59
Herbert Xu4cc77202006-08-06 21:16:34 +100060static int crypto_check_alg(struct crypto_alg *alg)
Herbert Xucce9e062006-08-21 21:08:13 +100061{
Herbert Xucce9e062006-08-21 21:08:13 +100062 if (alg->cra_alignmask & (alg->cra_alignmask + 1))
63 return -EINVAL;
64
65 if (alg->cra_alignmask & alg->cra_blocksize)
66 return -EINVAL;
67
68 if (alg->cra_blocksize > PAGE_SIZE / 8)
69 return -EINVAL;
70
71 if (alg->cra_priority < 0)
72 return -EINVAL;
Herbert Xucce9e062006-08-21 21:08:13 +100073
Herbert Xu4cc77202006-08-06 21:16:34 +100074 return crypto_set_driver_name(alg);
75}
76
Herbert Xu6bfd4802006-09-21 11:39:29 +100077static void crypto_destroy_instance(struct crypto_alg *alg)
78{
79 struct crypto_instance *inst = (void *)alg;
80 struct crypto_template *tmpl = inst->tmpl;
81
82 tmpl->free(inst);
83 crypto_tmpl_put(tmpl);
84}
85
86static void crypto_remove_spawns(struct list_head *spawns,
87 struct list_head *list)
88{
89 struct crypto_spawn *spawn, *n;
90
91 list_for_each_entry_safe(spawn, n, spawns, list) {
92 struct crypto_instance *inst = spawn->inst;
93 struct crypto_template *tmpl = inst->tmpl;
94
95 list_del_init(&spawn->list);
96 spawn->alg = NULL;
97
98 if (crypto_is_dead(&inst->alg))
99 continue;
100
101 inst->alg.cra_flags |= CRYPTO_ALG_DEAD;
102 if (!tmpl || !crypto_tmpl_get(tmpl))
103 continue;
104
105 crypto_notify(CRYPTO_MSG_ALG_UNREGISTER, &inst->alg);
106 list_move(&inst->alg.cra_list, list);
107 hlist_del(&inst->list);
108 inst->alg.cra_destroy = crypto_destroy_instance;
109
110 if (!list_empty(&inst->alg.cra_users)) {
111 if (&n->list == spawns)
112 n = list_entry(inst->alg.cra_users.next,
113 typeof(*n), list);
114 __list_splice(&inst->alg.cra_users, spawns->prev);
115 }
116 }
117}
118
119static int __crypto_register_alg(struct crypto_alg *alg,
120 struct list_head *list)
Herbert Xu4cc77202006-08-06 21:16:34 +1000121{
122 struct crypto_alg *q;
Herbert Xu6bfd4802006-09-21 11:39:29 +1000123 int ret = -EAGAIN;
124
125 if (crypto_is_dead(alg))
126 goto out;
127
128 INIT_LIST_HEAD(&alg->cra_users);
129
130 ret = -EEXIST;
Herbert Xu4cc77202006-08-06 21:16:34 +1000131
Herbert Xu28259822006-08-06 21:23:26 +1000132 atomic_set(&alg->cra_refcnt, 1);
Herbert Xucce9e062006-08-21 21:08:13 +1000133 list_for_each_entry(q, &crypto_alg_list, cra_list) {
Herbert Xu4cc77202006-08-06 21:16:34 +1000134 if (q == alg)
Herbert Xucce9e062006-08-21 21:08:13 +1000135 goto out;
Herbert Xu6bfd4802006-09-21 11:39:29 +1000136
137 if (crypto_is_moribund(q))
138 continue;
139
140 if (crypto_is_larval(q)) {
Herbert Xu28259822006-08-06 21:23:26 +1000141 struct crypto_larval *larval = (void *)q;
142
Herbert Xu6bfd4802006-09-21 11:39:29 +1000143 if (strcmp(alg->cra_name, q->cra_name) &&
144 strcmp(alg->cra_driver_name, q->cra_name))
145 continue;
146
147 if (larval->adult)
148 continue;
Herbert Xu492e2b62006-09-21 11:35:17 +1000149 if ((q->cra_flags ^ alg->cra_flags) & larval->mask)
150 continue;
Herbert Xu28259822006-08-06 21:23:26 +1000151 if (!crypto_mod_get(alg))
152 continue;
Herbert Xu6bfd4802006-09-21 11:39:29 +1000153
Herbert Xu28259822006-08-06 21:23:26 +1000154 larval->adult = alg;
155 complete(&larval->completion);
Herbert Xu6bfd4802006-09-21 11:39:29 +1000156 continue;
Herbert Xu28259822006-08-06 21:23:26 +1000157 }
Herbert Xu6bfd4802006-09-21 11:39:29 +1000158
159 if (strcmp(alg->cra_name, q->cra_name))
160 continue;
161
162 if (strcmp(alg->cra_driver_name, q->cra_driver_name) &&
163 q->cra_priority > alg->cra_priority)
164 continue;
165
166 crypto_remove_spawns(&q->cra_users, list);
Herbert Xucce9e062006-08-21 21:08:13 +1000167 }
168
169 list_add(&alg->cra_list, &crypto_alg_list);
Herbert Xu28259822006-08-06 21:23:26 +1000170
171 crypto_notify(CRYPTO_MSG_ALG_REGISTER, alg);
Herbert Xu4cc77202006-08-06 21:16:34 +1000172 ret = 0;
Herbert Xu28259822006-08-06 21:23:26 +1000173
Herbert Xucce9e062006-08-21 21:08:13 +1000174out:
Herbert Xucce9e062006-08-21 21:08:13 +1000175 return ret;
176}
Herbert Xu4cc77202006-08-06 21:16:34 +1000177
Herbert Xu6bfd4802006-09-21 11:39:29 +1000178static void crypto_remove_final(struct list_head *list)
179{
180 struct crypto_alg *alg;
181 struct crypto_alg *n;
182
183 list_for_each_entry_safe(alg, n, list, cra_list) {
184 list_del_init(&alg->cra_list);
185 crypto_alg_put(alg);
186 }
187}
188
Herbert Xu4cc77202006-08-06 21:16:34 +1000189int crypto_register_alg(struct crypto_alg *alg)
190{
Herbert Xu6bfd4802006-09-21 11:39:29 +1000191 LIST_HEAD(list);
Herbert Xu4cc77202006-08-06 21:16:34 +1000192 int err;
193
194 err = crypto_check_alg(alg);
195 if (err)
196 return err;
197
198 down_write(&crypto_alg_sem);
Herbert Xu6bfd4802006-09-21 11:39:29 +1000199 err = __crypto_register_alg(alg, &list);
Herbert Xu4cc77202006-08-06 21:16:34 +1000200 up_write(&crypto_alg_sem);
201
Herbert Xu6bfd4802006-09-21 11:39:29 +1000202 crypto_remove_final(&list);
Herbert Xu4cc77202006-08-06 21:16:34 +1000203 return err;
204}
Herbert Xucce9e062006-08-21 21:08:13 +1000205EXPORT_SYMBOL_GPL(crypto_register_alg);
206
Herbert Xu6bfd4802006-09-21 11:39:29 +1000207static int crypto_remove_alg(struct crypto_alg *alg, struct list_head *list)
208{
209 if (unlikely(list_empty(&alg->cra_list)))
210 return -ENOENT;
211
212 alg->cra_flags |= CRYPTO_ALG_DEAD;
213
214 crypto_notify(CRYPTO_MSG_ALG_UNREGISTER, alg);
215 list_del_init(&alg->cra_list);
216 crypto_remove_spawns(&alg->cra_users, list);
217
218 return 0;
219}
220
Herbert Xucce9e062006-08-21 21:08:13 +1000221int crypto_unregister_alg(struct crypto_alg *alg)
222{
Herbert Xu6bfd4802006-09-21 11:39:29 +1000223 int ret;
224 LIST_HEAD(list);
Herbert Xucce9e062006-08-21 21:08:13 +1000225
226 down_write(&crypto_alg_sem);
Herbert Xu6bfd4802006-09-21 11:39:29 +1000227 ret = crypto_remove_alg(alg, &list);
Herbert Xucce9e062006-08-21 21:08:13 +1000228 up_write(&crypto_alg_sem);
229
230 if (ret)
231 return ret;
232
233 BUG_ON(atomic_read(&alg->cra_refcnt) != 1);
234 if (alg->cra_destroy)
235 alg->cra_destroy(alg);
236
Herbert Xu6bfd4802006-09-21 11:39:29 +1000237 crypto_remove_final(&list);
Herbert Xucce9e062006-08-21 21:08:13 +1000238 return 0;
239}
240EXPORT_SYMBOL_GPL(crypto_unregister_alg);
241
Herbert Xu4cc77202006-08-06 21:16:34 +1000242int crypto_register_template(struct crypto_template *tmpl)
243{
244 struct crypto_template *q;
245 int err = -EEXIST;
246
247 down_write(&crypto_alg_sem);
248
249 list_for_each_entry(q, &crypto_template_list, list) {
250 if (q == tmpl)
251 goto out;
252 }
253
254 list_add(&tmpl->list, &crypto_template_list);
Herbert Xu28259822006-08-06 21:23:26 +1000255 crypto_notify(CRYPTO_MSG_TMPL_REGISTER, tmpl);
Herbert Xu4cc77202006-08-06 21:16:34 +1000256 err = 0;
257out:
258 up_write(&crypto_alg_sem);
259 return err;
260}
261EXPORT_SYMBOL_GPL(crypto_register_template);
262
263void crypto_unregister_template(struct crypto_template *tmpl)
264{
265 struct crypto_instance *inst;
266 struct hlist_node *p, *n;
267 struct hlist_head *list;
Herbert Xu6bfd4802006-09-21 11:39:29 +1000268 LIST_HEAD(users);
Herbert Xu4cc77202006-08-06 21:16:34 +1000269
270 down_write(&crypto_alg_sem);
271
272 BUG_ON(list_empty(&tmpl->list));
273 list_del_init(&tmpl->list);
274
275 list = &tmpl->instances;
276 hlist_for_each_entry(inst, p, list, list) {
Herbert Xu6bfd4802006-09-21 11:39:29 +1000277 int err = crypto_remove_alg(&inst->alg, &users);
278 BUG_ON(err);
Herbert Xu4cc77202006-08-06 21:16:34 +1000279 }
280
Herbert Xu28259822006-08-06 21:23:26 +1000281 crypto_notify(CRYPTO_MSG_TMPL_UNREGISTER, tmpl);
282
Herbert Xu4cc77202006-08-06 21:16:34 +1000283 up_write(&crypto_alg_sem);
284
285 hlist_for_each_entry_safe(inst, p, n, list, list) {
286 BUG_ON(atomic_read(&inst->alg.cra_refcnt) != 1);
287 tmpl->free(inst);
288 }
Herbert Xu6bfd4802006-09-21 11:39:29 +1000289 crypto_remove_final(&users);
Herbert Xu4cc77202006-08-06 21:16:34 +1000290}
291EXPORT_SYMBOL_GPL(crypto_unregister_template);
292
293static struct crypto_template *__crypto_lookup_template(const char *name)
294{
295 struct crypto_template *q, *tmpl = NULL;
296
297 down_read(&crypto_alg_sem);
298 list_for_each_entry(q, &crypto_template_list, list) {
299 if (strcmp(q->name, name))
300 continue;
301 if (unlikely(!crypto_tmpl_get(q)))
302 continue;
303
304 tmpl = q;
305 break;
306 }
307 up_read(&crypto_alg_sem);
308
309 return tmpl;
310}
311
312struct crypto_template *crypto_lookup_template(const char *name)
313{
314 return try_then_request_module(__crypto_lookup_template(name), name);
315}
316EXPORT_SYMBOL_GPL(crypto_lookup_template);
317
318int crypto_register_instance(struct crypto_template *tmpl,
319 struct crypto_instance *inst)
320{
Herbert Xu6bfd4802006-09-21 11:39:29 +1000321 LIST_HEAD(list);
Herbert Xu4cc77202006-08-06 21:16:34 +1000322 int err = -EINVAL;
323
324 if (inst->alg.cra_destroy)
325 goto err;
326
327 err = crypto_check_alg(&inst->alg);
328 if (err)
329 goto err;
330
331 inst->alg.cra_module = tmpl->module;
332
333 down_write(&crypto_alg_sem);
334
Herbert Xu6bfd4802006-09-21 11:39:29 +1000335 err = __crypto_register_alg(&inst->alg, &list);
Herbert Xu4cc77202006-08-06 21:16:34 +1000336 if (err)
337 goto unlock;
338
339 hlist_add_head(&inst->list, &tmpl->instances);
340 inst->tmpl = tmpl;
341
342unlock:
343 up_write(&crypto_alg_sem);
344
Herbert Xu6bfd4802006-09-21 11:39:29 +1000345 crypto_remove_final(&list);
346
Herbert Xu4cc77202006-08-06 21:16:34 +1000347err:
348 return err;
349}
350EXPORT_SYMBOL_GPL(crypto_register_instance);
351
Herbert Xu6bfd4802006-09-21 11:39:29 +1000352int crypto_init_spawn(struct crypto_spawn *spawn, struct crypto_alg *alg,
353 struct crypto_instance *inst)
354{
355 int err = -EAGAIN;
356
357 spawn->inst = inst;
358
359 down_write(&crypto_alg_sem);
360 if (!crypto_is_moribund(alg)) {
361 list_add(&spawn->list, &alg->cra_users);
362 spawn->alg = alg;
363 err = 0;
364 }
365 up_write(&crypto_alg_sem);
366
367 return err;
368}
369EXPORT_SYMBOL_GPL(crypto_init_spawn);
370
371void crypto_drop_spawn(struct crypto_spawn *spawn)
372{
373 down_write(&crypto_alg_sem);
374 list_del(&spawn->list);
375 up_write(&crypto_alg_sem);
376}
377EXPORT_SYMBOL_GPL(crypto_drop_spawn);
378
379struct crypto_tfm *crypto_spawn_tfm(struct crypto_spawn *spawn)
380{
381 struct crypto_alg *alg;
382 struct crypto_alg *alg2;
383 struct crypto_tfm *tfm;
384
385 down_read(&crypto_alg_sem);
386 alg = spawn->alg;
387 alg2 = alg;
388 if (alg2)
389 alg2 = crypto_mod_get(alg2);
390 up_read(&crypto_alg_sem);
391
392 if (!alg2) {
393 if (alg)
394 crypto_shoot_alg(alg);
395 return ERR_PTR(-EAGAIN);
396 }
397
398 tfm = __crypto_alloc_tfm(alg, 0);
399 if (IS_ERR(tfm))
400 crypto_mod_put(alg);
401
402 return tfm;
403}
404EXPORT_SYMBOL_GPL(crypto_spawn_tfm);
405
Herbert Xu28259822006-08-06 21:23:26 +1000406int crypto_register_notifier(struct notifier_block *nb)
407{
408 return blocking_notifier_chain_register(&crypto_chain, nb);
409}
410EXPORT_SYMBOL_GPL(crypto_register_notifier);
411
412int crypto_unregister_notifier(struct notifier_block *nb)
413{
414 return blocking_notifier_chain_unregister(&crypto_chain, nb);
415}
416EXPORT_SYMBOL_GPL(crypto_unregister_notifier);
417
Herbert Xucce9e062006-08-21 21:08:13 +1000418static int __init crypto_algapi_init(void)
419{
420 crypto_init_proc();
421 return 0;
422}
423
424static void __exit crypto_algapi_exit(void)
425{
426 crypto_exit_proc();
427}
428
429module_init(crypto_algapi_init);
430module_exit(crypto_algapi_exit);
431
432MODULE_LICENSE("GPL");
433MODULE_DESCRIPTION("Cryptographic algorithms API");