From 495b80b9b026cc71e8136da50738fd66c42dd65b Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 21 Aug 2026 01:49:53 +0200 Subject: [PATCH] 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. --- src/libwolfboot.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/libwolfboot.c b/src/libwolfboot.c index d054e8a7..9116f84a 100644 --- a/src/libwolfboot.c +++ b/src/libwolfboot.c @@ -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