diff --git a/.flake8 b/.flake8 index 0dd0e54..e417eb4 100644 --- a/.flake8 +++ b/.flake8 @@ -4,5 +4,4 @@ max-line-length = 110 doctests = True ignore = E201,E202,W503 per-file-ignores = - omemo/__init__.py:F401 omemo/project.py:E203 diff --git a/README.md b/README.md index ac0b612..49e6264 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ TODO: Add doc badge A Python implementation of the [OMEMO Multi-End Message and Object Encryption protocol](https://xmpp.org/extensions/xep-0384.html). -A complete implementation of [XEP-0384](https://xmpp.org/extensions/xep-0384.html) on protocol-level, i.e. more than just the cryptography. python-omemo supports different versions of the specification through so-called backends. One backend for OMEMO in the `urn:xmpp:omemo:2` namespace is shipped with python-omemo. A backend for (legacy) OMEMO in the `eu.siacs.conversations.axolotl` namespace is available as a separate package: [python-omemo-backend-legacy](https://github.com/Syndace/python-omemo-backend-legacy). Multiple backends can be loaded and used at the same time, the library manages their coexistence transparently. +A complete implementation of [XEP-0384](https://xmpp.org/extensions/xep-0384.html) on protocol-level, i.e. more than just the cryptography. python-omemo supports different versions of the specification through so-called backends. A backend for OMEMO in the `urn:xmpp:omemo:2` namespace (the most recent version of the specification) is available in the [python-newmemo](https://github.com/Syndace/python-newmemo) Python package. A backend for (legacy) OMEMO in the `eu.siacs.conversations.axolotl` namespace is available in the [python-omemo-backend-legacy](https://github.com/Syndace/python-omemo-backend-legacy) package. Multiple backends can be loaded and used at the same time, the library manages their coexistence transparently. ## Installation ## diff --git a/docs/conf.py b/docs/conf.py index 31b8553..ebdbb3a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -78,22 +78,11 @@ def autodoc_skip_member_handler(app, what, name, obj, skip, options): # Could be achieved using exclude-members, but this is more comfy if name in { "__abstractmethods__", - "__annotations__", "__dict__", - "__getnewargs__", "__module__", "__new__", - "__orig_bases__", - "__parameters__", - "__repr__", - "__slots__", "__weakref__", - "_abc_impl", - "_asdict", - "_field_defaults", - "_fields", - "_make", - "_replace" + "_abc_impl" }: return True # Skip __init__s without documentation. Those are just used for type hints. diff --git a/docs/getting_started.rst b/docs/getting_started.rst index b00f843..ecf1c72 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -69,4 +69,4 @@ Finally, instantiate the storage, backends and then the :class:`~omemo.session_m Migration --------- -Refer to :ref:`migration_from_legacy` for information about migrating from pre-stable python-omemo to python-omemo 1.0+. Migrations withing stable (1.0+) versions are handled automatically. +Refer to :ref:`migration_from_legacy` for information about migrating from pre-stable python-omemo to python-omemo 1.0+. Migrations within stable (1.0+) versions are handled automatically. diff --git a/docs/index.rst b/docs/index.rst index 8dd679a..e22e925 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,11 +3,11 @@ OMEMO - A Python implementation of the OMEMO Multi-End Message and Object Encryp A Python implementation of the `OMEMO Multi-End Message and Object Encryption protocol `_. -A complete implementation of `XEP-0384 `_ on protocol-level, i.e. more than just the cryptography. python-omemo supports different versions of the specification through so-called backends. One backend for OMEMO in the `urn:xmpp:omemo:2` namespace is shipped with python-omemo. A backend for (legacy) OMEMO in the `eu.siacs.conversations.axolotl` namespace is available as a separate package: `python-omemo-backend-legacy `_. Multiple backends can be loaded and used at the same time, the library manages their coexistence transparently. +A complete implementation of `XEP-0384 `_ on protocol-level, i.e. more than just the cryptography. python-omemo supports different versions of the specification through so-called backends. A backend for OMEMO in the ``urn:xmpp:omemo:2`` namespace (the most recent version of the specification) is available in the `python-newmemo `_ Python package. A backend for (legacy) OMEMO in the ``eu.siacs.conversations.axolotl`` namespace is available in the `python-omemo-backend-legacy `_ package. Multiple backends can be loaded and used at the same time, the library manages their coexistence transparently. .. toctree:: - Installation - Getting Started - Writing a Backend - Migration from Legacy - Package: omemo + installation + getting_started + writing_a_backend + migration_from_legacy + API Documentation diff --git a/docs/installation.rst b/docs/installation.rst index f02790d..bbad694 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -3,4 +3,4 @@ Installation python-omemo depends on two system libraries, `libxeddsa `__>=2,<3 and `libsodium `__. -Install the latest release using pip (``pip install OMEMO``) or manually from source by running ``pip install .`` (preferred) or ``python setup.py install`` in the cloned repository. The installation requires libsodium and the Python development headers to be installed. If a locally installed version of libxeddsa is available, `python-xeddsa `__ (a dependency of python-omemo) tries to use that. Otherwise it uses prebuilt binaries of the library, which are available for Linux, MacOS and Windows for the amd64 architecture. Set the ``LIBXEDDSA_FORCE_LOCAL`` environment variable to forbid the usage of prebuilt binaries. +Install the latest release using pip (``pip install OMEMO``) or manually from source by running ``pip install .`` (preferred) or ``python setup.py install`` in the cloned repository. The installation requires libsodium and the Python development headers to be installed. If a locally installed version of libxeddsa is available, `python-xeddsa `__ (a dependency of python-omemo) tries to use that. Otherwise it uses prebuilt binaries of the library, which are available for Linux, MacOS and Windows for the amd64 architecture, and potentially for MacOS arm64 too. Set the ``LIBXEDDSA_FORCE_LOCAL`` environment variable to forbid the usage of prebuilt binaries. diff --git a/docs/omemo/message.rst b/docs/omemo/message.rst index f567d8f..4f019c9 100644 --- a/docs/omemo/message.rst +++ b/docs/omemo/message.rst @@ -3,8 +3,7 @@ Module: message .. automodule:: omemo.message :members: - :special-members: - :private-members: + :special-members: __eq__, __hash__ :undoc-members: :member-order: bysource :show-inheritance: diff --git a/docs/omemo/package.rst b/docs/omemo/package.rst index 1474a03..953f279 100644 --- a/docs/omemo/package.rst +++ b/docs/omemo/package.rst @@ -1,5 +1,5 @@ -omemo -===== +Package: omemo +============== .. toctree:: Module: backend diff --git a/docs/omemo/session.rst b/docs/omemo/session.rst index 77a7005..870e9fc 100644 --- a/docs/omemo/session.rst +++ b/docs/omemo/session.rst @@ -3,8 +3,6 @@ Module: session .. automodule:: omemo.session :members: - :special-members: - :private-members: :undoc-members: :member-order: bysource :show-inheritance: diff --git a/docs/omemo/storage.rst b/docs/omemo/storage.rst index 3c45474..7242a3f 100644 --- a/docs/omemo/storage.rst +++ b/docs/omemo/storage.rst @@ -3,7 +3,7 @@ Module: storage .. automodule:: omemo.storage :members: - :special-members: + :special-members: __init__ :private-members: :undoc-members: :member-order: bysource diff --git a/docs/omemo/types.rst b/docs/omemo/types.rst index 585743c..179889d 100644 --- a/docs/omemo/types.rst +++ b/docs/omemo/types.rst @@ -3,8 +3,6 @@ Module: types .. automodule:: omemo.types :members: - :special-members: - :private-members: :undoc-members: :member-order: bysource :show-inheritance: diff --git a/omemo/backend.py b/omemo/backend.py index 1cd5552..6be9c32 100644 --- a/omemo/backend.py +++ b/omemo/backend.py @@ -43,7 +43,7 @@ class KeyExchangeFailed(BackendException): class TooManySkippedMessageKeys(BackendException): """ - Raised by :meth:`Backend.decrypt` if a message skips more message keys than allowed. + Raised by :meth:`Backend.decrypt_key_material` if a message skips more message keys than allowed. """ @@ -53,9 +53,6 @@ class Backend(ABC): version to the core library. Refer to the documentation page :ref:`writing_a_backend` for details about the concept and a guide on building your own backend. - Warning: - All parameters must be treated as immutable unless explicitly noted otherwise. - Warning: Make sure to call :meth:`__init__` from your subclass to configure per-message and per-session skipped message key DoS protection thresholds, and respect those thresholds when decrypting key material using @@ -72,7 +69,7 @@ class Backend(ABC): Note: For backend implementors: as part of your backend implementation, you are expected to subclass various abstract base classes like :class:`~omemo.session.Session`, :class:`~omemo.message.Content`, - :class:`~omemo.message.PlaintKeyMaterial`, :class:`~omemo.message.EncryptedKeyMaterial` and + :class:`~omemo.message.PlainKeyMaterial`, :class:`~omemo.message.EncryptedKeyMaterial` and :class:`~omemo.message.KeyExchange`. Whenever any of these abstract base types appears in a method signature of the :class:`Backend` class, what's actually meant is an instance of your respective subclass. This is not correctly expressed through the type system, since I couldn't think of a clean @@ -101,12 +98,12 @@ def __init__( Args: max_num_per_session_skipped_keys: The maximum number of skipped message keys to keep around per session. Once the maximum is reached, old message keys are deleted to make space for newer - ones. Accessible via :meth:`max_num_per_session_skipped_keys`. + ones. Accessible via :attr:`max_num_per_session_skipped_keys`. max_num_per_message_skipped_keys: The maximum number of skipped message keys to accept in a single message. When set to ``None`` (the default), this parameter defaults to the per-session maximum (i.e. the value of the ``max_num_per_session_skipped_keys`` parameter). This parameter may only be 0 if the per-session maximum is 0, otherwise it must be a number between 1 and the - per-session maximum. Accessible via :meth:`max_num_per_message_skipped_keys`. + per-session maximum. Accessible via :attr:`max_num_per_message_skipped_keys`. """ if max_num_per_message_skipped_keys == 0 and max_num_per_session_skipped_keys != 0: @@ -207,8 +204,8 @@ async def build_session_active( Returns: The newly built session, the encrypted key material and the key exchange information required by the other device to complete the passive part of session building. The - :meth:`~omemo.session.Session.initiation` property of the returned session must return - :attr:`~omemo.session.Initiation.ACTIVE`. The :meth:`~omemo.session.Session.key_exchange` property + :attr:`~omemo.session.Session.initiation` property of the returned session must return + :attr:`~omemo.session.Initiation.ACTIVE`. The :attr:`~omemo.session.Session.key_exchange` property of the returned session must return the information required by the other party to complete its part of the key exchange. @@ -331,16 +328,16 @@ async def decrypt_key_material( Raises: TooManySkippedMessageKeys: if the number of message keys skipped by this message exceeds the upper - limit enforced by :meth:`max_num_per_message_skipped_keys`. + limit enforced by :attr:`max_num_per_message_skipped_keys`. DecryptionFailed: in case of backend-specific failures during decryption. Warning: - Make sure to respect the values of :meth:`max_num_per_session_skipped_keys` and - :meth:`max_num_per_message_skipped_keys`. + Make sure to respect the values of :attr:`max_num_per_session_skipped_keys` and + :attr:`max_num_per_message_skipped_keys`. Note: When the maximum number of skipped message keys for this session, given by - :meth:`max_num_per_session_skipped_keys`, is exceeded, old skipped message keys are deleted to + :attr:`max_num_per_session_skipped_keys`, is exceeded, old skipped message keys are deleted to make space for new ones. """ @@ -364,7 +361,7 @@ async def rotate_signed_pre_key(self) -> Any: @abstractmethod async def hide_pre_key(self, session: Session) -> bool: """ - Hide a pre key from the bundle returned by :meth:`bundle` and pre key count returned by + Hide a pre key from the bundle returned by :meth:`get_bundle` and pre key count returned by :meth:`get_num_visible_pre_keys`, but keep the pre key for cryptographic operations. Args: @@ -404,7 +401,7 @@ async def get_num_visible_pre_keys(self) -> int: """ Returns: The number of visible pre keys available. The number returned here should match the number of pre - keys included in the bundle returned by :meth:`bundle`. + keys included in the bundle returned by :meth:`get_bundle`. """ @abstractmethod @@ -420,7 +417,7 @@ async def generate_pre_keys(self, num_pre_keys: int) -> Any: """ @abstractmethod - async def bundle(self, bare_jid: str, device_id: int) -> Bundle: + async def get_bundle(self, bare_jid: str, device_id: int) -> Bundle: """ Args: bare_jid: The bare JID of this XMPP account, to be included in the bundle. diff --git a/omemo/session.py b/omemo/session.py index f1ca47e..66da36c 100644 --- a/omemo/session.py +++ b/omemo/session.py @@ -17,8 +17,8 @@ class Initiation(enum.Enum): Enumeration identifying whether a session was built through active or passive session initiation. """ - ACTIVE = "ACTIVE" - PASSIVE = "PASSIVE" + ACTIVE: str = "ACTIVE" + PASSIVE: str = "PASSIVE" class Session(ABC): diff --git a/omemo/session_manager.py b/omemo/session_manager.py index efc8036..0b8cc01 100644 --- a/omemo/session_manager.py +++ b/omemo/session_manager.py @@ -367,7 +367,7 @@ async def create( # Publish the bundles for all backends for backend in self.__backends: - await self._upload_bundle(await backend.bundle(self.__own_bare_jid, self.__own_device_id)) + await self._upload_bundle(await backend.get_bundle(self.__own_bare_jid, self.__own_device_id)) # Trigger a refresh of the own device lists for all backends, this will result in this device # being added to the lists and the lists republished. @@ -400,7 +400,10 @@ async def create( for backend in self.__backends: if backend.namespace not in active_namespaces: # Publish the bundle of the new backend - await self._upload_bundle(await backend.bundle(self.__own_bare_jid, self.__own_device_id)) + await self._upload_bundle(await backend.get_bundle( + self.__own_bare_jid, + self.__own_device_id + )) # Trigger a refresh of the own device list of the new backend, this will result in this # device being added to the lists and the lists republished. @@ -416,7 +419,7 @@ async def create( logging.getLogger(SessionManager.LOG_TAG).debug(f"Signed pre key age: {signed_pre_key_age}") if signed_pre_key_age > signed_pre_key_rotation_period: await backend.rotate_signed_pre_key() - await self._upload_bundle(await backend.bundle(self.__own_bare_jid, self.__own_device_id)) + await self._upload_bundle(await backend.get_bundle(self.__own_bare_jid, self.__own_device_id)) logging.getLogger(SessionManager.LOG_TAG).info( "Library core prepared, entering history synchronization mode." @@ -578,7 +581,7 @@ async def ensure_data_consistency(self) -> None: for backend in self.__backends: await self.refresh_device_list(backend.namespace, self.__own_bare_jid) - local_bundle = await backend.bundle(self.__own_bare_jid, self.__own_device_id) + local_bundle = await backend.get_bundle(self.__own_bare_jid, self.__own_device_id) upload_bundle = False try: @@ -2007,7 +2010,7 @@ async def handle_key_exchange( if num_visible_pre_keys <= self.__pre_key_refill_threshold: logging.getLogger(SessionManager.LOG_TAG).debug("Refilling pre keys.") await backend.generate_pre_keys(100 - num_visible_pre_keys) - bundle = await backend.bundle(self.__own_bare_jid, self.__own_device_id) + bundle = await backend.get_bundle(self.__own_bare_jid, self.__own_device_id) await self._upload_bundle(bundle) diff --git a/omemo/storage.py b/omemo/storage.py index 8463008..1b7ede9 100644 --- a/omemo/storage.py +++ b/omemo/storage.py @@ -1,3 +1,6 @@ +# This import from future (theoretically) enables sphinx_autodoc_typehints to handle type aliases better +from __future__ import annotations # pylint: disable=unused-variable + from abc import ABC, abstractmethod import base64 import copy diff --git a/omemo/types.py b/omemo/types.py index 6ab5e00..8b7c761 100644 --- a/omemo/types.py +++ b/omemo/types.py @@ -40,9 +40,9 @@ class TrustLevel(enum.Enum): The three core trust levels. """ - TRUSTED = 1 - DISTRUSTED = 2 - UNDECIDED = 3 + TRUSTED: str = "TRUSTED" + DISTRUSTED: str = "DISTRUSTED" + UNDECIDED: str = "UNDECIDED" # # Thanks @vanburgerberg - https://github.com/python/typing/issues/182 diff --git a/setup.py b/setup.py index 79422bf..26d2ef8 100644 --- a/setup.py +++ b/setup.py @@ -57,7 +57,7 @@ license="MIT", packages=find_packages(), install_requires=[ "XEdDSA>=1.0.0,<2" ], - python_requires=">=3.7,<4", + python_requires=">=3.7", include_package_data=True, zip_safe=False, classifiers=classifiers,