All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/7] dm crypt fixes and loop-aes mode support
@ 2010-12-21 15:28 Milan Broz
  2010-12-21 15:28 ` [PATCH 1/7] dm crypt: fix per cpu struct dereference in destructor Milan Broz
                   ` (6 more replies)
  0 siblings, 7 replies; 8+ messages in thread
From: Milan Broz @ 2010-12-21 15:28 UTC (permalink / raw)
  To: dm-devel; +Cc: Milan Broz

Patch 1 and 2 are fixes (maybe fold it in per cpu patch)
others are preparation and changes for multikey support
(compatibility mode for loop-aes mappings).

Note: the patchset is based on top of dm devel queue,
http://www.kernel.org/pub/linux/kernel/people/agk/patches/2.6/editing/series.html
it requires per-cpu dmcrypt change

Milan Broz (7):
  dm crypt: fix per cpu struct dereference in destructor
  dm crypt: remove redundant setkey call
  dm crypt: set key size early
  dm crypt: remove unused compatible table output
  dm crypt: redefine IV generator function and add post IV call
  [RFC]dm crypt: add multi-key capability
  [RFC]dm crypt: add loop-aes LMK IV generator

 Documentation/device-mapper/dm-crypt.txt |    7 +-
 drivers/md/dm-crypt.c                    |  382 ++++++++++++++++++++++++------
 2 files changed, 314 insertions(+), 75 deletions(-)

-- 
1.7.2.3

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/7] dm crypt: fix per cpu struct dereference in destructor
  2010-12-21 15:28 [PATCH 0/7] dm crypt fixes and loop-aes mode support Milan Broz
@ 2010-12-21 15:28 ` Milan Broz
  2010-12-21 15:28 ` [PATCH 2/7] dm crypt: remove redundant setkey call Milan Broz
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Milan Broz @ 2010-12-21 15:28 UTC (permalink / raw)
  To: dm-devel; +Cc: Milan Broz

If destructor is called early (e.g. wrong cipher format)
the destructor tries to access nonexistent per cpu struct
and causes OOps.

Patch adds check for struct existence (and simplifies
tfm destructor as well).

(introduced in per cpu patch)

Signed-off-by: Milan Broz <mbroz@redhat.com>
---
 drivers/md/dm-crypt.c |   30 ++++++++----------------------
 1 files changed, 8 insertions(+), 22 deletions(-)

diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 98a7ad0..6aeb1fe 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -1088,7 +1088,6 @@ static int crypt_setkey_allcpus(struct crypt_config *cc)
 	return err;
 }
 
-
 static int crypt_set_key(struct crypt_config *cc, char *key)
 {
 	unsigned key_size = strlen(key) >> 1;
@@ -1114,20 +1113,6 @@ static int crypt_wipe_key(struct crypt_config *cc)
 	return crypt_setkey_allcpus(cc);
 }
 
