luks: Add a subcommand to unlock an encrypted partition

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>
This commit is contained in:
Simon Glass
2025-10-24 06:53:04 +01:00
parent 0cbfb2d490
commit fb5cd8627a
3 changed files with 180 additions and 3 deletions

View File

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

View File

@@ -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

View File

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