AesCipher
AES cipher implementation.
using Renci.SshNet.Common;
using Renci.SshNet.Security.Cryptography.Ciphers.Modes;
using Renci.SshNet.Security.Cryptography.Ciphers.Paddings;
using System;
using System.Buffers.Binary;
using System.Numerics;
using System.Security.Cryptography;
namespace Renci.SshNet.Security.Cryptography.Ciphers
{
public sealed class AesCipher : BlockCipher, IDisposable
{
private sealed class BclImpl : BlockCipher, IDisposable
{
private readonly Aes _aes;
private readonly ICryptoTransform _encryptor;
private readonly ICryptoTransform _decryptor;
public BclImpl(byte[] key, byte[] iv, System.Security.Cryptography.CipherMode cipherMode, PaddingMode paddingMode)
: base(key, 16, null, null)
{
Aes aes = Aes.Create();
aes.Key = key;
if (cipherMode != System.Security.Cryptography.CipherMode.ECB) {
if (iv == null)
throw new ArgumentNullException("iv");
aes.IV = iv.Take(16);
}
aes.Mode = cipherMode;
aes.Padding = paddingMode;
aes.FeedbackSize = 128;
_aes = aes;
_encryptor = aes.CreateEncryptor();
_decryptor = aes.CreateDecryptor();
}
public override byte[] Encrypt(byte[] input, int offset, int length)
{
if (_aes.Padding != PaddingMode.None)
return _encryptor.TransformFinalBlock(input, offset, length);
byte[] array = new byte[length];
_encryptor.TransformBlock(input, offset, length, array, 0);
return array;
}
public override byte[] Decrypt(byte[] input, int offset, int length)
{
if (_aes.Padding != PaddingMode.None)
return _decryptor.TransformFinalBlock(input, offset, length);
byte[] array = new byte[length];
_decryptor.TransformBlock(input, offset, length, array, 0);
return array;
}
public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
throw new NotImplementedException("Invalid usage of EncryptBlock.");
}
public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
throw new NotImplementedException("Invalid usage of DecryptBlock.");
}
private void Dispose(bool disposing)
{
if (disposing) {
_aes.Dispose();
_encryptor.Dispose();
_decryptor.Dispose();
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
private sealed class BlockImpl : BlockCipher, IDisposable
{
private readonly Aes _aes;
private readonly ICryptoTransform _encryptor;
private readonly ICryptoTransform _decryptor;
public BlockImpl(byte[] key, CipherMode mode, CipherPadding padding)
: base(key, 16, mode, padding)
{
Aes aes = Aes.Create();
aes.Key = key;
aes.Mode = System.Security.Cryptography.CipherMode.ECB;
aes.Padding = PaddingMode.None;
_aes = aes;
_encryptor = aes.CreateEncryptor();
_decryptor = aes.CreateDecryptor();
}
public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
return _encryptor.TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset);
}
public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
return _decryptor.TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset);
}
private void Dispose(bool disposing)
{
if (disposing) {
_aes.Dispose();
_encryptor.Dispose();
_decryptor.Dispose();
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
private sealed class CtrImpl : BlockCipher, IDisposable
{
private readonly Aes _aes;
private readonly ICryptoTransform _encryptor;
private ulong _ivUpper;
private ulong _ivLower;
public CtrImpl(byte[] key, byte[] iv)
: base(key, 16, null, null)
{
Aes aes = Aes.Create();
aes.Key = key;
aes.Mode = System.Security.Cryptography.CipherMode.ECB;
aes.Padding = PaddingMode.None;
_aes = aes;
_encryptor = aes.CreateEncryptor();
_ivLower = BinaryPrimitives.ReadUInt64BigEndian(iv.AsSpan(8));
_ivUpper = BinaryPrimitives.ReadUInt64BigEndian(iv);
}
public override byte[] Encrypt(byte[] input, int offset, int length)
{
return CTREncryptDecrypt(input, offset, length);
}
public override byte[] Decrypt(byte[] input, int offset, int length)
{
return CTREncryptDecrypt(input, offset, length);
}
public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
throw new NotImplementedException("Invalid usage of DecryptBlock.");
}
public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
throw new NotImplementedException("Invalid usage of EncryptBlock.");
}
private byte[] CTREncryptDecrypt(byte[] data, int offset, int length)
{
int num = length / (int)base.BlockSize;
if (length % (int)base.BlockSize != 0)
num++;
byte[] array = new byte[num * base.BlockSize];
CTRCreateCounterArray(array);
_encryptor.TransformBlock(array, 0, array.Length, array, 0);
ArrayXOR(array, data, offset, length);
if (array.Length > length)
Array.Resize(ref array, length);
return array;
}
private void CTRCreateCounterArray(byte[] buffer)
{
for (int i = 0; i < buffer.Length; i += 16) {
BinaryPrimitives.WriteUInt64BigEndian(buffer.AsSpan(i + 8), _ivLower);
BinaryPrimitives.WriteUInt64BigEndian(buffer.AsSpan(i), _ivUpper);
_ivLower++;
_ivUpper += (ulong)((_ivLower == 0) ? 1 : 0);
}
}
private static void ArrayXOR(byte[] buffer, byte[] data, int offset, int length)
{
int i = 0;
for (int num = length - Vector<byte>.Count; i <= num; i += Vector<byte>.Count) {
(new Vector<byte>(buffer, i) ^ new Vector<byte>(data, offset + i)).CopyTo(buffer, i);
}
for (; i < length; i++) {
buffer[i] ^= data[offset + i];
}
}
private void Dispose(bool disposing)
{
if (disposing) {
_aes.Dispose();
_encryptor.Dispose();
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
private readonly BlockCipher _impl;
public AesCipher(byte[] key, byte[] iv, AesCipherMode mode, bool pkcs7Padding = false)
: base(key, 16, null, null)
{
switch (mode) {
case AesCipherMode.OFB:
_impl = new BlockImpl(key, new OfbCipherMode(iv), pkcs7Padding ? new PKCS7Padding() : null);
break;
case AesCipherMode.CFB:
_impl = new BlockImpl(key, new CfbCipherMode(iv), pkcs7Padding ? new PKCS7Padding() : null);
break;
case AesCipherMode.CTR:
_impl = new CtrImpl(key, iv);
break;
default:
_impl = new BclImpl(key, iv, (System.Security.Cryptography.CipherMode)mode, (!pkcs7Padding) ? PaddingMode.None : PaddingMode.PKCS7);
break;
}
}
public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
return _impl.EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset);
}
public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
return _impl.EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset);
}
public override byte[] Encrypt(byte[] input, int offset, int length)
{
return _impl.Encrypt(input, offset, length);
}
public override byte[] Decrypt(byte[] input, int offset, int length)
{
return _impl.Decrypt(input, offset, length);
}
public void Dispose(bool disposing)
{
if (disposing)
(_impl as IDisposable)?.Dispose();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}