Bug 554827 - Support additional pseudorandom functions for PKCS #5 PBKDF2, r=mt,wtc
authorMartin Thomson <martin.thomson@gmail.com>
Fri, 13 Nov 2015 14:56:44 -0800
changeset 11719 541dd3958e59eaa99c43604d49ec2afee7eae422
parent 11718 372db375adaa38a356048182c46a120ef5b74b42
child 11720 eff8e4bc3a7e0ab31ff84f034d292503dc124449
push id855
push usermartin.thomson@gmail.com
push dateFri, 13 Nov 2015 22:58:55 +0000
reviewersmt, wtc
bugs554827
Bug 554827 - Support additional pseudorandom functions for PKCS #5 PBKDF2, r=mt,wtc
external_tests/common/scoped_ptrs.h
external_tests/pk11_gtest/manifest.mn
external_tests/pk11_gtest/pk11_pbkdf2_unittest.cc
external_tests/pk11_gtest/pk11_rsapss_unittest.cc
lib/pk11wrap/pk11pbe.c
lib/softoken/lowpbe.c
lib/softoken/lowpbe.h
lib/softoken/pkcs11c.c
lib/softoken/sftkpwd.c
lib/util/pkcs11t.h
--- a/external_tests/common/scoped_ptrs.h
+++ b/external_tests/common/scoped_ptrs.h
@@ -2,35 +2,43 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef scoped_ptrs_h__
 #define scoped_ptrs_h__
 
+#include "keyhi.h"
+
 namespace nss_test {
 
-void ScopedDelete(PK11SlotInfo* slot) { PK11_FreeSlot(slot); }
-void ScopedDelete(SECItem* item) { SECITEM_FreeItem(item, true); }
-void ScopedDelete(SECKEYPublicKey* key) { SECKEY_DestroyPublicKey(key); }
-void ScopedDelete(SECKEYPrivateKey* key) { SECKEY_DestroyPrivateKey(key); }
-void ScopedDelete(CERTSubjectPublicKeyInfo* spki) {
-  SECKEY_DestroySubjectPublicKeyInfo(spki);
-}
+struct ScopedDelete {
+  void operator()(PK11SlotInfo* slot) { PK11_FreeSlot(slot); }
+  void operator()(SECItem* item) { SECITEM_FreeItem(item, true); }
+  void operator()(PK11SymKey* key) { PK11_FreeSymKey(key); }
+  void operator()(SECKEYPublicKey* key) { SECKEY_DestroyPublicKey(key); }
+  void operator()(SECKEYPrivateKey* key) { SECKEY_DestroyPrivateKey(key); }
+  void operator()(SECAlgorithmID* id) { SECOID_DestroyAlgorithmID(id, true); }
+  void operator()(CERTSubjectPublicKeyInfo* spki) {
+    SECKEY_DestroySubjectPublicKeyInfo(spki);
+  }
+};
 
 template<class T>
 struct ScopedMaybeDelete {
-  void operator()(T* ptr) { if (ptr) ScopedDelete(ptr); }
+  void operator()(T* ptr) { if (ptr) { ScopedDelete del; del(ptr); } }
 };
 
 template<class T>
 using ScopedUniquePtr = std::unique_ptr<T, ScopedMaybeDelete<T>>;
 
 using ScopedPK11SlotInfo = ScopedUniquePtr<PK11SlotInfo>;
 using ScopedSECItem = ScopedUniquePtr<SECItem>;
+using ScopedPK11SymKey = ScopedUniquePtr<PK11SymKey>;
 using ScopedSECKEYPublicKey = ScopedUniquePtr<SECKEYPublicKey>;
 using ScopedSECKEYPrivateKey = ScopedUniquePtr<SECKEYPrivateKey>;
+using ScopedSECAlgorithmID = ScopedUniquePtr<SECAlgorithmID>;
 using ScopedCERTSubjectPublicKeyInfo = ScopedUniquePtr<CERTSubjectPublicKeyInfo>;
 
 }  // namespace nss_test
 
 #endif
