HomeAbout UsContact Us

Security Best Practices for Embedded Firmware

By Jithin Tom
July 07, 2026
3 min read
Security Best Practices for Embedded Firmware

Table Of Contents

01
Threat Model First
02
Secure Boot: The Foundation
03
Firmware Encryption and Confidentiality
04
Memory Protection: MPU/MMU Configuration
05
Hardening the Attack Surface
06
Secure Key Storage
07
Secure Firmware Update (OTA) Design
08
Cryptographic Hygiene
09
Supply Chain Security
10
Incident Response Preparation
11
Summary
12
Related Reading
13
References
14
Frequently Asked Questions

Security in embedded firmware is no longer optional. As devices connect to networks, handle sensitive data, and control physical systems, they become targets. The constraints of embedded systems — limited compute, no secure OS, long lifecycles — make traditional security approaches difficult. This article covers practical hardening techniques that work within those constraints.

Threat Model First

Before writing code, define what you’re protecting against. A medical device faces different threats than a smart light bulb. Common embedded threat vectors:

  • Physical access: JTAG/SWD debugging, UART consoles, voltage glitching, side-channel analysis
  • Firmware extraction: Reading flash via debug interfaces or bus snooping
  • Firmware modification: Replacing firmware with malicious versions
  • Runtime exploitation: Buffer overflows, integer overflows, use-after-free in parsing code
  • Supply chain: Compromised toolchains, counterfeit chips, malicious third-party libraries

Document your threat model. It drives every architectural decision.

Secure Boot: The Foundation

Secure boot establishes a chain of trust from power-on reset to application code. Each stage verifies the next before execution.

+--------------------------+
| ROM Bootloader (Root) | <-- Immutable, in silicon
| Verifies: Stage 1 sig |
+------------+-------------+
|
v
+--------------------------+
| Stage 1 Bootloader | <-- Signed by OEM key
| Verifies: Stage 2 sig |
| Initializes: MPU, clock |
+------------+-------------+
|
v
+--------------------------+
| Stage 2 / Application | <-- Signed by OEM key
| Verifies: Config, data |
| Enforces: MPU regions |
+--------------------------+

Implementation requirements:

  1. Root of trust in hardware: The first verification must occur in ROM or OTP. Most modern MCUs (STM32H7, NXP i.MX RT, TI Sitara, ESP32) support this.
  2. Cryptographic signatures: Use ECDSA P-256 or RSA-2048 minimum. Ed25519 is preferred for performance.
  3. Anti-rollback: Store a monotonic counter in OTP/secure storage. Reject firmware with a lower version counter.
  4. Key revocation: Support certificate chains or key rotation via OTP fuses.

Common pitfalls:

  • Verifying only the header, not the full image
  • Using symmetric keys (HMAC) for boot verification — allows key extraction to sign arbitrary firmware
  • Skipping verification on “development” builds that ship to production

Firmware Encryption and Confidentiality

Encryption protects IP and prevents firmware analysis. Two approaches:

Encrypted Flash Storage

The MCU decrypts firmware on-the-fly during execution (e.g., STM32 OTFDEC, ESP32 flash encryption). Keys never leave the chip.

// STM32 OTFDEC example: configure external flash encrypted region
// Note: OTFDEC inherently uses AES-128 CTR mode
OTFDEC_RegionConfigTypeDef config = {
.StartAddress = 0x90000000, // OCTOSPI external flash start
.EndAddress = 0x9007FFFF, // OCTOSPI external flash end
.Nonce = {0x11223344, 0x55667788}, // 64-bit nonce for AES-CTR
.Version = 0x1234 // 16-bit firmware version
};
// Configure region 1 without locking configuration
// Decryption keys are loaded separately (e.g., via HAL_OTFDEC_RegionSetKey)
HAL_OTFDEC_RegionConfig(&hotfdec, OTFDEC_REGION_1, &config, OTFDEC_REG_CONFIGR_LOCK_DISABLE);
HAL_OTFDEC_RegionEnable(&hotfdec, OTFDEC_REGION_1);

