Ejemplo n.º 1
0
def kbdecrypt(pkt, key=None, verbose=None):
    """Decrypt Zigbee frames using AES CCM* with 32-bit MIC"""
    if verbose is None:
        verbose = conf.verb
    if key == None:
        if conf.killerbee_nkey == None:
            log_killerbee.error(
                "Cannot find decryption key. (Set conf.killerbee_nkey)")
            return None
        key = conf.killerbee_nkey
    if len(key) != 16:
        log_killerbee.error(
            "Invalid decryption key, must be a 16 byte string.")
        return None
    elif not pkt.haslayer(ZigbeeSecurityHeader) or not pkt.haslayer(ZigbeeNWK):
        log_killerbee.error(
            "Cannot decrypt frame without a ZigbeeSecurityHeader.")
        return None
    try:
        import zigbee_crypt
    except ImportError:
        log_killerbee.error(
            "Could not import zigbee_crypt extension, cryptographic functionality is not available."
        )
        return None

    #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 = struct.unpack(">I", f['mic'])
    mic = pkt.mic

    f = pkt.getlayer(ZigbeeSecurityHeader).fields
    encrypted = f['data']

    sec_ctrl_byte = str(pkt.getlayer(ZigbeeSecurityHeader))[0]

    # Bug fix thanks to cutaway (https://code.google.com/p/killerbee/issues/detail?id=25):
    #nonce = struct.pack('L',f['ext_source'])+struct.pack('I',f['fc']) + sec_ctrl_byte
    nonce = struct.pack('L', f['source']) + struct.pack(
        'I', f['fc']) + sec_ctrl_byte

    #nonce = "" # build the nonce
    #nonce += struct.pack(">Q", f['ext_source'])
    #nonce += struct.pack(">I", f['fc'])
    #fc = (f['reserved1'] << 6) | (f['extended_nonce'] << 5) | (f['key_type'] << 3) | f['reserved2']
    #nonce += chr(fc | 0x05)

    if verbose > 2:
        print "Decrypt Details:"
        print "\tKey:            " + key.encode('hex')
        print "\tNonce:          " + nonce.encode('hex')
        print "\tMic:            " + mic.encode('hex')
        print "\tEncrypted Data: " + encrypted.encode('hex')

    crop_size = 4 + 2 + len(
        pkt.getlayer(ZigbeeSecurityHeader).fields['data']
    )  # the size of all the zigbee crap, minus the length of the encrypted data, mic and FCS

    # the Security Control Field flags have to be adjusted before this is calculated, so we store their original values so we can reset them later
    #reserved2 = pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2']
    #pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2'] = (pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2'] | 0x05)
    zigbeeData = pkt.getlayer(ZigbeeNWK).do_build()
    zigbeeData = zigbeeData[:-crop_size]
    #pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2'] = reserved2

    (payload, micCheck) = zigbee_crypt.decrypt_ccm(key, nonce, mic, encrypted,
                                                   zigbeeData)

    if verbose > 2:
        print "\tDecrypted Data: " + payload.encode('hex')

    frametype = pkt.getlayer(ZigbeeNWK).fields['frametype']
    if frametype == 0:
        payload = ZigbeeAppDataPayload(payload)
    elif frametype == 1:
        payload = ZigbeeNWKCommandPayload(payload)
    else:
        payload = Raw(payload)

    return payload
