diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/ClassicDecryptingPartInputStream.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/ClassicDecryptingPartInputStream.java
index 3fa7f63e01..061c844eb4 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/crypto/ClassicDecryptingPartInputStream.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/ClassicDecryptingPartInputStream.java
@@ -19,7 +19,7 @@ package org.thoughtcrime.securesms.crypto;
import androidx.annotation.NonNull;
import org.signal.core.util.logging.Log;
-import org.thoughtcrime.securesms.util.LimitedInputStream;
+import org.signal.core.util.stream.TruncatingInputStream;
import org.thoughtcrime.securesms.util.Util;
import java.io.File;
@@ -63,7 +63,7 @@ public class ClassicDecryptingPartInputStream {
IvParameterSpec iv = new IvParameterSpec(ivBytes);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(attachmentSecret.getClassicCipherKey(), "AES"), iv);
- return new CipherInputStreamWrapper(new LimitedInputStream(fileStream, file.length() - MAC_LENGTH - IV_LENGTH), cipher);
+ return new CipherInputStreamWrapper(new TruncatingInputStream(fileStream, file.length() - MAC_LENGTH - IV_LENGTH), cipher);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) {
throw new AssertionError(e);
}
@@ -72,7 +72,7 @@ public class ClassicDecryptingPartInputStream {
private static void verifyMac(AttachmentSecret attachmentSecret, File file) throws IOException {
Mac mac = initializeMac(new SecretKeySpec(attachmentSecret.getClassicMacKey(), "HmacSHA1"));
FileInputStream macStream = new FileInputStream(file);
- InputStream dataStream = new LimitedInputStream(new FileInputStream(file), file.length() - MAC_LENGTH);
+ InputStream dataStream = new TruncatingInputStream(new FileInputStream(file), file.length() - MAC_LENGTH);
byte[] theirMac = new byte[MAC_LENGTH];
if (macStream.skip(file.length() - MAC_LENGTH) != file.length() - MAC_LENGTH) {
diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/LimitedInputStream.java b/app/src/main/java/org/thoughtcrime/securesms/util/LimitedInputStream.java
deleted file mode 100644
index 9092b785fc..0000000000
--- a/app/src/main/java/org/thoughtcrime/securesms/util/LimitedInputStream.java
+++ /dev/null
@@ -1,120 +0,0 @@
-package org.thoughtcrime.securesms.util;
-
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-
-/**
- * An input stream, which limits its data size. This stream is
- * used, if the content length is unknown.
- */
-public class LimitedInputStream extends FilterInputStream {
-
- /**
- * The maximum size of an item, in bytes.
- */
- private long sizeMax;
-
- /**
- * The current number of bytes.
- */
- private long count;
-
- /**
- * Whether this stream is already closed.
- */
- private boolean closed;
-
- /**
- * Creates a new instance.
- * @param pIn The input stream, which shall be limited.
- * @param pSizeMax The limit; no more than this number of bytes
- * shall be returned by the source stream.
- */
- public LimitedInputStream(InputStream pIn, long pSizeMax) {
- super(pIn);
- sizeMax = pSizeMax;
- }
-
- /**
- * Reads the next byte of data from this input stream. The value
- * byte is returned as an int in the range
- * 0 to 255. If no byte is available
- * because the end of the stream has been reached, the value
- * -1 is returned. This method blocks until input data
- * is available, the end of the stream is detected, or an exception
- * is thrown.
- *
- * This method
- * simply performs in.read() and returns the result.
- *
- * @return the next byte of data, or -1 if the end of the
- * stream is reached.
- * @exception IOException if an I/O error occurs.
- * @see java.io.FilterInputStream#in
- */
- public int read() throws IOException {
- if (count >= sizeMax) return -1;
-
- int res = super.read();
- if (res != -1) {
- count++;
- }
- return res;
- }
-
- /**
- * Reads up to len bytes of data from this input stream
- * into an array of bytes. If len is not zero, the method
- * blocks until some input is available; otherwise, no
- * bytes are read and 0 is returned.
- *
- * This method simply performs in.read(b, off, len)
- * and returns the result.
- *
- * @param b the buffer into which the data is read.
- * @param off The start offset in the destination array
- * b.
- * @param len the maximum number of bytes read.
- * @return the total number of bytes read into the buffer, or
- * -1 if there is no more data because the end of
- * the stream has been reached.
- * @exception NullPointerException If b is null.
- * @exception IndexOutOfBoundsException If off is negative,
- * len is negative, or len is greater than
- * b.length - off
- * @exception IOException if an I/O error occurs.
- * @see java.io.FilterInputStream#in
- */
- public int read(byte[] b, int off, int len) throws IOException {
- if (count >= sizeMax) return -1;
-
- long correctLength = Math.min(len, sizeMax - count);
-
- int res = super.read(b, off, Util.toIntExact(correctLength));
- if (res > 0) {
- count += res;
- }
- return res;
- }
-
-}
\ No newline at end of file
diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java
index e81ab35963..2dae01b6d4 100644
--- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java
+++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherInputStream.java
@@ -6,12 +6,12 @@
package org.whispersystems.signalservice.api.crypto;
+import org.signal.core.util.stream.TruncatingInputStream;
import org.signal.libsignal.protocol.InvalidMessageException;
import org.signal.libsignal.protocol.incrementalmac.ChunkSizeChoice;
import org.signal.libsignal.protocol.incrementalmac.IncrementalMacInputStream;
import org.signal.libsignal.protocol.kdf.HKDF;
import org.whispersystems.signalservice.api.backup.BackupKey;
-import org.whispersystems.signalservice.internal.util.ContentLengthInputStream;
import org.whispersystems.signalservice.internal.util.Util;
import java.io.ByteArrayInputStream;
@@ -25,7 +25,6 @@ import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
-import java.util.function.Supplier;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -118,7 +117,7 @@ public class AttachmentCipherInputStream extends FilterInputStream {
InputStream inputStream = new AttachmentCipherInputStream(wrappedStream, parts[0], streamLength - BLOCK_SIZE - mac.getMacLength());
if (plaintextLength != 0) {
- inputStream = new ContentLengthInputStream(inputStream, plaintextLength);
+ inputStream = new TruncatingInputStream(inputStream, plaintextLength);
}
return inputStream;
@@ -143,7 +142,7 @@ public class AttachmentCipherInputStream extends FilterInputStream {
InputStream inputStream = new AttachmentCipherInputStream(new FileInputStream(file), archivedMediaKeyMaterial.getCipherKey(), file.length() - BLOCK_SIZE - mac.getMacLength());
if (originalCipherTextLength != 0) {
- inputStream = new ContentLengthInputStream(inputStream, originalCipherTextLength);
+ inputStream = new TruncatingInputStream(inputStream, originalCipherTextLength);
}
return inputStream;
@@ -180,7 +179,7 @@ public class AttachmentCipherInputStream extends FilterInputStream {
InputStream inputStream = new AttachmentCipherInputStream(wrappedStream, parts[0], file.length() - BLOCK_SIZE - mac.getMacLength());
if (plaintextLength != 0) {
- inputStream = new ContentLengthInputStream(inputStream, plaintextLength);
+ inputStream = new TruncatingInputStream(inputStream, plaintextLength);
}
return inputStream;
diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/ChunkedInputStream.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/ChunkedInputStream.java
index bc906db004..0247926107 100644
--- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/ChunkedInputStream.java
+++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/ChunkedInputStream.java
@@ -26,83 +26,4 @@ public class ChunkedInputStream {
}
throw new IOException("Malformed varint!");
}
-
- protected static final class LimitedInputStream extends InputStream {
-
- private final InputStream in;
-
- private long left;
- private long mark = -1;
-
- LimitedInputStream(InputStream in, long limit) {
- this.in = in;
- this.left = limit;
- }
-
- @Override
- public int available() throws IOException {
- return (int) Math.min(in.available(), left);
- }
-
- // it's okay to mark even if mark isn't supported, as reset won't work
- @Override
- public synchronized void mark(int readLimit) {
- in.mark(readLimit);
- mark = left;
- }
-
- @Override
- public int read() throws IOException {
- if (left == 0) {
- return -1;
- }
-
- int result = in.read();
- if (result != -1) {
- --left;
- }
- return result;
- }
-
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- if (left == 0) {
- return -1;
- }
-
- len = (int) Math.min(len, left);
- int result = in.read(b, off, len);
- if (result != -1) {
- left -= result;
- }
- return result;
- }
-
- @Override
- public synchronized void reset() throws IOException {
- if (!in.markSupported()) {
- throw new IOException("Mark not supported");
- }
- if (mark == -1) {
- throw new IOException("Mark not set");
- }
-
- in.reset();
- left = mark;
- }
-
- @Override
- public long skip(long n) throws IOException {
- n = Math.min(n, left);
- long skipped = in.skip(n);
- left -= skipped;
- return skipped;
- }
-
- @Override
- public void close() throws IOException {
- // do nothing
- }
- }
-
}
diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceContactsInputStream.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceContactsInputStream.java
index 46055b5040..12c11c0d29 100644
--- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceContactsInputStream.java
+++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceContactsInputStream.java
@@ -6,6 +6,7 @@
package org.whispersystems.signalservice.api.messages.multidevice;
+import org.signal.core.util.stream.TruncatingInputStream;
import org.signal.libsignal.protocol.IdentityKey;
import org.signal.libsignal.protocol.InvalidKeyException;
import org.signal.libsignal.protocol.InvalidMessageException;
@@ -61,7 +62,7 @@ public class DeviceContactsInputStream extends ChunkedInputStream {
if (details.avatar != null && details.avatar.length != null) {
long avatarLength = details.avatar.length;
- InputStream avatarStream = new LimitedInputStream(in, avatarLength);
+ InputStream avatarStream = new TruncatingInputStream(in, avatarLength);
String avatarContentType = details.avatar.contentType != null ? details.avatar.contentType : "image/*";
avatar = Optional.of(new DeviceContactAvatar(avatarStream, avatarLength, avatarContentType));
diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/util/ContentLengthInputStream.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/util/ContentLengthInputStream.java
deleted file mode 100644
index f1a428c270..0000000000
--- a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/util/ContentLengthInputStream.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.whispersystems.signalservice.internal.util;
-
-
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-public class ContentLengthInputStream extends FilterInputStream {
-
- private long bytesRemaining;
-
- public ContentLengthInputStream(InputStream inputStream, long contentLength) {
- super(inputStream);
- this.bytesRemaining = contentLength;
- }
-
- @Override
- public int read() throws IOException {
- if (bytesRemaining == 0) return -1;
- int result = super.read();
- bytesRemaining--;
-
- return result;
- }
-
- @Override
- public int read(byte[] buffer) throws IOException {
- return read(buffer, 0, buffer.length);
- }
-
- @Override
- public int read(byte[] buffer, int offset, int length) throws IOException {
- if (bytesRemaining == 0) return -1;
-
- int result = super.read(buffer, offset, Math.min(length, Util.toIntExact(bytesRemaining)));
-
- bytesRemaining -= result;
- return result;
- }
-
-}
diff --git a/video/lib/src/main/java/org/thoughtcrime/securesms/video/postprocessing/Mp4FaststartPostProcessor.kt b/video/lib/src/main/java/org/thoughtcrime/securesms/video/postprocessing/Mp4FaststartPostProcessor.kt
index 127be2921d..7a4e5673bd 100644
--- a/video/lib/src/main/java/org/thoughtcrime/securesms/video/postprocessing/Mp4FaststartPostProcessor.kt
+++ b/video/lib/src/main/java/org/thoughtcrime/securesms/video/postprocessing/Mp4FaststartPostProcessor.kt
@@ -6,12 +6,11 @@
package org.thoughtcrime.securesms.video.postprocessing
import org.signal.core.util.readLength
+import org.signal.core.util.stream.TruncatingInputStream
import org.signal.libsignal.media.Mp4Sanitizer
import org.signal.libsignal.media.SanitizedMetadata
import org.thoughtcrime.securesms.video.exceptions.VideoPostProcessingException
import java.io.ByteArrayInputStream
-import java.io.FilterInputStream
-import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.io.SequenceInputStream
@@ -35,7 +34,7 @@ class Mp4FaststartPostProcessor(private val inputStreamFactory: InputStreamFacto
}
val inputStream = inputStreamFactory.create()
inputStream.skip(metadata.dataOffset)
- return SequenceInputStream(ByteArrayInputStream(metadata.sanitizedMetadata), LimitedInputStream(inputStream, metadata.dataLength))
+ return SequenceInputStream(ByteArrayInputStream(metadata.sanitizedMetadata), TruncatingInputStream(inputStream, metadata.dataLength))
}
fun processAndWriteTo(outputStream: OutputStream, inputLength: Long = calculateStreamLength(inputStreamFactory.create())): Long {
@@ -65,72 +64,4 @@ class Mp4FaststartPostProcessor(private val inputStreamFactory: InputStreamFacto
}
}
}
-
- private class LimitedInputStream(innerStream: InputStream, limit: Long) : FilterInputStream(innerStream) {
- private var left: Long = limit
- private var mark: Long = -1
-
- init {
- if (limit < 0) {
- throw IllegalArgumentException("Limit must be non-negative!")
- }
- }
-
- @Throws(IOException::class)
- override fun available(): Int {
- return `in`.available().toLong().coerceAtMost(left).toInt()
- }
-
- @Synchronized
- override fun mark(readLimit: Int) {
- `in`.mark(readLimit)
- mark = left
- }
-
- @Throws(IOException::class)
- override fun read(): Int {
- if (left == 0L) {
- return -1
- }
- val result = `in`.read()
- if (result != -1) {
- --left
- }
- return result
- }
-
- @Throws(IOException::class)
- override fun read(b: ByteArray, off: Int, len: Int): Int {
- if (left == 0L) {
- return -1
- }
- val toRead = len.toLong().coerceAtMost(left).toInt()
- val result = `in`.read(b, off, toRead)
- if (result != -1) {
- left -= result.toLong()
- }
- return result
- }
-
- @Synchronized
- @Throws(IOException::class)
- override fun reset() {
- if (!`in`.markSupported()) {
- throw IOException("Mark not supported")
- }
- if (mark == -1L) {
- throw IOException("Mark not set")
- }
- `in`.reset()
- left = mark
- }
-
- @Throws(IOException::class)
- override fun skip(n: Long): Long {
- val toSkip = n.coerceAtMost(left)
- val skipped = `in`.skip(toSkip)
- left -= skipped
- return skipped
- }
- }
}