/* * New driver for /dev/crypto device (aka CryptoDev) * Copyright (c) 2010 Katholieke Universiteit Leuven * * Author: Nikos Mavrogiannopoulos * * 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 #include #include #include #include #include #include #include #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) { 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=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;ikey.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;itype != 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;ikey.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;ikey.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; } 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; 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: 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; 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: 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: 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: 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) } * 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; }