Ejemplo n.º 2
0
def kbdecrypt(pkt, key=None, verbose=None):
    """Decrypt Zigbee frames using AES CCM* with 32-bit MIC"""
    if verbose is None:
        verbose = conf.verb
    if key == None:
        if conf.killerbee_nkey == None:
            log_killerbee.error("Cannot find decryption key. (Set conf.killerbee_nkey)")
            return None
        key = conf.killerbee_nkey
    if len(key) != 16:
        log_killerbee.error("Invalid decryption key, must be a 16 byte string.")
        return None
    elif not pkt.haslayer(ZigbeeSecurityHeader) or not pkt.haslayer(ZigbeeNWK):
        log_killerbee.error("Cannot decrypt frame without a ZigbeeSecurityHeader.")
        return None
    try:
        import zigbee_crypt
    except ImportError:
        log_killerbee.error("Could not import zigbee_crypt extension, cryptographic functionality is not available.")
        return None

    # 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 = struct.unpack(">I", f['mic'])
    mic = pkt.mic

    f = pkt.getlayer(ZigbeeSecurityHeader).fields
    encrypted = f["data"]

    sec_ctrl_byte = str(pkt.getlayer(ZigbeeSecurityHeader))[0]

    # Bug fix thanks to cutaway (https://code.google.com/p/killerbee/issues/detail?id=25):
    # nonce = struct.pack('L',f['ext_source'])+struct.pack('I',f['fc']) + sec_ctrl_byte
    nonce = struct.pack("L", f["source"]) + struct.pack("I", f["fc"]) + sec_ctrl_byte

    # nonce = "" # build the nonce
    # nonce += struct.pack(">Q", f['ext_source'])
    # nonce += struct.pack(">I", f['fc'])
    # fc = (f['reserved1'] << 6) | (f['extended_nonce'] << 5) | (f['key_type'] << 3) | f['reserved2']
    # nonce += chr(fc | 0x05)

    if verbose > 2:
        print "Decrypt Details:"
        print "\tKey:            " + key.encode("hex")
        print "\tNonce:          " + nonce.encode("hex")
        print "\tMic:            " + mic.encode("hex")
        print "\tEncrypted Data: " + encrypted.encode("hex")

    crop_size = (
        4 + 2 + len(pkt.getlayer(ZigbeeSecurityHeader).fields["data"])
    )  # the size of all the zigbee crap, minus the length of the encrypted data, mic and FCS

    # the Security Control Field flags have to be adjusted before this is calculated, so we store their original values so we can reset them later
    # reserved2 = pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2']
    # pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2'] = (pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2'] | 0x05)
    zigbeeData = pkt.getlayer(ZigbeeNWK).do_build()
    zigbeeData = zigbeeData[:-crop_size]
    # pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2'] = reserved2

    (payload, micCheck) = zigbee_crypt.decrypt_ccm(key, nonce, mic, encrypted, zigbeeData)

    if verbose > 2:
        print "\tDecrypted Data: " + payload.encode("hex")

    frametype = pkt.getlayer(ZigbeeNWK).fields["frametype"]
    if frametype == 0:
        payload = ZigbeeAppDataPayload(payload)
    elif frametype == 1:
        payload = ZigbeeNWKCommandPayload(payload)
    else:
        payload = Raw(payload)

    return payload
