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.c832
1 files changed, 832 insertions, 0 deletions
diff --git a/crypto/userspace/ncr-key.c b/crypto/userspace/ncr-key.c
new file mode 100644
index 00000000000..e6f7f025848
--- /dev/null
+++ b/crypto/userspace/ncr-key.c
@@ -0,0 +1,832 @@
+/*
+ * New driver for /dev/crypto device (aka CryptoDev)
+ *
+ * Copyright (c) 2010 Katholieke Universiteit Leuven
+ *
+ * Author: Nikos Mavrogiannopoulos <nmav@gnutls.org>
+ *
+ * This file is part of linux cryptodev.
+ *
+ * This program 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/audit.h>
+#include <linux/ioctl.h>
+#include <linux/mm.h>
+#include <linux/ncr.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/uaccess.h>
+#include <linux/scatterlist.h>
+#include <net/netlink.h>
+#include "ncr-int.h"
+#include "utils.h"
+
+static int key_list_deinit_fn(int id, void *item, void *unused)
+{
+ (void)unused;
+ _ncr_key_item_put(item);
+ return 0;
+}
+
+void ncr_key_list_deinit(struct ncr_lists *lst)
+{
+ /* The mutex is not necessary, but doesn't hurt and makes it easier to
+ verify locking correctness. */
+ mutex_lock(&lst->key_idr_mutex);
+ idr_for_each(&lst->key_idr, key_list_deinit_fn, NULL);
+ idr_remove_all(&lst->key_idr);
+ idr_destroy(&lst->key_idr);
+ mutex_unlock(&lst->key_idr_mutex);
+}
+
+/* returns the data item corresponding to desc */
+int ncr_key_item_get_read(struct key_item_st**st, struct ncr_lists *lst,
+ ncr_key_t desc)
+{
+struct key_item_st* item;
+int ret;
+
+ *st = NULL;
+
+ mutex_lock(&lst->key_idr_mutex);
+ item = idr_find(&lst->key_idr, desc);
+ if (item == NULL) {
+ err();
+ ret = -EINVAL;
+ goto exit;
+ }
+ 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;
+
+exit:
+ mutex_unlock(&lst->key_idr_mutex);
+ 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 ncr_lists *lst, ncr_key_t desc)
+{
+struct key_item_st* item;
+int ret;
+
+ *st = NULL;
+
+ mutex_lock(&lst->key_idr_mutex);
+ item = idr_find(&lst->key_idr, desc);
+ if (item == NULL) {
+ err();
+ ret = -EINVAL;
+ goto exit;
+ }
+ /* 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;
+
+exit:
+ mutex_unlock(&lst->key_idr_mutex);
+ 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);
+ }
+}
+
+static void _ncr_key_remove(struct ncr_lists *lst, ncr_key_t desc)
+{
+ struct key_item_st * item;
+
+ mutex_lock(&lst->key_idr_mutex);
+ item = idr_find(&lst->key_idr, desc);
+ if (item != NULL)
+ idr_remove(&lst->key_idr, desc); /* Steal the reference */
+ mutex_unlock(&lst->key_idr_mutex);
+
+ if (item != NULL)
+ _ncr_key_item_put(item);
+}
+
+int ncr_key_init(struct ncr_lists *lst)
+{
+ 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);
+ key->uid = current_euid();
+ key->pid = task_pid_nr(current);
+ key->context_id = lst->id;
+
+ mutex_lock(&lst->key_idr_mutex);
+ /* idr_pre_get() should preallocate enough, and, due to key_idr_mutex,
+ nobody else can use the preallocated data. Therefore the loop
+ recommended in idr_get_new() documentation is not necessary. */
+ if (idr_pre_get(&lst->key_idr, GFP_KERNEL) == 0 ||
+ idr_get_new(&lst->key_idr, key, &key->desc) != 0) {
+ mutex_unlock(&lst->key_idr_mutex);
+ _ncr_key_item_put(key);
+ return -ENOMEM;
+ }
+ desc = key->desc;
+ mutex_unlock(&lst->key_idr_mutex);
+
+ return desc;
+
+err_limits:
+ ncr_limits_remove(current_euid(), task_pid_nr(current), LIMIT_TYPE_KEY);
+ return ret;
+}
+
+int ncr_key_deinit(struct ncr_lists *lst, ncr_key_t desc)
+{
+ _ncr_key_remove(lst, desc);
+ return 0;
+}
+
+int ncr_key_export(struct ncr_lists *lst, const struct ncr_key_export *data,
+ struct nlattr *tb[])
+{
+struct key_item_st* item = NULL;
+void* tmp = NULL;
+uint32_t tmp_size;
+int ret;
+
+ if (data->buffer_size < 0) {
+ err();
+ return -EINVAL;
+ }
+
+ ret = ncr_key_item_get_read(&item, lst, data->key);
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+
+ if (!(item->flags & NCR_KEY_FLAG_EXPORTABLE)) {
+ err();
+ ret = -EPERM;
+ goto fail;
+ }
+
+ switch (item->type) {
+ case NCR_KEY_TYPE_SECRET:
+ if (item->key.secret.size > data->buffer_size) {
+ err();
+ ret = -ERANGE;
+ goto fail;
+ }
+
+ /* found */
+ if (item->key.secret.size > 0) {
+ ret = copy_to_user(data->buffer, item->key.secret.data, item->key.secret.size);
+ if (unlikely(ret)) {
+ err();
+ ret = -EFAULT;
+ goto fail;
+ }
+ }
+
+ ret = item->key.secret.size;
+ break;
+ case NCR_KEY_TYPE_PUBLIC:
+ case NCR_KEY_TYPE_PRIVATE:
+ tmp_size = data->buffer_size;
+
+ tmp = kmalloc(tmp_size, GFP_KERNEL);
+ if (tmp == NULL) {
+ err();
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ ret = ncr_pk_pack(item, tmp, &tmp_size);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ ret = copy_to_user(data->buffer, tmp, tmp_size);
+ if (unlikely(ret)) {
+ err();
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ ret = tmp_size;
+ break;
+ default:
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+fail:
+ audit_log_crypto_op(AUDIT_CRYPTO_OP_KEY_EXPORT, lst->id, -1, NULL,
+ ncr_algorithm_name(item->algorithm), item->desc,
+ item->key_id, item->key_id_size, -1, NULL, 0);
+
+ kfree(tmp);
+ _ncr_key_item_put(item);
+ return ret;
+
+}
+
+int ncr_key_update_flags(struct key_item_st* item, const struct nlattr *nla)
+{
+ uint32_t flags;
+
+ if (nla == NULL)
+ return 0;
+ flags = nla_get_u32(nla);
+ if (!capable(CAP_SYS_ADMIN)
+ && (flags & (NCR_KEY_FLAG_WRAPPING | NCR_KEY_FLAG_UNWRAPPING)) != 0)
+ return -EPERM;
+ item->flags = flags;
+ return 0;
+}
+
+int ncr_key_import(struct ncr_lists *lst, const struct ncr_key_import *data,
+ struct nlattr *tb[])
+{
+const struct nlattr *nla;
+struct key_item_st* item = NULL;
+int ret;
+void* tmp = NULL;
+size_t tmp_size;
+
+ ret = ncr_key_item_get_write( &item, lst, data->key);
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+
+ ncr_key_clear(item);
+
+ tmp = kmalloc(data->data_size, GFP_KERNEL);
+ if (tmp == NULL) {
+ err();
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ if (unlikely(copy_from_user(tmp, data->data, data->data_size))) {
+ err();
+ ret = -EFAULT;
+ goto fail;
+ }
+ tmp_size = data->data_size;
+
+ nla = tb[NCR_ATTR_KEY_TYPE];
+ if (tb == NULL) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+ item->type = nla_get_u32(nla);
+
+ item->algorithm = _ncr_nla_to_properties(tb[NCR_ATTR_ALGORITHM]);
+ if (item->algorithm == NULL) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ ret = ncr_key_update_flags(item, tb[NCR_ATTR_KEY_FLAGS]);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ nla = tb[NCR_ATTR_KEY_ID];
+ if (nla != NULL) {
+ if (nla_len(nla) > MAX_KEY_ID_SIZE) {
+ err();
+ ret = -EOVERFLOW;
+ goto fail;
+ }
+
+ item->key_id_size = nla_len(nla);
+ memcpy(item->key_id, nla_data(nla), item->key_id_size);
+ }
+
+ switch(item->type) {
+ case NCR_KEY_TYPE_SECRET:
+ if (tmp_size > NCR_CIPHER_MAX_KEY_LEN) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ memcpy(item->key.secret.data, tmp, tmp_size);
+ item->key.secret.size = tmp_size;
+ break;
+ case NCR_KEY_TYPE_PRIVATE:
+ case NCR_KEY_TYPE_PUBLIC:
+ ret = ncr_pk_unpack( item, tmp, tmp_size);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+ break;
+
+ default:
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ ret = 0;
+
+fail:
+ audit_log_crypto_op(AUDIT_CRYPTO_OP_KEY_IMPORT, lst->id, -1, NULL,
+ ncr_algorithm_name(item->algorithm), item->desc,
+ item->key_id, item->key_id_size, -1, NULL, 0);
+
+ _ncr_key_item_put(item);
+ kfree(tmp);
+
+ return ret;
+}
+
+void ncr_key_clear(struct key_item_st* item)
+{
+ audit_log_crypto_op(AUDIT_CRYPTO_OP_KEY_ZEROIZE, item->context_id, -1,
+ NULL, ncr_algorithm_name(item->algorithm),
+ item->desc, item->key_id, item->key_id_size, -1,
+ NULL, 0);
+ if (item->type == NCR_KEY_TYPE_PRIVATE ||
+ item->type == NCR_KEY_TYPE_PUBLIC)
+ ncr_pk_audit_values(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));
+ memset(item->key_id, 0, sizeof(item->key_id));
+ item->key_id_size = 0;
+ item->flags = 0;
+
+ return;
+}
+
+/* Generate a secret key
+ */
+int ncr_key_generate(struct ncr_lists *lst, const struct ncr_key_generate *gen,
+ struct nlattr *tb[])
+{
+const struct nlattr *nla;
+struct key_item_st* item = NULL;
+const struct algo_properties_st *algo = NULL;
+int ret;
+size_t size;
+
+ ret = ncr_key_item_get_write(&item, lst, gen->key);
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+
+ ncr_key_clear(item);
+
+ /* we generate only secret keys */
+ ret = ncr_key_update_flags(item, tb[NCR_ATTR_KEY_FLAGS]);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ algo = _ncr_nla_to_properties(tb[NCR_ATTR_ALGORITHM]);
+ if (algo == NULL) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+ item->type = algo->key_type;
+ if (item->type == NCR_KEY_TYPE_SECRET) {
+ u32 key_bits;
+
+ item->algorithm = algo;
+
+ nla = tb[NCR_ATTR_SECRET_KEY_BITS];
+ if (nla == NULL) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+ key_bits = nla_get_u32(nla);
+ size = key_bits / 8;
+ if (key_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:
+ audit_log_crypto_op(AUDIT_CRYPTO_OP_KEY_GEN, lst->id, -1, NULL,
+ ncr_algorithm_name(algo), item->desc, item->key_id,
+ item->key_id_size, -1, NULL, 0);
+ if (item->type == NCR_KEY_TYPE_PUBLIC
+ || item->type == NCR_KEY_TYPE_PRIVATE)
+ ncr_pk_audit_values(item);
+
+ if (ret < 0) item->type = NCR_KEY_TYPE_INVALID;
+ _ncr_key_item_put(item);
+ return ret;
+}
+
+/* Those values are derived from "ECRYPT II Yearly Report on Algorithms and
+ * Keysizes (2009-2010)". It maps the strength of public key algorithms to
+ * symmetric ones. Should be kept up to date.
+ */
+static const struct {
+ unsigned int bits; /* sec level */
+ unsigned int rsa_bits;
+ unsigned int dlog_bits;
+} ecrypt_vals[] = {
+ {64, 816, 816},
+ {80, 1248, 1248},
+ {112, 2432, 2432},
+ {128, 3248, 3248},
+ {160, 5312, 5312},
+ {192, 7936, 7936},
+ {256, 15424, 15424},
+ {0,0,0}
+};
+
+static unsigned int rsa_to_bits(unsigned int rsa_bits)
+{
+int i = 1;
+
+ if (rsa_bits <= ecrypt_vals[0].rsa_bits)
+ return ecrypt_vals[0].rsa_bits;
+
+ do {
+ if (rsa_bits <= ecrypt_vals[i].rsa_bits &&
+ rsa_bits > ecrypt_vals[i-1].rsa_bits) {
+
+ return ecrypt_vals[i].bits;
+ }
+ } while(ecrypt_vals[++i].bits != 0);
+
+ /* return the highest found so far */
+ return ecrypt_vals[i-1].bits;
+}
+
+static unsigned int dlog_to_bits(unsigned int dlog_bits)
+{
+int i = 1;
+
+ if (dlog_bits <= ecrypt_vals[0].dlog_bits)
+ return ecrypt_vals[0].dlog_bits;
+
+ do {
+ if (dlog_bits <= ecrypt_vals[i].dlog_bits &&
+ dlog_bits > ecrypt_vals[i-1].dlog_bits) {
+
+ return ecrypt_vals[i].bits;
+ }
+ } while(ecrypt_vals[++i].bits != 0);
+
+ /* return the highest found so far */
+ return ecrypt_vals[i-1].bits;
+}
+
+/* returns the security level of the key in bits. Private/Public keys
+ * are mapped to symmetric key bits using the ECRYPT II 2010 recommendation.
+ */
+int _ncr_key_get_sec_level(struct key_item_st* item)
+{
+int bits;
+
+ /* FIXME: should we move everything here into algorithm properties?
+ */
+ if (item->type == NCR_KEY_TYPE_SECRET) {
+ if (item->algorithm->algo == NCR_ALG_3DES_CBC || item->algorithm->algo == NCR_ALG_3DES_ECB)
+ return 112;
+
+ return item->key.secret.size*8;
+ } else if (item->type == NCR_KEY_TYPE_PRIVATE) {
+ switch(item->algorithm->algo) {
+ case NCR_ALG_RSA:
+ bits = ncr_pk_get_rsa_size(&item->key.pk.rsa);
+ if (bits < 0) {
+ err();
+ return bits;
+ }
+
+ return rsa_to_bits(bits);
+ case NCR_ALG_DSA:
+ bits = ncr_pk_get_dsa_size(&item->key.pk.dsa);
+ if (bits < 0) {
+ err();
+ return bits;
+ }
+
+ return dlog_to_bits(bits);
+ case NCR_ALG_DH:
+ bits = ncr_pk_get_dh_size(&item->key.pk.dh);
+ if (bits < 0) {
+ err();
+ return bits;
+ }
+
+ return dlog_to_bits(bits);
+ default:
+ return -EINVAL;
+ }
+ } else {
+ return -EINVAL;
+ }
+}
+
+int ncr_key_get_info(struct ncr_lists *lst, struct ncr_out *out,
+ const struct ncr_key_get_info *info, struct nlattr *tb[])
+{
+const struct nlattr *nla;
+const u16 *attr, *attr_end;
+struct key_item_st* item = NULL;
+int ret;
+
+ ret = ncr_key_item_get_read(&item, lst, info->key);
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+
+ if (item->type == NCR_KEY_TYPE_INVALID) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ nla = tb[NCR_ATTR_WANTED_ATTRS];
+ if (nla == NULL || nla_len(nla) % sizeof(u16) != 0) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+ attr = nla_data(nla);
+ attr_end = attr + nla_len(nla) / sizeof(u16);
+ while (attr < attr_end) {
+ switch (*attr) {
+ case NCR_ATTR_KEY_FLAGS:
+ ret = ncr_out_put_u32(out, *attr, item->flags);
+ break;
+ case NCR_ATTR_KEY_TYPE:
+ ret = ncr_out_put_u32(out, *attr, item->type);
+ break;
+ case NCR_ATTR_ALGORITHM:
+ ret = ncr_out_put_string(out, *attr,
+ item->algorithm->kstr);
+ break;
+ default:
+ break; /* Silently ignore */
+ }
+ if (ret != 0) {
+ err();
+ goto fail;
+ }
+ attr++;
+ }
+
+ ret = ncr_out_finish(out);
+ if (ret != 0) {
+ err();
+ goto fail;
+ }
+
+fail:
+ audit_log_crypto_op(AUDIT_CRYPTO_OP_KEY_GET_INFO, lst->id, -1, NULL,
+ ncr_algorithm_name(item->algorithm), item->desc,
+ item->key_id, item->key_id_size, -1, NULL, 0);
+
+ _ncr_key_item_put( item);
+
+ return ret;
+}
+
+int ncr_key_generate_pair(struct ncr_lists *lst,
+ const struct ncr_key_generate_pair *gen,
+ struct nlattr *tb[])
+{
+struct key_item_st* private = NULL;
+struct key_item_st* public = NULL;
+int ret;
+
+ ret = ncr_key_item_get_write(&private, lst, gen->private_key);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ ret = ncr_key_item_get_write(&public, lst, gen->public_key);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ ncr_key_clear(public);
+ ncr_key_clear(private);
+
+ /* we generate only secret keys */
+ private->algorithm = public->algorithm
+ = _ncr_nla_to_properties(tb[NCR_ATTR_ALGORITHM]);
+ if (private->algorithm == NULL) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+ public->type = public->algorithm->key_type;
+ private->type = NCR_KEY_TYPE_PRIVATE;
+ ret = ncr_key_update_flags(private, tb[NCR_ATTR_KEY_FLAGS]);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+ ret = ncr_key_update_flags(public, tb[NCR_ATTR_KEY_FLAGS]);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ public->flags |= (NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPABLE);
+
+ if (public->type == NCR_KEY_TYPE_PUBLIC) {
+ ret = ncr_pk_generate(public->algorithm, tb, private, public);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+ } else {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ ret = 0;
+fail:
+ audit_log_crypto_op(AUDIT_CRYPTO_OP_KEY_GEN, lst->id, -1, NULL,
+ ncr_algorithm_name(private != NULL
+ ? private->algorithm : NULL),
+ private != NULL ? private->desc : -1,
+ private != NULL ? private->key_id : NULL,
+ private != NULL ? private->key_id_size : 0,
+ public != NULL ? public->desc : -1,
+ public != NULL ? public->key_id : NULL,
+ public != NULL ? public->key_id_size : 0);
+ if (public != NULL && ret >= 0)
+ ncr_pk_audit_values(public);
+
+ 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 ncr_lists *lst, const struct ncr_key_derive *data,
+ struct nlattr *tb[])
+{
+int ret;
+struct key_item_st* key = NULL;
+struct key_item_st* newkey = NULL;
+
+ ret = ncr_key_item_get_read(&key, lst, data->input_key);
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+
+ /* wrapping keys cannot be used for anything except wrapping.
+ */
+ if (key->flags & NCR_KEY_FLAG_WRAPPING || key->flags & NCR_KEY_FLAG_UNWRAPPING) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ ret = ncr_key_item_get_write(&newkey, lst, data->new_key);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ ncr_key_clear(newkey);
+
+ ret = ncr_key_update_flags(newkey, tb[NCR_ATTR_KEY_FLAGS]);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ switch (key->type) {
+ case NCR_KEY_TYPE_PUBLIC:
+ case NCR_KEY_TYPE_PRIVATE:
+ ret = ncr_pk_derive(newkey, key, tb);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+ break;
+ default:
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+fail:
+ audit_log_crypto_op(AUDIT_CRYPTO_OP_KEY_DERIVE, lst->id, -1, NULL,
+ ncr_algorithm_name(key->algorithm), key->desc,
+ key->key_id, key->key_id_size, data->input_key,
+ newkey != NULL ? newkey->key_id : NULL,
+ newkey != NULL ? newkey->key_id_size : 0);
+
+ _ncr_key_item_put(key);
+ if (newkey)
+ _ncr_key_item_put(newkey);
+ return ret;
+
+}
+