Still ugly as hell (and therefore TODO):
- no real error handling
- having to pass task_struct and mm_struct from the calling process
  around
- need to copy the IV right at write() time (copy_from_user seems to
  work in process context only)
- ugly branching using is_compat_task() to stay compatible to 32bit
  userland from inside 64bit kernel
---

Hi!

Please do not apply this patch as is - this is just meant for review, therefore
I didn't bother removing debug output and dead code.

IMO, a bigger rewrite of the internal routines in cryptodev_main.c is needed in
order to support async operation in a clean manner: struct crypt_op should be
used for communication with the user only, internally more things need to be
passed around. For zero-copy, this is either the process' corresponding
task_struct and mm_struct OR (when mapping pages at write() time) the set of
user pages and scatterlists. In both cases (copy or not) the given IV needs to
be held separately (the above mentioned copy_from_user problem) and most
importantly some error reporting is needed.

I'm looking forward to hearing your suggestions, hints and flame about my Kung 
Fu. :)

Greetings, Phil
---
 .gitignore              |    1 +
 Makefile                |    3 +-
 cryptodev_int.h         |    2 +-
 cryptodev_main.c        |  356 +++++++++++++++++++++++++++++++++++++++++++----
 examples/Makefile       |    8 +-
 examples/async_cipher.c |  306 ++++++++++++++++++++++++++++++++++++++++
 6 files changed, 642 insertions(+), 34 deletions(-)
 create mode 100644 examples/async_cipher.c

diff --git a/.gitignore b/.gitignore
index 8b6dce9..4335219 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,7 @@ modules.order
 examples/cipher
 examples/hmac
 examples/speed
+examples/async_cipher
 releases
 scripts
 version.h
diff --git a/Makefile b/Makefile
index cd6c8d8..bd84e5e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,5 @@
-KERNEL_DIR = /lib/modules/$(shell uname -r)/build
+#KERNEL_DIR = /lib/modules/$(shell uname -r)/build
+KERNEL_DIR = /home/n0-1/git/make-embedded/linux-2.6
 VERSION = 0.5
 
 cryptodev-objs = cryptodev_main.o cryptodev_cipher.o
diff --git a/cryptodev_int.h b/cryptodev_int.h
index 15eb5bf..1a17413 100644
--- a/cryptodev_int.h
+++ b/cryptodev_int.h
@@ -26,7 +26,7 @@ extern int cryptodev_verbosity;
 
 /* For zero copy */
 int __get_userbuf(uint8_t __user *addr, uint32_t len, int write,
-               int pgcount, struct page **pg, struct scatterlist *sg);
+               int pgcount, struct page **pg, struct scatterlist *sg, struct 
task_struct *task, struct mm_struct *mm);
 void release_user_pages(struct page **pg, int pagecount);
 
 /* last page - first page + 1 */
diff --git a/cryptodev_main.c b/cryptodev_main.c
index a27da96..eea6e78 100644
--- a/cryptodev_main.c
+++ b/cryptodev_main.c
@@ -39,12 +39,16 @@
 #include <linux/random.h>
 #include <linux/syscalls.h>
 #include <linux/pagemap.h>
+#include <linux/poll.h>
 #include <linux/uaccess.h>
 #include "cryptodev.h"
 #include <linux/scatterlist.h>
 #include "cryptodev_int.h"
 #include "version.h"
 
+/* power of two! */
+#define DEF_COP_RINGSIZE 16
+
 MODULE_AUTHOR("Nikos Mavrogiannopoulos <n...@gnutls.org>");
 MODULE_DESCRIPTION("CryptoDev driver");
 MODULE_LICENSE("GPL");
@@ -71,8 +75,26 @@ struct fcrypt {
        struct mutex sem;
 };
 
+union cryptask_result {
+       CR_GOOD = 0;
+       CR_BAD  = 1;
+};
+
+struct todo_list_item {
+       struct list_head __hook;
+       struct crypt_op cop;
+       uint8_t iv[EALG_MAX_BLOCK_LEN];
+       struct mutex sem;
+       struct task_struct *task;
+       struct mm_struct *mm;
+       union cryptask_result result;
+};
+
 struct crypt_priv {
        struct fcrypt fcrypt;
+       struct list_head free, todo, done;
+       struct work_struct cryptask;
+       wait_queue_head_t user_waiter;
 };
 
 #define FILL_SG(sg, ptr, len)                                  \
