Index: /trunk/src/kWorker/kWorker.c
===================================================================
--- /trunk/src/kWorker/kWorker.c	(revision 3373)
+++ /trunk/src/kWorker/kWorker.c	(revision 3374)
@@ -70,9 +70,9 @@
 #define WITH_TEMP_MEMORY_FILES
 
-/** @def WITH_HASH_MD5_CACHE
- * Enables caching of MD5 sums for cl.exe.
+/** @def WITH_HASH_CACHE
+ * Enables caching of MD5, SHA-1, SHA-256 and SHA-512 hashes for cl.exe.
  * This prevents wasting time on rehashing common headers each time
  * they are included. */
-#define WITH_HASH_MD5_CACHE
+#define WITH_HASH_CACHE
 
 /** @def WITH_CRYPT_CTX_REUSE
@@ -492,9 +492,21 @@
     /** The file size. */
     KU32                cbCached;
-#ifdef WITH_HASH_MD5_CACHE
+#ifdef WITH_HASH_CACHE
     /** Set if we've got a valid MD5 hash in abMd5Digest. */
     KBOOL               fValidMd5;
+    /** Set if we've got a valid SHA-1 hash in abMd5Digest. */
+    KBOOL               fValidSha1;
+    /** Set if we've got a valid SHA-256 hash in abMd5Digest. */
+    KBOOL               fValidSha256;
+    /** Set if we've got a valid SHA-512 hash in abMd5Digest. */
+    KBOOL               fValidSha512;
     /** The MD5 digest if fValidMd5 is set. */
     KU8                 abMd5Digest[16];
+    /** The SHA-1 digest if fValidSha1 is set. */
+    KU8                 abSha1Digest[20];
+    /** The SHA-256 digest if fValidSha256 is set. */
+    KU8                 abSha256Digest[32];
+    /** The SHA-512 digest if fValidSha256 is set. */
+    KU8                 abSha512Digest[64];
 #endif
 
@@ -508,17 +520,17 @@
 typedef KFSWCACHEDFILE *PKFSWCACHEDFILE;
 
-#ifdef WITH_HASH_MD5_CACHE
+#ifdef WITH_HASH_CACHE
 
 /** Pointer to a MD5 hash instance. */
-typedef struct KWHASHMD5 *PKWHASHMD5;
+typedef struct KWCRYPTHASH *PKWCRYPTHASH;
 /**
  * A MD5 hash instance.
  */
-typedef struct KWHASHMD5
+typedef struct KWCRYPTHASH
 {
     /** The magic value. */
     KUPTR               uMagic;
     /** Pointer to the next hash handle. */
-    PKWHASHMD5          pNext;
+    PKWCRYPTHASH        pNext;
     /** The cached file we've associated this handle with. */
     PKFSWCACHEDFILE     pCachedFile;
@@ -527,18 +539,21 @@
     /** Set if this has gone wrong. */
     KBOOL               fGoneBad;
-    /** Set if we're in fallback mode (file not cached). */
-    KBOOL               fFallbackMode;
     /** Set if we've already finalized the digest. */
     KBOOL               fFinal;
-    /** The MD5 fallback context. */
-    struct MD5Context   Md5Ctx;
+    /** If in fallback mode. */
+    HCRYPTHASH          hFallback;
+    /** The algorithm. */
+    ALG_ID              idAlg;
+    /** The hash name. */
+    const char         *pszAlgName;
+    /** The digest size. */
+    KU32                cbDigest;
     /** The finalized digest. */
-    KU8                 abDigest[16];
-
-} KWHASHMD5;
-/** Magic value for KWHASHMD5::uMagic (Les McCann). */
-# define KWHASHMD5_MAGIC    KUPTR_C(0x19350923)
-
-#endif /* WITH_HASH_MD5_CACHE */
+    KU8                 abDigest[64];
+} KWCRYPTHASH;
+/** Magic value for KWCRYPTHASH::uMagic (Les McCann). */
+# define KWCRYPTHASH_MAGIC    KUPTR_C(0x19350923)
+
+#endif /* WITH_HASH_CACHE */
 #ifdef WITH_TEMP_MEMORY_FILES
 
@@ -930,12 +945,17 @@
     MY_UNICODE_STRING SavedCommandLine;
 
