Beispiel #1
0
def test_application_data():
    name = 'test_node'
    addr = get_random_bytes(2)
    devkey = get_random_bytes(16)
    device_uuid = get_random_bytes(16)
    network = f'test_net'
    num_apps = 10

    apps = []
    for x in range(num_apps):
        apps.append(f'test_app{x}')

    data = NodeData(name=name,
                    addr=addr,
                    network=network,
                    device_uuid=device_uuid,
                    devkey=devkey,
                    apps=apps)

    assert file_helper.file_exist(base_dir + node_dir + name + '.yml') is \
        False

    data.save()

    assert file_helper.file_exist(base_dir + node_dir + name + '.yml') is \
        True

    r_data = NodeData.load(base_dir + node_dir + name + '.yml')

    assert data == r_data
    def __header_segmented_transport_pdu(self, soft_ctx: SoftContext,
                                         seg_o: int) -> bytes:
        net_data = NetworkData.load(base_dir + net_dir +
                                    soft_ctx.network_name + '.yml')
        node_data = NodeData.load(base_dir + node_dir + soft_ctx.node_name +
                                  '.yml')
        seq_auth = (int.from_bytes(net_data.iv_index, 'big') << 24) | \
            node_data.seq
        self.net_layer.hard_ctx.seq_zero = seq_auth & 0x1fff

        first_byte = 0x80
        if not soft_ctx.is_devkey:
            app_data = ApplicationData.load(base_dir + app_dir +
                                            soft_ctx.application_name + '.yml')
            aid = crypto.k4(n=app_data.key)
            first_byte = first_byte | 0x40
        else:
            node_data = NodeData.load(base_dir + node_dir +
                                      soft_ctx.node_name + '.yml')
            aid = crypto.k4(n=node_data.devkey)
        first_byte = (first_byte | (aid[0] & 0x3f)).to_bytes(1, 'big')

        cont = (self.net_layer.hard_ctx.seg_n & 0x1f)
        cont = cont | ((seg_o & 0x1f) << 5)
        cont = cont | ((self.net_layer.hard_ctx.seq_zero & 0x1fff) << 10)
        cont = cont.to_bytes(3, 'big')

        return first_byte + cont
    def _encrypt_access_pdu(self, pdu: bytes, soft_ctx: SoftContext) -> bytes:
        net_data = NetworkData.load(base_dir + net_dir +
                                    soft_ctx.network_name + '.yml')
        node_data = NodeData.load(base_dir + node_dir + soft_ctx.node_name +
                                  '.yml')
        self.net_layer.hard_ctx.seq = node_data.seq

        if not soft_ctx.is_devkey:
            app_data = ApplicationData.load(base_dir + app_dir +
                                            soft_ctx.application_name + '.yml')
            app_key = app_data.key
            app_nonce = b'\x01\x00' + \
                self.net_layer.hard_ctx.seq.to_bytes(3, 'big') + \
                soft_ctx.src_addr + soft_ctx.dst_addr + net_data.iv_index
        else:
            node_data = NodeData.load(base_dir + node_dir +
                                      soft_ctx.node_name + '.yml')
            app_key = node_data.devkey
            app_nonce = b'\x02\x00' + \
                self.net_layer.hard_ctx.seq.to_bytes(3, 'big') + \
                soft_ctx.src_addr + soft_ctx.dst_addr + net_data.iv_index

        result, mic = crypto.aes_ccm_complete(key=app_key,
                                              nonce=app_nonce,
                                              text=pdu,
                                              adata=b'',
                                              mic_size=4)

        return result + mic
Beispiel #4
0
    async def send_pdu(self, transport_pdu: bytes, soft_ctx: SoftContext):
        net_data = NetworkData.load(base_dir + net_dir +
                                    soft_ctx.network_name + '.yml')
        node_data = NodeData.load(base_dir + node_dir + soft_ctx.node_name +
                                  '.yml')
        self.hard_ctx.seq = node_data.seq

        nid, encryption_key, privacy_key = \
            self._gen_security_material(net_data)

        ivi = ((int.from_bytes(net_data.iv_index, 'big') & 0x01) << 7)
        ctl = 0x80 if self.hard_ctx.is_ctrl_msg else 0x00
        ttl = 0x02
        seq = self.hard_ctx.seq
        src = soft_ctx.src_addr

        net_nonce = b'\x00' + (ctl | ttl).to_bytes(1, 'big') + \
            seq.to_bytes(3, 'big') + src + b'\x00\x00' + net_data.iv_index

        enc_dst, enc_transport_pdu, net_mic = self._encrypt(
            soft_ctx, transport_pdu, encryption_key, net_nonce)

        obsfucated = self._obsfucate(ctl, ttl, seq, src, enc_dst,
                                     enc_transport_pdu, net_mic, privacy_key,
                                     net_data)

        network_pdu = (ivi | nid).to_bytes(1, 'big') + obsfucated + enc_dst + \
            enc_transport_pdu + net_mic

        await self.send_queue.put((b'message_s', network_pdu))

        self.__increment_seq(soft_ctx)
