mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-24 21:15:48 +00:00
Replace other limiting streams with TruncatingInputStream.
This commit is contained in:
committed by
Cody Henthorne
parent
b00855b097
commit
a6767e4f8a
@@ -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) {
|
||||
|
||||
@@ -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 <code>int</code> in the range
|
||||
* <code>0</code> to <code>255</code>. If no byte is available
|
||||
* because the end of the stream has been reached, the value
|
||||
* <code>-1</code> 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 <code>in.read()</code> and returns the result.
|
||||
*
|
||||
* @return the next byte of data, or <code>-1</code> 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 <code>len</code> bytes of data from this input stream
|
||||
* into an array of bytes. If <code>len</code> is not zero, the method
|
||||
* blocks until some input is available; otherwise, no
|
||||
* bytes are read and <code>0</code> is returned.
|
||||
*
|
||||
* This method simply performs <code>in.read(b, off, len)</code>
|
||||
* and returns the result.
|
||||
*
|
||||
* @param b the buffer into which the data is read.
|
||||
* @param off The start offset in the destination array
|
||||
* <code>b</code>.
|
||||
* @param len the maximum number of bytes read.
|
||||
* @return the total number of bytes read into the buffer, or
|
||||
* <code>-1</code> if there is no more data because the end of
|
||||
* the stream has been reached.
|
||||
* @exception NullPointerException If <code>b</code> is <code>null</code>.
|
||||
* @exception IndexOutOfBoundsException If <code>off</code> is negative,
|
||||
* <code>len</code> is negative, or <code>len</code> is greater than
|
||||
* <code>b.length - off</code>
|
||||
* @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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user