@@ -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
@@ -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)
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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 */