plugin XEP-0384: better bundle handling + misc improvments

- public bundles are now retrieved only when needed (i.e. on MissingBundleException when encrypting a message)
- a feedback is given to user when a message is received but it has not been encrypted for our device
- fixed feedback on decryption error if message come from an other device of the client (i.e. it's a carbon copy)
- setBundle if called in a "finally" if republish_bundle is set after decrypting a message
This commit is contained in:
Goffi 2019-02-28 18:57:26 +01:00
commit e8e8881016
2 changed files with 54 additions and 59 deletions

View file

@ -783,35 +783,12 @@ class OMEMO(object):
@defer.inlineCallbacks
def onNewDevices(self, itemsEvent, profile):
client = self.host.getClient(profile)
cache = client._xep_0384_cache
omemo_session = client._xep_0384_session
entity = itemsEvent.sender
entity_cache = cache.setdefault(entity, {})
devices = self.parseDevices(itemsEvent.items)
omemo_session.newDeviceList(entity, devices)
missing_devices = devices.difference(entity_cache.keys())
if missing_devices:
bundles, bundles_not_found = yield self.getBundles(
client, entity, missing_devices)
entity_cache.update(bundles)
if bundles_not_found and entity == client.jid.userhostJID():
# we have devices announced in our own public list
# with missing bundles
own_device = client._xep_0384_device_id
if own_device in bundles_not_found:
log.warning(_(u"Our own device has no attached bundle, fixing it"))
bundles_not_found.remove(own_device)
yield self.setBundle(client, omemo_session.public_bundle, own_device)
if bundles_not_found:
# some announced devices have no bundle, we update our public
# list to remove missing devices.
log.warning(_(
u"Some devices have missing bundles, cleaning out public "
u"devices list"))
existing_devices = devices - bundles_not_found
yield self.setDevices(client, existing_devices)
# we check that our device has not been removed from the list
if entity == client.jid.userhostJID():
own_device = client._xep_0384_device_id
if own_device not in devices:
@ -819,7 +796,6 @@ class OMEMO(object):
devices.add(own_device)
yield self.setDevices(client, devices)
## triggers
@defer.inlineCallbacks
@ -837,13 +813,44 @@ class OMEMO(object):
"""
# FIXME: not all problems are handled yet
untrusted = {}
missing_bundles = {}
expect_problems = {}
cache = client._xep_0384_cache
for problem in problems:
if isinstance(problem, omemo_excpt.UntrustedException):
untrusted[unicode(hash(problem))] = problem
if isinstance(problem, omemo_excpt.MissingBundleException):
pb_entity = jid.JID(problem.bare_jid)
entity_cache = cache.setdefault(pb_entity, {})
entity_bundles = bundles.setdefault(pb_entity, {})
if problem.device in entity_cache:
entity_bundles[problem.device] = entity_cache[problem.device]
else:
found_bundles, missing = yield self.getBundles(
client, pb_entity, [problem.device])
entity_cache.update(bundles)
entity_bundles.update(found_bundles)
if problem.device in missing:
missing_bundles.setdefault(pb_entity, set()).add(
problem.device)
expect_problems.setdefault(problem.bare_jid, set()).add(
problem.device)
elif isinstance(problem, omemo_excpt.NoEligibleDevicesException):
pass
for peer_jid, devices in missing_bundles.iteritems():
devices_s = [unicode(d) for d in devices]
log.warning(
_(u"Can't retrieve bundle for device(s) {devices} of entity {peer}, "
u"the message will not be readable on this/those device(s)").format(
devices=u", ".join(devices_s), peer=peer_jid.full()))
client.feedback(
entity,
D_(u"You're destinee {peer} has missing encryption data on some of "
u"his/her device(s) (bundle on device {devices}), the message won't "
u"be readable on this/those device.").format(
peer=peer_jid.full(), devices=u", ".join(devices_s)))
if untrusted:
trust_data = {}
for trust_id, data in untrusted.iteritems():
@ -873,38 +880,16 @@ class OMEMO(object):
@defer.inlineCallbacks
def encryptMessage(self, client, entity_bare_jid, message):
omemo_session = client._xep_0384_session
cache = client._xep_0384_cache
try:
bundles = {entity_bare_jid: cache[entity_bare_jid]}
except KeyError:
# No devices know for this entity, let try to find them.
# This can happen if the entity is not in our roster, or doesn't handle OMEMO
# or if we haven't received the devices from PEP yet.
try:
devices = yield self.getDevices(client, entity_bare_jid)
bundles, __ = yield self.getBundles(client, entity_bare_jid, devices)
except Exception as e:
raise exceptions.NotFound(
_(u"Can retrieve bundles for {entity}: {reason}" .format(
entity=entity_bare_jid.full(), reason=e)))
else:
cache[entity_bare_jid] = bundles
bundles = {entity_bare_jid: bundles}
own_jid = client.jid.userhostJID()
if entity_bare_jid != own_jid:
# message will be copied to our devices, so we need to add our own bundles
bundles[own_jid] = cache[own_jid]
try:
# first try may fail, in case of e.g. trust issue or missing bundle
encrypted = yield omemo_session.encryptMessage(
entity_bare_jid,
message,
bundles)
message)
except omemo_excpt.EncryptionProblemsException as e:
# we know the problem to solve, we can try to fix them
expect_problems = yield self.handleProblems(client, entity_bare_jid, bundles, e.problems)
bundles = {}
expect_problems = yield self.handleProblems(client, entity_bare_jid, bundles,
e.problems)
# and try an encryption again.
try:
encrypted = yield omemo_session.encryptMessage(
@ -932,6 +917,10 @@ class OMEMO(object):
# we have an encrypted message let's decrypt it
from_jid = jid.JID(message_elt['from'])
if from_jid.userhostJID() == client.jid.userhostJID():
feedback_jid = jid.JID(message_elt['to'])
else:
feedback_jid = from_jid
try:
omemo_session = client._xep_0384_session
except AttributeError:
@ -965,6 +954,11 @@ class OMEMO(object):
device_id=device_id,
fingerprint=omemo_session.public_bundle.ik.encode('hex'),
xml=encrypted_elt.toXml()))
user_msg = (D_(u"An OMEMO message from {sender} has not been encrypted for "
u"our device, we can't decrypt it").format(
sender=from_jid.full()))
extra = {C.MESS_EXTRA_INFO: C.EXTRA_INFO_DECR_ERR}
client.feedback(feedback_jid, user_msg, extra)
defer.returnValue(False)
except ValueError as e:
log.warning(_(u"Invalid recipient ID: {msg}".format(msg=e)))
@ -1000,13 +994,14 @@ class OMEMO(object):
user_msg = (D_(u"An OMEMO message from {sender} can't be decrypted: {reason}")
.format(sender=from_jid.full(), reason=e))
extra = {C.MESS_EXTRA_INFO: C.EXTRA_INFO_DECR_ERR}
client.feedback(from_jid, user_msg, extra)
client.feedback(feedback_jid, user_msg, extra)
defer.returnValue(False)
if omemo_session.republish_bundle:
# we don't wait for the Deferred (i.e. no yield) on purpose
# there is no need to block the whole message workflow while
# updating the bundle
self.setBundle(client, omemo_session.public_bundle, device_id)
finally:
if omemo_session.republish_bundle:
# we don't wait for the Deferred (i.e. no yield) on purpose
# there is no need to block the whole message workflow while
# updating the bundle
self.setBundle(client, omemo_session.public_bundle, device_id)
message_elt.children.remove(encrypted_elt)
if plaintext:

View file

@ -50,7 +50,7 @@ install_requires = [
'urwid >= 1.2.0',
'urwid-satext >= 0.6.1',
'wokkel >= 0.7.1',
'omemo >= 0.10.3',
'omemo == 0.10.3', # FIXME: we block to this version until bêta
'omemo_backend_signal',
]