using System;
using System.Globalization;
namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes
{
///
/// Implements CTR cipher mode
///
public class CtrCipherMode : CipherMode
{
private readonly byte[] _ivOutput;
///
/// Initializes a new instance of the class.
///
/// The iv.
public CtrCipherMode(byte[] iv)
: base(iv)
{
_ivOutput = new byte[iv.Length];
}
///
/// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array.
///
/// The input data to encrypt.
/// The offset into the input byte array from which to begin using data.
/// The number of bytes in the input byte array to use as data.
/// The output to which to write encrypted data.
/// The offset into the output byte array from which to begin writing data.
///
/// The number of bytes encrypted.
///
public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
if (inputBuffer.Length - inputOffset < _blockSize)
{
throw new ArgumentException("Invalid input buffer");
}
if (outputBuffer.Length - outputOffset < _blockSize)
{
throw new ArgumentException("Invalid output buffer");
}
if (inputCount != _blockSize)
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize));
}
_ = Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0);
for (var i = 0; i < _blockSize; i++)
{
outputBuffer[outputOffset + i] = (byte)(_ivOutput[i] ^ inputBuffer[inputOffset + i]);
}
var j = IV.Length;
while (--j >= 0 && ++IV[j] == 0)
{
// Intentionally empty block
}
return _blockSize;
}
///
/// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array.
///
/// The input data to decrypt.
/// The offset into the input byte array from which to begin using data.
/// The number of bytes in the input byte array to use as data.
/// The output to which to write decrypted data.
/// The offset into the output byte array from which to begin writing data.
///
/// The number of bytes decrypted.
///
public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
if (inputBuffer.Length - inputOffset < _blockSize)
{
throw new ArgumentException("Invalid input buffer");
}
if (outputBuffer.Length - outputOffset < _blockSize)
{
throw new ArgumentException("Invalid output buffer");
}
if (inputCount != _blockSize)
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize));
}
_ = Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0);
for (var i = 0; i < _blockSize; i++)
{
outputBuffer[outputOffset + i] = (byte)(_ivOutput[i] ^ inputBuffer[inputOffset + i]);
}
var j = IV.Length;
while (--j >= 0 && ++IV[j] == 0)
{
// Intentionally empty block
}
return _blockSize;
}
}
}