Working on encryption
This commit is contained in:
@@ -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<String, Class<? extends TypedCommand>> commands = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
// Client
|
||||||
|
commands.put("clientinitiv", ClientInitIV.class);
|
||||||
|
|
||||||
|
// Server
|
||||||
|
commands.put("initivexpand", ServerInitIVExpand.class);
|
||||||
|
commands.put("initivexpand2", ServerInitIVExpand2.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@ package com.github.manevolent.ts3j.command;
|
|||||||
|
|
||||||
import com.github.manevolent.ts3j.command.part.CommandParameter;
|
import com.github.manevolent.ts3j.command.part.CommandParameter;
|
||||||
import com.github.manevolent.ts3j.protocol.ProtocolRole;
|
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.Collections;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@@ -47,4 +49,12 @@ public abstract class TypedCommand implements Command {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return build();
|
return build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void handleServer(TeamspeakServer server) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleClient(TeamspeakClient client) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package com.github.manevolent.ts3j.protocol;
|
|||||||
import com.github.manevolent.ts3j.protocol.packet.Packet;
|
import com.github.manevolent.ts3j.protocol.packet.Packet;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
public interface Endpoint {
|
public interface Endpoint {
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.github.manevolent.ts3j.protocol;
|
package com.github.manevolent.ts3j.protocol;
|
||||||
|
|
||||||
import com.github.manevolent.ts3j.identity.LocalIdentity;
|
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.packet.handler.PacketHandler;
|
||||||
import com.github.manevolent.ts3j.protocol.socket.LocalTeamspeakSocket;
|
import com.github.manevolent.ts3j.protocol.socket.LocalTeamspeakSocket;
|
||||||
import com.github.manevolent.ts3j.util.Ts3Logging;
|
import com.github.manevolent.ts3j.util.Ts3Logging;
|
||||||
@@ -77,7 +78,11 @@ public abstract class LocalEndpoint implements Endpoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void handlePacket(NetworkPacket packet) throws IOException {
|
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() {
|
public LocalIdentity getLocalIdentity() {
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -18,6 +18,7 @@ import java.net.SocketException;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class LocalTeamspeakClient extends LocalEndpoint implements TeamspeakClient {
|
public class LocalTeamspeakClient extends LocalEndpoint implements TeamspeakClient {
|
||||||
private final Object connectionStateLock = new Object();
|
private final Object connectionStateLock = new Object();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import java.io.IOException;
|
|||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
public interface TeamspeakClient extends Endpoint {
|
public interface TeamspeakClient extends Endpoint {
|
||||||
void connect(InetSocketAddress remote, String password, long timeout) throws IOException;
|
void connect(InetSocketAddress remote, String password, long timeout) throws IOException;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public class ClientPacketHeader extends PacketHeader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void writeHeader(ByteBuffer buffer) {
|
protected ByteBuffer writeHeader(ByteBuffer buffer) {
|
||||||
super.writeHeader(buffer);
|
super.writeHeader(buffer);
|
||||||
|
|
||||||
// https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md
|
// 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
|
buffer.put((byte) ((getType().getIndex() & 0x0F) | (getPacketFlags() & 0xF0))); // PT / Packet Type + Flags
|
||||||
|
|
||||||
// (payload) -- see writeBody impl in higher levels
|
// (payload) -- see writeBody impl in higher levels
|
||||||
|
|
||||||
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void readHeader(ByteBuffer buffer) {
|
protected ByteBuffer readHeader(ByteBuffer buffer) {
|
||||||
super.readHeader(buffer);
|
super.readHeader(buffer);
|
||||||
|
|
||||||
// https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md
|
// https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md
|
||||||
@@ -46,6 +48,8 @@ public class ClientPacketHeader extends PacketHeader {
|
|||||||
setPacketFlags(packetTypeAndFlags & 0xF0);
|
setPacketFlags(packetTypeAndFlags & 0xF0);
|
||||||
|
|
||||||
// (payload) -- see writeBody impl in higher levels
|
// (payload) -- see writeBody impl in higher levels
|
||||||
|
|
||||||
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public abstract class PacketHeader {
|
|||||||
public boolean getPacketFlag(HeaderFlag flag) {
|
public boolean getPacketFlag(HeaderFlag flag) {
|
||||||
int idx = flag.getIndex();
|
int idx = flag.getIndex();
|
||||||
|
|
||||||
return (packetFlags & idx) == idx;
|
return (getPacketFlags() & idx) == idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPacketFlag(HeaderFlag flag, boolean value) {
|
public void setPacketFlag(HeaderFlag flag, boolean value) {
|
||||||
@@ -69,24 +69,28 @@ public abstract class PacketHeader {
|
|||||||
this.mac = mac;
|
this.mac = mac;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void write(ByteBuffer buffer) {
|
public final ByteBuffer write(ByteBuffer buffer) {
|
||||||
writeHeader(buffer);
|
return writeHeader(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void read(ByteBuffer buffer) {
|
public final ByteBuffer read(ByteBuffer buffer) {
|
||||||
readHeader(buffer);
|
return readHeader(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void writeHeader(ByteBuffer buffer) {
|
protected ByteBuffer writeHeader(ByteBuffer buffer) {
|
||||||
// https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md
|
// https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md
|
||||||
buffer.put(getMac(), 0, 8);
|
buffer.put(getMac(), 0, 8);
|
||||||
buffer.putShort((short) (getPacketId() & 0x0000FFFF)); // PiD / Packet ID
|
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
|
// https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md
|
||||||
buffer.get(mac);
|
buffer.get(mac);
|
||||||
setPacketId((int) buffer.getShort() & 0x0000FFFF);
|
setPacketId((int) buffer.getShort() & 0x0000FFFF);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSize() {
|
public int getSize() {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.github.manevolent.ts3j.protocol.header;
|
|||||||
|
|
||||||
import com.github.manevolent.ts3j.protocol.ProtocolRole;
|
import com.github.manevolent.ts3j.protocol.ProtocolRole;
|
||||||
import com.github.manevolent.ts3j.protocol.packet.PacketType;
|
import com.github.manevolent.ts3j.protocol.packet.PacketType;
|
||||||
|
import com.github.manevolent.ts3j.util.Ts3Logging;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
@@ -21,7 +22,7 @@ public class ServerPacketHeader extends PacketHeader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void writeHeader(ByteBuffer buffer) {
|
protected ByteBuffer writeHeader(ByteBuffer buffer) {
|
||||||
super.writeHeader(buffer);
|
super.writeHeader(buffer);
|
||||||
|
|
||||||
// https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md
|
// 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
|
buffer.put((byte) ((getType().getIndex() & 0x0F) | (getPacketFlags() & 0xF0))); // PT / Packet Type + Flags
|
||||||
|
|
||||||
// (payload) -- see writeBody impl in higher levels
|
// (payload) -- see writeBody impl in higher levels
|
||||||
|
|
||||||
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void readHeader(ByteBuffer buffer) {
|
protected ByteBuffer readHeader(ByteBuffer buffer) {
|
||||||
super.readHeader(buffer);
|
super.readHeader(buffer);
|
||||||
|
|
||||||
// https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md
|
// 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
|
setType(PacketType.fromId((packetTypeAndFlags & 0x0F))); // PT / Packet Type + Flags
|
||||||
setPacketFlags(packetTypeAndFlags & 0xF0);
|
setPacketFlags(packetTypeAndFlags & 0xF0);
|
||||||
// (payload) -- see writeBody impl in higher levels
|
// (payload) -- see writeBody impl in higher levels
|
||||||
|
|
||||||
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,23 +1,36 @@
|
|||||||
package com.github.manevolent.ts3j.protocol.packet;
|
package com.github.manevolent.ts3j.protocol.packet;
|
||||||
|
|
||||||
public enum PacketType {
|
public enum PacketType {
|
||||||
VOICE(0x0, null),
|
VOICE(0x0, null, null, false, false, false, false),
|
||||||
VOICE_WHISPER(0x1, null),
|
VOICE_WHISPER(0x1, null, null, false, false, false, false),
|
||||||
COMMAND(0x2, null),
|
PONG(0x5, null, null, false, false, false, false),
|
||||||
COMMAND_LOW(0x3, null),
|
PING(0x4, null, PONG, false, false, false, false),
|
||||||
PING(0x4, null),
|
ACK(0x6, null, null, true, true, false, false),
|
||||||
PONG(0x5, null),
|
ACK_LOW(0x7, null, null, true, true, false, false),
|
||||||
ACK(0x6, null),
|
INIT1(0x8, Packet8Init1.class, null, false, true, false, false),
|
||||||
ACK_LOW(0x7, null),
|
COMMAND_LOW(0x3, null, ACK_LOW, true, true, true, true),
|
||||||
INIT1(0x8, Packet8Init1.class)
|
COMMAND(0x2, Packet2Command.class, ACK, true, true, true, true)
|
||||||
;
|
;
|
||||||
|
|
||||||
private final int index;
|
private final int index;
|
||||||
private final Class<? extends Packet> packetClass;
|
private final Class<? extends Packet> packetClass;
|
||||||
|
|
||||||
PacketType(int index, Class<? extends Packet> packetClass) {
|
private final PacketType acknolwedgedBy;
|
||||||
|
private final boolean encrypted, resend, splittable, compressible;
|
||||||
|
|
||||||
|
PacketType(int index, Class<? extends Packet> packetClass,
|
||||||
|
PacketType acknolwedgedBy,
|
||||||
|
boolean encrypted,
|
||||||
|
boolean resend,
|
||||||
|
boolean splittable,
|
||||||
|
boolean compressible) {
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.packetClass = packetClass;
|
this.packetClass = packetClass;
|
||||||
|
this.acknolwedgedBy = acknolwedgedBy;
|
||||||
|
this.resend = resend;
|
||||||
|
this.encrypted = encrypted;
|
||||||
|
this.splittable = splittable;
|
||||||
|
this.compressible = compressible;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getIndex() {
|
public int getIndex() {
|
||||||
@@ -34,4 +47,24 @@ public enum PacketType {
|
|||||||
public Class<? extends Packet> getPacketClass() {
|
public Class<? extends Packet> getPacketClass() {
|
||||||
return packetClass;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -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.NetworkPacket;
|
||||||
import com.github.manevolent.ts3j.protocol.ProtocolRole;
|
import com.github.manevolent.ts3j.protocol.ProtocolRole;
|
||||||
import com.github.manevolent.ts3j.protocol.client.LocalTeamspeakClient;
|
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.protocol.packet.Packet8Init1;
|
||||||
import com.github.manevolent.ts3j.util.Ts3Logging;
|
import com.github.manevolent.ts3j.util.Ts3Logging;
|
||||||
|
|
||||||
@@ -25,6 +26,8 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
|
|||||||
public void onAssigned() throws IOException {
|
public void onAssigned() throws IOException {
|
||||||
Packet8Init1 packet = new Packet8Init1(ProtocolRole.CLIENT);
|
Packet8Init1 packet = new Packet8Init1(ProtocolRole.CLIENT);
|
||||||
|
|
||||||
|
// Initialize connection
|
||||||
|
|
||||||
Packet8Init1.Step0 step = new Packet8Init1.Step0();
|
Packet8Init1.Step0 step = new Packet8Init1.Step0();
|
||||||
Random random = new Random();
|
Random random = new Random();
|
||||||
randomBytes = new byte[4];
|
randomBytes = new byte[4];
|
||||||
@@ -132,6 +135,8 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
|
|||||||
throw new IllegalArgumentException("unexpected Init1 server step: " + init1.getStep().getNumber());
|
throw new IllegalArgumentException("unexpected Init1 server step: " + init1.getStep().getNumber());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
} else if (packet.getPacket() instanceof Packet2Command) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+22
@@ -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<byte[], byte[]> computeParameters(PacketHeader header) {
|
||||||
|
return new Pair<>(key, nonce);
|
||||||
|
}
|
||||||
|
}
|
||||||
+116
@@ -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<byte[], byte[]> 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<byte[], byte[]> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ package com.github.manevolent.ts3j.protocol.socket;
|
|||||||
|
|
||||||
import com.github.manevolent.ts3j.protocol.NetworkPacket;
|
import com.github.manevolent.ts3j.protocol.NetworkPacket;
|
||||||
import com.github.manevolent.ts3j.protocol.SocketRole;
|
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.header.PacketHeader;
|
||||||
import com.github.manevolent.ts3j.protocol.packet.Packet;
|
import com.github.manevolent.ts3j.protocol.packet.Packet;
|
||||||
import com.github.manevolent.ts3j.util.Ts3Logging;
|
import com.github.manevolent.ts3j.util.Ts3Logging;
|
||||||
@@ -17,7 +17,7 @@ import java.nio.ByteOrder;
|
|||||||
*
|
*
|
||||||
* Used simply to transcribe packets on the network
|
* 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 SocketRole socketRole;
|
||||||
|
|
||||||
private final DatagramSocket datagramSocket;
|
private final DatagramSocket datagramSocket;
|
||||||
@@ -88,7 +88,20 @@ public class LocalTeamspeakSocket implements TeamspeakSocket {
|
|||||||
|
|
||||||
ByteBuffer outputBuffer = ByteBuffer.allocate(networkPacket.getSize());
|
ByteBuffer outputBuffer = ByteBuffer.allocate(networkPacket.getSize());
|
||||||
outputBuffer.order(ByteOrder.BIG_ENDIAN);
|
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());
|
Ts3Logging.debug("[PROTOCOL] WRITE " + packet.getType().name());
|
||||||
|
|
||||||
@@ -120,7 +133,17 @@ public class LocalTeamspeakSocket implements TeamspeakSocket {
|
|||||||
networkPacket.readHeader(buffer);
|
networkPacket.readHeader(buffer);
|
||||||
|
|
||||||
Ts3Logging.debug("[PROTOCOL] READ " + networkPacket.getHeader().getType().name());
|
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;
|
return networkPacket;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user