enable axolotl encryption for jingle supported file transfers
This commit is contained in:
parent
6059b96456
commit
60cd307f73
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in a new issue