cryptodev_cipher_set_iv() made inline.
[cryptodev-linux.git] / cryptodev_cipher.c
blob0dd3fba3391091f0e38a0c808b2025863f908602
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 static inline int waitfor(struct cryptodev_result *cr, ssize_t ret)
145 switch (ret) {
146 case 0:
147 break;
148 case -EINPROGRESS:
149 case -EBUSY:
150 wait_for_completion(&cr->completion);
151 /* At this point we known for sure the request has finished,
152 * because wait_for_completion above was not interruptible.
153 * This is important because otherwise hardware or driver
154 * might try to access memory which will be freed or reused for
155 * another request. */
157 if (unlikely(cr->err)) {
158 dprintk(0, KERN_ERR, "error from async request: %d\n",
159 cr->err);
160 return cr->err;
163 break;
164 default:
165 return ret;
168 return 0;
171 ssize_t cryptodev_cipher_encrypt(struct cipher_data *cdata,
172 const struct scatterlist *sg1, struct scatterlist *sg2,
173 size_t len)
175 int ret;
177 INIT_COMPLETION(cdata->async.result->completion);
178 ablkcipher_request_set_crypt(cdata->async.request,
179 (struct scatterlist *)sg1, sg2,
180 len, cdata->async.iv);
181 ret = crypto_ablkcipher_encrypt(cdata->async.request);
183 return waitfor(cdata->async.result, ret);
186 ssize_t cryptodev_cipher_decrypt(struct cipher_data *cdata,
187 const struct scatterlist *sg1, struct scatterlist *sg2,
188 size_t len)
190 int ret;
192 INIT_COMPLETION(cdata->async.result->completion);
193 ablkcipher_request_set_crypt(cdata->async.request,
194 (struct scatterlist *)sg1, sg2,
195 len, cdata->async.iv);
196 ret = crypto_ablkcipher_decrypt(cdata->async.request);
198 return waitfor(cdata->async.result, ret);
201 /* Hash functions */
203 int cryptodev_hash_init(struct hash_data *hdata, const char *alg_name,
204 int hmac_mode, void *mackey, size_t mackeylen)
206 int ret;
208 hdata->async.s = crypto_alloc_ahash(alg_name, 0, 0);
209 if (unlikely(IS_ERR(hdata->async.s))) {
210 dprintk(1, KERN_DEBUG, "%s: Failed to load transform for %s\n",
211 __func__, alg_name);
212 return -EINVAL;
215 /* Copy the key from user and set to TFM. */
216 if (hmac_mode != 0) {
217 ret = crypto_ahash_setkey(hdata->async.s, mackey, mackeylen);
218 if (unlikely(ret)) {
219 dprintk(1, KERN_DEBUG,
220 "Setting hmac key failed for %s-%zu.\n",
221 alg_name, mackeylen*8);
222 ret = -EINVAL;
223 goto error;
227 hdata->digestsize = crypto_ahash_digestsize(hdata->async.s);
229 hdata->async.result = kmalloc(sizeof(*hdata->async.result), GFP_KERNEL);
230 if (unlikely(!hdata->async.result)) {
231 ret = -ENOMEM;
232 goto error;
235 memset(hdata->async.result, 0, sizeof(*hdata->async.result));
236 init_completion(&hdata->async.result->completion);
238 hdata->async.request = ahash_request_alloc(hdata->async.s, GFP_KERNEL);
239 if (unlikely(!hdata->async.request)) {
240 dprintk(0, KERN_ERR, "error allocating async crypto request\n");
241 ret = -ENOMEM;
242 goto error;
245 ahash_request_set_callback(hdata->async.request,
246 CRYPTO_TFM_REQ_MAY_BACKLOG,
247 cryptodev_complete, hdata->async.result);
249 ret = crypto_ahash_init(hdata->async.request);
250 if (unlikely(ret)) {
251 dprintk(0, KERN_ERR, "error in crypto_hash_init()\n");
252 goto error_request;
255 hdata->init = 1;
256 return 0;
258 error_request:
259 ahash_request_free(hdata->async.request);
260 error:
261 kfree(hdata->async.result);
262 crypto_free_ahash(hdata->async.s);
263 return ret;
266 void cryptodev_hash_deinit(struct hash_data *hdata)
268 if (hdata->init) {
269 if (hdata->async.request)
270 ahash_request_free(hdata->async.request);
271 kfree(hdata->async.result);
272 if (hdata->async.s)
273 crypto_free_ahash(hdata->async.s);
274 hdata->init = 0;
278 int cryptodev_hash_reset(struct hash_data *hdata)
280 int ret;
282 ret = crypto_ahash_init(hdata->async.request);
283 if (unlikely(ret)) {
284 dprintk(0, KERN_ERR, "error in crypto_hash_init()\n");
285 return ret;
288 return 0;
292 ssize_t cryptodev_hash_update(struct hash_data *hdata,
293 struct scatterlist *sg, size_t len)
295 int ret;
297 INIT_COMPLETION(hdata->async.result->completion);
298 ahash_request_set_crypt(hdata->async.request, sg, NULL, len);
300 ret = crypto_ahash_update(hdata->async.request);
302 return waitfor(hdata->async.result, ret);
305 int cryptodev_hash_final(struct hash_data *hdata, void* output)
307 int ret;
309 INIT_COMPLETION(hdata->async.result->completion);
310 ahash_request_set_crypt(hdata->async.request, NULL, output, 0);
312 ret = crypto_ahash_final(hdata->async.request);
314 return waitfor(hdata->async.result, ret);