Touchlink (Zigbee Light Link)
Proximity commissioning for Zigbee lighting: hands the network key over the air, wrapped in a key the specification publishes.
Touchlink is the proximity commissioning procedure of Zigbee Light Link (ZLL), later folded into Zigbee 3.0. You hold a controller close to a bulb, it joins. The convenience comes from a design decision that is also the attack: the network key is handed over the air, wrapped only in a key whose value is fixed for the whole ecosystem.
What it is
Touchlink frames are inter-PAN: they carry no network layer state, are addressed with 64-bit IEEE addresses and a PAN identifier of 0xFFFF, and are not encrypted at all. They ride profile 0xC05E on cluster 0x1000, the ZLL Commissioning cluster. The proximity is enforced by nothing but transmit power, which is why the attack is usually described as "touchlink from across the street with a better antenna".
A commissioning burst is short:
| Command | ID | Direction | Carries |
|---|---|---|---|
| Scan Request | 0x00 | initiator to all | inter-PAN transaction identifier |
| Scan Response | 0x01 | device to initiator | response identifier, key bitmask, current PAN |
| Identify Request | 0x06 | initiator to device | how long to blink |
| Network Start Request | 0x10 | initiator to device | key index, encrypted network key |
| Network Join Router Request | 0x12 | initiator to device | key index, encrypted network key |
| Reset to Factory New | 0x07 | initiator to device | nothing, and that is the point |
The key transport
The network key is wrapped twice, and neither wrap involves a secret the device chose. Both stages are plain AES-128 in ECB. The wrapping key is derived from two 32-bit identifiers expanded into a single 16-octet block, and the ZLL key selected by the frame's key index field; the network key is then wrapped under that derived key. The exact expansion order and the direction of each stage are spelled out in the ZLL specification, section 8.7.5.2.3, and implemented in the Z3sec research toolkit.
The two identifiers are the interesting part. The inter-PAN transaction identifier is in the frame that carries the key. The response identifier is not: it is in the Scan Response of the device the request was addressed to, which is why a capture of the key transport alone is not enough.
| Key index | ZLL key used | Status |
|---|---|---|
| 0 | development key, built from the two identifiers and two fixed four-letter strings | no secret at all |
| 4 | ZLL master key | leaked in 2015, still in use |
| 15 | ZLL certification key | published in the specification |
A device left in certification mode therefore transports its network key under a key anyone can look up, and the whole mesh follows.
Why it matters
Two separate problems sit here. The first is the key transport above: a passive listener who catches one commissioning burst holds the network key for good. The second is that touchlink commands need no authentication at all, so Reset to Factory New (0x07) is an unauthenticated remote wipe of any bulb in range, and Network Join Router Request lets an attacker move a device onto a network of their own. Ronen and Shamir's ZLL work, and the "IoT goes nuclear" bulb worm, both start here.
How to assess it
# every touchlink frame in a capture
tshark -r capture.pcap -Y 'zbee_zcl_general.touchlink'
# the fields the unwrap needs
tshark -r capture.pcap -Y 'zbee_zcl_general.touchlink' \
-T fields -e zbee_zcl_general.touchlink.transaction_id \
-e zbee_zcl_general.touchlink.key_index
Wireshark dissects the exchange completely, names the key index, and prints the encrypted network key. It stops there: no standard tool implements the unwrap, so you write it, with a handful of lines around an ECB cipher. Z3sec is the reference research implementation.
Pitfalls
- Pair on the transaction identifier AND the target. Several devices answer the same scan, each with a different response identifier, and only the one addressed by the key transport gives the right key.
- The identifiers are big endian inside the wrap, and little endian on the wire. Getting this backwards yields a plausible-looking 16-octet key that decrypts nothing.
- Verify, do not assume: the right network key makes the CCM* message integrity codes of the following frames verify. If none verify, your pairing or your endianness is wrong.
- Touchlink lives on the ZLL profile 0xC05E. The same cluster number under another profile is a different cluster.