From c7399401e14dd12e3bd325fd0113c5a8beb63017 Mon Sep 17 00:00:00 2001
From: Phil Sutter <phil.sutter@viprinet.com>
Date: Tue, 18 Jan 2011 13:18:29 +0100
Subject: [PATCH 127/291] cryptodev: allow

When the user has specified COP_FLAG_WRITE_IV in crypt_op.flags, the
updated IV will be written back to userspace. This is useful for
encryption of continuous data in several steps, without having to care
for each cipher's inerna.

Protecting this functionality by a flag allows for backwards
compatibility as well as using a read-only (i.e. const) buffer holding
the IV.

Signed-off-by: Nikos Mavrogiannopoulos <nmav@gnutls.org>
---
 drivers/staging/crypto/cryptodev/cryptodev.h      |  1 +
 drivers/staging/crypto/cryptodev/cryptodev_int.h  |  6 ++
 drivers/staging/crypto/cryptodev/cryptodev_main.c | 99 ++++++++++++++---------
 3 files changed, 66 insertions(+), 40 deletions(-)

diff --git a/drivers/staging/crypto/cryptodev/cryptodev.h b/drivers/staging/crypto/cryptodev/cryptodev.h
index c50505b84210..db4697c81669 100644
--- a/drivers/staging/crypto/cryptodev/cryptodev.h
+++ b/drivers/staging/crypto/cryptodev/cryptodev.h
@@ -120,6 +120,7 @@ struct session_info_op {
 #define COP_FLAG_NONE		(0 << 0) /* totally no flag */
 #define COP_FLAG_UPDATE		(1 << 0) /* multi-update hash mode */
 #define COP_FLAG_FINAL		(1 << 1) /* multi-update final hash mode */
+#define COP_FLAG_WRITE_IV	(1 << 2) /* update the IV during operation */
 
 /* Stuff for bignum arithmetic and public key
  * cryptography - not supported yet by linux
diff --git a/drivers/staging/crypto/cryptodev/cryptodev_int.h b/drivers/staging/crypto/cryptodev/cryptodev_int.h
index 4be66ecfbc94..d20c01e480b6 100644
--- a/drivers/staging/crypto/cryptodev/cryptodev_int.h
+++ b/drivers/staging/crypto/cryptodev/cryptodev_int.h
@@ -66,6 +66,12 @@ inline static void cryptodev_cipher_set_iv(struct cipher_data *cdata,
 	memcpy(cdata->async.iv, iv, min(iv_size, sizeof(cdata->async.iv)));
 }
 
+inline static void cryptodev_cipher_get_iv(struct cipher_data *cdata,
+				void *iv, size_t iv_size)
+{
+	memcpy(iv, cdata->async.iv, min(iv_size, sizeof(cdata->async.iv)));
+}
+
 /* hash stuff */
 struct hash_data {
 	int init; /* 0 uninitialized */
diff --git a/drivers/staging/crypto/cryptodev/cryptodev_main.c b/drivers/staging/crypto/cryptodev/cryptodev_main.c
index 871690b89071..cb81107c1195 100644
--- a/drivers/staging/crypto/cryptodev/cryptodev_main.c
+++ b/drivers/staging/crypto/cryptodev/cryptodev_main.c
@@ -738,6 +738,11 @@ static int crypto_run(struct fcrypt *fcr, struct kernel_crypt_op *kcop)
 			goto out_unlock;
 	}
 
+	if (ses_ptr->cdata.init != 0) {
+		cryptodev_cipher_get_iv(&ses_ptr->cdata, kcop->iv,
+				min(ses_ptr->cdata.ivsize, kcop->ivlen));
+	}
+
 	if (ses_ptr->hdata.init != 0 &&
 		((cop->flags & COP_FLAG_FINAL) ||
 		   (!(cop->flags & COP_FLAG_UPDATE) || cop->len == 0))) {
@@ -999,6 +1004,26 @@ static int fill_kcop_from_cop(struct kernel_crypt_op *kcop, struct fcrypt *fcr)
 	return 0;
 }
 
+/* this function has to be called from process context */
+static int fill_cop_from_kcop(struct kernel_crypt_op *kcop, struct fcrypt *fcr)
+{
+	int ret;
+
+	if (kcop->digestsize) {
+		ret = copy_to_user(kcop->cop.mac,
+				kcop->hash_output, kcop->digestsize);
+		if (unlikely(ret))
+			return -EFAULT;
+	}
+	if (kcop->ivlen && kcop->cop.flags & COP_FLAG_WRITE_IV) {
+		ret = copy_to_user(kcop->cop.iv,
+				kcop->iv, kcop->ivlen);
+		if (unlikely(ret))
+			return -EFAULT;
+	}
+	return 0;
+}
+
 static int kcop_from_user(struct kernel_crypt_op *kcop,
 			struct fcrypt *fcr, void __user *arg)
 {
@@ -1008,6 +1033,20 @@ static int kcop_from_user(struct kernel_crypt_op *kcop,
 	return fill_kcop_from_cop(kcop, fcr);
 }
 
+static int kcop_to_user(struct kernel_crypt_op *kcop,
+			struct fcrypt *fcr, void __user *arg)
+{
+	int ret;
+
+	ret = fill_cop_from_kcop(kcop, fcr);
+	if (unlikely(ret))
+		return ret;
+
+	if (unlikely(copy_to_user(arg, &kcop->cop, sizeof(kcop->cop))))
+		return -EFAULT;
+	return 0;
+}
+
 static inline void tfm_info_to_alg_info(struct alg_info *dst, struct crypto_tfm *tfm)
 {
 	snprintf(dst->cra_name, CRYPTODEV_MAX_ALG_NAME,
@@ -1106,15 +1145,7 @@ cryptodev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg_)
 		if (unlikely(ret))
 			return ret;
 
-		if (kcop.digestsize) {
-			ret = copy_to_user(kcop.cop.mac,
-					kcop.hash_output, kcop.digestsize);
-			if (unlikely(ret))
-				return -EFAULT;
-		}
-		if (unlikely(copy_to_user(arg, &kcop.cop, sizeof(kcop.cop))))
-			return -EFAULT;
-		return 0;
+		return kcop_to_user(&kcop, fcr, arg);
 	case CIOCASYNCCRYPT:
 		if (unlikely(ret = kcop_from_user(&kcop, fcr, arg)))
 			return ret;
@@ -1125,15 +1156,7 @@ cryptodev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg_)
 		if (unlikely(ret))
 			return ret;
 
-		if (kcop.digestsize) {
-			ret = copy_to_user(kcop.cop.mac,
-					kcop.hash_output, kcop.digestsize);
-			if (unlikely(ret))
-				return -EFAULT;
-		}
-
-		return copy_to_user(arg, &kcop.cop, sizeof(kcop.cop));
-
+		return kcop_to_user(&kcop, fcr, arg);
 	default:
 		return -EINVAL;
 	}
@@ -1208,6 +1231,22 @@ static int compat_kcop_from_user(struct kernel_crypt_op *kcop,
 	return fill_kcop_from_cop(kcop, fcr);
 }
 
+static int compat_kcop_to_user(struct kernel_crypt_op *kcop,
+                                 struct fcrypt *fcr, void __user *arg)
+{
+	int ret;
+	struct compat_crypt_op compat_cop;
+
+	ret = fill_cop_from_kcop(kcop, fcr);
+	if (unlikely(ret))
+		return ret;
+	crypt_op_to_compat(&kcop->cop, &compat_cop);
+
+	if (unlikely(copy_to_user(arg, &compat_cop, sizeof(compat_cop))))
+		return -EFAULT;
+	return 0;
+}
+
 static long
 cryptodev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg_)
 {
@@ -1217,7 +1256,6 @@ cryptodev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg_)
 	struct session_op sop;
 	struct compat_session_op compat_sop;
 	struct kernel_crypt_op kcop;
