# HG changeset patch
# User Robert Relyea <rrelyea@redhat.com>
# Date 1598309191 25200
# Node ID 5dca54fe61c2916e540129590a40772d5be89a1d
# Parent  fd76978efe6ac269a0d30e5ae2499b04366a73ac
Bug 1660304 New FIPS IG requires self-tests for approved kdfs. r=ueno comments=kjacobs

FIPS guidance now requires self-tests for our kdfs. It also requires self-tests for cmac which we didn't have in the cmac patch.

Currently only one test per kdf is necessary. Specifially for SP-800-108, only
one of the three flavors are needed (counter, feedback, or pipeline). This
patch includes more complete testing but it has been turned off the currently
extraneous tests under the assumption that NIST guidance may require them
in the future. HKDF is currently not included in FIPS, but is on track to be
included, so hkdf have been included in this patch.

Because the test vectors are const strings, the patch pushes some const
definitions that were missing in existing private interfaces.

There are three flavors of self-tests:
Function implemented in freebl are added to the freebl/fipsfreebl.c
Functions implemented in pkcs11c.c have selftests completely implemented in
softoken/fipstest.c
Functions implemented in their own .c file have their selftest function
implemented in that .c file and called by fipstests.c
These are consistant with the previous choices for selftests.

Some private interfaces that took in keys from pkcs #11 structures or outputted keys to pkcs #11 structures were modified to optionally take keys in by bytes
and output keys as bytes so the self-tests can work in just bytes.

Differential Revision: https://phabricator.services.mozilla.com/D87812

Rebased for Suse by hpj@suse.com

diff --git a/lib/freebl/fipsfreebl.c b/lib/freebl/fipsfreebl.c
--- a/lib/freebl/fipsfreebl.c
+++ b/lib/freebl/fipsfreebl.c
@@ -12,16 +12,17 @@
 #endif
 
 #include "blapi.h"
 #include "seccomon.h" /* Required for RSA and DSA. */
 #include "secerr.h"
 #include "prtypes.h"
 #include "secitem.h"
 #include "pkcs11t.h"
+#include "cmac.h"
 
 #include "ec.h" /* Required for EC */
 
 #include "fips-selftest.inc"
 
 #include "fips.h"
 
 #define RNG_DEV_FIPS0   "/dev/urandom"
@@ -101,16 +102,17 @@ BOOL WINAPI DllMain(
 /* FIPS preprocessor directives for DES3-CBC and DES3-ECB.      */
 #define FIPS_DES3_ENCRYPT_LENGTH 8 /*  64-bits */
 #define FIPS_DES3_DECRYPT_LENGTH 8 /*  64-bits */
 
 /* FIPS preprocessor directives for AES-ECB and AES-CBC.        */
 #define FIPS_AES_BLOCK_SIZE 16     /* 128-bits */
 #define FIPS_AES_ENCRYPT_LENGTH 16 /* 128-bits */
 #define FIPS_AES_DECRYPT_LENGTH 16 /* 128-bits */
+#define FIPS_AES_CMAC_LENGTH 16    /* 128-bits */
 #define FIPS_AES_128_KEY_SIZE 16   /* 128-bits */
 #define FIPS_AES_192_KEY_SIZE 24   /* 192-bits */
 #define FIPS_AES_256_KEY_SIZE 32   /* 256-bits */
 
 /* FIPS preprocessor directives for AES Keywrap */
 #define FIPS_AES_KEYWRAP_KNOWN_CIPHERTEXT_SIZE 24  /* 192-bits */
 
 /* FIPS preprocessor directives for message digests             */
@@ -318,16 +320,21 @@ freebl_fips_AES_PowerUpSelfTest(int aes_
 
     static const PRUint8 aes_gcm128_known_ciphertext[] = {
         0x63, 0xf4, 0x95, 0x28, 0xe6, 0x78, 0xee, 0x6e,
         0x4f, 0xe0, 0xfc, 0x8d, 0xd7, 0xa2, 0xb1, 0xff,
         0x0c, 0x97, 0x1b, 0x0a, 0xdd, 0x97, 0x75, 0xed,
         0x8b, 0xde, 0xbf, 0x16, 0x5e, 0x57, 0x6b, 0x4f
     };
 
+    static const PRUint8 aes_cmac128_known_ciphertext[] = {
+        0x54, 0x11, 0xe2, 0x57, 0xbd, 0x2a, 0xdf, 0x9d,
+        0x1a, 0x89, 0x72, 0x80, 0x84, 0x4c, 0x7e, 0x93
+    };
+
     /* AES Known Ciphertext (192-bit key). */
     static const PRUint8 aes_ecb192_known_ciphertext[] = {
         0xa0, 0x18, 0x62, 0xed, 0x88, 0x19, 0xcb, 0x62,
         0x88, 0x1d, 0x4d, 0xfe, 0x84, 0x02, 0x89, 0x0e
     };
 
     static const PRUint8 aes_cbc192_known_ciphertext[] = {
         0x83, 0xf7, 0xa4, 0x76, 0xd1, 0x6f, 0x07, 0xbe,
@@ -336,16 +343,21 @@ freebl_fips_AES_PowerUpSelfTest(int aes_
 
     static const PRUint8 aes_gcm192_known_ciphertext[] = {
         0xc1, 0x0b, 0x92, 0x1d, 0x68, 0x21, 0xf4, 0x25,
         0x41, 0x61, 0x20, 0x2d, 0x59, 0x7f, 0x53, 0xde,
         0x93, 0x39, 0xab, 0x09, 0x76, 0x41, 0x57, 0x2b,
         0x90, 0x2e, 0x44, 0xbb, 0x52, 0x03, 0xe9, 0x07
     };
 
+    static const PRUint8 aes_cmac192_known_ciphertext[] = {
+        0x0e, 0x07, 0x99, 0x1e, 0xf6, 0xee, 0xfa, 0x2c,
+        0x1b, 0xfc, 0xce, 0x94, 0x92, 0x2d, 0xf1, 0xab
+    };
+
     /* AES Known Ciphertext (256-bit key). */
     static const PRUint8 aes_ecb256_known_ciphertext[] = {
         0xdb, 0xa6, 0x52, 0x01, 0x8a, 0x70, 0xae, 0x66,
         0x3a, 0x99, 0xd8, 0x95, 0x7f, 0xfb, 0x01, 0x67
     };
 
     static const PRUint8 aes_cbc256_known_ciphertext[] = {
         0x37, 0xea, 0x07, 0x06, 0x31, 0x1c, 0x59, 0x27,
@@ -354,16 +366,21 @@ freebl_fips_AES_PowerUpSelfTest(int aes_
 
     static const PRUint8 aes_gcm256_known_ciphertext[] = {
         0x5d, 0x9e, 0xd2, 0xa2, 0x74, 0x9c, 0xd9, 0x1c,
         0xd1, 0xc9, 0xee, 0x5d, 0xb6, 0xf2, 0xc9, 0xb6,
         0x79, 0x27, 0x53, 0x02, 0xa3, 0xdc, 0x22, 0xce,
         0xf4, 0xb0, 0xc1, 0x8c, 0x86, 0x51, 0xf5, 0xa1
     };
 
+    static const PRUint8 aes_cmac256_known_ciphertext[] = {
+        0xc1, 0x26, 0x69, 0x32, 0x51, 0x13, 0x65, 0xac,
+        0x71, 0x23, 0xe4, 0xe7, 0xb9, 0x0c, 0x88, 0x9f
+    };
+
     /* AES Keywrap Known Ciphertexts. */
     static const PRUint8 aes_kw128_known_ciphertext[] = {
         0xd7, 0xec, 0x33, 0x3a, 0x35, 0x50, 0x91, 0x4d,
         0x04, 0x69, 0x1f, 0xbc, 0x9b, 0x3a, 0x51, 0x9d,
         0xf3, 0x45, 0x01, 0xec, 0xaa, 0x43, 0x33, 0x42
     };
 
     static const PRUint8 aes_kw192_known_ciphertext[] = {
@@ -382,23 +399,27 @@ freebl_fips_AES_PowerUpSelfTest(int aes_
         (aes_key_size == FIPS_AES_128_KEY_SIZE) ? aes_ecb128_known_ciphertext : (aes_key_size == FIPS_AES_192_KEY_SIZE) ? aes_ecb192_known_ciphertext : aes_ecb256_known_ciphertext;
 
     const PRUint8 *aes_cbc_known_ciphertext =
         (aes_key_size == FIPS_AES_128_KEY_SIZE) ? aes_cbc128_known_ciphertext : (aes_key_size == FIPS_AES_192_KEY_SIZE) ? aes_cbc192_known_ciphertext : aes_cbc256_known_ciphertext;
 
     const PRUint8 *aes_gcm_known_ciphertext =
         (aes_key_size == FIPS_AES_128_KEY_SIZE) ? aes_gcm128_known_ciphertext : (aes_key_size == FIPS_AES_192_KEY_SIZE) ? aes_gcm192_known_ciphertext : aes_gcm256_known_ciphertext;
 
+    const PRUint8 *aes_cmac_known_ciphertext =
+        (aes_key_size == FIPS_AES_128_KEY_SIZE) ? aes_cmac128_known_ciphertext : (aes_key_size == FIPS_AES_192_KEY_SIZE) ? aes_cmac192_known_ciphertext : aes_cmac256_known_ciphertext;
+
     const PRUint8 *aes_keywrap_known_ciphertext =
         (aes_key_size == FIPS_AES_128_KEY_SIZE) ? aes_kw128_known_ciphertext : (aes_key_size == FIPS_AES_192_KEY_SIZE) ? aes_kw192_known_ciphertext : aes_kw256_known_ciphertext;
 
     /* AES variables. */
     PRUint8 aes_computed_ciphertext[FIPS_AES_ENCRYPT_LENGTH * 2];
     PRUint8 aes_computed_plaintext[FIPS_AES_DECRYPT_LENGTH * 2];
     AESContext *aes_context;
+    CMACContext *cmac_context;
     AESKeyWrapContext *aes_keywrap_context;
     unsigned int aes_bytes_encrypted;
     unsigned int aes_bytes_decrypted;
     CK_NSS_GCM_PARAMS gcmParams;
     SECStatus aes_status;
 
     /*check if aes_key_size is 128, 192, or 256 bits */
     if ((aes_key_size != FIPS_AES_128_KEY_SIZE) &&
@@ -578,16 +599,54 @@ freebl_fips_AES_PowerUpSelfTest(int aes_
     if ((aes_status != SECSuccess) ||
         (aes_bytes_decrypted != FIPS_AES_DECRYPT_LENGTH) ||
         (PORT_Memcmp(aes_computed_plaintext, aes_known_plaintext,
                      FIPS_AES_DECRYPT_LENGTH) != 0)) {
         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
         return (SECFailure);
     }
 
+    /******************************************************/
+    /* AES-CMAC Known Answer Encryption Test.             */
+    /******************************************************/
+    cmac_context = CMAC_Create(CMAC_AES, aes_known_key, aes_key_size);
+
+    if (cmac_context == NULL) {
+        PORT_SetError(SEC_ERROR_NO_MEMORY);
+        return (SECFailure);
+    }
+
+    aes_status = CMAC_Begin(cmac_context);
+    if (aes_status != SECSuccess) {
+        CMAC_Destroy(cmac_context, PR_TRUE);
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return (SECFailure);
+    }
+
+    aes_status = CMAC_Update(cmac_context, aes_known_plaintext,
+                             FIPS_AES_DECRYPT_LENGTH);
+    if (aes_status != SECSuccess) {
+        CMAC_Destroy(cmac_context, PR_TRUE);
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return (SECFailure);
+    }
+
+    aes_status = CMAC_Finish(cmac_context, aes_computed_ciphertext,
+                             &aes_bytes_encrypted, FIPS_AES_CMAC_LENGTH);
+
+    CMAC_Destroy(cmac_context, PR_TRUE);
+
+    if ((aes_status != SECSuccess) ||
+        (aes_bytes_encrypted != FIPS_AES_CMAC_LENGTH) ||
+        (PORT_Memcmp(aes_computed_ciphertext, aes_cmac_known_ciphertext,
+                     FIPS_AES_CMAC_LENGTH) != 0)) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return (SECFailure);
+    }
+
     /********************************/
     /* AES Keywrap En/Decrypt Test. */
     /********************************/
 
     /* Create encryption context */
     aes_keywrap_context = AESKeyWrap_CreateContext(aes_known_key, aes_key_wrap_iv, PR_TRUE,
                                                    aes_key_size);
     if (aes_keywrap_context == NULL) {
@@ -820,16 +879,180 @@ freebl_fips_HMAC_PowerUpSelfTest(void)
                      SHA512_LENGTH) != 0)) {
         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
         return (SECFailure);
     }
 
     return (SECSuccess);
 }
 
+SECStatus
+freebl_fips_TLS_PowerUpSelfTest(void)
+{
+    static const PRUint8 TLS_known_secret_key[] = {
+        "Firefox and ThunderBird are awesome!"
+    };
+
+    static const PRUint8 TLS_known_secret_key_length = sizeof TLS_known_secret_key;
+
+    /* known tls prf with sha1/md5 */
+    static const PRUint8 known_TLS_PRF[] = {
+        0x87, 0x4c, 0xc0, 0xc5, 0x15, 0x14, 0x2b, 0xdc,
+        0x73, 0x48, 0x9e, 0x88, 0x9d, 0xf5, 0x83, 0x2f,
+        0x2d, 0x66, 0x1e, 0x78, 0x6c, 0x54, 0x78, 0x29,
+        0xb9, 0xa4, 0x4c, 0x90, 0x5e, 0xa2, 0xe6, 0x5c,
+        0xf1, 0x4f, 0xb5, 0x95, 0xa5, 0x54, 0xc0, 0x9f,
+        0x84, 0x47, 0xb4, 0x4c, 0xda, 0xae, 0x19, 0x29,
+        0x2b, 0x91, 0x2a, 0x81, 0x9d, 0x3a, 0x30, 0x40,
+        0xc5, 0xdf, 0xbb, 0xfa, 0xd8, 0x4c, 0xbc, 0x18
+    };
+
+    /* known SHA256 tls mac */
+    static const PRUint8 known_TLS_SHA256[] = {
+        0x66, 0xd6, 0x94, 0xd4, 0x0d, 0x32, 0x61, 0x38,
+        0x26, 0xf6, 0x8b, 0xfe, 0x9e, 0xac, 0xa2, 0xf5,
+        0x40, 0x52, 0x74, 0x3f, 0xbe, 0xb8, 0xca, 0x94,
+        0xc3, 0x64, 0xd6, 0x02, 0xf5, 0x88, 0x98, 0x35,
+        0x73, 0x9f, 0xce, 0xaa, 0x68, 0xe3, 0x7c, 0x93,
+        0x30, 0x21, 0x45, 0xec, 0xe9, 0x8f, 0x1c, 0x7e,
+        0xd1, 0x54, 0xf5, 0xbe, 0xff, 0xc8, 0xd7, 0x72,
+        0x7f, 0x9c, 0x0c, 0x7f, 0xa9, 0xd3, 0x4a, 0xd2
+    };
+
+#ifdef NSS_FULL_POST
+    /* known SHA224 tls mac */
+    static const PRUint8 known_TLS_SHA224[] = {
+        0xd8, 0x68, 0x15, 0xff, 0xa1, 0xa2, 0x5e, 0x16,
+        0xce, 0xb1, 0xfd, 0xbd, 0xda, 0x39, 0xbc, 0xa7,
+        0x27, 0x32, 0x78, 0x94, 0x66, 0xf0, 0x84, 0xcf,
+        0x46, 0xc0, 0x22, 0x76, 0xdc, 0x6b, 0x2e, 0xed,
+        0x1d, 0x2d, 0xd2, 0x93, 0xfd, 0xae, 0xca, 0xf9,
+        0xe0, 0x4c, 0x17, 0x23, 0x22, 0x5a, 0x73, 0x93,
+        0x20, 0x0a, 0xbd, 0xa0, 0x72, 0xf8, 0x8b, 0x74,
+        0xfb, 0xf1, 0xab, 0xb7, 0xe0, 0xec, 0x34, 0xc9
+    };
+
+    /* known SHA384 tls mac */
+    static const PRUint8 known_TLS_SHA384[] = {
+        0xb2, 0xac, 0x06, 0x10, 0xad, 0x50, 0xd5, 0xdc,
+        0xdb, 0x01, 0xea, 0xa6, 0x2d, 0x8a, 0x34, 0xb6,
+        0xeb, 0x84, 0xbc, 0x37, 0xc9, 0x9f, 0xa1, 0x9c,
+        0xd5, 0xbd, 0x4e, 0x66, 0x16, 0x24, 0xe5, 0x3d,
+        0xce, 0x74, 0xe0, 0x30, 0x41, 0x5c, 0xdb, 0xb7,
+        0x52, 0x1d, 0x2d, 0x4d, 0x9b, 0xbe, 0x6b, 0x86,
+        0xda, 0x8a, 0xca, 0x73, 0x39, 0xb4, 0xc7, 0x8f,
+        0x03, 0xb1, 0xf9, 0x7e, 0x65, 0xae, 0x17, 0x10
+    };
+
+    /* known SHA512 tls mac */
+    static const PRUint8 known_TLS_SHA512[] = {
+        0x73, 0x21, 0x4f, 0x40, 0x81, 0x1e, 0x90, 0xa1,
+        0x16, 0x40, 0x1e, 0x33, 0x69, 0xc5, 0x00, 0xc7,
+        0xc4, 0x81, 0xa3, 0x4f, 0xa7, 0xcc, 0x4a, 0xeb,
+        0x1a, 0x66, 0x00, 0x82, 0x52, 0xe2, 0x2f, 0x69,
+        0x14, 0x59, 0x05, 0x7c, 0xb0, 0x32, 0xce, 0xcc,
+        0xb7, 0xc9, 0xab, 0x0f, 0x73, 0x00, 0xe5, 0x52,
+        0x9d, 0x6b, 0x0e, 0x66, 0x4b, 0xb3, 0x0b, 0x0d,
+        0x34, 0x53, 0x97, 0x13, 0x84, 0x18, 0x31, 0x7a
+    };
+#endif
+
+    SECStatus status;
+    PRUint8 tls_computed[HASH_LENGTH_MAX];
+    SECItem secret;
+    SECItem seed;
+    SECItem result;
+    const char *tls_label = "fips test label";
+
+    secret.data = (unsigned char *)TLS_known_secret_key;
+    secret.len = TLS_known_secret_key_length;
+    seed.data = (unsigned char *)known_hash_message;
+    seed.len = FIPS_KNOWN_HASH_MESSAGE_LENGTH;
+    result.data = tls_computed;
+    result.len = sizeof(tls_computed);
+
+    /***************************************************/
+    /* TLS 1.0 PRF Known Answer Test                   */
+    /***************************************************/
+
+    status = TLS_PRF(&secret, tls_label, &seed, &result, PR_TRUE);
+
+    if ((status != SECSuccess) ||
+        (result.len != HASH_LENGTH_MAX) ||
+        (PORT_Memcmp(tls_computed, known_TLS_PRF,
+                     HASH_LENGTH_MAX) != 0)) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return (SECFailure);
+    }
+
+    /***************************************************/
+    /* TLS 1.2 SHA-256 Known Answer Test.              */
+    /***************************************************/
+
+    status = TLS_P_hash(HASH_AlgSHA256, &secret, tls_label,
+                        &seed, &result, PR_TRUE);
+
+    if ((status != SECSuccess) ||
+        (result.len != HASH_LENGTH_MAX) ||
+        (PORT_Memcmp(tls_computed, known_TLS_SHA256,
+                     HASH_LENGTH_MAX) != 0)) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return (SECFailure);
+    }
+
+#ifdef NSS_FULL_POST
+    /***************************************************/
+    /* TLS 1.2 SHA-224 Known Answer Test.              */
+    /***************************************************/
+
+    status = TLS_P_hash(HASH_AlgSHA224, &secret, tls_label,
+                        &seed, &result, PR_TRUE);
+
+    if ((status != SECSuccess) ||
+        (result.len != HASH_LENGTH_MAX) ||
+        (PORT_Memcmp(tls_computed, known_TLS_SHA224,
+                     HASH_LENGTH_MAX) != 0)) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return (SECFailure);
+    }
+
+    /***************************************************/
+    /* TLS 1.2 SHA-384 Known Answer Test.              */
+    /***************************************************/
+
+    status = TLS_P_hash(HASH_AlgSHA384, &secret, tls_label,
+                        &seed, &result, PR_TRUE);
+
+    if ((status != SECSuccess) ||
+        (result.len != HASH_LENGTH_MAX) ||
+        (PORT_Memcmp(tls_computed, known_TLS_SHA384,
+                     HASH_LENGTH_MAX) != 0)) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return (SECFailure);
+    }
+
+    /***************************************************/
+    /* TLS 1.2 SHA-512 Known Answer Test.              */
+    /***************************************************/
+
+    status = TLS_P_hash(HASH_AlgSHA512, &secret, tls_label,
+                        &seed, &result, PR_TRUE);
+
+    if ((status != SECSuccess) ||
+        (result.len != HASH_LENGTH_MAX) ||
+        (PORT_Memcmp(tls_computed, known_TLS_SHA512,
+                     HASH_LENGTH_MAX) != 0)) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return (SECFailure);
+    }
+#endif
+
+    return (SECSuccess);
+}
+
 static SECStatus
 freebl_fips_SHA_PowerUpSelfTest(void)
 {
     /* SHA-1 Known Digest Message (160-bits). */
     static const PRUint8 sha1_known_digest[] = {
         0x0a, 0x6d, 0x07, 0xba, 0x1e, 0xbd, 0x8a, 0x1b,
         0x72, 0xf6, 0xc7, 0x22, 0xf1, 0x27, 0x9f, 0xf0,
         0xe0, 0x68, 0x47, 0x7a
@@ -1954,16 +2177,22 @@ freebl_fipsPowerUpSelfTest(unsigned int 
             return rv;
 
         /* HMAC SHA-X Power-Up SelfTest(s). */
         rv = freebl_fips_HMAC_PowerUpSelfTest();
 
         if (rv != SECSuccess)
             return rv;
 
+        /* TLS PRF Power-Up SelfTest(s). */
+        rv = freebl_fips_TLS_PowerUpSelfTest();
+
+        if (rv != SECSuccess)
+            return rv;
+
         /* NOTE: RSA can only be tested in full freebl. It requires access to
          * the locking primitives */
         /* RSA Power-Up SelfTest(s). */
         rv = freebl_fips_RSA_PowerUpSelfTest();
 
         if (rv != SECSuccess)
             return rv;
 
diff --git a/lib/softoken/fipstest.c b/lib/softoken/fipstest.c
--- a/lib/softoken/fipstest.c
+++ b/lib/softoken/fipstest.c
@@ -8,16 +8,17 @@
 #ifndef NSS_FIPS_DISABLED
 #include "seccomon.h"
 #include "blapi.h"
 #include "softoken.h"
 #include "lowkeyi.h"
 #include "secoid.h"
 #include "secerr.h"
 #include "pkcs11i.h"
+#include "lowpbe.h"
 
 /*
  * different platforms have different ways of calling and initial entry point
  * when the dll/.so is loaded. Most platforms support either a posix pragma
  * or the GCC attribute. Some platforms suppor a pre-defined name, and some
  * platforms have a link line way of invoking this function.
  */
 
@@ -576,16 +577,116 @@ rsa_loser:
 
     nsslowkey_DestroyPublicKey(rsa_public_key);
     nsslowkey_DestroyPrivateKey(rsa_private_key);
 
     PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
     return (SECFailure);
 }
 
+static SECStatus
+sftk_fips_HKDF_PowerUpSelfTest(void)
+{
+    SECStatus status;
+    static const unsigned char base_key[] = {
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+        0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+        0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+        0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+        0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f
+    };
+    static const unsigned char known_hkdf_sha256_key[] = {
+        0xdd, 0xdb, 0xeb, 0xe5, 0x6d, 0xd2, 0x96, 0xa4,
+        0x07, 0xc5, 0x7d, 0xda, 0x31, 0x56, 0x8d, 0xa5,
+        0x41, 0x3e, 0x90, 0xd4, 0xe6, 0x98, 0xeb, 0xf8,
+        0x5a, 0x49, 0x7f, 0x38, 0xef, 0x01, 0x8a, 0xe5,
+        0xda, 0x36, 0xe5, 0xcf, 0x21, 0xe3, 0x9f, 0xc3,
+        0x32, 0xb3, 0x1e, 0xf6, 0xc5, 0x10, 0x4c, 0x86,
+        0x53, 0x5e, 0x6f, 0xe0, 0x63, 0x6e, 0x43, 0x33,
+        0x61, 0x35, 0xf4, 0x17, 0x10, 0x77, 0x75, 0x2a
+    };
+/* current NIST IG's say we only need to test one instance
+ * of kdfs, keep these others around in case the guidance
+ * changes */
+#ifdef NSS_FULL_POST
+    static const unsigned char known_hkdf_sha384_key[] = {
+        0x35, 0x64, 0xc4, 0xa1, 0xcc, 0xc1, 0xdc, 0xe4,
+        0xe2, 0xca, 0x51, 0xae, 0xe8, 0x92, 0x88, 0x30,
+        0x8b, 0xb0, 0x2b, 0xac, 0x00, 0x15, 0xac, 0x15,
+        0x97, 0xc9, 0xf4, 0x6b, 0xf6, 0x3f, 0x97, 0xea,
+        0x48, 0x55, 0x38, 0x25, 0x06, 0x5d, 0x91, 0x64,
+        0xbd, 0x09, 0xf3, 0x44, 0xbc, 0x82, 0xbe, 0xdb,
+        0x5c, 0xd7, 0xf2, 0x24, 0xa5, 0x55, 0x8d, 0xa9,
+        0xa8, 0x85, 0xde, 0x8c, 0x33, 0xe0, 0x4d, 0xc3
+    };
+    static const unsigned char known_hkdf_sha512_key[] = {
+        0x63, 0x4e, 0xbc, 0x42, 0xb3, 0x56, 0x74, 0x7d,
+        0x1b, 0x55, 0xf0, 0x34, 0x54, 0xcb, 0x6d, 0x58,
+        0x39, 0x96, 0x10, 0xda, 0x03, 0x20, 0x8f, 0x77,
+        0x0d, 0xb4, 0xf7, 0xf6, 0x67, 0x0d, 0x5b, 0x6b,
+        0xd0, 0x30, 0xc4, 0xdd, 0x67, 0x61, 0x5d, 0x9a,
+        0xf5, 0x18, 0x6e, 0x1b, 0x60, 0x97, 0xc2, 0x4d,
+        0x23, 0x43, 0x69, 0xe6, 0x3b, 0xa5, 0xdf, 0xe9,
+        0x7c, 0xf1, 0x87, 0x48, 0x6f, 0xb9, 0xd3, 0x02
+    };
+#endif
+    unsigned char outBytes[64] = { 0 };
+
+    CK_HKDF_PARAMS hkdf_params;
+
+    hkdf_params.bExpand = CK_TRUE;
+    hkdf_params.bExtract = CK_TRUE;
+    hkdf_params.ulSaltType = CKF_HKDF_SALT_DATA;
+    hkdf_params.pSalt = (CK_BYTE_PTR)base_key;
+    hkdf_params.ulSaltLen = sizeof(base_key);
+    hkdf_params.pInfo = (CK_BYTE_PTR)base_key;
+    hkdf_params.ulInfoLen = sizeof(base_key);
+
+    /**************************************************/
+    /* HKDF tests                                     */
+    /**************************************************/
+
+    hkdf_params.prfHashMechanism = CKM_SHA256_HMAC;
+    status = sftk_HKDF(&hkdf_params, CK_INVALID_HANDLE, NULL,
+                       base_key, 32, NULL, outBytes, sizeof(outBytes),
+                       PR_TRUE, PR_TRUE);
+    if ((status != SECSuccess) ||
+        PORT_Memcmp(outBytes, known_hkdf_sha256_key, sizeof(outBytes)) != 0) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return (SECFailure);
+    }
+
+#ifdef NSS_FULL_POST
+    hkdf_params.prfHashMechanism = CKM_SHA384_HMAC;
+    status = sftk_HKDF(&hkdf_params, CK_INVALID_HANDLE, NULL,
+                       base_key, 48, NULL, outBytes, sizeof(outBytes),
+                       PR_TRUE, PR_TRUE);
+    if ((status != SECSuccess) ||
+        PORT_Memcmp(outBytes, known_hkdf_sha384_key, sizeof(outBytes)) != 0) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return (SECFailure);
+    }
+
+    hkdf_params.prfHashMechanism = CKM_SHA512_HMAC;
+    status = sftk_HKDF(&hkdf_params, CK_INVALID_HANDLE, NULL,
+                       base_key, 64, NULL, outBytes, sizeof(outBytes),
+                       PR_TRUE, PR_TRUE);
+    if ((status != SECSuccess) ||
+        PORT_Memcmp(outBytes, known_hkdf_sha512_key, sizeof(outBytes)) != 0) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return (SECFailure);
+    }
+#endif
+
+    return (SECSuccess);
+}
+
 #define FIPS_DSA_TYPE siBuffer
 #define FIPS_DSA_DIGEST_LENGTH 28    /*  224-bits */
 #define FIPS_DSA_SUBPRIME_LENGTH 28  /*  224-bits */
 #define FIPS_DSA_SIGNATURE_LENGTH 56 /*  448-bits */
 #define FIPS_DSA_PRIME_LENGTH 256    /* 2048-bits */
 #define FIPS_DSA_BASE_LENGTH 256     /* 2048-bits */
 
 /* Similar to freebl_fips_DSA_PowerUpSelfTest, but using DSA_HashSign() */
