🔐 Bluetooth Mesh — Part 3
ble mesh tutorial – Security, Encryption Keys, Obfuscation & Key Identifiers Explained
Security & Keys
~12 minutes
Intermediate
Key Terms in This Post
Why is Mesh Security Complex?
In a Bluetooth Mesh network, messages are relayed by intermediate nodes — a message may pass through several bulbs and switches before reaching its destination. This creates a challenge: how do you allow relaying without letting relay nodes read or modify the data? The answer is Bluetooth Mesh’s two-layer key system. There are no unencrypted or unauthenticated messages in a mesh network — everything is secured.
🗝️ The Three Keys — DevKey, AppKey, NetKey
Bluetooth Mesh security is built around three distinct key types. Each protects a different layer of communication.
| 🔵 Network Key NetKey |
🟢 Application Key AppKey |
🔴 Device Key DevKey |
|---|---|---|
|
Shared by ALL nodes
Secures the network layer. Allows messages to be relayed through the mesh.
One NetKey = one mesh network
A node can know multiple NetKeys |
Shared by specific nodes
Secures application data. Only nodes with the AppKey can read the payload.
Lighting AppKey ≠ Door AppKey
Up to 251 AppKeys per model |
Only Config Client + one node
Secures key distribution. Used to safely send keys to a specific node.
Config Client knows all DevKeys
Protects against “trash-can attack” |
🏗️ Two-Layer Encryption — Network vs Application
Think of a mesh message like a sealed envelope inside another sealed envelope:
Why two layers? A relay node only needs to open the outer envelope to forward the message. It cannot open the inner envelope (no AppKey). So:
🔄 How Relay Nodes Stay Secure
Relay nodes increase the range of a mesh network by forwarding messages they receive. But how do we make sure they cannot tamper with or read application data?
message
Cannot read payload
(TTL–1)
and act
Network keys are widely distributed (every node in the network has them) to maximise the number of relay nodes. Application keys are shared only with nodes that need to act on the data.
🎯 Device Key (DevKey) — Node-to-Node Secure Channel
The DevKey is unique to each node. It is known only by the Configuration Client (the provisioning tool/app) and the node itself. It is used to securely deliver keys to individual nodes.
of ALL nodes
with DevKey(Node1)
(Node 1)
AppKey (door)
with DevKey(Node2)
(Node 2)
AppKey (light)
🔗 Key Binding — AppKey ↔ NetKey
An AppKey can only work with one specific NetKey. This link is called the key binding.
| NetKey Index | AppKey Index | Bound Application |
|---|---|---|
| 0 (Main Network) | 0 | 💡 Lighting control |
| 0 (Main Network) | 1 | 🔒 Door security |
| 0 (Main Network) | 2 | 📊 Sensor reporting |
At the model level, each server model has a set of AppKeys bound to it. This means the model will only accept messages encrypted with those specific keys. Up to 251 AppKeys can be bound to a single model.
🕵️ Obfuscation — Hiding Who Is Talking
Even though messages are encrypted, an attacker monitoring the radio could still track devices by watching which source address (SRC) sends messages over time. Bluetooth Mesh prevents this with obfuscation.
| Without Obfuscation | With Obfuscation |
|---|---|
|
Packet 1: SRC=0x0023, SEQ=0100
Packet 2: SRC=0x0023, SEQ=0101 Packet 3: SRC=0x0023, SEQ=0102 ⚠️ Attacker can track: “Device 0x0023 is active at these times and locations”
|
Packet 1: SRC=[AES encrypted]
Packet 2: SRC=[AES encrypted] Packet 3: SRC=[AES encrypted] ✅ Attacker cannot correlate packets to a specific node
|
Obfuscation uses AES encryption to encrypt the source address, sequence numbers, and other header fields with a derived Privacy Key (derived from the NetKey). The receiving node knows the Privacy Key so it can reverse the obfuscation and read the actual header.
🏷️ Key Identifiers — Efficient Key Lookup
A node may have many AppKeys and NetKeys. When a message arrives, the node needs to know which key to use to decrypt it. Trying all keys would be wasteful. Enter Key Identifiers.
A Key Identifier (Key ID) is a short value derived from the key itself using a key derivation function. It is included in the message header. The node checks the Key ID first to narrow down candidates, then tries only those matching keys.
DST: 0xC001
Payload: [encrypted]
| AppKey Idx | Key ID bits | Match? |
|---|---|---|
| AppKey 0 | 0x15 | ❌ skip |
| AppKey 1 | 0x2A | ✅ try! |
| AppKey 2 | 0x3F | ❌ skip |
If a message arrives with an unknown Key ID, the node immediately discards it without spending time trying to decrypt. This is efficient and also a first line of defence against unknown/rogue messages.
💻 BlueZ in Action — Managing Keys
Here is how you manage NetKeys, AppKeys, and model bindings using mesh-cfgclient in BlueZ.
Step 1: Add a new Application Key to the network (provisioner side)
# The primary NetKey (index 0) is created during provisioning.
# Add a new AppKey bound to NetKey 0
[mesh-cfgclient]# appkey-add 0
# Output: Added AppKey: index=1, bound to NetKey index=0
Step 2: Distribute the AppKey to a node using its Device Key
# Target node with unicast address 0x0001
[mesh-cfgclient]# target 0x0001
# Send AppKey Add message — internally uses the node's DevKey for security
# Command: appkey-add <net_key_index> <app_key_index>
[mesh-cfgclient]# appkey-add 0 1
# Output: AppKey Add Status: success
# The node 0x0001 now has AppKey index=1 in its key store
Step 3: Bind the AppKey to a specific model on the node
# Bind AppKey index=1 to Generic Power Level Server (0x1306) on element 0x0001
# Command: bind <element_addr> <app_key_index> <model_id>
[mesh-cfgclient]# bind 0x0001 1 0x1306
# Output: Model App Bind Status: success
# Now the Generic Power Level Server on Socket 1 ONLY accepts
# messages encrypted with AppKey index=1
Step 4: Verify key bindings — get model app status
# Check which AppKeys are bound to the model
[mesh-cfgclient]# model-app-get 0x0001 0x0001 0x1306
# Output:
# Model App Get: element=0x0001, model=0x1306
# Bound AppKeys: [1] <--- only AppKey index 1 is bound
Step 5: Add a separate AppKey for door security (isolated from lighting)
# Add AppKey index=2 for door security — bound to the same NetKey 0
[mesh-cfgclient]# appkey-add 0 # creates index=2
# Distribute to door lock node at 0x0050
[mesh-cfgclient]# target 0x0050
[mesh-cfgclient]# appkey-add 0 2
# Bind to the door model on the lock
[mesh-cfgclient]# bind 0x0050 2 0x1000 # Generic OnOff Server on door
# The light bulb (0x0001) was NEVER given AppKey index=2
# So it can NEVER decrypt or act on door control messages
📝 Quick Summary
| Concept | Protects | Known By |
|---|---|---|
| NetKey | Network layer (routing + relay) | All nodes in the network |
| AppKey | Application data payload | Only nodes for that application |
| DevKey | Key distribution to each node | Config Client + that node only |
| Key Binding | Ties AppKey to a NetKey | One AppKey → one NetKey only |
| Obfuscation | Source address, SEQ (privacy) | Uses Privacy Key from NetKey |
| Key Identifier | Efficient key selection | Derived from key via KDF |
🎉 You’ve Completed the Bluetooth Mesh Foundations Series!
You now understand how mesh devices are structured (Elements & Models), how they communicate (Pub-Sub), and how everything stays secure (Keys & Obfuscation).
