diff options
26 files changed, 2665 insertions, 2134 deletions
diff --git a/crypto/userspace/Makefile b/crypto/userspace/Makefile index 65edf8fb2ce..689ee0d25b6 100644 --- a/crypto/userspace/Makefile +++ b/crypto/userspace/Makefile @@ -65,7 +65,7 @@ TOMCRYPT_OBJECTS = libtomcrypt/misc/zeromem.o libtomcrypt/misc/crypt/crypt_argch cryptodev-objs := cryptodev_main.o cryptodev_cipher.o ncr.o \ ncr-key.o ncr-limits.o ncr-pk.o ncr-sessions.o ncr-dh.o \ - ncr-key-wrap.o ncr-key-storage.o $(TOMMATH_OBJECTS) \ + ncr-key-wrap.o ncr-key-storage.o utils.o $(TOMMATH_OBJECTS) \ $(TOMCRYPT_OBJECTS) diff --git a/crypto/userspace/cryptodev_cipher.c b/crypto/userspace/cryptodev_cipher.c index 8a003c5613a..1fb11473715 100644 --- a/crypto/userspace/cryptodev_cipher.c +++ b/crypto/userspace/cryptodev_cipher.c @@ -23,7 +23,6 @@ */ #include <linux/crypto.h> -#include <linux/cryptodev.h> #include <linux/mm.h> #include <linux/highmem.h> #include <linux/ioctl.h> @@ -217,7 +216,7 @@ ssize_t cryptodev_cipher_decrypt( struct cipher_data* cdata, const struct scatte /* Hash functions */ -int cryptodev_hash_init( struct hash_data* hdata, const char* alg_name, int hmac_mode, void * mackey, size_t mackeylen) +int cryptodev_hash_init(struct hash_data *hdata, const char *alg_name, const void *mackey, size_t mackeylen) { int ret; @@ -229,7 +228,7 @@ int ret; } /* Copy the key from user and set to TFM. */ - if (hmac_mode != 0) { + if (mackey != NULL) { ret = crypto_ahash_setkey(hdata->async.s, mackey, mackeylen); if (unlikely(ret)) { @@ -279,6 +278,48 @@ error: return ret; } +int cryptodev_hash_clone(struct hash_data *hdata, struct hash_data *old_data, + const void *mackey, size_t mackeylen) +{ + const char *algo; + void *state; + int ret; + + /* We want exactly the same driver. */ + algo = crypto_tfm_alg_driver_name(crypto_ahash_tfm(old_data->async.s)); + ret = cryptodev_hash_init(hdata, algo, mackey, mackeylen); + if (unlikely(ret != 0)) + return ret; + + state = kmalloc(crypto_ahash_statesize(hdata->async.s), GFP_KERNEL); + if (unlikely(state == NULL)) { + ret = -ENOMEM; + goto err; + } + + ret = crypto_ahash_export(old_data->async.request, state); + if (unlikely(ret != 0)) { + dprintk(0, KERN_ERR, "error exporting hash state\n"); + goto err; + } + ret = crypto_ahash_import(hdata->async.request, state); + if (unlikely(ret != 0)) { + dprintk(0,KERN_ERR, + "error in crypto_hash_init()\n"); + goto err; + } + + kfree(state); + + hdata->init = 1; + return 0; + +err: + kfree(state); + cryptodev_hash_deinit(hdata); + return ret; +} + void cryptodev_hash_deinit(struct hash_data* hdata) { if (hdata->init) { diff --git a/crypto/userspace/cryptodev_int.h b/crypto/userspace/cryptodev_int.h index fdab390df4b..ce705be363a 100644 --- a/crypto/userspace/cryptodev_int.h +++ b/crypto/userspace/cryptodev_int.h @@ -10,8 +10,8 @@ #include <linux/miscdevice.h> #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/ncr.h> #include <linux/scatterlist.h> -#include <linux/cryptodev.h> #define PFX "cryptodev: " #define dprintk(level,severity,format,a...) \ @@ -45,7 +45,7 @@ struct cipher_data struct crypto_ablkcipher* s; struct cryptodev_result *result; struct ablkcipher_request *request; - uint8_t iv[EALG_MAX_BLOCK_LEN]; + uint8_t iv[NCR_CIPHER_MAX_BLOCK_LEN]; } async; }; @@ -77,43 +77,8 @@ ssize_t cryptodev_hash_update( struct hash_data* hdata, struct scatterlist *sg, ssize_t _cryptodev_hash_update( struct hash_data* hdata, const void* data, 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, void* mackey, size_t mackeylen); - -/* compatibility stuff */ -#ifdef CONFIG_COMPAT -#include <linux/compat.h> - -/* input of CIOCGSESSION */ -struct compat_session_op { - /* Specify either cipher or mac - */ - uint32_t cipher; /* cryptodev_crypto_op_t */ - uint32_t mac; /* cryptodev_crypto_op_t */ - - uint32_t keylen; - compat_uptr_t key; /* pointer to key data */ - uint32_t mackeylen; - compat_uptr_t mackey; /* pointer to mac key data */ - - uint32_t ses; /* session identifier */ -}; - -/* input of CIOCCRYPT */ - struct compat_crypt_op { - uint32_t ses; /* session identifier */ - uint16_t op; /* COP_ENCRYPT or COP_DECRYPT */ - uint16_t flags; /* no usage so far, use 0 */ - uint32_t len; /* length of source data */ - compat_uptr_t src; /* source data */ - compat_uptr_t dst; /* pointer to output data */ - compat_uptr_t mac; /* pointer to output data for hash/MAC operations */ - compat_uptr_t iv; /* initialization vector for encryption operations */ -}; - -/* compat ioctls, defined for the above structs */ -#define COMPAT_CIOCGSESSION _IOWR('c', 102, struct compat_session_op) -#define COMPAT_CIOCCRYPT _IOWR('c', 104, struct compat_crypt_op) - -#endif /* CONFIG_COMPAT */ +int cryptodev_hash_init(struct hash_data *hdata, const char *alg_name, const void *mackey, size_t mackeylen); +int cryptodev_hash_clone(struct hash_data *hdata, struct hash_data *old_data, + const void *mackey, size_t mackeylen); #endif /* CRYPTODEV_INT_H */ diff --git a/crypto/userspace/cryptodev_main.c b/crypto/userspace/cryptodev_main.c index 01082693c5f..0cbe3c4bdc3 100644 --- a/crypto/userspace/cryptodev_main.c +++ b/crypto/userspace/cryptodev_main.c @@ -48,452 +48,13 @@ MODULE_AUTHOR("Nikos Mavrogiannopoulos <nmav@gnutls.org>"); MODULE_DESCRIPTION("CryptoDev driver"); MODULE_LICENSE("GPL"); -/* ====== Compile-time config ====== */ - -#define CRYPTODEV_STATS - /* ====== Module parameters ====== */ int cryptodev_verbosity = 0; module_param(cryptodev_verbosity, int, 0644); MODULE_PARM_DESC(cryptodev_verbosity, "0: normal, 1: verbose, 2: debug"); -#ifdef CRYPTODEV_STATS -static int enable_stats = 0; -module_param(enable_stats, int, 0644); -MODULE_PARM_DESC(enable_stats, "collect statictics about cryptodev usage"); -#endif - /* ====== CryptoAPI ====== */ -struct fcrypt { - struct list_head list; - struct semaphore sem; -}; - -struct crypt_priv { - void * ncr; - struct fcrypt fcrypt; -}; - -#define FILL_SG(sg,ptr,len) \ - do { \ - (sg)->page = virt_to_page(ptr); \ - (sg)->offset = offset_in_page(ptr); \ - (sg)->length = len; \ - (sg)->dma_address = 0; \ - } while (0) - -struct csession { - struct list_head entry; - struct semaphore sem; - struct cipher_data cdata; - struct hash_data hdata; - uint32_t sid; -#ifdef CRYPTODEV_STATS -#if ! ((COP_ENCRYPT < 2) && (COP_DECRYPT < 2)) -#error Struct csession.stat uses COP_{ENCRYPT,DECRYPT} as indices. Do something! -#endif - unsigned long long stat[2]; - size_t stat_max_size, stat_count; -#endif - int array_size; - struct page **pages; - struct scatterlist *sg; -}; - -/* Prepare session for future use. */ -static int -crypto_create_session(struct fcrypt *fcr, struct session_op *sop) -{ - struct csession *ses_new = NULL, *ses_ptr; - int ret = 0; - const char *alg_name=NULL; - const char *hash_name=NULL; - int hmac_mode = 1; - - /* Does the request make sense? */ - if (unlikely(!sop->cipher && !sop->mac)) { - dprintk(1,KERN_DEBUG,"Both 'cipher' and 'mac' unset.\n"); - return -EINVAL; - } - - switch (sop->cipher) { - case 0: - break; - case CRYPTO_DES_CBC: - alg_name = "cbc(des)"; - break; - case CRYPTO_3DES_CBC: - alg_name = "cbc(des3_ede)"; - break; - case CRYPTO_BLF_CBC: - alg_name = "cbc(blowfish)"; - break; - case CRYPTO_AES_CBC: - alg_name = "cbc(aes)"; - break; - case CRYPTO_CAMELLIA_CBC: - alg_name = "cbc(camelia)"; - break; - case CRYPTO_AES_CTR: - alg_name = "ctr(aes)"; - break; - case CRYPTO_NULL: - alg_name = "ecb(cipher_null)"; - break; - default: - dprintk(1,KERN_DEBUG,"%s: bad cipher: %d\n", __func__, sop->cipher); - return -EINVAL; - } - - switch (sop->mac) { - case 0: - break; - case CRYPTO_MD5_HMAC: - hash_name = "hmac(md5)"; - break; - case CRYPTO_RIPEMD160_HMAC: - hash_name = "hmac(rmd160)"; - break; - case CRYPTO_SHA1_HMAC: - hash_name = "hmac(sha1)"; - break; - case CRYPTO_SHA2_256_HMAC: - hash_name = "hmac(sha256)"; - break; - case CRYPTO_SHA2_384_HMAC: - hash_name = "hmac(sha384)"; - break; - case CRYPTO_SHA2_512_HMAC: - hash_name = "hmac(sha512)"; - break; - - /* non-hmac cases */ - case CRYPTO_MD5: - hash_name = "md5"; - hmac_mode = 0; - break; - case CRYPTO_RIPEMD160: - hash_name = "rmd160"; - hmac_mode = 0; - break; - case CRYPTO_SHA1: - hash_name = "sha1"; - hmac_mode = 0; - break; - case CRYPTO_SHA2_256: - hash_name = "sha256"; - hmac_mode = 0; - break; - case CRYPTO_SHA2_384: - hash_name = "sha384"; - hmac_mode = 0; - break; - case CRYPTO_SHA2_512: - hash_name = "sha512"; - hmac_mode = 0; - break; - - default: - dprintk(1,KERN_DEBUG,"%s: bad mac: %d\n", __func__, sop->mac); - return -EINVAL; - } - - /* Create a session and put it to the list. */ - ses_new = kzalloc(sizeof(*ses_new), GFP_KERNEL); - if(!ses_new) { - return -ENOMEM; - } - - /* Set-up crypto transform. */ - if (alg_name) { - 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, (size_t)sop->keylen*8); - ret = -EINVAL; - goto error_cipher; - } - - if (unlikely(copy_from_user(keyp, sop->key, sop->keylen))) { - ret = -EFAULT; - goto error_cipher; - } - - 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); - ret = -EINVAL; - goto error_cipher; - } - } - - if (hash_name) { - 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, (size_t)sop->mackeylen*8); - ret = -EINVAL; - goto error_hash; - } - - if (unlikely(copy_from_user(keyp, sop->mackey, - sop->mackeylen))) { - ret = -EFAULT; - goto error_hash; - } - - 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); - ret = -EINVAL; - goto error_hash; - } - } - - ses_new->array_size = DEFAULT_PREALLOC_PAGES; - dprintk(2, KERN_DEBUG, "%s: preallocating for %d user pages\n", - __func__, ses_new->array_size); - ses_new->pages = kzalloc(ses_new->array_size * - sizeof(struct page *), GFP_KERNEL); - ses_new->sg = kzalloc(ses_new->array_size * - sizeof(struct scatterlist), GFP_KERNEL); - if (ses_new->sg == NULL || ses_new->pages == NULL) { - dprintk(0,KERN_DEBUG,"Memory error\n"); - ret = -ENOMEM; - goto error_hash; - } - - /* put the new session to the list */ - get_random_bytes(&ses_new->sid, sizeof(ses_new->sid)); - init_MUTEX(&ses_new->sem); - - down(&fcr->sem); -restart: - list_for_each_entry(ses_ptr, &fcr->list, entry) { - /* Check for duplicate SID */ - if (unlikely(ses_new->sid == ses_ptr->sid)) { - get_random_bytes(&ses_new->sid, sizeof(ses_new->sid)); - /* Unless we have a broken RNG this - shouldn't loop forever... ;-) */ - goto restart; - } - } - - list_add(&ses_new->entry, &fcr->list); - up(&fcr->sem); - - /* Fill in some values for the user. */ - sop->ses = ses_new->sid; - - return 0; - -error_hash: - cryptodev_cipher_deinit( &ses_new->cdata); - kfree(ses_new->sg); - kfree(ses_new->pages); -error_cipher: - if (ses_new) kfree(ses_new); - - return ret; - -} - -/* Everything that needs to be done when remowing a session. */ -static inline void -crypto_destroy_session(struct csession *ses_ptr) -{ - if(down_trylock(&ses_ptr->sem)) { - dprintk(2, KERN_DEBUG, "Waiting for semaphore of sid=0x%08X\n", - ses_ptr->sid); - down(&ses_ptr->sem); - } - dprintk(2, KERN_DEBUG, "Removed session 0x%08X\n", ses_ptr->sid); -#if defined(CRYPTODEV_STATS) - if(enable_stats) - dprintk(2, KERN_DEBUG, - "Usage in Bytes: enc=%llu, dec=%llu, max=%zu, avg=%lu, cnt=%zu\n", - ses_ptr->stat[COP_ENCRYPT], ses_ptr->stat[COP_DECRYPT], - ses_ptr->stat_max_size, ses_ptr->stat_count > 0 - ? ((unsigned long)(ses_ptr->stat[COP_ENCRYPT]+ - ses_ptr->stat[COP_DECRYPT]) / - ses_ptr->stat_count) : 0, - ses_ptr->stat_count); -#endif - cryptodev_cipher_deinit(&ses_ptr->cdata); - cryptodev_hash_deinit(&ses_ptr->hdata); - dprintk(2, KERN_DEBUG, "%s: freeing space for %d user pages\n", - __func__, ses_ptr->array_size); - kfree(ses_ptr->pages); - kfree(ses_ptr->sg); - - up(&ses_ptr->sem); - kfree(ses_ptr); -} - -/* Look up a session by ID and remove. */ -static int -crypto_finish_session(struct fcrypt *fcr, uint32_t sid) -{ - struct csession *tmp, *ses_ptr; - struct list_head *head; - int ret = 0; - - down(&fcr->sem); - head = &fcr->list; - list_for_each_entry_safe(ses_ptr, tmp, head, entry) { - if(ses_ptr->sid == sid) { - list_del(&ses_ptr->entry); - crypto_destroy_session(ses_ptr); - break; - } - } - - if (unlikely(!ses_ptr)) { - dprintk(1, KERN_ERR, "Session with sid=0x%08X not found!\n", sid); - ret = -ENOENT; - } - up(&fcr->sem); - - return ret; -} - -/* Remove all sessions when closing the file */ -static int -crypto_finish_all_sessions(struct fcrypt *fcr) -{ - struct csession *tmp, *ses_ptr; - struct list_head *head; - - down(&fcr->sem); - - head = &fcr->list; - list_for_each_entry_safe(ses_ptr, tmp, head, entry) { - list_del(&ses_ptr->entry); - crypto_destroy_session(ses_ptr); - } - up(&fcr->sem); - - return 0; -} - -/* Look up session by session ID. The returned session is locked. */ -static struct csession * -crypto_get_session_by_sid(struct fcrypt *fcr, uint32_t sid) -{ - struct csession *ses_ptr; - - down(&fcr->sem); - list_for_each_entry(ses_ptr, &fcr->list, entry) { - if(ses_ptr->sid == sid) { - down(&ses_ptr->sem); - break; - } - } - up(&fcr->sem); - - return ses_ptr; -} - -static int -hash_n_crypt(struct csession *ses_ptr, struct crypt_op *cop, - struct scatterlist *src_sg, struct scatterlist *dst_sg, uint32_t len) -{ - int ret; - - /* Always hash before encryption and after decryption. Maybe - * we should introduce a flag to switch... TBD later on. - */ - if (cop->op == COP_ENCRYPT) { - if (ses_ptr->hdata.init != 0) { - ret = cryptodev_hash_update(&ses_ptr->hdata, src_sg, len); - if (unlikely(ret)) - goto out_err; - } - if (ses_ptr->cdata.init != 0) { - ret = cryptodev_cipher_encrypt( &ses_ptr->cdata, src_sg, dst_sg, len); - - if (unlikely(ret)) - goto out_err; - } - } else { - if (ses_ptr->cdata.init != 0) { - ret = cryptodev_cipher_decrypt( &ses_ptr->cdata, src_sg, dst_sg, len); - - if (unlikely(ret)) - goto out_err; - } - - if (ses_ptr->hdata.init != 0) { - ret = cryptodev_hash_update(&ses_ptr->hdata, dst_sg, len); - if (unlikely(ret)) - goto out_err; - } - } - return 0; -out_err: - dprintk(0, KERN_ERR, "CryptoAPI failure: %d\n",ret); - return ret; -} - -/* This is the main crypto function - feed it with plaintext - and get a ciphertext (or vice versa :-) */ -static int -__crypto_run_std(struct csession *ses_ptr, struct crypt_op *cop) -{ - char *data; - char __user *src, *dst; - struct scatterlist sg; - size_t nbytes, bufsize; - int ret = 0; - - nbytes = cop->len; - data = (char*)__get_free_page(GFP_KERNEL); - - if (unlikely(!data)) { - return -ENOMEM; - } - bufsize = PAGE_SIZE < nbytes ? PAGE_SIZE : nbytes; - - src = cop->src; - dst = cop->dst; - - while(nbytes > 0) { - size_t current_len = nbytes > bufsize ? bufsize : nbytes; - - if (unlikely(copy_from_user(data, src, current_len))) { - ret = -EFAULT; - break; - } - - sg_init_one(&sg, data, current_len); - - ret = hash_n_crypt(ses_ptr, cop, &sg, &sg, current_len); - - if (unlikely(ret)) - break; - - if (ses_ptr->cdata.init != 0) { - if (unlikely(copy_to_user(dst, data, current_len))) { - ret = -EFAULT; - break; - } - } - - dst += current_len; - nbytes -= current_len; - src += current_len; - } - - free_page((unsigned long)data); - return ret; -} - -#ifndef DISABLE_ZCOPY void release_user_pages(struct page **pg, int pagecount) { @@ -536,415 +97,58 @@ int __get_userbuf(uint8_t __user *addr, uint32_t len, int write, return 0; } -/* make cop->src and cop->dst available in scatterlists */ -static int get_userbuf(struct csession *ses, - struct crypt_op *cop, struct scatterlist **src_sg, - struct scatterlist **dst_sg, int *tot_pages) -{ - int src_pagecount, dst_pagecount = 0, pagecount, write_src = 1; - - if (cop->src == NULL) { - return -EINVAL; - } - - src_pagecount = PAGECOUNT(cop->src, cop->len); - if (!ses->cdata.init) { /* hashing only */ - write_src = 0; - } else if (cop->src != cop->dst) { /* non-in-situ transformation */ - if (cop->dst == NULL) { - return -EINVAL; - } - dst_pagecount = PAGECOUNT(cop->dst, cop->len); - write_src = 0; - } - (*tot_pages) = pagecount = src_pagecount + dst_pagecount; - - if (pagecount > ses->array_size) { - struct scatterlist *sg; - struct page **pages; - int array_size; - - for (array_size = ses->array_size; array_size < pagecount; - array_size *= 2) - ; - - dprintk(2, KERN_DEBUG, "%s: reallocating to %d elements\n", - __func__, array_size); - pages = krealloc(ses->pages, array_size * sizeof(struct page *), - GFP_KERNEL); - if (pages == NULL) - return -ENOMEM; - ses->pages = pages; - sg = krealloc(ses->sg, array_size * sizeof(struct scatterlist), - GFP_KERNEL); - if (sg == NULL) - return -ENOMEM; - ses->sg = sg; - ses->array_size = array_size; - } - - if (__get_userbuf(cop->src, cop->len, write_src, - src_pagecount, ses->pages, ses->sg)) { - dprintk(1, KERN_ERR, "failed to get user pages for data input\n"); - return -EINVAL; - } - (*src_sg) = (*dst_sg) = ses->sg; - - if (dst_pagecount) { - (*dst_sg) = ses->sg + src_pagecount; - - if (__get_userbuf(cop->dst, cop->len, 1, dst_pagecount, - ses->pages + src_pagecount, *dst_sg)) { - dprintk(1, KERN_ERR, "failed to get user pages for data output\n"); - release_user_pages(ses->pages, src_pagecount); - return -EINVAL; - } - } - return 0; -} - -/* This is the main crypto function - zero-copy edition */ -static int -__crypto_run_zc(struct csession *ses_ptr, struct crypt_op *cop) -{ - struct scatterlist *src_sg, *dst_sg; - int ret = 0, pagecount; - - ret = get_userbuf(ses_ptr, cop, &src_sg, &dst_sg, &pagecount); - if (unlikely(ret)) { - dprintk(1, KERN_ERR, "Error getting user pages. Falling back to non zero copy.\n"); - return __crypto_run_std(ses_ptr, cop); - } - - ret = hash_n_crypt(ses_ptr, cop, src_sg, dst_sg, cop->len); - - release_user_pages(ses_ptr->pages, pagecount); - return ret; -} - -#endif /* DISABLE_ZCOPY */ - -static int crypto_run(struct fcrypt *fcr, struct crypt_op *cop) -{ - struct csession *ses_ptr; - uint8_t hash_output[AALG_MAX_RESULT_LEN]; - int ret; - - if (unlikely(cop->op != COP_ENCRYPT && cop->op != COP_DECRYPT)) { - dprintk(1, KERN_DEBUG, "invalid operation op=%u\n", cop->op); - return -EINVAL; - } - - /* this also enters ses_ptr->sem */ - ses_ptr = crypto_get_session_by_sid(fcr, cop->ses); - if (unlikely(!ses_ptr)) { - dprintk(1, KERN_ERR, "invalid session ID=0x%08X\n", cop->ses); - return -EINVAL; - } - - if (ses_ptr->hdata.init != 0) { - ret = cryptodev_hash_reset(&ses_ptr->hdata); - if (unlikely(ret)) { - dprintk(1, KERN_ERR, - "error in cryptodev_hash_reset()\n"); - goto out_unlock; - } - } - - if (ses_ptr->cdata.init != 0) { - int blocksize = ses_ptr->cdata.blocksize; - - if (unlikely(cop->len % blocksize)) { - dprintk(1, KERN_ERR, - "data size (%u) isn't a multiple of block size (%u)\n", - cop->len, blocksize); - ret = -EINVAL; - goto out_unlock; - } - - if (cop->iv) { - uint8_t iv[EALG_MAX_BLOCK_LEN]; - - ret = copy_from_user(iv, cop->iv, min( (int)sizeof(iv), (ses_ptr->cdata.ivsize))); - if (unlikely(ret)) { - dprintk(1, KERN_ERR, "error copying IV (%d bytes)\n", min( (int)sizeof(iv), (ses_ptr->cdata.ivsize))); - ret = -EFAULT; - goto out_unlock; - } - - cryptodev_cipher_set_iv(&ses_ptr->cdata, iv, ses_ptr->cdata.ivsize); - } - } - -#ifdef DISABLE_ZCOPY - ret = __crypto_run_std(ses_ptr, cop); -#else /* normal */ - ret = __crypto_run_zc(ses_ptr, cop); -#endif - if (unlikely(ret)) - goto out_unlock; - - if (ses_ptr->hdata.init != 0) { - ret = cryptodev_hash_final(&ses_ptr->hdata, hash_output); - if (unlikely(ret)) { - dprintk(0, KERN_ERR, "CryptoAPI failure: %d\n",ret); - goto out_unlock; - } - - if (unlikely(copy_to_user(cop->mac, hash_output, ses_ptr->hdata.digestsize))) { - ret = -EFAULT; - goto out_unlock; - } - } - -#if defined(CRYPTODEV_STATS) - if (enable_stats) { - /* this is safe - we check cop->op at the function entry */ - ses_ptr->stat[cop->op] += cop->len; - if (ses_ptr->stat_max_size < cop->len) - ses_ptr->stat_max_size = cop->len; - ses_ptr->stat_count++; - } -#endif - -out_unlock: - up(&ses_ptr->sem); - return ret; -} - - /* ====== /dev/crypto ====== */ static int cryptodev_open(struct inode *inode, struct file *filp) { - struct crypt_priv *pcr; - - pcr = kmalloc(sizeof(*pcr), GFP_KERNEL); - if(!pcr) - return -ENOMEM; + void *ncr; - memset(pcr, 0, sizeof(*pcr)); - init_MUTEX(&pcr->fcrypt.sem); - INIT_LIST_HEAD(&pcr->fcrypt.list); - - pcr->ncr = ncr_init_lists(); - if (pcr->ncr == NULL) { - kfree(pcr); + ncr = ncr_init_lists(); + if (ncr == NULL) { return -ENOMEM; } - filp->private_data = pcr; + filp->private_data = ncr; return 0; } static int cryptodev_release(struct inode *inode, struct file *filp) { - struct crypt_priv *pcr = filp->private_data; + void *ncr = filp->private_data; - if(pcr) { - crypto_finish_all_sessions(&pcr->fcrypt); - ncr_deinit_lists(pcr->ncr); - kfree(pcr); + if (ncr) { + ncr_deinit_lists(ncr); filp->private_data = NULL; } return 0; } -static int -clonefd(struct file *filp) +static long +cryptodev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - int ret; - ret = get_unused_fd(); - if (ret >= 0) { - get_file(filp); - fd_install(ret, filp); - } + void *ncr = filp->private_data; - return ret; -} - -static int -cryptodev_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg_) -{ - void __user *arg = (void __user *)arg_; - int __user *p = arg; - struct session_op sop; - struct crypt_op cop; - struct crypt_priv *pcr = filp->private_data; - struct fcrypt * fcr; - uint32_t ses; - int ret, fd; - - if (unlikely(!pcr)) + if (unlikely(!ncr)) BUG(); - fcr = &pcr->fcrypt; - - switch (cmd) { - case CIOCASYMFEAT: - return put_user(0, p); - case CRIOGET: - fd = clonefd(filp); - ret = put_user(fd, p); - if (unlikely(ret)) { - sys_close(fd); - return ret; - } - return ret; - case CIOCGSESSION: - if (unlikely(copy_from_user(&sop, arg, sizeof(sop)))) - return -EFAULT; - - ret = crypto_create_session(fcr, &sop); - if (unlikely(ret)) - return ret; - ret = copy_to_user(arg, &sop, sizeof(sop)); - if (unlikely(ret)) { - crypto_finish_session(fcr, sop.ses); - return -EFAULT; - } - return ret; - case CIOCFSESSION: - ret = get_user(ses, (uint32_t __user *)arg); - if (unlikely(ret)) - return ret; - ret = crypto_finish_session(fcr, ses); - return ret; - case CIOCCRYPT: - if (unlikely(copy_from_user(&cop, arg, sizeof(cop)))) - return -EFAULT; - - ret = crypto_run(fcr, &cop); - if (unlikely(ret)) - return ret; - if (unlikely(copy_to_user(arg, &cop, sizeof(cop)))) - return -EFAULT; - return 0; - - default: - return ncr_ioctl(pcr->ncr, cmd, arg_); - } + return ncr_ioctl(ncr, cmd, arg); } /* compatibility code for 32bit userlands */ #ifdef CONFIG_COMPAT -static inline void -compat_to_session_op(struct compat_session_op *compat, struct session_op *sop) -{ - sop->cipher = compat->cipher; - sop->mac = compat->mac; - sop->keylen = compat->keylen; - - sop->key = compat_ptr(compat->key); - sop->mackeylen = compat->mackeylen; - sop->mackey = compat_ptr(compat->mackey); - sop->ses = compat->ses; -} - -static inline void -session_op_to_compat(struct session_op *sop, struct compat_session_op *compat) -{ - compat->cipher = sop->cipher; - compat->mac = sop->mac; - compat->keylen = sop->keylen; - - compat->key = ptr_to_compat(sop->key); - compat->mackeylen = sop->mackeylen; - compat->mackey = ptr_to_compat(sop->mackey); - compat->ses = sop->ses; -} - -static inline void -compat_to_crypt_op(struct compat_crypt_op *compat, struct crypt_op *cop) -{ - cop->ses = compat->ses; - cop->op = compat->op; - cop->flags = compat->flags; - cop->len = compat->len; - - cop->src = compat_ptr(compat->src); - cop->dst = compat_ptr(compat->dst); - cop->mac = compat_ptr(compat->mac); - cop->iv = compat_ptr(compat->iv); -} - -static inline void -crypt_op_to_compat(struct crypt_op *cop, struct compat_crypt_op *compat) -{ - compat->ses = cop->ses; - compat->op = cop->op; - compat->flags = cop->flags; - compat->len = cop->len; - - compat->src = ptr_to_compat(cop->src); - compat->dst = ptr_to_compat(cop->dst); - compat->mac = ptr_to_compat(cop->mac); - compat->iv = ptr_to_compat(cop->iv); -} - static long -cryptodev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg_) +cryptodev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - void __user *arg = (void __user *)arg_; - struct fcrypt *fcr = file->private_data; - struct session_op sop; - struct compat_session_op compat_sop; - struct crypt_op cop; - struct compat_crypt_op compat_cop; - int ret; - - if (unlikely(!fcr)) + void *ncr = file->private_data; + + if (unlikely(!ncr)) BUG(); - switch (cmd) { - case CIOCASYMFEAT: - case CRIOGET: - case CIOCFSESSION: - return cryptodev_ioctl(NULL, file, cmd, arg_); - - case COMPAT_CIOCGSESSION: - if (unlikely(copy_from_user(&compat_sop, arg, - sizeof(compat_sop)))) - return -EFAULT; - compat_to_session_op(&compat_sop, &sop); - - ret = crypto_create_session(fcr, &sop); - if (unlikely(ret)) - return ret; - - session_op_to_compat(&sop, &compat_sop); - ret = copy_to_user(arg, &compat_sop, sizeof(compat_sop)); - if (unlikely(ret)) { - crypto_finish_session(fcr, sop.ses); - return -EFAULT; - } - return ret; - - case COMPAT_CIOCCRYPT: - if (unlikely(copy_from_user(&compat_cop, arg, - sizeof(compat_cop)))) - return -EFAULT; - - compat_to_crypt_op(&compat_cop, &cop); - - ret = crypto_run(fcr, &cop); - if (unlikely(ret)) - return ret; - - crypt_op_to_compat(&cop, &compat_cop); - if (unlikely(copy_to_user(arg, &compat_cop, - sizeof(compat_cop)))) - return -EFAULT; - return 0; - - default: - return -EINVAL; - } + return ncr_compat_ioctl(ncr, cmd, arg); } #endif /* CONFIG_COMPAT */ @@ -953,7 +157,7 @@ static const struct file_operations cryptodev_fops = { .owner = THIS_MODULE, .open = cryptodev_open, .release = cryptodev_release, - .ioctl = cryptodev_ioctl, + .unlocked_ioctl = cryptodev_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = cryptodev_compat_ioctl, #endif /* CONFIG_COMPAT */ diff --git a/crypto/userspace/libtomcrypt/hashes/hash_memory.c b/crypto/userspace/libtomcrypt/hashes/hash_memory.c index a416de9624e..c6f51881245 100644 --- a/crypto/userspace/libtomcrypt/hashes/hash_memory.c +++ b/crypto/userspace/libtomcrypt/hashes/hash_memory.c @@ -44,7 +44,7 @@ int hash_memory(const struct algo_properties_st *hash, const unsigned char *in, return CRYPT_BUFFER_OVERFLOW; } - err = cryptodev_hash_init( &hdata, hash->kstr, 0, NULL, 0); + err = cryptodev_hash_init(&hdata, hash->kstr, NULL, 0); if (err < 0) { err = CRYPT_INVALID_HASH; goto LBL_ERR; diff --git a/crypto/userspace/libtomcrypt/hashes/hash_memory_multi.c b/crypto/userspace/libtomcrypt/hashes/hash_memory_multi.c index a9149166700..74226767a72 100644 --- a/crypto/userspace/libtomcrypt/hashes/hash_memory_multi.c +++ b/crypto/userspace/libtomcrypt/hashes/hash_memory_multi.c @@ -50,7 +50,7 @@ int hash_memory_multi(const struct algo_properties_st *hash, unsigned char *out, return CRYPT_BUFFER_OVERFLOW; } - err = cryptodev_hash_init( &hdata, hash->kstr, 0, NULL, 0); + err = cryptodev_hash_init(&hdata, hash->kstr, NULL, 0); if (err < 0) { err = CRYPT_INVALID_HASH; goto LBL_ERR; diff --git a/crypto/userspace/libtomcrypt/headers/tomcrypt_argchk.h b/crypto/userspace/libtomcrypt/headers/tomcrypt_argchk.h index 1b94434ccb3..1ba08c7ffad 100644 --- a/crypto/userspace/libtomcrypt/headers/tomcrypt_argchk.h +++ b/crypto/userspace/libtomcrypt/headers/tomcrypt_argchk.h @@ -25,8 +25,8 @@ void crypt_argchk(char *v, char *s, int d); #elif ARGTYPE == 4 -#define LTC_ARGCHK(x) if (!(x)) return CRYPT_INVALID_ARG; -#define LTC_ARGCHKVD(x) if (!(x)) return; +#define LTC_ARGCHK(x) if (!(x)) { printk( "\nwarning: ARGCHK failed at %s:%d\n", __FILE__, __LINE__); return CRYPT_INVALID_ARG; } +#define LTC_ARGCHKVD(x) if (!(x)) { printk( "\nwarning: ARGCHK failed at %s:%d\n", __FILE__, __LINE__); return; } #endif diff --git a/crypto/userspace/libtomcrypt/headers/tomcrypt_cfg.h b/crypto/userspace/libtomcrypt/headers/tomcrypt_cfg.h index b750c8be78c..8ad90bce4b0 100644 --- a/crypto/userspace/libtomcrypt/headers/tomcrypt_cfg.h +++ b/crypto/userspace/libtomcrypt/headers/tomcrypt_cfg.h @@ -42,9 +42,9 @@ LTC_EXPORT int LTC_CALL XSTRCMP(const char *s1, const char *s2); #endif -/* type of argument checking, 0=default, 1=fatal and 2=error+continue, 3=nothing */ +/* type of argument checking, 0=default, 1=fatal and 2=error+continue, 3=nothing, 4=return error */ #ifndef ARGTYPE - #define ARGTYPE 0 + #define ARGTYPE 4 #endif /* Controls endianess and size of registers. Leave uncommented to get platform neutral [slower] code diff --git a/crypto/userspace/libtomcrypt/headers/tomcrypt_pk.h b/crypto/userspace/libtomcrypt/headers/tomcrypt_pk.h index 145165efe51..73348805748 100644 --- a/crypto/userspace/libtomcrypt/headers/tomcrypt_pk.h +++ b/crypto/userspace/libtomcrypt/headers/tomcrypt_pk.h @@ -26,7 +26,7 @@ int rand_prime(mp_int *N, long len); /* Min and Max RSA key sizes (in bits) */ #define MIN_RSA_SIZE 1024 -#define MAX_RSA_SIZE 4096 +#define MAX_RSA_SIZE 8192 /** RSA LTC_PKCS style key */ typedef struct Rsa_key { diff --git a/crypto/userspace/libtomcrypt/pk/rsa/rsa_import.c b/crypto/userspace/libtomcrypt/pk/rsa/rsa_import.c index 9f3c5bfa642..87cb1030d47 100644 --- a/crypto/userspace/libtomcrypt/pk/rsa/rsa_import.c +++ b/crypto/userspace/libtomcrypt/pk/rsa/rsa_import.c @@ -84,7 +84,7 @@ int rsa_import(const unsigned char *in, unsigned long inlen, rsa_key *key) } /* it's a private key */ if ((err = der_decode_sequence_multi(in, inlen, - LTC_ASN1_INTEGER, 1UL, zero, + LTC_ASN1_INTEGER, 1UL, &zero, LTC_ASN1_INTEGER, 1UL, &key->N, LTC_ASN1_INTEGER, 1UL, &key->e, LTC_ASN1_INTEGER, 1UL, &key->d, diff --git a/crypto/userspace/ncr-dh.c b/crypto/userspace/ncr-dh.c index 3a00c0bbc38..e743c4b8994 100644 --- a/crypto/userspace/ncr-dh.c +++ b/crypto/userspace/ncr-dh.c @@ -280,3 +280,15 @@ fail: return ret; } + +int ncr_pk_get_dh_size( dh_key* key) +{ +int ret; + ret = mp_count_bits(&key->p); + if (ret <= 0) { + err(); + return -EINVAL; + } + + return ret; +} diff --git a/crypto/userspace/ncr-dh.h b/crypto/userspace/ncr-dh.h index cc45d3206cc..183f7909154 100644 --- a/crypto/userspace/ncr-dh.h +++ b/crypto/userspace/ncr-dh.h @@ -22,4 +22,6 @@ int dh_import(const uint8_t *in, size_t inlen, dh_key *key); int dh_derive_gxy(struct key_item_st* newkey, dh_key * key, void* pk, size_t pk_size); +int ncr_pk_get_dh_size( dh_key* key); + #endif diff --git a/crypto/userspace/ncr-int.h b/crypto/userspace/ncr-int.h index 8574b5ace57..5e40e204512 100644 --- a/crypto/userspace/ncr-int.h +++ b/crypto/userspace/ncr-int.h @@ -1,6 +1,7 @@ #ifndef NCR_INT_H # define NCR_INT_H +#include <linux/compat.h> #include <linux/idr.h> #include <linux/mutex.h> #include <linux/ncr.h> @@ -14,9 +15,33 @@ #define err() printk(KERN_DEBUG"ncr: %s: %s: %d\n", __FILE__, __func__, __LINE__) +struct nlattr; +struct ncr_out; + +// Not all known algorithms - only for quick internal identification. Note +// that more than one struct algo_properties_st may share the same enum value! +enum ncr_algorithm { + NCR_ALG_NONE__, + NCR_ALG_NULL, + + NCR_ALG_3DES_CBC, + + NCR_ALG_MD5, + NCR_ALG_SHA1, + NCR_ALG_SHA2_224, + NCR_ALG_SHA2_256, + NCR_ALG_SHA2_384, + NCR_ALG_SHA2_512, + + NCR_ALG_RSA, + NCR_ALG_DSA, + NCR_ALG_DH, +}; + struct algo_properties_st { - ncr_algorithm_t algo; + enum ncr_algorithm algo; const char *kstr; + size_t kstr_len; unsigned needs_iv:1; unsigned is_hmac:1; unsigned can_sign:1; @@ -25,6 +50,7 @@ struct algo_properties_st { unsigned can_kx:1; /* key exchange */ unsigned is_symmetric:1; unsigned is_pk:1; + unsigned has_transparent_hash:1; int digest_size; /* NCR_KEY_TYPE_SECRET if for a secret key algorithm or MAC, * NCR_KEY_TYPE_PUBLIC for a public key algorithm. @@ -32,32 +58,6 @@ struct algo_properties_st { ncr_key_type_t key_type; }; -struct session_item_st { - const struct algo_properties_st *algorithm; - ncr_crypto_op_t op; - - /* contexts for various options. - * simpler to have them like that than - * in a union. - */ - struct cipher_data cipher; - struct ncr_pk_ctx pk; - struct hash_data hash; - - struct scatterlist *sg; - struct page **pages; - unsigned array_size; - unsigned available_pages; - struct mutex mem_mutex; /* down when the - * values above are changed. - */ - - struct key_item_st* key; - - atomic_t refcnt; - ncr_session_t desc; -}; - struct key_item_st { /* This object is also not protected from concurrent access. */ @@ -104,21 +104,33 @@ struct ncr_lists { void* ncr_init_lists(void); void ncr_deinit_lists(struct ncr_lists *lst); -int ncr_ioctl(struct ncr_lists *lst, unsigned int cmd, unsigned long arg); +long ncr_ioctl(struct ncr_lists *lst, unsigned int cmd, unsigned long arg); +long ncr_compat_ioctl(struct ncr_lists *lst, unsigned int cmd, + unsigned long arg); /* key derivation */ -int ncr_key_derive(struct ncr_lists *lst, void __user* arg); +int ncr_key_derive(struct ncr_lists *lst, const struct ncr_key_derive *data, + struct nlattr *tb[]); + +void ncr_key_clear(struct key_item_st* item); +int ncr_key_update_flags(struct key_item_st *item, const struct nlattr *nla); /* key handling */ -int ncr_key_init(struct ncr_lists *lst, void __user* arg); -int ncr_key_deinit(struct ncr_lists *lst, void __user* arg); -int ncr_key_export(struct ncr_lists *lst, void __user* arg); -int ncr_key_import(struct ncr_lists *lst, void __user* arg); +int ncr_key_init(struct ncr_lists *lst); +int ncr_key_deinit(struct ncr_lists *lst, ncr_key_t desc); +int ncr_key_export(struct ncr_lists *lst, const struct ncr_key_export *data, + struct nlattr *tb[]); +int ncr_key_import(struct ncr_lists *lst, const struct ncr_key_import *data, + struct nlattr *tb[]); void ncr_key_list_deinit(struct ncr_lists *lst); -int ncr_key_generate(struct ncr_lists *lst, void __user* arg); -int ncr_key_info(struct ncr_lists *lst, void __user* arg); - -int ncr_key_generate_pair(struct ncr_lists *lst, void __user* arg); +int ncr_key_generate(struct ncr_lists *lst, const struct ncr_key_generate *gen, + struct nlattr *tb[]); +int ncr_key_get_info(struct ncr_lists *lst, struct ncr_out *out, + const struct ncr_key_get_info *info, struct nlattr *tb[]); + +int ncr_key_generate_pair(struct ncr_lists *lst, + const struct ncr_key_generate_pair *gen, + struct nlattr *tb[]); int ncr_key_get_public(struct ncr_lists *lst, void __user* arg); int ncr_key_item_get_read(struct key_item_st**st, struct ncr_lists *lst, @@ -138,21 +150,32 @@ int ncr_limits_add_and_check(uid_t uid, pid_t pid, limits_type_t type); void ncr_limits_init(void); void ncr_limits_deinit(void); -int ncr_key_wrap(struct ncr_lists *lst, void __user* arg); -int ncr_key_unwrap(struct ncr_lists *lst, void __user* arg); -int ncr_key_storage_wrap(struct ncr_lists *lst, void __user* arg); -int ncr_key_storage_unwrap(struct ncr_lists *lst, void __user* arg); +int ncr_key_wrap(struct ncr_lists *lst, const struct ncr_key_wrap *wrap, + struct nlattr *tb[]); +int ncr_key_unwrap(struct ncr_lists *lst, const struct ncr_key_unwrap *wrap, + struct nlattr *tb[]); +int ncr_key_storage_wrap(struct ncr_lists *lst, + const struct ncr_key_storage_wrap *wrap, + struct nlattr *tb[]); +int ncr_key_storage_unwrap(struct ncr_lists *lst, + const struct ncr_key_storage_unwrap *wrap, + struct nlattr *tb[]); /* sessions */ -struct session_item_st* ncr_session_new(struct ncr_lists *lst); -void _ncr_sessions_item_put( struct session_item_st* item); -struct session_item_st* ncr_sessions_item_get(struct ncr_lists *lst, ncr_session_t desc); void ncr_sessions_list_deinit(struct ncr_lists *lst); -int ncr_session_init(struct ncr_lists* lists, void __user* arg); -int ncr_session_update(struct ncr_lists* lists, void __user* arg); -int ncr_session_final(struct ncr_lists* lists, void __user* arg); -int ncr_session_once(struct ncr_lists* lists, void __user* arg); +int ncr_session_init(struct ncr_lists *lists, + const struct ncr_session_init *session, + struct nlattr *tb[]); +int ncr_session_update(struct ncr_lists *lists, + const struct ncr_session_update *op, struct nlattr *tb[], + int compat); +int ncr_session_final(struct ncr_lists *lists, + const struct ncr_session_final *op, struct nlattr *tb[], + int compat); +int ncr_session_once(struct ncr_lists *lists, + const struct ncr_session_once *once, struct nlattr *tb[], + int compat); /* master key */ extern struct key_item_st master_key; @@ -166,7 +189,32 @@ int key_to_storage_data( uint8_t** data, size_t * data_size, const struct key_it /* misc helper macros */ -const struct algo_properties_st *_ncr_algo_to_properties(ncr_algorithm_t algo); -const struct algo_properties_st *ncr_key_params_get_sign_hash(const struct algo_properties_st *algo, struct ncr_key_params_st * params); +const struct algo_properties_st *_ncr_algo_to_properties(const char *algo); +const struct algo_properties_st *_ncr_nla_to_properties(const struct nlattr *nla); +int _ncr_key_get_sec_level(struct key_item_st* item); + +/* CONFIG_COMPAT handling */ + +#ifdef CONFIG_COMPAT +struct compat_ncr_session_input_data { + compat_uptr_t data; + compat_size_t data_size; +}; + +struct compat_ncr_session_output_buffer { + compat_uptr_t buffer; + compat_size_t buffer_size; + compat_uptr_t result_size_ptr; +}; +#endif + +int ncr_session_input_data_from_nla(struct ncr_session_input_data *dest, + const struct nlattr *nla, int compat); + +int ncr_session_output_buffer_from_nla(struct ncr_session_output_buffer *dest, + const struct nlattr *nla, int compat); + +int ncr_session_output_buffer_set_size(const struct ncr_session_output_buffer *dest, + size_t size, int compat); #endif diff --git a/crypto/userspace/ncr-key-storage.c b/crypto/userspace/ncr-key-storage.c index 0db4eeddfff..4d0cb872153 100644 --- a/crypto/userspace/ncr-key-storage.c +++ b/crypto/userspace/ncr-key-storage.c @@ -32,9 +32,10 @@ #include "cryptodev_int.h" struct packed_key { + uint32_t version; uint8_t type; uint32_t flags; - uint16_t algorithm; /* valid for public/private keys */ + uint8_t algorithm[32]; /* NUL-terminated */ uint8_t key_id[MAX_KEY_ID_SIZE]; uint8_t key_id_size; @@ -42,6 +43,8 @@ struct packed_key { uint32_t raw_size; } __attribute__((__packed__)); +#define THIS_VERSION 1 + int key_to_storage_data( uint8_t** sdata, size_t * sdata_size, const struct key_item_st *key) { struct packed_key * pkey; @@ -53,9 +56,11 @@ int key_to_storage_data( uint8_t** sdata, size_t * sdata_size, const struct key_ return -ENOMEM; } + pkey->version = THIS_VERSION; pkey->type = key->type; pkey->flags = key->flags; - pkey->algorithm = key->algorithm->algo; + BUG_ON(strlen(key->algorithm->kstr) > sizeof(pkey->algorithm) - 1); + strcpy(pkey->algorithm, key->algorithm->kstr); pkey->key_id_size = key->key_id_size; memcpy(pkey->key_id, key->key_id, key->key_id_size); @@ -90,7 +95,9 @@ int key_from_storage_data(struct key_item_st* key, const void* data, size_t data const struct packed_key * pkey = data; int ret; - if (data_size != sizeof(*pkey) || pkey->key_id_size > MAX_KEY_ID_SIZE) { + if (data_size != sizeof(*pkey) || pkey->version != THIS_VERSION + || memchr(pkey->algorithm, '\0', sizeof(pkey->algorithm)) == NULL + || pkey->key_id_size > MAX_KEY_ID_SIZE) { err(); return -EINVAL; } diff --git a/crypto/userspace/ncr-key-wrap.c b/crypto/userspace/ncr-key-wrap.c index e36a7db0080..17be632fb0e 100644 --- a/crypto/userspace/ncr-key-wrap.c +++ b/crypto/userspace/ncr-key-wrap.c @@ -30,13 +30,20 @@ #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 + 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) { @@ -55,7 +62,7 @@ int i,j; if (*output_size < (n+1)*8) { err(); - return -EINVAL; + return -ERANGE; } memcpy(A, iv, 8); @@ -258,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; @@ -357,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; @@ -412,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; @@ -429,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; @@ -455,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(); @@ -469,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) { @@ -488,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); @@ -512,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(); @@ -546,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: @@ -574,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; @@ -589,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(); @@ -626,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); @@ -647,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; @@ -664,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(); @@ -683,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; @@ -697,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(); @@ -719,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; +} diff --git a/crypto/userspace/ncr-key.c b/crypto/userspace/ncr-key.c index 82fa5113662..de2a4ab70f1 100644 --- a/crypto/userspace/ncr-key.c +++ b/crypto/userspace/ncr-key.c @@ -29,9 +29,9 @@ #include <linux/random.h> #include <linux/uaccess.h> #include <linux/scatterlist.h> +#include <net/netlink.h> #include "ncr-int.h" - -static void ncr_key_clear(struct key_item_st* item); +#include "utils.h" static int key_list_deinit_fn(int id, void *item, void *unused) { @@ -150,7 +150,7 @@ static void _ncr_key_remove(struct ncr_lists *lst, ncr_key_t desc) _ncr_key_item_put(item); } -int ncr_key_init(struct ncr_lists *lst, void __user* arg) +int ncr_key_init(struct ncr_lists *lst) { ncr_key_t desc; struct key_item_st* key; @@ -189,49 +189,33 @@ int ncr_key_init(struct ncr_lists *lst, void __user* arg) desc = key->desc; mutex_unlock(&lst->key_idr_mutex); - ret = copy_to_user(arg, &desc, sizeof(desc)); - if (unlikely(ret)) { - _ncr_key_remove(lst, desc); - return -EFAULT; - } - return ret; + return desc; err_limits: ncr_limits_remove(current_euid(), task_pid_nr(current), LIMIT_TYPE_KEY); return ret; } -int ncr_key_deinit(struct ncr_lists *lst, void __user* arg) +int ncr_key_deinit(struct ncr_lists *lst, ncr_key_t desc) { - ncr_key_t desc; - - if (unlikely(copy_from_user(&desc, arg, sizeof(desc)))) { - err(); - return -EFAULT; - } - _ncr_key_remove(lst, desc); - return 0; } -/* "exports" a key to a data item. If the key is not exportable - * to userspace then the data item will also not be. - */ -int ncr_key_export(struct ncr_lists *lst, void __user* arg) +int ncr_key_export(struct ncr_lists *lst, const struct ncr_key_export *data, + struct nlattr *tb[]) { -struct ncr_key_data_st data; struct key_item_st* item = NULL; void* tmp = NULL; uint32_t tmp_size; int ret; - if (unlikely(copy_from_user(&data, arg, sizeof(data)))) { + if (data->buffer_size < 0) { err(); - return -EFAULT; + return -EINVAL; } - ret = ncr_key_item_get_read( &item, lst, data.key); + ret = ncr_key_item_get_read(&item, lst, data->key); if (ret < 0) { err(); return ret; @@ -245,15 +229,15 @@ int ret; switch (item->type) { case NCR_KEY_TYPE_SECRET: - if (item->key.secret.size > data.idata_size) { + if (item->key.secret.size > data->buffer_size) { err(); - ret = -EINVAL; + ret = -ERANGE; goto fail; } /* found */ if (item->key.secret.size > 0) { - ret = copy_to_user(data.idata, item->key.secret.data, item->key.secret.size); + ret = copy_to_user(data->buffer, item->key.secret.data, item->key.secret.size); if (unlikely(ret)) { err(); ret = -EFAULT; @@ -261,11 +245,11 @@ int ret; } } - data.idata_size = item->key.secret.size; + ret = item->key.secret.size; break; case NCR_KEY_TYPE_PUBLIC: case NCR_KEY_TYPE_PRIVATE: - tmp_size = data.idata_size; + tmp_size = data->buffer_size; tmp = kmalloc(tmp_size, GFP_KERNEL); if (tmp == NULL) { @@ -275,20 +259,19 @@ int ret; } ret = ncr_pk_pack(item, tmp, &tmp_size); - data.idata_size = tmp_size; - if (ret < 0) { err(); goto fail; } - ret = copy_to_user(data.idata, tmp, tmp_size); + ret = copy_to_user(data->buffer, tmp, tmp_size); if (unlikely(ret)) { err(); ret = -EFAULT; goto fail; } + ret = tmp_size; break; default: err(); @@ -296,12 +279,6 @@ int ret; goto fail; } - if (unlikely(copy_to_user(arg, &data, sizeof(data)))) { - err(); - ret = -EFAULT; - } else - ret = 0; - fail: kfree(tmp); if (item) @@ -310,23 +287,30 @@ fail: } -/* "imports" a key from a data item. If the key is not exportable - * to userspace then the key item will also not be. - */ -int ncr_key_import(struct ncr_lists *lst, void __user* arg) +int ncr_key_update_flags(struct key_item_st* item, const struct nlattr *nla) +{ + uint32_t flags; + + if (nla == NULL) + return 0; + flags = nla_get_u32(nla); + if (!capable(CAP_SYS_ADMIN) + && (flags & (NCR_KEY_FLAG_WRAPPING | NCR_KEY_FLAG_UNWRAPPING)) != 0) + return -EPERM; + item->flags = flags; + return 0; +} + +int ncr_key_import(struct ncr_lists *lst, const struct ncr_key_import *data, + struct nlattr *tb[]) { -struct ncr_key_data_st data; +const struct nlattr *nla; struct key_item_st* item = NULL; int ret; void* tmp = NULL; size_t tmp_size; - if (unlikely(copy_from_user(&data, arg, sizeof(data)))) { - err(); - return -EFAULT; - } - - ret = ncr_key_item_get_write( &item, lst, data.key); + ret = ncr_key_item_get_write( &item, lst, data->key); if (ret < 0) { err(); return ret; @@ -334,42 +318,55 @@ size_t tmp_size; ncr_key_clear(item); - tmp = kmalloc(data.idata_size, GFP_KERNEL); + tmp = kmalloc(data->data_size, GFP_KERNEL); if (tmp == NULL) { err(); ret = -ENOMEM; goto fail; } - if (unlikely(copy_from_user(tmp, data.idata, data.idata_size))) { + if (unlikely(copy_from_user(tmp, data->data, data->data_size))) { err(); ret = -EFAULT; goto fail; } - tmp_size = data.idata_size; - - item->type = data.type; - item->algorithm = _ncr_algo_to_properties(data.algorithm); - if (item->algorithm == NULL) { + tmp_size = data->data_size; + + nla = tb[NCR_ATTR_KEY_TYPE]; + if (tb == NULL) { err(); ret = -EINVAL; goto fail; } - item->flags = data.flags; + item->type = nla_get_u32(nla); - if (data.key_id_size > MAX_KEY_ID_SIZE) { + item->algorithm = _ncr_nla_to_properties(tb[NCR_ATTR_ALGORITHM]); + if (item->algorithm == NULL) { err(); ret = -EINVAL; goto fail; } - item->key_id_size = data.key_id_size; - if (data.key_id_size > 0) - memcpy(item->key_id, data.key_id, data.key_id_size); + ret = ncr_key_update_flags(item, tb[NCR_ATTR_KEY_FLAGS]); + if (ret < 0) { + err(); + goto fail; + } + + nla = tb[NCR_ATTR_KEY_ID]; + if (nla != NULL) { + if (nla_len(nla) > MAX_KEY_ID_SIZE) { + err(); + ret = -EOVERFLOW; + goto fail; + } + + item->key_id_size = nla_len(nla); + memcpy(item->key_id, nla_data(nla), item->key_id_size); + } switch(item->type) { case NCR_KEY_TYPE_SECRET: - if (tmp_size > NCR_CIPHER_MAX_KEY_LEN) { err(); ret = -EINVAL; @@ -404,7 +401,7 @@ fail: return ret; } -static void ncr_key_clear(struct key_item_st* item) +void ncr_key_clear(struct key_item_st* item) { /* clears any previously allocated parameters */ if (item->type == NCR_KEY_TYPE_PRIVATE || @@ -422,20 +419,16 @@ static void ncr_key_clear(struct key_item_st* item) /* Generate a secret key */ -int ncr_key_generate(struct ncr_lists *lst, void __user* arg) +int ncr_key_generate(struct ncr_lists *lst, const struct ncr_key_generate *gen, + struct nlattr *tb[]) { -struct ncr_key_generate_st gen; +const struct nlattr *nla; struct key_item_st* item = NULL; const struct algo_properties_st *algo; int ret; size_t size; - if (unlikely(copy_from_user(&gen, arg, sizeof(gen)))) { - err(); - return -EFAULT; - } - - ret = ncr_key_item_get_write( &item, lst, gen.desc); + ret = ncr_key_item_get_write(&item, lst, gen->key); if (ret < 0) { err(); return ret; @@ -444,8 +437,13 @@ size_t size; ncr_key_clear(item); /* we generate only secret keys */ - item->flags = gen.params.keyflags; - algo = _ncr_algo_to_properties(gen.params.algorithm); + ret = ncr_key_update_flags(item, tb[NCR_ATTR_KEY_FLAGS]); + if (ret < 0) { + err(); + goto fail; + } + + algo = _ncr_nla_to_properties(tb[NCR_ATTR_ALGORITHM]); if (algo == NULL) { err(); ret = -EINVAL; @@ -453,11 +451,19 @@ size_t size; } item->type = algo->key_type; if (item->type == NCR_KEY_TYPE_SECRET) { + u32 key_bits; + item->algorithm = algo; - size = gen.params.params.secret.bits/8; - if ((gen.params.params.secret.bits % 8 != 0) || - (size > NCR_CIPHER_MAX_KEY_LEN)) { + nla = tb[NCR_ATTR_SECRET_KEY_BITS]; + if (nla == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + key_bits = nla_get_u32(nla); + size = key_bits / 8; + if (key_bits % 8 != 0 || size > NCR_CIPHER_MAX_KEY_LEN) { err(); ret = -EINVAL; goto fail; @@ -485,18 +491,120 @@ fail: return ret; } -int ncr_key_info(struct ncr_lists *lst, void __user* arg) +/* Those values are derived from "ECRYPT II Yearly Report on Algorithms and + * Keysizes (2009-2010)". It maps the strength of public key algorithms to + * symmetric ones. Should be kept up to date. + */ +static const struct { + unsigned int bits; /* sec level */ + unsigned int rsa_bits; + unsigned int dlog_bits; +} ecrypt_vals[] = { + {64, 816, 816}, + {80, 1248, 1248}, + {112, 2432, 2432}, + {128, 3248, 3248}, + {160, 5312, 5312}, + {192, 7936, 7936}, + {256, 15424, 15424}, + {0,0,0} +}; + +static unsigned int rsa_to_bits(unsigned int rsa_bits) { -struct ncr_key_info_st info; -struct key_item_st* item = NULL; -int ret; +int i = 1; - if (unlikely(copy_from_user(&info, arg, sizeof(info)))) { - err(); - return -EFAULT; + if (rsa_bits <= ecrypt_vals[0].rsa_bits) + return ecrypt_vals[0].rsa_bits; + + do { + if (rsa_bits <= ecrypt_vals[i].rsa_bits && + rsa_bits > ecrypt_vals[i-1].rsa_bits) { + + return ecrypt_vals[i].bits; + } + } while(ecrypt_vals[++i].bits != 0); + + /* return the highest found so far */ + return ecrypt_vals[i-1].bits; +} + +static unsigned int dlog_to_bits(unsigned int dlog_bits) +{ +int i = 1; + + if (dlog_bits <= ecrypt_vals[0].dlog_bits) + return ecrypt_vals[0].dlog_bits; + + do { + if (dlog_bits <= ecrypt_vals[i].dlog_bits && + dlog_bits > ecrypt_vals[i-1].dlog_bits) { + + return ecrypt_vals[i].bits; + } + } while(ecrypt_vals[++i].bits != 0); + + /* return the highest found so far */ + return ecrypt_vals[i-1].bits; +} + +/* returns the security level of the key in bits. Private/Public keys + * are mapped to symmetric key bits using the ECRYPT II 2010 recommendation. + */ +int _ncr_key_get_sec_level(struct key_item_st* item) +{ +int bits; + + /* FIXME: should we move everything here into algorithm properties? + */ + if (item->type == NCR_KEY_TYPE_SECRET) { + if (item->algorithm->algo == NCR_ALG_3DES_CBC) + return 112; + + return item->key.secret.size*8; + } else if (item->type == NCR_KEY_TYPE_PRIVATE) { + switch(item->algorithm->algo) { + case NCR_ALG_RSA: + bits = ncr_pk_get_rsa_size(&item->key.pk.rsa); + if (bits < 0) { + err(); + return bits; + } + + return rsa_to_bits(bits); + case NCR_ALG_DSA: + bits = ncr_pk_get_dsa_size(&item->key.pk.dsa); + if (bits < 0) { + err(); + return bits; + } + + return dlog_to_bits(bits); + case NCR_ALG_DH: + bits = ncr_pk_get_dh_size(&item->key.pk.dh); + if (bits < 0) { + err(); + return bits; + } + + return dlog_to_bits(bits); + default: + return -EINVAL; + } + } else { + return -EINVAL; } +} - ret = ncr_key_item_get_read(&item, lst, info.key); +int ncr_key_get_info(struct ncr_lists *lst, struct ncr_out *out, + const struct ncr_key_get_info *info, struct nlattr *tb[]) +{ +const struct nlattr *nla; +const u16 *attr, *attr_end; +struct key_item_st* item = NULL; +int ret; + + ret = ncr_key_item_get_read(&item, lst, info->key); if (ret < 0) { err(); return ret; @@ -508,11 +616,41 @@ int ret; goto fail; } - info.flags = item->flags; - info.type = item->type; - info.algorithm = item->algorithm->algo; - - ret = 0; + nla = tb[NCR_ATTR_WANTED_ATTRS]; + if (nla == NULL || nla_len(nla) % sizeof(u16) != 0) { + err(); + ret = -EINVAL; + goto fail; + } + attr = nla_data(nla); + attr_end = attr + nla_len(nla) / sizeof(u16); + while (attr < attr_end) { + switch (*attr) { + case NCR_ATTR_KEY_FLAGS: + ret = ncr_out_put_u32(out, *attr, item->flags); + break; + case NCR_ATTR_KEY_TYPE: + ret = ncr_out_put_u32(out, *attr, item->type); + break; + case NCR_ATTR_ALGORITHM: + ret = ncr_out_put_string(out, *attr, + item->algorithm->kstr); + break; + default: + break; /* Silently ignore */ + } + if (ret != 0) { + err(); + goto fail; + } + attr++; + } + + ret = ncr_out_finish(out); + if (ret != 0) { + err(); + goto fail; + } fail: _ncr_key_item_put( item); @@ -520,25 +658,21 @@ fail: return ret; } -int ncr_key_generate_pair(struct ncr_lists *lst, void __user* arg) +int ncr_key_generate_pair(struct ncr_lists *lst, + const struct ncr_key_generate_pair *gen, + struct nlattr *tb[]) { -struct ncr_key_generate_st gen; struct key_item_st* private = NULL; struct key_item_st* public = NULL; int ret; - if (unlikely(copy_from_user(&gen, arg, sizeof(gen)))) { - err(); - return -EFAULT; - } - - ret = ncr_key_item_get_write( &private, lst, gen.desc); + ret = ncr_key_item_get_write(&private, lst, gen->private_key); if (ret < 0) { err(); goto fail; } - ret = ncr_key_item_get_write( &public, lst, gen.desc2); + ret = ncr_key_item_get_write(&public, lst, gen->public_key); if (ret < 0) { err(); goto fail; @@ -548,8 +682,8 @@ int ret; ncr_key_clear(private); /* we generate only secret keys */ - private->flags = public->flags = gen.params.keyflags; - private->algorithm = public->algorithm = _ncr_algo_to_properties(gen.params.algorithm); + private->algorithm = public->algorithm + = _ncr_nla_to_properties(tb[NCR_ATTR_ALGORITHM]); if (private->algorithm == NULL) { err(); ret = -EINVAL; @@ -557,10 +691,21 @@ int ret; } public->type = public->algorithm->key_type; private->type = NCR_KEY_TYPE_PRIVATE; + ret = ncr_key_update_flags(private, tb[NCR_ATTR_KEY_FLAGS]); + if (ret < 0) { + err(); + goto fail; + } + ret = ncr_key_update_flags(public, tb[NCR_ATTR_KEY_FLAGS]); + if (ret < 0) { + err(); + goto fail; + } + public->flags |= (NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPABLE); if (public->type == NCR_KEY_TYPE_PUBLIC) { - ret = ncr_pk_generate(public->algorithm, &gen.params, private, public); + ret = ncr_pk_generate(public->algorithm, tb, private, public); if (ret < 0) { err(); goto fail; @@ -584,28 +729,28 @@ fail: return ret; } -/* "exports" a key to a data item. If the key is not exportable - * to userspace then the data item will also not be. - */ -int ncr_key_derive(struct ncr_lists *lst, void __user* arg) +int ncr_key_derive(struct ncr_lists *lst, const struct ncr_key_derive *data, + struct nlattr *tb[]) { -struct ncr_key_derivation_params_st data; int ret; struct key_item_st* key = NULL; struct key_item_st* newkey = NULL; - if (unlikely(copy_from_user(&data, arg, sizeof(data)))) { - err(); - return -EFAULT; - } - - ret = ncr_key_item_get_read( &key, lst, data.key); + ret = ncr_key_item_get_read(&key, lst, data->input_key); if (ret < 0) { err(); return ret; } + + /* wrapping keys cannot be used for anything except wrapping. + */ + if (key->flags & NCR_KEY_FLAG_WRAPPING || key->flags & NCR_KEY_FLAG_UNWRAPPING) { + err(); + ret = -EINVAL; + goto fail; + } - ret = ncr_key_item_get_write( &newkey, lst, data.newkey); + ret = ncr_key_item_get_write(&newkey, lst, data->new_key); if (ret < 0) { err(); goto fail; @@ -613,12 +758,16 @@ struct key_item_st* newkey = NULL; ncr_key_clear(newkey); - newkey->flags = data.keyflags; + ret = ncr_key_update_flags(newkey, tb[NCR_ATTR_KEY_FLAGS]); + if (ret < 0) { + err(); + goto fail; + } switch (key->type) { case NCR_KEY_TYPE_PUBLIC: case NCR_KEY_TYPE_PRIVATE: - ret = ncr_pk_derive(newkey, key, &data); + ret = ncr_pk_derive(newkey, key, tb); if (ret < 0) { err(); goto fail; @@ -630,12 +779,6 @@ struct key_item_st* newkey = NULL; goto fail; } - if (unlikely(copy_to_user(arg, &data, sizeof(data)))) { - err(); - ret = -EFAULT; - } else - ret = 0; - fail: if (key) _ncr_key_item_put(key); diff --git a/crypto/userspace/ncr-limits.c b/crypto/userspace/ncr-limits.c index 0a9112515a1..345cca74603 100644 --- a/crypto/userspace/ncr-limits.c +++ b/crypto/userspace/ncr-limits.c @@ -22,7 +22,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include <linux/cryptodev.h> #include <linux/hash.h> #include <linux/mutex.h> #include <linux/types.h> diff --git a/crypto/userspace/ncr-pk.c b/crypto/userspace/ncr-pk.c index 4500495430a..4b2b6aaf770 100644 --- a/crypto/userspace/ncr-pk.c +++ b/crypto/userspace/ncr-pk.c @@ -29,6 +29,7 @@ #include <linux/random.h> #include <linux/uaccess.h> #include <linux/scatterlist.h> +#include <net/netlink.h> #include "ncr-int.h" #include <tomcrypt.h> @@ -36,7 +37,7 @@ int _ncr_tomerr(int err) { switch (err) { case CRYPT_BUFFER_OVERFLOW: - return -EOVERFLOW; + return -ERANGE; case CRYPT_MEM: return -ENOMEM; default: @@ -122,7 +123,8 @@ static int ncr_pk_make_public_and_id( struct key_item_st * private, struct key_i } key_id_size = MAX_KEY_ID_SIZE; - cret = hash_memory(_ncr_algo_to_properties(NCR_ALG_SHA1), tmp, max_size, private->key_id, &key_id_size); + cret = hash_memory(_ncr_algo_to_properties("sha1"), tmp, max_size, + private->key_id, &key_id_size); if (cret != CRYPT_OK) { err(); ret = _ncr_tomerr(cret); @@ -166,8 +168,9 @@ int ncr_pk_pack( const struct key_item_st * key, uint8_t * packed, uint32_t * pa } break; case NCR_ALG_DH: - ret = dh_export(packed, &max_size, key->key.pk.dsa.type, (void*)&key->key.pk.dsa); + ret = dh_export(packed, &max_size, key->key.pk.dh.type, (void*)&key->key.pk.dh); if (ret < 0) { + *packed_size = max_size; err(); return ret; } @@ -221,75 +224,88 @@ int ncr_pk_unpack( struct key_item_st * key, const void * packed, size_t packed_ return 0; } -struct keygen_st { -}; +static int binary_to_ulong(unsigned long *dest, const struct nlattr *nla) +{ + unsigned long value; + const uint8_t *start, *end, *p; + + value = 0; + start = nla_data(nla); + end = start + nla_len(nla); + for (p = start; p < end; p++) { + if (value > (ULONG_MAX - *p) / 256) + return -EOVERFLOW; + value = value * 256 + *p; + } + *dest = value; + return 0; +} -int ncr_pk_generate(const struct algo_properties_st *algo, - struct ncr_key_generate_params_st * params, +int ncr_pk_generate(const struct algo_properties_st *algo, struct nlattr *tb[], struct key_item_st* private, struct key_item_st* public) { + const struct nlattr *nla; unsigned long e; int cret, ret; - uint8_t * tmp = NULL; private->algorithm = public->algorithm = algo; ret = 0; switch(algo->algo) { case NCR_ALG_RSA: - e = params->params.rsa.e; - - if (e == 0) + nla = tb[NCR_ATTR_RSA_E]; + if (nla != NULL) { + ret = binary_to_ulong(&e, nla); + if (ret != 0) + break; + } else e = 65537; - cret = rsa_make_key(params->params.rsa.bits/8, e, &private->key.pk.rsa); + + nla = tb[NCR_ATTR_RSA_MODULUS_BITS]; + if (nla == NULL) { + ret = -EINVAL; + break; + } + cret = rsa_make_key(nla_get_u32(nla) / 8, e, &private->key.pk.rsa); if (cret != CRYPT_OK) { err(); return _ncr_tomerr(cret); } break; - case NCR_ALG_DSA: - if (params->params.dsa.q_bits==0) - params->params.dsa.q_bits = 160; - if (params->params.dsa.p_bits==0) - params->params.dsa.p_bits = 1024; + case NCR_ALG_DSA: { + u32 q_bits, p_bits; - cret = dsa_make_key(params->params.dsa.q_bits/8, - params->params.dsa.p_bits/8, &private->key.pk.dsa); + nla = tb[NCR_ATTR_DSA_Q_BITS]; + if (nla != NULL) + q_bits = nla_get_u32(nla); + else + q_bits = 160; + nla = tb[NCR_ATTR_DSA_P_BITS]; + if (nla != NULL) + p_bits = nla_get_u32(nla); + else + p_bits = 1024; + cret = dsa_make_key(q_bits / 8, p_bits / 8, + &private->key.pk.dsa); if (cret != CRYPT_OK) { err(); return _ncr_tomerr(cret); } break; + } case NCR_ALG_DH: { - uint8_t * p, *g; - size_t p_size, g_size; - - p_size = params->params.dh.p_size; - g_size = params->params.dh.g_size; - - tmp = kmalloc(g_size+p_size, GFP_KERNEL); - if (tmp == NULL) { - err(); - ret = -ENOMEM; - goto fail; - } - - p = tmp; - g = &tmp[p_size]; - - if (unlikely(copy_from_user(p, params->params.dh.p, p_size))) { - err(); - ret = -EFAULT; - goto fail; - } + const struct nlattr *p, *g; - if (unlikely(copy_from_user(g, params->params.dh.g, g_size))) { - err(); - ret = -EFAULT; + p = tb[NCR_ATTR_DH_PRIME]; + g = tb[NCR_ATTR_DH_BASE]; + if (p == NULL || g == NULL) { + ret = -EINVAL; goto fail; } - - ret = dh_import_params(&private->key.pk.dh, p, p_size, g, g_size); + + ret = dh_import_params(&private->key.pk.dh, nla_data(p), + nla_len(p), nla_data(g), + nla_len(g)); if (ret < 0) { err(); goto fail; @@ -308,8 +324,6 @@ int ncr_pk_generate(const struct algo_properties_st *algo, } fail: - kfree(tmp); - if (ret < 0) { err(); return ret; @@ -324,25 +338,6 @@ fail: return 0; } -const struct algo_properties_st *ncr_key_params_get_sign_hash( - const struct algo_properties_st *algo, - struct ncr_key_params_st * params) -{ - ncr_algorithm_t id; - - switch(algo->algo) { - case NCR_ALG_RSA: - id = params->params.rsa.sign_hash; - break; - case NCR_ALG_DSA: - id = params->params.dsa.sign_hash; - break; - default: - return ERR_PTR(-EINVAL); - } - return _ncr_algo_to_properties(id); -} - /* Encryption/Decryption */ @@ -355,12 +350,17 @@ void ncr_pk_cipher_deinit(struct ncr_pk_ctx* ctx) } int ncr_pk_cipher_init(const struct algo_properties_st *algo, - struct ncr_pk_ctx* ctx, struct ncr_key_params_st* params, - struct key_item_st *key, const struct algo_properties_st *sign_hash) + struct ncr_pk_ctx* ctx, struct nlattr *tb[], + struct key_item_st *key, + const struct algo_properties_st *sign_hash) { + const struct nlattr *nla; + memset(ctx, 0, sizeof(*ctx)); - if (key->algorithm != algo) { + /* Allow using the same key for transparent and non-transparent + hashing. */ + if (key->algorithm->algo != algo->algo) { err(); return -EINVAL; } @@ -368,26 +368,38 @@ int ncr_pk_cipher_init(const struct algo_properties_st *algo, ctx->algorithm = algo; ctx->key = key; ctx->sign_hash = sign_hash; + ctx->salt_len = 0; switch(algo->algo) { case NCR_ALG_RSA: - if (params->params.rsa.type == RSA_PKCS1_V1_5) + nla = tb[NCR_ATTR_RSA_ENCODING_METHOD]; + if (nla == NULL) { + err(); + return -EINVAL; + } + switch (nla_get_u32(nla)) { + case RSA_PKCS1_V1_5: ctx->type = LTC_LTC_PKCS_1_V1_5; - else if (params->params.rsa.type == RSA_PKCS1_OAEP) { + break; + case RSA_PKCS1_OAEP: ctx->type = LTC_LTC_PKCS_1_OAEP; - ctx->oaep_hash = _ncr_algo_to_properties(params->params.rsa.oaep_hash); + nla = tb[NCR_ATTR_RSA_OAEP_HASH_ALGORITHM]; + ctx->oaep_hash = _ncr_nla_to_properties(nla); if (ctx->oaep_hash == NULL) { err(); return -EINVAL; } - } else if (params->params.rsa.type == RSA_PKCS1_PSS) { + break; + case RSA_PKCS1_PSS: ctx->type = LTC_LTC_PKCS_1_PSS; - } else { + nla = tb[NCR_ATTR_RSA_PSS_SALT_LENGTH]; + if (nla != NULL) + ctx->salt_len = nla_get_u32(nla); + break; + default: err(); return -EINVAL; } - - ctx->salt_len = params->params.rsa.pss_salt; break; case NCR_ALG_DSA: break; @@ -530,30 +542,11 @@ fail: return ret; } -int ncr_pk_cipher_sign(const struct ncr_pk_ctx* ctx, - const struct scatterlist* isg, unsigned int isg_cnt, size_t isg_size, - struct scatterlist *osg, unsigned int osg_cnt, size_t* osg_size) +int ncr_pk_cipher_sign(const struct ncr_pk_ctx *ctx, const void *hash, + size_t hash_size, void *sig, size_t *sig_size) { -int cret, ret; -unsigned long osize = *osg_size; -uint8_t* tmp; -void * input, *output; - - tmp = kmalloc(isg_size + *osg_size, GFP_KERNEL); - if (tmp == NULL) { - err(); - return -ENOMEM; - } - - input = tmp; - output = &tmp[isg_size]; - - ret = sg_copy_to_buffer((struct scatterlist*)isg, isg_cnt, input, isg_size); - if (ret != isg_size) { - err(); - ret = -EINVAL; - goto fail; - } + int cret; + unsigned long osize = *sig_size; switch(ctx->algorithm->algo) { case NCR_ALG_RSA: @@ -561,63 +554,35 @@ void * input, *output; err(); return -EINVAL; } - cret = rsa_sign_hash_ex( input, isg_size, output, &osize, + cret = rsa_sign_hash_ex(hash, hash_size, sig, &osize, ctx->type, ctx->sign_hash, ctx->salt_len, &ctx->key->key.pk.rsa); if (cret != CRYPT_OK) { err(); return _ncr_tomerr(cret); } - *osg_size = osize; + *sig_size = osize; break; case NCR_ALG_DSA: - cret = dsa_sign_hash( input, isg_size, output, &osize, + cret = dsa_sign_hash(hash, hash_size, sig, &osize, &ctx->key->key.pk.dsa); if (cret != CRYPT_OK) { err(); return _ncr_tomerr(cret); } - *osg_size = osize; + *sig_size = osize; break; default: err(); - ret = -EINVAL; - goto fail; - } - - ret = sg_copy_from_buffer(osg, osg_cnt, output, *osg_size); - if (ret != *osg_size) { - err(); - ret = -EINVAL; - goto fail; + return -EINVAL; } - ret = 0; -fail: - kfree(tmp); - - return ret; + return 0; } -int ncr_pk_cipher_verify(const struct ncr_pk_ctx* ctx, - const struct scatterlist* sign_sg, unsigned int sign_sg_cnt, size_t sign_sg_size, - const void* hash, size_t hash_size, ncr_error_t* err) +int ncr_pk_cipher_verify(const struct ncr_pk_ctx* ctx, const void *sig, + size_t sig_size, const void *hash, size_t hash_size) { -int cret, ret; -int stat = 0; -uint8_t* sig; - - sig = kmalloc(sign_sg_size, GFP_KERNEL); - if (sig == NULL) { - err(); - return -ENOMEM; - } - - ret = sg_copy_to_buffer((struct scatterlist*)sign_sg, sign_sg_cnt, sig, sign_sg_size); - if (ret != sign_sg_size) { - err(); - ret = -EINVAL; - goto fail; - } + int cret, ret, stat; switch(ctx->algorithm->algo) { case NCR_ALG_RSA: @@ -625,35 +590,28 @@ uint8_t* sig; err(); return -EINVAL; } - cret = rsa_verify_hash_ex( sig, sign_sg_size, - hash, hash_size, ctx->type, ctx->sign_hash, - ctx->salt_len, &stat, &ctx->key->key.pk.rsa); + cret = rsa_verify_hash_ex(sig, sig_size, hash, + hash_size, ctx->type, + ctx->sign_hash, ctx->salt_len, + &stat, &ctx->key->key.pk.rsa); if (cret != CRYPT_OK) { err(); ret = _ncr_tomerr(cret); goto fail; } - if (stat == 1) - *err = 0; - else - *err = NCR_VERIFICATION_FAILED; - + ret = (stat == 1); break; case NCR_ALG_DSA: - cret = dsa_verify_hash( sig, sign_sg_size, - hash, hash_size, &stat, &ctx->key->key.pk.dsa); + cret = dsa_verify_hash(sig, sig_size, hash, hash_size, + &stat, &ctx->key->key.pk.dsa); if (cret != CRYPT_OK) { err(); ret = _ncr_tomerr(cret); goto fail; } - if (stat == 1) - *err = 0; - else - *err = NCR_VERIFICATION_FAILED; - + ret = (stat == 1); break; default: err(); @@ -661,55 +619,67 @@ uint8_t* sig; goto fail; } - ret = 0; fail: - kfree(sig); return ret; } int ncr_pk_derive(struct key_item_st* newkey, struct key_item_st* oldkey, - struct ncr_key_derivation_params_st * params) + struct nlattr *tb[]) { +const struct nlattr *nla; int ret; -void* tmp = NULL; -size_t size; - switch(params->derive) { - case NCR_DERIVE_DH: - if (oldkey->type != NCR_KEY_TYPE_PRIVATE && - oldkey->algorithm->algo != NCR_ALG_DH) { - err(); - return -EINVAL; - } - - size = params->params.params.dh.pub_size; - tmp = kmalloc(size, GFP_KERNEL); - if (tmp == NULL) { - err(); - return -ENOMEM; - } - - if (unlikely(copy_from_user(tmp, params->params.params.dh.pub, - size))) { - err(); - ret = -EFAULT; - goto fail; - } - - ret = dh_derive_gxy(newkey, &oldkey->key.pk.dh, tmp, size); - if (ret < 0) { - err(); - goto fail; - } - - break; - default: + nla = tb[NCR_ATTR_DERIVATION_ALGORITHM]; + if (nla == NULL) { + err(); + return -EINVAL; + } + if (nla_strcmp(nla, NCR_DERIVE_DH) == 0) { + if (oldkey->type != NCR_KEY_TYPE_PRIVATE && + oldkey->algorithm->algo != NCR_ALG_DH) { + err(); + return -EINVAL; + } + + nla = tb[NCR_ATTR_DH_PUBLIC]; + if (nla == NULL) { err(); return -EINVAL; + } + ret = dh_derive_gxy(newkey, &oldkey->key.pk.dh, nla_data(nla), + nla_len(nla)); + if (ret < 0) { + err(); + return ret; + } + } else { + err(); + return -EINVAL; } - ret = 0; -fail: - kfree(tmp); + return 0; +} + +int ncr_pk_get_rsa_size( rsa_key* key) +{ +int ret; + ret = mp_count_bits(&key->N); + if (ret <= 0) { + err(); + return -EINVAL; + } + + return ret; +} + +int ncr_pk_get_dsa_size( dsa_key* key) +{ +int ret; + ret = mp_count_bits(&key->p); + if (ret <= 0) { + err(); + return -EINVAL; + } + return ret; } diff --git a/crypto/userspace/ncr-pk.h b/crypto/userspace/ncr-pk.h index 25a057120f9..fb9aba5b5ee 100644 --- a/crypto/userspace/ncr-pk.h +++ b/crypto/userspace/ncr-pk.h @@ -3,6 +3,8 @@ #include <tomcrypt.h> +struct nlattr; + struct ncr_pk_ctx { const struct algo_properties_st *algorithm; /* algorithm */ @@ -19,16 +21,16 @@ struct ncr_pk_ctx { /* PK */ void ncr_pk_clear(struct key_item_st* key); -int ncr_pk_generate(const struct algo_properties_st *algo, - struct ncr_key_generate_params_st * params, +int ncr_pk_generate(const struct algo_properties_st *algo, struct nlattr *tb[], struct key_item_st* private, struct key_item_st* public); int ncr_pk_pack( const struct key_item_st * key, uint8_t * packed, uint32_t * packed_size); int ncr_pk_unpack( struct key_item_st * key, const void * packed, size_t packed_size); /* encryption/decryption */ int ncr_pk_cipher_init(const struct algo_properties_st *algo, - struct ncr_pk_ctx* ctx, struct ncr_key_params_st* params, - struct key_item_st *key, const struct algo_properties_st *sign_hash); + struct ncr_pk_ctx* ctx, struct nlattr *tb[], + struct key_item_st *key, + const struct algo_properties_st *sign_hash); void ncr_pk_cipher_deinit(struct ncr_pk_ctx* ctx); int ncr_pk_cipher_encrypt(const struct ncr_pk_ctx* ctx, @@ -39,17 +41,19 @@ int ncr_pk_cipher_decrypt(const struct ncr_pk_ctx* ctx, const struct scatterlist* isg, unsigned int isg_cnt, size_t isg_size, struct scatterlist *osg, unsigned int osg_cnt, size_t* osg_size); -int ncr_pk_cipher_sign(const struct ncr_pk_ctx* ctx, - const struct scatterlist* isg, unsigned int isg_cnt, size_t isg_size, - struct scatterlist *osg, unsigned int osg_cnt, size_t* osg_size); +int ncr_pk_cipher_sign(const struct ncr_pk_ctx *ctx, const void *hash, + size_t hash_size, void *sig, size_t *sig_size); -int ncr_pk_cipher_verify(const struct ncr_pk_ctx* ctx, - const struct scatterlist* sign_sg, unsigned int sign_sg_cnt, size_t sign_sg_size, - const void* hash, size_t hash_size, ncr_error_t* err); +int ncr_pk_cipher_verify(const struct ncr_pk_ctx* ctx, const void *sig, + size_t sig_size, const void *hash, size_t hash_size); int _ncr_tomerr(int err); int ncr_pk_derive(struct key_item_st* newkey, struct key_item_st* oldkey, - struct ncr_key_derivation_params_st * params); + struct nlattr *tb[]); + +int ncr_pk_get_rsa_size( rsa_key* key); +int ncr_pk_get_dsa_size( dsa_key* key); + #endif diff --git a/crypto/userspace/ncr-sessions.c b/crypto/userspace/ncr-sessions.c index f3d3590359f..e4af261dca5 100644 --- a/crypto/userspace/ncr-sessions.c +++ b/crypto/userspace/ncr-sessions.c @@ -29,9 +29,42 @@ #include "ncr-int.h" #include <linux/mm_types.h> #include <linux/scatterlist.h> +#include <net/netlink.h> + +struct session_item_st { + atomic_t refcnt; + /* Constant values throughout the life of this object follow. */ + ncr_session_t desc; + const struct algo_properties_st *algorithm; + ncr_crypto_op_t op; + struct key_item_st *key; + + /* Variable values, protected usually by mutex, follow. */ + struct mutex mutex; + + /* contexts for various options. + * simpler to have them like that than + * in a union. + */ + struct cipher_data cipher; + struct ncr_pk_ctx pk; + struct hash_data hash; + /* This is a hack, ideally we'd have a hash algorithm that simply + outputs its input as a digest. We'd still need to distinguish + between the hash to identify in the signature and the hash to + actually use, though. */ + void *transparent_hash; -static int _ncr_session_update_key(struct ncr_lists* lists, struct ncr_session_op_st* op); -static void _ncr_session_remove(struct ncr_lists *lst, ncr_session_t desc); + struct scatterlist *sg; + struct page **pages; + unsigned array_size; + unsigned available_pages; +}; + +static void _ncr_sessions_item_put(struct session_item_st *item); +static int _ncr_session_update_key(struct ncr_lists *lists, + struct session_item_st *sess, + struct nlattr *tb[]); static int session_list_deinit_fn(int id, void *item, void *unused) { @@ -51,12 +84,57 @@ void ncr_sessions_list_deinit(struct ncr_lists *lst) mutex_unlock(&lst->session_idr_mutex); } +/* Allocate a descriptor without making a sesssion available to userspace. */ +static ncr_session_t session_alloc_desc(struct ncr_lists *lst) +{ + int ret, desc; + + mutex_lock(&lst->session_idr_mutex); + if (idr_pre_get(&lst->session_idr, GFP_KERNEL) == 0) { + ret = -ENOMEM; + goto end; + } + /* idr_pre_get() should preallocate enough, and, due to + session_idr_mutex, nobody else can use the preallocated data. + Therefore the loop recommended in idr_get_new() documentation is not + necessary. */ + ret = idr_get_new(&lst->session_idr, NULL, &desc); + if (ret != 0) + goto end; + ret = desc; +end: + mutex_unlock(&lst->session_idr_mutex); + return ret; +} + +/* Drop a pre-allocated, unpublished session descriptor */ +static void session_drop_desc(struct ncr_lists *lst, ncr_session_t desc) +{ + mutex_lock(&lst->session_idr_mutex); + idr_remove(&lst->session_idr, desc); + mutex_unlock(&lst->session_idr_mutex); +} + +/* Make a session descriptor visible in user-space, stealing the reference */ +static void session_publish_ref(struct ncr_lists *lst, + struct session_item_st *sess) +{ + void *old; + + mutex_lock(&lst->session_idr_mutex); + old = idr_replace(&lst->session_idr, sess, sess->desc); + mutex_unlock(&lst->session_idr_mutex); + BUG_ON(old != NULL); +} + /* returns the data item corresponding to desc */ -struct session_item_st* ncr_sessions_item_get(struct ncr_lists *lst, ncr_session_t desc) +static struct session_item_st *session_get_ref(struct ncr_lists *lst, + ncr_session_t desc) { struct session_item_st* item; mutex_lock(&lst->session_idr_mutex); + /* item may be NULL for pre-allocated session IDs. */ item = idr_find(&lst->session_idr, desc); if (item != NULL) { atomic_inc(&item->refcnt); @@ -69,12 +147,30 @@ struct session_item_st* item; return NULL; } -void _ncr_sessions_item_put( struct session_item_st* item) +/* Find a session, stealing the reference, but keep the descriptor allocated. */ +static struct session_item_st *session_unpublish_ref(struct ncr_lists *lst, + ncr_session_t desc) +{ + struct session_item_st *sess; + + mutex_lock(&lst->session_idr_mutex); + /* sess may be NULL for pre-allocated session IDs. */ + sess = idr_replace(&lst->session_idr, NULL, desc); + mutex_unlock(&lst->session_idr_mutex); + if (sess != NULL && !IS_ERR(sess)) + return sess; + + err(); + return NULL; +} + +static void _ncr_sessions_item_put(struct session_item_st *item) { if (atomic_dec_and_test(&item->refcnt)) { cryptodev_cipher_deinit(&item->cipher); ncr_pk_cipher_deinit(&item->pk); cryptodev_hash_deinit(&item->hash); + kfree(item->transparent_hash); if (item->key) _ncr_key_item_put(item->key); kfree(item->sg); @@ -83,7 +179,7 @@ void _ncr_sessions_item_put( struct session_item_st* item) } } -struct session_item_st* ncr_session_new(struct ncr_lists *lst) +static struct session_item_st *ncr_session_new(ncr_session_t desc) { struct session_item_st* sess; @@ -102,21 +198,10 @@ struct session_item_st* ncr_session_new(struct ncr_lists *lst) err(); goto err_sess; } - mutex_init(&sess->mem_mutex); - - atomic_set(&sess->refcnt, 2); /* One for lst->list, one for "sess" */ + mutex_init(&sess->mutex); - mutex_lock(&lst->session_idr_mutex); - /* idr_pre_get() should preallocate enough, and, due to - session_idr_mutex, nobody else can use the preallocated data. - Therefore the loop recommended in idr_get_new() documentation is not - necessary. */ - if (idr_pre_get(&lst->session_idr, GFP_KERNEL) == 0 || - idr_get_new(&lst->session_idr, sess, &sess->desc) != 0) { - mutex_unlock(&lst->session_idr_mutex); - goto err_sess; - } - mutex_unlock(&lst->session_idr_mutex); + atomic_set(&sess->refcnt, 1); + sess->desc = desc; return sess; @@ -128,113 +213,210 @@ err_sess: } static const struct algo_properties_st algo_properties[] = { - { .algo = NCR_ALG_NULL, .kstr = "ecb(cipher_null)", +#define KSTR(x) .kstr = x, .kstr_len = sizeof(x) - 1 + { .algo = NCR_ALG_NULL, KSTR("ecb(cipher_null)"), .needs_iv = 0, .is_symmetric=1, .can_encrypt=1, .key_type = NCR_KEY_TYPE_INVALID }, - { .algo = NCR_ALG_3DES_CBC, .kstr = "cbc(des3_ede)", + { .algo = NCR_ALG_3DES_CBC, KSTR("cbc(des3_ede)"), .needs_iv = 1, .is_symmetric=1, .can_encrypt=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_AES_CBC, .kstr = "cbc(aes)", + { KSTR("cbc(aes)"), .needs_iv = 1, .is_symmetric=1, .can_encrypt=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_CAMELLIA_CBC, .kstr = "cbc(camelia)", + { KSTR("cbc(camelia)"), .needs_iv = 1, .is_symmetric=1, .can_encrypt=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_AES_CTR, .kstr = "ctr(aes)", + { KSTR("ctr(aes)"), .needs_iv = 1, .is_symmetric=1, .can_encrypt=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_CAMELLIA_CTR, .kstr = "ctr(camelia)", + { KSTR("ctr(camelia)"), .needs_iv = 1, .is_symmetric=1, .can_encrypt=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_ARCFOUR, .kstr = NULL, + { KSTR("ecb(aes)"), .needs_iv = 0, .is_symmetric=1, .can_encrypt=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_AES_ECB, .kstr = "ecb(aes)", + { KSTR("ecb(camelia)"), .needs_iv = 0, .is_symmetric=1, .can_encrypt=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_CAMELLIA_ECB, .kstr = "ecb(camelia)", - .needs_iv = 0, .is_symmetric=1, .can_encrypt=1, - .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_SHA1, .kstr = "sha1", + { .algo = NCR_ALG_SHA1, KSTR("sha1"), .digest_size = 20, .can_digest=1, .key_type = NCR_KEY_TYPE_INVALID }, - { .algo = NCR_ALG_MD5, .kstr = "md5", + { .algo = NCR_ALG_MD5, KSTR("md5"), .digest_size = 16, .can_digest=1, .key_type = NCR_KEY_TYPE_INVALID }, - { .algo = NCR_ALG_SHA2_224, .kstr = "sha224", + { .algo = NCR_ALG_SHA2_224, KSTR("sha224"), .digest_size = 28, .can_digest=1, .key_type = NCR_KEY_TYPE_INVALID }, - { .algo = NCR_ALG_SHA2_256, .kstr = "sha256", + { .algo = NCR_ALG_SHA2_256, KSTR("sha256"), .digest_size = 32, .can_digest=1, .key_type = NCR_KEY_TYPE_INVALID }, - { .algo = NCR_ALG_SHA2_384, .kstr = "sha384", + { .algo = NCR_ALG_SHA2_384, KSTR("sha384"), .digest_size = 48, .can_digest=1, .key_type = NCR_KEY_TYPE_INVALID }, - { .algo = NCR_ALG_SHA2_512, .kstr = "sha512", + { .algo = NCR_ALG_SHA2_512, KSTR("sha512"), .digest_size = 64, .can_digest=1, .key_type = NCR_KEY_TYPE_INVALID }, - { .algo = NCR_ALG_HMAC_SHA1, .is_hmac = 1, .kstr = "hmac(sha1)", + { .is_hmac = 1, KSTR("hmac(sha1)"), .digest_size = 20, .can_sign=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_HMAC_MD5, .is_hmac = 1, .kstr = "hmac(md5)", + { .is_hmac = 1, KSTR("hmac(md5)"), .digest_size = 16, .can_sign=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_HMAC_SHA2_224, .is_hmac = 1, .kstr = "hmac(sha224)", + { .is_hmac = 1, KSTR("hmac(sha224)"), .digest_size = 28, .can_sign=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_HMAC_SHA2_256, .is_hmac = 1, .kstr = "hmac(sha256)", + { .is_hmac = 1, KSTR("hmac(sha256)"), .digest_size = 32, .can_sign=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_HMAC_SHA2_384, .is_hmac = 1, .kstr = "hmac(sha384)", + { .is_hmac = 1, KSTR("hmac(sha384)"), .digest_size = 48, .can_sign=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_HMAC_SHA2_512, .is_hmac = 1, .kstr = "hmac(sha512)", + { .is_hmac = 1, KSTR("hmac(sha512)"), .digest_size = 64, .can_sign=1, .key_type = NCR_KEY_TYPE_SECRET }, - { .algo = NCR_ALG_RSA, .kstr = NULL, .is_pk = 1, + /* NOTE: These algorithm names are not available through the kernel API + (yet). */ + { .algo = NCR_ALG_RSA, KSTR("rsa"), .is_pk = 1, .can_encrypt=1, .can_sign=1, .key_type = NCR_KEY_TYPE_PUBLIC }, - { .algo = NCR_ALG_DSA, .kstr = NULL, .is_pk = 1, + { .algo = NCR_ALG_RSA, KSTR(NCR_ALG_RSA_TRANSPARENT_HASH), .is_pk = 1, + .can_encrypt=1, .can_sign=1, .has_transparent_hash = 1, + .key_type = NCR_KEY_TYPE_PUBLIC }, + { .algo = NCR_ALG_DSA, KSTR("dsa"), .is_pk = 1, .can_sign=1, .key_type = NCR_KEY_TYPE_PUBLIC }, - { .algo = NCR_ALG_DH, .kstr = NULL, .is_pk = 1, + { .algo = NCR_ALG_DSA, KSTR(NCR_ALG_DSA_TRANSPARENT_HASH), .is_pk = 1, + .can_sign=1, .has_transparent_hash = 1, + .key_type = NCR_KEY_TYPE_PUBLIC }, + { .algo = NCR_ALG_DH, KSTR("dh"), .is_pk = 1, .can_kx=1, .key_type = NCR_KEY_TYPE_PUBLIC }, - { .algo = NCR_ALG_NONE } - +#undef KSTR }; -const struct algo_properties_st *_ncr_algo_to_properties(ncr_algorithm_t algo) +/* The lookups by string are inefficient - can we look up all we need from + crypto API? */ +const struct algo_properties_st *_ncr_algo_to_properties(const char *algo) { - ncr_algorithm_t a; - int i = 0; - - for (i = 0; (a = algo_properties[i].algo) != NCR_ALG_NONE; i++) { - if (a == algo) - return &algo_properties[i]; + const struct algo_properties_st *a; + size_t name_len; + + name_len = strlen(algo); + for (a = algo_properties; + a < algo_properties + ARRAY_SIZE(algo_properties); a++) { + if (a->kstr_len == name_len + && memcmp(a->kstr, algo, name_len) == 0) + return a; } return NULL; } -static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* session) +const struct algo_properties_st *_ncr_nla_to_properties(const struct nlattr *nla) +{ + const struct algo_properties_st *a; + size_t name_len; + + if (nla == NULL) + return NULL; + + /* nla_len() >= 1 ensured by validate_nla() case NLA_NUL_STRING */ + name_len = nla_len(nla) - 1; + for (a = algo_properties; + a < algo_properties + ARRAY_SIZE(algo_properties); a++) { + if (a->kstr_len == name_len + && memcmp(a->kstr, nla_data(nla), name_len + 1) == 0) + return a; + } + return NULL; +} + +static int key_item_get_nla_read(struct key_item_st **st, + struct ncr_lists *lists, + const struct nlattr *nla) { - struct session_item_st* ns = NULL; int ret; - const struct algo_properties_st *sign_hash; - ns = ncr_session_new(lists); - if (ns == NULL) { + if (nla == NULL) { err(); - return -ENOMEM; + return -EINVAL; + } + ret = ncr_key_item_get_read(st, lists, nla_get_u32(nla)); + if (ret < 0) { + err(); + return ret; + } + return ret; +} + +/* The caller is responsible for locking of "session". old_session must not be + locked on entry. */ +static int +init_or_clone_hash(struct session_item_st *session, + struct session_item_st *old_session, + const struct algo_properties_st *algo, + const void *mac_key, size_t mac_key_size) +{ + int ret; + + if (old_session == NULL) + return cryptodev_hash_init(&session->hash, algo->kstr, + mac_key, mac_key_size); + + if (mutex_lock_interruptible(&old_session->mutex)) { + err(); + return -ERESTARTSYS; } + ret = cryptodev_hash_clone(&session->hash, &old_session->hash, mac_key, + mac_key_size); + mutex_unlock(&old_session->mutex); + + return ret; +} - ns->op = session->op; - ns->algorithm = _ncr_algo_to_properties(session->algorithm); - if (ns->algorithm == NULL) { +static struct session_item_st *_ncr_session_init(struct ncr_lists *lists, + ncr_session_t desc, + ncr_crypto_op_t op, + struct nlattr *tb[]) +{ + const struct nlattr *nla; + struct session_item_st *ns, *old_session = NULL; + int ret; + const struct algo_properties_st *sign_hash; + + ns = ncr_session_new(desc); + if (ns == NULL) { err(); - ret = -EINVAL; - goto fail; + return ERR_PTR(-ENOMEM); } + /* ns is the only reference throughout this function, so no locking + is necessary. */ + + ns->op = op; + nla = tb[NCR_ATTR_SESSION_CLONE_FROM]; + if (nla != NULL) { + /* "ns" is not visible to userspace, so this is safe. */ + old_session = session_get_ref(lists, nla_get_u32(nla)); + if (old_session == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + if (ns->op != old_session->op) { + err(); + ret = -EINVAL; + goto fail; + } + } + + if (old_session == NULL) { + ns->algorithm = _ncr_nla_to_properties(tb[NCR_ATTR_ALGORITHM]); + if (ns->algorithm == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + } else + ns->algorithm = old_session->algorithm; - switch(session->op) { + switch(op) { case NCR_OP_ENCRYPT: case NCR_OP_DECRYPT: if (!ns->algorithm->can_encrypt) { @@ -243,19 +425,35 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses goto fail; } + if (old_session != NULL) { + err(); + ret = -EOPNOTSUPP; + goto fail; + } + /* read key */ - ret = ncr_key_item_get_read( &ns->key, lists, session->key); + ret = key_item_get_nla_read(&ns->key, lists, + tb[NCR_ATTR_KEY]); if (ret < 0) { err(); goto fail; } + + /* wrapping keys cannot be used for encryption or decryption + */ + if (ns->key->flags & NCR_KEY_FLAG_WRAPPING || ns->key->flags & NCR_KEY_FLAG_UNWRAPPING) { + err(); + ret = -EINVAL; + goto fail; + } + if (ns->key->type == NCR_KEY_TYPE_SECRET) { int keysize = ns->key->key.secret.size; - if (session->algorithm == NCR_ALG_NULL) + if (ns->algorithm->algo == NCR_ALG_NULL) keysize = 0; - if (ns->algorithm->kstr == NULL) { + if (ns->algorithm->is_pk) { err(); ret = -EINVAL; goto fail; @@ -269,16 +467,19 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses } if (ns->algorithm->needs_iv) { - if (session->params.params.cipher.iv_size > sizeof(session->params.params.cipher.iv)) { + nla = tb[NCR_ATTR_IV]; + if (nla == NULL) { err(); ret = -EINVAL; goto fail; } - cryptodev_cipher_set_iv(&ns->cipher, session->params.params.cipher.iv, session->params.params.cipher.iv_size); + cryptodev_cipher_set_iv(&ns->cipher, + nla_data(nla), + nla_len(nla)); } } else if (ns->key->type == NCR_KEY_TYPE_PRIVATE || ns->key->type == NCR_KEY_TYPE_PUBLIC) { ret = ncr_pk_cipher_init(ns->algorithm, &ns->pk, - &session->params, ns->key, NULL); + tb, ns->key, NULL); if (ret < 0) { err(); goto fail; @@ -299,34 +500,51 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses } if (ns->algorithm->can_digest) { - if (ns->algorithm->kstr == NULL) { + if (ns->algorithm->is_pk) { err(); ret = -EINVAL; goto fail; } - ret = cryptodev_hash_init(&ns->hash, ns->algorithm->kstr, 0, NULL, 0); + ret = init_or_clone_hash(ns, old_session, + ns->algorithm, NULL, + 0); if (ret < 0) { err(); goto fail; } } else { - /* read key */ - ret = ncr_key_item_get_read( &ns->key, lists, session->key); - if (ret < 0) { + /* Get key */ + if (old_session == NULL) { + ret = key_item_get_nla_read(&ns->key, + lists, + tb[NCR_ATTR_KEY]); + if (ret < 0) { + err(); + goto fail; + } + } else { + atomic_inc(&old_session->key->refcnt); + ns->key = old_session->key; + } + + /* wrapping keys cannot be used for anything except wrapping. + */ + if (ns->key->flags & NCR_KEY_FLAG_WRAPPING || ns->key->flags & NCR_KEY_FLAG_UNWRAPPING) { err(); + ret = -EINVAL; goto fail; } if (ns->algorithm->is_hmac && ns->key->type == NCR_KEY_TYPE_SECRET) { - if (ns->algorithm->kstr == NULL) { + if (ns->algorithm->is_pk) { err(); ret = -EINVAL; goto fail; } - ret = cryptodev_hash_init(&ns->hash, ns->algorithm->kstr, 1, + ret = init_or_clone_hash(ns, old_session, ns->algorithm, ns->key->key.secret.data, ns->key->key.secret.size); if (ret < 0) { err(); @@ -334,10 +552,17 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses } } else if (ns->algorithm->is_pk && (ns->key->type == NCR_KEY_TYPE_PRIVATE || ns->key->type == NCR_KEY_TYPE_PUBLIC)) { - sign_hash = ncr_key_params_get_sign_hash(ns->key->algorithm, &session->params); - if (IS_ERR(sign_hash)) { + if (old_session != NULL) { + err(); + ret = -EOPNOTSUPP; + goto fail; + } + + nla = tb[NCR_ATTR_SIGNATURE_HASH_ALGORITHM]; + sign_hash = _ncr_nla_to_properties(nla); + if (sign_hash == NULL) { err(); - ret = PTR_ERR(sign_hash); + ret = -EINVAL; goto fail; } @@ -347,24 +572,33 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses goto fail; } - if (sign_hash->kstr == NULL) { + if (sign_hash->is_pk) { err(); ret = -EINVAL; goto fail; } ret = ncr_pk_cipher_init(ns->algorithm, &ns->pk, - &session->params, ns->key, sign_hash); + tb, ns->key, sign_hash); if (ret < 0) { err(); goto fail; } - ret = cryptodev_hash_init(&ns->hash, sign_hash->kstr, 0, NULL, 0); + ret = cryptodev_hash_init(&ns->hash, sign_hash->kstr, NULL, 0); if (ret < 0) { err(); goto fail; } + + if (ns->algorithm->has_transparent_hash) { + ns->transparent_hash = kzalloc(ns->hash.digestsize, GFP_KERNEL); + if (ns->transparent_hash == NULL) { + err(); + ret = -ENOMEM; + goto fail; + } + } } else { err(); ret = -EINVAL; @@ -378,44 +612,46 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses ret = -EINVAL; goto fail; } + + if (old_session != NULL) + _ncr_sessions_item_put(old_session); - ret = 0; - session->ses = ns->desc; + return ns; fail: - if (ret < 0) { - _ncr_session_remove(lists, ns->desc); - } + if (old_session != NULL) + _ncr_sessions_item_put(old_session); _ncr_sessions_item_put(ns); - return ret; + return ERR_PTR(ret); } -int ncr_session_init(struct ncr_lists* lists, void __user* arg) +int ncr_session_init(struct ncr_lists *lists, + const struct ncr_session_init *session, + struct nlattr *tb[]) { - struct ncr_session_st session; - int ret; + ncr_session_t desc; + struct session_item_st *sess; - if (unlikely(copy_from_user(&session, arg, sizeof(session)))) { + desc = session_alloc_desc(lists); + if (desc < 0) { err(); - return -EFAULT; + return desc; } - ret = _ncr_session_init(lists, &session); - if (unlikely(ret)) { + sess = _ncr_session_init(lists, desc, session->op, tb); + if (IS_ERR(sess)) { err(); - return ret; + session_drop_desc(lists, desc); + return PTR_ERR(sess); } - ret = copy_to_user( arg, &session, sizeof(session)); - if (unlikely(ret)) { - err(); - _ncr_session_remove(lists, session.ses); - return -EFAULT; - } - return ret; + session_publish_ref(lists, sess); + + return desc; } +/* The caller is responsible for locking of the session. */ static int _ncr_session_encrypt(struct session_item_st* sess, const struct scatterlist* input, unsigned input_cnt, size_t input_size, void *output, unsigned output_cnt, size_t *output_size) { @@ -444,6 +680,7 @@ int ret; return 0; } +/* The caller is responsible for locking of the session. */ static int _ncr_session_decrypt(struct session_item_st* sess, const struct scatterlist* input, unsigned input_cnt, size_t input_size, struct scatterlist *output, unsigned output_cnt, size_t *output_size) @@ -473,20 +710,7 @@ int ret; return 0; } -static void _ncr_session_remove(struct ncr_lists *lst, ncr_session_t desc) -{ - struct session_item_st * item; - - mutex_lock(&lst->session_idr_mutex); - item = idr_find(&lst->session_idr, desc); - if (item != NULL) - idr_remove(&lst->session_idr, desc); /* Steal the reference */ - mutex_unlock(&lst->session_idr_mutex); - - if (item != NULL) - _ncr_sessions_item_put(item); -} - +/* The caller is responsible for locking of the session. */ static int _ncr_session_grow_pages(struct session_item_st *ses, int pagecount) { struct scatterlist *sg; @@ -517,66 +741,65 @@ static int _ncr_session_grow_pages(struct session_item_st *ses, int pagecount) return 0; } -/* Only the output buffer is given as scatterlist */ -static int get_userbuf1(struct session_item_st* ses, - void __user * udata, size_t udata_size, struct scatterlist **dst_sg, unsigned *dst_cnt) +/* Make NCR_ATTR_UPDATE_INPUT_DATA and NCR_ATTR_UPDATE_OUTPUT_BUFFER available + in scatterlists. + The caller is responsible for locking of the session. */ +static int get_userbuf2(struct session_item_st *ses, struct nlattr *tb[], + struct scatterlist **src_sg, unsigned *src_cnt, + size_t *src_size, struct ncr_session_output_buffer *dst, + struct scatterlist **dst_sg, unsigned *dst_cnt, + int compat) { - int pagecount = 0; + const struct nlattr *src_nla, *dst_nla; + struct ncr_session_input_data src; + int src_pagecount, dst_pagecount = 0, pagecount, write_src = 1, ret; + size_t input_size; - if (unlikely(udata == NULL)) { - err(); - return -EINVAL; - } + src_nla = tb[NCR_ATTR_UPDATE_INPUT_DATA]; + dst_nla = tb[NCR_ATTR_UPDATE_OUTPUT_BUFFER]; - pagecount = PAGECOUNT(udata, udata_size); - _ncr_session_grow_pages(ses, pagecount); - - if (__get_userbuf(udata, udata_size, 1, - pagecount, ses->pages, ses->sg)) { + ret = ncr_session_input_data_from_nla(&src, src_nla, compat); + if (unlikely(ret != 0)) { err(); - return -EINVAL; + return ret; } - (*dst_sg) = ses->sg; - *dst_cnt = pagecount; + *src_size = src.data_size; - ses->available_pages = pagecount; - - return 0; -} - -/* make op->data.udata.input and op->data.udata.output available in scatterlists */ -static int get_userbuf2(struct session_item_st* ses, - struct ncr_session_op_st* op, struct scatterlist **src_sg, - unsigned *src_cnt, struct scatterlist **dst_sg, unsigned *dst_cnt) -{ - int src_pagecount, dst_pagecount = 0, pagecount, write_src = 1; - size_t input_size = op->data.udata.input_size; - - if (unlikely(op->data.udata.input == NULL)) { - err(); - return -EINVAL; + if (dst_nla != NULL) { + ret = ncr_session_output_buffer_from_nla(dst, dst_nla, compat); + if (unlikely(ret != 0)) { + err(); + return ret; + } } - src_pagecount = PAGECOUNT(op->data.udata.input, input_size); + input_size = src.data_size; + src_pagecount = PAGECOUNT(src.data, input_size); - if (op->data.udata.input != op->data.udata.output) { /* non-in-situ transformation */ + if (dst_nla == NULL || src.data != dst->buffer) { /* non-in-situ transformation */ write_src = 0; - if (op->data.udata.output != NULL) { - dst_pagecount = PAGECOUNT(op->data.udata.output, op->data.udata.output_size); + if (dst_nla != NULL) { + dst_pagecount = PAGECOUNT(dst->buffer, + dst->buffer_size); } else { dst_pagecount = 0; } } else { - src_pagecount = max((int)(PAGECOUNT(op->data.udata.output, op->data.udata.output_size)), - src_pagecount); - input_size = max(input_size, (size_t)op->data.udata.output_size); + src_pagecount = max((int)(PAGECOUNT(dst->buffer, + dst->buffer_size)), + src_pagecount); + input_size = max(input_size, dst->buffer_size); } pagecount = src_pagecount + dst_pagecount; - _ncr_session_grow_pages(ses, pagecount); + ret = _ncr_session_grow_pages(ses, pagecount); + if (ret != 0) { + err(); + return ret; + } - if (__get_userbuf(op->data.udata.input, input_size, write_src, - src_pagecount, ses->pages, ses->sg)) { + if (__get_userbuf((void __user *)src.data, input_size, write_src, + src_pagecount, ses->pages, ses->sg)) { err(); printk("write: %d\n", write_src); return -EINVAL; @@ -588,14 +811,15 @@ static int get_userbuf2(struct session_item_st* ses, *dst_cnt = dst_pagecount; (*dst_sg) = ses->sg + src_pagecount; - if (__get_userbuf(op->data.udata.output, op->data.udata.output_size, 1, dst_pagecount, - ses->pages + src_pagecount, *dst_sg)) { + if (__get_userbuf(dst->buffer, dst->buffer_size, 1, + dst_pagecount, ses->pages + src_pagecount, + *dst_sg)) { err(); release_user_pages(ses->pages, src_pagecount); return -EINVAL; } } else { - if (op->data.udata.output != NULL) { + if (dst_nla != NULL) { *dst_cnt = src_pagecount; (*dst_sg) = (*src_sg); } else { @@ -609,35 +833,25 @@ static int get_userbuf2(struct session_item_st* ses, return 0; } -/* Called when userspace buffers are used */ -static int _ncr_session_update(struct ncr_lists* lists, struct ncr_session_op_st* op) +/* Called when userspace buffers are used. + The caller is responsible for locking of the session. */ +static int _ncr_session_update(struct session_item_st *sess, + struct nlattr *tb[], int compat) { + const struct nlattr *nla; int ret; - struct session_item_st* sess; - struct scatterlist *isg; - struct scatterlist *osg; + struct scatterlist *isg = NULL; + struct scatterlist *osg = NULL; unsigned osg_cnt=0, isg_cnt=0; - size_t isg_size, osg_size; - - sess = ncr_sessions_item_get(lists, op->ses); - if (sess == NULL) { - err(); - return -EINVAL; - } - - if (mutex_lock_interruptible(&sess->mem_mutex)) { - err(); - _ncr_sessions_item_put(sess); - return -ERESTARTSYS; - } + size_t isg_size = 0, osg_size; + struct ncr_session_output_buffer out; - ret = get_userbuf2(sess, op, &isg, &isg_cnt, &osg, &osg_cnt); + ret = get_userbuf2(sess, tb, &isg, &isg_cnt, &isg_size, &out, &osg, + &osg_cnt, compat); if (ret < 0) { err(); - goto fail; + return ret; } - isg_size = op->data.udata.input_size; - osg_size = op->data.udata.output_size; switch(sess->op) { case NCR_OP_ENCRYPT: @@ -647,20 +861,35 @@ static int _ncr_session_update(struct ncr_lists* lists, struct ncr_session_op_st goto fail; } + osg_size = out.buffer_size; if (osg_size < isg_size) { err(); ret = -EINVAL; goto fail; } + if (sess->algorithm->is_symmetric + && sess->algorithm->needs_iv) { + nla = tb[NCR_ATTR_IV]; + if (nla != NULL) + cryptodev_cipher_set_iv(&sess->cipher, + nla_data(nla), + nla_len(nla)); + } + ret = _ncr_session_encrypt(sess, isg, isg_cnt, isg_size, osg, osg_cnt, &osg_size); if (ret < 0) { err(); goto fail; } - op->data.udata.output_size = osg_size; - + + ret = ncr_session_output_buffer_set_size(&out, osg_size, + compat); + if (ret != 0) { + err(); + goto fail; + } break; case NCR_OP_DECRYPT: if (osg == NULL) { @@ -669,28 +898,60 @@ static int _ncr_session_update(struct ncr_lists* lists, struct ncr_session_op_st goto fail; } + osg_size = out.buffer_size; if (osg_size < isg_size) { err(); ret = -EINVAL; goto fail; } + if (sess->algorithm->is_symmetric + && sess->algorithm->needs_iv) { + nla = tb[NCR_ATTR_IV]; + if (nla != NULL) + cryptodev_cipher_set_iv(&sess->cipher, + nla_data(nla), + nla_len(nla)); + } + ret = _ncr_session_decrypt(sess, isg, isg_cnt, isg_size, osg, osg_cnt, &osg_size); if (ret < 0) { err(); goto fail; } - op->data.udata.output_size = osg_size; + ret = ncr_session_output_buffer_set_size(&out, osg_size, + compat); + if (ret != 0) { + err(); + goto fail; + } break; case NCR_OP_SIGN: case NCR_OP_VERIFY: - ret = cryptodev_hash_update(&sess->hash, isg, isg_size); - if (ret < 0) { - err(); - goto fail; + if (sess->algorithm->has_transparent_hash) { + if (isg_size != sess->hash.digestsize) { + err(); + ret = -EINVAL; + goto fail; + } + ret = sg_copy_to_buffer(isg, isg_cnt, + sess->transparent_hash, + isg_size); + if (ret != isg_size) { + err(); + ret = -EINVAL; + goto fail; + } + } else { + ret = cryptodev_hash_update(&sess->hash, isg, + isg_size); + if (ret < 0) { + err(); + goto fail; + } } break; default: @@ -706,213 +967,199 @@ fail: release_user_pages(sess->pages, sess->available_pages); sess->available_pages = 0; } - mutex_unlock(&sess->mem_mutex); - _ncr_sessions_item_put(sess); return ret; } -static int try_session_update(struct ncr_lists* lists, struct ncr_session_op_st* op) +/* The caller is responsible for locking of the session. */ +static int try_session_update(struct ncr_lists *lists, + struct session_item_st *sess, struct nlattr *tb[], + int compat) { - if (op->type == NCR_KEY_DATA) { - if (op->data.kdata.input != NCR_KEY_INVALID) - return _ncr_session_update_key(lists, op); - } else if (op->type == NCR_DIRECT_DATA) { - if (op->data.udata.input != NULL) - return _ncr_session_update(lists, op); - } - - return 0; + if (tb[NCR_ATTR_UPDATE_INPUT_KEY_AS_DATA] != NULL) + return _ncr_session_update_key(lists, sess, tb); + else if (tb[NCR_ATTR_UPDATE_INPUT_DATA] != NULL) + return _ncr_session_update(sess, tb, compat); + else + return 0; } -static int _ncr_session_final(struct ncr_lists* lists, struct ncr_session_op_st* op) +/* The caller is responsible for locking of the session. + Note that one or more _ncr_session_update()s may still be blocked on + sess->mutex and will execute after this function! */ +static int _ncr_session_final(struct ncr_lists *lists, + struct session_item_st *sess, struct nlattr *tb[], + int compat) { + const struct nlattr *nla; int ret; - struct session_item_st* sess; int digest_size; uint8_t digest[NCR_HASH_MAX_OUTPUT_SIZE]; - uint8_t vdigest[NCR_HASH_MAX_OUTPUT_SIZE]; - struct scatterlist *osg; - unsigned osg_cnt=0; - size_t osg_size = 0; - size_t orig_osg_size; - void __user * udata = NULL; - size_t *udata_size; - - sess = ncr_sessions_item_get(lists, op->ses); - if (sess == NULL) { - err(); - return -EINVAL; - } + void *buffer = NULL; - ret = try_session_update(lists, op); + ret = try_session_update(lists, sess, tb, compat); if (ret < 0) { err(); - _ncr_sessions_item_put(sess); return ret; } - if (mutex_lock_interruptible(&sess->mem_mutex)) { - err(); - _ncr_sessions_item_put(sess); - return -ERESTARTSYS; - } - - if (op->type == NCR_DIRECT_DATA) { - udata = op->data.udata.output; - udata_size = &op->data.udata.output_size; - } else if (op->type == NCR_KEY_DATA) { - udata = op->data.kdata.output; - udata_size = &op->data.kdata.output_size; - } else { - err(); - ret = -EINVAL; - goto fail; - } - switch(sess->op) { - case NCR_OP_ENCRYPT: - case NCR_OP_DECRYPT: - break; - case NCR_OP_VERIFY: - ret = get_userbuf1(sess, udata, *udata_size, &osg, &osg_cnt); + case NCR_OP_ENCRYPT: + case NCR_OP_DECRYPT: + break; + case NCR_OP_VERIFY: { + struct ncr_session_input_data src; + + nla = tb[NCR_ATTR_FINAL_INPUT_DATA]; + ret = ncr_session_input_data_from_nla(&src, nla, compat); + if (unlikely(ret != 0)) { + err(); + goto fail; + } + + buffer = kmalloc(src.data_size, GFP_KERNEL); + if (buffer == NULL) { + err(); + ret = -ENOMEM; + goto fail; + } + if (unlikely(copy_from_user(buffer, src.data, src.data_size))) { + err(); + ret = -EFAULT; + goto fail; + } + + digest_size = sess->hash.digestsize; + if (digest_size == 0 || sizeof(digest) < digest_size) { + err(); + ret = -EINVAL; + goto fail; + } + if (sess->algorithm->has_transparent_hash) + memcpy(digest, sess->transparent_hash, digest_size); + else { + ret = cryptodev_hash_final(&sess->hash, digest); if (ret < 0) { err(); goto fail; } - orig_osg_size = osg_size = *udata_size; + } - digest_size = sess->hash.digestsize; - if (digest_size == 0 || sizeof(digest) < digest_size) { + if (!sess->algorithm->is_pk) + ret = (digest_size == src.data_size + && memcmp(buffer, digest, digest_size) == 0); + else { + ret = ncr_pk_cipher_verify(&sess->pk, buffer, + src.data_size, digest, + digest_size); + if (ret < 0) { err(); - ret = -EINVAL; goto fail; } + } + break; + } + + case NCR_OP_SIGN: { + struct ncr_session_output_buffer dst; + size_t output_size; + + nla = tb[NCR_ATTR_FINAL_OUTPUT_BUFFER]; + ret = ncr_session_output_buffer_from_nla(&dst, nla, compat); + if (unlikely(ret != 0)) { + err(); + goto fail; + } + + digest_size = sess->hash.digestsize; + if (digest_size == 0) { + err(); + ret = -EINVAL; + goto fail; + } + + if (sess->algorithm->has_transparent_hash) + memcpy(digest, sess->transparent_hash, digest_size); + else { ret = cryptodev_hash_final(&sess->hash, digest); if (ret < 0) { err(); goto fail; } + } - if (sess->algorithm->is_hmac) { - ret = sg_copy_to_buffer(osg, osg_cnt, vdigest, digest_size); - if (ret != digest_size) { - err(); - ret = -EINVAL; - goto fail; - } - - if (digest_size != osg_size || - memcmp(vdigest, digest, digest_size) != 0) { - - op->err = NCR_VERIFICATION_FAILED; - } else { - op->err = NCR_SUCCESS; - } - } else { - /* PK signature */ - ret = ncr_pk_cipher_verify(&sess->pk, osg, osg_cnt, osg_size, - digest, digest_size, &op->err); - if (ret < 0) { - err(); - goto fail; - } - } - break; + cryptodev_hash_deinit(&sess->hash); - case NCR_OP_SIGN: - ret = get_userbuf1(sess, udata, *udata_size, &osg, &osg_cnt); - if (ret < 0) { + if (!sess->algorithm->is_pk) { + if (dst.buffer_size < digest_size) { err(); + ret = -ERANGE; goto fail; } - orig_osg_size = osg_size = *udata_size; - - digest_size = sess->hash.digestsize; - if (digest_size == 0 || osg_size < digest_size) { + if (unlikely(copy_to_user(dst.buffer, digest, + digest_size))) { err(); - ret = -EINVAL; + ret = -EFAULT; goto fail; } - - ret = cryptodev_hash_final(&sess->hash, digest); + output_size = digest_size; + } else { + output_size = dst.buffer_size; + buffer = kmalloc(output_size, GFP_KERNEL); + if (buffer == NULL) { + err(); + ret = -ENOMEM; + goto fail; + } + ret = ncr_pk_cipher_sign(&sess->pk, digest, digest_size, + buffer, &output_size); if (ret < 0) { err(); goto fail; } - - ret = sg_copy_from_buffer(osg, osg_cnt, digest, digest_size); - if (ret != digest_size) { + if (unlikely(copy_to_user(dst.buffer, buffer, + output_size))) { err(); - ret = -EINVAL; + ret = -EFAULT; goto fail; } - osg_size = digest_size; - - cryptodev_hash_deinit(&sess->hash); + } - if (sess->algorithm->is_pk) { - /* PK signature */ - - ret = ncr_pk_cipher_sign(&sess->pk, osg, osg_cnt, osg_size, - osg, osg_cnt, &orig_osg_size); - if (ret < 0) { - err(); - goto fail; - } - osg_size = orig_osg_size; - } - break; - default: + ret = ncr_session_output_buffer_set_size(&dst, output_size, + compat); + if (ret != 0) { err(); - ret = -EINVAL; goto fail; + } + break; } - - if (osg_size > 0) - *udata_size = osg_size; - - ret = 0; - -fail: - if (sess->available_pages) { - release_user_pages(sess->pages, sess->available_pages); - sess->available_pages = 0; - } - mutex_unlock(&sess->mem_mutex); - - cryptodev_hash_deinit(&sess->hash); - if (sess->algorithm->is_symmetric) { - cryptodev_cipher_deinit(&sess->cipher); - } else { - ncr_pk_cipher_deinit(&sess->pk); + default: + err(); + ret = -EINVAL; + goto fail; } - _ncr_sessions_item_put(sess); - _ncr_session_remove(lists, op->ses); +fail: + kfree(buffer); return ret; } -/* Direct with key: Allows to hash a key */ -/* Called when userspace buffers are used */ -static int _ncr_session_update_key(struct ncr_lists* lists, struct ncr_session_op_st* op) +/* Direct with key: Allows to hash a key. + The caller is responsible for locking of the session. */ +static int _ncr_session_update_key(struct ncr_lists *lists, + struct session_item_st* sess, + struct nlattr *tb[]) { int ret; - struct session_item_st* sess; struct key_item_st* key = NULL; - sess = ncr_sessions_item_get(lists, op->ses); - if (sess == NULL) { - err(); - return -EINVAL; - } - /* read key */ - ret = ncr_key_item_get_read( &key, lists, op->data.kdata.input); + ret = key_item_get_nla_read(&key, lists, + tb[NCR_ATTR_UPDATE_INPUT_KEY_AS_DATA]); if (ret < 0) { err(); - goto fail; + return ret; } if (key->type != NCR_KEY_TYPE_SECRET) { @@ -929,6 +1176,11 @@ static int _ncr_session_update_key(struct ncr_lists* lists, struct ncr_session_o goto fail; case NCR_OP_SIGN: case NCR_OP_VERIFY: + if (sess->algorithm->has_transparent_hash) { + err(); + ret = -EINVAL; + goto fail; + } ret = _cryptodev_hash_update(&sess->hash, key->key.secret.data, key->key.secret.size); if (ret < 0) { @@ -945,89 +1197,102 @@ static int _ncr_session_update_key(struct ncr_lists* lists, struct ncr_session_o ret = 0; fail: - if (key) _ncr_key_item_put(key); - _ncr_sessions_item_put(sess); + _ncr_key_item_put(key); return ret; } -int ncr_session_update(struct ncr_lists* lists, void __user* arg) +int ncr_session_update(struct ncr_lists *lists, + const struct ncr_session_update *op, struct nlattr *tb[], + int compat) { - struct ncr_session_op_st op; + struct session_item_st *sess; int ret; - if (unlikely(copy_from_user( &op, arg, sizeof(op)))) { + sess = session_get_ref(lists, op->ses); + if (sess == NULL) { err(); - return -EFAULT; + return -EINVAL; } - if (op.type == NCR_DIRECT_DATA) - ret = _ncr_session_update(lists, &op); - else if (op.type == NCR_KEY_DATA) - ret = _ncr_session_update_key(lists, &op); + /* Note that op->ses may be reallocated from now on, making the audit + information confusing. */ + + if (mutex_lock_interruptible(&sess->mutex)) { + err(); + ret = -ERESTARTSYS; + goto end; + } + if (tb[NCR_ATTR_UPDATE_INPUT_DATA] != NULL) + ret = _ncr_session_update(sess, tb, compat); + else if (tb[NCR_ATTR_UPDATE_INPUT_KEY_AS_DATA] != NULL) + ret = _ncr_session_update_key(lists, sess, tb); else ret = -EINVAL; + mutex_unlock(&sess->mutex); + +end: + _ncr_sessions_item_put(sess); if (unlikely(ret)) { err(); return ret; } - if (unlikely(copy_to_user(arg, &op, sizeof(op)))) { - err(); - return -EFAULT; - } - return 0; } -int ncr_session_final(struct ncr_lists* lists, void __user* arg) +int ncr_session_final(struct ncr_lists *lists, + const struct ncr_session_final *op, struct nlattr *tb[], + int compat) { - struct ncr_session_op_st op; + struct session_item_st *sess; int ret; - if (unlikely(copy_from_user(&op, arg, sizeof(op)))) { + /* Make the session inaccessible atomically to avoid concurrent + session_final() callers, but keep the ID allocated to keep audit + information unambiguous. */ + sess = session_unpublish_ref(lists, op->ses); + if (sess == NULL) { err(); - return -EFAULT; + return -EINVAL; } - ret = _ncr_session_final(lists, &op); - if (unlikely(ret)) { + if (mutex_lock_interruptible(&sess->mutex)) { err(); - return ret; + /* Other threads may observe the session descriptor + disappearing and reappearing - but then they should not be + accessing it anyway if it is being freed. + session_unpublish_ref keeps the ID allocated for us. */ + session_publish_ref(lists, sess); + return -ERESTARTSYS; } + ret = _ncr_session_final(lists, sess, tb, compat); + mutex_unlock(&sess->mutex); - if (unlikely(copy_to_user(arg, &op, sizeof(op)))) { - err(); - return -EFAULT; - } - return 0; + _ncr_sessions_item_put(sess); + session_drop_desc(lists, op->ses); + + return ret; } -int ncr_session_once(struct ncr_lists* lists, void __user* arg) +int ncr_session_once(struct ncr_lists *lists, + const struct ncr_session_once *once, struct nlattr *tb[], + int compat) { - struct ncr_session_once_op_st kop; + struct session_item_st *sess; int ret; - if (unlikely(copy_from_user(&kop, arg, sizeof(kop)))) { + sess = _ncr_session_init(lists, -1, once->op, tb); + if (IS_ERR(sess)) { err(); - return -EFAULT; + return PTR_ERR(sess); } - ret = _ncr_session_init(lists, &kop.init); - if (ret < 0) { - err(); - return ret; - } - kop.op.ses = kop.init.ses; + /* No locking of sess necessary, "sess" is the only reference. */ + ret = _ncr_session_final(lists, sess, tb, compat); - ret = _ncr_session_final(lists, &kop.op); - if (ret < 0) { - err(); - return ret; - } + _ncr_sessions_item_put(sess); - if (unlikely(copy_to_user(arg, &kop, sizeof(kop)))) - return -EFAULT; - return 0; + return ret; } diff --git a/crypto/userspace/ncr.c b/crypto/userspace/ncr.c index 4cf79b3a025..e643fe139a0 100644 --- a/crypto/userspace/ncr.c +++ b/crypto/userspace/ncr.c @@ -22,6 +22,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include <linux/compat.h> #include <linux/crypto.h> #include <linux/ioctl.h> #include <linux/mm.h> @@ -32,7 +33,9 @@ #include <linux/cred.h> #include <linux/capability.h> #include <linux/ncr.h> +#include <net/netlink.h> #include "ncr-int.h" +#include "utils.h" #include <linux/workqueue.h> /* This is the master wrapping key for storage of keys @@ -74,26 +77,20 @@ void ncr_master_key_reset(void) memset(&master_key, 0, sizeof(master_key)); } -static int ncr_master_key_set(void __user *arg) +static int ncr_master_key_set(const struct ncr_master_key_set *st, + struct nlattr *tb[]) { -struct ncr_master_key_st st; - - if (current_euid() != 0 && !capable(CAP_SYS_ADMIN)) { + if (!capable(CAP_SYS_ADMIN)) { err(); return -EPERM; } - if (unlikely(copy_from_user(&st, arg, sizeof(st)))) { - err(); - return -EFAULT; - } - - if (st.key_size > sizeof(master_key.key.secret.data)) { + if (st->key_size > sizeof(master_key.key.secret.data)) { err(); return -EINVAL; } - if (st.key_size != 16 && st.key_size != 24 && st.key_size != 32) { + if (st->key_size != 16 && st->key_size != 24 && st->key_size != 32) { dprintk(0, KERN_DEBUG, "Master key size must be 16,24 or 32.\n"); return -EINVAL; } @@ -102,7 +99,8 @@ struct ncr_master_key_st st; dprintk(0, KERN_DEBUG, "Master key was previously initialized.\n"); } - if (unlikely(copy_from_user(master_key.key.secret.data, st.key, st.key_size))) { + if (unlikely(copy_from_user(master_key.key.secret.data, st->key, + st->key_size))) { err(); return -EFAULT; } @@ -110,56 +108,370 @@ struct ncr_master_key_st st; dprintk(0, KERN_INFO, "Initializing master key.\n"); master_key.type = NCR_KEY_TYPE_SECRET; - master_key.key.secret.size = st.key_size; + master_key.key.secret.size = st->key_size; return 0; } -int +long ncr_ioctl(struct ncr_lists *lst, unsigned int cmd, unsigned long arg_) { void __user *arg = (void __user *)arg_; + struct nlattr *tb[NCR_ATTR_MAX + 1]; + void *attr_buf; + int ret; + + if (unlikely(!lst)) + BUG(); + + switch (cmd) { +#define CASE_(LABEL, STRUCT, FUNCTION, ARGS) \ + case (LABEL): { \ + struct STRUCT data; \ + \ + attr_buf = NCR_GET_INPUT_ARGS_NO_OUTPUT(&data, tb, arg); \ + if (IS_ERR(attr_buf)) { \ + err(); \ + return PTR_ERR(attr_buf); \ + } \ + ret = (FUNCTION)ARGS; \ + break; \ + } +#define CASE_NO_OUTPUT(LABEL, STRUCT, FUNCTION) \ + CASE_(LABEL, STRUCT, FUNCTION, (lst, &data, tb)) +#define CASE_NO_OUTPUT_COMPAT(LABEL, STRUCT, FUNCTION) \ + CASE_(LABEL, STRUCT, FUNCTION, (lst, &data, tb, 0)) + + case NCRIO_KEY_INIT: + return ncr_key_init(lst); + CASE_NO_OUTPUT(NCRIO_KEY_GENERATE, ncr_key_generate, ncr_key_generate); + CASE_NO_OUTPUT(NCRIO_KEY_GENERATE_PAIR, ncr_key_generate_pair, + ncr_key_generate_pair); + CASE_NO_OUTPUT(NCRIO_KEY_DERIVE, ncr_key_derive, ncr_key_derive); + case NCRIO_KEY_GET_INFO: { + struct ncr_key_get_info data; + struct ncr_out out; + + attr_buf = NCR_GET_INPUT_ARGS(&data, tb, arg); + if (IS_ERR(attr_buf)) { + err(); + return PTR_ERR(attr_buf); + } + ret = NCR_OUT_INIT(&out, &data, arg); + if (ret != 0) { + err(); + break; + } + ret = ncr_key_get_info(lst, &out, &data, tb); + ncr_out_free(&out); + break; + } + CASE_NO_OUTPUT(NCRIO_KEY_EXPORT, ncr_key_export, ncr_key_export); + CASE_NO_OUTPUT(NCRIO_KEY_IMPORT, ncr_key_import, ncr_key_import); + case NCRIO_KEY_DEINIT: { + ncr_key_t key; + + ret = get_user(key, (const ncr_key_t __user *)arg); + if (unlikely(ret)) { + err(); + return ret; + } + return ncr_key_deinit(lst, key); + } + CASE_NO_OUTPUT(NCRIO_KEY_WRAP, ncr_key_wrap, ncr_key_wrap); + CASE_NO_OUTPUT(NCRIO_KEY_UNWRAP, ncr_key_unwrap, ncr_key_unwrap); + CASE_NO_OUTPUT(NCRIO_KEY_STORAGE_WRAP, ncr_key_storage_wrap, + ncr_key_storage_wrap); + CASE_NO_OUTPUT(NCRIO_KEY_STORAGE_UNWRAP, ncr_key_storage_unwrap, + ncr_key_storage_unwrap); + CASE_NO_OUTPUT(NCRIO_SESSION_INIT, ncr_session_init, ncr_session_init); + CASE_NO_OUTPUT_COMPAT(NCRIO_SESSION_UPDATE, ncr_session_update, + ncr_session_update); + CASE_NO_OUTPUT_COMPAT(NCRIO_SESSION_FINAL, ncr_session_final, + ncr_session_final); + CASE_NO_OUTPUT_COMPAT(NCRIO_SESSION_ONCE, ncr_session_once, + ncr_session_once); + CASE_(NCRIO_MASTER_KEY_SET, ncr_master_key_set, ncr_master_key_set, + (&data, tb)); + default: + return -EINVAL; +#undef CASE_ +#undef CASE_NO_OUTPUT +#undef CASE_NO_OUTPUT_COMPAT + } + kfree(attr_buf); + return ret; +} + +#ifdef CONFIG_COMPAT +struct compat_ncr_key_export { + __u32 input_size, output_size; + ncr_key_t key; + compat_uptr_t buffer; + compat_int_t buffer_size; + __NL_ATTRIBUTES; +}; +#define COMPAT_NCRIO_KEY_EXPORT _IOWR('c', 209, struct compat_ncr_key_export) + +static void convert_ncr_key_export(struct ncr_key_export *new, + const struct compat_ncr_key_export *old) +{ + new->key = old->key; + new->buffer = compat_ptr(old->buffer); + new->buffer_size = old->buffer_size; +} + +struct compat_ncr_key_import { + __u32 input_size, output_size; + ncr_key_t key; + compat_uptr_t data; + __u32 data_size; + __NL_ATTRIBUTES; +}; +#define COMPAT_NCRIO_KEY_IMPORT _IOWR('c', 210, struct compat_ncr_key_import) + +static void convert_ncr_key_import(struct ncr_key_import *new, + const struct compat_ncr_key_import *old) +{ + new->key = old->key; + new->data = compat_ptr(old->data); + new->data_size = old->data_size; +} + +struct compat_ncr_key_wrap { + __u32 input_size, output_size; + ncr_key_t wrapping_key; + ncr_key_t source_key; + compat_uptr_t buffer; + compat_int_t buffer_size; + __NL_ATTRIBUTES; +}; +#define COMPAT_NCRIO_KEY_WRAP _IOWR('c', 250, struct compat_ncr_key_wrap) + +static void convert_ncr_key_wrap(struct ncr_key_wrap *new, + const struct compat_ncr_key_wrap *old) +{ + new->wrapping_key = old->wrapping_key; + new->source_key = old->source_key; + new->buffer = compat_ptr(old->buffer); + new->buffer_size = old->buffer_size; +} + +struct compat_ncr_key_unwrap { + __u32 input_size, output_size; + ncr_key_t wrapping_key; + ncr_key_t dest_key; + compat_uptr_t data; + __u32 data_size; + __NL_ATTRIBUTES; +}; +#define COMPAT_NCRIO_KEY_UNWRAP _IOWR('c', 251, struct compat_ncr_key_unwrap) + +static void convert_ncr_key_unwrap(struct ncr_key_unwrap *new, + const struct compat_ncr_key_unwrap *old) +{ + new->wrapping_key = old->wrapping_key; + new->dest_key = old->dest_key; + new->data = compat_ptr(old->data); + new->data_size = old->data_size; +} + +struct compat_ncr_key_storage_wrap { + __u32 input_size, output_size; + ncr_key_t key; + compat_uptr_t buffer; + compat_int_t buffer_size; + __NL_ATTRIBUTES; +}; +#define COMPAT_NCRIO_KEY_STORAGE_WRAP \ + _IOWR('c', 261, struct compat_ncr_key_storage_wrap) + +static void convert_ncr_key_storage_wrap(struct ncr_key_storage_wrap *new, + const struct compat_ncr_key_storage_wrap *old) +{ + new->key = old->key; + new->buffer = compat_ptr(old->buffer); + new->buffer_size = old->buffer_size; +} + +struct compat_ncr_key_storage_unwrap { + __u32 input_size, output_size; + ncr_key_t key; + compat_uptr_t data; + __u32 data_size; + __NL_ATTRIBUTES; +}; +#define COMPAT_NCRIO_KEY_STORAGE_UNWRAP \ + _IOWR('c', 262, struct compat_ncr_key_storage_wrap) + +static void convert_ncr_key_storage_unwrap(struct ncr_key_storage_unwrap *new, + const struct compat_ncr_key_storage_unwrap *old) +{ + new->key = old->key; + new->data = compat_ptr(old->data); + new->data_size = old->data_size; +} + +struct compat_ncr_master_key_set { + __u32 input_size, output_size; + compat_uptr_t key; + __u32 key_size; + __NL_ATTRIBUTES; +}; +#define COMPAT_NCRIO_MASTER_KEY_SET \ + _IOWR('c', 260, struct compat_ncr_master_key_set) + +static void convert_ncr_master_key_set(struct ncr_master_key_set *new, + const struct compat_ncr_master_key_set *old) +{ + new->key = compat_ptr(old->key); + new->key_size = old->key_size; +} + +long +ncr_compat_ioctl(struct ncr_lists *lst, unsigned int cmd, unsigned long arg_) +{ + void __user *arg = (void __user *)arg_; + struct nlattr *tb[NCR_ATTR_MAX + 1]; + void *attr_buf; + int ret; if (unlikely(!lst)) BUG(); switch (cmd) { - case NCRIO_KEY_INIT: - return ncr_key_init(lst, arg); - case NCRIO_KEY_DEINIT: - return ncr_key_deinit(lst, arg); - case NCRIO_KEY_GENERATE: - return ncr_key_generate(lst, arg); - case NCRIO_KEY_EXPORT: - return ncr_key_export(lst, arg); - case NCRIO_KEY_IMPORT: - return ncr_key_import(lst, arg); - case NCRIO_KEY_GET_INFO: - return ncr_key_info(lst, arg); - case NCRIO_KEY_WRAP: - return ncr_key_wrap(lst, arg); - case NCRIO_KEY_UNWRAP: - return ncr_key_unwrap(lst, arg); - case NCRIO_KEY_STORAGE_WRAP: - return ncr_key_storage_wrap(lst, arg); - case NCRIO_KEY_STORAGE_UNWRAP: - return ncr_key_storage_unwrap(lst, arg); - case NCRIO_SESSION_INIT: - return ncr_session_init(lst, arg); - case NCRIO_SESSION_UPDATE: - return ncr_session_update(lst, arg); - case NCRIO_SESSION_FINAL: - return ncr_session_final(lst, arg); - case NCRIO_SESSION_ONCE: - return ncr_session_once(lst, arg); - - case NCRIO_MASTER_KEY_SET: - return ncr_master_key_set(arg); - case NCRIO_KEY_GENERATE_PAIR: - return ncr_key_generate_pair(lst, arg); - case NCRIO_KEY_DERIVE: - return ncr_key_derive(lst, arg); - default: - return -EINVAL; + case NCRIO_KEY_INIT: + case NCRIO_KEY_GENERATE: + case NCRIO_KEY_GENERATE_PAIR: + case NCRIO_KEY_DERIVE: + case NCRIO_KEY_GET_INFO: + case NCRIO_KEY_DEINIT: + case NCRIO_SESSION_INIT: + return ncr_ioctl(lst, cmd, arg_); + +#define CASE_(LABEL, STRUCT, FUNCTION, ARGS) \ + case (LABEL): { \ + struct compat_##STRUCT old; \ + struct STRUCT new; \ + \ + attr_buf = NCR_GET_INPUT_ARGS_NO_OUTPUT(&old, tb, arg); \ + if (IS_ERR(attr_buf)) { \ + err(); \ + return PTR_ERR(attr_buf); \ + } \ + convert_##STRUCT(&new, &old); \ + ret = (FUNCTION)ARGS; \ + break; \ + } +#define CASE_NO_OUTPUT(LABEL, STRUCT, FUNCTION) \ + CASE_(LABEL, STRUCT, FUNCTION, (lst, &new, tb)) + +#define CASE_COMPAT_ONLY(LABEL, STRUCT, FUNCTION) \ + case (LABEL): { \ + struct STRUCT data; \ + \ + attr_buf = NCR_GET_INPUT_ARGS_NO_OUTPUT(&data, tb, arg); \ + if (IS_ERR(attr_buf)) { \ + err(); \ + return PTR_ERR(attr_buf); \ + } \ + ret = (FUNCTION)(lst, &data, tb, 1); \ + break; \ + } + + CASE_NO_OUTPUT(COMPAT_NCRIO_KEY_EXPORT, ncr_key_export, ncr_key_export); + CASE_NO_OUTPUT(COMPAT_NCRIO_KEY_IMPORT, ncr_key_import, ncr_key_import); + CASE_NO_OUTPUT(COMPAT_NCRIO_KEY_WRAP, ncr_key_wrap, ncr_key_wrap); + CASE_NO_OUTPUT(COMPAT_NCRIO_KEY_UNWRAP, ncr_key_unwrap, ncr_key_unwrap); + CASE_NO_OUTPUT(COMPAT_NCRIO_KEY_STORAGE_WRAP, ncr_key_storage_wrap, + ncr_key_storage_wrap); + CASE_NO_OUTPUT(COMPAT_NCRIO_KEY_STORAGE_UNWRAP, ncr_key_storage_unwrap, + ncr_key_storage_unwrap); + CASE_COMPAT_ONLY(NCRIO_SESSION_UPDATE, ncr_session_update, + ncr_session_update); + CASE_COMPAT_ONLY(NCRIO_SESSION_FINAL, ncr_session_final, + ncr_session_final); + CASE_COMPAT_ONLY(NCRIO_SESSION_ONCE, ncr_session_once, + ncr_session_once); + CASE_(COMPAT_NCRIO_MASTER_KEY_SET, ncr_master_key_set, + ncr_master_key_set, (&new, tb)); + default: + return -EINVAL; +#undef CASE_ +#undef CASE_NO_OUTPUT +#undef CASE_COMPAT_ONLY + } + kfree(attr_buf); + return ret; +} +#endif + +int ncr_session_input_data_from_nla(struct ncr_session_input_data *dest, + const struct nlattr *nla, int compat) +{ + if (unlikely(nla == NULL)) + return -EINVAL; +#ifdef CONFIG_COMPAT + if (!compat) { +#endif + if (unlikely(nla_len(nla) < sizeof(dest))) + return -ERANGE; /* nla_validate would return -ERANGE. */ + memcpy(dest, nla_data(nla), sizeof(*dest)); +#ifdef CONFIG_COMPAT + } else { + struct compat_ncr_session_input_data old; + + if (unlikely(nla_len(nla) < sizeof(old))) + return -ERANGE; + memcpy(&old, nla_data(nla), sizeof(old)); + dest->data = compat_ptr(old.data); + dest->data_size = old.data_size; + } +#endif + return 0; +} + +int ncr_session_output_buffer_from_nla(struct ncr_session_output_buffer *dest, + const struct nlattr *nla, int compat) +{ + if (unlikely(nla == NULL)) + return -EINVAL; +#ifdef CONFIG_COMPAT + if (!compat) { +#endif + if (unlikely(nla_len(nla) < sizeof(dest))) + return -ERANGE; /* nla_validate would return -ERANGE. */ + memcpy(dest, nla_data(nla), sizeof(*dest)); +#ifdef CONFIG_COMPAT + } else { + struct compat_ncr_session_output_buffer old; + + if (unlikely(nla_len(nla) < sizeof(old))) + return -ERANGE; + memcpy(&old, nla_data(nla), sizeof(old)); + dest->buffer = compat_ptr(old.buffer); + dest->buffer_size = old.buffer_size; + dest->result_size_ptr = compat_ptr(old.result_size_ptr); + } +#endif + return 0; +} + + +int ncr_session_output_buffer_set_size(const struct ncr_session_output_buffer *dest, + size_t size, int compat) +{ +#ifdef CONFIG_COMPAT + if (!compat) +#endif + return put_user(size, dest->result_size_ptr); +#ifdef CONFIG_COMPAT + else { + compat_size_t old; + + old = size; + return put_user(old, + (compat_size_t __user *)dest->result_size_ptr); } +#endif } diff --git a/crypto/userspace/utils.c b/crypto/userspace/utils.c new file mode 100644 index 00000000000..a427833fdad --- /dev/null +++ b/crypto/userspace/utils.c @@ -0,0 +1,298 @@ +/* + * New driver for /dev/crypto device (aka CryptoDev) + * + * Copyright (c) 2010 Red Hat Inc. + * + * 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. + * + * Red Hat Author: Miloslav Trmač + * + */ + +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <net/netlink.h> +#include "ncr-int.h" +#include "utils.h" + +#ifdef CONFIG_COMPAT +/* max() is too clever for compile-time constants */ +#define CONST_MAX(A, B) ((A) > (B) ? (A) : (B)) + +#define MAX_SESSION_INPUT_DATA_SIZE \ + (CONST_MAX(sizeof(struct ncr_session_input_data), \ + sizeof(struct compat_ncr_session_input_data))) +#define MAX_SESSION_OUTPUT_BUFFER_SIZE \ + (CONST_MAX(sizeof(struct ncr_session_output_buffer), \ + sizeof(struct compat_ncr_session_output_buffer))) + +#else /* !CONFIG_COMPAT */ + +#define MAX_SESSION_INPUT_DATA_SIZE (sizeof(struct ncr_session_input_data)) +#define MAX_SESSION_OUTPUT_BUFFER_SIZE \ + (sizeof(struct ncr_session_output_buffer)) + +#endif /* !CONFIG_COMPAT */ + +static const struct nla_policy ncr_attr_policy[NCR_ATTR_MAX + 1] = { + [NCR_ATTR_ALGORITHM] = { NLA_NUL_STRING, 0 }, + [NCR_ATTR_DERIVATION_ALGORITHM] = { NLA_NUL_STRING, 0 }, + [NCR_ATTR_SIGNATURE_HASH_ALGORITHM] = { NLA_NUL_STRING, 0 }, + [NCR_ATTR_WRAPPING_ALGORITHM] = { NLA_NUL_STRING, 0 }, + [NCR_ATTR_UPDATE_INPUT_DATA] = { + NLA_BINARY, MAX_SESSION_INPUT_DATA_SIZE + }, + [NCR_ATTR_UPDATE_OUTPUT_BUFFER] = { + NLA_BINARY, MAX_SESSION_OUTPUT_BUFFER_SIZE + }, + [NCR_ATTR_UPDATE_INPUT_KEY_AS_DATA] = { NLA_U32, 0 }, + [NCR_ATTR_FINAL_INPUT_DATA] = { + NLA_BINARY, MAX_SESSION_INPUT_DATA_SIZE + }, + [NCR_ATTR_FINAL_OUTPUT_BUFFER] = { + NLA_BINARY, MAX_SESSION_OUTPUT_BUFFER_SIZE + }, + [NCR_ATTR_KEY] = { NLA_U32, 0 }, + [NCR_ATTR_KEY_FLAGS] = { NLA_U32, 0 }, + [NCR_ATTR_KEY_ID] = { NLA_BINARY, 0 }, + [NCR_ATTR_KEY_TYPE] = { NLA_U32, 0 }, + [NCR_ATTR_IV] = { NLA_BINARY, 0 }, + [NCR_ATTR_SECRET_KEY_BITS] = { NLA_U32, 0 }, + [NCR_ATTR_RSA_MODULUS_BITS] = { NLA_U32, 0 }, + [NCR_ATTR_RSA_E] = { NLA_BINARY, 0 }, + [NCR_ATTR_RSA_ENCODING_METHOD] = { NLA_U32, 0 }, + [NCR_ATTR_RSA_OAEP_HASH_ALGORITHM] = { NLA_NUL_STRING, 0 }, + [NCR_ATTR_RSA_PSS_SALT_LENGTH] = { NLA_U32, 0 }, + [NCR_ATTR_DSA_P_BITS] = { NLA_U32, 0 }, + [NCR_ATTR_DSA_Q_BITS] = { NLA_U32, 0 }, + [NCR_ATTR_DH_PRIME] = { NLA_BINARY, 0 }, + [NCR_ATTR_DH_BASE] = { NLA_BINARY, 0 }, + [NCR_ATTR_DH_PUBLIC] = { NLA_BINARY, 0 }, + [NCR_ATTR_WANTED_ATTRS] = { NLA_BINARY, 0 }, + [NCR_ATTR_SESSION_CLONE_FROM] = { NLA_U32, 0 }, +}; + +void *__ncr_get_input_args(void *fixed, struct nlattr *tb[], size_t fixed_size, + u32 *input_size_ptr, const void __user *arg) +{ + size_t input_size, buf_size; + void *buf; + int ret; + + if (unlikely(copy_from_user(fixed, arg, fixed_size))) { + err(); + return ERR_PTR(-EFAULT); + } + input_size = *input_size_ptr; + + /* NCR_GET_INPUT_ARGS/NCR_GET_INPUT_ARGS_NO_OUTPUT has verified + fixed_size is correctly aligned for a struct nlattr. */ + if (input_size == 0) + input_size = fixed_size; + else if (unlikely(input_size < fixed_size)) { + err(); + return ERR_PTR(-EINVAL); + } + buf_size = input_size - fixed_size; + if (unlikely(buf_size > NCR_MAX_ATTR_SIZE)) { + err(); + return ERR_PTR(-EOVERFLOW); + } + + if (buf_size == 0) + buf = NULL; + else { + const char __user *var_arg; + + buf = kmalloc(buf_size, GFP_KERNEL); + if (unlikely(buf == NULL)) { + err(); + return ERR_PTR(-ENOMEM); + } + var_arg = (const char __user *)arg + fixed_size; + if (unlikely(copy_from_user(buf, var_arg, buf_size))) { + err(); + ret = -EFAULT; + goto err_buf; + } + } + + ret = nla_parse(tb, NCR_ATTR_MAX, buf, buf_size, ncr_attr_policy); + if (ret != 0) { + err(); + goto err_buf; + } + + return buf; + +err_buf: + kfree(buf); + return ERR_PTR(ret); +} + +static int update_output_size(void __user *arg, size_t output_size_offset, + u32 old_value, u32 new_value) +{ + if (old_value != 0 && old_value != new_value) { + u32 __user *dest; + + dest = (u32 __user *)((char __user *)arg + output_size_offset); + return put_user(new_value, dest); + } + return 0; +} + +void *__ncr_get_input_args_no_output(void *fixed, struct nlattr *tb[], + size_t fixed_size, u32 *input_size_ptr, + size_t output_size_offset, + void __user *arg) +{ + void *attr_buf; + u32 output_size; + int ret; + + attr_buf = __ncr_get_input_args(fixed, tb, fixed_size, input_size_ptr, + arg); + if (IS_ERR(attr_buf)) + return attr_buf; + + output_size = *(const u32 *)((const char *)fixed + output_size_offset); + ret = update_output_size(arg, output_size_offset, output_size, + fixed_size); + if (ret != 0) { + kfree(attr_buf); + return ERR_PTR(ret); + } + return attr_buf; +} + +int __ncr_out_init(struct ncr_out *out, const void *fixed, size_t fixed_size, + size_t output_size_offset, void __user *arg) +{ + u32 output_size; + + /* NCR_OUT_INIT has verified fixed_size is correctly aligned for a + struct nlattr. */ + output_size = *(const u32 *)((const char *)fixed + output_size_offset); + if (output_size == 0) + out->left = 0; + else { + /* NCR_OUT_INIT has verified fixed_size is correctly aligned for + a struct nlattr. */ + if (output_size < fixed_size) + return -EINVAL; + out->left = min_t(size_t, output_size - fixed_size, + NCR_MAX_ATTR_SIZE); + } + out->buf = kmalloc(out->left, GFP_KERNEL); + if (out->buf == NULL) + return -ENOMEM; + out->p = out->buf; + out->arg = arg; + out->output_size_offset = output_size_offset; + out->fixed_size = fixed_size; + out->orig_output_size = output_size; + return 0; +} + +int ncr_out_finish(struct ncr_out *out) +{ + size_t buf_size; + + buf_size = (char *)out->p - (char *)out->buf; + if (buf_size != 0) { + if (unlikely(copy_to_user((char __user *)out->arg + + out->fixed_size, + out->buf, buf_size))) + return -EFAULT; + } + + return update_output_size(out->arg, out->output_size_offset, + out->orig_output_size, + out->fixed_size + buf_size); +} + +void ncr_out_free(struct ncr_out *out) +{ + kfree(out->buf); +} + +struct nlattr *ncr_out_reserve(struct ncr_out *out, int attrtype, int attrlen) +{ + size_t needed; + struct nlattr *nla; + + needed = nla_total_size(attrlen); + if (out->left < needed) + ERR_PTR(-ERANGE); + nla = out->p; + out->p = (char *)out->p + needed; + out->left -= needed; + + nla->nla_len = nla_attr_size(attrlen); + nla->nla_type = attrtype; + memset((unsigned char *)nla + nla->nla_len, 0, nla_padlen(attrlen)); + return nla; +} + +int ncr_out_put(struct ncr_out *out, int attrtype, int attrlen, const void *data) +{ + struct nlattr *nla; + + nla = ncr_out_reserve(out, attrtype, attrlen); + if (IS_ERR(nla)) + return PTR_ERR(nla); + memcpy(nla_data(nla), data, attrlen); + return 0; +} + +/** + * Initialize a nlattr with @attrtype as a buffer of maximum possible size in + * @out. The buffer must be finalized using ncr_out_commit_buffer. + */ +struct nlattr *ncr_out_begin_buffer(struct ncr_out *out, int attrtype) +{ + struct nlattr *nla; + + if (out->left < NLA_HDRLEN) + return ERR_PTR(-ERANGE); + nla = out->p; + + /* Ensure the rounding down of out->left does not decrease it below + NLA_HDRLEN. */ + BUILD_BUG_ON(NLA_ALIGN(NLA_HDRLEN) != NLA_HDRLEN); + nla->nla_len = out->left & ~(NLA_ALIGNTO - 1); + nla->nla_type = attrtype; + return nla; +} + +/** + * Set the length of buffer initialied in @out with ncr_out_begin_buffer() to + * @attrlen and allow adding more attributes. + */ +void ncr_out_commit_buffer(struct ncr_out *out, int attrlen) +{ + struct nlattr *nla; + size_t total; + + nla = out->p; + nla->nla_len = nla_attr_size(attrlen); + memset((unsigned char *)nla + nla->nla_len, 0, nla_padlen(attrlen)); + total = nla_total_size(attrlen); + + out->p = (char *)out->p + total; + out->left -= total; +} diff --git a/crypto/userspace/utils.h b/crypto/userspace/utils.h new file mode 100644 index 00000000000..2802afa7758 --- /dev/null +++ b/crypto/userspace/utils.h @@ -0,0 +1,98 @@ +#ifndef NCR_UTILS_H +#define NCR_UTILS_H + +#include <linux/kernel.h> +#include <linux/netlink.h> + +#define NCR_MAX_ATTR_SIZE 4096 + +struct nlattr; + +struct ncr_out { + void *buf, *p; + size_t left; + void __user *arg; + size_t output_size_offset, fixed_size; + u32 orig_output_size; +}; + +#define __NCR_VERIFY_FIXED_SIZE(fixed) \ + (BUILD_BUG_ON(sizeof(*(fixed)) != NLA_ALIGN(sizeof(*(fixed))))) +#define __NCR_VERIFY_TB(tb) (BUILD_BUG_ON(ARRAY_SIZE(tb) != NCR_ATTR_MAX + 1)) + +extern u32 __ncr_u32_type_check; +#define __OUT_SIZE_OFF(fixed) \ + ((void)(&(fixed)->output_size == &__ncr_u32_type_check), \ + (char *)&(fixed)->output_size - (char *)(fixed)) + + +/** + * Load *@fixed and a sequence of netlink-like attributes from @arg. @fixed + * contains "input_size", which is an u32 filled with total input size, + * including the attributes, which are parsed into @tb. + */ +#define NCR_GET_INPUT_ARGS(fixed, tb, arg) \ + (__NCR_VERIFY_FIXED_SIZE(fixed), \ + __NCR_VERIFY_TB(tb), \ + __ncr_get_input_args(fixed, tb, sizeof(*(fixed)), \ + &(fixed)->input_size, arg)) +void *__ncr_get_input_args(void *fixed, struct nlattr *tb[], size_t fixed_size, + u32 *input_size_ptr, const void __user *arg); + +/** + * Load *@fixed and a sequence of netlink-like attributes from @arg. @fixed + * contains "input_size", which is an u32 filled with total input size, + * including the attributes, which are parsed into @tb. In addition, indicate + * to the user through u32 "output_size" that no output attributes will be + * returned. + */ +#define NCR_GET_INPUT_ARGS_NO_OUTPUT(fixed, tb, arg) \ + (__NCR_VERIFY_FIXED_SIZE(fixed), \ + __NCR_VERIFY_TB(tb), \ + __ncr_get_input_args_no_output(fixed, tb, sizeof(*(fixed)), \ + &(fixed)->input_size, \ + __OUT_SIZE_OFF(fixed), arg)) +void *__ncr_get_input_args_no_output(void *fixed, struct nlattr *tb[], + size_t fixed_size, u32 *input_size_ptr, + size_t output_size_offset, + void __user *arg); + +/** + * Return a new output attribute context for attributes of *@fixed. @fixed + * contains "output_size", an u32 containing total output size, including + * @fixed. Store @arg for later ncr_out_finish(). + */ +#define NCR_OUT_INIT(out, fixed, arg) \ + (__NCR_VERIFY_FIXED_SIZE(fixed), \ + __ncr_out_init((out), (fixed), sizeof(*(fixed)), \ + __OUT_SIZE_OFF(fixed), (arg))) +int __ncr_out_init(struct ncr_out *out, const void *fixed, size_t fixed_size, + size_t output_size_offset, void __user *arg); + +/** + * Write attributes from @out to user space and update user-space output_size. + */ +int ncr_out_finish(struct ncr_out *out); + +void ncr_out_free(struct ncr_out *out); + +int ncr_out_put(struct ncr_out *out, int attrtype, int attrlen, + const void *data); + +static inline int ncr_out_put_u32(struct ncr_out *out, int attrtype, u32 value) +{ + return ncr_out_put(out, attrtype, sizeof(value), &value); +} + +static inline int ncr_out_put_string(struct ncr_out *out, int attrtype, + const char *value) +{ + return ncr_out_put(out, attrtype, strlen(value) + 1, value); +} + +struct nlattr *ncr_out_reserve(struct ncr_out *out, int attrtype, int attrlen); + +struct nlattr *ncr_out_begin_buffer(struct ncr_out *out, int attrtype); +void ncr_out_commit_buffer(struct ncr_out *out, int attrlen); + +#endif diff --git a/include/linux/Kbuild b/include/linux/Kbuild index f35589a2ab5..41790cd58dd 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -51,7 +51,6 @@ header-y += comstats.h header-y += const.h header-y += cgroupstats.h header-y += cramfs_fs.h -header-y += cryptodev.h header-y += cycx_cfm.h header-y += dcbnl.h header-y += dlmconstants.h diff --git a/include/linux/cryptodev.h b/include/linux/cryptodev.h deleted file mode 100644 index a916177eaa7..00000000000 --- a/include/linux/cryptodev.h +++ /dev/null @@ -1,151 +0,0 @@ -/* This is a source compatible implementation with the original API of - * cryptodev by Angelos D. Keromytis, found at openbsd cryptodev.h. - * Placed under public domain */ - -#ifndef _LINUX_CRYPTODEV_H -#define _LINUX_CRYPTODEV_H - -#include <linux/types.h> - -/* API extensions for linux */ -#define CRYPTO_HMAC_MAX_KEY_LEN 512 -#define CRYPTO_CIPHER_MAX_KEY_LEN 64 - -/* All the supported algorithms - */ -typedef enum { - CRYPTO_DES_CBC=1, - CRYPTO_3DES_CBC=2, - CRYPTO_BLF_CBC=3, - CRYPTO_CAST_CBC=4, - CRYPTO_SKIPJACK_CBC=5, - CRYPTO_MD5_HMAC=6, - CRYPTO_SHA1_HMAC=7, - CRYPTO_RIPEMD160_HMAC=8, - CRYPTO_MD5_KPDK=9, - CRYPTO_SHA1_KPDK=10, - CRYPTO_RIJNDAEL128_CBC=11, - CRYPTO_AES_CBC=CRYPTO_RIJNDAEL128_CBC, - CRYPTO_ARC4=12, - CRYPTO_MD5=13, - CRYPTO_SHA1=14, - CRYPTO_DEFLATE_COMP=15, - CRYPTO_NULL=16, - CRYPTO_LZS_COMP=17, - CRYPTO_SHA2_256_HMAC=18, - CRYPTO_SHA2_384_HMAC=19, - CRYPTO_SHA2_512_HMAC=20, - CRYPTO_AES_CTR=21, - CRYPTO_AES_XTS=22, - - CRYPTO_CAMELLIA_CBC=101, - CRYPTO_RIPEMD160, - CRYPTO_SHA2_256, - CRYPTO_SHA2_384, - CRYPTO_SHA2_512, - CRYPTO_ALGORITHM_ALL, /* Keep updated - see below */ -} cryptodev_crypto_op_t; -#define CRYPTO_ALGORITHM_MAX (CRYPTO_ALGORITHM_ALL - 1) - -/* Values for ciphers */ -#define DES_BLOCK_LEN 8 -#define DES3_BLOCK_LEN 8 -#define RIJNDAEL128_BLOCK_LEN 16 -#define AES_BLOCK_LEN RIJNDAEL128_BLOCK_LEN -#define CAMELLIA_BLOCK_LEN -#define BLOWFISH_BLOCK_LEN 8 -#define SKIPJACK_BLOCK_LEN 8 -#define CAST128_BLOCK_LEN 8 - -/* the maximum of the above */ -#define EALG_MAX_BLOCK_LEN 16 - -/* Values for hashes/MAC */ -#define AALG_MAX_RESULT_LEN 64 - -/* input of CIOCGSESSION */ -struct session_op { - /* Specify either cipher or mac - */ - __u32 cipher; /* cryptodev_crypto_op_t */ - __u32 mac; /* cryptodev_crypto_op_t */ - - __u32 keylen; - __u8 __user *key; - __u32 mackeylen; - __u8 __user *mackey; - - __u32 ses; /* session identifier */ -}; - -#define COP_ENCRYPT 0 -#define COP_DECRYPT 1 - -/* input of CIOCCRYPT */ - struct crypt_op { - __u32 ses; /* session identifier */ - __u16 op; /* COP_ENCRYPT or COP_DECRYPT */ - __u16 flags; /* no usage so far, use 0 */ - __u32 len; /* length of source data */ - __u8 __user *src; /* source data */ - __u8 __user *dst; /* pointer to output data */ - __u8 __user *mac; /* pointer to output data for hash/MAC operations */ - __u8 __user *iv; /* initialization vector for encryption operations */ -}; - -/* Stuff for bignum arithmetic and public key - * cryptography - not supported yet by linux - * cryptodev. - */ - -#define CRYPTO_ALG_FLAG_SUPPORTED 1 -#define CRYPTO_ALG_FLAG_RNG_ENABLE 2 -#define CRYPTO_ALG_FLAG_DSA_SHA 4 - -struct crparam { - __u8* crp_p; - __u32 crp_nbits; -}; - -#define CRK_MAXPARAM 8 - -/* input of CIOCKEY */ -struct crypt_kop { - __u32 crk_op; /* cryptodev_crk_ot_t */ - __u32 crk_status; - __u16 crk_iparams; - __u16 crk_oparams; - __u32 crk_pad1; - struct crparam crk_param[CRK_MAXPARAM]; -}; - -typedef enum { - CRK_MOD_EXP=0, - CRK_MOD_EXP_CRT=1, - CRK_DSA_SIGN=2, - CRK_DSA_VERIFY=3, - CRK_DH_COMPUTE_KEY=4, - CRK_ALGORITHM_ALL -} cryptodev_crk_op_t; - -#define CRK_ALGORITHM_MAX CRK_ALGORITHM_ALL-1 - -/* features to be queried with CIOCASYMFEAT ioctl - */ -#define CRF_MOD_EXP (1 << CRK_MOD_EXP) -#define CRF_MOD_EXP_CRT (1 << CRK_MOD_EXP_CRT) -#define CRF_DSA_SIGN (1 << CRK_DSA_SIGN) -#define CRF_DSA_VERIFY (1 << CRK_DSA_VERIFY) -#define CRF_DH_COMPUTE_KEY (1 << CRK_DH_COMPUTE_KEY) - - -/* ioctl's. Compatible with old linux cryptodev.h - */ -#define CRIOGET _IOWR('c', 101, __u32) -#define CIOCGSESSION _IOWR('c', 102, struct session_op) -#define CIOCFSESSION _IOW('c', 103, __u32) -#define CIOCCRYPT _IOWR('c', 104, struct crypt_op) -#define CIOCKEY _IOWR('c', 105, struct crypt_kop) -#define CIOCASYMFEAT _IOR('c', 106, __u32) - -#endif /* _LINUX_CRYPTODEV_H */ diff --git a/include/linux/ncr.h b/include/linux/ncr.h index 9c1f33a24b4..4f4d8a957ac 100644 --- a/include/linux/ncr.h +++ b/include/linux/ncr.h @@ -3,46 +3,75 @@ #include <linux/types.h> +/* Serves to make sure the structure is suitably aligned to continue with + a struct nlattr without external padding. + + 4 is NLA_ALIGNTO from <linux/netlink.h>, but if we + included <linux/netlink.h>, the user would have to include <sys/socket.h> + as well for no obvious reason. "4" is fixed by ABI. */ +#define __NL_ATTRIBUTES char __align[] __attribute__((aligned(4))) + +/* In all ioctls, input_size specifies size of the ncr_* structure and the + following attributes. + + output_size specifies space available for returning output, including the + initial ncr_* structure, and is updated by the ioctl() with the space + actually used. + + There are two special cases: input_size 0 means not attributes are supplied, + and is treated equivalent to sizeof(struct ncr_*). output_size 0 means no + space for output attributes is available, and is not updated. */ + +/* FIXME: better names for algorithm parameters? */ +/* FIXME: Split key generation/derivation attributes to decrease the number + of attributes used for the frequent operations? */ +enum { + NCR_ATTR_UNSPEC, /* 0 is special in lib/nlattr.c. */ + NCR_ATTR_ALGORITHM, /* NLA_NUL_STRING */ + NCR_ATTR_DERIVATION_ALGORITHM, /* NLA_NUL_STRING - NCR_DERIVE_* */ + NCR_ATTR_SIGNATURE_HASH_ALGORITHM, /* NLA_NUL_STRING */ + NCR_ATTR_WRAPPING_ALGORITHM, /* NLA_NUL_STRING - NCR_WALG_* */ + NCR_ATTR_UPDATE_INPUT_DATA, /* NLA_BINARY - ncr_session_input_data */ + /* NLA_BINARY - ncr_session_output_buffer */ + NCR_ATTR_UPDATE_OUTPUT_BUFFER, + NCR_ATTR_UPDATE_INPUT_KEY_AS_DATA, /* NLA_U32 - ncr_key_t */ + NCR_ATTR_FINAL_INPUT_DATA, /* NLA_BINARY - ncr_session_input_data */ + /* NLA_BINARY - ncr_session_output_buffer */ + NCR_ATTR_FINAL_OUTPUT_BUFFER, + NCR_ATTR_KEY, /* NLA_U32 - ncr_key_t */ + NCR_ATTR_KEY_FLAGS, /* NLA_U32 - NCR_KEY_FLAG_* */ + NCR_ATTR_KEY_ID, /* NLA_BINARY */ + NCR_ATTR_KEY_TYPE, /* NLA_U32 - ncr_key_type_t */ + NCR_ATTR_IV, /* NLA_BINARY */ + NCR_ATTR_SECRET_KEY_BITS, /* NLA_U32 */ + NCR_ATTR_RSA_MODULUS_BITS, /* NLA_U32 */ + NCR_ATTR_RSA_E, /* NLA_BINARY */ + NCR_ATTR_RSA_ENCODING_METHOD, /* NLA_U32 - ncr_rsa_type_t */ + NCR_ATTR_RSA_OAEP_HASH_ALGORITHM, /* NLA_NUL_STRING */ + NCR_ATTR_RSA_PSS_SALT_LENGTH, /* NLA_U32 */ + NCR_ATTR_DSA_P_BITS, /* NLA_U32 */ + NCR_ATTR_DSA_Q_BITS, /* NLA_U32 */ + NCR_ATTR_DH_PRIME, /* NLA_BINARY */ + NCR_ATTR_DH_BASE, /* NLA_BINARY */ + NCR_ATTR_DH_PUBLIC, /* NLA_BINARY */ + NCR_ATTR_WANTED_ATTRS, /* NLA_BINARY - array of u16 IDs */ + NCR_ATTR_SESSION_CLONE_FROM, /* NLA_U32 - ncr_session_t */ + + /* Add new attributes here */ + + NCR_ATTR_END__, + NCR_ATTR_MAX = NCR_ATTR_END__ - 1 +}; + #define NCR_CIPHER_MAX_BLOCK_LEN 32 #define NCR_HASH_MAX_OUTPUT_SIZE 64 -typedef enum { - NCR_ALG_NONE, - NCR_ALG_NULL, - NCR_ALG_3DES_CBC, - NCR_ALG_AES_CBC, - NCR_ALG_CAMELLIA_CBC, - NCR_ALG_ARCFOUR, - NCR_ALG_AES_ECB, - NCR_ALG_CAMELLIA_ECB, - NCR_ALG_AES_CTR, - NCR_ALG_CAMELLIA_CTR, - - NCR_ALG_SHA1=40, - NCR_ALG_MD5, - NCR_ALG_SHA2_224, - NCR_ALG_SHA2_256, - NCR_ALG_SHA2_384, - NCR_ALG_SHA2_512, - - NCR_ALG_HMAC_SHA1=80, - NCR_ALG_HMAC_MD5, - NCR_ALG_HMAC_SHA2_224, - NCR_ALG_HMAC_SHA2_256, - NCR_ALG_HMAC_SHA2_384, - NCR_ALG_HMAC_SHA2_512, - - NCR_ALG_RSA=140, - NCR_ALG_DSA, - NCR_ALG_DH, /* DH as in PKCS #3 */ -} ncr_algorithm_t; - - +/* Better names wanted */ +#define NCR_ALG_DSA_TRANSPARENT_HASH "__dsa_transparent_hash" +#define NCR_ALG_RSA_TRANSPARENT_HASH "__rsa_transparent_hash" -typedef enum { - NCR_WALG_AES_RFC3394, /* for secret keys only */ - NCR_WALG_AES_RFC5649, /* can wrap arbitrary key */ -} ncr_wrap_algorithm_t; +#define NCR_WALG_AES_RFC3394 "walg-aes-rfc3394" /* for secret keys only */ +#define NCR_WALG_AES_RFC5649 "walg-aes-rfc5649" /* can wrap arbitrary key */ typedef enum { NCR_KEY_TYPE_INVALID, @@ -54,7 +83,7 @@ typedef enum { /* Key handling */ -typedef int ncr_key_t; +typedef __s32 ncr_key_t; #define NCR_KEY_INVALID ((ncr_key_t)-1) @@ -67,46 +96,25 @@ typedef int ncr_key_t; */ #define NCR_KEY_FLAG_DECRYPT (1<<2) #define NCR_KEY_FLAG_SIGN (1<<3) +#define NCR_KEY_FLAG_ENCRYPT (1<<4) +#define NCR_KEY_FLAG_VERIFY (1<<5) +/* These flags can only be set by administrator, to prevent + * adversaries exporting wrappable keys with random ones. + */ +#define NCR_KEY_FLAG_WRAPPING (1<<6) +#define NCR_KEY_FLAG_UNWRAPPING (1<<7) -struct ncr_key_generate_params_st { - ncr_algorithm_t algorithm; /* just a cipher algorithm when - * generating secret keys - */ - - unsigned int keyflags; - union { - struct { - unsigned int bits; - } secret; - struct { - unsigned int bits; - unsigned long e; /* use zero for default */ - } rsa; - struct { - /* For DSS standard allowed values - * are: p:1024 q: 160 - * p:2048 q: 224 - * p:2048 q: 256 - * p:3072 q: 256 - */ - unsigned int p_bits; - unsigned int q_bits; - } dsa; - struct { - __u8 __user *p; /* prime */ - __kernel_size_t p_size; - __u8 __user *g; /* generator */ - __kernel_size_t g_size; - } dh; - } params; +struct ncr_key_generate { + __u32 input_size, output_size; + ncr_key_t key; + __NL_ATTRIBUTES; }; -/* used in generation - */ -struct ncr_key_generate_st { - ncr_key_t desc; - ncr_key_t desc2; /* public key when called with GENERATE_PAIR */ - struct ncr_key_generate_params_st params; +struct ncr_key_generate_pair { + __u32 input_size, output_size; + ncr_key_t private_key; + ncr_key_t public_key; + __NL_ATTRIBUTES; }; typedef enum { @@ -115,127 +123,109 @@ typedef enum { RSA_PKCS1_PSS, /* for signatures only */ } ncr_rsa_type_t; -/* used in derivation/encryption - */ -struct ncr_key_params_st { - /* this structure always corresponds to a key. Hence the - * parameters of the union selected are based on the corresponding - * key */ - union { - struct { - __u8 iv[NCR_CIPHER_MAX_BLOCK_LEN]; - __kernel_size_t iv_size; - } cipher; - struct { - __u8 __user *pub; - __kernel_size_t pub_size; - } dh; - struct { - ncr_rsa_type_t type; - ncr_algorithm_t oaep_hash; /* for OAEP */ - ncr_algorithm_t sign_hash; /* for signatures */ - unsigned int pss_salt; /* PSS signatures */ - } rsa; - struct { - ncr_algorithm_t sign_hash; /* for signatures */ - } dsa; - } params; -}; - -typedef enum { - NCR_DERIVE_DH=1, -} ncr_derive_t; +#define NCR_DERIVE_DH "dh" -struct ncr_key_derivation_params_st { - ncr_derive_t derive; /* the derivation algorithm */ - ncr_key_t newkey; - unsigned int keyflags; /* for new key */ - - ncr_key_t key; - struct ncr_key_params_st params; +struct ncr_key_derive { + __u32 input_size, output_size; + ncr_key_t input_key; + ncr_key_t new_key; + __NL_ATTRIBUTES; }; #define MAX_KEY_ID_SIZE 20 -struct ncr_key_info_st { - ncr_key_t key; /* input */ - - unsigned int flags; - ncr_key_type_t type; - ncr_algorithm_t algorithm; /* valid for public/private keys */ - - __u8 key_id[MAX_KEY_ID_SIZE]; - __kernel_size_t key_id_size; +struct ncr_key_get_info { + __u32 input_size, output_size; + ncr_key_t key; + __NL_ATTRIBUTES; }; -struct ncr_key_data_st { +struct ncr_key_import { + __u32 input_size, output_size; ncr_key_t key; + const void __user *data; + __u32 data_size; + __NL_ATTRIBUTES; +}; - void __user *idata; - __kernel_size_t idata_size; /* rw in get */ - - /* in case of import this will be used as key id */ - __u8 key_id[MAX_KEY_ID_SIZE]; - __kernel_size_t key_id_size; - ncr_key_type_t type; - unsigned int flags; - ncr_algorithm_t algorithm; /* valid for public/private keys */ +struct ncr_key_export { + __u32 input_size, output_size; + ncr_key_t key; + void __user *buffer; + int buffer_size; + __NL_ATTRIBUTES; }; -#define NCRIO_KEY_INIT _IOW ('c', 204, ncr_key_t) +#define NCRIO_KEY_INIT _IO('c', 0xC0) /* generate a secret key */ -#define NCRIO_KEY_GENERATE _IOR ('c', 205, struct ncr_key_generate_st) +#define NCRIO_KEY_GENERATE _IOWR('c', 0xC1, struct ncr_key_generate) /* generate a public key pair */ -#define NCRIO_KEY_GENERATE_PAIR _IOR ('c', 206, struct ncr_key_generate_st) +#define NCRIO_KEY_GENERATE_PAIR _IOWR('c', 0xC2, struct ncr_key_generate_pair) /* derive a new key from an old one */ -#define NCRIO_KEY_DERIVE _IOR ('c', 207, struct ncr_key_derivation_params_st) +#define NCRIO_KEY_DERIVE _IOWR('c', 0xC3, struct ncr_key_derive) /* return information on a key */ -#define NCRIO_KEY_GET_INFO _IOWR('c', 208, struct ncr_key_info_st) +#define NCRIO_KEY_GET_INFO _IOWR('c', 0xC4, struct ncr_key_get_info) /* export a secret key */ -#define NCRIO_KEY_EXPORT _IOWR('c', 209, struct ncr_key_data_st) +#define NCRIO_KEY_EXPORT _IOWR('c', 0xC5, struct ncr_key_export) /* import a secret key */ -#define NCRIO_KEY_IMPORT _IOWR('c', 210, struct ncr_key_data_st) +#define NCRIO_KEY_IMPORT _IOWR('c', 0xC6, struct ncr_key_import) -#define NCRIO_KEY_DEINIT _IOR ('c', 215, ncr_key_t) +#define NCRIO_KEY_DEINIT _IOW('c', 0xC7, ncr_key_t) /* Key wrap ioctls */ -struct ncr_key_wrap_st { - ncr_wrap_algorithm_t algorithm; - ncr_key_t keytowrap; - - ncr_key_t key; - struct ncr_key_params_st params; +struct ncr_key_wrap { + __u32 input_size, output_size; + ncr_key_t wrapping_key; + ncr_key_t source_key; + void __user *buffer; + int buffer_size; + __NL_ATTRIBUTES; +}; - void __user * io; /* encrypted keytowrap */ - /* this will be updated by the actual size on wrap */ - __kernel_size_t io_size; +struct ncr_key_unwrap { + __u32 input_size, output_size; + ncr_key_t wrapping_key; + ncr_key_t dest_key; + const void __user *data; + __u32 data_size; + __NL_ATTRIBUTES; }; -#define NCRIO_KEY_WRAP _IOWR ('c', 250, struct ncr_key_wrap_st) -#define NCRIO_KEY_UNWRAP _IOR ('c', 251, struct ncr_key_wrap_st) +#define NCRIO_KEY_WRAP _IOWR('c', 0xC8, struct ncr_key_wrap) +#define NCRIO_KEY_UNWRAP _IOWR('c', 0xC9, struct ncr_key_unwrap) /* Internal ops */ -struct ncr_master_key_st { - __u8 __user * key; - __u16 key_size; +struct ncr_master_key_set { + __u32 input_size, output_size; + const void __user *key; + __u32 key_size; + __NL_ATTRIBUTES; }; -#define NCRIO_MASTER_KEY_SET _IOR ('c', 260, struct ncr_master_key_st) +#define NCRIO_MASTER_KEY_SET _IOWR('c', 0xCA, struct ncr_master_key_set) /* These are similar to key_wrap and unwrap except that will store some extra * fields to be able to recover a key */ -struct ncr_key_storage_wrap_st { - ncr_key_t keytowrap; +struct ncr_key_storage_wrap { + __u32 input_size, output_size; + ncr_key_t key; + void __user *buffer; + int buffer_size; + __NL_ATTRIBUTES; +}; - void __user * io; /* encrypted keytowrap */ - /* this will be updated by the actual size on wrap */ - __kernel_size_t io_size; +struct ncr_key_storage_unwrap { + __u32 input_size, output_size; + ncr_key_t key; + const void __user *data; + __u32 data_size; + __NL_ATTRIBUTES; }; -#define NCRIO_KEY_STORAGE_WRAP _IOWR ('c', 261, struct ncr_key_storage_wrap_st) -#define NCRIO_KEY_STORAGE_UNWRAP _IOR ('c', 262, struct ncr_key_storage_wrap_st) +#define NCRIO_KEY_STORAGE_WRAP _IOWR('c', 0xCB, struct ncr_key_storage_wrap) +#define NCRIO_KEY_STORAGE_UNWRAP _IOWR('c', 0xCC, struct ncr_key_storage_wrap) /* Crypto Operations ioctls */ @@ -247,68 +237,49 @@ typedef enum { NCR_OP_VERIFY, } ncr_crypto_op_t; -typedef int ncr_session_t; +typedef __s32 ncr_session_t; #define NCR_SESSION_INVALID ((ncr_session_t)-1) -/* input of CIOCGSESSION */ -struct ncr_session_st { - /* input */ - ncr_algorithm_t algorithm; - - ncr_key_t key; - struct ncr_key_params_st params; - ncr_crypto_op_t op; - - /* output */ - ncr_session_t ses; /* session identifier */ +struct ncr_session_input_data { + const void __user *data; + __kernel_size_t data_size; }; -typedef enum { - NCR_SUCCESS = 0, - NCR_ERROR_GENERIC = -1, - NCR_VERIFICATION_FAILED = -2, -} ncr_error_t; +struct ncr_session_output_buffer { + void __user *buffer; + __kernel_size_t buffer_size; + __kernel_size_t __user *result_size_ptr; +}; -typedef enum { - NCR_KEY_DATA, - NCR_DIRECT_DATA, -} ncr_data_type_t; +struct ncr_session_init { + __u32 input_size, output_size; + __u32 op; /* ncr_crypto_op_t */ + __NL_ATTRIBUTES; +}; -struct ncr_session_op_st { - /* input */ +struct ncr_session_update { + __u32 input_size, output_size; ncr_session_t ses; + __NL_ATTRIBUTES; +}; - union { - struct { - ncr_key_t input; - void __user * output; /* when verifying signature this is - * the place of the signature. - */ - __kernel_size_t output_size; - } kdata; /* NCR_KEY_DATA */ - struct { - void __user * input; - __kernel_size_t input_size; - void __user * output; - __kernel_size_t output_size; - } udata; /* NCR_DIRECT_DATA */ - } data; - ncr_data_type_t type; - - /* output of verification */ - ncr_error_t err; +struct ncr_session_final { + __u32 input_size, output_size; + ncr_session_t ses; + __NL_ATTRIBUTES; }; -struct ncr_session_once_op_st { - struct ncr_session_st init; - struct ncr_session_op_st op; +struct ncr_session_once { + __u32 input_size, output_size; + ncr_crypto_op_t op; + __NL_ATTRIBUTES; }; -#define NCRIO_SESSION_INIT _IOR ('c', 300, struct ncr_session_st) -#define NCRIO_SESSION_UPDATE _IOWR ('c', 301, struct ncr_session_op_st) -#define NCRIO_SESSION_FINAL _IOWR ('c', 302, struct ncr_session_op_st) +#define NCRIO_SESSION_INIT _IOWR('c', 0xD0, struct ncr_session_init) +#define NCRIO_SESSION_UPDATE _IOWR('c', 0xD1, struct ncr_session_update) +#define NCRIO_SESSION_FINAL _IOWR('c', 0xD2, struct ncr_session_final) /* everything in one call */ -#define NCRIO_SESSION_ONCE _IOWR ('c', 303, struct ncr_session_once_op_st) +#define NCRIO_SESSION_ONCE _IOWR('c', 0xD3, struct ncr_session_once) #endif |