using System;
//using System.Xml;
using System.Text;
//using System.IO;
using System.Security.Cryptography;
using SmartCard.Runtime;

namespace MyCompany.Server
{

    public class Service : MarshalByRefObject
    {
        static int RSAsize = 1024;

        #region ProtocolParameters
        static byte[] g1;
        static byte[] g2;
        static byte[] g3;
        static byte[] n;
        static byte[] w;
        static byte[] wi;

        static byte[] r1=new byte[128];
        static byte[] r1i = new byte[128];
        static byte[] r2 = new byte[128];
        static byte[] r2i = new byte[128];

        static byte[] c11;
        static byte[] c3;
        static byte[] c22;
        static byte[] c33;

        static byte[] z1;
        static byte[] z2;
        static byte[] z1i;
        static byte[] z2i;
        #endregion

        private RSACryptoServiceProvider RSAd = new RSACryptoServiceProvider(RSAsize);
        //private static RSACryptoServiceProvider TestRSA = new RSACryptoServiceProvider(2048);
        private static RNGCryptoServiceProvider generator = new RNGCryptoServiceProvider();
        public static RSAParameters RSAparam = new RSAParameters();

        // DES kodovani
        public static DES DESMain = DES.Create();
        public static byte[] DESInputVector = DESMain.IV;
        public static byte[] DESKey = DESMain.Key;
        public static ICryptoTransform DesEncryptor = DESMain.CreateEncryptor();
        public static ICryptoTransform DesDecryptor = DESMain.CreateDecryptor();

        // AES kodovani
        public static Rijndael AESMain = Rijndael.Create();
        public static byte[] AESInputVector = AESMain.IV;
        public static byte[] AESKey = AESMain.Key;
        public static ICryptoTransform AesEncryptor = AESMain.CreateEncryptor();
        public static ICryptoTransform AesDecryptor = AESMain.CreateDecryptor();
        public static int Size;
        //Import parametrů RSA
        //public void RSA_import(byte[] e, byte[] modulus)
        //{
        //    RSAparam = RSAd.ExportParameters(true);
        //    RSAparam.Exponent = e;
        //    RSAparam.Modulus = modulus;
        //    RSAd.ImportParameters(RSAparam);
        //}

        public byte[] DES_NovyIV()
        {
            DESMain.GenerateIV(); 
            DESInputVector = DESMain.IV;
            return DESMain.IV;
        }
        public byte[] DES_NovyKlic(int KeySize)
        {
            DESMain.KeySize = KeySize;
            DESMain.GenerateKey();
            DESKey = DESMain.Key;
            return DESMain.Key;
        }
        public byte[] AES_NovyIV()
        {
            AESMain.GenerateIV();
            AESInputVector = AESMain.IV;
            return AESMain.IV;
        }
        public byte[] AES_NovyKlic(int KeySize)
        {
            AESMain.KeySize = KeySize;
            AESMain.GenerateKey();
            AESKey = AESMain.Key;
            return AESMain.Key;
        }

        public byte[] Enc_DES(byte[] baze, byte[] exponent, byte[] modulus)
        {
            //DESMain.BlockSize = 11;
            if (DESInputVector.Length == 0) { DESMain.GenerateIV(); DESInputVector = DESMain.IV; }
            DESMain.IV = DESInputVector;
            DESMain.Key = DESKey; //new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; 
            //DESMain.Mode = CipherMode.ECB;
            byte[] x = DesEncryptor.TransformFinalBlock(baze, 0, baze.Length);
            Size = x.Length;
            return x;
        }
        public byte[] Dec_DES(byte[] baze, byte[] exponent, byte[] modulus)
        {
            //DESMain.BlockSize = 11;
            //DESMain.IV = exponent;
            if (DESInputVector.Length == 0) { DESMain.GenerateIV(); DESInputVector = DESMain.IV; }
            DESMain.Key = DESKey;//new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
            DESMain.IV = DESInputVector;
            //DESMain.Mode = CipherMode.ECB;
            byte[] x = DesDecryptor.TransformFinalBlock(baze, 0, baze.Length);
            Size = x.Length;
            return x;
        }
        public byte[] Enc_AES(byte[] baze, byte[] exponent, byte[] modulus)
        {
            //DESMain.BlockSize = 11;
            //DESMain.IV = exponent;
            if (AESInputVector.Length == 0) { AESMain.GenerateIV(); AESInputVector = AESMain.IV; }
            AESMain.Key = AESKey;//Encoding.UTF8.GetBytes("12345678901234567890123456789012");
            AESMain.IV = AESInputVector;
            //AESMain.Key = Encoding.UTF8.GetBytes("12345678901234567890123456789012"); //new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
            //DESMain.Mode = CipherMode.ECB;
            byte[] x = AesEncryptor.TransformFinalBlock(baze, 0, baze.Length);
            Size = x.Length;
            return x;
        }
        public byte[] Dec_AES(byte[] baze, byte[] exponent, byte[] modulus)
        {
            //DESMain.BlockSize = 11;
            //DESMain.IV = exponent;
            if (AESInputVector.Length == 0) { AESMain.GenerateIV(); AESInputVector = AESMain.IV; }
            AESMain.Key = AESKey;//Encoding.UTF8.GetBytes("12345678901234567890123456789012");//new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
            AESMain.IV = AESInputVector;
            //DESMain.Mode = CipherMode.ECB;
            byte[] x = AesDecryptor.TransformFinalBlock(baze, 0, baze.Length);
            Size = x.Length;
            return x;
        }
        //public static SmartCard.ContentManager MMM = from SmartCard.ContentManager[];

