Esempio n. 1
0
        public static bool TryDecrypt(byte[] Source, byte[] Passphrase, out byte[] Result)
        {
            if (Source == null || Source.Length == 0)
            {
                throw new ArgumentOutOfRangeException("Cannot decrypt an empty payload!");
            }
            if (Source.Length < CIPHERTEXT_START)
            {
                Result = null;
                return(false); // not a complete header, nothing to decrypt
            }
            //test: is this the correct version and is this a TripleSec payload to begin with?
            byte[] buffer = new byte[4 + 4];
            Buffer.BlockCopy(Source, 0, buffer, 0, buffer.Length);
            if (buffer.ToHexString() != TRIPLESEC_MAGICPLUSVERSION4_HEXBYTES)
            {
                throw new ArgumentException("Not a TripleSec v3 encrypted payload!");
            }

            byte[] theSalt = new byte[SALT_LEN];
            Buffer.BlockCopy(Source, SALT_START, theSalt, 0, SALT_LEN);
            // need the keys to verify the signatures
#if DEBUG
#pragma warning disable 618 // we know in DEBUG mode this will throw an Obsolete warning; this is a legitimate use of this function, so supress it
#endif
            KeysV4 keys = new KeysV4(Passphrase, theSalt);
#if DEBUG
#pragma warning restore 618
#endif
            // VERIFY SIGNATURES HERE
            // get existing signatures
            byte[] SENT_HMACSHA512 = new byte[HMACSHA512_LEN];
            Buffer.BlockCopy(Source, HMACSHA512_START, SENT_HMACSHA512, 0, HMACSHA512_LEN);
            byte[] SENT_HMACSHA3_512 = new byte[HMACSHA3_512_LEN];
            Buffer.BlockCopy(Source, HMACSHA3_512_START, SENT_HMACSHA3_512, 0, HMACSHA3_512_LEN);
            string SENT_SIGNATURE = Utilities.BytesToHexString(SENT_HMACSHA512) + Utilities.BytesToHexString(SENT_HMACSHA3_512);

            //calculate hash on sent data before decryption
            int sourceLength = Source.Length - CIPHERTEXT_START; // the AES_IV isn't counted here

            byte[] AES_IV = new byte[AES_IV_LEN];                // we need the AES_IV as part of the hmac calculations
            Buffer.BlockCopy(Source, AES_IV_START, AES_IV, 0, AES_IV_LEN);

            HMACSHA512 sha512 = new HMACSHA512(keys.HMACSHA512_KEY);
            SHA3Managed.HMACSHA3_512 sha3_512 = new SHA3Managed.HMACSHA3_512(keys.HMACSHA3_512_KEY);
            sha512.TransformBlock(TRIPLESEC_MAGICPLUSVERSION4_BYTES, 0, TRIPLESEC_MAGICPLUSVERSION4_BYTELENGTH, TRIPLESEC_MAGICPLUSVERSION4_BYTES, 0);
            sha3_512.HashCore(TRIPLESEC_MAGICPLUSVERSION4_BYTES, 0, TRIPLESEC_MAGICPLUSVERSION4_BYTELENGTH);
            sha512.TransformBlock(theSalt, 0, theSalt.Length, theSalt, 0);
            sha3_512.HashCore(theSalt, 0, theSalt.Length);
            sha512.TransformBlock(AES_IV, 0, AES_IV.Length, AES_IV, 0);
            sha3_512.HashCore(AES_IV, 0, AES_IV.Length);
            sha512.TransformBlock(Source, CIPHERTEXT_START, sourceLength, Source, CIPHERTEXT_START);
            sha3_512.HashCore(Source, CIPHERTEXT_START, sourceLength);
            sha512.TransformFinalBlock(Source, 0, 0);
            sha3_512.HashFinal(Source, 0, 0);
//#if DEBUG
//            System.Diagnostics.Debug.Print("DECRYPT SIGNATURES:");
//            System.Diagnostics.Debug.Print(BitConverter.ToString(sha512.Hash).Replace("-", ""));
//            System.Diagnostics.Debug.Print(BitConverter.ToString(sha3_512.Hash).Replace("-", ""));
//#endif
            string CALCULATED_SIGNATURE = Utilities.BytesToHexString(sha512.Hash) + Utilities.BytesToHexString(sha3_512.Hash);

            if (CALCULATED_SIGNATURE != SENT_SIGNATURE)
            {
                Result = null;
                return(false);
            }
            //==================================================================================

            byte[] OUTPUT = new byte[sourceLength];
            // AES is first (outermost)
            BC.Crypto.Modes.SicBlockCipher mySIC_AES = new BC.Crypto.Modes.SicBlockCipher(new BC.Crypto.Engines.AesEngine());
            mySIC_AES.Init(true,
                           new BC.Crypto.Parameters.ParametersWithIV(
                               new BC.Crypto.Parameters.KeyParameter(keys.AES_KEY),
                               AES_IV));
            ProcessArray(mySIC_AES, Source, CIPHERTEXT_START, OUTPUT, 0, sourceLength);

            //Twofish is REMOVED IN V4

            // prepare for next
            byte[] IV_XSALSA20 = new byte[24];
            Buffer.BlockCopy(OUTPUT, 0, IV_XSALSA20, 0, IV_XSALSA20.Length);
            byte[] INPUT = new byte[OUTPUT.Length - IV_XSALSA20.Length];
            Buffer.BlockCopy(OUTPUT, IV_XSALSA20.Length, INPUT, 0, INPUT.Length);
            OUTPUT.Wipe(); // DON'T LEAK!

            //XSalsa20 is last
            OUTPUT = Chaos.NaCl.XSalsa20.Process(INPUT, keys.XSalsa20_KEY, IV_XSALSA20);
            INPUT.Wipe(); // DON'T LEAK!
            Result = OUTPUT;
            return(true);
        }
Esempio n. 2
0
        /// <summary>
        /// Encrypt an array of bytes using the TripleSec protocol with the supplied passphrase.  Internal function, not intended for use by an end coder.
        /// </summary>
        /// <param name="Source">Source array of bytes to process.</param>
        /// <param name="Keys">The KeysV3 object initialized with the proper keys.</param>
        /// <param name="IVs">The RNGV3 object initialized with the proper initialization arrays.</param>
        /// <returns>The encrypted array.</returns>
        internal static byte[] Encrypt(byte[] Source, KeysV4 Keys, RNGV4 IVs)
#endif
        {
            if (Source == null || Source.Length == 0)
            {
                throw new ArgumentNullException("Source");
            }
            if (Keys == null || !Keys.IsInitialized || IVs == null || !IVs.IsInitialized)
            {
                throw new InvalidOperationException("Keys and IVs objects must not be null (and both must be initialized)");
            }

            // the added IV's and the signatures and the header always add up to the same length added to the original message length
            //  if we're going to run out of memory, it will be here, or shortly thereafter
            byte[] OUT_BUFFER = new byte[Source.Length + 192];
            //standard header stuff
            // All the header pieces are now copied by the built-in SHA512 method TransformBlock

            //XSalsa20 is up first
            byte[] interim  = Chaos.NaCl.XSalsa20.Process(Source, Keys.XSalsa20_KEY, IVs.XSalsa20_IV);
            byte[] interim2 = new byte[interim.Length + IVs.XSalsa20_IV.Length]; // IV included here as it is part of the input to the next process
            Buffer.BlockCopy(IVs.XSalsa20_IV, 0, interim2, 0, IVs.XSalsa20_IV.Length);
            Buffer.BlockCopy(interim, 0, interim2, IVs.XSalsa20_IV.Length, interim.Length);

            ////Twofish is REMOVED FROM V4

            //AES is last
            interim.Wipe(); // DON'T LEAK!
            BC.Crypto.Modes.SicBlockCipher aes = new BC.Crypto.Modes.SicBlockCipher(new BC.Crypto.Engines.AesEngine());
            aes.Init(true, new BC.Crypto.Parameters.ParametersWithIV(new BC.Crypto.Parameters.KeyParameter(Keys.AES_KEY), IVs.AES_IV));
            ProcessArray(aes, interim2, interim2); // process array in-place
            // buffer is copied to output by built-in HMACSHA512 TransFormBlock
            aes.Reset();
            aes = null;

            HMACSHA512 sha512 = new HMACSHA512(Keys.HMACSHA512_KEY);

            sha512.TransformBlock(TRIPLESEC_MAGICPLUSVERSION4_BYTES, 0, TRIPLESEC_MAGICPLUSVERSION4_BYTELENGTH, OUT_BUFFER, 0);
            sha512.TransformBlock(Keys.Salt, 0, Keys.Salt.Length, OUT_BUFFER, SALT_START);
            sha512.TransformBlock(IVs.AES_IV, 0, IVs.AES_IV.Length, OUT_BUFFER, AES_IV_START);
            sha512.TransformBlock(interim2, 0, interim2.Length, OUT_BUFFER, CIPHERTEXT_START);
            sha512.TransformFinalBlock(interim2, 0, 0); // transform final block doesn't copy the output
            Buffer.BlockCopy(sha512.Hash, 0, OUT_BUFFER, HMACSHA512_START, sha512.Hash.Length);

            SHA3Managed.HMACSHA3_512 sha3_512 = new SHA3Managed.HMACSHA3_512(Keys.HMACSHA3_512_KEY);
            sha3_512.HashCore(TRIPLESEC_MAGICPLUSVERSION4_BYTES, 0, TRIPLESEC_MAGICPLUSVERSION4_BYTELENGTH);
            sha3_512.HashCore(Keys.Salt, 0, Keys.Salt.Length);
            sha3_512.HashCore(IVs.AES_IV, 0, IVs.AES_IV.Length);
            sha3_512.HashCore(interim2, 0, interim2.Length);
            sha3_512.HashFinal(interim2, 0, 0); // transform final block doesn't copy the output
            Buffer.BlockCopy(sha3_512.Hash, 0, OUT_BUFFER, HMACSHA3_512_START, sha3_512.Hash.Length);
//#if DEBUG
//            System.Diagnostics.Debug.Print("ENCRYPT SIGNATURES: HMACSHA512, HMACSHA3_512, CipherText");
//            System.Diagnostics.Debug.Print(BitConverter.ToString(sha512.Hash).Replace("-", "").ToLowerInvariant());
//            System.Diagnostics.Debug.Print(BitConverter.ToString(sha3_512.Hash).Replace("-", "").ToLowerInvariant());
//            System.Diagnostics.Debug.Print(BitConverter.ToString(OUT_BUFFER).Replace("-","").ToLowerInvariant());
//#endif
            // HASHLIB REMOVED -------- !

            return(OUT_BUFFER);
        }
