[PATCH 4/7] cipher-ocb: fix split-AAD abort at L-table wrap boundary
Jussi Kivilinna
jussi.kivilinna at iki.fi
Fri Jul 24 20:48:03 CEST 2026
* cipher/cipher-ocb.c (_gcry_cipher_ocb_authenticate): Pass aad_nblocks
to ocb_get_L_big in buffered-block path, not aad_nblocks + 1.
* tests/basic.c (ocb_aad_tag, check_ocb_cipher_aad_tablewrap): New.
(check_ocb_cipher): Call check_ocb_cipher_aad_tablewrap.
--
Buffered-AAD path incremented aad_nblocks and then, at each L-table
wrap (every 65536 blocks), asked ocb_get_L_big for aad_nblocks + 1.
That helper needs a wrap-aligned block number, so the off-by-one hits
"Assertion `ntz >= 16' failed" and aborts whenever split AAD finishes
the wrap block through buffered path. Full-blocks path already passes
the correct count.
New test feeds about 1 MiB of AAD split so the wrap block finishes in
buffered path, comparing tag against one-shot.
Reported-by: JEAN Jeremy <Jeremy.Jean at ssi.gouv.fr>
Signed-off-by: Jussi Kivilinna <jussi.kivilinna at iki.fi>
---
cipher/cipher-ocb.c | 2 +-
tests/basic.c | 60 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 61 insertions(+), 1 deletion(-)
diff --git a/cipher/cipher-ocb.c b/cipher/cipher-ocb.c
index 05e56f44..8d4f576e 100644
--- a/cipher/cipher-ocb.c
+++ b/cipher/cipher-ocb.c
@@ -281,7 +281,7 @@ _gcry_cipher_ocb_authenticate (gcry_cipher_hd_t c, const unsigned char *abuf,
if ((c->u_mode.ocb.aad_nblocks % table_maxblks) == 0)
{
/* Table overflow, L needs to be generated. */
- ocb_get_L_big(c, c->u_mode.ocb.aad_nblocks + 1, l_tmp);
+ ocb_get_L_big(c, c->u_mode.ocb.aad_nblocks, l_tmp);
}
else
{
diff --git a/tests/basic.c b/tests/basic.c
index 0dceebd7..1f4273ea 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -9474,6 +9474,65 @@ check_ocb_cipher_largebuf (int algo, int keylen, const char *tagexpect)
}
+static void
+ocb_aad_tag (const unsigned char key[16], const unsigned char nonce[12],
+ const unsigned char *aad, size_t len0, size_t len1,
+ unsigned char tag[16])
+{
+ gcry_cipher_hd_t hd;
+ gcry_error_t err;
+ unsigned char out[16];
+
+ err = gcry_cipher_open (&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_OCB, 0);
+ if (!err)
+ err = gcry_cipher_setkey (hd, key, 16);
+ if (!err)
+ err = gcry_cipher_setiv (hd, nonce, 12);
+ if (!err)
+ err = gcry_cipher_authenticate (hd, aad, len0);
+ if (!err && len1)
+ err = gcry_cipher_authenticate (hd, aad + len0, len1);
+ if (!err)
+ err = gcry_cipher_final (hd);
+ if (!err)
+ err = gcry_cipher_encrypt (hd, out, sizeof out, aad, sizeof out);
+ if (!err)
+ err = gcry_cipher_gettag (hd, tag, 16);
+ gcry_cipher_close (hd);
+ if (err)
+ fail ("ocb aad tablewrap: %s\n", gpg_strerror (err));
+}
+
+
+static void
+check_ocb_cipher_aad_tablewrap (void)
+{
+ static const unsigned char key[16] _GCRY_GCC_ATTR_NONSTRING =
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f";
+ static const unsigned char nonce[12] _GCRY_GCC_ATTR_NONSTRING =
+ "\xbb\xaa\x99\x88\x77\x66\x55\x44\x33\x22\x11\x0d";
+ const size_t aadlen = (size_t)65536 * 16; /* fills one OCB L-table wrap */
+ unsigned char tag_ref[16], tag_split[16];
+ unsigned char *aad;
+ size_t i;
+
+ if (verbose)
+ fprintf (stderr, " checking OCB AAD split across L-table boundary\n");
+
+ aad = xmalloc (aadlen);
+ for (i = 0; i < aadlen; i++)
+ aad[i] = (unsigned char)i;
+
+ ocb_aad_tag (key, nonce, aad, aadlen, 0, tag_ref);
+ ocb_aad_tag (key, nonce, aad, aadlen - 15, 15, tag_split);
+
+ if (memcmp (tag_ref, tag_split, 16))
+ fail ("ocb aad tablewrap: split tag mismatch\n");
+
+ xfree (aad);
+}
+
+
static void
check_ocb_cipher_splitaad (void)
{
@@ -9766,6 +9825,7 @@ check_ocb_cipher (void)
/* Check that the AAD data is correctly buffered. */
check_ocb_cipher_splitaad ();
+ check_ocb_cipher_aad_tablewrap ();
}
--
2.53.0
More information about the Gcrypt-devel
mailing list