xep_0384: Temporarily add wrappers for omemo (thanks lovetox)

This commit is contained in:
Maxime “pep” Buquet 2018-05-14 11:12:50 +02:00
parent 78f42fde5c
commit af662289dd
3 changed files with 277 additions and 0 deletions

15
db_helpers.py Normal file
View file

@ -0,0 +1,15 @@
''' Database helper functions '''
def table_exists(db_con, name):
""" Check if the specified table exists in the db. """
query = """ SELECT name FROM sqlite_master
WHERE type='table' AND name=?;
"""
return db_con.execute(query, (name, )).fetchone() is not None
def user_version(db_con):
""" Return the value of PRAGMA user_version. """
return db_con.execute('PRAGMA user_version').fetchone()[0]

57
session.py Normal file
View file

@ -0,0 +1,57 @@
import omemo
from slixmpp.plugins.xep_0384.storage import SQLiteDatabase
from omemo.util import generateDeviceID
class SessionManager:
def __init__(self, own_jid, db_path):
# Database Inferface
self._store = SQLiteDatabase(db_path)
# OmemoSessionManager
self._sm = omemo.SessionManager(own_jid, self._store, generateDeviceID())
def build_session(self, bundle):
self._store.createSession()
def get_bundle(self):
return self._sm.state.getPublicBundle()
def set_devicelist(self, device_list, jid=None):
self._sm.newDeviceList(device_list, jid)
def get_devicelist(self, jid):
return self._sm.getDevices(jid)
def get_own_device_id(self):
return self._sm.__my_device_id
def get_own_devices(self):
devices = self._sm.getDevices()['active']
if self._sm.__my_device_id not in devices:
devices = list(devices)
devices.append(self._sm.__my_device_id)
return devices
def get_devices_without_session(self, jid):
return self._store.getDevicesWithoutSession(jid)
def get_trusted_fingerprints(self, jid):
return self._store.getTrustedFingerprints(jid)
def save_bundle(self, jid, device_id, bundle):
fingerprint = bundle.fingerprint
self._store.storeBundle(jid, device_id, fingerprint)
def clear_devicelist(self):
return
def encrypt(self, jids, plaintext, bundles=None, devices=None, callback=None):
return self._sm.encryptMessage(jids, plaintext, bundles, devices, callback)
def decrypt(self, jid, sid, iv, message, payload, prekey):
if prekey:
return self._sm.decryptMessage(jid, sid, iv, message, payload)
return self._sm.decryptPreKeyMessage(jid, sid, iv, message, payload)
def buid_session(self, jid, device, bundle, callback):
return self._sm.buildSession(jid, device, bundle, callback)

205
storage.py Normal file
View file

