From 6cc25e2e7e76e678aa7aae74f84538651f118363 Mon Sep 17 00:00:00 2001 From: night1rider Date: Tue, 25 Aug 2026 00:39:36 -0600 Subject: [PATCH] nxp hashcrypt: preserve AES OFB/CFB streaming state across calls Drive OFB/CFB from the ECB primitive with reg/tmp/left kept in the Aes context, add NULL/zero-length guards, and re-enable the streaming regression checks. --- wolfcrypt/src/port/nxp/hashcrypt_port.c | 109 ++++++++++++++++-------- wolfcrypt/test/test.c | 12 --- 2 files changed, 75 insertions(+), 46 deletions(-) diff --git a/wolfcrypt/src/port/nxp/hashcrypt_port.c b/wolfcrypt/src/port/nxp/hashcrypt_port.c index 5f4d43c5786..3067e2219fe 100644 --- a/wolfcrypt/src/port/nxp/hashcrypt_port.c +++ b/wolfcrypt/src/port/nxp/hashcrypt_port.c @@ -283,73 +283,114 @@ int wc_AesCbcDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) #endif /* HAVE_AES_CBC */ #ifdef WOLFSSL_AES_OFB -int wc_AesOfbEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) +/* OFB is symmetric (encrypt and decrypt run the same keystream). Generate the + * keystream one block at a time with the SDK's ECB primitive and keep the + * feedback register plus any unused keystream bytes in the Aes context, so a + * message split across calls continues the stream instead of restarting from + * the original IV. */ +static int _hashcrypt_aes_ofb(Aes* aes, byte* out, const byte* in, word32 sz) { int ret; + byte* tmp; + + if (aes == NULL || out == NULL || in == NULL) + return BAD_FUNC_ARG; + + if (sz == 0) + return 0; ret = _hashcrypt_set_key(aes); if (ret) return ret; - if (HASHCRYPT_AES_CryptOfb( - HASHCRYPT, &aes_handle, in, out, sz, (const uint8_t *)aes->reg) - != kStatus_Success) - return WC_HW_E; + while (sz > 0) { + if (aes->left == 0) { + /* keystream block O = E(reg); OFB feeds O back as the next reg */ + if (HASHCRYPT_AES_EncryptEcb(HASHCRYPT, &aes_handle, + (const uint8_t *)aes->reg, (uint8_t *)aes->tmp, + WC_AES_BLOCK_SIZE) != kStatus_Success) + return WC_HW_E; + XMEMCPY(aes->reg, aes->tmp, WC_AES_BLOCK_SIZE); + aes->left = WC_AES_BLOCK_SIZE; + } + tmp = (byte*)aes->tmp + WC_AES_BLOCK_SIZE - aes->left; + while (aes->left > 0 && sz > 0) { + *(out++) = *(in++) ^ *(tmp++); + aes->left--; + sz--; + } + } return 0; } +int wc_AesOfbEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) +{ + return _hashcrypt_aes_ofb(aes, out, in, sz); +} + #ifdef HAVE_AES_DECRYPT int wc_AesOfbDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) { - int ret; - - ret = _hashcrypt_set_key(aes); - if (ret) - return ret; - - if (HASHCRYPT_AES_CryptOfb( - HASHCRYPT, &aes_handle, in, out, sz, (const uint8_t *)aes->reg) - != kStatus_Success) - return WC_HW_E; - - return 0; + return _hashcrypt_aes_ofb(aes, out, in, sz); } #endif #endif /* WOLFSSL_AES_OFB */ #ifdef WOLFSSL_AES_CFB -int wc_AesCfbEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) +/* CFB-128, byte capable. The keystream block is E(reg); each ciphertext byte is + * fed back into reg at the matching offset, so once a full block has passed reg + * holds the next feedback block. State (reg, tmp keystream, left) persists in + * the Aes context so one message may be processed across several calls. */ +static int _hashcrypt_aes_cfb(Aes* aes, byte* out, const byte* in, word32 sz, + int decrypt) { int ret; + word32 idx; + byte c; + + if (aes == NULL || out == NULL || in == NULL) + return BAD_FUNC_ARG; + + if (sz == 0) + return 0; ret = _hashcrypt_set_key(aes); if (ret) return ret; - if (HASHCRYPT_AES_EncryptCfb( - HASHCRYPT, &aes_handle, in, out, sz, (const uint8_t *)aes->reg) - != kStatus_Success) - return WC_HW_E; + while (sz > 0) { + if (aes->left == 0) { + if (HASHCRYPT_AES_EncryptEcb(HASHCRYPT, &aes_handle, + (const uint8_t *)aes->reg, (uint8_t *)aes->tmp, + WC_AES_BLOCK_SIZE) != kStatus_Success) + return WC_HW_E; + aes->left = WC_AES_BLOCK_SIZE; + } + idx = WC_AES_BLOCK_SIZE - aes->left; + /* ciphertext = plaintext XOR keystream (and vice-versa for decrypt) */ + c = (byte)(*in ^ ((byte*)aes->tmp)[idx]); + /* feedback register always takes the ciphertext byte */ + ((byte*)aes->reg)[idx] = decrypt ? *in : c; + *out = c; + in++; + out++; + sz--; + aes->left--; + } return 0; } +int wc_AesCfbEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) +{ + return _hashcrypt_aes_cfb(aes, out, in, sz, 0); +} + #ifdef HAVE_AES_DECRYPT int wc_AesCfbDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) { - int ret; - - ret = _hashcrypt_set_key(aes); - if (ret) - return ret; - - if (HASHCRYPT_AES_DecryptCfb( - HASHCRYPT, &aes_handle, in, out, sz, (const uint8_t *)aes->reg) - != kStatus_Success) - return WC_HW_E; - - return 0; + return _hashcrypt_aes_cfb(aes, out, in, sz, 1); } #endif #endif /* WOLFSSL_AES_CFB */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index efdfc5b68df..ef95557933c 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -13828,11 +13828,9 @@ static wc_test_ret_t aes_ofb_256_test(Aes* enc, Aes* dec, byte* cipher, plain1 + WC_AES_BLOCK_SIZE, WC_AES_BLOCK_SIZE); if (ret != 0) return WC_TEST_RET_ENC_EC(ret); -#ifndef WOLFSSL_NXP_HASHCRYPT_AES if (XMEMCMP(cipher + WC_AES_BLOCK_SIZE, cipher1 + WC_AES_BLOCK_SIZE, WC_AES_BLOCK_SIZE)) return WC_TEST_RET_ENC_NC; -#endif /* !WOLFSSL_NXP_HASHCRYPT_AES */ #ifdef HAVE_AES_DECRYPT ret = wc_AesOfbDecrypt(dec, plain, cipher1, WC_AES_BLOCK_SIZE); @@ -13845,11 +13843,9 @@ static wc_test_ret_t aes_ofb_256_test(Aes* enc, Aes* dec, byte* cipher, cipher1 + WC_AES_BLOCK_SIZE, WC_AES_BLOCK_SIZE); if (ret != 0) return WC_TEST_RET_ENC_EC(ret); -#ifndef WOLFSSL_NXP_HASHCRYPT_AES if (XMEMCMP(plain + WC_AES_BLOCK_SIZE, plain1 + WC_AES_BLOCK_SIZE, WC_AES_BLOCK_SIZE)) return WC_TEST_RET_ENC_NC; -#endif /* !WOLFSSL_NXP_HASHCRYPT_AES */ #endif /* HAVE_AES_DECRYPT */ /* Multiple blocks at once */ @@ -13922,10 +13918,8 @@ static wc_test_ret_t aes_ofb_256_test(Aes* enc, Aes* dec, byte* cipher, ret = wc_AesOfbEncrypt(enc, cipher + 3, plain1 + 3, WC_AES_BLOCK_SIZE); if (ret != 0) return WC_TEST_RET_ENC_EC(ret); -#ifndef WOLFSSL_NXP_HASHCRYPT_AES if (XMEMCMP(cipher + 3, cipher1 + 3, WC_AES_BLOCK_SIZE)) return WC_TEST_RET_ENC_NC; -#endif /* !WOLFSSL_NXP_HASHCRYPT_AES */ #ifdef HAVE_AES_DECRYPT ret = wc_AesOfbDecrypt(dec, plain, cipher1, 6); @@ -13937,10 +13931,8 @@ static wc_test_ret_t aes_ofb_256_test(Aes* enc, Aes* dec, byte* cipher, ret = wc_AesOfbDecrypt(dec, plain + 6, cipher1 + 6, WC_AES_BLOCK_SIZE); if (ret != 0) return WC_TEST_RET_ENC_EC(ret); -#ifndef WOLFSSL_NXP_HASHCRYPT_AES if (XMEMCMP(plain + 6, plain1 + 6, WC_AES_BLOCK_SIZE)) return WC_TEST_RET_ENC_NC; -#endif /* !WOLFSSL_NXP_HASHCRYPT_AES */ #endif /* HAVE_AES_DECRYPT */ return 0; @@ -14302,7 +14294,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aesofb_test(void) ERROR_OUT(WC_TEST_RET_ENC_NC, out); /* test restarting encryption process */ - #ifndef WOLFSSL_NXP_HASHCRYPT_AES ret = wc_AesCfbEncrypt(enc, cipher + (WC_AES_BLOCK_SIZE * 2), msg1 + (WC_AES_BLOCK_SIZE * 2), WC_AES_BLOCK_SIZE); if (ret != 0) @@ -14320,7 +14311,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aesofb_test(void) if (XMEMCMP(plain, msg1, WC_AES_BLOCK_SIZE * 3)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #endif /* HAVE_AES_DECRYPT */ - #endif /* !WOLFSSL_NXP_HASHCRYPT_AES */ #endif /* WOLFSSL_AES_128 */ #ifdef WOLFSSL_AES_192 @@ -14386,7 +14376,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aesofb_test(void) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); #endif - #ifndef WOLFSSL_NXP_HASHCRYPT_AES /* test with data left overs, magic lengths are checking near edges */ XMEMSET(cipher, 0, sizeof(cipher)); ret = wc_AesCfbEncrypt(enc, cipher, msg3, 4); @@ -14438,7 +14427,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aesofb_test(void) if (XMEMCMP(plain, msg3, WC_AES_BLOCK_SIZE * 4)) ERROR_OUT(WC_TEST_RET_ENC_NC, out); #endif /* HAVE_AES_DECRYPT */ - #endif /* !WOLFSSL_NXP_HASHCRYPT_AES */ #endif /* WOLFSSL_AES_256 */ out: