From: slontis Date: Fri, 7 Nov 2025 04:22:48 +0000 (+1100) Subject: AES-GCM: Allow the IV getter to generate the IV if it is not set yet. X-Git-Tag: 4.0-PRE-CLANG-FORMAT-WEBKIT~228 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2d74855164c24f0b43cafd0c52225cd2b6621b86;p=thirdparty%2Fopenssl.git AES-GCM: Allow the IV getter to generate the IV if it is not set yet. The EVP_CipherInit API allows mutiple calls to set up parameters such as the key and iv. If the iv is not specified for encryption, then it is generated internally during the update phase. If you try to get the IV before the update it would return an error. This PR allows the getter to generate the IV early for this case. This also means that the gen_rand variable needs to be reset to 0 if an iv is manually set after the getter is called. Issue found by @davidmakepeace Reviewed-by: Tomas Mraz Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/29094) --- diff --git a/providers/implementations/ciphers/ciphercommon_gcm.c b/providers/implementations/ciphers/ciphercommon_gcm.c index 5bad232bfdf..ca13834b983 100644 --- a/providers/implementations/ciphers/ciphercommon_gcm.c +++ b/providers/implementations/ciphers/ciphercommon_gcm.c @@ -26,6 +26,7 @@ static int gcm_tls_cipher(PROV_GCM_CTX *ctx, unsigned char *out, size_t *padlen, static int gcm_cipher_internal(PROV_GCM_CTX *ctx, unsigned char *out, size_t *padlen, const unsigned char *in, size_t len); +static int on_preupdate_generate_iv(PROV_GCM_CTX *ctx); /* * Called from EVP_CipherInit when there is currently no context via @@ -63,6 +64,7 @@ static int gcm_init(void *vctx, const unsigned char *key, size_t keylen, ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); return 0; } + ctx->iv_gen_rand = 0; ctx->ivlen = ivlen; memcpy(ctx->iv, iv, ivlen); ctx->iv_state = IV_STATE_BUFFERED; @@ -178,8 +180,12 @@ int ossl_gcm_get_ctx_params(void *vctx, OSSL_PARAM params[]) } } + /* + * Note p.updiv and p.iv are aliases that get the same information, + * so any code changes should be duplicated below. + */ if (p.iv != NULL) { - if (ctx->iv_state == IV_STATE_UNINITIALISED) + if (!on_preupdate_generate_iv(ctx)) return 0; if (ctx->ivlen > p.iv->data_size) { ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); @@ -190,9 +196,8 @@ int ossl_gcm_get_ctx_params(void *vctx, OSSL_PARAM params[]) return 0; } } - if (p.updiv != NULL) { - if (ctx->iv_state == IV_STATE_UNINITIALISED) + if (!on_preupdate_generate_iv(ctx)) return 0; if (ctx->ivlen > p.updiv->data_size) { ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); @@ -400,6 +405,21 @@ static int gcm_iv_generate(PROV_GCM_CTX *ctx, int offset) return 1; } +/* + * If we try to grab the iv before the update - it wont be generated yet, + * so we generate it here for this case. + */ +static int on_preupdate_generate_iv(PROV_GCM_CTX *ctx) +{ + if (ctx->iv_state == IV_STATE_UNINITIALISED) { + if (ctx->tls_aad_len != UNINITIALISED_SIZET) + return 0; + if (!ctx->enc || !gcm_iv_generate(ctx, 0)) + return 0; + } + return 1; +} + static int gcm_cipher_internal(PROV_GCM_CTX *ctx, unsigned char *out, size_t *padlen, const unsigned char *in, size_t len) diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c index ac1b62c4fa2..52d684ea678 100644 --- a/test/evp_extra_test.c +++ b/test/evp_extra_test.c @@ -4370,13 +4370,12 @@ static int test_evp_iv_aes(int idx) 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; - unsigned char ciphertext[32], oiv[16], iv[16]; - unsigned char *ref_iv; + unsigned char ciphertext[32], oiv1[16], oiv[16], iv[16], uiv[16]; + unsigned char *ref_iv, *start_iv = init_iv; unsigned char cbc_state[16] = { 0x10, 0x2f, 0x05, 0xcc, 0xc2, 0x55, 0x72, 0xb9, 0x88, 0xe6, 0x4a, 0x17, 0x10, 0x74, 0x22, 0x5e }; - unsigned char ofb_state[16] = { 0x76, 0xe6, 0x66, 0x61, 0xd0, 0x8a, 0xe4, 0x64, 0xdd, 0x66, 0xbf, 0x00, 0xf0, 0xe3, 0x6f, 0xfd @@ -4444,6 +4443,12 @@ static int test_evp_iv_aes(int idx) ref_iv = gcm_state; ref_len = sizeof(gcm_state); break; + case 12: + ref_iv = NULL; + ref_len = 0; + start_iv = NULL; /* This will cause a random iv to be generated internally */ + type = EVP_CIPHER_fetch(testctx, "aes-128-gcm", testpropq); + break; case 4: type = EVP_aes_128_ccm(); /* FALLTHROUGH */ @@ -4474,7 +4479,8 @@ static int test_evp_iv_aes(int idx) if (!TEST_ptr(type) || !TEST_ptr((ctx = EVP_CIPHER_CTX_new())) - || !TEST_true(EVP_EncryptInit_ex(ctx, type, NULL, key, init_iv)) + || !TEST_true(EVP_EncryptInit_ex(ctx, type, NULL, key, start_iv)) + || !TEST_true(EVP_CIPHER_CTX_get_original_iv(ctx, oiv1, sizeof(oiv1))) || !TEST_true(EVP_EncryptUpdate(ctx, ciphertext, &len, msg, (int)sizeof(msg))) || !TEST_true(EVP_CIPHER_CTX_get_original_iv(ctx, oiv, sizeof(oiv))) @@ -4486,19 +4492,21 @@ static int test_evp_iv_aes(int idx) if (!TEST_size_t_gt(ivlen, 0)) goto err; - if (!TEST_mem_eq(init_iv, ivlen, oiv, ivlen) - || !TEST_mem_eq(ref_iv, ref_len, iv, ivlen)) + if (start_iv == NULL) + start_iv = oiv1; + if (!TEST_mem_eq(start_iv, ivlen, oiv, ivlen) + || (ref_iv != NULL && !TEST_mem_eq(ref_iv, ref_len, iv, ivlen))) goto err; /* CBC, OFB, and CFB modes: the updated iv must be reset after reinit */ if (!TEST_true(EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, NULL)) - || !TEST_true(EVP_CIPHER_CTX_get_updated_iv(ctx, iv, sizeof(iv)))) + || !TEST_true(EVP_CIPHER_CTX_get_updated_iv(ctx, uiv, sizeof(uiv)))) goto err; if (iv_reset) { - if (!TEST_mem_eq(init_iv, ivlen, iv, ivlen)) + if (!TEST_mem_eq(start_iv, ivlen, uiv, ivlen)) goto err; } else { - if (!TEST_mem_eq(ref_iv, ivlen, iv, ivlen)) + if (!TEST_mem_eq(uiv, ivlen, iv, ivlen)) goto err; } @@ -6986,7 +6994,7 @@ int setup_tests(void) ADD_ALL_TESTS(test_pkey_ctx_fail_without_provider, 2); ADD_TEST(test_rand_agglomeration); - ADD_ALL_TESTS(test_evp_iv_aes, 12); + ADD_ALL_TESTS(test_evp_iv_aes, 13); #ifndef OPENSSL_NO_DES ADD_ALL_TESTS(test_evp_iv_des, 6); #endif