From 00664617763319deff60637bf068d3fc84099f2b Mon Sep 17 00:00:00 2001 From: night1rider Date: Sun, 2 Aug 2026 16:17:54 -0600 Subject: [PATCH] Xilinx Versal Gen2 ASU port: split AES-CTR at the 32-bit counter wrap and carry in software to keep it on hardware --- .wolfssl_known_macro_extras | 1 + .../port/xilinx/versal_gen2_asu/asu_cipher.c | 62 ++++++++++++++----- wolfcrypt/test/test.c | 12 +--- 3 files changed, 49 insertions(+), 26 deletions(-) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 1d9037460f..e0c9a731c2 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -1092,6 +1092,7 @@ WOLFSSL_VALIDATE_DH_KEYGEN WOLFSSL_VAULTIC_DEBUG WOLFSSL_VERSAL_GEN2_ASU WOLFSSL_VERSAL_GEN2_ASU_CCM_ALIGN_DECLINE +WOLFSSL_VERSAL_GEN2_ASU_CTR_WRAP_HW_FIXED WOLFSSL_VERSAL_GEN2_ASU_RTC WOLFSSL_VERSAL_GEN2_ASU_TRNG_DIRECT WOLFSSL_WC_SLHDSA_RECURSIVE diff --git a/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cipher.c b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cipher.c index 6640fe6f86..830139b047 100644 --- a/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cipher.c +++ b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cipher.c @@ -217,15 +217,26 @@ static int wc_AsuCipherEcb(wc_CryptoInfo* info) } #ifdef WOLFSSL_AES_COUNTER -/* AES-CTR (counter in aes->reg). The ASU counts differently than wolfSSL once - * the low 32 counter bits wrap around, so that case runs in software. */ +/* Add n to the 16-byte counter, starting at the last byte and carrying toward + * the first, so it matches wolfSSL's software counter. */ +static void wc_AsuCtrAdd(byte* ctr, word32 n) +{ + word32 carry = n; + int i; + for (i = WC_AES_BLOCK_SIZE - 1; (i >= 0) && (carry != 0); i--) { + carry += (word32)ctr[i]; + ctr[i] = (byte)carry; + carry >>= 8; + } +} + +/* AES-CTR. The ASU only counts the last 4 bytes of the counter and cannot carry + * past them, so we split the work there and add the carry in software. */ static int wc_AsuCipherCtr(wc_CryptoInfo* info) { byte* ctr; word32 blocks; - word32 carry; int ret; - int i; if (info == NULL || info->cipher.aesctr.aes == NULL || info->cipher.aesctr.out == NULL || info->cipher.aesctr.in == NULL) { @@ -234,31 +245,48 @@ static int wc_AsuCipherCtr(wc_CryptoInfo* info) /* Partly-used keystream from an earlier call lives in the Aes context and * the ASU always starts fresh, so those calls run in software. */ if (info->cipher.aesctr.aes->left != 0 || info->cipher.aesctr.sz == 0 || - (info->cipher.aesctr.sz % WC_AES_BLOCK_SIZE) != 0) { + (info->cipher.aesctr.sz % WC_AES_BLOCK_SIZE) != 0 || + info->cipher.aesctr.sz > XASU_ASU_DMA_MAX_TRANSFER_LENGTH) { return CRYPTOCB_UNAVAILABLE; } - /* The ASU counts only the low 32 bits of the counter and wraps them to zero, - * while wolfSSL software carries across the full 128 bits. */ ctr = (byte*)info->cipher.aesctr.aes->reg; blocks = info->cipher.aesctr.sz / WC_AES_BLOCK_SIZE; - /* CTR encrypt and decrypt are the same operation, so always run encrypt. */ + /* Default: do the split. Define this macro on a firmware that carries the + * whole 128-bit counter to skip the split and use one hardware call. */ +#ifndef WOLFSSL_VERSAL_GEN2_ASU_CTR_WRAP_HW_FIXED + { + word32 off = 0; + word32 remaining = blocks; + while (remaining != 0) { + word32 low = ((word32)ctr[12] << 24) | ((word32)ctr[13] << 16) | + ((word32)ctr[14] << 8) | (word32)ctr[15]; + /* How many blocks fit before the last 4 bytes roll over. */ + word64 toWrap = (word64)0x100000000ULL - (word64)low; + word32 chunk = ((word64)remaining < toWrap) ? remaining + : (word32)toWrap; + /* CTR encrypt and decrypt are the same op, so always run encrypt. */ + ret = wc_AsuCipherOneShot(info->cipher.aesctr.aes, + info->cipher.aesctr.out + off, info->cipher.aesctr.in + off, + chunk * WC_AES_BLOCK_SIZE, 1, (u8)XASU_AES_CTR_MODE, ctr); + if (ret != 0) { + return ret; + } + wc_AsuCtrAdd(ctr, chunk); + off += chunk * WC_AES_BLOCK_SIZE; + remaining -= chunk; + } + } +#else ret = wc_AsuCipherOneShot(info->cipher.aesctr.aes, info->cipher.aesctr.out, info->cipher.aesctr.in, info->cipher.aesctr.sz, 1, (u8)XASU_AES_CTR_MODE, ctr); if (ret != 0) { return ret; } - - /* Add the block count to the counter for the next call: start at the last - * byte (ctr[15]) and carry toward the first, matching wolfSSL software. */ - carry = blocks; - for (i = WC_AES_BLOCK_SIZE - 1; (i >= 0) && (carry != 0); i--) { - carry += (word32)ctr[i]; - ctr[i] = (byte)carry; - carry >>= 8; - } + wc_AsuCtrAdd(ctr, blocks); +#endif return 0; } diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index c0eb1cace8..e9426ab71c 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -15508,8 +15508,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_ctr_test(void) ctrPlain, (int)sizeof(oddCipher), ctr128Cipher }, /* and an additional 9 bytes to reuse tmp left buffer */ { NULL, 0, NULL, ctrPlain, (int)sizeof(oddCipher), oddCipher }, - /* NO_AES_CTR_WRAP_TEST disables testing counter wrap on the low 32 bits. */ - #ifndef NO_AES_CTR_WRAP_TEST + /* Counter wrapping */ #ifndef WOLFSSL_NXP_HASHCRYPT_AES { ctr128Key, (int)sizeof(ctr128Key), ctrIvWrap128, ctrPlain, (int)sizeof(ctr128Wrap128Cipher), ctr128Wrap128Cipher }, @@ -15546,7 +15545,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_ctr_test(void) ctrPlain, (int)sizeof(ctr128Wrap32_2CipherLong), ctr128Wrap32_2CipherLong }, #endif - #endif /* !NO_AES_CTR_WRAP_TEST */ #endif #ifdef WOLFSSL_AES_192 { ctr192Key, (int)sizeof(ctr192Key), ctrIv, @@ -15554,8 +15552,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_ctr_test(void) /* let's try with just 9 bytes, non block size test */ { ctr192Key, (int)sizeof(ctr192Key), ctrIv, ctrPlain, (int)sizeof(oddCipher), ctr192Cipher }, - /* NO_AES_CTR_WRAP_TEST disables testing counter wrap on the low 32 bits. */ - #ifndef NO_AES_CTR_WRAP_TEST + /* Counter wrapping */ #ifndef WOLFSSL_NXP_HASHCRYPT_AES { ctr192Key, (int)sizeof(ctr192Key), ctrIvWrap128, ctrPlain, (int)sizeof(ctr192Wrap128Cipher), ctr192Wrap128Cipher }, @@ -15592,7 +15589,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_ctr_test(void) ctrPlain, (int)sizeof(ctr192Wrap32_2CipherLong), ctr192Wrap32_2CipherLong }, #endif - #endif /* !NO_AES_CTR_WRAP_TEST */ #endif #ifdef WOLFSSL_AES_256 { ctr256Key, (int)sizeof(ctr256Key), ctrIv, @@ -15600,8 +15596,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_ctr_test(void) /* let's try with just 9 bytes, non block size test */ { ctr256Key, (int)sizeof(ctr256Key), ctrIv, ctrPlain, (int)sizeof(oddCipher), ctr256Cipher }, - /* NO_AES_CTR_WRAP_TEST disables testing counter wrap on the low 32 bits. */ - #ifndef NO_AES_CTR_WRAP_TEST + /* Counter wrapping */ #ifndef WOLFSSL_NXP_HASHCRYPT_AES { ctr256Key, (int)sizeof(ctr256Key), ctrIvWrap128, ctrPlain, (int)sizeof(ctr256Wrap128Cipher), ctr256Wrap128Cipher }, @@ -15638,7 +15633,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_ctr_test(void) ctrPlain, (int)sizeof(ctr256Wrap32_2CipherLong), ctr256Wrap32_2CipherLong }, #endif - #endif /* !NO_AES_CTR_WRAP_TEST */ #endif }; #define AES_CTR_TEST_LEN (int)(sizeof(testVec) / sizeof(*testVec))