--- 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 + * 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) { *(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:". + * 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); }