aboutsummaryrefslogtreecommitdiff
path: root/xmpp-vala/src/module/xep/0384_omemo
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/src/module/xep/0384_omemo
parent5d85b6cdb0165d863aadd25d9a73707b8f5cc83e (diff)
downloaddino-421f43dd8bd993eb88581e1b5011cc061ceb4fc8.tar.gz
dino-421f43dd8bd993eb88581e1b5011cc061ceb4fc8.zip
Add support for OMEMO call encryption
Diffstat (limited to 'xmpp-vala/src/module/xep/0384_omemo')
-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
2 files changed, 178 insertions, 0 deletions
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))";
+ }
+ }
+}
+