- IoT & embedded
- Cybersecurity
- Development & tooling
Secure IoT telemetry
MQTT telemetry hardened in three stages, up to mutual TLS with each device’s private key in an ATECC608B secure element
Context
SecureLink IoT was a three-student project on connected-device security, run as SAÉ 5.12 at IUT de Blagnac (an SAÉ is a project-based module of the BUT degree). I led the team and owned the MQTT workstream; my two teammates covered LoRa with a secure element and Wi-Fi and Bluetooth attacks.
The goal of my part: sensor telemetry that stays confidential on the network and that only a genuine device can publish, even if an attacker gets hold of its firmware. I reached it in three stages, each one closing the weakness left by the previous one, and proved every stage with captures and tests.
The whole project is documented on a website I built by hand, with interactive packet analysis, an attack simulation, a STRIDE analysis per stage and the complete reproduction procedure.
Architecture
From sensor to screen, through an authenticating broker
Three-stage hardening path
Each stage closes the weakness of the previous one, and each claim is backed by evidence.
Stage 1 Port 1883
Plain MQTT
No confidentiality, no authentication
The M5Go publishes a JSON payload (temperature, humidity, pressure) every five seconds over plain TCP. Any client can connect with any client ID: a passive capture reads the topic and every value, and an active attacker can alter readings or take over the session with a duplicate client ID.
Evidence Wireshark: the topic m5go/env and the JSON payload are readable in the capture.
STRIDE
- Spoofing: exposed
- Tampering: exposed
- Repudiation: exposed
- Information disclosure: exposed
- Denial of service: exposed
- Elevation of privilege: exposed
Stage 2 TLS 1.2 · port 8883
Mutual TLS, key in the firmware
Channel secured, identity still copyable
I built a private PKI with OpenSSL (an EC P-256 certificate authority, a broker certificate and one client certificate per device) and switched Mosquitto to mutual TLS: no anonymous clients, a certificate signed by the CA required, and its common name used as the MQTT username. Traffic is now encrypted and authenticated both ways. But the private key is compiled into the firmware: with a USB-to-UART adapter and esptool, a firmware dump reveals it and clones the device’s identity.
Evidence Wireshark: only TLS 1.2 application data; following the TCP stream shows ciphertext.
STRIDE
- Spoofing: exposed
- Tampering: covered
- Repudiation: exposed
- Information disclosure: covered
- Denial of service: covered
- Elevation of privilege: exposed
Stage 3 ATECC608B · slot 8
Mutual TLS, key in the secure element
Key out of the firmware, identity bound to the chip
Each device’s key is converted to PKCS#8 DER, prefixed with its length and written into data slot 8 of an ATECC608B (416 bytes, 13 blocks of 32) by a provisioning sketch; the firmware no longer contains it. At boot, the ESP32 checks the chip at I²C address 0x35, reads the slot, rebuilds the PEM key and hands it to the TLS stack. Without the chip the device stops; with another chip, the broker rejects the handshake.
Evidence Broker log: “bad signature” with the wrong secure element; with the right one, mutual TLS and the subscription succeed.
STRIDE
- Spoofing: mitigated, with a caveat
- Tampering: covered
- Repudiation: mitigated, with a caveat
- Information disclosure: covered
- Denial of service: covered
- Elevation of privilege: mitigated, with a caveat
Tested on the bench
The final stage was checked against its failure modes, not only the happy path.
-
Secure element removed
The firmware finds no chip at 0x35 and stops before any network connection.
-
Wrong secure element
The handshake signature does not match the certificate: the broker answers “bad signature” and closes the connection.
-
Genuine hardware
Mutual TLS succeeds; the broker checks the certificate and acknowledges the subscription to m5go/env.
-
Publisher and subscriber together
Readings published every five seconds appear on the subscriber’s screen through the encrypted channel.
What I did
- Led the project: split the work into three packs, planned and tracked it in ClickUp (Gantt and Kanban), and re-planned twice when the deadlines changed.
- Built the telemetry chain: an M5Go publisher with an ENV II unit (SHT30 and BMP280), a Mosquitto broker and an M5Stack subscriber that displays the readings.
- Set up the PKI with OpenSSL: an EC P-256 certificate authority, a broker certificate and one client certificate per device (m5go-pub and m5sub-display).
- Configured Mosquitto for mutual TLS on port 8883: anonymous clients refused, a client certificate required, its common name used as the MQTT username.
- Wrote the diagnostic, provisioning and client sketches (C++ on Arduino, SparkFun ATECCX08a library) that store each key in slot 8 and rebuild it at boot.
- Proved each stage with Wireshark captures, a STRIDE analysis and failure tests (chip removed, wrong chip).
- Built the project website by hand, and helped my teammates integrate the secure element into their LoRa link.
Delivered
- The project website, used as the final report: one interactive page per stage, a packet analyser and an attack simulation
- A STRIDE threat analysis for each stage
- A step-by-step reproduction procedure, from creating the certificate authority to flashing the devices
- The complete source code: seven Arduino sketches in C++ and the broker configuration
- A presentation video and slides
Leading the project
Besides my own workstream, I led the three-person team, from the first plan in October 2025 to the final report in January 2026.
Three work packs, one per member
-
Wireless attacks and LoRa transport
Flipper Zero and M5StickC Plus2 compared through Wi-Fi and Bluetooth tests, then the LoRa transport layer.
-
Secure element and LoRa encryption
The ATECC608B studied in depth (physical protections, random number generator, I²C), then used to encrypt and sign LoRa messages.
-
MQTT, in three stages (mine)
Clear text, then mutual TLS with a private PKI, then the private key in the secure element.
A plan that changed twice
-
October 2025
Rotating packs
Each member was to rotate through the three hardware kits every two weeks, to work on every part of the project.
-
Deadline brought forward to 5 December
Specialisation
Much less time: rotations dropped, each member kept one pack to the end, to go further in less time.
-
Final report moved to 23 January
MVP first, documentation after
The 5 December presentation focused on a working MVP; the extra weeks went into the report and the website.
Tools and decisions
- ClickUp for the plan and the tasks (Gantt, Kanban), GitHub for the code and its history, Google Drive for the documents.
- The brief called for a CMS. After a trial with WordPress and Divi, I built the website by hand (HTML, Tailwind CSS, vanilla JavaScript) for interactive demonstrations and no admin interface to attack; my teammates did the same.
- In hindsight: a longer research phase on the secure element up front would have saved us time.
Problems solved
-
The pre-provisioned identity was too rigid to test with
The ATECC608B-TNGTLSU (Trust&GO) ships with its own keys and certificates, which proved too complex to work with during development. I stored our own keys in the general-purpose data slot 8 instead, so they could be regenerated as often as the tests required.
-
Libraries that would not drive the chip
Neither ArduinoECCX08 nor Microchip’s CryptoAuthLib worked with the chip in our setup. After many attempts, the SparkFun ATECCX08a library did, which unblocked the integration, for my stage and for the LoRa part of the project.
-
A variable-length key in fixed 32-byte blocks
EC keys in DER vary slightly in length, and the chip only reads and writes 32-byte blocks. I prefixed the key with its length on two bytes (big-endian) and read the whole slot back, so the firmware knows where the key ends and the padding begins.
Limits and next steps
What this lab does not yet do, and what a production device would need.
-
The key is read into RAM
The ESP32 loads the key into RAM for its software TLS stack: storage is protected, execution is not.
Sign the handshake inside the chip (PKCS#11 or ECDSA callbacks), so that the key never leaves it.
-
A brief exposure at boot
The key exists in RAM for a moment during start-up, before the TLS engine takes it.
Wipe the buffers after use and enable ESP32 Secure Boot.
-
An unencrypted I²C bus
The bus between the ESP32 and the chip is not encrypted, so a determined attacker could probe it.
Use a secure element built into the board, such as the one in the M5Stack Core2 for AWS IoT, to remove the exposed wiring.
Skills demonstrated
Each skill links to the skills map on the home page.