@@ -956,16 +1057,32 @@ sftk_startup_tests(void)
     }
 
     /* Checksum is done by fips_initTestSoftoken() in fips.c */
 
     rv = sftk_fips_IKE_PowerUpSelfTests();
     if (rv != SECSuccess) {
         return;
     }
+
+    rv = sftk_fips_SP800_108_PowerUpSelfTests();
+    if (rv != SECSuccess) {
+        return;
+    }
+
+    rv = sftk_fips_HKDF_PowerUpSelfTest();
+    if (rv != SECSuccess) {
+        return;
+    }
+
+    rv = sftk_fips_pbkdf_PowerUpSelfTests();
+    if (rv != SECSuccess) {
+        return;
+    }
+
     sftk_self_tests_success = PR_TRUE;
 }
 
 /*
  * this is called from nsc_Common_Initizialize entry points that gates access
  * to * all other pkcs11 functions. This prevents softoken operation if our
  * power on selftest failed.
  */
diff --git a/lib/softoken/kbkdf.c b/lib/softoken/kbkdf.c
--- a/lib/softoken/kbkdf.c
+++ b/lib/softoken/kbkdf.c
@@ -85,17 +85,17 @@ kbkdf_LoadParameters(CK_MECHANISM_TYPE m
 
         (*kdf_params) = *in_params;
     }
 
     return CKR_OK;
 }
 
 static CK_RV
-kbkdf_ValidateParameter(CK_MECHANISM_TYPE mech, CK_PRF_DATA_PARAM_PTR data)
+kbkdf_ValidateParameter(CK_MECHANISM_TYPE mech, const CK_PRF_DATA_PARAM *data)
 {
     /* This function validates that the passed data parameter (data) conforms
      * to PKCS#11 v3.0's expectations for KDF parameters. This depends both on
      * the type of this parameter (data->type) and on the KDF mechanism (mech)
      * as certain parameters are context dependent (like Iteration Variable).
      */
 
     /* If the parameter is missing a value when one is expected, then this
@@ -260,17 +260,17 @@ failure:
      * key's template (ie the template defined by the content of pTemplate),
      * the corresponding phKey value will be set to CK_INVALID_HANDLE to
      * identify the offending template. */
     *(key->phKey) = CK_INVALID_HANDLE;
     return CKR_MECHANISM_PARAM_INVALID;
 }
 
 static CK_RV
