Cap the number of incrementalMacs populated in an envelope.

Add a remote config `global.maxIncrementalMacsPerEnvelope` (client
fallback of 10) that limits how many attachment pointers in a single
envelope can have their incrementalMac field populated. Each
incrementalMac can be up to 8 KiB, so having too many risks exceeding
the 96 KiB envelope size threshold. Excess attachment pointers have
their incrementalMac and chunkSize fields stripped.
This commit is contained in:
Greyson Parrelli
2026-03-03 19:41:55 +00:00
committed by jeffrey-signal
parent 6eee4db87b
commit 2bd440e07c
3 changed files with 65 additions and 17 deletions

View File

@@ -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);

View File

@@ -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")

View File

@@ -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> 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<AttachmentPointer> capIncrementalMacs(List<AttachmentPointer> 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<AttachmentPointer> 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) {