Industrial IoT Firmware Security: Secure Boot, Flash Encryption, and OTA Updates

Why IoT Firmware Security Is a Critical Concern in 2026

In industrial IoT, every connected node is a potential entry point into the factory network. Temperature sensors, telemanagement modules, edge gateways, actuators — these devices, often deployed in uncontrolled environments, are prime targets for attackers. A recent ENISA study (2025) reports that 67% of industrial cybersecurity incidents originate from a compromised IoT device — unsigned firmware, private keys extracted via flash reading, or unauthenticated OTA updates.

For an industrial SME or IoT integrator, the reality is stark: an unsecured firmware can lead to production stoppages, intellectual property theft (embedded business algorithms), remote takeover of critical equipment, or worse, a lateral attack toward the central information system. The associated costs — operational loss, ransom, reputational damage — often run into hundreds of thousands of euros.

This article is aimed at embedded architects, R&D managers, and technical decision-makers. We detail the fundamental security mechanisms of an industrial IoT firmware: secure boot, flash memory encryption, secure OTA updates, and cryptographic key lifecycle management. Each section includes real code examples from IOTINNOV deployments and field-identified pitfalls.

Secure Boot: The Chain of Trust from Reset

Secure boot is the foundation of any firmware security. It is the first link in the chain of trust: a mechanism guaranteeing that only a signed, intact firmware is executed by the microcontroller, from boot ROM to application.

Operating Principle

The process unfolds in several locked stages:

  1. Boot ROM (fused into silicon, unalterable): at reset, the processor executes microcode that verifies the first-stage bootloader (FSBL) signature using a public key burned into OTP (One-Time Programmable) fuses. If the signature is invalid, boot stops immediately.
  2. FSBL: loads and verifies the second-stage bootloader (SSBL) or directly the RTOS kernel/application.
  3. Application: can in turn verify the integrity of data in flash (configuration, certificates) before starting its tasks.

The critical point is that each link verifies the next before passing control. If any link is compromised, the chain is broken and the system refuses to boot.

ESP32-S3 Implementation

Espressif’s ESP32-S3 features second-generation secure boot (Secure Boot V2) based on ECDSA with the P-256 curve (secp256r1):

# Step 1: Generate the private key (on a dedicated machine, never shared)
espsecure.py generate_signing_key --scheme ecdsa256 secure_boot_signing_key.pem

# Step 2: Burn the public key into efuses (IRREVERSIBLE)
# This operation locks the efuses — the key can never be changed afterward
espefuse.py burn_key DIGEST secure_boot_signing_key.pem --sha-digest
espefuse.py burn_efuse SECURE_BOOT_EN

# Step 3: Sign the firmware at each build
espsecure.py sign_data --keyfile secure_boot_signing_key.pem \
  --version 2 --output build/app-signed.bin build/app.bin

# Step 4: Verification by the bootloader at startup
# SHA-256 hash of the signed firmware is compared to the efuse public key
# Mismatch => boot immediately halted

⚠️ Critical pitfall: Once the SECURE_BOOT_EN efuse is blown, it is impossible to disable secure boot. Any unsigned firmware will be rejected. If the private key is lost, the microcontroller becomes permanently unusable. Key management must be integrated into the build process with a secure backup mechanism (HSM or software vault).

STM32 Implementation with TrustZone

STM32U5 and STM32H5 MCUs integrate hardware secure boot via TrustZone (ARMv8-M) and OEMiRoT (OEM immutable Root of Trust):

/* OEMiRoT activation via STM32CubeProgrammer */
# 1. Burn the OEMiRoT bootloader into the secure zone
# 2. Configure RDP (Read Protection) to level 2 — irreversible
# 3. Boot verifies the SHA-256 hash of the user firmware before execution

/* Signed firmware header structure for STM32 */
typedef struct __attribute__((packed)) {
    uint32_t magic;              // 0x45535552 ('USER')
    uint32_t image_length;       // Total firmware size
    uint32_t image_version;      // Version incremented at each release
    uint8_t  sha256_hash[32];    // SHA-256 of the firmware
    uint8_t  signature[64];      // ECDSA P-256 signature
    uint32_t crc32;              // Header CRC32
} stm32_firmware_header_t;

The STM32 boot ROM compares the signature against a trust anchor stored in OTP. If valid, the firmware is decrypted (if flash encryption is enabled) and executed in non-secure mode.

