Reworked CO2 Cipher (Unsafe)

06/22/2011 03:43 CptSky#1
Similar to my older cipher ([Only registered and activated users can see links. Click Here To Register...]), this one use pointers, so it can be considered as unsafe. In general, the speed is better with this crypto, but it doesn't change a lot... (Few ms...) Anyway, I think that the code is better than my older cipher. Maybe it will interest some people.

Special thanks to Sparkie for the IV generation algorithm.

Auth: Any client...
Game: 5017 & before...

Code:
// * ************************************************************
// * * START:                                          cosac.cs *
// * ************************************************************

// * ************************************************************
// *                      INFORMATIONS
// * ************************************************************
// * Conquer Online Server Asymmetric Cipher for the library.
// * cosac.cs
// * 
// * --
// *
// * Feel free to use this class in your projects, but don't
// * remove the header to keep the paternity of the class.
// * 
// * ************************************************************
// *                      CREDITS
// * ************************************************************
// * Originally created by CptSky (May 10th, 2011)
// * Copyright (C) 2011 CptSky
// *
// * ************************************************************
// *                      SPECIAL THANKS
// * ************************************************************
// * Sparkie (unknownone @ e*pvp)
// * 
// * ************************************************************
// *                      CHANGE LOG
// * ************************************************************
// *
// * ************************************************************

using System;

namespace CO2_CORE_DLL.Security
{
    /// <summary>
    /// Conquer Online Server Asymmetric Cipher
    /// </summary>
    public unsafe class COSAC
    {
        protected const Int32 COSAC_IV = 512;
        protected const Int32 COSAC_KEY = 512;

        protected Byte* m_BufIV = null;
        protected Byte* m_BufKey = null;
        protected UInt16 EncryptCounter = 0;
        protected UInt16 DecryptCounter = 0;

        /// <summary>
        /// Create a new COSAC instance.
        /// </summary>
        public COSAC() { }

        ~COSAC()
        {
            if (m_BufIV != null)
                Marshal.FreeHGlobal((IntPtr)m_BufIV);
            if (m_BufKey != null)
                Marshal.FreeHGlobal((IntPtr)m_BufKey);
        }

        /// <summary>
        /// Generates an initialization vector (IV) to use for the algorithm.
        /// CO2(P: 0x13FA0F9D, G: 0x6D5C7962)
        /// </summary>
        public void GenerateIV(Int32 P, Int32 G)
        {
            if (m_BufIV != null)
                Marshal.FreeHGlobal((IntPtr)m_BufIV);

            m_BufIV = (Byte*)Marshal.AllocHGlobal(COSAC_IV);
            Int16 K = COSAC_IV / 2;

            Byte* pBufPKey = (Byte*)&P;
            Byte* pBufGKey = (Byte*)&G;

            for (Int16 i = 0; i < K; i++)
            {
                m_BufIV[i + 0] = pBufPKey[0];
                m_BufIV[i + K] = pBufGKey[0];
                pBufPKey[0] = (Byte)((pBufPKey[1] + (Byte)(pBufPKey[0] * pBufPKey[2])) * pBufPKey[0] + pBufPKey[3]);
                pBufGKey[0] = (Byte)((pBufGKey[1] - (Byte)(pBufGKey[0] * pBufGKey[2])) * pBufGKey[0] + pBufGKey[3]);
            }
        }

        /// <summary>
        /// Generates a key (Key) to use for the algorithm and reset the encrypt counter.
        /// In Conquer Online: A = Token, B = AccountUID
        /// </summary>
        public void GenerateKey(Int32 A, Int32 B)
        {
            if (m_BufIV == null)
                throw new NullReferenceException("IV needs to be generated before generating the key!");

            if (m_BufKey != null)
                Marshal.FreeHGlobal((IntPtr)m_BufKey);

            m_BufKey = (Byte*)Marshal.AllocHGlobal(COSAC_KEY);
            Int16 K = COSAC_KEY / 2;

            UInt32 tmp1 = 0;
            tmp1 = (UInt32)(A + B);

            Byte* tmpKey1 = (Byte*)&tmp1;
            ((Int16*)tmpKey1)[0] ^= 0x4321;

            for (SByte i = 0; i < 4; i++)
                tmpKey1[3 - i] ^= (Byte)(A >> (24 - (8 * i)));

            UInt32 tmp2 = tmp1;
            tmp2 *= tmp2;

            Byte* tmpKey2 = (Byte*)&tmp2;

            for (Int16 i = 0; i < K; i++)
            {
                m_BufKey[i + 0] = (Byte)(m_BufIV[i + 0] ^ tmpKey1[(i % 4)]);
                m_BufKey[i + K] = (Byte)(m_BufIV[i + K] ^ tmpKey2[(i % 4)]);
            }
            EncryptCounter = 0;
        }

