blob: 5994a58ef954e97ffbfcc9f286210439872ad0e4 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Scatterlist Cryptographic API.
3 *
4 * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
5 * Copyright (c) 2002 David S. Miller (davem@redhat.com)
Herbert Xu5cb14542005-11-05 16:58:14 +11006 * Copyright (c) 2005 Herbert Xu <herbert@gondor.apana.org.au>
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 *
8 * Portions derived from Cryptoapi, by Alexander Kjeldaas <astor@fast.no>
9 * and Nettle, by Niels Möller.
10 *
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the Free
13 * Software Foundation; either version 2 of the License, or (at your option)
14 * any later version.
15 *
16 */
Jesper Juhla61cc442005-07-06 13:54:31 -070017
18#include <linux/compiler.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include <linux/init.h>
20#include <linux/crypto.h>
21#include <linux/errno.h>
Herbert Xu5cb14542005-11-05 16:58:14 +110022#include <linux/kernel.h>
Adrian Bunk176c3652005-07-06 13:53:09 -070023#include <linux/kmod.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include <linux/rwsem.h>
25#include <linux/slab.h>
Herbert Xu5cb14542005-11-05 16:58:14 +110026#include <linux/string.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#include "internal.h"
28
29LIST_HEAD(crypto_alg_list);
30DECLARE_RWSEM(crypto_alg_sem);
31
Herbert Xu6521f302006-08-06 20:28:44 +100032static inline struct crypto_alg *crypto_alg_get(struct crypto_alg *alg)
Linus Torvalds1da177e2005-04-16 15:20:36 -070033{
Herbert Xu6521f302006-08-06 20:28:44 +100034 atomic_inc(&alg->cra_refcnt);
35 return alg;
Linus Torvalds1da177e2005-04-16 15:20:36 -070036}
37
Herbert Xu6521f302006-08-06 20:28:44 +100038static inline void crypto_alg_put(struct crypto_alg *alg)
Linus Torvalds1da177e2005-04-16 15:20:36 -070039{
Herbert Xu6521f302006-08-06 20:28:44 +100040 if (atomic_dec_and_test(&alg->cra_refcnt) && alg->cra_destroy)
41 alg->cra_destroy(alg);
42}
43
44static struct crypto_alg *crypto_mod_get(struct crypto_alg *alg)
45{
46 return try_module_get(alg->cra_module) ? crypto_alg_get(alg) : NULL;
47}
48
49static void crypto_mod_put(struct crypto_alg *alg)
50{
51 crypto_alg_put(alg);
Linus Torvalds1da177e2005-04-16 15:20:36 -070052 module_put(alg->cra_module);
53}
54
Adrian Bunk176c3652005-07-06 13:53:09 -070055static struct crypto_alg *crypto_alg_lookup(const char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -070056{
57 struct crypto_alg *q, *alg = NULL;
Herbert Xu5cb14542005-11-05 16:58:14 +110058 int best = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070059
60 if (!name)
61 return NULL;
62
63 down_read(&crypto_alg_sem);
64
65 list_for_each_entry(q, &crypto_alg_list, cra_list) {
Herbert Xu5cb14542005-11-05 16:58:14 +110066 int exact, fuzzy;
67
68 exact = !strcmp(q->cra_driver_name, name);
69 fuzzy = !strcmp(q->cra_name, name);
70 if (!exact && !(fuzzy && q->cra_priority > best))
71 continue;
72
Herbert Xu72fa4912006-05-28 09:05:24 +100073 if (unlikely(!crypto_mod_get(q)))
Herbert Xu5cb14542005-11-05 16:58:14 +110074 continue;
75
76 best = q->cra_priority;
77 if (alg)
Herbert Xu72fa4912006-05-28 09:05:24 +100078 crypto_mod_put(alg);
Herbert Xu5cb14542005-11-05 16:58:14 +110079 alg = q;
80
81 if (exact)
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 }
84
85 up_read(&crypto_alg_sem);
86 return alg;
87}
88
Adrian Bunk176c3652005-07-06 13:53:09 -070089/* A far more intelligent version of this is planned. For now, just
90 * try an exact match on the name of the algorithm. */
91static inline struct crypto_alg *crypto_alg_mod_lookup(const char *name)
92{
93 return try_then_request_module(crypto_alg_lookup(name), name);
94}
95
Linus Torvalds1da177e2005-04-16 15:20:36 -070096static int crypto_init_flags(struct crypto_tfm *tfm, u32 flags)
97{
Herbert Xu64baf3c2005-09-01 17:43:05 -070098 tfm->crt_flags = flags & CRYPTO_TFM_REQ_MASK;
99 flags &= ~CRYPTO_TFM_REQ_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100
101 switch (crypto_tfm_alg_type(tfm)) {
102 case CRYPTO_ALG_TYPE_CIPHER:
103 return crypto_init_cipher_flags(tfm, flags);
104
105 case CRYPTO_ALG_TYPE_DIGEST:
106 return crypto_init_digest_flags(tfm, flags);
107
108 case CRYPTO_ALG_TYPE_COMPRESS:
109 return crypto_init_compress_flags(tfm, flags);
110
111 default:
112 break;
113 }
114
115 BUG();
116 return -EINVAL;
117}
118
119static int crypto_init_ops(struct crypto_tfm *tfm)
120{
121 switch (crypto_tfm_alg_type(tfm)) {
122 case CRYPTO_ALG_TYPE_CIPHER:
123 return crypto_init_cipher_ops(tfm);
124
125 case CRYPTO_ALG_TYPE_DIGEST:
126 return crypto_init_digest_ops(tfm);
127
128 case CRYPTO_ALG_TYPE_COMPRESS:
129 return crypto_init_compress_ops(tfm);
130
131 default:
132 break;
133 }
134
135 BUG();
136 return -EINVAL;
137}
138
139static void crypto_exit_ops(struct crypto_tfm *tfm)
140{
141 switch (crypto_tfm_alg_type(tfm)) {
142 case CRYPTO_ALG_TYPE_CIPHER:
143 crypto_exit_cipher_ops(tfm);
144 break;
145
146 case CRYPTO_ALG_TYPE_DIGEST:
147 crypto_exit_digest_ops(tfm);
148 break;
149
150 case CRYPTO_ALG_TYPE_COMPRESS:
151 crypto_exit_compress_ops(tfm);
152 break;
153
154 default:
155 BUG();
156
157 }
158}
159
Herbert Xufbdae9f2005-07-06 13:53:29 -0700160static unsigned int crypto_ctxsize(struct crypto_alg *alg, int flags)
161{
162 unsigned int len;
163
164 switch (alg->cra_flags & CRYPTO_ALG_TYPE_MASK) {
165 default:
166 BUG();
167
168 case CRYPTO_ALG_TYPE_CIPHER:
169 len = crypto_cipher_ctxsize(alg, flags);
170 break;
171
172 case CRYPTO_ALG_TYPE_DIGEST:
173 len = crypto_digest_ctxsize(alg, flags);
174 break;
175
176 case CRYPTO_ALG_TYPE_COMPRESS:
177 len = crypto_compress_ctxsize(alg, flags);
178 break;
179 }
180
Herbert Xuf10b7892006-01-25 22:34:01 +1100181 return len + (alg->cra_alignmask & ~(crypto_tfm_ctx_alignment() - 1));
Herbert Xufbdae9f2005-07-06 13:53:29 -0700182}
183
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184struct crypto_tfm *crypto_alloc_tfm(const char *name, u32 flags)
185{
186 struct crypto_tfm *tfm = NULL;
187 struct crypto_alg *alg;
Herbert Xufbdae9f2005-07-06 13:53:29 -0700188 unsigned int tfm_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189
190 alg = crypto_alg_mod_lookup(name);
191 if (alg == NULL)
192 goto out;
Herbert Xufbdae9f2005-07-06 13:53:29 -0700193
194 tfm_size = sizeof(*tfm) + crypto_ctxsize(alg, flags);
Eric Sesterhennbbeb5632006-03-06 21:42:07 +1100195 tfm = kzalloc(tfm_size, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 if (tfm == NULL)
197 goto out_put;
198
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 tfm->__crt_alg = alg;
200
201 if (crypto_init_flags(tfm, flags))
202 goto out_free_tfm;
203
Herbert Xuc7fc0592006-05-24 13:02:26 +1000204 if (crypto_init_ops(tfm))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 goto out_free_tfm;
Herbert Xuc7fc0592006-05-24 13:02:26 +1000206
207 if (alg->cra_init && alg->cra_init(tfm))
208 goto cra_init_failed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209
210 goto out;
211
Herbert Xuc7fc0592006-05-24 13:02:26 +1000212cra_init_failed:
213 crypto_exit_ops(tfm);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214out_free_tfm:
215 kfree(tfm);
216 tfm = NULL;
217out_put:
Herbert Xu72fa4912006-05-28 09:05:24 +1000218 crypto_mod_put(alg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219out:
220 return tfm;
221}
222
223void crypto_free_tfm(struct crypto_tfm *tfm)
224{
Jesper Juhla61cc442005-07-06 13:54:31 -0700225 struct crypto_alg *alg;
226 int size;
227
228 if (unlikely(!tfm))
229 return;
230
231 alg = tfm->__crt_alg;
232 size = sizeof(*tfm) + alg->cra_ctxsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233
Herbert Xuc7fc0592006-05-24 13:02:26 +1000234 if (alg->cra_exit)
235 alg->cra_exit(tfm);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 crypto_exit_ops(tfm);
Herbert Xu72fa4912006-05-28 09:05:24 +1000237 crypto_mod_put(alg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 memset(tfm, 0, size);
239 kfree(tfm);
240}
241
Herbert Xu5cb14542005-11-05 16:58:14 +1100242static inline int crypto_set_driver_name(struct crypto_alg *alg)
243{
244 static const char suffix[] = "-generic";
Herbert Xud913ea02006-05-21 08:45:26 +1000245 char *driver_name = alg->cra_driver_name;
Herbert Xu5cb14542005-11-05 16:58:14 +1100246 int len;
247
248 if (*driver_name)
249 return 0;
250
251 len = strlcpy(driver_name, alg->cra_name, CRYPTO_MAX_ALG_NAME);
252 if (len + sizeof(suffix) > CRYPTO_MAX_ALG_NAME)
253 return -ENAMETOOLONG;
254
255 memcpy(driver_name + len, suffix, sizeof(suffix));
256 return 0;
257}
258
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259int crypto_register_alg(struct crypto_alg *alg)
260{
Herbert Xu5cb14542005-11-05 16:58:14 +1100261 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 struct crypto_alg *q;
Herbert Xu95477372005-07-06 13:52:09 -0700263
264 if (alg->cra_alignmask & (alg->cra_alignmask + 1))
265 return -EINVAL;
266
Herbert Xu1b40efd2005-10-03 15:15:36 +1000267 if (alg->cra_alignmask & alg->cra_blocksize)
268 return -EINVAL;
269
Herbert Xu73025332006-01-07 16:24:15 +1100270 if (alg->cra_blocksize > PAGE_SIZE / 8)
Herbert Xu95477372005-07-06 13:52:09 -0700271 return -EINVAL;
Herbert Xu5cb14542005-11-05 16:58:14 +1100272
273 if (alg->cra_priority < 0)
274 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275
Herbert Xu5cb14542005-11-05 16:58:14 +1100276 ret = crypto_set_driver_name(alg);
277 if (unlikely(ret))
278 return ret;
279
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 down_write(&crypto_alg_sem);
281
282 list_for_each_entry(q, &crypto_alg_list, cra_list) {
Herbert Xu996e2522006-05-21 11:57:20 +1000283 if (q == alg) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 ret = -EEXIST;
285 goto out;
286 }
287 }
288
Herbert Xu996e2522006-05-21 11:57:20 +1000289 list_add(&alg->cra_list, &crypto_alg_list);
Herbert Xu6521f302006-08-06 20:28:44 +1000290 atomic_set(&alg->cra_refcnt, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291out:
292 up_write(&crypto_alg_sem);
293 return ret;
294}
295
296int crypto_unregister_alg(struct crypto_alg *alg)
297{
298 int ret = -ENOENT;
299 struct crypto_alg *q;
300
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 down_write(&crypto_alg_sem);
302 list_for_each_entry(q, &crypto_alg_list, cra_list) {
303 if (alg == q) {
304 list_del(&alg->cra_list);
305 ret = 0;
306 goto out;
307 }
308 }
309out:
310 up_write(&crypto_alg_sem);
Herbert Xu6521f302006-08-06 20:28:44 +1000311
312 if (ret)
313 return ret;
314
315 BUG_ON(atomic_read(&alg->cra_refcnt) != 1);
316 if (alg->cra_destroy)
317 alg->cra_destroy(alg);
318
319 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320}
321
322int crypto_alg_available(const char *name, u32 flags)
323{
324 int ret = 0;
325 struct crypto_alg *alg = crypto_alg_mod_lookup(name);
326
327 if (alg) {
Herbert Xu72fa4912006-05-28 09:05:24 +1000328 crypto_mod_put(alg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 ret = 1;
330 }
331
332 return ret;
333}
334
335static int __init init_crypto(void)
336{
337 printk(KERN_INFO "Initializing Cryptographic API\n");
338 crypto_init_proc();
339 return 0;
340}
341
342__initcall(init_crypto);
343
344EXPORT_SYMBOL_GPL(crypto_register_alg);
345EXPORT_SYMBOL_GPL(crypto_unregister_alg);
346EXPORT_SYMBOL_GPL(crypto_alloc_tfm);
347EXPORT_SYMBOL_GPL(crypto_free_tfm);
348EXPORT_SYMBOL_GPL(crypto_alg_available);