mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-09-17 06:07:18 +01:00
Limit the size of incoming text to 2 kibibytes.
This commit is contained in:
committed by
Cody Henthorne
parent
cb8bcba86b
commit
ec9e5e5dee
@@ -28,7 +28,6 @@ import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientUtil
|
||||
import org.thoughtcrime.securesms.transport.RetryLaterException
|
||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException
|
||||
import org.thoughtcrime.securesms.util.MessageUtil
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig
|
||||
import org.thoughtcrime.securesms.util.SignalLocalMetrics
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender.IndividualSendEvents
|
||||
@@ -38,6 +37,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachment
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.PaymentActivation
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceEditMessage
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceMessageLimits
|
||||
import org.whispersystems.signalservice.api.messages.SignalServicePreview
|
||||
import org.whispersystems.signalservice.api.messages.shared.SharedContact
|
||||
import org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException
|
||||
@@ -258,8 +258,8 @@ class IndividualSendJob private constructor(parameters: Parameters, private val
|
||||
|
||||
@Throws(IOException::class, UnregisteredUserException::class, UntrustedIdentityException::class, UndeliverableMessageException::class)
|
||||
private fun deliver(message: OutgoingMessage, originalEditedMessage: MessageRecord?): Boolean {
|
||||
if (message.body.utf8Size() > MessageUtil.MAX_INLINE_BODY_SIZE_BYTES) {
|
||||
throw UndeliverableMessageException("The total body size was greater than our limit of " + MessageUtil.MAX_INLINE_BODY_SIZE_BYTES + " bytes.")
|
||||
if (message.body.utf8Size() > SignalServiceMessageLimits.MAX_INLINE_BODY_SIZE_BYTES) {
|
||||
throw UndeliverableMessageException("The total body size was greater than our limit of " + SignalServiceMessageLimits.MAX_INLINE_BODY_SIZE_BYTES + " bytes.")
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -41,7 +41,6 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.ratelimit.ProofRequiredExceptionHandler
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientUtil
|
||||
import org.thoughtcrime.securesms.util.MessageUtil
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig
|
||||
import org.thoughtcrime.securesms.util.SignalLocalMetrics
|
||||
import org.thoughtcrime.securesms.util.isUrgent
|
||||
@@ -49,6 +48,7 @@ import org.thoughtcrime.securesms.util.toDataMessage
|
||||
import org.whispersystems.signalservice.api.crypto.ContentHint
|
||||
import org.whispersystems.signalservice.api.crypto.EnvelopeContent
|
||||
import org.whispersystems.signalservice.api.messages.SendMessageResult
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceMessageLimits
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||
import org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException
|
||||
import org.whispersystems.signalservice.internal.push.Content
|
||||
@@ -179,8 +179,8 @@ class IndividualSendJobV2 private constructor(parameters: Parameters, private va
|
||||
null
|
||||
}
|
||||
|
||||
if (message.body.utf8Size() > MessageUtil.MAX_INLINE_BODY_SIZE_BYTES) {
|
||||
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Body size exceeds limit of ${MessageUtil.MAX_INLINE_BODY_SIZE_BYTES} bytes; failing.")
|
||||
if (message.body.utf8Size() > SignalServiceMessageLimits.MAX_INLINE_BODY_SIZE_BYTES) {
|
||||
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Body size exceeds limit of ${SignalServiceMessageLimits.MAX_INLINE_BODY_SIZE_BYTES} bytes; failing.")
|
||||
return Result.failure()
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,6 @@ import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.util.MessageUtil;
|
||||
import org.thoughtcrime.securesms.util.RecipientAccessList;
|
||||
import org.thoughtcrime.securesms.util.SignalLocalMetrics;
|
||||
import org.whispersystems.signalservice.api.crypto.ContentHint;
|
||||
@@ -56,6 +55,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceEditMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceMessageLimits;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServicePreview;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceStoryMessage;
|
||||
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
|
||||
@@ -276,8 +276,8 @@ public final class PushGroupSendJob extends PushSendJob {
|
||||
private List<SendMessageResult> deliver(OutgoingMessage message, @Nullable MessageRecord originalEditedMessage, @NonNull Recipient groupRecipient, @NonNull List<Recipient> destinations)
|
||||
throws IOException, UntrustedIdentityException, UndeliverableMessageException, NoSessionException
|
||||
{
|
||||
if (Utf8.size(message.getBody()) > MessageUtil.MAX_INLINE_BODY_SIZE_BYTES) {
|
||||
throw new UndeliverableMessageException("The total body size was greater than our limit of " + MessageUtil.MAX_INLINE_BODY_SIZE_BYTES + " bytes.");
|
||||
if (Utf8.size(message.getBody()) > SignalServiceMessageLimits.MAX_INLINE_BODY_SIZE_BYTES) {
|
||||
throw new UndeliverableMessageException("The total body size was greater than our limit of " + SignalServiceMessageLimits.MAX_INLINE_BODY_SIZE_BYTES + " bytes.");
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -45,6 +45,7 @@ import org.signal.core.util.Base64;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.util.MessageUtil;
|
||||
import org.signal.core.util.Util;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceMessageLimits;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -111,7 +112,7 @@ public final class MultiShareSender {
|
||||
List<Contact> contacts = multiShareArgs.getSharedContacts();
|
||||
SlideDeck slideDeck = new SlideDeck(primarySlideDeck);
|
||||
|
||||
boolean needsSplit = message != null && Utf8.size(message) > MessageUtil.MAX_INLINE_BODY_SIZE_BYTES;
|
||||
boolean needsSplit = message != null && Utf8.size(message) > SignalServiceMessageLimits.MAX_INLINE_BODY_SIZE_BYTES;
|
||||
boolean hasMmsMedia = !multiShareArgs.getMedia().isEmpty() ||
|
||||
(multiShareArgs.getDataUri() != null && multiShareArgs.getDataUri() != Uri.EMPTY) ||
|
||||
multiShareArgs.getStickerLocator() != null ||
|
||||
|
||||
@@ -5,16 +5,13 @@ import org.signal.core.util.kibiBytes
|
||||
import org.signal.core.util.splitByByteLength
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.mms.TextSlide
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceMessageLimits
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import java.util.Optional
|
||||
|
||||
object MessageUtil {
|
||||
/** The maximum size of an inlined text body we'll allow in a proto. Anything larger than this will need to be a long-text attachment. */
|
||||
@JvmField
|
||||
val MAX_INLINE_BODY_SIZE_BYTES: Int = 2.kibiBytes.bytes.toInt()
|
||||
|
||||
/** The maximum total message size we'll allow ourselves to send, even as a long text attachment. */
|
||||
@JvmField
|
||||
val MAX_TOTAL_BODY_SIZE_BYTES = 64.kibiBytes.bytes.toInt()
|
||||
@@ -25,7 +22,7 @@ object MessageUtil {
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getSplitMessage(context: Context, rawText: String): SplitResult {
|
||||
val (trimmed, remainder) = rawText.splitByByteLength(MAX_INLINE_BODY_SIZE_BYTES)
|
||||
val (trimmed, remainder) = rawText.splitByByteLength(SignalServiceMessageLimits.MAX_INLINE_BODY_SIZE_BYTES)
|
||||
|
||||
return if (remainder != null) {
|
||||
val textData = rawText.toByteArray()
|
||||
|
||||
@@ -64,11 +64,11 @@ import org.thoughtcrime.securesms.service.ExpiringMessageManager
|
||||
import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule
|
||||
import org.thoughtcrime.securesms.testutil.MockSignalStoreRule
|
||||
import org.thoughtcrime.securesms.util.DataMessageError
|
||||
import org.thoughtcrime.securesms.util.MessageUtil
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig
|
||||
import org.thoughtcrime.securesms.util.toDataMessage
|
||||
import org.whispersystems.signalservice.api.crypto.ContentHint
|
||||
import org.whispersystems.signalservice.api.crypto.EnvelopeContent
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceMessageLimits
|
||||
import org.whispersystems.signalservice.internal.push.Content
|
||||
import org.whispersystems.signalservice.internal.push.DataMessage
|
||||
import org.whispersystems.signalservice.internal.push.PniSignatureMessage
|
||||
@@ -293,7 +293,7 @@ class IndividualSendJobV2Test {
|
||||
|
||||
@Test
|
||||
fun `Given body exceeds inline size limit, when run, then return failure`() {
|
||||
val tooLargeBody = "x".repeat(MessageUtil.MAX_INLINE_BODY_SIZE_BYTES + 1)
|
||||
val tooLargeBody = "x".repeat(SignalServiceMessageLimits.MAX_INLINE_BODY_SIZE_BYTES + 1)
|
||||
every { outgoingMessage.body } returns tooLargeBody
|
||||
|
||||
val result = createAndRunJob()
|
||||
|
||||
@@ -40,3 +40,35 @@ fun OutputStream.writeUInt(value: UInt) {
|
||||
// Note that casting to an int here is fine, because at the end of the day, we're just writing 4 bytes to the stream
|
||||
this.write(ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putInt(value.toInt()).array())
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes [count] zero bytes to the stream, using a bounded buffer so that we never allocate the full [count] at once.
|
||||
* A non-positive [count] writes nothing.
|
||||
*/
|
||||
fun OutputStream.writeZeros(count: Long, maxBufferSize: Int = 32 * 1024) {
|
||||
writeRepeated(0, count, maxBufferSize)
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes [value] to the stream [count] times, using a bounded buffer so that we never allocate the full [count] at once.
|
||||
* A non-positive [count] writes nothing.
|
||||
*/
|
||||
fun OutputStream.writeRepeated(value: Byte, count: Long, maxBufferSize: Int = 32 * 1024) {
|
||||
require(maxBufferSize > 0) { "maxBufferSize must be positive, was $maxBufferSize" }
|
||||
|
||||
if (count <= 0) {
|
||||
return
|
||||
}
|
||||
|
||||
val buffer = ByteArray(minOf(count, maxBufferSize.toLong()).toInt())
|
||||
if (value != 0.toByte()) {
|
||||
buffer.fill(value)
|
||||
}
|
||||
|
||||
var remaining = count
|
||||
while (remaining > 0) {
|
||||
val chunkSize = minOf(remaining, buffer.size.toLong()).toInt()
|
||||
this.write(buffer, 0, chunkSize)
|
||||
remaining -= chunkSize
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.signal.archive.stream
|
||||
|
||||
import org.signal.core.util.writeZeros
|
||||
import org.whispersystems.signalservice.internal.crypto.PaddingInputStream
|
||||
import java.io.FilterOutputStream
|
||||
import java.io.OutputStream
|
||||
@@ -24,9 +25,8 @@ class PaddedGzipOutputStream private constructor(private val outputStream: SizeO
|
||||
|
||||
val totalLength = outputStream.size
|
||||
val paddedSize: Long = PaddingInputStream.getPaddedSize(totalLength)
|
||||
val paddingToAdd: Int = (paddedSize - totalLength).toInt()
|
||||
|
||||
outputStream.write(ByteArray(paddingToAdd))
|
||||
outputStream.writeZeros(paddedSize - totalLength)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
-2
@@ -62,6 +62,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStre
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceEditMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceMessageLimits;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServicePreview;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceStoryMessage;
|
||||
@@ -1004,8 +1005,8 @@ public class SignalServiceMessageSender {
|
||||
Content.Builder container = new Content.Builder();
|
||||
DataMessage.Builder dataMessage = createDataMessage(message);
|
||||
|
||||
if (dataMessage.body != null && Utf8.size(dataMessage.body) > 2048) {
|
||||
throw new ContentTooLargeException(Utf8.size(dataMessage.body), "UTF-8 size of the data message body was over 2048 bytes!");
|
||||
if (dataMessage.body != null && Utf8.size(dataMessage.body) > SignalServiceMessageLimits.MAX_INLINE_BODY_SIZE_BYTES) {
|
||||
throw new ContentTooLargeException(Utf8.size(dataMessage.body), "UTF-8 size of the data message body was over " + SignalServiceMessageLimits.MAX_INLINE_BODY_SIZE_BYTES + " bytes!");
|
||||
}
|
||||
|
||||
return enforceMaxContentSize(container.dataMessage(dataMessage.build()).build());
|
||||
|
||||
+13
@@ -1,6 +1,7 @@
|
||||
package org.whispersystems.signalservice.api.messages
|
||||
|
||||
import okio.ByteString
|
||||
import okio.utf8Size
|
||||
import org.signal.core.models.ServiceId
|
||||
import org.signal.core.models.ServiceId.ACI
|
||||
import org.signal.libsignal.protocol.message.CiphertextMessage
|
||||
@@ -91,6 +92,10 @@ object EnvelopeContentValidator {
|
||||
return Result.Invalid("[DataMessage] Timestamps don't match! envelope: ${envelope.clientTimestamp}, content: ${dataMessage.timestamp}")
|
||||
}
|
||||
|
||||
if (dataMessage.body.isBodyTooLarge()) {
|
||||
return Result.Invalid("[DataMessage] Body exceeds ${SignalServiceMessageLimits.MAX_INLINE_BODY_SIZE_BYTES} bytes!")
|
||||
}
|
||||
|
||||
if (dataMessage.quote != null && ACI.parseOrNull(dataMessage.quote.authorAci, dataMessage.quote.authorAciBinary).isNullOrInvalidServiceId()) {
|
||||
return Result.Invalid("[DataMessage] Invalid ACI on quote!")
|
||||
}
|
||||
@@ -435,6 +440,10 @@ object EnvelopeContentValidator {
|
||||
return Result.Invalid("[EditMessage] Timestamps don't match! envelope: ${envelope.clientTimestamp}, content: ${dataMessage.timestamp}")
|
||||
}
|
||||
|
||||
if (dataMessage.body.isBodyTooLarge()) {
|
||||
return Result.Invalid("[EditMessage] Body exceeds ${SignalServiceMessageLimits.MAX_INLINE_BODY_SIZE_BYTES} bytes!")
|
||||
}
|
||||
|
||||
if (dataMessage.requiredProtocolVersion != null && dataMessage.requiredProtocolVersion > DataMessage.ProtocolVersion.CURRENT.value) {
|
||||
return Result.UnsupportedDataMessage(
|
||||
ourVersion = DataMessage.ProtocolVersion.CURRENT.value,
|
||||
@@ -484,6 +493,10 @@ object EnvelopeContentValidator {
|
||||
return this.attachments.any { it.contentType == LONG_TEXT_CONTENT_TYPE }
|
||||
}
|
||||
|
||||
private fun String?.isBodyTooLarge(): Boolean {
|
||||
return this != null && this.utf8Size() > SignalServiceMessageLimits.MAX_INLINE_BODY_SIZE_BYTES
|
||||
}
|
||||
|
||||
private fun BodyRange.isStyleRangeMissingOffsets(): Boolean {
|
||||
return this.style != null && (this.start == null || this.length == null)
|
||||
}
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package org.whispersystems.signalservice.api.messages
|
||||
|
||||
import org.signal.core.util.kibiBytes
|
||||
|
||||
/**
|
||||
* Size limits that messages must respect on the wire. Shared by the send path, the receive-side
|
||||
* content validation, and the app layer so that all three agree on a single set of numbers.
|
||||
*/
|
||||
object SignalServiceMessageLimits {
|
||||
/** The maximum size of an inlined text body we'll allow in a proto. Anything larger than this will need to be a long-text attachment. */
|
||||
@JvmField
|
||||
val MAX_INLINE_BODY_SIZE_BYTES: Int = 2.kibiBytes.bytes.toInt()
|
||||
}
|
||||
+2
-2
@@ -56,10 +56,10 @@ public class PaddingInputStream extends FilterInputStream {
|
||||
}
|
||||
|
||||
public static long getPaddedSize(long size) {
|
||||
return (int) Math.max(541, Math.floor(Math.pow(1.05, Math.ceil(Math.log(size) / Math.log(1.05)))));
|
||||
return (long) Math.max(541, Math.floor(Math.pow(1.05, Math.ceil(Math.log(size) / Math.log(1.05)))));
|
||||
}
|
||||
|
||||
public static long getMaxUnpaddedSize(long maxPaddedSize) {
|
||||
return (int) Math.floor(Math.pow(1.05, Math.floor(Math.log(maxPaddedSize) / Math.log(1.05))));
|
||||
return (long) Math.floor(Math.pow(1.05, Math.floor(Math.log(maxPaddedSize) / Math.log(1.05))));
|
||||
}
|
||||
}
|
||||
|
||||
+75
@@ -839,6 +839,81 @@ class EnvelopeContentValidatorTest {
|
||||
assert(result is EnvelopeContentValidator.Result.Valid)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validate - ensure data message body of exactly 2048 bytes is marked valid`() {
|
||||
val content = Content(
|
||||
dataMessage = DataMessage(
|
||||
timestamp = 1234,
|
||||
body = "a".repeat(2048)
|
||||
)
|
||||
)
|
||||
|
||||
val result = EnvelopeContentValidator.validate(Envelope(clientTimestamp = 1234), content, SELF_ACI, CiphertextMessage.WHISPER_TYPE)
|
||||
assert(result is EnvelopeContentValidator.Result.Valid)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validate - ensure data message body over 2048 bytes is marked invalid`() {
|
||||
val content = Content(
|
||||
dataMessage = DataMessage(
|
||||
timestamp = 1234,
|
||||
body = "a".repeat(2049)
|
||||
)
|
||||
)
|
||||
|
||||
val result = EnvelopeContentValidator.validate(Envelope(clientTimestamp = 1234), content, SELF_ACI, CiphertextMessage.WHISPER_TYPE)
|
||||
assert(result is EnvelopeContentValidator.Result.Invalid)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validate - ensure data message body over 2048 UTF-8 bytes is marked invalid`() {
|
||||
val content = Content(
|
||||
dataMessage = DataMessage(
|
||||
timestamp = 1234,
|
||||
body = "é".repeat(1025)
|
||||
)
|
||||
)
|
||||
|
||||
val result = EnvelopeContentValidator.validate(Envelope(clientTimestamp = 1234), content, SELF_ACI, CiphertextMessage.WHISPER_TYPE)
|
||||
assert(result is EnvelopeContentValidator.Result.Invalid)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validate - ensure edit message body over 2048 bytes is marked invalid`() {
|
||||
val content = Content(
|
||||
editMessage = EditMessage(
|
||||
targetSentTimestamp = 1000,
|
||||
dataMessage = DataMessage(
|
||||
timestamp = 1234,
|
||||
body = "a".repeat(2049)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val result = EnvelopeContentValidator.validate(Envelope(clientTimestamp = 1234), content, SELF_ACI, CiphertextMessage.WHISPER_TYPE)
|
||||
assert(result is EnvelopeContentValidator.Result.Invalid)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validate - ensure sync sent body over 2048 bytes is marked invalid`() {
|
||||
val envelope = Envelope(sourceServiceId = SELF_ACI.toString(), clientTimestamp = 1234)
|
||||
val content = Content(
|
||||
syncMessage = SyncMessage(
|
||||
sent = SyncMessage.Sent(
|
||||
timestamp = 1234,
|
||||
destinationServiceId = OTHER_ACI.toString(),
|
||||
message = DataMessage(
|
||||
timestamp = 1234,
|
||||
body = "a".repeat(2049)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val result = EnvelopeContentValidator.validate(envelope, content, SELF_ACI, CiphertextMessage.WHISPER_TYPE)
|
||||
assert(result is EnvelopeContentValidator.Result.Invalid)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validate - ensure sync sent without a timestamp is marked invalid`() {
|
||||
val envelope = Envelope(sourceServiceId = SELF_ACI.toString(), clientTimestamp = 1234)
|
||||
|
||||
+31
@@ -6,6 +6,9 @@ package org.whispersystems.signalservice.internal.crypto
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isGreaterThan
|
||||
import assertk.assertions.isGreaterThanOrEqualTo
|
||||
import assertk.assertions.isLessThanOrEqualTo
|
||||
import org.junit.Test
|
||||
import org.signal.core.util.StreamUtil
|
||||
import java.io.ByteArrayInputStream
|
||||
@@ -37,4 +40,32 @@ class PaddingInputStreamTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sizes above [Int.MAX_VALUE] must not be truncated. A padded size smaller than the input implies a *negative* amount of padding, which blows up
|
||||
* callers that use the difference to size a buffer.
|
||||
*/
|
||||
@Test
|
||||
fun `getPaddedSize does not truncate sizes above Int MAX_VALUE`() {
|
||||
val sizes = listOf(
|
||||
Int.MAX_VALUE.toLong() - 1,
|
||||
Int.MAX_VALUE.toLong(),
|
||||
Int.MAX_VALUE.toLong() + 1,
|
||||
3L * 1024 * 1024 * 1024,
|
||||
100L * 1024 * 1024 * 1024
|
||||
)
|
||||
|
||||
sizes.forEach { size ->
|
||||
assertThat(PaddingInputStream.getPaddedSize(size), "padded size of $size").isGreaterThanOrEqualTo(size)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getMaxUnpaddedSize does not truncate sizes above Int MAX_VALUE`() {
|
||||
val maxPaddedSize = 100L * 1024 * 1024 * 1024
|
||||
val maxUnpaddedSize = PaddingInputStream.getMaxUnpaddedSize(maxPaddedSize)
|
||||
|
||||
assertThat(maxUnpaddedSize).isGreaterThan(Int.MAX_VALUE.toLong())
|
||||
assertThat(PaddingInputStream.getPaddedSize(maxUnpaddedSize)).isLessThanOrEqualTo(maxPaddedSize)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user