Esempio n. 1
0
    def basic_check(self, received_testscript_msg_bytes):
        """
        Basic check for all the LoRaWAN test steps. It verifies the MIC of the message and sets
        a flag if the received message if of a CONFIRMED_UP type
        (so an ACK could be sent to the DUT).
        """
        super().basic_check(
            received_testscript_msg_bytes=received_testscript_msg_bytes)
        self.received_testscript_msg = flora_messages.GatewayMessage(
            json_ttm_str=received_testscript_msg_bytes.decode())
        lorawan_msg = self.received_testscript_msg.parse_lorawan_message()
        mtype_str = lorawan_msg.mhdr.mtype_str
        # Register in flag if the received message needs Acknowdlegment knowdlege
        if mtype_str in ('CONFIRMED_UP', ):
            self.ctx_test_manager.device_under_test.message_to_ack = True
        else:
            self.ctx_test_manager.device_under_test.message_to_ack = False
        network_key = self.ctx_test_manager.device_under_test.loramac_params.nwkskey
        if mtype_str in ('JOIN_REQUEST', ):
            network_key = self.ctx_test_manager.device_under_test.appkey
        logger.info(
            f"Checking MIC using key {utils.bytes_to_text(network_key)}.")
        calculated_mic = lorawan_msg.calculate_mic(key=network_key)

        if not lorawan_msg.mic_bytes == calculated_mic:
            description_template = "Wrong MIC.\nKey: {key}\nMIC: {received_mic}\nCalculated: {calc}"
            raise lorawan_errors.MICError(
                description=description_template.format(
                    key=utils.bytes_to_text(network_key),
                    received_mic=utils.bytes_to_text(lorawan_msg.mic_bytes),
                    calc=utils.bytes_to_text(calculated_mic)),
                test_case=self.ctx_test_manager.tc_name,
                step_name=self.name,
                last_message=self.received_testscript_msg.get_printable_str())
 def __str__(self):
     """ Human readable string representation of the MAC Command."""
     ret_str = "{name} MAC Command.\n".format(name=type(self).__name__.split('.')[-1])
     ret_str += "Command ID (CID): 0x{cid}\n".format(cid=utils.bytes_to_text(self.cid))
     ret_str += "Size: {size}\n".format(size=self.command_size)
     ret_str += "Content: 0x{content}\n".format(content=utils.bytes_to_text(self.content))
     return ret_str
Esempio n. 3
0
 def to_print_str(self):
     """ Creates a human readable string with the contained information."""
     DevAddr = bytes_to_text(self.devaddr),
     DevEUI = bytes_to_text(self.deveui),
     AppKey = bytes_to_text(self.appkey),
     AppSKey = bytes_to_text(self.appskey),
     NwkSKey = bytes_to_text(self.nwkskey)
     return f"DevAddr: {DevAddr}\nDevEUI: {DevEUI}\nAppKey: {AppKey}\nAppSKey: {AppSKey}\nNwkSKey: {NwkSKey}"
    def __str__(self):
        dadd = utils.bytes_to_text(self.loramac_params.devaddr)
        deui = utils.bytes_to_text(self.deveui)
        ask = utils.bytes_to_text(self.loramac_params.appskey)
        nsk = utils.bytes_to_text(self.loramac_params.nwkskey)
        ak = utils.bytes_to_text(self.appkey)

        return f"\tDevAddr: {dadd}\n\tDevEUI: {deui}\n\tAppSKey: {ask}\n\tNwkSKey: {nsk}\n\tAppKey: {ak}\n"
