Commit 78f60e095d for openssl.org

commit 78f60e095d0dbcd0438cb360eeedd73d7d10c3ac
Author: Milan Broz <gmazyland@gmail.com>
Date:   Mon May 4 16:11:32 2026 +0200

    chacha_poly: Use IV_STATE guard to prevent IV reuse

    If IV was set for Chacha20-Poly1305, code should not
    allow reusing IV after calling CipherFinal.

    Use iv_state (as used in GCM or OCB mode) to prevent that.

    Thanks to Alex Gaynor for reporting the issue.

    Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
    Reviewed-by: Paul Dale <paul.dale@oracle.com>
    MergeDate: Tue May 12 05:14:09 2026
    (Merged from https://github.com/openssl/openssl/pull/31104)

diff --git a/providers/implementations/ciphers/cipher_chacha20_poly1305.c b/providers/implementations/ciphers/cipher_chacha20_poly1305.c
index e95289598a..a48b9c3725 100644
--- a/providers/implementations/ciphers/cipher_chacha20_poly1305.c
+++ b/providers/implementations/ciphers/cipher_chacha20_poly1305.c
@@ -36,7 +36,6 @@ 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)
 {
@@ -190,6 +189,7 @@ static int chacha20_poly1305_set_ctx_params(void *vctx,
             ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
             return 0;
         }
+        ctx->iv_state = IV_STATE_UNINITIALISED;
     }

     if (p.tag != NULL) {
@@ -249,9 +249,11 @@ static int chacha20_poly1305_einit(void *vctx, const unsigned char *key,
     ret = ossl_cipher_generic_einit(vctx, key, keylen, iv, ivlen, NULL);
     if (ret && iv != NULL) {
         PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+        PROV_CHACHA20_POLY1305_CTX *cctx = (PROV_CHACHA20_POLY1305_CTX *)vctx;
         PROV_CIPHER_HW_CHACHA20_POLY1305 *hw = (PROV_CIPHER_HW_CHACHA20_POLY1305 *)ctx->hw;

         hw->initiv(ctx);
+        cctx->iv_state = IV_STATE_BUFFERED;
     }
     if (ret && !chacha20_poly1305_set_ctx_params(vctx, params))
         ret = 0;
@@ -268,9 +270,11 @@ static int chacha20_poly1305_dinit(void *vctx, const unsigned char *key,
     ret = ossl_cipher_generic_dinit(vctx, key, keylen, iv, ivlen, NULL);
     if (ret && iv != NULL) {
         PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+        PROV_CHACHA20_POLY1305_CTX *cctx = (PROV_CHACHA20_POLY1305_CTX *)vctx;
         PROV_CIPHER_HW_CHACHA20_POLY1305 *hw = (PROV_CIPHER_HW_CHACHA20_POLY1305 *)ctx->hw;

         hw->initiv(ctx);
+        cctx->iv_state = IV_STATE_BUFFERED;
     }
     if (ret && !chacha20_poly1305_set_ctx_params(vctx, params))
         ret = 0;
@@ -303,6 +307,18 @@ 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)
+{
+    PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)vctx;
+
+    if (ctx->iv_state == IV_STATE_FINISHED)
+        return 0;
+
+    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)
 {
@@ -322,6 +338,9 @@ static int chacha20_poly1305_final(void *vctx, unsigned char *out, size_t *outl,
         return 0;

     *outl = 0;
+
+    /* Don't reuse the IV */
+    ctx->iv_state = IV_STATE_FINISHED;
     return 1;
 }

diff --git a/providers/implementations/ciphers/cipher_chacha20_poly1305.h b/providers/implementations/ciphers/cipher_chacha20_poly1305.h
index 7a4575fa2e..6914966f6e 100644
--- a/providers/implementations/ciphers/cipher_chacha20_poly1305.h
+++ b/providers/implementations/ciphers/cipher_chacha20_poly1305.h
@@ -33,6 +33,7 @@ typedef struct {
     size_t tag_len;
     size_t tls_payload_length;
     size_t tls_aad_pad_sz;
+    unsigned int iv_state; /* set to one of IV_STATE_XXX */
 } PROV_CHACHA20_POLY1305_CTX;

 typedef struct prov_cipher_hw_chacha_aead_st {