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); + } + +}