• 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

Architecture of the secure telemetry chain Certificate authority OpenSSL · EC P-256 CN=m5go-pub CN=m5sub-display CN=broker.local M5Go Publisher · ESP32 Certificates · firmware TLS 1.2 key loaded into RAM I²C ENV II 0x44 · 0x76 ATECC608B 0x35 · slot 8 PKCS#8 DER read at boot 8883 mTLS m5go/env Mosquitto MQTT broker listener 8883 Client certificate required allow_anonymous false require_certificate true use_identity_as_username true 8883 mTLS m5go/env M5Stack Subscriber · ESP32 Certificates · firmware TLS 1.2 key loaded into RAM I²C ATECC608B 0x35 · slot 8 PKCS#8 DER read at boot Architecture of the secure telemetry chain Certificate authority OpenSSL · EC P-256 M5Go Publisher · ESP32 CN=m5go-pub Certificates · firmware TLS 1.2 key loaded into RAM M5Stack Subscriber · ESP32 CN=m5sub-display Certificates · firmware TLS 1.2 key loaded into RAM I²C ENV II 0x44 · 0x76 I²C ATECC608B 0x35 · slot 8 PKCS#8 DER read at boot ATECC608B 0x35 · slot 8 PKCS#8 DER read at boot Mosquitto MQTT broker listener 8883 CN=broker.local Client certificate required allow_anonymous false require_certificate true use_identity_as_username true 8883 mTLS m5go/env 8883 mTLS m5go/env
An M5Go publisher with an ENV II unit (temperature, humidity, pressure) publishes its readings every five seconds on the topic m5go/env, over Wi-Fi, to a Mosquitto broker on port 8883. The broker only accepts clients whose certificate was signed by the project’s certificate authority, and routes the data to an M5Stack subscriber that displays it. On each device, the private key sits in an ATECC608B secure element on the I²C bus.

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

  1. 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.

  2. 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.

  3. 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.

Command palette

At a glance
Experience
Projects
Skills
Credentials
Education
About
Languages
Journey
Contact
Secure IoT telemetry
Secure multi-site architecture
5G Standalone network
System and network hardening
Vulnerability assessment and exploitation
Multi-site network design and security
Regenerative repeater for satellite links
Use the light theme
Use the dark theme
Use the system theme
Lire en français
Copy my email address
Download my CV (PDF)
Open my LinkedIn profile
Back to the top