def prepare_lorawan_data( self, frmpayload, fport, mhdr=lorawan_parameters.MHDR.UNCONFIRMED_DOWN, fctr=lorawan_parameters.FCTRL.DOWN_ADROFF_ACKOFF_FPENDOFF_FOPTLEN0, fopts=b'', force_fcntdown_int=None): """ Creates the PHYPayload of a LoRaWAN DATA message with the specified lorawan_parameters. It does the FRMPayload encryption and calculates de MIC using the device's keys. :param frmpayload: plain text of the FRMPayload (bytes) :param fport: frame port of the message (int) :param mhdr: MAC Header (1 byte) :param fctr: Frame control field of the Frame header (FHDR) :param fopts: Frame options field used to send MAC commands (0 to 15 bytes). :param force_fcntdown_int: Force the use of a forged downlink frame count (don't increase the downlink count). :return: bytes of the PHYPayload """ if force_fcntdown_int: fcnt_down = force_fcntdown_int % 2**16 else: fcnt_down = self.fcnt_down self.fcnt_down += 1 if self.message_to_ack and mhdr in (b'\xA0', b'\x60'): fctr = struct.pack('B', struct.unpack('B', fctr)[0] | 32) fhdr = self.loramac_params.devaddr[::-1] + fctr + struct.pack( '<H', fcnt_down) + fopts mhdr_fhdr = mhdr + fhdr assert mhdr in (b'\x00', b'\x40', b'\x80', b'\x20', b'\x60', b'\xA0', b'\xC0'), "Unrecognized MHDR." if mhdr in (b'\x00', b'\x40', b'\x80'): direction = 0 else: direction = 1 if fport is not None and frmpayload is not None: if fport == 0: key = self.loramac_params.nwkskey else: key = self.loramac_params.appskey mac_hdr_payload = mhdr_fhdr + struct.pack( 'B', fport) + utils.encrypt_ieee802154( key=key, frmpayload=frmpayload, direction=direction, devaddr=self.loramac_params.devaddr, fcnt=fcnt_down) else: mac_hdr_payload = mhdr_fhdr phy_payload = mac_hdr_payload + utils.mic_rfc4493( key=self.loramac_params.nwkskey, msg=mac_hdr_payload, direction=direction, devaddr=self.loramac_params.devaddr, fcnt=fcnt_down) return phy_payload
def test_downlink_encryption(self, device_session_id, default_test_key, dl_args, expected): """ Tests the encryption for a downlink message with known plain text as the FRMPayload, and using different frame count (FCnt (2 bytes) field of the FHDR). """ calculated = utils.encrypt_ieee802154(key=default_test_key, frmpayload=dl_args[0], direction=dl_args[1], devaddr=device_session_id["DevAddr"], fcnt=dl_args[2]) assert calculated == expected
def get_frmpayload_plaintext(self, key): """ Return the FRMPayload plain text of a LoRaWAN data message when the content was encrypted used the provided key. :param key: byte sequence of the AppSKey used to encrypt the message (16 bytes). :return: byte sequence of the decrypted FRMPayload. """ if self.macpayload.frmpayload_bytes is None or self.mhdr.mtype_str not in ( 'UNCONFIRMED_UP', 'UNCONFIRMED_DOWN', 'CONFIRMED_UP', 'CONFIRMED_DOWN'): return None devaddr = self.macpayload.fhdr.devaddr_bytes fcnt = self.macpayload.fhdr.get_fcnt_int() plain_frmpayload = utils.encrypt_ieee802154( key=key, frmpayload=self.macpayload.frmpayload_bytes, direction=self.mhdr.message_dir, devaddr=devaddr, fcnt=fcnt) return plain_frmpayload
def create_appmessage_str(self, appskey): """ Decrypts the FRMPayload of the message and creates an Application Message :param appskey: byte sequence of the Application Session Key (16 bytes). :return: json formatted string. """ lorawan_message = self.parse_lorawan_message() devaddr = lorawan_message.macpayload.fhdr.devaddr_bytes fcnt = lorawan_message.macpayload.fhdr.get_fcnt_int() plain_frmpayload = utils.encrypt_ieee802154( key=appskey, frmpayload=lorawan_message.macpayload.frmpayload_bytes, direction=lorawan_message.mhdr.message_dir, devaddr=devaddr, fcnt=fcnt) port = lorawan_message.macpayload.fport_int json_app_dict = self.testingtool_msg_dict json_app_dict["DevAddr"] = base64.b64encode(devaddr).decode() json_app_dict["FCnt"] = fcnt json_app_dict["Dir"] = lorawan_message.mhdr.message_dir json_app_dict["FPort"] = port json_app_dict["FRMPayload"] = base64.b64encode( plain_frmpayload).decode() return json.dumps(json_app_dict)