I am working with a fork of scapy (a Python packet manipulation tool) called scapy-com. This implements 802.15.4 and Zigbee parsing/manipulation, amongst other protocols.
A quirk of the Zigbee protcol is found in the network level security header. Initially, the security level (which defines the encryption and length of message integrity code) is set correctly, but is then set to 0 (no encryption) before it is sent. From the spec:
The security level sub-field of the security control field shall be over-written by the 3-bit all-zero string '000'
The spec can be found here. The relevant section is "4.3.1.1 Security Processing of Outgoing Frames".
This means that packet captures indicate that no encryption or message integrity code is in use. The security level must be communicated out-of-band.
scapy-com doesn't deal with this. It naively parses the security level and sets the length of the MIC to 0. The code that does this is:
def util_mic_len(pkt):
''' Calculate the length of the attribute value field '''
# NWK security level 0 seems to implicitly be same as 5
if ( pkt.nwk_seclevel == 0 ): # no encryption, no mic
return 0
elif ( pkt.nwk_seclevel == 1 ): # MIC-32
return 4
elif ( pkt.nwk_seclevel == 2 ): # MIC-64
return 8
elif ( pkt.nwk_seclevel == 3 ): # MIC-128
return 16
elif ( pkt.nwk_seclevel == 4 ): # ENC
return 0
elif ( pkt.nwk_seclevel == 5 ): # ENC-MIC-32
return 4
elif ( pkt.nwk_seclevel == 6 ): # ENC-MIC-64
return 8
elif ( pkt.nwk_seclevel == 7 ): # ENC-MIC-128
return 16
else:
return 0
The project that uses scapy-com attempts to deal with this by setting the security level to 5:
#TODO: Investigate and issue a different fix:
# https://code.google.com/p/killerbee/issues/detail?id=30
# This function destroys the packet, therefore work on a copy - @cutaway
pkt = pkt.copy() #this is hack to fix the below line
pkt.nwk_seclevel=5 #the issue appears to be when this is set
mic = pkt.mic
However, this doesn't work - the message integrity code has already been set. I have worked around this by simply altering the util_mic_len function to set the mic length correctly.
The question is, how should the Zigbee parser be changed so that altering the nwk_seclevel after the initial dissection causes the mic length to be updated?
I can see two solutions:
- Change the scapy-com code so that changing nwk_seclevel automatically changes the mic length.
- Re-dissect the packets from outside scapy-com as they are changed.
The issue with 1 is I have no idea about how to go about it.
The issue with 2 is that I have some idea but can't get it to work - I can't work out how to call dissect on a packet after it has been loaded. Calling pkt.dissect(pkt) seems to not work and looks odd.
What is the best or recommended solution here?