Quellcode durchsuchen

BETA: LoginCrypt Optimization by FBIagent. Also a Javadoc update. Thanks.

nonom vor 13 Jahren
Ursprung
Commit
117f89906a

+ 72 - 27
L2J_Server_BETA/java/com/l2jserver/gameserver/LoginServerThread.java

@@ -168,14 +168,14 @@ public class LoginServerThread extends Thread
 				_in = _loginSocket.getInputStream();
 				_out = new BufferedOutputStream(_loginSocket.getOutputStream());
 				
-				//init Blowfish
+				// init Blowfish
 				_blowfishKey = Util.generateHex(40);
 				_blowfish = new NewCrypt("_;v.]05-31!|+-%xT!^[$\00");
 				while (!isInterrupted())
 				{
 					lengthLo = _in.read();
 					lengthHi = _in.read();
-					length = lengthHi * 256 + lengthLo;
+					length = (lengthHi * 256) + lengthLo;
 					
 					if (lengthHi < 0)
 					{
@@ -188,22 +188,22 @@ public class LoginServerThread extends Thread
 					int receivedBytes = 0;
 					int newBytes = 0;
 					int left = length - 2;
-					while (newBytes != -1 && receivedBytes < length - 2)
+					while ((newBytes != -1) && (receivedBytes < (length - 2)))
 					{
-						newBytes =  _in.read(incoming, receivedBytes, left);
+						newBytes = _in.read(incoming, receivedBytes, left);
 						receivedBytes = receivedBytes + newBytes;
 						left -= newBytes;
 					}
 					
-					if (receivedBytes != length - 2)
+					if (receivedBytes != (length - 2))
 					{
 						_log.warning("Incomplete Packet is sent to the server, closing connection.(LS)");
 						break;
 					}
 					
 					// decrypt if we have a key
-					byte[] decrypt = _blowfish.decrypt(incoming);
-					checksumOk = NewCrypt.verifyChecksum(decrypt);
+					_blowfish.decrypt(incoming, 0, incoming.length);
+					checksumOk = NewCrypt.verifyChecksum(incoming);
 					
 					if (!checksumOk)
 					{
@@ -212,18 +212,21 @@ public class LoginServerThread extends Thread
 					}
 					
 					if (Config.DEBUG)
-						_log.warning("[C]\n" + Util.printData(decrypt));
-					
-					int packetType = decrypt[0] & 0xff;
+					{
+						_log.warning("[C]\n" + Util.printData(incoming));
+					}
+					int packetType = incoming[0] & 0xff;
 					switch (packetType)
 					{
 						case 0x00:
-							InitLS init = new InitLS(decrypt);
+							InitLS init = new InitLS(incoming);
 							if (Config.DEBUG)
+							{
 								_log.info("Init received");
+							}
 							if (init.getRevision() != REVISION)
 							{
-								//TODO: revision mismatch
+								// TODO: revision mismatch
 								_log.warning("/!\\ Revision mismatch between LS and GS /!\\");
 								break;
 							}
@@ -234,7 +237,9 @@ public class LoginServerThread extends Thread
 								RSAPublicKeySpec kspec1 = new RSAPublicKeySpec(modulus, RSAKeyGenParameterSpec.F4);
 								_publicKey = (RSAPublicKey) kfac.generatePublic(kspec1);
 								if (Config.DEBUG)
+								{
 									_log.info("RSA key set up");
+								}
 							}
 							
 							catch (GeneralSecurityException e)
@@ -242,27 +247,33 @@ public class LoginServerThread extends Thread
 								_log.warning("Troubles while init the public key send by login");
 								break;
 							}
-							//send the blowfish key through the rsa encryption
+							// send the blowfish key through the rsa encryption
 							BlowFishKey bfk = new BlowFishKey(_blowfishKey, _publicKey);
 							sendPacket(bfk);
 							if (Config.DEBUG)
+							{
 								_log.info("Sent new blowfish key");
-							//now, only accept packet with the new encryption
+							}
+							// now, only accept packet with the new encryption
 							_blowfish = new NewCrypt(_blowfishKey);
 							if (Config.DEBUG)
+							{
 								_log.info("Changed blowfish key");
+							}
 							AuthRequest ar = new AuthRequest(_requestID, _acceptAlternate, _hexID, _gamePort, _reserveHost, _maxPlayer, _subnets, _hosts);
 							sendPacket(ar);
 							if (Config.DEBUG)
+							{
 								_log.info("Sent AuthRequest to login");
+							}
 							break;
 						case 0x01:
-							LoginServerFail lsf = new LoginServerFail(decrypt);
+							LoginServerFail lsf = new LoginServerFail(incoming);
 							_log.info("Damn! Registeration Failed: " + lsf.getReasonString());
 							// login will close the connection here
 							break;
 						case 0x02:
-							AuthResponse aresp = new AuthResponse(decrypt);
+							AuthResponse aresp = new AuthResponse(incoming);
 							_serverID = aresp.getServerId();
 							_serverName = aresp.getServerName();
 							Config.saveHexid(_serverID, hexToString(_hexID));
@@ -310,7 +321,7 @@ public class LoginServerThread extends Thread
 							}
 							break;
 						case 0x03:
