aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Gultsch <daniel@gultsch.de>2015-08-01 01:19:16 +0200
committerDaniel Gultsch <daniel@gultsch.de>2015-08-01 01:19:16 +0200
commit60cd307f73d5f31f25ba84541fbe1cce4aae2bc2 (patch)
tree9d5e82266d6e3e27472e0305df3f47e230b4da68
parent6059b964569fb406bbc86a0ccb19e76851fba2b6 (diff)
enable axolotl encryption for jingle supported file transfers
-rw-r--r--src/main/java/eu/siacs/conversations/entities/DownloadableFile.java34
-rw-r--r--src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java25
-rw-r--r--src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java24
-rw-r--r--src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java97
-rw-r--r--src/main/java/eu/siacs/conversations/services/XmppConnectionService.java2
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java97
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java10
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java6
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleTransport.java61
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java5
10 files changed, 223 insertions, 138 deletions
diff --git a/src/main/java/eu/siacs/conversations/entities/DownloadableFile.java b/src/main/java/eu/siacs/conversations/entities/DownloadableFile.java
index 452bf815..d35a4b01 100644
--- a/src/main/java/eu/siacs/conversations/entities/DownloadableFile.java
+++ b/src/main/java/eu/siacs/conversations/entities/DownloadableFile.java
@@ -24,15 +24,7 @@ public class DownloadableFile extends File {
}
public long getExpectedSize() {
- if (this.aeskey != null) {
- if (this.expectedSize == 0) {
- return 0;
- } else {
- return (this.expectedSize / 16 + 1) * 16;
- }
- } else {
- return this.expectedSize;
- }
+ return this.expectedSize;
}
public String getMimeType() {
@@ -58,25 +50,33 @@ public class DownloadableFile extends File {
this.sha1sum = sum;
}
- public void setKey(byte[] key) {
- if (key.length == 48) {
+ public void setKeyAndIv(byte[] keyIvCombo) {
+ if (keyIvCombo.length == 48) {
byte[] secretKey = new byte[32];
byte[] iv = new byte[16];
- System.arraycopy(key, 0, iv, 0, 16);
- System.arraycopy(key, 16, secretKey, 0, 32);
+ System.arraycopy(keyIvCombo, 0, iv, 0, 16);
+ System.arraycopy(keyIvCombo, 16, secretKey, 0, 32);
this.aeskey = secretKey;
this.iv = iv;
- } else if (key.length >= 32) {
+ } else if (keyIvCombo.length >= 32) {
byte[] secretKey = new byte[32];
- System.arraycopy(key, 0, secretKey, 0, 32);
+ System.arraycopy(keyIvCombo, 0, secretKey, 0, 32);
this.aeskey = secretKey;
- } else if (key.length >= 16) {
+ } else if (keyIvCombo.length >= 16) {
byte[] secretKey = new byte[16];
- System.arraycopy(key, 0, secretKey, 0, 16);
+ System.arraycopy(keyIvCombo, 0, secretKey, 0, 16);
this.aeskey = secretKey;
}
}
+ public void setKey(byte[] key) {
+ this.aeskey = key;
+ }
+
+ public void setIv(byte[] iv) {
+ this.iv = iv;
+ }
+
public byte[] getKey() {
return this.aeskey;
}
diff --git a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java
index 51479836..79e4654b 100644
--- a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java
+++ b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java
@@ -4,15 +4,7 @@ import android.content.Intent;
import android.net.Uri;
import android.util.Log;
-import org.bouncycastle.crypto.engines.AESEngine;
-import org.bouncycastle.crypto.io.CipherOutputStream;
-import org.bouncycastle.crypto.modes.AEADBlockCipher;
-import org.bouncycastle.crypto.modes.GCMBlockCipher;
-import org.bouncycastle.crypto.params.AEADParameters;
-import org.bouncycastle.crypto.params.KeyParameter;
-
import java.io.BufferedInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
@@ -28,6 +20,8 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.Transferable;
+import eu.siacs.conversations.persistance.FileBackend;
+import eu.siacs.conversations.services.AbstractConnectionManager;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.CryptoHelper;
@@ -90,7 +84,7 @@ public class HttpDownloadConnection implements Transferable {
this.file = mXmppConnectionService.getFileBackend().getFile(message, false);
String reference = mUrl.getRef();
if (reference != null && reference.length() == 96) {
- this.file.setKey(CryptoHelper.hexToBytes(reference));
+ this.file.setKeyAndIv(CryptoHelper.hexToBytes(reference));
}
if ((this.message.getEncryption() == Message.ENCRYPTION_OTR
@@ -194,6 +188,8 @@ public class HttpDownloadConnection implements Transferable {
private boolean interactive = false;
+ private OutputStream os;
+
public FileDownloader(boolean interactive) {
this.interactive = interactive;
}
@@ -206,8 +202,10 @@ public class HttpDownloadConnection implements Transferable {
updateImageBounds();
finish();
} catch (SSLHandshakeException e) {
+ FileBackend.close(os);
changeStatus(STATUS_OFFER);
} catch (IOException e) {
+ FileBackend.close(os);
mXmppConnectionService.showErrorToastInUi(R.string.file_not_found_on_remote_host);
cancel();
}
@@ -222,14 +220,7 @@ public class HttpDownloadConnection implements Transferable {
BufferedInputStream is = new BufferedInputStream(connection.getInputStream());
file.getParentFile().mkdirs();
file.createNewFile();
- OutputStream os;
- if (file.getKey() != null) {
- AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
- cipher.init(false, new AEADParameters(new KeyParameter(file.getKey()), 128, file.getIv()));
- os = new CipherOutputStream(new FileOutputStream(file), cipher);
- } else {
- os = new FileOutputStream(file);
- }
+ os = AbstractConnectionManager.createOutputStream(file,true);
long transmitted = 0;
long expected = file.getExpectedSize();
int count = -1;
diff --git a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java
index 70dcc642..b21d0d41 100644
--- a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java
+++ b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java
@@ -4,15 +4,8 @@ import android.app.PendingIntent;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;
+import android.util.Pair;
-import org.bouncycastle.crypto.engines.AESEngine;
-import org.bouncycastle.crypto.io.CipherInputStream;
-import org.bouncycastle.crypto.modes.AEADBlockCipher;
-import org.bouncycastle.crypto.modes.GCMBlockCipher;
-import org.bouncycastle.crypto.params.AEADParameters;
-import org.bouncycastle.crypto.params.KeyParameter;
-
-import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -28,6 +21,7 @@ import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.Transferable;
import eu.siacs.conversations.persistance.FileBackend;
+import eu.siacs.conversations.services.AbstractConnectionManager;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.UiCallback;
import eu.siacs.conversations.utils.CryptoHelper;
@@ -105,7 +99,7 @@ public class HttpUploadConnection implements Transferable {
|| message.getEncryption() == Message.ENCRYPTION_OTR) {
this.key = new byte[48];
mXmppConnectionService.getRNG().nextBytes(this.key);
- this.file.setKey(this.key);
+ this.file.setKeyAndIv(this.key);
}
Jid host = account.getXmppConnection().findDiscoItemByFeature(Xmlns.HTTP_UPLOAD);
@@ -152,15 +146,9 @@ public class HttpUploadConnection implements Transferable {
if (connection instanceof HttpsURLConnection) {
mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, true);
}
- if (file.getKey() != null) {
- AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
- cipher.init(true, new AEADParameters(new KeyParameter(file.getKey()), 128, file.getIv()));
- expected = cipher.getOutputSize((int) file.getSize());
- is = new CipherInputStream(new FileInputStream(file), cipher);
- } else {
- expected = (int) file.getSize();
- is = new FileInputStream(file);
- }
+ Pair<InputStream,Integer> pair = AbstractConnectionManager.createInputStream(file,true);
+ is = pair.first;
+ expected = pair.second;
connection.setRequestMethod("PUT");
connection.setFixedLengthStreamingMode(expected);
connection.setDoOutput(true);
diff --git a/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java b/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java
index 676a09c9..b7e7c8d3 100644
--- a/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java
+++ b/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java
@@ -1,5 +1,33 @@
package eu.siacs.conversations.services;
+import android.util.Log;
+import android.util.Pair;
+
+import org.bouncycastle.crypto.engines.AESEngine;
+import org.bouncycastle.crypto.modes.AEADBlockCipher;
+import org.bouncycastle.crypto.modes.GCMBlockCipher;
+import org.bouncycastle.crypto.params.AEADParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.entities.DownloadableFile;
+
public class AbstractConnectionManager {
protected XmppConnectionService mXmppConnectionService;
@@ -20,4 +48,73 @@ public class AbstractConnectionManager {
return 524288;
}
}
+
+ public static Pair<InputStream,Integer> createInputStream(DownloadableFile file, boolean gcm) {
+ FileInputStream is;
+ int size;
+ try {
+ is = new FileInputStream(file);
+ size = (int) file.getSize();
+ if (file.getKey() == null) {
+ return new Pair<InputStream,Integer>(is,size);
+ }
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ try {
+ if (gcm) {
+ AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
+ cipher.init(true, new AEADParameters(new KeyParameter(file.getKey()), 128, file.getIv()));
+ InputStream cis = new org.bouncycastle.crypto.io.CipherInputStream(is, cipher);
+ return new Pair<>(cis, cipher.getOutputSize(size));
+ } else {
+ IvParameterSpec ips = new IvParameterSpec(file.getIv());
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(file.getKey(), "AES"), ips);
+ Log.d(Config.LOGTAG, "opening encrypted input stream");
+ return new Pair<InputStream,Integer>(new CipherInputStream(is, cipher),(size / 16 + 1) * 16);
+ }
+ } catch (InvalidKeyException e) {
+ return null;
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ } catch (NoSuchPaddingException e) {
+ return null;
+ } catch (InvalidAlgorithmParameterException e) {
+ return null;
+ }
+ }
+
+ public static OutputStream createOutputStream(DownloadableFile file, boolean gcm) {
+ FileOutputStream os;
+ try {
+ os = new FileOutputStream(file);
+ if (file.getKey() == null) {
+ return os;
+ }
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ try {
+ if (gcm) {
+ AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
+ cipher.init(false, new AEADParameters(new KeyParameter(file.getKey()), 128, file.getIv()));
+ return new org.bouncycastle.crypto.io.CipherOutputStream(os, cipher);
+ } else {
+ IvParameterSpec ips = new IvParameterSpec(file.getIv());
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(file.getKey(), "AES"), ips);
+ Log.d(Config.LOGTAG, "opening encrypted output stream");
+ return new CipherOutputStream(os, cipher);
+ }
+ } catch (InvalidKeyException e) {
+ return null;
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ } catch (NoSuchPaddingException e) {
+ return null;
+ } catch (InvalidAlgorithmParameterException e) {
+ return null;
+ }
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index 61453ab0..ffe587d6 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -755,6 +755,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
break;
case Message.ENCRYPTION_AXOLOTL:
+ message.setAxolotlFingerprint(account.getAxolotlService().getOwnPublicKey().getFingerprint().replaceAll("\\s", ""));
if (message.needsUploading()) {
if (account.httpUploadAvailable() || message.fixCounterpart()) {
this.sendFileMessage(message,delay);
@@ -765,7 +766,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
XmppAxolotlMessage axolotlMessage = account.getAxolotlService().fetchAxolotlMessageFromCache(message);
if (axolotlMessage == null) {
account.getAxolotlService().preparePayloadMessage(message, delay);
- message.setAxolotlFingerprint(account.getAxolotlService().getOwnPublicKey().getFingerprint().replaceAll("\\s", ""));
} else {
packet = mMessageGenerator.generateAxolotlChat(message, axolotlMessage);
}
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 e15fc522..e9ca66b7 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
@@ -2,9 +2,11 @@ package eu.siacs.conversations.xmpp.jingle;
import android.content.Intent;
import android.net.Uri;
-import android.os.SystemClock;
import android.util.Log;
+import android.util.Pair;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
@@ -14,13 +16,19 @@ import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import eu.siacs.conversations.Config;
+import eu.siacs.conversations.crypto.axolotl.AxolotlService;
+import eu.siacs.conversations.crypto.axolotl.OnMessageCreatedCallback;
+import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.Transferable;
import eu.siacs.conversations.entities.TransferablePlaceholder;
+import eu.siacs.conversations.persistance.FileBackend;
+import eu.siacs.conversations.services.AbstractConnectionManager;
import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.utils.Xmlns;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
import eu.siacs.conversations.xmpp.jid.Jid;
@@ -66,8 +74,13 @@ public class JingleConnection implements Transferable {
private boolean acceptedAutomatically = false;
+ private XmppAxolotlMessage mXmppAxolotlMessage;
+
private JingleTransport transport = null;
+ private OutputStream mFileOutputStream;
+ private InputStream mFileInputStream;
+
private OnIqPacketReceived responseListener = new OnIqPacketReceived() {
@Override
@@ -113,6 +126,14 @@ public class JingleConnection implements Transferable {
}
};
+ public InputStream getFileInputStream() {
+ return this.mFileInputStream;
+ }
+
+ public OutputStream getFileOutputStream() {
+ return this.mFileOutputStream;
+ }
+
private OnProxyActivated onProxyActivated = new OnProxyActivated() {
@Override
@@ -194,7 +215,22 @@ public class JingleConnection implements Transferable {
mXmppConnectionService.sendIqPacket(account,response,null);
}
- public void init(Message message) {
+ public void init(final Message message) {
+ if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
+ Conversation conversation = message.getConversation();
+ conversation.getAccount().getAxolotlService().prepareKeyTransportMessage(conversation.getContact(), new OnMessageCreatedCallback() {
+ @Override
+ public void run(XmppAxolotlMessage xmppAxolotlMessage) {
+ init(message, xmppAxolotlMessage);
+ }
+ });
+ } else {
+ init(message, null);
+ }
+ }
+
+ private void init(Message message, XmppAxolotlMessage xmppAxolotlMessage) {
+ this.mXmppAxolotlMessage = xmppAxolotlMessage;
this.contentCreator = "initiator";
this.contentName = this.mJingleConnectionManager.nextRandomId();
this.message = message;
@@ -238,8 +274,7 @@ public class JingleConnection implements Transferable {
});
mergeCandidate(candidate);
} else {
- Log.d(Config.LOGTAG,
- "no primary candidate of our own was found");
+ Log.d(Config.LOGTAG,"no primary candidate of our own was found");
sendInitRequest();
}
}
@@ -267,13 +302,16 @@ public class JingleConnection implements Transferable {
this.contentCreator = content.getAttribute("creator");
this.contentName = content.getAttribute("name");
this.transportId = content.getTransportId();
- this.mergeCandidates(JingleCandidate.parse(content.socks5transport()
- .getChildren()));
+ this.mergeCandidates(JingleCandidate.parse(content.socks5transport().getChildren()));
this.fileOffer = packet.getJingleContent().getFileOffer();
mXmppConnectionService.sendIqPacket(account,packet.generateResponse(IqPacket.TYPE.RESULT),null);
if (fileOffer != null) {
+ Element encrypted = fileOffer.findChild("encrypted", AxolotlService.PEP_PREFIX);
+ if (encrypted != null) {
+ this.mXmppAxolotlMessage = XmppAxolotlMessage.fromElement(encrypted, packet.getFrom().toBareJid());
+ }
Element fileSize = fileOffer.findChild("size");
Element fileNameElement = fileOffer.findChild("name");
if (fileNameElement != null) {
@@ -319,10 +357,8 @@ public class JingleConnection implements Transferable {
message.setBody(Long.toString(size));
conversation.add(message);
mXmppConnectionService.updateConversationUi();
- if (size < this.mJingleConnectionManager
- .getAutoAcceptFileSize()) {
- Log.d(Config.LOGTAG, "auto accepting file from "
- + packet.getFrom());
+ if (size < this.mJingleConnectionManager.getAutoAcceptFileSize()) {
+ Log.d(Config.LOGTAG, "auto accepting file from "+ packet.getFrom());
this.acceptedAutomatically = true;
this.sendAccept();
} else {
@@ -333,22 +369,32 @@ public class JingleConnection implements Transferable {
+ " allowed size:"
+ this.mJingleConnectionManager
.getAutoAcceptFileSize());
- this.mXmppConnectionService.getNotificationService()
- .push(message);
+ this.mXmppConnectionService.getNotificationService().push(message);
}
- this.file = this.mXmppConnectionService.getFileBackend()
- .getFile(message, false);
- if (message.getEncryption() == Message.ENCRYPTION_OTR) {
+ this.file = this.mXmppConnectionService.getFileBackend().getFile(message, false);
+ if (mXmppAxolotlMessage != null) {
+ XmppAxolotlMessage.XmppAxolotlKeyTransportMessage transportMessage = account.getAxolotlService().processReceivingKeyTransportMessage(mXmppAxolotlMessage);
+ if (transportMessage != null) {
+ message.setEncryption(Message.ENCRYPTION_AXOLOTL);
+ this.file.setKey(transportMessage.getKey());
+ this.file.setIv(transportMessage.getIv());
+ message.setAxolotlFingerprint(transportMessage.getFingerprint());
+ } else {
+ Log.d(Config.LOGTAG,"could not process KeyTransportMessage");
+ }
+ } else if (message.getEncryption() == Message.ENCRYPTION_OTR) {
byte[] key = conversation.getSymmetricKey();
if (key == null) {
this.sendCancel();
this.fail();
return;
} else {
- this.file.setKey(key);
+ this.file.setKeyAndIv(key);
}
}
+ this.mFileOutputStream = AbstractConnectionManager.createOutputStream(this.file,message.getEncryption() == Message.ENCRYPTION_AXOLOTL);
this.file.setExpectedSize(size);
+ Log.d(Config.LOGTAG, "receiving file: expecting size of " + this.file.getExpectedSize());
} else {
this.sendCancel();
this.fail();
@@ -364,19 +410,30 @@ public class JingleConnection implements Transferable {
Content content = new Content(this.contentCreator, this.contentName);
if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) {
content.setTransportId(this.transportId);
- this.file = this.mXmppConnectionService.getFileBackend().getFile(
- message, false);
+ this.file = this.mXmppConnectionService.getFileBackend().getFile(message, false);
+ Pair<InputStream,Integer> pair;
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
Conversation conversation = this.message.getConversation();
if (!this.mXmppConnectionService.renewSymmetricKey(conversation)) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not set symmetric key");
cancel();
}
+ this.file.setKeyAndIv(conversation.getSymmetricKey());
+ pair = AbstractConnectionManager.createInputStream(this.file,false);
+ this.file.setExpectedSize(pair.second);
content.setFileOffer(this.file, true);
- this.file.setKey(conversation.getSymmetricKey());
+ } else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
+ this.file.setKey(mXmppAxolotlMessage.getInnerKey());
+ this.file.setIv(mXmppAxolotlMessage.getIV());
+ pair = AbstractConnectionManager.createInputStream(this.file,true);
+ this.file.setExpectedSize(pair.second);
+ content.setFileOffer(this.file, false).addChild(mXmppAxolotlMessage.toElement());
} else {
+ pair = AbstractConnectionManager.createInputStream(this.file,false);
+ this.file.setExpectedSize(pair.second);
content.setFileOffer(this.file, false);
}
+ this.mFileInputStream = pair.first;
this.transportId = this.mJingleConnectionManager.nextRandomId();
content.setTransportId(this.transportId);
content.socks5transport().setChildren(getCandidatesAsElements());
@@ -748,6 +805,8 @@ public class JingleConnection implements Transferable {
if (this.transport != null && this.transport instanceof JingleInbandTransport) {
this.transport.disconnect();
}
+ FileBackend.close(mFileInputStream);
+ FileBackend.close(mFileOutputStream);
if (this.message != null) {
if (this.responder.equals(account.getJid())) {
this.message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_FAILED));
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java
index 11055b8f..ab7ab73b 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java
@@ -93,7 +93,7 @@ public class JingleInbandTransport extends JingleTransport {
digest.reset();
file.getParentFile().mkdirs();
file.createNewFile();
- this.fileOutputStream = createOutputStream(file);
+ this.fileOutputStream = connection.getFileOutputStream();
if (this.fileOutputStream == null) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not create output stream");
callback.onFileTransferAborted();
@@ -112,15 +112,11 @@ public class JingleInbandTransport extends JingleTransport {
this.onFileTransmissionStatusChanged = callback;
this.file = file;
try {
- if (this.file.getKey() != null) {
- this.remainingSize = (this.file.getSize() / 16 + 1) * 16;
- } else {
- this.remainingSize = this.file.getSize();
- }
+ this.remainingSize = this.file.getExpectedSize();
this.fileSize = this.remainingSize;
this.digest = MessageDigest.getInstance("SHA-1");
this.digest.reset();
- fileInputStream = createInputStream(this.file);
+ fileInputStream = connection.getFileInputStream();
if (fileInputStream == null) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could no create input stream");
callback.onFileTransferAborted();
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 55039070..7545dd64 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
@@ -106,13 +106,13 @@ public class JingleSocks5Transport extends JingleTransport {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.reset();
- fileInputStream = createInputStream(file); //file.createInputStream();
+ fileInputStream = connection.getFileInputStream();
if (fileInputStream == null) {
Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create input stream");
callback.onFileTransferAborted();
return;
}
- long size = file.getSize();
+ long size = file.getExpectedSize();
long transmitted = 0;
int count;
byte[] buffer = new byte[8192];
@@ -157,7 +157,7 @@ public class JingleSocks5Transport extends JingleTransport {
socket.setSoTimeout(30000);
file.getParentFile().mkdirs();
file.createNewFile();
- fileOutputStream = createOutputStream(file);
+ fileOutputStream = connection.getFileOutputStream();
if (fileOutputStream == null) {
callback.onFileTransferAborted();
Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create output stream");
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleTransport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleTransport.java
index 1219794f..b3211158 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleTransport.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleTransport.java
@@ -1,6 +1,13 @@
package eu.siacs.conversations.xmpp.jingle;
import android.util.Log;
+import android.util.Pair;
+
+import org.bouncycastle.crypto.engines.AESEngine;
+import org.bouncycastle.crypto.modes.AEADBlockCipher;
+import org.bouncycastle.crypto.modes.GCMBlockCipher;
+import org.bouncycastle.crypto.params.AEADParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -31,58 +38,4 @@ public abstract class JingleTransport {
final OnFileTransmissionStatusChanged callback);
public abstract void disconnect();
-
- protected InputStream createInputStream(DownloadableFile file) {
- FileInputStream is;
- try {
- is = new FileInputStream(file);
- if (file.getKey() == null) {
- return is;
- }
- } catch (FileNotFoundException e) {
- return null;
- }
- try {
- IvParameterSpec ips = new IvParameterSpec(file.getIv());
- Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
- cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(file.getKey(), "AES"), ips);
- Log.d(Config.LOGTAG, "opening encrypted input stream");
- return new CipherInputStream(is, cipher);
- } catch (InvalidKeyException e) {
- return null;
- } catch (NoSuchAlgorithmException e) {
- return null;
- } catch (NoSuchPaddingException e) {
- return null;
- } catch (InvalidAlgorithmParameterException e) {
- return null;
- }
- }
-
- protected OutputStream createOutputStream(DownloadableFile file) {
- FileOutputStream os;
- try {
- os = new FileOutputStream(file);
- if (file.getKey() == null) {
- return os;
- }
- } catch (FileNotFoundException e) {
- return null;
- }
- try {
- IvParameterSpec ips = new IvParameterSpec(file.getIv());
- Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
- cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(file.getKey(), "AES"), ips);
- Log.d(Config.LOGTAG, "opening encrypted output stream");
- return new CipherOutputStream(os, cipher);
- } catch (InvalidKeyException e) {
- return null;
- } catch (NoSuchAlgorithmException e) {
- return null;
- } catch (NoSuchPaddingException e) {
- return null;
- } catch (InvalidAlgorithmParameterException e) {
- return null;
- }
- }
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java
index bcadbe77..f752cc5d 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java
@@ -25,17 +25,18 @@ public class Content extends Element {
this.transportId = sid;
}
- public void setFileOffer(DownloadableFile actualFile, boolean otr) {
+ public Element setFileOffer(DownloadableFile actualFile, boolean otr) {
Element description = this.addChild("description",
"urn:xmpp:jingle:apps:file-transfer:3");
Element offer = description.addChild("offer");
Element file = offer.addChild("file");
- file.addChild("size").setContent(Long.toString(actualFile.getSize()));
+ file.addChild("size").setContent(Long.toString(actualFile.getExpectedSize()));
if (otr) {
file.addChild("name").setContent(actualFile.getName() + ".otr");
} else {
file.addChild("name").setContent(actualFile.getName());
}
+ return file;
}
public Element getFileOffer() {