From 323f0b6e7d530a4cb4336d50c88cb70f3ac2a451 Mon Sep 17 00:00:00 2001
From: Viktor Dukhovni <viktor@openssl.org>
Date: Mon, 18 May 2026 18:09:44 +1000
Subject: [PATCH] Apply the buffered IV on the AES-OCB EVP_Cipher() path
Conflict: NA
aes_ocb_cipher(), the OCB provider's OSSL_FUNC_CIPHER_CIPHER slot,
processed input without flushing the buffered IV into the OCB
context. Effective nonce was 0 regardless of the caller's IV;
EVP_*Final_ex() then emitted a tag depending only on (key, iv).
This gave (key, nonce) reuse and single-query universal forgery on
the EVP_Cipher() path.
Apply update_iv() at the head of aes_ocb_cipher() to mirror the
streaming handler. The matching GCM one-shot does this already.
Add a cross-driver round-trip test for AES-{GCM,CCM,OCB} and
ChaCha20-Poly1305 in test/evp_extra_test.c. Each cipher is
exercised with and without AAD; the no-AAD case is needed because
any prior EVP_CipherUpdate(NULL, aad, ...) routes through the
streaming handler and applies the IV itself, masking the bug.
Fixes CVE-2026-45445
Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
MergeDate: Mon Jun 8 20:02:00 2026
(cherry picked from commit 50c95c5d1e83f4f46a555dfa7fd9c632d3eba9dc)
.../implementations/ciphers/cipher_aes_ocb.c | 13 +
test/evp_extra_test.c | 266 ++++++++++++++++++
2 files changed, 279 insertions(+)
@@ -510,6 +510,19 @@ static int aes_ocb_cipher(void *vctx, unsigned char *out, size_t *outl,
return 0;
}
+ /*
+ * Mirror the streaming handler: refuse if the key has not been set,
+ * and push the buffered IV into the OCB context before any data is
+ * processed. Without this, CRYPTO_ocb128_encrypt/decrypt runs with
+ * Offset_0 = 0 regardless of the caller's IV -- catastrophic
+ * (key, nonce) reuse, and a subsequent EVP_*Final_ex() emits a tag
+ * that is a function of (key, iv) only.
+ */
+ if (!ctx->key_set || !update_iv(ctx)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return 0;
+ }
+
if (!aes_generic_ocb_cipher(ctx, in, out, inl)) {
ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
return 0;