Client authentication is done. It will now put an API client in a server, however we clearly need to work on decompression, and we also need to accept and handle ping packets appropriately.

Then we can start working on the text-based protocol Teamspeak uses.
This commit is contained in:
Manevolent
2018-07-27 16:07:29 -04:00
parent 146f2b6564
commit 6007b24b5f
16 changed files with 191 additions and 88 deletions
@@ -1058,26 +1058,14 @@ public class GroupOperations
public static void ge_p3_tobytes(byte[] s, int offset, GroupElementP3 h)
{
FieldElement recip = new FieldElement();
FieldElement x = new FieldElement();
FieldElement y = new FieldElement();
FieldElement h1 = new FieldElement();
FieldElement h2 = new FieldElement();
FieldOperations.fe_invert(recip, h.Z);
FieldOperations.fe_mul(x, h.X, recip);
FieldOperations.fe_mul(y, h.Y, recip);
FieldOperations.fe_tobytes(s, offset, y);
}
FieldOperations.fe_mul(h1, h.X, recip);
FieldOperations.fe_mul(h2, h.Y, recip);
FieldOperations.fe_tobytes(s, offset, h2);
public static void ge_p3_tobytes_negate(byte[] s, int offset, GroupElementP3 h)
{
FieldElement recip = new FieldElement();
FieldElement x = new FieldElement();
FieldElement y = new FieldElement();
FieldOperations.fe_invert(recip, h.Z);
FieldOperations.fe_mul(x, h.X, recip);
FieldOperations.fe_mul(y, h.Y, recip);
FieldOperations.fe_tobytes(s, offset, y);
s[offset + 31] ^= (byte) (FieldOperations.fe_isnegative(x) << 7);
s[offset + 31] ^= (byte) (FieldOperations.fe_isnegative(h1) << 7);
}
}
@@ -28,7 +28,6 @@ public class Identity {
public Identity(ECPoint publicKey) {
this.publicKey = publicKey;
this.publicKeyString = Base64.getEncoder().encodeToString(Ts3Crypt.encodePublicKey(publicKey));
this.fingerprint = generateFingerprint();
}
@@ -98,7 +97,7 @@ public class Identity {
int numLen = 0;
do
{
numBuffer[numLen] = (byte)((offset % 10) & 0xFF);
numBuffer[numLen] = (byte)(48 + (offset % 10));
offset /= 10;
numLen ++;
} while (offset > 0);
@@ -132,7 +131,9 @@ public class Identity {
private String generateFingerprint() {
synchronized (sha1) {
return Base64.getEncoder().encodeToString(sha1.digest(Ts3Crypt.encodePublicKey(publicKey)));
return Base64.getEncoder().encodeToString(
sha1.digest(getPublicKeyString().getBytes(Charset.forName("ASCII")))
);
}
}
}
@@ -24,15 +24,12 @@ public class LocalIdentity extends Identity {
}
public static LocalIdentity load(ECPoint publicKey,
BigInteger privateKey,
long keyOffset,
long lastCheckedKeyOffset) {
LocalIdentity localIdentity = new LocalIdentity(publicKey, privateKey);
BigInteger privateKey) {
return new LocalIdentity(publicKey, privateKey);
}
localIdentity.setKeyOffset(keyOffset);
localIdentity.setLastCheckedKeyOffset(lastCheckedKeyOffset);
return localIdentity;
public static LocalIdentity load(BigInteger privateKey) {
return new LocalIdentity(Ts3Crypt.generatePublicKeyFromPrivateKey(privateKey), privateKey);
}
public static LocalIdentity generateNew(int securityLevel) throws GeneralSecurityException {
@@ -48,7 +45,7 @@ public class LocalIdentity extends Identity {
ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters) keyPair.getPrivate();
ECPublicKeyParameters publicKey = (ECPublicKeyParameters) keyPair.getPublic();
LocalIdentity localIdentity = load(publicKey.getQ().normalize(), privateKey.getD(), 0, 0);
LocalIdentity localIdentity = load(publicKey.getQ().normalize(), privateKey.getD());
localIdentity.improveSecurity(securityLevel);
return localIdentity;
@@ -218,6 +218,7 @@ public class License {
byte[] final_ = new byte[32];
GroupOperations.ge_p3_tobytes(final_, 0, r2);
final_[31] ^= 0x80;
return final_;
}
@@ -4,6 +4,7 @@ import com.github.manevolent.ts3j.command.SimpleCommand;
import com.github.manevolent.ts3j.command.part.CommandSingleParameter;
import com.github.manevolent.ts3j.protocol.Packet;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
import com.github.manevolent.ts3j.protocol.client.ClientConnectionState;
import com.github.manevolent.ts3j.protocol.packet.PacketBody2Command;
import com.github.manevolent.ts3j.protocol.packet.PacketBody8Init1;
import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket;
@@ -45,11 +46,6 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
}
private void sendInit1(PacketBody8Init1 packet) throws IOException, TimeoutException {
Ts3Debugging.debug("Connecting: sending " + packet.getClass().getSimpleName()
+ ":"
+ packet.getStep().getClass().getSimpleName() + "..."
);
packet.setVersion(new byte[] { 0x09, (byte)0x83, (byte)0x8C, (byte)0xCF });
getClient().writePacket(packet);
@@ -57,8 +53,6 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
@Override
public void handlePacket(Packet packet) throws IOException, TimeoutException {
Ts3Debugging.debug("Connecting: handling " + packet.getBody().getClass().getSimpleName());
if (packet.getBody() instanceof PacketBody8Init1) {
PacketBody8Init1 init1 = (PacketBody8Init1) packet.getBody();
@@ -101,8 +95,6 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
byte[] solution =
x.modPow(BigInteger.valueOf(2L).pow(serverReplyStep3.getLevel()), n).toByteArray();
Ts3Debugging.debug(Ts3Debugging.getHex(solution));
System.arraycopy(
solution, Math.abs(solution.length - 64),
y, solution.length < 64 ? 64 - solution.length : 0,
@@ -187,8 +179,6 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
getClient().getIdentity()
);
Ts3Debugging.debug("Sending clientek...");
getClient().sendCommand(
new SimpleCommand(
"clientek", ProtocolRole.CLIENT,
@@ -199,36 +189,52 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
getClient().setSecureParameters(Ts3Crypt.cryptoInit2(license, alphaBytes, beta, keyPair.getValue()));
Ts3Debugging.debug("Sending clientinit...");
getClient().sendCommand(new SimpleCommand(
"clientinit",
ProtocolRole.CLIENT,
new CommandSingleParameter("client_nickname", "test"),
new CommandSingleParameter("client_version", "3.1 [Build: 1471417187]"),
new CommandSingleParameter("client_nickname", getClient().getNickname()),
new CommandSingleParameter("client_version", "3.1.8 [Build: 1516614607]"),
new CommandSingleParameter("client_platform", "Windows"),
new CommandSingleParameter(
"client_version_sign",
"gDEgQf/BiOQZdAheKccM1XWcMUj2OUQqt75oFuvF2c0MQMXyv88cZQdUuckKbcBRp7RpmLInto4PIgd7mPO7BQ=="
"LJ5q+KWT4KwBX7oR/9j9A12hBrq5ds5ony99" +
"f9kepNmqFskhT7gfB51bAJNgAMOzXVCeaItNmc10F2wUNktqCw=="
),
new CommandSingleParameter("client_input_hardware", "1"),
new CommandSingleParameter("client_output_hardware", "1"),
new CommandSingleParameter("client_default_channel", ""),
new CommandSingleParameter("client_default_channel_password", ""),
new CommandSingleParameter("client_server_password", ""),
new CommandSingleParameter("client_nickname_phonetic", "test"),
new CommandSingleParameter("client_default_channel",
getClient().getOption("client.default_channel", String.class)
),
new CommandSingleParameter("client_default_channel_password",
getClient().getOption("client.default_channel_password", String.class)
),
new CommandSingleParameter(
"client_server_password",
getClient().getOption("client.server_password", String.class)
),
new CommandSingleParameter("client_nickname_phonetic",
getClient().getOption("client.nickname_phonetic", String.class)
),
new CommandSingleParameter("client_meta_data", ""),
new CommandSingleParameter("client_default_token", ""),
new CommandSingleParameter("client_default_token",
getClient().getOption("client.default_token", String.class)
),
new CommandSingleParameter(
"client_key_offset",
Long.toString(getClient().getIdentity().getKeyOffset())
),
new CommandSingleParameter(
"hwid",
"+LyYqbDqOvJJpN5pdAbF8/v5kZ0="
getClient().getOption("client.hwid", String.class) != null ?
getClient().getOption("client.hwid", String.class) :
"+LyYqbDqOvEEpN5pdAbF8/v5kZ0="
)
)
);
} else if (command.getName().equals("error")) {
getClient().setState(ClientConnectionState.DISCONNECTED);
throw new IOException(command.get("msg").getValue());
}
}
}
@@ -327,15 +327,13 @@ public abstract class AbstractTeamspeakClientSocket
packet.getHeader().getPacketId(),
response = new PacketResponse(
networkPacket,
Integer.MAX_VALUE
networkPacket.getHeader().getType().canResend() ? Integer.MAX_VALUE : 0
)
);
else
response = null;
Ts3Debugging.debug("[PROTOCOL] WRITE " + networkPacket.getHeader().getType().name());
for (HeaderFlag flag : HeaderFlag.values())
Ts3Debugging.debug(flag.name() + ": " + networkPacket.getHeader().getPacketFlag(flag));
writeNetworkPacket(networkPacket);
@@ -350,7 +348,10 @@ public abstract class AbstractTeamspeakClientSocket
response.waitForAcknowledgement(1000);
break;
} catch (TimeoutException e) {
if (response.willResend())
response.resend();
else
throw e;
} catch (CancellationException e) {
throw e;
} catch (Exception e) {
@@ -398,11 +399,6 @@ public abstract class AbstractTeamspeakClientSocket
);
}
Ts3Debugging.debug("[NETWORK] READ " +
packet.getHeader().getType() + " BODY "
+ Ts3Debugging.getHex(packetBuffer.array())
);
// Fragment handling
if (fragment) {
Ts3Debugging.debug("[PROTOCOL] READ " + networkPacket.getHeader().getType().name() + " fragment");
@@ -547,6 +543,14 @@ public abstract class AbstractTeamspeakClientSocket
this.generationId = generationId;
}
public String getNickname( ){
return getOption("client.nickname", String.class);
}
public void setNickname(String name) {
setOption("client.nickname", name);
}
private class NetworkHandler implements Runnable {
@Override
public void run() {
@@ -554,9 +558,10 @@ public abstract class AbstractTeamspeakClientSocket
try {
Packet packet = readQueue.take();
Ts3Debugging.debug("Processing " + packet + "...");
// separate these so we getHandler at proper runtime instant rather than get it and let it change
// on us
getHandler().handlePacket(packet);
Ts3Debugging.debug("Processed " + packet + ".");
} catch (Exception e) {
if (e instanceof InterruptedException) continue;
@@ -605,12 +610,25 @@ public abstract class AbstractTeamspeakClientSocket
tries ++;
}
/**
* Waits for a package acknowledgement.
* @return true if the acknowledgement wasn't null (shouldn't ever happen)
* @throws ExecutionException
* @throws InterruptedException
*/
public boolean waitForAcknowledgement()
throws ExecutionException, InterruptedException {
Packet acknowledgement = future.get();
return acknowledgement != null;
}
/**
* Waits for a package acknowledgement.
* @param millis Milliseconds to wait. When transpired with no completion, TimeoutException is thrown.
* @return true if the acknowledgement wasn't null (shouldn't ever happen)
* @throws ExecutionException
* @throws InterruptedException
*/
public boolean waitForAcknowledgement(long millis)
throws TimeoutException, ExecutionException, InterruptedException {
Packet acknowledgement = future.get(millis, TimeUnit.MILLISECONDS);
@@ -618,7 +636,7 @@ public abstract class AbstractTeamspeakClientSocket
return acknowledgement != null;
}
public boolean isWillResend() {
public boolean willResend() {
return willResend;
}
@@ -65,7 +65,6 @@ public class LocalTeamspeakClientSocket extends AbstractTeamspeakClientSocket {
packet.getDatagram().getLength()
+ " to " +
socket.getRemoteSocketAddress());
Ts3Debugging.debug("[NETWORK] WRITE " + Ts3Debugging.getHex(packet.getBuffer().array()));
socket.send(packet.getDatagram());
}
@@ -19,7 +19,7 @@ public interface TeamspeakClientSocket extends TeamspeakSocket {
default <T extends Object> T getOption(String key, Class<T> clazz) {
Object value = getOptions().get(key);
if (value == null) return (T) null;
if (value == null) return null;
else if (!value.getClass().isAssignableFrom(clazz)) {
throw new ClassCastException(
@@ -226,11 +226,15 @@ public final class Ts3Crypt {
GroupOperations.ge_scalarmult_base(A, privateKey, 0);
byte[] publicKey = new byte[32];
GroupOperations.ge_p3_tobytes_negate(publicKey, 0, A);
GroupOperations.ge_p3_tobytes(publicKey, 0, A);
return new Pair<>(publicKey, privateKey);
}
public static ECPoint generatePublicKeyFromPrivateKey(BigInteger privateKey) {
return ECNamedCurveTable.getParameterSpec("prime256v1").getG().multiply(privateKey).normalize();
}
public static final class SecureChannelParameters {
private final byte[] fakeSignature;
private final byte[] ivStruct;
@@ -41,7 +41,7 @@ public final class Ts3String {
case '/': sb.append('/'); break;
case '\\': sb.append('\\'); break;
default:
throw new IllegalArgumentException("invalid escape code: " + c);
throw new IllegalArgumentException("invalid escape code: " + string.charAt(i) + " (" + Character.getName(string.charAt(i)) + ")");
}
} else sb.append(c);
}
@@ -8,6 +8,10 @@ import junit.framework.TestCase;
import java.math.BigInteger;
import java.util.Base64;
/**
* This is a self proof to make sure we can encrypt and decrypt our OWN data. This does not prove that the encrypted
* data would be correct. For that, there are other tests.
*/
public class ClientEkTest extends TestCase {
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
@@ -25,15 +29,14 @@ public class ClientEkTest extends TestCase {
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
new BigInteger(Base64.getDecoder().decode("Tj6YXM3qyRv8n25L2pH+OEJ" +
"nRUl4auQf8+znjYrOmWU="))
);
byte[] key = hexStringToByteArray("5763FA00EBC38DCE3953AFCACD8CFF2209208D15BB7D9B699D61A5675EDA9370");
byte[] beta = hexStringToByteArray("EA581E7CBADED7C47B9CBA3D3EEFB33A49B61ED0F0B45DF50D900C12F551B5F6B0EFEE3F593FB225184C78C06DB2D688669B70ABAC6D");
byte[] key = hexStringToByteArray("5763FA00EBC38DCE3953AFCACD8CFF2209208D15BB7D" +
"9B699D61A5675EDA9370");
byte[] beta = hexStringToByteArray("EA581E7CBADED7C47B9CBA3D3EEFB33A49B61ED0F0B45DF" +
"50D900C12F551B5F6B0EFEE3F593FB225184C78C06DB2D688669B70ABAC6D");
byte[] signature = Ts3Crypt.generateClientEkProof(key, beta, keyPair.getPrivateKey());
@@ -0,0 +1,37 @@
package com.github.manevolent.ts3j;
import com.github.manevolent.ts3j.identity.LocalIdentity;
import com.github.manevolent.ts3j.util.Ts3Crypt;
import junit.framework.TestCase;
import java.math.BigInteger;
import java.util.Base64;
public class ClientUidGenerationTest extends TestCase {
public static void main(String[] args) throws Exception {
new ClientUidGenerationTest().testParser();
}
public void testParser() throws Exception {
LocalIdentity keyPair = LocalIdentity.load(
new BigInteger(Base64.getDecoder().decode("Tj6YXM3qyRv8n25L2pH+OEJnRUl4auQf8+znjYrOmWU="))
);
keyPair.improveSecurity(10);
assertEquals(2294, keyPair.getKeyOffset());
assertEquals(2295, keyPair.getLastCheckedKeyOffset());
assertEquals("MEsDAgcAAgEgAiBpPRbTliVt9KxtIz8saYdwcnNgcwaKLb" +
"KYSpDNO87u9gIgSWWPKcSJ9P6VZKJfRdpWwcfMdJv+NA9/hXUtz1uwRVI=", keyPair.getPublicKeyString());
assertEquals("27QHSPRzQe+enxh56eUokkLmsFg=", keyPair.getFingerprint());
assertEquals(
"MEsDAgcAAgEgAiBpPRbTliVt9KxtIz8saYdwcnNgcwaKLbKY" +
"SpDNO87u9gIgSWWPKcSJ9P6VZKJfRdpWwcfMdJv+NA9/hXUtz1uwRVI=",
keyPair.getPublicKeyString());
}
}
@@ -6,6 +6,10 @@ import junit.framework.TestCase;
import static com.github.manevolent.ts3j.util.Ts3Debugging.hexStringToByteArray;
/**
* The assertion check values SHOULD be retrieved from another known working codebase (i.e. Splamy's C# codebase)
* for the given input license string that is in no way associated with this codebase.
*/
public class CryptoInit2Test extends TestCase {
public static void main(String[] args) throws Exception {
@@ -13,15 +17,20 @@ public class CryptoInit2Test extends TestCase {
}
public void testParser() throws Exception {
byte[] licenseBytes = hexStringToByteArray("0100358541498A24ACD30157918B8F50955C0DAE970AB65372CBE407415FCF3E029B02084D15E00AA793600700000020416E6F6E796D6F7573000047D9E4DC25AA2E90ACD4DB5FA61C8FED369B346D84C2CA2FCCCA86F73AFEF092200A77C8810A787141");
byte[] licenseBytes = hexStringToByteArray("0100358541498A24ACD30157918B8F50955C0DAE970AB65372CBE407" +
"415FCF3E029B02084D15E00AA793600700000020416E6F6E796D6F7573000047D9E4DC25AA2E90ACD4DB5FA61C8F" +
"ED369B346D84C2CA2FCCCA86F73AFEF092200A77C8810A787141");
byte[] alpha = hexStringToByteArray("9500A5DB3B50ACECAB81");
byte[] beta = hexStringToByteArray("EAFFC9A8BC996B25C8AA700264E99E372ECCDEB1C121D6EC0F4D49FB46CEEBA4E3C724B3070FD70CB03D7BC08129205690ECE228CA7C");
byte[] privateKey = hexStringToByteArray("102E591ABA4508129E812FF3437E2DDD3CA1F1EC341117CA3514CC347A7C2A77");
byte[] beta = hexStringToByteArray("EAFFC9A8BC996B25C8AA700264E99E372ECCDEB1C121D6EC0F4D49FB46" +
"CEEBA4E3C724B3070FD70CB03D7BC08129205690ECE228CA7C");
byte[] privateKey = hexStringToByteArray("102E591ABA4508129E812FF3437E2DDD3CA1F1EC341117CA35" +
"14CC347A7C2A77");
Ts3Crypt.SecureChannelParameters result =
Ts3Crypt.cryptoInit2(licenseBytes, alpha, beta, privateKey);
assertEquals(Ts3Debugging.getHex(result.getIvStruct()), "E4082A92F71C96A947452F5582EF2879B2051ED2D3F2C6B0643CF5A266EE6B5180573C2F5F3F1C4AC579188366F16AE0EADC3AAF860805D8F2A831E9E49F4513");
assertEquals(Ts3Debugging.getHex(result.getIvStruct()), "E4082A92F71C96A947452F5582EF2879B2051ED2D3" +
"F2C6B0643CF5A266EE6B5180573C2F5F3F1C4AC579188366F16AE0EADC3AAF860805D8F2A831E9E49F4513");
assertEquals(Ts3Debugging.getHex(result.getFakeSignature()), "54F2B4D661E0F9AB");
}
@@ -5,7 +5,17 @@ import com.github.manevolent.ts3j.util.Ts3Debugging;
import junit.framework.TestCase;
import java.nio.ByteBuffer;
import java.util.Base64;
/**
* Tests license derivation. Licenses are part of generating a shared key. The seed for the randomness is an
* ephemeral license at the tail end of a license block. That jumbles up the registers in the hash of the block,
* which is used in the protocol to generate the shared AES keys. What we're doing here is making sure that derivation
* happens appropriately and continues to in the future.
*
* The assertion check values SHOULD be retrieved from another known working codebase (i.e. Splamy's C# codebase)
* for the given input license string that is in no way associated with this codebase.
*/
public class LicenseDerivationTest extends TestCase {
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
@@ -21,11 +31,38 @@ public class LicenseDerivationTest extends TestCase {
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");
private void check(String base64License, String expectedResult) {
assertEquals(Base64.getEncoder().encodeToString(
License.deriveKey(License.readLicenses(ByteBuffer.wrap(
Base64.getDecoder().decode(base64License)
)))
), expectedResult);
}
public void testParser() throws Exception {
check("AQA1hUFJiiSs0wFXkYuPUJVcDa6XCrZTcsvkB0Ffzz4CmwIITRXgCqeTYA" +
"cAAAAgQW5vbnltb3VzAADSN9wlGHZEHZvX7ImHoqYezibj5byDh0f4oMsG3afDxyAKePI" +
"VCnma1Q==",
"z/bYm6TmHmuAil/osx8eGi6Oits2vIO4i6Bm13RuiGg=");
check("AQA1hUFJiiSs0wFXkYuPUJVcDa6XCrZTcsvkB0Ffzz4C" +
"mwIITRXgCqeTYAcAAAAgQW5vbnltb3VzAABx1YQfzCiB8b" +
"ZZAdGwXNTLmdhiOpjaH3OOlISy5vrM3iAKePBVCnmZFQ==",
"lrukIi392D7ltdKFp5mURT3Ydk+oWYNjMt3kptbQl6I=");
check("AQA1hUFJiiSs0wFXkYuPUJVcDa6XCrZTcsvkB0Ffzz4CmwIITR" +
"XgCqeTYAcAAAAgQW5vbnltb3VzAAAK5C0l+xtOTAZGEA/GHHOySAUEBmq7fN5" +
"PG7uSGPEADiAKePGHCnmaRw==",
"H+UcEreBUkCWN18nTYZp0QQkQqGA8IqzqvJ5qB225Z8=");
check("AQCvbHFTQDY/terPeilrp/ECU9xCH5U3xC92lYTNaY/0KQ" +
"AJFueAazbsgAAAACVUZWFtU3BlYWsgU3lzdGVtcyBHbWJIAACOaeEfqmGhp2qXAO" +
"QS7IfKw939kEy1i6NSE0OsO4IIPgAJRa7CL1XtIQAAACRUZWFtU3BlYWsgc3lzdGVtcy" +
"BHbWJIAACvbItqlhq4qOHjWFNky0ASbs2BBL//QMVspoGLWmXhzQIJSQCAH" +
"/mxgAYAAAAAVGVhbVNwZWFrIFN5c3RlbXMgR21iSAAAWysE+/5ug1yKk4WiiWMNeB+" +
"E+vQ5RmxglJLi8rW15UsgCnjyQAp5mwA=",
"ZWGGM5QmRdl6AQOgBUZgOAVHcicsm/YPYR3EukrFiGE=");
}
}
@@ -9,6 +9,9 @@ import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;
import java.util.Base64;
/**
* Public keys get encoded to base 64 and get passed around a lot in Teamspeak, especially early on in the protocol.
*/
public class PublicKeyEncodingTest extends TestCase {
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
@@ -26,11 +29,7 @@ public class PublicKeyEncodingTest extends TestCase {
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
new BigInteger(Base64.getDecoder().decode("Tj6YXM3qyRv8n25L2pH+OEJnRUl4auQf8+znjYrOmWU="))
);
ECPoint point = Ts3Crypt.decodePublicKey(Base64.getDecoder().decode(keyPair.getPublicKeyString()));
@@ -28,6 +28,10 @@ public class ServerConnectionTest {
client.setIdentity(localIdentity);
client.setNickname("Hello from Java");
client.setOption("client.hwid", "JAVAJAVAJAVA");
client.connect(new InetSocketAddress(
"ts.teamlixo.net",
9987),