@@ -1289,6 +1289,14 @@ F: lib/getopt.c
F: test/log/
F: test/py/tests/test_log.py
+LUKS
+M: Simon Glass <sjg@chromium.org>
+S: Maintained
+T: git https://concept.u-boot.org/u-boot/u-boot.git
+F: drivers/block/luks.c
+F: include/luks.h
+F: test/boot/luks.c
+
MALI DISPLAY PROCESSORS
M: Liviu Dudau <liviu.dudau@foss.arm.com>
S: Supported
@@ -362,6 +362,7 @@ CONFIG_ADDR_MAP=y
CONFIG_PANIC_POWEROFF=y
CONFIG_CMD_DHRYSTONE=y
CONFIG_MBEDTLS_LIB=y
+CONFIG_BLK_LUKS=y
CONFIG_ECDSA=y
CONFIG_ECDSA_VERIFY=y
CONFIG_TPM=y
@@ -269,3 +269,24 @@ config RKMTD
Enable "rkmtd" class and driver to create a virtual block device
to transfer Rockchip boot block data to and from NAND with block
orientate tools like "ums" and "rockusb".
+
+config BLK_LUKS
+ bool "Enable LUKS detection and decryption support"
+ depends on BLK && MBEDTLS_LIB
+ select BLKMAP
+ select AES
+ select SHA256
+ select PBKDF2
+ select PKCS5_MBEDTLS if MBEDTLS_LIB_CRYPTO
+ help
+ This provides support for detecting and decrypting LUKS (Linux Unified
+ Key Setup) encrypted partitions. LUKS is a disk encryption specification
+ used for full disk encryption on Linux systems.
+
+ This option enables detection of both LUKS1 and LUKS2 encrypted
+ partitions by checking for the LUKS magic bytes and version
+ information in the partition header.
+
+ LUKS1 decryption is supported using PBKDF2 key derivation and AES-CBC.
+ Decrypted partitions can be accessed transparently through the blkmap
+ device layer.
@@ -11,6 +11,7 @@ endif
ifndef CONFIG_XPL_BUILD
obj-$(CONFIG_IDE) += ide.o
+obj-$(CONFIG_BLK_LUKS) += luks.o
obj-$(CONFIG_RKMTD) += rkmtd.o
endif
obj-$(CONFIG_SANDBOX) += sandbox.o host-uclass.o host_dev.o
new file mode 100644
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * LUKS (Linux Unified Key Setup) filesystem support
+ *
+ * Copyright (C) 2025 Canonical Ltd
+ */
+
+#include <blk.h>
+#include <dm.h>
+#include <hexdump.h>
+#include <log.h>
+#include <luks.h>
+#include <memalign.h>
+#include <part.h>
+#include <uboot_aes.h>
+#include <linux/byteorder/generic.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <mbedtls/md.h>
+#include <mbedtls/pkcs5.h>
+
+int luks_get_version(struct udevice *blk, struct disk_partition *pinfo)
+{
+ struct blk_desc *desc;
+ int version;
+
+ desc = dev_get_uclass_plat(blk);
+
+ ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, desc->blksz);
+
+ /* Read first block of the partition */
+ if (blk_dread(desc, pinfo->start, 1, buffer) != 1) {
+ log_debug("Error: failed to read LUKS header\n");
+ return -EIO;
+ }
+
+ /* Check for LUKS magic bytes */
+ if (memcmp(buffer, LUKS_MAGIC, LUKS_MAGIC_LEN))
+ return -ENOENT;
+
+ /* Read version field (16-bit big-endian at offset 6) */
+ version = be16_to_cpu(*(__be16 *)(buffer + LUKS_MAGIC_LEN));
+
+ /* Validate version */
+ if (version != LUKS_VERSION_1 && version != LUKS_VERSION_2) {
+ log_debug("Warning: unknown LUKS version %d\n", version);
+ return -EPROTONOSUPPORT;
+ }
+
+ return version;
+}
+
+int luks_detect(struct udevice *blk, struct disk_partition *pinfo)
+{
+ int version;
+
+ version = luks_get_version(blk, pinfo);
+ if (IS_ERR_VALUE(version))
+ return version;
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,140 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * LUKS (Linux Unified Key Setup) filesystem support
+ *
+ * Copyright (C) 2025 Canonical Ltd
+ */
+
+#ifndef __LUKS_H__
+#define __LUKS_H__
+
+#include <linux/types.h>
+
+struct udevice;
+struct disk_partition;
+
+/* LUKS magic bytes: "LUKS" followed by 0xba 0xbe */
+#define LUKS_MAGIC "LUKS\xba\xbe"
+#define LUKS_MAGIC_LEN 6
+
+/* LUKS versions */
+#define LUKS_VERSION_1 1
+#define LUKS_VERSION_2 2
+
+/* LUKS constants */
+#define LUKS_DIGESTSIZE 20
+#define LUKS_SALTSIZE 32
+#define LUKS_NUMKEYS 8
+#define LUKS_KEY_DISABLED 0x0000dead
+#define LUKS_KEY_ENABLED 0x00ac71f3
+#define LUKS_STRIPES 4000
+
+/**
+ * struct luks1_keyslot - LUKS1 key slot
+ *
+ * @active: Key slot state (LUKS_KEY_ENABLED/DISABLED)
+ * @iterations: PBKDF2 iteration count
+ * @salt: Salt for PBKDF2
+ * @key_material_offset: Start sector of key material
+ * @stripes: Number of anti-forensic stripes
+ */
+struct luks1_keyslot {
+ __be32 active;
+ __be32 iterations;
+ char salt[LUKS_SALTSIZE];
+ __be32 key_material_offset;
+ __be32 stripes;
+} __packed;
+
+/**
+ * struct luks1_phdr - LUKS1 header structure
+ *
+ * @magic: LUKS magic bytes
+ * @version: LUKS version
+ * @cipher_name: Cipher name
+ * @cipher_mode: Cipher mode
+ * @hash_spec: Hash specification
+ * @payload_offset: Payload offset in sectors
+ * @key_bytes: Key length in bytes
+ * @mk_digest: Master key digest
+ * @mk_digest_salt: Salt for master key digest
+ * @mk_digest_iter: Iterations for master key digest
+ * @uuid: Partition UUID
+ * @key_slot: Key slots (8 total)
+ */
+struct luks1_phdr {
+ char magic[LUKS_MAGIC_LEN];
+ __be16 version;
+ char cipher_name[32];
+ char cipher_mode[32];
+ char hash_spec[32];
+ __be32 payload_offset;
+ __be32 key_bytes;
+ char mk_digest[LUKS_DIGESTSIZE];
+ char mk_digest_salt[LUKS_SALTSIZE];
+ __be32 mk_digest_iter;
+ char uuid[40];
+ struct luks1_keyslot key_slot[LUKS_NUMKEYS];
+} __packed;
+
+/**
+ * struct luks2_hdr - LUKS2 binary header
+ *
+ * @magic: LUKS magic bytes
+ * @version: LUKS version
+ * @hdr_size: Header size (includes binary header + JSON area)
+ * @seqid: Sequence ID
+ * @label: Label string
+ * @csum_alg: Checksum algorithm
+ * @salt: Salt for header checksum
+ * @uuid: Partition UUID
+ * @subsystem: Subsystem identifier
+ * @hdr_offset: Offset of this header
+ * @_padding: Reserved padding
+ * @csum: Header checksum
+ * @_padding4096: Padding to 4096 bytes
+ */
+struct luks2_hdr {
+ char magic[LUKS_MAGIC_LEN];
+ __be16 version;
+ __be64 hdr_size;
+ __be64 seqid;
+ char label[48];
+ char csum_alg[32];
+ u8 salt[64];
+ char uuid[40];
+ char subsystem[48];
+ __be64 hdr_offset;
+ u8 _padding[184];
+ u8 csum[64];
+ u8 _padding4096[3584];
+} __packed;
+
+/**
+ * luks_detect() - Detect if a partition is LUKS encrypted
+ *
+ * @blk: Block device
+ * @pinfo: Partition information
+ * Return: 0 if LUKS partition detected, -ve on error
+ */
+int luks_detect(struct udevice *blk, struct disk_partition *pinfo);
+
+/**
+ * luks_get_version() - Get LUKS version of a partition
+ *
+ * @blk: Block device
+ * @pinfo: Partition information
+ * Return: LUKS version (1 or 2) if detected, -ve on error
+ */
+int luks_get_version(struct udevice *blk, struct disk_partition *pinfo);
+
+/**
+ * luks_show_info() - Display LUKS header information
+ *
+ * @blk: Block device
+ * @pinfo: Partition information
+ * Return: 0 on success, -ve on error
+ */
+int luks_show_info(struct udevice *blk, struct disk_partition *pinfo);
+
+#endif /* __LUKS_H__ */
@@ -5,6 +5,7 @@
ifdef CONFIG_UT_BOOTSTD
obj-$(CONFIG_BOOTSTD) += bootdev.o bootstd_common.o bootflow.o bootmeth.o
obj-$(CONFIG_FIT) += image.o
+obj-$(CONFIG_BLK_LUKS) += luks.o
obj-$(CONFIG_EXPO) += expo.o expo_common.o
obj-$(CONFIG_CEDIT) += cedit.o expo_common.o
new file mode 100644
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for LUKS detection
+ *
+ * Copyright (C) 2025 Canonical Ltd
+ */
+
+#include <blk.h>
+#include <dm.h>
+#include <luks.h>
+#include <mmc.h>
+#include <part.h>
+#include <asm/global_data.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+#include "bootstd_common.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Common function to setup mmc11 device */
+static int setup_mmc11(struct unit_test_state *uts, struct udevice **mmcp)
+{
+ ofnode root, node;
+
+ /* Enable the mmc11 node */
+ root = oftree_root(oftree_default());
+ node = ofnode_find_subnode(root, "mmc11");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(lists_bind_fdt(gd->dm_root, node, mmcp, NULL, false));
+
+ /* Probe the device */
+ ut_assertok(device_probe(*mmcp));
+
+ return 0;
+}
+
+/* Test LUKS detection on mmc11 partitions */
+static int bootstd_test_luks_detect(struct unit_test_state *uts)
+{
+ struct disk_partition info;
+ struct blk_desc *desc;
+ struct udevice *mmc;
+ int ret;
+
+ ut_assertok(setup_mmc11(uts, &mmc));
+ desc = blk_get_by_device(mmc);
+ ut_assertnonnull(desc);
+ ut_assertnonnull(desc->bdev);
+
+ /* Check partition 1 - should NOT be LUKS */
+ ut_assertok(part_get_info(desc, 1, &info));
+ ret = luks_detect(desc->bdev, &info);
+ ut_assert(ret < 0); /* Should fail - not LUKS */
+
+ /* Check partition 2 - should BE LUKS */
+ ut_assertok(part_get_info(desc, 2, &info));
+ ut_assertok(luks_detect(desc->bdev, &info));
+
+ /* Verify it's LUKS version 1 */
+ ut_asserteq(1, luks_get_version(desc->bdev, &info));
+
+ return 0;
+}
+BOOTSTD_TEST(bootstd_test_luks_detect, UTF_DM | UTF_SCAN_FDT | UTF_CONSOLE);