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