--- linux-2.6.8.orig/drivers/md/dm-crypt.c	2004-08-14 07:36:11.000000000 +0200
+++ linux-2.6.9-therpre/drivers/md/dm-crypt.c	2004-10-03 12:09:32.092391168 +0200
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2003 Christophe Saout <christophe@saout.de>
+ * Copyright (C) 2004 Clemens Fruhwirth <clemens@endorphin.org>
  *
  * This file is released under the GPL.
  */
@@ -15,6 +16,7 @@
 #include <linux/workqueue.h>
 #include <asm/atomic.h>
 #include <asm/scatterlist.h>
+#include <asm/page.h>
 
 #include "dm.h"
 
@@ -67,6 +69,7 @@
 	struct crypto_tfm *tfm;
 	sector_t iv_offset;
 	int (*iv_generator)(struct crypt_config *cc, u8 *iv, sector_t sector);
+	void *iv_gen_private;
 	int iv_size;
 	int key_size;
 	u8 key[0];
@@ -93,14 +96,39 @@
 
 
 /*
- * Different IV generation algorithms
+ * Different IV generation algorithms:
+ *
+ * plain: the initial vector is the 32-bit low-endien version of the sector
+ *        number, padded with zeros if neccessary.
+ *
+ * ess_iv: "encrypted sector|salt initial vector", the sector number is 
+ *         encrypted with the bulk cipher using a salt as key. The salt
+ *         should be derived from the bulk cipher's key via hashing.
+ *
+ * plumb: unimplemented, see:
+ * http://article.gmane.org/gmane.linux.kernel.device-mapper.dm-crypt/454
  */
+
 static int crypt_iv_plain(struct crypt_config *cc, u8 *iv, sector_t sector)
 {
 	*(u32 *)iv = cpu_to_le32(sector & 0xffffffff);
 	if (cc->iv_size > sizeof(u32) / sizeof(u8))
 		memset(iv + (sizeof(u32) / sizeof(u8)), 0,
 		       cc->iv_size - (sizeof(u32) / sizeof(u8)));
+	return 0;
+}
+
+static int crypt_iv_essiv(struct crypt_config *cc, u8 *iv, sector_t sector)
+{
+	struct scatterlist sg = { NULL, };
+
+	memset(iv,0,cc->iv_size);
+	*(u64 *)iv = cpu_to_le64(sector);
+
+	sg.page = virt_to_page(iv);
+	sg.offset = offset_in_page(iv);
+	sg.length = cc->iv_size;
+	crypto_cipher_encrypt((struct crypto_tfm *)cc->iv_gen_private,&sg,&sg,cc->iv_size);
 
 	return 0;
 }
@@ -414,9 +442,11 @@
 	struct crypto_tfm *tfm;
 	char *tmp;
 	char *cipher;
-	char *mode;
+	char *chainmode;
+	char *ivmode;
 	int crypto_flags;
 	int key_size;
+	int need_iv;
 
 	if (argc != 5) {
 		ti->error = PFX "Not enough arguments";
@@ -425,7 +455,8 @@
 
 	tmp = argv[0];
 	cipher = strsep(&tmp, "-");
-	mode = strsep(&tmp, "-");
+	chainmode = strsep(&tmp, "-");
+	ivmode = strsep(&tmp, "-");
 
 	if (tmp)
 		DMWARN(PFX "Unexpected additional cipher options");
@@ -439,25 +470,127 @@
 		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) {
+		chainmode = "cbc";
+		ivmode = "plain";
+	}
+
+	/*
+	 * Choose crypto_flags according to chainmode
+	 */
+	if (strcmp(chainmode,"cbc") == 0) {
 		crypto_flags = CRYPTO_TFM_MODE_CBC;
-	else
+		need_iv = 1;
+	}
+	else if(strcmp(chainmode,"ecb") == 0) {
 		crypto_flags = CRYPTO_TFM_MODE_ECB;
+		need_iv = 0;
+	} 
+	else {
+		ti->error = PFX "Unknown chaining mode";
+		goto bad1;
+	}
 
 	tfm = crypto_alloc_tfm(cipher, crypto_flags);
 	if (!tfm) {
 		ti->error = PFX "Error allocating crypto tfm";
 		goto bad1;
 	}
