From: Simon Glass <sjg@chromium.org> Provide a new 'luks unlock' command which can unlock a LUKS1 partition, given a passphrase. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- cmd/luks.c | 70 ++++++++++++++++++++++++++++++++- doc/usage/cmd/luks.rst | 87 +++++++++++++++++++++++++++++++++++++++++- test/boot/luks.c | 26 +++++++++++++ 3 files changed, 180 insertions(+), 3 deletions(-) diff --git a/cmd/luks.c b/cmd/luks.c index 04ddcdb1d46..19f909be96b 100644 --- a/cmd/luks.c +++ b/cmd/luks.c @@ -57,11 +57,77 @@ static int do_luks_info(struct cmd_tbl *cmdtp, int flag, int argc, return CMD_RET_SUCCESS; } +static int do_luks_unlock(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct blk_desc *dev_desc; + struct disk_partition info; + struct udevice *blkmap_dev; + const char *passphrase; + int part, ret, version; + u8 master_key[128]; + char label[64]; + u32 key_size; + + if (argc != 4) + 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; + + passphrase = argv[3]; + + /* Verify it's a LUKS partition */ + version = luks_get_version(dev_desc->bdev, &info); + if (version < 0) { + printf("Not a LUKS partition\n"); + return CMD_RET_FAILURE; + } + + if (version != LUKS_VERSION_1) { + printf("Only LUKS1 is currently supported\n"); + return CMD_RET_FAILURE; + } + + /* Unlock the partition to get the master key */ + ret = luks_unlock(dev_desc->bdev, &info, passphrase, master_key, + &key_size); + if (ret) { + printf("Failed to unlock LUKS partition (err %dE)\n", ret); + return CMD_RET_FAILURE; + } + + /* Create blkmap device with label based on source device */ + snprintf(label, sizeof(label), "luks-%s-%s", argv[1], argv[2]); + + /* Create and map the blkmap device */ + ret = luks_create_blkmap(dev_desc->bdev, &info, master_key, key_size, + label, &blkmap_dev); + if (ret) { + printf("Failed to create blkmap device (err %dE)\n", ret); + ret = CMD_RET_FAILURE; + goto cleanup; + } + + printf("Unlocked LUKS partition as blkmap device '%s'\n", label); + + ret = CMD_RET_SUCCESS; + +cleanup: + /* Wipe master key from stack */ + memset(master_key, '\0', sizeof(master_key)); + + return ret; +} + 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"; + "luks info <interface> <dev[:part]> - show LUKS header information\n" + "luks unlock <interface> <dev[:part]> <passphrase> - unlock LUKS partition\n"; 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)); + U_BOOT_SUBCMD_MKENT(info, 3, 1, do_luks_info), + U_BOOT_SUBCMD_MKENT(unlock, 4, 1, do_luks_unlock)); diff --git a/doc/usage/cmd/luks.rst b/doc/usage/cmd/luks.rst index c3b03eeff2e..8dbcb8549c7 100644 --- a/doc/usage/cmd/luks.rst +++ b/doc/usage/cmd/luks.rst @@ -13,11 +13,12 @@ Synopsis luks detect <interface> <dev[:part]> luks info <interface> <dev[:part]> + luks unlock <interface> <dev[:part]> <passphrase> Description ----------- -The *luks* command provides an interface to detect and inspect LUKS +The *luks* command provides an interface to detect, inspect, and unlock LUKS (Linux Unified Key Setup) encrypted partitions. LUKS is a disk encryption specification used for full disk encryption on Linux systems. @@ -25,6 +26,7 @@ This command supports: * Detection of LUKS encrypted partitions (LUKS1 and LUKS2) * Display of LUKS header information +* Unlocking LUKS1 partitions with passphrase-based authentication * Access to decrypted data via blkmap devices The LUKS format uses a distinctive header containing: @@ -83,6 +85,42 @@ dev[:part] The device number and optional partition number. If partition is omitted, defaults to the whole device. +luks unlock +~~~~~~~~~~~ + +Unlock a LUKS1 encrypted partition using a passphrase. This command: + +1. Verifies the partition is LUKS1 encrypted +2. Derives the encryption key using PBKDF2 with the provided passphrase +3. Attempts to unlock each active key slot +4. Verifies the master key against the stored digest +5. Creates a blkmap device providing on-the-fly decryption + +After successful unlock, the decrypted data is accessible through a blkmap +device (typically ``blkmap 0``). Standard U-Boot filesystem commands can then +be used to access files on the unlocked partition. + +**Currently only LUKS1 is supported for unlocking. LUKS2 unlock is not yet +implemented.** + +Supported cipher modes: + +* aes-cbc-essiv:sha256 (AES in CBC mode with ESSIV) + +interface + The storage interface type (e.g., mmc, usb, scsi) + +dev[:part] + The device number and optional partition number + +passphrase + The passphrase to unlock the LUKS partition. Note that the passphrase is + passed as a command-line argument and may be visible in command history. + Consider using environment variables to minimize exposure. + +The unlocked data remains accessible until U-Boot exits or the blkmap device +is explicitly destroyed. + Examples -------- @@ -145,6 +183,40 @@ Display LUKS header information for a LUKS1 partition:: Payload offset: 4096 sectors Key bytes: 32 +Unlock a LUKS1 partition and access files:: + + => luks unlock mmc 0:2 mypassword + Trying to unlock LUKS partition... + Key size: 32 bytes + Trying key slot 0... + Successfully unlocked with key slot 0! + Unlocked LUKS partition as blkmap device 'luks-mmc-0:2' + Access decrypted data via: blkmap 0 + + => ls blkmap 0 / + ./ + ../ + lost+found/ + 221 README.md + 17 hello.txt + subdir/ + 20 test.txt + + 3 file(s), 4 dir(s) + + => cat blkmap 0 /hello.txt + Hello from LUKS! + +Unlock and load a kernel from encrypted partition:: + + => luks unlock mmc 0:2 ${rootfs_password} + Successfully unlocked with key slot 0! + + => ext4load blkmap 0 ${kernel_addr_r} /boot/vmlinuz + 5242880 bytes read in 123 ms (40.6 MiB/s) + + => bootz ${kernel_addr_r} - ${fdt_addr_r} + Configuration ------------- @@ -155,14 +227,27 @@ For LUKS detection and info commands:: CONFIG_BLK_LUKS=y CONFIG_CMD_LUKS=y +For LUKS unlock functionality, additional options are required:: + + CONFIG_BLK_LUKS=y + CONFIG_CMD_LUKS=y + CONFIG_BLKMAP=y # For blkmap device support + CONFIG_AES=y # For AES encryption + CONFIG_SHA256=y # For SHA-256 hashing + CONFIG_PBKDF2=y # For PBKDF2 key derivation + Return value ------------ For *detect* and *info*: The return value $? is 0 (true) on success, 1 (false) on failure. +For *unlock*: The return value $? is 0 (true) if unlock succeeded, 1 (false) +if unlock failed (wrong passphrase, unsupported format, etc.). + See also -------- +* :doc:`blkmap` - Blkmap device documentation * cryptsetup project: https://gitlab.com/cryptsetup/cryptsetup * LUKS on-disk format specifications: https://gitlab.com/cryptsetup/cryptsetup/-/wikis/home diff --git a/test/boot/luks.c b/test/boot/luks.c index 70ee0fb0824..afa8c9f172e 100644 --- a/test/boot/luks.c +++ b/test/boot/luks.c @@ -213,3 +213,29 @@ static int bootstd_test_luks2_info(struct unit_test_state *uts) return 0; } BOOTSTD_TEST(bootstd_test_luks2_info, UTF_DM | UTF_SCAN_FDT | UTF_CONSOLE); + +/* Test LUKS unlock command with LUKS1 encrypted partition */ +static int bootstd_test_luks_unlock(struct unit_test_state *uts) +{ + struct udevice *mmc; + + ut_assertok(setup_mmc11(uts, &mmc)); + + /* Test that unlock command exists and handles errors properly */ + /* Should fail because partition 1 is not LUKS */ + ut_asserteq(1, run_command("luks unlock mmc b:1 test", 0)); + ut_assert_nextline("Not a LUKS partition"); + ut_assert_console_end(); + + /* Test unlocking partition 2 with correct passphrase */ + ut_assertok(run_command("luks unlock mmc b:2 test", 0)); + ut_assert_nextline("Unlocked LUKS partition as blkmap device 'luks-mmc-b:2'"); + ut_assert_console_end(); + + /* Test unlocking with wrong passphrase */ + ut_asserteq(1, run_command("luks unlock mmc b:2 wrongpass", 0)); + ut_assert_skip_to_line("Failed to unlock LUKS partition (err -13: Permission denied)"); + + return 0; +} +BOOTSTD_TEST(bootstd_test_luks_unlock, UTF_DM | UTF_SCAN_FDT | UTF_CONSOLE); -- 2.43.0