Skip to content

PSoC Edge E84 with OPTIGA™ Trust M: keys that never leave the chip

  1. Explain the provisioning workflow that creates and keeps the private key inside OPTIGA Trust M
  2. Connect MQTT over TLS with the certificate stored in OPTIGA, following the example

What OPTIGA Trust M is, and why the key never leaves the chip

Section titled “What OPTIGA Trust M is, and why the key never leaves the chip”

OPTIGA™ Trust M is a secure element (a separate security chip, CC EAL6+ certified) connected to the PSoC Edge E84 over I2C. This chip generates and keeps ECC P-256 keypairs entirely inside itself: the firmware can ask the chip to “sign data”, but there is no way to ever read the private key back out — not even by dumping the MCU’s memory — because the key never enters the MCU’s RAM in the first place. The rest of TLS, such as encrypting data in transit, is still done by mbedTLS on the MCU as usual; only the step that needs the private key (signing during the TLS handshake) is handed off to the chip.

OIDs: the map of keys and certificates inside the chip

Section titled “OIDs: the map of keys and certificates inside the chip”

OPTIGA stores data in numbered slots called OIDs. This example uses these main ones: 0xE0C2 holds the Factory UID (the hardware’s identity number, read-only, 27 bytes); 0xE0E0 holds the Factory Certificate (provisioned at the factory), paired with the key at 0xE0F0; 0xE0E1 holds the Device Certificate (issued by the TESAIoT Platform through Protected Update), paired with the key at 0xE0F1; and 0xE0E3 holds the trust anchor (ROOT_CA) used to verify Protected Update signatures. Each certificate must always be used with the key at its matching OID (0xE0E0↔0xE0F0, 0xE0E1↔0xE0F1) — pairing them wrongly signs the TLS handshake with a key that does not match the certificate being presented.

Access conditions: the key cannot be read, but it can be told to sign

Section titled “Access conditions: the key cannot be read, but it can be told to sign”

The OID Access Conditions table in the upstream documentation states clearly that the key slots 0xE0F0 and 0xE0F1 have Read = Never (never readable, under any circumstance) but Execute = Always (always usable to sign, for example during a TLS handshake). The certificate slot 0xE0E0 is Change = Never (unwritable again after provisioning), while 0xE0E1 can be rewritten (Change = Always*), though changing the metadata’s own access rules is locked once the lifecycle state (LcsO) reaches Operational (0x07). This “usable but unreadable” property is the heart of the “hardware root of trust”: even if the firmware has a vulnerability or is reached through a debugger, the key still cannot be copied out — unlike a key stored in the MCU’s flash, which a debugger or a firmware bug can read out.

Two-Certificate PKI and SAFE MODE: why there are two certificates

Section titled “Two-Certificate PKI and SAFE MODE: why there are two certificates”

This example uses two paired certificates: the Factory Certificate (0xE0E0+0xE0F0, provisioned at the factory, used to bootstrap or recover) and the Device Certificate (0xE0E1+0xE0F1, issued by the TESAIoT Platform through Protected Update, used in normal operation). Every time the device powers on or resets, the firmware enters “SAFE MODE” by defaulting g_force_factory_cert = true, forcing it to use the Factory Certificate first. The upstream README gives the reason: the Device Certificate (0xE0E1) may not match the Device Key (0xE0F1) after a reset, because the pairing of a freshly provisioned key stays in temporary memory until its certificate has been successfully written, while the Factory Certificate and Factory Key are always correctly paired. Starting with the Factory Certificate therefore guarantees the MQTT connection works for recovery. The user then chooses to run Protected Update to switch over to the Device Certificate.

The order of steps for obtaining a usable certificate (the Protected Update workflow)

Section titled “The order of steps for obtaining a usable certificate (the Protected Update workflow)”

The order for obtaining the Device Certificate is: (1) OPTIGA generates an ECC P-256 keypair inside the chip itself (at OID 0xE0F1); (2) it creates a CSR (Certificate Signing Request) signed with that in-chip key; (3) it connects to MQTT with the Factory Certificate first (per SAFE MODE), then sends the CSR to the platform; (4) the platform issues a certificate and sends back a signed Protected Update manifest; (5) OPTIGA checks the manifest’s signature against the trust anchor at 0xE0E3 first — if the signature does not match, it refuses to write it; if it matches, it writes the new certificate to OID 0xE0E1. Throughout this process, the private key at 0xE0F1 never leaves the chip — only the CSR (which carries the public key) and the certificate ever travel over the network. Checking the manifest’s signature before every write is what causes a forged update to be rejected.

Connecting MQTT over TLS with a key inside OPTIGA: binding TLS to the secure element at boot