-							PlayerAuthResponse par = new PlayerAuthResponse(decrypt);
+							PlayerAuthResponse par = new PlayerAuthResponse(incoming);
 							String account = par.getAccount();
 							WaitingClient wcToRemove = null;
 							synchronized (_waitingClients)
@@ -328,8 +339,9 @@ public class LoginServerThread extends Thread
 								if (par.isAuthed())
 								{
 									if (Config.DEBUG)
-										_log.info("Login accepted player " + wcToRemove.account + " waited("
-												+ (GameTimeController.getGameTicks() - wcToRemove.timestamp) + "ms)");
+									{
+										_log.info("Login accepted player " + wcToRemove.account + " waited(" + (GameTimeController.getGameTicks() - wcToRemove.timestamp) + "ms)");
+									}
 									PlayerInGame pig = new PlayerInGame(par.getAccount());
 									sendPacket(pig);
 									wcToRemove.gameClient.setState(GameClientState.AUTHED);
@@ -341,7 +353,7 @@ public class LoginServerThread extends Thread
 								else
 								{
 									_log.warning("Session key is not correct. Closing connection for account " + wcToRemove.account + ".");
-									//wcToRemove.gameClient.getConnection().sendPacket(new LoginFail(LoginFail.SYSTEM_ERROR_LOGIN_LATER));
+									// wcToRemove.gameClient.getConnection().sendPacket(new LoginFail(LoginFail.SYSTEM_ERROR_LOGIN_LATER));
 									wcToRemove.gameClient.close(new LoginFail(LoginFail.SYSTEM_ERROR_LOGIN_LATER));
 									_accountsInGameServer.remove(wcToRemove.account);
 								}
@@ -349,15 +361,15 @@ public class LoginServerThread extends Thread
 							}
 							break;
 						case 0x04:
-							KickPlayer kp = new KickPlayer(decrypt);
+							KickPlayer kp = new KickPlayer(incoming);
 							doKickPlayer(kp.getAccount());
 							break;
 						case 0x05:
-							RequestCharacters rc = new RequestCharacters(decrypt);
+							RequestCharacters rc = new RequestCharacters(incoming);
 							getCharsOnServer(rc.getAccount());
 							break;
 						case 0x06:
-							new ChangePasswordResponse(decrypt);
+							new ChangePasswordResponse(incoming);
 							break;
 					}
 				}
@@ -365,7 +377,9 @@ public class LoginServerThread extends Thread
 			catch (UnknownHostException e)
 			{
 				if (Config.DEBUG)
+				{
 					_log.log(Level.WARNING, "", e);
+				}
 			}
 			catch (SocketException e)
 			{
@@ -381,7 +395,9 @@ public class LoginServerThread extends Thread
 				{
 					_loginSocket.close();
 					if (isInterrupted())
+					{
 						return;
+					}
 				}
 				catch (Exception e)
 				{
@@ -408,7 +424,9 @@ public class LoginServerThread extends Thread
 	public void addWaitingClientAndSendRequest(String acc, L2GameClient client, SessionKey key)
 	{
 		if (Config.DEBUG)
+		{
 			_log.info(String.valueOf(key));
+		}
 		WaitingClient wc = new WaitingClient(acc, client, key);
 		synchronized (_waitingClients)
 		{
@@ -423,7 +441,9 @@ public class LoginServerThread extends Thread
 		{
 			_log.warning("Error while sending player auth request");
 			if (Config.DEBUG)
+			{
 				_log.log(Level.WARNING, "", e);
+			}
 		}
 	}
 	
@@ -444,7 +464,9 @@ public class LoginServerThread extends Thread
 				}
 			}
 			if (toRemove != null)
+			{
 				_waitingClients.remove(toRemove);
+			}
 		}
 	}
 	
@@ -455,8 +477,9 @@ public class LoginServerThread extends Thread
 	public void sendLogout(String account)
 	{
 		if (account == null)
+		{
 			return;
-
+		}
 		PlayerLogout pl = new PlayerLogout(account);
 		try
 		{
@@ -466,7 +489,9 @@ public class LoginServerThread extends Thread
 		{
 			_log.warning("Error while sending logout packet to login");
 			if (Config.DEBUG)
+			{
 				_log.log(Level.WARNING, "", e);
+			}
 		}
 		finally
 		{
@@ -499,7 +524,9 @@ public class LoginServerThread extends Thread
 		catch (IOException e)
 		{
 			if (Config.DEBUG)
+			{
 				_log.log(Level.WARNING, "", e);
+			}
 		}
 	}
 	
@@ -518,7 +545,9 @@ public class LoginServerThread extends Thread
 		catch (IOException e)
 		{
 			if (Config.DEBUG)
+			{
 				_log.log(Level.WARNING, "", e);
+			}
 		}
 	}
 	
@@ -538,7 +567,9 @@ public class LoginServerThread extends Thread
 		catch (IOException e)
 		{
 			if (Config.DEBUG)
+			{
 				_log.log(Level.WARNING, "", e);
+			}
 		}
 	}
 	