-static void crypt_dtr_cpus(struct crypt_config *cc)
-{
-	struct crypt_cpu *cs;
-	int cpu;
-
-	for_each_possible_cpu(cpu) {
-		cs = per_cpu_ptr(cc->cpu, cpu);
-		if (cs->tfm) {
-			crypto_free_ablkcipher(cs->tfm);
-			cs->tfm = NULL;
-		}
-	}
-}
-
 static void crypt_dtr(struct dm_target *ti)
 {
 	struct crypt_config *cc = ti->private;
@@ -1144,11 +1129,14 @@ static void crypt_dtr(struct dm_target *ti)
 	if (cc->crypt_queue)
 		destroy_workqueue(cc->crypt_queue);
 
-	for_each_possible_cpu(cpu) {
-		cs = per_cpu_ptr(cc->cpu, cpu);
-		if (cs->req)
-			mempool_free(cs->req, cc->req_pool);
-	}
+	if (cc->cpu)
+		for_each_possible_cpu(cpu) {
+			cs = per_cpu_ptr(cc->cpu, cpu);
+			if (cs->req)
+				mempool_free(cs->req, cc->req_pool);
+			if (cs->tfm)
+				crypto_free_ablkcipher(cs->tfm);
+		}
 
 	if (cc->bs)
 		bioset_free(cc->bs);
@@ -1166,8 +1154,6 @@ static void crypt_dtr(struct dm_target *ti)
 	if (cc->dev)
 		dm_put_device(ti, cc->dev);
 
-	crypt_dtr_cpus(cc);
-
 	if (cc->cpu)
 		free_percpu(cc->cpu);
 
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/7] dm crypt: remove redundant setkey call
  2010-12-21 15:28 [PATCH 0/7] dm crypt fixes and loop-aes mode support Milan Broz
  2010-12-21 15:28 ` [PATCH 1/7] dm crypt: fix per cpu struct dereference in destructor Milan Broz
@ 2010-12-21 15:28 ` Milan Broz
  2010-12-21 15:28 ` [PATCH 3/7] dm crypt: set key size early Milan Broz
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Milan Broz @ 2010-12-21 15:28 UTC (permalink / raw)
  To: dm-devel; +Cc: Milan Broz

All keys are already set in crypt_ctr_cipher() call,
no need set the key again later.

(introduced in per cpu patch)

Signed-off-by: Milan Broz <mbroz@redhat.com>
---
 drivers/md/dm-crypt.c |   11 +----------
 1 files changed, 1 insertions(+), 10 deletions(-)

diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 6aeb1fe..dbed83d 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -1317,10 +1317,9 @@ bad_mem:
 static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 {
 	struct crypt_config *cc;
-	struct crypt_cpu *cs;
 	unsigned int key_size;
 	unsigned long long tmpll;
-	int ret, cpu;
+	int ret;
 
 	if (argc != 5) {
 		ti->error = "Not enough arguments";
@@ -1372,14 +1371,6 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 		goto bad;
 	}
 
-	for_each_possible_cpu(cpu) {
-		cs = per_cpu_ptr(cc->cpu, cpu);
-		if (crypto_ablkcipher_setkey(cs->tfm, cc->key, key_size) < 0) {
-			ti->error = "Error setting key";
-			goto bad;
-		}
-	}
-
 	ret = -EINVAL;
 	if (sscanf(argv[2], "%llu", &tmpll) != 1) {
 		ti->error = "Invalid iv_offset sector";
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 3/7] dm crypt: set key size early
  2010-12-21 15:28 [PATCH 0/7] dm crypt fixes and loop-aes mode support Milan Broz
  2010-12-21 15:28 ` [PATCH 1/7] dm crypt: fix per cpu struct dereference in destructor Milan Broz
  2010-12-21 15:28 ` [PATCH 2/7] dm crypt: remove redundant setkey call Milan Broz
@ 2010-12-21 15:28 ` Milan Broz
  2010-12-21 15:28 ` [PATCH 4/7] dm crypt: remove unused compatible table output Milan Broz
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Milan Broz @ 2010-12-21 15:28 UTC (permalink / raw)
  To: dm-devel; +Cc: Milan Broz

Simplify key size verification (hexa string) and
set key size early in constructor.

(The "-" string means no key - this is just
for compatibility reasons. Because of shift it properly
decodes to zero key size.)

(Patch required by later changes.)

Signed-off-by: Milan Broz <mbroz@redhat.com>
---
 drivers/md/dm-crypt.c |   11 +++++------
 1 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index dbed83d..5c04196 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -1090,15 +1090,13 @@ static int crypt_setkey_allcpus(struct crypt_config *cc)
 
 static int crypt_set_key(struct crypt_config *cc, char *key)
 {
-	unsigned key_size = strlen(key) >> 1;
-
-	if (cc->key_size && cc->key_size != key_size)
+	if (cc->key_size != (strlen(key) >> 1))
 		return -EINVAL;
 
-	cc->key_size = key_size; /* initial settings */
+	if (!cc->key_size && strcmp(key, "-"))
+		return -EINVAL;
 
-	if ((!key_size && strcmp(key, "-")) ||
-	   (key_size && crypt_decode_key(cc->key, key, key_size) < 0))
+	if (cc->key_size && crypt_decode_key(cc->key, key, cc->key_size) < 0)
 		return -EINVAL;
 
 	set_bit(DM_CRYPT_KEY_VALID, &cc->flags);
@@ -1333,6 +1331,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 		ti->error = "Cannot allocate encryption context";
 		return -ENOMEM;
 	}
+	cc->key_size = key_size;
 
 	ti->private = cc;
 	ret = crypt_ctr_cipher(ti, argv[0], argv[1]);
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 4/7] dm crypt: remove unused compatible table output
  2010-12-21 15:28 [PATCH 0/7] dm crypt fixes and loop-aes mode support Milan Broz
                   ` (2 preceding siblings ...)
  2010-12-21 15:28 ` [PATCH 3/7] dm crypt: set key size early Milan Broz
@ 2010-12-21 15:28 ` Milan Broz
  2010-12-21 15:28 ` [PATCH 5/7] dm crypt: redefine IV generator function and add post IV call Milan Broz
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Milan Broz @ 2010-12-21 15:28 UTC (permalink / raw)
  To: dm-devel; +Cc: Milan Broz

cc->cipher_mode is always set in constructor

dm-crypt still can use compatible mode without IV
or blockmode set but it is already internaly converted
to "cbc-plain" so we can remove unused table output.

(Also simplify tfm variable which will be removed later.)

Signed-off-by: Milan Broz <mbroz@redhat.com>
---
 drivers/md/dm-crypt.c |    8 ++------
 1 files changed, 2 insertions(+), 6 deletions(-)

diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 5c04196..863e412 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -1241,7 +1241,6 @@ static int crypt_ctr_cipher(struct dm_target *ti,
 		}
 		per_cpu_ptr(cc->cpu, cpu)->tfm = tfm;
  	}
-	tfm = any_tfm(cc);
 
 	/* Initialize and set key */
 	ret = crypt_set_key(cc, key);
@@ -1251,7 +1250,7 @@ static int crypt_ctr_cipher(struct dm_target *ti,
 	}
 
 	/* Initialize IV */
-	cc->iv_size = crypto_ablkcipher_ivsize(tfm);
+	cc->iv_size = crypto_ablkcipher_ivsize(any_tfm(cc));
 	if (cc->iv_size)
 		/* at least a 64 bit sector number should fit in our buffer */
 		cc->iv_size = max(cc->iv_size,
@@ -1451,10 +1450,7 @@ static int crypt_status(struct dm_target *ti, status_type_t type,
 		break;
 
 	case STATUSTYPE_TABLE:
-		if (cc->cipher_mode)
-			DMEMIT("%s-%s ", cc->cipher, cc->cipher_mode);
-		else
-			DMEMIT("%s ", cc->cipher);
+		DMEMIT("%s-%s ", cc->cipher, cc->cipher_mode);
 
 		if (cc->key_size > 0) {
 			if ((maxlen - sz) < ((cc->key_size << 1) + 1))
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 5/7] dm crypt: redefine IV generator function and add post IV call
  2010-12-21 15:28 [PATCH 0/7] dm crypt fixes and loop-aes mode support Milan Broz
                   ` (3 preceding siblings ...)
  2010-12-21 15:28 ` [PATCH 4/7] dm crypt: remove unused compatible table output Milan Broz
@ 2010-12-21 15:28 ` Milan Broz
  2010-12-21 15:28 ` [PATCH 6/7] [RFC]dm crypt: add multi-key capability Milan Broz
  2010-12-21 15:28 ` [PATCH 7/7] [RFC]dm crypt: add loop-aes LMK IV generator Milan Broz
  6 siblings, 0 replies; 8+ messages in thread
