diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2010-07-25 22:17:22 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2010-07-25 22:17:22 +0200 |
commit | 6a2560330da7bc05ccb9bc75e70ce745acba7d6c (patch) | |
tree | 2973ea99e08e08ff9119768ca40be21cfbbcd937 /ncr-sessions.c | |
parent | c2dda95767e03af277f4d7dab1334aa10e3ee10f (diff) | |
download | cryptodev-linux-6a2560330da7bc05ccb9bc75e70ce745acba7d6c.tar.gz cryptodev-linux-6a2560330da7bc05ccb9bc75e70ce745acba7d6c.tar.xz cryptodev-linux-6a2560330da7bc05ccb9bc75e70ce745acba7d6c.zip |
No need for ncr-direct. All session operations are being done on keys or on userspace data.
Diffstat (limited to 'ncr-sessions.c')
-rw-r--r-- | ncr-sessions.c | 412 |
1 files changed, 318 insertions, 94 deletions
diff --git a/ncr-sessions.c b/ncr-sessions.c index 3202e69..f0aebc5 100644 --- a/ncr-sessions.c +++ b/ncr-sessions.c @@ -27,6 +27,8 @@ #include <linux/scatterlist.h> #include <ncr-sessions.h> +static int _ncr_session_direct_update_key(struct ncr_lists* lists, struct ncr_session_op_st* op); + void ncr_sessions_list_deinit(struct list_sem_st* lst) { if(lst) { @@ -468,15 +470,147 @@ int ret; return 0; } -/* Main update function - */ -static int _ncr_session_update(struct ncr_lists* lists, struct ncr_session_op_st* op) +void _ncr_session_remove(struct list_sem_st* lst, ncr_session_t desc) +{ + struct session_item_st * item, *tmp; + + down(&lst->sem); + + list_for_each_entry_safe(item, tmp, &lst->list, list) { + if(item->desc == desc) { + list_del(&item->list); + _ncr_sessions_item_put( item); /* decrement ref count */ + break; + } + } + + up(&lst->sem); + + return; +} + +/* Only the output buffer is given as scatterlist */ +static int get_userbuf1(struct session_item_st* ses, + struct ncr_session_op_st* op, struct scatterlist **dst_sg, unsigned *dst_cnt) +{ + int pagecount = 0; + + if (op->data.udata.output == NULL) { + return -EINVAL; + } + + pagecount = PAGECOUNT(op->data.udata.output, op->data.udata.output_size); + + + ses->available_pages = pagecount; + + if (pagecount > ses->array_size) { + while (ses->array_size < pagecount) + ses->array_size *= 2; + + dprintk(2, KERN_DEBUG, "%s: reallocating to %d elements\n", + __func__, ses->array_size); + ses->pages = krealloc(ses->pages, ses->array_size * + sizeof(struct page *), GFP_KERNEL); + ses->sg = krealloc(ses->sg, ses->array_size * + sizeof(struct scatterlist), GFP_KERNEL); + + if (ses->sg == NULL || ses->pages == NULL) { + return -ENOMEM; + } + } + + if (__get_userbuf(op->data.udata.output, op->data.udata.output_size, 1, + pagecount, ses->pages, ses->sg)) { + dprintk(1, KERN_ERR, "failed to get user pages for data input\n"); + return -EINVAL; + } + (*dst_sg) = ses->sg; + *dst_cnt = pagecount; + + return 0; +} + +/* make op->data.udata.input and op->data.udata.output available in scatterlists */ +static int get_userbuf2(struct session_item_st* ses, + struct ncr_session_op_st* op, struct scatterlist **src_sg, + unsigned *src_cnt, struct scatterlist **dst_sg, unsigned *dst_cnt) +{ + int src_pagecount, dst_pagecount = 0, pagecount, write_src = 1; + + if (op->data.udata.input == NULL) { + return -EINVAL; + } + + src_pagecount = PAGECOUNT(op->data.udata.input, op->data.udata.input_size); + + if (op->data.udata.input != op->data.udata.output) { /* non-in-situ transformation */ + if (op->data.udata.output != NULL) { + dst_pagecount = PAGECOUNT(op->data.udata.output, op->data.udata.output_size); + write_src = 0; + } else { + dst_pagecount = 0; + } + } + + ses->available_pages = pagecount = src_pagecount + dst_pagecount; + + if (pagecount > ses->array_size) { + while (ses->array_size < pagecount) + ses->array_size *= 2; + + dprintk(2, KERN_DEBUG, "%s: reallocating to %d elements\n", + __func__, ses->array_size); + ses->pages = krealloc(ses->pages, ses->array_size * + sizeof(struct page *), GFP_KERNEL); + ses->sg = krealloc(ses->sg, ses->array_size * + sizeof(struct scatterlist), GFP_KERNEL); + + if (ses->sg == NULL || ses->pages == NULL) { + return -ENOMEM; + } + } + + if (__get_userbuf(op->data.udata.input, op->data.udata.input_size, write_src, + src_pagecount, ses->pages, ses->sg)) { + dprintk(1, KERN_ERR, "failed to get user pages for data input\n"); + return -EINVAL; + } + (*src_sg) = ses->sg; + *src_cnt = src_pagecount; + + if (dst_pagecount) { + *dst_cnt = dst_pagecount; + (*dst_sg) = ses->sg + src_pagecount; + + if (__get_userbuf(op->data.udata.output, op->data.udata.output_size, 1, dst_pagecount, + ses->pages + src_pagecount, *dst_sg)) { + dprintk(1, KERN_ERR, "failed to get user pages for data output\n"); + release_user_pages(ses->pages, src_pagecount); + return -EINVAL; + } + } else { + if (op->data.udata.output != NULL) { + *dst_cnt = src_pagecount; + (*dst_sg) = (*src_sg); + } else { + *dst_cnt = 0; + *dst_sg = NULL; + } + } + + return 0; +} + +/* Called when userspace buffers are used */ +int _ncr_session_direct_update(struct ncr_lists* lists, struct ncr_session_op_st* op) { int ret; struct session_item_st* sess; - struct data_item_st* data = NULL; - struct data_item_st* odata = NULL; - size_t new_size; + struct scatterlist *isg; + struct scatterlist *osg; + unsigned osg_cnt=0, isg_cnt=0; + size_t isg_size, osg_size; sess = ncr_sessions_item_get( &lists->sessions, op->ses); if (sess == NULL) { @@ -484,65 +618,63 @@ static int _ncr_session_update(struct ncr_lists* lists, struct ncr_session_op_st return -EINVAL; } - /* obtain data item */ - data = ncr_data_item_get( &lists->data, op->data.ndata.input); - if (data == NULL) { + if (down_interruptible(&sess->mem_mutex)) { + err(); + _ncr_sessions_item_put(sess); + return -ERESTARTSYS; + } + + ret = get_userbuf2(sess, op, &isg, &isg_cnt, &osg, &osg_cnt); + if (ret < 0) { err(); - ret = -EINVAL; goto fail; } + isg_size = op->data.udata.input_size; + osg_size = op->data.udata.output_size; switch(sess->op) { case NCR_OP_ENCRYPT: - odata = ncr_data_item_get( &lists->data, op->data.ndata.output); - if (odata == NULL) { - err(); - ret = -EINVAL; - goto fail; - } - - if (odata->max_data_size < data->data_size) { + if (osg == NULL) { err(); ret = -EINVAL; goto fail; } - odata->data_size = odata->max_data_size; - ret = _ncr_session_encrypt(sess, &data->sg, 1, data->data_size, - &odata->sg, 1, &odata->data_size); + ret = _ncr_session_encrypt(sess, isg, isg_cnt, isg_size, + osg, osg_cnt, &osg_size); if (ret < 0) { err(); goto fail; } + op->data.udata.output_size = osg_size; + break; case NCR_OP_DECRYPT: - odata = ncr_data_item_get( &lists->data, op->data.ndata.output); - if (odata == NULL) { + if (osg == NULL) { err(); ret = -EINVAL; goto fail; } - if (odata->max_data_size < data->data_size) { + if (osg_size < isg_size) { err(); ret = -EINVAL; goto fail; } - new_size = odata->max_data_size; - ret = _ncr_session_decrypt(sess, &data->sg, 1, data->data_size, - &odata->sg, 1, &new_size); + ret = _ncr_session_decrypt(sess, isg, isg_cnt, isg_size, + osg, osg_cnt, &osg_size); if (ret < 0) { err(); goto fail; } - odata->data_size = new_size; + op->data.udata.output_size = osg_size; break; case NCR_OP_SIGN: case NCR_OP_VERIFY: - ret = cryptodev_hash_update(&sess->hash, &data->sg, data->data_size); + ret = cryptodev_hash_update(&sess->hash, isg, isg_size); if (ret < 0) { err(); goto fail; @@ -557,48 +689,41 @@ static int _ncr_session_update(struct ncr_lists* lists, struct ncr_session_op_st ret = 0; fail: - if (odata) _ncr_data_item_put(odata); - if (data) _ncr_data_item_put(data); + if (sess->available_pages) { + release_user_pages(sess->pages, sess->available_pages); + sess->available_pages = 0; + } + up(&sess->mem_mutex); _ncr_sessions_item_put(sess); return ret; } -void _ncr_session_remove(struct list_sem_st* lst, ncr_session_t desc) -{ - struct session_item_st * item, *tmp; - - down(&lst->sem); - - list_for_each_entry_safe(item, tmp, &lst->list, list) { - if(item->desc == desc) { - list_del(&item->list); - _ncr_sessions_item_put( item); /* decrement ref count */ - break; - } - } - - up(&lst->sem); - - return; -} - -static int try_session_update(struct ncr_lists* lists, struct ncr_session_op_st* op) +static int try_session_direct_update(struct ncr_lists* lists, struct ncr_session_op_st* op) { - if (op->data.ndata.input != NCR_DATA_INVALID) { - return _ncr_session_update(lists, op); + if (op->type == NCR_KEY_DATA) { + if (op->data.kdata.input != NCR_KEY_INVALID) + return _ncr_session_direct_update_key(lists, op); + } else if (op->type == NCR_DIRECT_DATA) { + if (op->data.udata.input != NULL) + return _ncr_session_direct_update(lists, op); } return 0; } -static int _ncr_session_final(struct ncr_lists* lists, struct ncr_session_op_st* op) +int _ncr_session_direct_final(struct ncr_lists* lists, struct ncr_session_op_st* op) { int ret; struct session_item_st* sess; struct data_item_st* odata = NULL; int digest_size; uint8_t digest[NCR_HASH_MAX_OUTPUT_SIZE]; + uint8_t vdigest[NCR_HASH_MAX_OUTPUT_SIZE]; + struct scatterlist *osg; + unsigned osg_cnt=0; + size_t osg_size = 0; + size_t orig_osg_size; sess = ncr_sessions_item_get( &lists->sessions, op->ses); if (sess == NULL) { @@ -606,18 +731,31 @@ static int _ncr_session_final(struct ncr_lists* lists, struct ncr_session_op_st* return -EINVAL; } - ret = try_session_update(lists, op); + ret = try_session_direct_update(lists, op); if (ret < 0) { err(); - goto fail; + _ncr_sessions_item_put(sess); + return ret; + } + + if (down_interruptible(&sess->mem_mutex)) { + err(); + _ncr_sessions_item_put(sess); + return -ERESTARTSYS; } switch(sess->op) { case NCR_OP_ENCRYPT: case NCR_OP_DECRYPT: break; - case NCR_OP_VERIFY: + ret = get_userbuf1(sess, op, &osg, &osg_cnt); + if (ret < 0) { + err(); + goto fail; + } + orig_osg_size = osg_size = op->data.udata.output_size; + digest_size = sess->hash.digestsize; if (digest_size == 0 || sizeof(digest) < digest_size) { err(); @@ -630,16 +768,16 @@ static int _ncr_session_final(struct ncr_lists* lists, struct ncr_session_op_st* goto fail; } - odata = ncr_data_item_get( &lists->data, op->data.ndata.output); - if (odata == NULL) { - err(); - ret = -EINVAL; - goto fail; - } - if (sess->algorithm->is_hmac) { + ret = sg_copy_to_buffer(osg, osg_cnt, vdigest, digest_size); + if (ret != digest_size) { + err(); + ret = -EINVAL; + goto fail; + } + if (digest_size != odata->data_size || - memcmp(odata->data, digest, digest_size) != 0) { + memcmp(vdigest, digest, digest_size) != 0) { op->err = NCR_VERIFICATION_FAILED; } else { @@ -647,7 +785,7 @@ static int _ncr_session_final(struct ncr_lists* lists, struct ncr_session_op_st* } } else { /* PK signature */ - ret = ncr_pk_cipher_verify(&sess->pk, &odata->sg, 1, odata->data_size, + ret = ncr_pk_cipher_verify(&sess->pk, osg, osg_cnt, osg_size, digest, digest_size, &op->err); if (ret < 0) { err(); @@ -657,34 +795,46 @@ static int _ncr_session_final(struct ncr_lists* lists, struct ncr_session_op_st* break; case NCR_OP_SIGN: - odata = ncr_data_item_get( &lists->data, op->data.ndata.output); - if (odata == NULL) { + ret = get_userbuf1(sess, op, &osg, &osg_cnt); + if (ret < 0) { err(); - ret = -EINVAL; goto fail; } + orig_osg_size = osg_size = op->data.udata.output_size; digest_size = sess->hash.digestsize; - if (digest_size == 0 || odata->max_data_size < digest_size) { + if (digest_size == 0 || osg_size < digest_size) { + err(); + ret = -EINVAL; + goto fail; + } + + ret = cryptodev_hash_final(&sess->hash, digest); + if (ret < 0) { + err(); + goto fail; + } + + ret = sg_copy_from_buffer(osg, osg_cnt, digest, digest_size); + if (ret != digest_size) { err(); ret = -EINVAL; goto fail; } - ret = cryptodev_hash_final(&sess->hash, odata->data); - odata->data_size = digest_size; + osg_size = digest_size; cryptodev_hash_deinit(&sess->hash); if (sess->algorithm->is_pk) { /* PK signature */ - size_t new_size = odata->max_data_size; - ret = ncr_pk_cipher_sign(&sess->pk, &odata->sg, 1, odata->data_size, - &odata->sg, 1, &new_size); + + ret = ncr_pk_cipher_sign(&sess->pk, osg, osg_cnt, osg_size, + osg, osg_cnt, &orig_osg_size); if (ret < 0) { err(); goto fail; } - odata->data_size = new_size; + osg_size = orig_osg_size; } break; default: @@ -693,10 +843,18 @@ static int _ncr_session_final(struct ncr_lists* lists, struct ncr_session_op_st* goto fail; } + if (osg_size > 0) + op->data.udata.output_size = osg_size; + ret = 0; fail: - if (odata) _ncr_data_item_put(odata); + if (sess->available_pages) { + release_user_pages(sess->pages, sess->available_pages); + sess->available_pages = 0; + } + up(&sess->mem_mutex); + cryptodev_hash_deinit(&sess->hash); if (sess->algorithm->is_symmetric) { cryptodev_cipher_deinit(&sess->cipher); @@ -710,6 +868,85 @@ fail: return ret; } +/* Direct with key: Allows to hash a key */ +/* Called when userspace buffers are used */ +static int _ncr_session_direct_update_key(struct ncr_lists* lists, struct ncr_session_op_st* op) +{ + int ret; + struct session_item_st* sess; + struct key_item_st* key = NULL; + struct scatterlist *osg; + unsigned osg_cnt=0; + size_t osg_size; + + sess = ncr_sessions_item_get( &lists->sessions, op->ses); + if (sess == NULL) { + err(); + return -EINVAL; + } + + /* read key */ + ret = ncr_key_item_get_read( &key, &lists->key, op->data.kdata.input); + if (ret < 0) { + err(); + goto fail; + } + + if (key->type != NCR_KEY_TYPE_SECRET) { + err(); + ret = -EINVAL; + goto fail; + } + + if (down_interruptible(&sess->mem_mutex)) { + err(); + _ncr_sessions_item_put(sess); + return -ERESTARTSYS; + } + + ret = get_userbuf1(sess, op, &osg, &osg_cnt); + if (ret < 0) { + err(); + goto fail; + } + + osg_size = op->data.kdata.output_size; + + switch(sess->op) { + case NCR_OP_ENCRYPT: + case NCR_OP_DECRYPT: + err(); + ret = -EINVAL; + goto fail; + case NCR_OP_SIGN: + case NCR_OP_VERIFY: + ret = _cryptodev_hash_update(&sess->hash, + key->key.secret.data, key->key.secret.size); + if (ret < 0) { + err(); + goto fail; + } + break; + default: + err(); + ret = -EINVAL; + goto fail; + } + + ret = 0; + +fail: + if (sess->available_pages) { + release_user_pages(sess->pages, sess->available_pages); + sess->available_pages = 0; + } + up(&sess->mem_mutex); + if (key) _ncr_key_item_put(key); + _ncr_sessions_item_put(sess); + + return ret; +} + int ncr_session_update(struct ncr_lists* lists, void __user* arg) { struct ncr_session_op_st op; @@ -722,8 +959,8 @@ int ncr_session_update(struct ncr_lists* lists, void __user* arg) if (op.type == NCR_DIRECT_DATA) ret = _ncr_session_direct_update(lists, &op); - else if (op.type == NCR_DATA) - ret = _ncr_session_update(lists, &op); + else if (op.type == NCR_KEY_DATA) + ret = _ncr_session_direct_update_key(lists, &op); else ret = -EINVAL; @@ -750,14 +987,7 @@ int ncr_session_final(struct ncr_lists* lists, void __user* arg) return -EFAULT; } - if (op.type == NCR_DATA) { - ret = _ncr_session_final(lists, &op); - } else if (op.type == NCR_DIRECT_DATA) { - ret = _ncr_session_direct_final(lists, &op); - } else { - ret = -EINVAL; - } - + ret = _ncr_session_direct_final(lists, &op); if (unlikely(ret)) { err(); return ret; @@ -787,13 +1017,7 @@ int ncr_session_once(struct ncr_lists* lists, void __user* arg) } kop.op.ses = kop.init.ses; - if (kop.op.type == NCR_DIRECT_DATA) - ret = _ncr_session_direct_final(lists, &kop.op); - else if (kop.op.type == NCR_DATA) - ret = _ncr_session_final(lists, &kop.op); - else - ret = -EINVAL; - + ret = _ncr_session_direct_final(lists, &kop.op); if (ret < 0) { err(); return ret; |