-#ifdef WITH_HASH_MD5_CACHE
-    /** The special MD5 hash instance. */
-    PKWHASHMD5      pHashHead;
+#ifdef WITH_HASH_CACHE
+    /** The crypto provider instance we use for hashes. */
+    HCRYPTPROV      hCryptProvRsa;
+    /** The crypto provider instance we use for hashes. */
+    HCRYPTPROV      hCryptProvAes;
+    /** List of crypto hash instances. */
+    PKWCRYPTHASH    pHashHead;
     /** ReadFile sets these while CryptHashData claims and clears them.
      *
-     * This is part of the heuristics we use for MD5 caching for header files. The
-     * observed pattern is that c1.dll/c1xx.dll first reads a chunk of a source or
-     * header, then passes the same buffer and read byte count to CryptHashData.
+     * This is part of the heuristics we use for MD5/SHA1/SHA256 caching for header
+     * files. The observed pattern is that c1.dll/c1xx.dll first reads a chunk of a
+     * source or header, then passes the same buffer and read byte count to
+     * CryptHashData.
      */
     struct
@@ -1321,4 +1341,23 @@
 /** Total bytes written to in-memory temporary files. */
 static KSIZE    g_cbWriteFileToInMemTemp;
+
+#ifdef WITH_HASH_CACHE
+/** Total number of hashes. */
+static KSIZE    g_cHashes;
+/** Number of cached hash hits. */
+static KSIZE    g_cHashesCached;
+/** Number of fallbacks. */
+static KSIZE    g_cHashesFallbacks;
+/** Number of partial cached file hashes. */
+static KSIZE    g_cHashesPartial;
+/** Total number of MD5 hashes. */
+static KSIZE    g_cHashesMd5;
+/** Total number of SHA-1 hashes. */
+static KSIZE    g_cHashesSha1;
+/** Total number of SHA-256 hashes. */
+static KSIZE    g_cHashesSha256;
+/** Total number of SHA-512 hashes. */
+static KSIZE    g_cHashesSha512;
+#endif
 
 
@@ -7558,5 +7597,5 @@
                     cbActually = cbToRead;
 
