Commit 77bde2b32b for openssl.org

commit 77bde2b32b4563f1fe4248eafdcd2bd6dfb29dc3
Author: Trust-Worthy <jondeveloper0@gmail.com>
Date:   Wed Sep 30 18:22:45 2026 -0400

    Add test for an update with no input

    An update with no input and a nonzero length must fail, except for the
    CCM message length. test_evp_aead_update_null_input checks this for
    every AEAD, and that a rejected update writes no output.

    Assisted-by: Claude:claude-opus-5-5
    Reviewed-by: Paul Dale <paul.dale@oracle.com>
    Reviewed-by: Neil Horman <nhorman@openssl.org>
    Merge-date: Wed Oct 21 16:59:57 2026
    Merged-from: https://github.com/openssl/openssl/pull/33157

diff --git a/test/evp_aead_test.c b/test/evp_aead_test.c
index d0ec6876b4..48c217ffc6 100644
--- a/test/evp_aead_test.c
+++ b/test/evp_aead_test.c
@@ -858,6 +858,95 @@ err:
     return ret;
 }

+/*
+ * An update with no input and a nonzero length must fail, except for the
+ * CCM message length, which is passed with no input and no output. This is
+ * checked for both encryption and decryption.
+ */
+static int test_evp_aead_update_null_input(int idx)
+{
+    const AEAD_DATA *info = &aead_list[idx];
+    EVP_CIPHER_CTX *ctx = NULL;
+    unsigned char key[EVP_MAX_KEY_LENGTH] = { 0 };
+    unsigned char iv[EVP_MAX_IV_LENGTH] = { 0 };
+    unsigned char out[16] = { 0xAB }, untouched[16] = { 0xAB };
+    int outl = 0, testresult = 0;
+
+    /* encrypt: no input, no output -- valid only as the CCM message length */
+    ERR_clear_error();
+    if (!TEST_ptr(ctx = EVP_CIPHER_CTX_new())
+        || !TEST_true(EVP_EncryptInit_ex2(ctx, info->ciph, key, iv, NULL)))
+        goto err;
+    if (info->mode == EVP_CIPH_CCM_MODE) {
+        if (!TEST_true(EVP_EncryptUpdate(ctx, NULL, &outl, NULL, 16))) {
+            TEST_info("encrypt CCM length rejected: idx=%d cipher=%s",
+                idx, info->name);
+            goto err;
+        }
+    } else if (!TEST_false(EVP_EncryptUpdate(ctx, NULL, &outl, NULL, 16))
+        || !TEST_err_r(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER)) {
+        TEST_info("encrypt no input, no output: idx=%d cipher=%s",
+            idx, info->name);
+        goto err;
+    }
+    EVP_CIPHER_CTX_free(ctx);
+    ctx = NULL;
+
+    /* encrypt: no input with an output buffer -- always fails, writes nothing */
+    ERR_clear_error();
+    if (!TEST_ptr(ctx = EVP_CIPHER_CTX_new())
+        || !TEST_true(EVP_EncryptInit_ex2(ctx, info->ciph, key, iv, NULL))
+        || !TEST_false(EVP_EncryptUpdate(ctx, out, &outl, NULL, 16))
+        || !TEST_err_r(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER)
+        || !TEST_int_eq(outl, 0)
+        || !TEST_mem_eq(out, sizeof(out), untouched, sizeof(untouched))) {
+        TEST_info("encrypt no input, with output: idx=%d cipher=%s",
+            idx, info->name);
+        goto err;
+    }
+    EVP_CIPHER_CTX_free(ctx);
+    ctx = NULL;
+
+    /* decrypt: no input, no output -- valid only as the CCM message length */
+    ERR_clear_error();
+    if (!TEST_ptr(ctx = EVP_CIPHER_CTX_new())
+        || !TEST_true(EVP_DecryptInit_ex2(ctx, info->ciph, key, iv, NULL)))
+        goto err;
+    if (info->mode == EVP_CIPH_CCM_MODE) {
+        if (!TEST_true(EVP_DecryptUpdate(ctx, NULL, &outl, NULL, 16))) {
+            TEST_info("decrypt CCM length rejected: idx=%d cipher=%s",
+                idx, info->name);
+            goto err;
+        }
+    } else if (!TEST_false(EVP_DecryptUpdate(ctx, NULL, &outl, NULL, 16))
+        || !TEST_err_r(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER)) {
+        TEST_info("decrypt no input, no output: idx=%d cipher=%s",
+            idx, info->name);
+        goto err;
+    }
+    EVP_CIPHER_CTX_free(ctx);
+    ctx = NULL;
+
+    /* decrypt: no input with an output buffer -- always fails, writes nothing */
+    ERR_clear_error();
+    if (!TEST_ptr(ctx = EVP_CIPHER_CTX_new())
+        || !TEST_true(EVP_DecryptInit_ex2(ctx, info->ciph, key, iv, NULL))
+        || !TEST_false(EVP_DecryptUpdate(ctx, out, &outl, NULL, 16))
+        || !TEST_err_r(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER)
+        || !TEST_int_eq(outl, 0)
+        || !TEST_mem_eq(out, sizeof(out), untouched, sizeof(untouched))) {
+        TEST_info("decrypt no input, with output: idx=%d cipher=%s",
+            idx, info->name);
+        goto err;
+    }
+
+    testresult = 1;
+err:
+    ERR_clear_error();
+    EVP_CIPHER_CTX_free(ctx);
+    return testresult;
+}
+
 int setup_tests(void)
 {
     size_t i = 0;
@@ -879,6 +968,7 @@ int setup_tests(void)
     ADD_ALL_TESTS(test_evp_aead_get_tag_pairwise, OSSL_NELEM(aead_list));
     ADD_ALL_TESTS(test_evp_final_no_tag, OSSL_NELEM(aead_list));
     ADD_ALL_TESTS(test_evp_aead_enc_retval, OSSL_NELEM(aead_list));
+    ADD_ALL_TESTS(test_evp_aead_update_null_input, OSSL_NELEM(aead_list));
     return 1;
 }