diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java index 5d7b37ac49..7fc444c323 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java @@ -19,8 +19,8 @@ import org.thoughtcrime.securesms.database.AttachmentTable; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.events.PartProgressEvent; -import org.thoughtcrime.securesms.jobmanager.JsonJobData; import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.JsonJobData; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.net.NotPushRegisteredException; @@ -29,7 +29,7 @@ import org.thoughtcrime.securesms.service.NotificationController; import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.MediaUtil; import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.crypto.AttachmentCipherOutputStream; +import org.whispersystems.signalservice.api.crypto.AttachmentCipherStreamUtil; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; @@ -69,7 +69,7 @@ public final class AttachmentUploadJob extends BaseJob { public static long getMaxPlaintextSize() { long maxCipherTextSize = FeatureFlags.maxAttachmentSizeBytes(); - long maxPaddedSize = AttachmentCipherOutputStream.getPlaintextLength(maxCipherTextSize); + long maxPaddedSize = AttachmentCipherStreamUtil.getPlaintextLength(maxCipherTextSize); return PaddingInputStream.getMaxUnpaddedSize(maxPaddedSize); } diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java index 9cf9699e14..ab940fd859 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java @@ -23,6 +23,7 @@ import org.signal.libsignal.protocol.state.PreKeyBundle; import org.signal.libsignal.protocol.util.Pair; import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations; import org.whispersystems.signalservice.api.crypto.AttachmentCipherOutputStream; +import org.whispersystems.signalservice.api.crypto.AttachmentCipherStreamUtil; import org.whispersystems.signalservice.api.crypto.ContentHint; import org.whispersystems.signalservice.api.crypto.EnvelopeContent; import org.whispersystems.signalservice.api.crypto.SignalGroupSessionBuilder; @@ -786,7 +787,7 @@ public class SignalServiceMessageSender { byte[] attachmentIV = attachment.getResumableUploadSpec().map(ResumableUploadSpec::getIV).orElseGet(() -> Util.getSecretBytes(16)); long paddedLength = PaddingInputStream.getPaddedSize(attachment.getLength()); InputStream dataStream = new PaddingInputStream(attachment.getInputStream(), attachment.getLength()); - long ciphertextLength = AttachmentCipherOutputStream.getCiphertextLength(paddedLength); + long ciphertextLength = AttachmentCipherStreamUtil.getCiphertextLength(paddedLength); PushAttachmentData attachmentData = new PushAttachmentData(attachment.getContentType(), dataStream, ciphertextLength, 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 602a3548ca..7b559542f7 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 @@ -11,6 +11,8 @@ 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.HKDFv3; +import org.signal.libsignal.protocol.logging.Log; +import org.whispersystems.signalservice.internal.crypto.PaddingInputStream; import org.whispersystems.signalservice.internal.util.ContentLengthInputStream; import org.whispersystems.signalservice.internal.util.Util; @@ -78,10 +80,12 @@ public class AttachmentCipherInputStream extends FilterInputStream { } wrappedStream = new FileInputStream(file); } else { + final int dataSize = Math.toIntExact(AttachmentCipherStreamUtil.getCiphertextLength(PaddingInputStream.getPaddedSize(plaintextLength))); + final ChunkSizeChoice sizeChoice = ChunkSizeChoice.inferChunkSize(dataSize); wrappedStream = new IncrementalMacInputStream( new FileInputStream(file), parts[1], - ChunkSizeChoice.inferChunkSize(Math.toIntExact(plaintextLength)), + sizeChoice, incrementalDigest); } InputStream inputStream = new AttachmentCipherInputStream(wrappedStream, parts[0], file.length() - BLOCK_SIZE - mac.getMacLength()); diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherOutputStream.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherOutputStream.java index a352369290..da579aeae1 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherOutputStream.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherOutputStream.java @@ -88,14 +88,6 @@ public class AttachmentCipherOutputStream extends DigestingOutputStream { } } - public static long getCiphertextLength(long plaintextLength) { - return 16 + (((plaintextLength / 16) + 1) * 16) + 32; - } - - public static long getPlaintextLength(long ciphertextLength) { - return (((ciphertextLength - 16 - 32) / 16) - 1) * 16; - } - private Mac initializeMac() { try { return Mac.getInstance("HmacSHA256"); diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherStreamUtil.kt b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherStreamUtil.kt new file mode 100644 index 0000000000..bd4050f0b1 --- /dev/null +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherStreamUtil.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.crypto + +class AttachmentCipherStreamUtil { + companion object { + @JvmStatic + fun getCiphertextLength(plaintextLength: Long): Long { + return 16 + (plaintextLength / 16 + 1) * 16 + 32 + } + + @JvmStatic + fun getPlaintextLength(ciphertextLength: Long): Long { + return ((ciphertextLength - 16 - 32) / 16 - 1) * 16 + } + } +} diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/http/AttachmentCipherOutputStreamFactory.kt b/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/http/AttachmentCipherOutputStreamFactory.kt index 1fb68054e0..a86a91926a 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/http/AttachmentCipherOutputStreamFactory.kt +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/http/AttachmentCipherOutputStreamFactory.kt @@ -33,8 +33,8 @@ class AttachmentCipherOutputStreamFactory(private val key: ByteArray, private va } val privateKey = key.sliceArray(AES_KEY_LENGTH until key.size) - val chunkSizeChoice = ChunkSizeChoice.inferChunkSize(length.toInt()) - val incrementalStream = IncrementalMacOutputStream(wrap, privateKey, chunkSizeChoice, incrementalDigestOut) + val sizeChoice = ChunkSizeChoice.inferChunkSize(length.toInt()) + val incrementalStream = IncrementalMacOutputStream(wrap, privateKey, sizeChoice, incrementalDigestOut) return createFor(incrementalStream) } } diff --git a/libsignal/service/src/test/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherTest.java b/libsignal/service/src/test/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherTest.java index 7460ceca54..a1b018d9c6 100644 --- a/libsignal/service/src/test/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherTest.java +++ b/libsignal/service/src/test/java/org/whispersystems/signalservice/api/crypto/AttachmentCipherTest.java @@ -35,7 +35,7 @@ public final class AttachmentCipherTest { public void attachment_encryptDecrypt() throws IOException, InvalidMessageException { byte[] key = Util.getSecretBytes(64); byte[] plaintextInput = "Peter Parker".getBytes(); - EncryptResult encryptResult = encryptData(plaintextInput, key, true); + EncryptResult encryptResult = encryptData(plaintextInput, key, false); File cipherFile = writeToFile(encryptResult.ciphertext); InputStream inputStream = AttachmentCipherInputStream.createForAttachment(cipherFile, plaintextInput.length, key, encryptResult.digest, encryptResult.incrementalDigest); byte[] plaintextOutput = readInputStreamFully(inputStream); @@ -158,8 +158,7 @@ public final class AttachmentCipherTest { ByteArrayInputStream inputStream = new ByteArrayInputStream(plaintextInput); InputStream paddedInputStream = new PaddingInputStream(inputStream, length); ByteArrayOutputStream destinationOutputStream = new ByteArrayOutputStream(); - ByteArrayOutputStream incrementalDigestOutputStream = new ByteArrayOutputStream(); - DigestingOutputStream encryptingOutputStream = new AttachmentCipherOutputStreamFactory(key, iv).createIncrementalFor(destinationOutputStream, length, incrementalDigestOutputStream); + DigestingOutputStream encryptingOutputStream = new AttachmentCipherOutputStreamFactory(key, iv).createFor(destinationOutputStream); Util.copy(paddedInputStream, encryptingOutputStream); @@ -168,11 +167,10 @@ public final class AttachmentCipherTest { byte[] encryptedData = destinationOutputStream.toByteArray(); byte[] digest = encryptingOutputStream.getTransmittedDigest(); - byte[] incrementalDigest = incrementalDigestOutputStream.toByteArray(); File cipherFile = writeToFile(encryptedData); - InputStream decryptedStream = AttachmentCipherInputStream.createForAttachment(cipherFile, length, key, digest, incrementalDigest); + InputStream decryptedStream = AttachmentCipherInputStream.createForAttachment(cipherFile, length, key, digest, null); byte[] plaintextOutput = readInputStreamFully(decryptedStream); assertArrayEquals(plaintextInput, plaintextOutput); diff --git a/libsignal/service/src/test/java/org/whispersystems/signalservice/internal/push/http/DigestingRequestBodyTest.java b/libsignal/service/src/test/java/org/whispersystems/signalservice/internal/push/http/DigestingRequestBodyTest.java index 30d8e3a335..edd5aa3064 100644 --- a/libsignal/service/src/test/java/org/whispersystems/signalservice/internal/push/http/DigestingRequestBodyTest.java +++ b/libsignal/service/src/test/java/org/whispersystems/signalservice/internal/push/http/DigestingRequestBodyTest.java @@ -2,6 +2,7 @@ package org.whispersystems.signalservice.internal.push.http; import org.junit.Test; import org.whispersystems.signalservice.api.crypto.AttachmentCipherOutputStream; +import org.whispersystems.signalservice.api.crypto.AttachmentCipherStreamUtil; import org.whispersystems.signalservice.internal.util.Util; import java.io.ByteArrayInputStream; @@ -14,7 +15,7 @@ import static org.junit.Assert.assertEquals; public class DigestingRequestBodyTest { private static int CONTENT_LENGTH = 70000; - private static int TOTAL_LENGTH = (int) AttachmentCipherOutputStream.getCiphertextLength(CONTENT_LENGTH); + private static int TOTAL_LENGTH = (int) AttachmentCipherStreamUtil.getCiphertextLength(CONTENT_LENGTH); private final byte[] attachmentKey = Util.getSecretBytes(64); private final byte[] attachmentIV = Util.getSecretBytes(16);