From: Milan Broz @ 2010-12-21 15:28 UTC (permalink / raw)
  To: dm-devel; +Cc: Milan Broz

IV (initialisation vector) can in principle depend not only
on sector but also on plaintext data (or other attributes).

Change IV generator interface to work directly with dmreq
structure to allow such dependence in generator.

Also add post() function which is called after the crypto
operation.

This allows tricky modification of decrypted data or IV
internals. (If not suitable directly in crypto API.)

In asynchronous mode the post() can be called after
ctx->sector count was increased so it is needed
to add iv_sector copy directly to dmreq structure.
(N.B. dmreq always include only one sector in scatterlists)

Signed-off-by: Milan Broz <mbroz@redhat.com>
---
 drivers/md/dm-crypt.c |   48 +++++++++++++++++++++++++++++++++++-------------
 1 files changed, 35 insertions(+), 13 deletions(-)

diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 863e412..451a743 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -64,6 +64,7 @@ struct dm_crypt_request {
 	struct convert_context *ctx;
 	struct scatterlist sg_in;
 	struct scatterlist sg_out;
+	sector_t iv_sector;
 };
 
 struct crypt_config;
@@ -74,7 +75,10 @@ struct crypt_iv_operations {
 	void (*dtr)(struct crypt_config *cc);
 	int (*init)(struct crypt_config *cc);
 	int (*wipe)(struct crypt_config *cc);
-	int (*generator)(struct crypt_config *cc, u8 *iv, sector_t sector);
+	int (*generator)(struct crypt_config *cc, u8 *iv,
+			 struct dm_crypt_request *dmreq);
+	int (*post)(struct crypt_config *cc, u8 *iv,
+		    struct dm_crypt_request *dmreq);
 };
 
 struct iv_essiv_private {
@@ -166,6 +170,7 @@ static struct kmem_cache *_crypt_io_pool;
 
 static void clone_init(struct dm_crypt_io *, struct bio *);
 static void kcryptd_queue_crypt(struct dm_crypt_io *io);
+static u8 *iv_of_dmreq(struct crypt_config *cc, struct dm_crypt_request *dmreq);
 
 static struct crypt_cpu *crypt_me(struct crypt_config *cc)
 {
@@ -204,19 +209,20 @@ static struct crypto_ablkcipher *any_tfm(struct crypt_config *cc)
  * http://article.gmane.org/gmane.linux.kernel.device-mapper.dm-crypt/454
  */
 
-static int crypt_iv_plain_gen(struct crypt_config *cc, u8 *iv, sector_t sector)
+static int crypt_iv_plain_gen(struct crypt_config *cc, u8 *iv,
+			      struct dm_crypt_request *dmreq)
 {
 	memset(iv, 0, cc->iv_size);
-	*(u32 *)iv = cpu_to_le32(sector & 0xffffffff);
+	*(u32 *)iv = cpu_to_le32(dmreq->iv_sector & 0xffffffff);
 
 	return 0;
 }
 
 static int crypt_iv_plain64_gen(struct crypt_config *cc, u8 *iv,
-				sector_t sector)
+				struct dm_crypt_request *dmreq)
 {
 	memset(iv, 0, cc->iv_size);
-	*(u64 *)iv = cpu_to_le64(sector);
+	*(u64 *)iv = cpu_to_le64(dmreq->iv_sector);
 
 	return 0;
 }
@@ -379,12 +385,13 @@ bad:
 	return err;
 }
 
-static int crypt_iv_essiv_gen(struct crypt_config *cc, u8 *iv, sector_t sector)
+static int crypt_iv_essiv_gen(struct crypt_config *cc, u8 *iv,
+			      struct dm_crypt_request *dmreq)
 {
 	struct crypto_cipher *essiv_tfm = crypt_me(cc)->iv_private;
 
 	memset(iv, 0, cc->iv_size);
-	*(u64 *)iv = cpu_to_le64(sector);
+	*(u64 *)iv = cpu_to_le64(dmreq->iv_sector);
 	crypto_cipher_encrypt_one(essiv_tfm, iv, iv);
 	return 0;
 }
@@ -417,19 +424,21 @@ static void crypt_iv_benbi_dtr(struct crypt_config *cc)
 {
 }
 