Esempio n. 5
0
 def analyze_join_request(self, join_request_phypayload_bytes):
     appeui_bytes = join_request_phypayload_bytes[8:0:-1]
     deveui_bytes = join_request_phypayload_bytes[16:8:-1]
     devnonce = join_request_phypayload_bytes[-5:-7:-1]
     self._used_otaa_devnonces.append(devnonce)
     app_hex = utils.bytes_to_text(appeui_bytes)
     dev_hex = utils.bytes_to_text(deveui_bytes)
     nonce_hex = utils.bytes_to_text(devnonce[::-1])
     logger.info(
         f"appEUI: {app_hex}\ndevEUI: {dev_hex}\ndevnonce: {nonce_hex}")
 def __str__(self):
     ret_str = super().__str__()
     ret_str += "ChIndex: 0x{chi_b}\n".format(chi_b=utils.bytes_to_text(self.chindex))
     ret_str += "Freq: 0x{freq_b} -> {freq_mhz}\n".format(freq_b=utils.bytes_to_text(self.freq),
                                                          freq_mhz=lorawan_parameters.freq_24bits_to_mhz(
                                                              freq_bytes=self.freq[::-1]))
     ret_str += "DrRange: 0x{dr_b}\n".format(dr_b=utils.bytes_to_text(self.drrange))
     ret_str += "Min DR: {min_dr}\n".format(min_dr=self.mindr)
     ret_str += "Max DR: {max_dr}\n".format(max_dr=self.maxdr)
     return ret_str
Esempio n. 7
0
 def __str__(self):
     """ Human readable string representation."""
     retstr = ''
     retstr += "----DevAddr: {0}\n".format(
         utils.bytes_to_text(self.devaddr_bytes))
     retstr += "----FCtrl: {0}\n".format(self.fctrl.fctrl_binary_str())
     retstr += str(self.fctrl)
     retstr += "----FCnt: {0} ({1})\n".format(
         self.get_fcnt_int(), utils.bytes_to_text(self.fcnt_bytes))
     retstr += "----FOpts: {0}\n".format(self.fopts_to_str())
     return retstr
    def accept_join(self, devnonce, dlsettings, rxdelay, cflist):
        """
        Updates the session information and creates the PHYPayload of a join accept message to be sent to the DUT.

        :param devnonce: 2 bytes of the device nonce used in the join request message.
        :param dlsettings: byte of the dlsettings field (join accept)
        :param rxdelay: byte of the rxdelay field (join accept)
        :param cflist: 16 bytes with the frequency list.
        :return: bytes of the lorawan join accept message PHYPayload.
        """
        appnonce = struct.pack("<L", self.create_appnonce())[:3]
        devaddr_int = random.randint(0, 2**32 - 1)
        nwkid_int = (devaddr_int & 0xfe000000) // 2**25
        netid_int = (random.randint(0, 2**24 - 1) & 0xffff80) | nwkid_int
        devaddr = struct.pack(">L", devaddr_int)
        netid = struct.pack(">L", netid_int)[-3:]
        appnonce_netid_devnonce = appnonce + netid[::-1] + devnonce[::-1]

        appkey_bytes = bytes.fromhex(self.appkey_hex)
        nwkskey = utils.aes128_encrypt(
            appkey_bytes, b'\x01' + appnonce_netid_devnonce + bytes(7))
        appskey = utils.aes128_encrypt(
            appkey_bytes, b'\x02' + appnonce_netid_devnonce + bytes(7))
        logger.info(f"AppSKey: {utils.bytes_to_text(appskey)}")
        logger.info(f"NwkSKey: {utils.bytes_to_text(nwkskey)}")
        self.store_used_devnonce(devnonce)
        macpayload = appnonce + netid[::
                                      -1] + devaddr[::
                                                    -1] + dlsettings + rxdelay + cflist
        mhdr_macpayload = lorawan_parameters.MHDR.JOIN_ACCEPT + macpayload
        mic = utils.aes128_cmac(appkey_bytes, mhdr_macpayload)[:4]

        join_accept_phypayload = (
            lorawan_parameters.MHDR.JOIN_ACCEPT + utils.aes128_decrypt(
                key=appkey_bytes, cipher_text=macpayload + mic))
        devaddr_hex = utils.bytes_to_text(devaddr)
        appskey_hex = utils.bytes_to_text(appskey)
        nwkskey_hex = utils.bytes_to_text(nwkskey)
        self.update_device_session(devaddr_hex=devaddr_hex,
                                   appskey_hex=appskey_hex,
                                   nwkskey_hex=nwkskey_hex)

        self.rx1_dr_offset = (int.from_bytes(dlsettings, byteorder='big')
                              & 0x70) >> 4
        rx2_dr = (int.from_bytes(dlsettings, byteorder='big') & 0x0f)
        self.rx2_dr = lorawan_parameters.LORA_DR[rx2_dr]
        seconds_delay = max(1,
                            (int.from_bytes(rxdelay, byteorder='big') & 0x0f))
        self.rx1_delay = seconds_delay * lorawan_parameters.TIMING.MS_IN_SEC

        self.last_join_accept_hex = utils.bytes_to_text(join_accept_phypayload)
