Package killerbee :: Module dot154decode
[hide private]
[frames] | no frames]

Source Code for Module killerbee.dot154decode

  1  import struct 
  2  from Crypto.Cipher import AES 
  3   
  4   
  5  ## Constants for packet decoding fields 
  6  # Frame Control Field 
  7  DOT154_FCF_TYPE_MASK            = 0x0007  #: Frame type mask 
  8  DOT154_FCF_SEC_EN               = 0x0008  #: Set for encrypted payload 
  9  DOT154_FCF_FRAME_PND            = 0x0010  #: Frame pending 
 10  DOT154_FCF_ACK_REQ              = 0x0020  #: ACK request 
 11  DOT154_FCF_INTRA_PAN            = 0x0040  #: Intra-PAN activity 
 12  DOT154_FCF_DADDR_MASK           = 0x0C00  #: Destination addressing mode mask 
 13  DOT154_FCF_VERSION_MASK         = 0x3000  #: Frame version 
 14  DOT154_FCF_SADDR_MASK           = 0xC000  #: Source addressing mask mode 
 15   
 16  # Frame Control Field Bit Shifts 
 17  DOT154_FCF_TYPE_MASK_SHIFT      = 0  #: Frame type mask mode shift 
 18  DOT154_FCF_DADDR_MASK_SHIFT     = 10  #: Destination addressing mode mask 
 19  DOT154_FCF_VERSION_MASK_SHIFT   = 12  #: Frame versions mask mode shift 
 20  DOT154_FCF_SADDR_MASK_SHIFT     = 14  #: Source addressing mask mode shift 
 21   
 22  # Address Mode Definitions 
 23  DOT154_FCF_ADDR_NONE            = 0x0000  #: Not sure when this is used 
 24  DOT154_FCF_ADDR_SHORT           = 0x0002  #: 4-byte addressing 
 25  DOT154_FCF_ADDR_EXT             = 0x0003  #: 8-byte addressing 
 26   
 27  DOT154_FCF_TYPE_BEACON          = 0     #: Beacon frame 
 28  DOT154_FCF_TYPE_DATA            = 1     #: Data frame 
 29  DOT154_FCF_TYPE_ACK             = 2     #: Acknowledgement frame 
 30  DOT154_FCF_TYPE_MACCMD          = 3     #: MAC Command frame 
 31   
 32  DOT154_CRYPT_NONE               = 0x00    #: No encryption, no MIC 
 33  DOT154_CRYPT_MIC32              = 0x01    #: No encryption, 32-bit MIC 
 34  DOT154_CRYPT_MIC64              = 0x02    #: No encryption, 64-bit MIC 
 35  DOT154_CRYPT_MIC128             = 0x03    #: No encryption, 128-bit MIC 
 36  DOT154_CRYPT_ENC                = 0x04    #: Encryption, no MIC 
 37  DOT154_CRYPT_ENC_MIC32          = 0x05    #: Encryption, 32-bit MIC 
 38  DOT154_CRYPT_ENC_MIC64          = 0x06    #: Encryption, 64-bit MIC 
 39  DOT154_CRYPT_ENC_MIC128         = 0x07    #: Encryption, 128-bit MIC 
 40   
