From: Simon Glass <sjg@chromium.org> Enhance blkmap to support decrypting a partition encrypted with LUKS version 1. This will allow filesystems to access files on the parition. This will be tested once filesystems support is plumbed in. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/block/blkmap.c | 152 +++++++++++++++++++++++++++++++++++++++++ include/blkmap.h | 24 +++++++ 2 files changed, 176 insertions(+) diff --git a/drivers/block/blkmap.c b/drivers/block/blkmap.c index 34eed1380dc..13a79001979 100644 --- a/drivers/block/blkmap.c +++ b/drivers/block/blkmap.c @@ -9,10 +9,13 @@ #include <dm.h> #include <malloc.h> #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; @@ -290,6 +293,155 @@ 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/include/blkmap.h b/include/blkmap.h index d53095437fa..a0f46748b92 100644 --- a/include/blkmap.h +++ b/include/blkmap.h @@ -65,6 +65,30 @@ int blkmap_map_mem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, int blkmap_map_pmem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, phys_addr_t paddr); +/** + * blkmap_map_crypt() - Map region of encrypted device + * + * Creates a mapping that performs on-the-fly decryption of the specified + * region from another block device. Suitable for LUKS and other encrypted + * block devices. + * + * @dev: Blkmap to create the mapping on + * @blknr: Start block number of the mapping + * @blkcnt: Number of blocks to map + * @lblk: The target block device containing encrypted data + * @lblknr: The start block number of the encrypted partition + * @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 + * @use_essiv: True to use ESSIV mode, false for plain64 mode + * @essiv_key: ESSIV key (SHA256 of master key), or NULL if use_essiv is false + * Returns: 0 on success, negative error code on failure + */ +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); + /** * blkmap_from_label() - Find blkmap from label * -- 2.43.0