Module: session_manager

exception omemo.session_manager.SessionManagerException[source]

Bases: OMEMOException

Parent type for all exceptions specific to SessionManager.

exception omemo.session_manager.TrustDecisionFailed[source]

Bases: SessionManagerException

Raised by SessionManager._make_trust_decision() if the trust decisions that were queried somehow failed. Indirectly raised by the encryption flow.

exception omemo.session_manager.StillUndecided[source]

Bases: SessionManagerException

Raised by SessionManager.encrypt() in case there are still undecided devices after a trust decision was queried via SessionManager._make_trust_decision().

exception omemo.session_manager.NoEligibleDevices(bare_jids, *args)[source]

Bases: SessionManagerException

Raised by SessionManager.encrypt() in case none of the devices of one or more recipient are eligible for encryption, for example due to distrust or bundle downloading failures.

Parameters
  • bare_jids (FrozenSet[str]) –

  • args (object) –

Return type

None

__init__(bare_jids, *args)[source]
Parameters
  • bare_jids (FrozenSet[str]) – The JIDs whose devices were not eligible. Accessible as an attribute of the returned instance.

  • args (object) –

Return type

None

exception omemo.session_manager.MessageNotForUs[source]

Bases: SessionManagerException

Raised by SessionManager.decrypt() in case the message to decrypt does not seem to be encrypting for this device.

exception omemo.session_manager.SenderNotFound[source]

Bases: SessionManagerException

Raised by SessionManager.decrypt() in case the usual public information of the sending device could not be downloaded.

exception omemo.session_manager.SenderDistrusted[source]

Bases: SessionManagerException

Raised by SessionManager.decrypt() in case the sending device is explicitly distrusted.

exception omemo.session_manager.NoSession[source]

Bases: SessionManagerException

Raised by SessionManager.decrypt() in case there is no session with the sending device, and a new session can’t be built either.

exception omemo.session_manager.PublicDataInconsistency[source]

Bases: SessionManagerException

Raised by SessionManager.decrypt() in case inconsistencies were found in the public data of the sending device.

exception omemo.session_manager.UnknownTrustLevel[source]

Bases: SessionManagerException

Raised by SessionManager._evaluate_custom_trust_level() if the custom trust level name to evaluate is unknown. Indirectly raised by the encryption and decryption flows.

exception omemo.session_manager.UnknownNamespace[source]

Bases: SessionManagerException

Raised by various methods of SessionManager, in case the namespace to perform an operation under is not known or the corresponding backend is not currently loaded.

exception omemo.session_manager.XMPPInteractionFailed[source]

Bases: SessionManagerException

Parent type for all exceptions related to network/XMPP interactions.

exception omemo.session_manager.BundleUploadFailed[source]

Bases: XMPPInteractionFailed

Raised by SessionManager._upload_bundle(), and indirectly by various methods of SessionManager.

exception omemo.session_manager.BundleDownloadFailed[source]

Bases: XMPPInteractionFailed

Raised by SessionManager._download_bundle(), and indirectly by various methods of SessionManager.

exception omemo.session_manager.BundleNotFound[source]

Bases: XMPPInteractionFailed

Raised by SessionManager._download_bundle(), and indirectly by various methods of SessionManager.

exception omemo.session_manager.BundleDeletionFailed[source]

Bases: XMPPInteractionFailed

Raised by SessionManager._delete_bundle(), and indirectly by SessionManager.purge_backend().

exception omemo.session_manager.DeviceListUploadFailed[source]

Bases: XMPPInteractionFailed

Raised by SessionManager._upload_device_list(), and indirectly by various methods of SessionManager.

exception omemo.session_manager.DeviceListDownloadFailed[source]

Bases: XMPPInteractionFailed

Raised by SessionManager._download_device_list(), and indirectly by various methods of SessionManager.

exception omemo.session_manager.MessageSendingFailed[source]

Bases: XMPPInteractionFailed

Raised by SessionManager._send_message(), and indirectly by various methods of SessionManager.

class omemo.session_manager.SessionManager[source]

Bases: ABC

