Reworked CO2 Cipher

01/30/2011 20:08 CptSky#1
I made a new version of the CO2 cipher used by the auth server and the game server (5017 and before). I don't use any pointers in this version. The class is based on the dotNet AsymmetricAlgorithm class.

If you use this class in your projects, please mention who is the autor... Thanks.

Code:
// * ***************************************
// *       _                 _ _
// *      | |               (_) |
// *      | |     ___   __ _ _| | __
// *      | |    / _ \ / _` | | |/ /
// *      | |___| (_) | (_| | |   < _
// *      \_____/\___/ \__, |_|_|\_(_)
// *                    __/ |
// *                   |___/
// *
// *       Copyright (C) 2010 - 2011
// *
// * ***************************************
// *              CREDITS
// * ***************************************
// * Originally created by CptSky @ e*pvp, Copyright (C) 2010-2011,
// * Logik, All rights reserved.
// *  
// * ***************************************
// *              SPECIAL THANKS
// * ***************************************
// * Sparkie (Unknownone @ e*pvp)
// * 
// * ***************************************

using System;
using System.Security.Cryptography;

namespace Logik.Security.Cryptography
{
    /// <summary>
    /// Conquer Online Server Asymmetric Cipher
    /// </summary>
    public class COSAC : AsymmetricAlgorithm
    {
        const Int32 COSAC_KEY_SIZE = 4096;

        protected Byte[] IVValue;
        protected Byte[] KeyValue;
        protected UInt16 EncryptCounter;
        protected UInt16 DecryptCounter;

        protected String KeyExchangeAlgorithmValue;
        protected String SignatureAlgorithmValue;

        /// <summary>
        /// Create a new COSAC instance with a key size of 4096.
        /// </summary>
        public COSAC()
            : base()
        {
            this.KeyExchangeAlgorithmValue = "TQ_DIGITAL_CONQUER_ONLINE_KEY_EXCHANGE_ALGORITHM";
            this.SignatureAlgorithmValue = "TQ_DIGITAL_CONQUER_ONLINE_SERVER_ASYMMETRIC_CIPHER";

            this.LegalKeySizesValue = new KeySizes[] { new KeySizes(COSAC_KEY_SIZE, COSAC_KEY_SIZE, 0) };
            this.KeySizeValue = COSAC_KEY_SIZE;
        }

        ~COSAC()
        {
            KeyValue = null;
            IVValue = null;

            KeyExchangeAlgorithmValue = null;
            SignatureAlgorithmValue = null;
        }

        /// <summary>
        /// Releases the unmanaged resources used by the AsymmetricAlgorithm class and optionally releases the managed resources.
        /// </summary>
        protected override void Dispose(Boolean disposing)
        {
            Clear();
            if (disposing)
            {
                KeyValue = null;
                IVValue = null;

                KeyExchangeAlgorithmValue = null;
                SignatureAlgorithmValue = null;
            }
        }

        /// <summary>
        /// Reconstructs an AsymmetricAlgorithm object from an XML string. (Not Implemented!)
        /// </summary>
        public override void FromXmlString(String xmlString) { throw new NotImplementedException(); }

        /// <summary>
        /// Creates and returns an XML string representation of the current AsymmetricAlgorithm object. (Not Implemented!)
        /// </summary>
        public override String ToXmlString(Boolean includePrivateParameters) { throw new NotImplementedException(); }

        /// <summary>
        /// Gets the name of the key exchange algorithm.
        /// </summary>
        public override String KeyExchangeAlgorithm { get { return KeyExchangeAlgorithmValue; } }

        /// <summary>
        /// Gets the size, in bits, of the key modulus used by the asymmetric algorithm.
        /// </summary>
        public override Int32 KeySize { get { return base.KeySize; } }

        /// <summary>
        /// Gets the key sizes that are supported by the asymmetric algorithm.
        /// </summary>
        public override KeySizes[] LegalKeySizes { get { return base.LegalKeySizes; } }