Section titled “Connecting MQTT over TLS with a key inside OPTIGA: binding TLS to the secure element at boot”

At startup, the firmware reads the current certificate from OPTIGA (read_certificate_from_optiga()), then registers OPTIGA’s secure-element driver with PSA Crypto (optiga_psa_register(), followed by psa_crypto_init()). It then creates a PSA key handle located at PSA_KEY_LOCATION_OPTIGA through psa_generate_key() — “generate” here means attaching a PSA handle to an OID that already holds a key from the factory; it does not create a new key at this step. It then binds the TLS layer to that key with cy_tls_set_optiga_key_id(), and sets the TLS client certificate with cy_tls_set_client_cert(). When connecting to the platform’s MQTT broker (port 8883 when mutual-auth is on), during the CertificateVerify step of the TLS handshake, mbedTLS calls back into this driver to sign a hash with the key at whichever OID is selected (0xE0F0 or 0xE0F1, depending on which certificate is in use). The result is an ECDSA signature handed back to mbedTLS for the next handshake packet — with no byte of the key itself ever flowing through the MCU’s RAM.

Before you run the example (checked 26 Sep 2026): at this commit the example’s mqtt_client_config.h still sets MQTT_BROKER_ADDRESS and MQTT_SNI_HOSTNAME to the platform’s former MQTT name ending in .com. That name still works for now, but the platform has moved to tesaiot.dev: set both to mqtt.tesaiot.dev (tracked in developer-hub issue #3).

This example’s code lives on the Developer Hub (pinned to commit d2ed42c) — read the example README for the full OID table, the two-certificate PKI, and SAFE MODE, then follow the code in this order (runs on the PSoC Edge E84 board with the OPTIGA™ Trust M; this project has the Eva Kit BSP: APP_KIT_PSE84_EVAL_EPC2).

  • main.c — the boot-time sequence: read the certificate from OPTIGA, register the PSA secure-element driver, bind TLS to the key inside OPTIGA
  • optiga_psa_se.c — the sign function (optiga_psa_sign) that mbedTLS calls during the TLS handshake, asking OPTIGA to sign a hash with the key at the selected OID
  • optiga_trust_helpers.c — trustm_gen_ecc_keypair(), which calls optiga_crypt_ecc_generate_keypair() with export_private=false, the part that keeps a key generated in the chip from ever being exported
  • mqtt_task.c — selecting the certificate (tesaiot_select_mqtt_certificate()) and then switching the signing key’s OID to match the selected certificate before every connection
  • See the full folder at examples/security/pse84_tesaiot_client/
  • You need device credentials from the TESAIoT Platform, following the steps in the README. Never commit real credentials to a public repository.

This code is under the Cypress (Infineon) EULA, so it is referenced by link only — no code is copied into this lesson.

  • Assuming this example’s code generates a new key every time the board boots — psa_generate_key() here only attaches a PSA key handle to an OID that already holds a key from the factory (OPTIGA_TLS_ATTACH_ONLY); it does not create a new key each time. The actual key is created only once, either during provisioning (for the Factory Key) or during the Protected Update workflow (for the Device Key).
  • Pairing a certificate with a key at the wrong OID — certificate 0xE0E0 must only be paired with key 0xE0F0, and 0xE0E1 only with 0xE0F1. The code in mqtt_task.c therefore switches optiga_psa_set_signing_key_oid() to match whichever certificate is selected, every time, before connecting. Forgetting to switch it means the TLS handshake is signed with a key that does not match the certificate.
  • Assuming a board reset gets you the Device Certificate right away — every reset returns the firmware to SAFE MODE (using the Factory Certificate) first. Protected Update has to be run again each time you want to switch to the Device Certificate.
  • Rerunning make getlibs and forgetting ./apply_patches.sh — the upstream README warns that the patches get wiped along with the library; secure-sockets can no longer use the key inside OPTIGA during the TLS handshake. ./apply_patches.sh must be rerun every time after make getlibs.
  1. Guess before you change anything: pick one value the example’s README explains in the How section, and write down what you expect to change on the screen or in the log.
  2. Change and run: build + flash, then compare against your guess. If it does not match, find which part you misunderstood.
  3. Extend: add one thing the example does not yet have, and keep a photo or video in your portfolio.
  • Why is a private key inside a secure element safer than one in flash?
  • What does Protected Update do with the certificate?

The answers are in the example’s README and in the code. If you cannot answer one, go back and read the Why / What / How section again.

Review questions

Answer on your own first, then open the answer.

  1. Put in order the steps for obtaining a device certificate with OPTIGA Trust M and the TESAIoT Platform. (Objective 1)

    1. แพลตฟอร์มออกใบรับรองแล้วส่ง manifest ของ Protected Update ที่ลงนามแล้วกลับมา
    2. OPTIGA สร้างคู่กุญแจ ECC P-256 ภายในชิป (เช่นที่ OID 0xE0F1)
    3. OPTIGA ตรวจลายเซ็นของ manifest ด้วย trust anchor แล้วเขียนใบรับรองใหม่ลง OID 0xE0E1
    4. ส่ง CSR ขึ้นแพลตฟอร์มผ่าน MQTT โดยเชื่อมต่อด้วย factory certificate
    5. สร้าง CSR ที่ลงนามด้วยกุญแจในชิป
    Show answer

    Correct order: B. OPTIGA สร้างคู่กุญแจ ECC P-256 ภายในชิป (เช่นที่ OID 0xE0F1) → E. สร้าง CSR ที่ลงนามด้วยกุญแจในชิป → D. ส่ง CSR ขึ้นแพลตฟอร์มผ่าน MQTT โดยเชื่อมต่อด้วย factory certificate → A. แพลตฟอร์มออกใบรับรองแล้วส่ง manifest ของ Protected Update ที่ลงนามแล้วกลับมา → C. OPTIGA ตรวจลายเซ็นของ manifest ด้วย trust anchor แล้วเขียนใบรับรองใหม่ลง OID 0xE0E1

    tesaiot_optiga.h มี tesaiot_optiga_generate_keypair() และ tesaiot_optiga_generate_csr() และ state ของ workflow ไล่เป็น PUBLISHING_CSR → WAITING_FOR_MANIFEST → VERIFYING_MANIFEST → APPLYING_FRAGMENTS → PROTECTED_UPDATE_SUCCESS ตลอดกระบวนการ private key ไม่ออกจากชิป ส่วนการเชื่อมต่อช่วงแรกใช้ factory certificate (SAFE MODE)

  2. Why is a private key inside OPTIGA Trust M safer than one stored in the microcontroller's flash? (Objective 1)

    1. เพราะ OPTIGA เข้ารหัสกุญแจด้วยรหัสผ่านของผู้ใช้
    2. เพราะกุญแจถูกสร้างในชิปและตั้งสิทธิ์ read = Never, execute = Always เฟิร์มแวร์สั่งให้ชิปลงนามระหว่าง TLS ได้ แต่ไม่มีใครอ่านตัวกุญแจออกมาได้ แม้จะ dump หน่วยความจำของ MCU
    3. เพราะ OPTIGA ทำ TLS แทน MCU ทั้งหมด
    4. เพราะ flash ของ MCU ลบตัวเองเมื่อถูกอ่าน
    Show answer

    Answer: B. เพราะกุญแจถูกสร้างในชิปและตั้งสิทธิ์ read = Never, execute = Always เฟิร์มแวร์สั่งให้ชิปลงนามระหว่าง TLS ได้ แต่ไม่มีใครอ่านตัวกุญแจออกมาได้ แม้จะ dump หน่วยความจำของ MCU

    ตาราง OID Access Conditions ใน README แสดงว่า 0xE0F0 และ 0xE0F1 อ่านไม่ได้ (Never) แต่ใช้ลงนามได้ (Always) กุญแจใน flash ถูกคัดลอกได้ด้วย debugger หรือช่องโหว่ในเฟิร์มแวร์ ส่วนกุญแจใน secure element ถูกใช้ได้แต่ถูกขโมยไม่ได้ งานส่วนอื่นของ TLS เช่นการเข้ารหัสข้อมูลยังทำใน mbedTLS บน MCU

  3. What is Protected Update used for with certificates? (Objective 1)

    1. อัปเดตเฟิร์มแวร์แอปพลิเคชันทั้งก้อน
    2. เข้ารหัส telemetry ก่อนส่ง
    3. เขียนใบรับรองใหม่หรือที่ต่ออายุลงในชิปอย่างปลอดภัย: ข้อมูลมาพร้อม manifest ที่แพลตฟอร์มลงนาม OPTIGA ตรวจลายเซ็นกับ trust anchor ก่อนยอมเขียน การอัปเดตปลอมจึงถูกปฏิเสธ
    4. รีเซ็ต factory certificate ให้เป็นค่าใหม่
    Show answer

    Answer: C. เขียนใบรับรองใหม่หรือที่ต่ออายุลงในชิปอย่างปลอดภัย: ข้อมูลมาพร้อม manifest ที่แพลตฟอร์มลงนาม OPTIGA ตรวจลายเซ็นกับ trust anchor ก่อนยอมเขียน การอัปเดตปลอมจึงถูกปฏิเสธ

    README อธิบายว่า Protected Update รับใบรับรองที่ลงนามผ่านการตรวจ manifest และใช้ OID 0xE0E3 เป็น trust anchor สำหรับตรวจลายเซ็น ใช้ได้ทั้งตอนเริ่มใช้งาน ก่อนใบรับรองหมดอายุ และหลังเหตุกุญแจรั่ว ส่วน factory certificate (0xE0E0) เปลี่ยนไม่ได้ (Change = Never)

  4. After a board reset, which certificate does the first MQTT test use? (Objective 2)

    1. factory certificate (0xE0E0) คู่กับกุญแจ 0xE0F0 เพราะ SAFE MODE (g_force_factory_cert = true) กันไม่ให้ใช้ใบรับรองกับกุญแจที่อาจไม่ตรงกัน จนกว่าจะทำ Protected Update
    2. device certificate (0xE0E1) เสมอ
    3. ไม่มีใบรับรอง ต้อง provision ใหม่ทุกครั้ง
    4. ใบรับรองที่เก็บใน flash ของ MCU
    Show answer

    Answer: A. factory certificate (0xE0E0) คู่กับกุญแจ 0xE0F0 เพราะ SAFE MODE (g_force_factory_cert = true) กันไม่ให้ใช้ใบรับรองกับกุญแจที่อาจไม่ตรงกัน จนกว่าจะทำ Protected Update

    README อธิบายว่าหลังรีเซ็ต device certificate อาจไม่ตรงกับ device key ส่วน factory certificate กับ factory key ถูกจับคู่ไว้ถาวร การเชื่อมต่อจึงใช้ได้เสมอเพื่อกู้ระบบ หลัง Protected Update สำเร็จ ระบบจึงสลับไปใช้ 0xE0E1 + 0xE0F1 จนกว่าจะรีเซ็ตครั้งถัดไป

  5. You rerun make getlibs and build without running ./apply_patches.sh. What happens? (Objective 2)

    1. ไม่มีผล เพราะ patch อยู่ในโค้ดของโปรเจกต์แล้ว
    2. บอร์ดใช้กุญแจซอฟต์แวร์แทนโดยอัตโนมัติ
    3. Wi-Fi ใช้ไม่ได้แต่ TLS ใช้ได้
    4. patch ถูกล้างไปพร้อม library secure-sockets จะใช้กุญแจใน OPTIGA ระหว่าง TLS handshake ไม่ได้ และการแก้ของ mbedTLS กับ lwIP ก็หายไป build หรือการเชื่อมต่อ TLS จะล้ม
    Show answer

    Answer: D. patch ถูกล้างไปพร้อม library secure-sockets จะใช้กุญแจใน OPTIGA ระหว่าง TLS handshake ไม่ได้ และการแก้ของ mbedTLS กับ lwIP ก็หายไป build หรือการเชื่อมต่อ TLS จะล้ม

    README เขียนว่า If you run make getlibs again, patches will be reset. Always re-run ./apply_patches.sh โดย patch ของ secure-sockets ทำให้ handshake ใช้กุญแจฮาร์ดแวร์ ifx-mbedtls เพิ่ม PSA และ mbedtls_ms_time() และ lwipopts.h แก้ deadlock ตอน publish

Cite this lesson

If you teach from this lesson or reuse it in slides or documents, credit it with the text below. If you changed it, add (adapted) after the title.

"PSoC Edge E84 with OPTIGA™ Trust M: keys that never leave the chip" from TESA Open Knowledge by the Thai Embedded Systems Association (TESA), https://github.com/tesaiot/tesa-qualification-program, licensed under CC BY-NC 4.0

Thai attribution: "PSoC Edge E84 กับ OPTIGA™ Trust M: กุญแจที่ไม่ออกจากชิป" จาก TESA Open Knowledge โดยสมาคมสมองกลฝังตัวไทย (Thai Embedded Systems Association: TESA) https://github.com/tesaiot/tesa-qualification-program สัญญาอนุญาต CC BY-NC 4.0

Lesson link: https://tesaiot.github.io/tesa-qualification-program/en/courses/tesaiot-firmware-stack/m05-connect-to-platform/l03-optiga-mqtt-client/

This lesson adapts the source below; keep its credit too.
https://github.com/tesaiot/developer-hub/blob/d2ed42c4a31232f553b6b8cef9ee7373db348c21/examples/security/pse84_tesaiot_client · Code stays in the Developer Hub and is linked at pinned commits, never copied: the episodes, practice codes and main-branch examples are Apache-2.0; the master template and the OPTIGA client carry Infineon/Cypress EULAs.

Full guide: how to cite TESA

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · CC BY-NC 4.0

Content is licensed CC BY-NC 4.0. Reuse it non-commercially and credit the Thai Embedded Systems Association (TESA) every time. · How to cite TESA