%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#16A085', 'secondaryColor': '#E67E22', 'tertiaryColor': '#7F8C8D', 'background': '#ffffff', 'mainBkg': '#2C3E50', 'secondBkg': '#16A085', 'tertiaryBkg': '#E67E22'}}}%%
graph TB
subgraph device[" End Device "]
PAYLOAD[Application Payload<br/>Sensor Data]
APPENC[Encrypt with AppSKey<br/>AES-128]
MACENC[Add MAC Header<br/>Encrypt with NwkSKey]
MIC[Calculate MIC<br/>NwkSKey]
end
subgraph network[" Network Server "]
NMIC[Verify MIC<br/>NwkSKey]
NMAC[Decrypt MAC Layer<br/>NwkSKey]
NFORWARD[Forward Encrypted<br/>App Payload]
end
subgraph app[" Application Server "]
ADEC[Decrypt Payload<br/>AppSKey]
ADATA[Application Data<br/>Plaintext]
end
PAYLOAD --> APPENC
APPENC --> MACENC
MACENC --> MIC
MIC -->|LoRa RF| NMIC
NMIC --> NMAC
NMAC -->|Can't decrypt<br/>app payload| NFORWARD
NFORWARD --> ADEC
ADEC --> ADATA
style device fill:#f0f0f0,stroke:#2C3E50,stroke-width:2px
style network fill:#f0f0f0,stroke:#16A085,stroke-width:2px
style app fill:#f0f0f0,stroke:#E67E22,stroke-width:2px
style PAYLOAD fill:#2C3E50,stroke:#16A085,color:#fff
style APPENC fill:#E67E22,stroke:#2C3E50,color:#fff
style MACENC fill:#16A085,stroke:#2C3E50,color:#fff
style MIC fill:#16A085,stroke:#2C3E50,color:#fff
style NMIC fill:#16A085,stroke:#2C3E50,color:#fff
style NMAC fill:#16A085,stroke:#2C3E50,color:#fff
style NFORWARD fill:#7F8C8D,stroke:#2C3E50,color:#fff
style ADEC fill:#E67E22,stroke:#2C3E50,color:#fff
style ADATA fill:#E67E22,stroke:#2C3E50,color:#fff
1079 LoRaWAN Security and Joining
1079.1 Learning Objectives
By the end of this chapter, you will be able to:
- Explain Security Architecture: Describe the dual-layer encryption model with NwkSKey and AppSKey
- Compare Activation Methods: Differentiate OTAA and ABP joining procedures
- Evaluate Security Trade-offs: Assess when to use OTAA vs ABP based on deployment requirements
- Implement Best Practices: Apply security guidelines for production LoRaWAN deployments
1079.2 Prerequisites
Before diving into this chapter, you should be familiar with:
- LoRaWAN Network Topology: Understanding network components and server roles
- LoRaWAN Device Classes: Device communication patterns
LoRaWAN uses two layers of encryption to protect your data:
- Network Layer (NwkSKey): Protects the network infrastructure—ensures only authorized devices can use the network
- Application Layer (AppSKey): Protects your actual sensor data—even the network operator can’t read it!
Analogy: Think of sending a letter through a courier service: - NwkSKey: The sealed envelope that proves you’re a valid customer (network can verify) - AppSKey: The coded message inside that only you and the recipient understand (courier can’t read)
Two ways to get these keys: - OTAA (Over-The-Air Activation): Like registering for a new SIM card—you authenticate and get fresh keys each time - ABP (Activation By Personalization): Like a pre-paid SIM—keys are pre-loaded at the factory, simpler but less secure
| Term | Simple Explanation |
|---|---|
| NwkSKey | Network Session Key—proves you’re allowed on the network |
| AppSKey | Application Session Key—encrypts your sensor data |
| OTAA | Join procedure that generates fresh keys (more secure) |
| ABP | Pre-programmed keys, no join needed (simpler but riskier) |
| DevEUI | Device’s unique identifier (like a MAC address) |
| AppKey | Root secret used to derive session keys in OTAA |
1079.3 Security Architecture
LoRaWAN provides end-to-end security with two encryption layers:
1079.3.1 Network Session Key (NwkSKey)
- Encrypts MAC layer payload
- Validates message integrity (MIC)
- Known only to device and network server
1079.3.2 Application Session Key (AppSKey)
- Encrypts application payload
- Provides end-to-end encryption
- Network server cannot decrypt application data
1079.3.3 Security Properties
| Property | Protection | Key Used |
|---|---|---|
| Confidentiality | Payload encrypted end-to-end | AppSKey |
| Integrity | MIC prevents tampering | NwkSKey |
| Authentication | Only valid devices accepted | NwkSKey |
| Replay Protection | Frame counter prevents replays | NwkSKey |
1079.4 Joining a LoRaWAN Network
Devices can join a network using two methods:
1079.4.1 Over-The-Air Activation (OTAA)
Recommended method - More secure, keys derived dynamically
- Device sends Join Request with DevEUI and AppEUI
- Network server validates and sends Join Accept
- Session keys derived from AppKey and nonces
- Device assigned DevAddr for the session
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#16A085'}}}%%
sequenceDiagram
participant D as End Device
participant G as Gateway
participant NS as Network Server
participant JS as Join Server
Note over D: Has DevEUI, AppEUI, AppKey
D->>G: Join Request (DevEUI, AppEUI, DevNonce)
G->>NS: Forward Join Request
NS->>JS: Verify credentials
JS->>NS: Approved + AppKey
Note over NS: Derive NwkSKey, AppSKey
NS->>G: Join Accept (DevAddr, AppNonce)
G->>D: Join Accept (encrypted with AppKey)
Note over D: Derive NwkSKey, AppSKey
D->>G: First uplink with new session keys
OTAA Credentials:
| Credential | Size | Purpose |
|---|---|---|
| DevEUI | 64-bit | Globally unique device identifier |
| AppEUI/JoinEUI | 64-bit | Application identifier |
| AppKey | 128-bit | Root key for deriving session keys |
1079.4.2 Activation By Personalization (ABP)
Simpler but less secure - Pre-provisioned keys
- DevAddr, NwkSKey, and AppSKey programmed at manufacturing
- No join procedure required
- Useful for constrained devices or private networks
- Less secure: keys never change
ABP Pre-Provisioned Values:
| Value | Size | Notes |
|---|---|---|
| DevAddr | 32-bit | Network address (pre-assigned) |
| NwkSKey | 128-bit | Pre-programmed, never changes |
| AppSKey | 128-bit | Pre-programmed, never changes |
1079.4.3 OTAA vs ABP Comparison
| Aspect | OTAA | ABP |
|---|---|---|
| Security | High (keys rotate on rejoin) | Lower (static keys) |
| Setup Complexity | Requires join procedure | Simpler (no join) |
| Key Compromise | Rejoin generates new keys | Keys compromised forever |
| Frame Counter | Resets on rejoin | Must persist across reboots |
| Roaming | Supported | Not supported |
| Recommended | Yes (production) | Testing only |
1079.5 Knowledge Check
Test your understanding of LoRaWAN security.
1079.6 Common Pitfalls
The Mistake: Deploying ABP (Activation By Personalization) devices without implementing non-volatile frame counter storage, causing frame counter reset after power loss and subsequent message rejection.
Why It Happens: ABP simplifies deployment by avoiding join procedures, but developers overlook that LoRaWAN network servers reject messages with frame counters lower than previously seen (replay attack protection). When an ABP device reboots and resets its frame counter to zero, all subsequent messages are silently dropped.
The Fix: For ABP devices, store the frame counter in EEPROM or flash memory, incrementing by 100+ on each save to account for messages sent before the next save. Better yet, use OTAA activation which generates fresh session keys and resets frame counters on each join, eliminating this problem entirely.
The Mistake: Developers choose Activation By Personalization (ABP) over OTAA because it appears simpler (no join procedure, works immediately), then discover critical issues in production: frame counter resets causing packet rejection, security vulnerabilities from static keys, and inability to roam between network servers.
Why It Happens: ABP looks attractive during prototyping:
- Instant gratification: Device transmits immediately without waiting for join
- Deterministic behavior: Same session keys every boot, easier to debug
- Simpler network setup: No Join Server required
- Works in poor coverage: No two-way communication needed for activation
But ABP creates severe production problems:
- Frame counter vulnerability: After power loss, counter resets to 0. Network server expects higher counter and rejects all messages as replays
- Static keys = static risk: If AppSKey/NwkSKey are compromised, device must be physically retrieved and re-provisioned
- No server-side key refresh: Keys never change over device lifetime (potentially 10+ years)
- Roaming impossible: Device is permanently bound to one network server
The Fix: Use OTAA for production, ABP only for specific constrained scenarios:
When ABP is acceptable: - Single-transmission sensors (measure, transmit, die) - Development/testing only - Private networks with physical security - Devices with guaranteed power (no resets)
When OTAA is required (most production cases): - Any device that may power-cycle - Multi-year deployments - Public network connectivity (TTN, Helium) - Devices that may roam between regions/operators
If you must use ABP, implement frame counter persistence:
// Store frame counter in EEPROM/Flash every N messages
void save_frame_counter(uint32_t fcnt) {
// Write to non-volatile storage
eeprom_write_dword(FCNT_ADDR, fcnt);
}
uint32_t load_frame_counter() {
uint32_t fcnt = eeprom_read_dword(FCNT_ADDR);
// Add safety margin to handle unexpected resets
return fcnt + 100; // Skip 100 counts to ensure acceptance
}Production deployment statistics: In a 5-year agricultural deployment with 600 ABP devices, 18% required manual intervention due to frame counter issues (power outages, firmware updates). The same deployment with OTAA would have zero such interventions, as each rejoin generates fresh keys and resets counters server-side.
The Mistake: Developers swap or confuse the AppEUI (JoinEUI in LoRaWAN 1.1), AppKey, and DevEUI when registering devices on the network server, or use the same value for multiple fields. The join procedure fails silently with no clear error message indicating which credential is incorrect.
Why It Happens: OTAA requires three distinct 128-bit (AppKey) and 64-bit (DevEUI, JoinEUI) values that look similar in hex format. Confusion arises from:
- Naming changes: LoRaWAN 1.0 uses
AppEUI, LoRaWAN 1.1 renamed it toJoinEUI- different network servers use different terminology - Copy-paste errors: All values are 16 or 32 hex characters; easy to paste into wrong field
- Sample code defaults: Tutorials often use identical placeholder values (
0x00...00) for all fields - Key vs EUI confusion: AppKey is 128-bit (16 bytes), DevEUI/JoinEUI are 64-bit (8 bytes) - wrong length indicates wrong field
The Fix: Understand and carefully verify each OTAA credential:
- DevEUI (64-bit): Unique device identifier, typically from hardware. Format:
70B3D57ED00XXXXX - JoinEUI/AppEUI (64-bit): Application identifier, can be shared across device fleet. Often all-zeros for TTN:
0000000000000000 - AppKey (128-bit): Root key for session key derivation. MUST be unique per device, randomly generated:
2B7E151628AED2A6ABF7158809CF4F3C
OTAA credential checklist:
Before deploying, verify:
[ ] DevEUI is 8 bytes (16 hex chars)
[ ] JoinEUI/AppEUI is 8 bytes (16 hex chars)
[ ] AppKey is 16 bytes (32 hex chars)
[ ] DevEUI is unique per device (not copied from tutorial)
[ ] AppKey is randomly generated (not default all-zeros or all-Fs)
[ ] Network server shows same values as device firmware
[ ] Byte order matches (MSB vs LSB) between device and server
Common byte-order trap: Some platforms display DevEUI as MSB-first (70B3D57ED00XXXXX), others as LSB-first (XXXXX00D7ED5B370). If Join Request appears in gateway logs but network server rejects it, suspect byte-order mismatch. The Things Stack uses MSB format; verify your device library’s convention.
The Mistake: Developers deploy thousands of devices with OTAA activation, then power them all on simultaneously (e.g., after a firmware update or mass deployment). All devices send Join Requests at once, overwhelming the network server and causing most joins to fail.
Why It Happens: OTAA requires a successful handshake: device sends Join Request, network responds with Join Accept during the RX1/RX2 windows (1-2 seconds after request). With EU868’s 1% duty cycle, a gateway can only transmit ~36 seconds per hour on downlink. If 500 devices join simultaneously, the gateway can only respond to ~30 devices per hour (each Join Accept takes ~1.2 seconds at SF12). The other 470 devices timeout and retry, creating a “join storm.”
The Fix: Implement randomized join backoff. Before sending a Join Request, each device should:
- Wait a random delay:
delay_ms = random(0, 30000)(spread joins over 30 seconds) - For large deployments (1000+ devices), use longer windows:
delay_ms = random(0, device_count * 60)(1 minute per 1000 devices) - Configure exponential backoff on failure:
retry_delay = min(2^attempt * 5000, 3600000)(5s, 10s, 20s… up to 1 hour) - Pre-calculate DevNonce to avoid replay issues: store last used DevNonce in non-volatile memory
Example for 2000-device deployment:
// Spread joins over 2 hours (3600ms * 2)
initial_delay = random(0, 7200000);
sleep(initial_delay);
send_join_request();
This reduces peak join load from 2000 simultaneous requests to ~0.3 requests/second, ensuring 99%+ successful joins on first attempt.
1079.7 Practice Exercise
1079.8 Summary
LoRaWAN security provides:
- Dual-layer encryption: NwkSKey for network, AppSKey for application (end-to-end)
- Flexible activation: OTAA (secure, dynamic keys) vs ABP (simple, static keys)
- Integrity protection: MIC prevents message tampering
- Replay protection: Frame counters prevent message replay attacks
Key Recommendation: Always use OTAA for production deployments. ABP should only be used for testing or specific constrained scenarios with proper frame counter persistence.
Continue to LoRaWAN Link Budget and ADR to learn about range calculations, spreading factor optimization, and the Adaptive Data Rate algorithm.