From: Simon Glass <simon.glass@canonical.com> Create a new blkmap_crypt.c file to hold the LUKS code, since it is fairly large. Add an internal header for blkmap as well. Signed-off-by: Simon Glass <simon.glass@canonical.com> --- drivers/block/Makefile | 1 + drivers/block/blkmap.c | 204 +------------------------------- drivers/block/blkmap_crypt.c | 172 +++++++++++++++++++++++++++ drivers/block/blkmap_internal.h | 74 ++++++++++++ 4 files changed, 249 insertions(+), 202 deletions(-) create mode 100644 drivers/block/blkmap_crypt.c create mode 100644 drivers/block/blkmap_internal.h diff --git a/drivers/block/Makefile b/drivers/block/Makefile index 538b602790d..0735efea719 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -17,6 +17,7 @@ endif obj-$(CONFIG_SANDBOX) += sandbox.o host-uclass.o host_dev.o obj-$(CONFIG_$(PHASE_)BLOCK_CACHE) += blkcache.o obj-$(CONFIG_$(PHASE_)BLKMAP) += blkmap.o +obj-$(CONFIG_BLK_LUKS) += blkmap_crypt.o obj-$(CONFIG_$(PHASE_)BLKMAP) += blkmap_helper.o obj-$(CONFIG_EFI_MEDIA) += efi-media-uclass.o diff --git a/drivers/block/blkmap.c b/drivers/block/blkmap.c index 13a79001979..14aa1e4d088 100644 --- a/drivers/block/blkmap.c +++ b/drivers/block/blkmap.c @@ -11,62 +11,11 @@ #include <mapmem.h> #include <memalign.h> #include <part.h> -#include <uboot_aes.h> #include <dm/device-internal.h> #include <dm/lists.h> #include <dm/root.h> #include <linux/string.h> - -struct blkmap; - -/** - * struct blkmap_slice - Region mapped to a blkmap - * - * Common data for a region mapped to a blkmap, specialized by each - * map type. - * - * @node: List node used to associate this slice with a blkmap - * @blknr: Start block number of the mapping - * @blkcnt: Number of blocks covered by this mapping - */ -struct blkmap_slice { - struct list_head node; - - lbaint_t blknr; - lbaint_t blkcnt; - - /** - * @read: - Read from slice - * - * @read.bm: Blkmap to which this slice belongs - * @read.bms: This slice - * @read.blknr: Start block number to read from - * @read.blkcnt: Number of blocks to read - * @read.buffer: Buffer to store read data to - */ - ulong (*read)(struct blkmap *bm, struct blkmap_slice *bms, - lbaint_t blknr, lbaint_t blkcnt, void *buffer); - - /** - * @write: - Write to slice - * - * @write.bm: Blkmap to which this slice belongs - * @write.bms: This slice - * @write.blknr: Start block number to write to - * @write.blkcnt: Number of blocks to write - * @write.buffer: Data to be written - */ - ulong (*write)(struct blkmap *bm, struct blkmap_slice *bms, - lbaint_t blknr, lbaint_t blkcnt, const void *buffer); - - /** - * @destroy: - Tear down slice - * - * @read.bm: Blkmap to which this slice belongs - * @read.bms: This slice - */ - void (*destroy)(struct blkmap *bm, struct blkmap_slice *bms); -}; +#include "blkmap_internal.h" static bool blkmap_slice_contains(struct blkmap_slice *bms, lbaint_t blknr) { @@ -92,7 +41,7 @@ static bool blkmap_slice_available(struct blkmap *bm, struct blkmap_slice *new) return true; } -static int blkmap_slice_add(struct blkmap *bm, struct blkmap_slice *new) +int blkmap_slice_add(struct blkmap *bm, struct blkmap_slice *new) { struct blk_desc *bd = dev_get_uclass_plat(bm->blk); struct list_head *insert = &bm->slices; @@ -293,155 +242,6 @@ int blkmap_map_pmem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, return err; } -/** - * struct blkmap_crypt - Encrypted device mapping - * - * @slice: Common map data - * @blk: Target encrypted block device - * @blknr: Start block number of encrypted data on target device - * @master_key: Decrypted master key for this mapping - * @key_size: Size of master key in bytes - * @payload_offset: LUKS payload offset in sectors - * @use_essiv: True if ESSIV mode is used for IV generation - * @essiv_key: ESSIV key (SHA256 hash of master key) - */ -struct blkmap_crypt { - struct blkmap_slice slice; - struct udevice *blk; - lbaint_t blknr; - u8 master_key[128]; - u32 key_size; - u32 payload_offset; - bool use_essiv; - u8 essiv_key[32]; -}; - -static ulong blkmap_crypt_read(struct blkmap *bm, struct blkmap_slice *bms, - lbaint_t blknr, lbaint_t blkcnt, void *buffer) -{ - struct blkmap_crypt *bmc = container_of(bms, struct blkmap_crypt, slice); - struct blk_desc *bd = dev_get_uclass_plat(bm->blk); - struct blk_desc *src_bd = dev_get_uclass_plat(bmc->blk); - lbaint_t src_blknr, blocks_read; - u8 *encrypted_buf, *dest = buffer; - u8 expkey[AES256_EXPAND_KEY_LENGTH]; - u8 iv[AES_BLOCK_LENGTH]; - u64 sector; - lbaint_t i; - - /* Allocate buffer for encrypted data */ - encrypted_buf = malloc_cache_aligned(blkcnt * src_bd->blksz); - if (!encrypted_buf) - return 0; - - /* - * Calculate source block number (LUKS payload offset + requested - * block) - */ - src_blknr = bmc->blknr + bmc->payload_offset + blknr; - - /* Read encrypted data from underlying device */ - blocks_read = blk_read(bmc->blk, src_blknr, blkcnt, encrypted_buf); - if (blocks_read != blkcnt) { - free(encrypted_buf); - return 0; - } - - /* Expand AES key */ - aes_expand_key(bmc->master_key, bmc->key_size * 8, expkey); - - /* Decrypt each sector */ - for (i = 0; i < blkcnt; i++) { - /* Calculate sector number for IV */ - sector = blknr + i; - - if (bmc->use_essiv) { - /* - * ESSIV mode: - * IV = AES_encrypt(sector_number, SHA256(master_key)) - */ - u8 essiv_expkey[AES256_EXPAND_KEY_LENGTH]; - u8 sector_iv[AES_BLOCK_LENGTH]; - - /* Create sector number as IV input (little-endian) */ - memset(sector_iv, 0, sizeof(sector_iv)); - *(u64 *)sector_iv = cpu_to_le64(sector); - - /* Expand ESSIV key */ - aes_expand_key(bmc->essiv_key, 256, essiv_expkey); - - /* Encrypt sector number with ESSIV key to get IV */ - aes_encrypt(256, sector_iv, essiv_expkey, iv); - } else { - /* - * Plain64 mode: - * IV is sector number in little-endian format - */ - memset(iv, '\0', sizeof(iv)); - *(u64 *)iv = cpu_to_le64(sector); - } - - /* Decrypt sector using AES-CBC */ - aes_cbc_decrypt_blocks(bmc->key_size * 8, expkey, iv, - encrypted_buf + i * bd->blksz, - dest + i * bd->blksz, - bd->blksz / AES_BLOCK_LENGTH); - } - free(encrypted_buf); - - return blkcnt; -} - -static void blkmap_crypt_destroy(struct blkmap *bm, struct blkmap_slice *bms) -{ - struct blkmap_crypt *bmc = container_of(bms, struct blkmap_crypt, slice); - - /* Securely wipe master key before freeing */ - memset(bmc->master_key, 0, sizeof(bmc->master_key)); - free(bmc); -} - -int blkmap_map_crypt(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, - struct udevice *lblk, lbaint_t lblknr, - const u8 *master_key, u32 key_size, u32 payload_offset, - bool use_essiv, const u8 *essiv_key) -{ - struct blkmap *bm = dev_get_plat(dev); - struct blkmap_crypt *bmc; - int err; - - if (key_size > 128) - return -EINVAL; - - bmc = malloc(sizeof(*bmc)); - if (!bmc) - return -ENOMEM; - - bmc->blk = lblk; - bmc->blknr = lblknr; - bmc->key_size = key_size; - bmc->payload_offset = payload_offset; - bmc->use_essiv = use_essiv; - memcpy(bmc->master_key, master_key, key_size); - - if (use_essiv && essiv_key) - memcpy(bmc->essiv_key, essiv_key, sizeof(bmc->essiv_key)); - else - memset(bmc->essiv_key, 0, sizeof(bmc->essiv_key)); - - bmc->slice.blknr = blknr; - bmc->slice.blkcnt = blkcnt; - bmc->slice.read = blkmap_crypt_read; - bmc->slice.write = NULL; /* Read-only for now */ - bmc->slice.destroy = blkmap_crypt_destroy; - - err = blkmap_slice_add(bm, &bmc->slice); - if (err) - free(bmc); - - return err; -} - static ulong blkmap_blk_read_slice(struct blkmap *bm, struct blkmap_slice *bms, lbaint_t blknr, lbaint_t blkcnt, void *buffer) diff --git a/drivers/block/blkmap_crypt.c b/drivers/block/blkmap_crypt.c new file mode 100644 index 00000000000..bc77ddc2751 --- /dev/null +++ b/drivers/block/blkmap_crypt.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2025 Canonical Ltd + * Author: Simon Glass <simon.glass@canonical.com> + * + * LUKS encryption/decryption support + */ + +#include <blk.h> +#include <blkmap.h> +#include <dm.h> +#include <malloc.h> +#include <memalign.h> +#include <uboot_aes.h> +#include <mbedtls/aes.h> +#include <dm/device-internal.h> +#include <linux/string.h> +#include "blkmap_internal.h" + +/** + * struct blkmap_crypt - Encrypted mapping + * + * Data associated with an encrypted region of a block device (e.g., LUKS). + * Provides on-the-fly decryption of data using AES-CBC or AES-XTS modes. + * + * @slice: Common slice data (must be first member) + * @blk: Underlying block device containing encrypted data + * @blknr: Start block of the underlying block device + * @master_key: Decrypted master key for decryption + * @key_size: Size of the master key in bytes (must be <= 128) + * @payload_offset: Offset in sectors from lblknr to actual encrypted payload + * @sector_size: Sector size for IV calculation (typically 512 or 4096) + * @use_essiv: True if ESSIV mode is used for IV generation (CBC only) + * @essiv_key: ESSIV key (SHA256 hash of master key) + */ +struct blkmap_crypt { + struct blkmap_slice slice; + struct udevice *blk; + lbaint_t blknr; + u8 master_key[128]; + u32 key_size; + u32 payload_offset; + u32 sector_size; + bool use_essiv; + u8 essiv_key[32]; +}; + +static ulong blkmap_crypt_read(struct blkmap *bm, struct blkmap_slice *bms, + lbaint_t blknr, lbaint_t blkcnt, void *buffer) +{ + struct blkmap_crypt *bmc = container_of(bms, struct blkmap_crypt, slice); + struct blk_desc *bd = dev_get_uclass_plat(bm->blk); + struct blk_desc *src_bd = dev_get_uclass_plat(bmc->blk); + lbaint_t src_blknr, blocks_read; + u8 *encrypted_buf, *dest = buffer; + u8 expkey[AES256_EXPAND_KEY_LENGTH]; + u8 iv[AES_BLOCK_LENGTH]; + u64 sector; + lbaint_t i; + + /* Allocate buffer for encrypted data */ + encrypted_buf = malloc_cache_aligned(blkcnt * src_bd->blksz); + if (!encrypted_buf) + return 0; + + /* + * Calculate source block number (LUKS payload offset + requested + * block) + */ + src_blknr = bmc->blknr + bmc->payload_offset + blknr; + + /* Read encrypted data from underlying device */ + blocks_read = blk_read(bmc->blk, src_blknr, blkcnt, encrypted_buf); + if (blocks_read != blkcnt) { + free(encrypted_buf); + return 0; + } + + /* Expand AES key */ + aes_expand_key(bmc->master_key, bmc->key_size * 8, expkey); + + /* Decrypt each sector */ + for (i = 0; i < blkcnt; i++) { + /* Calculate sector number for IV */ + sector = blknr + i; + + if (bmc->use_essiv) { + /* + * ESSIV mode: + * IV = AES_encrypt(sector_number, SHA256(master_key)) + */ + u8 essiv_expkey[AES256_EXPAND_KEY_LENGTH]; + u8 sector_iv[AES_BLOCK_LENGTH]; + + /* Create sector number as IV input (little-endian) */ + memset(sector_iv, '\0', sizeof(sector_iv)); + *(u64 *)sector_iv = cpu_to_le64(sector); + + /* Expand ESSIV key */ + aes_expand_key(bmc->essiv_key, 256, essiv_expkey); + + /* Encrypt sector number with ESSIV key to get IV */ + aes_encrypt(256, sector_iv, essiv_expkey, iv); + } else { + /* + * Plain64 mode: + * IV is sector number in little-endian format + */ + memset(iv, '\0', sizeof(iv)); + *(u64 *)iv = cpu_to_le64(sector); + } + + /* Decrypt sector using AES-CBC */ + aes_cbc_decrypt_blocks(bmc->key_size * 8, expkey, iv, + encrypted_buf + i * bd->blksz, + dest + i * bd->blksz, + bd->blksz / AES_BLOCK_LENGTH); + } + free(encrypted_buf); + + return blkcnt; +} + +static void blkmap_crypt_destroy(struct blkmap *bm, struct blkmap_slice *bms) +{ + struct blkmap_crypt *bmc = container_of(bms, struct blkmap_crypt, slice); + + /* Securely wipe master key before freeing */ + memset(bmc->master_key, '\0', sizeof(bmc->master_key)); + free(bmc); +} + +int blkmap_map_crypt(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, + struct udevice *lblk, lbaint_t lblknr, + const u8 *master_key, u32 key_size, u32 payload_offset, + bool use_essiv, const u8 *essiv_key) +{ + struct blkmap *bm = dev_get_plat(dev); + struct blkmap_crypt *bmc; + int err; + + if (key_size > 128) + return -EINVAL; + + bmc = malloc(sizeof(*bmc)); + if (!bmc) + return -ENOMEM; + + bmc->blk = lblk; + bmc->blknr = lblknr; + bmc->key_size = key_size; + bmc->payload_offset = payload_offset; + bmc->use_essiv = use_essiv; + memcpy(bmc->master_key, master_key, key_size); + + if (use_essiv && essiv_key) + memcpy(bmc->essiv_key, essiv_key, sizeof(bmc->essiv_key)); + else + memset(bmc->essiv_key, '\0', sizeof(bmc->essiv_key)); + + bmc->slice.blknr = blknr; + bmc->slice.blkcnt = blkcnt; + bmc->slice.read = blkmap_crypt_read; + bmc->slice.write = NULL; /* Read-only for now */ + bmc->slice.destroy = blkmap_crypt_destroy; + + err = blkmap_slice_add(bm, &bmc->slice); + if (err) + free(bmc); + + return err; +} diff --git a/drivers/block/blkmap_internal.h b/drivers/block/blkmap_internal.h new file mode 100644 index 00000000000..07947c7ef0b --- /dev/null +++ b/drivers/block/blkmap_internal.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2023 Addiva Elektronik + * Author: Tobias Waldekranz <tobias@waldekranz.com> + * + * Internal blkmap structures and functions + */ + +#ifndef _BLKMAP_INTERNAL_H +#define _BLKMAP_INTERNAL_H + +#include <dm/lists.h> + +struct blkmap; + +/** + * struct blkmap_slice - Region mapped to a blkmap + * + * Common data for a region mapped to a blkmap, specialized by each + * map type. + * + * @node: List node used to associate this slice with a blkmap + * @blknr: Start block number of the mapping + * @blkcnt: Number of blocks covered by this mapping + */ +struct blkmap_slice { + struct list_head node; + + lbaint_t blknr; + lbaint_t blkcnt; + + /** + * @read: - Read from slice + * + * @read.bm: Blkmap to which this slice belongs + * @read.bms: This slice + * @read.blknr: Start block number to read from + * @read.blkcnt: Number of blocks to read + * @read.buffer: Buffer to store read data to + */ + ulong (*read)(struct blkmap *bm, struct blkmap_slice *bms, + lbaint_t blknr, lbaint_t blkcnt, void *buffer); + + /** + * @write: - Write to slice + * + * @write.bm: Blkmap to which this slice belongs + * @write.bms: This slice + * @write.blknr: Start block number to write to + * @write.blkcnt: Number of blocks to write + * @write.buffer: Data to be written + */ + ulong (*write)(struct blkmap *bm, struct blkmap_slice *bms, + lbaint_t blknr, lbaint_t blkcnt, const void *buffer); + + /** + * @destroy: - Tear down slice + * + * @read.bm: Blkmap to which this slice belongs + * @read.bms: This slice + */ + void (*destroy)(struct blkmap *bm, struct blkmap_slice *bms); +}; + +/** + * blkmap_slice_add() - Add a slice to a blkmap + * + * @bm: Blkmap to add the slice to + * @new: New slice to add + * Returns: 0 on success, negative error code on failure + */ +int blkmap_slice_add(struct blkmap *bm, struct blkmap_slice *new); + +#endif /* _BLKMAP_INTERNAL_H */ -- 2.43.0