-#ifdef WITH_HASH_MD5_CACHE
+#ifdef WITH_HASH_CACHE
                 if (g_Sandbox.pHashHead)
                 {
@@ -9896,5 +9935,42 @@
  */
 
-#ifdef WITH_HASH_MD5_CACHE
+#ifdef WITH_HASH_CACHE
+
+/**
+ * Gets our crypto provider context/instance, creating it if needed.
+ */
+static HCRYPTPROV kwSandboxGetCryptoProvider(ALG_ID idAlg)
+{
+    DWORD       dwProvider;
+    HCRYPTPROV *phCryptProv;
+    HCRYPTPROV  hCryptProv;
+    if (   idAlg == CALG_SHA_256
+        || idAlg == CALG_SHA_512)
+    {
+        phCryptProv = &g_Sandbox.hCryptProvAes;
+        dwProvider = PROV_RSA_AES;
+    }
+    else
+    {
+        phCryptProv = &g_Sandbox.hCryptProvRsa;
+        dwProvider = PROV_RSA_FULL;
+    }
+    hCryptProv = *phCryptProv;
+    if (hCryptProv)
+        return hCryptProv;
+
+    /* Create it. */
+    if (CryptAcquireContextW(&hCryptProv, NULL, NULL, dwProvider, CRYPT_VERIFYCONTEXT))
+    {
+        kHlpAssert(hCryptProv != 0);
+        kHlpAssert(hCryptProv != KUPTR_MAX);
+        *phCryptProv = hCryptProv;
+        return hCryptProv;
+    }
+
+    kwErrPrintf("kwSandboxGetCryptoProvider: CryptAcquireContext(,,,%#x, CRYPT_VERIFYCONTEXT) failed! %u\n",
+                dwProvider, GetLastError());
+    return (HCRYPTPROV)NULL;
+}
 
 /** AdvApi32 - CryptCreateHash */
@@ -9909,7 +9985,35 @@
     if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
     {
-        /** @todo do generic caching of hash results.  Need SHA-1 and SHA-256 for
-         *        more recent compilers. */
-        if (idAlg == CALG_MD5)
+        KU32        cbDigest;
+        const char *pszName;
+        switch (idAlg)
+        {
+            case CALG_MD5:
+                cbDigest = 128/8;
+                pszName  = "MD5";
+                g_cHashesMd5++;
+                break;
+            case CALG_SHA1:
+                cbDigest = 160/8;
+                pszName  = "SHA1";
+                g_cHashesSha1++;
+                break;
+            case CALG_SHA_256:
+                cbDigest = 256/8;
+                pszName  = "SHA-256";
+                g_cHashesSha256++;
+                break;
+            case CALG_SHA_512:
+                cbDigest = 512/8;
+                pszName  = "SHA-512";
+                g_cHashesSha512++;
+                break;
+            default:
+                cbDigest = 0;
+                pszName  = NULL;
+                break;
+        }
+
+        if (cbDigest)
         {
             if (hKey == 0)
@@ -9917,13 +10021,16 @@
                 if (dwFlags == 0)
                 {
-                    PKWHASHMD5 pHash = (PKWHASHMD5)kHlpAllocZ(sizeof(*pHash));
+                    PKWCRYPTHASH pHash = (PKWCRYPTHASH)kHlpAllocZ(sizeof(*pHash));
                     if (pHash)
                     {
                         kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
-                        pHash->uMagic        = KWHASHMD5_MAGIC;
+                        pHash->uMagic        = KWCRYPTHASH_MAGIC;
                         pHash->cbHashed      = 0;
                         pHash->fGoneBad      = K_FALSE;
-                        pHash->fFallbackMode = K_FALSE;
                         pHash->fFinal        = K_FALSE;
+                        pHash->hFallback     = KUPTR_MAX;
+                        pHash->idAlg         = idAlg;
+                        pHash->pszAlgName    = pszName;
+                        pHash->cbDigest      = cbDigest;
 
                         /* link it. */
@@ -9932,6 +10039,6 @@
 
                         *phHash = (KUPTR)pHash;
-                        KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=CALG_MD5, 0, 0, *phHash=%p) -> %d [cached]\n",
-                                     hProv, *phHash, TRUE));
+                        KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%s, 0, 0, *phHash=%p) -> %d [cached]\n",
+                                     hProv, pszName, *phHash, TRUE));
                         return TRUE;
                     }
@@ -9940,11 +10047,11 @@
                 }
                 else
-                    kwErrPrintf("CryptCreateHash: dwFlags=%p is not supported with CALG_MD5\n", hKey);
+                    kwErrPrintf("CryptCreateHash: dwFlags=%p is not supported with %s\n", hKey, pszName);
             }
             else
-                kwErrPrintf("CryptCreateHash: hKey=%p is not supported with CALG_MD5\n", hKey);
-        }
-        //else
-        //    kwErrPrintf("CryptCreateHash: idAlg=%#x is not supported\n", idAlg);
+                kwErrPrintf("CryptCreateHash: hKey=%p is not supported with %s\n", hKey, pszName);
+        }
+        else
+            kwErrPrintf("CryptCreateHash: idAlg=%#x is not supported\n", idAlg);
     }
 