41 -class Dot154PacketParser:
42 - def __init__(self):
43 ''' 44 Instantiates the Dot154PacketParser class. 45 ''' 46 47 # State values for AES-CTR mode 48 self.__crypt_blockcntr = 1 49 self.__crypt_A_i = [] 50 return
51
52 - def __crypt_counter(self):
53 ''' 54 Used for AES-CTR mode after populating self.__crypt_A_i 55 Don't call this directly. Just don't. 56 ''' 57 retindex = self.__crypt_blockcntr 58 self.__crypt_blockcntr += 1 59 return self.__crypt_A_i[retindex]
60
61 - def decrypt(self, packet, key):
62 ''' 63 Decrypts the specified packet. Returns empty string if the packet is 64 not encrypted, or if decryption MIC validation fails. 65 66 @type packet: String 67 @param packet: Packet contents. 68 @type key: String 69 @param key: Key contents. 70 @rtype: String 71 @return: Decrypted packet contents, empty string if not encrypted or if 72 decryped MIC fails validation. 73 ''' 74 75 # Retrieve the data payload from the packet contents 76 encpayload = packet[-self.payloadlen(packet):] 77 78 if ord(encpayload[0]) != DOT154_CRYPT_ENC_MIC64: 79 raise Exception("Unsupported security level in packet: 0x%02x." % ord(encpayload[0])) 80 81 if len(key) != 16: 82 raise Exception("Invalid key length (%d)." % len(key)) 83 84 # Encrypted content is: 85 # Sec Level | 4-byte counter | Flags | Ciphertext | Encrypted 8-byte MIC 86 if self.payloadlen(packet) < 15: 87 raise Exception("Payload length too short (%d)." % self.payloadlen(packet)) 88 89 nonce = self.nonce(packet) 90 91 # c = ciphertext payload including trailing 8-byte encrypted MIC 92 c = encpayload[-9:] 93 94 95 # 1. Parse C||U where U is the right-most bytes for MIC and C is the 96 # remaining bytes (representing encrypted packet payload content) 97 C = c[0:-8] 98 U = c[-8:] 99 100 # 2. Form cipherText by padding C to a block size 101 cipherText = C + ("\x00" * (16 - len(C)%16)) 102 103 # 3. Form 1-byte flags field = 01 104 # XXX will vary when L changes 105 flags = "\x01" 106 107 # 4. Define 16-octet A_i consisting of: 108 # Flags || Nonce || 2-byte counter i for i=0,1,2, ... 109 # A[0] is for authenticity check, A[1] is for the first block of data, 110 # A[2] is for the 2nd block of data, if C > 16 111 self.__crypt_A_i = [] 112 for i in xrange(0, (1+1+(len(C)/16))): 113 self.__crypt_A_i.append(flags + nonce + struct.pack(">H",i)) 114 115 # 5. Decrypt cipherText producing plainText (observed) 116 self.__crypt_blockcntr = 1 # Start at A[1] to decrypt 117 crypt = AES.new(key, AES.MODE_CTR, counter=self.__crypt_counter) 118 plainText = crypt.decrypt(cipherText)[0:len(C)] 119 120 # 6. Compute S_0 as E(Key, A[0]) 121 crypt = AES.new(key, AES.MODE_CBC) 122 S_0 = crypt.encrypt(self.__crypt_A_i[0]) 123 124 # 7. Compute MIC (T) observed as S_0 XOR U 125 T_obs = [] 126 for i in xrange(0,len(S_0[0:8])): 127 T_obs.append((ord(S_0[i]) ^ ord(U[i]))) 128 129 # Convert T_obs back into a string (please, I need Python help) 130 T_obs = ''.join(struct.pack("B",i) for i in T_obs) 131 132 # 8. Compute a over packet contents before ciphertext payload 133 # This is the 802.15.4 header,plus the security level, frame 134 # counter and flags byte (01) 135 hdrlen = self.hdrlen(packet) 136 a = packet[0:hdrlen] + packet[hdrlen:hdrlen+6] 137 138 # 9. Concatenate L(a) of 2-byte length a with a 139 addAuthData = struct.pack(">H",len(a)) + a 140 141 # 10. Pad addAuthData to an even block size 142 addAuthData += ("\x00" * (16 - len(addAuthData)%16)) 143 144 # 11. Form AuthData by concatenating addAuthData and PlaintextData 145 # Pad plainText to an even block size 146 plainTextPadded = plainText + ("\x00" * (16 - len(plainText)%16)) 147 148 authData = addAuthData + plainTextPadded 149 150 # 12. Perform authData transformation into B[0], B[1], ..., B[i] 151 B = "\x59" + nonce + "\x00\x01" + authData 152 153 # 13. Calculate the MIC (T) calculated with CBC-MAC 154 iv = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 155 for i in xrange(0, len(B)/16): 156 crypt = AES.new(key, AES.MODE_CBC, iv) 157 Bn = B[i*16:(i*16)+16] 158 iv = crypt.encrypt(Bn) 159 T_calc = iv[0:8] 160 161 # 14. Compare 162 if T_obs == T_calc: 163 return plainText 164 else: 165 return ""
166 167
168 - def pktchop(self, packet):
169 ''' 170 Chops up the specified packet contents into a list of fields. Does 171 not attempt to re-order the field values for parsing. ''.join(X) will 172 reassemble original packet string. Fields which may or may not be 173 present (such as the Source PAN field) are empty if they are not 174 present, keeping the list elements consistent, as follows: 175 FCF | Seq# | DPAN | DA | SPAN | SA | [Beacon Data] | PHY Payload 176 177 If the packet is a beacon frame, the Beacon Data field will be populated 178 as a list element in the format: 179 180 Superframe Spec | GTS Fields | Pending Addr Counts | Proto ID | Stack Profile/Profile Version | Device Capabilities | Ext PAN ID | TX Offset | Update ID 181 182 An exception is raised if the packet contents are too short to 183 decode. 184 185 @type packet: String 186 @param packet: Packet contents. 187 @rtype: list 188 @return: Chopped contents of the 802.15.4 packet into list elements. 189 ''' 190 pktchop = ['', '', '', '', '', '', [], ''] 191 192 pktchop[0] = packet[0:2] 193 194 # Sequence number 195 pktchop[1] = packet[2] 196 197 # Byte swap 198 fcf = struct.unpack("<H",pktchop[0])[0] 199 200 # Check if we are dealing with a beacon frame 201 if (fcf & DOT154_FCF_TYPE_MASK) == DOT154_FCF_TYPE_BEACON: 202 203 beacondata = ["", "", "", "", "", "", "", "", "", ""] 204 205 try: 206 207 # 802.15.4 fields, SPAN and SA 208 pktchop[4] = packet[3:5] 209 pktchop[5] = packet[5:7] 210 offset = 7 211 212 # Superframe specification 213 beacondata[0] = packet[offset:offset+2] 214 offset+=2 215 216 # GTS data 217 beacondata[1] = packet[offset] 218 offset+=1 219 220 # Pending address count 221 beacondata[2] = packet[offset] 222 offset+=1 223 224 # Protocol ID 225 beacondata[3] = packet[offset] 226 offset+=1 227 228 # Stack Profile version 229 beacondata[4] = packet[offset] 230 offset+=1 231 232 # Capability information 233 beacondata[5] = packet[offset] 234 offset+=1 235 236 # Extended PAN ID 237 beacondata[6] = packet[offset:offset+8] 238 offset+=8 239 240 # TX Offset 241 beacondata[7] = packet[offset:offset+3] 242 offset+=3 243 244 # Update ID 245 beacondata[8] = packet[offset] 246 offset+=1 247 248 except: 249 pass 250 251 pktchop[6] = beacondata 252 253 else: 254 # Not a beacon frame 255 256 # DPAN 257 pktchop[2] = packet[3:5] 258 offset = 5 259 260 # Examine the destination addressing mode 261 daddr_mask = (fcf & DOT154_FCF_DADDR_MASK) >> 10 262 if daddr_mask == DOT154_FCF_ADDR_EXT: 263 pktchop[3] = packet[offset:offset+8] 264 offset+=8 265 elif daddr_mask == DOT154_FCF_ADDR_SHORT: 266 pktchop[3] = packet[offset:offset+2] 267 offset+=2 268 269 # Examine the Intra-PAN flag 270 if (fcf & DOT154_FCF_INTRA_PAN) == 0: 271 pktchop[4] = packet[offset:offset+2] 272 offset+=2 273 274 # Examine the source addressing mode 275 saddr_mask = (fcf & DOT154_FCF_SADDR_MASK) >> 14 276 if daddr_mask == DOT154_FCF_ADDR_EXT: 277 pktchop[5] = packet[offset:offset+8] 278 offset+=8 279 elif daddr_mask == DOT154_FCF_ADDR_SHORT: 280 pktchop[5] = packet[offset:offset+2] 281 offset+=2 282 283 # Append remaining payload 284 if offset < len(packet): 285 pktchop[7] = packet[offset:] 286 287 return pktchop
288 289
290 - def hdrlen(self, packet):
291 ''' 292 Returns the length of the 802.15.4 header. 293 @type packet: String 294 @param packet: Packet contents to evaluate for header length. 295 @rtype: Int 296 @return: Length of the 802.15.4 header. 297 ''' 298 299 # Minimum size is 11 (2 bytes FCF + 1 byte SEQ + 2 bytes DPAN + 300 # 2 bytes DstAddr + 2 bytes SPAN + 2 bytes SrcAddr) 301 # XXX Need to validate this logic based on specification 302 if (len(packet) < 9): 303 raise Exception("Packet too small, %d bytes." % len(packet)) 304 305 # Start with minimum size, increase as needed based on FCF flags 306 plen = 9 307 308 # Byte swap 309 fcf = struct.unpack("<H",packet[0:2])[0] 310 311 # Examine the destination addressing mode 312 if (fcf & DOT154_FCF_DADDR_MASK) >> 10 == DOT154_FCF_ADDR_EXT: 313 plen += 6 # 8-byte addressing is in use, increasing addr 6 bytes 314 315 # Examine the source addressing mode 316 if (fcf & DOT154_FCF_SADDR_MASK) >> 14 == DOT154_FCF_ADDR_EXT: 317 plen += 6 # 8-byte addressing is in use, increasing addr 6 bytes 318 319 # Examine the Intra-PAN flag 320 if (fcf & DOT154_FCF_INTRA_PAN) == 0: 321 plen += 2 # Intra-PAN is false, source PAN 2-bytes is present 322 323 return plen
324
325 - def payloadlen(self, packet):
326 ''' 327 Returns the length of the 802.15.4 payload. 328 @type packet: String 329 @param packet: Packet contents to evaluate for header length. 330 @rtype: Int 331 @return: Length of the 802.15.4 payload. 332 ''' 333 return len(packet) - self.hdrlen(packet)
334
335 - def nonce(self, packet):
336 ''' 337 Returns the nonce of the 802.15.4 packet. Returns empty string for 338 unencrypted frames. 339 @type packet: String 340 @param packet: Packet contents to evaluate for nonce. 341 @rtype: String 342 @return: Nonce, empty when the frame is not encrypted. 343 ''' 344 345 # Byte swap 346 fcf = struct.unpack("<H",packet[0:2])[0] 347 348 if (fcf & DOT154_FCF_SEC_EN) == 0: 349 # Packet is not encrypted 350 return "" 351 352 # Nonce formation is Src Addr || Frame Counter || Security Level 353 pchop = self.pktchop(packet) 354 355 # SA is the 5th list element, reverse it 356 noncep1 = pchop[5][::-1] 357 358 # Retrieve the data payload from the packet contents 359 encpayload = packet[-self.payloadlen(packet):] 360 361 # First byte of encrypted payload is the security level 362 noncep3 = encpayload[0] 363 364 # The next 4 bytes of the encrypted payload is the frame counter, rev 365 noncep2 = encpayload[1:5][::-1] 366 367 return noncep1 + noncep2 + noncep3
368