Daniel Brötzmann pushed to branch omemo_integration at gajim / gajim

Commits:

7 changed files:

Changes:

  • gajim/common/modules/omemo.py
    ... ... @@ -16,25 +16,40 @@
    16 16
     
    
    17 17
     # XEP-0384: OMEMO Encryption
    
    18 18
     
    
    19
    +from __future__ import annotations
    
    20
    +
    
    21
    +from typing import Any
    
    22
    +from typing import Optional
    
    23
    +
    
    19 24
     from pathlib import Path
    
    20 25
     
    
    21 26
     from nbxmpp.namespaces import Namespace
    
    27
    +from nbxmpp.protocol import Message
    
    22 28
     from nbxmpp.protocol import NodeProcessed
    
    29
    +from nbxmpp.protocol import Presence
    
    23 30
     from nbxmpp.errors import StanzaError
    
    24 31
     from nbxmpp.const import PresenceType
    
    25 32
     from nbxmpp.const import Affiliation
    
    33
    +from nbxmpp.structs import MessageProperties
    
    34
    +from nbxmpp.structs import OMEMOMessage
    
    35
    +from nbxmpp.structs import PresenceProperties
    
    26 36
     from nbxmpp.structs import StanzaHandler
    
    27 37
     from nbxmpp.modules.omemo import create_omemo_message
    
    28 38
     from nbxmpp.modules.omemo import get_key_transport_message
    
    29 39
     from nbxmpp.modules.util import is_error
    
    40
    +from nbxmpp.task import Task
    
    30 41
     
    
    31 42
     from gajim.common import app
    
    32 43
     from gajim.common import configpaths
    
    33 44
     from gajim.common import ged
    
    45
    +from gajim.common import types
    
    34 46
     from gajim.common.structs import OutgoingMessage
    
    35 47
     from gajim.common.const import EncryptionData
    
    36 48
     from gajim.common.const import Trust as GajimTrust
    
    49
    +from gajim.common.events import MucDiscoUpdate
    
    50
    +from gajim.common.events import SignedIn
    
    37 51
     from gajim.common.modules.base import BaseModule
    
    52
    +from gajim.common.modules.contacts import GroupchatContact
    
    38 53
     from gajim.common.modules.util import event_node
    
    39 54
     from gajim.common.modules.util import as_task
    
    40 55
     
    
    ... ... @@ -85,8 +100,8 @@ class OMEMO(BaseModule):
    85 100
             'request_bundle',
    
    86 101
         ]
    
    87 102
     
    
    88
    -    def __init__(self, con):
    
    89
    -        BaseModule.__init__(self, con)
    
    103
    +    def __init__(self, client: types.Client) -> None:
    
    104
    +        BaseModule.__init__(self, client)
    
    90 105
     
    
    91 106
             self.handlers = [
    
    92 107
                 StanzaHandler(name='message',
    
    ... ... @@ -101,50 +116,46 @@ def __init__(self, con):
    101 116
     
    
    102 117
             self._register_pubsub_handler(self._devicelist_notification_received)
    
    103 118
             self.register_event('signed-in', ged.CORE, self._on_signed_in)
    
    104
    -        self.register_event('muc-disco-update', ged.GUI1, self._on_muc_disco_update)
    
    119
    +        self.register_event(
    
    120
    +            'muc-disco-update', ged.GUI1, self._on_muc_disco_update)
    
    105 121
     
    
    106 122
             self.encryption_name = 'OMEMO'
    
    107 123
             self.allow_groupchat = True
    
    108 124
     
    
    109
    -        self._own_jid = self._con.get_own_jid().bare
    
    125
    +        self._own_jid = self._client.get_own_jid().bare
    
    110 126
             self._backend = self._get_backend()
    
    111 127
     
    
    112
    -        self._omemo_groupchats = set()
    
    113
    -        self._muc_temp_store = {}
    
    114
    -        self._query_for_bundles = []
    
    115
    -        self._device_bundle_querys = []
    
    116
    -        self._query_for_devicelists = []
    
    128
    +        self._omemo_groupchats: set[str] = set()
    
    129
    +        self._muc_temp_store: dict[bytes, str] = {}
    
    130
    +        self._query_for_bundles: list[str] = []
    
    131
    +        self._device_bundle_querys: list[int] = []
    
    132
    +        self._query_for_devicelists: list[str] = []
    
    117 133
     
    
    118
    -    def _on_signed_in(self, event) -> None:
    
    134
    +    def _on_signed_in(self, _event: SignedIn) -> None:
    
    119 135
             self._log.info('Announce Support after Sign In')
    
    120 136
             self._query_for_bundles = []
    
    121 137
             self.set_bundle()
    
    122 138
             self.request_devicelist()
    
    123 139
     
    
    124
    -    def on_muc_disco_update(self, event):
    
    125
    -        self._check_if_omemo_capable(event.jid)
    
    126
    -
    
    127
    -    def get_own_jid(self, stripped=False):
    
    128
    -        if stripped:
    
    129
    -            return self._con.get_own_jid().bare
    
    130
    -        return self._con.get_own_jid()
    
    140
    +    def _on_muc_disco_update(self, event: MucDiscoUpdate) -> None:
    
    141
    +        self._check_if_omemo_capable(str(event.jid))
    
    131 142
     
    
    132 143
         @property
    
    133
    -    def backend(self):
    
    144
    +    def backend(self) -> OmemoState:
    
    134 145
             return self._backend
    
    135 146
     
    
    136
    -    def _get_backend(self):
    
    147
    +    def _get_backend(self) -> OmemoState:
    
    137 148
             data_dir = Path(configpaths.get('MY_DATA'))
    
    138 149
             db_path = data_dir / f'omemo_{self._own_jid}.db'
    
    139 150
             return OmemoState(self._own_jid, db_path, self._account, self)
    
    140 151
     
    
    141
    -    def is_omemo_groupchat(self, room_jid):
    
    152
    +    def is_omemo_groupchat(self, room_jid: str) -> bool:
    
    142 153
             return room_jid in self._omemo_groupchats
    
    143 154
     
    
    144 155
         def activate(self):
    
    145 156
             """ Method called when the Plugin is activated in the PluginManager
    
    146 157
             """
    
    147
    -        self._con.get_module('Caps').update_caps()
    
    158
    +        self._client.get_module('Caps').update_caps()
    
    148 159
     
    
    149 160
             if app.account_is_connected(self._account):
    
    150 161
                 self._log.info('Announce Support after Plugin Activation')
    
    ... ... @@ -161,7 +172,7 @@ def encrypt_message(self, event: OutgoingMessage) -> None:
    161 172
             if not event.message:
    
    162 173
                 return
    
    163 174
     
    
    164
    -        omemo_message = self.backend.encrypt(event.jid, event.message)
    
    175
    +        omemo_message = self.backend.encrypt(str(event.jid), event.message)
    
    165 176
             if omemo_message is None:
    
    166 177
                 raise Exception('Encryption error')
    
    167 178
     
    
    ... ... @@ -179,7 +190,12 @@ def encrypt_message(self, event: OutgoingMessage) -> None:
    179 190
     
    
    180 191
             self._debug_print_stanza(event.stanza)
    
    181 192
     
    
    182
    -    def _send_key_transport_message(self, typ, jid, devices):
    
    193
    +    def _send_key_transport_message(self,
    
    194
    +                                    typ: str,
    
    195
    +                                    jid: str,
    
    196
    +                                    devices: list[int]
    
    197
    +                                    ) -> None:
    
    198
    +
    
    183 199
             omemo_message = self.backend.encrypt_key_transport(jid, devices)
    
    184 200
             if omemo_message is None:
    
    185 201
                 self._log.warning('Key transport message to %s (%s) failed',
    
    ... ... @@ -188,9 +204,14 @@ def _send_key_transport_message(self, typ, jid, devices):
    188 204
     
    
    189 205
             transport_message = get_key_transport_message(typ, jid, omemo_message)
    
    190 206
             self._log.info('Send key transport message %s (%s)', jid, devices)
    
    191
    -        self._con.connection.send(transport_message)
    
    207
    +        self._client.connection.send(transport_message)
    
    208
    +
    
    209
    +    def _message_received(self,
    
    210
    +                          _client: types.xmppClient,
    
    211
    +                          stanza: Message,
    
    212
    +                          properties: MessageProperties
    
    213
    +                          ) -> None:
    
    192 214
     
    
    193
    -    def _message_received(self, _con, stanza, properties):
    
    194 215
             if not properties.is_omemo:
    
    195 216
                 return
    
    196 217
     
    
    ... ... @@ -211,6 +232,7 @@ def _message_received(self, _con, stanza, properties):
    211 232
     
    
    212 233
             self._log.info('Message received from: %s', from_jid)
    
    213 234
     
    
    235
    +        assert isinstance(properties.omemo, OMEMOMessage)
    
    214 236
             try:
    
    215 237
                 plaintext, fingerprint, trust = self.backend.decrypt_message(
    
    216 238
                     properties.omemo, from_jid)
    
    ... ... @@ -247,28 +269,35 @@ def _message_received(self, _con, stanza, properties):
    247 269
     
    
    248 270
             prepare_stanza(stanza, plaintext)
    
    249 271
             self._debug_print_stanza(stanza)
    
    250
    -        properties.encrypted = EncryptionData({'name': ENCRYPTION_NAME,
    
    251
    -                                               'fingerprint': fingerprint,
    
    252
    -                                               'trust': GajimTrust[trust.name]})
    
    272
    +        properties.encrypted = EncryptionData({
    
    273
    +            'name': ENCRYPTION_NAME,
    
    274
    +            'fingerprint': fingerprint,
    
    275
    +            'trust': GajimTrust[trust.name]})
    
    276
    +
    
    277
    +    def _process_muc_message(self,
    
    278
    +                             properties: MessageProperties
    
    279
    +                             ) -> Optional[str]:
    
    253 280
     
    
    254
    -    def _process_muc_message(self, properties):
    
    255 281
             resource = properties.jid.resource
    
    256 282
             if properties.muc_ofrom is not None:
    
    257 283
                 # History Message from MUC
    
    258 284
                 return properties.muc_ofrom.bare
    
    259 285
     
    
    260
    -        contact = self._con.get_module('Contacts').get_contact(properties.jid)
    
    286
    +        contact = self._client.get_module('Contacts').get_contact(
    
    287
    +            properties.jid)
    
    261 288
             if contact.real_jid is not None:
    
    262 289
                 return contact.real_jid.bare
    
    263 290
     
    
    291
    +        assert isinstance(properties.omemo, OMEMOMessage)
    
    264 292
             self._log.info('Groupchat: Last resort trying to find SID in DB')
    
    265 293
             from_jid = self.backend.storage.getJidFromDevice(properties.omemo.sid)
    
    266 294
             if not from_jid:
    
    267
    -            self._log.error("Can't decrypt GroupChat Message from %s", resource)
    
    295
    +            self._log.error(
    
    296
    +                "Can't decrypt GroupChat Message from %s", resource)
    
    268 297
                 return
    
    269 298
             return from_jid
    
    270 299
     
    
    271
    -    def _process_mam_message(self, properties):
    
    300
    +    def _process_mam_message(self, properties: MessageProperties) -> str:
    
    272 301
             self._log.info('Message received, archive: %s', properties.mam.archive)
    
    273 302
             if properties.from_muc:
    
    274 303
                 self._log.info('MUC MAM Message received')
    
    ... ... @@ -279,7 +308,12 @@ def _process_mam_message(self, properties):
    279 308
                 return properties.muc_user.jid.bare
    
    280 309
             return properties.from_.bare
    
    281 310
     
    
    282
    -    def _on_muc_user_presence(self, _con, _stanza, properties):
    
    311
    +    def _on_muc_user_presence(self,
    
    312
    +                              _client: types.xmppClient,
    
    313
    +                              _stanza: Presence,
    
    314
    +                              properties: PresenceProperties
    
    315
    +                              ) -> None:
    
    316
    +
    
    283 317
             if properties.type == PresenceType.ERROR:
    
    284 318
                 return
    
    285 319
     
    
    ... ... @@ -305,7 +339,7 @@ def _on_muc_user_presence(self, _con, _stanza, properties):
    305 339
                     self._log.info('%s not in Roster, query devicelist...', jid)
    
    306 340
                     self.request_devicelist(jid)
    
    307 341
     
    
    308
    -    def get_affiliation_list(self, room_jid):
    
    342
    +    def get_affiliation_list(self, room_jid: str) -> None:
    
    309 343
             for affiliation in ('owner', 'admin', 'member'):
    
    310 344
                 self._nbxmpp('MUC').get_affiliation(
    
    311 345
                     room_jid,
    
    ... ... @@ -313,7 +347,7 @@ def get_affiliation_list(self, room_jid):
    313 347
                     callback=self._on_affiliations_received,
    
    314 348
                     user_data=room_jid)
    
    315 349
     
    
    316
    -    def _on_affiliations_received(self, task):
    
    350
    +    def _on_affiliations_received(self, task: Task) -> None:
    
    317 351
             room_jid = task.get_user_data()
    
    318 352
             try:
    
    319 353
                 result = task.finish()
    
    ... ... @@ -330,24 +364,24 @@ def _on_affiliations_received(self, task):
    330 364
                     self._log.info('%s not in Roster, query devicelist...', jid)
    
    331 365
                     self.request_devicelist(jid)
    
    332 366
     
    
    333
    -    def is_contact_in_roster(self, jid):
    
    367
    +    def is_contact_in_roster(self, jid: str) -> bool:
    
    334 368
             if jid == self._own_jid:
    
    335 369
                 return True
    
    336 370
     
    
    337
    -        roster_item = self._con.get_module('Roster').get_item(jid)
    
    371
    +        roster_item = self._client.get_module('Roster').get_item(jid)
    
    338 372
             if roster_item is None:
    
    339 373
                 return False
    
    340 374
     
    
    341
    -        contact = self._con.get_module('Contacts').get_contact(jid)
    
    375
    +        contact = self._client.get_module('Contacts').get_contact(jid)
    
    342 376
             return contact.subscription == 'both'
    
    343 377
     
    
    344
    -    def on_room_joined(self, contact):
    
    378
    +    def on_room_joined(self, contact: GroupchatContact) -> None:
    
    345 379
             jid = str(contact.jid)
    
    346 380
             self._check_if_omemo_capable(jid)
    
    347 381
             if self.is_omemo_groupchat(jid):
    
    348 382
                 self.get_affiliation_list(jid)
    
    349 383
     
    
    350
    -    def _check_if_omemo_capable(self, jid):
    
    384
    +    def _check_if_omemo_capable(self, jid: str) -> None:
    
    351 385
             disco_info = app.storage.cache.get_last_disco_info(jid)
    
    352 386
             if disco_info.muc_is_members_only and disco_info.muc_is_nonanonymous:
    
    353 387
                 self._log.info('OMEMO room discovered: %s', jid)
    
    ... ... @@ -356,7 +390,7 @@ def _check_if_omemo_capable(self, jid):
    356 390
                 self._log.info('OMEMO room removed due to config change: %s', jid)
    
    357 391
                 self._omemo_groupchats.discard(jid)
    
    358 392
     
    
    359
    -    def _check_for_missing_sessions(self, jid):
    
    393
    +    def _check_for_missing_sessions(self, jid: str) -> None:
    
    360 394
             devices_without_session = self.backend.devices_without_sessions(jid)
    
    361 395
             for device_id in devices_without_session:
    
    362 396
                 if device_id in self._device_bundle_querys:
    
    ... ... @@ -364,7 +398,7 @@ def _check_for_missing_sessions(self, jid):
    364 398
                 self._device_bundle_querys.append(device_id)
    
    365 399
                 self.request_bundle(jid, device_id)
    
    366 400
     
    
    367
    -    def are_keys_missing(self, contact_jid):
    
    401
    +    def are_keys_missing(self, contact_jid: str) -> bool:
    
    368 402
             """ Checks if devicekeys are missing and queries the
    
    369 403
                 bundles
    
    370 404
     
    
    ... ... @@ -407,12 +441,12 @@ def are_keys_missing(self, contact_jid):
    407 441
                 return False
    
    408 442
             return True
    
    409 443
     
    
    410
    -    def set_bundle(self):
    
    444
    +    def set_bundle(self) -> None:
    
    411 445
             self._nbxmpp('OMEMO').set_bundle(self.backend.bundle,
    
    412 446
                                              self.backend.own_device)
    
    413 447
     
    
    414 448
         @as_task
    
    415
    -    def request_bundle(self, jid, device_id):
    
    449
    +    def request_bundle(self, jid: str, device_id: int):
    
    416 450
             _task = yield
    
    417 451
     
    
    418 452
             self._log.info('Fetch device bundle %s %s', device_id, jid)
    
    ... ... @@ -437,19 +471,20 @@ def request_bundle(self, jid, device_id):
    437 471
             if ctrl:
    
    438 472
                 app.ged.raise_event(OMEMONewFingerprint(chat_control=ctrl))
    
    439 473
     
    
    440
    -    def set_devicelist(self, devicelist=None):
    
    441
    -        devicelist_ = set([self.backend.own_device])
    
    474
    +    def set_devicelist(self, devicelist: Optional[list[int]] = None) -> None:
    
    475
    +        devicelist_: set[int] = set([self.backend.own_device])
    
    442 476
             if devicelist is not None:
    
    443 477
                 devicelist_.update(devicelist)
    
    444 478
             self._log.info('Publishing own devicelist: %s', devicelist_)
    
    445 479
             self._nbxmpp('OMEMO').set_devicelist(devicelist_)
    
    446 480
     
    
    447
    -    def clear_devicelist(self):
    
    448
    -        self.backend.update_devicelist(self._own_jid, [self.backend.own_device])
    
    481
    +    def clear_devicelist(self) -> None:
    
    482
    +        self.backend.update_devicelist(
    
    483
    +            self._own_jid, [self.backend.own_device])
    
    449 484
             self.set_devicelist()
    
    450 485
     
    
    451 486
         @as_task
    
    452
    -    def request_devicelist(self, jid=None):
    
    487
    +    def request_devicelist(self, jid: Optional[str] = None):
    
    453 488
             _task = yield
    
    454 489
     
    
    455 490
             if jid is None:
    
    ... ... @@ -468,7 +503,12 @@ def request_devicelist(self, jid=None):
    468 503
             self._process_devicelist_update(jid, devicelist)
    
    469 504
     
    
    470 505
         @event_node(Namespace.OMEMO_TEMP_DL)
    
    471
    -    def _devicelist_notification_received(self, _con, _stanza, properties):
    
    506
    +    def _devicelist_notification_received(self,
    
    507
    +                                          _client: types.xmppClient,
    
    508
    +                                          _stanza: Message,
    
    509
    +                                          properties: MessageProperties
    
    510
    +                                          ) -> None:
    
    511
    +
    
    472 512
             if properties.pubsub_event.retracted:
    
    473 513
                 return
    
    474 514
     
    
    ... ... @@ -476,8 +516,12 @@ def _devicelist_notification_received(self, _con, _stanza, properties):
    476 516
     
    
    477 517
             self._process_devicelist_update(str(properties.jid), devicelist)
    
    478 518
     
    
    479
    -    def _process_devicelist_update(self, jid, devicelist):
    
    480
    -        own_devices = jid is None or self._con.get_own_jid().bare_match(jid)
    
    519
    +    def _process_devicelist_update(self,
    
    520
    +                                   jid: str,
    
    521
    +                                   devicelist: list[int]
    
    522
    +                                   ) -> None:
    
    523
    +
    
    524
    +        own_devices = jid is None or self._client.get_own_jid().bare_match(jid)
    
    481 525
             if own_devices:
    
    482 526
                 jid = self._own_jid
    
    483 527
     
    
    ... ... @@ -496,7 +540,7 @@ def _process_devicelist_update(self, jid, devicelist):
    496 540
     
    
    497 541
             self._check_for_missing_sessions(jid)
    
    498 542
     
    
    499
    -    def _debug_print_stanza(self, stanza):
    
    543
    +    def _debug_print_stanza(self, stanza: Any) -> None:
    
    500 544
             stanzastr = '\n' + stanza.__str__(fancy=True)
    
    501 545
             stanzastr = stanzastr[0:-1]
    
    502 546
             self._log.debug(stanzastr)

  • gajim/common/modules/omemobackend/aes.py
    ... ... @@ -14,10 +14,13 @@
    14 14
     # You should have received a copy of the GNU General Public License
    
    15 15
     # along with OMEMO Gajim Plugin. If not, see <http://www.gnu.org/licenses/>.
    
    16 16
     
    
    17
    +from __future__ import annotations
    
    18
    +
    
    19
    +from typing import NamedTuple
    
    20
    +from typing import Union
    
    17 21
     
    
    18 22
     import os
    
    19 23
     import logging
    
    20
    -from collections import namedtuple
    
    21 24
     
    
    22 25
     from cryptography.hazmat.primitives.ciphers import Cipher
    
    23 26
     from cryptography.hazmat.primitives.ciphers import algorithms
    
    ... ... @@ -26,12 +29,16 @@
    26 29
     
    
    27 30
     log = logging.getLogger('gajim.p.omemo')
    
    28 31
     
    
    29
    -EncryptionResult = namedtuple('EncryptionResult', 'payload key iv')
    
    30
    -
    
    31 32
     IV_SIZE = 12
    
    32 33
     
    
    33 34
     
    
    34
    -def _decrypt(key, iv, tag, data):
    
    35
    +class EncryptionResult(NamedTuple):
    
    36
    +    payload: bytes
    
    37
    +    key: bytes
    
    38
    +    iv: bytes
    
    39
    +
    
    40
    +
    
    41
    +def _decrypt(key: bytes, iv: bytes, tag: bytes, data: bytes) -> bytes:
    
    35 42
         decryptor = Cipher(
    
    36 43
             algorithms.AES(key),
    
    37 44
             GCM(iv, tag=tag),
    
    ... ... @@ -39,7 +46,7 @@ def _decrypt(key, iv, tag, data):
    39 46
         return decryptor.update(data) + decryptor.finalize()
    
    40 47
     
    
    41 48
     
    
    42
    -def aes_decrypt(_key, iv, payload):
    
    49
    +def aes_decrypt(_key: bytes, iv: bytes, payload: bytes) -> str:
    
    43 50
         if len(_key) >= 32:
    
    44 51
             # XEP-0384
    
    45 52
             log.debug('XEP Compliant Key/Tag')
    
    ... ... @@ -56,13 +63,17 @@ def aes_decrypt(_key, iv, payload):
    56 63
         return _decrypt(key, iv, tag, data).decode()
    
    57 64
     
    
    58 65
     
    
    59
    -def aes_decrypt_file(key, iv, payload):
    
    66
    +def aes_decrypt_file(key: bytes, iv: bytes, payload: bytes) -> bytes:
    
    60 67
         data = payload[:-16]
    
    61 68
         tag = payload[-16:]
    
    62 69
         return _decrypt(key, iv, tag, data)
    
    63 70
     
    
    64 71
     
    
    65
    -def _encrypt(data, key_size, iv_size=IV_SIZE):
    
    72
    +def _encrypt(data: Union[str, bytes],
    
    73
    +             key_size: int,
    
    74
    +             iv_size: int = IV_SIZE
    
    75
    +             ) -> tuple[bytes, bytes, bytes, bytes]:
    
    76
    +
    
    66 77
         if isinstance(data, str):
    
    67 78
             data = data.encode()
    
    68 79
         key = os.urandom(key_size)
    
    ... ... @@ -76,21 +87,21 @@ def _encrypt(data, key_size, iv_size=IV_SIZE):
    76 87
         return key, iv, encryptor.tag, payload
    
    77 88
     
    
    78 89
     
    
    79
    -def aes_encrypt(plaintext):
    
    90
    +def aes_encrypt(plaintext: str) -> EncryptionResult:
    
    80 91
         key, iv, tag, payload = _encrypt(plaintext, 16)
    
    81 92
         key += tag
    
    82 93
         return EncryptionResult(payload=payload, key=key, iv=iv)
    
    83 94
     
    
    84 95
     
    
    85
    -def aes_encrypt_file(data):
    
    96
    +def aes_encrypt_file(data: bytes) -> EncryptionResult:
    
    86 97
         key, iv, tag, payload, = _encrypt(data, 32)
    
    87 98
         payload += tag
    
    88 99
         return EncryptionResult(payload=payload, key=key, iv=iv)
    
    89 100
     
    
    90 101
     
    
    91
    -def get_new_key():
    
    102
    +def get_new_key() -> bytes:
    
    92 103
         return os.urandom(16)
    
    93 104
     
    
    94 105
     
    
    95
    -def get_new_iv():
    
    106
    +def get_new_iv() -> bytes:
    
    96 107
         return os.urandom(IV_SIZE)

  • gajim/common/modules/omemobackend/devices.py
    ... ... @@ -14,17 +14,19 @@
    14 14
     # You should have received a copy of the GNU General Public License
    
    15 15
     # along with OMEMO Gajim Plugin. If not, see <http://www.gnu.org/licenses/>.
    
    16 16
     
    
    17
    +from __future__ import annotations
    
    18
    +
    
    17 19
     from collections import defaultdict
    
    18 20
     
    
    19 21
     from gajim.common import app
    
    20 22
     
    
    21
    -from gajim.common.modules.util import UNACKNOWLEDGED_COUNT
    
    23
    +from gajim.common.modules.omemobackend.util import UNACKNOWLEDGED_COUNT
    
    22 24
     
    
    23 25
     
    
    24 26
     class DeviceManager:
    
    25
    -    def __init__(self):
    
    26
    -        self.__device_store = defaultdict(set)
    
    27
    -        self.__muc_member_store = defaultdict(set)
    
    27
    +    def __init__(self) -> None:
    
    28
    +        self.__device_store: dict[str, set[int]] = defaultdict(set)
    
    29
    +        self.__muc_member_store: dict[str, set[str]] = defaultdict(set)
    
    28 30
     
    
    29 31
             reg_id = self._storage.getLocalRegistrationId()
    
    30 32
             if reg_id is None:
    
    ... ... @@ -38,7 +40,7 @@ def __init__(self):
    38 40
                 self._log.info('Load device from storage: %s - %s', jid, device)
    
    39 41
                 self.add_device(jid, device)
    
    40 42
     
    
    41
    -    def update_devicelist(self, jid, devicelist):
    
    43
    +    def update_devicelist(self, jid: str, devicelist: list[int]) -> None:
    
    42 44
             for device in list(devicelist):
    
    43 45
                 if device == self.own_device:
    
    44 46
                     continue
    
    ... ... @@ -52,35 +54,39 @@ def update_devicelist(self, jid, devicelist):
    52 54
             self._log.info('Saved devices for %s', jid)
    
    53 55
             self._storage.setActiveState(jid, devicelist)
    
    54 56
     
    
    55
    -    def add_muc_member(self, room_jid, jid):
    
    57
    +    def add_muc_member(self, room_jid: str, jid: str) -> None:
    
    56 58
             self._log.info('Saved MUC member %s %s', room_jid, jid)
    
    57 59
             self.__muc_member_store[room_jid].add(jid)
    
    58 60
     
    
    59
    -    def remove_muc_member(self, room_jid, jid):
    
    61
    +    def remove_muc_member(self, room_jid: str, jid: str) -> None:
    
    60 62
             self._log.info('Removed MUC member %s %s', room_jid, jid)
    
    61 63
             self.__muc_member_store[room_jid].discard(jid)
    
    62 64
     
    
    63
    -    def get_muc_members(self, room_jid, without_self=True):
    
    65
    +    def get_muc_members(self,
    
    66
    +                        room_jid: str,
    
    67
    +                        without_self: bool = True
    
    68
    +                        ) -> set[str]:
    
    69
    +
    
    64 70
             members = set(self.__muc_member_store[room_jid])
    
    65 71
             if without_self:
    
    66 72
                 members.discard(self._own_jid)
    
    67 73
             return members
    
    68 74
     
    
    69
    -    def add_device(self, jid, device):
    
    75
    +    def add_device(self, jid: str, device: int) -> None:
    
    70 76
             self.__device_store[jid].add(device)
    
    71 77
     
    
    72
    -    def remove_device(self, jid, device):
    
    78
    +    def remove_device(self, jid: str, device: int) -> None:
    
    73 79
             self.__device_store[jid].discard(device)
    
    74 80
             self._storage.setInactive(jid, device)
    
    75 81
     
    
    76
    -    def get_devices(self, jid, without_self=False):
    
    82
    +    def get_devices(self, jid: str, without_self: bool = False) -> set[int]:
    
    77 83
             devices = set(self.__device_store[jid])
    
    78 84
             if without_self:
    
    79 85
                 devices.discard(self.own_device)
    
    80 86
             return devices
    
    81 87
     
    
    82
    -    def get_devices_for_encryption(self, jid):
    
    83
    -        devices_for_encryption = []
    
    88
    +    def get_devices_for_encryption(self, jid: str) -> set[tuple[str, int]]:
    
    89
    +        devices_for_encryption: list[tuple[str, int]] = []
    
    84 90
     
    
    85 91
             client = app.get_client(self._account)
    
    86 92
             contact = client.get_module('Contacts').get_contact(jid)
    
    ... ... @@ -95,22 +101,25 @@ def get_devices_for_encryption(self, jid):
    95 101
             devices_for_encryption += self._get_own_devices_for_encryption()
    
    96 102
             return set(devices_for_encryption)
    
    97 103
     
    
    98
    -    def _get_devices_for_muc_encryption(self, jid):
    
    99
    -        devices_for_encryption = []
    
    104
    +    def _get_devices_for_muc_encryption(self,
    
    105
    +                                        jid: str
    
    106
    +                                        ) -> list[tuple[str, int]]:
    
    107
    +
    
    108
    +        devices_for_encryption: list[tuple[str, int]] = []
    
    100 109
             for jid_ in self.__muc_member_store[jid]:
    
    101 110
                 devices_for_encryption += self._get_devices_for_encryption(jid_)
    
    102 111
             return devices_for_encryption
    
    103 112
     
    
    104
    -    def _get_own_devices_for_encryption(self):
    
    105
    -        devices_for_encryption = []
    
    113
    +    def _get_own_devices_for_encryption(self) -> list[tuple[str, int]]:
    
    114
    +        devices_for_encryption: list[tuple[str, int]] = []
    
    106 115
             own_devices = self.get_devices(self._own_jid, without_self=True)
    
    107 116
             for device in own_devices:
    
    108 117
                 if self._storage.isTrusted(self._own_jid, device):
    
    109 118
                     devices_for_encryption.append((self._own_jid, device))
    
    110 119
             return devices_for_encryption
    
    111 120
     
    
    112
    -    def _get_devices_for_encryption(self, jid):
    
    113
    -        devices_for_encryption = []
    
    121
    +    def _get_devices_for_encryption(self, jid: str) -> list[tuple[str, int]]:
    
    122
    +        devices_for_encryption: list[tuple[str, int]] = []
    
    114 123
             devices = self.get_devices(jid)
    
    115 124
     
    
    116 125
             for device in devices:
    
    ... ... @@ -119,18 +128,18 @@ def _get_devices_for_encryption(self, jid):
    119 128
             return devices_for_encryption
    
    120 129
     
    
    121 130
         @property
    
    122
    -    def own_device(self):
    
    131
    +    def own_device(self) -> int:
    
    123 132
             return self.__own_device
    
    124 133
     
    
    125 134
         @property
    
    126
    -    def devices_for_publish(self):
    
    135
    +    def devices_for_publish(self) -> set[int]:
    
    127 136
             devices = self.get_devices(self._own_jid)
    
    128 137
             if self.own_device not in devices:
    
    129 138
                 devices.add(self.own_device)
    
    130 139
             return devices
    
    131 140
     
    
    132 141
         @property
    
    133
    -    def is_own_device_published(self):
    
    142
    +    def is_own_device_published(self) -> bool:
    
    134 143
             return self.own_device in self.get_devices(self._own_jid)
    
    135 144
     
    
    136 145
     
    

  • gajim/common/modules/omemobackend/liteaxolotlstore.py
    ... ... @@ -15,9 +15,16 @@
    15 15
     # You should have received a copy of the GNU General Public License
    
    16 16
     # along with OMEMO Gajim Plugin. If not, see <http://www.gnu.org/licenses/>.
    
    17 17
     
    
    18
    +from __future__ import annotations
    
    19
    +
    
    20
    +from typing import Any
    
    21
    +from typing import NamedTuple
    
    22
    +from typing import Optional
    
    23
    +
    
    18 24
     import time
    
    19 25
     import sqlite3
    
    20 26
     from collections import namedtuple
    
    27
    +from pathlib import Path
    
    21 28
     
    
    22 29
     from axolotl.state.axolotlstore import AxolotlStore
    
    23 30
     from axolotl.state.signedprekeyrecord import SignedPreKeyRecord
    
    ... ... @@ -26,28 +33,29 @@
    26 33
     from axolotl.invalidkeyidexception import InvalidKeyIdException
    
    27 34
     from axolotl.ecc.djbec import DjbECPrivateKey
    
    28 35
     from axolotl.ecc.djbec import DjbECPublicKey
    
    36
    +from axolotl.identitykey import IdentityKey
    
    29 37
     from axolotl.identitykeypair import IdentityKeyPair
    
    30 38
     from axolotl.util.medium import Medium
    
    31 39
     from axolotl.util.keyhelper import KeyHelper
    
    32 40
     
    
    33 41
     from gajim.common import app
    
    34
    -
    
    35
    -from gajim.common.modules.util import Trust
    
    36
    -from gajim.common.modules.util import IdentityKeyExtended
    
    37
    -from gajim.common.modules.util import DEFAULT_PREKEY_AMOUNT
    
    42
    +from gajim.common.modules.omemobackend.util import Trust
    
    43
    +from gajim.common.modules.omemobackend.util import IdentityKeyExtended
    
    44
    +from gajim.common.modules.omemobackend.util import DEFAULT_PREKEY_AMOUNT
    
    45
    +from gajim.common.modules.util import LogAdapter
    
    38 46
     
    
    39 47
     
    
    40 48
     def _convert_to_string(text):
    
    41 49
         return text.decode()
    
    42 50
     
    
    43 51
     
    
    44
    -def _convert_identity_key(key):
    
    52
    +def _convert_identity_key(key: bytes) -> Optional[IdentityKeyExtended]:
    
    45 53
         if not key:
    
    46 54
             return
    
    47 55
         return IdentityKeyExtended(DjbECPublicKey(key[1:]))
    
    48 56
     
    
    49 57
     
    
    50
    -def _convert_record(record):
    
    58
    +def _convert_record(record: bytes) -> SessionRecord:
    
    51 59
         return SessionRecord(serialized=record)
    
    52 60
     
    
    53 61
     
    
    ... ... @@ -56,7 +64,7 @@ def _convert_record(record):
    56 64
     
    
    57 65
     
    
    58 66
     class LiteAxolotlStore(AxolotlStore):
    
    59
    -    def __init__(self, account: str, db_path, log):
    
    67
    +    def __init__(self, account: str, db_path: Path, log: LogAdapter) -> None:
    
    60 68
             self._log = log
    
    61 69
             self._account = account
    
    62 70
             self._con = sqlite3.connect(db_path,
    
    ... ... @@ -78,13 +86,16 @@ def __init__(self, account: str, db_path, log):
    78 86
                 self._log.info("Generating OMEMO keys")
    
    79 87
                 self._generate_axolotl_keys()
    
    80 88
     
    
    81
    -    def _is_blind_trust_enabled(self):
    
    89
    +    def _is_blind_trust_enabled(self) -> bool:
    
    82 90
             return app.settings.get_account_setting(self._account,
    
    83 91
                                                     'omemo_blind_trust')
    
    84 92
     
    
    85 93
         @staticmethod
    
    86
    -    def _namedtuple_factory(cursor, row):
    
    87
    -        fields = []
    
    94
    +    def _namedtuple_factory(cursor: sqlite3.Cursor,
    
    95
    +                            row: tuple[Any, ...]
    
    96
    +                            ) -> NamedTuple:
    
    97
    +
    
    98
    +        fields: list[str] = []
    
    88 99
             for col in cursor.description:
    
    89 100
                 if col[0] == '_id':
    
    90 101
                     fields.append('id')
    
    ... ... @@ -98,7 +109,7 @@ def _namedtuple_factory(cursor, row):
    98 109
                     fields.append(col[0])
    
    99 110
             return namedtuple("Row", fields)(*row)
    
    100 111
     
    
    101
    -    def _generate_axolotl_keys(self):
    
    112
    +    def _generate_axolotl_keys(self) -> None:
    
    102 113
             identity_key_pair = KeyHelper.generateIdentityKeyPair()
    
    103 114
             registration_id = KeyHelper.getRandomSequence(2147483647)
    
    104 115
             pre_keys = KeyHelper.generatePreKeys(
    
    ... ... @@ -114,10 +125,10 @@ def _generate_axolotl_keys(self):
    114 125
             for pre_key in pre_keys:
    
    115 126
                 self.storePreKey(pre_key.getId(), pre_key)
    
    116 127
     
    
    117
    -    def user_version(self):
    
    128
    +    def user_version(self) -> int:
    
    118 129
             return self._con.execute('PRAGMA user_version').fetchone()[0]
    
    119 130
     
    
    120
    -    def createDb(self):
    
    131
    +    def createDb(self) -> None:
    
    121 132
             if self.user_version() == 0:
    
    122 133
     
    
    123 134
                 create_tables = '''
    
    ... ... @@ -159,7 +170,7 @@ def createDb(self):
    159 170
                     """ % (create_tables)
    
    160 171
                 self._con.executescript(create_db_sql)
    
    161 172
     
    
    162
    -    def migrateDb(self):
    
    173
    +    def migrateDb(self) -> None:
    
    163 174
             """ Migrates the DB
    
    164 175
             """
    
    165 176
     
    
    ... ... @@ -377,7 +388,7 @@ def migrateDb(self):
    377 388
                 self._con.execute('PRAGMA user_version=12')
    
    378 389
                 self._con.commit()
    
    379 390
     
    
    380
    -    def loadSignedPreKey(self, signedPreKeyId):
    
    391
    +    def loadSignedPreKey(self, signedPreKeyId: int) -> SignedPreKeyRecord:
    
    381 392
             query = 'SELECT record FROM signed_prekeys WHERE prekey_id = ?'
    
    382 393
             result = self._con.execute(query, (signedPreKeyId, )).fetchone()
    
    383 394
             if result is None:
    
    ... ... @@ -385,39 +396,43 @@ def loadSignedPreKey(self, signedPreKeyId):
    385 396
                                             signedPreKeyId)
    
    386 397
             return SignedPreKeyRecord(serialized=result.record)
    
    387 398
     
    
    388
    -    def loadSignedPreKeys(self):
    
    399
    +    def loadSignedPreKeys(self) -> list[SignedPreKeyRecord]:
    
    389 400
             query = 'SELECT record FROM signed_prekeys'
    
    390 401
             results = self._con.execute(query).fetchall()
    
    391 402
             return [SignedPreKeyRecord(serialized=row.record) for row in results]
    
    392 403
     
    
    393
    -    def storeSignedPreKey(self, signedPreKeyId, signedPreKeyRecord):
    
    404
    +    def storeSignedPreKey(self,
    
    405
    +                          signedPreKeyId: int,
    
    406
    +                          signedPreKeyRecord: SignedPreKeyRecord
    
    407
    +                          ) -> None:
    
    408
    +
    
    394 409
             query = 'INSERT INTO signed_prekeys (prekey_id, record) VALUES(?,?)'
    
    395 410
             self._con.execute(query, (signedPreKeyId,
    
    396 411
                                       signedPreKeyRecord.serialize()))
    
    397 412
             self._con.commit()
    
    398 413
     
    
    399
    -    def containsSignedPreKey(self, signedPreKeyId):
    
    414
    +    def containsSignedPreKey(self, signedPreKeyId: int) -> bool:
    
    400 415
             query = 'SELECT record FROM signed_prekeys WHERE prekey_id = ?'
    
    401 416
             result = self._con.execute(query, (signedPreKeyId,)).fetchone()
    
    402 417
             return result is not None
    
    403 418
     
    
    404
    -    def removeSignedPreKey(self, signedPreKeyId):
    
    419
    +    def removeSignedPreKey(self, signedPreKeyId: int) -> None:
    
    405 420
             query = 'DELETE FROM signed_prekeys WHERE prekey_id = ?'
    
    406 421
             self._con.execute(query, (signedPreKeyId,))
    
    407 422
             self._con.commit()
    
    408 423
     
    
    409
    -    def getNextSignedPreKeyId(self):
    
    424
    +    def getNextSignedPreKeyId(self) -> int:
    
    410 425
             result = self.getCurrentSignedPreKeyId()
    
    411 426
             if result is None:
    
    412 427
                 return 1  # StartId if no SignedPreKeys exist
    
    413 428
             return (result % (Medium.MAX_VALUE - 1)) + 1
    
    414 429
     
    
    415
    -    def getCurrentSignedPreKeyId(self):
    
    430
    +    def getCurrentSignedPreKeyId(self) -> Optional[int]:
    
    416 431
             query = 'SELECT MAX(prekey_id) FROM signed_prekeys'
    
    417 432
             result = self._con.execute(query).fetchone()
    
    418 433
             return result.max_prekey_id if result is not None else None
    
    419 434
     
    
    420
    -    def getSignedPreKeyTimestamp(self, signedPreKeyId):
    
    435
    +    def getSignedPreKeyTimestamp(self, signedPreKeyId: int) -> int:
    
    421 436
             query = '''SELECT strftime('%s', timestamp) FROM
    
    422 437
                        signed_prekeys WHERE prekey_id = ?'''
    
    423 438
     
    
    ... ... @@ -428,19 +443,19 @@ def getSignedPreKeyTimestamp(self, signedPreKeyId):
    428 443
     
    
    429 444
             return result.formated_time
    
    430 445
     
    
    431
    -    def removeOldSignedPreKeys(self, timestamp):
    
    446
    +    def removeOldSignedPreKeys(self, timestamp: int) -> None:
    
    432 447
             query = '''DELETE FROM signed_prekeys
    
    433 448
                        WHERE timestamp < datetime(?, "unixepoch")'''
    
    434 449
             self._con.execute(query, (timestamp,))
    
    435 450
             self._con.commit()
    
    436 451
     
    
    437
    -    def loadSession(self, recipientId, deviceId):
    
    452
    +    def loadSession(self, recipientId: str, deviceId: int) -> SessionRecord:
    
    438 453
             query = '''SELECT record as "record [session_record]"
    
    439 454
                        FROM sessions WHERE recipient_id = ? AND device_id = ?'''
    
    440 455
             result = self._con.execute(query, (recipientId, deviceId)).fetchone()
    
    441 456
             return result.record if result is not None else SessionRecord()
    
    442 457
     
    
    443
    -    def getJidFromDevice(self, device_id):
    
    458
    +    def getJidFromDevice(self, device_id: int) -> Optional[str]:
    
    444 459
             query = '''SELECT recipient_id
    
    445 460
                        FROM sessions WHERE device_id = ?'''
    
    446 461
             result = self._con.execute(query, (device_id, )).fetchone()
    
    ... ... @@ -451,7 +466,12 @@ def getActiveDeviceTuples(self):
    451 466
                        FROM sessions WHERE active = 1'''
    
    452 467
             return self._con.execute(query).fetchall()
    
    453 468
     
    
    454
    -    def storeSession(self, recipientId, deviceId, sessionRecord):
    
    469
    +    def storeSession(self,
    
    470
    +                     recipientId: str,
    
    471
    +                     deviceId: int,
    
    472
    +                     sessionRecord: SessionRecord
    
    473
    +                     ) -> None:
    
    474
    +
    
    455 475
             query = '''INSERT INTO sessions(recipient_id, device_id, record)
    
    456 476
                        VALUES(?,?,?)'''
    
    457 477
             try:
    
    ... ... @@ -467,24 +487,24 @@ def storeSession(self, recipientId, deviceId, sessionRecord):
    467 487
     
    
    468 488
             self._con.commit()
    
    469 489
     
    
    470
    -    def containsSession(self, recipientId, deviceId):
    
    490
    +    def containsSession(self, recipientId: str, deviceId: int) -> bool:
    
    471 491
             query = '''SELECT record FROM sessions
    
    472 492
                        WHERE recipient_id = ? AND device_id = ?'''
    
    473 493
             result = self._con.execute(query, (recipientId, deviceId)).fetchone()
    
    474 494
             return result is not None
    
    475 495
     
    
    476
    -    def deleteSession(self, recipientId, deviceId):
    
    496
    +    def deleteSession(self, recipientId: str, deviceId: int) -> None:
    
    477 497
             self._log.info('Delete session for %s %s', recipientId, deviceId)
    
    478 498
             query = "DELETE FROM sessions WHERE recipient_id = ? AND device_id = ?"
    
    479 499
             self._con.execute(query, (recipientId, deviceId))
    
    480 500
             self._con.commit()
    
    481 501
     
    
    482
    -    def deleteAllSessions(self, recipientId):
    
    502
    +    def deleteAllSessions(self, recipientId: str) -> None:
    
    483 503
             query = 'DELETE FROM sessions WHERE recipient_id = ?'
    
    484 504
             self._con.execute(query, (recipientId,))
    
    485 505
             self._con.commit()
    
    486 506
     
    
    487
    -    def getSessionsFromJid(self, recipientId):
    
    507
    +    def getSessionsFromJid(self, recipientId: str):
    
    488 508
             query = '''SELECT recipient_id,
    
    489 509
                               device_id,
    
    490 510
                               record as "record [session_record]",
    
    ... ... @@ -492,7 +512,7 @@ def getSessionsFromJid(self, recipientId):
    492 512
                        FROM sessions WHERE recipient_id = ?'''
    
    493 513
             return self._con.execute(query, (recipientId,)).fetchall()
    
    494 514
     
    
    495
    -    def getSessionsFromJids(self, recipientIds):
    
    515
    +    def getSessionsFromJids(self, recipientIds: list[str]):
    
    496 516
             query = '''SELECT recipient_id,
    
    497 517
                               device_id,
    
    498 518
                               record as "record [session_record]",
    
    ... ... @@ -502,7 +522,7 @@ def getSessionsFromJids(self, recipientIds):
    502 522
                            ', '.join(['?'] * len(recipientIds)))
    
    503 523
             return self._con.execute(query, recipientIds).fetchall()
    
    504 524
     
    
    505
    -    def setActiveState(self, jid, devicelist):
    
    525
    +    def setActiveState(self, jid: str, devicelist: list[int]) -> None:
    
    506 526
             query = '''UPDATE sessions SET active = 1
    
    507 527
                        WHERE recipient_id = ? AND device_id IN ({})'''.format(
    
    508 528
                            ', '.join(['?'] * len(devicelist)))
    
    ... ... @@ -514,24 +534,27 @@ def setActiveState(self, jid, devicelist):
    514 534
             self._con.execute(query, (jid,) + tuple(devicelist))
    
    515 535
             self._con.commit()
    
    516 536
     
    
    517
    -    def setInactive(self, jid, device_id):
    
    537
    +    def setInactive(self, jid: str, device_id: int) -> None:
    
    518 538
             query = '''UPDATE sessions SET active = 0
    
    519 539
                        WHERE recipient_id = ? AND device_id = ?'''
    
    520 540
             self._con.execute(query, (jid, device_id))
    
    521 541
             self._con.commit()
    
    522 542
     
    
    523
    -    def getInactiveSessionsKeys(self, recipientId):
    
    543
    +    def getInactiveSessionsKeys(self,
    
    544
    +                                recipientId: str
    
    545
    +                                ) -> list[IdentityKeyExtended]:
    
    546
    +
    
    524 547
             query = '''SELECT record as "record [session_record]" FROM sessions
    
    525 548
                        WHERE active = 0 AND recipient_id = ?'''
    
    526 549
             results = self._con.execute(query, (recipientId,)).fetchall()
    
    527 550
     
    
    528
    -        keys = []
    
    551
    +        keys: list[IdentityKeyExtended] = []
    
    529 552
             for result in results:
    
    530 553
                 key = result.record.getSessionState().getRemoteIdentityKey()
    
    531 554
                 keys.append(IdentityKeyExtended(key.getPublicKey()))
    
    532 555
             return keys
    
    533 556
     
    
    534
    -    def loadPreKey(self, preKeyId):
    
    557
    +    def loadPreKey(self, preKeyId: int) -> PreKeyRecord:
    
    535 558
             query = '''SELECT record FROM prekeys WHERE prekey_id = ?'''
    
    536 559
     
    
    537 560
             result = self._con.execute(query, (preKeyId,)).fetchone()
    
    ... ... @@ -539,41 +562,41 @@ def loadPreKey(self, preKeyId):
    539 562
                 raise Exception("No such prekeyRecord!")
    
    540 563
             return PreKeyRecord(serialized=result.record)
    
    541 564
     
    
    542
    -    def loadPendingPreKeys(self):
    
    565
    +    def loadPendingPreKeys(self) -> list[PreKeyRecord]:
    
    543 566
             query = '''SELECT record FROM prekeys'''
    
    544 567
             result = self._con.execute(query).fetchall()
    
    545 568
             return [PreKeyRecord(serialized=row.record) for row in result]
    
    546 569
     
    
    547
    -    def storePreKey(self, preKeyId, preKeyRecord):
    
    570
    +    def storePreKey(self, preKeyId: int, preKeyRecord: PreKeyRecord) -> None:
    
    548 571
             query = 'INSERT INTO prekeys (prekey_id, record) VALUES(?,?)'
    
    549 572
             self._con.execute(query, (preKeyId, preKeyRecord.serialize()))
    
    550 573
             self._con.commit()
    
    551 574
     
    
    552
    -    def containsPreKey(self, preKeyId):
    
    575
    +    def containsPreKey(self, preKeyId: int) -> bool:
    
    553 576
             query = 'SELECT record FROM prekeys WHERE prekey_id = ?'
    
    554 577
             result = self._con.execute(query, (preKeyId,)).fetchone()
    
    555 578
             return result is not None
    
    556 579
     
    
    557
    -    def removePreKey(self, preKeyId):
    
    580
    +    def removePreKey(self, preKeyId: int) -> None:
    
    558 581
             query = 'DELETE FROM prekeys WHERE prekey_id = ?'
    
    559 582
             self._con.execute(query, (preKeyId,))
    
    560 583
             self._con.commit()
    
    561 584
     
    
    562
    -    def getCurrentPreKeyId(self):
    
    585
    +    def getCurrentPreKeyId(self) -> int:
    
    563 586
             query = 'SELECT MAX(prekey_id) FROM prekeys'
    
    564 587
             return self._con.execute(query).fetchone().max_prekey_id
    
    565 588
     
    
    566
    -    def getPreKeyCount(self):
    
    589
    +    def getPreKeyCount(self) -> int:
    
    567 590
             query = 'SELECT COUNT(prekey_id) FROM prekeys'
    
    568 591
             return self._con.execute(query).fetchone().count_prekey_id
    
    569 592
     
    
    570
    -    def generateNewPreKeys(self, count):
    
    593
    +    def generateNewPreKeys(self, count: int) -> None:
    
    571 594
             prekey_id = self.getCurrentPreKeyId() or 0
    
    572 595
             pre_keys = KeyHelper.generatePreKeys(prekey_id + 1, count)
    
    573 596
             for pre_key in pre_keys:
    
    574 597
                 self.storePreKey(pre_key.getId(), pre_key)
    
    575 598
     
    
    576
    -    def getIdentityKeyPair(self):
    
    599
    +    def getIdentityKeyPair(self) -> IdentityKeyPair:
    
    577 600
             query = '''SELECT public_key as "public_key [pk]", private_key
    
    578 601
                        FROM secret LIMIT 1'''
    
    579 602
             result = self._con.execute(query).fetchone()
    
    ... ... @@ -581,12 +604,16 @@ def getIdentityKeyPair(self):
    581 604
             return IdentityKeyPair(result.public_key,
    
    582 605
                                    DjbECPrivateKey(result.private_key))
    
    583 606
     
    
    584
    -    def getLocalRegistrationId(self):
    
    607
    +    def getLocalRegistrationId(self) -> Optional[int]:
    
    585 608
             query = 'SELECT device_id FROM secret LIMIT 1'
    
    586 609
             result = self._con.execute(query).fetchone()
    
    587 610
             return result.device_id if result is not None else None
    
    588 611
     
    
    589
    -    def storeLocalData(self, device_id, identityKeyPair):
    
    612
    +    def storeLocalData(self,
    
    613
    +                       device_id: int,
    
    614
    +                       identityKeyPair: IdentityKeyPair
    
    615
    +                       ) -> None:
    
    616
    +
    
    590 617
             query = 'SELECT * FROM secret'
    
    591 618
             result = self._con.execute(query).fetchone()
    
    592 619
             if result is not None:
    
    ... ... @@ -602,7 +629,7 @@ def storeLocalData(self, device_id, identityKeyPair):
    602 629
             self._con.execute(query, (device_id, public_key, private_key))
    
    603 630
             self._con.commit()
    
    604 631
     
    
    605
    -    def saveIdentity(self, recipientId, identityKey):
    
    632
    +    def saveIdentity(self, recipientId: int, identityKey: IdentityKey) -> None:
    
    606 633
             query = '''INSERT INTO identities (recipient_id, public_key, trust, shown)
    
    607 634
                        VALUES(?, ?, ?, ?)'''
    
    608 635
             if not self.containsIdentity(recipientId, identityKey):
    
    ... ... @@ -613,7 +640,11 @@ def saveIdentity(self, recipientId, identityKey):
    613 640
                                           1 if trust == Trust.BLIND else 0))
    
    614 641
                 self._con.commit()
    
    615 642
     
    
    616
    -    def containsIdentity(self, recipientId, identityKey):
    
    643
    +    def containsIdentity(self,
    
    644
    +                         recipientId: str,
    
    645
    +                         identityKey: IdentityKey
    
    646
    +                         ) -> bool:
    
    647
    +
    
    617 648
             query = '''SELECT * FROM identities WHERE recipient_id = ?
    
    618 649
                        AND public_key = ?'''
    
    619 650
     
    
    ... ... @@ -623,24 +654,36 @@ def containsIdentity(self, recipientId, identityKey):
    623 654
     
    
    624 655
             return result is not None
    
    625 656
     
    
    626
    -    def deleteIdentity(self, recipientId, identityKey):
    
    657
    +    def deleteIdentity(self,
    
    658
    +                       recipientId: str,
    
    659
    +                       identityKey: IdentityKey
    
    660
    +                       ) -> None:
    
    661
    +
    
    627 662
             query = '''DELETE FROM identities
    
    628 663
                        WHERE recipient_id = ? AND public_key = ?'''
    
    629 664
             public_key = identityKey.getPublicKey().serialize()
    
    630 665
             self._con.execute(query, (recipientId, public_key))
    
    631 666
             self._con.commit()
    
    632 667
     
    
    633
    -    def isTrustedIdentity(self, recipientId, identityKey):
    
    668
    +    def isTrustedIdentity(self,
    
    669
    +                          recipientId: str,
    
    670
    +                          identityKey: IdentityKey
    
    671
    +                          ) -> bool:
    
    672
    +
    
    634 673
             return True
    
    635 674
     
    
    636
    -    def getTrustForIdentity(self, recipientId, identityKey):
    
    675
    +    def getTrustForIdentity(self,
    
    676
    +                            recipientId: str,
    
    677
    +                            identityKey: IdentityKey
    
    678
    +                            ) -> Optional[Trust]:
    
    679
    +
    
    637 680
             query = '''SELECT trust FROM identities WHERE recipient_id = ?
    
    638 681
                        AND public_key = ?'''
    
    639 682
             public_key = identityKey.getPublicKey().serialize()
    
    640 683
             result = self._con.execute(query, (recipientId, public_key)).fetchone()
    
    641 684
             return result.trust if result is not None else None
    
    642 685
     
    
    643
    -    def getFingerprints(self, jid):
    
    686
    +    def getFingerprints(self, jid: str):
    
    644 687
             query = '''SELECT recipient_id,
    
    645 688
                               public_key as "public_key [pk]",
    
    646 689
                               trust,
    
    ... ... @@ -649,7 +692,7 @@ def getFingerprints(self, jid):
    649 692
                        WHERE recipient_id = ? ORDER BY trust ASC'''
    
    650 693
             return self._con.execute(query, (jid,)).fetchall()
    
    651 694
     
    
    652
    -    def getMucFingerprints(self, jids):
    
    695
    +    def getMucFingerprints(self, jids: list[str]):
    
    653 696
             query = '''
    
    654 697
                 SELECT recipient_id,
    
    655 698
                        public_key as "public_key [pk]",
    
    ... ... @@ -661,7 +704,7 @@ def getMucFingerprints(self, jids):
    661 704
     
    
    662 705
             return self._con.execute(query, jids).fetchall()
    
    663 706
     
    
    664
    -    def hasUndecidedFingerprints(self, jid):
    
    707
    +    def hasUndecidedFingerprints(self, jid: str) -> bool:
    
    665 708
             query = '''SELECT public_key as "public_key [pk]" FROM identities
    
    666 709
                        WHERE recipient_id = ? AND trust = ?'''
    
    667 710
             result = self._con.execute(query, (jid, Trust.UNDECIDED)).fetchall()
    
    ... ... @@ -671,7 +714,7 @@ def hasUndecidedFingerprints(self, jid):
    671 714
             undecided = set(undecided) - set(inactive)
    
    672 715
             return bool(undecided)
    
    673 716
     
    
    674
    -    def getDefaultTrust(self, jid):
    
    717
    +    def getDefaultTrust(self, jid: str) -> Trust:
    
    675 718
             if not self._is_blind_trust_enabled():
    
    676 719
                 return Trust.UNDECIDED
    
    677 720
     
    
    ... ... @@ -682,33 +725,38 @@ def getDefaultTrust(self, jid):
    682 725
                 return Trust.BLIND
    
    683 726
             return Trust.UNDECIDED
    
    684 727
     
    
    685
    -    def getTrustedFingerprints(self, jid):
    
    728
    +    def getTrustedFingerprints(self, jid: str) -> list[IdentityKeyExtended]:
    
    686 729
             query = '''SELECT public_key as "public_key [pk]" FROM identities
    
    687 730
                        WHERE recipient_id = ? AND trust IN(1, 3)'''
    
    688 731
             result = self._con.execute(query, (jid,)).fetchall()
    
    689 732
             return [row.public_key for row in result]
    
    690 733
     
    
    691
    -    def getNewFingerprints(self, jid):
    
    734
    +    def getNewFingerprints(self, jid: str) -> list[int]:
    
    692 735
             query = '''SELECT _id FROM identities WHERE shown = 0
    
    693 736
                        AND recipient_id = ?'''
    
    694 737
     
    
    695 738
             result = self._con.execute(query, (jid,)).fetchall()
    
    696 739
             return [row.id for row in result]
    
    697 740
     
    
    698
    -    def setShownFingerprints(self, fingerprints):
    
    741
    +    def setShownFingerprints(self, fingerprints: list[int]) -> None:
    
    699 742
             query = 'UPDATE identities SET shown = 1 WHERE _id IN ({})'.format(
    
    700 743
                 ', '.join(['?'] * len(fingerprints)))
    
    701 744
             self._con.execute(query, fingerprints)
    
    702 745
             self._con.commit()
    
    703 746
     
    
    704
    -    def setTrust(self, recipient_id, identityKey, trust):
    
    747
    +    def setTrust(self,
    
    748
    +                 recipient_id: str,
    
    749
    +                 identityKey: IdentityKey,
    
    750
    +                 trust: Trust
    
    751
    +                 ) -> None:
    
    752
    +
    
    705 753
             query = '''UPDATE identities SET trust = ? WHERE public_key = ?
    
    706 754
                        AND recipient_id = ?'''
    
    707 755
             public_key = identityKey.getPublicKey().serialize()
    
    708 756
             self._con.execute(query, (trust, public_key, recipient_id))
    
    709 757
             self._con.commit()
    
    710 758
     
    
    711
    -    def isTrusted(self, recipient_id, device_id):
    
    759
    +    def isTrusted(self, recipient_id: str, device_id: int) -> bool:
    
    712 760
             record = self.loadSession(recipient_id, device_id)
    
    713 761
             if record.isFresh():
    
    714 762
                 return False
    
    ... ... @@ -716,7 +764,11 @@ def isTrusted(self, recipient_id, device_id):
    716 764
             return self.getTrustForIdentity(
    
    717 765
                 recipient_id, identity_key) in (Trust.VERIFIED, Trust.BLIND)
    
    718 766
     
    
    719
    -    def getIdentityLastSeen(self, recipient_id, identity_key):
    
    767
    +    def getIdentityLastSeen(self,
    
    768
    +                            recipient_id: str,
    
    769
    +                            identity_key: IdentityKey
    
    770
    +                            ) -> Optional[int]:
    
    771
    +
    
    720 772
             identity_key = identity_key.getPublicKey().serialize()
    
    721 773
             query = '''SELECT timestamp FROM identities
    
    722 774
                        WHERE recipient_id = ? AND public_key = ?'''
    
    ... ... @@ -724,7 +776,11 @@ def getIdentityLastSeen(self, recipient_id, identity_key):
    724 776
                                                identity_key)).fetchone()
    
    725 777
             return result.timestamp if result is not None else None
    
    726 778
     
    
    727
    -    def setIdentityLastSeen(self, recipient_id, identity_key):
    
    779
    +    def setIdentityLastSeen(self,
    
    780
    +                            recipient_id: str,
    
    781
    +                            identity_key: IdentityKey
    
    782
    +                            ) -> None:
    
    783
    +
    
    728 784
             timestamp = int(time.time())
    
    729 785
             identity_key = identity_key.getPublicKey().serialize()
    
    730 786
             self._log.info('Set last seen for %s %s', recipient_id, timestamp)
    
    ... ... @@ -733,7 +789,7 @@ def setIdentityLastSeen(self, recipient_id, identity_key):
    733 789
             self._con.execute(query, (timestamp, recipient_id, identity_key))
    
    734 790
             self._con.commit()
    
    735 791
     
    
    736
    -    def getUnacknowledgedCount(self, recipient_id, device_id):
    
    792
    +    def getUnacknowledgedCount(self, recipient_id: str, device_id: int) -> int:
    
    737 793
             record = self.loadSession(recipient_id, device_id)
    
    738 794
             if record.isFresh():
    
    739 795
                 return 0
    

  • gajim/common/modules/omemobackend/state.py
    ... ... @@ -15,8 +15,14 @@
    15 15
     # You should have received a copy of the GNU General Public License
    
    16 16
     # along with OMEMO Gajim Plugin. If not, see <http://www.gnu.org/licenses/>.
    
    17 17
     
    
    18
    +from __future__ import annotations
    
    19
    +
    
    20
    +from typing import Any
    
    21
    +from typing import Optional
    
    22
    +
    
    18 23
     import time
    
    19 24
     from collections import defaultdict
    
    25
    +from pathlib import Path
    
    20 26
     
    
    21 27
     from nbxmpp.structs import OMEMOBundle
    
    22 28
     from nbxmpp.structs import OMEMOMessage
    
    ... ... @@ -24,6 +30,7 @@
    24 30
     from axolotl.ecc.djbec import DjbECPublicKey
    
    25 31
     from axolotl.identitykey import IdentityKey
    
    26 32
     
    
    33
    +from axolotl.identitykeypair import IdentityKeyPair
    
    27 34
     from axolotl.protocol.prekeywhispermessage import PreKeyWhisperMessage
    
    28 35
     from axolotl.protocol.whispermessage import WhisperMessage
    
    29 36
     from axolotl.sessionbuilder import SessionBuilder
    
    ... ... @@ -32,6 +39,7 @@
    32 39
     from axolotl.util.keyhelper import KeyHelper
    
    33 40
     from axolotl.duplicatemessagexception import DuplicateMessageException
    
    34 41
     
    
    42
    +from gajim.common import types
    
    35 43
     from gajim.common.modules.omemobackend.aes import aes_decrypt
    
    36 44
     from gajim.common.modules.omemobackend.aes import aes_encrypt
    
    37 45
     from gajim.common.modules.omemobackend.aes import get_new_key
    
    ... ... @@ -39,21 +47,28 @@
    39 47
     from gajim.common.modules.omemobackend.devices import DeviceManager
    
    40 48
     from gajim.common.modules.omemobackend.devices import NoDevicesFound
    
    41 49
     from gajim.common.modules.omemobackend.liteaxolotlstore import LiteAxolotlStore
    
    42
    -from gajim.common.modules.util import get_fingerprint
    
    43
    -from gajim.common.modules.util import Trust
    
    44
    -from gajim.common.modules.util import DEFAULT_PREKEY_AMOUNT
    
    45
    -from gajim.common.modules.util import MIN_PREKEY_AMOUNT
    
    46
    -from gajim.common.modules.util import SPK_CYCLE_TIME
    
    47
    -from gajim.common.modules.util import SPK_ARCHIVE_TIME
    
    48
    -from gajim.common.modules.util import UNACKNOWLEDGED_COUNT
    
    50
    +from gajim.common.modules.omemobackend.util import get_fingerprint
    
    51
    +from gajim.common.modules.omemobackend.util import Trust
    
    52
    +from gajim.common.modules.omemobackend.util import DEFAULT_PREKEY_AMOUNT
    
    53
    +from gajim.common.modules.omemobackend.util import MIN_PREKEY_AMOUNT
    
    54
    +from gajim.common.modules.omemobackend.util import SPK_CYCLE_TIME
    
    55
    +from gajim.common.modules.omemobackend.util import SPK_ARCHIVE_TIME
    
    56
    +from gajim.common.modules.omemobackend.util import UNACKNOWLEDGED_COUNT
    
    49 57
     
    
    50 58
     
    
    51 59
     class OmemoState(DeviceManager):
    
    52
    -    def __init__(self, own_jid, db_path, account, xmpp_con):
    
    60
    +    def __init__(self,
    
    61
    +                 own_jid: str,
    
    62
    +                 db_path: Path,
    
    63
    +                 account: str,
    
    64
    +                 xmpp_con: types.xmppClient
    
    65
    +                 ) -> None:
    
    66
    +
    
    53 67
             self._account = account
    
    54 68
             self._own_jid = own_jid
    
    55 69
             self._log = xmpp_con._log
    
    56
    -        self._session_ciphers = defaultdict(dict)
    
    70
    +        self._session_ciphers: dict[
    
    71
    +            str, dict[int, SessionCipher]] = defaultdict(dict)
    
    57 72
             self._storage = LiteAxolotlStore(account, db_path, self._log)
    
    58 73
     
    
    59 74
             DeviceManager.__init__(self)
    
    ... ... @@ -63,7 +78,12 @@ def __init__(self, own_jid, db_path, account, xmpp_con):
    63 78
             self._log.info('%s PreKeys available',
    
    64 79
                            self._storage.getPreKeyCount())
    
    65 80
     
    
    66
    -    def build_session(self, jid, device_id, bundle):
    
    81
    +    def build_session(self,
    
    82
    +                      jid: str,
    
    83
    +                      device_id: int,
    
    84
    +                      bundle: OMEMOBundle
    
    85
    +                      ) -> None:
    
    86
    +
    
    67 87
             session = SessionBuilder(self._storage, self._storage, self._storage,
    
    68 88
                                      self._storage, jid, device_id)
    
    69 89
     
    
    ... ... @@ -88,18 +108,18 @@ def build_session(self, jid, device_id, bundle):
    88 108
             self._get_session_cipher(jid, device_id)
    
    89 109
     
    
    90 110
         @property
    
    91
    -    def storage(self):
    
    111
    +    def storage(self) -> LiteAxolotlStore:
    
    92 112
             return self._storage
    
    93 113
     
    
    94 114
         @property
    
    95
    -    def own_fingerprint(self):
    
    115
    +    def own_fingerprint(self) -> str:
    
    96 116
             return get_fingerprint(self._storage.getIdentityKeyPair())
    
    97 117
     
    
    98 118
         @property
    
    99
    -    def bundle(self):
    
    119
    +    def bundle(self) -> OMEMOBundle:
    
    100 120
             self._check_pre_key_count()
    
    101 121
     
    
    102
    -        bundle = {'otpks': []}
    
    122
    +        bundle: dict[str, Any] = {'otpks': []}
    
    103 123
             for k in self._storage.loadPendingPreKeys():
    
    104 124
                 key = k.getKeyPair().getPublicKey().serialize()
    
    105 125
                 bundle['otpks'].append({'key': key, 'id': k.getId()})
    
    ... ... @@ -117,7 +137,11 @@ def bundle(self):
    117 137
     
    
    118 138
             return OMEMOBundle(**bundle)
    
    119 139
     
    
    120
    -    def decrypt_message(self, omemo_message, jid):
    
    140
    +    def decrypt_message(self,
    
    141
    +                        omemo_message: OMEMOMessage,
    
    142
    +                        jid: str
    
    143
    +                        ) -> tuple[str, str, Trust]:
    
    144
    +
    
    121 145
             if omemo_message.sid == self.own_device:
    
    122 146
                 self._log.info('Received previously sent message by us')
    
    123 147
                 raise SelfMessage
    
    ... ... @@ -157,13 +181,18 @@ def decrypt_message(self, omemo_message, jid):
    157 181
             self._log.debug("Decrypted Message => %s", result)
    
    158 182
             return result, fingerprint, trust
    
    159 183
     
    
    160
    -    def _get_whisper_message(self, jid, device, key):
    
    184
    +    def _get_whisper_message(self,
    
    185
    +                             jid: str,
    
    186
    +                             device: int,
    
    187
    +                             key: bytes
    
    188
    +                             ) -> tuple[bytes, bool]:
    
    189
    +
    
    161 190
             cipher = self._get_session_cipher(jid, device)
    
    162 191
             cipher_key = cipher.encrypt(key)
    
    163 192
             prekey = isinstance(cipher_key, PreKeyWhisperMessage)
    
    164 193
             return cipher_key.serialize(), prekey
    
    165 194
     
    
    166
    -    def encrypt(self, jid, plaintext):
    
    195
    +    def encrypt(self, jid: str, plaintext: str) -> Optional[OMEMOMessage]:
    
    167 196
             try:
    
    168 197
                 devices_for_encryption = self.get_devices_for_encryption(jid)
    
    169 198
             except NoDevicesFound:
    
    ... ... @@ -171,7 +200,8 @@ def encrypt(self, jid, plaintext):
    171 200
                 return
    
    172 201
     
    
    173 202
             result = aes_encrypt(plaintext)
    
    174
    -        whisper_messages = defaultdict(dict)
    
    203
    +        whisper_messages: dict[
    
    204
    +            str, dict[int, tuple[bytes, bool]]] = defaultdict(dict)
    
    175 205
     
    
    176 206
             for jid_, device in devices_for_encryption:
    
    177 207
                 count = self._storage.getUnacknowledgedCount(jid_, device)
    
    ... ... @@ -194,7 +224,7 @@ def encrypt(self, jid, plaintext):
    194 224
                 self._log.error('Encrypted keys empty')
    
    195 225
                 return
    
    196 226
     
    
    197
    -        encrypted_keys = {}
    
    227
    +        encrypted_keys: dict[int, tuple[bytes, bool]] = {}
    
    198 228
             for jid_ in whisper_messages:
    
    199 229
                 encrypted_keys.update(whisper_messages[jid_])
    
    200 230
     
    
    ... ... @@ -204,8 +234,13 @@ def encrypt(self, jid, plaintext):
    204 234
                                 iv=result.iv,
    
    205 235
                                 payload=result.payload)
    
    206 236
     
    
    207
    -    def encrypt_key_transport(self, jid, devices):
    
    208
    -        whisper_messages = defaultdict(dict)
    
    237
    +    def encrypt_key_transport(self,
    
    238
    +                              jid: str,
    
    239
    +                              devices: list[int]
    
    240
    +                              ) -> Optional[OMEMOMessage]:
    
    241
    +
    
    242
    +        whisper_messages: dict[
    
    243
    +            str, dict[int, tuple[bytes, bool]]] = defaultdict(dict)
    
    209 244
             for device in devices:
    
    210 245
                 try:
    
    211 246
                     whisper_messages[jid][device] = self._get_whisper_message(
    
    ... ... @@ -224,12 +259,12 @@ def encrypt_key_transport(self, jid, devices):
    224 259
                                 iv=get_new_iv(),
    
    225 260
                                 payload=None)
    
    226 261
     
    
    227
    -    def has_trusted_keys(self, jid):
    
    262
    +    def has_trusted_keys(self, jid: str) -> bool:
    
    228 263
             inactive = self._storage.getInactiveSessionsKeys(jid)
    
    229 264
             trusted = self._storage.getTrustedFingerprints(jid)
    
    230 265
             return bool(set(trusted) - set(inactive))
    
    231 266
     
    
    232
    -    def devices_without_sessions(self, jid):
    
    267
    +    def devices_without_sessions(self, jid: str) -> list[int]:
    
    233 268
             known_devices = self.get_devices(jid, without_self=True)
    
    234 269
             missing_devices = [dev
    
    235 270
                                for dev in known_devices
    
    ... ... @@ -239,7 +274,7 @@ def devices_without_sessions(self, jid):
    239 274
                                jid, missing_devices)
    
    240 275
             return missing_devices
    
    241 276
     
    
    242
    -    def _get_session_cipher(self, jid, device_id):
    
    277
    +    def _get_session_cipher(self, jid: str, device_id: int) -> SessionCipher:
    
    243 278
             try:
    
    244 279
                 return self._session_ciphers[jid][device_id]
    
    245 280
             except KeyError:
    
    ... ... @@ -248,7 +283,12 @@ def _get_session_cipher(self, jid, device_id):
    248 283
                 self._session_ciphers[jid][device_id] = cipher
    
    249 284
                 return cipher
    
    250 285
     
    
    251
    -    def _process_pre_key_message(self, jid, device, key):
    
    286
    +    def _process_pre_key_message(self,
    
    287
    +                                 jid: str,
    
    288
    +                                 device: int,
    
    289
    +                                 key: bytes
    
    290
    +                                 ) -> tuple[bytes, str, Trust]:
    
    291
    +
    
    252 292
             self._log.info('Process pre key message from %s', jid)
    
    253 293
             pre_key_message = PreKeyWhisperMessage(serialized=key)
    
    254 294
             if not pre_key_message.getPreKeyId():
    
    ... ... @@ -268,7 +308,12 @@ def _process_pre_key_message(self, jid, device, key):
    268 308
             self.add_device(jid, device)
    
    269 309
             return key, fingerprint, trust
    
    270 310
     
    
    271
    -    def _process_message(self, jid, device, key):
    
    311
    +    def _process_message(self,
    
    312
    +                         jid: str,
    
    313
    +                         device: int,
    
    314
    +                         key: bytes
    
    315
    +                         ) -> tuple[bytes, str, Trust]:
    
    316
    +
    
    272 317
             self._log.info('Process message from %s', jid)
    
    273 318
             message = WhisperMessage(serialized=key)
    
    274 319
     
    
    ... ... @@ -290,15 +335,23 @@ def _get_identity_key_from_pk_message(key):
    290 335
             pre_key_message = PreKeyWhisperMessage(serialized=key)
    
    291 336
             return pre_key_message.getIdentityKey()
    
    292 337
     
    
    293
    -    def _get_identity_key_from_device(self, jid, device):
    
    338
    +    def _get_identity_key_from_device(self,
    
    339
    +                                      jid: str,
    
    340
    +                                      device: int
    
    341
    +                                      ) -> Optional[IdentityKey]:
    
    342
    +
    
    294 343
             session_record = self._storage.loadSession(jid, device)
    
    295 344
             return session_record.getSessionState().getRemoteIdentityKey()
    
    296 345
     
    
    297
    -    def _get_trust_from_identity_key(self, jid, identity_key):
    
    346
    +    def _get_trust_from_identity_key(self,
    
    347
    +                                     jid: str,
    
    348
    +                                     identity_key: IdentityKey
    
    349
    +                                     ) -> Trust:
    
    350
    +
    
    298 351
             trust = self._storage.getTrustForIdentity(jid, identity_key)
    
    299 352
             return Trust(trust) if trust is not None else Trust.UNDECIDED
    
    300 353
     
    
    301
    -    def _check_pre_key_count(self):
    
    354
    +    def _check_pre_key_count(self) -> None:
    
    302 355
             # Check if enough PreKeys are available
    
    303 356
             pre_key_count = self._storage.getPreKeyCount()
    
    304 357
             if pre_key_count < MIN_PREKEY_AMOUNT:
    
    ... ... @@ -306,7 +359,7 @@ def _check_pre_key_count(self):
    306 359
                 self._storage.generateNewPreKeys(missing_count)
    
    307 360
                 self._log.info('%s PreKeys created', missing_count)
    
    308 361
     
    
    309
    -    def _cycle_signed_pre_key(self, ik_pair):
    
    362
    +    def _cycle_signed_pre_key(self, ik_pair: IdentityKeyPair) -> None:
    
    310 363
             # Publish every SPK_CYCLE_TIME a new SignedPreKey
    
    311 364
             # Delete all exsiting SignedPreKeys that are older
    
    312 365
             # then SPK_ARCHIVE_TIME
    

  • gajim/common/modules/omemobackend/util.py
    ... ... @@ -14,11 +14,15 @@
    14 14
     # You should have received a copy of the GNU General Public License
    
    15 15
     # along with OMEMO Gajim Plugin. If not, see <http://www.gnu.org/licenses/>.
    
    16 16
     
    
    17
    +from __future__ import annotations
    
    18
    +
    
    17 19
     import binascii
    
    18 20
     import textwrap
    
    19 21
     from enum import IntEnum
    
    20 22
     
    
    21 23
     from axolotl.identitykey import IdentityKey
    
    24
    +from axolotl.identitykeypair import IdentityKeyPair
    
    25
    +
    
    22 26
     
    
    23 27
     DEFAULT_PREKEY_AMOUNT = 100
    
    24 28
     MIN_PREKEY_AMOUNT = 80
    
    ... ... @@ -34,7 +38,10 @@ class Trust(IntEnum):
    34 38
         BLIND = 3
    
    35 39
     
    
    36 40
     
    
    37
    -def get_fingerprint(identity_key, formatted=False):
    
    41
    +def get_fingerprint(identity_key: IdentityKeyPair,
    
    42
    +                    formatted: bool = False
    
    43
    +                    ) -> str:
    
    44
    +
    
    38 45
         public_key = identity_key.getPublicKey().serialize()
    
    39 46
         fingerprint = binascii.hexlify(public_key).decode()[2:]
    
    40 47
         if not formatted:
    
    ... ... @@ -49,8 +56,8 @@ def get_fingerprint(identity_key, formatted=False):
    49 56
     
    
    50 57
     
    
    51 58
     class IdentityKeyExtended(IdentityKey):
    
    52
    -    def __hash__(self):
    
    59
    +    def __hash__(self) -> int:
    
    53 60
             return hash(self.publicKey.serialize())
    
    54 61
     
    
    55
    -    def get_fingerprint(self, formatted=False):
    
    62
    +    def get_fingerprint(self, formatted: bool = False) -> str:
    
    56 63
             return get_fingerprint(self, formatted=formatted)

  • gajim/common/modules/util.py
    ... ... @@ -17,6 +17,7 @@
    17 17
     from __future__ import annotations
    
    18 18
     
    
    19 19
     from typing import Any
    
    20
    +from typing import Optional
    
    20 21
     from typing import Union
    
    21 22
     
    
    22 23
     import logging
    
    ... ... @@ -187,13 +188,17 @@ def check_if_message_correction(properties: MessageProperties,
    187 188
         return True
    
    188 189
     
    
    189 190
     
    
    190
    -def prepare_stanza(stanza, plaintext):
    
    191
    +def prepare_stanza(stanza: Message, plaintext: str) -> None:
    
    191 192
         delete_nodes(stanza, 'encrypted', Namespace.OMEMO_TEMP)
    
    192 193
         delete_nodes(stanza, 'body')
    
    193 194
         stanza.setBody(plaintext)
    
    194 195
     
    
    195 196
     
    
    196
    -def delete_nodes(stanza, name, namespace=None):
    
    197
    +def delete_nodes(stanza: Message,
    
    198
    +                 name: str,
    
    199
    +                 namespace: Optional[str] = None
    
    200
    +                 ) -> None:
    
    201
    +
    
    197 202
         nodes = stanza.getTags(name, namespace=namespace)
    
    198 203
         for node in nodes:
    
    199 204
             stanza.delChild(node)