diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2009-11-28 19:35:35 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2009-11-28 19:35:35 +0200 |
commit | 30181c1a49a63fddc97dd10dbac1b49568aede1f (patch) | |
tree | eaa074d13276370bce20198ccad68c0df25d18b9 /cryptodev.c | |
parent | 3c0c804b7ffd37e03e83d486080f65ce2952a2fc (diff) | |
download | cryptodev-linux-30181c1a49a63fddc97dd10dbac1b49568aede1f.tar.gz cryptodev-linux-30181c1a49a63fddc97dd10dbac1b49568aede1f.tar.xz cryptodev-linux-30181c1a49a63fddc97dd10dbac1b49568aede1f.zip |
Added CIOCCRYPTV that will encrypt/hash iovectors.
Diffstat (limited to 'cryptodev.c')
-rw-r--r-- | cryptodev.c | 184 |
1 files changed, 111 insertions, 73 deletions
diff --git a/cryptodev.c b/cryptodev.c index 6007648..76581c4 100644 --- a/cryptodev.c +++ b/cryptodev.c @@ -401,10 +401,7 @@ crypto_get_session_by_sid(struct fcrypt *fcr, uint32_t sid) 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) +static int crypto_runv(struct fcrypt *fcr, struct crypt_opv *copv) { char *data, ivp[EALG_MAX_BLOCK_LEN]; char __user *src, __user *dst; @@ -414,6 +411,7 @@ crypto_run(struct fcrypt *fcr, struct crypt_op *cop) size_t nbytes, bufsize; int ret = 0; uint8_t hash_output[HASH_MAX_LEN]; + int blocksize=1, i; struct blkcipher_desc bdesc = { .flags = CRYPTO_TFM_REQ_MAY_SLEEP, }; @@ -422,29 +420,27 @@ crypto_run(struct fcrypt *fcr, struct crypt_op *cop) }; - if (unlikely(cop->op != COP_ENCRYPT && cop->op != COP_DECRYPT)) { - dprintk(1, KERN_DEBUG, "invalid operation op=%u\n", cop->op); + if (unlikely(copv->op != COP_ENCRYPT && copv->op != COP_DECRYPT)) { + dprintk(1, KERN_DEBUG, "invalid operation op=%u\n", copv->op); return -EINVAL; } - ses_ptr = crypto_get_session_by_sid(fcr, cop->ses); + ses_ptr = crypto_get_session_by_sid(fcr, copv->ses); if (!ses_ptr) { - dprintk(1, KERN_ERR, "invalid session ID=0x%08X\n", cop->ses); + dprintk(1, KERN_ERR, "invalid session ID=0x%08X\n", copv->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)) { @@ -455,7 +451,21 @@ crypto_run(struct fcrypt *fcr, struct crypt_op *cop) } if (ses_ptr->tfm) { - if (nbytes % crypto_blkcipher_blocksize(ses_ptr->tfm)) { + blocksize = crypto_blkcipher_blocksize(ses_ptr->tfm); + ivsize = crypto_blkcipher_ivsize(ses_ptr->tfm); + + if (copv->iv) { + copy_from_user(ivp, copv->iv, ivsize); + crypto_blkcipher_set_iv(ses_ptr->tfm, ivp, ivsize); + } + } + + dst = copv->dst; + + for (i=0;i<copv->iovec_cnt;i++) { + nbytes = copv->iovec[i].len; + + if (unlikely(bdesc.tfm && (nbytes % blocksize))) { dprintk(1, KERN_ERR, "data size (%zu) isn't a multiple of block size (%u)\n", nbytes, crypto_blkcipher_blocksize(ses_ptr->tfm)); @@ -463,70 +473,74 @@ crypto_run(struct fcrypt *fcr, struct crypt_op *cop) 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; + bufsize = PAGE_SIZE < nbytes ? PAGE_SIZE : nbytes; + src = copv->iovec[i].src; - while(nbytes > 0) { - size_t current_len = nbytes > bufsize ? bufsize : nbytes; + while(nbytes > 0) { + size_t current_len = nbytes > bufsize ? bufsize : nbytes; - copy_from_user(data, src, current_len); + copy_from_user(data, src, current_len); - sg_set_buf(&sg, data, 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; + /* Always hash before encryption and after decryption. Maybe + * we should introduce a flag to switch... TBD later on. + */ + if (copv->op == COP_ENCRYPT) { + if (hdesc.tfm && (copv->iovec[i].op_flags & IOP_HASH)) { + 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; + if (bdesc.tfm && (copv->iovec[i].op_flags & IOP_CIPHER)) { + 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; } - copy_to_user(dst, data, current_len); - dst += current_len; - } - } else { - if (bdesc.tfm) { - ret = crypto_blkcipher_decrypt(&bdesc, &sg, &sg, current_len); + } else { + if (bdesc.tfm && (copv->iovec[i].op_flags & IOP_CIPHER)) { + 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 (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; + if (hdesc.tfm && (copv->iovec[i].op_flags & IOP_HASH)) { + 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; } - nbytes -= current_len; - src += current_len; +#if defined(CRYPTODEV_STATS) + if (enable_stats) { + /* this is safe - we check cop->op at the function entry */ + ses_ptr->stat[copv->op] += copv->iovec[i].len; + if (ses_ptr->stat_max_size < copv->iovec[i].len) + ses_ptr->stat_max_size = copv->iovec[i].len; + ses_ptr->stat_count++; + } +#endif + } if (hdesc.tfm) { @@ -536,19 +550,9 @@ crypto_run(struct fcrypt *fcr, struct crypt_op *cop) goto out; } - copy_to_user(cop->mac, hash_output, crypto_hash_digestsize(ses_ptr->hash_tfm)); + copy_to_user(copv->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); @@ -558,6 +562,33 @@ out_unlock: return ret; } +/* 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) +{ + struct crypt_opv copv; + struct crypt_iovec iovec; + + iovec.src = cop->src; + iovec.len = cop->len; + iovec.op_flags = IOP_CIPHER|IOP_HASH; + + copv.op = cop->op; + copv.ses = cop->ses; + copv.flags = cop->flags; + copv.iovec = &iovec; + copv.iovec_cnt = 1; + + copv.dst = cop->dst; + copv.mac = cop->mac; + copv.iv = cop->iv; + + return crypto_runv(fcr, &copv); + +} + + + /* ====== /dev/crypto ====== */ static int @@ -613,6 +644,7 @@ cryptodev_ioctl(struct inode *inode, struct file *filp, int __user *p = (void __user *)arg; struct session_op sop; struct crypt_op cop; + struct crypt_opv copv; struct fcrypt *fcr = filp->private_data; uint32_t ses; int ret, fd; @@ -648,6 +680,12 @@ cryptodev_ioctl(struct inode *inode, struct file *filp, copy_to_user((void*)arg, &cop, sizeof(cop)); return ret; + case CIOCCRYPTV: + copy_from_user(&cop, (void*)arg, sizeof(copv)); + ret = crypto_runv(fcr, &copv); + copy_to_user((void*)arg, &copv, sizeof(copv)); + return ret; + default: return -EINVAL; } |