[omemo] Refactor AxolotlStore
- Merge all stores into AxolotlStore
This commit is contained in:
@@ -1,15 +0,0 @@
|
||||
''' Database helper functions '''
|
||||
|
||||
|
||||
def table_exists(db, name):
|
||||
""" Check if the specified table exists in the db. """
|
||||
|
||||
query = """ SELECT name FROM sqlite_master
|
||||
WHERE type='table' AND name=?;
|
||||
"""
|
||||
return db.execute(query, (name, )).fetchone() is not None
|
||||
|
||||
|
||||
def user_version(db):
|
||||
""" Return the value of PRAGMA user_version. """
|
||||
return db.execute('PRAGMA user_version').fetchone()[0]
|
||||
@@ -1,64 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2015 Bahtiar `kalkin-` Gadimov <bahtiar@gadimov.de>
|
||||
# Copyright 2015 Daniel Gultsch <daniel@cgultsch.de>
|
||||
#
|
||||
# This file is part of Gajim-OMEMO plugin.
|
||||
#
|
||||
# The Gajim-OMEMO 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, either version 3 of the License, or (at your option) any
|
||||
# later version.
|
||||
#
|
||||
# Gajim-OMEMO 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
|
||||
# the Gajim-OMEMO plugin. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
|
||||
class EncryptionState():
|
||||
""" Used to store if OMEMO is enabled or not between gajim restarts """
|
||||
|
||||
def __init__(self, dbConn):
|
||||
"""
|
||||
:type dbConn: Connection
|
||||
"""
|
||||
self.dbConn = dbConn
|
||||
|
||||
def activate(self, jid):
|
||||
q = """INSERT OR REPLACE INTO encryption_state (jid, encryption)
|
||||
VALUES (?, 1) """
|
||||
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, (jid, ))
|
||||
self.dbConn.commit()
|
||||
|
||||
def deactivate(self, jid):
|
||||
q = """INSERT OR REPLACE INTO encryption_state (jid, encryption)
|
||||
VALUES (?, 0)"""
|
||||
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, (jid, ))
|
||||
self.dbConn.commit()
|
||||
|
||||
def is_active(self, jid):
|
||||
q = 'SELECT encryption FROM encryption_state where jid = ?;'
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, (jid, ))
|
||||
result = c.fetchone()
|
||||
if result is None:
|
||||
return False
|
||||
return result[0]
|
||||
|
||||
def exist(self, jid):
|
||||
q = 'SELECT encryption FROM encryption_state where jid = ?;'
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, (jid, ))
|
||||
result = c.fetchone()
|
||||
if result is None:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
@@ -1,33 +1,35 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2019 Philipp Hörist <philipp AT hoerist.com>
|
||||
# Copyright (C) 2015 Tarek Galal <tare2.galal@gmail.com>
|
||||
#
|
||||
# Copyright 2015 Tarek Galal <tare2.galal@gmail.com>
|
||||
# This file is part of OMEMO Gajim Plugin.
|
||||
#
|
||||
# This file is part of Gajim-OMEMO 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.
|
||||
#
|
||||
# The Gajim-OMEMO 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, either version 3 of the License, or (at your option) any
|
||||
# later version.
|
||||
#
|
||||
# Gajim-OMEMO 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
|
||||
# the Gajim-OMEMO plugin. If not, see <http://www.gnu.org/licenses/>.
|
||||
# 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 logging
|
||||
|
||||
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.identitykey import IdentityKey
|
||||
from axolotl.identitykeypair import IdentityKeyPair
|
||||
from axolotl.util.medium import Medium
|
||||
from axolotl.util.keyhelper import KeyHelper
|
||||
|
||||
from .liteidentitykeystore import LiteIdentityKeyStore
|
||||
from .liteprekeystore import LitePreKeyStore
|
||||
from .litesessionstore import LiteSessionStore
|
||||
from .litesignedprekeystore import LiteSignedPreKeyStore
|
||||
from .encryption import EncryptionState
|
||||
from .sql import SQLDatabase
|
||||
|
||||
log = logging.getLogger('gajim.plugin_system.omemo')
|
||||
|
||||
@@ -36,24 +38,28 @@ MIN_PREKEY_AMOUNT = 80
|
||||
SPK_ARCHIVE_TIME = 86400 * 15 # 15 Days
|
||||
SPK_CYCLE_TIME = 86400 # 24 Hours
|
||||
|
||||
UNDECIDED = 2
|
||||
TRUSTED = 1
|
||||
UNTRUSTED = 0
|
||||
|
||||
|
||||
class LiteAxolotlStore(AxolotlStore):
|
||||
def __init__(self, connection):
|
||||
try:
|
||||
connection.text_factory = bytes
|
||||
except(AttributeError):
|
||||
raise AssertionError('Expected a sqlite3.Connection got ' +
|
||||
str(connection))
|
||||
|
||||
self.sql = SQLDatabase(connection)
|
||||
self.identityKeyStore = LiteIdentityKeyStore(connection)
|
||||
self.preKeyStore = LitePreKeyStore(connection)
|
||||
self.signedPreKeyStore = LiteSignedPreKeyStore(connection)
|
||||
self.sessionStore = LiteSessionStore(connection)
|
||||
self.encryptionStore = EncryptionState(connection)
|
||||
self.dbConn = connection
|
||||
self.dbConn.text_factory = bytes
|
||||
self.createDb()
|
||||
self.migrateDb()
|
||||
c = self.dbConn.cursor()
|
||||
c.execute("PRAGMA synchronous=NORMAL;")
|
||||
c.execute("PRAGMA journal_mode;")
|
||||
mode = c.fetchone()[0]
|
||||
# WAL is a persistent DB mode, don't override it if user has set it
|
||||
if mode != 'wal':
|
||||
c.execute("PRAGMA journal_mode=MEMORY;")
|
||||
self.dbConn.commit()
|
||||
|
||||
if not self.getLocalRegistrationId():
|
||||
log.info("Generating Axolotl keys")
|
||||
log.info("Generating OMEMO keys")
|
||||
self._generate_axolotl_keys()
|
||||
|
||||
def _generate_axolotl_keys(self):
|
||||
@@ -71,116 +77,542 @@ class LiteAxolotlStore(AxolotlStore):
|
||||
for preKey in preKeys:
|
||||
self.storePreKey(preKey.getId(), preKey)
|
||||
|
||||
def getIdentityKeyPair(self):
|
||||
return self.identityKeyStore.getIdentityKeyPair()
|
||||
def user_version(self):
|
||||
return self.dbConn.execute('PRAGMA user_version').fetchone()[0]
|
||||
|
||||
def storeLocalData(self, registrationId, identityKeyPair):
|
||||
self.identityKeyStore.storeLocalData(registrationId, identityKeyPair)
|
||||
def createDb(self):
|
||||
if self.user_version() == 0:
|
||||
|
||||
def getLocalRegistrationId(self):
|
||||
return self.identityKeyStore.getLocalRegistrationId()
|
||||
create_tables = '''
|
||||
CREATE TABLE IF NOT EXISTS identities (
|
||||
_id INTEGER PRIMARY KEY AUTOINCREMENT, recipient_id TEXT,
|
||||
registration_id INTEGER, public_key BLOB, private_key BLOB,
|
||||
next_prekey_id INTEGER, timestamp INTEGER, trust INTEGER,
|
||||
shown INTEGER DEFAULT 0);
|
||||
|
||||
def saveIdentity(self, recepientId, identityKey):
|
||||
self.identityKeyStore.saveIdentity(recepientId, identityKey)
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS
|
||||
public_key_index ON identities (public_key, recipient_id);
|
||||
|
||||
def deleteIdentity(self, recipientId, identityKey):
|
||||
self.identityKeyStore.deleteIdentity(recipientId, identityKey)
|
||||
CREATE TABLE IF NOT EXISTS prekeys(
|
||||
_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
prekey_id INTEGER UNIQUE, sent_to_server BOOLEAN,
|
||||
record BLOB);
|
||||
|
||||
def isTrustedIdentity(self, recepientId, identityKey):
|
||||
return self.identityKeyStore.isTrustedIdentity(recepientId,
|
||||
identityKey)
|
||||
CREATE TABLE IF NOT EXISTS signed_prekeys (
|
||||
_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
prekey_id INTEGER UNIQUE,
|
||||
timestamp NUMERIC DEFAULT CURRENT_TIMESTAMP, record BLOB);
|
||||
|
||||
def setTrust(self, identityKey, trust):
|
||||
return self.identityKeyStore.setTrust(identityKey, trust)
|
||||
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));
|
||||
|
||||
def getTrustedFingerprints(self, jid):
|
||||
return self.identityKeyStore.getTrustedFingerprints(jid)
|
||||
CREATE TABLE IF NOT EXISTS encryption_state (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
jid TEXT UNIQUE,
|
||||
encryption INTEGER
|
||||
);
|
||||
'''
|
||||
|
||||
def getUndecidedFingerprints(self, jid):
|
||||
return self.identityKeyStore.getUndecidedFingerprints(jid)
|
||||
create_db_sql = """
|
||||
BEGIN TRANSACTION;
|
||||
%s
|
||||
PRAGMA user_version=5;
|
||||
END TRANSACTION;
|
||||
""" % (create_tables)
|
||||
self.dbConn.executescript(create_db_sql)
|
||||
|
||||
def setShownFingerprints(self, jid):
|
||||
return self.identityKeyStore.setShownFingerprints(jid)
|
||||
def migrateDb(self):
|
||||
""" Migrates the DB
|
||||
"""
|
||||
|
||||
def getNewFingerprints(self, jid):
|
||||
return self.identityKeyStore.getNewFingerprints(jid)
|
||||
# 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
|
||||
);
|
||||
"""
|
||||
|
||||
def loadPreKey(self, preKeyId):
|
||||
return self.preKeyStore.loadPreKey(preKeyId)
|
||||
self.dbConn.executescript(""" BEGIN TRANSACTION;
|
||||
%s
|
||||
PRAGMA user_version=2;
|
||||
END TRANSACTION;
|
||||
""" % (delete_dupes))
|
||||
|
||||
def loadPreKeys(self):
|
||||
return self.preKeyStore.loadPendingPreKeys()
|
||||
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);
|
||||
"""
|
||||
|
||||
def storePreKey(self, preKeyId, preKeyRecord):
|
||||
self.preKeyStore.storePreKey(preKeyId, preKeyRecord)
|
||||
self.dbConn.executescript(""" BEGIN TRANSACTION;
|
||||
%s
|
||||
PRAGMA user_version=3;
|
||||
END TRANSACTION;
|
||||
""" % (add_index))
|
||||
|
||||
def containsPreKey(self, preKeyId):
|
||||
return self.preKeyStore.containsPreKey(preKeyId)
|
||||
if self.user_version() < 4:
|
||||
# Adds column "active" to the sessions table
|
||||
add_active = """ ALTER TABLE sessions
|
||||
ADD COLUMN active INTEGER DEFAULT 1;
|
||||
"""
|
||||
|
||||
def removePreKey(self, preKeyId):
|
||||
self.preKeyStore.removePreKey(preKeyId)
|
||||
self.dbConn.executescript(""" BEGIN TRANSACTION;
|
||||
%s
|
||||
PRAGMA user_version=4;
|
||||
END TRANSACTION;
|
||||
""" % (add_active))
|
||||
|
||||
def loadSession(self, recepientId, deviceId):
|
||||
return self.sessionStore.loadSession(recepientId, deviceId)
|
||||
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;
|
||||
"""
|
||||
|
||||
def getActiveDeviceTuples(self):
|
||||
return self.sessionStore.getActiveDeviceTuples()
|
||||
self.dbConn.executescript(""" BEGIN TRANSACTION;
|
||||
%s
|
||||
PRAGMA user_version=5;
|
||||
END TRANSACTION;
|
||||
""" % (add_timestamp))
|
||||
|
||||
def getInactiveSessionsKeys(self, recipientId):
|
||||
return self.sessionStore.getInactiveSessionsKeys(recipientId)
|
||||
|
||||
def getSubDeviceSessions(self, recepientId):
|
||||
# TODO Reuse this
|
||||
return self.sessionStore.getSubDeviceSessions(recepientId)
|
||||
|
||||
def getJidFromDevice(self, device_id):
|
||||
return self.sessionStore.getJidFromDevice(device_id)
|
||||
|
||||
def storeSession(self, recepientId, deviceId, sessionRecord):
|
||||
self.sessionStore.storeSession(recepientId, deviceId, sessionRecord)
|
||||
|
||||
def containsSession(self, recepientId, deviceId):
|
||||
return self.sessionStore.containsSession(recepientId, deviceId)
|
||||
|
||||
def deleteSession(self, recepientId, deviceId):
|
||||
self.sessionStore.deleteSession(recepientId, deviceId)
|
||||
|
||||
def deleteAllSessions(self, recepientId):
|
||||
self.sessionStore.deleteAllSessions(recepientId)
|
||||
|
||||
def getSessionsFromJid(self, recipientId):
|
||||
return self.sessionStore.getSessionsFromJid(recipientId)
|
||||
|
||||
def getSessionsFromJids(self, recipientId):
|
||||
return self.sessionStore.getSessionsFromJids(recipientId)
|
||||
|
||||
def getAllSessions(self):
|
||||
return self.sessionStore.getAllSessions()
|
||||
|
||||
def loadSignedPreKey(self, signedPreKeyId):
|
||||
return self.signedPreKeyStore.loadSignedPreKey(signedPreKeyId)
|
||||
q = "SELECT record FROM signed_prekeys WHERE prekey_id = ?"
|
||||
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q, (signedPreKeyId, ))
|
||||
|
||||
result = cursor.fetchone()
|
||||
if not result:
|
||||
raise InvalidKeyIdException("No such signedprekeyrecord! %s " %
|
||||
signedPreKeyId)
|
||||
|
||||
return SignedPreKeyRecord(serialized=result[0])
|
||||
|
||||
def loadSignedPreKeys(self):
|
||||
return self.signedPreKeyStore.loadSignedPreKeys()
|
||||
q = "SELECT record FROM signed_prekeys"
|
||||
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q, )
|
||||
result = cursor.fetchall()
|
||||
results = []
|
||||
for row in result:
|
||||
results.append(SignedPreKeyRecord(serialized=row[0]))
|
||||
|
||||
return results
|
||||
|
||||
def storeSignedPreKey(self, signedPreKeyId, signedPreKeyRecord):
|
||||
self.signedPreKeyStore.storeSignedPreKey(signedPreKeyId,
|
||||
signedPreKeyRecord)
|
||||
q = "INSERT INTO signed_prekeys (prekey_id, record) VALUES(?,?)"
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q, (signedPreKeyId, signedPreKeyRecord.serialize()))
|
||||
self.dbConn.commit()
|
||||
|
||||
def containsSignedPreKey(self, signedPreKeyId):
|
||||
return self.signedPreKeyStore.containsSignedPreKey(signedPreKeyId)
|
||||
q = "SELECT record FROM signed_prekeys WHERE prekey_id = ?"
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q, (signedPreKeyId, ))
|
||||
return cursor.fetchone() is not None
|
||||
|
||||
def removeSignedPreKey(self, signedPreKeyId):
|
||||
self.signedPreKeyStore.removeSignedPreKey(signedPreKeyId)
|
||||
q = "DELETE FROM signed_prekeys WHERE prekey_id = ?"
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q, (signedPreKeyId, ))
|
||||
self.dbConn.commit()
|
||||
|
||||
def getNextSignedPreKeyId(self):
|
||||
return self.signedPreKeyStore.getNextSignedPreKeyId()
|
||||
result = self.getCurrentSignedPreKeyId()
|
||||
if not result:
|
||||
return 1 # StartId if no SignedPreKeys exist
|
||||
else:
|
||||
return (result % (Medium.MAX_VALUE - 1)) + 1
|
||||
|
||||
def getCurrentSignedPreKeyId(self):
|
||||
return self.signedPreKeyStore.getCurrentSignedPreKeyId()
|
||||
q = "SELECT MAX(prekey_id) FROM signed_prekeys"
|
||||
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q)
|
||||
result = cursor.fetchone()
|
||||
if not result:
|
||||
return None
|
||||
else:
|
||||
return result[0]
|
||||
|
||||
def getSignedPreKeyTimestamp(self, signedPreKeyId):
|
||||
return self.signedPreKeyStore.getSignedPreKeyTimestamp(signedPreKeyId)
|
||||
q = "SELECT strftime('%s', timestamp) FROM " \
|
||||
"signed_prekeys WHERE prekey_id = ?"
|
||||
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q, (signedPreKeyId, ))
|
||||
|
||||
result = cursor.fetchone()
|
||||
if not result:
|
||||
raise InvalidKeyIdException("No such signedprekeyrecord! %s " %
|
||||
signedPreKeyId)
|
||||
|
||||
return result[0]
|
||||
|
||||
def removeOldSignedPreKeys(self, timestamp):
|
||||
self.signedPreKeyStore.removeOldSignedPreKeys(timestamp)
|
||||
q = "DELETE FROM signed_prekeys " \
|
||||
"WHERE timestamp < datetime(?, 'unixepoch')"
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q, (timestamp, ))
|
||||
self.dbConn.commit()
|
||||
|
||||
def loadSession(self, recipientId, deviceId):
|
||||
q = "SELECT record FROM sessions WHERE recipient_id = ? AND device_id = ?"
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, (recipientId, deviceId))
|
||||
result = c.fetchone()
|
||||
|
||||
if result:
|
||||
return SessionRecord(serialized=result[0])
|
||||
else:
|
||||
return SessionRecord()
|
||||
|
||||
def getSubDeviceSessions(self, recipientId):
|
||||
q = "SELECT device_id from sessions WHERE recipient_id = ?"
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, (recipientId, ))
|
||||
result = c.fetchall()
|
||||
|
||||
deviceIds = [r[0] for r in result]
|
||||
return deviceIds
|
||||
|
||||
def getJidFromDevice(self, device_id):
|
||||
q = "SELECT recipient_id from sessions WHERE device_id = ?"
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, (device_id, ))
|
||||
result = c.fetchone()
|
||||
|
||||
return result[0].decode('utf-8') if result else None
|
||||
|
||||
def getActiveDeviceTuples(self):
|
||||
q = "SELECT recipient_id, device_id FROM sessions WHERE active = 1"
|
||||
c = self.dbConn.cursor()
|
||||
result = []
|
||||
for row in c.execute(q):
|
||||
result.append((row[0].decode('utf-8'), row[1]))
|
||||
return result
|
||||
|
||||
def storeSession(self, recipientId, deviceId, sessionRecord):
|
||||
self.deleteSession(recipientId, deviceId)
|
||||
|
||||
q = "INSERT INTO sessions(recipient_id, device_id, record) VALUES(?,?,?)"
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, (recipientId, deviceId, sessionRecord.serialize()))
|
||||
self.dbConn.commit()
|
||||
|
||||
def containsSession(self, recipientId, deviceId):
|
||||
q = "SELECT record FROM sessions WHERE recipient_id = ? AND device_id = ?"
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, (recipientId, deviceId))
|
||||
result = c.fetchone()
|
||||
|
||||
return result is not None
|
||||
|
||||
def deleteSession(self, recipientId, deviceId):
|
||||
q = "DELETE FROM sessions WHERE recipient_id = ? AND device_id = ?"
|
||||
self.dbConn.cursor().execute(q, (recipientId, deviceId))
|
||||
self.dbConn.commit()
|
||||
|
||||
def deleteAllSessions(self, recipientId):
|
||||
q = "DELETE FROM sessions WHERE recipient_id = ?"
|
||||
self.dbConn.cursor().execute(q, (recipientId, ))
|
||||
self.dbConn.commit()
|
||||
|
||||
def getAllSessions(self):
|
||||
q = "SELECT _id, recipient_id, device_id, record, active from sessions"
|
||||
c = self.dbConn.cursor()
|
||||
result = []
|
||||
for row in c.execute(q):
|
||||
result.append((row[0], row[1].decode('utf-8'), row[2], row[3], row[4]))
|
||||
return result
|
||||
|
||||
def getSessionsFromJid(self, recipientId):
|
||||
q = "SELECT _id, recipient_id, device_id, record, active from sessions" \
|
||||
" WHERE recipient_id = ?"
|
||||
c = self.dbConn.cursor()
|
||||
result = []
|
||||
for row in c.execute(q, (recipientId,)):
|
||||
result.append((row[0], row[1].decode('utf-8'), row[2], row[3], row[4]))
|
||||
return result
|
||||
|
||||
def getSessionsFromJids(self, recipientId):
|
||||
q = "SELECT _id, recipient_id, device_id, record, active from sessions" \
|
||||
" WHERE recipient_id IN ({})" \
|
||||
.format(', '.join(['?'] * len(recipientId)))
|
||||
c = self.dbConn.cursor()
|
||||
result = []
|
||||
for row in c.execute(q, recipientId):
|
||||
result.append((row[0], row[1].decode('utf-8'), row[2], row[3], row[4]))
|
||||
return result
|
||||
|
||||
def setActiveState(self, deviceList, jid):
|
||||
c = self.dbConn.cursor()
|
||||
|
||||
q = "UPDATE sessions SET active = {} " \
|
||||
"WHERE recipient_id = '{}' AND device_id IN ({})" \
|
||||
.format(1, jid, ', '.join(['?'] * len(deviceList)))
|
||||
c.execute(q, deviceList)
|
||||
|
||||
q = "UPDATE sessions SET active = {} " \
|
||||
"WHERE recipient_id = '{}' AND device_id NOT IN ({})" \
|
||||
.format(0, jid, ', '.join(['?'] * len(deviceList)))
|
||||
c.execute(q, deviceList)
|
||||
self.dbConn.commit()
|
||||
|
||||
def getInactiveSessionsKeys(self, recipientId):
|
||||
q = "SELECT record FROM sessions WHERE active = 0 AND recipient_id = ?"
|
||||
c = self.dbConn.cursor()
|
||||
result = []
|
||||
for row in c.execute(q, (recipientId,)):
|
||||
public_key = (SessionRecord(serialized=row[0]).
|
||||
getSessionState().getRemoteIdentityKey().
|
||||
getPublicKey())
|
||||
result.append(public_key.serialize())
|
||||
return result
|
||||
|
||||
def loadPreKey(self, preKeyId):
|
||||
q = "SELECT record FROM prekeys WHERE prekey_id = ?"
|
||||
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q, (preKeyId, ))
|
||||
|
||||
result = cursor.fetchone()
|
||||
if not result:
|
||||
raise Exception("No such prekeyRecord!")
|
||||
|
||||
return PreKeyRecord(serialized=result[0])
|
||||
|
||||
def loadPendingPreKeys(self):
|
||||
q = "SELECT record FROM prekeys"
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q)
|
||||
result = cursor.fetchall()
|
||||
|
||||
return [PreKeyRecord(serialized=r[0]) for r in result]
|
||||
|
||||
def storePreKey(self, preKeyId, preKeyRecord):
|
||||
q = "INSERT INTO prekeys (prekey_id, record) VALUES(?,?)"
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q, (preKeyId, preKeyRecord.serialize()))
|
||||
self.dbConn.commit()
|
||||
|
||||
def containsPreKey(self, preKeyId):
|
||||
q = "SELECT record FROM prekeys WHERE prekey_id = ?"
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q, (preKeyId, ))
|
||||
return cursor.fetchone() is not None
|
||||
|
||||
def removePreKey(self, preKeyId):
|
||||
q = "DELETE FROM prekeys WHERE prekey_id = ?"
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q, (preKeyId, ))
|
||||
self.dbConn.commit()
|
||||
|
||||
def getCurrentPreKeyId(self):
|
||||
q = "SELECT MAX(prekey_id) FROM prekeys"
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q)
|
||||
return cursor.fetchone()[0]
|
||||
|
||||
def getPreKeyCount(self):
|
||||
q = "SELECT COUNT(prekey_id) FROM prekeys"
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q)
|
||||
return cursor.fetchone()[0]
|
||||
|
||||
def generateNewPreKeys(self, count):
|
||||
startId = self.getCurrentPreKeyId() + 1
|
||||
preKeys = KeyHelper.generatePreKeys(startId, count)
|
||||
|
||||
for preKey in preKeys:
|
||||
self.storePreKey(preKey.getId(), preKey)
|
||||
|
||||
def getIdentityKeyPair(self):
|
||||
q = "SELECT public_key, private_key FROM identities " + \
|
||||
"WHERE recipient_id = -1"
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q)
|
||||
result = c.fetchone()
|
||||
|
||||
publicKey, privateKey = result
|
||||
return IdentityKeyPair(
|
||||
IdentityKey(DjbECPublicKey(publicKey[1:])),
|
||||
DjbECPrivateKey(privateKey))
|
||||
|
||||
def getLocalRegistrationId(self):
|
||||
q = "SELECT registration_id FROM identities WHERE recipient_id = -1"
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q)
|
||||
result = c.fetchone()
|
||||
return result[0] if result else None
|
||||
|
||||
def storeLocalData(self, registrationId, identityKeyPair):
|
||||
q = "INSERT INTO identities( " + \
|
||||
"recipient_id, registration_id, public_key, private_key) " + \
|
||||
"VALUES(-1, ?, ?, ?)"
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q,
|
||||
(registrationId,
|
||||
identityKeyPair.getPublicKey().getPublicKey().serialize(),
|
||||
identityKeyPair.getPrivateKey().serialize()))
|
||||
|
||||
self.dbConn.commit()
|
||||
|
||||
def saveIdentity(self, recipientId, identityKey):
|
||||
q = "INSERT INTO identities (recipient_id, public_key, trust) " \
|
||||
"VALUES(?, ?, ?)"
|
||||
c = self.dbConn.cursor()
|
||||
|
||||
if not self.getIdentity(recipientId, identityKey):
|
||||
c.execute(q, (recipientId,
|
||||
identityKey.getPublicKey().serialize(),
|
||||
UNDECIDED))
|
||||
self.dbConn.commit()
|
||||
|
||||
def getIdentity(self, recipientId, identityKey):
|
||||
q = "SELECT * FROM identities WHERE recipient_id = ? " \
|
||||
"AND public_key = ?"
|
||||
c = self.dbConn.cursor()
|
||||
|
||||
c.execute(q, (recipientId, identityKey.getPublicKey().serialize()))
|
||||
result = c.fetchone()
|
||||
|
||||
return result is not None
|
||||
|
||||
def deleteIdentity(self, recipientId, identityKey):
|
||||
q = "DELETE FROM identities WHERE recipient_id = ? AND public_key = ?"
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, (recipientId,
|
||||
identityKey.getPublicKey().serialize()))
|
||||
self.dbConn.commit()
|
||||
|
||||
def isTrustedIdentity(self, recipientId, identityKey):
|
||||
q = "SELECT trust FROM identities WHERE recipient_id = ? " \
|
||||
"AND public_key = ?"
|
||||
c = self.dbConn.cursor()
|
||||
|
||||
c.execute(q, (recipientId, identityKey.getPublicKey().serialize()))
|
||||
result = c.fetchone()
|
||||
|
||||
states = [UNTRUSTED, TRUSTED, UNDECIDED]
|
||||
|
||||
if result and result[0] in states:
|
||||
return result[0]
|
||||
else:
|
||||
return True
|
||||
|
||||
def getAllFingerprints(self):
|
||||
q = "SELECT _id, recipient_id, public_key, trust FROM identities " \
|
||||
"WHERE recipient_id != -1 ORDER BY recipient_id ASC"
|
||||
c = self.dbConn.cursor()
|
||||
|
||||
result = []
|
||||
for row in c.execute(q):
|
||||
result.append((row[0], row[1], row[2], row[3]))
|
||||
return result
|
||||
|
||||
def getFingerprints(self, jid):
|
||||
q = "SELECT _id, recipient_id, public_key, trust FROM identities " \
|
||||
"WHERE recipient_id =? ORDER BY trust ASC"
|
||||
c = self.dbConn.cursor()
|
||||
|
||||
result = []
|
||||
c.execute(q, (jid,))
|
||||
rows = c.fetchall()
|
||||
for row in rows:
|
||||
result.append((row[0], row[1], row[2], row[3]))
|
||||
return result
|
||||
|
||||
def getTrustedFingerprints(self, jid):
|
||||
q = "SELECT public_key FROM identities WHERE recipient_id = ? AND trust = ?"
|
||||
c = self.dbConn.cursor()
|
||||
|
||||
result = []
|
||||
c.execute(q, (jid, TRUSTED))
|
||||
rows = c.fetchall()
|
||||
for row in rows:
|
||||
result.append(row[0])
|
||||
return result
|
||||
|
||||
def getUndecidedFingerprints(self, jid):
|
||||
q = "SELECT trust FROM identities WHERE recipient_id = ? AND trust = ?"
|
||||
c = self.dbConn.cursor()
|
||||
|
||||
result = []
|
||||
c.execute(q, (jid, UNDECIDED))
|
||||
result = c.fetchall()
|
||||
|
||||
return result
|
||||
|
||||
def getNewFingerprints(self, jid):
|
||||
q = "SELECT _id FROM identities WHERE shown = 0 AND " \
|
||||
"recipient_id = ?"
|
||||
c = self.dbConn.cursor()
|
||||
result = []
|
||||
for row in c.execute(q, (jid,)):
|
||||
result.append(row[0])
|
||||
return result
|
||||
|
||||
def setShownFingerprints(self, fingerprints):
|
||||
q = "UPDATE identities SET shown = 1 WHERE _id IN ({})" \
|
||||
.format(', '.join(['?'] * len(fingerprints)))
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, fingerprints)
|
||||
self.dbConn.commit()
|
||||
|
||||
def setTrust(self, identityKey, trust):
|
||||
q = "UPDATE identities SET trust = ? WHERE public_key = ?"
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, (trust, identityKey.getPublicKey().serialize()))
|
||||
self.dbConn.commit()
|
||||
|
||||
def activate(self, jid):
|
||||
q = """INSERT OR REPLACE INTO encryption_state (jid, encryption)
|
||||
VALUES (?, 1) """
|
||||
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, (jid, ))
|
||||
self.dbConn.commit()
|
||||
|
||||
def deactivate(self, jid):
|
||||
q = """INSERT OR REPLACE INTO encryption_state (jid, encryption)
|
||||
VALUES (?, 0)"""
|
||||
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, (jid, ))
|
||||
self.dbConn.commit()
|
||||
|
||||
def is_active(self, jid):
|
||||
q = 'SELECT encryption FROM encryption_state where jid = ?;'
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, (jid, ))
|
||||
result = c.fetchone()
|
||||
if result is None:
|
||||
return False
|
||||
return result[0]
|
||||
|
||||
def exist(self, jid):
|
||||
q = 'SELECT encryption FROM encryption_state where jid = ?;'
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, (jid, ))
|
||||
result = c.fetchone()
|
||||
if result is None:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2015 Tarek Galal <tare2.galal@gmail.com>
|
||||
#
|
||||
# This file is part of Gajim-OMEMO plugin.
|
||||
#
|
||||
# The Gajim-OMEMO 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, either version 3 of the License, or (at your option) any
|
||||
# later version.
|
||||
#
|
||||
# Gajim-OMEMO 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
|
||||
# the Gajim-OMEMO plugin. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
from axolotl.ecc.djbec import DjbECPrivateKey, DjbECPublicKey
|
||||
from axolotl.identitykey import IdentityKey
|
||||
from axolotl.identitykeypair import IdentityKeyPair
|
||||
from axolotl.state.identitykeystore import IdentityKeyStore
|
||||
|
||||
UNDECIDED = 2
|
||||
TRUSTED = 1
|
||||
UNTRUSTED = 0
|
||||
|
||||
|
||||
class LiteIdentityKeyStore(IdentityKeyStore):
|
||||
def __init__(self, dbConn):
|
||||
"""
|
||||
:type dbConn: Connection
|
||||
"""
|
||||
self.dbConn = dbConn
|
||||
|
||||
def getIdentityKeyPair(self):
|
||||
q = "SELECT public_key, private_key FROM identities " + \
|
||||
"WHERE recipient_id = -1"
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q)
|
||||
result = c.fetchone()
|
||||
|
||||
publicKey, privateKey = result
|
||||
return IdentityKeyPair(
|
||||
IdentityKey(DjbECPublicKey(publicKey[1:])),
|
||||
DjbECPrivateKey(privateKey))
|
||||
|
||||
def getLocalRegistrationId(self):
|
||||
q = "SELECT registration_id FROM identities WHERE recipient_id = -1"
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q)
|
||||
result = c.fetchone()
|
||||
return result[0] if result else None
|
||||
|
||||
def storeLocalData(self, registrationId, identityKeyPair):
|
||||
q = "INSERT INTO identities( " + \
|
||||
"recipient_id, registration_id, public_key, private_key) " + \
|
||||
"VALUES(-1, ?, ?, ?)"
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q,
|
||||
(registrationId,
|
||||
identityKeyPair.getPublicKey().getPublicKey().serialize(),
|
||||
identityKeyPair.getPrivateKey().serialize()))
|
||||
|
||||
self.dbConn.commit()
|
||||
|
||||
def saveIdentity(self, recipientId, identityKey):
|
||||
q = "INSERT INTO identities (recipient_id, public_key, trust) " \
|
||||
"VALUES(?, ?, ?)"
|
||||
c = self.dbConn.cursor()
|
||||
|
||||
if not self.getIdentity(recipientId, identityKey):
|
||||
c.execute(q, (recipientId,
|
||||
identityKey.getPublicKey().serialize(),
|
||||
UNDECIDED))
|
||||
self.dbConn.commit()
|
||||
|
||||
def getIdentity(self, recipientId, identityKey):
|
||||
q = "SELECT * FROM identities WHERE recipient_id = ? " \
|
||||
"AND public_key = ?"
|
||||
c = self.dbConn.cursor()
|
||||
|
||||
c.execute(q, (recipientId, identityKey.getPublicKey().serialize()))
|
||||
result = c.fetchone()
|
||||
|
||||
return result is not None
|
||||
|
||||
def deleteIdentity(self, recipientId, identityKey):
|
||||
q = "DELETE FROM identities WHERE recipient_id = ? AND public_key = ?"
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, (recipientId,
|
||||
identityKey.getPublicKey().serialize()))
|
||||
self.dbConn.commit()
|
||||
|
||||
def isTrustedIdentity(self, recipientId, identityKey):
|
||||
q = "SELECT trust FROM identities WHERE recipient_id = ? " \
|
||||
"AND public_key = ?"
|
||||
c = self.dbConn.cursor()
|
||||
|
||||
c.execute(q, (recipientId, identityKey.getPublicKey().serialize()))
|
||||
result = c.fetchone()
|
||||
|
||||
states = [UNTRUSTED, TRUSTED, UNDECIDED]
|
||||
|
||||
if result and result[0] in states:
|
||||
return result[0]
|
||||
else:
|
||||
return True
|
||||
|
||||
def getAllFingerprints(self):
|
||||
q = "SELECT _id, recipient_id, public_key, trust FROM identities " \
|
||||
"WHERE recipient_id != -1 ORDER BY recipient_id ASC"
|
||||
c = self.dbConn.cursor()
|
||||
|
||||
result = []
|
||||
for row in c.execute(q):
|
||||
result.append((row[0], row[1], row[2], row[3]))
|
||||
return result
|
||||
|
||||
def getFingerprints(self, jid):
|
||||
q = "SELECT _id, recipient_id, public_key, trust FROM identities " \
|
||||
"WHERE recipient_id =? ORDER BY trust ASC"
|
||||
c = self.dbConn.cursor()
|
||||
|
||||
result = []
|
||||
c.execute(q, (jid,))
|
||||
rows = c.fetchall()
|
||||
for row in rows:
|
||||
result.append((row[0], row[1], row[2], row[3]))
|
||||
return result
|
||||
|
||||
def getTrustedFingerprints(self, jid):
|
||||
q = "SELECT public_key FROM identities WHERE recipient_id = ? AND trust = ?"
|
||||
c = self.dbConn.cursor()
|
||||
|
||||
result = []
|
||||
c.execute(q, (jid, TRUSTED))
|
||||
rows = c.fetchall()
|
||||
for row in rows:
|
||||
result.append(row[0])
|
||||
return result
|
||||
|
||||
def getUndecidedFingerprints(self, jid):
|
||||
q = "SELECT trust FROM identities WHERE recipient_id = ? AND trust = ?"
|
||||
c = self.dbConn.cursor()
|
||||
|
||||
result = []
|
||||
c.execute(q, (jid, UNDECIDED))
|
||||
result = c.fetchall()
|
||||
|
||||
return result
|
||||
|
||||
def getNewFingerprints(self, jid):
|
||||
q = "SELECT _id FROM identities WHERE shown = 0 AND " \
|
||||
"recipient_id = ?"
|
||||
c = self.dbConn.cursor()
|
||||
result = []
|
||||
for row in c.execute(q, (jid,)):
|
||||
result.append(row[0])
|
||||
return result
|
||||
|
||||
def setShownFingerprints(self, fingerprints):
|
||||
q = "UPDATE identities SET shown = 1 WHERE _id IN ({})" \
|
||||
.format(', '.join(['?'] * len(fingerprints)))
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, fingerprints)
|
||||
self.dbConn.commit()
|
||||
|
||||
def setTrust(self, identityKey, trust):
|
||||
q = "UPDATE identities SET trust = ? WHERE public_key = ?"
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, (trust, identityKey.getPublicKey().serialize()))
|
||||
self.dbConn.commit()
|
||||
@@ -1,87 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2015 Tarek Galal <tare2.galal@gmail.com>
|
||||
#
|
||||
# This file is part of Gajim-OMEMO plugin.
|
||||
#
|
||||
# The Gajim-OMEMO 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, either version 3 of the License, or (at your option) any
|
||||
# later version.
|
||||
#
|
||||
# Gajim-OMEMO 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
|
||||
# the Gajim-OMEMO plugin. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
from axolotl.state.prekeyrecord import PreKeyRecord
|
||||
from axolotl.state.prekeystore import PreKeyStore
|
||||
from axolotl.util.keyhelper import KeyHelper
|
||||
|
||||
|
||||
class LitePreKeyStore(PreKeyStore):
|
||||
def __init__(self, dbConn):
|
||||
"""
|
||||
:type dbConn: Connection
|
||||
"""
|
||||
self.dbConn = dbConn
|
||||
|
||||
def loadPreKey(self, preKeyId):
|
||||
q = "SELECT record FROM prekeys WHERE prekey_id = ?"
|
||||
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q, (preKeyId, ))
|
||||
|
||||
result = cursor.fetchone()
|
||||
if not result:
|
||||
raise Exception("No such prekeyRecord!")
|
||||
|
||||
return PreKeyRecord(serialized=result[0])
|
||||
|
||||
def loadPendingPreKeys(self):
|
||||
q = "SELECT record FROM prekeys"
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q)
|
||||
result = cursor.fetchall()
|
||||
|
||||
return [PreKeyRecord(serialized=r[0]) for r in result]
|
||||
|
||||
def storePreKey(self, preKeyId, preKeyRecord):
|
||||
q = "INSERT INTO prekeys (prekey_id, record) VALUES(?,?)"
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q, (preKeyId, preKeyRecord.serialize()))
|
||||
self.dbConn.commit()
|
||||
|
||||
def containsPreKey(self, preKeyId):
|
||||
q = "SELECT record FROM prekeys WHERE prekey_id = ?"
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q, (preKeyId, ))
|
||||
return cursor.fetchone() is not None
|
||||
|
||||
def removePreKey(self, preKeyId):
|
||||
q = "DELETE FROM prekeys WHERE prekey_id = ?"
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q, (preKeyId, ))
|
||||
self.dbConn.commit()
|
||||
|
||||
def getCurrentPreKeyId(self):
|
||||
q = "SELECT MAX(prekey_id) FROM prekeys"
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q)
|
||||
return cursor.fetchone()[0]
|
||||
|
||||
def getPreKeyCount(self):
|
||||
q = "SELECT COUNT(prekey_id) FROM prekeys"
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q)
|
||||
return cursor.fetchone()[0]
|
||||
|
||||
def generateNewPreKeys(self, count):
|
||||
startId = self.getCurrentPreKeyId() + 1
|
||||
preKeys = KeyHelper.generatePreKeys(startId, count)
|
||||
|
||||
for preKey in preKeys:
|
||||
self.storePreKey(preKey.getId(), preKey)
|
||||
@@ -1,143 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2015 Tarek Galal <tare2.galal@gmail.com>
|
||||
#
|
||||
# This file is part of Gajim-OMEMO plugin.
|
||||
#
|
||||
# The Gajim-OMEMO 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, either version 3 of the License, or (at your option) any
|
||||
# later version.
|
||||
#
|
||||
# Gajim-OMEMO 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
|
||||
# the Gajim-OMEMO plugin. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
from axolotl.state.sessionrecord import SessionRecord
|
||||
from axolotl.state.sessionstore import SessionStore
|
||||
|
||||
|
||||
class LiteSessionStore(SessionStore):
|
||||
def __init__(self, dbConn):
|
||||
"""
|
||||
:type dbConn: Connection
|
||||
"""
|
||||
self.dbConn = dbConn
|
||||
|
||||
def loadSession(self, recipientId, deviceId):
|
||||
q = "SELECT record FROM sessions WHERE recipient_id = ? AND device_id = ?"
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, (recipientId, deviceId))
|
||||
result = c.fetchone()
|
||||
|
||||
if result:
|
||||
return SessionRecord(serialized=result[0])
|
||||
else:
|
||||
return SessionRecord()
|
||||
|
||||
def getSubDeviceSessions(self, recipientId):
|
||||
q = "SELECT device_id from sessions WHERE recipient_id = ?"
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, (recipientId, ))
|
||||
result = c.fetchall()
|
||||
|
||||
deviceIds = [r[0] for r in result]
|
||||
return deviceIds
|
||||
|
||||
def getJidFromDevice(self, device_id):
|
||||
q = "SELECT recipient_id from sessions WHERE device_id = ?"
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, (device_id, ))
|
||||
result = c.fetchone()
|
||||
|
||||
return result[0].decode('utf-8') if result else None
|
||||
|
||||
def getActiveDeviceTuples(self):
|
||||
q = "SELECT recipient_id, device_id FROM sessions WHERE active = 1"
|
||||
c = self.dbConn.cursor()
|
||||
result = []
|
||||
for row in c.execute(q):
|
||||
result.append((row[0].decode('utf-8'), row[1]))
|
||||
return result
|
||||
|
||||
def storeSession(self, recipientId, deviceId, sessionRecord):
|
||||
self.deleteSession(recipientId, deviceId)
|
||||
|
||||
q = "INSERT INTO sessions(recipient_id, device_id, record) VALUES(?,?,?)"
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, (recipientId, deviceId, sessionRecord.serialize()))
|
||||
self.dbConn.commit()
|
||||
|
||||
def containsSession(self, recipientId, deviceId):
|
||||
q = "SELECT record FROM sessions WHERE recipient_id = ? AND device_id = ?"
|
||||
c = self.dbConn.cursor()
|
||||
c.execute(q, (recipientId, deviceId))
|
||||
result = c.fetchone()
|
||||
|
||||
return result is not None
|
||||
|
||||
def deleteSession(self, recipientId, deviceId):
|
||||
q = "DELETE FROM sessions WHERE recipient_id = ? AND device_id = ?"
|
||||
self.dbConn.cursor().execute(q, (recipientId, deviceId))
|
||||
self.dbConn.commit()
|
||||
|
||||
def deleteAllSessions(self, recipientId):
|
||||
q = "DELETE FROM sessions WHERE recipient_id = ?"
|
||||
self.dbConn.cursor().execute(q, (recipientId, ))
|
||||
self.dbConn.commit()
|
||||
|
||||
def getAllSessions(self):
|
||||
q = "SELECT _id, recipient_id, device_id, record, active from sessions"
|
||||
c = self.dbConn.cursor()
|
||||
result = []
|
||||
for row in c.execute(q):
|
||||
result.append((row[0], row[1].decode('utf-8'), row[2], row[3], row[4]))
|
||||
return result
|
||||
|
||||
def getSessionsFromJid(self, recipientId):
|
||||
q = "SELECT _id, recipient_id, device_id, record, active from sessions" \
|
||||
" WHERE recipient_id = ?"
|
||||
c = self.dbConn.cursor()
|
||||
result = []
|
||||
for row in c.execute(q, (recipientId,)):
|
||||
result.append((row[0], row[1].decode('utf-8'), row[2], row[3], row[4]))
|
||||
return result
|
||||
|
||||
def getSessionsFromJids(self, recipientId):
|
||||
q = "SELECT _id, recipient_id, device_id, record, active from sessions" \
|
||||
" WHERE recipient_id IN ({})" \
|
||||
.format(', '.join(['?'] * len(recipientId)))
|
||||
c = self.dbConn.cursor()
|
||||
result = []
|
||||
for row in c.execute(q, recipientId):
|
||||
result.append((row[0], row[1].decode('utf-8'), row[2], row[3], row[4]))
|
||||
return result
|
||||
|
||||
def setActiveState(self, deviceList, jid):
|
||||
c = self.dbConn.cursor()
|
||||
|
||||
q = "UPDATE sessions SET active = {} " \
|
||||
"WHERE recipient_id = '{}' AND device_id IN ({})" \
|
||||
.format(1, jid, ', '.join(['?'] * len(deviceList)))
|
||||
c.execute(q, deviceList)
|
||||
|
||||
q = "UPDATE sessions SET active = {} " \
|
||||
"WHERE recipient_id = '{}' AND device_id NOT IN ({})" \
|
||||
.format(0, jid, ', '.join(['?'] * len(deviceList)))
|
||||
c.execute(q, deviceList)
|
||||
self.dbConn.commit()
|
||||
|
||||
def getInactiveSessionsKeys(self, recipientId):
|
||||
q = "SELECT record FROM sessions WHERE active = 0 AND recipient_id = ?"
|
||||
c = self.dbConn.cursor()
|
||||
result = []
|
||||
for row in c.execute(q, (recipientId,)):
|
||||
public_key = (SessionRecord(serialized=row[0]).
|
||||
getSessionState().getRemoteIdentityKey().
|
||||
getPublicKey())
|
||||
result.append(public_key.serialize())
|
||||
return result
|
||||
@@ -1,113 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2015 Tarek Galal <tare2.galal@gmail.com>
|
||||
#
|
||||
# This file is part of Gajim-OMEMO plugin.
|
||||
#
|
||||
# The Gajim-OMEMO 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, either version 3 of the License, or (at your option) any
|
||||
# later version.
|
||||
#
|
||||
# Gajim-OMEMO 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
|
||||
# the Gajim-OMEMO plugin. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
from axolotl.invalidkeyidexception import InvalidKeyIdException
|
||||
from axolotl.state.signedprekeyrecord import SignedPreKeyRecord
|
||||
from axolotl.state.signedprekeystore import SignedPreKeyStore
|
||||
from axolotl.util.medium import Medium
|
||||
|
||||
|
||||
class LiteSignedPreKeyStore(SignedPreKeyStore):
|
||||
def __init__(self, dbConn):
|
||||
"""
|
||||
:type dbConn: Connection
|
||||
"""
|
||||
self.dbConn = dbConn
|
||||
|
||||
def loadSignedPreKey(self, signedPreKeyId):
|
||||
q = "SELECT record FROM signed_prekeys WHERE prekey_id = ?"
|
||||
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q, (signedPreKeyId, ))
|
||||
|
||||
result = cursor.fetchone()
|
||||
if not result:
|
||||
raise InvalidKeyIdException("No such signedprekeyrecord! %s " %
|
||||
signedPreKeyId)
|
||||
|
||||
return SignedPreKeyRecord(serialized=result[0])
|
||||
|
||||
def loadSignedPreKeys(self):
|
||||
q = "SELECT record FROM signed_prekeys"
|
||||
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q, )
|
||||
result = cursor.fetchall()
|
||||
results = []
|
||||
for row in result:
|
||||
results.append(SignedPreKeyRecord(serialized=row[0]))
|
||||
|
||||
return results
|
||||
|
||||
def storeSignedPreKey(self, signedPreKeyId, signedPreKeyRecord):
|
||||
q = "INSERT INTO signed_prekeys (prekey_id, record) VALUES(?,?)"
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q, (signedPreKeyId, signedPreKeyRecord.serialize()))
|
||||
self.dbConn.commit()
|
||||
|
||||
def containsSignedPreKey(self, signedPreKeyId):
|
||||
q = "SELECT record FROM signed_prekeys WHERE prekey_id = ?"
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q, (signedPreKeyId, ))
|
||||
return cursor.fetchone() is not None
|
||||
|
||||
def removeSignedPreKey(self, signedPreKeyId):
|
||||
q = "DELETE FROM signed_prekeys WHERE prekey_id = ?"
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q, (signedPreKeyId, ))
|
||||
self.dbConn.commit()
|
||||
|
||||
def getNextSignedPreKeyId(self):
|
||||
result = self.getCurrentSignedPreKeyId()
|
||||
if not result:
|
||||
return 1 # StartId if no SignedPreKeys exist
|
||||
else:
|
||||
return (result % (Medium.MAX_VALUE - 1)) + 1
|
||||
|
||||
def getCurrentSignedPreKeyId(self):
|
||||
q = "SELECT MAX(prekey_id) FROM signed_prekeys"
|
||||
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q)
|
||||
result = cursor.fetchone()
|
||||
if not result:
|
||||
return None
|
||||
else:
|
||||
return result[0]
|
||||
|
||||
def getSignedPreKeyTimestamp(self, signedPreKeyId):
|
||||
q = "SELECT strftime('%s', timestamp) FROM " \
|
||||
"signed_prekeys WHERE prekey_id = ?"
|
||||
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q, (signedPreKeyId, ))
|
||||
|
||||
result = cursor.fetchone()
|
||||
if not result:
|
||||
raise InvalidKeyIdException("No such signedprekeyrecord! %s " %
|
||||
signedPreKeyId)
|
||||
|
||||
return result[0]
|
||||
|
||||
def removeOldSignedPreKeys(self, timestamp):
|
||||
q = "DELETE FROM signed_prekeys " \
|
||||
"WHERE timestamp < datetime(?, 'unixepoch')"
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(q, (timestamp, ))
|
||||
self.dbConn.commit()
|
||||
@@ -1,154 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2015 Tarek Galal <tare2.galal@gmail.com>
|
||||
#
|
||||
# This file is part of Gajim-OMEMO plugin.
|
||||
#
|
||||
# The Gajim-OMEMO 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, either version 3 of the License, or (at your option) any
|
||||
# later version.
|
||||
#
|
||||
# Gajim-OMEMO 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
|
||||
# the Gajim-OMEMO plugin. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from .db_helpers import user_version
|
||||
|
||||
|
||||
class SQLDatabase():
|
||||
""" SQL Database """
|
||||
|
||||
def __init__(self, dbConn):
|
||||
"""
|
||||
:type dbConn: Connection
|
||||
"""
|
||||
self.dbConn = dbConn
|
||||
self.createDb()
|
||||
self.migrateDb()
|
||||
c = self.dbConn.cursor()
|
||||
c.execute("PRAGMA synchronous=NORMAL;")
|
||||
c.execute("PRAGMA journal_mode;")
|
||||
mode = c.fetchone()[0]
|
||||
# WAL is a persistent DB mode, don't override it if user has set it
|
||||
if mode != 'wal':
|
||||
c.execute("PRAGMA journal_mode=MEMORY;")
|
||||
self.dbConn.commit()
|
||||
|
||||
def createDb(self):
|
||||
if user_version(self.dbConn) == 0:
|
||||
|
||||
# Creates
|
||||
# IdentityKeyStore
|
||||
# PreKeyStore
|
||||
# SignedPreKeyStore
|
||||
# SessionStore
|
||||
# EncryptionStore
|
||||
|
||||
create_tables = '''
|
||||
CREATE TABLE IF NOT EXISTS identities (
|
||||
_id INTEGER PRIMARY KEY AUTOINCREMENT, recipient_id TEXT,
|
||||
registration_id INTEGER, public_key BLOB, private_key BLOB,
|
||||
next_prekey_id INTEGER, 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 TABLE IF NOT EXISTS encryption_state (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
jid TEXT UNIQUE,
|
||||
encryption INTEGER
|
||||
);
|
||||
'''
|
||||
|
||||
create_db_sql = """
|
||||
BEGIN TRANSACTION;
|
||||
%s
|
||||
PRAGMA user_version=5;
|
||||
END TRANSACTION;
|
||||
""" % (create_tables)
|
||||
self.dbConn.executescript(create_db_sql)
|
||||
|
||||
def migrateDb(self):
|
||||
""" Migrates the DB
|
||||
"""
|
||||
|
||||
# Find all double entries and delete them
|
||||
if user_version(self.dbConn) < 2:
|
||||
delete_dupes = """ DELETE FROM identities WHERE _id not in (
|
||||
SELECT MIN(_id)
|
||||
FROM identities
|
||||
GROUP BY
|
||||
recipient_id, public_key
|
||||
);
|
||||
"""
|
||||
|
||||
self.dbConn.executescript(""" BEGIN TRANSACTION;
|
||||
%s
|
||||
PRAGMA user_version=2;
|
||||
END TRANSACTION;
|
||||
""" % (delete_dupes))
|
||||
|
||||
if user_version(self.dbConn) < 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.dbConn.executescript(""" BEGIN TRANSACTION;
|
||||
%s
|
||||
PRAGMA user_version=3;
|
||||
END TRANSACTION;
|
||||
""" % (add_index))
|
||||
|
||||
if user_version(self.dbConn) < 4:
|
||||
# Adds column "active" to the sessions table
|
||||
add_active = """ ALTER TABLE sessions
|
||||
ADD COLUMN active INTEGER DEFAULT 1;
|
||||
"""
|
||||
|
||||
self.dbConn.executescript(""" BEGIN TRANSACTION;
|
||||
%s
|
||||
PRAGMA user_version=4;
|
||||
END TRANSACTION;
|
||||
""" % (add_active))
|
||||
|
||||
if user_version(self.dbConn) < 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.dbConn.executescript(""" BEGIN TRANSACTION;
|
||||
%s
|
||||
PRAGMA user_version=5;
|
||||
END TRANSACTION;
|
||||
""" % (add_timestamp))
|
||||
@@ -1,21 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2019 Philipp Hörist <philipp AT hoerist.com>
|
||||
# Copyright (C) 2015 Bahtiar `kalkin-` Gadimov <bahtiar@gadimov.de>
|
||||
#
|
||||
# Copyright 2015 Bahtiar `kalkin-` Gadimov <bahtiar@gadimov.de>
|
||||
# This file is part of OMEMO Gajim Plugin.
|
||||
#
|
||||
# This file is part of Gajim-OMEMO 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.
|
||||
#
|
||||
# The Gajim-OMEMO 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, either version 3 of the License, or (at your option) any
|
||||
# later version.
|
||||
#
|
||||
# Gajim-OMEMO 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
|
||||
# the Gajim-OMEMO plugin. If not, see <http://www.gnu.org/licenses/>.
|
||||
# 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 logging
|
||||
import time
|
||||
@@ -35,9 +33,12 @@ from axolotl.state.prekeybundle import PreKeyBundle
|
||||
from axolotl.util.keyhelper import KeyHelper
|
||||
|
||||
from omemo.backend.aes import aes_decrypt, aes_encrypt
|
||||
from .liteaxolotlstore import (LiteAxolotlStore, DEFAULT_PREKEY_AMOUNT,
|
||||
MIN_PREKEY_AMOUNT, SPK_CYCLE_TIME,
|
||||
SPK_ARCHIVE_TIME)
|
||||
from omemo.backend.liteaxolotlstore import LiteAxolotlStore
|
||||
from omemo.backend.liteaxolotlstore import DEFAULT_PREKEY_AMOUNT
|
||||
from omemo.backend.liteaxolotlstore import MIN_PREKEY_AMOUNT
|
||||
from omemo.backend.liteaxolotlstore import SPK_CYCLE_TIME
|
||||
from omemo.backend.liteaxolotlstore import SPK_ARCHIVE_TIME
|
||||
|
||||
|
||||
log = logging.getLogger('gajim.plugin_system.omemo')
|
||||
|
||||
@@ -49,10 +50,6 @@ UNDECIDED = 2
|
||||
|
||||
class OmemoState:
|
||||
def __init__(self, own_jid, db_con, account, xmpp_con):
|
||||
""" Instantiates an OmemoState object.
|
||||
|
||||
:param connection: an :py:class:`sqlite3.Connection`
|
||||
"""
|
||||
self.account = account
|
||||
self.xmpp_con = xmpp_con
|
||||
self.session_ciphers = {}
|
||||
@@ -60,20 +57,19 @@ class OmemoState:
|
||||
self.device_ids = {}
|
||||
self.own_devices = []
|
||||
self.store = LiteAxolotlStore(db_con)
|
||||
self.encryption = self.store.encryptionStore
|
||||
for jid, device_id in self.store.getActiveDeviceTuples():
|
||||
if jid != own_jid:
|
||||
self.add_device(jid, device_id)
|
||||
else:
|
||||
self.add_own_device(device_id)
|
||||
|
||||
log.info(self.account + ' => Roster devices after boot:' +
|
||||
str(self.device_ids))
|
||||
log.info(self.account + ' => Own devices after boot:' +
|
||||
str(self.own_devices))
|
||||
log.debug(self.account + ' => ' +
|
||||
str(self.store.preKeyStore.getPreKeyCount()) +
|
||||
' PreKeys available')
|
||||
log.info('%s => Roster devices after boot: %s',
|
||||
self.account, self.device_ids)
|
||||
log.info('%s => Own devices after boot: %s',
|
||||
self.account, self.own_devices)
|
||||
log.debug('%s => %s PreKeys available',
|
||||
self.account,
|
||||
self.store.getPreKeyCount())
|
||||
|
||||
def build_session(self, recipient_id, device_id, bundle):
|
||||
sessionBuilder = SessionBuilder(self.store, self.store, self.store,
|
||||
@@ -110,7 +106,7 @@ class OmemoState:
|
||||
"""
|
||||
|
||||
self.device_ids[name] = devices
|
||||
log.info(self.account + ' => Saved devices for ' + name)
|
||||
log.info('%s => Saved devices for %s', self.account, name)
|
||||
|
||||
def add_device(self, name, device_id):
|
||||
if name not in self.device_ids:
|
||||
@@ -128,7 +124,7 @@ class OmemoState:
|
||||
A list of device_ids
|
||||
"""
|
||||
self.own_devices = devices
|
||||
log.info(self.account + ' => Saved own devices')
|
||||
log.info('%s => Saved own devices', self.account)
|
||||
|
||||
def add_own_device(self, device_id):
|
||||
if device_id not in self.own_devices:
|
||||
@@ -140,7 +136,7 @@ class OmemoState:
|
||||
assert reg_id is not None, \
|
||||
"Requested device_id but there is no generated"
|
||||
|
||||
return ((reg_id % 2147483646) + 1)
|
||||
return (reg_id % 2147483646) + 1
|
||||
|
||||
def own_device_id_published(self):
|
||||
""" Return `True` only if own device id was added via
|
||||
@@ -153,7 +149,7 @@ class OmemoState:
|
||||
self.checkPreKeyAmount()
|
||||
|
||||
bundle = {'otpks': []}
|
||||
for k in self.store.loadPreKeys():
|
||||
for k in self.store.loadPendingPreKeys():
|
||||
key = k.getKeyPair().getPublicKey().serialize()
|
||||
bundle['otpks'].append({'key': key, 'id': k.getId()})
|
||||
|
||||
@@ -261,7 +257,7 @@ class OmemoState:
|
||||
|
||||
devices_list = self.device_list_for(jid, True)
|
||||
|
||||
result = aes_encrypt(plaintext, append_tag=True)
|
||||
result = aes_encrypt(plaintext)
|
||||
|
||||
for tup in devices_list:
|
||||
self.get_session_cipher(tup[0], tup[1])
|
||||
@@ -382,8 +378,8 @@ class OmemoState:
|
||||
for dev in known_devices
|
||||
if not self.store.containsSession(jid, dev)]
|
||||
if missing_devices:
|
||||
log.info(self.account + ' => Missing device sessions for ' +
|
||||
jid + ': ' + str(missing_devices))
|
||||
log.info('%s => Missing device sessions for %s: %s',
|
||||
self.account, jid, missing_devices)
|
||||
return missing_devices
|
||||
|
||||
def get_session_cipher(self, jid, device_id):
|
||||
@@ -400,44 +396,43 @@ class OmemoState:
|
||||
def handlePreKeyWhisperMessage(self, recipient_id, device_id, key):
|
||||
preKeyWhisperMessage = PreKeyWhisperMessage(serialized=key)
|
||||
if not preKeyWhisperMessage.getPreKeyId():
|
||||
raise Exception("Received PreKeyWhisperMessage without PreKey =>" +
|
||||
recipient_id)
|
||||
raise Exception('Received PreKeyWhisperMessage '
|
||||
'without PreKey => %s' % recipient_id)
|
||||
sessionCipher = self.get_session_cipher(recipient_id, device_id)
|
||||
try:
|
||||
log.debug(self.account +
|
||||
" => Received PreKeyWhisperMessage from " +
|
||||
recipient_id)
|
||||
log.debug('%s => Received PreKeyWhisperMessage from %s',
|
||||
self.account, recipient_id)
|
||||
key = sessionCipher.decryptPkmsg(preKeyWhisperMessage)
|
||||
# Publish new bundle after PreKey has been used
|
||||
# for building a new Session
|
||||
self.xmpp_con.set_bundle()
|
||||
self.add_device(recipient_id, device_id)
|
||||
return key
|
||||
except UntrustedIdentityException as e:
|
||||
log.info(self.account + " => Received WhisperMessage " +
|
||||
"from Untrusted Fingerprint! => " + e.getName())
|
||||
except UntrustedIdentityException as error:
|
||||
log.info('%s => Received WhisperMessage '
|
||||
'from Untrusted Fingerprint! => %s',
|
||||
self.account, error.getName())
|
||||
|
||||
def handleWhisperMessage(self, recipient_id, device_id, key):
|
||||
whisperMessage = WhisperMessage(serialized=key)
|
||||
log.debug(self.account + " => Received WhisperMessage from " +
|
||||
recipient_id)
|
||||
log.debug('%s => Received WhisperMessage from %s',
|
||||
self.account, recipient_id)
|
||||
if self.isTrusted(recipient_id, device_id):
|
||||
sessionCipher = self.get_session_cipher(recipient_id, device_id)
|
||||
key = sessionCipher.decryptMsg(whisperMessage, textMsg=False)
|
||||
self.add_device(recipient_id, device_id)
|
||||
return key
|
||||
else:
|
||||
raise Exception("Received WhisperMessage "
|
||||
"from Untrusted Fingerprint! => " + recipient_id)
|
||||
|
||||
raise Exception('Received WhisperMessage '
|
||||
'from Untrusted Fingerprint! => %s' % recipient_id)
|
||||
|
||||
def checkPreKeyAmount(self):
|
||||
# Check if enough PreKeys are available
|
||||
preKeyCount = self.store.preKeyStore.getPreKeyCount()
|
||||
preKeyCount = self.store.getPreKeyCount()
|
||||
if preKeyCount < MIN_PREKEY_AMOUNT:
|
||||
newKeys = DEFAULT_PREKEY_AMOUNT - preKeyCount
|
||||
self.store.preKeyStore.generateNewPreKeys(newKeys)
|
||||
log.info(self.account + ' => ' + str(newKeys) +
|
||||
' PreKeys created')
|
||||
self.store.generateNewPreKeys(newKeys)
|
||||
log.info('%s => %s PreKeys created', self.account, newKeys)
|
||||
|
||||
def cycleSignedPreKey(self, identityKeyPair):
|
||||
# Publish every SPK_CYCLE_TIME a new SignedPreKey
|
||||
@@ -449,8 +444,8 @@ class OmemoState:
|
||||
signedPreKey = KeyHelper.generateSignedPreKey(
|
||||
identityKeyPair, self.store.getNextSignedPreKeyId())
|
||||
self.store.storeSignedPreKey(signedPreKey.getId(), signedPreKey)
|
||||
log.debug(self.account +
|
||||
' => New SignedPreKey created, because none existed')
|
||||
log.debug('%s => New SignedPreKey created, because none existed',
|
||||
self.account)
|
||||
|
||||
# if SPK_CYCLE_TIME is reached, generate a new SignedPreKey
|
||||
now = int(time.time())
|
||||
@@ -461,7 +456,7 @@ class OmemoState:
|
||||
signedPreKey = KeyHelper.generateSignedPreKey(
|
||||
identityKeyPair, self.store.getNextSignedPreKeyId())
|
||||
self.store.storeSignedPreKey(signedPreKey.getId(), signedPreKey)
|
||||
log.debug(self.account + ' => Cycled SignedPreKey')
|
||||
log.debug('%s => Cycled SignedPreKey', self.account)
|
||||
|
||||
# Delete all SignedPreKeys that are older than SPK_ARCHIVE_TIME
|
||||
timestamp = now - SPK_ARCHIVE_TIME
|
||||
|
||||
@@ -88,7 +88,7 @@ class FileDecryption:
|
||||
if not self.is_encrypted(file):
|
||||
log.info('Url not encrypted: %s', url)
|
||||
return
|
||||
print('ADASD')
|
||||
|
||||
self.create_paths(file)
|
||||
|
||||
if os.path.exists(file.filepath):
|
||||
|
||||
@@ -534,8 +534,7 @@ class OMEMO(BaseModule):
|
||||
log.info('%s => Received own device list: %s',
|
||||
self._account, devicelist)
|
||||
self.omemo.set_own_devices(devicelist)
|
||||
self.omemo.store.sessionStore.setActiveState(
|
||||
devicelist, self.own_jid)
|
||||
self.omemo.store.setActiveState(devicelist, self.own_jid)
|
||||
|
||||
# remove contact from list, so on send button pressed
|
||||
# we query for bundle and build a session
|
||||
@@ -551,7 +550,7 @@ class OMEMO(BaseModule):
|
||||
log.info('%s => Received device list for %s: %s',
|
||||
self._account, jid, devicelist)
|
||||
self.omemo.set_devices(jid, devicelist)
|
||||
self.omemo.store.sessionStore.setActiveState(devicelist, jid)
|
||||
self.omemo.store.setActiveState(devicelist, jid)
|
||||
|
||||
# remove contact from list, so on send button pressed
|
||||
# we query for bundle and build a session
|
||||
|
||||
Reference in New Issue
Block a user