Ejemplo n.º 3
0
def kbdecrypt(source_pkt, key=None, verbose=None, doMicCheck=False):
    """Decrypt Zigbee frames using AES CCM* with 32-bit MIC"""
    if verbose is None:
        verbose = conf.verb
    if key == None:
        if conf.killerbee_nkey == None:
            log_killerbee.error(
                "Cannot find decryption key. (Set conf.killerbee_nkey)")
            return None
        key = conf.killerbee_nkey
    if len(key) != 16:
        log_killerbee.error(
            "Invalid decryption key, must be a 16 byte string.")
        return None
    if not source_pkt.haslayer(ZigbeeSecurityHeader):
        log_killerbee.error(
            "Cannot decrypt frame without a ZigbeeSecurityHeader.")
        return None
    if not source_pkt.haslayer(ZigbeeNWK):
        log_killerbee.error("Cannot decrypt frame without a ZigbeeNWK.")
        return None
    try:
        import zigbee_crypt
    except ImportError:
        log_killerbee.error(
            "Could not import zigbee_crypt extension, cryptographic functionality is not available."
        )
        return None

    # This function destroys the packet, therefore work on a copy - @cutaway
    pkt = source_pkt.copy()

    # DOT154_CRYPT_ENC_MIC32 is always used regardless of what is claimed in OTA packet, so we will force it here.
    # This is done because the value of nwk_seclevel in the ZigbeeSecurityHeader does
    # not have to be accurate in the transmitted frame: the Zigbee NWK standard states that
    # the nwk_seclevel should be overwritten in the received frame with the value that is being
    # used by all nodes in the Zigbee network - this is to ensure that unencrypted frames can't be
    # maliciously injected.  i.e. the receiver shouldn't trust the received nwk_seclevel.
    # Recreate 'pkt' by rebuilding the raw data and mic to match:
    pkt.nwk_seclevel = DOT154_CRYPT_ENC_MIC32
    pkt.data += pkt.mic
    pkt.mic = pkt.data[-4:]
    pkt.data = pkt.data[:-4]

    encrypted = pkt.data
    # So calculate an amount to crop, equal to the size of the encrypted data and mic.  Note that
    # if there was an FCS, scapy will have already stripped it, so it will not returned by the
    # do_build() call below (and hence doesn't need to be taken into account in crop_size).
    crop_size = len(pkt.mic) + len(pkt.data)

    # create NONCE (for crypt) and zigbeeData (for MIC) according to packet type
    sec_ctrl_byte = str(pkt[ZigbeeSecurityHeader])[0]
    if pkt.haslayer(ZigbeeAppDataPayload):
        nonce = struct.pack('L', source_pkt[ZigbeeNWK].ext_src) + struct.pack(
            'I', source_pkt[ZigbeeSecurityHeader].fc) + sec_ctrl_byte
        zigbeeData = pkt[ZigbeeAppDataPayload].do_build()
    else:
        nonce = struct.pack(
            'L', source_pkt[ZigbeeSecurityHeader].source) + struct.pack(
                'I', source_pkt[ZigbeeSecurityHeader].fc) + sec_ctrl_byte
        zigbeeData = pkt[ZigbeeNWK].do_build()
    # For zigbeeData, we need the entire zigbee packet, minus the encrypted data and mic (4 bytes).
    zigbeeData = zigbeeData[:-crop_size]

    (payload, micCheck) = zigbee_crypt.decrypt_ccm(key, nonce, pkt.mic,
                                                   encrypted, zigbeeData)

    if verbose > 2:
        print "Decrypt Details:"
        print "\tKey:            " + key.encode('hex')
        print "\tNonce:          " + nonce.encode('hex')
        print "\tZigbeeData:     " + zigbeeData.encode('hex')
        print "\tDecrypted Data: " + payload.encode('hex')
        print "\tEncrypted Data: " + encrypted.encode('hex')
        print "\tMic:            " + pkt.mic.encode('hex')

    frametype = pkt[ZigbeeNWK].frametype
    if frametype == 0 and micCheck == 1:
        payload = ZigbeeAppDataPayload(payload)
    elif frametype == 1 and micCheck == 1:
        payload = ZigbeeNWKCommandPayload(payload)
    else:
        payload = Raw(payload)

    if doMicCheck == False:
        return payload
    else:
        if micCheck == 1: return (payload, True)
        else: return (payload, False)
Ejemplo n.º 4
0
def kbdecrypt(pkt, key = None, verbose = None):
    """Decrypt Zigbee frames using AES CCM* with 32-bit MIC"""
    if verbose is None:
        verbose = conf.verb
    if key == None:
        if conf.killerbee_nkey == None:
            log_killerbee.error("Cannot find decryption key. (Set conf.killerbee_nkey)")
            return None
        key = conf.killerbee_nkey
    if len(key) != 16:
        log_killerbee.error("Invalid decryption key, must be a 16 byte string.")
        return None
    elif not pkt.haslayer(ZigbeeSecurityHeader) or not pkt.haslayer(ZigbeeNWK):
        log_killerbee.error("Cannot decrypt frame without a ZigbeeSecurityHeader.")
        return None
    try:
        import zigbee_crypt
    except ImportError:
        log_killerbee.error("Could not import zigbee_crypt extension, cryptographic functionality is not available.")
        return None

    #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

    # Parse the payload as ciphertext || MIC, where MIC is 4 bytes
    pkt.mic = pkt.getlayer(ZigbeeSecurityHeader).data[-4:]
    pkt.data = pkt.getlayer(ZigbeeSecurityHeader).data[:-4]
    mic = pkt.mic

    f = pkt.getlayer(ZigbeeSecurityHeader).fields
    encrypted = f['data']

    sec_ctrl_byte = str(pkt.getlayer(ZigbeeSecurityHeader))[0]

    nonce = struct.pack('L',f['source'])+struct.pack('I',f['fc']) + sec_ctrl_byte

    if verbose > 2:
        print "Decrypt Details:"
        print "\tKey:            " + key.encode('hex')
        print "\tNonce:          " + nonce.encode('hex')
        print "\tMic:            " + mic.encode('hex')
        print "\tEncrypted Data: " + encrypted.encode('hex')
    
    crop_size = 4 + len(pkt.getlayer(ZigbeeSecurityHeader).fields['data'])  # the size of all the zigbee crap, minus the length of the encrypted data, mic

    zigbeeData = pkt.getlayer(ZigbeeNWK).do_build()
    zigbeeData = zigbeeData[:-crop_size]
    
    # zigbeeData = octet string a = NwkHeader || AuxiliaryHeader
    # encrypted  = octet string c = Payload
    (payload, micCheck) = zigbee_crypt.decrypt_ccm(key, nonce, mic, encrypted, zigbeeData)

    if verbose > 2:
        print "\tDecrypted Data: " + payload.encode('hex')
        print "\tmicCheck:       " + str(micCheck)

    frametype = pkt.getlayer(ZigbeeNWK).fields['frametype']
    if frametype == 0:
        payload = ZigbeeAppDataPayload(payload)
    elif frametype == 1:
        payload = ZigbeeNWKCommandPayload(payload)
    else:
        payload = Raw(payload)

    return payload
