Bug 1606992 - Cache the most recent PBKDF2 password hash, to speed up repeated SDR operations. r=jcj
authorKai Engert <kaie@kuix.de>
Sat, 11 Jan 2020 10:59:54 +0100
changeset 15453 a06bd0f6bbe85c79f804b1074f3bd30248db0c7a
parent 15452 4921046404f197526969a6b79f19c136469e69f8
child 15460 64c5410f98e0df3d242442c8c9945eed02d4f64f
push id3631
push userkaie@kuix.de
push dateSat, 11 Jan 2020 10:00:17 +0000
reviewersjcj
bugs1606992
Bug 1606992 - Cache the most recent PBKDF2 password hash, to speed up repeated SDR operations. r=jcj
lib/softoken/lowpbe.c
lib/softoken/pkcs11.c
--- a/lib/softoken/lowpbe.c
+++ b/lib/softoken/lowpbe.c
@@ -552,16 +552,62 @@ loser:
         } else {
             A->len = bytesNeeded;
         }
     }
 
     return A;
 }
 
+/* Bug 1606992 - Cache the hash result for the common case that we're
+ * asked to repeatedly compute the key for the same password item,
+ * hash, iterations and salt. */
+static PZLock *PBE_cache_lock = NULL;
+static SECItem *cached_PBKDF2_item = NULL;
+static HASH_HashType cached_hashType;
+static int cached_iterations;
+static int cached_keyLen;
+static SECItem *cached_salt = NULL;
+static SECItem *cached_pwitem = NULL;
+
+void
+sftk_PBELockInit(void)
+{
+    if (!PBE_cache_lock) {
+        PBE_cache_lock = PZ_NewLock(nssIPBECacheLock);
+    }
+}
+
+static void
+sftk_clearPBECacheItems(void)
+{
+    if (cached_PBKDF2_item) {
+        SECITEM_FreeItem(cached_PBKDF2_item, PR_TRUE);
+        cached_PBKDF2_item = NULL;
+    }
+    if (cached_salt) {
+        SECITEM_FreeItem(cached_salt, PR_TRUE);
+        cached_salt = NULL;
+    }
+    if (cached_pwitem) {
+        SECITEM_FreeItem(cached_pwitem, PR_TRUE);
+        cached_pwitem = NULL;
+    }
+}
+
+void
+sftk_PBELockShutdown(void)
+{
+    if (PBE_cache_lock) {
+        PZ_DestroyLock(PBE_cache_lock);
+        PBE_cache_lock = 0;
+    }
+    sftk_clearPBECacheItems();
+}
+
 /*
  * generate key as per PKCS 5
  */
 SECItem *
 nsspkcs5_ComputeKeyAndIV(NSSPKCS5PBEParameter *pbe_param, SECItem *pwitem,
                          SECItem *iv, PRBool faulty3DES)
 {
     SECItem *hash = NULL, *key = NULL;
@@ -595,17 +641,45 @@ nsspkcs5_ComputeKeyAndIV(NSSPKCS5PBEPara
             }
             PORT_Assert(hash->len >= key->len + (getIV ? iv->len : 0));
             if (getIV) {
                 PORT_Memcpy(iv->data, hash->data + (hash->len - iv->len), iv->len);
             }
 
             break;
         case NSSPKCS5_PBKDF2:
-            hash = nsspkcs5_PBKDF2(hashObj, pbe_param, pwitem);
+            PZ_Lock(PBE_cache_lock);
+            if (cached_PBKDF2_item) {
+                if (pbe_param->hashType == cached_hashType &&
+                    pbe_param->iter == cached_iterations &&
+                    pbe_param->keyLen == cached_keyLen &&
+                    cached_salt &&
+                    SECITEM_ItemsAreEqual(&pbe_param->salt, cached_salt) &&
+                    cached_pwitem &&
+                    SECITEM_ItemsAreEqual(pwitem, cached_pwitem)) {
+                    hash = SECITEM_DupItem(cached_PBKDF2_item);
+                } else {
+                    sftk_clearPBECacheItems();
+                }
+            }
+            PZ_Unlock(PBE_cache_lock);
+            if (!hash) {
+                hash = nsspkcs5_PBKDF2(hashObj, pbe_param, pwitem);
+                PZ_Lock(PBE_cache_lock);
+                /* ensure no other thread was quicker than us setting the cache */
+                if (!cached_PBKDF2_item) {
+                    cached_PBKDF2_item = SECITEM_DupItem(hash);
+                    cached_hashType = pbe_param->hashType;
+                    cached_iterations = pbe_param->iter;
+                    cached_keyLen = pbe_param->keyLen;
+                    cached_salt = SECITEM_DupItem(&pbe_param->salt);
+                    cached_pwitem = SECITEM_DupItem(pwitem);
+                }
+                PZ_Unlock(PBE_cache_lock);
+            }
             if (getIV) {
                 PORT_Memcpy(iv->data, pbe_param->ivData, iv->len);
             }
             break;
         case NSSPKCS5_PKCS12_V2:
             if (getIV) {
                 hash = nsspkcs5_PKCS12PBE(hashObj, pbe_param, pwitem,
                                           pbeBitGenCipherIV, iv->len);
--- a/lib/softoken/pkcs11.c
+++ b/lib/softoken/pkcs11.c
@@ -3099,32 +3099,37 @@ sftk_closePeer(PRBool isFIPS)
     slot = (SFTKSlot *)PL_HashTableLookup(tmpSlotHashTable, (void *)slotID);
     if (slot == NULL) {
         return;
     }
     sftk_DBShutdown(slot);
     return;
 }
 
+extern void sftk_PBELockInit(void);
+extern void sftk_PBELockShutdown(void);
+
 /* NSC_Initialize initializes the Cryptoki library. */
 CK_RV
 nsc_CommonInitialize(CK_VOID_PTR pReserved, PRBool isFIPS)
 {
     CK_RV crv = CKR_OK;
     SECStatus rv;
     CK_C_INITIALIZE_ARGS *init_args = (CK_C_INITIALIZE_ARGS *)pReserved;
     int i;
     int moduleIndex = isFIPS ? NSC_FIPS_MODULE : NSC_NON_FIPS_MODULE;
 
     if (isFIPS) {
         loginWaitTime = PR_SecondsToInterval(1);
     }
 
     ENABLE_FORK_CHECK();
 
+    sftk_PBELockInit();
+
     rv = SECOID_Init();
     if (rv != SECSuccess) {
         crv = CKR_DEVICE_ERROR;
         return crv;
     }
 
     rv = RNG_RNGInit(); /* initialize random number generator */
     if (rv != SECSuccess) {
@@ -3295,16 +3300,18 @@ nsc_CommonFinalize(CK_VOID_PTR pReserved
     /* unload freeBL shared library from memory. This may only decrement the
      * OS refcount if it's been loaded multiple times, eg. by libssl */
     BL_Unload();
 #endif
 
     /* clean up the default OID table */
     SECOID_Shutdown();
 
+    sftk_PBELockShutdown();
+
     /* reset fork status in util */
     UTIL_SetForkState(PR_FALSE);
 
     nsc_init = PR_FALSE;
 
 #ifdef CHECK_FORK_MIXED
     if (!usePthread_atfork) {
         myPid = 0; /* allow CHECK_FORK in the next softoken initialization to