2

I have been working on a networking project recently to revive a dead mmo game for personal learning, I have a python implementation which works to decode the game data using blowfish(pypi / pycryptodome) and would like to transfer this 'server' into a java project.

Initially using blowfish decryption in java (BouncyCastle and Cipher -- default) I was getting entirely different results between java and python. Through some research I found that java (along with most things) actually use blowfish-compat big endian.

This one python library seems to be the only one which correctly decodes the data. Next I decided to use a python asyncio server as a middle relay simply for encryption and decryption. The network flow now looks like this:

GameClient -> Java SocketServer -> Python server (decryption) -> Java SocketServer.

The original Python implementation results in these bytes in hex format:

32004a815f49367cc3691be26d7b668132506dc972d5a6bbad38299640c6e222c6e55096f50ff33711250675431633ca9ede

The Java implementation produces these results in hex format(using apache commons Hex.encodeHexString())

32004a815f49367cc3691be26d7b668132506dc972d5a6bbad38299640c6e222c6e5c65830d65f9b4d60eb26730685f486d7

Both of these hex representations are pre-blowfish decryption in Python they are just the raw bytes being sent from the game client.

My question is why do these bytes start off the same and then it seems java trails off? The python results are the correct results they are tested and work. I have tried wrapping the bytes in java in a buffer and then calling flip() however this did not produce the correct results either.

The code I am using is spread over multiple classes and files due to the server needing to be multithreaded to implement the python server as a middleman, but if I need to post my code to get a response I will happily edit and post what is needed. Any help is greatly appreciated

EDIT: CODE POST

Python Implementation

#!/usr/bin/env python3

import asyncio
import binascii
import blowfish
import ipaddress
import os
import struct
import sys

AUTH_BLOWFISHKEY = b"[;'.]94-31==-%&@!^+]\000"
bf = blowfish.Cipher(AUTH_BLOWFISHKEY, byte_order="little")


class EncryptionRelay(asyncio.Protocol):
    def connection_made(self, transport):
        self.transport = transport
        self.client = (transport.get_extra_info('peername')[0] + ":"    # IP
        + str(transport.get_extra_info('peername')[1]))             # port
        print("Connection from: " + self.client)


    def connection_lost(self, exc):
        print("Connection closed: " + self.client)


    def data_received(self, data):
        print(data.hex()) #python output above
        pt = b''.join(bf.decrypt_ecb(data[2:]))
        self.transport.write(pt)

    def closeSocket(self, reason):
        print(reason)
        self.transport.close()


def main():
    loop = asyncio.get_event_loop()
    coroutine = loop.create_server(EncryptionRelay, host=None, port=54556)
    server = loop.run_until_complete(coroutine)

    for socket in server.sockets:
        print("Listening on: " + socket.getsockname()[0] + ":" +
        str(socket.getsockname()[1]))
    try:
        loop.run_forever()
    except KeyboardInterrupt:
        pass

    server.close()
    loop.run_until_complete(server.wait_closed())
    loop.close()


if __name__ == "__main__":
    main()

Java Implementation

public AuthServer(int port) {
    serverGUI = new AuthServerGUI(port);

    try {
        serverSocket = new ServerSocket(port);
        relay = new PythonEncryptionRelay(this);
        new Thread(relay).start();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

@Override
public void run() {
    while(true) {
        try {
            Socket socket = serverSocket.accept();
            onConnection(socket); //sends an init packet to client -- irrelevant to question 

            byte[] incomingData = new byte[0];
            byte[] temp = new byte[1024];
            int k = -1;

            while((k = socket.getInputStream().read(temp, 0, temp.length)) > -1) {
                byte[] tbuff = new byte[incomingData.length + k];
                System.arraycopy(incomingData, 0, tbuff, 0, incomingData.length);
                System.arraycopy(temp, 0, tbuff, incomingData.length, k);
                incomingData = tbuff;

                receiveData(socket, incomingData);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

public void receiveData(Socket socket, byte[] data) {
    int lenLo = (int) (data[0]);
    int lenHi = (int) (data[1]);
    int length = lenHi * 256 + lenLo;


    if(lenHi < 0) {
        System.out.println("Invalid Packet Length");
    }

    if(data.length != length) {
        System.out.println("Incomplete Packet Received");
    }

    serverGUI.serverDebug("DATA RECEIVED");
    serverGUI.serverDebug(Hex.encodeHexString(data)); //this is the java ouput above serverGUI is simply a jframe i built no data manipulation
    serverGUI.serverDebug("DATA_RECEIVED DONE");
    this.relay.sendData(data); //this function sends the data from socket server to the python asyncio server
}

public void receiveDataFromPythonRelay(Socket socket, byte[] data) {
    serverGUI.debugPythonRelay("DATA RECEIVED");
    serverGUI.debugPythonRelay(Hex.encodeHexString(data)); //this will be the output from the python script aka data decrypted. 
//The data byte[] is created in the exact same way the incomingData array is built in the AuthServer run function
    serverGUI.debugPythonRelay("DATA_RECEIVED DONE");
}

Additionally the way I am importing the data byte[] from sockets is programmed as such because the client does not send endl therefore readLine will not work from the streams.

Walter Tross
  • 12,237
  • 2
  • 40
  • 64
  • [This may get you in the right direction](https://stackoverflow.com/questions/38558820/hashing-raw-bytes-in-python-and-java-produces-different-results), though I can't help further unless I see some code tbh – Edeki Okoh Feb 21 '19 at 20:42
  • 1
    I had already found this link, that is where I got the idea to flip() the buffer. I will edit and post some of the code so you can see what im working with – Cody Hafemeister Feb 21 '19 at 21:04
  • 1
    In the future you should always post code and any relevant Stack Overflow answers you have used in your question to give more context to what has already been tried and what has failed. – Edeki Okoh Feb 21 '19 at 21:05
  • Good point! The code is posted now if that helps you at all to see my errors? – Cody Hafemeister Feb 21 '19 at 21:17
  • Is the differing tail of the data by chance different each time you repeat the communication? Or is it by chance the same if you transmit different data? Or any other combination that could hint to “rubbish data”? – Walter Tross Feb 22 '19 at 10:27
  • I read a post earlier this morning where java had extra bytes attached to the end, so the solution was to just cut that extra padding off that java puts in by default. However these sequences are identical in length meaning they should be a flat out match. – Cody Hafemeister Feb 23 '19 at 03:17

0 Answers0