        public int getMemory()
        {
            //SmartCard.ContentManager Manager = new SmartCard.ContentManager();
            //return Manager.FreePersistentMemory;
            //System.Diagnostics.Process currentProcess = System.Diagnostics.Process.GetCurrentProcess();
            //return currentProcess.WorkingSet64;

            return Size;
            //return System.Diagnostics.Process.GetCurrentProcess().PeakWorkingSet64;
            
        }
        
        //Mocneni
        public byte[] Power(byte[] baze, byte[] exponent, byte[] modulus)
        {
            //RSAparam = RSAd.ExportParameters(false);
            //RSAsize = modulus.Length * 8;
            
            RSAparam.Exponent = exponent;
            RSAparam.Modulus = modulus;
            RSAd.ImportParameters(RSAparam);
            RSAparam = RSAd.ExportParameters(false);
            return RSAd.EncryptValue(baze);
            
        }

        public byte[] DePower(byte[] baze, byte[] exponent, byte[] modulus)
        {
            //RSAparam = RSAd.ExportParameters(false);
            //RSAsize = modulus.Length * 8;

            RSAparam.Exponent = exponent;
            RSAparam.Modulus = modulus;
            
            RSAd.ImportParameters(RSAparam);
            return RSAd.DecryptValue(baze);

        }
        
        public void SetRSAsize(int size)
        {
            RSAsize = size;
        }

        //public byte[] Test(byte[] input, byte[] exponent, byte[] modulus)
        //{

        //    RSAparam = TestRSA.ExportParameters(false);
        //    RSAparam.Exponent = exponent;
        //    RSAparam.Modulus = modulus;
        //    TestRSA.ImportParameters(RSAparam);
        //    return TestRSA.EncryptValue(input);
        //    //return TestRSA.ExportParameters(false);
        //}

        //public byte[] Test1(byte[]input)
        //{

        //    return TestRSA.Decrypt(input, false);
        //}

        //Inverze RSA, nepouzivane
        


        //Export RSA parametru
        //public RSAParameters RSA_export()
        //{
        //    return RSAd.ExportParameters(true);
        //}

        //Nasobeni
        public byte[] Multiply(byte[] a_byte, byte[] b_byte, byte[] mod_byte)
        {
            //RSAsize = mod_byte.Length * 8;
            byte[] aMULTb = new byte[RSAsize / 8];
            byte[] two = new byte[1];
            two[0] = 2;

            aMULTb = ArrSubSimple(ArrSubSimple(Power(ArrAddSimple(a_byte, b_byte, mod_byte), two, mod_byte), Power(a_byte, two, mod_byte), mod_byte), Power(b_byte, two, mod_byte), mod_byte);

            return ArrHalfSimple(aMULTb, mod_byte);
        }