        /// <summary>
        /// Encrypts data with the COSAC algorithm.
        /// </summary>
        public void Encrypt(Byte* pBuf, Int32 Length)
        {
            if (pBuf == null)
                throw new NullReferenceException("Buffer can't be null!");

            if (Length <= 0)
                return;

            Int16 K = COSAC_IV / 2;
            for (Int32 i = 0; i < Length; i++)
            {
                pBuf[i] ^= (Byte)0xAB;
                pBuf[i] = (Byte)(pBuf[i] >> 4 | pBuf[i] << 4);
                if (m_BufIV != null)
                {
                    pBuf[i] ^= (Byte)(m_BufIV[(Byte)(EncryptCounter & 0xFF) + 0]);
                    pBuf[i] ^= (Byte)(m_BufIV[(Byte)(EncryptCounter >> 8) + K]);
                }
                EncryptCounter++;
            }
        }

        /// <summary>
        /// Encrypts data with the COSAC algorithm.
        /// </summary>
        public void Encrypt(ref Byte[] Buf)
        {
            Int32 Length = Buf.Length;
            fixed (Byte* pBuf = Buf)
            {
                if (pBuf == null)
                    throw new NullReferenceException("Buffer can't be null!");

                if (Length <= 0)
                    return;

                Int16 K = COSAC_IV / 2;
                for (Int32 i = 0; i < Length; i++)
                {
                    pBuf[i] ^= (Byte)0xAB;
                    pBuf[i] = (Byte)(pBuf[i] >> 4 | pBuf[i] << 4);
                    if (m_BufIV != null)
                    {
                        pBuf[i] ^= (Byte)(m_BufIV[(Byte)(EncryptCounter & 0xFF) + 0]);
                        pBuf[i] ^= (Byte)(m_BufIV[(Byte)(EncryptCounter >> 8) + K]);
                    }
                    EncryptCounter++;
                }
            }
        }

        /// <summary>
        /// Decrypts data with the COSAC algorithm.
        /// </summary>
        public void Decrypt(Byte* pBuf, Int32 Length)
        {
            if (pBuf == null)
                throw new NullReferenceException("Buffer can't be null!");

            if (Length <= 0)
                return;

            Int16 K = COSAC_IV / 2;
            if (m_BufKey != null)
                K = COSAC_KEY / 2;

            for (Int32 i = 0; i < Length; i++)
            {
                pBuf[i] ^= (Byte)0xAB;
                pBuf[i] = (Byte)(pBuf[i] >> 4 | pBuf[i] << 4);
                if (m_BufKey != null)
                {
                    pBuf[i] ^= (Byte)(m_BufKey[(Byte)(DecryptCounter & 0xFF) + 0]);
                    pBuf[i] ^= (Byte)(m_BufKey[(Byte)(DecryptCounter >> 8) + K]);
                }
                else if (m_BufIV != null)
                {
                    pBuf[i] ^= (Byte)(m_BufIV[(Byte)(DecryptCounter & 0xFF) + 0]);
                    pBuf[i] ^= (Byte)(m_BufIV[(Byte)(DecryptCounter >> 8) + K]);
                }
                DecryptCounter++;
            }
        }

        /// <summary>
        /// Decrypts data with the COSAC algorithm.
        /// </summary>
        public void Decrypt(ref Byte[] Buf)
        {
            Int32 Length = Buf.Length;
            fixed (Byte* pBuf = Buf)
            {
                if (pBuf == null)
                    throw new NullReferenceException("Buffer can't be null!");

                if (Length <= 0)
                    return;

                Int16 K = COSAC_IV / 2;
                if (m_BufKey != null)
                    K = COSAC_KEY / 2;

                for (Int32 i = 0; i < Length; i++)
                {
                    pBuf[i] ^= (Byte)0xAB;
                    pBuf[i] = (Byte)(pBuf[i] >> 4 | pBuf[i] << 4);
                    if (m_BufKey != null)
                    {
                        pBuf[i] ^= (Byte)(m_BufKey[(Byte)(DecryptCounter & 0xFF) + 0]);
                        pBuf[i] ^= (Byte)(m_BufKey[(Byte)(DecryptCounter >> 8) + K]);
                    }
                    else if (m_BufIV != null)
                    {
                        pBuf[i] ^= (Byte)(m_BufIV[(Byte)(DecryptCounter & 0xFF) + 0]);
                        pBuf[i] ^= (Byte)(m_BufIV[(Byte)(DecryptCounter >> 8) + K]);
                    }
                    DecryptCounter++;
                }
            }
        }

