This patch adds a new IV mode, I baptise ''encrypted sector|salt IV'', short ESSIV. I describe ESSIV in http://article.gmane.org/gmane.linux.kernel.device-mapper.dm-crypt/472 This patch also redefines the syntax of . The new syntax is: -- This has been done to support chaining modes different from CBC mode, for example CMC. CMC is not implemented in cryptoapi yet, however I would like dm-crypt to be ready for it, so the problems outlined by Adam J. Richter in http://article.gmane.org/gmane.linux.kernel.device-mapper.dm-crypt/454 can be fixed easily, by switching to CMC chaining mode. Compatibility code has been added to accommodate the old sytnax. To use ESSIV, set the ivmode (according to the new syntax) to "essiv:". "hash" should be a valid cryptoapi hash. This, for example, is a valid dm-target line: 0 200 crypt aes-cbc-essiv:sha256 00000000000000000000000000000000 0 /dev/loop/5 0 Signed-Off-By: Fruhwirth Clemens Signed-Off-By: Christophe Saout --- diff/drivers/md/dm-crypt.c 2004-05-19 22:11:47.000000000 +0100 +++ source/drivers/md/dm-crypt.c 2004-10-04 22:33:43.000000000 +0100 @@ -1,5 +1,6 @@ /* * Copyright (C) 2003 Christophe Saout + * Copyright (C) 2004 Clemens Fruhwirth * * This file is released under the GPL. */ @@ -15,6 +16,7 @@ #include #include #include +#include #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) { + memset(iv, 0, cc->iv_size); *(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,34 +470,124 @@ 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:". + * 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; + struct scatterlist sg; + char *salt; + int saltsize; + + 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; + } + + if (crypto_tfm_alg_type(hash_tfm) != CRYPTO_ALG_TYPE_DIGEST) { + ti->error = + PFX "Expected digest algorithm for ESSIV hash"; + crypto_free_tfm(hash_tfm); + 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; } 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 */ + /* at least a 64 bit sector number should fit in our buffer */ cc->iv_size = max(crypto_tfm_alg_ivsize(tfm), - (unsigned int)(sizeof(u32) / sizeof(u8))); + (unsigned int)(sizeof(u64) / sizeof(u8))); else { cc->iv_size = 0; if (cc->iv_generator) { @@ -490,12 +611,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 +643,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 +657,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); } @@ -740,7 +859,7 @@ 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,