diff options
-rw-r--r-- | Makefile | 14 | ||||
-rw-r--r-- | cryptodev.c | 711 | ||||
-rw-r--r-- | cryptodev.h | 156 | ||||
-rw-r--r-- | cryptodev.mod.c | 57 | ||||
-rw-r--r-- | example1.c | 133 | ||||
-rw-r--r-- | example2.c | 182 |
6 files changed, 1253 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..12bb5e2 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +KERNEL_DIR := /lib/modules/$(shell uname -r)/build + +obj-m += cryptodev.o + +build: + make -C $(KERNEL_DIR) SUBDIRS=`pwd` modules + +install: + make -C $(KERNEL_DIR) SUBDIRS=`pwd` modules_install + @echo "Installing cryptodev.h in /usr/include/crypto ..." + @install -D cryptodev.h /usr/include/crypto + +clean: + make -C $(KERNEL_DIR) SUBDIRS=`pwd` clean diff --git a/cryptodev.c b/cryptodev.c new file mode 100644 index 0000000..6007648 --- /dev/null +++ b/cryptodev.c @@ -0,0 +1,711 @@ +/* + * Driver for /dev/crypto device (aka CryptoDev) + * + * Copyright (c) 2004 Michal Ludvig <mludvig@logix.net.nz>, SuSE Labs + * Copyright (c) 2009 Nikos Mavrogiannopoulos <nmav@gennetsa.com>, Gennet S.A. + * + * Device /dev/crypto provides an interface for + * accessing kernel CryptoAPI algorithms (ciphers, + * hashes) from userspace programs. + * + * /dev/crypto interface was originally introduced in + * OpenBSD and this module attempts to keep the API, + * although a bit extended. + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/fdtable.h> +#include <linux/miscdevice.h> +#include <linux/crypto.h> +#include <linux/mm.h> +#include <linux/highmem.h> +#include <linux/random.h> +#include "cryptodev.h" +#include <asm/uaccess.h> +#include <asm/ioctl.h> +#include <linux/scatterlist.h> + +MODULE_AUTHOR("Michal Ludvig <mludvig@logix.net.nz>"); +MODULE_DESCRIPTION("CryptoDev driver"); +MODULE_LICENSE("Dual BSD/GPL"); + +/* ====== Compile-time config ====== */ + +#define CRYPTODEV_STATS + +/* ====== Module parameters ====== */ + +static int verbosity = 0; +module_param(verbosity, int, 0644); +MODULE_PARM_DESC(verbosity, "0: normal, 1: verbose, 2: debug"); + +#ifdef CRYPTODEV_STATS +static int enable_stats = 0; +module_param(enable_stats, int, 0644); +MODULE_PARM_DESC(enable_stats, "collect statictics about cryptodev usage"); +#endif + +/* ====== Debug helpers ====== */ + +#define PFX "cryptodev: " +#define dprintk(level,severity,format,a...) \ + do { \ + if (level <= verbosity) \ + printk(severity PFX "%s[%u]: " format, \ + current->comm, current->pid, \ + ##a); \ + } while (0) + +/* ====== CryptoAPI ====== */ + +#define FILL_SG(sg,ptr,len) \ + do { \ + (sg)->page = virt_to_page(ptr); \ + (sg)->offset = offset_in_page(ptr); \ + (sg)->length = len; \ + (sg)->dma_address = 0; \ + } while (0) + +struct csession { + struct list_head entry; + struct semaphore sem; + struct crypto_blkcipher *tfm; + struct crypto_hash *hash_tfm; + uint32_t sid; +#ifdef CRYPTODEV_STATS +#if ! ((COP_ENCRYPT < 2) && (COP_DECRYPT < 2)) +#error Struct csession.stat uses COP_{ENCRYPT,DECRYPT} as indices. Do something! +#endif + unsigned long long stat[2]; + size_t stat_max_size, stat_count; +#endif +}; + +struct fcrypt { + struct list_head list; + struct semaphore sem; +}; + +/* Prepare session for future use. */ +static int +crypto_create_session(struct fcrypt *fcr, struct session_op *sop) +{ + struct csession *ses_new, *ses_ptr; + struct crypto_blkcipher *blk_tfm=NULL; + struct crypto_hash *hash_tfm=NULL; + int ret = 0; + const char *alg_name=NULL; + const char *hash_name=NULL; + struct blkcipher_alg* blkalg; + int hmac_mode = 1; + + /* Does the request make sense? */ + if (!sop->cipher && !sop->mac) { + dprintk(1,KERN_DEBUG,"Both 'cipher' and 'mac' unset.\n"); + return -EINVAL; + } + + switch (sop->cipher) { + case 0: + break; + case CRYPTO_DES_CBC: + alg_name = "cbc(des)"; + break; + case CRYPTO_3DES_CBC: + alg_name = "cbc(3des_ede)"; + break; + case CRYPTO_BLF_CBC: + alg_name = "cbc(blowfish)"; + break; + case CRYPTO_AES_CBC: + alg_name = "cbc(aes)"; + break; + case CRYPTO_CAMELLIA_CBC: + alg_name = "cbc(camelia)"; + break; + default: + dprintk(1,KERN_DEBUG,"%s: bad cipher: %d\n", __func__, sop->cipher); + return -EINVAL; + } + + switch (sop->mac) { + case 0: + break; + case CRYPTO_MD5_HMAC: + hash_name = "hmac(md5)"; + break; + case CRYPTO_RIPEMD160_HMAC: + hash_name = "hmac(rmd160)"; + break; + case CRYPTO_SHA1_HMAC: + hash_name = "hmac(sha1)"; + break; + case CRYPTO_SHA2_256_HMAC: + hash_name = "hmac(sha256)"; + break; + case CRYPTO_SHA2_384_HMAC: + hash_name = "hmac(sha384)"; + break; + case CRYPTO_SHA2_512_HMAC: + hash_name = "hmac(sha512)"; + break; + + /* non-hmac cases */ + case CRYPTO_MD5: + hash_name = "md5"; + hmac_mode = 0; + break; + case CRYPTO_RIPEMD160: + hash_name = "rmd160"; + hmac_mode = 0; + break; + case CRYPTO_SHA1: + hash_name = "sha1"; + hmac_mode = 0; + break; + case CRYPTO_SHA2_256: + hash_name = "sha256"; + hmac_mode = 0; + break; + case CRYPTO_SHA2_384: + hash_name = "sha384"; + hmac_mode = 0; + break; + case CRYPTO_SHA2_512: + hash_name = "sha512"; + hmac_mode = 0; + break; + + default: + dprintk(1,KERN_DEBUG,"%s: bad mac: %d\n", __func__, sop->mac); + return -EINVAL; + } + + /* Set-up crypto transform. */ + if (alg_name) { + uint8_t keyp[CRYPTO_CIPHER_MAX_KEY_LEN]; + + blk_tfm = crypto_alloc_blkcipher(alg_name, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(blk_tfm)) { + dprintk(1,KERN_DEBUG,"%s: Failed to load transform for %s\n", __func__, + alg_name); + return -EINVAL; + } + + blkalg = crypto_blkcipher_alg(blk_tfm); + if (blkalg != NULL) { + /* Was correct key length supplied? */ + if ((sop->keylen < blkalg->min_keysize) || + (sop->keylen > blkalg->max_keysize)) { + dprintk(0,KERN_DEBUG,"Wrong keylen '%zu' for algorithm '%s'. Use %u to %u.\n", + sop->keylen, alg_name, blkalg->min_keysize, + blkalg->max_keysize); + ret = -EINVAL; + goto error; + } + } + + if (sop->keylen > CRYPTO_CIPHER_MAX_KEY_LEN) { + dprintk(0,KERN_DEBUG,"Setting key failed for %s-%zu.\n", + alg_name, sop->keylen*8); + ret = -EINVAL; + goto error; + } + + /* Copy the key from user and set to TFM. */ + copy_from_user(keyp, sop->key, sop->keylen); + ret = crypto_blkcipher_setkey(blk_tfm, keyp, sop->keylen); + if (ret) { + dprintk(0,KERN_DEBUG,"Setting key failed for %s-%zu.\n", + alg_name, sop->keylen*8); + ret = -EINVAL; + goto error; + } + + } + + if (hash_name) { + hash_tfm = crypto_alloc_hash(hash_name, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(hash_tfm)) { + dprintk(1,KERN_DEBUG,"%s: Failed to load transform for %s\n", __func__, + hash_name); + return -EINVAL; + } + + /* Copy the key from user and set to TFM. */ + if (hmac_mode != 0) { + uint8_t hkeyp[CRYPTO_HMAC_MAX_KEY_LEN]; + + if (sop->mackeylen > CRYPTO_HMAC_MAX_KEY_LEN) { + dprintk(0,KERN_DEBUG,"Setting hmac key failed for %s-%zu.\n", + hash_name, sop->mackeylen*8); + ret = -EINVAL; + goto error; + } + + copy_from_user(hkeyp, sop->mackey, sop->mackeylen); + ret = crypto_hash_setkey(hash_tfm, hkeyp, sop->mackeylen); + if (ret) { + dprintk(0,KERN_DEBUG,"Setting hmac key failed for %s-%zu.\n", + hash_name, sop->mackeylen*8); + ret = -EINVAL; + goto error; + } + } + + } + + /* Create a session and put it to the list. */ + ses_new = kmalloc(sizeof(*ses_new), GFP_KERNEL); + if(!ses_new) { + ret = -ENOMEM; + goto error; + } + + memset(ses_new, 0, sizeof(*ses_new)); + get_random_bytes(&ses_new->sid, sizeof(ses_new->sid)); + ses_new->tfm = blk_tfm; + ses_new->hash_tfm = hash_tfm; + init_MUTEX(&ses_new->sem); + + down(&fcr->sem); +restart: + list_for_each_entry(ses_ptr, &fcr->list, entry) { + /* Check for duplicate SID */ + if (unlikely(ses_new->sid == ses_ptr->sid)) { + get_random_bytes(&ses_new->sid, sizeof(ses_new->sid)); + /* Unless we have a broken RNG this + shouldn't loop forever... ;-) */ + goto restart; + } + } + + list_add(&ses_new->entry, &fcr->list); + up(&fcr->sem); + + /* Fill in some values for the user. */ + sop->ses = ses_new->sid; + + return 0; + +error: + if (blk_tfm) + crypto_free_blkcipher(blk_tfm); + if (hash_tfm) + crypto_free_hash(hash_tfm); + + return ret; + +} + +/* Everything that needs to be done when remowing a session. */ +static inline void +crypto_destroy_session(struct csession *ses_ptr) +{ + if(down_trylock(&ses_ptr->sem)) { + dprintk(2, KERN_DEBUG, "Waiting for semaphore of sid=0x%08X\n", + ses_ptr->sid); + down(&ses_ptr->sem); + } + dprintk(2, KERN_DEBUG, "Removed session 0x%08X\n", ses_ptr->sid); +#if defined(CRYPTODEV_STATS) + if(enable_stats) + dprintk(2, KERN_DEBUG, + "Usage in Bytes: enc=%llu, dec=%llu, max=%zu, avg=%lu, cnt=%zu\n", + ses_ptr->stat[COP_ENCRYPT], ses_ptr->stat[COP_DECRYPT], + ses_ptr->stat_max_size, ses_ptr->stat_count > 0 + ? ((unsigned long)(ses_ptr->stat[COP_ENCRYPT]+ + ses_ptr->stat[COP_DECRYPT]) / + ses_ptr->stat_count) : 0, + ses_ptr->stat_count); +#endif + if (ses_ptr->tfm) { + crypto_free_blkcipher(ses_ptr->tfm); + ses_ptr->tfm = NULL; + } + if (ses_ptr->hash_tfm) { + crypto_free_hash(ses_ptr->hash_tfm); + ses_ptr->hash_tfm = NULL; + } + up(&ses_ptr->sem); + kfree(ses_ptr); +} + +/* Look up a session by ID and remove. */ +static int +crypto_finish_session(struct fcrypt *fcr, uint32_t sid) +{ + struct csession *tmp, *ses_ptr; + struct list_head *head; + int ret = 0; + + down(&fcr->sem); + head = &fcr->list; + list_for_each_entry_safe(ses_ptr, tmp, head, entry) { + if(ses_ptr->sid == sid) { + list_del(&ses_ptr->entry); + crypto_destroy_session(ses_ptr); + break; + } + } + + if (!ses_ptr) { + dprintk(1, KERN_ERR, "Session with sid=0x%08X not found!\n", sid); + ret = -ENOENT; + } + up(&fcr->sem); + + return ret; +} + +/* Remove all sessions when closing the file */ +static int +crypto_finish_all_sessions(struct fcrypt *fcr) +{ + struct csession *tmp, *ses_ptr; + struct list_head *head; + + down(&fcr->sem); + + head = &fcr->list; + list_for_each_entry_safe(ses_ptr, tmp, head, entry) { + list_del(&ses_ptr->entry); + crypto_destroy_session(ses_ptr); + } + up(&fcr->sem); + + return 0; +} + +/* Look up session by session ID. The returned session is locked. */ +static struct csession * +crypto_get_session_by_sid(struct fcrypt *fcr, uint32_t sid) +{ + struct csession *ses_ptr; + + down(&fcr->sem); + list_for_each_entry(ses_ptr, &fcr->list, entry) { + if(ses_ptr->sid == sid) { + down(&ses_ptr->sem); + break; + } + } + up(&fcr->sem); + + return ses_ptr; +} + +/* This is the main crypto function - feed it with plaintext + and get a ciphertext (or vice versa :-) */ +static int +crypto_run(struct fcrypt *fcr, struct crypt_op *cop) +{ + char *data, ivp[EALG_MAX_BLOCK_LEN]; + char __user *src, __user *dst; + struct scatterlist sg; + struct csession *ses_ptr; + unsigned int ivsize=0; + size_t nbytes, bufsize; + int ret = 0; + uint8_t hash_output[HASH_MAX_LEN]; + struct blkcipher_desc bdesc = { + .flags = CRYPTO_TFM_REQ_MAY_SLEEP, + }; + struct hash_desc hdesc = { + .flags = CRYPTO_TFM_REQ_MAY_SLEEP, + }; + + + if (unlikely(cop->op != COP_ENCRYPT && cop->op != COP_DECRYPT)) { + dprintk(1, KERN_DEBUG, "invalid operation op=%u\n", cop->op); + return -EINVAL; + } + + ses_ptr = crypto_get_session_by_sid(fcr, cop->ses); + if (!ses_ptr) { + dprintk(1, KERN_ERR, "invalid session ID=0x%08X\n", cop->ses); + return -EINVAL; + } + + nbytes = cop->len; + data = (char*)__get_free_page(GFP_KERNEL); + + if (unlikely(!data)) { + ret = -ENOMEM; + goto out_unlock; + } + bufsize = PAGE_SIZE < nbytes ? PAGE_SIZE : nbytes; + + nbytes = cop->len; + bdesc.tfm = ses_ptr->tfm; + hdesc.tfm = ses_ptr->hash_tfm; + if (hdesc.tfm) { + ret = crypto_hash_init(&hdesc); + if (unlikely(ret)) { + dprintk(1, KERN_ERR, + "error in crypto_hash_init()\n"); + goto out_unlock; + } + } + + if (ses_ptr->tfm) { + if (nbytes % crypto_blkcipher_blocksize(ses_ptr->tfm)) { + dprintk(1, KERN_ERR, + "data size (%zu) isn't a multiple of block size (%u)\n", + nbytes, crypto_blkcipher_blocksize(ses_ptr->tfm)); + ret = -EINVAL; + goto out_unlock; + } + + ivsize = crypto_blkcipher_ivsize(ses_ptr->tfm); + + if (cop->iv) { + copy_from_user(ivp, cop->iv, ivsize); + crypto_blkcipher_set_iv(ses_ptr->tfm, ivp, ivsize); + } + } + + src = cop->src; + dst = cop->dst; + + + while(nbytes > 0) { + size_t current_len = nbytes > bufsize ? bufsize : nbytes; + + copy_from_user(data, src, current_len); + + sg_set_buf(&sg, data, current_len); + + /* Always hash before encryption and after decryption. Maybe + * we should introduce a flag to switch... TBD later on. + */ + if (cop->op == COP_ENCRYPT) { + if (hdesc.tfm) { + ret = crypto_hash_update(&hdesc, &sg, current_len); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "CryptoAPI failure: %d\n",ret); + goto out; + } + } + if (bdesc.tfm) { + ret = crypto_blkcipher_encrypt(&bdesc, &sg, &sg, current_len); + + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "CryptoAPI failure: %d\n",ret); + goto out; + } + copy_to_user(dst, data, current_len); + dst += current_len; + } + } else { + if (bdesc.tfm) { + ret = crypto_blkcipher_decrypt(&bdesc, &sg, &sg, current_len); + + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "CryptoAPI failure: %d\n",ret); + goto out; + } + copy_to_user(dst, data, current_len); + dst += current_len; + + } + + if (hdesc.tfm) { + ret = crypto_hash_update(&hdesc, &sg, current_len); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "CryptoAPI failure: %d\n",ret); + goto out; + } + } + } + + nbytes -= current_len; + src += current_len; + } + + if (hdesc.tfm) { + ret = crypto_hash_final(&hdesc, hash_output); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "CryptoAPI failure: %d\n",ret); + goto out; + } + + copy_to_user(cop->mac, hash_output, crypto_hash_digestsize(ses_ptr->hash_tfm)); + } + +#if defined(CRYPTODEV_STATS) + if (enable_stats) { + /* this is safe - we check cop->op at the function entry */ + ses_ptr->stat[cop->op] += cop->len; + if (ses_ptr->stat_max_size < cop->len) + ses_ptr->stat_max_size = cop->len; + ses_ptr->stat_count++; + } +#endif + +out: + free_page((unsigned long)data); + +out_unlock: + up(&ses_ptr->sem); + + return ret; +} + +/* ====== /dev/crypto ====== */ + +static int +cryptodev_open(struct inode *inode, struct file *filp) +{ + struct fcrypt *fcr; + + fcr = kmalloc(sizeof(*fcr), GFP_KERNEL); + if(!fcr) + return -ENOMEM; + + memset(fcr, 0, sizeof(*fcr)); + init_MUTEX(&fcr->sem); + INIT_LIST_HEAD(&fcr->list); + filp->private_data = fcr; + + return 0; +} + +static int +cryptodev_release(struct inode *inode, struct file *filp) +{ + struct fcrypt *fcr = filp->private_data; + + if(fcr) { + crypto_finish_all_sessions(fcr); + kfree(fcr); + filp->private_data = NULL; + } + + return 0; +} + +static int +clonefd(struct file *filp) +{ + struct fdtable *fdt = files_fdtable(current->files); + int ret; + ret = get_unused_fd(); + if (ret >= 0) { + get_file(filp); + FD_SET(ret, fdt->open_fds); + fd_install(ret, filp); + } + + return ret; +} + +static int +cryptodev_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + int __user *p = (void __user *)arg; + struct session_op sop; + struct crypt_op cop; + struct fcrypt *fcr = filp->private_data; + uint32_t ses; + int ret, fd; + + if (!fcr) + BUG(); + + switch (cmd) { + case CIOCASYMFEAT: + put_user(0, p); + return 0; + case CRIOGET: + fd = clonefd(filp); + put_user(fd, p); + return 0; + + case CIOCGSESSION: + copy_from_user(&sop, (void*)arg, sizeof(sop)); + ret = crypto_create_session(fcr, &sop); + if (ret) + return ret; + copy_to_user((void*)arg, &sop, sizeof(sop)); + return 0; + + case CIOCFSESSION: + get_user(ses, (uint32_t*)arg); + ret = crypto_finish_session(fcr, ses); + return ret; + + case CIOCCRYPT: + copy_from_user(&cop, (void*)arg, sizeof(cop)); + ret = crypto_run(fcr, &cop); + copy_to_user((void*)arg, &cop, sizeof(cop)); + return ret; + + default: + return -EINVAL; + } +} + +struct file_operations cryptodev_fops = { + .owner = THIS_MODULE, + .open = cryptodev_open, + .release = cryptodev_release, + .ioctl = cryptodev_ioctl, +}; + +struct miscdevice cryptodev = { + .minor = CRYPTODEV_MINOR, + .name = "crypto", + .fops = &cryptodev_fops, +}; + +static int +cryptodev_register(void) +{ + int rc; + + rc = misc_register (&cryptodev); + if (rc) { + printk(KERN_ERR PFX "registeration of /dev/crypto failed\n"); + return rc; + } + + return 0; +} + +static void +cryptodev_deregister(void) +{ + misc_deregister(&cryptodev); +} + +/* ====== Module init/exit ====== */ + +int __init init_cryptodev(void) +{ + int rc; + + rc = cryptodev_register(); + if (rc) + return rc; + + printk(KERN_INFO PFX "driver loaded.\n"); + + return 0; +} + +void __exit exit_cryptodev(void) +{ + cryptodev_deregister(); + printk(KERN_INFO PFX "driver unloaded.\n"); +} + +module_init(init_cryptodev); +module_exit(exit_cryptodev); diff --git a/cryptodev.h b/cryptodev.h new file mode 100644 index 0000000..895ad1c --- /dev/null +++ b/cryptodev.h @@ -0,0 +1,156 @@ +/* + * Driver for /dev/crypto device (aka CryptoDev) + * + * Copyright (c) 2004 Michal Ludvig <mludvig@logix.net.nz>, SuSE Labs + * + * Structures and ioctl command names were taken from + * OpenBSD to preserve compatibility with their API. + * + */ + +#ifndef _CRYPTODEV_H +#define _CRYPTODEV_H + +#ifndef __KERNEL__ +#include <inttypes.h> +#endif + +#define CRYPTODEV_MINOR MISC_DYNAMIC_MINOR + + +#define CRYPTO_FLAG_HMAC 0x0010 +#define CRYPTO_FLAG_MASK 0x00FF + +enum { + CRYPTO_DES_CBC=1, + CRYPTO_3DES_CBC, + CRYPTO_BLF_CBC, + CRYPTO_AES_CBC, + CRYPTO_RIJNDAEL128_CBC=CRYPTO_AES_CBC, + CRYPTO_CAMELLIA_CBC, + /* unsupported from here */ + CRYPTO_CAST_CBC, + CRYPTO_SKIPJACK_CBC, + + CRYPTO_MD5_KPDK=200, + CRYPTO_SHA1_KPDK, + CRYPTO_MD5, + CRYPTO_RIPEMD160, + CRYPTO_SHA1, + CRYPTO_SHA2_256, + CRYPTO_SHA2_384, + CRYPTO_SHA2_512, + CRYPTO_MD5_HMAC, + CRYPTO_RIPEMD160_HMAC, + CRYPTO_SHA1_HMAC, + CRYPTO_SHA2_256_HMAC, + CRYPTO_SHA2_384_HMAC, + CRYPTO_SHA2_512_HMAC, + CRYPTO_ALGORITHM_MAX +}; + +#define CRYPTO_CIPHER_MAX_KEY_LEN 512 +#define CRYPTO_HMAC_MAX_KEY_LEN 512 + +#define HASH_MAX_LEN 64 + +struct crparam; +struct crypt_kop; + +/* ioctl parameter to create a session */ +struct session_op { + uint16_t cipher; /* e.g. CRYPTO_DES_CBC */ + uint16_t mac; /* e.g. CRYPTO_MD5_HMAC */ + uint8_t *key; + size_t keylen; /* cipher key */ + size_t mackeylen; /* mac key */ + uint8_t *mackey; + + /* Return values */ + uint32_t ses; /* session ID */ +}; + +/* ioctl parameter to request a crypt/decrypt operation against a session */ +struct crypt_op { + uint32_t ses; /* from session_op->ses */ + #define COP_DECRYPT 0 + #define COP_ENCRYPT 1 + uint32_t op; /* ie. COP_ENCRYPT */ + uint32_t flags; /* unused */ + + size_t len; + void *src, *dst; + void *mac; + void *iv; +}; + +/* clone original filedescriptor */ +#define CRIOGET _IOWR('c', 101, uint32_t) + +/* create crypto session */ +#define CIOCGSESSION _IOWR('c', 102, struct session_op) + +/* finish crypto session */ +#define CIOCFSESSION _IOW('c', 103, uint32_t) + +/* request encryption/decryptions of a given buffer */ +#define CIOCCRYPT _IOWR('c', 104, struct crypt_op) + +/* ioctl()s for asym-crypto. Not yet supported. */ +#define CIOCKEY _IOWR('c', 105, void *) +#define CIOCASYMFEAT _IOR('c', 106, uint32_t) + +#endif /* _CRYPTODEV_H */ + +/* unused structures */ +struct crparam { + caddr_t crp_p; + uint32_t crp_nbits; +}; + +#define CRK_MAXPARAM 8 + +struct crypt_kop { + uint32_t crk_op; /* ie. CRK_MOD_EXP or other */ + uint32_t crk_status; /* return status */ + uint16_t crk_iparams; /* # of input parameters */ + uint16_t crk_oparams; /* # of output parameters */ + uint32_t crk_crid; /* NB: only used by CIOCKEY2 (rw) */ + struct crparam crk_param[CRK_MAXPARAM]; +}; + +/* Definitions from openbsd's cryptodev */ + +#define DES_BLOCK_LEN 8 +#define DES3_BLOCK_LEN 8 +#define BLOWFISH_BLOCK_LEN 8 +#define SKIPJACK_BLOCK_LEN 8 +#define CAST128_BLOCK_LEN 8 +#define RIJNDAEL128_BLOCK_LEN 16 +#define AES_BLOCK_LEN RIJNDAEL128_BLOCK_LEN +#define EALG_MAX_BLOCK_LEN AES_BLOCK_LEN /* Keep this updated */ + +#define NULL_HASH_LEN 16 +#define MD5_HASH_LEN 16 +#define SHA1_HASH_LEN 20 +#define RIPEMD160_HASH_LEN 20 +#define SHA2_256_HASH_LEN 32 +#define SHA2_384_HASH_LEN 48 +#define SHA2_512_HASH_LEN 64 +#define MD5_KPDK_HASH_LEN 16 +#define SHA1_KPDK_HASH_LEN 20 + +#define CRK_ALGORITM_MIN 0 +#define CRK_MOD_EXP 0 +#define CRK_MOD_EXP_CRT 1 +#define CRK_DSA_SIGN 2 +#define CRK_DSA_VERIFY 3 +#define CRK_DH_COMPUTE_KEY 4 +#define CRK_ALGORITHM_MAX 4 /* Keep updated - see below */ + +#define CRF_MOD_EXP (1 << CRK_MOD_EXP) +#define CRF_MOD_EXP_CRT (1 << CRK_MOD_EXP_CRT) +#define CRF_DSA_SIGN (1 << CRK_DSA_SIGN) +#define CRF_DSA_VERIFY (1 << CRK_DSA_VERIFY) +#define CRF_DH_COMPUTE_KEY (1 << CRK_DH_COMPUTE_KEY) + diff --git a/cryptodev.mod.c b/cryptodev.mod.c new file mode 100644 index 0000000..983f1f4 --- /dev/null +++ b/cryptodev.mod.c @@ -0,0 +1,57 @@ +#include <linux/module.h> +#include <linux/vermagic.h> +#include <linux/compiler.h> + +MODULE_INFO(vermagic, VERMAGIC_STRING); + +struct module __this_module +__attribute__((section(".gnu.linkonce.this_module"))) = { + .name = KBUILD_MODNAME, + .init = init_module, +#ifdef CONFIG_MODULE_UNLOAD + .exit = cleanup_module, +#endif + .arch = MODULE_ARCH_INIT, +}; + +static const struct modversion_info ____versions[] +__used +__attribute__((section("__versions"))) = { + { 0x903a5ec4, "module_layout" }, + { 0x6980fe91, "param_get_int" }, + { 0xff964b25, "param_set_int" }, + { 0x79aa04a2, "get_random_bytes" }, + { 0x4661e311, "__tracepoint_kmalloc" }, + { 0x893412fe, "kmem_cache_alloc" }, + { 0xadee93f3, "kmalloc_caches" }, + { 0x4e1e7401, "crypto_alloc_base" }, + { 0x4302d0eb, "free_pages" }, + { 0xe52947e7, "__phys_addr" }, + { 0x236c8c64, "memcpy" }, + { 0xbe499d81, "copy_to_user" }, + { 0x93fca811, "__get_free_pages" }, + { 0x37a0cba, "kfree" }, + { 0x3f1899f1, "up" }, + { 0x9bf377f5, "crypto_destroy_tfm" }, + { 0x9ced38aa, "down_trylock" }, + { 0x945bc6a7, "copy_from_user" }, + { 0x748caf40, "down" }, + { 0x6729d3df, "__get_user_4" }, + { 0xf0fdf6cb, "__stack_chk_fail" }, + { 0xb2fd5ceb, "__put_user_4" }, + { 0xa1c76e0a, "_cond_resched" }, + { 0xa62a61be, "fd_install" }, + { 0x99bfbe39, "get_unused_fd" }, + { 0xd8778690, "per_cpu__current_task" }, + { 0x54b8041c, "misc_register" }, + { 0xea147363, "printk" }, + { 0x11cef3ea, "misc_deregister" }, +}; + +static const char __module_depends[] +__used +__attribute__((section(".modinfo"))) = +"depends="; + + +MODULE_INFO(srcversion, "A0688FBD1488A8A38E2DF83"); diff --git a/example1.c b/example1.c new file mode 100644 index 0000000..3bd2610 --- /dev/null +++ b/example1.c @@ -0,0 +1,133 @@ +/* + * Demo on how to use OpenBSD /dev/crypto device. + * + * Author: Michal Ludvig <michal@logix.cz> + * http://www.logix.cz/michal + * + * Note: by default OpenBSD doesn't allow using + * /dev/crypto if there is no hardware accelerator + * for a given algorithm. To change this you'll have to + * set cryptodevallowsoft=1 in + * /usr/src/sys/crypto/cryptodev.c and rebuild your kernel. + */ +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> + +#include <sys/ioctl.h> +//#include <crypto/cryptodev.h> +#include "cryptodev.h" + +#define DATA_SIZE 4096 +#define BLOCK_SIZE 16 +#define KEY_SIZE 16 + +static int +test_crypto(int cfd) +{ + struct { + char in[DATA_SIZE], + encrypted[DATA_SIZE], + decrypted[DATA_SIZE], + iv[BLOCK_SIZE], + key[KEY_SIZE]; + } data; + struct session_op sess; + struct crypt_op cryp; + + memset(&sess, 0, sizeof(sess)); + memset(&cryp, 0, sizeof(cryp)); + + /* Use the garbage that is on the stack :-) */ + /* memset(&data, 0, sizeof(data)); */ + + /* Get crypto session for AES128 */ + sess.cipher = CRYPTO_AES_CBC; + sess.keylen = KEY_SIZE; + sess.key = data.key; + if (ioctl(cfd, CIOCGSESSION, &sess)) { + perror("ioctl(CIOCGSESSION)"); + return 1; + } + + /* Encrypt data.in to data.encrypted */ + cryp.ses = sess.ses; + cryp.len = sizeof(data.in); + cryp.src = data.in; + cryp.dst = data.encrypted; + cryp.iv = data.iv; + cryp.op = COP_ENCRYPT; + if (ioctl(cfd, CIOCCRYPT, &cryp)) { + perror("ioctl(CIOCCRYPT)"); + return 1; + } + + /* Decrypt data.encrypted to data.decrypted */ + cryp.src = data.encrypted; + cryp.dst = data.decrypted; + cryp.op = COP_DECRYPT; + if (ioctl(cfd, CIOCCRYPT, &cryp)) { + perror("ioctl(CIOCCRYPT)"); + return 1; + } + + /* Verify the result */ + if (memcmp(data.in, data.decrypted, sizeof(data.in)) != 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; +} + +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_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; +} diff --git a/example2.c b/example2.c new file mode 100644 index 0000000..d4c49f0 --- /dev/null +++ b/example2.c @@ -0,0 +1,182 @@ +/* + * Demo on how to use OpenBSD /dev/crypto device. + * + * Author: Michal Ludvig <michal@logix.cz> + * http://www.logix.cz/michal + * + * Note: by default OpenBSD doesn't allow using + * /dev/crypto if there is no hardware accelerator + * for a given algorithm. To change this you'll have to + * set cryptodevallowsoft=1 in + * /usr/src/sys/crypto/cryptodev.c and rebuild your kernel. + */ +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> + +#include <sys/ioctl.h> +//#include <crypto/cryptodev.h> +#include "cryptodev.h" + +#define DATA_SIZE 4096 +#define BLOCK_SIZE 16 +#define KEY_SIZE 16 + +static int +test_crypto(int cfd) +{ + struct { + uint8_t in[DATA_SIZE], + encrypted[DATA_SIZE], + decrypted[DATA_SIZE], + iv[BLOCK_SIZE], + key[KEY_SIZE]; + } data; + struct session_op sess; + struct crypt_op cryp; + uint8_t mac[HASH_MAX_LEN]; + uint8_t oldmac[HASH_MAX_LEN]; + uint8_t def_out[] = "\x75\x0c\x78\x3e\x6a\xb0\xb5\x03\xea\xa8\x6e\x31\x0a\x5d\xb7\x38"; + int i; + + memset(&sess, 0, sizeof(sess)); + memset(&cryp, 0, sizeof(cryp)); + + /* Use the garbage that is on the stack :-) */ + /* memset(&data, 0, sizeof(data)); */ + + /* Get crypto session for AES128 */ + sess.cipher = 0; + sess.mac = CRYPTO_SHA1_HMAC; + sess.mackeylen = 4; + sess.mackey = (uint8_t*)"Jefe"; + if (ioctl(cfd, CIOCGSESSION, &sess)) { + perror("ioctl(CIOCGSESSION)"); + return 1; + } + + memset(mac, 0, sizeof(mac)); + + cryp.ses = sess.ses; + cryp.len = sizeof("what do ya want for nothing?")-1; + cryp.src = "what do ya want for nothing?"; + cryp.mac = mac; + cryp.op = COP_ENCRYPT; + if (ioctl(cfd, CIOCCRYPT, &cryp)) { + perror("ioctl(CIOCCRYPT)"); + return 1; + } + + if (memcmp(mac, def_out, sizeof(mac))!=0) { + printf("mac: "); + for (i=0;i<SHA1_HASH_LEN;i++) { + printf("%.2x", (uint8_t)mac[i]); + } + puts("\n"); + fprintf(stderr, "HMAC test 1: failed\n"); + } else { + fprintf(stderr, "HMAC test 1: passed\n"); + } +return 0; + sess.cipher = CRYPTO_AES_CBC; + sess.mac = CRYPTO_SHA1_HMAC; + sess.keylen = KEY_SIZE; + sess.key = data.key; + sess.mackeylen = 16; + sess.mackey = (uint8_t*)"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"; + if (ioctl(cfd, CIOCGSESSION, &sess)) { + perror("ioctl(CIOCGSESSION)"); + return 1; + } + + /* Encrypt data.in to data.encrypted */ + cryp.ses = sess.ses; + cryp.len = sizeof(data.in); + cryp.src = data.in; + cryp.dst = data.encrypted; + cryp.iv = data.iv; + cryp.mac = mac; + cryp.op = COP_ENCRYPT; + if (ioctl(cfd, CIOCCRYPT, &cryp)) { + perror("ioctl(CIOCCRYPT)"); + return 1; + } + + memcpy(oldmac, mac, sizeof(mac)); + + /* Decrypt data.encrypted to data.decrypted */ + cryp.src = data.encrypted; + cryp.dst = data.decrypted; + cryp.op = COP_DECRYPT; + if (ioctl(cfd, CIOCCRYPT, &cryp)) { + perror("ioctl(CIOCCRYPT)"); + return 1; + } + + /* Verify the result */ + if (memcmp(data.in, data.decrypted, sizeof(data.in)) != 0) { + fprintf(stderr, + "FAIL: Decrypted data are different from the input data.\n"); + return 1; + } else + printf("Crypt Test: passed\n"); + + if (memcmp(mac, oldmac, sizeof(mac)) != 0) { + fprintf(stderr, + "FAIL: Hash in decrypted data different than in encrypted.\n"); + return 1; + } else + printf("HMAC Test 2: passed\n"); + + /* Finish crypto session */ + if (ioctl(cfd, CIOCFSESSION, &sess.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_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; +} |