summaryrefslogtreecommitdiffstats
path: root/crypto/userspace/ncr-key.c
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/userspace/ncr-key.c')
-rw-r--r--crypto/userspace/ncr-key.c618
1 files changed, 618 insertions, 0 deletions
diff --git a/crypto/userspace/ncr-key.c b/crypto/userspace/ncr-key.c
new file mode 100644
index 00000000000..18cb38718a3
--- /dev/null
+++ b/crypto/userspace/ncr-key.c
@@ -0,0 +1,618 @@
+/*
+ * 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/mm.h>
+#include <linux/random.h>
+#include "cryptodev.h"
+#include <asm/uaccess.h>
+#include <asm/ioctl.h>
+#include <linux/scatterlist.h>
+#include "ncr.h"
+#include "ncr_int.h"
+
+static void ncr_key_clear(struct key_item_st* item);
+
+/* must be called with data semaphore down */
+static void _ncr_key_unlink_item(struct key_item_st *item)
+{
+ list_del(&item->list);
+ _ncr_key_item_put( item); /* decrement ref count */
+}
+
+void ncr_key_list_deinit(struct list_sem_st* lst)
+{
+ if(lst) {
+ struct key_item_st * item, *tmp;
+
+ down(&lst->sem);
+
+ list_for_each_entry_safe(item, tmp, &lst->list, list) {
+ _ncr_key_unlink_item(item);
+ }
+ up(&lst->sem);
+ }
+}
+
+/* must be called with data semaphore down
+ */
+static ncr_key_t _ncr_key_get_new_desc( struct list_sem_st* lst)
+{
+struct key_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 */
+int ncr_key_item_get_read(struct key_item_st**st, struct list_sem_st* lst,
+ ncr_key_t desc)
+{
+struct key_item_st* item;
+int ret;
+
+ *st = NULL;
+
+ down(&lst->sem);
+ list_for_each_entry(item, &lst->list, list) {
+ if (item->desc == desc) {
+ atomic_inc(&item->refcnt);
+
+ if (atomic_read(&item->writer) != 0) {
+ /* writer in place busy */
+ atomic_dec(&item->refcnt);
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ *st = item;
+ ret = 0;
+ goto exit;
+ }
+ }
+
+ err();
+ ret = -EINVAL;
+exit:
+ up(&lst->sem);
+ return ret;
+}
+
+/* as above but will never return anything that
+ * is in use.
+ */
+int ncr_key_item_get_write( struct key_item_st** st,
+ struct list_sem_st* lst, ncr_key_t desc)
+{
+struct key_item_st* item;
+int ret;
+
+ *st = NULL;
+
+ down(&lst->sem);
+ list_for_each_entry(item, &lst->list, list) {
+ if (item->desc == desc) {
+ /* do not return items that are in use already */
+
+ if (atomic_add_unless(&item->writer, 1, 1)==0) {
+ /* another writer so busy */
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ if (atomic_add_unless(&item->refcnt, 1, 2)==0) {
+ /* some reader is active so busy */
+ atomic_dec(&item->writer);
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ *st = item;
+ ret = 0;
+ goto exit;
+ }
+ }
+
+ err();
+ ret = -EINVAL;
+
+exit:
+ up(&lst->sem);
+ return ret;
+}
+
+void _ncr_key_item_put( struct key_item_st* item)
+{
+ if (atomic_read(&item->writer) > 0)
+ atomic_dec(&item->writer);
+ if (atomic_dec_and_test(&item->refcnt)) {
+ ncr_limits_remove(item->uid, item->pid, LIMIT_TYPE_KEY);
+ ncr_key_clear(item);
+ kfree(item);
+ }
+}
+
+int ncr_key_init(struct list_sem_st* lst, void __user* arg)
+{
+ ncr_key_t desc;
+ struct key_item_st* key;
+ int ret;
+
+ ret = ncr_limits_add_and_check(current_euid(), task_pid_nr(current), LIMIT_TYPE_KEY);
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+
+ key = kmalloc(sizeof(*key), GFP_KERNEL);
+ if (key == NULL) {
+ err();
+ ret = -ENOMEM;
+ goto err_limits;
+ }
+
+ memset(key, 0, sizeof(*key));
+
+ atomic_set(&key->refcnt, 1);
+ atomic_set(&key->writer, 0);
+
+ down(&lst->sem);
+
+ key->desc = _ncr_key_get_new_desc(lst);
+ key->uid = current_euid();
+ key->pid = task_pid_nr(current);
+
+ list_add(&key->list, &lst->list);
+
+ up(&lst->sem);
+
+ desc = key->desc;
+ ret = copy_to_user(arg, &desc, sizeof(desc));
+ if (unlikely(ret)) {
+ down(&lst->sem);
+ _ncr_key_unlink_item(key);
+ up(&lst->sem);
+ return -EFAULT;
+ }
+ return ret;
+
+err_limits:
+ ncr_limits_remove(current_euid(), task_pid_nr(current), LIMIT_TYPE_KEY);
+ return ret;
+}
+
+
+int ncr_key_deinit(struct list_sem_st* lst, void __user* arg)
+{
+ ncr_key_t desc;
+ struct key_item_st * item, *tmp;
+
+ if (unlikely(copy_from_user(&desc, arg, sizeof(desc)))) {
+ err();
+ return -EFAULT;
+ }
+
+ down(&lst->sem);
+
+ list_for_each_entry_safe(item, tmp, &lst->list, list) {
+ if(item->desc == desc) {
+ _ncr_key_unlink_item(item);
+ break;
+ }
+ }
+
+ up(&lst->sem);
+
+ return 0;
+}
+
+/* "exports" a key to a data item. If the key is not exportable
+ * to userspace then the data item will also not be.
+ */
+int ncr_key_export(struct list_sem_st* data_lst,
+ struct list_sem_st* key_lst, void __user* arg)
+{
+struct ncr_key_data_st data;
+struct key_item_st* item = NULL;
+struct data_item_st* ditem = NULL;
+uint32_t size;
+uint32_t data_flags;
+int ret;
+uint8_t* tmp = NULL;
+size_t max_data_size;
+
+ if (unlikely(copy_from_user(&data, arg, sizeof(data)))) {
+ err();
+ return -EFAULT;
+ }
+
+ ret = ncr_key_item_get_read( &item, key_lst, data.key);
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+
+ ditem = ncr_data_item_get( data_lst, data.data);
+ if (ditem == NULL) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ data_flags = key_flags_to_data(item->flags);
+
+ ret = ncr_data_item_size(ditem, 1);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+ max_data_size = ret;
+
+ switch (item->type) {
+ case NCR_KEY_TYPE_SECRET:
+ if (item->key.secret.size > max_data_size) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ /* found */
+ if (item->key.secret.size > 0) {
+ ret = ncr_data_item_setd( ditem,
+ item->key.secret.data, item->key.secret.size,
+ data_flags);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+ }
+ break;
+ case NCR_KEY_TYPE_PUBLIC:
+ case NCR_KEY_TYPE_PRIVATE:
+ size = max_data_size;
+
+ tmp = kmalloc(size, GFP_KERNEL);
+ if (tmp == NULL) {
+ err();
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ ret = ncr_pk_pack(item, tmp, &size);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ ret = ncr_data_item_setd( ditem, tmp, size, data_flags);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ kfree(tmp);
+
+ break;
+ default:
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ _ncr_key_item_put( item);
+ _ncr_data_item_put( ditem);
+
+ return 0;
+
+fail:
+ kfree(tmp);
+ if (item)
+ _ncr_key_item_put(item);
+ if (ditem)
+ _ncr_data_item_put(ditem);
+ return ret;
+
+}
+
+/* "imports" a key from a data item. If the key is not exportable
+ * to userspace then the key item will also not be.
+ */
+int ncr_key_import(struct list_sem_st* data_lst,
+ struct list_sem_st* key_lst, void __user* arg)
+{
+struct ncr_key_data_st data;
+struct key_item_st* item = NULL;
+struct data_item_st* ditem = NULL;
+uint8_t *tmp = NULL;
+int ret;
+size_t data_size;
+
+ if (unlikely(copy_from_user(&data, arg, sizeof(data)))) {
+ err();
+ return -EFAULT;
+ }
+
+ ret = ncr_key_item_get_write( &item, key_lst, data.key);
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+
+ ditem = ncr_data_item_get( data_lst, data.data);
+ if (ditem == NULL) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ item->type = data.type;
+ item->algorithm = data.algorithm;
+ item->flags = data.flags;
+ /* if data cannot be exported then the flags above
+ * should be overriden */
+ if (!(ditem->flags & NCR_DATA_FLAG_EXPORTABLE)) {
+ item->flags &= ~NCR_KEY_FLAG_EXPORTABLE;
+ }
+
+ if (data.key_id_size > MAX_KEY_ID_SIZE) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ item->key_id_size = data.key_id_size;
+ if (data.key_id_size > 0)
+ memcpy(item->key_id, data.key_id, data.key_id_size);
+
+ ret = ncr_data_item_size(ditem, 0);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+ data_size = ret;
+
+ switch(item->type) {
+ case NCR_KEY_TYPE_SECRET:
+ if (data_size > NCR_CIPHER_MAX_KEY_LEN) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ ret = ncr_data_item_getd(ditem, item->key.secret.data, data_size, item->flags);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+ item->key.secret.size = data_size;
+ break;
+ case NCR_KEY_TYPE_PRIVATE:
+ case NCR_KEY_TYPE_PUBLIC:
+ tmp = kmalloc(data_size, GFP_KERNEL);
+ if (tmp == NULL) {
+ err();
+ return -ENOMEM;
+ }
+
+ ret = ncr_data_item_getd(ditem, tmp, data_size, item->flags);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ ret = ncr_pk_unpack( item, tmp, data_size);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+ kfree(tmp);
+ break;
+
+ default:
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ _ncr_key_item_put( item);
+ _ncr_data_item_put( ditem);
+
+ return 0;
+
+fail:
+ kfree(tmp);
+ if (item)
+ _ncr_key_item_put(item);
+ if (ditem)
+ _ncr_data_item_put(ditem);
+ return ret;
+}
+
+static void ncr_key_clear(struct key_item_st* item)
+{
+ /* clears any previously allocated parameters */
+ if (item->type == NCR_KEY_TYPE_PRIVATE ||
+ item->type == NCR_KEY_TYPE_PUBLIC) {
+
+ ncr_pk_clear(item);
+ }
+ memset(&item->key, 0, sizeof(item->key));
+
+ return;
+}
+
+/* Generate a secret key
+ */
+int ncr_key_generate(struct list_sem_st* lst, void __user* arg)
+{
+struct ncr_key_generate_st gen;
+struct key_item_st* item = NULL;
+int ret;
+size_t size;
+
+ if (unlikely(copy_from_user(&gen, arg, sizeof(gen)))) {
+ err();
+ return -EFAULT;
+ }
+
+ ret = ncr_key_item_get_write( &item, lst, gen.desc);
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+
+ ncr_key_clear(item);
+
+ /* we generate only secret keys */
+ item->flags = gen.params.keyflags;
+ item->type = ncr_algorithm_to_key_type(gen.params.algorithm);
+ if (item->type == NCR_KEY_TYPE_SECRET) {
+ item->algorithm = /* arbitrary */ NCR_ALG_AES_CBC;
+
+ size = gen.params.params.secret.bits/8;
+ if ((gen.params.params.secret.bits % 8 != 0) ||
+ (size > NCR_CIPHER_MAX_KEY_LEN)) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ get_random_bytes(item->key.secret.data, size);
+ item->key.secret.size = size;
+
+ /* generate random key id */
+ item->key_id_size = 5;
+ get_random_bytes(item->key_id, item->key_id_size);
+ } else {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ ret = 0;
+
+fail:
+ if (item) {
+ if (ret < 0) item->type = NCR_KEY_TYPE_INVALID;
+ _ncr_key_item_put(item);
+ }
+ return ret;
+}
+
+int ncr_key_info(struct list_sem_st* lst, void __user* arg)
+{
+struct ncr_key_info_st info;
+struct key_item_st* item = NULL;
+int ret;
+
+ if (unlikely(copy_from_user(&info, arg, sizeof(info)))) {
+ err();
+ return -EFAULT;
+ }
+
+ ret = ncr_key_item_get_read(&item, lst, info.key);
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+
+ info.flags = item->flags;
+ info.type = item->type;
+ info.algorithm = item->algorithm;
+
+ _ncr_key_item_put( item);
+
+ return 0;
+}
+
+int ncr_key_generate_pair(struct list_sem_st* lst, void __user* arg)
+{
+struct ncr_key_generate_st gen;
+struct key_item_st* private = NULL;
+struct key_item_st* public = NULL;
+int ret;
+
+ if (unlikely(copy_from_user(&gen, arg, sizeof(gen)))) {
+ err();
+ return -EFAULT;
+ }
+
+ ret = ncr_key_item_get_write( &private, lst, gen.desc);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ ret = ncr_key_item_get_write( &public, lst, gen.desc2);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ ncr_key_clear(public);
+ ncr_key_clear(private);
+
+ /* we generate only secret keys */
+ private->flags = public->flags = gen.params.keyflags;
+ public->type = ncr_algorithm_to_key_type(gen.params.algorithm);
+ private->type = NCR_KEY_TYPE_PRIVATE;
+ private->algorithm = public->algorithm = gen.params.algorithm;
+ public->flags |= (NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPABLE);
+
+ if (public->type == NCR_KEY_TYPE_PUBLIC) {
+ ret = ncr_pk_generate(gen.params.algorithm, &gen.params, private, public);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+ } else {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ ret = 0;
+fail:
+ if (public) {
+ if (ret < 0) public->type = NCR_KEY_TYPE_INVALID;
+ _ncr_key_item_put(public);
+ }
+ if (private) {
+ if (ret < 0) private->type = NCR_KEY_TYPE_INVALID;
+ _ncr_key_item_put(private);
+ }
+ return ret;
+}
+
+int ncr_key_derive(struct list_sem_st* lst, void __user* arg)
+{
+ return -EINVAL;
+}
+
+int ncr_key_get_public(struct list_sem_st* lst, void __user* arg)
+{
+ return -EINVAL;
+}
+