Use unlikely() on unlikely situations.
[cryptodev-linux.git] / cryptodev_cipher.c
blobd42bb2e0ba7e97dbe843bbf032829d7fd71bec70
1 /*
2 * Driver for /dev/crypto device (aka CryptoDev)
4 * Copyright (c) 2010 Nikos Mavrogiannopoulos <nmav@gnutls.org>
6 * This file is part of linux cryptodev.
8 * cryptodev is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
13 * cryptodev is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include <linux/crypto.h>
23 #include <linux/mm.h>
24 #include <linux/highmem.h>
25 #include <linux/random.h>
26 #include <asm/uaccess.h>
27 #include <asm/ioctl.h>
28 #include <linux/scatterlist.h>
29 #include <crypto/algapi.h>
30 #include <crypto/hash.h>
31 #include "cryptodev.h"
32 #include "cryptodev_int.h"
35 struct cryptodev_result {
36 struct completion completion;
37 int err;
40 static void cryptodev_complete(struct crypto_async_request *req, int err)
42 struct cryptodev_result *res = req->data;
44 if (err == -EINPROGRESS)
45 return;
47 res->err = err;
48 complete(&res->completion);
51 int cryptodev_cipher_init(struct cipher_data* out, const char* alg_name, uint8_t __user * key, size_t keylen)
53 uint8_t keyp[CRYPTO_CIPHER_MAX_KEY_LEN];
54 struct ablkcipher_alg* alg;
55 int ret;
57 memset(out, 0, sizeof(*out));
59 out->init = 1;
61 if (unlikely(keylen > CRYPTO_CIPHER_MAX_KEY_LEN)) {
62 dprintk(1,KERN_DEBUG,"Setting key failed for %s-%zu.\n",
63 alg_name, keylen*8);
64 return -EINVAL;
66 /* Copy the key from user and set to TFM. */
67 copy_from_user(keyp, key, keylen);
69 out->async.s = crypto_alloc_ablkcipher(alg_name, 0, 0);
70 if (unlikely(IS_ERR(out->async.s))) {
71 dprintk(1,KERN_DEBUG,"%s: Failed to load cipher %s\n", __func__,
72 alg_name);
73 return -EINVAL;
76 alg = crypto_ablkcipher_alg(out->async.s);
78 if (alg != NULL) {
79 /* Was correct key length supplied? */
80 if (unlikely((keylen < alg->min_keysize) ||
81 (keylen > alg->max_keysize))) {
82 dprintk(1,KERN_DEBUG,"Wrong keylen '%zu' for algorithm '%s'. Use %u to %u.\n",
83 keylen, alg_name, alg->min_keysize,
84 alg->max_keysize);
85 ret = -EINVAL;
86 goto error;
90 ret = crypto_ablkcipher_setkey(out->async.s, keyp, keylen);
91 if (unlikely(ret)) {
92 dprintk(1,KERN_DEBUG,"Setting key failed for %s-%zu.\n",
93 alg_name, keylen*8);
94 ret = -EINVAL;
95 goto error;
98 out->blocksize = crypto_ablkcipher_blocksize(out->async.s);
99 out->ivsize = crypto_ablkcipher_ivsize(out->async.s);
101 out->async.result = kmalloc(sizeof(*out->async.result), GFP_KERNEL);
102 if (unlikely(!out->async.result)) {
103 ret = -ENOMEM;
104 goto error;
107 memset(out->async.result, 0, sizeof(*out->async.result));
108 init_completion(&out->async.result->completion);
110 out->async.request = ablkcipher_request_alloc(out->async.s, GFP_KERNEL);
111 if (unlikely(!out->async.request)) {
112 dprintk(1,KERN_ERR,"error allocating async crypto request\n");
113 ret = -ENOMEM;
114 goto error;
117 ablkcipher_request_set_callback(out->async.request, CRYPTO_TFM_REQ_MAY_BACKLOG,
118 cryptodev_complete, out->async.result);
120 return 0;
121 error:
122 crypto_free_ablkcipher(out->async.s);
123 kfree(out->async.result);
124 ablkcipher_request_free(out->async.request);
126 return ret;
129 void cryptodev_cipher_deinit(struct cipher_data* cdata)
131 crypto_free_ablkcipher(cdata->async.s);
132 kfree(cdata->async.result);
133 ablkcipher_request_free(cdata->async.request);
135 cdata->init = 0;
138 void cryptodev_cipher_set_iv(struct cipher_data* cdata, void __user* iv, size_t iv_size)
140 copy_from_user(cdata->async.iv, iv, min(iv_size,sizeof(cdata->async.iv)));
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: %zd \n", ret);
159 return cr->err;
162 break;
163 default:
164 return ret;
167 return 0;
170 ssize_t cryptodev_cipher_encrypt( struct cipher_data* cdata, struct scatterlist *sg1, struct scatterlist *sg2, size_t len)
172 int ret;
174 INIT_COMPLETION(cdata->async.result->completion);
175 ablkcipher_request_set_crypt(cdata->async.request, sg1, sg2,
176 len, cdata->async.iv);
177 ret = crypto_ablkcipher_encrypt(cdata->async.request);
179 return waitfor(cdata->async.result,ret);
182 ssize_t cryptodev_cipher_decrypt( struct cipher_data* cdata, struct scatterlist *sg1, struct scatterlist *sg2, size_t len)
184 int ret;
186 INIT_COMPLETION(cdata->async.result->completion);
187 ablkcipher_request_set_crypt(cdata->async.request, sg1, sg2,
188 len, cdata->async.iv);
189 ret = crypto_ablkcipher_decrypt(cdata->async.request);
191 return waitfor(cdata->async.result, ret);
194 /* Hash functions */
196 int cryptodev_hash_init( struct hash_data* hdata, const char* alg_name, int hmac_mode, void __user* mackey, size_t mackeylen)
198 int ret;
199 uint8_t hkeyp[CRYPTO_HMAC_MAX_KEY_LEN];
201 hdata->init = 1;
203 if (unlikely(mackeylen > CRYPTO_HMAC_MAX_KEY_LEN)) {
204 dprintk(1,KERN_DEBUG,"Setting hmac key failed for %s-%zu.\n",
205 alg_name, mackeylen*8);
206 return -EINVAL;
208 copy_from_user(hkeyp, mackey, mackeylen);
210 hdata->async.s = crypto_alloc_ahash(alg_name, 0, 0);
211 if (unlikely(IS_ERR(hdata->async.s))) {
212 dprintk(1,KERN_DEBUG,"%s: Failed to load transform for %s\n", __func__,
213 alg_name);
214 return -EINVAL;
217 /* Copy the key from user and set to TFM. */
218 if (hmac_mode != 0) {
220 ret = crypto_ahash_setkey(hdata->async.s, hkeyp, mackeylen);
221 if (unlikely(ret)) {
222 dprintk(1,KERN_DEBUG,"Setting hmac key failed for %s-%zu.\n",
223 alg_name, mackeylen*8);
224 ret = -EINVAL;
225 goto error;
229 hdata->digestsize = crypto_ahash_digestsize(hdata->async.s);
231 hdata->async.result = kmalloc(sizeof(*hdata->async.result), GFP_KERNEL);
232 if (unlikely(!hdata->async.result)) {
233 ret = -ENOMEM;
234 goto error;
237 memset(hdata->async.result, 0, sizeof(*hdata->async.result));
238 init_completion(&hdata->async.result->completion);
240 hdata->async.request = ahash_request_alloc(hdata->async.s, GFP_KERNEL);
241 if (unlikely(!hdata->async.request)) {
242 dprintk(0,KERN_ERR,"error allocating async crypto request\n");
243 ret = -ENOMEM;
244 goto error;
247 ahash_request_set_callback(hdata->async.request, CRYPTO_TFM_REQ_MAY_BACKLOG,
248 cryptodev_complete, hdata->async.result);
251 return 0;
253 error:
254 crypto_free_ahash(hdata->async.s);
255 return ret;
258 void cryptodev_hash_deinit(struct hash_data* hdata)
260 crypto_free_ahash(hdata->async.s);
261 hdata->init = 0;
264 int cryptodev_hash_reset( struct hash_data* hdata)
266 int ret;
267 ret = crypto_ahash_init(hdata->async.request);
268 if (unlikely(ret)) {
269 dprintk(0,KERN_ERR,
270 "error in crypto_hash_init()\n");
271 return ret;
274 return 0;
278 ssize_t cryptodev_hash_update( struct hash_data* hdata, struct scatterlist *sg, size_t len)
280 int ret;
282 INIT_COMPLETION(hdata->async.result->completion);
283 ahash_request_set_crypt(hdata->async.request, sg, NULL,
284 len);
286 ret = crypto_ahash_update(hdata->async.request);
288 return waitfor(hdata->async.result,ret);
292 int cryptodev_hash_final( struct hash_data* hdata, void* output)
294 int ret;
296 INIT_COMPLETION(hdata->async.result->completion);
297 ahash_request_set_crypt(hdata->async.request, NULL, output, 0);
299 ret = crypto_ahash_final(hdata->async.request);
301 return waitfor(hdata->async.result,ret);