@@ -9962,6 +10069,6 @@
 static BOOL WINAPI kwSandbox_Advapi32_CryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD cbData, DWORD dwFlags)
 {
-    BOOL        fRc;
-    PKWHASHMD5  pHash = g_Sandbox.pHashHead;
+    BOOL         fRc;
+    PKWCRYPTHASH pHash = g_Sandbox.pHashHead;
     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
     while (pHash && (KUPTR)pHash != hHash)
@@ -9974,8 +10081,9 @@
          * Validate the state.
          */
-        if (   pHash->uMagic == KWHASHMD5_MAGIC
-            && !pHash->fFinal)
-        {
-            if (!pHash->fFallbackMode)
+        if (   pHash->uMagic == KWCRYPTHASH_MAGIC
+            && !pHash->fFinal
+            && !pHash->fGoneBad)
+        {
+            if (pHash->hFallback == KUPTR_MAX)
             {
                 /*
@@ -10017,5 +10125,5 @@
                     }
                     else if (!pCachedFile)
-                        kwErrPrintf("CryptHashData: Last pCachedFile is NULL when buffer address and size matches!\n");
+                        KWCRYPT_LOG(("CryptHashData: Last pCachedFile is NULL when buffer address and size matches!\n"));
                     else
                         kwErrPrintf("CryptHashData: First 64 bytes of the buffer doesn't match the cache.\n");
@@ -10025,15 +10133,29 @@
                                 g_Sandbox.LastHashRead.cbRead, g_Sandbox.LastHashRead.pvRead, cbData, pbData);
                 if (pHash->cbHashed == 0)
-                    pHash->fFallbackMode = K_TRUE;
-                if (pHash->fFallbackMode)
                 {
                     /* Initiate fallback mode (file that we don't normally cache, like .c/.cpp). */
-                    pHash->fFallbackMode = K_TRUE;
-                    MD5Init(&pHash->Md5Ctx);
-                    MD5Update(&pHash->Md5Ctx, pbData, cbData);
-                    pHash->cbHashed = cbData;
-                    KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback!]\n",
-                                 hHash, pbData, cbData, dwFlags));
-                    return TRUE;
+                    HCRYPTPROV hCryptProv = kwSandboxGetCryptoProvider(pHash->idAlg);
+                    if (hCryptProv)
+                    {
+                        HCRYPTHASH hCryptHash = KUPTR_MAX;
+                        if (CryptCreateHash(hCryptProv, pHash->idAlg, 0, 0, &hCryptHash))
+                        {
+                            kHlpAssert(hCryptHash != KUPTR_MAX);
+                            pHash->hFallback = hCryptHash;
+                            fRc = CryptHashData(hCryptHash, pbData, cbData, dwFlags);
+                            if (fRc)
+                                pHash->cbHashed = cbData;
+                            g_cHashesFallbacks++;
+                            KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d (%u) [fallback!]\n",
+                                         hHash, pbData, cbData, dwFlags, fRc, GetLastError()));
+                        }
+                        else
+                        {
+                            kwErrPrintf("kwSandbox_Advapi32_CryptHashData: Fallback CryptCreateHash(%u) failed: %u\n",
+                                        pHash->idAlg, GetLastError());
+                            fRc = FALSE;
+                        }
+                        return fRc;
+                    }
                 }
                 pHash->fGoneBad = K_TRUE;
@@ -10044,9 +10166,9 @@
             {
                 /* fallback. */
-                MD5Update(&pHash->Md5Ctx, pbData, cbData);
-                pHash->cbHashed += cbData;
-                fRc = TRUE;
-                KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback]\n",
-                             hHash, pbData, cbData, dwFlags));
+                fRc = CryptHashData(pHash->hFallback, pbData, cbData, dwFlags);
+                if (fRc)
+                    pHash->cbHashed += cbData;
+                KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d [fallback]\n",
+                             hHash, pbData, cbData, dwFlags, fRc));
             }
         }
