예제 #1
0
    async def set_key(self, module, conn_id, conn_io, encryption, key):
        assert module.node is self
        assert encryption in module.get_supported_encryption()

        io_id = await conn_io.get_index(module)
        nonce = module.nonce
        module.nonce += 1

        ad = tools.pack_int8(encryption) + \
            tools.pack_int16(conn_id) + \
            tools.pack_int16(io_id) + \
            tools.pack_int16(nonce)

        cipher = await Encryption.AES.encrypt(await module.get_key(), ad, key)

        payload = tools.pack_int16(module.id) + \
            tools.pack_int16(ReactiveEntrypoint.SetKey) + \
            ad + \
            cipher

        command = CommandMessage(ReactiveCommand.Call, Message(payload),
                                 self.ip_address, self.reactive_port)

        await self._send_reactive_command(
            command,
            log='Setting key of connection {} ({}:{}) on {} to {}'.format(
                conn_id, module.name, conn_io.name, self.name,
                binascii.hexlify(key).decode('ascii')))
예제 #2
0
    async def connect(self, to_module, conn_id):
        """
        ### Description ###
        Coroutine. Inform the EM of the source module that a new connection has
        been established, so that events can be correctly forwared to the recipient

        ### Parameters ###
        self: Node object
        to_module (XXXModule): destination module
        conn_id (int): ID of the connection

        ### Returns ###
        """
        module_id = await to_module.get_id()

        payload = tools.pack_int16(conn_id) + \
            tools.pack_int16(module_id) + \
            tools.pack_int8(int(to_module.node is self)) + \
            tools.pack_int16(to_module.node.reactive_port) + \
            to_module.node.ip_address.packed

        command = CommandMessage(ReactiveCommand.Connect, Message(payload),
                                 self.ip_address, self.reactive_port)

        await self._send_reactive_command(command,
                                          log='Connecting id {} to {}'.format(
                                              conn_id, to_module.name))
예제 #3
0
    async def register_entrypoint(self, module, entry, frequency):
        """
        ### Description ###
        Coroutine. Register an entry point for periodic tasks

        ### Parameters ###
        self: Node object
        module (XXXModule): target module
        entry (str): entry point to call
        frequency (int): desired frequency of which the entry point will be called

        ### Returns ###
        """
        assert module.node is self
        module_id, entry_id = \
            await asyncio.gather(module.get_id(), module.get_entry_id(entry))

        payload = tools.pack_int16(module_id) + \
            tools.pack_int16(entry_id) + \
            tools.pack_int32(frequency)

        command = CommandMessage(ReactiveCommand.RegisterEntrypoint,
                                 Message(payload), self.ip_address,
                                 self.reactive_port)

        await self._send_reactive_command(
            command,
            log='Sending RegisterEntrypoint command of {}:{} ({}:{}) on {}'.
            format(module.name, entry, module_id, entry_id, self.name))
예제 #4
0
    async def attest(self, module):
        assert module.node is self

        module_id, module_key = await asyncio.gather(module.id, module.key)

        challenge = tools.generate_key(16)

        # The payload format is [sm_id, entry_id, 16 bit nonce, index, wrapped(key), tag]
        # where the tag includes the nonce and the index.
        payload = tools.pack_int16(module_id) + \
            tools.pack_int16(ReactiveEntrypoint.Attest) + \
            tools.pack_int16(len(challenge)) + \
            challenge

        command = CommandMessage(ReactiveCommand.Call, Message(payload),
                                 self.ip_address, self.reactive_port)

        res = await self._send_reactive_command(command,
                                                log='Attesting {}'.format(
                                                    module.name))

        # The result format is [tag] where the tag is the challenge's MAC
        challenge_response = res.message.payload
        expected_tag = await Encryption.SPONGENT.mac(module_key, challenge)

        if challenge_response != expected_tag:
            raise Error('Attestation of {} failed'.format(module.name))

        logging.info("Attestation of {} succeeded".format(module.name))
        module.attested = True
예제 #5
0
    async def deploy(self, module):
        assert module.node is self

        if module.deployed:
            return

        async with aiofile.AIOFile(await module.binary, "rb") as f:
            file_data = await f.read()

        # The packet format is [NAME \0 VID ELF_FILE]
        payload = module.name.encode('ascii') + b'\0' + \
            tools.pack_int16(self.vendor_id) + \
            file_data

        command = CommandMessage(ReactiveCommand.Load, Message(payload),
                                 self.ip_address, self.deploy_port)

        res = await self._send_reactive_command(
            command, log='Deploying {} on {}'.format(module.name, self.name))

        sm_id = tools.unpack_int16(res.message.payload[:2])
        if sm_id == 0:
            raise Error('Deploying {} on {} failed'.format(
                module.name, self.name))

        symtab = res.message.payload[2:]
        symtab_file = tools.create_tmp(suffix='.ld', dir_name=module.out_dir)

        # aiofile for write operations is bugged (version 3.3.3)
        # I get a "bad file descriptor" error after writes.
        with open(symtab_file, "wb") as f:
            f.write(symtab[:-1])  # Drop last 0 byte

        module.deployed = True
        return sm_id, symtab_file
