Browse Source

Merge pull request #5170 from haydenroche5/evp_cipher_aes_gcm

Fix EVP_CTRL_GCM_IV_GEN with AES-GCM.
Sean Parkinson 2 years ago
parent
commit
81cd1e652e
3 changed files with 609 additions and 245 deletions
  1. 268 16
      tests/api.c
  2. 337 229
      wolfcrypt/src/evp.c
  3. 4 0
      wolfssl/openssl/evp.h

+ 268 - 16
tests/api.c

@@ -45185,27 +45185,279 @@ static void test_wolfSSL_EVP_BytesToKey(void)
     printf(resultFmt, passed);
 #endif
 }
-static void test_IncCtr(void)
+static void test_evp_cipher_aes_gcm(void)
 {
-#if defined(HAVE_AESGCM) && !defined(HAVE_FIPS)
-    byte key[AES_128_KEY_SIZE] = {0};
-    byte iv[GCM_NONCE_MID_SZ] = {0};
-    int type = EVP_CTRL_GCM_IV_GEN;
-    int arg = 0;
-    void *ptr = NULL;
+#if defined(HAVE_AESGCM) && ((!defined(HAVE_FIPS) && \
+    !defined(HAVE_SELFTEST)) || (defined(HAVE_FIPS_VERSION) && \
+    (HAVE_FIPS_VERSION >= 2)))
+    /*
+     * This test checks data at various points in the encrypt/decrypt process
+     * against known values produced using the same test with OpenSSL. This
+     * interop testing is critical for verifying the correctness of our
+     * EVP_Cipher implementation with AES-GCM. Specifically, this test exercises
+     * a flow supported by OpenSSL that uses the control command
+     * EVP_CTRL_GCM_IV_GEN to increment the IV between cipher operations without
+     * the need to call EVP_CipherInit. OpenSSH uses this flow, for example. We
+     * had a bug with OpenSSH where wolfSSL OpenSSH servers could only talk to
+     * wolfSSL OpenSSH clients because there was a bug in this flow that
+     * happened to "cancel out" if both sides of the connection had the bug.
+     */
+    enum { 
+        NUM_ENCRYPTIONS = 3,
+        AAD_SIZE = 4
+    };
+    byte plainText1[] = {
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+        0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+        0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23
+    };
+    byte plainText2[] = {
+        0x42, 0x49, 0x3b, 0x27, 0x03, 0x35, 0x59, 0x14, 0x41, 0x47, 0x37, 0x14,
+        0x0e, 0x34, 0x0d, 0x28, 0x63, 0x09, 0x0a, 0x5b, 0x22, 0x57, 0x42, 0x22,
+        0x0f, 0x5c, 0x1e, 0x53, 0x45, 0x15, 0x62, 0x08, 0x60, 0x43, 0x50, 0x2c
+    };
+    byte plainText3[] = {
+        0x36, 0x0d, 0x2b, 0x09, 0x4a, 0x56, 0x3b, 0x4c, 0x21, 0x22, 0x58, 0x0e,
+        0x5b, 0x57, 0x10
+    };
+    byte* plainTexts[NUM_ENCRYPTIONS] = {
+        plainText1,
+        plainText2,
+        plainText3
+    };
+    const int plainTextSzs[NUM_ENCRYPTIONS] = {
+        sizeof(plainText1),
+        sizeof(plainText2),
+        sizeof(plainText3)
+    };
+    byte aad1[AAD_SIZE] = {
+        0x00, 0x00, 0x00, 0x01
+    };
+    byte aad2[AAD_SIZE] = {
+        0x00, 0x00, 0x00, 0x10
+    };
+    byte aad3[AAD_SIZE] = {
+        0x00, 0x00, 0x01, 0x00
+    };
+    byte* aads[NUM_ENCRYPTIONS] = {
+        aad1,
+        aad2,
+        aad3
+    };
+    const byte iv[GCM_NONCE_MID_SZ] = {
+        0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF
+    };
+    byte currentIv[GCM_NONCE_MID_SZ];
+    const byte key[] = {
+        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
+        0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+        0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f
+    };
+    const byte expIvs[NUM_ENCRYPTIONS][GCM_NONCE_MID_SZ] = {
+        {
+            0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE,
+            0xEF
+        },
+        {
+            0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE,
+            0xF0
+        },
+        {
+            0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE,
+            0xF1
+        }
+    };
+    const byte expTags[NUM_ENCRYPTIONS][AES_BLOCK_SIZE] = {
+        {
+            0x65, 0x4F, 0xF7, 0xA0, 0xBB, 0x7B, 0x90, 0xB7, 0x9C, 0xC8, 0x14,
+            0x3D, 0x32, 0x18, 0x34, 0xA9
+        },
+        {
+            0x50, 0x3A, 0x13, 0x8D, 0x91, 0x1D, 0xEC, 0xBB, 0xBA, 0x5B, 0x57,
+            0xA2, 0xFD, 0x2D, 0x6B, 0x7F
+        },
+        {
+            0x3B, 0xED, 0x18, 0x9C, 0xB3, 0xE3, 0x61, 0x1E, 0x11, 0xEB, 0x13,
+            0x5B, 0xEC, 0x52, 0x49, 0x32, 
+        }
+    };
 
-    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
-    const EVP_CIPHER *init = EVP_aes_128_gcm();
+    const byte expCipherText1[] = {
+        0xCB, 0x93, 0x4F, 0xC8, 0x22, 0xE2, 0xC0, 0x35, 0xAA, 0x6B, 0x41, 0x15,
+        0x17, 0x30, 0x2F, 0x97, 0x20, 0x74, 0x39, 0x28, 0xF8, 0xEB, 0xC5, 0x51,
+        0x7B, 0xD9, 0x8A, 0x36, 0xB8, 0xDA, 0x24, 0x80, 0xE7, 0x9E, 0x09, 0xDE
+    };
+    const byte expCipherText2[] = {
+        0xF9, 0x32, 0xE1, 0x87, 0x37, 0x0F, 0x04, 0xC1, 0xB5, 0x59, 0xF0, 0x45,
+        0x3A, 0x0D, 0xA0, 0x26, 0xFF, 0xA6, 0x8D, 0x38, 0xFE, 0xB8, 0xE5, 0xC2,
+        0x2A, 0x98, 0x4A, 0x54, 0x8F, 0x1F, 0xD6, 0x13, 0x03, 0xB2, 0x1B, 0xC0
+    };
+    const byte expCipherText3[] = {
+        0xD0, 0x37, 0x59, 0x1C, 0x2F, 0x85, 0x39, 0x4D, 0xED, 0xC2, 0x32, 0x5B,
+        0x80, 0x5E, 0x6B, 
+    };
+    const byte* expCipherTexts[NUM_ENCRYPTIONS] = {
+        expCipherText1,
+        expCipherText2,
+        expCipherText3
+    };
+    byte* cipherText;
+    byte* calcPlainText;
+    byte tag[AES_BLOCK_SIZE];
+    EVP_CIPHER_CTX* encCtx = NULL;
+    EVP_CIPHER_CTX* decCtx = NULL;
+    int i, j, outl;
 
