diff options
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | cryptodev_cipher.c | 30 | ||||
-rw-r--r-- | cryptodev_int.h | 5 | ||||
-rw-r--r-- | cryptodev_main.c | 24 | ||||
-rw-r--r-- | ncr-cipher.c | 236 | ||||
-rw-r--r-- | ncr-cipher.h | 20 | ||||
-rw-r--r-- | ncr-key-wrap.c | 309 | ||||
-rw-r--r-- | ncr-key.c | 5 | ||||
-rw-r--r-- | ncr-sessions.c | 125 | ||||
-rw-r--r-- | ncr.c | 8 | ||||
-rw-r--r-- | ncr.h | 7 | ||||
-rw-r--r-- | ncr_int.h | 42 |
12 files changed, 778 insertions, 36 deletions
@@ -2,7 +2,8 @@ KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build VERSION = 0.1 cryptodev-objs = cryptodev_main.o cryptodev_cipher.o ncr.o \ - ncr-data.o ncr-key.o ncr-limits.o + ncr-data.o ncr-key.o ncr-limits.o ncr-sessions.o ncr-cipher.o \ + ncr-key-wrap.o obj-m += cryptodev.o diff --git a/cryptodev_cipher.c b/cryptodev_cipher.c index 2f52f46..7c1db6a 100644 --- a/cryptodev_cipher.c +++ b/cryptodev_cipher.c @@ -48,9 +48,9 @@ static void cryptodev_complete(struct crypto_async_request *req, int err) complete(&res->completion); } -int cryptodev_cipher_init(struct cipher_data* out, const char* alg_name, uint8_t __user * key, size_t keylen) +int cryptodev_cipher_init(struct cipher_data* out, const char* alg_name, uint8_t * keyp, size_t keylen) { - uint8_t keyp[CRYPTO_CIPHER_MAX_KEY_LEN]; + struct ablkcipher_alg* alg; int ret; @@ -58,17 +58,6 @@ int cryptodev_cipher_init(struct cipher_data* out, const char* alg_name, uint8_t out->init = 1; - if (unlikely(keylen > CRYPTO_CIPHER_MAX_KEY_LEN)) { - dprintk(1,KERN_DEBUG,"Setting key failed for %s-%zu.\n", - alg_name, keylen*8); - return -EINVAL; - } - /* Copy the key from user and set to TFM. */ - if (unlikely(copy_from_user(keyp, key, keylen))) { - dprintk(1, KERN_DEBUG, "copy_from_user() failed for key\n"); - return -EINVAL; - } - out->async.s = crypto_alloc_ablkcipher(alg_name, 0, 0); if (unlikely(IS_ERR(out->async.s))) { dprintk(1,KERN_DEBUG,"%s: Failed to load cipher %s\n", __func__, @@ -196,23 +185,12 @@ ssize_t cryptodev_cipher_decrypt( struct cipher_data* cdata, struct scatterlist /* Hash functions */ -int cryptodev_hash_init( struct hash_data* hdata, const char* alg_name, int hmac_mode, void __user* mackey, size_t mackeylen) +int cryptodev_hash_init( struct hash_data* hdata, const char* alg_name, int hmac_mode, void * mackey, size_t mackeylen) { int ret; -uint8_t hkeyp[CRYPTO_HMAC_MAX_KEY_LEN]; hdata->init = 1; - if (unlikely(mackeylen > CRYPTO_HMAC_MAX_KEY_LEN)) { - dprintk(1,KERN_DEBUG,"Setting hmac key failed for %s-%zu.\n", - alg_name, mackeylen*8); - return -EINVAL; - } - if (unlikely(copy_from_user(hkeyp, mackey, mackeylen))) { - dprintk(1, KERN_DEBUG, "copy_from_user() failed for mackey\n"); - return -EINVAL; - } - hdata->async.s = crypto_alloc_ahash(alg_name, 0, 0); if (unlikely(IS_ERR(hdata->async.s))) { dprintk(1,KERN_DEBUG,"%s: Failed to load transform for %s\n", __func__, @@ -223,7 +201,7 @@ uint8_t hkeyp[CRYPTO_HMAC_MAX_KEY_LEN]; /* Copy the key from user and set to TFM. */ if (hmac_mode != 0) { - ret = crypto_ahash_setkey(hdata->async.s, hkeyp, mackeylen); + ret = crypto_ahash_setkey(hdata->async.s, mackey, mackeylen); if (unlikely(ret)) { dprintk(1,KERN_DEBUG,"Setting hmac key failed for %s-%zu.\n", alg_name, mackeylen*8); diff --git a/cryptodev_int.h b/cryptodev_int.h index 6f2cc5e..5d4ebfe 100644 --- a/cryptodev_int.h +++ b/cryptodev_int.h @@ -10,6 +10,7 @@ #include <linux/miscdevice.h> #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/scatterlist.h> #define PFX "cryptodev: " #define dprintk(level,severity,format,a...) \ @@ -35,7 +36,7 @@ struct cipher_data } async; }; -int cryptodev_cipher_init(struct cipher_data* out, const char* alg_name, __user uint8_t * key, size_t keylen); +int cryptodev_cipher_init(struct cipher_data* out, const char* alg_name, uint8_t * key, size_t keylen); void cryptodev_cipher_deinit(struct cipher_data* cdata); ssize_t cryptodev_cipher_decrypt( struct cipher_data* cdata, struct scatterlist *sg1, struct scatterlist *sg2, size_t len); ssize_t cryptodev_cipher_encrypt( struct cipher_data* cdata, struct scatterlist *sg1, struct scatterlist *sg2, size_t len); @@ -57,7 +58,7 @@ int cryptodev_hash_final( struct hash_data* hdata, void* output); ssize_t cryptodev_hash_update( struct hash_data* hdata, struct scatterlist *sg, size_t len); int cryptodev_hash_reset( struct hash_data* hdata); void cryptodev_hash_deinit(struct hash_data* hdata); -int cryptodev_hash_init( struct hash_data* hdata, const char* alg_name, int hmac_mode, __user void* mackey, size_t mackeylen); +int cryptodev_hash_init( struct hash_data* hdata, const char* alg_name, int hmac_mode, void* mackey, size_t mackeylen); /* compatibility stuff */ #ifdef CONFIG_COMPAT diff --git a/cryptodev_main.c b/cryptodev_main.c index 58ae45b..0b2493d 100644 --- a/cryptodev_main.c +++ b/cryptodev_main.c @@ -198,7 +198,17 @@ crypto_create_session(struct fcrypt *fcr, struct session_op *sop) /* Set-up crypto transform. */ if (alg_name) { - ret = cryptodev_cipher_init(&ses_new->cdata, alg_name, sop->key, sop->keylen); + uint8_t keyp[CRYPTO_CIPHER_MAX_KEY_LEN]; + + if (unlikely(sop->keylen > CRYPTO_CIPHER_MAX_KEY_LEN)) { + dprintk(1,KERN_DEBUG,"Setting key failed for %s-%zu.\n", + alg_name, sop->keylen*8); + ret = -EINVAL; + goto error; + } + copy_from_user(keyp, sop->key, sop->keylen); + + ret = cryptodev_cipher_init(&ses_new->cdata, alg_name, keyp, sop->keylen); if (ret < 0) { dprintk(1,KERN_DEBUG,"%s: Failed to load cipher for %s\n", __func__, alg_name); @@ -208,7 +218,17 @@ crypto_create_session(struct fcrypt *fcr, struct session_op *sop) } if (hash_name) { - ret = cryptodev_hash_init(&ses_new->hdata, hash_name, hmac_mode, sop->mackey, sop->mackeylen); + uint8_t keyp[CRYPTO_HMAC_MAX_KEY_LEN]; + + if (unlikely(sop->mackeylen > CRYPTO_HMAC_MAX_KEY_LEN)) { + dprintk(1,KERN_DEBUG,"Setting key failed for %s-%zu.\n", + alg_name, sop->mackeylen*8); + ret = -EINVAL; + goto error; + } + copy_from_user(keyp, sop->mackey, sop->mackeylen); + + ret = cryptodev_hash_init(&ses_new->hdata, hash_name, hmac_mode, keyp, sop->mackeylen); if (ret != 0) { dprintk(1,KERN_DEBUG,"%s: Failed to load hash for %s\n", __func__, hash_name); diff --git a/ncr-cipher.c b/ncr-cipher.c new file mode 100644 index 0000000..d0fc320 --- /dev/null +++ b/ncr-cipher.c @@ -0,0 +1,236 @@ +/* + * Driver for /dev/crypto device (aka CryptoDev) + * + * Copyright (c) 2010 Nikos Mavrogiannopoulos <nmav@gnutls.org> + * + * This file is part of linux cryptodev. + * + * cryptodev 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 3 of the License, or + * (at your option) any later version. + * + * cryptodev 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/crypto.h> +#include <linux/mm.h> +#include <linux/highmem.h> +#include <linux/random.h> +#include <asm/uaccess.h> +#include <asm/ioctl.h> +#include <crypto/algapi.h> +#include <crypto/hash.h> +#include "cryptodev.h" +#include "cryptodev_int.h" +#include "ncr_int.h" + +static inline const char* algo2str(ncr_algorithm_t algo) +{ + switch(algo) { + case NCR_ALG_AES_ECB: + return "ecb(aes)"; + case NCR_ALG_3DES_CBC: + return "cbc(des3_ede)"; + case NCR_ALG_AES_CBC: + return "cbc(aes)"; + case NCR_ALG_CAMELLIA_CBC: + return "cbc(camelia)"; + case NCR_ALG_SHA1: + return "sha1"; + case NCR_ALG_MD5: + return "md5"; + case NCR_ALG_SHA2_224: + return "sha224"; + case NCR_ALG_SHA2_256: + return "sha256"; + case NCR_ALG_SHA2_384: + return "sha384"; + case NCR_ALG_SHA2_512: + return "sha512"; + case NCR_ALG_HMAC_SHA1: + return "hmac(sha1)"; + case NCR_ALG_HMAC_MD5: + return "hmac(md5)"; + case NCR_ALG_HMAC_SHA2_224: + return "hmac(sha224)"; + case NCR_ALG_HMAC_SHA2_256: + return "hmac(sha256)"; + case NCR_ALG_HMAC_SHA2_384: + return "hmac(sha384)"; + case NCR_ALG_HMAC_SHA2_512: + return "hmac(sha512)"; + default: + return NULL; + } +} + +ncr_session_t ncr_cipher_init(struct list_sem_st* sess_lst, ncr_algorithm_t algorithm, struct key_item_st *key, void* iv, size_t iv_size); +void ncr_cipher_deinit(struct list_sem_st* sess_lst, ncr_session_t session); +int ncr_cipher_encrypt(struct list_sem_st* sess_lst, ncr_session_t session, const struct data_item_st * plaintext, struct data_item_st* ciphertext); +int ncr_cipher_decrypt(struct list_sem_st* sess_lst, ncr_session_t session, const struct data_item_st * ciphertext, struct data_item_st* plaintext); + +ncr_session_t ncr_cipher_init(struct list_sem_st* sess_lst, ncr_algorithm_t algorithm, struct key_item_st *key, void* iv, size_t iv_size) +{ +struct session_item_st* sess; +int ret; +const char* str; + + if (key->type != NCR_KEY_TYPE_SECRET) { + err(); + return NCR_SESSION_INVALID; + } + + sess = ncr_session_new(sess_lst); + if (sess == NULL) { + err(); + return NCR_SESSION_INVALID; + } + + str = algo2str(algorithm); + if (str == NULL) { + err(); + return NCR_SESSION_INVALID; + } + + ret = cryptodev_cipher_init(&sess->ctx, str, key->key.secret.data, key->key.secret.size); + if (ret < 0) { + err(); + _ncr_sessions_item_put(sess); + return NCR_SESSION_INVALID; + } + + return sess->desc; +} + +int ncr_cipher_encrypt(struct list_sem_st* sess_lst, ncr_session_t session, const struct data_item_st * plaintext, struct data_item_st* ciphertext) +{ +ssize_t output; +struct scatterlist sg, sgo; +struct session_item_st* sess; + + sess = ncr_sessions_item_get( sess_lst, session); + if (sess == NULL) { + err(); + return -EINVAL; + } + + sg_init_one(&sg, plaintext->data, plaintext->data_size); + + sg_init_one(&sgo, ciphertext->data, ciphertext->data_size); + + output = cryptodev_cipher_encrypt( &sess->ctx, &sg, &sgo, plaintext->data_size); + _ncr_sessions_item_put(sess); + + if (output < 0) { + err(); + return output; + } + + return 0; +} + +/* inplace encryption */ +int _ncr_cipher_encrypt(struct list_sem_st* sess_lst, ncr_session_t session, void* plaintext, size_t plaintext_size) +{ +ssize_t output; +struct scatterlist sg; +struct session_item_st* sess; + + sess = ncr_sessions_item_get( sess_lst, session); + if (sess == NULL) { + err(); + return -EINVAL; + } + + sg_init_one(&sg, plaintext, plaintext_size); + + output = cryptodev_cipher_encrypt( &sess->ctx, &sg, &sg, plaintext_size); + _ncr_sessions_item_put(sess); + + if (output < 0) { + err(); + return output; + } + + return 0; +} + +/* inplace encryption */ +int _ncr_cipher_decrypt(struct list_sem_st* sess_lst, ncr_session_t session, void* ciphertext, size_t ciphertext_size) +{ +ssize_t output; +struct scatterlist sg; +struct session_item_st* sess; + + sess = ncr_sessions_item_get( sess_lst, session); + if (sess == NULL) { + err(); + return -EINVAL; + } + + sg_init_one(&sg, ciphertext, ciphertext_size); + + output = cryptodev_cipher_encrypt( &sess->ctx, &sg, &sg, ciphertext_size); + _ncr_sessions_item_put(sess); + + if (output < 0) { + err(); + return output; + } + + return 0; +} + +int ncr_cipher_decrypt(struct list_sem_st* sess_lst, ncr_session_t session, const struct data_item_st * ciphertext, struct data_item_st* plaintext) +{ +ssize_t output; +struct scatterlist sg, sgo; +struct session_item_st* sess; + + sess = ncr_sessions_item_get( sess_lst, session); + if (sess == NULL) { + err(); + return -EINVAL; + } + + sg_init_one(&sgo, plaintext->data, plaintext->data_size); + + sg_init_one(&sg, ciphertext->data, ciphertext->data_size); + + output = cryptodev_cipher_decrypt( &sess->ctx, &sg, &sgo, ciphertext->data_size); + _ncr_sessions_item_put(sess); + + if (output < 0) { + err(); + return output; + } + + return 0; +} + +void ncr_cipher_deinit(struct list_sem_st* lst, ncr_session_t session) +{ + struct session_item_st * item, *tmp; + + down(&lst->sem); + + list_for_each_entry_safe(item, tmp, &lst->list, list) { + if(item->desc == session) { + list_del(&item->list); + cryptodev_cipher_deinit(&item->ctx); + _ncr_sessions_item_put( item); /* decrement ref count */ + break; + } + } + + up(&lst->sem); + + return; +} diff --git a/ncr-cipher.h b/ncr-cipher.h new file mode 100644 index 0000000..a582fc6 --- /dev/null +++ b/ncr-cipher.h @@ -0,0 +1,20 @@ +/* cipher stuff */ + +#ifndef NCR_CIPHER_H +# define NCR_CIPHER_H + +ncr_session_t ncr_cipher_init(struct list_sem_st* sess_lst, + ncr_algorithm_t algorithm, struct key_item_st *key, void* iv, size_t iv_size); +int ncr_cipher_encrypt(struct list_sem_st* sess_lst, ncr_session_t session, + const struct data_item_st * plaintext, struct data_item_st* ciphertext); +int ncr_cipher_decrypt(struct list_sem_st* sess_lst, ncr_session_t session, + const struct data_item_st * ciphertext, struct data_item_st* plaintext); +void ncr_cipher_deinit(struct list_sem_st* lst, ncr_session_t session); + +int _ncr_cipher_encrypt(struct list_sem_st* sess_lst, + ncr_session_t session, void* plaintext, size_t plaintext_size); + +int _ncr_cipher_decrypt(struct list_sem_st* sess_lst, + ncr_session_t session, void* plaintext, size_t plaintext_size); + +#endif /* NCR_CIPHER_H */ diff --git a/ncr-key-wrap.c b/ncr-key-wrap.c new file mode 100644 index 0000000..e9cd884 --- /dev/null +++ b/ncr-key-wrap.c @@ -0,0 +1,309 @@ +/* + * New driver for /dev/crypto device (aka CryptoDev) + + * Copyright (c) 2010 Nikos Mavrogiannopoulos <nmav@gnutls.org> + * + * This file is part of linux cryptodev. + * + * cryptodev 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 3 of the License, or + * (at your option) any later version. + * + * cryptodev 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/mm.h> +#include <linux/highmem.h> +#include <linux/random.h> +#include "cryptodev.h" +#include <asm/uaccess.h> +#include <asm/ioctl.h> +#include <linux/scatterlist.h> +#include "ncr.h" +#include "ncr_int.h" +#include "ncr-cipher.h" + +typedef uint8_t val64_t[8]; + +static const val64_t initA = "\xA6\xA6\xA6\xA6\xA6\xA6\xA6\xA6"; + +void val64_zero( val64_t * val) +{ + memset(val, 0, sizeof(*val)); +} + +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; +} + +/* Wraps using the RFC3394 way. + */ +static int wrap_aes(struct list_sem_st* sess_lst, + struct key_item_st* tobewrapped, struct key_item_st *kek, + struct data_item_st* output) +{ +size_t key_size, n; +uint8_t *raw_key; +val64_t A; +int i, j, ret; +uint8_t aes_block[16]; +ncr_session_t kd; + + kd = ncr_cipher_init(sess_lst, NCR_ALG_AES_ECB, kek, NULL, 0); + if (kd == NCR_SESSION_INVALID) { + err(); + return -ENOMEM; + } + + if (tobewrapped->type != NCR_KEY_TYPE_SECRET) { + err(); + ret = -EINVAL; + goto cleanup; + } + + 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; + + if (output->max_data_size < (n+1)*8) { + err(); + ret = -EINVAL; + goto cleanup; + } + + + { + val64_t R[n+1]; + + /* R = P */ + for (i=0;i<n;i++) { + memcpy(R[i], &raw_key[i*8], 8); + } + + memcpy(A, initA, sizeof(initA)); + + for (i=0;i<6*n;i++) { + memcpy(aes_block, A, 8); + memcpy(&aes_block[8], R[0], 8); + + _ncr_cipher_encrypt(sess_lst, kd, 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[i], R[i+1], sizeof(R[i])); + memcpy(R[n-1], &aes_block[8], 8); /* R[n-1] = LSB64(AES(A^{t-1}|R_{1}^{t-1})) */ + } + + memcpy(output->data, A, sizeof(A)); + for (j=1;j<n;j++) + memcpy(&output->data[j*8], R[j], 8); + output->data_size = (n+1)*8; + } + + + ret = 0; + +cleanup: + ncr_cipher_deinit(sess_lst, kd); + + return ret; +} + +static int unwrap_aes(struct list_sem_st* sess_lst, + struct key_item_st* output, struct key_item_st *kek, + struct data_item_st* wrapped) +{ +size_t key_size, n; +uint8_t *raw_key; +val64_t A; +int i, j, ret; +uint8_t aes_block[16]; +ncr_session_t kd; + + kd = ncr_cipher_init(sess_lst, NCR_ALG_AES_ECB, kek, NULL, 0); + if (kd == NCR_SESSION_INVALID) { + err(); + return -ENOMEM; + } + + output->type = NCR_KEY_TYPE_SECRET; + + raw_key = wrapped->data; + key_size = wrapped->data_size; + + if (key_size % 8 != 0) { + err(); + ret = -EINVAL; + goto cleanup; + } + + n = key_size/8 - 1; + + if (sizeof(output->key.secret.data) < (n-1)*8) { + err(); + ret = -EINVAL; + goto cleanup; + } + + { + val64_t R[n]; + + memcpy(A, raw_key, 8); /* A = C[0] */ + for (i=0;i<n;i++) + memcpy(R[i], &raw_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); + + _ncr_cipher_decrypt(sess_lst, kd, aes_block, sizeof(aes_block)); + + memcpy(A, aes_block, 8); + memcpy(R[0], &aes_block[8], 8); + + for (j=1;j<n;j++) + memcpy(R[j], R[j-1], sizeof(R[j])); + } + + if (memcmp(A, initA, sizeof(initA))!= 0) { + err(); + ret = -EINVAL; + goto cleanup; + } + + for (i=0;i<n;i++) { + memcpy(&output->key.secret.data[i*8], R[i], sizeof(R[i])); + } + output->key.secret.size = n*8; + + } + + + ret = 0; + +cleanup: + ncr_cipher_deinit(sess_lst, kd); + + return ret; +} + +int ncr_key_wrap(struct list_sem_st* key_lst, struct list_sem_st* data_lst, struct list_sem_st* sess_lst, void __user* arg) +{ +struct ncr_key_wrap_st wrap; +struct key_item_st* wkey = NULL; +struct key_item_st* key = NULL; +struct data_item_st * data = NULL; +int ret; + + copy_from_user( &wrap, arg, sizeof(wrap)); + + wkey = ncr_key_item_get( key_lst, wrap.keytowrap); + if (wkey == NULL) { + err(); + return -EINVAL; + } + + if (!(wkey->flags & NCR_KEY_FLAG_WRAPPABLE)) { + err(); + return -EPERM; + } + + key = ncr_key_item_get( key_lst, wrap.key.key); + if (key == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + + data = ncr_data_item_get(data_lst, wrap.data); + if (data == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + + data->flags = key_flags_to_data(wkey->flags) | NCR_DATA_FLAG_EXPORTABLE; + + switch(wrap.algorithm) { + case NCR_WALG_AES_RFC3394: + ret = wrap_aes(sess_lst, wkey, key, data); + default: + ret = -EINVAL; + } + +fail: + if (wkey != NULL) _ncr_key_item_put(wkey); + if (key != NULL) _ncr_key_item_put(key); + if (data != NULL) _ncr_data_item_put(data); + + return ret; +} + +int ncr_key_unwrap(struct list_sem_st* key_lst, struct list_sem_st* data_lst, struct list_sem_st* sess_lst, void __user* arg) +{ +struct ncr_key_wrap_st wrap; +struct key_item_st* wkey = NULL; +struct key_item_st* key = NULL; +struct data_item_st * data = NULL; +int ret; + + copy_from_user( &wrap, arg, sizeof(wrap)); + + wkey = ncr_key_item_get( key_lst, wrap.keytowrap); + if (wkey == NULL) { + err(); + return -EINVAL; + } + + key = ncr_key_item_get( key_lst, wrap.key.key); + if (key == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + + data = ncr_data_item_get(data_lst, wrap.data); + if (data == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + + wkey->flags = data_flags_to_key(wkey->flags) | NCR_KEY_FLAG_WRAPPABLE; + + switch(wrap.algorithm) { + case NCR_WALG_AES_RFC3394: + ret = unwrap_aes(sess_lst, wkey, key, data); + default: + ret = -EINVAL; + } + +fail: + if (wkey != NULL) _ncr_key_item_put(wkey); + if (key != NULL) _ncr_key_item_put(key); + if (data != NULL) _ncr_data_item_put(data); + + return ret; +} @@ -183,10 +183,7 @@ int ret; } /* found */ - ditem->flags = 0; - if ((item->flags & NCR_KEY_FLAG_EXPORTABLE)) { - ditem->flags |= NCR_DATA_FLAG_EXPORTABLE; - } + ditem->flags = key_flags_to_data(item->flags); if (item->key.secret.size > 0) { memcpy(ditem->data, item->key.secret.data, item->key.secret.size); diff --git a/ncr-sessions.c b/ncr-sessions.c new file mode 100644 index 0000000..a692588 --- /dev/null +++ b/ncr-sessions.c @@ -0,0 +1,125 @@ +/* + * New driver for /dev/crypto device (aka CryptoDev) + + * Copyright (c) 2010 Nikos Mavrogiannopoulos <nmav@gnutls.org> + * + * This file is part of linux cryptodev. + * + * cryptodev 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 3 of the License, or + * (at your option) any later version. + * + * cryptodev 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/crypto.h> +#include "cryptodev.h" +#include "ncr.h" +#include "ncr_int.h" + +void ncr_sessions_list_deinit(struct list_sem_st* lst) +{ + if(lst) { + struct session_item_st * item, *tmp; + + down(&lst->sem); + + list_for_each_entry_safe(item, tmp, &lst->list, list) { + list_del(&item->list); + _ncr_sessions_item_put( item); /* decrement ref count */ + } + up(&lst->sem); + + } +} + +/* must be called with data semaphore down + */ +static ncr_session_t _ncr_sessions_get_new_desc( struct list_sem_st* lst) +{ +struct session_item_st* item; +int mx = 0; + + list_for_each_entry(item, &lst->list, list) { + mx = max(mx, item->desc); + } + mx++; + + return mx; +} + +/* returns the data item corresponding to desc */ +struct session_item_st* ncr_sessions_item_get( struct list_sem_st* lst, ncr_session_t desc) +{ +struct session_item_st* item; + + down(&lst->sem); + list_for_each_entry(item, &lst->list, list) { + if (item->desc == desc) { + atomic_inc(&item->refcnt); + up(&lst->sem); + return item; + } + } + up(&lst->sem); + + err(); + return NULL; +} + +void _ncr_sessions_item_put( struct session_item_st* item) +{ + if (atomic_dec_and_test(&item->refcnt)) { + kfree(item); + } +} + +struct session_item_st* ncr_session_new(struct list_sem_st* lst) +{ + struct session_item_st* sess; + + sess = kmalloc(sizeof(*sess), GFP_KERNEL); + if (sess == NULL) { + err(); + return NULL; + } + + memset(sess, 0, sizeof(*sess)); + + atomic_set(&sess->refcnt, 1); + + down(&lst->sem); + + sess->desc = _ncr_sessions_get_new_desc(lst); + list_add(&sess->list, &lst->list); + + up(&lst->sem); + + return sess; +} + +void ncr_session_deinit(struct list_sem_st* lst, ncr_session_t desc) +{ + struct session_item_st * item, *tmp; + + down(&lst->sem); + + list_for_each_entry_safe(item, tmp, &lst->list, list) { + if(item->desc == desc) { + list_del(&item->list); + _ncr_sessions_item_put( item); /* decrement ref count */ + break; + } + } + + up(&lst->sem); + + return; +} @@ -48,6 +48,9 @@ void* ncr_init_lists(void) init_MUTEX(&lst->key.sem); INIT_LIST_HEAD(&lst->key.list); + init_MUTEX(&lst->sessions.sem); + INIT_LIST_HEAD(&lst->sessions.list); + return lst; } @@ -56,6 +59,7 @@ void ncr_deinit_lists(struct ncr_lists *lst) if(lst) { ncr_data_list_deinit(&lst->data); ncr_key_list_deinit(&lst->key); + ncr_sessions_list_deinit(&lst->sessions); kfree(lst); } } @@ -90,6 +94,10 @@ ncr_ioctl(struct ncr_lists* lst, struct file *filp, return ncr_key_import(&lst->data, &lst->key, (void*)arg); case NCRIO_KEY_GET_INFO: return ncr_key_info(&lst->key, (void*)arg); + case NCRIO_KEY_WRAP: + return ncr_key_wrap(&lst->key, &lst->data, &lst->sessions, (void*)arg); + case NCRIO_KEY_UNWRAP: + return ncr_key_unwrap(&lst->key, &lst->data, &lst->sessions, (void*)arg); #if 0 case NCRIO_KEY_GENERATE_PAIR: return ncr_key_generate_pair(&lst->key, (void*)arg); @@ -11,6 +11,8 @@ typedef enum { NCR_ALG_CAMELLIA_CBC, NCR_ALG_ARCFOUR, + NCR_ALG_AES_ECB, + NCR_ALG_SHA1=40, NCR_ALG_MD5, NCR_ALG_SHA2_224, @@ -29,6 +31,9 @@ typedef enum { NCR_ALG_DSA, } ncr_algorithm_t; +typedef enum { + NCR_WALG_AES_RFC3394, +} ncr_wrap_algorithm_t; typedef enum { NCR_KEY_TYPE_INVALID, @@ -261,7 +266,7 @@ struct ncr_private_key_params_st /* FIXME key wrap ioctls */ struct ncr_key_wrap_st { - ncr_algorithm_t algorithm; + ncr_wrap_algorithm_t algorithm; ncr_key_t keytowrap; struct ncr_key_params_st key; ncr_data_t data; /* encrypted keytowrap */ @@ -3,15 +3,26 @@ #include "ncr.h" #include <asm/atomic.h> +#include "cryptodev_int.h" #define err() printk(KERN_DEBUG"ncr: %s: %s: %d\n", __FILE__, __func__, __LINE__) +struct session_item_st { + struct list_head list; + + struct cipher_data ctx; + + atomic_t refcnt; + ncr_session_t desc; +}; + struct data_item_st { struct list_head list; /* This object is not protected from concurrent access. * I see no reason to allow concurrent writes (reads are * not an issue). */ + uint8_t* data; size_t data_size; size_t max_data_size; @@ -60,6 +71,7 @@ struct ncr_lists { struct list_sem_st key; /* sessions */ + struct list_sem_st sessions; }; void* ncr_init_lists(void); @@ -105,5 +117,35 @@ void ncr_limits_deinit(void); ncr_key_type_t ncr_algorithm_to_key_type(ncr_algorithm_t algo); +int ncr_key_wrap(struct list_sem_st* keys, struct list_sem_st* data, struct list_sem_st* sess_lst, void __user* arg); +int ncr_key_unwrap(struct list_sem_st*, struct list_sem_st* data, struct list_sem_st* sess_lst, void __user* arg); + +/* sessions */ +struct session_item_st* ncr_session_new(struct list_sem_st* lst); +void _ncr_sessions_item_put( struct session_item_st* item); +struct session_item_st* ncr_sessions_item_get( struct list_sem_st* lst, ncr_session_t desc); +void ncr_sessions_list_deinit(struct list_sem_st* lst); +void ncr_session_deinit(struct list_sem_st* lst, ncr_session_t desc); + +/* misc helper macros */ +inline static unsigned int key_flags_to_data(unsigned int key_flags) +{ + unsigned int flags = 0; + + if (key_flags & NCR_KEY_FLAG_EXPORTABLE) + flags |= NCR_DATA_FLAG_EXPORTABLE; + + return flags; +} + +inline static unsigned int data_flags_to_key(unsigned int data_flags) +{ + unsigned int flags = 0; + + if (data_flags & NCR_DATA_FLAG_EXPORTABLE) + flags |= NCR_KEY_FLAG_EXPORTABLE; + + return flags; +} #endif |