Esempio n. 9
0
 def fopts_to_str(self):
     """
     (LoRaWANFHDR) -> (str)
     Get string repr of FOpts
     """
     if self.fopts_bytes is None:
         return None
     else:
         return utils.bytes_to_text(self.fopts_bytes)
Esempio n. 10
0
    def step_handler(self, ch, method, properties, body):
        """ Pong message handler."""
        if not self.received_testscript_msg:
            self.received_testscript_msg = flora_messages.GatewayMessage(
                json_ttm_str=body.decode())

        received_lorawan = self.received_testscript_msg.parse_lorawan_message()

        appskey = self.ctx_test_manager.device_under_test.loramac_params.appskey
        received_frmpayload = received_lorawan.get_frmpayload_plaintext(
            key=appskey)

        mtype_str = received_lorawan.mhdr.mtype_str
        if mtype_str in ('UNCONFIRMED_UP', 'UNCONFIRMED_DOWN', 'CONFIRMED_UP',
                         'CONFIRMED_DOWN'):
            if (received_lorawan.macpayload.fport_int == 224
                    and received_frmpayload[0:1]
                    == lorawan.lorawan_parameters.testing.TEST_CODE.PINGPONG):

                if not received_frmpayload == self.expected_bytes:
                    raise lorawan_errors.EchoError(
                        description="PONG {0} received when expecting {1}.".
                        format(utils.bytes_to_text(received_frmpayload),
                               utils.bytes_to_text(self.expected_bytes)),
                        step_name=self.name,
                        test_case=self.ctx_test_manager.tc_name,
                        last_message=self.received_testscript_msg.
                        get_printable_str(encryption_key=appskey))
            else:
                # If it's a data message, but not a PONG, the FRMPayload is decrypted and showed in the GUI.
                raise test_errors.UnexpectedResponseError(
                    description="Waiting for a PONG response.",
                    step_name=self.name,
                    last_message=self.received_testscript_msg.
                    get_printable_str(encryption_key=appskey),
                    test_case=self.ctx_test_manager.tc_name)
        else:
            raise test_errors.UnexpectedResponseError(
                description="Waiting for a PONG response.",
                step_name=self.name,
                last_message=self.received_testscript_msg.get_printable_str(),
                test_case=self.ctx_test_manager.tc_name)
Esempio n. 11
0
 def step_handler(self, ch, method, properties, body):
     super().step_handler(ch, method, properties, body)
     lw_response, send_ping, _ = self.pingpong_echo_exchange(
         next_step=self.next_step)
     lw_response_wrong_mic = lw_response[:-4:] + b'\xff\xff\xff\xff'
     device = self.ctx_test_manager.device_under_test
     json_nwk_response = self.received_testscript_msg.create_nwk_response_str(
         phypayload=lw_response_wrong_mic,
         delay=device.loramac_params.rx1_delay,
         datr_offset=device.loramac_params.rx1_dr_offset)
     self.send_downlink(msg=json_nwk_response,
                        routing_key=message_broker.routing_keys.toAgent +
                        '.gw1')
     # Manually decrease the downlink counter.
     self.ctx_test_manager.ctx_test_session_coordinator.downlink_counter -= 1
     mic = bytes_to_text(lw_response[-4::])
     mic_modif = bytes_to_text(b'\xff\xff\xff\xff')
     self.print_step_info(
         sending=send_ping,
         additional_message=f"Modified MIC: {mic} ->{mic_modif}\n")
 def get_printable_str(self,
                       encryption_key=None,
                       ignore_format_errors=False):
     """ Creates a human readable string representation of the message."""
     logger.info(
         f"Getting printable representation of the Gateway Message.")
     lorawan_message = self.parse_lorawan_message(
         ignore_format_errors=ignore_format_errors)
     ret_str = f"tmst: {self.testingtool_msg_dict['tmst']}, freq: {self.testingtool_msg_dict['freq']}, DR: {self.testingtool_msg_dict['datr']}\n"
     phypay = utils.bytes_to_text(
         base64.b64decode(self.testingtool_msg_dict["data"]))
     ret_str += f"PHYPayload: {phypay} (Size: {self.testingtool_msg_dict['size']} bytes)\n"
     ret_str += str(lorawan_message)
     if encryption_key is None:
         return ret_str
     frmpayload_plaintext = lorawan_message.get_frmpayload_plaintext(
         key=encryption_key)
     if frmpayload_plaintext is None:
         return ret_str
     frmpay = utils.bytes_to_text(frmpayload_plaintext)
     ekey = utils.bytes_to_text(encryption_key)
     ret_str += f"Decrypted FRMPayload: {frmpay}\n (Key {ekey})\n"
     return ret_str
 def store_used_devnonce(self, devnonce_bytes):
     """
     (EndDevice, int) -> (None)
     Store the used device nonce to avoid repeated use of the same value and prevent replay attacks.
     :param devnonce: (int) Value to store as a used device nonce.
     :return: None
     """
     devnonce_hex_new = utils.bytes_to_text(devnonce_bytes)
     devnonce_hex_list = self.get_used_devnoce_hex_list()
     if devnonce_hex_new in devnonce_hex_list:
         raise scheduler_errors.DuplicatedNonce()
     if len(devnonce_hex_list) > 3:
         devnonce_hex_list = devnonce_hex_list[1:]
     devnonce_hex_list.append(devnonce_hex_new)
     self.used_otaa_devnonces_hex = ",".join(devnonce_hex_list)
