forked from Minki/linux
dm crypt: switch to ESSIV crypto API template
Replace the explicit ESSIV handling in the dm-crypt driver with calls into the crypto API, which now possesses the capability to perform this processing within the crypto subsystem. Note that we reorder the AEAD cipher_api string parsing with the TFM instantiation: this is needed because cipher_api is mangled by the ESSIV handling, and throws off the parsing of "authenc(" otherwise. Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Tested-by: Milan Broz <gmazyland@gmail.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com>
This commit is contained in:
parent
be1eb7f78a
commit
a1a262b66e
@ -271,6 +271,7 @@ config DM_CRYPT
|
||||
depends on BLK_DEV_DM
|
||||
select CRYPTO
|
||||
select CRYPTO_CBC
|
||||
select CRYPTO_ESSIV
|
||||
---help---
|
||||
This device-mapper target allows you to create a device that
|
||||
transparently encrypts the data on it. You'll need to activate
|
||||
|
@ -98,11 +98,6 @@ struct crypt_iv_operations {
|
||||
struct dm_crypt_request *dmreq);
|
||||
};
|
||||
|
||||
struct iv_essiv_private {
|
||||
struct crypto_shash *hash_tfm;
|
||||
u8 *salt;
|
||||
};
|
||||
|
||||
struct iv_benbi_private {
|
||||
int shift;
|
||||
};
|
||||
@ -155,7 +150,6 @@ struct crypt_config {
|
||||
|
||||
const struct crypt_iv_operations *iv_gen_ops;
|
||||
union {
|
||||
struct iv_essiv_private essiv;
|
||||
struct iv_benbi_private benbi;
|
||||
struct iv_lmk_private lmk;
|
||||
struct iv_tcw_private tcw;
|
||||
@ -165,8 +159,6 @@ struct crypt_config {
|
||||
unsigned short int sector_size;
|
||||
unsigned char sector_shift;
|
||||
|
||||
/* ESSIV: struct crypto_cipher *essiv_tfm */
|
||||
void *iv_private;
|
||||
union {
|
||||
struct crypto_skcipher **tfms;
|
||||
struct crypto_aead **tfms_aead;
|
||||
@ -324,157 +316,15 @@ static int crypt_iv_plain64be_gen(struct crypt_config *cc, u8 *iv,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialise ESSIV - compute salt but no local memory allocations */
|
||||
static int crypt_iv_essiv_init(struct crypt_config *cc)
|
||||
{
|
||||
struct iv_essiv_private *essiv = &cc->iv_gen_private.essiv;
|
||||
SHASH_DESC_ON_STACK(desc, essiv->hash_tfm);
|
||||
struct crypto_cipher *essiv_tfm;
|
||||
int err;
|
||||
|
||||
desc->tfm = essiv->hash_tfm;
|
||||
|
||||
err = crypto_shash_digest(desc, cc->key, cc->key_size, essiv->salt);
|
||||
shash_desc_zero(desc);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
essiv_tfm = cc->iv_private;
|
||||
|
||||
err = crypto_cipher_setkey(essiv_tfm, essiv->salt,
|
||||
crypto_shash_digestsize(essiv->hash_tfm));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Wipe salt and reset key derived from volume key */
|
||||
static int crypt_iv_essiv_wipe(struct crypt_config *cc)
|
||||
{
|
||||
struct iv_essiv_private *essiv = &cc->iv_gen_private.essiv;
|
||||
unsigned salt_size = crypto_shash_digestsize(essiv->hash_tfm);
|
||||
struct crypto_cipher *essiv_tfm;
|
||||
int r, err = 0;
|
||||
|
||||
memset(essiv->salt, 0, salt_size);
|
||||
|
||||
essiv_tfm = cc->iv_private;
|
||||
r = crypto_cipher_setkey(essiv_tfm, essiv->salt, salt_size);
|
||||
if (r)
|
||||
err = r;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Allocate the cipher for ESSIV */
|
||||
static struct crypto_cipher *alloc_essiv_cipher(struct crypt_config *cc,
|
||||
struct dm_target *ti,
|
||||
const u8 *salt,
|
||||
unsigned int saltsize)
|
||||
{
|
||||
struct crypto_cipher *essiv_tfm;
|
||||
int err;
|
||||
|
||||
/* Setup the essiv_tfm with the given salt */
|
||||
essiv_tfm = crypto_alloc_cipher(cc->cipher, 0, 0);
|
||||
if (IS_ERR(essiv_tfm)) {
|
||||
ti->error = "Error allocating crypto tfm for ESSIV";
|
||||
return essiv_tfm;
|
||||
}
|
||||
|
||||
if (crypto_cipher_blocksize(essiv_tfm) != cc->iv_size) {
|
||||
ti->error = "Block size of ESSIV cipher does "
|
||||
"not match IV size of block cipher";
|
||||
crypto_free_cipher(essiv_tfm);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
err = crypto_cipher_setkey(essiv_tfm, salt, saltsize);
|
||||
if (err) {
|
||||
ti->error = "Failed to set key for ESSIV cipher";
|
||||
crypto_free_cipher(essiv_tfm);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
return essiv_tfm;
|
||||
}
|
||||
|
||||
static void crypt_iv_essiv_dtr(struct crypt_config *cc)
|
||||
{
|
||||
struct crypto_cipher *essiv_tfm;
|
||||
struct iv_essiv_private *essiv = &cc->iv_gen_private.essiv;
|
||||
|
||||
crypto_free_shash(essiv->hash_tfm);
|
||||
essiv->hash_tfm = NULL;
|
||||
|
||||
kzfree(essiv->salt);
|
||||
essiv->salt = NULL;
|
||||
|
||||
essiv_tfm = cc->iv_private;
|
||||
|
||||
if (essiv_tfm)
|
||||
crypto_free_cipher(essiv_tfm);
|
||||
|
||||
cc->iv_private = NULL;
|
||||
}
|
||||
|
||||
static int crypt_iv_essiv_ctr(struct crypt_config *cc, struct dm_target *ti,
|
||||
const char *opts)
|
||||
{
|
||||
struct crypto_cipher *essiv_tfm = NULL;
|
||||
struct crypto_shash *hash_tfm = NULL;
|
||||
u8 *salt = NULL;
|
||||
int err;
|
||||
|
||||
if (!opts) {
|
||||
ti->error = "Digest algorithm missing for ESSIV mode";
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Allocate hash algorithm */
|
||||
hash_tfm = crypto_alloc_shash(opts, 0, 0);
|
||||
if (IS_ERR(hash_tfm)) {
|
||||
ti->error = "Error initializing ESSIV hash";
|
||||
err = PTR_ERR(hash_tfm);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
salt = kzalloc(crypto_shash_digestsize(hash_tfm), GFP_KERNEL);
|
||||
if (!salt) {
|
||||
ti->error = "Error kmallocing salt storage in ESSIV";
|
||||
err = -ENOMEM;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
cc->iv_gen_private.essiv.salt = salt;
|
||||
cc->iv_gen_private.essiv.hash_tfm = hash_tfm;
|
||||
|
||||
essiv_tfm = alloc_essiv_cipher(cc, ti, salt,
|
||||
crypto_shash_digestsize(hash_tfm));
|
||||
if (IS_ERR(essiv_tfm)) {
|
||||
crypt_iv_essiv_dtr(cc);
|
||||
return PTR_ERR(essiv_tfm);
|
||||
}
|
||||
cc->iv_private = essiv_tfm;
|
||||
|
||||
return 0;
|
||||
|
||||
bad:
|
||||
if (hash_tfm && !IS_ERR(hash_tfm))
|
||||
crypto_free_shash(hash_tfm);
|
||||
kfree(salt);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int crypt_iv_essiv_gen(struct crypt_config *cc, u8 *iv,
|
||||
struct dm_crypt_request *dmreq)
|
||||
{
|
||||
struct crypto_cipher *essiv_tfm = cc->iv_private;
|
||||
|
||||
/*
|
||||
* ESSIV encryption of the IV is now handled by the crypto API,
|
||||
* so just pass the plain sector number here.
|
||||
*/
|
||||
memset(iv, 0, cc->iv_size);
|
||||
*(__le64 *)iv = cpu_to_le64(dmreq->iv_sector);
|
||||
crypto_cipher_encrypt_one(essiv_tfm, iv, iv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -898,10 +748,6 @@ static const struct crypt_iv_operations crypt_iv_plain64be_ops = {
|
||||
};
|
||||
|
||||
static const struct crypt_iv_operations crypt_iv_essiv_ops = {
|
||||
.ctr = crypt_iv_essiv_ctr,
|
||||
.dtr = crypt_iv_essiv_dtr,
|
||||
.init = crypt_iv_essiv_init,
|
||||
.wipe = crypt_iv_essiv_wipe,
|
||||
.generator = crypt_iv_essiv_gen
|
||||
};
|
||||
|
||||
@ -2464,7 +2310,7 @@ static int crypt_ctr_cipher_new(struct dm_target *ti, char *cipher_in, char *key
|
||||
char **ivmode, char **ivopts)
|
||||
{
|
||||
struct crypt_config *cc = ti->private;
|
||||
char *tmp, *cipher_api;
|
||||
char *tmp, *cipher_api, buf[CRYPTO_MAX_ALG_NAME];
|
||||
int ret = -EINVAL;
|
||||
|
||||
cc->tfms_count = 1;
|
||||
@ -2490,9 +2336,32 @@ static int crypt_ctr_cipher_new(struct dm_target *ti, char *cipher_in, char *key
|
||||
/* The rest is crypto API spec */
|
||||
cipher_api = tmp;
|
||||
|
||||
/* Alloc AEAD, can be used only in new format. */
|
||||
if (crypt_integrity_aead(cc)) {
|
||||
ret = crypt_ctr_auth_cipher(cc, cipher_api);
|
||||
if (ret < 0) {
|
||||
ti->error = "Invalid AEAD cipher spec";
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
if (*ivmode && !strcmp(*ivmode, "lmk"))
|
||||
cc->tfms_count = 64;
|
||||
|
||||
if (*ivmode && !strcmp(*ivmode, "essiv")) {
|
||||
if (!*ivopts) {
|
||||
ti->error = "Digest algorithm missing for ESSIV mode";
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = snprintf(buf, CRYPTO_MAX_ALG_NAME, "essiv(%s,%s)",
|
||||
cipher_api, *ivopts);
|
||||
if (ret < 0 || ret >= CRYPTO_MAX_ALG_NAME) {
|
||||
ti->error = "Cannot allocate cipher string";
|
||||
return -ENOMEM;
|
||||
}
|
||||
cipher_api = buf;
|
||||
}
|
||||
|
||||
cc->key_parts = cc->tfms_count;
|
||||
|
||||
/* Allocate cipher */
|
||||
@ -2502,15 +2371,9 @@ static int crypt_ctr_cipher_new(struct dm_target *ti, char *cipher_in, char *key
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Alloc AEAD, can be used only in new format. */
|
||||
if (crypt_integrity_aead(cc)) {
|
||||
ret = crypt_ctr_auth_cipher(cc, cipher_api);
|
||||
if (ret < 0) {
|
||||
ti->error = "Invalid AEAD cipher spec";
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (crypt_integrity_aead(cc))
|
||||
cc->iv_size = crypto_aead_ivsize(any_tfm_aead(cc));
|
||||
} else
|
||||
else
|
||||
cc->iv_size = crypto_skcipher_ivsize(any_tfm(cc));
|
||||
|
||||
ret = crypt_ctr_blkdev_cipher(cc);
|
||||
@ -2579,9 +2442,19 @@ static int crypt_ctr_cipher_old(struct dm_target *ti, char *cipher_in, char *key
|
||||
if (!cipher_api)
|
||||
goto bad_mem;
|
||||
|
||||
ret = snprintf(cipher_api, CRYPTO_MAX_ALG_NAME,
|
||||
"%s(%s)", chainmode, cipher);
|
||||
if (ret < 0) {
|
||||
if (*ivmode && !strcmp(*ivmode, "essiv")) {
|
||||
if (!*ivopts) {
|
||||
ti->error = "Digest algorithm missing for ESSIV mode";
|
||||
kfree(cipher_api);
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = snprintf(cipher_api, CRYPTO_MAX_ALG_NAME,
|
||||
"essiv(%s(%s),%s)", chainmode, cipher, *ivopts);
|
||||
} else {
|
||||
ret = snprintf(cipher_api, CRYPTO_MAX_ALG_NAME,
|
||||
"%s(%s)", chainmode, cipher);
|
||||
}
|
||||
if (ret < 0 || ret >= CRYPTO_MAX_ALG_NAME) {
|
||||
kfree(cipher_api);
|
||||
goto bad_mem;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user