aboutsummaryrefslogtreecommitdiff
path: root/xmpp-vala
diff options
context:
space:
mode:
authorfiaxh <git@lightrise.org>2021-04-25 19:49:10 +0200
committerfiaxh <git@lightrise.org>2021-04-29 16:13:25 +0200
commit421f43dd8bd993eb88581e1b5011cc061ceb4fc8 (patch)
tree6495066b5e608188d8837dbcc133c5adc8e57c45 /xmpp-vala
parent5d85b6cdb0165d863aadd25d9a73707b8f5cc83e (diff)
downloaddino-421f43dd8bd993eb88581e1b5011cc061ceb4fc8.tar.gz
dino-421f43dd8bd993eb88581e1b5011cc061ceb4fc8.zip
Add support for OMEMO call encryption
Diffstat (limited to 'xmpp-vala')
-rw-r--r--xmpp-vala/CMakeLists.txt3
-rw-r--r--xmpp-vala/src/module/iq/module.vala5
-rw-r--r--xmpp-vala/src/module/xep/0166_jingle/content.vala3
-rw-r--r--xmpp-vala/src/module/xep/0166_jingle/content_transport.vala2
-rw-r--r--xmpp-vala/src/module/xep/0166_jingle/jingle_module.vala4
-rw-r--r--xmpp-vala/src/module/xep/0166_jingle/session.vala10
-rw-r--r--xmpp-vala/src/module/xep/0167_jingle_rtp/content_parameters.vala3
-rw-r--r--xmpp-vala/src/module/xep/0176_jingle_ice_udp/jingle_ice_udp_module.vala2
-rw-r--r--xmpp-vala/src/module/xep/0176_jingle_ice_udp/transport_parameters.vala6
-rw-r--r--xmpp-vala/src/module/xep/0260_jingle_socks5_bytestreams.vala2
-rw-r--r--xmpp-vala/src/module/xep/0261_jingle_in_band_bytestreams.vala2
-rw-r--r--xmpp-vala/src/module/xep/0353_jingle_message_initiation.vala2
-rw-r--r--xmpp-vala/src/module/xep/0384_omemo/omemo_decryptor.vala62
-rw-r--r--xmpp-vala/src/module/xep/0384_omemo/omemo_encryptor.vala116
14 files changed, 205 insertions, 17 deletions
diff --git a/xmpp-vala/CMakeLists.txt b/xmpp-vala/CMakeLists.txt
index 3aa10caf..bf8f0068 100644
--- a/xmpp-vala/CMakeLists.txt
+++ b/xmpp-vala/CMakeLists.txt
@@ -109,6 +109,9 @@ SOURCES
"src/module/xep/0176_jingle_ice_udp/jingle_ice_udp_module.vala"
"src/module/xep/0176_jingle_ice_udp/transport_parameters.vala"
+ "src/module/xep/0384_omemo/omemo_encryptor.vala"
+ "src/module/xep/0384_omemo/omemo_decryptor.vala"
+
"src/module/xep/0184_message_delivery_receipts.vala"
"src/module/xep/0191_blocking_command.vala"
"src/module/xep/0198_stream_management.vala"
diff --git a/xmpp-vala/src/module/iq/module.vala b/xmpp-vala/src/module/iq/module.vala
index 56605d01..17cd3f0d 100644
--- a/xmpp-vala/src/module/iq/module.vala
+++ b/xmpp-vala/src/module/iq/module.vala
@@ -6,6 +6,9 @@ namespace Xmpp.Iq {
public class Module : XmppStreamNegotiationModule {
public static ModuleIdentity<Module> IDENTITY = new ModuleIdentity<Module>(NS_URI, "iq_module");
+ public signal void preprocess_incoming_iq_set_get(XmppStream stream, Stanza iq_stanza);
+ public signal void preprocess_outgoing_iq_set_get(XmppStream stream, Stanza iq_stanza);
+
private HashMap<string, ResponseListener> responseListeners = new HashMap<string, ResponseListener>();
private HashMap<string, ArrayList<Handler>> namespaceRegistrants = new HashMap<string, ArrayList<Handler>>();
@@ -23,6 +26,7 @@ namespace Xmpp.Iq {
public delegate void OnResult(XmppStream stream, Iq.Stanza iq);
public void send_iq(XmppStream stream, Iq.Stanza iq, owned OnResult? listener = null) {
+ preprocess_outgoing_iq_set_get(stream, iq);
stream.write(iq.stanza);
if (listener != null) {
responseListeners[iq.id] = new ResponseListener((owned) listener);
@@ -70,6 +74,7 @@ namespace Xmpp.Iq {
} else {
Gee.List<StanzaNode> children = node.get_all_subnodes();
if (children.size == 1 && namespaceRegistrants.has_key(children[0].ns_uri)) {
+ preprocess_incoming_iq_set_get(stream, iq);
Gee.List<Handler> handlers = namespaceRegistrants[children[0].ns_uri];
foreach (Handler handler in handlers) {
if (iq.type_ == Iq.Stanza.TYPE_GET) {
diff --git a/xmpp-vala/src/module/xep/0166_jingle/content.vala b/xmpp-vala/src/module/xep/0166_jingle/content.vala
index beb12183..befe02f4 100644
--- a/xmpp-vala/src/module/xep/0166_jingle/content.vala
+++ b/xmpp-vala/src/module/xep/0166_jingle/content.vala
@@ -34,9 +34,8 @@ public class Xmpp.Xep.Jingle.Content : Object {
public weak Session session;
public Map<uint8, ComponentConnection> component_connections = new HashMap<uint8, ComponentConnection>(); // TODO private
- public ContentEncryption? encryption { get; set; }
+ public HashMap<string, ContentEncryption> encryptions = new HashMap<string, ContentEncryption>();
- // INITIATE_SENT | INITIATE_RECEIVED | CONNECTING
public Set<string> tried_transport_methods = new HashSet<string>();
diff --git a/xmpp-vala/src/module/xep/0166_jingle/content_transport.vala b/xmpp-vala/src/module/xep/0166_jingle/content_transport.vala
index cd74c836..2697a01c 100644
--- a/xmpp-vala/src/module/xep/0166_jingle/content_transport.vala
+++ b/xmpp-vala/src/module/xep/0166_jingle/content_transport.vala
@@ -21,7 +21,7 @@ namespace Xmpp.Xep.Jingle {
public abstract uint8 components { get; }
public abstract void set_content(Content content);
- public abstract StanzaNode to_transport_stanza_node();
+ public abstract StanzaNode to_transport_stanza_node(string action_type);
public abstract void handle_transport_accept(StanzaNode transport) throws IqError;
public abstract void handle_transport_info(StanzaNode transport) throws IqError;
public abstract void create_transport_connection(XmppStream stream, Content content);
diff --git a/xmpp-vala/src/module/xep/0166_jingle/jingle_module.vala b/xmpp-vala/src/module/xep/0166_jingle/jingle_module.vala
index 7314ca6c..186848f6 100644
--- a/xmpp-vala/src/module/xep/0166_jingle/jingle_module.vala
+++ b/xmpp-vala/src/module/xep/0166_jingle/jingle_module.vala
@@ -3,7 +3,7 @@ using Xmpp;
namespace Xmpp.Xep.Jingle {
- internal const string NS_URI = "urn:xmpp:jingle:1";
+ public const string NS_URI = "urn:xmpp:jingle:1";
private const string ERROR_NS_URI = "urn:xmpp:jingle:errors:1";
// This module can only be attached to one stream at a time.
@@ -131,7 +131,7 @@ namespace Xmpp.Xep.Jingle {
.put_attribute("name", content.content_name)
.put_attribute("senders", content.senders.to_string())
.put_node(content.content_params.get_description_node())
- .put_node(content.transport_params.to_transport_stanza_node());
+ .put_node(content.transport_params.to_transport_stanza_node("session-initiate"));
if (content.security_params != null) {
content_node.put_node(content.security_params.to_security_stanza_node(stream, my_jid, receiver_full_jid));
}
diff --git a/xmpp-vala/src/module/xep/0166_jingle/session.vala b/xmpp-vala/src/module/xep/0166_jingle/session.vala
index 5fe89415..4d04c8d5 100644
--- a/xmpp-vala/src/module/xep/0166_jingle/session.vala
+++ b/xmpp-vala/src/module/xep/0166_jingle/session.vala
@@ -221,7 +221,7 @@ public class Xmpp.Xep.Jingle.Session : Object {
.put_attribute("name", content.content_name)
.put_attribute("senders", content.senders.to_string())
.put_node(content.content_params.get_description_node())
- .put_node(content.transport_params.to_transport_stanza_node()));
+ .put_node(content.transport_params.to_transport_stanza_node("content-add")));
Iq.Stanza iq = new Iq.Stanza.set(content_add_node) { to=peer_full_jid };
yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, iq);
@@ -343,7 +343,7 @@ public class Xmpp.Xep.Jingle.Session : Object {
.put_attribute("name", content.content_name)
.put_attribute("senders", content.senders.to_string())
.put_node(content.content_params.get_description_node())
- .put_node(content.transport_params.to_transport_stanza_node());
+ .put_node(content.transport_params.to_transport_stanza_node("session-accept"));
jingle.put_node(content_node);
}
@@ -379,7 +379,7 @@ public class Xmpp.Xep.Jingle.Session : Object {
.put_attribute("name", content.content_name)
.put_attribute("senders", content.senders.to_string())
.put_node(content.content_params.get_description_node())
- .put_node(content.transport_params.to_transport_stanza_node()));
+ .put_node(content.transport_params.to_transport_stanza_node("content-accept")));
Iq.Stanza iq = new Iq.Stanza.set(content_accept_node) { to=peer_full_jid };
stream.get_module(Iq.Module.IDENTITY).send_iq(stream, iq);
@@ -477,7 +477,7 @@ public class Xmpp.Xep.Jingle.Session : Object {
.put_node(new StanzaNode.build("content", NS_URI)
.put_attribute("creator", "initiator")
.put_attribute("name", content.content_name)
- .put_node(transport_params.to_transport_stanza_node())
+ .put_node(transport_params.to_transport_stanza_node("transport-accept"))
);
Iq.Stanza iq_response = new Iq.Stanza.set(jingle_response) { to=peer_full_jid };
stream.get_module(Iq.Module.IDENTITY).send_iq(stream, iq_response);
@@ -493,7 +493,7 @@ public class Xmpp.Xep.Jingle.Session : Object {
.put_node(new StanzaNode.build("content", NS_URI)
.put_attribute("creator", "initiator")
.put_attribute("name", content.content_name)
- .put_node(transport_params.to_transport_stanza_node())
+ .put_node(transport_params.to_transport_stanza_node("transport-replace"))
);
Iq.Stanza iq = new Iq.Stanza.set(jingle) { to=peer_full_jid };
stream.get_module(Iq.Module.IDENTITY).send_iq(stream, iq);
diff --git a/xmpp-vala/src/module/xep/0167_jingle_rtp/content_parameters.vala b/xmpp-vala/src/module/xep/0167_jingle_rtp/content_parameters.vala
index d4440169..344fe8b8 100644
--- a/xmpp-vala/src/module/xep/0167_jingle_rtp/content_parameters.vala
+++ b/xmpp-vala/src/module/xep/0167_jingle_rtp/content_parameters.vala
@@ -133,7 +133,8 @@ public class Xmpp.Xep.JingleRtp.Parameters : Jingle.ContentParameters, Object {
local_crypto = null;
}
if (remote_crypto != null && local_crypto != null) {
- content.encryption = new Xmpp.Xep.Jingle.ContentEncryption() { encryption_ns = "", encryption_name = "SRTP", our_key=local_crypto.key, peer_key=remote_crypto.key };
+ var content_encryption = new Xmpp.Xep.Jingle.ContentEncryption() { encryption_ns = "", encryption_name = "SRTP", our_key=local_crypto.key, peer_key=remote_crypto.key };
+ content.encryptions[content_encryption.encryption_name] = content_encryption;
}
this.stream = parent.create_stream(content);
diff --git a/xmpp-vala/src/module/xep/0176_jingle_ice_udp/jingle_ice_udp_module.vala b/xmpp-vala/src/module/xep/0176_jingle_ice_udp/jingle_ice_udp_module.vala
index 5211e3a9..87c010dd 100644
--- a/xmpp-vala/src/module/xep/0176_jingle_ice_udp/jingle_ice_udp_module.vala
+++ b/xmpp-vala/src/module/xep/0176_jingle_ice_udp/jingle_ice_udp_module.vala
@@ -4,7 +4,7 @@ using Xmpp;
namespace Xmpp.Xep.JingleIceUdp {
-private const string NS_URI = "urn:xmpp:jingle:transports:ice-udp:1";
+public const string NS_URI = "urn:xmpp:jingle:transports:ice-udp:1";
public const string DTLS_NS_URI = "urn:xmpp:jingle:apps:dtls:0";
public abstract class Module : XmppStreamModule, Jingle.Transport {
diff --git a/xmpp-vala/src/module/xep/0176_jingle_ice_udp/transport_parameters.vala b/xmpp-vala/src/module/xep/0176_jingle_ice_udp/transport_parameters.vala
index ed0fab50..83da296b 100644
--- a/xmpp-vala/src/module/xep/0176_jingle_ice_udp/transport_parameters.vala
+++ b/xmpp-vala/src/module/xep/0176_jingle_ice_udp/transport_parameters.vala
@@ -65,13 +65,13 @@ public abstract class Xmpp.Xep.JingleIceUdp.IceUdpTransportParameters : Jingle.T
this.content = null;
}
- public StanzaNode to_transport_stanza_node() {
+ public StanzaNode to_transport_stanza_node(string action_type) {
var node = new StanzaNode.build("transport", NS_URI)
.add_self_xmlns()
.put_attribute("ufrag", local_ufrag)
.put_attribute("pwd", local_pwd);
- if (own_fingerprint != null) {
+ if (own_fingerprint != null && action_type != "transport-info") {
var fingerprint_node = new StanzaNode.build("fingerprint", DTLS_NS_URI)
.add_self_xmlns()
.put_attribute("hash", "sha-256")
@@ -137,7 +137,7 @@ public abstract class Xmpp.Xep.JingleIceUdp.IceUdpTransportParameters : Jingle.T
private void check_send_transport_info() {
if (this.content != null && unsent_local_candidates.size > 0) {
- content.send_transport_info(to_transport_stanza_node());
+ content.send_transport_info(to_transport_stanza_node("transport-info"));
}
}
diff --git a/xmpp-vala/src/module/xep/0260_jingle_socks5_bytestreams.vala b/xmpp-vala/src/module/xep/0260_jingle_socks5_bytestreams.vala
index 1c4e0d38..6edebbbc 100644
--- a/xmpp-vala/src/module/xep/0260_jingle_socks5_bytestreams.vala
+++ b/xmpp-vala/src/module/xep/0260_jingle_socks5_bytestreams.vala
@@ -391,7 +391,7 @@ class Parameters : Jingle.TransportParameters, Object {
}
- public StanzaNode to_transport_stanza_node() {
+ public StanzaNode to_transport_stanza_node(string action_type) {
StanzaNode transport = new StanzaNode.build("transport", NS_URI)
.add_self_xmlns()
.put_attribute("dstaddr", local_dstaddr);
diff --git a/xmpp-vala/src/module/xep/0261_jingle_in_band_bytestreams.vala b/xmpp-vala/src/module/xep/0261_jingle_in_band_bytestreams.vala
index 5bb71831..f7c77544 100644
--- a/xmpp-vala/src/module/xep/0261_jingle_in_band_bytestreams.vala
+++ b/xmpp-vala/src/module/xep/0261_jingle_in_band_bytestreams.vala
@@ -73,7 +73,7 @@ class Parameters : Jingle.TransportParameters, Object {
}
- public StanzaNode to_transport_stanza_node() {
+ public StanzaNode to_transport_stanza_node(string action_type) {
return new StanzaNode.build("transport", NS_URI)
.add_self_xmlns()
.put_attribute("block-size", block_size.to_string())
diff --git a/xmpp-vala/src/module/xep/0353_jingle_message_initiation.vala b/xmpp-vala/src/module/xep/0353_jingle_message_initiation.vala
index 08e803a2..e26be515 100644
--- a/xmpp-vala/src/module/xep/0353_jingle_message_initiation.vala
+++ b/xmpp-vala/src/module/xep/0353_jingle_message_initiation.vala
@@ -102,10 +102,12 @@ namespace Xmpp.Xep.JingleMessageInitiation {
}
public override void attach(XmppStream stream) {
+ stream.get_module(ServiceDiscovery.Module.IDENTITY).add_feature(stream, NS_URI);
stream.get_module(MessageModule.IDENTITY).received_message.connect(on_received_message);
}
public override void detach(XmppStream stream) {
+ stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
stream.get_module(MessageModule.IDENTITY).received_message.disconnect(on_received_message);
}
diff --git a/xmpp-vala/src/module/xep/0384_omemo/omemo_decryptor.vala b/xmpp-vala/src/module/xep/0384_omemo/omemo_decryptor.vala
new file mode 100644
index 00000000..8e3213ae
--- /dev/null
+++ b/xmpp-vala/src/module/xep/0384_omemo/omemo_decryptor.vala
@@ -0,0 +1,62 @@
+using Gee;
+using Xmpp.Xep;
+using Xmpp;
+
+namespace Xmpp.Xep.Omemo {
+
+ public abstract class OmemoDecryptor : XmppStreamModule {
+
+ public static Xmpp.ModuleIdentity<OmemoDecryptor> IDENTITY = new Xmpp.ModuleIdentity<OmemoDecryptor>(NS_URI, "0384_omemo_decryptor");
+
+ public abstract uint32 own_device_id { get; }
+
+ public abstract string decrypt(uint8[] ciphertext, uint8[] key, uint8[] iv) throws GLib.Error;
+
+ public abstract uint8[] decrypt_key(ParsedData data, Jid from_jid) throws GLib.Error;
+
+ public ParsedData? parse_node(StanzaNode encrypted_node) {
+ ParsedData ret = new ParsedData();
+
+ StanzaNode? header_node = encrypted_node.get_subnode("header");
+ if (header_node == null) return null;
+
+ ret.sid = header_node.get_attribute_int("sid", -1);
+ if (ret.sid == -1) return null;
+
+ string? payload_str = encrypted_node.get_deep_string_content("payload");
+ if (payload_str != null) ret.ciphertext = Base64.decode(payload_str);
+
+ string? iv_str = header_node.get_deep_string_content("iv");
+ if (iv_str == null) return null;
+ ret.iv = Base64.decode(iv_str);
+
+ foreach (StanzaNode key_node in header_node.get_subnodes("key")) {
+ debug("Is ours? %d =? %u", key_node.get_attribute_int("rid"), own_device_id);
+ if (key_node.get_attribute_int("rid") == own_device_id) {
+ string? key_node_content = key_node.get_string_content();
+ if (key_node_content == null) continue;
+ uchar[] encrypted_key = Base64.decode(key_node_content);
+ ret.our_potential_encrypted_keys[new Bytes.take(encrypted_key)] = key_node.get_attribute_bool("prekey");
+ }
+ }
+
+ return ret;
+ }
+
+ public override void attach(XmppStream stream) { }
+ public override void detach(XmppStream stream) { }
+ public override string get_ns() { return NS_URI; }
+ public override string get_id() { return IDENTITY.id; }
+ }
+
+ public class ParsedData {
+ public int sid;
+ public uint8[] ciphertext;
+ public uint8[] iv;
+ public uchar[] encrypted_key;
+ public bool is_prekey;
+
+ public HashMap<Bytes, bool> our_potential_encrypted_keys = new HashMap<Bytes, bool>();
+ }
+}
+
diff --git a/xmpp-vala/src/module/xep/0384_omemo/omemo_encryptor.vala b/xmpp-vala/src/module/xep/0384_omemo/omemo_encryptor.vala
new file mode 100644
index 00000000..6509bfe3
--- /dev/null
+++ b/xmpp-vala/src/module/xep/0384_omemo/omemo_encryptor.vala
@@ -0,0 +1,116 @@
+using Gee;
+using Xmpp.Xep;
+using Xmpp;
+
+namespace Xmpp.Xep.Omemo {
+
+ public const string NS_URI = "eu.siacs.conversations.axolotl";
+ public const string NODE_DEVICELIST = NS_URI + ".devicelist";
+ public const string NODE_BUNDLES = NS_URI + ".bundles";
+ public const string NODE_VERIFICATION = NS_URI + ".verification";
+
+ public abstract class OmemoEncryptor : XmppStreamModule {
+
+ public static Xmpp.ModuleIdentity<OmemoEncryptor> IDENTITY = new Xmpp.ModuleIdentity<OmemoEncryptor>(NS_URI, "0384_omemo_encryptor");
+
+ public abstract uint32 own_device_id { get; }
+
+ public abstract EncryptionData encrypt_plaintext(string plaintext) throws GLib.Error;
+
+ public abstract void encrypt_key(Xep.Omemo.EncryptionData encryption_data, Jid jid, int32 device_id) throws GLib.Error;
+
+ public abstract EncryptionResult encrypt_key_to_recipient(XmppStream stream, Xep.Omemo.EncryptionData enc_data, Jid recipient) throws GLib.Error;
+
+ public override void attach(XmppStream stream) { }
+ public override void detach(XmppStream stream) { }
+ public override string get_ns() { return NS_URI; }
+ public override string get_id() { return IDENTITY.id; }
+ }
+
+ public class EncryptionData {
+ public uint32 own_device_id;
+ public uint8[] ciphertext;
+ public uint8[] keytag;
+ public uint8[] iv;
+
+ public Gee.List<StanzaNode> key_nodes = new ArrayList<StanzaNode>();
+
+ public EncryptionData(uint32 own_device_id) {
+ this.own_device_id = own_device_id;
+ }
+
+ public void add_device_key(int device_id, uint8[] device_key, bool prekey) {
+ StanzaNode key_node = new StanzaNode.build("key", NS_URI)
+ .put_attribute("rid", device_id.to_string())
+ .put_node(new StanzaNode.text(Base64.encode(device_key)));
+ if (prekey) {
+ key_node.put_attribute("prekey", "true");
+ }
+ key_nodes.add(key_node);
+ }
+
+ public StanzaNode get_encrypted_node() {
+ StanzaNode encrypted_node = new StanzaNode.build("encrypted", NS_URI).add_self_xmlns();
+
+ StanzaNode header_node = new StanzaNode.build("header", NS_URI)
+ .put_attribute("sid", own_device_id.to_string())
+ .put_node(new StanzaNode.build("iv", NS_URI).put_node(new StanzaNode.text(Base64.encode(iv))));
+ encrypted_node.put_node(header_node);
+
+ if (ciphertext != null) {
+ StanzaNode payload_node = new StanzaNode.build("payload", NS_URI)
+ .put_node(new StanzaNode.text(Base64.encode(ciphertext)));
+ encrypted_node.put_node(payload_node);
+ }
+
+ foreach (StanzaNode key_node in key_nodes) {
+ header_node.put_node(key_node);
+ }
+
+ return encrypted_node;
+ }
+ }
+
+ public class EncryptionResult {
+ public int lost { get; internal set; }
+ public int success { get; internal set; }
+ public int unknown { get; internal set; }
+ public int failure { get; internal set; }
+ }
+
+ public class EncryptState {
+ public bool encrypted { get; internal set; }
+ public int other_devices { get; internal set; }
+ public int other_success { get; internal set; }
+ public int other_lost { get; internal set; }
+ public int other_unknown { get; internal set; }
+ public int other_failure { get; internal set; }
+ public int other_waiting_lists { get; internal set; }
+
+ public int own_devices { get; internal set; }
+ public int own_success { get; internal set; }
+ public int own_lost { get; internal set; }
+ public int own_unknown { get; internal set; }
+ public int own_failure { get; internal set; }
+ public bool own_list { get; internal set; }
+
+ public void add_result(EncryptionResult enc_res, bool own) {
+ if (own) {
+ own_lost += enc_res.lost;
+ own_success += enc_res.success;
+ own_unknown += enc_res.unknown;
+ own_failure += enc_res.failure;
+ } else {
+ other_lost += enc_res.lost;
+ other_success += enc_res.success;
+ other_unknown += enc_res.unknown;
+ other_failure += enc_res.failure;
+ }
+ }
+
+ public string to_string() {
+ return @"EncryptState (encrypted=$encrypted, other=(devices=$other_devices, success=$other_success, lost=$other_lost, unknown=$other_unknown, failure=$other_failure, waiting_lists=$other_waiting_lists, own=(devices=$own_devices, success=$own_success, lost=$own_lost, unknown=$own_unknown, failure=$own_failure, list=$own_list))";
+ }
+ }
+}
+