Encrypted Firmware Updates

OTA payloads encrypted with AES-GCM or ChaCha20-Poly1305. The device decrypts only after signature verification.

+------------------+ +------------------+
| OTA Server | | Device |
| | | |
| Firmware v1.2 | | 1. Verify sig |
| AES-GCM encrypt | | 2. Decrypt |
| Sign (ECDSA) |---->| 3. Write flash |
| Package: | | 4. Update ctr |
| {ct, tag, nonce,| | 5. Reboot |
| sig, version} | | |
+------------------+ +------------------+

Memory Protection: MPU/MMU Configuration

Memory Protection Units (MPU) on Cortex-M or MMUs on Cortex-A enforce isolation. Define regions early — before untrusted code runs.

Typical MPU Region Layout (Cortex-M)

+--------------------------+ 0x00000000
| Bootloader (RO, XN=0) | Execute only, no write
| Size: 64 KB |
+--------------------------+
| Secure Config (RO, XN=1)| Keys, certs, no execute
| Size: 16 KB |
+--------------------------+
| Application Code (RX) | Execute + read
| Size: Variable |
+--------------------------+
| App Data (RW, XN=1) | Read/write, no execute
| Stack (RW, XN=1) |
+--------------------------+
| Peripherals (RW, XN=1) | Device memory attributes
| Size: 512 MB |
+--------------------------+ 0xFFFFFFFF
// Example: MPU region for secure key storage (no execute, privileged read-only)
MPU_Region_InitTypeDef region = {
.Enable = MPU_REGION_ENABLE,
.Number = MPU_REGION_NUMBER_1,
.BaseAddress = SECURE_KEY_BASE,
.Size = MPU_REGION_SIZE_16KB,
.SubRegionDisable = 0,
.TypeExtField = MPU_TEX_LEVEL_0,
.AccessPermission = MPU_REGION_PRIV_RO, // Privileged read-only, user no-access
.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE,
.IsShareable = MPU_ACCESS_NOT_SHAREABLE,
.IsCacheable = MPU_ACCESS_NOT_CACHEABLE,
.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE,
};
HAL_MPU_ConfigRegion(&region);

Key principles:

  • Mark all writable regions as execute-never (XN=1)
  • Mark all executable regions as write-never
  • Use privileged-only access for critical regions
  • Enable MPU before jumping to application

Hardening the Attack Surface

Disable Debug Interfaces in Production

// STM32: Disable SWD/JTAG by reconfiguring pins as analog
// Note: This is a software defense. Use RDP Level 2 for hardware protection.
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_13 | GPIO_PIN_14; // SWDIO, SWCLK
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

For permanent disable, blow OTP fuses (e.g., STM32 RDP Level 2, NXP SJC_DISABLE). Warning: RDP Level 2 is irreversible.

Input Validation and Parsing Safety

Network-facing parsers are the #1 exploit vector. Use bounded parsers, avoid scanf/strcpy/sprintf.

// Bad: unbounded
char buf[64];
strcpy(buf, network_input); // Overflow!
// Good: bounded with explicit length
size_t len = strnlen(network_input, sizeof(buf) - 1);
memcpy(buf, network_input, len);
buf[len] = '\0';
// Better: use a safe parsing library (e.g., nanopb for Protobuf, tinycbor for CBOR)
// Pseudocode — consult your library's API for exact function signatures
if (parse(network_input, input_len, &value) != OK) return ERROR_INVALID_FORMAT;

Stack Smashing Protection

Enable compiler stack protection:

# GCC/Clang (arm-none-eabi)
-fstack-protector-strong

