Introduces a mode for dm-crypt which uses multiple encryption keys (and thus tfms) alternating based on the sector number and the number of keys. This change is needed to support loop-AES compatible block chaining modes, which use "multi:64". Signed-off-by: Max Vozeler <max@xxxxxxxxxxxxx> Cc: Milan Broz <mbroz@xxxxxxxxxx> Cc: Jari Ruusu <jariruusu@xxxxxxxxxxxxxxxxxxxxx> --- Documentation/device-mapper/dm-crypt.txt | 3 +- drivers/md/dm-crypt.c | 138 +++++++++++++++++++++++++---- 2 files changed, 121 insertions(+), 20 deletions(-) diff --git a/Documentation/device-mapper/dm-crypt.txt b/Documentation/device-mapper/dm-crypt.txt index 6680cab..0d57442 100644 --- a/Documentation/device-mapper/dm-crypt.txt +++ b/Documentation/device-mapper/dm-crypt.txt @@ -8,11 +8,12 @@ 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-chainmode-ivopts:ivmode-multi:nkeys). Examples: des aes-cbc-essiv:sha256 twofish-ecb + aes-lmk3-plain64-multi:64 /proc/crypto contains supported crypto modes diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index e783f93..a7c7c22 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -22,6 +22,7 @@ #include <linux/scatterlist.h> #include <asm/page.h> #include <asm/unaligned.h> +#include <asm/div64.h> #include <linux/device-mapper.h> @@ -120,6 +121,12 @@ struct crypt_config { unsigned int iv_size; /* + * crypto context selection + */ + struct crypto_ablkcipher **tfms; + unsigned long numtfms; + + /* * Layout of each crypto request: * * struct ablkcipher_request @@ -137,7 +144,6 @@ struct crypt_config { char cipher[CRYPTO_MAX_ALG_NAME]; char chainmode[CRYPTO_MAX_ALG_NAME]; - struct crypto_ablkcipher *tfm; unsigned long flags; unsigned int key_size; u8 key[0]; @@ -273,7 +279,7 @@ static int crypt_iv_essiv_ctr(struct crypt_config *cc, struct dm_target *ti, goto bad; } if (crypto_cipher_blocksize(essiv_tfm) != - crypto_ablkcipher_ivsize(cc->tfm)) { + crypto_ablkcipher_ivsize(cc->tfms[0])) { ti->error = "Block size of ESSIV cipher does " "not match IV size of block cipher"; err = -EINVAL; @@ -306,7 +312,7 @@ static int crypt_iv_essiv_gen(struct crypt_config *cc, u8 *iv, sector_t sector) static int crypt_iv_benbi_ctr(struct crypt_config *cc, struct dm_target *ti, const char *opts) { - unsigned bs = crypto_ablkcipher_blocksize(cc->tfm); + unsigned bs = crypto_ablkcipher_blocksize(cc->tfms[0]); int log = ilog2(bs); /* we need to calculate how far we must shift the sector count @@ -376,6 +382,56 @@ static struct crypt_iv_operations crypt_iv_null_ops = { .generator = crypt_iv_null_gen }; +static void crypt_free_tfms(struct crypt_config *cc) +{ + int i; + + for (i=0; i < cc->numtfms; i++) + if (cc->tfms[i]) + crypto_free_ablkcipher(cc->tfms[i]); + + kfree(cc->tfms); +} + +static int crypt_alloc_tfms(struct crypt_config *cc, struct dm_target *ti, char *ciphermode) +{ + struct crypto_ablkcipher **tfms; + int i; + + tfms = kcalloc(cc->numtfms, sizeof(*tfms), GFP_KERNEL); + if (!tfms) + return -ENOMEM; + + for (i=0; i < cc->numtfms; i++) { + struct crypto_ablkcipher *tfm; + + tfm = crypto_alloc_ablkcipher(ciphermode, 0, 0); + if (IS_ERR(tfm)) { + crypt_free_tfms(cc); + + ti->error = "Error allocating crypto tfm"; + return PTR_ERR(tfm); + } + + tfms[i] = tfm; + } + + cc->tfms = tfms; + + return 0; +} + +static struct crypto_ablkcipher *crypt_select_tfm(struct crypt_config *cc, + struct convert_context *ctx) +{ + if (cc->numtfms == 1) + return cc->tfms[0]; + else { + sector_t tmp = ctx->sector; + return cc->tfms[do_div(tmp, cc->numtfms)]; + } +} + static void crypt_convert_init(struct crypt_config *cc, struct convert_context *ctx, struct bio *bio_out, struct bio *bio_in, @@ -415,7 +471,7 @@ static int crypt_convert_block(struct crypt_config *cc, dmreq = dmreq_of_req(cc, req); iv = (u8 *)ALIGN((unsigned long)(dmreq + 1), - crypto_ablkcipher_alignmask(cc->tfm) + 1); + crypto_ablkcipher_alignmask(cc->tfms[0]) + 1); dmreq->ctx = ctx; sg_init_table(&dmreq->sg_in, 1); @@ -460,9 +516,15 @@ static void kcryptd_async_done(struct crypto_async_request *async_req, static void crypt_alloc_req(struct crypt_config *cc, struct convert_context *ctx) { + struct crypto_ablkcipher *tfm; + if (!cc->req) cc->req = mempool_alloc(cc->req_pool, GFP_NOIO); - ablkcipher_request_set_tfm(cc->req, cc->tfm); + + tfm = crypt_select_tfm(cc, ctx); + + ablkcipher_request_set_tfm(cc->req, tfm); + ablkcipher_request_set_callback(cc->req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, kcryptd_async_done, @@ -974,6 +1036,21 @@ static void crypt_encode_key(char *hex, u8 *key, unsigned int size) } } +static int crypt_set_subkeys(struct crypt_config *cc, const u8 *key) +{ + int i, err = 0; + unsigned subkey_size = cc->key_size / cc->numtfms; + + for (i=0; i < cc->numtfms; i++) { + err = crypto_ablkcipher_setkey(cc->tfms[i], + cc->key + (i * subkey_size), subkey_size); + if (err) + break; + } + + return err; +} + static int crypt_set_key(struct crypt_config *cc, char *key) { unsigned key_size = strlen(key) >> 1; @@ -989,14 +1066,15 @@ static int crypt_set_key(struct crypt_config *cc, char *key) set_bit(DM_CRYPT_KEY_VALID, &cc->flags); - return crypto_ablkcipher_setkey(cc->tfm, cc->key, cc->key_size); + return crypt_set_subkeys(cc, cc->key); } static int crypt_wipe_key(struct crypt_config *cc) { clear_bit(DM_CRYPT_KEY_VALID, &cc->flags); memset(&cc->key, 0, cc->key_size * sizeof(u8)); - return crypto_ablkcipher_setkey(cc->tfm, cc->key, cc->key_size); + + return crypt_set_subkeys(cc, cc->key); } /* @@ -1006,12 +1084,13 @@ static int crypt_wipe_key(struct crypt_config *cc) static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) { struct crypt_config *cc; - struct crypto_ablkcipher *tfm; char *tmp; char *cipher; char *chainmode; char *ivmode; char *ivopts; + char *tfmmode; + char *tfmopts; unsigned int key_size; unsigned long long tmpll; char ciphermode[CRYPTO_MAX_ALG_NAME]; @@ -1026,6 +1105,8 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) chainmode = strsep(&tmp, "-"); ivopts = strsep(&tmp, "-"); ivmode = strsep(&ivopts, ":"); + tfmopts = strsep(&tmp, "-"); + tfmmode = strsep(&tfmopts, ":"); if (tmp) DMWARN("Unexpected additional cipher options"); @@ -1056,15 +1137,32 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) goto bad_cipher; } - tfm = crypto_alloc_ablkcipher(ciphermode, 0, 0); - if (IS_ERR(tfm)) { - ti->error = "Error allocating crypto tfm"; + strcpy(cc->cipher, cipher); + strcpy(cc->chainmode, chainmode); + + /* + * Choose multi key mode. Valid modes: "single", "multi:<n>" + */ + + if (tfmmode == NULL || strcmp(tfmmode, "single") == 0) + cc->numtfms = 1; + else if (strcmp(tfmmode, "multi") == 0) { + if (tfmopts == NULL) { + ti->error = "Number of keys missing for multi-tfm mode"; + return -EINVAL; + } + + if (strict_strtoul(tfmopts, 10, &cc->numtfms) < 0) { + ti->error = "Number of keys badly formatted"; + return -EINVAL; + } + } else { + ti->error = "Invalid tfm mode"; goto bad_cipher; } - strcpy(cc->cipher, cipher); - strcpy(cc->chainmode, chainmode); - cc->tfm = tfm; + if (crypt_alloc_tfms(cc, ti, ciphermode) < 0) + goto bad_cipher; if (crypt_set_key(cc, argv[1]) < 0) { ti->error = "Error decoding and setting key"; @@ -1103,7 +1201,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) goto bad_slab_pool; } - cc->iv_size = crypto_ablkcipher_ivsize(tfm); + cc->iv_size = crypto_ablkcipher_ivsize(cc->tfms[0]); if (cc->iv_size) /* at least a 64 bit sector number should fit in our buffer */ cc->iv_size = max(cc->iv_size, @@ -1124,9 +1222,9 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) } cc->dmreq_start = sizeof(struct ablkcipher_request); - cc->dmreq_start += crypto_ablkcipher_reqsize(tfm); + cc->dmreq_start += crypto_ablkcipher_reqsize(cc->tfms[0]); cc->dmreq_start = ALIGN(cc->dmreq_start, crypto_tfm_ctx_alignment()); - cc->dmreq_start += crypto_ablkcipher_alignmask(tfm) & + cc->dmreq_start += crypto_ablkcipher_alignmask(cc->tfms[0]) & ~(crypto_tfm_ctx_alignment() - 1); cc->req_pool = mempool_create_kmalloc_pool(MIN_IOS, cc->dmreq_start + @@ -1213,7 +1311,7 @@ bad_slab_pool: if (cc->iv_gen_ops && cc->iv_gen_ops->dtr) cc->iv_gen_ops->dtr(cc); bad_ivmode: - crypto_free_ablkcipher(tfm); + crypt_free_tfms(cc); bad_cipher: /* Must zero key material before freeing */ kzfree(cc); @@ -1238,7 +1336,9 @@ static void crypt_dtr(struct dm_target *ti) kfree(cc->iv_mode); if (cc->iv_gen_ops && cc->iv_gen_ops->dtr) cc->iv_gen_ops->dtr(cc); - crypto_free_ablkcipher(cc->tfm); + + crypt_free_tfms(cc); + dm_put_device(ti, cc->dev); /* Must zero key material before freeing */ -- 1.6.5.4 -- To unsubscribe from this list: send the line "unsubscribe linux-crypto" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html