@@ -10056,5 +10178,5 @@
         else
         {
-            if (pHash->uMagic != KWHASHMD5_MAGIC)
+            if (pHash->uMagic != KWCRYPTHASH_MAGIC)
                 kwErrPrintf("CryptHashData: Invalid cached hash handle!!\n");
             else
@@ -10074,10 +10196,54 @@
 
 
+/** Helper for simpe data hashing.   */
+static BOOL kwSandboxCalcHash(ALG_ID idAlg, void const *pvData, KSIZE cbData, KU8 *pbDigest, KSIZE cbDigest)
+{
+    BOOL fRet = FALSE;
+    if (idAlg == CALG_MD5)
+    {
+        struct MD5Context Ctx;
+        MD5Init(&Ctx);
+        MD5Update(&Ctx, (unsigned char const *)pvData, (unsigned)cbData);
+        MD5Final(pbDigest, &Ctx);
+        fRet = TRUE;
+    }
+    else
+    {
+        HCRYPTPROV hCryptProv = kwSandboxGetCryptoProvider(idAlg);
+        if (hCryptProv)
+        {
+            HCRYPTHASH hCryptHash = KUPTR_MAX;
+            if (CryptCreateHash(hCryptProv, idAlg, 0, 0, &hCryptHash))
+            {
+                if (CryptHashData(hCryptHash, (const BYTE *)pvData, (DWORD)cbData, 0))
+                {
+                    DWORD cbActual = (DWORD)cbDigest;
+                    if (CryptGetHashParam(hCryptHash, HP_HASHVAL, pbDigest, &cbActual, 0))
+                    {
+                        fRet = TRUE;
+                        kHlpAssert(cbActual == cbDigest);
+                    }
+                    else
+                        kwErrPrintf("CryptGetHashParam([%#x],HP_HASHVAL,%p,%#x,0) failed: %u\n",
+                                    idAlg, pbDigest, cbDigest, GetLastError());
+                }
+                else
+                    kwErrPrintf("CryptHashData([%#x],%p,%#x,0) failed: %u\n", idAlg, pvData, cbData, GetLastError());
+                CryptDestroyHash(hCryptHash);
+            }
+            else
+                kwErrPrintf("CryptCreateHash(%#x) failed: %u\n", idAlg, GetLastError());
+        }
+    }
+    return fRet;
+}
+
+
 /** AdvApi32 - CryptGetHashParam */
 static BOOL WINAPI kwSandbox_Advapi32_CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam,
                                                         BYTE *pbData, DWORD *pcbData, DWORD dwFlags)
 {
-    BOOL        fRc;
-    PKWHASHMD5  pHash = g_Sandbox.pHashHead;
+    BOOL            fRc;
+    PKWCRYPTHASH    pHash = g_Sandbox.pHashHead;
     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
     while (pHash && (KUPTR)pHash != hHash)
@@ -10085,5 +10251,5 @@
     if (pHash)
     {
-        if (pHash->uMagic == KWHASHMD5_MAGIC)
+        if (pHash->uMagic == KWCRYPTHASH_MAGIC)
         {
             if (dwFlags == 0)
@@ -10102,4 +10268,5 @@
                         /* Check the hash progress. */
                         PKFSWCACHEDFILE pCachedFile = pHash->pCachedFile;
+                        g_cHashes++;
                         if (pCachedFile)
                         {
@@ -10107,15 +10274,43 @@
                                 && !pHash->fGoneBad)
                             {
-                                if (pCachedFile->fValidMd5)
+                                KBOOL *pfValid;
+                                switch (pHash->idAlg)
+                                {
+                                    case CALG_MD5:
+                                        pfValid = &pCachedFile->fValidMd5;
+                                        pvRet   = pCachedFile->abMd5Digest;
+                                        break;
+                                    case CALG_SHA1:
+                                        pfValid = &pCachedFile->fValidSha1;
+                                        pvRet   = pCachedFile->abSha1Digest;
+                                        break;
+                                    case CALG_SHA_256:
+                                        pfValid = &pCachedFile->fValidSha256;
+                                        pvRet   = pCachedFile->abSha256Digest;
+                                        break;
+                                    case CALG_SHA_512:
+                                        pfValid = &pCachedFile->fValidSha512;
+                                        pvRet   = pCachedFile->abSha512Digest;
+                                        break;
+                                    default:
+                                        kwErrPrintf("Invalid idAlg value: %#x\n", pHash->idAlg);
+                                        SetLastError(ERROR_INVALID_SERVER_STATE);
+                                        return FALSE;
+                                }
+
+                                if (*pfValid)
+                                {
                                     KWCRYPT_LOG(("Already calculated hash for %p/%s! [hit]\n", pCachedFile, pCachedFile->szPath));
+                                    g_cHashesCached++;
+                                }
                                 else
                                 {
-                                    MD5Init(&pHash->Md5Ctx);
-                                    MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pCachedFile->cbCached);
-                                    MD5Final(pCachedFile->abMd5Digest, &pHash->Md5Ctx);
-                                    pCachedFile->fValidMd5 = K_TRUE;
+                                    fRc = kwSandboxCalcHash(pHash->idAlg, pCachedFile->pbCached, pCachedFile->cbCached,
+                                                            pvRet, pHash->cbDigest);
+                                    if (!fRc)
+                                        return FALSE;
+                                    *pfValid = K_TRUE;
                                     KWCRYPT_LOG(("Calculated hash for %p/%s.\n", pCachedFile, pCachedFile->szPath));
                                 }
-                                pvRet = pCachedFile->abMd5Digest;
                             }
                             else
@@ -10126,25 +10321,34 @@
                                              pHash->pCachedFile->cbCached, pHash->cbHashed, pHash->fGoneBad,
                                              pHash, pCachedFile, pCachedFile->szPath));