        //Ulozeni parametru
      /*  public void SetParameters(string variable, string name, bool first, bool last)
        {
            //Prida dalsi parametry do souboru
            FileStream file = new FileStream("C:/Data/TokenList.xml", FileMode.Append, FileAccess.Write);
            StreamWriter sw = new StreamWriter(file);
            string parameters = "";

            //Hlavicka otevreni
            if (first == true)
            {
                parameters += "<token id=\"" + "TokenID" + "\">\n";
            }

            //Parametry
            parameters += "\t<" + name + ">" + variable + "</" + name + ">\n";

            //Hlavicka uzavreni
            if (last == true)
            {
                parameters += "</token>\n";
            }

            //Zapsaní všech tokenù na kartu
            sw.Write(parameters);

            sw.Close();
            file.Close();
        }
        

        //Vypocet prvniho kroku
        public void FirstStep()
        {
            r1 = RandomGen(r1,80,48);
            r1i = RandomGen(r1i, 60, 68);
            r2 = RandomGen(r2, 53, 75);
            r2i = RandomGen(r2i, 32, 96);
            RSAsize = 1024;
            StreamReader sReader = new StreamReader("C:/Data/TokenList.xml");
            XmlTextReader xr = new XmlTextReader(sReader);

            while (xr.Read())
            {
                if (xr.Name == "token" && xr.HasAttributes == true)
                {

                    xr.Read();
                    g1 = Convert.FromBase64String(xr.ReadElementString("g1"));
                    g2 = Convert.FromBase64String(xr.ReadElementString("g2"));
                    g3 = Convert.FromBase64String(xr.ReadElementString("g3"));
                    n = Convert.FromBase64String(xr.ReadElementString("n"));
                    w = Convert.FromBase64String(xr.ReadElementString("w"));
                    wi = Convert.FromBase64String(xr.ReadElementString("wi"));

                    break;

                }
            }

            c11 = Multiply(Power(g1, r1, n), Power(g2, r1i, n), n);
            c3 = Power(g3, r1, n);
            c22 = Multiply(Power(g1, r2, n), Power(g2, r2i, n), n);
            c33 = Power(g3, r2, n);

        }
        
        //Vypocet odpovedi na vyzvu
        public void LastStep(byte[] e1)
        {
            RSAsize = 1024;
            z1 = ArrSubSimple(r1, Multiply(e1, w, n), n);
            z1i = ArrSubSimple(r1i, Multiply(e1, wi, n), n);
            z2 = ArrSubSimple(r2, Multiply(e1, r1, n), n);
            z2i = ArrSubSimple(r2i, Multiply(e1, r1i, n), n);
        }
        */
        //Zkouska nahodneho generatoru
        public byte[] RandomGen(byte[] input, int ofset, int length)
        {
            //generator.GetBytes(input, ofset, length);
            generator.GetBytes(input);
            return input;
        }

        //Nějaké gettery na zkoušku, potřeba udělat lépe

        #region Getters
        public byte[] Getc11()
        {
            return c11;
        }

        public byte[] Getc22()
        {
            return c22;
        }

        public byte[] Getc33()
        {
            return c33;
        }
        public byte[] Getz1i()
        {
            return z1i;
        }
        public byte[] Getz2()
        {
            return z2;
        }
        public byte[] Getz2i()
        {
            return z2i;
        }
        public byte[] Getc3()
        {
            return c3;
        }
        public byte[] Getz1()
        {
            return z1;
        }
        #endregion

        #region Lukas, Schoolbook(opt), Comba(opt), Montgomery+podpora

        public byte[] ArrMultiObecneOpt2(byte[] a, byte[] b)
        {


            int x_len = a.Length;
            int y_len = b.Length;

            byte[] result = new byte[a.Length + b.Length];

            for (int i = a.Length - 1; i >= 0; i--)
            {
                int mcarry = 0;
                for (int j = b.Length - 1; j >= 0; j--)
                {
                    int val = (a[i] * b[j]) +
                                     result[i + j + 1] + mcarry;

                    result[i + j + 1] = (byte)(val & 0xFF);
                    mcarry = (val >> 8);
                }

                if (mcarry != 0)
                    result[i + y_len] = (byte)mcarry;
            }
            return result;

        }

