From 66cd3d611686472d49e41ca3cda3d3b10c270a0e Mon Sep 17 00:00:00 2001 From: Manevolent Date: Mon, 23 Jul 2018 22:44:38 -0400 Subject: [PATCH] Working on encryption --- .../manevolent/ts3j/command/Commands.java | 20 +++ .../manevolent/ts3j/command/TypedCommand.java | 10 ++ .../type/server/ServerInitIVExpand.java | 15 +++ .../type/server/ServerInitIVExpand2.java | 18 +++ .../manevolent/ts3j/protocol/Endpoint.java | 1 + .../ts3j/protocol/LocalEndpoint.java | 7 +- .../ts3j/protocol/TeamspeakSocket.java | 12 -- .../protocol/client/LocalTeamspeakClient.java | 1 + .../ts3j/protocol/client/TeamspeakClient.java | 1 + .../protocol/header/ClientPacketHeader.java | 8 +- .../ts3j/protocol/header/PacketHeader.java | 18 +-- .../protocol/header/ServerPacketHeader.java | 9 +- .../ts3j/protocol/packet/Packet2Command.java | 26 ++++ .../ts3j/protocol/packet/PacketType.java | 53 ++++++-- .../client/LocalClientHandlerConnecting.java | 5 + .../DefaultPacketTransformation.java | 22 ++++ .../transformation/PacketTransformation.java | 116 ++++++++++++++++++ .../socket/AbstractTeamspeakSocket.java | 18 +++ .../protocol/socket/LocalTeamspeakSocket.java | 31 ++++- .../ts3j/protocol/socket/TeamspeakSocket.java | 19 +++ 20 files changed, 372 insertions(+), 38 deletions(-) create mode 100644 src/main/java/com/github/manevolent/ts3j/command/Commands.java create mode 100644 src/main/java/com/github/manevolent/ts3j/command/type/server/ServerInitIVExpand.java create mode 100644 src/main/java/com/github/manevolent/ts3j/command/type/server/ServerInitIVExpand2.java delete mode 100644 src/main/java/com/github/manevolent/ts3j/protocol/TeamspeakSocket.java create mode 100644 src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet2Command.java create mode 100644 src/main/java/com/github/manevolent/ts3j/protocol/packet/transformation/DefaultPacketTransformation.java create mode 100644 src/main/java/com/github/manevolent/ts3j/protocol/packet/transformation/PacketTransformation.java create mode 100644 src/main/java/com/github/manevolent/ts3j/protocol/socket/AbstractTeamspeakSocket.java create mode 100644 src/main/java/com/github/manevolent/ts3j/protocol/socket/TeamspeakSocket.java diff --git a/src/main/java/com/github/manevolent/ts3j/command/Commands.java b/src/main/java/com/github/manevolent/ts3j/command/Commands.java new file mode 100644 index 0000000..d54e966 --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/command/Commands.java @@ -0,0 +1,20 @@ +package com.github.manevolent.ts3j.command; + +import com.github.manevolent.ts3j.command.type.client.*; +import com.github.manevolent.ts3j.command.type.server.*; + +import java.util.LinkedHashMap; +import java.util.Map; + +public final class Commands { + private static final Map> commands = new LinkedHashMap<>(); + + static { + // Client + commands.put("clientinitiv", ClientInitIV.class); + + // Server + commands.put("initivexpand", ServerInitIVExpand.class); + commands.put("initivexpand2", ServerInitIVExpand2.class); + } +} diff --git a/src/main/java/com/github/manevolent/ts3j/command/TypedCommand.java b/src/main/java/com/github/manevolent/ts3j/command/TypedCommand.java index 4dc2b5f..83598f8 100644 --- a/src/main/java/com/github/manevolent/ts3j/command/TypedCommand.java +++ b/src/main/java/com/github/manevolent/ts3j/command/TypedCommand.java @@ -2,6 +2,8 @@ package com.github.manevolent.ts3j.command; import com.github.manevolent.ts3j.command.part.CommandParameter; import com.github.manevolent.ts3j.protocol.ProtocolRole; +import com.github.manevolent.ts3j.protocol.client.TeamspeakClient; +import com.github.manevolent.ts3j.protocol.server.TeamspeakServer; import java.util.Collections; import java.util.LinkedHashMap; @@ -47,4 +49,12 @@ public abstract class TypedCommand implements Command { public String toString() { return build(); } + + public void handleServer(TeamspeakServer server) { + + } + + public void handleClient(TeamspeakClient client) { + + } } diff --git a/src/main/java/com/github/manevolent/ts3j/command/type/server/ServerInitIVExpand.java b/src/main/java/com/github/manevolent/ts3j/command/type/server/ServerInitIVExpand.java new file mode 100644 index 0000000..2befdf3 --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/command/type/server/ServerInitIVExpand.java @@ -0,0 +1,15 @@ +package com.github.manevolent.ts3j.command.type.server; + +import com.github.manevolent.ts3j.command.TypedCommand; +import com.github.manevolent.ts3j.command.part.CommandSingleParameter; +import com.github.manevolent.ts3j.protocol.ProtocolRole; + +public class ServerInitIVExpand extends TypedCommand { + public ServerInitIVExpand() { + super(ProtocolRole.CLIENT, "initivexpand"); + + set(new CommandSingleParameter("alpha")); + set(new CommandSingleParameter("beta")); + set(new CommandSingleParameter("omega")); + } +} diff --git a/src/main/java/com/github/manevolent/ts3j/command/type/server/ServerInitIVExpand2.java b/src/main/java/com/github/manevolent/ts3j/command/type/server/ServerInitIVExpand2.java new file mode 100644 index 0000000..6048fdb --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/command/type/server/ServerInitIVExpand2.java @@ -0,0 +1,18 @@ +package com.github.manevolent.ts3j.command.type.server; + +import com.github.manevolent.ts3j.command.TypedCommand; +import com.github.manevolent.ts3j.command.part.CommandSingleParameter; +import com.github.manevolent.ts3j.protocol.ProtocolRole; + +public class ServerInitIVExpand2 extends TypedCommand { + public ServerInitIVExpand2() { + super(ProtocolRole.CLIENT, "initivexpand2"); + + set(new CommandSingleParameter("l")); + set(new CommandSingleParameter("beta")); + set(new CommandSingleParameter("omega")); + set(new CommandSingleParameter("ot")); + set(new CommandSingleParameter("proof")); + set(new CommandSingleParameter("tvd")); + } +} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/Endpoint.java b/src/main/java/com/github/manevolent/ts3j/protocol/Endpoint.java index ed71802..a4e0553 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/Endpoint.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/Endpoint.java @@ -3,6 +3,7 @@ package com.github.manevolent.ts3j.protocol; import com.github.manevolent.ts3j.protocol.packet.Packet; import java.io.IOException; +import java.util.function.Function; public interface Endpoint { diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/LocalEndpoint.java b/src/main/java/com/github/manevolent/ts3j/protocol/LocalEndpoint.java index 4d9a299..2643884 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/LocalEndpoint.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/LocalEndpoint.java @@ -1,6 +1,7 @@ package com.github.manevolent.ts3j.protocol; import com.github.manevolent.ts3j.identity.LocalIdentity; +import com.github.manevolent.ts3j.protocol.header.HeaderFlag; import com.github.manevolent.ts3j.protocol.packet.handler.PacketHandler; import com.github.manevolent.ts3j.protocol.socket.LocalTeamspeakSocket; import com.github.manevolent.ts3j.util.Ts3Logging; @@ -77,7 +78,11 @@ public abstract class LocalEndpoint implements Endpoint { } protected void handlePacket(NetworkPacket packet) throws IOException { - if (this.handler != null) this.handler.handlePacket(packet); + if (packet.getHeader().getPacketFlag(HeaderFlag.FRAGMENTED)) { + + } else { + if (this.handler != null) this.handler.handlePacket(packet); + } } public LocalIdentity getLocalIdentity() { diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/TeamspeakSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/TeamspeakSocket.java deleted file mode 100644 index 2796847..0000000 --- a/src/main/java/com/github/manevolent/ts3j/protocol/TeamspeakSocket.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.github.manevolent.ts3j.protocol; - -import com.github.manevolent.ts3j.protocol.header.PacketHeader; -import com.github.manevolent.ts3j.protocol.packet.Packet; - -import java.io.IOException; - -public interface TeamspeakSocket { - SocketRole getSocketRole(); - - void send(PacketHeader header, Packet packet) throws IOException; -} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/client/LocalTeamspeakClient.java b/src/main/java/com/github/manevolent/ts3j/protocol/client/LocalTeamspeakClient.java index 3b0a502..b3213b5 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/client/LocalTeamspeakClient.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/client/LocalTeamspeakClient.java @@ -18,6 +18,7 @@ import java.net.SocketException; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.TimeoutException; +import java.util.function.Function; public class LocalTeamspeakClient extends LocalEndpoint implements TeamspeakClient { private final Object connectionStateLock = new Object(); diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/client/TeamspeakClient.java b/src/main/java/com/github/manevolent/ts3j/protocol/client/TeamspeakClient.java index b90ef8d..d97ec7c 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/client/TeamspeakClient.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/client/TeamspeakClient.java @@ -6,6 +6,7 @@ import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.Map; +import java.util.function.Function; public interface TeamspeakClient extends Endpoint { void connect(InetSocketAddress remote, String password, long timeout) throws IOException; diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/header/ClientPacketHeader.java b/src/main/java/com/github/manevolent/ts3j/protocol/header/ClientPacketHeader.java index 0b940c0..1b4c5ef 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/header/ClientPacketHeader.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/header/ClientPacketHeader.java @@ -21,7 +21,7 @@ public class ClientPacketHeader extends PacketHeader { } @Override - protected void writeHeader(ByteBuffer buffer) { + protected ByteBuffer writeHeader(ByteBuffer buffer) { super.writeHeader(buffer); // https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md @@ -30,11 +30,13 @@ public class ClientPacketHeader extends PacketHeader { buffer.put((byte) ((getType().getIndex() & 0x0F) | (getPacketFlags() & 0xF0))); // PT / Packet Type + Flags // (payload) -- see writeBody impl in higher levels + + return buffer; } @Override - protected void readHeader(ByteBuffer buffer) { + protected ByteBuffer readHeader(ByteBuffer buffer) { super.readHeader(buffer); // https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md @@ -46,6 +48,8 @@ public class ClientPacketHeader extends PacketHeader { setPacketFlags(packetTypeAndFlags & 0xF0); // (payload) -- see writeBody impl in higher levels + + return buffer; } @Override diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/header/PacketHeader.java b/src/main/java/com/github/manevolent/ts3j/protocol/header/PacketHeader.java index 710db54..43f5a00 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/header/PacketHeader.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/header/PacketHeader.java @@ -30,7 +30,7 @@ public abstract class PacketHeader { public boolean getPacketFlag(HeaderFlag flag) { int idx = flag.getIndex(); - return (packetFlags & idx) == idx; + return (getPacketFlags() & idx) == idx; } public void setPacketFlag(HeaderFlag flag, boolean value) { @@ -69,24 +69,28 @@ public abstract class PacketHeader { this.mac = mac; } - public final void write(ByteBuffer buffer) { - writeHeader(buffer); + public final ByteBuffer write(ByteBuffer buffer) { + return writeHeader(buffer); } - public final void read(ByteBuffer buffer) { - readHeader(buffer); + public final ByteBuffer read(ByteBuffer buffer) { + return readHeader(buffer); } - protected void writeHeader(ByteBuffer buffer) { + protected ByteBuffer writeHeader(ByteBuffer buffer) { // https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md buffer.put(getMac(), 0, 8); buffer.putShort((short) (getPacketId() & 0x0000FFFF)); // PiD / Packet ID + + return buffer; } - protected void readHeader(ByteBuffer buffer) { + protected ByteBuffer readHeader(ByteBuffer buffer) { // https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md buffer.get(mac); setPacketId((int) buffer.getShort() & 0x0000FFFF); + + return buffer; } public int getSize() { diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/header/ServerPacketHeader.java b/src/main/java/com/github/manevolent/ts3j/protocol/header/ServerPacketHeader.java index d6b5d3e..ee90074 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/header/ServerPacketHeader.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/header/ServerPacketHeader.java @@ -2,6 +2,7 @@ package com.github.manevolent.ts3j.protocol.header; import com.github.manevolent.ts3j.protocol.ProtocolRole; import com.github.manevolent.ts3j.protocol.packet.PacketType; +import com.github.manevolent.ts3j.util.Ts3Logging; import java.nio.ByteBuffer; @@ -21,7 +22,7 @@ public class ServerPacketHeader extends PacketHeader { } @Override - protected void writeHeader(ByteBuffer buffer) { + protected ByteBuffer writeHeader(ByteBuffer buffer) { super.writeHeader(buffer); // https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md @@ -29,10 +30,12 @@ public class ServerPacketHeader extends PacketHeader { buffer.put((byte) ((getType().getIndex() & 0x0F) | (getPacketFlags() & 0xF0))); // PT / Packet Type + Flags // (payload) -- see writeBody impl in higher levels + + return buffer; } @Override - protected void readHeader(ByteBuffer buffer) { + protected ByteBuffer readHeader(ByteBuffer buffer) { super.readHeader(buffer); // https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md @@ -40,6 +43,8 @@ public class ServerPacketHeader extends PacketHeader { setType(PacketType.fromId((packetTypeAndFlags & 0x0F))); // PT / Packet Type + Flags setPacketFlags(packetTypeAndFlags & 0xF0); // (payload) -- see writeBody impl in higher levels + + return buffer; } @Override diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet2Command.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet2Command.java new file mode 100644 index 0000000..d4c35a8 --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet2Command.java @@ -0,0 +1,26 @@ +package com.github.manevolent.ts3j.protocol.packet; + +import com.github.manevolent.ts3j.protocol.ProtocolRole; + +import java.nio.ByteBuffer; + +public class Packet2Command extends Packet { + public Packet2Command(ProtocolRole role) { + super(PacketType.COMMAND, role); + } + + @Override + public void read(ByteBuffer buffer) { + + } + + @Override + public void write(ByteBuffer buffer) { + + } + + @Override + public int getSize() { + return 0; + } +} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketType.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketType.java index 1b45b86..3c5b77e 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketType.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketType.java @@ -1,23 +1,36 @@ package com.github.manevolent.ts3j.protocol.packet; public enum PacketType { - VOICE(0x0, null), - VOICE_WHISPER(0x1, null), - COMMAND(0x2, null), - COMMAND_LOW(0x3, null), - PING(0x4, null), - PONG(0x5, null), - ACK(0x6, null), - ACK_LOW(0x7, null), - INIT1(0x8, Packet8Init1.class) + VOICE(0x0, null, null, false, false, false, false), + VOICE_WHISPER(0x1, null, null, false, false, false, false), + PONG(0x5, null, null, false, false, false, false), + PING(0x4, null, PONG, false, false, false, false), + ACK(0x6, null, null, true, true, false, false), + ACK_LOW(0x7, null, null, true, true, false, false), + INIT1(0x8, Packet8Init1.class, null, false, true, false, false), + COMMAND_LOW(0x3, null, ACK_LOW, true, true, true, true), + COMMAND(0x2, Packet2Command.class, ACK, true, true, true, true) ; private final int index; private final Class packetClass; - PacketType(int index, Class packetClass) { + private final PacketType acknolwedgedBy; + private final boolean encrypted, resend, splittable, compressible; + + PacketType(int index, Class packetClass, + PacketType acknolwedgedBy, + boolean encrypted, + boolean resend, + boolean splittable, + boolean compressible) { this.index = index; this.packetClass = packetClass; + this.acknolwedgedBy = acknolwedgedBy; + this.resend = resend; + this.encrypted = encrypted; + this.splittable = splittable; + this.compressible = compressible; } public int getIndex() { @@ -34,4 +47,24 @@ public enum PacketType { public Class getPacketClass() { return packetClass; } + + public boolean isEncrypted() { + return encrypted; + } + + public PacketType getAcknolwedgedBy() { + return acknolwedgedBy; + } + + public boolean canResend() { + return resend; + } + + public boolean isSplittable() { + return splittable; + } + + public boolean isCompressible() { + return compressible; + } } diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnecting.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnecting.java index 153c825..37bae27 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnecting.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnecting.java @@ -4,6 +4,7 @@ import com.github.manevolent.ts3j.command.type.client.ClientInitIV; import com.github.manevolent.ts3j.protocol.NetworkPacket; import com.github.manevolent.ts3j.protocol.ProtocolRole; import com.github.manevolent.ts3j.protocol.client.LocalTeamspeakClient; +import com.github.manevolent.ts3j.protocol.packet.Packet2Command; import com.github.manevolent.ts3j.protocol.packet.Packet8Init1; import com.github.manevolent.ts3j.util.Ts3Logging; @@ -25,6 +26,8 @@ public class LocalClientHandlerConnecting extends LocalClientHandler { public void onAssigned() throws IOException { Packet8Init1 packet = new Packet8Init1(ProtocolRole.CLIENT); + // Initialize connection + Packet8Init1.Step0 step = new Packet8Init1.Step0(); Random random = new Random(); randomBytes = new byte[4]; @@ -132,6 +135,8 @@ public class LocalClientHandlerConnecting extends LocalClientHandler { throw new IllegalArgumentException("unexpected Init1 server step: " + init1.getStep().getNumber()); } + } else if (packet.getPacket() instanceof Packet2Command) { + } } } diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/transformation/DefaultPacketTransformation.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/transformation/DefaultPacketTransformation.java new file mode 100644 index 0000000..b1759d6 --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/transformation/DefaultPacketTransformation.java @@ -0,0 +1,22 @@ +package com.github.manevolent.ts3j.protocol.packet.transformation; + + +import com.github.manevolent.ts3j.protocol.header.PacketHeader; +import javafx.util.Pair; + +public class DefaultPacketTransformation extends PacketTransformation { + private static final byte[] key = + {0x63, 0x3A, 0x5C, 0x77, 0x69, 0x6E, 0x64, 0x6F, 0x77, 0x73, 0x5C, 0x73, 0x79, 0x73, 0x74, 0x65}; + + private static final byte[] nonce = + {0x6D, 0x5C, 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6C, 0x6C, 0x33, 0x32, 0x2E, 0x63, 0x70, 0x6C}; + + public DefaultPacketTransformation() { + super(key); + } + + @Override + public Pair computeParameters(PacketHeader header) { + return new Pair<>(key, nonce); + } +} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/transformation/PacketTransformation.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/transformation/PacketTransformation.java new file mode 100644 index 0000000..0bb7d87 --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/transformation/PacketTransformation.java @@ -0,0 +1,116 @@ +package com.github.manevolent.ts3j.protocol.packet.transformation; + +import com.github.manevolent.ts3j.protocol.ProtocolRole; +import com.github.manevolent.ts3j.protocol.header.PacketHeader; +import com.github.manevolent.ts3j.util.Ts3Logging; +import javafx.util.Pair; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.engines.AESEngine; +import org.bouncycastle.crypto.modes.EAXBlockCipher; +import org.bouncycastle.crypto.params.AEADParameters; +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 MessageDigest SHA256; + + { + try { + SHA256 = MessageDigest.getInstance("SHA256"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + private final byte[] key; + + private final EAXBlockCipher cipher; + + public PacketTransformation(byte[] key) { + this.key = key; + this.cipher = new EAXBlockCipher(new AESEngine()); + } + + public Pair computeParameters(PacketHeader header) { + ByteBuffer temporaryByteBuffer = ByteBuffer + .allocate(key.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) 0); // TODO generation + + 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); + } + + byte[] key_nonce = SHA256.digest(temporaryByteBuffer.array()); + + byte[] key = new byte[16]; + byte[] nonce = new byte[16]; + + 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) + + key[0] = (byte) (key[0] ^ ((byte) (header.getType().getIndex() & 0xFF00) >> 8)); + key[1] = (byte) (key[0] ^ ((byte) (header.getType().getIndex() & 0x00FF) >> 0)); + + return new Pair<>(key, nonce); + } + + public byte[] encrypt(PacketHeader header, byte[] body) { + return transform(header, body, true); + } + + public byte[] decrypt(PacketHeader header, byte[] body) { + return transform(header, body, false); + } + + private byte[] transform(PacketHeader header, byte[] body, boolean encrypt) { + Pair parameters = computeParameters(header); + + byte[] associatedText = new byte[8]; + + CipherParameters cipherParameters = new AEADParameters(new KeyParameter( + parameters.getKey()), + 8 * associatedText.length, + parameters.getValue(), + encrypt ? associatedText : header.getMac() + ); + + byte[] result; + int len; + synchronized (cipher){ + cipher.init(encrypt, cipherParameters); + + result = new byte[cipher.getOutputSize(body.length)]; + len = cipher.processBytes(body, 0, body.length, result, 0); + + try { + len += cipher.doFinal(result, len); + } catch (InvalidCipherTextException e) { + throw new RuntimeException(e); + } + } + + byte[] output = new byte[len]; + System.arraycopy(result, 0, output, 0, len); + + if (encrypt) + header.setMac(associatedText); + + return output; + } +} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/socket/AbstractTeamspeakSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/AbstractTeamspeakSocket.java new file mode 100644 index 0000000..1716064 --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/AbstractTeamspeakSocket.java @@ -0,0 +1,18 @@ +package com.github.manevolent.ts3j.protocol.socket; + +import com.github.manevolent.ts3j.protocol.packet.transformation.DefaultPacketTransformation; +import com.github.manevolent.ts3j.protocol.packet.transformation.PacketTransformation; + +public abstract class AbstractTeamspeakSocket implements TeamspeakSocket { + private PacketTransformation transformation = new DefaultPacketTransformation(); + + @Override + public PacketTransformation getPacketTransformation() { + return transformation; + } + + @Override + public void setPacketTransformation(PacketTransformation transformation) { + this.transformation = transformation; + } +} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/socket/LocalTeamspeakSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/LocalTeamspeakSocket.java index b0ff10d..701666f 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/socket/LocalTeamspeakSocket.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/LocalTeamspeakSocket.java @@ -2,7 +2,7 @@ package com.github.manevolent.ts3j.protocol.socket; import com.github.manevolent.ts3j.protocol.NetworkPacket; import com.github.manevolent.ts3j.protocol.SocketRole; -import com.github.manevolent.ts3j.protocol.TeamspeakSocket; +import com.github.manevolent.ts3j.protocol.header.HeaderFlag; import com.github.manevolent.ts3j.protocol.header.PacketHeader; import com.github.manevolent.ts3j.protocol.packet.Packet; import com.github.manevolent.ts3j.util.Ts3Logging; @@ -17,7 +17,7 @@ import java.nio.ByteOrder; * * Used simply to transcribe packets on the network */ -public class LocalTeamspeakSocket implements TeamspeakSocket { +public class LocalTeamspeakSocket extends AbstractTeamspeakSocket implements TeamspeakSocket { private final SocketRole socketRole; private final DatagramSocket datagramSocket; @@ -88,7 +88,20 @@ public class LocalTeamspeakSocket implements TeamspeakSocket { ByteBuffer outputBuffer = ByteBuffer.allocate(networkPacket.getSize()); outputBuffer.order(ByteOrder.BIG_ENDIAN); - networkPacket.write(outputBuffer); + networkPacket.writeHeader(outputBuffer); + + if (networkPacket.getHeader().getType().isEncrypted() && + !networkPacket.getHeader().getPacketFlag(HeaderFlag.UNENCRYPTED)) { + ByteBuffer encryptionBuffer = ByteBuffer.allocate(500); + networkPacket.writeBody(encryptionBuffer); + + byte[] unencryptedBody = new byte[encryptionBuffer.position()]; + System.arraycopy(encryptionBuffer.array(), 0, unencryptedBody, 0, unencryptedBody.length); + + outputBuffer.put(getPacketTransformation().encrypt(header, unencryptedBody)); + } else { + networkPacket.writeBody(outputBuffer); + } Ts3Logging.debug("[PROTOCOL] WRITE " + packet.getType().name()); @@ -120,7 +133,17 @@ public class LocalTeamspeakSocket implements TeamspeakSocket { networkPacket.readHeader(buffer); Ts3Logging.debug("[PROTOCOL] READ " + networkPacket.getHeader().getType().name()); - networkPacket.readBody(buffer); + + if (networkPacket.getHeader().getType().isEncrypted()) { + byte[] transformBuffer = new byte[buffer.remaining()]; + buffer.get(transformBuffer); + + networkPacket.readBody(ByteBuffer.wrap( + getPacketTransformation().decrypt(networkPacket.getHeader(), transformBuffer) + )); + } else { + networkPacket.readBody(buffer); + } return networkPacket; } diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/socket/TeamspeakSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/TeamspeakSocket.java new file mode 100644 index 0000000..afd9f4d --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/TeamspeakSocket.java @@ -0,0 +1,19 @@ +package com.github.manevolent.ts3j.protocol.socket; + +import com.github.manevolent.ts3j.protocol.SocketRole; +import com.github.manevolent.ts3j.protocol.header.PacketHeader; +import com.github.manevolent.ts3j.protocol.packet.Packet; +import com.github.manevolent.ts3j.protocol.packet.transformation.PacketTransformation; + +import java.io.IOException; + +public interface TeamspeakSocket { + SocketRole getSocketRole(); + + void send(PacketHeader header, Packet packet) throws IOException; + + PacketTransformation getPacketTransformation(); + + void setPacketTransformation(PacketTransformation transformation); + +}