-                                pHash->fFallbackMode = K_TRUE;
-                                pHash->pCachedFile   = NULL;
-                                MD5Init(&pHash->Md5Ctx);
-                                MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pHash->cbHashed);
-                                MD5Final(pHash->abDigest, &pHash->Md5Ctx);
+                                g_cHashesPartial++;
+                                pHash->pCachedFile = NULL;
                                 pvRet = pHash->abDigest;
+                                fRc = kwSandboxCalcHash(pHash->idAlg, pCachedFile->pbCached, pHash->cbHashed,
+                                                        pvRet, pHash->cbDigest);
+                                if (!fRc)
+                                {
+                                    pHash->fGoneBad = K_TRUE;
+                                    return FALSE;
+                                }
                             }
                             pHash->fFinal = K_TRUE;
-                            cbRet = 16;
+                            cbRet = pHash->cbDigest;
                             break;
                         }
-                        else if (pHash->fFallbackMode)
+
+                        pvRet = pHash->abDigest;
+                        cbRet = pHash->cbDigest;
+                        if (pHash->fFinal)
+                            break;
+                        if (pHash->hFallback != KUPTR_MAX)
                         {
-                            if (!pHash->fFinal)
+                            DWORD cbActual = (DWORD)pHash->cbDigest;
+                            if (CryptGetHashParam(pHash->hFallback, HP_HASHVAL, pHash->abDigest, &cbActual, 0))
                             {
+                                kHlpAssert(cbActual == pHash->cbDigest);
                                 pHash->fFinal = K_TRUE;
-                                MD5Final(pHash->abDigest, &pHash->Md5Ctx);
+                                break;
                             }
-                            pvRet = pHash->abDigest;
-                            cbRet = 16;
-                            break;
+                            kwErrPrintf("CryptGetHashParam/HP_HASHVAL: Fallback CryptGetHashParam failed: %u!!\n", GetLastError());
                         }
                         else
@@ -10157,5 +10361,5 @@
 
                     case HP_HASHSIZE:
-                        uBuf.dw = 16;
+                        uBuf.dw = pHash->cbDigest;
                         pvRet = &uBuf;
                         cbRet = sizeof(DWORD);
@@ -10163,5 +10367,5 @@
 
                     case HP_ALGID:
-                        uBuf.dw = CALG_MD5;
+                        uBuf.dw = pHash->idAlg;
                         pvRet = &uBuf;
                         cbRet = sizeof(DWORD);
@@ -10193,4 +10397,23 @@
                                          pbData[8],  pbData[9],  pbData[10], pbData[11],
                                          pbData[12], pbData[13], pbData[14], pbData[15]));
