Released 0.3
[cryptodev-linux.git] / cryptodev_cipher.c
blob2f52f46793bcc784f3844acd49f762072e0ab661
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 if (unlikely(copy_from_user(keyp, key, keylen))) {
68 dprintk(1, KERN_DEBUG, "copy_from_user() failed for key\n");
69 return -EINVAL;
72 out->async.s = crypto_alloc_ablkcipher(alg_name, 0, 0);
73 if (unlikely(IS_ERR(out->async.s))) {
74 dprintk(1,KERN_DEBUG,"%s: Failed to load cipher %s\n", __func__,
75 alg_name);
76 return -EINVAL;
79 alg = crypto_ablkcipher_alg(out->async.s);
81 if (alg != NULL) {
82 /* Was correct key length supplied? */
83 if (alg->max_keysize > 0 && unlikely((keylen < alg->min_keysize) ||
84 (keylen > alg->max_keysize))) {
85 dprintk(1,KERN_DEBUG,"Wrong keylen '%zu' for algorithm '%s'. Use %u to %u.\n",
86 keylen, alg_name, alg->min_keysize,
87 alg->max_keysize);
88 ret = -EINVAL;
89 goto error;
93 ret = crypto_ablkcipher_setkey(out->async.s, keyp, keylen);
94 if (unlikely(ret)) {
95 dprintk(1,KERN_DEBUG,"Setting key failed for %s-%zu.\n",
96 alg_name, keylen*8);
97 ret = -EINVAL;
98 goto error;
101 out->blocksize = crypto_ablkcipher_blocksize(out->async.s);
102 out->ivsize = crypto_ablkcipher_ivsize(out->async.s);
104 out->async.result = kmalloc(sizeof(*out->async.result), GFP_KERNEL);
105 if (unlikely(!out->async.result)) {
106 ret = -ENOMEM;
107 goto error;
110 memset(out->async.result, 0, sizeof(*out->async.result));
111 init_completion(&out->async.result->completion);
113 out->async.request = ablkcipher_request_alloc(out->async.s, GFP_KERNEL);
114 if (unlikely(!out->async.request)) {
115 dprintk(1,KERN_ERR,"error allocating async crypto request\n");
116 ret = -ENOMEM;
117 goto error;
120 ablkcipher_request_set_callback(out->async.request, CRYPTO_TFM_REQ_MAY_BACKLOG,
121 cryptodev_complete, out->async.result);
123 return 0;
124 error:
125 crypto_free_ablkcipher(out->async.s);
126 kfree(out->async.result);
127 ablkcipher_request_free(out->async.request);
129 return ret;
132 void cryptodev_cipher_deinit(struct cipher_data* cdata)
134 crypto_free_ablkcipher(cdata->async.s);
135 kfree(cdata->async.result);
136 ablkcipher_request_free(cdata->async.request);
138 cdata->init = 0;
141 int cryptodev_cipher_set_iv(struct cipher_data* cdata, void __user* iv, size_t iv_size)
143 return copy_from_user(cdata->async.iv, iv, min(iv_size,sizeof(cdata->async.iv)));
146 static inline int waitfor (struct cryptodev_result* cr, ssize_t ret)
148 switch (ret) {
149 case 0:
150 break;
151 case -EINPROGRESS:
152 case -EBUSY:
153 wait_for_completion(&cr->completion);
154 /* At this point we known for sure the request has finished,
155 * because wait_for_completion above was not interruptible.
156 * This is important because otherwise hardware or driver
157 * might try to access memory which will be freed or reused for
158 * another request. */
160 if (unlikely(cr->err)) {
161 dprintk(0,KERN_ERR,"error from async request: %zd \n", ret);
162 return cr->err;
165 break;
166 default:
167 return ret;
170 return 0;
173 ssize_t cryptodev_cipher_encrypt( struct cipher_data* cdata, struct scatterlist *sg1, struct scatterlist *sg2, size_t len)
175 int ret;
177 INIT_COMPLETION(cdata->async.result->completion);
178 ablkcipher_request_set_crypt(cdata->async.request, sg1, sg2,
179 len, cdata->async.iv);
180 ret = crypto_ablkcipher_encrypt(cdata->async.request);
182 return waitfor(cdata->async.result,ret);
185 ssize_t cryptodev_cipher_decrypt( struct cipher_data* cdata, struct scatterlist *sg1, struct scatterlist *sg2, size_t len)
187 int ret;
189 INIT_COMPLETION(cdata->async.result->completion);
190 ablkcipher_request_set_crypt(cdata->async.request, sg1, sg2,
191 len, cdata->async.iv);
192 ret = crypto_ablkcipher_decrypt(cdata->async.request);
194 return waitfor(cdata->async.result, ret);
197 /* Hash functions */
199 int cryptodev_hash_init( struct hash_data* hdata, const char* alg_name, int hmac_mode, void __user* mackey, size_t mackeylen)
201 int ret;
202 uint8_t hkeyp[CRYPTO_HMAC_MAX_KEY_LEN];
204 hdata->init = 1;
206 if (unlikely(mackeylen > CRYPTO_HMAC_MAX_KEY_LEN)) {
207 dprintk(1,KERN_DEBUG,"Setting hmac key failed for %s-%zu.\n",
208 alg_name, mackeylen*8);
209 return -EINVAL;
211 if (unlikely(copy_from_user(hkeyp, mackey, mackeylen))) {
212 dprintk(1, KERN_DEBUG, "copy_from_user() failed for mackey\n");
213 return -EINVAL;
216 hdata->async.s = crypto_alloc_ahash(alg_name, 0, 0);
217 if (unlikely(IS_ERR(hdata->async.s))) {
218 dprintk(1,KERN_DEBUG,"%s: Failed to load transform for %s\n", __func__,
219 alg_name);
220 return -EINVAL;
223 /* Copy the key from user and set to TFM. */
224 if (hmac_mode != 0) {
226 ret = crypto_ahash_setkey(hdata->async.s, hkeyp, mackeylen);
227 if (unlikely(ret)) {
228 dprintk(1,KERN_DEBUG,"Setting hmac key failed for %s-%zu.\n",
229 alg_name, mackeylen*8);
230 ret = -EINVAL;
231 goto error;
235 hdata->digestsize = crypto_ahash_digestsize(hdata->async.s);
237 hdata->async.result = kmalloc(sizeof(*hdata->async.result), GFP_KERNEL);
238 if (unlikely(!hdata->async.result)) {
239 ret = -ENOMEM;
240 goto error;
243 memset(hdata->async.result, 0, sizeof(*hdata->async.result));
244 init_completion(&hdata->async.result->completion);
246 hdata->async.request = ahash_request_alloc(hdata->async.s, GFP_KERNEL);
247 if (unlikely(!hdata->async.request)) {
248 dprintk(0,KERN_ERR,"error allocating async crypto request\n");
249 ret = -ENOMEM;
250 goto error;
253 ahash_request_set_callback(hdata->async.request, CRYPTO_TFM_REQ_MAY_BACKLOG,
254 cryptodev_complete, hdata->async.result);
257 return 0;
259 error:
260 crypto_free_ahash(hdata->async.s);
261 return ret;
264 void cryptodev_hash_deinit(struct hash_data* hdata)
266 crypto_free_ahash(hdata->async.s);
267 hdata->init = 0;
270 int cryptodev_hash_reset( struct hash_data* hdata)
272 int ret;
273 ret = crypto_ahash_init(hdata->async.request);
274 if (unlikely(ret)) {
275 dprintk(0,KERN_ERR,
276 "error in crypto_hash_init()\n");
277 return ret;
280 return 0;
284 ssize_t cryptodev_hash_update( struct hash_data* hdata, struct scatterlist *sg, size_t len)
286 int ret;
288 INIT_COMPLETION(hdata->async.result->completion);
289 ahash_request_set_crypt(hdata->async.request, sg, NULL,
290 len);
292 ret = crypto_ahash_update(hdata->async.request);
294 return waitfor(hdata->async.result,ret);
298 int cryptodev_hash_final( struct hash_data* hdata, void* output)
300 int ret;
302 INIT_COMPLETION(hdata->async.result->completion);
303 ahash_request_set_crypt(hdata->async.request, NULL, output, 0);
305 ret = crypto_ahash_final(hdata->async.request);
307 return waitfor(hdata->async.result,ret);