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);
}
}
}