From patchwork Sun Oct 19 07:23:05 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 629 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1760858636; bh=q+XJLbl9u1u/mWo4WBR75OfHAa36qdCH8CAS8rgYcBE=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=fhSkv/B0htoWvB3/nkTbRZ+xa4odg42/AfDlnaQHubnM4wLn4Jy49yZ0PT7iKAwwU Vtmq54XmPzTeBgrs1itwRWH/QeCT0XUaPf1eNg7tS1T26qLeJwE6VeQ2F/4TqAzmhZ GDgWWBdlc3MomWVww6ymAY7JtOcKrNEUlDNq3oPopTqWrgnZSJvyv2EDulkkm0Gpor KF7YYS7W32iNz0Z1CZ//eKXYLdLOEZYklWKMQwESVkVL9YGtmJJOHCx9YitfydtH0p 1pFoKahwOqZVEwiW0L2YC9EpjkTYARiUUZYle3/qHsFir+V0+CKo+diEZzYYmr6BAH OYiXVRC3/WPEA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 9388668198 for ; Sun, 19 Oct 2025 01:23:56 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id ZUennoUsIB3v for ; Sun, 19 Oct 2025 01:23:56 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1760858636; bh=q+XJLbl9u1u/mWo4WBR75OfHAa36qdCH8CAS8rgYcBE=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=fhSkv/B0htoWvB3/nkTbRZ+xa4odg42/AfDlnaQHubnM4wLn4Jy49yZ0PT7iKAwwU Vtmq54XmPzTeBgrs1itwRWH/QeCT0XUaPf1eNg7tS1T26qLeJwE6VeQ2F/4TqAzmhZ GDgWWBdlc3MomWVww6ymAY7JtOcKrNEUlDNq3oPopTqWrgnZSJvyv2EDulkkm0Gpor KF7YYS7W32iNz0Z1CZ//eKXYLdLOEZYklWKMQwESVkVL9YGtmJJOHCx9YitfydtH0p 1pFoKahwOqZVEwiW0L2YC9EpjkTYARiUUZYle3/qHsFir+V0+CKo+diEZzYYmr6BAH OYiXVRC3/WPEA== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 840D6680DD for ; Sun, 19 Oct 2025 01:23:56 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1760858634; bh=kjS1rNhG1GPmlHgYKnCICu0j0ik9Fw7uPLLlZFnNBgg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=YR+DSNi6vY8THFmWfqnaaGF1z1LUfnG4+Cx77a0zrUlU3rIheA9nlDDkGRFg21UwL XRQijxosLCkP0jOy59TwQmQ4pybi4LVZd3rMcZI5mH7r6cTBVAFNcAxaacbSP678ff E+UGM/PsjI2FfluWyhsdM3JBiVZLZMs/zyLlkoke50WTJBXUBQi+84Hth0gFzUg6tP Cdng8qtIhp0u/7aTAe5Hgj38okPw4y8B7nIFCkvvto4FVmgZgfskctcAg0HW1MpYSQ YAjoc/07sycahh7O8suAqOmDkWjM0fYVaAIVrBcventk27V1j1kh31AHYP0wciblp3 xA7Wq7HK1iKLQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 39811681B3; Sun, 19 Oct 2025 01:23:54 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id sWKUCGnbr6FB; Sun, 19 Oct 2025 01:23:54 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1760858632; bh=xiK2k5gwbwN0y5zu+W3yaW/+epXHr225voLYGzt7S/s=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ljRnfeyfa3MG2cUMUq5uCS6LezUe5d7LENDPy4efBI4VlUIDXsjA9MxWITzcYihMX Cyr7Nlf26zAA/TwI7+uWFOwbZDtTV1tG6r0poWfmtKNmkcl/V2GIYHgfkH7MrQs+Nu u5itdKXY1xC5bFUHyK43r+Cpxr6JbyFRw4s3O8UH48QJdCk7oeKebr0+xX00MlfUfi lViuGt9rMFzgRrwucP4o/D3YrwHltxfSgn5QPT3WpHKLv8vs2mZXnLM8UU/frjLXWm GDKuseXFcce3/cNY6fEXMta15callvkc29sZqGu5d/HIUxEdPEEzIaWmStJS4dr/ct q1vNWUf7acFQw== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id AD69D6810E; Sun, 19 Oct 2025 01:23:52 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Sun, 19 Oct 2025 01:23:05 -0600 Message-ID: <20251019072313.3235339-6-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251019072313.3235339-1-sjg@u-boot.org> References: <20251019072313.3235339-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: VE44KFCEPW4ZEO2TQCPCD7JJ7LHFMJMG X-Message-ID-Hash: VE44KFCEPW4ZEO2TQCPCD7JJ7LHFMJMG X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass , Claude X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 5/8] tkey: Add emulator and test List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Provide a simple emulator which can handle the TKey operations. Add a test which uses it. Co-developed-by: Claude Signed-off-by: Simon Glass --- arch/sandbox/dts/test.dts | 4 + drivers/misc/Makefile | 3 + drivers/misc/tkey_emul.c | 284 +++++++++++++++++++++++++++++++++++++ test/dm/Makefile | 1 + test/dm/tkey.c | 290 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 582 insertions(+) create mode 100644 drivers/misc/tkey_emul.c create mode 100644 test/dm/tkey.c diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 86c01545462..9b0a5736cf8 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1123,6 +1123,10 @@ }; }; + tkey-emul { + compatible = "tkey,emul"; + }; + mmc2 { compatible = "sandbox,mmc"; non-removable; diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 8db02bb9614..298bc1c0a69 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -18,6 +18,9 @@ obj-$(CONFIG_CROS_EC_I2C) += cros_ec_i2c.o obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o obj-$(CONFIG_SANDBOX) += p2sb_sandbox.o p2sb_emul.o obj-$(CONFIG_SANDBOX) += swap_case.o +ifdef CONFIG_SANDBOX +obj-$(CONFIG_TKEY) += tkey_emul.o +endif endif ifdef CONFIG_$(PHASE_)DM_I2C diff --git a/drivers/misc/tkey_emul.c b/drivers/misc/tkey_emul.c new file mode 100644 index 00000000000..d8d81280486 --- /dev/null +++ b/drivers/misc/tkey_emul.c @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Canonical Ltd + * + * TKey emulator for testing TKey functionality in sandbox + */ + +#define LOG_CATEGORY UCLASS_TKEY + +#include +#include +#include +#include +#include +#include +#include +#include + +/* TKey protocol frame structure */ +#define FRAME_SIZE 128 +#define FRAME_HEADER_SIZE 1 +#define FRAME_DATA_SIZE (FRAME_SIZE - FRAME_HEADER_SIZE) + +/* Frame header bit masks and values */ +#define FRAME_ENDPOINT_MASK 0x18 +#define FRAME_ENDPOINT_SHIFT 3 +#define ENDPOINT_FIRMWARE 2 +#define ENDPOINT_APP 3 + +/* Firmware Commands */ +#define FW_CMD_GET_NAME_VERSION 0x01 +#define FW_CMD_GET_UDI 0x08 +#define FW_CMD_LOAD_APP 0x03 +#define FW_CMD_LOAD_APP_DATA 0x05 + +/* App Commands */ +#define APP_CMD_GET_PUBKEY 0x01 + +/* USB Response format markers */ +#define USB_FRAME_MARKER 0x52 +#define USB_RSP_NAME_VERSION 0x02 +#define USB_RSP_GET_UDI 0x09 + +/* Status codes */ +#define STATUS_OK 0x00 +#define STATUS_ERROR 0x01 + +/* + * struct tkey_emul_priv - TKey emulator state + * + * @app_loaded: Whether an app is loaded (app mode vs firmware mode) + * @udi: Unique Device Identifier (8 bytes) + * @app_size: Size of loaded app + * @pubkey: Simulated public key (32 bytes) + * @resp: Buffer for storing response to be read + * @resp_len: Length of data in response buffer + * @total_loaded: Track total app data loaded + */ +struct tkey_emul_priv { + bool app_loaded; + u8 udi[8]; + u32 app_size; + u8 pubkey[32]; + u8 resp[FRAME_SIZE]; + int resp_len; + u32 total_loaded; +}; + +static int tkey_emul_read(struct udevice *dev, void *buf, int len, + int timeout_ms) +{ + /* + * Read operations are immediate with no actual I/O. The data is + * prepared by write operations in the emulated response buffer + */ + log_debug("read: %d bytes requested\n", len); + + return -ENOSYS; +} + +static int handle_fw_get_name_version(struct tkey_emul_priv *priv) +{ + /* USB format: 0x52 0x02 [tk1 ] [name1] [version] */ + priv->resp[0] = USB_FRAME_MARKER; + priv->resp[1] = USB_RSP_NAME_VERSION; + memcpy(priv->resp + 2, "tk1 ", 4); + + /* name1 changes based on firmware vs app mode */ + if (priv->app_loaded) + memcpy(priv->resp + 6, "sign", 4); + else + memcpy(priv->resp + 6, "mkdf", 4); + put_unaligned_le32(4, priv->resp + 10); + priv->resp_len = 14; + log_debug("GET_NAME_VERSION (mode=%s)\n", + priv->app_loaded ? "app" : "firmware"); + + return 0; +} + +static int handle_fw_get_udi(struct tkey_emul_priv *priv) +{ + /* UDI is only available in firmware mode */ + if (priv->app_loaded) { + priv->resp_len = 0; + log_debug("GET_UDI rejected (app mode)\n"); + } else { + priv->resp[0] = USB_FRAME_MARKER; + priv->resp[1] = USB_RSP_GET_UDI; + priv->resp[2] = STATUS_OK; + memcpy(priv->resp + 3, priv->udi, 8); + priv->resp_len = 11; + log_debug("GET_UDI OK\n"); + } + + return 0; +} + +static int handle_fw_load_app(struct tkey_emul_priv *priv, const u8 *data) +{ + /* App size is in bytes 2-5 (big endian) */ + priv->app_size = get_unaligned_be32(data + 2); + + /* Simple ACK - just return status */ + priv->resp[0] = STATUS_OK; + priv->resp_len = 1; + log_debug("LOAD_APP (size=%u)\n", priv->app_size); + + return 0; +} + +static int handle_fw_load_app_data(struct tkey_emul_priv *priv, const u8 *data) +{ + int chunk_size = get_unaligned_be32(data + 2); + + priv->total_loaded += chunk_size; + + /* Simple ACK */ + priv->resp[0] = STATUS_OK; + priv->resp_len = 1; + + if (priv->total_loaded >= priv->app_size) { + /* App fully loaded - enter app mode */ + priv->app_loaded = true; + priv->total_loaded = 0; + log_debug("App loaded, entering app mode\n"); + } else { + log_debug("LOAD_APP_DATA (%u/%u)\n", + priv->total_loaded, priv->app_size); + } + + return 0; +} + +static int handle_firmware_cmd(struct udevice *dev, u8 cmd, const u8 *data) +{ + struct tkey_emul_priv *priv = dev_get_priv(dev); + + switch (cmd) { + case FW_CMD_GET_NAME_VERSION: + return handle_fw_get_name_version(priv); + case FW_CMD_GET_UDI: + return handle_fw_get_udi(priv); + case FW_CMD_LOAD_APP: + return handle_fw_load_app(priv, data); + case FW_CMD_LOAD_APP_DATA: + return handle_fw_load_app_data(priv, data); + default: + log_err("Unknown firmware command %02x\n", cmd); + return -EINVAL; + } +} + +static int handle_app_get_pubkey(struct tkey_emul_priv *priv) +{ + memcpy(priv->resp, priv->pubkey, 32); + priv->resp_len = 32; + log_debug("GET_PUBKEY\n"); + + return 0; +} + +static int handle_app_cmd(struct udevice *dev, u8 cmd) +{ + struct tkey_emul_priv *priv = dev_get_priv(dev); + + if (!priv->app_loaded) { + log_err("App command sent but not in app mode\n"); + return -EINVAL; + } + + switch (cmd) { + case APP_CMD_GET_PUBKEY: + return handle_app_get_pubkey(priv); + default: + log_err("Unknown app command %02x\n", cmd); + return -EINVAL; + } +} + +static int tkey_emul_write(struct udevice *dev, const void *buf, int len) +{ + const u8 *data = buf; + u8 header, endpoint, cmd; + int ret; + + if (len < 2) + return -EINVAL; + + header = data[0]; + endpoint = (header & FRAME_ENDPOINT_MASK) >> FRAME_ENDPOINT_SHIFT; + cmd = data[1]; + + log_debug("header %02x endpoint %u cmd %02x\n", header, endpoint, cmd); + + /* Route to appropriate endpoint handler */ + if (endpoint == ENDPOINT_FIRMWARE) { + ret = handle_firmware_cmd(dev, cmd, data); + } else if (endpoint == ENDPOINT_APP) { + ret = handle_app_cmd(dev, cmd); + } else { + log_err("Unknown endpoint %u\n", endpoint); + return -EINVAL; + } + + return ret ? ret : len; +} + +static int tkey_emul_read_all(struct udevice *dev, void *buf, int maxlen, + int timeout_ms) +{ + struct tkey_emul_priv *priv = dev_get_priv(dev); + int len = min(priv->resp_len, maxlen); + + log_debug("read_all: %d bytes max, returning %d bytes\n", maxlen, len); + + /* Copy the raw USB response data including the 0x52 marker */ + if (len > 0) + memcpy(buf, priv->resp, len); + + return len; +} + +static int tkey_emul_probe(struct udevice *dev) +{ + struct tkey_emul_priv *priv = dev_get_priv(dev); + int i; + + /* Generate a deterministic UDI based on device name */ + for (i = 0; i < 8; i++) + priv->udi[i] = 0xa0 + i; + + /* Generate a deterministic public key */ + for (i = 0; i < 32; i++) + priv->pubkey[i] = 0x50 + (i & 0xf); + + log_debug("init with UDI: "); + for (i = 0; i < 8; i++) + log_debug("%02x", priv->udi[i]); + log_debug("\n"); + + return 0; +} + +/* TKey uclass operations */ +static const struct tkey_ops tkey_emul_ops = { + .read = tkey_emul_read, + .write = tkey_emul_write, + .read_all = tkey_emul_read_all, +}; + +static const struct udevice_id tkey_emul_ids[] = { + { .compatible = "tkey,emul" }, + { } +}; + +U_BOOT_DRIVER(tkey_emul) = { + .name = "tkey_emul", + .id = UCLASS_TKEY, + .of_match = tkey_emul_ids, + .probe = tkey_emul_probe, + .ops = &tkey_emul_ops, + .priv_auto = sizeof(struct tkey_emul_priv), +}; diff --git a/test/dm/Makefile b/test/dm/Makefile index b38de3e12d0..45aee79c8f2 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -121,6 +121,7 @@ obj-$(CONFIG_SYSINFO) += sysinfo.o obj-$(CONFIG_SYSINFO_GPIO) += sysinfo-gpio.o obj-$(CONFIG_UT_DM) += tag.o obj-$(CONFIG_TEE) += tee.o +obj-$(CONFIG_TKEY) += tkey.o obj-$(CONFIG_TIMER) += timer.o obj-$(CONFIG_TPM_V2) += tpm.o obj-$(CONFIG_DM_USB) += usb.o diff --git a/test/dm/tkey.c b/test/dm/tkey.c new file mode 100644 index 00000000000..9dffae66a5e --- /dev/null +++ b/test/dm/tkey.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Canonical Ltd + * + * Test for TKey uclass and emulator + */ + +#include +#include +#include +#include +#include + +/* Test that we can find a TKey device */ +static int dm_test_tkey_find(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev)); + ut_assertnonnull(dev); + + return 0; +} +DM_TEST(dm_test_tkey_find, UTF_SCAN_FDT); + +/* Test getting UDI from TKey */ +static int dm_test_tkey_get_udi(struct unit_test_state *uts) +{ + u8 udi[TKEY_UDI_SIZE]; + struct udevice *dev; + + ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev)); + + ut_assertok(tkey_get_udi(dev, udi)); + + /* Verify emulator returns expected UDI */ + ut_asserteq(0xa0, udi[0]); + ut_asserteq(0xa1, udi[1]); + ut_asserteq(0xa2, udi[2]); + ut_asserteq(0xa3, udi[3]); + ut_asserteq(0xa4, udi[4]); + ut_asserteq(0xa5, udi[5]); + ut_asserteq(0xa6, udi[6]); + ut_asserteq(0xa7, udi[7]); + + return 0; +} +DM_TEST(dm_test_tkey_get_udi, UTF_SCAN_FDT); + +/* Test getting name and version from TKey */ +static int dm_test_tkey_get_name_version(struct unit_test_state *uts) +{ + char name0[TKEY_NAME_SIZE], name1[TKEY_NAME_SIZE]; + struct udevice *dev; + u32 version; + + ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev)); + + /* Get name and version */ + ut_assertok(tkey_get_name_version(dev, name0, name1, &version)); + + /* Verify emulator returns expected values */ + ut_asserteq_str("tk1 ", name0); + ut_asserteq_str("mkdf", name1); + ut_asserteq(4, version); + + return 0; +} +DM_TEST(dm_test_tkey_get_name_version, UTF_SCAN_FDT); + +/* Test checking firmware mode */ +static int dm_test_tkey_in_app_mode(struct unit_test_state *uts) +{ + struct udevice *dev; + int ret; + + ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev)); + + /* Check mode - should be in firmware mode initially */ + ret = tkey_in_app_mode(dev); + ut_assert(ret >= 0); + ut_asserteq(0, ret); /* 0 = firmware mode */ + + return 0; +} +DM_TEST(dm_test_tkey_in_app_mode, UTF_SCAN_FDT); + +/* Test loading an app */ +static int dm_test_tkey_load_app(struct unit_test_state *uts) +{ + struct udevice *dev; + u8 dummy_app[128]; + int ret; + + ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev)); + + /* Create a dummy app */ + memset(dummy_app, 0x42, sizeof(dummy_app)); + + /* Load the app */ + ret = tkey_load_app(dev, dummy_app, sizeof(dummy_app)); + ut_assertok(ret); + + /* After loading, should be in app mode */ + ret = tkey_in_app_mode(dev); + ut_assert(ret >= 0); + ut_asserteq(1, ret); /* 1 = app mode */ + + return 0; +} +DM_TEST(dm_test_tkey_load_app, UTF_SCAN_FDT); + +/* Test getting public key from signer app */ +static int dm_test_tkey_get_pubkey(struct unit_test_state *uts) +{ + u8 pubkey[TKEY_PUBKEY_SIZE]; + struct udevice *dev; + u8 dummy_app[128]; + int i; + + ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev)); + + /* Load a dummy app first */ + memset(dummy_app, 0x42, sizeof(dummy_app)); + ut_assertok(tkey_load_app(dev, dummy_app, sizeof(dummy_app))); + + /* Get public key */ + ut_assertok(tkey_get_pubkey(dev, pubkey)); + + /* Verify emulator returns expected pattern */ + for (i = 0; i < TKEY_PUBKEY_SIZE; i++) + ut_asserteq(0x50 + (i & 0xf), pubkey[i]); + + return 0; +} +DM_TEST(dm_test_tkey_get_pubkey, UTF_SCAN_FDT); + +/* Test deriving wrapping key from password */ +static int dm_test_tkey_derive_wrapping_key(struct unit_test_state *uts) +{ + u8 wrapping_key[TKEY_WRAPPING_KEY_SIZE]; + const char *password = "test_password"; + /* Expected BLAKE2b(UDI || password) where UDI = a0a1a2a3a4a5a6a7 */ + const u8 expected[TKEY_WRAPPING_KEY_SIZE] = { + 0x95, 0x22, 0x9c, 0xd3, 0x76, 0x89, 0x8f, 0x3f, + 0xb0, 0x22, 0xa6, 0x27, 0x34, 0x9d, 0xc9, 0x85, + 0xbc, 0x46, 0x75, 0xda, 0x58, 0x0d, 0x26, 0x96, + 0xbd, 0xd6, 0xf7, 0x1f, 0x48, 0x8e, 0x30, 0x6c, + }; + struct udevice *dev; + + ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev)); + + /* Derive wrapping key from password */ + ut_assertok(tkey_derive_wrapping_key(dev, password, wrapping_key)); + + /* Verify the exact wrapping key value */ + ut_asserteq_mem(expected, wrapping_key, TKEY_WRAPPING_KEY_SIZE); + + return 0; +} +DM_TEST(dm_test_tkey_derive_wrapping_key, UTF_SCAN_FDT); + +/* Test deriving disk key with USS */ +static int dm_test_tkey_derive_disk_key(struct unit_test_state *uts) +{ + const char *uss = "user_secret"; + u8 disk_key[TKEY_DISK_KEY_SIZE]; + u8 pubkey[TKEY_PUBKEY_SIZE]; + u8 key_hash[TKEY_HASH_SIZE]; + /* Expected pubkey from emulator (deterministic pattern) */ + const u8 expected_pubkey[TKEY_PUBKEY_SIZE] = { + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + }; + /* Expected disk key: BLAKE2b(pubkey) */ + const u8 expected_disk_key[TKEY_DISK_KEY_SIZE] = { + 0x22, 0x8b, 0x2f, 0x6a, 0xbf, 0x8b, 0xe0, 0x56, + 0x49, 0xb2, 0x41, 0x75, 0x86, 0x15, 0x0b, 0xbf, + 0x3e, 0x1b, 0x3f, 0x66, 0x9a, 0xfa, 0x1c, 0x61, + 0x51, 0xdd, 0xc7, 0x29, 0x57, 0x93, 0x3c, 0x21, + }; + /* Expected key hash: BLAKE2b(disk_key) */ + const u8 expected_key_hash[TKEY_HASH_SIZE] = { + 0xa7, 0x2a, 0x46, 0xb8, 0xf8, 0xc7, 0xff, 0x08, + 0x24, 0x41, 0x6a, 0xda, 0x88, 0x6f, 0x62, 0xb6, + 0xc2, 0x80, 0x88, 0x96, 0xd7, 0x12, 0x01, 0xa3, + 0x28, 0x14, 0xab, 0x43, 0x2c, 0x7a, 0x81, 0xcf, + }; + struct udevice *dev; + u8 dummy_app[128]; + + ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev)); + + /* Create a dummy signer app */ + memset(dummy_app, 0x42, sizeof(dummy_app)); + + /* Derive disk key */ + ut_assertok(tkey_derive_disk_key(dev, dummy_app, sizeof(dummy_app), + uss, strlen(uss), disk_key, pubkey, + key_hash)); + + ut_asserteq_mem(expected_pubkey, pubkey, TKEY_PUBKEY_SIZE); + ut_asserteq_mem(expected_disk_key, disk_key, TKEY_DISK_KEY_SIZE); + ut_asserteq_mem(expected_key_hash, key_hash, TKEY_HASH_SIZE); + + return 0; +} +DM_TEST(dm_test_tkey_derive_disk_key, UTF_SCAN_FDT); + +/* Test UDI not available in app mode */ +static int dm_test_tkey_udi_app_mode(struct unit_test_state *uts) +{ + u8 udi[TKEY_UDI_SIZE]; + struct udevice *dev; + u8 dummy_app[128]; + + ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev)); + + /* Load an app to enter app mode */ + memset(dummy_app, 0x42, sizeof(dummy_app)); + ut_assertok(tkey_load_app(dev, dummy_app, sizeof(dummy_app))); + + /* Verify we're in app mode */ + ut_asserteq(1, tkey_in_app_mode(dev)); + + /* Try to get UDI - emulator returns -EIO for empty response */ + ut_asserteq(-EIO, tkey_get_udi(dev, udi)); + + return 0; +} +DM_TEST(dm_test_tkey_udi_app_mode, UTF_SCAN_FDT); + +/* Test loading app with USS */ +static int dm_test_tkey_load_app_with_uss(struct unit_test_state *uts) +{ + struct udevice *dev; + u8 dummy_app[128]; + const char *uss = "my_secret"; + + ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev)); + + /* Create a dummy app */ + memset(dummy_app, 0x55, sizeof(dummy_app)); + + /* Load app with USS */ + ut_assertok(tkey_load_app_with_uss(dev, dummy_app, sizeof(dummy_app), + uss, strlen(uss))); + + /* Should be in app mode */ + ut_asserteq(1, tkey_in_app_mode(dev)); + + return 0; +} +DM_TEST(dm_test_tkey_load_app_with_uss, UTF_SCAN_FDT); + +/* Test basic read/write operations */ +static int dm_test_tkey_read_write(struct unit_test_state *uts) +{ + /* Expected USB response: 0x52 0x02 [tk1 ] [mkdf] [version=4] */ + static const u8 expected[14] = { + 0x52, 0x02, /* USB marker and response type */ + 't', 'k', '1', ' ', /* name0 */ + 'm', 'k', 'd', 'f', /* name1 */ + 0x04, 0x00, 0x00, 0x00, /* version = 4 (little-endian) */ + }; + struct udevice *dev; + u8 write_buf[129]; /* Header + command */ + u8 read_buf[256]; + + ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev)); + + /* Prepare a GET_NAME_VERSION command */ + write_buf[0] = 0x10; /* Header: CMD, FIRMWARE endpoint */ + write_buf[1] = 0x01; /* CMD_GET_NAME_VERSION */ + + /* Write the command - should return 2 bytes written */ + ut_asserteq(2, tkey_write(dev, write_buf, 2)); + + /* Read the response - should get exactly 14 bytes */ + ut_asserteq(14, tkey_read_all(dev, read_buf, sizeof(read_buf), 1000)); + + /* Verify full response matches expected */ + ut_asserteq_mem(expected, read_buf, 14); + + return 0; +} +DM_TEST(dm_test_tkey_read_write, UTF_SCAN_FDT);