-	struct compat_crypt_op compat_cop;
 	int ret;
 
 	if (unlikely(!pcr))
@@ -1258,18 +1296,7 @@ cryptodev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg_)
 		if (unlikely(ret))
 			return ret;
 
-		if (kcop.digestsize) {
-			ret = copy_to_user(kcop.cop.mac,
-					kcop.hash_output, kcop.digestsize);
-			if (unlikely(ret))
-				return -EFAULT;
-		}
-
-		crypt_op_to_compat(&kcop.cop, &compat_cop);
-		if (unlikely(copy_to_user(arg, &compat_cop,
-					  sizeof(compat_cop))))
-			return -EFAULT;
-		return 0;
+		return compat_kcop_to_user(&kcop, fcr, arg);
 	case COMPAT_CIOCASYNCCRYPT:
 		if (unlikely(ret = compat_kcop_from_user(&kcop, fcr, arg)))
 			return ret;
@@ -1280,15 +1307,7 @@ cryptodev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg_)
 		if (unlikely(ret))
 			return ret;
 
-		if (kcop.digestsize) {
-			ret = copy_to_user(kcop.cop.mac,
-					kcop.hash_output, kcop.digestsize);
-			if (unlikely(ret))
-				return -EFAULT;
-		}
-
-		crypt_op_to_compat(&kcop.cop, &compat_cop);
-		return copy_to_user(arg, &compat_cop, sizeof(compat_cop));
+		return compat_kcop_to_user(&kcop, fcr, arg);
 
 	default:
 		return -EINVAL;
-- 
2.1.0

