diff options
Diffstat (limited to 'crypto/userspace/ncr-sessions.c')
-rw-r--r-- | crypto/userspace/ncr-sessions.c | 1005 |
1 files changed, 1005 insertions, 0 deletions
diff --git a/crypto/userspace/ncr-sessions.c b/crypto/userspace/ncr-sessions.c new file mode 100644 index 00000000000..45cd337a820 --- /dev/null +++ b/crypto/userspace/ncr-sessions.c @@ -0,0 +1,1005 @@ +/* + * New driver for /dev/crypto device (aka CryptoDev) + + * Copyright (c) 2010 Nikos Mavrogiannopoulos <nmav@gnutls.org> + * + * This file is part of linux cryptodev. + * + * cryptodev is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * cryptodev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/crypto.h> +#include "cryptodev.h" +#include "ncr.h" +#include "ncr_int.h" + +static void _ncr_session_remove(struct list_sem_st* lst, ncr_session_t desc); + +void ncr_sessions_list_deinit(struct list_sem_st* lst) +{ + if(lst) { + struct session_item_st * item, *tmp; + + down(&lst->sem); + + list_for_each_entry_safe(item, tmp, &lst->list, list) { + list_del(&item->list); + _ncr_sessions_item_put( item); /* decrement ref count */ + } + up(&lst->sem); + + } +} + +/* must be called with data semaphore down + */ +static ncr_session_t _ncr_sessions_get_new_desc( struct list_sem_st* lst) +{ +struct session_item_st* item; +int mx = 1; + + list_for_each_entry(item, &lst->list, list) { + mx = max(mx, item->desc); + } + mx++; + + return mx; +} + +/* returns the data item corresponding to desc */ +struct session_item_st* ncr_sessions_item_get( struct list_sem_st* lst, ncr_session_t desc) +{ +struct session_item_st* item; + + down(&lst->sem); + list_for_each_entry(item, &lst->list, list) { + if (item->desc == desc) { + atomic_inc(&item->refcnt); + up(&lst->sem); + return item; + } + } + up(&lst->sem); + + err(); + return NULL; +} + +void _ncr_sessions_item_put( struct session_item_st* item) +{ + if (atomic_dec_and_test(&item->refcnt)) { + cryptodev_cipher_deinit(&item->cipher); + ncr_pk_cipher_deinit(&item->pk); + cryptodev_hash_deinit(&item->hash); + if (item->key) + _ncr_key_item_put(item->key); + kfree(item); + } +} + +struct session_item_st* ncr_session_new(struct list_sem_st* lst) +{ + struct session_item_st* sess; + + sess = kmalloc(sizeof(*sess), GFP_KERNEL); + if (sess == NULL) { + err(); + return NULL; + } + + memset(sess, 0, sizeof(*sess)); + + atomic_set(&sess->refcnt, 1); + + down(&lst->sem); + + sess->desc = _ncr_sessions_get_new_desc(lst); + list_add(&sess->list, &lst->list); + + up(&lst->sem); + + return sess; +} + +static const struct algo_properties_st { + ncr_algorithm_t algo; + const char* kstr; + unsigned needs_iv:1; + unsigned hmac:1; + unsigned can_sign:1; + unsigned can_digest:1; + unsigned can_encrypt:1; + unsigned symmetric:1; + int digest_size; +} algo_properties[] = { + { .algo = NCR_ALG_NULL, .kstr = "ecb(cipher_null)", + .needs_iv = 0, .symmetric=1, .can_encrypt=1 }, + { .algo = NCR_ALG_3DES_CBC, .kstr = "cbc(des3_ede)", + .needs_iv = 1, .symmetric=1, .can_encrypt=1 }, + { .algo = NCR_ALG_AES_CBC, .kstr = "cbc(aes)", + .needs_iv = 1, .symmetric=1, .can_encrypt=1 }, + { .algo = NCR_ALG_CAMELLIA_CBC, .kstr = "cbc(camelia)", + .needs_iv = 1, .symmetric=1, .can_encrypt=1 }, + { .algo = NCR_ALG_AES_CTR, .kstr = "ctr(aes)", + .needs_iv = 1, .symmetric=1, .can_encrypt=1 }, + { .algo = NCR_ALG_CAMELLIA_CTR, .kstr = "ctr(camelia)", + .needs_iv = 1, .symmetric=1, .can_encrypt=1 }, + { .algo = NCR_ALG_ARCFOUR, .kstr = NULL, + .needs_iv = 0, .symmetric=1, .can_encrypt=1 }, + { .algo = NCR_ALG_AES_ECB, .kstr = "ecb(aes)", + .needs_iv = 0, .symmetric=1, .can_encrypt=1 }, + { .algo = NCR_ALG_CAMELLIA_ECB, .kstr = "ecb(camelia)", + .needs_iv = 0, .symmetric=1, .can_encrypt=1 }, + { .algo = NCR_ALG_SHA1, .kstr = "sha1", + .digest_size = 20, .can_digest=1 }, + { .algo = NCR_ALG_MD5, .kstr = "md5", + .digest_size = 16, .can_digest=1 }, + { .algo = NCR_ALG_SHA2_224, .kstr = "sha224", + .digest_size = 28, .can_digest=1 }, + { .algo = NCR_ALG_SHA2_256, .kstr = "sha256", + .digest_size = 32, .can_digest=1 }, + { .algo = NCR_ALG_SHA2_384, .kstr = "sha384", + .digest_size = 48, .can_digest=1 }, + { .algo = NCR_ALG_SHA2_512, .kstr = "sha512", + .digest_size = 64, .can_digest=1 }, + { .algo = NCR_ALG_HMAC_SHA1, .hmac = 1, .kstr = "hmac(sha1)", + .digest_size = 20, .can_sign=1 }, + { .algo = NCR_ALG_HMAC_MD5, .hmac = 1, .kstr = "hmac(md5)", + .digest_size = 16, .can_sign=1 }, + { .algo = NCR_ALG_HMAC_SHA2_224, .hmac = 1, .kstr = "hmac(sha224)", + .digest_size = 28, .can_sign=1 }, + { .algo = NCR_ALG_HMAC_SHA2_256, .hmac = 1, .kstr = "hmac(sha256)", + .digest_size = 32, .can_sign=1 }, + { .algo = NCR_ALG_HMAC_SHA2_384, .hmac = 1, .kstr = "hmac(sha384)", + .digest_size = 48, .can_sign=1 }, + { .algo = NCR_ALG_HMAC_SHA2_512, .hmac = 1, .kstr = "hmac(sha512)", + .digest_size = 64, .can_sign=1 }, + { .algo = NCR_ALG_RSA, .kstr = NULL, + .can_encrypt=1, .can_sign=1}, + { .algo = NCR_ALG_DSA, .kstr = NULL, + .can_sign=1 }, + { .algo = NCR_ALG_NONE } + +}; + +const char* _ncr_algo_to_str(ncr_algorithm_t algo) +{ +ncr_algorithm_t a; +int i = 0; + + while((a=algo_properties[i].algo)!=NCR_ALG_NONE) { + if (a == algo) + return algo_properties[i].kstr; + i++; + } + + return NULL; +} + +static int algo_needs_iv(ncr_algorithm_t algo) +{ +ncr_algorithm_t a; +int i = 0; + + while((a=algo_properties[i].algo)!=NCR_ALG_NONE) { + if (a == algo) + return algo_properties[i].needs_iv; + i++; + } + + return 0; +} + +static int algo_can_sign(ncr_algorithm_t algo) +{ +ncr_algorithm_t a; +int i = 0; + + while((a=algo_properties[i].algo)!=NCR_ALG_NONE) { + if (a == algo) + return algo_properties[i].can_sign; + i++; + } + + return 0; +} + +static int algo_can_encrypt(ncr_algorithm_t algo) +{ +ncr_algorithm_t a; +int i = 0; + + while((a=algo_properties[i].algo)!=NCR_ALG_NONE) { + if (a == algo) + return algo_properties[i].can_encrypt; + i++; + } + + return 0; +} + +static int algo_can_digest(ncr_algorithm_t algo) +{ +ncr_algorithm_t a; +int i = 0; + + while((a=algo_properties[i].algo)!=NCR_ALG_NONE) { + if (a == algo) + return algo_properties[i].can_digest; + i++; + } + + return 0; +} + + +static int algo_is_hmac(ncr_algorithm_t algo) +{ +ncr_algorithm_t a; +int i = 0; + + while((a=algo_properties[i].algo)!=NCR_ALG_NONE) { + if (a == algo) + return algo_properties[i].hmac; + i++; + } + + return 0; +} + +static int algo_is_symmetric(ncr_algorithm_t algo) +{ +ncr_algorithm_t a; +int i = 0; + + while((a=algo_properties[i].algo)!=NCR_ALG_NONE) { + if (a == algo) + return algo_properties[i].symmetric; + i++; + } + + return 0; +} + +int _ncr_algo_digest_size(ncr_algorithm_t algo) +{ +ncr_algorithm_t a; +int i = 0; + + while((a=algo_properties[i].algo)!=NCR_ALG_NONE) { + if (a == algo) + return algo_properties[i].digest_size; + i++; + } + + return 0; +} + +static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* session) +{ + struct session_item_st* ns = NULL; + int ret; + const char* str = NULL; + + ns = ncr_session_new(&lists->sessions); + if (ns == NULL) { + err(); + return -EINVAL; + } + + ns->op = session->op; + ns->algorithm = session->algorithm; + switch(session->op) { + case NCR_OP_ENCRYPT: + case NCR_OP_DECRYPT: + if (algo_can_encrypt(session->algorithm)==0) { + err(); + ret = -EINVAL; + goto fail; + } + + /* read key */ + ret = ncr_key_item_get_read( &ns->key, &lists->key, session->params.key); + if (ret < 0) { + err(); + goto fail; + } + if (ns->key->type == NCR_KEY_TYPE_SECRET) { + int keysize = ns->key->key.secret.size; + + if (session->algorithm == NCR_ALG_NULL) + keysize = 0; + + str = _ncr_algo_to_str(session->algorithm); + if (str == NULL) { + err(); + return -EINVAL; + } + + ret = cryptodev_cipher_init(&ns->cipher, str, + ns->key->key.secret.data, keysize); + if (ret < 0) { + err(); + goto fail; + } + + if (algo_needs_iv(session->algorithm)) { + if (session->params.params.cipher.iv_size > sizeof(session->params.params.cipher.iv)) { + err(); + ret = -EINVAL; + goto fail; + } + cryptodev_cipher_set_iv(&ns->cipher, session->params.params.cipher.iv, session->params.params.cipher.iv_size); + } + } else if (ns->key->type == NCR_KEY_TYPE_PRIVATE || ns->key->type == NCR_KEY_TYPE_PUBLIC) { + ret = ncr_pk_cipher_init(ns->algorithm, &ns->pk, + &session->params, ns->key); + if (ret < 0) { + err(); + goto fail; + } + } else { + err(); + ret = -EINVAL; + goto fail; + } + break; + + case NCR_OP_SIGN: + case NCR_OP_VERIFY: + if (algo_can_sign(session->algorithm)==0) { + err(); + ret = -EINVAL; + goto fail; + } + + /* read key */ + ret = ncr_key_item_get_read( &ns->key, &lists->key, session->params.key); + if (ret < 0) { + err(); + goto fail; + } + + if (ns->key->type == NCR_KEY_TYPE_SECRET) { + str = _ncr_algo_to_str(session->algorithm); + if (str == NULL) { + err(); + return -EINVAL; + } + + ret = cryptodev_hash_init(&ns->hash, str, 1, + ns->key->key.secret.data, ns->key->key.secret.size); + if (ret < 0) { + err(); + goto fail; + } + + } else if (ns->key->type == NCR_KEY_TYPE_PRIVATE || ns->key->type == NCR_KEY_TYPE_PUBLIC) { + if (algo_can_digest(session->params.params.pk.sign_hash) == 0) { + err(); + ret = -EINVAL; + goto fail; + } + str = _ncr_algo_to_str(session->params.params.pk.sign_hash); + if (str == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + + ret = ncr_pk_cipher_init(ns->algorithm, &ns->pk, + &session->params, ns->key); + if (ret < 0) { + err(); + goto fail; + } + + ret = cryptodev_hash_init(&ns->hash, str, 0, NULL, 0); + if (ret < 0) { + err(); + goto fail; + } + } else { + err(); + ret = -EINVAL; + goto fail; + } + + break; + case NCR_OP_DIGEST: + if (algo_can_digest(session->algorithm)==0) { + err(); + ret = -EINVAL; + goto fail; + } + str = _ncr_algo_to_str(session->algorithm); + if (str == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + + ret = cryptodev_hash_init(&ns->hash, str, 0, NULL, 0); + if (ret < 0) { + err(); + goto fail; + } + + break; + default: + err(); + ret = -EINVAL; + goto fail; + } + + ret = 0; + session->ses = ns->desc; + +fail: + if (ret < 0) { + _ncr_session_remove(&lists->sessions, ns->desc); + } + + return ret; +} + +int ncr_session_init(struct ncr_lists* lists, void __user* arg) +{ + struct ncr_session_st session; + int ret; + + if (unlikely(copy_from_user(&session, arg, sizeof(session)))) { + err(); + return -EFAULT; + } + + ret = _ncr_session_init(lists, &session); + if (unlikely(ret)) { + err(); + return ret; + } + + ret = copy_to_user( arg, &session, sizeof(session)); + if (unlikely(ret)) { + err(); + _ncr_session_remove(&lists->sessions, session.ses); + return -EFAULT; + } + return ret; +} + +/* Main update function + */ +static int _ncr_session_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; + struct scatterlist *osg; + struct scatterlist *isg; + size_t osg_size, isg_size, new_size, max_odata_size, idata_size; + unsigned int osg_cnt, isg_cnt; + + sess = ncr_sessions_item_get( &lists->sessions, op->ses); + if (sess == NULL) { + err(); + return -EINVAL; + } + + switch(sess->op) { + case NCR_OP_ENCRYPT: + /* obtain data item */ + data = ncr_data_item_get( &lists->data, op->data.cipher.plaintext); + if (data == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + + odata = ncr_data_item_get( &lists->data, op->data.cipher.ciphertext); + if (odata == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + + /* output data will have the same status as input data */ + ret = ncr_data_item_get_sg(odata, &osg, &osg_cnt, NULL, &osg_size, data->flags, 1); + if (ret < 0) { + err(); + goto fail; + } + + if (odata != data) { + ret = ncr_data_item_get_sg(data, &isg, &isg_cnt, &isg_size, NULL, data->flags, 0); + if (ret < 0) { + err(); + goto fail; + } + } else { + isg = osg; + isg_cnt = osg_cnt; + isg_size = osg_size; + } + + ret = ncr_data_item_size(odata, 1); + if (ret < 0) { + err(); + goto fail; + } + max_odata_size = ret; + + ret = ncr_data_item_size(data, 0); + if (ret < 0) { + err(); + goto fail; + } + idata_size = ret; + + if (max_odata_size < idata_size) { + err(); + ret = -EINVAL; + goto fail; + } + + if (algo_is_symmetric(sess->algorithm)) { + /* read key */ + ret = cryptodev_cipher_encrypt(&sess->cipher, isg, osg, isg_size); + if (ret < 0) { + err(); + goto fail; + } + + /* FIXME: handle ciphers that do not require that */ + new_size = idata_size; + + } else { /* public key */ + new_size = osg_size; + ret = ncr_pk_cipher_encrypt(&sess->pk, isg, isg_cnt, isg_size, + osg, osg_cnt, &new_size); + if (ret < 0) { + err(); + goto fail; + } + } + ret = ncr_data_item_set_size(odata, new_size); + if (ret < 0) { + err(); + goto fail; + } + + break; + case NCR_OP_DECRYPT: + /* obtain data item */ + data = ncr_data_item_get( &lists->data, op->data.cipher.ciphertext); + if (data == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + + odata = ncr_data_item_get( &lists->data, op->data.cipher.plaintext); + if (odata == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + + ret = ncr_data_item_get_sg(odata, &osg, &osg_cnt, NULL, &osg_size, data->flags, 1); + if (ret < 0) { + err(); + goto fail; + } + + if (odata != data) { + ret = ncr_data_item_get_sg(data, &isg, &isg_cnt, NULL, &isg_size, data->flags, 0); + if (ret < 0) { + err(); + goto fail; + } + } else { + isg = osg; + isg_cnt = osg_cnt; + isg_size = osg_size; + } + + ret = ncr_data_item_size(odata, 1); + if (ret < 0) { + err(); + goto fail; + } + max_odata_size = ret; + + ret = ncr_data_item_size(data, 0); + if (ret < 0) { + err(); + goto fail; + } + idata_size = ret; + + if (max_odata_size < idata_size) { + err(); + ret = -EINVAL; + goto fail; + } + + /* read key */ + if (algo_is_symmetric(sess->algorithm)) { + ret = cryptodev_cipher_decrypt(&sess->cipher, isg, osg, isg_size); + if (ret < 0) { + err(); + goto fail; + } + /* FIXME: handle ciphers that do not require that */ + new_size = idata_size; + } else { /* public key */ + size_t new_size = osg_size; + ret = ncr_pk_cipher_decrypt(&sess->pk, isg, isg_cnt, isg_size, + osg, osg_cnt, &new_size); + if (ret < 0) { + err(); + goto fail; + } + } + + ret = ncr_data_item_set_size(odata, new_size); + if (ret < 0) { + err(); + goto fail; + } + + + break; + + case NCR_OP_SIGN: + case NCR_OP_DIGEST: + /* obtain data item */ + data = ncr_data_item_get( &lists->data, op->data.sign.text); + if (data == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + + ret = ncr_data_item_get_sg(data, &isg, &isg_cnt, &isg_size, NULL, data->flags, 0); + if (ret < 0) { + err(); + goto fail; + } + + ret = cryptodev_hash_update(&sess->hash, isg, isg_size); + if (ret < 0) { + err(); + goto fail; + } + break; + + case NCR_OP_VERIFY: + /* obtain data item */ + data = ncr_data_item_get( &lists->data, op->data.verify.text); + if (data == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + + ret = ncr_data_item_get_sg(data, &isg, &isg_cnt, &isg_size, NULL, data->flags, 0); + if (ret < 0) { + err(); + goto fail; + } + + ret = cryptodev_hash_update(&sess->hash, isg, isg_size); + if (ret < 0) { + err(); + goto fail; + } + break; + + default: + err(); + ret = -EINVAL; + goto fail; + } + + ret = 0; + +fail: + if (odata) { + ncr_data_item_put_sg(odata); + _ncr_data_item_put(odata); + } + if (data) { + ncr_data_item_put_sg(data); + _ncr_data_item_put(data); + } + _ncr_sessions_item_put(sess); + + return ret; +} + +int ncr_session_update(struct ncr_lists* lists, void __user* arg) +{ + struct ncr_session_op_st op; + + if (unlikely(copy_from_user( &op, arg, sizeof(op)))) { + err(); + return -EFAULT; + } + + return _ncr_session_update(lists, &op); +} + +static 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 _ncr_session_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]; + struct scatterlist *osg; + size_t osg_size, odata_size, max_odata_size; + unsigned int osg_cnt; + + sess = ncr_sessions_item_get( &lists->sessions, op->ses); + if (sess == NULL) { + err(); + return -EINVAL; + } + + switch(sess->op) { + case NCR_OP_ENCRYPT: + case NCR_OP_DECRYPT: + /* obtain data item */ + if (op->data.cipher.plaintext != NCR_DATA_INVALID && + op->data.cipher.ciphertext != NCR_DATA_INVALID) { + ret = _ncr_session_update(lists, op); + if (ret < 0) + goto fail; + } + break; + + case NCR_OP_VERIFY: + /* obtain data item */ + if (op->data.sign.text != NCR_DATA_INVALID) { + ret = _ncr_session_update(lists, op); + if (ret < 0) + goto fail; + } + + odata = ncr_data_item_get( &lists->data, op->data.verify.signature); + if (odata == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + + digest_size = sess->hash.digestsize; + if (digest_size == 0 || sizeof(digest) < digest_size) { + err(); + ret = -EINVAL; + goto fail; + } + ret = cryptodev_hash_final(&sess->hash, digest); + if (ret < 0) { + err(); + goto fail; + } + + ret = ncr_data_item_size(odata, 0); + if (ret < 0) { + err(); + goto fail; + } + odata_size = ret; + + if (algo_is_hmac(sess->algorithm)) { + uint8_t vdigest[digest_size]; + + ret = ncr_data_item_getd( odata, vdigest, sizeof(vdigest), odata->flags); + if (ret < 0) { + err(); + goto fail; + } + + if (digest_size != odata_size || + memcmp(vdigest, digest, digest_size) != 0) { + + op->err = NCR_VERIFICATION_FAILED; + } else { + op->err = NCR_SUCCESS; + } + } else { + /* PK signature */ + ret = ncr_data_item_get_sg(odata, &osg, &osg_cnt, NULL, &osg_size, odata->flags, 0); + if (ret < 0) { + err(); + goto fail; + } + + ret = ncr_pk_cipher_verify(&sess->pk, osg, osg_cnt, osg_size, + digest, digest_size, &op->err); + if (ret < 0) { + err(); + goto fail; + } + } + break; + + case NCR_OP_SIGN: + case NCR_OP_DIGEST: + /* obtain data item */ + if (op->data.sign.text != NCR_DATA_INVALID) { + ret = _ncr_session_update(lists, op); + if (ret < 0) + goto fail; + } + odata = ncr_data_item_get( &lists->data, op->data.sign.output); + if (odata == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + + ret = ncr_data_item_size(odata, 1); + if (ret < 0) { + err(); + goto fail; + } + max_odata_size = ret; + + digest_size = sess->hash.digestsize; + if (digest_size == 0 || max_odata_size < digest_size) { + err(); + ret = -EINVAL; + goto fail; + } + ret = cryptodev_hash_final(&sess->hash, digest); + if (ret < 0) { + err(); + goto fail; + } + + ret = ncr_data_item_setd( odata, digest, digest_size, odata->flags); + if (ret < 0) { + err(); + goto fail; + } + + cryptodev_hash_deinit(&sess->hash); + + if (sess->op != NCR_OP_DIGEST && !algo_is_hmac(sess->algorithm)) { + size_t new_size; + + ret = ncr_data_item_get_sg(odata, &osg, &osg_cnt, NULL, &osg_size, odata->flags, 1); + if (ret < 0) { + err(); + goto fail; + } + + new_size = osg_size; + + /* PK signature */ + ret = ncr_pk_cipher_sign(&sess->pk, osg, osg_cnt, digest_size, + osg, osg_cnt, &new_size); + if (ret < 0) { + err(); + goto fail; + } + + ret = ncr_data_item_set_size(odata, new_size); + if (ret < 0) { + err(); + goto fail; + } + } + + break; + default: + err(); + ret = -EINVAL; + goto fail; + } + + ret = 0; + +fail: + if (odata) { + ncr_data_item_put_sg(odata); + _ncr_data_item_put(odata); + } + cryptodev_hash_deinit(&sess->hash); + if (algo_is_symmetric(sess->algorithm)) { + cryptodev_cipher_deinit(&sess->cipher); + } else { + ncr_pk_cipher_deinit(&sess->pk); + } + + _ncr_sessions_item_put(sess); + _ncr_session_remove(&lists->sessions, op->ses); + + return ret; +} + +int ncr_session_final(struct ncr_lists* lists, void __user* arg) +{ + struct ncr_session_op_st op; + int ret; + + if (unlikely(copy_from_user(&op, arg, sizeof(op)))) { + err(); + return -EFAULT; + } + + ret = _ncr_session_final(lists, &op); + if (unlikely(ret)) { + err(); + return ret; + } + + if (unlikely(copy_to_user(arg, &op, sizeof(op)))) { + err(); + return -EFAULT; + } + return 0; +} + +int ncr_session_once(struct ncr_lists* lists, void __user* arg) +{ + struct ncr_session_once_op_st kop; + int ret; + + if (unlikely(copy_from_user(&kop, arg, sizeof(kop)))) { + err(); + return -EFAULT; + } + + ret = _ncr_session_init(lists, &kop.init); + if (ret < 0) { + err(); + return ret; + } + kop.op.ses = kop.init.ses; + + ret = _ncr_session_final(lists, &kop.op); + if (ret < 0) { + err(); + return ret; + } + + if (unlikely(copy_to_user(arg, &kop, sizeof(kop)))) + return -EFAULT; + return 0; +} + |