[Concept,16/24] luks: Add the beginning of LUKS support

Message ID 20251031065439.3251464-17-sjg@u-boot.org
State New
Headers
Series luks: Provide basic support for unlocking a LUKS1 partition |

Commit Message

Simon Glass Oct. 31, 2025, 6:54 a.m. UTC
  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
  

Patch

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);