@@ -558,7 +589,9 @@ public class LoginServerThread extends Thread
 		catch (IOException e)
 		{
 			if (Config.DEBUG)
+			{
 				_log.log(Level.WARNING, "", e);
+			}
 		}
 	}
 	
@@ -609,7 +642,9 @@ public class LoginServerThread extends Thread
 				chars++;
 				long delTime = rset.getLong("deletetime");
 				if (delTime != 0)
+				{
 					charToDel.add(delTime);
+				}
 			}
 			rset.close();
 			statement.close();
@@ -631,7 +666,9 @@ public class LoginServerThread extends Thread
 		catch (IOException e)
 		{
 			if (Config.DEBUG)
+			{
 				_log.log(Level.WARNING, "", e);
+			}
 		}
 	}
 	
@@ -645,14 +682,16 @@ public class LoginServerThread extends Thread
 		byte[] data = sl.getContent();
 		NewCrypt.appendChecksum(data);
 		if (Config.DEBUG)
+		{
 			_log.finest("[S]\n" + Util.printData(data));
-		data = _blowfish.crypt(data);
+		}
+		_blowfish.crypt(data, 0, data.length);
 		
 		int len = data.length + 2;
-		synchronized (_out) //avoids tow threads writing in the mean time
+		synchronized (_out) // avoids tow threads writing in the mean time
 		{
 			_out.write(len & 0xff);
-			_out.write(len >> 8 & 0xff);
+			_out.write((len >> 8) & 0xff);
 			_out.write(data);
 			_out.flush();
 		}
@@ -693,7 +732,9 @@ public class LoginServerThread extends Thread
 		catch (IOException e)
 		{
 			if (Config.DEBUG)
+			{
 				_log.log(Level.WARNING, "", e);
+			}
 		}
 	}
 	
@@ -711,7 +752,9 @@ public class LoginServerThread extends Thread
 		catch (IOException e)
 		{
 			if (Config.DEBUG)
+			{
 				_log.log(Level.WARNING, "", e);
+			}
 		}
 	}
 	
@@ -732,7 +775,9 @@ public class LoginServerThread extends Thread
 		catch (IOException e)
 		{
 			if (Config.DEBUG)
+			{
 				_log.log(Level.WARNING, "", e);
+			}
 		}
 	}
 	

+ 2 - 2
L2J_Server_BETA/java/com/l2jserver/loginserver/GameServerThread.java

@@ -116,7 +116,7 @@ public class GameServerThread extends Thread
 				}
 				
 				// decrypt if we have a key
-				data = _blowfish.decrypt(data);
+				_blowfish.decrypt(data, 0, data.length);
 				checksumOk = NewCrypt.verifyChecksum(data);
 				if (!checksumOk)
 				{
@@ -239,7 +239,7 @@ public class GameServerThread extends Thread
 			{
 				_log.finest("[S] " + sl.getClass().getSimpleName() + ":\n" + Util.printData(data));
 			}
-			data = _blowfish.crypt(data);
+			_blowfish.crypt(data, 0, data.length);
 			
 			int len = data.length + 2;
 			synchronized (_out)

+ 9 - 12
L2J_Server_BETA/java/com/l2jserver/loginserver/network/L2LoginClient.java

@@ -92,10 +92,17 @@ public final class L2LoginClient extends MMOClient<MMOConnection<L2LoginClient>>
 	@Override
 	public boolean decrypt(ByteBuffer buf, int size)
 	{
-		boolean ret = false;
+		boolean isChecksumValid = false;
 		try
 		{
-			ret = _loginCrypt.decrypt(buf.array(), buf.position(), size);
+			isChecksumValid = _loginCrypt.decrypt(buf.array(), buf.position(), size);
+			if (!isChecksumValid)
+			{
+				_log.warning("Wrong checksum from client: " + toString());
+				super.getConnection().close((SendablePacket<L2LoginClient>) null);
+				return false;
+			}
+			return true;
 		}
 		catch (IOException e)
 		{
@@ -103,16 +110,6 @@ public final class L2LoginClient extends MMOClient<MMOConnection<L2LoginClient>>
 			super.getConnection().close((SendablePacket<L2LoginClient>) null);
 			return false;
 		}
-		
-		if (!ret)
-		{
-			byte[] dump = new byte[size];
-			System.arraycopy(buf.array(), buf.position(), dump, 0, size);
-			_log.warning("Wrong checksum from client: " + toString());
-			super.getConnection().close((SendablePacket<L2LoginClient>) null);
-		}
-		
-		return ret;
 	}
 	
 	/**

+ 245 - 74
L2J_Server_BETA/java/com/l2jserver/util/crypt/BlowfishEngine.java

@@ -33,7 +33,7 @@ import java.io.IOException;
  * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,<br>
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
-public class BlowfishEngine
+public final class BlowfishEngine
 {
 	private static final int[] KP =
 	{
@@ -1102,7 +1102,6 @@ public class BlowfishEngine
 	private static final int P_SZ = ROUNDS + 2;
 	private final int[] S0, S1, S2, S3; // the s-boxes
 	private final int[] P; // the p-array
-	private boolean encrypting = false;
 	private byte[] workingKey = null;
 	
 	public BlowfishEngine()
@@ -1116,16 +1115,12 @@ public class BlowfishEngine
 	
 	/**
 	 * Initialize a Blowfish cipher.
-	 * @param pEncrypting whether or not we are for encryption.
-	 * @param key the key used to set up the cipher.
-	 * @exception IllegalArgumentException if the params argument is inappropriate.
+	 * @param key the key used to set up the cipher
 	 */
-	public void init(boolean pEncrypting, byte[] key)
+	public void init(byte[] key)
 	{
-		encrypting = pEncrypting;
 		workingKey = key;
 		setKey(workingKey);
-		return;
 	}
 	
 	public String getAlgorithmName()
@@ -1133,35 +1128,6 @@ public class BlowfishEngine
 		return "Blowfish";
 	}
 	
