@@ -2390,6 +2390,16 @@ config CMD_MP
This enables commands to bringup different processors
in multiprocessor cases.
+config CMD_TKEY
+ bool "tkey - Tillitis TKey operations"
+ depends on TKEY
+ default y
+ help
+ The allows interacting with a Tillitis TKey security token,
+ including connecting to the device, getting device information and
+ creating cryptographic wrapping keys from passwords combined with
+ device secrets.
+
config CMD_TIMER
bool "timer"
help
@@ -189,6 +189,7 @@ obj-$(CONFIG_CMD_TEMPERATURE) += temperature.o
obj-$(CONFIG_CMD_TERMINAL) += terminal.o
obj-$(CONFIG_CMD_TIME) += time.o
obj-$(CONFIG_CMD_TIMER) += timer.o
+obj-$(CONFIG_CMD_TKEY) += tkey.o
obj-$(CONFIG_CMD_TRACE) += trace.o
obj-$(CONFIG_HUSH_PARSER) += test.o
obj-$(CONFIG_CMD_TPM) += tpm-common.o
new file mode 100644
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025 Canonical Ltd
+ *
+ * Command for communicating with Tillitis TKey to create wrapping keys
+ * from user-provided passwords.
+ */
+
+#include <command.h>
+#include <console.h>
+#include <dm.h>
+#include <hexdump.h>
+#include <malloc.h>
+#include <time.h>
+#include <tkey.h>
+#include <asm/unaligned.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+
+static struct udevice *tkey_get_device(void)
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = uclass_first_device_err(UCLASS_TKEY, &dev);
+ if (ret) {
+ printf("No device found (err %dE)\n", ret);
+ return NULL;
+ }
+
+ return dev;
+}
+
+static void print_hex(const char *label, const u8 *data, size_t len)
+{
+ size_t i;
+
+ printf("%s: ", label);
+ for (i = 0; i < len; i++)
+ printf("%02x", data[i]);
+ printf("\n");
+}
+
+static int do_tkey_connect(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+
+ dev = tkey_get_device();
+ if (!dev)
+ return CMD_RET_FAILURE;
+
+ printf("Connected to TKey device\n");
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_tkey_info(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char name0[TKEY_NAME_SIZE], name1[TKEY_NAME_SIZE];
+ u8 udi[TKEY_UDI_SIZE];
+ struct udevice *dev;
+ u32 version;
+ int ret;
+
+ dev = tkey_get_device();
+ if (!dev)
+ return CMD_RET_FAILURE;
+
+ ret = tkey_get_name_version(dev, name0, name1, &version);
+ if (ret) {
+ printf("Failed to get device info (err %dE)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ printf("Name0: %.4s Name1: %.4s Version: %u\n", name0, name1, version);
+
+ ret = tkey_get_udi(dev, udi);
+ if (ret) {
+ if (ret == -ENOTSUPP)
+ printf("UDI not available - replug device\n");
+ else
+ printf("Failed to get UDI (err %dE)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+ print_hex("UDI", udi, TKEY_UDI_SIZE);
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_tkey_wrapkey(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ u8 wrapping_key[TKEY_WRAPPING_KEY_SIZE];
+ const char *password;
+ struct udevice *dev;
+ int ret;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ dev = tkey_get_device();
+ if (!dev)
+ return CMD_RET_FAILURE;
+
+ password = argv[1];
+
+ ret = tkey_derive_wrapping_key(dev, password, wrapping_key);
+ if (ret) {
+ if (ret == -ENOTSUPP)
+ printf("UDI not available - replug device\n");
+ else
+ printf("Cannot derive wrapping key (err %dE)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ print_hex("Wrapping Key", wrapping_key, TKEY_WRAPPING_KEY_SIZE);
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_tkey_fwmode(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ int ret;
+
+ dev = tkey_get_device();
+ if (!dev)
+ return CMD_RET_FAILURE;
+
+ ret = tkey_in_app_mode(dev);
+ if (ret < 0) {
+ printf("Failed to check device mode (err %dE)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ if (!ret)
+ printf("firmware mode\n");
+ else
+ printf("app mode\n");
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_tkey_signer(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ printf("signer binary: %lx bytes at %p-%p\n", TKEY_SIGNER_SIZE,
+ __signer_1_0_0_begin, __signer_1_0_0_end);
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_tkey_getkey(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *hash = NULL;
+ u8 expect[TKEY_HASH_SIZE];
+ u8 disk_key[TKEY_DISK_KEY_SIZE];
+ u8 key_hash[TKEY_HASH_SIZE];
+ u8 pubkey[TKEY_PUBKEY_SIZE];
+ bool verify = false;
+ struct udevice *dev;
+ u32 uss_len, ret;
+ const char *uss;
+
+ if (argc != 2 && argc != 3)
+ return CMD_RET_USAGE;
+
+ dev = tkey_get_device();
+ if (!dev)
+ return CMD_RET_FAILURE;
+
+ uss = argv[1];
+ uss_len = strlen(uss);
+ if (uss_len > TKEY_USS_MAX_SIZE) {
+ printf("USS too long (max %x bytes, got %x)\n",
+ TKEY_USS_MAX_SIZE, uss_len);
+ return CMD_RET_FAILURE;
+ }
+
+ /* Check if verification hash is provided */
+ if (argc == 3) {
+ int i;
+
+ hash = argv[2];
+ verify = true;
+
+ /* Convert hex string to bytes */
+ if (strlen(hash) != TKEY_HASH_SIZE * 2) {
+ printf("Verification hash must be %x hex chars\n",
+ TKEY_HASH_SIZE * 2);
+ return CMD_RET_USAGE;
+ }
+
+ for (i = 0; i < TKEY_HASH_SIZE; i++)
+ expect[i] = (hex_to_bin(hash[i * 2]) << 4) |
+ hex_to_bin(hash[i * 2 + 1]);
+ }
+
+ /* Derive disk key using uclass function */
+ ret = tkey_derive_disk_key(dev, (const u8 *)__signer_1_0_0_begin,
+ TKEY_SIGNER_SIZE, (const u8 *)uss,
+ uss_len, disk_key, pubkey, key_hash);
+ if (ret) {
+ printf("Failed to derive disk key (err %dE)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ /* Display results */
+ print_hex("Public Key", pubkey, TKEY_PUBKEY_SIZE);
+ print_hex("Disk Key", disk_key, TKEY_DISK_KEY_SIZE);
+
+ /* Verify or display verification hash */
+ if (verify) {
+ /* Verify USS by comparing hashes */
+ if (memcmp(key_hash, expect, TKEY_HASH_SIZE) == 0) {
+ printf("\npassword correct\n");
+ } else {
+ printf("\nwrong password\n");
+ print_hex("Expected", expect, TKEY_HASH_SIZE);
+ print_hex("Got", key_hash, TKEY_HASH_SIZE);
+ return CMD_RET_FAILURE;
+ }
+ } else {
+ print_hex("Verification Hash", key_hash, TKEY_HASH_SIZE);
+ /* to verify USS later: tkey getkey <uss> <verification-hash> */
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+static int do_tkey_loadapp(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ const char *uss = NULL;
+ u32 ret, ulen = 0;
+
+ if (argc != 1 && argc != 2)
+ return CMD_RET_USAGE;
+
+ dev = tkey_get_device();
+ if (!dev)
+ return CMD_RET_FAILURE;
+
+ /* Optional USS parameter */
+ if (argc == 2) {
+ uss = argv[1];
+ ulen = strlen(uss);
+ if (ulen > TKEY_USS_MAX_SIZE) {
+ printf("USS too long (max %x bytes, got %x)\n",
+ TKEY_USS_MAX_SIZE, ulen);
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ printf("Loading signer app (%lx bytes)%s...", TKEY_SIGNER_SIZE,
+ uss ? " with USS" : "");
+ ret = tkey_load_app_with_uss(dev, (const u8 *)__signer_1_0_0_begin,
+ TKEY_SIGNER_SIZE, (const u8 *)uss, ulen);
+ if (ret) {
+ if (ret == -ENOTSUPP)
+ printf("Invalid mode - replug device?\n");
+ else
+ printf("Failed to load app (err %dE)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+ printf("done\n");
+
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_LONGHELP(tkey,
+ "connect - Connect to TKey device\n"
+ "tkey fwmode - Check if device is in firmware or app mode\n"
+ "tkey getkey <uss> [verify-hash] - Get disk encryption key\n"
+ " Loads app with USS, derives key. Same USS always produces same key.\n"
+ " Optional verify-hash checks if USS is correct\n"
+ "tkey info - Show TKey device information\n"
+ "tkey loadapp [uss] - Load embedded signer app to TKey\n"
+ " Firmware mode only. Optional USS for key derivation\n"
+ "tkey signer - Show embedded signer binary information\n"
+ "tkey wrapkey <password> - Create wrapping key from password and UDI");
+
+U_BOOT_CMD_WITH_SUBCMDS(tkey, "Tillitis TKey security token operations",
+ tkey_help_text,
+ U_BOOT_SUBCMD_MKENT(connect, 1, 1, do_tkey_connect),
+ U_BOOT_SUBCMD_MKENT(fwmode, 1, 1, do_tkey_fwmode),
+ U_BOOT_SUBCMD_MKENT(getkey, 3, 1, do_tkey_getkey),
+ U_BOOT_SUBCMD_MKENT(info, 1, 1, do_tkey_info),
+ U_BOOT_SUBCMD_MKENT(loadapp, 2, 1, do_tkey_loadapp),
+ U_BOOT_SUBCMD_MKENT(signer, 1, 1, do_tkey_signer),
+ U_BOOT_SUBCMD_MKENT(wrapkey, 2, 1, do_tkey_wrapkey));
new file mode 100644
@@ -0,0 +1,247 @@
+.. SPDX-License-Identifier: GPL-2.0+:
+
+.. index::
+ single: tkey (command)
+
+tkey command
+============
+
+Synopsis
+--------
+
+::
+
+ tkey connect
+ tkey fwmode
+ tkey getkey <uss> [verify-hash]
+ tkey info
+ tkey loadapp [uss]
+ tkey signer
+ tkey wrapkey <password>
+
+Description
+-----------
+
+The *tkey* command provides an interface to interact with Tillitis TKey
+security tokens. The TKey is a USB security device that can be used for
+cryptographic operations, particularly for deriving encryption keys in a
+secure and reproducible manner.
+
+The TKey operates in two modes:
+
+Firmware mode
+ The device starts in this mode after being plugged in. In firmware mode,
+ the device provides access to its Unique Device Identifier (UDI) and allows
+ loading applications.
+
+App mode
+ After an application is loaded, the device enters app mode. The UDI is no
+ longer accessible, but the loaded app can perform cryptographic operations.
+
+The primary use case is full disk encryption (FDE) key derivation, where the
+TKey combines a User-Supplied Secret (USS, typically a password) with its
+internal UDI to generate deterministic encryption keys.
+
+
+tkey connect
+~~~~~~~~~~~~
+
+Test connectivity to a TKey device. This command attempts to find and connect
+to the first available TKey device in the system.
+
+
+tkey fwmode
+~~~~~~~~~~~
+
+Check whether the TKey device is currently in firmware mode or app mode.
+
+Firmware mode
+ The device has just been plugged in or reset. The UDI is accessible and
+ apps can be loaded.
+
+App mode
+ An application has been loaded and is running. The UDI is not accessible.
+
+
+tkey getkey
+~~~~~~~~~~~
+
+Derive a disk encryption key by loading the embedded signer app with a
+User-Supplied Secret (USS). This is the main command for full-disk-encryption
+workflows.
+
+The command performs these steps:
+
+#. Loads the embedded signer app with the provided USS
+#. Retrieves the public key from the signer app
+#. Derives the disk encryption key (BLAKE2b hash of the public key)
+#. Computes a verification hash (BLAKE2b hash of the disk key)
+
+The USS is typically a user password or passphrase. The same USS always produces
+the same disk encryption key, making this suitable for unlocking encrypted
+disks.
+
+If a verification hash is provided as the second argument, the command verifies
+that the USS is correct by comparing the computed hash with the expected hash.
+This is useful for validating user passwords before attempting to decrypt a
+disk.
+
+uss
+ User-Supplied Secret (password/passphrase) for key derivation
+
+verify-hash
+ Optional 64-character hex string to verify the USS is correct
+
+The command outputs:
+
+Public Key
+ 32-byte Ed25519 public key derived from USS
+
+Disk Key
+ 32-byte encryption key (BLAKE2b-256 of public key)
+
+Verification Hash
+ 32-byte hash of disk key (save this for later verification)
+
+
+tkey info
+~~~~~~~~~
+
+Display information about the TKey device, including its name, version, and
+Unique Device Identifier (UDI).
+
+The UDI is only available in firmware mode. If the device is in app mode, the
+command will report that the UDI is not available and suggest replugging the
+device.
+
+
+tkey loadapp
+~~~~~~~~~~~~
+
+Load the embedded signer application to the TKey device. This can only be done
+when the device is in firmware mode.
+
+An optional USS (User-Supplied Secret) can be provided, which will be used by
+the signer app for key derivation. If no USS is provided, the app loads without
+a secret.
+
+After loading, the device transitions to app mode and the UDI becomes
+inaccessible.
+
+uss
+ Optional User-Supplied Secret for the signer app
+
+
+tkey signer
+~~~~~~~~~~~
+
+Display information about the embedded signer application binary that is
+compiled into U-Boot.
+
+
+tkey wrapkey
+~~~~~~~~~~~~
+
+Derive a wrapping key from a password and the device's UDI. This uses the
+BLAKE2b-256 hash function to combine the UDI with the password.
+
+The wrapping key can be used to encrypt/decrypt other secrets. Unlike getkey,
+this command does not load an app - it only requires the UDI, so it must be
+run in firmware mode.
+
+The same password always produces the same wrapping key for a given device,
+but different TKey devices (with different UDIs) will produce different
+wrapping keys even with the same password.
+
+password
+ Password to combine with UDI for key derivation
+
+
+Example
+-------
+
+Connect to device::
+
+ => tkey connect
+ Connected to TKey device
+
+Check device mode::
+
+ => tkey fwmode
+ firmware mode
+
+ => tkey loadapp
+ Loading signer app (a9c bytes)...
+
+ => tkey fwmode
+ app mode
+
+Get device information (firmware mode)::
+
+ => tkey info
+ Name0: tk1 Name1: mkdf Version: 4
+ UDI: a0a1a2a3a4a5a6a7
+
+Get device information (app mode)::
+
+ => tkey info
+ Name0: tk1 Name1: sign Version: 4
+ UDI not available - replug device
+
+Derive disk encryption key without verification::
+
+ => tkey getkey mypassword
+ Public Key: 1a2b3c4d...
+ Disk Key: 9f8e7d6c...
+ Verification Hash: 5a4b3c2d...
+
+Derive disk encryption key with verification (correct password)::
+
+ => tkey getkey mypassword 5a4b3c2d1e0f...
+ Public Key: 1a2b3c4d...
+ Disk Key: 9f8e7d6c...
+
+ password correct
+
+Derive disk encryption key with verification (wrong password)::
+
+ => tkey getkey wrongpassword 5a4b3c2d1e0f...
+ Public Key: aaaa1111...
+ Disk Key: bbbb2222...
+
+ wrong password
+ Expected: 5a4b3c2d1e0f...
+ Got: cccc3333...
+
+Load app without USS::
+
+ => tkey loadapp
+ Loading signer app (a9c bytes)...
+
+Load app with USS::
+
+ => tkey loadapp mypassword
+ Loading signer app (a9c bytes) with USS...
+
+Show signer binary information::
+
+ => tkey signer
+ signer binary: a9c bytes at 0x1234-0x5678
+
+Derive wrapping key from password::
+
+ => tkey wrapkey mypassword
+ Wrapping Key: 95229cd376898f3fb022a627349dc985bc4675da580d2696bdd6f71f488e306c
+
+
+Configuration
+-------------
+
+The tkey command is available if CONFIG_CMD_TKEY is enabled. The command
+requires a TKey driver to be configured (USB or serial).
+
+
+See also
+--------
+
+* `Tillitis TKey documentation <https://tillitis.se/>`_
@@ -130,6 +130,7 @@ Shell commands
cmd/source
cmd/tcpm
cmd/temperature
+ cmd/tkey
cmd/tftpput
cmd/trace
cmd/true
@@ -43,6 +43,7 @@ obj-$(CONFIG_CMD_READ) += rw.o
obj-$(CONFIG_CMD_SETEXPR) += setexpr.o
obj-$(CONFIG_CMD_SMBIOS) += smbios.o
obj-$(CONFIG_CMD_TEMPERATURE) += temperature.o
+obj-$(CONFIG_CMD_TKEY) += tkey.o
ifdef CONFIG_NET
obj-$(CONFIG_CMD_WGET) += wget.o
endif
new file mode 100644
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for tkey command
+ *
+ * Copyright (C) 2025 Canonical Ltd
+ */
+
+#include <console.h>
+#include <dm.h>
+#include <dm/test.h>
+#include <test/cmd.h>
+#include <test/ut.h>
+
+/* Test 'tkey' command help output */
+static int cmd_test_tkey_help(struct unit_test_state *uts)
+{
+ ut_asserteq(1, run_command("tkey", 0));
+ ut_assert_nextlinen("tkey - Tillitis TKey security token operations");
+ ut_assert_nextline_empty();
+ ut_assert_nextlinen("Usage:");
+ ut_assert_nextlinen("tkey connect");
+ ut_assert_skip_to_linen("tkey wrapkey");
+
+ return 0;
+}
+CMD_TEST(cmd_test_tkey_help, UTF_DM | UTF_SCAN_FDT | UTF_CONSOLE);
+
+/* Test 'tkey' subcommands with emulator */
+static int cmd_test_tkey_sandbox(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ /* TKey device should be available in sandbox */
+ ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev));
+
+ /* Test info command */
+ ut_assertok(run_command("tkey info", 0));
+ ut_assert_nextline("Name0: tk1 Name1: mkdf Version: 4");
+ ut_assert_nextline("UDI: a0a1a2a3a4a5a6a7");
+
+ /* Test fwmode command - should be in firmware mode initially */
+ ut_assertok(run_command("tkey fwmode", 0));
+ ut_assert_nextline("firmware mode");
+
+ /* Test signer command */
+ ut_assertok(run_command("tkey signer", 0));
+ ut_assert_nextlinen("signer binary: ");
+
+ /* Test wrapkey command */
+ ut_assertok(run_command("tkey wrapkey testpass", 0));
+ ut_assert_nextline("Wrapping Key: f91450f0396768885aeaee7f0cc3305de25f6e50db79e7978a83c08896fcbf0d");
+
+ /* Test getkey command */
+ ut_assertok(run_command("tkey getkey testuss", 0));
+ ut_assert_nextline("Public Key: 505152535455565758595a5b5c5d5e5f505152535455565758595a5b5c5d5e5f");
+ ut_assert_nextline("Disk Key: 228b2f6abf8be05649b2417586150bbf3e1b3f669afa1c6151ddc72957933c21");
+ ut_assert_nextline("Verification Hash: a72a46b8f8c7ff0824416ada886f62b6c2808896d71201a32814ab432c7a81cf");
+
+ /* After getkey, device should be in app mode */
+ ut_assertok(run_command("tkey fwmode", 0));
+ ut_assert_nextline("app mode");
+
+ ut_assert_console_end();
+
+ return 0;
+}
+CMD_TEST(cmd_test_tkey_sandbox, UTF_DM | UTF_SCAN_FDT | UTF_CONSOLE);