feat(core): Add emulator ML-DSA certificates.
What changed, and why it matters
This commit adds hardcoded test-only cryptographic credentials used by the Trezor emulator (software simulator) for a new ML-DSA device-attestation feature. It does not change real hardware firmware. The credentials are clearly emulator-only placeholders and are not intended for production devices.
No immediate action required. Verify that USE_MCU_ATTESTATION is never enabled in production hardware builds and that these emulator certificates/keys are documented as test-only. Ensure the gen.sh script and placeholder values are not accidentally shipped in production firmware.
Security signals we found
Hardcoded cryptographic material (emulator-only certificate and fixed ML-DSA seed)
Fixed deterministic secret (memset(dest, 3, SHA256_DIGEST_LENGTH)) used for device authentication in emulator
New attestation read/write functions operate on a static in-memory buffer with bounds checks
Code is guarded by USE_MCU_ATTESTATION and TREZOR_MODEL_T3W1 macros, limiting scope
Evidence from the diff
The patch introduces emulator-side support for MCU attestation on the T3W1 model. It adds a DER certificate (T3W1.der), a generated C header containing a minimal 2-byte placeholder certificate, a shell script to regenerate headers from DER files, and read/write/size functions in the Unix emulator secret.c. It also moves the existing emulator-only secret_key_mcu_device_auth() function (which returns a fixed seed of all 0x03 bytes) so it is available whenever USE_MCU_ATTESTATION is defined, not only when USE_TROPIC is defined. These are test fixtures for the emulator build and are not used on real secure hardware.
Changed components
core/embed/sec/secret/unix/secret.ccore/embed/sec/secret_keys/unix/secret_keys.ccore/embed/sec/secret/unix/certs/T3W1.dercore/embed/sec/secret/unix/certs/T3W1.hcore/embed/sec/secret/unix/certs/gen.shInspect captured patch +61 / −6
diff --git a/core/embed/sec/secret/unix/certs/T3W1.der b/core/embed/sec/secret/unix/certs/T3W1.der
new file mode 100644
index 00000000..def7fcb5
Binary files /dev/null and b/core/embed/sec/secret/unix/certs/T3W1.der differ
diff --git a/core/embed/sec/secret/unix/certs/T3W1.h b/core/embed/sec/secret/unix/certs/T3W1.h
new file mode 100644
index 00000000..352a44bf
--- /dev/null
+++ b/core/embed/sec/secret/unix/certs/T3W1.h
@@ -0,0 +1,4 @@
+// This file was generated via ./gen.sh
+
+static uint8_t mcu_device_cert[MCU_ATTESTATION_MAX_CERT_SIZE] = {0x30, 0x00};
+static size_t mcu_device_cert_size = 2;
diff --git a/core/embed/sec/secret/unix/certs/gen.sh b/core/embed/sec/secret/unix/certs/gen.sh
new file mode 100755
index 00000000..0f8486ca
--- /dev/null
+++ b/core/embed/sec/secret/unix/certs/gen.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+cd "$(dirname "$0")"
+for file in *.der
+do
+ header_file="${file%.der}.h"
+ echo "// This file was generated via ./gen.sh" > "$header_file"
+ echo >> "$header_file"
+ xxd -i "$file" | sed '
+ s/unsigned.*_der\[\]/static uint8_t mcu_device_cert\[MCU_ATTESTATION_MAX_CERT_SIZE\]/;
+ s/unsigned.*_len/static size_t mcu_device_cert_size/' >> "$header_file"
+ clang-format -i "$header_file"
+done
diff --git a/core/embed/sec/secret/unix/secret.c b/core/embed/sec/secret/unix/secret.c
index c7c943b7..ad6e1d58 100644
--- a/core/embed/sec/secret/unix/secret.c
+++ b/core/embed/sec/secret/unix/secret.c
@@ -194,3 +194,39 @@ secbool secret_lock(void) {
void secret_bhk_regenerate(void) {}
#endif // KERNEL_MODE
+
+#ifdef USE_MCU_ATTESTATION
+
+#include <sec/mcu_attestation.h>
+
+#if defined(TREZOR_MODEL_T3W1)
+#include "certs/T3W1.h"
+#else
+#error "MCU attestation is only supported for T3W1 model."
+#endif
+
+secbool secret_mcu_device_cert_write(const uint8_t* cert, size_t cert_size) {
+ if (cert_size > MCU_ATTESTATION_MAX_CERT_SIZE) {
+ return secfalse;
+ }
+ memcpy(mcu_device_cert, cert, cert_size);
+ mcu_device_cert_size = cert_size;
+ return sectrue;
+}
+
+secbool secret_mcu_device_cert_size(size_t* cert_size) {
+ *cert_size = mcu_device_cert_size;
+ return sectrue;
+}
+
+secbool secret_mcu_device_cert_read(uint8_t* cert, size_t max_cert_size,
+ size_t* cert_size) {
+ if (mcu_device_cert_size > max_cert_size) {
+ return secfalse;
+ }
+ *cert_size = mcu_device_cert_size;
+ memcpy(cert, mcu_device_cert, *cert_size);
+ return sectrue;
+}
+
+#endif // USE_MCU_ATTESTATION
diff --git a/core/embed/sec/secret_keys/unix/secret_keys.c b/core/embed/sec/secret_keys/unix/secret_keys.c
index 30e44e63..5061db4f 100644
--- a/core/embed/sec/secret_keys/unix/secret_keys.c
+++ b/core/embed/sec/secret_keys/unix/secret_keys.c
@@ -26,6 +26,14 @@
#include <sec/secret_keys.h>
#include "../secret_keys_common.h"
+#ifdef USE_MCU_ATTESTATION
+secbool secret_key_mcu_device_auth(uint8_t dest[MLDSA_SEEDBYTES]) {
+ _Static_assert(MLDSA_SEEDBYTES == SHA256_DIGEST_LENGTH);
+ memset(dest, 3, SHA256_DIGEST_LENGTH);
+ return sectrue;
+}
+#endif // USE_MCU_ATTESTATION
+
#ifdef USE_TROPIC
static uint8_t SECRET_TROPIC_PAIRING_BYTES[] = {
@@ -44,12 +52,6 @@ _Static_assert(sizeof(SECRET_TROPIC_PAIRING_BYTES) == sizeof(curve25519_key),
_Static_assert(sizeof(SECRET_TROPIC_PUBKEY_BYTES) == sizeof(curve25519_key),
"Invalid size of Tropic public key");
-secbool secret_key_mcu_device_auth(uint8_t dest[MLDSA_SEEDBYTES]) {
- _Static_assert(MLDSA_SEEDBYTES == SHA256_DIGEST_LENGTH);
- memset(dest, 3, SHA256_DIGEST_LENGTH);
- return sectrue;
-}
-
secbool secret_key_tropic_public(curve25519_key dest) {
memcpy(dest, SECRET_TROPIC_PUBKEY_BYTES, sizeof(curve25519_key));
return sectrue;
Why this scored 18/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.