@@ -523,15 +545,15 @@ void release_user_pages(struct page **pg, int pagecount)
 
 /* fetch the pages addr resides in into pg and initialise sg with them */
 int __get_userbuf(uint8_t __user *addr, uint32_t len, int write,
-               int pgcount, struct page **pg, struct scatterlist *sg)
+               int pgcount, struct page **pg, struct scatterlist *sg, struct 
task_struct *task, struct mm_struct *mm)
 {
        int ret, pglen, i = 0;
        struct scatterlist *sgp;
 
-       down_write(&current->mm->mmap_sem);
-       ret = get_user_pages(current, current->mm,
+       down_write(&mm->mmap_sem);
+       ret = get_user_pages(task, mm,
                        (unsigned long)addr, pgcount, write, 0, pg, NULL);
-       up_write(&current->mm->mmap_sem);
+       up_write(&mm->mmap_sem);
        if (ret != pgcount)
                return -EINVAL;
 
@@ -551,8 +573,8 @@ int __get_userbuf(uint8_t __user *addr, uint32_t len, int 
write,
 }
 
 /* make cop->src and cop->dst available in scatterlists */
-static int get_userbuf(struct csession *ses,
-               struct crypt_op *cop, struct scatterlist **src_sg,
+static int get_userbuf(struct csession *ses, struct crypt_op *cop,
+               struct task_struct *task, struct mm_struct *mm, struct 
scatterlist **src_sg,
                struct scatterlist **dst_sg, int *tot_pages)
 {
        int src_pagecount, dst_pagecount = 0, pagecount, write_src = 1;
@@ -597,7 +619,7 @@ static int get_userbuf(struct csession *ses,
        }
 
        if (__get_userbuf(cop->src, cop->len, write_src,
-                       src_pagecount, ses->pages, ses->sg)) {
+                       src_pagecount, ses->pages, ses->sg, task, mm)) {
                dprintk(1, KERN_ERR,
                        "failed to get user pages for data input\n");
                return -EINVAL;
@@ -608,7 +630,7 @@ static int get_userbuf(struct csession *ses,
                (*dst_sg) = ses->sg + src_pagecount;
 
                if (__get_userbuf(cop->dst, cop->len, 1, dst_pagecount,
-                                       ses->pages + src_pagecount, *dst_sg)) {
+                                       ses->pages + src_pagecount, *dst_sg, 
task, mm)) {
                        dprintk(1, KERN_ERR,
                                "failed to get user pages for data output\n");
                        release_user_pages(ses->pages, src_pagecount);
@@ -620,12 +642,12 @@ static int get_userbuf(struct csession *ses,
 
 /* This is the main crypto function - zero-copy edition */
 static int
-__crypto_run_zc(struct csession *ses_ptr, struct crypt_op *cop)
+__crypto_run_zc(struct csession *ses_ptr, struct crypt_op *cop, struct 
task_struct *task, struct mm_struct *mm)
 {
        struct scatterlist *src_sg, *dst_sg;
        int ret = 0, pagecount;
 
-       ret = get_userbuf(ses_ptr, cop, &src_sg, &dst_sg, &pagecount);
+       ret = get_userbuf(ses_ptr, cop, task, mm, &src_sg, &dst_sg, &pagecount);
        if (unlikely(ret)) {
                dprintk(1, KERN_ERR, "Error getting user pages. \
                                        Falling back to non zero copy.\n");
@@ -640,7 +662,7 @@ __crypto_run_zc(struct csession *ses_ptr, struct crypt_op 
*cop)
 
 #endif /* DISABLE_ZCOPY */
 
-static int crypto_run(struct fcrypt *fcr, struct crypt_op *cop)
+static int crypto_run(struct fcrypt *fcr, struct crypt_op *cop, struct 
task_struct *task, struct mm_struct *mm, u8 *user_iv)
 {
        struct csession *ses_ptr;
        uint8_t hash_output[AALG_MAX_RESULT_LEN];
@@ -679,21 +701,23 @@ static int crypto_run(struct fcrypt *fcr, struct crypt_op 
*cop)
                        goto out_unlock;
                }
 
-               if (cop->iv) {
+               if (cop->iv || user_iv) {
                        uint8_t iv[EALG_MAX_BLOCK_LEN];
-
-                       ret = copy_from_user(iv, cop->iv,
-                               min((int)sizeof(iv), (ses_ptr->cdata.ivsize)));
-                       if (unlikely(ret)) {
-                               dprintk(1, KERN_ERR,
-                                       "error copying IV (%d bytes)\n",
-                                       min((int)sizeof(iv),
-                                       (ses_ptr->cdata.ivsize)));
-                               ret = -EFAULT;
-                               goto out_unlock;
+                       if (!user_iv) {
+
+                               ret = copy_from_user(iv, cop->iv,
+                                       min((int)sizeof(iv), 
(ses_ptr->cdata.ivsize)));
+                               if (unlikely(ret)) {
+                                       dprintk(1, KERN_ERR,
+                                               "error copying IV (%d bytes), 
copy_from_user returned %d for address %lx\n",
+                                               min((int)sizeof(iv),
+                                               (ses_ptr->cdata.ivsize)), ret, 
(unsigned long)cop->iv);
+                                       ret = -EFAULT;
+                                       goto out_unlock;
+                               }
                        }
 
-                       cryptodev_cipher_set_iv(&ses_ptr->cdata, iv,
+                       cryptodev_cipher_set_iv(&ses_ptr->cdata, user_iv ? 
user_iv : iv,
                                                ses_ptr->cdata.ivsize);
                }
        }
@@ -702,7 +726,7 @@ static int crypto_run(struct fcrypt *fcr, struct crypt_op 
*cop)
 #ifdef DISABLE_ZCOPY
                ret = __crypto_run_std(ses_ptr, cop);
 #else /* normal */
-               ret = __crypto_run_zc(ses_ptr, cop);
+               ret = __crypto_run_zc(ses_ptr, cop, task, mm);
 #endif
                if (unlikely(ret))
                        goto out_unlock;
@@ -738,12 +762,38 @@ out_unlock:
        return ret;
 }
 
+static void cryptask_routine(struct work_struct *work)
+{
+       struct crypt_priv *pcr = container_of(work, struct crypt_priv, 
cryptask);
+       struct todo_list_item *item, *item_safe;
+
+       printk(KERN_INFO "%s: being called\n", __func__);
+
+       list_for_each_entry_safe(item, item_safe, &pcr->todo, __hook) {
+               mutex_lock(&item->sem);
+               printk(KERN_INFO "%s: calling crypto_run for item %lu\n", 
__func__, (unsigned long)item);
+               printk(KERN_INFO "%s: task is now %lu, mm %lu\n", __func__, 
(unsigned long)item->task, (unsigned long)item->mm);
+               /* FIXME: error handling! */
+               if (crypto_run(&pcr->fcrypt, &item->cop, item->task, item->mm, 
item->iv)) {
+                       item->result = CR_BAD;
+                       printk(KERN_ERR "%s: crypto_run() failed!\n", __func__);
+               } else {
+                       item->result = CR_GOOD;
+               }
+               list_move(&item->__hook, &pcr->done);
+               mutex_unlock(&item->sem);
+
+               wake_up_interruptible(&pcr->user_waiter);
+       }
+}
+
 /* ====== /dev/crypto ====== */
 
 static int
 cryptodev_open(struct inode *inode, struct file *filp)
 {
        struct crypt_priv *pcr;
+       int i;
 
        pcr = kmalloc(sizeof(*pcr), GFP_KERNEL);
        if (!pcr)
@@ -753,7 +803,21 @@ cryptodev_open(struct inode *inode, struct file *filp)
        mutex_init(&pcr->fcrypt.sem);
        INIT_LIST_HEAD(&pcr->fcrypt.list);
 
+       INIT_LIST_HEAD(&pcr->free);
+       INIT_LIST_HEAD(&pcr->todo);
+       INIT_LIST_HEAD(&pcr->done);
+       INIT_WORK(&pcr->cryptask, cryptask_routine);
+       init_waitqueue_head(&pcr->user_waiter);
+
+       for (i = 0; i < DEF_COP_RINGSIZE; i++) {
+               struct todo_list_item *tmp = kzalloc(sizeof(struct 
todo_list_item), GFP_KERNEL);
+               printk(KERN_INFO "%s: allocated new item at %lu\n", __func__, 
(unsigned long)tmp);
+               mutex_init(&tmp->sem);
+               list_add(&tmp->__hook, &pcr->free);
+       }
+
        filp->private_data = pcr;
+       dprintk(2, KERN_DEBUG, "Cryptodev handle initialised, %d elements in 
queue\n", DEF_COP_RINGSIZE);
        return 0;
 }
 
@@ -761,13 +825,36 @@ static int
 cryptodev_release(struct inode *inode, struct file *filp)
 {
        struct crypt_priv *pcr = filp->private_data;
+       struct todo_list_item *item, *item_safe;
+       int items_freed = 0;
 
-       if (pcr) {
-               crypto_finish_all_sessions(&pcr->fcrypt);
-               kfree(pcr);
-               filp->private_data = NULL;
+       printk(KERN_INFO "%s: being called\n", __func__);
+       if (!pcr)
+               return 0;
+
+       cancel_work_sync(&pcr->cryptask);
+       printk(KERN_INFO "%s: canceled cryptask\n", __func__);
+
+       list_splice_tail(&pcr->todo, &pcr->free);
+       list_splice_tail(&pcr->done, &pcr->free);
+       printk(KERN_INFO "%s: lists spliced\n", __func__);
+
+       list_for_each_entry_safe(item, item_safe, &pcr->free, __hook) {
+               printk(KERN_INFO "%s: freeing item at %lu\n", __func__, 
(unsigned long)item);
+               list_del(&item->__hook);
+               printk(KERN_INFO "%s: deleted item from list\n", __func__);
+               kfree(item);
+               printk(KERN_INFO "%s: item kfree'd\n", __func__);
+               items_freed++;
        }
+       printk(KERN_INFO "%s: %d items freed\n", __func__, items_freed);
+
+       crypto_finish_all_sessions(&pcr->fcrypt);
+       kfree(pcr);
+       filp->private_data = NULL;
 
+       printk(KERN_INFO "%s: returning\n", __func__);
+       dprintk(2, KERN_DEBUG, "Cryptodev handle deinitialised, %d elements 
freed\n", items_freed);
        return 0;
 }
 
@@ -835,7 +922,7 @@ cryptodev_ioctl(struct file *filp, unsigned int cmd, 
unsigned long arg_)
                if (unlikely(copy_from_user(&cop, arg, sizeof(cop))))
                        return -EFAULT;
 
-               ret = crypto_run(fcr, &cop);
+               ret = crypto_run(fcr, &cop, current, 0, 0);
                if (unlikely(ret))
                        return ret;
                if (unlikely(copy_to_user(arg, &cop, sizeof(cop))))
@@ -949,7 +1036,7 @@ cryptodev_compat_ioctl(struct file *file, unsigned int 
cmd, unsigned long arg_)
 
                compat_to_crypt_op(&compat_cop, &cop);
 
-               ret = crypto_run(fcr, &cop);
+               ret = crypto_run(fcr, &cop, current, 0, 0);
                if (unlikely(ret))
                        return ret;
 
@@ -966,14 +1053,223 @@ cryptodev_compat_ioctl(struct file *file, unsigned int 
cmd, unsigned long arg_)
 
 #endif /* CONFIG_COMPAT */
 
+static ssize_t cryptodev_read(struct file *filp, char __user *buf, size_t 
buflen, loff_t *offset)
+{
+       int ret = 0;
+       struct crypt_priv *pcr = filp->private_data;
+       struct todo_list_item *item;
+       struct compat_crypt_op compat_cop;
+
+       printk(KERN_INFO "%s: being called, buflen is %zd\n", __func__, buflen);
+
+again:
+       if (is_compat_task()) {
+               printk(KERN_INFO "%s: is compat task\n", __func__);
+               list_for_each_entry(item, &pcr->done, __hook) {
+                       if (buflen - ret < sizeof(compat_cop))
+                               break;
+
+                       mutex_lock(&item->sem);
+                       crypt_op_to_compat(&item->cop, &compat_cop);
+                       /*
+                       if (copy_to_user(item->cop.iv, item->iv, 
sizeof(item->cop.iv))) {
+                               mutex_unlock(&item->sem);
+                               break;
+                       }
+                       */
+                       if (copy_to_user(buf + ret, &compat_cop, 
sizeof(compat_cop))) {
+                               mutex_unlock(&item->sem);
+                               break;
+                       }
+                       list_move_tail(&item->__hook, &pcr->free);
+                       mutex_unlock(&item->sem);
+
+                       ret += sizeof(compat_cop);
+               }
+               if (buflen - ret >= sizeof(compat_cop)) {
+                       /* block when no done descriptors are available */
+                       while (list_empty(&pcr->done)) {
+                               set_current_state(TASK_UNINTERRUPTIBLE);
+                               if (!list_empty(&pcr->done))
+                                       break;
+                               io_schedule();
+                       }
+                       __set_current_state(TASK_RUNNING);
+                       goto again;
+               }
+       } else {
+               list_for_each_entry(item, &pcr->done, __hook) {
+                       if (buflen - ret <sizeof(item->cop))
+                               break;
+
+                       mutex_lock(&item->sem);
+                       /*
+                       if (copy_to_user(item->cop.iv, item->iv, 
sizeof(item->cop.iv))) {
+                               mutex_unlock(&item->sem);
+                               break;
+                       }
+                       */
+                       if (copy_to_user(buf + ret, &item->cop, 
sizeof(item->cop))) {
+                               mutex_unlock(&item->sem);
+                               break;
+                       }
+                       list_move_tail(&item->__hook, &pcr->free);
+                       mutex_unlock(&item->sem);
+
+                       ret += sizeof(item->cop);
+               }
+               if (buflen - ret >= sizeof(struct crypt_op)) {
+                       /* block when no done descriptors are available */
+                       while (list_empty(&pcr->done)) {
+                               set_current_state(TASK_UNINTERRUPTIBLE);
+                               if (!list_empty(&pcr->done))
+                                       break;
+                               io_schedule();
+                       }
+                       __set_current_state(TASK_RUNNING);
+                       goto again;
+               }
+       }
+       if (ret)
+               wake_up_interruptible(&pcr->user_waiter);
+       return ret;
+
+}
+
+static ssize_t cryptodev_write(struct file *filp,
+               const char __user *buf, size_t buflen, loff_t *offset)
+{
+       int read = 0;
+       struct crypt_priv *pcr = filp->private_data;
+       struct todo_list_item *item;
+       struct compat_crypt_op compat_cop;
+       int i;
+
+       printk(KERN_INFO "%s: being called, buflen is %zd, crypt_op is %zd 
bytes big\n",
+                       __func__, buflen, sizeof(struct crypt_op));
+       printk(KERN_INFO "%s: current is %lu, mm %lu\n", __func__, (unsigned 
long)current, (unsigned long)current->mm);
+
+       /*
+       if (list_empty(pcr->free))
+               return 0;
+       */
+
+again:
+       if (is_compat_task()) {
+               printk(KERN_INFO "%s: is compat task\n", __func__);
+               list_for_each_entry(item, &pcr->free, __hook) {
+                       if (buflen - read < sizeof(compat_cop))
+                               break;
+
+                       mutex_lock(&item->sem);
+                       if (copy_from_user(&compat_cop, buf + read, 
sizeof(compat_cop))) {
+                               printk(KERN_INFO "%s: copy_from_user for 
crypt_op failed!\n", __func__);
+                               mutex_unlock(&item->sem);
+                               return -EFAULT;
+                       }
+                       memset(&item->cop, 0, sizeof(item->cop));
+                       memset(item->iv, 0, EALG_MAX_BLOCK_LEN);
+                       compat_to_crypt_op(&compat_cop, &item->cop);
+                       //if (copy_from_user(item->iv, item->cop.iv, 
sizeof(item->cop.iv))) {
+                       if (copy_from_user(item->iv, item->cop.iv, 16)) {
+                               printk(KERN_INFO "%s: copy_from_user for IV 
failed!\n", __func__);
+                               mutex_unlock(&item->sem);
+                               return -EFAULT;
+                       }
+                       printk(KERN_INFO "%s: IV is: ", __func__);
+                       for (i = 0; i < 16; i++)
+                               printk("%02x ", item->iv[i]);
+                       printk("\n");
+                       item->task = current;
+                       item->mm = current->mm;
+                       list_move_tail(&item->__hook, &pcr->todo);
+                       mutex_unlock(&item->sem);
+
+                       read += sizeof(compat_cop);
+               }
+               schedule_work(&pcr->cryptask);
+
+               /* block when no free descriptors are available */
+               if (buflen - read >= sizeof(compat_cop)) {
+                       while (list_empty(&pcr->free)) {
+                               set_current_state(TASK_UNINTERRUPTIBLE);
+                               if (!list_empty(&pcr->free))
+                                       break;
+                               io_schedule();
+                       }
+                       __set_current_state(TASK_RUNNING);
+                       goto again;
+               }
+       } else {
+               list_for_each_entry(item, &pcr->free, __hook) {
+                       if (buflen - read < sizeof(item->cop))
+                               break;
+
+                       mutex_lock(&item->sem);
+                       memset(&item->cop, 0, sizeof(item->cop));
+                       memset(item->iv, 0, EALG_MAX_BLOCK_LEN);
+                       if (copy_from_user(&item->cop, buf + read, 
sizeof(item->cop))) {
+                               printk(KERN_INFO "%s: copy_from_user for 
crypt_op failed!\n", __func__);
+                               mutex_unlock(&item->sem);
+                               return -EFAULT;
+                       }
+                       if (copy_from_user(item->iv, item->cop.iv, 
sizeof(item->cop.iv))) {
+                               printk(KERN_INFO "%s: copy_from_user for IV 
failed!\n", __func__);
+                               mutex_unlock(&item->sem);
+                               return -EFAULT;
+                       }
+                       item->task = current;
+                       item->mm = current->mm;
+                       list_move_tail(&item->__hook, &pcr->todo);
+                       mutex_unlock(&item->sem);
+
+                       read += sizeof(item->cop);
+               }
+               schedule_work(&pcr->cryptask);
+               /* block when no free descriptors are available */
+               if (buflen - read >= sizeof(struct crypt_op)) {
+                       while (list_empty(&pcr->free)) {
+                               set_current_state(TASK_UNINTERRUPTIBLE);
+                               if (!list_empty(&pcr->free))
+                                       break;
+                               io_schedule();
+                       }
+                       __set_current_state(TASK_RUNNING);
+                       goto again;
+               }
+       }
+       /* TODO: trigger work_queue */
+       return read;
+}
+
+static unsigned int cryptodev_poll(struct file *file, poll_table *wait)
+{
+       struct crypt_priv *pcr = file->private_data;
+       int ret = 0;
+
+       poll_wait(file, &pcr->user_waiter, wait);
+
+       /* trigger for POLLIN when pcr->done is non-empty */
+       if (!list_empty(&pcr->done))
+               ret |= POLLIN | POLLRDNORM;
+       if (!list_empty(&pcr->free))
+               ret |= POLLOUT | POLLWRNORM;
+
+       printk(KERN_INFO "%s: returning %x\n", __func__, ret);
+       return ret;
+}
+
 static const struct file_operations cryptodev_fops = {
        .owner = THIS_MODULE,
        .open = cryptodev_open,
+       .read = cryptodev_read,
+       .write = cryptodev_write,
        .release = cryptodev_release,
        .unlocked_ioctl = cryptodev_ioctl,
 #ifdef CONFIG_COMPAT
        .compat_ioctl = cryptodev_compat_ioctl,
 #endif /* CONFIG_COMPAT */
+       .poll = cryptodev_poll,
 };
 
 static struct miscdevice cryptodev = {
diff --git a/examples/Makefile b/examples/Makefile
index 937eb6d..9ddec43 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -1,13 +1,17 @@
 KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build
 
-hostprogs := cipher hmac speed
+CFLAGS=-m32
+
+#hostprogs := cipher hmac speed async-cipher
+hostprogs := async_cipher
 example-cipher-objs := cipher.o
 example-hmac-objs := hmac.o
 example-speed-objs := speed.c
+example-async_cipher-objs := async_cipher.o
 
 check: $(hostprogs)
        ./cipher
        ./hmac
 
 clean:
-       rm -f *.o *~ hmac cipher speed
+       rm -f *.o *~ hmac cipher speed async_cipher
diff --git a/examples/async_cipher.c b/examples/async_cipher.c
new file mode 100644
index 0000000..ee6a1f5
--- /dev/null
+++ b/examples/async_cipher.c
@@ -0,0 +1,306 @@
+/*
+ * Demo on how to use /dev/crypto device for ciphering.
+ *
+ * Placed under public domain.
+ *
+ */
+#include <poll.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <sys/ioctl.h>
+#include <crypto/cryptodev.h>
+
+#define        DATA_SIZE       8*1024
+#define        BLOCK_SIZE      16
+#define        KEY_SIZE        16
+
+static int
+test_crypto(int cfd)
+{
+       char plaintext[DATA_SIZE];
+       char ciphertext[DATA_SIZE];
+       char iv[BLOCK_SIZE];
+       char key[KEY_SIZE];
+
+       struct session_op sess;
+       struct crypt_op cryp;
+
+       printf("running %s\n", __func__);
+
+       memset(&sess, 0, sizeof(sess));
+       memset(&cryp, 0, sizeof(cryp));
+
+       memset(plaintext, 0x15,  sizeof(plaintext));
+       memset(key, 0x33,  sizeof(key));
+       memset(iv, 0x03,  sizeof(iv));
+
+       /* Get crypto session for AES128 */
+       sess.cipher = CRYPTO_AES_CBC;
+       sess.keylen = KEY_SIZE;
+       sess.key = key;
+       if (ioctl(cfd, CIOCGSESSION, &sess)) {
+               perror("ioctl(CIOCGSESSION)");
+               return 1;
+       }
+
+
+       /* Encrypt data.in to data.encrypted */
+       cryp.ses = sess.ses;
+       cryp.len = sizeof(plaintext);
+       cryp.src = plaintext;
+       cryp.dst = ciphertext;
+       cryp.iv = iv;
+       cryp.op = COP_ENCRYPT;
+       if (ioctl(cfd, CIOCCRYPT, &cryp)) {
+               perror("ioctl(CIOCCRYPT)");
+               return 1;
+       }
+
+       if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
+               perror("ioctl(CIOCFSESSION)");
+               return 1;
+       }
+
+       if (ioctl(cfd, CIOCGSESSION, &sess)) {
+               perror("ioctl(CIOCGSESSION)");
+               return 1;
+       }
+       
+       /* Decrypt data.encrypted to data.decrypted */
+       cryp.ses = sess.ses;
+       cryp.len = sizeof(plaintext);
+       cryp.src = ciphertext;
+       cryp.dst = ciphertext;
+       cryp.iv = iv;
+       cryp.op = COP_DECRYPT;
+       if (ioctl(cfd, CIOCCRYPT, &cryp)) {
+               perror("ioctl(CIOCCRYPT)");
+               return 1;
+       }
+
+       /* Verify the result */
+       if (memcmp(plaintext, ciphertext, sizeof(plaintext)) != 0) {
+               fprintf(stderr,
+                       "FAIL: Decrypted data are different from the input 
data.\n");
+               return 1;
+       } else
+               printf("Test passed\n");
+
+       /* Finish crypto session */
+       if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
+               perror("ioctl(CIOCFSESSION)");
+               return 1;
+       }
+
+       return 0;
+}
+
+static int test_aes(int cfd)
+{
+       char plaintext1[BLOCK_SIZE];
+       char ciphertext1[BLOCK_SIZE] = { 0xdf, 0x55, 0x6a, 0x33, 0x43, 0x8d, 
0xb8, 0x7b, 0xc4, 0x1b, 0x17, 0x52, 0xc5, 0x5e, 0x5e, 0x49 };
+       char iv1[BLOCK_SIZE];
+       char key1[KEY_SIZE] = { 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+       char plaintext2[BLOCK_SIZE] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00 };
+       char ciphertext2[BLOCK_SIZE] = { 0xb7, 0x97, 0x2b, 0x39, 0x41, 0xc4, 
0x4b, 0x90, 0xaf, 0xa7, 0xb2, 0x64, 0xbf, 0xba, 0x73, 0x87 };
+       char iv2[BLOCK_SIZE];
+       char key2[KEY_SIZE];
+
+       struct session_op sess1, sess2;
+       struct crypt_op cryp1, cryp2;
+       struct pollfd pfd;
+
+       printf("running %s\n", __func__);
+
+       memset(&sess1, 0, sizeof(sess1));
+       memset(&sess2, 0, sizeof(sess2));
+       memset(&cryp1, 0, sizeof(cryp1));
+       memset(&cryp2, 0, sizeof(cryp2));
+
+       memset(plaintext1, 0x0, sizeof(plaintext1));
+       memset(iv1, 0x0, sizeof(iv1));
+
+       /* Get crypto session for AES128 */
+       sess1.cipher = CRYPTO_AES_CBC;
+       sess1.keylen = KEY_SIZE;
+       sess1.key = key1;
+       if (ioctl(cfd, CIOCGSESSION, &sess1)) {
+               perror("ioctl(CIOCGSESSION)");
+               return 1;
+       }
+
+       memset(key2, 0x0, sizeof(key2));
+
+       /* Get second crypto session for AES128 */
+       sess2.cipher = CRYPTO_AES_CBC;
+       sess2.keylen = KEY_SIZE;
+       sess2.key = key2;
+       if (ioctl(cfd, CIOCGSESSION, &sess2)) {
+               perror("ioctl(CIOCGSESSION)");
+               return 1;
+       }
+
+       pfd.fd = cfd;
+       pfd.events = POLLOUT;
+       if (poll(&pfd, 1, 0) < 1) {
+               perror("poll()");
+               return 1;
+       }
+       printf("poll() returned good\n");
+
+       /* Encrypt data.in to data.encrypted */
+       cryp1.ses = sess1.ses;
+       cryp1.len = sizeof(plaintext1);
+       cryp1.src = plaintext1;
+       cryp1.dst = plaintext1;
+       cryp1.iv = iv1;
+       cryp1.op = COP_ENCRYPT;
+       if (write(cfd, &cryp1, sizeof(cryp1)) < sizeof(cryp1)) {
+               perror("write(cryp1)");
+               return 1;
+       }
+       printf("cryp1 written out\n");
+
+       memset(iv2, 0x0, sizeof(iv2));
+
+       /* Encrypt data.in to data.encrypted */
+       cryp2.ses = sess2.ses;
+       cryp2.len = sizeof(plaintext2);
+       cryp2.src = plaintext2;
+       cryp2.dst = plaintext2;
+       cryp2.iv = iv2;
+       cryp2.op = COP_ENCRYPT;
+
+       pfd.fd = cfd;
+       pfd.events = POLLOUT;
+       if (poll(&pfd, 1, 0) < 1) {
+               perror("poll()");
+               return 1;
+       }
+       printf("poll() returned good\n");
+
+       if (write(cfd, &cryp2, sizeof(cryp2)) < sizeof(cryp2)) {
+               perror("write(cryp2)");
+               return 1;
+       }
+       printf("cryp2 written out\n");
+
+       pfd.fd = cfd;
+       pfd.events = POLLIN;
+       if (poll(&pfd, 1, 0) < 1) {
+               perror("poll()");
+               return 1;
+       }
+       printf("poll() returned good\n");
+
+       if (read(cfd, &cryp1, sizeof(cryp1)) < sizeof(cryp1) ||
+           read(cfd, &cryp2, sizeof(cryp2)) < sizeof(cryp2)) {
+               perror("read(cryp1 + cryp2)");
+               return 1;
+       }
+       printf("cryp1 + cryp2 successfully read\n");
+
+       /* Verify the result */
+       if (memcmp(plaintext1, ciphertext1, sizeof(plaintext1)) != 0) {
+               int i;
+               fprintf(stderr,
+                       "FAIL: Decrypted data are different from the input 
data.\n");
+               printf("cipher text should be:\t");
+               for (i = 0; i < BLOCK_SIZE; i++) {
+                       printf("%02x ", (unsigned char)ciphertext1[i]);
+               }
+               printf("\nbut really is:\t\t");
+               for (i = 0; i < BLOCK_SIZE; i++) {
+                       printf("%02x ", (unsigned char)plaintext1[i]);
+               }
+               printf("\n");
+               return 1;
+       } else {
+               printf("result 1 passed\n");
+       }
+
+       /* Test 2 */
+
+       /* Verify the result */
+       if (memcmp(plaintext2, ciphertext2, sizeof(plaintext2)) != 0) {
+               int i;
+               fprintf(stderr,
+                       "FAIL: Decrypted data are different from the input 
data.\n");
+               printf("cipher text should be:\t");
+               for (i = 0; i < BLOCK_SIZE; i++) {
+                       printf("%02x ", (unsigned char)ciphertext2[i]);
+               }
+               printf("\nbut really is:\t\t");
+               for (i = 0; i < BLOCK_SIZE; i++) {
+                       printf("%02x ", (unsigned char)plaintext2[i]);
+               }
+               printf("\n");
+               return 1;
+       } else {
+               printf("result 2 passed\n");
+       }
+
+       printf("AES Test passed\n");
+
+       /* Finish crypto session */
+       if (ioctl(cfd, CIOCFSESSION, &sess1.ses)) {
+               perror("ioctl(CIOCFSESSION)");
+               return 1;
+       }
+       if (ioctl(cfd, CIOCFSESSION, &sess2.ses)) {
+               perror("ioctl(CIOCFSESSION)");
+               return 1;
+       }
+
+       return 0;
+}
+
+int
+main()
+{
+       int fd = -1, cfd = -1;
+
+       /* Open the crypto device */
+       fd = open("/dev/crypto", O_RDWR, 0);
+       if (fd < 0) {
+               perror("open(/dev/crypto)");
+               return 1;
+       }
+
+       /* Clone file descriptor */
+       if (ioctl(fd, CRIOGET, &cfd)) {
+               perror("ioctl(CRIOGET)");
+               return 1;
+       }
+
+       /* Set close-on-exec (not really neede here) */
+       if (fcntl(cfd, F_SETFD, 1) == -1) {
+               perror("fcntl(F_SETFD)");
+               return 1;
+       }
+
+       /* Run the test itself */
+       if (test_aes(cfd))
+               return 1;
+
+       if (test_crypto(cfd))
+               return 1;
+
+       /* Close cloned descriptor */
+       if (close(cfd)) {
+               perror("close(cfd)");
+               return 1;
+       }
+
+       /* Close the original descriptor */
+       if (close(fd)) {
+               perror("close(fd)");
+               return 1;
+       }
+
+       return 0;
+}
+
-- 
1.7.2.2



_______________________________________________
Cryptodev-linux-devel mailing list
Cryptodev-linux-devel@gna.org
https://mail.gna.org/listinfo/cryptodev-linux-devel

Reply via email to