diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnecting.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnecting.java index f8cfd53..b7a30a3 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnecting.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnecting.java @@ -204,6 +204,7 @@ public class LocalClientHandlerConnecting extends LocalClientHandler { byte[] alpha = Base64.getDecoder().decode(command.get("alpha").getValue()); // alpha byte[] beta = Base64.getDecoder().decode(command.get("beta").getValue()); // beta byte[] omega = Base64.getDecoder().decode(command.get("omega").getValue()); // omega + getClient().setServerPublicKey(command.get("omega").getValue()); getClient().setSecureParameters(Ts3Crypt.cryptoInit(alpha, beta, omega, getClient().getIdentity())); @@ -224,6 +225,7 @@ public class LocalClientHandlerConnecting extends LocalClientHandler { byte[] beta = Base64.getDecoder().decode(command.get("beta").getValue()); // beta byte[] omega = Base64.getDecoder().decode(command.get("omega").getValue()); // omega + getClient().setServerPublicKey(command.get("omega").getValue()); byte[] proof = Base64.getDecoder().decode(command.get("proof").getValue()); Pair keyPair = Ts3Crypt.generateKeypair(); diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/LocalTeamspeakClientSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/LocalTeamspeakClientSocket.java index 2b18bd3..f6bbd86 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/LocalTeamspeakClientSocket.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/LocalTeamspeakClientSocket.java @@ -32,6 +32,9 @@ import java.io.IOException; import java.net.*; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.*; import java.util.concurrent.*; import java.util.function.Function; @@ -59,6 +62,9 @@ public class LocalTeamspeakClientSocket /** The virtual server's properties as announced by initserver. */ private volatile Map serverProperties = Collections.emptyMap(); + /** The public key the server identified itself with in the handshake, base64 as sent. */ + private volatile String serverPublicKey; + /** * The internal command buffer *

@@ -325,6 +331,7 @@ public class LocalTeamspeakClientSocket serverId = null; serverProperties = Collections.emptyMap(); + serverPublicKey = null; remote = null; } @@ -399,6 +406,7 @@ public class LocalTeamspeakClientSocket // Free stuff serverId = null; serverProperties = Collections.emptyMap(); + serverPublicKey = null; synchronized (commandSendLock) { @@ -840,6 +848,25 @@ public class LocalTeamspeakClientSocket return serverProperties; } + public void setServerPublicKey(String serverPublicKey) { + this.serverPublicKey = serverPublicKey; + } + + /** + * The virtual server's unique identifier, or null before the handshake. It is derived from + * the server's public key as a client's is from its own; the server tells a client no other + * way, as guests may not read it with serverinfo. + */ + public Uid getServerUid() { + String key = serverPublicKey; + if (key == null) return null; + try { + return new Uid(MessageDigest.getInstance("SHA-1").digest(key.getBytes(StandardCharsets.US_ASCII))); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(e); + } + } + /** The virtual server's name, or null if it was not announced. */ public String getServerName() { return serverProperties.get("virtualserver_name"); diff --git a/src/test/java/com/github/manevolent/ts3j/ServerUidTest.java b/src/test/java/com/github/manevolent/ts3j/ServerUidTest.java new file mode 100644 index 0000000..686b6b8 --- /dev/null +++ b/src/test/java/com/github/manevolent/ts3j/ServerUidTest.java @@ -0,0 +1,15 @@ +package com.github.manevolent.ts3j; + +import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket; +import junit.framework.TestCase; + +public class ServerUidTest extends TestCase { + /** A real server's handshake key, and the unique id the official client files its chat logs under. */ + public void testDerivedFromTheHandshakeKey() { + LocalTeamspeakClientSocket client = new LocalTeamspeakClientSocket(); + assertNull(client.getServerUid()); + + client.setServerPublicKey("MEwDAgcAAgEgAiEAtg1UQ4UpfiyTjrYPQcWV7Pa04/HTWr/viRhqEOg7+MQCIH/FqUKJDpBJDn/v01KOR+snSWlcoxOsfb2R8C02T3/z"); + assertEquals("+Tyg2JtxE8vRNZp+JiUBnmBh0MY=", client.getServerUid().toBase64()); + } +}