The core of python-omemo. Manages your own key material and bundle, device lists, sessions with other users and much more, all while being flexibly usable with different backends and transparenlty maintaining a level of compatibility between the backends that allows you to maintain a single identity throughout all of them. Easy APIs are provided to handle common use-cases of OMEMO-enabled XMPP clients, with one of the primary goals being strict type safety.

Note

Most methods can raise StorageException in addition to those exceptions listed explicitly.

Note

All parameters are treated as immutable unless explicitly noted otherwise.

Note

All usages of “identity key” in the public API refer to the public part of the identity key pair in Ed25519 format. Otherwise, “identity key pair” is explicitly used to refer to the full key pair.

Note

The library was designed for use as part of an XMPP library/client. The API is shaped for XMPP and comments/documentation contain references to XEPs and other XMPP-specific nomenclature. However, the library can be used with any economy that provides similar functionality.

DEVICE_ID_MIN = 1
DEVICE_ID_MAX = 2147483647
STALENESS_MAGIC_NUMBER = 53
LOG_TAG = 'omemo.core'
async classmethod create(backends, storage, own_bare_jid, initial_own_label, undecided_trust_level_name, signed_pre_key_rotation_period=604800, pre_key_refill_threshold=99, async_framework=AsyncFramework.ASYNCIO)[source]

Load or create OMEMO backends. This method takes care of everything regarding the initialization of OMEMO: generating a unique device id, uploading the bundle and adding the new device to the device list. While doing so, it makes sure that all backends share the same identity key, so that a certain level of compatibility between the backends can be achieved. If a backend was created before, this method loads the backend from the storage instead of creating it.

Parameters
  • backends (List[Backend]) – The list of backends to use.

  • storage (Storage) – The storage for all OMEMO-related data.

  • own_bare_jid (str) – The own bare JID of the account this device belongs to.

  • initial_own_label (Optional[str]) – The initial (optional) label to assign to this device if supported by any of the backends.

  • undecided_trust_level_name (str) – The name of the custom trust level to initialize the trust level with when a new device is first encoutered. _evaluate_custom_trust_level() should evaluate this custom trust level to UNDECIDED.

  • signed_pre_key_rotation_period (int) – The rotation period for the signed pre key, in seconds. The rotation period is recommended to be between one week (the default) and one month.

  • pre_key_refill_threshold (int) – The number of pre keys that triggers a refill to 100. Defaults to 99, which means that each pre key gets replaced with a new one right away. The threshold can not be configured to lower than 25.

  • async_framework (AsyncFramework) – The framework to use to create asynchronous tasks and perform asynchronous waiting. Defaults to asyncio, since it’s part of the standard library. Make sure the respective framework is installed when using something other than asyncio.

Return type

TypeVar(SessionManagerTypeT, bound= SessionManager)

Returns

A configured instance of SessionManager, with all backends loaded, bundles published and device lists adjusted.

Raises

Warning

The library starts in history synchronization mode. Call after_history_sync() to return to normal operation. Refer to the documentation of before_history_sync() and after_history_sync() for details.

Warning

The library takes care of keeping online data in sync. That means, if the library is loaded without a backend that was loaded before, it will remove all online data related to the missing backend and as much of the offline data as possible (refer to purge_backend() for details).

Note

This method takes care of leaving the device lists in a consistent state. To do so, backends are “initialized” one after the other. For each backend, the device list is updated as the very last step, after everything else that could fail is done. This ensures that either all data is consistent or the device list does not yet list the inconsistent device. If the creation of one backend succeeds, the data is persisted in the storage before the next backend is created. This guarantees that even if the next backend creation fails, the data is not lost and will be loaded from the storage when calling this method again.

Note

The order of the backends can optionally be used by encrypt() as the order of priority, in case a recipient device supports multiple backends. Refer to the documentation of encrypt() for details.

async purge_backend(namespace)[source]

Purge a backend, removing both the online data (bundle, device list entry) and the offline data that belongs to this backend. Note that the backend-specific offline data can only be purged if the respective backend is currently loaded. This backend-specific removal can be triggered manually at any time by calling the purge() method of the respecfive backend. If the backend to purge is currently loaded, the method will unload it.

Parameters

namespace (str) – The XML namespace managed by the backend to purge.

Raises
Return type

