// Circular hash code. // // This code implements a circular hash algorithm, intended as a variable // length hash function that is fast to update. (The hash function will be // called many times.) This is done by SHA-1'ing each of the inputs, then // circularly XORing this value into a buffer. #ifndef KMODE_RNG #include #include #include #include #else #include #include #endif // KMODE_RNG #include #include #include "circhash.h" #ifdef KMODE_RNG #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, InitCircularHash) #pragma alloc_text(PAGE, DestroyCircularHash) #pragma alloc_text(PAGE, GetCircularHashValue) #pragma alloc_text(PAGE, UpdateCircularHash) #endif // ALLOC_PRAGMA #endif // KMODE_RNG // // internal state flags // #define CH_INVALID_HASH_CTXT 0 #define CH_VALID_HASH_CTXT 0x1423 BOOL InitCircularHash( IN CircularHash *NewHash, IN DWORD dwUpdateInc, IN DWORD dwAlgId, IN DWORD dwMode ) { #ifdef KMODE_RNG PAGED_CODE(); #endif // KMODE_RNG if (NULL == NewHash) return FALSE; NewHash->dwCircHashVer = CH_VALID_HASH_CTXT; NewHash->dwCircSize = sizeof(NewHash->CircBuf); NewHash->dwMode = dwMode; NewHash->dwCircInc = dwUpdateInc; NewHash->dwCurCircPos = 0; NewHash->dwAlgId = dwAlgId; return TRUE; } VOID DestroyCircularHash( IN CircularHash *OldHash ) { #ifdef KMODE_RNG PAGED_CODE(); #endif // KMODE_RNG if ((NULL == OldHash) || (CH_VALID_HASH_CTXT != OldHash->dwCircHashVer)) return; RtlZeroMemory( OldHash, sizeof( *OldHash ) ); } BOOL GetCircularHashValue( IN CircularHash *CurrentHash, OUT BYTE **ppbHashValue, OUT DWORD *pcbHashValue ) { #ifdef KMODE_RNG PAGED_CODE(); #endif // KMODE_RNG if ((NULL == CurrentHash) || (CH_VALID_HASH_CTXT != CurrentHash->dwCircHashVer)) return FALSE; *ppbHashValue = CurrentHash->CircBuf; *pcbHashValue = CurrentHash->dwCircSize; return TRUE; } BOOL UpdateCircularHash( IN CircularHash *CurrentHash, IN VOID *pvData, IN DWORD cbData ) { A_SHA_CTX shaCtx; MD4_CTX md4Ctx; BYTE LocalResBuf[A_SHA_DIGEST_LEN]; PBYTE pHash; DWORD dwHashSize; DWORD i, j; PBYTE pbCircularBuffer; DWORD cbCircularBuffer; DWORD cbCircularPosition; #ifdef KMODE_RNG PAGED_CODE(); #endif // KMODE_RNG if ((NULL == CurrentHash) || (CH_VALID_HASH_CTXT != CurrentHash->dwCircHashVer)) return FALSE; pbCircularBuffer = CurrentHash->CircBuf; cbCircularBuffer = CurrentHash->dwCircSize; cbCircularPosition = CurrentHash->dwCurCircPos; // // First, hash in the result // if( CurrentHash->dwAlgId == CH_ALG_MD4 ) { dwHashSize = MD4DIGESTLEN; MD4Init(&md4Ctx); MD4Update(&md4Ctx, (unsigned char*)pvData, cbData); if (CurrentHash->dwMode & CH_MODE_FEEDBACK) { MD4Update(&md4Ctx, pbCircularBuffer, cbCircularBuffer); } MD4Final(&md4Ctx); pHash = md4Ctx.digest; } else { dwHashSize = A_SHA_DIGEST_LEN; A_SHAInit(&shaCtx); A_SHAUpdateNS(&shaCtx, (unsigned char*)pvData, cbData); if (CurrentHash->dwMode & CH_MODE_FEEDBACK) { A_SHAUpdateNS(&shaCtx, pbCircularBuffer, cbCircularBuffer); } A_SHAFinalNS(&shaCtx, LocalResBuf); pHash = LocalResBuf; } // // Now, XOR this into the circular buffer // // // this is a slow way of doing this (byte at time, versus DWORD/DWORD64), // but it'll work for now... // In most cases, we can assume we'll wrap once, but let's keep it general for now. // j = cbCircularPosition; for( i = 0 ; i < dwHashSize ; i++ ) { if (j >= cbCircularBuffer) j = 0; pbCircularBuffer[j] ^= pHash[i]; j++; } // // Update. Since dwCircInc should be relatively prime to dwCircSize, this // should result in the pointer continually cycling through dwCircSize values. // CurrentHash->dwCurCircPos = (cbCircularPosition + CurrentHash->dwCircInc) % cbCircularBuffer; return TRUE; }