        /// <summary>
        /// Resets the decrypt and the encrypt counters.
        /// </summary>
        public void ResetCounters() { DecryptCounter = 0; EncryptCounter = 0; }
    }
}

// * ************************************************************
// * * END:                                            cosac.cs *
// * ************************************************************
06/22/2011 10:47 BaussHacker#2
This is very useful! :)
06/22/2011 10:54 KraHen#3
Though it`s pretty straightforward, still a useful release :)
06/22/2011 11:11 Korvacs#4
Anyone looking for Safe (instead of unsafe) version of this crypto can find it in the fusionorigins source i released, including the initial Key Creation (labelled GenerateIV in this).

But nice work making it unsafe. :)
06/22/2011 11:39 Korvacs#5
Quote:
Originally Posted by Y u k i View Post
I think, this class looks failsafe to me, it will speed up the de/encrypting a lot
I actually think this is too safe, the encryption shouldnt be checking lengths or to see if things are null or not, if your socket system receives a packet with length 0 then you shouldnt be attempting to handle that at all, let alone decrypting it.

And at no point should you ever be attempting to encrypt something with length 0.

Even the safe crypt i wrote for the auth doesnt perform these kinds of checks because they are entirely unnecessary.

So this:

Code:
        public void Encrypt(ref Byte[] Buf)
        {
            Int32 Length = Buf.Length;
            fixed (Byte* pBuf = Buf)
            {
                if (pBuf == null)
                    throw new NullReferenceException("Buffer can't be null!");

                if (Length <= 0)
                    return;

                Int16 K = COSAC_IV / 2;
                for (Int32 i = 0; i < Length; i++)
                {
                    pBuf[i] ^= (Byte)0xAB;
                    pBuf[i] = (Byte)(pBuf[i] >> 4 | pBuf[i] << 4);
                    if (m_BufIV != null)
                    {
                        pBuf[i] ^= (Byte)(m_BufIV[(Byte)(EncryptCounter & 0xFF) + 0]);
                        pBuf[i] ^= (Byte)(m_BufIV[(Byte)(EncryptCounter >> 8) + K]);
                    }
                    EncryptCounter++;
                }
            }
        }
Can actually be just this:

Code:
        public void Encrypt(ref byte[] Data)
        {
            for (int i = 0; i < Data.Length; i++)
            {
                Data[i] ^= 0xab; Data[i] = (byte)(Data[i] >> 4 | Data[i] << 4);
                Data[i] ^= (byte)(_cryptKey1[_encryptCounter.Key1] ^ _cryptKey2[_encryptCounter.Key2]);
                _encryptCounter.Increment();
            }
        }
See what i mean?
06/22/2011 11:48 JobvdH#6
rofl, thats way easier and faster then the old code xD
06/22/2011 16:07 CptSky#7
Quote:
Originally Posted by Korvacs View Post
I actually think this is too safe, the encryption shouldnt be checking lengths or to see if things are null or not, if your socket system receives a packet with length 0 then you shouldnt be attempting to handle that at all, let alone decrypting it.

And at no point should you ever be attempting to encrypt something with length 0.

Even the safe crypt i wrote for the auth doesnt perform these kinds of checks because they are entirely unnecessary.[...]
Yeah, I know... All the check are for me useless. I will always generate the IV before generating the Key. I will always encrypt or decrypt a valid buffer... Anyway, I tried to create a safe class for stupid people, while using unsafe code (pointer). You can remove these checks if you want to upgrade the speed, but with or without these checks, it will be faster than the old crypto (reference to the LOTF one...), and a bit faster than the unknownone crypto (old one I was using before creating mine). The GenerateKey method is way more optimised.

This class is maybe "too safe", but it's probably idiot proof :rolleyes:
11/16/2011 06:23 12k#8
sorry to revive a decently old topic, but does anyone happen to have a base with this used in it. I have generated the IV and then generated the key and cant seem to get it to decrypt properly. Cant get it to decrypt the auth properly prior to that either. Am i missing something ^_-
11/16/2011 16:37 CptSky#9
Quote:
Originally Posted by 12k View Post
sorry to revive a decently old topic, but does anyone happen to have a base with this used in it. I have generated the IV and then generated the key and cant seem to get it to decrypt properly. Cant get it to decrypt the auth properly prior to that either. Am i missing something ^_-
On AuthServer, you only need to generate the IV with the two values that are in the comment (P/G).

AuthServer : OnConnection
-> new COSAC()
-> GenerateIV(P, G)
-> Decrypt / Encrypt

MsgServer : OnConnection
-> new COSAC()
-> GenerateIV(P, G)
-> Decrypt first packet
-> GenerateKey(Token, AccountUID)
-> Decrypt/Encrypt