]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
chacha_poly: Use IV_STATE guard to prevent IV reuse
authorMilan Broz <gmazyland@gmail.com>
Mon, 4 May 2026 14:11:32 +0000 (16:11 +0200)
committerNikola Pajkovsky <nikolap@openssl.org>
Tue, 12 May 2026 05:13:44 +0000 (07:13 +0200)
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)

providers/implementations/ciphers/cipher_chacha20_poly1305.c
providers/implementations/ciphers/cipher_chacha20_poly1305.h

index e95289598a166a1eca1a9be802e612fc2c8348e2..a48b9c372516686cfdae8950a26e854a9d65ce6f 100644 (file)
@@ -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;
 }
 
index 7a4575fa2e817cc2a54fec20a55f9da2c8ff4e75..6914966f6e6d801a23172216ffcf42cf1ad4a6a7 100644 (file)
@@ -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 {