Esempio n. 3
0
        /// <summary>
        /// Encrypt an array of bytes using the TripleSec protocol with the supplied passphrase.  Internal function, not intended for use by an end coder.
        /// </summary>
        /// <param name="Source">Source array of bytes to process.</param>
        /// <param name="Keys">The KeysV3 object initialized with the proper keys.</param>
        /// <param name="IVs">The RNGV3 object initialized with the proper initialization arrays.</param>
        /// <returns>The encrypted array.</returns>
        internal static byte[] Encrypt(byte[] Source, KeysV3 Keys, RNGV3 IVs)
#endif
        {
            if (Source == null || Source.Length == 0)
                throw new ArgumentNullException("Source");
            if (Keys == null || !Keys.IsInitialized || IVs == null || !IVs.IsInitialized)
                throw new InvalidOperationException("Keys and IVs objects must not be null (and both must be initialized)");
            
            // the added IV's and the signatures and the header always add up to the same length added to the original message length
            //  if we're going to run out of memory, it will be here, or shortly thereafter
            byte[] OUT_BUFFER = new byte[Source.Length + 208];
            //standard header stuff
            Buffer.BlockCopy(Utilities.HexStringToBytes(TRIPLESEC_MAGICPLUSVERSION3_HEXBYTES), 0, OUT_BUFFER, 0, 8);
            // The SALT and AES IV are public in the final construction, so putting them here is a minimal risk.  
            // At worst, it leaks RNG material on abort.
            //salt
            Buffer.BlockCopy(IVs.Salt, 0, OUT_BUFFER, SALT_START, SALT_LEN);
            //AES IV
            Buffer.BlockCopy(IVs.AES_IV, 0, OUT_BUFFER, AES_IV_START, AES_IV_LEN);

            //XSalsa20 is up first
            byte[] interim = Chaos.NaCl.XSalsa20.Process(Source, Keys.XSalsa20_KEY, IVs.XSalsa20_IV);
            byte[] interim2 = new byte[interim.Length + IVs.XSalsa20_IV.Length]; // IV included here as it is part of the input to the next process
            Buffer.BlockCopy(IVs.XSalsa20_IV, 0, interim2, 0, IVs.XSalsa20_IV.Length);
            Buffer.BlockCopy(interim, 0, interim2, IVs.XSalsa20_IV.Length, interim.Length);
            
            //Twofish is up next
            interim.Wipe();  // DON'T LEAK!
            interim = new byte[interim2.Length + IVs.Twofish_IV.Length]; // IV included here as it is part of the input to the next process
            Buffer.BlockCopy(IVs.Twofish_IV, 0, interim, 0, IVs.Twofish_IV.Length);
            BC.Crypto.Modes.SicBlockCipher tf = new BC.Crypto.Modes.SicBlockCipher(new BC.Crypto.Engines.TwofishEngine());
            tf.Init(true, new BC.Crypto.Parameters.ParametersWithIV(new BC.Crypto.Parameters.KeyParameter(Keys.Twofish_KEY), IVs.Twofish_IV));
            ProcessArray(tf, interim2, interim2); // process array in-place
            Buffer.BlockCopy(interim2, 0, interim, IVs.Twofish_IV.Length, interim2.Length);
            tf.Reset(); tf = null;
            
            //AES is last
            interim2.Wipe(); // DON'T LEAK!
            BC.Crypto.Modes.SicBlockCipher aes = new BC.Crypto.Modes.SicBlockCipher(new BC.Crypto.Engines.AesEngine());
            aes.Init(true, new BC.Crypto.Parameters.ParametersWithIV(new BC.Crypto.Parameters.KeyParameter(Keys.AES_KEY), IVs.AES_IV));
            ProcessArray(aes, interim, interim); // process array in-place
            Buffer.BlockCopy(interim, 0, OUT_BUFFER, CIPHERTEXT_START, interim.Length);
            aes.Reset();
            aes = null;

            HashLib.IHMAC h512 = CreateSHA512MacObject(Keys.HMACSHA512_KEY);
            h512.TransformBytes(Utilities.HexStringToBytes(TRIPLESEC_MAGICPLUSVERSION3_HEXBYTES)); //header
            h512.TransformBytes(IVs.Salt); //salt
            h512.TransformBytes(IVs.AES_IV); // IV for AES
            h512.TransformBytes(interim); // ciphertext
            HashLib.HashResult sig1 = h512.TransformFinal(); // get the result
            Buffer.BlockCopy(sig1.GetBytes(), 0, OUT_BUFFER, HMACSHA512_START, HMACSHA512_LEN); // store it

            HashLib.IHMAC keccak = CreateKeccak512MacObject(Keys.HMACKeccak512_KEY);
            keccak.TransformBytes(Utilities.HexStringToBytes(TRIPLESEC_MAGICPLUSVERSION3_HEXBYTES)); //header
            keccak.TransformBytes(IVs.Salt); //salt
            keccak.TransformBytes(IVs.AES_IV); // IV for AES
            keccak.TransformBytes(interim); // ciphertext
            HashLib.HashResult sig2 = keccak.TransformFinal(); // get the result
            Buffer.BlockCopy(sig2.GetBytes(), 0, OUT_BUFFER, HMACKECCAK512_START, HMACKECCAK512_LEN); //store it
            
            interim.Wipe(); // this isn't really leaking since it's encrypted AND copied to the output buffer, but clean house all the same.
            
            return OUT_BUFFER;
        }