@ -0,0 +1,205 @@
# -*- coding: utf-8 -*-
#
# Copyright 2018 Philipp Hörist <philipp@hoerist.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/>.
#
import sqlite3
import pickle
import logging
from collections import namedtuple
from omemo import Storage
from omemo.x3dhdoubleratchet import X3DHDoubleRatchet
from omemo.signal.doubleratchet.doubleratchet import DoubleRatchet
from .db_helpers import user_version
log = logging.getLogger(__name__)
class SQLiteDatabase(Storage):
""" SQLite Database """
def __init__(self, db_path):
sqlite3.register_adapter(X3DHDoubleRatchet, self._pickle_object)
sqlite3.register_adapter(DoubleRatchet, self._pickle_object)
sqlite3.register_converter("omemo_state", self._unpickle_object)
sqlite3.register_converter("omemo_session", self._unpickle_object)
self._con = sqlite3.connect(db_path,
detect_types=sqlite3.PARSE_DECLTYPES)
self._con.text_factory = bytes
self._con.row_factory = self.namedtuple_factory
self._create_database()
self._migrate_database()
self._con.execute("PRAGMA synchronous=FULL;")
self._con.commit()
self._own_device_id = None
def _create_database(self):
if user_version(self._con) == 0:
create_tables = '''
CREATE TABLE IF NOT EXISTS sessions (
_id INTEGER PRIMARY KEY AUTOINCREMENT,
jid TEXT,
device_id INTEGER,
session omemo_session BLOB,
state omemo_state BLOB,
fingerprint TEXT,
active INTEGER DEFAULT 1,
trust INTEGER DEFAULT 1,
UNIQUE(jid, device_id));
CREATE TABLE IF NOT EXISTS state (
_id INTEGER PRIMARY KEY,
device_id INTEGER,
state omemo_state BLOB
);
'''
create_db_sql = """
BEGIN TRANSACTION;
%s
PRAGMA user_version=1;
END TRANSACTION;
""" % (create_tables)
self._con.executescript(create_db_sql)
def _migrate_database(self):
""" Migrates the DB
"""
pass
@staticmethod
def _pickle_object(session):
return pickle.dumps(session, pickle.HIGHEST_PROTOCOL)
@staticmethod
def _unpickle_object(session):
return pickle.loads(session)
@staticmethod
def namedtuple_factory(cursor, row):
fields = [col[0] for col in cursor.description]
Row = namedtuple("Row", fields)
named_row = Row(*row)
return named_row
def loadState(self):
log.info('Load State')
q = 'SELECT device_id, state FROM state'
result = self._con.execute(q).fetchone()
if result is not None:
self._own_device_id = result.device_id
return {'state': result.state, 'device_id': result.device_id}
def storeState(self, state, device_id):
log.info('Store State')
self._own_device_id = device_id
q = 'INSERT OR REPLACE INTO state(device_id, state) VALUES(?, ?)'
self._con.execute(q, (device_id, state))
self._con.commit()
def loadSession(self, jid, device_id):
log.info('Load Session')
q = 'SELECT session FROM sessions WHERE jid = ? AND device_id = ?'
result = self._con.execute(q, (jid, device_id)).fetchone()
if result is not None:
return result.session
def storeSession(self, jid, device_id, session):
log.info('Store Session: %s, %s', jid, device_id)
q = 'UPDATE sessions SET session = ? WHERE jid= ? AND device_id = ?'
self._con.execute(q, (session, jid, device_id))
self._con.commit()
def createSession(self, jid, device_id, session):
log.info('Create Session')
q = '''INSERT INTO sessions(jid, device_id, session, trust, active)
VALUES (?, ?, ?, 1, 1)'''
self._con.execute(q, (jid, device_id, session))
self._con.commit()
def loadActiveDevices(self, jid):
return self.loadDevices(jid, 1)
def loadInactiveDevices(self, jid):
return self.loadDevices(jid, 0)
def loadDevices(self, jid, active):
q = 'SELECT device_id FROM sessions WHERE jid = ? AND active = ?'
result = self._con.execute(q, (jid, active)).fetchall()
if result:
devices = [row.device_id for row in result]
state = 'Active' if active else 'Inactive'
log.info('Load %s Devices: %s, %s', state, jid, devices)
return devices
return []
def storeActiveDevices(self, jid, devices):
if not devices:
return
# python-omemo returns own device as active,
# dont store it in this table
if self._own_device_id in devices:
devices.remove(self._own_device_id)
log.info('Store Active Devices: %s, %s', jid, devices)
self.storeDevices(jid, devices, 1)
def storeInactiveDevices(self, jid, devices):
if not devices:
return
log.info('Store Inactive Devices: %s, %s', jid, devices)
self.storeDevices(jid, devices, 0)
def storeDevices(self, jid, devices, active):
for device_id in devices:
try:
insert = '''INSERT INTO sessions(jid, device_id, active)
VALUES(?, ?, ?)'''
self._con.execute(insert, (jid, device_id, active))
self._con.commit()
except sqlite3.IntegrityError:
update = '''UPDATE sessions SET active = ?
WHERE jid = ? AND device_id = ?'''
self._con.execute(update, (active, jid, device_id))
self._con.commit()
def getDevicesWithoutSession(self, jid):
log.info('Get Devices without Session')
q = '''SELECT device_id FROM sessions
WHERE jid = ? AND (session IS NULL OR session = "")'''
result = self._con.execute(q, (jid,)).fetchall()
if result:
devices = [row.device_id for row in result]
log.info('Get Devices without Session: %s', devices)
return devices
log.info('Get Devices without Session: []')
return []
def getTrustedFingerprints(self, jid):
return True
def storeBundle(self, jid, device_id, fingerprint):
log.info('Store Bundle')
q = '''UPDATE sessions SET fingerprint = ?
WHERE jid = ? and device_id = ?'''
self._con.execute(q, (fingerprint, jid, device_id))
self._con.commit()
def isTrusted(self, *args):
return True