        /// <summary>
        /// Gets the name of the signature algorithm.
        /// </summary>
        public override String SignatureAlgorithm { get { return SignatureAlgorithmValue; } }

        /// <summary>
        /// Generates an initialization vector (IV) to use for the algorithm.
        /// CO2(P: 0x13FA0F9D, G: 0x6D5C7962)
        /// </summary>
        public void GenerateIV(Int32 P, Int32 G)
        {
            IVValue = new Byte[COSAC_KEY_SIZE / 8];
            Int16 K = COSAC_KEY_SIZE / 16;

            Byte[] PArray = new Byte[4] { (Byte)(P), (Byte)(P >> 8), (Byte)(P >> 16), (Byte)(P >> 24) };
            Byte[] GArray = new Byte[4] { (Byte)(G), (Byte)(G >> 8), (Byte)(G >> 16), (Byte)(G >> 24) };
            for (Int16 i = 0; i < K; i++)
            {
                IVValue[i + 0] = PArray[0];
                IVValue[i + K] = GArray[0];
                PArray[0] = (Byte)((PArray[1] + (Byte)(PArray[0] * PArray[2])) * PArray[0] + PArray[3]);
                GArray[0] = (Byte)((GArray[1] - (Byte)(GArray[0] * GArray[2])) * GArray[0] + GArray[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 (IVValue == null)
                throw new NullReferenceException("IV needs to be generated before generating the key!");

            KeyValue = new Byte[COSAC_KEY_SIZE / 8];
            Int16 K = COSAC_KEY_SIZE / 16;

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

            Byte[] tmpKey1 = new Byte[4] { (Byte)(tmp >> 24), (Byte)(tmp >> 16), (Byte)(tmp >> 8), (Byte)(tmp) };
            tmpKey1[2] ^= 0x43;
            tmpKey1[3] ^= 0x21;

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

            tmp = (UInt32)((tmpKey1[0] << 24) + (tmpKey1[1] << 16) + (tmpKey1[2] << 8) + tmpKey1[3]);
            tmp *= tmp;

            Byte[] tmpKey2 = new Byte[4] { (Byte)(tmp >> 24), (Byte)(tmp >> 16), (Byte)(tmp >> 8), (Byte)(tmp) };

            for (Int16 i = 0; i < K; i++)
            {
                KeyValue[i + 0] = (Byte)(IVValue[i + 0] ^ tmpKey1[3 - (i % 4)]);
                KeyValue[i + K] = (Byte)(IVValue[i + K] ^ tmpKey2[3 - (i % 4)]);
            }
            EncryptCounter = 0;
        }

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

            Int16 K = COSAC_KEY_SIZE / 16;
            for (Int32 i = 0; i < rgb.Length; i++)
            {
                rgb[i] ^= (Byte)0xAB;
                rgb[i] = (Byte)(rgb[i] >> 4 | rgb[i] << 4);
                if (KeyValue != null)
                {
                    rgb[i] ^= (Byte)(KeyValue[(Byte)(DecryptCounter & 0xFF) + 0]);
                    rgb[i] ^= (Byte)(KeyValue[(Byte)(DecryptCounter >> 8) + K]);
                }
                else if (IVValue != null)
                {
                    rgb[i] ^= (Byte)(IVValue[(Byte)(DecryptCounter & 0xFF) + 0]);
                    rgb[i] ^= (Byte)(IVValue[(Byte)(DecryptCounter >> 8) + K]);
                }
                DecryptCounter++;
            }
        }

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

            Int16 K = COSAC_KEY_SIZE / 16;
            for (Int32 i = 0; i < rgb.Length; i++)
            {
                rgb[i] ^= (Byte)0xAB;
                rgb[i] = (Byte)(rgb[i] >> 4 | rgb[i] << 4);
                if (IVValue != null)
                {
                    rgb[i] ^= (Byte)(IVValue[(Byte)(EncryptCounter & 0xFF) + 0]);
                    rgb[i] ^= (Byte)(IVValue[(Byte)(EncryptCounter >> 8) + K]);
                }
                EncryptCounter++;
            }
        }

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