refactor(core): define Tropic factory keys in a single place
What changed, and why it matters
This change is a code cleanup, not a security fix. It moves three copies of the same factory private key into one shared function so the code is easier to maintain. The actual keys and how they are used do not change, and the commit message explicitly calls it a refactor with no changelog entry.
No immediate security action required. As a hardening follow-up, consider zeroizing the factory private key buffer after use and ensuring production builds cannot be accidentally compiled with TROPIC_TESTING_KEYS or emulator keys.
Security signals we found
Private key material is still compiled into firmware images (pre-existing condition, unchanged by this commit).
Factory key selection still depends on build-time macros (TREZOR_EMULATOR, TROPIC_TESTING_KEYS).
No new bounds checking or clearing of sensitive buffers is added; the output buffer is written via memcpy.
Refactor reduces duplication, which is a maintainability improvement but not a vulnerability remediation.
Evidence from the diff
The commit refactors Tropic secure-element factory key handling by introducing tropic_get_factory_privkey() in core/embed/sec/tropic/tropic.c and exposing it via core/embed/sec/tropic/inc/sec/tropic.h. It removes duplicate inline definitions from prodtest_tropic.c and tropic.c. The key material, conditional compilation (TREZOR_EMULATOR / TROPIC_TESTING_KEYS), and runtime behavior remain identical. No access controls, scope, or cryptographic usage are altered.
Changed components
core/embed/sec/tropic/tropic.ccore/embed/sec/tropic/inc/sec/tropic.hcore/embed/projects/prodtest/cmd/prodtest_tropic.cInspect captured patch +32 / −26
diff --git a/core/embed/projects/prodtest/cmd/prodtest_tropic.c b/core/embed/projects/prodtest/cmd/prodtest_tropic.c
index bdea6d3a0..f5ca6c615 100644
--- a/core/embed/projects/prodtest/cmd/prodtest_tropic.c
+++ b/core/embed/projects/prodtest/cmd/prodtest_tropic.c
@@ -831,26 +831,9 @@ cleanup:
}
bool prodtest_tropic_factory_session_start(lt_handle_t* tropic_handle) {
-#ifdef TREZOR_EMULATOR
- curve25519_key factory_private = {
- 0xf0, 0xc4, 0xaa, 0x04, 0x8f, 0x00, 0x13, 0xa0, 0x96, 0x84, 0xdf,
- 0x05, 0xe8, 0xa2, 0x2e, 0xf7, 0x21, 0x38, 0x98, 0x28, 0x2b, 0xa9,
- 0x43, 0x12, 0xf3, 0x13, 0xdf, 0x2d, 0xce, 0x8d, 0x41, 0x64};
-#else
-#ifdef TROPIC_TESTING_KEYS
- // Testing keys (used in TROPIC01-P2S-P001)
- curve25519_key factory_private = {
- 0xd0, 0x99, 0x92, 0xb1, 0xf1, 0x7a, 0xbc, 0x4d, 0xb9, 0x37, 0x17,
- 0x68, 0xa2, 0x7d, 0xa0, 0x5b, 0x18, 0xfa, 0xb8, 0x56, 0x13, 0xa7,
- 0x84, 0x2c, 0xa6, 0x4c, 0x79, 0x10, 0xf2, 0x2e, 0x71, 0x6b};
-#else
- // Production keys
- curve25519_key factory_private = {
- 0x28, 0x3f, 0x5a, 0x0f, 0xfc, 0x41, 0xcf, 0x50, 0x98, 0xa8, 0xe1,
- 0x7d, 0xb6, 0x37, 0x2c, 0x3c, 0xaa, 0xd1, 0xee, 0xee, 0xdf, 0x0f,
- 0x75, 0xbc, 0x3f, 0xbf, 0xcd, 0x9c, 0xab, 0x3d, 0xe9, 0x72};
-#endif
-#endif
+ curve25519_key factory_private = {0};
+ tropic_get_factory_privkey(factory_private);
+
curve25519_key factory_public = {0};
curve25519_scalarmult_basepoint(factory_public, factory_private);
diff --git a/core/embed/sec/tropic/inc/sec/tropic.h b/core/embed/sec/tropic/inc/sec/tropic.h
index ea92fef1b..caf407936 100644
--- a/core/embed/sec/tropic/inc/sec/tropic.h
+++ b/core/embed/sec/tropic/inc/sec/tropic.h
@@ -21,6 +21,8 @@
#include <trezor_types.h>
+#include "ed25519-donna/ed25519.h"
+
// FIDO attestation key and certificate.
#define TROPIC_FIDO_CERT_FIRST_SLOT 0
#define TROPIC_FIDO_CERT_SLOT_COUNT 3
@@ -55,6 +57,8 @@ lt_handle_t* tropic_get_handle(void);
#endif
+void tropic_get_factory_privkey(curve25519_key privkey);
+
bool tropic_ping(const uint8_t* msg_out, uint8_t* msg_in, uint16_t msg_len);
bool tropic_ecc_key_generate(uint16_t slot_index);
diff --git a/core/embed/sec/tropic/tropic.c b/core/embed/sec/tropic/tropic.c
index 09c5316ae..e73b4338a 100644
--- a/core/embed/sec/tropic/tropic.c
+++ b/core/embed/sec/tropic/tropic.c
@@ -93,13 +93,8 @@ bool tropic_init(void) {
#if !PRODUCTION
// Allow running with default factory keys in non-production fw
if (privkey_ok != sectrue) {
- static const curve25519_key factory_private = {
- 0x28, 0x3f, 0x5a, 0x0f, 0xfc, 0x41, 0xcf, 0x50, 0x98, 0xa8, 0xe1,
- 0x7d, 0xb6, 0x37, 0x2c, 0x3c, 0xaa, 0xd1, 0xee, 0xee, 0xdf, 0x0f,
- 0x75, 0xbc, 0x3f, 0xbf, 0xcd, 0x9c, 0xab, 0x3d, 0xe9, 0x72};
-
+ tropic_get_factory_privkey(trezor_privkey);
pairing_key_slot = TROPIC_FACTORY_PAIRING_KEY_SLOT;
- memcpy(trezor_privkey, factory_private, sizeof(trezor_privkey));
privkey_ok = sectrue;
}
@@ -236,6 +231,30 @@ static bool tropic_get_tropic_pubkey(lt_handle_t *handle,
}
#endif // !PRODUCTION
+void tropic_get_factory_privkey(curve25519_key privkey) {
+#ifdef TREZOR_EMULATOR
+ curve25519_key factory_private = {
+ 0xf0, 0xc4, 0xaa, 0x04, 0x8f, 0x00, 0x13, 0xa0, 0x96, 0x84, 0xdf,
+ 0x05, 0xe8, 0xa2, 0x2e, 0xf7, 0x21, 0x38, 0x98, 0x28, 0x2b, 0xa9,
+ 0x43, 0x12, 0xf3, 0x13, 0xdf, 0x2d, 0xce, 0x8d, 0x41, 0x64};
+#else
+#ifdef TROPIC_TESTING_KEYS
+ // Testing keys (used in TROPIC01-P2S-P001)
+ curve25519_key factory_private = {
+ 0xd0, 0x99, 0x92, 0xb1, 0xf1, 0x7a, 0xbc, 0x4d, 0xb9, 0x37, 0x17,
+ 0x68, 0xa2, 0x7d, 0xa0, 0x5b, 0x18, 0xfa, 0xb8, 0x56, 0x13, 0xa7,
+ 0x84, 0x2c, 0xa6, 0x4c, 0x79, 0x10, 0xf2, 0x2e, 0x71, 0x6b};
+#else
+ // Production keys
+ curve25519_key factory_private = {
+ 0x28, 0x3f, 0x5a, 0x0f, 0xfc, 0x41, 0xcf, 0x50, 0x98, 0xa8, 0xe1,
+ 0x7d, 0xb6, 0x37, 0x2c, 0x3c, 0xaa, 0xd1, 0xee, 0xee, 0xdf, 0x0f,
+ 0x75, 0xbc, 0x3f, 0xbf, 0xcd, 0x9c, 0xab, 0x3d, 0xe9, 0x72};
+#endif
+#endif
+ memcpy(privkey, factory_private, sizeof(curve25519_key));
+}
+
bool tropic_random_buffer(void *buffer, size_t length) {
tropic_driver_t *drv = &g_tropic_driver;
Why this scored 20/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.