/** * This file is part of aion-emu <aion-emu.com>. * * aion-emu is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * aion-emu is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with aion-emu. If not, see <http://www.gnu.org/licenses/>. */ package com.aionemu.gameserver.network; import java.nio.ByteBuffer; import com.aionemu.commons.utils.Rnd; /** * Crypt will encrypt server packet and decrypt client packet. * * @author hack99 * @author -Nemesiss- */ public class Crypt { /** * Second byte of client packet must be equal to this */ public final static byte staticClientPacketCode = 0x54; /** * Second byte of server packet must be equal to this */ public final static byte staticServerPacketCode = 0x55;// 1.5.x (0x54 works too) /** * Static xor key */ private static byte[] staticKey = "nKO/WctQ0AVLbpzfBkS6NevDYT8ourG5CRlmdjyJ72aswx4EPq1UgZhFMXH?3iI9" .getBytes(); /** * Current xor key for client packet decoding. */ private byte[] clientPacketKey; /** * Current xor key for server packet encoding. */ private byte[] serverPacketKey; /** * Crypt is enabled after first server packet was send. */ private boolean isEnabled; /** * Enable crypt key - generate random key that will be used to encrypt second server packet [first one is * unencrypted] and decrypt client packets. This method is called from SM_KEY server packet, that packet sends key * to aion client. * * @return "false key" that should by used by aion client to encrypt/decrypt packets. */ public final int enableKey() { if(clientPacketKey != null) throw new KeyAlreadySetException(); /** rnd key - this will be used to encrypt/decrypt packet */ int key = Rnd.nextInt(); clientPacketKey = new byte[] { (byte) (key & 0xff), (byte) ((key >> 8) & 0xff), (byte) ((key >> 16) & 0xff), (byte) ((key >> 24) & 0xff), (byte) 0xa1, (byte) 0x6c, (byte) 0x54, (byte) 0x87 }; serverPacketKey = new byte[clientPacketKey.length]; System.arraycopy(clientPacketKey, 0, serverPacketKey, 0, clientPacketKey.length); /** false key that will be sent to aion client in SM_KEY packet */ return (key ^ 0xCD92E451) + 0x3FF2CC87; } /** * Decrypt client packet from this ByteBuffer. * * @param buf * @return true if decryption was successful. */ public final boolean decrypt(ByteBuffer buf) { if(!isEnabled) return false; final byte[] data = buf.array(); final int size = buf.remaining(); /** index to byte that should be decrypted now */ int arrayIndex = buf.arrayOffset() + buf.position(); /** prev encrypted byte */ int prev = data[arrayIndex]; /** decrypt first byte */ data[arrayIndex++] ^= (clientPacketKey[0] & 0xff); /** decrypt loop */ for(int i = 1; i < size; i++, arrayIndex++) { int curr = data[arrayIndex] & 0xff; data[arrayIndex] ^= (staticKey[i & 63] & 0xff) ^ (clientPacketKey[i & 7] & 0xff) ^ prev; prev = curr; } /** oldKey value as long */ long oldKey = (((long) clientPacketKey[0] & 0xff) << 0) | (((long) clientPacketKey[1] & 0xff) << 8) | (((long) clientPacketKey[2] & 0xff) << 16) | (((long) clientPacketKey[3] & 0xff) << 24) | (((long) clientPacketKey[4] & 0xff) << 32) | (((long) clientPacketKey[5] & 0xff) << 40) | (((long) clientPacketKey[6] & 0xff) << 48) | (((long) clientPacketKey[7] & 0xff) << 56); /** change key */ oldKey += size; /** set key new value */ clientPacketKey[0] = (byte) (oldKey >> 0 & 0xff); clientPacketKey[1] = (byte) (oldKey >> 8 & 0xff); clientPacketKey[2] = (byte) (oldKey >> 16 & 0xff); clientPacketKey[3] = (byte) (oldKey >> 24 & 0xff); clientPacketKey[4] = (byte) (oldKey >> 32 & 0xff); clientPacketKey[5] = (byte) (oldKey >> 40 & 0xff); clientPacketKey[6] = (byte) (oldKey >> 48 & 0xff); clientPacketKey[7] = (byte) (oldKey >> 56 & 0xff); return validateClientPacket(buf); } /** * Check if packet was correctly decoded, also check if packet was correctly coded by aion client. * * @param buf * @return true if packet is correctly decoded */ private final boolean validateClientPacket(ByteBuffer buf) { return buf.get(0) == ~buf.get(2) && buf.get(1) == staticClientPacketCode; } /** * Encrypt server packet from this ByteBuffer. * * @param buf */ public final void encrypt(ByteBuffer buf) { if(!isEnabled) { /** first packet is not encrypted */ isEnabled = true; return; } final byte[] data = buf.array(); final int size = buf.remaining(); /** index to byte that should be encrypted now */ int arrayIndex = buf.arrayOffset() + buf.position(); /** encrypt first byte */ data[arrayIndex] ^= (serverPacketKey[0] & 0xff); /** prev encrypted byte */ int prev = data[arrayIndex++]; /** encrypt loop */ for(int i = 1; i < size; i++, arrayIndex++) { data[arrayIndex] ^= (staticKey[i & 63] & 0xff) ^ (serverPacketKey[i & 7] & 0xff) ^ prev; prev = data[arrayIndex]; } /** oldKey value as long */ long oldKey = (((long) serverPacketKey[0] & 0xff) << 0) | (((long) serverPacketKey[1] & 0xff) << 8) | (((long) serverPacketKey[2] & 0xff) << 16) | (((long) serverPacketKey[3] & 0xff) << 24) | (((long) serverPacketKey[4] & 0xff) << 32) | (((long) serverPacketKey[5] & 0xff) << 40) | (((long) serverPacketKey[6] & 0xff) << 48) | (((long) serverPacketKey[7] & 0xff) << 56); /** change key */ oldKey += size; /** set key new value */ serverPacketKey[0] = (byte) (oldKey >> 0 & 0xff); serverPacketKey[1] = (byte) (oldKey >> 8 & 0xff); serverPacketKey[2] = (byte) (oldKey >> 16 & 0xff); serverPacketKey[3] = (byte) (oldKey >> 24 & 0xff); serverPacketKey[4] = (byte) (oldKey >> 32 & 0xff); serverPacketKey[5] = (byte) (oldKey >> 40 & 0xff); serverPacketKey[6] = (byte) (oldKey >> 48 & 0xff); serverPacketKey[7] = (byte) (oldKey >> 56 & 0xff); } /** * Server packet opcodec obfuscation. * * @param op * @return obfuscated opcodec */ public static final byte encodeOpcodec(int op) { return (byte) ((op + 0xAE) ^ 0xEE); } }