Subject: [PATCH 2/4] /dev/crypto implementation
From: Miloslav Trmač <[email-masked]>
To: Herbert Xu <[email-masked]>
Cc: Neil Horman <[email-masked]>, Nikos Mavrogiannopoulos <[email-masked]>, [email-masked],Miloslav Trmač <[email-masked]>
Date: Thu, 5 Aug 2010 22:17:55 +0200

This the heart of the patch set, implementing the interface in patch 1/4.

---
 Kconfig                      |    5
 Makefile                     |    2
 userspace/Makefile           |   72 ++
 userspace/cryptodev_cipher.c |  340 +++++++++++++
 userspace/cryptodev_int.h    |   82 +++
 userspace/cryptodev_main.c   |  234 +++++++++
 userspace/ncr-dh.c           |  283 +++++++++++
 userspace/ncr-dh.h           |   25
 userspace/ncr-int.h          |  177 ++++++
 userspace/ncr-key-storage.c  |  130 +++++
 userspace/ncr-key-wrap.c     |  759 +++++++++++++++++++++++++++++
 userspace/ncr-key.c          |  680 ++++++++++++++++++++++++++
 userspace/ncr-limits.c       |  248 +++++++++
 userspace/ncr-pk.c           |  716 ++++++++++++++++++++++++++++
 userspace/ncr-pk.h           |   55 ++
 userspace/ncr-sessions.c     | 1098 +++++++++++++++++++++++++++++++++++++++++++
 userspace/ncr.c              |  210 ++++++++
 17 files changed, 5116 insertions(+)

diff --git a/crypto/Kconfig b/crypto/Kconfig
index 81c185a..022768a 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -31,6 +31,11 @@ config CRYPTO_FIPS
 	  this is. Note that CRYPTO_ANSI_CPRNG is requred if this
 	  option is selected
 
+config CRYPTO_USERSPACE
+	tristate "User-space crypto API"
+	help
+	  This option provides an user-space API for cryptographic operations.
+
 config CRYPTO_ALGAPI
 	tristate
 	select CRYPTO_ALGAPI2
diff --git a/crypto/Makefile b/crypto/Makefile
index 9e8f619..bf21088 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -90,3 +90,5 @@ obj-$(CONFIG_CRYPTO_GHASH) += ghash-generic.o
 #
 obj-$(CONFIG_XOR_BLOCKS) += xor.o
 obj-$(CONFIG_ASYNC_CORE) += async_tx/
