|  | @@ -1,8 +1,6 @@
 | 
	
		
			
				|  |  | -#if NET6_0_OR_GREATER
 | 
	
		
			
				|  |  | -using System;
 | 
	
		
			
				|  |  | +using System;
 | 
	
		
			
				|  |  |  using System.Buffers.Binary;
 | 
	
		
			
				|  |  |  using System.Diagnostics;
 | 
	
		
			
				|  |  | -using System.Security.Cryptography;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  using Renci.SshNet.Common;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -12,10 +10,16 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
 | 
	
		
			
				|  |  |      /// AES GCM cipher implementation.
 | 
	
		
			
				|  |  |      /// <see href="https://datatracker.ietf.org/doc/html/rfc5647"/>.
 | 
	
		
			
				|  |  |      /// </summary>
 | 
	
		
			
				|  |  | -    internal sealed class AesGcmCipher : SymmetricCipher, IDisposable
 | 
	
		
			
				|  |  | +    internal sealed partial class AesGcmCipher : SymmetricCipher, IDisposable
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | +        private const int PacketLengthFieldLength = 4;
 | 
	
		
			
				|  |  | +        private const int TagSizeInBytes = 16;
 | 
	
		
			
				|  |  |          private readonly byte[] _iv;
 | 
	
		
			
				|  |  | -        private readonly AesGcm _aesGcm;
 | 
	
		
			
				|  |  | +#if NET6_0_OR_GREATER
 | 
	
		
			
				|  |  | +        private readonly Impl _impl;
 | 
	
		
			
				|  |  | +#else
 | 
	
		
			
				|  |  | +        private readonly BouncyCastleImpl _impl;
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// Gets the minimun block size.
 | 
	
	
		
			
				|  | @@ -42,7 +46,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              get
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                return 16;
 | 
	
		
			
				|  |  | +                return TagSizeInBytes;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -56,11 +60,16 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              // SSH AES-GCM requires a 12-octet Initial IV
 | 
	
		
			
				|  |  |              _iv = iv.Take(12);
 | 
	
		
			
				|  |  | -#if NET8_0_OR_GREATER
 | 
	
		
			
				|  |  | -            _aesGcm = new AesGcm(key, TagSize);
 | 
	
		
			
				|  |  | -#else
 | 
	
		
			
				|  |  | -            _aesGcm = new AesGcm(key);
 | 
	
		
			
				|  |  | +#if NET6_0_OR_GREATER
 | 
	
		
			
				|  |  | +            if (System.Security.Cryptography.AesGcm.IsSupported)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                _impl = new BclImpl(key, _iv);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            else
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                _impl = new BouncyCastleImpl(key, _iv);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
	
		
			
				|  | @@ -84,15 +93,17 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
 | 
	
		
			
				|  |  |          /// </returns>
 | 
	
		
			
				|  |  |          public override byte[] Encrypt(byte[] input, int offset, int length)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            var packetLengthField = new ReadOnlySpan<byte>(input, offset, 4);
 | 
	
		
			
				|  |  | -            var plainText = new ReadOnlySpan<byte>(input, offset + 4, length - 4);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |              var output = new byte[length + TagSize];
 | 
	
		
			
				|  |  | -            packetLengthField.CopyTo(output);
 | 
	
		
			
				|  |  | -            var cipherText = new Span<byte>(output, 4, length - 4);
 | 
	
		
			
				|  |  | -            var tag = new Span<byte>(output, length, TagSize);
 | 
	
		
			
				|  |  | +            Buffer.BlockCopy(input, offset, output, 0, PacketLengthFieldLength);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            _aesGcm.Encrypt(nonce: _iv, plainText, cipherText, tag, associatedData: packetLengthField);
 | 
	
		
			
				|  |  | +            _impl.Encrypt(
 | 
	
		
			
				|  |  | +                input,
 | 
	
		
			
				|  |  | +                plainTextOffset: offset + PacketLengthFieldLength,
 | 
	
		
			
				|  |  | +                plainTextLength: length - PacketLengthFieldLength,
 | 
	
		
			
				|  |  | +                associatedDataOffset: offset,
 | 
	
		
			
				|  |  | +                associatedDataLength: PacketLengthFieldLength,
 | 
	
		
			
				|  |  | +                output,
 | 
	
		
			
				|  |  | +                cipherTextOffset: PacketLengthFieldLength);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              IncrementCounter();
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -122,14 +133,16 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              Debug.Assert(offset == 8, "The offset must be 8");
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            var packetLengthField = new ReadOnlySpan<byte>(input, 4, 4);
 | 
	
		
			
				|  |  | -            var cipherText = new ReadOnlySpan<byte>(input, offset, length);
 | 
	
		
			
				|  |  | -            var tag = new ReadOnlySpan<byte>(input, offset + length, TagSize);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |              var output = new byte[length];
 | 
	
		
			
				|  |  | -            var plainText = new Span<byte>(output);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            _aesGcm.Decrypt(nonce: _iv, cipherText, tag, plainText, associatedData: packetLengthField);
 | 
	
		
			
				|  |  | +            _impl.Decrypt(
 | 
	
		
			
				|  |  | +                input,
 | 
	
		
			
				|  |  | +                cipherTextOffset: offset,
 | 
	
		
			
				|  |  | +                cipherTextLength: length,
 | 
	
		
			
				|  |  | +                associatedDataOffset: offset - PacketLengthFieldLength,
 | 
	
		
			
				|  |  | +                associatedDataLength: PacketLengthFieldLength,
 | 
	
		
			
				|  |  | +                output,
 | 
	
		
			
				|  |  | +                plainTextOffset: 0);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              IncrementCounter();
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -158,7 +171,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              if (disposing)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                _aesGcm.Dispose();
 | 
	
		
			
				|  |  | +                _impl.Dispose();
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -169,6 +182,23 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
 | 
	
		
			
				|  |  |              Dispose(disposing: true);
 | 
	
		
			
				|  |  |              GC.SuppressFinalize(this);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        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()
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
 | 
	
		
			
				|  |  | +                Dispose(disposing: true);
 | 
	
		
			
				|  |  | +                GC.SuppressFinalize(this);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | -#endif
 |