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
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()
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
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)
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'))
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 __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
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
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'))
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
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
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()
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()