@@ -29,6 +29,7 @@
* @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
+ * @cipher_mode: Cipher mode (CBC or XTS)
* @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)
@@ -40,11 +41,202 @@ struct blkmap_crypt {
u8 master_key[128];
u32 key_size;
u32 payload_offset;
+ enum blkmap_crypt_mode cipher_mode;
u32 sector_size;
bool use_essiv;
u8 essiv_key[32];
};
+/**
+ * process_xts_sector() - Read, decrypt and copy one XTS sector
+ *
+ * This reads an encrypted sector from disk, decrypts it using AES-XTS with
+ * the plain64 IV mode, and copies the requested portion to the output buffer.
+ * Handles partial sector reads at the start and end of the requested range.
+ *
+ * @bmc: Blkmap crypt context with key and device info
+ * @ctx: Initialized AES-XTS context
+ * @cur_sector: Current XTS sector number being processed
+ * @start_sector: First XTS sector in the requested range
+ * @end_sector: Last XTS sector in the requested range
+ * @blks_per_sect: Number of 512-byte blocks per XTS sector
+ * @offset_in_first_sector: Byte offset within first sector to start copying
+ * @blkcnt: Total number of blocks requested
+ * @buf: Buffer for reading/decrypting one full sector
+ * @dest: Output buffer for decrypted data
+ * @blksz: Block-device block size (typically 512 bytes)
+ * @blocks_donep: Count of blocks copied so far (updated)
+ * Return: 0 on success, -EIO on read failure, other negative on decrypt failure
+ */
+static int process_xts_sector(struct blkmap_crypt *bmc,
+ mbedtls_aes_xts_context *ctx, lbaint_t cur_sector,
+ lbaint_t start_sector, lbaint_t end_sector,
+ uint blks_per_sect, uint offset_in_first_sector,
+ lbaint_t blkcnt, u8 *buf, u8 *dest, uint blksz,
+ lbaint_t *blocks_donep)
+{
+ lbaint_t start_blk = cur_sector * blks_per_sect;
+ lbaint_t src_blk = bmc->blknr + bmc->payload_offset + start_blk;
+ uint copy_offset = 0;
+ lbaint_t iv_sector;
+ u8 data_unit[16];
+ uint copy_len;
+ lbaint_t j;
+ int ret;
+
+ log_debug("XTS: cur_sector=%lu bmc->blknr=%lu bmc->payload_offset=%u src_blk=%lu\n",
+ cur_sector, bmc->blknr, bmc->payload_offset, src_blk);
+
+ /* Read entire sector from disk */
+ if (blk_read(bmc->blk, src_blk, blks_per_sect, buf) !=
+ blks_per_sect) {
+ log_err("Failed to read sector %lu\n", cur_sector);
+ return -EIO;
+ }
+
+ /*
+ * Prepare data_unit (IV) for XTS decryption.
+ * For plain64 IV mode, the IV is the 512-byte sector number,
+ * not the larger XTS sector number. This matches dm-crypt behavior.
+ */
+ iv_sector = start_blk;
+ memset(data_unit, '\0', sizeof(data_unit));
+ for (j = 0; j < 8; j++)
+ data_unit[j] = (iv_sector >> (j * 8)) & 0xff;
+
+ /* Decrypt entire sector */
+ ret = mbedtls_aes_crypt_xts(ctx, MBEDTLS_AES_DECRYPT, bmc->sector_size,
+ data_unit, buf, buf);
+ if (ret) {
+ log_err("XTS decrypt sector %lu failed: %d\n", cur_sector, ret);
+ return ret;
+ }
+
+ /* Calculate which portion of this sector to copy */
+ if (cur_sector == start_sector)
+ copy_offset = offset_in_first_sector;
+
+ if (cur_sector == end_sector) {
+ /* Last sector: copy only up to the end of requested data */
+ uint remaining = (blkcnt - *blocks_donep) * blksz;
+
+ copy_len = remaining;
+ } else {
+ /* Not the last sector: copy from offset to end of sector */
+ copy_len = bmc->sector_size - copy_offset;
+ }
+
+ /* Copy decrypted data to output buffer */
+ memcpy(dest + *blocks_donep * blksz, buf + copy_offset, copy_len);
+ *blocks_donep += copy_len / blksz;
+
+ return 0;
+}
+
+/**
+ * crypt_read_xts() - Decrypt data using AES-XTS cipher mode
+ *
+ * Decrypts blocks from an encrypted device using AES-XTS with plain64 IV mode.
+ * Handles requests that span multiple XTS sectors and partial sector reads.
+ * The IV for each XTS sector is the 512-byte block number (not the larger
+ * XTS sector number), matching dm-crypt's plain64 IV generation.
+ *
+ * @bm: Blkmap device context
+ * @bmc: Blkmap crypt context with encryption parameters
+ * @blknr: Starting block number (relative to decrypted device)
+ * @blkcnt: Number of blocks to read
+ * @buffer: Output buffer for decrypted data
+ * Return: number of blocks successfully decrypted, or negative error code
+ * (-ENOMEM if buffer allocation failed, -EINVAL if key setup failed,
+ * -EIO or other negative on sector read/decrypt failure)
+ */
+static ulong crypt_read_xts(struct blkmap *bm, struct blkmap_crypt *bmc,
+ lbaint_t blknr, lbaint_t blkcnt, void *out_buf)
+{
+ struct blk_desc *bd = dev_get_uclass_plat(bm->blk);
+ lbaint_t start_sector, end_sector, cur_sect;
+ uint offset_in_first_sector;
+ mbedtls_aes_xts_context ctx;
+ lbaint_t blocks_done;
+ uint blks_per_sect;
+ u8 *buf;
+ int ret;
+
+ blks_per_sect = bmc->sector_size / bd->blksz;
+
+ log_debug("key_size=%u blkcnt=%lu\n", bmc->key_size, blkcnt);
+ log_debug("XTS: sector_size=%u blocks_per_sector=%u\n",
+ bmc->sector_size, blks_per_sect);
+ log_debug("Master key (all %u bytes):\n", bmc->key_size);
+ log_debug_hex("", bmc->master_key, bmc->key_size);
+
+ /* Calculate which encryption sectors we need */
+ start_sector = blknr / blks_per_sect;
+ end_sector = (blknr + blkcnt - 1) / blks_per_sect;
+ offset_in_first_sector = (blknr % blks_per_sect) * bd->blksz;
+
+ log_debug("XTS: blknr=%lu blkcnt=%lu start_sector=%lu end_sector=%lu offset=%u\n",
+ blknr, blkcnt, start_sector, end_sector,
+ offset_in_first_sector);
+
+ /* Allocate buffer for one full sector */
+ buf = malloc_cache_aligned(bmc->sector_size);
+ if (!buf) {
+ log_err("Failed to allocate sector buffer\n");
+ return -ENOMEM;
+ }
+
+ mbedtls_aes_xts_init(&ctx);
+ ret = mbedtls_aes_xts_setkey_dec(&ctx, bmc->master_key,
+ bmc->key_size * 8);
+ if (ret) {
+ log_err("XTS setkey_dec failed: %d\n", ret);
+ mbedtls_aes_xts_free(&ctx);
+ free(buf);
+ return -EINVAL;
+ }
+
+ /* Process each sector */
+ blocks_done = 0;
+ for (cur_sect = start_sector; cur_sect <= end_sector; cur_sect++) {
+ ret = process_xts_sector(bmc, &ctx, cur_sect, start_sector,
+ end_sector, blks_per_sect,
+ offset_in_first_sector, blkcnt,
+ buf, out_buf, bd->blksz, &blocks_done);
+ if (ret) {
+ mbedtls_aes_xts_free(&ctx);
+ free(buf);
+ return ret;
+ }
+ }
+
+ free(buf);
+ mbedtls_aes_xts_free(&ctx);
+
+ log_debug("XTS decryption completed successfully for %lu blocks\n", blkcnt);
+ if (blknr == 0 && blkcnt >= 1) {
+ log_debug("First 32 bytes of decrypted data:\n");
+ log_debug_hex("", out_buf, 32);
+ }
+
+ return blkcnt;
+}
+
+/**
+ * crypt_read_cbc() - Decrypt data using AES-CBC cipher mode
+ *
+ * Decrypts blocks from an encrypted device using AES-CBC. Supports both
+ * plain64 mode (IV = sector number) and ESSIV mode (IV = AES_encrypt(sector
+ * number, SHA256(master_key))). Used for LUKS1 volumes.
+ *
+ * @bm: Blkmap device context
+ * @bmc: Blkmap crypt context with encryption parameters and ESSIV key
+ * @blknr: Starting block number (relative to decrypted device)
+ * @blkcnt: Number of blocks to decrypt
+ * @encrypted_buf: Buffer containing encrypted data (already read from disk)
+ * @buffer: Output buffer for decrypted data
+ * Return: number of blocks successfully decrypted
+ */
static ulong crypt_read_cbc(struct blkmap *bm, struct blkmap_crypt *bmc,
lbaint_t blknr, lbaint_t blkcnt,
u8 *encrypted_buf, void *buffer)
@@ -132,6 +324,16 @@ static ulong blkmap_crypt_read(struct blkmap *bm, struct blkmap_slice *bms,
log_debug_hex("", encrypted_buf, 32);
}
+ if (bmc->cipher_mode == BLKMAP_CRYPT_MODE_XTS) {
+ result = crypt_read_xts(bm, bmc, blknr, blkcnt, buffer);
+ /* XTS reads its own data, so free encrypted_buf early */
+ free(encrypted_buf);
+ /* Check for error - result will be negative on failure */
+ if ((long)result < 0)
+ return 0;
+ return result;
+ }
+
result = crypt_read_cbc(bm, bmc, blknr, blkcnt, encrypted_buf, buffer);
free(encrypted_buf);
@@ -150,6 +352,7 @@ static void blkmap_crypt_destroy(struct blkmap *bm, struct blkmap_slice *bms)
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,
+ enum blkmap_crypt_mode cipher_mode, u32 sector_size,
bool use_essiv, const u8 *essiv_key)
{
struct blkmap *bm = dev_get_plat(dev);
@@ -167,6 +370,8 @@ int blkmap_map_crypt(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
bmc->blknr = lblknr;
bmc->key_size = key_size;
bmc->payload_offset = payload_offset;
+ bmc->cipher_mode = cipher_mode;
+ bmc->sector_size = sector_size;
bmc->use_essiv = use_essiv;
memcpy(bmc->master_key, master_key, key_size);
@@ -596,6 +596,7 @@ int luks_create_blkmap(struct udevice *blk, struct disk_partition *pinfo,
struct udevice **blkmapp)
{
u8 essiv_key[SHA256_SUM_LEN]; /* SHA-256 output */
+ enum blkmap_crypt_mode cipher_mode;
lbaint_t decrypted_size;
struct luks1_phdr *hdr;
struct luks2_hdr *hdr2;
@@ -603,6 +604,7 @@ int luks_create_blkmap(struct udevice *blk, struct disk_partition *pinfo,
struct udevice *dev;
uint payload_offset;
int ret, version;
+ u32 sector_size;
bool use_essiv;
if (!blk || !pinfo || !master_key || !label || !blkmapp)
@@ -698,12 +700,26 @@ int luks_create_blkmap(struct udevice *blk, struct disk_partition *pinfo,
/* Convert byte offset to sectors */
payload_offset = segment_offset / desc->blksz;
- /* Check if ESSIV mode is used */
+ /* Parse cipher mode from encryption string */
encryption = ofnode_read_string(segment0_node, "encryption");
- if (encryption)
+ if (encryption) {
use_essiv = strstr(encryption, "essiv");
- else
+ /* Check if XTS mode is used */
+ if (strstr(encryption, "xts"))
+ cipher_mode = BLKMAP_CRYPT_MODE_XTS;
+ else
+ cipher_mode = BLKMAP_CRYPT_MODE_CBC;
+ } else {
use_essiv = false;
+ cipher_mode = BLKMAP_CRYPT_MODE_CBC;
+ }
+
+ /* Read sector_size if present */
+ if (ofnode_read_u32(segment0_node, "sector_size", §or_size)) {
+ /* If not found, default to 512 */
+ sector_size = 512;
+ }
+ log_debug("LUKS2: sector_size=%u\n", sector_size);
abuf_uninit(&fdt_buf);
free(json_data);
@@ -711,11 +727,20 @@ int luks_create_blkmap(struct udevice *blk, struct disk_partition *pinfo,
/* LUKS1 */
hdr = (struct luks1_phdr *)buf;
- /* Check if ESSIV mode is used */
+ /* Parse cipher mode from cipher_mode string */
use_essiv = strstr(hdr->cipher_mode, "essiv");
+ /* Check if XTS mode is used */
+ if (strstr(hdr->cipher_mode, "xts"))
+ cipher_mode = BLKMAP_CRYPT_MODE_XTS;
+ else
+ cipher_mode = BLKMAP_CRYPT_MODE_CBC;
/* Get payload offset */
payload_offset = be32_to_cpu(hdr->payload_offset);
+
+ /* LUKS1 always uses 512-byte sectors */
+ sector_size = 512;
+ log_debug("LUKS1: sector_size=%u\n", sector_size);
}
/* Create blkmap device */
@@ -747,7 +772,8 @@ int luks_create_blkmap(struct udevice *blk, struct disk_partition *pinfo,
use_essiv);
ret = blkmap_map_crypt(dev, 0, decrypted_size, blk, pinfo->start,
master_key, key_size, payload_offset,
- use_essiv, use_essiv ? essiv_key : NULL);
+ cipher_mode, sector_size, use_essiv,
+ use_essiv ? essiv_key : NULL);
if (ret) {
log_debug("failed to map encrypted partition\n");
blkmap_destroy(dev);
@@ -9,6 +9,14 @@
#include <dm/lists.h>
+/**
+ * enum blkmap_crypt_mode - Cipher mode for encrypted block devices
+ */
+enum blkmap_crypt_mode {
+ BLKMAP_CRYPT_MODE_CBC = 0, /* AES-CBC */
+ BLKMAP_CRYPT_MODE_XTS = 1, /* AES-XTS */
+};
+
/**
* struct blkmap - Block map
*
@@ -80,13 +88,16 @@ int blkmap_map_pmem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
* @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
+ * @cipher_mode: Cipher mode (CBC or XTS)
+ * @sector_size: Sector size for IV calculation (typically 512 or 4096)
+ * @use_essiv: True to use ESSIV mode, false for plain64 mode (CBC only)
* @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,
+ enum blkmap_crypt_mode cipher_mode, u32 sector_size,
bool use_essiv, const u8 *essiv_key);
/**
@@ -141,7 +141,7 @@ int luks_show_info(struct udevice *blk, struct disk_partition *pinfo);
* luks_unlock() - Unlock a LUKS partition with a passphrase
*
* This attempts to decrypt the master key using the provided passphrase.
- * Currently only supports LUKS1 with PBKDF2 and AES-CBC.
+ * Supports LUKS1 (PBKDF2, AES-CBC/XTS) and LUKS2 (PBKDF2/Argon2, AES-XTS).
*
* @blk: Block device
* @pinfo: Partition information
@@ -287,12 +287,9 @@ static int bootstd_test_luks2_unlock(struct unit_test_state *uts)
desc = blk_get_devnum_by_uclass_idname("blkmap", 0);
ut_assertnonnull(desc);
- /* at present this fails due to incorrect decryption */
- if (0) {
- ut_assertok(fs_set_blk_dev_with_part(desc, 0));
- ut_assertok(fs_size("/bin/bash", &file_size));
- ut_asserteq(5, file_size);
- }
+ ut_assertok(fs_set_blk_dev_with_part(desc, 0));
+ ut_assertok(fs_size("/bin/bash", &file_size));
+ ut_asserteq(5, file_size);
/* Test unlocking with wrong passphrase */
ut_asserteq(1, run_command("luks unlock mmc c:2 wrongpass", 0));