        public byte[] CombaOpt(byte[] a, byte[] b, int size)
        {
            int ucarry;
            int utv = 0;
            byte[] result = new byte[size + size];

            for (int i = 0; i < size; i++)
            {
                for (int j = 0; j <= i; j++)
                {
                    utv = (a[j] * b[i - j]) + utv;
                }
                result[i] = (byte)(utv & 0xFF);
                ucarry = (utv >> 8);
                utv = ucarry;
            }
            for (int i = size; i < size + size; i++)
            {
                for (int j = i - size + 1; j <= size - 1; j++)
                {
                    utv = (a[j] * b[i - j]) + utv;
                }
                result[i] = (byte)(utv & 0xFF);
                ucarry = (utv >> 8);
                utv = ucarry;
            }

            return result;
        }

        public byte[] ArrMultiMontgomeryREV(byte[] x, byte[] y, byte[] m, int N, int mQ)
        {
            int LONG_MASK = 0xFF;
            int BITS = 8;
            byte[] a = new byte[N + 1];
            for (int i = 0; i < N; i++)
            {
                int u = ((a[0] + (((x[i] & LONG_MASK) * (y[0] & LONG_MASK)) & LONG_MASK)) * mQ) & LONG_MASK;
                int xy = (x[i] & LONG_MASK) * (y[0] & LONG_MASK);
                int um = u * (m[0] & LONG_MASK);
                int temp = (a[0] & LONG_MASK) + (xy & LONG_MASK) + (um & LONG_MASK);
                int carry = (xy >> BITS) + (um >> BITS) + (temp >> BITS);
                for (int pos = 1; pos < N; pos++)
                {
                    xy = (x[i] & LONG_MASK) * (y[pos] & LONG_MASK);
                    um = u * (m[pos] & LONG_MASK);
                    temp = (a[pos] & LONG_MASK) + (xy & LONG_MASK) + (um & LONG_MASK) + (carry & LONG_MASK);
                    carry = (carry >> BITS) + (xy >> BITS) + (um >> BITS) + (temp >> BITS);
                    a[pos - 1] = (byte)temp;
                }
                carry += (a[N] & LONG_MASK);
                a[N - 1] = (byte)carry;
                a[N] = (byte)(carry >> BITS);
            }
            if (UrciVetsiInt(a, m) == true)
            {
                ArrSubSimple(a, m, m);
            }
            return a;
        }


        public bool UrciVetsiInt(byte[] a, byte[] b)
        {
            byte[] aReduced;
            if (a[0] == 0)
            {
                aReduced = RedukceByteArr(a);
            }
            else
            {
                aReduced = new byte[a.Length];
                aReduced = a;
            }
            byte[] bReduced;
            if (b[0] == 0)
            {
                bReduced = RedukceByteArr(b);
            }
            else
            {
                bReduced = new byte[b.Length];
                bReduced = b;
            }
            int delkaA = aReduced.Length;
            int delkaB = bReduced.Length;
            if (delkaA > delkaB)
            {
                return true;
            }
            else if (delkaB > delkaA)
            {
                return false;
            }
            else
            {
                for (int i = 0; i < delkaA; i++)
                {
                    if (aReduced[i] > bReduced[i])
                    {
                        return true;
                    }
                    else if (aReduced[i] < bReduced[i])
                    {
                        return false;
                    }
                    if ((delkaA - 1) == i)
                    {
                        return true;
                    }
                }
                return true;
            }
        }

        //Metoda pro odstranění nul ze začátku pole
        public byte[] RedukceByteArr(byte[] a)
        {
            int pocetNul = 0;
            byte[] aReduced;
            int difference = 0;
            if (a[0] == 0)
            {
                for (int i = 0; i < a.Length - 1; i++)
                {
                    if ((i == 0 && a[0] == 0) || (a[0] == 0 && a[i] == 0))
                    {
                        pocetNul++;
                    }
                    else
                    {
                        break;
                    }
                }
                aReduced = new byte[a.Length - pocetNul];
                difference = a.Length - aReduced.Length;
                for (int i = aReduced.Length - 1; i >= 0; i--)
                {
                    aReduced[i] = a[i + difference];
                }
            }
            else
            {
                aReduced = new byte[a.Length];
                aReduced = a;
            }
            return aReduced;
        }
        #endregion

        #region scitani, odcitani, deleni - na co je AddEqual?

        public byte[] ArrAddEqual(byte[] a, byte[] b)
        {

            int size = a.Length;

            // byte[] res = new byte[RSAsize / 8];
            byte[] res = new byte[size];

            int[] carry = new int[2];

            for (int i = size - 1; i >= 0; i--)
            {
                carry[(i + 1) % 2] = ((a[i] + b[i] + carry[i % 2]) > 255) ? 0x1 : 0x0;
                res[i] = (byte)((a[i] + b[i] + carry[i % 2]) % 256);
            }

            return res;
        }