Flash Encryption: Protecting Data at Rest

Flash memory encryption prevents extraction of firmware and sensitive data (keys, certificates, configuration) through physical readout of the memory component — bus sniffing, microprobing, or desoldering the SPI flash. This is a critical barrier when the product is deployed in an unattended site.

ESP32: AES-XTS Flash Encryption

The ESP32 implements transparent hardware AES-XTS-256 encryption. The flash controller includes an automatic encryption/decryption engine: the CPU reads decrypted data without any software intervention. The encryption key is stored in efuses, inaccessible to the CPU once the secure zone is locked.

# Enabling flash encryption (ESP32-S3)
# Development Mode: the key can be regenerated (debug)
# Release Mode: the key is locked — irreversible
espefuse.py --port /dev/ttyUSB0 burn_efuse FLASH_CRYPT_CNT 1
espefuse.py --port /dev/ttyUSB0 burn_efuse FLASH_CRYPT_CONFIG 0xF

# To activate Release Mode (production):
espefuse.py --port /dev/ttyUSB0 burn_efuse FLASH_CRYPT_CNT 127
# The encryption key is then permanently inaccessible

# Secure data storage in flash (NVS):
// Use nvs_flash with NVS_ENCRYPT_FLAG parameter
#include "nvs_flash.h"
esp_err_t ret = nvs_flash_secure_init(nvs_default_partition,
                                       nvs_encryption_keys);

⚠️ Pitfall: Activating flash encryption in Release mode is irreversible. A corrupted firmware or failed OTA update turns the product into a brick. Always test the OTA update mechanism BEFORE enabling Release mode. We recommend validating at least 100 successful OTA cycles in Development mode before moving to production.

Secure OTA Updates: The Most Attacked Link

Over-The-Air updates are both a functional necessity and the most exposed attack surface of an IoT product. An unsecured OTA channel allows an attacker to deploy their own firmware across an entire product fleet — the nightmare scenario.

3-Layer Secure OTA Architecture

  1. Secure transport: firmware is downloaded via HTTPS (TLS 1.3) from an authenticated server. No HTTP, no FTP, no unencrypted channel.
  2. Firmware signing: the binary is signed server-side with a private key distinct from the transport key. The device verifies the signature BEFORE applying the update.
  3. Dual-bank flash with rollback: two firmware partitions (slot A and slot B) allow reverting to the previous version in case of failure.

ESP-IDF Implementation (esp_ota)

#include "esp_ota_ops.h"
#include "esp_https_ota.h"
#include "esp_image_format.h"

static const char *TAG = "ota";

/* Update descriptor structure */
typedef struct {
    char url[256];              // HTTPS URL of the firmware
    uint8_t expected_sha[32];   // Expected SHA-256 (verified before flash)
    uint32_t version;           // Version number (for rollback decision)
} ota_update_info_t;

esp_err_t perform_secure_ota(const ota_update_info_t *update) {
    esp_http_client_config_t http_cfg = {
        .url = update->url,
        .cert_pem = server_cert_pem_start,  // SSL certificate pinning
        .timeout_ms = 30000,
        .keep_alive_enable = false,
    };

    esp_https_ota_config_t ota_cfg = {
        .http_config = &http_cfg,
        .partial_http_download = true,
        .max_http_request_size = 32768,
    };

    /* Download and automatic verification */
    esp_err_t ret = esp_https_ota(&ota_cfg);
    if (ret == ESP_OK) {
        /* Additional check: compare versions */
        const esp_partition_t *running = esp_ota_get_running_partition();
        esp_app_desc_t running_app_info;
        esp_ota_get_partition_description(running, &running_app_info);

        const esp_partition_t *update_part = esp_ota_get_next_update_partition(NULL);
        esp_app_desc_t update_app_info;
        esp_ota_get_partition_description(update_part, &update_app_info);

        if (update_app_info.version <= running_app_info.version) {
            ESP_LOGE(TAG, "Version downgrade detected — rollback");
            esp_ota_mark_app_invalid_rollback_and_reboot();
            return ESP_FAIL;
        }

        /* Validate the update */
        esp_ota_set_boot_partition(update_part);
        esp_ota_mark_app_valid_cancel_rollback();
        esp_restart();
    }
    return ret;
}