Ejemplo n.º 5
0
def kbdecrypt(pkt, key=None, verbose=None, doMicCheck=False):
    """Decrypt Zigbee frames using AES CCM* with 32-bit MIC"""
    if verbose is None:
        verbose = conf.verb
    if key == None:
        if conf.killerbee_nkey == None:
            log_killerbee.error(
                "Cannot find decryption key. (Set conf.killerbee_nkey)")
            return None
        key = conf.killerbee_nkey
    if len(key) != 16:
        log_killerbee.error(
            "Invalid decryption key, must be a 16 byte string.")
        return None
    elif not pkt.haslayer(ZigbeeSecurityHeader) or not pkt.haslayer(ZigbeeNWK):
        log_killerbee.error(
            "Cannot decrypt frame without a ZigbeeSecurityHeader.")
        return None
    try:
        import zigbee_crypt
    except ImportError:
        log_killerbee.error(
            "Could not import zigbee_crypt extension, cryptographic functionality is not available."
        )
        return None

    #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
    # Now recreate 'pkt' by rebuilding the raw data and creating a new scapy Packet, because
    # scapy splits the data/mic according to the nwk_seclevel in the ZigbeeSecurityHeader when
    # the scapy Packet is created.  The value of nwk_seclevel in the ZigbeeSecurityHeader does
    # not have to be accurate in the transmitted frame: the Zigbee NWK standard states that
    # the nwk_seclevel should be overwritten in the received frame with the value that is being
    # used by all nodes in the Zigbee network - this is to ensure that unencrypted frames can't be
    # maliciously injected.  i.e. the receiver shouldn't trust the received nwk_seclevel.
    newpkt = pkt.build()
    if pkt.haslayer(Dot15d4FCS): pkt = Dot15d4FCS(newpkt)
    else: pkt = Dot15d4(newpkt)

    #mic = struct.unpack(">I", f['mic'])
    mic = pkt.mic

    f = pkt.getlayer(ZigbeeSecurityHeader).fields
    encrypted = f['data']

    sec_ctrl_byte = str(pkt.getlayer(ZigbeeSecurityHeader))[0]

    # Bug fix thanks to cutaway (https://code.google.com/p/killerbee/issues/detail?id=25):
    #nonce = struct.pack('L',f['ext_source'])+struct.pack('I',f['fc']) + sec_ctrl_byte
    nonce = struct.pack('L', f['source']) + struct.pack(
        'I', f['fc']) + sec_ctrl_byte

    #nonce = "" # build the nonce
    #nonce += struct.pack(">Q", f['ext_source'])
    #nonce += struct.pack(">I", f['fc'])
    #fc = (f['reserved1'] << 6) | (f['extended_nonce'] << 5) | (f['key_type'] << 3) | f['reserved2']
    #nonce += chr(fc | 0x05)

    if verbose > 2:
        print "Decrypt Details:"
        print "\tKey:            " + key.encode('hex')
        print "\tNonce:          " + nonce.encode('hex')
        print "\tMic:            " + mic.encode('hex')
        print "\tEncrypted Data: " + encrypted.encode('hex')

    # For zigbeeData, we need the entire zigbee packet, minus the encrypted data and mic (4 bytes).
    # So calculate an amount to crop, equal to the size of the encrypted data and mic.  Note that
    # if there was an FCS, scapy will have already stripped it, so it will not returned by the
    # do_build() call below (and hence doesn't need to be taken into account in crop_size).
    crop_size = 4 + len(pkt.getlayer(ZigbeeSecurityHeader).fields['data'])

    # the Security Control Field flags have to be adjusted before this is calculated, so we store their original values so we can reset them later
    #reserved2 = pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2']
    #pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2'] = (pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2'] | 0x05)
    zigbeeData = pkt.getlayer(ZigbeeNWK).do_build()
    zigbeeData = zigbeeData[:-crop_size]
    #pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2'] = reserved2

    (payload, micCheck) = zigbee_crypt.decrypt_ccm(key, nonce, mic, encrypted,
                                                   zigbeeData)

    if verbose > 2:
        print "\tDecrypted Data: " + payload.encode('hex')

    frametype = pkt.getlayer(ZigbeeNWK).fields['frametype']
    if frametype == 0 and micCheck == 1:
        payload = ZigbeeAppDataPayload(payload)
    elif frametype == 1 and micCheck == 1:
        payload = ZigbeeNWKCommandPayload(payload)
    else:
        payload = Raw(payload)

    if doMicCheck == False:
        return payload
    else:
        if micCheck == 1: return (payload, True)
        else: return (payload, False)