        public byte[] ArrAddSimple(byte[] a, byte[] b, byte[] modulus)
        {
            byte[] res = new byte[RSAsize / 8];
            int[] carry = new int[2];

            for (int i = RSAsize / 8 - 1; i >= 0; i--)
            {
                carry[(i + 1) % 2] = ((a[i] + b[i] + carry[i % 2]) > 255) ? 0x1 : 0x0;
                res[i] = (byte)((a[i] + b[i] + carry[i % 2]) % 256);
            }

            if (carry[1] == 1)
            {
                a = ArrSubSimple(a, modulus, modulus);
                res = ArrAddSimple(b, a, modulus);
            }

            return res;
        }

        public byte[] ArrSubSimple(byte[] a, byte[] b, byte[] modulus)
        {
            byte[] res = new byte[RSAsize / 8];
            int[] carry = new int[2];


            for (int j = 0; j < RSAsize / 8; j++)
            {
                if (a[j] > b[j])
                {
                    for (int i = RSAsize / 8 - 1; i >= 0; i--)
                    {
                        carry[(i + 1) % 2] = ((a[i] - b[i] - carry[i % 2]) < 0) ? 0x1 : 0x0;
                        res[i] = (byte)((a[i] - b[i] - carry[i % 2] + 256 * carry[(i + 1) % 2]) % 256);
                    }
                    return res;
                }

                if (a[j] < b[j])
                {
                    for (int i = RSAsize / 8 - 1; i >= 0; i--)
                    {
                        carry[(i + 1) % 2] = ((modulus[i] + a[i] + carry[i % 2]) > 255) ? 0x1 : 0x0;
                        res[i] = (byte)((modulus[i] + a[i] + carry[i % 2]) % 256);
                    }

                    for (int i = RSAsize / 8 - 1; i >= 0; i--)
                    {
                        carry[(i + 1) % 2] = ((res[i] - b[i] - carry[i % 2]) < 0) ? 0x1 : 0x0;
                        res[i] = (byte)((res[i] - b[i] - carry[i % 2] + 256 * carry[(i + 1) % 2]) % 256);
                    }

                    return res;
                }

            }

            for (int i = RSAsize / 8 - 1; i >= 0; i--)
            {
                carry[(i + 1) % 2] = ((a[i] - b[i] - carry[i % 2]) < 0) ? 0x1 : 0x0;
                res[i] = (byte)((a[i] - b[i] - carry[i % 2] + 256 * carry[(i + 1) % 2]) % 256);
            }

            return res;

        }

        public byte[] ArrHalfSimple(byte[] input, byte[] modulus)
        {
            //Fix pro velke hodnoty
            if (input[RSAsize / 8 - 1] % 2 == 1)
            {
                byte[] res = new byte[RSAsize / 8 + 1];
                int[] carry = new int[2];

                for (int i = RSAsize / 8 - 1; i >= 0; i--)
                {
                    carry[(i + 1) % 2] = ((input[i] + modulus[i] + carry[i % 2]) > 255) ? 0x1 : 0x0;
                    res[i + 1] = (byte)((input[i] + modulus[i] + carry[i % 2]) % 256);
                }

                res[0] = (byte)carry[1];

                int[] carry2 = new int[2];

                for (int j = 0; j < RSAsize / 8 + 1; j++)
                {
                    carry2[(j + 1) % 2] = (res[j] % 2) * 128;
                    res[j] = (byte)((res[j] >> 1) + carry2[j % 2]);
                };

                for (int j = 0; j < RSAsize / 8; j++)
                {
                    input[j] = res[j + 1];
                };

                return input;

            }

            //if (input[RSAsize / 8 - 1] % 2 == 1)
            //{
            //    input = ArrAddSimple(input, modulus, modulus);
            //}

            int[] carry3 = new int[2];
            for (int j = 0; j < RSAsize / 8; j++)
            {
                carry3[(j + 1) % 2] = (input[j] % 2) * 128;
                input[j] = (byte)((input[j] >> 1) + carry3[j % 2]);
            };

            return input;
        }
        
        #endregion

    }
}

