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 {