[CRYPTO] api: Split out low-level API
[safe/jmp/linux-2.6] / crypto / api.c
1 /*
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)
6  * Copyright (c) 2005 Herbert Xu <herbert@gondor.apana.org.au>
7  *
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  */
17
18 #include <linux/errno.h>
19 #include <linux/kernel.h>
20 #include <linux/kmod.h>
21 #include <linux/slab.h>
22 #include <linux/string.h>
23 #include "internal.h"
24
25 LIST_HEAD(crypto_alg_list);
26 EXPORT_SYMBOL_GPL(crypto_alg_list);
27 DECLARE_RWSEM(crypto_alg_sem);
28 EXPORT_SYMBOL_GPL(crypto_alg_sem);
29
30 static inline struct crypto_alg *crypto_alg_get(struct crypto_alg *alg)
31 {
32         atomic_inc(&alg->cra_refcnt);
33         return alg;
34 }
35
36 static inline void crypto_alg_put(struct crypto_alg *alg)
37 {
38         if (atomic_dec_and_test(&alg->cra_refcnt) && alg->cra_destroy)
39                 alg->cra_destroy(alg);
40 }
41
42 static struct crypto_alg *crypto_mod_get(struct crypto_alg *alg)
43 {
44         return try_module_get(alg->cra_module) ? crypto_alg_get(alg) : NULL;
45 }
46
47 static void crypto_mod_put(struct crypto_alg *alg)
48 {
49         crypto_alg_put(alg);
50         module_put(alg->cra_module);
51 }
52
53 static struct crypto_alg *crypto_alg_lookup(const char *name)
54 {
55         struct crypto_alg *q, *alg = NULL;
56         int best = -1;
57
58         if (!name)
59                 return NULL;
60         
61         down_read(&crypto_alg_sem);
62         
63         list_for_each_entry(q, &crypto_alg_list, cra_list) {
64                 int exact, fuzzy;
65
66                 exact = !strcmp(q->cra_driver_name, name);
67                 fuzzy = !strcmp(q->cra_name, name);
68                 if (!exact && !(fuzzy && q->cra_priority > best))
69                         continue;
70
71                 if (unlikely(!crypto_mod_get(q)))
72                         continue;
73
74                 best = q->cra_priority;
75                 if (alg)
76                         crypto_mod_put(alg);
77                 alg = q;
78
79                 if (exact)
80                         break;
81         }
82         
83         up_read(&crypto_alg_sem);
84         return alg;
85 }
86
87 /* A far more intelligent version of this is planned.  For now, just
88  * try an exact match on the name of the algorithm. */
89 static inline struct crypto_alg *crypto_alg_mod_lookup(const char *name)
90 {
91         return try_then_request_module(crypto_alg_lookup(name), name);
92 }
93
94 static int crypto_init_flags(struct crypto_tfm *tfm, u32 flags)
95 {
96         tfm->crt_flags = flags & CRYPTO_TFM_REQ_MASK;
97         flags &= ~CRYPTO_TFM_REQ_MASK;
98         
99         switch (crypto_tfm_alg_type(tfm)) {
100         case CRYPTO_ALG_TYPE_CIPHER:
101                 return crypto_init_cipher_flags(tfm, flags);
102                 
103         case CRYPTO_ALG_TYPE_DIGEST:
104                 return crypto_init_digest_flags(tfm, flags);
105                 
106         case CRYPTO_ALG_TYPE_COMPRESS:
107                 return crypto_init_compress_flags(tfm, flags);
108         
109         default:
110                 break;
111         }
112         
113         BUG();
114         return -EINVAL;
115 }
116
117 static int crypto_init_ops(struct crypto_tfm *tfm)
118 {
119         switch (crypto_tfm_alg_type(tfm)) {
120         case CRYPTO_ALG_TYPE_CIPHER:
121                 return crypto_init_cipher_ops(tfm);
122                 
123         case CRYPTO_ALG_TYPE_DIGEST:
124                 return crypto_init_digest_ops(tfm);
125                 
126         case CRYPTO_ALG_TYPE_COMPRESS:
127                 return crypto_init_compress_ops(tfm);
128         
129         default:
130                 break;
131         }
132         
133         BUG();
134         return -EINVAL;
135 }
136
137 static void crypto_exit_ops(struct crypto_tfm *tfm)
138 {
139         switch (crypto_tfm_alg_type(tfm)) {
140         case CRYPTO_ALG_TYPE_CIPHER:
141                 crypto_exit_cipher_ops(tfm);
142                 break;
143                 
144         case CRYPTO_ALG_TYPE_DIGEST:
145                 crypto_exit_digest_ops(tfm);
146                 break;
147                 
148         case CRYPTO_ALG_TYPE_COMPRESS:
149                 crypto_exit_compress_ops(tfm);
150                 break;
151         
152         default:
153                 BUG();
154                 
155         }
156 }
157
158 static unsigned int crypto_ctxsize(struct crypto_alg *alg, int flags)
159 {
160         unsigned int len;
161
162         switch (alg->cra_flags & CRYPTO_ALG_TYPE_MASK) {
163         default:
164                 BUG();
165
166         case CRYPTO_ALG_TYPE_CIPHER:
167                 len = crypto_cipher_ctxsize(alg, flags);
168                 break;
169                 
170         case CRYPTO_ALG_TYPE_DIGEST:
171                 len = crypto_digest_ctxsize(alg, flags);
172                 break;
173                 
174         case CRYPTO_ALG_TYPE_COMPRESS:
175                 len = crypto_compress_ctxsize(alg, flags);
176                 break;
177         }
178
179         return len + (alg->cra_alignmask & ~(crypto_tfm_ctx_alignment() - 1));
180 }
181
182 struct crypto_tfm *crypto_alloc_tfm(const char *name, u32 flags)
183 {
184         struct crypto_tfm *tfm = NULL;
185         struct crypto_alg *alg;
186         unsigned int tfm_size;
187
188         alg = crypto_alg_mod_lookup(name);
189         if (alg == NULL)
190                 goto out;
191
192         tfm_size = sizeof(*tfm) + crypto_ctxsize(alg, flags);
193         tfm = kzalloc(tfm_size, GFP_KERNEL);
194         if (tfm == NULL)
195                 goto out_put;
196
197         tfm->__crt_alg = alg;
198         
199         if (crypto_init_flags(tfm, flags))
200                 goto out_free_tfm;
201                 
202         if (crypto_init_ops(tfm))
203                 goto out_free_tfm;
204
205         if (alg->cra_init && alg->cra_init(tfm))
206                 goto cra_init_failed;
207
208         goto out;
209
210 cra_init_failed:
211         crypto_exit_ops(tfm);
212 out_free_tfm:
213         kfree(tfm);
214         tfm = NULL;
215 out_put:
216         crypto_mod_put(alg);
217 out:
218         return tfm;
219 }
220
221 void crypto_free_tfm(struct crypto_tfm *tfm)
222 {
223         struct crypto_alg *alg;
224         int size;
225
226         if (unlikely(!tfm))
227                 return;
228
229         alg = tfm->__crt_alg;
230         size = sizeof(*tfm) + alg->cra_ctxsize;
231
232         if (alg->cra_exit)
233                 alg->cra_exit(tfm);
234         crypto_exit_ops(tfm);
235         crypto_mod_put(alg);
236         memset(tfm, 0, size);
237         kfree(tfm);
238 }
239
240 int crypto_alg_available(const char *name, u32 flags)
241 {
242         int ret = 0;
243         struct crypto_alg *alg = crypto_alg_mod_lookup(name);
244         
245         if (alg) {
246                 crypto_mod_put(alg);
247                 ret = 1;
248         }
249         
250         return ret;
251 }
252
253 EXPORT_SYMBOL_GPL(crypto_alloc_tfm);
254 EXPORT_SYMBOL_GPL(crypto_free_tfm);
255 EXPORT_SYMBOL_GPL(crypto_alg_available);