Files
u-boot/cmd/tkey.c
Simon Glass 6873d2bf79 tkey: Allow using the selected TKey from luks
Export the function which sets the current TKey so that the luks command
can work with 'tkey connect'.

Signed-off-by: Simon Glass <simon.glass@canonical.com>
2025-12-07 17:18:57 -07:00

325 lines
7.8 KiB
C

// 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 device pointer set by tkey connect command */
static struct udevice *tkey_dev;
struct udevice *tkey_get_device(void)
{
struct udevice *dev;
int ret;
/* If a device was set by tkey connect, return it */
if (tkey_dev)
return tkey_dev;
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;
int ret;
/* Check if device name is provided as optional first argument */
if (argc > 1) {
const char *dev_name = argv[1];
ret = uclass_get_device_by_name(UCLASS_TKEY, dev_name, &dev);
if (ret) {
printf("Failed to find TKey device '%s' (err %dE)\n",
dev_name, ret);
return CMD_RET_FAILURE;
}
} else {
ret = uclass_first_device_err(UCLASS_TKEY, &dev);
if (ret) {
printf("No device found (err %dE)\n", ret);
return CMD_RET_FAILURE;
}
}
/* Set the static device pointer for subsequent commands */
tkey_dev = dev;
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 [device-name] - Connect to TKey device\n"
" Optional device-name to connect to specific 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, 2, 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));