SHA-3: harden against glitch attack

Check loop counts to ensure glitching didn't change number of times loop
was performed.
This commit is contained in:
Sean Parkinson
2026-01-29 10:14:03 +10:00
parent b56eeb91aa
commit 5924524920
3 changed files with 146 additions and 72 deletions

View File

@@ -637,6 +637,7 @@ WC_RSA_NONBLOCK
WC_RSA_NONBLOCK_TIME
WC_RSA_NO_FERMAT_CHECK
WC_RWLOCK_OPS_INLINE
WC_SHA3_HARDEN
WC_SHA384
WC_SHA384_DIGEST_SIZE
WC_SHA512

View File

@@ -2726,7 +2726,7 @@ AC_ARG_ENABLE([faultharden],
if test "$ENABLED_FAULTHARDEN" = "yes"
then
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_CHECK_SIG_FAULTS -DWOLFSSL_CHECK_VER_FAULTS"
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_CHECK_SIG_FAULTS -DWOLFSSL_CHECK_VER_FAULTS -DWC_SHA3_HARDEN"
fi
AC_ARG_ENABLE([compileharden],

View File

@@ -585,7 +585,7 @@ static WC_INLINE word64 Load64Unaligned(const unsigned char *a)
* a Array of bytes.
* returns a 64-bit integer.
*/
static word64 Load64BitBigEndian(const byte* a)
static word64 Load64BitLittleEndian(const byte* a)
{
word64 n = 0;
int i;
@@ -595,6 +595,31 @@ static word64 Load64BitBigEndian(const byte* a)
return n;
}
#elif defined(WC_SHA3_HARDEN)
static WC_INLINE word64 Load64Unaligned(const unsigned char *a)
{
#ifdef WC_64BIT_CPU
return *(word64*)a;
#elif defined(WC_32BIT_CPU)
return (((word64)((word32*)a)[1]) << 32) ||
((word32*)a)[0];
#else
return (((word64)((word16*)a)[3]) << 48) ||
(((word64)((word16*)a)[2]) << 32) ||
(((word64)((word16*)a)[1]) << 16) ||
((word16*)a)[0];
#endif
}
/* Convert the array of bytes, in little-endian order, to a 64-bit integer.
*
* a Array of bytes.
* returns a 64-bit integer.
*/
static word64 Load64BitLittleEndian(const byte* a)
{
return Load64Unaligned(a);
}
#endif
/* Initialize the state for a SHA3-224 hash operation.
@@ -687,6 +712,10 @@ static int Sha3Update(wc_Sha3* sha3, const byte* data, word32 len, byte p)
{
word32 i;
word32 blocks;
#ifdef WC_SHA3_HARDEN
byte check = 0;
byte total_check = 0;
#endif
#if defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) && defined(USE_INTEL_SPEEDUP)
if (SHA3_BLOCK == sha3_block_avx2) {
@@ -703,19 +732,37 @@ static int Sha3Update(wc_Sha3* sha3, const byte* data, word32 len, byte p)
t = &sha3->t[sha3->i];
for (i = 0; i < l; i++) {
t[i] = data[i];
#ifdef WC_SHA3_HARDEN
check++;
#endif
}
#ifdef WC_SHA3_HARDEN
if (check != l) {
return BAD_COND_E;
}
total_check += l;
#endif
data += i;
len -= i;
sha3->i = (byte)(sha3->i + i);
if (sha3->i == p * 8) {
#if !defined(BIG_ENDIAN_ORDER)
#if !defined(BIG_ENDIAN_ORDER) && !defined(WC_SHA3_HARDEN)
xorbuf(sha3->s, sha3->t, (word32)(p * 8));
#else
#else
for (i = 0; i < p; i++) {
sha3->s[i] ^= Load64BitBigEndian(sha3->t + 8 * i);
sha3->s[i] ^= Load64BitLittleEndian(sha3->t + 8 * i);
#ifdef WC_SHA3_HARDEN
check++;
#endif
}
#ifdef WC_SHA3_HARDEN
if (check != p + l) {
return BAD_COND_E;
}
total_check += p;
#endif
#endif
#ifdef SHA3_FUNC_PTR
(*SHA3_BLOCK)(sha3->s);
#else
@@ -733,14 +780,25 @@ static int Sha3Update(wc_Sha3* sha3, const byte* data, word32 len, byte p)
blocks = 0;
}
#endif
#ifdef WC_SHA3_HARDEN
total_check += blocks * p;
#endif
for (; blocks > 0; blocks--) {
#if !defined(BIG_ENDIAN_ORDER)
#if !defined(BIG_ENDIAN_ORDER) && !defined(WC_SHA3_HARDEN)
xorbuf(sha3->s, data, (word32)(p * 8));
#else
#else
for (i = 0; i < p; i++) {
sha3->s[i] ^= Load64Unaligned(data + 8 * i);
#ifdef WC_SHA3_HARDEN
check++;
#endif
}
#ifdef WC_SHA3_HARDEN
if (check != total_check - ((blocks - 1) * p)) {
return BAD_COND_E;
}
#endif
#endif
#ifdef SHA3_FUNC_PTR
(*SHA3_BLOCK)(sha3->s);
#else
@@ -749,6 +807,11 @@ static int Sha3Update(wc_Sha3* sha3, const byte* data, word32 len, byte p)
len -= p * 8U;
data += p * 8U;
}
#ifdef WC_SHA3_HARDEN
if (check != total_check) {
return BAD_COND_E;
}
#endif
#if defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) && defined(USE_INTEL_SPEEDUP)
if (SHA3_BLOCK == sha3_block_avx2) {
RESTORE_VECTOR_REGISTERS();
@@ -774,11 +837,14 @@ static int Sha3Final(wc_Sha3* sha3, byte padChar, byte* hash, byte p, word32 l)
{
word32 rate = p * 8U;
word32 j;
#if defined(BIG_ENDIAN_ORDER)
#if defined(BIG_ENDIAN_ORDER) || defined(WC_SHA3_HARDEN)
word32 i;
#endif
#ifdef WC_SHA3_HARDEN
int check = 0;
#endif
#if !defined(BIG_ENDIAN_ORDER)
#if !defined(BIG_ENDIAN_ORDER) && !defined(WC_SHA3_HARDEN)
xorbuf(sha3->s, sha3->t, sha3->i);
#ifdef WOLFSSL_HASH_FLAGS
if ((p == WC_SHA3_256_COUNT) && (sha3->flags & WC_HASH_SHA3_KECCAK256)) {
@@ -800,8 +866,16 @@ static int Sha3Final(wc_Sha3* sha3, byte padChar, byte* hash, byte p, word32 l)
XMEMSET(sha3->t + sha3->i + 1, 0, rate - 1U - (sha3->i + 1U));
}
for (i = 0; i < p; i++) {
sha3->s[i] ^= Load64BitBigEndian(sha3->t + 8 * i);
sha3->s[i] ^= Load64BitLittleEndian(sha3->t + 8 * i);
#ifdef WC_SHA3_HARDEN
check++;
#endif
}
#ifdef WC_SHA3_HARDEN
if (check != p) {
return BAD_COND_E;
}
#endif
#endif
#if defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) && defined(USE_INTEL_SPEEDUP)
@@ -843,80 +917,79 @@ static int Sha3Final(wc_Sha3* sha3, byte padChar, byte* hash, byte p, word32 l)
#endif
#if defined(STM32_HASH_SHA3)
/* Supports CubeMX HAL or Standard Peripheral Library */
/* Supports CubeMX HAL or Standard Peripheral Library */
static int wc_InitSha3(wc_Sha3* sha3, void* heap, int devId)
{
if (sha3 == NULL)
return BAD_FUNC_ARG;
static int wc_InitSha3(wc_Sha3* sha3, void* heap, int devId)
{
if (sha3 == NULL)
return BAD_FUNC_ARG;
(void)devId;
(void)heap;
(void)devId;
(void)heap;
XMEMSET(sha3, 0, sizeof(wc_Sha3));
wc_Stm32_Hash_Init(&sha3->stmCtx);
XMEMSET(sha3, 0, sizeof(wc_Sha3));
wc_Stm32_Hash_Init(&sha3->stmCtx);
return 0;
}
static int Stm32GetAlgo(byte p)
{
switch(p) {
case WC_SHA3_224_COUNT:
return HASH_ALGOSELECTION_SHA3_224;
case WC_SHA3_256_COUNT:
return HASH_ALGOSELECTION_SHA3_256;
case WC_SHA3_384_COUNT:
return HASH_ALGOSELECTION_SHA3_384;
case WC_SHA3_512_COUNT:
return HASH_ALGOSELECTION_SHA3_512;
}
/* Should never get here */
return WC_SHA3_224_COUNT;
}
static int wc_Sha3Update(wc_Sha3* sha3, const byte* data, word32 len, byte p)
{
int ret = 0;
if (sha3 == NULL) {
return BAD_FUNC_ARG;
}
if (data == NULL && len == 0) {
/* valid, but do nothing */
return 0;
}
static int Stm32GetAlgo(byte p)
{
switch(p) {
case WC_SHA3_224_COUNT:
return HASH_ALGOSELECTION_SHA3_224;
case WC_SHA3_256_COUNT:
return HASH_ALGOSELECTION_SHA3_256;
case WC_SHA3_384_COUNT:
return HASH_ALGOSELECTION_SHA3_384;
case WC_SHA3_512_COUNT:
return HASH_ALGOSELECTION_SHA3_512;
}
/* Should never get here */
return WC_SHA3_224_COUNT;
if (data == NULL) {
return BAD_FUNC_ARG;
}
static int wc_Sha3Update(wc_Sha3* sha3, const byte* data, word32 len, byte p)
{
int ret = 0;
ret = wolfSSL_CryptHwMutexLock();
if (ret == 0) {
ret = wc_Stm32_Hash_Update(&sha3->stmCtx, Stm32GetAlgo(p), data, len,
p * 8);
wolfSSL_CryptHwMutexUnLock();
}
return ret;
}
if (sha3 == NULL) {
return BAD_FUNC_ARG;
}
if (data == NULL && len == 0) {
/* valid, but do nothing */
return 0;
}
if (data == NULL) {
return BAD_FUNC_ARG;
}
static int wc_Sha3Final(wc_Sha3* sha3, byte* hash, byte p, byte len)
{
int ret = 0;
ret = wolfSSL_CryptHwMutexLock();
if (ret == 0) {
ret = wc_Stm32_Hash_Update(&sha3->stmCtx,
Stm32GetAlgo(p), data, len, p * 8);
wolfSSL_CryptHwMutexUnLock();
}
return ret;
if (sha3 == NULL || hash == NULL) {
return BAD_FUNC_ARG;
}
static int wc_Sha3Final(wc_Sha3* sha3, byte* hash, byte p, byte len)
{
int ret = 0;
if (sha3 == NULL || hash == NULL) {
return BAD_FUNC_ARG;
}
ret = wolfSSL_CryptHwMutexLock();
if (ret == 0) {
ret = wc_Stm32_Hash_Final(&sha3->stmCtx,
Stm32GetAlgo(p), hash, len);
wolfSSL_CryptHwMutexUnLock();
}
(void)wc_InitSha3(sha3, NULL, 0); /* reset state */
return ret;
ret = wolfSSL_CryptHwMutexLock();
if (ret == 0) {
ret = wc_Stm32_Hash_Final(&sha3->stmCtx, Stm32GetAlgo(p), hash, len);
wolfSSL_CryptHwMutexUnLock();
}
(void)wc_InitSha3(sha3, NULL, 0); /* reset state */
return ret;
}
#elif defined(PSOC6_HASH_SHA3)
static int wc_InitSha3(wc_Sha3* sha3, void* heap, int devId)