summaryrefslogtreecommitdiffstats
path: root/ncr-key-wrap.c
diff options
context:
space:
mode:
Diffstat (limited to 'ncr-key-wrap.c')
-rw-r--r--ncr-key-wrap.c537
1 files changed, 440 insertions, 97 deletions
diff --git a/ncr-key-wrap.c b/ncr-key-wrap.c
index 0c56def27f7..7ea70cfc847 100644
--- a/ncr-key-wrap.c
+++ b/ncr-key-wrap.c
@@ -28,16 +28,22 @@
#include <linux/highmem.h>
#include <linux/random.h>
#include <linux/uaccess.h>
-#include "cryptodev.h"
#include <linux/scatterlist.h>
+#include <net/netlink.h>
#include "ncr.h"
#include "ncr-int.h"
#include "cryptodev_int.h"
+#define KEY_WRAP_VERSION 0
+
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)
{
@@ -56,7 +62,7 @@ int i,j;
if (*output_size < (n+1)*8) {
err();
- return -EINVAL;
+ return -ERANGE;
}
memcpy(A, iv, 8);
@@ -259,29 +265,73 @@ cleanup:
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)
{
- if (tobewrapped->type != NCR_KEY_TYPE_SECRET) {
+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 -EINVAL;
+ return ret;
}
- return _wrap_aes_rfc5649(tobewrapped->key.secret.data, tobewrapped->key.secret.size,
+ 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, const void* iv, size_t iv_size)
+ void *wrapped, size_t wrapped_size, struct nlattr *tb[])
{
- output->type = NCR_KEY_TYPE_SECRET;
+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;
- return _unwrap_aes_rfc5649(output->key.secret.data, &output->key.secret.size, kek,
- wrapped, wrapped_size, iv, iv_size);
}
/* Wraps using the RFC3394 way.
*/
-static int wrap_aes(struct key_item_st* tobewrapped, struct key_item_st *kek,
+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;
@@ -358,15 +408,25 @@ void print_val64(char* str, val64_t val)
}
#endif
-static int unwrap_aes(struct key_item_st* output, struct key_item_st *kek,
- void* wrapped_key, size_t wrapped_key_size, const void* iv, size_t iv_size)
+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;
@@ -413,12 +473,17 @@ val64_t * R = NULL;
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->flags = NCR_KEY_FLAG_WRAPPABLE;
output->type = NCR_KEY_TYPE_SECRET;
ret = 0;
@@ -430,21 +495,54 @@ cleanup:
return ret;
}
-int ncr_key_wrap(struct ncr_lists *lst, void __user* arg)
+/* 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;
+}
+
+int ncr_key_wrap(struct ncr_lists *lst, const struct ncr_key_wrap *wrap,
+ struct nlattr *tb[])
{
-struct ncr_key_wrap_st wrap;
+const struct nlattr *nla;
struct key_item_st* wkey = NULL;
struct key_item_st* key = NULL;
void* data = NULL;
-size_t data_size;
+const void *iv;
+size_t data_size, iv_size;
int ret;
- if (unlikely(copy_from_user(&wrap, arg, sizeof(wrap)))) {
+ if (wrap->buffer_size < 0) {
err();
- return -EFAULT;
+ return -EINVAL;
}
- ret = ncr_key_item_get_read( &wkey, lst, wrap.keytowrap);
+ ret = ncr_key_item_get_read(&wkey, lst, wrap->source_key);
if (ret < 0) {
err();
return ret;
@@ -456,13 +554,25 @@ int ret;
goto fail;
}
- ret = ncr_key_item_get_read( &key, lst, wrap.key);
+ 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.io_size;
+ data_size = wrap->buffer_size;
data = kmalloc(data_size, GFP_KERNEL);
if (data == NULL) {
err();
@@ -470,18 +580,30 @@ int ret;
goto fail;
}
- switch(wrap.algorithm) {
- case NCR_WALG_AES_RFC3394:
- ret = wrap_aes(wkey, key, data, &data_size,
- wrap.params.params.cipher.iv, wrap.params.params.cipher.iv_size);
- break;
- case NCR_WALG_AES_RFC5649:
- ret = wrap_aes_rfc5649(wkey, key, data, &data_size,
- wrap.params.params.cipher.iv, wrap.params.params.cipher.iv_size);
- break;
- default:
- err();
- ret = -EINVAL;
+ 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) {
@@ -489,18 +611,13 @@ int ret;
goto fail;
}
- ret = copy_to_user(wrap.io, data, data_size);
+ ret = copy_to_user(wrap->buffer, data, data_size);
if (unlikely(ret)) {
ret = -EFAULT;
goto fail;
}
- wrap.io_size = data_size;
-
- ret = copy_to_user(arg, &wrap, sizeof(wrap));
- if (unlikely(ret)) {
- ret = -EFAULT;
- }
+ ret = data_size;
fail:
if (wkey != NULL) _ncr_key_item_put(wkey);
@@ -513,33 +630,35 @@ fail:
/* Unwraps keys. All keys unwrapped are not accessible by
* userspace.
*/
-int ncr_key_unwrap(struct ncr_lists *lst, void __user* arg)
+int ncr_key_unwrap(struct ncr_lists *lst, const struct ncr_key_unwrap *wrap,
+ struct nlattr *tb[])
{
-struct ncr_key_wrap_st wrap;
+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;
- if (unlikely(copy_from_user(&wrap, arg, sizeof(wrap)))) {
+ ret = ncr_key_item_get_write(&wkey, lst, wrap->dest_key);
+ if (ret < 0) {
err();
- return -EFAULT;
+ return ret;
}
- ret = ncr_key_item_get_write( &wkey, lst, wrap.keytowrap);
+ ret = ncr_key_item_get_read(&key, lst, wrap->wrapping_key);
if (ret < 0) {
err();
- return ret;
+ goto fail;
}
- ret = ncr_key_item_get_read( &key, lst, wrap.key);
- if (ret < 0) {
+ if (!(key->flags & NCR_KEY_FLAG_UNWRAPPING)) {
err();
+ ret = -EPERM;
goto fail;
}
- data_size = wrap.io_size;
+ data_size = wrap->data_size;
data = kmalloc(data_size, GFP_KERNEL);
if (data == NULL) {
err();
@@ -547,24 +666,27 @@ int ret;
goto fail;
}
- if (unlikely(copy_from_user(data, wrap.io, data_size))) {
+ if (unlikely(copy_from_user(data, wrap->data, data_size))) {
err();
ret = -EFAULT;
goto fail;
}
+
+ ncr_key_clear(wkey);
- switch(wrap.algorithm) {
- case NCR_WALG_AES_RFC3394:
- ret = unwrap_aes(wkey, key, data, data_size,
- wrap.params.params.cipher.iv, wrap.params.params.cipher.iv_size);
- break;
- case NCR_WALG_AES_RFC5649:
- ret = unwrap_aes_rfc5649(wkey, key, data, data_size,
- wrap.params.params.cipher.iv, wrap.params.params.cipher.iv_size);
- break;
- default:
- err();
- ret = -EINVAL;
+ 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:
@@ -575,9 +697,10 @@ fail:
return ret;
}
-int ncr_key_storage_wrap(struct ncr_lists *lst, void __user* arg)
+int ncr_key_storage_wrap(struct ncr_lists *lst,
+ const struct ncr_key_storage_wrap *wrap,
+ struct nlattr *tb[])
{
-struct ncr_key_storage_wrap_st wrap;
struct key_item_st* wkey = NULL;
void* data = NULL;
size_t data_size;
@@ -590,24 +713,18 @@ int ret;
return -ENOKEY;
}
- if (unlikely(copy_from_user(&wrap, arg, sizeof(wrap)))) {
+ if (wrap->buffer_size < 0) {
err();
- return -EFAULT;
+ return -EINVAL;
}
- ret = ncr_key_item_get_read( &wkey, lst, wrap.keytowrap);
+ ret = ncr_key_item_get_read(&wkey, lst, wrap->key);
if (ret < 0) {
err();
return ret;
}
- if (!(wkey->flags & NCR_KEY_FLAG_WRAPPABLE)) {
- err();
- ret = -EPERM;
- goto fail;
- }
-
- data_size = wrap.io_size;
+ data_size = wrap->buffer_size;
data = kmalloc(data_size, GFP_KERNEL);
if (data == NULL) {
err();
@@ -627,18 +744,13 @@ int ret;
goto fail;
}
- ret = copy_to_user(wrap.io, data, data_size);
+ ret = copy_to_user(wrap->buffer, data, data_size);
if (unlikely(ret)) {
ret = -EFAULT;
goto fail;
}
- wrap.io_size = data_size;
-
- ret = copy_to_user(arg, &wrap, sizeof(wrap));
- if (unlikely(ret)) {
- ret = -EFAULT;
- }
+ ret = data_size;
fail:
if (wkey != NULL) _ncr_key_item_put(wkey);
@@ -648,12 +760,10 @@ fail:
return ret;
}
-/* Unwraps keys. All keys unwrapped are not accessible by
- * userspace.
- */
-int ncr_key_storage_unwrap(struct ncr_lists *lst, void __user* arg)
+int ncr_key_storage_unwrap(struct ncr_lists *lst,
+ const struct ncr_key_storage_unwrap *wrap,
+ struct nlattr *tb[])
{
-struct ncr_key_storage_wrap_st wrap;
struct key_item_st* wkey = NULL;
void* data = NULL;
uint8_t * sdata = NULL;
@@ -665,18 +775,13 @@ int ret;
return -ENOKEY;
}
- if (unlikely(copy_from_user(&wrap, arg, sizeof(wrap)))) {
- err();
- return -EFAULT;
- }
-
- ret = ncr_key_item_get_write( &wkey, lst, wrap.keytowrap);
+ ret = ncr_key_item_get_write(&wkey, lst, wrap->key);
if (ret < 0) {
err();
return ret;
}
- data_size = wrap.io_size;
+ data_size = wrap->data_size;
data = kmalloc(data_size, GFP_KERNEL);
if (data == NULL) {
err();
@@ -684,7 +789,7 @@ int ret;
goto fail;
}
- if (unlikely(copy_from_user(data, wrap.io, data_size))) {
+ if (unlikely(copy_from_user(data, wrap->data, data_size))) {
err();
ret = -EFAULT;
goto fail;
@@ -698,14 +803,14 @@ int ret;
goto fail;
}
- wkey->flags = NCR_KEY_FLAG_WRAPPABLE;
-
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();
@@ -720,3 +825,241 @@ fail:
return ret;
}
+
+#define DER_KEY_MAX_SIZE (KEY_DATA_MAX_SIZE+16)
+
+/* Packed data are DER encoded:
+ * PackedData ::= SEQUENCE {
+ * version INTEGER { v1(0) }
+ * type INTEGER { secret_key(0), rsa_privkey(1), rsa_pubkey(2), dsa_privkey(3), dsa_pubkey(4),
+ * dh_privkey(5), dh_pubkey(6) },
+ * 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;
+
+ *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;
+ }
+
+ switch (key->algorithm->algo) {
+ case NCR_ALG_RSA:
+ if (key->type == NCR_KEY_TYPE_PUBLIC)
+ type = 2;
+ else type = 1;
+ break;
+ case NCR_ALG_DSA:
+ if (key->type == NCR_KEY_TYPE_PUBLIC)
+ type = 4;
+ else type = 3;
+ break;
+ case NCR_ALG_DH:
+ if (key->type == NCR_KEY_TYPE_PUBLIC)
+ type = 6;
+ else type = 5;
+ break;
+ default:
+ /* unsupported yet */
+ ret = -EINVAL;
+ err();
+ goto fail;
+ }
+
+ } else {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ err = der_encode_sequence_multi(derkey, &derlen,
+ LTC_ASN1_SHORT_INTEGER, 1UL, &version,
+ 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;
+ key->algorithm = _ncr_algo_to_properties("cbc(aes)");
+ break;
+ case 1:
+ key->type = NCR_KEY_TYPE_PRIVATE;
+ key->algorithm = _ncr_algo_to_properties("rsa");
+ break;
+ case 2:
+ key->type = NCR_KEY_TYPE_PUBLIC;
+ key->algorithm = _ncr_algo_to_properties("rsa");
+ break;
+ case 3:
+ key->type = NCR_KEY_TYPE_PRIVATE;
+ key->algorithm = _ncr_algo_to_properties("dsa");
+ break;
+ case 4:
+ key->type = NCR_KEY_TYPE_PUBLIC;
+ key->algorithm = _ncr_algo_to_properties("dsa");
+ break;
+ case 5:
+ key->type = NCR_KEY_TYPE_PRIVATE;
+ key->algorithm = _ncr_algo_to_properties("dh");
+ break;
+ case 6:
+ key->type = NCR_KEY_TYPE_PUBLIC;
+ key->algorithm = _ncr_algo_to_properties("dh");
+ break;
+ default:
+ err();
+ return -EINVAL;
+ }
+
+ if (key->algorithm == NULL) {
+ 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 = 0, pkey_size, err;
+ unsigned long version, type;
+ uint8_t * pkey = NULL;
+
+ 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;
+ }
+
+ list[i].type = LTC_ASN1_SHORT_INTEGER;
+ list[i].size = 1;
+ list[i++].data = &version;
+
+ 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;
+ }
+
+ pkey_size = list[2].size;
+
+ ret = packed_type_to_key_type(type, key);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ ret = ncr_key_update_flags(key, tb[NCR_ATTR_KEY_FLAGS]);
+ if (ret != 0) {
+ err();
+ return ret;
+ }
+
+ 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;
+}