Skip to content

Commit 3a58272

Browse files
committed
Throw error for oversized inbound noise messages
1 parent 3d96d73 commit 3a58272

File tree

5 files changed

+36
-0
lines changed

5 files changed

+36
-0
lines changed

service/src/main/java/org/whispersystems/textsecuregcm/grpc/net/ErrorHandler.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public void exceptionCaught(final ChannelHandlerContext context, final Throwable
4343
case NoiseHandshakeException e -> ApplicationWebSocketCloseReason.NOISE_HANDSHAKE_ERROR.toWebSocketCloseStatus(e.getMessage());
4444
case ClientAuthenticationException ignored -> ApplicationWebSocketCloseReason.CLIENT_AUTHENTICATION_ERROR.toWebSocketCloseStatus("Not authenticated");
4545
case BadPaddingException ignored -> ApplicationWebSocketCloseReason.NOISE_ENCRYPTION_ERROR.toWebSocketCloseStatus("Noise encryption error");
46+
case NoiseException ignored -> ApplicationWebSocketCloseReason.NOISE_ENCRYPTION_ERROR.toWebSocketCloseStatus("Noise encryption error");
4647
default -> {
4748
log.warn("An unexpected exception reached the end of the pipeline", cause);
4849
yield WebSocketCloseStatus.INTERNAL_SERVER_ERROR;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.whispersystems.textsecuregcm.grpc.net;
2+
3+
/**
4+
* Indicates that some problem occurred while processing an encrypted noise message (e.g. an unexpected message size/
5+
* format or a general encryption error).
6+
*/
7+
class NoiseException extends Exception {
8+
public NoiseException(final String message) {
9+
super(message);
10+
}
11+
}

service/src/main/java/org/whispersystems/textsecuregcm/grpc/net/NoiseHandler.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ abstract CompletableFuture<HandshakeResult> handleHandshakePayload(
8383
public void channelRead(final ChannelHandlerContext context, final Object message) throws Exception {
8484
try {
8585
if (message instanceof BinaryWebSocketFrame frame) {
86+
if (frame.content().readableBytes() > Noise.MAX_PACKET_LEN) {
87+
final String error = "Invalid noise message length " + frame.content().readableBytes();
88+
throw state == State.HANDSHAKE ? new NoiseHandshakeException(error) : new NoiseException(error);
89+
}
8690
// We've read this frame off the wire, and so it's most likely a direct buffer that's not backed by an array.
8791
// We'll need to copy it to a heap buffer.
8892
handleInboundMessage(context, ByteBufUtil.getBytes(frame.content()));

service/src/test/java/org/whispersystems/textsecuregcm/grpc/net/AbstractNoiseHandlerTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
77
import static org.junit.jupiter.api.Assertions.assertNotNull;
88
import static org.junit.jupiter.api.Assertions.assertNull;
9+
import static org.junit.jupiter.api.Assertions.assertThrows;
910
import static org.junit.jupiter.api.Assertions.assertTrue;
1011

1112
import com.southernstorm.noise.protocol.CipherStatePair;
@@ -284,4 +285,11 @@ void writeHugeOutboundMessage(final int plaintextLength) throws Throwable {
284285

285286
}
286287

288+
@Test
289+
public void writeHugeInboundMessage() throws Throwable {
290+
doHandshake();
291+
final byte[] big = TestRandomUtil.nextBytes(Noise.MAX_PACKET_LEN + 1);
292+
embeddedChannel.pipeline().fireChannelRead(new BinaryWebSocketFrame(Unpooled.wrappedBuffer(big)));
293+
assertThrows(NoiseException.class, embeddedChannel::checkException);
294+
}
287295
}

service/src/test/java/org/whispersystems/textsecuregcm/grpc/net/NoiseAuthenticatedHandlerTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import com.southernstorm.noise.protocol.CipherStatePair;
1616
import com.southernstorm.noise.protocol.HandshakeState;
17+
import com.southernstorm.noise.protocol.Noise;
1718
import io.netty.buffer.Unpooled;
1819
import io.netty.channel.ChannelFuture;
1920
import io.netty.channel.embedded.EmbeddedChannel;
@@ -35,6 +36,7 @@
3536
import org.whispersystems.textsecuregcm.auth.grpc.AuthenticatedDevice;
3637
import org.whispersystems.textsecuregcm.storage.ClientPublicKeysManager;
3738
import org.whispersystems.textsecuregcm.storage.Device;
39+
import org.whispersystems.textsecuregcm.util.TestRandomUtil;
3840
import org.whispersystems.textsecuregcm.util.UUIDUtil;
3941

4042
class NoiseAuthenticatedHandlerTest extends AbstractNoiseHandlerTest {
@@ -219,6 +221,16 @@ void handleInvalidExtraWrites() throws NoSuchAlgorithmException, ShortBufferExce
219221
assertNull(embeddedChannel.outboundMessages().poll());
220222
}
221223

224+
@Test
225+
public void handleOversizeHandshakeMessage() {
226+
final EmbeddedChannel embeddedChannel = getEmbeddedChannel();
227+
final byte[] big = TestRandomUtil.nextBytes(Noise.MAX_PACKET_LEN + 1);
228+
ByteBuffer.wrap(big)
229+
.put(UUIDUtil.toBytes(UUID.randomUUID()))
230+
.put((byte) 0x01);
231+
assertThrows(NoiseHandshakeException.class, () -> doHandshake(big));
232+
}
233+
222234
private HandshakeState clientHandshakeState() throws NoSuchAlgorithmException {
223235
final HandshakeState clientHandshakeState =
224236
new HandshakeState(HandshakePattern.IK.protocol(), HandshakeState.INITIATOR);

0 commit comments

Comments
 (0)