summaryrefslogtreecommitdiffstats
path: root/crypto/userspace/ncr-sessions.c
diff options
context:
space:
mode:
authorMiloslav Trmač <mitr@redhat.com>2010-07-26 21:07:19 +0200
committerMiloslav Trmač <mitr@redhat.com>2010-07-26 21:07:19 +0200
commit7cc28b8bafe112037edae18ea0e590e7c9d074fa (patch)
tree5836f1f9cdd22d731e986ea4f8ed73649bf7b6e4 /crypto/userspace/ncr-sessions.c
parentda3eeeff5744e8ea4bcdb819db3afad6437f5231 (diff)
parenta04fd1aa4f807e2f97632a46070306e1389264ed (diff)
downloadkernel-crypto-7cc28b8bafe112037edae18ea0e590e7c9d074fa.tar.gz
kernel-crypto-7cc28b8bafe112037edae18ea0e590e7c9d074fa.tar.xz
kernel-crypto-7cc28b8bafe112037edae18ea0e590e7c9d074fa.zip
Merge branch 'standalone-rename' into userspace-crypto
Conflicts: crypto/userspace/Makefile crypto/userspace/ncr-data.c crypto/userspace/ncr-key-storage.c crypto/userspace/ncr-key-wrap.c crypto/userspace/ncr-key.c crypto/userspace/ncr-limits.c crypto/userspace/ncr-pk.c crypto/userspace/ncr-sessions.c crypto/userspace/ncr.c
Diffstat (limited to 'crypto/userspace/ncr-sessions.c')
-rw-r--r--crypto/userspace/ncr-sessions.c954
1 files changed, 566 insertions, 388 deletions
diff --git a/crypto/userspace/ncr-sessions.c b/crypto/userspace/ncr-sessions.c
index 5d9fc93f3be..42a352ca62b 100644
--- a/crypto/userspace/ncr-sessions.c
+++ b/crypto/userspace/ncr-sessions.c
@@ -22,8 +22,11 @@
#include <linux/crypto.h>
#include <linux/cryptodev.h>
#include <linux/ncr.h>
-#include "ncr_int.h"
+#include "ncr-int.h"
+#include <linux/mm_types.h>
+#include <linux/scatterlist.h>
+static int _ncr_session_update_key(struct ncr_lists* lists, struct ncr_session_op_st* op);
static void _ncr_session_remove(struct list_sem_st* lst, ncr_session_t desc);
void ncr_sessions_list_deinit(struct list_sem_st* lst)
@@ -84,6 +87,8 @@ void _ncr_sessions_item_put( struct session_item_st* item)
cryptodev_hash_deinit(&item->hash);
if (item->key)
_ncr_key_item_put(item->key);
+ kfree(item->sg);
+ kfree(item->pages);
kfree(item);
}
}
@@ -92,13 +97,25 @@ struct session_item_st* ncr_session_new(struct list_sem_st* lst)
{
struct session_item_st* sess;
- sess = kmalloc(sizeof(*sess), GFP_KERNEL);
+ sess = kzalloc(sizeof(*sess), GFP_KERNEL);
if (sess == NULL) {
err();
return NULL;
}
- memset(sess, 0, sizeof(*sess));
+ sess->array_size = DEFAULT_PREALLOC_PAGES;
+ sess->pages = kzalloc(sess->array_size *
+ sizeof(struct page *), GFP_KERNEL);
+ sess->sg = kzalloc(sess->array_size *
+ sizeof(struct scatterlist), GFP_KERNEL);
+ if (sess->sg == NULL || sess->pages == NULL) {
+ err();
+ kfree(sess->sg);
+ kfree(sess->pages);
+ kfree(sess);
+ return NULL;
+ }
+ init_MUTEX(&sess->mem_mutex);
atomic_set(&sess->refcnt, 1);
@@ -112,186 +129,96 @@ struct session_item_st* ncr_session_new(struct list_sem_st* lst)
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[] = {
+static const struct algo_properties_st algo_properties[] = {
{ .algo = NCR_ALG_NULL, .kstr = "ecb(cipher_null)",
- .needs_iv = 0, .symmetric=1, .can_encrypt=1 },
+ .needs_iv = 0, .is_symmetric=1, .can_encrypt=1,
+ .key_type = NCR_KEY_TYPE_INVALID },
{ .algo = NCR_ALG_3DES_CBC, .kstr = "cbc(des3_ede)",
- .needs_iv = 1, .symmetric=1, .can_encrypt=1 },
+ .needs_iv = 1, .is_symmetric=1, .can_encrypt=1,
+ .key_type = NCR_KEY_TYPE_SECRET },
{ .algo = NCR_ALG_AES_CBC, .kstr = "cbc(aes)",
- .needs_iv = 1, .symmetric=1, .can_encrypt=1 },
+ .needs_iv = 1, .is_symmetric=1, .can_encrypt=1,
+ .key_type = NCR_KEY_TYPE_SECRET },
{ .algo = NCR_ALG_CAMELLIA_CBC, .kstr = "cbc(camelia)",
- .needs_iv = 1, .symmetric=1, .can_encrypt=1 },
+ .needs_iv = 1, .is_symmetric=1, .can_encrypt=1,
+ .key_type = NCR_KEY_TYPE_SECRET },
{ .algo = NCR_ALG_AES_CTR, .kstr = "ctr(aes)",
- .needs_iv = 1, .symmetric=1, .can_encrypt=1 },
+ .needs_iv = 1, .is_symmetric=1, .can_encrypt=1,
+ .key_type = NCR_KEY_TYPE_SECRET },
{ .algo = NCR_ALG_CAMELLIA_CTR, .kstr = "ctr(camelia)",
- .needs_iv = 1, .symmetric=1, .can_encrypt=1 },
+ .needs_iv = 1, .is_symmetric=1, .can_encrypt=1,
+ .key_type = NCR_KEY_TYPE_SECRET },
{ .algo = NCR_ALG_ARCFOUR, .kstr = NULL,
- .needs_iv = 0, .symmetric=1, .can_encrypt=1 },
+ .needs_iv = 0, .is_symmetric=1, .can_encrypt=1,
+ .key_type = NCR_KEY_TYPE_SECRET },
{ .algo = NCR_ALG_AES_ECB, .kstr = "ecb(aes)",
- .needs_iv = 0, .symmetric=1, .can_encrypt=1 },
+ .needs_iv = 0, .is_symmetric=1, .can_encrypt=1,
+ .key_type = NCR_KEY_TYPE_SECRET },
{ .algo = NCR_ALG_CAMELLIA_ECB, .kstr = "ecb(camelia)",
- .needs_iv = 0, .symmetric=1, .can_encrypt=1 },
+ .needs_iv = 0, .is_symmetric=1, .can_encrypt=1,
+ .key_type = NCR_KEY_TYPE_SECRET },
{ .algo = NCR_ALG_SHA1, .kstr = "sha1",
- .digest_size = 20, .can_digest=1 },
+ .digest_size = 20, .can_digest=1,
+ .key_type = NCR_KEY_TYPE_INVALID },
{ .algo = NCR_ALG_MD5, .kstr = "md5",
- .digest_size = 16, .can_digest=1 },
+ .digest_size = 16, .can_digest=1,
+ .key_type = NCR_KEY_TYPE_INVALID },
{ .algo = NCR_ALG_SHA2_224, .kstr = "sha224",
- .digest_size = 28, .can_digest=1 },
+ .digest_size = 28, .can_digest=1,
+ .key_type = NCR_KEY_TYPE_INVALID },
{ .algo = NCR_ALG_SHA2_256, .kstr = "sha256",
- .digest_size = 32, .can_digest=1 },
+ .digest_size = 32, .can_digest=1,
+ .key_type = NCR_KEY_TYPE_INVALID },
{ .algo = NCR_ALG_SHA2_384, .kstr = "sha384",
- .digest_size = 48, .can_digest=1 },
+ .digest_size = 48, .can_digest=1,
+ .key_type = NCR_KEY_TYPE_INVALID },
{ .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 },
+ .digest_size = 64, .can_digest=1,
+ .key_type = NCR_KEY_TYPE_INVALID },
+ { .algo = NCR_ALG_HMAC_SHA1, .is_hmac = 1, .kstr = "hmac(sha1)",
+ .digest_size = 20, .can_sign=1,
+ .key_type = NCR_KEY_TYPE_SECRET },
+ { .algo = NCR_ALG_HMAC_MD5, .is_hmac = 1, .kstr = "hmac(md5)",
+ .digest_size = 16, .can_sign=1,
+ .key_type = NCR_KEY_TYPE_SECRET },
+ { .algo = NCR_ALG_HMAC_SHA2_224, .is_hmac = 1, .kstr = "hmac(sha224)",
+ .digest_size = 28, .can_sign=1,
+ .key_type = NCR_KEY_TYPE_SECRET },
+ { .algo = NCR_ALG_HMAC_SHA2_256, .is_hmac = 1, .kstr = "hmac(sha256)",
+ .digest_size = 32, .can_sign=1,
+ .key_type = NCR_KEY_TYPE_SECRET },
+ { .algo = NCR_ALG_HMAC_SHA2_384, .is_hmac = 1, .kstr = "hmac(sha384)",
+ .digest_size = 48, .can_sign=1,
+ .key_type = NCR_KEY_TYPE_SECRET },
+ { .algo = NCR_ALG_HMAC_SHA2_512, .is_hmac = 1, .kstr = "hmac(sha512)",
+ .digest_size = 64, .can_sign=1,
+ .key_type = NCR_KEY_TYPE_SECRET },
+ { .algo = NCR_ALG_RSA, .kstr = NULL, .is_pk = 1,
+ .can_encrypt=1, .can_sign=1, .key_type = NCR_KEY_TYPE_PUBLIC },
+ { .algo = NCR_ALG_DSA, .kstr = NULL, .is_pk = 1,
+ .can_sign=1, .key_type = NCR_KEY_TYPE_PUBLIC },
{ .algo = NCR_ALG_NONE }
};
-const char* _ncr_algo_to_str(ncr_algorithm_t algo)
+const struct algo_properties_st *_ncr_algo_to_properties(ncr_algorithm_t algo)
{
-ncr_algorithm_t a;
-int i = 0;
+ ncr_algorithm_t a;
+ int i = 0;
- while((a=algo_properties[i].algo)!=NCR_ALG_NONE) {
+ for (i = 0; (a = algo_properties[i].algo) != NCR_ALG_NONE; i++) {
if (a == algo)
- return algo_properties[i].kstr;
- i++;
+ return &algo_properties[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;
- ncr_algorithm_t sign_hash;
- const char* str = NULL;
+ const struct algo_properties_st *sign_hash;
ns = ncr_session_new(&lists->sessions);
if (ns == NULL) {
@@ -300,11 +227,17 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses
}
ns->op = session->op;
- ns->algorithm = session->algorithm;
+ ns->algorithm = _ncr_algo_to_properties(session->algorithm);
+ if (ns->algorithm == NULL) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
switch(session->op) {
case NCR_OP_ENCRYPT:
case NCR_OP_DECRYPT:
- if (algo_can_encrypt(session->algorithm)==0) {
+ if (!ns->algorithm->can_encrypt) {
err();
ret = -EINVAL;
goto fail;
@@ -322,20 +255,19 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses
if (session->algorithm == NCR_ALG_NULL)
keysize = 0;
- str = _ncr_algo_to_str(session->algorithm);
- if (str == NULL) {
+ if (ns->algorithm->kstr == NULL) {
err();
return -EINVAL;
}
- ret = cryptodev_cipher_init(&ns->cipher, str,
+ ret = cryptodev_cipher_init(&ns->cipher, ns->algorithm->kstr,
ns->key->key.secret.data, keysize);
if (ret < 0) {
err();
goto fail;
}
- if (algo_needs_iv(session->algorithm)) {
+ if (ns->algorithm->needs_iv) {
if (session->params.params.cipher.iv_size > sizeof(session->params.params.cipher.iv)) {
err();
ret = -EINVAL;
@@ -345,7 +277,7 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses
}
} 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);
+ &session->params, ns->key, NULL);
if (ret < 0) {
err();
goto fail;
@@ -359,89 +291,83 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses
case NCR_OP_SIGN:
case NCR_OP_VERIFY:
- if (algo_can_sign(session->algorithm)==0) {
+ if (!ns->algorithm->can_sign && !ns->algorithm->can_digest) {
err();
ret = -EINVAL;
goto fail;
}
- /* read key */
- ret = ncr_key_item_get_read( &ns->key, &lists->key, session->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) {
+ if (ns->algorithm->can_digest) {
+ if (ns->algorithm->kstr == NULL) {
err();
+ ret = -EINVAL;
goto fail;
}
- } else if (ns->key->type == NCR_KEY_TYPE_PRIVATE || ns->key->type == NCR_KEY_TYPE_PUBLIC) {
- ret = ncr_key_params_get_sign_hash(ns->key->algorithm, &session->params);
+ ret = cryptodev_hash_init(&ns->hash, ns->algorithm->kstr, 0, NULL, 0);
if (ret < 0) {
err();
- return ret;
- }
- sign_hash = ret;
-
- if (algo_can_digest(sign_hash) == 0) {
- err();
- ret = -EINVAL;
goto fail;
}
- str = _ncr_algo_to_str(sign_hash);
- if (str == NULL) {
- err();
- ret = -EINVAL;
- goto fail;
- }
-
- ret = ncr_pk_cipher_init(ns->algorithm, &ns->pk,
- &session->params, ns->key);
+
+ } else {
+ /* read key */
+ ret = ncr_key_item_get_read( &ns->key, &lists->key, session->key);
if (ret < 0) {
err();
goto fail;
}
- ret = cryptodev_hash_init(&ns->hash, str, 0, NULL, 0);
- if (ret < 0) {
+ if (ns->algorithm->is_hmac && ns->key->type == NCR_KEY_TYPE_SECRET) {
+ if (ns->algorithm->kstr == NULL) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ ret = cryptodev_hash_init(&ns->hash, ns->algorithm->kstr, 1,
+ ns->key->key.secret.data, ns->key->key.secret.size);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ } else if (ns->algorithm->is_pk && (ns->key->type == NCR_KEY_TYPE_PRIVATE || ns->key->type == NCR_KEY_TYPE_PUBLIC)) {
+ sign_hash = ncr_key_params_get_sign_hash(ns->key->algorithm, &session->params);
+ if (IS_ERR(sign_hash)) {
+ err();
+ return PTR_ERR(sign_hash);
+ }
+
+ if (!sign_hash->can_digest) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (sign_hash->kstr == NULL) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ ret = ncr_pk_cipher_init(ns->algorithm, &ns->pk,
+ &session->params, ns->key, sign_hash);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ ret = cryptodev_hash_init(&ns->hash, sign_hash->kstr, 0, NULL, 0);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+ } else {
err();
+ ret = -EINVAL;
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;
@@ -487,14 +413,223 @@ int ncr_session_init(struct ncr_lists* lists, void __user* arg)
return ret;
}
-/* Main update function
- */
-static int _ncr_session_update(struct ncr_lists* lists, struct ncr_session_op_st* op)
+int _ncr_session_encrypt(struct session_item_st* sess, const struct scatterlist* input, unsigned input_cnt,
+ size_t input_size, void *output, unsigned output_cnt, size_t *output_size)
+{
+int ret;
+
+ if (sess->algorithm->is_symmetric) {
+ /* read key */
+ ret = cryptodev_cipher_encrypt(&sess->cipher, input,
+ output, input_size);
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+ /* FIXME: handle ciphers that do not require that */
+
+ } else { /* public key */
+ ret = ncr_pk_cipher_encrypt(&sess->pk, input, input_cnt, input_size,
+ output, output_cnt, output_size);
+
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int _ncr_session_decrypt(struct session_item_st* sess, const struct scatterlist* input,
+ unsigned input_cnt, size_t input_size,
+ struct scatterlist *output, unsigned output_cnt, size_t *output_size)
+{
+int ret;
+
+ if (sess->algorithm->is_symmetric) {
+ /* read key */
+ ret = cryptodev_cipher_decrypt(&sess->cipher, input,
+ output, input_size);
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+ /* FIXME: handle ciphers that do not require equality */
+
+ } else { /* public key */
+ ret = ncr_pk_cipher_decrypt(&sess->pk, input, input_cnt, input_size,
+ output, output_cnt, output_size);
+
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+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,
+ void __user * udata, size_t udata_size, struct scatterlist **dst_sg, unsigned *dst_cnt)
+{
+ int pagecount = 0;
+
+ if (unlikely(udata == NULL)) {
+ err();
+ return -EINVAL;
+ }
+
+ if (unlikely(ses->sg == NULL || ses->pages == NULL)) {
+ err();
+ return -ENOMEM;
+ }
+
+ pagecount = PAGECOUNT(udata, udata_size);
+
+ 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 (unlikely(ses->sg == NULL || ses->pages == NULL)) {
+ return -ENOMEM;
+ }
+ }
+
+ if (__get_userbuf(udata, udata_size, 1,
+ pagecount, ses->pages, ses->sg)) {
+ err();
+ return -EINVAL;
+ }
+ (*dst_sg) = ses->sg;
+ *dst_cnt = pagecount;
+
+ ses->available_pages = 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;
+ size_t input_size = op->data.udata.input_size;
+
+ if (unlikely(op->data.udata.input == NULL)) {
+ err();
+ return -EINVAL;
+ }
+
+ if (unlikely(ses->sg == NULL || ses->pages == NULL)) {
+ err();
+ return -ENOMEM;
+ }
+
+ src_pagecount = PAGECOUNT(op->data.udata.input, input_size);
+
+ if (op->data.udata.input != op->data.udata.output) { /* non-in-situ transformation */
+ write_src = 0;
+ if (op->data.udata.output != NULL) {
+ dst_pagecount = PAGECOUNT(op->data.udata.output, op->data.udata.output_size);
+ } else {
+ dst_pagecount = 0;
+ }
+ } else {
+ src_pagecount = max((int)(PAGECOUNT(op->data.udata.output, op->data.udata.output_size)),
+ src_pagecount);
+ input_size = max(input_size, (size_t)op->data.udata.output_size);
+ }
+
+ 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, input_size, write_src,
+ src_pagecount, ses->pages, ses->sg)) {
+ err();
+ printk("write: %d\n", write_src);
+ 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)) {
+ err();
+ 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;
+ }
+ }
+
+ ses->available_pages = pagecount;
+
+ return 0;
+}
+
+/* Called when userspace buffers are used */
+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 *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) {
@@ -502,131 +637,68 @@ static int _ncr_session_update(struct ncr_lists* lists, struct ncr_session_op_st
return -EINVAL;
}
+ 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();
+ goto fail;
+ }
+ isg_size = op->data.udata.input_size;
+ osg_size = op->data.udata.output_size;
+
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) {
+ if (osg == NULL) {
err();
ret = -EINVAL;
goto fail;
}
- if (odata->max_data_size < data->data_size) {
+ ret = _ncr_session_encrypt(sess, isg, isg_cnt, isg_size,
+ osg, osg_cnt, &osg_size);
+ if (ret < 0) {
err();
- ret = -EINVAL;
goto fail;
}
+ op->data.udata.output_size = osg_size;
- if (algo_is_symmetric(sess->algorithm)) {
- /* read key */
- ret = _cryptodev_cipher_encrypt(&sess->cipher, data->data,
- data->data_size, odata->data, data->data_size);
- if (ret < 0) {
- err();
- goto fail;
- }
- /* FIXME: handle ciphers that do not require that */
- odata->data_size = data->data_size;
- } else { /* public key */
- size_t new_size = odata->max_data_size;
- ret = ncr_pk_cipher_encrypt(&sess->pk, data->data, data->data_size,
- odata->data, &new_size);
-
- odata->data_size = 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) {
+ if (osg == NULL) {
err();
ret = -EINVAL;
goto fail;
}
- odata = ncr_data_item_get( &lists->data, op->data.cipher.plaintext);
- if (odata == NULL) {
+ if (osg_size < isg_size) {
err();
ret = -EINVAL;
goto fail;
}
- if (odata->max_data_size < data->data_size) {
+ ret = _ncr_session_decrypt(sess, isg, isg_cnt, isg_size,
+ osg, osg_cnt, &osg_size);
+ if (ret < 0) {
err();
- ret = -EINVAL;
goto fail;
}
-
- /* read key */
- if (algo_is_symmetric(sess->algorithm)) {
- ret = _cryptodev_cipher_decrypt(&sess->cipher, data->data, data->data_size, odata->data, data->data_size);
- if (ret < 0) {
- err();
- goto fail;
- }
- /* FIXME: handle ciphers that do not require that */
- odata->data_size = data->data_size;
- } else { /* public key */
- size_t new_size = odata->max_data_size;
- ret = ncr_pk_cipher_decrypt(&sess->pk, data->data, data->data_size,
- odata->data, &new_size);
-
- odata->data_size = new_size;
-
- if (ret < 0) {
- err();
- goto fail;
- }
- }
+ op->data.udata.output_size = osg_size;
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 = _cryptodev_hash_update(&sess->hash, data->data, data->data_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 = _cryptodev_hash_update(&sess->hash, data->data, data->data_size);
+ ret = cryptodev_hash_update(&sess->hash, isg, isg_size);
if (ret < 0) {
err();
goto fail;
}
break;
-
default:
err();
ret = -EINVAL;
@@ -636,51 +708,42 @@ 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;
}
-int ncr_session_update(struct ncr_lists* lists, void __user* arg)
+static int try_session_update(struct ncr_lists* lists, struct ncr_session_op_st* op)
{
- struct ncr_session_op_st op;
-
- if (unlikely(copy_from_user( &op, arg, sizeof(op)))) {
- err();
- return -EFAULT;
+ if (op->type == NCR_KEY_DATA) {
+ if (op->data.kdata.input != NCR_KEY_INVALID)
+ return _ncr_session_update_key(lists, op);
+ } else if (op->type == NCR_DIRECT_DATA) {
+ if (op->data.udata.input != NULL)
+ return _ncr_session_update(lists, op);
}
-
- 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;
+ return 0;
}
-static int _ncr_session_final(struct ncr_lists* lists, struct ncr_session_op_st* op)
+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];
+ 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;
+ void __user * udata = NULL;
+ size_t *udata_size;
sess = ncr_sessions_item_get( &lists->sessions, op->ses);
if (sess == NULL) {
@@ -688,32 +751,42 @@ static int _ncr_session_final(struct ncr_lists* lists, struct ncr_session_op_st*
return -EINVAL;
}
+ ret = try_session_update(lists, op);
+ if (ret < 0) {
+ err();
+ _ncr_sessions_item_put(sess);
+ return ret;
+ }
+
+ if (down_interruptible(&sess->mem_mutex)) {
+ err();
+ _ncr_sessions_item_put(sess);
+ return -ERESTARTSYS;
+ }
+
+ if (op->type == NCR_DIRECT_DATA) {
+ udata = op->data.udata.output;
+ udata_size = &op->data.udata.output_size;
+ } else if (op->type == NCR_KEY_DATA) {
+ udata = op->data.kdata.output;
+ udata_size = &op->data.kdata.output_size;
+ } else {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
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) {
+ ret = get_userbuf1(sess, udata, *udata_size, &osg, &osg_cnt);
+ if (ret < 0) {
err();
- ret = -EINVAL;
goto fail;
}
+ orig_osg_size = osg_size = *udata_size;
digest_size = sess->hash.digestsize;
if (digest_size == 0 || sizeof(digest) < digest_size) {
@@ -726,11 +799,17 @@ static int _ncr_session_final(struct ncr_lists* lists, struct ncr_session_op_st*
err();
goto fail;
}
-
- if (algo_is_hmac(sess->algorithm)) {
- if (digest_size != odata->data_size ||
- memcmp(odata->data, digest, digest_size) != 0) {
+ 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 != osg_size ||
+ memcmp(vdigest, digest, digest_size) != 0) {
op->err = NCR_VERIFICATION_FAILED;
} else {
@@ -738,7 +817,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->data, odata->data_size,
+ ret = ncr_pk_cipher_verify(&sess->pk, osg, osg_cnt, osg_size,
digest, digest_size, &op->err);
if (ret < 0) {
err();
@@ -748,41 +827,46 @@ static int _ncr_session_final(struct ncr_lists* lists, struct ncr_session_op_st*
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;
+ ret = get_userbuf1(sess, udata, *udata_size, &osg, &osg_cnt);
+ if (ret < 0) {
+ err();
+ goto fail;
}
- odata = ncr_data_item_get( &lists->data, op->data.sign.output);
- if (odata == NULL) {
+ orig_osg_size = osg_size = *udata_size;
+
+ digest_size = sess->hash.digestsize;
+ if (digest_size == 0 || osg_size < digest_size) {
err();
ret = -EINVAL;
goto fail;
}
- digest_size = sess->hash.digestsize;
- if (digest_size == 0 || odata->max_data_size < digest_size) {
+ 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->op != NCR_OP_DIGEST && !algo_is_hmac(sess->algorithm)) {
+ if (sess->algorithm->is_pk) {
/* PK signature */
- size_t new_size = odata->max_data_size;
- ret = ncr_pk_cipher_sign(&sess->pk, odata->data, odata->data_size,
- odata->data, &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:
@@ -791,12 +875,20 @@ static int _ncr_session_final(struct ncr_lists* lists, struct ncr_session_op_st*
goto fail;
}
+ if (osg_size > 0)
+ *udata_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 (algo_is_symmetric(sess->algorithm)) {
+ if (sess->algorithm->is_symmetric) {
cryptodev_cipher_deinit(&sess->cipher);
} else {
ncr_pk_cipher_deinit(&sess->pk);
@@ -808,6 +900,93 @@ fail:
return ret;
}
+/* Direct with key: Allows to hash a key */
+/* Called when userspace buffers are used */
+static int _ncr_session_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;
+
+ 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;
+ }
+
+ 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 (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;
+ int ret;
+
+ if (unlikely(copy_from_user( &op, arg, sizeof(op)))) {
+ err();
+ return -EFAULT;
+ }
+
+ if (op.type == NCR_DIRECT_DATA)
+ ret = _ncr_session_update(lists, &op);
+ else if (op.type == NCR_KEY_DATA)
+ ret = _ncr_session_update_key(lists, &op);
+ else
+ ret = -EINVAL;
+
+ if (unlikely(ret)) {
+ err();
+ return ret;
+ }
+
+ if (unlikely(copy_to_user(arg, &op, sizeof(op)))) {
+ err();
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
int ncr_session_final(struct ncr_lists* lists, void __user* arg)
{
struct ncr_session_op_st op;
@@ -858,4 +1037,3 @@ int ncr_session_once(struct ncr_lists* lists, void __user* arg)
return -EFAULT;
return 0;
}
-