Bug 1562671 - Support higher iteration count for Master Password KDF. Bob Relyea's base patch. Requires the follow-up patch. r=kaie
authorBob Relyea <rrelyea@redhat.com>
Fri, 01 Nov 2019 10:37:23 +0100
changeset 15366 6619bb43d746307df1db57bdd5bbaa17aaec6337
parent 15365 dbba7db4b79d43ff5e2e655f7a978eb5e43d6458
child 15367 ced91a705aa399e63e2084c608a31faf947a06a4
push id3561
push userkaie@kuix.de
push dateFri, 01 Nov 2019 09:57:47 +0000
reviewerskaie
bugs1562671
Bug 1562671 - Support higher iteration count for Master Password KDF. Bob Relyea's base patch. Requires the follow-up patch. r=kaie
lib/softoken/legacydb/keydb.c
lib/softoken/lgglue.c
lib/softoken/pkcs11.c
lib/softoken/sftkdb.c
lib/softoken/sftkdb.h
lib/softoken/sftkdbti.h
lib/softoken/sftkpwd.c
--- a/lib/softoken/legacydb/keydb.c
+++ b/lib/softoken/legacydb/keydb.c
@@ -1208,24 +1208,37 @@ static SECItem *
 nsslowkey_EncodePW(SECOidTag alg, const SECItem *salt, SECItem *data)
 {
     NSSLowPasswordDataParam param;
     LGEncryptedDataInfo edi;
     PLArenaPool *arena;
     unsigned char one = 1;
     SECItem *epw = NULL;
     SECItem *encParam;
+    int iterLen = 0;
+    int saltLen;
     SECStatus rv;
 
     param.salt = *salt;
     param.iter.type = siBuffer; /* encode as signed integer */
     param.iter.data = &one;
     param.iter.len = 1;
     edi.encryptedData = *data;
 
+    iterLen = salt->len > 1 ? salt->data[salt->len - 1] : 2;
+    saltLen = (salt->len - iterLen) - 1;
+    /* if the resulting saltLen is a sha hash length, then assume that
+     * the iteration count is tacked on the end of the buffer */
+    if ((saltLen == SHA1_LENGTH) || (saltLen == SHA256_LENGTH) || (saltLen == SHA384_LENGTH) || (saltLen == SHA224_LENGTH) ||
+        (saltLen == SHA512_LENGTH)) {
+        param.iter.data = &salt->data[saltLen];
+        param.iter.len = iterLen;
+        param.salt.len = saltLen;
+    }
+
     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
     if (arena == NULL) {
         return NULL;
     }
 
     encParam = SEC_ASN1EncodeItem(arena, NULL, &param,
                                   NSSLOWPasswordParamTemplate);
     if (encParam == NULL) {
@@ -1265,19 +1278,33 @@ nsslowkey_DecodePW(const SECItem *derDat
         goto loser;
     }
     *alg = SECOID_GetAlgorithmTag(&edi.algorithm);
     rv = SEC_QuickDERDecodeItem(arena, &param, NSSLOWPasswordParamTemplate,
                                 &edi.algorithm.parameters);
     if (rv != SECSuccess) {
         goto loser;
     }
-    rv = SECITEM_CopyItem(NULL, salt, &param.salt);
-    if (rv != SECSuccess) {
-        goto loser;
+    /* if the iteration count isn't one, tack it at the end of the salt */
+    if (!((param.iter.len == 1) && (param.iter.data[0] == 1))) {
+        int total_len = param.salt.len + param.iter.len + 1;
+        salt->data = PORT_Alloc(total_len);
+        if (salt->data == NULL) {
+            goto loser;
+        }
+        PORT_Memcpy(salt->data, param.salt.data, param.salt.len);
+        PORT_Memcpy(&salt->data[param.salt.len], param.iter.data,
+                    param.iter.len);
+        salt->data[total_len - 1] = param.iter.len;
+        salt->len = total_len;
+    } else {
+        rv = SECITEM_CopyItem(NULL, salt, &param.salt);
+        if (rv != SECSuccess) {
+            goto loser;
+        }
     }
     pwe = SECITEM_DupItem(&edi.encryptedData);
 
 loser:
     if (!pwe && salt->data) {
         PORT_Free(salt->data);
         salt->data = NULL;
     }
--- a/lib/softoken/lgglue.c
+++ b/lib/softoken/lgglue.c
@@ -166,16 +166,18 @@ done:
  * various keys and attributes.
  */
 static SECStatus
 sftkdb_encrypt_stub(PLArenaPool *arena, SDB *sdb, SECItem *plainText,
                     SECItem **cipherText)
 {
     SFTKDBHandle *handle = sdb->app_private;
     SECStatus rv;
+    SECItem *key;
+    int iterationCount;
 
     if (handle == NULL) {
         return SECFailure;
     }
 
     /* if we aren't the key handle, try the other handle */
     if (handle->type != SFTK_KEYDB_TYPE) {
         handle = handle->peerDB;
@@ -187,19 +189,25 @@ sftkdb_encrypt_stub(PLArenaPool *arena, 
     }
 
     PZ_Lock(handle->passwordLock);
     if (handle->passwordKey.data == NULL) {
         PZ_Unlock(handle->passwordLock);
         /* PORT_SetError */
         return SECFailure;
     }
+    if (handle->newKey) {
+        key = handle->newKey;
+        iterationCount = handle->newDefaultIterationCount;
+    } else {
+        key = &handle->passwordKey;
+        iterationCount = handle->defaultIterationCount;
+    }
 
-    rv = sftkdb_EncryptAttribute(arena,
-                                 handle->newKey ? handle->newKey : &handle->passwordKey,
+    rv = sftkdb_EncryptAttribute(arena, key, iterationCount,
                                  plainText, cipherText);
     PZ_Unlock(handle->passwordLock);
 
     return rv;
 }
 
 /*
  * stub files for legacy db's to be able to encrypt and decrypt
--- a/lib/softoken/pkcs11.c
+++ b/lib/softoken/pkcs11.c
@@ -621,17 +621,17 @@ sftk_configure(const char *man, const ch
 static PRBool
 sftk_hasNullPassword(SFTKSlot *slot, SFTKDBHandle *keydb)
 {
     PRBool pwenabled;
 
     pwenabled = PR_FALSE;
     if (sftkdb_HasPasswordSet(keydb) == SECSuccess) {
         PRBool tokenRemoved = PR_FALSE;
-        SECStatus rv = sftkdb_CheckPassword(keydb, "", &tokenRemoved);
+        SECStatus rv = sftkdb_CheckPasswordNull(keydb, &tokenRemoved);
         if (tokenRemoved) {
             sftk_CloseAllSessions(slot, PR_FALSE);
         }
         return (rv == SECSuccess);
     }
 
     return pwenabled;
 }
@@ -3942,17 +3942,17 @@ NSC_SetPIN(CK_SESSION_HANDLE hSession, C
         /* Reset login flags. */
         if (ulNewLen == 0) {
             PZ_Lock(slot->slotLock);
             slot->isLoggedIn = PR_FALSE;
             slot->ssoLoggedIn = PR_FALSE;
             PZ_Unlock(slot->slotLock);
 
             tokenRemoved = PR_FALSE;
-            rv = sftkdb_CheckPassword(handle, "", &tokenRemoved);
+            rv = sftkdb_CheckPasswordNull(handle, &tokenRemoved);
             if (tokenRemoved) {
                 sftk_CloseAllSessions(slot, PR_FALSE);
             }
         }
         sftk_update_all_states(slot);
         sftk_freeDB(handle);
         return CKR_OK;
     }
--- a/lib/softoken/sftkdb.c
+++ b/lib/softoken/sftkdb.c
@@ -525,16 +525,17 @@ sftk_signTemplate(PLArenaPool *arena, SF
             plainText.len = template[i].ulValueLen;
             PZ_Lock(keyHandle->passwordLock);
             if (keyHandle->passwordKey.data == NULL) {
                 PZ_Unlock(keyHandle->passwordLock);
                 crv = CKR_USER_NOT_LOGGED_IN;
                 goto loser;
             }
             rv = sftkdb_SignAttribute(arena, &keyHandle->passwordKey,
+                                      keyHandle->defaultIterationCount,
                                       objectID, template[i].type,
                                       &plainText, &signText);
             PZ_Unlock(keyHandle->passwordLock);
             if (rv != SECSuccess) {
                 crv = CKR_GENERAL_ERROR; /* better error code here? */
                 goto loser;
             }
             rv = sftkdb_PutAttributeSignature(handle, keyTarget,
@@ -658,16 +659,17 @@ sftk_ExtractTemplate(PLArenaPool *arena,
                 plainText.len = tp->ulValueLen;
                 PZ_Lock(handle->passwordLock);
                 if (handle->passwordKey.data == NULL) {
                     PZ_Unlock(handle->passwordLock);
                     *crv = CKR_USER_NOT_LOGGED_IN;
                     break;
                 }
                 rv = sftkdb_EncryptAttribute(arena, &handle->passwordKey,
+                                             handle->defaultIterationCount,
                                              &plainText, &cipherText);
                 PZ_Unlock(handle->passwordLock);
                 if (rv == SECSuccess) {
                     tp->pValue = cipherText->data;
                     tp->ulValueLen = cipherText->len;
                 } else {
                     *crv = CKR_GENERAL_ERROR; /* better error code here? */
                     break;
@@ -2754,17 +2756,17 @@ sftk_DBInit(const char *configdir, const
                 (*keyDB)->update = updateKey;
                 (*keyDB)->updateID = updateID && *updateID ? PORT_Strdup(updateID) : NULL;
                 updateKey->app_private = (*keyDB);
                 (*keyDB)->updateDBIsInit = PR_TRUE;
                 (*keyDB)->updateDBIsInit =
                     (sftkdb_HasPasswordSet(*keyDB) == SECSuccess) ? PR_TRUE : PR_FALSE;
                 /* if the password on the key db is NULL, kick off our update
                  * chain of events */
-                sftkdb_CheckPassword((*keyDB), "", &tokenRemoved);
+                sftkdb_CheckPasswordNull((*keyDB), &tokenRemoved);
             } else {
                 /* we don't have a key DB, update the certificate DB now */
                 sftkdb_Update(*certDB, NULL);
             }
         }
     }
 done:
     if (appName) {
--- a/lib/softoken/sftkdb.h
+++ b/lib/softoken/sftkdb.h
@@ -20,16 +20,17 @@ CK_RV sftkdb_SetAttributeValue(SFTKDBHan
 CK_RV sftkdb_DestroyObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE object_id);
 CK_RV sftkdb_closeDB(SFTKDBHandle *handle);
 
 /* keydb functions */
 
 SECStatus sftkdb_PWIsInitialized(SFTKDBHandle *keydb);
 SECStatus sftkdb_CheckPassword(SFTKDBHandle *keydb, const char *pw,
                                PRBool *tokenRemoved);
+SECStatus sftkdb_CheckPasswordNull(SFTKDBHandle *keydb, PRBool *tokenRemoved);
 SECStatus sftkdb_PWCached(SFTKDBHandle *keydb);
 SECStatus sftkdb_HasPasswordSet(SFTKDBHandle *keydb);
 SECStatus sftkdb_ResetKeyDB(SFTKDBHandle *keydb);
 SECStatus sftkdb_ChangePassword(SFTKDBHandle *keydb,
                                 char *oldPin, char *newPin,
                                 PRBool *tokenRemoved);
 SECStatus sftkdb_ClearPassword(SFTKDBHandle *keydb);
 PRBool sftkdb_InUpdateMerge(SFTKDBHandle *keydb);
--- a/lib/softoken/sftkdbti.h
+++ b/lib/softoken/sftkdbti.h
@@ -8,17 +8,19 @@
 /*
  * private defines
  */
 struct SFTKDBHandleStr {
     SDB *db;
     PRInt32 ref;
     CK_OBJECT_HANDLE type;
     SECItem passwordKey;
+    int defaultIterationCount;
     SECItem *newKey;
+    int newDefaultIterationCount;
     SECItem *oldKey;
     SECItem *updatePasswordKey;
     PZLock *passwordLock;
     SFTKDBHandle *peerDB;
     SDB *update;
     char *updateID;
     PRBool updateDBIsInit;
 };
@@ -34,19 +36,20 @@ struct SFTKDBHandleStr {
 #define SFTK_MAX_IDS 10
 
 #define SFTK_GET_SDB(handle) \
     ((handle)->update ? (handle)->update : (handle)->db)
 
 SECStatus sftkdb_DecryptAttribute(SECItem *passKey, SECItem *cipherText,
                                   SECItem **plainText);
 SECStatus sftkdb_EncryptAttribute(PLArenaPool *arena, SECItem *passKey,
-                                  SECItem *plainText, SECItem **cipherText);
+                                  int iterationCount, SECItem *plainText,
+                                  SECItem **cipherText);
 SECStatus sftkdb_SignAttribute(PLArenaPool *arena, SECItem *passKey,
-                               CK_OBJECT_HANDLE objectID,
+                               int iterationCount, CK_OBJECT_HANDLE objectID,
                                CK_ATTRIBUTE_TYPE attrType,
                                SECItem *plainText, SECItem **sigText);
 SECStatus sftkdb_VerifyAttribute(SECItem *passKey,
                                  CK_OBJECT_HANDLE objectID,
                                  CK_ATTRIBUTE_TYPE attrType,
                                  SECItem *plainText, SECItem *sigText);
 
 PRBool sftkdb_isULONGAttribute(CK_ATTRIBUTE_TYPE type);
--- a/lib/softoken/sftkpwd.c
+++ b/lib/softoken/sftkpwd.c
@@ -127,17 +127,17 @@ const SEC_ASN1Template sftkdb_EncryptedD
     { 0 }
 };
 
 /*
  * This parses the cipherText into cipher value. NOTE: cipherValue will point
  * to data in cipherText, if cipherText is freed, cipherValue will be invalid.
  */
 static SECStatus
-sftkdb_decodeCipherText(SECItem *cipherText, sftkCipherValue *cipherValue)
+sftkdb_decodeCipherText(const SECItem *cipherText, sftkCipherValue *cipherValue)
 {
     PLArenaPool *arena = NULL;
     SFTKDBEncryptedDataInfo edi;
     SECStatus rv;
 
     PORT_Assert(cipherValue);
     cipherValue->arena = NULL;
     cipherValue->param = NULL;
@@ -220,17 +220,18 @@ loser:
 
 /*
  * Use our key to decode a cipherText block from the database.
  *
  * plain text is allocated by nsspkcs5_CipherData and must be freed
  * with SECITEM_FreeItem by the caller.
  */
 SECStatus
-sftkdb_DecryptAttribute(SECItem *passKey, SECItem *cipherText, SECItem **plain)
+sftkdb_DecryptAttribute(SECItem *passKey, SECItem *cipherText,
+                        SECItem **plain)
 {
     SECStatus rv;
     sftkCipherValue cipherValue;
 
     /* First get the cipher type */
     rv = sftkdb_decodeCipherText(cipherText, &cipherValue);
     if (rv != SECSuccess) {
         goto loser;
@@ -256,31 +257,32 @@ loser:
 /*
  * encrypt a block. This function returned the encrypted ciphertext which
  * the caller must free. If the caller provides an arena, cipherText will
  * be allocated out of that arena. This also generated the per entry
  * salt automatically.
  */
 SECStatus
 sftkdb_EncryptAttribute(PLArenaPool *arena, SECItem *passKey,
-                        SECItem *plainText, SECItem **cipherText)
+                        int iterationCount, SECItem *plainText,
+                        SECItem **cipherText)
 {
     SECStatus rv;
     sftkCipherValue cipherValue;
     SECItem *cipher = NULL;
     NSSPKCS5PBEParameter *param = NULL;
     unsigned char saltData[HASH_LENGTH_MAX];
 
     cipherValue.alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC;
     cipherValue.salt.len = SHA1_LENGTH;
     cipherValue.salt.data = saltData;
     RNG_GenerateGlobalRandomBytes(saltData, cipherValue.salt.len);
 
     param = nsspkcs5_NewParam(cipherValue.alg, HASH_AlgSHA1, &cipherValue.salt,
-                              1);
+                              iterationCount);
     if (param == NULL) {
         rv = SECFailure;
         goto loser;
     }
     cipher = nsspkcs5_CipherData(param, passKey, plainText, PR_TRUE, NULL);
     if (cipher == NULL) {
         rv = SECFailure;
         goto loser;
@@ -408,17 +410,18 @@ loser:
 }
 
 /*
  * Use our key to create a signText block the plain text of an
  * attribute. The signText is a PKCS 5 v2 pbe.
  */
 SECStatus
 sftkdb_SignAttribute(PLArenaPool *arena, SECItem *passKey,
-                     CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE attrType,
+                     int iterationCount, CK_OBJECT_HANDLE objectID,
+                     CK_ATTRIBUTE_TYPE attrType,
                      SECItem *plainText, SECItem **signature)
 {
     SECStatus rv;
     sftkCipherValue signValue;
     NSSPKCS5PBEParameter *param = NULL;
     unsigned char saltData[HASH_LENGTH_MAX];
     unsigned char signData[HASH_LENGTH_MAX];
     SECOidTag hmacAlg = SEC_OID_HMAC_SHA256; /* hash for authentication */
@@ -441,17 +444,18 @@ sftkdb_SignAttribute(PLArenaPool *arena,
     signValue.alg = SEC_OID_PKCS5_PBMAC1;
     signValue.salt.len = prfLength;
     signValue.salt.data = saltData;
     signValue.value.data = signData;
     signValue.value.len = hmacLength;
     RNG_GenerateGlobalRandomBytes(saltData, prfLength);
 
     /* initialize our pkcs5 parameter */
-    param = nsspkcs5_NewParam(signValue.alg, HASH_AlgSHA1, &signValue.salt, 1);
+    param = nsspkcs5_NewParam(signValue.alg, HASH_AlgSHA1, &signValue.salt,
+                              iterationCount);
     if (param == NULL) {
         rv = SECFailure;
         goto loser;
     }
     param->keyID = pbeBitGenIntegrityKey;
     /* set the PKCS 5 v2 parameters, not extractable from the
      * data passed into nsspkcs5_NewParam */
     param->encAlg = hmacAlg;
@@ -486,32 +490,33 @@ loser:
 /*
  * safely swith the passed in key for the one caches in the keydb handle
  *
  * A key attached to the handle tells us the the token is logged in.
  * We can used the key attached to the handle in sftkdb_EncryptAttribute
  *  and sftkdb_DecryptAttribute calls.
  */
 static void
-sftkdb_switchKeys(SFTKDBHandle *keydb, SECItem *passKey)
+sftkdb_switchKeys(SFTKDBHandle *keydb, SECItem *passKey, int iterationCount)
 {
     unsigned char *data;
     int len;
 
     if (keydb->passwordLock == NULL) {
         PORT_Assert(keydb->type != SFTK_KEYDB_TYPE);
         return;
     }
 
     /* an atomic pointer set would be nice */
     SKIP_AFTER_FORK(PZ_Lock(keydb->passwordLock));
     data = keydb->passwordKey.data;
     len = keydb->passwordKey.len;
     keydb->passwordKey.data = passKey->data;
     keydb->passwordKey.len = passKey->len;
+    keydb->defaultIterationCount = iterationCount;
     passKey->data = data;
     passKey->len = len;
     SKIP_AFTER_FORK(PZ_Unlock(keydb->passwordLock));
 }
 
 /*
  * returns true if we are in a middle of a merge style update.
  */
@@ -655,31 +660,114 @@ sftkdb_HasPasswordSet(SFTKDBHandle *keyd
         if (keydb->peerDB) {
             sftkdb_Update(keydb->peerDB, NULL);
         }
         sftkdb_Update(keydb, NULL);
     }
     return (crv == CKR_OK) ? SECSuccess : SECFailure;
 }
 
+/* pull out the common final part of checking a password */
+SECStatus
+sftkdb_finishPasswordCheck(SFTKDBHandle *keydb, SECItem *key,
+                           const char *pw, SECItem *value,
+                           PRBool *tokenRemoved);
+
+/*
+ * check to see if we have the NULL password set.
+ * We special case the NULL password so that if you have no password set, you
+ * don't do thousands of hash rounds. This allows us to startup and get 
+ * webpages without slowdown in normal mode.
+ */
+SECStatus
+sftkdb_CheckPasswordNull(SFTKDBHandle *keydb, PRBool *tokenRemoved)
+{
+    /* just like sftkdb_CheckPassowd, we get the salt and value, and
+     * create a dbkey */
+    SECStatus rv;
+    SECItem salt, value;
+    unsigned char saltData[SDB_MAX_META_DATA_LEN];
+    unsigned char valueData[SDB_MAX_META_DATA_LEN];
+    SECItem key;
+    SDB *db;
+    CK_RV crv;
+    sftkCipherValue cipherValue;
+
+    cipherValue.param = NULL;
+    cipherValue.arena = NULL;
+
+    if (keydb == NULL) {
+        return SECFailure;
+    }
+
+    db = sftk_getPWSDB(keydb);
+    if (db == NULL) {
+        return SECFailure;
+    }
+
+    key.data = NULL;
+    key.len = 0;
+
+    /* get the entry from the database */
+    salt.data = saltData;
+    salt.len = sizeof(saltData);
+    value.data = valueData;
+    value.len = sizeof(valueData);
+    crv = (*db->sdb_GetMetaData)(db, "password", &salt, &value);
+    if (crv != CKR_OK) {
+        rv = SECFailure;
+        goto done;
+    }
+
+    /* get our intermediate key based on the entry salt value */
+    rv = sftkdb_passwordToKey(keydb, &salt, "", &key);
+    if (rv != SECSuccess) {
+        goto done;
+    }
+
+    /* First get the cipher type */
+    rv = sftkdb_decodeCipherText(&value, &cipherValue);
+    if (rv != SECSuccess) {
+        goto done;
+    }
+
+    if (cipherValue.param->iter != 1) {
+        rv = SECFailure;
+        goto done;
+    }
+
+    rv = sftkdb_finishPasswordCheck(keydb, &key, "", &value, tokenRemoved);
+
+done:
+    if (key.data) {
+        PORT_ZFree(key.data, key.len);
+    }
+    if (cipherValue.param) {
+        nsspkcs5_DestroyPBEParameter(cipherValue.param);
+    }
+    if (cipherValue.arena) {
+        PORT_FreeArena(cipherValue.arena, PR_FALSE);
+    }
+    return rv;
+}
+
 #define SFTK_PW_CHECK_STRING "password-check"
 #define SFTK_PW_CHECK_LEN 14
 
 /*
  * check if the supplied password is valid
  */
 SECStatus
 sftkdb_CheckPassword(SFTKDBHandle *keydb, const char *pw, PRBool *tokenRemoved)
 {
     SECStatus rv;
     SECItem salt, value;
     unsigned char saltData[SDB_MAX_META_DATA_LEN];
     unsigned char valueData[SDB_MAX_META_DATA_LEN];
     SECItem key;
-    SECItem *result = NULL;
     SDB *db;
     CK_RV crv;
 
     if (keydb == NULL) {
         return SECFailure;
     }
 
     db = sftk_getPWSDB(keydb);
@@ -705,18 +793,40 @@ sftkdb_CheckPassword(SFTKDBHandle *keydb
     }
 
     /* get our intermediate key based on the entry salt value */
     rv = sftkdb_passwordToKey(keydb, &salt, pw, &key);
     if (rv != SECSuccess) {
         goto done;
     }
 
+    rv = sftkdb_finishPasswordCheck(keydb, &key, pw, &value, tokenRemoved);
+
+done:
+    if (key.data) {
+        PORT_ZFree(key.data, key.len);
+    }
+    return rv;
+}
+
+/* we need to pass iterationCount in case we are updating a new database
+ * and from an old one. */
+SECStatus
+sftkdb_finishPasswordCheck(SFTKDBHandle *keydb, SECItem *key, const char *pw,
+                           SECItem *value, PRBool *tokenRemoved)
+{
+    SECItem *result = NULL;
+    SECStatus rv;
+    int iterationCount = NSS_DEFAULT_ITERATION_COUNT;
+
+    if (*pw == 0) {
+        iterationCount = 1;
+    }
     /* decrypt the entry value */
-    rv = sftkdb_DecryptAttribute(&key, &value, &result);
+    rv = sftkdb_DecryptAttribute(key, value, &result);
     if (rv != SECSuccess) {
         goto done;
     }
 
     /* if it's what we expect, update our key in the database handle and
      * return Success */
     if ((result->len == SFTK_PW_CHECK_LEN) &&
         PORT_Memcmp(result->data, SFTK_PW_CHECK_STRING, SFTK_PW_CHECK_LEN) == 0) {
@@ -747,17 +857,17 @@ sftkdb_CheckPassword(SFTKDBHandle *keydb
          */
         PZ_Lock(keydb->passwordLock);
         if (sftkdb_NeedUpdateDBPassword(keydb)) {
             /* Squirrel this special key away.
              * This has the side effect of turning sftkdb_NeedLegacyPW off,
              * as well as changing which database is returned from
              * SFTK_GET_PW_DB (thus effecting both sftkdb_CheckPassword()
              * and sftkdb_HasPasswordSet()) */
-            keydb->updatePasswordKey = SECITEM_DupItem(&key);
+            keydb->updatePasswordKey = SECITEM_DupItem(key);
             PZ_Unlock(keydb->passwordLock);
             if (keydb->updatePasswordKey == NULL) {
                 /* PORT_Error set by SECITEM_DupItem */
                 rv = SECFailure;
                 goto done;
             }
 
             /* Simulate a token removal -- we need to do this any
@@ -782,17 +892,17 @@ sftkdb_CheckPassword(SFTKDBHandle *keydb
                  *  block and we've already set that update password at this
                  *  point.  */
                 rv = sftkdb_CheckPassword(keydb, pw, tokenRemoved);
                 if (rv == SECSuccess) {
                     /* source and target databases have the same password, we
                      * are good to go */
                     goto done;
                 }
-                sftkdb_CheckPassword(keydb, "", tokenRemoved);
+                sftkdb_CheckPasswordNull(keydb, tokenRemoved);
 
                 /*
                  * Important 'NULL' code here. At this point either we
                  * succeeded in logging in with "" or we didn't.
                  *
                  *  If we did succeed at login, our machine state will be set
                  * to logged in appropriately. The application will find that
                  * it's logged in as soon as it opens a new session. We have
@@ -816,35 +926,32 @@ sftkdb_CheckPassword(SFTKDBHandle *keydb
                  * update will write the source DB's password record
                  * into the target DB just like it would in a non-merge
                  * update case. */
             }
         } else {
             PZ_Unlock(keydb->passwordLock);
         }
         /* load the keys, so the keydb can parse it's key set */
-        sftkdb_switchKeys(keydb, &key);
+        sftkdb_switchKeys(keydb, key, iterationCount);
 
         /* we need to update, do it now */
         if (((keydb->db->sdb_flags & SDB_RDONLY) == 0) && keydb->update) {
             /* update the peer certdb if it exists */
             if (keydb->peerDB) {
-                sftkdb_Update(keydb->peerDB, &key);
+                sftkdb_Update(keydb->peerDB, key);
             }
-            sftkdb_Update(keydb, &key);
+            sftkdb_Update(keydb, key);
         }
     } else {
         rv = SECFailure;
         /*PORT_SetError( bad password); */
     }
 
 done:
-    if (key.data) {
-        PORT_ZFree(key.data, key.len);
-    }
     if (result) {
         SECITEM_FreeItem(result, PR_TRUE);
     }
     return rv;
 }
 
 /*
  * return Success if the there is a cached password key.
@@ -852,17 +959,17 @@ done:
 SECStatus
 sftkdb_PWCached(SFTKDBHandle *keydb)
 {
     return keydb->passwordKey.data ? SECSuccess : SECFailure;
 }
 
 static CK_RV
 sftk_updateMacs(PLArenaPool *arena, SFTKDBHandle *handle,
-                CK_OBJECT_HANDLE id, SECItem *newKey)
+                CK_OBJECT_HANDLE id, SECItem *newKey, int iterationCount)
 {
     SFTKDBHandle *keyHandle = handle;
     SDB *keyTarget = NULL;
     if (handle->type != SFTK_KEYDB_TYPE) {
         keyHandle = handle->peerDB;
     }
     if (keyHandle == NULL) {
         return CKR_OK;
@@ -919,32 +1026,33 @@ sftk_updateMacs(PLArenaPool *arena, SFTK
             CK_ULONG value = *(CK_ULONG *)authAttr.pValue;
             sftk_ULong2SDBULong(authAttr.pValue, value);
             authAttr.ulValueLen = SDB_ULONG_SIZE;
         }
         SECItem *signText;
         SECItem plainText;
         plainText.data = authAttr.pValue;
         plainText.len = authAttr.ulValueLen;
-        if (sftkdb_SignAttribute(arena, newKey, id, authAttr.type, &plainText,
+        if (sftkdb_SignAttribute(arena, newKey, iterationCount, id,
+                                 authAttr.type, &plainText,
                                  &signText) != SECSuccess) {
             return CKR_GENERAL_ERROR;
         }
         if (sftkdb_PutAttributeSignature(handle, keyTarget, id, authAttr.type,
                                          signText) != SECSuccess) {
             return CKR_GENERAL_ERROR;
         }
     }
 
     return CKR_OK;
 }
 
 static CK_RV
 sftk_updateEncrypted(PLArenaPool *arena, SFTKDBHandle *keydb,
-                     CK_OBJECT_HANDLE id, SECItem *newKey)
+                     CK_OBJECT_HANDLE id, SECItem *newKey, int iterationCount)
 {
     CK_ATTRIBUTE_TYPE privAttrTypes[] = {
         CKA_VALUE,
         CKA_PRIVATE_EXPONENT,
         CKA_PRIME_1,
         CKA_PRIME_2,
         CKA_EXPONENT_1,
         CKA_EXPONENT_2,
@@ -975,60 +1083,63 @@ sftk_updateEncrypted(PLArenaPool *arena,
         }
         if ((privAttr.ulValueLen == -1) || (privAttr.ulValueLen == 0)) {
             return CKR_GENERAL_ERROR;
         }
         SECItem plainText;
         SECItem *result;
         plainText.data = privAttr.pValue;
         plainText.len = privAttr.ulValueLen;
-        if (sftkdb_EncryptAttribute(arena, newKey, &plainText, &result) != SECSuccess) {
+        if (sftkdb_EncryptAttribute(arena, newKey, iterationCount,
+                                    &plainText, &result) != SECSuccess) {
             return CKR_GENERAL_ERROR;
         }
         privAttr.pValue = result->data;
         privAttr.ulValueLen = result->len;
         // Clear sensitive data.
         PORT_Memset(plainText.data, 0, plainText.len);
 
         // Write the newly encrypted attributes out directly.
         CK_OBJECT_HANDLE newId = id & SFTK_OBJ_ID_MASK;
         keydb->newKey = newKey;
+        keydb->newDefaultIterationCount = iterationCount;
         crv = (*keydb->db->sdb_SetAttributeValue)(keydb->db, newId, &privAttr, 1);
         keydb->newKey = NULL;
         if (crv != CKR_OK) {
             return crv;
         }
     }
 
     return CKR_OK;
 }
 
 static CK_RV
-sftk_convertAttributes(SFTKDBHandle *handle,
-                       CK_OBJECT_HANDLE id, SECItem *newKey)
+sftk_convertAttributes(SFTKDBHandle *handle, CK_OBJECT_HANDLE id,
+                       SECItem *newKey, int iterationCount)
 {
     CK_RV crv = CKR_OK;
     PLArenaPool *arena = NULL;
 
     /* get a new arena to simplify cleanup */
     arena = PORT_NewArena(1024);
     if (!arena) {
         return CKR_HOST_MEMORY;
     }
 
     /*
      * first handle the MACS
      */
-    crv = sftk_updateMacs(arena, handle, id, newKey);
+    crv = sftk_updateMacs(arena, handle, id, newKey, iterationCount);
     if (crv != CKR_OK) {
         goto loser;
     }
 
     if (handle->type == SFTK_KEYDB_TYPE) {
-        crv = sftk_updateEncrypted(arena, handle, id, newKey);
+        crv = sftk_updateEncrypted(arena, handle, id, newKey,
+                                   iterationCount);
         if (crv != CKR_OK) {
             goto loser;
         }
     }
 
     /* free up our mess */
     /* NOTE: at this point we know we've cleared out any unencrypted data */
     PORT_FreeArena(arena, PR_FALSE);
@@ -1040,33 +1151,34 @@ loser:
     return crv;
 }
 
 /*
  * must be called with the old key active.
  */
 CK_RV
 sftkdb_convertObjects(SFTKDBHandle *handle, CK_ATTRIBUTE *template,
-                      CK_ULONG count, SECItem *newKey)
+                      CK_ULONG count, SECItem *newKey, int iterationCount)
 {
     SDBFind *find = NULL;
     CK_ULONG idCount = SFTK_MAX_IDS;
     CK_OBJECT_HANDLE ids[SFTK_MAX_IDS];
     CK_RV crv, crv2;
     unsigned int i;
 
     crv = sftkdb_FindObjectsInit(handle, template, count, &find);
 
     if (crv != CKR_OK) {
         return crv;
     }
     while ((crv == CKR_OK) && (idCount == SFTK_MAX_IDS)) {
         crv = sftkdb_FindObjects(handle, find, ids, SFTK_MAX_IDS, &idCount);
         for (i = 0; (crv == CKR_OK) && (i < idCount); i++) {
-            crv = sftk_convertAttributes(handle, ids[i], newKey);
+            crv = sftk_convertAttributes(handle, ids[i], newKey,
+                                         iterationCount);
         }
     }
     crv2 = sftkdb_FindObjectsFinal(handle, find);
     if (crv == CKR_OK)
         crv = crv2;
 
     return crv;
 }
@@ -1081,16 +1193,17 @@ sftkdb_ChangePassword(SFTKDBHandle *keyd
     SECStatus rv = SECSuccess;
     SECItem plainText;
     SECItem newKey;
     SECItem *result = NULL;
     SECItem salt, value;
     SFTKDBHandle *certdb;
     unsigned char saltData[SDB_MAX_META_DATA_LEN];
     unsigned char valueData[SDB_MAX_META_DATA_LEN];
+    int iterationCount = NSS_DEFAULT_ITERATION_COUNT;
     CK_RV crv;
     SDB *db;
 
     if (keydb == NULL) {
         return SECFailure;
     }
 
     db = SFTK_GET_SDB(keydb);
@@ -1116,53 +1229,60 @@ sftkdb_ChangePassword(SFTKDBHandle *keyd
         if (rv == SECFailure) {
             goto loser;
         }
     } else {
         salt.len = SHA1_LENGTH;
         RNG_GenerateGlobalRandomBytes(salt.data, salt.len);
     }
 
+    if (newPin && *newPin == 0) {
+        iterationCount = 1;
+    }
+
     rv = sftkdb_passwordToKey(keydb, &salt, newPin, &newKey);
     if (rv != SECSuccess) {
         goto loser;
     }
 
     /*
      * convert encrypted entries here.
      */
-    crv = sftkdb_convertObjects(keydb, NULL, 0, &newKey);
+    crv = sftkdb_convertObjects(keydb, NULL, 0, &newKey, iterationCount);
     if (crv != CKR_OK) {
         rv = SECFailure;
         goto loser;
     }
     /* fix up certdb macs */
     certdb = keydb->peerDB;
     if (certdb) {
         CK_ATTRIBUTE objectType = { CKA_CLASS, 0, sizeof(CK_OBJECT_CLASS) };
         CK_OBJECT_CLASS myClass = CKO_NETSCAPE_TRUST;
 
         objectType.pValue = &myClass;
-        crv = sftkdb_convertObjects(certdb, &objectType, 1, &newKey);
+        crv = sftkdb_convertObjects(certdb, &objectType, 1, &newKey,
+                                    iterationCount);
         if (crv != CKR_OK) {
             rv = SECFailure;
             goto loser;
         }
         myClass = CKO_PUBLIC_KEY;
-        crv = sftkdb_convertObjects(certdb, &objectType, 1, &newKey);
+        crv = sftkdb_convertObjects(certdb, &objectType, 1, &newKey,
+                                    iterationCount);
         if (crv != CKR_OK) {
             rv = SECFailure;
             goto loser;
         }
     }
 
     plainText.data = (unsigned char *)SFTK_PW_CHECK_STRING;
     plainText.len = SFTK_PW_CHECK_LEN;
 
-    rv = sftkdb_EncryptAttribute(NULL, &newKey, &plainText, &result);
+    rv = sftkdb_EncryptAttribute(NULL, &newKey, iterationCount,
+                                 &plainText, &result);
     if (rv != SECSuccess) {
         goto loser;
     }
     value.data = result->data;
     value.len = result->len;
     crv = (*keydb->db->sdb_PutMetaData)(keydb->db, "password", &salt, &value);
     if (crv != CKR_OK) {
         rv = SECFailure;
@@ -1171,17 +1291,17 @@ sftkdb_ChangePassword(SFTKDBHandle *keyd
     crv = (*keydb->db->sdb_Commit)(keydb->db);
     if (crv != CKR_OK) {
         rv = SECFailure;
         goto loser;
     }
 
     keydb->newKey = NULL;
 
-    sftkdb_switchKeys(keydb, &newKey);
+    sftkdb_switchKeys(keydb, &newKey, iterationCount);
 
 loser:
     if (newKey.data) {
         PORT_ZFree(newKey.data, newKey.len);
     }
     if (result) {
         SECITEM_FreeItem(result, PR_TRUE);
     }
@@ -1196,14 +1316,14 @@ loser:
  * lose our cached password
  */
 SECStatus
 sftkdb_ClearPassword(SFTKDBHandle *keydb)
 {
     SECItem oldKey;
     oldKey.data = NULL;
     oldKey.len = 0;
-    sftkdb_switchKeys(keydb, &oldKey);
+    sftkdb_switchKeys(keydb, &oldKey, 1);
     if (oldKey.data) {
         PORT_ZFree(oldKey.data, oldKey.len);
     }
     return SECSuccess;
 }