Esempio n. 14
0
 def step_handler(self, ch, method, properties, body):
     super().step_handler(ch, method, properties, body)
     frmpayload = None
     fport = None
     fctr = lorawan_parameters.FCTRL.DOWN_ADROFF_ACKOFF_FPENDOFF_FOPTLEN0
     fopts = b''
     if self.piggybacked:
         fctr = struct.pack('B', len(self.command_bytes) % 16)
         fopts = self.command_bytes
     if self.in_frmpayload:
         frmpayload = self.command_bytes
         fport = 0
     lw_response = self.ctx_test_manager.device_under_test.prepare_lorawan_data(
         frmpayload=frmpayload,
         fport=fport,
         mhdr=lorawan_parameters.MHDR.CONFIRMED_DOWN,
         fctr=fctr,
         fopts=fopts)
     device = self.ctx_test_manager.device_under_test
     json_nwk_response = self.received_testscript_msg.create_nwk_response_str(
         phypayload=lw_response,
         delay=device.loramac_params.rx1_delay,
         datr_offset=device.loramac_params.rx1_dr_offset)
     self.send_downlink(msg=json_nwk_response,
                        routing_key=message_broker.routing_keys.toAgent +
                        '.gw1')
     # manually decrease the downlink counter, because this message should be ignored by the DUT.
     if self.piggybacked and self.in_frmpayload:
         self.ctx_test_manager.ctx_test_session_coordinator.downlink_counter -= 1
     if self.received_testscript_msg.parse_lorawan_message(
     ).macpayload.fport_int == 0:
         key = self.ctx_test_manager.device_under_test.loramac_params.nwkskey
     else:
         key = self.ctx_test_manager.device_under_test.loramac_params.appskey
     self.print_step_info(
         received_str=self.received_testscript_msg.get_printable_str(
             encryption_key=key),
         additional_message=
         "Sending MAC Command: 0x{comm}\nPiggybacked: {p}\nIn FRMPayload: {f}"
         .format(comm=utils.bytes_to_text(self.command_bytes),
                 p=self.piggybacked,
                 f=self.in_frmpayload))
 def print_step_info(self,
                     received_str=None,
                     sending=None,
                     additional_message=None):
     """
     Handles the display of the information of the current step, logging, printing on standard output or
     displaying in the GUI the information related to the step of the test under execution.
     :param received_str: Recieved message string.
     :param sending: Message to be sent to the user in this step.
     :param additional_message: Additional message to be presented to the user.
     :return: None
     """
     if not received_str:
         received_str = self.received_testscript_msg.get_printable_str(
             encryption_key=self.ctx_test_manager.device_under_test.
             loramac_params.appskey)
     step_report = ui_reports.InputFormBody(
         title=f"{self.ctx_test_manager.tc_name.upper()}: Step information",
         tag_key=self.ctx_test_manager.tc_name,
         tag_value=" ")
     if self.next_step:
         step_name = self.next_step.name
     else:
         step_name = "No next step."
     step_info_str = f"\nNext step: {step_name}\nReceived from DUT:\n {received_str}"
     step_report.add_field(
         ui_reports.ParagraphField(name=f"Completed Step: {self.name}",
                                   value=""))
     for line in step_info_str.split("\n"):
         step_report.add_field(
             ui_reports.ParagraphField(name="", value=line))
     if sending:
         step_report.add_field(
             ui_reports.ParagraphField(name="Sending to DUT:",
                                       value=utils.bytes_to_text(sending)))
     if additional_message:
         step_report.add_field(
             ui_reports.ParagraphField(name="Additional information:",
                                       value=additional_message))
     ui_publisher.display_on_gui(
         msg_str=str(step_report),
         key_prefix=message_broker.service_names.test_session_coordinator)
 def setdefault(self, key, default=None):
     self._assert_mutable()
     key = bytes_to_text(key, self.encoding)
     default = bytes_to_text(default, self.encoding)
     return super(QueryDict, self).setdefault(key, default)
 def __str__(self):
     ret_str = super().__str__()
     ret_str += "Battery Level: 0x{bat_b}\n".format(bat_b=utils.bytes_to_text(self.battery))
     ret_str += "Margin: 0x{mar_b}\n".format(mar_b=utils.bytes_to_text(self.margin))
     return ret_str
Esempio n. 18
0
 def test_bytes_nosep(self, bytes_to_convert, expected):
     """ Tests the utility function with no separator ('')."""
     assert utils.bytes_to_text(bytes_to_convert) == expected
 def appendlist(self, key, value):
     self._assert_mutable()
     key = bytes_to_text(key, self.encoding)
     value = bytes_to_text(value, self.encoding)
     super(QueryDict, self).appendlist(key, value)
 def setlist(self, key, list_):
     self._assert_mutable()
     key = bytes_to_text(key, self.encoding)
     list_ = [bytes_to_text(elt, self.encoding) for elt in list_]
     super(QueryDict, self).setlist(key, list_)
 def __setitem__(self, key, value):
     self._assert_mutable()
     key = bytes_to_text(key, self.encoding)
     value = bytes_to_text(value, self.encoding)
     super(QueryDict, self).__setitem__(key, value)
