summaryrefslogtreecommitdiffstats
path: root/crypto/userspace/ncr-key-wrap.c
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/userspace/ncr-key-wrap.c')
-rw-r--r--crypto/userspace/ncr-key-wrap.c1118
1 files changed, 1118 insertions, 0 deletions
diff --git a/crypto/userspace/ncr-key-wrap.c b/crypto/userspace/ncr-key-wrap.c
new file mode 100644
index 00000000000..ce061312dc0
--- /dev/null
+++ b/crypto/userspace/ncr-key-wrap.c
@@ -0,0 +1,1118 @@
+/*
+ * 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/highmem.h>
+#include <linux/random.h>
+#include <linux/uaccess.h>
+#include <linux/scatterlist.h>
+#include <net/netlink.h>
+#include "ncr-int.h"
+#include "cryptodev_int.h"
+
+#define KEY_WRAP_VERSION 0
+
+/* To be further checked. If the current implemented key wrapping mechanism
+ * has no issues, it might be possible to relax the requirement for
+ * privileged key wrapping.
+ */
+#define KEY_WRAP_IS_PRIVILEGED
+
+typedef uint8_t val64_t[8];
+
+static const val64_t initA = "\xA6\xA6\xA6\xA6\xA6\xA6\xA6\xA6";
+
+static int key_to_packed_data( uint8_t** sdata, size_t * sdata_size, const struct key_item_st *key);
+static int key_from_packed_data(struct nlattr *tb[], struct key_item_st* key,
+ const void* data, size_t data_size);
+
+
+static void val64_xor( val64_t val, uint32_t x)
+{
+ val[7] ^= x & 0xff;
+ val[6] ^= (x >> 8) & 0xff;
+ val[5] ^= (x >> 16) & 0xff;
+ val[4] ^= (x >> 24) & 0xff;
+}
+
+static int rfc3394_wrap(val64_t *R, unsigned int n, struct cipher_data* ctx,
+ uint8_t* output, size_t *output_size, const uint8_t iv[8])
+{
+val64_t A;
+uint8_t aes_block[16];
+int i,j;
+
+ if (*output_size < (n+1)*8) {
+ err();
+ return -ERANGE;
+ }
+
+ memcpy(A, iv, 8);
+
+ for (i=0;i<6*n;i++) {
+ memcpy(aes_block, A, 8);
+ memcpy(&aes_block[8], R[0], 8);
+
+ _cryptodev_cipher_encrypt(ctx, aes_block, sizeof(aes_block),
+ aes_block, sizeof(aes_block));
+
+ memcpy(A, aes_block, 8); /* A = MSB64(AES(A^{t-1}|R_{1}^{t-1})) */
+ val64_xor(A, i+1); /* A ^= t */
+
+ for (j=0;j<n-1;j++)
+ memcpy(R[j], R[j+1], sizeof(R[j]));
+ memcpy(R[n-1], &aes_block[8], 8); /* R[n-1] = LSB64(AES(A^{t-1}|R_{1}^{t-1})) */
+ }
+
+ memcpy(output, A, sizeof(A));
+ for (j=0;j<n;j++)
+ memcpy(&output[(j+1)*8], R[j], 8);
+ *output_size = (n+1)*8;
+
+ return 0;
+}
+
+static int rfc3394_unwrap(const uint8_t *wrapped_key, val64_t R[], unsigned int n, val64_t A, struct cipher_data *ctx)
+{
+ int i, j;
+ uint8_t aes_block[16];
+
+ memcpy(A, wrapped_key, 8); /* A = C[0] */
+ for (i=0;i<n;i++)
+ memcpy(R[i], &wrapped_key[(i+1)*8], 8);
+
+ for (i=(6*n)-1;i>=0;i--) {
+ val64_xor(A, i+1);
+
+ memcpy(aes_block, A, 8);
+ memcpy(&aes_block[8], R[n-1], 8);
+
+ _cryptodev_cipher_decrypt(ctx, aes_block, sizeof(aes_block),
+ aes_block, sizeof(aes_block));
+
+ memcpy(A, aes_block, 8);
+
+ for (j=n-1;j>=1;j--)
+ memcpy(R[j], R[j-1], sizeof(R[j]));
+
+ memcpy(R[0], &aes_block[8], 8);
+ }
+
+ return 0;
+}
+
+#define RFC5649_IV "\xA6\x59\x59\xA6"
+static int _wrap_aes_rfc5649(void* kdata, size_t kdata_size, struct key_item_st* kek,
+ void* output, size_t* output_size, const void* _iv, size_t iv_size)
+{
+size_t n;
+int i, ret;
+struct cipher_data ctx;
+uint8_t iv[8];
+val64_t *R = NULL;
+
+ if (iv_size != 4) {
+ memcpy(iv, RFC5649_IV, 4);
+ } else {
+ memcpy(iv, _iv, 4);
+ }
+ iv_size = 8;
+ iv[4] = (kdata_size >> 24) & 0xff;
+ iv[5] = (kdata_size >> 16) & 0xff;
+ iv[6] = (kdata_size >> 8) & 0xff;
+ iv[7] = (kdata_size) & 0xff;
+
+ n = (kdata_size+7)/8;
+ if (n==1) { /* unimplemented */
+ err();
+ return -EINVAL;
+ }
+
+ ret = cryptodev_cipher_init(&ctx, "ecb(aes)", kek->key.secret.data, kek->key.secret.size);
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+
+ R = kmalloc(n * sizeof (*R), GFP_KERNEL);
+ if (R == NULL) {
+ err();
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ /* R = P */
+ for (i=0;i<kdata_size;i++) {
+ R[i/8][i%8] = ((uint8_t*)kdata)[i];
+ }
+
+ for (;i<n*8;i++) {
+ R[i/8][i%8] = 0;
+ }
+
+ ret = rfc3394_wrap( R, n, &ctx, output, output_size, iv);
+ if (ret < 0) {
+ err();
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ kfree(R);
+ cryptodev_cipher_deinit(&ctx);
+
+ return ret;
+}
+
+static int _unwrap_aes_rfc5649(void* kdata, size_t *kdata_size, struct key_item_st* kek,
+ const void *wrapped_key, size_t wrapped_key_size, const void* _iv, size_t iv_size)
+{
+size_t n;
+int i, ret;
+struct cipher_data ctx;
+uint8_t iv[4];
+size_t size;
+val64_t *R = NULL, A;
+
+ if (iv_size != 4) {
+ memcpy(iv, RFC5649_IV, 4);
+ } else {
+ memcpy(iv, _iv, 4);
+ }
+ iv_size = 4;
+
+ ret = cryptodev_cipher_init(&ctx, "ecb(aes)", kek->key.secret.data, kek->key.secret.size);
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+
+ if (wrapped_key_size % 8 != 0) {
+ err();
+ ret = -EINVAL;
+ goto cleanup;
+ }
+
+ n = wrapped_key_size/8 - 1;
+
+ if (*kdata_size < (n-1)*8) {
+ err();
+ ret = -EINVAL;
+ goto cleanup;
+ }
+
+ R = kmalloc(n * sizeof (*R), GFP_KERNEL);
+ if (R == NULL) {
+ err();
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ ret = rfc3394_unwrap(wrapped_key, R, n, A, &ctx);
+ if (ret < 0) {
+ err();
+ goto cleanup;
+ }
+
+ if (memcmp(A, iv, 4)!= 0) {
+ err();
+ ret = -EINVAL;
+ goto cleanup;
+ }
+
+ size = (A[4] << 24) | (A[5] << 16) | (A[6] << 8) | A[7];
+ if (size > n*8 || size < (n-1)*8 || *kdata_size < size) {
+ err();
+ ret = -EINVAL;
+ goto cleanup;
+ }
+
+ memset(kdata, 0, size);
+ *kdata_size = size;
+ for (i=0;i<size;i++) {
+ ((uint8_t*)kdata)[i] = R[i/8][i%8];
+ }
+
+ ret = 0;
+
+cleanup:
+ kfree(R);
+ cryptodev_cipher_deinit(&ctx);
+
+ return ret;
+}
+
+
+static int wrap_aes_rfc5649(struct key_item_st* tobewrapped, struct key_item_st *kek,
+ void* output, size_t* output_size, const void* iv, size_t iv_size)
+{
+int ret;
+uint8_t* sdata = NULL;
+size_t sdata_size = 0;
+
+ ret = key_to_packed_data(&sdata, &sdata_size, tobewrapped);
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+
+ ret = _wrap_aes_rfc5649(sdata, sdata_size,
+ kek, output, output_size, iv, iv_size);
+
+ kfree(sdata);
+
+ return ret;
+}
+
+static int unwrap_aes_rfc5649(struct key_item_st* output, struct key_item_st *kek,
+ void *wrapped, size_t wrapped_size, struct nlattr *tb[])
+{
+const struct nlattr *nla;
+int ret, iv_size;
+void * sdata;
+size_t sdata_size = KEY_DATA_MAX_SIZE;
+const uint8_t *iv;
+
+ sdata = kmalloc(sdata_size, GFP_KERNEL);
+ if (sdata == NULL) {
+ err();
+ return -ENOMEM;
+ }
+
+ nla = tb[NCR_ATTR_IV];
+ if (nla != NULL) {
+ iv = nla_data(nla);
+ iv_size = nla_len(nla);
+ } else {
+ iv = NULL;
+ iv_size = 0;
+ }
+
+ ret = _unwrap_aes_rfc5649(sdata, &sdata_size, kek,
+ wrapped, wrapped_size, iv, iv_size);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ ret = key_from_packed_data(tb, output, sdata, sdata_size);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ ret = 0;
+
+fail:
+ kfree(sdata);
+ return ret;
+
+}
+
+
+/* Wraps using the RFC3394 way.
+ */
+static int wrap_aes_rfc3394(struct key_item_st* tobewrapped, struct key_item_st *kek,
+ void* output, size_t *output_size, const void* iv, size_t iv_size)
+{
+size_t key_size, n;
+uint8_t *raw_key;
+int i, ret;
+struct cipher_data ctx;
+val64_t *R = NULL;
+
+ if (tobewrapped->type != NCR_KEY_TYPE_SECRET) {
+ err();
+ return -EINVAL;
+ }
+
+ if (iv_size < sizeof(initA)) {
+ iv_size = sizeof(initA);
+ iv = initA;
+ }
+
+ ret = cryptodev_cipher_init(&ctx, "ecb(aes)", kek->key.secret.data, kek->key.secret.size);
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+
+ raw_key = tobewrapped->key.secret.data;
+ key_size = tobewrapped->key.secret.size;
+
+ if (key_size % 8 != 0) {
+ err();
+ ret = -EINVAL;
+ goto cleanup;
+ }
+
+ n = key_size/8;
+
+
+ R = kmalloc(sizeof(*R)*n, GFP_KERNEL);
+ if (R == NULL) {
+ err();
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ /* R = P */
+ for (i=0;i<n;i++) {
+ memcpy(R[i], &raw_key[i*8], 8);
+ }
+
+ ret = rfc3394_wrap( R, n, &ctx, output, output_size, iv);
+ if (ret < 0) {
+ err();
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ kfree(R);
+ cryptodev_cipher_deinit(&ctx);
+
+ return ret;
+}
+
+#if 0
+/* for debugging */
+void print_val64(char* str, val64_t val)
+{
+ int i;
+ printk("%s: ",str);
+ for (i=0;i<8;i++)
+ printk("%.2x", val[i]);
+ printk("\n");
+
+}
+#endif
+
+static int unwrap_aes_rfc3394(struct key_item_st* output, struct key_item_st *kek,
+ void* wrapped_key, size_t wrapped_key_size,
+ struct nlattr *tb[])
+{
+const struct nlattr *nla;
+size_t n;
+val64_t A;
+int i, ret;
+struct cipher_data ctx;
+val64_t * R = NULL;
+int iv_size;
+const uint8_t *iv;
+
+ nla = tb[NCR_ATTR_IV];
+ if (nla != NULL) {
+ iv = nla_data(nla);
+ iv_size = nla_len(nla);
+ } else
+ iv_size = 0;
+ if (iv_size < sizeof(initA)) {
+ iv_size = sizeof(initA);
+ iv = initA;
+ }
+
+ ret = cryptodev_cipher_init(&ctx, "ecb(aes)", kek->key.secret.data, kek->key.secret.size);
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+
+ output->type = NCR_KEY_TYPE_SECRET;
+
+ if (wrapped_key_size % 8 != 0) {
+ err();
+ ret = -EINVAL;
+ goto cleanup;
+ }
+
+ n = wrapped_key_size/8 - 1;
+
+ if (NCR_CIPHER_MAX_KEY_LEN < (n-1)*8) {
+ err();
+ ret = -EINVAL;
+ goto cleanup;
+ }
+
+ R = kmalloc(sizeof(*R)*n, GFP_KERNEL);
+ if (R == NULL) {
+ err();
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ ret = rfc3394_unwrap(wrapped_key, R, n, A, &ctx);
+ if (ret < 0) {
+ err();
+ goto cleanup;
+ }
+
+ if (memcmp(A, iv, 8)!= 0) {
+ err();
+ ret = -EINVAL;
+ goto cleanup;
+ }
+
+ ret = ncr_key_update_flags(output, tb[NCR_ATTR_KEY_FLAGS]);
+ if (ret != 0) {
+ err();
+ goto cleanup;
+ }
+
+ memset(&output->key, 0, sizeof(output->key));
+ for (i=0;i<n;i++) {
+ memcpy(&output->key.secret.data[i*8], R[i], sizeof(R[i]));
+ }
+ output->key.secret.size = n*8;
+ output->type = NCR_KEY_TYPE_SECRET;
+
+ ret = 0;
+
+cleanup:
+ kfree(R);
+ cryptodev_cipher_deinit(&ctx);
+
+ return ret;
+}
+
+/* will check if the kek is of equal or higher security level than
+ * wkey. To prevent encrypting a 256 bit key with an 128 bit one.
+ */
+static int check_key_level(struct key_item_st* kek, struct key_item_st* wkey)
+{
+int kek_level, wkey_level;
+
+ /* allow wrapping of public keys with any key */
+ if (wkey->type == NCR_KEY_TYPE_PUBLIC)
+ return 0;
+
+ kek_level = _ncr_key_get_sec_level(kek);
+ if (kek_level < 0) {
+ err();
+ return kek_level;
+ }
+
+ wkey_level = _ncr_key_get_sec_level(wkey);
+ if (wkey_level < 0) {
+ err();
+ return wkey_level;
+ }
+
+ if (wkey_level > kek_level) {
+ err();
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+static const char *ncr_wrap_name(struct nlattr *tb[])
+{
+ static const char *const known_algorithms[] = {
+ NCR_WALG_AES_RFC3394, NCR_WALG_AES_RFC5649
+ };
+
+ size_t i;
+ const struct nlattr *nla;
+
+ /* Only allow known algorithms to prevent log injection. Return the
+ static string, not nla_data(), which will go away before the ioctl()
+ handler returns. */
+ nla = tb[NCR_ATTR_WRAPPING_ALGORITHM];
+ for (i = 0; i < ARRAY_SIZE(known_algorithms); i++) {
+ if (nla_strcmp(nla, known_algorithms[i]) == 0)
+ return known_algorithms[i];
+ }
+ return "unknown";
+}
+
+int ncr_key_wrap(struct ncr_lists *lst, const struct ncr_key_wrap *wrap,
+ struct nlattr *tb[])
+{
+const struct nlattr *nla;
+struct key_item_st* wkey = NULL;
+struct key_item_st* key = NULL;
+void* data = NULL;
+const void *iv;
+size_t data_size, iv_size;
+int ret;
+
+#ifdef KEY_WRAP_IS_PRIVILEGED
+ if (current_euid() != 0) {
+ err();
+ return -EPERM;
+ }
+#endif
+
+ if (wrap->buffer_size < 0) {
+ err();
+ return -EINVAL;
+ }
+
+ ret = ncr_key_item_get_read(&wkey, lst, wrap->source_key);
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+
+ if (!(wkey->flags & NCR_KEY_FLAG_WRAPPABLE)) {
+ err();
+ ret = -EPERM;
+ goto fail;
+ }
+
+ ret = ncr_key_item_get_read(&key, lst, wrap->wrapping_key);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ if (!(key->flags & NCR_KEY_FLAG_WRAPPING)) {
+ err();
+ ret = -EPERM;
+ goto fail;
+ }
+
+ ret = check_key_level(key, wkey);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ data_size = wrap->buffer_size;
+ data = kmalloc(data_size, GFP_KERNEL);
+ if (data == NULL) {
+ err();
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ nla = tb[NCR_ATTR_IV];
+ if (nla != NULL) {
+ iv = nla_data(nla);
+ iv_size = nla_len(nla);
+ } else {
+ iv = NULL;
+ iv_size = 0;
+ }
+
+ nla = tb[NCR_ATTR_WRAPPING_ALGORITHM];
+ if (nla == NULL) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+ if (nla_strcmp(nla, NCR_WALG_AES_RFC3394) == 0)
+ ret = wrap_aes_rfc3394(wkey, key, data, &data_size, iv,
+ iv_size);
+ else if (nla_strcmp(nla, NCR_WALG_AES_RFC5649) == 0)
+ ret = wrap_aes_rfc5649(wkey, key, data, &data_size, iv,
+ iv_size);
+ else {
+ err();
+ ret = -EINVAL;
+ }
+
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ ret = copy_to_user(wrap->buffer, data, data_size);
+ if (unlikely(ret)) {
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ ret = data_size;
+
+fail:
+ audit_log_crypto_op(AUDIT_CRYPTO_OP_KEY_WRAP, lst->id, -1, NULL,
+ ncr_wrap_name(tb), wrap->wrapping_key,
+ key != NULL ? key->key_id : NULL,
+ key != NULL ? key->key_id_size : 0,
+ wrap->source_key,
+ wkey != NULL ? wkey->key_id : NULL,
+ wkey != NULL ? wkey->key_id_size : 0);
+
+ if (wkey != NULL) _ncr_key_item_put(wkey);
+ if (key != NULL) _ncr_key_item_put(key);
+ kfree(data);
+
+ return ret;
+}
+
+/* Unwraps keys. All keys unwrapped are not accessible by
+ * userspace.
+ */
+int ncr_key_unwrap(struct ncr_lists *lst, const struct ncr_key_unwrap *wrap,
+ struct nlattr *tb[])
+{
+const struct nlattr *nla;
+struct key_item_st* wkey = NULL;
+struct key_item_st* key = NULL;
+void* data = NULL;
+size_t data_size;
+int ret;
+
+#ifdef KEY_WRAP_IS_PRIVILEGED
+ if (current_euid() != 0) {
+ err();
+ return -EPERM;
+ }
+#endif
+
+ ret = ncr_key_item_get_write(&wkey, lst, wrap->dest_key);
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+
+ ret = ncr_key_item_get_read(&key, lst, wrap->wrapping_key);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ if (!(key->flags & NCR_KEY_FLAG_UNWRAPPING)) {
+ err();
+ ret = -EPERM;
+ goto fail;
+ }
+
+ data_size = wrap->data_size;
+ data = kmalloc(data_size, GFP_KERNEL);
+ if (data == NULL) {
+ err();
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ if (unlikely(copy_from_user(data, wrap->data, data_size))) {
+ err();
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ ncr_key_clear(wkey);
+
+ nla = tb[NCR_ATTR_WRAPPING_ALGORITHM];
+ if (nla == NULL) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+ if (nla_strcmp(nla, NCR_WALG_AES_RFC3394) == 0)
+ ret = unwrap_aes_rfc3394(wkey, key, data, data_size, tb);
+ else if (nla_strcmp(nla, NCR_WALG_AES_RFC5649) == 0)
+ ret = unwrap_aes_rfc5649(wkey, key, data, data_size, tb);
+ else {
+ err();
+ ret = -EINVAL;
+ }
+
+fail:
+ audit_log_crypto_op(AUDIT_CRYPTO_OP_KEY_UNWRAP, lst->id, -1, NULL,
+ ncr_wrap_name(tb), wrap->wrapping_key,
+ key != NULL ? key->key_id : NULL,
+ key != NULL ? key->key_id_size : 0, wrap->dest_key,
+ wkey != NULL ? wkey->key_id : NULL,
+ wkey != NULL ? wkey->key_id_size : 0);
+
+ if (wkey != NULL) _ncr_key_item_put(wkey);
+ if (key != NULL) _ncr_key_item_put(key);
+ if (data != NULL) kfree(data);
+
+ return ret;
+}
+
+int ncr_key_storage_wrap(struct ncr_lists *lst,
+ const struct ncr_key_storage_wrap *wrap,
+ struct nlattr *tb[])
+{
+struct key_item_st* wkey = NULL;
+void* data = NULL;
+size_t data_size;
+uint8_t * sdata = NULL;
+size_t sdata_size = 0;
+int ret;
+
+ if (master_key.type != NCR_KEY_TYPE_SECRET) {
+ err();
+ return -ENOKEY;
+ }
+
+ if (wrap->buffer_size < 0) {
+ err();
+ return -EINVAL;
+ }
+
+ ret = ncr_key_item_get_read(&wkey, lst, wrap->key);
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+
+ data_size = wrap->buffer_size;
+ data = kmalloc(data_size, GFP_KERNEL);
+ if (data == NULL) {
+ err();
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ ret = key_to_storage_data(&sdata, &sdata_size, wkey);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ ret = _wrap_aes_rfc5649(sdata, sdata_size, &master_key, data, &data_size, NULL, 0);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ ret = copy_to_user(wrap->buffer, data, data_size);
+ if (unlikely(ret)) {
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ ret = data_size;
+
+fail:
+ audit_log_crypto_op(AUDIT_CRYPTO_OP_KEY_WRAP, lst->id, -1, NULL, NULL,
+ -1, NULL, 0, wrap->key,
+ wkey != NULL ? wkey->key_id : NULL,
+ wkey != NULL ? wkey->key_id_size : 0);
+
+ if (wkey != NULL) _ncr_key_item_put(wkey);
+ if (data != NULL) kfree(data);
+ if (sdata != NULL) kfree(sdata);
+
+ return ret;
+}
+
+int ncr_key_storage_unwrap(struct ncr_lists *lst,
+ const struct ncr_key_storage_unwrap *wrap,
+ struct nlattr *tb[])
+{
+struct key_item_st* wkey = NULL;
+void* data = NULL;
+uint8_t * sdata = NULL;
+size_t sdata_size = 0, data_size;
+int ret;
+
+ if (master_key.type != NCR_KEY_TYPE_SECRET) {
+ err();
+ return -ENOKEY;
+ }
+
+ ret = ncr_key_item_get_write(&wkey, lst, wrap->key);
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+
+ data_size = wrap->data_size;
+ data = kmalloc(data_size, GFP_KERNEL);
+ if (data == NULL) {
+ err();
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ if (unlikely(copy_from_user(data, wrap->data, data_size))) {
+ err();
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ sdata_size = data_size;
+ sdata = kmalloc(sdata_size, GFP_KERNEL);
+ if (sdata == NULL) {
+ err();
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ ret = _unwrap_aes_rfc5649(sdata, &sdata_size, &master_key, data, data_size, NULL, 0);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ ncr_key_clear(wkey);
+
+ ret = key_from_storage_data(wkey, sdata, sdata_size);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+
+fail:
+ audit_log_crypto_op(AUDIT_CRYPTO_OP_KEY_UNWRAP, lst->id, -1, NULL, NULL,
+ -1, NULL, 0, wrap->key,
+ wkey != NULL ? wkey->key_id : NULL,
+ wkey != NULL ? wkey->key_id_size : 0);
+
+ if (wkey != NULL) _ncr_key_item_put(wkey);
+ if (data != NULL) kfree(data);
+ if (sdata != NULL) kfree(sdata);
+
+ return ret;
+}
+
+#define DER_KEY_MAX_SIZE (KEY_DATA_MAX_SIZE+16)
+
+/* Packed data are DER encoded:
+ * PackedData ::= SEQUENCE {
+ * version INTEGER { v1(0) }
+ * algorithm OBJECT IDENTIFIER,
+ * type INTEGER { secret_key(0), public(1), private(2) },
+ * data OCTET STRING
+ * }
+ *
+ * This allows distinguishing types of wrapped keys.
+ */
+static int key_to_packed_data( uint8_t** sdata, size_t * sdata_size, const struct key_item_st *key)
+{
+ uint8_t * pkey = NULL;
+ uint8_t * derkey = NULL;
+ uint32_t pkey_size;
+ int ret, err;
+ unsigned long version = KEY_WRAP_VERSION;
+ unsigned long type;
+ unsigned long derlen;
+ const oid_st* oid;
+
+ *sdata_size = KEY_DATA_MAX_SIZE;
+ pkey = kmalloc(*sdata_size, GFP_KERNEL);
+ if (pkey == NULL) {
+ err();
+ return -ENOMEM;
+ }
+
+ derlen = DER_KEY_MAX_SIZE;
+ derkey = kmalloc(derlen, GFP_KERNEL);
+ if (derkey == NULL) {
+ err();
+ goto fail;
+ }
+
+ if (key->type == NCR_KEY_TYPE_SECRET) {
+ memcpy(pkey, key->key.secret.data, key->key.secret.size);
+ pkey_size = key->key.secret.size;
+
+ type = 0;
+ } else if (key->type == NCR_KEY_TYPE_PRIVATE || key->type == NCR_KEY_TYPE_PUBLIC) {
+ pkey_size = *sdata_size;
+ ret = ncr_pk_pack( key, pkey, &pkey_size);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ if (key->type == NCR_KEY_TYPE_PUBLIC)
+ type = 1;
+ else type = 2;
+ } else {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ oid = _ncr_properties_to_oid(key->algorithm, pkey_size);
+ if (oid == NULL) {
+ err();
+ ret = -EOPNOTSUPP;
+ goto fail;
+ }
+
+ err = der_encode_sequence_multi(derkey, &derlen,
+ LTC_ASN1_SHORT_INTEGER, 1UL, &version,
+ LTC_ASN1_OBJECT_IDENTIFIER, oid->OIDlen, oid->OID,
+ LTC_ASN1_SHORT_INTEGER, 1UL, &type,
+ LTC_ASN1_OCTET_STRING, (unsigned long)pkey_size, pkey,
+ LTC_ASN1_EOL, 0UL, NULL);
+
+ kfree(pkey);
+
+ if (err != CRYPT_OK) {
+ err();
+ ret = _ncr_tomerr(err);
+ goto fail;
+ }
+
+ *sdata = (void*)derkey;
+ *sdata_size = derlen;
+
+ return 0;
+fail:
+ kfree(pkey);
+ kfree(derkey);
+
+ return ret;
+}
+
+inline static int packed_type_to_key_type(unsigned long type, struct key_item_st* key)
+{
+ switch(type) {
+ case 0:
+ key->type = NCR_KEY_TYPE_SECRET;
+ break;
+ case 1:
+ key->type = NCR_KEY_TYPE_PUBLIC;
+ break;
+ case 2:
+ key->type = NCR_KEY_TYPE_PRIVATE;
+ break;
+ default:
+ err();
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+
+/* Unpack, or better decode the DER data
+ */
+static int key_from_packed_data(struct nlattr *tb[], struct key_item_st *key,
+ const void *data, size_t data_size)
+{
+ ltc_asn1_list list[6];
+ int ret, i, pkey_size, err;
+ unsigned long version, type;
+ uint8_t * pkey = NULL;
+ oid_st oid;
+
+ if (data_size > DER_KEY_MAX_SIZE) {
+ err();
+ return -EINVAL;
+ }
+
+ pkey_size = KEY_DATA_MAX_SIZE;
+ pkey = kmalloc(pkey_size, GFP_KERNEL);
+ if (pkey == NULL) {
+ err();
+ return -ENOMEM;
+ }
+
+ i = 0;
+
+ list[i].type = LTC_ASN1_SHORT_INTEGER;
+ list[i].size = 1;
+ list[i++].data = &version;
+
+ list[i].type = LTC_ASN1_OBJECT_IDENTIFIER;
+ list[i].size = sizeof(oid.OID)/sizeof(oid.OID[0]);
+ list[i++].data = oid.OID;
+
+ list[i].type = LTC_ASN1_SHORT_INTEGER;
+ list[i].size = 1;
+ list[i++].data = &type;
+
+ list[i].type = LTC_ASN1_OCTET_STRING;
+ list[i].size = pkey_size;
+ list[i++].data = pkey;
+
+ err = der_decode_sequence(data, data_size, list, i);
+ if (err != CRYPT_OK) {
+ err();
+ ret = _ncr_tomerr(err);
+ goto fail;
+ }
+
+ if (version != KEY_WRAP_VERSION) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ oid.OIDlen = list[1].size;
+ pkey_size = list[3].size;
+
+ ret = packed_type_to_key_type(type, key);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ key->algorithm = _ncr_oid_to_properties(&oid);
+ if (key->algorithm == NULL) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ ret = ncr_key_update_flags(key, tb[NCR_ATTR_KEY_FLAGS]);
+ if (ret != 0) {
+ err();
+ return ret;
+ }
+
+
+#ifndef KEY_WRAP_IS_PRIVILEGED
+ /* Do not allow key unwrapping to result to exportable keys
+ */
+ if (current_euid() != 0)
+ key->flags &= (~NCR_KEY_FLAG_EXPORTABLE);
+#endif
+
+ if (key->type == NCR_KEY_TYPE_SECRET) {
+ if (data_size > NCR_CIPHER_MAX_KEY_LEN) {
+ err();
+ return -EINVAL;
+ }
+ key->key.secret.size = pkey_size;
+ memcpy(key->key.secret.data, pkey, pkey_size);
+ } else if (key->type == NCR_KEY_TYPE_PUBLIC
+ || key->type == NCR_KEY_TYPE_PRIVATE) {
+
+ ret = ncr_pk_unpack( key, pkey, pkey_size);
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+ } else {
+ err();
+ return -EINVAL;
+ }
+
+ ret = 0;
+
+fail:
+ kfree(pkey);
+
+ return ret;
+}