Beispiel #5
0
def info(name):
    '''Get description about a node'''

    node_data = NodeData.load(base_dir + node_dir + name + '.yml')

    click.echo(click.style('***** Node data *****', fg='cyan'))
    click.echo(click.style(str(node_data), fg='cyan'))
Beispiel #6
0
async def model_subscription_add(client_element: Element, target: str,
                                 model_id: bytes, addr: bytes) -> bool:
    node_data = NodeData.load(base_dir + node_dir + target + '.yml')

    opcode = b'\x80\x1b'
    r_opcode = b'\x80\x1f'
    parameters = node_data.addr[::-1] + addr[::-1] + model_id[::-1]

    context = SoftContext(src_addr=b'\x00\x01', dst_addr=node_data.addr,
                          node_name=node_data.name,
                          network_name=node_data.network,
                          application_name='',
                          is_devkey=True, ack_timeout=3, segment_timeout=3)

    success = await client_element.send_message(opcode=opcode,
                                                parameters=parameters,
                                                ctx=context)
    if not success:
        return False

    r_content = await client_element.recv_message(opcode=r_opcode,
                                                  segment_timeout=1,
                                                  timeout=3, ctx=context)

    if r_content:
        if r_content[0] == 0 and r_content[1:] == parameters:
            return True

    return False
