15 Secure Comms & Firmware
15.1 Learning Objectives
By the end of this chapter, you should be able to:
- Implement TLS/SSL for secure MQTT communications
- Configure DTLS for CoAP over UDP
- Deploy WireGuard VPN for IoT remote access
- Implement secure boot with firmware signature verification
- Design secure over-the-air (OTA) update systems
For Beginners: Understanding Secure Communications
What is Secure Communication? Secure communication ensures that data traveling between devices cannot be intercepted (confidentiality), modified (integrity), or sent by impostors (authentication). It’s like sending a letter in a locked box that only the recipient can open.
Why does it matter for IoT? IoT devices transmit sensitive data (health readings, location, commands) over networks that attackers can monitor. Without encryption, anyone on the network can read your data or inject malicious commands.
Key terms: | Term | Definition | |——|————| | TLS | Transport Layer Security - encrypts TCP connections (HTTPS, MQTTS) | | DTLS | Datagram TLS - encrypts UDP connections (CoAP) | | VPN | Virtual Private Network - encrypted tunnel for all traffic | | Secure Boot | Verifying firmware integrity before execution | | OTA Update | Over-the-Air firmware update (wireless) |
Sensor Squad: The Armored Data Highway!
“When I send data to the cloud, it travels through the wild internet!” Sammy the Sensor said nervously. “Anyone could be listening along the way.”
Max the Microcontroller reassured him. “That is why we use TLS – Transport Layer Security. Think of it as putting your data in an armored truck. When Sammy connects to the cloud server, they first do a special handshake where they exchange secret keys. After that, everything they say to each other is encrypted. Even if a bad guy intercepts the traffic, all they see is meaningless jumbled data!”
“For devices that use UDP instead of TCP, like tiny sensors, we have DTLS,” Lila the LED explained. “It is the same protection as TLS but designed for the lighter UDP protocol. And for really secure connections, we can use VPNs – Virtual Private Networks – that create an encrypted tunnel for ALL traffic, not just one connection.”
“Secure boot is another piece of the puzzle,” Bella the Battery said. “Every time Max powers on, he checks that his software has not been tampered with. It is like a doctor checking your temperature before a checkup – if something is off, you stop and investigate. Between TLS for communications, DTLS for constrained devices, VPNs for full tunnel protection, and secure boot for firmware integrity, our data highway is locked down tight!”
15.2 Prerequisites
Before diving into this chapter, you should be familiar with:
- Cryptography for IoT: Understanding of TLS/DTLS cryptographic foundations
- Authentication Methods: Certificate-based authentication for mTLS
Key Concepts
- TLS (Transport Layer Security): A cryptographic protocol that provides authentication, confidentiality, and integrity for data in transit over TCP; the ‘S’ in HTTPS.
- DTLS (Datagram TLS): TLS adapted for UDP — handles packet loss and reordering, making it suitable for IoT protocols like CoAP that use UDP.
- mTLS (Mutual TLS): TLS where both client and server authenticate each other using certificates; essential for device-to-cloud IoT authentication.
- Certificate Authority (CA): A trusted entity that issues and signs digital certificates, allowing parties to verify each other’s identity.
- Secure Boot: A process that verifies the cryptographic signature of firmware before execution, preventing unsigned or tampered code from running on a device.
- VPN (Virtual Private Network): An encrypted tunnel over an untrusted network, used to secure communication between IoT gateways and cloud back-ends.
- Perfect Forward Secrecy (PFS): A property of key exchange where session keys are ephemeral — compromise of the long-term private key does not expose past session data.
15.3 How It Works: TLS Handshake for MQTT
TLS establishes an encrypted channel through a multi-step handshake before any application data is sent:
Step 1: Client Hello (ESP32 to Broker)
- Client announces supported TLS versions (TLS 1.2, 1.3)
- Lists cipher suites: TLS_AES_128_GCM_SHA256, TLS_CHACHA20_POLY1305_SHA256
- Sends random nonce (32 bytes) for key derivation
Step 2: Server Hello (Broker to ESP32)
- Selects TLS version and cipher suite
- Sends server’s X.509 certificate (contains public key)
- Sends random nonce (32 bytes)
Step 3: Certificate Verification (ESP32 validates broker)
- Extract server’s public key from certificate
- Verify certificate signature using CA’s public key (pre-loaded on ESP32)
- Check certificate validity period (not expired)
- Verify hostname matches (broker.example.com)
- If any check fails: Abort connection, prevent man-in-the-middle attack
Step 4: Key Exchange (Establish shared secret)
- ESP32 generates ephemeral ECDH key pair
- Sends public key to server
- Server generates ephemeral ECDH key pair, sends public key to client
- Both compute shared secret (pre-master secret) using ECDH
- Derive session keys from: client_nonce + server_nonce + shared_secret
Step 5: Finished Messages (Verify handshake integrity)
- Client sends HMAC of entire handshake encrypted with session key
- Server sends HMAC of entire handshake encrypted with session key
- Both verify HMACs match – handshake complete
Step 6: Application Data (MQTT over encrypted channel)
- All MQTT messages encrypted with AES-128-GCM using session keys
- Each message has authentication tag preventing tampering
- Session keys discarded after disconnect (forward secrecy)
Mutual TLS (mTLS) Addition: After Step 3, server requests client certificate:
- ESP32 sends its device certificate (unique per device)
- Server verifies client certificate against CA
- Both sides authenticated (server proves identity to client, client proves identity to server)
Performance Impact on ESP32:
- Handshake time: 200-500ms (one-time per connection)
- Encryption overhead: 1-3ms per MQTT message
- RAM usage: ~40KB for TLS buffers
- CPU overhead: ~5% for AES-128-GCM (hardware accelerated on ESP32)
Attack Scenario Prevented: Without certificate verification (setInsecure()), attacker performs ARP spoofing on local network, intercepts connection to broker.example.com, presents self-signed certificate. Device accepts it and sends all data to attacker’s fake broker. With proper verification, device rejects untrusted certificate and aborts connection.
15.4 TLS/SSL for MQTT
MQTT is the most common IoT messaging protocol. Securing it with TLS prevents eavesdropping and ensures only authorized devices connect.
Why MQTT Security Matters: Attack Scenario
Without TLS encryption and proper input validation, attackers can:
- Inject malformed messages that crash subscribers
- Exploit QoS 2 retry mechanisms to amplify attacks
- Eavesdrop on sensitive sensor data transmitted in cleartext
- Spoof device identities without certificate authentication
15.4.1 MQTT over TLS Implementation (ESP32)
// MQTT over TLS (MQTTS) on ESP32
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
WiFiClientSecure espClient;
PubSubClient mqtt(espClient);
void setup() {
WiFi.begin(ssid, password);
// Configure TLS with server CA + optional mutual TLS
espClient.setCACert(ca_cert);
espClient.setCertificate(client_cert); // mutual TLS (optional)
espClient.setPrivateKey(client_key);
mqtt.setServer("broker.example.com", 8883); // MQTTS port
mqtt.setCallback(messageCallback);
}
void reconnect() {
while (!mqtt.connected()) {
if (mqtt.connect("ESP32Client", mqtt_user, mqtt_pass)) {
mqtt.subscribe("sensors/#");
} else {
delay(5000); // retry
}
}
}15.4.2 MQTT Security Levels
| Level | Configuration | Security | Use Case |
|---|---|---|---|
| None | Port 1883, no auth | None | Development only |
| Password | Port 1883, username/password | Low | Internal networks |
| TLS | Port 8883, server cert | Medium | General production |
| mTLS | Port 8883, server + client certs | High | Enterprise, critical systems |
15.5 DTLS for CoAP
CoAP uses UDP, so TLS cannot be used directly. DTLS (Datagram TLS) provides equivalent security for UDP-based protocols.
15.5.1 DTLS Implementation
// CoAP over DTLS
#include <coap-simple.h>
#include <mbedtls/ssl.h>
mbedtls_ssl_context ssl;
mbedtls_ssl_config conf;
void setup_dtls() {
mbedtls_ssl_init(&ssl);
mbedtls_ssl_config_init(&conf);
// Configure DTLS (datagram mode)
mbedtls_ssl_config_defaults(&conf,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_DATAGRAM,
MBEDTLS_SSL_PRESET_DEFAULT);
// Set certificates
mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL);
mbedtls_ssl_conf_own_cert(&conf, &clicert, &pkey);
mbedtls_ssl_setup(&ssl, &conf);
}
void sendCoapMessage(String payload) {
// Encrypt and send with DTLS
mbedtls_ssl_write(&ssl, (uint8_t*)payload.c_str(),
payload.length());
// DTLS handles encryption, integrity, and replay protection
}15.5.2 TLS vs DTLS Comparison
| Feature | TLS (TCP) | DTLS (UDP) |
|---|---|---|
| Transport | TCP (reliable) | UDP (unreliable) |
| Connection | Stateful | Connectionless |
| Handshake | Simpler | Handles packet loss, reordering |
| Protocols | HTTPS, MQTTS | CoAP, VoIP, gaming |
| IoT Suitability | Good | Excellent for constrained |
Knowledge Check: TLS vs DTLS
Question: Why can TLS not be used directly over UDP for CoAP communications?
A. TLS encryption algorithms do not support UDP packets B. TLS relies on TCP’s reliable, ordered delivery which UDP does not provide C. UDP is inherently secure and does not need TLS D. TLS is only designed for web browsers
Click to reveal answer
Answer: B – TLS relies on TCP’s reliable, ordered delivery which UDP does not provide
TLS assumes packets arrive in order and without loss (guaranteed by TCP). UDP provides no such guarantees, so TLS handshake messages and encrypted records could arrive out of order or be lost entirely. DTLS adds its own retransmission timers and sequence numbers to handle this, making it the correct choice for UDP-based protocols like CoAP.15.6 VPN for Remote Access
VPNs create encrypted tunnels for all traffic, enabling secure remote access to IoT networks.
15.6.1 WireGuard VPN Configuration
# WireGuard VPN configuration for IoT gateway
import subprocess
def setup_wireguard():
config = """
[Interface]
PrivateKey = <device_private_key>
Address = 10.0.0.2/24
DNS = 10.0.0.1
[Peer]
PublicKey = <server_public_key>
Endpoint = vpn.example.com:51820
AllowedIPs = 10.0.0.0/24
PersistentKeepalive = 25
"""
with open('/etc/wireguard/wg0.conf', 'w') as f:
f.write(config)
# Start VPN
subprocess.run(['wg-quick', 'up', 'wg0'])
# All IoT traffic now encrypted through VPN15.6.2 Worked Example: WireGuard for Distributed Agriculture
Scenario: A precision agriculture company operates 20 remote farms with IoT sensors. Each farm has intermittent 4G connectivity. Implement secure VPN using WireGuard.
Solution:
Architecture: Hub-and-spoke topology with central server
- Central VPN server: 10.200.0.1
- Farm 1 subnet: 10.200.1.0/24
- Farm 2 subnet: 10.200.2.0/24
- Technician pool: 10.200.100.0/24
Central server configuration:
[Interface] PrivateKey = <server_private_key> Address = 10.200.0.1/16 ListenPort = 51820 # Farm 1 gateway (entire farm subnet behind gateway) [Peer] PublicKey = <farm1_public_key> AllowedIPs = 10.200.1.0/24 PersistentKeepalive = 25 # Technician 1 (single IP) [Peer] PublicKey = <tech1_public_key> AllowedIPs = 10.200.100.101/32Access control rules:
# Technicians can reach farm gateways iptables -A FORWARD -s 10.200.100.0/24 -p tcp --dport 22 -j ACCEPT # Farms can only reach central MQTT broker iptables -A FORWARD -s 10.200.1.0/20 -d 10.200.0.1 -p tcp --dport 8883 -j ACCEPT # Block inter-farm traffic iptables -A FORWARD -s 10.200.1.0/24 -d 10.200.2.0/24 -j DROP
Result:
- Encryption: Curve25519 key exchange (256-bit keys, ~128-bit security level) with ChaCha20-Poly1305
- Latency overhead: 2-5ms
- Isolation: Farms cannot communicate directly
- Scalability: 200+ concurrent peers
15.7 Secure Boot and Firmware Verification
Secure boot ensures that only authenticated firmware runs on a device, preventing attackers from installing malware.
The code-signing process shown above is the foundation of secure OTA updates (Section 15.8). The manufacturer signs firmware with their private key, and the device verifies the signature using the corresponding public key embedded in the secure boot chain.
15.7.1 Secure Boot Implementation (ESP32)
// ESP32 Secure Boot
#include "esp_secure_boot.h"
#include "esp_flash_encrypt.h"
void setup() {
Serial.begin(115200);
// Check if secure boot is enabled
if (esp_secure_boot_enabled()) {
Serial.println("Secure boot ENABLED");
Serial.println(" Firmware signature verified at boot");
} else {
Serial.println("WARNING: Secure boot DISABLED");
Serial.println(" Device vulnerable to firmware attacks");
}
// Check flash encryption
if (esp_flash_encryption_enabled()) {
Serial.println("Flash encryption ENABLED");
Serial.println(" Firmware encrypted in flash");
} else {
Serial.println("WARNING: Flash encryption DISABLED");
}
}15.7.2 Secure Boot Chain of Trust
Key Insight: The chain of trust starts with immutable Boot ROM (hardware). Each stage verifies the next before execution. If ANY verification fails, the device refuses to boot.
15.7.3 Hardware Trojan Countermeasures
Hardware trojans are malicious modifications to integrated circuits that bypass software security.
| Strategy | Description | IoT Application |
|---|---|---|
| Side-Channel Analysis | Monitor power/EM emissions for anomalies | Detecting trojans in deployed sensors |
| Logic Testing | Exhaustive testing of rare input combinations | Pre-deployment validation |
| Design Obfuscation | Hide circuit design intent | Chip manufacturing security |
| Run-time Monitoring | Continuous behavioral analysis | Detecting activated trojans |
15.8 Secure OTA Updates
Over-the-air updates must verify authenticity before installation to prevent malicious firmware injection.
15.8.1 OTA with Signature Verification
// Secure OTA with signature verification (ESP32)
#include <Update.h>
#include <HTTPClient.h>
#include "mbedtls/md.h"
#include "mbedtls/pk.h"
// Manufacturer's public key (embedded at build time)
const char* manufacturer_public_key = "-----BEGIN PUBLIC KEY-----\n...";
bool performSecureOTA(String firmwareUrl, String signatureUrl) {
// 1. Download signature
HTTPClient http;
http.begin(signatureUrl);
String signature = http.getString();
http.end();
// 2. Download firmware, computing SHA-256 hash in-stream
http.begin(firmwareUrl);
WiFiClient *stream = http.getStreamPtr();
int remaining = http.getSize();
mbedtls_md_context_t ctx;
mbedtls_md_init(&ctx);
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 0);
mbedtls_md_starts(&ctx);
uint8_t buffer[1024];
while (remaining > 0) {
int c = stream->readBytes(buffer, min((size_t)remaining, sizeof(buffer)));
mbedtls_md_update(&ctx, buffer, c);
remaining -= c;
}
uint8_t hash[32];
mbedtls_md_finish(&ctx, hash);
// 3. Verify RSA signature against hash
mbedtls_pk_context pk;
mbedtls_pk_init(&pk);
mbedtls_pk_parse_public_key(&pk, (const uint8_t*)manufacturer_public_key,
strlen(manufacturer_public_key) + 1);
int ret = mbedtls_pk_verify(&pk, MBEDTLS_MD_SHA256, hash, 32,
signature_bytes, signature_len);
if (ret != 0) return false; // REJECT tampered firmware
// 4. Install verified firmware ...
return true;
}15.8.2 OTA Security Checklist
| Security Measure | Purpose | Implementation |
|---|---|---|
| Digital signature | Verify firmware authenticity | RSA-2048 or ECDSA P-256 |
| HTTPS download | Protect during transit | TLS 1.2+ with certificate pinning |
| Version check | Prevent downgrade attacks | Reject older versions |
| Rollback partition | Recovery from failed update | Dual-partition A/B scheme |
| Staged rollout | Limit blast radius | Update 1% of devices first |
Knowledge Check: Secure Boot
Question: An IoT gateway needs to securely boot and load firmware. An attacker replaces the application firmware with malware. What prevents the malware from running?
A. Boot ROM loads bootloader without verification B. Bootloader checks firmware file size before loading C. Bootloader verifies firmware RSA signature against manufacturer’s public key D. Firmware runs in sandbox that limits malware damage
Click to reveal answer
Answer: C - Bootloader verifies firmware RSA signature against manufacturer’s public key
Why?
- The chain of trust: Boot ROM (immutable) verifies bootloader signature, then bootloader verifies firmware signature
- Attackers cannot forge signatures without the manufacturer’s private key
- Even if they replace firmware, signature verification fails and device refuses to boot
- File size checks are easily spoofed; sandboxing helps but doesn’t prevent execution
Worked Example: Securing LoRaWAN Network with End-to-End Encryption
Scenario: Agricultural IoT company deploys 1,000 soil moisture sensors across 50 farms using LoRaWAN. Network server is cloud-hosted, but customer demands that raw sensor data never be visible to the cloud provider.
Requirements:
- Sensor data encrypted end-to-end (device to customer application)
- LoRaWAN network server cannot read sensor values
- Bandwidth: 51-byte max MACPayload at LoRaWAN SF10 (EU868)
- Battery life: 5 years on 2x AA batteries
Solution Architecture:
| Layer | Protocol | Key Type | Purpose |
|---|---|---|---|
| E1: Link | LoRaWAN AES-128 | Network session key | Protect RF transmission |
| E2: App | Custom AES-128-GCM | Per-device key | Prevent network server reading |
| E3: Transport | TLS 1.3 | ECDHE | Secure network server to customer app |
Payload Calculation:
Max MACPayload at SF10/EU868: 51 bytes
MACPayload = FHDR (7 bytes) + FPort (1 byte) + FRMPayload
Available application payload (FRMPayload): 51 - 7 - 1 = 43 bytes
Sensor data (E2 plaintext):
- Device ID: 4 bytes
- Timestamp: 4 bytes
- Moisture reading: 2 bytes
- Temperature: 2 bytes
- Battery voltage: 2 bytes
Total plaintext: 14 bytes
E2 encryption overhead:
- AES-GCM nonce: 12 bytes
- AES-GCM auth tag: 16 bytes
- Encrypted payload: 14 bytes
Total E2 packet: 42 bytes
42 bytes <= 43 bytes max FRMPayload
Result: Fits within LoRaWAN constraints with 1 byte to spare
Energy Analysis:
Per message (sent hourly):
- LoRaWAN transmission: 45 mA x 1.5s = 67.5 mAs
- AES-128-GCM encryption: 8 mA x 0.02s = 0.16 mAs
- Total per message: 67.66 mAs
Annual energy:
- Messages per year: 24 x 365 = 8,760
- Transmission + encryption: 8,760 x 67.66 = 592,702 mAs = 164.6 mAh
- Sleep/quiescent overhead (MCU sleep, sensor probe, RTC): ~760 mAh/year
Battery capacity: 2 x 2,400 mAh = 4,800 mAh Battery life: 4,800 / (164.6 + 760) = 5.2 years
Note: The sleep/quiescent current (~87 uA average) dominates battery consumption, not the transmission energy. This is typical for agricultural sensors with continuous soil moisture probe power draw.
Key Management:
Manufacturing:
- Generate unique 128-bit AES key per device
- Provision key in device secure flash
- Escrow customer’s public key in device
- Device generates ephemeral ECDH key pair
Field Operation:
- Device encrypts sensor data with AES-GCM (E2)
- LoRaWAN encrypts E2 packet with network session key (E1)
- Network server decrypts E1, forwards E2 blob to customer app
- Customer app decrypts E2 using provisioned device key
Security Properties Achieved:
| Threat | Mitigation | Layer |
|---|---|---|
| RF eavesdropping | LoRaWAN AES-128 | E1 |
| Network server compromise | End-to-end AES-GCM | E2 |
| Data tampering | AES-GCM authentication tag | E2 |
| Key distribution | Pre-provisioned at manufacturing | E2 |
| Transport security | TLS 1.3 with ECDHE | E3 |
Result: Customer receives sensor data encrypted at the device level. Even if the LoRaWAN network provider is compromised, raw sensor data remains protected. Bandwidth overhead (28 bytes for E2 encryption) fits within LoRaWAN limits, and encryption energy consumption is negligible (<1% of transmission energy).
Decision Framework: Choosing TLS vs DTLS vs VPN
Use this framework to select the appropriate secure communication approach:
| Criteria | TLS (TCP) | DTLS (UDP) | VPN (WireGuard) |
|---|---|---|---|
| Protocol compatibility | HTTPS, MQTTS, WebSocket | CoAP, QUIC | Any IP traffic |
| Latency requirement | <500ms acceptable | <100ms required | <50ms required |
| Packet loss tolerance | Poor (TCP retransmits) | Excellent | Excellent |
| Setup complexity | Medium | Medium | Low |
| Per-connection overhead | ~5KB | ~1KB | ~300 bytes (peer state) |
| Battery impact | Medium | Low | Very low |
| Firewall traversal | Easy (port 443) | Moderate (custom ports) | Excellent (UDP hole punching) |
Decision Tree:
Is your application protocol TCP-based (HTTP, MQTT)?
+-- YES --> Use TLS 1.3
| +-- Configure cipher suite: TLS_AES_128_GCM_SHA256
|
+-- NO --> Does your protocol use UDP?
+-- YES --> Use DTLS 1.3
| +-- Example: CoAP with DTLS (RFC 7252)
|
+-- NO --> Do you need to secure ALL traffic?
+-- YES --> Use VPN (WireGuard)
| +-- Encrypts entire IP layer
|
+-- NO --> Application-layer encryption
+-- Use E2/E3 with AES-GCM
Real-World Example Decision:
Smart meter deployment:
- Protocol: CoAP (UDP-based)
- Network: Cellular NB-IoT (high latency, packet loss)
- Data: Meter readings every 15 minutes
- Constraint: 200ms maximum latency for demand response
Analysis:
| Option | Pros | Cons | Verdict |
|---|---|---|---|
| TLS | Industry standard | Requires TCP (overhead) | Rejected |
| DTLS | Designed for UDP, lightweight | Certificate management | Selected |
| VPN | Secures all traffic | Battery drain, always-on | Rejected |
| App-layer | Ultra-lightweight | No standard key exchange | Rejected |
Final choice: DTLS 1.3 with PSK (pre-shared keys) mode to avoid certificate overhead. Handshake completes in 1-RTT, meeting latency requirement.
Common Mistake: Disabling Certificate Validation
The Mistake: Developers disable TLS certificate validation to “fix” connection errors during development, then forget to re-enable it in production.
Code example (WRONG):
// ESP32 - DON'T DO THIS
WiFiClientSecure espClient;
espClient.setInsecure(); // Disables certificate validation
mqtt.setClient(espClient);
mqtt.connect("broker.example.com", 8883);Why this is catastrophic:
With certificate validation disabled, your device will accept TLS connections from anyone - including attackers performing man-in-the-middle attacks.
Attack scenario:
- Device connects to “broker.example.com:8883”
- Attacker intercepts connection (ARP spoofing, DNS hijack, rogue Wi-Fi AP)
- Attacker presents self-signed certificate
- Device accepts it (validation disabled!) and establishes “secure” connection
- Attacker now has full access to device commands and data
The fix (CORRECT):
// Option 1: Pin specific CA certificate (recommended for ESP32)
const char* broker_cert = \
"-----BEGIN CERTIFICATE-----\n" \
"MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQsF...\n" \
"-----END CERTIFICATE-----\n";
espClient.setCACert(broker_cert);
// Option 2: Use system CA bundle (ESP32)
espClient.setCACertBundle(ca_bundle_start);Why developers make this mistake:
| Reason | Reality | Solution |
|---|---|---|
| “Certificate errors during testing” | Usually hostname mismatch | Use correct hostname in test environment |
| “Don’t know how to get CA cert” | CA certs are public | Download from broker provider website |
| “Certificate expired” | Test environment issue | Update test broker certificates |
| “Works without it” | Only against non-malicious network | Enable validation ALWAYS |
Real-world incident:
A smart home product shipped with setInsecure() enabled. Attackers created fake MQTT brokers at coffee shops. When devices connected to Wi-Fi, they sent data to attacker’s fake broker. Over 50,000 devices compromised before firmware patch deployed.
Cost: $12M in emergency firmware updates, legal settlements, and brand damage.
The rule: NEVER disable certificate validation in production. If you face certificate errors, fix the root cause – don’t disable security.
Concept Relationships
| Concept | Related To | Relationship Type |
|---|---|---|
| TLS (Transport Layer) | TCP, MQTT, HTTPS | Secures - Reliable connection-oriented protocols |
| DTLS (Datagram TLS) | UDP, CoAP | Adapts - TLS concepts to unreliable connectionless protocols |
| VPN (WireGuard) | IP Layer Encryption | Tunnels - All network traffic through encrypted channel |
| Secure Boot | Chain of Trust, ROM | Establishes - Hardware root of trust validates firmware signatures |
| OTA Updates | Code Signing, Versioning | Protects - Firmware integrity during wireless updates |
| Certificate Pinning | Man-in-the-Middle Defense | Prevents - Acceptance of fraudulent certificates even if CA compromised |
See Also
Foundation Concepts:
- Cryptography for IoT - AES-GCM, ECDH, digital signatures
- Authentication Methods - Certificate-based authentication
Related Security Topics:
- IoT Protocol Security - MQTT and CoAP encryption layers
- Firmware Security - Secure boot and code signing details
- Network Segmentation - VPN for isolated networks
Practical Applications:
- Access Control Labs - Implement mTLS authentication
- Threat Modelling - Man-in-the-middle attack analysis
Try It Yourself: Implement Certificate Verification
Build a TLS certificate validator to understand the chain of trust verification process.
Exercise Steps:
- Load server certificate (leaf cert) from connection
- Extract issuer information (which CA signed this cert)
- Load CA certificate (root or intermediate)
- Verify signature: hash(server_cert) matches signature decrypted with CA public key
- Check validity period (not_before <= current_time <= not_after)
- Verify hostname matches (cert’s CN or SAN matches broker.example.com)
Starter Code (Python with cryptography library):
from cryptography import x509
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from datetime import datetime, timezone
def verify_certificate(server_cert_pem, ca_cert_pem, hostname):
# Load certificates
server_cert = x509.load_pem_x509_certificate(server_cert_pem.encode())
ca_cert = x509.load_pem_x509_certificate(ca_cert_pem.encode())
# Check 1: Verify signature (server cert signed by CA)
try:
ca_public_key = ca_cert.public_key()
ca_public_key.verify(
server_cert.signature,
server_cert.tbs_certificate_bytes,
padding.PKCS1v15(),
server_cert.signature_hash_algorithm
)
print("Signature valid (cert signed by CA)")
except Exception as e:
print(f"Signature invalid: {e}")
return False
# Check 2: Verify validity period
now = datetime.now(timezone.utc)
if server_cert.not_valid_before_utc <= now <= server_cert.not_valid_after_utc:
print(f"Validity period OK (expires {server_cert.not_valid_after_utc})")
else:
print("Certificate expired or not yet valid")
return False
# Check 3: Verify hostname (CN or SAN)
# Exercise: Extract CN from subject or SAN extension
# Exercise: Compare with hostname parameter
print(f"Hostname matches: {hostname}")
return True
# Test with real certificate
server_cert = """-----BEGIN CERTIFICATE-----..."""
ca_cert = """-----BEGIN CERTIFICATE-----..."""
verify_certificate(server_cert, ca_cert, "broker.example.com")What to Observe:
- Signature verification uses CA’s PUBLIC key (anyone can verify)
- Signing uses CA’s PRIVATE key (only CA can sign)
- Expired certificates rejected even if signature valid
- Hostname mismatch detected (prevents wrong-domain attacks)
- Chain of trust: root CA -> intermediate CA -> server cert
Extension: Implement full chain validation for 3-level hierarchy (root -> intermediate -> leaf)
Putting Numbers to It: TLS 1.3 Key Derivation with HKDF
TLS 1.3 derives session keys using HKDF (HMAC-based Key Derivation Function), defined in RFC 5869:
\[\text{HKDF-Extract}(\text{salt}, \text{IKM}) = HMAC\text{-}SHA256(\text{salt}, \text{IKM})\]
\[\text{HKDF-Expand}(\text{PRK}, \text{info}, L) = \text{first } L \text{ bytes of } T\]
where \(T = T_1 \parallel T_2 \parallel \ldots\) and \(T_i = HMAC\text{-}SHA256(\text{PRK}, T_{i-1} \parallel \text{info} \parallel i)\).
TLS 1.3 Key Schedule:
\[\text{Handshake Secret} = \text{HKDF-Extract}(\text{derived salt}, \text{ECDH shared secret})\]
\[\text{Master Secret} = \text{HKDF-Extract}(\text{derived salt}, 0)\]
Working through an example:
Given: ESP32 establishes TLS 1.3 connection to MQTT broker. Both sides performed ECDH key exchange using X25519.
Client random: \(C = \text{0xa3f8b2c1...}\) (32 bytes)
Server random: \(S = \text{0x7d4e9a6f...}\) (32 bytes)
ECDH shared secret: \(K = \text{0x5b2c8f1a...}\) (32 bytes)
Step 1: Extract handshake secret using HKDF-Extract \[\text{salt} = \text{HKDF-Expand-Label}(\text{Early Secret}, \text{"derived"}, \text{""}, 32)\]
\[\text{HS} = HMAC\text{-}SHA256(\text{salt}, K)\]
\[\text{HS} = \text{0x8f3d1c9e...} \quad (32 \text{ bytes})\]
Step 2: Derive client handshake traffic secret \[\text{label} = \text{"c hs traffic"}\]
\[\text{context} = \text{SHA-256}(\text{ClientHello} \parallel \text{ServerHello})\]
\[\text{CHTS} = \text{HKDF-Expand-Label}(\text{HS}, \text{label}, \text{context}, 32)\]
Step 3: Derive client write key (AES-128-GCM = 16 bytes) \[K_{client} = \text{HKDF-Expand-Label}(\text{CHTS}, \text{"key"}, \text{""}, 16)\]
\[K_{client} = \text{0x2b7e1516...} \quad (16 \text{ bytes})\]
Step 4: Derive server application traffic secret and write key similarly
\[K_{server} = \text{0x8c6d5a3f...} \quad (16 \text{ bytes})\]
Result: Client encrypts with \(K_{client}\), server encrypts with \(K_{server}\). Keys are unique per connection (forward secrecy). Eavesdropper capturing handshake cannot derive keys without ECDH private keys.
In practice: If attacker records all TLS traffic today and later compromises the server’s long-term private key, they CANNOT decrypt past sessions because ephemeral ECDH keys were destroyed after handshake. This is forward secrecy – critical for long-lived IoT deployments where key compromise years later should not expose historical data.
15.9 Chapter Summary
Secure communications protect IoT data in transit using TLS for TCP-based protocols (MQTT) and DTLS for UDP-based protocols (CoAP). VPNs like WireGuard provide encrypted tunnels for remote access with low latency overhead suitable for constrained IoT networks.
Secure boot establishes a chain of trust from immutable hardware (Boot ROM) through bootloader to application firmware, with each stage cryptographically verifying the next before execution. OTA updates must verify digital signatures before installation to prevent malicious firmware injection, with rollback capabilities for recovery from failed updates.
15.10 Knowledge Check
Common Pitfalls
1. Disabling Certificate Verification
Mistake: Setting verify=False in TLS connections to avoid certificate management complexity. Why it happens: Self-signed certificates cause errors during development. Fix: Use a proper IoT PKI with device certificates issued by a trusted CA; implement certificate pinning for high-security devices.
2. Confusing TLS 1.2 and DTLS 1.2
Mistake: Using TLS over UDP-based protocols like CoAP, causing connection failures. Why it happens: Developers familiar with HTTP/TLS apply the same stack to all protocols. Fix: Use DTLS (Datagram TLS) for UDP-based IoT protocols; TLS requires TCP, DTLS handles packet loss and reordering natively.
3. Ignoring Firmware Signature Verification
Mistake: Accepting any firmware update without verifying its cryptographic signature. Why it happens: Adding signature verification increases OTA update complexity. Fix: Sign firmware images with ECDSA private key, verify with device-stored public key before flashing; use secure boot to verify the bootloader itself.
4. Storing Session Tokens in Plaintext Flash
Mistake: Writing MQTT session credentials or JWT tokens to unprotected flash storage. Why it happens: Flash storage is the obvious place for persistent credentials. Fix: Encrypt credentials at rest using a device-unique key derived from hardware identifiers, or use a secure element for credential storage.
5. Reusing IVs with AES-GCM
Mistake: Using a counter-based IV that resets to zero on device reboot. Why it happens: IV generation seems like an implementation detail. Fix: Use a combination of a monotonic counter stored in non-volatile memory plus a random nonce; IV reuse in GCM mode catastrophically breaks both confidentiality and authentication.
15.11 What’s Next
With secure communications and firmware integrity in place, the next chapter examines Monitoring and Detection where you’ll learn to implement intrusion detection systems, anomaly detection for IoT networks, and comprehensive security exercises.
| Direction | Chapter | Focus |
|---|---|---|
| Prerequisites | Cryptography for IoT | TLS/DTLS cryptographic foundations |
| Prerequisites | Authentication Methods | Certificate-based authentication for mTLS |
| Next | Monitoring and Detection | Intrusion detection for IoT networks |
| Next | Encryption Principles | Deeper dive into symmetric and asymmetric basics |