diff options
author | Philipp Hörist <philipp@hoerist.com> | 2023-05-01 23:05:36 +0300 |
---|---|---|
committer | Philipp Hörist <philipp@hoerist.com> | 2023-05-03 23:19:54 +0300 |
commit | 9b8b1839d1a5398c46925b1b299ad04177f82de7 (patch) | |
tree | 7270ec2a1698e960a500010b63405bd66d74bb9b | |
parent | f8ba3f06ec9d4b695403ecd0c5a0f5fc48d888c2 (diff) |
refactor: Adapt to omemo-dr changes
- Rename method and variables to snake case
- Let omemo-dr generate the keys
- Add needs_init()
- Fix return types for some storage methods
- Remove obsolete database methods
-rw-r--r-- | debian/control | 4 | ||||
-rw-r--r-- | gajim/common/modules/omemo.py | 61 | ||||
-rw-r--r-- | gajim/common/storage/omemo.py | 357 | ||||
-rw-r--r-- | gajim/gtk/omemo_trust_manager.py | 44 | ||||
-rw-r--r-- | pyproject.toml | 2 |
5 files changed, 236 insertions, 232 deletions
diff --git a/debian/control b/debian/control index d27066e1f..c5cec30c3 100644 --- a/debian/control +++ b/debian/control @@ -13,7 +13,7 @@ Build-Depends: python3-gi, python3-gi-cairo, python3-nbxmpp-nightly (>=20230405), - python3-omemo-dr (>=20230407), + python3-omemo-dr (>=20230503), python3-setuptools, python3-packaging, python3-cryptography (>=3.4.8), @@ -36,7 +36,7 @@ Depends: python3-gi (>= 3.42.0), python3-gi-cairo (>= 1.14.0~), python3-nbxmpp-nightly (>=20230405), - python3-omemo-dr (>=20230407), + python3-omemo-dr (>=20230503), gir1.2-pango-1.0 (>= 1.50.0), gir1.2-gtk-3.0 (>= 3.24.30), gir1.2-gtksource-4, diff --git a/gajim/common/modules/omemo.py b/gajim/common/modules/omemo.py index 1cf0ffd2c..7c6778ed1 100644 --- a/gajim/common/modules/omemo.py +++ b/gajim/common/modules/omemo.py @@ -51,6 +51,8 @@ from omemo_dr.exceptions import KeyExchangeMessage from omemo_dr.exceptions import MessageNotForDevice from omemo_dr.exceptions import SelfMessage from omemo_dr.session import OMEMOSession +from omemo_dr.structs import OMEMOBundle +from omemo_dr.structs import OMEMOConfig from gajim.common import app from gajim.common import configpaths @@ -130,7 +132,15 @@ class OMEMO(BaseModule): db_path = data_dir / f'omemo_{self._own_jid}.db' storage = OMEMOStorage(self._account, db_path, self._log) - self._backend = OMEMOSession(self._own_jid, storage, self) + omemo_config = OMEMOConfig(default_prekey_amount=100, + min_prekey_amount=80, + spk_archive_seconds=86400 * 15, + spk_cycle_seconds=86400, + unacknowledged_count=2000) + + self._backend = OMEMOSession(self._own_jid, storage, omemo_config) + self._backend.register_signal('republish-bundle', + self._on_republish_bundle) self._omemo_groupchats: set[str] = set() self._muc_temp_store: dict[bytes, str] = {} @@ -168,6 +178,14 @@ class OMEMO(BaseModule): if self.is_omemo_groupchat(jid): self.get_affiliation_list(jid) + def _on_republish_bundle(self, + _session: OMEMOSession, + _signal_name: str, + bundle: OMEMOBundle + ) -> None: + + self.set_bundle(bundle=bundle) + @property def backend(self) -> OMEMOSession: return self._backend @@ -183,7 +201,7 @@ class OMEMO(BaseModule): return False missing = True - for member_jid in self.backend.get_muc_members(jid): + for member_jid in self.backend.get_group_members(jid): if not self.are_keys_missing(member_jid): missing = False if missing: @@ -205,7 +223,7 @@ class OMEMO(BaseModule): return False # check if bundles are missing for some devices - if self.backend.storage.hasUndecidedFingerprints(jid): + if self.backend.storage.has_undecided_fingerprints(jid): self._log.info('%s => Undecided Fingerprints for %s', contact.account, jid) app.ged.raise_event(EncryptionInfo( @@ -225,15 +243,15 @@ class OMEMO(BaseModule): def _new_fingerprints_available(self, contact: types.ChatContactT) -> bool: fingerprints: list[int] = [] if contact.is_groupchat: - for member_jid in self.backend.get_muc_members(str(contact.jid), + for member_jid in self.backend.get_group_members(str(contact.jid), without_self=False): - fingerprints = self.backend.storage.getNewFingerprints( + fingerprints = self.backend.storage.get_new_fingerprints( member_jid) if fingerprints: break else: - fingerprints = self.backend.storage.getNewFingerprints( + fingerprints = self.backend.storage.get_new_fingerprints( str(contact.jid)) if not fingerprints: @@ -359,7 +377,7 @@ class OMEMO(BaseModule): return plaintext = self._muc_temp_store[properties.omemo.payload] - fingerprint = self.backend.own_fingerprint + fingerprint = self.backend.get_own_fingerprint() trust = OMEMOTrust.VERIFIED del self._muc_temp_store[properties.omemo.payload] @@ -401,7 +419,8 @@ class OMEMO(BaseModule): assert isinstance(properties.omemo, OMEMOMessage) self._log.info('Groupchat: Last resort trying to find SID in DB') - from_jid = self.backend.storage.getJidFromDevice(properties.omemo.sid) + from_jid = self.backend.storage.get_jid_from_device( + properties.omemo.sid) if not from_jid: self._log.error( "Can't decrypt GroupChat Message from %s", resource) @@ -443,9 +462,9 @@ class OMEMO(BaseModule): jid = properties.muc_user.jid.bare if properties.muc_user.affiliation in (Affiliation.OUTCAST, Affiliation.NONE): - self.backend.remove_muc_member(room, jid) + self.backend.remove_group_member(room, jid) else: - self.backend.add_muc_member(room, jid) + self.backend.add_group_member(room, jid) if self.is_omemo_groupchat(room): if not self.is_contact_in_roster(jid): @@ -471,7 +490,7 @@ class OMEMO(BaseModule): for user_jid in result.users: jid = str(user_jid) - self.backend.add_muc_member(room_jid, jid) + self.backend.add_group_member(room_jid, jid) if not self.is_contact_in_roster(jid): # Query Devicelists from JIDs not in our Roster @@ -499,7 +518,7 @@ class OMEMO(BaseModule): self._omemo_groupchats.discard(jid) def _check_for_missing_sessions(self, jid: str) -> None: - devices_without_session = self.backend.devices_without_sessions(jid) + devices_without_session = self.backend.get_devices_without_sessions(jid) for device_id in devices_without_session: if device_id in self._device_bundle_querys: continue @@ -524,7 +543,7 @@ class OMEMO(BaseModule): # Fetch Bundles of own other Devices if self._own_jid not in self._query_for_bundles: - devices_without_session = self.backend.devices_without_sessions( + devices_without_session = self.backend.get_devices_without_sessions( self._own_jid) self._query_for_bundles.append(self._own_jid) @@ -536,7 +555,7 @@ class OMEMO(BaseModule): # Fetch Bundles of contacts devices if contact_jid not in self._query_for_bundles: - devices_without_session = self.backend.devices_without_sessions( + devices_without_session = self.backend.get_devices_without_sessions( contact_jid) self._query_for_bundles.append(contact_jid) @@ -549,9 +568,11 @@ class OMEMO(BaseModule): return False return True - def set_bundle(self) -> None: - self._nbxmpp('OMEMO').set_bundle(self.backend.bundle, - self.backend.own_device) + def set_bundle(self, bundle: OMEMOBundle | None = None) -> None: + if bundle is None: + bundle = self.backend.get_bundle() + self._nbxmpp('OMEMO').set_bundle(bundle, + self.backend.get_own_device()) @as_task def request_bundle(self, jid: str, device_id: int): @@ -579,7 +600,7 @@ class OMEMO(BaseModule): message=EncryptionInfoMsg.UNDECIDED_FINGERPRINTS)) def set_devicelist(self, devicelist: Optional[list[int]] = None) -> None: - devicelist_: set[int] = {self.backend.own_device} + devicelist_: set[int] = {self.backend.get_own_device()} if devicelist is not None: devicelist_.update(devicelist) self._log.info('Publishing own devicelist: %s', devicelist_) @@ -587,7 +608,7 @@ class OMEMO(BaseModule): def clear_devicelist(self) -> None: self.backend.update_devicelist( - self._own_jid, [self.backend.own_device]) + self._own_jid, [self.backend.get_own_device()]) self.set_devicelist() @as_task @@ -640,7 +661,7 @@ class OMEMO(BaseModule): self._query_for_bundles.remove(jid) if own_devices: - if not self.backend.is_own_device_published: + if not self.backend.is_own_device_published(): # Our own device_id is not in the list, it could be # overwritten by some other client self.set_devicelist(devicelist) diff --git a/gajim/common/storage/omemo.py b/gajim/common/storage/omemo.py index adae07ae4..9ace1f028 100644 --- a/gajim/common/storage/omemo.py +++ b/gajim/common/storage/omemo.py @@ -26,29 +26,26 @@ import time from collections import namedtuple from pathlib import Path -from omemo_dr.const import DEFAULT_PREKEY_AMOUNT from omemo_dr.const import OMEMOTrust from omemo_dr.ecc.djbec import CurvePublicKey from omemo_dr.ecc.djbec import DjbECPrivateKey from omemo_dr.exceptions import InvalidKeyIdException from omemo_dr.identitykey import IdentityKey from omemo_dr.identitykeypair import IdentityKeyPair -from omemo_dr.state.axolotlstore import AxolotlStore from omemo_dr.state.prekeyrecord import PreKeyRecord from omemo_dr.state.sessionrecord import SessionRecord from omemo_dr.state.signedprekeyrecord import SignedPreKeyRecord -from omemo_dr.util.keyhelper import IdentityKeyExtended -from omemo_dr.util.keyhelper import KeyHelper +from omemo_dr.state.store import Store from omemo_dr.util.medium import Medium from gajim.common import app from gajim.common.modules.util import LogAdapter -def _convert_identity_key(key: bytes) -> Optional[IdentityKeyExtended]: +def _convert_identity_key(key: bytes) -> Optional[IdentityKey]: if not key: return - return IdentityKeyExtended(CurvePublicKey(key[1:])) + return IdentityKey(CurvePublicKey(key[1:])) def _convert_record(record: bytes) -> SessionRecord: @@ -59,15 +56,15 @@ sqlite3.register_converter('pk', _convert_identity_key) sqlite3.register_converter('session_record', _convert_record) -class OMEMOStorage(AxolotlStore): +class OMEMOStorage(Store): def __init__(self, account: str, db_path: Path, log: LogAdapter) -> None: self._log = log self._account = account self._con = sqlite3.connect(db_path, detect_types=sqlite3.PARSE_COLNAMES) self._con.row_factory = self._namedtuple_factory - self.createDb() - self.migrateDb() + self.create_db() + self.migrate_db() self._con.execute('PRAGMA secure_delete=1') self._con.execute('PRAGMA synchronous=NORMAL;') @@ -78,10 +75,6 @@ class OMEMOStorage(AxolotlStore): self._con.execute('PRAGMA journal_mode=MEMORY;') self._con.commit() - if not self.getLocalRegistrationId(): - self._log.info('Generating OMEMO keys') - self._generate_axolotl_keys() - def _is_blind_trust_enabled(self) -> bool: return app.settings.get_account_setting(self._account, 'omemo_blind_trust') @@ -105,26 +98,10 @@ class OMEMOStorage(AxolotlStore): fields.append(col[0]) return namedtuple('Row', fields)(*row) # pyright: ignore - def _generate_axolotl_keys(self) -> None: - identity_key_pair = KeyHelper.generateIdentityKeyPair() - registration_id = KeyHelper.getRandomSequence(2147483647) - pre_keys = KeyHelper.generatePreKeys( - KeyHelper.getRandomSequence(4294967296), - DEFAULT_PREKEY_AMOUNT) - self.storeLocalData(registration_id, identity_key_pair) - - signed_pre_key = KeyHelper.generateSignedPreKey( - identity_key_pair, KeyHelper.getRandomSequence(65536)) - - self.storeSignedPreKey(signed_pre_key.getId(), signed_pre_key) - - for pre_key in pre_keys: - self.storePreKey(pre_key.getId(), pre_key) - def user_version(self) -> int: return self._con.execute('PRAGMA user_version').fetchone()[0] - def createDb(self) -> None: + def create_db(self) -> None: if self.user_version() == 0: create_tables = ''' @@ -166,7 +143,7 @@ class OMEMOStorage(AxolotlStore): ''' % (create_tables) self._con.executescript(create_db_sql) - def migrateDb(self) -> None: + def migrate_db(self) -> None: ''' Migrates the DB ''' @@ -384,131 +361,130 @@ class OMEMOStorage(AxolotlStore): self._con.execute('PRAGMA user_version=12') self._con.commit() - def loadSignedPreKey(self, signedPreKeyId: int) -> SignedPreKeyRecord: + def load_signed_pre_key(self, signed_pre_key_id: int) -> SignedPreKeyRecord: query = 'SELECT record FROM signed_prekeys WHERE prekey_id = ?' - result = self._con.execute(query, (signedPreKeyId, )).fetchone() + result = self._con.execute(query, (signed_pre_key_id, )).fetchone() if result is None: raise InvalidKeyIdException('No such signedprekeyrecord! %s ' % - signedPreKeyId) + signed_pre_key_id) return SignedPreKeyRecord.from_bytes(result.record) - def loadSignedPreKeys(self) -> list[SignedPreKeyRecord]: + def load_signed_pre_keys(self) -> list[SignedPreKeyRecord]: query = 'SELECT record FROM signed_prekeys' results = self._con.execute(query).fetchall() return [SignedPreKeyRecord.from_bytes(row.record) for row in results] - def storeSignedPreKey(self, - signedPreKeyId: int, - signedPreKeyRecord: SignedPreKeyRecord - ) -> None: + def store_signed_pre_key(self, + signed_pre_key_id: int, + signed_pre_key_record: SignedPreKeyRecord + ) -> None: query = 'INSERT INTO signed_prekeys (prekey_id, record) VALUES(?,?)' - self._con.execute(query, (signedPreKeyId, - signedPreKeyRecord.serialize())) + self._con.execute(query, (signed_pre_key_id, + signed_pre_key_record.serialize())) self._con.commit() - def containsSignedPreKey(self, signedPreKeyId: int) -> bool: + def contains_signed_pre_key(self, signed_pre_key_id: int) -> bool: query = 'SELECT record FROM signed_prekeys WHERE prekey_id = ?' - result = self._con.execute(query, (signedPreKeyId,)).fetchone() + result = self._con.execute(query, (signed_pre_key_id,)).fetchone() return result is not None - def removeSignedPreKey(self, signedPreKeyId: int) -> None: + def remove_signed_pre_key(self, signed_pre_key_id: int) -> None: query = 'DELETE FROM signed_prekeys WHERE prekey_id = ?' - self._con.execute(query, (signedPreKeyId,)) + self._con.execute(query, (signed_pre_key_id,)) self._con.commit() - def getNextSignedPreKeyId(self) -> int: - result = self.getCurrentSignedPreKeyId() - if result is None: - return 1 # StartId if no SignedPreKeys exist + def get_next_signed_pre_key_id(self) -> int: + result = self.get_current_signed_pre_key_id() return (result % (Medium.MAX_VALUE - 1)) + 1 - def getCurrentSignedPreKeyId(self) -> Optional[int]: + def get_current_signed_pre_key_id(self) -> int: query = 'SELECT MAX(prekey_id) FROM signed_prekeys' result = self._con.execute(query).fetchone() - return result.max_prekey_id if result is not None else None + assert result is not None + return result.max_prekey_id - def getSignedPreKeyTimestamp(self, signedPreKeyId: int) -> int: + def get_signed_pre_key_timestamp(self, signed_pre_key_id: int) -> int: query = '''SELECT strftime('%s', timestamp) FROM signed_prekeys WHERE prekey_id = ?''' - result = self._con.execute(query, (signedPreKeyId,)).fetchone() + result = self._con.execute(query, (signed_pre_key_id,)).fetchone() if result is None: raise InvalidKeyIdException('No such signedprekeyrecord! %s' % - signedPreKeyId) + signed_pre_key_id) return result.formated_time - def removeOldSignedPreKeys(self, timestamp: int) -> None: + def remove_old_signed_pre_keys(self, timestamp: int) -> None: query = '''DELETE FROM signed_prekeys WHERE timestamp < datetime(?, "unixepoch")''' self._con.execute(query, (timestamp,)) self._con.commit() - def loadSession(self, recipientId: str, deviceId: int) -> SessionRecord: + def load_session(self, recipient_id: str, device_id: int) -> SessionRecord: query = '''SELECT record as "record [session_record]" FROM sessions WHERE recipient_id = ? AND device_id = ?''' - result = self._con.execute(query, (recipientId, deviceId)).fetchone() + result = self._con.execute(query, (recipient_id, device_id)).fetchone() return result.record if result is not None else SessionRecord() - def getJidFromDevice(self, device_id: int) -> Optional[str]: + def get_jid_from_device(self, device_id: int) -> Optional[str]: query = '''SELECT recipient_id FROM sessions WHERE device_id = ?''' result = self._con.execute(query, (device_id, )).fetchone() return result.recipient_id if result is not None else None - def getActiveDeviceTuples(self): + def get_active_device_tuples(self): query = '''SELECT recipient_id, device_id FROM sessions WHERE active = 1''' return self._con.execute(query).fetchall() - def storeSession(self, - recipientId: str, - deviceId: int, - sessionRecord: SessionRecord - ) -> None: + def store_session(self, + recipient_id: str, + device_id: int, + session_record: SessionRecord + ) -> None: query = '''INSERT INTO sessions(recipient_id, device_id, record) VALUES(?,?,?)''' try: - self._con.execute(query, (recipientId, - deviceId, - sessionRecord.serialize())) + self._con.execute(query, (recipient_id, + device_id, + session_record.serialize())) except sqlite3.IntegrityError: query = '''UPDATE sessions SET record = ? WHERE recipient_id = ? AND device_id = ?''' - self._con.execute(query, (sessionRecord.serialize(), - recipientId, - deviceId)) + self._con.execute(query, (session_record.serialize(), + recipient_id, + device_id)) self._con.commit() - def containsSession(self, recipientId: str, deviceId: int) -> bool: + def contains_session(self, recipient_id: str, device_id: int) -> bool: query = '''SELECT record FROM sessions WHERE recipient_id = ? AND device_id = ?''' - result = self._con.execute(query, (recipientId, deviceId)).fetchone() + result = self._con.execute(query, (recipient_id, device_id)).fetchone() return result is not None - def deleteSession(self, recipientId: str, deviceId: int) -> None: - self._log.info('Delete session for %s %s', recipientId, deviceId) + def delete_session(self, recipient_id: str, device_id: int) -> None: + self._log.info('Delete session for %s %s', recipient_id, device_id) query = 'DELETE FROM sessions WHERE recipient_id = ? AND device_id = ?' - self._con.execute(query, (recipientId, deviceId)) + self._con.execute(query, (recipient_id, device_id)) self._con.commit() - def deleteAllSessions(self, recipientId: str) -> None: + def delete_all_sessions(self, recipient_id: str) -> None: query = 'DELETE FROM sessions WHERE recipient_id = ?' - self._con.execute(query, (recipientId,)) + self._con.execute(query, (recipient_id,)) self._con.commit() - def getSessionsFromJid(self, recipientId: str): + def get_sessions_from_jid(self, recipient_id: str): query = '''SELECT recipient_id, device_id, record as "record [session_record]", active FROM sessions WHERE recipient_id = ?''' - return self._con.execute(query, (recipientId,)).fetchall() + return self._con.execute(query, (recipient_id,)).fetchall() - def getSessionsFromJids(self, recipientIds: list[str]): + def get_sessions_from_jids(self, recipient_ids: list[str]): query = ''' SELECT recipient_id, device_id, @@ -516,86 +492,84 @@ class OMEMOStorage(AxolotlStore): active FROM sessions WHERE recipient_id IN ({})'''.format( - ', '.join(['?'] * len(recipientIds))) - return self._con.execute(query, recipientIds).fetchall() + ', '.join(['?'] * len(recipient_ids))) + return self._con.execute(query, recipient_ids).fetchall() - def setActiveState(self, jid: str, devicelist: list[int]) -> None: + def set_active_state(self, address: str, devicelist: list[int]) -> None: query = ''' UPDATE sessions SET active = 1 WHERE recipient_id = ? AND device_id IN ({})'''.format( ', '.join(['?'] * len(devicelist))) - self._con.execute(query, (jid,) + tuple(devicelist)) + self._con.execute(query, (address,) + tuple(devicelist)) query = ''' UPDATE sessions SET active = 0 WHERE recipient_id = ? AND device_id NOT IN ({})'''.format( ', '.join(['?'] * len(devicelist))) - self._con.execute(query, (jid,) + tuple(devicelist)) + self._con.execute(query, (address,) + tuple(devicelist)) self._con.commit() - def setInactive(self, jid: str, device_id: int) -> None: + def set_inactive(self, address: str, device_id: int) -> None: query = '''UPDATE sessions SET active = 0 WHERE recipient_id = ? AND device_id = ?''' - self._con.execute(query, (jid, device_id)) + self._con.execute(query, (address, device_id)) self._con.commit() - def getInactiveSessionsKeys(self, - recipientId: str - ) -> list[IdentityKeyExtended]: + def get_inactive_sessions_keys(self, + recipient_id: str + ) -> list[IdentityKey]: query = '''SELECT record as "record [session_record]" FROM sessions WHERE active = 0 AND recipient_id = ?''' - results = self._con.execute(query, (recipientId,)).fetchall() + results = self._con.execute(query, (recipient_id,)).fetchall() - keys: list[IdentityKeyExtended] = [] + keys: list[IdentityKey] = [] for result in results: - key = result.record.getSessionState().getRemoteIdentityKey() - keys.append(IdentityKeyExtended(key.getPublicKey())) + key = result.record.get_session_state().get_remote_identity_key() + keys.append(IdentityKey(key.get_public_key())) return keys - def loadPreKey(self, preKeyId: int) -> PreKeyRecord: + def load_pre_key(self, pre_key_id: int) -> PreKeyRecord: query = '''SELECT record FROM prekeys WHERE prekey_id = ?''' - result = self._con.execute(query, (preKeyId,)).fetchone() + result = self._con.execute(query, (pre_key_id,)).fetchone() if result is None: raise Exception('No such prekeyRecord!') return PreKeyRecord.from_bytes(result.record) - def loadPendingPreKeys(self) -> list[PreKeyRecord]: + def load_pending_pre_keys(self) -> list[PreKeyRecord]: query = '''SELECT record FROM prekeys''' result = self._con.execute(query).fetchall() return [PreKeyRecord.from_bytes(row.record) for row in result] - def storePreKey(self, preKeyId: int, preKeyRecord: PreKeyRecord) -> None: + def store_pre_key(self, + pre_key_id: int, + pre_key_record: PreKeyRecord + ) -> None: query = 'INSERT INTO prekeys (prekey_id, record) VALUES(?,?)' - self._con.execute(query, (preKeyId, preKeyRecord.serialize())) + self._con.execute(query, (pre_key_id, pre_key_record.serialize())) self._con.commit() - def containsPreKey(self, preKeyId: int) -> bool: + def contains_pre_key(self, pre_key_id: int) -> bool: query = 'SELECT record FROM prekeys WHERE prekey_id = ?' - result = self._con.execute(query, (preKeyId,)).fetchone() + result = self._con.execute(query, (pre_key_id,)).fetchone() return result is not None - def removePreKey(self, preKeyId: int) -> None: + def remove_pre_key(self, pre_key_id: int) -> None: query = 'DELETE FROM prekeys WHERE prekey_id = ?' - self._con.execute(query, (preKeyId,)) + self._con.execute(query, (pre_key_id,)) self._con.commit() - def getCurrentPreKeyId(self) -> int: + def get_current_pre_key_id(self) -> Optional[int]: query = 'SELECT MAX(prekey_id) FROM prekeys' - return self._con.execute(query).fetchone().max_prekey_id + result = self._con.execute(query).fetchone() + return result.max_prekey_id if result is not None else None - def getPreKeyCount(self) -> int: + def get_pre_key_count(self) -> int: query = 'SELECT COUNT(prekey_id) FROM prekeys' return self._con.execute(query).fetchone().count_prekey_id - def generateNewPreKeys(self, count: int) -> None: - prekey_id = self.getCurrentPreKeyId() or 0 - pre_keys = KeyHelper.generatePreKeys(prekey_id + 1, count) - for pre_key in pre_keys: - self.storePreKey(pre_key.getId(), pre_key) - - def getIdentityKeyPair(self) -> IdentityKeyPair: + def get_identity_key_pair(self) -> IdentityKeyPair: query = '''SELECT public_key as "public_key [pk]", private_key FROM secret LIMIT 1''' result = self._con.execute(query).fetchone() @@ -603,15 +577,16 @@ class OMEMOStorage(AxolotlStore): return IdentityKeyPair.new(result.public_key, DjbECPrivateKey(result.private_key)) - def getLocalRegistrationId(self) -> Optional[int]: # pyright: ignore + def get_local_registration_id(self) -> int: query = 'SELECT device_id FROM secret LIMIT 1' result = self._con.execute(query).fetchone() - return result.device_id if result is not None else None + assert result is not None + return result.device_id - def storeLocalData(self, - device_id: int, - identityKeyPair: IdentityKeyPair - ) -> None: + def store_own_identity(self, + device_id: int, + identity_key_pair: IdentityKeyPair + ) -> None: query = 'SELECT * FROM secret' result = self._con.execute(query).fetchone() @@ -623,67 +598,71 @@ class OMEMOStorage(AxolotlStore): query = '''INSERT INTO secret(device_id, public_key, private_key) VALUES(?, ?, ?)''' - public_key = identityKeyPair.getPublicKey().getPublicKey().serialize() - private_key = identityKeyPair.getPrivateKey().serialize() + public_key = identity_key_pair.get_public_key().get_public_key().\ + serialize() + private_key = identity_key_pair.get_private_key().serialize() self._con.execute(query, (device_id, public_key, private_key)) self._con.commit() - def saveIdentity(self, recipientId: str, identityKey: IdentityKey) -> None: + def save_identity(self, + recipient_id: str, + identity_key: IdentityKey + ) -> None: query = '''INSERT INTO identities (recipient_id, public_key, trust, shown) VALUES(?, ?, ?, ?)''' - if not self.containsIdentity(recipientId, identityKey): - trust = self.getDefaultTrust(recipientId) - self._con.execute(query, (recipientId, - identityKey.getPublicKey().serialize(), + if not self.contains_identity(recipient_id, identity_key): + trust = self.get_default_trust(recipient_id) + self._con.execute(query, (recipient_id, + identity_key.get_public_key().serialize(), trust, 1 if trust == OMEMOTrust.BLIND else 0)) self._con.commit() - def containsIdentity(self, - recipientId: str, - identityKey: IdentityKey - ) -> bool: + def contains_identity(self, + recipient_id: str, + identity_key: IdentityKey + ) -> bool: query = '''SELECT * FROM identities WHERE recipient_id = ? AND public_key = ?''' - public_key = identityKey.getPublicKey().serialize() - result = self._con.execute(query, (recipientId, + public_key = identity_key.get_public_key().serialize() + result = self._con.execute(query, (recipient_id, public_key)).fetchone() return result is not None - def deleteIdentity(self, - recipientId: str, - identityKey: IdentityKey - ) -> None: + def delete_identity(self, + recipient_id: str, + identity_key: IdentityKey + ) -> None: query = '''DELETE FROM identities WHERE recipient_id = ? AND public_key = ?''' - public_key = identityKey.getPublicKey().serialize() - self._con.execute(query, (recipientId, public_key)) + public_key = identity_key.get_public_key().serialize() + self._con.execute(query, (recipient_id, public_key)) self._con.commit() - def isTrustedIdentity(self, - recipientId: str, - identityKey: IdentityKey - ) -> bool: + def is_trusted_identity(self, + recipient_id: str, + identity_key: IdentityKey + ) -> bool: return True - def getTrustForIdentity(self, - recipientId: str, - identityKey: IdentityKey - ) -> Optional[OMEMOTrust]: + def get_trust_for_identity(self, + recipient_id: str, + identity_key: IdentityKey + ) -> Optional[OMEMOTrust]: query = '''SELECT trust FROM identities WHERE recipient_id = ? AND public_key = ?''' - public_key = identityKey.getPublicKey().serialize() - result = self._con.execute(query, (recipientId, public_key)).fetchone() + public_key = identity_key.get_public_key().serialize() + result = self._con.execute(query, (recipient_id, public_key)).fetchone() return result.trust if result is not None else None - def getFingerprints(self, jid: str): + def get_fingerprints(self, jid: str): query = '''SELECT recipient_id, public_key as "public_key [pk]", trust, @@ -692,7 +671,7 @@ class OMEMOStorage(AxolotlStore): WHERE recipient_id = ? ORDER BY trust ASC''' return self._con.execute(query, (jid,)).fetchall() - def getMucFingerprints(self, jids: list[str]): + def get_muc_fingerprints(self, jids: list[str]): query = ''' SELECT recipient_id, public_key as "public_key [pk]", @@ -704,18 +683,18 @@ class OMEMOStorage(AxolotlStore): return self._con.execute(query, jids).fetchall() - def hasUndecidedFingerprints(self, jid: str) -> bool: + def has_undecided_fingerprints(self, jid: str) -> bool: query = '''SELECT public_key as "public_key [pk]" FROM identities WHERE recipient_id = ? AND trust = ?''' result = self._con.execute(query, (jid, OMEMOTrust.UNDECIDED)).fetchall() undecided = [row.public_key for row in result] - inactive = self.getInactiveSessionsKeys(jid) + inactive = self.get_inactive_sessions_keys(jid) undecided = set(undecided) - set(inactive) return bool(undecided) - def getDefaultTrust(self, jid: str) -> OMEMOTrust: + def get_default_trust(self, jid: str) -> OMEMOTrust: if not self._is_blind_trust_enabled(): return OMEMOTrust.UNDECIDED @@ -726,78 +705,84 @@ class OMEMOStorage(AxolotlStore): return OMEMOTrust.BLIND return OMEMOTrust.UNDECIDED - def getTrustedFingerprints(self, jid: str) -> list[IdentityKeyExtended]: + def get_trusted_fingerprints(self, address: str) -> list[IdentityKey]: query = '''SELECT public_key as "public_key [pk]" FROM identities WHERE recipient_id = ? AND trust IN(1, 3)''' - result = self._con.execute(query, (jid,)).fetchall() + result = self._con.execute(query, (address,)).fetchall() return [row.public_key for row in result] - def getNewFingerprints(self, jid: str) -> list[int]: + def get_new_fingerprints(self, jid: str) -> list[int]: query = '''SELECT _id FROM identities WHERE shown = 0 AND recipient_id = ?''' result = self._con.execute(query, (jid,)).fetchall() return [row.id for row in result] - def setShownFingerprints(self, fingerprints: list[int]) -> None: + def set_shown_fingerprints(self, fingerprints: list[int]) -> None: query = 'UPDATE identities SET shown = 1 WHERE _id IN ({})'.format( ', '.join(['?'] * len(fingerprints))) self._con.execute(query, fingerprints) self._con.commit() - def setTrust(self, - recipient_id: str, - identityKey: IdentityKey, - trust: OMEMOTrust - ) -> None: + def set_trust(self, + recipient_id: str, + identity_key: IdentityKey, + trust: OMEMOTrust + ) -> None: query = '''UPDATE identities SET trust = ? WHERE public_key = ? AND recipient_id = ?''' - public_key = identityKey.getPublicKey().serialize() + public_key = identity_key.get_public_key().serialize() self._con.execute(query, (trust, public_key, recipient_id)) self._con.commit() - def isTrusted(self, recipient_id: str, device_id: int) -> bool: - record = self.loadSession(recipient_id, device_id) - if record.isFresh(): + def is_trusted(self, recipient_id: str, device_id: int) -> bool: + record = self.load_session(recipient_id, device_id) + if record.is_fresh(): return False - identity_key = record.getSessionState().getRemoteIdentityKey() - return self.getTrustForIdentity( + identity_key = record.get_session_state().get_remote_identity_key() + return self.get_trust_for_identity( recipient_id, identity_key) in (OMEMOTrust.VERIFIED, OMEMOTrust.BLIND) - def getIdentityLastSeen(self, - recipient_id: str, - identity_key: IdentityKey - ) -> Optional[int]: + def get_identity_last_seen(self, + recipient_id: str, + identity_key: IdentityKey + ) -> Optional[int]: - serialized = identity_key.getPublicKey().serialize() + serialized = identity_key.get_public_key().serialize() query = '''SELECT timestamp FROM identities WHERE recipient_id = ? AND public_key = ?''' result = self._con.execute(query, (recipient_id, serialized)).fetchone() return result.timestamp if result is not None else None - def setIdentityLastSeen(self, - recipient_id: str, - identity_key: IdentityKey - ) -> None: + def set_identity_last_seen(self, + recipient_id: str, + identity_key: IdentityKey + ) -> None: timestamp = int(time.time()) - serialized = identity_key.getPublicKey().serialize() + serialized = identity_key.get_public_key().serialize() self._log.info('Set last seen for %s %s', recipient_id, timestamp) query = '''UPDATE identities SET timestamp = ? WHERE recipient_id = ? AND public_key = ?''' self._con.execute(query, (timestamp, recipient_id, serialized)) self._con.commit() - def getUnacknowledgedCount(self, recipient_id: str, device_id: int) -> int: - record = self.loadSession(recipient_id, device_id) - if record.isFresh(): + def get_unacknowledged_count(self, + recipient_id: str, + device_id: int + ) -> int: + record = self.load_session(recipient_id, device_id) + if record.is_fresh(): return 0 - state = record.getSessionState() - return state.getSenderChainKey().getIndex() + state = record.get_session_state() + return state.get_sender_chain_key().get_index() - def getSubDeviceSessions(self, recipientId: str) -> list[int]: - # Not used - return [] + def needs_init(self) -> bool: + try: + self.get_local_registration_id() + except AssertionError: + return True + return False diff --git a/gajim/gtk/omemo_trust_manager.py b/gajim/gtk/omemo_trust_manager.py index 6623829f3..acd749231 100644 --- a/gajim/gtk/omemo_trust_manager.py +++ b/gajim/gtk/omemo_trust_manager.py @@ -29,9 +29,7 @@ from gi.repository import GdkPixbuf from gi.repository import Gtk from nbxmpp.protocol import JID from omemo_dr.const import OMEMOTrust -from omemo_dr.identitykeypair import IdentityKeyPair -from omemo_dr.util.keyhelper import get_fingerprint -from omemo_dr.util.keyhelper import IdentityKeyExtended +from omemo_dr.identitykey import IdentityKey from gajim.common import app from gajim.common import ged @@ -125,9 +123,8 @@ class OMEMOTrustManager(Gtk.Box, EventHelper): self._omemo = client.get_module('OMEMO') - self._identity_key = cast( - IdentityKeyPair, self._omemo.backend.storage.getIdentityKeyPair()) - our_fpr_formatted = get_fingerprint(self._identity_key, formatted=True) + our_fpr_formatted = self._omemo.backend.get_own_fingerprint( + formatted=True) self._ui.our_fingerprint_1.set_text(our_fpr_formatted) self._ui.our_fingerprint_2.set_text(our_fpr_formatted) @@ -198,15 +195,16 @@ class OMEMOTrustManager(Gtk.Box, EventHelper): if contact.is_groupchat: members = list(self._omemo.backend.get_muc_members( str(contact.jid))) - sessions = self._omemo.backend.storage.getSessionsFromJids(members) - results = self._omemo.backend.storage.getMucFingerprints(members) + sessions = self._omemo.backend.storage.get_sessions_from_jids( + members) + results = self._omemo.backend.storage.get_muc_fingerprints(members) else: - sessions = self._omemo.backend.storage.getSessionsFromJid( + sessions = self._omemo.backend.storage.get_sessions_from_jid( str(contact.jid)) - results = self._omemo.backend.storage.getFingerprints( + results = self._omemo.backend.storage.get_fingerprints( str(contact.jid)) - rows: dict[IdentityKeyExtended, KeyRow] = {} + rows: dict[IdentityKey, KeyRow] = {} for result in results: rows[result.public_key] = KeyRow(contact, result.recipient_id, @@ -215,16 +213,17 @@ class OMEMOTrustManager(Gtk.Box, EventHelper): result.timestamp) for item in sessions: - if item.record.isFresh(): + if item.record.is_fresh(): return - identity_key = item.record.getSessionState().getRemoteIdentityKey() - identity_key = IdentityKeyExtended(identity_key.getPublicKey()) + identity_key = item.record.get_session_state().\ + get_remote_identity_key() + identity_key = IdentityKey(identity_key.get_public_key()) try: key_row = rows[identity_key] except KeyError: log.warning('Could not find session identitykey %s', item.device_id) - self._omemo.backend.storage.deleteSession(item.recipient_id, + self._omemo.backend.storage.delete_session(item.recipient_id, item.device_id) continue @@ -237,10 +236,9 @@ class OMEMOTrustManager(Gtk.Box, EventHelper): @staticmethod def _get_qrcode(jid: JID, sid: int, - identity_key: IdentityKeyPair + fingerprint: str ) -> GdkPixbuf.Pixbuf | None: - fingerprint = get_fingerprint(identity_key) qry = (XmppUriQuery.MESSAGE.value, [(f'omemo-sid-{sid}', fingerprint)]) ver_string = jid.new_as_bare().to_iri(qry) log.debug('Verification String: %s', ver_string) @@ -249,8 +247,8 @@ class OMEMOTrustManager(Gtk.Box, EventHelper): def _load_qrcode(self) -> None: client = app.get_client(self._account) pixbuf = self._get_qrcode(client.get_own_jid(), - self._omemo.backend.own_device, - self._identity_key) + self._omemo.backend.get_own_device(), + self._omemo.backend.get_own_fingerprint()) self._ui.qr_code_image.set_from_pixbuf(pixbuf) def _on_show_inactive(self, switch: Gtk.Switch, _param: Any) -> None: @@ -280,7 +278,7 @@ class KeyRow(Gtk.ListBoxRow): def __init__(self, contact: types.ChatContactT, jid: JID, - identity_key: IdentityKeyExtended, + identity_key: IdentityKey, trust: OMEMOTrust, last_seen: Optional[float] ) -> None: @@ -347,9 +345,9 @@ class KeyRow(Gtk.ListBoxRow): def _remove(): self._omemo.backend.remove_device(str(self.jid), self.device_id) - self._omemo.backend.storage.deleteSession( + self._omemo.backend.storage.delete_session( str(self.jid), self.device_id) - self._omemo.backend.storage.deleteIdentity( + self._omemo.backend.storage.delete_identity( str(self.jid), self._identity_key) listbox = cast(Gtk.ListBox, self.get_parent()) @@ -373,7 +371,7 @@ class KeyRow(Gtk.ListBoxRow): image.get_style_context().add_class(css_class) image.set_tooltip_text(tooltip) - self._omemo.backend.storage.setTrust( + self._omemo.backend.storage.set_trust( str(self.jid), self._identity_key, self.trust) @property diff --git a/pyproject.toml b/pyproject.toml index 4545e450b..0efb16b34 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,7 @@ dependencies = [ "pycairo>=1.16.0", "PyGObject>=3.42.0", "qrcode>=7.3.1", - "omemo-dr>=0.99.0,<2.0.0", + "omemo-dr>=0.99.1,<2.0.0", "winsdk ; platform_system == 'Windows'", ] dynamic = ["version"] |