aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfiaxh <git@lightrise.org>2021-05-03 13:17:17 +0200
committerfiaxh <git@lightrise.org>2021-05-11 12:52:00 +0200
commit90f9ecf62b2ebfef14de2874e7942552409632bf (patch)
treec9cd6da902ee89ab9cfcc435c2e98680e2ec3c50
parent8044b546d0ac15d34a3e6499b9c0d55d3d8f9c94 (diff)
downloaddino-90f9ecf62b2ebfef14de2874e7942552409632bf.tar.gz
dino-90f9ecf62b2ebfef14de2874e7942552409632bf.zip
Calls: Indicate whether OMEMO key is verified
-rw-r--r--libdino/src/plugin/interfaces.vala10
-rw-r--r--libdino/src/plugin/registry.vala8
-rw-r--r--libdino/src/service/calls.vala4
-rw-r--r--main/CMakeLists.txt1
-rw-r--r--main/src/ui/call_window/call_bottom_bar.vala64
-rw-r--r--main/src/ui/call_window/call_encryption_button.vala77
-rw-r--r--main/src/ui/call_window/call_window_controller.vala17
-rw-r--r--plugins/omemo/CMakeLists.txt1
-rw-r--r--plugins/omemo/src/dtls_srtp_verification_draft.vala7
-rw-r--r--plugins/omemo/src/logic/decrypt.vala3
-rw-r--r--plugins/omemo/src/plugin.vala6
-rw-r--r--plugins/omemo/src/ui/call_encryption_entry.vala57
12 files changed, 181 insertions, 74 deletions
diff --git a/libdino/src/plugin/interfaces.vala b/libdino/src/plugin/interfaces.vala
index 97951850..eadbb085 100644
--- a/libdino/src/plugin/interfaces.vala
+++ b/libdino/src/plugin/interfaces.vala
@@ -29,6 +29,16 @@ public interface EncryptionListEntry : Object {
public abstract Object? get_encryption_icon(Entities.Conversation conversation, ContentItem content_item);
}
+public interface CallEncryptionEntry : Object {
+ public abstract CallEncryptionWidget? get_widget(Account account, Xmpp.Xep.Jingle.ContentEncryption encryption);
+}
+
+public interface CallEncryptionWidget : Object {
+ public abstract string? get_title();
+ public abstract bool show_keys();
+ public abstract string? get_icon_name();
+}
+
public abstract class AccountSettingsEntry : Object {
public abstract string id { get; }
public virtual Priority priority { get { return Priority.DEFAULT; } }
diff --git a/libdino/src/plugin/registry.vala b/libdino/src/plugin/registry.vala
index 27d72b80..e28c4de7 100644
--- a/libdino/src/plugin/registry.vala
+++ b/libdino/src/plugin/registry.vala
@@ -4,6 +4,7 @@ namespace Dino.Plugins {
public class Registry {
internal ArrayList<EncryptionListEntry> encryption_list_entries = new ArrayList<EncryptionListEntry>();
+ internal HashMap<string, CallEncryptionEntry> call_encryption_entries = new HashMap<string, CallEncryptionEntry>();
internal ArrayList<AccountSettingsEntry> account_settings_entries = new ArrayList<AccountSettingsEntry>();
internal ArrayList<ContactDetailsProvider> contact_details_entries = new ArrayList<ContactDetailsProvider>();
internal Map<string, TextCommand> text_commands = new HashMap<string, TextCommand>();
@@ -25,6 +26,13 @@ public class Registry {
}
}
+ public bool register_call_entryption_entry(string ns, CallEncryptionEntry entry) {
+ lock (call_encryption_entries) {
+ call_encryption_entries[ns] = entry;
+ }
+ return true;
+ }
+
public bool register_account_settings_entry(AccountSettingsEntry entry) {
lock(account_settings_entries) {
foreach(var e in account_settings_entries) {
diff --git a/libdino/src/service/calls.vala b/libdino/src/service/calls.vala
index a44b59fd..4c3bbea7 100644
--- a/libdino/src/service/calls.vala
+++ b/libdino/src/service/calls.vala
@@ -75,7 +75,7 @@ namespace Dino {
call.account = conversation.account;
call.counterpart = conversation.counterpart;
call.ourpart = conversation.account.full_jid;
- call.time = call.local_time = new DateTime.now_utc();
+ call.time = call.local_time = call.end_time = new DateTime.now_utc();
call.state = Call.State.RINGING;
stream_interactor.get_module(CallStore.IDENTITY).add_call(call, conversation);
@@ -380,7 +380,7 @@ namespace Dino {
call.counterpart = from;
}
call.account = account;
- call.time = call.local_time = new DateTime.now_utc();
+ call.time = call.local_time = call.end_time = new DateTime.now_utc();
call.state = Call.State.RINGING;
Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(call.counterpart.bare_jid, account, Conversation.Type.CHAT);
diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt
index 69992f06..4891abb0 100644
--- a/main/CMakeLists.txt
+++ b/main/CMakeLists.txt
@@ -139,6 +139,7 @@ SOURCES
src/ui/call_window/audio_settings_popover.vala
src/ui/call_window/call_bottom_bar.vala
+ src/ui/call_window/call_encryption_button.vala
src/ui/call_window/call_window.vala
src/ui/call_window/call_window_controller.vala
src/ui/call_window/video_settings_popover.vala
diff --git a/main/src/ui/call_window/call_bottom_bar.vala b/main/src/ui/call_window/call_bottom_bar.vala
index 64b157dd..8a0604b3 100644
--- a/main/src/ui/call_window/call_bottom_bar.vala
+++ b/main/src/ui/call_window/call_bottom_bar.vala
@@ -25,8 +25,7 @@ public class Dino.Ui.CallBottomBar : Gtk.Box {
private MenuButton video_settings_button = new MenuButton() { halign=Align.END, valign=Align.END };
public VideoSettingsPopover? video_settings_popover;
- private MenuButton encryption_button = new MenuButton() { relief=ReliefStyle.NONE, height_request=30, width_request=30, margin_start=20, margin_bottom=25, halign=Align.START, valign=Align.END };
- private Image encryption_image = new Image.from_icon_name("changes-allow-symbolic", IconSize.BUTTON) { visible=true };
+ public CallEntryptionButton encryption_button = new CallEntryptionButton() { relief=ReliefStyle.NONE, height_request=30, width_request=30, margin_start=20, margin_bottom=25, halign=Align.START, valign=Align.END };
private Label label = new Label("") { margin=20, halign=Align.CENTER, valign=Align.CENTER, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, hexpand=true, visible=true };
private Stack stack = new Stack() { visible=true };
@@ -35,8 +34,6 @@ public class Dino.Ui.CallBottomBar : Gtk.Box {
Object(orientation:Orientation.HORIZONTAL, spacing:0);
Overlay default_control = new Overlay() { visible=true };
- encryption_button.add(encryption_image);
- encryption_button.get_style_context().add_class("encryption-box");
default_control.add_overlay(encryption_button);
Box main_buttons = new Box(Orientation.HORIZONTAL, 20) { margin_start=40, margin_end=40, margin=20, halign=Align.CENTER, hexpand=true, visible=true };
@@ -89,54 +86,6 @@ public class Dino.Ui.CallBottomBar : Gtk.Box {
this.get_style_context().add_class("call-bottom-bar");
}
- public void set_encryption(Xmpp.Xep.Jingle.ContentEncryption? audio_encryption, Xmpp.Xep.Jingle.ContentEncryption? video_encryption, bool same) {
- encryption_button.visible = true;
-
- Popover popover = new Popover(encryption_button);
- if (audio_encryption == null) {
- encryption_image.set_from_icon_name("changes-allow-symbolic", IconSize.BUTTON);
- encryption_button.get_style_context().add_class("unencrypted");
-
- popover.add(new Label("This call isn't encrypted.") { margin=10, visible=true } );
- return;
- }
-
- encryption_image.set_from_icon_name("changes-prevent-symbolic", IconSize.BUTTON);
- encryption_button.get_style_context().remove_class("unencrypted");
-
- Box box = new Box(Orientation.VERTICAL, 5) { margin=10, visible=true };
- if (audio_encryption.encryption_name == "OMEMO") {
- box.add(new Label("<b>This call is encrypted with OMEMO.</b>") { use_markup=true, xalign=0, visible=true } );
- } else {
- box.add(new Label("<b>This call is end-to-end encrypted.</b>") { use_markup=true, xalign=0, visible=true });
- }
-
- if (same) {
- box.add(create_media_encryption_grid(audio_encryption));
- } else {
- box.add(new Label("<b>Audio</b>") { use_markup=true, xalign=0, visible=true });
- box.add(create_media_encryption_grid(audio_encryption));
- box.add(new Label("<b>Video</b>") { use_markup=true, xalign=0, visible=true });
- box.add(create_media_encryption_grid(video_encryption));
- }
- popover.add(box);
-
- encryption_button.set_popover(popover);
- }
-
- private Grid create_media_encryption_grid(Xmpp.Xep.Jingle.ContentEncryption? encryption) {
- Grid ret = new Grid() { row_spacing=3, column_spacing=5, visible=true };
- if (encryption.peer_key.length > 0) {
- ret.attach(new Label("Peer call key") { xalign=0, visible=true }, 1, 2, 1, 1);
- ret.attach(new Label("<span font_family='monospace'>" + format_fingerprint(encryption.peer_key) + "</span>") { use_markup=true, max_width_chars=25, ellipsize=EllipsizeMode.MIDDLE, xalign=0, hexpand=true, visible=true }, 2, 2, 1, 1);
- }
- if (encryption.our_key.length > 0) {
- ret.attach(new Label("Your call key") { xalign=0, visible=true }, 1, 3, 1, 1);
- ret.attach(new Label("<span font_family='monospace'>" + format_fingerprint(encryption.our_key) + "</span>") { use_markup=true, max_width_chars=25, ellipsize=EllipsizeMode.MIDDLE, xalign=0, hexpand=true, visible=true }, 2, 3, 1, 1);
- }
- return ret;
- }
-
public AudioSettingsPopover? show_audio_device_choices(bool show) {
audio_settings_button.visible = show;
if (audio_settings_popover != null) audio_settings_popover.visible = false;
@@ -212,15 +161,4 @@ public class Dino.Ui.CallBottomBar : Gtk.Box {
public bool is_menu_active() {
return video_settings_button.active || audio_settings_button.active || encryption_button.active;
}
-
- private string format_fingerprint(uint8[] fingerprint) {
- var sb = new StringBuilder();
- for (int i = 0; i < fingerprint.length; i++) {
- sb.append("%02x".printf(fingerprint[i]));
- if (i < fingerprint.length - 1) {
- sb.append(":");
- }
- }
- return sb.str;
- }
} \ No newline at end of file
diff --git a/main/src/ui/call_window/call_encryption_button.vala b/main/src/ui/call_window/call_encryption_button.vala
new file mode 100644
index 00000000..1d785d51
--- /dev/null
+++ b/main/src/ui/call_window/call_encryption_button.vala
@@ -0,0 +1,77 @@
+using Dino.Entities;
+using Gtk;
+using Pango;
+
+public class Dino.Ui.CallEntryptionButton : MenuButton {
+
+ private Image encryption_image = new Image.from_icon_name("changes-allow-symbolic", IconSize.BUTTON) { visible=true };
+
+ construct {
+ add(encryption_image);
+ get_style_context().add_class("encryption-box");
+ this.set_popover(popover);
+ }
+
+ public void set_icon(bool encrypted, string? icon_name) {
+ this.visible = true;
+
+ if (encrypted) {
+ encryption_image.set_from_icon_name(icon_name ?? "changes-prevent-symbolic", IconSize.BUTTON);
+ get_style_context().remove_class("unencrypted");
+ } else {
+ encryption_image.set_from_icon_name(icon_name ?? "changes-allow-symbolic", IconSize.BUTTON);
+ get_style_context().add_class("unencrypted");
+ }
+ }
+
+ public void set_info(string? title, bool show_keys, Xmpp.Xep.Jingle.ContentEncryption? audio_encryption, Xmpp.Xep.Jingle.ContentEncryption? video_encryption) {
+ Popover popover = new Popover(this);
+ this.set_popover(popover);
+
+ if (audio_encryption == null) {
+ popover.add(new Label("This call is unencrypted.") { margin=10, visible=true } );
+ return;
+ }
+ if (title != null && !show_keys) {
+ popover.add(new Label(title) { use_markup=true, margin=10, visible=true } );
+ return;
+ }
+
+ Box box = new Box(Orientation.VERTICAL, 10) { margin=10, visible=true };
+ box.add(new Label("<b>%s</b>".printf(title ?? "This call is end-to-end encrypted.")) { use_markup=true, xalign=0, visible=true });
+
+ if (video_encryption == null) {
+ box.add(create_media_encryption_grid(audio_encryption));
+ } else {
+ box.add(new Label("<b>Audio</b>") { use_markup=true, xalign=0, visible=true });
+ box.add(create_media_encryption_grid(audio_encryption));
+ box.add(new Label("<b>Video</b>") { use_markup=true, xalign=0, visible=true });
+ box.add(create_media_encryption_grid(video_encryption));
+ }
+ popover.add(box);
+ }
+
+ private Grid create_media_encryption_grid(Xmpp.Xep.Jingle.ContentEncryption? encryption) {
+ Grid ret = new Grid() { row_spacing=3, column_spacing=5, visible=true };
+ if (encryption.peer_key.length > 0) {
+ ret.attach(new Label("Peer call key") { xalign=0, visible=true }, 1, 2, 1, 1);
+ ret.attach(new Label("<span font_family='monospace'>" + format_fingerprint(encryption.peer_key) + "</span>") { use_markup=true, max_width_chars=25, ellipsize=EllipsizeMode.MIDDLE, xalign=0, hexpand=true, visible=true }, 2, 2, 1, 1);
+ }
+ if (encryption.our_key.length > 0) {
+ ret.attach(new Label("Your call key") { xalign=0, visible=true }, 1, 3, 1, 1);
+ ret.attach(new Label("<span font_family='monospace'>" + format_fingerprint(encryption.our_key) + "</span>") { use_markup=true, max_width_chars=25, ellipsize=EllipsizeMode.MIDDLE, xalign=0, hexpand=true, visible=true }, 2, 3, 1, 1);
+ }
+ return ret;
+ }
+
+ private string format_fingerprint(uint8[] fingerprint) {
+ var sb = new StringBuilder();
+ for (int i = 0; i < fingerprint.length; i++) {
+ sb.append("%02x".printf(fingerprint[i]));
+ if (i < fingerprint.length - 1) {
+ sb.append(":");
+ }
+ }
+ return sb.str;
+ }
+} \ No newline at end of file
diff --git a/main/src/ui/call_window/call_window_controller.vala b/main/src/ui/call_window/call_window_controller.vala
index 7e5920ce..b07b41b1 100644
--- a/main/src/ui/call_window/call_window_controller.vala
+++ b/main/src/ui/call_window/call_window_controller.vala
@@ -78,7 +78,22 @@ public class Dino.Ui.CallWindowController : Object {
});
calls.encryption_updated.connect((call, audio_encryption, video_encryption, same) => {
if (!this.call.equals(call)) return;
- call_window.bottom_bar.set_encryption(audio_encryption, video_encryption, same);
+
+ string? title = null;
+ string? icon_name = null;
+ bool show_keys = true;
+ Plugins.Registry registry = Dino.Application.get_default().plugin_registry;
+ Plugins.CallEncryptionEntry? encryption_entry = audio_encryption != null ? registry.call_encryption_entries[audio_encryption.encryption_ns] : null;
+ if (encryption_entry != null) {
+ Plugins.CallEncryptionWidget? encryption_widgets = encryption_entry.get_widget(call.account, audio_encryption);
+ if (encryption_widgets != null) {
+ title = encryption_widgets.get_title();
+ icon_name = encryption_widgets.get_icon_name();
+ show_keys = encryption_widgets.show_keys();
+ }
+ }
+ call_window.bottom_bar.encryption_button.set_info(title, show_keys, audio_encryption, same ? null :video_encryption);
+ call_window.bottom_bar.encryption_button.set_icon(audio_encryption != null, icon_name);
});
own_video.resolution_changed.connect((width, height) => {
diff --git a/plugins/omemo/CMakeLists.txt b/plugins/omemo/CMakeLists.txt
index 944fc649..195001cb 100644
--- a/plugins/omemo/CMakeLists.txt
+++ b/plugins/omemo/CMakeLists.txt
@@ -55,6 +55,7 @@ SOURCES
src/ui/account_settings_entry.vala
src/ui/account_settings_widget.vala
src/ui/bad_messages_populator.vala
+ src/ui/call_encryption_entry.vala
src/ui/contact_details_provider.vala
src/ui/contact_details_dialog.vala
src/ui/device_notification_populator.vala
diff --git a/plugins/omemo/src/dtls_srtp_verification_draft.vala b/plugins/omemo/src/dtls_srtp_verification_draft.vala
index 66a31954..5fc9b339 100644
--- a/plugins/omemo/src/dtls_srtp_verification_draft.vala
+++ b/plugins/omemo/src/dtls_srtp_verification_draft.vala
@@ -66,7 +66,7 @@ namespace Dino.Plugins.Omemo.DtlsSrtpVerificationDraft {
stream.get_flag(Xep.Jingle.Flag.IDENTITY).get_session.begin(jingle_sid, (_, res) => {
Xep.Jingle.Session? session = stream.get_flag(Xep.Jingle.Flag.IDENTITY).get_session.end(res);
if (session == null || !session.contents_map.has_key(content_name)) return;
- var encryption = new OmemoContentEncryption() { encryption_ns=NS_URI, encryption_name="OMEMO", our_key=new uint8[0], peer_key=new uint8[0], peer_device_id=device_id_by_jingle_sid[jingle_sid] };
+ var encryption = new OmemoContentEncryption() { encryption_ns=NS_URI, encryption_name="OMEMO", our_key=new uint8[0], peer_key=new uint8[0], sid=device_id_by_jingle_sid[jingle_sid], jid=iq.from.bare_jid };
session.contents_map[content_name].encryptions[NS_URI] = encryption;
if (iq.stanza.get_deep_attribute(Xep.Jingle.NS_URI + ":jingle", "action") == "session-accept") {
@@ -143,7 +143,7 @@ namespace Dino.Plugins.Omemo.DtlsSrtpVerificationDraft {
private void on_content_add_received(XmppStream stream, Xep.Jingle.Content content) {
if (!content_names_by_jingle_sid.has_key(content.session.sid) || content_names_by_jingle_sid[content.session.sid].contains(content.content_name)) {
- var encryption = new OmemoContentEncryption() { encryption_ns=NS_URI, encryption_name="OMEMO", our_key=new uint8[0], peer_key=new uint8[0], peer_device_id=device_id_by_jingle_sid[content.session.sid] };
+ var encryption = new OmemoContentEncryption() { encryption_ns=NS_URI, encryption_name="OMEMO", our_key=new uint8[0], peer_key=new uint8[0], sid=device_id_by_jingle_sid[content.session.sid], jid=content.peer_full_jid.bare_jid };
content.encryptions[encryption.encryption_ns] = encryption;
}
}
@@ -188,7 +188,8 @@ namespace Dino.Plugins.Omemo.DtlsSrtpVerificationDraft {
}
public class OmemoContentEncryption : Xep.Jingle.ContentEncryption {
- public int peer_device_id { get; set; }
+ public Jid jid { get; set; }
+ public int sid { get; set; }
}
}
diff --git a/plugins/omemo/src/logic/decrypt.vala b/plugins/omemo/src/logic/decrypt.vala
index 3cdacbf7..cfbb9c58 100644
--- a/plugins/omemo/src/logic/decrypt.vala
+++ b/plugins/omemo/src/logic/decrypt.vala
@@ -59,9 +59,10 @@ namespace Dino.Plugins.Omemo {
message.real_jid = possible_jid;
}
- trust_manager.message_device_id_map[message] = data.sid;
message.body = cleartext;
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);
diff --git a/plugins/omemo/src/plugin.vala b/plugins/omemo/src/plugin.vala
index 7a0304d1..643428a8 100644
--- a/plugins/omemo/src/plugin.vala
+++ b/plugins/omemo/src/plugin.vala
@@ -35,7 +35,6 @@ public class Plugin : RootInterface, Object {
public DeviceNotificationPopulator device_notification_populator;
public OwnNotifications own_notifications;
public TrustManager trust_manager;
- public DecryptMessageListener decrypt_message_listener;
public HashMap<Account, OmemoDecryptor> decryptors = new HashMap<Account, OmemoDecryptor>(Account.hash_func, Account.equals_func);
public HashMap<Account, OmemoEncryptor> encryptors = new HashMap<Account, OmemoEncryptor>(Account.hash_func, Account.equals_func);
@@ -54,6 +53,7 @@ public class Plugin : RootInterface, Object {
this.app.plugin_registry.register_contact_details_entry(contact_details_provider);
this.app.plugin_registry.register_notification_populator(device_notification_populator);
this.app.plugin_registry.register_conversation_addition_populator(new BadMessagesPopulator(this.app.stream_interactor, this));
+ this.app.plugin_registry.register_call_entryption_entry(DtlsSrtpVerificationDraft.NS_URI, new CallEncryptionEntry(db));
this.app.stream_interactor.module_manager.initialize_account_modules.connect((account, list) => {
Signal.Store signal_store = Plugin.get_context().create_store();
@@ -67,9 +67,7 @@ public class Plugin : RootInterface, Object {
this.own_notifications = new OwnNotifications(this, this.app.stream_interactor, account);
});
- decrypt_message_listener = new DecryptMessageListener(decryptors);
- app.stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(decrypt_message_listener);
-
+ app.stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(new DecryptMessageListener(decryptors));
app.stream_interactor.get_module(FileManager.IDENTITY).add_file_decryptor(new OmemoFileDecryptor());
app.stream_interactor.get_module(FileManager.IDENTITY).add_file_encryptor(new OmemoFileEncryptor());
JingleFileHelperRegistry.instance.add_encryption_helper(Encryption.OMEMO, new JetOmemo.EncryptionHelper(app.stream_interactor));
diff --git a/plugins/omemo/src/ui/call_encryption_entry.vala b/plugins/omemo/src/ui/call_encryption_entry.vala
new file mode 100644
index 00000000..69b7b686
--- /dev/null
+++ b/plugins/omemo/src/ui/call_encryption_entry.vala
@@ -0,0 +1,57 @@
+using Dino.Entities;
+using Gtk;
+using Qlite;
+using Xmpp;
+
+namespace Dino.Plugins.Omemo {
+
+ public class CallEncryptionEntry : Plugins.CallEncryptionEntry, Object {
+ private Database db;
+
+ public CallEncryptionEntry(Database db) {
+ this.db = db;
+ }
+
+ public Plugins.CallEncryptionWidget? get_widget(Account account, Xmpp.Xep.Jingle.ContentEncryption encryption) {
+ DtlsSrtpVerificationDraft.OmemoContentEncryption? omemo_encryption = encryption as DtlsSrtpVerificationDraft.OmemoContentEncryption;
+ if (omemo_encryption == null) return null;
+
+ int identity_id = db.identity.get_id(account.id);
+ Row? device = db.identity_meta.get_device(identity_id, omemo_encryption.jid.to_string(), omemo_encryption.sid);
+ if (device == null) return null;
+ TrustLevel trust = (TrustLevel) device[db.identity_meta.trust_level];
+
+ return new CallEncryptionWidget(trust);
+ }
+ }
+
+ public class CallEncryptionWidget : Plugins.CallEncryptionWidget, Object {
+
+ string? title = null;
+ string? icon = null;
+ bool should_show_keys = false;
+
+ public CallEncryptionWidget(TrustLevel trust) {
+ if (trust == TrustLevel.VERIFIED) {
+ title = "This call is <b>encrypted and verified</b> with OMEMO.";
+ icon = "dino-security-high-symbolic";
+ should_show_keys = false;
+ } else {
+ title = "This call is encrypted with OMEMO.";
+ should_show_keys = true;
+ }
+ }
+
+ public string? get_title() {
+ return title;
+ }
+
+ public string? get_icon_name() {
+ return icon;
+ }
+
+ public bool show_keys() {
+ return should_show_keys;
+ }
+ }
+}