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