Example #1
0
 def decrypt(self, args):
     nick = self.get_nick_matcher(args.nick)
     try:
         key = config['id_key', config['nick_id', nick]]
     except KeyError:
         raise ValueError('key for {} does not exist'.format(format_nick(nick)))
     print(add_color('blue', find_msg_cls(args.unparsed)(key).unpack(args.unparsed)))
Example #2
0
 def __init__(self):
     # all commands are subcommands of /fish, so that namespaces have a cleaner separation
     super().__init__(prog='/fish',
                      description=add_color('blue', self.get_version_str()))
     self.sub_parsers = self.add_subparsers(
         dest='mode', parser_class=NoExitArgumentParser)
     self.sub_parsers.required = True
     self.commands = {}
Example #3
0
 def on_dh1080_init(self, nick, msg):
     '''
     Reply to key exchange initiated by somebody else. Key exchanges already in progress are assumed to have failed
     (e.g. network problems) and discarded.
     '''
     if not config.has('nick_id', nick):
         config['nick_id', nick] = config.create_id()
     if config['id_config', config['nick_id', nick], 'protect']:
         xchat.command('NOTICE {} {}'.format(
             nick.split('@')[0],
             add_color('red', 'key protection is on, exchange denied')))
         print(
             add_color(
                 'red',
                 'key protection is on for {}, exchange denied'.format(
                     format_nick(nick))))
         return
     if config['id_config', config['nick_id', nick], 'stealth']:
         print('stealth is on for {}, exchange denied'.format(
             format_nick(nick)))
         return
     # discard existing key exchanges
     self.id_dh[config['nick_id', nick]] = DH1080()
     dh = self.id_dh[config['nick_id', nick]]
     try:
         dh.receive_any(msg)
     except ValueError as e:
         print(e)
         return
     with hexfish_hook.raw_command(xchat.EAT_NONE):
         xchat.command('NOTICE {} {}'.format(
             nick.split('@')[0], dh.send_response()))
     # check if dh was discarded
     if self.id_dh[config['nick_id', nick]] != dh:
         return
     dh = self.id_dh.pop(config['nick_id', nick])
     config['id_key', config['nick_id', nick]] = dh.get_secret().decode()
     config['id_config', config['nick_id', nick], 'cbc'] = dh.cbc
     config['id_config', config['nick_id', nick], 'active'] = True
     config.dump()
     print('{} init from {} succeeded'.format(
         'DH1080 (CBC)' if dh.cbc else 'DH1080', format_nick(nick)))
     print('Key set to {}'.format(config['id_key', config['nick_id',
                                                          nick]]))
Example #4
0
 def decrypt(self, args):
     nick = self.get_nick_matcher(args.nick)
     try:
         key = config['id_key', config['nick_id', nick]]
     except KeyError:
         raise ValueError('key for {} does not exist'.format(
             format_nick(nick)))
     print(
         add_color('blue',
                   find_msg_cls(args.unparsed)(key).unpack(args.unparsed)))
Example #5
0
 def encrypt(self, args):
     nick = self.get_nick_matcher(args.nick)
     try:
         key = config['id_key', config['nick_id', nick]]
     except KeyError:
         raise ValueError('key for {} does not exist'.format(format_nick(nick)))
     cls = BlowCryptCBC if (
         config['id_config', config['nick_id', nick], 'cbc'] or
         config['id_config', config['nick_id', nick], 'cbc_force']
     ) else BlowCrypt
     print(add_color('blue', cls(key).pack(args.unparsed)))
Example #6
0
 def encrypt(self, args):
     nick = self.get_nick_matcher(args.nick)
     try:
         key = config['id_key', config['nick_id', nick]]
     except KeyError:
         raise ValueError('key for {} does not exist'.format(
             format_nick(nick)))
     cls = BlowCryptCBC if (config['id_config', config['nick_id', nick],
                                   'cbc']
                            or config['id_config', config['nick_id', nick],
                                      'cbc_force']) else BlowCrypt
     print(add_color('blue', cls(key).pack(args.unparsed)))
Example #7
0
 def main(self, word, word_eol, userdata):
     args = None
     print('/{}'.format(word_eol[0]))
     with suppress(ValueError):
         args = self.arg_parser.parse_args(word[1:])
     if args is not None:
         try:
             if hasattr(args, 'unparsed'):
                 args.unparsed = word_eol[-len(args.unparsed)]
             self.arg_parser.commands[args.mode](self, args)
         except ValueError as e:
             print(add_color('red', str(e)))
     return xchat.EAT_ALL