-static int crypt_iv_benbi_gen(struct crypt_config *cc, u8 *iv, sector_t sector)
+static int crypt_iv_benbi_gen(struct crypt_config *cc, u8 *iv,
+			      struct dm_crypt_request *dmreq)
 {
 	__be64 val;
 
 	memset(iv, 0, cc->iv_size - sizeof(u64)); /* rest is cleared below */
 
-	val = cpu_to_be64(((u64)sector << cc->iv_gen_private.benbi.shift) + 1);
+	val = cpu_to_be64(((u64)dmreq->iv_sector << cc->iv_gen_private.benbi.shift) + 1);
 	put_unaligned(val, (__be64 *)(iv + cc->iv_size - sizeof(u64)));
 
 	return 0;
 }
 
-static int crypt_iv_null_gen(struct crypt_config *cc, u8 *iv, sector_t sector)
+static int crypt_iv_null_gen(struct crypt_config *cc, u8 *iv,
+			     struct dm_crypt_request *dmreq)
 {
 	memset(iv, 0, cc->iv_size);
 
@@ -489,6 +498,13 @@ static struct ablkcipher_request *req_of_dmreq(struct crypt_config *cc,
 	return (struct ablkcipher_request *)((char *)dmreq - cc->dmreq_start);
 }
 
+static u8 *iv_of_dmreq(struct crypt_config *cc,
+		       struct dm_crypt_request *dmreq)
+{
+	return (u8 *)ALIGN((unsigned long)(dmreq + 1),
+		crypto_ablkcipher_alignmask(any_tfm(cc)) + 1);
+}
+
 static int crypt_convert_block(struct crypt_config *cc,
 			       struct convert_context *ctx,
 			       struct ablkcipher_request *req)
@@ -500,9 +516,9 @@ static int crypt_convert_block(struct crypt_config *cc,
 	int r = 0;
 
 	dmreq = dmreq_of_req(cc, req);
-	iv = (u8 *)ALIGN((unsigned long)(dmreq + 1),
-			 crypto_ablkcipher_alignmask(any_tfm(cc)) + 1);
+	iv = iv_of_dmreq(cc, dmreq);
 
+	dmreq->iv_sector = ctx->sector;
 	dmreq->ctx = ctx;
 	sg_init_table(&dmreq->sg_in, 1);
 	sg_set_page(&dmreq->sg_in, bv_in->bv_page, 1 << SECTOR_SHIFT,
@@ -525,7 +541,7 @@ static int crypt_convert_block(struct crypt_config *cc,
 	}
 
 	if (cc->iv_gen_ops) {
-		r = cc->iv_gen_ops->generator(cc, iv, ctx->sector);
+		r = cc->iv_gen_ops->generator(cc, iv, dmreq);
 		if (r < 0)
 			return r;
 	}
@@ -538,6 +554,9 @@ static int crypt_convert_block(struct crypt_config *cc,
 	else
 		r = crypto_ablkcipher_decrypt(req);
 
+	if (!r && cc->iv_gen_ops && cc->iv_gen_ops->post)
+		r = cc->iv_gen_ops->post(cc, iv, dmreq);
+
 	return r;
 }
 
@@ -1003,6 +1022,9 @@ static void kcryptd_async_done(struct crypto_async_request *async_req,
 		return;
 	}
 
+	if (!error && cc->iv_gen_ops && cc->iv_gen_ops->post)
+		error = cc->iv_gen_ops->post(cc, iv_of_dmreq(cc, dmreq), dmreq);
+
 	mempool_free(req_of_dmreq(cc, dmreq), cc->req_pool);
 
 	if (!atomic_dec_and_test(&ctx->pending))
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 6/7] [RFC]dm crypt: add multi-key capability
  2010-12-21 15:28 [PATCH 0/7] dm crypt fixes and loop-aes mode support Milan Broz
                   ` (4 preceding siblings ...)
  2010-12-21 15:28 ` [PATCH 5/7] dm crypt: redefine IV generator function and add post IV call Milan Broz
@ 2010-12-21 15:28 ` Milan Broz
  2010-12-21 15:28 ` [PATCH 7/7] [RFC]dm crypt: add loop-aes LMK IV generator Milan Broz
  6 siblings, 0 replies; 8+ messages in thread
From: Milan Broz @ 2010-12-21 15:28 UTC (permalink / raw)
  To: dm-devel; +Cc: Max Vozeler, Milan Broz

For supporting multikey mapping we need some way
how to setup more keys in mapping table.

This patch extends mapping table to optional keycount and
implements generic multi-key capability.

With more keys defined the <key> string is divided into
several <keycount> sections and these are used for the tfms.

The tfm is used according to sector offset
(sector 0->tfm[0], sector 1->tfm[1], sector N->tfm[N modulo keycount])
(only power of two values supported for keycount here).

(N.B. This capability is there only to allow legacy support
of loop-aes encryption scheme designed by Jari Ruusu.

There is no third party analysis that multikey scheme increases
security of system in comparison to using just one key and secure
block mode and appropriate IV generator.)

Because of tfms per-cpu allocation, this mode can be take
a lot of memory on large smp systems.

Signed-off-by: Milan Broz <mbroz@redhat.com>
CC: Max Vozeler <max@hinterhof.net>
---
 Documentation/device-mapper/dm-crypt.txt |    7 ++-
 drivers/md/dm-crypt.c                    |  100 +++++++++++++++++++++++------
 2 files changed, 85 insertions(+), 22 deletions(-)

diff --git a/Documentation/device-mapper/dm-crypt.txt b/Documentation/device-mapper/dm-crypt.txt
index 524de92..2bee19f 100644
--- a/Documentation/device-mapper/dm-crypt.txt
+++ b/Documentation/device-mapper/dm-crypt.txt
@@ -8,7 +8,7 @@ Parameters: <cipher> <key> <iv_offset> <device path> <offset>
 
 <cipher>
     Encryption cipher and an optional IV generation mode.
-    (In format cipher-chainmode-ivopts:ivmode).
+    (In format cipher[:keycount]-chainmode-ivopts:ivmode).
     Examples:
        des
        aes-cbc-essiv:sha256
@@ -20,6 +20,11 @@ Parameters: <cipher> <key> <iv_offset> <device path> <offset>
     Key used for encryption. It is encoded as a hexadecimal number.
     You can only use key sizes that are valid for the selected cipher.
 
+<keycount>
+    Multi-key compatibility mode - you can define different keys and
+    the sectors are according its offset (sector 0 -> key0, 1 -> key1 etc).
+    Keycount must power of two.
+
 <iv_offset>
     The IV offset is a sector count that is added to the sector number
     before creating the IV.
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 451a743..3b9d3a0 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -99,10 +99,9 @@ enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID };
 /* Duplicated per CPU state for cipher */
 struct crypt_cpu {
 	struct ablkcipher_request *req;
-	struct crypto_ablkcipher *tfm;
-
 	/* ESSIV: struct crypto_cipher *essiv_tfm */
 	void *iv_private;
+	struct crypto_ablkcipher *tfms[0];
 };
 
 /*
@@ -141,6 +140,7 @@ struct crypt_config {
 	 * per_cpu_ptr() only.
 	 */
 	struct crypt_cpu __percpu *cpu;
+	unsigned int tfms_count;
 
 	/*
 	 * Layout of each crypto request:
@@ -159,6 +159,7 @@ struct crypt_config {
 
 	unsigned long flags;
 	unsigned int key_size;
+	unsigned int key_parts;
 	u8 key[0];
 };
 
@@ -182,7 +183,7 @@ static struct crypto_ablkcipher *any_tfm(struct crypt_config *cc)
 {
 	struct crypto_ablkcipher *tfm;
 	/* cpu doesn't matter, output is always the same */
-	tfm = __this_cpu_ptr(cc->cpu)->tfm;
+	tfm = __this_cpu_ptr(cc->cpu)->tfms[0];
 	return tfm;
 }
 
