Commit a4ee6965a0 for openssl.org
commit a4ee6965a09273425af35cabfefa08862c388fc3
Author: Mounir IDRASSI <mounir.idrassi@idrix.fr>
Date: Sun Jun 28 07:50:14 2026 +0900
Reject AES-XTS operations without an IV
Commit 774525b38bd4 moved AES-XTS to an implementation-specific cipher
function but did not carry over the generic iv_set guard. This allowed
AES-XTS operations initialized with a NULL IV to proceed.
Restore the missing iv_set check before processing input and add an
evp_extra_test regression covering both a valid-IV control and the
missing-IV failure case.
Fixes #31755
Reviewed-by: Simo Sorce <simo@redhat.com>
Reviewed-by: Paul Dale <paul.dale@oracle.com>
MergeDate: Wed Jul 8 18:16:55 2026
(Merged from https://github.com/openssl/openssl/pull/31756)
diff --git a/providers/implementations/ciphers/cipher_aes_xts.c b/providers/implementations/ciphers/cipher_aes_xts.c
index 9be87374d1..54820469a9 100644
--- a/providers/implementations/ciphers/cipher_aes_xts.c
+++ b/providers/implementations/ciphers/cipher_aes_xts.c
@@ -180,7 +180,8 @@ static int aes_xts_cipher(void *vctx, unsigned char *out, size_t *outl,
} else
#endif
{
- if (ctx->xts.key1 == NULL || ctx->xts.key2 == NULL)
+ if (ctx->xts.key1 == NULL || ctx->xts.key2 == NULL
+ || !ctx->base.iv_set)
return 0;
}
diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c
index e1b80256bd..7c8059350a 100644
--- a/test/evp_extra_test.c
+++ b/test/evp_extra_test.c
@@ -7901,6 +7901,70 @@ end:
return ret;
}
+static int test_aes_xts_rejects_missing_iv(void)
+{
+ static const unsigned char key[32] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
+ };
+ static const unsigned char in[32] = {
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+ 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
+ 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00
+ };
+ static const unsigned char iv[16] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02
+ };
+ EVP_CIPHER_CTX *ctx = NULL;
+ EVP_CIPHER *cipher = NULL;
+ unsigned char out[sizeof(in)];
+ int outl = 0;
+ int ret = 0;
+
+ if ((cipher = EVP_CIPHER_fetch(testctx, "AES-128-XTS", testpropq)) == NULL)
+ return TEST_skip("AES-128-XTS cipher is not available");
+
+ /* Initialize with a valid IV as a positive control */
+ if (!TEST_ptr(ctx = EVP_CIPHER_CTX_new())
+ || !TEST_true(EVP_EncryptInit_ex2(ctx, cipher, key, iv, NULL))
+ || !TEST_true(EVP_EncryptUpdate(ctx, out, &outl, in, sizeof(in)))
+ || !TEST_int_eq(outl, (int)sizeof(in)))
+ goto err;
+
+ EVP_CIPHER_CTX_free(ctx);
+ ctx = NULL;
+ outl = 0;
+
+ if (!TEST_ptr(ctx = EVP_CIPHER_CTX_new()))
+ goto err;
+
+ /* Initialize with a NULL IV, which may fail immediately */
+ ERR_set_mark();
+ if (!EVP_EncryptInit_ex2(ctx, cipher, key, NULL, NULL)) {
+ ERR_pop_to_mark();
+ ret = 1;
+ goto err;
+ }
+
+ /* Test EVP_EncryptUpdate after NULL IV initialization, which should fail */
+ if (!TEST_false(EVP_EncryptUpdate(ctx, out, &outl, in, sizeof(in)))) {
+ ERR_clear_last_mark();
+ goto err;
+ }
+ ERR_pop_to_mark();
+
+ ret = 1;
+
+err:
+ EVP_CIPHER_free(cipher);
+ EVP_CIPHER_CTX_free(ctx);
+ return ret;
+}
+
/*
* Cross-driver round-trip test for AEAD one-shot vs streaming paths.
*
@@ -9098,6 +9162,7 @@ int setup_tests(void)
ADD_TEST(test_invalid_ctx_for_digest);
ADD_TEST(test_evp_cipher_negative_length);
+ ADD_TEST(test_aes_xts_rejects_missing_iv);
ADD_TEST(test_evp_cipher_pipeline);