diff options
Diffstat (limited to 'ncr-key-wrap.c')
-rw-r--r-- | ncr-key-wrap.c | 537 |
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; +} |