-    printf(testingFmt, "IncCtr");
+    printf(testingFmt, "test_evp_cipher_aes_gcm");
 
-    AssertNotNull(ctx);
-    wolfSSL_EVP_CIPHER_CTX_init(ctx);
-    AssertIntEQ(EVP_CipherInit(ctx, init, key, iv, 1), WOLFSSL_SUCCESS);
+    /****************************************************/
+    for (i = 0; i < 3; ++i) {
+        AssertNotNull(encCtx = EVP_CIPHER_CTX_new());
+        AssertNotNull(decCtx = EVP_CIPHER_CTX_new());
 
-    AssertIntEQ(wolfSSL_EVP_CIPHER_CTX_ctrl(ctx, type, arg, ptr), WOLFSSL_SUCCESS);
+        /* First iteration, set key before IV. */
+        if (i == 0) {
+            AssertIntEQ(EVP_CipherInit(encCtx, EVP_aes_256_gcm(), key, NULL, 1),
+                        SSL_SUCCESS);
+            AssertIntEQ(EVP_CipherInit(encCtx, NULL, NULL, iv, 1),
+                        SSL_SUCCESS);
+            AssertIntEQ(EVP_CipherInit(decCtx, EVP_aes_256_gcm(), key, NULL, 0),
+                        SSL_SUCCESS);
+            AssertIntEQ(EVP_CipherInit(decCtx, NULL, NULL, iv, 0),
+                        SSL_SUCCESS);
+        }
+        /* Second iteration, IV before key. */
+        else {
+            AssertIntEQ(EVP_CipherInit(encCtx, EVP_aes_256_gcm(), NULL, iv, 1),
+                        SSL_SUCCESS);
+            AssertIntEQ(EVP_CipherInit(encCtx, NULL, key, NULL, 1),
+                        SSL_SUCCESS);
+            AssertIntEQ(EVP_CipherInit(decCtx, EVP_aes_256_gcm(), NULL, iv, 0),
+                        SSL_SUCCESS);
+            AssertIntEQ(EVP_CipherInit(decCtx, NULL, key, NULL, 0),
+                        SSL_SUCCESS);
+        }
 
-    EVP_CIPHER_CTX_free(ctx);
+        /*
+         * EVP_CTRL_GCM_IV_GEN should fail if EVP_CTRL_GCM_SET_IV_FIXED hasn't
+         * been issued first.
+         */
+        AssertIntEQ(EVP_CIPHER_CTX_ctrl(encCtx, EVP_CTRL_GCM_IV_GEN, -1,
+                        currentIv), SSL_FAILURE);
+
+        AssertIntEQ(EVP_CIPHER_CTX_ctrl(encCtx, EVP_CTRL_GCM_SET_IV_FIXED, -1,
+                    (void*)iv), SSL_SUCCESS);
+        AssertIntEQ(EVP_CIPHER_CTX_ctrl(decCtx, EVP_CTRL_GCM_SET_IV_FIXED, -1,
+                    (void*)iv), SSL_SUCCESS);
+
+        for (j = 0; j < NUM_ENCRYPTIONS; ++j) {
+            /*************** Encrypt ***************/
+            AssertIntEQ(EVP_CIPHER_CTX_ctrl(encCtx, EVP_CTRL_GCM_IV_GEN, -1,
+                        currentIv), SSL_SUCCESS);
+            /* Check current IV against expected. */
+            AssertIntEQ(XMEMCMP(currentIv, expIvs[j], GCM_NONCE_MID_SZ), 0);
+
+            /* Add AAD. */
+            if (i == 2) {
+                /* Test streaming API. */
+                AssertIntEQ(EVP_CipherUpdate(encCtx, NULL, &outl, aads[j],
+                                             AAD_SIZE), SSL_SUCCESS);
+            }
+            else {
+                AssertIntEQ(EVP_Cipher(encCtx, NULL, aads[j], AAD_SIZE),
+                                       AAD_SIZE);
+            }
+
+            AssertNotNull(cipherText = (byte*)XMALLOC(plainTextSzs[j], NULL,
+                          DYNAMIC_TYPE_TMP_BUFFER));
+
+            /* Encrypt plaintext. */
+            if (i == 2){
+                AssertIntEQ(EVP_CipherUpdate(encCtx, cipherText, &outl,
+                                             plainTexts[j], plainTextSzs[j]),
+                            SSL_SUCCESS);
+            }
+            else {
+                AssertIntEQ(EVP_Cipher(encCtx, cipherText, plainTexts[j],
+                            plainTextSzs[j]), plainTextSzs[j]);
+            }
+
+            if (i == 2) {
+                AssertIntEQ(EVP_CipherFinal(encCtx, cipherText, &outl),
+                            SSL_SUCCESS);
+            }
+            else {
+                /*
+                 * Calling EVP_Cipher with NULL input and output for AES-GCM is
+                 * akin to calling EVP_CipherFinal.
+                 */
+                AssertIntGE(EVP_Cipher(encCtx, NULL, NULL, 0), 0);
+            }
+
+            /* Check ciphertext against expected. */
+            AssertIntEQ(XMEMCMP(cipherText, expCipherTexts[j], plainTextSzs[j]),
+                        0);
+
+            /* Get and check tag against expected. */
+            AssertIntEQ(EVP_CIPHER_CTX_ctrl(encCtx, EVP_CTRL_GCM_GET_TAG,
+                        sizeof(tag), tag), SSL_SUCCESS);
+            AssertIntEQ(XMEMCMP(tag, expTags[j], sizeof(tag)), 0);
+
+            /*************** Decrypt ***************/
+            AssertIntEQ(EVP_CIPHER_CTX_ctrl(decCtx, EVP_CTRL_GCM_IV_GEN, -1,
+                        currentIv), SSL_SUCCESS);
+            /* Check current IV against expected. */
+            AssertIntEQ(XMEMCMP(currentIv, expIvs[j], GCM_NONCE_MID_SZ), 0);
+
+            /* Add AAD. */
+            if (i == 2) {
+                /* Test streaming API. */
+                AssertIntEQ(EVP_CipherUpdate(decCtx, NULL, &outl, aads[j],
+                                             AAD_SIZE), SSL_SUCCESS);
+            }
+            else {
+                AssertIntEQ(EVP_Cipher(decCtx, NULL, aads[j], AAD_SIZE),
+                            AAD_SIZE);
+            }
+
+            /* Set expected tag. */
+            AssertIntEQ(EVP_CIPHER_CTX_ctrl(decCtx, EVP_CTRL_GCM_SET_TAG,
+                        sizeof(tag), tag), SSL_SUCCESS);
+
+            /* Decrypt ciphertext. */
+            AssertNotNull(calcPlainText = (byte*)XMALLOC(plainTextSzs[j], NULL,
+                          DYNAMIC_TYPE_TMP_BUFFER));
+            if (i == 2){
+                AssertIntEQ(EVP_CipherUpdate(decCtx, calcPlainText, &outl,
+                                             cipherText, plainTextSzs[j]),
+                            SSL_SUCCESS);
+            }
+            else {
+                /* This first EVP_Cipher call will check the tag, too. */
+                AssertIntEQ(EVP_Cipher(decCtx, calcPlainText, cipherText,
+                        plainTextSzs[j]), plainTextSzs[j]);
+            }
+
+            if (i == 2) {
+                AssertIntEQ(EVP_CipherFinal(decCtx, calcPlainText, &outl),
+                            SSL_SUCCESS);
+            }
+            else {
+                AssertIntGE(EVP_Cipher(decCtx, NULL, NULL, 0), 0);
+            }
+
+            /* Check plaintext against expected. */
+            AssertIntEQ(XMEMCMP(calcPlainText, plainTexts[j], plainTextSzs[j]),
+                        0);
+
+            XFREE(cipherText, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+            XFREE(calcPlainText, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        }
+
+        EVP_CIPHER_CTX_free(encCtx);
+        EVP_CIPHER_CTX_free(decCtx);
+    }
+    
     printf(resultFmt, passed);
 #endif
 }
@@ -54485,7 +54737,7 @@ void ApiTest(void)
     test_wolfSSL_EVP_BytesToKey();
     test_wolfSSL_EVP_PKEY_param_check();
     test_wolfSSL_QT_EVP_PKEY_CTX_free();
-    test_IncCtr();
+    test_evp_cipher_aes_gcm();
     test_wolfSSL_OBJ_ln();
     test_wolfSSL_OBJ_sn();
     test_wolfSSL_TXT_DB();

+ 337 - 229
wolfcrypt/src/evp.c

@@ -824,17 +824,36 @@ static int checkPad(WOLFSSL_EVP_CIPHER_CTX *ctx, unsigned char *buff)
     return ctx->block_size - n;
 }
 