Esempio n. 4
0
        public static bool TryDecrypt(byte[] Source, byte[] Passphrase, out byte[] Result)
        {
            //is this the correct version and is this a TripleSec payload to begin with
            byte[] buffer = new byte[4 + 4];
            Buffer.BlockCopy(Source, 0, buffer, 0, buffer.Length);
            if (buffer.ToHexString() != TRIPLESEC_MAGICPLUSVERSION3_HEXBYTES)
                throw new ArgumentException("Not a TripleSec encrypted payload!");

            byte[] theSalt = new byte[SALT_LEN];
            Buffer.BlockCopy(Source, SALT_START, theSalt, 0, SALT_LEN);
            // need the keys to verify the signatures
#pragma warning disable 618 // we know in DEBUG mode this will throw an Obsolete warning, this is a legitimate use of this function, so supress it
            KeysV3 keys = new KeysV3(Passphrase, theSalt);
#pragma warning restore 618
            // VERIFY SIGNATURES HERE
            // get existing signatures
            byte[] SENT_HMACSHA512 = new byte[HMACSHA512_LEN];
            Buffer.BlockCopy(Source, HMACSHA512_START, SENT_HMACSHA512, 0, HMACSHA512_LEN);
            byte[] SENT_HMACKECCAK512 = new byte[HMACKECCAK512_LEN];
            Buffer.BlockCopy(Source, HMACKECCAK512_START, SENT_HMACKECCAK512, 0, HMACKECCAK512_LEN);
            string SENT_SIGNATURE = Utilities.BytesToHexString(SENT_HMACSHA512) + Utilities.BytesToHexString(SENT_HMACKECCAK512);

            //calculate hash on sent data before decryption
            int sourceLength = Source.Length - CIPHERTEXT_START; // the AES_IV isn't counted here

            byte[] AES_IV = new byte[AES_IV_LEN];
            Buffer.BlockCopy(Source, AES_IV_START, AES_IV, 0, AES_IV_LEN);

            HashLib.IHMAC hsha512 = CreateSHA512MacObject(keys.HMACSHA512_KEY);
            hsha512.TransformBytes(Utilities.HexStringToBytes(TRIPLESEC_MAGICPLUSVERSION3_HEXBYTES)); //header
            hsha512.TransformBytes(theSalt); //salt
            hsha512.TransformBytes(AES_IV); // IV for AES
            hsha512.TransformBytes(Source, CIPHERTEXT_START, sourceLength);
            HashLib.HashResult sig1 = hsha512.TransformFinal();

            HashLib.IHMAC hkeccak512 = CreateKeccak512MacObject(keys.HMACKeccak512_KEY);
            hkeccak512.TransformBytes(Utilities.HexStringToBytes(TRIPLESEC_MAGICPLUSVERSION3_HEXBYTES)); //header
            hkeccak512.TransformBytes(theSalt); //salt
            hkeccak512.TransformBytes(AES_IV); // IV for AES
            hkeccak512.TransformBytes(Source, CIPHERTEXT_START, sourceLength);
            HashLib.HashResult sig2 = hkeccak512.TransformFinal();
            string CALCULATED_SIGNATURE = Utilities.BytesToHexString(sig1.GetBytes()) + Utilities.BytesToHexString(sig2.GetBytes());

            if (CALCULATED_SIGNATURE != SENT_SIGNATURE)
            {
                Result = null;
                return false;
            }
            //==================================================================================

            byte[] OUTPUT = new byte[sourceLength];
            // AES is first (outermost)
            BC.Crypto.Modes.SicBlockCipher mySIC_AES = new BC.Crypto.Modes.SicBlockCipher(new BC.Crypto.Engines.AesEngine());
            mySIC_AES.Init(true,
                new BC.Crypto.Parameters.ParametersWithIV(
                    new BC.Crypto.Parameters.KeyParameter(keys.AES_KEY),
                    AES_IV));
            ProcessArray(mySIC_AES, Source, CIPHERTEXT_START, OUTPUT, 0, sourceLength);

            // prepare for next
            byte[] IV_TWOFISH = new byte[16];
            Buffer.BlockCopy(OUTPUT, 0, IV_TWOFISH, 0, IV_TWOFISH.Length);
            byte[] INPUT = new byte[OUTPUT.Length - IV_TWOFISH.Length];
            Buffer.BlockCopy(OUTPUT, IV_TWOFISH.Length, INPUT, 0, INPUT.Length);
            OUTPUT.Wipe(); // DON'T LEAK!
            OUTPUT = new byte[INPUT.Length];

            //Twofish is next
            BC.Crypto.Modes.SicBlockCipher mySIC_TF = new BC.Crypto.Modes.SicBlockCipher(new BC.Crypto.Engines.TwofishEngine());
            mySIC_TF.Init(true,
                new BC.Crypto.Parameters.ParametersWithIV(
                    new BC.Crypto.Parameters.KeyParameter(keys.Twofish_KEY),
                    IV_TWOFISH));
            ProcessArray(mySIC_TF, INPUT, OUTPUT);
            INPUT.Wipe(); // DON'T LEAK!
            
            // prepare for next
            byte[] IV_XSALSA20 = new byte[24];
            Buffer.BlockCopy(OUTPUT, 0, IV_XSALSA20, 0, IV_XSALSA20.Length);
            INPUT = new byte[OUTPUT.Length - IV_XSALSA20.Length];
            Buffer.BlockCopy(OUTPUT, IV_XSALSA20.Length, INPUT, 0, INPUT.Length);
            OUTPUT.Wipe(); // DON'T LEAK!

            //XSalsa20 is last
            OUTPUT = Chaos.NaCl.XSalsa20.Process(INPUT, keys.XSalsa20_KEY, IV_XSALSA20);
            INPUT.Wipe(); // DON'T LEAK!
            Result = OUTPUT;
            return true;
        }
