using System;
using Renci.SshNet.Security.Cryptography.Ciphers;
namespace Renci.SshNet.Security.Cryptography
{
///
/// Base class for block cipher implementations.
///
public abstract class BlockCipher : SymmetricCipher
{
private readonly CipherMode _mode;
private readonly CipherPadding _padding;
///
/// Gets the size of the block in bytes.
///
///
/// The size of the block in bytes.
///
private readonly byte _blockSize;
///
/// Gets the minimum data size.
///
///
/// The minimum data size.
///
public override byte MinimumSize
{
get { return BlockSize; }
}
///
/// Gets the size of the block.
///
///
/// The size of the block.
///
public byte BlockSize
{
get
{
return _blockSize;
}
}
///
/// Initializes a new instance of the class.
///
/// The key.
/// Size of the block.
/// Cipher mode.
/// Cipher padding.
/// is null.
protected BlockCipher(byte[] key, byte blockSize, CipherMode mode, CipherPadding padding)
: base(key)
{
_blockSize = blockSize;
_mode = mode;
_padding = padding;
_mode?.Init(this);
}
///
/// Encrypts the specified data.
///
/// The data.
/// The zero-based offset in at which to begin encrypting.
/// The number of bytes to encrypt from .
/// Encrypted data
public override byte[] Encrypt(byte[] input, int offset, int length)
{
if (length % _blockSize > 0)
{
if (_padding is null)
{
throw new ArgumentException("data");
}
var paddingLength = _blockSize - (length % _blockSize);
input = _padding.Pad(input, offset, length, paddingLength);
length += paddingLength;
offset = 0;
}
var output = new byte[length];
var writtenBytes = 0;
for (var i = 0; i < length / _blockSize; i++)
{
if (_mode is null)
{
writtenBytes += EncryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize);
}
else
{
writtenBytes += _mode.EncryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize);
}
}
if (writtenBytes < length)
{
throw new InvalidOperationException("Encryption error.");
}
return output;
}
///
/// Decrypts the specified data.
///
/// The data.
/// Decrypted data
public override byte[] Decrypt(byte[] input)
{
return Decrypt(input, 0, input.Length);
}
///
/// Decrypts the specified input.
///
/// The input.
/// The zero-based offset in at which to begin decrypting.
/// The number of bytes to decrypt from .
///
/// The decrypted data.
///
public override byte[] Decrypt(byte[] input, int offset, int length)
{
if (length % _blockSize > 0)
{
if (_padding is null)
{
throw new ArgumentException("data");
}
input = _padding.Pad(_blockSize, input, offset, length);
offset = 0;
length = input.Length;
}
var output = new byte[length];
var writtenBytes = 0;
for (var i = 0; i < length / _blockSize; i++)
{
if (_mode is null)
{
writtenBytes += DecryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize);
}
else
{
writtenBytes += _mode.DecryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize);
}
}
if (writtenBytes < length)
{
throw new InvalidOperationException("Encryption error.");
}
return output;
}
}
}