Packet decryption problem

12/20/2011 18:18 Kiyono#1
So I tried downgrading the Albetros source to 5017 and posted this issue in that thread but since I don't know how many people actually check that thread, I'll post it here as well. The problem is as follows:

The gameserver only handles the first packet properly, in other words packet 1052 is handled ok (correct sizes, type w/e) but then when it receives the next packet, it has these weird huge sizes and wacky IDs such as the charcreation packet becoming ID 34669 instead of 1001.

pro suggested to check the decryption counters and here's my reply:
Quote:
Originally Posted by Kiyono View Post
Well I checked the encryption counters and it all works properly but just in case I overlooked something I decided to swap out the encryption with one from Hybrid's source since I know it works but it still failed, though, at least now every now and then I get the correct packet ID (actually showed 1001) but that was just once in...10+ times of trying and the size was 17k+.

Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Albetros.Game.Encryption //Hybrid's source
{
    public class ConquerKeys
    [spoiler]{
        #region public static byte[] Key1 = new byte[256];
        public static byte[] Key1 = 
            {
			    0x9D, 0x90, 0x83, 0x8A, 0xD1, 0x8C, 0xE7, 0xF6, 0x25, 0x28, 0xEB, 0x82, 0x99, 0x64, 0x8F, 0x2E,
			    0x2D, 0x40, 0xD3, 0xFA, 0xE1, 0xBC, 0xB7, 0xE6, 0xB5, 0xD8, 0x3B, 0xF2, 0xA9, 0x94, 0x5F, 0x1E, 
		    	0xBD, 0xF0, 0x23, 0x6A, 0xF1, 0xEC, 0x87, 0xD6, 0x45, 0x88, 0x8B, 0x62, 0xB9, 0xC4, 0x2F, 0x0E, 
		        0x4D, 0xA0, 0x73, 0xDA, 0x01, 0x1C, 0x57, 0xC6, 0xD5, 0x38, 0xDB, 0xD2, 0xC9, 0xF4, 0xFF, 0xFE, 
		    	0xDD, 0x50, 0xC3, 0x4A, 0x11, 0x4C, 0x27, 0xB6, 0x65, 0xE8, 0x2B, 0x42, 0xD9, 0x24, 0xCF, 0xEE, 
		    	0x6D, 0x00, 0x13, 0xBA, 0x21, 0x7C, 0xF7, 0xA6, 0xF5, 0x98, 0x7B, 0xB2, 0xE9, 0x54, 0x9F, 0xDE, 
		    	0xFD, 0xB0, 0x63, 0x2A, 0x31, 0xAC, 0xC7, 0x96, 0x85, 0x48, 0xCB, 0x22, 0xF9, 0x84, 0x6F, 0xCE, 
		        0x8D, 0x60, 0xB3, 0x9A, 0x41, 0xDC, 0x97, 0x86, 0x15, 0xF8, 0x1B, 0x92, 0x09, 0xB4, 0x3F, 0xBE, 
		        0x1D, 0x10, 0x03, 0x0A, 0x51, 0x0C, 0x67, 0x76, 0xA5, 0xA8, 0x6B, 0x02, 0x19, 0xE4, 0x0F, 0xAE, 
		    	0xAD, 0xC0, 0x53, 0x7A, 0x61, 0x3C, 0x37, 0x66, 0x35, 0x58, 0xBB, 0x72, 0x29, 0x14, 0xDF, 0x9E, 
			    0x3D, 0x70, 0xA3, 0xEA, 0x71, 0x6C, 0x07, 0x56, 0xC5, 0x08, 0x0B, 0xE2, 0x39, 0x44, 0xAF, 0x8E, 
			    0xCD, 0x20, 0xF3, 0x5A, 0x81, 0x9C, 0xD7, 0x46, 0x55, 0xB8, 0x5B, 0x52, 0x49, 0x74, 0x7F, 0x7E, 
		        0x5D, 0xD0, 0x43, 0xCA, 0x91, 0xCC, 0xA7, 0x36, 0xE5, 0x68, 0xAB, 0xC2, 0x59, 0xA4, 0x4F, 0x6E, 
			    0xED, 0x80, 0x93, 0x3A, 0xA1, 0xFC, 0x77, 0x26, 0x75, 0x18, 0xFB, 0x32, 0x69, 0xD4, 0x1F, 0x5E, 
			    0x7D, 0x30, 0xE3, 0xAA, 0xB1, 0x2C, 0x47, 0x16, 0x05, 0xC8, 0x4B, 0xA2, 0x79, 0x04, 0xEF, 0x4E, 
			    0x0D, 0xE0, 0x33, 0x1A, 0xC1, 0x5C, 0x17, 0x06, 0x95, 0x78, 0x9B, 0x12, 0x89, 0x34, 0xBF, 0x3E
            };
        #endregion
        #region public static byte[] Key2 = new byte[256];
        public static byte[] Key2 =
            {
				0x62, 0x4F, 0xE8, 0x15, 0xDE, 0xEB, 0x04, 0x91, 0x1A, 0xC7, 0xE0, 0x4D, 0x16, 0xE3, 0x7C, 0x49,
				0xD2, 0x3F, 0xD8, 0x85, 0x4E, 0xDB, 0xF4, 0x01, 0x8A, 0xB7, 0xD0, 0xBD, 0x86, 0xD3, 0x6C, 0xB9,
				0x42, 0x2F, 0xC8, 0xF5, 0xBE, 0xCB, 0xE4, 0x71, 0xFA, 0xA7, 0xC0, 0x2D, 0xF6, 0xC3, 0x5C, 0x29,
				0xB2, 0x1F, 0xB8, 0x65, 0x2E, 0xBB, 0xD4, 0xE1, 0x6A, 0x97, 0xB0, 0x9D, 0x66, 0xB3, 0x4C, 0x99,
				0x22, 0x0F, 0xA8, 0xD5, 0x9E, 0xAB, 0xC4, 0x51, 0xDA, 0x87, 0xA0, 0x0D, 0xD6, 0xA3, 0x3C, 0x09,
				0x92, 0xFF, 0x98, 0x45, 0x0E, 0x9B, 0xB4, 0xC1, 0x4A, 0x77, 0x90, 0x7D, 0x46, 0x93, 0x2C, 0x79,
				0x02, 0xEF, 0x88, 0xB5, 0x7E, 0x8B, 0xA4, 0x31, 0xBA, 0x67, 0x80, 0xED, 0xB6, 0x83, 0x1C, 0xE9,
				0x72, 0xDF, 0x78, 0x25, 0xEE, 0x7B, 0x94, 0xA1, 0x2A, 0x57, 0x70, 0x5D, 0x26, 0x73, 0x0C, 0x59,
				0xE2, 0xCF, 0x68, 0x95, 0x5E, 0x6B, 0x84, 0x11, 0x9A, 0x47, 0x60, 0xCD, 0x96, 0x63, 0xFC, 0xC9,
				0x52, 0xBF, 0x58, 0x05, 0xCE, 0x5B, 0x74, 0x81, 0x0A, 0x37, 0x50, 0x3D, 0x06, 0x53, 0xEC, 0x39,
				0xC2, 0xAF, 0x48, 0x75, 0x3E, 0x4B, 0x64, 0xF1, 0x7A, 0x27, 0x40, 0xAD, 0x76, 0x43, 0xDC, 0xA9,
				0x32, 0x9F, 0x38, 0xE5, 0xAE, 0x3B, 0x54, 0x61, 0xEA, 0x17, 0x30, 0x1D, 0xE6, 0x33, 0xCC, 0x19,
				0xA2, 0x8F, 0x28, 0x55, 0x1E, 0x2B, 0x44, 0xD1, 0x5A, 0x07, 0x20, 0x8D, 0x56, 0x23, 0xBC, 0x89,
				0x12, 0x7F, 0x18, 0xC5, 0x8E, 0x1B, 0x34, 0x41, 0xCA, 0xF7, 0x10, 0xFD, 0xC6, 0x13, 0xAC, 0xF9,
				0x82, 0x6F, 0x08, 0x35, 0xFE, 0x0B, 0x24, 0xB1, 0x3A, 0xE7, 0x00, 0x6D, 0x36, 0x03, 0x9C, 0x69,
				0xF2, 0x5F, 0xF8, 0xA5, 0x6E, 0xFB, 0x14, 0x21, 0xAA, 0xD7, 0xF0, 0xDD, 0xA6, 0xF3, 0x8C, 0xD9
            };
        #endregion
    }[/spoiler]
    public sealed class Cipher : IConquerCipher
    {
        private ushort InCounter;
        private ushort OutCounter;

        public Cipher()
        {
            InCounter = OutCounter = 0;
        }
        public void Encrypt(byte[] In, byte[] Out, int Length)
        {
            lock (this)
            {
                for (int i = 0; i < Length; i++)
                {
                    Out[i] = (byte)(In[i] ^ 0xAB);
                    Out[i] = (byte)((Out[i] << 4) | (Out[i] >> 4));
                    Out[i] = (byte)(ConquerKeys.Key2[OutCounter >> 8] ^ Out[i]);
                    Out[i] = (byte)(ConquerKeys.Key1[OutCounter & 0xFF] ^ Out[i]);
                    OutCounter = (ushort)(OutCounter + 1);
                }
            }
        }

        public void Decrypt(byte[] In, byte[] Out, int Length)
        {
            lock (this)
            {
                for (int i = 0; i < Length; i++)
                {
                    Out[i] = (byte)(In[i] ^ 0xAB);
                    Out[i] = (byte)((Out[i] << 4) | (Out[i] >> 4));
                    Out[i] = (byte)(ConquerKeys.Key2[InCounter >> 8] ^ Out[i]);
                    Out[i] = (byte)(ConquerKeys.Key1[InCounter & 0xFF] ^ Out[i]);
                    InCounter = (ushort)(InCounter + 1);
                }
            }
        }
    }
}
That's how it looks like now and this is where the decryption kicks in:
Code:
public override void OnPacketReceive(IClientWrapper client, byte[] buffer) //Most of it is from Hybrid's source
        {
            try
            {
                Player user = client as Player;
                if (user.Client.Connected && buffer.Length >= 4)
                {
                    var Out = new byte[buffer.Length];
                    lock (user.Cryptographer)
                    {
                        user.Cryptographer.Decrypt(buffer, Out, Out.Length);
#if DUMP
                        Kernel.WritePacket(Out);
#endif
                    }
                    byte[] Recv = Out;
                    int Counter = 0;
                    while (Counter < Recv.Length)
                    {
                        ushort Size = BitConverter.ToUInt16(Recv, Counter);
                        ushort Type = BitConverter.ToUInt16(Recv, Counter + 2);
                            
                        if (Size < Recv.Length)
                        {
                            byte[] InitialPacket = new byte[Size];
                            Buffer.BlockCopy(Recv, Counter, InitialPacket, 0, Size);
                            user.Handler.Handle(InitialPacket, Type);
                        }
                        else if (Size > Recv.Length)
                        {
                            OnError(client, new Exception("Packet ID: " + Type + " with size: " + Size + " is invalid"));
                            break;
                        }
                        else
                        {
                            user.Handler.Handle(Recv, Type);
                        }
                        Counter += Size;
                    }
                }
                else
                {
                    OnError(client, new Exception("An error occured with the client or the buffersize is invalid"));
                }
            }
            catch (Exception P)
            {
                Console.WriteLine(P.ToString());// throw new Exception(P.ToString()); 
            }
        }
