feat(prodtest): enable disabling Tropic sensors in prodtest
What changed, and why it matters
This commit adds new factory-test (prodtest) commands for the Tropic secure chip used in some Trezor devices. It lets authorized production-line tools read and change a reversible chip setting called 'sensors configuration,' and also erase cryptographic slots or dump the whole chip configuration. The change is intended for manufacturing/testing, not normal user wallets, and requires a privileged pairing session. The code itself is not a hidden backdoor, but it does expand what a party with prodtest access can do to the secure chip.
Treat this as a manufacturing-feature change rather than a vulnerability. Review whether prodtest access is adequately restricted on production devices (e.g., disabled in shipped firmware, protected physical test points, signed/authorized tooling). Verify that tropic-set-sensors' mandatory erase-all behavior is intentional and documented for production workflows. Audit the retry macro change to ensure it does not mask persistent hardware faults or enable unintended state transitions.
Security signals we found
New privileged CLI commands that can erase all ECC/data/MAC&Destroy slots and rewrite reversible Tropic configuration
tropic-set-sensors erases all cryptographic slots before changing the sensors config, which is a destructive operation
privileged_session_start falls back to factory pairing key if privileged pairing key fails, broadening access within prodtest context
TROPIC_RETRY_COMMAND now resets/reinitializes the chip on any retryable error and increases max retries from 5 to 10
Whole reversible and irreversible configuration can be dumped via tropic-read-configs
No changelog entry ([no changelog])
Evidence from the diff
The patch introduces four prodtest CLI commands: tropic-set-sensors, tropic-read-sensors, tropic-read-configs, and tropic-erase-all-slots. They wrap Tropic library calls (lt_r_config_read, lt_read_whole_R_config, lt_erase_and_write_R_config, lt_ecc_key_erase, lt_r_mem_data_erase, lt_mac_and_destroy) and are gated by privileged_session_start(), which tries the privileged or factory pairing key slot. lt_erase_and_write_R_config_retry erases the reversible config region and rewrites each object with retry logic. The TROPIC_RETRY_COMMAND macro was also refactored to reset/reinit/restart the session on every retryable failure (not only LT_L1_CHIP_ALARM_MODE), and TROPIC_MAX_RETRIES was raised from 5 to 10.
Changed components
core/embed/projects/prodtest/cmd/prodtest_tropic.ccore/embed/sec/tropic/tropic.ccore/embed/sec/tropic/inc/sec/tropic.hTropic secure chip integration in Trezor prodtest firmwareInspect captured patch +462 / −30
diff --git a/core/embed/projects/prodtest/README.md b/core/embed/projects/prodtest/README.md
index f80bfaab..a68df3a2 100644
--- a/core/embed/projects/prodtest/README.md
+++ b/core/embed/projects/prodtest/README.md
@@ -1307,6 +1307,107 @@ tropic-lock
OK <hexadecimal string>
```
+### tropic-set-sensors
+
+Erases all ECC key slots, data slots and MAC & Destroy slots and then sets the reversible configuration of Tropic sensors to the input value.
+
+Example:
+```
+tropic-set-sensors fffffff5
+# Erasing all ECC key slots, data slots and MAC&Destroy slots
+# All cryptographic data erased successfully
+OK
+```
+
+### tropic-read-sensors
+
+Read the current sensor reversible configuration from Tropic.
+
+Example:
+```
+tropic-read-sensors
+OK 0xFFFFFFF5
+```
+
+### tropic-read-configs
+
+Read whole irreversible and reversible configurations.
+
+Example:
+```
+tropic-read-configs
+# === Reversible Configuration ===
+# R_config.obj[0]: 0x00000009 (addr: 0x00)
+# R_config.obj[1]: 0x00000000 (addr: 0x08)
+# R_config.obj[2]: 0x00000000 (addr: 0x10)
+# R_config.obj[3]: 0x00000000 (addr: 0x18)
+# R_config.obj[4]: 0x00000001 (addr: 0x20)
+# R_config.obj[5]: 0x04040404 (addr: 0x28)
+# R_config.obj[6]: 0x06060606 (addr: 0x30)
+# R_config.obj[7]: 0x06060606 (addr: 0x38)
+# R_config.obj[8]: 0x00000004 (addr: 0x40)
+# R_config.obj[9]: 0x00000606 (addr: 0x48)
+# R_config.obj[10]: 0x00000606 (addr: 0x50)
+# R_config.obj[11]: 0x00000606 (addr: 0x58)
+# R_config.obj[12]: 0x00000006 (addr: 0x60)
+# R_config.obj[13]: 0x06060404 (addr: 0x68)
+# R_config.obj[14]: 0x06060406 (addr: 0x70)
+# R_config.obj[15]: 0x06060406 (addr: 0x78)
+# R_config.obj[16]: 0x00000006 (addr: 0x80)
+# R_config.obj[17]: 0x06060604 (addr: 0x88)
+# R_config.obj[18]: 0x06060604 (addr: 0x90)
+# R_config.obj[19]: 0x06060606 (addr: 0x98)
+# R_config.obj[20]: 0x06060606 (addr: 0xA0)
+# R_config.obj[21]: 0x06060604 (addr: 0xA8)
+# R_config.obj[22]: 0x06060604 (addr: 0xB0)
+# R_config.obj[23]: 0x06060604 (addr: 0xB8)
+# R_config.obj[24]: 0x06060606 (addr: 0xC0)
+# R_config.obj[25]: 0x06060606 (addr: 0xC8)
+# R_config.obj[26]: 0x06060404 (addr: 0xD0)
+#
+# === Irreversible Configuration ===
+# I_config.obj[0]: 0xFFFFFFF9 (addr: 0x00)
+# I_config.obj[1]: 0xFFFFFFFF (addr: 0x08)
+# I_config.obj[2]: 0xFFFFFFFE (addr: 0x10)
+# I_config.obj[3]: 0xFFFFFFFF (addr: 0x18)
+# I_config.obj[4]: 0xFFFFFFFF (addr: 0x20)
+# I_config.obj[5]: 0xFCFCFCFC (addr: 0x28)
+# I_config.obj[6]: 0xFEFEFEFE (addr: 0x30)
+# I_config.obj[7]: 0xFEFEFEFE (addr: 0x38)
+# I_config.obj[8]: 0xFFFFFFFC (addr: 0x40)
+# I_config.obj[9]: 0xFFFFFEFE (addr: 0x48)
+# I_config.obj[10]: 0xFFFFFEFE (addr: 0x50)
+# I_config.obj[11]: 0xFFFFFEFE (addr: 0x58)
+# I_config.obj[12]: 0xFFFFFFFE (addr: 0x60)
+# I_config.obj[13]: 0xFEFEFCFC (addr: 0x68)
+# I_config.obj[14]: 0xFEFEFCFE (addr: 0x70)
+# I_config.obj[15]: 0xFEFEFCFE (addr: 0x78)
+# I_config.obj[16]: 0xFFFFFFFE (addr: 0x80)
+# I_config.obj[17]: 0xFEFEFEFC (addr: 0x88)
+# I_config.obj[18]: 0xFEFEFEFC (addr: 0x90)
+# I_config.obj[19]: 0xFEFEFEFE (addr: 0x98)
+# I_config.obj[20]: 0xFEFEFEFE (addr: 0xA0)
+# I_config.obj[21]: 0xFEFEFEFC (addr: 0xA8)
+# I_config.obj[22]: 0xFEFEFEFC (addr: 0xB0)
+# I_config.obj[23]: 0xFEFEFEFC (addr: 0xB8)
+# I_config.obj[24]: 0xFEFEFEFE (addr: 0xC0)
+# I_config.obj[25]: 0xFEFEFEFE (addr: 0xC8)
+# I_config.obj[26]: 0xFEFEFCFC (addr: 0xD0)
+OK
+```
+
+### tropic-erase-all-slots
+
+Erases all ECC key slots, data slots and MAC & Destroy slots.
+
+Example:
+```
+tropic-erase-all-slots
+# Erasing all ECC key slots, data slots and MAC&Destroy slots
+# All cryptographic data erased successfully
+OK
+```
+
### secure-channel-handshake-1
Returns the first handshake message for establishing a secure channel between the device and HSM.
diff --git a/core/embed/projects/prodtest/cmd/prodtest_tropic.c b/core/embed/projects/prodtest/cmd/prodtest_tropic.c
index 3948cd41..36570ad4 100644
--- a/core/embed/projects/prodtest/cmd/prodtest_tropic.c
+++ b/core/embed/projects/prodtest/cmd/prodtest_tropic.c
@@ -1775,6 +1775,238 @@ static void prodtest_tropic_stress_test(cli_t* cli) {
cli_ok(cli, "");
}
+static bool privileged_session_start(cli_t* cli) {
+ g_tropic_handshake_state = TROPIC_HANDSHAKE_STATE_0;
+
+ if (tropic_custom_session_start(cli, TROPIC_PRIVILEGED_PAIRING_KEY_SLOT) ==
+ LT_OK) {
+ return true;
+ }
+
+ if (tropic_custom_session_start(cli, TROPIC_FACTORY_PAIRING_KEY_SLOT) ==
+ LT_OK) {
+ return true;
+ }
+
+ return false;
+}
+
+static lt_ret_t tropic_erase_all_slots_internal(cli_t* cli,
+ lt_handle_t* tropic_handle) {
+ cli_trace(cli, "Erasing all ECC key slots, data slots and MAC&Destroy slots");
+
+ lt_ret_t ret = LT_OK;
+
+ // Erase all 32 ECC key slots
+ for (lt_ecc_slot_t slot = TR01_ECC_SLOT_0; slot <= TR01_ECC_SLOT_31; slot++) {
+ ret = lt_ecc_key_erase_retry(tropic_handle, slot);
+ if (ret != LT_OK) {
+ cli_trace(cli, "ECC slot %2d: Erase failed (error %s)", slot,
+ lt_ret_verbose(ret));
+ return ret;
+ }
+ }
+
+ // Erase all 512 R_MEM data slots
+ for (uint16_t slot = 0; slot <= TR01_R_MEM_DATA_SLOT_MAX; slot++) {
+ ret = lt_r_mem_data_erase_retry(tropic_handle, slot);
+ if (ret != LT_OK) {
+ cli_trace(cli, "Data slot %3d: Erase failed (error %s)", slot,
+ lt_ret_verbose(ret));
+ return ret;
+ }
+ }
+
+ // Destroy all 128 MAC and destroy slots by triggering self-destruct
+ // Dummy data to trigger MAC computation
+ uint8_t dummy_data[TROPIC_MAC_AND_DESTROY_SIZE] = {0};
+ // Output buffer for MAC result
+ uint8_t mac_output[TROPIC_MAC_AND_DESTROY_SIZE] = {0};
+ for (lt_mac_and_destroy_slot_t slot = 0;
+ slot <= TR01_MAC_AND_DESTROY_SLOT_127; slot++) {
+ ret = lt_mac_and_destroy_retry(tropic_handle, slot, dummy_data, mac_output);
+ if (ret != LT_OK) {
+ cli_trace(cli, "M&D slot %3d: Destroy failed (error %s)", slot,
+ lt_ret_verbose(ret));
+ return ret;
+ }
+ }
+
+ cli_trace(cli, "All cryptographic data erased successfully");
+ return LT_OK;
+}
+
+static void prodtest_tropic_erase_all_slots(cli_t* cli) {
+ if (cli_arg_count(cli) > 0) {
+ cli_error_arg_count(cli);
+ return;
+ }
+
+ if (!privileged_session_start(cli)) {
+ cli_error(cli, CLI_ERROR, "`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, "");
+ } else {
+ cli_error(cli, CLI_ERROR, "Erase operation failed");
+ }
+}
+
+static void prodtest_tropic_set_sensors(cli_t* cli) {
+ if (cli_arg_count(cli) != 1) {
+ cli_error_arg_count(cli);
+ return;
+ }
+
+ uint8_t input[4] = {0};
+ size_t input_length = 0;
+ if (!cli_arg_hex(cli, "hex-data", input, sizeof(input), &input_length)) {
+ if (input_length == sizeof(input)) {
+ cli_error(cli, CLI_ERROR, "Input too long.");
+ } else {
+ cli_error(cli, CLI_ERROR, "Hexadecimal decoding error.");
+ }
+ return;
+ }
+
+ if (input_length != sizeof(input)) {
+ cli_error(cli, CLI_ERROR, "Expected 4 bytes (8 hex digits) for uint32.");
+ return;
+ }
+
+ uint32_t new_sensors_config =
+ ((uint32_t)input[0] << 24) | ((uint32_t)input[1] << 16) |
+ ((uint32_t)input[2] << 8) | ((uint32_t)input[3]);
+
+ if (!privileged_session_start(cli)) {
+ cli_error(cli, CLI_ERROR, "`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_error(cli, CLI_ERROR, "Erase operation failed");
+ return;
+ }
+
+ struct lt_config_t configuration = {0};
+ ret = lt_read_whole_R_config_retry(tropic_handle, &configuration);
+ if (ret != LT_OK) {
+ cli_error(cli, CLI_ERROR, "`lt_read_whole_R_config()` failed with error %s",
+ lt_ret_verbose(ret));
+ return;
+ }
+
+ // Update sensors in the configuration
+ configuration.obj[TR01_CFG_SENSORS_IDX] = new_sensors_config;
+
+ ret = lt_erase_and_write_R_config_retry(tropic_handle, &configuration);
+ if (ret != LT_OK) {
+ cli_error(cli, CLI_ERROR,
+ "`lt_erase_and_write_R_config_retry()` failed with error %s",
+ lt_ret_verbose(ret));
+ return;
+ }
+
+ // Verify the write
+ struct lt_config_t verify_configuration = {0};
+ ret = lt_read_whole_R_config_retry(tropic_handle, &verify_configuration);
+ if (ret != LT_OK) {
+ cli_error(cli, CLI_ERROR,
+ "`lt_r_config_read()` verification failed with error %s",
+ lt_ret_verbose(ret));
+ return;
+ }
+
+ if (memcmp(&configuration, &verify_configuration,
+ sizeof(verify_configuration)) != 0) {
+ cli_error(cli, CLI_ERROR, "Configuration was not written correctly.");
+ return;
+ }
+
+ cli_ok(cli, "");
+}
+
+static void prodtest_tropic_read_sensors(cli_t* cli) {
+ if (cli_arg_count(cli) != 0) {
+ cli_error_arg_count(cli);
+ return;
+ }
+
+ lt_handle_t* tropic_handle = tropic_get_handle();
+
+ if (!privileged_session_start(cli)) {
+ cli_error(cli, CLI_ERROR, "`privileged_session_start()` failed.");
+ return;
+ }
+
+ // Read current configuration
+ uint32_t sensors_config = 0;
+ lt_ret_t ret =
+ lt_r_config_read(tropic_handle, TR01_CFG_SENSORS_ADDR, &sensors_config);
+ if (ret != LT_OK) {
+ cli_error(cli, CLI_ERROR, "`lt_r_config_read()` failed with error %s",
+ lt_ret_verbose(ret));
+ return;
+ }
+
+ cli_ok(cli, "0x%08X", sensors_config);
+}
+
+static void prodtest_tropic_read_configs(cli_t* cli) {
+ if (cli_arg_count(cli) != 0) {
+ cli_error_arg_count(cli);
+ return;
+ }
+
+ lt_handle_t* tropic_handle = tropic_get_handle();
+
+ if (!privileged_session_start(cli)) {
+ cli_error(cli, CLI_ERROR, "`privileged_session_start()` failed.");
+ return;
+ }
+
+ // read reversible configuration
+ struct lt_config_t r_config = {0};
+ lt_ret_t ret = lt_read_whole_R_config_retry(tropic_handle, &r_config);
+ if (ret != LT_OK) {
+ cli_error(cli, CLI_ERROR, "`lt_read_whole_R_config()` failed with error %s",
+ lt_ret_verbose(ret));
+ return;
+ }
+
+ cli_trace(cli, "=== Reversible Configuration ===");
+ for (size_t i = 0; i < LT_CONFIG_OBJ_CNT; i++) {
+ cli_trace(cli, " R_config.obj[%zu]: 0x%08X (addr: 0x%02zX)", i,
+ r_config.obj[i], i * 0x08);
+ }
+
+ // read irreversible configuration
+ struct lt_config_t i_config = {0};
+ ret = lt_read_whole_I_config(tropic_handle, &i_config);
+ if (ret != LT_OK) {
+ cli_error(cli, CLI_ERROR, "`lt_read_whole_I_config()` failed with error %s",
+ lt_ret_verbose(ret));
+ return;
+ }
+
+ cli_trace(cli, "");
+ cli_trace(cli, "=== Irreversible Configuration ===");
+ for (size_t i = 0; i < LT_CONFIG_OBJ_CNT; i++) {
+ cli_trace(cli, " I_config.obj[%zu]: 0x%08X (addr: 0x%02zX)", i,
+ i_config.obj[i], i * 0x08);
+ }
+
+ cli_ok(cli, "");
+}
+
// clang-format off
PRODTEST_CLI_CMD(
@@ -1903,4 +2135,33 @@ PRODTEST_CLI_CMD(
.args = "[<start-session-iterations> <mac-and-destroy-slot-count> <mac-and-destroy-per-slot-iterations> <signing-iterations>]"
);
-#endif
+PRODTEST_CLI_CMD(
+ .name = "tropic-set-sensors",
+ .func = prodtest_tropic_set_sensors,
+ .info = "Set the reversible configuration of the sensors in Tropic",
+ .args = "<hex-data>"
+);
+
+PRODTEST_CLI_CMD(
+ .name = "tropic-read-sensors",
+ .func = prodtest_tropic_read_sensors,
+ .info = "Read the current sensor reversible configuration from Tropic",
+ .args = ""
+);
+
+PRODTEST_CLI_CMD(
+ .name = "tropic-read-configs",
+ .func = prodtest_tropic_read_configs,
+ .info = "Read whole I_config and R_config.",
+ .args = ""
+);
+
+PRODTEST_CLI_CMD(
+ .name = "tropic-erase-all-slots",
+ .func = prodtest_tropic_erase_all_slots,
+ .info = "Erase all ECC keys, data slots and Mac&Destroy slots. Keeps pairing keys intact.",
+ .args = ""
+);
+
+
+#endif // USE_TROPIC
diff --git a/core/embed/sec/tropic/inc/sec/tropic.h b/core/embed/sec/tropic/inc/sec/tropic.h
index 11177036..0937651f 100644
--- a/core/embed/sec/tropic/inc/sec/tropic.h
+++ b/core/embed/sec/tropic/inc/sec/tropic.h
@@ -86,6 +86,23 @@ bool tropic_get_pubkey(cli_t* cli, curve25519_key pubkey);
bool tropic_get_cert_chain_ptr(cli_t* cli, uint8_t const** cert_chain,
size_t* length);
+
+lt_ret_t lt_ecc_key_erase_retry(lt_handle_t* tropic_handle,
+ const lt_ecc_slot_t ecc_slot);
+
+lt_ret_t lt_r_mem_data_erase_retry(lt_handle_t* tropic_handle,
+ const uint16_t udata_slot);
+
+lt_ret_t lt_mac_and_destroy_retry(lt_handle_t* tropic_handle,
+ const lt_mac_and_destroy_slot_t slot,
+ const uint8_t* data_out, uint8_t* data_in);
+
+lt_ret_t lt_read_whole_R_config_retry(lt_handle_t* tropic_handle,
+ struct lt_config_t* config);
+
+lt_ret_t lt_erase_and_write_R_config_retry(lt_handle_t* tropic_handle,
+ const struct lt_config_t* config);
+
#endif
#endif
diff --git a/core/embed/sec/tropic/tropic.c b/core/embed/sec/tropic/tropic.c
index 1563f132..727ad96c 100644
--- a/core/embed/sec/tropic/tropic.c
+++ b/core/embed/sec/tropic/tropic.c
@@ -65,7 +65,7 @@
#ifdef TREZOR_EMULATOR
#define TROPIC_RETRY_COMMAND(command) command
#else
-#define TROPIC_MAX_RETRIES 5
+#define TROPIC_MAX_RETRIES 10
bool tropic_session_start(void);
@@ -76,34 +76,32 @@ static bool is_retryable(lt_ret_t ret) {
// Statement expression, see
// https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
-#define TROPIC_RETRY_COMMAND(command) \
- ({ \
- bool TROPIC_RETRY_COMMAND_session_started = \
- g_tropic_driver.session_started; \
- lt_pkey_index_t TROPIC_RETRY_COMMAND_pairing_key_index = \
- g_tropic_driver.pairing_key_index; \
- lt_ret_t TROPIC_RETRY_COMMAND_res = command; \
- for (int TROPIC_RETRY_COMMAND_i = 0; \
- TROPIC_RETRY_COMMAND_i < TROPIC_MAX_RETRIES - 1; \
- TROPIC_RETRY_COMMAND_i++) { \
- if (!is_retryable(TROPIC_RETRY_COMMAND_res)) { \
- break; \
- } \
- if (TROPIC_RETRY_COMMAND_res == LT_L1_CHIP_ALARM_MODE) { \
- tropic01_reset(); \
- tropic_deinit(); \
- tropic_init(); \
- tropic_wait_for_ready(NULL); \
- if (TROPIC_RETRY_COMMAND_session_started) { \
- if (tropic_custom_session_start( \
- NULL, TROPIC_RETRY_COMMAND_pairing_key_index) != LT_OK) { \
- continue; \
- } \
- } \
- } \
- TROPIC_RETRY_COMMAND_res = command; \
- } \
- TROPIC_RETRY_COMMAND_res; \
+#define TROPIC_RETRY_COMMAND(command) \
+ ({ \
+ bool TROPIC_RETRY_COMMAND_session_started = \
+ g_tropic_driver.session_started; \
+ lt_pkey_index_t TROPIC_RETRY_COMMAND_pairing_key_index = \
+ g_tropic_driver.pairing_key_index; \
+ lt_ret_t TROPIC_RETRY_COMMAND_res = command; \
+ for (int TROPIC_RETRY_COMMAND_i = 0; \
+ TROPIC_RETRY_COMMAND_i < TROPIC_MAX_RETRIES - 1; \
+ TROPIC_RETRY_COMMAND_i++) { \
+ if (!is_retryable(TROPIC_RETRY_COMMAND_res)) { \
+ break; \
+ } \
+ tropic01_reset(); \
+ tropic_deinit(); \
+ tropic_init(); \
+ tropic_wait_for_ready(NULL); \
+ if (TROPIC_RETRY_COMMAND_session_started) { \
+ if (tropic_custom_session_start( \
+ NULL, TROPIC_RETRY_COMMAND_pairing_key_index) != LT_OK) { \
+ continue; \
+ } \
+ } \
+ TROPIC_RETRY_COMMAND_res = command; \
+ } \
+ TROPIC_RETRY_COMMAND_res; \
})
#endif // TREZOR_EMULATOR
@@ -196,6 +194,7 @@ bool tropic_get_cert_chain_ptr(cli_t *cli, uint8_t const **cert_chain,
*cert_chain_length = tropic_cert_chain_length;
return true;
}
+
#endif // !PRODUCTION || defined(TREZOR_PRODTEST)
// If `TREZOR_PRODTEST` is not defined, the `cli` argument is ignored.
@@ -367,6 +366,60 @@ void tropic_session_start_time(uint32_t *time_ms) {
}
}
+lt_ret_t lt_ecc_key_erase_retry(lt_handle_t *tropic_handle,
+ const lt_ecc_slot_t ecc_slot) {
+ return TROPIC_RETRY_COMMAND(lt_ecc_key_erase(tropic_handle, ecc_slot));
+}
+
+lt_ret_t lt_r_mem_data_erase_retry(lt_handle_t *tropic_handle,
+ const uint16_t udata_slot) {
+ return TROPIC_RETRY_COMMAND(lt_r_mem_data_erase(tropic_handle, udata_slot));
+}
+
+lt_ret_t lt_mac_and_destroy_retry(lt_handle_t *tropic_handle,
+ const lt_mac_and_destroy_slot_t slot,
+ const uint8_t *data_out, uint8_t *data_in) {
+ return TROPIC_RETRY_COMMAND(
+ lt_mac_and_destroy(tropic_handle, slot, data_out, data_in));
+}
+
+lt_ret_t lt_read_whole_R_config_retry(lt_handle_t *tropic_handle,
+ struct lt_config_t *config) {
+ return TROPIC_RETRY_COMMAND(lt_read_whole_R_config(tropic_handle, config));
+}
+
+static lt_ret_t lt_erase_and_write_R_config(lt_handle_t *tropic_handle,
+ const struct lt_config_t *config) {
+ lt_ret_t ret = lt_r_config_erase(tropic_handle);
+ if (ret != LT_OK) {
+ return ret;
+ }
+
+ for (uint8_t i = 0; i < LT_CONFIG_OBJ_CNT; i++) {
+ ret = TROPIC_RETRY_COMMAND(lt_r_config_write(
+ tropic_handle, cfg_desc_table[i].addr, config->obj[i]));
+ if (ret != LT_OK) {
+ uint32_t obj = 0;
+ lt_ret_t inside_ret = TROPIC_RETRY_COMMAND(
+ lt_r_config_read(tropic_handle, cfg_desc_table[i].addr, &obj));
+ if (inside_ret != LT_OK) {
+ return inside_ret;
+ }
+ if (memcmp(&obj, &config->obj[i], sizeof(uint32_t)) != 0) {
+ return ret;
+ }
+ }
+ }
+
+ return LT_OK;
+}
+
+lt_ret_t lt_erase_and_write_R_config_retry(lt_handle_t *tropic_handle,
+ const struct lt_config_t *config) {
+ return TROPIC_RETRY_COMMAND(
+ lt_erase_and_write_R_config(tropic_handle, config));
+}
+
#ifdef TREZOR_EMULATOR
bool tropic_init(uint16_t port) {
#else
Why this scored 35/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.