def test_omemo_state_raises_runtime_error_if_not_connected(self, mockdb): mockdb.return_value = get_test_db_connection() ProfOmemoUser.reset() with pytest.raises(RuntimeError): _ = ProfOmemoState()
def test_set_omemo_user(self): account = '*****@*****.**' fulljid = '[email protected]/profanity' ProfOmemoUser().account is None ProfOmemoUser().fulljid is None ProfOmemoUser.set_user(account, fulljid) ProfOmemoUser().account == account ProfOmemoUser().fulljid == fulljid
def test_omemo_state_returns_singleton(self, mockdb): mockdb.return_value = get_test_db_connection() account = '*****@*****.**' fulljid = '[email protected]/profanity' ProfOmemoUser.set_user(account, fulljid) state = ProfOmemoState() new_state = ProfOmemoState() assert state == new_state
def unpack_devicelist_info(stanza): xml = stanza_as_xml(stanza) try: sender_jid = xml.attrib.get('from') except AttributeError: sender_jid = None if sender_jid is None: event_node = xml.find('./{%s}event' % 'http://jabber.org/protocol/pubsub#event') try: sender_jid = event_node.attrib.get('from') except AttributeError: # device list info is result of a request for our own account sender_jid = ProfOmemoUser().account logger.debug('Found sender jid {0}'.format(sender_jid)) item_list = xml.find('.//{%s}list' % NS_OMEMO) if item_list is not None: device_ids = [int(d.attrib['id']) for d in item_list] else: device_ids = [] logger.debug('Found device ids {0}'.format(device_ids)) msg_dict = {'from': sender_jid, 'devices': device_ids} return msg_dict
def prof_init(version, status, account_name, fulljid): log.info('prof_init() called') synopsis = [ '/omemo', '/omemo on|off', '/omemo start|end [jid]', '/omemo set' '/omemo status', '/omemo account', '/omemo fulljid', '/omemo fingerprints', '/omemo show_devices', '/omemo reset_devicelist' ] description = 'Plugin to enable OMEMO encryption' args = [ ['on|off', 'Enable/Disable the Profanity OMEMO Plugin'], ['start|end <jid>', ('Start an OMEMO based conversation with <jid> ' 'window or current window.')], ['set', 'Set Settings like Message Prefix'], ['status', 'Display the current Profanity OMEMO Plugin status.'], ['fingerprints <jid>', 'Display the known fingerprints for <jid>'], ['account', 'Show current account name'], ['reset_devicelist <jid>', 'Manually reset a contacts devicelist.'], ['fulljid', 'Show current <full-jid>'] ] examples = [] # ensure the plugin is not registered if python-omemo is not available prof.register_command('/omemo', 1, 3, synopsis, description, args, examples, _parse_args) prof.completer_add('/omemo', ['on', 'off', 'status', 'start', 'end', 'set' 'account', 'fulljid', 'show_devices', 'reset_devicelist', 'fingerprints']) prof.completer_add('/omemo set', ['message_prefix']) # set user and init omemo only if account_name and fulljid provided if account_name is not None and fulljid is not None: ProfOmemoUser.set_user(account_name, fulljid) _init_omemo() else: log.warning('No User logged in on plugin.prof_init()')
def test_encrypt_stanza(self, mockdb): pytest.skip('Work in Progress') mockdb.return_value = get_test_db_connection() current_user = '******' ProfOmemoUser.set_user(current_user, current_user + '/profanity') recipient = '*****@*****.**' omemo_state = ProfOmemoState() omemo_state.set_devices(recipient, [4711, 995746]) raw_stanza = ('<message id="msg0x001" to="{0}" type="chat">' '<body>Hollo</body>' '<active xmlns="http://jabber.org/protocol/chatstates"/>' '</message>').format(recipient) encrypted = xmpp.encrypt_stanza(raw_stanza)
def prof_init(version, status, account_name, fulljid): log.info('prof_init() called') synopsis = [ '/omemo', '/omemo on|off', '/omemo start|end [jid]', '/omemo set' '/omemo status', '/omemo account', '/omemo fulljid', '/omemo fingerprints', '/omemo show_devices', '/omemo reset_devicelist' ] description = 'Plugin to enable OMEMO encryption' args = [ ['on|off', 'Enable/Disable the Profanity OMEMO Plugin'], ['start|end <jid>', ('Start an OMEMO based conversation with <jid> ' 'window or current window.')], ['set', 'Set Settings like Message Prefix'], ['status', 'Display the current Profanity OMEMO Plugin status.'], ['fingerprints <jid>', 'Display the known fingerprints for <jid>'], ['account', 'Show current account name'], ['reset_devicelist <jid>', 'Manually reset a contacts devicelist.'], ['fulljid', 'Show current <full-jid>'] ] examples = [] # ensure the plugin is not registered if python-omemo is not available prof.register_command('/omemo', 1, 3, synopsis, description, args, examples, _parse_args) prof.completer_add('/omemo', ['on', 'off', 'status', 'start', 'end', 'set' 'account', 'fulljid', 'show_devices', 'reset_devicelist', 'fingerprints']) prof.completer_add('/omemo set', ['message_prefix']) # set user and init omemo only if account_name and fulljid provided if account_name is not None and fulljid is not None: ProfOmemoUser.set_user(account_name, fulljid) _init_omemo() else: log.warning('No User logged in on plugin.prof_init()')
def test_encrypt_stanza(self, mockdb): pytest.skip('Work in Progress') mockdb.return_value = get_test_db_connection() current_user = '******' ProfOmemoUser.set_user(current_user, current_user + '/profanity') recipient = '*****@*****.**' omemo_state = ProfOmemoState() omemo_state.set_devices(recipient, [4711, 995746]) raw_stanza = ('<message id="msg0x001" to="{0}" type="chat">' '<body>Hollo</body>' '<active xmlns="http://jabber.org/protocol/chatstates"/>' '</message>' ).format(recipient) encrypted = xmpp.encrypt_stanza(raw_stanza)
def test_reset_omemo_user(self): account = '*****@*****.**' fulljid = '[email protected]/profanity' ProfOmemoUser.set_user(account, fulljid) ProfOmemoUser().account == account ProfOmemoUser().fulljid == fulljid ProfOmemoUser.reset() ProfOmemoUser().account is None ProfOmemoUser().fulljid is None
def test_unpack_devicelist_request_result_for_own_devices(self, mockdb): mockdb.return_value = get_test_db_connection() test_stanza = ( '<iq id="0x011" to="[email protected]/profanity" type="result">' '<pubsub xmlns="http://jabber.org/protocol/pubsub">' '<items node="{0}">' '<item id="1">' '<list xmlns="{1}">' '<device id="1426586702"/>' '</list>' '</item>' '</items>' '</pubsub>' '</iq>').format(NS_DEVICE_LIST, NS_OMEMO) current_user = '******' ProfOmemoUser.set_user(current_user, current_user + '/profanity') msg_dict = xmpp.unpack_devicelist_info(test_stanza) expected_msg_dict = {'from': current_user, 'devices': [1426586702]} assert msg_dict == expected_msg_dict
def test_unpack_devicelist_request_result_for_own_devices(self, mockdb): mockdb.return_value = get_test_db_connection() test_stanza = ('<iq id="0x011" to="[email protected]/profanity" type="result">' '<pubsub xmlns="http://jabber.org/protocol/pubsub">' '<items node="{0}">' '<item id="1">' '<list xmlns="{1}">' '<device id="1426586702"/>' '</list>' '</item>' '</items>' '</pubsub>' '</iq>' ).format(NS_DEVICE_LIST, NS_OMEMO) current_user = '******' ProfOmemoUser.set_user(current_user, current_user + '/profanity') msg_dict = xmpp.unpack_devicelist_info(test_stanza) expected_msg_dict = {'from': current_user, 'devices': [1426586702]} assert msg_dict == expected_msg_dict
def _init_omemo(): account = ProfOmemoUser().account if account: # subscribe to devicelist updates log.info('Adding Disco Feature {0}.'.format(NS_DEVICE_LIST_NOTIFY)) # subscribe to device list updates prof.disco_add_feature(NS_DEVICE_LIST_NOTIFY) log.debug('Announcing own bundle info.') _announce_own_devicelist() # announce own device list _announce_own_bundle() # announce own bundle # we query our own devices to add possible other devices we own _query_device_list(account)
def encrypt_stanza(stanza): logger.debug('Enrypting stanza {0}'.format(stanza)) logger.debug('Convert stanza to xml.') msg_xml = stanza_as_xml(stanza) fulljid = msg_xml.attrib.get('from', ProfOmemoUser().fulljid) logger.debug('Sender: {0}'.format(fulljid)) jid = msg_xml.attrib['to'] account, resource = jid.rsplit('/', 1) logger.debug('Recipient {0} [{1}]'.format(account, resource)) msg_id = msg_xml.attrib['id'] logger.debug('Message ID: {0}'.format(msg_id)) body_node = msg_xml.find('.//body') plaintext = body_node.text logger.debug('Message: {0}'.format(plaintext)) try: plaintext = plaintext.encode('utf-8') except: pass return create_encrypted_message(fulljid, account, plaintext, msg_id=msg_id)
def test_initial_user_is_not_set(self): omemo_user = ProfOmemoUser() assert omemo_user.account is None assert omemo_user.fulljid is None
def test_prof_omemo_user_is_singleton(self): assert ProfOmemoUser().account == ProfOmemoUser().account assert ProfOmemoUser().fulljid == ProfOmemoUser().fulljid
def prof_on_disconnect(account_name, fulljid): log.debug('prof_on_disconnect() called') ProfOmemoUser.reset()
def prof_on_shutdown(): log.debug('prof_on_shutdown() called') ProfOmemoUser.reset()
def prof_on_disconnect(account_name, fulljid): log.debug('prof_on_disconnect() called') ProfOmemoUser.reset()
def prof_on_connect(account_name, fulljid): log.debug('prof_on_connect() called') ProfOmemoUser.set_user(account_name, fulljid) _init_omemo()
def prof_on_message_stanza_receive(stanza): stanza = ensure_unicode_stanza(stanza) log.info('Received Message: {0}'.format(stanza)) if xmpp.is_devicelist_update(stanza): log.info('Device List update detected.') try: _handle_devicelist_update(stanza) except: log.exception('Failed to handle devicelist update.') return False if xmpp.is_encrypted_message(stanza): log.info('Received OMEMO encrypted message.') omemo_state = ProfOmemoState() try: msg_dict = xmpp.unpack_encrypted_stanza(stanza) sender = msg_dict['sender_jid'] resource = msg_dict['sender_resource'] sender_fulljid = sender + '/' + resource if sender_fulljid == ProfOmemoUser().fulljid: log.debug('Skipping own Message.') return False try: plain_msg = omemo_state.decrypt_msg(msg_dict) except Exception: log.exception('Could not decrypt Message.') return False if plain_msg is None: log.error('Could not decrypt Message') return True if plain_msg: # only mark the message if it was an OMEMO encrypted message try: message_char = _get_omemo_message_char() log.debug('Set incoming Message Character: {0}'.format( message_char)) prof.chat_set_incoming_char(sender, message_char) prof.incoming_message(sender, resource, plain_msg) finally: prof.chat_unset_incoming_char(sender) # if this was the first OMEMO encrypted message received by # the sender (a.k.a. whenever profanity opens a new chat window # for a recipient), we automatically respond with OMEMO encrypted # messages. If encryption is turned off later by the user, # we respect that. if not ProfActiveOmemoChats.account_is_active(sender): if not ProfActiveOmemoChats.account_is_deactivated(sender): _start_omemo_session(sender) return False except Exception: # maybe not OMEMO encrypted, profanity will take care then log.exception('Could not handle encrypted message.') return True
def teardown_method(self, test_method): ProfOmemoUser.reset() ProfActiveOmemoChats.reset()
def _query_bundle_info_for(recipient, deviceid): log.info('Query Bundle for {0}:{1}'.format(recipient, deviceid)) account = ProfOmemoUser().account stanza = xmpp.create_bundle_request_stanza(account, recipient, deviceid) send_stanza(stanza)
def _query_device_list(contact_jid): log.info('Query Device list for {0}'.format(contact_jid)) fulljid = ProfOmemoUser().fulljid query_msg = xmpp.create_devicelist_query_msg(fulljid, contact_jid) send_stanza(query_msg)
def _announce_own_devicelist(): fulljid = ProfOmemoUser().fulljid query_msg = xmpp.create_devicelist_update_msg(fulljid) log.info('Announce own device list.') log.info(query_msg) send_stanza(query_msg)
def prof_on_unload(): log.debug('prof_on_unload() called') ProfOmemoUser.reset()
def prof_on_shutdown(): log.debug('prof_on_shutdown() called') ProfOmemoUser.reset()
def setup_method(self, test_method): account = '*****@*****.**' fulljid = '[email protected]/profanity' ProfOmemoUser.set_user(account, fulljid)
def _parse_args(arg1=None, arg2=None, arg3=None): """ Parse arguments given in command window arg1: start || end arg2: muc || jid (optional) Starts or ends an encrypted chat session """ account = ProfOmemoUser().account fulljid = ProfOmemoUser().fulljid if arg1 == 'on': _set_omemo_enabled_setting(True) elif arg1 == 'off': _set_omemo_enabled_setting(False) elif arg1 == 'start': # ensure we are in a chat window current_recipient = prof.get_current_recipient() if not current_recipient and arg2 != current_recipient: log.info('Opening Chat Window for {0}'.format(arg2)) prof.send_line('/msg {0}'.format(arg2)) recipient = arg2 or current_recipient if recipient: log.info('Start OMEMO session with: {0}'.format(recipient)) _start_omemo_session(recipient) elif arg1 == 'end': # ensure we are in a chat window jid = arg2 or prof.get_current_muc() or prof.get_current_recipient() log.info('Ending OMEMO session with: {0}'.format(jid)) if jid: _end_omemo_session(jid) elif arg1 == 'set': if arg2 == 'message_prefix': if arg3 is not None: _set_omemo_message_char(arg3) elif arg1 == 'account': prof.cons_show('Account: {0}'.format(account)) elif arg1 == 'status': enabled = _get_omemo_enabled_setting() prof.cons_show('OMEMO Plugin Enabled: {0}'.format(enabled)) elif arg1 == 'fulljid': prof.cons_show('Current JID: {0}'.format(fulljid)) elif arg1 == 'show_devices' and arg2 is not None: account = arg2 omemo_state = ProfOmemoState() prof.cons_show('Requesting Devices...') devices = omemo_state.device_list_for(account) prof.cons_show('Devices: {0}'.format(devices)) prof.cons_show('{0}: {1}'.format(account, ', '.join(devices))) elif arg1 == 'reset_devicelist' and arg2 is not None: contact_jid = arg2 if contact_jid != ProfOmemoUser.account: omemo_state = ProfOmemoState() omemo_state.set_devices(contact_jid, []) _query_device_list(contact_jid) elif arg1 == 'fingerprints': if arg2: contact_jid = query_jid = arg2 else: # The local account is identified as '-1' in the OMEMO database contact_jid = ProfOmemoUser.account query_jid = '-1' omemo_state = ProfOmemoState() fingerprints = omemo_state.getFingerprints(query_jid) prof.cons_show('Fingerprints for account: {0}'.format(contact_jid)) for record in fingerprints: _id, recipient_id, public_key, trust = record fpr = binascii.hexlify(public_key) fpr = human_hash(fpr[2:]) prof.cons_show(' {0}'.format(fpr)) else: prof.cons_show('Argument {0} not supported.'.format(arg1))
def teardown_method(self, test_method): ProfActiveOmemoChats.reset() ProfOmemoUser.reset()
def prof_on_unload(): log.debug('prof_on_unload() called') ProfOmemoUser.reset()
def setup_method(self, test_method): account = '*****@*****.**' fulljid = '[email protected]/profanity' ProfOmemoUser.set_user(account, fulljid)
def prof_on_connect(account_name, fulljid): log.debug('prof_on_connect() called') ProfOmemoUser.set_user(account_name, fulljid) _init_omemo()