Esempio n. 5
0
        /// <summary>
        /// Encrypt an array of bytes using the TripleSec protocol with the supplied passphrase.  Internal function, not intended for use by an end coder.
        /// </summary>
        /// <param name="Source">Source array of bytes to process.</param>
        /// <param name="Keys">The KeysV3 object initialized with the proper keys.</param>
        /// <param name="IVs">The RNGV3 object initialized with the proper initialization arrays.</param>
        /// <returns>The encrypted array.</returns>
        internal static byte[] Encrypt(byte[] Source, KeysV3 Keys, RNGV3 IVs)
#endif
        {
            if (Source == null || Source.Length == 0)
            {
                throw new ArgumentNullException("Source");
            }
            if (Keys == null || !Keys.IsInitialized || IVs == null || !IVs.IsInitialized)
            {
                throw new InvalidOperationException("Keys and IVs objects must not be null (and both must be initialized)");
            }

            // the added IV's and the signatures and the header always add up to the same length added to the original message length
            //  if we're going to run out of memory, it will be here, or shortly thereafter
            byte[] OUT_BUFFER = new byte[Source.Length + 208];
            //standard header stuff
            // All the header pieces are now copied by the built-in SHA512 method TransformBlock

            //XSalsa20 is up first
            byte[] interim  = Chaos.NaCl.XSalsa20.Process(Source, Keys.XSalsa20_KEY, IVs.XSalsa20_IV);
            byte[] interim2 = new byte[interim.Length + IVs.XSalsa20_IV.Length]; // IV included here as it is part of the input to the next process
            Buffer.BlockCopy(IVs.XSalsa20_IV, 0, interim2, 0, IVs.XSalsa20_IV.Length);
            Buffer.BlockCopy(interim, 0, interim2, IVs.XSalsa20_IV.Length, interim.Length);

            //Twofish is up next
            interim.Wipe();                                              // DON'T LEAK!
            interim = new byte[interim2.Length + IVs.Twofish_IV.Length]; // IV included here as it is part of the input to the next process
            Buffer.BlockCopy(IVs.Twofish_IV, 0, interim, 0, IVs.Twofish_IV.Length);
            BC.Crypto.Modes.SicBlockCipher tf = new BC.Crypto.Modes.SicBlockCipher(new BC.Crypto.Engines.TwofishEngine());
            tf.Init(true, new BC.Crypto.Parameters.ParametersWithIV(new BC.Crypto.Parameters.KeyParameter(Keys.Twofish_KEY), IVs.Twofish_IV));
            ProcessArray(tf, interim2, interim2); // process array in-place
            Buffer.BlockCopy(interim2, 0, interim, IVs.Twofish_IV.Length, interim2.Length);
            tf.Reset(); tf = null;

            //AES is last
            interim2.Wipe(); // DON'T LEAK!
            BC.Crypto.Modes.SicBlockCipher aes = new BC.Crypto.Modes.SicBlockCipher(new BC.Crypto.Engines.AesEngine());
            aes.Init(true, new BC.Crypto.Parameters.ParametersWithIV(new BC.Crypto.Parameters.KeyParameter(Keys.AES_KEY), IVs.AES_IV));
            ProcessArray(aes, interim, interim); // process array in-place
            //the HMACSHA512 process will copy the data to the output as it hashes
            aes.Reset();
            aes = null;

            HMACSHA512 sha512 = new HMACSHA512(Keys.HMACSHA512_KEY);

            sha512.TransformBlock(TRIPLESEC_MAGICPLUSVERSION3_BYTES, 0, TRIPLESEC_MAGICPLUSVERSION3_BYTELENGTH, OUT_BUFFER, 0);
            sha512.TransformBlock(Keys.Salt, 0, Keys.Salt.Length, OUT_BUFFER, SALT_START);
            sha512.TransformBlock(IVs.AES_IV, 0, IVs.AES_IV.Length, OUT_BUFFER, AES_IV_START);
            sha512.TransformBlock(interim, 0, interim.Length, OUT_BUFFER, CIPHERTEXT_START);
            sha512.TransformFinalBlock(interim, 0, 0); // transform final block doesn't copy the output
            Buffer.BlockCopy(sha512.Hash, 0, OUT_BUFFER, HMACSHA512_START, sha512.Hash.Length);


            // Proposed SHA3 uses a delimiter/padding byte of 0x01 intead of 0x06 or 0x1f (SHAKE128/256), that's the only difference
            // This is IMPORTANT for V3, as it used a pre-release version of the proposed SHA3 standard.  NIST changed SHA3 well after
            //  TripleSec was released and had been used in the wild.
            SHA3Managed.HMAC_Proposed_SHA3_512 sha3_512 = new SHA3Managed.HMAC_Proposed_SHA3_512(Keys.HMACKeccak512_KEY);
            sha3_512.HashCore(TRIPLESEC_MAGICPLUSVERSION3_BYTES, 0, TRIPLESEC_MAGICPLUSVERSION3_BYTELENGTH);
            sha3_512.HashCore(Keys.Salt, 0, Keys.Salt.Length);
            sha3_512.HashCore(IVs.AES_IV, 0, IVs.AES_IV.Length);
            sha3_512.HashCore(interim, 0, interim.Length);
            sha3_512.HashFinal(interim, 0, 0); // transform final block doesn't copy the output
            Buffer.BlockCopy(sha3_512.Hash, 0, OUT_BUFFER, HMACKECCAK512_START, sha3_512.Hash.Length);
//#if DEBUG
//            System.Diagnostics.Debug.Print("ENCRYPT SIGNATURES:");
//            System.Diagnostics.Debug.Print(BitConverter.ToString(sha512.Hash).Replace("-", ""));
//            System.Diagnostics.Debug.Print(BitConverter.ToString(sha3_512.Hash).Replace("-", ""));
//            System.Diagnostics.Debug.Print(BitConverter.ToString(OUT_BUFFER).Replace("-",""));
//#endif
            // HASHLIB REMOVED -----------!

            interim.Wipe(); // this isn't really leaking since it's encrypted AND copied to the output buffer, but clean house all the same.

            return(OUT_BUFFER);
        }