GcmAuthenticatedCryptographicTransform
using Azure.Storage.Cryptography.Models;
using System;
using System.Security.Cryptography;
namespace Azure.Storage.Cryptography
{
    internal class GcmAuthenticatedCryptographicTransform : IAuthenticatedCryptographicTransform, IDisposable
    {
        private AesGcm _gcm;
        private long _nonceCounter = 1;
        public TransformMode TransformMode { get; }
        public int NonceLength => 12;
        public int TagLength => 16;
        public GcmAuthenticatedCryptographicTransform(byte[] key, TransformMode mode)
        {
            TransformMode = mode;
            _gcm = new AesGcm(key);
        }
        public int TransformAuthenticationBlock(ReadOnlySpan<byte> input, Span<byte> output)
        {
            switch (TransformMode) {
            case TransformMode.Encrypt: {
                ReadOnlySpan<byte> newNonce = GetNewNonce();
                Span<byte> tag = new Span<byte>(new byte[TagLength]);
                newNonce.CopyTo(output.Slice(0, NonceLength));
                _gcm.Encrypt(newNonce, input, output.Slice(NonceLength, input.Length), tag, default(ReadOnlySpan<byte>));
                tag.CopyTo(output.Slice(NonceLength + input.Length, TagLength));
                return NonceLength + input.Length + TagLength;
            }
            case TransformMode.Decrypt: {
                int length = input.Length - NonceLength - TagLength;
                _gcm.Decrypt(input.Slice(0, NonceLength), input.Slice(NonceLength, length), input.Slice(input.Length - TagLength, TagLength), output.Slice(0, length), default(ReadOnlySpan<byte>));
                return input.Length - NonceLength - TagLength;
            }
            default:
                throw new InvalidOperationException("TransformMode invalid for this operation.");
            }
        }
        public void Dispose()
        {
            _gcm.Dispose();
        }
        private ReadOnlySpan<byte> GetNewNonce()
        {
            Span<byte> span = new Span<byte>(new byte[NonceLength]);
            int num = NonceLength - 8;
            new byte[4].CopyTo(span.Slice(0, num));
            BitConverter.GetBytes(_nonceCounter++).CopyTo(span.Slice(num, 8));
            return span;
        }
    }
}