Bluetooth Mesh: IV Update & Key Refresh

 

Bluetooth Mesh: IV Update & Key Refresh
How mesh networks stay secure β€” key rotation, IV Index management, and node recovery explained simply
πŸ“– Spec Pages 129–132
πŸ”‘ Key Refresh
πŸ”’ IV Index
βš™οΈ BlueZ Examples

What This Post Covers

In a Bluetooth Mesh network, security does not stop at provisioning. Two ongoing maintenance procedures keep the network healthy and secure over its lifetime:

  • Key Refresh β€” rotate network and application keys when a node is removed or compromised
  • IV Update β€” refresh the IV Index to prevent nonce reuse in AES-CCM encryption

Both procedures involve all nodes in the mesh coordinating state transitions through beacon messages. This post breaks them down step by step.

Keywords

IV Index IV Update Flag Key Refresh Phase NetKey Secure Network Beacon Sequence Number AES-CCM Nonce IV Index Recovery Node Removal Blacklisting BlueZ mesh-cfgclient Primary Subnet

1. Key Refresh β€” Phase 3: Revoking Old Keys

Before we talk about IV Update, let us finish the Key Refresh story. The Key Refresh procedure has three phases. By the time we reach Phase 3, every non-blacklisted node already has the new keys installed. Phase 3 is purely about telling everyone to stop using the old keys.

How Phase 3 works:

Actor Action Key Refresh Flag in Beacon
Configuration Client Sends Secure Network beacon with Key Refresh flag = 0 using NEW NetKey, OR sends Config Key Refresh Phase Set (Transition = 0x03) 0
Relay / Friend node Forwards beacons for new NetKey with flag = 0 0
Any mesh node On receiving beacon/Friend Update with flag=0 β†’ revokes old keys, uses only new keys from now on 0 (after revoke)

Key Refresh state after each phase:

Phase 1
Distribute New Keys
β†’
Phase 2
Use New Keys for TX
β†’
Phase 3
Revoke Old Keys
β†’
Key Refresh State = 0
Complete

Important notes:

  • After revocation, the node ignores any beacon still using the new NetKey with flag=1
  • Low Power nodes may lag behind because they depend on Friend nodes. The Configuration Client must account for this
  • A newly provisioned node that never had the old keys simply skips the revocation step

BlueZ: Trigger Phase 3 via mesh-cfgclient

# Connect to the mesh daemon
mesh-cfgclient

# Inside mesh-cfgclient: send Key Refresh Phase Set with transition 0x03
# This tells the target node to move to Phase 3 and revoke old keys
# Syntax: kr-phase-set <node_unicast_addr> <net_index> <transition>
kr-phase-set 0x0005 0x0000 3

# Confirm the current phase on the node
kr-phase-get 0x0005 0x0000

The kr-phase-set command maps to the Config Key Refresh Phase Set message in the spec. Transition value 3 = move to Phase 3.

2. Why IV Update Exists β€” The Nonce Problem

Bluetooth Mesh uses AES-CCM encryption at both the application and network layers. AES-CCM requires a unique value called a nonce for every single encrypted packet. If the same nonce is ever used twice with the same key, an attacker can break the encryption.

What makes up a Mesh nonce?

IV Index
4 bytes
Sequence Number (SEQ)
3 bytes
Source Address
2 bytes
Other fields
Changes via IV Update Monotonically increasing per node, resets to 0 after IV Update Unique per node

The SEQ number is only 3 bytes = about 16 million possible values. A busy node sending many messages per second will eventually exhaust this range. When that happens, the IV Index must be incremented so SEQ can safely reset to zero without reusing a nonce combination.

⚠️ Rule: The transition from Normal Operation to IV Update in Progress must happen at least 96 hours before SEQ numbers are exhausted. Plan ahead!

3. IV Update β€” Two States Explained

The IV Update procedure uses two operating states. The transition between them is controlled by the IV Update Flag carried in Secure Network beacons and Friend Update messages.

🟒 Normal Operation
IV Update Flag = 0
  • Transmit using: current IV Index (n)
  • Accept messages from: n and nβˆ’1
  • Beacon flag: 0
