Esempio n. 1
0
def test_network_data():
    name = 'test_net'
    key = get_random_bytes(16)
    key_index = get_random_bytes(2)
    iv_index = get_random_bytes(4)
    num_apps = 10
    num_nodes = 15

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

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

    data = NetworkData(name=name,
                       key=key,
                       key_index=key_index,
                       iv_index=iv_index,
                       apps=apps,
                       nodes=nodes)

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

    data.save()

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

    r_data = NetworkData.load(base_dir + net_dir + name + '.yml')

    assert data == r_data
Esempio n. 2
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:
        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)

    net_data = NetworkData(name=name,
                           key=key,
                           key_index=bytes.fromhex(key_index),
                           iv_index=bytes(4))
    net_data.save()

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

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

    ctx.exit()
Esempio n. 3
0
    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
Esempio n. 4
0
    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
Esempio n. 5
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)
Esempio n. 6
0
def info(name):
    '''Get description about a network'''

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

    click.echo(click.style('***** Network data *****', fg='cyan'))
    click.echo(click.style(str(net_data), fg='cyan'))
Esempio n. 7
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'))
Esempio n. 8
0
    def __search_network_by_nid(self, nid: int) -> NetworkData:
        filenames = file_helper.list_files(base_dir + net_dir)

        for f in filenames:
            net_data = NetworkData.load(base_dir + net_dir + f)
            net_nid, _, _ = self._gen_security_material(net_data)
            if net_nid == nid:
                return net_data

        return None
Esempio n. 9
0
def provisioning_device(device_uuid: bytes, network: str,
                        addr: bytes, node_name: str, debug: bool):
    click.echo(click.style(f'Provisioning device "{device_uuid}" to network '
                           f'"{network}"', fg='cyan'))
    success = False
    devkey = None
    net_data = NetworkData.load(base_dir + net_dir + network + '.yml')

    try:
        loop = asyncio.get_event_loop()
        prov = Provisioner(loop, device_uuid, net_data.key,
                           net_data.key_index, net_data.iv_index, addr,
                           debug=debug)
        asyncio.gather(prov.spwan_tasks(loop))
        loop.run_forever()
    except Exception as e:
        click.echo(f'Unknown error\n{e}')
    except LinkOpenError:
        pass
    except ProvisioningError:
        pass
    except ProvisioningSuccess:
        devkey = prov.devkey
        success = True
        net_data = NetworkData.load(base_dir + net_dir + network + '.yml')
        net_data.nodes.append(node_name)
        net_data.save()
    except KeyboardInterrupt:
        click.echo(click.style('Interruption by user', fg='yellow'))
        if prov and prov.tasks_h:
            prov.tasks_h.cancel()
            close_task = asyncio.gather(prov.close_link(b'\x02'))
            loop.run_until_complete(close_task)
    except RuntimeError:
        click.echo('Runtime error')
    finally:
        prov.disconnect()
        tasks_running = asyncio.Task.all_tasks()
        for t in tasks_running:
            t.cancel()
        loop.stop()

    return success, devkey
Esempio n. 10
0
def new(name, key, template):
    '''Create a new network'''
    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)

    net_data = NetworkData(name=name,
                           key=key,
                           key_index=bytes.fromhex(key_index),
                           iv_index=bytes(4))
    net_data.save()

    click.echo(click.style('A new network was created', fg='green'))
    click.echo(click.style(str(net_data), fg='green'))
Esempio n. 11
0
    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
Esempio n. 12
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
Esempio n. 13
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()
Esempio n. 14
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()