refactored file encryption to give access to inner stream

Conscrypt on some plattforms doesn’t like when we close the CipherInputStream. Therefor we refactor the api to give us access to the inner stream so we can close that independently.
This commit is contained in:
Daniel Gultsch 2018-10-03 18:14:41 +02:00
parent 4c08ba8d03
commit f608fb349a
5 changed files with 44 additions and 63 deletions

View File

@ -4,6 +4,7 @@ import android.os.PowerManager;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -31,7 +32,7 @@ import eu.siacs.conversations.utils.WakeLockHelper;
public class HttpUploadConnection implements Transferable { public class HttpUploadConnection implements Transferable {
public static final List<String> WHITE_LISTED_HEADERS = Arrays.asList( static final List<String> WHITE_LISTED_HEADERS = Arrays.asList(
"Authorization", "Authorization",
"Cookie", "Cookie",
"Expires" "Expires"
@ -52,8 +53,6 @@ public class HttpUploadConnection implements Transferable {
private long transmitted = 0; private long transmitted = 0;
private InputStream mFileInputStream;
public HttpUploadConnection(Method method, HttpConnectionManager httpConnectionManager) { public HttpUploadConnection(Method method, HttpConnectionManager httpConnectionManager) {
this.method = method; this.method = method;
this.mHttpConnectionManager = httpConnectionManager; this.mHttpConnectionManager = httpConnectionManager;
@ -94,7 +93,6 @@ public class HttpUploadConnection implements Transferable {
mHttpConnectionManager.finishUploadConnection(this); mHttpConnectionManager.finishUploadConnection(this);
message.setTransferable(null); message.setTransferable(null);
mXmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED, errorMessage); mXmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED, errorMessage);
FileBackend.close(mFileInputStream);
} }
public void init(Message message, boolean delay) { public void init(Message message, boolean delay) {
@ -119,7 +117,7 @@ public class HttpUploadConnection implements Transferable {
if (method == Method.P1_S3) { if (method == Method.P1_S3) {
try { try {
md5 = Checksum.md5(AbstractConnectionManager.createInputStream(file).first); md5 = Checksum.md5(AbstractConnectionManager.upgrade(file, new FileInputStream(file)));
} catch (Exception e) { } catch (Exception e) {
Log.d(Config.LOGTAG, account.getJid().asBareJid()+": unable to calculate md5()", e); Log.d(Config.LOGTAG, account.getJid().asBareJid()+": unable to calculate md5()", e);
fail(e.getMessage()); fail(e.getMessage());
@ -129,17 +127,8 @@ public class HttpUploadConnection implements Transferable {
md5 = null; md5 = null;
} }
Pair<InputStream,Integer> pair; this.file.setExpectedSize(file.getSize() + (file.getKey() != null ? 16 : 0));
try {
pair = AbstractConnectionManager.createInputStream(file);
} catch (FileNotFoundException e) {
Log.d(Config.LOGTAG, account.getJid().asBareJid()+": could not find file to upload - "+e.getMessage());
fail(e.getMessage());
return;
}
this.file.setExpectedSize(pair.second);
message.resetFileParams(); message.resetFileParams();
this.mFileInputStream = pair.first;
this.mSlotRequester.request(method, account, file, mime, md5, new SlotRequester.OnSlotRequested() { this.mSlotRequester.request(method, account, file, mime, md5, new SlotRequester.OnSlotRequested() {
@Override @Override
public void success(SlotRequester.Slot slot) { public void success(SlotRequester.Slot slot) {
@ -160,9 +149,11 @@ public class HttpUploadConnection implements Transferable {
private void upload() { private void upload() {
OutputStream os = null; OutputStream os = null;
InputStream fileInputStream = null;
HttpURLConnection connection = null; HttpURLConnection connection = null;
PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_upload_"+message.getUuid()); PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_upload_"+message.getUuid());
try { try {
fileInputStream = new FileInputStream(file);
final int expectedFileSize = (int) file.getExpectedSize(); final int expectedFileSize = (int) file.getExpectedSize();
final int readTimeout = (expectedFileSize / 2048) + Config.SOCKET_TIMEOUT; //assuming a minimum transfer speed of 16kbit/s final int readTimeout = (expectedFileSize / 2048) + Config.SOCKET_TIMEOUT; //assuming a minimum transfer speed of 16kbit/s
wakeLock.acquire(readTimeout); wakeLock.acquire(readTimeout);
@ -189,18 +180,18 @@ public class HttpUploadConnection implements Transferable {
connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000); connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000);
connection.setReadTimeout(readTimeout * 1000); connection.setReadTimeout(readTimeout * 1000);
connection.connect(); connection.connect();
final InputStream innerInputStream = AbstractConnectionManager.upgrade(file, fileInputStream);
os = connection.getOutputStream(); os = connection.getOutputStream();
transmitted = 0; transmitted = 0;
int count; int count;
byte[] buffer = new byte[4096]; byte[] buffer = new byte[4096];
while (((count = mFileInputStream.read(buffer)) != -1) && !canceled) { while (((count = innerInputStream.read(buffer)) != -1) && !canceled) {
transmitted += count; transmitted += count;
os.write(buffer, 0, count); os.write(buffer, 0, count);
mHttpConnectionManager.updateConversationUi(false); mHttpConnectionManager.updateConversationUi(false);
} }
os.flush(); os.flush();
os.close(); os.close();
mFileInputStream.close();
int code = connection.getResponseCode(); int code = connection.getResponseCode();
InputStream is = connection.getErrorStream(); InputStream is = connection.getErrorStream();
if (is != null) { if (is != null) {
@ -235,7 +226,7 @@ public class HttpUploadConnection implements Transferable {
Log.d(Config.LOGTAG,"http upload failed "+e.getMessage()); Log.d(Config.LOGTAG,"http upload failed "+e.getMessage());
fail(e.getMessage()); fail(e.getMessage());
} finally { } finally {
FileBackend.close(mFileInputStream); FileBackend.close(fileInputStream);
FileBackend.close(os); FileBackend.close(os);
if (connection != null) { if (connection != null) {
connection.disconnect(); connection.disconnect();

View File

@ -13,11 +13,16 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.CipherInputStream; import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream; import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
@ -39,25 +44,19 @@ public class AbstractConnectionManager {
this.mXmppConnectionService = service; this.mXmppConnectionService = service;
} }
public static Pair<InputStream, Integer> createInputStream(DownloadableFile file) throws FileNotFoundException { public static InputStream upgrade(DownloadableFile file, InputStream is) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, NoSuchProviderException {
FileInputStream is; if (file.getKey() != null && file.getIv() != null) {
int size; Cipher cipher = Compatibility.twentyTwo() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
is = new FileInputStream(file);
size = (int) file.getSize();
if (file.getKey() == null) {
return new Pair<>(is, size);
}
try {
Cipher cipher = Cipher.getInstance(CIPHERMODE);
SecretKeySpec keySpec = new SecretKeySpec(file.getKey(), KEYTYPE); SecretKeySpec keySpec = new SecretKeySpec(file.getKey(), KEYTYPE);
IvParameterSpec ivSpec = new IvParameterSpec(file.getIv()); IvParameterSpec ivSpec = new IvParameterSpec(file.getIv());
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
return new Pair<>(new CipherInputStream(is, cipher), cipher.getOutputSize(size)); return new CipherInputStream(is, cipher);
} catch (Exception e) { } else {
throw new AssertionError(e); return is;
} }
} }
public static OutputStream createAppendedOutputStream(DownloadableFile file) { public static OutputStream createAppendedOutputStream(DownloadableFile file) {
return createOutputStream(file, true); return createOutputStream(file, true);
} }

View File

@ -4,6 +4,7 @@ import android.util.Base64;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -471,25 +472,22 @@ public class JingleConnection implements Transferable {
if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) { if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) {
content.setTransportId(this.transportId); 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_AXOLOTL) {
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());
} else {
this.file.setExpectedSize(file.getSize());
content.setFileOffer(this.file, false, this.ftVersion);
}
message.resetFileParams();
try { try {
if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) { this.mFileInputStream = new FileInputStream(file);
this.file.setKey(mXmppAxolotlMessage.getInnerKey());
this.file.setIv(mXmppAxolotlMessage.getIV());
pair = AbstractConnectionManager.createInputStream(this.file);
this.file.setExpectedSize(pair.second);
content.setFileOffer(this.file, false, this.ftVersion).addChild(mXmppAxolotlMessage.toElement());
} else {
pair = AbstractConnectionManager.createInputStream(this.file);
this.file.setExpectedSize(pair.second);
content.setFileOffer(this.file, false, this.ftVersion);
}
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
cancel(); cancel();
return; return;
} }
message.resetFileParams();
this.mFileInputStream = pair.first;
content.setTransportId(this.transportId); content.setTransportId(this.transportId);
content.socks5transport().setChildren(getCandidatesAsElements()); content.socks5transport().setChildren(getCandidatesAsElements());
packet.setContent(content); packet.setContent(content);

View File

@ -14,6 +14,7 @@ import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account; 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.utils.CryptoHelper; import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnIqPacketReceived; import eu.siacs.conversations.xmpp.OnIqPacketReceived;
@ -36,6 +37,7 @@ public class JingleInbandTransport extends JingleTransport {
private JingleConnection connection; private JingleConnection connection;
private InputStream fileInputStream = null; private InputStream fileInputStream = null;
private InputStream innerInputStream = null;
private OutputStream fileOutputStream = null; private OutputStream fileOutputStream = null;
private long remainingSize = 0; private long remainingSize = 0;
private long fileSize = 0; private long fileSize = 0;
@ -128,10 +130,11 @@ public class JingleInbandTransport extends JingleTransport {
callback.onFileTransferAborted(); callback.onFileTransferAborted();
return; return;
} }
innerInputStream = AbstractConnectionManager.upgrade(file, fileInputStream);
if (this.connected) { if (this.connected) {
this.sendNextBlock(); this.sendNextBlock();
} }
} catch (NoSuchAlgorithmException e) { } catch (Exception e) {
callback.onFileTransferAborted(); callback.onFileTransferAborted();
Log.d(Config.LOGTAG,account.getJid().asBareJid()+": "+e.getMessage()); Log.d(Config.LOGTAG,account.getJid().asBareJid()+": "+e.getMessage());
} }
@ -140,26 +143,14 @@ public class JingleInbandTransport extends JingleTransport {
@Override @Override
public void disconnect() { public void disconnect() {
this.connected = false; this.connected = false;
if (this.fileOutputStream != null) { FileBackend.close(fileOutputStream);
try { FileBackend.close(fileInputStream);
this.fileOutputStream.close();
} catch (IOException e) {
}
}
if (this.fileInputStream != null) {
try {
this.fileInputStream.close();
} catch (IOException e) {
}
}
} }
private void sendNextBlock() { private void sendNextBlock() {
byte[] buffer = new byte[this.blockSize]; byte[] buffer = new byte[this.blockSize];
try { try {
int count = fileInputStream.read(buffer); int count = innerInputStream.read(buffer);
if (count == -1) { if (count == -1) {
sendClose(); sendClose();
file.setSha1Sum(digest.digest()); file.setSha1Sum(digest.digest());
@ -167,7 +158,7 @@ public class JingleInbandTransport extends JingleTransport {
fileInputStream.close(); fileInputStream.close();
return; return;
} else if (count != buffer.length) { } else if (count != buffer.length) {
int rem = fileInputStream.read(buffer,count,buffer.length-count); int rem = innerInputStream.read(buffer,count,buffer.length-count);
if (rem > 0) { if (rem > 0) {
count += rem; count += rem;
} }

View File

@ -15,6 +15,7 @@ import java.security.NoSuchAlgorithmException;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
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.utils.CryptoHelper; import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.SocksSocketFactory; import eu.siacs.conversations.utils.SocksSocketFactory;
import eu.siacs.conversations.utils.WakeLockHelper; import eu.siacs.conversations.utils.WakeLockHelper;
@ -94,11 +95,12 @@ public class JingleSocks5Transport extends JingleTransport {
callback.onFileTransferAborted(); callback.onFileTransferAborted();
return; return;
} }
final InputStream innerInputStream = AbstractConnectionManager.upgrade(file, fileInputStream);
long size = file.getExpectedSize(); long size = file.getExpectedSize();
long transmitted = 0; long transmitted = 0;
int count; int count;
byte[] buffer = new byte[8192]; byte[] buffer = new byte[8192];
while ((count = fileInputStream.read(buffer)) > 0) { while ((count = innerInputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, count); outputStream.write(buffer, 0, count);
digest.update(buffer, 0, count); digest.update(buffer, 0, count);
transmitted += count; transmitted += count;