From 7ec1b443abe48f89e4e98bffa06bd6d814ba780e Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 4 Sep 2019 16:14:01 +0200 Subject: [PATCH] implemented support for for jingle encrypted transports (XEP-0396) --- .../java/eu/siacs/conversations/Config.java | 4 +- .../generator/AbstractGenerator.java | 2 + .../eu/siacs/conversations/xml/Namespace.java | 2 + .../xmpp/jingle/JingleConnection.java | 38 +++++++++++++++---- .../xmpp/jingle/JingleSocks5Transport.java | 9 ++++- src/main/res/values-hu/strings.xml | 1 + 6 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index a5b3ab720..75104500e 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -101,8 +101,8 @@ public final class Config { public static final boolean DISABLE_PROXY_LOOKUP = false; //useful to debug ibb - public static final boolean USE_DIRECT_JINGLE_CANDIDATES = false; - public static final boolean DISABLE_HTTP_UPLOAD = false; + public static final boolean USE_DIRECT_JINGLE_CANDIDATES = true; + public static final boolean DISABLE_HTTP_UPLOAD = true; public static final boolean EXTENDED_SM_LOGGING = false; // log stanza counts public static final boolean BACKGROUND_STANZA_LOGGING = false; //log all stanzas that were received while the app is in background public static final boolean RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE = true; //setting to true might increase power consumption diff --git a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java index 1270c1019..5f93400e2 100644 --- a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java @@ -29,6 +29,8 @@ public abstract class AbstractGenerator { Content.Version.FT_5.getNamespace(), Namespace.JINGLE_TRANSPORTS_S5B, Namespace.JINGLE_TRANSPORTS_IBB, + Namespace.JINGLE_ENCRYPTED_TRANSPORT, + Namespace.JINGLE_ENCRYPTED_TRANSPORT_OMEMO, "http://jabber.org/protocol/muc", "jabber:x:conference", Namespace.OOB, diff --git a/src/main/java/eu/siacs/conversations/xml/Namespace.java b/src/main/java/eu/siacs/conversations/xml/Namespace.java index dbab039d7..55d54853e 100644 --- a/src/main/java/eu/siacs/conversations/xml/Namespace.java +++ b/src/main/java/eu/siacs/conversations/xml/Namespace.java @@ -30,4 +30,6 @@ public final class Namespace { public static final String PING = "urn:xmpp:ping"; public static final String PUSH = "urn:xmpp:push:0"; public static final String COMMANDS = "http://jabber.org/protocol/commands"; + public static final String JINGLE_ENCRYPTED_TRANSPORT = "urn:xmpp:jingle:jet:0"; + public static final String JINGLE_ENCRYPTED_TRANSPORT_OMEMO = "urn:xmpp:jingle:jet-omemo:0"; } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java index b112799d5..828d4a021 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -43,6 +43,8 @@ import rocks.xmpp.addr.Jid; public class JingleConnection implements Transferable { + private static final String JET_OMEMO_CIPHER = "urn:xmpp:ciphers:aes-128-gcm-nopadding"; + private static final int JINGLE_STATUS_INITIATED = 0; private static final int JINGLE_STATUS_ACCEPTED = 1; private static final int JINGLE_STATUS_FINISHED = 4; @@ -72,6 +74,7 @@ public class JingleConnection implements Transferable { private String contentName; private String contentCreator; private Transport initialTransport; + private boolean remoteSupportsOmemoJet; private int mProgress = 0; @@ -295,8 +298,10 @@ public class JingleConnection implements Transferable { this.contentName = this.mJingleConnectionManager.nextRandomId(); this.message = message; this.account = message.getConversation().getAccount(); - upgradeNamespace(); - this.initialTransport = getRemoteFeatures().contains(Namespace.JINGLE_TRANSPORTS_S5B) ? Transport.SOCKS : Transport.IBB; + final List remoteFeatures = getRemoteFeatures(); + upgradeNamespace(remoteFeatures); + this.initialTransport = remoteFeatures.contains(Namespace.JINGLE_TRANSPORTS_S5B) ? Transport.SOCKS : Transport.IBB; + this.remoteSupportsOmemoJet = remoteFeatures.contains(Namespace.JINGLE_ENCRYPTED_TRANSPORT_OMEMO); this.message.setTransferable(this); this.mStatus = Transferable.STATUS_UPLOADING; this.initiator = this.account.getJid(); @@ -356,11 +361,10 @@ public class JingleConnection implements Transferable { } } - private void upgradeNamespace() { - List features = getRemoteFeatures(); - if (features.contains(Content.Version.FT_5.getNamespace())) { + private void upgradeNamespace(List remoteFeatures) { + if (remoteFeatures.contains(Content.Version.FT_5.getNamespace())) { this.ftVersion = Content.Version.FT_5; - } else if (features.contains(Content.Version.FT_4.getNamespace())) { + } else if (remoteFeatures.contains(Content.Version.FT_4.getNamespace())) { this.ftVersion = Content.Version.FT_4; } } @@ -430,6 +434,13 @@ public class JingleConnection implements Transferable { if (fileOffer != null) { Element encrypted = fileOffer.findChild("encrypted", AxolotlService.PEP_PREFIX); + if (encrypted == null) { + final Element security = content.findChild("security", Namespace.JINGLE_ENCRYPTED_TRANSPORT); + if (security != null && AxolotlService.PEP_PREFIX.equals(security.getAttribute("type"))) { + Log.d(Config.LOGTAG, account.getJid().asBareJid()+": received jingle file offer with JET"); + encrypted = security.findChild("encrypted", AxolotlService.PEP_PREFIX); + } + } if (encrypted != null) { this.mXmppAxolotlMessage = XmppAxolotlMessage.fromElement(encrypted, packet.getFrom().asBareJid()); } @@ -520,7 +531,18 @@ public class JingleConnection implements Transferable { this.file.setKey(mXmppAxolotlMessage.getInnerKey()); this.file.setIv(mXmppAxolotlMessage.getIV()); this.file.setExpectedSize(file.getSize() + 16); - content.setFileOffer(this.file, false, this.ftVersion).addChild(mXmppAxolotlMessage.toElement()); + final Element file = content.setFileOffer(this.file, false, this.ftVersion); + if (remoteSupportsOmemoJet) { + Log.d(Config.LOGTAG, account.getJid().asBareJid()+": remote announced support for JET"); + final Element security = new Element("security", Namespace.JINGLE_ENCRYPTED_TRANSPORT); + security.setAttribute("name", this.contentName); + security.setAttribute("cipher", JET_OMEMO_CIPHER); + security.setAttribute("type", AxolotlService.PEP_PREFIX); + security.addChild(mXmppAxolotlMessage.toElement()); + content.addChild(security); + } else { + file.addChild(mXmppAxolotlMessage.toElement()); + } } else { this.file.setExpectedSize(file.getSize()); content.setFileOffer(this.file, false, this.ftVersion); @@ -754,6 +776,8 @@ public class JingleConnection implements Transferable { this.sendFallbackToIbb(); } } else { + final JingleCandidate candidate = connection.getCandidate(); + Log.d(Config.LOGTAG, account.getJid().asBareJid()+": elected candidate "+candidate.getHost()+":"+candidate.getPort()); this.mJingleStatus = JINGLE_STATUS_TRANSMITTING; if (connection.needsActivation()) { if (connection.getCandidate().isOurs()) { diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java index f0ee5c12e..41aa75ad9 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java @@ -16,6 +16,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import eu.siacs.conversations.Config; +import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.services.AbstractConnectionManager; @@ -134,11 +135,14 @@ public class JingleSocks5Transport extends JingleTransport { outputStream.write(response.array()); outputStream.flush(); if (success) { + Log.d(Config.LOGTAG,connection.getAccount().getJid().asBareJid()+": successfully processed connection to candidate "+candidate.getHost()+":"+candidate.getPort()); this.socket = socket; this.inputStream = inputStream; this.outputStream = outputStream; this.isEstablished = true; FileBackend.close(serverSocket); + } else { + this.socket.close(); } } else { socket.close(); @@ -174,6 +178,7 @@ public class JingleSocks5Transport extends JingleTransport { new Thread(() -> { InputStream fileInputStream = null; final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_send_" + connection.getSessionId()); + long transmitted = 0; try { wakeLock.acquire(); MessageDigest digest = MessageDigest.getInstance("SHA-1"); @@ -186,7 +191,6 @@ public class JingleSocks5Transport extends JingleTransport { } final InputStream innerInputStream = AbstractConnectionManager.upgrade(file, fileInputStream); long size = file.getExpectedSize(); - long transmitted = 0; int count; byte[] buffer = new byte[8192]; while ((count = innerInputStream.read(buffer)) > 0) { @@ -201,7 +205,8 @@ public class JingleSocks5Transport extends JingleTransport { callback.onFileTransmitted(file); } } catch (Exception e) { - Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": " + e.getMessage()); + final Account account = connection.getAccount(); + Log.d(Config.LOGTAG, account.getJid().asBareJid()+": failed sending file after "+transmitted+"/"+file.getExpectedSize()+" ("+ socket.getInetAddress()+":"+socket.getPort()+")", e); callback.onFileTransferAborted(); } finally { FileBackend.close(fileInputStream); diff --git a/src/main/res/values-hu/strings.xml b/src/main/res/values-hu/strings.xml index 8ad9d1a45..d880a6707 100644 --- a/src/main/res/values-hu/strings.xml +++ b/src/main/res/values-hu/strings.xml @@ -873,4 +873,5 @@ A kiválasztott fájl nem a Conversations biztonsági mentése Ez a fiók már be lett állítva Kérem, adja meg a fiókhoz tartozó jelszót + Nem sikerült ezt a cselekvést elvégezni