-kbkdf_ValidateParameters(CK_MECHANISM_TYPE mech, CK_SP800_108_KDF_PARAMS_PTR params, CK_ULONG keySize)
+kbkdf_ValidateParameters(CK_MECHANISM_TYPE mech, const CK_SP800_108_KDF_PARAMS *params, CK_ULONG keySize)
 {
     CK_RV ret = CKR_MECHANISM_PARAM_INVALID;
     int param_type_count[5] = { 0, 0, 0, 0, 0 };
     size_t offset = 0;
 
     /* Start with checking the prfType as a mechanism against a list of
      * PRFs allowed by PKCS#11 v3.0. */
     if (!(/* The following types aren't defined in NSS yet. */
@@ -339,17 +339,17 @@ kbkdf_ValidateParameters(CK_MECHANISM_TY
     }
 
     return CKR_OK;
 }
 
 /* [ section: parameter helpers ] */
 
 static CK_VOID_PTR
-kbkdf_FindParameter(CK_SP800_108_KDF_PARAMS_PTR params, CK_PRF_DATA_TYPE type)
+kbkdf_FindParameter(const CK_SP800_108_KDF_PARAMS *params, CK_PRF_DATA_TYPE type)
 {
     for (size_t offset = 0; offset < params->ulNumberOfDataParams; offset++) {
         if (params->pDataParams[offset].type == type) {
             return params->pDataParams[offset].pValue;
         }
     }
 
     return NULL;
@@ -387,17 +387,17 @@ kbkdf_GetDerivedKeySize(CK_DERIVED_KEY_P
     }
 
     /* Else, fall back to this mapping. We know kbkdf_ValidateDerived(...)
      * passed, so this should return non-zero. */
     return sftk_MapKeySize(keyType);
 }
 
 static CK_RV
-kbkdf_CalculateLength(CK_SP800_108_KDF_PARAMS_PTR params, sftk_MACCtx *ctx, CK_ULONG ret_key_size, PRUint64 *output_bitlen, size_t *buffer_length)
+kbkdf_CalculateLength(const CK_SP800_108_KDF_PARAMS *params, sftk_MACCtx *ctx, CK_ULONG ret_key_size, PRUint64 *output_bitlen, size_t *buffer_length)
 {
     /* Two cases: either we have additional derived keys or we don't. In the
      * case that we don't, the length of the derivation is the size of the
      * single derived key, and that is the length of the PRF buffer. Otherwise,
      * we need to use the proper CK_SP800_108_DKM_LENGTH_METHOD to calculate
      * the length of the output (in bits), with a separate value for the size
      * of the PRF data buffer. This means that, under PKCS#11 with additional
      * derived keys, we lie to the KDF about the _actual_ length of the PRF
@@ -460,17 +460,17 @@ kbkdf_CalculateLength(CK_SP800_108_KDF_P
      * the size in bytes. However, output_bitlen needs to be in bits, so
      * multiply by 8 here. */
     *output_bitlen *= 8;
 
     return CKR_OK;
 }
 
 static CK_RV
-kbkdf_CalculateIterations(CK_MECHANISM_TYPE mech, CK_SP800_108_KDF_PARAMS_PTR params, sftk_MACCtx *ctx, size_t buffer_length, PRUint32 *num_iterations)
+kbkdf_CalculateIterations(CK_MECHANISM_TYPE mech, const CK_SP800_108_KDF_PARAMS *params, sftk_MACCtx *ctx, size_t buffer_length, PRUint32 *num_iterations)
 {
     CK_SP800_108_COUNTER_FORMAT_PTR param_ptr = NULL;
     PRUint64 iteration_count;
     PRUint64 r = 32;
 
     /* We need to know how many full iterations are required. This is done
      * by rounding up the division of the PRF length into buffer_length.
      * However, we're not guaranteed that the last output is a full PRF
@@ -514,17 +514,17 @@ kbkdf_CalculateIterations(CK_MECHANISM_T
     }
 
     *num_iterations = (PRUint32)iteration_count;
 
     return CKR_OK;
 }
 
 static CK_RV
-kbkdf_AddParameters(CK_MECHANISM_TYPE mech, sftk_MACCtx *ctx, CK_SP800_108_KDF_PARAMS *params, PRUint32 counter, PRUint64 length, unsigned char *chaining_prf, size_t chaining_prf_len, CK_PRF_DATA_TYPE exclude)
+kbkdf_AddParameters(CK_MECHANISM_TYPE mech, sftk_MACCtx *ctx, const CK_SP800_108_KDF_PARAMS *params, PRUint32 counter, PRUint64 length, const unsigned char *chaining_prf, size_t chaining_prf_len, CK_PRF_DATA_TYPE exclude)
 {
     size_t offset = 0;
     CK_RV ret = CKR_OK;
 
     for (offset = 0; offset < params->ulNumberOfDataParams; offset++) {
         CK_PRF_DATA_PARAM_PTR param = params->pDataParams + offset;
 
         if (param->type == exclude) {
@@ -744,17 +744,17 @@ kbkdf_SaveKeys(CK_MECHANISM_TYPE mech, C
     }
 
     return CKR_OK;
 }
 
 /* [ section: KDFs ] */
 
 static CK_RV
-kbkdf_CounterRaw(CK_SP800_108_KDF_PARAMS_PTR params, sftk_MACCtx *ctx, unsigned char *ret_buffer, size_t buffer_length, PRUint64 output_bitlen)
+kbkdf_CounterRaw(const CK_SP800_108_KDF_PARAMS *params, sftk_MACCtx *ctx, unsigned char *ret_buffer, size_t buffer_length, PRUint64 output_bitlen)
 {
     CK_RV ret = CKR_OK;
 
     /* Counter variable for this KDF instance. */
     PRUint32 counter;
 
     /* Number of iterations required of this PRF necessary to reach the
      * desired output length. */
@@ -832,17 +832,17 @@ kbkdf_CounterRaw(CK_SP800_108_KDF_PARAMS
             }
         }
     }
 
     return CKR_OK;
 }
 
 static CK_RV
-kbkdf_FeedbackRaw(CK_SP800_108_KDF_PARAMS_PTR params, unsigned char *initial_value, CK_ULONG initial_value_length, sftk_MACCtx *ctx, unsigned char *ret_buffer, size_t buffer_length, PRUint64 output_bitlen)
+kbkdf_FeedbackRaw(const CK_SP800_108_KDF_PARAMS *params, const unsigned char *initial_value, CK_ULONG initial_value_length, sftk_MACCtx *ctx, unsigned char *ret_buffer, size_t buffer_length, PRUint64 output_bitlen)
 {
     CK_RV ret = CKR_OK;
 
     /* Counter variable for this KDF instance. */
     PRUint32 counter;
 
     /* Number of iterations required of this PRF necessary to reach the
      * desired output length. */
@@ -854,17 +854,17 @@ kbkdf_FeedbackRaw(CK_SP800_108_KDF_PARAM
     /* Size of this block, in bytes. Defaults to ctx->mac_size except on
      * the last iteration where it could be a partial block. */
     size_t block_size = ctx->mac_size;
 
     /* The last PRF invocation and/or the initial value; used for feedback
      * chaining in this KDF. Note that we have to make it large enough to
      * fit the output of the PRF, but we can delay its actual creation until
      * the first PRF invocation. Until then, point to the IV value. */
-    unsigned char *chaining_value = initial_value;
+    unsigned char *chaining_value = (unsigned char *)initial_value;
 
     /* Size of the chaining value discussed above. Defaults to the size of
      * the IV value. */
     size_t chaining_length = initial_value_length;
 
     /* Calculate the number of iterations required based on the size of the
      * output buffer. */
     ret = kbkdf_CalculateIterations(CKM_SP800_108_FEEDBACK_KDF, params, ctx, buffer_length, &num_iterations);
@@ -957,17 +957,17 @@ finish:
     if (chaining_value != initial_value && chaining_value != NULL) {
         PORT_ZFree(chaining_value, chaining_length);
     }
 
     return ret;
 }
 
 static CK_RV
-kbkdf_PipelineRaw(CK_SP800_108_KDF_PARAMS_PTR params, sftk_MACCtx *ctx, unsigned char *ret_buffer, size_t buffer_length, PRUint64 output_bitlen)
+kbkdf_PipelineRaw(const CK_SP800_108_KDF_PARAMS *params, sftk_MACCtx *ctx, unsigned char *ret_buffer, size_t buffer_length, PRUint64 output_bitlen)
 {
     CK_RV ret = CKR_OK;
 
     /* Counter variable for this KDF instance. */
     PRUint32 counter;
 
     /* Number of iterations required of this PRF necessary to reach the
      * desired output length. */
@@ -1104,41 +1104,27 @@ kbkdf_PipelineRaw(CK_SP800_108_KDF_PARAM
     }
 
 finish:
     PORT_ZFree(chaining_value, chaining_length);
 
     return ret;
 }
 
-/* [ section: PKCS#11 entry ] */
-
-CK_RV
-kbkdf_Dispatch(CK_MECHANISM_TYPE mech, CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, SFTKObject *prf_key, SFTKObject *ret_key, CK_ULONG ret_key_size)
+static CK_RV
+kbkdf_RawDispatch(CK_MECHANISM_TYPE mech,
+                  const CK_SP800_108_KDF_PARAMS *kdf_params,
+                  const CK_BYTE *initial_value,
+                  CK_ULONG initial_value_length,
+                  SFTKObject *prf_key, const unsigned char *prf_key_bytes,
+                  unsigned int prf_key_length, unsigned char **out_key_bytes,
+                  size_t *out_key_length, unsigned int *mac_size,
+                  CK_ULONG ret_key_size)
 {
-    /* This handles boilerplate common to all KBKDF types. Instead of placing
-     * this in pkcs11c.c, place it here to reduce clutter. */
-
     CK_RV ret;
-
-    /* Assumptions about our calling environment. */
-    PR_ASSERT(pMechanism != NULL && prf_key != NULL && ret_key != NULL);
-
-    /* Validate that the caller passed parameters. */
-    if (pMechanism->pParameter == NULL) {
-        return CKR_MECHANISM_PARAM_INVALID;
-    }
-
-    /* Create a common set of parameters to use for all KDF types. This
-     * separates out the KDF parameters from the Feedback-specific IV,
-     * allowing us to use a common type for all calls. */
-    CK_SP800_108_KDF_PARAMS kdf_params = { 0 };
-    CK_BYTE_PTR initial_value = NULL;
-    CK_ULONG initial_value_length = 0;
-
     /* Context for our underlying PRF function.
      *
      * Zeroing context required unconditional call of sftk_MAC_Destroy.
      */
     sftk_MACCtx ctx = { 0 };
 
     /* We need one buffers large enough to fit the entire KDF key stream for
      * all iterations of the PRF. This needs only include to the end of the
@@ -1163,80 +1149,370 @@ kbkdf_Dispatch(CK_MECHANISM_TYPE mech, C
      * and size of keys (again, 2^32); this could easily exceed 256GB when
      * counting the backing softoken key, the key data, template data, and
      * the input parameters to this KDF.
      *
      * This is the L parameter in NIST SP800-108.
      */
     PRUint64 output_bitlen = 0;
 
-    /* Split Feedback-specific IV from remaining KDF parameters. */
-    ret = kbkdf_LoadParameters(mech, pMechanism, &kdf_params, &initial_value, &initial_value_length);
-    if (ret != CKR_OK) {
-        goto finish;
-    }
-
     /* First validate our passed input parameters against PKCS#11 v3.0
      * and NIST SP800-108 requirements. */
-    ret = kbkdf_ValidateParameters(mech, &kdf_params, ret_key_size);
+    ret = kbkdf_ValidateParameters(mech, kdf_params, ret_key_size);
     if (ret != CKR_OK) {
         goto finish;
     }
 
     /* Initialize the underlying PRF state. */
-    ret = sftk_MAC_Init(&ctx, kdf_params.prfType, prf_key);
+    if (prf_key) {
+        ret = sftk_MAC_Init(&ctx, kdf_params->prfType, prf_key);
+    } else {
+        ret = sftk_MAC_InitRaw(&ctx, kdf_params->prfType, prf_key_bytes,
+                               prf_key_length, PR_TRUE);
+    }
     if (ret != CKR_OK) {
         goto finish;
     }
 
     /* Compute the size of our output buffer based on passed parameters and
      * the output size of the underlying PRF. */
-    ret = kbkdf_CalculateLength(&kdf_params, &ctx, ret_key_size, &output_bitlen, &buffer_length);
+    ret = kbkdf_CalculateLength(kdf_params, &ctx, ret_key_size, &output_bitlen, &buffer_length);
     if (ret != CKR_OK) {
         goto finish;
     }
 
     /* Allocate memory for the PRF output */
     output_buffer = PORT_ZNewArray(unsigned char, buffer_length);
     if (output_buffer == NULL) {
         ret = CKR_HOST_MEMORY;
         goto finish;
     }
 
     /* Call into the underlying KDF */
     switch (mech) {
         case CKM_NSS_SP800_108_COUNTER_KDF_DERIVE_DATA: /* fall through */
         case CKM_SP800_108_COUNTER_KDF:
-            ret = kbkdf_CounterRaw(&kdf_params, &ctx, output_buffer, buffer_length, output_bitlen);
+            ret = kbkdf_CounterRaw(kdf_params, &ctx, output_buffer, buffer_length, output_bitlen);
             break;
         case CKM_NSS_SP800_108_FEEDBACK_KDF_DERIVE_DATA: /* fall through */
         case CKM_SP800_108_FEEDBACK_KDF:
-            ret = kbkdf_FeedbackRaw(&kdf_params, initial_value, initial_value_length, &ctx, output_buffer, buffer_length, output_bitlen);
+            ret = kbkdf_FeedbackRaw(kdf_params, initial_value, initial_value_length, &ctx, output_buffer, buffer_length, output_bitlen);
             break;
         case CKM_NSS_SP800_108_DOUBLE_PIPELINE_KDF_DERIVE_DATA: /* fall through */
         case CKM_SP800_108_DOUBLE_PIPELINE_KDF:
-            ret = kbkdf_PipelineRaw(&kdf_params, &ctx, output_buffer, buffer_length, output_bitlen);
+            ret = kbkdf_PipelineRaw(kdf_params, &ctx, output_buffer, buffer_length, output_bitlen);
             break;
         default:
             /* Shouldn't happen unless NIST introduces a new KBKDF type. */
             PR_ASSERT(PR_FALSE);
             ret = CKR_FUNCTION_FAILED;
     }
 
     /* Validate the above KDF succeeded. */
     if (ret != CKR_OK) {
         goto finish;
     }
 
+    *out_key_bytes = output_buffer;
+    *out_key_length = buffer_length;
+    *mac_size = ctx.mac_size;
+
+    output_buffer = NULL; /* returning the buffer, don't zero and free it */
+
+finish:
+    PORT_ZFree(output_buffer, buffer_length);
+
+    /* Free the PRF. This should handle clearing all sensitive information. */
+    sftk_MAC_Destroy(&ctx, PR_FALSE);
+    return ret;
+}
+
+/* [ section: PKCS#11 entry ] */
+
+CK_RV
+kbkdf_Dispatch(CK_MECHANISM_TYPE mech, CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, SFTKObject *prf_key, SFTKObject *ret_key, CK_ULONG ret_key_size)
+{
+    /* This handles boilerplate common to all KBKDF types. Instead of placing
+     * this in pkcs11c.c, place it here to reduce clutter. */
+
+    CK_RV ret;
+
+    /* Assumptions about our calling environment. */
+    PR_ASSERT(pMechanism != NULL && prf_key != NULL && ret_key != NULL);
+
+    /* Validate that the caller passed parameters. */
+    if (pMechanism->pParameter == NULL) {
+        return CKR_MECHANISM_PARAM_INVALID;
+    }
+
+    /* Create a common set of parameters to use for all KDF types. This
+     * separates out the KDF parameters from the Feedback-specific IV,
+     * allowing us to use a common type for all calls. */
+    CK_SP800_108_KDF_PARAMS kdf_params = { 0 };
+    CK_BYTE_PTR initial_value = NULL;
+    CK_ULONG initial_value_length = 0;
+    unsigned char *output_buffer = NULL;
+    size_t buffer_length = 0;
+    unsigned int mac_size = 0;
+
+    /* Split Feedback-specific IV from remaining KDF parameters. */
+    ret = kbkdf_LoadParameters(mech, pMechanism, &kdf_params, &initial_value, &initial_value_length);
+    if (ret != CKR_OK) {
+        goto finish;
+    }
+    /* let rawDispatch handle the rest. We split this out so we could
+     * handle the POST test without accessing pkcs #11 objects. */
+    ret = kbkdf_RawDispatch(mech, &kdf_params, initial_value,
+                            initial_value_length, prf_key, NULL, 0,
+                            &output_buffer, &buffer_length, &mac_size,
+                            ret_key_size);
+    if (ret != CKR_OK) {
+        goto finish;
+    }
+
     /* Write the output of the PRF into the appropriate keys. */
-    ret = kbkdf_SaveKeys(mech, hSession, &kdf_params, output_buffer, buffer_length, ctx.mac_size, ret_key, ret_key_size);
+    ret = kbkdf_SaveKeys(mech, hSession, &kdf_params, output_buffer, buffer_length, mac_size, ret_key, ret_key_size);
     if (ret != CKR_OK) {
         goto finish;
     }
 
 finish:
     PORT_ZFree(output_buffer, buffer_length);
 
-    /* Free the PRF. This should handle clearing all sensitive information. */
-    sftk_MAC_Destroy(&ctx, PR_FALSE);
-
     return ret;
 }
+
+struct sftk_SP800_Test_struct {
+    CK_MECHANISM_TYPE mech;
+    CK_SP800_108_KDF_PARAMS kdf_params;
+    unsigned int expected_mac_size;
+    unsigned int ret_key_length;
+    const unsigned char expected_key_bytes[64];
+};
+
+static const CK_SP800_108_COUNTER_FORMAT counter_32 = { 0, 32 };
+static const CK_PRF_DATA_PARAM counter_32_data =
+    { CK_SP800_108_ITERATION_VARIABLE, (CK_VOID_PTR)&counter_32, sizeof(counter_32) };
+
+#ifdef NSS_FULL_POST
+static const CK_SP800_108_COUNTER_FORMAT counter_16 = { 0, 16 };
+static const CK_PRF_DATA_PARAM counter_16_data =
+    { CK_SP800_108_ITERATION_VARIABLE, (CK_VOID_PTR)&counter_16, sizeof(counter_16) };
+static const CK_PRF_DATA_PARAM counter_null_data =
+    { CK_SP800_108_ITERATION_VARIABLE, NULL, 0 };
+#endif
+
+static const struct sftk_SP800_Test_struct sftk_SP800_Tests[] =
+    {
+#ifdef NSS_FULL_POST
+      {
+          CKM_SP800_108_COUNTER_KDF,
+          { CKM_AES_CMAC, 1, (CK_PRF_DATA_PARAM_PTR)&counter_16_data, 0, NULL },
+          16,
+          64,
+          { 0x7b, 0x1c, 0xe7, 0xf3, 0x14, 0x67, 0x15, 0xdd,
+            0xde, 0x0c, 0x09, 0x46, 0x3f, 0x47, 0x7b, 0xa6,
+            0xb8, 0xba, 0x40, 0x07, 0x7c, 0xe3, 0x19, 0x53,
+            0x26, 0xac, 0x4c, 0x2e, 0x2b, 0x37, 0x41, 0xe4,
+            0x1b, 0x01, 0x3f, 0x2f, 0x2d, 0x16, 0x95, 0xee,
+            0xeb, 0x7e, 0x72, 0x7d, 0xa4, 0xab, 0x2e, 0x67,
+            0x1d, 0xef, 0x6f, 0xa2, 0xc6, 0xee, 0x3c, 0xcf,
+            0xef, 0x88, 0xfd, 0x5c, 0x1d, 0x7b, 0xa0, 0x5a },
+      },
+      {
+          CKM_SP800_108_COUNTER_KDF,
+          { CKM_SHA384_HMAC, 1, (CK_PRF_DATA_PARAM_PTR)&counter_32_data, 0, NULL },
+          48,
+          64,
+          { 0xe6, 0x62, 0xa4, 0x32, 0x5c, 0xe4, 0xc2, 0x28,
+            0x73, 0x8a, 0x5d, 0x94, 0xe7, 0x05, 0xe0, 0x5a,
+            0x71, 0x61, 0xb2, 0x3c, 0x51, 0x28, 0x03, 0x1d,
+            0xa7, 0xf5, 0x10, 0x83, 0x34, 0xdb, 0x11, 0x73,
+            0x92, 0xa6, 0x79, 0x74, 0x81, 0x5d, 0x22, 0x7e,
+            0x8d, 0xf2, 0x59, 0x14, 0x56, 0x60, 0xcf, 0xb2,
+            0xb3, 0xfd, 0x46, 0xfd, 0x9b, 0x74, 0xfe, 0x4a,
+            0x09, 0x30, 0x4a, 0xdf, 0x07, 0x43, 0xfe, 0x85 },
+      },
+      {
+          CKM_SP800_108_COUNTER_KDF,
+          { CKM_SHA512_HMAC, 1, (CK_PRF_DATA_PARAM_PTR)&counter_32_data, 0, NULL },
+          64,
+          64,
+          { 0xb0, 0x78, 0x36, 0xe1, 0x15, 0xd6, 0xf0, 0xac,
+            0x68, 0x7b, 0x42, 0xd3, 0xb6, 0x82, 0x51, 0xad,
+            0x95, 0x0a, 0x69, 0x88, 0x84, 0xc2, 0x2e, 0x07,
+            0x34, 0x62, 0x8d, 0x42, 0x72, 0x0f, 0x22, 0xe6,
+            0xd5, 0x7f, 0x80, 0x15, 0xe6, 0x84, 0x00, 0x65,
+            0xef, 0x64, 0x77, 0x29, 0xd6, 0x3b, 0xc7, 0x9a,
+            0x15, 0x6d, 0x36, 0xf3, 0x96, 0xc9, 0x14, 0x3f,
+            0x2d, 0x4a, 0x7c, 0xdb, 0xc3, 0x6c, 0x3d, 0x6a },
+      },
+      {
+          CKM_SP800_108_FEEDBACK_KDF,
+          { CKM_AES_CMAC, 1, (CK_PRF_DATA_PARAM_PTR)&counter_null_data, 0, NULL },
+          16,
+          64,
+          { 0xc0, 0xa0, 0x23, 0x96, 0x16, 0x4d, 0xd6, 0xbd,
+            0x2a, 0x75, 0x8e, 0x72, 0xf5, 0xc3, 0xa0, 0xb8,
+            0x78, 0x83, 0x15, 0x21, 0x34, 0xd3, 0xd8, 0x71,
+            0xc9, 0xe7, 0x4b, 0x20, 0xb7, 0x65, 0x5b, 0x13,
+            0xbc, 0x85, 0x54, 0xe3, 0xb6, 0xee, 0x73, 0xd5,
+            0xf2, 0xa0, 0x94, 0x1a, 0x79, 0x66, 0x3b, 0x1e,
+            0x67, 0x3e, 0x69, 0xa4, 0x12, 0x40, 0xa9, 0xda,
+            0x8d, 0x14, 0xb1, 0xce, 0xf1, 0x4b, 0x79, 0x4e },
+      },
+      {
+          CKM_SP800_108_FEEDBACK_KDF,
+          { CKM_SHA256_HMAC, 1, (CK_PRF_DATA_PARAM_PTR)&counter_null_data, 0, NULL },
+          32,
+          64,
+          { 0x99, 0x9b, 0x08, 0x79, 0x14, 0x2e, 0x58, 0x34,
+            0xd7, 0x92, 0xa7, 0x7e, 0x7f, 0xc2, 0xf0, 0x34,
+            0xa3, 0x4e, 0x33, 0xf0, 0x63, 0x95, 0x2d, 0xad,
+            0xbf, 0x3b, 0xcb, 0x6d, 0x4e, 0x07, 0xd9, 0xe9,
+            0xbd, 0xbd, 0x77, 0x54, 0xe1, 0xa3, 0x36, 0x26,
+            0xcd, 0xb1, 0xf9, 0x2d, 0x80, 0x68, 0xa2, 0x01,
+            0x4e, 0xbf, 0x35, 0xec, 0x65, 0xae, 0xfd, 0x71,
+            0xa6, 0xd7, 0x62, 0x26, 0x2c, 0x3f, 0x73, 0x63 },
+      },
+      {
+          CKM_SP800_108_FEEDBACK_KDF,
+          { CKM_SHA384_HMAC, 1, (CK_PRF_DATA_PARAM_PTR)&counter_null_data, 0, NULL },
+          48,
+          64,
+          { 0xc8, 0x7a, 0xf8, 0xd9, 0x6b, 0x90, 0x82, 0x35,
+            0xea, 0xf5, 0x2c, 0x8f, 0xce, 0xaa, 0x3b, 0xa5,
+            0x68, 0xd3, 0x7f, 0xae, 0x31, 0x93, 0xe6, 0x69,
+            0x0c, 0xd1, 0x74, 0x7f, 0x8f, 0xc2, 0xe2, 0x33,
+            0x93, 0x45, 0x23, 0xba, 0xb3, 0x73, 0xc9, 0x2c,
+            0xd6, 0xd2, 0x10, 0x16, 0xe9, 0x9f, 0x9e, 0xe8,
+            0xc1, 0x0e, 0x29, 0x95, 0x3d, 0x16, 0x68, 0x24,
+            0x40, 0x4d, 0x40, 0x21, 0x41, 0xa6, 0xc8, 0xdb },
+      },
+      {
+          CKM_SP800_108_FEEDBACK_KDF,
+          { CKM_SHA512_HMAC, 1, (CK_PRF_DATA_PARAM_PTR)&counter_null_data, 0, NULL },
+          64,
+          64,
+          { 0x81, 0x39, 0x12, 0xc2, 0xf9, 0x31, 0x24, 0x7c,
+            0x71, 0x12, 0x97, 0x08, 0x82, 0x76, 0x83, 0x55,
+            0x8c, 0x82, 0xf3, 0x09, 0xd6, 0x1b, 0x7a, 0xa2,
+            0x6e, 0x71, 0x6b, 0xad, 0x46, 0x57, 0x60, 0x89,
+            0x38, 0xcf, 0x63, 0xfa, 0xf4, 0x38, 0x27, 0xef,
+            0xf0, 0xaf, 0x75, 0x4e, 0xc2, 0xe0, 0x31, 0xdb,
+            0x59, 0x7d, 0x19, 0xc9, 0x6d, 0xbb, 0xed, 0x95,
+            0xaf, 0x3e, 0xd8, 0x33, 0x76, 0xab, 0xec, 0xfa },
+      },
+      {
+          CKM_SP800_108_DOUBLE_PIPELINE_KDF,
+          { CKM_AES_CMAC, 1, (CK_PRF_DATA_PARAM_PTR)&counter_null_data, 0, NULL },
+          16,
+          64,
+          { 0x3e, 0xa8, 0xbf, 0x77, 0x84, 0x90, 0xb0, 0x3a,
+            0x89, 0x16, 0x32, 0x01, 0x92, 0xd3, 0x1f, 0x1b,
+            0xc1, 0x06, 0xc5, 0x32, 0x62, 0x03, 0x50, 0x16,
+            0x3b, 0xb9, 0xa7, 0xdc, 0xb5, 0x68, 0x6a, 0xbb,
+            0xbb, 0x7d, 0x63, 0x69, 0x24, 0x6e, 0x09, 0xd6,
+            0x6f, 0x80, 0x57, 0x65, 0xc5, 0x62, 0x33, 0x96,
+            0x69, 0xe6, 0xab, 0x65, 0x36, 0xd0, 0xe2, 0x5c,
+            0xd7, 0xbd, 0xe4, 0x68, 0x13, 0xd6, 0xb1, 0x46 },
+      },
+      {
+          CKM_SP800_108_DOUBLE_PIPELINE_KDF,
+          { CKM_SHA256_HMAC, 1, (CK_PRF_DATA_PARAM_PTR)&counter_null_data, 0, NULL },
+          32,
+          64,
+          { 0xeb, 0x28, 0xd9, 0x2c, 0x19, 0x33, 0xb9, 0x2a,
+            0xf9, 0xac, 0x85, 0xbd, 0xf4, 0xdb, 0xfa, 0x88,
+            0x73, 0xf4, 0x36, 0x08, 0xdb, 0xfe, 0x13, 0xd1,
+            0x5a, 0xec, 0x7b, 0x68, 0x13, 0x53, 0xb3, 0xd1,
+            0x31, 0xf2, 0x83, 0xae, 0x9f, 0x75, 0x47, 0xb6,
+            0x6d, 0x3c, 0x20, 0x16, 0x47, 0x9c, 0x27, 0x66,
+            0xec, 0xa9, 0xdf, 0x0c, 0xda, 0x2a, 0xf9, 0xf4,
+            0x55, 0x74, 0xde, 0x9d, 0x3f, 0xe3, 0x5e, 0x14 },
+      },
+      {
+          CKM_SP800_108_DOUBLE_PIPELINE_KDF,
+          { CKM_SHA384_HMAC, 1, (CK_PRF_DATA_PARAM_PTR)&counter_null_data, 0, NULL },
+          48,
+          64,
+          { 0xa5, 0xca, 0x32, 0x40, 0x00, 0x93, 0xb2, 0xcc,
+            0x78, 0x3c, 0xa6, 0xc4, 0xaf, 0xa8, 0xb3, 0xd0,
+            0xa4, 0x6b, 0xb5, 0x31, 0x35, 0x87, 0x33, 0xa2,
+            0x6a, 0x6b, 0xe1, 0xff, 0xea, 0x1d, 0x6e, 0x9e,
+            0x0b, 0xde, 0x8b, 0x92, 0x15, 0xd6, 0x56, 0x2f,
+            0xb6, 0x1a, 0xd7, 0xd2, 0x01, 0x3e, 0x28, 0x2e,
+            0xfa, 0x84, 0x3c, 0xc0, 0xe8, 0xbe, 0x94, 0xc0,
+            0x06, 0xbd, 0xbf, 0x87, 0x1f, 0xb8, 0x64, 0xc2 },
+      },
+      {
+          CKM_SP800_108_DOUBLE_PIPELINE_KDF,
+          { CKM_SHA512_HMAC, 1, (CK_PRF_DATA_PARAM_PTR)&counter_null_data, 0, NULL },
+          64,
+          64,
+          { 0x3f, 0xd9, 0x4e, 0x80, 0x58, 0x21, 0xc8, 0xea,
+            0x22, 0x17, 0xcf, 0x7d, 0xce, 0xfd, 0xec, 0x03,
+            0xb9, 0xe4, 0xa2, 0xf7, 0xc0, 0xf1, 0x68, 0x81,
+            0x53, 0x71, 0xb7, 0x42, 0x14, 0x4e, 0x5b, 0x09,
+            0x05, 0x31, 0xb9, 0x27, 0x18, 0x2d, 0x23, 0xf8,
+            0x9c, 0x3d, 0x4e, 0xd0, 0xdd, 0xf3, 0x1e, 0x4b,
+            0xf2, 0xf9, 0x1a, 0x5d, 0x00, 0x66, 0x22, 0x83,
+            0xae, 0x3c, 0x53, 0xd2, 0x54, 0x4b, 0x06, 0x4c },
+      },
+#endif
+      {
+          CKM_SP800_108_COUNTER_KDF,
+          { CKM_SHA256_HMAC, 1, (CK_PRF_DATA_PARAM_PTR)&counter_32_data, 0, NULL },
+          32,
+          64,
+          { 0xfb, 0x2b, 0xb5, 0xde, 0xce, 0x5a, 0x2b, 0xdc,
+            0x25, 0x8f, 0x54, 0x17, 0x4b, 0x5a, 0xa7, 0x90,
+            0x64, 0x36, 0xeb, 0x43, 0x1f, 0x1d, 0xf9, 0x23,
+            0xb2, 0x22, 0x29, 0xa0, 0xfa, 0x2e, 0x21, 0xb6,
+            0xb7, 0xfb, 0x27, 0x0a, 0x1c, 0xa6, 0x58, 0x43,
+            0xa1, 0x16, 0x44, 0x29, 0x4b, 0x1c, 0xb3, 0x72,
+            0xd5, 0x98, 0x9d, 0x27, 0xd5, 0x75, 0x25, 0xbf,
+            0x23, 0x61, 0x40, 0x48, 0xbb, 0x0b, 0x49, 0x8e },
+      }
+    };
+
+SECStatus
+sftk_fips_SP800_108_PowerUpSelfTests(void)
+{
+    int i;
+    CK_RV crv;
+
+    const unsigned char prf_key[] = {
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+        0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+        0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+        0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+        0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+        0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78
+    };
+    for (i = 0; i < PR_ARRAY_SIZE(sftk_SP800_Tests); i++) {
+        const struct sftk_SP800_Test_struct *test = &sftk_SP800_Tests[i];
+        unsigned char *output_buffer;
+        size_t buffer_length;
+        unsigned int mac_size;
+
+        crv = kbkdf_RawDispatch(test->mech, &test->kdf_params,
+                                prf_key, test->expected_mac_size,
+                                NULL, prf_key, test->expected_mac_size,
+                                &output_buffer, &buffer_length, &mac_size,
+                                test->ret_key_length);
+        if (crv != CKR_OK) {
+            PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+            return SECFailure;
+        }
+        if ((mac_size != test->expected_mac_size) ||
+            (buffer_length != test->ret_key_length) ||
+            (output_buffer == NULL) ||
+            (PORT_Memcmp(output_buffer, test->expected_key_bytes, buffer_length) != 0)) {
+            PORT_ZFree(output_buffer, buffer_length);
+            return SECFailure;
+        }
+        PORT_ZFree(output_buffer, buffer_length);
+    }
+    return SECSuccess;
+}
diff --git a/lib/softoken/lowpbe.c b/lib/softoken/lowpbe.c
--- a/lib/softoken/lowpbe.c
+++ b/lib/softoken/lowpbe.c
@@ -1732,8 +1732,72 @@ nsspkcs5_CreateAlgorithmID(PLArenaPool *
         SECOID_DestroyAlgorithmID(ret_algid, PR_TRUE);
         ret_algid = NULL;
     }
 
 loser:
 
     return ret_algid;
 }
+
+#define TEST_KEY "pbkdf test key"
+SECStatus
+sftk_fips_pbkdf_PowerUpSelfTests(void)
+{
+    SECItem *result;
+    SECItem inKey;
+    NSSPKCS5PBEParameter pbe_params;
+    unsigned char iteration_count = 5;
+    unsigned char keyLen = 64;
+    char *inKeyData = TEST_KEY;
+    static const unsigned char saltData[] =
+        { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
+    static const unsigned char pbkdf_known_answer[] = {
+        0x31, 0xf0, 0xe5, 0x39, 0x9f, 0x39, 0xb9, 0x29,
+        0x68, 0xac, 0xf2, 0xe9, 0x53, 0x9b, 0xb4, 0x9c,
+        0x28, 0x59, 0x8b, 0x5c, 0xd8, 0xd4, 0x02, 0x37,
+        0x18, 0x22, 0xc1, 0x92, 0xd0, 0xfa, 0x72, 0x90,
+        0x2c, 0x8d, 0x19, 0xd4, 0x56, 0xfb, 0x16, 0xfa,
+        0x8d, 0x5c, 0x06, 0x33, 0xd1, 0x5f, 0x17, 0xb1,
+        0x22, 0xd9, 0x9c, 0xaf, 0x5e, 0x3f, 0xf3, 0x66,
+        0xc6, 0x14, 0xfe, 0x83, 0xfa, 0x1a, 0x2a, 0xc5
+    };
+
+    sftk_PBELockInit();
+
+    inKey.data = (unsigned char *)inKeyData;
+    inKey.len = sizeof(TEST_KEY) - 1;
+
+    pbe_params.salt.data = (unsigned char *)saltData;
+    pbe_params.salt.len = sizeof(saltData);
+    /* the interation and keyLength are used as intermediate
+     * values when decoding the Algorithm ID, set them for completeness,
+     * but they are not used */
+    pbe_params.iteration.data = &iteration_count;
+    pbe_params.iteration.len = 1;
+    pbe_params.keyLength.data = &keyLen;
+    pbe_params.keyLength.len = 1;
+    /* pkcs5v2 stores the key in the AlgorithmID, so we don't need to
+     * generate it here */
+    pbe_params.ivLen = 0;
+    pbe_params.ivData = NULL;
+    /* keyID is only used by pkcs12 extensions to pkcs5v1 */
+    pbe_params.keyID = pbeBitGenCipherKey;
+    /* Algorithm is used by the decryption code after get get our key */
+    pbe_params.encAlg = SEC_OID_AES_256_CBC;
+    /* these are the fields actually used in nsspkcs5_ComputeKeyAndIV
+     * for NSSPKCS5_PBKDF2 */
+    pbe_params.iter = iteration_count;
+    pbe_params.keyLen = keyLen;
+    pbe_params.hashType = HASH_AlgSHA256;
+    pbe_params.pbeType = NSSPKCS5_PBKDF2;
+    pbe_params.is2KeyDES = PR_FALSE;
+
+    result = nsspkcs5_ComputeKeyAndIV(&pbe_params, &inKey, NULL, PR_FALSE);
+    if ((result == NULL) || (result->len != sizeof(pbkdf_known_answer)) ||
+        (PORT_Memcmp(result->data, pbkdf_known_answer, sizeof(pbkdf_known_answer)) != 0)) {
+        SECITEM_FreeItem(result, PR_TRUE);
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    SECITEM_FreeItem(result, PR_TRUE);
+    return SECSuccess;
+}
diff --git a/lib/softoken/lowpbe.h b/lib/softoken/lowpbe.h
--- a/lib/softoken/lowpbe.h
+++ b/lib/softoken/lowpbe.h
@@ -98,11 +98,15 @@ nsspkcs5_ComputeKeyAndIV(NSSPKCS5PBEPara
 
 /* Destroys PBE parameter */
 extern void
 nsspkcs5_DestroyPBEParameter(NSSPKCS5PBEParameter *param);
 
 HASH_HashType HASH_FromHMACOid(SECOidTag oid);
 SECOidTag HASH_HMACOidFromHash(HASH_HashType);
 
+/* fips selftest */
+extern SECStatus
+sftk_fips_pbkdf_PowerUpSelfTests(void);
+
 SEC_END_PROTOS
 
 #endif
diff --git a/lib/softoken/pkcs11c.c b/lib/softoken/pkcs11c.c
--- a/lib/softoken/pkcs11c.c
+++ b/lib/softoken/pkcs11c.c
@@ -6909,32 +6909,32 @@ sftk_DeriveEncrypt(SFTKCipher encrypt, v
     }
 
     crv = sftk_forceAttribute(key, CKA_VALUE, tmpdata, keySize);
     return crv;
 }
 
 CK_RV
 sftk_HKDF(CK_HKDF_PARAMS_PTR params, CK_SESSION_HANDLE hSession,
-          SFTKObject *sourceKey, unsigned char *sourceKeyBytes,
-          int sourceKeyLen, SFTKObject *key, int keySize,
-          PRBool canBeData, PRBool isFIPS)
+          SFTKObject *sourceKey, const unsigned char *sourceKeyBytes,
+          int sourceKeyLen, SFTKObject *key, unsigned char *outKeyBytes,
+          int keySize, PRBool canBeData, PRBool isFIPS)
 {
     SFTKSession *session;
     SFTKAttribute *saltKey_att = NULL;
     const SECHashObject *rawHash;
     unsigned hashLen;
     unsigned genLen = 0;
     unsigned char hashbuf[HASH_LENGTH_MAX];
     unsigned char keyBlock[9 * SFTK_MAX_MAC_LENGTH];
     unsigned char *keyBlockAlloc = NULL;    /* allocated keyBlock */
     unsigned char *keyBlockData = keyBlock; /* pointer to current keyBlock */
-    unsigned char *prk;                     /* psuedo-random key */
+    const unsigned char *prk;               /* psuedo-random key */
     CK_ULONG prkLen;
-    unsigned char *okm; /* output keying material */
+    const unsigned char *okm; /* output keying material */
     HASH_HashType hashType = GetHashTypeFromMechanism(params->prfHashMechanism);
     SFTKObject *saltKey = NULL;
     CK_RV crv = CKR_OK;
 
     /* Spec says it should be the base hash, but also accept the HMAC */
     if (hashType == HASH_AlgNULL) {
         hashType = sftk_HMACMechanismToHash(params->prfHashMechanism);
     }
@@ -6949,19 +6949,24 @@ sftk_HKDF(CK_HKDF_PARAMS_PTR params, CK_
         (params->bExpand && params->ulInfoLen > 0 && !params->pInfo)) {
         return CKR_MECHANISM_PARAM_INVALID;
     }
     if ((params->bExpand && keySize == 0) ||
         (!params->bExpand && keySize > hashLen) ||
         (params->bExpand && keySize > 255 * hashLen)) {
         return CKR_TEMPLATE_INCONSISTENT;
     }
-    crv = sftk_DeriveSensitiveCheck(sourceKey, key, canBeData);
-    if (crv != CKR_OK)
-        return crv;
+
+    /* sourceKey is NULL if we are called from the POST, skip the
+     * sensitiveCheck */
+    if (sourceKey != NULL) {
+        crv = sftk_DeriveSensitiveCheck(sourceKey, key, canBeData);
+        if (crv != CKR_OK)
+            return crv;
+    }
 
     /* HKDF-Extract(salt, base key value) */
     if (params->bExtract) {
         CK_BYTE *salt;
         CK_ULONG saltLen;
         HMACContext *hmac;
         unsigned int bufLen;
 
@@ -7067,19 +7072,25 @@ sftk_HKDF(CK_HKDF_PARAMS_PTR params, CK_
             HMAC_Update(hmac, &bi, 1);
             HMAC_Finish(hmac, &keyBlockData[(bi - 1) * hashLen], &len,
                         hashLen);
             PORT_Assert(len == hashLen);
         }
         HMAC_Destroy(hmac, PR_TRUE);
         okm = &keyBlockData[0];
     }
-    /* key material = prk */
-    crv = sftk_forceAttribute(key, CKA_VALUE, okm, keySize);
-    PORT_Memset(okm, 0, genLen);
+    /* key material = okm */
+    crv = CKR_OK;
+    if (key) {
+        crv = sftk_forceAttribute(key, CKA_VALUE, okm, keySize);
+    } else {
+        PORT_Assert(outKeyBytes != NULL);
+        PORT_Memcpy(outKeyBytes, okm, keySize);
+    }
+    PORT_Memset(keyBlockData, 0, genLen);
     PORT_Memset(hashbuf, 0, sizeof(hashbuf));
     PORT_Free(keyBlockAlloc);
     return CKR_OK;
 }
 
 /*
  * SSL Key generation given pre master secret
  */
@@ -8625,28 +8636,28 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession
             hkdfParams.ulSaltLen = params->ulSaltLen;
             hkdfParams.hSaltKey = CK_INVALID_HANDLE;
             hkdfParams.pInfo = params->pInfo;
             hkdfParams.ulInfoLen = params->ulInfoLen;
             hkdfParams.prfHashMechanism = hashMech;
 
             crv = sftk_HKDF(&hkdfParams, hSession, sourceKey,
                             att->attrib.pValue, att->attrib.ulValueLen,
-                            key, keySize, PR_FALSE, isFIPS);
+                            key, NULL, keySize, PR_FALSE, isFIPS);
         } break;
         case CKM_HKDF_DERIVE:
         case CKM_HKDF_DATA: /* only difference is the class of key */
             if ((pMechanism->pParameter == NULL) ||
                 (pMechanism->ulParameterLen != sizeof(CK_HKDF_PARAMS))) {
                 crv = CKR_MECHANISM_PARAM_INVALID;
                 break;
             }
             crv = sftk_HKDF((CK_HKDF_PARAMS_PTR)pMechanism->pParameter,
                             hSession, sourceKey, att->attrib.pValue,
-                            att->attrib.ulValueLen, key, keySize, PR_TRUE,
+                            att->attrib.ulValueLen, key, NULL, keySize, PR_TRUE,
                             isFIPS);
             break;
         case CKM_NSS_JPAKE_ROUND2_SHA1:
             hashType = HASH_AlgSHA1;
             goto jpake2;
         case CKM_NSS_JPAKE_ROUND2_SHA256:
             hashType = HASH_AlgSHA256;
             goto jpake2;
