Fall back to the handshake key when a packet fails to decrypt
The server encrypts initivexpand2 (COMMAND 0 and 1) with the handshake key. If our ACK for one of those fragments is lost, its retransmit arrives after we switched to the session key, fails with "mac check in EAX failed", is never acknowledged and blocks the server's command stream until it drops us. Common on lossy links such as mobile data. Retry generation 0 packets that fail under the session key with the handshake key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+18
-7
@@ -75,7 +75,8 @@ public abstract class AbstractTeamspeakClientSocket
|
|||||||
|
|
||||||
private Identity identity = null;
|
private Identity identity = null;
|
||||||
private ClientConnectionState connectionState = null;
|
private ClientConnectionState connectionState = null;
|
||||||
private PacketTransformation transformation = new InitPacketTransformation();
|
private final PacketTransformation initTransformation = new InitPacketTransformation();
|
||||||
|
private PacketTransformation transformation = initTransformation;
|
||||||
private PacketHandler handler;
|
private PacketHandler handler;
|
||||||
|
|
||||||
private int clientId = 0;
|
private int clientId = 0;
|
||||||
@@ -543,15 +544,11 @@ public abstract class AbstractTeamspeakClientSocket
|
|||||||
+ " generation=" + networkPacket.getHeader().getGeneration());
|
+ " generation=" + networkPacket.getHeader().getGeneration());
|
||||||
|
|
||||||
PacketTransformation transformation = getTransformation();
|
PacketTransformation transformation = getTransformation();
|
||||||
|
int dataLen = networkPacket.getDatagram().getLength() - networkPacket.getHeader().getSize();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
packetBuffer = ByteBuffer.wrap(
|
packetBuffer = ByteBuffer.wrap(
|
||||||
transformation.decrypt(
|
decrypt(transformation, networkPacket.getHeader(), packetBuffer, dataLen)
|
||||||
networkPacket.getHeader(),
|
|
||||||
packetBuffer,
|
|
||||||
networkPacket.getDatagram().getLength() -
|
|
||||||
networkPacket.getHeader().getSize()
|
|
||||||
)
|
|
||||||
).order(ByteOrder.BIG_ENDIAN);
|
).order(ByteOrder.BIG_ENDIAN);
|
||||||
} catch (InvalidCipherTextException e) {
|
} catch (InvalidCipherTextException e) {
|
||||||
throw new IOException(
|
throw new IOException(
|
||||||
@@ -604,6 +601,20 @@ public abstract class AbstractTeamspeakClientSocket
|
|||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts with the session key, falling back to the handshake key: the server encrypts initivexpand2 with
|
||||||
|
* the latter, so its retransmits (sent when our ACK was lost) still arrive under it after the key switch.
|
||||||
|
*/
|
||||||
|
private byte[] decrypt(PacketTransformation transformation, PacketHeader header, ByteBuffer buffer, int dataLen)
|
||||||
|
throws InvalidCipherTextException {
|
||||||
|
try {
|
||||||
|
return transformation.decrypt(header, buffer, dataLen);
|
||||||
|
} catch (InvalidCipherTextException e) {
|
||||||
|
if (transformation instanceof InitPacketTransformation || header.getGeneration() != 0) throw e;
|
||||||
|
return initTransformation.decrypt(header, buffer, dataLen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Packet readPacket() throws
|
public Packet readPacket() throws
|
||||||
IOException,
|
IOException,
|
||||||
|
|||||||
Reference in New Issue
Block a user