-	public final int processBlock(byte[] in, int inOff, byte[] out, int outOff) throws IOException
-	{
-		if (workingKey == null)
-		{
-			throw new IllegalStateException("Blowfish not initialised");
-		}
-		if ((inOff + BLOCK_SIZE) > in.length)
-		{
-			throw new IOException("input buffer too short");
-		}
-		if ((outOff + BLOCK_SIZE) > out.length)
-		{
-			throw new IOException("output buffer too short");
-		}
-		if (encrypting)
-		{
-			encryptBlock(in, inOff, out, outOff);
-		}
-		else
-		{
-			decryptBlock(in, inOff, out, outOff);
-		}
-		return BLOCK_SIZE;
-	}
-	
-	public void reset()
-	{
-	}
-	
 	public int getBlockSize()
 	{
 		return BLOCK_SIZE;
@@ -1187,12 +1153,24 @@ public class BlowfishEngine
 		for (int s = 0; s < size; s += 2)
 		{
 			xl ^= P[0];
-			for (int i = 1; i < ROUNDS; i += 2)
-			{
-				xr ^= func(xl) ^ P[i];
-				xl ^= func(xr) ^ P[i + 1];
-			}
-			xr ^= P[ROUNDS + 1];
+			xr ^= func(xl) ^ P[1];
+			xl ^= func(xr) ^ P[2];
+			xr ^= func(xl) ^ P[3];
+			xl ^= func(xr) ^ P[4];
+			xr ^= func(xl) ^ P[5];
+			xl ^= func(xr) ^ P[6];
+			xr ^= func(xl) ^ P[7];
+			xl ^= func(xr) ^ P[8];
+			xr ^= func(xl) ^ P[9];
+			xl ^= func(xr) ^ P[10];
+			xr ^= func(xl) ^ P[11];
+			xl ^= func(xr) ^ P[12];
+			xr ^= func(xl) ^ P[13];
+			xl ^= func(xr) ^ P[14];
+			xr ^= func(xl) ^ P[15];
+			xl ^= func(xr) ^ P[16];
+			xr ^= P[17];
+			
 			table[s] = xr;
 			table[s + 1] = xl;
 			xr = xl; // end of cycle swap
@@ -1250,59 +1228,252 @@ public class BlowfishEngine
 	}
 	
 	/**
-	 * Encrypt the given input starting at the given offset and place the result in the provided buffer starting at the given offset. The input will be an exact multiple of our blocksize.
-	 * @param src
-	 * @param srcIndex
-	 * @param dst
-	 * @param dstIndex
+	 * Method to encrypt the block at the given index.<br>
+	 * The encrypted block goes directly to the source array at the given<br>
+	 * index.
+	 * @param src source array with the plain data
+	 * @param srcIndex index where the block to encrypt is located
+	 * @throws IllegalStateException The cipher was not yet initialized
+	 * @throws IOException The source array is too small to hold a block at the given index
+	 */
+	public void tryEncryptBlock(byte[] src, final int srcIndex) throws IOException
+	{
+		if (workingKey == null)
+		{
+			throw new IllegalStateException("Blowfish not initialised");
+		}
+		if ((srcIndex + BLOCK_SIZE) > src.length)
+		{
+			throw new IOException("input buffer too short");
+		}
+		encryptBlock(src, srcIndex);
+	}
+	
+	/**
+	 * Method to encrypt the block at the given index.<br>
+	 * The encrypted block goes to the destination array at the given index.<br>
+	 * <br>
+	 * @param src source array with the plain data
+	 * @param srcIndex index where the block to encrypt is located
+	 * @param dst destination array the encryption will go to
+	 * @param dstIndex index where the encrypted block is to be stored
+	 * @throws IllegalStateException The cipher was not yet initialized
+	 * @throws IOException The source or destination array is too small to hold a block at the given index
 	 */
-	private void encryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex)
+	public void tryEncryptBlock(final byte[] src, final int srcIndex, byte[] dst, final int dstIndex) throws IOException
+	{
+		if (workingKey == null)
+		{
+			throw new IllegalStateException("Blowfish not initialised");
+		}
+		if ((srcIndex + BLOCK_SIZE) > src.length)
+		{
+			throw new IOException("input buffer too short");
+		}
+		if ((dstIndex + BLOCK_SIZE) > dst.length)
+		{
+			throw new IOException("output buffer too short");
+		}
+		encryptBlock(src, srcIndex, dst, dstIndex);
+	}
+	
+	/**
+	 * Method to encrypt the block at the given index.<br>
+	 * The encrypted block goes directly to the source array at the given<br>
+	 * index.<br>
+	 * <br>
+	 * This method does not perform any error checking. This could be<br>
+	 * usefull when code calling this method performs size checks already or<br>
+	 * perfroming steps to ensure nothing can go wrong.<br>
+	 * <br>
+	 * If you want error checking use {@link #tryEncryptBlock(byte[], int)}.
+	 * @param src source array with the plain data
+	 * @param srcIndex index where the block to encrypt is located
+	 */
+	public void encryptBlock(byte[] src, final int srcIndex)
+	{
+		encryptBlock(src, srcIndex, src, srcIndex);
+	}
+	
+	/**
+	 * Method to encrypt the block at the given index.<br>
+	 * The encrypted block goes to the destination array at the given index.<br>
+	 * <br>
+	 * This method does not perform any error checking. This could be<br>
+	 * usefull when code calling this method performs size checks already or<br>
+	 * perfroming steps to ensure nothing can go wrong.<br>
+	 * <br>
+	 * If you want error checking use {@link #tryEncryptBlock(byte[], int, byte[], int)}.
+	 * @param src source array with the plain data
+	 * @param srcIndex index where the block to encrypt is located
+	 * @param dst destination array the encryption will go to
+	 * @param dstIndex index where the encrypted block is to be stored
+	 */
+	public void encryptBlock(final byte[] src, final int srcIndex, byte[] dst, final int dstIndex)
 	{
 		int xl = bytesTo32bits(src, srcIndex);
 		int xr = bytesTo32bits(src, srcIndex + 4);
+		
 		xl ^= P[0];
-		for (int i = 1; i < ROUNDS; i += 2)
-		{
-			xr ^= func(xl) ^ P[i];
-			xl ^= func(xr) ^ P[i + 1];
-		}
-		xr ^= P[ROUNDS + 1];
+		xr ^= func(xl) ^ P[1];
+		xl ^= func(xr) ^ P[2];
+		xr ^= func(xl) ^ P[3];
+		xl ^= func(xr) ^ P[4];
+		xr ^= func(xl) ^ P[5];
+		xl ^= func(xr) ^ P[6];
+		xr ^= func(xl) ^ P[7];
+		xl ^= func(xr) ^ P[8];
+		xr ^= func(xl) ^ P[9];
+		xl ^= func(xr) ^ P[10];
+		xr ^= func(xl) ^ P[11];
+		xl ^= func(xr) ^ P[12];
+		xr ^= func(xl) ^ P[13];
+		xl ^= func(xr) ^ P[14];
+		xr ^= func(xl) ^ P[15];
+		xl ^= func(xr) ^ P[16];
+		xr ^= P[17];
+		
 		bits32ToBytes(xr, dst, dstIndex);
 		bits32ToBytes(xl, dst, dstIndex + 4);
 	}
 	
 	/**
-	 * Decrypt the given input starting at the given offset and place the result in the provided buffer starting at the given offset. The input will be an exact multiple of our blocksize.
-	 * @param src
-	 * @param srcIndex
-	 * @param dst
-	 * @param dstIndex
+	 * Method to decrypt the block at the given index.<br>
+	 * The decrypted block goes directly to the source array at the given<br>
+	 * index.
+	 * @param src source array with the encrypted data
+	 * @param srcIndex index where the block to decrypt is located
+	 * @throws IllegalStateException The cipher was not yet initialized
+	 * @throws IOException The source array is too small to hold a block at the given index
 	 */