🟑 IV Update in Progress
IV Update Flag = 1
  • IV Index is now incremented to m = n+1
  • Transmit using: mβˆ’1 (old index) β€” so neighbours still understand
  • Accept messages from: mβˆ’1 and m
  • Beacon flag: 1

Summary Table (Table 3.57 from spec):

IV Index IV Update Flag State IV Index Accepted IV Index Used for TX
n 0 Normal nβˆ’1, n n
m (m=n+1) 1 In Progress mβˆ’1, m mβˆ’1
m 0 Normal (complete) mβˆ’1, m m

The key design insight: During the “In Progress” state the node still transmits using the OLD index (mβˆ’1). This gives every other node in the network time to catch up and learn the new IV Index before the transmitting node actually switches over.

4. Timing Rules β€” The 96/144/192 Hour Windows

The IV Update procedure has strict time constraints to prevent rapid cycling and ensure all nodes get enough time to synchronise.

IV Update timeline for a single cycle:

Normal Operation
Min 96 hours
(before starting update)
IV Update in Progress
96 – 144 hours
(must return to Normal within 144h)
Back to Normal
SEQ reset to 0
New IV Index active

Rule Value Why
Min time in Normal before starting IV Update 96 hours Prevents rapid cycling
Max time allowed in “IV Update in Progress” 144 hours Node must return to Normal before 144h
Min time between two complete IV Updates 192 hours Safety gap between full cycles
Max offline time before reproviΒ­sioning required 48 weeks IV Index can only jump max +42 steps
When returning to Normal Operation: The node resets its sequence number to 0x000000. This is safe because the IV Index has now incremented, so old nonce values cannot repeat.

5. Worked Example β€” Step by Step with Real IV Values

Let us trace through a complete IV Update cycle using the example values from the specification.

Step State IV Index (current) TX using Accept RX Flag in beacon
1. Start Normal 0x00101847 0x00101847 0x00101846, 0x00101847 0
2. IV Update triggered (β‰₯96h) In Progress 0x00101848 (incremented) 0x00101847 (mβˆ’1) 0x00101847, 0x00101848 1
3. Return to Normal (96–144h later) Normal 0x00101848 (stays) 0x00101848 0x00101847, 0x00101848 0

Notice at step 2: even though the stored IV Index is 0x00101848, the node still sends using the old 0x00101847. This ensures nodes that are still in Normal Operation (using the old index) can still receive packets from this node.

BlueZ: Observe IV Index in mesh daemon logs

# Run bluetoothd with mesh daemon in verbose mode
sudo bluetoothd -n -d &

# Watch syslog for IV Update events
sudo journalctl -u bluetooth -f | grep -i "iv\|mesh"

# Or check the mesh node storage file (BlueZ stores mesh state in JSON)
cat /var/lib/bluetooth/<adapter_addr>/<node_uuid>/node.json | python3 -m json.tool | grep -i "iv"
# Example node.json excerpt showing IV Index
{
  "IVIndex": 1053767,        /* 0x00101847 in decimal */
  "IVUpdate": false,         /* false = Normal Operation */
  "SequenceNumber": 4096
}

6. IV Index Recovery β€” Coming Back After a Long Absence

Imagine a sensor node that has been sleeping for several months. When it wakes up, the mesh network may have already completed multiple IV Update cycles. The node’s stored IV Index is now stale β€” it cannot communicate until it learns the current IV Index.

Recovery flow:

Node wakes up
Old IV Index stored
β†’
Listen for Secure
Network Beacon

~5 seconds avg wait
β†’
Authenticate beacon
Verify using known NetKey
β†’
Update IV Index & state
Resume communication
Beacon IV vs Known IV Node Action
Beacon IV > known IV + 1 Initiate IV Index Recovery β€” jump directly to beacon IV Index
Beacon IV = known IV + 1, flag = 0 May update directly, or run recovery, or ignore beacon (node’s choice based on timing)
Beacon IV < known IV OR > known IV + 42 Ignore beacon β€” likely spoofed or the node has been offline >48 weeks β†’ must reprovision
Low Power Nodes: If the node does not have enough battery to listen for 5 seconds after wake-up, it must poll its Friend node at least once every 96 hours to stay current.
After recovery: The normal 96-hour waiting limits do not apply. The node can start communicating immediately using the recovered IV Index.

7. Node Removal β€” Kicking a Node Out Securely

Sometimes you need to permanently remove a node β€” it was stolen, it failed, or it belongs to an employee who left. Simply powering it off is not enough. The other nodes still trust its credentials.

The correct removal flow:

Step Action Mechanism
1 Blacklist the node Exclude it from Key Refresh procedure
2 Run Key Refresh (Phases 1β†’2β†’3) All other nodes get new keys; removed node gets nothing
3 Removed node can no longer decrypt mesh traffic It only has old keys that are now revoked
4 (optional) Reuse its unicast address for a new node Only after current IV Index has been updated via IV Update procedure
Why wait for IV Update before reusing unicast addresses? Sequence numbers (SEQ) are per-node. A new node at the same address must start from 0. If the IV Index has not changed, other nodes might reject its messages as “already seen” (replay attack protection). Incrementing the IV Index effectively resets replay caches.

BlueZ: Remove a node from a mesh network

# In mesh-cfgclient, reset the node (sends Config Node Reset message)
# This instructs the node to remove itself; works only if node is reachable
node-reset 0x0005

# If node is unreachable (lost/stolen), remove it locally from Provisioner DB
# BlueZ stores provisioner data in /var/lib/bluetooth/<addr>/
# Edit or delete the node entry from the mesh network JSON config
# Then run Key Refresh manually for remaining nodes

# Verify it's gone from the provisioner's known nodes list
list-nodes

8. IV Update Test Mode β€” For Lab Testing

In production, you must wait 96 hours between IV Update state changes. That makes automated testing painful. The spec defines an IV Update test mode specifically for the Bluetooth Qualification Process.

Signal Effect
Transit to IV Update in Progress Node moves to In Progress state immediately, ignoring the 96h wait
Transit to Normal Node moves back to Normal state immediately, ignoring the 96h wait

Test mode is activated locally β€” via a hardware pin or software interface on the device under test. All other mesh behaviour stays exactly the same; only the 96-hour gate is removed.

BlueZ: Enable IV Update test mode

# BlueZ exposes IV Update test mode via the D-Bus mesh network interface
# This is typically done programmatically using gdbus or a Python script

# Using gdbus command line:
gdbus call --system \
  --dest org.bluez.mesh \
  --object-path /org/bluez/mesh \
  --method org.bluez.mesh.Network1.SetIVUpdateTestMode \
  true

# To trigger IV Update in Progress immediately (test mode active):
gdbus call --system \
  --dest org.bluez.mesh \
  --object-path /org/bluez/mesh/node/<node_path> \
  --method org.bluez.mesh.Node1.SetIVUpdateTestMode \
  true
# Python3 snippet using dbus module (for automated test scripts)
import dbus

bus = dbus.SystemBus()
mesh_mgr = bus.get_object('org.bluez.mesh', '/org/bluez/mesh')
iface = dbus.Interface(mesh_mgr, 'org.bluez.mesh.Network1')

# Enable test mode
iface.SetIVUpdateTestMode(dbus.Boolean(True))
print("IV Update test mode enabled β€” 96h limits removed")

9. Quick Reference β€” What Happens When
Event Node Action
Receive beacon: IV Update flag = 1 while in Normal Transition to IV Update in Progress as soon as possible
Receive beacon: IV Update flag = 0 while in Progress Transition to Normal as soon as possible
Have an unsegmented Segmented msg in flight during IV Update complete Defer state change until ACK received or timeout β€” SEQ reset would break SeqAuth
Node joins a network that is in Normal Operate in Normal for β‰₯96 hours before initiating IV Update
Node joins a network that is in IV Update in Progress Given new IV Index, operate in Normal for β‰₯96 hours
Node offline >48 weeks Must be reprovisioned β€” IV Index gap too large

Continue Learning Bluetooth Mesh

This post is part of the EmbeddedPathashala Bluetooth Classic & Mesh series. Next up: Mesh Provisioning and the OOB authentication process.

🏠 Back to Home πŸ“‘ All Bluetooth Posts

Leave a Reply

Your email address will not be published. Required fields are marked *