[Concept,14/14] luks: Support disk unlock using a TKey

Message ID 20251116212334.1603490-15-simon.glass@canonical.com
State New
Headers
Series luks: Integrate support for a TKey |

Commit Message

Simon Glass Nov. 16, 2025, 9:23 p.m. UTC
  Add a -t option to the 'luks unlock' command to allow a TKey to be used
to unlock a disk. The password is used as the user-supplied secret (USS)
in this case.

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
---

 cmd/luks.c             | 94 +++++++++++++++++++++++++++++++++++++++---
 doc/usage/cmd/luks.rst | 36 +++++++++++++---
 2 files changed, 119 insertions(+), 11 deletions(-)
  

Patch

diff --git a/cmd/luks.c b/cmd/luks.c
index 47d3e5bed7b..ec4d400b44e 100644
--- a/cmd/luks.c
+++ b/cmd/luks.c
@@ -8,8 +8,11 @@ 
 #include <blk.h>
 #include <command.h>
 #include <dm.h>
+#include <hexdump.h>
 #include <luks.h>
 #include <part.h>
+#include <tkey.h>
+#include <u-boot/sha256.h>
 
 static int do_luks_detect(struct cmd_tbl *cmdtp, int flag, int argc,
 			  char *const argv[])
@@ -57,18 +60,86 @@  static int do_luks_info(struct cmd_tbl *cmdtp, int flag, int argc,
 	return CMD_RET_SUCCESS;
 }
 
+/**
+ * unlock_with_tkey() - Unlock LUKS partition using TKey-derived key
+ *
+ * This function uses TKey to derive a disk encryption key from the
+ * provided passphrase (used as USS) and uses it to unlock the LUKS partition.
+ *
+ * @dev_desc:	Block device descriptor
+ * @info:	Partition information
+ * @passphrase:	Passphrase to use as USS for TKey
+ * @master_key:	Buffer to receive unlocked master key
+ * @key_size:	Pointer to receive key size
+ * Return: 0 on success, -ve on error
+ */
+static int unlock_with_tkey(struct blk_desc *dev_desc,
+			    struct disk_partition *info, const char *passphrase,
+			    u8 *master_key, u32 *key_size)
+{
+	u8 tkey_disk_key[TKEY_DISK_KEY_SIZE];
+	u8 pubkey[TKEY_PUBKEY_SIZE];
+	struct udevice *tkey_dev;
+	int ret;
+
+	printf("Using TKey for disk encryption key\n");
+
+	/* Find TKey device */
+	ret = uclass_first_device_err(UCLASS_TKEY, &tkey_dev);
+	if (ret) {
+		printf("Failed to find TKey device (err %dE)\n", ret);
+		return ret;
+	}
+
+	/* Derive disk key using TKey with passphrase as USS */
+	printf("Loading TKey signer app (%lx bytes) with USS...\n",
+	       TKEY_SIGNER_SIZE);
+	ret = tkey_derive_disk_key(tkey_dev, (const u8 *)__signer_1_0_0_begin,
+				   TKEY_SIGNER_SIZE, (const u8 *)passphrase,
+				   strlen(passphrase), tkey_disk_key, pubkey,
+				   NULL);
+	if (ret) {
+		printf("Failed to derive TKey disk key (err %dE)\n", ret);
+		return ret;
+	}
+
+	printf("TKey public key: ");
+	print_hex_dump("  ", DUMP_PREFIX_NONE, 16, 1, pubkey,
+		       TKEY_PUBKEY_SIZE, false);
+
+	printf("TKey disk key derived successfully\n");
+	printf("TKey derived disk key: ");
+	print_hex_dump("  ", DUMP_PREFIX_NONE, 16, 1, tkey_disk_key,
+		       TKEY_DISK_KEY_SIZE, false);
+
+	ret = luks_unlock(dev_desc->bdev, info, tkey_disk_key,
+			  TKEY_DISK_KEY_SIZE, true, master_key, key_size);
+
+	/* Wipe TKey disk key */
+	memset(tkey_disk_key, '\0', sizeof(tkey_disk_key));
+
+	return ret;
+}
+
 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;
+	const char *passphrase = NULL;
+	bool use_tkey = false;
 	int part, ret, version;
 	u8 master_key[128];
 	char label[64];
 	u32 key_size;
 
+	/* Check for -t flag */
+	if (!strcmp(argv[1], "-t")) {
+		use_tkey = true;
+		argc--;
+		argv++;
+	}
 	if (argc != 4)
 		return CMD_RET_USAGE;
 
