/********EXTERNAL OBJECT PUBLIC METHODS  - END ********/


        /// <summary>
        /// Gets the BufferedBlockCipher loaded with Padding, Mode and Engine to Encrypt with a Symmetric Block Algorithm
        /// </summary>
        /// <param name="algorithm">string SymmetricBlockAlgorithm enum, symmetric block algorithm name</param>
        /// <param name="mode">string SymmetricBlockModes enum, symmetric block mode name</param>
        /// <param name="padding">string SymmetricBlockPadding enum, symmetric block padding name</param>
        /// <returns>BufferedBlockCipher loaded with Padding, Mode and Engine to Encrypt with a Symmetric Block Algorithm</returns>
        private BufferedBlockCipher getCipher(SymmetricBlockAlgorithm algorithm, SymmetricBlockMode mode,
                                              SymmetricBlockPadding padding)
        {
            IBlockCipher        engine        = getCipherEngine(algorithm);
            IBlockCipherPadding paddingCipher = getPadding(padding);
            IBlockCipher        bc;

            if (mode != SymmetricBlockMode.ECB)
            {
                bc = getCipherMode(engine, mode);
            }
            else
            {
                bc = engine;
            }
            // si el padding es WITHCTS el paddingCipher es null
            if (usesCTS(mode, padding))
            {
                return(new CtsBlockCipher(bc)); // no usa el paddingCipher que es el null
            }
            if (padding == SymmetricBlockPadding.NOPADDING)
            {
                return(new BufferedBlockCipher(bc));
            }
            else
            {
                return(new PaddedBufferedBlockCipher(bc, paddingCipher));
            }
        }
        /// <summary>
        /// Buils an AEADBlockCipher engine
        /// </summary>
        /// <param name="blockCipher">BlockCipher engine</param>
        /// <param name="mode">SymmetricBlockModes enum, symmetric block mode name</param>
        /// <returns>AEADBlockCipher loaded with a given BlockCipher</returns>
        private IAeadBlockCipher getAEADCipherMode(IBlockCipher blockCipher, SymmetricBlockMode mode)
        {
            IAeadBlockCipher bc = null;

            switch (mode)
            {
            case SymmetricBlockMode.AEAD_CCM:
                bc = new CcmBlockCipher(blockCipher);
                break;

            case SymmetricBlockMode.AEAD_EAX:
                bc = new EaxBlockCipher(blockCipher);
                break;

            case SymmetricBlockMode.AEAD_GCM:
                bc = new GcmBlockCipher(blockCipher);
                break;

            case SymmetricBlockMode.AEAD_KCCM:
                bc = new KCcmBlockCipher(blockCipher);
                break;

            default:
                this.error.setError("SB017", "AEADCipher " + mode + " not recognised.");
                break;
            }
            return(bc);
        }
        /// <summary>
        /// Decrypts the given encrypted text with an AEAD encryption algorithm
        /// </summary>
        /// <param name="symmetricBlockAlgorithm">string SymmetricBlockAlgorithm enum, symmetric block algorithm name</param>
        /// <param name="symmetricBlockMode">string SymmetricBlockModes enum, symmetric block mode name</param>
        /// <param name="key">string Hexa key for the algorithm excecution</param>
        /// <param name="macSize">int macSize in bits for MAC length for AEAD Encryption algorithm</param>
        /// <param name="nonce">string Hexa nonce for MAC length for AEAD Encryption algorithm</param>
        /// <param name="encryptedInput">string Base64 text to decrypt</param>
        /// <returns></returns>
        public string DoAEADDecrypt(string symmetricBlockAlgorithm, string symmetricBlockMode,
                                    string key, int macSize, string nonce, string encryptedInput)
        {
            this.error.cleanError();
            SymmetricBlockAlgorithm algorithm = SymmetricBlockAlgorithmUtils.getSymmetricBlockAlgorithm(symmetricBlockAlgorithm, this.error);
            SymmetricBlockMode      mode      = SymmetricBlockModeUtils.getSymmetricBlockMode(symmetricBlockMode, this.error);

            if (this.error.existsError())
            {
                return("");
            }

            IBlockCipher     engine = getCipherEngine(algorithm);
            IAeadBlockCipher bbc    = getAEADCipherMode(engine, mode);

            if (this.error.existsError() && !(string.Compare(this.error.Code, "SB016", true) == 0))
            {
                return("");
            }
            byte[] nonceBytes = SecurityUtils.GetHexa(nonce, "SB025", this.error);
            byte[] keyBytes   = SecurityUtils.GetHexa(key, "SB025", this.error);
            if (this.HasError())
            {
                return("");
            }
            KeyParameter keyParam = new KeyParameter(keyBytes);

            AeadParameters AEADparams = new AeadParameters(keyParam, macSize, nonceBytes);

            try
            {
                bbc.Init(false, AEADparams);
            }catch (Exception e)
            {
                this.error.setError("SB030", e.Message);
                return("");
            }
            byte[] out2            = Base64.Decode(encryptedInput);
            byte[] comparisonBytes = new byte[bbc.GetOutputSize(out2.Length)];
            int    length          = bbc.ProcessBytes(out2, 0, out2.Length, comparisonBytes, 0);

            try
            {
                bbc.DoFinal(comparisonBytes, length);
            }
            catch (Exception)
            {
                this.error.setError("SB012", "AEAD decryption exception");
                return("");
            }
            this.error.cleanError();
            // return System.Text.Encoding.UTF8.GetString(comparisonBytes).Trim();
            EncodingUtil eu = new EncodingUtil();

            this.error = eu.GetError();

            return(eu.getString(comparisonBytes));
        }
        /// <summary>
        /// Mapping between SymmetricBlockMode enum representation and string name
        /// </summary>
        /// <param name="symmetricBlockMode">SymmetricBlockMode enum, mode name</param>
        /// <param name="error">Error type for error management</param>
        /// <returns>SymmetricBlockMode name value in string</returns>
        public static string valueOf(SymmetricBlockMode symmetricBlockMode, Error error)
        {
            switch (symmetricBlockMode)
            {
            case SymmetricBlockMode.ECB:
                return("ECB");

            case SymmetricBlockMode.CBC:
                return("CBC");

            case SymmetricBlockMode.CFB:
                return("CFB");

            case SymmetricBlockMode.CTS:
                return("CTS");

            case SymmetricBlockMode.GOFB:
                return("GOFB");

            case SymmetricBlockMode.OFB:
                return("OFB");

            case SymmetricBlockMode.OPENPGPCFB:
                return("OPENPGPCFB");

            case SymmetricBlockMode.SIC:
                return("SIC");

            case SymmetricBlockMode.CTR:
                return("CTR");

            /* AEAD */


            case SymmetricBlockMode.AEAD_EAX:
                return("AEAD_EAX");

            case SymmetricBlockMode.AEAD_GCM:
                return("AEAD_GCM");

            case SymmetricBlockMode.AEAD_KCCM:
                return("AEAD_KCCM");

            case SymmetricBlockMode.AEAD_CCM:
                return("AEAD_CCM");

            default:
                error.setError("SB006", "Unrecognized SymmetricBlockMode");
                return("Unrecognized operation mode");
            }
        }
        /// <summary>
        /// Buisl a BlockCipher with a mode
        /// </summary>
        /// <param name="blockCipher">BlockCipher loaded with the algorithm Engine</param>
        /// <param name="mode">SymmetricBlockModes enum, mode name</param>
        /// <returns>BlockCipher with mode loaded</returns>
        private IBlockCipher getCipherMode(IBlockCipher blockCipher, SymmetricBlockMode mode)
        {
            IBlockCipher bc = null;

            switch (mode)
            {
            case SymmetricBlockMode.ECB:
            case SymmetricBlockMode.NONE:
                bc = blockCipher;
                break;

            case SymmetricBlockMode.CBC:
                bc = new CbcBlockCipher(blockCipher);
                break;

            case SymmetricBlockMode.CFB:
                bc = new CfbBlockCipher(blockCipher, blockCipher.GetBlockSize());
                break;

            case SymmetricBlockMode.CTR:
                bc = new SicBlockCipher(blockCipher);
                break;

            case SymmetricBlockMode.CTS:
                bc = new CbcBlockCipher(blockCipher);
                break;

            case SymmetricBlockMode.GOFB:
                bc = new GOfbBlockCipher(blockCipher);
                break;

            case SymmetricBlockMode.OFB:
                bc = new OfbBlockCipher(blockCipher, blockCipher.GetBlockSize());
                break;

            case SymmetricBlockMode.OPENPGPCFB:
                bc = new OpenPgpCfbBlockCipher(blockCipher);
                break;

            case SymmetricBlockMode.SIC:
                if (blockCipher.GetBlockSize() < 16)
                {
                    this.error.setError("SB016",
                                        "Warning: SIC-Mode can become a twotime-pad if the blocksize of the cipher is too small. Use a cipher with a block size of at least 128 bits (e.g. AES)");
                }
                blockCipher = new SicBlockCipher(blockCipher);
                break;
            }
            return(bc);
        }
        /// <summary>
        /// Find if a given mode is AEAD type
        /// </summary>
        /// <param name="symmetricBlockMode">SymmetricBlockMode enum, mode name</param>
        /// <param name="error">Error type for error management</param>
        /// <returns>boolean true if operation mode is AEAD type</returns>
        public static bool isAEAD(SymmetricBlockMode symmetricBlockMode, Error error)
        {
            switch (symmetricBlockMode)
            {
            case SymmetricBlockMode.AEAD_EAX:
            case SymmetricBlockMode.AEAD_GCM:
            case SymmetricBlockMode.AEAD_KCCM:
            case SymmetricBlockMode.AEAD_CCM:
                return(true);

            default:
                error.setError("SB007", "Unrecognized Symmetric AEAD BlockMode");
                return(false);
            }
        }
        /********EXTERNAL OBJECT PUBLIC METHODS  - BEGIN ********/



        /// <summary>
        /// Encrypts the given text with an AEAD encryption algorithm
        /// </summary>
        /// <param name="symmetricBlockAlgorithm">string SymmetricBlockAlgorithm enum, symmetric block algorithm name</param>
        /// <param name="symmetricBlockMode">string SymmetricBlockModes enum, symmetric block mode name</param>
        /// <param name="key">string Hexa key for the algorithm excecution</param>
        /// <param name="macSize">int macSize in bits for MAC length for AEAD Encryption algorithm</param>
        /// <param name="nonce">string Hexa nonce for MAC length for AEAD Encryption algorithm</param>
        /// <param name="plainText"> string UTF-8 plain text to encrypt</param>
        /// <returns></returns>
        public string DoAEADEncrypt(string symmetricBlockAlgorithm, string symmetricBlockMode,
                                    string key, int macSize, string nonce, string plainText)
        {
            this.error.cleanError();
            SymmetricBlockAlgorithm algorithm = SymmetricBlockAlgorithmUtils.getSymmetricBlockAlgorithm(symmetricBlockAlgorithm, this.error);
            SymmetricBlockMode      mode      = SymmetricBlockModeUtils.getSymmetricBlockMode(symmetricBlockMode, this.error);

            if (this.error.existsError())
            {
                return("");
            }

            IBlockCipher     engine = getCipherEngine(algorithm);
            IAeadBlockCipher bbc    = getAEADCipherMode(engine, mode);

            if (this.error.existsError() && !(string.Compare(this.error.Code, "SB016", true) == 0))
            {
                return("");
            }
            byte[] nonceBytes = SecurityUtils.GetHexa(nonce, "SB024", this.error);
            byte[] keyBytes   = SecurityUtils.GetHexa(key, "SB024", this.error);
            if (this.HasError())
            {
                return("");
            }

            KeyParameter keyParam = new KeyParameter(keyBytes);

            AeadParameters AEADparams = new AeadParameters(keyParam, macSize, nonceBytes);

            try
            {
                bbc.Init(true, AEADparams);
            }catch (Exception e)
            {
                this.error.setError("SB029", e.Message);
                return("");
            }
            EncodingUtil eu = new EncodingUtil();

            byte[] inputBytes = eu.getBytes(plainText);
            if (eu.GetError().existsError())
            {
                this.error = eu.GetError();
                return("");
            }
            byte[] outputBytes = new byte[bbc.GetOutputSize(inputBytes.Length)];
            int    length      = bbc.ProcessBytes(inputBytes, 0, inputBytes.Length, outputBytes, 0);

            try
            {
                bbc.DoFinal(outputBytes, length);
            }
            catch (Exception)
            {
                this.error.setError("SB010", "AEAD encryption exception");
                return("");
            }
            string result = Base64.ToBase64String(outputBytes);

            if (result == null || result.Length == 0)
            {
                this.error.setError("SB011", "Error encoding base64");
                return("");
            }
            this.error.cleanError();
            return(result);
        }
 /// <summary>
 /// True if it uses CTS
 /// </summary>
 /// <param name="mode">string SymmetricBlockModes enum, symmetric block mode name</param>
 /// <param name="padding">string SymmetricBlockPadding enum, symmetric block padding name</param>
 /// <returns>boolean true if it uses CTS</returns>
 private bool usesCTS(SymmetricBlockMode mode, SymmetricBlockPadding padding)
 {
     return(mode == SymmetricBlockMode.CTS || padding == SymmetricBlockPadding.WITHCTS);
 }
        public string DoDecrypt(string symmetricBlockAlgorithm, string symmetricBlockMode,
                                string symmetricBlockPadding, string key, string IV, string encryptedInput)
        {
            this.error.cleanError();
            SymmetricBlockAlgorithm algorithm = SymmetricBlockAlgorithmUtils.getSymmetricBlockAlgorithm(symmetricBlockAlgorithm, this.error);
            SymmetricBlockMode      mode      = SymmetricBlockModeUtils.getSymmetricBlockMode(symmetricBlockMode, this.error);
            SymmetricBlockPadding   padding   = SymmetricBlockPaddingUtils.getSymmetricBlockPadding(symmetricBlockPadding, this.error);

            if (this.error.existsError())
            {
                return("");
            }

            BufferedBlockCipher bbc = getCipher(algorithm, mode, padding);

            if (this.error.existsError() && !(string.Compare(this.error.Code, "SB016", true) == 0))
            {
                return("");
            }
            byte[] bytesKey = SecurityUtils.GetHexa(key, "SB023", this.error);
            byte[] bytesIV  = SecurityUtils.GetHexa(IV, "SB023", this.error);
            if (this.HasError())
            {
                return("");
            }

            KeyParameter keyParam = new KeyParameter(bytesKey);

            if (SymmetricBlockMode.ECB != mode && SymmetricBlockMode.OPENPGPCFB != mode)
            {
                ParametersWithIV keyParamWithIV = new ParametersWithIV(keyParam, bytesIV);
                try
                {
                    bbc.Init(false, keyParamWithIV);
                }catch (Exception e)
                {
                    this.error.setError("SB027", e.Message);
                    return("");
                }
            }
            else
            {
                try
                {
                    bbc.Init(false, keyParam);
                }catch (Exception e)
                {
                    this.error.setError("SB028", e.Message);
                    return("");
                }
            }

            byte[] out2            = Base64.Decode(encryptedInput);
            byte[] comparisonBytes = new byte[bbc.GetOutputSize(out2.Length)];
            int    length          = bbc.ProcessBytes(out2, 0, out2.Length, comparisonBytes, 0);

            try
            {
                bbc.DoFinal(comparisonBytes, length);
            }
            catch (Exception)
            {
                this.error.setError("SB015", "Block decryption exception");
                return("");
            }
            this.error.cleanError();

            EncodingUtil eu = new EncodingUtil();

            this.error = eu.GetError();
            return(eu.getString(comparisonBytes));
        }
        public string DoEncrypt(string symmetricBlockAlgorithm, string symmetricBlockMode,
                                string symmetricBlockPadding, string key, string IV, string plainText)
        {
            this.error.cleanError();
            SymmetricBlockAlgorithm algorithm = SymmetricBlockAlgorithmUtils.getSymmetricBlockAlgorithm(symmetricBlockAlgorithm, this.error);
            SymmetricBlockMode      mode      = SymmetricBlockModeUtils.getSymmetricBlockMode(symmetricBlockMode, this.error);
            SymmetricBlockPadding   padding   = SymmetricBlockPaddingUtils.getSymmetricBlockPadding(symmetricBlockPadding, this.error);

            if (this.error.existsError())
            {
                return("");
            }

            BufferedBlockCipher bbc = getCipher(algorithm, mode, padding);

            if (this.error.existsError() && !(string.Compare(this.error.Code, "SB016", true) == 0))
            {
                return("");
            }
            byte[] byteIV  = SecurityUtils.GetHexa(IV, "SB022", this.error);
            byte[] byteKey = SecurityUtils.GetHexa(key, "SB022", this.error);
            if (this.HasError())
            {
                return("");
            }
            KeyParameter keyParam = new KeyParameter(byteKey);

            if (SymmetricBlockMode.ECB != mode && SymmetricBlockMode.OPENPGPCFB != mode)
            {
                ParametersWithIV keyParamWithIV = new ParametersWithIV(keyParam, byteIV);
                try{
                    bbc.Init(true, keyParamWithIV);
                }catch (Exception e)
                {
                    this.error.setError("SB025", e.Message);
                    return("");
                }
            }
            else
            {
                try
                {
                    bbc.Init(true, keyParam);
                }catch (Exception e)
                {
                    this.error.setError("SB026", e.Message);
                    return("");
                }
            }

            EncodingUtil eu = new EncodingUtil();

            byte[] inputBytes = eu.getBytes(plainText);
            if (eu.GetError().existsError())
            {
                this.error = eu.GetError();
                return("");
            }
            byte[] outputBytes = new byte[bbc.GetOutputSize(inputBytes.Length)];
            int    length      = bbc.ProcessBytes(inputBytes, 0, inputBytes.Length, outputBytes, 0);

            try
            {
                bbc.DoFinal(outputBytes, length);
            }
            catch (Exception)
            {
                this.error.setError("SB013", "Block encryption exception");
                return("");
            }
            string result = Base64.ToBase64String(outputBytes);

            if (result == null || result.Length == 0)
            {
                this.error.setError("SB014", "Error encoding base64");
                return("");
            }
            this.error.cleanError();
            return(result);
        }