Example #8
0
 def decrypt(self, nick, msg):
     if msg.startswith(self.plain_prefix):
         return msg
     key = config['id_key', config['nick_id', nick]]
     cls = find_msg_cls(msg)
     with suppress(ValueError):
         return '{}{}'.format(cls(key).unpack(msg), self.decrypted_suffix)
     with suppress(ValueError):
         res = cls(key).unpack(msg, True)
         print('partially decrypted {!r}'.format(msg))
         return '{}{}'.format(res, add_color('yellow', '<incomplete>'))
     print('could not decrypt {!r}'.format(msg))
     return msg
Example #9
0
 def main(self, word, word_eol, userdata):
     args = None
     print('/{}'.format(word_eol[0]))
     with suppress(ValueError):
         args = self.arg_parser.parse_args(word[1:])
     if args is not None:
         try:
             if hasattr(args, 'unparsed'):
                 args.unparsed = word_eol[-len(args.unparsed)]
             self.arg_parser.commands[args.mode](self, args)
         except ValueError as e:
             print(add_color('red', str(e)))
     return xchat.EAT_ALL
Example #10
0
 def decrypt(self, nick, msg):
     if msg.startswith(self.plain_prefix):
         return msg
     key = config['id_key', config['nick_id', nick]]
     cls = find_msg_cls(msg)
     with suppress(ValueError):
         return '{}{}'.format(cls(key).unpack(msg), self.decrypted_suffix)
     with suppress(ValueError):
         res = cls(key).unpack(msg, True)
         print('partially decrypted {!r}'.format(msg))
         return '{}{}'.format(res, add_color('yellow', '<incomplete>'))
     print('could not decrypt {!r}'.format(msg))
     return msg
Example #11
0
 def on_dh1080_init(self, nick, msg):
     '''
     Reply to key exchange initiated by somebody else. Key exchanges already in progress are assumed to have failed
     (e.g. network problems) and discarded.
     '''
     if not config.has('nick_id', nick):
         config['nick_id', nick] = config.create_id()
     if config['id_config', config['nick_id', nick], 'protect']:
         xchat.command('NOTICE {} {}'.format(
             nick.split('@')[0], add_color('red', 'key protection is on, exchange denied'))
         )
         print(add_color('red', 'key protection is on for {}, exchange denied'.format(format_nick(nick))))
         return
     if config['id_config', config['nick_id', nick], 'stealth']:
         print('stealth is on for {}, exchange denied'.format(format_nick(nick)))
         return
     # discard existing key exchanges
     self.id_dh[config['nick_id', nick]] = DH1080()
     dh = self.id_dh[config['nick_id', nick]]
     try:
         dh.receive_any(msg)
     except ValueError as e:
         print(e)
         return
     with hexfish_hook.raw_command(xchat.EAT_NONE):
         xchat.command('NOTICE {} {}'.format(nick.split('@')[0], dh.send_response()))
     # check if dh was discarded
     if self.id_dh[config['nick_id', nick]] != dh:
         return
     dh = self.id_dh.pop(config['nick_id', nick])
     config['id_key', config['nick_id', nick]] = dh.get_secret().decode()
     config['id_config', config['nick_id', nick], 'cbc'] = dh.cbc
     config['id_config', config['nick_id', nick], 'active'] = True
     config.dump()
     print('{} init from {} succeeded'.format('DH1080 (CBC)' if dh.cbc else 'DH1080', format_nick(nick)))
     print('Key set to {}'.format(config['id_key', config['nick_id', nick]]))
Example #12
0
 def dh1080_exchange(self, nick):
     '''
     Initiate a key exchange.
     '''
     if nick.startswith('#'):
         print('can\'t exchange keys with a channel')
         return
     if not config.has('nick_id', nick):
         config['nick_id', nick] = config.create_id()
     if config['id_config', config['nick_id', nick], 'protect']:
         print(add_color('red', 'key protection is on for {}, exchange denied'.format(format_nick(nick))))
         return
     self.id_dh[config['nick_id', nick]] = DH1080()
     dh = self.id_dh[config['nick_id', nick]]
     with hexfish_hook.raw_command(xchat.EAT_NONE):
         xchat.command('NOTICE {} {}'.format(
             nick.split('@')[0], dh.send_request(config['id_config', config['nick_id', nick], 'cbc']))
         )
     config.dump()
