[Concept,17/24] luks: Add a simple command

Message ID 20251031065439.3251464-18-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>

Add a 'luks' command which allows querying a partition to see if it is
encrypted using LUKS, as well as showing information about a LUKS
partition.

Provide some documentation and a test.

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <sjg@chromium.org>
---

 MAINTAINERS            |   2 +
 cmd/Kconfig            |   9 +++
 cmd/Makefile           |   1 +
 cmd/luks.c             |  67 ++++++++++++++++++++
 doc/usage/cmd/luks.rst | 139 +++++++++++++++++++++++++++++++++++++++++
 doc/usage/index.rst    |   1 +
 drivers/block/luks.c   |  59 +++++++++++++++--
 test/boot/luks.c       |  42 +++++++++++++
 8 files changed, 315 insertions(+), 5 deletions(-)
 create mode 100644 cmd/luks.c
 create mode 100644 doc/usage/cmd/luks.rst
  

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 08d3806b6c7..9b00829db93 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1293,6 +1293,8 @@  LUKS
 M:	Simon Glass <sjg@chromium.org>
 S:	Maintained
 T:	git https://concept.u-boot.org/u-boot/u-boot.git
+F:	cmd/luks.c
+F:	doc/usage/cmd/luks.rst
 F:	drivers/block/luks.c
 F:	include/luks.h
 F:	test/boot/luks.c
diff --git a/cmd/Kconfig b/cmd/Kconfig
index f4d6e544cc0..a45df78c8fd 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -2819,6 +2819,15 @@  config CMD_FS_UUID
 	help
 	  Enables fsuuid command for filesystem UUID.
 
+config CMD_LUKS
+	bool "luks command"
+	depends on BLK_LUKS
+	default y if BLK_LUKS
+	help
+	  Enables the 'luks' command for detecting LUKS (Linux Unified Key
+	  Setup) encrypted partitions. This command checks if a partition
+	  is LUKS encrypted and displays the LUKS version (1 or 2).
+
 config CMD_JFFS2
 	bool "jffs2 command"
 	select FS_JFFS2
diff --git a/cmd/Makefile b/cmd/Makefile
index 28e9c261683..2c6a16752bd 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -90,6 +90,7 @@  obj-$(CONFIG_CMD_SQUASHFS) += sqfs.o
 obj-$(CONFIG_CMD_SELECT_FONT) += font.o
 obj-$(CONFIG_CMD_FLASH) += flash.o
 obj-$(CONFIG_CMD_FPGA) += fpga.o
+obj-$(CONFIG_CMD_LUKS) += luks.o
 obj-$(CONFIG_CMD_FPGAD) += fpgad.o
 obj-$(CONFIG_CMD_FS_GENERIC) += fs.o
 obj-$(CONFIG_CMD_FUSE) += fuse.o