Ejemplo n.º 6
0
def kbdecrypt(pkt, key = None, verbose = None):
	"""Decrypt Zigbee frames using AES CCM* with 32-bit MIC"""
	if verbose is None:
		verbose = conf.verb
	if key == None:
		if conf.killerbee_nkey == None:
			log_killerbee.error("Cannot find decryption key. (Set conf.killerbee_nkey)")
			return None
		key = conf.killerbee_nkey
	if len(key) != 16:
		log_killerbee.error("Invalid decryption key, must be a 16 byte string.")
		return None
	elif not pkt.haslayer(ZigbeeSecurityHeader) or not pkt.haslayer(ZigbeeNWK):
		log_killerbee.error("Cannot decrypt frame without a ZigbeeSecurityHeader.")
		return None
	try:
		import zigbee_crypt
	except ImportError:
		log_killerbee.error("Could not import zigbee_crypt extension, cryptographic functionality is not available.")
		return None

	from struct import pack
	f = pkt.getlayer(ZigbeeSecurityHeader).fields

	mic = pack(">I", f['mic'])
	encrypted = f['data']

	nonce = ""	# build the nonce
	nonce += pack(">Q", f['source'])
	nonce += pack(">I", f['fc'])
	fc = (f['reserved1'] << 6) | (f['extended_nonce'] << 5) | (f['key_type'] << 3) | f['reserved2']
	nonce += chr(fc | 0x05)

	if verbose > 2:
		print "Decrypt Details:"
		print "\tKey:            " + key.encode('hex')
		print "\tNonce:          " + nonce.encode('hex')
		print "\tMic:            " + mic.encode('hex')
		print "\tEncrypted Data: " + encrypted.encode('hex')
		
	crop_size = 4 + 2 + len(pkt.getlayer(ZigbeeSecurityHeader).fields['data'])	# the size of all the zigbee crap, minus the length of the encrypted data, mic and FCS
	
	# the Security Control Field flags have to be adjusted before this is calculated, so we store their original values so we can reset them later
	reserved2 = pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2']
	pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2'] = (pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2'] | 0x05)
	zigbeeData = pkt.getlayer(ZigbeeNWK).do_build()
	zigbeeData = zigbeeData[:-crop_size]
	pkt.getlayer(ZigbeeSecurityHeader).fields['reserved2'] = reserved2
	
	(payload, micCheck) = zigbee_crypt.decrypt_ccm(key, nonce, mic, encrypted, zigbeeData)

	if verbose > 2:
		print "\tDecrypted Data: " + payload.encode('hex')

	frametype = pkt.getlayer(ZigbeeNWK).fields['frametype']
	if frametype == 0:
		payload = ZigbeeAppDataPayload(payload)
	elif frametype == 1:
		payload = ZigbeeNWKCommandPayload(payload)
	else:
		payload = Raw(payload)
	if micCheck == 1:
		return (payload, True)
	else:
		return (payload, False)