From 76acabff7f5369cb5a0625be4d30aa23f9af399a Mon Sep 17 00:00:00 2001 From: Yosuke Shimizu Date: Wed, 26 Aug 2026 14:50:21 +0900 Subject: [PATCH] gmac: clear the cached message on init - wp_gmac_init() releases macCtx->data with OPENSSL_clear_free() and resets data and dataLen once the running check passes, before the parameters and key are applied. - test_gmac_reinit drives one EVP_MAC_CTX through init/update/final rounds plus an init mid-stream, re-supplying the IV on each init, and compares every MAC against OpenSSL. - test/unit.c and test/unit.h register the new test case. Issue: F-11551 --- src/wp_gmac.c | 5 ++ test/test_gmac.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++ test/unit.c | 1 + test/unit.h | 1 + 4 files changed, 150 insertions(+) diff --git a/src/wp_gmac.c b/src/wp_gmac.c index 8e2f5abc..6ff9fb4d 100644 --- a/src/wp_gmac.c +++ b/src/wp_gmac.c @@ -218,6 +218,11 @@ static int wp_gmac_init(wp_GmacCtx* macCtx, const unsigned char* key, if (!wolfssl_prov_is_running()) { ok = 0; } + if (ok) { + OPENSSL_clear_free(macCtx->data, macCtx->dataLen); + macCtx->data = NULL; + macCtx->dataLen = 0; + } if (ok && (params != NULL) && (!wp_gmac_set_ctx_params(macCtx, params))) { ok = 0; } diff --git a/test/test_gmac.c b/test/test_gmac.c index fc023571..f5b08d8a 100644 --- a/test/test_gmac.c +++ b/test/test_gmac.c @@ -157,6 +157,149 @@ int test_gmac_create(void *data) return ret; } +/** + * Test that one GMAC context can be reset by calling EVP_MAC_init() again, + * both after EVP_MAC_final() and mid-stream, reusing the cached key. + */ +static int test_gmac_reinit_helper(OSSL_LIB_CTX* libCtx, unsigned char *macA, + unsigned char *macB) +{ + int err; + EVP_MAC *emac = NULL; + EVP_MAC_CTX *ctx = NULL; + OSSL_PARAM params[4]; + OSSL_PARAM ivParams[2]; + char cipher[] = "AES-256-GCM"; + unsigned char key[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + }; + unsigned char iv[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03 + }; + unsigned char msgA[32]; + unsigned char msgB[21]; + unsigned char macC[AES_BLOCK_SIZE]; + unsigned char macD[AES_BLOCK_SIZE]; + size_t macASz = AES_BLOCK_SIZE; + size_t macBSz = AES_BLOCK_SIZE; + size_t macCSz = sizeof(macC); + size_t macDSz = sizeof(macD); + + memset(msgA, 0x41, sizeof(msgA)); + memset(msgB, 0x5a, sizeof(msgB)); + + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_CIPHER, + cipher, 0); + params[1] = OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_KEY, + (void *)key, sizeof(key)); + params[2] = OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_IV, + (void *)iv, sizeof(iv)); + params[3] = OSSL_PARAM_construct_end(); + /* The IV has to be set again on each init to restart the calculation. */ + ivParams[0] = params[2]; + ivParams[1] = OSSL_PARAM_construct_end(); + + err = (emac = EVP_MAC_fetch(libCtx, "GMAC", NULL)) == NULL; + if (err == 0) { + err = (ctx = EVP_MAC_CTX_new(emac)) == NULL; + } + + /* First round with the cipher, key and IV installed by init. */ + if (err == 0) { + err = EVP_MAC_init(ctx, NULL, 0, params) != 1; + } + if (err == 0) { + err = EVP_MAC_update(ctx, msgA, sizeof(msgA)) != 1; + } + if (err == 0) { + err = EVP_MAC_final(ctx, macA, &macASz, AES_BLOCK_SIZE) != 1; + } + + /* Reset after final and MAC a different message with the cached key. */ + if (err == 0) { + err = EVP_MAC_init(ctx, NULL, 0, ivParams) != 1; + } + if (err == 0) { + err = EVP_MAC_update(ctx, msgB, sizeof(msgB)) != 1; + } + if (err == 0) { + err = EVP_MAC_final(ctx, macB, &macBSz, AES_BLOCK_SIZE) != 1; + } + + /* The first message must produce the first MAC again. */ + if (err == 0) { + err = EVP_MAC_init(ctx, NULL, 0, ivParams) != 1; + } + if (err == 0) { + err = EVP_MAC_update(ctx, msgA, sizeof(msgA)) != 1; + } + if (err == 0) { + err = EVP_MAC_final(ctx, macC, &macCSz, sizeof(macC)) != 1; + } + if ((err == 0) && ((macCSz != macASz) || + (memcmp(macC, macA, macASz) != 0))) { + PRINT_ERR_MSG("GMAC after reset doesn't match the first MAC"); + err = 1; + } + + /* A reset mid-stream must discard the data buffered so far. */ + if (err == 0) { + err = EVP_MAC_init(ctx, NULL, 0, ivParams) != 1; + } + if (err == 0) { + err = EVP_MAC_update(ctx, msgA, sizeof(msgA)) != 1; + } + if (err == 0) { + err = EVP_MAC_init(ctx, NULL, 0, ivParams) != 1; + } + if (err == 0) { + err = EVP_MAC_update(ctx, msgB, sizeof(msgB)) != 1; + } + if (err == 0) { + err = EVP_MAC_final(ctx, macD, &macDSz, sizeof(macD)) != 1; + } + if ((err == 0) && ((macDSz != macBSz) || + (memcmp(macD, macB, macBSz) != 0))) { + PRINT_ERR_MSG("GMAC after mid-stream reset covers stale data"); + err = 1; + } + + EVP_MAC_CTX_free(ctx); + EVP_MAC_free(emac); + return err; +} + +int test_gmac_reinit(void *data) +{ + int err; + unsigned char osslMacA[AES_BLOCK_SIZE]; + unsigned char osslMacB[AES_BLOCK_SIZE]; + unsigned char wpMacA[AES_BLOCK_SIZE]; + unsigned char wpMacB[AES_BLOCK_SIZE]; + + (void)data; + + PRINT_MSG("GMAC context reset with OpenSSL"); + err = test_gmac_reinit_helper(osslLibCtx, osslMacA, osslMacB); + if (err == 0) { + PRINT_MSG("GMAC context reset with wolfProvider"); + err = test_gmac_reinit_helper(wpLibCtx, wpMacA, wpMacB); + } + if ((err == 0) && (memcmp(osslMacA, wpMacA, AES_BLOCK_SIZE) != 0)) { + PRINT_ERR_MSG("First GMAC doesn't match OpenSSL"); + err = 1; + } + if ((err == 0) && (memcmp(osslMacB, wpMacB, AES_BLOCK_SIZE) != 0)) { + PRINT_ERR_MSG("GMAC after reset doesn't match OpenSSL"); + err = 1; + } + return err; +} + int test_gmac_dup(void *data) { int ret = 0; diff --git a/test/unit.c b/test/unit.c index 87152438..977eeacc 100644 --- a/test/unit.c +++ b/test/unit.c @@ -230,6 +230,7 @@ TEST_CASE test_case[] = { #endif #ifdef WP_HAVE_GMAC TEST_DECL(test_gmac_create, &flags), + TEST_DECL(test_gmac_reinit, &flags), TEST_DECL(test_gmac_dup, &flags), #endif #ifdef WP_HAVE_TLS1_PRF diff --git a/test/unit.h b/test/unit.h index a023ee21..4b9f9584 100644 --- a/test/unit.h +++ b/test/unit.h @@ -170,6 +170,7 @@ int test_cmac_size_query(void *data); #ifdef WP_HAVE_GMAC int test_gmac_create(void *data); +int test_gmac_reinit(void *data); int test_gmac_dup(void *data); #endif /* WP_HAVE_GMAC */