Welcome to mirror list, hosted at ThFree Co, Russian Federation.

dev.gajim.org/gajim/gajim-plugins.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'omemo/backend/state.py')
-rw-r--r--omemo/backend/state.py362
1 files changed, 0 insertions, 362 deletions
diff --git a/omemo/backend/state.py b/omemo/backend/state.py
deleted file mode 100644
index ce3b099..0000000
--- a/omemo/backend/state.py
+++ /dev/null
@@ -1,362 +0,0 @@
-# Copyright (C) 2019 Philipp Hörist <philipp AT hoerist.com>
-# Copyright (C) 2015 Bahtiar `kalkin-` Gadimov <bahtiar@gadimov.de>
-#
-# This file is part of OMEMO Gajim Plugin.
-#
-# OMEMO Gajim Plugin is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published
-# by the Free Software Foundation; version 3 only.
-#
-# OMEMO Gajim Plugin is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with OMEMO Gajim Plugin. If not, see <http://www.gnu.org/licenses/>.
-
-import time
-from collections import defaultdict
-
-from nbxmpp.structs import OMEMOBundle
-from nbxmpp.structs import OMEMOMessage
-
-from axolotl.ecc.djbec import DjbECPublicKey
-from axolotl.identitykey import IdentityKey
-
-from axolotl.protocol.prekeywhispermessage import PreKeyWhisperMessage
-from axolotl.protocol.whispermessage import WhisperMessage
-from axolotl.sessionbuilder import SessionBuilder
-from axolotl.sessioncipher import SessionCipher
-from axolotl.state.prekeybundle import PreKeyBundle
-from axolotl.util.keyhelper import KeyHelper
-from axolotl.duplicatemessagexception import DuplicateMessageException
-
-from omemo.backend.aes import aes_decrypt
-from omemo.backend.aes import aes_encrypt
-from omemo.backend.aes import get_new_key
-from omemo.backend.aes import get_new_iv
-from omemo.backend.devices import DeviceManager
-from omemo.backend.devices import NoDevicesFound
-from omemo.backend.liteaxolotlstore import LiteAxolotlStore
-from omemo.backend.util import get_fingerprint
-from omemo.backend.util import Trust
-from omemo.backend.util import DEFAULT_PREKEY_AMOUNT
-from omemo.backend.util import MIN_PREKEY_AMOUNT
-from omemo.backend.util import SPK_CYCLE_TIME
-from omemo.backend.util import SPK_ARCHIVE_TIME
-from omemo.backend.util import UNACKNOWLEDGED_COUNT
-
-
-class OmemoState(DeviceManager):
- def __init__(self, own_jid, db_path, account, xmpp_con):
- self._account = account
- self._own_jid = own_jid
- self._log = xmpp_con._log
- self._session_ciphers = defaultdict(dict)
- self._storage = LiteAxolotlStore(db_path, self._log)
-
- DeviceManager.__init__(self)
-
- self.xmpp_con = xmpp_con
-
- self._log.info('%s PreKeys available',
- self._storage.getPreKeyCount())
-
- def build_session(self, jid, device_id, bundle):
- session = SessionBuilder(self._storage, self._storage, self._storage,
- self._storage, jid, device_id)
-
- registration_id = self._storage.getLocalRegistrationId()
-
- prekey = bundle.pick_prekey()
- otpk = DjbECPublicKey(prekey['key'][1:])
-
- spk = DjbECPublicKey(bundle.spk['key'][1:])
- ik = IdentityKey(DjbECPublicKey(bundle.ik[1:]))
-
- prekey_bundle = PreKeyBundle(registration_id,
- device_id,
- prekey['id'],
- otpk,
- bundle.spk['id'],
- spk,
- bundle.spk_signature,
- ik)
-
- session.processPreKeyBundle(prekey_bundle)
- self._get_session_cipher(jid, device_id)
-
- @property
- def storage(self):
- return self._storage
-
- @property
- def own_fingerprint(self):
- return get_fingerprint(self._storage.getIdentityKeyPair())
-
- @property
- def bundle(self):
- self._check_pre_key_count()
-
- bundle = {'otpks': []}
- for k in self._storage.loadPendingPreKeys():
- key = k.getKeyPair().getPublicKey().serialize()
- bundle['otpks'].append({'key': key, 'id': k.getId()})
-
- ik_pair = self._storage.getIdentityKeyPair()
- bundle['ik'] = ik_pair.getPublicKey().serialize()
-
- self._cycle_signed_pre_key(ik_pair)
-
- spk = self._storage.loadSignedPreKey(
- self._storage.getCurrentSignedPreKeyId())
- bundle['spk_signature'] = spk.getSignature()
- bundle['spk'] = {'key': spk.getKeyPair().getPublicKey().serialize(),
- 'id': spk.getId()}
-
- return OMEMOBundle(**bundle)
-
- def decrypt_message(self, omemo_message, jid):
- if omemo_message.sid == self.own_device:
- self._log.info('Received previously sent message by us')
- raise SelfMessage
-
- try:
- encrypted_key, prekey = omemo_message.keys[self.own_device]
- except KeyError:
- self._log.info('Received message not for our device')
- raise MessageNotForDevice
-
- try:
- if prekey:
- key, fingerprint, trust = self._process_pre_key_message(
- jid, omemo_message.sid, encrypted_key)
- else:
- key, fingerprint, trust = self._process_message(
- jid, omemo_message.sid, encrypted_key)
-
- except DuplicateMessageException:
- self._log.info('Received duplicated message')
- raise DuplicateMessage
-
- except Exception as error:
- self._log.warning(error)
- raise DecryptionFailed
-
- if omemo_message.payload is None:
- self._log.debug("Decrypted Key Exchange Message")
- raise KeyExchangeMessage
-
- try:
- result = aes_decrypt(key, omemo_message.iv, omemo_message.payload)
- except Exception as error:
- self._log.warning(error)
- raise DecryptionFailed
-
- self._log.debug("Decrypted Message => %s", result)
- return result, fingerprint, trust
-
- def _get_whisper_message(self, jid, device, key):
- cipher = self._get_session_cipher(jid, device)
- cipher_key = cipher.encrypt(key)
- prekey = isinstance(cipher_key, PreKeyWhisperMessage)
- return cipher_key.serialize(), prekey
-
- def encrypt(self, jid, plaintext):
- try:
- devices_for_encryption = self.get_devices_for_encryption(jid)
- except NoDevicesFound:
- self._log.warning('No devices for encryption found for: %s', jid)
- return
-
- result = aes_encrypt(plaintext)
- whisper_messages = defaultdict(dict)
-
- for jid_, device in devices_for_encryption:
- count = self._storage.getUnacknowledgedCount(jid_, device)
- if count >= UNACKNOWLEDGED_COUNT:
- self._log.warning('Set device inactive %s because of %s '
- 'unacknowledged messages', device, count)
- self.remove_device(jid_, device)
-
- try:
- whisper_messages[jid_][device] = self._get_whisper_message(
- jid_, device, result.key)
- except Exception:
- self._log.exception('Failed to encrypt')
- continue
-
- recipients = set(whisper_messages.keys())
- if jid != self._own_jid:
- recipients -= set([self._own_jid])
- if not recipients:
- self._log.error('Encrypted keys empty')
- return
-
- encrypted_keys = {}
- for jid_ in whisper_messages:
- encrypted_keys.update(whisper_messages[jid_])
-
- self._log.debug('Finished encrypting message')
- return OMEMOMessage(sid=self.own_device,
- keys=encrypted_keys,
- iv=result.iv,
- payload=result.payload)
-
- def encrypt_key_transport(self, jid, devices):
- whisper_messages = defaultdict(dict)
- for device in devices:
- try:
- whisper_messages[jid][device] = self._get_whisper_message(
- jid, device, get_new_key())
- except Exception:
- self._log.exception('Failed to encrypt')
- continue
-
- if not whisper_messages[jid]:
- self._log.error('Encrypted keys empty')
- return
-
- self._log.debug('Finished Key Transport message')
- return OMEMOMessage(sid=self.own_device,
- keys=whisper_messages[jid],
- iv=get_new_iv(),
- payload=None)
-
- def has_trusted_keys(self, jid):
- inactive = self._storage.getInactiveSessionsKeys(jid)
- trusted = self._storage.getTrustedFingerprints(jid)
- return bool(set(trusted) - set(inactive))
-
- def devices_without_sessions(self, jid):
- known_devices = self.get_devices(jid, without_self=True)
- missing_devices = [dev
- for dev in known_devices
- if not self._storage.containsSession(jid, dev)]
- if missing_devices:
- self._log.info('Missing device sessions for %s: %s',
- jid, missing_devices)
- return missing_devices
-
- def _get_session_cipher(self, jid, device_id):
- try:
- return self._session_ciphers[jid][device_id]
- except KeyError:
- cipher = SessionCipher(self._storage, self._storage, self._storage,
- self._storage, jid, device_id)
- self._session_ciphers[jid][device_id] = cipher
- return cipher
-
- def _process_pre_key_message(self, jid, device, key):
- self._log.info('Process pre key message from %s', jid)
- pre_key_message = PreKeyWhisperMessage(serialized=key)
- if not pre_key_message.getPreKeyId():
- raise Exception('Received Pre Key Message '
- 'without PreKey => %s' % jid)
-
- session_cipher = self._get_session_cipher(jid, device)
- key = session_cipher.decryptPkmsg(pre_key_message)
-
- identity_key = pre_key_message.getIdentityKey()
- trust = self._get_trust_from_identity_key(jid, identity_key)
- fingerprint = get_fingerprint(identity_key)
-
- self._storage.setIdentityLastSeen(jid, identity_key)
-
- self.xmpp_con.set_bundle()
- self.add_device(jid, device)
- return key, fingerprint, trust
-
- def _process_message(self, jid, device, key):
- self._log.info('Process message from %s', jid)
- message = WhisperMessage(serialized=key)
-
- session_cipher = self._get_session_cipher(jid, device)
- key = session_cipher.decryptMsg(message, textMsg=False)
-
- identity_key = self._get_identity_key_from_device(jid, device)
- trust = self._get_trust_from_identity_key(jid, identity_key)
- fingerprint = get_fingerprint(identity_key)
-
- self._storage.setIdentityLastSeen(jid, identity_key)
-
- self.add_device(jid, device)
-
- return key, fingerprint, trust
-
- @staticmethod
- def _get_identity_key_from_pk_message(key):
- pre_key_message = PreKeyWhisperMessage(serialized=key)
- return pre_key_message.getIdentityKey()
-
- def _get_identity_key_from_device(self, jid, device):
- session_record = self._storage.loadSession(jid, device)
- return session_record.getSessionState().getRemoteIdentityKey()
-
- def _get_trust_from_identity_key(self, jid, identity_key):
- trust = self._storage.getTrustForIdentity(jid, identity_key)
- return Trust(trust) if trust is not None else Trust.UNDECIDED
-
- def _check_pre_key_count(self):
- # Check if enough PreKeys are available
- pre_key_count = self._storage.getPreKeyCount()
- if pre_key_count < MIN_PREKEY_AMOUNT:
- missing_count = DEFAULT_PREKEY_AMOUNT - pre_key_count
- self._storage.generateNewPreKeys(missing_count)
- self._log.info('%s PreKeys created', missing_count)
-
- def _cycle_signed_pre_key(self, ik_pair):
- # Publish every SPK_CYCLE_TIME a new SignedPreKey
- # Delete all exsiting SignedPreKeys that are older
- # then SPK_ARCHIVE_TIME
-
- # Check if SignedPreKey exist and create if not
- if not self._storage.getCurrentSignedPreKeyId():
- spk = KeyHelper.generateSignedPreKey(
- ik_pair, self._storage.getNextSignedPreKeyId())
- self._storage.storeSignedPreKey(spk.getId(), spk)
- self._log.debug('New SignedPreKey created, because none existed')
-
- # if SPK_CYCLE_TIME is reached, generate a new SignedPreKey
- now = int(time.time())
- timestamp = self._storage.getSignedPreKeyTimestamp(
- self._storage.getCurrentSignedPreKeyId())
-
- if int(timestamp) < now - SPK_CYCLE_TIME:
- spk = KeyHelper.generateSignedPreKey(
- ik_pair, self._storage.getNextSignedPreKeyId())
- self._storage.storeSignedPreKey(spk.getId(), spk)
- self._log.debug('Cycled SignedPreKey')
-
- # Delete all SignedPreKeys that are older than SPK_ARCHIVE_TIME
- timestamp = now - SPK_ARCHIVE_TIME
- self._storage.removeOldSignedPreKeys(timestamp)
-
-
-class NoValidSessions(Exception):
- pass
-
-
-class SelfMessage(Exception):
- pass
-
-
-class MessageNotForDevice(Exception):
- pass
-
-
-class DecryptionFailed(Exception):
- pass
-
-
-class KeyExchangeMessage(Exception):
- pass
-
-
-class InvalidMessage(Exception):
- pass
-
-
-class DuplicateMessage(Exception):
- pass