-int  wolfSSL_EVP_CipherFinal(WOLFSSL_EVP_CIPHER_CTX *ctx,
-                                   unsigned char *out, int *outl)
+#if defined(HAVE_AESGCM) && ((!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) \
+    || FIPS_VERSION_GE(2,0))
+static WC_INLINE void IncCtr(byte* ctr, word32 ctrSz)
+{
+    int i;
+    for (i = ctrSz-1; i >= 0; i--) {
+        if (++ctr[i])
+            break;
+    }
+}
+#endif /* HAVE_AESGCM && ((!HAVE_FIPS && !HAVE_SELFTEST) ||
+        * HAVE_FIPS_VERSION >= 2 */
+
+int wolfSSL_EVP_CipherFinal(WOLFSSL_EVP_CIPHER_CTX *ctx, unsigned char *out,
+                            int *outl)
 {
     int fl;
     int ret = WOLFSSL_SUCCESS;
+#if defined(HAVE_AESGCM) && ((!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) \
+    || FIPS_VERSION_GE(2,0))
+    byte tmp = 0;
+#endif
+
     if (!ctx || !outl)
         return WOLFSSL_FAILURE;
 
     WOLFSSL_ENTER("wolfSSL_EVP_CipherFinal");
     switch (ctx->cipherType) {
-#if !defined(NO_AES) && defined(HAVE_AESGCM)
+#if defined(HAVE_AESGCM) && ((!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) \
+    || FIPS_VERSION_GE(2,0))
         case AES_128_GCM_TYPE:
         case AES_192_GCM_TYPE:
         case AES_256_GCM_TYPE:
@@ -864,6 +883,12 @@ int  wolfSSL_EVP_CipherFinal(WOLFSSL_EVP_CIPHER_CTX *ctx,
                 XFREE(ctx->gcmBuffer, NULL, DYNAMIC_TYPE_OPENSSL);
                 ctx->gcmBuffer = NULL;
                 ctx->gcmBufferLen = 0;
+
+                if (ctx->gcmIncIv) {
+                    IncCtr((byte*)ctx->cipher.aes.reg,
+                           ctx->cipher.aes.nonceSz);
+                    ctx->gcmIncIv = 0;
+                }
             }
             else {
                 *outl = 0;
@@ -878,18 +903,41 @@ int  wolfSSL_EVP_CipherFinal(WOLFSSL_EVP_CIPHER_CTX *ctx,
             else {
                 ret = wc_AesGcmDecryptFinal(&ctx->cipher.aes, ctx->authTag,
                     ctx->authTagSz);
+                if (ctx->gcmIncIv) {
+                    IncCtr((byte*)ctx->cipher.aes.reg, ctx->cipher.aes.nonceSz);
+                }
             }
             if (ret == 0) {
-                ret = WOLFSSL_SUCCESS;
+                /* Reinitialize for subsequent wolfSSL_EVP_Cipher calls. */
+                if (wc_AesGcmInit(&ctx->cipher.aes, NULL, 0,
+                                  (byte*)ctx->cipher.aes.reg,
+                                  ctx->ivSz) != 0) {
+                    WOLFSSL_MSG("wc_AesGcmInit failed");
+                    ret = WOLFSSL_FAILURE;
+                }
+                else {
+                    ret = WOLFSSL_SUCCESS;
+                }
             }
             else {
                 ret = WOLFSSL_FAILURE;
             }
 #endif /* WOLFSSL_AESGCM_STREAM */
-            /* Clear IV, since IV reuse is not recommended for AES GCM. */
-            XMEMSET(ctx->iv, 0, AES_BLOCK_SIZE);
+            if (ret == WOLFSSL_SUCCESS) {
+                if (ctx->gcmIncIv) {
+                    ctx->gcmIncIv = 0;
+                }
+                else {
+                    /* Clear IV, since IV reuse is not recommended for AES GCM. */
+                    XMEMSET(ctx->iv, 0, AES_BLOCK_SIZE);
+                }
+                if (wolfSSL_StoreExternalIV(ctx) != WOLFSSL_SUCCESS) {
+                    ret = WOLFSSL_FAILURE;
+                }
+            }
             break;
-#endif /* !NO_AES && HAVE_AESGCM */
+#endif /* HAVE_AESGCM && ((!HAVE_FIPS && !HAVE_SELFTEST) ||
+        * HAVE_FIPS_VERSION >= 2 */
 #if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
         case CHACHA20_POLY1305_TYPE:
             if (wc_ChaCha20Poly1305_Final(&ctx->cipher.chachaPoly,
@@ -962,9 +1010,33 @@ int  wolfSSL_EVP_CipherFinal(WOLFSSL_EVP_CIPHER_CTX *ctx,
     }
 
     if (ret == WOLFSSL_SUCCESS) {
+#if defined(HAVE_AESGCM) && ((!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) \
+    || FIPS_VERSION_GE(2,0))
+        /* 
+         * This flag needs to retain its value between wolfSSL_EVP_CipherFinal
+         * calls. wolfSSL_EVP_CipherInit will clear it, so we save and restore
+         * it here.
+         */
+        if (ctx->cipherType == AES_128_GCM_TYPE ||
+            ctx->cipherType == AES_192_GCM_TYPE ||
+            ctx->cipherType == AES_256_GCM_TYPE) {
+            tmp = ctx->gcmIvGenEnable;
+        }
+#endif
+
         /* reset cipher state after final */
         ret = wolfSSL_EVP_CipherInit(ctx, NULL, NULL, NULL, -1);
+
+#if defined(HAVE_AESGCM) && ((!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) \
+    || FIPS_VERSION_GE(2,0))
+        if (ctx->cipherType == AES_128_GCM_TYPE ||
+            ctx->cipherType == AES_192_GCM_TYPE ||
+            ctx->cipherType == AES_256_GCM_TYPE) {
+            ctx->gcmIvGenEnable = tmp;
+        }
+#endif
     }
+
     return ret;
 }
 
@@ -5287,17 +5359,6 @@ int wolfSSL_EVP_MD_type(const WOLFSSL_EVP_MD* type)
         }
     }
 
-#if defined(HAVE_AESGCM) && !defined(HAVE_SELFTEST)
-    static WC_INLINE void IncCtr(byte* ctr, word32 ctrSz)
-    {
-        int i;
-        for (i = ctrSz-1; i >= 0; i--) {
-            if (++ctr[i])
-                break;
-        }
-    }
-#endif
-
     /* This function allows cipher specific parameters to be
     determined and set. */
     int wolfSSL_EVP_CIPHER_CTX_ctrl(WOLFSSL_EVP_CIPHER_CTX *ctx, int type, \
@@ -5381,35 +5442,58 @@ int wolfSSL_EVP_MD_type(const WOLFSSL_EVP_MD* type)
                         break;
                     }
                 }
+            #ifdef HAVE_AESGCM
+                if (ret == WOLFSSL_SUCCESS) {
+                    /* 
+                     * OpenSSL requires that a EVP_CTRL_AEAD_SET_IV_FIXED
+                     * command be issued before a EVP_CTRL_GCM_IV_GEN command.
+                     * This flag is used to enforce that.
+                     */
+                    ctx->gcmIvGenEnable = 1;
+                }
+            #endif
 #endif /* !WC_NO_RNG */
                 break;
 #if defined(HAVE_AESGCM) && !defined(_WIN32) && !defined(HAVE_SELFTEST) && \
     (!defined(HAVE_FIPS) || FIPS_VERSION_GE(2,0))
+            /*
+             * Using EVP_CTRL_GCM_IV_GEN is a way to do AES-GCM encrypt/decrypt
+             * multiple times with EVP_Cipher without having to call
+             * EVP_CipherInit between each iteration. The IV is incremented for
+             * each subsequent EVP_Cipher call to prevent IV reuse.
+             */
             case EVP_CTRL_GCM_IV_GEN:
                 if ((ctx->flags & WOLFSSL_EVP_CIPH_FLAG_AEAD_CIPHER) == 0)
                     break;
+                if (!ctx->gcmIvGenEnable) {
+                    WOLFSSL_MSG("Must use EVP_CTRL_AEAD_SET_IV_FIXED before "
+                                "EVP_CTRL_GCM_IV_GEN");
+                    break;
+                }
                 if (ctx->cipher.aes.keylen == 0 || ctx->ivSz == 0) {
                     WOLFSSL_MSG("Key or IV not set");
                     break;
                 }
