feat(prodtest): log Tropic initialization errors
What changed, and why it matters
This commit changes how the Trezor hardware wallet's production-test firmware initializes the Tropic secure chip. Previously, the chip was initialized automatically at boot and failures were silently ignored. Now, initialization is done on-demand inside each production-test command, and any failure is reported back with a detailed error reason. This is primarily a diagnostic improvement for factory testing, not a fix for an exploitable security vulnerability in end-user firmware.
No immediate action required for end users. For manufacturing/QA, verify that the new `PRODTEST_ERR_TROPIC_INIT` error is correctly surfaced in production logs and that on-demand init does not introduce race conditions during parallel or repeated prodtest commands. Review whether removal of `tropic_wait_for_ready()` affects any hardware variants with slower Tropic boot timing.
Security signals we found
Change from silent boolean init failure to explicit error-code reporting
Removal of boot-time Tropic initialization in production-test firmware
Deletion of `tropic_wait_for_ready()` polling loop
Addition of new prodtest error code `PRODTEST_ERR_TROPIC_INIT`
On-demand initialization pattern reduces early-boot failure surface in prodtest
Evidence from the diff
The patch refactors Tropic initialization in the Trezor firmware. The tropic_init() signature changes from returning bool to returning lt_ret_t, and it now accepts an optional cli_t* so that production-test builds can log the libtropic error code via lt_ret_verbose(). A new helper tropic_prodtest_init_and_get_handle() performs on-demand init and returns the handle, or reports PRODTEST_ERR_TROPIC_INIT on failure. The previous boot-time tropic_init(); tropic_wait_for_ready(NULL); calls are removed from prodtest main.c, and tropic_wait_for_ready() is deleted entirely. Kernel, secmon, unix emulator, and suspend-resume paths now call tropic_init(NULL). The change improves observability of Tropic init failures during manufacturing but does not alter cryptographic operations or access controls.
Changed components
core/embed/sec/tropic/tropic.ccore/embed/sec/tropic/inc/sec/tropic.hcore/embed/projects/prodtest/cmd/prodtest_tropic.ccore/embed/projects/prodtest/cmd/prodtest_otp_variant.ccore/embed/projects/prodtest/cmd/prodtest_secrets.ccore/embed/projects/prodtest/main.ccore/embed/projects/kernel/main.ccore/embed/projects/secmon/main.ccore/embed/projects/unix/main_main.ccore/embed/projects/unix/rust_c_setup.ccore/embed/sec/suspend/stm32u5/suspend_io.cInspect captured patch +313 / −181
diff --git a/core/embed/projects/kernel/main.c b/core/embed/projects/kernel/main.c
index b12b12ea..64a430c7 100644
--- a/core/embed/projects/kernel/main.c
+++ b/core/embed/projects/kernel/main.c
@@ -207,7 +207,7 @@ void drivers_init() {
optiga_init_and_configure();
#endif
#ifdef USE_TROPIC
- tropic_init();
+ tropic_init(NULL);
#endif
#endif // SECURE_MODE
diff --git a/core/embed/projects/prodtest/cmd/prodtest_otp_variant.c b/core/embed/projects/prodtest/cmd/prodtest_otp_variant.c
index 0bd9da52..d1e92ec7 100644
--- a/core/embed/projects/prodtest/cmd/prodtest_otp_variant.c
+++ b/core/embed/projects/prodtest/cmd/prodtest_otp_variant.c
@@ -31,6 +31,7 @@
#include "prodtest_optiga.h"
#ifdef USE_TROPIC
+#include <sec/tropic.h>
#include "prodtest_tropic.h"
#endif
@@ -168,7 +169,14 @@ static void prodtest_otp_variant_write(cli_t* cli) {
#endif
#ifdef USE_TROPIC
- tropic_locked_status tropic_status = get_tropic_locked_status(cli);
+ lt_handle_t* tropic_handle = tropic_prodtest_init_and_get_handle(cli);
+ if (tropic_handle == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "`tropic_init()` failed");
+ return;
+ }
+
+ tropic_locked_status tropic_status =
+ get_tropic_locked_status(cli, tropic_handle);
if (tropic_status == TROPIC_LOCKED_FALSE) {
cli_error(cli, PRODTEST_ERR_OTP_VARIANT_TROPIC_NOT_LOCKED,
diff --git a/core/embed/projects/prodtest/cmd/prodtest_secrets.c b/core/embed/projects/prodtest/cmd/prodtest_secrets.c
index 35155dce..ba3a7595 100644
--- a/core/embed/projects/prodtest/cmd/prodtest_secrets.c
+++ b/core/embed/projects/prodtest/cmd/prodtest_secrets.c
@@ -132,13 +132,21 @@ static void prodtest_secrets_init(cli_t* cli) {
return;
}
+ // Make sure the Tropic chip is initialized
+ if (tropic_prodtest_init_and_get_handle(cli) == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "`tropic_init()` failed");
+ return;
+ }
+
// Ensure that a session with Tropic is established so that we can include
// randomness from the chip when generating the secrets. At this point in
// provisioning the factory pairing key should still be valid.
- if (tropic_custom_session_start(cli, TROPIC_FACTORY_PAIRING_KEY_SLOT) !=
- LT_OK) {
+ lt_ret_t ret =
+ tropic_custom_session_start(cli, TROPIC_FACTORY_PAIRING_KEY_SLOT);
+ if (ret != LT_OK) {
cli_error(cli, PRODTEST_ERR_SECRETS_TROPIC_SESSION,
- "`tropic_custom_session_start()` failed.");
+ "`tropic_custom_session_start()` failed with error '%s'",
+ lt_ret_verbose(ret));
return;
}
#endif
diff --git a/core/embed/projects/prodtest/cmd/prodtest_tropic.c b/core/embed/projects/prodtest/cmd/prodtest_tropic.c
index 6c25a350..b7f18bc5 100644
--- a/core/embed/projects/prodtest/cmd/prodtest_tropic.c
+++ b/core/embed/projects/prodtest/cmd/prodtest_tropic.c
@@ -94,7 +94,11 @@ static void prodtest_tropic_get_riscv_fw_version(cli_t* cli) {
return;
}
- lt_handle_t* tropic_handle = tropic_get_handle();
+ lt_handle_t* tropic_handle = tropic_prodtest_init_and_get_handle(cli);
+ if (tropic_handle == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "`tropic_init()` failed");
+ return;
+ }
uint8_t version[TR01_L2_GET_INFO_RISCV_FW_SIZE] = {0};
lt_ret_t ret = lt_get_info_riscv_fw_ver(tropic_handle, version);
@@ -115,7 +119,11 @@ static void prodtest_tropic_get_spect_fw_version(cli_t* cli) {
return;
}
- lt_handle_t* tropic_handle = tropic_get_handle();
+ lt_handle_t* tropic_handle = tropic_prodtest_init_and_get_handle(cli);
+ if (tropic_handle == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "`tropic_init()` failed");
+ return;
+ }
uint8_t version[TR01_L2_GET_INFO_SPECT_FW_SIZE];
lt_ret_t ret = lt_get_info_spect_fw_ver(tropic_handle, version);
@@ -136,8 +144,11 @@ static void prodtest_tropic_get_chip_id(cli_t* cli) {
return;
}
- lt_handle_t* tropic_handle = tropic_get_handle();
-
+ lt_handle_t* tropic_handle = tropic_prodtest_init_and_get_handle(cli);
+ if (tropic_handle == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "`tropic_init()` failed");
+ return;
+ }
lt_chip_id_t chip_id;
lt_ret_t ret = lt_get_info_chip_id(tropic_handle, &chip_id);
if (ret != LT_OK) {
@@ -161,9 +172,15 @@ static void prodtest_tropic_certtropic_read(cli_t* cli) {
return;
}
+ lt_handle_t* tropic_handle = tropic_prodtest_init_and_get_handle(cli);
+ if (tropic_handle == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "`tropic_init()` failed");
+ return;
+ }
+
const uint8_t* tropic_cert_chain = NULL;
size_t tropic_cert_chain_length = 0;
- if (!tropic_get_cert_chain_ptr(cli, &tropic_cert_chain,
+ if (!tropic_get_cert_chain_ptr(cli, tropic_handle, &tropic_cert_chain,
&tropic_cert_chain_length)) {
cli_error(cli, PRODTEST_ERR_TROPIC_CERT_READ,
"`tropic_get_cert_chain_ptr()` failed");
@@ -179,7 +196,13 @@ static void prodtest_tropic_lock_check(cli_t* cli) {
return;
}
- tropic_locked_status status = get_tropic_locked_status(cli);
+ lt_handle_t* tropic_handle = tropic_prodtest_init_and_get_handle(cli);
+ if (tropic_handle == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "`tropic_init()` failed");
+ return;
+ }
+
+ tropic_locked_status status = get_tropic_locked_status(cli, tropic_handle);
switch (status) {
case TROPIC_LOCKED_TRUE:
cli_trace(cli, "Tropic is locked.");
@@ -195,19 +218,18 @@ static void prodtest_tropic_lock_check(cli_t* cli) {
}
}
-tropic_locked_status get_tropic_locked_status(cli_t* cli) {
+tropic_locked_status get_tropic_locked_status(cli_t* cli,
+ lt_handle_t* tropic_handle) {
g_tropic_handshake_state = TROPIC_HANDSHAKE_STATE_0;
- lt_handle_t* tropic_handle = tropic_get_handle();
- lt_ret_t ret = LT_FAIL;
-
curve25519_key tropic_public = {0};
if (secret_key_tropic_public(tropic_public) != sectrue) {
cli_trace(cli, "The Tropic pairing process was not initiated.");
return TROPIC_LOCKED_FALSE;
}
- ret = tropic_custom_session_start(cli, TROPIC_PRIVILEGED_PAIRING_KEY_SLOT);
+ lt_ret_t ret =
+ tropic_custom_session_start(cli, TROPIC_PRIVILEGED_PAIRING_KEY_SLOT);
if (ret != LT_OK) {
if (ret == LT_L2_HSK_ERR) {
cli_trace(cli,
@@ -339,17 +361,15 @@ static lt_ret_t pairing_key_write(cli_t* cli, lt_handle_t* handle,
return LT_OK;
}
-static bool tropic_is_paired(cli_t* cli) {
+static bool tropic_is_paired(cli_t* cli, lt_handle_t* tropic_handle) {
static bool is_paired = false;
if (is_paired) {
return true;
}
- lt_handle_t* tropic_handle = tropic_get_handle();
- lt_ret_t ret = LT_FAIL;
-
// Try to establish a session using the unprivileged key pair.
- ret = tropic_custom_session_start(cli, TROPIC_UNPRIVILEGED_PAIRING_KEY_SLOT);
+ lt_ret_t ret =
+ tropic_custom_session_start(cli, TROPIC_UNPRIVILEGED_PAIRING_KEY_SLOT);
if (ret != LT_OK) {
cli_trace(
cli,
@@ -417,18 +437,24 @@ static void prodtest_tropic_pair(cli_t* cli) {
if (cli_arg_count(cli) > 0) {
cli_error_arg_count(cli);
- goto cleanup;
+ return;
}
g_tropic_handshake_state = TROPIC_HANDSHAKE_STATE_0;
- lt_handle_t* tropic_handle = tropic_get_handle();
+ lt_handle_t* tropic_handle = tropic_prodtest_init_and_get_handle(cli);
+ if (tropic_handle == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "`tropic_init()` failed");
+ return;
+ }
+
+ curve25519_key unprivileged_private = {0};
+ curve25519_key privileged_private = {0};
// Retrieve the unprivileged pairing key pair.
// NOTE: This ensures that secrets-init has already been called before any
// other steps take place. Otherwise, if we wrote Tropic's public key to the
// MCU's flash before completing secrets-init, we would run into a deadlock.
- curve25519_key unprivileged_private = {0};
if (secret_key_tropic_pairing_unprivileged(unprivileged_private) != sectrue) {
cli_error(cli, PRODTEST_ERR_TROPIC_PAIR_PUBKEY_UNPRIV,
"`secret_key_tropic_pairing_unprivileged()` failed.");
@@ -438,7 +464,6 @@ static void prodtest_tropic_pair(cli_t* cli) {
curve25519_scalarmult_basepoint(unprivileged_public, unprivileged_private);
// Retrieve the privileged pairing key pair.
- curve25519_key privileged_private = {0};
if (secret_key_tropic_pairing_privileged(privileged_private) != sectrue) {
cli_error(cli, PRODTEST_ERR_TROPIC_PAIR_PUBKEY_PRIV,
"`secret_key_tropic_pairing_privileged()` failed.");
@@ -449,9 +474,9 @@ static void prodtest_tropic_pair(cli_t* cli) {
// Get the Tropic01 public pairing key from the chip's certificate.
curve25519_key tropic_public = {0};
- if (!tropic_get_pubkey(cli, tropic_public)) {
+ if (!tropic_get_pubkey(cli, tropic_handle, tropic_public)) {
cli_error(cli, PRODTEST_ERR_TROPIC_PAIR_GET_PUBKEY,
- "`tropic_get_tropic_pubkey()` failed");
+ "`tropic_get_pubkey()` failed");
goto cleanup;
}
@@ -527,7 +552,7 @@ static void prodtest_tropic_pair(cli_t* cli) {
}
}
- if (!tropic_is_paired(cli)) {
+ if (!tropic_is_paired(cli, tropic_handle)) {
cli_error(cli, PRODTEST_ERR_TROPIC_PAIR_IS_PAIRED_FAILED,
"`tropic_is_paired()` failed.");
goto cleanup;
@@ -549,6 +574,12 @@ static void prodtest_tropic_get_access_credential(cli_t* cli) {
g_tropic_handshake_state = TROPIC_HANDSHAKE_STATE_0;
+ lt_handle_t* tropic_handle = tropic_prodtest_init_and_get_handle(cli);
+ if (tropic_handle == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "`tropic_init()` failed");
+ return;
+ }
+
curve25519_key unprivileged_private = {0};
if (secret_key_tropic_pairing_unprivileged(unprivileged_private) != sectrue) {
cli_error(cli, PRODTEST_ERR_TROPIC_ACCESS_CRED_PUBKEY,
@@ -557,9 +588,9 @@ static void prodtest_tropic_get_access_credential(cli_t* cli) {
}
curve25519_key tropic_public = {0};
- if (!tropic_get_pubkey(cli, tropic_public)) {
+ if (!tropic_get_pubkey(cli, tropic_handle, tropic_public)) {
cli_error(cli, PRODTEST_ERR_TROPIC_ACCESS_CRED_GET_PUBKEY,
- "`tropic_get_tropic_pubkey()` failed");
+ "`tropic_get_pubkey()` failed");
goto cleanup;
}
@@ -655,7 +686,13 @@ static void prodtest_tropic_handshake(cli_t* cli) {
return;
}
- if (!tropic_is_paired(cli)) {
+ lt_handle_t* tropic_handle = tropic_prodtest_init_and_get_handle(cli);
+ if (tropic_handle == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "`tropic_init()` failed");
+ return;
+ }
+
+ if (!tropic_is_paired(cli, tropic_handle)) {
cli_error(cli, PRODTEST_ERR_TROPIC_HANDSHAKE_NOT_PAIRED,
"`tropic-pair` must be called first.");
return;
@@ -680,11 +717,10 @@ static void prodtest_tropic_handshake(cli_t* cli) {
return;
}
- lt_ret_t ret = LT_FAIL;
- lt_l2_state_t l2_state = tropic_get_handle()->l2;
+ lt_l2_state_t l2_state = tropic_handle->l2;
size_t request_length = 0;
- ret = l2_get_req_len(input, sizeof(input), &request_length);
+ lt_ret_t ret = l2_get_req_len(input, sizeof(input), &request_length);
if (ret != LT_OK) {
cli_error(cli, PRODTEST_ERR_TROPIC_HANDSHAKE_REQ_LEN,
"`get_req_len()` failed with error '%s'.", lt_ret_verbose(ret));
@@ -787,11 +823,16 @@ static void prodtest_tropic_send_command(cli_t* cli) {
return;
}
- lt_ret_t ret = LT_FAIL;
- lt_l2_state_t l2_state = tropic_get_handle()->l2;
+ lt_handle_t* tropic_handle = tropic_prodtest_init_and_get_handle(cli);
+ if (tropic_handle == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "`tropic_init()` failed");
+ return;
+ }
+
+ lt_l2_state_t l2_state = tropic_handle->l2;
size_t command_length = 0;
- ret = l3_get_frame_len(input, sizeof(input), &command_length);
+ lt_ret_t ret = l3_get_frame_len(input, sizeof(input), &command_length);
if (ret != LT_OK) {
cli_error(cli, PRODTEST_ERR_TROPIC_CMD_L3_LEN_REQ,
"`l3_get_cmd_len()` failed with error '%s'.",
@@ -944,16 +985,22 @@ static void prodtest_tropic_lock(cli_t* cli) {
return;
}
- if (!tropic_is_paired(cli)) {
+ lt_handle_t* tropic_handle = tropic_prodtest_init_and_get_handle(cli);
+ if (tropic_handle == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "`tropic_init()` failed");
+ return;
+ }
+
+ if (!tropic_is_paired(cli, tropic_handle)) {
cli_error(cli, PRODTEST_ERR_TROPIC_LOCK_NOT_PAIRED,
"`tropic-pair` must be called first.");
return;
}
g_tropic_handshake_state = TROPIC_HANDSHAKE_STATE_0;
- lt_ret_t ret = LT_FAIL;
- ret = tropic_custom_session_start(cli, TROPIC_PRIVILEGED_PAIRING_KEY_SLOT);
+ lt_ret_t ret =
+ tropic_custom_session_start(cli, TROPIC_PRIVILEGED_PAIRING_KEY_SLOT);
if (ret != LT_OK) {
cli_error(cli, PRODTEST_ERR_TROPIC_LOCK_INIT,
"`tropic_custom_session_start()` for privileged key failed with "
@@ -970,9 +1017,6 @@ static void prodtest_tropic_lock(cli_t* cli) {
return;
}
- lt_config_t configuration_read = {0};
- lt_handle_t* tropic_handle = tropic_get_handle();
-
ret = lt_r_config_erase(tropic_handle);
if (ret != LT_OK) {
cli_error(cli, PRODTEST_ERR_TROPIC_LOCK_ERASE,
@@ -989,6 +1033,7 @@ static void prodtest_tropic_lock(cli_t* cli) {
return;
}
+ lt_config_t configuration_read = {0};
ret = lt_read_whole_R_config(tropic_handle, &configuration_read);
if (ret != LT_OK) {
cli_error(cli, PRODTEST_ERR_TROPIC_LOCK_R_CONFIG_VERIFY_READ,
@@ -1136,9 +1181,7 @@ static lt_ret_t data_write(lt_handle_t* h, uint16_t first_slot,
uint16_t slot = first_slot;
while (slot <= last_data_slot) {
- lt_ret_t ret = LT_FAIL;
-
- ret = lt_r_mem_data_erase(h, slot);
+ lt_ret_t ret = lt_r_mem_data_erase(h, slot);
if (ret != LT_OK) {
return ret;
}
@@ -1208,7 +1251,13 @@ static bool check_device_cert_chain(cli_t* cli, const uint8_t* chain,
ed25519_signature signature = {0};
- lt_ret_t ret = lt_ecc_eddsa_sign(tropic_get_handle(), TROPIC_DEVICE_KEY_SLOT,
+ lt_handle_t* tropic_handle = tropic_prodtest_init_and_get_handle(cli);
+ if (tropic_handle == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "`tropic_init()` failed");
+ return false;
+ }
+
+ lt_ret_t ret = lt_ecc_eddsa_sign(tropic_handle, TROPIC_DEVICE_KEY_SLOT,
challenge, sizeof(challenge), signature);
if (ret != LT_OK) {
cli_error(cli, PRODTEST_ERR_TROPIC_CERT_SIGN,
@@ -1247,6 +1296,12 @@ static void cert_write(cli_t* cli, uint16_t first_slot, uint16_t slots_count) {
g_tropic_handshake_state = TROPIC_HANDSHAKE_STATE_0;
+ lt_handle_t* tropic_handle = tropic_prodtest_init_and_get_handle(cli);
+ if (tropic_handle == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "`tropic_init()` failed");
+ return;
+ }
+
lt_ret_t ret =
tropic_custom_session_start(cli, TROPIC_PRIVILEGED_PAIRING_KEY_SLOT);
if (ret != LT_OK) {
@@ -1263,8 +1318,6 @@ static void cert_write(cli_t* cli, uint16_t first_slot, uint16_t slots_count) {
return;
}
- lt_handle_t* tropic_handle = tropic_get_handle();
-
ret = data_write(tropic_handle, first_slot, slots_count, certificate,
certificate_length);
if (ret != LT_OK) {
@@ -1299,9 +1352,15 @@ static void cert_read(cli_t* cli, uint16_t first_slot, uint16_t slots_count) {
}
g_tropic_handshake_state = TROPIC_HANDSHAKE_STATE_0;
- lt_ret_t ret = LT_FAIL;
- ret = tropic_custom_session_start(cli, TROPIC_PRIVILEGED_PAIRING_KEY_SLOT);
+ lt_handle_t* tropic_handle = tropic_prodtest_init_and_get_handle(cli);
+ if (tropic_handle == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "`tropic_init()` failed");
+ return;
+ }
+
+ lt_ret_t ret =
+ tropic_custom_session_start(cli, TROPIC_PRIVILEGED_PAIRING_KEY_SLOT);
if (ret != LT_OK) {
cli_error(cli, PRODTEST_ERR_TROPIC_CERT_READ_DATA,
"`tropic_custom_session_start()` for privileged key failed with "
@@ -1312,7 +1371,7 @@ static void cert_read(cli_t* cli, uint16_t first_slot, uint16_t slots_count) {
uint8_t certificate[TROPIC_SLOT_MAX_SIZE_V1 * slots_count];
size_t certificate_length = 0;
- ret = data_read(tropic_get_handle(), first_slot, slots_count, certificate,
+ ret = data_read(tropic_handle, first_slot, slots_count, certificate,
sizeof(certificate), &certificate_length);
if (ret != LT_OK) {
cli_error(cli, PRODTEST_ERR_TROPIC_CERT_READ_FAILED,
@@ -1347,9 +1406,14 @@ static void pubkey_read(cli_t* cli, lt_ecc_slot_t slot,
return;
}
- lt_ret_t ret = LT_FAIL;
+ lt_handle_t* tropic_handle = tropic_prodtest_init_and_get_handle(cli);
+ if (tropic_handle == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "`tropic_init()` failed");
+ return;
+ }
- ret = tropic_custom_session_start(cli, TROPIC_PRIVILEGED_PAIRING_KEY_SLOT);
+ lt_ret_t ret =
+ tropic_custom_session_start(cli, TROPIC_PRIVILEGED_PAIRING_KEY_SLOT);
if (ret != LT_OK) {
cli_error(cli, PRODTEST_ERR_TROPIC_PUBKEY_READ_LT,
"`tropic_custom_session_start()` for privileged key failed with "
@@ -1361,7 +1425,7 @@ static void pubkey_read(cli_t* cli, lt_ecc_slot_t slot,
uint8_t public_key[ECDSA_PUBLIC_KEY_SIZE] = {0x04};
lt_ecc_curve_type_t curve_type = 0;
lt_ecc_key_origin_t origin = 0;
- ret = lt_ecc_key_read(tropic_get_handle(), slot, &public_key[1],
+ ret = lt_ecc_key_read(tropic_handle, slot, &public_key[1],
ECDSA_PUBLIC_KEY_SIZE - 1, &curve_type, &origin);
if (ret != LT_OK) {
cli_error(cli, PRODTEST_ERR_TROPIC_PUBKEY_READ_KEY,
@@ -1408,7 +1472,11 @@ static void prodtest_tropic_update_fw(cli_t* cli) {
return;
}
- lt_handle_t* h = tropic_get_handle();
+ lt_handle_t* h = tropic_prodtest_init_and_get_handle(cli);
+ if (h == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "`tropic_init()` failed");
+ return;
+ }
lt_chip_id_t chip_id = {0};
if (lt_get_info_chip_id(h, &chip_id) != LT_OK) {
@@ -1625,18 +1693,20 @@ static void prodtest_tropic_stress_init(cli_t* cli) {
g_tropic_handshake_state = TROPIC_HANDSHAKE_STATE_0;
+ if (tropic_prodtest_init_and_get_handle(cli) == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "Initial `tropic_init()` failed");
+ return;
+ }
+
for (int i = 0; i < iterations; i++) {
tropic_deinit();
// Simulate a delay between suspend and wake-up.
systick_delay_ms(delay_ms);
- if (!tropic_init()) {
+ lt_ret_t ret = tropic_init(cli);
+ if (ret != LT_OK) {
cli_error(cli, PRODTEST_ERR_TROPIC_STRESS_INIT,
- "Call #%d of `tropic_init()` failed", i + 1);
- return;
- }
- if (!tropic_wait_for_ready(cli)) {
- cli_error(cli, PRODTEST_ERR_TROPIC_STRESS_READY,
- "Call #%d of `tropic_wait_for_ready()` failed", i + 1);
+ "Call #%d of `tropic_init()` failed with error '%s'", i + 1,
+ lt_ret_verbose(ret));
return;
}
}
@@ -1651,6 +1721,11 @@ static void prodtest_tropic_stress_session(cli_t* cli) {
}
cli_trace(cli, "Session iterations: %u.", (unsigned)iterations);
+ if (tropic_prodtest_init_and_get_handle(cli) == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "Initial `tropic_init()` failed");
+ return;
+ }
+
lt_pkey_index_t pairing_key_index = -1;
if (!tropic_ensure_session(cli, &pairing_key_index)) {
return;
@@ -1690,6 +1765,12 @@ static void prodtest_tropic_stress_mac_and_destroy(cli_t* cli) {
cli_trace(cli, "Iterations per slot: %u. Slot count: %u. Explicit slot: %d.",
(unsigned)iterations, (unsigned)slot_count, (int)explicit_slot);
+ lt_handle_t* h = tropic_prodtest_init_and_get_handle(cli);
+ if (h == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "Initial `tropic_init()` failed");
+ return;
+ }
+
lt_pkey_index_t pairing_key_index = -1;
if (!tropic_ensure_session(cli, &pairing_key_index)) {
return;
@@ -1710,7 +1791,6 @@ static void prodtest_tropic_stress_mac_and_destroy(cli_t* cli) {
return;
}
- lt_handle_t* h = tropic_get_handle();
for (int s = 0; s < slot_count; s++) {
lt_mac_and_destroy_slot_t slot = slots[s];
for (int i = 0; i < iterations; i++) {
@@ -1750,6 +1830,12 @@ static void prodtest_tropic_test_mac_and_destroy(cli_t* cli) {
cli_trace(cli, "Iterations per slot: %u. Slot count: %u. Explicit slot: %d.",
(unsigned)iterations, (unsigned)slot_count, (int)explicit_slot);
+ lt_handle_t* h = tropic_prodtest_init_and_get_handle(cli);
+ if (h == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "Initial `tropic_init()` failed");
+ return;
+ }
+
lt_pkey_index_t pairing_key_index = -1;
if (!tropic_ensure_session(cli, &pairing_key_index)) {
return;
@@ -1770,7 +1856,6 @@ static void prodtest_tropic_test_mac_and_destroy(cli_t* cli) {
return;
}
- lt_handle_t* h = tropic_get_handle();
for (int s = 0; s < slot_count; s++) {
lt_mac_and_destroy_slot_t slot = slots[s];
@@ -1840,11 +1925,16 @@ static void prodtest_tropic_test_sign(cli_t* cli) {
cli_trace(cli, "Signing iterations: %u. ECC slot: %d.", (unsigned)iterations,
TR01_ECC_SLOT_31);
+ lt_handle_t* h = tropic_prodtest_init_and_get_handle(cli);
+ if (h == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "Initial `tropic_init()` failed");
+ return;
+ }
+
if (!tropic_ensure_session(cli, NULL)) {
return;
}
- lt_handle_t* h = tropic_get_handle();
// Slot 31 is usable by both privileged and unprivileged sessions.
lt_ecc_slot_t ecc_slot = TR01_ECC_SLOT_31;
@@ -1925,6 +2015,12 @@ static void prodtest_tropic_test_counter(cli_t* cli) {
"Iterations per counter: %u. Counters: %u. Explicit counter: %d.",
(unsigned)iterations, (unsigned)slot_count, (int)explicit_slot);
+ lt_handle_t* h = tropic_prodtest_init_and_get_handle(cli);
+ if (h == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "Initial `tropic_init()` failed");
+ return;
+ }
+
lt_pkey_index_t pairing_key_index = -1;
if (!tropic_ensure_session(cli, &pairing_key_index)) {
return;
@@ -1944,7 +2040,6 @@ static void prodtest_tropic_test_counter(cli_t* cli) {
return;
}
- lt_handle_t* h = tropic_get_handle();
for (int s = 0; s < slot_count; s++) {
lt_mcounter_index_t idx = slots[s];
@@ -2023,6 +2118,12 @@ static void prodtest_tropic_test_rmem(cli_t* cli) {
cli_trace(cli, "Iterations per slot: %u. Slot count: %u. Explicit slot: %d.",
(unsigned)iterations, (unsigned)slot_count, (int)explicit_slot);
+ lt_handle_t* h = tropic_prodtest_init_and_get_handle(cli);
+ if (h == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "Initial `tropic_init()` failed");
+ return;
+ }
+
lt_pkey_index_t pairing_key_index = -1;
if (!tropic_ensure_session(cli, &pairing_key_index)) {
return;
@@ -2043,7 +2144,6 @@ static void prodtest_tropic_test_rmem(cli_t* cli) {
return;
}
- lt_handle_t* h = tropic_get_handle();
for (int s = 0; s < slot_count; s++) {
uint16_t slot = slots[s];
for (int i = 0; i < iterations; i++) {
@@ -2112,11 +2212,16 @@ static void prodtest_tropic_test_rng(cli_t* cli) {
}
cli_trace(cli, "RNG iterations: %u.", (unsigned)iterations);
+ lt_handle_t* h = tropic_prodtest_init_and_get_handle(cli);
+ if (h == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "Initial `tropic_init()` failed");
+ return;
+ }
+
if (!tropic_ensure_session(cli, NULL)) {
return;
}
- lt_handle_t* h = tropic_get_handle();
uint8_t previous[32] = {0};
for (int i = 0; i < iterations; i++) {
uint8_t value[32] = {0};
@@ -2272,22 +2377,24 @@ static void prodtest_tropic_stress_test(cli_t* cli) {
g_tropic_handshake_state = TROPIC_HANDSHAKE_STATE_0;
+ lt_handle_t* tropic_handle = tropic_prodtest_init_and_get_handle(cli);
+ if (tropic_handle == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "`tropic_init()` failed");
+ return;
+ }
+
// test Tropic gets initialized
for (int i = 0; i < init_iterations; i++) {
tropic_deinit();
- if (!tropic_init()) {
+ lt_ret_t res = tropic_init(cli);
+ if (res != LT_OK) {
cli_error(cli, PRODTEST_ERR_TROPIC_STRESS_INIT,
- "Call #%d of `tropic_init()` failed", i + 1);
- return;
- }
- if (!tropic_wait_for_ready(cli)) {
- cli_error(cli, PRODTEST_ERR_TROPIC_STRESS_READY,
- "Call #%d of `tropic_wait_for_ready()` failed", i + 1);
+ "Call #%d of `tropic_init()` failed with error '%s'", i + 1,
+ lt_ret_verbose(res));
return;
}
}
- lt_ret_t res = LT_FAIL;
lt_pkey_index_t pairing_key_index = -1;
if (!tropic_ensure_session(cli, &pairing_key_index)) {
@@ -2298,7 +2405,7 @@ static void prodtest_tropic_stress_test(cli_t* cli) {
// Test `lt_session_start()`
for (int i = 0; i < start_session_iterations; i++) {
- res = tropic_session_invalidate();
+ lt_ret_t res = tropic_session_invalidate();
if (res != LT_OK) {
cli_error(
cli, PRODTEST_ERR_TROPIC_STRESS_SESSION_INVALIDATE,
@@ -2324,7 +2431,8 @@ static void prodtest_tropic_stress_test(cli_t* cli) {
for (int i = 0; i < mac_and_destroy_per_slot_iterations; i++) {
uint8_t buffer[TROPIC_MAC_AND_DESTROY_SIZE] = {0};
rng_fill_buffer(buffer, sizeof(buffer));
- res = lt_mac_and_destroy(tropic_get_handle(), slot_index, buffer, buffer);
+ lt_ret_t res =
+ lt_mac_and_destroy(tropic_handle, slot_index, buffer, buffer);
if (res != LT_OK) {
cli_error(cli, PRODTEST_ERR_TROPIC_STRESS_SIGN_FAILED,
"Call #%d of `lt_mac_and_destroy()` for slot %d failed "
@@ -2339,7 +2447,8 @@ static void prodtest_tropic_stress_test(cli_t* cli) {
uint8_t message[32] = {0};
ed25519_signature signature = {0};
lt_ecc_slot_t ecc_slot = TR01_ECC_SLOT_31;
- res = lt_ecc_key_generate(tropic_get_handle(), ecc_slot, TR01_CURVE_ED25519);
+ lt_ret_t res =
+ lt_ecc_key_generate(tropic_handle, ecc_slot, TR01_CURVE_ED25519);
if (res != LT_OK) {
cli_error(cli, PRODTEST_ERR_TROPIC_STRESS_KEY_GENERATE,
"`lt_ecc_key_generate()` failed with error '%s'",
@@ -2348,17 +2457,17 @@ static void prodtest_tropic_stress_test(cli_t* cli) {
}
for (int i = 0; i < signing_iterations; i++) {
rng_fill_buffer(message, sizeof(message));
- res = lt_ecc_eddsa_sign(tropic_get_handle(), ecc_slot, message,
- sizeof(message), signature);
+ res = lt_ecc_eddsa_sign(tropic_handle, ecc_slot, message, sizeof(message),
+ signature);
if (res != LT_OK) {
cli_error(cli, PRODTEST_ERR_TROPIC_STRESS_EDDSA_SIGN,
"Call #%d of `lt_ecc_eddsa_sign()` failed with error '%s'",
i + 1, lt_ret_verbose(res));
- lt_ecc_key_erase(tropic_get_handle(), ecc_slot);
+ lt_ecc_key_erase(tropic_handle, ecc_slot);
return;
}
}
- res = lt_ecc_key_erase(tropic_get_handle(), ecc_slot);
+ res = lt_ecc_key_erase(tropic_handle, ecc_slot);
if (res != LT_OK) {
cli_error(cli, PRODTEST_ERR_TROPIC_STRESS_KEY_ERASE,
"`lt_ecc_key_erase()` failed with error '%s'",
@@ -2369,8 +2478,8 @@ static void prodtest_tropic_stress_test(cli_t* cli) {
// Test lt_random_value_get()
for (int i = 0; i < rng_iterations; i++) {
uint8_t random_value[32] = {0};
- res = lt_random_value_get(tropic_get_handle(), random_value,
- sizeof(random_value));
+ lt_ret_t res =
+ lt_random_value_get(tropic_handle, random_value, sizeof(random_value));
if (res != LT_OK) {
cli_error(cli, PRODTEST_ERR_TROPIC_STRESS_RANDOM_GET,
"Call #%d of `lt_random_value_get()` failed with error '%s'",
@@ -2393,11 +2502,16 @@ static void prodtest_tropic_benchmark(cli_t* cli) {
lt_ret_t res = LT_FAIL;
lt_pkey_index_t pairing_key_index = -1;
+ lt_handle_t* h = tropic_prodtest_init_and_get_handle(cli);
+ if (h == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "Initial `tropic_init()` failed");
+ return;
+ }
+
if (!tropic_ensure_session(cli, &pairing_key_index)) {
return;
}
- lt_handle_t* h = tropic_get_handle();
uint32_t start_ms = 0;
const int iterations = 25;
@@ -2644,14 +2758,18 @@ static void prodtest_tropic_erase_all_slots(cli_t* cli) {
return;
}
+ lt_handle_t* tropic_handle = tropic_prodtest_init_and_get_handle(cli);
+ if (tropic_handle == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "`tropic_init()` failed");
+ return;
+ }
+
if (!privileged_session_start(cli)) {
cli_error(cli, PRODTEST_ERR_TROPIC_ERASE_SLOTS_SESSION,
"`privileged_session_start()` failed.");
return;
}
- lt_handle_t* tropic_handle = tropic_get_handle();
-
lt_ret_t ret = tropic_erase_all_slots_internal(cli, tropic_handle);
if (ret == LT_OK) {
cli_ok(cli, "");
@@ -2693,13 +2811,17 @@ static void prodtest_tropic_set_sensors(cli_t* cli) {
((uint32_t)input[2] << 8) | ((uint32_t)input[3]);
}
+ lt_handle_t* tropic_handle = tropic_prodtest_init_and_get_handle(cli);
+ if (tropic_handle == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "`tropic_init()` failed");
+ return;
+ }
+
lt_pkey_index_t pairing_key_index = 0;
if (!tropic_ensure_session(cli, &pairing_key_index)) {
return;
}
- lt_handle_t* tropic_handle = tropic_get_handle();
-
// No need to wipe under a factory session. Tropic is unprovisioned.
if (pairing_key_index != TROPIC_FACTORY_PAIRING_KEY_SLOT) {
lt_ret_t ret = tropic_erase_all_slots_internal(cli, tropic_handle);
@@ -2755,9 +2877,11 @@ static void prodtest_tropic_set_sensors(cli_t* cli) {
return;
}
tropic_deinit();
- if (!tropic_init() || !tropic_wait_for_ready(cli)) {
+ ret = tropic_init(cli);
+ if (ret != LT_OK) {
cli_error(cli, PRODTEST_ERR_TROPIC_SENSORS_REBOOT,
- "Re-initialization after reboot failed.");
+ "Re-initialization after reboot failed with error '%s'",
+ lt_ret_verbose(ret));
return;
}
@@ -2770,7 +2894,11 @@ static void prodtest_tropic_read_sensors(cli_t* cli) {
return;
}
- lt_handle_t* tropic_handle = tropic_get_handle();
+ lt_handle_t* tropic_handle = tropic_prodtest_init_and_get_handle(cli);
+ if (tropic_handle == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "`tropic_init()` failed");
+ return;
+ }
if (!privileged_session_start(cli)) {
cli_error(cli, PRODTEST_ERR_TROPIC_READ_SENSORS_SESSION,
@@ -2797,7 +2925,11 @@ static void prodtest_tropic_read_configs(cli_t* cli) {
return;
}
- lt_handle_t* tropic_handle = tropic_get_handle();
+ lt_handle_t* tropic_handle = tropic_prodtest_init_and_get_handle(cli);
+ if (tropic_handle == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "`tropic_init()` failed");
+ return;
+ }
if (!privileged_session_start(cli)) {
cli_error(cli, PRODTEST_ERR_TROPIC_READ_CONFIGS_SESSION,
@@ -2844,8 +2976,8 @@ static void prodtest_tropic_read_configs(cli_t* cli) {
uint8_t read_value_bytes[sizeof(uint32_t)] = {0};
uint16_t read_length = 0;
ret = lt_r_mem_data_read(
- tropic_get_handle(), TROPIC_CONFIG_DISTRIBUTION_VERSION_SLOT,
- read_value_bytes, sizeof(read_value_bytes), &read_length);
+ tropic_handle, TROPIC_CONFIG_DISTRIBUTION_VERSION_SLOT, read_value_bytes,
+ sizeof(read_value_bytes), &read_length);
if (ret == LT_L3_R_MEM_DATA_READ_SLOT_EMPTY) {
cli_trace(cli, "Configuration distribution version: empty");
} else if (ret != LT_OK) {
@@ -2863,7 +2995,7 @@ static void prodtest_tropic_read_configs(cli_t* cli) {
}
read_length = 0;
ret = lt_r_mem_data_read(
- tropic_get_handle(), TROPIC_CONFIG_BACKUP_DISTRIBUTION_VERSION_SLOT,
+ tropic_handle, TROPIC_CONFIG_BACKUP_DISTRIBUTION_VERSION_SLOT,
read_value_bytes, sizeof(read_value_bytes), &read_length);
if (ret == LT_L3_R_MEM_DATA_READ_SLOT_EMPTY) {
cli_trace(cli, "Configuration backup distribution version: empty");
@@ -2890,12 +3022,19 @@ static void prodtest_tropic_tests_cleanup(cli_t* cli) {
cli_error_arg_count(cli);
return;
}
+
+ lt_handle_t* h = tropic_prodtest_init_and_get_handle(cli);
+ if (h == NULL) {
+ cli_error(cli, PRODTEST_ERR_TROPIC_INIT, "Initial `tropic_init()` failed");
+ return;
+ }
+
lt_pkey_index_t pairing_key_index = -1;
if (!tropic_ensure_session(cli, &pairing_key_index)) {
return;
}
bool unprivileged = pairing_key_index == TROPIC_UNPRIVILEGED_PAIRING_KEY_SLOT;
- if (!tropic_tests_cleanup(cli, tropic_get_handle(), unprivileged)) {
+ if (!tropic_tests_cleanup(cli, h, unprivileged)) {
// Error already reported by tropic_tests_cleanup().
return;
}
diff --git a/core/embed/projects/prodtest/cmd/prodtest_tropic.h b/core/embed/projects/prodtest/cmd/prodtest_tropic.h
index d803fe22..ad466bc1 100644
--- a/core/embed/projects/prodtest/cmd/prodtest_tropic.h
+++ b/core/embed/projects/prodtest/cmd/prodtest_tropic.h
@@ -29,4 +29,5 @@ typedef enum {
TROPIC_LOCKED_ERROR,
} tropic_locked_status;
-tropic_locked_status get_tropic_locked_status(cli_t* cli);
+tropic_locked_status get_tropic_locked_status(cli_t* cli,
+ lt_handle_t* tropic_handle);
diff --git a/core/embed/projects/prodtest/error_codes.json b/core/embed/projects/prodtest/error_codes.json
index a503daca..d65639f6 100644
--- a/core/embed/projects/prodtest/error_codes.json
+++ b/core/embed/projects/prodtest/error_codes.json
@@ -1731,6 +1731,11 @@
"name": "PRODTEST_ERR_TROPIC_TESTS_CLEANUP_COUNTER",
"module": "tropic"
},
+ {
+ "code": 20159,
+ "name": "PRODTEST_ERR_TROPIC_INIT",
+ "module": "tropic"
+ },
{
"code": 21010,
"name": "PRODTEST_ERR_UNIT_TEST_FAILED",
diff --git a/core/embed/projects/prodtest/main.c b/core/embed/projects/prodtest/main.c
index 3f2787bb..ed520323 100644
--- a/core/embed/projects/prodtest/main.c
+++ b/core/embed/projects/prodtest/main.c
@@ -197,10 +197,6 @@ static void drivers_init(void) {
#ifdef USE_BLE
ble_init();
#endif
-#ifdef USE_TROPIC
- tropic_init();
- tropic_wait_for_ready(NULL);
-#endif
#ifdef USE_HW_REVISION
hw_revision_init();
#endif
diff --git a/core/embed/projects/prodtest/prodtest_error_codes.h b/core/embed/projects/prodtest/prodtest_error_codes.h
index bdf23eff..7028c82c 100644
--- a/core/embed/projects/prodtest/prodtest_error_codes.h
+++ b/core/embed/projects/prodtest/prodtest_error_codes.h
@@ -417,6 +417,7 @@ typedef enum {
PRODTEST_ERR_TROPIC_TESTS_CLEANUP_RMEM = 20156,
PRODTEST_ERR_TROPIC_TESTS_CLEANUP_ECC = 20157,
PRODTEST_ERR_TROPIC_TESTS_CLEANUP_COUNTER = 20158,
+ PRODTEST_ERR_TROPIC_INIT = 20159,
// === unit-test (21000–21999) ===
PRODTEST_ERR_UNIT_TEST_FAILED = 21010,
diff --git a/core/embed/projects/secmon/main.c b/core/embed/projects/secmon/main.c
index 1d57d13b..52a9c94e 100644
--- a/core/embed/projects/secmon/main.c
+++ b/core/embed/projects/secmon/main.c
@@ -125,7 +125,7 @@ static void drivers_init(void) {
#endif
#ifdef USE_TROPIC
- tropic_init();
+ tropic_init(NULL);
#if defined(USE_SECRET) && defined(LOCKABLE_BOOTLOADER)
if (secfalse != secret_bootloader_locked()) {
ensure(tropic_ensure_configuration(), "Tropic configuration check failed");
diff --git a/core/embed/projects/unix/main_main.c b/core/embed/projects/unix/main_main.c
index 038951f3..208087f5 100644
--- a/core/embed/projects/unix/main_main.c
+++ b/core/embed/projects/unix/main_main.c
@@ -100,7 +100,7 @@ static void drivers_init(void) {
#endif
#ifdef USE_TROPIC
- tropic_init();
+ tropic_init(NULL);
ensure(tropic_ensure_configuration(), "Tropic configuration check failed");
#endif
diff --git a/core/embed/projects/unix/rust_c_setup.c b/core/embed/projects/unix/rust_c_setup.c
index 24950e22..e4bed6c0 100644
--- a/core/embed/projects/unix/rust_c_setup.c
+++ b/core/embed/projects/unix/rust_c_setup.c
@@ -59,7 +59,7 @@ void rust_tests_c_setup(void) {
#endif
#ifdef USE_TROPIC
- tropic_init();
+ tropic_init(NULL);
#endif
usb_configure(NULL);
diff --git a/core/embed/sec/suspend/stm32u5/suspend_io.c b/core/embed/sec/suspend/stm32u5/suspend_io.c
index 75969810..56628df2 100644
--- a/core/embed/sec/suspend/stm32u5/suspend_io.c
+++ b/core/embed/sec/suspend/stm32u5/suspend_io.c
@@ -72,7 +72,7 @@ void resume_secure_drivers() {
secure_aes_init();
#endif
#ifdef USE_TROPIC
- tropic_init();
+ tropic_init(NULL);
#endif
}
diff --git a/core/embed/sec/tropic/inc/sec/tropic.h b/core/embed/sec/tropic/inc/sec/tropic.h
index ec3ce25c..fa5cfbfa 100644
--- a/core/embed/sec/tropic/inc/sec/tropic.h
+++ b/core/embed/sec/tropic/inc/sec/tropic.h
@@ -67,7 +67,9 @@
#ifdef KERNEL_MODE
-bool tropic_init(void);
+#include "libtropic.h"
+
+lt_ret_t tropic_init(cli_t* cli);
void tropic01_reset(void);
@@ -81,20 +83,19 @@ typedef struct {
} tropic_expected_config_t;
#ifdef TREZOR_PRODTEST
-#include "libtropic.h"
-lt_handle_t* tropic_get_handle(void);
+lt_handle_t* tropic_prodtest_init_and_get_handle(cli_t* cli);
lt_ret_t tropic_custom_session_start(cli_t* cli,
lt_pkey_index_t pairing_key_index);
lt_ret_t tropic_session_invalidate(void);
-bool tropic_wait_for_ready(cli_t* cli);
-
-bool tropic_get_pubkey(cli_t* cli, curve25519_key pubkey);
+bool tropic_get_pubkey(cli_t* cli, lt_handle_t* tropic_handle,
+ curve25519_key pubkey);
-bool tropic_get_cert_chain_ptr(cli_t* cli, uint8_t const** cert_chain,
- size_t* length);
+bool tropic_get_cert_chain_ptr(cli_t* cli, lt_handle_t* tropic_handle,
+ uint8_t const** cert_chain,
+ size_t* cert_chain_length);
lt_ret_t lt_ecc_key_erase_retry(lt_handle_t* tropic_handle,
const lt_ecc_slot_t ecc_slot);
diff --git a/core/embed/sec/tropic/tropic.c b/core/embed/sec/tropic/tropic.c
index 4a4f9c44..89985efe 100644
--- a/core/embed/sec/tropic/tropic.c
+++ b/core/embed/sec/tropic/tropic.c
@@ -42,9 +42,6 @@
#ifdef SECURE_MODE
-// Maximum time to wait for Tropic to boot. Chosen arbitrarily.
-#define TROPIC_BOOT_TIMEOUT_MS 1000
-
// KEK masks used in PIN verification
#define TROPIC_KEK_MASKS_PRIVILEGED_SLOT 128
#define TROPIC_KEK_MASKS_UNPRIVILEGED_SLOT 256
@@ -157,8 +154,7 @@ static bool is_retryable(lt_ret_t ret) {
} \
tropic01_reset(); \
tropic_deinit(); \
- tropic_init(); \
- tropic_wait_for_ready(NULL); \
+ tropic_init(NULL); \
if (TROPIC_RETRY_COMMAND_session_started) { \
if (tropic_custom_session_start( \
NULL, TROPIC_RETRY_COMMAND_pairing_key_index) != LT_OK) { \
@@ -174,7 +170,6 @@ static bool is_retryable(lt_ret_t ret) {
typedef struct {
bool initialized;
bool session_started;
- bool chip_ready;
lt_pkey_index_t pairing_key_index; // This field is valid only if
// session_started is true.
lt_handle_t handle;
@@ -193,7 +188,7 @@ static size_t tropic_cert_chain_length = 0;
static curve25519_key tropic_public_cached = {0};
// If `TREZOR_PRODTEST` is not defined, the `cli` argument is ignored.
-static bool cache_tropic_cert_chain(cli_t *cli) {
+static bool cache_tropic_cert_chain(cli_t *cli, lt_handle_t *tropic_handle) {
if (tropic_cert_chain_length > 0) {
return true;
}
@@ -205,9 +200,7 @@ static bool cache_tropic_cert_chain(cli_t *cli) {
cert_store.buf_len[i] = TR01_L2_GET_INFO_REQ_CERT_SIZE_SINGLE;
}
- lt_ret_t ret = LT_FAIL;
-
- ret = lt_get_info_cert_store(&g_tropic_driver.handle, &cert_store);
+ lt_ret_t ret = lt_get_info_cert_store(tropic_handle, &cert_store);
if (ret != LT_OK) {
#if TREZOR_PRODTEST
if (cli) {
@@ -243,17 +236,19 @@ static bool cache_tropic_cert_chain(cli_t *cli) {
return true;
}
-bool tropic_get_pubkey(cli_t *cli, curve25519_key pubkey) {
- if (!cache_tropic_cert_chain(cli)) {
+bool tropic_get_pubkey(cli_t *cli, lt_handle_t *tropic_handle,
+ curve25519_key pubkey) {
+ if (!cache_tropic_cert_chain(cli, tropic_handle)) {
return false;
}
memcpy(pubkey, tropic_public_cached, sizeof(curve25519_key));
return true;
}
-bool tropic_get_cert_chain_ptr(cli_t *cli, uint8_t const **cert_chain,
+bool tropic_get_cert_chain_ptr(cli_t *cli, lt_handle_t *tropic_handle,
+ uint8_t const **cert_chain,
size_t *cert_chain_length) {
- if (!cache_tropic_cert_chain(cli)) {
+ if (!cache_tropic_cert_chain(cli, tropic_handle)) {
return false;
}
*cert_chain = tropic_cert_chain;
@@ -263,41 +258,6 @@ bool tropic_get_cert_chain_ptr(cli_t *cli, uint8_t const **cert_chain,
#endif // !PRODUCTION || defined(TREZOR_PRODTEST)
-// If `TREZOR_PRODTEST` is not defined, the `cli` argument is ignored.
-bool tropic_wait_for_ready(cli_t *cli) {
- tropic_driver_t *drv = &g_tropic_driver;
-
- if (!drv->initialized) {
-#if TREZOR_PRODTEST
- if (cli) {
- cli_trace(cli, "Tropic driver is not initialized");
- }
-#endif
- return false;
- }
-
- if (drv->chip_ready) {
- return true;
- }
-
- // Wait for Tropic to boot before issuing any session commands.
- uint32_t boot_start_ms = hal_ticks_ms();
- while (hal_ticks_ms() - boot_start_ms < TROPIC_BOOT_TIMEOUT_MS) {
- uint8_t ver[TR01_L2_GET_INFO_RISCV_FW_SIZE] = {0};
- if (lt_get_info_riscv_fw_ver(&drv->handle, ver) != LT_L1_CHIP_BUSY) {
- drv->chip_ready = true;
- return true;
- }
- }
-
-#if TREZOR_PRODTEST
- if (cli) {
- cli_trace(cli, "Tropic is busy");
- }
-#endif
- return false;
-}
-
lt_ret_t tropic_session_invalidate(void) {
lt_ret_t ret = lt_session_abort(&g_tropic_driver.handle);
if (ret != LT_OK) {
@@ -358,7 +318,7 @@ lt_ret_t tropic_custom_session_start(cli_t *cli,
if (secret_key_tropic_public(tropic_public) != sectrue) {
#if !PRODUCTION || defined(TREZOR_PRODTEST)
if (pairing_key_index != TROPIC_FACTORY_PAIRING_KEY_SLOT ||
- !tropic_get_pubkey(cli, tropic_public))
+ !tropic_get_pubkey(cli, &drv->handle, tropic_public))
#endif
{
#ifdef TREZOR_PRODTEST
@@ -372,8 +332,6 @@ lt_ret_t tropic_custom_session_start(cli_t *cli,
}
}
- tropic_wait_for_ready(cli);
-
ret = TROPIC_RETRY_COMMAND(lt_session_start(&drv->handle, tropic_public,
pairing_key_index, trezor_private,
trezor_public));
@@ -809,10 +767,7 @@ static secbool tropic_restart_chip(void) {
tropic01_reset();
#endif
tropic_deinit();
- if (!tropic_init()) {
- return secfalse;
- }
- if (!tropic_wait_for_ready(NULL)) {
+ if (tropic_init(NULL) != LT_OK) {
return secfalse;
}
return sectrue;
@@ -1011,11 +966,12 @@ static uint16_t get_tropic_model_port(void) {
}
#endif
-bool tropic_init(void) {
+// If `TREZOR_PRODTEST` is not defined, the `cli` argument is ignored.
+lt_ret_t tropic_init(cli_t *cli) {
tropic_driver_t *drv = &g_tropic_driver;
if (drv->initialized) {
- return true;
+ return LT_OK;
}
#ifdef TREZOR_EMULATOR
@@ -1027,21 +983,30 @@ bool tropic_init(void) {
// Initialize crypto context
drv->handle.l3.crypto_ctx = &drv->crypto_ctx;
- if (lt_init(&drv->handle) != LT_OK) {
- return false;
+ lt_ret_t ret = lt_init(&drv->handle);
+ if (ret != LT_OK) {
+#ifdef TREZOR_PRODTEST
+ if (cli) {
+ cli_trace(cli, "`lt_init()` failed with error '%s'", lt_ret_verbose(ret));
+ }
+#endif
+ return ret;
}
drv->initialized = true;
- return true;
+ return LT_OK;
}
void tropic_deinit(void) {
tropic_driver_t *drv = &g_tropic_driver;
- lt_deinit(&drv->handle);
+ if (drv->handle.l3.crypto_ctx != NULL) {
+ lt_deinit(&drv->handle);
+ }
memset(drv, 0, sizeof(*drv));
}
-lt_handle_t *tropic_get_handle(void) {
+#ifdef TREZOR_PRODTEST
+static lt_handle_t *tropic_get_handle(void) {
tropic_driver_t *drv = &g_tropic_driver;
if (!drv->initialized) {
@@ -1051,6 +1016,15 @@ lt_handle_t *tropic_get_handle(void) {
return &drv->handle;
}
+lt_handle_t *tropic_prodtest_init_and_get_handle(cli_t *cli) {
+ if (tropic_init(cli) != LT_OK) {
+ return NULL;
+ }
+
+ return tropic_get_handle();
+}
+#endif // TREZOR_PRODTEST
+
bool tropic_ping(const uint8_t *msg_out, uint8_t *msg_in, uint16_t msg_len) {
tropic_driver_t *drv = &g_tropic_driver;
@@ -1244,11 +1218,10 @@ static void get_change_pin_counter_time(uint32_t *time_ms,
static bool update_change_pin_counter() {
tropic_driver_t *drv = &g_tropic_driver;
- lt_ret_t ret = LT_FAIL;
// The cache is invalidated because the counter may be updated more than once
g_is_change_pin_counter_cached = false;
- ret = TROPIC_RETRY_COMMAND(
+ lt_ret_t ret = TROPIC_RETRY_COMMAND(
lt_mcounter_update(&drv->handle, TROPIC_CHANGE_COUNTER_SLOT));
if (ret == LT_L3_COUNTER_INVALID || ret == LT_L3_UPDATE_ERR) {
// The counter has not been initialized yet or is depleted.
Why this scored 22/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.