aboutsummaryrefslogtreecommitdiff
path: root/plugins/omemo/src/logic
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/omemo/src/logic')
-rw-r--r--plugins/omemo/src/logic/database.vala284
-rw-r--r--plugins/omemo/src/logic/decrypt.vala210
-rw-r--r--plugins/omemo/src/logic/encrypt.vala131
-rw-r--r--plugins/omemo/src/logic/manager.vala429
-rw-r--r--plugins/omemo/src/logic/pre_key_store.vala45
-rw-r--r--plugins/omemo/src/logic/session_store.vala49
-rw-r--r--plugins/omemo/src/logic/signed_pre_key_store.vala45
-rw-r--r--plugins/omemo/src/logic/trust_manager.vala141
8 files changed, 0 insertions, 1334 deletions
diff --git a/plugins/omemo/src/logic/database.vala b/plugins/omemo/src/logic/database.vala
deleted file mode 100644
index ec5a0924..00000000
--- a/plugins/omemo/src/logic/database.vala
+++ /dev/null
@@ -1,284 +0,0 @@
-using Gee;
-using Qlite;
-
-using Dino.Entities;
-
-namespace Dino.Plugins.Omemo {
-
-public class Database : Qlite.Database {
- private const int VERSION = 5;
-
- public class IdentityMetaTable : Table {
- //Default to provide backwards compatability
- public Column<int> identity_id = new Column.Integer("identity_id") { not_null = true, min_version = 2, default = "-1" };
- public Column<string> address_name = new Column.Text("address_name") { not_null = true };
- public Column<int> device_id = new Column.Integer("device_id") { not_null = true };
- public Column<string?> identity_key_public_base64 = new Column.Text("identity_key_public_base64");
- public Column<bool> trusted_identity = new Column.BoolInt("trusted_identity") { default = "0", max_version = 1 };
- public Column<int> trust_level = new Column.Integer("trust_level") { default = TrustLevel.UNKNOWN.to_string(), min_version = 2 };
- public Column<bool> now_active = new Column.BoolInt("now_active") { default = "1" };
- public Column<long> last_active = new Column.Long("last_active");
- public Column<long> last_message_untrusted = new Column.Long("last_message_untrusted") { min_version = 5 };
- public Column<long> last_message_undecryptable = new Column.Long("last_message_undecryptable") { min_version = 5 };
-
- internal IdentityMetaTable(Database db) {
- base(db, "identity_meta");
- init({identity_id, address_name, device_id, identity_key_public_base64, trusted_identity, trust_level, now_active, last_active, last_message_untrusted, last_message_undecryptable});
- index("identity_meta_idx", {identity_id, address_name, device_id}, true);
- index("identity_meta_list_idx", {identity_id, address_name});
- }
-
- public QueryBuilder with_address(int identity_id, string address_name) {
- return select().with(this.identity_id, "=", identity_id).with(this.address_name, "=", address_name);
- }
-
- public QueryBuilder get_with_device_id(int identity_id, int device_id) {
- return select().with(this.identity_id, "=", identity_id).with(this.device_id, "=", device_id);
- }
-
- public void insert_device_list(int32 identity_id, string address_name, ArrayList<int32> devices) {
- update().with(this.identity_id, "=", identity_id).with(this.address_name, "=", address_name).set(now_active, false).perform();
- foreach (int32 device_id in devices) {
- upsert()
- .value(this.identity_id, identity_id, true)
- .value(this.address_name, address_name, true)
- .value(this.device_id, device_id, true)
- .value(this.now_active, true)
- .value(this.last_active, (long) new DateTime.now_utc().to_unix())
- .perform();
- }
- }
-
- public int64 insert_device_bundle(int32 identity_id, string address_name, int device_id, Bundle bundle, TrustLevel trust) {
- if (bundle == null || bundle.identity_key == null) return -1;
- // Do not replace identity_key if it was known before, it should never change!
- string identity_key = Base64.encode(bundle.identity_key.serialize());
- RowOption row = with_address(identity_id, address_name).with(this.device_id, "=", device_id).single().row();
- if (row.is_present() && row[identity_key_public_base64] != null && row[identity_key_public_base64] != identity_key) {
- critical("Tried to change the identity key for a known device id. Likely an attack.");
- return -1;
- }
- return upsert()
- .value(this.identity_id, identity_id, true)
- .value(this.address_name, address_name, true)
- .value(this.device_id, device_id, true)
- .value(this.identity_key_public_base64, identity_key)
- .value(this.trust_level, trust).perform();
- }
-
- public int64 insert_device_session(int32 identity_id, string address_name, int device_id, string identity_key, TrustLevel trust) {
- RowOption row = with_address(identity_id, address_name).with(this.device_id, "=", device_id).single().row();
- if (row.is_present() && row[identity_key_public_base64] != null && row[identity_key_public_base64] != identity_key) {
- critical("Tried to change the identity key for a known device id. Likely an attack.");
- return -1;
- }
- return upsert()
- .value(this.identity_id, identity_id, true)
- .value(this.address_name, address_name, true)
- .value(this.device_id, device_id, true)
- .value(this.identity_key_public_base64, identity_key)
- .value(this.trust_level, trust).perform();
- }
-
- public void update_last_message_untrusted(int identity_id, int device_id, DateTime? time) {
- var stmt = update()
- .with(this.identity_id, "=", identity_id)
- .with(this.device_id, "=", device_id);
- if (time != null) {
- stmt.set(last_message_untrusted, (long)time.to_unix());
- } else {
- stmt.set_null(last_message_untrusted);
- }
- stmt.perform();
- }
-
- public void update_last_message_undecryptable(int identity_id, int device_id, DateTime? time) {
- var stmt = update()
- .with(this.identity_id, "=", identity_id)
- .with(this.device_id, "=", device_id);
- if (time != null) {
- stmt.set(last_message_undecryptable, (long)time.to_unix());
- } else {
- stmt.set_null(last_message_undecryptable);
- }
- stmt.perform();
- }
-
- public QueryBuilder get_trusted_devices(int identity_id, string address_name) {
- return this.with_address(identity_id, address_name)
- .with(this.trust_level, "!=", TrustLevel.UNTRUSTED)
- .with(this.now_active, "=", true);
- }
-
- public QueryBuilder get_known_devices(int identity_id, string address_name) {
- return this.with_address(identity_id, address_name)
- .with(this.trust_level, "!=", TrustLevel.UNKNOWN)
- .without_null(this.identity_key_public_base64);
- }
-
- public QueryBuilder get_unknown_devices(int identity_id, string address_name) {
- return this.with_address(identity_id, address_name)
- .with_null(this.identity_key_public_base64);
- }
-
- public QueryBuilder get_new_devices(int identity_id, string address_name) {
- return this.with_address(identity_id, address_name)
- .with(this.trust_level, "=", TrustLevel.UNKNOWN)
- .without_null(this.identity_key_public_base64);
- }
-
- public Row? get_device(int identity_id, string address_name, int device_id) {
- return this.with_address(identity_id, address_name)
- .with(this.device_id, "=", device_id).single().row().inner;
- }
- }
-
-
- public class TrustTable : Table {
- public Column<int> identity_id = new Column.Integer("identity_id") { not_null = true };
- public Column<string> address_name = new Column.Text("address_name");
- public Column<bool> blind_trust = new Column.BoolInt("blind_trust") { default = "1" } ;
-
- internal TrustTable(Database db) {
- base(db, "trust");
- init({identity_id, address_name, blind_trust});
- index("trust_idx", {identity_id, address_name}, true);
- }
-
- public bool get_blind_trust(int32 identity_id, string address_name, bool def = false) {
- RowOption row = this.select().with(this.identity_id, "=", identity_id)
- .with(this.address_name, "=", address_name).single().row();
- if (row.is_present()) return row[blind_trust];
- return def;
- }
- }
-
- public class IdentityTable : Table {
- public Column<int> id = new Column.Integer("id") { primary_key = true, auto_increment = true };
- public Column<int> account_id = new Column.Integer("account_id") { unique = true, not_null = true };
- public Column<int> device_id = new Column.Integer("device_id") { not_null = true };
- public Column<string> identity_key_private_base64 = new Column.NonNullText("identity_key_private_base64");
- public Column<string> identity_key_public_base64 = new Column.NonNullText("identity_key_public_base64");
-
- internal IdentityTable(Database db) {
- base(db, "identity");
- init({id, account_id, device_id, identity_key_private_base64, identity_key_public_base64});
- }
-
- public int get_id(int account_id) {
- int id = -1;
- Row? row = this.row_with(this.account_id, account_id).inner;
- if (row != null) id = ((!)row)[this.id];
- return id;
- }
- }
-
- public class SignedPreKeyTable : Table {
- public Column<int> identity_id = new Column.Integer("identity_id") { not_null = true };
- public Column<int> signed_pre_key_id = new Column.Integer("signed_pre_key_id") { not_null = true };
- public Column<string> record_base64 = new Column.NonNullText("record_base64");
-
- internal SignedPreKeyTable(Database db) {
- base(db, "signed_pre_key");
- init({identity_id, signed_pre_key_id, record_base64});
- unique({identity_id, signed_pre_key_id});
- index("signed_pre_key_idx", {identity_id, signed_pre_key_id}, true);
- }
- }
-
- public class PreKeyTable : Table {
- public Column<int> identity_id = new Column.Integer("identity_id") { not_null = true };
- public Column<int> pre_key_id = new Column.Integer("pre_key_id") { not_null = true };
- public Column<string> record_base64 = new Column.NonNullText("record_base64");
-
- internal PreKeyTable(Database db) {
- base(db, "pre_key");
- init({identity_id, pre_key_id, record_base64});
- unique({identity_id, pre_key_id});
- index("pre_key_idx", {identity_id, pre_key_id}, true);
- }
- }
-
- public class SessionTable : Table {
- public Column<int> identity_id = new Column.Integer("identity_id") { not_null = true };
- public Column<string> address_name = new Column.NonNullText("name");
- public Column<int> device_id = new Column.Integer("device_id") { not_null = true };
- public Column<string> record_base64 = new Column.NonNullText("record_base64");
-
- internal SessionTable(Database db) {
- base(db, "session");
- init({identity_id, address_name, device_id, record_base64});
- unique({identity_id, address_name, device_id});
- index("session_idx", {identity_id, address_name, device_id}, true);
- }
- }
-
- public class ContentItemMetaTable : Table {
- public Column<int> content_item_id = new Column.Integer("message_id") { primary_key = true };
- public Column<int> identity_id = new Column.Integer("identity_id") { not_null = true };
- public Column<string> address_name = new Column.Text("address_name") { not_null = true };
- public Column<int> device_id = new Column.Integer("device_id") { not_null = true };
- public Column<bool> trusted_when_received = new Column.BoolInt("trusted_when_received") { not_null = true, default = "1" };
-
- internal ContentItemMetaTable(Database db) {
- base(db, "content_item_meta");
- init({content_item_id, identity_id, address_name, device_id, trusted_when_received});
- index("content_item_meta_device_idx", {identity_id, device_id, address_name});
- }
-
- public RowOption with_content_item(ContentItem item) {
- return row_with(content_item_id, item.id);
- }
-
- public QueryBuilder with_device(int identity_id, string address_name, int device_id) {
- return select()
- .with(this.identity_id, "=", identity_id)
- .with(this.address_name, "=", address_name)
- .with(this.device_id, "=", device_id);
- }
- }
-
- public IdentityMetaTable identity_meta { get; private set; }
- public TrustTable trust { get; private set; }
- public IdentityTable identity { get; private set; }
- public SignedPreKeyTable signed_pre_key { get; private set; }
- public PreKeyTable pre_key { get; private set; }
- public SessionTable session { get; private set; }
- public ContentItemMetaTable content_item_meta { get; private set; }
-
- public Database(string fileName) {
- base(fileName, VERSION);
- identity_meta = new IdentityMetaTable(this);
- trust = new TrustTable(this);
- identity = new IdentityTable(this);
- signed_pre_key = new SignedPreKeyTable(this);
- pre_key = new PreKeyTable(this);
- session = new SessionTable(this);
- content_item_meta = new ContentItemMetaTable(this);
- init({identity_meta, trust, identity, signed_pre_key, pre_key, session, content_item_meta});
-
- try {
- exec("PRAGMA journal_mode = WAL");
- exec("PRAGMA synchronous = NORMAL");
- exec("PRAGMA secure_delete = ON");
- } catch (Error e) {
- error("Failed to set OMEMO database properties: %s", e.message);
- }
- }
-
- public override void migrate(long oldVersion) {
- if(oldVersion == 1) {
- try {
- exec("DROP INDEX identity_meta_idx");
- exec("DROP INDEX identity_meta_list_idx");
- exec("CREATE UNIQUE INDEX identity_meta_idx ON identity_meta (identity_id, address_name, device_id)");
- exec("CREATE INDEX identity_meta_list_idx ON identity_meta (identity_id, address_name)");
- } catch (Error e) {
- stderr.printf("Failed to migrate OMEMO database\n");
- Process.exit(-1);
- }
- }
- }
-}
-
-}
diff --git a/plugins/omemo/src/logic/decrypt.vala b/plugins/omemo/src/logic/decrypt.vala
deleted file mode 100644
index 2861b42f..00000000
--- a/plugins/omemo/src/logic/decrypt.vala
+++ /dev/null
@@ -1,210 +0,0 @@
-using Dino.Entities;
-using Qlite;
-using Gee;
-using Omemo;
-using Xmpp;
-
-namespace Dino.Plugins.Omemo {
-
- public class OmemoDecryptor : Xep.Omemo.OmemoDecryptor {
-
- private Account account;
- private Store store;
- private Database db;
- private StreamInteractor stream_interactor;
- private TrustManager trust_manager;
-
- public override uint32 own_device_id { get { return store.local_registration_id; }}
-
- public OmemoDecryptor(Account account, StreamInteractor stream_interactor, TrustManager trust_manager, Database db, Store store) {
- this.account = account;
- this.stream_interactor = stream_interactor;
- this.trust_manager = trust_manager;
- this.db = db;
- this.store = store;
- }
-
- public bool decrypt_message(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) {
- StanzaNode? encrypted_node = stanza.stanza.get_subnode("encrypted", NS_URI);
- if (encrypted_node == null || MessageFlag.get_flag(stanza) != null || stanza.from == null) return false;
-
- if (!Plugin.ensure_context()) return false;
- int identity_id = db.identity.get_id(conversation.account.id);
-
- MessageFlag flag = new MessageFlag();
- stanza.add_flag(flag);
-
- Xep.Omemo.ParsedData? data = parse_node(encrypted_node);
- if (data == null) return false;
-
-
- foreach (Bytes encr_key in data.our_potential_encrypted_keys.keys) {
- data.is_prekey = data.our_potential_encrypted_keys[encr_key];
- data.encrypted_key = encr_key.get_data();
- Gee.List<Jid> possible_jids = get_potential_message_jids(message, data, identity_id);
- if (possible_jids.size == 0) {
- debug("Received message from unknown entity with device id %d", data.sid);
- }
-
- foreach (Jid possible_jid in possible_jids) {
- try {
- uint8[] key = decrypt_key(data, possible_jid);
- if (data.ciphertext != null) {
- string cleartext = arr_to_str(aes_decrypt(Cipher.AES_GCM_NOPADDING, key, data.iv, data.ciphertext));
- message.body = cleartext;
- }
-
- // If we figured out which real jid a message comes from due to decryption working, save it
- if (conversation.type_ == Conversation.Type.GROUPCHAT && message.real_jid == null) {
- message.real_jid = possible_jid;
- }
-
- message.encryption = Encryption.OMEMO;
-
- trust_manager.message_device_id_map[message] = data.sid;
- return true;
- } catch (Error e) {
- debug("Decrypting message from %s/%d failed: %s", possible_jid.to_string(), data.sid, e.message);
- }
- }
- }
-
- if (
- data.ciphertext != null && // Ratchet forwarding doesn't contain payload and might not include us, which is ok
- data.our_potential_encrypted_keys.size == 0 && // The message was not encrypted to us
- stream_interactor.module_manager.get_module(message.account, StreamModule.IDENTITY).store.local_registration_id != data.sid // Message from this device. Never encrypted to itself.
- ) {
- db.identity_meta.update_last_message_undecryptable(identity_id, data.sid, message.time);
- trust_manager.bad_message_state_updated(conversation.account, message.from, data.sid);
- }
-
- debug("Received OMEMO encryped message that could not be decrypted.");
- return false;
- }
-
- public Gee.List<Jid> get_potential_message_jids(Entities.Message message, Xmpp.Xep.Omemo.ParsedData data, int identity_id) {
- Gee.List<Jid> possible_jids = new ArrayList<Jid>();
- if (message.type_ == Message.Type.CHAT) {
- possible_jids.add(message.from.bare_jid);
- } else {
- if (message.real_jid != null) {
- possible_jids.add(message.real_jid.bare_jid);
- } else if (data.is_prekey) {
- // pre key messages do store the identity key, so we can use that to find the real jid
- PreKeyOmemoMessage msg = Plugin.get_context().deserialize_signal_pre_key_message(data.encrypted_key);
- string identity_key = Base64.encode(msg.identity_key.serialize());
- foreach (Row row in db.identity_meta.get_with_device_id(identity_id, data.sid).with(db.identity_meta.identity_key_public_base64, "=", identity_key)) {
- try {
- possible_jids.add(Jid.from_string(row[db.identity_meta.address_name]));
- } catch (InvalidJidError e) {
- warning("Ignoring invalid jid from database: %s", e.message);
- }
- }
- } else {
- // If we don't know the device name (MUC history w/o MAM), test decryption with all keys with fitting device id
- foreach (Row row in db.identity_meta.get_with_device_id(identity_id, data.sid)) {
- try {
- possible_jids.add(Jid.from_string(row[db.identity_meta.address_name]));
- } catch (InvalidJidError e) {
- warning("Ignoring invalid jid from database: %s", e.message);
- }
- }
- }
- }
- return possible_jids;
- }
-
- public override uint8[] decrypt_key(Xmpp.Xep.Omemo.ParsedData data, Jid from_jid) throws GLib.Error {
- int sid = data.sid;
- uint8[] ciphertext = data.ciphertext;
- uint8[] encrypted_key = data.encrypted_key;
-
- Address address = new Address(from_jid.to_string(), sid);
- uint8[] key;
-
- if (data.is_prekey) {
- int identity_id = db.identity.get_id(account.id);
- PreKeyOmemoMessage msg = Plugin.get_context().deserialize_signal_pre_key_message(encrypted_key);
- string identity_key = Base64.encode(msg.identity_key.serialize());
-
- bool ok = update_db_for_prekey(identity_id, identity_key, from_jid, sid);
- if (!ok) throw new GLib.Error(-1, 0, "Failed updating db for prekey");
-
- debug("Starting new session for decryption with device from %s/%d", from_jid.to_string(), sid);
- SessionCipher cipher = store.create_session_cipher(address);
- key = cipher.decrypt_pre_key_message(msg);
- // TODO: Finish session
- } else {
- debug("Continuing session for decryption with device from %s/%d", from_jid.to_string(), sid);
- OmemoMessage msg = Plugin.get_context().deserialize_signal_message(encrypted_key);
- SessionCipher cipher = store.create_session_cipher(address);
- key = cipher.decrypt_message(msg);
- }
-
- if (key.length >= 32) {
- int authtaglength = key.length - 16;
- uint8[] new_ciphertext = new uint8[ciphertext.length + authtaglength];
- uint8[] new_key = new uint8[16];
- Memory.copy(new_ciphertext, ciphertext, ciphertext.length);
- Memory.copy((uint8*)new_ciphertext + ciphertext.length, (uint8*)key + 16, authtaglength);
- Memory.copy(new_key, key, 16);
- data.ciphertext = new_ciphertext;
- key = new_key;
- }
-
- return key;
- }
-
- public override string decrypt(uint8[] ciphertext, uint8[] key, uint8[] iv) throws GLib.Error {
- return arr_to_str(aes_decrypt(Cipher.AES_GCM_NOPADDING, key, iv, ciphertext));
- }
-
- private bool update_db_for_prekey(int identity_id, string identity_key, Jid from_jid, int sid) {
- Row? device = db.identity_meta.get_device(identity_id, from_jid.to_string(), sid);
- if (device != null && device[db.identity_meta.identity_key_public_base64] != null) {
- if (device[db.identity_meta.identity_key_public_base64] != identity_key) {
- critical("Tried to use a different identity key for a known device id.");
- return false;
- }
- } else {
- debug("Learn new device from incoming message from %s/%d", from_jid.to_string(), sid);
- bool blind_trust = db.trust.get_blind_trust(identity_id, from_jid.to_string(), true);
- if (db.identity_meta.insert_device_session(identity_id, from_jid.to_string(), sid, identity_key, blind_trust ? TrustLevel.TRUSTED : TrustLevel.UNKNOWN) < 0) {
- critical("Failed learning a device.");
- return false;
- }
-
- XmppStream? stream = stream_interactor.get_stream(account);
- if (device == null && stream != null) {
- stream.get_module(StreamModule.IDENTITY).request_user_devicelist.begin(stream, from_jid);
- }
- }
- return true;
- }
-
- private string arr_to_str(uint8[] arr) {
- // null-terminate the array
- uint8[] rarr = new uint8[arr.length+1];
- Memory.copy(rarr, arr, arr.length);
- return (string)rarr;
- }
- }
-
- public class DecryptMessageListener : MessageListener {
- public string[] after_actions_const = new string[]{ };
- public override string action_group { get { return "DECRYPT"; } }
- public override string[] after_actions { get { return after_actions_const; } }
-
- private HashMap<Account, OmemoDecryptor> decryptors;
-
- public DecryptMessageListener(HashMap<Account, OmemoDecryptor> decryptors) {
- this.decryptors = decryptors;
- }
-
- public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) {
- decryptors[message.account].decrypt_message(message, stanza, conversation);
- return false;
- }
- }
-}
-
diff --git a/plugins/omemo/src/logic/encrypt.vala b/plugins/omemo/src/logic/encrypt.vala
deleted file mode 100644
index e5d11c69..00000000
--- a/plugins/omemo/src/logic/encrypt.vala
+++ /dev/null
@@ -1,131 +0,0 @@
-using Gee;
-using Omemo;
-using Dino.Entities;
-using Xmpp;
-using Xmpp.Xep.Omemo;
-
-namespace Dino.Plugins.Omemo {
-
- public class OmemoEncryptor : Xep.Omemo.OmemoEncryptor {
-
- private Account account;
- private Store store;
- private TrustManager trust_manager;
-
- public override uint32 own_device_id { get { return store.local_registration_id; }}
-
- public OmemoEncryptor(Account account, TrustManager trust_manager, Store store) {
- this.account = account;
- this.trust_manager = trust_manager;
- this.store = store;
- }
-
- public override Xep.Omemo.EncryptionData encrypt_plaintext(string plaintext) throws GLib.Error {
- const uint KEY_SIZE = 16;
- const uint IV_SIZE = 12;
-
- //Create a key and use it to encrypt the message
- uint8[] key = new uint8[KEY_SIZE];
- Plugin.get_context().randomize(key);
- uint8[] iv = new uint8[IV_SIZE];
- Plugin.get_context().randomize(iv);
-
- uint8[] aes_encrypt_result = aes_encrypt(Cipher.AES_GCM_NOPADDING, key, iv, plaintext.data);
- uint8[] ciphertext = aes_encrypt_result[0:aes_encrypt_result.length - 16];
- uint8[] tag = aes_encrypt_result[aes_encrypt_result.length - 16:aes_encrypt_result.length];
- uint8[] keytag = new uint8[key.length + tag.length];
- Memory.copy(keytag, key, key.length);
- Memory.copy((uint8*)keytag + key.length, tag, tag.length);
-
- var ret = new Xep.Omemo.EncryptionData(own_device_id);
- ret.ciphertext = ciphertext;
- ret.keytag = keytag;
- ret.iv = iv;
- return ret;
- }
-
- public EncryptState encrypt(MessageStanza message, Jid self_jid, Gee.List<Jid> recipients, XmppStream stream) {
-
- EncryptState status = new EncryptState();
- if (!Plugin.ensure_context()) return status;
- if (message.to == null) return status;
-
- try {
- EncryptionData enc_data = encrypt_plaintext(message.body);
- status = encrypt_key_to_recipients(enc_data, self_jid, recipients, stream);
-
- message.stanza.put_node(enc_data.get_encrypted_node());
- Xep.ExplicitEncryption.add_encryption_tag_to_message(message, NS_URI, "OMEMO");
- message.body = "[This message is OMEMO encrypted]";
- status.encrypted = true;
- } catch (Error e) {
- warning(@"error while encrypting message: $(e.message)\n");
- message.body = "[OMEMO encryption failed]";
- status.encrypted = false;
- }
- return status;
- }
-
- internal EncryptState encrypt_key_to_recipients(EncryptionData enc_data, Jid self_jid, Gee.List<Jid> recipients, XmppStream stream) throws Error {
- EncryptState status = new EncryptState();
-
- //Check we have the bundles and device lists needed to send the message
- if (!trust_manager.is_known_address(account, self_jid)) return status;
- status.own_list = true;
- status.own_devices = trust_manager.get_trusted_devices(account, self_jid).size;
- status.other_waiting_lists = 0;
- status.other_devices = 0;
- foreach (Jid recipient in recipients) {
- if (!trust_manager.is_known_address(account, recipient)) {
- status.other_waiting_lists++;
- }
- if (status.other_waiting_lists > 0) return status;
- status.other_devices += trust_manager.get_trusted_devices(account, recipient).size;
- }
- if (status.own_devices == 0 || status.other_devices == 0) return status;
-
-
- //Encrypt the key for each recipient's device individually
- foreach (Jid recipient in recipients) {
- EncryptionResult enc_res = encrypt_key_to_recipient(stream, enc_data, recipient);
- status.add_result(enc_res, false);
- }
-
- // Encrypt the key for each own device
- EncryptionResult enc_res = encrypt_key_to_recipient(stream, enc_data, self_jid);
- status.add_result(enc_res, true);
-
- return status;
- }
-
- public override EncryptionResult encrypt_key_to_recipient(XmppStream stream, Xep.Omemo.EncryptionData enc_data, Jid recipient) throws GLib.Error {
- var result = new EncryptionResult();
- StreamModule module = stream.get_module(StreamModule.IDENTITY);
-
- foreach(int32 device_id in trust_manager.get_trusted_devices(account, recipient)) {
- if (module.is_ignored_device(recipient, device_id)) {
- result.lost++;
- continue;
- }
- try {
- encrypt_key(enc_data, recipient, device_id);
- result.success++;
- } catch (Error e) {
- if (e.code == ErrorCode.UNKNOWN) result.unknown++;
- else result.failure++;
- }
- }
- return result;
- }
-
- public override void encrypt_key(Xep.Omemo.EncryptionData encryption_data, Jid jid, int32 device_id) throws GLib.Error {
- Address address = new Address(jid.to_string(), device_id);
- SessionCipher cipher = store.create_session_cipher(address);
- CiphertextMessage device_key = cipher.encrypt(encryption_data.keytag);
- address.device_id = 0;
- debug("Created encrypted key for %s/%d", jid.to_string(), device_id);
-
- encryption_data.add_device_key(device_id, device_key.serialized, device_key.type == CiphertextType.PREKEY);
- }
- }
-} \ No newline at end of file
diff --git a/plugins/omemo/src/logic/manager.vala b/plugins/omemo/src/logic/manager.vala
deleted file mode 100644
index 71f17aae..00000000
--- a/plugins/omemo/src/logic/manager.vala
+++ /dev/null
@@ -1,429 +0,0 @@
-using Dino.Entities;
-using Omemo;
-using Qlite;
-using Xmpp;
-using Gee;
-
-namespace Dino.Plugins.Omemo {
-
-public class Manager : StreamInteractionModule, Object {
- public static ModuleIdentity<Manager> IDENTITY = new ModuleIdentity<Manager>("omemo_manager");
- public string id { get { return IDENTITY.id; } }
-
- private StreamInteractor stream_interactor;
- private Database db;
- private TrustManager trust_manager;
- private HashMap<Account, OmemoEncryptor> encryptors;
- private Map<Entities.Message, MessageState> message_states = new HashMap<Entities.Message, MessageState>(Entities.Message.hash_func, Entities.Message.equals_func);
-
- private class MessageState {
- public Entities.Message msg { get; private set; }
- public Xep.Omemo.EncryptState last_try { get; private set; }
- public int waiting_other_sessions { get; set; }
- public int waiting_own_sessions { get; set; }
- public bool waiting_own_devicelist { get; set; }
- public int waiting_other_devicelists { get; set; }
- public bool force_next_attempt { get; set; }
- public bool will_send_now { get; private set; }
- public bool active_send_attempt { get; set; }
-
- public MessageState(Entities.Message msg, Xep.Omemo.EncryptState last_try) {
- update_from_encrypt_status(msg, last_try);
- }
-
- public void update_from_encrypt_status(Entities.Message msg, Xep.Omemo.EncryptState new_try) {
- this.msg = msg;
- this.last_try = new_try;
- this.waiting_other_sessions = new_try.other_unknown;
- this.waiting_own_sessions = new_try.own_unknown;
- this.waiting_own_devicelist = !new_try.own_list;
- this.waiting_other_devicelists = new_try.other_waiting_lists;
- this.active_send_attempt = false;
- will_send_now = false;
- if (new_try.other_failure > 0 || (new_try.other_lost == new_try.other_devices && new_try.other_devices > 0)) {
- msg.marked = Entities.Message.Marked.WONTSEND;
- } else if (new_try.other_unknown > 0 || new_try.own_unknown > 0 || new_try.other_waiting_lists > 0 || !new_try.own_list || new_try.own_devices == 0) {
- msg.marked = Entities.Message.Marked.UNSENT;
- } else if (!new_try.encrypted) {
- msg.marked = Entities.Message.Marked.WONTSEND;
- } else {
- will_send_now = true;
- }
- }
-
- public bool should_retry_now() {
- return !waiting_own_devicelist && waiting_other_devicelists <= 0 && waiting_other_sessions <= 0 && waiting_own_sessions <= 0 && !active_send_attempt;
- }
-
- public string to_string() {
- return @"MessageState (msg=$(msg.stanza_id), send=$will_send_now, $last_try)";
- }
- }
-
- private Manager(StreamInteractor stream_interactor, Database db, TrustManager trust_manager, HashMap<Account, OmemoEncryptor> encryptors) {
- this.stream_interactor = stream_interactor;
- this.db = db;
- this.trust_manager = trust_manager;
- this.encryptors = encryptors;
-
- stream_interactor.account_added.connect(on_account_added);
- stream_interactor.stream_negotiated.connect(on_stream_negotiated);
- stream_interactor.get_module(MessageProcessor.IDENTITY).pre_message_send.connect(on_pre_message_send);
- stream_interactor.get_module(RosterManager.IDENTITY).mutual_subscription.connect(on_mutual_subscription);
- }
-
- public void clear_device_list(Account account) {
- XmppStream? stream = stream_interactor.get_stream(account);
- if (stream == null) return;
-
- stream.get_module(StreamModule.IDENTITY).clear_device_list(stream);
- }
-
- private Gee.List<Jid> get_occupants(Jid jid, Account account){
- Gee.List<Jid> occupants = new ArrayList<Jid>(Jid.equals_bare_func);
- if(!stream_interactor.get_module(MucManager.IDENTITY).is_groupchat(jid, account)){
- occupants.add(jid);
- }
- Gee.List<Jid>? occupant_jids = stream_interactor.get_module(MucManager.IDENTITY).get_offline_members(jid, account);
- if(occupant_jids == null) {
- return occupants;
- }
- foreach (Jid occupant in occupant_jids) {
- if(!occupant.equals(account.bare_jid)){
- occupants.add(occupant.bare_jid);
- }
- }
- return occupants;
- }
-
- private void on_pre_message_send(Entities.Message message, Xmpp.MessageStanza message_stanza, Conversation conversation) {
- if (message.encryption == Encryption.OMEMO) {
- if (message.type_ == Message.Type.GROUPCHAT_PM) {
- message.marked = Message.Marked.WONTSEND;
- return;
- }
- XmppStream? stream = stream_interactor.get_stream(conversation.account);
- if (stream == null) {
- message.marked = Entities.Message.Marked.UNSENT;
- return;
- }
- StreamModule? module_ = ((!)stream).get_module(StreamModule.IDENTITY);
- if (module_ == null) {
- message.marked = Entities.Message.Marked.UNSENT;
- return;
- }
- StreamModule module = (!)module_;
-
- //Get a list of everyone for whom the message should be encrypted
- Gee.List<Jid> recipients;
- if (message_stanza.type_ == MessageStanza.TYPE_GROUPCHAT) {
- recipients = get_occupants((!)message.to.bare_jid, conversation.account);
- if (recipients.size == 0) {
- message.marked = Entities.Message.Marked.WONTSEND;
- return;
- }
- } else {
- recipients = new ArrayList<Jid>(Jid.equals_bare_func);
- recipients.add(message_stanza.to);
- }
-
- //Attempt to encrypt the message
- Xep.Omemo.EncryptState enc_state = encryptors[conversation.account].encrypt(message_stanza, conversation.account.bare_jid, recipients, stream);
- MessageState state;
- lock (message_states) {
- if (message_states.has_key(message)) {
- state = message_states.get(message);
- state.update_from_encrypt_status(message, enc_state);
- if (state.will_send_now) {
- debug("sending message delayed: %s", state.to_string());
- }
- } else {
- state = new MessageState(message, enc_state);
- message_states[message] = state;
- }
- if (state.will_send_now) {
- message_states.unset(message);
- }
- }
-
- //Encryption failed - need to fetch more information
- if (!state.will_send_now) {
- if (message.marked == Entities.Message.Marked.WONTSEND) {
- debug("retracting message %s", state.to_string());
- message_states.unset(message);
- } else {
- debug("delaying message %s", state.to_string());
-
- if (state.waiting_own_sessions > 0) {
- module.fetch_bundles((!)stream, conversation.account.bare_jid, trust_manager.get_trusted_devices(conversation.account, conversation.account.bare_jid));
- }
- if (state.waiting_other_sessions > 0 && message.counterpart != null) {
- foreach(Jid jid in get_occupants(((!)message.counterpart).bare_jid, conversation.account)) {
- module.fetch_bundles((!)stream, jid, trust_manager.get_trusted_devices(conversation.account, jid));
- }
- }
- if (state.waiting_other_devicelists > 0 && message.counterpart != null) {
- foreach(Jid jid in get_occupants(((!)message.counterpart).bare_jid, conversation.account)) {
- module.request_user_devicelist.begin((!)stream, jid);
- }
- }
- }
- }
- }
- }
-
- private void on_mutual_subscription(Account account, Jid jid) {
- XmppStream? stream = stream_interactor.get_stream(account);
- if(stream == null) return;
-
- stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY).request_user_devicelist.begin((!)stream, jid);
- }
-
- private void on_stream_negotiated(Account account, XmppStream stream) {
- StreamModule module = stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY);
- if (module != null) {
- module.request_user_devicelist.begin(stream, account.bare_jid);
- }
- }
-
- private void on_account_added(Account account) {
- StreamModule module = stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY);
- if (module != null) {
- module.device_list_loaded.connect((jid, devices) => on_device_list_loaded(account, jid, devices));
- module.bundle_fetched.connect((jid, device_id, bundle) => on_bundle_fetched(account, jid, device_id, bundle));
- module.bundle_fetch_failed.connect((jid) => continue_message_sending(account, jid));
- }
- initialize_store.begin(account);
- }
-
- private void on_device_list_loaded(Account account, Jid jid, ArrayList<int32> device_list) {
- debug("received device list for %s from %s", account.bare_jid.to_string(), jid.to_string());
-
- XmppStream? stream = stream_interactor.get_stream(account);
- if (stream == null) {
- return;
- }
- StreamModule? module = ((!)stream).get_module(StreamModule.IDENTITY);
- if (module == null) {
- return;
- }
-
- int identity_id = db.identity.get_id(account.id);
- if (identity_id < 0) return;
-
- //Update meta database
- db.identity_meta.insert_device_list(identity_id, jid.bare_jid.to_string(), device_list);
-
- //Fetch the bundle for each new device
- int inc = 0;
- foreach (Row row in db.identity_meta.get_unknown_devices(identity_id, jid.bare_jid.to_string())) {
- try {
- module.fetch_bundle(stream, Jid.from_string(row[db.identity_meta.address_name]), row[db.identity_meta.device_id], false);
- inc++;
- } catch (InvalidJidError e) {
- warning("Ignoring device with invalid Jid: %s", e.message);
- }
- }
- if (inc > 0) {
- debug("new bundles %i/%i for %s", inc, device_list.size, jid.to_string());
- }
-
- //Create an entry for the jid in the account table if one does not exist already
- if (db.trust.select().with(db.trust.identity_id, "=", identity_id).with(db.trust.address_name, "=", jid.bare_jid.to_string()).count() == 0) {
- db.trust.insert().value(db.trust.identity_id, identity_id).value(db.trust.address_name, jid.bare_jid.to_string()).value(db.trust.blind_trust, true).perform();
- }
-
- //Get all messages that needed the devicelist and determine if we can now send them
- HashSet<Entities.Message> send_now = new HashSet<Entities.Message>();
- lock (message_states) {
- foreach (Entities.Message msg in message_states.keys) {
- if (!msg.account.equals(account)) continue;
- Gee.List<Jid> occupants = get_occupants(msg.counterpart.bare_jid, account);
- MessageState state = message_states[msg];
- if (account.bare_jid.equals(jid)) {
- state.waiting_own_devicelist = false;
- } else if (msg.counterpart != null && occupants.contains(jid)) {
- state.waiting_other_devicelists--;
- }
- if (state.should_retry_now()) {
- send_now.add(msg);
- state.active_send_attempt = true;
- }
- }
- }
- foreach (Entities.Message msg in send_now) {
- if (msg.counterpart == null) continue;
- Entities.Conversation? conv = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(((!)msg.counterpart), account);
- if (conv == null) continue;
- stream_interactor.get_module(MessageProcessor.IDENTITY).send_xmpp_message(msg, (!)conv, true);
- }
-
- }
-
- private void on_bundle_fetched(Account account, Jid jid, int32 device_id, Bundle bundle) {
- int identity_id = db.identity.get_id(account.id);
- if (identity_id < 0) return;
-
- bool blind_trust = db.trust.get_blind_trust(identity_id, jid.bare_jid.to_string(), true);
-
- //If we don't blindly trust new devices and we haven't seen this key before then don't trust it
- bool untrust = !(blind_trust || db.identity_meta.with_address(identity_id, jid.bare_jid.to_string())
- .with(db.identity_meta.device_id, "=", device_id)
- .with(db.identity_meta.identity_key_public_base64, "=", Base64.encode(bundle.identity_key.serialize()))
- .single().row().is_present());
-
- //Get trust information from the database if the device id is known
- Row device = db.identity_meta.get_device(identity_id, jid.bare_jid.to_string(), device_id);
- TrustLevel trusted = TrustLevel.UNKNOWN;
- if (device != null) {
- trusted = (TrustLevel) device[db.identity_meta.trust_level];
- }
-
- if(untrust) {
- trusted = TrustLevel.UNKNOWN;
- } else if (blind_trust && trusted == TrustLevel.UNKNOWN) {
- trusted = TrustLevel.TRUSTED;
- }
-
- //Update the database with the appropriate trust information
- db.identity_meta.insert_device_bundle(identity_id, jid.bare_jid.to_string(), device_id, bundle, trusted);
-
- if (should_start_session(account, jid)) {
- XmppStream? stream = stream_interactor.get_stream(account);
- if (stream != null) {
- StreamModule? module = ((!)stream).get_module(StreamModule.IDENTITY);
- if (module != null) {
- module.start_session(stream, jid, device_id, bundle);
- }
- }
- }
- continue_message_sending(account, jid);
- }
-
- private bool should_start_session(Account account, Jid jid) {
- lock (message_states) {
- foreach (Entities.Message msg in message_states.keys) {
- if (!msg.account.equals(account)) continue;
- Gee.List<Jid> occupants = get_occupants(msg.counterpart.bare_jid, account);
- if (account.bare_jid.equals(jid) || (msg.counterpart != null && (msg.counterpart.equals_bare(jid) || occupants.contains(jid)))) {
- return true;
- }
- }
- }
- return false;
- }
-
- private void continue_message_sending(Account account, Jid jid) {
- //Get all messages waiting and determine if they can now be sent
- HashSet<Entities.Message> send_now = new HashSet<Entities.Message>();
- lock (message_states) {
- foreach (Entities.Message msg in message_states.keys) {
- if (!msg.account.equals(account)) continue;
- Gee.List<Jid> occupants = get_occupants(msg.counterpart.bare_jid, account);
-
- MessageState state = message_states[msg];
-
- if (account.bare_jid.equals(jid)) {
- state.waiting_own_sessions--;
- } else if (msg.counterpart != null && (msg.counterpart.equals_bare(jid) || occupants.contains(jid))) {
- state.waiting_other_sessions--;
- }
- if (state.should_retry_now()){
- send_now.add(msg);
- state.active_send_attempt = true;
- }
- }
- }
- foreach (Entities.Message msg in send_now) {
- if (msg.counterpart == null) continue;
- Entities.Conversation? conv = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation((!)msg.counterpart, account);
- if (conv == null) continue;
- stream_interactor.get_module(MessageProcessor.IDENTITY).send_xmpp_message(msg, (!)conv, true);
- }
- }
-
- private async void initialize_store(Account account) {
- // If the account is not yet persisted, wait for that and then continue - without identity.account_id the entry isn't worth much.
- if (account.id == -1) {
- account.notify["id"].connect(() => initialize_store.callback());
- yield;
- }
- StreamModule? module = stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY);
- if (module == null) return;
- Store store = module.store;
- Qlite.Row? row = db.identity.row_with(db.identity.account_id, account.id).inner;
- int identity_id = -1;
- bool publish_identity = false;
-
- if (row == null) {
- // OMEMO not yet initialized, starting with empty base
- publish_identity = true;
- try {
- store.identity_key_store.local_registration_id = Random.int_range(1, int32.MAX);
-
- ECKeyPair key_pair = Plugin.get_context().generate_key_pair();
- store.identity_key_store.identity_key_private = new Bytes(key_pair.private.serialize());
- store.identity_key_store.identity_key_public = new Bytes(key_pair.public.serialize());
-
- identity_id = (int) db.identity.upsert()
- .value(db.identity.account_id, account.id, true)
- .value(db.identity.device_id, (int) store.local_registration_id)
- .value(db.identity.identity_key_private_base64, Base64.encode(store.identity_key_store.identity_key_private.get_data()))
- .value(db.identity.identity_key_public_base64, Base64.encode(store.identity_key_store.identity_key_public.get_data()))
- .perform();
- } catch (Error e) {
- // Ignore error
- }
- } else {
- store.identity_key_store.local_registration_id = ((!)row)[db.identity.device_id];
- store.identity_key_store.identity_key_private = new Bytes(Base64.decode(((!)row)[db.identity.identity_key_private_base64]));
- store.identity_key_store.identity_key_public = new Bytes(Base64.decode(((!)row)[db.identity.identity_key_public_base64]));
- identity_id = ((!)row)[db.identity.id];
- }
-
- if (identity_id >= 0) {
- store.signed_pre_key_store = new BackedSignedPreKeyStore(db, identity_id);
- store.pre_key_store = new BackedPreKeyStore(db, identity_id);
- store.session_store = new BackedSessionStore(db, identity_id);
- } else {
- warning("store for %s is not persisted!", account.bare_jid.to_string());
- }
-
- // Generated new device ID, ensure this gets added to the devicelist
- XmppStream? stream = stream_interactor.get_stream(account);
- if (stream != null) {
- module.request_user_devicelist.begin((!)stream, account.bare_jid);
- }
- }
-
- public async bool ensure_get_keys_for_conversation(Conversation conversation) {
- if (stream_interactor.get_module(MucManager.IDENTITY).is_private_room(conversation.account, conversation.counterpart)) {
- foreach (Jid offline_member in stream_interactor.get_module(MucManager.IDENTITY).get_offline_members(conversation.counterpart, conversation.account)) {
- bool ok = yield ensure_get_keys_for_jid(conversation.account, offline_member);
- if (!ok) {
- return false;
- }
- }
- return true;
- }
-
- return yield ensure_get_keys_for_jid(conversation.account, conversation.counterpart.bare_jid);
- }
-
- public async bool ensure_get_keys_for_jid(Account account, Jid jid) {
- if (trust_manager.is_known_address(account, jid)) return true;
- XmppStream? stream = stream_interactor.get_stream(account);
- if (stream != null) {
- var device_list = yield stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY).request_user_devicelist(stream, jid);
- return device_list.size > 0;
- }
- return true; // TODO wait for stream?
- }
-
- public static void start(StreamInteractor stream_interactor, Database db, TrustManager trust_manager, HashMap<Account, OmemoEncryptor> encryptors) {
- Manager m = new Manager(stream_interactor, db, trust_manager, encryptors);
- stream_interactor.add_module(m);
- }
-}
-
-}
diff --git a/plugins/omemo/src/logic/pre_key_store.vala b/plugins/omemo/src/logic/pre_key_store.vala
deleted file mode 100644
index c15acc84..00000000
--- a/plugins/omemo/src/logic/pre_key_store.vala
+++ /dev/null
@@ -1,45 +0,0 @@
-using Omemo;
-using Qlite;
-
-namespace Dino.Plugins.Omemo {
-
-private class BackedPreKeyStore : SimplePreKeyStore {
- private Database db;
- private int identity_id;
-
- public BackedPreKeyStore(Database db, int identity_id) {
- this.db = db;
- this.identity_id = identity_id;
- init();
- }
-
- private void init() {
- try {
- foreach (Row row in db.pre_key.select().with(db.pre_key.identity_id, "=", identity_id)) {
- store_pre_key(row[db.pre_key.pre_key_id], Base64.decode(row[db.pre_key.record_base64]));
- }
- } catch (Error e) {
- warning("Error while initializing pre key store: %s", e.message);
- }
-
- pre_key_stored.connect(on_pre_key_stored);
- pre_key_deleted.connect(on_pre_key_deleted);
- }
-
- public void on_pre_key_stored(PreKeyStore.Key key) {
- db.pre_key.upsert()
- .value(db.pre_key.identity_id, identity_id, true)
- .value(db.pre_key.pre_key_id, (int) key.key_id, true)
- .value(db.pre_key.record_base64, Base64.encode(key.record))
- .perform();
- }
-
- public void on_pre_key_deleted(PreKeyStore.Key key) {
- db.pre_key.delete()
- .with(db.pre_key.identity_id, "=", identity_id)
- .with(db.pre_key.pre_key_id, "=", (int) key.key_id)
- .perform();
- }
-}
-
-}
diff --git a/plugins/omemo/src/logic/session_store.vala b/plugins/omemo/src/logic/session_store.vala
deleted file mode 100644
index ea5738fd..00000000
--- a/plugins/omemo/src/logic/session_store.vala
+++ /dev/null
@@ -1,49 +0,0 @@
-using Omemo;
-using Qlite;
-
-namespace Dino.Plugins.Omemo {
-
-private class BackedSessionStore : SimpleSessionStore {
- private Database db;
- private int identity_id;
-
- public BackedSessionStore(Database db, int identity_id) {
- this.db = db;
- this.identity_id = identity_id;
- init();
- }
-
- private void init() {
- try {
- foreach (Row row in db.session.select().with(db.session.identity_id, "=", identity_id)) {
- Address addr = new Address(row[db.session.address_name], row[db.session.device_id]);
- store_session(addr, Base64.decode(row[db.session.record_base64]));
- addr.device_id = 0;
- }
- } catch (Error e) {
- print("Error while initializing session store: %s", e.message);
- }
-
- session_stored.connect(on_session_stored);
- session_removed.connect(on_session_deleted);
- }
-
- public void on_session_stored(SessionStore.Session session) {
- db.session.upsert()
- .value(db.session.identity_id, identity_id, true)
- .value(db.session.address_name, session.name, true)
- .value(db.session.device_id, session.device_id, true)
- .value(db.session.record_base64, Base64.encode(session.record))
- .perform();
- }
-
- public void on_session_deleted(SessionStore.Session session) {
- db.session.delete()
- .with(db.session.identity_id, "=", identity_id)
- .with(db.session.address_name, "=", session.name)
- .with(db.session.device_id, "=", session.device_id)
- .perform();
- }
-}
-
-}
diff --git a/plugins/omemo/src/logic/signed_pre_key_store.vala b/plugins/omemo/src/logic/signed_pre_key_store.vala
deleted file mode 100644
index d73cbe71..00000000
--- a/plugins/omemo/src/logic/signed_pre_key_store.vala
+++ /dev/null
@@ -1,45 +0,0 @@
-using Qlite;
-using Omemo;
-
-namespace Dino.Plugins.Omemo {
-
-private class BackedSignedPreKeyStore : SimpleSignedPreKeyStore {
- private Database db;
- private int identity_id;
-
- public BackedSignedPreKeyStore(Database db, int identity_id) {
- this.db = db;
- this.identity_id = identity_id;
- init();
- }
-
- private void init() {
- try {
- foreach (Row row in db.signed_pre_key.select().with(db.signed_pre_key.identity_id, "=", identity_id)) {
- store_signed_pre_key(row[db.signed_pre_key.signed_pre_key_id], Base64.decode(row[db.signed_pre_key.record_base64]));
- }
- } catch (Error e) {
- print("Error while initializing signed pre key store: %s", e.message);
- }
-
- signed_pre_key_stored.connect(on_signed_pre_key_stored);
- signed_pre_key_deleted.connect(on_signed_pre_key_deleted);
- }
-
- public void on_signed_pre_key_stored(SignedPreKeyStore.Key key) {
- db.signed_pre_key.upsert()
- .value(db.signed_pre_key.identity_id, identity_id, true)
- .value(db.signed_pre_key.signed_pre_key_id, (int) key.key_id, true)
- .value(db.signed_pre_key.record_base64, Base64.encode(key.record))
- .perform();
- }
-
- public void on_signed_pre_key_deleted(SignedPreKeyStore.Key key) {
- db.signed_pre_key.delete()
- .with(db.signed_pre_key.identity_id, "=", identity_id)
- .with(db.signed_pre_key.signed_pre_key_id, "=", (int) key.key_id)
- .perform();
- }
-}
-
-}
diff --git a/plugins/omemo/src/logic/trust_manager.vala b/plugins/omemo/src/logic/trust_manager.vala
deleted file mode 100644
index 8cf1ddeb..00000000
--- a/plugins/omemo/src/logic/trust_manager.vala
+++ /dev/null
@@ -1,141 +0,0 @@
-using Dino.Entities;
-using Gee;
-using Xmpp;
-using Omemo;
-using Qlite;
-
-namespace Dino.Plugins.Omemo {
-
-public class TrustManager {
-
- public signal void bad_message_state_updated(Account account, Jid jid, int device_id);
-
- private StreamInteractor stream_interactor;
- private Database db;
- private TagMessageListener tag_message_listener;
-
- public HashMap<Message, int> message_device_id_map = new HashMap<Message, int>(Message.hash_func, Message.equals_func);
-
- public TrustManager(StreamInteractor stream_interactor, Database db) {
- this.stream_interactor = stream_interactor;
- this.db = db;
-
- tag_message_listener = new TagMessageListener(stream_interactor, this, db, message_device_id_map);
- stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(tag_message_listener);
- }
-
- public void set_blind_trust(Account account, Jid jid, bool blind_trust) {
- int identity_id = db.identity.get_id(account.id);
- if (identity_id < 0) return;
- db.trust.update()
- .with(db.trust.identity_id, "=", identity_id)
- .with(db.trust.address_name, "=", jid.bare_jid.to_string())
- .set(db.trust.blind_trust, blind_trust).perform();
- }
-
- public void set_device_trust(Account account, Jid jid, int device_id, TrustLevel trust_level) {
- int identity_id = db.identity.get_id(account.id);
- db.identity_meta.update()
- .with(db.identity_meta.identity_id, "=", identity_id)
- .with(db.identity_meta.address_name, "=", jid.bare_jid.to_string())
- .with(db.identity_meta.device_id, "=", device_id)
- .set(db.identity_meta.trust_level, trust_level).perform();
-
- // Hide messages from untrusted or unknown devices
- string selection = null;
- string[] selection_args = {};
- var app_db = Application.get_default().db;
- foreach (Row row in db.content_item_meta.with_device(identity_id, jid.bare_jid.to_string(), device_id).with(db.content_item_meta.trusted_when_received, "=", false)) {
- if (selection == null) {
- selection = @"$(app_db.content_item.id) = ?";
- } else {
- selection += @" OR $(app_db.content_item.id) = ?";
- }
- selection_args += row[db.content_item_meta.content_item_id].to_string();
- }
- if (selection != null) {
- app_db.content_item.update()
- .set(app_db.content_item.hide, trust_level == TrustLevel.UNTRUSTED || trust_level == TrustLevel.UNKNOWN)
- .where(selection, selection_args)
- .perform();
- }
-
- if (trust_level == TrustLevel.TRUSTED) {
- db.identity_meta.update_last_message_untrusted(identity_id, device_id, null);
- bad_message_state_updated(account, jid, device_id);
- }
- }
-
- public bool is_known_address(Account account, Jid jid) {
- int identity_id = db.identity.get_id(account.id);
- if (identity_id < 0) return false;
- return db.identity_meta.with_address(identity_id, jid.to_string()).with(db.identity_meta.last_active, ">", 0).count() > 0;
- }
-
- public Gee.List<int32> get_trusted_devices(Account account, Jid jid) {
- Gee.List<int32> devices = new ArrayList<int32>();
- int identity_id = db.identity.get_id(account.id);
- if (identity_id < 0) return devices;
- foreach (Row device in db.identity_meta.get_trusted_devices(identity_id, jid.bare_jid.to_string())) {
- if(device[db.identity_meta.trust_level] != TrustLevel.UNKNOWN || device[db.identity_meta.identity_key_public_base64] == null)
- devices.add(device[db.identity_meta.device_id]);
- }
- return devices;
- }
-
- private class TagMessageListener : MessageListener {
- public string[] after_actions_const = new string[]{ "STORE" };
- public override string action_group { get { return "DECRYPT_TAG"; } }
- public override string[] after_actions { get { return after_actions_const; } }
-
- private StreamInteractor stream_interactor;
- private TrustManager trust_manager;
- private Database db;
- private HashMap<Message, int> message_device_id_map;
-
- public TagMessageListener(StreamInteractor stream_interactor, TrustManager trust_manager, Database db, HashMap<Message, int> message_device_id_map) {
- this.stream_interactor = stream_interactor;
- this.trust_manager = trust_manager;
- this.db = db;
- this.message_device_id_map = message_device_id_map;
- }
-
- public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) {
- int device_id = 0;
- if (message_device_id_map.has_key(message)) {
- device_id = message_device_id_map[message];
- message_device_id_map.unset(message);
- }
-
- // TODO: Handling of files
-
- ContentItem? content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_foreign(conversation, 1, message.id);
-
- if (content_item != null && device_id != 0) {
- Jid jid = content_item.jid;
- if (conversation.type_ == Conversation.Type.GROUPCHAT) {
- jid = message.real_jid;
- }
-
- int identity_id = db.identity.get_id(conversation.account.id);
- TrustLevel trust_level = (TrustLevel) db.identity_meta.get_device(identity_id, jid.bare_jid.to_string(), device_id)[db.identity_meta.trust_level];
- if (trust_level == TrustLevel.UNTRUSTED || trust_level == TrustLevel.UNKNOWN) {
- stream_interactor.get_module(ContentItemStore.IDENTITY).set_item_hide(content_item, true);
- db.identity_meta.update_last_message_untrusted(identity_id, device_id, message.time);
- trust_manager.bad_message_state_updated(conversation.account, jid, device_id);
- }
-
- db.content_item_meta.insert()
- .value(db.content_item_meta.content_item_id, content_item.id)
- .value(db.content_item_meta.identity_id, identity_id)
- .value(db.content_item_meta.address_name, jid.bare_jid.to_string())
- .value(db.content_item_meta.device_id, device_id)
- .value(db.content_item_meta.trusted_when_received, trust_level != TrustLevel.UNTRUSTED)
- .perform();
- }
- return false;
- }
- }
-}
-
-}