
From: Alasdair G Kergon <agk@redhat.com>

Create crypt_iv_operations structure with generator method and move the
plain iv generator into this structure.  Optionally accept an extended
<sector format> syntax: <cipher>-<chaining mode>-<iv mode>

This also makes it ready to support chaining modes other than CBC 
mode, such as CMC (not implemented in cryptoapi yet), 
The problems outlined by Adam J. Richter in
  http://article.gmane.org/gmane.linux.kernel.device-mapper.dm-crypt/454
would be fixed by switching to CMC chaining mode. 

Example of a valid target line:
  0 200 crypt aes-cbc-plain 00000000000000000000000000000000 0 /dev/loop/5 0
 
Signed-Off-By: Alasdair G Kergon <agk@redhat.com>
Signed-Off-By: Christophe Saout <christophe@saout.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/drivers/md/dm-crypt.c |  149 +++++++++++++++++++++++++++++++-----------
 1 files changed, 112 insertions(+), 37 deletions(-)

diff -puN drivers/md/dm-crypt.c~1-2-device-mapper-dm-crypt-generator-extension drivers/md/dm-crypt.c
--- 25/drivers/md/dm-crypt.c~1-2-device-mapper-dm-crypt-generator-extension	Wed Oct 20 15:10:46 2004
+++ 25-akpm/drivers/md/dm-crypt.c	Wed Oct 20 15:10:46 2004
@@ -46,6 +46,16 @@ struct convert_context {
 	int write;
 };
 
+struct crypt_config;
+
+struct crypt_iv_operations {
+	int (*ctr)(struct crypt_config *cc, struct dm_target *ti,
+	           const char *opts);
+	void (*dtr)(struct crypt_config *cc);
+	const char *(*status)(struct crypt_config *cc);
+	int (*generator)(struct crypt_config *cc, u8 *iv, sector_t sector);
+};
+
 /*
  * Crypt: maps a linear range of a block device
  * and encrypts / decrypts at the same time.
@@ -64,7 +74,9 @@ struct crypt_config {
 	/*
 	 * crypto related data
 	 */
-	int (*iv_generator)(struct crypt_config *cc, u8 *iv, sector_t sector);
+	struct crypt_iv_operations *iv_gen_ops;
+	char *iv_mode;
+	void *iv_gen_private;
 	sector_t iv_offset;
 	unsigned int iv_size;
 