-                if (wc_AesGcmSetExtIV(&ctx->cipher.aes, ctx->iv,
-                                                             ctx->ivSz) != 0) {
-                    WOLFSSL_MSG("wc_AesGcmSetIV failed");
+                if (ptr == NULL) {
+                    WOLFSSL_MSG("Destination buffer for IV bytes NULL.");
                     break;
                 }
-#ifdef WOLFSSL_AESGCM_STREAM
-                /* Initialize using IV cached in Aes object. */
-                if (wc_AesGcmInit(&ctx->cipher.aes, NULL, 0, NULL, 0) != 0) {
-                    WOLFSSL_MSG("wc_AesGcmInit failed");
-                    break;
+                if (arg <= 0 || arg > ctx->ivSz) {
+                    XMEMCPY(ptr, ctx->iv, ctx->ivSz);
                 }
-#endif /* WOLFSSL_AESGCM_STREAM */
-                /* OpenSSL increments the IV. Not sure why */
-                IncCtr(ctx->iv, ctx->ivSz);
-                /* Clear any leftover AAD. */
-                if (ctx->gcmAuthIn != NULL)
-                    XMEMSET(ctx->gcmAuthIn, 0, ctx->gcmAuthInSz);
-                ctx->gcmAuthInSz = 0;
+                else {
+                    /*
+                     * Copy the last "arg" bytes of ctx->iv into the buffer at
+                     * "ptr." Not sure why OpenSSL does this, but it does.
+                     */
+                    XMEMCPY(ptr, ctx->iv + ctx->ivSz - arg, arg);
+                }
+
+                /*
+                 * The gcmIncIV flag indicates that the IV should be incremented
+                 * after the next cipher operation.
+                 */
+                ctx->gcmIncIv = 1;
                 ret = WOLFSSL_SUCCESS;
                 break;
 #endif /* HAVE_AESGCM && !_WIN32 && !HAVE_SELFTEST && (!HAVE_FIPS ||
@@ -5498,6 +5582,8 @@ int wolfSSL_EVP_MD_type(const WOLFSSL_EVP_MD* type)
                 ctx->gcmAuthIn = NULL;
             }
             ctx->gcmAuthInSz = 0;
+            ctx->gcmIvGenEnable = 0;
+            ctx->gcmIncIv = 0;
 #endif
         }
 
@@ -5608,8 +5694,190 @@ int wolfSSL_EVP_MD_type(const WOLFSSL_EVP_MD* type)
 #endif /* AES_ANY_SIZE && AES_SET_KEY */
 #endif /* NO_AES */
 
+#if defined(HAVE_AESGCM) && ((!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) \
+    || FIPS_VERSION_GE(2,0))
+    static int EvpCipherInitAesGCM(WOLFSSL_EVP_CIPHER_CTX* ctx,
+                                   const WOLFSSL_EVP_CIPHER* type,
+                                   const byte* key, const byte* iv, int enc)
+    {
+        int ret = WOLFSSL_SUCCESS;
+
+        ctx->block_size = AES_BLOCK_SIZE;
+        ctx->authTagSz = AES_BLOCK_SIZE;
+        if (ctx->ivSz == 0) {
+            ctx->ivSz = GCM_NONCE_MID_SZ;
+        }
+        ctx->flags &= ~WOLFSSL_EVP_CIPH_MODE;
+        ctx->flags |= WOLFSSL_EVP_CIPH_GCM_MODE |
+                      WOLFSSL_EVP_CIPH_FLAG_AEAD_CIPHER;
+        if (enc == 0 || enc == 1) {
+            ctx->enc = enc ? 1 : 0;
+        }
+
+    #ifdef WOLFSSL_AES_128
+        if (ctx->cipherType == AES_128_GCM_TYPE ||
+            (type && EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_128_GCM))) {
+            WOLFSSL_MSG("EVP_AES_128_GCM");
+            ctx->cipherType = AES_128_GCM_TYPE;
+            ctx->keyLen = AES_128_KEY_SIZE;
+        }
+    #endif
+    #ifdef WOLFSSL_AES_192
+        if (ctx->cipherType == AES_192_GCM_TYPE ||
+            (type && EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_192_GCM))) {
+            WOLFSSL_MSG("EVP_AES_192_GCM");
+            ctx->cipherType = AES_192_GCM_TYPE;
+            ctx->keyLen = AES_192_KEY_SIZE;
+        }
+    #endif
+    #ifdef WOLFSSL_AES_256
+        if (ctx->cipherType == AES_256_GCM_TYPE ||
+            (type && EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_256_GCM))) {
+            WOLFSSL_MSG("EVP_AES_256_GCM");
+            ctx->cipherType = AES_256_GCM_TYPE;
+            ctx->keyLen = AES_256_KEY_SIZE;
+        }
+    #endif
+
+    #ifndef WOLFSSL_AESGCM_STREAM
+        if (ret == WOLFSSL_SUCCESS && key &&
+            wc_AesGcmSetKey(&ctx->cipher.aes, key, ctx->keyLen)) {
+            WOLFSSL_MSG("wc_AesGcmSetKey() failed");
+            ret = WOLFSSL_FAILURE;
+        }
+    #endif /* !WOLFSSL_AESGCM_STREAM */
+        if (ret == WOLFSSL_SUCCESS && iv &&
+            wc_AesGcmSetExtIV(&ctx->cipher.aes, iv, ctx->ivSz)) {
+            WOLFSSL_MSG("wc_AesGcmSetExtIV() failed");
+            ret = WOLFSSL_FAILURE;
+        }
+    #ifdef WOLFSSL_AESGCM_STREAM
+        /* 
+         * Initialize with key and IV if available. wc_AesGcmInit will fail
+         * if called with IV only and no key has been set.
+         */
+        if (ret == WOLFSSL_SUCCESS &&
+            (key || (iv && ctx->cipher.aes.gcmKeySet)) &&
+            wc_AesGcmInit(&ctx->cipher.aes, key,
+                          (key == NULL) ? 0 : ctx->keyLen, iv,
+                          (iv == NULL) ? 0 : ctx->ivSz) != 0) {
+            WOLFSSL_MSG("wc_AesGcmInit() failed");
+            ret = WOLFSSL_FAILURE;
+        }
+    #endif /* WOLFSSL_AESGCM_STREAM */
+
+        return ret;
+    }
+
+    static int EvpCipherAesGCM(WOLFSSL_EVP_CIPHER_CTX* ctx, byte* dst,
+                               byte* src, word32 len)
+    {
+        int ret = WOLFSSL_FAILURE;
+
+    #ifndef WOLFSSL_AESGCM_STREAM
+        /* No destination means only AAD. */
+        if (src != NULL && dst == NULL) {
+            ret = wolfSSL_EVP_CipherUpdate_GCM_AAD(ctx, src, len);
+        }
+        else if (src != NULL && dst != NULL) {
+            if (ctx->enc) {
+                ret = wc_AesGcmEncrypt(&ctx->cipher.aes, dst, src,
+                        len, ctx->iv, ctx->ivSz, ctx->authTag,
+                        ctx->authTagSz, ctx->gcmAuthIn,
+                        ctx->gcmAuthInSz);
+            }
+            else {
+                ret = wc_AesGcmDecrypt(&ctx->cipher.aes, dst, src,
+                        len, ctx->iv, ctx->ivSz, ctx->authTag,
+                        ctx->authTagSz, ctx->gcmAuthIn,
+                        ctx->gcmAuthInSz);
+            }
+            if (ctx->gcmIncIv) {
+                IncCtr((byte*)ctx->cipher.aes.reg,
+                       ctx->cipher.aes.nonceSz);
+                ctx->gcmIncIv = 0;
+            }
+        }
+    #else
+        /*
+         * No need to call wc_AesGcmInit. Should have been called by
+         * wolfSSL_EVP_CipherInit.
+         */
+        /* NULL dst and non-NULL src means only AAD. */
+        if (src != NULL && dst == NULL) {
+            if (ctx->enc) {
+                ret = wc_AesGcmEncryptUpdate(&ctx->cipher.aes, NULL,
+                    NULL, 0, src, len);
+            }
+            else {
+                ret = wc_AesGcmDecryptUpdate(&ctx->cipher.aes, NULL,
+                    NULL, 0, src, len);
+            }
+        }
+        /* Only plain/cipher text. */
+        else if (src != NULL && dst != NULL) {
+            if (ctx->enc) {
+                ret = wc_AesGcmEncryptUpdate(&ctx->cipher.aes, dst, src,
+                    len, NULL, 0);
+            }
+            else {
+                ret = wc_AesGcmDecryptUpdate(&ctx->cipher.aes, dst, src,
+                    len, NULL, 0);
+            }
+        }
+        /*
+         * src == NULL is analogous to other "final"-type functions
+         * (e.g. EVP_CipherFinal). Calculates tag on encrypt
+         * and checks tag on decrypt.
+         */
+        else {
+            if (ctx->enc) {
+                /* Calculate authentication tag. */
+                ret = wc_AesGcmEncryptFinal(&ctx->cipher.aes,
+                    ctx->authTag, ctx->authTagSz);
+                /*
+                 * wc_AesGcmEncryptFinal increments the IV in
+                 * ctx->cipher.aes.reg, so we don't call IncCtr here.
+                 */
+            }
+            else {
+                /* Calculate authentication tag and compare. */
+                ret = wc_AesGcmDecryptFinal(&ctx->cipher.aes,
+                    ctx->authTag, ctx->authTagSz);
+                if (ctx->gcmIncIv) {
+                    IncCtr((byte*)ctx->cipher.aes.reg,
+                           ctx->cipher.aes.nonceSz);
+                }
+            }
+            /* Reinitialize for subsequent wolfSSL_EVP_Cipher calls. */
+            if (wc_AesGcmInit(&ctx->cipher.aes, NULL, 0,
+                              (byte*)ctx->cipher.aes.reg,
+                              ctx->ivSz) != 0) {
+                WOLFSSL_MSG("wc_AesGcmInit failed");
+                return WOLFSSL_FATAL_ERROR;
+            }
+            ctx->gcmIncIv = 0;
+        }
+    #endif /* WOLFSSL_AESGCM_STREAM */
+        if (src == NULL) {
+            /*
+             * Clear any leftover AAD on final (final is when src is
+             * NULL).
+             */
+            XMEMSET(ctx->gcmAuthIn, 0, ctx->gcmAuthInSz);
+            ctx->gcmAuthInSz = 0;
+        }
+        if (ret == 0) {
+            ret = len;
+        }
+
+        return ret;
+    }
+#endif /* HAVE_AESGCM && ((!HAVE_FIPS && !HAVE_SELFTEST) ||
+        * HAVE_FIPS_VERSION >= 2 */
+
     /* return WOLFSSL_SUCCESS on ok, 0 on failure to match API compatibility */