+                        else if (cbRet == 20)
+                            KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x [cached]\n",
+                                         dwParam, pHash, pHash->pCachedFile, cbRet,
+                                         pbData[0],  pbData[1],  pbData[2],  pbData[3],
+                                         pbData[4],  pbData[5],  pbData[6],  pbData[7],
+                                         pbData[8],  pbData[9],  pbData[10], pbData[11],
+                                         pbData[12], pbData[13], pbData[14], pbData[15],
+                                         pbData[16], pbData[17], pbData[18], pbData[19] ));
+                        else if (cbRet >= 32)
+                            KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%s [cached]\n",
+                                         dwParam, pHash, pHash->pCachedFile, cbRet,
+                                         pbData[0],  pbData[1],  pbData[2],  pbData[3],
+                                         pbData[4],  pbData[5],  pbData[6],  pbData[7],
+                                         pbData[8],  pbData[9],  pbData[10], pbData[11],
+                                         pbData[12], pbData[13], pbData[14], pbData[15],
+                                         pbData[16], pbData[17], pbData[18], pbData[19],
+                                         pbData[20], pbData[21], pbData[22], pbData[23],
+                                         pbData[24], pbData[25], pbData[26], pbData[27],
+                                         pbData[28], pbData[29], pbData[30], pbData[31], cbRet > 32 ? "..." : ""));
                         else
                             KWCRYPT_LOG(("CryptGetHashParam/%#x%/p%/%p: TRUE, cbRet=%#x [cached]\n",
@@ -10235,7 +10458,7 @@
 static BOOL WINAPI kwSandbox_Advapi32_CryptDestroyHash(HCRYPTHASH hHash)
 {
-    BOOL        fRc;
-    PKWHASHMD5  pPrev = NULL;
-    PKWHASHMD5  pHash = g_Sandbox.pHashHead;
+    BOOL            fRc;
+    PKWCRYPTHASH    pPrev = NULL;
+    PKWCRYPTHASH    pHash = g_Sandbox.pHashHead;
     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
     while (pHash && (KUPTR)pHash != hHash)
@@ -10246,5 +10469,5 @@
     if (pHash)
     {
-        if (pHash->uMagic == KWHASHMD5_MAGIC)
+        if (pHash->uMagic == KWCRYPTHASH_MAGIC)
         {
             pHash->uMagic = 0;
@@ -10276,5 +10499,5 @@
 }
 
-#endif /* WITH_HASH_MD5_CACHE */
+#endif /* WITH_HASH_CACHE */
 
 
@@ -10748,5 +10971,5 @@
 #endif
 
-#ifdef WITH_HASH_MD5_CACHE
+#ifdef WITH_HASH_CACHE
     { TUPLE("CryptCreateHash"),             NULL,       (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
     { TUPLE("CryptHashData"),               NULL,       (KUPTR)kwSandbox_Advapi32_CryptHashData },
@@ -10883,5 +11106,5 @@
 #endif
 
-#ifdef WITH_HASH_MD5_CACHE
+#ifdef WITH_HASH_CACHE
     { TUPLE("CryptCreateHash"),             NULL,       (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
     { TUPLE("CryptHashData"),               NULL,       (KUPTR)kwSandbox_Advapi32_CryptHashData },
@@ -11441,6 +11664,6 @@
     PKWHEAP                     pHeap;
     PKWLOCALSTORAGE             pLocalStorage;
-#ifdef WITH_HASH_MD5_CACHE
-    PKWHASHMD5                  pHash;
+#ifdef WITH_HASH_CACHE
+    PKWCRYPTHASH                pHash;
 #endif
 #ifdef WITH_TEMP_MEMORY_FILES
@@ -11627,5 +11850,5 @@
     }
 
-#ifdef WITH_HASH_MD5_CACHE
+#ifdef WITH_HASH_CACHE
     /*
      * Hash handles.
@@ -11635,6 +11858,8 @@
     while (pHash)
     {
-        PKWHASHMD5 pNext = pHash->pNext;
+        PKWCRYPTHASH pNext = pHash->pNext;
         KWCRYPT_LOG(("Freeing leaked hash instance %#p\n", pHash));
+        if (pHash->hFallback != KUPTR_MAX)
+            CryptDestroyHash(pHash->hFallback);
         kHlpFree(pHash);
         pHash = pNext;
@@ -12579,4 +12804,14 @@
                    (unsigned)(g_pFsCache->cNameGrowths * 100 / K_MAX(g_pFsCache->cNameChanges, 1)) );
 
+#ifdef WITH_HASH_CACHE
+    off += sprintf(&szBuf[off], "%s %14s hashes calculated, %s cache hits (%u%%), %s fallbacks, %s partial\n", szPrf,
+                   kwFmtU64(sz1, g_cHashes),
+                   kwFmtU64(sz2, g_cHashesCached),
+                   (unsigned)(g_cHashesCached * 100 / K_MAX(g_cHashes, 1)),
+                   kwFmtU64(sz3, g_cHashesFallbacks),
+                   kwFmtU64(sz4, g_cHashesPartial));
+    off += sprintf(&szBuf[off], "%s %14s MD5: %s  SHA-1: %s  SHA-256: %s  SHA-512: %s\n", szPrf, "", kwFmtU64(sz1, g_cHashesMd5),
+                   kwFmtU64(sz2, g_cHashesSha1), kwFmtU64(sz3, g_cHashesSha256), kwFmtU64(sz4, g_cHashesSha512));
+#endif
 
     /*
