diff --git a/plugins/omemo/CMakeLists.txt b/plugins/omemo/CMakeLists.txt index 0331851e..8639478c 100644 --- a/plugins/omemo/CMakeLists.txt +++ b/plugins/omemo/CMakeLists.txt @@ -32,6 +32,7 @@ vala_precompile(OMEMO_VALA_C SOURCES src/plugin.vala src/register_plugin.vala + src/trust_level.vala src/file_transfer/file_provider.vala src/file_transfer/file_sender.vala diff --git a/plugins/omemo/src/logic/database.vala b/plugins/omemo/src/logic/database.vala index bce1d4e6..1f9e1304 100644 --- a/plugins/omemo/src/logic/database.vala +++ b/plugins/omemo/src/logic/database.vala @@ -9,18 +9,6 @@ public class Database : Qlite.Database { private const int VERSION = 4; public class IdentityMetaTable : Table { - public enum TrustLevel { - VERIFIED, - TRUSTED, - UNTRUSTED, - UNKNOWN; - - public string to_string() { - int val = this; - return val.to_string(); - } - } - //Default to provide backwards compatability public Column identity_id = new Column.Integer("identity_id") { not_null = true, min_version = 2, default = "-1" }; public Column address_name = new Column.Text("address_name") { not_null = true }; diff --git a/plugins/omemo/src/logic/manager.vala b/plugins/omemo/src/logic/manager.vala index db64c3ee..0fe4fe50 100644 --- a/plugins/omemo/src/logic/manager.vala +++ b/plugins/omemo/src/logic/manager.vala @@ -248,15 +248,15 @@ public class Manager : StreamInteractionModule, Object { //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); - Database.IdentityMetaTable.TrustLevel trusted = Database.IdentityMetaTable.TrustLevel.UNKNOWN; + TrustLevel trusted = TrustLevel.UNKNOWN; if (device != null) { - trusted = (Database.IdentityMetaTable.TrustLevel) device[db.identity_meta.trust_level]; + trusted = (TrustLevel) device[db.identity_meta.trust_level]; } if(untrust) { - trusted = Database.IdentityMetaTable.TrustLevel.UNKNOWN; - } else if (blind_trust && trusted == Database.IdentityMetaTable.TrustLevel.UNKNOWN) { - trusted = Database.IdentityMetaTable.TrustLevel.TRUSTED; + trusted = TrustLevel.UNKNOWN; + } else if (blind_trust && trusted == TrustLevel.UNKNOWN) { + trusted = TrustLevel.TRUSTED; } //Update the database with the appropriate trust information @@ -278,7 +278,7 @@ public class Manager : StreamInteractionModule, Object { MessageState state = message_states[msg]; - if (trusted == Database.IdentityMetaTable.TrustLevel.TRUSTED || trusted == Database.IdentityMetaTable.TrustLevel.VERIFIED) { + if (trusted == TrustLevel.TRUSTED || trusted == TrustLevel.VERIFIED) { if(account.bare_jid.equals(jid) || (msg.counterpart != null && (msg.counterpart.equals_bare(jid) || occupants.contains(jid)))) { session_created = module.start_session(stream, jid, device_id, bundle); } @@ -366,6 +366,7 @@ public class Manager : StreamInteractionModule, Object { if (flag.has_room_feature(conversation.counterpart, Xep.Muc.Feature.NON_ANONYMOUS) && flag.has_room_feature(conversation.counterpart, Xep.Muc.Feature.MEMBERS_ONLY)) { foreach(Jid jid in stream_interactor.get_module(MucManager.IDENTITY).get_offline_members(conversation.counterpart, conversation.account)) { if (!trust_manager.is_known_address(conversation.account, jid.bare_jid)) { + debug(@"Can't enable OMEMO for $(conversation.counterpart): missing keys for $(jid.bare_jid)"); return false; } } diff --git a/plugins/omemo/src/logic/trust_manager.vala b/plugins/omemo/src/logic/trust_manager.vala index d57adc35..662cea73 100644 --- a/plugins/omemo/src/logic/trust_manager.vala +++ b/plugins/omemo/src/logic/trust_manager.vala @@ -34,13 +34,15 @@ public class TrustManager { .set(db.trust.blind_trust, blind_trust).perform(); } - public void set_device_trust(Account account, Jid jid, int device_id, Database.IdentityMetaTable.TrustLevel trust_level) { + 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; @@ -54,7 +56,7 @@ public class TrustManager { } if (selection != null) { app_db.content_item.update() - .set(app_db.content_item.hide, trust_level == Database.IdentityMetaTable.TrustLevel.UNTRUSTED || trust_level == Database.IdentityMetaTable.TrustLevel.UNKNOWN) + .set(app_db.content_item.hide, trust_level == TrustLevel.UNTRUSTED || trust_level == TrustLevel.UNKNOWN) .where(selection, selection_args) .perform(); } @@ -135,6 +137,8 @@ public class TrustManager { } } } + + // Encrypt the key for each own device address.name = self_jid.bare_jid.to_string(); foreach(int32 device_id in get_trusted_devices(account, self_jid)) { if (module.is_ignored_device(self_jid, device_id)) { @@ -175,7 +179,7 @@ public class TrustManager { 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] != Database.IdentityMetaTable.TrustLevel.UNKNOWN || device[db.identity_meta.identity_key_public_base64] == null) + 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; @@ -214,8 +218,8 @@ public class TrustManager { } int identity_id = db.identity.get_id(conversation.account.id); - Database.IdentityMetaTable.TrustLevel trust_level = (Database.IdentityMetaTable.TrustLevel) db.identity_meta.get_device(identity_id, jid.bare_jid.to_string(), device_id)[db.identity_meta.trust_level]; - if (trust_level == Database.IdentityMetaTable.TrustLevel.UNTRUSTED || trust_level == Database.IdentityMetaTable.TrustLevel.UNKNOWN) { + 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); } @@ -224,7 +228,7 @@ public class TrustManager { .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 != Database.IdentityMetaTable.TrustLevel.UNTRUSTED) + .value(db.content_item_meta.trusted_when_received, trust_level != TrustLevel.UNTRUSTED) .perform(); } return false; @@ -281,6 +285,7 @@ public class TrustManager { if (real_jid != null) { possible_jids.add(real_jid); } 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(sid)) { possible_jids.add(new Jid(row[db.identity_meta.address_name])); } @@ -320,7 +325,7 @@ public class TrustManager { continue; } - // If we figured out which real jid a message comes from due to + // 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; } diff --git a/plugins/omemo/src/protocol/stream_module.vala b/plugins/omemo/src/protocol/stream_module.vala index 555fd68a..9d78dde5 100644 --- a/plugins/omemo/src/protocol/stream_module.vala +++ b/plugins/omemo/src/protocol/stream_module.vala @@ -84,7 +84,7 @@ public class StreamModule : XmppStreamModule { } } } - address.device_id = 0; + address.device_id = 0; // TODO: Hack to have address obj live longer } public void fetch_bundle(XmppStream stream, Jid jid, int device_id) { diff --git a/plugins/omemo/src/trust_level.vala b/plugins/omemo/src/trust_level.vala new file mode 100644 index 00000000..afcfb0fb --- /dev/null +++ b/plugins/omemo/src/trust_level.vala @@ -0,0 +1,15 @@ +namespace Dino.Plugins.Omemo { + +public enum TrustLevel { + VERIFIED, + TRUSTED, + UNTRUSTED, + UNKNOWN; + + public string to_string() { + int val = this; + return val.to_string(); + } +} + +} diff --git a/plugins/omemo/src/ui/contact_details_dialog.vala b/plugins/omemo/src/ui/contact_details_dialog.vala index 6899acf6..eee06f0f 100644 --- a/plugins/omemo/src/ui/contact_details_dialog.vala +++ b/plugins/omemo/src/ui/contact_details_dialog.vala @@ -91,7 +91,7 @@ public class ContactDetailsDialog : Gtk.Dialog { if(own && device[plugin.db.identity_meta.device_id] == own_id) { continue; } - add_fingerprint(device, (Database.IdentityMetaTable.TrustLevel) device[plugin.db.identity_meta.trust_level]); + add_fingerprint(device, (TrustLevel) device[plugin.db.identity_meta.trust_level]); } auto_accept_switch.set_active(plugin.db.trust.get_blind_trust(identity_id, jid.bare_jid.to_string())); @@ -103,8 +103,8 @@ public class ContactDetailsDialog : Gtk.Dialog { new_keys_container.visible = false; foreach (Row device in plugin.db.identity_meta.get_new_devices(identity_id, jid.to_string())) { - plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], Database.IdentityMetaTable.TrustLevel.TRUSTED); - add_fingerprint(device, Database.IdentityMetaTable.TrustLevel.TRUSTED); + plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.TRUSTED); + add_fingerprint(device, TrustLevel.TRUSTED); } } @@ -121,17 +121,17 @@ public class ContactDetailsDialog : Gtk.Dialog { private void set_row(int trust, bool now_active, Image img, Label status_lbl, Label lbl, ListBoxRow lbr){ switch(trust) { - case Database.IdentityMetaTable.TrustLevel.TRUSTED: + case TrustLevel.TRUSTED: img.icon_name = "emblem-ok-symbolic"; status_lbl.set_markup("%s".printf(_("Accepted"))); lbl.get_style_context().remove_class("dim-label"); break; - case Database.IdentityMetaTable.TrustLevel.UNTRUSTED: + case TrustLevel.UNTRUSTED: img.icon_name = "action-unavailable-symbolic"; status_lbl.set_markup("%s".printf(_("Rejected"))); lbl.get_style_context().add_class("dim-label"); break; - case Database.IdentityMetaTable.TrustLevel.VERIFIED: + case TrustLevel.VERIFIED: img.icon_name = "security-high-symbolic"; status_lbl.set_markup("%s".printf(_("Verified"))); lbl.get_style_context().remove_class("dim-label"); @@ -145,7 +145,7 @@ public class ContactDetailsDialog : Gtk.Dialog { } } - private void add_fingerprint(Row device, Database.IdentityMetaTable.TrustLevel trust) { + private void add_fingerprint(Row device, TrustLevel trust) { keys_container.visible = true; ListBoxRow lbr = new ListBoxRow() { visible = true, activatable = true, hexpand = true }; @@ -188,14 +188,14 @@ public class ContactDetailsDialog : Gtk.Dialog { private void update_device(int response, Row device){ switch (response) { - case Database.IdentityMetaTable.TrustLevel.TRUSTED: - plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], Database.IdentityMetaTable.TrustLevel.TRUSTED); + case TrustLevel.TRUSTED: + plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.TRUSTED); break; - case Database.IdentityMetaTable.TrustLevel.UNTRUSTED: - plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], Database.IdentityMetaTable.TrustLevel.UNTRUSTED); + case TrustLevel.UNTRUSTED: + plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.UNTRUSTED); break; - case Database.IdentityMetaTable.TrustLevel.VERIFIED: - plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], Database.IdentityMetaTable.TrustLevel.VERIFIED); + case TrustLevel.VERIFIED: + plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.VERIFIED); plugin.trust_manager.set_blind_trust(account, jid, false); auto_accept_switch.set_active(false); break; @@ -219,15 +219,15 @@ public class ContactDetailsDialog : Gtk.Dialog { no_button.get_style_context().add_class("destructive-action"); yes_button.clicked.connect(() => { - plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], Database.IdentityMetaTable.TrustLevel.TRUSTED); - add_fingerprint(device, Database.IdentityMetaTable.TrustLevel.TRUSTED); + plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.TRUSTED); + add_fingerprint(device, TrustLevel.TRUSTED); new_keys_listbox.remove(lbr); if (new_keys_listbox.get_children().length() < 1) new_keys_container.visible = false; }); no_button.clicked.connect(() => { - plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], Database.IdentityMetaTable.TrustLevel.UNTRUSTED); - add_fingerprint(device, Database.IdentityMetaTable.TrustLevel.UNTRUSTED); + plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.UNTRUSTED); + add_fingerprint(device, TrustLevel.UNTRUSTED); new_keys_listbox.remove(lbr); if (new_keys_listbox.get_children().length() < 1) new_keys_container.visible = false; }); diff --git a/plugins/omemo/src/ui/manage_key_dialog.vala b/plugins/omemo/src/ui/manage_key_dialog.vala index 87d43de8..51dcbad0 100644 --- a/plugins/omemo/src/ui/manage_key_dialog.vala +++ b/plugins/omemo/src/ui/manage_key_dialog.vala @@ -50,7 +50,7 @@ public class ManageKeyDialog : Gtk.Dialog { manage_stack.set_visible_child_name("confirm"); ok_button.sensitive = true; return_to_main = false; - current_response = Database.IdentityMetaTable.TrustLevel.VERIFIED; + current_response = TrustLevel.VERIFIED; }); verify_no_button.clicked.connect(() => { @@ -115,17 +115,17 @@ public class ManageKeyDialog : Gtk.Dialog { ListBoxRow accept_row = new ListBoxRow() {visible = true }; accept_row.add(make_action_box(_("Accept key"), _("Start accepting this key during communication with its associated contact"))); - switch((Database.IdentityMetaTable.TrustLevel) device[db.identity_meta.trust_level]) { - case Database.IdentityMetaTable.TrustLevel.TRUSTED: + switch((TrustLevel) device[db.identity_meta.trust_level]) { + case TrustLevel.TRUSTED: main_desc_label.set_markup(_("This key is currently %s.").printf(""+_("accepted")+"")+" "+_("This means it can be used by %s to receive and send messages.").printf(@"$(device[db.identity_meta.address_name])")); main_action_list.add(verify_row); main_action_list.add(reject_row); break; - case Database.IdentityMetaTable.TrustLevel.VERIFIED: + case TrustLevel.VERIFIED: main_desc_label.set_markup(_("This key is currently %s.").printf(""+_("verified")+"")+" "+_("This means it can be used by %s to receive and send messages.") + " " + _("Additionally it has been verified to match the key on the contact's device.").printf(@"$(device[db.identity_meta.address_name])")); main_action_list.add(reject_row); break; - case Database.IdentityMetaTable.TrustLevel.UNTRUSTED: + case TrustLevel.UNTRUSTED: main_desc_label.set_markup(_("This key is currently %s.").printf(""+_("rejected")+"")+" "+_("This means it cannot be used by %s to receive messages, and any messages sent by it will be ignored.").printf(@"$(device[db.identity_meta.address_name])")); main_action_list.add(accept_row); break; @@ -142,7 +142,7 @@ public class ManageKeyDialog : Gtk.Dialog { manage_stack.set_visible_child_name("confirm"); ok_button.sensitive = true; return_to_main = true; - current_response = Database.IdentityMetaTable.TrustLevel.UNTRUSTED; + current_response = TrustLevel.UNTRUSTED; } else if (row == accept_row) { confirm_image.set_from_icon_name("emblem-ok-symbolic", IconSize.DIALOG); confirm_title_label.label = _("Accept key"); @@ -150,7 +150,7 @@ public class ManageKeyDialog : Gtk.Dialog { manage_stack.set_visible_child_name("confirm"); ok_button.sensitive = true; return_to_main = true; - current_response = Database.IdentityMetaTable.TrustLevel.TRUSTED; + current_response = TrustLevel.TRUSTED; } cancel_button.label = _("Back"); });