|
SDK for TESAIoT Dev Kit
API reference & tutorials (ModusToolbox)
|
Where the private key lives (inside the Trust M, in a slot it never leaves), how a TLS handshake on CM33_NS reaches it (PSA opaque key → secure-element driver → trustm_ecdsa_sign), and what is patched in the ModusToolbox tree to make that possible — and how to apply the eleven patches that ship with this package and prove they went in correctly.
Two pairs and a CA:
| OID | Contents | Used by |
|---|---|---|
| 0xE0E0 / 0xE0F0 | Infineon factory certificate / key | this chapter — the TLS identity |
| 0xE0E1 / 0xE0F1 | TESAIoT device certificate / key | enrolment and Protected Update — chapter D2 |
| 0xE0E9 | TESA CA (trust anchor for the device pair) | D2 |
mqtt_mtls_setup_optiga() now chooses for itself. It calls optiga_verify_cert_key_pair(0xE0E1, 0xE0F1) first: if the TESAIoT certificate and key are the same identity, that pair is used; otherwise it falls back to the factory pair 0xE0E0 / 0xE0F0.
That check answers the question the old fallback could not — are these two the same identity — and is safe on an unenrolled board, because it asks the chip a bounded question rather than reading a slot that may never return, which is what made the previous attempt hang.
Order matters.** The check must come after optiga_trust_init(). Called with the OPTIGA application closed, the read spins in a bare while (status == BUSY) with no timeout and CM33_NS never comes back — measured 2026-08-31 by placing it too early.
From a Dev Kit that day:
What the factory certificate proves, and what it does not.** The certificate in 0xE0E0 has subject CN=InfineonIoTNode, and that subject is the same on every OPTIGA Trust M part (decoded from a real board's board-factory-cert-cd16334d.pem with openssl x509, 2026-08-29: issuer Infineon OPTIGA(TM) Trust M CA 300, valid 2025-10-21 to 2045-10-21, no SAN and no device-identifying field). The only per-part fields are the serial number and the public key.
So the handshake proves the peer holds a genuine Infineon Trust M — not which device it is. Meanwhile the MQTT client id in mTLS mode comes from cfg.factory_uid and the subscribed command topic from cfg.device_id (mqtt_client_config.c:143-144, subscriber_task.c:92-93), and both are free-form strings settable from MicroPython with tesaiot.config_set(...) and persisted (tesaiot_config_store.c:174-176).
State the consequence exactly: at the firmware level nothing binds the identity a device asserts to the certificate it presents. The decisive control is broker-side — an ACL that grants device/{id}/# only to the client whose certificate is pinned to that id, pinned by public-key fingerprint or certificate serial, never by subject, which is identical across parts. Verify that ACL before putting the chapter D2 enrolment recipe into production use.
mqtt_client_config_init() (chapter C3) calls mqtt_mtls_setup_optiga() when tls_mode == TESAIOT_MODE_MTLS. That function (mqtt_mtls_setup.c:61-310):
During the TLS handshake the patched cy_tls.c builds an opaque mbedtls_pk from the key id and installs it with mbedtls_ssl_conf_own_cert() (cy_tls.c:1755-1809 in the patched secure-sockets — see the caveat below). When the server asks for CertificateVerify, mbedTLS calls PSA, PSA dispatches to the registered SE driver, and the driver's optiga_psa_sign() (optiga_psa_se.c:315, registered at :151 and :235-236) calls trustm_ecdsa_sign(map->oid, hash, …). That function takes optiga_manager_lock() then optiga_manager_touch_hold() for the whole transaction (optiga_trust_helpers.c:1665, :1673) — chapter D1's discipline, exercised on every connect.
SHA-384 suites: the driver truncates the hash to its leftmost 256 bits (optiga_psa_se.c:332-343); that is what makes PSA_ALG_ANY_HASH work.
The cy_tls ↔ OPTIGA binding is not in the template — it lives in mtb_shared/, a third-party tree. So this package ships the whole of third_party_patches/: eleven diffs across five assets, a series file giving the order they apply in, and PATCHED.sha256 — eleven file paths under mtb_shared/ with the SHA-256 each must have, including secure-sockets/…/cy_tls.c, cy_tls_optiga_key.c and cy_tls_optiga_key.h.
Shipping the diffs was settled on 2026-08-28, on the practice Buildroot follows for packages it marks non-redistributable and that Infineon's own meta-freescale layer follows against EULA'd NXP sources: a patch carries the licence of the work it patches, and a package the customer cannot build is not a release.
You applied those patches in chapter A1/A2's patch step and verified them with shasum -a 256 -c PATCHED.sha256. If that check did not pass, mTLS cannot work: cy_tls_set_optiga_key_id() will not exist and the build fails at link — or, with an unpatched cy_tls.c that happens to link, the handshake will use no client certificate and the broker will reject it.
What you should observe. Every line OK. Any FAILED means the tree is not the tree this firmware was built for; stop here.
Set tls_mode=0 in /.tesaiot_config (chapter C3). Leave port= alone — it is unused; the mode selects 8883.
What you should observe. Nothing yet.
tesaiot.connect() or the page's Connect button.
What you should observe on the UART, live, in order:
Client= in the [MQTT-Config] line is now the factory UID, not device_id.
The [PSA-Sign] line (optiga_psa_se.c:364-365) deserves precision. It prints before the signature is attempted — it marks "sign attempted with a resolved OID", not "sign succeeded". Success additionally requires the absence of the ERROR line that follows it in the same function:
(optiga_psa_se.c:372). The pair — the Using Key OID line present, the ERROR: trustm_ecdsa_sign line absent, then [MQTT] Connected to broker — is the success signal. Any of the other four [PSA-Sign] ERROR forms (:328, :346, :352, :359) means the driver never reached the chip.
On screen: the TESAIoT page shows connected, and in this mode also optiga_state = PROVISIONED, cert_state = LOADED (tesaiot_mqtt.c:60-64).
If the patched secure-sockets was built with TLS_DIAG defined (cy_tls.c:168), two more lines appear between setup and the sign: [TLS-DIAG] optiga_key=lu cert_ready=d and [TLS-DIAG] mbedtls_pk_setup_opaque = d (-0x%04X). They live in the patched code and are off by default — useful if you turn them on, not required for the verification above.
Disconnect the Trust M's I2C (or, on a board with the debug header, hold the chip in reset) and reconnect.
What you should observe. [mTLS] Certificate read never appears; instead one of [mTLS] OPTIGA application would not open — aborting mTLS setup, [mTLS] optiga_manager_init failed — signing would be impossible, or [mTLS] No usable certificate in OID 0x%04X (got u bytes) (:182, :208, :223), followed by [MQTT-Config] mTLS OPTIGA setup failed and [MQTT] Configuration failed — not attempting to connect. No private key exists anywhere in flash to fall back to. That is the point.
Disconnect from the broker and connect again without rebooting.
What you should observe. [mTLS] Re-binding existing OPTIGA key (key_id=lu, cert=u bytes) (:107) — or, if the teardown wiped the PSA key, [mTLS] PSA key lu no longer exists — the TLS teardown wiped … (:95) followed by a full setup. Both are normal; the second is why step 5 of the setup re-registers the driver every time.
libbento_hsm.a needs zero MicroPython symbols (variants/mtb-only.mk:14); mqtt_mtls_setup.c, optiga_psa_se.c and optiga_trust_helpers.c compile on both variants. The only mtb-mpy-specific element in this chapter is the tesaiot.connect() trigger. The PSA driver registration order at boot — optiga_psa_register() then psa_crypto_init() — is in proj_cm33_ns/main.c:228-235 on both; its failure lines [BOOT] optiga_psa_register failed: d / [BOOT] psa_crypto_init failed: d are live, and success is silent.