1

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&lt;byte&gt;();
    }
    catch (ArgumentNullException ex)
    {
        Array.Clear(password, 0, password.Length);
        ErrorLogging.ErrorLog(ex);
        return Array.Empty&lt;byte&gt;();
    }
    catch (Exception ex)
    {
        Array.Clear(password!, 0, password!.Length);
        ErrorLogging.ErrorLog(ex);
        return Array.Empty&lt;byte&gt;();
    }
}

public static async Task&lt;byte[]&gt; DecryptAsyncV2(byte[] cipherText, byte[] password, byte[] salt)
{
    try
    {
        if (cipherText == null)
            throw new ArgumentException(@&quot;Value was empty or null.&quot;, nameof(cipherText));
        if (password == null)
            throw new ArgumentException(@&quot;Value was empty or null.&quot;, nameof(password));
        if (salt == null)
            throw new ArgumentException(@&quot;Value was empty or null.&quot;, 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&lt;byte&gt;();
    }
    catch (ArgumentNullException ex)
    {
        Array.Clear(password, 0, password.Length);
        ErrorLogging.ErrorLog(ex);
        return Array.Empty&lt;byte&gt;();
    }
    catch (Exception ex)
    {
        Array.Clear(password!, 0, password!.Length);
        ErrorLogging.ErrorLog(ex);
        return Array.Empty&lt;byte&gt;();
    }

#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;
    }
  • Start from here. What are the rules for using AES-GCM correctly?. You may choose AES-GCM-SIV to reduce the IV reuse problem under the same key. Note that it is better if you write down you requirements instead of asking one by one! – kelalaka Oct 30 '23 at 20:12
  • My requirements are basically I’m looking for the most secure encryption class. I’ve seen about gcm-siv but I don’t think I’ve seen any implementation of it in .net – Bismofunyuns Oct 30 '23 at 20:27
  • There is no such option. AFAIK, for such an application you can live with AES-GCM or xChaCha20-Poly1305 which has 192-bit nonce that you can use random nonces for a long time. – kelalaka Oct 30 '23 at 20:33
  • What do you think about aes-cbc-hmac? – Bismofunyuns Oct 30 '23 at 20:35
  • I wanted to use xchacha20 but I could not figure out how to get it working. I was getting encryption errors, I wasn’t sure how to get the nonce and tag to append to the ciphertext – Bismofunyuns Oct 30 '23 at 20:37
  • You can use that, too, however your risk and requirement are not clear! For the programming issues ask at [so] with proper tags. – kelalaka Oct 30 '23 at 20:38
  • I’ve posted there and my questions keep getting closed due to them being “opinionated”. I’m basically looking for the most secure method of encryption with the least amount of vulnerabilities – Bismofunyuns Oct 30 '23 at 20:46
  • We are not reviewing codes here. There is [so] and codereview for that purposes. – kelalaka Oct 30 '23 at 20:48
  • Like I said, stack overflow keeps closing my questions on bs grounds. Looks like asking here was equally as useless, I’ll try codereview. If I can’t get help there then I won’t bother anymore – Bismofunyuns Oct 30 '23 at 20:58
  • Remember that Codereview requires running code not a place to find errors etc. – kelalaka Oct 30 '23 at 21:02
  • "most secure" is not universal. Security requires confidentiality, integrity, and availability. There are lots of cases where protocol design means AES-GCM is secure. There are other cases where it's not, e.g. where unique IVs can't be reasonably guaranteed. XChaCha20-Poly1305 is secure in more cases, though still not all (you can't publish the key publicly, for example). But XChaCha20-Poly1305 is slower than AES-GCM on many systems, so the "availability" portion of security is reduced (easier DOS attack). Without the requirements security cannot be judged. – SAI Peregrinus Oct 30 '23 at 22:51
  • I've actually asked this question myself before that one, all the way back in 2013. – Maarten Bodewes Nov 04 '23 at 00:57

0 Answers0