From 1df0bbf358fd56e37decd9cb3893ca3707ef0061 Mon Sep 17 00:00:00 2001 From: Billy Brumley Date: Tue, 4 Aug 2026 07:35:48 -0400 Subject: [PATCH] Check the tag on EVP_Cipher() finalize: Poly1305 and OCB AEADs For the affected OpenSSL built-in provider AEAD implementations, EVP_Cipher(ctx, out, NULL, 0) reaches the ccipher callback as a NULL-input terminal call. OCB and ChaCha20-Poly1305 took an early exit on an empty message, with or without AAD, and returned success without comparing an explicitly supplied tag. Consequently a corrupted tag was accepted before this change. Make these built-in callbacks perform their terminal tag operation, aligning their explicit-tag handling with the streaming Final path without defining NULL input as part of the generic EVP_Cipher() contract. AES-GCM-SIV also failed to generate a tag when Final was its first empty-message operation. Generate the tag in that case and propagate failures from the matching empty-message decrypt operation. The stable ChaCha20-Poly1305 implementation aliases Update to the one-shot cipher callback, so this backport introduces a dedicated Update callback to preserve zero-length Update as a no-op. Follow-up to #31555 Fixes #32258 Fixes CVE-2026-75803 Assisted-by: Claude:claude-opus-4-8 Assisted-by: Codex:gpt-5.6-sol (cherry picked from commit 5741d29a5f356e05262cd0936a472a9961398d53) Co-authored-by: Mounir IDRASSI Reviewed-by: Bob Beck Reviewed-by: Tomas Mraz Merge-date: Wed Aug 19 17:43:24 2026 Merged-from: https://github.com/openssl/openssl/pull/32417 --- .../implementations/ciphers/cipher_aes_ocb.c | 4 +++ .../ciphers/cipher_chacha20_poly1305.c | 27 ++++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/providers/implementations/ciphers/cipher_aes_ocb.c b/providers/implementations/ciphers/cipher_aes_ocb.c index 71b1fda63e..24845f32f4 100644 --- a/providers/implementations/ciphers/cipher_aes_ocb.c +++ b/providers/implementations/ciphers/cipher_aes_ocb.c @@ -509,6 +509,10 @@ static int aes_ocb_cipher(void *vctx, unsigned char *out, size_t *outl, if (!ossl_prov_is_running()) return 0; + /* NULL input indicates Final, which must generate or check the tag. */ + if (in == NULL) + return aes_ocb_block_final(vctx, out, outl, outsize); + if (outsize < inl) { ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); return 0; diff --git a/providers/implementations/ciphers/cipher_chacha20_poly1305.c b/providers/implementations/ciphers/cipher_chacha20_poly1305.c index 4e4c8b0e15..7e7d1e0c1a 100644 --- a/providers/implementations/ciphers/cipher_chacha20_poly1305.c +++ b/providers/implementations/ciphers/cipher_chacha20_poly1305.c @@ -1,5 +1,5 @@ /* - * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2019-2026 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -30,11 +30,11 @@ static OSSL_FUNC_cipher_get_params_fn chacha20_poly1305_get_params; static OSSL_FUNC_cipher_get_ctx_params_fn chacha20_poly1305_get_ctx_params; static OSSL_FUNC_cipher_set_ctx_params_fn chacha20_poly1305_set_ctx_params; static OSSL_FUNC_cipher_cipher_fn chacha20_poly1305_cipher; +static OSSL_FUNC_cipher_update_fn chacha20_poly1305_update; static OSSL_FUNC_cipher_final_fn chacha20_poly1305_final; static OSSL_FUNC_cipher_gettable_ctx_params_fn chacha20_poly1305_gettable_ctx_params; static OSSL_FUNC_cipher_settable_ctx_params_fn chacha20_poly1305_settable_ctx_params; #define chacha20_poly1305_gettable_params ossl_cipher_generic_gettable_params -#define chacha20_poly1305_update chacha20_poly1305_cipher static void *chacha20_poly1305_newctx(void *provctx) { @@ -301,11 +301,6 @@ static int chacha20_poly1305_cipher(void *vctx, unsigned char *out, if (!ossl_prov_is_running()) return 0; - if (inl == 0) { - *outl = 0; - return 1; - } - if (outsize < inl) { ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); return 0; @@ -317,6 +312,24 @@ static int chacha20_poly1305_cipher(void *vctx, unsigned char *out, return 1; } +static int chacha20_poly1305_update(void *vctx, unsigned char *out, + size_t *outl, size_t outsize, + const unsigned char *in, size_t inl) +{ + /* + * A zero-length update is a no-op. Only EVP_Cipher() and Final produce or + * check the authentication tag. + */ + if (inl == 0) { + if (!ossl_prov_is_running()) + return 0; + *outl = 0; + return 1; + } + + return chacha20_poly1305_cipher(vctx, out, outl, outsize, in, inl); +} + static int chacha20_poly1305_final(void *vctx, unsigned char *out, size_t *outl, size_t outsize) { -- 2.55.0