Example #13
0
 def dh1080_exchange(self, nick):
     '''
     Initiate a key exchange.
     '''
     if nick.startswith('#'):
         print('can\'t exchange keys with a channel')
         return
     if not config.has('nick_id', nick):
         config['nick_id', nick] = config.create_id()
     if config['id_config', config['nick_id', nick], 'protect']:
         print(
             add_color(
                 'red',
                 'key protection is on for {}, exchange denied'.format(
                     format_nick(nick))))
         return
     self.id_dh[config['nick_id', nick]] = DH1080()
     dh = self.id_dh[config['nick_id', nick]]
     with hexfish_hook.raw_command(xchat.EAT_NONE):
         xchat.command('NOTICE {} {}'.format(
             nick.split('@')[0],
             dh.send_request(config['id_config', config['nick_id', nick],
                                    'cbc'])))
     config.dump()
Example #14
0
class HexFish:
    plain_prefix = '+p '
    decrypted_suffix = add_style('bold', add_color('blue', '\xb7'))

    def __init__(self):
        self.id_dh = {}
        self.hooks = [
            xchat.hook_command('', self.on_send_message),
            xchat.hook_command('ME', self.on_send_me),
            xchat.hook_command('MSG', self.on_send_msg),
            xchat.hook_command('NOTICE', self.on_send_notice),
            xchat.hook_server('notice',
                              self.on_recv_notice,
                              priority=xchat.PRI_HIGHEST),
            xchat.hook_print('Change Nick', self.on_change_nick),
            xchat.hook_unload(self.unload),
        ]
        for name in ('Channel Action', 'Private Action to Dialog',
                     'Private Action', 'Channel Message',
                     'Private Message to Dialog', 'Private Message'):
            xchat.hook_print(name,
                             self.on_recv_message,
                             name,
                             priority=xchat.PRI_HIGHEST),

    def unload(self, userdata):
        del self

    def __del__(self):
        for hook in self.hooks:
            xchat.unhook(hook)

    @staticmethod
    def get_nick(first_word=None, network=None):
        '''
        Get the key id of a nick (if specified) or channel.
        '''
        if first_word:
            nick = first_word.split(
                '!')[0] if '!' in first_word else first_word
        else:
            nick = xchat.get_info('channel')
        return '{}@{}'.format(nick, network or xchat.get_info('network'))

    @staticmethod
    def emit_print(event_name, nick, msg, *args, context=None):
        if not context:
            context = xchat.get_context()
        for highlight in [xchat.get_info('nick')
                          ] + xchat.get_prefs('irc_extra_hilight').split(','):
            if highlight and highlight in msg:
                if event_name == 'Channel Message':
                    event_name = 'Channel Msg Hilight'
                elif event_name == 'Channel Action':
                    event_name = 'Channel Action Hilight'
                xchat.command('GUI COLOR 3')
        context.emit_print(event_name, nick, msg, *args)

    def dh1080_exchange(self, nick):
        '''
        Initiate a key exchange.
        '''
        if nick.startswith('#'):
            print('can\'t exchange keys with a channel')
            return
        if not config.has('nick_id', nick):
            config['nick_id', nick] = config.create_id()
        if config['id_config', config['nick_id', nick], 'protect']:
            print(
                add_color(
                    'red',
                    'key protection is on for {}, exchange denied'.format(
                        format_nick(nick))))
            return
        self.id_dh[config['nick_id', nick]] = DH1080()
        dh = self.id_dh[config['nick_id', nick]]
        with hexfish_hook.raw_command(xchat.EAT_NONE):
            xchat.command('NOTICE {} {}'.format(
                nick.split('@')[0],
                dh.send_request(config['id_config', config['nick_id', nick],
                                       'cbc'])))
        config.dump()

    def on_dh1080_init(self, nick, msg):
        '''
        Reply to key exchange initiated by somebody else. Key exchanges already in progress are assumed to have failed
        (e.g. network problems) and discarded.
        '''
        if not config.has('nick_id', nick):
            config['nick_id', nick] = config.create_id()
        if config['id_config', config['nick_id', nick], 'protect']:
            xchat.command('NOTICE {} {}'.format(
                nick.split('@')[0],
                add_color('red', 'key protection is on, exchange denied')))
            print(
                add_color(
                    'red',
                    'key protection is on for {}, exchange denied'.format(
                        format_nick(nick))))
            return
        if config['id_config', config['nick_id', nick], 'stealth']:
            print('stealth is on for {}, exchange denied'.format(
                format_nick(nick)))
            return
        # discard existing key exchanges
        self.id_dh[config['nick_id', nick]] = DH1080()
        dh = self.id_dh[config['nick_id', nick]]
        try:
            dh.receive_any(msg)
        except ValueError as e:
            print(e)
            return
        with hexfish_hook.raw_command(xchat.EAT_NONE):
            xchat.command('NOTICE {} {}'.format(
                nick.split('@')[0], dh.send_response()))
        # check if dh was discarded
        if self.id_dh[config['nick_id', nick]] != dh:
            return
        dh = self.id_dh.pop(config['nick_id', nick])
        config['id_key', config['nick_id', nick]] = dh.get_secret().decode()
        config['id_config', config['nick_id', nick], 'cbc'] = dh.cbc
        config['id_config', config['nick_id', nick], 'active'] = True
        config.dump()
        print('{} init from {} succeeded'.format(
            'DH1080 (CBC)' if dh.cbc else 'DH1080', format_nick(nick)))
        print('Key set to {}'.format(config['id_key', config['nick_id',
                                                             nick]]))

    def on_dh1080_finish(self, nick, msg):
        '''
        Finish the key exchange upon receiving the target's response.
        '''
        # check if dh exists
        if not config.has('nick_id', nick) or config['nick_id',
                                                     nick] not in self.id_dh:
            print('DH1080 finish received from {} but no request was sent'.
                  format(format_nick(nick)))
            return
        dh = self.id_dh.pop(config['nick_id', nick])
        try:
            dh.receive_any(msg)
        except ValueError as e:
            print(e)
            return
        config['id_key', config['nick_id', nick]] = dh.get_secret().decode()
        config['id_config', config['nick_id', nick], 'cbc'] = dh.cbc
        config['id_config', config['nick_id', nick], 'active'] = True
        config.dump()
        print('{} finish from {} succeeded'.format(
            'DH1080 (CBC)' if dh.cbc else 'DH1080', format_nick(nick)))
        print('Key set to {}'.format(config['id_key', config['nick_id',
                                                             nick]]))

    def on_dh1080(self, nick, msg):
        # delay to display exchange status after the key exchange notice is printed
        if msg.startswith('DH1080_INIT'):
            hexfish_hook.delay(0, self.on_dh1080_init, nick, msg)
            return xchat.EAT_PLUGIN
        elif msg.startswith('DH1080_FINISH'):
            hexfish_hook.delay(0, self.on_dh1080_finish, nick, msg)
            return xchat.EAT_PLUGIN
        else:
            raise ValueError

    def encrypt(self, nick, msg):
        if msg.startswith(self.plain_prefix):
            return msg[len(self.plain_prefix):]
        if not config['id_config', config['nick_id', nick], 'active']:
            raise ValueError
        key = config['id_key', config['nick_id', nick]]
        cls = BlowCryptCBC if (config['id_config', config['nick_id', nick],
                                      'cbc']
                               or config['id_config', config['nick_id', nick],
                                         'cbc_force']) else BlowCrypt
        return cls(key).pack(msg)

    # noinspection PyUnreachableCode
    def decrypt(self, nick, msg):
        if msg.startswith(self.plain_prefix):
            return msg
        key = config['id_key', config['nick_id', nick]]
        cls = find_msg_cls(msg)
        with suppress(ValueError):
            return '{}{}'.format(cls(key).unpack(msg), self.decrypted_suffix)
        with suppress(ValueError):
            res = cls(key).unpack(msg, True)
            print('partially decrypted {!r}'.format(msg))
            return '{}{}'.format(res, add_color('yellow', '<incomplete>'))
        print('could not decrypt {!r}'.format(msg))
        return msg

    # noinspection PyUnreachableCode
    def on_recv_message(self, word, word_eol, userdata):
        nick = self.get_nick(word[0][1:])
        key_nick = self.get_nick() if self.get_nick().startswith(
            '#') else self.get_nick(word[0])
        msg = word_eol[1]
        # remove mode chars
        if len(word_eol) >= 3:
            msg = msg[:-2]
        with suppress(ValueError, KeyError):
            msg = self.decrypt(key_nick, msg)
            self.emit_print(userdata, nick.split('@')[0], msg)
            return xchat.EAT_XCHAT
        return xchat.EAT_NONE

    # noinspection PyUnreachableCode
    def on_recv_notice(self, word, word_eol, userdata):
        nick = self.get_nick(word[0][1:])
        msg = word_eol[3]
        for prefix in (':-', ':'):
            if msg.startswith(prefix):
                msg = msg[len(prefix):]
                break
        with suppress(ValueError, KeyError):
            return self.on_dh1080(nick, msg)
        with suppress(ValueError, KeyError):
            msg = self.decrypt(nick, msg)
            self.emit_print('Notice', nick.split('@')[0], msg)
            return xchat.EAT_XCHAT
        return xchat.EAT_NONE

    # noinspection PyUnreachableCode
    def on_send_message(self, word, word_eol, userdata):
        nick = self.get_nick()
        msg = word_eol[0]
        with suppress(ValueError, KeyError):
            msg_ = self.encrypt(nick, msg)
            self.emit_print('Your Message', xchat.get_info('nick'), msg)
            xchat.command('PRIVMSG {} :{}'.format(nick.split('@')[0], msg_))
            return xchat.EAT_XCHAT
        return xchat.EAT_NONE

    # noinspection PyUnreachableCode
    @hexfish_hook.skippable
    def on_send_me(self, word, word_eol, userdata):
        if len(word) == 1:
            return xchat.EAT_NONE
        nick = self.get_nick()
        msg = word_eol[1]
        with suppress(ValueError, KeyError):
            msg_ = self.encrypt(nick, msg)
            self.emit_print('Your Action', xchat.get_info('nick'), msg)
            with hexfish_hook.raw_command(
                    xchat.EAT_NONE), hexfish_hook.skip_print('Your Action'):
                xchat.command('ME {}'.format(msg_))
            return xchat.EAT_XCHAT
        return xchat.EAT_NONE

    @hexfish_hook.skippable
    def on_send_msg(self, word, word_eol, userdata):
        context = xchat.find_context(channel=word[1])
        if context:
            nick = self.get_nick(word[1])
            msg = word_eol[2]
            with suppress(ValueError, KeyError):
                msg_ = self.encrypt(nick, msg)
                self.emit_print('Your Message',
                                xchat.get_info('nick'),
                                msg,
                                context=context)
                with hexfish_hook.raw_command(
                        xchat.EAT_NONE), hexfish_hook.skip_print(
                            'Your Message'):
                    xchat.command('MSG {} {}'.format(nick.split('@')[0], msg_))
                return xchat.EAT_XCHAT
        return xchat.EAT_NONE

    @hexfish_hook.skippable
    def on_send_notice(self, word, word_eol, userdata):
        context = xchat.find_context(channel=word[1])
        if context:
            nick = self.get_nick(word[1])
            msg = word_eol[2]
            with suppress(ValueError, KeyError):
                msg_ = self.encrypt(nick, msg)
                self.emit_print('Notice Send', nick.split('@')[0], msg)
                with hexfish_hook.raw_command(
                        xchat.EAT_NONE), hexfish_hook.skip_print(
                            'Notice Send'):
                    xchat.command('NOTICE {} {}'.format(
                        nick.split('@')[0], msg_))
                return xchat.EAT_XCHAT
        return xchat.EAT_NONE

    def on_change_nick(self, word, word_eol, userdata):
        prev_nick = self.get_nick(word[0])
        nick = self.get_nick(word[1])
        if not config.has('nick_id', prev_nick):
            return xchat.EAT_NONE
        config['nick_id', nick] = config['nick_id', prev_nick]
        config.dump()
        return xchat.EAT_NONE
Example #15
0
 def __init__(self):
     # all commands are subcommands of /fish, so that namespaces have a cleaner separation
     super().__init__(prog='/fish', description=add_color('blue', self.get_version_str()))
     self.sub_parsers = self.add_subparsers(dest='mode', parser_class=NoExitArgumentParser)
     self.sub_parsers.required = True
     self.commands = {}