# HG changeset patch
# User Robert Relyea <rrelyea@redhat.com>
# Date 1598309191 25200
# Node ID 5dca54fe61c2916e540129590a40772d5be89a1d
# Parent  0e1b5c711cb9d810f8958857fafb5d0349d3c56f
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 and revised for Suse by <hpj@suse.com>.

diff --git a/nss/lib/freebl/fipsfreebl.c b/nss/lib/freebl/fipsfreebl.c
index 4dc9f47..042fbe4 100644
--- a/nss/lib/freebl/fipsfreebl.c
+++ b/nss/lib/freebl/fipsfreebl.c
@@ -17,6 +17,7 @@
 #include "prtypes.h"
 #include "secitem.h"
 #include "pkcs11t.h"
+#include "cmac.h"
 
 #include "ec.h" /* Required for EC */
 
@@ -106,6 +107,7 @@ BOOL WINAPI DllMain(
 #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 */
@@ -323,6 +325,11 @@ freebl_fips_AES_PowerUpSelfTest(int aes_key_size)
         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,
@@ -341,6 +348,11 @@ freebl_fips_AES_PowerUpSelfTest(int aes_key_size)
         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,
@@ -359,6 +371,11 @@ freebl_fips_AES_PowerUpSelfTest(int aes_key_size)
         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,
@@ -387,6 +404,9 @@ freebl_fips_AES_PowerUpSelfTest(int aes_key_size)
     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;
 
@@ -394,6 +414,7 @@ freebl_fips_AES_PowerUpSelfTest(int aes_key_size)
     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;
@@ -583,6 +604,44 @@ freebl_fips_AES_PowerUpSelfTest(int aes_key_size)
         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. */
     /********************************/
@@ -825,6 +884,170 @@ freebl_fips_HMAC_PowerUpSelfTest(void)
     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)
 {
@@ -1956,6 +2179,12 @@ freebl_fipsPowerUpSelfTest(unsigned int tests)
         /* 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;
 
diff --git a/nss/lib/softoken/fipstest.c b/nss/lib/softoken/fipstest.c
index 3effcdc..336b666 100644
--- a/nss/lib/softoken/fipstest.c
+++ b/nss/lib/softoken/fipstest.c
@@ -13,6 +13,7 @@
 #include "secoid.h"
 #include "secerr.h"
 #include "pkcs11i.h"
+#include "lowpbe.h"
 
 /*
  * different platforms have different ways of calling and initial entry point
@@ -961,6 +962,12 @@ sftk_startup_tests(void)
     if (rv != SECSuccess) {
         return;
     }
+
+    rv = sftk_fips_pbkdf_PowerUpSelfTests();
+    if (rv != SECSuccess) {
+        return;
+    }
+
     sftk_self_tests_success = PR_TRUE;
 }
 
diff --git a/nss/lib/softoken/lowpbe.c b/nss/lib/softoken/lowpbe.c
index 4a101c6..9a613c2 100644
--- a/nss/lib/softoken/lowpbe.c
+++ b/nss/lib/softoken/lowpbe.c
@@ -1364,3 +1364,65 @@ 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
+    };
+
+    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/nss/lib/softoken/lowpbe.h b/nss/lib/softoken/lowpbe.h
index 2080138..97fb8aa 100644
--- a/nss/lib/softoken/lowpbe.h
+++ b/nss/lib/softoken/lowpbe.h
@@ -102,6 +102,10 @@ nsspkcs5_DestroyPBEParameter(NSSPKCS5PBEParameter *param);
 
 HASH_HashType HASH_FromHMACOid(SECOidTag oid);
 
+/* fips selftest */
+extern SECStatus
+sftk_fips_pbkdf_PowerUpSelfTests(void);
+
 SEC_END_PROTOS
 
 #endif
diff --git a/nss/lib/softoken/sftkike.c b/nss/lib/softoken/sftkike.c
index 7d5370b..7525c8f 100644
--- a/nss/lib/softoken/sftkike.c
+++ b/nss/lib/softoken/sftkike.c
@@ -800,10 +800,12 @@ fail:
  * 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;
@@ -869,8 +871,7 @@ sftk_ike_prf_plus(CK_SESSION_HANDLE hSession, const SFTKAttribute *inKey,
             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;
         }
@@ -909,7 +910,9 @@ sftk_ike_prf_plus(CK_SESSION_HANDLE hSession, const SFTKAttribute *inKey,
         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);
@@ -924,6 +927,30 @@ fail:
     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
@@ -1239,7 +1266,21 @@ sftk_fips_IKE_PowerUpSelfTests(void)
         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),
@@ -1290,5 +1331,23 @@ sftk_fips_IKE_PowerUpSelfTests(void)
                   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;
 }