On hosted platforms (Linux/RTOS with glibc), additionally use -D_FORTIFY_SOURCE=2 for hardened libc functions. Note: _FORTIFY_SOURCE requires libc support — bare-metal newlib-nano does not fully implement it. On Cortex-M, combine stack canaries with MPU stack guard regions for hardware-enforced protection.

Secure Key Storage

Never store keys in plain flash. Options in order of preference:

  1. Hardware Secure Element (ATECC608A, STSAFE-A110, OPTIGA Trust M) — keys never leave chip, supports ECDSA, ECDH, AES
  2. Internal MCU Secure Storage (STM32 HUK, NXP CAAM, TI AES/SA) — derived from device-unique keys
  3. OTP/One-Time Programmable memory — for root keys, immutable
  4. Encrypted external flash — with key wrapped by internal hardware key
// Example: Using ATECC608A for ECDSA signing
ATCA_STATUS status = atcab_sign(PRIVATE_KEY_SLOT, digest, signature);
if (status != ATCA_SUCCESS) return ERROR_CRYPTO_FAILURE;
// Private key never exposed to MCU firmware

Secure Firmware Update (OTA) Design

A robust OTA system requires:

PropertyImplementation
AuthenticityECDSA signature on full payload
IntegritySHA-256 hash verified before write
ConfidentialityAES-GCM encryption of payload
FreshnessMonotonic version counter, timestamp
AtomicityA/B partition scheme
Rollback protectionReject versions <= current
RecoveryFallback to known-good partition

A/B Partition Layout

Flash Memory Map:
+--------------------------+ 0x08000000
| Bootloader (immutable) | 64 KB
+--------------------------+
| Slot A (active) | 512 KB
| Firmware v1.2 |
+--------------------------+
| Slot B (standby) | 512 KB
| Firmware v1.3 (new) |
+--------------------------+
| Metadata / Config | 32 KB
| {active_slot: A, |
| version_A: 1.2, |
| version_B: 1.3, |
| rollback_ctr: 5} |
+--------------------------+

Update flow:

  1. Download to inactive slot (B)
  2. Verify signature + hash + version > current
  3. Write metadata: pending_slot = B
  4. Reboot
  5. Bootloader sees pending_slot, verifies Slot B, swaps active
  6. Mark Slot A as fallback
  7. On next successful boot, confirm Slot B permanent

Cryptographic Hygiene

Random Number Generation

Use hardware TRNG (True Random Number Generator), not PRNG. Seed any CSPRNG from TRNG.

// STM32 RNG example
uint32_t random32;
if (HAL_RNG_GenerateRandomNumber(&hrng, &random32) != HAL_OK) {
Error_Handler(); // Clock or seed error — do not proceed
}
// For larger buffers, generate in a loop (32 bits at a time)
for (size_t i = 0; i < word_count; i++) {
if (HAL_RNG_GenerateRandomNumber(&hrng, &buffer[i]) != HAL_OK) {
Error_Handler();
}
}

Algorithm Selection (2026 Recommendations)

PurposeAlgorithmParameters
SignaturesEd25519 / ECDSA P-256Ed25519: inherently deterministic (RFC 8032); ECDSA: deterministic nonce per RFC 6979
Key ExchangeX25519 / ECDH P-256Ephemeral keys
Symmetric EncryptionAES-256-GCM / ChaCha20-Poly130596-bit nonce, never reuse
HashingSHA-256 / SHA-384
KDFHKDF-SHA256Salt from TRNG
Password HashingArgon2id / PBKDF2Argon2id (m=32KB) or PBKDF2-HMAC-SHA256

Avoid: RSA < 2048, SHA-1, MD5, ECB mode, static nonces, custom crypto.

Side-Channel Resistance

On devices handling secrets, use constant-time implementations. Most secure elements handle this internally. For software crypto, use libraries with constant-time guarantees (e.g., mbedTLS, wolfSSL, libsodium).