@@ -94,9 +106,13 @@ static void mempool_free_page(void *page
 
 
 /*
- * Different IV generation algorithms
+ * Different IV generation algorithms:
+ *
+ * plain: the initial vector is the 32-bit low-endian version of the sector
+ *        number, padded with zeros if neccessary.
  */
-static int crypt_iv_plain(struct crypt_config *cc, u8 *iv, sector_t sector)
+
+static int crypt_iv_plain_gen(struct crypt_config *cc, u8 *iv, sector_t sector)
 {
 	memset(iv, 0, cc->iv_size);
 	*(u32 *)iv = cpu_to_le32(sector & 0xffffffff);
@@ -104,6 +120,11 @@ static int crypt_iv_plain(struct crypt_c
 	return 0;
 }
 
+static struct crypt_iv_operations crypt_iv_plain_ops = {
+	.generator = crypt_iv_plain_gen
+};
+
+
 static inline int
 crypt_convert_scatterlist(struct crypt_config *cc, struct scatterlist *out,
                           struct scatterlist *in, unsigned int length,
@@ -112,8 +133,8 @@ crypt_convert_scatterlist(struct crypt_c
 	u8 iv[cc->iv_size];
 	int r;
 
-	if (cc->iv_generator) {
-		r = cc->iv_generator(cc, iv, sector);
+	if (cc->iv_gen_ops) {
+		r = cc->iv_gen_ops->generator(cc, iv, sector);
 		if (r < 0)
 			return r;
 
@@ -412,7 +433,9 @@ static int crypt_ctr(struct dm_target *t
 	struct crypto_tfm *tfm;
 	char *tmp;
 	char *cipher;
-	char *mode;
+	char *chainmode;
+	char *ivmode;
+	char *ivopts;
 	unsigned int crypto_flags;
 	unsigned int key_size;
 
@@ -423,7 +446,9 @@ static int crypt_ctr(struct dm_target *t
 
 	tmp = argv[0];
 	cipher = strsep(&tmp, "-");
-	mode = strsep(&tmp, "-");
+	chainmode = strsep(&tmp, "-");
+	ivopts = strsep(&tmp, "-");
+	ivmode = strsep(&ivopts, ":");
 
 	if (tmp)
 		DMWARN(PFX "Unexpected additional cipher options");
@@ -437,19 +462,33 @@ static int crypt_ctr(struct dm_target *t
 		return -ENOMEM;
 	}
 
-	if (!mode || strcmp(mode, "plain") == 0)
-		cc->iv_generator = crypt_iv_plain;
-	else if (strcmp(mode, "ecb") == 0)
-		cc->iv_generator = NULL;
-	else {
-		ti->error = PFX "Invalid chaining mode";
+	cc->key_size = key_size;
+	if ((key_size == 0 && strcmp(argv[1], "-") != 0)
+	    || crypt_decode_key(cc->key, argv[1], key_size) < 0) {
+		ti->error = PFX "Error decoding key";
 		goto bad1;
 	}
 
-	if (cc->iv_generator)
+	/* Compatiblity mode for old dm-crypt cipher strings */
+	if (!chainmode || (strcmp(chainmode, "plain") == 0 && !ivmode)) {
+		chainmode = "cbc";
+		ivmode = "plain";
+	}
+
+	/* Choose crypto_flags according to chainmode */
+	if (strcmp(chainmode, "cbc") == 0)
 		crypto_flags = CRYPTO_TFM_MODE_CBC;
-	else
+	else if (strcmp(chainmode, "ecb") == 0)
 		crypto_flags = CRYPTO_TFM_MODE_ECB;
+	else {
+		ti->error = PFX "Unknown chaining mode";
+		goto bad1;
+	}
+
+	if (crypto_flags != CRYPTO_TFM_MODE_ECB && !ivmode) {
+		ti->error = PFX "This chaining mode requires an IV mechanism";
+		goto bad1;
+	}
 
 	tfm = crypto_alloc_tfm(cipher, crypto_flags);
 	if (!tfm) {
@@ -461,15 +500,37 @@ static int crypt_ctr(struct dm_target *t
 		goto bad2;
 	}
 
+	cc->tfm = tfm;
+
+	/*
+	 * Choose ivmode. Valid modes: "plain"
+	 * See comments at iv code
+	 */
+
+	if (ivmode == NULL)
+		cc->iv_gen_ops = NULL;
+	else if (strcmp(ivmode, "plain") == 0)
+		cc->iv_gen_ops = &crypt_iv_plain_ops;
+	else {
+		ti->error = PFX "Invalid IV mode";
+		goto bad2;
+	}
+
+	if (cc->iv_gen_ops && cc->iv_gen_ops->ctr &&
+	    cc->iv_gen_ops->ctr(cc, ti, ivopts) < 0)
+		goto bad2;
+
 	if (tfm->crt_cipher.cit_decrypt_iv && tfm->crt_cipher.cit_encrypt_iv)
 		/* at least a 32 bit sector number should fit in our buffer */
 		cc->iv_size = max(crypto_tfm_alg_ivsize(tfm),
 		                  (unsigned int)(sizeof(u32) / sizeof(u8)));
 	else {
 		cc->iv_size = 0;
-		if (cc->iv_generator) {
+		if (cc->iv_gen_ops) {
 			DMWARN(PFX "Selected cipher does not support IVs");
-			cc->iv_generator = NULL;
+			if (cc->iv_gen_ops->dtr)
+				cc->iv_gen_ops->dtr(cc);
+			cc->iv_gen_ops = NULL;
 		}
 	}
 
@@ -477,52 +538,59 @@ static int crypt_ctr(struct dm_target *t
 				     mempool_free_slab, _crypt_io_pool);
 	if (!cc->io_pool) {
 		ti->error = PFX "Cannot allocate crypt io mempool";
-		goto bad2;
+		goto bad3;
 	}
 
 	cc->page_pool = mempool_create(MIN_POOL_PAGES, mempool_alloc_page,
 				       mempool_free_page, NULL);
 	if (!cc->page_pool) {
 		ti->error = PFX "Cannot allocate page mempool";
-		goto bad3;
-	}
-
-	cc->tfm = tfm;
-	cc->key_size = key_size;
-	if ((key_size == 0 && strcmp(argv[1], "-") != 0)
-	    || crypt_decode_key(cc->key, argv[1], key_size) < 0) {
-		ti->error = PFX "Error decoding key";
 		goto bad4;
 	}
 
 	if (tfm->crt_cipher.cit_setkey(tfm, cc->key, key_size) < 0) {
 		ti->error = PFX "Error setting key";
-		goto bad4;
+		goto bad5;
 	}
 
 	if (sscanf(argv[2], SECTOR_FORMAT, &cc->iv_offset) != 1) {
 		ti->error = PFX "Invalid iv_offset sector";
-		goto bad4;
+		goto bad5;
 	}
 
 	if (sscanf(argv[4], SECTOR_FORMAT, &cc->start) != 1) {
 		ti->error = PFX "Invalid device sector";
-		goto bad4;
+		goto bad5;
 	}
 
 	if (dm_get_device(ti, argv[3], cc->start, ti->len,
 	                  dm_table_get_mode(ti->table), &cc->dev)) {
 		ti->error = PFX "Device lookup failed";
-		goto bad4;
+		goto bad5;
 	}
 
+	if (ivmode && cc->iv_gen_ops) {
+		if (ivopts)
+			*(ivopts - 1) = ':';
+		cc->iv_mode = kmalloc(strlen(ivmode) + 1, GFP_KERNEL);
+		if (!cc->iv_mode) {
+			ti->error = PFX "Error kmallocing iv_mode string";
+			goto bad5;
+		}
+		strcpy(cc->iv_mode, ivmode);
+	} else
+		cc->iv_mode = NULL;
+
 	ti->private = cc;
 	return 0;
 
-bad4:
+bad5:
 	mempool_destroy(cc->page_pool);
-bad3:
+bad4:
 	mempool_destroy(cc->io_pool);
+bad3:
+	if (cc->iv_gen_ops && cc->iv_gen_ops->dtr)
+		cc->iv_gen_ops->dtr(cc);
 bad2:
 	crypto_free_tfm(tfm);
 bad1:
@@ -537,6 +605,10 @@ static void crypt_dtr(struct dm_target *
 	mempool_destroy(cc->page_pool);
 	mempool_destroy(cc->io_pool);
 
+	if (cc->iv_mode)
+		kfree(cc->iv_mode);
+	if (cc->iv_gen_ops && cc->iv_gen_ops->dtr)
+		cc->iv_gen_ops->dtr(cc);
 	crypto_free_tfm(cc->tfm);
 	dm_put_device(ti, cc->dev);
 	kfree(cc);
@@ -691,7 +763,7 @@ static int crypt_status(struct dm_target
 	struct crypt_config *cc = (struct crypt_config *) ti->private;
 	char buffer[32];
 	const char *cipher;
-	const char *mode = NULL;
+	const char *chainmode = NULL;
 	unsigned int sz = 0;
 
 	switch (type) {
@@ -704,16 +776,19 @@ static int crypt_status(struct dm_target
 
 		switch(cc->tfm->crt_cipher.cit_mode) {
 		case CRYPTO_TFM_MODE_CBC:
-			mode = "plain";
+			chainmode = "cbc";
 			break;
 		case CRYPTO_TFM_MODE_ECB:
-			mode = "ecb";
+			chainmode = "ecb";
 			break;
 		default:
 			BUG();
 		}
 
-		DMEMIT("%s-%s ", cipher, mode);
+		if (cc->iv_mode)
+			DMEMIT("%s-%s-%s ", cipher, chainmode, cc->iv_mode);
+		else
+			DMEMIT("%s-%s ", cipher, chainmode);
 
 		if (cc->key_size > 0) {
 			if ((maxlen - sz) < ((cc->key_size << 1) + 1))
@@ -737,7 +812,7 @@ static int crypt_status(struct dm_target
 
 static struct target_type crypt_target = {
 	.name   = "crypt",
-	.version= {1, 0, 0},
+	.version= {1, 1, 0},
 	.module = THIS_MODULE,
 	.ctr    = crypt_ctr,
 	.dtr    = crypt_dtr,
_
