Why Do We Need Key Refresh?
Imagine you have a smart home with 20 lights, locks, and sensors all talking over Bluetooth Mesh. One day, you remove a device — maybe a bulb you sold or a sensor that got stolen. That removed device still knows the encryption keys your network uses. If someone gets their hands on it, they could spy on your mesh traffic or even control your devices.
This is called the “trash-can attack” — someone fishes a discarded device out of the trash and uses its stored keys to attack your live network.
The Key Refresh Procedure solves this by rotating all network keys across the entire mesh, so the old device’s keys become useless.
Key Terms
In Bluetooth Classic, every device has a unique hardware address called BD_ADDR. In Bluetooth Mesh, that’s not required. Instead, every node gets a 128-bit number called a Device UUID.
During the Key Refresh procedure, the Configuration Client uses the device key (established during provisioning) to securely send new keys to each specific node — this is why every node’s UUID must be unique.
| Node | Device UUID (128-bit) | Device Key (from provisioning) |
| Light Bulb A | 550e8400-e29b-41d4-… | DevKey_A |
| Door Lock B | 6ba7b810-9dad-11d1-… | DevKey_B |
| Sensor C (removed) | 6ba7b811-9dad-11d1-… | ⚠ Blacklisted |
Each node has a unique UUID; blacklisted nodes won’t receive new keys
The whole process happens in three clean phases. The network never goes fully offline — devices keep communicating throughout. Here’s the big picture:
| Normal Operation |
→ | Phase 1 Key Distribution |
→ | Phase 2 Use New Keys |
→ | Phase 3 Revoke Old Keys |
→ | Normal Operation |
|
| TX Key: | Key A1 | Key A1 | Key A2 | Key A2 | Key A2 | ||||
| RX Key: | Key A1 | A1 + A2 | A1 + A2 | Key A2 | Key A2 |
A1 = old key | A2 = new key | Purple = dual-key receive window
Notice that during Phases 1 and 2, nodes accept both old and new keys for receiving. This is the transition window that keeps the network alive while all nodes slowly update.
The Configuration Client (think of it as the mesh network manager — typically a phone or gateway running BlueZ mesh tools) starts Phase 1. It decides which nodes will get the new keys and which ones will be blacklisted.
It sends two types of config messages to each non-blacklisted node:
- Config NetKey Update — delivers the new network key
- Config AppKey Update — delivers new application key(s) bound to the NetKey
| Config Client | Node (e.g. Light Bulb) | |
| Sends Config NetKey Update {Device Key encrypted} |
▶
|
Stores New NetKey (Key A2) Still TXing with Key A1 |
| Sends Config AppKey Update {Device Key encrypted} |
▶
|
Stores New AppKey RX accepts A1 and A2 |
| Confirms all nodes updated? | ✓ Phase 1 Complete → move to Phase 2 |
Low Power Nodes (LPN) caution: LPNs sleep most of the time and only wake up to poll their Friend node. The Config Client must check the PollTimeout timer and retry key delivery accordingly — LPNs can take much longer to receive the new keys.
Using BlueZ’s mesh tools, you can interact with the Configuration Client model via D-Bus. Here’s how to inspect and trigger key operations:
# Start BlueZ mesh daemon (ensure bluetooth.service is running)
sudo systemctl start bluetooth
# Launch interactive mesh-cfgclient tool
sudo mesh-cfgclient
# Inside mesh-cfgclient shell:
# List known nodes and their addresses
list-nodes
# Get the current key refresh phase for a node at address 0x0005
get-composition 0005
# Send a NetKey Update to node 0x0005 (Phase 1 trigger)
# net-update <node-addr> <net-index>
net-update 0005 0000
# Send AppKey Update bound to NetKey index 0x0000
# app-update <node-addr> <net-index> <app-index>
app-update 0005 0000 0000
# Once all nodes are updated, push Phase 2 beacon
# key-refresh-phase <node-addr> <net-index> <transition>
# transition=2 moves to Phase 2
key-refresh-phase 0005 0000 2
# transition=3 triggers Phase 3 (revoke old keys)
key-refresh-phase 0005 0000 3
Note: The exact CLI commands depend on your BlueZ version. The underlying D-Bus interface is org.bluez.mesh.Management1. For scripted automation, use the Python bluetooth-mesh library which wraps these D-Bus calls.
// Python bluetooth-mesh library example (Phase 1 via D-Bus)
// Install: pip3 install bluetooth-mesh
import asyncio
from bluetooth_mesh.application import Application
from bluetooth_mesh.messages.config import ConfigNetKeyUpdateMessage
async def send_netkey_update(app, node_addr, net_index, new_key_bytes):
# Build the Config NetKey Update message
msg = ConfigNetKeyUpdateMessage.build(dict(
net_key_index=net_index,
net_key=new_key_bytes
))
# Send using device key of target node
await app.send_dev(
destination=node_addr,
app_index=0,
data=msg
)
print(f"NetKey Update sent to node 0x{node_addr:04X}")
asyncio.run(send_netkey_update(app, 0x0005, 0x0000, new_key))
Once every non-blacklisted node has the new keys stored, the Config Client kicks off Phase 2. It does this in one of two ways:
| Method | How It Works |
| Secure Network Beacon | Config Client broadcasts a beacon secured with the new NetKey with Key Refresh Flag = 1. All nodes that receive this switch to Phase 2. |
| Config Key Refresh Phase Set (transition=0x02) | Config Client sends this unicast message directly to specific nodes to explicitly move them to Phase 2. |
In Phase 2, each node:
- Transmits packets using new keys only
- Receives packets using both old and new keys (so no one gets locked out mid-transition)
- Only processes Secure Network Beacons secured with the new NetKey
| Direction | Key Used in Phase 2 |
| 📤 Transmit (TX) | Key A2 (new only) |
| 📥 Receive (RX) | Key A1 + Key A2 (both) |
This is the final step. The Config Client sends another Secure Network Beacon (or a Config Key Refresh Phase Set with transition=3) signalling all nodes to drop the old keys entirely.
After Phase 3:
- All nodes transmit and receive using only Key A2
- The blacklisted nodes still only have Key A1 — they are now completely cut off
- The mesh is back to normal operation, just with fresh keys
|
Before Key Refresh
All nodes know Key A1
⚠ Removed node also has Key A1
|
→ |
After Key Refresh
All active nodes know Key A2
✓ Removed node locked out (still has A1 only)
|
# Inside mesh-cfgclient: trigger Phase 3 (revoke old keys)
# This sends Config Key Refresh Phase Set with transition=3
# which tells each node to permanently drop Key A1
key-refresh-phase 0005 0000 3
# Alternatively, the Secure Network beacon with Key Refresh Flag=0
# transmitted using the NEW key automatically triggers Phase 3
# on any node that receives it while still in Phase 1
# Verify the current key refresh state of a node
get-key-refresh-phase 0005 0000
# Expected output: Phase 0 (normal operation with new key)
Here is how the four states (Phase 0 through Phase 3) connect to each other and what triggers each transition:
| From State | To State | Trigger / Message |
| Phase 0 (Normal) | Phase 1 | Config NetKey Update message (Device Key encrypted) |
| Phase 1 | Phase 2 | Secure Network Beacon with Key Refresh Flag=1 (new NetKey) OR Config Key Refresh Phase Set transition=2 |
| Phase 1 | Phase 3 | Secure Network Beacon with Key Refresh Flag=0 (new NetKey) — skips Phase 2 |
| Phase 2 | Phase 3 | Config Key Refresh Phase Set transition=3 OR Secure Network Beacon with Key Refresh Flag=0 (new NetKey) |
| Phase 3 | Phase 0 (Normal) | Old keys revoked — back to single key operation with Key A2 |
The spec has strict rules about when update messages are valid. Violating these causes errors:
| Message | Valid When | Generates Error When |
| Config NetKey Update | Phase 0 (new key value) OR Phase 1 (same key value) | Phase 2 or Phase 3 |
| Config AppKey Update | Phase 1 only (same AppKey value on valid index) | Normal operation, Phase 2, Phase 3, or Phase 1 with different AppKey value |
| Key Refresh vs IV Update | Both can run simultaneously — completely independent | Neither affects the other |
Also: if a node is in Phase 1 and receives a Secure Network Beacon with Key Refresh Flag=0 using the new NetKey, it jumps straight to Phase 3 — skipping Phase 2 entirely. This is an optimisation for smaller networks.
Explore More on EmbeddedPathashala
Continue learning Bluetooth Mesh, BLE Stack internals, and Linux Kernel programming
