Added files.
[cryptodev-linux.git] / cryptodev.c
blob60076484e8e39c015d05ac94871b8b2566dc79b9
1 /*
2 * Driver for /dev/crypto device (aka CryptoDev)
4 * Copyright (c) 2004 Michal Ludvig <mludvig@logix.net.nz>, SuSE Labs
5 * Copyright (c) 2009 Nikos Mavrogiannopoulos <nmav@gennetsa.com>, Gennet S.A.
7 * Device /dev/crypto provides an interface for
8 * accessing kernel CryptoAPI algorithms (ciphers,
9 * hashes) from userspace programs.
11 * /dev/crypto interface was originally introduced in
12 * OpenBSD and this module attempts to keep the API,
13 * although a bit extended.
17 #include <linux/module.h>
18 #include <linux/moduleparam.h>
19 #include <linux/init.h>
20 #include <linux/sched.h>
21 #include <linux/fs.h>
22 #include <linux/file.h>
23 #include <linux/fdtable.h>
24 #include <linux/miscdevice.h>
25 #include <linux/crypto.h>
26 #include <linux/mm.h>
27 #include <linux/highmem.h>
28 #include <linux/random.h>
29 #include "cryptodev.h"
30 #include <asm/uaccess.h>
31 #include <asm/ioctl.h>
32 #include <linux/scatterlist.h>
34 MODULE_AUTHOR("Michal Ludvig <mludvig@logix.net.nz>");
35 MODULE_DESCRIPTION("CryptoDev driver");
36 MODULE_LICENSE("Dual BSD/GPL");
38 /* ====== Compile-time config ====== */
40 #define CRYPTODEV_STATS
42 /* ====== Module parameters ====== */
44 static int verbosity = 0;
45 module_param(verbosity, int, 0644);
46 MODULE_PARM_DESC(verbosity, "0: normal, 1: verbose, 2: debug");
48 #ifdef CRYPTODEV_STATS
49 static int enable_stats = 0;
50 module_param(enable_stats, int, 0644);
51 MODULE_PARM_DESC(enable_stats, "collect statictics about cryptodev usage");
52 #endif
54 /* ====== Debug helpers ====== */
56 #define PFX "cryptodev: "
57 #define dprintk(level,severity,format,a...) \
58 do { \
59 if (level <= verbosity) \
60 printk(severity PFX "%s[%u]: " format, \
61 current->comm, current->pid, \
62 ##a); \
63 } while (0)
65 /* ====== CryptoAPI ====== */
67 #define FILL_SG(sg,ptr,len) \
68 do { \
69 (sg)->page = virt_to_page(ptr); \
70 (sg)->offset = offset_in_page(ptr); \
71 (sg)->length = len; \
72 (sg)->dma_address = 0; \
73 } while (0)
75 struct csession {
76 struct list_head entry;
77 struct semaphore sem;
78 struct crypto_blkcipher *tfm;
79 struct crypto_hash *hash_tfm;
80 uint32_t sid;
81 #ifdef CRYPTODEV_STATS
82 #if ! ((COP_ENCRYPT < 2) && (COP_DECRYPT < 2))
83 #error Struct csession.stat uses COP_{ENCRYPT,DECRYPT} as indices. Do something!
84 #endif
85 unsigned long long stat[2];
86 size_t stat_max_size, stat_count;
87 #endif
90 struct fcrypt {
91 struct list_head list;
92 struct semaphore sem;
95 /* Prepare session for future use. */
96 static int
97 crypto_create_session(struct fcrypt *fcr, struct session_op *sop)
99 struct csession *ses_new, *ses_ptr;
100 struct crypto_blkcipher *blk_tfm=NULL;
101 struct crypto_hash *hash_tfm=NULL;
102 int ret = 0;
103 const char *alg_name=NULL;
104 const char *hash_name=NULL;
105 struct blkcipher_alg* blkalg;
106 int hmac_mode = 1;
108 /* Does the request make sense? */
109 if (!sop->cipher && !sop->mac) {
110 dprintk(1,KERN_DEBUG,"Both 'cipher' and 'mac' unset.\n");
111 return -EINVAL;
114 switch (sop->cipher) {
115 case 0:
116 break;
117 case CRYPTO_DES_CBC:
118 alg_name = "cbc(des)";
119 break;
120 case CRYPTO_3DES_CBC:
121 alg_name = "cbc(3des_ede)";
122 break;
123 case CRYPTO_BLF_CBC:
124 alg_name = "cbc(blowfish)";
125 break;
126 case CRYPTO_AES_CBC:
127 alg_name = "cbc(aes)";
128 break;
129 case CRYPTO_CAMELLIA_CBC:
130 alg_name = "cbc(camelia)";
131 break;
132 default:
133 dprintk(1,KERN_DEBUG,"%s: bad cipher: %d\n", __func__, sop->cipher);
134 return -EINVAL;
137 switch (sop->mac) {
138 case 0:
139 break;
140 case CRYPTO_MD5_HMAC:
141 hash_name = "hmac(md5)";
142 break;
143 case CRYPTO_RIPEMD160_HMAC:
144 hash_name = "hmac(rmd160)";
145 break;
146 case CRYPTO_SHA1_HMAC:
147 hash_name = "hmac(sha1)";
148 break;
149 case CRYPTO_SHA2_256_HMAC:
150 hash_name = "hmac(sha256)";
151 break;
152 case CRYPTO_SHA2_384_HMAC:
153 hash_name = "hmac(sha384)";
154 break;
155 case CRYPTO_SHA2_512_HMAC:
156 hash_name = "hmac(sha512)";
157 break;
159 /* non-hmac cases */
160 case CRYPTO_MD5:
161 hash_name = "md5";
162 hmac_mode = 0;
163 break;
164 case CRYPTO_RIPEMD160:
165 hash_name = "rmd160";
166 hmac_mode = 0;
167 break;
168 case CRYPTO_SHA1:
169 hash_name = "sha1";
170 hmac_mode = 0;
171 break;
172 case CRYPTO_SHA2_256:
173 hash_name = "sha256";
174 hmac_mode = 0;
175 break;
176 case CRYPTO_SHA2_384:
177 hash_name = "sha384";
178 hmac_mode = 0;
179 break;
180 case CRYPTO_SHA2_512:
181 hash_name = "sha512";
182 hmac_mode = 0;
183 break;
185 default:
186 dprintk(1,KERN_DEBUG,"%s: bad mac: %d\n", __func__, sop->mac);
187 return -EINVAL;
190 /* Set-up crypto transform. */
191 if (alg_name) {
192 uint8_t keyp[CRYPTO_CIPHER_MAX_KEY_LEN];
194 blk_tfm = crypto_alloc_blkcipher(alg_name, 0, CRYPTO_ALG_ASYNC);
195 if (IS_ERR(blk_tfm)) {
196 dprintk(1,KERN_DEBUG,"%s: Failed to load transform for %s\n", __func__,
197 alg_name);
198 return -EINVAL;
201 blkalg = crypto_blkcipher_alg(blk_tfm);
202 if (blkalg != NULL) {
203 /* Was correct key length supplied? */
204 if ((sop->keylen < blkalg->min_keysize) ||
205 (sop->keylen > blkalg->max_keysize)) {
206 dprintk(0,KERN_DEBUG,"Wrong keylen '%zu' for algorithm '%s'. Use %u to %u.\n",
207 sop->keylen, alg_name, blkalg->min_keysize,
208 blkalg->max_keysize);
209 ret = -EINVAL;
210 goto error;
214 if (sop->keylen > CRYPTO_CIPHER_MAX_KEY_LEN) {
215 dprintk(0,KERN_DEBUG,"Setting key failed for %s-%zu.\n",
216 alg_name, sop->keylen*8);
217 ret = -EINVAL;
218 goto error;
221 /* Copy the key from user and set to TFM. */
222 copy_from_user(keyp, sop->key, sop->keylen);
223 ret = crypto_blkcipher_setkey(blk_tfm, keyp, sop->keylen);
224 if (ret) {
225 dprintk(0,KERN_DEBUG,"Setting key failed for %s-%zu.\n",
226 alg_name, sop->keylen*8);
227 ret = -EINVAL;
228 goto error;
233 if (hash_name) {
234 hash_tfm = crypto_alloc_hash(hash_name, 0, CRYPTO_ALG_ASYNC);
235 if (IS_ERR(hash_tfm)) {
236 dprintk(1,KERN_DEBUG,"%s: Failed to load transform for %s\n", __func__,
237 hash_name);
238 return -EINVAL;
241 /* Copy the key from user and set to TFM. */
242 if (hmac_mode != 0) {
243 uint8_t hkeyp[CRYPTO_HMAC_MAX_KEY_LEN];
245 if (sop->mackeylen > CRYPTO_HMAC_MAX_KEY_LEN) {
246 dprintk(0,KERN_DEBUG,"Setting hmac key failed for %s-%zu.\n",
247 hash_name, sop->mackeylen*8);
248 ret = -EINVAL;
249 goto error;
252 copy_from_user(hkeyp, sop->mackey, sop->mackeylen);
253 ret = crypto_hash_setkey(hash_tfm, hkeyp, sop->mackeylen);
254 if (ret) {
255 dprintk(0,KERN_DEBUG,"Setting hmac key failed for %s-%zu.\n",
256 hash_name, sop->mackeylen*8);
257 ret = -EINVAL;
258 goto error;
264 /* Create a session and put it to the list. */
265 ses_new = kmalloc(sizeof(*ses_new), GFP_KERNEL);
266 if(!ses_new) {
267 ret = -ENOMEM;
268 goto error;
271 memset(ses_new, 0, sizeof(*ses_new));
272 get_random_bytes(&ses_new->sid, sizeof(ses_new->sid));
273 ses_new->tfm = blk_tfm;
274 ses_new->hash_tfm = hash_tfm;
275 init_MUTEX(&ses_new->sem);
277 down(&fcr->sem);
278 restart:
279 list_for_each_entry(ses_ptr, &fcr->list, entry) {
280 /* Check for duplicate SID */
281 if (unlikely(ses_new->sid == ses_ptr->sid)) {
282 get_random_bytes(&ses_new->sid, sizeof(ses_new->sid));
283 /* Unless we have a broken RNG this
284 shouldn't loop forever... ;-) */
285 goto restart;
289 list_add(&ses_new->entry, &fcr->list);
290 up(&fcr->sem);
292 /* Fill in some values for the user. */
293 sop->ses = ses_new->sid;
295 return 0;
297 error:
298 if (blk_tfm)
299 crypto_free_blkcipher(blk_tfm);
300 if (hash_tfm)
301 crypto_free_hash(hash_tfm);
303 return ret;
307 /* Everything that needs to be done when remowing a session. */
308 static inline void
309 crypto_destroy_session(struct csession *ses_ptr)
311 if(down_trylock(&ses_ptr->sem)) {
312 dprintk(2, KERN_DEBUG, "Waiting for semaphore of sid=0x%08X\n",
313 ses_ptr->sid);
314 down(&ses_ptr->sem);
316 dprintk(2, KERN_DEBUG, "Removed session 0x%08X\n", ses_ptr->sid);
317 #if defined(CRYPTODEV_STATS)
318 if(enable_stats)
319 dprintk(2, KERN_DEBUG,
320 "Usage in Bytes: enc=%llu, dec=%llu, max=%zu, avg=%lu, cnt=%zu\n",
321 ses_ptr->stat[COP_ENCRYPT], ses_ptr->stat[COP_DECRYPT],
322 ses_ptr->stat_max_size, ses_ptr->stat_count > 0
323 ? ((unsigned long)(ses_ptr->stat[COP_ENCRYPT]+
324 ses_ptr->stat[COP_DECRYPT]) /
325 ses_ptr->stat_count) : 0,
326 ses_ptr->stat_count);
327 #endif
328 if (ses_ptr->tfm) {
329 crypto_free_blkcipher(ses_ptr->tfm);
330 ses_ptr->tfm = NULL;
332 if (ses_ptr->hash_tfm) {
333 crypto_free_hash(ses_ptr->hash_tfm);
334 ses_ptr->hash_tfm = NULL;
336 up(&ses_ptr->sem);
337 kfree(ses_ptr);
340 /* Look up a session by ID and remove. */
341 static int
342 crypto_finish_session(struct fcrypt *fcr, uint32_t sid)
344 struct csession *tmp, *ses_ptr;
345 struct list_head *head;
346 int ret = 0;
348 down(&fcr->sem);
349 head = &fcr->list;
350 list_for_each_entry_safe(ses_ptr, tmp, head, entry) {
351 if(ses_ptr->sid == sid) {
352 list_del(&ses_ptr->entry);
353 crypto_destroy_session(ses_ptr);
354 break;
358 if (!ses_ptr) {
359 dprintk(1, KERN_ERR, "Session with sid=0x%08X not found!\n", sid);
360 ret = -ENOENT;
362 up(&fcr->sem);
364 return ret;
367 /* Remove all sessions when closing the file */
368 static int
369 crypto_finish_all_sessions(struct fcrypt *fcr)
371 struct csession *tmp, *ses_ptr;
372 struct list_head *head;
374 down(&fcr->sem);
376 head = &fcr->list;
377 list_for_each_entry_safe(ses_ptr, tmp, head, entry) {
378 list_del(&ses_ptr->entry);
379 crypto_destroy_session(ses_ptr);
381 up(&fcr->sem);
383 return 0;
386 /* Look up session by session ID. The returned session is locked. */
387 static struct csession *
388 crypto_get_session_by_sid(struct fcrypt *fcr, uint32_t sid)
390 struct csession *ses_ptr;
392 down(&fcr->sem);
393 list_for_each_entry(ses_ptr, &fcr->list, entry) {
394 if(ses_ptr->sid == sid) {
395 down(&ses_ptr->sem);
396 break;
399 up(&fcr->sem);
401 return ses_ptr;
404 /* This is the main crypto function - feed it with plaintext
405 and get a ciphertext (or vice versa :-) */
406 static int
407 crypto_run(struct fcrypt *fcr, struct crypt_op *cop)
409 char *data, ivp[EALG_MAX_BLOCK_LEN];
410 char __user *src, __user *dst;
411 struct scatterlist sg;
412 struct csession *ses_ptr;
413 unsigned int ivsize=0;
414 size_t nbytes, bufsize;
415 int ret = 0;
416 uint8_t hash_output[HASH_MAX_LEN];
417 struct blkcipher_desc bdesc = {
418 .flags = CRYPTO_TFM_REQ_MAY_SLEEP,
420 struct hash_desc hdesc = {
421 .flags = CRYPTO_TFM_REQ_MAY_SLEEP,
425 if (unlikely(cop->op != COP_ENCRYPT && cop->op != COP_DECRYPT)) {
426 dprintk(1, KERN_DEBUG, "invalid operation op=%u\n", cop->op);
427 return -EINVAL;
430 ses_ptr = crypto_get_session_by_sid(fcr, cop->ses);
431 if (!ses_ptr) {
432 dprintk(1, KERN_ERR, "invalid session ID=0x%08X\n", cop->ses);
433 return -EINVAL;
436 nbytes = cop->len;
437 data = (char*)__get_free_page(GFP_KERNEL);
439 if (unlikely(!data)) {
440 ret = -ENOMEM;
441 goto out_unlock;
443 bufsize = PAGE_SIZE < nbytes ? PAGE_SIZE : nbytes;
445 nbytes = cop->len;
446 bdesc.tfm = ses_ptr->tfm;
447 hdesc.tfm = ses_ptr->hash_tfm;
448 if (hdesc.tfm) {
449 ret = crypto_hash_init(&hdesc);
450 if (unlikely(ret)) {
451 dprintk(1, KERN_ERR,
452 "error in crypto_hash_init()\n");
453 goto out_unlock;
457 if (ses_ptr->tfm) {
458 if (nbytes % crypto_blkcipher_blocksize(ses_ptr->tfm)) {
459 dprintk(1, KERN_ERR,
460 "data size (%zu) isn't a multiple of block size (%u)\n",
461 nbytes, crypto_blkcipher_blocksize(ses_ptr->tfm));
462 ret = -EINVAL;
463 goto out_unlock;
466 ivsize = crypto_blkcipher_ivsize(ses_ptr->tfm);
468 if (cop->iv) {
469 copy_from_user(ivp, cop->iv, ivsize);
470 crypto_blkcipher_set_iv(ses_ptr->tfm, ivp, ivsize);
474 src = cop->src;
475 dst = cop->dst;
478 while(nbytes > 0) {
479 size_t current_len = nbytes > bufsize ? bufsize : nbytes;
481 copy_from_user(data, src, current_len);
483 sg_set_buf(&sg, data, current_len);
485 /* Always hash before encryption and after decryption. Maybe
486 * we should introduce a flag to switch... TBD later on.
488 if (cop->op == COP_ENCRYPT) {
489 if (hdesc.tfm) {
490 ret = crypto_hash_update(&hdesc, &sg, current_len);
491 if (unlikely(ret)) {
492 dprintk(0, KERN_ERR, "CryptoAPI failure: %d\n",ret);
493 goto out;
496 if (bdesc.tfm) {
497 ret = crypto_blkcipher_encrypt(&bdesc, &sg, &sg, current_len);
499 if (unlikely(ret)) {
500 dprintk(0, KERN_ERR, "CryptoAPI failure: %d\n",ret);
501 goto out;
503 copy_to_user(dst, data, current_len);
504 dst += current_len;
506 } else {
507 if (bdesc.tfm) {
508 ret = crypto_blkcipher_decrypt(&bdesc, &sg, &sg, current_len);
510 if (unlikely(ret)) {
511 dprintk(0, KERN_ERR, "CryptoAPI failure: %d\n",ret);
512 goto out;
514 copy_to_user(dst, data, current_len);
515 dst += current_len;
519 if (hdesc.tfm) {
520 ret = crypto_hash_update(&hdesc, &sg, current_len);
521 if (unlikely(ret)) {
522 dprintk(0, KERN_ERR, "CryptoAPI failure: %d\n",ret);
523 goto out;
528 nbytes -= current_len;
529 src += current_len;
532 if (hdesc.tfm) {
533 ret = crypto_hash_final(&hdesc, hash_output);
534 if (unlikely(ret)) {
535 dprintk(0, KERN_ERR, "CryptoAPI failure: %d\n",ret);
536 goto out;
539 copy_to_user(cop->mac, hash_output, crypto_hash_digestsize(ses_ptr->hash_tfm));
542 #if defined(CRYPTODEV_STATS)
543 if (enable_stats) {
544 /* this is safe - we check cop->op at the function entry */
545 ses_ptr->stat[cop->op] += cop->len;
546 if (ses_ptr->stat_max_size < cop->len)
547 ses_ptr->stat_max_size = cop->len;
548 ses_ptr->stat_count++;
550 #endif
552 out:
553 free_page((unsigned long)data);
555 out_unlock:
556 up(&ses_ptr->sem);
558 return ret;
561 /* ====== /dev/crypto ====== */
563 static int
564 cryptodev_open(struct inode *inode, struct file *filp)
566 struct fcrypt *fcr;
568 fcr = kmalloc(sizeof(*fcr), GFP_KERNEL);
569 if(!fcr)
570 return -ENOMEM;
572 memset(fcr, 0, sizeof(*fcr));
573 init_MUTEX(&fcr->sem);
574 INIT_LIST_HEAD(&fcr->list);
575 filp->private_data = fcr;
577 return 0;
580 static int
581 cryptodev_release(struct inode *inode, struct file *filp)
583 struct fcrypt *fcr = filp->private_data;
585 if(fcr) {
586 crypto_finish_all_sessions(fcr);
587 kfree(fcr);
588 filp->private_data = NULL;
591 return 0;
594 static int
595 clonefd(struct file *filp)
597 struct fdtable *fdt = files_fdtable(current->files);
598 int ret;
599 ret = get_unused_fd();
600 if (ret >= 0) {
601 get_file(filp);
602 FD_SET(ret, fdt->open_fds);
603 fd_install(ret, filp);
606 return ret;
609 static int
610 cryptodev_ioctl(struct inode *inode, struct file *filp,
611 unsigned int cmd, unsigned long arg)
613 int __user *p = (void __user *)arg;
614 struct session_op sop;
615 struct crypt_op cop;
616 struct fcrypt *fcr = filp->private_data;
617 uint32_t ses;
618 int ret, fd;
620 if (!fcr)
621 BUG();
623 switch (cmd) {
624 case CIOCASYMFEAT:
625 put_user(0, p);
626 return 0;
627 case CRIOGET:
628 fd = clonefd(filp);
629 put_user(fd, p);
630 return 0;
632 case CIOCGSESSION:
633 copy_from_user(&sop, (void*)arg, sizeof(sop));
634 ret = crypto_create_session(fcr, &sop);
635 if (ret)
636 return ret;
637 copy_to_user((void*)arg, &sop, sizeof(sop));
638 return 0;
640 case CIOCFSESSION:
641 get_user(ses, (uint32_t*)arg);
642 ret = crypto_finish_session(fcr, ses);
643 return ret;
645 case CIOCCRYPT:
646 copy_from_user(&cop, (void*)arg, sizeof(cop));
647 ret = crypto_run(fcr, &cop);
648 copy_to_user((void*)arg, &cop, sizeof(cop));
649 return ret;
651 default:
652 return -EINVAL;
656 struct file_operations cryptodev_fops = {
657 .owner = THIS_MODULE,
658 .open = cryptodev_open,
659 .release = cryptodev_release,
660 .ioctl = cryptodev_ioctl,
663 struct miscdevice cryptodev = {
664 .minor = CRYPTODEV_MINOR,
665 .name = "crypto",
666 .fops = &cryptodev_fops,
669 static int
670 cryptodev_register(void)
672 int rc;
674 rc = misc_register (&cryptodev);
675 if (rc) {
676 printk(KERN_ERR PFX "registeration of /dev/crypto failed\n");
677 return rc;
680 return 0;
683 static void
684 cryptodev_deregister(void)
686 misc_deregister(&cryptodev);
689 /* ====== Module init/exit ====== */
691 int __init init_cryptodev(void)
693 int rc;
695 rc = cryptodev_register();
696 if (rc)
697 return rc;
699 printk(KERN_INFO PFX "driver loaded.\n");
701 return 0;
704 void __exit exit_cryptodev(void)
706 cryptodev_deregister();
707 printk(KERN_INFO PFX "driver unloaded.\n");
710 module_init(init_cryptodev);
711 module_exit(exit_cryptodev);