feat(core/prodtest): Improve state handling around Tropic locking.
What changed, and why it matters
This commit tightens the order in which factory-setup commands must run for Trezor devices that use a Tropic secure chip. It prevents secrets from being initialized or the Tropic chip from being locked if earlier pairing steps were skipped or failed. The changes reduce the chance that a device leaves the factory in an inconsistent or partially-secured state, but they are defensive hardening rather than a fix for a known active attack.
Treat as a defensive hardening commit. Review the updated provisioning command ordering in the prodtest README, ensure factory tooling enforces the new sequence (secrets-init before nrf-pair/tropic-pair, tropic-pair before tropic-lock), and verify that devices already in the field were not affected by the pre-patch state-handling behavior.
Security signals we found
Prevents secrets initialization after Tropic pairing has already started, avoiding overwrite of pairing secrets
Adds lock-ordering guard: Tropic lock now requires prior successful pairing
Replaces fatal errors with 'NO' status in lock-check when pairing incomplete, improving safe state reporting
Caches paired state to avoid repeated cryptographic session attempts
Centralizes pairing prerequisite checks across nrf-pair, secrets-init, tropic-lock, and tropic-handshake
Evidence from the diff
The patch refactors prodtest state handling around Tropic pairing and locking. Key changes: (1) prodtest_secrets_init now refuses to run if the secrets sector, Optiga, or Tropic pairing public key is already present/locked, preventing overwrite of pairing material. (2) prodtest_tropic_lock_check returns ‘NO’ instead of fatal errors when pairing was not started or failed midway. (3) prodtest_tropic_lock now requires tropic_is_paired(NULL) before executing irreversible config writes. (4) is_paired was renamed to tropic_is_paired, caches its result, and accepts NULL cli to allow silent checks. (5) prodtest_nrf_pair now checks secret_is_locked() instead of separate Optiga and OTP SN locks. These are state-machine hardening changes in the manufacturing test tooling.
Changed components
core/embed/projects/prodtest/cmd/prodtest_tropic.ccore/embed/projects/prodtest/cmd/prodtest_secrets.ccore/embed/projects/prodtest/cmd/prodtest_nrf.ccore/embed/projects/prodtest/cmd/prodtest_otp_variant.ccore/embed/projects/prodtest/cmd/common.ccore/embed/projects/prodtest/README.mdInspect captured patch +143 / −88
diff --git a/core/embed/projects/prodtest/README.md b/core/embed/projects/prodtest/README.md
index ae1374f32..a8be461be 100644
--- a/core/embed/projects/prodtest/README.md
+++ b/core/embed/projects/prodtest/README.md
@@ -362,8 +362,7 @@ Updates the nRF firmware. Use `core/tools/bin_update.py` script to update the nR
### nrf-pair
Writes the pairing secret to the nRF chip to pair it with the MCU.
-The command `secrets-init` must be executed before calling this command.
-Pairing needs to be done before writing device serial number in the OTP memory and before locking the Optiga chip.
+This command may be called only after `secrets-init` was executed and before `secrets-lock` is executed.
Example:
```
diff --git a/core/embed/projects/prodtest/cmd/common.c b/core/embed/projects/prodtest/cmd/common.c
index a8d533f7e..b7cbc3d5d 100644
--- a/core/embed/projects/prodtest/cmd/common.c
+++ b/core/embed/projects/prodtest/cmd/common.c
@@ -406,7 +406,7 @@ bool check_cert_chain(cli_t* cli, const uint8_t* chain, size_t chain_size,
sizeof(OID_SERIAL_NUMBER), &subject_sn,
&subject_sn_size)) {
cli_error(cli, CLI_ERROR,
- "check_device_cert_chain, device_sn not set.");
+ "check_device_cert_chain, serialNumber not set.");
}
if (subject_sn_size != device_sn_size ||
diff --git a/core/embed/projects/prodtest/cmd/prodtest_nrf.c b/core/embed/projects/prodtest/cmd/prodtest_nrf.c
index 82b563f99..c1eddccdc 100644
--- a/core/embed/projects/prodtest/cmd/prodtest_nrf.c
+++ b/core/embed/projects/prodtest/cmd/prodtest_nrf.c
@@ -24,10 +24,9 @@
#include <io/nrf.h>
#include <rtl/cli.h>
-#include <util/flash_otp.h>
+#include <sec/secret.h>
#include "common.h"
-#include "prodtest_optiga.h"
static void prodtest_nrf_communication(cli_t* cli) {
cli_trace(cli, "Testing SPI communication...");
@@ -88,18 +87,12 @@ static void prodtest_nrf_pair(cli_t* cli) {
return;
}
- if (OPTIGA_LOCKED_FALSE != get_optiga_locked_status(cli)) {
+ if (secfalse != secret_is_locked()) {
cli_error(cli, CLI_ERROR,
- "Optiga is not unlocked. Pairing is not allowed.");
+ "Secrets sector is locked. Pairing is not allowed.");
return;
}
- if (secfalse != flash_otp_is_locked(FLASH_OTP_BLOCK_DEVICE_SN)) {
- cli_error(
- cli, CLI_ERROR,
- "OTP Device serial number block is locked. Pairing is not allowed.");
- }
-
if (nrf_test_pair()) {
cli_ok(cli, "");
} else {
diff --git a/core/embed/projects/prodtest/cmd/prodtest_otp_variant.c b/core/embed/projects/prodtest/cmd/prodtest_otp_variant.c
index 065c149d2..7ff14c40c 100644
--- a/core/embed/projects/prodtest/cmd/prodtest_otp_variant.c
+++ b/core/embed/projects/prodtest/cmd/prodtest_otp_variant.c
@@ -29,6 +29,10 @@
#include <stdlib.h>
#include "prodtest_optiga.h"
+#ifdef USE_TROPIC
+#include "prodtest_tropic.h"
+#endif
+
static void prodtest_otp_variant_read(cli_t* cli) {
if (cli_arg_count(cli) > 0) {
cli_error_arg_count(cli);
diff --git a/core/embed/projects/prodtest/cmd/prodtest_secrets.c b/core/embed/projects/prodtest/cmd/prodtest_secrets.c
index 289b4abaa..0f33fc071 100644
--- a/core/embed/projects/prodtest/cmd/prodtest_secrets.c
+++ b/core/embed/projects/prodtest/cmd/prodtest_secrets.c
@@ -33,6 +33,7 @@
#ifdef USE_OPTIGA
#include <sec/optiga.h>
+#include "prodtest_optiga.h"
#endif
#ifdef USE_TROPIC
@@ -116,7 +117,40 @@ static void prodtest_secrets_init(cli_t* cli) {
return;
}
+#ifdef SECRET_LOCK_SLOT_OFFSET
+ // Make sure that the secrets sector isn't locked so that we don't overwrite
+ // the MCU's nRF pairing secret.
+ if (secfalse != secret_is_locked()) {
+ cli_error(cli, CLI_ERROR, "Secret sector is already locked");
+ return;
+ }
+#endif
+
+#ifdef USE_OPTIGA
+ // Make sure that Optiga isn't locked so that we don't overwrite the MCU's
+ // pairing secrets.
+ optiga_locked_status optiga_status = get_optiga_locked_status(cli);
+
+ if (optiga_status == OPTIGA_LOCKED_TRUE) {
+ cli_error(cli, CLI_ERROR, "Optiga is already locked");
+ return;
+ }
+
+ if (optiga_status != OPTIGA_LOCKED_FALSE) {
+ // Error reported by get_optiga_locked_status().
+ return;
+ }
+#endif
+
#ifdef USE_TROPIC
+ // Make sure that Tropic pairing hasn't started so that we don't overwrite the
+ // MCU's pairing secrets.
+ curve25519_key tropic_public = {0};
+ if (secret_key_tropic_public(tropic_public) == sectrue) {
+ cli_error(cli, CLI_ERROR, "Tropic pairing has already started.");
+ 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.
diff --git a/core/embed/projects/prodtest/cmd/prodtest_tropic.c b/core/embed/projects/prodtest/cmd/prodtest_tropic.c
index c3dea386f..7155a5db6 100644
--- a/core/embed/projects/prodtest/cmd/prodtest_tropic.c
+++ b/core/embed/projects/prodtest/cmd/prodtest_tropic.c
@@ -68,8 +68,6 @@ typedef enum {
static tropic_handshake_state_t tropic_handshake_state =
TROPIC_HANDSHAKE_STATE_0;
-static bool tropic_is_paired = false;
-
static uint8_t tropic_cert_chain[LT_NUM_CERTIFICATES *
LT_L2_GET_INFO_REQ_CERT_SIZE_SINGLE] = {0};
static size_t tropic_cert_chain_length = 0;
@@ -504,7 +502,7 @@ static struct lt_config_t reversible_configuration = {
BIT(1) | BIT(2) | BIT(9) | BIT(10) | BIT(17) | BIT(18) | BIT(25) |
BIT(26),
}};
-// clang-format-on
+// clang-format on
// TODO: Implement a function `lt_handle_t* start_session(const pkey_index_t
// pkey_index)`. This function will check whether a session with a specified
@@ -591,12 +589,14 @@ static bool cache_tropic_cert_chain(void) {
return false;
}
- ret = lt_get_st_pub(&cert_store, tropic_public_cached, sizeof(tropic_public_cached));
+ ret = lt_get_st_pub(&cert_store, tropic_public_cached,
+ sizeof(tropic_public_cached));
if (ret != LT_OK) {
return false;
}
- // Compactify tropic_cert_chain for future use. This invalidates the cert_store.
+ // Compactify tropic_cert_chain for future use. This invalidates the
+ // cert_store.
size_t length = 0;
for (size_t i = 0; i < LT_NUM_CERTIFICATES; i++) {
memmove(&tropic_cert_chain[length], cert_store.certs[i],
@@ -609,7 +609,7 @@ static bool cache_tropic_cert_chain(void) {
return true;
}
-static const curve25519_key *prodtest_tropic_get_tropic_public(void) {
+static const curve25519_key* prodtest_tropic_get_tropic_public(void) {
if (!cache_tropic_cert_chain()) {
return NULL;
}
@@ -643,7 +643,8 @@ static void prodtest_tropic_lock_check(cli_t* cli) {
curve25519_key tropic_public = {0};
if (secret_key_tropic_public(tropic_public) != sectrue) {
- cli_error(cli, CLI_ERROR, "`secret_key_tropic_public()` failed.");
+ // The Tropic pairing process was not initiated.
+ cli_ok(cli, "NO");
goto cleanup;
}
@@ -656,13 +657,12 @@ static void prodtest_tropic_lock_check(cli_t* cli) {
curve25519_key privileged_public = {0};
curve25519_scalarmult_basepoint(privileged_public, privileged_private);
- ret =
- lt_session_start(tropic_handle, tropic_public, PRIVILEGED_PAIRING_KEY_SLOT,
- privileged_private, privileged_public);
+ ret = lt_session_start(tropic_handle, tropic_public,
+ PRIVILEGED_PAIRING_KEY_SLOT, privileged_private,
+ privileged_public);
if (ret != LT_OK) {
- cli_error(cli, CLI_ERROR,
- "`lt_session_start()` for privileged key failed with error %d",
- ret);
+ // The Tropic pairing process was initiated but probably failed midway.
+ cli_ok(cli, "NO");
goto cleanup;
}
@@ -672,32 +672,32 @@ static void prodtest_tropic_lock_check(cli_t* cli) {
if (ret != LT_OK) {
cli_error(cli, CLI_ERROR, "`lt_read_whole_R_config()` failed with error %d",
ret);
- return;
+ goto cleanup;
}
if (memcmp(&reversible_configuration, (uint8_t*)&configuration_read,
sizeof(reversible_configuration)) != 0) {
cli_ok(cli, "NO");
- return;
+ goto cleanup;
}
ret = lt_read_whole_I_config(tropic_handle, &configuration_read);
if (ret != LT_OK) {
cli_error(cli, CLI_ERROR, "`lt_read_whole_I_config()` failed with error %d",
ret);
- return;
+ goto cleanup;
}
if (memcmp(&irreversible_configuration, (uint8_t*)&configuration_read,
sizeof(irreversible_configuration)) != 0) {
cli_ok(cli, "NO");
- return;
+ goto cleanup;
}
cli_ok(cli, "YES");
cleanup:
- memzero(privileged_private, sizeof(privileged_private));
+ memzero(privileged_private, sizeof(privileged_private));
}
static lt_ret_t pairing_key_write(lt_handle_t* handle, pkey_index_t slot,
@@ -722,22 +722,31 @@ static lt_ret_t pairing_key_write(lt_handle_t* handle, pkey_index_t slot,
return LT_OK;
}
-static bool is_paired(cli_t* cli) {
+static bool tropic_is_paired(cli_t* cli) {
+ static bool is_paired = false;
+ if (is_paired) {
+ return true;
+ }
+
lt_handle_t* tropic_handle = tropic_get_handle();
lt_ret_t ret = LT_FAIL;
// Retrieve the tropic public key.
curve25519_key tropic_public = {0};
if (secret_key_tropic_public(tropic_public) != sectrue) {
- cli_error(cli, CLI_ERROR, "`secret_key_tropic_public()` failed.");
+ if (cli != NULL) {
+ cli_error(cli, CLI_ERROR, "`secret_key_tropic_public()` failed.");
+ }
goto cleanup;
}
// Retrieve the unprivileged key pair and try to establish a session using it.
curve25519_key unprivileged_private = {0};
if (secret_key_tropic_pairing_unprivileged(unprivileged_private) != sectrue) {
- cli_error(cli, CLI_ERROR,
- "`secret_key_tropic_pairing_unprivileged()` failed.");
+ if (cli != NULL) {
+ cli_error(cli, CLI_ERROR,
+ "`secret_key_tropic_pairing_unprivileged()` failed.");
+ }
goto cleanup;
}
curve25519_key unprivileged_public = {0};
@@ -746,29 +755,36 @@ static bool is_paired(cli_t* cli) {
UNPRIVILEGED_PAIRING_KEY_SLOT, unprivileged_private,
unprivileged_public);
if (ret != LT_OK) {
- cli_error(cli, CLI_ERROR,
- "`lt_session_start()` for unprivileged key failed with error %d",
- ret);
+ if (cli != NULL) {
+ cli_error(
+ cli, CLI_ERROR,
+ "`lt_session_start()` for unprivileged key failed with error %d",
+ ret);
+ }
goto cleanup;
}
// Retrieve the privileged key pair and try to establish a session using it.
curve25519_key privileged_private = {0};
if (secret_key_tropic_pairing_privileged(privileged_private) != sectrue) {
- cli_error(cli, CLI_ERROR,
- "`secret_key_tropic_pairing_privileged()` failed.");
+ if (cli != NULL) {
+ cli_error(cli, CLI_ERROR,
+ "`secret_key_tropic_pairing_privileged()` failed.");
+ }
goto cleanup;
}
curve25519_key privileged_public = {0};
curve25519_scalarmult_basepoint(privileged_public, privileged_private);
- ret =
- lt_session_start(tropic_handle, tropic_public, PRIVILEGED_PAIRING_KEY_SLOT,
- privileged_private, privileged_public);
+ ret = lt_session_start(tropic_handle, tropic_public,
+ PRIVILEGED_PAIRING_KEY_SLOT, privileged_private,
+ privileged_public);
if (ret != LT_OK) {
- cli_error(cli, CLI_ERROR,
- "`lt_session_start()` for privileged key failed with error %d",
- ret);
+ if (cli != NULL) {
+ cli_error(cli, CLI_ERROR,
+ "`lt_session_start()` for privileged key failed with error %d",
+ ret);
+ }
goto cleanup;
}
@@ -777,10 +793,12 @@ static bool is_paired(cli_t* cli) {
ret =
lt_pairing_key_read(tropic_handle, public_read, FACTORY_PAIRING_KEY_SLOT);
if (ret != LT_L3_PAIRING_KEY_INVALID) {
- cli_error(cli, CLI_ERROR,
- "`lt_pairing_key_read()` for factory pairing key failed with "
- "error %d",
- ret);
+ if (cli != NULL) {
+ cli_error(cli, CLI_ERROR,
+ "`lt_pairing_key_read()` for factory pairing key failed with "
+ "error %d",
+ ret);
+ }
goto cleanup;
}
@@ -788,21 +806,22 @@ static bool is_paired(cli_t* cli) {
ret =
lt_pairing_key_read(tropic_handle, public_read, PAIRING_KEY_SLOT_INDEX_3);
if (ret != LT_L3_PAIRING_KEY_EMPTY) {
- cli_error(cli, CLI_ERROR,
- "`lt_pairing_key_read()` for pairing key slot 3 failed with "
- "error %d",
- ret);
+ if (cli != NULL) {
+ cli_error(cli, CLI_ERROR,
+ "`lt_pairing_key_read()` for pairing key slot 3 failed with "
+ "error %d",
+ ret);
+ }
goto cleanup;
}
- tropic_is_paired = true;
- ret = LT_OK;
+ is_paired = true;
cleanup:
memzero(privileged_private, sizeof(privileged_private));
memzero(unprivileged_private, sizeof(unprivileged_private));
- return ret == LT_OK;
+ return is_paired;
}
bool prodtest_tropic_factory_session_start(lt_handle_t* tropic_handle) {
@@ -829,14 +848,15 @@ bool prodtest_tropic_factory_session_start(lt_handle_t* tropic_handle) {
curve25519_key factory_public = {0};
curve25519_scalarmult_basepoint(factory_public, factory_private);
- const curve25519_key *tropic_public = prodtest_tropic_get_tropic_public();
+ const curve25519_key* tropic_public = prodtest_tropic_get_tropic_public();
if (tropic_public == NULL) {
return false;
}
// Try to establish a session using the factory pairing key.
- return LT_OK == lt_session_start(tropic_handle, *tropic_public, FACTORY_PAIRING_KEY_SLOT,
- factory_private, factory_public);
+ return LT_OK == lt_session_start(tropic_handle, *tropic_public,
+ FACTORY_PAIRING_KEY_SLOT, factory_private,
+ factory_public);
}
static void prodtest_tropic_pair(cli_t* cli) {
@@ -866,7 +886,8 @@ static void prodtest_tropic_pair(cli_t* cli) {
lt_handle_t* tropic_handle = tropic_get_handle();
// Get the Tropic01 public pairing key from the chip's certificate.
- const curve25519_key *tropic_public_cert = prodtest_tropic_get_tropic_public();
+ const curve25519_key* tropic_public_cert =
+ prodtest_tropic_get_tropic_public();
if (tropic_public_cert == NULL) {
cli_error(cli, CLI_ERROR, "`prodtest_tropic_get_tropic_public()` failed");
goto cleanup;
@@ -890,7 +911,8 @@ static void prodtest_tropic_pair(cli_t* cli) {
goto cleanup;
}
}
- if (memcmp(*tropic_public_cert, tropic_public_flash, sizeof(curve25519_key)) != 0) {
+ if (memcmp(*tropic_public_cert, tropic_public_flash,
+ sizeof(curve25519_key)) != 0) {
cli_error(cli, CLI_ERROR,
"Tropic public key does not match the expected value.");
goto cleanup;
@@ -920,7 +942,7 @@ static void prodtest_tropic_pair(cli_t* cli) {
// Write the privileged pairing key to the tropic's pairing key slot if it
// has not been written yet.
lt_ret_t ret = pairing_key_write(tropic_handle, PRIVILEGED_PAIRING_KEY_SLOT,
- privileged_public);
+ privileged_public);
// If the pairing key has already been written, `pairing_key_write()`
// returns `LT_OK`.
if (ret != LT_OK) {
@@ -959,7 +981,7 @@ static void prodtest_tropic_pair(cli_t* cli) {
}
}
- if (is_paired(cli) == true) {
+ if (tropic_is_paired(cli)) {
cli_ok(cli, "");
}
@@ -984,7 +1006,7 @@ static void prodtest_tropic_get_access_credential(cli_t* cli) {
goto cleanup;
}
- const curve25519_key *tropic_public = prodtest_tropic_get_tropic_public();
+ const curve25519_key* tropic_public = prodtest_tropic_get_tropic_public();
if (tropic_public == NULL) {
cli_error(cli, CLI_ERROR, "`prodtest_tropic_get_tropic_public()` failed");
goto cleanup;
@@ -1079,8 +1101,8 @@ static void prodtest_tropic_handshake(cli_t* cli) {
return;
}
- if (tropic_is_paired != true) {
- cli_error(cli, CLI_ERROR, "You have to call `tropic-pair` first.");
+ if (!tropic_is_paired(NULL)) {
+ cli_error(cli, CLI_ERROR, "`tropic-pair` must be called first.");
return;
}
@@ -1245,6 +1267,11 @@ static void prodtest_tropic_lock(cli_t* cli) {
return;
}
+ if (!tropic_is_paired(NULL)) {
+ cli_error(cli, CLI_ERROR, "`tropic-pair` must be called first.");
+ return;
+ }
+
tropic_handshake_state = TROPIC_HANDSHAKE_STATE_0;
lt_handle_t* tropic_handle = tropic_get_handle();
@@ -1265,9 +1292,9 @@ static void prodtest_tropic_lock(cli_t* cli) {
curve25519_key privileged_public = {0};
curve25519_scalarmult_basepoint(privileged_public, privileged_private);
- ret =
- lt_session_start(tropic_handle, tropic_public, PRIVILEGED_PAIRING_KEY_SLOT,
- privileged_private, privileged_public);
+ ret = lt_session_start(tropic_handle, tropic_public,
+ PRIVILEGED_PAIRING_KEY_SLOT, privileged_private,
+ privileged_public);
if (ret != LT_OK) {
cli_error(cli, CLI_ERROR,
"`lt_session_start()` for privileged key failed with error %d",
@@ -1281,54 +1308,54 @@ static void prodtest_tropic_lock(cli_t* cli) {
if (ret != LT_OK) {
cli_error(cli, CLI_ERROR, "`lt_r_config_erase()` failed with error %d",
ret);
- return;
+ goto cleanup;
}
ret = lt_write_whole_R_config(tropic_handle, &reversible_configuration);
if (ret != LT_OK) {
cli_error(cli, CLI_ERROR,
"`lt_write_whole_R_config()` failed with error %d", ret);
- return;
+ goto cleanup;
}
ret = lt_read_whole_R_config(tropic_handle, &configuration_read);
if (ret != LT_OK) {
cli_error(cli, CLI_ERROR, "`lt_read_whole_R_config()` failed with error %d",
ret);
- return;
+ goto cleanup;
}
if (memcmp(&reversible_configuration, (uint8_t*)&configuration_read,
sizeof(reversible_configuration)) != 0) {
cli_error(cli, CLI_ERROR, "Reversible configuration mismatch after write.");
- return;
+ goto cleanup;
}
ret = lt_write_whole_I_config(tropic_handle, &irreversible_configuration);
if (ret != LT_OK) {
cli_error(cli, CLI_ERROR,
"`lt_write_whole_I_config()` failed with error %d", ret);
- return;
+ goto cleanup;
}
ret = lt_read_whole_I_config(tropic_handle, &configuration_read);
if (ret != LT_OK) {
cli_error(cli, CLI_ERROR, "`lt_read_whole_I_config()` failed with error %d",
ret);
- return;
+ goto cleanup;
}
if (memcmp(&irreversible_configuration, (uint8_t*)&configuration_read,
sizeof(irreversible_configuration)) != 0) {
cli_error(cli, CLI_ERROR,
"Irreversible configuration mismatch after write.");
- return;
+ goto cleanup;
}
cli_ok(cli, "");
cleanup:
- memzero(privileged_private, sizeof(privileged_private));
+ memzero(privileged_private, sizeof(privileged_private));
}
static lt_ret_t data_write(lt_handle_t* h, uint16_t first_slot,
@@ -1485,9 +1512,9 @@ static void cert_write(cli_t* cli, uint16_t first_slot, uint16_t slots_count) {
curve25519_key privileged_public = {0};
curve25519_scalarmult_basepoint(privileged_public, privileged_private);
- ret =
- lt_session_start(tropic_handle, tropic_public, PRIVILEGED_PAIRING_KEY_SLOT,
- privileged_private, privileged_public);
+ ret = lt_session_start(tropic_handle, tropic_public,
+ PRIVILEGED_PAIRING_KEY_SLOT, privileged_private,
+ privileged_public);
if (ret != LT_OK) {
cli_error(cli, CLI_ERROR,
"`lt_session_start()` for privileged key failed with error %d",
@@ -1552,9 +1579,9 @@ static void cert_read(cli_t* cli, uint16_t first_slot, uint16_t slots_count) {
curve25519_key privileged_public = {0};
curve25519_scalarmult_basepoint(privileged_public, privileged_private);
- ret =
- lt_session_start(tropic_handle, tropic_public, PRIVILEGED_PAIRING_KEY_SLOT,
- privileged_private, privileged_public);
+ ret = lt_session_start(tropic_handle, tropic_public,
+ PRIVILEGED_PAIRING_KEY_SLOT, privileged_private,
+ privileged_public);
if (ret != LT_OK) {
cli_error(cli, CLI_ERROR,
"`lt_session_start()` for privileged key failed with error %d",
@@ -1582,7 +1609,6 @@ static void prodtest_tropic_certfido_write(cli_t* cli) {
static void prodtest_tropic_certdev_write(cli_t* cli) {
cert_write(cli, TROPIC_DEV_CERT_FIRST_SLOT, TROPIC_DEV_CERT_SLOTS_COUNT);
- // TODO: Parse the device serial number from the certificate and verify that it matches the device serial number written by `otp-device-sn-write`. Do the same for the MCU device certificate and the Optiga device certificate.
}
static void prodtest_tropic_certfido_read(cli_t* cli) {
@@ -1603,9 +1629,8 @@ static void pubkey_read(cli_t* cli, ecc_slot_t slot,
uint8_t public_key[ECDSA_PUBLIC_KEY_SIZE] = {0x04};
lt_ecc_curve_type_t curve_type = 0;
ecc_key_origin_t origin = 0;
- lt_ret_t ret =
- lt_ecc_key_read(tropic_get_handle(), slot,
- &public_key[1], &curve_type, &origin);
+ lt_ret_t ret = lt_ecc_key_read(tropic_get_handle(), slot, &public_key[1],
+ &curve_type, &origin);
if (ret != LT_OK || curve_type != CURVE_P256) {
cli_error(cli, CLI_ERROR, "lt_ecc_key_read error %d.", ret);
return;
Why this scored 42/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.