Philipp Hörist pushed to branch omemo at gajim / gajim
Commits: 9b8b1839 by Philipp Hörist at 2023-05-03T22:19:54+02:00 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
- - - - -
5 changed files:
- debian/control - gajim/common/modules/omemo.py - gajim/common/storage/omemo.py - gajim/gtk/omemo_trust_manager.py - pyproject.toml
Changes:
===================================== 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,
===================================== gajim/common/modules/omemo.py ===================================== @@ -51,6 +51,8 @@ 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 @@ def __init__(self, client: types.Client) -> None: 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 @@ def _on_room_joined(self, 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 @@ def check_send_preconditions(self, contact: types.ChatContactT) -> bool: 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 @@ def check_send_preconditions(self, contact: types.ChatContactT) -> bool: 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 @@ def check_send_preconditions(self, contact: types.ChatContactT) -> bool: 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 @@ def _message_received(self, 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 @@ def _process_muc_message(self,
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 @@ def _on_muc_user_presence(self, 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 @@ def _on_affiliations_received(self, task: Task) -> None:
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 @@ def _check_if_omemo_capable(self, jid: str) -> None: 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 @@ def are_keys_missing(self, contact_jid: str) -> bool: # 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 @@ def are_keys_missing(self, contact_jid: str) -> bool: # 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 @@ def are_keys_missing(self, contact_jid: str) -> bool: 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 @@ def request_bundle(self, jid: str, device_id: int): 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 @@ def set_devicelist(self, devicelist: Optional[list[int]] = None) -> None:
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 @@ def _process_devicelist_update(self, 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)
===================================== gajim/common/storage/omemo.py ===================================== @@ -26,29 +26,26 @@ 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 @@ def _convert_record(record: bytes) -> SessionRecord: 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 @@ def __init__(self, account: str, db_path: Path, log: LogAdapter) -> None: 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 @@ def _namedtuple_factory(cursor: sqlite3.Cursor, 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 @@ def createDb(self) -> None: ''' % (create_tables) self._con.executescript(create_db_sql)
- def migrateDb(self) -> None: + def migrate_db(self) -> None: ''' Migrates the DB '''
@@ -384,131 +361,130 @@ def migrateDb(self) -> None: 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 @@ def getSessionsFromJids(self, recipientIds: list[str]): 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 @@ def getIdentityKeyPair(self) -> IdentityKeyPair: 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 @@ def storeLocalData(self, 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 @@ def getFingerprints(self, jid: str): 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 @@ def getMucFingerprints(self, jids: list[str]):
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 @@ def getDefaultTrust(self, jid: str) -> OMEMOTrust: 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
===================================== gajim/gtk/omemo_trust_manager.py ===================================== @@ -29,9 +29,7 @@ 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 @@ def _update(self) -> None:
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 @@ def _load_fingerprints(self, contact: types.ChatContactT) -> None: 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 @@ def _load_fingerprints(self, contact: types.ChatContactT) -> None: 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 @@ def _load_fingerprints(self, contact: types.ChatContactT) -> None: @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 @@ def _get_qrcode(jid: JID, 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 @@ def delete_fingerprint(self, *args: Any) -> None:
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 @@ def set_trust(self) -> None: 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
===================================== 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"]
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/commit/9b8b1839d1a5398c46925b1b299ad0417...