Beispiel #7
0
def new(name, network, address, uuid, template):
    '''Create a new node'''

    if template:
        if template[0]:
            name = template[0]
        if template[1]:
            network = template[1]
        if template[2]:
            address = template[2]
        if template[3]:
            uuid = template[3]
        if template[4]:
            tmpl = template[4]

    validate_name(None, None, name)
    validate_network(None, None, network)
    validate_addr(None, None, address)
    validate_uuid(None, None, uuid)

    if len(uuid) < 32:
        uuid = bytes.fromhex(uuid) + bytes((32 - len(uuid)) // 2)
    elif len(uuid) > 32:
        uuid = bytes.fromhex(uuid)[0:16]
    else:
        uuid = bytes.fromhex(uuid)

    print(f'address: {address}')
    address = bytes.fromhex(address)

    # provisioning device
    success, devkey = provisioning_device(uuid, network, address, name, False)
    if not success:
        click.echo(click.style('Error in provisioning', fg='red'))
    else:
        if tmpl['name_is_seq']:
            template_helper.update_sequence(tmpl, 'name')

        if tmpl['addr_is_seq']:
            template_helper.update_sequence(tmpl, 'address', custom_pattern=tmpl['name'])

        node_data = NodeData(name=name, addr=address, network=network,
                             device_uuid=uuid, devkey=devkey)
        node_data.save()

        click.echo(click.style('A new node was created.', fg='green'))
        click.echo(click.style(str(node_data), fg='green'))
    def __search_node_by_addr(self, addr: bytes) -> str:
        filenames = file_helper.list_files(base_dir + node_dir)

        for f in filenames:
            node_data = NodeData.load(base_dir + node_dir + f)
            if addr == node_data.addr:
                return node_data.name

        return ''
Beispiel #9
0
def send(target, opcode, parameters, devkey, app):
    '''Send a message to node'''

    click.echo(click.style(f'Sending message [{opcode}, {parameters}] to '
                           f'"{target}" node', fg='green'))
    node_data = NodeData.load(base_dir + node_dir + target + '.yml')

    opcode = bytes.fromhex(opcode)
    parameters = bytes.fromhex(parameters)

    try:
        loop = asyncio.get_event_loop()
        client_element = Element()
        if devkey:
            app_name = ''
            is_devkey = True
        elif not node_data.apps:
            click.echo(click.style('Using devkey beacuse node hasn\'t '
                                   'application registred', fg='yellow'))
            app_name = ''
            is_devkey = True
        else:
            if app in node_data.apps:
                app_name = app
                is_devkey = False
            else:
                app_name = node_data.apps[0]
                is_devkey = False
        context = SoftContext(src_addr=b'\x00\x01',
                              dst_addr=node_data.addr,
                              node_name=node_data.name,
                              network_name=node_data.network,
                              application_name=app_name,
                              is_devkey=is_devkey,
                              ack_timeout=30,
                              segment_timeout=10)
        run_seq_t = run_seq([
            client_element.spwan_tasks(loop),
            client_element.send_message(opcode=opcode, parameters=parameters,
                                        ctx=context)
        ])
        loop.run_until_complete(run_seq_t)
    except KeyboardInterrupt:
        click.echo(click.style('Interruption by user', fg='yellow'))
    except RuntimeError:
        click.echo('Runtime error')
    except Exception as e:
        click.echo(f'Unknown error\n[{e}]')
    finally:
        client_element.disconnect()
        tasks_running = asyncio.Task.all_tasks()
        for t in tasks_running:
            t.cancel()
        loop.stop()

# 4d096b543184ab000000000000000000000000
# PublishTTL default 7 (4.3.2.16)
    def _decrypt_transport_pdu(self, pdu: bytes, ctx: SoftContext,
                               first_seq: int) -> bytes:
        if self.net_layer.hard_ctx.szmic == 0:
            encrypted_pdu = pdu[0:-4]
            transport_mic = pdu[-4:]
        else:
            encrypted_pdu = pdu[0:-8]
            transport_mic = pdu[-8:]

        self.log.debug(f'Encrypted pdu: {encrypted_pdu.hex()}, mic = '
                       f'{transport_mic.hex()}')

        net_data = NetworkData.load(base_dir + net_dir + ctx.network_name +
                                    '.yml')

        if ctx.is_devkey:
            self.log.debug(f'Using devkey, node name [{ctx.node_name}]')
            if not ctx.node_name:
                self.log.debug('No node found')
                return None

            node_data = NodeData.load(base_dir + node_dir + ctx.node_name +
                                      '.yml')
            key = node_data.devkey
            nonce = b'\x02'
        else:
            app_data = ApplicationData.load(base_dir + app_dir +
                                            ctx.application_name + '.yml')
            key = app_data.key
            nonce = b'\x01'

        nonce += (self.net_layer.hard_ctx.szmic << 7).to_bytes(1, 'big')
        nonce += (first_seq).to_bytes(3, 'big')
        nonce += ctx.src_addr
        nonce += ctx.dst_addr
        nonce += net_data.iv_index

        access_pdu, mic_is_ok = crypto.aes_ccm_decrypt(key=key,
                                                       nonce=nonce,
                                                       text=encrypted_pdu,
                                                       mic=transport_mic)

        self.log.debug(f'Access PDU: {access_pdu.hex()}, first seq: '
                       f'{hex(first_seq)}')

        if not mic_is_ok:
            self.log.debug(f'Mic is wrong, pdu: {access_pdu.hex()}, seq: '
                           f'{hex(first_seq)}')
            return None
        else:
            return access_pdu
Beispiel #11
0
async def send_cmd(client_element: Element, target: str, opcode: bytes,
                   parameters: bytes, application: str):
    node_data = NodeData.load(base_dir + node_dir + target + '.yml')

    app_name = application if application else node_data.apps[0]

    context = SoftContext(src_addr=b'\x00\x01', dst_addr=node_data.addr,
                          node_name=node_data.name,
                          network_name=node_data.network,
                          application_name=app_name,
                          is_devkey=False, ack_timeout=3, segment_timeout=3)

    success = await client_element.send_message(opcode=opcode,
                                                parameters=parameters,
                                                ctx=context)
    return success
    def _unsegmented_transport_pdu(self, pdu: bytes,
                                   soft_ctx: SoftContext) -> bytes:
        if not soft_ctx.is_devkey:
            app_data = ApplicationData.load(base_dir + app_dir +
                                            soft_ctx.application_name + '.yml')
            aid = crypto.k4(n=app_data.key)
            unseg_tr_pdu = 0x40
        else:
            node_data = NodeData.load(base_dir + node_dir +
                                      soft_ctx.node_name + '.yml')
            aid = crypto.k4(n=node_data.devkey)
            unseg_tr_pdu = 0x00

        unseg_tr_pdu = (unseg_tr_pdu |
                        (int.from_bytes(aid, 'big') & 0x3f)).to_bytes(
                            1, 'big')
        unseg_tr_pdu += pdu

        return unseg_tr_pdu
Beispiel #13
0
async def appkey_add(client_element: Element, target: str,
                     application: str) -> bool:
    node_data = NodeData.load(base_dir + node_dir + target + '.yml')
    app_data = ApplicationData.load(base_dir + app_dir + application + '.yml')
    net_data = NetworkData.load(base_dir + net_dir + app_data.network + '.yml')

    net_key = int.from_bytes(net_data.key_index, 'big')
    app_key = int.from_bytes(app_data.key_index, 'big')
    key_index = (net_key | (app_key << 12)).to_bytes(3, 'big')[::-1]

    opcode = b'\x00'
    r_opcode = b'\x80\x03'
    parameters = key_index + app_data.key

    context = SoftContext(src_addr=b'\x00\x01', dst_addr=node_data.addr,
                          node_name=node_data.name,
                          network_name=node_data.network,
                          application_name='',
                          is_devkey=True, ack_timeout=3, segment_timeout=3)

    success = await client_element.send_message(opcode=opcode,
                                                parameters=parameters,
                                                ctx=context)
    if not success:
        return False

    r_content = await client_element.recv_message(opcode=r_opcode,
                                                  segment_timeout=1,
                                                  timeout=3, ctx=context)

    if r_content:
        if r_content[0] == 0 and r_content[1:] == key_index:
            if app_data.name not in node_data.apps:
                node_data.apps.append(app_data.name)
                node_data.save()

            if node_data.name not in app_data.nodes:
                app_data.nodes.append(node_data.name)
                app_data.save()

            return True

    return False
Beispiel #14
0
async def model_publication_set(client_element: Element, target: str,
                                model_id: bytes, addr: bytes,
                                application: str) -> bool:
    node_data = NodeData.load(base_dir + node_dir + target + '.yml')
    app_data = ApplicationData.load(base_dir + app_dir + application + '.yml')

    key_index = app_data.key_index
    default_ttl = b'\x07'
    period = b'\x00'
    default_xmit_int = b'\x40'

    opcode = b'\x03'
    r_opcode = b'\x80\x19'
    parameters = node_data.addr[::-1] + addr[::-1] + key_index[::-1] + \
        default_ttl[::-1] + period[::-1] + default_xmit_int[::-1] + \
        model_id[::-1]

    context = SoftContext(src_addr=b'\x00\x01', dst_addr=node_data.addr,
                          node_name=node_data.name,
                          network_name=node_data.network,
                          application_name='',
                          is_devkey=True, ack_timeout=3, segment_timeout=3)

    success = await client_element.send_message(opcode=opcode,
                                                parameters=parameters,
                                                ctx=context)
    if not success:
        return False

    r_content = await client_element.recv_message(opcode=r_opcode,
                                                  segment_timeout=1,
                                                  timeout=3, ctx=context)

    if r_content:
        if r_content[0] == 0 and r_content[1:] == parameters:
            return True

    return False
Beispiel #15
0
 def __increment_seq(self, soft_ctx: SoftContext):
     node_name = soft_ctx.node_name
     node_data = NodeData.load(base_dir + node_dir + node_name + '.yml')
     node_data.seq += 1
     node_data.save()
Beispiel #16
0
def add_appkey(target, app):
    '''Add a appkey to node'''

    click.echo(
        click.style(
            f'Add the appkey of "{app}" application to '
            f'"{target}" node',
            fg='green'))
    node_data = NodeData.load(base_dir + node_dir + target + '.yml')
    app_data = ApplicationData.load(base_dir + app_dir + app + '.yml')
    net_data = NetworkData.load(base_dir + net_dir + node_data.network +
                                '.yml')

    net_key = int.from_bytes(net_data.key_index, 'big')
    app_key = int.from_bytes(app_data.key_index, 'big')
    key_index = (net_key | (app_key << 12)).to_bytes(3, 'big')[::-1]

    opcode = b'\x00'
    r_opcode = b'\x80\x03'
    parameters = key_index + app_data.key

    try:
        loop = asyncio.get_event_loop()
        client_element = Element()
        context = SoftContext(src_addr=b'\x00\x01',
                              dst_addr=node_data.addr,
                              node_name=node_data.name,
                              network_name=node_data.network,
                              application_name='',
                              is_devkey=True,
                              ack_timeout=10,
                              segment_timeout=3)
        run_seq_t = run_seq([
            client_element.spwan_tasks(loop),
            client_element.send_message(opcode=opcode,
                                        parameters=parameters,
                                        ctx=context),
            client_element.recv_message(opcode=r_opcode,
                                        segment_timeout=3,
                                        timeout=10,
                                        ctx=context)
        ])
        results = loop.run_until_complete(run_seq_t)

        content = results[2][0]
        if content:
            if content[0] == 0:
                if content[1:] == key_index:
                    click.echo(
                        click.style('App key add with successful', fg='green'))
                    if app_data.name not in node_data.apps:
                        node_data.apps.append(app_data.name)
                        node_data.save()

                    if node_data.name not in app_data.nodes:
                        app_data.nodes.append(node_data.name)
                        app_data.save()
                else:
                    click.echo(
                        click.style(f'Wrong key index: {content[1:].hex()}',
                                    fg='red'))
            else:
                click.echo(
                    click.style(f'Fail. Error code: {content[0:1].hex()}',
                                fg='red'))
    except KeyboardInterrupt:
        click.echo(click.style('Interruption by user', fg='yellow'))
    except RuntimeError:
        click.echo('Runtime error')
    except Exception:
        click.echo(traceback.format_exc())
    finally:
        client_element.disconnect()
        tasks_running = asyncio.Task.all_tasks()
        for t in tasks_running:
            t.cancel()
        loop.stop()
Beispiel #17
0
def req(target, opcode, parameters, r_opcode, devkey, app):
    '''Request a message to node (Send a message and wait the response)'''

    click.echo(
        click.style(
            f'Sending message [{opcode}, {parameters}] to '
            f'"{target}" node and wait receive a message with '
            f'opcode {r_opcode}',
            fg='green'))
    node_data = NodeData.load(base_dir + node_dir + target + '.yml')

    print(f'node addr: {node_data.addr}')

    opcode = bytes.fromhex(opcode)
    r_opcode = bytes.fromhex(r_opcode)
    parameters = bytes.fromhex(parameters)

    try:
        loop = asyncio.get_event_loop()
        client_element = Element()
        if devkey:
            app_name = ''
            is_devkey = True
        elif not node_data.apps:
            click.echo(
                click.style(
                    'Using devkey beacuse node hasn\'t '
                    'application registred',
                    fg='yellow'))
            app_name = ''
            is_devkey = True
        else:
            if app in node_data.apps:
                app_name = app
                is_devkey = False
            else:
                app_name = node_data.apps[0]
                is_devkey = False
        context = SoftContext(src_addr=b'\x00\x01',
                              dst_addr=node_data.addr,
                              node_name=node_data.name,
                              network_name=node_data.network,
                              application_name=app_name,
                              is_devkey=is_devkey,
                              ack_timeout=30,
                              segment_timeout=10)
        run_seq_t = run_seq([
            client_element.spwan_tasks(loop),
            client_element.send_message(opcode=opcode,
                                        parameters=parameters,
                                        ctx=context),
            client_element.recv_message(opcode=r_opcode,
                                        segment_timeout=3,
                                        timeout=10,
                                        ctx=context)
        ])
        results = loop.run_until_complete(run_seq_t)

        content = results[2][0]
        if content:
            click.echo(
                click.style(f'Message received: {content.hex()}', fg='white'))
        else:
            click.echo(
                click.style(
                    f'No message with opcode {r_opcode.hex()} '
                    f'received',
                    fg='red'))
    except KeyboardInterrupt:
        click.echo(click.style('Interruption by user', fg='yellow'))
    except RuntimeError:
        click.echo('Runtime error')
    except Exception as e:
        click.echo(f'Unknown error\n[{e}]')
    finally:
        client_element.disconnect()
        tasks_running = asyncio.Task.all_tasks()
        for t in tasks_running:
            t.cancel()
        loop.stop()