Esempio n. 22
0
 def test_bytes_defaultsep(self, bytes_to_convert, expected):
     """ Tests the utility function with the default separator (' ', a space)."""
     assert utils.bytes_to_text(bytes_to_convert) == expected
    def up_message_handler(self, body_str):

        received_testscript_msg = flora_messages.GatewayMessage(
            json_ttm_str=body_str)
        lorawan_msg = received_testscript_msg.parse_lorawan_message()
        logger.info("--------------------------------------------------------\n")
        logger.info(f"Received Uplink: {str(lorawan_msg)}")

        mtype_int = lorawan_msg.mhdr.mtype_int

        if lorawan_msg.mhdr.mhdr_bytes == lorawan_parameters.MHDR.JOIN_REQUEST:
            try:
                devnonce = lorawan_msg.macpayload.devnonce_bytes
                deveui_hex = utils.bytes_to_text(lorawan_msg.macpayload.deveui_bytes).upper()
                appeui_hex = utils.bytes_to_text(lorawan_msg.macpayload.appeui_bytes).upper()
                if not self.sessions_handler.is_registered(dev_eui_hex=deveui_hex,
                                                           app_eui_hex=appeui_hex):
                    logger.info(f"Device Not Registered: {deveui_hex}")
                    return
                self.sessions_handler.process_otta_join(
                    deveui_hex=deveui_hex, devnonce=devnonce,
                    dlsettings=self.accept_dlsettings,
                    rxdelay=self.accept_rxdelay,
                    cflist=self.accept_cflist)
                jaccept_phypayload = self.sessions_handler.get_joinaccept_bytes(
                    deveui_hex=deveui_hex)
                json_nwk_response = received_testscript_msg.create_nwk_response_str(
                    phypayload=jaccept_phypayload,
                    delay=lorawan_parameters.TIMING.JOIN_ACCEPT_DELAY1,
                    datr_offset=lorawan_parameters.DR_OFFSET.RX1_DEFAULT)
                logger.info(f"Sending Join Accept: {str(json_nwk_response)}")

                self.downlink_mq_interface.send(routing_key=routing_keys.fromSchedulerToAgent,
                                                data=json_nwk_response)
            except scheduler_errors.DuplicatedNonce as dne:
                logger.info(f"Ignoring Duplicated nonce {devnonce}")
        elif lorawan_msg.mhdr.mhdr_bytes == lorawan_parameters.MHDR.UNCONFIRMED_UP:
            devaddrhex = utils.bytes_to_text(lorawan_msg.macpayload.fhdr.devaddr_bytes).upper()
            network_key_hex = self.sessions_handler.get_nwk_s_key_hex(dev_addr_hex=devaddrhex)
            if network_key_hex is None:
                logger.info(f"No active session for device: {devaddrhex}")
                return
            network_key = bytes.fromhex(network_key_hex)
            calculated_mic = lorawan_msg.calculate_mic(key=network_key)
            dev_eui_hex = self.sessions_handler.get_dev_eui_hex(dev_addr_hex=devaddrhex)
            if not lorawan_msg.mic_bytes == calculated_mic:
                logger.info(
                    f"Wrong MIC. Expecting {calculated_mic}, Device {dev_eui_hex} ({devaddrhex}).")
            logger.info(f"MIC OK (NwkSKey: {utils.bytes_to_text(network_key)}, dev {dev_eui_hex})")

            frmpayload_command = bytes.fromhex(
                self.sessions_handler.get_command_hex(dev_addr_hex=devaddrhex))
            lw_response = self.sessions_handler.prepare_lorawan_data(dev_eui_hex=dev_eui_hex,
                                                                     frmpayload=frmpayload_command)
            json_nwk_response = received_testscript_msg.create_nwk_response_str(
                phypayload=lw_response,
                delay=lorawan_parameters.TIMING.RECEIVE_DELAY1,
                datr_offset=lorawan_parameters.DR_OFFSET.RX1_DEFAULT)
            logger.info(f"Sending Downlink Data: {str(json_nwk_response)}")
            self.downlink_mq_interface.send(routing_key=routing_keys.fromSchedulerToAgent,
                                            data=json_nwk_response)