Still not getting the last packet to work.

This commit is contained in:
Manevolent
2018-07-26 22:07:05 -04:00
parent a0009693e2
commit 5cdedba51c
21 changed files with 448 additions and 181 deletions
@@ -1065,6 +1065,6 @@ public class GroupOperations
FieldOperations.fe_mul(x, h.X, recip);
FieldOperations.fe_mul(y, h.Y, recip);
FieldOperations.fe_tobytes(s, offset, y);
s[offset + 31] ^= (byte) (FieldOperations.fe_isnegative(x) << 7);
//s[offset + 31] ^= (byte) (FieldOperations.fe_isnegative(x) << 7);
}
}
@@ -4,7 +4,6 @@ import com.github.manevolent.ts3j.command.part.CommandOption;
import com.github.manevolent.ts3j.command.part.CommandParameter;
import com.github.manevolent.ts3j.command.part.CommandSingleParameter;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
import com.github.manevolent.ts3j.util.Ts3Logging;
import com.github.manevolent.ts3j.util.Ts3String;
import java.util.*;
@@ -1,16 +1,10 @@
package com.github.manevolent.ts3j.identity;
import com.github.manevolent.ts3j.util.Ts3Crypt;
import com.github.manevolent.ts3j.util.Ts3Logging;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DERInteger;
import org.bouncycastle.asn1.DERSequence;
import com.github.manevolent.ts3j.util.Ts3Debugging;
import org.bouncycastle.math.ec.ECPoint;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.DigestException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
@@ -83,7 +77,7 @@ public class Identity {
if (curr > best)
{
Ts3Logging.debug("Improved identity security level: from " + best + " to " + curr);
Ts3Debugging.debug("Improved identity security level: from " + best + " to " + curr);
keyOffset = lastCheckedKeyOffset;
best = curr;
}
@@ -15,10 +15,6 @@ import java.math.BigInteger;
import java.security.*;
public class LocalIdentity extends Identity {
static {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
}
private final BigInteger privateKey;
public LocalIdentity(ECPoint publicKey, BigInteger privateKey) {
@@ -61,4 +57,8 @@ public class LocalIdentity extends Identity {
public byte[] sign(byte[] data) {
return Ts3Crypt.createSignature(privateKey, data);
}
public BigInteger getPrivateKey() {
return privateKey;
}
}
@@ -1,13 +1,10 @@
package com.github.manevolent.ts3j.license;
import Punisher.NaCl.Internal.Ed25519Ref10.*;
import com.github.manevolent.ts3j.util.Ts3Logging;
import com.github.manevolent.ts3j.util.Ts3Crypt;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.security.DigestException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
@@ -137,19 +134,19 @@ public class License {
while (buffer.remaining() > 0) {
int c = buffer.get();
switch (c) {
case 0:
break;
case 0x00:
byte[] stringBytes = new byte[len];
buffer.position(buffer.position() - len);
buffer.get(stringBytes);
return new String(stringBytes, Charset.forName("UTF8"));
default:
len++;
break;
}
}
byte[] stringBytes = new byte[len];
buffer.position(buffer.position() - len);
buffer.get(stringBytes);
return new String(stringBytes, Charset.forName("UTF8"));
throw new IllegalStateException("string not null terminated");
}
public static void writeNullTerminatedLicenseString(ByteBuffer buffer, String s) {
@@ -160,13 +157,28 @@ public class License {
public static List<License> readLicenses(ByteBuffer buffer) {
List<License> licenses = new ArrayList<>();
byte licenseVersion = buffer.get();
if (licenseVersion != 0x01)
throw new IllegalArgumentException("invalid license version");
while (buffer.remaining() > 0) {
byte licenseVersion = buffer.get();
if (licenseVersion != 0x01)
throw new IllegalArgumentException("invalid license version");
int start = buffer.position();
License license = new License();
license.read(buffer);
int end = buffer.position();
int len = end - start;
byte[] hashableLicense = new byte[len - 1];
System.arraycopy(buffer.array(), start + 1, hashableLicense, 0, len - 1);
byte[] licenseHash = Ts3Crypt.hash512(hashableLicense);
byte[] splicedHash = new byte[32];
System.arraycopy(licenseHash, 0, splicedHash, 0, 32);
license.computedHash = splicedHash;
licenses.add(license);
}
@@ -174,40 +186,15 @@ public class License {
}
public byte[] getHash() {
if (computedHash == null) {
ByteBuffer buffer = ByteBuffer.allocate(getSize());
write(buffer);
MessageDigest digest;
try {
digest = MessageDigest.getInstance("SHA512");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
digest.update(buffer.array(), 2, buffer.array().length - 2);
byte[] sliced = new byte[32];
System.arraycopy(digest.digest(), 0, sliced, 0, 32);
Ts3Logging.debug(Ts3Logging.getHex(buffer.array()));
return sliced;
}
return computedHash;
}
public byte[] deriveKey(byte[] parent) {
// ScalarOperations.sc_clamp(Hash);
Ts3Logging.debug("ScalarOperations.sc_clamp: " + Ts3Logging.getHex(getHash()));
ScalarOperations.sc_clamp(getHash(), 0);
Ts3Logging.debug("ScalarOperations.sc_clamp: " + Ts3Logging.getHex(getHash()));
// GroupOperations.ge_frombytes_negate_vartime(out var pubkey, Key);
GroupElementP3 pubkey = new GroupElementP3();
Ts3Logging.debug("ScalarOperations.ge_frombytes_negate_vartime: " + Ts3Logging.getHex(getPublicKey()));
GroupOperations.ge_frombytes_negate_vartime(pubkey, getPublicKey(), 0);
// GroupOperations.ge_frombytes_negate_vartime(out var parkey, parent);
@@ -232,7 +219,7 @@ public class License {
byte[] final_ = new byte[32];
GroupOperations.ge_p3_tobytes(final_, 0, r2);
final_[1] ^= 0x80;
//final_[1] ^= 0x80;
return final_;
}
@@ -1,7 +1,6 @@
package com.github.manevolent.ts3j.license;
import com.github.manevolent.ts3j.enums.LicenseType;
import com.github.manevolent.ts3j.util.Ts3Logging;
import java.nio.ByteBuffer;
@@ -1,5 +1,6 @@
package com.github.manevolent.ts3j.protocol.packet;
import com.github.manevolent.ts3j.command.Command;
import com.github.manevolent.ts3j.command.SimpleCommand;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
@@ -13,6 +14,16 @@ public class PacketBody2Command extends PacketBody {
super(PacketBodyType.COMMAND, role);
}
public PacketBody2Command(ProtocolRole role, String command) {
this(role);
this.text = command;
}
public PacketBody2Command(ProtocolRole role, Command command) {
this(role, command.build());
}
public String getText() {
return text;
}
@@ -27,12 +38,12 @@ public class PacketBody2Command extends PacketBody {
@Override
public void read(ByteBuffer buffer) {
text = Charset.forName("ASCII").decode(buffer).toString();
text = Charset.forName("UTF8").decode(buffer).toString();
}
@Override
public void write(ByteBuffer buffer) {
buffer.put(text.getBytes(Charset.forName("ASCII")));
buffer.put(text.getBytes(Charset.forName("UTF8")));
}
@Override
@@ -10,10 +10,6 @@ import java.nio.ByteOrder;
import static com.github.manevolent.ts3j.protocol.packet.PacketBodyType.INIT1;
public class PacketBody8Init1 extends PacketBody {
private static final byte[] LOW_LEVEL_MAC = new byte[] {
0x54, 0x53, 0x33, 0x49, 0x4E, 0x49, 0x54, 0x31
};
private byte[] version = new byte[4]; // 4 bytes
private Step step; // step instance
@@ -25,7 +21,6 @@ public class PacketBody8Init1 extends PacketBody {
@Override
public void setHeaderValues(PacketHeader header) {
header.setMac(LOW_LEVEL_MAC);
header.setPacketFlag(HeaderFlag.UNENCRYPTED, true);
header.setPacketId((short)101);
}
@@ -4,12 +4,11 @@ import com.github.manevolent.ts3j.command.SimpleCommand;
import com.github.manevolent.ts3j.command.part.CommandSingleParameter;
import com.github.manevolent.ts3j.protocol.Packet;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
import com.github.manevolent.ts3j.protocol.client.ClientConnectionState;
import com.github.manevolent.ts3j.protocol.packet.PacketBody2Command;
import com.github.manevolent.ts3j.protocol.packet.PacketBody8Init1;
import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket;
import com.github.manevolent.ts3j.util.Ts3Crypt;
import com.github.manevolent.ts3j.util.Ts3Logging;
import com.github.manevolent.ts3j.util.Ts3Debugging;
import javafx.util.Pair;
import org.bouncycastle.math.ec.ECPoint;
@@ -46,9 +45,9 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
}
private void sendInit1(PacketBody8Init1 packet) throws IOException, TimeoutException {
Ts3Logging.debug("Connecting: sending " + packet.getClass().getSimpleName()
+ ":"
+ packet.getStep().getClass().getSimpleName() + "..."
Ts3Debugging.debug("Connecting: sending " + packet.getClass().getSimpleName()
+ ":"
+ packet.getStep().getClass().getSimpleName() + "..."
);
packet.setVersion(new byte[] { 0x09, (byte)0x83, (byte)0x8C, (byte)0xCF });
@@ -58,7 +57,7 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
@Override
public void handlePacket(Packet packet) throws IOException, TimeoutException {
Ts3Logging.debug("Connecting: handling " + packet.getBody().getClass().getSimpleName());
Ts3Debugging.debug("Connecting: handling " + packet.getBody().getClass().getSimpleName());
if (packet.getBody() instanceof PacketBody8Init1) {
PacketBody8Init1 init1 = (PacketBody8Init1) packet.getBody();
@@ -102,7 +101,7 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
byte[] solution =
x.modPow(BigInteger.valueOf(2L).pow(serverReplyStep3.getLevel()), n).toByteArray();
Ts3Logging.debug(Ts3Logging.getHex(solution));
Ts3Debugging.debug(Ts3Debugging.getHex(solution));
System.arraycopy(
solution, Math.abs(solution.length - 64),
@@ -155,19 +154,19 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
if (command.getName().equalsIgnoreCase("initivexpand2")) {
// 3.2.2 initivexpand2 (Client <- Server)
Ts3Logging.debug("initivexpand2");
Ts3Debugging.debug(command.build());
if (!command.get("ot").getValue().equals("1"))
throw new IllegalArgumentException("ot constant != 1: " + command.get("ot").getValue());
byte[] license = Base64.getDecoder().decode(command.get("l").getValue());
byte[] licsense_validation = command.get("tvd").getValue() == null ?
byte[] licsense_validation = !command.has("tvd") ?
null :
Base64.getDecoder().decode(command.get("tvd").getValue());
byte[] beta = Base64.getDecoder().decode(command.get("beta").getValue()); // beta
byte[] omega = Base64.getDecoder().decode(command.get("omega").getValue()); // omega
byte[] proof = Base64.getDecoder().decode(command.get("proof").getValue()); // ecdh_sign(1)
byte[] proof = Base64.getDecoder().decode(command.get("proof").getValue());
Pair<byte[], byte[]> keyPair = Ts3Crypt.generateKeypair();
@@ -178,53 +177,69 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
ECPoint publicKey = Ts3Crypt.decodePublicKey(omega);
if (!Ts3Crypt.verifySignature(publicKey, license, proof))
throw new SecurityException("invalid proof signature: " + Ts3Logging.getHex(proof));
throw new SecurityException("invalid proof signature: " + Ts3Debugging.getHex(proof));
// generate proof for clientek
byte[] toSign = new byte[86];
System.arraycopy(keyPair.getKey(), 0, toSign, 0, 32);
System.arraycopy(beta, 0, toSign, 32, 54);
byte[] sign = getClient().getIdentity().sign(toSign);
byte[] signature = Ts3Crypt.generateClientEkProof(
keyPair.getKey(),
beta,
getClient().getIdentity()
);
Ts3Logging.debug("Created proof (clientek): " + Ts3Logging.getHex(sign));
Ts3Debugging.debug("Sending clientek...");
// Send clientek (telling them the shared secret)
PacketBody2Command clientek = new PacketBody2Command(ProtocolRole.CLIENT);
clientek.setText(
new SimpleCommand(
"clientek", ProtocolRole.CLIENT,
new CommandSingleParameter("ek", Base64.getEncoder().encodeToString(keyPair.getKey())),
new CommandSingleParameter("proof", Base64.getEncoder().encodeToString(sign))
new CommandSingleParameter("proof", Base64.getEncoder().encodeToString(signature))
).build()
);
Ts3Debugging.debug(clientek.getText());
getClient().writePacket(clientek);
getClient().setSecureParameters(
Ts3Crypt.cryptoInit2(license, alphaBytes, omega, proof, beta, keyPair.getValue())
);
Ts3Debugging.debug("Sending clientinit...");
PacketBody2Command clientinit = new PacketBody2Command(ProtocolRole.CLIENT);
clientinit.setText(
new SimpleCommand(
"clientinit", ProtocolRole.CLIENT,
"clientinit",
ProtocolRole.CLIENT,
new CommandSingleParameter("client_nickname", "test"),
new CommandSingleParameter("client_version", "3.0.19.3"),
new CommandSingleParameter("client_version", "3.1 [Build: 1471417187]"),
new CommandSingleParameter("client_platform", "Windows"),
new CommandSingleParameter("client_version_sign", "a1OYzvM18mrmfUQBUgxYBxYz2DUU6y5k3/mEL6FurzU0y97Bd1FL7+PRpcHyPkg4R+kKAFZ1nhyzbgkGphDWDg=="),
new CommandSingleParameter("client_input_hardware", "true"),
new CommandSingleParameter("client_output_hardware", "false"),
new CommandSingleParameter(
"client_version_sign",
"gDEgQf/BiOQZdAheKccM1XWcMUj2OUQqt75oFuvF2c0MQMXyv88cZQdUuckKbcBRp7RpmLInto4PIgd7mPO7BQ=="
),
new CommandSingleParameter("client_input_hardware", "1"),
new CommandSingleParameter("client_output_hardware", "1"),
new CommandSingleParameter("client_default_channel", ""),
new CommandSingleParameter("client_default_channel_password", ""),
new CommandSingleParameter("client_server_password", ""),
new CommandSingleParameter("client_nickname_phonetic", "test"),
new CommandSingleParameter("client_meta_data", ""),
new CommandSingleParameter("client_default_token", ""),
new CommandSingleParameter("client_key_offset", Long.toString(getClient().getIdentity().getKeyOffset())),
new CommandSingleParameter("hwid", "87056c6e1268aaf5055abf8256415e0e,408978b6d98810cc03f0aa16a4c75600")
new CommandSingleParameter(
"client_key_offset",
Long.toString(getClient().getIdentity().getKeyOffset())
),
new CommandSingleParameter(
"hwid",
"+LyYqbDqOvJJpN5pdAbF8/v5kZ0="
)
).build()
);
Ts3Debugging.debug(clientinit.getText());
getClient().writePacket(clientinit);
}
}
@@ -12,7 +12,7 @@ public class DefaultPacketTransformation extends PacketTransformation {
{0x6D, 0x5C, 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6C, 0x6C, 0x33, 0x32, 0x2E, 0x63, 0x70, 0x6C};
public DefaultPacketTransformation() {
super(key);
super(key, new byte[8]);
}
@Override
@@ -3,7 +3,7 @@ package com.github.manevolent.ts3j.protocol.packet.transformation;
import com.github.manevolent.ts3j.protocol.Packet;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
import com.github.manevolent.ts3j.protocol.header.PacketHeader;
import com.github.manevolent.ts3j.util.Ts3Logging;
import com.github.manevolent.ts3j.util.Ts3Crypt;
import javafx.util.Pair;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
@@ -14,48 +14,38 @@ import org.bouncycastle.crypto.params.KeyParameter;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class PacketTransformation {
private static final int MAC_LEN = 8;
private static MessageDigest SHA256;
{
try {
SHA256 = MessageDigest.getInstance("SHA256");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
private final byte[] key;
private final byte[] ivStruct;
private final byte[] fakeMac;
private final EAXBlockCipher cipher;
public PacketTransformation(byte[] key) {
this.key = key;
public PacketTransformation(byte[] ivStruct, byte[] fakeMac) {
this.ivStruct = ivStruct;
this.cipher = new EAXBlockCipher(new AESEngine());
this.fakeMac = fakeMac;
}
public byte[] getFakeMac() {
return fakeMac;
}
public Pair<byte[], byte[]> computeParameters(PacketHeader header) {
ByteBuffer temporaryByteBuffer = ByteBuffer
.allocate(key.length == 20 ? 26 : 70)
.allocate(ivStruct.length == 20 ? 26 : 70)
.order(ByteOrder.BIG_ENDIAN);
temporaryByteBuffer.put((byte) (header.getRole() == ProtocolRole.SERVER ? 0x30 : 0x31));
temporaryByteBuffer.put((byte) (header.getType().getIndex() & 0xFF));
temporaryByteBuffer.put((byte) (header.getGeneration() & 0xFF));
temporaryByteBuffer.putInt(header.getGeneration() & 0x000000FFFFFF);
if (key.length == 20) {
//temporary[6 - 26] = SIV[0 - 20]
temporaryByteBuffer.put(key, 0, 20);
} else {
//temporary[6 - 70] = SIV[0 - 64]
temporaryByteBuffer.put(key, 0, 64);
}
temporaryByteBuffer.put(ivStruct);
byte[] key_nonce = SHA256.digest(temporaryByteBuffer.array());
byte[] arrayHash = temporaryByteBuffer.array();
byte[] key_nonce = Ts3Crypt.hash256(arrayHash);
byte[] key = new byte[16];
byte[] nonce = new byte[16];
@@ -63,11 +53,11 @@ public class PacketTransformation {
System.arraycopy(key_nonce, 0, key, 0, 16);
System.arraycopy(key_nonce, 16, nonce, 0, 16);
// key[0] = key[0] xor ((PId & 0xFF00) >> 8)
// key[1] = key[1] xor ((PId & 0x00FF) >> 0)
// ivStruct[0] = ivStruct[0] xor ((PId & 0xFF00) >> 8)
// ivStruct[1] = ivStruct[1] xor ((PId & 0x00FF) >> 0)
key[0] = (byte) (key[0] ^ ((byte) (header.getType().getIndex() & 0xFF00) >> 8));
key[1] = (byte) (key[0] ^ ((byte) (header.getType().getIndex() & 0x00FF) >> 0));
key[0] = (byte) (key[0] ^ ((byte) (header.getPacketId() & 0xFF) >> 8) & 0xFF);
key[1] = (byte) (key[1] ^ ((byte) (header.getPacketId() & 0xFF) >> 0) & 0xFF);
return new Pair<>(key, nonce);
}
@@ -120,8 +110,6 @@ public class PacketTransformation {
// Rest of header
outputBuffer.put(headerWithoutMac);
Ts3Logging.debug("WRITE HEADER: " + Ts3Logging.getHex(outputBuffer.array(), headerBuffer.limit()));
// Encrypted body
outputBuffer.put(result, 0, len - MAC_LEN);
@@ -146,7 +134,6 @@ public class PacketTransformation {
int len;
synchronized (cipher) {
cipher.init(false, cipherParameters);
result = new byte[cipher.getOutputSize(dataLen + MAC_LEN)];
@@ -160,10 +147,8 @@ public class PacketTransformation {
if (len != dataLen)
throw new IllegalArgumentException(len + " != " + dataLen);
}
return result;
}
}
@@ -20,7 +20,7 @@ import com.github.manevolent.ts3j.protocol.packet.transformation.DefaultPacketTr
import com.github.manevolent.ts3j.protocol.packet.transformation.PacketTransformation;
import com.github.manevolent.ts3j.protocol.socket.AbstractTeamspeakSocket;
import com.github.manevolent.ts3j.util.Ts3Crypt;
import com.github.manevolent.ts3j.util.Ts3Logging;
import com.github.manevolent.ts3j.util.Ts3Debugging;
import java.io.IOException;
import java.net.DatagramPacket;
@@ -32,6 +32,10 @@ import java.util.concurrent.*;
public abstract class AbstractTeamspeakClientSocket
extends AbstractTeamspeakSocket
implements TeamspeakClientSocket {
private static final byte[] INIT1_MAC = new byte[] {
0x54, 0x53, 0x33, 0x49, 0x4E, 0x49, 0x54, 0x31
};
private final Object connectionStateLock = new Object();
private final LinkedBlockingDeque<Packet> readQueue = new LinkedBlockingDeque<>(65536);
private final Map<PacketBodyType, Reassembly> reassemblyQueue = new LinkedHashMap<>();
@@ -85,7 +89,7 @@ public abstract class AbstractTeamspeakClientSocket
}
public final void setSecureParameters(Ts3Crypt.SecureChannelParameters parameters) {
setTransformation(new PacketTransformation(parameters.getIvStruct()));
setTransformation(new PacketTransformation(parameters.getIvStruct(), parameters.getFakeSignature()));
}
protected abstract NetworkPacket readNetworkPacket() throws IOException;
@@ -95,7 +99,7 @@ public abstract class AbstractTeamspeakClientSocket
public void setState(ClientConnectionState state) throws IOException, TimeoutException {
synchronized (connectionStateLock) {
if (state != this.connectionState) {
Ts3Logging.debug("State changing: " + state.name());
Ts3Debugging.debug("State changing: " + state.name());
boolean wasDisconnected = this.connectionState == ClientConnectionState.DISCONNECTED;
@@ -110,7 +114,7 @@ public abstract class AbstractTeamspeakClientSocket
if (state == ClientConnectionState.DISCONNECTED)
clientOptions.clear();
Ts3Logging.debug("State changed: " + state.name());
Ts3Debugging.debug("State changed: " + state.name());
}
}
@@ -120,11 +124,11 @@ public abstract class AbstractTeamspeakClientSocket
((LocalClientHandler) handler).handleConnectionStateChanging(state);
if (handler == null || handler.getClass() != state.getHandlerClass()) {
Ts3Logging.debug("Assigning " + state.getHandlerClass().getName() + " handler...");
Ts3Debugging.debug("Assigning " + state.getHandlerClass().getName() + " handler...");
setHandler(state.createHandler(this));
Ts3Logging.debug("Assigned " + state.getHandlerClass().getName() + " handler.");
Ts3Debugging.debug("Assigned " + state.getHandlerClass().getName() + " handler.");
}
}
@@ -206,10 +210,6 @@ public abstract class AbstractTeamspeakClientSocket
// Ensure type matches
packet.getHeader().setType(packet.getBody().getType());
// Make sure new protocol is set if it must be set
if (getState() == ClientConnectionState.CONNECTING)
packet.getHeader().setPacketFlag(HeaderFlag.NEW_PROTOCOL, true);
// Set header values
packet.getBody().setHeaderValues(packet.getHeader());
@@ -226,6 +226,10 @@ public abstract class AbstractTeamspeakClientSocket
packet.getHeader().setPacketId(packetId);
}
if (packet.getHeader().getType() == PacketBodyType.COMMAND ||
packet.getHeader().getType() == PacketBodyType.COMMAND_LOW)
packet.getHeader().setPacketFlag(HeaderFlag.NEW_PROTOCOL, true);
if (packet.getSize() > 500) {
if (!packet.getHeader().getType().isSplittable())
throw new IllegalArgumentException("packet too large: " + packet.getSize());
@@ -272,10 +276,18 @@ public abstract class AbstractTeamspeakClientSocket
// Flush to a buffer
ByteBuffer outputBuffer;
if (packet.getBody().getType().canEncrypt() &&
!packet.getHeader().getPacketFlag(HeaderFlag.UNENCRYPTED)) {
if (!packet.getHeader().getPacketFlag(HeaderFlag.UNENCRYPTED)) {
if (!packet.getHeader().getType().canEncrypt())
throw new IllegalArgumentException("packet flagged as encrypted but this would be a violation");
outputBuffer = getTransformation().encrypt(packet);
} else {
if (packet.getHeader().getType() == PacketBodyType.INIT1) {
System.arraycopy(INIT1_MAC, 0, packet.getHeader().getMac(), 0, 8);
} else {
System.arraycopy(transformation.getFakeMac(), 0, packet.getHeader().getMac(), 0, 8);
}
outputBuffer = ByteBuffer.allocate(packet.getSize());
outputBuffer.order(ByteOrder.BIG_ENDIAN);
@@ -304,7 +316,10 @@ public abstract class AbstractTeamspeakClientSocket
else
response = null;
Ts3Logging.debug("[PROTOCOL] WRITE " + networkPacket.getHeader().getType().name());
Ts3Debugging.debug("[PROTOCOL] WRITE " + networkPacket.getHeader().getType().name());
for (HeaderFlag flag : HeaderFlag.values())
Ts3Debugging.debug(flag.name() + ": " + networkPacket.getHeader().getPacketFlag(flag));
writeNetworkPacket(networkPacket);
// If necessary, fulfill promise for the packet acknowledgement or throw TimeoutException
@@ -343,12 +358,9 @@ public abstract class AbstractTeamspeakClientSocket
boolean fragment = networkPacket.getHeader().getPacketFlag(HeaderFlag.FRAGMENTED);
if (networkPacket.getHeader().getType().isSplittable()) {
if (fragment)
throw new IllegalArgumentException("packet is fragment, but not splittable");
else {
fragment = reassemblyQueue.get(networkPacket.getHeader().getType()).isReassembling();
}
}
fragment = fragment || reassemblyQueue.get(networkPacket.getHeader().getType()).isReassembling();
} else if (fragment)
throw new IllegalArgumentException("packet is fragment, but not splittable");
boolean encrypted = !networkPacket.getHeader().getPacketFlag(HeaderFlag.UNENCRYPTED) &&
networkPacket.getHeader().getType().canEncrypt();
@@ -370,9 +382,14 @@ public abstract class AbstractTeamspeakClientSocket
);
}
Ts3Debugging.debug("[NETWORK] READ " +
packet.getHeader().getType() + " BODY "
+ Ts3Debugging.getHex(packetBuffer.array())
);
// Fragment handling
if (fragment) {
Ts3Logging.debug("[PROTOCOL] READ " + networkPacket.getHeader().getType().name() + " fragment");
Ts3Debugging.debug("[PROTOCOL] READ " + networkPacket.getHeader().getType().name() + " fragment");
packet.setBody(
new PacketBodyFragment(
@@ -381,7 +398,7 @@ public abstract class AbstractTeamspeakClientSocket
)
);
} else {
Ts3Logging.debug("[PROTOCOL] READ " + networkPacket.getHeader().getType().name());
Ts3Debugging.debug("[PROTOCOL] READ " + networkPacket.getHeader().getType().name());
}
// Compression?
@@ -408,6 +425,12 @@ public abstract class AbstractTeamspeakClientSocket
}
}
// Find if the packet must be reassembled
if (packet.getBody() instanceof PacketBodyFragment) {
packet = reassemblyQueue.get(packet.getHeader().getType()).put(packet);
if (packet == null) continue;
}
// Find if the packet is itself an acknowledgement
switch (packet.getHeader().getType()) {
case ACK:
@@ -504,13 +527,13 @@ public abstract class AbstractTeamspeakClientSocket
try {
Packet packet = readQueue.take();
Ts3Logging.debug("Processing " + packet + "...");
Ts3Debugging.debug("Processing " + packet + "...");
getHandler().handlePacket(packet);
Ts3Logging.debug("Processed " + packet + ".");
Ts3Debugging.debug("Processed " + packet + ".");
} catch (Exception e) {
if (e instanceof InterruptedException) continue;
Ts3Logging.debug("Problem handling packet", e);
Ts3Debugging.debug("Problem handling packet", e);
}
}
}
@@ -525,7 +548,7 @@ public abstract class AbstractTeamspeakClientSocket
} catch (Exception e) {
if (e instanceof InterruptedException) continue;
Ts3Logging.debug("Problem reading packet", e);
Ts3Debugging.debug("Problem reading packet", e);
}
}
}
@@ -629,11 +652,9 @@ public abstract class AbstractTeamspeakClientSocket
if (!(packet.getBody() instanceof PacketBodyFragment))
throw new IllegalArgumentException("packet fragment object is not representative of a fragment");
boolean fragmentationEnded = oldState;
queue.put(packet.getHeader().getPacketId(), packet);
if (fragmentationEnded)
if (oldState)
return reassemble(packet);
return null;
@@ -4,7 +4,7 @@ import com.github.manevolent.ts3j.identity.LocalIdentity;
import com.github.manevolent.ts3j.protocol.NetworkPacket;
import com.github.manevolent.ts3j.protocol.client.ClientConnectionState;
import com.github.manevolent.ts3j.protocol.header.PacketHeader;
import com.github.manevolent.ts3j.util.Ts3Logging;
import com.github.manevolent.ts3j.util.Ts3Debugging;
import java.io.IOException;
import java.net.DatagramPacket;
@@ -36,7 +36,7 @@ public class LocalTeamspeakClientSocket extends AbstractTeamspeakClientSocket {
protected NetworkPacket readNetworkPacket() throws IOException {
socket.receive(packet);
Ts3Logging.debug("[NETWORK] READ len=" + packet.getLength() + " from " + packet.getSocketAddress());
Ts3Debugging.debug("[NETWORK] READ len=" + packet.getLength() + " from " + packet.getSocketAddress());
PacketHeader header;
@@ -61,10 +61,11 @@ public class LocalTeamspeakClientSocket extends AbstractTeamspeakClientSocket {
@Override
protected void writeNetworkPacket(NetworkPacket packet) throws IOException {
Ts3Logging.debug("[NETWORK] WRITE id=" + packet.getHeader().getPacketId() + " len=" +
Ts3Debugging.debug("[NETWORK] WRITE id=" + packet.getHeader().getPacketId() + " len=" +
packet.getDatagram().getLength()
+ " to " +
socket.getRemoteSocketAddress());
Ts3Debugging.debug("[NETWORK] WRITE " + Ts3Debugging.getHex(packet.getBuffer().array()));
socket.send(packet.getDatagram());
}
@@ -89,7 +90,7 @@ public class LocalTeamspeakClientSocket extends AbstractTeamspeakClientSocket {
public void connect(InetSocketAddress remote, String password, long timeout)
throws IOException, TimeoutException {
try {
Ts3Logging.debug("Connecting to " + remote + "...");
Ts3Debugging.debug("Connecting to " + remote + "...");
ClientConnectionState connectionState = getState();
@@ -5,6 +5,7 @@ import Punisher.NaCl.Internal.Ed25519Ref10.GroupElementP3;
import Punisher.NaCl.Internal.Ed25519Ref10.GroupOperations;
import Punisher.NaCl.Internal.Ed25519Ref10.ScalarOperations;
import Punisher.NaCl.Internal.Sha512;
import com.github.manevolent.ts3j.identity.*;
import com.github.manevolent.ts3j.license.License;
import javafx.util.Pair;
import org.bouncycastle.asn1.*;
@@ -22,34 +23,50 @@ import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.*;
import java.util.List;
public final class Ts3Crypt {
static {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
}
public static byte[] hash128(byte[] data) {
try {
return MessageDigest.getInstance("SHA1").digest(data);
} catch (GeneralSecurityException ex) {
throw new RuntimeException(ex);
}
}
public static byte[] hash256(byte[] data) {
try {
return MessageDigest.getInstance("SHA256").digest(data);
} catch (GeneralSecurityException ex) {
throw new RuntimeException(ex);
}
}
public static byte[] hash512(byte[] data) {
try {
return MessageDigest.getInstance("SHA512").digest(data);
} catch (GeneralSecurityException ex) {
throw new RuntimeException(ex);
}
}
public static Ts3Crypt.SecureChannelParameters cryptoInit2(byte[] license, byte[] alpha,
byte[] omega, byte[] proof,
byte[] beta, byte[] privateKey) {
// 3.2.2.2 Parsing the license
List<License> licenses = License.readLicenses(ByteBuffer.wrap(license));
Ts3Logging.debug("License.deriveKey: " + Ts3Logging.getHex(license));
byte[] key = License.deriveKey(licenses);
Ts3Logging.debug("License.deriveKey: " + Ts3Logging.getHex(key));
Ts3Logging.debug("Ts3Crypt.generateSharedSecret2: " + Ts3Logging.getHex(key) + " " + Ts3Logging.getHex(privateKey));
byte[] sharedSecret = Ts3Crypt.generateSharedSecret2(key, privateKey);
Ts3Logging.debug("Ts3Crypt.generateSharedSecret2: " + Ts3Logging.getHex(sharedSecret));
Ts3Logging.debug("Ts3Crypt.SecureChannelParameters: " + Ts3Logging.getHex(alpha) + " " + Ts3Logging.getHex(beta) +
" " + Ts3Logging.getHex(sharedSecret));
Ts3Crypt.SecureChannelParameters parameters =
Ts3Crypt.getSecureParameters(alpha, beta, sharedSecret);
Ts3Logging.debug("Computed fake fingerprint: " + Ts3Logging.getHex(parameters.getFakeSignature()));
Ts3Logging.debug("Computed IV struct: " + Ts3Logging.getHex(parameters.getIvStruct()));
return parameters;
}
@@ -62,6 +79,30 @@ public final class Ts3Crypt {
outBuf[i + outOffs] = (byte)(a[i + aoffs] ^ b[i + boffs]);
}
public static byte[] generateClientEkProof(byte[] key, byte[] beta,
LocalIdentity identity) {
return generateClientEkProof(key, beta, identity.getPrivateKey());
}
public static byte[] generateClientEkProof(byte[] key, byte[] beta,
BigInteger privateKey) {
// generate proof for clientek
byte[] data = new byte[86];
System.arraycopy(key, 0, data, 0, 32);
System.arraycopy(beta, 0, data, 32, 54);
return createSignature(privateKey, data);
}
public static boolean verifyClientEkProof(byte[] key, byte[] beta, byte[] signature, ECPoint publicKey) {
// generate proof for clientek
byte[] data = new byte[86];
System.arraycopy(key, 0, data, 0, 32);
System.arraycopy(beta, 0, data, 32, 54);
return verifySignature(publicKey, data, signature);
}
public static SecureChannelParameters getSecureParameters(byte[] alpha, byte[] beta, byte[] sharedKey) {
if (beta.length != 10 && beta.length != 54)
throw new IllegalArgumentException("invalid beta size (" + beta.length + ")");
@@ -72,13 +113,7 @@ public final class Ts3Crypt {
xor(sharedKey, 0, alpha, 0, alpha.length, ivStruct, 0);
xor(sharedKey, 10, beta, 0, beta.length, ivStruct, 10);
byte[] buffer;
try {
buffer = MessageDigest.getInstance("SHA1").digest(ivStruct);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
byte[] buffer = hash128(ivStruct);
System.arraycopy(buffer, 0, fakeSignature, 0, 8);
@@ -191,6 +226,9 @@ public final class Ts3Crypt {
byte[] publicKey = new byte[32];
GroupOperations.ge_p3_tobytes(publicKey, 0, A);
Ts3Debugging.debug(publicKey);
Ts3Debugging.debug(privateKey);
return new Pair<>(publicKey, privateKey);
}
@@ -1,6 +1,16 @@
package com.github.manevolent.ts3j.util;
public final class Ts3Logging {
public final class Ts3Debugging {
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
private static final String HEXES = "0123456789ABCDEF";
public static String getHex(byte[] raw) {
return getHex(raw, raw.length);
@@ -22,6 +32,10 @@ public final class Ts3Logging {
System.err.println("[DEBUG] " + message.toString());
}
public static void debug(byte[] message) {
debug(getHex(message));
}
public static void debug(String message, Throwable ex) {
System.err.println("[DEBUG] " + message);
ex.printStackTrace();
@@ -0,0 +1,43 @@
package com.github.manevolent.ts3j;
import com.github.manevolent.ts3j.identity.LocalIdentity;
import com.github.manevolent.ts3j.util.Ts3Crypt;
import com.github.manevolent.ts3j.util.Ts3Debugging;
import junit.framework.TestCase;
import java.math.BigInteger;
import java.util.Base64;
public class ClientEkTest extends TestCase {
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
public static void main(String[] args) throws Exception {
new ClientEkTest().testParser();
}
public void testParser() throws Exception {
LocalIdentity keyPair = LocalIdentity.load(
Ts3Crypt.decodePublicKey(Base64.getDecoder()
.decode("MEsDAgcAAgEgAiBpPRbTliVt9KxtIz8saYdwcnNgcwaKLbKYSpDNO87u9gIgSWWPKcSJ9P6VZKJfRdpWwcfMdJv+NA9/hXUtz1uwRVI=")),
new BigInteger(Base64.getDecoder().decode("Tj6YXM3qyRv8n25L2pH+OEJnRUl4auQf8+znjYrOmWU=")),
2294,
2295
);
byte[] key = hexStringToByteArray("5763FA00EBC38DCE3953AFCACD8CFF2209208D15BB7D9B699D61A5675EDA9370");
byte[] beta = hexStringToByteArray("EA581E7CBADED7C47B9CBA3D3EEFB33A49B61ED0F0B45DF50D900C12F551B5F6B0EFEE3F593FB225184C78C06DB2D688669B70ABAC6D");
byte[] signature = Ts3Crypt.generateClientEkProof(key, beta, keyPair.getPrivateKey());
assertEquals(true, Ts3Crypt.verifyClientEkProof(key, beta, signature, keyPair.getPublicKey()));
}
}
@@ -0,0 +1,30 @@
package com.github.manevolent.ts3j;
import com.github.manevolent.ts3j.util.Ts3Crypt;
import com.github.manevolent.ts3j.util.Ts3Debugging;
import junit.framework.TestCase;
import static com.github.manevolent.ts3j.util.Ts3Debugging.hexStringToByteArray;
public class CryptoInit2Test extends TestCase {
public static void main(String[] args) throws Exception {
new CryptoInit2Test().testParser();
}
public void testParser() throws Exception {
byte[] licenseBytes = hexStringToByteArray("0100358541498A24ACD30157918B8F50955C0DAE970AB65372CBE407415FCF3E029B02084D15E00AA793600700000020416E6F6E796D6F7573000047D9E4DC25AA2E90ACD4DB5FA61C8FED369B346D84C2CA2FCCCA86F73AFEF092200A77C8810A787141");
byte[] alpha = hexStringToByteArray("9500A5DB3B50ACECAB81");
byte[] omega = hexStringToByteArray("304B03020700020120022025D13CF0C5A17A467961B04418E015941D6C6558DA6AEC79CFFDDA1DEF8AB55202200F3B3F0D3A4F1C5C02AD1F9A3A5114AC58E89905792FBB83AD4FFE7F82ACAADA");
byte[] proof = hexStringToByteArray("3046022100F4B921DDB2C9737D9AB100BA251D11216DF1601C62AF17150E6FC29A2C6EEC0F022100FC8948E595BBD895A2FDFE0EB22A5241655BD47591F8D2733035CF76A72245AD");
byte[] beta = hexStringToByteArray("EAFFC9A8BC996B25C8AA700264E99E372ECCDEB1C121D6EC0F4D49FB46CEEBA4E3C724B3070FD70CB03D7BC08129205690ECE228CA7C");
byte[] privateKey = hexStringToByteArray("102E591ABA4508129E812FF3437E2DDD3CA1F1EC341117CA3514CC347A7C2A77");
Ts3Crypt.SecureChannelParameters result =
Ts3Crypt.cryptoInit2(licenseBytes, alpha, omega, proof, beta, privateKey);
assertEquals(Ts3Debugging.getHex(result.getIvStruct()), "E4082A92F71C96A947452F5582EF2879B2051ED2D3F2C6B0643CF5A266EE6B5180573C2F5F3F1C4AC579188366F16AE0EADC3AAF860805D8F2A831E9E49F4513");
assertEquals(Ts3Debugging.getHex(result.getFakeSignature()), "54F2B4D661E0F9AB");
}
}
@@ -0,0 +1,33 @@
package com.github.manevolent.ts3j;
import com.github.manevolent.ts3j.identity.LocalIdentity;
import com.github.manevolent.ts3j.util.Ts3Crypt;
import com.github.manevolent.ts3j.util.Ts3Debugging;
import junit.framework.TestCase;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.util.Base64;
public class EncodingTest extends TestCase {
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
public static void main(String[] args) throws Exception {
new EncodingTest().testParser();
}
public void testParser() throws Exception {
byte[] encoded = "aW<#CD$)(A@$)2398i4mc20xA#W$)Aw3m403w49xw4xamw4c;w9re __//\\uc".getBytes(Charset.forName("UTF8"));
String base64 = Base64.getEncoder().encodeToString(encoded);
assertEquals(base64, "YVc8I0NEJCkoQUAkKTIzOThpNG1jMjB4QSNXJClBdzNtNDAzdzQ5eHc0eGFtdzRjO3c5cmUgIF9fLy9cdWM=");
}
}
@@ -0,0 +1,58 @@
package com.github.manevolent.ts3j;
import com.github.manevolent.ts3j.protocol.Packet;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
import com.github.manevolent.ts3j.protocol.header.ClientPacketHeader;
import com.github.manevolent.ts3j.protocol.header.HeaderFlag;
import com.github.manevolent.ts3j.protocol.packet.PacketBody2Command;
import com.github.manevolent.ts3j.protocol.packet.PacketBodyType;
import com.github.manevolent.ts3j.protocol.packet.transformation.PacketTransformation;
import com.github.manevolent.ts3j.util.Ts3Crypt;
import com.github.manevolent.ts3j.util.Ts3Debugging;
import javafx.util.Pair;
import junit.framework.TestCase;
import java.math.BigInteger;
import java.util.Base64;
public class EncryptionTest extends TestCase {
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
public static void main(String[] args) throws Exception {
new EncryptionTest().testParser();
}
public void testParser() throws Exception {
byte[] fakeFingerprint = Base64.getDecoder().decode("XuhegJMKpa0=");
byte[] ivStruct = Base64.getDecoder().decode("/rn6nR71hV8eFl+15WO68fRU8pOCBw3t0FmcG5c7WNxIkeZ1NtaWTVMBde0cdU5tTKwOl8sE6gpHjnCEF4hhDw==");
PacketTransformation transformation = new PacketTransformation(ivStruct, fakeFingerprint);
Packet packet = new Packet(ProtocolRole.CLIENT);
ClientPacketHeader header = new ClientPacketHeader();
header.setType(PacketBodyType.COMMAND);
header.setPacketFlag(HeaderFlag.NEW_PROTOCOL, true);
header.setPacketId(1);
packet.setHeader(header);
Pair<byte[], byte[]> params = transformation.computeParameters(header);
assertEquals(Base64.getEncoder().encodeToString(params.getKey()), "BF+lO776+e45u+qYAOHihg==");
assertEquals(Base64.getEncoder().encodeToString(params.getValue()), "1IVcTMuizpDHjQgn2yGCgg==");
packet.setBody(new PacketBody2Command(ProtocolRole.CLIENT, "clientinit client_nickname=TestClient client_version=3.1.8\\s[Build:\\s1516614607] client_platform=Windows client_input_hardware=1 client_output_hardware=1 client_default_channel= client_default_channel_password= client_server_password=QL0AFWMIX8NRZTKeof9cXsvbvu8= client_meta_data= client_version_sign=gDEgQf\\/BiOQZdAheKccM1XWcMUj2OUQqt75oFuvF2c0MQMXyv88cZQdUuckKbcBRp7RpmLInto4PIgd7mPO7BQ== client_key_offset=6199 client_nickname_phonetic= client_default_token= hwid=+LyYqbDqOvJJpN5pdAbF8\\/v5kZ0="));
byte[] packetBytes = transformation.encrypt(packet).array();
Ts3Debugging.debug(Base64.getEncoder().encode(packetBytes));
}
}
@@ -0,0 +1,31 @@
package com.github.manevolent.ts3j;
import com.github.manevolent.ts3j.license.License;
import com.github.manevolent.ts3j.util.Ts3Debugging;
import junit.framework.TestCase;
import java.nio.ByteBuffer;
public class LicenseDerivationTest extends TestCase {
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
public static void main(String[] args) throws Exception {
new LicenseDerivationTest().testParser();
}
public void testParser() throws Exception {
byte[] licenseBytes = hexStringToByteArray("0100358541498A24ACD30157918B8F50955C0DAE970AB65372CBE407415FCF3E029B02084D15E00AA793600700000020416E6F6E796D6F75730000AA43CD339C6E30E4E848F2FF031B93146B2E4925B28B33A3E8054D2101D06FF7200A776C5E0A78151E");
byte[] derrivedKey = License.deriveKey(License.readLicenses(ByteBuffer.wrap(licenseBytes)));
assertEquals(Ts3Debugging.getHex(derrivedKey), "B4EBE337EA2AA9F37C3E2FF8889D24B08B7439E654513B8BF342D1D174895C30");
}
}
@@ -3,25 +3,34 @@ package com.github.manevolent.ts3j;
import com.github.manevolent.ts3j.identity.LocalIdentity;
import com.github.manevolent.ts3j.protocol.client.ClientConnectionState;
import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket;
import com.github.manevolent.ts3j.util.Ts3Crypt;
import junit.framework.TestCase;
import java.math.BigInteger;
import java.net.InetSocketAddress;
import java.util.Base64;
public class ServerConnectionTest extends TestCase {
public ServerConnectionTest(String name) {
super(name);
public ServerConnectionTest() {
super("ServerConnectionTest");
}
public void testParser() throws Exception {
try {
LocalIdentity localIdentity = LocalIdentity.generateNew(8);
LocalIdentity localIdentity = LocalIdentity.load(
Ts3Crypt.decodePublicKey(Base64.getDecoder()
.decode("MEsDAgcAAgEgAiBpPRbTliVt9KxtIz8saYdwcnNgcwaKLbKYSpDNO87u9gIgSWWPKcSJ9P6VZKJfRdpWwcfMdJv+NA9/hXUtz1uwRVI=")),
new BigInteger(Base64.getDecoder().decode("Tj6YXM3qyRv8n25L2pH+OEJnRUl4auQf8+znjYrOmWU=")),
2294,
2295
);
LocalTeamspeakClientSocket client = new LocalTeamspeakClientSocket();
client.setIdentity(localIdentity);
client.connect(new InetSocketAddress(
"ts.teamlixo.net",
"voice.teamspeak.com",
9987),
null,
10000L
@@ -32,4 +41,8 @@ public class ServerConnectionTest extends TestCase {
ex.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
new ServerConnectionTest().testParser();
}
}