/// <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(); }