--- a/external_tests/pk11_gtest/manifest.mn
+++ b/external_tests/pk11_gtest/manifest.mn
@@ -2,16 +2,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 CORE_DEPTH = ../..
 DEPTH      = ../..
 MODULE = nss
 
 CPPSRCS = \
+      pk11_pbkdf2_unittest.cc \
       pk11_rsapss_unittest.cc \
       pk11_gtest.cc \
       $(NULL)
 
 INCLUDES += -I$(CORE_DEPTH)/external_tests/google_test/gtest/include \
             -I$(CORE_DEPTH)/external_tests/common
 
 REQUIRES = nspr nss libdbm gtest
new file mode 100644
--- /dev/null
+++ b/external_tests/pk11_gtest/pk11_pbkdf2_unittest.cc
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nss.h"
+#include "pk11pub.h"
+#include <memory>
+
+#include "gtest/gtest.h"
+#include "scoped_ptrs.h"
+
+namespace nss_test {
+
+static unsigned char* ToUcharPtr(std::string& str) {
+  return const_cast<unsigned char*>(
+    reinterpret_cast<const unsigned char*>(str.c_str()));
+}
+
+class Pkcs11Pbkdf2Test : public ::testing::Test {
+ public:
+  void Derive(std::vector<uint8_t>& derived, SECOidTag hash_alg)
+  {
+    // Shared between test vectors.
+    const unsigned int iterations = 4096;
+    std::string pass("passwordPASSWORDpassword");
+    std::string salt("saltSALTsaltSALTsaltSALTsaltSALTsalt");
+
+    // Derivation must succeed with the right values.
+    EXPECT_TRUE(DeriveBytes(pass, salt, derived, hash_alg, iterations));
+
+    // Derivation must fail when the password is bogus.
+    std::string bogusPass("PasswordPASSWORDpassword");
+    EXPECT_FALSE(DeriveBytes(bogusPass, salt, derived, hash_alg, iterations));
+
+    // Derivation must fail when the salt is bogus.
+    std::string bogusSalt("SaltSALTsaltSALTsaltSALTsaltSALTsalt");
+    EXPECT_FALSE(DeriveBytes(pass, bogusSalt, derived, hash_alg, iterations));
+
+    // Derivation must fail when using the wrong hash function.
+    SECOidTag next_hash_alg = static_cast<SECOidTag>(hash_alg + 1);
+    EXPECT_FALSE(DeriveBytes(pass, salt, derived, next_hash_alg, iterations));
+
+    // Derivation must fail when using the wrong number of iterations.
+    EXPECT_FALSE(DeriveBytes(pass, salt, derived, hash_alg, iterations + 1));
+  }
+
+ private:
+  bool DeriveBytes(std::string& pass, std::string& salt,
+                   std::vector<uint8_t>& derived, SECOidTag hash_alg,
+                   unsigned int iterations)
+  {
+    SECItem passItem = { siBuffer, ToUcharPtr(pass),
+                         static_cast<unsigned int>(pass.length()) };
+    SECItem saltItem = { siBuffer, ToUcharPtr(salt),
+                         static_cast<unsigned int>(salt.length()) };
+
+    // Set up PBKDF2 params.
+    ScopedSECAlgorithmID alg_id(
+      PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, hash_alg, hash_alg,
+                                  derived.size(), iterations, &saltItem));
+
+    // Derive.
+    ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+    ScopedPK11SymKey symKey(
+      PK11_PBEKeyGen(slot.get(), alg_id.get(), &passItem, false, nullptr));
+
+    SECStatus rv = PK11_ExtractKeyValue(symKey.get());
+    EXPECT_EQ(rv, SECSuccess);
+
+    SECItem* keyData = PK11_GetKeyData(symKey.get());
+    return !memcmp(&derived[0], keyData->data, keyData->len);
+  }
+};
+
+// RFC 6070 <http://tools.ietf.org/html/rfc6070>
+TEST_F(Pkcs11Pbkdf2Test, DeriveKnown1) {
+  std::vector<uint8_t> derived = {
+    0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b, 0x80, 0xc8, 0xd8, 0x36,
+    0x62, 0xc0, 0xe4, 0x4a, 0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70, 0x38
+  };
+
+  Derive(derived, SEC_OID_HMAC_SHA1);
+}
+
+// https://stackoverflow.com/questions/5130513/pbkdf2-hmac-sha2-test-vectors
+TEST_F(Pkcs11Pbkdf2Test, DeriveKnown2) {
+  std::vector<uint8_t> derived = {
+    0x34, 0x8c, 0x89, 0xdb, 0xcb, 0xd3, 0x2b, 0x2f, 0x32, 0xd8, 0x14, 0xb8,
+    0x11, 0x6e, 0x84, 0xcf, 0x2b, 0x17, 0x34, 0x7e, 0xbc, 0x18, 0x00, 0x18,
+    0x1c, 0x4e, 0x2a, 0x1f, 0xb8, 0xdd, 0x53, 0xe1, 0xc6, 0x35, 0x51, 0x8c,
+    0x7d, 0xac, 0x47, 0xe9
+  };
+
+  Derive(derived, SEC_OID_HMAC_SHA256);
+}
+
+}  // namespace nss_test
+
--- a/external_tests/pk11_gtest/pk11_rsapss_unittest.cc
+++ b/external_tests/pk11_gtest/pk11_rsapss_unittest.cc
@@ -1,16 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nss.h"
-#include "keyhi.h"
 #include "pk11pub.h"
 #include "sechash.h"
 #include <memory>
 
 #include "gtest/gtest.h"
 #include "scoped_ptrs.h"
 
 namespace nss_test {
--- a/lib/pk11wrap/pk11pbe.c
+++ b/lib/pk11wrap/pk11pbe.c
@@ -631,17 +631,17 @@ sec_pkcs5CreateAlgorithmID(SECOidTag alg
 		    goto loser;
 		}
 	        keyLength = PK11_GetMaxKeyLength(cryptoMech);
 	    }
 	    if (keyLength == 0) {
 		goto loser;
 	    }
 	}