@@ -562,13 +563,23 @@ static int crypt_convert_block(struct crypt_config *cc,
 
 static void kcryptd_async_done(struct crypto_async_request *async_req,
 			       int error);
+static struct crypto_ablkcipher *crypt_tfm(struct crypt_config *cc,
+					   struct crypt_cpu *cs,
+					   struct convert_context *ctx)
+{
+	if (cc->tfms_count == 1)
+		return cs->tfms[0];
+	else
+		return cs->tfms[(ctx->sector & (cc->tfms_count - 1))];
+}
+
 static void crypt_alloc_req(struct crypt_config *cc,
 			    struct convert_context *ctx)
 {
 	struct crypt_cpu *cs = crypt_me(cc);
 	if (!cs->req)
 		cs->req = mempool_alloc(cc->req_pool, GFP_NOIO);
-	ablkcipher_request_set_tfm(cs->req, cs->tfm);
+	ablkcipher_request_set_tfm(cs->req, crypt_tfm(cc, cs, ctx));
 	ablkcipher_request_set_callback(cs->req, CRYPTO_TFM_REQ_MAY_BACKLOG |
 					CRYPTO_TFM_REQ_MAY_SLEEP,
 					kcryptd_async_done,
@@ -1095,17 +1106,50 @@ static void crypt_encode_key(char *hex, u8 *key, unsigned int size)
 	}
 }
 
+static void crypt_free_tfms(struct crypt_config *cc, int cpu)
+{
+	struct crypt_cpu *cs = per_cpu_ptr(cc->cpu, cpu);
+	int i;
+
+	for (i = 0; i < cc->tfms_count; i++)
+		if (cs->tfms[i] && !IS_ERR(cs->tfms[i])) {
+			crypto_free_ablkcipher(cs->tfms[i]);
+			cs->tfms[i] = NULL;
+		}
+}
+
+static int crypt_alloc_tfms(struct crypt_config *cc, int cpu, char *ciphermode)
+{
+	struct crypt_cpu *cs = per_cpu_ptr(cc->cpu, cpu);
+	int i, err;
+
+	for (i = 0; i < cc->tfms_count; i++) {
+		cs->tfms[i] = crypto_alloc_ablkcipher(ciphermode, 0, 0);
+		if (IS_ERR(cs->tfms[i])) {
+			err = PTR_ERR(cs->tfms[i]);
+			crypt_free_tfms(cc, cpu);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
 static int crypt_setkey_allcpus(struct crypt_config *cc)
 {
 	struct crypt_cpu *cs;
-	int cpu, n, err;
+	int cpu, i, n, err;
+	unsigned subkey_size = cc->key_size / cc->tfms_count;
 
 	err = 0;
 	for_each_possible_cpu(cpu) {
 		cs = per_cpu_ptr(cc->cpu, cpu);
-		n = crypto_ablkcipher_setkey(cs->tfm, cc->key, cc->key_size);
-		if (n)
-			err = n;
+		for (i = 0; i < cc->tfms_count; i++) {
+			n = crypto_ablkcipher_setkey(cs->tfms[i],
+				cc->key + (i * subkey_size), subkey_size);
+			if (n)
+				err = n;
+		}
 	}
 	return err;
 }
@@ -1154,8 +1198,7 @@ static void crypt_dtr(struct dm_target *ti)
 			cs = per_cpu_ptr(cc->cpu, cpu);
 			if (cs->req)
 				mempool_free(cs->req, cc->req_pool);
-			if (cs->tfm)
-				crypto_free_ablkcipher(cs->tfm);
+			crypt_free_tfms(cc, cpu);
 		}
 
 	if (cc->bs)
@@ -1188,8 +1231,7 @@ static int crypt_ctr_cipher(struct dm_target *ti,
 			    char *cipher_in, char *key)
 {
 	struct crypt_config *cc = ti->private;
-	struct crypto_ablkcipher *tfm;
-	char *tmp, *cipher, *chainmode, *ivmode, *ivopts;
+	char *tmp, *cipher, *chainmode, *ivmode, *ivopts, *keycount;
 	char *cipher_api = NULL;
 	int cpu, ret = -EINVAL;
 
@@ -1201,11 +1243,23 @@ static int crypt_ctr_cipher(struct dm_target *ti,
 
 	/*
 	 * Legacy dm-crypt cipher specification
-	 * cipher-mode-iv:ivopts
+	 * cipher[:keycount]-mode-iv:ivopts
 	 */
 	tmp = cipher_in;
 	cipher = strsep(&tmp, "-");
 
+	keycount = cipher;
+	cipher = strsep(&keycount, ":");
+
+	if (!keycount)
+		cc->tfms_count = 1;
+	else if (sscanf(keycount, "%u", &cc->tfms_count) != 1 ||
+		 !is_power_of_2(cc->tfms_count)) {
+		ti->error = "Bad cipher key count specification";
+		return -EINVAL;
+	}
+	cc->key_parts = cc->tfms_count;
+
 	cc->cipher = kstrdup(cipher, GFP_KERNEL);
 	if (!cc->cipher)
 		goto bad_mem;
@@ -1223,7 +1277,9 @@ static int crypt_ctr_cipher(struct dm_target *ti,
 	if (tmp)
 		DMWARN("Ignoring unexpected additional cipher options");
 
-	cc->cpu = alloc_percpu(struct crypt_cpu);
+	cc->cpu = __alloc_percpu(sizeof(*(cc->cpu)) +
+				 cc->tfms_count * sizeof(*(cc->cpu->tfms)),
+				 __alignof__(struct crypt_cpu));
 	if (!cc->cpu) {
 		ti->error = "Cannot allocate per cpu state";
 		goto bad_mem;
@@ -1255,14 +1311,12 @@ static int crypt_ctr_cipher(struct dm_target *ti,
 
 	/* Allocate cipher */
 	for_each_possible_cpu (cpu) {
-		tfm = crypto_alloc_ablkcipher(cipher_api, 0, 0);
-		if (IS_ERR(tfm)) {
-			ret = PTR_ERR(tfm);
+		ret = crypt_alloc_tfms(cc, cpu, cipher_api);
+		if (ret < 0) {
 			ti->error = "Error allocating crypto tfm";
 			goto bad;
 		}
-		per_cpu_ptr(cc->cpu, cpu)->tfm = tfm;
- 	}
+	}
 
 	/* Initialize and set key */
 	ret = crypt_set_key(cc, key);
@@ -1472,7 +1526,11 @@ static int crypt_status(struct dm_target *ti, status_type_t type,
 		break;
 
 	case STATUSTYPE_TABLE:
-		DMEMIT("%s-%s ", cc->cipher, cc->cipher_mode);
+		if (cc->tfms_count == 1)
+			DMEMIT("%s-%s ", cc->cipher, cc->cipher_mode);
+		else
+			DMEMIT("%s:%d-%s ", cc->cipher, cc->tfms_count,
+			       cc->cipher_mode);
 
 		if (cc->key_size > 0) {
 			if ((maxlen - sz) < ((cc->key_size << 1) + 1))
@@ -1584,7 +1642,7 @@ static int crypt_iterate_devices(struct dm_target *ti,
 
 static struct target_type crypt_target = {
 	.name   = "crypt",
-	.version = {1, 7, 0},
+	.version = {1, 8, 0},
 	.module = THIS_MODULE,
 	.ctr    = crypt_ctr,
 	.dtr    = crypt_dtr,
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 7/7] [RFC]dm crypt: add loop-aes LMK IV generator
  2010-12-21 15:28 [PATCH 0/7] dm crypt fixes and loop-aes mode support Milan Broz
                   ` (5 preceding siblings ...)
  2010-12-21 15:28 ` [PATCH 6/7] [RFC]dm crypt: add multi-key capability Milan Broz
@ 2010-12-21 15:28 ` Milan Broz
  6 siblings, 0 replies; 8+ messages in thread
From: Milan Broz @ 2010-12-21 15:28 UTC (permalink / raw)
  To: dm-devel; +Cc: Max Vozeler, Milan Broz

This IV is designed by Jari Ruusu and is used
in loop-aes package.

This patch just adds compatibility mode to dmcrypt
for this IV scheme.

(N.B it is not meant as loop-aes replacement which
here provides better perfomance but just to provide
compatible mappings for people which are unable to patch
kernel and userspace to access loop-aes devices.)

loop-aes can use three encryption schemes:
 version 1: is plain aes-cbc mode (already compatible)
 version 2: uses 64 multikey scheme with LMK IV
 version 3: the same as version 2 with additional IV seed
 (so v3 uses 65 keys, last key is used as IV seed)

For v2/3 the cipher specification looks like: aes:64-cbc-lmk

In fact, LMK is not only IV generator but also slight
modification to CBC encryption mode - the first block
of CBC mode depends not only on sector and optional
IV seed, but also on plainext data of this sector.

This means that after decryption the first block of sector
must be tweaked according to decrypted data.

The format of key is just hexa encoded "raw key"
used in loop-aes ioctl.

There is needed small userspace utility which gpg decrypts
the file with key and performs simple hash operation to generate
raw keys.
(It will be added to cryptsetup once if this patch is accepted.)

Code is inspired by Max Vozeler cryptoAPI implementation
(which was not accepted some time ago.)

Signed-off-by: Milan Broz <mbroz@redhat.com>
CC: Max Vozeler <max@hinterhof.net>
---
 drivers/md/dm-crypt.c |  184 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 183 insertions(+), 1 deletions(-)

diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 3b9d3a0..c36b977 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -23,6 +23,9 @@
 #include <linux/scatterlist.h>
 #include <asm/page.h>
 #include <asm/unaligned.h>
+#include <crypto/hash.h>
+#include <crypto/md5.h>
+#include <crypto/algapi.h>
 
 #include <linux/device-mapper.h>
 
@@ -90,6 +93,12 @@ struct iv_benbi_private {
 	int shift;
 };
 
+#define LMK_SEED_SIZE 64 /* hash + 0 */
+struct iv_lmk_private {
+	struct crypto_shash *hash_tfm;
+	u8 *seed;
+};
+
 /*
  * Crypt: maps a linear range of a block device
  * and encrypts / decrypts at the same time.
@@ -131,6 +140,7 @@ struct crypt_config {
 	union {
 		struct iv_essiv_private essiv;
 		struct iv_benbi_private benbi;
+		struct iv_lmk_private lmk;
 	} iv_gen_private;
 	sector_t iv_offset;
 	unsigned int iv_size;
@@ -446,6 +456,164 @@ static int crypt_iv_null_gen(struct crypt_config *cc, u8 *iv,
 	return 0;
 }
 
+static void crypt_iv_lmk_dtr(struct crypt_config *cc)
+{
+	struct iv_lmk_private *lmk = &cc->iv_gen_private.lmk;
+
+	if (lmk->hash_tfm && !IS_ERR(lmk->hash_tfm))
+		crypto_free_shash(lmk->hash_tfm);
+	lmk->hash_tfm = NULL;
+
+	kzfree(lmk->seed);
+	lmk->seed = NULL;
+}
+
+static int crypt_iv_lmk_ctr(struct crypt_config *cc, struct dm_target *ti,
+			    const char *opts)
+{
+	struct iv_lmk_private *lmk = &cc->iv_gen_private.lmk;
+
+	lmk->hash_tfm = crypto_alloc_shash("md5", 0, 0);
+	if (IS_ERR(lmk->hash_tfm)) {
+		ti->error = "Error initializing LMK hash";
+		return PTR_ERR(lmk->hash_tfm);
+	}
+
+	/* No seed in LMK version 2 */
+	if (cc->key_parts == cc->tfms_count) {
+		lmk->seed = NULL;
+		return 0;
+	}
+
+	lmk->seed = kzalloc(LMK_SEED_SIZE, GFP_KERNEL);
+	if (!lmk->seed) {
+		crypt_iv_lmk_dtr(cc);
+		ti->error = "Error kmallocing seed storage in LMK";
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int crypt_iv_lmk_init(struct crypt_config *cc)
+{
+	struct iv_lmk_private *lmk = &cc->iv_gen_private.lmk;
+	int subkey_size = cc->key_size / cc->key_parts;
+
+	/* LMK seed is on the position of LMK_KEYS + 1 key */
+	if (lmk->seed)
+		memcpy(lmk->seed, cc->key + (cc->tfms_count * subkey_size),
+		       crypto_shash_digestsize(lmk->hash_tfm));
+
+	return 0;
+}
+
+static int crypt_iv_lmk_wipe(struct crypt_config *cc)
+{
+	struct iv_lmk_private *lmk = &cc->iv_gen_private.lmk;
+
+	if (lmk->seed)
+		memset(lmk->seed, 0, LMK_SEED_SIZE);
+
+	return 0;
+}
+
+static inline void cpu_to_le32_array(u32 *buf, unsigned int words)
+{
+	while (words--) {
+		__cpu_to_le32s(buf);
+		buf++;
+	}
+}
+
+static int crypt_iv_lmk_one(struct crypt_config *cc, u8 *iv,
+			    struct dm_crypt_request *dmreq,
+			    u8 *data)
+{
+	struct iv_lmk_private *lmk = &cc->iv_gen_private.lmk;
+	struct {
+		struct shash_desc desc;
+		char ctx[crypto_shash_descsize(lmk->hash_tfm)];
+	} sdesc;
+	struct md5_state md5state;
+	u32 buf[4];
+	int r;
+
+	sdesc.desc.tfm = lmk->hash_tfm;
+	sdesc.desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	r = crypto_shash_init(&sdesc.desc);
+	if (r)
+		return r;
+
+	if (lmk->seed) {
+		r = crypto_shash_update(&sdesc.desc, lmk->seed, LMK_SEED_SIZE);
+		if (r)
+			return r;
+	}
+
+	/* Sector is always 512B, block size 16, add data of blocks 1-31 */
+	r = crypto_shash_update(&sdesc.desc, data + 16, 16 * 31);
+	if (r)
+		return r;
+
+	/* Sector is cropped to 56 bits here */
+	buf[0] = dmreq->iv_sector & 0xFFFFFFFF;
+	buf[1] = ((dmreq->iv_sector >> 32) & 0x00FFFFFF) | 0x80000000;
+	buf[2] = 4024;
+	buf[3] = 0;
+	cpu_to_le32_array(buf, 4);
+
+	r = crypto_shash_update(&sdesc.desc, (u8 *)buf, sizeof(buf));
+	if (r)
+		return r;
+
+	/* No MD5 padding here */
+	r = crypto_shash_export(&sdesc.desc, &md5state);
+	if (r)
+		return r;
+
+	cpu_to_le32_array(md5state.hash, 4);
+	memcpy(iv, &md5state.hash, cc->iv_size);
+	return 0;
+}
+
+static int crypt_iv_lmk_gen(struct crypt_config *cc, u8 *iv,
+			    struct dm_crypt_request *dmreq)
+{
+	u8 *src;
+	int r = 0;
+
+	if (bio_data_dir(dmreq->ctx->bio_in) == WRITE) {
+		src = kmap_atomic(sg_page(&dmreq->sg_in), KM_USER0);
+		r = crypt_iv_lmk_one(cc, iv, dmreq, src + dmreq->sg_in.offset);
+		kunmap_atomic(src, KM_USER0);
+	} else
+		memset(iv, 0, cc->iv_size);
+
+	return r;
+}
+
+static int crypt_iv_lmk_post(struct crypt_config *cc, u8 *iv,
+			     struct dm_crypt_request *dmreq)
+{
+	u8 *dst;
+	int r;
+
+	if (bio_data_dir(dmreq->ctx->bio_in) == WRITE)
+		return 0;
+
+	dst = kmap_atomic(sg_page(&dmreq->sg_out), KM_USER0);
+	r = crypt_iv_lmk_one(cc, iv, dmreq, dst + dmreq->sg_out.offset);
+
+	/* Code must fix the first block of plaintext sector */
+	if (!r)
+		crypto_xor(dst + dmreq->sg_out.offset, iv, cc->iv_size);
+
+	kunmap_atomic(dst, KM_USER0);
+	return r;
+}
+
 static struct crypt_iv_operations crypt_iv_plain_ops = {
 	.generator = crypt_iv_plain_gen
 };
@@ -472,6 +640,15 @@ static struct crypt_iv_operations crypt_iv_null_ops = {
 	.generator = crypt_iv_null_gen
 };
 
+static struct crypt_iv_operations crypt_iv_lmk_ops = {
+	.ctr	   = crypt_iv_lmk_ctr,
+	.dtr	   = crypt_iv_lmk_dtr,
+	.init	   = crypt_iv_lmk_init,
+	.wipe	   = crypt_iv_lmk_wipe,
+	.generator = crypt_iv_lmk_gen,
+	.post	   = crypt_iv_lmk_post
+};
+
 static void crypt_convert_init(struct crypt_config *cc,
 			       struct convert_context *ctx,
 			       struct bio *bio_out, struct bio *bio_in,
@@ -1349,7 +1526,12 @@ static int crypt_ctr_cipher(struct dm_target *ti,
 		cc->iv_gen_ops = &crypt_iv_benbi_ops;
 	else if (strcmp(ivmode, "null") == 0)
 		cc->iv_gen_ops = &crypt_iv_null_ops;
-	else {
+	else if (strcmp(ivmode, "lmk") == 0) {
+		cc->iv_gen_ops = &crypt_iv_lmk_ops;
+		/* loop-aes multikey version 3 - last key used for IV seed */
+		if (cc->key_size % cc->key_parts)
+			cc->key_parts++;
+	} else {
 		ret = -EINVAL;
 		ti->error = "Invalid IV mode";
 		goto bad;
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2010-12-21 15:28 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-12-21 15:28 [PATCH 0/7] dm crypt fixes and loop-aes mode support Milan Broz
2010-12-21 15:28 ` [PATCH 1/7] dm crypt: fix per cpu struct dereference in destructor Milan Broz
2010-12-21 15:28 ` [PATCH 2/7] dm crypt: remove redundant setkey call Milan Broz
2010-12-21 15:28 ` [PATCH 3/7] dm crypt: set key size early Milan Broz
2010-12-21 15:28 ` [PATCH 4/7] dm crypt: remove unused compatible table output Milan Broz
2010-12-21 15:28 ` [PATCH 5/7] dm crypt: redefine IV generator function and add post IV call Milan Broz
2010-12-21 15:28 ` [PATCH 6/7] [RFC]dm crypt: add multi-key capability Milan Broz
2010-12-21 15:28 ` [PATCH 7/7] [RFC]dm crypt: add loop-aes LMK IV generator Milan Broz

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.