From: Simon Glass <sjg@chromium.org> Linux Unified Key Setup (LUKS) provides a way to encryption a disk partition with a a key an later unlock it. There are two versions (1 and 2). Add a definition of the main structures and the ability to detect a LUKS partition. Enable this for the sandbox board. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- MAINTAINERS | 8 +++ configs/sandbox_defconfig | 1 + drivers/block/Kconfig | 21 ++++++ drivers/block/Makefile | 1 + drivers/block/luks.c | 63 +++++++++++++++++ include/luks.h | 140 ++++++++++++++++++++++++++++++++++++++ test/boot/Makefile | 1 + test/boot/luks.c | 67 ++++++++++++++++++ 8 files changed, 302 insertions(+) create mode 100644 drivers/block/luks.c create mode 100644 include/luks.h create mode 100644 test/boot/luks.c diff --git a/MAINTAINERS b/MAINTAINERS index 2a2a42aec5e..08d3806b6c7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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 diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 601afde421d..6a4b3e4363a 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -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 diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index adf13f2a243..b07012ec7c9 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -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. diff --git a/drivers/block/Makefile b/drivers/block/Makefile index f5a9d8637a3..b428a5cfb78 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -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 diff --git a/drivers/block/luks.c b/drivers/block/luks.c new file mode 100644 index 00000000000..189226cb0ab --- /dev/null +++ b/drivers/block/luks.c @@ -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; +} diff --git a/include/luks.h b/include/luks.h new file mode 100644 index 00000000000..ea6dd510c53 --- /dev/null +++ b/include/luks.h @@ -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__ */ diff --git a/test/boot/Makefile b/test/boot/Makefile index 7c492ba92af..71c482f8d24 100644 --- a/test/boot/Makefile +++ b/test/boot/Makefile @@ -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 diff --git a/test/boot/luks.c b/test/boot/luks.c new file mode 100644 index 00000000000..684fd643c3a --- /dev/null +++ b/test/boot/luks.c @@ -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); -- 2.43.0