@@ -78,6 +149,10 @@  static int do_luks_unlock(struct cmd_tbl *cmdtp, int flag, int argc,
 
 	passphrase = argv[3];
 
+	log_debug("Partition start %llx blks %llx blksz%lx\n",
+		  (unsigned long long)info.start, (unsigned long long)info.size,
+		  (ulong)dev_desc->blksz);
+
 	/* Verify it's a LUKS partition */
 	version = luks_get_version(dev_desc->bdev, &info);
 	if (version < 0) {
@@ -87,9 +162,15 @@  static int do_luks_unlock(struct cmd_tbl *cmdtp, int flag, int argc,
 
 	printf("Unlocking LUKS%d partition...\n", version);
 
-	/* Unlock the partition to get the master key */
-	ret = luks_unlock(dev_desc->bdev, &info, (const u8 *)passphrase,
-			  strlen(passphrase), false, master_key, &key_size);
+	if (use_tkey) {
+		ret = unlock_with_tkey(dev_desc, &info, passphrase, master_key,
+				       &key_size);
+	} else {
+		/* Unlock with passphrase */
+		ret = luks_unlock(dev_desc->bdev, &info,(const u8 *)passphrase,
+				  strlen(passphrase), false, master_key,
+				  &key_size);
+	}
 	if (ret) {
 		printf("Failed to unlock LUKS partition (err %dE)\n", ret);
 		return CMD_RET_FAILURE;
@@ -121,10 +202,11 @@  cleanup:
 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\n"
-	"luks unlock <interface> <dev[:part]> <passphrase> - unlock LUKS partition\n";
+	"luks unlock [-t] <interface> <dev[:part]> <passphrase> - unlock LUKS partition\n"
+	"  -t: Use TKey hardware security token with passphrase as USS\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(unlock, 4, 1, do_luks_unlock));
+	U_BOOT_SUBCMD_MKENT(unlock, 5, 1, do_luks_unlock));
diff --git a/doc/usage/cmd/luks.rst b/doc/usage/cmd/luks.rst
index 3c8576dc8a1..ccf915f5844 100644
--- a/doc/usage/cmd/luks.rst
+++ b/doc/usage/cmd/luks.rst
@@ -13,7 +13,7 @@  Synopsis
 
     luks detect <interface> <dev[:part]>
     luks info <interface> <dev[:part]>
-    luks unlock <interface> <dev[:part]> <passphrase>
+    luks unlock [-t] <interface> <dev[:part]> <passphrase>
 
 Description
 -----------
@@ -88,12 +88,17 @@  dev[:part]
 luks unlock
 ~~~~~~~~~~~
 
-Unlock a LUKS encrypted partition using a passphrase. This command:
+Unlock a LUKS encrypted partition using a passphrase or TKey hardware token.
+This command:
 
 1. Verifies the partition is LUKS encrypted (LUKS1 or LUKS2)
 2. Parses LUKS2 JSON metadata (if LUKS2) using FDT conversion
-3. Derives the encryption key using PBKDF2 or Argon2id with the provided
-   passphrase
+3. Derives the encryption key:
+
+   - **Without -t**: Uses PBKDF2 or Argon2id with the provided passphrase
+   - **With -t**: Uses TKey hardware token with passphrase as USS (User-Supplied
+     Secret) to derive a disk encryption key
+
 4. Attempts to unlock each active key slot
 5. Verifies the master key against the stored digest
 6. Creates a blkmap device providing on-the-fly decryption
@@ -118,6 +123,11 @@  be used to access files on the unlocked partition.
 * **Argon2id**: Memory-hard KDF resistant to GPU attacks (LUKS2 only, requires 
   CONFIG_ARGON2)
 
+-t
+    Optional flag to use TKey hardware security token. When specified, the
+    passphrase is used as the USS (User-Supplied Secret) to derive a disk
+    encryption key from the TKey's public key.
+
 interface
     The storage interface type (e.g., mmc, usb, scsi)
 
@@ -125,7 +135,8 @@  dev[:part]
     The device number and optional partition number
 
 passphrase
-    The passphrase to unlock the LUKS partition. Note that the passphrase is
+    The passphrase to unlock the LUKS partition. When using -t flag, this is
+    used as the USS for TKey key derivation. 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.
 
@@ -228,6 +239,17 @@  Unlock and load a kernel from encrypted partition::
 
     => bootz ${kernel_addr_r} - ${fdt_addr_r}
 
+Unlock using TKey hardware token::
+
+    => luks unlock -t mmc 0:2 mypassword
+    Using TKey for disk encryption key
+    Loading TKey signer app (7168 bytes) with USS...
+    TKey public key: 3a b2 c4 ... (32 bytes)
+    TKey disk key derived successfully
+    Unlocking LUKS2 partition...
+    Successfully unlocked with key slot 0!
+    Unlocked LUKS partition as blkmap device 'luks-mmc-0:2'
+
 Configuration
 -------------
 
@@ -254,6 +276,10 @@  For Argon2id support (modern LUKS2 KDF)::
 
     CONFIG_ARGON2=y      # Argon2 password hashing (adds ~50KB to binary)
 
+For TKey hardware token support (requires -t flag)::
+
+    CONFIG_TKEY=y        # TKey hardware security token support
+
 Return value
 ------------