refactor(core): Move Tropic constants to tropic.h.
What changed, and why it matters
This is a routine code cleanup that moves Tropic chip constants (like which key slot is used for what) into a shared header file so multiple parts of the firmware can use the same definitions. It also updates the Python binding for signing to use the correct Ed25519 signature size and exposes two key-slot constants to Python. There is no security vulnerability here.
No security action required; this is a benign refactor. Reviewers may optionally verify that the renamed constants and Ed25519 signature-size change are consistently applied across all build configurations.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors Tropic secure-element constants from a prodtest source file into core/embed/sec/tropic/inc/sec/tropic.h and renames some (e.g., TROPIC_DEV_ to TROPIC_DEVICE_, _SLOTS_COUNT to _SLOT_COUNT). It updates all consumers (prodtest, MicroPython module, generated type stub, and unit test) to use the centralized names. Additionally, modtrezorcrypto-tropic.h switches the signature buffer size from ECDSA_RAW_SIGNATURE_SIZE to sizeof(ed25519_signature) and removes a hard-coded 32-byte digest length check before calling tropic_ecc_sign. The test is adjusted to expect a TropicError because the key is not yet generated, rather than a ValueError for digest length.
Changed components
core/embed/projects/prodtest/cmd/prodtest_tropic.ccore/embed/sec/tropic/inc/sec/tropic.hcore/embed/upymod/modtrezorcrypto/modtrezorcrypto-tropic.hcore/mocks/generated/trezorcrypto/tropic.pyicore/tests/test_trezor.crypto.tropic.pyInspect captured patch +60 / −45
diff --git a/core/embed/projects/prodtest/cmd/prodtest_tropic.c b/core/embed/projects/prodtest/cmd/prodtest_tropic.c
index 52ead8aa..bdea6d3a 100644
--- a/core/embed/projects/prodtest/cmd/prodtest_tropic.c
+++ b/core/embed/projects/prodtest/cmd/prodtest_tropic.c
@@ -40,24 +40,9 @@
#include "libtropic.h"
#include "lt_l2.h"
-#include "secure_channel.h"
+#include <sec/tropic.h>
-#define FACTORY_PAIRING_KEY_SLOT \
- PAIRING_KEY_SLOT_INDEX_0 // This key is used by prodtest to inject the
- // privileged and unprivileged pairing keys
-#define UNPRIVILEGED_PAIRING_KEY_SLOT \
- PAIRING_KEY_SLOT_INDEX_1 // This key is used by HSM to inject the attestation
- // FIDO key and generate the device key, and by an
- // unofficial firwmare with an unlocked bootloader
-#define PRIVILEGED_PAIRING_KEY_SLOT \
- PAIRING_KEY_SLOT_INDEX_2 // This key is used by an official firmware
-
-#define TROPIC_FIDO_CERT_FIRST_SLOT 0
-#define TROPIC_FIDO_CERT_SLOTS_COUNT 3
-#define TROPIC_FIDO_KEY_SLOT ECC_SLOT_1
-#define TROPIC_DEV_CERT_FIRST_SLOT 3
-#define TROPIC_DEV_CERT_SLOTS_COUNT 3
-#define TROPIC_DEV_KEY_SLOT 0
+#include "secure_channel.h"
typedef enum {
TROPIC_HANDSHAKE_STATE_0, // Handshake has not been initiated yet
@@ -676,7 +661,7 @@ tropic_locked_status get_tropic_locked_status(cli_t* cli) {
curve25519_scalarmult_basepoint(privileged_public, privileged_private);
ret = lt_session_start(tropic_handle, tropic_public,
- PRIVILEGED_PAIRING_KEY_SLOT, privileged_private,
+ TROPIC_PRIVILEGED_PAIRING_KEY_SLOT, privileged_private,
privileged_public);
if (ret != LT_OK) {
// The Tropic pairing process was initiated but probably failed midway.
@@ -773,8 +758,8 @@ static bool tropic_is_paired(cli_t* cli) {
curve25519_key unprivileged_public = {0};
curve25519_scalarmult_basepoint(unprivileged_public, unprivileged_private);
ret = lt_session_start(tropic_handle, tropic_public,
- UNPRIVILEGED_PAIRING_KEY_SLOT, unprivileged_private,
- unprivileged_public);
+ TROPIC_UNPRIVILEGED_PAIRING_KEY_SLOT,
+ unprivileged_private, unprivileged_public);
if (ret != LT_OK) {
if (cli != NULL) {
cli_error(
@@ -798,7 +783,7 @@ static bool tropic_is_paired(cli_t* cli) {
curve25519_scalarmult_basepoint(privileged_public, privileged_private);
ret = lt_session_start(tropic_handle, tropic_public,
- PRIVILEGED_PAIRING_KEY_SLOT, privileged_private,
+ TROPIC_PRIVILEGED_PAIRING_KEY_SLOT, privileged_private,
privileged_public);
if (ret != LT_OK) {
if (cli != NULL) {
@@ -811,8 +796,8 @@ static bool tropic_is_paired(cli_t* cli) {
// Read the factory pairing key to ensure it is invalidated.
curve25519_key public_read = {0};
- ret =
- lt_pairing_key_read(tropic_handle, public_read, FACTORY_PAIRING_KEY_SLOT);
+ ret = lt_pairing_key_read(tropic_handle, public_read,
+ TROPIC_FACTORY_PAIRING_KEY_SLOT);
if (ret != LT_L3_PAIRING_KEY_INVALID) {
if (cli != NULL) {
cli_error(cli, CLI_ERROR,
@@ -876,8 +861,8 @@ bool prodtest_tropic_factory_session_start(lt_handle_t* tropic_handle) {
// 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);
+ TROPIC_FACTORY_PAIRING_KEY_SLOT,
+ factory_private, factory_public);
}
static void prodtest_tropic_pair(cli_t* cli) {
@@ -962,8 +947,8 @@ static void prodtest_tropic_pair(cli_t* cli) {
if (prodtest_tropic_factory_session_start(tropic_handle)) {
// 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);
+ lt_ret_t ret = pairing_key_write(
+ tropic_handle, TROPIC_PRIVILEGED_PAIRING_KEY_SLOT, privileged_public);
// If the pairing key has already been written, `pairing_key_write()`
// returns `LT_OK`.
if (ret != LT_OK) {
@@ -976,7 +961,7 @@ static void prodtest_tropic_pair(cli_t* cli) {
// Write the unprivileged pairing key to the tropic's pairing key slot if it
// has not been written yet.
- ret = pairing_key_write(tropic_handle, UNPRIVILEGED_PAIRING_KEY_SLOT,
+ ret = pairing_key_write(tropic_handle, TROPIC_UNPRIVILEGED_PAIRING_KEY_SLOT,
unprivileged_public);
// If the pairing key has already been written, `pairing_key_write()`
// returns `LT_OK`.
@@ -990,7 +975,8 @@ static void prodtest_tropic_pair(cli_t* cli) {
}
// Invalidate the factory pairing key if it has not been invalidated yet.
- ret = lt_pairing_key_invalidate(tropic_handle, FACTORY_PAIRING_KEY_SLOT);
+ ret = lt_pairing_key_invalidate(tropic_handle,
+ TROPIC_FACTORY_PAIRING_KEY_SLOT);
// If the factory has already been invalidated,
// `lt_pairing_key_invalidate()` returns `LT_OK`.
if (ret != LT_OK) {
@@ -1314,7 +1300,7 @@ static void prodtest_tropic_lock(cli_t* cli) {
curve25519_scalarmult_basepoint(privileged_public, privileged_private);
ret = lt_session_start(tropic_handle, tropic_public,
- PRIVILEGED_PAIRING_KEY_SLOT, privileged_private,
+ TROPIC_PRIVILEGED_PAIRING_KEY_SLOT, privileged_private,
privileged_public);
if (ret != LT_OK) {
cli_error(cli, CLI_ERROR,
@@ -1480,7 +1466,7 @@ static bool check_device_cert_chain(cli_t* cli, const uint8_t* chain,
ed25519_signature signature = {0};
- if (lt_ecc_eddsa_sign(tropic_get_handle(), TROPIC_DEV_KEY_SLOT, challenge,
+ if (lt_ecc_eddsa_sign(tropic_get_handle(), TROPIC_DEVICE_KEY_SLOT, challenge,
sizeof(challenge), signature) != LT_OK) {
return false;
}
@@ -1534,7 +1520,7 @@ static void cert_write(cli_t* cli, uint16_t first_slot, uint16_t slots_count) {
curve25519_scalarmult_basepoint(privileged_public, privileged_private);
ret = lt_session_start(tropic_handle, tropic_public,
- PRIVILEGED_PAIRING_KEY_SLOT, privileged_private,
+ TROPIC_PRIVILEGED_PAIRING_KEY_SLOT, privileged_private,
privileged_public);
if (ret != LT_OK) {
cli_error(cli, CLI_ERROR,
@@ -1559,7 +1545,7 @@ static void cert_write(cli_t* cli, uint16_t first_slot, uint16_t slots_count) {
goto cleanup;
}
- if (first_slot == TROPIC_DEV_CERT_FIRST_SLOT &&
+ if (first_slot == TROPIC_DEVICE_CERT_FIRST_SLOT &&
!check_device_cert_chain(cli, certificate, certificate_length)) {
// Error returned by check_device_cert_chain().
return;
@@ -1601,7 +1587,7 @@ static void cert_read(cli_t* cli, uint16_t first_slot, uint16_t slots_count) {
curve25519_scalarmult_basepoint(privileged_public, privileged_private);
ret = lt_session_start(tropic_handle, tropic_public,
- PRIVILEGED_PAIRING_KEY_SLOT, privileged_private,
+ TROPIC_PRIVILEGED_PAIRING_KEY_SLOT, privileged_private,
privileged_public);
if (ret != LT_OK) {
cli_error(cli, CLI_ERROR,
@@ -1625,19 +1611,19 @@ cleanup:
}
static void prodtest_tropic_certfido_write(cli_t* cli) {
- cert_write(cli, TROPIC_FIDO_CERT_FIRST_SLOT, TROPIC_FIDO_CERT_SLOTS_COUNT);
+ cert_write(cli, TROPIC_FIDO_CERT_FIRST_SLOT, TROPIC_FIDO_CERT_SLOT_COUNT);
}
static void prodtest_tropic_certdev_write(cli_t* cli) {
- cert_write(cli, TROPIC_DEV_CERT_FIRST_SLOT, TROPIC_DEV_CERT_SLOTS_COUNT);
+ cert_write(cli, TROPIC_DEVICE_CERT_FIRST_SLOT, TROPIC_DEVICE_CERT_SLOT_COUNT);
}
static void prodtest_tropic_certfido_read(cli_t* cli) {
- cert_read(cli, TROPIC_FIDO_CERT_FIRST_SLOT, TROPIC_FIDO_CERT_SLOTS_COUNT);
+ cert_read(cli, TROPIC_FIDO_CERT_FIRST_SLOT, TROPIC_FIDO_CERT_SLOT_COUNT);
}
static void prodtest_tropic_certdev_read(cli_t* cli) {
- cert_read(cli, TROPIC_DEV_CERT_FIRST_SLOT, TROPIC_DEV_CERT_SLOTS_COUNT);
+ cert_read(cli, TROPIC_DEVICE_CERT_FIRST_SLOT, TROPIC_DEVICE_CERT_SLOT_COUNT);
}
static void pubkey_read(cli_t* cli, ecc_slot_t slot,
diff --git a/core/embed/sec/tropic/inc/sec/tropic.h b/core/embed/sec/tropic/inc/sec/tropic.h
index 312ca35f..5aa26d43 100644
--- a/core/embed/sec/tropic/inc/sec/tropic.h
+++ b/core/embed/sec/tropic/inc/sec/tropic.h
@@ -21,6 +21,27 @@
#include <trezor_types.h>
+// FIDO attestation key and certificate.
+#define TROPIC_FIDO_CERT_FIRST_SLOT 0
+#define TROPIC_FIDO_CERT_SLOT_COUNT 3
+#define TROPIC_FIDO_KEY_SLOT 1 // ECC_SLOT_1
+
+// Device attestation key and certificate.
+#define TROPIC_DEVICE_CERT_FIRST_SLOT 3
+#define TROPIC_DEVICE_CERT_SLOT_COUNT 3
+#define TROPIC_DEVICE_KEY_SLOT 0 // ECC_SLOT_0
+
+// Pairing key used by prodtest to inject the privileged and unprivileged
+// pairing keys.
+#define TROPIC_FACTORY_PAIRING_KEY_SLOT 0 // PAIRING_KEY_SLOT_INDEX_0
+
+// Pairing key used by the HSM to inject the attestation FIDO key and generate
+// the device key, and by unofficial firwmare.
+#define TROPIC_UNPRIVILEGED_PAIRING_KEY_SLOT 1 // PAIRING_KEY_SLOT_INDEX_1
+
+// Pairing key used by official firmware.
+#define TROPIC_PRIVILEGED_PAIRING_KEY_SLOT 2 // PAIRING_KEY_SLOT_INDEX_2
+
#ifdef KERNEL_MODE
bool tropic_init(void);
diff --git a/core/embed/upymod/modtrezorcrypto/modtrezorcrypto-tropic.h b/core/embed/upymod/modtrezorcrypto/modtrezorcrypto-tropic.h
index aa4ec05e..00fcac93 100644
--- a/core/embed/upymod/modtrezorcrypto/modtrezorcrypto-tropic.h
+++ b/core/embed/upymod/modtrezorcrypto/modtrezorcrypto-tropic.h
@@ -22,6 +22,8 @@
#include <sec/secret.h>
#include <sec/tropic.h>
+#include "ed25519-donna/ed25519.h"
+
/// package: trezorcrypto.tropic
/// class TropicError(Exception):
@@ -96,12 +98,9 @@ STATIC mp_obj_t mod_trezorcrypto_tropic_sign(mp_obj_t key_index,
mp_buffer_info_t dig = {0};
mp_get_buffer_raise(digest, &dig, MP_BUFFER_READ);
- if (dig.len != 32) {
- mp_raise_ValueError(MP_ERROR_TEXT("Invalid length of digest."));
- }
vstr_t sig = {0};
- vstr_init_len(&sig, ECDSA_RAW_SIGNATURE_SIZE);
+ vstr_init_len(&sig, sizeof(ed25519_signature));
bool ret = tropic_ecc_sign(idx, (const uint8_t *)dig.buf, dig.len,
((uint8_t *)sig.buf));
@@ -111,14 +110,19 @@ STATIC mp_obj_t mod_trezorcrypto_tropic_sign(mp_obj_t key_index,
MP_ERROR_TEXT("lt_ecc_eddsa_sign failed."));
}
- sig.len = ECDSA_RAW_SIGNATURE_SIZE;
+ sig.len = sizeof(ed25519_signature);
return mp_obj_new_str_from_vstr(&mp_type_bytes, &sig);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_tropic_sign_obj,
mod_trezorcrypto_tropic_sign);
+/// DEVICE_KEY_SLOT: int
+/// FIDO_KEY_SLOT: int
+
STATIC const mp_rom_map_elem_t mod_trezorcrypto_tropic_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_tropic)},
+ {MP_ROM_QSTR(MP_QSTR_DEVICE_KEY_SLOT), MP_ROM_INT(TROPIC_DEVICE_KEY_SLOT)},
+ {MP_ROM_QSTR(MP_QSTR_FIDO_KEY_SLOT), MP_ROM_INT(TROPIC_FIDO_KEY_SLOT)},
{MP_ROM_QSTR(MP_QSTR_ping), MP_ROM_PTR(&mod_trezorcrypto_tropic_ping_obj)},
{MP_ROM_QSTR(MP_QSTR_key_generate),
MP_ROM_PTR(&mod_trezorcrypto_tropic_key_generate_obj)},
diff --git a/core/mocks/generated/trezorcrypto/tropic.pyi b/core/mocks/generated/trezorcrypto/tropic.pyi
index 2616d55c..3dd84cb4 100644
--- a/core/mocks/generated/trezorcrypto/tropic.pyi
+++ b/core/mocks/generated/trezorcrypto/tropic.pyi
@@ -24,3 +24,5 @@ class TropicError(Exception):
"""
Uses the private key at key_index to produce a signature of the digest.
"""
+ DEVICE_KEY_SLOT: int
+ FIDO_KEY_SLOT: int
diff --git a/core/tests/test_trezor.crypto.tropic.py b/core/tests/test_trezor.crypto.tropic.py
index 9a4c7da7..341abfe1 100644
--- a/core/tests/test_trezor.crypto.tropic.py
+++ b/core/tests/test_trezor.crypto.tropic.py
@@ -15,8 +15,10 @@ class TestCryptoTropic(unittest.TestCase):
try:
tropic.sign(0, "ASD")
assert False
- except ValueError as e:
- self.assertIn("invalid length", str(e).lower())
+ except tropic.TropicError as e:
+ # key is not generated yet
+ self.assertIn("lt_ecc_eddsa_sign failed", str(e).lower())
+
tropic.key_generate(0)
Why this scored 15/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.