diff --git a/cmd/luks.c b/cmd/luks.c
new file mode 100644
index 00000000000..04ddcdb1d46
--- /dev/null
+++ b/cmd/luks.c
@@ -0,0 +1,67 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * LUKS (Linux Unified Key Setup) command
+ *
+ * Copyright (C) 2025 Canonical Ltd
+ */
+
+#include <blk.h>
+#include <command.h>
+#include <dm.h>
+#include <luks.h>
+#include <part.h>
+
+static int do_luks_detect(struct cmd_tbl *cmdtp, int flag, int argc,
+			  char *const argv[])
+{
+	struct blk_desc *dev_desc;
+	struct disk_partition info;
+	int part, ret, version;
+
+	if (argc != 3)
+		return CMD_RET_USAGE;
+
+	part = blk_get_device_part_str(argv[1], argv[2], &dev_desc, &info, 1);
+	if (part < 0)
+		return CMD_RET_FAILURE;
+
+	ret = luks_detect(dev_desc->bdev, &info);
+	if (ret < 0) {
+		printf("Not a LUKS partition (error %dE)\n", ret);
+		return CMD_RET_FAILURE;
+	}
+	version = luks_get_version(dev_desc->bdev, &info);
+	printf("LUKS%d encrypted partition detected\n", version);
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_luks_info(struct cmd_tbl *cmdtp, int flag, int argc,
+			char *const argv[])
+{
+	struct blk_desc *dev_desc;
+	struct disk_partition info;
+	int part, ret;
+
+	if (argc != 3)
+		return CMD_RET_USAGE;
+
+	part = blk_get_device_part_str(argv[1], argv[2], &dev_desc, &info, 1);
+	if (part < 0)
+		return CMD_RET_FAILURE;
+
+	ret = luks_show_info(dev_desc->bdev, &info);
+	if (ret < 0)
+		return CMD_RET_FAILURE;
+
+	return CMD_RET_SUCCESS;
+}
+
+static char luks_help_text[] =
+	"detect <interface> <dev[:part]> - detect if partition is LUKS encrypted\n"
+	"luks info <interface> <dev[:part]> - show LUKS header information";
+
+U_BOOT_CMD_WITH_SUBCMDS(luks, "LUKS (Linux Unified Key Setup) operations",
+			luks_help_text,
+	U_BOOT_SUBCMD_MKENT(detect, 3, 1, do_luks_detect),
+	U_BOOT_SUBCMD_MKENT(info, 3, 1, do_luks_info));
diff --git a/doc/usage/cmd/luks.rst b/doc/usage/cmd/luks.rst
new file mode 100644
index 00000000000..b88fcd96439
--- /dev/null
+++ b/doc/usage/cmd/luks.rst
@@ -0,0 +1,139 @@ 
+.. SPDX-License-Identifier: GPL-2.0+:
+
+.. index::
+   single: luks (command)
+
+luks command
+============
+
+Synopsis
+--------
+
+::
+
+    luks detect <interface> <dev[:part]>
+    luks info <interface> <dev[:part]>
+
+Description
+-----------
+
+The *luks* command provides an interface to detect and inspect LUKS
+(Linux Unified Key Setup) encrypted partitions. LUKS is a disk encryption
+specification used for full disk encryption on Linux systems.
+
+This command supports:
+
+* Detection of LUKS encrypted partitions (LUKS1 and LUKS2)
+* Display of LUKS header information
+* Access to decrypted data via blkmap devices
+
+The LUKS format uses a distinctive header containing:
+
+* Magic bytes: "LUKS" followed by 0xBA 0xBE
+* Version information (16-bit big-endian)
+* Encryption metadata (cipher, mode, hash specification)
+
+luks detect
+~~~~~~~~~~~
+
+Detect whether a specified partition is LUKS encrypted and report its version.
+
+interface
+    The storage interface type (e.g., mmc, usb, scsi)
+
+dev[:part]
+    The device number and optional partition number. If partition is omitted,
+    defaults to the whole device.
+
+luks info
+~~~~~~~~~
+
+Display detailed header information for a LUKS encrypted partition. This
+subcommand reads the LUKS header and displays format-specific metadata.
+
+For LUKS1 partitions, the following information is displayed:
+
+* Version number
+* Cipher name (encryption algorithm)
+* Cipher mode (e.g., xts-plain64)
+* Hash specification (e.g., sha256)
+* Payload offset (in sectors)
+* Key bytes (key size)
+
+For LUKS2 partitions, the following information is displayed:
+
+* Version number
+* Header size (in bytes)
+* Sequence ID (metadata update counter)
+* UUID (partition identifier)
+* Label (optional partition label)
+* Checksum algorithm
+* Full JSON metadata containing:
+
+  - keyslots: Encryption key slot information including KDF parameters
+  - tokens: Optional token metadata
+  - segments: Encrypted data segment descriptions
+  - digests: Master key digest information
+  - config: Configuration parameters
+
+interface
+    The storage interface type (e.g., mmc, usb, scsi)
+
+dev[:part]
+    The device number and optional partition number. If partition is omitted,
+    defaults to the whole device.
+
+Examples
+--------
+
+Check if MMC device 0 partition 2 is LUKS encrypted::
+
+    => luks detect mmc 0:2
+    LUKS2 encrypted partition detected
+
+Check a USB device partition::
+
+    => luks detect usb 0:1
+    Not a LUKS partition (error -2: No such file or directory)
+
+Display LUKS header information for a LUKS2 partition::
+
+    => luks info mmc 0:2
+    Version:        2
+    Header size:    16384 bytes
+    Sequence ID:    3
+    UUID:           7640da3a-d0a2-4238-9813-4714e7f62203
+    Label:
+    Checksum alg:   sha256
+
+Display LUKS header information for a LUKS1 partition::
+
+    => luks info mmc 1:1
+    Version:        1
+    Cipher name:    aes
+    Cipher mode:    cbc-essiv:sha256
+    Hash spec:      sha256
+    Payload offset: 4096 sectors
+    Key bytes:      32
+
+Configuration
+-------------
+
+The luks command is available when CONFIG_CMD_LUKS is enabled.
+
+For LUKS detection and info commands::
+
+    CONFIG_BLK_LUKS=y
+    CONFIG_CMD_LUKS=y
+
+Return value
+------------
+
+For *detect* and *info*: The return value $? is 0 (true) on success, 1 (false)
+on failure.
+
+See also
+--------
+
+* cryptsetup project: https://gitlab.com/cryptsetup/cryptsetup
+* LUKS on-disk format specifications: https://gitlab.com/cryptsetup/cryptsetup/-/wikis/home
diff --git a/doc/usage/index.rst b/doc/usage/index.rst
index 2fff101868c..2bb01211227 100644
--- a/doc/usage/index.rst
+++ b/doc/usage/index.rst
@@ -93,6 +93,7 @@  Shell commands
    cmd/loads
    cmd/loadx
    cmd/loady
+   cmd/luks
    cmd/meminfo
    cmd/mbr
    cmd/md
diff --git a/drivers/block/luks.c b/drivers/block/luks.c
index 189226cb0ab..597359b98ff 100644
--- a/drivers/block/luks.c
+++ b/drivers/block/luks.c
@@ -22,15 +22,13 @@ 
 
 int luks_get_version(struct udevice *blk, struct disk_partition *pinfo)
 {
-	struct blk_desc *desc;
+	struct blk_desc *desc = dev_get_uclass_plat(blk);
+	ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, desc->blksz);
 	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) {
+	if (blk_read(blk, pinfo->start, 1, buffer) != 1) {
 		log_debug("Error: failed to read LUKS header\n");
 		return -EIO;
 	}
@@ -61,3 +59,54 @@  int luks_detect(struct udevice *blk, struct disk_partition *pinfo)
 
 	return 0;
 }
