def test_application_data():
    name = 'test_app'
    key = get_random_bytes(16)
    key_index = get_random_bytes(2)
    network = f'test_net'
    num_nodes = 10

    nodes = []
    for x in range(num_nodes):
        nodes.append(f'test_node{x}')

    data = ApplicationData(name=name,
                           key=key,
                           key_index=key_index,
                           network=network,
                           nodes=nodes)

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

    data.save()

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

    r_data = ApplicationData.load(base_dir + app_dir + name + '.yml')

    assert data == r_data
Beispiel #2
0
def new(name, network, key, template):
    '''Create a new application'''
    key_index = random_key_index()
    validate_key_index(key_index)

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

    app_data = ApplicationData(name=name,
                               key=key,
                               key_index=bytes.fromhex(key_index),
                               network=network)
    app_data.save()

    net_data = NetworkData.load(base_dir + net_dir + network + '.yml')
    if app_data.name not in net_data.apps:
        net_data.apps.append(app_data.name)
    net_data.save()

    click.echo(click.style('A new application was created', fg='green'))
    click.echo(click.style(str(app_data), fg='green'))
    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 #5
0
async def model_app_bind(client_element: Element, target: str, model_id: 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

    opcode = b'\x80\x3d'
    r_opcode = b'\x80\x3e'
    parameters = node_data.addr[::-1] + key_index[::-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
    def __search_application_by_aid(self, aid: int) -> str:
        filenames = file_helper.list_files(base_dir + app_dir)

        for f in filenames:
            app_data = ApplicationData.load(base_dir + app_dir + f)
            app_aid = crypto.k4(n=app_data.key)
            if app_aid == aid:
                return app_data.name

        return None
    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
    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 #9
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 #10
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 #11
0
def parse_template(ctx, param, value):
    if not value:
        return value

    template = file_helper.read(value)
    if not template:
        raise click.BadParameter(f'File "{value}" not found')

    try:
        name, name_is_seq = template_helper.get_field(template, 'name')
        validate_name(ctx, param, name)
    except KeyError:
        raise click.BadParameter(f'Field "name" not found in template file '
                                 f'"{value}"')
    except click.BadParameter as bp:
        if name_is_seq:
            template_helper.update_sequence(template, 'name')
        raise bp

    try:
        network, net_is_seq = template_helper.get_field(template, 'network')
        validate_network(ctx, param, network)
    except KeyError:
        raise click.BadParameter(f'Field "network" not found in template file '
                                 f'"{value}"')
    except click.BadParameter as bp:
        if net_is_seq:
            template_helper.update_sequence(template, 'network')
        raise bp

    try:
        key, _ = template_helper.get_field(template, 'key')
        validate_key(ctx, param, key)
    except KeyError:
        key = random_key()

    key_index = random_key_index()
    validate_key_index(key_index)

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

    app_data = ApplicationData(name=name,
                               key=key,
                               key_index=bytes.fromhex(key_index),
                               network=network)
    app_data.save()

    net_data = NetworkData.load(base_dir + net_dir + network + '.yml')
    if app_data.name not in net_data.apps:
        net_data.apps.append(app_data.name)
    net_data.save()

    if name_is_seq:
        template_helper.update_sequence(template, 'name')
    if net_is_seq:
        template_helper.update_sequence(template, 'network')

    click.echo(click.style('A new application was created', fg='green'))
    click.echo(click.style(str(app_data), fg='green'))

    ctx.exit()