Supply Chain Security

  • SBOM (Software Bill of Materials): Generate SPDX or CycloneDX for every build
  • Reproducible builds: Same source + toolchain = identical binary
  • Signed artifacts: Sign build outputs, not just source commits
  • Dependency pinning: Exact versions, not ranges (libfoo==1.2.3, not ^1.2.3)
  • Vulnerability scanning: Integrate grype, trivy, or syft in CI

Incident Response Preparation

Even hardened systems can be compromised. Prepare:

  1. Forensic logging: Tamper-evident event log in secure storage
  2. Device attestation: Remote verification of firmware hash + config
  3. Revocation mechanism: Certificate/key revocation via CRL or OCSP
  4. Update capability: Emergency OTA path for critical patches

Summary

Embedded firmware security is a system property, not a checklist item. The most effective defenses layer together:

  1. Secure boot establishes trust from reset
  2. Memory protection limits blast radius of exploits
  3. Encrypted storage/updates protect confidentiality
  4. Hardware key storage prevents key extraction
  5. Safe parsing + stack protection eliminate common vuln classes
  6. A/B OTA with rollback protection enables safe fleet updates
  7. Supply chain hygiene prevents poisoned dependencies

Start with threat modeling. Implement secure boot first — everything else builds on it. Use hardware security features (MPU, TRNG, secure elements) rather than software-only approximations. Test with fault injection and side-channel analysis if the threat model warrants it.

  • Memory Protection Unit in Embedded Systems
  • Lock-Free Ring Buffers for ISR-to-Task Communication

References

  1. NIST SP 800-193: Platform Firmware Resiliency Guidelines (https://csrc.nist.gov/publications/detail/sp/800-193/final)
  2. ARM Platform Security Architecture (PSA) Certified (https://www.psacertified.org/)
  3. MITRE ATT&CK for Industrial Control Systems (https://attack.mitre.org/matrices/ics/)
  4. Common Weakness Enumeration (CWE) Top 25 (https://cwe.mitre.org/top25/)
  5. PSA Functional API Specification (https://arm-software.github.io/psa-api/crypto/1.1/)
  6. IEC 62443-4-2: Security for Industrial Automation and Control Systems (https://www.iec.ch/)

Frequently Asked Questions

What is the single most important security practice for embedded firmware?

Implementing secure boot with cryptographic verification of firmware integrity before execution is the foundation. Without it, an attacker can replace firmware entirely, bypassing all other protections.

How does secure boot differ from regular bootloading?

Regular bootloaders load and execute firmware without verification. Secure boot validates a cryptographic signature (typically RSA/ECDSA) against a hardware-rooted trust anchor before transferring control, ensuring only authorized firmware runs.

Why is key management critical in embedded security?

Keys protect the entire chain of trust. If a private signing key is extracted, attackers can sign malicious firmware that passes secure boot. Hardware secure elements (ATECC608, STSAFE) or TPMs should store keys, never plain flash or external memory.

What role does memory protection (MPU/MMU) play in firmware security?

Memory protection isolates critical code and data (keys, crypto contexts, secure boot logic) from application code. It prevents buffer overflows in one component from compromising the entire system, enforcing least-privilege execution.

How should firmware updates be secured over-the-air (OTA)?

OTA updates must use authenticated encryption (AES-GCM or ChaCha20-Poly1305), verify signatures before installation, implement rollback protection (anti-rollback counters), and use A/B partitioning for atomic updates with fallback.

Tags

embedded-securityfirmwarehardeningsecure-bootcryptography

Share


Previous Article
Getting Started with Zephyr RTOS: A Practical Guide for Embedded Engineers
Jithin Tom

Jithin Tom

A Closer Look at C/C++, RTOS, and Embedded Systems

Related Posts

Memory Protection Unit in Embedded Systems
Memory Protection Unit in Embedded Systems
May 20, 2026
4 min
© 2026, All Rights Reserved.
Powered By Netlyft

Quick Links

Advertise with usAbout UsContact Us

Social Media