<PackageReference Include="SSH.NET" Version="2024.2.0" />

AesGcmCipher

AES GCM cipher implementation. .
using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Parameters; using Renci.SshNet.Common; using Renci.SshNet.Messages.Transport; using System; using System.Buffers.Binary; namespace Renci.SshNet.Security.Cryptography.Ciphers { internal sealed class AesGcmCipher : SymmetricCipher, IDisposable { private sealed class BouncyCastleImpl : Impl { private readonly GcmBlockCipher _cipher; private readonly AeadParameters _parameters; public BouncyCastleImpl(byte[] key, byte[] nonce) { _cipher = new GcmBlockCipher(new AesEngine()); _parameters = new AeadParameters(new KeyParameter(key), 128, nonce); } public override void Encrypt(byte[] input, int plainTextOffset, int plainTextLength, int associatedDataOffset, int associatedDataLength, byte[] output, int cipherTextOffset) { _cipher.Init(true, _parameters); _cipher.ProcessAadBytes(input, associatedDataOffset, associatedDataLength); int num = _cipher.ProcessBytes(input, plainTextOffset, plainTextLength, output, cipherTextOffset); _cipher.DoFinal(output, cipherTextOffset + num); } public override void Decrypt(byte[] input, int cipherTextOffset, int cipherTextLength, int associatedDataOffset, int associatedDataLength, byte[] output, int plainTextOffset) { _cipher.Init(false, _parameters); _cipher.ProcessAadBytes(input, associatedDataOffset, associatedDataLength); int outOff = _cipher.ProcessBytes(input, cipherTextOffset, cipherTextLength + 16, output, plainTextOffset); try { _cipher.DoFinal(output, outOff); } catch (InvalidCipherTextException inner) { throw new SshConnectionException("MAC error", DisconnectReason.MacError, inner); } } } private abstract class Impl : IDisposable { public abstract void Encrypt(byte[] input, int plainTextOffset, int plainTextLength, int associatedDataOffset, int associatedDataLength, byte[] output, int cipherTextOffset); public abstract void Decrypt(byte[] input, int cipherTextOffset, int cipherTextLength, int associatedDataOffset, int associatedDataLength, byte[] output, int plainTextOffset); protected virtual void Dispose(bool disposing) { } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } } private const int TagSizeInBytes = 16; private readonly byte[] _iv; private readonly int _aadLength; private readonly BouncyCastleImpl _impl; public override byte MinimumSize => 16; public override int TagSize => 16; public AesGcmCipher(byte[] key, byte[] iv, int aadLength) : base(key) { _iv = iv.Take(12); _aadLength = aadLength; _impl = new BouncyCastleImpl(key, _iv); } public override byte[] Encrypt(byte[] input, int offset, int length) { byte[] array = new byte[length + TagSize]; Buffer.BlockCopy(input, offset, array, 0, _aadLength); _impl.Encrypt(input, offset + _aadLength, length - _aadLength, offset, _aadLength, array, _aadLength); IncrementCounter(); return array; } public override byte[] Decrypt(byte[] input, int offset, int length) { byte[] array = new byte[length]; _impl.Decrypt(input, offset, length, offset - _aadLength, _aadLength, array, 0); IncrementCounter(); return array; } private void IncrementCounter() { Span<byte> span = new Span<byte>(_iv, 4, 8); ulong num = BinaryPrimitives.ReadUInt64BigEndian(span); BinaryPrimitives.WriteUInt64BigEndian(span, num + 1); } public void Dispose(bool disposing) { if (disposing) _impl.Dispose(); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } } }