diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java index fcc43840e2..e74b62a24a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java @@ -176,6 +176,7 @@ public class ApplicationDependencyProvider implements AppDependencies.Provider { Optional.of(new SecurityEventListener(context)), SignalExecutors.newCachedBoundedExecutor("signal-messages", ThreadUtil.PRIORITY_IMPORTANT_BACKGROUND_THREAD, 1, 16, 30), RemoteConfig.maxEnvelopeSizeBytes(), + RemoteConfig.maxIncrementalMacsPerEnvelope(), RemoteConfig::useMessageSendRestFallback, RemoteConfig.useBinaryId(), BuildConfig.USE_STRING_ID); diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt b/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt index 7b222bb306..523c4ccbdc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt @@ -1182,6 +1182,15 @@ object RemoteConfig { hotSwappable = true ) + /** The maximum number of attachment pointers that can have incrementalMac populated in a single envelope. */ + @JvmStatic + @get:JvmName("maxIncrementalMacsPerEnvelope") + val maxIncrementalMacsPerEnvelope: Int by remoteInt( + key = "global.maxIncrementalMacsPerEnvelope", + defaultValue = 10, + hotSwappable = true + ) + /** Whether or not to send over binary service ids (alongside string service ids). */ @JvmStatic @get:JvmName("useBinaryId") diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java index 3fe0368366..b5924b3d47 100644 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java @@ -184,6 +184,7 @@ public class SignalServiceMessageSender { private final Scheduler scheduler; private final long maxEnvelopeSize; + private final int maxIncrementalMacsPerEnvelope; private final BooleanSupplier useRestFallback; private final boolean useBinaryId; private final boolean useStringId; @@ -197,28 +198,30 @@ public class SignalServiceMessageSender { Optional eventListener, ExecutorService executor, long maxEnvelopeSize, + int maxIncrementalMacsPerEnvelope, BooleanSupplier useRestFallback, boolean useBinaryId, boolean useStringId) { CredentialsProvider credentialsProvider = pushServiceSocket.getCredentialsProvider(); - this.socket = pushServiceSocket; - this.aciStore = store.aci(); - this.sessionLock = sessionLock; - this.localAddress = new SignalServiceAddress(credentialsProvider.getAci(), credentialsProvider.getE164()); - this.localDeviceId = credentialsProvider.getDeviceId(); - this.localPni = credentialsProvider.getPni(); - this.attachmentApi = attachmentApi; - this.messageApi = messageApi; - this.eventListener = eventListener; - this.maxEnvelopeSize = maxEnvelopeSize; - this.localPniIdentity = store.pni().getIdentityKeyPair(); - this.scheduler = Schedulers.from(executor, false, false); - this.keysApi = keysApi; - this.useRestFallback = useRestFallback; - this.useBinaryId = useBinaryId; - this.useStringId = useStringId; + this.socket = pushServiceSocket; + this.aciStore = store.aci(); + this.sessionLock = sessionLock; + this.localAddress = new SignalServiceAddress(credentialsProvider.getAci(), credentialsProvider.getE164()); + this.localDeviceId = credentialsProvider.getDeviceId(); + this.localPni = credentialsProvider.getPni(); + this.attachmentApi = attachmentApi; + this.messageApi = messageApi; + this.eventListener = eventListener; + this.maxEnvelopeSize = maxEnvelopeSize; + this.maxIncrementalMacsPerEnvelope = maxIncrementalMacsPerEnvelope; + this.localPniIdentity = store.pni().getIdentityKeyPair(); + this.scheduler = Schedulers.from(executor, false, false); + this.keysApi = keysApi; + this.useRestFallback = useRestFallback; + this.useBinaryId = useBinaryId; + this.useStringId = useStringId; } /** @@ -2691,7 +2694,42 @@ public class SignalServiceMessageSender { } } - return pointers; + return capIncrementalMacs(pointers); + } + + private List capIncrementalMacs(List pointers) { + if (maxIncrementalMacsPerEnvelope <= 0) { + return pointers; + } + + int incrementalMacCount = 0; + for (AttachmentPointer pointer : pointers) { + if (pointer.incrementalMac != null) { + incrementalMacCount++; + } + } + + if (incrementalMacCount <= maxIncrementalMacsPerEnvelope) { + return pointers; + } + + Log.w(TAG, "Envelope has " + incrementalMacCount + " incrementalMacs, which exceeds the limit of " + maxIncrementalMacsPerEnvelope + ". Stripping excess."); + + List result = new ArrayList<>(pointers.size()); + int kept = 0; + + for (AttachmentPointer pointer : pointers) { + if (pointer.incrementalMac != null && kept >= maxIncrementalMacsPerEnvelope) { + result.add(pointer.newBuilder().incrementalMac(null).chunkSize(null).build()); + } else { + if (pointer.incrementalMac != null) { + kept++; + } + result.add(pointer); + } + } + + return result; } private AttachmentPointer createAttachmentPointer(SignalServiceAttachmentPointer attachment) {