AesCipher
AES cipher implementation.
            
                using Org.BouncyCastle.Crypto.Paddings;
using Renci.SshNet.Common;
using Renci.SshNet.Security.Cryptography.Ciphers.Modes;
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) {
                    ThrowHelper.ThrowIfNull(iv, "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);
                int num = 0;
                if (length % (int)base.BlockSize > 0) {
                    System.Security.Cryptography.CipherMode mode = _aes.Mode;
                    if (((uint)(mode - 3) <= 1) ? true : false) {
                        num = base.BlockSize - length % (int)base.BlockSize;
                        input = input.Take(offset, length);
                        length += num;
                        Array.Resize(ref input, length);
                        offset = 0;
                    }
                }
                byte[] array = new byte[length];
                _encryptor.TransformBlock(input, offset, length, array, 0);
                if (num > 0)
                    Array.Resize(ref array, array.Length - num);
                return array;
            }
            public override byte[] Decrypt(byte[] input, int offset, int length)
            {
                if (_aes.Padding != PaddingMode.None)
                    return _decryptor.TransformFinalBlock(input, offset, length);
                int num = 0;
                if (length % (int)base.BlockSize > 0) {
                    System.Security.Cryptography.CipherMode mode = _aes.Mode;
                    if (((uint)(mode - 3) <= 1) ? true : false) {
                        num = base.BlockSize - length % (int)base.BlockSize;
                        input = input.Take(offset, length);
                        length += num;
                        Array.Resize(ref input, length);
                        offset = 0;
                    }
                }
                byte[] array = new byte[length];
                _decryptor.TransformBlock(input, offset, length, array, 0);
                if (num > 0)
                    Array.Resize(ref array, array.Length - num);
                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.");
            }
            public void Dispose()
            {
                _aes.Dispose();
                _encryptor.Dispose();
                _decryptor.Dispose();
            }
        }
        private sealed class BlockImpl : BlockCipher, IDisposable
        {
            private readonly Aes _aes;
            private readonly ICryptoTransform _encryptor;
            private readonly ICryptoTransform _decryptor;
            public BlockImpl(byte[] key, CipherMode mode, IBlockCipherPadding 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);
            }
            public void Dispose()
            {
                _aes.Dispose();
                _encryptor.Dispose();
                _decryptor.Dispose();
            }
        }
        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];
                }
            }
            public void Dispose()
            {
                _aes.Dispose();
                _encryptor.Dispose();
            }
        }
        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()
        {
            (_impl as IDisposable)?.Dispose();
        }
    }
}