[dm-devel] [PATCH] dm-crypt: Implement Elephant diffuser for Bitlocker compatibility
Mikulas Patocka
mpatocka at redhat.com
Sun Jan 5 09:41:06 UTC 2020
On Fri, 3 Jan 2020, Milan Broz wrote:
> This patch adds experimental support for BitLocker encryption
> with CBC mode and additional Elephant diffuser.
>
> The mode was used in older Windows systems and it is provided
> mainly for compatibility reasons. The userspace support
> to activate these devices is being added to cryptsetup utility.
>
> Read-write activation of such a device is very simple, for example
> echo <password> | cryptsetup bitlkOpen bitlk_image.img test
>
> The Elephant diffuser uses two rotations in opposite direction
> for data (Diffuser A and B) and also XOR operation with Sector key
> over the sector data; Sector key is derived from additional key data.
> The original public documentation is available here
> http://download.microsoft.com/download/0/2/3/0238acaf-d3bf-4a6d-b3d6-0a0be4bbb36e/bitlockercipher200608.pdf
>
> The dm-crypt implementation is embedded to "elephant" IV
> (similar to tcw IV construction).
>
> Because we cannot modify original bio data for write (before
> encryption), an additional internal flag to pre-process data is added.
>
> Signed-off-by: Milan Broz <gmazyland at gmail.com>
Reviewed-by: Mikulas Patocka <mpatocka at redhat.com>
> ---
> drivers/md/dm-crypt.c | 323 +++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 319 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
> index eb9782fc93fe..f9370a1a574b 100644
> --- a/drivers/md/dm-crypt.c
> +++ b/drivers/md/dm-crypt.c
> @@ -1,8 +1,8 @@
> /*
> * Copyright (C) 2003 Jana Saout <jana at saout.de>
> * Copyright (C) 2004 Clemens Fruhwirth <clemens at endorphin.org>
> - * Copyright (C) 2006-2017 Red Hat, Inc. All rights reserved.
> - * Copyright (C) 2013-2017 Milan Broz <gmazyland at gmail.com>
> + * Copyright (C) 2006-2020 Red Hat, Inc. All rights reserved.
> + * Copyright (C) 2013-2020 Milan Broz <gmazyland at gmail.com>
> *
> * This file is released under the GPL.
> */
> @@ -115,6 +115,11 @@ struct iv_tcw_private {
> u8 *whitening;
> };
>
> +#define ELEPHANT_MAX_KEY_SIZE 32
> +struct iv_elephant_private {
> + struct crypto_skcipher *tfm;
> +};
> +
> /*
> * Crypt: maps a linear range of a block device
> * and encrypts / decrypts at the same time.
> @@ -125,6 +130,7 @@ enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID,
> enum cipher_flags {
> CRYPT_MODE_INTEGRITY_AEAD, /* Use authenticated mode for cihper */
> CRYPT_IV_LARGE_SECTORS, /* Calculate IV from sector_size, not 512B sectors */
> + CRYPT_ENCRYPT_PREPROCESS, /* Must preprocess data for encryption (elephant) */
> };
>
> /*
> @@ -152,6 +158,7 @@ struct crypt_config {
> struct iv_benbi_private benbi;
> struct iv_lmk_private lmk;
> struct iv_tcw_private tcw;
> + struct iv_elephant_private elephant;
> } iv_gen_private;
> u64 iv_offset;
> unsigned int iv_size;
> @@ -285,6 +292,11 @@ static struct crypto_aead *any_tfm_aead(struct crypt_config *cc)
> * eboiv: Encrypted byte-offset IV (used in Bitlocker in CBC mode)
> * The IV is encrypted little-endian byte-offset (with the same key
> * and cipher as the volume).
> + *
> + * elephant: The extended version of eboiv with additional Elephant diffuser
> + * used with Bitlocker CBC mode.
> + * This mode was used in older Windows systems
> + * http://download.microsoft.com/download/0/2/3/0238acaf-d3bf-4a6d-b3d6-0a0be4bbb36e/bitlockercipher200608.pdf
> */
>
> static int crypt_iv_plain_gen(struct crypt_config *cc, u8 *iv,
> @@ -734,6 +746,290 @@ static int crypt_iv_eboiv_gen(struct crypt_config *cc, u8 *iv,
> return err;
> }
>
> +static void crypt_iv_elephant_dtr(struct crypt_config *cc)
> +{
> + struct iv_elephant_private *elephant = &cc->iv_gen_private.elephant;
> +
> + crypto_free_skcipher(elephant->tfm);
> + elephant->tfm = NULL;
> +}
> +
> +static int crypt_iv_elephant_ctr(struct crypt_config *cc, struct dm_target *ti,
> + const char *opts)
> +{
> + struct iv_elephant_private *elephant = &cc->iv_gen_private.elephant;
> + int r;
> +
> + elephant->tfm = crypto_alloc_skcipher("ecb(aes)", 0, 0);
> + if (IS_ERR(elephant->tfm)) {
> + r = PTR_ERR(elephant->tfm);
> + elephant->tfm = NULL;
> + return r;
> + }
> +
> + r = crypt_iv_eboiv_ctr(cc, ti, NULL);
> + if (r)
> + crypt_iv_elephant_dtr(cc);
> + return r;
> +}
> +
> +static void diffuser_disk_to_cpu(u32 *d, size_t n)
> +{
> +#ifndef __LITTLE_ENDIAN
> + int i;
> +
> + for (i = 0; i < n; i++)
> + d[i] = le32_to_cpu((__le32)d[i]);
> +#endif
> +}
> +
> +static void diffuser_cpu_to_disk(__le32 *d, size_t n)
> +{
> +#ifndef __LITTLE_ENDIAN
> + int i;
> +
> + for (i = 0; i < n; i++)
> + d[i] = cpu_to_le32((u32)d[i]);
> +#endif
> +}
> +
> +static void diffuser_a_decrypt(u32 *d, size_t n)
> +{
> + int i, i1, i2, i3;
> +
> + for (i = 0; i < 5; i++) {
> + i1 = 0;
> + i2 = n - 2;
> + i3 = n - 5;
> +
> + while (i1 < (n - 1)) {
> + d[i1] += d[i2] ^ (d[i3] << 9 | d[i3] >> 23);
> + i1++; i2++; i3++;
> +
> + if (i3 >= n)
> + i3 -= n;
> +
> + d[i1] += d[i2] ^ d[i3];
> + i1++; i2++; i3++;
> +
> + if (i2 >= n)
> + i2 -= n;
> +
> + d[i1] += d[i2] ^ (d[i3] << 13 | d[i3] >> 19);
> + i1++; i2++; i3++;
> +
> + d[i1] += d[i2] ^ d[i3];
> + i1++; i2++; i3++;
> + }
> + }
> +}
> +
> +static void diffuser_a_encrypt(u32 *d, size_t n)
> +{
> + int i, i1, i2, i3;
> +
> + for (i = 0; i < 5; i++) {
> + i1 = n - 1;
> + i2 = n - 2 - 1;
> + i3 = n - 5 - 1;
> +
> + while (i1 > 0) {
> + d[i1] -= d[i2] ^ d[i3];
> + i1--; i2--; i3--;
> +
> + d[i1] -= d[i2] ^ (d[i3] << 13 | d[i3] >> 19);
> + i1--; i2--; i3--;
> +
> + if (i2 < 0)
> + i2 += n;
> +
> + d[i1] -= d[i2] ^ d[i3];
> + i1--; i2--; i3--;
> +
> + if (i3 < 0)
> + i3 += n;
> +
> + d[i1] -= d[i2] ^ (d[i3] << 9 | d[i3] >> 23);
> + i1--; i2--; i3--;
> + }
> + }
> +}
> +
> +static void diffuser_b_decrypt(u32 *d, size_t n)
> +{
> + int i, i1, i2, i3;
> +
> + for (i = 0; i < 3; i++) {
> + i1 = 0;
> + i2 = 2;
> + i3 = 5;
> +
> + while (i1 < (n - 1)) {
> + d[i1] += d[i2] ^ d[i3];
> + i1++; i2++; i3++;
> +
> + d[i1] += d[i2] ^ (d[i3] << 10 | d[i3] >> 22);
> + i1++; i2++; i3++;
> +
> + if (i2 >= n)
> + i2 -= n;
> +
> + d[i1] += d[i2] ^ d[i3];
> + i1++; i2++; i3++;
> +
> + if (i3 >= n)
> + i3 -= n;
> +
> + d[i1] += d[i2] ^ (d[i3] << 25 | d[i3] >> 7);
> + i1++; i2++; i3++;
> + }
> + }
> +}
> +
> +static void diffuser_b_encrypt(u32 *d, size_t n)
> +{
> + int i, i1, i2, i3;
> +
> + for (i = 0; i < 3; i++) {
> + i1 = n - 1;
> + i2 = 2 - 1;
> + i3 = 5 - 1;
> +
> + while (i1 > 0) {
> + d[i1] -= d[i2] ^ (d[i3] << 25 | d[i3] >> 7);
> + i1--; i2--; i3--;
> +
> + if (i3 < 0)
> + i3 += n;
> +
> + d[i1] -= d[i2] ^ d[i3];
> + i1--; i2--; i3--;
> +
> + if (i2 < 0)
> + i2 += n;
> +
> + d[i1] -= d[i2] ^ (d[i3] << 10 | d[i3] >> 22);
> + i1--; i2--; i3--;
> +
> + d[i1] -= d[i2] ^ d[i3];
> + i1--; i2--; i3--;
> + }
> + }
> +}
> +
> +static int crypt_iv_elephant(struct crypt_config *cc, struct dm_crypt_request *dmreq)
> +{
> + struct iv_elephant_private *elephant = &cc->iv_gen_private.elephant;
> + u8 *es, *ks, *data, *data2, *data_offset;
> + struct skcipher_request *req;
> + struct scatterlist *sg, *sg2, src, dst;
> + struct crypto_wait wait;
> + int i, r;
> +
> + req = skcipher_request_alloc(elephant->tfm, GFP_NOIO);
> + es = kzalloc(16, GFP_NOIO); /* Key for AES */
> + ks = kzalloc(32, GFP_NOIO); /* Elephant sector key */
> +
> + if (!req || !es || !ks) {
> + r = -ENOMEM;
> + goto out;
> + }
> +
> + *(__le64 *)es = cpu_to_le64(dmreq->iv_sector * cc->sector_size);
> +
> + /* E(Ks, e(s)) */
> + sg_init_one(&src, es, 16);
> + sg_init_one(&dst, ks, 16);
> + skcipher_request_set_crypt(req, &src, &dst, 16, NULL);
> + skcipher_request_set_callback(req, 0, crypto_req_done, &wait);
> + r = crypto_wait_req(crypto_skcipher_encrypt(req), &wait);
> + if (r)
> + goto out;
> +
> + /* E(Ks, e'(s)) */
> + es[15] = 0x80;
> + sg_init_one(&dst, &ks[16], 16);
> + r = crypto_wait_req(crypto_skcipher_encrypt(req), &wait);
> + if (r)
> + goto out;
> +
> + sg = crypt_get_sg_data(cc, dmreq->sg_out);
> + data = kmap_atomic(sg_page(sg));
> + data_offset = data + sg->offset;
> +
> + /* Cannot modify original bio, copy to sg_out and apply Elephant to it */
> + if (bio_data_dir(dmreq->ctx->bio_in) == WRITE) {
> + sg2 = crypt_get_sg_data(cc, dmreq->sg_in);
> + data2 = kmap_atomic(sg_page(sg2));
> + memcpy(data_offset, data2 + sg2->offset, cc->sector_size);
> + kunmap_atomic(data2);
> + }
> +
> + if (bio_data_dir(dmreq->ctx->bio_in) != WRITE) {
> + diffuser_disk_to_cpu((u32*)data_offset, cc->sector_size / sizeof(u32));
> + diffuser_b_decrypt((u32*)data_offset, cc->sector_size / sizeof(u32));
> + diffuser_a_decrypt((u32*)data_offset, cc->sector_size / sizeof(u32));
> + diffuser_cpu_to_disk((__le32*)data_offset, cc->sector_size / sizeof(u32));
> + }
> +
> + for (i = 0; i < (cc->sector_size / 32); i++)
> + crypto_xor(data_offset + i * 32, ks, 32);
> +
> + if (bio_data_dir(dmreq->ctx->bio_in) == WRITE) {
> + diffuser_disk_to_cpu((u32*)data_offset, cc->sector_size / sizeof(u32));
> + diffuser_a_encrypt((u32*)data_offset, cc->sector_size / sizeof(u32));
> + diffuser_b_encrypt((u32*)data_offset, cc->sector_size / sizeof(u32));
> + diffuser_cpu_to_disk((__le32*)data_offset, cc->sector_size / sizeof(u32));
> + }
> +
> + kunmap_atomic(data);
> +out:
> + kzfree(ks);
> + kzfree(es);
> + skcipher_request_free(req);
> + return r;
> +}
> +
> +static int crypt_iv_elephant_gen(struct crypt_config *cc, u8 *iv,
> + struct dm_crypt_request *dmreq)
> +{
> + int r;
> +
> + if (bio_data_dir(dmreq->ctx->bio_in) == WRITE) {
> + r = crypt_iv_elephant(cc, dmreq);
> + if (r)
> + return r;
> + }
> +
> + return crypt_iv_eboiv_gen(cc, iv, dmreq);
> +}
> +
> +static int crypt_iv_elephant_post(struct crypt_config *cc, u8 *iv,
> + struct dm_crypt_request *dmreq)
> +{
> + if (bio_data_dir(dmreq->ctx->bio_in) != WRITE)
> + return crypt_iv_elephant(cc, dmreq);
> +
> + return 0;
> +}
> +
> +static int crypt_iv_elephant_init(struct crypt_config *cc)
> +{
> + struct iv_elephant_private *elephant = &cc->iv_gen_private.elephant;
> + int key_offset = cc->key_size - cc->key_extra_size;
> +
> + return crypto_skcipher_setkey(elephant->tfm, &cc->key[key_offset], cc->key_extra_size);
> +}
> +
> +static int crypt_iv_elephant_wipe(struct crypt_config *cc)
> +{
> + struct iv_elephant_private *elephant = &cc->iv_gen_private.elephant;
> + u8 key[ELEPHANT_MAX_KEY_SIZE];
> +
> + memset(key, 0, cc->key_extra_size);
> + return crypto_skcipher_setkey(elephant->tfm, key, cc->key_extra_size);
> +}
> +
> static const struct crypt_iv_operations crypt_iv_plain_ops = {
> .generator = crypt_iv_plain_gen
> };
> @@ -787,6 +1083,15 @@ static struct crypt_iv_operations crypt_iv_eboiv_ops = {
> .generator = crypt_iv_eboiv_gen
> };
>
> +static struct crypt_iv_operations crypt_iv_elephant_ops = {
> + .ctr = crypt_iv_elephant_ctr,
> + .dtr = crypt_iv_elephant_dtr,
> + .init = crypt_iv_elephant_init,
> + .wipe = crypt_iv_elephant_wipe,
> + .generator = crypt_iv_elephant_gen,
> + .post = crypt_iv_elephant_post
> +};
> +
> /*
> * Integrity extensions
> */
> @@ -1103,6 +1408,9 @@ static int crypt_convert_block_skcipher(struct crypt_config *cc,
> r = cc->iv_gen_ops->generator(cc, org_iv, dmreq);
> if (r < 0)
> return r;
> + /* Data can be already preprocessed in generator */
> + if (test_bit(CRYPT_ENCRYPT_PREPROCESS, &cc->cipher_flags))
> + sg_in = sg_out;
> /* Store generated IV in integrity metadata */
> if (cc->integrity_iv_size)
> memcpy(tag_iv, org_iv, cc->integrity_iv_size);
> @@ -2191,7 +2499,14 @@ static int crypt_ctr_ivmode(struct dm_target *ti, const char *ivmode)
> cc->iv_gen_ops = &crypt_iv_null_ops;
> else if (strcmp(ivmode, "eboiv") == 0)
> cc->iv_gen_ops = &crypt_iv_eboiv_ops;
> - else if (strcmp(ivmode, "lmk") == 0) {
> + else if (strcmp(ivmode, "elephant") == 0) {
> + cc->iv_gen_ops = &crypt_iv_elephant_ops;
> + cc->key_parts = 2;
> + cc->key_extra_size = cc->key_size / 2;
> + if (cc->key_extra_size > ELEPHANT_MAX_KEY_SIZE)
> + return -EINVAL;
> + set_bit(CRYPT_ENCRYPT_PREPROCESS, &cc->cipher_flags);
> + } else if (strcmp(ivmode, "lmk") == 0) {
> cc->iv_gen_ops = &crypt_iv_lmk_ops;
> /*
> * Version 2 and 3 is recognised according
> @@ -2959,7 +3274,7 @@ static void crypt_io_hints(struct dm_target *ti, struct queue_limits *limits)
>
> static struct target_type crypt_target = {
> .name = "crypt",
> - .version = {1, 19, 0},
> + .version = {1, 20, 0},
> .module = THIS_MODULE,
> .ctr = crypt_ctr,
> .dtr = crypt_dtr,
> --
> 2.25.0.rc0
>
>
> --
> dm-devel mailing list
> dm-devel at redhat.com
> https://www.redhat.com/mailman/listinfo/dm-devel
>
More information about the dm-devel
mailing list