/** * 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.loginserver.network.ncrypt; //~--- non-JDK imports -------------------------------------------------------- import com.aionemu.commons.utils.Rnd; /** * Crypto engine for ecnrypting/decrypting packets, error handling * and verifying checksum * * @author EvilSpirit */ public class CryptEngine { /** * A key */ private byte[] key = { (byte) 0x6b, (byte) 0x60, (byte) 0xcb, (byte) 0x5b, (byte) 0x82, (byte) 0xce, (byte) 0x90, (byte) 0xb1, (byte) 0xcc, (byte) 0x2b, (byte) 0x6c, (byte) 0x55, (byte) 0x6c, (byte) 0x6c, (byte) 0x6c, (byte) 0x6c }; /** * Tells you whether the key is updated or not */ private boolean updatedKey = false; /** * A secret blowfish cipher */ private BlowfishCipher cipher; /** * Default constructor. Initialize the Blowfish Cipher with an initial static key * to encrypt the first packet sent to the client */ public CryptEngine() { cipher = new BlowfishCipher(key); } /** * Update the key for packet encryption/decryption with the Blowfish Cipher * @param newKey new Blowfish Key */ public void updateKey(byte[] newKey) { this.key = newKey; } /** * Decrypt given data * @param data byte array to be decrypted * @param offset byte array offset * @param length byte array length * @return true, if decrypted packet has valid checksum, false overwise */ public boolean decrypt(byte[] data, int offset, int length) { cipher.decipher(data, offset, length); return verifyChecksum(data, offset, length); } /** * Encrypt given data * @param data byte array to be encrypted * @param offset byte array offset * @param length byte array length * @return length of encrypted byte array */ public int encrypt(byte[] data, int offset, int length) { length += 4; // the key is not updated, so the first packet should be encrypted with initial key if (!updatedKey) { length += 4; length += 8 - length % 8; encXORPass(data, offset, length, Rnd.nextInt()); cipher.cipher(data, offset, length); cipher.updateKey(key); updatedKey = true; } else { length += 8 - length % 8; appendChecksum(data, offset, length); cipher.cipher(data, offset, length); } return length; } /** * Verify checksum in a packet * @param data byte array - encrypted packet * @param offset byte array offset * @param length byte array size * @return true, if checksum is ok, false overwise */ private boolean verifyChecksum(byte[] data, int offset, int length) { if ((length & 3) != 0 || (length <= 4)) { return false; } long chksum = 0; int count = length - 4; long check; int i; for (i = offset; i < count; i += 4) { check = data[i] & 0xff; check |= data[i + 1] << 8 & 0xff00; check |= data[i + 2] << 0x10 & 0xff0000; check |= data[i + 3] << 0x18 & 0xff000000; chksum ^= check; } check = data[i] & 0xff; check |= data[i + 1] << 8 & 0xff00; check |= data[i + 2] << 0x10 & 0xff0000; check |= data[i + 3] << 0x18 & 0xff000000; check = data[i] & 0xff; check |= data[i + 1] << 8 & 0xff00; check |= data[i + 2] << 0x10 & 0xff0000; check |= data[i + 3] << 0x18 & 0xff000000; return 0 == chksum; } /** * add checksum to the end of the packet * @param raw byte array - encrypted packet * @param offset byte array offset * @param length byte array size */ private void appendChecksum(byte[] raw, int offset, int length) { long chksum = 0; int count = length - 4; long ecx; int i; for (i = offset; i < count; i += 4) { ecx = raw[i] & 0xff; ecx |= raw[i + 1] << 8 & 0xff00; ecx |= raw[i + 2] << 0x10 & 0xff0000; ecx |= raw[i + 3] << 0x18 & 0xff000000; chksum ^= ecx; } ecx = raw[i] & 0xff; ecx |= raw[i + 1] << 8 & 0xff00; ecx |= raw[i + 2] << 0x10 & 0xff0000; ecx |= raw[i + 3] << 0x18 & 0xff000000; raw[i] = (byte) (chksum & 0xff); raw[i + 1] = (byte) (chksum >> 0x08 & 0xff); raw[i + 2] = (byte) (chksum >> 0x10 & 0xff); raw[i + 3] = (byte) (chksum >> 0x18 & 0xff); } /** * First packet encryption with XOR key (integer - 4 bytes) * @param data byte array to be encrypted * @param offset byte array offset * @param length byte array length * @param key integer value as key */ private void encXORPass(byte[] data, int offset, int length, int key) { int stop = length - 8; int pos = 4 + offset; int edx; int ecx = key; while (pos < stop) { edx = (data[pos] & 0xFF); edx |= (data[pos + 1] & 0xFF) << 8; edx |= (data[pos + 2] & 0xFF) << 16; edx |= (data[pos + 3] & 0xFF) << 24; ecx += edx; edx ^= ecx; data[pos++] = (byte) (edx & 0xFF); data[pos++] = (byte) (edx >> 8 & 0xFF); data[pos++] = (byte) (edx >> 16 & 0xFF); data[pos++] = (byte) (edx >> 24 & 0xFF); } data[pos++] = (byte) (ecx & 0xFF); data[pos++] = (byte) (ecx >> 8 & 0xFF); data[pos++] = (byte) (ecx >> 16 & 0xFF); data[pos] = (byte) (ecx >> 24 & 0xFF); } }