None

Warning

Make sure to unsubscribe from updates to all device lists before calling this method.

Note

If the backend-specific offline data is not purged, the backend can be loaded again at a later point and the online data can be restored. This is what happens when a backend that was previously loaded is omitted from create().

Return type

None

Parameters

namespace (str) –

async purge_bare_jid(bare_jid)[source]

Delete all data corresponding to an XMPP account. This includes the device list, trust information and all sessions across all loaded backends. The backend-specific data can be removed at any time by calling the purge_bare_jid() method of the respective backend.

Parameters

bare_jid (str) – Delete all data corresponding to this bare JID.

Return type

None

async ensure_data_consistency()[source]

Ensure that the online data for all loaded backends is consistent with the offline data. Refreshes device lists of all backends while making sure that this device is included in all of them. Downloads the bundle for each backend, compares it with the local bundle contents, and uploads the local bundle if necessary.

Raises
Return type

None

Note

This method is not called automatically by the library, since under normal working conditions, online and offline data should never desync. However, if clients can spare the network traffic, it is recommended to call this method e.g. once after starting the library and possibly in other scenarios/at regular intervals too.

Return type

None

abstract async static _upload_bundle(bundle)[source]

Upload the bundle corresponding to this device, overwriting any previously published bundle data.

Parameters

bundle (Bundle) – The bundle to publish.

Raises
Return type

None

Note

This method is called from create(), before create() has returned the instance. Thus, modifications to the object (self, in case of subclasses) may not have happened when this method is called.

Note

This method must be able to handle at least the namespaces of all loaded backends.

Return type

None

Parameters

bundle (Bundle) –

abstract async static _download_bundle(namespace, bare_jid, device_id)[source]

Download the bundle corresponding to a specific device.

Parameters
  • namespace (str) – The XML namespace to execute this operation under.

  • bare_jid (str) – The bare JID the device belongs to.

  • device_id (int) – The id of the device.

Return type

Bundle

Returns

The bundle.

Raises

Note

This method is called from create(), before create() has returned the instance. Thus, modifications to the object (self, in case of subclasses) may not have happened when this method is called.

Note

This method must be able to handle at least the namespaces of all loaded backends.

abstract async static _delete_bundle(namespace, device_id)[source]

Delete the bundle corresponding to this device.

Parameters
  • namespace (str) – The XML namespace to execute this operation under.

  • device_id (int) – The id of this device.

Raises
  • UnknownNamespace – if the namespace is unknown.

  • BundleDeletionFailed – if the deletion failed. Feel free to raise a subclass instead. Only raise this on a technical bundle deletion failure. If the bundle just doesn’t exist, don’t raise.

Return type

None

Note

This method is called from create(), before create() has returned the instance. Thus, modifications to the object (self, in case of subclasses) may not have happened when this method is called.

Note

This method must be able to handle at least the namespaces of all loaded backends. In case of backend purging via purge_backend(), the corresponding namespace must be supported even if the backend is not currently loaded.

Return type

None

Parameters
  • namespace (str) –

  • device_id (int) –

abstract async static _upload_device_list(namespace, device_list)[source]

Upload the device list for this XMPP account.

Parameters
  • namespace (str) – The XML namespace to execute this operation under.

  • device_list (Dict[int, Optional[str]]) – The device list to upload. Mapping from device id to optional label.

Raises
Return type

None

Note

This method is called from create(), before create() has returned the instance. Thus, modifications to the object (self, in case of subclasses) may not have happened when this method is called.

Note

This method must be able to handle at least the namespaces of all loaded backends.

Return type

None

Parameters
  • namespace (str) –

  • device_list (Dict[int, Optional[str]]) –

abstract async static _download_device_list(namespace, bare_jid)[source]

Download the device list of a specific XMPP account.

Parameters
  • namespace (str) – The XML namespace to execute this operation under.

  • bare_jid (str) – The bare JID of the XMPP account.

Return type

Dict[int, Optional[str]]

Returns

The device list as a dictionary, mapping the device ids to their optional label.

Raises
  • UnknownNamespace – if the namespace is unknown.

  • DeviceListDownloadFailed – if the download failed. Feel free to raise a subclass instead. Only raise this on a technical device list download failure. If the device list just doesn’t exist, return and empty list instead.

