ZigBee

# This is the "well-known" zigbee2mqtt key
# The ikea key is not known, might be per gateway
nwk_key = b"\x01\x03\x05\x07\x09\x0b\x0d\x0f\x00\x02\x04\x06\x08\x0a\x0c\x0d"

IEEE 802.15.4 header
| 2  | Frame Control Field
           2:0 frame type 0 = beacon, 1 = data, 2 = ack, 3 = cmd
           6 pan compression
           11:10 dest mode
           13:12 src mode
| 1  | Sequence
| 2? | Destination PAN
| 2? | Destination short address
| 8? | Destination long address
| 2? | Source PAN
| 2? | Source short address
| 8? | Source long address
| ?  | Payload

Command Packet
| 1  | Type 4 = data request, 7 = beacon request

Data Packet
| 2  | Frame Control Field
          1:0 type 0 = data, 1 = command, 2 = rerved, 3 = inter-pan
          9 security
          11 extended destination present
          12 extended source present
| 2  | Destination
| 2  | Source
| 1  | Radius
| 1  | Sequence
| 8? | Extended destination
| 8? | Extended source (if not present, use the one in the outer header?)
| 6-18? | Security header
| ?  | Payload (encrypted if security header is present)
| 4? | Encrypted Message Integrity Code (MIC if the security header is present)

Security header
| 1  | Security Control Field (this is often not right?)
           5 extended nonce (means the source is in the header)
| 4  | Sequence Number
| 8? | Source address (if extended nonce is set)
| 1  | Key sequence (always 0?)

Payload and MIC decryption:

key = keyring[key_seq](key_seq)
K = [ 1, src_addr[0:8](0:8), sequence[0:4](0:4), 0, 0 ]
mic = AES(key, K) ^ EncryptedMIC[0:4](0:4)
for i range(0, payload_len, 16):
        K[15](15) += 1
        message[i:i+16](i:i+16) = AES(key, K) ^ payload[i:i+16](i:i+16)
Message integrity validation: CCM\* is messy. ``` K = [ 1, src_addr[0:8](0:8), sequence[0:4](0:4), 0, 0 ] auth_data = data_packet[0:key_sequence](0:key_sequence) l_a = len(auth_data) auth_data = [ l_a >> 8, l_a >> 0, auth_data, padding to 16 bytes ] b0 = [ ?, src_addr[0:8](0:8), sequence[0:4](0:4), l, l_m >> 8, l_m >> 0 ] X = AES(key, b0) for i in range(0, l_a, 16): X ^= AES(Key, auth_data[i:i+16](i:i+16)) for i in range(0, l_m, 16): X ^= AES(Key, message[i:i+16](i:i+16)) if X[0:4](0:4) == mic[0:4](0:4): # hurrah!