Learn about chain length: new should_heartbeat method
With this commit, slixmpp-omemo now reads the ratchet chain length, (both receiving and sending), that we should track to know when to send a heartbeat message. This allows us to signal other devices that we are still active and listening. Some clients will stop encrypting to us if we haven't replied for a certain number of messages. The current 0384 spec (0.7) says we should send a heartbeat message at least once this number goes over 53 (fair dice roll). It doesn't say when a client may/should stop encrypting to us, or what it should do at all once we go over 53. Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
This commit is contained in:
parent
38701075e9
commit
af33cd41e5
2 changed files with 64 additions and 0 deletions
|
@ -90,6 +90,8 @@ class EchoBot(ClientXMPP):
|
||||||
await self.cmd_verbose(mto, mtype)
|
await self.cmd_verbose(mto, mtype)
|
||||||
elif cmd == 'error':
|
elif cmd == 'error':
|
||||||
await self.cmd_error(mto, mtype)
|
await self.cmd_error(mto, mtype)
|
||||||
|
elif cmd == 'chain_length':
|
||||||
|
await self.cmd_chain_length(mto, mtype)
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -112,6 +114,13 @@ class EchoBot(ClientXMPP):
|
||||||
body = '''Debug level set to 'error'.'''
|
body = '''Debug level set to 'error'.'''
|
||||||
return await self.encrypted_reply(mto, mtype, body)
|
return await self.encrypted_reply(mto, mtype, body)
|
||||||
|
|
||||||
|
async def cmd_chain_length(self, mto: JID, mtype: str) -> None:
|
||||||
|
body = (
|
||||||
|
'lengths: %r\n' % self['xep_0384']._chain_lengths(mto) +
|
||||||
|
'should heartbeat: %r' % self['xep_0384'].should_heartbeat(mto)
|
||||||
|
)
|
||||||
|
return await self.encrypted_reply(mto, mtype, body)
|
||||||
|
|
||||||
def message_handler(self, msg: Message) -> None:
|
def message_handler(self, msg: Message) -> None:
|
||||||
asyncio.ensure_future(self.message(msg))
|
asyncio.ensure_future(self.message(msg))
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,10 @@ import logging
|
||||||
|
|
||||||
from typing import Any, Dict, List, Optional, Set, Tuple, Union
|
from typing import Any, Dict, List, Optional, Set, Tuple, Union
|
||||||
|
|
||||||
|
# Not available in Python 3.7, and slixmpp already imports the right things
|
||||||
|
# for me
|
||||||
|
from slixmpp.types import TypedDict
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import base64
|
import base64
|
||||||
|
@ -57,6 +61,11 @@ PUBLISH_OPTIONS_NODE = 'http://jabber.org/protocol/pubsub#publish-options'
|
||||||
PUBSUB_ERRORS = 'http://jabber.org/protocol/pubsub#errors'
|
PUBSUB_ERRORS = 'http://jabber.org/protocol/pubsub#errors'
|
||||||
|
|
||||||
|
|
||||||
|
class ChainLengths(TypedDict):
|
||||||
|
receiving: List[Tuple[int, int]]
|
||||||
|
sending: List[Tuple[int, int]]
|
||||||
|
|
||||||
|
|
||||||
def b64enc(data: bytes) -> str:
|
def b64enc(data: bytes) -> str:
|
||||||
return base64.b64encode(bytes(bytearray(data))).decode('ASCII')
|
return base64.b64encode(bytes(bytearray(data))).decode('ASCII')
|
||||||
|
|
||||||
|
@ -188,6 +197,8 @@ class XEP_0384(BasePlugin):
|
||||||
'storage_backend': None,
|
'storage_backend': None,
|
||||||
'otpk_policy': DefaultOTPKPolicy,
|
'otpk_policy': DefaultOTPKPolicy,
|
||||||
'omemo_backend': SignalBackend,
|
'omemo_backend': SignalBackend,
|
||||||
|
'heartbeat_after': 53,
|
||||||
|
# TODO: 'drop_inactive_after': 300,
|
||||||
}
|
}
|
||||||
|
|
||||||
backend_loaded = HAS_OMEMO and HAS_OMEMO_BACKEND
|
backend_loaded = HAS_OMEMO and HAS_OMEMO_BACKEND
|
||||||
|
@ -481,6 +492,50 @@ class XEP_0384(BasePlugin):
|
||||||
"""Return active device ids. Always contains our own device id."""
|
"""Return active device ids. Always contains our own device id."""
|
||||||
return self._omemo.getDevices(jid.bare).get('active', [])
|
return self._omemo.getDevices(jid.bare).get('active', [])
|
||||||
|
|
||||||
|
def _chain_lengths(self, jid: JID) -> ChainLengths:
|
||||||
|
"""
|
||||||
|
Gather receiving and sending chain lengths for all devices (active
|
||||||
|
/ inactive) of a JID.
|
||||||
|
|
||||||
|
Receiving chain length is used to know when to send a heartbeat to
|
||||||
|
signal recipients our device is still active and listening. See:
|
||||||
|
https://xmpp.org/extensions/xep-0384.html#rules
|
||||||
|
|
||||||
|
Sending chain length is used on the other side when a device
|
||||||
|
hasn't been sending us messages and seems inactive.
|
||||||
|
|
||||||
|
# XXX: Only the receiving part is used in this library for the
|
||||||
|
# moment.
|
||||||
|
"""
|
||||||
|
# XXX: This method uses APIs that haven't been made public yet in the
|
||||||
|
# OMEMO library as of 0.12 (9fd7123).
|
||||||
|
|
||||||
|
bare = jid.bare
|
||||||
|
devices = self._omemo.getDevices(bare)
|
||||||
|
active = devices.get('active', set())
|
||||||
|
inactive = devices.get('inactive', set())
|
||||||
|
devices = active.union(inactive)
|
||||||
|
|
||||||
|
lengths: ChainLengths = {'sending': [], 'receiving': []}
|
||||||
|
for did in devices:
|
||||||
|
session = self._omemo._SessionManager__loadSession(bare, did)
|
||||||
|
skr = session._DoubleRatchet__skr
|
||||||
|
lengths['sending'].append((did, skr.sending_chain_length))
|
||||||
|
lengths['receiving'].append((did, skr.receiving_chain_length))
|
||||||
|
|
||||||
|
return lengths
|
||||||
|
|
||||||
|
def should_heartbeat(self, jid: JID) -> bool:
|
||||||
|
"""
|
||||||
|
Returns whether we should send a heartbeat message for JID.
|
||||||
|
See notes about heartbeat in
|
||||||
|
https://xmpp.org/extensions/xep-0384.html#rules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
receiving_chain_lengths = self._chain_lengths(jid).get('receiving', [])
|
||||||
|
lengths = map(lambda d_l: d_l[1], receiving_chain_lengths)
|
||||||
|
return max(lengths, default=0) > self.heartbeat_after
|
||||||
|
|
||||||
def trust(self, jid: JID, device_id: int, ik: bytes) -> None:
|
def trust(self, jid: JID, device_id: int, ik: bytes) -> None:
|
||||||
self._omemo.setTrust(jid.bare, device_id, ik, True)
|
self._omemo.setTrust(jid.bare, device_id, ik, True)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue