[PATCH v2 1/3] dm-inlinecrypt: Add inline encryption support

Md Sadre Alam
2024-09-16T08:58:31Z

QCOM SDCC controller supports Inline Crypto Engine
This driver will enables inline encryption/decryption
for ICE. The algorithm supported by ICE are XTS(AES)
and CBC(AES).

Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com>
---

Change in [v2]

* Added dm-inlinecrypt driver support

* squash the patch blk-crypto: Add additional algo modes for Inline
encryption and md: dm-crypt: Add additional algo modes for inline
encryption and added in this

Change in [v1]

* This patch was not included in [v1]

block/blk-crypto.c | 21 +++
drivers/md/Kconfig | 8 +
drivers/md/Makefile | 1 +
drivers/md/dm-inline-crypt.c | 316 +++++++++++++++++++++++++++++++++++
include/linux/blk-crypto.h | 3 +
5 files changed, 349 insertions(+)
create mode 100644 drivers/md/dm-inline-crypt.c

diff --git a/block/blk-crypto.c b/block/blk-crypto.c
index 4d760b092deb..e5bc3c7a405b 100644
--- a/block/blk-crypto.c
+++ b/block/blk-crypto.c
@@ -19,6 +19,12 @@
#include "blk-crypto-internal.h"

const struct blk_crypto_mode blk_crypto_modes[] = {
+ [BLK_ENCRYPTION_MODE_AES_128_XTS] = {
+ .name = "AES-128-XTS",
+ .cipher_str = "xts(aes)",
+ .keysize = 32,
+ .ivsize = 16,
+ },
[BLK_ENCRYPTION_MODE_AES_256_XTS] = {
.name = "AES-256-XTS",
.cipher_str = "xts(aes)",
@@ -43,6 +49,18 @@ const struct blk_crypto_mode blk_crypto_modes[] = {
.keysize = 32,
.ivsize = 16,
},
+ [BLK_ENCRYPTION_MODE_AES_128_CBC] = {
+ .name = "AES-128-CBC",
+ .cipher_str = "cbc(aes)",
+ .keysize = 16,
+ .ivsize = 16,
+ },
+ [BLK_ENCRYPTION_MODE_AES_256_CBC] = {
+ .name = "AES-256-CBC",
+ .cipher_str = "cbc(aes)",
+ .keysize = 32,
+ .ivsize = 16,
+ },
};

/*
@@ -106,6 +124,7 @@ void bio_crypt_set_ctx(struct bio *bio, const struct blk_crypto_key *key,

bio->bi_crypt_context = bc;
}
+EXPORT_SYMBOL_GPL(bio_crypt_set_ctx);

void __bio_crypt_free_ctx(struct bio *bio)
{
@@ -356,6 +375,7 @@ int blk_crypto_init_key(struct blk_crypto_key *blk_key, const u8 *raw_key,

return 0;
}
+EXPORT_SYMBOL_GPL(blk_crypto_init_key);

bool blk_crypto_config_supported_natively(struct block_device *bdev,
const struct blk_crypto_config *cfg)
@@ -398,6 +418,7 @@ int blk_crypto_start_using_key(struct block_device *bdev,
return 0;
return blk_crypto_fallback_start_using_mode(key->crypto_cfg.crypto_mode);
}
+EXPORT_SYMBOL_GPL(blk_crypto_start_using_key);

/**
* blk_crypto_evict_key() - Evict a blk_crypto_key from a block_device
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 1e9db8e4acdf..272a6a3274bb 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -270,6 +270,14 @@ config DM_CRYPT

If unsure, say N.

+config DM_INLINE_CRYPT
+ tristate "Inline crypt target support"
+ depends on BLK_DEV_DM || COMPILE_TEST
+ help
+ This inline crypt device-mapper target allows to create a device
+ that transparently encrypts the data on it using inline crypto HW
+ engine.
+
config DM_SNAPSHOT
tristate "Snapshot target"
depends on BLK_DEV_DM
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index 476a214e4bdc..0e09b7665803 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_DM_UNSTRIPED) += dm-unstripe.o
obj-$(CONFIG_DM_BUFIO) += dm-bufio.o
obj-$(CONFIG_DM_BIO_PRISON) += dm-bio-prison.o
obj-$(CONFIG_DM_CRYPT) += dm-crypt.o
+obj-$(CONFIG_DM_INLINE_CRYPT) += dm-inline-crypt.o
obj-$(CONFIG_DM_DELAY) += dm-delay.o
obj-$(CONFIG_DM_DUST) += dm-dust.o
obj-$(CONFIG_DM_FLAKEY) += dm-flakey.o
diff --git a/drivers/md/dm-inline-crypt.c b/drivers/md/dm-inline-crypt.c
new file mode 100644
index 000000000000..e94f86a3a5e0
--- /dev/null
+++ b/drivers/md/dm-inline-crypt.c
@@ -0,0 +1,316 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024, Qualcomm Innovation Center, Inc. All rights reserved
+ *
+ * Based on work by Israel Rukshin file: dm-crypt.c
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/bio.h>
+#include <linux/blkdev.h>
+#include <linux/crypto.h>
+#include <linux/blk-crypto.h>
+#include <linux/device-mapper.h>
+
+#define DM_MSG_PREFIX "inline-crypt"
+
+struct inlinecrypt_config {
+ struct dm_dev *dev;
+ sector_t start;
+ u64 iv_offset;
+ unsigned int iv_size;
+ unsigned short sector_size;
+ unsigned char sector_shift;
+ unsigned int key_size;
+ enum blk_crypto_mode_num crypto_mode;
+ struct blk_crypto_key *blk_key;
+ u8 key[] __counted_by(key_size);
+};
+
+#define DM_CRYPT_DEFAULT_MAX_READ_SIZE 131072
+#define DM_CRYPT_DEFAULT_MAX_WRITE_SIZE 131072
+
+static unsigned int get_max_request_size(struct inlinecrypt_config *cc, bool wrt)
+{
+ unsigned int val, sector_align;
+
+ val = !wrt ? DM_CRYPT_DEFAULT_MAX_READ_SIZE : DM_CRYPT_DEFAULT_MAX_WRITE_SIZE;
+ if (wrt) {
+ if (unlikely(val > BIO_MAX_VECS << PAGE_SHIFT))
+ val = BIO_MAX_VECS << PAGE_SHIFT;
+ }
+ sector_align = max(bdev_logical_block_size(cc->dev->bdev), (unsigned int)cc->sector_size);
+ val = round_down(val, sector_align);
+ if (unlikely(!val))
+ val = sector_align;
+ return val >> SECTOR_SHIFT;
+}
+
+static int crypt_select_inline_crypt_mode(struct dm_target *ti, char *cipher,
+ char *ivmode)
+{
+ struct inlinecrypt_config *cc = ti->private;
+
+ if (strcmp(cipher, "xts(aes128)") == 0) {
+ cc->crypto_mode = BLK_ENCRYPTION_MODE_AES_128_XTS;
+ } else if (strcmp(cipher, "xts(aes256)") == 0) {
+ cc->crypto_mode = BLK_ENCRYPTION_MODE_AES_256_XTS;
+ } else if (strcmp(cipher, "cbc(aes128)") == 0) {
+ cc->crypto_mode = BLK_ENCRYPTION_MODE_AES_128_CBC;
+ } else if (strcmp(cipher, "cbc(aes256)") == 0) {
+ cc->crypto_mode = BLK_ENCRYPTION_MODE_AES_256_CBC;
+ } else {
+ ti->error = "Invalid cipher for inline_crypt";
+ return -EINVAL;
+ }
+
+ cc->iv_size = 4;
+
+ return 0;
+}
+
+static int crypt_prepare_inline_crypt_key(struct inlinecrypt_config *cc)
+{
+ int ret;
+
+ cc->blk_key = kzalloc(sizeof(*cc->blk_key), GFP_KERNEL);
+ if (!cc->blk_key)
+ return -ENOMEM;
+
+ ret = blk_crypto_init_key(cc->blk_key, cc->key, cc->crypto_mode,
+ cc->iv_size, cc->sector_size);
+ if (ret) {
+ DMERR("Failed to init inline encryption key");
+ goto bad_key;
+ }
+
+ ret = blk_crypto_start_using_key(cc->dev->bdev, cc->blk_key);
+ if (ret) {
+ DMERR("Failed to use inline encryption key");
+ goto bad_key;
+ }
+
+ return 0;
+bad_key:
+ kfree_sensitive(cc->blk_key);
+ cc->blk_key = NULL;
+ return ret;
+}
+
+static void crypt_destroy_inline_crypt_key(struct inlinecrypt_config *cc)
+{
+ if (cc->blk_key) {
+ blk_crypto_evict_key(cc->dev->bdev, cc->blk_key);
+ kfree_sensitive(cc->blk_key);
+ cc->blk_key = NULL;
+ }
+}
+
+static void crypt_inline_encrypt_submit(struct dm_target *ti, struct bio *bio)
+{
+ struct inlinecrypt_config *cc = ti->private;
+ u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE];
+
+ bio_set_dev(bio, cc->dev->bdev);
+ if (bio_sectors(bio)) {
+ memset(dun, 0, BLK_CRYPTO_MAX_IV_SIZE);
+ bio->bi_iter.bi_sector = cc->start +
+ dm_target_offset(ti, bio->bi_iter.bi_sector);
+ dun[0] = le64_to_cpu(bio->bi_iter.bi_sector + cc->iv_offset);
+ bio_crypt_set_ctx(bio, cc->blk_key, dun, GFP_KERNEL);
+ }
+
+ submit_bio_noacct(bio);
+}
+
+static int inlinecrypt_setkey(struct inlinecrypt_config *cc)
+{
+ crypt_destroy_inline_crypt_key(cc);
+
+ return crypt_prepare_inline_crypt_key(cc);
+
+ return 0;
+}
+
+static int inlinecrypt_set_key(struct inlinecrypt_config *cc, char *key)
+{
+ int r = -EINVAL;
+ int key_string_len = strlen(key);
+
+ /* Decode key from its hex representation. */
+ if (cc->key_size && hex2bin(cc->key, key, cc->key_size) < 0)
+ goto out;
+
+ r = inlinecrypt_setkey(cc);
+out:
+ memset(key, '0', key_string_len);
+
+ return r;
+}
+
+static void inlinecrypt_dtr(struct dm_target *ti)
+{
+ struct inlinecrypt_config *cc = ti->private;
+
+ ti->private = NULL;
+
+ if (!cc)
+ return;
+
+ crypt_destroy_inline_crypt_key(cc);
+
+ if (cc->dev)
+ dm_put_device(ti, cc->dev);
+
+ kfree_sensitive(cc);
+}
+
+static int inlinecrypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+ struct inlinecrypt_config *cc;
+ char *cipher_api = NULL;
+ char *cipher, *chainmode;
+ unsigned long long tmpll;
+ char *ivmode;
+ int key_size;
+ char dummy;
+ int ret;
+
+ if (argc < 5) {
+ ti->error = "Not enough arguments";
+ return -EINVAL;
+ }
+
+ key_size = strlen(argv[1]) >> 1;
+
+ cc = kzalloc(struct_size(cc, key, key_size), GFP_KERNEL);
+ if (!cc) {
+ ti->error = "Cannot allocate encryption context";
+ return -ENOMEM;
+ }
+ cc->key_size = key_size;
+ cc->sector_size = (1 << SECTOR_SHIFT);
+ cc->sector_shift = 0;
+
+ ti->private = cc;
+
+ if ((sscanf(argv[2], "%llu%c", &tmpll, &dummy) != 1) ||
+ (tmpll & ((cc->sector_size >> SECTOR_SHIFT) - 1))) {
+ ti->error = "Invalid iv_offset sector";
+ goto bad;
+ }
+ cc->iv_offset = tmpll;
+
+ ret = dm_get_device(ti, argv[3], dm_table_get_mode(ti->table),
+ &cc->dev);
+ if (ret) {
+ ti->error = "Device lookup failed";
+ goto bad;
+ }
+
+ ret = -EINVAL;
+ if (sscanf(argv[4], "%llu%c", &tmpll, &dummy) != 1 ||
+ tmpll != (sector_t)tmpll) {
+ ti->error = "Invalid device sector";
+ goto bad;
+ }
+
+ cc->start = tmpll;
+
+ cipher = strsep(&argv[0], "-");
+ chainmode = strsep(&argv[0], "-");
+ ivmode = strsep(&argv[0], "-");
+
+ cipher_api = kmalloc(CRYPTO_MAX_ALG_NAME, GFP_KERNEL);
+ if (!cipher_api)
+ goto bad;
+
+ ret = snprintf(cipher_api, CRYPTO_MAX_ALG_NAME,
+ "%s(%s)", chainmode, cipher);
+ if (ret < 0 || ret >= CRYPTO_MAX_ALG_NAME) {
+ kfree(cipher_api);
+ ret = -ENOMEM;
+ goto bad;
+ }
+
+ ret = crypt_select_inline_crypt_mode(ti, cipher_api, ivmode);
+
+ /* Initialize and set key */
+ ret = inlinecrypt_set_key(cc, argv[1]);
+ if (ret < 0) {
+ ti->error = "Error decoding and setting key";
+ return ret;
+ }
+
+ return 0;
+bad:
+ ti->error = "Error in inlinecrypt mapping";
+ inlinecrypt_dtr(ti);
+ return ret;
+}
+
+static int inlinecrypt_map(struct dm_target *ti, struct bio *bio)
+{
+ struct inlinecrypt_config *cc = ti->private;
+ unsigned int max_sectors;
+
+ /*
+ * If bio is REQ_PREFLUSH or REQ_OP_DISCARD, just bypass crypt queues.
+ * - for REQ_PREFLUSH device-mapper core ensures that no IO is in-flight
+ * - for REQ_OP_DISCARD caller must use flush if IO ordering matters
+ */
+ if (unlikely(bio->bi_opf & REQ_PREFLUSH ||
+ bio_op(bio) == REQ_OP_DISCARD)) {
+ bio_set_dev(bio, cc->dev->bdev);
+ if (bio_sectors(bio))
+ bio->bi_iter.bi_sector = cc->start +
+ dm_target_offset(ti, bio->bi_iter.bi_sector);
+ return DM_MAPIO_REMAPPED;
+ }
+
+ /*
+ * Check if bio is too large, split as needed.
+ */
+ max_sectors = get_max_request_size(cc, bio_data_dir(bio) == WRITE);
+ if (unlikely(bio_sectors(bio) > max_sectors))
+ dm_accept_partial_bio(bio, max_sectors);
+
+ /*
+ * Ensure that bio is a multiple of internal sector eninlinecryption size
+ * and is aligned to this size as defined in IO hints.
+ */
+ if (unlikely((bio->bi_iter.bi_sector & ((cc->sector_size >> SECTOR_SHIFT) - 1)) != 0))
+ return DM_MAPIO_KILL;
+
+ if (unlikely(bio->bi_iter.bi_size & (cc->sector_size - 1)))
+ return DM_MAPIO_KILL;
+
+ crypt_inline_encrypt_submit(ti, bio);
+ return DM_MAPIO_SUBMITTED;
+
+ return 0;
+}
+
+static int inlinecrypt_iterate_devices(struct dm_target *ti,
+ iterate_devices_callout_fn fn, void *data)
+{
+ struct inlinecrypt_config *cc = ti->private;
+
+ return fn(ti, cc->dev, cc->start, ti->len, data);
+}
+
+static struct target_type inlinecrypt_target = {
+ .name = "inline-crypt",
+ .version = {1, 0, 0},
+ .module = THIS_MODULE,
+ .ctr = inlinecrypt_ctr,
+ .dtr = inlinecrypt_dtr,
+ .map = inlinecrypt_map,
+ .iterate_devices = inlinecrypt_iterate_devices,
+};
+module_dm(inlinecrypt);
+
+MODULE_AUTHOR("Md Sadre Alam <quic_mdalam@quicinc.com>");
+MODULE_DESCRIPTION(DM_NAME " target for inline encryption / decryption");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/blk-crypto.h b/include/linux/blk-crypto.h
index 5e5822c18ee4..da503a05c5f6 100644
--- a/include/linux/blk-crypto.h
+++ b/include/linux/blk-crypto.h
@@ -10,10 +10,13 @@

enum blk_crypto_mode_num {
BLK_ENCRYPTION_MODE_INVALID,
+ BLK_ENCRYPTION_MODE_AES_128_XTS,
BLK_ENCRYPTION_MODE_AES_256_XTS,
BLK_ENCRYPTION_MODE_AES_128_CBC_ESSIV,
BLK_ENCRYPTION_MODE_ADIANTUM,
BLK_ENCRYPTION_MODE_SM4_XTS,
+ BLK_ENCRYPTION_MODE_AES_128_CBC,
+ BLK_ENCRYPTION_MODE_AES_256_CBC,
BLK_ENCRYPTION_MODE_MAX,
};

--
2.34.1