-	/* currently only SEC_OID_HMAC_SHA1 is defined */
+	/* currently SEC_OID_HMAC_SHA1 is the default */
 	if (prfAlg == SEC_OID_UNKNOWN) {
 	    prfAlg = SEC_OID_HMAC_SHA1;
 	}
 
 	/* build the PKCS5v2 cipher algorithm id */
 	cipherParams = pk11_GenerateNewParamWithKeyLen(	
 			PK11_AlgtagToMechanism(cipherAlgorithm), keyLength);
 	if (!cipherParams) {
@@ -800,23 +800,36 @@ pbe_PK11AlgidToParam(SECAlgorithmID *alg
 	paramLen = sizeof(CK_PKCS5_PBKD2_PARAMS);
 
 	/* set the prf */
 	prfAlgTag = SEC_OID_HMAC_SHA1;
  	if (p5_param.pPrfAlgId &&
  	    p5_param.pPrfAlgId->algorithm.data != 0) {
  	    prfAlgTag = SECOID_GetAlgorithmTag(p5_param.pPrfAlgId);
 	}
-	if (prfAlgTag == SEC_OID_HMAC_SHA1) {
-	    pbeV2_params->prf = CKP_PKCS5_PBKD2_HMAC_SHA1;
-	} else {
-	    /* only SHA1_HMAC is currently supported by PKCS #11 */
-	    PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
-	    goto loser;
-	}
+        switch (prfAlgTag) {
+        case SEC_OID_HMAC_SHA1:
+            pbeV2_params->prf = CKP_PKCS5_PBKD2_HMAC_SHA1;
+            break;
+        case SEC_OID_HMAC_SHA224:
+            pbeV2_params->prf = CKP_PKCS5_PBKD2_HMAC_SHA224;
+            break;
+        case SEC_OID_HMAC_SHA256:
+            pbeV2_params->prf = CKP_PKCS5_PBKD2_HMAC_SHA256;
+            break;
+        case SEC_OID_HMAC_SHA384:
+            pbeV2_params->prf = CKP_PKCS5_PBKD2_HMAC_SHA384;
+            break;
+        case SEC_OID_HMAC_SHA512:
+            pbeV2_params->prf = CKP_PKCS5_PBKD2_HMAC_SHA512;
+            break;
+        default:
+            PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+            goto loser;
+        }
 	
 	/* probably should fetch these from the prfAlgid */
 	pbeV2_params->pPrfData = NULL;
 	pbeV2_params->ulPrfDataLen = 0;
 	pbeV2_params->saltSource = CKZ_SALT_SPECIFIED;
 	pSalt = ((CK_CHAR_PTR) pbeV2_params)+sizeof(CK_PKCS5_PBKD2_PARAMS);
         PORT_Memcpy(pSalt, salt->data, salt->len);
 	pbeV2_params->pSaltSourceData = pSalt;
--- a/lib/softoken/lowpbe.c
+++ b/lib/softoken/lowpbe.c
@@ -48,19 +48,17 @@ static const SEC_ASN1Template NSSPKCS5PK
 /* PKCS5 v2 */
 
 struct nsspkcs5V2PBEParameterStr {
     SECAlgorithmID keyParams;  /* parameters of the key generation */
     SECAlgorithmID algParams;  /* parameters for the encryption or mac op */
 };
 
 typedef struct nsspkcs5V2PBEParameterStr nsspkcs5V2PBEParameter;
-#define PBKDF2
 
-#ifdef PBKDF2
 static const SEC_ASN1Template NSSPKCS5V2PBES2ParameterTemplate[] =
 {   
     { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(nsspkcs5V2PBEParameter) },
     { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
         offsetof(nsspkcs5V2PBEParameter, keyParams), 
         SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
     { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
         offsetof(nsspkcs5V2PBEParameter, algParams),
@@ -76,17 +74,16 @@ static const SEC_ASN1Template NSSPKCS5V2
     { SEC_ASN1_OCTET_STRING, offsetof(NSSPKCS5PBEParameter, salt) },
     { SEC_ASN1_INTEGER, offsetof(NSSPKCS5PBEParameter, iteration) },
     { SEC_ASN1_INTEGER, offsetof(NSSPKCS5PBEParameter, keyLength) },
     { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
         offsetof(NSSPKCS5PBEParameter, prfAlg),
         SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
     { 0 }
 };
-#endif
 
 SECStatus
 nsspkcs5_HashBuf(const SECHashObject *hashObj, unsigned char *dest,
 					 unsigned char *src, int len)
 {
     void *ctx;
     unsigned int retLen;
 
@@ -296,18 +293,16 @@ nsspkcs5_PBKDF1Extended(const SECHashObj
     } 
 
     newHash = nsspkcs5_PFXPBE(hashObj, pbe_param, hash, bytes_needed);
     if (hash != newHash)
 	SECITEM_FreeItem(hash, PR_TRUE);
     return newHash;
 }
 
-#ifdef PBKDF2
-
 /*
  * PBDKDF2 is PKCS #5 v2.0 it's currently not used by NSS
  */
 static void
 do_xor(unsigned char *dest, unsigned char *src, int len)
 {
    /* use byt xor, not all platforms are happy about inaligned 
     * integer fetches */
@@ -408,17 +403,16 @@ loser:
 	SECITEM_FreeItem(result,PR_TRUE);
 	result = NULL;
     } else {
 	result->len = dkLen;
     }
 
     return result;
 }
-#endif
 
 #define HMAC_BUFFER 64
 #define NSSPBE_ROUNDUP(x,y) ((((x)+((y)-1))/(y))*(y))
 #define NSSPBE_MIN(x,y) ((x) < (y) ? (x) : (y))
 /*
  * This is the extended PBE function defined by the final PKCS #12 spec.
  */
 static SECItem *
@@ -595,24 +589,22 @@ nsspkcs5_ComputeKeyAndIV(NSSPKCS5PBEPara
 	    goto loser;
 	}
 	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;
-#ifdef PBKDF2
     case NSSPKCS5_PBKDF2:
 	hash = nsspkcs5_PBKDF2(hashObj,pbe_param,pwitem);
 	if (getIV) {
 	    PORT_Memcpy(iv->data, pbe_param->ivData, iv->len);
 	}
     	break;
-#endif
     case NSSPKCS5_PKCS12_V2:
 	if (getIV) {
 	    hash = nsspkcs5_PKCS12PBE(hashObj,pbe_param,pwitem,
 						pbeBitGenCipherIV,iv->len);
 	    if (hash == NULL) {
 		goto loser;
 	    }
 	    PORT_Memcpy(iv->data,hash->data,iv->len);
@@ -646,23 +638,24 @@ loser:
 	iv->data = NULL;
     }
 
     SECITEM_ZfreeItem(key, PR_TRUE);
     return NULL;
 }
 
 static SECStatus
-nsspkcs5_FillInParam(SECOidTag algorithm, NSSPKCS5PBEParameter *pbe_param)
+nsspkcs5_FillInParam(SECOidTag algorithm, HASH_HashType hashType,
+                     NSSPKCS5PBEParameter *pbe_param)
 {
     PRBool skipType = PR_FALSE;
 
     pbe_param->keyLen = 5;
     pbe_param->ivLen = 8;
-    pbe_param->hashType = HASH_AlgSHA1;
+    pbe_param->hashType = hashType;
     pbe_param->pbeType = NSSPKCS5_PBKDF1;
     pbe_param->encAlg = SEC_OID_RC2_CBC;
     pbe_param->is2KeyDES = PR_FALSE;
     switch(algorithm) {
     /* DES3 Algorithms */
     case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC:
 	pbe_param->is2KeyDES = PR_TRUE;
 	/* fall through */
@@ -712,39 +705,38 @@ finish_des:
     	    pbe_param->pbeType = NSSPKCS5_PKCS12_V2;
 	}
 	/* fall through */
     case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC4:
         pbe_param->ivLen = 0;
         pbe_param->encAlg =  SEC_OID_RC4;
         break;
 
-#ifdef PBKDF2
     case SEC_OID_PKCS5_PBKDF2:
     case SEC_OID_PKCS5_PBES2:
     case SEC_OID_PKCS5_PBMAC1:
 	/* everything else will be filled in by the template */
         pbe_param->ivLen = 0;
 	pbe_param->pbeType = NSSPKCS5_PBKDF2;
         pbe_param->encAlg =  SEC_OID_PKCS5_PBKDF2;
 	pbe_param->keyLen = 0; /* needs to be set by caller after return */
 	break;
-#endif
 
     default:
         return SECFailure;
     }
 
     return SECSuccess;
 }
 
 /* decode the algid and generate a PKCS 5 parameter from it
  */
 NSSPKCS5PBEParameter *
-nsspkcs5_NewParam(SECOidTag alg, SECItem *salt, int iterator)
+nsspkcs5_NewParam(SECOidTag alg, HASH_HashType hashType, SECItem *salt,
+                  int iterator)
 {
     PLArenaPool *arena = NULL;
     NSSPKCS5PBEParameter *pbe_param = NULL;
     SECStatus rv = SECFailure;
 
     arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
     if (arena == NULL)
 	return NULL;
@@ -754,17 +746,17 @@ nsspkcs5_NewParam(SECOidTag alg, SECItem
 	sizeof(NSSPKCS5PBEParameter));
 
     if (pbe_param == NULL) {
 	goto loser;
     }
 
     pbe_param->poolp = arena;
 
-    rv = nsspkcs5_FillInParam(alg, pbe_param);
+    rv = nsspkcs5_FillInParam(alg, hashType, pbe_param);
     if (rv != SECSuccess) {
 	goto loser;
     }
 
     pbe_param->iter = iterator;
     if (salt) {
 	rv = SECITEM_CopyItem(arena,&pbe_param->salt,salt);
     }
@@ -818,33 +810,32 @@ nsspkcs5_AlgidToParam(SECAlgorithmID *al
 	return NULL;
     }
 
     algorithm = SECOID_GetAlgorithmTag(algid);
     if (algorithm == SEC_OID_UNKNOWN) {
 	goto loser;
     }
 
-    pbe_param = nsspkcs5_NewParam(algorithm, NULL, 1);
+    pbe_param = nsspkcs5_NewParam(algorithm, HASH_AlgSHA1, NULL, 1);
     if (pbe_param == NULL) {
 	goto loser;
     }
 
     /* decode parameter */
     rv = SECFailure;
     switch (pbe_param->pbeType) {
     case NSSPKCS5_PBKDF1:
 	rv = SEC_ASN1DecodeItem(pbe_param->poolp, pbe_param, 
 	    NSSPKCS5PBEParameterTemplate, &algid->parameters);
 	break;
     case NSSPKCS5_PKCS12_V2:
 	rv = SEC_ASN1DecodeItem(pbe_param->poolp, pbe_param, 
 		NSSPKCS5PKCS12V2PBEParameterTemplate, &algid->parameters);
 	break;
-#ifdef PBKDF2
     case NSSPKCS5_PBKDF2:
 	PORT_Memset(&pbev2_param,0, sizeof(pbev2_param));
 	/* just the PBE */
 	if (algorithm == SEC_OID_PKCS5_PBKDF2) {
 	    rv = SEC_ASN1DecodeItem(pbe_param->poolp, pbe_param,
 		NSSPKCS5V2PBEParameterTemplate, &algid->parameters);
 	} else {
 	    /* PBE data an others */
@@ -869,17 +860,16 @@ nsspkcs5_AlgidToParam(SECAlgorithmID *al
 	}
 	pbe_param->hashType = 
 	    HASH_FromHMACOid(SECOID_GetAlgorithmTag(&pbe_param->prfAlg));
 	if (pbe_param->hashType == HASH_AlgNULL) {
 	    PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
 	    rv = SECFailure;
 	}
 	break;
-#endif
     }
 
 loser:
     if (rv == SECSuccess) {
     	pbe_param->iter = DER_GetInteger(&pbe_param->iteration);
     } else {
 	nsspkcs5_DestroyPBEParameter(pbe_param);
 	pbe_param = NULL;
@@ -1311,17 +1301,16 @@ nsspkcs5_CreateAlgorithmID(PLArenaPool *
     case NSSPKCS5_PBKDF1:
 	dummy = SEC_ASN1EncodeItem(arena, &der_param, pbe_param,
 					NSSPKCS5PBEParameterTemplate);
 	break;
     case NSSPKCS5_PKCS12_V2:
 	dummy = SEC_ASN1EncodeItem(arena, &der_param, pbe_param,
 	    				NSSPKCS5PKCS12V2PBEParameterTemplate);
 	break;
-#ifdef PBKDF2
     case NSSPKCS5_PBKDF2:
         if (pbe_param->keyLength.data == NULL) {
 	    dummy = SEC_ASN1EncodeInteger(pbe_param->poolp,
 				&pbe_param->keyLength, pbe_param->keyLen);
 	    if (dummy == NULL) {
 		goto loser;
 	    }
 	}
@@ -1342,17 +1331,16 @@ nsspkcs5_CreateAlgorithmID(PLArenaPool *
 	rv = SECOID_SetAlgorithmID(arena, &pkcs5v2_param.algParams, 
 		pbe_param->encAlg, pbe_param->ivLen ? &der_param : NULL);
 	if (rv != SECSuccess) {
 	    break;
 	}
 	dummy = SEC_ASN1EncodeItem(arena,  &der_param, &pkcs5v2_param,
 	    				NSSPKCS5V2PBES2ParameterTemplate);
 	break;
-#endif
     default:
 	break;
     }
 
     if (dummy == NULL) {
 	goto loser;
     }
 	
--- a/lib/softoken/lowpbe.h
+++ b/lib/softoken/lowpbe.h
@@ -72,17 +72,18 @@ NSSPKCS5PBEParameter *
 nsspkcs5_AlgidToParam(SECAlgorithmID *algid);
 
 /*
  * Convert an Algorithm ID to a PBE Param.
  * NOTE: this does not suppport PKCS 5 v2 because it's only used for the
  * keyDB which only support PKCS 5 v1, PFX, and PKCS 12.
  */
 NSSPKCS5PBEParameter *
-nsspkcs5_NewParam(SECOidTag alg, SECItem *salt, int iterator);
+nsspkcs5_NewParam(SECOidTag alg, HASH_HashType hashType, SECItem *salt,
+                  int iterator);
 
 
 /* Encrypt/Decrypt data using password based encryption.  
  *  algid is the PBE algorithm identifier,
  *  pwitem is the password,
  *  src is the source for encryption/decryption,
  *  encrypt is PR_TRUE for encryption, PR_FALSE for decryption.
  * The key and iv are generated based upon PKCS #5 then the src
--- a/lib/softoken/pkcs11c.c
+++ b/lib/softoken/pkcs11c.c
@@ -3730,42 +3730,65 @@ nsc_SetupHMACKeyGen(CK_MECHANISM_PTR pMe
 static CK_RV
 nsc_SetupPBEKeyGen(CK_MECHANISM_PTR pMechanism, NSSPKCS5PBEParameter  **pbe,
 				CK_KEY_TYPE *key_type, CK_ULONG *key_length)
 {
     CK_RV crv = CKR_OK;
     SECOidData *oid;
     CK_PBE_PARAMS *pbe_params = NULL;
     NSSPKCS5PBEParameter *params = NULL;
+    HASH_HashType hashType = HASH_AlgSHA1;
     CK_PKCS5_PBKD2_PARAMS *pbkd2_params = NULL;
     SECItem salt;
     CK_ULONG iteration = 0;
 
     *pbe = NULL;
 
     oid = SECOID_FindOIDByMechanism(pMechanism->mechanism);
     if (oid == NULL) {
 	return CKR_MECHANISM_INVALID;
     }
 
     if (pMechanism->mechanism == CKM_PKCS5_PBKD2) {
 	pbkd2_params = (CK_PKCS5_PBKD2_PARAMS *)pMechanism->pParameter;
+	if (pbkd2_params == NULL) {
+	    return CKR_MECHANISM_PARAM_INVALID;
+	}
+	switch (pbkd2_params->prf) {
+	case CKP_PKCS5_PBKD2_HMAC_SHA1:
+	    hashType = HASH_AlgSHA1;
+	    break;
+	case CKP_PKCS5_PBKD2_HMAC_SHA224:
+	    hashType = HASH_AlgSHA224;
+	    break;
+	case CKP_PKCS5_PBKD2_HMAC_SHA256:
+	    hashType = HASH_AlgSHA256;
+	    break;
+	case CKP_PKCS5_PBKD2_HMAC_SHA384:
+	    hashType = HASH_AlgSHA384;
+	    break;
+	case CKP_PKCS5_PBKD2_HMAC_SHA512:
+	    hashType = HASH_AlgSHA512;
+	    break;
+	default:
+	    return CKR_MECHANISM_PARAM_INVALID;
+	}
 	if (pbkd2_params->saltSource != CKZ_SALT_SPECIFIED) {
 	    return CKR_MECHANISM_PARAM_INVALID;
 	}
 	salt.data = (unsigned char *)pbkd2_params->pSaltSourceData;
 	salt.len = (unsigned int)pbkd2_params->ulSaltSourceDataLen;
 	iteration = pbkd2_params->iterations;
     } else {
 	pbe_params = (CK_PBE_PARAMS *)pMechanism->pParameter;
 	salt.data = (unsigned char *)pbe_params->pSalt;
 	salt.len = (unsigned int)pbe_params->ulSaltLen;
 	iteration = pbe_params->ulIteration;
     }
-    params=nsspkcs5_NewParam(oid->offset, &salt, iteration);
+    params=nsspkcs5_NewParam(oid->offset, hashType, &salt, iteration);
     if (params == NULL) {
 	return CKR_MECHANISM_INVALID;
     }
 
     switch (params->encAlg) {
     case SEC_OID_DES_CBC:
 	*key_type = CKK_DES;
 	*key_length = params->keyLen;
@@ -3778,24 +3801,16 @@ nsc_SetupPBEKeyGen(CK_MECHANISM_PTR pMec
 	*key_type = CKK_RC2;
 	*key_length = params->keyLen;
 	break;
     case SEC_OID_RC4:
 	*key_type = CKK_RC4;
 	*key_length = params->keyLen;
 	break;
     case SEC_OID_PKCS5_PBKDF2:
-	/* sigh, PKCS #11 currently only defines SHA1 for the KDF hash type. 
-	 * we do the check here because this where we would handle multiple
-	 * hash types in the future */
-	if (pbkd2_params == NULL || 
-		pbkd2_params->prf != CKP_PKCS5_PBKD2_HMAC_SHA1) {
-	    crv = CKR_MECHANISM_PARAM_INVALID;
-	    break;
-	}
 	/* key type must already be set */
 	if (*key_type == CKK_INVALID_KEY_TYPE) {
 	    crv = CKR_TEMPLATE_INCOMPLETE;
 	    break;
 	}
 	/* PBKDF2 needs to calculate the key length from the other parameters
 	 */
 	if (*key_length == 0) {
--- a/lib/softoken/sftkpwd.c
+++ b/lib/softoken/sftkpwd.c
@@ -272,17 +272,18 @@ sftkdb_EncryptAttribute(PLArenaPool *are
     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, &cipherValue.salt, 1);
+    param = nsspkcs5_NewParam(cipherValue.alg, HASH_AlgSHA1, &cipherValue.salt,
+                              1);
     if (param == NULL) {
 	rv = SECFailure;
 	goto loser;
     }
     cipher = nsspkcs5_CipherData(param, passKey, plainText, PR_TRUE, NULL);
     if (cipher == NULL) {
 	rv = SECFailure;
 	goto loser;
@@ -444,17 +445,17 @@ 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, &signValue.salt, 1);
+    param = nsspkcs5_NewParam(signValue.alg, HASH_AlgSHA1, &signValue.salt, 1);
     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;
--- a/lib/util/pkcs11t.h
+++ b/lib/util/pkcs11t.h
@@ -1782,19 +1782,25 @@ typedef CK_EXTRACT_PARAMS CK_PTR CK_EXTR
 /* CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE is new for v2.10.
  * CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE is used to
  * indicate the Pseudo-Random Function (PRF) used to generate
  * key bits using PKCS #5 PBKDF2. */
 typedef CK_ULONG CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE;
 
 typedef CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE CK_PTR CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE_PTR;
 
-/* The following PRFs are defined in PKCS #5 v2.0. */
-#define CKP_PKCS5_PBKD2_HMAC_SHA1 0x00000001
-
+/* The following PRFs are defined in PKCS #5 v2.1. */
+#define CKP_PKCS5_PBKD2_HMAC_SHA1       0x00000001
+#define CKP_PKCS5_PBKD2_HMAC_GOSTR3411  0x00000002
+#define CKP_PKCS5_PBKD2_HMAC_SHA224     0x00000003
+#define CKP_PKCS5_PBKD2_HMAC_SHA256     0x00000004
+#define CKP_PKCS5_PBKD2_HMAC_SHA384     0x00000005
+#define CKP_PKCS5_PBKD2_HMAC_SHA512     0x00000006
+#define CKP_PKCS5_PBKD2_HMAC_SHA512_224 0x00000007
+#define CKP_PKCS5_PBKD2_HMAC_SHA512_256 0x00000008
 
 /* CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE is new for v2.10.
  * CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE is used to indicate the
  * source of the salt value when deriving a key using PKCS #5
  * PBKDF2. */
 typedef CK_ULONG CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE;
 
 typedef CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE CK_PTR CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE_PTR;