def kbencrypt(pkt, data, key = None, verbose = None):
    """Encrypt 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 encryption key, must be a 16 byte string.")
        return None
    elif not pkt.haslayer(ZigbeeSecurityHeader) or not pkt.haslayer(ZigbeeNWK):
        log_killerbee.error("Cannot encrypt 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

    f = pkt.getlayer(ZigbeeSecurityHeader).fields
    f['data'] = ''  # explicitly clear it out, this should go without say
    
    if isinstance(data, Packet):
        decrypted = data.do_build()
    else:
        decrypted = data

    nonce = ""  # build the nonce
    nonce += struct.pack(">Q", f['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 "Encrypt Details:"
        print "\tKey:            " + key.encode('hex')
        print "\tNonce:          " + nonce.encode('hex')
        print "\tDecrypted Data: " + decrypted.encode('hex')
        
    crop_size = 4 + 2 # the size of all the zigbee crap, minus the length of the 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, mic) = zigbee_crypt.encrypt_ccm(key, nonce, 4, decrypted, zigbeeData)

    if verbose > 2:
        print "\tEncrypted Data: " + payload.encode('hex')
        print "\tMic:            " + mic.encode('hex')
    
    # Set pkt's values to reflect the encrypted ones to it's ready to be sent
    f['data'] = payload
    f['mic'] = struct.unpack(">I", mic)[0]
    return pkt
def kbencrypt(pkt, data, key = None, verbose = None):
    """Encrypt 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 encryption key, must be a 16 byte string.")
        return None
    elif not pkt.haslayer(ZigbeeSecurityHeader) or not pkt.haslayer(ZigbeeNWK):
        log_killerbee.error("Cannot encrypt 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

    f = pkt.getlayer(ZigbeeSecurityHeader).fields
    f['data'] = ''  # explicitly clear it out, this should go without say

    if isinstance(data, Packet):
        decrypted = data.do_build()
    else:
        decrypted = data

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

    if verbose > 2:
        print "Encrypt Details:"
        print "\tKey:            " + key.encode('hex')
        print "\tNonce:          " + nonce.encode('hex')
        print "\tDecrypted Data: " + decrypted.encode('hex')

    crop_size = 4 + 2 # the size of all the zigbee crap, minus the length of the 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, mic) = zigbee_crypt.encrypt_ccm(key, nonce, 4, decrypted, zigbeeData)

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

    # Set pkt's values to reflect the encrypted ones to it's ready to be sent
    f['data'] = payload
    f['mic'] = struct.unpack(">I", mic)[0]
    return pkt
def kbencrypt(source_pkt, data, key=None, verbose=None):
    """Encrypt 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 encryption key, must be a 16 byte string.")
        return None
    if not source_pkt.haslayer(ZigbeeSecurityHeader):
        log_killerbee.error(
            "Cannot encrypt frame without a ZigbeeSecurityHeader.")
        return None
    if not source_pkt.haslayer(ZigbeeNWK):
        log_killerbee.error("Cannot encrypt 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.
    pkt.nwk_seclevel = DOT154_CRYPT_ENC_MIC32

    # clear data and mic as we are about to create them
    pkt.data = ''
    pkt.mic = ''

    if isinstance(data, Packet):
        decrypted = data.do_build()
    else:
        decrypted = 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()

    # minimum security level is DOT154_CRYPT_ENC_MIC32 but provide more if requested
    miclen = kbgetmiclen(source_pkt.nwk_seclevel)
    if miclen < 4:
        miclen = 4

    (payload, mic) = zigbee_crypt.encrypt_ccm(key, nonce, miclen, decrypted,
                                              zigbeeData)

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

    # According to comments in e.g. https://github.com/wireshark/wireshark/blob/master/epan/dissectors/packet-zbee-security.c nwk_seclevel is not used any more but
    # we should reconstruct and return what was asked for anyway.
    pkt.data = payload + mic
    pkt.nwk_seclevel = source_pkt.nwk_seclevel
    ota_miclen = kbgetmiclen(pkt.nwk_seclevel)
    if ota_miclen > 0:
        pkt.mic = pkt.data[-ota_miclen:]
        pkt.data = pkt.data[:-ota_miclen]
    return pkt
Exemple #4
0
def kbencrypt(pkt, data, key=None, verbose=None):
    """Encrypt 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 encryption key, must be a 16 byte string.")
        return None
    elif not pkt.haslayer(ZigbeeSecurityHeader) or not pkt.haslayer(ZigbeeNWK):
        log_killerbee.error(
            "Cannot encrypt 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

    if verbose > 2:
        print "NWK SEC Details:" + str(pkt.nwk_seclevel)
    pkt = pkt.copy()  #this is hack to fix the below line
    pkt.nwk_seclevel = 5

    f = pkt.getlayer(ZigbeeSecurityHeader).fields
    f['data'] = ''  # explicitly clear it out, this should go without say

    if isinstance(data, Packet):
        decrypted = data.do_build()
    else:
        decrypted = data

    #nonce = ""  # build the nonce
    #nonce += struct.pack(">Q", f['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)

    sec_ctrl_byte = str(pkt.getlayer(ZigbeeSecurityHeader))[0]
    nonce = struct.pack('L', f['source']) + struct.pack(
        'I', f['fc']) + sec_ctrl_byte

    zigbeeData = pkt.getlayer(ZigbeeNWK).do_build()
    if pkt.getlayer(ZigbeeSecurityHeader).mic == '':
        crop_size = 0
    else:
        crop_size = len(
            pkt.getlayer(ZigbeeSecurityHeader).mic
        )  # the size of all the zigbee crap, minus the length of the mic and FCS
        zigbeeData = zigbeeData[:-crop_size]
    # 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

    if verbose > 2:
        print "Encrypt Details:"
        print "\tKey:            " + key.encode('hex')
        print "\tNonce:          " + nonce.encode('hex')
        print "\tDecrypted Data: " + decrypted.encode('hex')
        print "\tZigbee data:  	" + zigbeeData.encode('hex')

    #delete mic from decrypted data
    #decrypted = decrypted[:-4]

    (payload, mic) = zigbee_crypt.encrypt_ccm(key, nonce, 4, decrypted,
                                              zigbeeData)

    if verbose > 2:
        print "\tEncrypted Data: " + payload.encode('hex')
        #print "\tMic unpack:     " + struct.unpack(">I", mic)[0]
        print "\tMic encode:	" + mic.encode('hex')
    pkt.nwk_seclevel = 0
    # Set pkt's values to reflect the encrypted ones to it's ready to be sent
    # add mic to payload for consistency with other functions
    f['data'] = payload
    #f['mic'] = struct.unpack(">I", mic)[0]
    #f['mic'] = mic
    pkt.getlayer(ZigbeeSecurityHeader).mic = mic
    return pkt
def kbencrypt(pkt, data, key = None, verbose = None):
    """Encrypt 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 encryption key, must be a 16 byte string.")
        return None
    elif not pkt.haslayer(ZigbeeSecurityHeader) or not pkt.haslayer(ZigbeeNWK):
        log_killerbee.error("Cannot encrypt 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

    print "NWK SEC Details:" + str(pkt.nwk_seclevel)
    pkt = pkt.copy()  # this is hack to fix the below line
    pkt.nwk_seclevel = 5

    f = pkt.getlayer(ZigbeeSecurityHeader).fields
    f['data'] = ''  # explicitly clear it out, this should go without say

    if isinstance(data, Packet):
        decrypted = data.do_build()
    else:
        decrypted = data

    # nonce = ""  # build the nonce
    # nonce += struct.pack(">Q", f['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)

    sec_ctrl_byte = str(pkt.getlayer(ZigbeeSecurityHeader))[0]
    nonce = struct.pack('Q', f['source']) + struct.pack('I', f['fc']) + sec_ctrl_byte

    zigbeeData = pkt.getlayer(ZigbeeNWK).do_build()
    if pkt.getlayer(ZigbeeSecurityHeader).mic == '':
        crop_size = 0
    else:
        crop_size = len(pkt.getlayer(ZigbeeSecurityHeader).mic)  # the size of all the zigbee crap, minus the length of the mic and FCS
        zigbeeData = zigbeeData[:-crop_size]
    # 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

    if verbose > 2:
        print "Encrypt Details:"
        print "\tKey:            " + key.encode('hex')
        print "\tNonce:          " + nonce.encode('hex')
        print "\tDecrypted Data: " + decrypted.encode('hex')
    print "\tZigbee data:  	" + zigbeeData.encode('hex')

    # delete mic from decrypted data
    decrypted = decrypted[:-4]

    (payload, mic) = zigbee_crypt.encrypt_ccm(key, nonce, 4, decrypted, zigbeeData)

    if verbose > 2:
        print "\tEncrypted Data: " + payload.encode('hex')
        # print "\tMic unpack:     " + struct.unpack(">I", mic)[0]
    print "\tMic encode:	" + mic.encode('hex')
    pkt.nwk_seclevel = 0
    # Set pkt's values to reflect the encrypted ones to it's ready to be sent
    # add mic to payload for consistency with other functions
    f['data'] = payload
    # f['mic'] = struct.unpack(">I", mic)[0]
    # f['mic'] = mic
    pkt.getlayer(ZigbeeSecurityHeader).mic = mic
    return pkt
Exemple #6
0
def kbencrypt(pkt, data, key = None, verbose = None):
    """Encrypt 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 encryption key, must be a 16 byte string.")
        return None
    elif not pkt.haslayer(ZigbeeSecurityHeader) or not pkt.haslayer(ZigbeeNWK):
        log_killerbee.error("Cannot encrypt 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

    pkt = pkt.copy()
    pkt.nwk_seclevel=5 #the issue appears to be when this is set

    f = pkt.getlayer(ZigbeeSecurityHeader).fields
    f['data'] = ''  # explicitly clear it out, this should go without say
    pkt.mic = ''    # Clear out MIC
    
    if isinstance(data, Packet):
        decrypted = data.do_build()
    else:
        decrypted = 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 "Encrypt Details:"
        print "\tKey:            " + key.encode('hex')
        print "\tNonce:          " + nonce.encode('hex')
        print "\tDecrypted Data: " + decrypted.encode('hex')
        
    # 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
    zigbeeData = pkt.getlayer(ZigbeeNWK).do_build()
    
    # zigbeeData = octet string a = NwkHeader || AuxiliaryHeader
    # decrypted  = octet string m = Payload
    (payload, mic) = zigbee_crypt.encrypt_ccm(key, nonce, 4, decrypted, zigbeeData)

    if verbose > 2:
        print "\tEncrypted Data: " + payload.encode('hex')
        print "\tMic:            " + str(mic).encode('hex')
    
    # Set pkt's values to reflect the encrypted ones to it's ready to be sent
    f['data'] = payload
    f['mic'] = mic

    # 4.3.1.1 Security Processing of Outgoing Frames - Step 8
    # Overwrite network security level field with '000'
    f['nwk_seclevel'] = 0
    return pkt
Exemple #7
0
def kbencrypt(pkt, data, key=None, verbose=None):
    """Encrypt 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 encryption key, must be a 16 byte string.")
        return None
    elif not pkt.haslayer(ZigbeeSecurityHeader) or not pkt.haslayer(ZigbeeNWK):
        log_killerbee.error(
            "Cannot encrypt 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

    f = pkt.getlayer(ZigbeeSecurityHeader).fields
    f['data'] = ''  # explicitly clear it out, this should go without say

    if isinstance(data, Packet):
        decrypted = data.do_build()
    else:
        decrypted = data

    # Soteria: Build the nonce the same way it is built by the decryption method:
    pkt = pkt.copy()  #this is hack to fix the below line
    pkt.nwk_seclevel = 5  #the issue appears to be when this is set

    # Soteria: Omit the last 4 bytes of the MIC:
    crop_size = 4
    zigbeeData = pkt.getlayer(ZigbeeNWK).do_build()[:-crop_size]

    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 "Encrypt Details:"
        print "\tKey:            " + key.encode('hex')
        print "\tNonce:          " + nonce.encode('hex')
        print "\tDecrypted Data: " + decrypted.encode('hex')

    (payload, mic) = zigbee_crypt.encrypt_ccm(key, nonce, 4, decrypted,
                                              zigbeeData)

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

    # Set pkt's values to reflect the encrypted ones to it's ready to be sent
    f['data'] = payload
    # Soteria: MIC field encoding type is ASCII, thus Storing the mic in hex version:
    # f['mic'] = struct.unpack(">I", mic)[0]
    f['mic'] = mic

    # Soteria: Update MIC and encrypted data
    pkt.getlayer(ZigbeeSecurityHeader).fields = f
    return pkt
def kbencrypt(pkt, data, key = None, verbose = None):
    """Encrypt 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 encryption key, must be a 16 byte string.")
        return None
    elif not pkt.haslayer(ZigbeeSecurityHeader) or not pkt.haslayer(ZigbeeNWK):
        log_killerbee.error("Cannot encrypt 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

    f = pkt.getlayer(ZigbeeSecurityHeader).fields
    f['data'] = ''  # explicitly clear it out, this should go without say

    if isinstance(data, Packet):
        decrypted = data.do_build()
    else:
        decrypted = data


    # Soteria: Build the nonce the same way it is built by the decryption method:
    pkt = pkt.copy()   #this is hack to fix the below line
    pkt.nwk_seclevel=5 #the issue appears to be when this is set

    # Soteria: Omit the last 4 bytes of the MIC:
    crop_size = 4
    zigbeeData = pkt.getlayer(ZigbeeNWK).do_build()[:-crop_size]

    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 "Encrypt Details:"
        print "\tKey:            " + key.encode('hex')
        print "\tNonce:          " + nonce.encode('hex')
        print "\tDecrypted Data: " + decrypted.encode('hex')

    (payload, mic) = zigbee_crypt.encrypt_ccm(key, nonce, 4, decrypted, zigbeeData)

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

    # Set pkt's values to reflect the encrypted ones to it's ready to be sent
    f['data'] = payload
    # Soteria: MIC field encoding type is ASCII, thus Storing the mic in hex version:
    # f['mic'] = struct.unpack(">I", mic)[0]
    f['mic'] = mic

    # Soteria: Update MIC and encrypted data
    pkt.getlayer(ZigbeeSecurityHeader).fields = f
    return pkt
def kbencrypt(source_pkt, data, key = None, verbose = None):
    """Encrypt 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 encryption key, must be a 16 byte string.")
        return None
    if not source_pkt.haslayer(ZigbeeSecurityHeader):
        log_killerbee.error("Cannot encrypt frame without a ZigbeeSecurityHeader.")
        return None
    if not source_pkt.haslayer(ZigbeeNWK):
        log_killerbee.error("Cannot encrypt 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

    #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
    nwk_seclevel = source_pkt.nwk_seclevel
    pkt = source_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)

    pkt[ZigbeeSecurityHeader].data = ''

    if isinstance(data, Packet):
        decrypted = data.do_build()
    else:
        decrypted = data

    sec_ctrl_byte = str(pkt[ZigbeeSecurityHeader])[0]
    nonce = struct.pack('L',pkt[ZigbeeSecurityHeader].source)+struct.pack('I',pkt[ZigbeeSecurityHeader].fc) + sec_ctrl_byte

    if verbose > 2:
        print "Encrypt Details:"
        print "\tKey:            " + key.encode('hex')
        print "\tNonce:          " + nonce.encode('hex')
        print "\tDecrypted Data: " + decrypted.encode('hex')

    crop_size = 4 + len(pkt[ZigbeeSecurityHeader].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
    pkt[ZigbeeSecurityHeader].nwk_seclevel = (pkt[ZigbeeSecurityHeader].nwk_seclevel | 0x05)
    zigbeeData = pkt[ZigbeeNWK].do_build()
    zigbeeData = zigbeeData[:-crop_size]
    pkt[ZigbeeSecurityHeader].nwk_seclevel = nwk_seclevel

    (payload, mic) = zigbee_crypt.encrypt_ccm(key, nonce, 4, decrypted, zigbeeData)

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

    # Set pkt's values to reflect the encrypted ones to it's ready to be sent
    # note that calculated mic is for the encrypted sublayer, not the enclosing packet
    # TODO: calculate mic for overall packet if required
    pkt[ZigbeeSecurityHeader].data = payload + mic
    pkt[ZigbeeSecurityHeader].mic = ''
    return pkt