Commit 23d7714127 for openssl.org

commit 23d77141277fb98a1f8f7dbbbb399ffd1010e4ef
Author: Billy Brumley <bbb@iki.fi>
Date:   Mon Sep 7 04:24:24 2026 -0400

    GCM: reject out-of-order update calls

    For GCM, AAD must precede the payload.
    The lib was already rejecting late AAD,
    but this allows distinguishing the error
    PROV_R_UPDATE_CALL_OUT_OF_ORDER from the generic error
    PROV_R_CIPHER_OPERATION_FAILED.

    Follow-up to #31906

    Assisted-by: Claude:claude-fable-5-1
    Reviewed-by: Mounir Idrassi <mounir.idrassi@idrix.fr>
    Reviewed-by: Neil Horman <nhorman@openssl.org>
    Merge-date: Wed Sep 30 12:10:30 2026
    Merged-from: https://github.com/openssl/openssl/pull/32011

diff --git a/crypto/modes/gcm128.c b/crypto/modes/gcm128.c
index f1a6ebff17..7c685d4d43 100644
--- a/crypto/modes/gcm128.c
+++ b/crypto/modes/gcm128.c
@@ -724,6 +724,12 @@ void CRYPTO_gcm128_setiv(GCM128_CONTEXT *ctx, const unsigned char *iv,
         ctx->Yi.d[3] = ctr;
 }

+/*
+ * Returns:
+ * -  0: success
+ * - -1: error, AAD length overflow
+ * - -2: error, AAD follows the payload -> out of order
+ */
 int CRYPTO_gcm128_aad(GCM128_CONTEXT *ctx, const unsigned char *aad,
     size_t len)
 {
diff --git a/doc/man3/EVP_EncryptInit.pod b/doc/man3/EVP_EncryptInit.pod
index 42b768c86b..1b158f7fea 100644
--- a/doc/man3/EVP_EncryptInit.pod
+++ b/doc/man3/EVP_EncryptInit.pod
@@ -1532,6 +1532,10 @@ EVP_CipherUpdate() call in a single operation.

 =head2 GCM and OCB Modes

+In GCM mode all AAD must be supplied before any plaintext or ciphertext is
+processed. An AAD update made after payload data fails and reports
+B<PROV_R_UPDATE_CALL_OUT_OF_ORDER>.
+
 The following I<ctrl>s are supported in GCM and OCB modes.

 =over 4
diff --git a/providers/implementations/ciphers/cipher_aes_hw_aesni.c b/providers/implementations/ciphers/cipher_aes_hw_aesni.c
index 8a9cc264bb..dce9c1bc57 100644
--- a/providers/implementations/ciphers/cipher_aes_hw_aesni.c
+++ b/providers/implementations/ciphers/cipher_aes_hw_aesni.c
@@ -328,9 +328,9 @@ static int vaes_gcm_aadupdate(PROV_GCM_CTX *ctx,
     unsigned int ares;
     size_t i, lenBlks;

-    /* Bad sequence: call of AAD update after message processing */
+    /* Bad sequence: AAD update after message processing (out of order) */
     if (gcmctx->len.u[1] > 0)
-        return 0;
+        return -2;

     alen += aad_len;
     /* AAD is limited by 2^64 bits, thus 2^61 bytes */
diff --git a/providers/implementations/ciphers/cipher_aes_hw_s390x.c b/providers/implementations/ciphers/cipher_aes_hw_s390x.c
index a0a83e62ad..6afe7789f6 100644
--- a/providers/implementations/ciphers/cipher_aes_hw_s390x.c
+++ b/providers/implementations/ciphers/cipher_aes_hw_s390x.c
@@ -377,9 +377,9 @@ static int s390x_aes_gcm_aad_update(PROV_GCM_CTX *ctx,
     unsigned int fc;
     int n, rem;

-    /* If already processed pt/ct then error */
+    /* If already processed pt/ct then error (out-of-order AAD) */
     if (kma->tpcl != 0)
-        return 0;
+        return -2;

     /* update the total aad length */
     alen = kma->taadl + len;
diff --git a/providers/implementations/ciphers/ciphercommon_gcm.c b/providers/implementations/ciphers/ciphercommon_gcm.c
index a0068066d4..c5c1270328 100644
--- a/providers/implementations/ciphers/ciphercommon_gcm.c
+++ b/providers/implementations/ciphers/ciphercommon_gcm.c
@@ -364,10 +364,8 @@ int ossl_gcm_stream_update(void *vctx, unsigned char *out, size_t *outl,
         return 0;
     }

-    if (gcm_cipher_internal(ctx, out, outl, in, inl) <= 0) {
-        ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+    if (gcm_cipher_internal(ctx, out, outl, in, inl) <= 0)
         return 0;
-    }
     return 1;
 }

@@ -448,6 +446,7 @@ static int on_preupdate_generate_iv(PROV_GCM_CTX *ctx)
     return 1;
 }

+/* returns 1 on success, 0 on failure with a reason on the error queue */
 static int gcm_cipher_internal(PROV_GCM_CTX *ctx, unsigned char *out,
     size_t *padlen, const unsigned char *in,
     size_t len)
@@ -459,8 +458,10 @@ static int gcm_cipher_internal(PROV_GCM_CTX *ctx, unsigned char *out,
     if (ctx->tls_aad_len != UNINITIALISED_SIZET)
         return gcm_tls_cipher(ctx, out, padlen, in, len);

-    if (!ctx->key_set || ctx->iv_state == IV_STATE_FINISHED)
+    if (ctx->key_set == 0 || ctx->iv_state == IV_STATE_FINISHED) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
         goto err;
+    }

     /*
      * FIPS requires generation of AES-GCM IV's inside the FIPS module.
@@ -469,25 +470,39 @@ static int gcm_cipher_internal(PROV_GCM_CTX *ctx, unsigned char *out,
      * where setting the IV externally is the only option available.
      */
     if (ctx->iv_state == IV_STATE_UNINITIALISED) {
-        if (!ctx->enc || !gcm_iv_generate(ctx, 0))
+        if (ctx->enc == 0 || gcm_iv_generate(ctx, 0) == 0) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
             goto err;
+        }
     }

     if (ctx->iv_state == IV_STATE_BUFFERED) {
-        if (!hw->setiv(ctx, ctx->iv, ctx->ivlen))
+        if (hw->setiv(ctx, ctx->iv, ctx->ivlen) == 0) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
             goto err;
+        }
         ctx->iv_state = IV_STATE_COPIED;
     }

     if (in != NULL) {
         /*  The input is AAD if out is NULL */
         if (out == NULL) {
-            if (!hw->aadupdate(ctx, in, len))
+            int rv_aad = hw->aadupdate(ctx, in, len);
+
+            if (rv_aad == -2) { /* AAD after payload */
+                ERR_raise(ERR_LIB_PROV, PROV_R_UPDATE_CALL_OUT_OF_ORDER);
+                goto err;
+            }
+            if (rv_aad <= 0) { /* AAD length overflow or other failure */
+                ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
                 goto err;
+            }
         } else {
             /* The input is ciphertext OR plaintext */
-            if (!hw->cipherupdate(ctx, in, len, out))
+            if (hw->cipherupdate(ctx, in, len, out) == 0) {
+                ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
                 goto err;
+            }
         }
     } else {
         /* The tag must be set before actually decrypting data */
@@ -498,6 +513,8 @@ static int gcm_cipher_internal(PROV_GCM_CTX *ctx, unsigned char *out,
         if (hw->cipherfinal(ctx, ctx->buf) == 0) {
             if (ctx->enc == 0)
                 ERR_raise(ERR_LIB_PROV, PROV_R_BAD_DECRYPT);
+            else
+                ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
             goto err;
         }
         ctx->iv_state = IV_STATE_FINISHED; /* Don't reuse the IV */
@@ -584,12 +601,16 @@ static int gcm_tls_cipher(PROV_GCM_CTX *ctx, unsigned char *out, size_t *padlen,
     size_t plen = 0;
     unsigned char *tag = NULL;

-    if (!ossl_prov_is_running() || !ctx->key_set)
+    if (ossl_prov_is_running() == 0 || ctx->key_set == 0) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
         goto err;
+    }

     /* Encrypt/decrypt must be performed in place */
-    if (out != in || len < (EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN))
+    if (out != in || len < (EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN)) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
         goto err;
+    }

     /*
      * Check for too many keys as per FIPS 140-2 IG A.5 "Key/IV Pair Uniqueness
@@ -607,11 +628,15 @@ static int gcm_tls_cipher(PROV_GCM_CTX *ctx, unsigned char *out, size_t *padlen,
      * buffer.
      */
     if (ctx->enc) {
-        if (!getivgen(ctx, out, arg))
+        if (getivgen(ctx, out, arg) == 0) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
             goto err;
+        }
     } else {
-        if (!setivinv(ctx, out, arg))
+        if (setivinv(ctx, out, arg) == 0) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
             goto err;
+        }
     }

     /* Fix buffer and length to point to payload */
@@ -624,6 +649,7 @@ static int gcm_tls_cipher(PROV_GCM_CTX *ctx, unsigned char *out, size_t *padlen,
             EVP_GCM_TLS_TAG_LEN)) {
         if (!ctx->enc)
             OPENSSL_cleanse(out, len);
+        ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
         goto err;
     }
     if (ctx->enc)
diff --git a/providers/implementations/ciphers/ciphercommon_gcm_hw.c b/providers/implementations/ciphers/ciphercommon_gcm_hw.c
index 807834da87..dd26575f9e 100644
--- a/providers/implementations/ciphers/ciphercommon_gcm_hw.c
+++ b/providers/implementations/ciphers/ciphercommon_gcm_hw.c
@@ -19,7 +19,17 @@ int ossl_gcm_setiv(PROV_GCM_CTX *ctx, const unsigned char *iv, size_t ivlen)
 int ossl_gcm_aad_update(PROV_GCM_CTX *ctx, const unsigned char *aad,
     size_t aad_len)
 {
-    return CRYPTO_gcm128_aad(&ctx->gcm, aad, aad_len) == 0;
+    int rv = CRYPTO_gcm128_aad(&ctx->gcm, aad, aad_len);
+
+    /*
+     * CRYPTO_gcm128_aad() returns:
+     * -  0: success -> return 1
+     * - -1: AAD length overflow -> propagate
+     * - -2: AAD follows the payload -> propagate
+     */
+    if (rv == 0)
+        return 1;
+    return rv;
 }

 int ossl_gcm_cipher_update(PROV_GCM_CTX *ctx, const unsigned char *in,
@@ -54,7 +64,7 @@ int ossl_gcm_one_shot(PROV_GCM_CTX *ctx, unsigned char *aad, size_t aad_len,
     int ret = 0;

     /* Use saved AAD */
-    if (!ctx->hw->aadupdate(ctx, aad, aad_len))
+    if (ctx->hw->aadupdate(ctx, aad, aad_len) <= 0)
         goto err;
     if (!ctx->hw->cipherupdate(ctx, in, in_len, out))
         goto err;
diff --git a/test/evp_aead_test.c b/test/evp_aead_test.c
index 53b2655925..98e73a4763 100644
--- a/test/evp_aead_test.c
+++ b/test/evp_aead_test.c
@@ -8,6 +8,7 @@
  */

 #include <openssl/evp.h>
+#include <openssl/proverr.h>
 #include <openssl/rand.h>
 #include <openssl/core_names.h>
 #include "testutil.h"
@@ -364,12 +365,106 @@ err:
     return testresult;
 }

+/*
+ * With AEAD ciphers, associated data must precede the payload. Once plaintext
+ * or ciphertext processing has begun, a further AAD update (out == NULL) must
+ * be rejected, and the rejection must be reported the same way across every
+ * AEAD: ERR_LIB_PROV / PROV_R_UPDATE_CALL_OUT_OF_ORDER. The invariant is
+ * checked in both the encrypt and decrypt directions.
+ */
+static int test_evp_aead_late_aad(int idx)
+{
+    const AEAD_DATA *info = &aead_list[idx];
+    EVP_CIPHER_CTX *ctx_enc = NULL; /* late AAD after plaintext: must fail */
+    EVP_CIPHER_CTX *ctx_dec = NULL; /* late AAD after ciphertext: must fail */
+    EVP_CIPHER_CTX *ctx_c_enc = NULL; /* as ctx_enc, EVP_Cipher() interface */
+    EVP_CIPHER_CTX *ctx_c_dec = NULL; /* as ctx_dec, EVP_Cipher() interface */
+
+    unsigned char key[EVP_MAX_KEY_LENGTH] = { 0 };
+    unsigned char iv[EVP_MAX_IV_LENGTH] = { 0 };
+    unsigned char aad[] = "aad";
+    unsigned char msg[] = "message";
+    unsigned char out[sizeof(msg) + EVP_MAX_BLOCK_LENGTH];
+
+    int i = 0, len = 0, testresult = 0;
+
+    if (info->mode == EVP_CIPH_CCM_MODE /* fails at first AAD */
+        || info->mode == EVP_CIPH_SIV_MODE /* accepts late AAD */
+        || info->mode == EVP_CIPH_OCB_MODE) /* accepts late AAD */
+        return 1;
+
+    for (i = 0; i < info->keylen && i < (int)sizeof(key); i++)
+        key[i] = (unsigned char)(0xA0 + i);
+    for (i = 0; i < info->ivlen && i < (int)sizeof(iv); i++)
+        iv[i] = (unsigned char)(0xB0 + i);
+
+    /* encrypt: aad, then plaintext, then a late aad update must be rejected */
+    ERR_clear_error();
+    if (!TEST_ptr(ctx_enc = EVP_CIPHER_CTX_new())
+        || !TEST_true(EVP_EncryptInit_ex2(ctx_enc, info->ciph, key, iv, NULL))
+        || !TEST_true(EVP_EncryptUpdate(ctx_enc, NULL, &len, aad, sizeof(aad)))
+        || !TEST_true(EVP_EncryptUpdate(ctx_enc, out, &len, msg, sizeof(msg)))
+        || !TEST_false(EVP_EncryptUpdate(ctx_enc, NULL, &len, aad, sizeof(aad)))
+        || !TEST_err_r(ERR_LIB_PROV, PROV_R_UPDATE_CALL_OUT_OF_ORDER)) {
+        TEST_info("test_evp_aead_late_aad %s: encrypt", info->name);
+        goto err;
+    }
+
+    /*
+     * decrypt: same sequence, late aad after ciphertext must be rejected.
+     * the ciphertext content is irrelevant; the tag is never finalized here.
+     */
+    ERR_clear_error();
+    if (!TEST_ptr(ctx_dec = EVP_CIPHER_CTX_new())
+        || !TEST_true(EVP_DecryptInit_ex2(ctx_dec, info->ciph, key, iv, NULL))
+        || !TEST_true(EVP_DecryptUpdate(ctx_dec, NULL, &len, aad, sizeof(aad)))
+        || !TEST_true(EVP_DecryptUpdate(ctx_dec, out, &len, msg, sizeof(msg)))
+        || !TEST_false(EVP_DecryptUpdate(ctx_dec, NULL, &len, aad, sizeof(aad)))
+        || !TEST_err_r(ERR_LIB_PROV, PROV_R_UPDATE_CALL_OUT_OF_ORDER)) {
+        TEST_info("test_evp_aead_late_aad %s: decrypt", info->name);
+        goto err;
+    }
+
+    /* encrypt, EVP_Cipher() interface: out == NULL is AAD, same as update */
+    ERR_clear_error();
+    if (!TEST_ptr(ctx_c_enc = EVP_CIPHER_CTX_new())
+        || !TEST_true(EVP_EncryptInit_ex2(ctx_c_enc, info->ciph, key, iv, NULL))
+        || !TEST_int_ge(EVP_Cipher(ctx_c_enc, NULL, aad, sizeof(aad)), 0)
+        || !TEST_int_ge(EVP_Cipher(ctx_c_enc, out, msg, sizeof(msg)), 0)
+        || !TEST_int_lt(EVP_Cipher(ctx_c_enc, NULL, aad, sizeof(aad)), 0)
+        || !TEST_err_r(ERR_LIB_PROV, PROV_R_UPDATE_CALL_OUT_OF_ORDER)) {
+        TEST_info("test_evp_aead_late_aad %s: encrypt (EVP_Cipher)", info->name);
+        goto err;
+    }
+
+    /* decrypt, EVP_Cipher() interface */
+    ERR_clear_error();
+    if (!TEST_ptr(ctx_c_dec = EVP_CIPHER_CTX_new())
+        || !TEST_true(EVP_DecryptInit_ex2(ctx_c_dec, info->ciph, key, iv, NULL))
+        || !TEST_int_ge(EVP_Cipher(ctx_c_dec, NULL, aad, sizeof(aad)), 0)
+        || !TEST_int_ge(EVP_Cipher(ctx_c_dec, out, msg, sizeof(msg)), 0)
+        || !TEST_int_lt(EVP_Cipher(ctx_c_dec, NULL, aad, sizeof(aad)), 0)
+        || !TEST_err_r(ERR_LIB_PROV, PROV_R_UPDATE_CALL_OUT_OF_ORDER)) {
+        TEST_info("test_evp_aead_late_aad %s: decrypt (EVP_Cipher)", info->name);
+        goto err;
+    }
+
+    testresult = 1;
+err:
+    EVP_CIPHER_CTX_free(ctx_enc);
+    EVP_CIPHER_CTX_free(ctx_dec);
+    EVP_CIPHER_CTX_free(ctx_c_enc);
+    EVP_CIPHER_CTX_free(ctx_c_dec);
+    return testresult;
+}
+
 int setup_tests(void)
 {
     if (!setup_aead_list())
         return 0;

     ADD_ALL_TESTS(test_evp_oneshot_aead_zerolen, aead_list_n);
+    ADD_ALL_TESTS(test_evp_aead_late_aad, aead_list_n);
     return 1;
 }

diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c
index a4b00e3249..385272faf5 100644
--- a/test/evp_extra_test.c
+++ b/test/evp_extra_test.c
@@ -6181,133 +6181,6 @@ err:
     return testresult;
 }

-/*
- * With AEAD ciphers, associated data must precede the payload. Once plaintext
- * or ciphertext processing has begun, a further AAD update (out == NULL) must
- * be rejected, and the rejection must be reported the same way across every
- * AEAD: ERR_LIB_PROV / PROV_R_UPDATE_CALL_OUT_OF_ORDER. The invariant is
- * checked in both the encrypt and decrypt directions.
- */
-static int test_evp_aead_late_aad(int idx)
-{
-    const EVP_CIPHER_TEST_INFO *info = &cipher_list[idx];
-    EVP_CIPHER_CTX *ctx_enc = NULL; /* late AAD after plaintext: must fail */
-    EVP_CIPHER_CTX *ctx_dec = NULL; /* late AAD after ciphertext: must fail */
-
-    unsigned char key[EVP_MAX_KEY_LENGTH] = { 0 };
-    unsigned char iv[EVP_MAX_IV_LENGTH] = { 0 };
-    unsigned char aad[] = "aad";
-    unsigned char msg[] = "message";
-    unsigned char out[sizeof(msg) + EVP_MAX_BLOCK_LENGTH];
-
-    int i = 0, len = 0, testresult = 0, expected = 0;
-    char *errmsg = NULL;
-    unsigned long err_code = 0;
-
-    if (info->taglen == 0 /* skip non-AEAD */
-        || info->mode == EVP_CIPH_GCM_MODE /* rejects, raises 102 PROV_R_CIPHER_OPERATION_FAILED */
-        || info->mode == EVP_CIPH_CCM_MODE /* fails at first AAD */
-        || info->mode == EVP_CIPH_OCB_MODE /* accepts late AAD */
-        /* skip TLS stitched MTE cipher */
-        || EVP_CIPHER_is_a(info->ciph, "AES-128-CBC-HMAC-SHA1")
-        /* skip TLS stitched MTE cipher */
-        || EVP_CIPHER_is_a(info->ciph, "AES-256-CBC-HMAC-SHA1")
-        /* skip TLS stitched MTE cipher */
-        || EVP_CIPHER_is_a(info->ciph, "AES-128-CBC-HMAC-SHA256")
-        /* skip TLS stitched MTE cipher */
-        || EVP_CIPHER_is_a(info->ciph, "AES-256-CBC-HMAC-SHA256"))
-        return 1;
-
-    for (i = 0; i < info->keylen && i < (int)sizeof(key); i++)
-        key[i] = (unsigned char)(0xA0 + i);
-    for (i = 0; i < info->ivlen && i < (int)sizeof(iv); i++)
-        iv[i] = (unsigned char)(0xB0 + i);
-
-    /* encrypt: aad, then plaintext, then a late aad update must be rejected */
-    if (!TEST_ptr(ctx_enc = EVP_CIPHER_CTX_new())) {
-        errmsg = "ENC_ALLOC";
-        goto err;
-    }
-    if (!TEST_true(EVP_EncryptInit_ex2(ctx_enc, info->ciph, key, iv, NULL))) {
-        errmsg = "ENC_INIT";
-        goto err;
-    }
-    if (!TEST_true(EVP_EncryptUpdate(ctx_enc, NULL, &len, aad, sizeof(aad)))) {
-        errmsg = "ENC_AAD";
-        goto err;
-    }
-    if (!TEST_true(EVP_EncryptUpdate(ctx_enc, out, &len, msg, sizeof(msg)))) {
-        errmsg = "ENC_PLAINTEXT";
-        goto err;
-    }
-    ERR_set_mark();
-    if (!TEST_false(EVP_EncryptUpdate(ctx_enc, NULL, &len, aad, sizeof(aad)))) {
-        ERR_clear_last_mark();
-        errmsg = "ENC_LATE_AAD_NOT_REJECTED";
-        goto err;
-    }
-    err_code = ERR_peek_last_error();
-    if (!TEST_int_eq(ERR_GET_LIB(err_code), ERR_LIB_PROV)
-        || !TEST_int_eq(ERR_GET_REASON(err_code), PROV_R_UPDATE_CALL_OUT_OF_ORDER)) {
-        ERR_clear_last_mark();
-        expected = PROV_R_UPDATE_CALL_OUT_OF_ORDER;
-        errmsg = "ENC_LATE_AAD_WRONG_REASON";
-        goto err;
-    }
-    ERR_pop_to_mark();
-
-    /* decrypt: same sequence, late aad after ciphertext must be rejected */
-    if (!TEST_ptr(ctx_dec = EVP_CIPHER_CTX_new())) {
-        errmsg = "DEC_ALLOC";
-        goto err;
-    }
-    if (!TEST_true(EVP_DecryptInit_ex2(ctx_dec, info->ciph, key, iv, NULL))) {
-        errmsg = "DEC_INIT";
-        goto err;
-    }
-    if (!TEST_true(EVP_DecryptUpdate(ctx_dec, NULL, &len, aad, sizeof(aad)))) {
-        errmsg = "DEC_AAD";
-        goto err;
-    }
-    /* the ciphertext content is irrelevant; the tag is never finalized here */
-    if (!TEST_true(EVP_DecryptUpdate(ctx_dec, out, &len, msg, sizeof(msg)))) {
-        errmsg = "DEC_CIPHERTEXT";
-        goto err;
-    }
-    ERR_set_mark();
-    if (!TEST_false(EVP_DecryptUpdate(ctx_dec, NULL, &len, aad, sizeof(aad)))) {
-        ERR_clear_last_mark();
-        errmsg = "DEC_LATE_AAD_NOT_REJECTED";
-        goto err;
-    }
-    err_code = ERR_peek_last_error();
-    if (!TEST_int_eq(ERR_GET_LIB(err_code), ERR_LIB_PROV)
-        || !TEST_int_eq(ERR_GET_REASON(err_code), PROV_R_UPDATE_CALL_OUT_OF_ORDER)) {
-        ERR_clear_last_mark();
-        expected = PROV_R_UPDATE_CALL_OUT_OF_ORDER;
-        errmsg = "DEC_LATE_AAD_WRONG_REASON";
-        goto err;
-    }
-    ERR_pop_to_mark();
-
-    testresult = 1;
-
-err:
-    if (errmsg != NULL) {
-        if (expected != 0)
-            TEST_info("test_evp_aead_late_aad %d, %s: %s"
-                      " (expected reason %d, got %d)",
-                idx, errmsg, info->name,
-                expected, ERR_GET_REASON(err_code));
-        else
-            TEST_info("test_evp_aead_late_aad %d, %s: %s",
-                idx, errmsg, info->name);
-    }
-    EVP_CIPHER_CTX_free(ctx_enc);
-    EVP_CIPHER_CTX_free(ctx_dec);
-    return testresult;
-}
-
 /*
  * A decrypt with a wrong tag must be rejected by EVP_DecryptFinal_ex().
  * Negative test for the rejection, as well as the expected error reason.
@@ -10076,7 +9949,6 @@ int setup_tests(void)
     ADD_ALL_TESTS(test_evp_stale_key_reinit, cipher_list_n);
     ADD_ALL_TESTS(test_evp_decrypt_roundtrip_multistep, cipher_list_n);
     ADD_ALL_TESTS(test_evp_aead_tag_direction, cipher_list_n);
-    ADD_ALL_TESTS(test_evp_aead_late_aad, cipher_list_n);
     ADD_ALL_TESTS(test_evp_aead_tag_reject, cipher_list_n);

     ADD_ALL_TESTS(test_evp_init_seq, OSSL_NELEM(evp_init_tests));