Philipp Hörist pushed to branch master at gajim / gajim

Commits:

9 changed files:

Changes:

  • debian/control
    ... ... @@ -13,7 +13,7 @@ Build-Depends:
    13 13
         python3-gi,
    
    14 14
         python3-gi-cairo,
    
    15 15
         python3-nbxmpp-nightly (>=20230405),
    
    16
    -    python3-omemo-dr (>=20230407),
    
    16
    +    python3-omemo-dr (>=20230503),
    
    17 17
         python3-setuptools,
    
    18 18
         python3-packaging,
    
    19 19
         python3-cryptography (>=3.4.8),
    
    ... ... @@ -36,7 +36,7 @@ Depends:
    36 36
         python3-gi (>= 3.42.0),
    
    37 37
         python3-gi-cairo (>= 1.14.0~),
    
    38 38
         python3-nbxmpp-nightly (>=20230405),
    
    39
    -    python3-omemo-dr (>=20230407),
    
    39
    +    python3-omemo-dr (>=20230503),
    
    40 40
         gir1.2-pango-1.0 (>= 1.50.0),
    
    41 41
         gir1.2-gtk-3.0 (>= 3.24.30),
    
    42 42
         gir1.2-gtksource-4,
    

  • gajim/common/modules/omemo.py
    ... ... @@ -24,6 +24,7 @@
    24 24
     
    
    25 25
     import binascii
    
    26 26
     import threading
    
    27
    +from pathlib import Path
    
    27 28
     
    
    28 29
     from gi.repository import GLib
    
    29 30
     from nbxmpp.const import Affiliation
    
    ... ... @@ -42,8 +43,19 @@
    42 43
     from nbxmpp.structs import PresenceProperties
    
    43 44
     from nbxmpp.structs import StanzaHandler
    
    44 45
     from nbxmpp.task import Task
    
    46
    +from omemo_dr.aes import aes_encrypt_file
    
    47
    +from omemo_dr.const import OMEMOTrust
    
    48
    +from omemo_dr.exceptions import DecryptionFailed
    
    49
    +from omemo_dr.exceptions import DuplicateMessage
    
    50
    +from omemo_dr.exceptions import KeyExchangeMessage
    
    51
    +from omemo_dr.exceptions import MessageNotForDevice
    
    52
    +from omemo_dr.exceptions import SelfMessage
    
    53
    +from omemo_dr.session import OMEMOSession
    
    54
    +from omemo_dr.structs import OMEMOBundle
    
    55
    +from omemo_dr.structs import OMEMOConfig
    
    45 56
     
    
    46 57
     from gajim.common import app
    
    58
    +from gajim.common import configpaths
    
    47 59
     from gajim.common import ged
    
    48 60
     from gajim.common import types
    
    49 61
     from gajim.common.const import EncryptionData
    
    ... ... @@ -60,14 +72,7 @@
    60 72
     from gajim.common.modules.util import as_task
    
    61 73
     from gajim.common.modules.util import event_node
    
    62 74
     from gajim.common.modules.util import prepare_stanza
    
    63
    -from gajim.common.omemo.aes import aes_encrypt_file
    
    64
    -from gajim.common.omemo.state import DecryptionFailed
    
    65
    -from gajim.common.omemo.state import DuplicateMessage
    
    66
    -from gajim.common.omemo.state import KeyExchangeMessage
    
    67
    -from gajim.common.omemo.state import MessageNotForDevice
    
    68
    -from gajim.common.omemo.state import OmemoState
    
    69
    -from gajim.common.omemo.state import SelfMessage
    
    70
    -from gajim.common.omemo.util import Trust
    
    75
    +from gajim.common.storage.omemo import OMEMOStorage
    
    71 76
     from gajim.common.structs import OutgoingMessage
    
    72 77
     
    
    73 78
     ALLOWED_TAGS = [
    
    ... ... @@ -122,7 +127,20 @@ def __init__(self, client: types.Client) -> None:
    122 127
             self.allow_groupchat = True
    
    123 128
     
    
    124 129
             self._own_jid = self._client.get_own_jid().bare
    
    125
    -        self._backend = OmemoState(self._account, self._own_jid, self)
    
    130
    +
    
    131
    +        data_dir = Path(configpaths.get('MY_DATA'))
    
    132
    +        db_path = data_dir / f'omemo_{self._own_jid}.db'
    
    133
    +        storage = OMEMOStorage(self._account, db_path, self._log)
    
    134
    +
    
    135
    +        omemo_config = OMEMOConfig(default_prekey_amount=100,
    
    136
    +                                   min_prekey_amount=80,
    
    137
    +                                   spk_archive_seconds=86400 * 15,
    
    138
    +                                   spk_cycle_seconds=86400,
    
    139
    +                                   unacknowledged_count=2000)
    
    140
    +
    
    141
    +        self._backend = OMEMOSession(self._own_jid, storage, omemo_config)
    
    142
    +        self._backend.register_signal('republish-bundle',
    
    143
    +                                      self._on_republish_bundle)
    
    126 144
     
    
    127 145
             self._omemo_groupchats: set[str] = set()
    
    128 146
             self._muc_temp_store: dict[bytes, str] = {}
    
    ... ... @@ -160,8 +178,16 @@ def _on_room_joined(self,
    160 178
             if self.is_omemo_groupchat(jid):
    
    161 179
                 self.get_affiliation_list(jid)
    
    162 180
     
    
    181
    +    def _on_republish_bundle(self,
    
    182
    +                             _session: OMEMOSession,
    
    183
    +                             _signal_name: str,
    
    184
    +                             bundle: OMEMOBundle
    
    185
    +                             ) -> None:
    
    186
    +
    
    187
    +        self.set_bundle(bundle=bundle)
    
    188
    +
    
    163 189
         @property
    
    164
    -    def backend(self) -> OmemoState:
    
    190
    +    def backend(self) -> OMEMOSession:
    
    165 191
             return self._backend
    
    166 192
     
    
    167 193
         def check_send_preconditions(self, contact: types.ChatContactT) -> bool:
    
    ... ... @@ -175,7 +201,7 @@ def check_send_preconditions(self, contact: types.ChatContactT) -> bool:
    175 201
                     return False
    
    176 202
     
    
    177 203
                 missing = True
    
    178
    -            for member_jid in self.backend.get_muc_members(jid):
    
    204
    +            for member_jid in self.backend.get_group_members(jid):
    
    179 205
                     if not self.are_keys_missing(member_jid):
    
    180 206
                         missing = False
    
    181 207
                 if missing:
    
    ... ... @@ -197,7 +223,7 @@ def check_send_preconditions(self, contact: types.ChatContactT) -> bool:
    197 223
                     return False
    
    198 224
     
    
    199 225
                 # check if bundles are missing for some devices
    
    200
    -            if self.backend.storage.hasUndecidedFingerprints(jid):
    
    226
    +            if self.backend.storage.has_undecided_fingerprints(jid):
    
    201 227
                     self._log.info('%s => Undecided Fingerprints for %s',
    
    202 228
                                    contact.account, jid)
    
    203 229
                     app.ged.raise_event(EncryptionInfo(
    
    ... ... @@ -217,15 +243,15 @@ def check_send_preconditions(self, contact: types.ChatContactT) -> bool:
    217 243
         def _new_fingerprints_available(self, contact: types.ChatContactT) -> bool:
    
    218 244
             fingerprints: list[int] = []
    
    219 245
             if contact.is_groupchat:
    
    220
    -            for member_jid in self.backend.get_muc_members(str(contact.jid),
    
    246
    +            for member_jid in self.backend.get_group_members(str(contact.jid),
    
    221 247
                                                                without_self=False):
    
    222
    -                fingerprints = self.backend.storage.getNewFingerprints(
    
    248
    +                fingerprints = self.backend.storage.get_new_fingerprints(
    
    223 249
                         member_jid)
    
    224 250
                     if fingerprints:
    
    225 251
                         break
    
    226 252
     
    
    227 253
             else:
    
    228
    -            fingerprints = self.backend.storage.getNewFingerprints(
    
    254
    +            fingerprints = self.backend.storage.get_new_fingerprints(
    
    229 255
                     str(contact.jid))
    
    230 256
     
    
    231 257
             if not fingerprints:
    
    ... ... @@ -245,7 +271,12 @@ def encrypt_message(self, event: OutgoingMessage) -> bool:
    245 271
             if not event.message:
    
    246 272
                 return False
    
    247 273
     
    
    248
    -        omemo_message = self.backend.encrypt(str(event.jid), event.message)
    
    274
    +        client = app.get_client(self._account)
    
    275
    +        contact = client.get_module('Contacts').get_contact(event.jid)
    
    276
    +
    
    277
    +        omemo_message = self.backend.encrypt(str(event.jid),
    
    278
    +                                             event.message,
    
    279
    +                                             groupchat=contact.is_groupchat)
    
    249 280
             if omemo_message is None:
    
    250 281
                 raise Exception('Encryption error')
    
    251 282
     
    
    ... ... @@ -258,7 +289,7 @@ def encrypt_message(self, event: OutgoingMessage) -> bool:
    258 289
                 event.xhtml = None
    
    259 290
                 event.additional_data['encrypted'] = {
    
    260 291
                     'name': 'OMEMO',
    
    261
    -                'trust': GajimTrust[Trust.VERIFIED.name]}
    
    292
    +                'trust': GajimTrust[OMEMOTrust.VERIFIED.name]}
    
    262 293
     
    
    263 294
             self._debug_print_stanza(event.stanza)
    
    264 295
             return True
    
    ... ... @@ -346,8 +377,8 @@ def _message_received(self,
    346 377
                     return
    
    347 378
     
    
    348 379
                 plaintext = self._muc_temp_store[properties.omemo.payload]
    
    349
    -            fingerprint = self.backend.own_fingerprint
    
    350
    -            trust = Trust.VERIFIED
    
    380
    +            fingerprint = self.backend.get_own_fingerprint()
    
    381
    +            trust = OMEMOTrust.VERIFIED
    
    351 382
                 del self._muc_temp_store[properties.omemo.payload]
    
    352 383
     
    
    353 384
             except DecryptionFailed:
    
    ... ... @@ -362,7 +393,7 @@ def _message_received(self,
    362 393
                               'but not for your device.')
    
    363 394
                 # Neither trust nor fingerprint can be verified if we didn't
    
    364 395
                 # successfully decrypt the message
    
    365
    -            trust = Trust.UNTRUSTED
    
    396
    +            trust = OMEMOTrust.UNTRUSTED
    
    366 397
                 fingerprint = None
    
    367 398
     
    
    368 399
             prepare_stanza(stanza, plaintext)
    
    ... ... @@ -388,7 +419,8 @@ def _process_muc_message(self,
    388 419
     
    
    389 420
             assert isinstance(properties.omemo, OMEMOMessage)
    
    390 421
             self._log.info('Groupchat: Last resort trying to find SID in DB')
    
    391
    -        from_jid = self.backend.storage.getJidFromDevice(properties.omemo.sid)
    
    422
    +        from_jid = self.backend.storage.get_jid_from_device(
    
    423
    +            properties.omemo.sid)
    
    392 424
             if not from_jid:
    
    393 425
                 self._log.error(
    
    394 426
                     "Can't decrypt GroupChat Message from %s", resource)
    
    ... ... @@ -430,9 +462,9 @@ def _on_muc_user_presence(self,
    430 462
             jid = properties.muc_user.jid.bare
    
    431 463
             if properties.muc_user.affiliation in (Affiliation.OUTCAST,
    
    432 464
                                                    Affiliation.NONE):
    
    433
    -            self.backend.remove_muc_member(room, jid)
    
    465
    +            self.backend.remove_group_member(room, jid)
    
    434 466
             else:
    
    435
    -            self.backend.add_muc_member(room, jid)
    
    467
    +            self.backend.add_group_member(room, jid)
    
    436 468
     
    
    437 469
             if self.is_omemo_groupchat(room):
    
    438 470
                 if not self.is_contact_in_roster(jid):
    
    ... ... @@ -458,7 +490,7 @@ def _on_affiliations_received(self, task: Task) -> None:
    458 490
     
    
    459 491
             for user_jid in result.users:
    
    460 492
                 jid = str(user_jid)
    
    461
    -            self.backend.add_muc_member(room_jid, jid)
    
    493
    +            self.backend.add_group_member(room_jid, jid)
    
    462 494
     
    
    463 495
                 if not self.is_contact_in_roster(jid):
    
    464 496
                     # Query Devicelists from JIDs not in our Roster
    
    ... ... @@ -486,7 +518,7 @@ def _check_if_omemo_capable(self, jid: str) -> None:
    486 518
                 self._omemo_groupchats.discard(jid)
    
    487 519
     
    
    488 520
         def _check_for_missing_sessions(self, jid: str) -> None:
    
    489
    -        devices_without_session = self.backend.devices_without_sessions(jid)
    
    521
    +        devices_without_session = self.backend.get_devices_without_sessions(jid)
    
    490 522
             for device_id in devices_without_session:
    
    491 523
                 if device_id in self._device_bundle_querys:
    
    492 524
                     continue
    
    ... ... @@ -511,7 +543,7 @@ def are_keys_missing(self, contact_jid: str) -> bool:
    511 543
             # Fetch Bundles of own other Devices
    
    512 544
             if self._own_jid not in self._query_for_bundles:
    
    513 545
     
    
    514
    -            devices_without_session = self.backend.devices_without_sessions(
    
    546
    +            devices_without_session = self.backend.get_devices_without_sessions(
    
    515 547
                     self._own_jid)
    
    516 548
     
    
    517 549
                 self._query_for_bundles.append(self._own_jid)
    
    ... ... @@ -523,7 +555,7 @@ def are_keys_missing(self, contact_jid: str) -> bool:
    523 555
             # Fetch Bundles of contacts devices
    
    524 556
             if contact_jid not in self._query_for_bundles:
    
    525 557
     
    
    526
    -            devices_without_session = self.backend.devices_without_sessions(
    
    558
    +            devices_without_session = self.backend.get_devices_without_sessions(
    
    527 559
                     contact_jid)
    
    528 560
     
    
    529 561
                 self._query_for_bundles.append(contact_jid)
    
    ... ... @@ -536,9 +568,11 @@ def are_keys_missing(self, contact_jid: str) -> bool:
    536 568
                 return False
    
    537 569
             return True
    
    538 570
     
    
    539
    -    def set_bundle(self) -> None:
    
    540
    -        self._nbxmpp('OMEMO').set_bundle(self.backend.bundle,
    
    541
    -                                         self.backend.own_device)
    
    571
    +    def set_bundle(self, bundle: OMEMOBundle | None = None) -> None:
    
    572
    +        if bundle is None:
    
    573
    +            bundle = self.backend.get_bundle()
    
    574
    +        self._nbxmpp('OMEMO').set_bundle(bundle,
    
    575
    +                                         self.backend.get_own_device())
    
    542 576
     
    
    543 577
         @as_task
    
    544 578
         def request_bundle(self, jid: str, device_id: int):
    
    ... ... @@ -566,7 +600,7 @@ def request_bundle(self, jid: str, device_id: int):
    566 600
                 message=EncryptionInfoMsg.UNDECIDED_FINGERPRINTS))
    
    567 601
     
    
    568 602
         def set_devicelist(self, devicelist: Optional[list[int]] = None) -> None:
    
    569
    -        devicelist_: set[int] = {self.backend.own_device}
    
    603
    +        devicelist_: set[int] = {self.backend.get_own_device()}
    
    570 604
             if devicelist is not None:
    
    571 605
                 devicelist_.update(devicelist)
    
    572 606
             self._log.info('Publishing own devicelist: %s', devicelist_)
    
    ... ... @@ -574,7 +608,7 @@ def set_devicelist(self, devicelist: Optional[list[int]] = None) -> None:
    574 608
     
    
    575 609
         def clear_devicelist(self) -> None:
    
    576 610
             self.backend.update_devicelist(
    
    577
    -            self._own_jid, [self.backend.own_device])
    
    611
    +            self._own_jid, [self.backend.get_own_device()])
    
    578 612
             self.set_devicelist()
    
    579 613
     
    
    580 614
         @as_task
    
    ... ... @@ -627,7 +661,7 @@ def _process_devicelist_update(self,
    627 661
                 self._query_for_bundles.remove(jid)
    
    628 662
     
    
    629 663
             if own_devices:
    
    630
    -            if not self.backend.is_own_device_published:
    
    664
    +            if not self.backend.is_own_device_published():
    
    631 665
                     # Our own device_id is not in the list, it could be
    
    632 666
                     # overwritten by some other client
    
    633 667
                     self.set_devicelist(devicelist)
    
    ... ... @@ -638,3 +672,7 @@ def _debug_print_stanza(self, stanza: Any) -> None:
    638 672
             stanzastr = '\n' + stanza.__str__(fancy=True)
    
    639 673
             stanzastr = stanzastr[0:-1]
    
    640 674
             self._log.debug(stanzastr)
    
    675
    +
    
    676
    +    def cleanup(self) -> None:
    
    677
    +        self._backend.destroy()
    
    678
    +        del self._backend

  • gajim/common/omemo/__init__.py deleted

  • gajim/common/omemo/aes.py deleted
    1
    -# This file is part of Gajim.
    
    2
    -#
    
    3
    -# Gajim is free software; you can redistribute it and/or modify
    
    4
    -# it under the terms of the GNU General Public License as published
    
    5
    -# by the Free Software Foundation; version 3 only.
    
    6
    -#
    
    7
    -# Gajim is distributed in the hope that it will be useful,
    
    8
    -# but WITHOUT ANY WARRANTY; without even the implied warranty of
    
    9
    -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    
    10
    -# GNU General Public License for more details.
    
    11
    -#
    
    12
    -# You should have received a copy of the GNU General Public License
    
    13
    -# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
    
    14
    -
    
    15
    -from __future__ import annotations
    
    16
    -
    
    17
    -from typing import NamedTuple
    
    18
    -from typing import Union
    
    19
    -
    
    20
    -import logging
    
    21
    -import os
    
    22
    -
    
    23
    -from cryptography.hazmat.backends import default_backend
    
    24
    -from cryptography.hazmat.primitives.ciphers import algorithms
    
    25
    -from cryptography.hazmat.primitives.ciphers import Cipher
    
    26
    -from cryptography.hazmat.primitives.ciphers.modes import GCM
    
    27
    -
    
    28
    -log = logging.getLogger('gajim.c.omemo.aes')
    
    29
    -
    
    30
    -IV_SIZE = 12
    
    31
    -
    
    32
    -
    
    33
    -class EncryptionResult(NamedTuple):
    
    34
    -    payload: bytes
    
    35
    -    key: bytes
    
    36
    -    iv: bytes
    
    37
    -
    
    38
    -
    
    39
    -def _decrypt(key: bytes, iv: bytes, tag: bytes, data: bytes) -> bytes:
    
    40
    -    decryptor = Cipher(
    
    41
    -        algorithms.AES(key),
    
    42
    -        GCM(iv, tag=tag),
    
    43
    -        backend=default_backend()).decryptor()
    
    44
    -    return decryptor.update(data) + decryptor.finalize()
    
    45
    -
    
    46
    -
    
    47
    -def aes_decrypt(_key: bytes, iv: bytes, payload: bytes) -> str:
    
    48
    -    if len(_key) >= 32:
    
    49
    -        # XEP-0384
    
    50
    -        log.debug('XEP Compliant Key/Tag')
    
    51
    -        data = payload
    
    52
    -        key = _key[:16]
    
    53
    -        tag = _key[16:]
    
    54
    -    else:
    
    55
    -        # Legacy
    
    56
    -        log.debug('Legacy Key/Tag')
    
    57
    -        data = payload[:-16]
    
    58
    -        key = _key
    
    59
    -        tag = payload[-16:]
    
    60
    -
    
    61
    -    return _decrypt(key, iv, tag, data).decode()
    
    62
    -
    
    63
    -
    
    64
    -def aes_decrypt_file(key: bytes, iv: bytes, payload: bytes) -> bytes:
    
    65
    -    data = payload[:-16]
    
    66
    -    tag = payload[-16:]
    
    67
    -    return _decrypt(key, iv, tag, data)
    
    68
    -
    
    69
    -
    
    70
    -def _encrypt(data: Union[str, bytes],
    
    71
    -             key_size: int,
    
    72
    -             iv_size: int = IV_SIZE
    
    73
    -             ) -> tuple[bytes, bytes, bytes, bytes]:
    
    74
    -
    
    75
    -    if isinstance(data, str):
    
    76
    -        data = data.encode()
    
    77
    -    key = os.urandom(key_size)
    
    78
    -    iv = os.urandom(iv_size)
    
    79
    -    encryptor = Cipher(
    
    80
    -        algorithms.AES(key),
    
    81
    -        GCM(iv),
    
    82
    -        backend=default_backend()).encryptor()
    
    83
    -
    
    84
    -    payload = encryptor.update(data) + encryptor.finalize()
    
    85
    -    return key, iv, encryptor.tag, payload
    
    86
    -
    
    87
    -
    
    88
    -def aes_encrypt(plaintext: str) -> EncryptionResult:
    
    89
    -    key, iv, tag, payload = _encrypt(plaintext, 16)
    
    90
    -    key += tag
    
    91
    -    return EncryptionResult(payload=payload, key=key, iv=iv)
    
    92
    -
    
    93
    -
    
    94
    -def aes_encrypt_file(data: bytes) -> EncryptionResult:
    
    95
    -    key, iv, tag, payload, = _encrypt(data, 32)
    
    96
    -    payload += tag
    
    97
    -    return EncryptionResult(payload=payload, key=key, iv=iv)
    
    98
    -
    
    99
    -
    
    100
    -def get_new_key() -> bytes:
    
    101
    -    return os.urandom(16)
    
    102
    -
    
    103
    -
    
    104
    -def get_new_iv() -> bytes:
    
    105
    -    return os.urandom(IV_SIZE)

  • gajim/common/omemo/state.py deleted
    1
    -# Copyright (C) 2019 Philipp Hörist <philipp AT hoerist.com>
    
    2
    -# Copyright (C) 2015 Bahtiar `kalkin-` Gadimov <bahtiar@gadimov.de>
    
    3
    -#
    
    4
    -# This file is part of OMEMO Gajim Plugin.
    
    5
    -#
    
    6
    -# OMEMO Gajim Plugin is free software; you can redistribute it and/or modify
    
    7
    -# it under the terms of the GNU General Public License as published
    
    8
    -# by the Free Software Foundation; version 3 only.
    
    9
    -#
    
    10
    -# OMEMO Gajim Plugin is distributed in the hope that it will be useful,
    
    11
    -# but WITHOUT ANY WARRANTY; without even the implied warranty of
    
    12
    -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    
    13
    -# GNU General Public License for more details.
    
    14
    -#
    
    15
    -# You should have received a copy of the GNU General Public License
    
    16
    -# along with OMEMO Gajim Plugin. If not, see <http://www.gnu.org/licenses/>.
    
    17
    -
    
    18
    -from __future__ import annotations
    
    19
    -
    
    20
    -from typing import Any
    
    21
    -from typing import Optional
    
    22
    -
    
    23
    -import time
    
    24
    -from collections import defaultdict
    
    25
    -from pathlib import Path
    
    26
    -
    
    27
    -from nbxmpp.structs import OMEMOBundle
    
    28
    -from nbxmpp.structs import OMEMOMessage
    
    29
    -from omemo_dr.ecc.djbec import CurvePublicKey
    
    30
    -from omemo_dr.exceptions import DuplicateMessageException
    
    31
    -from omemo_dr.identitykey import IdentityKey
    
    32
    -from omemo_dr.identitykeypair import IdentityKeyPair
    
    33
    -from omemo_dr.protocol.prekeywhispermessage import PreKeyWhisperMessage
    
    34
    -from omemo_dr.protocol.whispermessage import WhisperMessage
    
    35
    -from omemo_dr.sessionbuilder import SessionBuilder
    
    36
    -from omemo_dr.sessioncipher import SessionCipher
    
    37
    -from omemo_dr.state.prekeybundle import PreKeyBundle
    
    38
    -from omemo_dr.util.keyhelper import KeyHelper
    
    39
    -
    
    40
    -from gajim.common import app
    
    41
    -from gajim.common import configpaths
    
    42
    -from gajim.common import types
    
    43
    -from gajim.common.omemo.aes import aes_decrypt
    
    44
    -from gajim.common.omemo.aes import aes_encrypt
    
    45
    -from gajim.common.omemo.aes import get_new_iv
    
    46
    -from gajim.common.omemo.aes import get_new_key
    
    47
    -from gajim.common.omemo.util import DEFAULT_PREKEY_AMOUNT
    
    48
    -from gajim.common.omemo.util import get_fingerprint
    
    49
    -from gajim.common.omemo.util import MIN_PREKEY_AMOUNT
    
    50
    -from gajim.common.omemo.util import SPK_ARCHIVE_TIME
    
    51
    -from gajim.common.omemo.util import SPK_CYCLE_TIME
    
    52
    -from gajim.common.omemo.util import Trust
    
    53
    -from gajim.common.omemo.util import UNACKNOWLEDGED_COUNT
    
    54
    -from gajim.common.storage.omemo import OMEMOStorage
    
    55
    -
    
    56
    -
    
    57
    -class OmemoState:
    
    58
    -    def __init__(self,
    
    59
    -                 account: str,
    
    60
    -                 own_jid: str,
    
    61
    -                 xmpp_con: types.xmppClient
    
    62
    -                 ) -> None:
    
    63
    -
    
    64
    -        self._account = account
    
    65
    -        self._own_jid = own_jid
    
    66
    -        self._log = xmpp_con._log
    
    67
    -        self._session_ciphers: dict[
    
    68
    -            str, dict[int, SessionCipher]] = defaultdict(dict)
    
    69
    -
    
    70
    -        data_dir = Path(configpaths.get('MY_DATA'))
    
    71
    -        db_path = data_dir / f'omemo_{self._own_jid}.db'
    
    72
    -        self._storage = OMEMOStorage(account, db_path, self._log)
    
    73
    -
    
    74
    -        self.xmpp_con = xmpp_con
    
    75
    -
    
    76
    -        self._log.info('%s PreKeys available',
    
    77
    -                       self._storage.getPreKeyCount())
    
    78
    -
    
    79
    -        self._device_store: dict[str, set[int]] = defaultdict(set)
    
    80
    -        self._muc_member_store: dict[str, set[str]] = defaultdict(set)
    
    81
    -
    
    82
    -        reg_id = self._storage.getLocalRegistrationId()
    
    83
    -        if reg_id is None:
    
    84
    -            raise ValueError('No own device found')
    
    85
    -
    
    86
    -        self._own_device = reg_id
    
    87
    -        self.add_device(self._own_jid, self._own_device)
    
    88
    -        self._log.info('Our device id: %s', self._own_device)
    
    89
    -
    
    90
    -        for jid, device in self._storage.getActiveDeviceTuples():
    
    91
    -            self._log.info('Load device from storage: %s - %s', jid, device)
    
    92
    -            self.add_device(jid, device)
    
    93
    -
    
    94
    -    def build_session(self,
    
    95
    -                      jid: str,
    
    96
    -                      device_id: int,
    
    97
    -                      bundle: OMEMOBundle
    
    98
    -                      ) -> None:
    
    99
    -
    
    100
    -        session = SessionBuilder(self._storage, self._storage, self._storage,
    
    101
    -                                 self._storage, jid, device_id)
    
    102
    -
    
    103
    -        registration_id = self._storage.getLocalRegistrationId()
    
    104
    -
    
    105
    -        prekey = bundle.pick_prekey()
    
    106
    -        otpk = CurvePublicKey(prekey['key'][1:])
    
    107
    -
    
    108
    -        spk = CurvePublicKey(bundle.spk['key'][1:])
    
    109
    -        ik = IdentityKey(CurvePublicKey(bundle.ik[1:]))
    
    110
    -
    
    111
    -        prekey_bundle = PreKeyBundle(registration_id,
    
    112
    -                                     device_id,
    
    113
    -                                     prekey['id'],
    
    114
    -                                     otpk,
    
    115
    -                                     bundle.spk['id'],
    
    116
    -                                     spk,
    
    117
    -                                     bundle.spk_signature,
    
    118
    -                                     ik)
    
    119
    -
    
    120
    -        session.processPreKeyBundle(prekey_bundle)
    
    121
    -        self._get_session_cipher(jid, device_id)
    
    122
    -
    
    123
    -    @property
    
    124
    -    def storage(self) -> OMEMOStorage:
    
    125
    -        return self._storage
    
    126
    -
    
    127
    -    @property
    
    128
    -    def own_fingerprint(self) -> str:
    
    129
    -        return get_fingerprint(self._storage.getIdentityKeyPair())
    
    130
    -
    
    131
    -    @property
    
    132
    -    def bundle(self) -> OMEMOBundle:
    
    133
    -        self._check_pre_key_count()
    
    134
    -
    
    135
    -        bundle: dict[str, Any] = {'otpks': []}
    
    136
    -        for k in self._storage.loadPendingPreKeys():
    
    137
    -            key = k.getKeyPair().getPublicKey().serialize()
    
    138
    -            bundle['otpks'].append({'key': key, 'id': k.getId()})
    
    139
    -
    
    140
    -        ik_pair = self._storage.getIdentityKeyPair()
    
    141
    -        bundle['ik'] = ik_pair.getPublicKey().serialize()
    
    142
    -
    
    143
    -        self._cycle_signed_pre_key(ik_pair)
    
    144
    -
    
    145
    -        spk = self._storage.loadSignedPreKey(
    
    146
    -            self._storage.getCurrentSignedPreKeyId())
    
    147
    -        bundle['spk_signature'] = spk.getSignature()
    
    148
    -        bundle['spk'] = {'key': spk.getKeyPair().getPublicKey().serialize(),
    
    149
    -                         'id': spk.getId()}
    
    150
    -
    
    151
    -        return OMEMOBundle(**bundle)
    
    152
    -
    
    153
    -    def decrypt_message(self,
    
    154
    -                        omemo_message: OMEMOMessage,
    
    155
    -                        jid: str
    
    156
    -                        ) -> tuple[str, str, Trust]:
    
    157
    -
    
    158
    -        if omemo_message.sid == self.own_device:
    
    159
    -            self._log.info('Received previously sent message by us')
    
    160
    -            raise SelfMessage
    
    161
    -
    
    162
    -        try:
    
    163
    -            encrypted_key, prekey = omemo_message.keys[self.own_device]
    
    164
    -        except KeyError:
    
    165
    -            self._log.info('Received message not for our device')
    
    166
    -            raise MessageNotForDevice
    
    167
    -
    
    168
    -        try:
    
    169
    -            if prekey:
    
    170
    -                key, fingerprint, trust = self._process_pre_key_message(
    
    171
    -                    jid, omemo_message.sid, encrypted_key)
    
    172
    -            else:
    
    173
    -                key, fingerprint, trust = self._process_message(
    
    174
    -                    jid, omemo_message.sid, encrypted_key)
    
    175
    -
    
    176
    -        except DuplicateMessageException:
    
    177
    -            self._log.info('Received duplicated message')
    
    178
    -            raise DuplicateMessage
    
    179
    -
    
    180
    -        except Exception as error:
    
    181
    -            self._log.warning(error)
    
    182
    -            raise DecryptionFailed
    
    183
    -
    
    184
    -        if omemo_message.payload is None:
    
    185
    -            self._log.debug('Decrypted Key Exchange Message')
    
    186
    -            raise KeyExchangeMessage
    
    187
    -
    
    188
    -        try:
    
    189
    -            result = aes_decrypt(key, omemo_message.iv, omemo_message.payload)
    
    190
    -        except Exception as error:
    
    191
    -            self._log.warning(error)
    
    192
    -            raise DecryptionFailed
    
    193
    -
    
    194
    -        self._log.debug('Decrypted Message => %s', result)
    
    195
    -        return result, fingerprint, trust
    
    196
    -
    
    197
    -    def _get_whisper_message(self,
    
    198
    -                             jid: str,
    
    199
    -                             device: int,
    
    200
    -                             key: bytes
    
    201
    -                             ) -> tuple[bytes, bool]:
    
    202
    -
    
    203
    -        cipher = self._get_session_cipher(jid, device)
    
    204
    -        cipher_key = cipher.encrypt(key)
    
    205
    -        prekey = isinstance(cipher_key, PreKeyWhisperMessage)
    
    206
    -        return cipher_key.serialize(), prekey
    
    207
    -
    
    208
    -    def encrypt(self, jid: str, plaintext: str) -> Optional[OMEMOMessage]:
    
    209
    -        try:
    
    210
    -            devices_for_encryption = self.get_devices_for_encryption(jid)
    
    211
    -        except NoDevicesFound:
    
    212
    -            self._log.warning('No devices for encryption found for: %s', jid)
    
    213
    -            return
    
    214
    -
    
    215
    -        result = aes_encrypt(plaintext)
    
    216
    -        whisper_messages: dict[
    
    217
    -            str, dict[int, tuple[bytes, bool]]] = defaultdict(dict)
    
    218
    -
    
    219
    -        for jid_, device in devices_for_encryption:
    
    220
    -            count = self._storage.getUnacknowledgedCount(jid_, device)
    
    221
    -            if count >= UNACKNOWLEDGED_COUNT:
    
    222
    -                self._log.warning('Set device inactive %s because of %s '
    
    223
    -                                  'unacknowledged messages', device, count)
    
    224
    -                self.remove_device(jid_, device)
    
    225
    -
    
    226
    -            try:
    
    227
    -                whisper_messages[jid_][device] = self._get_whisper_message(
    
    228
    -                    jid_, device, result.key)
    
    229
    -            except Exception:
    
    230
    -                self._log.exception('Failed to encrypt')
    
    231
    -                continue
    
    232
    -
    
    233
    -        recipients = set(whisper_messages.keys())
    
    234
    -        if jid != self._own_jid:
    
    235
    -            recipients -= {self._own_jid}
    
    236
    -        if not recipients:
    
    237
    -            self._log.error('Encrypted keys empty')
    
    238
    -            return
    
    239
    -
    
    240
    -        encrypted_keys: dict[int, tuple[bytes, bool]] = {}
    
    241
    -        for jid_ in whisper_messages:
    
    242
    -            encrypted_keys.update(whisper_messages[jid_])
    
    243
    -
    
    244
    -        self._log.debug('Finished encrypting message')
    
    245
    -        return OMEMOMessage(sid=self.own_device,
    
    246
    -                            keys=encrypted_keys,
    
    247
    -                            iv=result.iv,
    
    248
    -                            payload=result.payload)
    
    249
    -
    
    250
    -    def encrypt_key_transport(self,
    
    251
    -                              jid: str,
    
    252
    -                              devices: list[int]
    
    253
    -                              ) -> Optional[OMEMOMessage]:
    
    254
    -
    
    255
    -        whisper_messages: dict[
    
    256
    -            str, dict[int, tuple[bytes, bool]]] = defaultdict(dict)
    
    257
    -        for device in devices:
    
    258
    -            try:
    
    259
    -                whisper_messages[jid][device] = self._get_whisper_message(
    
    260
    -                    jid, device, get_new_key())
    
    261
    -            except Exception:
    
    262
    -                self._log.exception('Failed to encrypt')
    
    263
    -                continue
    
    264
    -
    
    265
    -        if not whisper_messages[jid]:
    
    266
    -            self._log.error('Encrypted keys empty')
    
    267
    -            return
    
    268
    -
    
    269
    -        self._log.debug('Finished Key Transport message')
    
    270
    -        return OMEMOMessage(sid=self.own_device,
    
    271
    -                            keys=whisper_messages[jid],
    
    272
    -                            iv=get_new_iv(),
    
    273
    -                            payload=None)
    
    274
    -
    
    275
    -    def has_trusted_keys(self, jid: str) -> bool:
    
    276
    -        inactive = self._storage.getInactiveSessionsKeys(jid)
    
    277
    -        trusted = self._storage.getTrustedFingerprints(jid)
    
    278
    -        return bool(set(trusted) - set(inactive))
    
    279
    -
    
    280
    -    def devices_without_sessions(self, jid: str) -> list[int]:
    
    281
    -        known_devices = self.get_devices(jid, without_self=True)
    
    282
    -        missing_devices = [dev
    
    283
    -                           for dev in known_devices
    
    284
    -                           if not self._storage.containsSession(jid, dev)]
    
    285
    -        if missing_devices:
    
    286
    -            self._log.info('Missing device sessions for %s: %s',
    
    287
    -                           jid, missing_devices)
    
    288
    -        return missing_devices
    
    289
    -
    
    290
    -    def _get_session_cipher(self, jid: str, device_id: int) -> SessionCipher:
    
    291
    -        try:
    
    292
    -            return self._session_ciphers[jid][device_id]
    
    293
    -        except KeyError:
    
    294
    -            cipher = SessionCipher(self._storage, self._storage, self._storage,
    
    295
    -                                   self._storage, jid, device_id)
    
    296
    -            self._session_ciphers[jid][device_id] = cipher
    
    297
    -            return cipher
    
    298
    -
    
    299
    -    def _process_pre_key_message(self,
    
    300
    -                                 jid: str,
    
    301
    -                                 device: int,
    
    302
    -                                 key: bytes
    
    303
    -                                 ) -> tuple[bytes, str, Trust]:
    
    304
    -
    
    305
    -        self._log.info('Process pre key message from %s', jid)
    
    306
    -        pre_key_message = PreKeyWhisperMessage.from_bytes(key)
    
    307
    -        if not pre_key_message.getPreKeyId():
    
    308
    -            raise Exception('Received Pre Key Message '
    
    309
    -                            'without PreKey => %s' % jid)
    
    310
    -
    
    311
    -        session_cipher = self._get_session_cipher(jid, device)
    
    312
    -        key = session_cipher.decryptPkmsg(pre_key_message)
    
    313
    -
    
    314
    -        identity_key = pre_key_message.getIdentityKey()
    
    315
    -        trust = self._get_trust_from_identity_key(jid, identity_key)
    
    316
    -        fingerprint = get_fingerprint(identity_key)
    
    317
    -
    
    318
    -        self._storage.setIdentityLastSeen(jid, identity_key)
    
    319
    -
    
    320
    -        self.xmpp_con.set_bundle()
    
    321
    -        self.add_device(jid, device)
    
    322
    -        return key, fingerprint, trust
    
    323
    -
    
    324
    -    def _process_message(self,
    
    325
    -                         jid: str,
    
    326
    -                         device: int,
    
    327
    -                         key: bytes
    
    328
    -                         ) -> tuple[bytes, str, Trust]:
    
    329
    -
    
    330
    -        self._log.info('Process message from %s', jid)
    
    331
    -        message = WhisperMessage.from_bytes(key)
    
    332
    -
    
    333
    -        session_cipher = self._get_session_cipher(jid, device)
    
    334
    -        key = session_cipher.decryptMsg(message)
    
    335
    -
    
    336
    -        identity_key = self._get_identity_key_from_device(jid, device)
    
    337
    -        trust = self._get_trust_from_identity_key(jid, identity_key)
    
    338
    -        fingerprint = get_fingerprint(identity_key)
    
    339
    -
    
    340
    -        self._storage.setIdentityLastSeen(jid, identity_key)
    
    341
    -
    
    342
    -        self.add_device(jid, device)
    
    343
    -
    
    344
    -        return key, fingerprint, trust
    
    345
    -
    
    346
    -    @staticmethod
    
    347
    -    def _get_identity_key_from_pk_message(key):
    
    348
    -        pre_key_message = PreKeyWhisperMessage.from_bytes(key)
    
    349
    -        return pre_key_message.getIdentityKey()
    
    350
    -
    
    351
    -    def _get_identity_key_from_device(self,
    
    352
    -                                      jid: str,
    
    353
    -                                      device: int
    
    354
    -                                      ) -> Optional[IdentityKey]:
    
    355
    -
    
    356
    -        session_record = self._storage.loadSession(jid, device)
    
    357
    -        return session_record.getSessionState().getRemoteIdentityKey()
    
    358
    -
    
    359
    -    def _get_trust_from_identity_key(self,
    
    360
    -                                     jid: str,
    
    361
    -                                     identity_key: IdentityKey
    
    362
    -                                     ) -> Trust:
    
    363
    -
    
    364
    -        trust = self._storage.getTrustForIdentity(jid, identity_key)
    
    365
    -        return Trust(trust) if trust is not None else Trust.UNDECIDED
    
    366
    -
    
    367
    -    def _check_pre_key_count(self) -> None:
    
    368
    -        # Check if enough PreKeys are available
    
    369
    -        pre_key_count = self._storage.getPreKeyCount()
    
    370
    -        if pre_key_count < MIN_PREKEY_AMOUNT:
    
    371
    -            missing_count = DEFAULT_PREKEY_AMOUNT - pre_key_count
    
    372
    -            self._storage.generateNewPreKeys(missing_count)
    
    373
    -            self._log.info('%s PreKeys created', missing_count)
    
    374
    -
    
    375
    -    def _cycle_signed_pre_key(self, ik_pair: IdentityKeyPair) -> None:
    
    376
    -        # Publish every SPK_CYCLE_TIME a new SignedPreKey
    
    377
    -        # Delete all existing SignedPreKeys that are older
    
    378
    -        # then SPK_ARCHIVE_TIME
    
    379
    -
    
    380
    -        # Check if SignedPreKey exist and create if not
    
    381
    -        if not self._storage.getCurrentSignedPreKeyId():
    
    382
    -            spk = KeyHelper.generateSignedPreKey(
    
    383
    -                ik_pair, self._storage.getNextSignedPreKeyId())
    
    384
    -            self._storage.storeSignedPreKey(spk.getId(), spk)
    
    385
    -            self._log.debug('New SignedPreKey created, because none existed')
    
    386
    -
    
    387
    -        # if SPK_CYCLE_TIME is reached, generate a new SignedPreKey
    
    388
    -        now = int(time.time())
    
    389
    -        timestamp = self._storage.getSignedPreKeyTimestamp(
    
    390
    -            self._storage.getCurrentSignedPreKeyId())
    
    391
    -
    
    392
    -        if int(timestamp) < now - SPK_CYCLE_TIME:
    
    393
    -            spk = KeyHelper.generateSignedPreKey(
    
    394
    -                ik_pair, self._storage.getNextSignedPreKeyId())
    
    395
    -            self._storage.storeSignedPreKey(spk.getId(), spk)
    
    396
    -            self._log.debug('Cycled SignedPreKey')
    
    397
    -
    
    398
    -        # Delete all SignedPreKeys that are older than SPK_ARCHIVE_TIME
    
    399
    -        timestamp = now - SPK_ARCHIVE_TIME
    
    400
    -        self._storage.removeOldSignedPreKeys(timestamp)
    
    401
    -
    
    402
    -    def update_devicelist(self, jid: str, devicelist: list[int]) -> None:
    
    403
    -        for device in list(devicelist):
    
    404
    -            if device == self.own_device:
    
    405
    -                continue
    
    406
    -            count = self._storage.getUnacknowledgedCount(jid, device)
    
    407
    -            if count > UNACKNOWLEDGED_COUNT:
    
    408
    -                self._log.warning('Ignore device because of %s unacknowledged'
    
    409
    -                                  ' messages: %s %s', count, jid, device)
    
    410
    -                devicelist.remove(device)
    
    411
    -
    
    412
    -        self._device_store[jid] = set(devicelist)
    
    413
    -        self._log.info('Saved devices for %s', jid)
    
    414
    -        self._storage.setActiveState(jid, devicelist)
    
    415
    -
    
    416
    -    def add_muc_member(self, room_jid: str, jid: str) -> None:
    
    417
    -        self._log.info('Saved MUC member %s %s', room_jid, jid)
    
    418
    -        self._muc_member_store[room_jid].add(jid)
    
    419
    -
    
    420
    -    def remove_muc_member(self, room_jid: str, jid: str) -> None:
    
    421
    -        self._log.info('Removed MUC member %s %s', room_jid, jid)
    
    422
    -        self._muc_member_store[room_jid].discard(jid)
    
    423
    -
    
    424
    -    def get_muc_members(self,
    
    425
    -                        room_jid: str,
    
    426
    -                        without_self: bool = True
    
    427
    -                        ) -> set[str]:
    
    428
    -
    
    429
    -        members = set(self._muc_member_store[room_jid])
    
    430
    -        if without_self:
    
    431
    -            members.discard(self._own_jid)
    
    432
    -        return members
    
    433
    -
    
    434
    -    def add_device(self, jid: str, device: int) -> None:
    
    435
    -        self._device_store[jid].add(device)
    
    436
    -
    
    437
    -    def remove_device(self, jid: str, device: int) -> None:
    
    438
    -        self._device_store[jid].discard(device)
    
    439
    -        self._storage.setInactive(jid, device)
    
    440
    -
    
    441
    -    def get_devices(self, jid: str, without_self: bool = False) -> set[int]:
    
    442
    -        devices = set(self._device_store[jid])
    
    443
    -        if without_self:
    
    444
    -            devices.discard(self.own_device)
    
    445
    -        return devices
    
    446
    -
    
    447
    -    def get_devices_for_encryption(self, jid: str) -> set[tuple[str, int]]:
    
    448
    -        devices_for_encryption: list[tuple[str, int]] = []
    
    449
    -
    
    450
    -        client = app.get_client(self._account)
    
    451
    -        contact = client.get_module('Contacts').get_contact(jid)
    
    452
    -        if contact.is_groupchat:
    
    453
    -            devices_for_encryption = self._get_devices_for_muc_encryption(jid)
    
    454
    -        else:
    
    455
    -            devices_for_encryption = self._get_devices_for_encryption(jid)
    
    456
    -
    
    457
    -        if not devices_for_encryption:
    
    458
    -            raise NoDevicesFound
    
    459
    -
    
    460
    -        devices_for_encryption += self._get_own_devices_for_encryption()
    
    461
    -        return set(devices_for_encryption)
    
    462
    -
    
    463
    -    def _get_devices_for_muc_encryption(self,
    
    464
    -                                        jid: str
    
    465
    -                                        ) -> list[tuple[str, int]]:
    
    466
    -
    
    467
    -        devices_for_encryption: list[tuple[str, int]] = []
    
    468
    -        for jid_ in self._muc_member_store[jid]:
    
    469
    -            devices_for_encryption += self._get_devices_for_encryption(jid_)
    
    470
    -        return devices_for_encryption
    
    471
    -
    
    472
    -    def _get_own_devices_for_encryption(self) -> list[tuple[str, int]]:
    
    473
    -        devices_for_encryption: list[tuple[str, int]] = []
    
    474
    -        own_devices = self.get_devices(self._own_jid, without_self=True)
    
    475
    -        for device in own_devices:
    
    476
    -            if self._storage.isTrusted(self._own_jid, device):
    
    477
    -                devices_for_encryption.append((self._own_jid, device))
    
    478
    -        return devices_for_encryption
    
    479
    -
    
    480
    -    def _get_devices_for_encryption(self, jid: str) -> list[tuple[str, int]]:
    
    481
    -        devices_for_encryption: list[tuple[str, int]] = []
    
    482
    -        devices = self.get_devices(jid)
    
    483
    -
    
    484
    -        for device in devices:
    
    485
    -            if self._storage.isTrusted(jid, device):
    
    486
    -                devices_for_encryption.append((jid, device))
    
    487
    -        return devices_for_encryption
    
    488
    -
    
    489
    -    @property
    
    490
    -    def own_device(self) -> int:
    
    491
    -        return self._own_device
    
    492
    -
    
    493
    -    @property
    
    494
    -    def devices_for_publish(self) -> set[int]:
    
    495
    -        devices = self.get_devices(self._own_jid)
    
    496
    -        if self.own_device not in devices:
    
    497
    -            devices.add(self.own_device)
    
    498
    -        return devices
    
    499
    -
    
    500
    -    @property
    
    501
    -    def is_own_device_published(self) -> bool:
    
    502
    -        return self.own_device in self.get_devices(self._own_jid)
    
    503
    -
    
    504
    -
    
    505
    -class NoDevicesFound(Exception):
    
    506
    -    pass
    
    507
    -
    
    508
    -
    
    509
    -class NoValidSessions(Exception):
    
    510
    -    pass
    
    511
    -
    
    512
    -
    
    513
    -class SelfMessage(Exception):
    
    514
    -    pass
    
    515
    -
    
    516
    -
    
    517
    -class MessageNotForDevice(Exception):
    
    518
    -    pass
    
    519
    -
    
    520
    -
    
    521
    -class DecryptionFailed(Exception):
    
    522
    -    pass
    
    523
    -
    
    524
    -
    
    525
    -class KeyExchangeMessage(Exception):
    
    526
    -    pass
    
    527
    -
    
    528
    -
    
    529
    -class InvalidMessage(Exception):
    
    530
    -    pass
    
    531
    -
    
    532
    -
    
    533
    -class DuplicateMessage(Exception):
    
    534
    -    pass

  • gajim/common/omemo/util.py deleted
    1
    -# Copyright (C) 2015 Bahtiar `kalkin-` Gadimov <bahtiar@gadimov.de>
    
    2
    -#
    
    3
    -# This file is part of OMEMO Gajim Plugin.
    
    4
    -#
    
    5
    -# OMEMO Gajim Plugin is free software; you can redistribute it and/or modify
    
    6
    -# it under the terms of the GNU General Public License as published
    
    7
    -# by the Free Software Foundation; version 3 only.
    
    8
    -#
    
    9
    -# OMEMO Gajim Plugin is distributed in the hope that it will be useful,
    
    10
    -# but WITHOUT ANY WARRANTY; without even the implied warranty of
    
    11
    -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    
    12
    -# GNU General Public License for more details.
    
    13
    -#
    
    14
    -# You should have received a copy of the GNU General Public License
    
    15
    -# along with OMEMO Gajim Plugin. If not, see <http://www.gnu.org/licenses/>.
    
    16
    -
    
    17
    -from __future__ import annotations
    
    18
    -
    
    19
    -import binascii
    
    20
    -import textwrap
    
    21
    -from enum import IntEnum
    
    22
    -
    
    23
    -from omemo_dr.identitykey import IdentityKey
    
    24
    -from omemo_dr.identitykeypair import IdentityKeyPair
    
    25
    -
    
    26
    -DEFAULT_PREKEY_AMOUNT = 100
    
    27
    -MIN_PREKEY_AMOUNT = 80
    
    28
    -SPK_ARCHIVE_TIME = 86400 * 15  # 15 Days
    
    29
    -SPK_CYCLE_TIME = 86400         # 24 Hours
    
    30
    -UNACKNOWLEDGED_COUNT = 2000
    
    31
    -
    
    32
    -
    
    33
    -class Trust(IntEnum):
    
    34
    -    UNTRUSTED = 0
    
    35
    -    VERIFIED = 1
    
    36
    -    UNDECIDED = 2
    
    37
    -    BLIND = 3
    
    38
    -
    
    39
    -
    
    40
    -def get_fingerprint(identity_key: IdentityKeyPair,
    
    41
    -                    formatted: bool = False
    
    42
    -                    ) -> str:
    
    43
    -
    
    44
    -    public_key = identity_key.getPublicKey().serialize()
    
    45
    -    fingerprint = binascii.hexlify(public_key).decode()[2:]
    
    46
    -    if not formatted:
    
    47
    -        return fingerprint
    
    48
    -    fplen = len(fingerprint)
    
    49
    -    wordsize = fplen // 8
    
    50
    -    buf = ''
    
    51
    -    for word in range(0, fplen, wordsize):
    
    52
    -        buf += '{0} '.format(fingerprint[word:word + wordsize])
    
    53
    -    buf = textwrap.fill(buf, width=36)
    
    54
    -    return buf.rstrip().upper()
    
    55
    -
    
    56
    -
    
    57
    -class IdentityKeyExtended(IdentityKey):
    
    58
    -    def __hash__(self) -> int:
    
    59
    -        return hash(self.publicKey.serialize())
    
    60
    -
    
    61
    -    def get_fingerprint(self, formatted: bool = False) -> str:
    
    62
    -        return get_fingerprint(self, formatted=formatted)

  • gajim/common/storage/omemo.py
    ... ... @@ -26,29 +26,26 @@
    26 26
     from collections import namedtuple
    
    27 27
     from pathlib import Path
    
    28 28
     
    
    29
    +from omemo_dr.const import OMEMOTrust
    
    29 30
     from omemo_dr.ecc.djbec import CurvePublicKey
    
    30 31
     from omemo_dr.ecc.djbec import DjbECPrivateKey
    
    31 32
     from omemo_dr.exceptions import InvalidKeyIdException
    
    32 33
     from omemo_dr.identitykey import IdentityKey
    
    33 34
     from omemo_dr.identitykeypair import IdentityKeyPair
    
    34
    -from omemo_dr.state.axolotlstore import AxolotlStore
    
    35 35
     from omemo_dr.state.prekeyrecord import PreKeyRecord
    
    36 36
     from omemo_dr.state.sessionrecord import SessionRecord
    
    37 37
     from omemo_dr.state.signedprekeyrecord import SignedPreKeyRecord
    
    38
    -from omemo_dr.util.keyhelper import KeyHelper
    
    38
    +from omemo_dr.state.store import Store
    
    39 39
     from omemo_dr.util.medium import Medium
    
    40 40
     
    
    41 41
     from gajim.common import app
    
    42 42
     from gajim.common.modules.util import LogAdapter
    
    43
    -from gajim.common.omemo.util import DEFAULT_PREKEY_AMOUNT
    
    44
    -from gajim.common.omemo.util import IdentityKeyExtended
    
    45
    -from gajim.common.omemo.util import Trust
    
    46 43
     
    
    47 44
     
    
    48
    -def _convert_identity_key(key: bytes) -> Optional[IdentityKeyExtended]:
    
    45
    +def _convert_identity_key(key: bytes) -> Optional[IdentityKey]:
    
    49 46
         if not key:
    
    50 47
             return
    
    51
    -    return IdentityKeyExtended(CurvePublicKey(key[1:]))
    
    48
    +    return IdentityKey(CurvePublicKey(key[1:]))
    
    52 49
     
    
    53 50
     
    
    54 51
     def _convert_record(record: bytes) -> SessionRecord:
    
    ... ... @@ -59,15 +56,15 @@ def _convert_record(record: bytes) -> SessionRecord:
    59 56
     sqlite3.register_converter('session_record', _convert_record)
    
    60 57
     
    
    61 58
     
    
    62
    -class OMEMOStorage(AxolotlStore):
    
    59
    +class OMEMOStorage(Store):
    
    63 60
         def __init__(self, account: str, db_path: Path, log: LogAdapter) -> None:
    
    64 61
             self._log = log
    
    65 62
             self._account = account
    
    66 63
             self._con = sqlite3.connect(db_path,
    
    67 64
                                         detect_types=sqlite3.PARSE_COLNAMES)
    
    68 65
             self._con.row_factory = self._namedtuple_factory
    
    69
    -        self.createDb()
    
    70
    -        self.migrateDb()
    
    66
    +        self.create_db()
    
    67
    +        self.migrate_db()
    
    71 68
     
    
    72 69
             self._con.execute('PRAGMA secure_delete=1')
    
    73 70
             self._con.execute('PRAGMA synchronous=NORMAL;')
    
    ... ... @@ -78,10 +75,6 @@ def __init__(self, account: str, db_path: Path, log: LogAdapter) -> None:
    78 75
                 self._con.execute('PRAGMA journal_mode=MEMORY;')
    
    79 76
             self._con.commit()
    
    80 77
     
    
    81
    -        if not self.getLocalRegistrationId():
    
    82
    -            self._log.info('Generating OMEMO keys')
    
    83
    -            self._generate_axolotl_keys()
    
    84
    -
    
    85 78
         def _is_blind_trust_enabled(self) -> bool:
    
    86 79
             return app.settings.get_account_setting(self._account,
    
    87 80
                                                     'omemo_blind_trust')
    
    ... ... @@ -105,26 +98,10 @@ def _namedtuple_factory(cursor: sqlite3.Cursor,
    105 98
                     fields.append(col[0])
    
    106 99
             return namedtuple('Row', fields)(*row)  # pyright: ignore
    
    107 100
     
    
    108
    -    def _generate_axolotl_keys(self) -> None:
    
    109
    -        identity_key_pair = KeyHelper.generateIdentityKeyPair()
    
    110
    -        registration_id = KeyHelper.getRandomSequence(2147483647)
    
    111
    -        pre_keys = KeyHelper.generatePreKeys(
    
    112
    -            KeyHelper.getRandomSequence(4294967296),
    
    113
    -            DEFAULT_PREKEY_AMOUNT)
    
    114
    -        self.storeLocalData(registration_id, identity_key_pair)
    
    115
    -
    
    116
    -        signed_pre_key = KeyHelper.generateSignedPreKey(
    
    117
    -            identity_key_pair, KeyHelper.getRandomSequence(65536))
    
    118
    -
    
    119
    -        self.storeSignedPreKey(signed_pre_key.getId(), signed_pre_key)
    
    120
    -
    
    121
    -        for pre_key in pre_keys:
    
    122
    -            self.storePreKey(pre_key.getId(), pre_key)
    
    123
    -
    
    124 101
         def user_version(self) -> int:
    
    125 102
             return self._con.execute('PRAGMA user_version').fetchone()[0]
    
    126 103
     
    
    127
    -    def createDb(self) -> None:
    
    104
    +    def create_db(self) -> None:
    
    128 105
             if self.user_version() == 0:
    
    129 106
     
    
    130 107
                 create_tables = '''
    
    ... ... @@ -166,7 +143,7 @@ def createDb(self) -> None:
    166 143
                     ''' % (create_tables)
    
    167 144
                 self._con.executescript(create_db_sql)
    
    168 145
     
    
    169
    -    def migrateDb(self) -> None:
    
    146
    +    def migrate_db(self) -> None:
    
    170 147
             ''' Migrates the DB
    
    171 148
             '''
    
    172 149
     
    
    ... ... @@ -384,131 +361,130 @@ def migrateDb(self) -> None:
    384 361
                 self._con.execute('PRAGMA user_version=12')
    
    385 362
                 self._con.commit()
    
    386 363
     
    
    387
    -    def loadSignedPreKey(self, signedPreKeyId: int) -> SignedPreKeyRecord:
    
    364
    +    def load_signed_pre_key(self, signed_pre_key_id: int) -> SignedPreKeyRecord:
    
    388 365
             query = 'SELECT record FROM signed_prekeys WHERE prekey_id = ?'
    
    389
    -        result = self._con.execute(query, (signedPreKeyId, )).fetchone()
    
    366
    +        result = self._con.execute(query, (signed_pre_key_id, )).fetchone()
    
    390 367
             if result is None:
    
    391 368
                 raise InvalidKeyIdException('No such signedprekeyrecord! %s ' %
    
    392
    -                                        signedPreKeyId)
    
    369
    +                                        signed_pre_key_id)
    
    393 370
             return SignedPreKeyRecord.from_bytes(result.record)
    
    394 371
     
    
    395
    -    def loadSignedPreKeys(self) -> list[SignedPreKeyRecord]:
    
    372
    +    def load_signed_pre_keys(self) -> list[SignedPreKeyRecord]:
    
    396 373
             query = 'SELECT record FROM signed_prekeys'
    
    397 374
             results = self._con.execute(query).fetchall()
    
    398 375
             return [SignedPreKeyRecord.from_bytes(row.record) for row in results]
    
    399 376
     
    
    400
    -    def storeSignedPreKey(self,
    
    401
    -                          signedPreKeyId: int,
    
    402
    -                          signedPreKeyRecord: SignedPreKeyRecord
    
    403
    -                          ) -> None:
    
    377
    +    def store_signed_pre_key(self,
    
    378
    +                             signed_pre_key_id: int,
    
    379
    +                             signed_pre_key_record: SignedPreKeyRecord
    
    380
    +                             ) -> None:
    
    404 381
     
    
    405 382
             query = 'INSERT INTO signed_prekeys (prekey_id, record) VALUES(?,?)'
    
    406
    -        self._con.execute(query, (signedPreKeyId,
    
    407
    -                                  signedPreKeyRecord.serialize()))
    
    383
    +        self._con.execute(query, (signed_pre_key_id,
    
    384
    +                                  signed_pre_key_record.serialize()))
    
    408 385
             self._con.commit()
    
    409 386
     
    
    410
    -    def containsSignedPreKey(self, signedPreKeyId: int) -> bool:
    
    387
    +    def contains_signed_pre_key(self, signed_pre_key_id: int) -> bool:
    
    411 388
             query = 'SELECT record FROM signed_prekeys WHERE prekey_id = ?'
    
    412
    -        result = self._con.execute(query, (signedPreKeyId,)).fetchone()
    
    389
    +        result = self._con.execute(query, (signed_pre_key_id,)).fetchone()
    
    413 390
             return result is not None
    
    414 391
     
    
    415
    -    def removeSignedPreKey(self, signedPreKeyId: int) -> None:
    
    392
    +    def remove_signed_pre_key(self, signed_pre_key_id: int) -> None:
    
    416 393
             query = 'DELETE FROM signed_prekeys WHERE prekey_id = ?'
    
    417
    -        self._con.execute(query, (signedPreKeyId,))
    
    394
    +        self._con.execute(query, (signed_pre_key_id,))
    
    418 395
             self._con.commit()
    
    419 396
     
    
    420
    -    def getNextSignedPreKeyId(self) -> int:
    
    421
    -        result = self.getCurrentSignedPreKeyId()
    
    422
    -        if result is None:
    
    423
    -            return 1  # StartId if no SignedPreKeys exist
    
    397
    +    def get_next_signed_pre_key_id(self) -> int:
    
    398
    +        result = self.get_current_signed_pre_key_id()
    
    424 399
             return (result % (Medium.MAX_VALUE - 1)) + 1
    
    425 400
     
    
    426
    -    def getCurrentSignedPreKeyId(self) -> Optional[int]:
    
    401
    +    def get_current_signed_pre_key_id(self) -> int:
    
    427 402
             query = 'SELECT MAX(prekey_id) FROM signed_prekeys'
    
    428 403
             result = self._con.execute(query).fetchone()
    
    429
    -        return result.max_prekey_id if result is not None else None
    
    404
    +        assert result is not None
    
    405
    +        return result.max_prekey_id
    
    430 406
     
    
    431
    -    def getSignedPreKeyTimestamp(self, signedPreKeyId: int) -> int:
    
    407
    +    def get_signed_pre_key_timestamp(self, signed_pre_key_id: int) -> int:
    
    432 408
             query = '''SELECT strftime('%s', timestamp) FROM
    
    433 409
                        signed_prekeys WHERE prekey_id = ?'''
    
    434 410
     
    
    435
    -        result = self._con.execute(query, (signedPreKeyId,)).fetchone()
    
    411
    +        result = self._con.execute(query, (signed_pre_key_id,)).fetchone()
    
    436 412
             if result is None:
    
    437 413
                 raise InvalidKeyIdException('No such signedprekeyrecord! %s' %
    
    438
    -                                        signedPreKeyId)
    
    414
    +                                        signed_pre_key_id)
    
    439 415
     
    
    440 416
             return result.formated_time
    
    441 417
     
    
    442
    -    def removeOldSignedPreKeys(self, timestamp: int) -> None:
    
    418
    +    def remove_old_signed_pre_keys(self, timestamp: int) -> None:
    
    443 419
             query = '''DELETE FROM signed_prekeys
    
    444 420
                        WHERE timestamp < datetime(?, "unixepoch")'''
    
    445 421
             self._con.execute(query, (timestamp,))
    
    446 422
             self._con.commit()
    
    447 423
     
    
    448
    -    def loadSession(self, recipientId: str, deviceId: int) -> SessionRecord:
    
    424
    +    def load_session(self, recipient_id: str, device_id: int) -> SessionRecord:
    
    449 425
             query = '''SELECT record as "record [session_record]"
    
    450 426
                        FROM sessions WHERE recipient_id = ? AND device_id = ?'''
    
    451
    -        result = self._con.execute(query, (recipientId, deviceId)).fetchone()
    
    427
    +        result = self._con.execute(query, (recipient_id, device_id)).fetchone()
    
    452 428
             return result.record if result is not None else SessionRecord()
    
    453 429
     
    
    454
    -    def getJidFromDevice(self, device_id: int) -> Optional[str]:
    
    430
    +    def get_jid_from_device(self, device_id: int) -> Optional[str]:
    
    455 431
             query = '''SELECT recipient_id
    
    456 432
                        FROM sessions WHERE device_id = ?'''
    
    457 433
             result = self._con.execute(query, (device_id, )).fetchone()
    
    458 434
             return result.recipient_id if result is not None else None
    
    459 435
     
    
    460
    -    def getActiveDeviceTuples(self):
    
    436
    +    def get_active_device_tuples(self):
    
    461 437
             query = '''SELECT recipient_id, device_id
    
    462 438
                        FROM sessions WHERE active = 1'''
    
    463 439
             return self._con.execute(query).fetchall()
    
    464 440
     
    
    465
    -    def storeSession(self,
    
    466
    -                     recipientId: str,
    
    467
    -                     deviceId: int,
    
    468
    -                     sessionRecord: SessionRecord
    
    469
    -                     ) -> None:
    
    441
    +    def store_session(self,
    
    442
    +                      recipient_id: str,
    
    443
    +                      device_id: int,
    
    444
    +                      session_record: SessionRecord
    
    445
    +                      ) -> None:
    
    470 446
     
    
    471 447
             query = '''INSERT INTO sessions(recipient_id, device_id, record)
    
    472 448
                        VALUES(?,?,?)'''
    
    473 449
             try:
    
    474
    -            self._con.execute(query, (recipientId,
    
    475
    -                                      deviceId,
    
    476
    -                                      sessionRecord.serialize()))
    
    450
    +            self._con.execute(query, (recipient_id,
    
    451
    +                                      device_id,
    
    452
    +                                      session_record.serialize()))
    
    477 453
             except sqlite3.IntegrityError:
    
    478 454
                 query = '''UPDATE sessions SET record = ?
    
    479 455
                            WHERE recipient_id = ? AND device_id = ?'''
    
    480
    -            self._con.execute(query, (sessionRecord.serialize(),
    
    481
    -                                      recipientId,
    
    482
    -                                      deviceId))
    
    456
    +            self._con.execute(query, (session_record.serialize(),
    
    457
    +                                      recipient_id,
    
    458
    +                                      device_id))
    
    483 459
     
    
    484 460
             self._con.commit()
    
    485 461
     
    
    486
    -    def containsSession(self, recipientId: str, deviceId: int) -> bool:
    
    462
    +    def contains_session(self, recipient_id: str, device_id: int) -> bool:
    
    487 463
             query = '''SELECT record FROM sessions
    
    488 464
                        WHERE recipient_id = ? AND device_id = ?'''
    
    489
    -        result = self._con.execute(query, (recipientId, deviceId)).fetchone()
    
    465
    +        result = self._con.execute(query, (recipient_id, device_id)).fetchone()
    
    490 466
             return result is not None
    
    491 467
     
    
    492
    -    def deleteSession(self, recipientId: str, deviceId: int) -> None:
    
    493
    -        self._log.info('Delete session for %s %s', recipientId, deviceId)
    
    468
    +    def delete_session(self, recipient_id: str, device_id: int) -> None:
    
    469
    +        self._log.info('Delete session for %s %s', recipient_id, device_id)
    
    494 470
             query = 'DELETE FROM sessions WHERE recipient_id = ? AND device_id = ?'
    
    495
    -        self._con.execute(query, (recipientId, deviceId))
    
    471
    +        self._con.execute(query, (recipient_id, device_id))
    
    496 472
             self._con.commit()
    
    497 473
     
    
    498
    -    def deleteAllSessions(self, recipientId: str) -> None:
    
    474
    +    def delete_all_sessions(self, recipient_id: str) -> None:
    
    499 475
             query = 'DELETE FROM sessions WHERE recipient_id = ?'
    
    500
    -        self._con.execute(query, (recipientId,))
    
    476
    +        self._con.execute(query, (recipient_id,))
    
    501 477
             self._con.commit()
    
    502 478
     
    
    503
    -    def getSessionsFromJid(self, recipientId: str):
    
    479
    +    def get_sessions_from_jid(self, recipient_id: str):
    
    504 480
             query = '''SELECT recipient_id,
    
    505 481
                               device_id,
    
    506 482
                               record as "record [session_record]",
    
    507 483
                               active
    
    508 484
                        FROM sessions WHERE recipient_id = ?'''
    
    509
    -        return self._con.execute(query, (recipientId,)).fetchall()
    
    485
    +        return self._con.execute(query, (recipient_id,)).fetchall()
    
    510 486
     
    
    511
    -    def getSessionsFromJids(self, recipientIds: list[str]):
    
    487
    +    def get_sessions_from_jids(self, recipient_ids: list[str]):
    
    512 488
             query = '''
    
    513 489
             SELECT recipient_id,
    
    514 490
                    device_id,
    
    ... ... @@ -516,86 +492,84 @@ def getSessionsFromJids(self, recipientIds: list[str]):
    516 492
                    active
    
    517 493
             FROM sessions
    
    518 494
             WHERE recipient_id IN ({})'''.format(
    
    519
    -            ', '.join(['?'] * len(recipientIds)))
    
    520
    -        return self._con.execute(query, recipientIds).fetchall()
    
    495
    +            ', '.join(['?'] * len(recipient_ids)))
    
    496
    +        return self._con.execute(query, recipient_ids).fetchall()
    
    521 497
     
    
    522
    -    def setActiveState(self, jid: str, devicelist: list[int]) -> None:
    
    498
    +    def set_active_state(self, address: str, devicelist: list[int]) -> None:
    
    523 499
             query = '''
    
    524 500
             UPDATE sessions SET active = 1
    
    525 501
             WHERE recipient_id = ? AND device_id IN ({})'''.format(
    
    526 502
                 ', '.join(['?'] * len(devicelist)))
    
    527
    -        self._con.execute(query, (jid,) + tuple(devicelist))
    
    503
    +        self._con.execute(query, (address,) + tuple(devicelist))
    
    528 504
     
    
    529 505
             query = '''
    
    530 506
             UPDATE sessions SET active = 0
    
    531 507
             WHERE recipient_id = ? AND device_id NOT IN ({})'''.format(
    
    532 508
                 ', '.join(['?'] * len(devicelist)))
    
    533
    -        self._con.execute(query, (jid,) + tuple(devicelist))
    
    509
    +        self._con.execute(query, (address,) + tuple(devicelist))
    
    534 510
             self._con.commit()
    
    535 511
     
    
    536
    -    def setInactive(self, jid: str, device_id: int) -> None:
    
    512
    +    def set_inactive(self, address: str, device_id: int) -> None:
    
    537 513
             query = '''UPDATE sessions SET active = 0
    
    538 514
                        WHERE recipient_id = ? AND device_id = ?'''
    
    539
    -        self._con.execute(query, (jid, device_id))
    
    515
    +        self._con.execute(query, (address, device_id))
    
    540 516
             self._con.commit()
    
    541 517
     
    
    542
    -    def getInactiveSessionsKeys(self,
    
    543
    -                                recipientId: str
    
    544
    -                                ) -> list[IdentityKeyExtended]:
    
    518
    +    def get_inactive_sessions_keys(self,
    
    519
    +                                   recipient_id: str
    
    520
    +                                   ) -> list[IdentityKey]:
    
    545 521
     
    
    546 522
             query = '''SELECT record as "record [session_record]" FROM sessions
    
    547 523
                        WHERE active = 0 AND recipient_id = ?'''
    
    548
    -        results = self._con.execute(query, (recipientId,)).fetchall()
    
    524
    +        results = self._con.execute(query, (recipient_id,)).fetchall()
    
    549 525
     
    
    550
    -        keys: list[IdentityKeyExtended] = []
    
    526
    +        keys: list[IdentityKey] = []
    
    551 527
             for result in results:
    
    552
    -            key = result.record.getSessionState().getRemoteIdentityKey()
    
    553
    -            keys.append(IdentityKeyExtended(key.getPublicKey()))
    
    528
    +            key = result.record.get_session_state().get_remote_identity_key()
    
    529
    +            keys.append(IdentityKey(key.get_public_key()))
    
    554 530
             return keys
    
    555 531
     
    
    556
    -    def loadPreKey(self, preKeyId: int) -> PreKeyRecord:
    
    532
    +    def load_pre_key(self, pre_key_id: int) -> PreKeyRecord:
    
    557 533
             query = '''SELECT record FROM prekeys WHERE prekey_id = ?'''
    
    558 534
     
    
    559
    -        result = self._con.execute(query, (preKeyId,)).fetchone()
    
    535
    +        result = self._con.execute(query, (pre_key_id,)).fetchone()
    
    560 536
             if result is None:
    
    561 537
                 raise Exception('No such prekeyRecord!')
    
    562 538
             return PreKeyRecord.from_bytes(result.record)
    
    563 539
     
    
    564
    -    def loadPendingPreKeys(self) -> list[PreKeyRecord]:
    
    540
    +    def load_pending_pre_keys(self) -> list[PreKeyRecord]:
    
    565 541
             query = '''SELECT record FROM prekeys'''
    
    566 542
             result = self._con.execute(query).fetchall()
    
    567 543
             return [PreKeyRecord.from_bytes(row.record) for row in result]
    
    568 544
     
    
    569
    -    def storePreKey(self, preKeyId: int, preKeyRecord: PreKeyRecord) -> None:
    
    545
    +    def store_pre_key(self,
    
    546
    +                      pre_key_id: int,
    
    547
    +                      pre_key_record: PreKeyRecord
    
    548
    +                      ) -> None:
    
    570 549
             query = 'INSERT INTO prekeys (prekey_id, record) VALUES(?,?)'
    
    571
    -        self._con.execute(query, (preKeyId, preKeyRecord.serialize()))
    
    550
    +        self._con.execute(query, (pre_key_id, pre_key_record.serialize()))
    
    572 551
             self._con.commit()
    
    573 552
     
    
    574
    -    def containsPreKey(self, preKeyId: int) -> bool:
    
    553
    +    def contains_pre_key(self, pre_key_id: int) -> bool:
    
    575 554
             query = 'SELECT record FROM prekeys WHERE prekey_id = ?'
    
    576
    -        result = self._con.execute(query, (preKeyId,)).fetchone()
    
    555
    +        result = self._con.execute(query, (pre_key_id,)).fetchone()
    
    577 556
             return result is not None
    
    578 557
     
    
    579
    -    def removePreKey(self, preKeyId: int) -> None:
    
    558
    +    def remove_pre_key(self, pre_key_id: int) -> None:
    
    580 559
             query = 'DELETE FROM prekeys WHERE prekey_id = ?'
    
    581
    -        self._con.execute(query, (preKeyId,))
    
    560
    +        self._con.execute(query, (pre_key_id,))
    
    582 561
             self._con.commit()
    
    583 562
     
    
    584
    -    def getCurrentPreKeyId(self) -> int:
    
    563
    +    def get_current_pre_key_id(self) -> Optional[int]:
    
    585 564
             query = 'SELECT MAX(prekey_id) FROM prekeys'
    
    586
    -        return self._con.execute(query).fetchone().max_prekey_id
    
    565
    +        result = self._con.execute(query).fetchone()
    
    566
    +        return result.max_prekey_id if result is not None else None
    
    587 567
     
    
    588
    -    def getPreKeyCount(self) -> int:
    
    568
    +    def get_pre_key_count(self) -> int:
    
    589 569
             query = 'SELECT COUNT(prekey_id) FROM prekeys'
    
    590 570
             return self._con.execute(query).fetchone().count_prekey_id
    
    591 571
     
    
    592
    -    def generateNewPreKeys(self, count: int) -> None:
    
    593
    -        prekey_id = self.getCurrentPreKeyId() or 0
    
    594
    -        pre_keys = KeyHelper.generatePreKeys(prekey_id + 1, count)
    
    595
    -        for pre_key in pre_keys:
    
    596
    -            self.storePreKey(pre_key.getId(), pre_key)
    
    597
    -
    
    598
    -    def getIdentityKeyPair(self) -> IdentityKeyPair:
    
    572
    +    def get_identity_key_pair(self) -> IdentityKeyPair:
    
    599 573
             query = '''SELECT public_key as "public_key [pk]", private_key
    
    600 574
                        FROM secret LIMIT 1'''
    
    601 575
             result = self._con.execute(query).fetchone()
    
    ... ... @@ -603,15 +577,16 @@ def getIdentityKeyPair(self) -> IdentityKeyPair:
    603 577
             return IdentityKeyPair.new(result.public_key,
    
    604 578
                                        DjbECPrivateKey(result.private_key))
    
    605 579
     
    
    606
    -    def getLocalRegistrationId(self) -> Optional[int]:  # pyright: ignore
    
    580
    +    def get_local_registration_id(self) -> int:
    
    607 581
             query = 'SELECT device_id FROM secret LIMIT 1'
    
    608 582
             result = self._con.execute(query).fetchone()
    
    609
    -        return result.device_id if result is not None else None
    
    583
    +        assert result is not None
    
    584
    +        return result.device_id
    
    610 585
     
    
    611
    -    def storeLocalData(self,
    
    612
    -                       device_id: int,
    
    613
    -                       identityKeyPair: IdentityKeyPair
    
    614
    -                       ) -> None:
    
    586
    +    def store_own_identity(self,
    
    587
    +                           device_id: int,
    
    588
    +                           identity_key_pair: IdentityKeyPair
    
    589
    +                           ) -> None:
    
    615 590
     
    
    616 591
             query = 'SELECT * FROM secret'
    
    617 592
             result = self._con.execute(query).fetchone()
    
    ... ... @@ -623,67 +598,71 @@ def storeLocalData(self,
    623 598
             query = '''INSERT INTO secret(device_id, public_key, private_key)
    
    624 599
                        VALUES(?, ?, ?)'''
    
    625 600
     
    
    626
    -        public_key = identityKeyPair.getPublicKey().getPublicKey().serialize()
    
    627
    -        private_key = identityKeyPair.getPrivateKey().serialize()
    
    601
    +        public_key = identity_key_pair.get_public_key().get_public_key().\
    
    602
    +            serialize()
    
    603
    +        private_key = identity_key_pair.get_private_key().serialize()
    
    628 604
             self._con.execute(query, (device_id, public_key, private_key))
    
    629 605
             self._con.commit()
    
    630 606
     
    
    631
    -    def saveIdentity(self, recipientId: str, identityKey: IdentityKey) -> None:
    
    607
    +    def save_identity(self,
    
    608
    +                      recipient_id: str,
    
    609
    +                      identity_key: IdentityKey
    
    610
    +                      ) -> None:
    
    632 611
             query = '''INSERT INTO identities
    
    633 612
                        (recipient_id, public_key, trust, shown)
    
    634 613
                        VALUES(?, ?, ?, ?)'''
    
    635
    -        if not self.containsIdentity(recipientId, identityKey):
    
    636
    -            trust = self.getDefaultTrust(recipientId)
    
    637
    -            self._con.execute(query, (recipientId,
    
    638
    -                                      identityKey.getPublicKey().serialize(),
    
    614
    +        if not self.contains_identity(recipient_id, identity_key):
    
    615
    +            trust = self.get_default_trust(recipient_id)
    
    616
    +            self._con.execute(query, (recipient_id,
    
    617
    +                                      identity_key.get_public_key().serialize(),
    
    639 618
                                           trust,
    
    640
    -                                      1 if trust == Trust.BLIND else 0))
    
    619
    +                                      1 if trust == OMEMOTrust.BLIND else 0))
    
    641 620
                 self._con.commit()
    
    642 621
     
    
    643
    -    def containsIdentity(self,
    
    644
    -                         recipientId: str,
    
    645
    -                         identityKey: IdentityKey
    
    646
    -                         ) -> bool:
    
    622
    +    def contains_identity(self,
    
    623
    +                          recipient_id: str,
    
    624
    +                          identity_key: IdentityKey
    
    625
    +                          ) -> bool:
    
    647 626
     
    
    648 627
             query = '''SELECT * FROM identities WHERE recipient_id = ?
    
    649 628
                        AND public_key = ?'''
    
    650 629
     
    
    651
    -        public_key = identityKey.getPublicKey().serialize()
    
    652
    -        result = self._con.execute(query, (recipientId,
    
    630
    +        public_key = identity_key.get_public_key().serialize()
    
    631
    +        result = self._con.execute(query, (recipient_id,
    
    653 632
                                                public_key)).fetchone()
    
    654 633
     
    
    655 634
             return result is not None
    
    656 635
     
    
    657
    -    def deleteIdentity(self,
    
    658
    -                       recipientId: str,
    
    659
    -                       identityKey: IdentityKey
    
    660
    -                       ) -> None:
    
    636
    +    def delete_identity(self,
    
    637
    +                        recipient_id: str,
    
    638
    +                        identity_key: IdentityKey
    
    639
    +                        ) -> None:
    
    661 640
     
    
    662 641
             query = '''DELETE FROM identities
    
    663 642
                        WHERE recipient_id = ? AND public_key = ?'''
    
    664
    -        public_key = identityKey.getPublicKey().serialize()
    
    665
    -        self._con.execute(query, (recipientId, public_key))
    
    643
    +        public_key = identity_key.get_public_key().serialize()
    
    644
    +        self._con.execute(query, (recipient_id, public_key))
    
    666 645
             self._con.commit()
    
    667 646
     
    
    668
    -    def isTrustedIdentity(self,
    
    669
    -                          recipientId: str,
    
    670
    -                          identityKey: IdentityKey
    
    671
    -                          ) -> bool:
    
    647
    +    def is_trusted_identity(self,
    
    648
    +                            recipient_id: str,
    
    649
    +                            identity_key: IdentityKey
    
    650
    +                            ) -> bool:
    
    672 651
     
    
    673 652
             return True
    
    674 653
     
    
    675
    -    def getTrustForIdentity(self,
    
    676
    -                            recipientId: str,
    
    677
    -                            identityKey: IdentityKey
    
    678
    -                            ) -> Optional[Trust]:
    
    654
    +    def get_trust_for_identity(self,
    
    655
    +                               recipient_id: str,
    
    656
    +                               identity_key: IdentityKey
    
    657
    +                               ) -> Optional[OMEMOTrust]:
    
    679 658
     
    
    680 659
             query = '''SELECT trust FROM identities WHERE recipient_id = ?
    
    681 660
                        AND public_key = ?'''
    
    682
    -        public_key = identityKey.getPublicKey().serialize()
    
    683
    -        result = self._con.execute(query, (recipientId, public_key)).fetchone()
    
    661
    +        public_key = identity_key.get_public_key().serialize()
    
    662
    +        result = self._con.execute(query, (recipient_id, public_key)).fetchone()
    
    684 663
             return result.trust if result is not None else None
    
    685 664
     
    
    686
    -    def getFingerprints(self, jid: str):
    
    665
    +    def get_fingerprints(self, jid: str):
    
    687 666
             query = '''SELECT recipient_id,
    
    688 667
                               public_key as "public_key [pk]",
    
    689 668
                               trust,
    
    ... ... @@ -692,7 +671,7 @@ def getFingerprints(self, jid: str):
    692 671
                        WHERE recipient_id = ? ORDER BY trust ASC'''
    
    693 672
             return self._con.execute(query, (jid,)).fetchall()
    
    694 673
     
    
    695
    -    def getMucFingerprints(self, jids: list[str]):
    
    674
    +    def get_muc_fingerprints(self, jids: list[str]):
    
    696 675
             query = '''
    
    697 676
                 SELECT recipient_id,
    
    698 677
                        public_key as "public_key [pk]",
    
    ... ... @@ -704,98 +683,106 @@ def getMucFingerprints(self, jids: list[str]):
    704 683
     
    
    705 684
             return self._con.execute(query, jids).fetchall()
    
    706 685
     
    
    707
    -    def hasUndecidedFingerprints(self, jid: str) -> bool:
    
    686
    +    def has_undecided_fingerprints(self, jid: str) -> bool:
    
    708 687
             query = '''SELECT public_key as "public_key [pk]" FROM identities
    
    709 688
                        WHERE recipient_id = ? AND trust = ?'''
    
    710
    -        result = self._con.execute(query, (jid, Trust.UNDECIDED)).fetchall()
    
    689
    +        result = self._con.execute(query,
    
    690
    +                                   (jid, OMEMOTrust.UNDECIDED)).fetchall()
    
    711 691
             undecided = [row.public_key for row in result]
    
    712 692
     
    
    713
    -        inactive = self.getInactiveSessionsKeys(jid)
    
    693
    +        inactive = self.get_inactive_sessions_keys(jid)
    
    714 694
             undecided = set(undecided) - set(inactive)
    
    715 695
             return bool(undecided)
    
    716 696
     
    
    717
    -    def getDefaultTrust(self, jid: str) -> Trust:
    
    697
    +    def get_default_trust(self, jid: str) -> OMEMOTrust:
    
    718 698
             if not self._is_blind_trust_enabled():
    
    719
    -            return Trust.UNDECIDED
    
    699
    +            return OMEMOTrust.UNDECIDED
    
    720 700
     
    
    721 701
             query = '''SELECT * FROM identities
    
    722 702
                        WHERE recipient_id = ? AND trust IN (0, 1)'''
    
    723 703
             result = self._con.execute(query, (jid,)).fetchone()
    
    724 704
             if result is None:
    
    725
    -            return Trust.BLIND
    
    726
    -        return Trust.UNDECIDED
    
    705
    +            return OMEMOTrust.BLIND
    
    706
    +        return OMEMOTrust.UNDECIDED
    
    727 707
     
    
    728
    -    def getTrustedFingerprints(self, jid: str) -> list[IdentityKeyExtended]:
    
    708
    +    def get_trusted_fingerprints(self, address: str) -> list[IdentityKey]:
    
    729 709
             query = '''SELECT public_key as "public_key [pk]" FROM identities
    
    730 710
                        WHERE recipient_id = ? AND trust IN(1, 3)'''
    
    731
    -        result = self._con.execute(query, (jid,)).fetchall()
    
    711
    +        result = self._con.execute(query, (address,)).fetchall()
    
    732 712
             return [row.public_key for row in result]
    
    733 713
     
    
    734
    -    def getNewFingerprints(self, jid: str) -> list[int]:
    
    714
    +    def get_new_fingerprints(self, jid: str) -> list[int]:
    
    735 715
             query = '''SELECT _id FROM identities WHERE shown = 0
    
    736 716
                        AND recipient_id = ?'''
    
    737 717
     
    
    738 718
             result = self._con.execute(query, (jid,)).fetchall()
    
    739 719
             return [row.id for row in result]
    
    740 720
     
    
    741
    -    def setShownFingerprints(self, fingerprints: list[int]) -> None:
    
    721
    +    def set_shown_fingerprints(self, fingerprints: list[int]) -> None:
    
    742 722
             query = 'UPDATE identities SET shown = 1 WHERE _id IN ({})'.format(
    
    743 723
                 ', '.join(['?'] * len(fingerprints)))
    
    744 724
             self._con.execute(query, fingerprints)
    
    745 725
             self._con.commit()
    
    746 726
     
    
    747
    -    def setTrust(self,
    
    748
    -                 recipient_id: str,
    
    749
    -                 identityKey: IdentityKey,
    
    750
    -                 trust: Trust
    
    751
    -                 ) -> None:
    
    727
    +    def set_trust(self,
    
    728
    +                  recipient_id: str,
    
    729
    +                  identity_key: IdentityKey,
    
    730
    +                  trust: OMEMOTrust
    
    731
    +                  ) -> None:
    
    752 732
     
    
    753 733
             query = '''UPDATE identities SET trust = ? WHERE public_key = ?
    
    754 734
                        AND recipient_id = ?'''
    
    755
    -        public_key = identityKey.getPublicKey().serialize()
    
    735
    +        public_key = identity_key.get_public_key().serialize()
    
    756 736
             self._con.execute(query, (trust, public_key, recipient_id))
    
    757 737
             self._con.commit()
    
    758 738
     
    
    759
    -    def isTrusted(self, recipient_id: str, device_id: int) -> bool:
    
    760
    -        record = self.loadSession(recipient_id, device_id)
    
    761
    -        if record.isFresh():
    
    739
    +    def is_trusted(self, recipient_id: str, device_id: int) -> bool:
    
    740
    +        record = self.load_session(recipient_id, device_id)
    
    741
    +        if record.is_fresh():
    
    762 742
                 return False
    
    763
    -        identity_key = record.getSessionState().getRemoteIdentityKey()
    
    764
    -        return self.getTrustForIdentity(
    
    765
    -            recipient_id, identity_key) in (Trust.VERIFIED, Trust.BLIND)
    
    743
    +        identity_key = record.get_session_state().get_remote_identity_key()
    
    744
    +        return self.get_trust_for_identity(
    
    745
    +            recipient_id, identity_key) in (OMEMOTrust.VERIFIED,
    
    746
    +                                            OMEMOTrust.BLIND)
    
    766 747
     
    
    767
    -    def getIdentityLastSeen(self,
    
    768
    -                            recipient_id: str,
    
    769
    -                            identity_key: IdentityKey
    
    770
    -                            ) -> Optional[int]:
    
    748
    +    def get_identity_last_seen(self,
    
    749
    +                               recipient_id: str,
    
    750
    +                               identity_key: IdentityKey
    
    751
    +                               ) -> Optional[int]:
    
    771 752
     
    
    772
    -        serialized = identity_key.getPublicKey().serialize()
    
    753
    +        serialized = identity_key.get_public_key().serialize()
    
    773 754
             query = '''SELECT timestamp FROM identities
    
    774 755
                        WHERE recipient_id = ? AND public_key = ?'''
    
    775 756
             result = self._con.execute(query, (recipient_id,
    
    776 757
                                                serialized)).fetchone()
    
    777 758
             return result.timestamp if result is not None else None
    
    778 759
     
    
    779
    -    def setIdentityLastSeen(self,
    
    780
    -                            recipient_id: str,
    
    781
    -                            identity_key: IdentityKey
    
    782
    -                            ) -> None:
    
    760
    +    def set_identity_last_seen(self,
    
    761
    +                               recipient_id: str,
    
    762
    +                               identity_key: IdentityKey
    
    763
    +                               ) -> None:
    
    783 764
     
    
    784 765
             timestamp = int(time.time())
    
    785
    -        serialized = identity_key.getPublicKey().serialize()
    
    766
    +        serialized = identity_key.get_public_key().serialize()
    
    786 767
             self._log.info('Set last seen for %s %s', recipient_id, timestamp)
    
    787 768
             query = '''UPDATE identities SET timestamp = ?
    
    788 769
                        WHERE recipient_id = ? AND public_key = ?'''
    
    789 770
             self._con.execute(query, (timestamp, recipient_id, serialized))
    
    790 771
             self._con.commit()
    
    791 772
     
    
    792
    -    def getUnacknowledgedCount(self, recipient_id: str, device_id: int) -> int:
    
    793
    -        record = self.loadSession(recipient_id, device_id)
    
    794
    -        if record.isFresh():
    
    773
    +    def get_unacknowledged_count(self,
    
    774
    +                                 recipient_id: str,
    
    775
    +                                 device_id: int
    
    776
    +                                 ) -> int:
    
    777
    +        record = self.load_session(recipient_id, device_id)
    
    778
    +        if record.is_fresh():
    
    795 779
                 return 0
    
    796
    -        state = record.getSessionState()
    
    797
    -        return state.getSenderChainKey().getIndex()
    
    780
    +        state = record.get_session_state()
    
    781
    +        return state.get_sender_chain_key().get_index()
    
    798 782
     
    
    799
    -    def getSubDeviceSessions(self, recipientId: str) -> list[int]:
    
    800
    -        # Not used
    
    801
    -        return []
    783
    +    def needs_init(self) -> bool:
    
    784
    +        try:
    
    785
    +            self.get_local_registration_id()
    
    786
    +        except AssertionError:
    
    787
    +            return True
    
    788
    +        return False

  • gajim/gtk/omemo_trust_manager.py
    ... ... @@ -28,7 +28,8 @@
    28 28
     from gi.repository import GdkPixbuf
    
    29 29
     from gi.repository import Gtk
    
    30 30
     from nbxmpp.protocol import JID
    
    31
    -from omemo_dr.identitykeypair import IdentityKeyPair
    
    31
    +from omemo_dr.const import OMEMOTrust
    
    32
    +from omemo_dr.identitykey import IdentityKey
    
    32 33
     
    
    33 34
     from gajim.common import app
    
    34 35
     from gajim.common import ged
    
    ... ... @@ -40,9 +41,6 @@
    40 41
     from gajim.common.helpers import generate_qr_code
    
    41 42
     from gajim.common.i18n import _
    
    42 43
     from gajim.common.modules.contacts import BareContact
    
    43
    -from gajim.common.omemo.util import get_fingerprint
    
    44
    -from gajim.common.omemo.util import IdentityKeyExtended
    
    45
    -from gajim.common.omemo.util import Trust
    
    46 44
     
    
    47 45
     from .builder import get_builder
    
    48 46
     from .dialogs import ConfirmationDialog
    
    ... ... @@ -53,16 +51,16 @@
    53 51
     
    
    54 52
     
    
    55 53
     TRUST_DATA = {
    
    56
    -    Trust.UNTRUSTED: ('dialog-error-symbolic',
    
    54
    +    OMEMOTrust.UNTRUSTED: ('dialog-error-symbolic',
    
    57 55
                           _('Untrusted'),
    
    58 56
                           'error-color'),
    
    59
    -    Trust.UNDECIDED: ('security-low-symbolic',
    
    57
    +    OMEMOTrust.UNDECIDED: ('security-low-symbolic',
    
    60 58
                           _('Not Decided'),
    
    61 59
                           'warning-color'),
    
    62
    -    Trust.VERIFIED: ('security-high-symbolic',
    
    60
    +    OMEMOTrust.VERIFIED: ('security-high-symbolic',
    
    63 61
                          _('Verified'),
    
    64 62
                          'encrypted-color'),
    
    65
    -    Trust.BLIND: ('security-medium-symbolic',
    
    63
    +    OMEMOTrust.BLIND: ('security-medium-symbolic',
    
    66 64
                       _('Blind Trust'),
    
    67 65
                       'encrypted-color')
    
    68 66
     }
    
    ... ... @@ -125,9 +123,8 @@ def _update(self) -> None:
    125 123
     
    
    126 124
             self._omemo = client.get_module('OMEMO')
    
    127 125
     
    
    128
    -        self._identity_key = cast(
    
    129
    -            IdentityKeyPair, self._omemo.backend.storage.getIdentityKeyPair())
    
    130
    -        our_fpr_formatted = get_fingerprint(self._identity_key, formatted=True)
    
    126
    +        our_fpr_formatted = self._omemo.backend.get_own_fingerprint(
    
    127
    +            formatted=True)
    
    131 128
             self._ui.our_fingerprint_1.set_text(our_fpr_formatted)
    
    132 129
             self._ui.our_fingerprint_2.set_text(our_fpr_formatted)
    
    133 130
     
    
    ... ... @@ -198,15 +195,16 @@ def _load_fingerprints(self, contact: types.ChatContactT) -> None:
    198 195
             if contact.is_groupchat:
    
    199 196
                 members = list(self._omemo.backend.get_muc_members(
    
    200 197
                     str(contact.jid)))
    
    201
    -            sessions = self._omemo.backend.storage.getSessionsFromJids(members)
    
    202
    -            results = self._omemo.backend.storage.getMucFingerprints(members)
    
    198
    +            sessions = self._omemo.backend.storage.get_sessions_from_jids(
    
    199
    +                members)
    
    200
    +            results = self._omemo.backend.storage.get_muc_fingerprints(members)
    
    203 201
             else:
    
    204
    -            sessions = self._omemo.backend.storage.getSessionsFromJid(
    
    202
    +            sessions = self._omemo.backend.storage.get_sessions_from_jid(
    
    205 203
                     str(contact.jid))
    
    206
    -            results = self._omemo.backend.storage.getFingerprints(
    
    204
    +            results = self._omemo.backend.storage.get_fingerprints(
    
    207 205
                     str(contact.jid))
    
    208 206
     
    
    209
    -        rows: dict[IdentityKeyExtended, KeyRow] = {}
    
    207
    +        rows: dict[IdentityKey, KeyRow] = {}
    
    210 208
             for result in results:
    
    211 209
                 rows[result.public_key] = KeyRow(contact,
    
    212 210
                                                  result.recipient_id,
    
    ... ... @@ -215,16 +213,17 @@ def _load_fingerprints(self, contact: types.ChatContactT) -> None:
    215 213
                                                  result.timestamp)
    
    216 214
     
    
    217 215
             for item in sessions:
    
    218
    -            if item.record.isFresh():
    
    216
    +            if item.record.is_fresh():
    
    219 217
                     return
    
    220
    -            identity_key = item.record.getSessionState().getRemoteIdentityKey()
    
    221
    -            identity_key = IdentityKeyExtended(identity_key.getPublicKey())
    
    218
    +            identity_key = item.record.get_session_state().\
    
    219
    +                get_remote_identity_key()
    
    220
    +            identity_key = IdentityKey(identity_key.get_public_key())
    
    222 221
                 try:
    
    223 222
                     key_row = rows[identity_key]
    
    224 223
                 except KeyError:
    
    225 224
                     log.warning('Could not find session identitykey %s',
    
    226 225
                                 item.device_id)
    
    227
    -                self._omemo.backend.storage.deleteSession(item.recipient_id,
    
    226
    +                self._omemo.backend.storage.delete_session(item.recipient_id,
    
    228 227
                                                               item.device_id)
    
    229 228
                     continue
    
    230 229
     
    
    ... ... @@ -237,10 +236,9 @@ def _load_fingerprints(self, contact: types.ChatContactT) -> None:
    237 236
         @staticmethod
    
    238 237
         def _get_qrcode(jid: JID,
    
    239 238
                         sid: int,
    
    240
    -                    identity_key: IdentityKeyPair
    
    239
    +                    fingerprint: str
    
    241 240
                         ) -> GdkPixbuf.Pixbuf | None:
    
    242 241
     
    
    243
    -        fingerprint = get_fingerprint(identity_key)
    
    244 242
             qry = (XmppUriQuery.MESSAGE.value, [(f'omemo-sid-{sid}', fingerprint)])
    
    245 243
             ver_string = jid.new_as_bare().to_iri(qry)
    
    246 244
             log.debug('Verification String: %s', ver_string)
    
    ... ... @@ -249,8 +247,8 @@ def _get_qrcode(jid: JID,
    249 247
         def _load_qrcode(self) -> None:
    
    250 248
             client = app.get_client(self._account)
    
    251 249
             pixbuf = self._get_qrcode(client.get_own_jid(),
    
    252
    -                                  self._omemo.backend.own_device,
    
    253
    -                                  self._identity_key)
    
    250
    +                                  self._omemo.backend.get_own_device(),
    
    251
    +                                  self._omemo.backend.get_own_fingerprint())
    
    254 252
             self._ui.qr_code_image.set_from_pixbuf(pixbuf)
    
    255 253
     
    
    256 254
         def _on_show_inactive(self, switch: Gtk.Switch, _param: Any) -> None:
    
    ... ... @@ -280,8 +278,8 @@ class KeyRow(Gtk.ListBoxRow):
    280 278
         def __init__(self,
    
    281 279
                      contact: types.ChatContactT,
    
    282 280
                      jid: JID,
    
    283
    -                 identity_key: IdentityKeyExtended,
    
    284
    -                 trust: Trust,
    
    281
    +                 identity_key: IdentityKey,
    
    282
    +                 trust: OMEMOTrust,
    
    285 283
                      last_seen: Optional[float]
    
    286 284
                      ) -> None:
    
    287 285
     
    
    ... ... @@ -347,9 +345,9 @@ def delete_fingerprint(self, *args: Any) -> None:
    347 345
     
    
    348 346
             def _remove():
    
    349 347
                 self._omemo.backend.remove_device(str(self.jid), self.device_id)
    
    350
    -            self._omemo.backend.storage.deleteSession(
    
    348
    +            self._omemo.backend.storage.delete_session(
    
    351 349
                     str(self.jid), self.device_id)
    
    352
    -            self._omemo.backend.storage.deleteIdentity(
    
    350
    +            self._omemo.backend.storage.delete_identity(
    
    353 351
                     str(self.jid), self._identity_key)
    
    354 352
     
    
    355 353
                 listbox = cast(Gtk.ListBox, self.get_parent())
    
    ... ... @@ -373,7 +371,7 @@ def set_trust(self) -> None:
    373 371
             image.get_style_context().add_class(css_class)
    
    374 372
             image.set_tooltip_text(tooltip)
    
    375 373
     
    
    376
    -        self._omemo.backend.storage.setTrust(
    
    374
    +        self._omemo.backend.storage.set_trust(
    
    377 375
                 str(self.jid), self._identity_key, self.trust)
    
    378 376
     
    
    379 377
         @property
    
    ... ... @@ -454,11 +452,11 @@ def _activated(self, _listbox: Gtk.ListBox, row: MenuOption) -> None:
    454 452
     
    
    455 453
         def update(self) -> None:
    
    456 454
             self._listbox.foreach(self._listbox.remove)
    
    457
    -        if self._row.trust != Trust.VERIFIED:
    
    455
    +        if self._row.trust != OMEMOTrust.VERIFIED:
    
    458 456
                 self._listbox.add(VerifiedOption())
    
    459
    -        if self._row.trust != Trust.BLIND:
    
    457
    +        if self._row.trust != OMEMOTrust.BLIND:
    
    460 458
                 self._listbox.add(BlindOption())
    
    461
    -        if self._row.trust != Trust.UNTRUSTED:
    
    459
    +        if self._row.trust != OMEMOTrust.UNTRUSTED:
    
    462 460
                 self._listbox.add(NotTrustedOption())
    
    463 461
             self._listbox.add(DeleteOption())
    
    464 462
     
    
    ... ... @@ -468,7 +466,7 @@ def __init__(self,
    468 466
                      icon: str,
    
    469 467
                      label_text: str,
    
    470 468
                      color: str,
    
    471
    -                 type_: Optional[Trust] = None
    
    469
    +                 type_: Optional[OMEMOTrust] = None
    
    472 470
                      ) -> None:
    
    473 471
     
    
    474 472
             Gtk.ListBoxRow.__init__(self)
    
    ... ... @@ -497,7 +495,7 @@ def __init__(self) -> None:
    497 495
                                 'security-medium-symbolic',
    
    498 496
                                 _('Blind Trust'),
    
    499 497
                                 'encrypted-color',
    
    500
    -                            Trust.BLIND)
    
    498
    +                            OMEMOTrust.BLIND)
    
    501 499
     
    
    502 500
     
    
    503 501
     class VerifiedOption(MenuOption):
    
    ... ... @@ -506,7 +504,7 @@ def __init__(self) -> None:
    506 504
                                 'security-high-symbolic',
    
    507 505
                                 _('Verified'),
    
    508 506
                                 'encrypted-color',
    
    509
    -                            Trust.VERIFIED)
    
    507
    +                            OMEMOTrust.VERIFIED)
    
    510 508
     
    
    511 509
     
    
    512 510
     class NotTrustedOption(MenuOption):
    
    ... ... @@ -515,7 +513,7 @@ def __init__(self) -> None:
    515 513
                                 'dialog-error-symbolic',
    
    516 514
                                 _('Untrusted'),
    
    517 515
                                 'error-color',
    
    518
    -                            Trust.UNTRUSTED)
    
    516
    +                            OMEMOTrust.UNTRUSTED)
    
    519 517
     
    
    520 518
     
    
    521 519
     class DeleteOption(MenuOption):
    

  • pyproject.toml
    ... ... @@ -35,7 +35,7 @@ dependencies = [
    35 35
       "pycairo>=1.16.0",
    
    36 36
       "PyGObject>=3.42.0",
    
    37 37
       "qrcode>=7.3.1",
    
    38
    -  "omemo-dr>=0.99.0,<2.0.0",
    
    38
    +  "omemo-dr>=0.99.1,<2.0.0",
    
    39 39
       "winsdk ; platform_system == 'Windows'",
    
    40 40
     ]
    
    41 41
     dynamic = ["version"]