diff --git a/lib/softoken/pkcs11i.h b/lib/softoken/pkcs11i.h
--- a/lib/softoken/pkcs11i.h
+++ b/lib/softoken/pkcs11i.h
@@ -895,28 +895,37 @@ sftk_TLSPRFInit(SFTKSessionContext *cont
                 unsigned int out_len);
 
 /* PKCS#11 MAC implementation. See sftk_MACCtxStr declaration above for
  * calling semantics for these functions. */
 HASH_HashType sftk_HMACMechanismToHash(CK_MECHANISM_TYPE mech);
 CK_RV sftk_MAC_Create(CK_MECHANISM_TYPE mech, SFTKObject *key, sftk_MACCtx **ret_ctx);
 CK_RV sftk_MAC_Init(sftk_MACCtx *ctx, CK_MECHANISM_TYPE mech, SFTKObject *key);
 CK_RV sftk_MAC_InitRaw(sftk_MACCtx *ctx, CK_MECHANISM_TYPE mech, const unsigned char *key, unsigned int key_len, PRBool isFIPS);
-CK_RV sftk_MAC_Update(sftk_MACCtx *ctx, CK_BYTE_PTR data, unsigned int data_len);
+CK_RV sftk_MAC_Update(sftk_MACCtx *ctx, const CK_BYTE *data, unsigned int data_len);
 CK_RV sftk_MAC_Finish(sftk_MACCtx *ctx, CK_BYTE_PTR result, unsigned int *result_len, unsigned int max_result_len);
 CK_RV sftk_MAC_Reset(sftk_MACCtx *ctx);
 void sftk_MAC_Destroy(sftk_MACCtx *ctx, PRBool free_it);
 
 /* constant time helpers */
 unsigned int sftk_CKRVToMask(CK_RV rv);
 CK_RV sftk_CheckCBCPadding(CK_BYTE_PTR pBuf, unsigned int bufLen,
                            unsigned int blockSize, unsigned int *outPadSize);
 
 /* NIST 800-108 (kbkdf.c) implementations */
 extern CK_RV kbkdf_Dispatch(CK_MECHANISM_TYPE mech, CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, SFTKObject *base_key, SFTKObject *ret_key, CK_ULONG keySize);
