Sign myTeamSpeak identity into clientinit

Adds an authenticator hook that appends the myTeamSpeak parameters to
clientinit/updatemytsid, and the per-connection challenge they sign.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-10 20:04:55 +00:00
co-authored by Claude Opus 5.5
parent 1430782e2a
commit 0549b6bf02
3 changed files with 25 additions and 1 deletions
@@ -355,7 +355,8 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
) )
); );
Ts3Debugging.debug(clientinit.build()); getClient().addMyTeamSpeakParameters(clientinit);
Ts3Debugging.debug("Sending clientinit");
getClient().writePacket(new PacketBody2Command(ProtocolRole.CLIENT, clientinit)); getClient().writePacket(new PacketBody2Command(ProtocolRole.CLIENT, clientinit));
} }
@@ -32,6 +32,12 @@ public class PacketTransformation {
return fakeMac; return fakeMac;
} }
/** Connection binding used by myTeamSpeak, in the AES-128/SHA-256 protocol mode. */
public byte[] getMyTeamSpeakChallenge() {
return Ts3Crypt.hash256(ByteBuffer.allocate(6 + ivStruct.length)
.put(new byte[] {0x30, 8, 0, 0, 0, 0}).put(ivStruct).array());
}
public Pair<byte[], byte[]> computeParameters(PacketHeader header) { public Pair<byte[], byte[]> computeParameters(PacketHeader header) {
ByteBuffer temporaryByteBuffer = ByteBuffer ByteBuffer temporaryByteBuffer = ByteBuffer
.allocate(ivStruct.length == 20 ? 26 : 70) .allocate(ivStruct.length == 20 ? 26 : 70)
@@ -3,6 +3,7 @@ package com.github.manevolent.ts3j.protocol.socket.client;
import com.github.manevolent.ts3j.command.CommandException; import com.github.manevolent.ts3j.command.CommandException;
import com.github.manevolent.ts3j.command.*; import com.github.manevolent.ts3j.command.*;
import com.github.manevolent.ts3j.command.parameter.CommandSingleParameter;
import com.github.manevolent.ts3j.identity.Identity; import com.github.manevolent.ts3j.identity.Identity;
import com.github.manevolent.ts3j.protocol.NetworkPacket; import com.github.manevolent.ts3j.protocol.NetworkPacket;
import com.github.manevolent.ts3j.protocol.Packet; import com.github.manevolent.ts3j.protocol.Packet;
@@ -36,6 +37,7 @@ import java.nio.*;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function;
/** /**
* Server/client common abstract client socket, used to interact with the UDP connection. Provides most of the protocol * Server/client common abstract client socket, used to interact with the UDP connection. Provides most of the protocol
@@ -165,6 +167,21 @@ public abstract class AbstractTeamspeakClientSocket
setTransformation(new PacketTransformation(parameters.getIvStruct(), parameters.getFakeSignature())); setTransformation(new PacketTransformation(parameters.getIvStruct(), parameters.getFakeSignature()));
} }
private volatile Function<byte[], Map<String, String>> myTeamSpeakAuthenticator;
/** Set before connecting; invoked after the handshake with this connection's challenge. */
public void setMyTeamSpeakAuthenticator(Function<byte[], Map<String, String>> authenticator) {
myTeamSpeakAuthenticator = authenticator;
}
public void addMyTeamSpeakParameters(Command command) {
Function<byte[], Map<String, String>> authenticator = myTeamSpeakAuthenticator;
if (authenticator == null) return;
Map<String, String> parameters = authenticator.apply(transformation.getMyTeamSpeakChallenge());
for (Map.Entry<String, String> entry : parameters.entrySet())
command.add(new CommandSingleParameter(entry.getKey(), entry.getValue()));
}
protected abstract NetworkPacket readNetworkPacket(int timeout) throws IOException; protected abstract NetworkPacket readNetworkPacket(int timeout) throws IOException;
protected abstract void writeNetworkPacket(NetworkPacket packet) throws IOException; protected abstract void writeNetworkPacket(NetworkPacket packet) throws IOException;