Still not getting the last packet to work.
This commit is contained in:
@@ -1065,6 +1065,6 @@ public class GroupOperations
|
|||||||
FieldOperations.fe_mul(x, h.X, recip);
|
FieldOperations.fe_mul(x, h.X, recip);
|
||||||
FieldOperations.fe_mul(y, h.Y, recip);
|
FieldOperations.fe_mul(y, h.Y, recip);
|
||||||
FieldOperations.fe_tobytes(s, offset, y);
|
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.CommandParameter;
|
||||||
import com.github.manevolent.ts3j.command.part.CommandSingleParameter;
|
import com.github.manevolent.ts3j.command.part.CommandSingleParameter;
|
||||||
import com.github.manevolent.ts3j.protocol.ProtocolRole;
|
import com.github.manevolent.ts3j.protocol.ProtocolRole;
|
||||||
import com.github.manevolent.ts3j.util.Ts3Logging;
|
|
||||||
import com.github.manevolent.ts3j.util.Ts3String;
|
import com.github.manevolent.ts3j.util.Ts3String;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|||||||
@@ -1,16 +1,10 @@
|
|||||||
package com.github.manevolent.ts3j.identity;
|
package com.github.manevolent.ts3j.identity;
|
||||||
|
|
||||||
import com.github.manevolent.ts3j.util.Ts3Crypt;
|
import com.github.manevolent.ts3j.util.Ts3Crypt;
|
||||||
import com.github.manevolent.ts3j.util.Ts3Logging;
|
import com.github.manevolent.ts3j.util.Ts3Debugging;
|
||||||
import org.bouncycastle.asn1.ASN1Encodable;
|
|
||||||
import org.bouncycastle.asn1.DERBitString;
|
|
||||||
import org.bouncycastle.asn1.DERInteger;
|
|
||||||
import org.bouncycastle.asn1.DERSequence;
|
|
||||||
import org.bouncycastle.math.ec.ECPoint;
|
import org.bouncycastle.math.ec.ECPoint;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.security.DigestException;
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
@@ -83,7 +77,7 @@ public class Identity {
|
|||||||
|
|
||||||
if (curr > best)
|
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;
|
keyOffset = lastCheckedKeyOffset;
|
||||||
best = curr;
|
best = curr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,6 @@ import java.math.BigInteger;
|
|||||||
import java.security.*;
|
import java.security.*;
|
||||||
|
|
||||||
public class LocalIdentity extends Identity {
|
public class LocalIdentity extends Identity {
|
||||||
static {
|
|
||||||
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
|
|
||||||
}
|
|
||||||
|
|
||||||
private final BigInteger privateKey;
|
private final BigInteger privateKey;
|
||||||
|
|
||||||
public LocalIdentity(ECPoint publicKey, BigInteger privateKey) {
|
public LocalIdentity(ECPoint publicKey, BigInteger privateKey) {
|
||||||
@@ -61,4 +57,8 @@ public class LocalIdentity extends Identity {
|
|||||||
public byte[] sign(byte[] data) {
|
public byte[] sign(byte[] data) {
|
||||||
return Ts3Crypt.createSignature(privateKey, data);
|
return Ts3Crypt.createSignature(privateKey, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BigInteger getPrivateKey() {
|
||||||
|
return privateKey;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
package com.github.manevolent.ts3j.license;
|
package com.github.manevolent.ts3j.license;
|
||||||
|
|
||||||
import Punisher.NaCl.Internal.Ed25519Ref10.*;
|
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.ByteBuffer;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.security.DigestException;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -137,19 +134,19 @@ public class License {
|
|||||||
while (buffer.remaining() > 0) {
|
while (buffer.remaining() > 0) {
|
||||||
int c = buffer.get();
|
int c = buffer.get();
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 0:
|
case 0x00:
|
||||||
break;
|
byte[] stringBytes = new byte[len];
|
||||||
|
buffer.position(buffer.position() - len);
|
||||||
|
buffer.get(stringBytes);
|
||||||
|
|
||||||
|
return new String(stringBytes, Charset.forName("UTF8"));
|
||||||
default:
|
default:
|
||||||
len++;
|
len++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] stringBytes = new byte[len];
|
throw new IllegalStateException("string not null terminated");
|
||||||
buffer.position(buffer.position() - len);
|
|
||||||
buffer.get(stringBytes);
|
|
||||||
|
|
||||||
return new String(stringBytes, Charset.forName("UTF8"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void writeNullTerminatedLicenseString(ByteBuffer buffer, String s) {
|
public static void writeNullTerminatedLicenseString(ByteBuffer buffer, String s) {
|
||||||
@@ -160,13 +157,28 @@ public class License {
|
|||||||
public static List<License> readLicenses(ByteBuffer buffer) {
|
public static List<License> readLicenses(ByteBuffer buffer) {
|
||||||
List<License> licenses = new ArrayList<>();
|
List<License> licenses = new ArrayList<>();
|
||||||
|
|
||||||
|
byte licenseVersion = buffer.get();
|
||||||
|
if (licenseVersion != 0x01)
|
||||||
|
throw new IllegalArgumentException("invalid license version");
|
||||||
|
|
||||||
while (buffer.remaining() > 0) {
|
while (buffer.remaining() > 0) {
|
||||||
byte licenseVersion = buffer.get();
|
int start = buffer.position();
|
||||||
if (licenseVersion != 0x01)
|
|
||||||
throw new IllegalArgumentException("invalid license version");
|
|
||||||
|
|
||||||
License license = new License();
|
License license = new License();
|
||||||
license.read(buffer);
|
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);
|
licenses.add(license);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,40 +186,15 @@ public class License {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getHash() {
|
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;
|
return computedHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] deriveKey(byte[] parent) {
|
public byte[] deriveKey(byte[] parent) {
|
||||||
// ScalarOperations.sc_clamp(Hash);
|
// ScalarOperations.sc_clamp(Hash);
|
||||||
Ts3Logging.debug("ScalarOperations.sc_clamp: " + Ts3Logging.getHex(getHash()));
|
|
||||||
ScalarOperations.sc_clamp(getHash(), 0);
|
ScalarOperations.sc_clamp(getHash(), 0);
|
||||||
Ts3Logging.debug("ScalarOperations.sc_clamp: " + Ts3Logging.getHex(getHash()));
|
|
||||||
|
|
||||||
// GroupOperations.ge_frombytes_negate_vartime(out var pubkey, Key);
|
// GroupOperations.ge_frombytes_negate_vartime(out var pubkey, Key);
|
||||||
GroupElementP3 pubkey = new GroupElementP3();
|
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(pubkey, getPublicKey(), 0);
|
||||||
|
|
||||||
// GroupOperations.ge_frombytes_negate_vartime(out var parkey, parent);
|
// GroupOperations.ge_frombytes_negate_vartime(out var parkey, parent);
|
||||||
@@ -232,7 +219,7 @@ public class License {
|
|||||||
byte[] final_ = new byte[32];
|
byte[] final_ = new byte[32];
|
||||||
GroupOperations.ge_p3_tobytes(final_, 0, r2);
|
GroupOperations.ge_p3_tobytes(final_, 0, r2);
|
||||||
|
|
||||||
final_[1] ^= 0x80;
|
//final_[1] ^= 0x80;
|
||||||
|
|
||||||
return final_;
|
return final_;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.github.manevolent.ts3j.license;
|
package com.github.manevolent.ts3j.license;
|
||||||
|
|
||||||
import com.github.manevolent.ts3j.enums.LicenseType;
|
import com.github.manevolent.ts3j.enums.LicenseType;
|
||||||
import com.github.manevolent.ts3j.util.Ts3Logging;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.github.manevolent.ts3j.protocol.packet;
|
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.command.SimpleCommand;
|
||||||
import com.github.manevolent.ts3j.protocol.ProtocolRole;
|
import com.github.manevolent.ts3j.protocol.ProtocolRole;
|
||||||
|
|
||||||
@@ -13,6 +14,16 @@ public class PacketBody2Command extends PacketBody {
|
|||||||
super(PacketBodyType.COMMAND, role);
|
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() {
|
public String getText() {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
@@ -27,12 +38,12 @@ public class PacketBody2Command extends PacketBody {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void read(ByteBuffer buffer) {
|
public void read(ByteBuffer buffer) {
|
||||||
text = Charset.forName("ASCII").decode(buffer).toString();
|
text = Charset.forName("UTF8").decode(buffer).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(ByteBuffer buffer) {
|
public void write(ByteBuffer buffer) {
|
||||||
buffer.put(text.getBytes(Charset.forName("ASCII")));
|
buffer.put(text.getBytes(Charset.forName("UTF8")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -10,10 +10,6 @@ import java.nio.ByteOrder;
|
|||||||
import static com.github.manevolent.ts3j.protocol.packet.PacketBodyType.INIT1;
|
import static com.github.manevolent.ts3j.protocol.packet.PacketBodyType.INIT1;
|
||||||
|
|
||||||
public class PacketBody8Init1 extends PacketBody {
|
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 byte[] version = new byte[4]; // 4 bytes
|
||||||
private Step step; // step instance
|
private Step step; // step instance
|
||||||
|
|
||||||
@@ -25,7 +21,6 @@ public class PacketBody8Init1 extends PacketBody {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setHeaderValues(PacketHeader header) {
|
public void setHeaderValues(PacketHeader header) {
|
||||||
header.setMac(LOW_LEVEL_MAC);
|
|
||||||
header.setPacketFlag(HeaderFlag.UNENCRYPTED, true);
|
header.setPacketFlag(HeaderFlag.UNENCRYPTED, true);
|
||||||
header.setPacketId((short)101);
|
header.setPacketId((short)101);
|
||||||
}
|
}
|
||||||
|
|||||||
+41
-26
@@ -4,12 +4,11 @@ import com.github.manevolent.ts3j.command.SimpleCommand;
|
|||||||
import com.github.manevolent.ts3j.command.part.CommandSingleParameter;
|
import com.github.manevolent.ts3j.command.part.CommandSingleParameter;
|
||||||
import com.github.manevolent.ts3j.protocol.Packet;
|
import com.github.manevolent.ts3j.protocol.Packet;
|
||||||
import com.github.manevolent.ts3j.protocol.ProtocolRole;
|
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.PacketBody2Command;
|
||||||
import com.github.manevolent.ts3j.protocol.packet.PacketBody8Init1;
|
import com.github.manevolent.ts3j.protocol.packet.PacketBody8Init1;
|
||||||
import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket;
|
import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket;
|
||||||
import com.github.manevolent.ts3j.util.Ts3Crypt;
|
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 javafx.util.Pair;
|
||||||
import org.bouncycastle.math.ec.ECPoint;
|
import org.bouncycastle.math.ec.ECPoint;
|
||||||
|
|
||||||
@@ -46,9 +45,9 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void sendInit1(PacketBody8Init1 packet) throws IOException, TimeoutException {
|
private void sendInit1(PacketBody8Init1 packet) throws IOException, TimeoutException {
|
||||||
Ts3Logging.debug("Connecting: sending " + packet.getClass().getSimpleName()
|
Ts3Debugging.debug("Connecting: sending " + packet.getClass().getSimpleName()
|
||||||
+ ":"
|
+ ":"
|
||||||
+ packet.getStep().getClass().getSimpleName() + "..."
|
+ packet.getStep().getClass().getSimpleName() + "..."
|
||||||
);
|
);
|
||||||
|
|
||||||
packet.setVersion(new byte[] { 0x09, (byte)0x83, (byte)0x8C, (byte)0xCF });
|
packet.setVersion(new byte[] { 0x09, (byte)0x83, (byte)0x8C, (byte)0xCF });
|
||||||
@@ -58,7 +57,7 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handlePacket(Packet packet) throws IOException, TimeoutException {
|
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) {
|
if (packet.getBody() instanceof PacketBody8Init1) {
|
||||||
PacketBody8Init1 init1 = (PacketBody8Init1) packet.getBody();
|
PacketBody8Init1 init1 = (PacketBody8Init1) packet.getBody();
|
||||||
@@ -102,7 +101,7 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
|
|||||||
byte[] solution =
|
byte[] solution =
|
||||||
x.modPow(BigInteger.valueOf(2L).pow(serverReplyStep3.getLevel()), n).toByteArray();
|
x.modPow(BigInteger.valueOf(2L).pow(serverReplyStep3.getLevel()), n).toByteArray();
|
||||||
|
|
||||||
Ts3Logging.debug(Ts3Logging.getHex(solution));
|
Ts3Debugging.debug(Ts3Debugging.getHex(solution));
|
||||||
|
|
||||||
System.arraycopy(
|
System.arraycopy(
|
||||||
solution, Math.abs(solution.length - 64),
|
solution, Math.abs(solution.length - 64),
|
||||||
@@ -155,19 +154,19 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
|
|||||||
if (command.getName().equalsIgnoreCase("initivexpand2")) {
|
if (command.getName().equalsIgnoreCase("initivexpand2")) {
|
||||||
// 3.2.2 initivexpand2 (Client <- Server)
|
// 3.2.2 initivexpand2 (Client <- Server)
|
||||||
|
|
||||||
Ts3Logging.debug("initivexpand2");
|
Ts3Debugging.debug(command.build());
|
||||||
|
|
||||||
if (!command.get("ot").getValue().equals("1"))
|
if (!command.get("ot").getValue().equals("1"))
|
||||||
throw new IllegalArgumentException("ot constant != 1: " + command.get("ot").getValue());
|
throw new IllegalArgumentException("ot constant != 1: " + command.get("ot").getValue());
|
||||||
|
|
||||||
byte[] license = Base64.getDecoder().decode(command.get("l").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 :
|
null :
|
||||||
Base64.getDecoder().decode(command.get("tvd").getValue());
|
Base64.getDecoder().decode(command.get("tvd").getValue());
|
||||||
|
|
||||||
byte[] beta = Base64.getDecoder().decode(command.get("beta").getValue()); // beta
|
byte[] beta = Base64.getDecoder().decode(command.get("beta").getValue()); // beta
|
||||||
byte[] omega = Base64.getDecoder().decode(command.get("omega").getValue()); // omega
|
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();
|
Pair<byte[], byte[]> keyPair = Ts3Crypt.generateKeypair();
|
||||||
|
|
||||||
@@ -178,53 +177,69 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
|
|||||||
ECPoint publicKey = Ts3Crypt.decodePublicKey(omega);
|
ECPoint publicKey = Ts3Crypt.decodePublicKey(omega);
|
||||||
|
|
||||||
if (!Ts3Crypt.verifySignature(publicKey, license, proof))
|
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[] signature = Ts3Crypt.generateClientEkProof(
|
||||||
byte[] toSign = new byte[86];
|
keyPair.getKey(),
|
||||||
System.arraycopy(keyPair.getKey(), 0, toSign, 0, 32);
|
beta,
|
||||||
System.arraycopy(beta, 0, toSign, 32, 54);
|
getClient().getIdentity()
|
||||||
byte[] sign = getClient().getIdentity().sign(toSign);
|
);
|
||||||
|
|
||||||
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);
|
PacketBody2Command clientek = new PacketBody2Command(ProtocolRole.CLIENT);
|
||||||
clientek.setText(
|
clientek.setText(
|
||||||
new SimpleCommand(
|
new SimpleCommand(
|
||||||
"clientek", ProtocolRole.CLIENT,
|
"clientek", ProtocolRole.CLIENT,
|
||||||
new CommandSingleParameter("ek", Base64.getEncoder().encodeToString(keyPair.getKey())),
|
new CommandSingleParameter("ek", Base64.getEncoder().encodeToString(keyPair.getKey())),
|
||||||
new CommandSingleParameter("proof", Base64.getEncoder().encodeToString(sign))
|
new CommandSingleParameter("proof", Base64.getEncoder().encodeToString(signature))
|
||||||
).build()
|
).build()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ts3Debugging.debug(clientek.getText());
|
||||||
|
|
||||||
getClient().writePacket(clientek);
|
getClient().writePacket(clientek);
|
||||||
|
|
||||||
getClient().setSecureParameters(
|
getClient().setSecureParameters(
|
||||||
Ts3Crypt.cryptoInit2(license, alphaBytes, omega, proof, beta, keyPair.getValue())
|
Ts3Crypt.cryptoInit2(license, alphaBytes, omega, proof, beta, keyPair.getValue())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ts3Debugging.debug("Sending clientinit...");
|
||||||
|
|
||||||
PacketBody2Command clientinit = new PacketBody2Command(ProtocolRole.CLIENT);
|
PacketBody2Command clientinit = new PacketBody2Command(ProtocolRole.CLIENT);
|
||||||
|
|
||||||
clientinit.setText(
|
clientinit.setText(
|
||||||
new SimpleCommand(
|
new SimpleCommand(
|
||||||
"clientinit", ProtocolRole.CLIENT,
|
"clientinit",
|
||||||
|
ProtocolRole.CLIENT,
|
||||||
new CommandSingleParameter("client_nickname", "test"),
|
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_platform", "Windows"),
|
||||||
new CommandSingleParameter("client_version_sign", "a1OYzvM18mrmfUQBUgxYBxYz2DUU6y5k3/mEL6FurzU0y97Bd1FL7+PRpcHyPkg4R+kKAFZ1nhyzbgkGphDWDg=="),
|
new CommandSingleParameter(
|
||||||
new CommandSingleParameter("client_input_hardware", "true"),
|
"client_version_sign",
|
||||||
new CommandSingleParameter("client_output_hardware", "false"),
|
"gDEgQf/BiOQZdAheKccM1XWcMUj2OUQqt75oFuvF2c0MQMXyv88cZQdUuckKbcBRp7RpmLInto4PIgd7mPO7BQ=="
|
||||||
|
),
|
||||||
|
new CommandSingleParameter("client_input_hardware", "1"),
|
||||||
|
new CommandSingleParameter("client_output_hardware", "1"),
|
||||||
new CommandSingleParameter("client_default_channel", ""),
|
new CommandSingleParameter("client_default_channel", ""),
|
||||||
new CommandSingleParameter("client_default_channel_password", ""),
|
new CommandSingleParameter("client_default_channel_password", ""),
|
||||||
new CommandSingleParameter("client_server_password", ""),
|
new CommandSingleParameter("client_server_password", ""),
|
||||||
new CommandSingleParameter("client_nickname_phonetic", "test"),
|
new CommandSingleParameter("client_nickname_phonetic", "test"),
|
||||||
new CommandSingleParameter("client_meta_data", ""),
|
new CommandSingleParameter("client_meta_data", ""),
|
||||||
new CommandSingleParameter("client_default_token", ""),
|
new CommandSingleParameter("client_default_token", ""),
|
||||||
new CommandSingleParameter("client_key_offset", Long.toString(getClient().getIdentity().getKeyOffset())),
|
new CommandSingleParameter(
|
||||||
new CommandSingleParameter("hwid", "87056c6e1268aaf5055abf8256415e0e,408978b6d98810cc03f0aa16a4c75600")
|
"client_key_offset",
|
||||||
|
Long.toString(getClient().getIdentity().getKeyOffset())
|
||||||
|
),
|
||||||
|
new CommandSingleParameter(
|
||||||
|
"hwid",
|
||||||
|
"+LyYqbDqOvJJpN5pdAbF8/v5kZ0="
|
||||||
|
)
|
||||||
).build()
|
).build()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ts3Debugging.debug(clientinit.getText());
|
||||||
|
|
||||||
getClient().writePacket(clientinit);
|
getClient().writePacket(clientinit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -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};
|
{0x6D, 0x5C, 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6C, 0x6C, 0x33, 0x32, 0x2E, 0x63, 0x70, 0x6C};
|
||||||
|
|
||||||
public DefaultPacketTransformation() {
|
public DefaultPacketTransformation() {
|
||||||
super(key);
|
super(key, new byte[8]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+20
-35
@@ -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.Packet;
|
||||||
import com.github.manevolent.ts3j.protocol.ProtocolRole;
|
import com.github.manevolent.ts3j.protocol.ProtocolRole;
|
||||||
import com.github.manevolent.ts3j.protocol.header.PacketHeader;
|
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 javafx.util.Pair;
|
||||||
import org.bouncycastle.crypto.CipherParameters;
|
import org.bouncycastle.crypto.CipherParameters;
|
||||||
import org.bouncycastle.crypto.InvalidCipherTextException;
|
import org.bouncycastle.crypto.InvalidCipherTextException;
|
||||||
@@ -14,48 +14,38 @@ import org.bouncycastle.crypto.params.KeyParameter;
|
|||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
public class PacketTransformation {
|
public class PacketTransformation {
|
||||||
private static final int MAC_LEN = 8;
|
private static final int MAC_LEN = 8;
|
||||||
private static MessageDigest SHA256;
|
private final byte[] ivStruct;
|
||||||
|
private final byte[] fakeMac;
|
||||||
{
|
|
||||||
try {
|
|
||||||
SHA256 = MessageDigest.getInstance("SHA256");
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final byte[] key;
|
|
||||||
|
|
||||||
private final EAXBlockCipher cipher;
|
private final EAXBlockCipher cipher;
|
||||||
|
|
||||||
public PacketTransformation(byte[] key) {
|
public PacketTransformation(byte[] ivStruct, byte[] fakeMac) {
|
||||||
this.key = key;
|
this.ivStruct = ivStruct;
|
||||||
this.cipher = new EAXBlockCipher(new AESEngine());
|
this.cipher = new EAXBlockCipher(new AESEngine());
|
||||||
|
this.fakeMac = fakeMac;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getFakeMac() {
|
||||||
|
return fakeMac;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pair<byte[], byte[]> computeParameters(PacketHeader header) {
|
public Pair<byte[], byte[]> computeParameters(PacketHeader header) {
|
||||||
ByteBuffer temporaryByteBuffer = ByteBuffer
|
ByteBuffer temporaryByteBuffer = ByteBuffer
|
||||||
.allocate(key.length == 20 ? 26 : 70)
|
.allocate(ivStruct.length == 20 ? 26 : 70)
|
||||||
.order(ByteOrder.BIG_ENDIAN);
|
.order(ByteOrder.BIG_ENDIAN);
|
||||||
|
|
||||||
temporaryByteBuffer.put((byte) (header.getRole() == ProtocolRole.SERVER ? 0x30 : 0x31));
|
temporaryByteBuffer.put((byte) (header.getRole() == ProtocolRole.SERVER ? 0x30 : 0x31));
|
||||||
temporaryByteBuffer.put((byte) (header.getType().getIndex() & 0xFF));
|
temporaryByteBuffer.put((byte) (header.getType().getIndex() & 0xFF));
|
||||||
temporaryByteBuffer.put((byte) (header.getGeneration() & 0xFF));
|
temporaryByteBuffer.putInt(header.getGeneration() & 0x000000FFFFFF);
|
||||||
|
|
||||||
if (key.length == 20) {
|
temporaryByteBuffer.put(ivStruct);
|
||||||
//temporary[6 - 26] = SIV[0 - 20]
|
|
||||||
temporaryByteBuffer.put(key, 0, 20);
|
|
||||||
} else {
|
|
||||||
//temporary[6 - 70] = SIV[0 - 64]
|
|
||||||
temporaryByteBuffer.put(key, 0, 64);
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] key_nonce = SHA256.digest(temporaryByteBuffer.array());
|
byte[] arrayHash = temporaryByteBuffer.array();
|
||||||
|
|
||||||
|
byte[] key_nonce = Ts3Crypt.hash256(arrayHash);
|
||||||
|
|
||||||
byte[] key = new byte[16];
|
byte[] key = new byte[16];
|
||||||
byte[] nonce = 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, 0, key, 0, 16);
|
||||||
System.arraycopy(key_nonce, 16, nonce, 0, 16);
|
System.arraycopy(key_nonce, 16, nonce, 0, 16);
|
||||||
|
|
||||||
// key[0] = key[0] xor ((PId & 0xFF00) >> 8)
|
// ivStruct[0] = ivStruct[0] xor ((PId & 0xFF00) >> 8)
|
||||||
// key[1] = key[1] xor ((PId & 0x00FF) >> 0)
|
// ivStruct[1] = ivStruct[1] xor ((PId & 0x00FF) >> 0)
|
||||||
|
|
||||||
key[0] = (byte) (key[0] ^ ((byte) (header.getType().getIndex() & 0xFF00) >> 8));
|
key[0] = (byte) (key[0] ^ ((byte) (header.getPacketId() & 0xFF) >> 8) & 0xFF);
|
||||||
key[1] = (byte) (key[0] ^ ((byte) (header.getType().getIndex() & 0x00FF) >> 0));
|
key[1] = (byte) (key[1] ^ ((byte) (header.getPacketId() & 0xFF) >> 0) & 0xFF);
|
||||||
|
|
||||||
return new Pair<>(key, nonce);
|
return new Pair<>(key, nonce);
|
||||||
}
|
}
|
||||||
@@ -120,8 +110,6 @@ public class PacketTransformation {
|
|||||||
// Rest of header
|
// Rest of header
|
||||||
outputBuffer.put(headerWithoutMac);
|
outputBuffer.put(headerWithoutMac);
|
||||||
|
|
||||||
Ts3Logging.debug("WRITE HEADER: " + Ts3Logging.getHex(outputBuffer.array(), headerBuffer.limit()));
|
|
||||||
|
|
||||||
// Encrypted body
|
// Encrypted body
|
||||||
outputBuffer.put(result, 0, len - MAC_LEN);
|
outputBuffer.put(result, 0, len - MAC_LEN);
|
||||||
|
|
||||||
@@ -146,7 +134,6 @@ public class PacketTransformation {
|
|||||||
int len;
|
int len;
|
||||||
|
|
||||||
synchronized (cipher) {
|
synchronized (cipher) {
|
||||||
|
|
||||||
cipher.init(false, cipherParameters);
|
cipher.init(false, cipherParameters);
|
||||||
result = new byte[cipher.getOutputSize(dataLen + MAC_LEN)];
|
result = new byte[cipher.getOutputSize(dataLen + MAC_LEN)];
|
||||||
|
|
||||||
@@ -160,10 +147,8 @@ public class PacketTransformation {
|
|||||||
|
|
||||||
if (len != dataLen)
|
if (len != dataLen)
|
||||||
throw new IllegalArgumentException(len + " != " + dataLen);
|
throw new IllegalArgumentException(len + " != " + dataLen);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+49
-28
@@ -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.packet.transformation.PacketTransformation;
|
||||||
import com.github.manevolent.ts3j.protocol.socket.AbstractTeamspeakSocket;
|
import com.github.manevolent.ts3j.protocol.socket.AbstractTeamspeakSocket;
|
||||||
import com.github.manevolent.ts3j.util.Ts3Crypt;
|
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.io.IOException;
|
||||||
import java.net.DatagramPacket;
|
import java.net.DatagramPacket;
|
||||||
@@ -32,6 +32,10 @@ import java.util.concurrent.*;
|
|||||||
public abstract class AbstractTeamspeakClientSocket
|
public abstract class AbstractTeamspeakClientSocket
|
||||||
extends AbstractTeamspeakSocket
|
extends AbstractTeamspeakSocket
|
||||||
implements TeamspeakClientSocket {
|
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 Object connectionStateLock = new Object();
|
||||||
private final LinkedBlockingDeque<Packet> readQueue = new LinkedBlockingDeque<>(65536);
|
private final LinkedBlockingDeque<Packet> readQueue = new LinkedBlockingDeque<>(65536);
|
||||||
private final Map<PacketBodyType, Reassembly> reassemblyQueue = new LinkedHashMap<>();
|
private final Map<PacketBodyType, Reassembly> reassemblyQueue = new LinkedHashMap<>();
|
||||||
@@ -85,7 +89,7 @@ public abstract class AbstractTeamspeakClientSocket
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final void setSecureParameters(Ts3Crypt.SecureChannelParameters parameters) {
|
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;
|
protected abstract NetworkPacket readNetworkPacket() throws IOException;
|
||||||
@@ -95,7 +99,7 @@ public abstract class AbstractTeamspeakClientSocket
|
|||||||
public void setState(ClientConnectionState state) throws IOException, TimeoutException {
|
public void setState(ClientConnectionState state) throws IOException, TimeoutException {
|
||||||
synchronized (connectionStateLock) {
|
synchronized (connectionStateLock) {
|
||||||
if (state != this.connectionState) {
|
if (state != this.connectionState) {
|
||||||
Ts3Logging.debug("State changing: " + state.name());
|
Ts3Debugging.debug("State changing: " + state.name());
|
||||||
|
|
||||||
boolean wasDisconnected = this.connectionState == ClientConnectionState.DISCONNECTED;
|
boolean wasDisconnected = this.connectionState == ClientConnectionState.DISCONNECTED;
|
||||||
|
|
||||||
@@ -110,7 +114,7 @@ public abstract class AbstractTeamspeakClientSocket
|
|||||||
if (state == ClientConnectionState.DISCONNECTED)
|
if (state == ClientConnectionState.DISCONNECTED)
|
||||||
clientOptions.clear();
|
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);
|
((LocalClientHandler) handler).handleConnectionStateChanging(state);
|
||||||
|
|
||||||
if (handler == null || handler.getClass() != state.getHandlerClass()) {
|
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));
|
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
|
// Ensure type matches
|
||||||
packet.getHeader().setType(packet.getBody().getType());
|
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
|
// Set header values
|
||||||
packet.getBody().setHeaderValues(packet.getHeader());
|
packet.getBody().setHeaderValues(packet.getHeader());
|
||||||
|
|
||||||
@@ -226,6 +226,10 @@ public abstract class AbstractTeamspeakClientSocket
|
|||||||
packet.getHeader().setPacketId(packetId);
|
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.getSize() > 500) {
|
||||||
if (!packet.getHeader().getType().isSplittable())
|
if (!packet.getHeader().getType().isSplittable())
|
||||||
throw new IllegalArgumentException("packet too large: " + packet.getSize());
|
throw new IllegalArgumentException("packet too large: " + packet.getSize());
|
||||||
@@ -272,10 +276,18 @@ public abstract class AbstractTeamspeakClientSocket
|
|||||||
// Flush to a buffer
|
// Flush to a buffer
|
||||||
ByteBuffer outputBuffer;
|
ByteBuffer outputBuffer;
|
||||||
|
|
||||||
if (packet.getBody().getType().canEncrypt() &&
|
if (!packet.getHeader().getPacketFlag(HeaderFlag.UNENCRYPTED)) {
|
||||||
!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);
|
outputBuffer = getTransformation().encrypt(packet);
|
||||||
} else {
|
} 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 = ByteBuffer.allocate(packet.getSize());
|
||||||
|
|
||||||
outputBuffer.order(ByteOrder.BIG_ENDIAN);
|
outputBuffer.order(ByteOrder.BIG_ENDIAN);
|
||||||
@@ -304,7 +316,10 @@ public abstract class AbstractTeamspeakClientSocket
|
|||||||
else
|
else
|
||||||
response = null;
|
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);
|
writeNetworkPacket(networkPacket);
|
||||||
|
|
||||||
// If necessary, fulfill promise for the packet acknowledgement or throw TimeoutException
|
// 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);
|
boolean fragment = networkPacket.getHeader().getPacketFlag(HeaderFlag.FRAGMENTED);
|
||||||
|
|
||||||
if (networkPacket.getHeader().getType().isSplittable()) {
|
if (networkPacket.getHeader().getType().isSplittable()) {
|
||||||
if (fragment)
|
fragment = fragment || reassemblyQueue.get(networkPacket.getHeader().getType()).isReassembling();
|
||||||
throw new IllegalArgumentException("packet is fragment, but not splittable");
|
} else if (fragment)
|
||||||
else {
|
throw new IllegalArgumentException("packet is fragment, but not splittable");
|
||||||
fragment = reassemblyQueue.get(networkPacket.getHeader().getType()).isReassembling();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean encrypted = !networkPacket.getHeader().getPacketFlag(HeaderFlag.UNENCRYPTED) &&
|
boolean encrypted = !networkPacket.getHeader().getPacketFlag(HeaderFlag.UNENCRYPTED) &&
|
||||||
networkPacket.getHeader().getType().canEncrypt();
|
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
|
// Fragment handling
|
||||||
if (fragment) {
|
if (fragment) {
|
||||||
Ts3Logging.debug("[PROTOCOL] READ " + networkPacket.getHeader().getType().name() + " fragment");
|
Ts3Debugging.debug("[PROTOCOL] READ " + networkPacket.getHeader().getType().name() + " fragment");
|
||||||
|
|
||||||
packet.setBody(
|
packet.setBody(
|
||||||
new PacketBodyFragment(
|
new PacketBodyFragment(
|
||||||
@@ -381,7 +398,7 @@ public abstract class AbstractTeamspeakClientSocket
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
Ts3Logging.debug("[PROTOCOL] READ " + networkPacket.getHeader().getType().name());
|
Ts3Debugging.debug("[PROTOCOL] READ " + networkPacket.getHeader().getType().name());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compression?
|
// 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
|
// Find if the packet is itself an acknowledgement
|
||||||
switch (packet.getHeader().getType()) {
|
switch (packet.getHeader().getType()) {
|
||||||
case ACK:
|
case ACK:
|
||||||
@@ -504,13 +527,13 @@ public abstract class AbstractTeamspeakClientSocket
|
|||||||
try {
|
try {
|
||||||
Packet packet = readQueue.take();
|
Packet packet = readQueue.take();
|
||||||
|
|
||||||
Ts3Logging.debug("Processing " + packet + "...");
|
Ts3Debugging.debug("Processing " + packet + "...");
|
||||||
getHandler().handlePacket(packet);
|
getHandler().handlePacket(packet);
|
||||||
Ts3Logging.debug("Processed " + packet + ".");
|
Ts3Debugging.debug("Processed " + packet + ".");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (e instanceof InterruptedException) continue;
|
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) {
|
} catch (Exception e) {
|
||||||
if (e instanceof InterruptedException) continue;
|
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))
|
if (!(packet.getBody() instanceof PacketBodyFragment))
|
||||||
throw new IllegalArgumentException("packet fragment object is not representative of a fragment");
|
throw new IllegalArgumentException("packet fragment object is not representative of a fragment");
|
||||||
|
|
||||||
boolean fragmentationEnded = oldState;
|
|
||||||
|
|
||||||
queue.put(packet.getHeader().getPacketId(), packet);
|
queue.put(packet.getHeader().getPacketId(), packet);
|
||||||
|
|
||||||
if (fragmentationEnded)
|
if (oldState)
|
||||||
return reassemble(packet);
|
return reassemble(packet);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
+5
-4
@@ -4,7 +4,7 @@ import com.github.manevolent.ts3j.identity.LocalIdentity;
|
|||||||
import com.github.manevolent.ts3j.protocol.NetworkPacket;
|
import com.github.manevolent.ts3j.protocol.NetworkPacket;
|
||||||
import com.github.manevolent.ts3j.protocol.client.ClientConnectionState;
|
import com.github.manevolent.ts3j.protocol.client.ClientConnectionState;
|
||||||
import com.github.manevolent.ts3j.protocol.header.PacketHeader;
|
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.io.IOException;
|
||||||
import java.net.DatagramPacket;
|
import java.net.DatagramPacket;
|
||||||
@@ -36,7 +36,7 @@ public class LocalTeamspeakClientSocket extends AbstractTeamspeakClientSocket {
|
|||||||
protected NetworkPacket readNetworkPacket() throws IOException {
|
protected NetworkPacket readNetworkPacket() throws IOException {
|
||||||
socket.receive(packet);
|
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;
|
PacketHeader header;
|
||||||
|
|
||||||
@@ -61,10 +61,11 @@ public class LocalTeamspeakClientSocket extends AbstractTeamspeakClientSocket {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void writeNetworkPacket(NetworkPacket packet) throws IOException {
|
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()
|
packet.getDatagram().getLength()
|
||||||
+ " to " +
|
+ " to " +
|
||||||
socket.getRemoteSocketAddress());
|
socket.getRemoteSocketAddress());
|
||||||
|
Ts3Debugging.debug("[NETWORK] WRITE " + Ts3Debugging.getHex(packet.getBuffer().array()));
|
||||||
|
|
||||||
socket.send(packet.getDatagram());
|
socket.send(packet.getDatagram());
|
||||||
}
|
}
|
||||||
@@ -89,7 +90,7 @@ public class LocalTeamspeakClientSocket extends AbstractTeamspeakClientSocket {
|
|||||||
public void connect(InetSocketAddress remote, String password, long timeout)
|
public void connect(InetSocketAddress remote, String password, long timeout)
|
||||||
throws IOException, TimeoutException {
|
throws IOException, TimeoutException {
|
||||||
try {
|
try {
|
||||||
Ts3Logging.debug("Connecting to " + remote + "...");
|
Ts3Debugging.debug("Connecting to " + remote + "...");
|
||||||
|
|
||||||
ClientConnectionState connectionState = getState();
|
ClientConnectionState connectionState = getState();
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import Punisher.NaCl.Internal.Ed25519Ref10.GroupElementP3;
|
|||||||
import Punisher.NaCl.Internal.Ed25519Ref10.GroupOperations;
|
import Punisher.NaCl.Internal.Ed25519Ref10.GroupOperations;
|
||||||
import Punisher.NaCl.Internal.Ed25519Ref10.ScalarOperations;
|
import Punisher.NaCl.Internal.Ed25519Ref10.ScalarOperations;
|
||||||
import Punisher.NaCl.Internal.Sha512;
|
import Punisher.NaCl.Internal.Sha512;
|
||||||
|
import com.github.manevolent.ts3j.identity.*;
|
||||||
import com.github.manevolent.ts3j.license.License;
|
import com.github.manevolent.ts3j.license.License;
|
||||||
import javafx.util.Pair;
|
import javafx.util.Pair;
|
||||||
import org.bouncycastle.asn1.*;
|
import org.bouncycastle.asn1.*;
|
||||||
@@ -22,34 +23,50 @@ import java.io.ByteArrayInputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.security.MessageDigest;
|
import java.security.*;
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public final class Ts3Crypt {
|
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,
|
public static Ts3Crypt.SecureChannelParameters cryptoInit2(byte[] license, byte[] alpha,
|
||||||
byte[] omega, byte[] proof,
|
byte[] omega, byte[] proof,
|
||||||
byte[] beta, byte[] privateKey) {
|
byte[] beta, byte[] privateKey) {
|
||||||
// 3.2.2.2 Parsing the license
|
// 3.2.2.2 Parsing the license
|
||||||
List<License> licenses = License.readLicenses(ByteBuffer.wrap(license));
|
List<License> licenses = License.readLicenses(ByteBuffer.wrap(license));
|
||||||
|
|
||||||
Ts3Logging.debug("License.deriveKey: " + Ts3Logging.getHex(license));
|
|
||||||
byte[] key = License.deriveKey(licenses);
|
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);
|
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.SecureChannelParameters parameters =
|
||||||
Ts3Crypt.getSecureParameters(alpha, beta, sharedSecret);
|
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;
|
return parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,6 +79,30 @@ public final class Ts3Crypt {
|
|||||||
outBuf[i + outOffs] = (byte)(a[i + aoffs] ^ b[i + boffs]);
|
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) {
|
public static SecureChannelParameters getSecureParameters(byte[] alpha, byte[] beta, byte[] sharedKey) {
|
||||||
if (beta.length != 10 && beta.length != 54)
|
if (beta.length != 10 && beta.length != 54)
|
||||||
throw new IllegalArgumentException("invalid beta size (" + beta.length + ")");
|
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, 0, alpha, 0, alpha.length, ivStruct, 0);
|
||||||
xor(sharedKey, 10, beta, 0, beta.length, ivStruct, 10);
|
xor(sharedKey, 10, beta, 0, beta.length, ivStruct, 10);
|
||||||
|
|
||||||
byte[] buffer;
|
byte[] buffer = hash128(ivStruct);
|
||||||
|
|
||||||
try {
|
|
||||||
buffer = MessageDigest.getInstance("SHA1").digest(ivStruct);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
System.arraycopy(buffer, 0, fakeSignature, 0, 8);
|
System.arraycopy(buffer, 0, fakeSignature, 0, 8);
|
||||||
|
|
||||||
@@ -191,6 +226,9 @@ public final class Ts3Crypt {
|
|||||||
byte[] publicKey = new byte[32];
|
byte[] publicKey = new byte[32];
|
||||||
GroupOperations.ge_p3_tobytes(publicKey, 0, A);
|
GroupOperations.ge_p3_tobytes(publicKey, 0, A);
|
||||||
|
|
||||||
|
Ts3Debugging.debug(publicKey);
|
||||||
|
Ts3Debugging.debug(privateKey);
|
||||||
|
|
||||||
return new Pair<>(publicKey, privateKey);
|
return new Pair<>(publicKey, privateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
-1
@@ -1,6 +1,16 @@
|
|||||||
package com.github.manevolent.ts3j.util;
|
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";
|
private static final String HEXES = "0123456789ABCDEF";
|
||||||
public static String getHex(byte[] raw) {
|
public static String getHex(byte[] raw) {
|
||||||
return getHex(raw, raw.length);
|
return getHex(raw, raw.length);
|
||||||
@@ -22,6 +32,10 @@ public final class Ts3Logging {
|
|||||||
System.err.println("[DEBUG] " + message.toString());
|
System.err.println("[DEBUG] " + message.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void debug(byte[] message) {
|
||||||
|
debug(getHex(message));
|
||||||
|
}
|
||||||
|
|
||||||
public static void debug(String message, Throwable ex) {
|
public static void debug(String message, Throwable ex) {
|
||||||
System.err.println("[DEBUG] " + message);
|
System.err.println("[DEBUG] " + message);
|
||||||
ex.printStackTrace();
|
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.identity.LocalIdentity;
|
||||||
import com.github.manevolent.ts3j.protocol.client.ClientConnectionState;
|
import com.github.manevolent.ts3j.protocol.client.ClientConnectionState;
|
||||||
import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket;
|
import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket;
|
||||||
|
import com.github.manevolent.ts3j.util.Ts3Crypt;
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
public class ServerConnectionTest extends TestCase {
|
public class ServerConnectionTest extends TestCase {
|
||||||
public ServerConnectionTest(String name) {
|
public ServerConnectionTest() {
|
||||||
super(name);
|
super("ServerConnectionTest");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testParser() throws Exception {
|
public void testParser() throws Exception {
|
||||||
try {
|
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();
|
LocalTeamspeakClientSocket client = new LocalTeamspeakClientSocket();
|
||||||
|
|
||||||
client.setIdentity(localIdentity);
|
client.setIdentity(localIdentity);
|
||||||
|
|
||||||
client.connect(new InetSocketAddress(
|
client.connect(new InetSocketAddress(
|
||||||
"ts.teamlixo.net",
|
"voice.teamspeak.com",
|
||||||
9987),
|
9987),
|
||||||
null,
|
null,
|
||||||
10000L
|
10000L
|
||||||
@@ -32,4 +41,8 @@ public class ServerConnectionTest extends TestCase {
|
|||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
new ServerConnectionTest().testParser();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user