implement asynchronous operation mode
[cryptodev-linux.git] / cryptodev_cipher.c
blobb214aee878744f594ce9a6d499a1460ad9c45868
1 /*
2 * Driver for /dev/crypto device (aka CryptoDev)
4 * Copyright (c) 2010 Nikos Mavrogiannopoulos <nmav@gnutls.org>
5 * Portions Copyright (c) 2010 Michael Weiser
6 * Portions Copyright (c) 2010 Phil Sutter
8 * This file is part of linux cryptodev.
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 #include <linux/crypto.h>
27 #include <linux/mm.h>
28 #include <linux/highmem.h>
29 #include <linux/ioctl.h>
30 #include <linux/random.h>
31 #include <linux/scatterlist.h>
32 #include <linux/uaccess.h>
33 #include <crypto/algapi.h>
34 #include <crypto/hash.h>
35 #include "cryptodev.h"
36 #include "cryptodev_int.h"
39 struct cryptodev_result {
40 struct completion completion;
41 int err;
44 static void cryptodev_complete(struct crypto_async_request *req, int err)
46 struct cryptodev_result *res = req->data;
48 if (err == -EINPROGRESS)
49 return;
51 res->err = err;
52 complete(&res->completion);
55 int cryptodev_cipher_init(struct cipher_data *out, const char *alg_name,
56 uint8_t *keyp, size_t keylen)
58 struct ablkcipher_alg *alg;
59 int ret;
61 memset(out, 0, sizeof(*out));
63 out->async.s = crypto_alloc_ablkcipher(alg_name, 0, 0);
64 if (unlikely(IS_ERR(out->async.s))) {
65 dprintk(1, KERN_DEBUG, "%s: Failed to load cipher %s\n",
66 __func__, alg_name);
67 return -EINVAL;
70 alg = crypto_ablkcipher_alg(out->async.s);
72 if (alg != NULL) {
73 /* Was correct key length supplied? */
74 if (alg->max_keysize > 0 &&
75 unlikely((keylen < alg->min_keysize) ||
76 (keylen > alg->max_keysize))) {
77 dprintk(1, KERN_DEBUG,
78 "Wrong keylen '%zu' for algorithm '%s'. \
79 Use %u to %u.\n",
80 keylen, alg_name, alg->min_keysize,
81 alg->max_keysize);
82 ret = -EINVAL;
83 goto error;
87 ret = crypto_ablkcipher_setkey(out->async.s, keyp, keylen);
88 if (unlikely(ret)) {
89 dprintk(1, KERN_DEBUG, "Setting key failed for %s-%zu.\n",
90 alg_name, keylen*8);
91 ret = -EINVAL;
92 goto error;
95 out->blocksize = crypto_ablkcipher_blocksize(out->async.s);
96 out->ivsize = crypto_ablkcipher_ivsize(out->async.s);
98 out->async.result = kmalloc(sizeof(*out->async.result), GFP_KERNEL);
99 if (unlikely(!out->async.result)) {
100 ret = -ENOMEM;
101 goto error;
104 memset(out->async.result, 0, sizeof(*out->async.result));
105 init_completion(&out->async.result->completion);
107 out->async.request = ablkcipher_request_alloc(out->async.s, GFP_KERNEL);
108 if (unlikely(!out->async.request)) {
109 dprintk(1, KERN_ERR, "error allocating async crypto request\n");
110 ret = -ENOMEM;
111 goto error;
114 ablkcipher_request_set_callback(out->async.request,
115 CRYPTO_TFM_REQ_MAY_BACKLOG,
116 cryptodev_complete, out->async.result);
118 out->init = 1;
119 return 0;
120 error:
121 if (out->async.request)
122 ablkcipher_request_free(out->async.request);
123 kfree(out->async.result);
124 if (out->async.s)
125 crypto_free_ablkcipher(out->async.s);
127 return ret;
130 void cryptodev_cipher_deinit(struct cipher_data *cdata)
132 if (cdata->init) {
133 if (cdata->async.request)
134 ablkcipher_request_free(cdata->async.request);
135 kfree(cdata->async.result);
136 if (cdata->async.s)
137 crypto_free_ablkcipher(cdata->async.s);
139 cdata->init = 0;
143 void cryptodev_cipher_set_iv(struct cipher_data *cdata,
144 void *iv, size_t iv_size)
146 memcpy(cdata->async.iv, iv, min(iv_size, sizeof(cdata->async.iv)));
149 static inline int waitfor(struct cryptodev_result *cr, ssize_t ret)
151 switch (ret) {
152 case 0:
153 break;
154 case -EINPROGRESS:
155 case -EBUSY:
156 wait_for_completion(&cr->completion);
157 /* At this point we known for sure the request has finished,
158 * because wait_for_completion above was not interruptible.
159 * This is important because otherwise hardware or driver
160 * might try to access memory which will be freed or reused for
161 * another request. */
163 if (unlikely(cr->err)) {
164 dprintk(0, KERN_ERR, "error from async request: %d\n",
165 cr->err);
166 return cr->err;
169 break;
170 default:
171 return ret;
174 return 0;
177 ssize_t cryptodev_cipher_encrypt(struct cipher_data *cdata,
178 const struct scatterlist *sg1, struct scatterlist *sg2,
179 size_t len)
181 int ret;
183 INIT_COMPLETION(cdata->async.result->completion);
184 ablkcipher_request_set_crypt(cdata->async.request,
185 (struct scatterlist *)sg1, sg2,
186 len, cdata->async.iv);
187 ret = crypto_ablkcipher_encrypt(cdata->async.request);
189 return waitfor(cdata->async.result, ret);
192 ssize_t cryptodev_cipher_decrypt(struct cipher_data *cdata,
193 const struct scatterlist *sg1, struct scatterlist *sg2,
194 size_t len)
196 int ret;
198 INIT_COMPLETION(cdata->async.result->completion);
199 ablkcipher_request_set_crypt(cdata->async.request,
200 (struct scatterlist *)sg1, sg2,
201 len, cdata->async.iv);
202 ret = crypto_ablkcipher_decrypt(cdata->async.request);
204 return waitfor(cdata->async.result, ret);
207 /* Hash functions */
209 int cryptodev_hash_init(struct hash_data *hdata, const char *alg_name,
210 int hmac_mode, void *mackey, size_t mackeylen)
212 int ret;
214 hdata->async.s = crypto_alloc_ahash(alg_name, 0, 0);
215 if (unlikely(IS_ERR(hdata->async.s))) {
216 dprintk(1, KERN_DEBUG, "%s: Failed to load transform for %s\n",
217 __func__, alg_name);
218 return -EINVAL;
221 /* Copy the key from user and set to TFM. */
222 if (hmac_mode != 0) {
223 ret = crypto_ahash_setkey(hdata->async.s, mackey, mackeylen);
224 if (unlikely(ret)) {
225 dprintk(1, KERN_DEBUG,
226 "Setting hmac key failed for %s-%zu.\n",
227 alg_name, mackeylen*8);
228 ret = -EINVAL;
229 goto error;
233 hdata->digestsize = crypto_ahash_digestsize(hdata->async.s);
235 hdata->async.result = kmalloc(sizeof(*hdata->async.result), GFP_KERNEL);
236 if (unlikely(!hdata->async.result)) {
237 ret = -ENOMEM;
238 goto error;
241 memset(hdata->async.result, 0, sizeof(*hdata->async.result));
242 init_completion(&hdata->async.result->completion);
244 hdata->async.request = ahash_request_alloc(hdata->async.s, GFP_KERNEL);
245 if (unlikely(!hdata->async.request)) {
246 dprintk(0, KERN_ERR, "error allocating async crypto request\n");
247 ret = -ENOMEM;
248 goto error;
251 ahash_request_set_callback(hdata->async.request,
252 CRYPTO_TFM_REQ_MAY_BACKLOG,
253 cryptodev_complete, hdata->async.result);
255 ret = crypto_ahash_init(hdata->async.request);
256 if (unlikely(ret)) {
257 dprintk(0, KERN_ERR, "error in crypto_hash_init()\n");
258 goto error_request;
261 hdata->init = 1;
262 return 0;
264 error_request:
265 ahash_request_free(hdata->async.request);
266 error:
267 kfree(hdata->async.result);
268 crypto_free_ahash(hdata->async.s);
269 return ret;
272 void cryptodev_hash_deinit(struct hash_data *hdata)
274 if (hdata->init) {
275 if (hdata->async.request)
276 ahash_request_free(hdata->async.request);
277 kfree(hdata->async.result);
278 if (hdata->async.s)
279 crypto_free_ahash(hdata->async.s);
280 hdata->init = 0;
284 int cryptodev_hash_reset(struct hash_data *hdata)
286 int ret;
288 ret = crypto_ahash_init(hdata->async.request);
289 if (unlikely(ret)) {
290 dprintk(0, KERN_ERR, "error in crypto_hash_init()\n");
291 return ret;
294 return 0;
298 ssize_t cryptodev_hash_update(struct hash_data *hdata,
299 struct scatterlist *sg, size_t len)
301 int ret;
303 INIT_COMPLETION(hdata->async.result->completion);
304 ahash_request_set_crypt(hdata->async.request, sg, NULL, len);
306 ret = crypto_ahash_update(hdata->async.request);
308 return waitfor(hdata->async.result, ret);
311 int cryptodev_hash_final(struct hash_data *hdata, void* output)
313 int ret;
315 INIT_COMPLETION(hdata->async.result->completion);
316 ahash_request_set_crypt(hdata->async.request, NULL, output, 0);
318 ret = crypto_ahash_final(hdata->async.request);
320 return waitfor(hdata->async.result, ret);