Use the same DTLS fingerprint in all contents. Display audio+video enc keys in UI if they differ.
This commit is contained in:
parent
7d2e647690
commit
0ad968df36
|
@ -14,7 +14,7 @@ namespace Dino {
|
||||||
public signal void counterpart_ringing(Call call);
|
public signal void counterpart_ringing(Call call);
|
||||||
public signal void counterpart_sends_video_updated(Call call, bool mute);
|
public signal void counterpart_sends_video_updated(Call call, bool mute);
|
||||||
public signal void info_received(Call call, Xep.JingleRtp.CallSessionInfo session_info);
|
public signal void info_received(Call call, Xep.JingleRtp.CallSessionInfo session_info);
|
||||||
public signal void encryption_updated(Call call, Xep.Jingle.ContentEncryption? encryption);
|
public signal void encryption_updated(Call call, Xep.Jingle.ContentEncryption? audio_encryption, Xep.Jingle.ContentEncryption? video_encryption, bool same);
|
||||||
|
|
||||||
public signal void stream_created(Call call, string media);
|
public signal void stream_created(Call call, string media);
|
||||||
|
|
||||||
|
@ -523,7 +523,7 @@ namespace Dino {
|
||||||
|
|
||||||
if ((audio_encryptions.has_key(call) && audio_encryptions[call].is_empty) || (video_encryptions.has_key(call) && video_encryptions[call].is_empty)) {
|
if ((audio_encryptions.has_key(call) && audio_encryptions[call].is_empty) || (video_encryptions.has_key(call) && video_encryptions[call].is_empty)) {
|
||||||
call.encryption = Encryption.NONE;
|
call.encryption = Encryption.NONE;
|
||||||
encryption_updated(call, null);
|
encryption_updated(call, null, null, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -545,16 +545,26 @@ namespace Dino {
|
||||||
|
|
||||||
if (omemo_encryption != null && dtls_encryption != null) {
|
if (omemo_encryption != null && dtls_encryption != null) {
|
||||||
call.encryption = Encryption.OMEMO;
|
call.encryption = Encryption.OMEMO;
|
||||||
encryption_updated(call, omemo_encryption);
|
Xep.Jingle.ContentEncryption? video_encryption = video_encryptions.has_key(call) ? video_encryptions[call]["http://gultsch.de/xmpp/drafts/omemo/dlts-srtp-verification"] : null;
|
||||||
|
omemo_encryption.peer_key = dtls_encryption.peer_key;
|
||||||
|
omemo_encryption.our_key = dtls_encryption.our_key;
|
||||||
|
encryption_updated(call, omemo_encryption, video_encryption, true);
|
||||||
} else if (dtls_encryption != null) {
|
} else if (dtls_encryption != null) {
|
||||||
call.encryption = Encryption.DTLS_SRTP;
|
call.encryption = Encryption.DTLS_SRTP;
|
||||||
encryption_updated(call, dtls_encryption);
|
Xep.Jingle.ContentEncryption? video_encryption = video_encryptions.has_key(call) ? video_encryptions[call][Xep.JingleIceUdp.DTLS_NS_URI] : null;
|
||||||
|
bool same = true;
|
||||||
|
if (video_encryption != null && dtls_encryption.peer_key.length == video_encryption.peer_key.length) {
|
||||||
|
for (int i = 0; i < dtls_encryption.peer_key.length; i++) {
|
||||||
|
if (dtls_encryption.peer_key[i] != video_encryption.peer_key[i]) { same = false; break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
encryption_updated(call, dtls_encryption, video_encryption, same);
|
||||||
} else if (srtp_encryption != null) {
|
} else if (srtp_encryption != null) {
|
||||||
call.encryption = Encryption.SRTP;
|
call.encryption = Encryption.SRTP;
|
||||||
encryption_updated(call, srtp_encryption);
|
encryption_updated(call, srtp_encryption, video_encryptions[call]["SRTP"], false);
|
||||||
} else {
|
} else {
|
||||||
call.encryption = Encryption.NONE;
|
call.encryption = Encryption.NONE;
|
||||||
encryption_updated(call, null);
|
encryption_updated(call, null, null, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,42 +89,54 @@ public class Dino.Ui.CallBottomBar : Gtk.Box {
|
||||||
this.get_style_context().add_class("call-bottom-bar");
|
this.get_style_context().add_class("call-bottom-bar");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set_encryption(Xmpp.Xep.Jingle.ContentEncryption? encryption) {
|
public void set_encryption(Xmpp.Xep.Jingle.ContentEncryption? audio_encryption, Xmpp.Xep.Jingle.ContentEncryption? video_encryption, bool same) {
|
||||||
encryption_button.visible = true;
|
encryption_button.visible = true;
|
||||||
|
|
||||||
Popover popover = new Popover(encryption_button);
|
Popover popover = new Popover(encryption_button);
|
||||||
|
if (audio_encryption == null) {
|
||||||
if (encryption == null) {
|
|
||||||
encryption_image.set_from_icon_name("changes-allow-symbolic", IconSize.BUTTON);
|
encryption_image.set_from_icon_name("changes-allow-symbolic", IconSize.BUTTON);
|
||||||
encryption_button.get_style_context().add_class("unencrypted");
|
encryption_button.get_style_context().add_class("unencrypted");
|
||||||
|
|
||||||
popover.add(new Label("This call isn't encrypted.") { margin=10, visible=true } );
|
popover.add(new Label("This call isn't encrypted.") { margin=10, visible=true } );
|
||||||
} else if (encryption.encryption_name == "OMEMO") {
|
return;
|
||||||
encryption_image.set_from_icon_name("changes-prevent-symbolic", IconSize.BUTTON);
|
|
||||||
encryption_button.get_style_context().remove_class("unencrypted");
|
|
||||||
|
|
||||||
popover.add(new Label("This call is encrypted with OMEMO.") { margin=10, visible=true } );
|
|
||||||
} else {
|
|
||||||
encryption_image.set_from_icon_name("changes-prevent-symbolic", IconSize.BUTTON);
|
|
||||||
encryption_button.get_style_context().remove_class("unencrypted");
|
|
||||||
|
|
||||||
Grid encryption_info_grid = new Grid() { margin=10, row_spacing=3, column_spacing=5, visible=true };
|
|
||||||
encryption_info_grid.attach(new Label("<b>This call is end-to-end encrypted.</b>") { use_markup=true, xalign=0, visible=true }, 1, 1, 2, 1);
|
|
||||||
if (encryption.peer_key.length > 0) {
|
|
||||||
encryption_info_grid.attach(new Label("Peer key") { xalign=0, visible=true }, 1, 2, 1, 1);
|
|
||||||
encryption_info_grid.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) {
|
|
||||||
encryption_info_grid.attach(new Label("Your key") { xalign=0, visible=true }, 1, 3, 1, 1);
|
|
||||||
encryption_info_grid.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
popover.add(encryption_info_grid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
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) {
|
public AudioSettingsPopover? show_audio_device_choices(bool show) {
|
||||||
audio_settings_button.visible = show;
|
audio_settings_button.visible = show;
|
||||||
if (audio_settings_popover != null) audio_settings_popover.visible = false;
|
if (audio_settings_popover != null) audio_settings_popover.visible = false;
|
||||||
|
|
|
@ -76,9 +76,9 @@ public class Dino.Ui.CallWindowController : Object {
|
||||||
call_window.set_status("ringing");
|
call_window.set_status("ringing");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
calls.encryption_updated.connect((call, encryption) => {
|
calls.encryption_updated.connect((call, audio_encryption, video_encryption, same) => {
|
||||||
if (!this.call.equals(call)) return;
|
if (!this.call.equals(call)) return;
|
||||||
call_window.bottom_bar.set_encryption(encryption);
|
call_window.bottom_bar.set_encryption(audio_encryption, video_encryption, same);
|
||||||
});
|
});
|
||||||
|
|
||||||
own_video.resolution_changed.connect((width, height) => {
|
own_video.resolution_changed.connect((width, height) => {
|
||||||
|
|
|
@ -2,10 +2,10 @@ using GnuTLS;
|
||||||
|
|
||||||
namespace Dino.Plugins.Ice.DtlsSrtp {
|
namespace Dino.Plugins.Ice.DtlsSrtp {
|
||||||
|
|
||||||
public static Handler setup() throws GLib.Error {
|
public class CredentialsCapsule {
|
||||||
var obj = new Handler();
|
public uint8[] own_fingerprint;
|
||||||
obj.generate_credentials();
|
public X509.Certificate[] own_cert;
|
||||||
return obj;
|
public X509.PrivateKey private_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Handler {
|
public class Handler {
|
||||||
|
@ -21,8 +21,7 @@ public class Handler {
|
||||||
public uint8[] peer_fingerprint { get; set; }
|
public uint8[] peer_fingerprint { get; set; }
|
||||||
public string peer_fp_algo { get; set; }
|
public string peer_fp_algo { get; set; }
|
||||||
|
|
||||||
private X509.Certificate[] own_cert;
|
private CredentialsCapsule credentials;
|
||||||
private X509.PrivateKey private_key;
|
|
||||||
private Cond buffer_cond = Cond();
|
private Cond buffer_cond = Cond();
|
||||||
private Mutex buffer_mutex = Mutex();
|
private Mutex buffer_mutex = Mutex();
|
||||||
private Gee.LinkedList<Bytes> buffer_queue = new Gee.LinkedList<Bytes>();
|
private Gee.LinkedList<Bytes> buffer_queue = new Gee.LinkedList<Bytes>();
|
||||||
|
@ -33,6 +32,11 @@ public class Handler {
|
||||||
|
|
||||||
private Crypto.Srtp.Session srtp_session = new Crypto.Srtp.Session();
|
private Crypto.Srtp.Session srtp_session = new Crypto.Srtp.Session();
|
||||||
|
|
||||||
|
public Handler.with_cert(CredentialsCapsule creds) {
|
||||||
|
this.credentials = creds;
|
||||||
|
this.own_fingerprint = creds.own_fingerprint;
|
||||||
|
}
|
||||||
|
|
||||||
public uint8[]? process_incoming_data(uint component_id, uint8[] data) {
|
public uint8[]? process_incoming_data(uint component_id, uint8[] data) {
|
||||||
if (srtp_session.has_decrypt) {
|
if (srtp_session.has_decrypt) {
|
||||||
try {
|
try {
|
||||||
|
@ -78,10 +82,10 @@ public class Handler {
|
||||||
buffer_mutex.unlock();
|
buffer_mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void generate_credentials() throws GLib.Error {
|
internal static CredentialsCapsule generate_credentials() throws GLib.Error {
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
private_key = X509.PrivateKey.create();
|
X509.PrivateKey private_key = X509.PrivateKey.create();
|
||||||
err = private_key.generate(PKAlgorithm.RSA, 2048);
|
err = private_key.generate(PKAlgorithm.RSA, 2048);
|
||||||
throw_if_error(err);
|
throw_if_error(err);
|
||||||
|
|
||||||
|
@ -99,8 +103,15 @@ public class Handler {
|
||||||
|
|
||||||
cert.sign(cert, private_key);
|
cert.sign(cert, private_key);
|
||||||
|
|
||||||
own_fingerprint = get_fingerprint(cert, DigestAlgorithm.SHA256);
|
uint8[] own_fingerprint = get_fingerprint(cert, DigestAlgorithm.SHA256);
|
||||||
own_cert = new X509.Certificate[] { (owned)cert };
|
X509.Certificate[] own_cert = new X509.Certificate[] { (owned)cert };
|
||||||
|
|
||||||
|
var creds = new CredentialsCapsule();
|
||||||
|
creds.own_fingerprint = own_fingerprint;
|
||||||
|
creds.own_cert = (owned) own_cert;
|
||||||
|
creds.private_key = (owned) private_key;
|
||||||
|
|
||||||
|
return creds;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stop_dtls_connection() {
|
public void stop_dtls_connection() {
|
||||||
|
@ -129,7 +140,7 @@ public class Handler {
|
||||||
debug("Setting up DTLS connection. We're %s", mode.to_string());
|
debug("Setting up DTLS connection. We're %s", mode.to_string());
|
||||||
|
|
||||||
CertificateCredentials cert_cred = CertificateCredentials.create();
|
CertificateCredentials cert_cred = CertificateCredentials.create();
|
||||||
int err = cert_cred.set_x509_key(own_cert, private_key);
|
int err = cert_cred.set_x509_key(credentials.own_cert, credentials.private_key);
|
||||||
throw_if_error(err);
|
throw_if_error(err);
|
||||||
|
|
||||||
Session? session = Session.create(server_or_client | InitFlags.DATAGRAM);
|
Session? session = Session.create(server_or_client | InitFlags.DATAGRAM);
|
||||||
|
@ -200,7 +211,7 @@ public class Handler {
|
||||||
srtp_session.set_encryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, client_key.extract(), client_salt.extract());
|
srtp_session.set_encryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, client_key.extract(), client_salt.extract());
|
||||||
srtp_session.set_decryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, server_key.extract(), server_salt.extract());
|
srtp_session.set_decryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, server_key.extract(), server_salt.extract());
|
||||||
}
|
}
|
||||||
return new Xmpp.Xep.Jingle.ContentEncryption() { encryption_ns=Xmpp.Xep.JingleIceUdp.DTLS_NS_URI, encryption_name = "DTLS-SRTP", our_key=own_fingerprint, peer_key=peer_fingerprint };
|
return new Xmpp.Xep.Jingle.ContentEncryption() { encryption_ns=Xmpp.Xep.JingleIceUdp.DTLS_NS_URI, encryption_name = "DTLS-SRTP", our_key=credentials.own_fingerprint, peer_key=peer_fingerprint };
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ssize_t pull_function(void* transport_ptr, uint8[] buffer) {
|
private static ssize_t pull_function(void* transport_ptr, uint8[] buffer) {
|
||||||
|
|
|
@ -10,6 +10,7 @@ public class Dino.Plugins.Ice.Module : JingleIceUdp.Module {
|
||||||
public Xep.ExternalServiceDiscovery.Service? turn_service = null;
|
public Xep.ExternalServiceDiscovery.Service? turn_service = null;
|
||||||
|
|
||||||
private weak Nice.Agent? agent;
|
private weak Nice.Agent? agent;
|
||||||
|
private HashMap<string, DtlsSrtp.CredentialsCapsule> cerds = new HashMap<string, DtlsSrtp.CredentialsCapsule>();
|
||||||
|
|
||||||
private Nice.Agent get_agent() {
|
private Nice.Agent get_agent() {
|
||||||
Nice.Agent? agent = this.agent;
|
Nice.Agent? agent = this.agent;
|
||||||
|
@ -29,11 +30,23 @@ public class Dino.Plugins.Ice.Module : JingleIceUdp.Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Jingle.TransportParameters create_transport_parameters(XmppStream stream, uint8 components, Jid local_full_jid, Jid peer_full_jid) {
|
public override Jingle.TransportParameters create_transport_parameters(XmppStream stream, uint8 components, Jid local_full_jid, Jid peer_full_jid) {
|
||||||
return new TransportParameters(get_agent(), turn_service, turn_ip, components, local_full_jid, peer_full_jid);
|
DtlsSrtp.CredentialsCapsule? cred = get_create_credentials(local_full_jid, peer_full_jid);
|
||||||
|
return new TransportParameters(get_agent(), cred, turn_service, turn_ip, components, local_full_jid, peer_full_jid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Jingle.TransportParameters parse_transport_parameters(XmppStream stream, uint8 components, Jid local_full_jid, Jid peer_full_jid, StanzaNode transport) throws Jingle.IqError {
|
public override Jingle.TransportParameters parse_transport_parameters(XmppStream stream, uint8 components, Jid local_full_jid, Jid peer_full_jid, StanzaNode transport) throws Jingle.IqError {
|
||||||
return new TransportParameters(get_agent(), turn_service, turn_ip, components, local_full_jid, peer_full_jid, transport);
|
DtlsSrtp.CredentialsCapsule? cred = get_create_credentials(local_full_jid, peer_full_jid);
|
||||||
|
return new TransportParameters(get_agent(), cred, turn_service, turn_ip, components, local_full_jid, peer_full_jid, transport);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DtlsSrtp.CredentialsCapsule? get_create_credentials(Jid local_full_jid, Jid peer_full_jid) {
|
||||||
|
string from_to_id = local_full_jid.to_string() + peer_full_jid.to_string();
|
||||||
|
try {
|
||||||
|
if (!cerds.has_key(from_to_id)) cerds[from_to_id] = DtlsSrtp.Handler.generate_credentials();
|
||||||
|
} catch (Error e) {
|
||||||
|
warning("Error creating dtls credentials: %s", e.message);
|
||||||
|
}
|
||||||
|
return cerds[from_to_id];
|
||||||
}
|
}
|
||||||
|
|
||||||
private void agent_unweak() {
|
private void agent_unweak() {
|
||||||
|
|
|
@ -60,13 +60,13 @@ public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransport
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public TransportParameters(Nice.Agent agent, Xep.ExternalServiceDiscovery.Service? turn_service, string? turn_ip, uint8 components, Jid local_full_jid, Jid peer_full_jid, StanzaNode? node = null) {
|
public TransportParameters(Nice.Agent agent, DtlsSrtp.CredentialsCapsule? credentials, Xep.ExternalServiceDiscovery.Service? turn_service, string? turn_ip, uint8 components, Jid local_full_jid, Jid peer_full_jid, StanzaNode? node = null) {
|
||||||
base(components, local_full_jid, peer_full_jid, node);
|
base(components, local_full_jid, peer_full_jid, node);
|
||||||
this.we_want_connection = (node == null);
|
this.we_want_connection = (node == null);
|
||||||
this.agent = agent;
|
this.agent = agent;
|
||||||
|
|
||||||
if (this.peer_fingerprint != null || !incoming) {
|
if (this.peer_fingerprint != null || !incoming) {
|
||||||
dtls_srtp_handler = setup_dtls(this);
|
dtls_srtp_handler = setup_dtls(this, credentials);
|
||||||
own_fingerprint = dtls_srtp_handler.own_fingerprint;
|
own_fingerprint = dtls_srtp_handler.own_fingerprint;
|
||||||
if (incoming) {
|
if (incoming) {
|
||||||
own_setup = "active";
|
own_setup = "active";
|
||||||
|
@ -113,9 +113,9 @@ public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransport
|
||||||
agent.gather_candidates(stream_id);
|
agent.gather_candidates(stream_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DtlsSrtp.Handler setup_dtls(TransportParameters tp) {
|
private static DtlsSrtp.Handler setup_dtls(TransportParameters tp, DtlsSrtp.CredentialsCapsule credentials) {
|
||||||
var weak_self = WeakRef(tp);
|
var weak_self = WeakRef(tp);
|
||||||
DtlsSrtp.Handler dtls_srtp = DtlsSrtp.setup();
|
DtlsSrtp.Handler dtls_srtp = new DtlsSrtp.Handler.with_cert(credentials);
|
||||||
dtls_srtp.send_data.connect((data) => {
|
dtls_srtp.send_data.connect((data) => {
|
||||||
TransportParameters self = (TransportParameters) weak_self.get();
|
TransportParameters self = (TransportParameters) weak_self.get();
|
||||||
if (self != null) self.agent.send(self.stream_id, 1, data);
|
if (self != null) self.agent.send(self.stream_id, 1, data);
|
||||||
|
|
Loading…
Reference in a new issue