diff --git a/src/main/java/eu/siacs/conversations/entities/Transferable.java b/src/main/java/eu/siacs/conversations/entities/Transferable.java index 5c833f603c00117c76af0bc9c08c258d0344b06a..0db1a8bc7a4e9a6bd73ce16d4990e3c5c68a740d 100644 --- a/src/main/java/eu/siacs/conversations/entities/Transferable.java +++ b/src/main/java/eu/siacs/conversations/entities/Transferable.java @@ -5,26 +5,27 @@ import java.util.List; public interface Transferable { - List VALID_IMAGE_EXTENSIONS = Arrays.asList("webp", "jpeg", "jpg", "png", "jpe"); - List VALID_CRYPTO_EXTENSIONS = Arrays.asList("pgp", "gpg"); + List VALID_IMAGE_EXTENSIONS = Arrays.asList("webp", "jpeg", "jpg", "png", "jpe"); + List VALID_CRYPTO_EXTENSIONS = Arrays.asList("pgp", "gpg"); - int STATUS_UNKNOWN = 0x200; - int STATUS_CHECKING = 0x201; - int STATUS_FAILED = 0x202; - int STATUS_OFFER = 0x203; - int STATUS_DOWNLOADING = 0x204; - int STATUS_OFFER_CHECK_FILESIZE = 0x206; - int STATUS_UPLOADING = 0x207; - int STATUS_CANCELLED = 0x208; + int GCM_AUTHENTICATION_TAG_LENGTH = 16; + int STATUS_UNKNOWN = 0x200; + int STATUS_CHECKING = 0x201; + int STATUS_FAILED = 0x202; + int STATUS_OFFER = 0x203; + int STATUS_DOWNLOADING = 0x204; + int STATUS_OFFER_CHECK_FILESIZE = 0x206; + int STATUS_UPLOADING = 0x207; + int STATUS_CANCELLED = 0x208; - boolean start(); + boolean start(); - int getStatus(); + int getStatus(); - Long getFileSize(); + Long getFileSize(); - int getProgress(); + int getProgress(); - void cancel(); + void cancel(); } diff --git a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java index 88e5f801f229cc2ed9ddff3542d46d0cfb55a7de..9560522c2aabfa730ce53ce3ba30fb9d7be7b2e4 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java +++ b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java @@ -1,21 +1,12 @@ package eu.siacs.conversations.http; -import android.util.Log; +import static eu.siacs.conversations.http.HttpConnectionManager.EXECUTOR; +import android.util.Log; import androidx.annotation.Nullable; - import com.google.common.base.Strings; import com.google.common.io.ByteStreams; import com.google.common.primitives.Longs; - -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Locale; - -import javax.net.ssl.SSLHandshakeException; - import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.DownloadableFile; @@ -27,13 +18,23 @@ import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.FileWriterException; import eu.siacs.conversations.utils.MimeUtils; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Locale; +import javax.net.ssl.SSLHandshakeException; import okhttp3.Call; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; - -import static eu.siacs.conversations.http.HttpConnectionManager.EXECUTOR; +import org.bouncycastle.crypto.engines.AESEngine; +import org.bouncycastle.crypto.io.CipherOutputStream; +import org.bouncycastle.crypto.modes.GCMBlockCipher; +import org.bouncycastle.crypto.params.AEADParameters; +import org.bouncycastle.crypto.params.KeyParameter; public class HttpDownloadConnection implements Transferable { @@ -88,30 +89,36 @@ public class HttpDownloadConnection implements Transferable { } else { mUrl = AesGcmURL.of(message.getBody().split("\n")[0]); } - final AbstractConnectionManager.Extension extension = AbstractConnectionManager.Extension.of(mUrl.encodedPath()); + final AbstractConnectionManager.Extension extension = + AbstractConnectionManager.Extension.of(mUrl.encodedPath()); if (VALID_CRYPTO_EXTENSIONS.contains(extension.main)) { this.message.setEncryption(Message.ENCRYPTION_PGP); } else if (message.getEncryption() != Message.ENCRYPTION_AXOLOTL) { this.message.setEncryption(Message.ENCRYPTION_NONE); } final String ext = extension.getExtension(); - final String filename = Strings.isNullOrEmpty(ext) ? message.getUuid() : String.format("%s.%s", message.getUuid(), ext); + final String filename = + Strings.isNullOrEmpty(ext) + ? message.getUuid() + : String.format("%s.%s", message.getUuid(), ext); mXmppConnectionService.getFileBackend().setupRelativeFilePath(message, filename); setupFile(); - if (this.message.getEncryption() == Message.ENCRYPTION_AXOLOTL && this.file.getKey() == null) { + if (this.message.getEncryption() == Message.ENCRYPTION_AXOLOTL + && this.file.getKey() == null) { this.message.setEncryption(Message.ENCRYPTION_NONE); } final Long knownFileSize; - if (message.getEncryption() == Message.ENCRYPTION_PGP || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { + if (message.getEncryption() == Message.ENCRYPTION_PGP + || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { knownFileSize = null; } else { knownFileSize = message.getFileParams().size; } - Log.d(Config.LOGTAG,"knownFileSize: "+knownFileSize+", body="+message.getBody()); + Log.d(Config.LOGTAG, "knownFileSize: " + knownFileSize + ", body=" + message.getBody()); if (knownFileSize != null && interactive) { if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL && this.file.getKey() != null) { - this.file.setExpectedSize(knownFileSize + 16); + this.file.setExpectedSize(knownFileSize + GCM_AUTHENTICATION_TAG_LENGTH); } else { this.file.setExpectedSize(knownFileSize); } @@ -127,9 +134,16 @@ public class HttpDownloadConnection implements Transferable { private void setupFile() { final String reference = mUrl.fragment(); if (reference != null && AesGcmURL.IV_KEY.matcher(reference).matches()) { - this.file = new DownloadableFile(mXmppConnectionService.getCacheDir(), message.getUuid()); + this.file = + new DownloadableFile(mXmppConnectionService.getCacheDir(), message.getUuid()); this.file.setKeyAndIv(CryptoHelper.hexToBytes(reference)); - Log.d(Config.LOGTAG, "create temporary OMEMO encrypted file: " + this.file.getAbsolutePath() + "(" + message.getMimeType() + ")"); + Log.d( + Config.LOGTAG, + "create temporary OMEMO encrypted file: " + + this.file.getAbsolutePath() + + "(" + + message.getMimeType() + + ")"); } else { this.file = mXmppConnectionService.getFileBackend().getFile(message, false); } @@ -158,29 +172,30 @@ public class HttpDownloadConnection implements Transferable { } private void decryptFile() throws IOException { - final DownloadableFile outputFile = mXmppConnectionService.getFileBackend().getFile(message, true); + final DownloadableFile outputFile = + mXmppConnectionService.getFileBackend().getFile(message, true); - if (outputFile.getParentFile().mkdirs()) { + final var directory = outputFile.getParentFile(); + if (directory != null && directory.mkdirs()) { Log.d(Config.LOGTAG, "created parent directories for " + outputFile.getAbsolutePath()); } if (!outputFile.createNewFile()) { Log.w(Config.LOGTAG, "unable to create output file " + outputFile.getAbsolutePath()); } + final var cipher = GCMBlockCipher.newInstance(AESEngine.newInstance()); + cipher.init( + false, new AEADParameters(new KeyParameter(this.file.getKey()), 128, file.getIv())); + try (final InputStream is = new FileInputStream(this.file); + final CipherOutputStream outputStream = + new CipherOutputStream(new FileOutputStream(outputFile), cipher)) { + ByteStreams.copy(is, outputStream); + } - final InputStream is = new FileInputStream(this.file); - - outputFile.setKey(this.file.getKey()); - outputFile.setIv(this.file.getIv()); - final OutputStream os = AbstractConnectionManager.createOutputStream(outputFile, false, true); - - ByteStreams.copy(is, os); - - FileBackend.close(is); - FileBackend.close(os); - - if (!file.delete()) { - Log.w(Config.LOGTAG, "unable to delete temporary OMEMO encrypted file " + file.getAbsolutePath()); + if (file.delete()) { + Log.w( + Config.LOGTAG, + "deleted temporary OMEMO encrypted file " + file.getAbsolutePath()); } } @@ -189,16 +204,25 @@ public class HttpDownloadConnection implements Transferable { mHttpConnectionManager.finishConnection(this); boolean notify = acceptedAutomatically && !message.isRead(); if (message.getEncryption() == Message.ENCRYPTION_PGP) { - notify = message.getConversation().getAccount().getPgpDecryptionService().decrypt(message, notify); + notify = + message.getConversation() + .getAccount() + .getPgpDecryptionService() + .decrypt(message, notify); } mHttpConnectionManager.updateConversationUi(true); final boolean notifyAfterScan = notify; - final DownloadableFile file = mXmppConnectionService.getFileBackend().getFile(message, true); - mXmppConnectionService.getFileBackend().updateMediaScanner(file, () -> { - if (notifyAfterScan) { - mXmppConnectionService.getNotificationService().push(message); - } - }); + final DownloadableFile file = + mXmppConnectionService.getFileBackend().getFile(message, true); + mXmppConnectionService + .getFileBackend() + .updateMediaScanner( + file, + () -> { + if (notifyAfterScan) { + mXmppConnectionService.getNotificationService().push(message); + } + }); } private void decryptIfNeeded() throws IOException { @@ -223,7 +247,8 @@ public class HttpDownloadConnection implements Transferable { } else if (e instanceof java.net.ConnectException) { mXmppConnectionService.showErrorToastInUi(R.string.download_failed_could_not_connect); } else if (e instanceof FileWriterException) { - mXmppConnectionService.showErrorToastInUi(R.string.download_failed_could_not_write_file); + mXmppConnectionService.showErrorToastInUi( + R.string.download_failed_could_not_write_file); } else if (e instanceof InvalidFileException) { mXmppConnectionService.showErrorToastInUi(R.string.download_failed_invalid_file); } else { @@ -267,7 +292,6 @@ public class HttpDownloadConnection implements Transferable { this.interactive = interactive; } - @Override public void run() { check(); @@ -279,7 +303,10 @@ public class HttpDownloadConnection implements Transferable { showToastForException(e); } else { HttpDownloadConnection.this.acceptedAutomatically = false; - HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message); + HttpDownloadConnection.this + .mXmppConnectionService + .getNotificationService() + .push(message); } cancel(); } @@ -289,12 +316,11 @@ public class HttpDownloadConnection implements Transferable { try { size = retrieveFileSize(); } catch (final Exception e) { - Log.d(Config.LOGTAG, "io exception in http file size checker: " + e.getMessage()); + Log.d(Config.LOGTAG, "could not retrieve file size", e); retrieveFailed(e); return; } - final Message.FileParams fileParams = message.getFileParams(); - FileBackend.updateFileParams(message, fileParams.url, size); + persistFileSize(size); message.setOob(true); mXmppConnectionService.databaseBackend.updateMessage(message, true); file.setExpectedSize(size); @@ -307,54 +333,69 @@ public class HttpDownloadConnection implements Transferable { } else { changeStatus(STATUS_OFFER); HttpDownloadConnection.this.acceptedAutomatically = false; - HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message); + HttpDownloadConnection.this + .mXmppConnectionService + .getNotificationService() + .push(message); } } private long retrieveFileSize() throws IOException { Log.d(Config.LOGTAG, "retrieve file size. interactive:" + interactive); changeStatus(STATUS_CHECKING); - final OkHttpClient client = mHttpConnectionManager.buildHttpClient( - mUrl, - message.getConversation().getAccount(), - interactive - ); - final Request request = new Request.Builder() - .url(URL.stripFragment(mUrl)) - .addHeader("Accept-Encoding", "identity") - .head() - .build(); + final OkHttpClient client = + mHttpConnectionManager.buildHttpClient( + mUrl, message.getConversation().getAccount(), interactive); + final Request request = + new Request.Builder() + .url(URL.stripFragment(mUrl)) + .addHeader("Accept-Encoding", "identity") + .head() + .build(); mostRecentCall = client.newCall(request); - try { - final Response response = mostRecentCall.execute(); + try (final Response response = mostRecentCall.execute()) { throwOnInvalidCode(response); final String contentLength = response.header("Content-Length"); final String contentType = response.header("Content-Type"); - final AbstractConnectionManager.Extension extension = AbstractConnectionManager.Extension.of(mUrl.encodedPath()); + final AbstractConnectionManager.Extension extension = + AbstractConnectionManager.Extension.of(mUrl.encodedPath()); if (Strings.isNullOrEmpty(extension.getExtension()) && contentType != null) { final String fileExtension = MimeUtils.guessExtensionFromMimeType(contentType); if (fileExtension != null) { - mXmppConnectionService.getFileBackend().setupRelativeFilePath(message, String.format("%s.%s", message.getUuid(), fileExtension), contentType); - Log.d(Config.LOGTAG, "rewriting name after not finding extension in url but in content type"); + mXmppConnectionService + .getFileBackend() + .setupRelativeFilePath( + message, + String.format("%s.%s", message.getUuid(), fileExtension), + contentType); + Log.d( + Config.LOGTAG, + "rewriting name after not finding extension in url but in content" + + " type"); setupFile(); } } - if (Strings.isNullOrEmpty(contentLength)) { + final Long size = Longs.tryParse(Strings.nullToEmpty(contentLength)); + if (size == null || size < 0) { throw new IOException("no content-length found in HEAD response"); } - final long size = Long.parseLong(contentLength, 10); - if (size < 0) { - throw new IOException("Server reported negative file size"); - } return size; - } catch (final IOException e) { - Log.d(Config.LOGTAG, "io exception during HEAD " + e.getMessage()); - throw e; - } catch (final NumberFormatException e) { - throw new IOException(e); } } + } + private void persistFileSize(final long size) { + final Message.FileParams fileParams = message.getFileParams(); + if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL && file.getKey() != null) { + // store the file size of the clear text file. If we resume the download we will add the + // auth tag size again + // this is equivalent to use updating file params *after* download (which would take the + // clear text size as well) + FileBackend.updateFileParams( + message, fileParams.url, size - GCM_AUTHENTICATION_TAG_LENGTH); + } else { + FileBackend.updateFileParams(message, fileParams.url, size); + } } private class FileDownloader implements Runnable { @@ -376,65 +417,110 @@ public class HttpDownloadConnection implements Transferable { } catch (final SSLHandshakeException e) { changeStatus(STATUS_OFFER); } catch (final Exception e) { - Log.d(Config.LOGTAG, message.getConversation().getAccount().getJid().asBareJid() + ": unable to download file", e); + Log.d( + Config.LOGTAG, + message.getConversation().getAccount().getJid().asBareJid() + + ": unable to download file", + e); if (interactive) { showToastForException(e); } else { HttpDownloadConnection.this.acceptedAutomatically = false; - HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message); + HttpDownloadConnection.this + .mXmppConnectionService + .getNotificationService() + .push(message); } cancel(); } } private void download() throws Exception { - final OkHttpClient client = mHttpConnectionManager.buildHttpClient( - mUrl, - message.getConversation().getAccount(), - interactive - ); + final OkHttpClient client = + mHttpConnectionManager.buildHttpClient( + mUrl, message.getConversation().getAccount(), interactive); - final Request.Builder requestBuilder = new Request.Builder().url(URL.stripFragment(mUrl)); + final Request.Builder requestBuilder = + new Request.Builder().url(URL.stripFragment(mUrl)); final long expected = file.getExpectedSize(); - final boolean tryResume = file.exists() && file.getSize() > 0 && file.getSize() < expected; + // TODO potentially just skip if file exists and size == expected? (this can happen if + // the decryption failed) + final boolean tryResume = + file.exists() && file.getSize() > 0 && file.getSize() < expected; final long resumeSize; if (tryResume) { resumeSize = file.getSize(); - Log.d(Config.LOGTAG, "http download trying resume after " + resumeSize + " of " + expected); - requestBuilder.addHeader("Range", String.format(Locale.ENGLISH, "bytes=%d-", resumeSize)); + Log.d( + Config.LOGTAG, + "http download trying resume after " + resumeSize + " of " + expected); + requestBuilder.addHeader( + "Range", String.format(Locale.ENGLISH, "bytes=%d-", resumeSize)); } else { resumeSize = 0; } final Request request = requestBuilder.build(); mostRecentCall = client.newCall(request); - final Response response = mostRecentCall.execute(); - throwOnInvalidCode(response); - final String contentRange = response.header("Content-Range"); - final boolean serverResumed = tryResume && contentRange != null && contentRange.startsWith("bytes " + resumeSize + "-"); - final InputStream inputStream = response.body().byteStream(); - final OutputStream outputStream; - long transmitted = 0; - if (tryResume && serverResumed) { - Log.d(Config.LOGTAG, "server resumed"); - transmitted = file.getSize(); - updateProgress(Math.round(((double) transmitted / expected) * 100)); - outputStream = AbstractConnectionManager.createOutputStream(file, true, false); - } else { - final String contentLength = response.header("Content-Length"); - final long size = Strings.isNullOrEmpty(contentLength) ? 0 : Longs.tryParse(contentLength); - if (expected != size) { - Log.d(Config.LOGTAG, "content-length reported on GET (" + size + ") did not match Content-Length reported on HEAD (" + expected + ")"); + try (final Response response = mostRecentCall.execute()) { + throwOnInvalidCode(response); + final String contentRange = response.header("Content-Range"); + final boolean serverResumed = + tryResume + && contentRange != null + && contentRange.startsWith("bytes " + resumeSize + "-"); + final var body = response.body(); + if (body == null) { + throw new IOException("response body was null"); } - file.getParentFile().mkdirs(); - Log.d(Config.LOGTAG,"creating file: "+file.getAbsolutePath()); - if (!file.exists() && !file.createNewFile()) { - throw new FileWriterException(file); + final InputStream inputStream = body.byteStream(); + if (tryResume && serverResumed) { + Log.d(Config.LOGTAG, "server resumed"); + final var offset = file.getSize(); + try (final OutputStream os = new FileOutputStream(file, true)) { + copy(inputStream, os, offset, expected); + } + } else { + final String contentLength = response.header("Content-Length"); + final Long size = Longs.tryParse(Strings.nullToEmpty(contentLength)); + if (size == null) { + Log.d(Config.LOGTAG, "no content-length in GET response (probably gzip)"); + } else { + if (expected != size) { + if (expected == 0) { + // this means we got 0 (unknown) on HEAD. We won't download the file + // but we update the file size so the user can try it again now that + // the actual file size is known + persistFileSize(size); + } + throw new IOException( + "Content-Length in GET response did not match expected size"); + } + } + final var directory = file.getParentFile(); + if (directory != null && directory.mkdirs()) { + Log.d(Config.LOGTAG, "create directory " + directory.getAbsolutePath()); + } + Log.d(Config.LOGTAG, "creating file: " + file.getAbsolutePath()); + if (!file.exists() && !file.createNewFile()) { + throw new FileWriterException(file); + } + try (final OutputStream os = new FileOutputStream(file)) { + copy(inputStream, os, 0, expected); + } } - outputStream = AbstractConnectionManager.createOutputStream(file, false, false); } + } + + private void copy( + final InputStream inputStream, + final OutputStream outputStream, + final long offset, + final long expected) + throws IOException, FileWriterException { + long transmitted = offset; int count; final byte[] buffer = new byte[4096]; + updateProgress(Math.round(((double) transmitted / expected) * 100)); while ((count = inputStream.read(buffer)) != -1) { transmitted += count; try { @@ -443,11 +529,11 @@ public class HttpDownloadConnection implements Transferable { throw new FileWriterException(file); } if (transmitted > expected) { - throw new InvalidFileException(String.format("File exceeds expected size of %d", expected)); + throw new InvalidFileException( + String.format("File exceeds expected size of %d", expected)); } updateProgress(Math.round(((double) transmitted / expected) * 100)); } - outputStream.flush(); } private void updateImageBounds() { @@ -463,7 +549,6 @@ public class HttpDownloadConnection implements Transferable { mXmppConnectionService.getFileBackend().updateFileParams(message, url); mXmppConnectionService.updateMessage(message); } - } private static void throwOnInvalidCode(final Response response) throws IOException { @@ -478,6 +563,5 @@ public class HttpDownloadConnection implements Transferable { private InvalidFileException(final String message) { super(message); } - } } diff --git a/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java b/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java index ce9a80cd0f84ee080428d580a0d09c830282d6da..d3887a82ddee6b0873b8e696f769c64c36ff4a2c 100644 --- a/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java @@ -4,19 +4,15 @@ import static eu.siacs.conversations.entities.Transferable.VALID_CRYPTO_EXTENSIO import android.os.PowerManager; import android.os.SystemClock; -import android.util.Log; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; -import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.utils.Compatibility; import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.util.concurrent.atomic.AtomicLong; import okhttp3.MediaType; import okhttp3.RequestBody; @@ -25,7 +21,6 @@ import okio.Okio; import okio.Source; import org.bouncycastle.crypto.engines.AESEngine; import org.bouncycastle.crypto.io.CipherInputStream; -import org.bouncycastle.crypto.io.CipherOutputStream; import org.bouncycastle.crypto.modes.AEADBlockCipher; import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.crypto.params.AEADParameters; @@ -71,7 +66,7 @@ public class AbstractConnectionManager { } @Override - public void writeTo(final BufferedSink sink) throws IOException { + public void writeTo(@NonNull final BufferedSink sink) throws IOException { long transmitted = 0; try (final Source source = Okio.source(upgrade(file, new FileInputStream(file)))) { long read; @@ -89,29 +84,6 @@ public class AbstractConnectionManager { void onProgress(long progress); } - public static OutputStream createOutputStream( - DownloadableFile file, boolean append, boolean decrypt) { - FileOutputStream os; - try { - os = new FileOutputStream(file, append); - if (file.getKey() == null || !decrypt) { - return os; - } - } catch (FileNotFoundException e) { - Log.d(Config.LOGTAG, "unable to create output stream", e); - return null; - } - try { - AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine()); - cipher.init( - false, new AEADParameters(new KeyParameter(file.getKey()), 128, file.getIv())); - return new CipherOutputStream(os, cipher); - } catch (Exception e) { - Log.d(Config.LOGTAG, "unable to create cipher output stream", e); - return null; - } - } - public XmppConnectionService getXmppConnectionService() { return this.mXmppConnectionService; } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java index c17176ad17801954bb44f53d7f872f338f76be69..ec73b11acdbe13ea0276903fd587df2bc1711a25 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java @@ -1303,7 +1303,8 @@ public class JingleFileTransferConnection extends AbstractJingleConnection this.file = file; this.transportSecurity = transportSecurity; this.transportTerminationLatch = transportTerminationLatch; - this.total = transportSecurity == null ? total : (total + 16); + this.total = + transportSecurity == null ? total : (total + GCM_AUTHENTICATION_TAG_LENGTH); this.updateRunnable = updateRunnable; } @@ -1445,7 +1446,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection if (this.transportSecurity == null) { return fileOutputStream; } else { - final AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine()); + final var cipher = GCMBlockCipher.newInstance(AESEngine.newInstance()); cipher.init( false, new AEADParameters(