예제 #6
0
    async def set_key(self, module, conn_id, conn_io, encryption, key):
        assert module.node is self
        assert encryption in module.get_supported_encryption()

        module_id, module_key, io_id = await asyncio.gather(
            module.id, module.key, conn_io.get_index(module))

        nonce = tools.pack_int16(module.nonce)
        io_id = tools.pack_int16(io_id)
        conn_id_packed = tools.pack_int16(conn_id)
        ad = conn_id_packed + io_id + nonce

        module.nonce += 1

        cipher = await encryption.SPONGENT.encrypt(module_key, ad, key)

        # The payload format is [sm_id, entry_id, 16 bit nonce, index, wrapped(key), tag]
        # where the tag includes the nonce and the index.
        payload = tools.pack_int16(module_id) + \
            tools.pack_int16(ReactiveEntrypoint.SetKey) + \
            ad + \
            cipher

        command = CommandMessage(ReactiveCommand.Call, Message(payload),
                                 self.ip_address, self.reactive_port)

        await self._send_reactive_command(
            command,
            log='Setting key of {}:{} on {} to {}'.format(
                module.name, conn_io.name, self.name,
                binascii.hexlify(key).decode('ascii')))
예제 #7
0
    async def attest(self, module):
        assert module.node is self

        module_id = await module.get_id()

        challenge = tools.generate_key(16)

        payload = tools.pack_int16(module_id) + \
            tools.pack_int16(ReactiveEntrypoint.Attest) + \
            tools.pack_int16(len(challenge)) + \
            challenge

        command = CommandMessage(ReactiveCommand.Call, Message(payload),
                                 self.ip_address, self.reactive_port)

        res = await self._send_reactive_command(command,
                                                log='Attesting {}'.format(
                                                    module.name))

        # The result format is [tag] where the tag is the challenge's MAC
        challenge_response = res.message.payload
        expected_tag = await Encryption.AES.mac(await module.key, challenge)
        if challenge_response != expected_tag:
            logging.debug("Key: {}".format(await module.key))
            logging.debug("Challenge: {}".format(challenge))
            logging.debug("Resp: {}".format(challenge_response))
            logging.debug("Expected: {}".format(expected_tag))
            raise Error('Attestation of {} failed'.format(module.name))

        logging.info("Attestation of {} succeeded".format(module.name))
        module.attested = True
예제 #8
0
    async def request(self, connection, arg=None, output=None):
        """
        ### Description ###
        Coroutine. Trigger the 'request' event of a direct connection

        ### Parameters ###
        self: Node object
        connection (Connection): connection object
        arg (bytes): argument to pass as a byte array (can be None)

        ### Returns ###
        """
        assert connection.to_module.node is self

        module_id = await connection.to_module.get_id()

        if arg is None:
            data = b''
        else:
            data = arg

        cipher = await connection.encryption.encrypt(
            connection.key, tools.pack_int16(connection.nonce), data)

        payload = tools.pack_int16(module_id) + \
            tools.pack_int16(connection.id) + \
            cipher

        command = CommandMessage(ReactiveCommand.RemoteRequest,
                                 Message(payload), self.ip_address,
                                 self.reactive_port)

        response = await self._send_reactive_command(
            command,
            log='Sending handle_request command of connection {}:{} to {} on {}'
            .format(connection.id, connection.name, connection.to_module.name,
                    self.name))

        if not response.ok():
            logging.error("Received error code {}".format(str(response.code)))
            return

        resp_encrypted = response.message.payload
        plaintext = await connection.encryption.decrypt(
            connection.key, tools.pack_int16(connection.nonce + 1),
            resp_encrypted)

        if output is None:
            logging.info("Response: \"{}\"".format(
                binascii.hexlify(plaintext).decode('ascii')))
        else:
            with open(output, "wb") as f:
                f.write(plaintext)
예제 #9
0
    async def call(self, module, entry, arg=None, output=None):
        """
        ### Description ###
        Coroutine. Call the entry point of a module

        ### Parameters ###
        self: Node object
        to_module (XXXModule): target module
        entry (str): name of the entry point to call
        arg (bytes): argument to pass as a byte array (can be None)

        ### Returns ###
        """
        assert module.node is self

        module_id, entry_id = \
            await asyncio.gather(module.get_id(), module.get_entry_id(entry))

        payload = tools.pack_int16(module_id) + \
            tools.pack_int16(entry_id) + \
            (b'' if arg is None else arg)

        command = CommandMessage(ReactiveCommand.Call, Message(payload),
                                 self.ip_address, self.reactive_port)

        response = await self._send_reactive_command(
            command,
            log='Sending call command to {}:{} ({}:{}) on {}'.format(
                module.name, entry, module_id, entry_id, self.name))

        if not response.ok():
            logging.error("Received error code {}".format(str(response.code)))
            return

        if output is None:
            logging.info("Response: \"{}\"".format(
                binascii.hexlify(response.message.payload).decode('ascii')))
        else:
            with open(output, "wb") as f:
                f.write(response.message.payload)
예제 #10
0
    async def output(self, connection, arg=None):
        """
        ### Description ###
        Coroutine. Trigger the 'output' event of a direct connection

        ### Parameters ###
        self: Node object
        connection (Connection): connection object
        arg (bytes): argument to pass as a byte array (can be None)

        ### Returns ###
        """
        assert connection.to_module.node is self

        module_id = await connection.to_module.get_id()

        if arg is None:
            data = b''
        else:
            data = arg

        cipher = await connection.encryption.encrypt(
            connection.key, tools.pack_int16(connection.nonce), data)

        payload = tools.pack_int16(module_id) + \
            tools.pack_int16(connection.id) + \
            cipher

        command = CommandMessage(ReactiveCommand.RemoteOutput,
                                 Message(payload), self.ip_address,
                                 self.reactive_port)

        await self._send_reactive_command(
            command,
            log='Sending handle_output command of connection {}:{} to {} on {}'
            .format(connection.id, connection.name, connection.to_module.name,
                    self.name))