blob: c922090b484288c5201999ea50bd4b3192d3773c [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
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <linux/errno.h>
Herbert Xu5cb14542005-11-05 16:58:14 +110019#include <linux/kernel.h>
Adrian Bunk176c3652005-07-06 13:53:09 -070020#include <linux/kmod.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include <linux/slab.h>
Herbert Xu5cb14542005-11-05 16:58:14 +110022#include <linux/string.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include "internal.h"
24
25LIST_HEAD(crypto_alg_list);
Herbert Xucce9e062006-08-21 21:08:13 +100026EXPORT_SYMBOL_GPL(crypto_alg_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -070027DECLARE_RWSEM(crypto_alg_sem);
Herbert Xucce9e062006-08-21 21:08:13 +100028EXPORT_SYMBOL_GPL(crypto_alg_sem);
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
Herbert Xu6521f302006-08-06 20:28:44 +100030static inline struct crypto_alg *crypto_alg_get(struct crypto_alg *alg)
Linus Torvalds1da177e2005-04-16 15:20:36 -070031{
Herbert Xu6521f302006-08-06 20:28:44 +100032 atomic_inc(&alg->cra_refcnt);
33 return alg;
Linus Torvalds1da177e2005-04-16 15:20:36 -070034}
35
Herbert Xu6521f302006-08-06 20:28:44 +100036static inline void crypto_alg_put(struct crypto_alg *alg)
Linus Torvalds1da177e2005-04-16 15:20:36 -070037{
Herbert Xu6521f302006-08-06 20:28:44 +100038 if (atomic_dec_and_test(&alg->cra_refcnt) && alg->cra_destroy)
39 alg->cra_destroy(alg);
40}
41
42static 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
47static void crypto_mod_put(struct crypto_alg *alg)
48{
49 crypto_alg_put(alg);
Linus Torvalds1da177e2005-04-16 15:20:36 -070050 module_put(alg->cra_module);
51}
52
Adrian Bunk176c3652005-07-06 13:53:09 -070053static struct crypto_alg *crypto_alg_lookup(const char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -070054{
55 struct crypto_alg *q, *alg = NULL;
Herbert Xu5cb14542005-11-05 16:58:14 +110056 int best = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070057
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) {
Herbert Xu5cb14542005-11-05 16:58:14 +110064 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
Herbert Xu72fa4912006-05-28 09:05:24 +100071 if (unlikely(!crypto_mod_get(q)))
Herbert Xu5cb14542005-11-05 16:58:14 +110072 continue;
73
74 best = q->cra_priority;
75 if (alg)
Herbert Xu72fa4912006-05-28 09:05:24 +100076 crypto_mod_put(alg);
Herbert Xu5cb14542005-11-05 16:58:14 +110077 alg = q;
78
79 if (exact)
Linus Torvalds1da177e2005-04-16 15:20:36 -070080 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 }
82
83 up_read(&crypto_alg_sem);
84 return alg;
85}
86
Adrian Bunk176c3652005-07-06 13:53:09 -070087/* A far more intelligent version of this is planned. For now, just
88 * try an exact match on the name of the algorithm. */
89static 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
Linus Torvalds1da177e2005-04-16 15:20:36 -070094static int crypto_init_flags(struct crypto_tfm *tfm, u32 flags)
95{
Herbert Xu64baf3c2005-09-01 17:43:05 -070096 tfm->crt_flags = flags & CRYPTO_TFM_REQ_MASK;
97 flags &= ~CRYPTO_TFM_REQ_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -070098
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
117static 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
137static 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
Herbert Xufbdae9f2005-07-06 13:53:29 -0700158static 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
Herbert Xuf10b7892006-01-25 22:34:01 +1100179 return len + (alg->cra_alignmask & ~(crypto_tfm_ctx_alignment() - 1));
Herbert Xufbdae9f2005-07-06 13:53:29 -0700180}
181
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182struct crypto_tfm *crypto_alloc_tfm(const char *name, u32 flags)
183{
184 struct crypto_tfm *tfm = NULL;
185 struct crypto_alg *alg;
Herbert Xufbdae9f2005-07-06 13:53:29 -0700186 unsigned int tfm_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187
188 alg = crypto_alg_mod_lookup(name);
189 if (alg == NULL)
190 goto out;
Herbert Xufbdae9f2005-07-06 13:53:29 -0700191
192 tfm_size = sizeof(*tfm) + crypto_ctxsize(alg, flags);
Eric Sesterhennbbeb5632006-03-06 21:42:07 +1100193 tfm = kzalloc(tfm_size, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 if (tfm == NULL)
195 goto out_put;
196
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 tfm->__crt_alg = alg;
198
199 if (crypto_init_flags(tfm, flags))
200 goto out_free_tfm;
201
Herbert Xuc7fc0592006-05-24 13:02:26 +1000202 if (crypto_init_ops(tfm))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 goto out_free_tfm;
Herbert Xuc7fc0592006-05-24 13:02:26 +1000204
205 if (alg->cra_init && alg->cra_init(tfm))
206 goto cra_init_failed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207
208 goto out;
209
Herbert Xuc7fc0592006-05-24 13:02:26 +1000210cra_init_failed:
211 crypto_exit_ops(tfm);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212out_free_tfm:
213 kfree(tfm);
214 tfm = NULL;
215out_put:
Herbert Xu72fa4912006-05-28 09:05:24 +1000216 crypto_mod_put(alg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217out:
218 return tfm;
219}
220
221void crypto_free_tfm(struct crypto_tfm *tfm)
222{
Jesper Juhla61cc442005-07-06 13:54:31 -0700223 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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231
Herbert Xuc7fc0592006-05-24 13:02:26 +1000232 if (alg->cra_exit)
233 alg->cra_exit(tfm);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 crypto_exit_ops(tfm);
Herbert Xu72fa4912006-05-28 09:05:24 +1000235 crypto_mod_put(alg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 memset(tfm, 0, size);
237 kfree(tfm);
238}
239
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240int 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) {
Herbert Xu72fa4912006-05-28 09:05:24 +1000246 crypto_mod_put(alg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 ret = 1;
248 }
249
250 return ret;
251}
252
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253EXPORT_SYMBOL_GPL(crypto_alloc_tfm);
254EXPORT_SYMBOL_GPL(crypto_free_tfm);
255EXPORT_SYMBOL_GPL(crypto_alg_available);