Skip to content

Commit

Permalink
limit size of multi-recipient messages
Browse files Browse the repository at this point in the history
  • Loading branch information
jkt-signal authored Jan 12, 2024
1 parent bf39be3 commit 394f992
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 12 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@
<dependency>
<groupId>org.signal</groupId>
<artifactId>libsignal-server</artifactId>
<version>0.37.0</version>
<version>0.39.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ public SealedSenderMultiRecipientMessage readFrom(Class<SealedSenderMultiRecipie
}

try {
return SealedSenderMultiRecipientMessage.parse(fullMessage);
final SealedSenderMultiRecipientMessage message = SealedSenderMultiRecipientMessage.parse(fullMessage);
if (message.getRecipients().values().stream().anyMatch(r -> message.messageSizeForRecipient(r) > MAX_MESSAGE_SIZE)) {
throw new BadRequestException("message payload too large");
}
return message;
} catch (InvalidMessageException | InvalidVersionException e) {
throw new BadRequestException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -994,11 +994,15 @@ private static void writeMultiPayloadExcludedRecipient(final ByteBuffer bb, fina
bb.put((byte) 0);
}

private static InputStream initializeMultiPayload(List<Recipient> recipients, byte[] buffer, final boolean explicitIdentifiers) {
return initializeMultiPayload(recipients, List.of(), buffer, explicitIdentifiers);
private static InputStream initializeMultiPayload(final List<Recipient> recipients, final byte[] buffer, final boolean explicitIdentifiers) {
return initializeMultiPayload(recipients, List.of(), buffer, explicitIdentifiers, 39);
}

private static InputStream initializeMultiPayload(List<Recipient> recipients, List<ServiceIdentifier> excludedRecipients, byte[] buffer, final boolean explicitIdentifiers) {
private static InputStream initializeMultiPayload(final List<Recipient> recipients, final List<ServiceIdentifier> excludedRecipients, final byte[] buffer, final boolean explicitIdentifiers) {
return initializeMultiPayload(recipients, excludedRecipients, buffer, explicitIdentifiers, 39);
}

private static InputStream initializeMultiPayload(final List<Recipient> recipients, final List<ServiceIdentifier> excludedRecipients, final byte[] buffer, final boolean explicitIdentifiers, final int payloadSize) {
// initialize a binary payload according to our wire format
ByteBuffer bb = ByteBuffer.wrap(buffer);
bb.order(ByteOrder.BIG_ENDIAN);
Expand All @@ -1007,23 +1011,28 @@ private static InputStream initializeMultiPayload(List<Recipient> recipients, Li
bb.put(explicitIdentifiers ? (byte) 0x23 : (byte) 0x22); // version byte

// count varint
int nRecip = recipients.size() + excludedRecipients.size();
while (nRecip > 127) {
bb.put((byte) (nRecip & 0x7F | 0x80));
nRecip = nRecip >> 7;
}
bb.put((byte)(nRecip & 0x7F));
writeVarint(bb, recipients.size() + excludedRecipients.size());

recipients.forEach(recipient -> writeMultiPayloadRecipient(bb, recipient, explicitIdentifiers));
excludedRecipients.forEach(recipient -> writeMultiPayloadExcludedRecipient(bb, recipient, explicitIdentifiers));

// now write the actual message body (empty for now)
bb.put(new byte[39]); // payload (variable but >= 32, 39 bytes here)
assert(payloadSize >= 32);
writeVarint(bb, payloadSize);
bb.put(new byte[payloadSize]);

// return the input stream
return new ByteArrayInputStream(buffer, 0, bb.position());
}

private static void writeVarint(ByteBuffer bb, long n) {
while (n >= 0x80) {
bb.put ((byte) (n & 0x7F | 0x80));
n = n >> 7;
}
bb.put((byte) (n & 0x7F));
}

@Test
void testManyRecipientMessage() throws Exception {
final int nRecipients = 999;
Expand Down Expand Up @@ -1387,6 +1396,27 @@ void testMultiRecipientRedisBombProtection(final boolean useExplicitIdentifier)
checkBadMultiRecipientResponse(response, 400);
}

@ParameterizedTest
@ValueSource(booleans = {true, false})
void testMultiRecipientSizeLimit() throws Exception {
final List<Recipient> recipients = List.of(
new Recipient(SINGLE_DEVICE_ACI_ID, SINGLE_DEVICE_ID1, SINGLE_DEVICE_REG_ID1, new byte[48]));

Response response = resources
.getJerseyTest()
.target("/v1/messages/multi_recipient")
.queryParam("online", true)
.queryParam("ts", 1663798405641L)
.queryParam("story", false)
.queryParam("urgent", false)
.request()
.header(HttpHeaders.USER_AGENT, "cluck cluck, i'm a parrot")
.header(HeaderUtils.UNIDENTIFIED_ACCESS_KEY, Base64.getEncoder().encodeToString(UNIDENTIFIED_ACCESS_BYTES))
.put(Entity.entity(initializeMultiPayload(recipients, List.of(), new byte[257<<10], true, 256<<10), MultiRecipientMessageProvider.MEDIA_TYPE));

checkBadMultiRecipientResponse(response, 400);
}

@Test
void testSendStoryToUnknownAccount() throws Exception {
String accessBytes = Base64.getEncoder().encodeToString(UNIDENTIFIED_ACCESS_BYTES);
Expand Down

0 comments on commit 394f992

Please sign in to comment.