Example #1
0
        /// <summary>
        /// En- or decrypt input stream with ChaCha.
        /// </summary>
        /// <param name="key">The secret 128-bit or 256-bit key. A 128-bit key will be expanded into a 256-bit key by concatenation with itself.</param>
        /// <param name="iv">Initialization vector (DJB version: 64-bit, IETF version: 96-bit)</param>
        /// <param name="initialCounter">Initial counter (DJB version: 64-bit, IETF version: 32-bit)</param>
        /// <param name="settings">Chosen Settings in the Plugin workspace. Includes Rounds and Version property.</param>
        /// <param name="input">Input stream</param>
        /// <param name="output">Output stream</param>
        /// <param name="keystreamWriter">Keystream Output stream</param>
        private void Xcrypt(byte[] key, byte[] iv, ulong initialCounter, ChaChaSettings settings, ICryptoolStream input, CStreamWriter output, CStreamWriter keystreamOutput)
        {
            if (!(key.Length == 32 || key.Length == 16))
            {
                throw new ArgumentOutOfRangeException("key", key.Length, "Key must be exactly 128-bit or 256-bit.");
            }
            if (iv.Length != settings.Version.IVBits / 8)
            {
                throw new ArgumentOutOfRangeException("iv", iv.Length, $"IV must be exactly {settings.Version.IVBits}-bit.");
            }
            void AssertCounter(ulong counter, ulong max)
            {
                if (!(0 <= counter && counter <= max))
                {
                    throw new ArgumentOutOfRangeException("initialCounter", counter, $"Initial counter must be between 0 and {max}.");
                }
            }

            if (settings.Version == Version.DJB)
            {
                AssertCounter(initialCounter, ulong.MaxValue);
            }
            else if (settings.Version == Version.IETF)
            {
                AssertCounter(initialCounter, uint.MaxValue);
            }

            // The first 512-bit state. Reused for counter insertion.
            uint[] firstState = State(key, iv, initialCounter, settings.Version);

            // Buffer to read 512-bit input block
            byte[]        inputBytes  = new byte[64];
            CStreamReader inputReader = input.CreateReader();

            // byte size of input
            long inputSize = inputReader.Length;

            // one keystream block is 64 bytes (512 bit)
            TotalKeystreamBlocks = (int)Math.Ceiling((double)(inputSize) / 64);

            ulong blockCounter = initialCounter;
            int   read         = inputReader.Read(inputBytes);

            while (read != 0)
            {
                // Will hold the state during each keystream
                uint[] state = (uint[])firstState.Clone();
                InsertCounter(ref state, blockCounter, settings.Version);
                ChaChaHash(ref state, settings.Rounds);

                byte[] keystream = ByteUtil.ToByteArray(state);
                keystreamOutput.Write(keystream);
                byte[] c = ByteUtil.XOR(keystream, inputBytes, read);
                output.Write(c);

                // Don't add to InputMessage during diffusion run because it won't
                // return a different list during the diffusion run.
                if (!DiffusionExecution)
                {
                    InputMessage.AddRange(inputBytes.Take(read));
                }
                Keystream.AddRange(keystream.Take(read));
                Output.AddRange(c);

                blockCounter++;

                // Read next input block
                read = inputReader.Read(inputBytes);
            }
            inputReader.Dispose();
            output.Flush();
            output.Close();
            output.Dispose();
            keystreamOutput.Flush();
            keystreamOutput.Close();
            keystreamOutput.Dispose();
        }