F-7966: propagate the CTR counter carry without branching on the nonce

aes_set_iv() and pkcs11_crypto_set_iv() added the block counter to the
nonce-derived counter block and propagated the carry with a loop that
only runs - and whose trip count depends - on the nonce words: the
overflow branch reveals that the high word was within one count of
wrapping, and the inner loop's exit point reveals how many of the low
words are 0xFFFFFFFF. crypto_set_iv() runs once per encrypted block,
so a timing attacker gets one measurement per block.

Replace both with an unconditional branch-free four-word carry
(standard carry-out flags, three iterations regardless of content).
Arithmetic is identical: verified old-vs-new expression equality over
5M random counter/nonce inputs plus the full-carry, zero-counter and
max-counter boundary cases; unit-aes128/unit-aes256 encrypted
roundtrips pass with the new code, and the ENCRYPT_PKCS11
CKM_AES_CTR path compiles clean.
pull/868/head
Daniele Lacamera 2026-08-21 01:49:53 +02:00 committed by Daniele Lacamera
parent f3098cbf6d
commit 495b80b9b0
1 changed files with 20 additions and 10 deletions

View File

@ -2314,12 +2314,18 @@ void aes_set_iv(uint8_t *nonce, uint32_t iv_ctr)
iv_buf[i] = wb_reverse_word32(iv_buf[i]);
}
#endif
iv_buf[3] += iv_ctr;
if (iv_buf[3] < iv_ctr) { /* overflow */
/* Add the block counter with an unconditional, branch-free carry:
* a conditional carry loop's trip count would depend on the nonce
* content and leak it through timing. */
{
uint32_t carry;
uint32_t old = iv_buf[3];
iv_buf[3] = old + iv_ctr;
carry = (uint32_t)(iv_buf[3] < old);
for (i = 2; i >= 0; i--) {
iv_buf[i]++;
if (iv_buf[i] != 0)
break;
uint32_t prev = iv_buf[i];
iv_buf[i] = prev + carry;
carry = (uint32_t)(iv_buf[i] < prev);
}
}
#ifndef BIG_ENDIAN_ORDER
@ -2474,12 +2480,16 @@ void pkcs11_crypto_set_iv(uint8_t *nonce, uint32_t iv_ctr)
cb_words[i] = wb_reverse_word32(cb_words[i]);
}
#endif
cb_words[3] += iv_ctr;
if (cb_words[3] < iv_ctr) { /* overflow */
/* Unconditional, branch-free carry (see aes_set_iv) */
{
uint32_t carry;
uint32_t old = cb_words[3];
cb_words[3] = old + iv_ctr;
carry = (uint32_t)(cb_words[3] < old);
for (i = 2; i >= 0; i--) {
cb_words[i]++;
if (cb_words[i] != 0)
break;
uint32_t prev = cb_words[i];
cb_words[i] = prev + carry;
carry = (uint32_t)(cb_words[i] < prev);
}
}
#ifndef BIG_ENDIAN_ORDER