Note

This method is called from create(), before create() has returned the instance. Thus, modifications to the object (self, in case of subclasses) may not have happened when this method is called.

Note

This method must be able to handle at least the namespaces of all loaded backends.

abstract async _evaluate_custom_trust_level(device)[source]

Evaluate a custom trust level to one of the three core trust levels:

  • TRUSTED: This device is trusted, encryption/decryption of messages to/from it is allowed.

  • DISTRUSTED: This device is explicitly not trusted, do not encrypt/decrypt messages to/from it.

  • UNDECIDED: A trust decision is yet to be made. It is not clear whether it is okay to encrypt messages to it, however decrypting messages from it is allowed.

Parameters

device (DeviceInformation) – Information about the device, including the custom trust level name to translate.

Return type

TrustLevel

Returns

The core trust level corresponding to the custom trust level.

Raises

UnknownTrustLevel – if a custom trust level with this name is not known. Feel free to raise a subclass instead.

abstract async _make_trust_decision(undecided, identifier)[source]

Make a trust decision on a set of undecided identity keys. The trust decisions are expected to be persisted by calling set_trust().

Parameters
  • undecided (FrozenSet[DeviceInformation]) – A set of devices that require trust decisions.

  • identifier (Optional[str]) – A piece of application-specific information that callers can pass to encrypt(), which is then forwarded here unaltered. This can be used, for example, by instant messaging clients, to identify the chat tab which triggered the call to encrypt() and subsequently this call to _make_trust_decision().

Raises

TrustDecisionFailed – if for any reason the trust decision failed/could not be completed. Feel free to raise a subclass instead.

Return type

None

Note

This is called when the encryption needs to know whether it is allowed to encrypt for these devices or not. When this method returns, all previously undecided trust levels should have been replaced by calling set_trust() with a different trust level. If they are not replaced or still evaluate to the undecided trust level after the call, the encryption will fail with an exception. See encrypt() for details.

Return type

None

Parameters
abstract async static _send_message(message, bare_jid)[source]

Send an OMEMO-encrypted message. This is required for various automated behaviours to improve the overall stability of the protocol, for example:

  • Automatic handshake completion, by responding to incoming key exchanges.

  • Automatic heartbeat messages to forward the ratchet if many messages were received without a (manual) response, to assure forward secrecy (aka staleness prevention). The number of messages required to trigger this behaviour is hardcoded in STALENESS_MAGIC_NUMBER.

  • Automatic session initiation if an encrypted message is received but no session exists for that device.

  • Backend-dependent session healing mechanisms.

  • Backend-dependent empty messages to notify other devices about potentially “broken” sessions.

Note that messages sent here do not contain any content, they just transport key material.

Parameters
  • message (Message) – The message to send.

  • bare_jid (str) – The bare JID to send the message to.

Raises
Return type

None

async update_device_list(namespace, bare_jid, device_list)[source]

Update the device list of a specific bare JID, e.g. after receiving an update for the XMPP account from PEP.

Parameters
  • namespace (str) – The XML namespace to execute this operation under.

  • bare_jid (str) – The bare JID of the XMPP account.

  • device_list (Dict[int, Optional[str]]) – The updated device list. Mapping from device id to optional label.

Raises
Return type

None

async refresh_device_list(namespace, bare_jid)[source]

Manually trigger the refresh of a device list.

Parameters
  • namespace (str) – The XML namespace to execute this operation under.

  • bare_jid (str) – The bare JID of the XMPP account.

Raises
Return type

None

async set_trust(bare_jid, identity_key, trust_level_name)[source]

Set the trust level for an identity key.

Parameters
  • bare_jid (str) – The bare JID of the XMPP account this identity key belongs to.

  • identity_key (bytes) – The identity key.

  • trust_level_name (str) – The custom trust level to set for the identity key.

Return type

None

async replace_sessions(device)[source]

Manually replace all sessions for a device. Can be used if sessions are suspected to be broken. This method automatically notifies the other end about the new sessions, so that hopefully no messages are lost.

Parameters

device (DeviceInformation) – The device whose sessions to replace.

Return type

Dict[str, OMEMOException]

Returns

Information about exceptions that happened during session replacement attempts. A mapping from the namespace of the backend for which the replacement failed, to the reason of failure. If the reason is a StorageException, there is a high change that the session was left in an inconsistent state. Other reasons imply that the session replacement failed before having any effect on the state of either side.

Warning

This method can not guarantee that sessions are left in a consistent state. For example, if a notification message for the recipient is lost or heavily delayed, the recipient may not know about the new session and keep using the old one. Only use this method to attempt replacement of sessions that already seem broken. Do not attempt to replace healthy sessions.

Warning

This method does not optimize towards minimizing network usage. One notification message is sent per session to replace, the notifications are not bundled. This is to minimize the negative impact of network failure.

async get_sending_chain_length(device)[source]

Get the sending chain lengths of all sessions with a device. Can be used for external staleness detection logic.

Parameters

device (DeviceInformation) – The device.

Return type

Dict[str, Optional[int]]

Returns

A mapping from namespace to sending chain length. None for the sending chain length implies that there is no session with the device for that backend.

async set_own_label(own_label)[source]

Replace the label for this device, if supported by any of the backends.

Parameters

own_label (Optional[str]) – The new (optional) label for this device.

Raises
Return type

None

Note

It is recommended to keep the length of the label under 53 unicode code points.

Return type

None

Parameters

own_label (Optional[str]) –

async get_device_information(bare_jid)[source]
Parameters

bare_jid (str) – Get information about the devices of the XMPP account belonging to this bare JID.

Return type

FrozenSet[DeviceInformation]

Returns

Information about each device of bare_jid. The information includes the device id, the identity key, the trust level, whether the device is active and, if supported by any of the backends, the optional label. Returns information about all known devices, regardless of the backend they belong to.

Note

Only returns information about cached devices. The cache, however, should be up to date if PEP updates are correctly fed to update_device_list(). A manual update of a device list can be triggered using refresh_device_list() if needed.

Warning

This method attempts to download the bundle of devices whose corresponding identity key is not known yet. In case the information can not be fetched due to bundle download failures, the device is not included in the returned set.

async get_own_device_information()[source]

Variation of get_device_information() for convenience.

Return type

Tuple[DeviceInformation, FrozenSet[DeviceInformation]]

Returns

A tuple, where the first entry is information about this device and the second entry contains information about the other devices of the own bare JID.

static format_identity_key(identity_key)[source]
Parameters

identity_key (bytes) – The identity key to generate the fingerprint of.

Return type

List[str]

Returns

The fingerprint of the identity key in its Curve25519 form as per the specficiaton, in eight groups of eight lowercase hex chars each. Consider applying Consistent Color Generation to each individual group when displaying the fingerprint, if applicable.

before_history_sync()[source]

Sets the library into “history synchronization mode”. In this state, the library assumes that it was offline before and is now running catch-up with whatever happened during the offline phase. Make sure to call after_history_sync() when the history synchronization (if any) is done, so that the library can change to normal working behaviour again. The library automatically enters history synchronization mode when loaded via create(). Calling this method again when already in history synchronization mode has no effect.

Internally, the library does the following things differently during history synchronization:

  • Pre keys are kept around during history synchronization, to account for the (hopefully rather hypothetical) case that two or more parties selected the same pre key to initiate a session with this device while it was offline. When history synchronization ends, all pre keys that were kept around are deleted and the library returns to normal behaviour.

  • Empty messages to “complete” sessions or prevent staleness are deferred until after the synchronization is done. Only one empty message is sent per session when exiting the history synchronization mode.

Note

While in history synchronization mode, the library can process live events too.

Return type

None

async after_history_sync()[source]

If the library is in “history synchronization mode” started by create() or before_history_sync(), calling this makes it return to normal working behaviour. Make sure to call this as soon as history synchronization (if any) is done.

Raises

MessageSendingFailed – if one of the queued empty messages could not be sent. Forwarded from _send_message().

Return type

None

async encrypt(bare_jids, plaintext, backend_priority_order=None, identifier=None)[source]

Encrypt some plaintext for a set of recipients.

