]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MINOR: jwe: enforce GCM tag length to 128 bits
authorRemi Tricot-Le Breton <rlebreton@haproxy.com>
Tue, 26 May 2026 14:20:52 +0000 (16:20 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 26 May 2026 16:14:21 +0000 (18:14 +0200)
Two fixes addressing cryptographic and parsing correctness issues:

1. Enforce 16-byte GCM authentication tag in decrypt_ciphertext()

   The base64url-decoded 5th JWE component (authentication tag) was passed
   directly to EVP_CTRL_AEAD_SET_TAG with its attacker-controlled length.
   OpenSSL accepts 1-16 byte GCM tags and only verifies that many bytes, so
   a 1-byte tag reduces forgery work factor to ~256. RFC 7518 mandates 128-bit
   (16 byte) tags for A*GCM. The CBC-HMAC path already enforced correct length,
   confirming this was an oversight.

   Fix: Add (*aead_tag)->data != 16 check before the GCM branch in
   decrypt_ciphertext(), rejecting any non-16-byte tag.

   Introduced by 416b87d5db (JWE A*GCM support).

2. Enforce 16-byte GCMKW tag in parse_jose() decode_jose_field()

   The $.tag field from the attacker-supplied protected header in A*GCMKW
   key-wrap was similarly decoded without length enforcement. Fix: Add a
   size != 16 check for fields named ".tag" in decode_jose_field() when
   called from the GCMKW path.

   Introduced by 026652a7eb (GCMKW tag field parsing).

src/jwe.c

index 27762c8d3278c999c9ac9579d0704b69e8b74c69..0471fb8043585291cb947a07207293d26f82016d 100644 (file)
--- a/src/jwe.c
+++ b/src/jwe.c
@@ -266,8 +266,10 @@ static int parse_jose(struct buffer *decoded_jose, int *alg, int *enc, struct jo
 
 
        if (gcm) {
-               /* Look for "tag" field (used by aes gcm encryption) */
-               if (decode_jose_field(decoded_jose, "$.tag", &jose_fields->tag, 1))
+               /* Look for "tag" field (used by aes gcm encryption).
+                * GCMKW tag must be exactly 16 bytes per RFC 7518 */
+               if (decode_jose_field(decoded_jose, "$.tag", &jose_fields->tag, 1) ||
+                   b_data(jose_fields->tag) != 16)
                        goto end;
 
                /* Look for "iv" field (used by aes gcm encryption) */
@@ -556,6 +558,12 @@ static int decrypt_ciphertext(jwe_enc enc, struct jwt_item items[JWE_ELT_MAX],
        (*aead_tag)->data = size;
 
        if (gcm) {
+               /* RFC 7518 mandates a 128-bit (16 byte) authentication tag for A*GCM.
+                * OpenSSL accepts 1-16 bytes but only verifies that many bytes, so a
+                * truncated tag reduces forgery work factor to ~256 per byte short. */
+               if ((*aead_tag)->data != 16)
+                       goto end;
+
                aad = alloc_trash_chunk();
                if (!aad)
                        goto end;