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')
-rw-r--r--omemo/backend/__init__.py1
-rw-r--r--omemo/backend/aes.py95
-rw-r--r--omemo/backend/devices.py138
-rw-r--r--omemo/backend/liteaxolotlstore.py741
-rw-r--r--omemo/backend/state.py362
-rw-r--r--omemo/backend/util.py56
6 files changed, 0 insertions, 1393 deletions
diff --git a/omemo/backend/__init__.py b/omemo/backend/__init__.py
deleted file mode 100644
index 3dc1f76..0000000
--- a/omemo/backend/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-__version__ = "0.1.0"
diff --git a/omemo/backend/aes.py b/omemo/backend/aes.py
deleted file mode 100644
index 49d288a..0000000
--- a/omemo/backend/aes.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# Copyright (C) 2019 Philipp Hörist <philipp AT hoerist.com>
-#
-# 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 os
-import logging
-from collections import namedtuple
-
-from cryptography.hazmat.primitives.ciphers import Cipher
-from cryptography.hazmat.primitives.ciphers import algorithms
-from cryptography.hazmat.primitives.ciphers.modes import GCM
-from cryptography.hazmat.backends import default_backend
-
-log = logging.getLogger('gajim.p.omemo')
-
-EncryptionResult = namedtuple('EncryptionResult', 'payload key iv')
-
-IV_SIZE = 12
-
-def _decrypt(key, iv, tag, data):
- decryptor = Cipher(
- algorithms.AES(key),
- GCM(iv, tag=tag),
- backend=default_backend()).decryptor()
- return decryptor.update(data) + decryptor.finalize()
-
-
-def aes_decrypt(_key, iv, payload):
- if len(_key) >= 32:
- # XEP-0384
- log.debug('XEP Compliant Key/Tag')
- data = payload
- key = _key[:16]
- tag = _key[16:]
- else:
- # Legacy
- log.debug('Legacy Key/Tag')
- data = payload[:-16]
- key = _key
- tag = payload[-16:]
-
- return _decrypt(key, iv, tag, data).decode()
-
-
-def aes_decrypt_file(key, iv, payload):
- data = payload[:-16]
- tag = payload[-16:]
- return _decrypt(key, iv, tag, data)
-
-
-def _encrypt(data, key_size, iv_size=IV_SIZE):
- if isinstance(data, str):
- data = data.encode()
- key = os.urandom(key_size)
- iv = os.urandom(iv_size)
- encryptor = Cipher(
- algorithms.AES(key),
- GCM(iv),
- backend=default_backend()).encryptor()
-
- payload = encryptor.update(data) + encryptor.finalize()
- return key, iv, encryptor.tag, payload
-
-
-def aes_encrypt(plaintext):
- key, iv, tag, payload = _encrypt(plaintext, 16)
- key += tag
- return EncryptionResult(payload=payload, key=key, iv=iv)
-
-
-def aes_encrypt_file(data):
- key, iv, tag, payload, = _encrypt(data, 32)
- payload += tag
- return EncryptionResult(payload=payload, key=key, iv=iv)
-
-
-def get_new_key():
- return os.urandom(16)
-
-
-def get_new_iv():
- return os.urandom(IV_SIZE)
diff --git a/omemo/backend/devices.py b/omemo/backend/devices.py
deleted file mode 100644
index 3790785..0000000
--- a/omemo/backend/devices.py
+++ /dev/null
@@ -1,138 +0,0 @@
-# Copyright (C) 2019 Philipp Hörist <philipp AT hoerist.com>
-#
-# 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/>.
-
-from collections import defaultdict
-
-from gajim.common import app
-
-from omemo.backend.util import UNACKNOWLEDGED_COUNT
-
-
-class DeviceManager:
- def __init__(self):
- self.__device_store = defaultdict(set)
- self.__muc_member_store = defaultdict(set)
-
- reg_id = self._storage.getLocalRegistrationId()
- if reg_id is None:
- raise ValueError('No own device found')
-
- self.__own_device = reg_id
- self.add_device(self._own_jid, self.__own_device)
- self._log.info('Our device id: %s', self.__own_device)
-
- for jid, device in self._storage.getActiveDeviceTuples():
- self._log.info('Load device from storage: %s - %s', jid, device)
- self.add_device(jid, device)
-
- def update_devicelist(self, jid, devicelist):
- for device in list(devicelist):
- if device == self.own_device:
- continue
- count = self._storage.getUnacknowledgedCount(jid, device)
- if count > UNACKNOWLEDGED_COUNT:
- self._log.warning('Ignore device because of %s unacknowledged'
- ' messages: %s %s', count, jid, device)
- devicelist.remove(device)
-
- self.__device_store[jid] = set(devicelist)
- self._log.info('Saved devices for %s', jid)
- self._storage.setActiveState(jid, devicelist)
-
- def add_muc_member(self, room_jid, jid):
- self._log.info('Saved MUC member %s %s', room_jid, jid)
- self.__muc_member_store[room_jid].add(jid)
-
- def remove_muc_member(self, room_jid, jid):
- self._log.info('Removed MUC member %s %s', room_jid, jid)
- self.__muc_member_store[room_jid].discard(jid)
-
- def get_muc_members(self, room_jid, without_self=True):
- members = set(self.__muc_member_store[room_jid])
- if without_self:
- members.discard(self._own_jid)
- return members
-
- def add_device(self, jid, device):
- self.__device_store[jid].add(device)
-
- def remove_device(self, jid, device):
- self.__device_store[jid].discard(device)
- self._storage.setInactive(jid, device)
-
- def get_devices(self, jid, without_self=False):
- devices = set(self.__device_store[jid])
- if without_self:
- devices.discard(self.own_device)
- return devices
-
- def get_devices_for_encryption(self, jid):
- devices_for_encryption = []
-
- client = app.get_client(self._account)
- contact = client.get_module('Contacts').get_contact(jid)
- if contact.is_groupchat:
- devices_for_encryption = self._get_devices_for_muc_encryption(jid)
- else:
- devices_for_encryption = self._get_devices_for_encryption(jid)
-
- if not devices_for_encryption:
- raise NoDevicesFound
-
- devices_for_encryption += self._get_own_devices_for_encryption()
- return set(devices_for_encryption)
-
- def _get_devices_for_muc_encryption(self, jid):
- devices_for_encryption = []
- for jid_ in self.__muc_member_store[jid]:
- devices_for_encryption += self._get_devices_for_encryption(jid_)
- return devices_for_encryption
-
- def _get_own_devices_for_encryption(self):
- devices_for_encryption = []
- own_devices = self.get_devices(self._own_jid, without_self=True)
- for device in own_devices:
- if self._storage.isTrusted(self._own_jid, device):
- devices_for_encryption.append((self._own_jid, device))
- return devices_for_encryption
-
- def _get_devices_for_encryption(self, jid):
- devices_for_encryption = []
- devices = self.get_devices(jid)
-
- for device in devices:
- if self._storage.isTrusted(jid, device):
- devices_for_encryption.append((jid, device))
- return devices_for_encryption
-
- @property
- def own_device(self):
- return self.__own_device
-
- @property
- def devices_for_publish(self):
- devices = self.get_devices(self._own_jid)
- if self.own_device not in devices:
- devices.add(self.own_device)
- return devices
-
- @property
- def is_own_device_published(self):
- return self.own_device in self.get_devices(self._own_jid)
-
-
-class NoDevicesFound(Exception):
- pass
diff --git a/omemo/backend/liteaxolotlstore.py b/omemo/backend/liteaxolotlstore.py
deleted file mode 100644
index c1004bc..0000000
--- a/omemo/backend/liteaxolotlstore.py
+++ /dev/null
@@ -1,741 +0,0 @@
-# Copyright (C) 2019 Philipp Hörist <philipp AT hoerist.com>
-# Copyright (C) 2015 Tarek Galal <tare2.galal@gmail.com>
-#
-# 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
-import sqlite3
-from collections import namedtuple
-
-from axolotl.state.axolotlstore import AxolotlStore
-from axolotl.state.signedprekeyrecord import SignedPreKeyRecord
-from axolotl.state.sessionrecord import SessionRecord
-from axolotl.state.prekeyrecord import PreKeyRecord
-from axolotl.invalidkeyidexception import InvalidKeyIdException
-from axolotl.ecc.djbec import DjbECPrivateKey
-from axolotl.ecc.djbec import DjbECPublicKey
-from axolotl.identitykeypair import IdentityKeyPair
-from axolotl.util.medium import Medium
-from axolotl.util.keyhelper import KeyHelper
-
-from gajim.common import app
-
-from omemo.backend.util import Trust
-from omemo.backend.util import IdentityKeyExtended
-from omemo.backend.util import DEFAULT_PREKEY_AMOUNT
-
-
-def _convert_to_string(text):
- return text.decode()
-
-
-def _convert_identity_key(key):
- if not key:
- return
- return IdentityKeyExtended(DjbECPublicKey(key[1:]))
-
-
-def _convert_record(record):
- return SessionRecord(serialized=record)
-
-
-sqlite3.register_converter('pk', _convert_identity_key)
-sqlite3.register_converter('session_record', _convert_record)
-
-
-class LiteAxolotlStore(AxolotlStore):
- def __init__(self, db_path, log):
- self._log = log
- self._con = sqlite3.connect(db_path,
- detect_types=sqlite3.PARSE_COLNAMES)
- self._con.row_factory = self._namedtuple_factory
- self.createDb()
- self.migrateDb()
-
- self._con.execute("PRAGMA secure_delete=1")
- self._con.execute("PRAGMA synchronous=NORMAL;")
- mode = self._con.execute("PRAGMA journal_mode;").fetchone()[0]
-
- # WAL is a persistent DB mode, don't override it if user has set it
- if mode != 'wal':
- self._con.execute("PRAGMA journal_mode=MEMORY;")
- self._con.commit()
-
- if not self.getLocalRegistrationId():
- self._log.info("Generating OMEMO keys")
- self._generate_axolotl_keys()
-
- @staticmethod
- def _is_blind_trust_enabled():
- plugin = app.plugin_manager.get_active_plugin('omemo')
- return plugin.config['BLIND_TRUST']
-
- @staticmethod
- def _namedtuple_factory(cursor, row):
- fields = []
- for col in cursor.description:
- if col[0] == '_id':
- fields.append('id')
- elif 'strftime' in col[0]:
- fields.append('formated_time')
- elif 'MAX' in col[0] or 'COUNT' in col[0]:
- col_name = col[0].replace('(', '_')
- col_name = col_name.replace(')', '')
- fields.append(col_name.lower())
- else:
- fields.append(col[0])
- return namedtuple("Row", fields)(*row)
-
- def _generate_axolotl_keys(self):
- 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):
- return self._con.execute('PRAGMA user_version').fetchone()[0]
-
- def createDb(self):
- if self.user_version() == 0:
-
- create_tables = '''
- CREATE TABLE IF NOT EXISTS secret (
- device_id INTEGER, public_key BLOB, private_key BLOB);
-
- CREATE TABLE IF NOT EXISTS identities (
- _id INTEGER PRIMARY KEY AUTOINCREMENT, recipient_id TEXT,
- registration_id INTEGER, public_key BLOB,
- timestamp INTEGER, trust INTEGER,
- shown INTEGER DEFAULT 0);
-
- CREATE UNIQUE INDEX IF NOT EXISTS
- public_key_index ON identities (public_key, recipient_id);
-
- CREATE TABLE IF NOT EXISTS prekeys(
- _id INTEGER PRIMARY KEY AUTOINCREMENT,
- prekey_id INTEGER UNIQUE, sent_to_server BOOLEAN,
- record BLOB);
-
- CREATE TABLE IF NOT EXISTS signed_prekeys (
- _id INTEGER PRIMARY KEY AUTOINCREMENT,
- prekey_id INTEGER UNIQUE,
- timestamp NUMERIC DEFAULT CURRENT_TIMESTAMP, record BLOB);
-
- CREATE TABLE IF NOT EXISTS sessions (
- _id INTEGER PRIMARY KEY AUTOINCREMENT,
- recipient_id TEXT, device_id INTEGER,
- record BLOB, timestamp INTEGER, active INTEGER DEFAULT 1,
- UNIQUE(recipient_id, device_id));
-
- '''
-
- create_db_sql = """
- BEGIN TRANSACTION;
- %s
- PRAGMA user_version=12;
- END TRANSACTION;
- """ % (create_tables)
- self._con.executescript(create_db_sql)
-
- def migrateDb(self):
- """ Migrates the DB
- """
-
- # Find all double entries and delete them
- if self.user_version() < 2:
- delete_dupes = """ DELETE FROM identities WHERE _id not in (
- SELECT MIN(_id)
- FROM identities
- GROUP BY
- recipient_id, public_key
- );
- """
-
- self._con.executescript(
- """ BEGIN TRANSACTION;
- %s
- PRAGMA user_version=2;
- END TRANSACTION;
- """ % (delete_dupes))
-
- if self.user_version() < 3:
- # Create a UNIQUE INDEX so every public key/recipient_id tuple
- # can only be once in the db
- add_index = """ CREATE UNIQUE INDEX IF NOT EXISTS
- public_key_index
- ON identities (public_key, recipient_id);
- """
-
- self._con.executescript(
- """ BEGIN TRANSACTION;
- %s
- PRAGMA user_version=3;
- END TRANSACTION;
- """ % (add_index))
-
- if self.user_version() < 4:
- # Adds column "active" to the sessions table
- add_active = """ ALTER TABLE sessions
- ADD COLUMN active INTEGER DEFAULT 1;
- """
-
- self._con.executescript(
- """ BEGIN TRANSACTION;
- %s
- PRAGMA user_version=4;
- END TRANSACTION;
- """ % (add_active))
-
- if self.user_version() < 5:
- # Adds DEFAULT Timestamp
- add_timestamp = """
- DROP TABLE signed_prekeys;
- CREATE TABLE IF NOT EXISTS signed_prekeys (
- _id INTEGER PRIMARY KEY AUTOINCREMENT,
- prekey_id INTEGER UNIQUE,
- timestamp NUMERIC DEFAULT CURRENT_TIMESTAMP, record BLOB);
- ALTER TABLE identities ADD COLUMN shown INTEGER DEFAULT 0;
- UPDATE identities SET shown = 1;
- """
-
- self._con.executescript(
- """ BEGIN TRANSACTION;
- %s
- PRAGMA user_version=5;
- END TRANSACTION;
- """ % (add_timestamp))
-
- if self.user_version() < 6:
- # Move secret data into own table
- # We add +1 to registration id because we did that in other code in
- # earlier versions. On this migration we correct this mistake now.
- move = """
- CREATE TABLE IF NOT EXISTS secret (
- device_id INTEGER, public_key BLOB, private_key BLOB);
- INSERT INTO secret (device_id, public_key, private_key)
- SELECT registration_id + 1, public_key, private_key
- FROM identities
- WHERE recipient_id = -1;
- """
-
- self._con.executescript(
- """ BEGIN TRANSACTION;
- %s
- PRAGMA user_version=6;
- END TRANSACTION;
- """ % move)
-
- if self.user_version() < 7:
- # Convert old device ids to integer
- convert = """
- UPDATE secret SET device_id = device_id % 2147483646;
- """
-
- self._con.executescript(
- """ BEGIN TRANSACTION;
- %s
- PRAGMA user_version=7;
- END TRANSACTION;
- """ % convert)
-
- if self.user_version() < 8:
- # Sanitize invalid BLOBs from the python2 days
- query_keys = '''SELECT recipient_id,
- registration_id,
- CAST(public_key as BLOB) as public_key,
- CAST(private_key as BLOB) as private_key,
- timestamp, trust, shown
- FROM identities'''
- rows = self._con.execute(query_keys).fetchall()
-
- delete = 'DELETE FROM identities'
- self._con.execute(delete)
-
- insert = '''INSERT INTO identities (
- recipient_id, registration_id, public_key, private_key,
- timestamp, trust, shown)
- VALUES (?, ?, ?, ?, ?, ?, ?)'''
- for row in rows:
- try:
- self._con.execute(insert, row)
- except Exception as error:
- self._log.warning(error)
- self._con.execute('PRAGMA user_version=8')
- self._con.commit()
-
- if self.user_version() < 9:
- # Sanitize invalid BLOBs from the python2 days
- query_keys = '''SELECT device_id,
- CAST(public_key as BLOB) as public_key,
- CAST(private_key as BLOB) as private_key
- FROM secret'''
- rows = self._con.execute(query_keys).fetchall()
-
- delete = 'DELETE FROM secret'
- self._con.execute(delete)
-
- insert = '''INSERT INTO secret (device_id, public_key, private_key)
- VALUES (?, ?, ?)'''
- for row in rows:
- try:
- self._con.execute(insert, row)
- except Exception as error:
- self._log.warning(error)
- self._con.execute('PRAGMA user_version=9')
- self._con.commit()
-
- if self.user_version() < 10:
- # Sanitize invalid BLOBs from the python2 days
- query_keys = '''SELECT _id,
- recipient_id,
- device_id,
- CAST(record as BLOB) as record,
- timestamp,
- active
- FROM sessions'''
- rows = self._con.execute(query_keys).fetchall()
-
- delete = 'DELETE FROM sessions'
- self._con.execute(delete)
-
- insert = '''INSERT INTO sessions (_id, recipient_id, device_id,
- record, timestamp, active)
- VALUES (?, ?, ?, ?, ?, ?)'''
- for row in rows:
- try:
- self._con.execute(insert, row)
- except Exception as error:
- self._log.warning(error)
- self._con.execute('PRAGMA user_version=10')
- self._con.commit()
-
- if self.user_version() < 11:
- # Sanitize invalid BLOBs from the python2 days
- query_keys = '''SELECT _id,
- prekey_id,
- sent_to_server,
- CAST(record as BLOB) as record
- FROM prekeys'''
- rows = self._con.execute(query_keys).fetchall()
-
- delete = 'DELETE FROM prekeys'
- self._con.execute(delete)
-
- insert = '''INSERT INTO prekeys (
- _id, prekey_id, sent_to_server, record)
- VALUES (?, ?, ?, ?)'''
- for row in rows:
- try:
- self._con.execute(insert, row)
- except Exception as error:
- self._log.warning(error)
- self._con.execute('PRAGMA user_version=11')
- self._con.commit()
-
- if self.user_version() < 12:
- # Sanitize invalid BLOBs from the python2 days
- query_keys = '''SELECT _id,
- prekey_id,
- timestamp,
- CAST(record as BLOB) as record
- FROM signed_prekeys'''
- rows = self._con.execute(query_keys).fetchall()
-
- delete = 'DELETE FROM signed_prekeys'
- self._con.execute(delete)
-
- insert = '''INSERT INTO signed_prekeys (
- _id, prekey_id, timestamp, record)
- VALUES (?, ?, ?, ?)'''
- for row in rows:
- try:
- self._con.execute(insert, row)
- except Exception as error:
- self._log.warning(error)
- self._con.execute('PRAGMA user_version=12')
- self._con.commit()
-
- def loadSignedPreKey(self, signedPreKeyId):
- query = 'SELECT record FROM signed_prekeys WHERE prekey_id = ?'
- result = self._con.execute(query, (signedPreKeyId, )).fetchone()
- if result is None:
- raise InvalidKeyIdException("No such signedprekeyrecord! %s " %
- signedPreKeyId)
- return SignedPreKeyRecord(serialized=result.record)
-
- def loadSignedPreKeys(self):
- query = 'SELECT record FROM signed_prekeys'
- results = self._con.execute(query).fetchall()
- return [SignedPreKeyRecord(serialized=row.record) for row in results]
-
- def storeSignedPreKey(self, signedPreKeyId, signedPreKeyRecord):
- query = 'INSERT INTO signed_prekeys (prekey_id, record) VALUES(?,?)'
- self._con.execute(query, (signedPreKeyId,
- signedPreKeyRecord.serialize()))
- self._con.commit()
-
- def containsSignedPreKey(self, signedPreKeyId):
- query = 'SELECT record FROM signed_prekeys WHERE prekey_id = ?'
- result = self._con.execute(query, (signedPreKeyId,)).fetchone()
- return result is not None
-
- def removeSignedPreKey(self, signedPreKeyId):
- query = 'DELETE FROM signed_prekeys WHERE prekey_id = ?'
- self._con.execute(query, (signedPreKeyId,))
- self._con.commit()
-
- def getNextSignedPreKeyId(self):
- result = self.getCurrentSignedPreKeyId()
- if result is None:
- return 1 # StartId if no SignedPreKeys exist
- return (result % (Medium.MAX_VALUE - 1)) + 1
-
- def getCurrentSignedPreKeyId(self):
- 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
-
- def getSignedPreKeyTimestamp(self, signedPreKeyId):
- query = '''SELECT strftime('%s', timestamp) FROM
- signed_prekeys WHERE prekey_id = ?'''
-
- result = self._con.execute(query, (signedPreKeyId,)).fetchone()
- if result is None:
- raise InvalidKeyIdException('No such signedprekeyrecord! %s' %
- signedPreKeyId)
-
- return result.formated_time
-
- def removeOldSignedPreKeys(self, timestamp):
- query = '''DELETE FROM signed_prekeys
- WHERE timestamp < datetime(?, "unixepoch")'''
- self._con.execute(query, (timestamp,))
- self._con.commit()
-
- def loadSession(self, recipientId, deviceId):
- query = '''SELECT record as "record [session_record]"
- FROM sessions WHERE recipient_id = ? AND device_id = ?'''
- result = self._con.execute(query, (recipientId, deviceId)).fetchone()
- return result.record if result is not None else SessionRecord()
-
- def getJidFromDevice(self, device_id):
- 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):
- query = '''SELECT recipient_id, device_id
- FROM sessions WHERE active = 1'''
- return self._con.execute(query).fetchall()
-
- def storeSession(self, recipientId, deviceId, sessionRecord):
- query = '''INSERT INTO sessions(recipient_id, device_id, record)
- VALUES(?,?,?)'''
- try:
- self._con.execute(query, (recipientId,
- deviceId,
- sessionRecord.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.commit()
-
- def containsSession(self, recipientId, deviceId):
- query = '''SELECT record FROM sessions
- WHERE recipient_id = ? AND device_id = ?'''
- result = self._con.execute(query, (recipientId, deviceId)).fetchone()
- return result is not None
-
- def deleteSession(self, recipientId, deviceId):
- self._log.info('Delete session for %s %s', recipientId, deviceId)
- query = "DELETE FROM sessions WHERE recipient_id = ? AND device_id = ?"
- self._con.execute(query, (recipientId, deviceId))
- self._con.commit()
-
- def deleteAllSessions(self, recipientId):
- query = 'DELETE FROM sessions WHERE recipient_id = ?'
- self._con.execute(query, (recipientId,))
- self._con.commit()
-
- def getSessionsFromJid(self, recipientId):
- query = '''SELECT recipient_id,
- device_id,
- record as "record [session_record]",
- active
- FROM sessions WHERE recipient_id = ?'''
- return self._con.execute(query, (recipientId,)).fetchall()
-
- def getSessionsFromJids(self, recipientIds):
- query = '''SELECT recipient_id,
- device_id,
- record as "record [session_record]",
- active
- FROM sessions
- WHERE recipient_id IN ({})'''.format(
- ', '.join(['?'] * len(recipientIds)))
- return self._con.execute(query, recipientIds).fetchall()
-
- def setActiveState(self, jid, devicelist):
- query = '''UPDATE sessions SET active = 1
- WHERE recipient_id = ? AND device_id IN ({})'''.format(
- ', '.join(['?'] * len(devicelist)))
- self._con.execute(query, (jid,) + 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.commit()
-
- def setInactive(self, jid, device_id):
- query = '''UPDATE sessions SET active = 0
- WHERE recipient_id = ? AND device_id = ?'''
- self._con.execute(query, (jid, device_id))
- self._con.commit()
-
- def getInactiveSessionsKeys(self, recipientId):
- query = '''SELECT record as "record [session_record]" FROM sessions
- WHERE active = 0 AND recipient_id = ?'''
- results = self._con.execute(query, (recipientId,)).fetchall()
-
- keys = []
- for result in results:
- key = result.record.getSessionState().getRemoteIdentityKey()
- keys.append(IdentityKeyExtended(key.getPublicKey()))
- return keys
-
- def loadPreKey(self, preKeyId):
- query = '''SELECT record FROM prekeys WHERE prekey_id = ?'''
-
- result = self._con.execute(query, (preKeyId,)).fetchone()
- if result is None:
- raise Exception("No such prekeyRecord!")
- return PreKeyRecord(serialized=result.record)
-
- def loadPendingPreKeys(self):
- query = '''SELECT record FROM prekeys'''
- result = self._con.execute(query).fetchall()
- return [PreKeyRecord(serialized=row.record) for row in result]
-
- def storePreKey(self, preKeyId, preKeyRecord):
- query = 'INSERT INTO prekeys (prekey_id, record) VALUES(?,?)'
- self._con.execute(query, (preKeyId, preKeyRecord.serialize()))
- self._con.commit()
-
- def containsPreKey(self, preKeyId):
- query = 'SELECT record FROM prekeys WHERE prekey_id = ?'
- result = self._con.execute(query, (preKeyId,)).fetchone()
- return result is not None
-
- def removePreKey(self, preKeyId):
- query = 'DELETE FROM prekeys WHERE prekey_id = ?'
- self._con.execute(query, (preKeyId,))
- self._con.commit()
-
- def getCurrentPreKeyId(self):
- query = 'SELECT MAX(prekey_id) FROM prekeys'
- return self._con.execute(query).fetchone().max_prekey_id
-
- def getPreKeyCount(self):
- query = 'SELECT COUNT(prekey_id) FROM prekeys'
- return self._con.execute(query).fetchone().count_prekey_id
-
- def generateNewPreKeys(self, count):
- 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):
- query = '''SELECT public_key as "public_key [pk]", private_key
- FROM secret LIMIT 1'''
- result = self._con.execute(query).fetchone()
-
- return IdentityKeyPair(result.public_key,
- DjbECPrivateKey(result.private_key))
-
- def getLocalRegistrationId(self):
- 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
-
- def storeLocalData(self, device_id, identityKeyPair):
- query = 'SELECT * FROM secret'
- result = self._con.execute(query).fetchone()
- if result is not None:
- self._log.error('Trying to save secret key into '
- 'non-empty secret table')
- return
-
- query = '''INSERT INTO secret(device_id, public_key, private_key)
- VALUES(?, ?, ?)'''
-
- public_key = identityKeyPair.getPublicKey().getPublicKey().serialize()
- private_key = identityKeyPair.getPrivateKey().serialize()
- self._con.execute(query, (device_id, public_key, private_key))
- self._con.commit()
-
- def saveIdentity(self, recipientId, identityKey):
- 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(),
- trust,
- 1 if trust == Trust.BLIND else 0))
- self._con.commit()
-
- def containsIdentity(self, recipientId, identityKey):
- query = '''SELECT * FROM identities WHERE recipient_id = ?
- AND public_key = ?'''
-
- public_key = identityKey.getPublicKey().serialize()
- result = self._con.execute(query, (recipientId,
- public_key)).fetchone()
-
- return result is not None
-
- def deleteIdentity(self, recipientId, identityKey):
- query = '''DELETE FROM identities
- WHERE recipient_id = ? AND public_key = ?'''
- public_key = identityKey.getPublicKey().serialize()
- self._con.execute(query, (recipientId, public_key))
- self._con.commit()
-
- def isTrustedIdentity(self, recipientId, identityKey):
- return True
-
- def getTrustForIdentity(self, recipientId, identityKey):
- 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()
- return result.trust if result is not None else None
-
- def getFingerprints(self, jid):
- query = '''SELECT recipient_id,
- public_key as "public_key [pk]",
- trust,
- timestamp
- FROM identities
- WHERE recipient_id = ? ORDER BY trust ASC'''
- return self._con.execute(query, (jid,)).fetchall()
-
- def getMucFingerprints(self, jids):
- query = '''
- SELECT recipient_id,
- public_key as "public_key [pk]",
- trust,
- timestamp
- FROM identities
- WHERE recipient_id IN ({}) ORDER BY trust ASC
- '''.format(', '.join(['?'] * len(jids)))
-
- return self._con.execute(query, jids).fetchall()
-
- def hasUndecidedFingerprints(self, jid):
- query = '''SELECT public_key as "public_key [pk]" FROM identities
- WHERE recipient_id = ? AND trust = ?'''
- result = self._con.execute(query, (jid, Trust.UNDECIDED)).fetchall()
- undecided = [row.public_key for row in result]
-
- inactive = self.getInactiveSessionsKeys(jid)
- undecided = set(undecided) - set(inactive)
- return bool(undecided)
-
- def getDefaultTrust(self, jid):
- if not self._is_blind_trust_enabled():
- return Trust.UNDECIDED
-
- query = '''SELECT * FROM identities
- WHERE recipient_id = ? AND trust IN (0, 1)'''
- result = self._con.execute(query, (jid,)).fetchone()
- if result is None:
- return Trust.BLIND
- return Trust.UNDECIDED
-
- def getTrustedFingerprints(self, jid):
- 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()
- return [row.public_key for row in result]
-
- def getNewFingerprints(self, jid):
- 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):
- 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, identityKey, trust):
- query = '''UPDATE identities SET trust = ? WHERE public_key = ?
- AND recipient_id = ?'''
- public_key = identityKey.getPublicKey().serialize()
- self._con.execute(query, (trust, public_key, recipient_id))
- self._con.commit()
-
- def isTrusted(self, recipient_id, device_id):
- record = self.loadSession(recipient_id, device_id)
- if record.isFresh():
- return False
- identity_key = record.getSessionState().getRemoteIdentityKey()
- return self.getTrustForIdentity(
- recipient_id, identity_key) in (Trust.VERIFIED, Trust.BLIND)
-
- def getIdentityLastSeen(self, recipient_id, identity_key):
- identity_key = identity_key.getPublicKey().serialize()
- query = '''SELECT timestamp FROM identities
- WHERE recipient_id = ? AND public_key = ?'''
- result = self._con.execute(query, (recipient_id,
- identity_key)).fetchone()
- return result.timestamp if result is not None else None
-
- def setIdentityLastSeen(self, recipient_id, identity_key):
- timestamp = int(time.time())
- identity_key = identity_key.getPublicKey().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, identity_key))
- self._con.commit()
-
- def getUnacknowledgedCount(self, recipient_id, device_id):
- record = self.loadSession(recipient_id, device_id)
- if record.isFresh():
- return 0
- state = record.getSessionState()
- return state.getSenderChainKey().getIndex()
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
diff --git a/omemo/backend/util.py b/omemo/backend/util.py
deleted file mode 100644
index 9d07d5f..0000000
--- a/omemo/backend/util.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# 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 binascii
-import textwrap
-from enum import IntEnum
-
-from axolotl.identitykey import IdentityKey
-
-DEFAULT_PREKEY_AMOUNT = 100
-MIN_PREKEY_AMOUNT = 80
-SPK_ARCHIVE_TIME = 86400 * 15 # 15 Days
-SPK_CYCLE_TIME = 86400 # 24 Hours
-UNACKNOWLEDGED_COUNT = 2000
-
-
-class Trust(IntEnum):
- UNTRUSTED = 0
- VERIFIED = 1
- UNDECIDED = 2
- BLIND = 3
-
-
-def get_fingerprint(identity_key, formatted=False):
- public_key = identity_key.getPublicKey().serialize()
- fingerprint = binascii.hexlify(public_key).decode()[2:]
- if not formatted:
- return fingerprint
- fplen = len(fingerprint)
- wordsize = fplen // 8
- buf = ''
- for w in range(0, fplen, wordsize):
- buf += '{0} '.format(fingerprint[w:w + wordsize])
- buf = textwrap.fill(buf, width=36)
- return buf.rstrip().upper()
-
-
-class IdentityKeyExtended(IdentityKey):
- def __hash__(self):
- return hash(self.publicKey.serialize())
-
- def get_fingerprint(self, formatted=False):
- return get_fingerprint(self, formatted=formatted)