/* Bootloader: active slot selection */
void check_ota_rollback(void) {
    const esp_partition_t *boot_partition = esp_ota_get_boot_partition();
    esp_ota_img_states_t ota_state;
    if (esp_ota_get_state_partition(boot_partition, &ota_state) == ESP_OK) {
        if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) {
            /* Firmware hasn't confirmed successful operation yet */
            if (app_self_test() == ESP_OK) {
                esp_ota_mark_app_valid_cancel_rollback();
                ESP_LOGI(TAG, "OTA validated — slot marked valid");
            } else {
                ESP_LOGE(TAG, "Self-test failed — rolling back");
                esp_ota_mark_app_invalid_rollback_and_reboot();
            }
        }
    }
}

Cryptographic Key Lifecycle Management

The security of any embedded system ultimately rests on its keys. A compromised key renders all other protection mechanisms useless. Key lifecycle management is therefore a strategic concern, often neglected in early prototypes.

Key Types in an IoT System

Key TypeUsageStorageRotation
Firmware signing keySign OTA binariesHSM or isolated machine (offline)Yearly
Secure boot public keyVerify bootOTP efuse (burned)Never (irreversible)
Flash encryption keyDecrypt firmware on-the-flyOTP efuseNever (irreversible)
Device identity (DeviceID)MQTT/TLS authenticationEncrypted NVS or secure elementAt each reset
TLS client certificateMutual TLS with serverEncrypted flash + NVS slotPolicy-based (30-365 days)
Session keysApplication data encryptionRAM (volatile)Each session

Secure Production Provisioning

The most vulnerable moment in an IoT device’s lifecycle is its production phase: this is when the unique identity (Device ID, certificate, private key) must be injected into each unit. Several approaches exist:

  1. Factory provisioning: keys are generated by the production server and injected via the test port (JTAG/SWD) into a secure element (SE05x, ATECC608). Flash is then locked. This is the most secure method but requires a controlled production infrastructure.
  2. On-site provisioning: the device generates its own key pair at first boot and sends the public key to the server via a pre-established TLS channel. This avoids transporting private keys on the production line but requires initial proof of identity (factory-signed certificate).
  3. Dedicated PKI provisioning: an EJBCA or Smallstep CA server delivers individual certificates to each device at first contact. The private key stays in the device (locally generated).

At IOTINNOV, we systematically use approach #3: a factory certificate (burned in OTP) allows the device to authenticate with an internal PKI that delivers its own certificate. The operational certificate’s private key is generated by the device and never leaves its secure element.

Full Security Stack Overview

Here is how these mechanisms fit together in a typical industrial IoT product:

PhaseMechanismProtects Against
ManufacturingOTP key burning, efuse locking, identity provisioningFactory tampering, identical keys across units
BootSecure Boot (signature verification), Flash Decrypt (hardware)Unauthorized firmware execution, flash extraction
OperationTLS 1.3 (mutual), application encryption, secure elementNetwork eavesdropping, MitM, device spoofing
UpdateHTTPS + ECDSA signing, dual bank, auto rollbackMalicious firmware, downgrade, OTA corruption
MaintenanceDebug port locked (RDP L2), signed logs, attestationReverse engineering, log replay, cloning
End of lifeRemote wipe, certificate revocation, key destructionIdentity reuse on counterfeit device

Each link in this chain is essential. A single weak link — for example, an unsigned OTA — nullifies the protection provided by all the others.

Conclusion: Securing Your IoT Firmware Is an Investment, Not a Cost

Embedded firmware security is not an option — it is a prerequisite for any industrial IoT product deployed in real-world conditions. An unsecured product exposes its manufacturer to legal (GDPR, product liability), financial (ransom, production loss), and reputational (customer trust) risks.

The mechanisms presented in this article — secure boot, flash encryption, signed OTA, secure element — are mature and accessible on mainstream microcontrollers (ESP32-S3, STM32U5, RP2350) as well as high-end platforms. Their implementation requires planning from the design phase, but the development overhead is far outweighed by risk reduction and the confidence they bring to customers.

At IOTINNOV, we design secure IoT firmware for industrial clients, from specification through certification. Our approach covers security architecture, secure element integration, production PKI setup, and penetration testing validation. Every product ships with a complete chain of trust — from boot to OTA update.

Are you developing an industrial IoT product and would like to assess your embedded architecture’s security? Contact our engineering team for a free 30-minute security audit — we will identify firmware vulnerabilities and provide a prioritized remediation plan.