diff --git a/cache/src/main/java/net/runelite/cache/fs/util/Xtea.java b/cache/src/main/java/net/runelite/cache/fs/util/Xtea.java new file mode 100644 index 0000000000..93712bce96 --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/fs/util/Xtea.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2016, 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam + * 4. Neither the name of the Adam nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Adam ''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 Adam 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.cache.fs.util; + +import java.nio.ByteBuffer; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.Security; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.SecretKeySpec; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.Arrays; + +public class Xtea +{ + static + { + Security.addProvider(new BouncyCastleProvider()); + } + + private final Cipher cipher; + private final int[] keys; + + public Xtea(int[] keys) throws NoSuchAlgorithmException, NoSuchPaddingException + { + this.cipher = Cipher.getInstance("XTEA/ECB/NoPadding"); + this.keys = keys; + } + + private static byte[] packKey(int[] key) + { + ByteBuffer buffer = ByteBuffer.allocate(4 * key.length); + for (int i : key) + buffer.putInt(i); + return buffer.array(); + } + + public byte[] encrypt(byte[] data, int len) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException + { + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(packKey(keys), cipher.getAlgorithm())); + byte[] out = cipher.update(data, 0, len - (len % cipher.getBlockSize())); + cipher.doFinal(); + + // add remaining data, which is not encrypted + if (out.length != len) + { + assert len > out.length; + + byte[] padded = Arrays.copyOf(out, len); + System.arraycopy(data, out.length, padded, out.length, len - out.length); + + out = padded; + } + + return out; + } + + public byte[] decrypt(byte[] data, int len) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException + { + cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(packKey(keys), cipher.getAlgorithm())); + byte[] out = cipher.update(data, 0, len - (len % cipher.getBlockSize())); + cipher.doFinal(); + + if (out.length != len) + { + assert len > out.length; + + byte[] padded = Arrays.copyOf(out, len); + System.arraycopy(data, out.length, padded, out.length, len - out.length); + + out = padded; + } + + return out; + } +} diff --git a/cache/src/test/java/net/runelite/cache/fs/util/XteaTest.java b/cache/src/test/java/net/runelite/cache/fs/util/XteaTest.java new file mode 100644 index 0000000000..89ccbd04b8 --- /dev/null +++ b/cache/src/test/java/net/runelite/cache/fs/util/XteaTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2016, 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam + * 4. Neither the name of the Adam nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Adam ''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 Adam 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.cache.fs.util; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Random; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import org.junit.Assert; +import org.junit.Test; + +public class XteaTest +{ + private final Random random = new Random(42L); + + @Test + public void test() throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException + { + byte[] data = new byte[1024]; + random.nextBytes(data); + + int[] key = new int[] { 4, 8, 15, 16 }; + + Xtea xtea = new Xtea(key); + byte[] encData = xtea.encrypt(data, data.length); + + xtea = new Xtea(key); + byte[] decData = xtea.decrypt(encData, encData.length); + + Assert.assertArrayEquals(data, decData); + } +}