Commit 0e24154dc8 for openssl.org
commit 0e24154dc80f7bb77484e04a1f24d0f8262f4d6d
Author: Billy Brumley <bbb@iki.fi>
Date: Fri Oct 2 03:09:51 2026 -0400
AEAD regression test for EVP_CIPHER_do_all_provided
... combined with a sanity check for expected AEAD algs in a static list.
- every cipher in aead_algs[] must be discovered
- every discovered cipher must be in aead_algs[]
Meant to catch:
- ciphers falling out of dynamic discovery for unexpected reasons
- ciphers discovered dynamically for unexpected reasons
Could happen for reasons like build system errors, flags shifting, etc.
Assisted-by: Claude:claude-fable-5-1
Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Saša NedvÄ›dický <sashan@openssl.org>
Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
Merge-date: Tue Oct 13 15:22:55 2026
Merged-from: https://github.com/openssl/openssl/pull/32842
diff --git a/test/evp_aead_test.c b/test/evp_aead_test.c
index 244da453ae..d0ec6876b4 100644
--- a/test/evp_aead_test.c
+++ b/test/evp_aead_test.c
@@ -14,17 +14,48 @@
#include "testutil.h"
#include "internal/nelem.h"
-/*
- * Dynamically discover all applicable AEAD ciphers and verify their
- * behavior at the EVP interface level. This test should require zero
- * maintenance when new AEAD ciphers are added, as the discovery loop
- * handles them.
- *
- * This is a copy of the cipher_list discovery mechanism in
- * evp_extra_test.c, narrowed to AEAD ciphers only. Unlike that file,
- * EVP_CIPH_SIV_MODE is deliberately NOT excluded here -- SIV mode is
- * an AEAD mode and belongs in this table.
- */
+/* golden list of AEAD ciphers that should end up in aead_list */
+static const char *const aead_algs[] = {
+ /* always present: no-aes / no-gcm / no-ccm do not exist */
+ "AES-128-GCM",
+ "AES-192-GCM",
+ "AES-256-GCM",
+ "AES-128-CCM",
+ "AES-192-CCM",
+ "AES-256-CCM",
+#ifndef OPENSSL_NO_OCB
+ "AES-128-OCB",
+ "AES-192-OCB",
+ "AES-256-OCB",
+#endif
+#ifndef OPENSSL_NO_SIV
+ /* plain SIV and GCM-SIV share one guard: defltprov.c, build.info */
+ "AES-128-SIV",
+ "AES-192-SIV",
+ "AES-256-SIV",
+ "AES-128-GCM-SIV",
+ "AES-192-GCM-SIV",
+ "AES-256-GCM-SIV",
+#endif
+#if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305)
+ "ChaCha20-Poly1305",
+#endif
+#ifndef OPENSSL_NO_ARIA
+ "ARIA-128-GCM",
+ "ARIA-192-GCM",
+ "ARIA-256-GCM",
+ "ARIA-128-CCM",
+ "ARIA-192-CCM",
+ "ARIA-256-CCM",
+#endif
+#ifndef OPENSSL_NO_SM4
+ "SM4-GCM",
+ "SM4-CCM", /* SM4 is 128-bit key only */
+#endif
+#ifndef OPENSSL_NO_ASCON128
+ "ASCON-AEAD128",
+#endif
+};
/* maximum AEAD tag buffer size */
#define EVPTEST_TAG_LEN_MAX EVP_MAX_MD_SIZE
@@ -40,31 +71,18 @@ typedef struct {
int ivlen;
int mode;
int taglen;
+ unsigned int found : 1;
} AEAD_DATA;
-static AEAD_DATA *aead_list = NULL;
-static int aead_list_n = 0;
-
-static int seen_name(const char *name)
-{
- int i = 0;
-
- if (name == NULL) {
- return 1;
- }
- for (i = 0; i < aead_list_n; i++) {
- if (OPENSSL_strcasecmp(aead_list[i].name, name) == 0)
- return 1;
- }
- return 0;
-}
+/* populated from aead_algs[] by setup_aead_list(); same indices */
+static AEAD_DATA aead_list[OSSL_NELEM(aead_algs)];
static void collect_aead_cipher_cb(EVP_CIPHER *ciph, void *arg)
{
- AEAD_DATA *info = NULL;
const char *name = NULL;
+ size_t i = 0;
- size_t *allocated = arg;
+ size_t *unknown = arg;
if (ciph == NULL
/*
@@ -94,70 +112,91 @@ static void collect_aead_cipher_cb(EVP_CIPHER *ciph, void *arg)
|| EVP_CIPHER_is_a(ciph, "AES-256-CBC-HMAC-SHA1-ETM")
|| EVP_CIPHER_is_a(ciph, "AES-256-CBC-HMAC-SHA256-ETM")
|| EVP_CIPHER_is_a(ciph, "AES-256-CBC-HMAC-SHA512-ETM")
- /* fetch name and skip potential dupes */
- || (name = EVP_CIPHER_get0_name(ciph)) == NULL
- || seen_name(name) == 1)
+ /* fetch name */
+ || (name = EVP_CIPHER_get0_name(ciph)) == NULL)
return;
- /* First pass only counts ciphers to allocate memory. */
- if (allocated != NULL) {
- (*allocated)++;
- return;
+ for (i = 0; i < OSSL_NELEM(aead_list); i++) {
+ if (OPENSSL_strcasecmp(aead_list[i].name, name) == 0) {
+ aead_list[i].found = 1;
+ return;
+ }
}
- info = &aead_list[aead_list_n];
-
- if (!EVP_CIPHER_up_ref(ciph))
- return;
-
- info->ciph = ciph;
- info->name = name;
- /* defaults, resolved later by test_evp_aead_params() */
- info->keylen = EVP_MAX_KEY_LENGTH;
- info->ivlen = EVP_MAX_IV_LENGTH;
- info->mode = -1;
- info->taglen = EVPTEST_TAG_LEN_MAX;
-
- aead_list_n++;
+ /* an AEAD the golden list does not know about */
+ TEST_info("collect_aead_cipher_cb: %s discovered at runtime but not in"
+ " aead_algs[]",
+ name);
+ (*unknown)++;
}
+/*
+ * Populate aead_list from aead_algs[]. If anything fails here, we abort
+ * since tests expect a correctly populated list rather than a partial one.
+ */
static int setup_aead_list(void)
{
- size_t aead_list_size = 0;
-
- aead_list = NULL;
- aead_list_n = 0;
+ AEAD_DATA *info = NULL;
+ size_t i;
- /* First pass counts only, to know how much to allocate. */
- EVP_CIPHER_do_all_provided(NULL, collect_aead_cipher_cb, &aead_list_size);
+ for (i = 0; i < OSSL_NELEM(aead_algs); i++) {
+ info = &aead_list[i];
- if (!TEST_size_t_gt(aead_list_size, 0))
- return 0;
+ if (!TEST_ptr(info->ciph = EVP_CIPHER_fetch(NULL, aead_algs[i], NULL))) {
+ TEST_info("setup_aead_list: %s failed to fetch", aead_algs[i]);
+ return 0;
+ }
- aead_list = OPENSSL_malloc(aead_list_size * sizeof(*aead_list));
- if (!TEST_ptr(aead_list))
- return 0;
+ info->name = aead_algs[i];
+ /* defaults, resolved later by test_evp_aead_params() */
+ info->keylen = EVP_MAX_KEY_LENGTH;
+ info->ivlen = EVP_MAX_IV_LENGTH;
+ info->mode = -1;
+ info->taglen = EVPTEST_TAG_LEN_MAX;
+ }
- /* Second pass actually populates aead_list. */
- EVP_CIPHER_do_all_provided(NULL, collect_aead_cipher_cb, NULL);
- return TEST_int_eq(aead_list_n, (int)aead_list_size);
+ return 1;
}
static void cleanup_aead_list(void)
{
- int i;
+ size_t i;
- if (aead_list == NULL)
- return;
+ for (i = 0; i < OSSL_NELEM(aead_list); i++)
+ EVP_CIPHER_free((EVP_CIPHER *)aead_list[i].ciph);
+}
+
+/*
+ * Regression test for EVP_CIPHER_do_all_provided(),
+ * combined with a sanity check for expected AEAD algs in the static list.
+ * - every cipher in aead_algs[] must be discovered (tracked by found)
+ * - every discovered cipher must be in aead_algs[] (tracked by unknown)
+ * Meant to catch:
+ * - ciphers falling out of dynamic discovery for unexpected reasons
+ * - ciphers discovered dynamically for unexpected reasons
+ * Could happen for reasons like build system errors, flags shifting, etc.
+ */
+static int test_evp_aead_provided(void)
+{
+ size_t i, unknown = 0;
+ int testresult = 0;
- for (i = 0; i < aead_list_n; i++) {
- if (aead_list[i].ciph != NULL)
- EVP_CIPHER_free((EVP_CIPHER *)aead_list[i].ciph);
+ EVP_CIPHER_do_all_provided(NULL, collect_aead_cipher_cb, &unknown);
+
+ for (i = 0; i < OSSL_NELEM(aead_list); i++) {
+ if (!TEST_true(aead_list[i].found)) {
+ TEST_info("test_evp_aead_provided: %s NOT dynamically discovered",
+ aead_list[i].name);
+ goto err;
+ }
}
- OPENSSL_free(aead_list);
- aead_list = NULL;
- aead_list_n = 0;
+ if (!TEST_size_t_eq(unknown, 0))
+ goto err;
+
+ testresult = 1;
+err:
+ return testresult;
}
/*
@@ -289,9 +328,6 @@ static int test_evp_oneshot_aead_zerolen(int idx)
int i = 0, finlen = 0, testresult = 0;
- /* Adding more verbose testing output messages */
- TEST_info("test_evp_oneshot_aead_zerolen: idx=%d, cipher=%s", idx, info->name);
-
for (i = 0; i < info->keylen; i++)
key[i] = (unsigned char)(0xA0 + i);
for (i = 0; i < info->ivlen; i++)
@@ -563,11 +599,6 @@ static int test_evp_aead_finished_ctx(int idx)
goto err;
}
- /* TODO: drop once AEAD_DATA.taglen is resolved from the cipher */
- i = EVP_CIPHER_CTX_get_tag_length(ctx_enc);
- if (i > 0)
- taglen = i;
-
tagparams[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG,
tag, taglen);
tagparams[1] = OSSL_PARAM_construct_end();
@@ -651,13 +682,6 @@ static int test_evp_aead_get_tag_pairwise(int idx)
|| !TEST_true(EVP_EncryptFinal_ex(ctx, ct + outlen, &finlen)))
goto err;
- /* clamp to the negotiated tag length if the cipher reports one */
- i = EVP_CIPHER_CTX_get_tag_length(ctx);
- if (i > 0)
- taglen = i;
- if (!TEST_int_le(taglen, EVPTEST_TAG_LEN_MAX))
- goto err;
-
/* read via ctrl */
if (!TEST_true(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, taglen,
tag_ctrl))) {
@@ -836,24 +860,25 @@ err:
int setup_tests(void)
{
- int i = 0;
+ size_t i = 0;
if (setup_aead_list() == 0)
return 0;
/* resolve aead_list before registering, tests run in arbitrary order */
- for (i = 0; i < aead_list_n; i++) {
- if (test_evp_aead_params(i) == 0)
+ for (i = 0; i < OSSL_NELEM(aead_list); i++) {
+ if (test_evp_aead_params((int)i) == 0)
TEST_info("%s: unresolved, tests will fail", aead_list[i].name);
}
- ADD_ALL_TESTS(test_evp_aead_params, aead_list_n);
- ADD_ALL_TESTS(test_evp_oneshot_aead_zerolen, aead_list_n);
- ADD_ALL_TESTS(test_evp_aead_late_aad, aead_list_n);
- ADD_ALL_TESTS(test_evp_aead_finished_ctx, aead_list_n);
- ADD_ALL_TESTS(test_evp_aead_get_tag_pairwise, aead_list_n);
- ADD_ALL_TESTS(test_evp_final_no_tag, aead_list_n);
- ADD_ALL_TESTS(test_evp_aead_enc_retval, aead_list_n);
+ ADD_TEST(test_evp_aead_provided);
+ ADD_ALL_TESTS(test_evp_aead_params, OSSL_NELEM(aead_list));
+ ADD_ALL_TESTS(test_evp_oneshot_aead_zerolen, OSSL_NELEM(aead_list));
+ ADD_ALL_TESTS(test_evp_aead_late_aad, OSSL_NELEM(aead_list));
+ ADD_ALL_TESTS(test_evp_aead_finished_ctx, OSSL_NELEM(aead_list));
+ 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));
return 1;
}