From 7014b204b6fbd448c7b653aa69762581d8c463ab Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sun, 12 Jul 2026 17:36:34 +0200 Subject: [PATCH] Interleave stream encryption and Poly1305 on cache-sized chunks --- .../chacha20poly1305/aead_chacha20poly1305.c | 36 ++++++++++++++++--- .../aead_xchacha20poly1305.c | 28 +++++++++++++-- .../crypto_secretbox/crypto_secretbox_easy.c | 24 ++++++++++--- 3 files changed, 78 insertions(+), 10 deletions(-) diff --git a/src/libsodium/crypto_aead/chacha20poly1305/aead_chacha20poly1305.c b/src/libsodium/crypto_aead/chacha20poly1305/aead_chacha20poly1305.c index e99a4d10..64560c7a 100644 --- a/src/libsodium/crypto_aead/chacha20poly1305/aead_chacha20poly1305.c +++ b/src/libsodium/crypto_aead/chacha20poly1305/aead_chacha20poly1305.c @@ -17,6 +17,8 @@ static const unsigned char _pad0[16] = { 0 }; +#define STREAM_POLY1305_CHUNK 131072 + int crypto_aead_chacha20poly1305_encrypt_detached(unsigned char *c, unsigned char *mac, @@ -42,9 +44,22 @@ crypto_aead_chacha20poly1305_encrypt_detached(unsigned char *c, STORE64_LE(slen, (uint64_t) adlen); crypto_onetimeauth_poly1305_update(&state, slen, sizeof slen); - crypto_stream_chacha20_xor_ic(c, m, mlen, npub, 1U, k); + { + unsigned long long off = 0U; + uint64_t ic = 1U; - crypto_onetimeauth_poly1305_update(&state, c, mlen); + COMPILER_ASSERT(STREAM_POLY1305_CHUNK % 64U == 0U); + while (off < mlen) { + unsigned long long cl = mlen - off; + if (cl > STREAM_POLY1305_CHUNK) { + cl = STREAM_POLY1305_CHUNK; + } + crypto_stream_chacha20_xor_ic(c + off, m + off, cl, npub, ic, k); + crypto_onetimeauth_poly1305_update(&state, c + off, cl); + off += cl; + ic += cl / 64U; + } + } STORE64_LE(slen, (uint64_t) mlen); crypto_onetimeauth_poly1305_update(&state, slen, sizeof slen); @@ -112,9 +127,22 @@ crypto_aead_chacha20poly1305_ietf_encrypt_detached(unsigned char *c, crypto_onetimeauth_poly1305_update(&state, ad, adlen); crypto_onetimeauth_poly1305_update(&state, _pad0, (0x10 - adlen) & 0xf); - crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, npub, 1U, k); + { + unsigned long long off = 0U; + uint32_t ic = 1U; - crypto_onetimeauth_poly1305_update(&state, c, mlen); + while (off < mlen) { + unsigned long long cl = mlen - off; + if (cl > STREAM_POLY1305_CHUNK) { + cl = STREAM_POLY1305_CHUNK; + } + crypto_stream_chacha20_ietf_xor_ic(c + off, m + off, cl, + npub, ic, k); + crypto_onetimeauth_poly1305_update(&state, c + off, cl); + off += cl; + ic += (uint32_t) (cl / 64U); + } + } crypto_onetimeauth_poly1305_update(&state, _pad0, (0x10 - mlen) & 0xf); STORE64_LE(slen, (uint64_t) adlen); diff --git a/src/libsodium/crypto_aead/xchacha20poly1305/aead_xchacha20poly1305.c b/src/libsodium/crypto_aead/xchacha20poly1305/aead_xchacha20poly1305.c index 0d5cd3cf..3bf40845 100644 --- a/src/libsodium/crypto_aead/xchacha20poly1305/aead_xchacha20poly1305.c +++ b/src/libsodium/crypto_aead/xchacha20poly1305/aead_xchacha20poly1305.c @@ -19,6 +19,8 @@ static const unsigned char _pad0[16] = { 0 }; +#define STREAM_POLY1305_CHUNK 131072 + static int _encrypt_detached(unsigned char *c, unsigned char *mac, @@ -43,9 +45,31 @@ _encrypt_detached(unsigned char *c, crypto_onetimeauth_poly1305_update(&state, ad, adlen); crypto_onetimeauth_poly1305_update(&state, _pad0, (0x10 - adlen) & 0xf); - crypto_stream_chacha20_ietf_ext_xor_ic(c, m, mlen, npub, 1U, k); + { + unsigned long long off = 0U; + uint32_t ic = 1U; + /* + * The ietf_ext counter is 32-bit and overflows into the IV past + * ~256 GiB, which a chunk restart cannot reproduce, so oversized + * messages make a single pass instead. + */ + unsigned long long chunk = mlen <= 64ULL * (0xffffffffULL - 1ULL) + ? STREAM_POLY1305_CHUNK + : mlen; - crypto_onetimeauth_poly1305_update(&state, c, mlen); + COMPILER_ASSERT(STREAM_POLY1305_CHUNK % 64U == 0U); + while (off < mlen) { + unsigned long long cl = mlen - off; + if (cl > chunk) { + cl = chunk; + } + crypto_stream_chacha20_ietf_ext_xor_ic(c + off, m + off, cl, + npub, ic, k); + crypto_onetimeauth_poly1305_update(&state, c + off, cl); + off += cl; + ic += (uint32_t) (cl / 64U); + } + } crypto_onetimeauth_poly1305_update(&state, _pad0, (0x10 - mlen) & 0xf); STORE64_LE(slen, (uint64_t) adlen); diff --git a/src/libsodium/crypto_secretbox/crypto_secretbox_easy.c b/src/libsodium/crypto_secretbox/crypto_secretbox_easy.c index e6c609f6..33259e68 100644 --- a/src/libsodium/crypto_secretbox/crypto_secretbox_easy.c +++ b/src/libsodium/crypto_secretbox/crypto_secretbox_easy.c @@ -13,6 +13,8 @@ #include "private/common.h" #include "utils.h" +#define STREAM_POLY1305_CHUNK 131072 + int crypto_secretbox_detached(unsigned char *c, unsigned char *mac, const unsigned char *m, @@ -60,13 +62,27 @@ crypto_secretbox_detached(unsigned char *c, unsigned char *mac, c[i] = block0[crypto_secretbox_ZEROBYTES + i]; } sodium_memzero(block0, sizeof block0); - if (mlen > mlen0) { - crypto_stream_salsa20_xor_ic(c + mlen0, m + mlen0, mlen - mlen0, - n + 16, 1U, subkey); + + crypto_onetimeauth_poly1305_update(&state, c, mlen0); + { + unsigned long long off = mlen0; + uint64_t ic = 1U; + + COMPILER_ASSERT(STREAM_POLY1305_CHUNK % 64U == 0U); + while (off < mlen) { + unsigned long long cl = mlen - off; + if (cl > STREAM_POLY1305_CHUNK) { + cl = STREAM_POLY1305_CHUNK; + } + crypto_stream_salsa20_xor_ic(c + off, m + off, cl, + n + 16, ic, subkey); + crypto_onetimeauth_poly1305_update(&state, c + off, cl); + off += cl; + ic += cl / 64U; + } } sodium_memzero(subkey, sizeof subkey); - crypto_onetimeauth_poly1305_update(&state, c, mlen); crypto_onetimeauth_poly1305_final(&state, mac); sodium_memzero(&state, sizeof state);