+
+int luks_show_info(struct udevice *blk, struct disk_partition *pinfo)
+{
+	struct blk_desc *desc = dev_get_uclass_plat(blk);
+	ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, desc->blksz);
+	int version;
+
+	/* Read first block of the partition */
+	if (blk_read(blk, pinfo->start, 1, buffer) != 1) {
+		printf("Error: failed to read LUKS header\n");
+		return -EIO;
+	}
+
+	/* Check for LUKS magic bytes */
+	if (memcmp(buffer, LUKS_MAGIC, LUKS_MAGIC_LEN)) {
+		printf("Not a LUKS partition\n");
+		return -ENOENT;
+	}
+
+	/* Read version field */
+	version = be16_to_cpu(*(__be16 *)(buffer + LUKS_MAGIC_LEN));
+
+	printf("Version:        %d\n", version);
+	if (version == LUKS_VERSION_1) {
+		struct luks1_phdr *luks1_hdr = (struct luks1_phdr *)buffer;
+
+		printf("Cipher name:    %.32s\n", luks1_hdr->cipher_name);
+		printf("Cipher mode:    %.32s\n", luks1_hdr->cipher_mode);
+		printf("Hash spec:      %.32s\n", luks1_hdr->hash_spec);
+		printf("Payload offset: %u sectors\n",
+		       be32_to_cpu(luks1_hdr->payload_offset));
+		printf("Key bytes:      %u\n",
+		       be32_to_cpu(luks1_hdr->key_bytes));
+	} else if (version == LUKS_VERSION_2) {
+		struct luks2_hdr *luks2_hdr = (struct luks2_hdr *)buffer;
+		u64 hdr_size;
+
+		hdr_size = be64_to_cpu(luks2_hdr->hdr_size);
+
+		printf("Header size:    %llu bytes\n", hdr_size);
+		printf("Sequence ID:    %llu\n", be64_to_cpu(luks2_hdr->seqid));
+		printf("UUID:           %.40s\n", luks2_hdr->uuid);
+		printf("Label:          %.48s\n", luks2_hdr->label);
+		printf("Checksum alg:   %.32s\n", luks2_hdr->csum_alg);
+	} else {
+		printf("Unknown LUKS version\n");
+		return -EPROTONOSUPPORT;
+	}
+
+	return 0;
+}
diff --git a/test/boot/luks.c b/test/boot/luks.c
index 684fd643c3a..fadd3819ffe 100644
--- a/test/boot/luks.c
+++ b/test/boot/luks.c
@@ -65,3 +65,45 @@  static int bootstd_test_luks_detect(struct unit_test_state *uts)
 	return 0;
 }
 BOOTSTD_TEST(bootstd_test_luks_detect, UTF_DM | UTF_SCAN_FDT | UTF_CONSOLE);
+
+/* Test LUKS command on mmc11 partitions */
+static int bootstd_test_luks_cmd(struct unit_test_state *uts)
+{
+	struct udevice *mmc;
+
+	ut_assertok(setup_mmc11(uts, &mmc));
+
+	/* Test partition 1 - should NOT be LUKS */
+	ut_asserteq(1, run_command("luks detect mmc b:1", 0));
+	ut_assert_nextlinen("Not a LUKS partition (error -");
+	ut_assert_console_end();
+
+	/* Test partition 2 - should BE LUKS */
+	ut_assertok(run_command("luks detect mmc b:2", 0));
+	ut_assert_nextline("LUKS1 encrypted partition detected");
+	ut_assert_console_end();
+
+	return 0;
+}
+BOOTSTD_TEST(bootstd_test_luks_cmd, UTF_DM | UTF_SCAN_FDT | UTF_CONSOLE);
+
+/* Test LUKS info command on mmc11 partition 2 */
+static int bootstd_test_luks_info(struct unit_test_state *uts)
+{
+	struct udevice *mmc;
+
+	ut_assertok(setup_mmc11(uts, &mmc));
+
+	/* Test partition 2 LUKS info */
+	ut_assertok(run_command("luks info mmc b:2", 0));
+	ut_assert_nextline("Version:        1");
+	ut_assert_nextlinen("Cipher name:");
+	ut_assert_nextlinen("Cipher mode:");
+	ut_assert_nextlinen("Hash spec:");
+	ut_assert_nextlinen("Payload offset:");
+	ut_assert_nextlinen("Key bytes:");
+	ut_assert_console_end();
+
+	return 0;
+}
+BOOTSTD_TEST(bootstd_test_luks_info, UTF_DM | UTF_SCAN_FDT | UTF_CONSOLE);