-	private void decryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex)
+	public void tryDecryptBlock(byte[] src, final int srcIndex) throws IOException
 	{
-		int xl = bytesTo32bits(src, srcIndex);
-		int xr = bytesTo32bits(src, srcIndex + 4);
-		xl ^= P[ROUNDS + 1];
-		for (int i = ROUNDS; i > 0; i -= 2)
+		if (workingKey == null)
 		{
-			xr ^= func(xl) ^ P[i];
-			xl ^= func(xr) ^ P[i - 1];
+			throw new IllegalStateException("Blowfish not initialised");
+		}
+		if ((srcIndex + BLOCK_SIZE) > src.length)
+		{
+			throw new IOException("input buffer too short");
 		}
+		decryptBlock(src, srcIndex);
+	}
+	
+	/**
+	 * Method to decrypt the block at the given index.<br>
+	 * The decrypted block goes to the destination array at the given index.<br>
+	 * @param src source array with the plain data
+	 * @param srcIndex index where the block to decrypt is located
+	 * @param dst destination array the decryption will go to
+	 * @param dstIndex index where the decrypted block is to be stored
+	 * @throws IllegalStateException The cipher was not yet initialized
+	 * @throws IOException The source or destination array is too small to hold a block at the given index
+	 */
+	public void tryDecryptBlock(final byte[] src, final int srcIndex, byte[] dst, final int dstIndex) throws IOException
+	{
+		if (workingKey == null)
+		{
+			throw new IllegalStateException("Blowfish not initialised");
+		}
+		if ((srcIndex + BLOCK_SIZE) > src.length)
+		{
+			throw new IOException("input buffer too short");
+		}
+		if ((dstIndex + BLOCK_SIZE) > src.length)
+		{
+			throw new IOException("output buffer too short");
+		}
+		decryptBlock(src, srcIndex, dst, dstIndex);
+	}
+	
+	/**
+	 * Method to decrypt the block at the given index.<br>
+	 * The decrypted block goes directly to the source array at the given<br>
+	 * index.<br>
+	 * <br>
+	 * This method does not perform any error checking. This could be<br>
+	 * usefull when code calling this method performs size checks already or<br>
+	 * perfroming steps to ensure nothing can go wrong.<br>
+	 * <br>
+	 * If you want error checking use {@link #tryDecryptBlock(byte[], int)}.
+	 * @param src source array with the encrypted data
+	 * @param srcIndex index where the block to decrypt is located
+	 */
+	public void decryptBlock(byte[] src, final int srcIndex)
+	{
+		decryptBlock(src, srcIndex, src, srcIndex);
+	}
+	
+	/**
+	 * Method to decrypt the block at the given index.<br>
+	 * The decrypted block goes to the destination array at the given index.<br>
+	 * <br>
+	 * This method does not perform any error checking. This could be<br>
+	 * usefull when code calling this method performs size checks already or<br>
+	 * perfroming steps to ensure nothing can go wrong.<br>
+	 * <br>
+	 * If you want error checking use {@link #tryDecryptBlock(byte[], int, byte[], int)}.
+	 * @param src source array with the plain data
+	 * @param srcIndex index where the block to decrypt is located
+	 * @param dst destination array the decryption will go to
+	 * @param dstIndex index where the decrypted block is to be stored
+	 * @throws IllegalStateException The cipher was not yet initialized
+	 */
+	public void decryptBlock(final byte[] src, final int srcIndex, byte[] dst, final int dstIndex)
+	{
+		int xl = bytesTo32bits(src, srcIndex);
+		int xr = bytesTo32bits(src, srcIndex + 4);
+		
+		xl ^= P[17];
+		xr ^= func(xl) ^ P[16];
+		xl ^= func(xr) ^ P[15];
+		xr ^= func(xl) ^ P[14];
+		xl ^= func(xr) ^ P[13];
+		xr ^= func(xl) ^ P[12];
+		xl ^= func(xr) ^ P[11];
+		xr ^= func(xl) ^ P[10];
+		xl ^= func(xr) ^ P[9];
+		xr ^= func(xl) ^ P[8];
+		xl ^= func(xr) ^ P[7];
+		xr ^= func(xl) ^ P[6];
+		xl ^= func(xr) ^ P[5];
+		xr ^= func(xl) ^ P[4];
+		xl ^= func(xr) ^ P[3];
+		xr ^= func(xl) ^ P[2];
+		xl ^= func(xr) ^ P[1];
 		xr ^= P[0];
+		
 		bits32ToBytes(xr, dst, dstIndex);
 		bits32ToBytes(xl, dst, dstIndex + 4);
 	}
 	
