diff --git a/pom.xml b/pom.xml
index b1ea9709dd..a9c0b6fd2a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -76,6 +76,7 @@
runescape-client-injector-plugin
http-api
http-service
+ runelite-proxy
diff --git a/runelite-proxy/pom.xml b/runelite-proxy/pom.xml
new file mode 100644
index 0000000000..d8dbabc080
--- /dev/null
+++ b/runelite-proxy/pom.xml
@@ -0,0 +1,64 @@
+
+
+
+ 4.0.0
+
+
+ net.runelite
+ runelite-parent
+ 1.1.26-SNAPSHOT
+
+
+ net.runelite
+ proxy
+ Runelite Proxy
+
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.12
+
+
+ org.slf4j
+ slf4j-simple
+ 1.7.12
+
+
+
+ com.google.guava
+ guava
+ 21.0
+
+
+
+ junit
+ junit
+ 4.12
+ test
+
+
+
diff --git a/runelite-proxy/src/main/java/net/runelite/proxy/IOCopy.java b/runelite-proxy/src/main/java/net/runelite/proxy/IOCopy.java
new file mode 100644
index 0000000000..d741b46d62
--- /dev/null
+++ b/runelite-proxy/src/main/java/net/runelite/proxy/IOCopy.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2017, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.proxy;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class IOCopy extends Thread
+{
+ private static final Logger logger = LoggerFactory.getLogger(IOCopy.class);
+
+ private final DataInputStream in;
+ private final DataOutputStream out;
+
+ public IOCopy(String name, DataInputStream in, DataOutputStream out)
+ {
+ super(name);
+ this.in = in;
+ this.out = out;
+ }
+
+ @Override
+ public void run()
+ {
+ try
+ {
+ int i;
+ while ((i = in.read()) != -1)
+ {
+ out.write(i);
+ }
+ }
+ catch (IOException ex)
+ {
+ logger.error(null, ex);
+ }
+ }
+
+}
diff --git a/runelite-proxy/src/main/java/net/runelite/proxy/PacketCopy.java b/runelite-proxy/src/main/java/net/runelite/proxy/PacketCopy.java
new file mode 100644
index 0000000000..96eb0f3d7c
--- /dev/null
+++ b/runelite-proxy/src/main/java/net/runelite/proxy/PacketCopy.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2017, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.proxy;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PacketCopy extends Thread
+{
+ private static final Logger logger = LoggerFactory.getLogger(PacketCopy.class);
+
+ private final DataInputStream in;
+ private final DataOutputStream out;
+ private final RLISAACCipher inCipher;
+ private final RLISAACCipher outCipher;
+
+ public PacketCopy(String name, DataInputStream in, DataOutputStream out, RLISAACCipher inCipher, RLISAACCipher outCipher)
+ {
+ super(name);
+ this.in = in;
+ this.out = out;
+ this.inCipher = inCipher;
+ this.outCipher = outCipher;
+ }
+
+ @Override
+ public void run()
+ {
+ try
+ {
+ for (;;)
+ {
+ int packetOpcode = ProxyRunner.readOpcode(inCipher, in);
+ int packetLength = Proxy.PACKET_LENGHTS[packetOpcode];
+
+ if (packetLength == -1)
+ {
+ packetLength = in.read() & 0xff;
+ }
+
+ if (packetLength == -2)
+ {
+ packetLength = in.readShort() & 0xffff;
+ }
+
+ byte[] b = new byte[packetLength];
+ int read = 0;
+
+ while (read < packetLength)
+ {
+ int r = in.read(b, read, packetLength - read);
+ if (r <= 0)
+ {
+ throw new IOException();
+ }
+ read += r;
+ }
+
+ logger.info("Read packet opcode {} length {}", packetOpcode, packetLength);
+
+ // Write out
+ ProxyRunner.writeOpcode(outCipher, out, packetOpcode);
+
+ switch (Proxy.PACKET_LENGHTS[packetOpcode])
+ {
+ case -1:
+ assert packetLength >= 0 && packetLength < 256;
+ out.write(packetLength);
+ break;
+ case -2:
+ out.writeShort(packetLength);
+ break;
+ }
+
+ out.write(b);
+
+ }
+ }
+ catch (IOException ex)
+ {
+ logger.error(null, ex);
+ }
+ }
+}
diff --git a/runelite-proxy/src/main/java/net/runelite/proxy/Proxy.java b/runelite-proxy/src/main/java/net/runelite/proxy/Proxy.java
new file mode 100644
index 0000000000..1547fbef2b
--- /dev/null
+++ b/runelite-proxy/src/main/java/net/runelite/proxy/Proxy.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2017, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.proxy;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.net.ServerSocket;
+import java.net.Socket;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Proxy
+{
+ private static final Logger logger = LoggerFactory.getLogger(Proxy.class);
+
+ public static final int REVISION = 139;
+
+ // For revision 139
+ public static final BigInteger SERVER_RSA_KEY_MODULUS = new BigInteger("a0211b55eb8010fd56feae6b163a67945b2ffcb6b3cd976a477f2d88e4cafefebd7174268fa9708acf26dbb44a772d4781f41ecb812f9ae459cf23fd7dc76bd70ac5b9ddaee2c1f6de8f8a8a39750f5f4a5c784079d6e7ba39a8915a6336cd681f3ce8083a344072adf5eec02a7221a05b8fda1e4ff4cf117ea89d290ed6fb7d", 16);
+ public static final BigInteger SERVER_RSA_KEY_EXPONENT = new BigInteger("10001", 16);
+ public static final int[] PACKET_LENGHTS = new int[]
+ {
+ 0, 0, 0, 0, 0, -1, 0, -2, 0, 5, 0, 0, 6, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, 4, 4, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 2, 0, 0, -2, 0, 0, 0, 6, -2, 8, 0, 0, 0, 0, -2, -1, 0, 0, -2, 6, 0, 0, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 0, 0, 10, 0, 0, -2, 0, 0, 0, 0, 2, 0, 0, 3, -2, 0, 0, 0, 8, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, -1, -2, 1, 0, 0, 0, 0, 6, 0, 0, 0, 28, 0, 6, 0, 0, 6, 0, 0, 0, 0, 3, -2, 6, 0, 0, 2, 0, 0, 0, 0, 0, 0, 6, 2, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, -2, 6, 0, -2, 5, 0, 5, 0, 0, 0, 0, 0, 0, 0, -1, 2, 4, 0, 0, 0, 2, 0, -2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 15, 0, 0, 0, -2, 0, 0, 0, 0, 0, -2, 14, 8, 20, 0, 2, 0, 0, 0, -2, 5, 4, 0, 0, 0, 10, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 8, 0, 4, 0, 1, -2, 7, 0, 12, 0, 6, 4, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0
+ };
+
+ public static final RSA myRsa = new RSA();
+ public static final RSA rsRsa = new RSA(SERVER_RSA_KEY_MODULUS, null);
+
+ public static void main(String[] args) throws IOException
+ {
+ ServerSocket socket = new ServerSocket(43594);
+
+ logger.info("Running with RSA modulus {}", myRsa.getN().toString(16));
+
+ Socket s;
+ while ((s = socket.accept()) != null)
+ {
+ new ProxyRunner(s).start();
+ }
+ }
+}
diff --git a/runelite-proxy/src/main/java/net/runelite/proxy/ProxyRunner.java b/runelite-proxy/src/main/java/net/runelite/proxy/ProxyRunner.java
new file mode 100644
index 0000000000..fcd19bf427
--- /dev/null
+++ b/runelite-proxy/src/main/java/net/runelite/proxy/ProxyRunner.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2017, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.proxy;
+
+import com.google.common.primitives.Ints;
+import com.google.common.primitives.Longs;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.nio.ByteBuffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ProxyRunner extends Thread
+{
+ private static final Logger logger = LoggerFactory.getLogger(ProxyRunner.class);
+
+ private final Socket client;
+ private final Socket server;
+
+ //private RLISAACCipher clientInCipher;
+ private RLISAACCipher clientOutCipher;
+
+ private RLISAACCipher serverInCipher;
+ //private RLISAACCipher serverOutCipher;
+
+ public ProxyRunner(Socket client) throws IOException
+ {
+ this.client = client;
+ this.server = new Socket("localhost", 8080);
+ }
+
+ @Override
+ public void run()
+ {
+ try
+ {
+ go();
+ }
+ catch (IOException ex)
+ {
+ logger.error(null, ex);
+ }
+ }
+
+ private void go() throws IOException
+ {
+ DataInputStream in = new DataInputStream(client.getInputStream());
+ DataOutputStream out = new DataOutputStream(client.getOutputStream());
+
+ DataInputStream serverIn = new DataInputStream(server.getInputStream());
+ DataOutputStream serverOut = new DataOutputStream(server.getOutputStream());
+
+ // For the http proxy..
+ serverOut.write("CONNECT oldschool78.runescape.com:43594 HTTP/1.0\r\n\r\n".getBytes());
+ System.out.println(serverIn.readLine());
+ System.out.println(serverIn.readLine());
+
+ int handshakeType = in.read();
+ logger.info("Handshake from client: {}", handshakeType);
+
+ if (handshakeType != 14) // login?
+ {
+ return;
+ }
+
+ // Forward to server
+ serverOut.write(handshakeType);
+
+ int handshakeResponse = serverIn.read();
+ logger.info("Handshake response from server: {}", handshakeResponse);
+
+ // Forward to client
+ out.write(handshakeResponse);
+
+ byte[] b = new byte[8];
+ int len = serverIn.read(b);
+ assert len == 8;
+
+ logger.info("Nonce from server: {}", Longs.fromByteArray(b));
+
+ // Forward to client
+ out.write(b);
+
+ // Now the client sends the login packet, some of it is rsa encrypted with a rsa key
+ // that is hardcoded into the client. In this data is the xtea key which is used later
+ // for the isaac cipher seeding and for encrypting the rest of the packet.
+ int gameState = in.read(); // 18 or 16 depending on some game state
+ int length = in.readShort(); // length of remaining packet
+ int revision = in.readInt(); // client revision
+
+ logger.info("Client game state: {}, revision: {}", gameState, revision);
+
+ if (revision != Proxy.REVISION)
+ {
+ client.close();
+ server.close();
+ return;
+ }
+
+ int encrypedDataLength = in.readShort();
+ byte[] rsaData = new byte[encrypedDataLength];
+ len = in.read(rsaData);
+ assert len == encrypedDataLength;
+
+ rsaData = Proxy.myRsa.decrypt(rsaData);
+
+ // Reencrypt data
+ byte[] reencrypted = Proxy.rsRsa.encrypt(rsaData);
+
+ // Extract xtea key
+ b = rsaData;
+ int key1 = Ints.fromBytes(b[2], b[3], b[4], b[5]);
+ int key2 = Ints.fromBytes(b[6], b[7], b[8], b[9]);
+ int key3 = Ints.fromBytes(b[10], b[11], b[12], b[13]);
+ int key4 = Ints.fromBytes(b[14], b[15], b[16], b[17]);
+ int[] keys = new int[]
+ {
+ key1, key2, key3, key4
+ };
+
+ logger.info("Xtea key is {} {} {} {}", key1, key2, key3, key4);
+
+ //clientInCipher = new RLISAACCipher(keys);
+ clientOutCipher = new RLISAACCipher(keys);
+ serverInCipher = new RLISAACCipher(keys);
+ //serverOutCipher = new RLISAACCipher(keys);
+
+ // Following this is xtea encrypted data
+ int xteaDataLength = length - 4 - encrypedDataLength - 2; // total length - revision - rsa encrypted data - rsa encrypted data length
+ byte[] xteaData = new byte[xteaDataLength];
+ len = in.read(xteaData);
+ assert len == xteaDataLength;
+
+ ByteBuffer buffer = ByteBuffer.allocate(4096);
+ buffer.put((byte) gameState);
+ buffer.putShort((short) 0); // length
+ buffer.putInt(revision);
+
+ buffer.putShort((short) reencrypted.length);
+ buffer.put(reencrypted);
+ buffer.put(xteaData);
+
+ len = buffer.position();
+ buffer.putShort(1, (short) (len - 3));
+ serverOut.write(buffer.array(), 0, len);
+
+ handshakeResponse = serverIn.read();
+ logger.info("Handshake response: {}", handshakeResponse);
+ out.write(handshakeResponse);
+
+ if (handshakeResponse != 2)
+ {
+ client.close();
+ server.close();
+ return;
+ }
+
+ int hasPreferenceValue = serverIn.read();
+ logger.info("Has preference value: {}", hasPreferenceValue);
+ out.write(hasPreferenceValue);
+ assert hasPreferenceValue != 1;
+
+ int byte1 = serverIn.read();
+ int byte2 = serverIn.read();
+ int interactingIndex = serverIn.readShort() & 0xffff;
+ int byte3 = serverIn.read();
+
+ logger.info("B1/B2/interactingIndex/B3: {}/{}/{}/{}", byte1, byte2, interactingIndex, byte3);
+
+ out.write(byte1);
+ out.write(byte2);
+ out.writeShort(interactingIndex);
+ out.write(byte3);
+
+ int packetOpcode = readOpcode(serverInCipher, serverIn);
+ int packetLength = serverIn.readShort() & 0xffff;
+
+ logger.info("Packet opcode: {}", packetOpcode);
+
+ writeOpcode(clientOutCipher, out, packetOpcode);
+ out.writeShort(packetLength);
+
+ byte[] packetData = new byte[packetLength];
+ len = serverIn.read(packetData);
+ assert len == packetData.length;
+ out.write(packetData);
+
+ new IOCopy("Client to Server", in, serverOut).start();
+ new IOCopy("Server to Client", serverIn, out).start();
+ //new PacketCopy("Server to Client", serverIn, out, serverInCipher, clientOutCipher).start();
+ }
+
+ public static int readOpcode(RLISAACCipher cipher, InputStream in) throws IOException
+ {
+ return (in.read() - cipher.nextInt()) & 0xff;
+ }
+
+ public static void writeOpcode(RLISAACCipher cipher, OutputStream out, int opcode) throws IOException
+ {
+ byte encrytpedOpcode = (byte) (opcode + cipher.nextInt());
+ out.write(encrytpedOpcode & 0xff);
+ }
+}
diff --git a/runelite-proxy/src/main/java/net/runelite/proxy/RLISAACCipher.java b/runelite-proxy/src/main/java/net/runelite/proxy/RLISAACCipher.java
new file mode 100644
index 0000000000..3750e85562
--- /dev/null
+++ b/runelite-proxy/src/main/java/net/runelite/proxy/RLISAACCipher.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2017, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.proxy;
+
+import java.util.Arrays;
+
+/**
+ * Based off of the implementation from
+ * https://rosettacode.org/wiki/The_ISAAC_Cipher#Java Modified to not extend
+ * java.util.Random, and return results in reverse order
+ *
+ * @author Adam
+ */
+public class RLISAACCipher
+{
+ private final int[] randResult = new int[256]; // output of last generation
+ private int valuesLeft; // the number of values already left in randResult
+
+ // internal generator state
+ private final int[] mm = new int[256];
+ private int aa, bb, cc;
+
+ public RLISAACCipher(int[] key)
+ {
+ init(key);
+ }
+
+ private void generateMoreResults()
+ {
+ cc++;
+ bb += cc;
+
+ for (int i = 0; i < 256; i++)
+ {
+ int x = mm[i];
+ switch (i & 3)
+ {
+ case 0:
+ aa = aa ^ (aa << 13);
+ break;
+ case 1:
+ aa = aa ^ (aa >>> 6);
+ break;
+ case 2:
+ aa = aa ^ (aa << 2);
+ break;
+ case 3:
+ aa = aa ^ (aa >>> 16);
+ break;
+ }
+ aa = mm[i ^ 128] + aa;
+ int y = mm[i] = mm[(x >>> 2) & 0xFF] + aa + bb;
+ randResult[i] = bb = mm[(y >>> 10) & 0xFF] + x;
+ }
+
+ valuesLeft = 256;
+ }
+
+ private static void mix(int[] s)
+ {
+ s[0] ^= s[1] << 11;
+ s[3] += s[0];
+ s[1] += s[2];
+ s[1] ^= s[2] >>> 2;
+ s[4] += s[1];
+ s[2] += s[3];
+ s[2] ^= s[3] << 8;
+ s[5] += s[2];
+ s[3] += s[4];
+ s[3] ^= s[4] >>> 16;
+ s[6] += s[3];
+ s[4] += s[5];
+ s[4] ^= s[5] << 10;
+ s[7] += s[4];
+ s[5] += s[6];
+ s[5] ^= s[6] >>> 4;
+ s[0] += s[5];
+ s[6] += s[7];
+ s[6] ^= s[7] << 8;
+ s[1] += s[6];
+ s[7] += s[0];
+ s[7] ^= s[0] >>> 9;
+ s[2] += s[7];
+ s[0] += s[1];
+ }
+
+ private void init(int[] seed)
+ {
+ if (seed != null && seed.length != 256)
+ {
+ seed = Arrays.copyOf(seed, 256);
+ }
+ aa = bb = cc = 0;
+ int[] initState = new int[8];
+ Arrays.fill(initState, 0x9e3779b9); // the golden ratio
+
+ for (int i = 0; i < 4; i++)
+ {
+ mix(initState);
+ }
+
+ for (int i = 0; i < 256; i += 8)
+ {
+ if (seed != null)
+ {
+ for (int j = 0; j < 8; j++)
+ {
+ initState[j] += seed[i + j];
+ }
+ }
+
+ mix(initState);
+ for (int j = 0; j < 8; j++)
+ {
+ mm[i + j] = initState[j];
+ }
+ }
+
+ if (seed != null)
+ {
+ for (int i = 0; i < 256; i += 8)
+ {
+ for (int j = 0; j < 8; j++)
+ {
+ initState[j] += mm[i + j];
+ }
+
+ mix(initState);
+
+ for (int j = 0; j < 8; j++)
+ {
+ mm[i + j] = initState[j];
+ }
+ }
+ }
+
+ valuesLeft = 0; // Make sure generateMoreResults() will be called by the next nextInt() call.
+ }
+
+ public int nextInt()
+ {
+ if (valuesLeft == 0)
+ {
+ generateMoreResults();
+ assert valuesLeft == 256;
+ }
+
+ int value = randResult[--valuesLeft];
+
+ return value;
+ }
+}
diff --git a/runelite-proxy/src/main/java/net/runelite/proxy/RSA.java b/runelite-proxy/src/main/java/net/runelite/proxy/RSA.java
new file mode 100644
index 0000000000..d284f8c181
--- /dev/null
+++ b/runelite-proxy/src/main/java/net/runelite/proxy/RSA.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2017, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.proxy;
+
+import java.math.BigInteger;
+import java.util.Random;
+
+public class RSA
+{
+ private final BigInteger e = Proxy.SERVER_RSA_KEY_EXPONENT; // Use same exponent as server
+ private final BigInteger n;
+ private final BigInteger d;
+
+ public RSA()
+ {
+ // This taken from http://stackoverflow.com/a/24547249
+ int keySize = Proxy.SERVER_RSA_KEY_MODULUS.bitLength(); // pick same key size as server
+
+ //SecureRandom random = new SecureRandom();
+ Random random = new Random(42); // Hack to make the modulus always the same
+
+ // Choose two distinct prime numbers p and q.
+ BigInteger p = BigInteger.probablePrime(keySize / 2, random);
+ BigInteger q = BigInteger.probablePrime(keySize / 2, random);
+ // Compute n = pq (modulus)
+ n = p.multiply(q);
+ // Compute φ(n) = φ(p)φ(q) = (p − 1)(q − 1) = n - (p + q -1), where φ is Euler's totient function.
+ // and choose an integer e such that 1 < e < φ(n) and gcd(e, φ(n)) = 1; i.e., e and φ(n) are coprime.
+ BigInteger m = (p.subtract(BigInteger.ONE)).multiply(q.subtract(BigInteger.ONE));
+ assert m.gcd(e).equals(BigInteger.ONE); // We always pick SERVER_RSA_KEY_EXPONENT instead
+ // Determine d as d ≡ e−1 (mod φ(n)); i.e., d is the multiplicative inverse of e (modulo φ(n)).
+ d = e.modInverse(m);
+ }
+
+ public RSA(BigInteger n, BigInteger d)
+ {
+ this.n = n;
+ this.d = d;
+ }
+
+ public BigInteger encrypt(BigInteger value)
+ {
+ // C = P**e % n
+ return value.modPow(e, n);
+ }
+
+ public BigInteger decrypt(BigInteger value)
+ {
+ // P = C**d % n
+ return value.modPow(d, n);
+ }
+
+ public byte[] encrypt(byte[] value)
+ {
+ return encrypt(new BigInteger(value)).toByteArray();
+ }
+
+ public byte[] decrypt(byte[] value)
+ {
+ return decrypt(new BigInteger(value)).toByteArray();
+ }
+
+ public BigInteger getE()
+ {
+ return e;
+ }
+
+ public BigInteger getN()
+ {
+ return n;
+ }
+
+ public BigInteger getD()
+ {
+ return d;
+ }
+}
diff --git a/runelite-proxy/src/test/java/net/runelite/proxy/RSATest.java b/runelite-proxy/src/test/java/net/runelite/proxy/RSATest.java
new file mode 100644
index 0000000000..e58877ff08
--- /dev/null
+++ b/runelite-proxy/src/test/java/net/runelite/proxy/RSATest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2017, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.proxy;
+
+import java.math.BigInteger;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class RSATest
+{
+
+ @Test
+ public void testSomeMethod()
+ {
+ RSA rsa = new RSA();
+
+ BigInteger C = rsa.encrypt(BigInteger.valueOf(42));
+ int value = rsa.decrypt(C).intValue();
+
+ Assert.assertEquals(42, value);
+ }
+
+}