-    int  wolfSSL_EVP_CipherInit(WOLFSSL_EVP_CIPHER_CTX* ctx,
+    int wolfSSL_EVP_CipherInit(WOLFSSL_EVP_CIPHER_CTX* ctx,
                                const WOLFSSL_EVP_CIPHER* type, const byte* key,
                                const byte* iv, int enc)
     {
@@ -5648,6 +5916,8 @@ int wolfSSL_EVP_MD_type(const WOLFSSL_EVP_MD* type)
             ctx->gcmAuthIn = NULL;
         }
         ctx->gcmAuthInSz = 0;
+        ctx->gcmIvGenEnable = 0;
+        ctx->gcmIncIv = 0;
 #endif
 
 #ifndef NO_AES
@@ -5732,125 +6002,29 @@ int wolfSSL_EVP_MD_type(const WOLFSSL_EVP_MD* type)
         }
         #endif /* WOLFSSL_AES_256 */
     #endif /* HAVE_AES_CBC || WOLFSSL_AES_DIRECT */
-#if (!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) || \
-    (defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION >= 2))
-    #ifdef HAVE_AESGCM
+    #if defined(HAVE_AESGCM) && ((!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) \
+        || FIPS_VERSION_GE(2,0))
+        if (
         #ifdef WOLFSSL_AES_128
-        if (ctx->cipherType == AES_128_GCM_TYPE ||
-            (type && EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_128_GCM))) {
-            WOLFSSL_MSG("EVP_AES_128_GCM");
-            ctx->cipherType = AES_128_GCM_TYPE;
-            ctx->flags     &= ~WOLFSSL_EVP_CIPH_MODE;
-            ctx->flags     |= WOLFSSL_EVP_CIPH_GCM_MODE |
-                    WOLFSSL_EVP_CIPH_FLAG_AEAD_CIPHER;
-            ctx->keyLen     = 16;
-            ctx->block_size = AES_BLOCK_SIZE;
-            ctx->authTagSz  = AES_BLOCK_SIZE;
-            if (ctx->ivSz == 0) {
-                ctx->ivSz = GCM_NONCE_MID_SZ;
-            }
-
-#ifndef WOLFSSL_AESGCM_STREAM
-            if (key && wc_AesGcmSetKey(&ctx->cipher.aes, key, ctx->keyLen)) {
-                WOLFSSL_MSG("wc_AesGcmSetKey() failed");
-                return WOLFSSL_FAILURE;
-            }
-#endif /* !WOLFSSL_AESGCM_STREAM */
-            if (iv && wc_AesGcmSetExtIV(&ctx->cipher.aes, iv, ctx->ivSz)) {
-                WOLFSSL_MSG("wc_AesGcmSetExtIV() failed");
-                return WOLFSSL_FAILURE;
-            }
-#ifdef WOLFSSL_AESGCM_STREAM
-            /* Initialize with key and IV if available. */
-            if (wc_AesGcmInit(&ctx->cipher.aes, key,
-                                    (key == NULL) ? 0 : ctx->keyLen, iv,
-                                    (iv == NULL) ? 0 : ctx->ivSz) != 0) {
-                WOLFSSL_MSG("wc_AesGcmInit() failed");
-                return WOLFSSL_FAILURE;
-            }
-#endif /* WOLFSSL_AESGCM_STREAM */
-            if (enc == 0 || enc == 1)
-                ctx->enc = enc ? 1 : 0;
-        }
-        #endif /* WOLFSSL_AES_128 */
+            ctx->cipherType == AES_128_GCM_TYPE ||
+            (type && EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_128_GCM))
+        #endif
         #ifdef WOLFSSL_AES_192