+extern SECStatus sftk_fips_SP800_108_PowerUpSelfTests(void);
+
+/* export the HKDF function for use in PowerupSelfTests */
+CK_RV sftk_HKDF(CK_HKDF_PARAMS_PTR params, CK_SESSION_HANDLE hSession,
+                SFTKObject *sourceKey, const unsigned char *sourceKeyBytes,
+                int sourceKeyLen, SFTKObject *key,
+                unsigned char *outKeyBytes, int keySize,
+                PRBool canBeData, PRBool isFIPS);
+
 char **NSC_ModuleDBFunc(unsigned long function, char *parameters, void *args);
 
 /* dh verify functions */
 /* verify that dhPrime matches one of our known primes, and if so return
  * it's subprime value */
 const SECItem *sftk_VerifyDH_Prime(SECItem *dhPrime);
 /* check if dhSubPrime claims dhPrime is a safe prime. */
 SECStatus sftk_IsSafePrime(SECItem *dhPrime, SECItem *dhSubPrime, PRBool *isSafe);
diff --git a/lib/softoken/sftkhmac.c b/lib/softoken/sftkhmac.c
--- a/lib/softoken/sftkhmac.c
+++ b/lib/softoken/sftkhmac.c
@@ -350,17 +350,17 @@ sftk_MAC_Reset(sftk_MACCtx *ctx)
             PR_ASSERT(PR_FALSE);
             return CKR_FUNCTION_FAILED;
     }
 
     return CKR_OK;
 }
 
 CK_RV
