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:
70
cmd/luks.c
70
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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user