From: Billy Brumley Date: Tue, 14 Jul 2026 05:45:04 +0000 (-0400) Subject: [providers/implementations/ciphers] GCM-SIV: reject out-of-order update calls X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=4bf85819b7cba298108ea03f3e2aa067f20ded72;p=thirdparty%2Fopenssl.git [providers/implementations/ciphers] GCM-SIV: reject out-of-order update calls 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 Reviewed-by: Daniel Kubec Reviewed-by: Bob Beck MergeDate: Mon Jul 20 06:56:04 2026 (Merged from https://github.com/openssl/openssl/pull/31940) --- diff --git a/doc/man7/EVP_CIPHER-AES.pod b/doc/man7/EVP_CIPHER-AES.pod index 6da3f96a2da..0cb6d20829d 100644 --- a/doc/man7/EVP_CIPHER-AES.pod +++ b/doc/man7/EVP_CIPHER-AES.pod @@ -65,9 +65,10 @@ L. =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 -or L 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 or L 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 rounded up to the cipher block size (8 bytes) plus the block size. diff --git a/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c b/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c index 452c9f00fea..9622c2dccaa 100644 --- a/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c +++ b/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c @@ -15,6 +15,7 @@ #include "internal/deprecated.h" #include +#include #include #include #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); diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c index 5c371020889..22e46899829 100644 --- a/test/evp_extra_test.c +++ b/test/evp_extra_test.c @@ -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 */