Parameters
  • bare_jids (FrozenSet[str]) – The bare JIDs of the intended recipients.

  • plaintext (Dict[str, bytes]) – The plaintext to encrypt for the recipients. Since different backends may use different kinds of plaintext, for example just the message body versus a whole stanza using Stanza Content Encryption, this parameter is a dictionary, where the keys are backend namespaces and the values are the plaintext for each specific backend. The plaintext has to be supplied for each backend.

  • backend_priority_order (Optional[List[str]]) – If a recipient device supports multiple versions of OMEMO, this parameter decides which version to prioritize. If None is supplied, the order of backends as passed to create() is assumed as the order of priority. If a list of namespaces is supplied, the first namespace supported by the recipient is chosen. Lower index means higher priority.

  • identifier (Optional[str]) – A value that is passed on to _make_trust_decision() in case a trust decision is required for any of the recipient devices. This value is not processed or altered, it is simply passed through. Refer to the documentation of _make_trust_decision() for details.

Return type

Tuple[Dict[Message, PlainKeyMaterial], FrozenSet[EncryptionError]]

Returns

A mapping with one message per backend as the keys encrypted for each device of each recipient and for other devices of this account, and the plain key material that was used to encrypt the content of the respective message as values. This plain key material can be used to implement things like legacy OMEMO’s KeyTransportMessages. Next to the messages, a set of non-critical errors encountered during encryption are returned.

Raises
  • UnknownNamespace – if the backend priority order list contains a namespace of a backend that is not currently available.

  • UnknownTrustLevel – if an unknown custom trust level name is encountered. Forwarded from _evaluate_custom_trust_level().

  • TrustDecisionFailed – if for any reason the trust decision for undecided devices failed/could not be completed. Forwarded from _make_trust_decision().

  • StillUndecided – if the trust level for one of the recipient devices still evaluates to undecided, even after _make_trust_decision() was called to decide on the trust.

  • NoEligibleDevices – if at least one of the intended recipients does not have a single device which qualifies for encryption. Either the recipient does not advertize any OMEMO-enabled devices or all devices were disqualified due to missing trust or failure to download their bundles.

  • KeyExchangeFailed – in case there is an error during the key exchange required for session building. Forwarded from build_session_active().

Note

The own JID is implicitly added to the set of recipients, there is no need to list it manually.

async decrypt(message)[source]

Decrypt a message.

Parameters

message (Message) – The message to decrypt.

Return type

Tuple[Optional[bytes], DeviceInformation, PlainKeyMaterial]

Returns

A triple, where the first entry is the decrypted plaintext and the second entry contains information about the device that sent the message. The plaintext is optional and will be None in case the message was an empty OMEMO message purely used for protocol stability reasons. The third entry is the plain key meterial transported by the message, which can be used to implement functionality like legacy OMEMO’s KeyTransportMessages.

Raises
  • UnknownNamespace – if the backend to handle the message is not currently loaded.

  • UnknownTrustLevel – if an unknown custom trust level name is encountered. Forwarded from _evaluate_custom_trust_level().

  • KeyExchangeFailed – in case a new session is built while decrypting this message, and there is an error during the key exchange that’s part of the session building. Forwarded from build_session_passive().

  • MessageNotForUs – in case the message does not seem to be encrypted for us.

  • SenderNotFound – in case the public information about the sending device could not be found or is incomplete.

  • SenderDistrusted – in case the identity key corresponding to the sending device is explicitly distrusted.

  • NoSession – in case there is no session with the sending device, and the information required to build a new session is not included either.

  • PublicDataInconsistency – in case there is an inconsistency in the public data of the sending device, which can affect the trust status.

  • MessageSendingFailed – if an attempt to send an empty OMEMO message failed. Forwarded from _send_message().

  • DecryptionFailed – in case of backend-specific failures during decryption. Forwarded from the respective backend implementation.

Warning

Do NOT implement any automatic reaction to decryption failures, those automatic reactions are transparently handled by the library! Do notify the user about decryption failures though, if applicable.

Note

If the trust level of the sender evaluates to undecided, the message is decrypted.

Note

May send empty OMEMO messages to “complete” key exchanges or prevent staleness.