]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
[providers/implementations/ciphers] GCM-SIV: reject out-of-order update calls
authorBilly Brumley <bbb@iki.fi>
Tue, 14 Jul 2026 05:45:04 +0000 (01:45 -0400)
committerNikola Pajkovsky <nikolap@openssl.org>
Mon, 20 Jul 2026 06:55:44 +0000 (08:55 +0200)
For GCM-SIV:

1. AAD must precede the payload
2. the payload must be single shot

(2) was already happening, this change moves from a silent fail to
an explicit error message for multiple update calls on the payload.

For (1), this change unifies the logic for (2) one level up in the wrapper.
So the code previously allowed (1), and now errors out after this change.

Follow-up to #31906

Assisted-by: Claude:claude-opus-4-8
Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
Reviewed-by: Daniel Kubec <kubec@openssl.foundation>
Reviewed-by: Bob Beck <beck@openssl.org>
MergeDate: Mon Jul 20 06:56:04 2026
(Merged from https://github.com/openssl/openssl/pull/31940)

doc/man7/EVP_CIPHER-AES.pod
providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c
test/evp_extra_test.c

index 6da3f96a2da508a9ff6f07c61a9e303364c0c73a..0cb6d20829d4f4b1190a4fb496d3dcf5794ab6df 100644 (file)
@@ -65,9 +65,10 @@ L<EVP_EncryptInit(3)/PARAMETERS>.
 
 =head1 NOTES
 
-The AES-SIV and AES-WRAP mode implementations do not support streaming. That
-means to obtain correct results there can be only one L<EVP_EncryptUpdate(3)>
-or L<EVP_DecryptUpdate(3)> call after the initialization of the context.
+The AES-SIV, AES-WRAP, and GCM-SIV mode implementations do not support
+streaming. That means to obtain correct results there can be only one
+L<EVP_EncryptUpdate(3)> or L<EVP_DecryptUpdate(3)> call on the payload after
+the initialization of the context.
 
 When wrapping with AES-WRAP-PAD ciphers, the output buffer must be at least
 I<inl> rounded up to the cipher block size (8 bytes) plus the block size.
index 452c9f00fea9d13ace3b5f40a91b829fe918f478..9622c2dccaa2239f332e48a1e99658aef44594f1 100644 (file)
@@ -15,6 +15,7 @@
 #include "internal/deprecated.h"
 
 #include <openssl/evp.h>
+#include <openssl/proverr.h>
 #include <internal/endian.h>
 #include <prov/implementations.h>
 #include "cipher_aes_gcm_siv.h"
@@ -151,8 +152,6 @@ static int aes_gcm_siv_encrypt(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *i
     DECLARE_IS_ENDIAN;
 
     ctx->generated_tag = 0;
-    if (!ctx->speed && ctx->used_enc)
-        return 0;
     /* need to check the size of the input! */
     if (len64 > ((int64_t)1 << 36))
         return 0;
@@ -212,8 +211,6 @@ static int aes_gcm_siv_decrypt(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *i
     DECLARE_IS_ENDIAN;
 
     ctx->generated_tag = 0;
-    if (!ctx->speed && ctx->used_dec)
-        return 0;
     /* need to check the size of the input! */
     if (len64 > ((int64_t)1 << 36))
         return 0;
@@ -285,6 +282,18 @@ static int aes_gcm_siv_cipher(void *vctx, unsigned char *out,
     if (in == NULL)
         return aes_gcm_siv_finish(ctx);
 
+    /*
+     * SIV derives the CTR IV from the tag, which depends on the whole plaintext,
+     * so the payload cannot be streamed.
+     * Payload must arrive in a single update, after which the tag is fixed.
+     * Any later AAD or payload update is therefore out of order and errors out.
+     * The speed benchmark test is exempt.
+     */
+    if (!ctx->speed && (ctx->used_enc || ctx->used_dec)) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_UPDATE_CALL_OUT_OF_ORDER);
+        return 0;
+    }
+
     /* Deal with associated data */
     if (out == NULL)
         return aes_gcm_siv_aad(ctx, in, len);
index 5c37102088935e277c9f992b0fec4b57ab02b29f..22e468998294b4e19217dad4cad22a0b38e07f7c 100644 (file)
@@ -6144,7 +6144,6 @@ static int test_evp_aead_late_aad(int idx)
         || info->mode == EVP_CIPH_GCM_MODE /* rejects, raises 102 PROV_R_CIPHER_OPERATION_FAILED */
         || info->mode == EVP_CIPH_CCM_MODE /* fails at first AAD */
         || info->mode == EVP_CIPH_OCB_MODE /* accepts late AAD */
-        || info->mode == EVP_CIPH_GCM_SIV_MODE /* accepts late AAD */
         /* skip TLS stitched MTE cipher */
         || EVP_CIPHER_is_a(info->ciph, "AES-128-CBC-HMAC-SHA1")
         /* skip TLS stitched MTE cipher */