+
+obj-$(CONFIG_CRYPTO_USERSPACE) += userspace/
\ No newline at end of file
diff --git a/crypto/userspace/Makefile b/crypto/userspace/Makefile
new file mode 100644
index 0000000..65edf8f
--- /dev/null
+++ b/crypto/userspace/Makefile
@@ -0,0 +1,72 @@
+ccflags-y += -I$(src)/libtommath -I$(src)/libtomcrypt/headers -I$(src) -DLTC_SOURCE
+
+TOMMATH_OBJECTS = libtommath/bncore.o libtommath/bn_mp_init.o libtommath/bn_mp_clear.o libtommath/bn_mp_exch.o libtommath/bn_mp_grow.o libtommath/bn_mp_shrink.o \
+	libtommath/bn_mp_clamp.o libtommath/bn_mp_zero.o  libtommath/bn_mp_set.o libtommath/bn_mp_set_int.o libtommath/bn_mp_init_size.o libtommath/bn_mp_copy.o \
+	libtommath/bn_mp_init_copy.o libtommath/bn_mp_abs.o libtommath/bn_mp_neg.o libtommath/bn_mp_cmp_mag.o libtommath/bn_mp_cmp.o libtommath/bn_mp_cmp_d.o \
+	libtommath/bn_mp_rshd.o libtommath/bn_mp_lshd.o libtommath/bn_mp_mod_2d.o libtommath/bn_mp_div_2d.o libtommath/bn_mp_mul_2d.o libtommath/bn_mp_div_2.o \
+	libtommath/bn_mp_mul_2.o libtommath/bn_s_mp_add.o libtommath/bn_s_mp_sub.o libtommath/bn_fast_s_mp_mul_digs.o libtommath/bn_s_mp_mul_digs.o \
+	libtommath/bn_fast_s_mp_mul_high_digs.o libtommath/bn_s_mp_mul_high_digs.o libtommath/bn_fast_s_mp_sqr.o libtommath/bn_s_mp_sqr.o \
+	libtommath/bn_mp_add.o libtommath/bn_mp_sub.o libtommath/bn_mp_karatsuba_mul.o libtommath/bn_mp_mul.o libtommath/bn_mp_karatsuba_sqr.o \
+	libtommath/bn_mp_sqr.o libtommath/bn_mp_div.o libtommath/bn_mp_mod.o libtommath/bn_mp_add_d.o libtommath/bn_mp_sub_d.o libtommath/bn_mp_mul_d.o \
+	libtommath/bn_mp_div_d.o libtommath/bn_mp_mod_d.o libtommath/bn_mp_expt_d.o libtommath/bn_mp_addmod.o libtommath/bn_mp_submod.o \
+	libtommath/bn_mp_mulmod.o libtommath/bn_mp_sqrmod.o libtommath/bn_mp_gcd.o libtommath/bn_mp_lcm.o libtommath/bn_fast_mp_invmod.o libtommath/bn_mp_invmod.o \
+	libtommath/bn_mp_reduce.o libtommath/bn_mp_montgomery_setup.o libtommath/bn_fast_mp_montgomery_reduce.o libtommath/bn_mp_montgomery_reduce.o \
+	libtommath/bn_mp_exptmod_fast.o libtommath/bn_mp_exptmod.o libtommath/bn_mp_2expt.o libtommath/bn_mp_n_root.o libtommath/bn_mp_jacobi.o libtommath/bn_reverse.o \
+	libtommath/bn_mp_count_bits.o libtommath/bn_mp_read_unsigned_bin.o libtommath/bn_mp_read_signed_bin.o libtommath/bn_mp_to_unsigned_bin.o \
+	libtommath/bn_mp_to_signed_bin.o libtommath/bn_mp_unsigned_bin_size.o libtommath/bn_mp_signed_bin_size.o  \
+	libtommath/bn_mp_xor.o libtommath/bn_mp_and.o libtommath/bn_mp_or.o libtommath/bn_mp_rand.o libtommath/bn_mp_montgomery_calc_normalization.o \
+	libtommath/bn_mp_prime_is_divisible.o libtommath/bn_prime_tab.o libtommath/bn_mp_prime_fermat.o libtommath/bn_mp_prime_miller_rabin.o \
+	libtommath/bn_mp_prime_is_prime.o libtommath/bn_mp_prime_next_prime.o libtommath/bn_mp_dr_reduce.o \
+	libtommath/bn_mp_dr_is_modulus.o libtommath/bn_mp_dr_setup.o libtommath/bn_mp_reduce_setup.o \
+	libtommath/bn_mp_toom_mul.o libtommath/bn_mp_toom_sqr.o libtommath/bn_mp_div_3.o libtommath/bn_s_mp_exptmod.o \
+	libtommath/bn_mp_reduce_2k.o libtommath/bn_mp_reduce_is_2k.o libtommath/bn_mp_reduce_2k_setup.o \
+	libtommath/bn_mp_reduce_2k_l.o libtommath/bn_mp_reduce_is_2k_l.o libtommath/bn_mp_reduce_2k_setup_l.o \
+	libtommath/bn_mp_radix_smap.o libtommath/bn_mp_read_radix.o libtommath/bn_mp_toradix.o libtommath/bn_mp_radix_size.o \
+	libtommath/bn_mp_cnt_lsb.o libtommath/bn_error.o \
+	libtommath/bn_mp_init_multi.o libtommath/bn_mp_clear_multi.o libtommath/bn_mp_exteuclid.o libtommath/bn_mp_toradix_n.o \
+	libtommath/bn_mp_prime_random_ex.o libtommath/bn_mp_get_int.o libtommath/bn_mp_sqrt.o libtommath/bn_mp_is_square.o libtommath/bn_mp_init_set.o \
+	libtommath/bn_mp_init_set_int.o libtommath/bn_mp_invmod_slow.o libtommath/bn_mp_prime_rabin_miller_trials.o \
+	libtommath/bn_mp_to_signed_bin_n.o libtommath/bn_mp_to_unsigned_bin_n.o
+
+TOMCRYPT_OBJECTS = libtomcrypt/misc/zeromem.o libtomcrypt/misc/crypt/crypt_argchk.o \
+	libtomcrypt/math/rand_prime.o  libtomcrypt/misc/qsort.o libtomcrypt/hashes/hash_get_oid.o \
+	libtomcrypt/hashes/crypt_hash_is_valid.o libtomcrypt/hashes/hash_memory.o libtomcrypt/hashes/hash_memory_multi.o \
+	libtomcrypt/pk/dsa/dsa_make_key.o libtomcrypt/pk/dsa/dsa_export.o libtomcrypt/pk/dsa/dsa_import.o \
+	libtomcrypt/pk/dsa/dsa_free.o libtomcrypt/pk/dsa/dsa_sign_hash.o libtomcrypt/pk/dsa/dsa_verify_hash.o \
+	libtomcrypt/pk/dsa/dsa_verify_key.o \
+	libtomcrypt/pk/asn1/der/bit/der_decode_bit_string.o libtomcrypt/pk/asn1/der/bit/der_encode_bit_string.o \
+	libtomcrypt/pk/asn1/der/bit/der_length_bit_string.o libtomcrypt/pk/asn1/der/boolean/der_decode_boolean.o \
+	libtomcrypt/pk/asn1/der/boolean/der_encode_boolean.o libtomcrypt/pk/asn1/der/boolean/der_length_boolean.o \
+	libtomcrypt/pk/asn1/der/choice/der_decode_choice.o libtomcrypt/pk/asn1/der/ia5/der_decode_ia5_string.o \
+	libtomcrypt/pk/asn1/der/ia5/der_encode_ia5_string.o libtomcrypt/pk/asn1/der/ia5/der_length_ia5_string.o \
+	libtomcrypt/pk/asn1/der/integer/der_decode_integer.o libtomcrypt/pk/asn1/der/integer/der_encode_integer.o \
+	libtomcrypt/pk/asn1/der/integer/der_length_integer.o libtomcrypt/pk/asn1/der/object_identifier/der_decode_object_identifier.o \
+	libtomcrypt/pk/asn1/der/object_identifier/der_encode_object_identifier.o libtomcrypt/pk/asn1/der/object_identifier/der_length_object_identifier.o \
+	libtomcrypt/pk/asn1/der/octet/der_decode_octet_string.o libtomcrypt/pk/asn1/der/octet/der_encode_octet_string.o \
+	libtomcrypt/pk/asn1/der/octet/der_length_octet_string.o libtomcrypt/pk/asn1/der/printable_string/der_decode_printable_string.o \
+	libtomcrypt/pk/asn1/der/printable_string/der_encode_printable_string.o libtomcrypt/pk/asn1/der/printable_string/der_length_printable_string.o \
+	libtomcrypt/pk/asn1/der/sequence/der_decode_sequence_ex.o libtomcrypt/pk/asn1/der/sequence/der_decode_sequence_flexi.o \
+	libtomcrypt/pk/asn1/der/sequence/der_decode_sequence_multi.o libtomcrypt/pk/asn1/der/sequence/der_encode_sequence_ex.o \
+	libtomcrypt/pk/asn1/der/sequence/der_encode_sequence_multi.o libtomcrypt/pk/asn1/der/sequence/der_length_sequence.o \
+	libtomcrypt/pk/asn1/der/sequence/der_sequence_free.o libtomcrypt/pk/asn1/der/short_integer/der_decode_short_integer.o \
+	libtomcrypt/pk/asn1/der/short_integer/der_encode_short_integer.o libtomcrypt/pk/asn1/der/short_integer/der_length_short_integer.o \
+	libtomcrypt/pk/asn1/der/utctime/der_decode_utctime.o libtomcrypt/pk/asn1/der/utctime/der_encode_utctime.o \
+	libtomcrypt/pk/asn1/der/utctime/der_length_utctime.o libtomcrypt/pk/asn1/der/utf8/der_decode_utf8_string.o \
+	libtomcrypt/pk/asn1/der/utf8/der_encode_utf8_string.o libtomcrypt/pk/asn1/der/utf8/der_length_utf8_string.o \
+	libtomcrypt/pk/asn1/der/set/der_encode_set.o  libtomcrypt/pk/asn1/der/set/der_encode_setof.o \
+	libtomcrypt/pk/rsa/rsa_decrypt_key.o libtomcrypt/pk/rsa/rsa_encrypt_key.o libtomcrypt/pk/rsa/rsa_export.o \
+	libtomcrypt/pk/rsa/rsa_exptmod.o libtomcrypt/pk/rsa/rsa_free.o libtomcrypt/pk/rsa/rsa_import.o \
+	libtomcrypt/pk/rsa/rsa_make_key.o libtomcrypt/pk/rsa/rsa_sign_hash.o libtomcrypt/pk/rsa/rsa_verify_hash.o \
+	libtomcrypt/pk/pkcs1/pkcs_1_i2osp.o libtomcrypt/pk/pkcs1/pkcs_1_mgf1.o 	libtomcrypt/pk/pkcs1/pkcs_1_oaep_decode.o \
+	libtomcrypt/pk/pkcs1/pkcs_1_oaep_encode.o libtomcrypt/pk/pkcs1/pkcs_1_os2ip.o libtomcrypt/pk/pkcs1/pkcs_1_pss_decode.o \
+	libtomcrypt/pk/pkcs1/pkcs_1_pss_encode.o libtomcrypt/pk/pkcs1/pkcs_1_v1_5_decode.o libtomcrypt/pk/pkcs1/pkcs_1_v1_5_encode.o \
+	libtomcrypt/misc/pk_get_oid.o libtomcrypt/pk/asn1/der/x509/der_encode_subject_public_key_info.o \
+	libtomcrypt/pk/asn1/der/x509/der_decode_subject_public_key_info.o
+
+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) \
+	$(TOMCRYPT_OBJECTS)
+
+
+obj-$(CONFIG_CRYPTO_USERSPACE) += cryptodev.o
diff --git a/crypto/userspace/cryptodev_cipher.c b/crypto/userspace/cryptodev_cipher.c
new file mode 100644
index 0000000..00fa763
--- /dev/null
+++ b/crypto/userspace/cryptodev_cipher.c
@@ -0,0 +1,340 @@
+/*
+ * Driver for /dev/crypto device (aka CryptoDev)
+ *
+ * Copyright (c) 2010 Nikos Mavrogiannopoulos <[email-masked]>
+ * Portions Copyright (c) 2010 Michael Weiser
+ * Portions Copyright (c) 2010 Phil Sutter
+ *
+ * This file is part of linux cryptodev.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/crypto.h>
+#include <linux/cryptodev.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/random.h>
+#include <asm/uaccess.h>
+#include <asm/ioctl.h>
+#include <linux/scatterlist.h>
+#include <crypto/algapi.h>
+#include <crypto/hash.h>
+#include "cryptodev_int.h"
+
+
+struct cryptodev_result {
+	struct completion completion;
+	int err;
+};
+
+static void cryptodev_complete(struct crypto_async_request *req, int err)
+{
+	struct cryptodev_result *res = req->data;
+
+	if (err == -EINPROGRESS)
+		return;
+
+	res->err = err;
+	complete(&res->completion);
+}
+
+int cryptodev_cipher_init(struct cipher_data* out, const char* alg_name, uint8_t * keyp, size_t keylen)
+{
+	
+	struct ablkcipher_alg* alg;
+	int ret;
+
+	memset(out, 0, sizeof(*out));
+
+	out->async.s = crypto_alloc_ablkcipher(alg_name, 0, 0);
+	if (unlikely(IS_ERR(out->async.s))) {
+		dprintk(1,KERN_DEBUG,"%s: Failed to load cipher %s\n", __func__,
+			   alg_name);
+		return -EINVAL;
+	}
+
+	alg = crypto_ablkcipher_alg(out->async.s);
+
+	if (alg != NULL) {
+		/* Was correct key length supplied? */
+		if (alg->max_keysize > 0 && unlikely((keylen < alg->min_keysize) ||
+			(keylen > alg->max_keysize))) {
+			dprintk(1,KERN_DEBUG,"Wrong keylen '%zu' for algorithm '%s'. Use %u to %u.\n",
+				   keylen, alg_name, alg->min_keysize, 
+				   alg->max_keysize);
+			ret = -EINVAL;
+			goto error;
+		}
+	}
+
+	ret = crypto_ablkcipher_setkey(out->async.s, keyp, keylen);
+	if (unlikely(ret)) {
+		dprintk(1,KERN_DEBUG,"Setting key failed for %s-%zu.\n",
+			alg_name, keylen*8);
+		ret = -EINVAL;
+		goto error;
+	}
+
+	out->blocksize = crypto_ablkcipher_blocksize(out->async.s);
+	out->ivsize = crypto_ablkcipher_ivsize(out->async.s);
+
+	out->async.result = kmalloc(sizeof(*out->async.result), GFP_KERNEL);
+	if (unlikely(!out->async.result)) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	memset(out->async.result, 0, sizeof(*out->async.result));
+	init_completion(&out->async.result->completion);
+
+	out->async.request = ablkcipher_request_alloc(out->async.s, GFP_KERNEL);
+	if (unlikely(!out->async.request)) {
+		dprintk(1,KERN_ERR,"error allocating async crypto request\n");
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	ablkcipher_request_set_callback(out->async.request, CRYPTO_TFM_REQ_MAY_BACKLOG,
+					cryptodev_complete, out->async.result);
+
+	out->init = 1;
+	return 0;
+error:
+	if (out->async.request)
+		ablkcipher_request_free(out->async.request);
+	kfree(out->async.result);
+	if (out->async.s)
+		crypto_free_ablkcipher(out->async.s);
+
+	return ret;
+}
+
+void cryptodev_cipher_deinit(struct cipher_data* cdata)
+{
+	if (cdata->init) {
+		if (cdata->async.request)
+			ablkcipher_request_free(cdata->async.request);
+		kfree(cdata->async.result);
+		if (cdata->async.s)
+			crypto_free_ablkcipher(cdata->async.s);
+
+		cdata->init = 0;
+	}
+}
+
+void cryptodev_cipher_set_iv(struct cipher_data* cdata, void *iv, size_t iv_size)
+{
+	memcpy(cdata->async.iv, iv, min(iv_size,sizeof(cdata->async.iv)));
+}
+
+static inline int waitfor (struct cryptodev_result* cr, ssize_t ret)
+{
+	switch (ret) {
+	case 0:
+		break;
+	case -EINPROGRESS:
+	case -EBUSY:
+		wait_for_completion(&cr->completion);
+		/* At this point we known for sure the request has finished,
+		 * because wait_for_completion above was not interruptible.
+		 * This is important because otherwise hardware or driver
+		 * might try to access memory which will be freed or reused for
+		 * another request. */
+
+		if (unlikely(cr->err)) {
+			dprintk(0,KERN_ERR,"error from async request: %d \n", cr->err);
+                        return cr->err; 
+		}
+
+		break;
+	default:
+		return ret;
+	}
+
+	return 0;
+}
+
+
+int _cryptodev_cipher_encrypt(struct cipher_data* cdata, const void* plaintext,
+	size_t plaintext_size, void* ciphertext, size_t ciphertext_size)
+{
+struct scatterlist sg, sg2;
+
+	sg_init_one(&sg, plaintext, plaintext_size);
+	sg_init_one(&sg2, ciphertext, ciphertext_size);
+
+	return cryptodev_cipher_encrypt( cdata, &sg, &sg2, plaintext_size);
+}
+
+int _cryptodev_cipher_decrypt(struct cipher_data* cdata, const void* ciphertext,
+	size_t ciphertext_size, void* plaintext, size_t plaintext_size)
+{
+struct scatterlist sg, sg2;
+
+	sg_init_one(&sg, ciphertext, ciphertext_size);
+	sg_init_one(&sg2, plaintext, plaintext_size);
+
+	return cryptodev_cipher_decrypt( cdata, &sg, &sg2, ciphertext_size);
+}
+
+
+ssize_t cryptodev_cipher_encrypt( struct cipher_data* cdata, const struct scatterlist *sg1, struct scatterlist *sg2, size_t len)
+{
+	int ret;
+
+	INIT_COMPLETION(cdata->async.result->completion);
+	ablkcipher_request_set_crypt(cdata->async.request, (struct scatterlist*)sg1, sg2,
+			len, cdata->async.iv);
+	ret = crypto_ablkcipher_encrypt(cdata->async.request);
+
+	return waitfor(cdata->async.result,ret);
+}
+
+ssize_t cryptodev_cipher_decrypt( struct cipher_data* cdata, const struct scatterlist *sg1, struct scatterlist *sg2, size_t len)
+{
+	int ret;
+
+	INIT_COMPLETION(cdata->async.result->completion);
+	ablkcipher_request_set_crypt(cdata->async.request, (struct scatterlist*)sg1, sg2,
+			len, cdata->async.iv);
+	ret = crypto_ablkcipher_decrypt(cdata->async.request);
+
+	return waitfor(cdata->async.result, ret);
+}
+
+/* Hash functions */
+
+int cryptodev_hash_init( struct hash_data* hdata, const char* alg_name, int hmac_mode, void * mackey, size_t mackeylen)
+{
+int ret;
+
+	hdata->async.s = crypto_alloc_ahash(alg_name, 0, 0);
+	if (unlikely(IS_ERR(hdata->async.s))) {
+		dprintk(1,KERN_DEBUG,"%s: Failed to load transform for %s\n", __func__,
+				   alg_name);
+		return -EINVAL;
+	}
+
+	/* Copy the key from user and set to TFM. */
+	if (hmac_mode != 0) {
+
+		ret = crypto_ahash_setkey(hdata->async.s, mackey, mackeylen);
+		if (unlikely(ret)) {
+			dprintk(1,KERN_DEBUG,"Setting hmac key failed for %s-%zu.\n",
+				alg_name, mackeylen*8);
+			ret = -EINVAL;
+			goto error;
+		}
+	}
+
+	hdata->digestsize = crypto_ahash_digestsize(hdata->async.s);
+
+	hdata->async.result = kmalloc(sizeof(*hdata->async.result), GFP_KERNEL);
+	if (unlikely(!hdata->async.result)) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	memset(hdata->async.result, 0, sizeof(*hdata->async.result));
+	init_completion(&hdata->async.result->completion);
+
+	hdata->async.request = ahash_request_alloc(hdata->async.s, GFP_KERNEL);
+	if (unlikely(!hdata->async.request)) {
+		dprintk(0,KERN_ERR,"error allocating async crypto request\n");
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	ahash_request_set_callback(hdata->async.request, CRYPTO_TFM_REQ_MAY_BACKLOG,
+					cryptodev_complete, hdata->async.result);
+
+	ret = crypto_ahash_init(hdata->async.request);
+	if (unlikely(ret)) {
+		dprintk(0,KERN_ERR,
+			"error in crypto_hash_init()\n");
+		goto error_request;
+	}
+
+	hdata->init = 1;
+	return 0;
+
+error_request:
+	ahash_request_free(hdata->async.request);
+error:
+	kfree(hdata->async.result);
+	crypto_free_ahash(hdata->async.s);
+	return ret;
+}
+
+void cryptodev_hash_deinit(struct hash_data* hdata)
+{
+	if (hdata->init) {
+		if (hdata->async.request)
+			ahash_request_free(hdata->async.request);
+		kfree(hdata->async.result);
+		if (hdata->async.s)
+			crypto_free_ahash(hdata->async.s);
+		hdata->init = 0;
+	}
+}
+
+int cryptodev_hash_reset( struct hash_data* hdata)
+{
+int ret;
+	ret = crypto_ahash_init(hdata->async.request);
+	if (unlikely(ret)) {
+		dprintk(0,KERN_ERR,
+			"error in crypto_hash_init()\n");
+		return ret;
+	}
+
+	return 0;
+
+}
+
+ssize_t cryptodev_hash_update( struct hash_data* hdata, struct scatterlist *sg, size_t len)
+{
+	int ret;
+
+	INIT_COMPLETION(hdata->async.result->completion);
+	ahash_request_set_crypt(hdata->async.request, sg, NULL,
+			len);
+
+	ret = crypto_ahash_update(hdata->async.request);
+
+	return waitfor(hdata->async.result,ret);
+}
+
+ssize_t _cryptodev_hash_update( struct hash_data* hdata, const void* data, size_t len)
+{
+struct scatterlist sg;
+
+	sg_init_one(&sg, data, len);
+
+	return cryptodev_hash_update( hdata, &sg, len);
+}
+
+int cryptodev_hash_final( struct hash_data* hdata, void* output)
+{
+	int ret;
+
+	INIT_COMPLETION(hdata->async.result->completion);
+	ahash_request_set_crypt(hdata->async.request, NULL, output, 0);
+
+	ret = crypto_ahash_final(hdata->async.request);
+
+	return waitfor(hdata->async.result,ret);
+}
diff --git a/crypto/userspace/cryptodev_int.h b/crypto/userspace/cryptodev_int.h
new file mode 100644
index 0000000..0a2c4e5
--- /dev/null
+++ b/crypto/userspace/cryptodev_int.h
@@ -0,0 +1,82 @@
+/* cipher stuff */
+#ifndef CRYPTODEV_INT_H
+# define CRYPTODEV_INT_H
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/fdtable.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/scatterlist.h>
+#include <linux/cryptodev.h>
+
+#define PFX "cryptodev: "
+#define dprintk(level,severity,format,a...)			\
+	do {						\
+		if (level <= cryptodev_verbosity)				\
+			printk(severity PFX "%s[%u]: " format,	\
+			       current->comm, current->pid,	\
+			       ##a);				\
+	} while (0)
+
+extern int cryptodev_verbosity;
+
+/* For zero copy */
+int __get_userbuf(uint8_t __user *addr, uint32_t len, int write,
+		int pgcount, struct page **pg, struct scatterlist *sg);
+void release_user_pages(struct page **pg, int pagecount);
+
+/* last page - first page + 1 */
+#define PAGECOUNT(buf, buflen) \
+        ((((unsigned long)(buf + buflen - 1) & PAGE_MASK) >> PAGE_SHIFT) - \
+         (((unsigned long) buf               & PAGE_MASK) >> PAGE_SHIFT) + 1)
+
+#define DEFAULT_PREALLOC_PAGES 32
+
+struct cipher_data
+{
+	int init; /* 0 uninitialized */
+	int blocksize;
+	int ivsize;
+	struct {
+		struct crypto_ablkcipher* s;
+		struct cryptodev_result *result;
+		struct ablkcipher_request *request;
+		uint8_t iv[EALG_MAX_BLOCK_LEN];
+	} async;
+};
+
+int cryptodev_cipher_init(struct cipher_data* out, const char* alg_name, uint8_t * key, size_t keylen);
+void cryptodev_cipher_deinit(struct cipher_data* cdata);
+ssize_t cryptodev_cipher_decrypt( struct cipher_data* cdata, const struct scatterlist *sg1, struct scatterlist *sg2, size_t len);
+ssize_t cryptodev_cipher_encrypt( struct cipher_data* cdata, const struct scatterlist *sg1, struct scatterlist *sg2, size_t len);
+
+void cryptodev_cipher_set_iv(struct cipher_data* cdata, void* iv, size_t iv_size);
+int _cryptodev_cipher_decrypt(struct cipher_data* cdata, const void* ciphertext,
+	size_t ciphertext_size, void* plaintext, size_t plaintext_size);
+int _cryptodev_cipher_encrypt(struct cipher_data* cdata, const void* plaintext,
+	size_t plaintext_size, void* ciphertext, size_t ciphertext_size);
+
+/* hash stuff */
+struct hash_data
+{
+	int init; /* 0 uninitialized */
+	int digestsize;
+	struct {
+		struct crypto_ahash *s;
+		struct cryptodev_result *result;
+		struct ahash_request *request;
+	} async;
+};
+
+int cryptodev_hash_final( struct hash_data* hdata, void* output);
+ssize_t cryptodev_hash_update( struct hash_data* hdata, struct scatterlist *sg, size_t len);
+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);
+
+#endif /* CRYPTODEV_INT_H */
diff --git a/crypto/userspace/cryptodev_main.c b/crypto/userspace/cryptodev_main.c
new file mode 100644
index 0000000..fb1b2b9
--- /dev/null
+++ b/crypto/userspace/cryptodev_main.c
@@ -0,0 +1,234 @@
+/*
+ * Driver for /dev/crypto device (aka CryptoDev)
+ *
+ * Copyright (c) 2004 Michal Ludvig <[email-masked]>, SuSE Labs
+ * Copyright (c) 2009,2010 Nikos Mavrogiannopoulos <[email-masked]>
+ *
+ * This file is part of linux cryptodev.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/*
+ * Device /dev/crypto provides an interface for 
+ * accessing kernel CryptoAPI algorithms (ciphers,
+ * hashes) from userspace programs.
+ *
+ * /dev/crypto interface was originally introduced in
+ * OpenBSD and this module attempts to keep the API.
+ *
+ */
+
+#include <linux/audit.h>
+#include <linux/crypto.h>
+#include <linux/cryptodev.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/random.h>
+#include <linux/syscalls.h>
+#include <linux/pagemap.h>
+#include <asm/uaccess.h>
+#include <asm/ioctl.h>
+#include <linux/scatterlist.h>
+#include "cryptodev_int.h"
+#include "ncr-int.h"
+#include <linux/version.h>
+
+MODULE_AUTHOR("Nikos Mavrogiannopoulos <[email-masked]>");
+MODULE_DESCRIPTION("CryptoDev driver");
+MODULE_LICENSE("GPL");
+
+/* ====== Module parameters ====== */
+
+int cryptodev_verbosity = 0;
+module_param(cryptodev_verbosity, int, 0644);
+MODULE_PARM_DESC(cryptodev_verbosity, "0: normal, 1: verbose, 2: debug");
+
+/* ====== CryptoAPI ====== */
+
+void release_user_pages(struct page **pg, int pagecount)
+{
+	while (pagecount--) {
+		if (!PageReserved(pg[pagecount]))
+			SetPageDirty(pg[pagecount]);
+		page_cache_release(pg[pagecount]);
+	}
+}
+
+/* offset of buf in it's first page */
+#define PAGEOFFSET(buf) ((unsigned long)buf & ~PAGE_MASK)
+
+/* fetch the pages addr resides in into pg and initialise sg with them */
+int __get_userbuf(uint8_t __user *addr, uint32_t len, int write,
+		int pgcount, struct page **pg, struct scatterlist *sg)
+{
+	int ret, pglen, i = 0;
+	struct scatterlist *sgp;
+
+	down_write(&current->mm->mmap_sem);
+	ret = get_user_pages(current, current->mm,
+			(unsigned long)addr, pgcount, write, 0, pg, NULL);
+	up_write(&current->mm->mmap_sem);
+	if (ret != pgcount)
+		return -EINVAL;
+
+	sg_init_table(sg, pgcount);
+
+	pglen = min((ptrdiff_t)(PAGE_SIZE - PAGEOFFSET(addr)), (ptrdiff_t)len);
+	sg_set_page(sg, pg[i++], pglen, PAGEOFFSET(addr));
+
+	len -= pglen;
+	for (sgp = sg_next(sg); len; sgp = sg_next(sgp)) {
+		pglen = min((uint32_t)PAGE_SIZE, len);
+		sg_set_page(sgp, pg[i++], pglen, 0);
+		len -= pglen;
+	}
+	sg_mark_end(sg_last(sg, pgcount));
+	return 0;
+}
+
+/* ====== /dev/crypto ====== */
+
+static int
+cryptodev_open(struct inode *inode, struct file *filp)
+{
+	struct ncr_lists *ncr;
+	int ret;
+
+	ncr = ncr_init_lists();
+	if (ncr == NULL) {
+		return -ENOMEM;
+	}
+
+	ret = audit_log_crypto_op(AUDIT_CRYPTO_OP_CONTEXT_NEW, ncr->id, -1,
+				  NULL, NULL, -1, NULL, 0, -1, NULL, 0);
+	if (ret < 0) {
+		ncr_deinit_lists(ncr);
+		return ret;
+	}
+
+	filp->private_data = ncr;
+	return 0;
+}
+
+static int
+cryptodev_release(struct inode *inode, struct file *filp)
+{
+	struct ncr_lists *ncr = filp->private_data;
+
+	if (ncr) {
+		audit_log_crypto_op(AUDIT_CRYPTO_OP_CONTEXT_DEL, ncr->id, -1,
+				    NULL, NULL, -1, NULL, 0, -1, NULL, 0);
+		ncr_deinit_lists(ncr);
+		filp->private_data = NULL;
+	}
+
+	return 0;
+}
+
+static int
+cryptodev_ioctl(struct inode *inode, struct file *filp,
+		unsigned int cmd, unsigned long arg)
+{
+	void *ncr = filp->private_data;
+
+	if (unlikely(!ncr))
+		BUG();
+
+	return ncr_ioctl(ncr, filp, cmd, arg);
+}
+
+/* compatibility code for 32bit userlands */
+#ifdef CONFIG_COMPAT
+
+static long
+cryptodev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	void *ncr = file->private_data;
+
+	if (unlikely(!ncr))
+		BUG();
+
+	switch (cmd) {
+	default:
+		return -EINVAL;
+	}
+}
+
+#endif /* CONFIG_COMPAT */
+
+static struct file_operations cryptodev_fops = {
+	.owner = THIS_MODULE,
+	.open = cryptodev_open,
+	.release = cryptodev_release,
+	.ioctl = cryptodev_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = cryptodev_compat_ioctl,
+#endif /* CONFIG_COMPAT */
+};
+
+static struct miscdevice cryptodev = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "crypto",
+	.fops = &cryptodev_fops,
+};
+
+static int __init
+cryptodev_register(void)
+{
+	int rc;
+
+	ncr_limits_init();
+	ncr_master_key_reset();
+	
+	rc = misc_register (&cryptodev);
+	if (unlikely(rc)) {
+		ncr_limits_deinit();
+		printk(KERN_ERR PFX "registration of /dev/crypto failed\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+static void __exit
+cryptodev_deregister(void)
+{
+	misc_deregister(&cryptodev);
+	ncr_limits_deinit();
+}
+
+/* ====== Module init/exit ====== */
+static int __init init_cryptodev(void)
+{
+	int rc;
+
+	rc = cryptodev_register();
+	if (unlikely(rc))
+		return rc;
+
+	printk(KERN_INFO PFX "driver loaded.\n");
+
+	return 0;
+}
+
+static void __exit exit_cryptodev(void)
+{
+	cryptodev_deregister();
+	printk(KERN_INFO PFX "driver unloaded.\n");
+}
+
+module_init(init_cryptodev);
+module_exit(exit_cryptodev);
diff --git a/crypto/userspace/ncr-int.h b/crypto/userspace/ncr-int.h
new file mode 100644
index 0000000..bc2482f
--- /dev/null
+++ b/crypto/userspace/ncr-int.h
@@ -0,0 +1,177 @@
+#ifndef NCR_INT_H
+# define NCR_INT_H
+
+#include <linux/idr.h>
+#include <linux/mutex.h>
+#include <linux/ncr.h>
+#include <asm/atomic.h>
+#include "cryptodev_int.h"
+#include <ncr-pk.h>
+#include <ncr-dh.h>
+
+#define KEY_DATA_MAX_SIZE 3*1024
+#define NCR_CIPHER_MAX_KEY_LEN 1024
+
+#define err() printk(KERN_DEBUG"ncr: %s: %s: %d\n", __FILE__, __func__, __LINE__)
+
+struct algo_properties_st {
+	ncr_algorithm_t algo;
+	const char *kstr;
+	unsigned needs_iv:1;
+	unsigned is_hmac:1;
+	unsigned can_sign:1;
+	unsigned can_digest:1;
+	unsigned can_encrypt:1;
+	unsigned can_kx:1; /* key exchange */
+	unsigned is_symmetric:1;
+	unsigned is_pk: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.
+	 */
+	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 semaphore 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.
+	 */
+	ncr_key_type_t type;
+	unsigned int flags;
+	const struct algo_properties_st *algorithm; /* non-NULL for public/private keys */
+	uint8_t key_id[MAX_KEY_ID_SIZE];
+	size_t key_id_size;
+
+	union {
+		struct {
+			uint8_t data[NCR_CIPHER_MAX_KEY_LEN];
+			size_t size;
+		} secret;
+		union {
+			rsa_key rsa;
+			dsa_key dsa;
+			dh_key dh;
+		} pk;
+	} key;
+
+	atomic_t refcnt;
+	atomic_t writer;
+
+	/* owner. The one charged with this */
+	uid_t uid;
+	pid_t pid;
+
+	int context_id;		/* Only for auditing */
+	ncr_key_t desc;
+};
+
+/* all the data associated with the open descriptor
+ * are here.
+ */
+struct ncr_lists {
+	int id;			/* Used only for auditing */
+
+	struct mutex key_idr_mutex;
+	struct idr key_idr;
+
+	/* sessions */
+	struct mutex session_idr_mutex;
+	struct idr session_idr;
+};
+
+struct ncr_lists *ncr_init_lists(void);
+void ncr_deinit_lists(struct ncr_lists *lst);
+
+int ncr_ioctl(struct ncr_lists*, struct file *filp,
+		unsigned int cmd, unsigned long arg);
+
+/* key derivation */
+int ncr_key_derive(struct ncr_lists *lst, void __user* arg);
+
+/* 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);
+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_get_public(struct ncr_lists *lst, void __user* arg);
+
+int ncr_key_item_get_read(struct key_item_st**st, struct ncr_lists *lst,
+	ncr_key_t desc);
+/* get key item for writing */
+int ncr_key_item_get_write( struct key_item_st** st,
+	struct ncr_lists *lst, ncr_key_t desc);
+void _ncr_key_item_put( struct key_item_st* item);
+
+typedef enum {
+	LIMIT_TYPE_KEY,
+	NUM_LIMIT_TYPES
+} limits_type_t;
+
+void ncr_limits_remove(uid_t uid, pid_t pid, limits_type_t type);
+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);
+
+/* 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);
+
+/* master key */
+extern struct key_item_st master_key;
+
+void ncr_master_key_reset(void);
+
+/* storage */
+int key_from_storage_data(struct key_item_st* key, const void* data, size_t data_size);
+int key_to_storage_data( uint8_t** data, size_t * data_size, const struct key_item_st *key);
+
+
+/* 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 char *ncr_algorithm_name(const struct algo_properties_st *algo);
+
+#endif
diff --git a/crypto/userspace/ncr-key-storage.c b/crypto/userspace/ncr-key-storage.c
new file mode 100644
index 0000000..c97b80c
--- /dev/null
+++ b/crypto/userspace/ncr-key-storage.c
@@ -0,0 +1,130 @@
+/*
+ * New driver for /dev/crypto device (aka CryptoDev)
+
+ * Copyright (c) 2010 Katholieke Universiteit Leuven
+ *
+ * Author: Nikos Mavrogiannopoulos <[email-masked]>
+ *
+ * This file is part of linux cryptodev.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/cryptodev.h>
+#include <linux/mm.h>
+#include <linux/ncr.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <asm/ioctl.h>
+#include <linux/scatterlist.h>
+#include "ncr-int.h"
+#include "cryptodev_int.h"
+
+struct packed_key {
+	uint8_t type;
+	uint32_t flags;
+	uint16_t algorithm; /* valid for public/private keys */
+	uint8_t key_id[MAX_KEY_ID_SIZE];
+	uint8_t key_id_size;
+
+	uint8_t raw[KEY_DATA_MAX_SIZE];
+	uint32_t raw_size;
+} __attribute__((__packed__));
+
+int key_to_storage_data( uint8_t** sdata, size_t * sdata_size, const struct key_item_st *key)
+{
+	struct packed_key * pkey;
+	int ret;
+
+	pkey = kmalloc(sizeof(*pkey), GFP_KERNEL);
+	if (pkey == NULL) {
+		err();
+		return -ENOMEM;
+	}
+
+	pkey->type = key->type;
+	pkey->flags = key->flags;
+	pkey->algorithm = key->algorithm->algo;
+	pkey->key_id_size = key->key_id_size;
+	memcpy(pkey->key_id, key->key_id, key->key_id_size);
+
+	if (key->type == NCR_KEY_TYPE_SECRET) {
+		pkey->raw_size = key->key.secret.size;
+		memcpy(pkey->raw, key->key.secret.data, pkey->raw_size);
+	} else if (key->type == NCR_KEY_TYPE_PRIVATE || key->type == NCR_KEY_TYPE_PUBLIC) {
+		pkey->raw_size = sizeof(pkey->raw);
+		ret = ncr_pk_pack( key, pkey->raw, &pkey->raw_size);
+		if (ret < 0) {
+			err();
+			goto fail;
+		}
+	} else {
+		err();
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	*sdata = (void*)pkey;
+	*sdata_size = sizeof(*pkey);
+
+	return 0;
+fail:
+	kfree(pkey);
+
+	return ret;
+}
+
+int key_from_storage_data(struct key_item_st* key, const void* data, size_t data_size)
+{
+	const struct packed_key * pkey = data;
+	int ret;
+
+	if (data_size != sizeof(*pkey) || pkey->key_id_size > MAX_KEY_ID_SIZE) {
+		err();
+		return -EINVAL;
+	}
+
+	key->type = pkey->type;
+	key->flags = pkey->flags;
+
+	key->algorithm = _ncr_algo_to_properties(pkey->algorithm);
+	if (key->algorithm == NULL) {
+		err();
+		return -EINVAL;
+	}
+	key->key_id_size = pkey->key_id_size;
+	memcpy(key->key_id, pkey->key_id, pkey->key_id_size);
+
+	if (key->type == NCR_KEY_TYPE_SECRET) {
+		if (pkey->raw_size > NCR_CIPHER_MAX_KEY_LEN) {
+			err();
+			return -EINVAL;
+		}
+		key->key.secret.size = pkey->raw_size;
+		memcpy(key->key.secret.data, pkey->raw, pkey->raw_size);
+	} else if (key->type == NCR_KEY_TYPE_PUBLIC 
+		|| key->type == NCR_KEY_TYPE_PRIVATE) {
+		ret = ncr_pk_unpack( key, pkey->raw, pkey->raw_size);
+		if (ret < 0) {
+			err();
+			return ret;
+		}
+	} else {
+		err();
+		return -EINVAL;
+	}
+
+	return 0;
+}
diff --git a/crypto/userspace/ncr-key-wrap.c b/crypto/userspace/ncr-key-wrap.c
new file mode 100644
index 0000000..4dc0fa3
--- /dev/null
+++ b/crypto/userspace/ncr-key-wrap.c
@@ -0,0 +1,759 @@
+/*
+ * New driver for /dev/crypto device (aka CryptoDev)
+
+ * Copyright (c) 2010 Katholieke Universiteit Leuven
+ *
+ * Author: Nikos Mavrogiannopoulos <[email-masked]>
+ *
+ * This file is part of linux cryptodev.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/audit.h>
+#include <linux/cryptodev.h>
+#include <linux/mm.h>
+#include <linux/ncr.h>
+#include <linux/slab.h>
+#include <linux/highmem.h>
+#include <linux/random.h>
+#include <asm/uaccess.h>
+#include <asm/ioctl.h>
+#include <linux/scatterlist.h>
+#include "ncr-int.h"
+#include "cryptodev_int.h"
+
+typedef uint8_t val64_t[8];
+
+static const val64_t initA = "\xA6\xA6\xA6\xA6\xA6\xA6\xA6\xA6";
+
+
+static void val64_xor( val64_t val, uint32_t x)
+{
+	val[7] ^= x & 0xff;
+	val[6] ^= (x >> 8) & 0xff;
+	val[5] ^= (x >> 16) & 0xff;
+	val[4] ^= (x >> 24) & 0xff;
+}
+
+static int rfc3394_wrap(val64_t *R, unsigned int n, struct cipher_data* ctx,
+	uint8_t* output, size_t *output_size, const uint8_t iv[8])
+{
+val64_t A;
+uint8_t aes_block[16];
+int i,j;
+
+	if (*output_size < (n+1)*8) {
+		err();
+		return -EINVAL;
+	}
+
+	memcpy(A, iv, 8);
+
+	for (i=0;i<6*n;i++) {
+		memcpy(aes_block, A, 8);
+		memcpy(&aes_block[8], R[0], 8);
+
+		_cryptodev_cipher_encrypt(ctx, aes_block, sizeof(aes_block),
+			aes_block, sizeof(aes_block));
+
+		memcpy(A, aes_block, 8); /* A = MSB64(AES(A^{t-1}|R_{1}^{t-1})) */
+		val64_xor(A, i+1); /* A ^= t */
+
+		for (j=0;j<n-1;j++)
+			memcpy(R[j], R[j+1], sizeof(R[j]));
+		memcpy(R[n-1], &aes_block[8], 8); /* R[n-1] = LSB64(AES(A^{t-1}|R_{1}^{t-1})) */
+	}
+
+	memcpy(output, A, sizeof(A));
+	for (j=0;j<n;j++)
+		memcpy(&output[(j+1)*8], R[j], 8);
+	*output_size = (n+1)*8;
+
+	return 0;
+}
+
+static int rfc3394_unwrap(const uint8_t *wrapped_key, val64_t R[], unsigned int n, val64_t A, struct cipher_data *ctx)
+{
+	int i, j;
+	uint8_t aes_block[16];
+
+	memcpy(A, wrapped_key, 8); /* A = C[0] */
+	for (i=0;i<n;i++)
+		memcpy(R[i], &wrapped_key[(i+1)*8], 8);
+
+	for (i=(6*n)-1;i>=0;i--) {
+		val64_xor(A, i+1);
+
+		memcpy(aes_block, A, 8);
+		memcpy(&aes_block[8], R[n-1], 8);
+
+		_cryptodev_cipher_decrypt(ctx, aes_block, sizeof(aes_block),
+			aes_block, sizeof(aes_block));
+
+		memcpy(A, aes_block, 8);
+
+		for (j=n-1;j>=1;j--)
+			memcpy(R[j], R[j-1], sizeof(R[j]));
+
+		memcpy(R[0], &aes_block[8], 8);
+	}
+
+	return 0;
+}
+
+#define RFC5649_IV "\xA6\x59\x59\xA6"
+static int _wrap_aes_rfc5649(void* kdata, size_t kdata_size, struct key_item_st* kek,
+	void* output, size_t* output_size, const void* _iv, size_t iv_size)
+{
+size_t n;
+int i, ret;
+struct cipher_data ctx;
+uint8_t iv[8];
+val64_t *R = NULL;
+
+	if (iv_size != 4) {
+		memcpy(iv, RFC5649_IV, 4);
+	} else {
+		memcpy(iv, _iv, 4);
+	}
+	iv_size = 8;
+	iv[4] = (kdata_size >> 24) & 0xff;
+	iv[5] = (kdata_size >> 16) & 0xff;
+	iv[6] = (kdata_size >> 8) & 0xff;
+	iv[7] = (kdata_size) & 0xff;
+
+	n = (kdata_size+7)/8;
+	if (n==1) { /* unimplemented */
+		err();
+		return -EINVAL;
+	}
+
+	ret = cryptodev_cipher_init(&ctx, "ecb(aes)", kek->key.secret.data, kek->key.secret.size);
+	if (ret < 0) {
+		err();
+		return ret;
+	}
+
+	R = kmalloc(n * sizeof (*R), GFP_KERNEL);
+	if (R == NULL) {
+		err();
+		ret = -ENOMEM;
+		goto cleanup;
+	}
+
+	/* R = P */
+	for (i=0;i<kdata_size;i++) {
+		R[i/8][i%8] = ((uint8_t*)kdata)[i];
+	}
+
+	for (;i<n*8;i++) {
+		R[i/8][i%8] = 0;
+	}
+
+	ret = rfc3394_wrap( R, n, &ctx, output, output_size, iv);
+	if (ret < 0) {
+		err();
+		goto cleanup;
+	}
+
+	ret = 0;
+
+cleanup:
+	kfree(R);
+	cryptodev_cipher_deinit(&ctx);
+
+	return ret;
+}
+
+static int _unwrap_aes_rfc5649(void* kdata, size_t *kdata_size, struct key_item_st* kek,
+	const void *wrapped_key, size_t wrapped_key_size, const void* _iv, size_t iv_size)
+{
+size_t n;
+int i, ret;
+struct cipher_data ctx;
+uint8_t iv[4];
+size_t size;
+val64_t *R = NULL, A;
+
+	if (iv_size != 4) {
+		memcpy(iv, RFC5649_IV, 4);
+	} else {
+		memcpy(iv, _iv, 4);
+	}
+	iv_size = 4;
+
+	ret = cryptodev_cipher_init(&ctx, "ecb(aes)", kek->key.secret.data, kek->key.secret.size);
+	if (ret < 0) {
+		err();
+		return ret;
+	}
+
+	if (wrapped_key_size % 8 != 0) {
+		err();
+		ret = -EINVAL;
+		goto cleanup;
+	}
+
+	n = wrapped_key_size/8 - 1;
+
+	if (*kdata_size < (n-1)*8) {
+		err();
+		ret = -EINVAL;
+		goto cleanup;
+	}
+
+	R = kmalloc(n * sizeof (*R), GFP_KERNEL);
+	if (R == NULL) {
+		err();
+		ret = -ENOMEM;
+		goto cleanup;
+	}
+
+	ret = rfc3394_unwrap(wrapped_key, R, n, A, &ctx);
+	if (ret < 0) {
+		err();
+		goto cleanup;
+	}
+
+	if (memcmp(A, iv, 4)!= 0) {
+		err();
+		ret = -EINVAL;
+		goto cleanup;
+	}
+
+	size = (A[4] << 24) | (A[5] << 16) | (A[6] << 8) | A[7];
+	if (size > n*8 || size < (n-1)*8 || *kdata_size < size) {
+		err();
+		ret = -EINVAL;
+		goto cleanup;
+	}
+
+	memset(kdata, 0, size);
+	*kdata_size = size;
+	for (i=0;i<size;i++) {
+		((uint8_t*)kdata)[i] = R[i/8][i%8];
+	}
+
+	ret = 0;
+
+cleanup:
+	kfree(R);
+	cryptodev_cipher_deinit(&ctx);
+
+	return ret;
+}
+
+
+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) {
+		err();
+		return -EINVAL;
+	}
+
+	return _wrap_aes_rfc5649(tobewrapped->key.secret.data, tobewrapped->key.secret.size,
+		kek, output, output_size, iv, iv_size);
+	
+}
+
+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)
+{
+	output->type = NCR_KEY_TYPE_SECRET;
+
+	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,
+	void* output, size_t *output_size, const void* iv, size_t iv_size)
+{
+size_t key_size, n;
+uint8_t *raw_key;
+int i, ret;
+struct cipher_data ctx;
+val64_t *R = NULL;
+
+	if (tobewrapped->type != NCR_KEY_TYPE_SECRET) {
+		err();
+		return -EINVAL;
+	}
+	
+	if (iv_size < sizeof(initA)) {
+		iv_size = sizeof(initA);
+		iv = initA;
+	}
+
+	ret = cryptodev_cipher_init(&ctx, "ecb(aes)", kek->key.secret.data, kek->key.secret.size);
+	if (ret < 0) {
+		err();
+		return ret;
+	}
+
+	raw_key = tobewrapped->key.secret.data;
+	key_size = tobewrapped->key.secret.size;
+
+	if (key_size % 8 != 0) {
+		err();
+		ret = -EINVAL;
+		goto cleanup;
+	}
+
+	n = key_size/8;
+
+
+	R = kmalloc(sizeof(*R)*n, GFP_KERNEL);
+	if (R == NULL) {
+		err();
+		ret = -ENOMEM;
+		goto cleanup;
+	}
+
+	/* R = P */
+	for (i=0;i<n;i++) {
+		memcpy(R[i], &raw_key[i*8], 8);
+	}
+
+	ret = rfc3394_wrap( R, n, &ctx, output, output_size, iv);
+	if (ret < 0) {
+		err();
+		goto cleanup;
+	}
+
+	ret = 0;
+
+cleanup:
+	kfree(R);
+	cryptodev_cipher_deinit(&ctx);
+
+	return ret;
+}
+
+#if 0
+/* for debugging */
+void print_val64(char* str, val64_t val)
+{
+	int i;
+	printk("%s: ",str);
+	for (i=0;i<8;i++)
+	  printk("%.2x", val[i]);
+	printk("\n");
+	
+}
+#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)
+{
+size_t n;
+val64_t A;
+int i, ret;
+struct cipher_data ctx;
+val64_t * R = NULL;
+
+	if (iv_size < sizeof(initA)) {
+		iv_size = sizeof(initA);
+		iv = initA;
+	}
+
+	ret = cryptodev_cipher_init(&ctx, "ecb(aes)", kek->key.secret.data, kek->key.secret.size);
+	if (ret < 0) {
+		err();
+		return ret;
+	}
+
+	output->type = NCR_KEY_TYPE_SECRET;
+
+	if (wrapped_key_size % 8 != 0) {
+		err();
+		ret = -EINVAL;
+		goto cleanup;
+	}
+
+	n = wrapped_key_size/8 - 1;
+
+	if (NCR_CIPHER_MAX_KEY_LEN < (n-1)*8) {
+		err();
+		ret = -EINVAL;
+		goto cleanup;
+	}
+
+	R = kmalloc(sizeof(*R)*n, GFP_KERNEL);
+	if (R == NULL) {
+		err();
+		ret = -ENOMEM;
+		goto cleanup;
+	}
+
+	ret = rfc3394_unwrap(wrapped_key, R, n, A, &ctx);
+	if (ret < 0) {
+		err();
+		goto cleanup;
+	}
+
+	if (memcmp(A, iv, 8)!= 0) {
+		err();
+		ret = -EINVAL;
+		goto cleanup;
+	}
+
+	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;
+
+cleanup:
+	kfree(R);
+	cryptodev_cipher_deinit(&ctx);
+
+	return ret;
+}
+
+static const char *ncr_wrap_name(ncr_wrap_algorithm_t algo)
+{
+	switch (algo) {
+	case NCR_WALG_AES_RFC3394:
+		return "aes-rfc3394";
+	case NCR_WALG_AES_RFC5649:
+		return "aes-rfc5649";
+	default:
+		return "unknown";
+	}
+}
+
+int ncr_key_wrap(struct ncr_lists *lst, void __user* arg)
+{
+struct ncr_key_wrap_st wrap;
+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)))) {
+		err();
+		return -EFAULT;
+	}
+
+	ret = ncr_key_item_get_read( &wkey, lst, wrap.keytowrap);
+	if (ret < 0) {
+		err();
+		return ret;
+	}
+
+	if (!(wkey->flags & NCR_KEY_FLAG_WRAPPABLE)) {
+		err();
+		ret = -EPERM;
+		goto fail;
+	}
+
+	ret = ncr_key_item_get_read( &key, lst, wrap.key);
+	if (ret < 0) {
+		err();
+		goto fail;
+	}
+
+	data_size = wrap.io_size;
+	data = kmalloc(data_size, GFP_KERNEL);
+	if (data == NULL) {
+		err();
+		ret = -ENOMEM;
+		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;
+	}
+	
+	if (ret < 0) {
+		err();
+		goto fail;
+	}
+
+	ret = copy_to_user(wrap.io, 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;
+	}
+
+fail:
+	audit_log_crypto_op(AUDIT_CRYPTO_OP_KEY_WRAP, lst->id, -1, NULL,
+			    ncr_wrap_name(wrap.algorithm), wrap.key,
+			    key != NULL ? key->key_id : NULL,
+			    key != NULL ? key->key_id_size : 0, wrap.keytowrap,
+			    wkey != NULL ? wkey->key_id : NULL,
+			    wkey != NULL ? wkey->key_id_size : 0);
+
+	if (wkey != NULL) _ncr_key_item_put(wkey);
+	if (key != NULL) _ncr_key_item_put(key);
+	kfree(data);
+
+	return ret;
+}
+
+/* Unwraps keys. All keys unwrapped are not accessible by 
+ * userspace.
+ */
+int ncr_key_unwrap(struct ncr_lists *lst, void __user* arg)
+{
+struct ncr_key_wrap_st wrap;
+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)))) {
+		err();
+		return -EFAULT;
+	}
+
+	ret = ncr_key_item_get_write( &wkey, lst, wrap.keytowrap);
+	if (ret < 0) {
+		err();
+		return ret;
+	}
+
+	ret = ncr_key_item_get_read( &key, lst, wrap.key);
+	if (ret < 0) {
+		err();
+		goto fail;
+	}
+
+	data_size = wrap.io_size;
+	data = kmalloc(data_size, GFP_KERNEL);
+	if (data == NULL) {
+		err();
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	if (unlikely(copy_from_user(data, wrap.io, data_size))) {
+		err();
+		ret = -EFAULT;
+		goto fail;
+	}
+
+	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;
+	}
+	
+fail:
+	audit_log_crypto_op(AUDIT_CRYPTO_OP_KEY_UNWRAP, lst->id, -1, NULL,
+			    ncr_wrap_name(wrap.algorithm), wrap.key,
+			    key != NULL ? key->key_id : NULL,
+			    key != NULL ? key->key_id_size : 0, wrap.keytowrap,
+			    wkey != NULL ? wkey->key_id : NULL,
+			    wkey != NULL ? wkey->key_id_size : 0);
+
+	if (wkey != NULL) _ncr_key_item_put(wkey);
+	if (key != NULL) _ncr_key_item_put(key);
+	if (data != NULL) kfree(data);
+
+	return ret;
+}
+
+int ncr_key_storage_wrap(struct ncr_lists *lst, void __user* arg)
+{
+struct ncr_key_storage_wrap_st wrap;
+struct key_item_st* wkey = NULL;
+void* data = NULL;
+size_t data_size;
+uint8_t * sdata = NULL;
+size_t sdata_size = 0;
+int ret;
+
+	if (master_key.type != NCR_KEY_TYPE_SECRET) {
+		err();
+		return -ENOKEY;
+	}
+
+	if (unlikely(copy_from_user(&wrap, arg, sizeof(wrap)))) {
+		err();
+		return -EFAULT;
+	}
+
+	ret = ncr_key_item_get_read( &wkey, lst, wrap.keytowrap);
+	if (ret < 0) {
+		err();
+		return ret;
+	}
+
+	if (!(wkey->flags & NCR_KEY_FLAG_WRAPPABLE)) {
+		err();
+		ret = -EPERM;
+		goto fail;
+	}
+
+	data_size = wrap.io_size;
+	data = kmalloc(data_size, GFP_KERNEL);
+	if (data == NULL) {
+		err();
+		ret = -ENOMEM;
+		goto fail;
+	}
+	
+	ret = key_to_storage_data(&sdata, &sdata_size, wkey);
+	if (ret < 0) {
+		err();
+		goto fail;
+	}
+
+	ret = _wrap_aes_rfc5649(sdata, sdata_size, &master_key, data, &data_size, NULL, 0);
+	if (ret < 0) {
+		err();
+		goto fail;
+	}
+
+	ret = copy_to_user(wrap.io, 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;
+	}
+
+fail:
+	audit_log_crypto_op(AUDIT_CRYPTO_OP_KEY_WRAP, lst->id, -1, NULL, NULL,
+			    -1, NULL, 0, wrap.keytowrap,
+			    wkey != NULL ? wkey->key_id : NULL,
+			    wkey != NULL ? wkey->key_id_size : 0);
+
+	if (wkey != NULL) _ncr_key_item_put(wkey);
+	if (data != NULL) kfree(data);
+	if (sdata != NULL) kfree(sdata);
+
+	return ret;
+}
+
+/* Unwraps keys. All keys unwrapped are not accessible by 
+ * userspace.
+ */
+int ncr_key_storage_unwrap(struct ncr_lists *lst, void __user* arg)
+{
+struct ncr_key_storage_wrap_st wrap;
+struct key_item_st* wkey = NULL;
+void* data = NULL;
+uint8_t * sdata = NULL;
+size_t sdata_size = 0, data_size;
+int ret;
+
+	if (master_key.type != NCR_KEY_TYPE_SECRET) {
+		err();
+		return -ENOKEY;
+	}
+
+	if (unlikely(copy_from_user(&wrap, arg, sizeof(wrap)))) {
+		err();
+		return -EFAULT;
+	}
+
+	ret = ncr_key_item_get_write( &wkey, lst, wrap.keytowrap);
+	if (ret < 0) {
+		err();
+		return ret;
+	}
+
+	data_size = wrap.io_size;
+	data = kmalloc(data_size, GFP_KERNEL);
+	if (data == NULL) {
+		err();
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	if (unlikely(copy_from_user(data, wrap.io, data_size))) {
+		err();
+		ret = -EFAULT;
+		goto fail;
+	}
+
+	sdata_size = data_size;
+	sdata = kmalloc(sdata_size, GFP_KERNEL);
+	if (sdata == NULL) {
+		err();
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	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;
+	}
+
+	ret = key_from_storage_data(wkey, sdata, sdata_size);
+	if (ret < 0) {
+		err();
+		goto fail;
+	}
+	
+
+fail:
+	audit_log_crypto_op(AUDIT_CRYPTO_OP_KEY_UNWRAP, lst->id, -1, NULL, NULL,
+			    -1, NULL, 0, wrap.keytowrap,
+			    wkey != NULL ? wkey->key_id : NULL,
+			    wkey != NULL ? wkey->key_id_size : 0);
+
+	if (wkey != NULL) _ncr_key_item_put(wkey);
+	if (data != NULL) kfree(data);
+	if (sdata != NULL) kfree(sdata);
+
+	return ret;
+}
diff --git a/crypto/userspace/ncr-key.c b/crypto/userspace/ncr-key.c
new file mode 100644
index 0000000..a6d90d3
--- /dev/null
+++ b/crypto/userspace/ncr-key.c
@@ -0,0 +1,680 @@
+/*
+ * New driver for /dev/crypto device (aka CryptoDev)
+ *
+ * Copyright (c) 2010 Katholieke Universiteit Leuven
+ *
+ * Author: Nikos Mavrogiannopoulos <[email-masked]>
+ *
+ * This file is part of linux cryptodev.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/audit.h>
+#include <linux/cryptodev.h>
+#include <linux/mm.h>
+#include <linux/ncr.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <asm/uaccess.h>
+#include <asm/ioctl.h>
+#include <linux/scatterlist.h>
+#include "ncr-int.h"
+
+static void ncr_key_clear(struct key_item_st* item);
+
+static int key_list_deinit_fn(int id, void *item, void *unused)
+{
+	(void)unused;
+	_ncr_key_item_put(item);
+	return 0;
+}
+
+void ncr_key_list_deinit(struct ncr_lists *lst)
+{
+	/* The mutex is not necessary, but doesn't hurt and makes it easier to
+	   verify locking correctness. */
+	mutex_lock(&lst->key_idr_mutex);
+	idr_for_each(&lst->key_idr, key_list_deinit_fn, NULL);
+	idr_remove_all(&lst->key_idr);
+	idr_destroy(&lst->key_idr);
+	mutex_unlock(&lst->key_idr_mutex);
+}
+
+/* returns the data item corresponding to desc */
+int ncr_key_item_get_read(struct key_item_st**st, struct ncr_lists *lst,
+	ncr_key_t desc)
+{
+struct key_item_st* item;
+int ret;
+	
+	*st = NULL;
+	
+	mutex_lock(&lst->key_idr_mutex);
+	item = idr_find(&lst->key_idr, desc);
+	if (item == NULL) {
+		err();
+		ret = -EINVAL;
+		goto exit;
+	}
+	atomic_inc(&item->refcnt);
+
+	if (atomic_read(&item->writer) != 0) {
+		/* writer in place busy */
+		atomic_dec(&item->refcnt);
+		ret = -EBUSY;
+		goto exit;
+	}
+
+	*st = item;
+	ret = 0;
+
+exit:
+	mutex_unlock(&lst->key_idr_mutex);
+	return ret;
+}
+
+/* as above but will never return anything that
+ * is in use.
+ */
+int ncr_key_item_get_write( struct key_item_st** st, 
+	struct ncr_lists *lst, ncr_key_t desc)
+{
+struct key_item_st* item;
+int ret;
+
+	*st = NULL;
+
+	mutex_lock(&lst->key_idr_mutex);
+	item = idr_find(&lst->key_idr, desc);
+	if (item == NULL) {
+		err();
+		ret = -EINVAL;
+		goto exit;
+	}
+	/* do not return items that are in use already */
+
+	if (atomic_add_unless(&item->writer, 1, 1)==0) {
+		/* another writer so busy */
+		ret = -EBUSY;
+		goto exit;
+	}
+
+	if (atomic_add_unless(&item->refcnt, 1, 2)==0) {
+		/* some reader is active so busy */
+		atomic_dec(&item->writer);
+		ret = -EBUSY;
+		goto exit;
+	}
+
+	*st = item;
+	ret = 0;
+
+exit:
+	mutex_unlock(&lst->key_idr_mutex);
+	return ret;
+}
+
+void _ncr_key_item_put( struct key_item_st* item)
+{
+	if (atomic_read(&item->writer) > 0)
+		atomic_dec(&item->writer);
+	if (atomic_dec_and_test(&item->refcnt)) {
+			ncr_limits_remove(item->uid, item->pid, LIMIT_TYPE_KEY);
+			ncr_key_clear(item);
+			kfree(item);
+	}
+}
+
+static void _ncr_key_remove(struct ncr_lists *lst, ncr_key_t desc)
+{
+	struct key_item_st * item;
+
+	mutex_lock(&lst->key_idr_mutex);
+	item = idr_find(&lst->key_idr, desc);
+	if (item != NULL)
+		idr_remove(&lst->key_idr, desc); /* Steal the reference */
+	mutex_unlock(&lst->key_idr_mutex);
+
+	if (item != NULL)
+		_ncr_key_item_put(item);
+}
+
+int ncr_key_init(struct ncr_lists *lst, void __user* arg)
+{
+	ncr_key_t desc;
+	struct key_item_st* key;
+	int ret;
+
+	ret = ncr_limits_add_and_check(current_euid(), task_pid_nr(current), LIMIT_TYPE_KEY);
+	if (ret < 0) {
+		err();
+		return ret;
+	}
+
+	key = kmalloc(sizeof(*key), GFP_KERNEL);
+	if (key == NULL) {
+		err();
+		ret = -ENOMEM;
+		goto err_limits;
+	}
+
+	memset(key, 0, sizeof(*key));
+
+	atomic_set(&key->refcnt, 1);
+	atomic_set(&key->writer, 0);
+	key->uid = current_euid();
+	key->pid = task_pid_nr(current);
+	key->context_id = lst->id;
+
+	mutex_lock(&lst->key_idr_mutex);
+	/* idr_pre_get() should preallocate enough, and, due to key_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->key_idr, GFP_KERNEL) == 0 ||
+	    idr_get_new(&lst->key_idr, key, &key->desc) != 0) {
+		mutex_unlock(&lst->key_idr_mutex);
+		_ncr_key_item_put(key);
+		return -ENOMEM;
+	}
+	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;
+
+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)
+{
+	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)
+{
+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)))) {
+		err();
+		return -EFAULT;
+	}
+
+	ret = ncr_key_item_get_read( &item, lst, data.key);
+	if (ret < 0) {
+		err();
+		return ret;
+	}
+
+	if (!(item->flags & NCR_KEY_FLAG_EXPORTABLE)) {
+		err();
+		ret = -EPERM;
+		goto fail;
+	}
+
+	switch (item->type) {
+		case NCR_KEY_TYPE_SECRET:
+			if (item->key.secret.size > data.idata_size) {
+				err();
+				ret = -EINVAL;
+				goto fail;
+			}
+
+			/* found */
+			if (item->key.secret.size > 0) {
+				ret = copy_to_user(data.idata, item->key.secret.data, item->key.secret.size);
+				if (unlikely(ret)) {
+					err();
+					ret = -EFAULT;
+					goto fail;
+				}
+			}
+
+			data.idata_size = item->key.secret.size;
+			break;
+		case NCR_KEY_TYPE_PUBLIC:
+		case NCR_KEY_TYPE_PRIVATE:
+			tmp_size = data.idata_size;
+			
+			tmp = kmalloc(tmp_size, GFP_KERNEL);
+			if (tmp == NULL) {
+				err();
+				ret = -ENOMEM;
+				goto fail;
+			}
+
+			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);
+			if (unlikely(ret)) {
+				err();
+				ret = -EFAULT;
+				goto fail;
+			}
+			
+			break;
+		default:
+			err();
+			ret = -EINVAL;
+			goto fail;
+	}
+
+	if (unlikely(copy_to_user(arg, &data, sizeof(data)))) {
+		err();
+		ret = -EFAULT;
+	} else
+		ret = 0;
+
+fail:
+	audit_log_crypto_op(AUDIT_CRYPTO_OP_KEY_EXPORT, lst->id, -1, NULL,
+			    ncr_algorithm_name(item->algorithm), item->desc,
+			    item->key_id, item->key_id_size, -1, NULL, 0);
+
+	kfree(tmp);
+	_ncr_key_item_put(item);
+	return ret;
+	
+}
+
+/* "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)
+{
+struct ncr_key_data_st data;
+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);
+	if (ret < 0) {
+		err();
+		return ret;
+	}
+
+	ncr_key_clear(item);
+
+	tmp = kmalloc(data.idata_size, GFP_KERNEL);
+	if (tmp == NULL) {
+		err();
+		ret = -ENOMEM;
+		goto fail;
+	}
+	
+	if (unlikely(copy_from_user(tmp, data.idata, data.idata_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) {
+		err();
+		ret = -EINVAL;
+		goto fail;
+	}
+	item->flags = data.flags;
+
+	if (data.key_id_size > MAX_KEY_ID_SIZE) {
+		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);
+
+	switch(item->type) {
+		case NCR_KEY_TYPE_SECRET:
+
+			if (tmp_size > NCR_CIPHER_MAX_KEY_LEN) {
+				err();
+				ret = -EINVAL;
+				goto fail;
+			}
+			
+			memcpy(item->key.secret.data, tmp, tmp_size);
+			item->key.secret.size = tmp_size;
+			break;
+		case NCR_KEY_TYPE_PRIVATE:
+		case NCR_KEY_TYPE_PUBLIC:
+			ret = ncr_pk_unpack( item, tmp, tmp_size);
+			if (ret < 0) {
+				err();
+				goto fail;
+			}
+			break;
+
+		default:
+			err();
+			ret = -EINVAL;
+			goto fail;
+	}
+
+	ret = 0;
+
+fail:
+	audit_log_crypto_op(AUDIT_CRYPTO_OP_KEY_IMPORT, lst->id, -1, NULL,
+			    ncr_algorithm_name(item->algorithm), item->desc,
+			    item->key_id, item->key_id_size, -1, NULL, 0);
+
+	_ncr_key_item_put(item);
+	kfree(tmp);
+
+	return ret;
+}
+
+static void ncr_key_clear(struct key_item_st* item)
+{
+	audit_log_crypto_op(AUDIT_CRYPTO_OP_KEY_ZEROIZE, item->context_id, -1,
+			    NULL, ncr_algorithm_name(item->algorithm),
+			    item->desc, item->key_id, item->key_id_size, -1,
+			    NULL, 0);
+
+	/* clears any previously allocated parameters */
+	if (item->type == NCR_KEY_TYPE_PRIVATE ||
+		item->type == NCR_KEY_TYPE_PUBLIC) {
+		
+		ncr_pk_clear(item);
+	}
+	memset(&item->key, 0, sizeof(item->key));
+	memset(item->key_id, 0, sizeof(item->key_id));
+	item->key_id_size = 0;
+	item->flags = 0;
+	
+	return;
+}
+
+/* Generate a secret key
+ */
+int ncr_key_generate(struct ncr_lists *lst, void __user* arg)
+{
+struct ncr_key_generate_st gen;
+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);
+	if (ret < 0) {
+		err();
+		return ret;
+	}
+
+	ncr_key_clear(item);
+
+	/* we generate only secret keys */
+	item->flags = gen.params.keyflags;
+	algo = _ncr_algo_to_properties(gen.params.algorithm);
+	if (algo == NULL) {
+		err();
+		ret = -EINVAL;
+		goto fail;
+	}
+	item->type = algo->key_type;
+	if (item->type == NCR_KEY_TYPE_SECRET) {
+		item->algorithm = algo;
+
+		size = gen.params.params.secret.bits/8;
+		if ((gen.params.params.secret.bits % 8 != 0) ||
+				(size > NCR_CIPHER_MAX_KEY_LEN)) {
+			err();
+			ret = -EINVAL;
+			goto fail;
+		}
+
+		get_random_bytes(item->key.secret.data, size);
+		item->key.secret.size = size;
+
+		/* generate random key id */
+		item->key_id_size = 5;
+		get_random_bytes(item->key_id, item->key_id_size);
+	} else {
+		err();
+		ret = -EINVAL;
+		goto fail;
+	}
+	
+	ret = 0;
+
+fail:
+	audit_log_crypto_op(AUDIT_CRYPTO_OP_KEY_GEN, lst->id, -1, NULL,
+			    ncr_algorithm_name(algo), item->desc, item->key_id,
+			    item->key_id_size, -1, NULL, 0);
+
+	if (ret < 0) item->type = NCR_KEY_TYPE_INVALID;
+	_ncr_key_item_put(item);
+	return ret;
+}
+
+int ncr_key_info(struct ncr_lists *lst, void __user* arg)
+{
+struct ncr_key_info_st info;
+struct key_item_st* item = NULL;
+int ret;
+
+	if (unlikely(copy_from_user(&info, arg, sizeof(info)))) {
+		err();
+		return -EFAULT;
+	}
+
+	ret = ncr_key_item_get_read(&item, lst, info.key);
+	if (ret < 0) {
+		err();
+		return ret;
+	}
+	
+	if (item->type == NCR_KEY_TYPE_INVALID) {
+		err();
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	info.flags = item->flags;
+	info.type = item->type;
+	info.algorithm = item->algorithm->algo;
+	
+	ret = 0;
+
+fail:
+	audit_log_crypto_op(AUDIT_CRYPTO_OP_KEY_GET_INFO, lst->id, -1, NULL,
+			    ncr_algorithm_name(item->algorithm), item->desc,
+			    item->key_id, item->key_id_size, -1, NULL, 0);
+
+	_ncr_key_item_put( item);
+
+	return ret;
+}
+
+int ncr_key_generate_pair(struct ncr_lists *lst, void __user* arg)
+{
+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);
+	if (ret < 0) {
+		err();
+		goto fail;
+	}
+
+	ret = ncr_key_item_get_write( &public, lst, gen.desc2);
+	if (ret < 0) {
+		err();
+		goto fail;
+	}
+
+	ncr_key_clear(public);
+	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);
+	if (private->algorithm == NULL) {
+		err();
+		ret = -EINVAL;
+		goto fail;
+	}
+	public->type = public->algorithm->key_type;
+	private->type = NCR_KEY_TYPE_PRIVATE;
+	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);
+		if (ret < 0) {
+			err();
+			goto fail;
+		}
+	} else {
+		err();
+		ret = -EINVAL;
+		goto fail;
+	}
+	
+	ret = 0;
+fail:
+	audit_log_crypto_op(AUDIT_CRYPTO_OP_KEY_GEN, lst->id, -1, NULL,
+			    ncr_algorithm_name(private != NULL
+					       ? private->algorithm : NULL),
+			    gen.desc, private != NULL ? private->key_id : NULL,
+			    private != NULL ? private->key_id_size : 0,
+			    gen.desc2, public != NULL ? public->key_id : NULL,
+			    public != NULL ? public->key_id_size : 0);
+
+	if (public) {
+		if (ret < 0) public->type = NCR_KEY_TYPE_INVALID;
+		_ncr_key_item_put(public);
+	}
+	if (private) {
+		if (ret < 0) private->type = NCR_KEY_TYPE_INVALID;
+		_ncr_key_item_put(private);
+	}
+	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)
+{
+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);
+	if (ret < 0) {
+		err();
+		return ret;
+	}
+
+	ret = ncr_key_item_get_write( &newkey, lst, data.newkey);
+	if (ret < 0) {
+		err();
+		goto fail;
+	}
+
+	ncr_key_clear(newkey);
+
+	newkey->flags = data.keyflags;
+
+	switch (key->type) {
+		case NCR_KEY_TYPE_PUBLIC:
+		case NCR_KEY_TYPE_PRIVATE:
+			ret = ncr_pk_derive(newkey, key, &data);
+			if (ret < 0) {
+				err();
+				goto fail;
+			}
+			break;
+		default:
+			err();
+			ret = -EINVAL;
+			goto fail;
+	}
+
+	if (unlikely(copy_to_user(arg, &data, sizeof(data)))) {
+		err();
+		ret = -EFAULT;
+	} else
+		ret = 0;
+
+fail:
+	audit_log_crypto_op(AUDIT_CRYPTO_OP_KEY_DERIVE, lst->id, -1, NULL,
+			    ncr_algorithm_name(key->algorithm), key->desc,
+			    key->key_id, key->key_id_size,
+			    data.newkey, newkey != NULL ? newkey->key_id : NULL,
+			    newkey != NULL ? newkey->key_id_size : 0);
+
+	_ncr_key_item_put(key);
+	if (newkey)
+		_ncr_key_item_put(newkey);
+	return ret;
+	
+}
+
diff --git a/crypto/userspace/ncr-limits.c b/crypto/userspace/ncr-limits.c
new file mode 100644
index 0000000..0a91125
--- /dev/null
+++ b/crypto/userspace/ncr-limits.c
@@ -0,0 +1,248 @@
+/*
+ * New driver for /dev/crypto device (aka CryptoDev)
+
+ * Copyright (c) 2010 Katholieke Universiteit Leuven
+ *
+ * Author: Nikos Mavrogiannopoulos <[email-masked]>
+ *
+ * This file is part of linux cryptodev.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/cryptodev.h>
+#include <linux/hash.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/ncr.h>
+#include <linux/slab.h>
+#include <linux/highmem.h>
+#include <linux/random.h>
+#include <asm/atomic.h>
+#include <linux/version.h>
+#include <linux/file.h>
+#include <linux/cred.h>
+#include "ncr-int.h"
+
+/* arbitrary now */
+static unsigned int max_per_user[] = {
+	[LIMIT_TYPE_KEY] = 128,
+};
+
+static unsigned int max_per_process[] = {
+	[LIMIT_TYPE_KEY] = 64,
+};
+
+struct limit_user_item_st {
+	struct hlist_node hlist;
+	uid_t uid;
+	atomic_t cnt[NUM_LIMIT_TYPES];
+};
+
+struct limit_process_item_st {
+	struct hlist_node hlist;
+	pid_t pid;
+	atomic_t cnt[NUM_LIMIT_TYPES];
+};
+
+static struct mutex user_limit_mutex;
+#define USER_LIMIT_HASH_BITS 7
+#define USER_LIMIT_TABLE_SIZE (1 << USER_LIMIT_HASH_BITS)
+static struct hlist_head user_limit_table[USER_LIMIT_TABLE_SIZE];
+
+static struct hlist_head *user_limit_hash(uid_t uid)
+{
+	return &user_limit_table[hash_long(uid, USER_LIMIT_HASH_BITS)];
+}
+
+static struct mutex process_limit_mutex;
+#define PROCESS_LIMIT_HASH_BITS 9
+#define PROCESS_LIMIT_TABLE_SIZE (1 << PROCESS_LIMIT_HASH_BITS)
+static struct hlist_head process_limit_table[PROCESS_LIMIT_TABLE_SIZE];
+
+static struct hlist_head *process_limit_hash(pid_t pid)
+{
+	return &process_limit_table[hash_long(pid, PROCESS_LIMIT_HASH_BITS)];
+}
+
+void ncr_limits_init(void)
+{
+	size_t i;
+
+	mutex_init(&user_limit_mutex);
+	for (i = 0; i < USER_LIMIT_TABLE_SIZE; i++)
+		INIT_HLIST_HEAD(&user_limit_table[i]);
+
+	mutex_init(&process_limit_mutex);
+	for (i = 0; i < PROCESS_LIMIT_TABLE_SIZE; i++)
+		INIT_HLIST_HEAD(&process_limit_table[i]);
+}
+
+void ncr_limits_deinit(void)
+{
+struct limit_process_item_st* pitem;
+struct limit_user_item_st* uitem;
+struct hlist_node *pos, *tmp;
+size_t i;
+
+	mutex_lock(&user_limit_mutex);
+	for (i = 0; i < USER_LIMIT_TABLE_SIZE; i++) {
+		hlist_for_each_entry_safe(uitem, pos, tmp, &user_limit_table[i],
+					  hlist) {
+			hlist_del(&uitem->hlist);
+			kfree(uitem);
+		}
+	}
+	mutex_unlock(&user_limit_mutex);
+	
+	mutex_lock(&process_limit_mutex);
+	for (i = 0; i < PROCESS_LIMIT_TABLE_SIZE; i++) {
+		hlist_for_each_entry_safe(pitem, pos, tmp,
+					  &process_limit_table[i], hlist) {
+			hlist_del(&pitem->hlist);
+			kfree(pitem);
+		}
+	}
+	mutex_unlock(&process_limit_mutex);
+
+}
+
+int ncr_limits_add_and_check(uid_t uid, pid_t pid, limits_type_t type)
+{
+struct limit_process_item_st* pitem;
+struct limit_user_item_st* uitem;
+struct hlist_head *user_head, *process_head;
+struct hlist_node *pos;
+int add = 1;
+int ret;
+	BUG_ON(type >= NUM_LIMIT_TYPES);
+
+	user_head = user_limit_hash(uid);
+	mutex_lock(&user_limit_mutex);
+	hlist_for_each_entry(uitem, pos, user_head, hlist) {
+		if (uitem->uid == uid) {
+			add = 0;
+
+			if (atomic_add_unless(&uitem->cnt[type], 1, max_per_user[type])==0) {
+				err();
+				mutex_unlock(&user_limit_mutex);
+				return -EPERM;
+			}
+			break;
+		}
+	}
+
+	if (add) {
+		size_t i;
+
+		uitem = kmalloc( sizeof(*uitem), GFP_KERNEL);
+		if (uitem == NULL) {
+			err();
+			mutex_unlock(&user_limit_mutex);
+			return -ENOMEM;
+		}
+		uitem->uid = uid;
+		for (i = 0; i < NUM_LIMIT_TYPES; i++)
+			atomic_set(&uitem->cnt[i], 0);
+		atomic_set(&uitem->cnt[type], 1);
+
+		hlist_add_head(&uitem->hlist, user_head);
+	}
+	mutex_unlock(&user_limit_mutex);
+
+	add = 1;
+	/* check process limits */
+	process_head = process_limit_hash(uid);
+	mutex_lock(&process_limit_mutex);
+	hlist_for_each_entry(pitem, pos, process_head, hlist) {
+		if (pitem->pid == pid) {
+			add = 0;
+			if (atomic_add_unless(&pitem->cnt[type], 1, max_per_process[type])==0) {
+				err();
+				mutex_unlock(&process_limit_mutex);
+
+				ret = -EPERM;
+				goto restore_user;
+			}
+			break;
+		}
+	}
+	
+
+	if (add) {
+		size_t i;
+
+		pitem = kmalloc(sizeof(*pitem), GFP_KERNEL);
+		if (pitem == NULL) {
+			err();
+			mutex_unlock(&process_limit_mutex);
+			ret = -ENOMEM;
+			goto restore_user;
+		}
+		pitem->pid = pid;
+		for (i = 0; i < NUM_LIMIT_TYPES; i++)
+			atomic_set(&pitem->cnt[i], 0);
+		atomic_set(&pitem->cnt[type], 1);
+
+		hlist_add_head(&pitem->hlist, process_head);
+	}
+	mutex_unlock(&process_limit_mutex);
+
+	return 0;
+
+restore_user:
+	mutex_lock(&user_limit_mutex);
+	hlist_for_each_entry(uitem, pos, user_head, hlist) {
+		if (uitem->uid == uid) {
+			atomic_dec(&uitem->cnt[type]);
+			break;
+		}
+	}
+	mutex_unlock(&user_limit_mutex);
+	return ret;
+}
+
+void ncr_limits_remove(uid_t uid, pid_t pid, limits_type_t type)
+{
+struct limit_process_item_st* pitem;
+struct limit_user_item_st* uitem;
+struct hlist_head *hhead;
+struct hlist_node *pos;
+
+	BUG_ON(type >= NUM_LIMIT_TYPES);
+	hhead = user_limit_hash(uid);
+	mutex_lock(&user_limit_mutex);
+	hlist_for_each_entry(uitem, pos, hhead, hlist) {
+		if (uitem->uid == uid) {
+			atomic_dec(&uitem->cnt[type]);
+			break;
+		}
+	}
+	mutex_unlock(&user_limit_mutex);
+
+	/* check process limits */
+	hhead = process_limit_hash(uid);
+	mutex_lock(&process_limit_mutex);
+	hlist_for_each_entry(pitem, pos, hhead, hlist) {
+		if (pitem->pid == pid) {
+			atomic_dec(&pitem->cnt[type]);
+			break;
+		}
+	}
+	mutex_unlock(&process_limit_mutex);
+
+	return;
+}
diff --git a/crypto/userspace/ncr-pk.c b/crypto/userspace/ncr-pk.c
new file mode 100644
index 0000000..36ea398
--- /dev/null
+++ b/crypto/userspace/ncr-pk.c
@@ -0,0 +1,716 @@
+/*
+ * New driver for /dev/crypto device (aka CryptoDev)
+
+ * Copyright (c) 2010 Katholieke Universiteit Leuven
+ *
+ * Author: Nikos Mavrogiannopoulos <[email-masked]>
+ *
+ * This file is part of linux cryptodev.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/cryptodev.h>
+#include <linux/mm.h>
+#include <linux/ncr.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <asm/uaccess.h>
+#include <asm/ioctl.h>
+#include <linux/scatterlist.h>
+#include "ncr-int.h"
+#include <tomcrypt.h>
+
+int _ncr_tomerr(int err)
+{
+	switch (err) {
+		case CRYPT_BUFFER_OVERFLOW:
+			return -EOVERFLOW;
+		case CRYPT_MEM:
+			return -ENOMEM;
+		default:
+			return -EINVAL;
+	}
+}
+
+void ncr_pk_clear(struct key_item_st* key)
+{
+	if (key->algorithm == NULL)
+		return;
+	switch(key->algorithm->algo) {
+		case NCR_ALG_RSA:
+			rsa_free(&key->key.pk.rsa);
+			break;
+		case NCR_ALG_DSA:
+			dsa_free(&key->key.pk.dsa);
+			break;
+		case NCR_ALG_DH:
+			dh_free(&key->key.pk.dh);
+			break;
+		default:
+			return;
+	}
+}
+
+static int ncr_pk_make_public_and_id( struct key_item_st * private, struct key_item_st * public)
+{
+	uint8_t * tmp;
+	unsigned long max_size;
+	int ret, cret;
+	unsigned long key_id_size;
+
+	max_size = KEY_DATA_MAX_SIZE;
+	tmp = kmalloc(max_size, GFP_KERNEL);
+	if (tmp == NULL) {
+		err();
+		return -ENOMEM;
+	}
+
+	switch(private->algorithm->algo) {
+		case NCR_ALG_RSA:
+			cret = rsa_export(tmp, &max_size, PK_PUBLIC, &private->key.pk.rsa);
+			if (cret != CRYPT_OK) {
+				err();
+				ret = _ncr_tomerr(cret);
+				goto fail;
+			}
+
+			cret = rsa_import(tmp, max_size, &public->key.pk.rsa);
+			if (cret != CRYPT_OK) {
+				err();
+				ret = _ncr_tomerr(cret);
+				goto fail;
+			}
+			break;
+		case NCR_ALG_DSA:
+			cret = dsa_export(tmp, &max_size, PK_PUBLIC, &private->key.pk.dsa);
+			if (cret != CRYPT_OK) {
+				err();
+				ret = _ncr_tomerr(cret);
+				goto fail;
+			}
+
+			cret = dsa_import(tmp, max_size, &public->key.pk.dsa);
+			if (cret != CRYPT_OK) {
+				err();
+				ret = _ncr_tomerr(cret);
+				goto fail;
+			}
+			break;
+		case NCR_ALG_DH:
+			ret = dh_generate_public(&public->key.pk.dh, &private->key.pk.dh);
+			if (ret < 0) {
+				err();
+				goto fail;
+			}
+			break;
+		default:
+			err();
+			ret = -EINVAL;
+			goto fail;
+	}
+
+	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);
+	if (cret != CRYPT_OK) {
+		err();
+		ret = _ncr_tomerr(cret);
+		goto fail;
+	}
+	private->key_id_size = public->key_id_size = key_id_size;
+	memcpy(public->key_id, private->key_id, key_id_size);			
+
+	ret = 0;
+fail:	
+	kfree(tmp);
+	
+	return ret;
+}
+
+int ncr_pk_pack( const struct key_item_st * key, uint8_t * packed, uint32_t * packed_size)
+{
+	unsigned long max_size = *packed_size;
+	int cret, ret;
+
+	if (packed == NULL || packed_size == NULL) {
+		err();
+		return -EINVAL;
+	}
+
+	switch(key->algorithm->algo) {
+		case NCR_ALG_RSA:
+			cret = rsa_export(packed, &max_size, key->key.pk.rsa.type, (void*)&key->key.pk.rsa);
+			if (cret != CRYPT_OK) {
+				*packed_size = max_size;
+				err();
+				return _ncr_tomerr(cret);
+			}
+			break;
+		case NCR_ALG_DSA:
+			cret = dsa_export(packed, &max_size, key->key.pk.dsa.type, (void*)&key->key.pk.dsa);
+			if (cret != CRYPT_OK) {
+				*packed_size = max_size;
+				err();
+				return _ncr_tomerr(cret);
+			}
+			break;
+		case NCR_ALG_DH:
+			ret = dh_export(packed, &max_size, key->key.pk.dsa.type, (void*)&key->key.pk.dsa);
+			if (ret < 0) {
+				err();
+				return ret;
+			}
+			break;
+		default:
+			err();
+			return -EINVAL;
+	}
+	
+	*packed_size = max_size;
+
+	return 0;
+}
+
+int ncr_pk_unpack( struct key_item_st * key, const void * packed, size_t packed_size)
+{
+	int cret, ret;
+
+	if (key == NULL || packed == NULL) {
+		err();
+		return -EINVAL;
+	}
+
+	switch(key->algorithm->algo) {
+		case NCR_ALG_RSA:
+			cret = rsa_import(packed, packed_size, (void*)&key->key.pk.rsa);
+			if (cret != CRYPT_OK) {
+				err();
+				return _ncr_tomerr(cret);
+			}
+			break;
+		case NCR_ALG_DSA:
+			cret = dsa_import(packed, packed_size, (void*)&key->key.pk.dsa);
+			if (cret != CRYPT_OK) {
+				err();
+				return _ncr_tomerr(cret);
+			}
+			break;
+		case NCR_ALG_DH:
+			ret = dh_import(packed, packed_size, (void*)&key->key.pk.dh);
+			if (ret < 0) {
+				err();
+				return ret;
+			}
+			break;
+		default:
+			err();
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+struct keygen_st {
+};
+
+int ncr_pk_generate(const struct algo_properties_st *algo,
+	struct ncr_key_generate_params_st * params,
+	struct key_item_st* private, struct key_item_st* public) 
+{
+	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)
+				e = 65537;
+			cret = rsa_make_key(params->params.rsa.bits/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;
+
+			cret = dsa_make_key(params->params.dsa.q_bits/8, 
+				params->params.dsa.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;
+			}
+
+			if (unlikely(copy_from_user(g, params->params.dh.g, g_size))) {
+				err();
+				ret = -EFAULT;
+				goto fail;
+			}
+		
+			ret = dh_import_params(&private->key.pk.dh, p, p_size, g, g_size);
+			if (ret < 0) {
+				err();
+				goto fail;
+			}
+
+			ret = dh_generate_key(&private->key.pk.dh);
+			if (ret < 0) {
+				err();
+				goto fail;
+			}
+			break;
+		}
+		default:
+			err();
+			return -EINVAL;
+	}
+
+fail:
+	kfree(tmp);
+
+	if (ret < 0) {
+		err();
+		return ret;
+	}
+
+	ret = ncr_pk_make_public_and_id(private, public);
+	if (ret < 0) {
+		err();
+		return ret;
+	}
+	
+	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
+ */
+
+void ncr_pk_cipher_deinit(struct ncr_pk_ctx* ctx)
+{
+	if (ctx->init) {
+		ctx->init = 0;
+		ctx->key = NULL;
+	}
+}
+
+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)
+{
+	memset(ctx, 0, sizeof(*ctx));
+	
+	if (key->algorithm != algo) {
+		err();
+		return -EINVAL;
+	}
+
+	ctx->algorithm = algo;
+	ctx->key = key;
+	ctx->sign_hash = sign_hash;
+
+	switch(algo->algo) {
+		case NCR_ALG_RSA:
+			if (params->params.rsa.type == RSA_PKCS1_V1_5)
+				ctx->type = LTC_LTC_PKCS_1_V1_5;
+			else if (params->params.rsa.type == RSA_PKCS1_OAEP) {
+				ctx->type = LTC_LTC_PKCS_1_OAEP;
+				ctx->oaep_hash = _ncr_algo_to_properties(params->params.rsa.oaep_hash);
+				if (ctx->oaep_hash == NULL) {
+				  err();
+				  return -EINVAL;
+				}
+			} else if (params->params.rsa.type == RSA_PKCS1_PSS) {
+				ctx->type = LTC_LTC_PKCS_1_PSS;
+			} else {
+				err();
+				return -EINVAL;
+			}
+
+			ctx->salt_len = params->params.rsa.pss_salt;
+			break;
+		case NCR_ALG_DSA:
+			break;
+		default:
+			err();
+			return -EINVAL;
+	}
+	
+	ctx->init = 1;
+
+	return 0;
+}
+
+int ncr_pk_cipher_encrypt(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 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;
+	}
+
+	ret = sg_copy_to_buffer((struct scatterlist*)isg, isg_cnt, tmp, isg_size);
+	if (ret != isg_size) {
+		err();
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	input = tmp;
+	output = &tmp[isg_size];
+
+
+	switch(ctx->algorithm->algo) {
+		case NCR_ALG_RSA:
+			cret = rsa_encrypt_key_ex( input, isg_size, output, &osize, 
+				NULL, 0, ctx->oaep_hash, ctx->type, &ctx->key->key.pk.rsa);
+
+			if (cret != CRYPT_OK) {
+				err();
+				ret = _ncr_tomerr(cret);
+				goto fail;
+			}
+			*osg_size = osize;
+
+			break;
+		case NCR_ALG_DSA:
+			ret = -EINVAL;
+			goto fail;
+		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;
+	}	
+
+	ret = 0;
+
+fail:
+	kfree(tmp);
+	return ret;
+}
+
+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 cret, ret;
+int stat;
+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;
+	}
+
+	switch(ctx->algorithm->algo) {
+		case NCR_ALG_RSA:
+			cret = rsa_decrypt_key_ex( input, isg_size, output, &osize, 
+				NULL, 0, ctx->oaep_hash, ctx->type, &stat, &ctx->key->key.pk.rsa);
+
+			if (cret != CRYPT_OK) {
+				err();
+				ret = _ncr_tomerr(cret);
+				goto fail;
+			}
+
+			if (stat==0) {
+				err();
+				ret = -EINVAL;
+				goto fail;
+			}
+			*osg_size = osize;
+			break;
+		case NCR_ALG_DSA:
+			ret = -EINVAL;
+			goto fail;
+		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;
+	}	
+
+	ret = 0;
+fail:
+	kfree(tmp);
+	
+	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 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;
+	}
+
+	switch(ctx->algorithm->algo) {
+		case NCR_ALG_RSA:
+			if (ctx->sign_hash == NULL) {
+				err();
+				return -EINVAL;
+			}
+			cret = rsa_sign_hash_ex( input, isg_size, output, &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;
+			break;
+		case NCR_ALG_DSA:
+			cret = dsa_sign_hash( input, isg_size, output, &osize, 
+				&ctx->key->key.pk.dsa);
+
+			if (cret != CRYPT_OK) {
+				err();
+				return _ncr_tomerr(cret);
+			}
+			*osg_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;
+	}
+	ret = 0;
+fail:
+	kfree(tmp);
+	
+	return ret;
+}
+
+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 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;
+	}
+
+	switch(ctx->algorithm->algo) {
+		case NCR_ALG_RSA:
+			if (ctx->sign_hash == NULL) {
+				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);
+			if (cret != CRYPT_OK) {
+				err();
+				ret = _ncr_tomerr(cret);
+				goto fail;
+			}
+
+			if (stat == 1)
+				*err = 0;
+			else
+				*err = NCR_VERIFICATION_FAILED;
+			
+			break;
+		case NCR_ALG_DSA:
+			cret = dsa_verify_hash( sig, sign_sg_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;
+
+			break;
+		default:
+			err();
+			ret = -EINVAL;
+			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)
+{
+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:
+			err();
+			return -EINVAL;
+	}
+
+	ret = 0;
+fail:
+	kfree(tmp);
+	return ret;
+}
diff --git a/crypto/userspace/ncr-pk.h b/crypto/userspace/ncr-pk.h
new file mode 100644
index 0000000..25a0571
--- /dev/null
+++ b/crypto/userspace/ncr-pk.h
@@ -0,0 +1,55 @@
+#ifndef NCR_PK_H
+# define NCR_PK_H
+
+#include <tomcrypt.h>
+
+struct ncr_pk_ctx {
+	const struct algo_properties_st *algorithm; /* algorithm */
+	
+	const struct algo_properties_st *sign_hash; /* for verification */
+	
+	const struct algo_properties_st *oaep_hash;
+	int salt_len; /* for RSA-PSS signatures */
+	
+	int type; /* libtomcrypt type */
+	int init; /* non zero if initialized */
+	
+	struct key_item_st * key;
+};
+
+/* 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,
+	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);
+void ncr_pk_cipher_deinit(struct ncr_pk_ctx* ctx);
+
+int ncr_pk_cipher_encrypt(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_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_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_tomerr(int err);
+
+int ncr_pk_derive(struct key_item_st* newkey, struct key_item_st* oldkey,
+	struct ncr_key_derivation_params_st * params);
+
+#endif
diff --git a/crypto/userspace/ncr-sessions.c b/crypto/userspace/ncr-sessions.c
new file mode 100644
index 0000000..ae87bff
--- /dev/null
+++ b/crypto/userspace/ncr-sessions.c
@@ -0,0 +1,1098 @@
+/*
+ * New driver for /dev/crypto device (aka CryptoDev)
+
+ * Copyright (c) 2010 Katholieke Universiteit Leuven
+ * Portions Copyright (c) 2010 Phil Sutter
+ * 
+ * Author: Nikos Mavrogiannopoulos <[email-masked]>
+ *
+ * This file is part of linux cryptodev.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/audit.h>
+#include <linux/crypto.h>
+#include <linux/cryptodev.h>
+#include <linux/ncr.h>
+#include "ncr-int.h"
+#include <linux/mm_types.h>
+#include <linux/scatterlist.h>
+
+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);
+
+static int session_list_deinit_fn(int id, void *item, void *unused)
+{
+	(void)unused;
+	_ncr_sessions_item_put(item);
+	return 0;
+}
+
+void ncr_sessions_list_deinit(struct ncr_lists *lst)
+{
+	/* The mutex is not necessary, but doesn't hurt and makes it easier to
+	   verify locking correctness. */
+	mutex_lock(&lst->session_idr_mutex);
+	idr_for_each(&lst->session_idr, session_list_deinit_fn, NULL);
+	idr_remove_all(&lst->session_idr);
+	idr_destroy(&lst->session_idr);
+	mutex_unlock(&lst->session_idr_mutex);
+}
+
+/* returns the data item corresponding to desc */
+struct session_item_st* ncr_sessions_item_get(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) {
+		atomic_inc(&item->refcnt);
+		mutex_unlock(&lst->session_idr_mutex);
+		return item;
+	}
+	mutex_unlock(&lst->session_idr_mutex);
+
+	err();
+	return NULL;
+}
+
+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);
+		if (item->key)
+			_ncr_key_item_put(item->key);
+		kfree(item->sg);
+		kfree(item->pages);
+		kfree(item);
+	}
+}
+
+struct session_item_st* ncr_session_new(struct ncr_lists *lst)
+{
+	struct session_item_st* sess;
+
+	sess = kzalloc(sizeof(*sess), GFP_KERNEL);
+	if (sess == NULL) {
+		err();
+		return NULL;
+	}
+
+	sess->array_size = DEFAULT_PREALLOC_PAGES;
+	sess->pages = kzalloc(sess->array_size *
+			sizeof(struct page *), GFP_KERNEL);
+	sess->sg = kzalloc(sess->array_size *
+			sizeof(struct scatterlist), GFP_KERNEL);
+	if (sess->sg == NULL || sess->pages == NULL) {
+		err();
+		goto err_sess;
+	}
+	init_MUTEX(&sess->mem_mutex);
+
+	atomic_set(&sess->refcnt, 2); /* One for lst->list, one for "sess" */
+
+	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);
+
+	return sess;
+
+err_sess:
+	kfree(sess->sg);
+	kfree(sess->pages);
+	kfree(sess);
+	return NULL;
+}
+
+static const struct algo_properties_st algo_properties[] = {
+	{ .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)", 
+		.needs_iv = 1, .is_symmetric=1, .can_encrypt=1,
+		.key_type = NCR_KEY_TYPE_SECRET },
+	{ .algo = NCR_ALG_AES_CBC, .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)", 
+		.needs_iv = 1, .is_symmetric=1, .can_encrypt=1,
+		.key_type = NCR_KEY_TYPE_SECRET },
+	{ .algo = NCR_ALG_AES_CTR, .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)", 
+		.needs_iv = 1, .is_symmetric=1, .can_encrypt=1,
+		.key_type = NCR_KEY_TYPE_SECRET },
+	{ .algo = NCR_ALG_ARCFOUR, .kstr = NULL, 
+		.needs_iv = 0, .is_symmetric=1, .can_encrypt=1,
+		.key_type = NCR_KEY_TYPE_SECRET },
+	{ .algo = NCR_ALG_AES_ECB, .kstr = "ecb(aes)", 
+		.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", 
+		.digest_size = 20, .can_digest=1,
+		.key_type = NCR_KEY_TYPE_INVALID },
+	{ .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", 
+		.digest_size = 28, .can_digest=1,
+		.key_type = NCR_KEY_TYPE_INVALID },
+	{ .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", 
+		.digest_size = 48, .can_digest=1,
+		.key_type = NCR_KEY_TYPE_INVALID },
+	{ .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)", 
+		.digest_size = 20, .can_sign=1,
+		.key_type = NCR_KEY_TYPE_SECRET },
+	{ .algo = NCR_ALG_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)", 
+		.digest_size = 28, .can_sign=1,
+		.key_type = NCR_KEY_TYPE_SECRET },
+	{ .algo = NCR_ALG_HMAC_SHA2_256, .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)", 
+		.digest_size = 48, .can_sign=1,
+		.key_type = NCR_KEY_TYPE_SECRET },
+	{ .algo = NCR_ALG_HMAC_SHA2_512, .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,
+		.can_encrypt=1, .can_sign=1, .key_type = NCR_KEY_TYPE_PUBLIC },
+	{ .algo = NCR_ALG_DSA, .kstr = NULL, .is_pk = 1,
+		.can_sign=1, .key_type = NCR_KEY_TYPE_PUBLIC },
+	{ .algo = NCR_ALG_DH, .kstr = NULL, .is_pk = 1,
+		.can_kx=1, .key_type = NCR_KEY_TYPE_PUBLIC },
+	{ .algo = NCR_ALG_NONE }
+
+};
+
+const struct algo_properties_st *_ncr_algo_to_properties(ncr_algorithm_t 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];
+	}
+
+	return NULL;
+}
+
+static const char *ncr_op_name(ncr_crypto_op_t op)
+{
+	static const char *const names[] = {
+		[NCR_OP_ENCRYPT] = "encrypt",
+		[NCR_OP_DECRYPT] = "decrypt",
+		[NCR_OP_SIGN] = "sign",
+		[NCR_OP_VERIFY] = "verify"
+	};
+
+	const char *res;
+
+	res = NULL;
+	if (op < ARRAY_SIZE(names))
+		res = names[op];
+	if (res == NULL)
+		res = "unknown";
+	return res;
+}
+
+const char *ncr_algorithm_name(const struct algo_properties_st *algo)
+{
+	if (algo == NULL)
+		return "unknown";
+	if (algo->kstr != NULL)
+		return algo->kstr;
+	switch (algo->algo) {
+	case NCR_ALG_RSA:
+		return "rsa";
+	case NCR_ALG_DSA:
+		return "dsa";
+	case NCR_ALG_DH:
+		return "dh";
+	default:
+		return "unknown";
+	}
+}
+
+static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* session)
+{
+	struct session_item_st* ns = NULL;
+	int ret, audit_ret;
+	const struct algo_properties_st *sign_hash;
+
+	ns = ncr_session_new(lists);
+	if (ns == NULL) {
+		err();
+		return -ENOMEM;
+	}
+
+	ns->op = session->op;
+	ns->algorithm = _ncr_algo_to_properties(session->algorithm);
+	if (ns->algorithm == NULL) {
+		err();
+		ret = -EINVAL;
+		goto fail;
+	}
+	
+	switch(session->op) {
+		case NCR_OP_ENCRYPT:
+		case NCR_OP_DECRYPT:
+			if (!ns->algorithm->can_encrypt) {
+				err();
+				ret = -EINVAL;
+				goto fail;
+			}
+
+			/* read key */
+			ret = ncr_key_item_get_read( &ns->key, lists, session->key);
+			if (ret < 0) {
+				err();
+				goto fail;
+			}
+			if (ns->key->type == NCR_KEY_TYPE_SECRET) {
+				int keysize = ns->key->key.secret.size;
+				
+				if (session->algorithm == NCR_ALG_NULL)
+				  keysize = 0;
+				
+				if (ns->algorithm->kstr == NULL) {
+					err();
+					ret = -EINVAL;
+					goto fail;
+				}
+
+				ret = cryptodev_cipher_init(&ns->cipher, ns->algorithm->kstr,
+					ns->key->key.secret.data, keysize);
+				if (ret < 0) {
+					err();
+					goto fail;
+				}
+
+				if (ns->algorithm->needs_iv) {
+					if (session->params.params.cipher.iv_size > sizeof(session->params.params.cipher.iv)) {
+						err();
+						ret = -EINVAL;
+						goto fail;
+					}
+					cryptodev_cipher_set_iv(&ns->cipher, session->params.params.cipher.iv, session->params.params.cipher.iv_size);
+				}
+			} 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);
+				if (ret < 0) {
+					err();
+					goto fail;
+				}
+			} else {
+				err();
+				ret = -EINVAL;
+				goto fail;
+			}
+			break;
+
+		case NCR_OP_SIGN:
+		case NCR_OP_VERIFY:
+			if (!ns->algorithm->can_sign && !ns->algorithm->can_digest) {
+				err();
+				ret = -EINVAL;
+				goto fail;
+			}
+
+			if (ns->algorithm->can_digest) {
+				if (ns->algorithm->kstr == NULL) {
+					err();
+					ret = -EINVAL;
+					goto fail;
+				}
+
+				ret = cryptodev_hash_init(&ns->hash, ns->algorithm->kstr, 0, 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) {
+					err();
+					goto fail;
+				}
+
+				if (ns->algorithm->is_hmac && ns->key->type == NCR_KEY_TYPE_SECRET) {
+					if (ns->algorithm->kstr == NULL) {
+						err();
+						ret = -EINVAL;
+						goto fail;
+					}
+
+					ret = cryptodev_hash_init(&ns->hash, ns->algorithm->kstr, 1,
+						ns->key->key.secret.data, ns->key->key.secret.size);
+					if (ret < 0) {
+						err();
+						goto fail;
+					}
+
+				} 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)) {
+						err();
+						ret = PTR_ERR(sign_hash);
+						goto fail;
+					}
+
+					if (!sign_hash->can_digest) {
+						err();
+						ret = -EINVAL;
+						goto fail;
+					}
+
+					if (sign_hash->kstr == NULL) {
+						err();
+						ret = -EINVAL;
+						goto fail;
+					}
+
+					ret = ncr_pk_cipher_init(ns->algorithm, &ns->pk, 
+						&session->params, ns->key, sign_hash);
+					if (ret < 0) {
+						err();
+						goto fail;
+					}
+
+					ret = cryptodev_hash_init(&ns->hash, sign_hash->kstr, 0, NULL, 0);
+					if (ret < 0) {
+						err();
+						goto fail;
+					}
+				} else {
+					err();
+					ret = -EINVAL;
+					goto fail;
+				}
+			}
+
+			break;
+		default:
+			err();
+			ret = -EINVAL;
+			goto fail;
+	}
+	
+	ret = 0;
+	session->ses = ns->desc;
+
+fail:
+	// algorithm, params, op
+	audit_ret = audit_log_crypto_op(AUDIT_CRYPTO_OP_SESSION_INIT, lists->id,
+					ns->desc, ncr_op_name(ns->op),
+					ncr_algorithm_name(ns->algorithm),
+					session->key,
+					ns->key != NULL ? ns->key->key_id : NULL,
+					ns->key != NULL ? ns->key->key_id_size : 0,
+					-1, NULL, 0);
+	if (audit_ret < 0 && ret == 0)
+		ret = audit_ret;
+	if (ret < 0) {
+		_ncr_session_remove(lists, ns->desc);
+	}
+	_ncr_sessions_item_put(ns);
+
+	return ret;
+}
+
+int ncr_session_init(struct ncr_lists* lists, void __user* arg)
+{
+	struct ncr_session_st session;
+	int ret;
+
+	if (unlikely(copy_from_user(&session, arg, sizeof(session)))) {
+		err();
+		return -EFAULT;
+	}
+
+	ret = _ncr_session_init(lists, &session);
+	if (unlikely(ret)) {
+		err();
+		return ret;
+	}
+
+	ret = copy_to_user( arg, &session, sizeof(session));
+	if (unlikely(ret)) {
+		err();
+		_ncr_session_remove(lists, session.ses);
+		return -EFAULT;
+	}
+	return ret;
+}
+
+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)
+{
+int ret;
+
+	if (sess->algorithm->is_symmetric) {
+		/* read key */
+		ret = cryptodev_cipher_encrypt(&sess->cipher, input, 
+			output, input_size);
+		if (ret < 0) {
+			err();
+			return ret;
+		}
+		/* FIXME: handle ciphers that do not require that */
+		*output_size = input_size;
+	} else { /* public key */
+		ret = ncr_pk_cipher_encrypt(&sess->pk, input, input_cnt, input_size,
+			output, output_cnt, output_size);
+		
+		if (ret < 0) {
+			err();
+			return ret;
+		}
+	}
+	
+	return 0;
+}
+
+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)
+{
+int ret;
+
+	if (sess->algorithm->is_symmetric) {
+		/* read key */
+		ret = cryptodev_cipher_decrypt(&sess->cipher, input, 
+			output, input_size);
+		if (ret < 0) {
+			err();
+			return ret;
+		}
+		/* FIXME: handle ciphers that do not require equality */
+		*output_size = input_size;
+	} else { /* public key */
+		ret = ncr_pk_cipher_decrypt(&sess->pk, input, input_cnt, input_size,
+			output, output_cnt, output_size);
+		
+		if (ret < 0) {
+			err();
+			return 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);
+}
+
+static int _ncr_session_grow_pages(struct session_item_st *ses, int pagecount)
+{
+	struct scatterlist *sg;
+	struct page **pages;
+	int array_size;
+
+	if (likely(pagecount < ses->array_size))
+		return 0;
+
+	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 (unlikely(pages == NULL))
+		return -ENOMEM;
+	ses->pages = pages;
+	sg = krealloc(ses->sg, array_size * sizeof(struct scatterlist),
+		      GFP_KERNEL);
+	if (unlikely(sg == NULL))
+		return -ENOMEM;
+	ses->sg = sg;
+
+	ses->array_size = array_size;
+	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)
+{
+	int pagecount = 0;
+
+	if (unlikely(udata == NULL)) {
+		err();
+		return -EINVAL;
+	}
+
+	pagecount = PAGECOUNT(udata, udata_size);
+	_ncr_session_grow_pages(ses, pagecount);
+
+	if (__get_userbuf(udata, udata_size, 1,
+			pagecount, ses->pages, ses->sg)) {
+		err();
+		return -EINVAL;
+	}
+	(*dst_sg) = ses->sg;
+	*dst_cnt = pagecount;
+
+	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;
+	}
+
+	src_pagecount = PAGECOUNT(op->data.udata.input, input_size);
+
+	if (op->data.udata.input != op->data.udata.output) {	/* 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);
+		} 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);
+	}
+
+	pagecount = src_pagecount + dst_pagecount;
+	_ncr_session_grow_pages(ses, pagecount);
+
+	if (__get_userbuf(op->data.udata.input, input_size, write_src,
+			src_pagecount, ses->pages, ses->sg)) {
+		err();
+		printk("write: %d\n", write_src);
+		return -EINVAL;
+	}
+	(*src_sg) = ses->sg;
+	*src_cnt = src_pagecount;
+
+	if (dst_pagecount) {
+		*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)) {
+			err();
+			release_user_pages(ses->pages, src_pagecount);
+			return -EINVAL;
+		}
+	} else {
+		if (op->data.udata.output != NULL) {
+			*dst_cnt = src_pagecount;
+			(*dst_sg) = (*src_sg);
+		} else {
+			*dst_cnt = 0;
+			*dst_sg = NULL;
+		}
+	}
+	
+	ses->available_pages = pagecount;
+
+	return 0;
+}
+
+/* Called when userspace buffers are used */
+static int _ncr_session_update(struct ncr_lists* lists, struct ncr_session_op_st* op)
+{
+	int ret;
+	struct session_item_st* sess;
+	struct scatterlist *isg;
+	struct scatterlist *osg;
+	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 (down_interruptible(&sess->mem_mutex)) {
+		err();
+		_ncr_sessions_item_put(sess);
+		return -ERESTARTSYS;
+	}
+
+	ret = get_userbuf2(sess, op, &isg, &isg_cnt, &osg, &osg_cnt);
+	if (ret < 0) {
+		err();
+		goto fail;
+	}
+	isg_size = op->data.udata.input_size;
+	osg_size = op->data.udata.output_size;
+
+	switch(sess->op) {
+		case NCR_OP_ENCRYPT:
+			if (osg == NULL) {
+				err();
+				ret = -EINVAL;
+				goto fail;
+			}
+
+			if (osg_size < isg_size) {
+				err();
+				ret = -EINVAL;
+				goto fail;
+			}
+
+			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;
+			
+			break;
+		case NCR_OP_DECRYPT:
+			if (osg == NULL) {
+				err();
+				ret = -EINVAL;
+				goto fail;
+			}
+
+			if (osg_size < isg_size) {
+				err();
+				ret = -EINVAL;
+				goto fail;
+			}
+
+			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;
+
+			break;
+
+		case NCR_OP_SIGN:
+		case NCR_OP_VERIFY:
+			ret = cryptodev_hash_update(&sess->hash, isg, isg_size);
+			if (ret < 0) {
+				err();
+				goto fail;
+			}
+			break;
+		default:
+			err();
+			ret = -EINVAL;
+			goto fail;
+	}
+
+	ret = 0;
+
+fail:
+	audit_log_crypto_op(AUDIT_CRYPTO_OP_SESSION_OP, lists->id, sess->desc,
+			    ncr_op_name(sess->op),
+			    ncr_algorithm_name(sess->algorithm), -1, NULL, 0,
+			    -1, NULL, 0);
+
+	if (sess->available_pages) {
+		release_user_pages(sess->pages, sess->available_pages);
+		sess->available_pages = 0;
+	}
+	up(&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)
+{
+	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;
+}
+
+static int _ncr_session_final(struct ncr_lists* lists, struct ncr_session_op_st* op)
+{
+	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;
+	}
+
+	ret = try_session_update(lists, op);
+	if (ret < 0) {
+		err();
+		_ncr_sessions_item_put(sess);
+		return ret;
+	}
+
+	if (down_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);
+			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) {
+				err();
+				ret = -EINVAL;
+				goto fail;
+			}
+			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;
+
+		case NCR_OP_SIGN:
+			ret = get_userbuf1(sess, udata, *udata_size, &osg, &osg_cnt);
+			if (ret < 0) {
+				err();
+				goto fail;
+			}
+			orig_osg_size = osg_size = *udata_size;
+
+			digest_size = sess->hash.digestsize;
+			if (digest_size == 0 || osg_size < digest_size) {
+				err();
+				ret = -EINVAL;
+				goto fail;
+			}
+
+			ret = cryptodev_hash_final(&sess->hash, digest);
+			if (ret < 0) {
+				err();
+				goto fail;
+			}
+
+			ret = sg_copy_from_buffer(osg, osg_cnt, digest, digest_size);
+			if (ret != digest_size) {
+				err();
+				ret = -EINVAL;
+				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:
+			err();
+			ret = -EINVAL;
+			goto fail;
+	}
+
+	if (osg_size > 0)
+		*udata_size = osg_size;
+
+	ret = 0;
+
+fail:
+	audit_log_crypto_op(AUDIT_CRYPTO_OP_SESSION_FINAL, lists->id,
+			    sess->desc, ncr_op_name(sess->op),
+			    ncr_algorithm_name(sess->algorithm), -1, NULL, 0,
+			    -1, NULL, 0);
+
+	if (sess->available_pages) {
+		release_user_pages(sess->pages, sess->available_pages);
+		sess->available_pages = 0;
+	}
+	up(&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);
+	}
+
+	_ncr_sessions_item_put(sess);
+	_ncr_session_remove(lists, op->ses);
+
+	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)
+{
+	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);
+	if (ret < 0) {
+		err();
+		goto fail;
+	}
+	
+	if (key->type != NCR_KEY_TYPE_SECRET) {
+		err();
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	switch(sess->op) {
+		case NCR_OP_ENCRYPT:
+		case NCR_OP_DECRYPT:
+			err();
+			ret = -EINVAL;
+			goto fail;
+		case NCR_OP_SIGN:
+		case NCR_OP_VERIFY:
+			ret = _cryptodev_hash_update(&sess->hash, 
+				key->key.secret.data, key->key.secret.size);
+			if (ret < 0) {
+				err();
+				goto fail;
+			}
+			break;
+		default:
+			err();
+			ret = -EINVAL;
+			goto fail;
+	}
+
+	ret = 0;
+
+fail:
+	audit_log_crypto_op(AUDIT_CRYPTO_OP_SESSION_OP, lists->id, sess->desc,
+			    ncr_op_name(sess->op),
+			    ncr_algorithm_name(sess->algorithm),
+			    op->data.kdata.input,
+			    key != NULL ? key->key_id : NULL,
+			    key != NULL ? key->key_id_size : 0, -1, NULL, 0);
+
+	if (key) _ncr_key_item_put(key);
+	_ncr_sessions_item_put(sess);
+
+	return ret;
+}
+
+int ncr_session_update(struct ncr_lists* lists, void __user* arg)
+{
+	struct ncr_session_op_st op;
+	int ret;
+
+	if (unlikely(copy_from_user( &op, arg, sizeof(op)))) {
+		err();
+		return -EFAULT;
+	}
+
+	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);
+	else
+		ret = -EINVAL;
+
+	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)
+{
+	struct ncr_session_op_st op;
+	int ret;
+
+	if (unlikely(copy_from_user(&op, arg, sizeof(op)))) {
+		err();
+		return -EFAULT;
+	}
+
+	ret = _ncr_session_final(lists, &op);
+	if (unlikely(ret)) {
+		err();
+		return ret;
+	}
+
+	if (unlikely(copy_to_user(arg, &op, sizeof(op)))) {
+		err();
+		return -EFAULT;
+	}
+	return 0;
+}
+
+int ncr_session_once(struct ncr_lists* lists, void __user* arg)
+{
+	struct ncr_session_once_op_st kop;
+	int ret;
+
+	if (unlikely(copy_from_user(&kop, arg, sizeof(kop)))) {
+		err();
+		return -EFAULT;
+	}
+
+	ret = _ncr_session_init(lists, &kop.init);
+	if (ret < 0) {
+		err();
+		return ret;
+	}
+	kop.op.ses = kop.init.ses;
+
+	ret = _ncr_session_final(lists, &kop.op);
+	if (ret < 0) {
+		err();
+		return ret;
+	}
+
+	if (unlikely(copy_to_user(arg, &kop, sizeof(kop))))
+		return -EFAULT;
+	return 0;
+}
diff --git a/crypto/userspace/ncr.c b/crypto/userspace/ncr.c
new file mode 100644
index 0000000..43432d8
--- /dev/null
+++ b/crypto/userspace/ncr.c
@@ -0,0 +1,210 @@
+/*
+ * New driver for /dev/crypto device (aka CryptoDev)
+ *
+ * Copyright (c) 2010 Katholieke Universiteit Leuven
+ *
+ * Author: Nikos Mavrogiannopoulos <[email-masked]>
+ *
+ * This file is part of linux cryptodev.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/audit.h>
+#include <linux/crypto.h>
+#include <linux/cryptodev.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/highmem.h>
+#include <linux/random.h>
+#include <asm/uaccess.h>
+#include <asm/ioctl.h>
+#include <linux/scatterlist.h>
+#include <linux/cred.h>  
+#include <linux/capability.h>
+#include <linux/ncr.h>
+#include "ncr-int.h"
+#include <linux/workqueue.h>
+
+/* This is the master wrapping key for storage of keys
+ */
+struct key_item_st master_key;
+
+static struct mutex lists_ida_mutex;
+static DEFINE_IDA(lists_ida);
+
+struct ncr_lists *ncr_init_lists(void)
+{
+	struct ncr_lists *lst;
+
+	lst = kmalloc(sizeof(*lst), GFP_KERNEL);
+	if(!lst) {
+		err();
+		return NULL;
+	}
+
+	memset(lst, 0, sizeof(*lst));
+
+	mutex_init(&lists_ida_mutex);
+	mutex_lock(&lists_ida_mutex);
+	/* ida_pre_get() should preallocate enough, and, due to lists_ida_mutex,
+	   nobody else can use the preallocated data.  Therefore the loop
+	   recommended in idr_get_new() documentation is not necessary. */
+	if (ida_pre_get(&lists_ida, GFP_KERNEL) == 0 ||
+	    ida_get_new(&lists_ida, &lst->id) != 0) {
+		mutex_unlock(&lists_ida_mutex);
+		kfree(lst);
+		return NULL;
+	}
+	mutex_unlock(&lists_ida_mutex);
+
+	mutex_init(&lst->key_idr_mutex);
+	idr_init(&lst->key_idr);
+
+	mutex_init(&lst->session_idr_mutex);
+	idr_init(&lst->session_idr);
+
+	return lst;
+}
+
+void ncr_deinit_lists(struct ncr_lists *lst)
+{
+	if(lst) {
+		ncr_key_list_deinit(lst);
+		ncr_sessions_list_deinit(lst);
+
+		mutex_lock(&lists_ida_mutex);
+		ida_remove(&lists_ida, lst->id);
+		mutex_unlock(&lists_ida_mutex);
+
+		kfree(lst);
+	}
+}
+
+void ncr_master_key_reset(void)
+{
+	memset(&master_key, 0, sizeof(master_key));
+}
+
+static int ncr_master_key_set(void __user *arg)
+{
+struct ncr_master_key_st st;
+struct audit_buffer *ab;
+int ret;
+
+	/* This will also cause auditing of the syscall, including information
+	   about the process, and success/failure indication.  Note that on
+	   error the AUDIT_CRYPTO_STORAGE_KEY record will be empty. */
+	ab = audit_log_start(current->audit_context, GFP_KERNEL,
+			     AUDIT_CRYPTO_STORAGE_KEY);
+
+	if (current_euid() != 0 && !capable(CAP_SYS_ADMIN)) {
+		err();
+		ret = -EPERM;
+		goto end;
+	}
+
+	if (unlikely(copy_from_user(&st, arg, sizeof(st)))) {
+		err();
+		ret = -EFAULT;
+		goto end;
+	}
+
+	if (st.key_size > sizeof(master_key.key.secret.data)) {
+		err();
+		ret = -EINVAL;
+		goto end;
+	}
+
+	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");
+		ret = -EINVAL;
+		goto end;
+	}
+
+	if (master_key.type != NCR_KEY_TYPE_INVALID) {
+		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))) {
+		err();
+		ret = -EFAULT;
+		goto end;
+	}
+
+	dprintk(0, KERN_INFO, "Initializing master key.\n");
+	/* Not much we can reveal... */
+	audit_log_format(ab, "key_size=%u", (unsigned)st.key_size);
+
+	master_key.type = NCR_KEY_TYPE_SECRET;
+	master_key.key.secret.size = st.key_size;
+
+	ret = 0;
+
+end:
+	audit_log_end(ab);
+
+	return ret;
+}
+
+int
+ncr_ioctl(struct ncr_lists* lst, struct file *filp,
+		unsigned int cmd, unsigned long arg_)
+{
+	void __user *arg = (void __user *)arg_;
+
+	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;
+	}
+}
diff --git a/crypto/userspace/ncr-dh.c b/crypto/userspace/ncr-dh.c
new file mode 100644
index 0000000..c185158
--- /dev/null
+++ b/crypto/userspace/ncr-dh.c
@@ -0,0 +1,283 @@
+/*
+ * New driver for /dev/crypto device (aka CryptoDev)
+
+ * Copyright (c) 2010 Katholieke Universiteit Leuven
+ *
+ * Author: Nikos Mavrogiannopoulos <[email-masked]>
+ *
+ * This file is part of linux cryptodev.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/cryptodev.h>
+#include <linux/mm.h>
+#include <linux/ncr.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <asm/uaccess.h>
+#include <asm/ioctl.h>
+#include <linux/scatterlist.h>
+#include <ncr-int.h>
+#include <tomcrypt.h>
+#include <ncr-dh.h>
+
+void dh_free(dh_key * key)
+{
+	mp_clear_multi(&key->p, &key->g, &key->x, NULL);
+}
+
+int dh_import_params(dh_key * key, uint8_t * p, size_t p_size, uint8_t * g,
+		     size_t g_size)
+{
+	int ret;
+	int err;
+
+	if ((err =
+	     mp_init_multi(&key->p, &key->g, &key->x, &key->y,
+			   NULL)) != CRYPT_OK) {
+		err();
+		return -ENOMEM;
+	}
+
+	if ((err =
+	     mp_read_unsigned_bin(&key->p, (unsigned char *) p,
+				  p_size)) != CRYPT_OK) {
+		err();
+		ret = _ncr_tomerr(err);
+		goto fail;
+	}
+
+	if ((err =
+	     mp_read_unsigned_bin(&key->g, (unsigned char *) g,
+				  g_size)) != CRYPT_OK) {
+		err();
+		ret = _ncr_tomerr(err);
+		goto fail;
+	}
+
+	return 0;
+      fail:
+	mp_clear_multi(&key->p, &key->g, &key->x, &key->y, NULL);
+
+	return ret;
+}
+
+int dh_generate_key(dh_key * key)
+{
+	void *buf;
+	int size;
+	int err, ret;
+
+	size = mp_unsigned_bin_size(&key->p);
+	if (size == 0) {
+		err();
+		return -EINVAL;
+	}
+
+	buf = kmalloc(size, GFP_KERNEL);
+	if (buf == NULL) {
+		err();
+		return -ENOMEM;
+	}
+
+	get_random_bytes(buf, size);
+
+	if ((err = mp_read_unsigned_bin(&key->x, buf, size)) != CRYPT_OK) {
+		err();
+		ret = _ncr_tomerr(err);
+		goto fail;
+	}
+
+	err = mp_mod(&key->x, &key->p, &key->x);
+	if (err != CRYPT_OK) {
+		err();
+		ret = _ncr_tomerr(err);
+		goto fail;
+	}
+
+	key->type = PK_PRIVATE;
+
+	ret = 0;
+      fail:
+	kfree(buf);
+
+	return ret;
+
+}
+
+int dh_generate_public(dh_key * public, dh_key * private)
+{
+	int err, ret;
+
+	err =
+	    mp_exptmod(&private->g, &private->x, &private->p, &public->y);
+	if (err != CRYPT_OK) {
+		err();
+		ret = _ncr_tomerr(err);
+		goto fail;
+	}
+
+	public->type = PK_PUBLIC;
+
+	ret = 0;
+      fail:
+
+	return ret;
+}
+
+int dh_export(uint8_t * out, unsigned long * outlen, int type, dh_key * key)
+{
+	unsigned long zero = 0;
+	int err;
+
+	if (out == NULL || outlen == NULL || key == NULL) {
+		err();
+		return -EINVAL;
+	}
+
+	/* can we store the static header?  */
+	if (type == PK_PRIVATE && key->type != PK_PRIVATE) {
+		return -EINVAL;
+	}
+
+	if (type != PK_PUBLIC && type != PK_PRIVATE) {
+		return -EINVAL;
+	}
+
+	/* This encoding is different from the one in original
+	 * libtomcrypt. It uses a compatible encoding with gnutls
+	 * and openssl 
+	 */
+	if (type == PK_PRIVATE) {
+		err = der_encode_sequence_multi(out, outlen,
+					LTC_ASN1_SHORT_INTEGER, 1UL, &zero, 
+					LTC_ASN1_INTEGER, 1UL, &key->p, 
+					LTC_ASN1_INTEGER, 1UL, &key->g,
+					LTC_ASN1_INTEGER, 1UL, &key->x, 
+					LTC_ASN1_EOL, 0UL, NULL);
+	} else {
+		err = mp_unsigned_bin_size(&key->y);
+		if (err > *outlen) {
+			err();
+			return -EOVERFLOW;
+		}
+		
+		*outlen = err;
+
+		err = mp_to_unsigned_bin(&key->y, out);
+	}
+	
+	if (err != CRYPT_OK) {
+		err();
+		return _ncr_tomerr(err);
+	}
+	
+	return 0;
+}
+
+int dh_import(const uint8_t * in, size_t inlen, dh_key * key)
+{
+	int err;
+	unsigned long zero = 0;
+
+	if (in == NULL || key == NULL) {
+		err();
+		return -EINVAL;
+	}
+
+	/* init key */
+	if (mp_init_multi
+	    (&key->p, &key->g, &key->x, &key->y, NULL) != CRYPT_OK) {
+		return -ENOMEM;
+	}
+
+	/* get key type */
+	if ((err = der_decode_sequence_multi(in, inlen,
+				LTC_ASN1_SHORT_INTEGER, 1UL, &zero, 
+				LTC_ASN1_INTEGER, 1UL, &key->p, 
+				LTC_ASN1_INTEGER, 1UL, &key->g,
+				LTC_ASN1_INTEGER, 1UL, &key->x,
+				LTC_ASN1_EOL, 0UL, NULL)) == CRYPT_OK) {
+		key->type = PK_PRIVATE;
+	} else {		/* public */
+		err = mp_read_unsigned_bin(&key->y, in, inlen);
+		key->type = PK_PUBLIC;
+	}
+
+	if (err != CRYPT_OK) {
+		goto LBL_ERR;
+	}
+
+	return 0;
+
+      LBL_ERR:
+	mp_clear_multi(&key->p, &key->g, &key->x, &key->y, NULL);
+	return _ncr_tomerr(err);
+}
+
+int dh_derive_gxy(struct key_item_st* newkey, dh_key * key,
+	void* pk, size_t pk_size)
+{
+int ret, err;
+mp_int y, gxy;
+	/* newkey will be a secret key with value of g^{xy}
+	 */
+	
+	if (mp_init_multi(&y, &gxy, NULL) != CRYPT_OK) {
+		err();
+		return -ENOMEM;
+	}
+	
+	if (key->type != PK_PRIVATE) {
+		err();
+		return -EINVAL;
+	}
+
+	if ((err=mp_read_unsigned_bin(&y, pk, pk_size)) != CRYPT_OK) {
+		err();
+		ret = _ncr_tomerr(err);
+		goto fail;
+	}
+	
+	if ((err=mp_exptmod(&y, &key->x, &key->p, &gxy))!= CRYPT_OK) {
+		err();
+		ret = _ncr_tomerr(err);
+		goto fail;
+	}
+	
+	err = mp_unsigned_bin_size(&gxy);
+	if (err > NCR_CIPHER_MAX_KEY_LEN) {
+		err();
+		ret = -EOVERFLOW;
+		goto fail;
+	}
+	newkey->key.secret.size = err;
+
+	err = mp_to_unsigned_bin(&gxy, newkey->key.secret.data);
+	if (err != CRYPT_OK) {
+		err();
+		ret = _ncr_tomerr(err);
+		goto fail;
+	}
+
+	newkey->type = NCR_KEY_TYPE_SECRET;
+
+	ret = 0;
+fail:
+	mp_clear_multi(&y, &gxy, NULL);
+	
+	return ret;
+}	
diff --git a/crypto/userspace/ncr-dh.h b/crypto/userspace/ncr-dh.h
new file mode 100644
index 0000000..cc45d32
--- /dev/null
+++ b/crypto/userspace/ncr-dh.h
@@ -0,0 +1,25 @@
+#ifndef NCR_DH_H
+# define NCR_DH_H
+
+#include <tomcrypt.h>
+
+typedef struct {
+	int type; /* PK_PRIVATE or PK_PUBLIC */
+	mp_int p;
+	mp_int g;
+	mp_int x; /* private */
+	mp_int y; /* public: y=g^x */
+} dh_key;
+
+int dh_generate_key(dh_key * key);
+int dh_import_params(dh_key * key, uint8_t* p, size_t p_size, uint8_t* g, size_t g_size);
+void dh_free(dh_key * key);
+int dh_generate_public(dh_key * public, dh_key* private);
+
+int dh_export(uint8_t *out, unsigned long *outlen, int type, dh_key *key);
+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);
+
+#endif