-	private int bytesTo32bits(byte[] b, int i)
+	/**
+	 * Method to construct an int from the source array.<br>
+	 * 4 bytes are used from the given index.<br>
+	 * <br>
+	 * This method does not do any error checking.
+	 * @param src source array with the bytes
+	 * @param srcIndex the index to extract the int from
+	 * @return the extracted integer
+	 */
+	private int bytesTo32bits(byte[] src, int srcIndex)
 	{
-		return ((b[i + 3] & 0xff) << 24) | ((b[i + 2] & 0xff) << 16) | ((b[i + 1] & 0xff) << 8) | ((b[i] & 0xff));
+		return ((src[srcIndex + 3] & 0xff) << 24) | ((src[srcIndex + 2] & 0xff) << 16) | ((src[srcIndex + 1] & 0xff) << 8) | ((src[srcIndex] & 0xff));
 	}
 	
-	private void bits32ToBytes(int in, byte[] b, int offset)
+	/**
+	 * Method to decompose an int into a byte array.<br>
+	 * <br>
+	 * This method does not do any error checking.
+	 * @param in the integer to decompose into bytes
+	 * @param dst the destination array the decomposed int goes to
+	 * @param dstIndex the index in the destination array the decomposed int will be stored at
+	 */
+	private void bits32ToBytes(int in, byte[] dst, int dstIndex)
 	{
-		b[offset] = (byte) in;
-		b[offset + 1] = (byte) (in >> 8);
-		b[offset + 2] = (byte) (in >> 16);
-		b[offset + 3] = (byte) (in >> 24);
+		dst[dstIndex] = (byte) in;
+		dst[dstIndex + 1] = (byte) (in >> 8);
+		dst[dstIndex + 2] = (byte) (in >> 16);
+		dst[dstIndex + 3] = (byte) (in >> 24);
 	}
 }

+ 42 - 4
L2J_Server_BETA/java/com/l2jserver/util/crypt/LoginCrypt.java

@@ -43,21 +43,52 @@ public class LoginCrypt
 		(byte) 0x6c
 	};
 	
