AesGcmCipher
AES GCM cipher implementation.
.
using Renci.SshNet.Common;
using System;
using System.Buffers.Binary;
using System.Security.Cryptography;
namespace Renci.SshNet.Security.Cryptography.Ciphers
{
internal sealed class AesGcmCipher : SymmetricCipher, IDisposable
{
private readonly byte[] _iv;
private readonly AesGcm _aesGcm;
public override byte MinimumSize => 16;
public override int TagSize => 16;
public AesGcmCipher(byte[] key, byte[] iv)
: base(key)
{
_iv = iv.Take(12);
_aesGcm = new AesGcm(key, TagSize);
}
public override byte[] Encrypt(byte[] input, int offset, int length)
{
ReadOnlySpan<byte> associatedData = new ReadOnlySpan<byte>(input, offset, 4);
ReadOnlySpan<byte> plaintext = new ReadOnlySpan<byte>(input, offset + 4, length - 4);
byte[] array = new byte[length + TagSize];
associatedData.CopyTo(array);
Span<byte> ciphertext = new Span<byte>(array, 4, length - 4);
Span<byte> tag = new Span<byte>(array, length, TagSize);
_aesGcm.Encrypt(_iv, plaintext, ciphertext, tag, associatedData);
IncrementCounter();
return array;
}
public override byte[] Decrypt(byte[] input, int offset, int length)
{
ReadOnlySpan<byte> associatedData = new ReadOnlySpan<byte>(input, 4, 4);
ReadOnlySpan<byte> ciphertext = new ReadOnlySpan<byte>(input, offset, length);
ReadOnlySpan<byte> tag = new ReadOnlySpan<byte>(input, offset + length, TagSize);
byte[] array = new byte[length];
Span<byte> plaintext = new Span<byte>(array);
_aesGcm.Decrypt(_iv, ciphertext, tag, plaintext, associatedData);
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)
_aesGcm.Dispose();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}