aboutsummaryrefslogtreecommitdiff
path: root/xmpp-vala/src/module/xep
diff options
context:
space:
mode:
authorMarvin W <git@larma.de>2021-03-23 15:05:50 +0100
committerMarvin W <git@larma.de>2021-03-23 15:11:00 +0100
commitb393d4160182873ea2acd9fbc6421f7e1a3adb9e (patch)
tree77ac2fe22b0750e7ea443822cf71bc937a16dd16 /xmpp-vala/src/module/xep
parent9fed5ea8650d6e7735fca4b3fe2cf4fc29f81c33 (diff)
downloaddino-b393d4160182873ea2acd9fbc6421f7e1a3adb9e.tar.gz
dino-b393d4160182873ea2acd9fbc6421f7e1a3adb9e.zip
Add support for SRTP
Diffstat (limited to 'xmpp-vala/src/module/xep')
-rw-r--r--xmpp-vala/src/module/xep/0167_jingle_rtp/content_parameters.vala46
-rw-r--r--xmpp-vala/src/module/xep/0167_jingle_rtp/jingle_rtp_module.vala132
-rw-r--r--xmpp-vala/src/module/xep/0167_jingle_rtp/stream.vala14
3 files changed, 177 insertions, 15 deletions
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 8a3668b2..cca03543 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
@@ -17,7 +17,9 @@ public class Xmpp.Xep.JingleRtp.Parameters : Jingle.ContentParameters, Object {
public bool encryption_required { get; private set; default = false; }
public PayloadType? agreed_payload_type { get; private set; }
public Gee.List<PayloadType> payload_types = new ArrayList<PayloadType>(PayloadType.equals_func);
- public Gee.List<Crypto> cryptos = new ArrayList<Crypto>();
+ public Gee.List<Crypto> remote_cryptos = new ArrayList<Crypto>();
+ public Crypto? local_crypto = null;
+ public Crypto? remote_crypto = null;
public weak Stream? stream { get; private set; }
@@ -27,7 +29,7 @@ public class Xmpp.Xep.JingleRtp.Parameters : Jingle.ContentParameters, Object {
string media, Gee.List<PayloadType> payload_types,
string? ssrc = null, bool rtcp_mux = false,
string? bandwidth = null, string? bandwidth_type = null,
- bool encryption_required = false, Gee.List<Crypto> cryptos = new ArrayList<Crypto>()
+ bool encryption_required = false, Crypto? local_crypto = null
) {
this.parent = parent;
this.media = media;
@@ -37,7 +39,7 @@ public class Xmpp.Xep.JingleRtp.Parameters : Jingle.ContentParameters, Object {
this.bandwidth_type = bandwidth_type;
this.encryption_required = encryption_required;
this.payload_types = payload_types;
- this.cryptos = cryptos;
+ this.local_crypto = local_crypto;
}
public Parameters.from_node(Module parent, StanzaNode node) throws Jingle.IqError {
@@ -49,7 +51,7 @@ public class Xmpp.Xep.JingleRtp.Parameters : Jingle.ContentParameters, Object {
if (encryption != null) {
this.encryption_required = encryption.get_attribute_bool("required", this.encryption_required);
foreach (StanzaNode crypto in encryption.get_subnodes("crypto")) {
- this.cryptos.add(Crypto.parse(crypto));
+ this.remote_cryptos.add(Crypto.parse(crypto));
}
}
foreach (StanzaNode payloadType in node.get_subnodes("payload-type")) {
@@ -64,6 +66,15 @@ public class Xmpp.Xep.JingleRtp.Parameters : Jingle.ContentParameters, Object {
content.reject();
return;
}
+ remote_crypto = parent.pick_remote_crypto(remote_cryptos);
+ if (local_crypto == null && remote_crypto != null) {
+ local_crypto = parent.pick_local_crypto(remote_crypto);
+ }
+ if ((local_crypto == null || remote_crypto == null) && encryption_required) {
+ debug("no usable encryption, but encryption required");
+ content.reject();
+ return;
+ }
}
public void accept(XmppStream stream, Jingle.Session session, Jingle.Content content) {
@@ -97,6 +108,15 @@ public class Xmpp.Xep.JingleRtp.Parameters : Jingle.ContentParameters, Object {
}
});
+ if (remote_crypto == null || local_crypto == null) {
+ if (encryption_required) {
+ warning("Encryption required but not provided in both directions");
+ return;
+ }
+ remote_crypto = null;
+ local_crypto = null;
+ }
+
this.stream = parent.create_stream(content);
rtp_datagram.datagram_received.connect(this.stream.on_recv_rtp_data);
rtcp_datagram.datagram_received.connect(this.stream.on_recv_rtcp_data);
@@ -118,6 +138,20 @@ public class Xmpp.Xep.JingleRtp.Parameters : Jingle.ContentParameters, Object {
}
agreed_payload_type = preferred_payload_type;
+ Gee.List<StanzaNode> crypto_nodes = description_node.get_deep_subnodes("encryption", "crypto");
+ if (crypto_nodes.size == 0) {
+ warning("Counterpart didn't include any cryptos");
+ if (encryption_required) {
+ return;
+ }
+ } else {
+ Crypto preferred_crypto = Crypto.parse(crypto_nodes[0]);
+ if (local_crypto.crypto_suite != preferred_crypto.crypto_suite) {
+ warning("Counterpart's crypto suite doesn't match any of our sent ones");
+ }
+ remote_crypto = preferred_crypto;
+ }
+
accept(stream, session, content);
}
@@ -137,6 +171,10 @@ public class Xmpp.Xep.JingleRtp.Parameters : Jingle.ContentParameters, Object {
ret.put_node(payload_type.to_xml());
}
}
+ if (local_crypto != null) {
+ ret.put_node(new StanzaNode.build("encryption", NS_URI)
+ .put_node(local_crypto.to_xml()));
+ }
return ret;
}
} \ No newline at end of file
diff --git a/xmpp-vala/src/module/xep/0167_jingle_rtp/jingle_rtp_module.vala b/xmpp-vala/src/module/xep/0167_jingle_rtp/jingle_rtp_module.vala
index 35e03168..23aee6c9 100644
--- a/xmpp-vala/src/module/xep/0167_jingle_rtp/jingle_rtp_module.vala
+++ b/xmpp-vala/src/module/xep/0167_jingle_rtp/jingle_rtp_module.vala
@@ -20,6 +20,9 @@ public abstract class Module : XmppStreamModule {
public abstract async Gee.List<PayloadType> get_supported_payloads(string media);
public abstract async PayloadType? pick_payload_type(string media, Gee.List<PayloadType> payloads);
+ public abstract Crypto? generate_local_crypto();
+ public abstract Crypto? pick_remote_crypto(Gee.List<Crypto> cryptos);
+ public abstract Crypto? pick_local_crypto(Crypto? remote);
public abstract Stream create_stream(Jingle.Content content);
public abstract void close_stream(Stream stream);
@@ -36,6 +39,7 @@ public abstract class Module : XmppStreamModule {
// Create audio content
Parameters audio_content_parameters = new Parameters(this, "audio", yield get_supported_payloads("audio"));
+ audio_content_parameters.local_crypto = generate_local_crypto();
Jingle.Transport? audio_transport = yield jingle_module.select_transport(stream, content_type.required_transport_type, content_type.required_components, receiver_full_jid, Set.empty());
if (audio_transport == null) {
throw new Jingle.Error.NO_SHARED_PROTOCOLS("No suitable audio transports");
@@ -52,6 +56,7 @@ public abstract class Module : XmppStreamModule {
if (video) {
// Create video content
Parameters video_content_parameters = new Parameters(this, "video", yield get_supported_payloads("video"));
+ video_content_parameters.local_crypto = generate_local_crypto();
Jingle.Transport? video_transport = yield stream.get_module(Jingle.Module.IDENTITY).select_transport(stream, content_type.required_transport_type, content_type.required_components, receiver_full_jid, Set.empty());
if (video_transport == null) {
throw new Jingle.Error.NO_SHARED_PROTOCOLS("No suitable video transports");
@@ -92,6 +97,7 @@ public abstract class Module : XmppStreamModule {
if (content == null) {
// Content for video does not yet exist -> create it
Parameters video_content_parameters = new Parameters(this, "video", yield get_supported_payloads("video"));
+ video_content_parameters.local_crypto = generate_local_crypto();
Jingle.Transport? video_transport = yield stream.get_module(Jingle.Module.IDENTITY).select_transport(stream, content_type.required_transport_type, content_type.required_components, receiver_full_jid, Set.empty());
if (video_transport == null) {
throw new Jingle.Error.NO_SHARED_PROTOCOLS("No suitable video transports");
@@ -148,26 +154,130 @@ public abstract class Module : XmppStreamModule {
}
public class Crypto {
- public string cryptoSuite { get; private set; }
- public string keyParams { get; private set; }
- public string? sessionParams { get; private set; }
- public string? tag { get; private set; }
+ public const string AES_CM_128_HMAC_SHA1_80 = "AES_CM_128_HMAC_SHA1_80";
+ public const string AES_CM_128_HMAC_SHA1_32 = "AES_CM_128_HMAC_SHA1_32";
+ public const string F8_128_HMAC_SHA1_80 = "F8_128_HMAC_SHA1_80";
+
+ public string crypto_suite { get; private set; }
+ public string key_params { get; private set; }
+ public string? session_params { get; private set; }
+ public string tag { get; private set; }
+
+ public uint8[] key_and_salt { owned get {
+ if (!key_params.has_prefix("inline:")) return null;
+ int endIndex = key_params.index_of("|");
+ if (endIndex < 0) endIndex = key_params.length;
+ string sub = key_params.substring(7, endIndex - 7);
+ return Base64.decode(sub);
+ }}
+
+ public string? lifetime { owned get {
+ if (!key_params.has_prefix("inline:")) return null;
+ int firstIndex = key_params.index_of("|");
+ if (firstIndex < 0) return null;
+ int endIndex = key_params.index_of("|", firstIndex + 1);
+ if (endIndex < 0) {
+ if (key_params.index_of(":", firstIndex) > 0) return null; // Is MKI
+ endIndex = key_params.length;
+ }
+ return key_params.substring(firstIndex + 1, endIndex);
+ }}
+
+ public int mki { get {
+ if (!key_params.has_prefix("inline:")) return -1;
+ int firstIndex = key_params.index_of("|");
+ if (firstIndex < 0) return -1;
+ int splitIndex = key_params.index_of(":", firstIndex);
+ if (splitIndex < 0) return -1;
+ int secondIndex = key_params.index_of("|", firstIndex + 1);
+ if (secondIndex < 0) {
+ return int.parse(key_params.substring(firstIndex + 1, splitIndex));
+ } else if (splitIndex > secondIndex) {
+ return int.parse(key_params.substring(secondIndex + 1, splitIndex));
+ }
+ return -1;
+ }}
+
+ public int mki_length { get {
+ if (!key_params.has_prefix("inline:")) return -1;
+ int firstIndex = key_params.index_of("|");
+ if (firstIndex < 0) return -1;
+ int splitIndex = key_params.index_of(":", firstIndex);
+ if (splitIndex < 0) return -1;
+ int secondIndex = key_params.index_of("|", firstIndex + 1);
+ if (secondIndex < 0 || splitIndex > secondIndex) {
+ return int.parse(key_params.substring(splitIndex + 1, key_params.length));
+ }
+ return -1;
+ }}
+
+ public bool is_valid { get {
+ switch(crypto_suite) {
+ case AES_CM_128_HMAC_SHA1_80:
+ case AES_CM_128_HMAC_SHA1_32:
+ case F8_128_HMAC_SHA1_80:
+ return key_and_salt.length == 30;
+ }
+ return false;
+ }}
+
+ public uint8[] key { owned get {
+ uint8[] key_and_salt = key_and_salt;
+ switch(crypto_suite) {
+ case AES_CM_128_HMAC_SHA1_80:
+ case AES_CM_128_HMAC_SHA1_32:
+ case F8_128_HMAC_SHA1_80:
+ if (key_and_salt.length >= 16) return key_and_salt[0:16];
+ break;
+ }
+ return null;
+ }}
+
+ public uint8[] salt { owned get {
+ uint8[] keyAndSalt = key_and_salt;
+ switch(crypto_suite) {
+ case AES_CM_128_HMAC_SHA1_80:
+ case AES_CM_128_HMAC_SHA1_32:
+ case F8_128_HMAC_SHA1_80:
+ if (keyAndSalt.length >= 30) return keyAndSalt[16:30];
+ break;
+ }
+ return null;
+ }}
+
+ public static Crypto create(string crypto_suite, uint8[] key_and_salt, string? session_params = null, string tag = "1") {
+ Crypto crypto = new Crypto();
+ crypto.crypto_suite = crypto_suite;
+ crypto.key_params = "inline:" + Base64.encode(key_and_salt);
+ crypto.session_params = session_params;
+ crypto.tag = tag;
+ return crypto;
+ }
+
+ public Crypto rekey(uint8[] key_and_salt) {
+ Crypto crypto = new Crypto();
+ crypto.crypto_suite = crypto_suite;
+ crypto.key_params = "inline:" + Base64.encode(key_and_salt);
+ crypto.session_params = session_params;
+ crypto.tag = tag;
+ return crypto;
+ }
public static Crypto parse(StanzaNode node) {
Crypto crypto = new Crypto();
- crypto.cryptoSuite = node.get_attribute("crypto-suite");
- crypto.keyParams = node.get_attribute("key-params");
- crypto.sessionParams = node.get_attribute("session-params");
+ crypto.crypto_suite = node.get_attribute("crypto-suite");
+ crypto.key_params = node.get_attribute("key-params");
+ crypto.session_params = node.get_attribute("session-params");
crypto.tag = node.get_attribute("tag");
return crypto;
}
public StanzaNode to_xml() {
StanzaNode node = new StanzaNode.build("crypto", NS_URI)
- .put_attribute("crypto-suite", cryptoSuite)
- .put_attribute("key-params", keyParams);
- if (sessionParams != null) node.put_attribute("session-params", sessionParams);
- if (tag != null) node.put_attribute("tag", tag);
+ .put_attribute("crypto-suite", crypto_suite)
+ .put_attribute("key-params", key_params)
+ .put_attribute("tag", tag);
+ if (session_params != null) node.put_attribute("session-params", session_params);
return node;
}
}
diff --git a/xmpp-vala/src/module/xep/0167_jingle_rtp/stream.vala b/xmpp-vala/src/module/xep/0167_jingle_rtp/stream.vala
index 62d85dec..2fc29291 100644
--- a/xmpp-vala/src/module/xep/0167_jingle_rtp/stream.vala
+++ b/xmpp-vala/src/module/xep/0167_jingle_rtp/stream.vala
@@ -17,6 +17,20 @@ public abstract class Xmpp.Xep.JingleRtp.Stream : Object {
}
return null;
}}
+ public JingleRtp.Crypto? local_crypto { get {
+ var content_params = content.content_params;
+ if (content_params is Parameters) {
+ return ((Parameters)content_params).local_crypto;
+ }
+ return null;
+ }}
+ public JingleRtp.Crypto? remote_crypto { get {
+ var content_params = content.content_params;
+ if (content_params is Parameters) {
+ return ((Parameters)content_params).remote_crypto;
+ }
+ return null;
+ }}
public bool sending { get {
return content.session.senders_include_us(content.senders);
}}