-	private final NewCrypt _staticCrypt = new NewCrypt(STATIC_BLOWFISH_KEY);
-	private NewCrypt _crypt;
+	private static final NewCrypt _STATIC_CRYPT = new NewCrypt(STATIC_BLOWFISH_KEY);
+	private NewCrypt _crypt = null;
 	private boolean _static = true;
 	
+	/**
+	 * Method to initialize the the blowfish cipher with dynamic key.
+	 * @param key the blowfish key to initialize the dynamic blowish cipher with
+	 */
 	public void setKey(byte[] key)
 	{
 		_crypt = new NewCrypt(key);
 	}
 	
+	/**
+	 * Method to decrypt an incomming login client packet.<br>
+	 * @param raw array with encrypted data
+	 * @param offset offset where the encrypted data is located
+	 * @param size number of bytes of encrypted data
+	 * @return true when checksum could be verified, false otherwise
+	 * @throws IOException the size is not multiple of blowfishs block size or the raw array can't hold size bytes starting at offset due to it's size
+	 */
 	public boolean decrypt(byte[] raw, final int offset, final int size) throws IOException
 	{
+		if ((size % 8) != 0)
+		{
+			throw new IOException("size have to be multiple of 8");
+		}
+		if ((offset + size) > raw.length)
+		{
+			throw new IOException("raw array too short for size starting from offset");
+		}
+		
 		_crypt.decrypt(raw, offset, size);
 		return NewCrypt.verifyChecksum(raw, offset, size);
 	}
 	
+	/**
+	 * Method to encrypt an outgoing packet to login client.<br>
+	 * <br>
+	 * Performs padding and resizing of data array.<br>
+	 * @param raw array with plain data
+	 * @param offset offset where the plain data is located
+	 * @param size number of bytes of plain data
+	 * @return the new array size
+	 * @throws IOException packet is too long to make padding and add verification data
+	 */
 	public int encrypt(byte[] raw, final int offset, int size) throws IOException
 	{
 		// reserve checksum
@@ -70,15 +101,22 @@ public class LoginCrypt
 			
 			// padding
 			size += 8 - (size % 8);
+			if ((offset + size) > raw.length)
+			{
+				throw new IOException("packet too long");
+			}
 			NewCrypt.encXORPass(raw, offset, size, Rnd.nextInt());
-			_staticCrypt.crypt(raw, offset, size);
-			
+			_STATIC_CRYPT.crypt(raw, offset, size);
 			_static = false;
 		}
 		else
 		{
 			// padding
 			size += 8 - (size % 8);
+			if ((offset + size) > raw.length)
+			{
+				throw new IOException("packet too long");
+			}
 			NewCrypt.appendChecksum(raw, offset, size);
 			_crypt.crypt(raw, offset, size);
 		}

+ 73 - 64
L2J_Server_BETA/java/com/l2jserver/util/crypt/NewCrypt.java

@@ -14,28 +14,25 @@
  */
 package com.l2jserver.util.crypt;
 
-import java.io.IOException;
-import java.util.logging.Logger;
-
 /**
- * This class ...
- * @version $Revision: 1.3.4.1 $ $Date: 2005/03/27 15:30:09 $
+ * Class to use a blowfish cipher with ECB processing.<br>
+ * <br>
+ * Static methods are present to append/check the checksum of<br>
+ * packets exchanged between the following partners:<br>
+ * Login Server <-> Game Client<br>
+ * Login Server <-> Game Server<br>
+ * <br>
+ * Also a static method is provided for the initial xor encryption<br>
+ * between Login Server <-> Game Client.
  */
-public class NewCrypt
+public final class NewCrypt
 {
-	protected static Logger _log = Logger.getLogger(NewCrypt.class.getName());
-	BlowfishEngine _crypt;
-	BlowfishEngine _decrypt;
+	private final BlowfishEngine _cipher;
 	
-	/**
-	 * @param blowfishKey
-	 */
 	public NewCrypt(byte[] blowfishKey)
 	{
-		_crypt = new BlowfishEngine();
-		_crypt.init(true, blowfishKey);
-		_decrypt = new BlowfishEngine();
-		_decrypt.init(false, blowfishKey);
+		_cipher = new BlowfishEngine();
+		_cipher.init(blowfishKey);
 	}
 	
 	public NewCrypt(String key)
@@ -43,12 +40,29 @@ public class NewCrypt
 		this(key.getBytes());
 	}
 	
-	public static boolean verifyChecksum(byte[] raw)
+	/**
+	 * Equivalent to calling {@link #verifyChecksum(byte[], int, int)}<br>
+	 * with parameters (raw, 0, raw.length)
+	 * @param raw data array to be verified
+	 * @return true when the checksum of the data is valid, false otherwise
+	 * @see #verifyChecksum(byte[], int, int)
+	 */
+	public static boolean verifyChecksum(final byte[] raw)
 	{
 		return NewCrypt.verifyChecksum(raw, 0, raw.length);
 	}
 	
-	public static boolean verifyChecksum(byte[] raw, final int offset, final int size)
+	/**
+	 * Method to verify the checksum of a packet received by<br>
+	 * login server from game client.<br>
+	 * <br>
+	 * This is also used for game server <-> login server communication.
+	 * @param raw data array to be verified
+	 * @param offset at which offset to start verifying
+	 * @param size number of bytes to verify
+	 * @return true if the checksum of the data is valid, false otherwise
+	 */
+	public static boolean verifyChecksum(final byte[] raw, final int offset, final int size)
 	{
 		// check if size is multiple of 4 and if there is more then only the checksum
 		if (((size & 3) != 0) || (size <= 4))
@@ -79,12 +93,23 @@ public class NewCrypt
 		return check == chksum;
 	}
 	
-	public static void appendChecksum(byte[] raw)
+	/**
+	 * Equivalent to calling {@link #appendChecksum(byte[], int, int)}<br>
+	 * with parameters (raw, 0, raw.length)
+	 * @param raw data array to compute the checksum from
+	 */
+	public static void appendChecksum(final byte[] raw)
 	{
 		NewCrypt.appendChecksum(raw, 0, raw.length);
 	}
 	
-	public static void appendChecksum(byte[] raw, final int offset, final int size)
+	/**
+	 * Method to append packet checksum at the end of the packet.
+	 * @param raw data array to compute the checksum from
+	 * @param offset offset where to start in the data array
+	 * @param size number of bytes to compute the checksum from
+	 */
+	public static void appendChecksum(final byte[] raw, final int offset, final int size)
 	{
 		long chksum = 0;
 		int count = size - 4;
@@ -117,7 +142,7 @@ public class NewCrypt
 	 * @param raw The raw bytes to be encrypted
 	 * @param key The 4 bytes (int) XOR key
 	 */
-	public static void encXORPass(byte[] raw, int key)
+	public static void encXORPass(final byte[] raw, final int key)
 	{
 		NewCrypt.encXORPass(raw, 0, raw.length, key);
 	}
@@ -129,7 +154,7 @@ public class NewCrypt
 	 * @param size Length of the data to be encrypted
 	 * @param key The 4 bytes (int) XOR key
 	 */
-	public static void encXORPass(byte[] raw, final int offset, final int size, int key)
+	public static void encXORPass(final byte[] raw, final int offset, final int size, int key)
 	{
 		int stop = size - 8;
 		int pos = 4 + offset;
@@ -159,55 +184,39 @@ public class NewCrypt
 		raw[pos++] = (byte) ((ecx >> 24) & 0xFF);
 	}
 	
-	public byte[] decrypt(byte[] raw) throws IOException
-	{
-		byte[] result = new byte[raw.length];
-		int count = raw.length / 8;
-		
-		for (int i = 0; i < count; i++)
-		{
-			_decrypt.processBlock(raw, i * 8, result, i * 8);
-		}
-		
-		return result;
-	}
-	
-	public void decrypt(byte[] raw, final int offset, final int size) throws IOException
-	{
-		byte[] result = new byte[size];
-		int count = size / 8;
-		
-		for (int i = 0; i < count; i++)
-		{
-			_decrypt.processBlock(raw, offset + (i * 8), result, i * 8);
-		}
-		// TODO can the crypt and decrypt go direct to the array
-		System.arraycopy(result, 0, raw, offset, size);
-	}
-	
-	public byte[] crypt(byte[] raw) throws IOException
+	/**
+	 * Method to decrypt using Blowfish-Blockcipher in ECB mode. The results<br>
+	 * will be directly placed inside {@code raw} array.<br>
+	 * <br>
+	 * This method does not do any error checking, since the calling code<br>
+	 * should ensure sizes.
+	 * @param raw the data array to be decrypted
+	 * @param offset the offset at which to start decrypting
+	 * @param size the number of bytes to be decrypted
+	 */
+	public void decrypt(byte[] raw, final int offset, final int size)
 	{
-		int count = raw.length / 8;
-		byte[] result = new byte[raw.length];
-		
-		for (int i = 0; i < count; i++)
+		for (int i = offset; i < (offset + size); i += 8)
 		{
-			_crypt.processBlock(raw, i * 8, result, i * 8);
+			_cipher.decryptBlock(raw, i);
 		}
-		
-		return result;
 	}
 	
-	public void crypt(byte[] raw, final int offset, final int size) throws IOException
+	/**
+	 * Method to encrypt using Blowfish-Blockcipher in ECB mode. The results<br>
+	 * will be directly placed inside {@code raw} array.<br>
+	 * <br>
+	 * This method does not do any error checking, since the calling code<br>
+	 * should ensure sizes.
+	 * @param raw the data array to be decrypted
+	 * @param offset the offset at which to start decrypting
+	 * @param size the number of bytes to be decrypted
+	 */
+	public void crypt(byte[] raw, final int offset, final int size)
 	{
-		int count = size / 8;
-		byte[] result = new byte[size];
-		
-		for (int i = 0; i < count; i++)
+		for (int i = offset; i < (offset + size); i += 8)
 		{
-			_crypt.processBlock(raw, offset + (i * 8), result, i * 8);
+			_cipher.encryptBlock(raw, i);
 		}
-		// TODO can the crypt and decrypt go direct to the array
-		System.arraycopy(result, 0, raw, offset, size);
 	}
 }