implemented support for for jingle encrypted transports (XEP-0396)

This commit is contained in:
Daniel Gultsch 2019-09-04 16:14:01 +02:00
parent ff4d127b6f
commit 7ec1b443ab
6 changed files with 45 additions and 11 deletions

View file

@ -101,8 +101,8 @@ public final class Config {
public static final boolean DISABLE_PROXY_LOOKUP = false; //useful to debug ibb 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 USE_DIRECT_JINGLE_CANDIDATES = true;
public static final boolean DISABLE_HTTP_UPLOAD = false; public static final boolean DISABLE_HTTP_UPLOAD = true;
public static final boolean EXTENDED_SM_LOGGING = false; // log stanza counts 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 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 public static final boolean RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE = true; //setting to true might increase power consumption

View file

@ -29,6 +29,8 @@ public abstract class AbstractGenerator {
Content.Version.FT_5.getNamespace(), Content.Version.FT_5.getNamespace(),
Namespace.JINGLE_TRANSPORTS_S5B, Namespace.JINGLE_TRANSPORTS_S5B,
Namespace.JINGLE_TRANSPORTS_IBB, Namespace.JINGLE_TRANSPORTS_IBB,
Namespace.JINGLE_ENCRYPTED_TRANSPORT,
Namespace.JINGLE_ENCRYPTED_TRANSPORT_OMEMO,
"http://jabber.org/protocol/muc", "http://jabber.org/protocol/muc",
"jabber:x:conference", "jabber:x:conference",
Namespace.OOB, Namespace.OOB,

View file

@ -30,4 +30,6 @@ public final class Namespace {
public static final String PING = "urn:xmpp:ping"; public static final String PING = "urn:xmpp:ping";
public static final String PUSH = "urn:xmpp:push:0"; public static final String PUSH = "urn:xmpp:push:0";
public static final String COMMANDS = "http://jabber.org/protocol/commands"; 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";
} }

View file

@ -43,6 +43,8 @@ import rocks.xmpp.addr.Jid;
public class JingleConnection implements Transferable { 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_INITIATED = 0;
private static final int JINGLE_STATUS_ACCEPTED = 1; private static final int JINGLE_STATUS_ACCEPTED = 1;
private static final int JINGLE_STATUS_FINISHED = 4; private static final int JINGLE_STATUS_FINISHED = 4;
@ -72,6 +74,7 @@ public class JingleConnection implements Transferable {
private String contentName; private String contentName;
private String contentCreator; private String contentCreator;
private Transport initialTransport; private Transport initialTransport;
private boolean remoteSupportsOmemoJet;
private int mProgress = 0; private int mProgress = 0;
@ -295,8 +298,10 @@ public class JingleConnection implements Transferable {
this.contentName = this.mJingleConnectionManager.nextRandomId(); this.contentName = this.mJingleConnectionManager.nextRandomId();
this.message = message; this.message = message;
this.account = message.getConversation().getAccount(); this.account = message.getConversation().getAccount();
upgradeNamespace(); final List<String> remoteFeatures = getRemoteFeatures();
this.initialTransport = getRemoteFeatures().contains(Namespace.JINGLE_TRANSPORTS_S5B) ? Transport.SOCKS : Transport.IBB; 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.message.setTransferable(this);
this.mStatus = Transferable.STATUS_UPLOADING; this.mStatus = Transferable.STATUS_UPLOADING;
this.initiator = this.account.getJid(); this.initiator = this.account.getJid();
@ -356,11 +361,10 @@ public class JingleConnection implements Transferable {
} }
} }
private void upgradeNamespace() { private void upgradeNamespace(List<String> remoteFeatures) {
List<String> features = getRemoteFeatures(); if (remoteFeatures.contains(Content.Version.FT_5.getNamespace())) {
if (features.contains(Content.Version.FT_5.getNamespace())) {
this.ftVersion = Content.Version.FT_5; 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; this.ftVersion = Content.Version.FT_4;
} }
} }
@ -430,6 +434,13 @@ public class JingleConnection implements Transferable {
if (fileOffer != null) { if (fileOffer != null) {
Element encrypted = fileOffer.findChild("encrypted", AxolotlService.PEP_PREFIX); 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) { if (encrypted != null) {
this.mXmppAxolotlMessage = XmppAxolotlMessage.fromElement(encrypted, packet.getFrom().asBareJid()); this.mXmppAxolotlMessage = XmppAxolotlMessage.fromElement(encrypted, packet.getFrom().asBareJid());
} }
@ -520,7 +531,18 @@ public class JingleConnection implements Transferable {
this.file.setKey(mXmppAxolotlMessage.getInnerKey()); this.file.setKey(mXmppAxolotlMessage.getInnerKey());
this.file.setIv(mXmppAxolotlMessage.getIV()); this.file.setIv(mXmppAxolotlMessage.getIV());
this.file.setExpectedSize(file.getSize() + 16); 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 { } else {
this.file.setExpectedSize(file.getSize()); this.file.setExpectedSize(file.getSize());
content.setFileOffer(this.file, false, this.ftVersion); content.setFileOffer(this.file, false, this.ftVersion);
@ -754,6 +776,8 @@ public class JingleConnection implements Transferable {
this.sendFallbackToIbb(); this.sendFallbackToIbb();
} }
} else { } else {
final JingleCandidate candidate = connection.getCandidate();
Log.d(Config.LOGTAG, account.getJid().asBareJid()+": elected candidate "+candidate.getHost()+":"+candidate.getPort());
this.mJingleStatus = JINGLE_STATUS_TRANSMITTING; this.mJingleStatus = JINGLE_STATUS_TRANSMITTING;
if (connection.needsActivation()) { if (connection.needsActivation()) {
if (connection.getCandidate().isOurs()) { if (connection.getCandidate().isOurs()) {

View file

@ -16,6 +16,7 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.services.AbstractConnectionManager; import eu.siacs.conversations.services.AbstractConnectionManager;
@ -134,11 +135,14 @@ public class JingleSocks5Transport extends JingleTransport {
outputStream.write(response.array()); outputStream.write(response.array());
outputStream.flush(); outputStream.flush();
if (success) { if (success) {
Log.d(Config.LOGTAG,connection.getAccount().getJid().asBareJid()+": successfully processed connection to candidate "+candidate.getHost()+":"+candidate.getPort());
this.socket = socket; this.socket = socket;
this.inputStream = inputStream; this.inputStream = inputStream;
this.outputStream = outputStream; this.outputStream = outputStream;
this.isEstablished = true; this.isEstablished = true;
FileBackend.close(serverSocket); FileBackend.close(serverSocket);
} else {
this.socket.close();
} }
} else { } else {
socket.close(); socket.close();
@ -174,6 +178,7 @@ public class JingleSocks5Transport extends JingleTransport {
new Thread(() -> { new Thread(() -> {
InputStream fileInputStream = null; InputStream fileInputStream = null;
final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_send_" + connection.getSessionId()); final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_send_" + connection.getSessionId());
long transmitted = 0;
try { try {
wakeLock.acquire(); wakeLock.acquire();
MessageDigest digest = MessageDigest.getInstance("SHA-1"); MessageDigest digest = MessageDigest.getInstance("SHA-1");
@ -186,7 +191,6 @@ public class JingleSocks5Transport extends JingleTransport {
} }
final InputStream innerInputStream = AbstractConnectionManager.upgrade(file, fileInputStream); final InputStream innerInputStream = AbstractConnectionManager.upgrade(file, fileInputStream);
long size = file.getExpectedSize(); long size = file.getExpectedSize();
long transmitted = 0;
int count; int count;
byte[] buffer = new byte[8192]; byte[] buffer = new byte[8192];
while ((count = innerInputStream.read(buffer)) > 0) { while ((count = innerInputStream.read(buffer)) > 0) {
@ -201,7 +205,8 @@ public class JingleSocks5Transport extends JingleTransport {
callback.onFileTransmitted(file); callback.onFileTransmitted(file);
} }
} catch (Exception e) { } 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(); callback.onFileTransferAborted();
} finally { } finally {
FileBackend.close(fileInputStream); FileBackend.close(fileInputStream);

View file

@ -873,4 +873,5 @@
<string name="not_a_backup_file">A kiválasztott fájl nem a Conversations biztonsági mentése</string> <string name="not_a_backup_file">A kiválasztott fájl nem a Conversations biztonsági mentése</string>
<string name="account_already_setup">Ez a fiók már be lett állítva</string> <string name="account_already_setup">Ez a fiók már be lett állítva</string>
<string name="please_enter_password">Kérem, adja meg a fiókhoz tartozó jelszót</string> <string name="please_enter_password">Kérem, adja meg a fiókhoz tartozó jelszót</string>
<string name="unable_to_perform_this_action">Nem sikerült ezt a cselekvést elvégezni</string>
</resources> </resources>