So as said before, login works flawlessly and you can login several chars at once and it will work but after packet 1052 nothing gets decrypted properly.
Example of what happens when you try to create a char (logging in with an existing char gives the same type of issue)
[Only registered and activated users can see links. Click Here To Register...]
So any ideas? This is rather odd.
12/20/2011 18:28 CptSky#2
Do you generate the "third" and "fourth" key with the right values?
12/20/2011 18:49 Kiyono#3
Those were never referenced by anything in Hybrid's source so I assumed that those were unneeded, mind clarifying?
12/20/2011 19:29 CptSky#4
Quote:
Originally Posted by Kiyono View Post
Those were never referenced by anything in Hybrid's source so I assumed that those were unneeded, mind clarifying?
I will explain with the implementation I did of the algorithm.
[Only registered and activated users can see links. Click Here To Register...]

When the MsgServer receive a new connection, it creates a new COSAC object. Also, it generates the IV with the public keys (P, G). These keys are suppose to be known by the server and the client. On a normal client/server, P: 0x13FA0F9D, G: 0x6D5C7962. After, this, the client should send the MsgConnect packet (1052) with the privates keys (A, B) that as been specified by the AccServer. The packet is crypted with the IV, so the server can decrypt it without any problem. With this packet, the MsgServer will check if the client use the right privates keys. If so, it will generate the Key with the private keys (A, B). In CO2, A = Token, B = AccountUID. When the server will generate the Key, it will also reset the encrypt counter as the encrypt routine will still use the IV to crypt the data. The decrypt counter will remain the same and the decrypt routine will use the Key to crypt the data. After generating the Key, the server will answer to the MsgConnect with some packets.

For the AccServer, the private keys are sent with the same packet than the IP of the MsgServer.

The MsgLoginReply of the AccServer
Code:
        public struct MsgInfo
        {
            public MsgHeader Header;
            public Int32 AccountUID;
            public Int32 Token;
            public fixed Byte IPAddress[0x10];
            public Int32 Port;
        };
The MsgConnect of the MsgServer
Code:
        public struct MsgInfo
        {
            public MsgHeader Header;
            public Int32 AccountUID;
            public Int32 Token;
            public Int16 Constant; //In the CO2 app...
            public fixed Byte Language[_MAX_LANGUAGE_SIZE];
            public Int32 ResValue;
        };
By the way, the crypto you use is only valid for the AccServer as it does not handle the Key, but only the IV.
12/20/2011 20:10 Kiyono#5
Oh I see, that's what was wrong.
I simply added
Code:
user.Cryptographer.SetKeys((uint)BitConverter.ToUInt32(packet, 8), (uint)BitConverter.ToUInt32(packet, 4));
To the 1052 packet, stuck the SetKeys void in there and it works, no longer weird packet sizes.