|
@@ -1,8 +1,6 @@
|
|
|
-#if NET6_0_OR_GREATER
|
|
|
|
|
-using System;
|
|
|
|
|
|
|
+using System;
|
|
|
using System.Buffers.Binary;
|
|
using System.Buffers.Binary;
|
|
|
using System.Diagnostics;
|
|
using System.Diagnostics;
|
|
|
-using System.Security.Cryptography;
|
|
|
|
|
|
|
|
|
|
using Renci.SshNet.Common;
|
|
using Renci.SshNet.Common;
|
|
|
|
|
|
|
@@ -12,10 +10,16 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
|
|
|
/// AES GCM cipher implementation.
|
|
/// AES GCM cipher implementation.
|
|
|
/// <see href="https://datatracker.ietf.org/doc/html/rfc5647"/>.
|
|
/// <see href="https://datatracker.ietf.org/doc/html/rfc5647"/>.
|
|
|
/// </summary>
|
|
/// </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 byte[] _iv;
|
|
|
- private readonly AesGcm _aesGcm;
|
|
|
|
|
|
|
+#if NET6_0_OR_GREATER
|
|
|
|
|
+ private readonly Impl _impl;
|
|
|
|
|
+#else
|
|
|
|
|
+ private readonly BouncyCastleImpl _impl;
|
|
|
|
|
+#endif
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// Gets the minimun block size.
|
|
/// Gets the minimun block size.
|
|
@@ -42,7 +46,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
|
|
|
{
|
|
{
|
|
|
get
|
|
get
|
|
|
{
|
|
{
|
|
|
- return 16;
|
|
|
|
|
|
|
+ return TagSizeInBytes;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -56,11 +60,16 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
|
|
|
{
|
|
{
|
|
|
// SSH AES-GCM requires a 12-octet Initial IV
|
|
// SSH AES-GCM requires a 12-octet Initial IV
|
|
|
_iv = iv.Take(12);
|
|
_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
|
|
#endif
|
|
|
|
|
+ {
|
|
|
|
|
+ _impl = new BouncyCastleImpl(key, _iv);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -84,15 +93,17 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
|
|
|
/// </returns>
|
|
/// </returns>
|
|
|
public override byte[] Encrypt(byte[] input, int offset, int length)
|
|
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];
|
|
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();
|
|
IncrementCounter();
|
|
|
|
|
|
|
@@ -122,14 +133,16 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
|
|
|
{
|
|
{
|
|
|
Debug.Assert(offset == 8, "The offset must be 8");
|
|
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 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();
|
|
IncrementCounter();
|
|
|
|
|
|
|
@@ -158,7 +171,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
|
|
|
{
|
|
{
|
|
|
if (disposing)
|
|
if (disposing)
|
|
|
{
|
|
{
|
|
|
- _aesGcm.Dispose();
|
|
|
|
|
|
|
+ _impl.Dispose();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -169,6 +182,23 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
|
|
|
Dispose(disposing: true);
|
|
Dispose(disposing: true);
|
|
|
GC.SuppressFinalize(this);
|
|
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
|
|
|