Besides the limited nonce size of 96 bits, are there any other weaknesses to the GCM mode of AES?
I am creating a password vault and am trying to narrow down my options of encryption schemes.
Also, is this a safe and secure implementation of AES-GCM?
public static async Task<byte[]> EncryptAsyncV2(byte[] plainText, byte[] password, byte[] nonce, byte[] salt)
{
try
{
if (plainText == null)
throw new ArgumentException(@"Value was empty or null.", nameof(plainText));
if (password == null)
throw new ArgumentException(@"Value was empty or null.", nameof(password));
if (salt == null)
throw new ArgumentException(@"Value was empty or null.", nameof(salt));
if (nonce == null)
throw new ArgumentException(@"Value was empty or null.", nameof(nonce));
var cipherText = new byte[plainText.Length];
var tag = new byte[TagLen];
using (var argon2 = new Argon2id(password))
{
argon2.Salt = salt;
argon2.DegreeOfParallelism = Environment.ProcessorCount * 2;
argon2.Iterations = Iterations;
argon2.MemorySize = (int)MemorySize;
var key = await argon2.GetBytesAsync(KeySize);
using var aesGcm = new AesGcm(key, TagLen);
aesGcm.Encrypt(nonce, plainText, cipherText, tag);
Array.Clear(key, 0, key.Length);
}
cipherText = tag.Concat(nonce.Concat(cipherText)).ToArray();
return cipherText;
}
catch (CryptographicException ex)
{
Array.Clear(password, 0, password.Length);
ErrorLogging.ErrorLog(ex);
return Array.Empty<byte>();
}
catch (ArgumentNullException ex)
{
Array.Clear(password, 0, password.Length);
ErrorLogging.ErrorLog(ex);
return Array.Empty<byte>();
}
catch (Exception ex)
{
Array.Clear(password!, 0, password!.Length);
ErrorLogging.ErrorLog(ex);
return Array.Empty<byte>();
}
}
public static async Task<byte[]> DecryptAsyncV2(byte[] cipherText, byte[] password, byte[] salt)
{
try
{
if (cipherText == null)
throw new ArgumentException(@"Value was empty or null.", nameof(cipherText));
if (password == null)
throw new ArgumentException(@"Value was empty or null.", nameof(password));
if (salt == null)
throw new ArgumentException(@"Value was empty or null.", nameof(salt));
using var argon2 = new Argon2id(password);
argon2.Salt = salt;
argon2.DegreeOfParallelism = Environment.ProcessorCount * 2;
argon2.Iterations = Iterations;
argon2.MemorySize = (int)MemorySize;
var key = await argon2.GetBytesAsync(KeySize);
using var aesGcm = new AesGcm(key, TagLen);
var tag = new byte[TagLen];
var nonce = new byte[NonceSize];
var cipherResult = new byte[cipherText.Length - nonce.Length - tag.Length];
Buffer.BlockCopy(cipherText, 0, tag, 0, tag.Length);
Buffer.BlockCopy(cipherText, tag.Length, nonce, 0, nonce.Length);
Buffer.BlockCopy(cipherText, tag.Length + nonce.Length, cipherResult, 0, cipherResult.Length);
var plainText = new byte[cipherResult.Length];
aesGcm.Decrypt(nonce, cipherResult, tag, plainText);
Array.Clear(key, 0, key.Length);
return plainText;
}
catch (CryptographicException ex)
{
Array.Clear(password, 0, password.Length);
ErrorLogging.ErrorLog(ex);
return Array.Empty<byte>();
}
catch (ArgumentNullException ex)
{
Array.Clear(password, 0, password.Length);
ErrorLogging.ErrorLog(ex);
return Array.Empty<byte>();
}
catch (Exception ex)
{
Array.Clear(password!, 0, password!.Length);
ErrorLogging.ErrorLog(ex);
return Array.Empty<byte>();
}
#pragma warning restore
}
The nonce is generated with the RandomNumberGenerator class using this method
public static byte[] RndByteSized(int size)
{
var buffer = new byte[size];
RndNum.GetBytes(buffer);
return buffer;
}