-        if (ctx->cipherType == AES_192_GCM_TYPE ||
-                 (type && EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_192_GCM))) {
-            WOLFSSL_MSG("EVP_AES_192_GCM");
-            ctx->cipherType = AES_192_GCM_TYPE;
-            ctx->flags     &= ~WOLFSSL_EVP_CIPH_MODE;
-            ctx->flags     |= WOLFSSL_EVP_CIPH_GCM_MODE |
-                    WOLFSSL_EVP_CIPH_FLAG_AEAD_CIPHER;
-            ctx->keyLen     = 24;
-            ctx->block_size = AES_BLOCK_SIZE;
-            ctx->authTagSz  = AES_BLOCK_SIZE;
-            if (ctx->ivSz == 0) {
-                ctx->ivSz = GCM_NONCE_MID_SZ;
-            }
-
-#ifndef WOLFSSL_AESGCM_STREAM
-            if (key && wc_AesGcmSetKey(&ctx->cipher.aes, key, ctx->keyLen)) {
-                WOLFSSL_MSG("wc_AesGcmSetKey() failed");
-                return WOLFSSL_FAILURE;
-            }
-#endif /* !WOLFSSL_AESGCM_STREAM */
-            if (iv && wc_AesGcmSetExtIV(&ctx->cipher.aes, iv, ctx->ivSz)) {
-                WOLFSSL_MSG("wc_AesGcmSetExtIV() failed");
-                return WOLFSSL_FAILURE;
-            }
-#ifdef WOLFSSL_AESGCM_STREAM
-            /* Initialize with key and IV if available. */
-            if (wc_AesGcmInit(&ctx->cipher.aes, key,
-                                    (key == NULL) ? 0 : ctx->keyLen, iv,
-                                    (iv == NULL) ? 0 : ctx->ivSz) != 0) {
-                WOLFSSL_MSG("wc_AesGcmInit() failed");
-                return WOLFSSL_FAILURE;
-            }
-#endif /* WOLFSSL_AESGCM_STREAM */
-            if (enc == 0 || enc == 1)
-                ctx->enc = enc ? 1 : 0;
-        }
-        #endif /* WOLFSSL_AES_192 */
+            || ctx->cipherType == AES_192_GCM_TYPE ||
+            (type && EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_192_GCM))
+        #endif
         #ifdef WOLFSSL_AES_256
-        if (ctx->cipherType == AES_256_GCM_TYPE ||
-                 (type && EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_256_GCM))) {
-            WOLFSSL_MSG("EVP_AES_256_GCM");
-            ctx->cipherType = AES_256_GCM_TYPE;
-            ctx->flags     &= ~WOLFSSL_EVP_CIPH_MODE;
-            ctx->flags     |= WOLFSSL_EVP_CIPH_GCM_MODE |
-                    WOLFSSL_EVP_CIPH_FLAG_AEAD_CIPHER;
-            ctx->keyLen     = 32;
-            ctx->block_size = AES_BLOCK_SIZE;
-            ctx->authTagSz  = AES_BLOCK_SIZE;
-            if (ctx->ivSz == 0) {
-                ctx->ivSz = GCM_NONCE_MID_SZ;
-            }
-
-#ifndef WOLFSSL_AESGCM_STREAM
-            if (key && wc_AesGcmSetKey(&ctx->cipher.aes, key, ctx->keyLen)) {
-                WOLFSSL_MSG("wc_AesGcmSetKey() failed");
-                return WOLFSSL_FAILURE;
-            }
-#endif /* !WOLFSSL_AESGCM_STREAM */
-            if (iv && wc_AesGcmSetExtIV(&ctx->cipher.aes, iv, ctx->ivSz)) {
-                WOLFSSL_MSG("wc_AesGcmSetExtIV() failed");
-                return WOLFSSL_FAILURE;
-            }
-#ifdef WOLFSSL_AESGCM_STREAM
-            /* Initialize with key and IV if available. */
-            if (wc_AesGcmInit(&ctx->cipher.aes,
-                                key, (key == NULL) ? 0 : ctx->keyLen,
-                                iv, (iv == NULL) ? 0 : ctx->ivSz) != 0) {
-                WOLFSSL_MSG("wc_AesGcmInit() failed");
+            || ctx->cipherType == AES_256_GCM_TYPE ||
+            (type && EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_256_GCM))
+        #endif
+          ) {
+            if (EvpCipherInitAesGCM(ctx, type, key, iv, enc)
+                != WOLFSSL_SUCCESS) {
                 return WOLFSSL_FAILURE;
             }
-#endif /* WOLFSSL_AESGCM_STREAM */
-            if (enc == 0 || enc == 1)
-                ctx->enc = enc ? 1 : 0;
         }
-        #endif /* WOLFSSL_AES_256 */
-    #endif /* HAVE_AESGCM */
-#endif /* (!HAVE_FIPS && !HAVE_SELFTEST) || HAVE_FIPS_VERSION >= 2 */
+    #endif /* HAVE_AESGCM && ((!HAVE_FIPS && !HAVE_SELFTEST) ||
+            * HAVE_FIPS_VERSION >= 2 */
 #ifdef WOLFSSL_AES_COUNTER
         #ifdef WOLFSSL_AES_128
         if (ctx->cipherType == AES_128_CTR_TYPE ||
@@ -6596,31 +6770,23 @@ int wolfSSL_EVP_MD_type(const WOLFSSL_EVP_MD* type)
 
     /* Return length on ok */
     int wolfSSL_EVP_Cipher(WOLFSSL_EVP_CIPHER_CTX* ctx, byte* dst, byte* src,
-                          word32 len)
+                           word32 len)
     {
-        int ret = 0;
+        int ret = WOLFSSL_FAILURE;
+
         WOLFSSL_ENTER("wolfSSL_EVP_Cipher");
 
-        if (ctx == NULL) {
-            WOLFSSL_MSG("Bad function argument");
+        if (ctx == NULL || ((src == NULL || dst == NULL) &&
+            (ctx->cipherType != AES_128_GCM_TYPE &&
+             ctx->cipherType != AES_192_GCM_TYPE &&
+             ctx->cipherType != AES_256_GCM_TYPE))) {
+            WOLFSSL_MSG("Bad argument.");
             return WOLFSSL_FATAL_ERROR;
         }
 
-        if (src == NULL || dst == NULL) {
-            if (src != NULL && dst == NULL &&
-                (ctx->cipherType == AES_128_GCM_TYPE ||
-                 ctx->cipherType == AES_192_GCM_TYPE ||
-                 ctx->cipherType == AES_256_GCM_TYPE)) {
-                WOLFSSL_MSG("Setting GCM AAD.");
-            }
-            else {
-                WOLFSSL_MSG("Bad function argument");
-                return WOLFSSL_FATAL_ERROR;
-            }
-        }
-
-        if (ctx->cipherType == 0xff) {
-            WOLFSSL_MSG("no init");
+        if (ctx->cipherType == WOLFSSL_EVP_CIPH_TYPE_INIT) {
+            WOLFSSL_MSG("Cipher operation not initialized. Call "
+                        "wolfSSL_EVP_CipherInit.");
             return WOLFSSL_FATAL_ERROR;
         }
 
@@ -6706,74 +6872,16 @@ int wolfSSL_EVP_MD_type(const WOLFSSL_EVP_MD* type)
                 break;
 #endif /* WOLFSSL_AES_XTS */
 
-#ifdef HAVE_AESGCM
+#if defined(HAVE_AESGCM) && ((!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) \
+    || FIPS_VERSION_GE(2,0))
             case AES_128_GCM_TYPE :
             case AES_192_GCM_TYPE :
             case AES_256_GCM_TYPE :
                 WOLFSSL_MSG("AES GCM");
-#ifndef WOLFSSL_AESGCM_STREAM
-                /* No destination means only AAD. */
-                if (!dst) {
-                    ret = wolfSSL_EVP_CipherUpdate_GCM_AAD(ctx, src, len);
-                }
-                else {
-                    if (ctx->enc)
-                        ret = wc_AesGcmEncrypt(&ctx->cipher.aes, dst, src,
-                                len, ctx->iv, ctx->ivSz, ctx->authTag,
-                                ctx->authTagSz, ctx->gcmAuthIn, ctx->gcmAuthInSz);
-                    else
-                        ret = wc_AesGcmDecrypt(&ctx->cipher.aes, dst, src,
-                                len, ctx->iv, ctx->ivSz, ctx->authTag,
-                                ctx->authTagSz, ctx->gcmAuthIn, ctx->gcmAuthInSz);
-                }
-#else
-                /* Do one shot operation with streaming API as other
-                 * initialization set up for streaming. */
-                ret = wc_AesGcmInit(&ctx->cipher.aes, NULL, 0, ctx->iv,
-                    ctx->ivSz);
-                /* No destination means only AAD. */
-                if ((ret == 0) && (dst == NULL)) {
-                    if (ctx->enc) {
-                        ret = wc_AesGcmEncryptUpdate(&ctx->cipher.aes, NULL,
-                            NULL, 0, src, len);
-                    }
-                    else {
-                        ret = wc_AesGcmDecryptUpdate(&ctx->cipher.aes, NULL,
-                            NULL, 0, src, len);
-                    }
-                }
-                /* Only plaintext/cipher text. */
-                else if (ret == 0) {
-                    if (ctx->enc) {
-                        ret = wc_AesGcmEncryptUpdate(&ctx->cipher.aes, dst, src,
-                            len, NULL, 0);
-                    }
-                    else {
-                        ret = wc_AesGcmDecryptUpdate(&ctx->cipher.aes, dst, src,
-                            len, NULL, 0);
-                        if (ret == 0) {
-                            ret = wc_AesGcmDecryptFinal(&ctx->cipher.aes,
-                                ctx->authTag, ctx->authTagSz);
-                        }
-                    }
-                }
-                if (ret == 0) {
-                    if (ctx->enc) {
-                        /* Calculate authentication tag. */
-                        ret = wc_AesGcmEncryptFinal(&ctx->cipher.aes,
-                            ctx->authTag, ctx->authTagSz);
-                    }
-                    else {
-                        /* Calculate authentication tag and compare. */
-                        ret = wc_AesGcmDecryptFinal(&ctx->cipher.aes,
-                            ctx->authTag, ctx->authTagSz);
-                    }
-                }
-#endif /* WOLFSSL_AESGCM_STREAM */
-                if (ret == 0)
-                    ret = len;
+                ret = EvpCipherAesGCM(ctx, dst, src, len);
                 break;
-#endif /* HAVE_AESGCM */
+#endif /* HAVE_AESGCM && ((!HAVE_FIPS && !HAVE_SELFTEST) ||
+        * HAVE_FIPS_VERSION >= 2 */
 #ifdef HAVE_AES_ECB
             case AES_128_ECB_TYPE :
             case AES_192_ECB_TYPE :

+ 4 - 0
wolfssl/openssl/evp.h

@@ -438,6 +438,10 @@ struct WOLFSSL_EVP_CIPHER_CTX {
 #endif
     int     authTagSz;
 #endif
+#ifdef HAVE_AESGCM
+    byte    gcmIvGenEnable:1;
+    byte    gcmIncIv:1;
+#endif
 #endif
 };