summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiloslav Trmač <mitr@redhat.com>2010-08-27 09:32:48 +0200
committerMiloslav Trmač <mitr@redhat.com>2010-08-27 09:32:48 +0200
commit9cab3a1a9660ed5f798b063aa7e827eb0c95ba94 (patch)
treefb5dadde55ce30a6634f113bb6ca35682833ecf3
parenta6d3a923924a7b751981d93ade82854985abebba (diff)
parent3d758323280763fe59b1f61d4f86aa2a29fe3775 (diff)
downloadkernel-crypto-9cab3a1a9660ed5f798b063aa7e827eb0c95ba94.tar.gz
kernel-crypto-9cab3a1a9660ed5f798b063aa7e827eb0c95ba94.tar.xz
kernel-crypto-9cab3a1a9660ed5f798b063aa7e827eb0c95ba94.zip
Merge branch 'standalone-master' into standalone-rename
Conflicts: TODO crypto.4 examples/Makefile examples/ncr.c examples/pk.c examples/speed.c include/linux/cryptodev.h userspace/setkey.c
-rw-r--r--.gitignore2
-rw-r--r--crypto/userspace/Makefile5
-rw-r--r--crypto/userspace/cryptodev_cipher.c47
-rw-r--r--crypto/userspace/cryptodev_int.h45
-rw-r--r--crypto/userspace/cryptodev_main.c833
-rw-r--r--crypto/userspace/libtomcrypt/hashes/hash_memory.c2
-rw-r--r--crypto/userspace/libtomcrypt/hashes/hash_memory_multi.c2
-rw-r--r--crypto/userspace/libtomcrypt/headers/tomcrypt_argchk.h4
-rw-r--r--crypto/userspace/libtomcrypt/headers/tomcrypt_cfg.h4
-rw-r--r--crypto/userspace/libtomcrypt/headers/tomcrypt_pk.h2
-rw-r--r--crypto/userspace/libtomcrypt/pk/rsa/rsa_import.c2
-rw-r--r--crypto/userspace/ncr-dh.c13
-rw-r--r--crypto/userspace/ncr-dh.h2
-rw-r--r--crypto/userspace/ncr-int.h148
-rw-r--r--crypto/userspace/ncr-key-storage.c14
-rw-r--r--crypto/userspace/ncr-key-wrap.c537
-rw-r--r--crypto/userspace/ncr-key.c390
-rw-r--r--crypto/userspace/ncr-limits.c1
-rw-r--r--crypto/userspace/ncr-pk.c349
-rw-r--r--crypto/userspace/ncr-pk.h26
-rw-r--r--crypto/userspace/ncr-sessions.c1032
-rw-r--r--crypto/userspace/ncr.c415
-rw-r--r--include/linux/cryptodev.h154
-rw-r--r--include/linux/ncr.h389
-rw-r--r--utils.c298
-rw-r--r--utils.h98
26 files changed, 2666 insertions, 2148 deletions
diff --git a/.gitignore b/.gitignore
index 4379d92e956..46c85a4058e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,8 +7,6 @@ Module.symvers
*.o
*.mod.c
modules.order
-examples/cipher
-examples/hmac
examples/ncr
examples/pk
examples/speed
diff --git a/crypto/userspace/Makefile b/crypto/userspace/Makefile
index 0e3354053d3..cdaab9339e3 100644
--- a/crypto/userspace/Makefile
+++ b/crypto/userspace/Makefile
@@ -68,7 +68,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)
@@ -80,8 +80,7 @@ build:
install:
$(MAKE) -C $(KERNEL_DIR) SUBDIRS=`pwd` modules_install
- @echo "Installing cryptodev.h in /usr/include/crypto ..."
- @install -D cryptodev.h /usr/include/crypto/cryptodev.h
+ @echo "Installing ncr.h in /usr/include/crypto ..."
@install -D ncr.h /usr/include/crypto/ncr.h
clean:
diff --git a/crypto/userspace/cryptodev_cipher.c b/crypto/userspace/cryptodev_cipher.c
index 832202789b6..1fb11473715 100644
--- a/crypto/userspace/cryptodev_cipher.c
+++ b/crypto/userspace/cryptodev_cipher.c
@@ -31,7 +31,6 @@
#include <linux/uaccess.h>
#include <crypto/algapi.h>
#include <crypto/hash.h>
-#include "cryptodev.h"
#include "cryptodev_int.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 b4059febbb0..4b140ba9820 100644
--- a/crypto/userspace/cryptodev_int.h
+++ b/crypto/userspace/cryptodev_int.h
@@ -11,7 +11,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/scatterlist.h>
-#include <cryptodev.h>
+#include <ncr.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 da75672615a..2a11fdb1258 100644
--- a/crypto/userspace/cryptodev_main.c
+++ b/crypto/userspace/cryptodev_main.c
@@ -39,7 +39,6 @@
#include <linux/syscalls.h>
#include <linux/pagemap.h>
#include <linux/uaccess.h>
-#include "cryptodev.h"
#include <linux/scatterlist.h>
#include "cryptodev_int.h"
#include "ncr-int.h"
@@ -50,452 +49,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)
{
@@ -538,415 +98,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 */
@@ -955,7 +158,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 235d021fae4..bc45723fe3e 100644
--- a/crypto/userspace/ncr-dh.c
+++ b/crypto/userspace/ncr-dh.c
@@ -27,7 +27,6 @@
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/uaccess.h>
-#include "cryptodev.h"
#include <linux/scatterlist.h>
#include <ncr.h>
#include <ncr-int.h>
@@ -281,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 2689804e29d..87964b08065 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 "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 fc6948f5c61..9afa2dc0411 100644
--- a/crypto/userspace/ncr-key-storage.c
+++ b/crypto/userspace/ncr-key-storage.c
@@ -26,16 +26,16 @@
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
-#include "cryptodev.h"
#include <linux/scatterlist.h>
#include "ncr.h"
#include "ncr-int.h"
#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;
@@ -43,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;
@@ -54,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);
@@ -91,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 0c56def27f7..7ea70cfc847 100644
--- a/crypto/userspace/ncr-key-wrap.c
+++ b/crypto/userspace/ncr-key-wrap.c
@@ -28,16 +28,22 @@
#include <linux/highmem.h>
#include <linux/random.h>
#include <linux/uaccess.h>
-#include "cryptodev.h"
#include <linux/scatterlist.h>
+#include <net/netlink.h>
#include "ncr.h"
#include "ncr-int.h"
#include "cryptodev_int.h"
+#define KEY_WRAP_VERSION 0
+
typedef uint8_t val64_t[8];
static const val64_t initA = "\xA6\xA6\xA6\xA6\xA6\xA6\xA6\xA6";
+static int key_to_packed_data( uint8_t** sdata, size_t * sdata_size, const struct key_item_st *key);
+static int key_from_packed_data(struct nlattr *tb[], struct key_item_st* key,
+ const void* data, size_t data_size);
+
static void val64_xor( val64_t val, uint32_t x)
{
@@ -56,7 +62,7 @@ int i,j;
if (*output_size < (n+1)*8) {
err();
- return -EINVAL;
+ return -ERANGE;
}
memcpy(A, iv, 8);
@@ -259,29 +265,73 @@ cleanup:
static int wrap_aes_rfc5649(struct key_item_st* tobewrapped, struct key_item_st *kek,
void* output, size_t* output_size, const void* iv, size_t iv_size)
{
- if (tobewrapped->type != NCR_KEY_TYPE_SECRET) {
+int ret;
+uint8_t* sdata = NULL;
+size_t sdata_size = 0;
+
+ ret = key_to_packed_data(&sdata, &sdata_size, tobewrapped);
+ if (ret < 0) {
err();
- return -EINVAL;
+ return ret;
}
- return _wrap_aes_rfc5649(tobewrapped->key.secret.data, tobewrapped->key.secret.size,
+ ret = _wrap_aes_rfc5649(sdata, sdata_size,
kek, output, output_size, iv, iv_size);
+
+ kfree(sdata);
+ return ret;
}
static int unwrap_aes_rfc5649(struct key_item_st* output, struct key_item_st *kek,
- void* wrapped, size_t wrapped_size, const void* iv, size_t iv_size)
+ void *wrapped, size_t wrapped_size, struct nlattr *tb[])
{
- output->type = NCR_KEY_TYPE_SECRET;
+const struct nlattr *nla;
+int ret, iv_size;
+void * sdata;
+size_t sdata_size = KEY_DATA_MAX_SIZE;
+const uint8_t *iv;
+
+ sdata = kmalloc(sdata_size, GFP_KERNEL);
+ if (sdata == NULL) {
+ err();
+ return -ENOMEM;
+ }
+
+ nla = tb[NCR_ATTR_IV];
+ if (nla != NULL) {
+ iv = nla_data(nla);
+ iv_size = nla_len(nla);
+ } else {
+ iv = NULL;
+ iv_size = 0;
+ }
+
+ ret = _unwrap_aes_rfc5649(sdata, &sdata_size, kek,
+ wrapped, wrapped_size, iv, iv_size);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ ret = key_from_packed_data(tb, output, sdata, sdata_size);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ ret = 0;
+
+fail:
+ kfree(sdata);
+ return ret;
- return _unwrap_aes_rfc5649(output->key.secret.data, &output->key.secret.size, kek,
- wrapped, wrapped_size, iv, iv_size);
}
/* Wraps using the RFC3394 way.
*/
-static int wrap_aes(struct key_item_st* tobewrapped, struct key_item_st *kek,
+static int wrap_aes_rfc3394(struct key_item_st* tobewrapped, struct key_item_st *kek,
void* output, size_t *output_size, const void* iv, size_t iv_size)
{
size_t key_size, n;
@@ -358,15 +408,25 @@ void print_val64(char* str, val64_t val)
}
#endif
-static int unwrap_aes(struct key_item_st* output, struct key_item_st *kek,
- void* wrapped_key, size_t wrapped_key_size, const void* iv, size_t iv_size)
+static int unwrap_aes_rfc3394(struct key_item_st* output, struct key_item_st *kek,
+ void* wrapped_key, size_t wrapped_key_size,
+ struct nlattr *tb[])
{
+const struct nlattr *nla;
size_t n;
val64_t A;
int i, ret;
struct cipher_data ctx;
val64_t * R = NULL;
-
+int iv_size;
+const uint8_t *iv;
+
+ nla = tb[NCR_ATTR_IV];
+ if (nla != NULL) {
+ iv = nla_data(nla);
+ iv_size = nla_len(nla);
+ } else
+ iv_size = 0;
if (iv_size < sizeof(initA)) {
iv_size = sizeof(initA);
iv = initA;
@@ -413,12 +473,17 @@ val64_t * R = NULL;
goto cleanup;
}
+ ret = ncr_key_update_flags(output, tb[NCR_ATTR_KEY_FLAGS]);
+ if (ret != 0) {
+ err();
+ goto cleanup;
+ }
+
memset(&output->key, 0, sizeof(output->key));
for (i=0;i<n;i++) {
memcpy(&output->key.secret.data[i*8], R[i], sizeof(R[i]));
}
output->key.secret.size = n*8;
- output->flags = NCR_KEY_FLAG_WRAPPABLE;
output->type = NCR_KEY_TYPE_SECRET;
ret = 0;
@@ -430,21 +495,54 @@ cleanup:
return ret;
}
-int ncr_key_wrap(struct ncr_lists *lst, void __user* arg)
+/* will check if the kek is of equal or higher security level than
+ * wkey. To prevent encrypting a 256 bit key with an 128 bit one.
+ */
+static int check_key_level(struct key_item_st* kek, struct key_item_st* wkey)
+{
+int kek_level, wkey_level;
+
+ /* allow wrapping of public keys with any key */
+ if (wkey->type == NCR_KEY_TYPE_PUBLIC)
+ return 0;
+
+ kek_level = _ncr_key_get_sec_level(kek);
+ if (kek_level < 0) {
+ err();
+ return kek_level;
+ }
+
+ wkey_level = _ncr_key_get_sec_level(wkey);
+ if (wkey_level < 0) {
+ err();
+ return wkey_level;
+ }
+
+ if (wkey_level > kek_level) {
+ err();
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+int ncr_key_wrap(struct ncr_lists *lst, const struct ncr_key_wrap *wrap,
+ struct nlattr *tb[])
{
-struct ncr_key_wrap_st wrap;
+const struct nlattr *nla;
struct key_item_st* wkey = NULL;
struct key_item_st* key = NULL;
void* data = NULL;
-size_t data_size;
+const void *iv;
+size_t data_size, iv_size;
int ret;
- if (unlikely(copy_from_user(&wrap, arg, sizeof(wrap)))) {
+ if (wrap->buffer_size < 0) {
err();
- return -EFAULT;
+ return -EINVAL;
}
- ret = ncr_key_item_get_read( &wkey, lst, wrap.keytowrap);
+ ret = ncr_key_item_get_read(&wkey, lst, wrap->source_key);
if (ret < 0) {
err();
return ret;
@@ -456,13 +554,25 @@ int ret;
goto fail;
}
- ret = ncr_key_item_get_read( &key, lst, wrap.key);
+ ret = ncr_key_item_get_read(&key, lst, wrap->wrapping_key);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ if (!(key->flags & NCR_KEY_FLAG_WRAPPING)) {
+ err();
+ ret = -EPERM;
+ goto fail;
+ }
+
+ ret = check_key_level(key, wkey);
if (ret < 0) {
err();
goto fail;
}
- data_size = wrap.io_size;
+ data_size = wrap->buffer_size;
data = kmalloc(data_size, GFP_KERNEL);
if (data == NULL) {
err();
@@ -470,18 +580,30 @@ int ret;
goto fail;
}
- switch(wrap.algorithm) {
- case NCR_WALG_AES_RFC3394:
- ret = wrap_aes(wkey, key, data, &data_size,
- wrap.params.params.cipher.iv, wrap.params.params.cipher.iv_size);
- break;
- case NCR_WALG_AES_RFC5649:
- ret = wrap_aes_rfc5649(wkey, key, data, &data_size,
- wrap.params.params.cipher.iv, wrap.params.params.cipher.iv_size);
- break;
- default:
- err();
- ret = -EINVAL;
+ nla = tb[NCR_ATTR_IV];
+ if (nla != NULL) {
+ iv = nla_data(nla);
+ iv_size = nla_len(nla);
+ } else {
+ iv = NULL;
+ iv_size = 0;
+ }
+
+ nla = tb[NCR_ATTR_WRAPPING_ALGORITHM];
+ if (nla == NULL) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+ if (nla_strcmp(nla, NCR_WALG_AES_RFC3394) == 0)
+ ret = wrap_aes_rfc3394(wkey, key, data, &data_size, iv,
+ iv_size);
+ else if (nla_strcmp(nla, NCR_WALG_AES_RFC5649) == 0)
+ ret = wrap_aes_rfc5649(wkey, key, data, &data_size, iv,
+ iv_size);
+ else {
+ err();
+ ret = -EINVAL;
}
if (ret < 0) {
@@ -489,18 +611,13 @@ int ret;
goto fail;
}
- ret = copy_to_user(wrap.io, data, data_size);
+ ret = copy_to_user(wrap->buffer, data, data_size);
if (unlikely(ret)) {
ret = -EFAULT;
goto fail;
}
- wrap.io_size = data_size;
-
- ret = copy_to_user(arg, &wrap, sizeof(wrap));
- if (unlikely(ret)) {
- ret = -EFAULT;
- }
+ ret = data_size;
fail:
if (wkey != NULL) _ncr_key_item_put(wkey);
@@ -513,33 +630,35 @@ fail:
/* Unwraps keys. All keys unwrapped are not accessible by
* userspace.
*/
-int ncr_key_unwrap(struct ncr_lists *lst, void __user* arg)
+int ncr_key_unwrap(struct ncr_lists *lst, const struct ncr_key_unwrap *wrap,
+ struct nlattr *tb[])
{
-struct ncr_key_wrap_st wrap;
+const struct nlattr *nla;
struct key_item_st* wkey = NULL;
struct key_item_st* key = NULL;
void* data = NULL;
size_t data_size;
int ret;
- if (unlikely(copy_from_user(&wrap, arg, sizeof(wrap)))) {
+ ret = ncr_key_item_get_write(&wkey, lst, wrap->dest_key);
+ if (ret < 0) {
err();
- return -EFAULT;
+ return ret;
}
- ret = ncr_key_item_get_write( &wkey, lst, wrap.keytowrap);
+ ret = ncr_key_item_get_read(&key, lst, wrap->wrapping_key);
if (ret < 0) {
err();
- return ret;
+ goto fail;
}
- ret = ncr_key_item_get_read( &key, lst, wrap.key);
- if (ret < 0) {
+ if (!(key->flags & NCR_KEY_FLAG_UNWRAPPING)) {
err();
+ ret = -EPERM;
goto fail;
}
- data_size = wrap.io_size;
+ data_size = wrap->data_size;
data = kmalloc(data_size, GFP_KERNEL);
if (data == NULL) {
err();
@@ -547,24 +666,27 @@ int ret;
goto fail;
}
- if (unlikely(copy_from_user(data, wrap.io, data_size))) {
+ if (unlikely(copy_from_user(data, wrap->data, data_size))) {
err();
ret = -EFAULT;
goto fail;
}
+
+ ncr_key_clear(wkey);
- switch(wrap.algorithm) {
- case NCR_WALG_AES_RFC3394:
- ret = unwrap_aes(wkey, key, data, data_size,
- wrap.params.params.cipher.iv, wrap.params.params.cipher.iv_size);
- break;
- case NCR_WALG_AES_RFC5649:
- ret = unwrap_aes_rfc5649(wkey, key, data, data_size,
- wrap.params.params.cipher.iv, wrap.params.params.cipher.iv_size);
- break;
- default:
- err();
- ret = -EINVAL;
+ nla = tb[NCR_ATTR_WRAPPING_ALGORITHM];
+ if (nla == NULL) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+ if (nla_strcmp(nla, NCR_WALG_AES_RFC3394) == 0)
+ ret = unwrap_aes_rfc3394(wkey, key, data, data_size, tb);
+ else if (nla_strcmp(nla, NCR_WALG_AES_RFC5649) == 0)
+ ret = unwrap_aes_rfc5649(wkey, key, data, data_size, tb);
+ else {
+ err();
+ ret = -EINVAL;
}
fail:
@@ -575,9 +697,10 @@ fail:
return ret;
}
-int ncr_key_storage_wrap(struct ncr_lists *lst, void __user* arg)
+int ncr_key_storage_wrap(struct ncr_lists *lst,
+ const struct ncr_key_storage_wrap *wrap,
+ struct nlattr *tb[])
{
-struct ncr_key_storage_wrap_st wrap;
struct key_item_st* wkey = NULL;
void* data = NULL;
size_t data_size;
@@ -590,24 +713,18 @@ int ret;
return -ENOKEY;
}
- if (unlikely(copy_from_user(&wrap, arg, sizeof(wrap)))) {
+ if (wrap->buffer_size < 0) {
err();
- return -EFAULT;
+ return -EINVAL;
}
- ret = ncr_key_item_get_read( &wkey, lst, wrap.keytowrap);
+ ret = ncr_key_item_get_read(&wkey, lst, wrap->key);
if (ret < 0) {
err();
return ret;
}
- if (!(wkey->flags & NCR_KEY_FLAG_WRAPPABLE)) {
- err();
- ret = -EPERM;
- goto fail;
- }
-
- data_size = wrap.io_size;
+ data_size = wrap->buffer_size;
data = kmalloc(data_size, GFP_KERNEL);
if (data == NULL) {
err();
@@ -627,18 +744,13 @@ int ret;
goto fail;
}
- ret = copy_to_user(wrap.io, data, data_size);
+ ret = copy_to_user(wrap->buffer, data, data_size);
if (unlikely(ret)) {
ret = -EFAULT;
goto fail;
}
- wrap.io_size = data_size;
-
- ret = copy_to_user(arg, &wrap, sizeof(wrap));
- if (unlikely(ret)) {
- ret = -EFAULT;
- }
+ ret = data_size;
fail:
if (wkey != NULL) _ncr_key_item_put(wkey);
@@ -648,12 +760,10 @@ fail:
return ret;
}
-/* Unwraps keys. All keys unwrapped are not accessible by
- * userspace.
- */
-int ncr_key_storage_unwrap(struct ncr_lists *lst, void __user* arg)
+int ncr_key_storage_unwrap(struct ncr_lists *lst,
+ const struct ncr_key_storage_unwrap *wrap,
+ struct nlattr *tb[])
{
-struct ncr_key_storage_wrap_st wrap;
struct key_item_st* wkey = NULL;
void* data = NULL;
uint8_t * sdata = NULL;
@@ -665,18 +775,13 @@ int ret;
return -ENOKEY;
}
- if (unlikely(copy_from_user(&wrap, arg, sizeof(wrap)))) {
- err();
- return -EFAULT;
- }
-
- ret = ncr_key_item_get_write( &wkey, lst, wrap.keytowrap);
+ ret = ncr_key_item_get_write(&wkey, lst, wrap->key);
if (ret < 0) {
err();
return ret;
}
- data_size = wrap.io_size;
+ data_size = wrap->data_size;
data = kmalloc(data_size, GFP_KERNEL);
if (data == NULL) {
err();
@@ -684,7 +789,7 @@ int ret;
goto fail;
}
- if (unlikely(copy_from_user(data, wrap.io, data_size))) {
+ if (unlikely(copy_from_user(data, wrap->data, data_size))) {
err();
ret = -EFAULT;
goto fail;
@@ -698,14 +803,14 @@ int ret;
goto fail;
}
- wkey->flags = NCR_KEY_FLAG_WRAPPABLE;
-
ret = _unwrap_aes_rfc5649(sdata, &sdata_size, &master_key, data, data_size, NULL, 0);
if (ret < 0) {
err();
goto fail;
}
+ ncr_key_clear(wkey);
+
ret = key_from_storage_data(wkey, sdata, sdata_size);
if (ret < 0) {
err();
@@ -720,3 +825,241 @@ fail:
return ret;
}
+
+#define DER_KEY_MAX_SIZE (KEY_DATA_MAX_SIZE+16)
+
+/* Packed data are DER encoded:
+ * PackedData ::= SEQUENCE {
+ * version INTEGER { v1(0) }
+ * type INTEGER { secret_key(0), rsa_privkey(1), rsa_pubkey(2), dsa_privkey(3), dsa_pubkey(4),
+ * dh_privkey(5), dh_pubkey(6) },
+ * data OCTET STRING
+ * }
+ *
+ * This allows distinguishing types of wrapped keys.
+ */
+static int key_to_packed_data( uint8_t** sdata, size_t * sdata_size, const struct key_item_st *key)
+{
+ uint8_t * pkey = NULL;
+ uint8_t * derkey = NULL;
+ uint32_t pkey_size;
+ int ret, err;
+ unsigned long version = KEY_WRAP_VERSION;
+ unsigned long type;
+ unsigned long derlen;
+
+ *sdata_size = KEY_DATA_MAX_SIZE;
+ pkey = kmalloc(*sdata_size, GFP_KERNEL);
+ if (pkey == NULL) {
+ err();
+ return -ENOMEM;
+ }
+
+ derlen = DER_KEY_MAX_SIZE;
+ derkey = kmalloc(derlen, GFP_KERNEL);
+ if (derkey == NULL) {
+ err();
+ goto fail;
+ }
+
+ if (key->type == NCR_KEY_TYPE_SECRET) {
+ memcpy(pkey, key->key.secret.data, key->key.secret.size);
+ pkey_size = key->key.secret.size;
+
+ type = 0;
+ } else if (key->type == NCR_KEY_TYPE_PRIVATE || key->type == NCR_KEY_TYPE_PUBLIC) {
+ pkey_size = *sdata_size;
+ ret = ncr_pk_pack( key, pkey, &pkey_size);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ switch (key->algorithm->algo) {
+ case NCR_ALG_RSA:
+ if (key->type == NCR_KEY_TYPE_PUBLIC)
+ type = 2;
+ else type = 1;
+ break;
+ case NCR_ALG_DSA:
+ if (key->type == NCR_KEY_TYPE_PUBLIC)
+ type = 4;
+ else type = 3;
+ break;
+ case NCR_ALG_DH:
+ if (key->type == NCR_KEY_TYPE_PUBLIC)
+ type = 6;
+ else type = 5;
+ break;
+ default:
+ /* unsupported yet */
+ ret = -EINVAL;
+ err();
+ goto fail;
+ }
+
+ } else {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ err = der_encode_sequence_multi(derkey, &derlen,
+ LTC_ASN1_SHORT_INTEGER, 1UL, &version,
+ LTC_ASN1_SHORT_INTEGER, 1UL, &type,
+ LTC_ASN1_OCTET_STRING, (unsigned long)pkey_size, pkey,
+ LTC_ASN1_EOL, 0UL, NULL);
+
+ kfree(pkey);
+
+ if (err != CRYPT_OK) {
+ err();
+ ret = _ncr_tomerr(err);
+ goto fail;
+ }
+
+ *sdata = (void*)derkey;
+ *sdata_size = derlen;
+
+ return 0;
+fail:
+ kfree(pkey);
+ kfree(derkey);
+
+ return ret;
+}
+
+inline static int packed_type_to_key_type(unsigned long type, struct key_item_st* key)
+{
+ switch(type) {
+ case 0:
+ key->type = NCR_KEY_TYPE_SECRET;
+ key->algorithm = _ncr_algo_to_properties("cbc(aes)");
+ break;
+ case 1:
+ key->type = NCR_KEY_TYPE_PRIVATE;
+ key->algorithm = _ncr_algo_to_properties("rsa");
+ break;
+ case 2:
+ key->type = NCR_KEY_TYPE_PUBLIC;
+ key->algorithm = _ncr_algo_to_properties("rsa");
+ break;
+ case 3:
+ key->type = NCR_KEY_TYPE_PRIVATE;
+ key->algorithm = _ncr_algo_to_properties("dsa");
+ break;
+ case 4:
+ key->type = NCR_KEY_TYPE_PUBLIC;
+ key->algorithm = _ncr_algo_to_properties("dsa");
+ break;
+ case 5:
+ key->type = NCR_KEY_TYPE_PRIVATE;
+ key->algorithm = _ncr_algo_to_properties("dh");
+ break;
+ case 6:
+ key->type = NCR_KEY_TYPE_PUBLIC;
+ key->algorithm = _ncr_algo_to_properties("dh");
+ break;
+ default:
+ err();
+ return -EINVAL;
+ }
+
+ if (key->algorithm == NULL) {
+ err();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+
+/* Unpack, or better decode the DER data
+ */
+static int key_from_packed_data(struct nlattr *tb[], struct key_item_st *key,
+ const void *data, size_t data_size)
+{
+ ltc_asn1_list list[6];
+ int ret, i = 0, pkey_size, err;
+ unsigned long version, type;
+ uint8_t * pkey = NULL;
+
+ if (data_size > DER_KEY_MAX_SIZE) {
+ err();
+ return -EINVAL;
+ }
+
+ pkey_size = KEY_DATA_MAX_SIZE;
+ pkey = kmalloc(pkey_size, GFP_KERNEL);
+ if (pkey == NULL) {
+ err();
+ return -ENOMEM;
+ }
+
+ list[i].type = LTC_ASN1_SHORT_INTEGER;
+ list[i].size = 1;
+ list[i++].data = &version;
+
+ list[i].type = LTC_ASN1_SHORT_INTEGER;
+ list[i].size = 1;
+ list[i++].data = &type;
+
+ list[i].type = LTC_ASN1_OCTET_STRING;
+ list[i].size = pkey_size;
+ list[i++].data = pkey;
+
+ err = der_decode_sequence(data, data_size, list, i);
+ if (err != CRYPT_OK) {
+ err();
+ ret = _ncr_tomerr(err);
+ goto fail;
+ }
+
+ if (version != KEY_WRAP_VERSION) {
+ err();
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ pkey_size = list[2].size;
+
+ ret = packed_type_to_key_type(type, key);
+ if (ret < 0) {
+ err();
+ goto fail;
+ }
+
+ ret = ncr_key_update_flags(key, tb[NCR_ATTR_KEY_FLAGS]);
+ if (ret != 0) {
+ err();
+ return ret;
+ }
+
+ if (key->type == NCR_KEY_TYPE_SECRET) {
+ if (data_size > NCR_CIPHER_MAX_KEY_LEN) {
+ err();
+ return -EINVAL;
+ }
+ key->key.secret.size = pkey_size;
+ memcpy(key->key.secret.data, pkey, pkey_size);
+ } else if (key->type == NCR_KEY_TYPE_PUBLIC
+ || key->type == NCR_KEY_TYPE_PRIVATE) {
+
+ ret = ncr_pk_unpack( key, pkey, pkey_size);
+ if (ret < 0) {
+ err();
+ return ret;
+ }
+ } else {
+ err();
+ return -EINVAL;
+ }
+
+ ret = 0;
+
+fail:
+ kfree(pkey);
+
+ return ret;
+}
diff --git a/crypto/userspace/ncr-key.c b/crypto/userspace/ncr-key.c
index bf438fa22cc..406e03299c3 100644
--- a/crypto/userspace/ncr-key.c
+++ b/crypto/userspace/ncr-key.c
@@ -27,12 +27,11 @@
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/uaccess.h>
-#include "cryptodev.h"
#include <linux/scatterlist.h>
+#include <net/netlink.h>
#include "ncr.h"
#include "ncr-int.h"
-
-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)
{
@@ -151,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;
@@ -190,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;
@@ -246,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;
@@ -262,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) {
@@ -276,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();
@@ -297,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)
@@ -311,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;
@@ -335,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;
@@ -405,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 ||
@@ -423,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;
@@ -445,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;
@@ -454,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;
@@ -486,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;
@@ -509,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);
@@ -521,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;
@@ -549,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;
@@ -558,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;
@@ -585,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;
@@ -614,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;
@@ -631,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 34acac708d0..0c12824a211 100644
--- a/crypto/userspace/ncr-limits.c
+++ b/crypto/userspace/ncr-limits.c
@@ -29,7 +29,6 @@
#include <linux/slab.h>
#include <linux/highmem.h>
#include <linux/random.h>
-#include "cryptodev.h"
#include <asm/atomic.h>
#include <linux/version.h>
#include <linux/file.h>
diff --git a/crypto/userspace/ncr-pk.c b/crypto/userspace/ncr-pk.c
index 1e83163c155..9b9078efe38 100644
--- a/crypto/userspace/ncr-pk.c
+++ b/crypto/userspace/ncr-pk.c
@@ -27,8 +27,8 @@
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/uaccess.h>
-#include "cryptodev.h"
#include <linux/scatterlist.h>
+#include <net/netlink.h>
#include "ncr.h"
#include "ncr-int.h"
#include <tomcrypt.h>
@@ -37,7 +37,7 @@ int _ncr_tomerr(int err)
{
switch (err) {
case CRYPT_BUFFER_OVERFLOW:
- return -EOVERFLOW;
+ return -ERANGE;
case CRYPT_MEM:
return -ENOMEM;
default:
@@ -123,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);
@@ -167,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;
}
@@ -222,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;
@@ -309,8 +324,6 @@ int ncr_pk_generate(const struct algo_properties_st *algo,
}
fail:
- kfree(tmp);
-
if (ret < 0) {
err();
return ret;
@@ -325,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
*/
@@ -356,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;
}
@@ -369,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;
@@ -531,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:
@@ -562,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:
@@ -626,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();
@@ -662,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 2916729b91a..c65db2f751e 100644
--- a/crypto/userspace/ncr-sessions.c
+++ b/crypto/userspace/ncr-sessions.c
@@ -25,14 +25,46 @@
#include <linux/crypto.h>
#include <linux/mutex.h>
-#include "cryptodev.h"
#include "ncr.h"
#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)
{
@@ -52,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);
@@ -70,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);
@@ -84,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;
@@ -103,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;
@@ -129,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) {
@@ -244,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;
@@ -270,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;
@@ -300,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();
@@ -335,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;
}
@@ -348,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;
@@ -379,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)
{
@@ -445,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)
@@ -474,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;
@@ -518,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;
@@ -589,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 {
@@ -610,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:
@@ -648,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) {
@@ -670,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:
@@ -707,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) {
@@ -930,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) {
@@ -946,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 81e3fa32cef..55b40ef6d24 100644
--- a/crypto/userspace/ncr.c
+++ b/crypto/userspace/ncr.c
@@ -22,18 +22,20 @@
* 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>
#include <linux/highmem.h>
#include <linux/random.h>
#include <linux/uaccess.h>
-#include "cryptodev.h"
#include <linux/scatterlist.h>
#include <linux/cred.h>
#include <linux/capability.h>
+#include <net/netlink.h>
#include "ncr.h"
#include "ncr-int.h"
+#include "utils.h"
#include <linux/workqueue.h>
/* This is the master wrapping key for storage of keys
@@ -75,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;
}
@@ -103,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;
}
@@ -111,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/include/linux/cryptodev.h b/include/linux/cryptodev.h
deleted file mode 100644
index 4a546b88d2d..00000000000
--- a/include/linux/cryptodev.h
+++ /dev/null
@@ -1,154 +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 L_CRYPTODEV_H
-#define L_CRYPTODEV_H
-
-#include <linux/types.h>
-#ifndef __KERNEL__
-#define __user
-#endif
-
-/* 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 /* L_CRYPTODEV_H */
diff --git a/include/linux/ncr.h b/include/linux/ncr.h
index a3778248630..ce84f7a7f85 100644
--- a/include/linux/ncr.h
+++ b/include/linux/ncr.h
@@ -6,46 +6,75 @@
#define __user
#endif
+/* 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,
@@ -57,7 +86,7 @@ typedef enum {
/* Key handling
*/
-typedef int ncr_key_t;
+typedef __s32 ncr_key_t;
#define NCR_KEY_INVALID ((ncr_key_t)-1)
@@ -70,46 +99,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 {
@@ -118,127 +126,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
*/
@@ -250,68 +240,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
diff --git a/utils.c b/utils.c
new file mode 100644
index 00000000000..a427833fdad
--- /dev/null
+++ b/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/utils.h b/utils.h
new file mode 100644
index 00000000000..2802afa7758
--- /dev/null
+++ b/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