-sftk_MAC_Update(sftk_MACCtx *ctx, CK_BYTE_PTR data, unsigned int data_len)
+sftk_MAC_Update(sftk_MACCtx *ctx, const CK_BYTE *data, unsigned int data_len)
 {
     switch (ctx->mech) {
         case CKM_MD2_HMAC:
         case CKM_MD5_HMAC:
         case CKM_SHA_1_HMAC:
         case CKM_SHA224_HMAC:
         case CKM_SHA256_HMAC:
         case CKM_SHA384_HMAC:
diff --git a/lib/softoken/sftkike.c b/lib/softoken/sftkike.c
--- a/lib/softoken/sftkike.c
+++ b/lib/softoken/sftkike.c
@@ -850,20 +850,22 @@ fail:
  *       T2 = prf(K, T1 | S | 0x02)
  *       T3 = prf(K, T3 | S | 0x03)
  *       T4 = prf(K, T4 | S | 0x04)
  *            .
  *       Tn = prf(K, T(n-1) | n)
  * K = inKey, S = seedKey | seedData
  */
 
-CK_RV
-sftk_ike_prf_plus(CK_SESSION_HANDLE hSession, const SFTKAttribute *inKey,
-                  const CK_NSS_IKE_PRF_PLUS_DERIVE_PARAMS *params, SFTKObject *outKey,
-                  unsigned int keySize)
+static CK_RV
+sftk_ike_prf_plus_raw(CK_SESSION_HANDLE hSession,
+                      const unsigned char *inKeyData, CK_ULONG inKeyLen,
+                      const CK_NSS_IKE_PRF_PLUS_DERIVE_PARAMS *params,
+                      unsigned char **outKeyDataPtr, unsigned int *outKeySizePtr,
+                      unsigned int keySize)
 {
     SFTKAttribute *seedValue = NULL;
     SFTKObject *seedKeyObj = NULL;
     unsigned char *outKeyData = NULL;
     unsigned int outKeySize;
     unsigned char *thisKey;
     unsigned char *lastKey = NULL;
     unsigned char currentByte = 0;
@@ -919,18 +921,17 @@ sftk_ike_prf_plus(CK_SESSION_HANDLE hSes
         /* if currentByte is 255, we'll overflow when we increment it below.
          * This can only happen if keysize > 255*macSize. In that case
          * the application has asked for too much key material, so return
          * an error */
         if (currentByte == 255) {
             crv = CKR_KEY_SIZE_RANGE;
             goto fail;
         }
-        crv = prf_init(&context, inKey->attrib.pValue,
-                       inKey->attrib.ulValueLen);
+        crv = prf_init(&context, inKeyData, inKeyLen);
         if (crv != CKR_OK) {
             goto fail;
         }
 
         if (lastKey) {
             crv = prf_update(&context, lastKey, macSize);
             if (crv != CKR_OK) {
                 goto fail;
@@ -959,31 +960,57 @@ sftk_ike_prf_plus(CK_SESSION_HANDLE hSes
         }
         crv = prf_final(&context, thisKey, macSize);
         if (crv != CKR_OK) {
             goto fail;
         }
         lastKey = thisKey;
         thisKey += macSize;
     }
-    crv = sftk_forceAttribute(outKey, CKA_VALUE, outKeyData, keySize);
+    *outKeyDataPtr = outKeyData;
+    *outKeySizePtr = outKeySize;
+    outKeyData = NULL; /* don't free it here, our caller will free it */
 fail:
     if (outKeyData) {
         PORT_ZFree(outKeyData, outKeySize);
     }
     if (seedValue) {
         sftk_FreeAttribute(seedValue);
     }
     if (seedKeyObj) {
         sftk_FreeObject(seedKeyObj);
     }
     prf_free(&context);
     return crv;
 }
 
+/*
+ * ike prf + with code to deliever results tosoftoken objects.
+ */
+CK_RV
+sftk_ike_prf_plus(CK_SESSION_HANDLE hSession, const SFTKAttribute *inKey,
+                  const CK_NSS_IKE_PRF_PLUS_DERIVE_PARAMS *params, SFTKObject *outKey,
+                  unsigned int keySize)
+{
+    unsigned char *outKeyData = NULL;
+    unsigned int outKeySize;
+    CK_RV crv;
+
+    crv = sftk_ike_prf_plus_raw(hSession, inKey->attrib.pValue,
+                                inKey->attrib.ulValueLen, params,
+                                &outKeyData, &outKeySize, keySize);
+    if (crv != CKR_OK) {
+        return crv;
+    }
+
+    crv = sftk_forceAttribute(outKey, CKA_VALUE, outKeyData, keySize);
+    PORT_ZFree(outKeyData, outKeySize);
+    return crv;
+}
+
 /* sftk_aes_xcbc_new_keys:
  *
  * aes xcbc creates 3 new keys from the input key. The first key will be the
  * base key of the underlying cbc. The sign code hooks directly into encrypt
  * so we'll have to create a full PKCS #11 key with handle for that key. The
  * caller needs to delete the key when it's through setting up the context.
  *
  * The other two keys will be stored in the sign context until we need them
@@ -1289,17 +1316,31 @@ sftk_fips_IKE_PowerUpSelfTests(void)
         0x58, 0xd4, 0x04, 0x18, 0xde, 0xa9, 0x80, 0x96,
         0xe5, 0x19, 0xbc, 0x78, 0x41, 0xe3, 0xdb, 0x3d,
         0xd9, 0x36, 0x58, 0xd1, 0x18, 0xc3, 0xe8, 0x3b,
         0x50, 0x2f, 0x39, 0x8e, 0xcb, 0x13, 0x61, 0xec,
         0x77, 0xd3, 0x8a, 0x88, 0x55, 0xef, 0xff, 0x40,
         0x7f, 0x6f, 0x77, 0x2e, 0x5d, 0x65, 0xb5, 0x8e,
         0xb1, 0x13, 0x40, 0x96, 0xe8, 0x47, 0x8d, 0x2b
     };
+    static const PRUint8 ike_known_sha256_prf_plus[] = {
+        0xe6, 0xf1, 0x9b, 0x4a, 0x02, 0xe9, 0x73, 0x72,
+        0x93, 0x9f, 0xdb, 0x46, 0x1d, 0xb1, 0x49, 0xcb,
+        0x53, 0x08, 0x98, 0x3d, 0x41, 0x36, 0xfa, 0x8b,
+        0x47, 0x04, 0x49, 0x11, 0x0d, 0x6e, 0x96, 0x1d,
+        0xab, 0xbe, 0x94, 0x28, 0xa0, 0xb7, 0x9c, 0xa3,
+        0x29, 0xe1, 0x40, 0xf8, 0xf8, 0x88, 0xb9, 0xb5,
+        0x40, 0xd4, 0x54, 0x4d, 0x25, 0xab, 0x94, 0xd4,
+        0x98, 0xd8, 0x00, 0xbf, 0x6f, 0xef, 0xe8, 0x39
+    };
     SECStatus rv;
+    CK_RV crv;
+    unsigned char *outKeyData = NULL;
+    unsigned int outKeySize;
+    CK_NSS_IKE_PRF_PLUS_DERIVE_PARAMS ike_params;
 
     rv = prf_test(CKM_AES_XCBC_MAC,
                   ike_xcbc_known_key, sizeof(ike_xcbc_known_key),
                   ike_xcbc_known_plain_text, sizeof(ike_xcbc_known_plain_text),
                   ike_xcbc_known_mac, sizeof(ike_xcbc_known_mac));
     if (rv != SECSuccess)
         return rv;
     rv = prf_test(CKM_AES_XCBC_MAC,
@@ -1340,10 +1381,28 @@ sftk_fips_IKE_PowerUpSelfTests(void)
                   ike_sha384_known_mac, sizeof(ike_sha384_known_mac));
     if (rv != SECSuccess)
         return rv;
     rv = prf_test(CKM_SHA512_HMAC,
                   ike_sha512_known_key, sizeof(ike_sha512_known_key),
                   ike_sha512_known_plain_text,
                   sizeof(ike_sha512_known_plain_text),
                   ike_sha512_known_mac, sizeof(ike_sha512_known_mac));
+
+    ike_params.prfMechanism = CKM_SHA256_HMAC;
+    ike_params.bHasSeedKey = PR_FALSE;
+    ike_params.hSeedKey = CK_INVALID_HANDLE;
+    ike_params.pSeedData = (CK_BYTE_PTR)ike_sha256_known_plain_text;
+    ike_params.ulSeedDataLen = sizeof(ike_sha256_known_plain_text);
+    crv = sftk_ike_prf_plus_raw(CK_INVALID_HANDLE, ike_sha256_known_key,
+                                sizeof(ike_sha256_known_key), &ike_params,
+                                &outKeyData, &outKeySize, 64);
+    if ((crv != CKR_OK) ||
+        (outKeySize != sizeof(ike_known_sha256_prf_plus)) ||
+        (PORT_Memcmp(outKeyData, ike_known_sha256_prf_plus,
+                     sizeof(ike_known_sha256_prf_plus)) != 0)) {
+        PORT_ZFree(outKeyData, outKeySize);
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    PORT_ZFree(outKeyData, outKeySize);
     return rv;
 }