+
+	/* 
+	 * Choose ivmode. Valid modes: "plain", "essiv:<esshash>". 
+	 * See comments at iv code
+	 */
+
+	if (!need_iv) {
+		cc->iv_generator = NULL;
+	}
+        else if (strcmp(ivmode,"plain") == 0) {
+		cc->iv_generator = crypt_iv_plain;
+	}
+	else if (strncmp(ivmode, "essiv:", 6) == 0) {
+		struct crypto_tfm *essiv_tfm;
+		struct crypto_tfm *hash_tfm;
+		char *salt;
+		int saltsize;
+		struct scatterlist sg;
+
+		/*
+		 * Forward error checking, if sector type == u64. In case this
+		 * gets ever changed in the kernel, so we don't bughunt for ages..
+		 */
+		if(sizeof(sector_t) != sizeof(u64)) {
+			printk(KERN_WARNING "internal code error in ESSIV mode in dm-crypt.c\n");
+			goto bad1;
+		}
+
+		strsep(&ivmode,":");	// iv mode points to hash string now
+		
+		/*
+		 * Hash the cipher key with the given hash algorithm
+		 */
+
+		hash_tfm = crypto_alloc_tfm(ivmode,0);
+		if (hash_tfm == NULL) {
+			ti->error = PFX "Error initializing ESSIV hash";
+			goto bad1;
+		}
+
+		saltsize = crypto_tfm_alg_digestsize(hash_tfm);
+		salt = kmalloc(saltsize,GFP_KERNEL);
+		if (salt == NULL) {
+			ti->error = PFX "Error kmallocing salt storage in ESSIV";
+			crypto_free_tfm(hash_tfm);
+			goto bad1;
+		}
+
+		sg.page = virt_to_page(cc->key);
+		sg.offset = offset_in_page(cc->key);
+		sg.length = cc->key_size;
+		crypto_digest_digest(hash_tfm,&sg,1,salt);
+		crypto_free_tfm(hash_tfm);
+	
+		/* 
+		 * Setup the essiv_tfm with the given salt
+		 */
+		essiv_tfm = crypto_alloc_tfm(cipher,CRYPTO_TFM_MODE_ECB);
+		if (essiv_tfm == NULL) {
+			ti->error = PFX "Error allocating crypto tfm for ESSIV";
+			kfree(salt);
+			goto bad1;
+		}
+		if (crypto_tfm_alg_blocksize(essiv_tfm) != crypto_tfm_alg_ivsize(tfm)) {
+			ti->error = PFX "Block size of ESSIV cipher does not match IV size of block cipher";
+			crypto_free_tfm(essiv_tfm);
+			kfree(salt);
+			goto bad1;
+		}
+		if (crypto_cipher_setkey(essiv_tfm,salt,saltsize) < 0) {
+			ti->error = PFX "Failed to set key for ESSIV cipher";
+			crypto_free_tfm(essiv_tfm);
+			kfree(salt);
+			goto bad1;
+		}
+			
+		kfree(salt);
+		cc->iv_gen_private = (void *)essiv_tfm;
+		cc->iv_generator = crypt_iv_essiv;
+	}
+	else {
+		ti->error = PFX "Invalid IV mode";
+		goto bad1;
+	}
+
 	if (crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER) {
 		ti->error = PFX "Expected cipher algorithm";
 		goto bad2;
@@ -490,12 +623,6 @@
 	}
 
 	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";
@@ -528,6 +655,8 @@
 bad2:
 	crypto_free_tfm(tfm);
 bad1:
+	if(cc->iv_generator == crypt_iv_essiv)
+		crypto_free_tfm((struct crypto_tfm *)cc->iv_gen_private);
 	kfree(cc);
 	return -EINVAL;
 }
@@ -540,6 +669,8 @@
 	mempool_destroy(cc->io_pool);
 
 	crypto_free_tfm(cc->tfm);
+	if(cc->iv_generator == crypt_iv_essiv)
+		crypto_free_tfm((struct crypto_tfm *)cc->iv_gen_private);
 	dm_put_device(ti, cc->dev);
 	kfree(cc);
 }
