#include "pkcs11i.h"
#include "blapi.h"
#include "secerr.h"
#include "softoken.h"

/* Overview:
 *
 * This file contains implementations of the three KDFs from NIST SP800-108
 * "Recommendation for Key Derivation Using Pseudorandom Functions":
 *
 *  1. KDF in Counter Mode (section 5.1)
 *  2. KDF in Feedback Mode (section 5.2)
 *  3. KDF in Double-Pipeline Iteration Mode (section 5.3)
 *
 * These KDFs are a form of negotiable building blocks for KDFs: protocol
 * designers can choose various fields, their endianness, and the underlying
 * PRF. These constructs are generic enough to handle creation of arbitrary,
 * (but known ahead of time) length outputs.
 *
 * The families of PRFs described here are used, among other places, in
 * Kerberos and GlobalPlatform's Secure Channel Protocol 03. The PKCS#11 v3.0
 * design for this KDF facilitates a wide range of uses.
 *
 * Implementation Details:
 *
 * We reuse the new sftk_MACCtx for handling the underlying MACing; with a few
 * safe restrictions, we can reuse whatever it gives us to use as a PRF.
 *
 * We implement the core of the KDF in the *Raw(...) version of the function
 * call. The PKCS#11 key handling happens in the non-Raw version. This means
 * we need a single large allocation upfront (large enough to store the entire
 * key stream), but means we can share key parsing logic and enable the
 * creation of data objects.
 */

/* [ section: #define's ] */

#define VALID_CK_BOOL(x) ((x) == CK_TRUE || (x) == CK_FALSE)
#define IS_COUNTER(_mech) ((_mech) == CKM_SP800_108_COUNTER_KDF || (_mech) == CKM_NSS_SP800_108_COUNTER_KDF_DERIVE_DATA)
#define DOES_DERIVE_DATA(_mech) ((_mech) == CKM_NSS_SP800_108_COUNTER_KDF_DERIVE_DATA || (_mech) == CKM_NSS_SP800_108_FEEDBACK_KDF_DERIVE_DATA || (_mech) == CKM_NSS_SP800_108_DOUBLE_PIPELINE_KDF_DERIVE_DATA)

/* [ section: parameter validation ] */

static CK_RV
kbkdf_LoadParameters(CK_MECHANISM_TYPE mech, CK_MECHANISM_PTR pMechanism, CK_SP800_108_KDF_PARAMS_PTR kdf_params, CK_BYTE_PTR *initial_value, CK_ULONG_PTR initial_value_length)
{
    /* This function loads the parameters for the given mechanism into the
     * specified kdf_params, splitting off the IV if present. In PKCS#11 v3.0,
     * CK_SP800_108_FEEDBACK_KDF_PARAMS and CK_SP800_108_KDF_PARAMS have
     * different ordering of internal parameters, which means that it isn't
     * easy to reuse feedback parameters in the same functions as non-feedback
     * parameters. Rather than duplicating the logic, split out the only
     * Feedback-specific data (the IV) into a separate argument and repack it
     * into the passed kdf_params struct instead. */
    PR_ASSERT(pMechanism != NULL && kdf_params != NULL && initial_value != NULL && initial_value_length != NULL);

    CK_SP800_108_KDF_PARAMS_PTR in_params;
    CK_SP800_108_FEEDBACK_KDF_PARAMS_PTR feedback_params;

    if (mech == CKM_SP800_108_FEEDBACK_KDF || mech == CKM_NSS_SP800_108_FEEDBACK_KDF_DERIVE_DATA) {
        if (pMechanism->ulParameterLen != sizeof(CK_SP800_108_FEEDBACK_KDF_PARAMS)) {
            return CKR_MECHANISM_PARAM_INVALID;
        }

        feedback_params = (CK_SP800_108_FEEDBACK_KDF_PARAMS *)pMechanism->pParameter;

        if (feedback_params->pIV == NULL && feedback_params->ulIVLen > 0) {
            return CKR_MECHANISM_PARAM_INVALID;
        }

        kdf_params->prfType = feedback_params->prfType;
        kdf_params->ulNumberOfDataParams = feedback_params->ulNumberOfDataParams;
        kdf_params->pDataParams = feedback_params->pDataParams;
        kdf_params->ulAdditionalDerivedKeys = feedback_params->ulAdditionalDerivedKeys;
        kdf_params->pAdditionalDerivedKeys = feedback_params->pAdditionalDerivedKeys;

        *initial_value = feedback_params->pIV;
        *initial_value_length = feedback_params->ulIVLen;
    } else {
        if (pMechanism->ulParameterLen != sizeof(CK_SP800_108_KDF_PARAMS)) {
            return CKR_MECHANISM_PARAM_INVALID;
        }

        in_params = (CK_SP800_108_KDF_PARAMS *)pMechanism->pParameter;

        (*kdf_params) = *in_params;
    }

    return CKR_OK;
}

static CK_RV
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
     * parameter is invalid. */
    if ((data->pValue == NULL) != (data->ulValueLen == 0)) {
        return CKR_MECHANISM_PARAM_INVALID;
    }

    switch (data->type) {
        case CK_SP800_108_ITERATION_VARIABLE:
        case CK_SP800_108_OPTIONAL_COUNTER: {
            if (data->type == CK_SP800_108_ITERATION_VARIABLE && !IS_COUNTER(mech)) {
                /* In Feedback and Double Pipeline KDFs, PKCS#11 v3.0 connotes the
                 * iteration variable as the chaining value from the previous PRF
                 * invocation. In contrast, counter mode treats this variable as a
                 * COUNTER_FORMAT descriptor. Thus we can skip validation of
                 * iteration variable parameters outside of counter mode. However,
                 * PKCS#11 v3.0 technically mandates that pValue is NULL, so we
                 * still have to validate that. */

                if (data->pValue != NULL) {
                    return CKR_MECHANISM_PARAM_INVALID;
                }

                return CKR_OK;
            }

            /* In counter mode, data->pValue should be a pointer to an instance of
             * CK_SP800_108_COUNTER_FORMAT; validate its length. */
            if (data->ulValueLen != sizeof(CK_SP800_108_COUNTER_FORMAT)) {
                return CKR_MECHANISM_PARAM_INVALID;
            }

            CK_SP800_108_COUNTER_FORMAT_PTR param = (CK_SP800_108_COUNTER_FORMAT_PTR)data->pValue;

            /* Validate the endian parameter. */
            if (!VALID_CK_BOOL(param->bLittleEndian)) {
                return CKR_MECHANISM_PARAM_INVALID;
            }

            /* Due to restrictions by our underlying hashes, we restrict bit
             * widths to actually be byte widths by ensuring they're a multiple
             * of eight. */
            if ((param->ulWidthInBits % 8) != 0) {
                return CKR_MECHANISM_PARAM_INVALID;
            }

            /* Note that section 5.1 denotes the maximum length of the counter
             * to be 32. */
            if (param->ulWidthInBits > 32) {
                return CKR_MECHANISM_PARAM_INVALID;
            }
            break;
        }
        case CK_SP800_108_DKM_LENGTH: {
            /* data->pValue should be a pointer to an instance of
             * CK_SP800_108_DKM_LENGTH_FORMAT; validate its length. */
            if (data->ulValueLen != sizeof(CK_SP800_108_DKM_LENGTH_FORMAT)) {
                return CKR_MECHANISM_PARAM_INVALID;
            }

            CK_SP800_108_DKM_LENGTH_FORMAT_PTR param = (CK_SP800_108_DKM_LENGTH_FORMAT_PTR)data->pValue;

            /* Validate the method parameter. */
            if (param->dkmLengthMethod != CK_SP800_108_DKM_LENGTH_SUM_OF_KEYS &&
                param->dkmLengthMethod != CK_SP800_108_DKM_LENGTH_SUM_OF_SEGMENTS) {
                return CKR_MECHANISM_PARAM_INVALID;
            }

            /* Validate the endian parameter. */
            if (!VALID_CK_BOOL(param->bLittleEndian)) {
                return CKR_MECHANISM_PARAM_INVALID;
            }

            /* Validate the maximum width: we restrict it to being a byte width
             * instead of a bit width due to restrictions by the underlying
             * PRFs. */
            if ((param->ulWidthInBits % 8) != 0) {
                return CKR_MECHANISM_PARAM_INVALID;
            }

            /* Ensure that the width doesn't overflow a 64-bit int. This
             * restriction is arbitrary but since the counters can't exceed
             * 32-bits (and most PRFs output at most 1024 bits), you're unlikely
             * to need all 64-bits of length indicator. */
            if (param->ulWidthInBits > 64) {
                return CKR_MECHANISM_PARAM_INVALID;
            }
            break;
        }
        case CK_SP800_108_BYTE_ARRAY:
            /* There is no additional data to validate for byte arrays; we can
             * only assume the byte array is of the specified size. */
            break;
        default:
            /* Unexpected parameter type. */
            return CKR_MECHANISM_PARAM_INVALID;
    }

    return CKR_OK;
}

static CK_RV
kbkdf_ValidateDerived(CK_DERIVED_KEY_PTR key)
{
    CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
    PRUint64 keySize = 0;

    /* The pointer to the key handle shouldn't be NULL. If it is, we can't
     * do anything else, so exit early. Every other failure case sets the
     * key->phKey = CK_INVALID_HANDLE, so we can't use `goto failure` here. */
    if (key->phKey == NULL) {
        return CKR_MECHANISM_PARAM_INVALID;
    }

    /* Validate that we have no attributes if and only if pTemplate is NULL.
     * Otherwise, there's an inconsistency somewhere. */
    if ((key->ulAttributeCount == 0) != (key->pTemplate == NULL)) {
        goto failure;
    }

    for (size_t offset = 0; offset < key->ulAttributeCount; offset++) {
        CK_ATTRIBUTE_PTR template = key->pTemplate + offset;

        /* We only look for the CKA_VALUE_LEN and CKA_KEY_TYPE attributes.
         * Everything else we assume we can set on the key if it is passed
         * here. However, if we can't inquire as to a length (and barring
         * that, if we have a key type without a standard length), we're
         * definitely stuck. This mirrors the logic at the top of
         * NSC_DeriveKey(...). */
        if (template->type == CKA_KEY_TYPE) {
            if (template->ulValueLen != sizeof(CK_KEY_TYPE)) {
                goto failure;
            }

            keyType = *(CK_KEY_TYPE *)template->pValue;
        } else if (template->type == CKA_VALUE_LEN) {
            if (template->ulValueLen != sizeof(CK_ULONG)) {
                goto failure;
            }

            keySize = *(CK_ULONG *)template->pValue;
        }
    }

    if (keySize == 0) {
        /* When we lack a keySize, see if we can infer it from the type of the
         * passed key. */
        keySize = sftk_MapKeySize(keyType);
    }

    /* The main piece of information we validate is that we have a length for
     * this key. */
    if (keySize == 0 || keySize >= (1ull << 32ull)) {
        goto failure;
    }

    return CKR_OK;

failure:
    /* PKCS#11 v3.0: If the failure was caused by the content of a specific
     * 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 PRBool
kbkdf_ValidPRF(CK_SP800_108_PRF_TYPE prf)
{
    // See Table 161 of PKCS#11 v3.0 or Table 192 of PKCS#11 v3.1.
    switch (prf) {
        case CKM_AES_CMAC:
            /* case CKM_DES3_CMAC: */
            return PR_TRUE;
        case CKM_SHA_1_HMAC:
        case CKM_SHA224_HMAC:
        case CKM_SHA256_HMAC:
        case CKM_SHA384_HMAC:
        case CKM_SHA512_HMAC:
        case CKM_SHA3_224_HMAC:
        case CKM_SHA3_256_HMAC:
        case CKM_SHA3_384_HMAC:
        case CKM_SHA3_512_HMAC:
            /* Valid HMAC <-> HASH isn't NULL */
            return sftk_HMACMechanismToHash(prf) != HASH_AlgNULL;
    }
    return PR_FALSE;
}

static CK_RV
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 (!kbkdf_ValidPRF(params->prfType)) {
        return CKR_MECHANISM_PARAM_INVALID;
    }

    /* We can't have a null pDataParams pointer: we always need at least one
     * parameter to succeed. */
    if (params->pDataParams == NULL) {
        return CKR_HOST_MEMORY;
    }

    /* Validate each KDF parameter. */
    for (offset = 0; offset < params->ulNumberOfDataParams; offset++) {
        /* Validate this parameter has acceptable values. */
        ret = kbkdf_ValidateParameter(mech, params->pDataParams + offset);
        if (ret != CKR_OK) {
            return CKR_MECHANISM_PARAM_INVALID;
        }

        /* Count that we have a parameter of this type. The above logic
         * in ValidateParameter MUST validate that type is within the
         * appropriate range. */
        PR_ASSERT(params->pDataParams[offset].type < sizeof(param_type_count) / sizeof(param_type_count[0]));
        param_type_count[params->pDataParams[offset].type] += 1;
    }

    if (IS_COUNTER(mech)) {
        /* We have to have at least one iteration variable parameter. */
        if (param_type_count[CK_SP800_108_ITERATION_VARIABLE] == 0) {
            return CKR_MECHANISM_PARAM_INVALID;
        }

        /* We can't have any optional counters parameters -- these belong in
         * iteration variable parameters instead. */
        if (param_type_count[CK_SP800_108_OPTIONAL_COUNTER] != 0) {
            return CKR_MECHANISM_PARAM_INVALID;
        }
    }

    /* Validate basic assumptions about derived keys:
     *      NULL <-> ulAdditionalDerivedKeys > 0
     */
    if ((params->ulAdditionalDerivedKeys == 0) != (params->pAdditionalDerivedKeys == NULL)) {
        return CKR_MECHANISM_PARAM_INVALID;
    }

    /* Validate each derived key. */
    for (offset = 0; offset < params->ulAdditionalDerivedKeys; offset++) {
        ret = kbkdf_ValidateDerived(params->pAdditionalDerivedKeys + offset);
        if (ret != CKR_OK) {
            return CKR_MECHANISM_PARAM_INVALID;
        }
    }

    /* Validate the length of our primary key. */
    if (keySize == 0 || ((PRUint64)keySize) >= (1ull << 32ull)) {
        return CKR_KEY_SIZE_RANGE;
    }

    return CKR_OK;
}

/* [ section: parameter helpers ] */

static CK_VOID_PTR
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;
}

size_t
kbkdf_IncrementBuffer(size_t cur_offset, size_t consumed, size_t prf_length)
{
    return cur_offset + PR_ROUNDUP(consumed, prf_length);
}

CK_ULONG
kbkdf_GetDerivedKeySize(CK_DERIVED_KEY_PTR derived_key)
{
    /* Precondition: kbkdf_ValidateDerived(...) returns CKR_OK for this key,
     * which implies that keySize is defined. */

    CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
    CK_ULONG keySize = 0;

    for (size_t offset = 0; offset < derived_key->ulAttributeCount; offset++) {
        CK_ATTRIBUTE_PTR template = derived_key->pTemplate + offset;

        /* Find the two attributes we care about. */
        if (template->type == CKA_KEY_TYPE) {
            keyType = *(CK_KEY_TYPE *)template->pValue;
        } else if (template->type == CKA_VALUE_LEN) {
            keySize = *(CK_ULONG *)template->pValue;
        }
    }

    /* Prefer keySize, if we have it. */
    if (keySize > 0) {
        return keySize;
    }

    /* 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(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
     * output.
     *
     * Note that *output_bitlen is the L parameter in NIST SP800-108 and is in
     * bits. However, *buffer_length is in bytes.
     */

    if (params->ulAdditionalDerivedKeys == 0) {
        /* When we have no additional derived keys, we get the keySize from
         * the value passed to one of our KBKDF_* methods. */
        *output_bitlen = ret_key_size;
        *buffer_length = ret_key_size;
    } else {
        /* Offset in the additional derived keys array. */
        size_t offset = 0;

        /* Size of the derived key. */
        CK_ULONG derived_size = 0;

        /* In the below, we place the sum of the keys into *output_bitlen
         * and the size of the buffer (with padding mandated by PKCS#11 v3.0)
         * into *buffer_length. If the method is the segment sum, then we
         * replace *output_bitlen with *buffer_length at the end. This ensures
         * we always get a output buffer large enough to handle all derived
         * keys, and *output_bitlen reflects the correct L value. */

        /* Count the initial derived key. */
        *output_bitlen = ret_key_size;
        *buffer_length = kbkdf_IncrementBuffer(0, ret_key_size, ctx->mac_size);

        /* Handle n - 1 keys. The last key is special. */
        for (; offset < params->ulAdditionalDerivedKeys - 1; offset++) {
            derived_size = kbkdf_GetDerivedKeySize(params->pAdditionalDerivedKeys + offset);

            *output_bitlen += derived_size;
            *buffer_length = kbkdf_IncrementBuffer(*buffer_length, derived_size, ctx->mac_size);
        }

        /* Handle the last key. */
        derived_size = kbkdf_GetDerivedKeySize(params->pAdditionalDerivedKeys + offset);

        *output_bitlen += derived_size;
        *buffer_length = kbkdf_IncrementBuffer(*buffer_length, derived_size, ctx->mac_size);

        /* Pointer to the DKM method parameter. Note that this implicit cast
         * is safe since we've assumed we've been validated by
         * kbkdf_ValidateParameters(...). When kdm_param is NULL, we don't
         * use the output_bitlen parameter. */
        CK_SP800_108_DKM_LENGTH_FORMAT_PTR dkm_param = kbkdf_FindParameter(params, CK_SP800_108_DKM_LENGTH);
        if (dkm_param != NULL) {
            if (dkm_param->dkmLengthMethod == CK_SP800_108_DKM_LENGTH_SUM_OF_SEGMENTS) {
                *output_bitlen = *buffer_length;
            }
        }
    }

    /* Note that keySize is the size in bytes and ctx->mac_size is also
     * 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, 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
     * invocation, so handle that here. */
    iteration_count = buffer_length + (ctx->mac_size - 1);
    iteration_count = iteration_count / ctx->mac_size;

    /* NIST SP800-108, section 5.1, process step #2:
     *
     *      if n > 2^r - 1, then indicate an error and stop.
     *
     * In non-counter mode KDFs, r is set at 32, leaving behavior
     * under-defined when the optional counter is included but fewer than
     * 32 bits. This implementation assumes r is 32, but if the counter
     * parameter is included, validates it against that. In counter-mode
     * KDFs, this is in the ITERATION_VARIABLE parameter; in feedback- or
     * pipeline-mode KDFs, this is in the COUNTER parameter.
     *
     * This is consistent with the supplied sample CAVP tests; none reuses the
     * same counter value. In some configurations, this could result in
     * duplicated KDF output. We seek to avoid that from happening.
     */
    if (IS_COUNTER(mech)) {
        param_ptr = kbkdf_FindParameter(params, CK_SP800_108_ITERATION_VARIABLE);

        /* Validated by kbkdf_ValidateParameters(...) above. */
        PR_ASSERT(param_ptr != NULL);

        r = ((CK_SP800_108_COUNTER_FORMAT_PTR)param_ptr)->ulWidthInBits;
    } else {
        param_ptr = kbkdf_FindParameter(params, CK_SP800_108_COUNTER);

        /* Not guaranteed to exist, hence the default value of r=32. */
        if (param_ptr != NULL) {
            r = ((CK_SP800_108_COUNTER_FORMAT_PTR)param_ptr)->ulWidthInBits;
        }
    }

    if (iteration_count >= (1ull << r) || r > 32) {
        return CKR_MECHANISM_PARAM_INVALID;
    }

    *num_iterations = (PRUint32)iteration_count;

    return CKR_OK;
}

static CK_RV
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) {
            /* Necessary for Double Pipeline mode: when constructing the IV,
             * we skip the  optional counter. */
            continue;
        }

        switch (param->type) {
            case CK_SP800_108_ITERATION_VARIABLE: {
                /* When present in COUNTER mode, this signifies adding the counter
                 * variable to the PRF. Otherwise, it signifies the chaining
                 * value for other KDF modes. */
                if (IS_COUNTER(mech)) {
                    CK_SP800_108_COUNTER_FORMAT_PTR counter_format = (CK_SP800_108_COUNTER_FORMAT_PTR)param->pValue;
                    CK_BYTE buffer[sizeof(PRUint64)];
                    CK_ULONG num_bytes;
                    sftk_EncodeInteger(counter, counter_format->ulWidthInBits, counter_format->bLittleEndian, buffer, &num_bytes);
                    ret = sftk_MAC_Update(ctx, buffer, num_bytes);
                } else {
                    ret = sftk_MAC_Update(ctx, chaining_prf, chaining_prf_len);
                }
                break;
            }
            case CK_SP800_108_COUNTER: {
                /* Only present in the case when not using COUNTER mode. */
                PR_ASSERT(!IS_COUNTER(mech));

                /* We should've already validated that this parameter is of
                 * type COUNTER_FORMAT. */
                CK_SP800_108_COUNTER_FORMAT_PTR counter_format = (CK_SP800_108_COUNTER_FORMAT_PTR)param->pValue;
                CK_BYTE buffer[sizeof(PRUint64)];
                CK_ULONG num_bytes;
                sftk_EncodeInteger(counter, counter_format->ulWidthInBits, counter_format->bLittleEndian, buffer, &num_bytes);
                ret = sftk_MAC_Update(ctx, buffer, num_bytes);
                break;
            }
            case CK_SP800_108_BYTE_ARRAY:
                ret = sftk_MAC_Update(ctx, (CK_BYTE_PTR)param->pValue, param->ulValueLen);
                break;
            case CK_SP800_108_DKM_LENGTH: {
                /* We've already done the hard work of calculating the length in
                 * the kbkdf_CalculateIterations function; we merely need to add
                 * the length to the desired point in the input stream. */
                CK_SP800_108_DKM_LENGTH_FORMAT_PTR length_format = (CK_SP800_108_DKM_LENGTH_FORMAT_PTR)param->pValue;
                CK_BYTE buffer[sizeof(PRUint64)];
                CK_ULONG num_bytes;
                sftk_EncodeInteger(length, length_format->ulWidthInBits, length_format->bLittleEndian, buffer, &num_bytes);
                ret = sftk_MAC_Update(ctx, buffer, num_bytes);
                break;
            }
            default:
                /* This should've been caught by kbkdf_ValidateParameters(...). */
                PR_ASSERT(PR_FALSE);
                return CKR_MECHANISM_PARAM_INVALID;
        }

        if (ret != CKR_OK) {
            return ret;
        }
    }

    return CKR_OK;
}

CK_RV
kbkdf_SaveKey(SFTKObject *key, unsigned char *key_buffer, unsigned int key_len)
{
    return sftk_forceAttribute(key, CKA_VALUE, key_buffer, key_len);
}

CK_RV
kbkdf_CreateKey(CK_MECHANISM_TYPE kdf_mech, CK_SESSION_HANDLE hSession, CK_DERIVED_KEY_PTR derived_key, SFTKObject **ret_key)
{
    /* Largely duplicated from NSC_DeriveKey(...) */
    CK_RV ret = CKR_HOST_MEMORY;
    SFTKObject *key = NULL;
    SFTKSlot *slot = sftk_SlotFromSessionHandle(hSession);
    size_t offset = 0;

    /* Slot should be non-NULL because NSC_DeriveKey(...) has already
     * performed a sftk_SlotFromSessionHandle(...) call on this session
     * handle. However, Coverity incorrectly flagged this (see 1607955). */
    PR_ASSERT(slot != NULL);
    PR_ASSERT(ret_key != NULL);
    PR_ASSERT(derived_key != NULL);
    PR_ASSERT(derived_key->phKey != NULL);

    if (slot == NULL) {
        return CKR_SESSION_HANDLE_INVALID;
    }

    /* Create the new key object for this additional derived key. */
    key = sftk_NewObject(slot);
    if (key == NULL) {
        return CKR_HOST_MEMORY;
    }

    /* Setup the key from the provided template. */
    for (offset = 0; offset < derived_key->ulAttributeCount; offset++) {
        ret = sftk_AddAttributeType(key, sftk_attr_expand(derived_key->pTemplate + offset));
        if (ret != CKR_OK) {
            sftk_FreeObject(key);
            return ret;
        }
    }

    /* When using the CKM_SP800_* series of mechanisms, the result must be a
     * secret key, so its contents can be adequately protected in FIPS mode.
     * However, when using the special CKM_NSS_SP800_*_DERIVE_DATA series, the
     * contents need not be protected, so we set CKO_DATA on these "keys". */
    CK_OBJECT_CLASS classType = CKO_SECRET_KEY;
    if (DOES_DERIVE_DATA(kdf_mech)) {
        classType = CKO_DATA;
    }

    ret = sftk_forceAttribute(key, CKA_CLASS, &classType, sizeof(classType));
    if (ret != CKR_OK) {
        sftk_FreeObject(key);
        return ret;
    }

    *ret_key = key;
    return CKR_OK;
}

CK_RV
kbkdf_FinalizeKey(CK_SESSION_HANDLE hSession, CK_DERIVED_KEY_PTR derived_key, SFTKObject *key)
{
    /* Largely duplicated from NSC_DeriveKey(...) */
    CK_RV ret = CKR_HOST_MEMORY;
    SFTKSession *session = NULL;

    PR_ASSERT(derived_key != NULL && key != NULL);

    SFTKSessionObject *sessionForKey = sftk_narrowToSessionObject(key);
    PR_ASSERT(sessionForKey != NULL);
    sessionForKey->wasDerived = PR_TRUE;

    session = sftk_SessionFromHandle(hSession);

    /* Session should be non-NULL because NSC_DeriveKey(...) has already
     * performed a sftk_SessionFromHandle(...) call on this session handle. */
    PR_ASSERT(session != NULL);

    ret = sftk_handleObject(key, session);
    if (ret != CKR_OK) {
        goto done;
    }

    *(derived_key->phKey) = key->handle;

done:
    /* Guaranteed that key != NULL */
    sftk_FreeObject(key);

    /* Doesn't do anything. */
    if (session) {
        sftk_FreeSession(session);
    }

    return ret;
}

CK_RV
kbkdf_SaveKeys(CK_MECHANISM_TYPE mech, CK_SESSION_HANDLE hSession, CK_SP800_108_KDF_PARAMS_PTR params, unsigned char *output_buffer, size_t buffer_len, size_t prf_length, SFTKObject *ret_key, CK_ULONG ret_key_size)
{
    CK_RV ret;
    size_t key_offset = 0;
    size_t buffer_offset = 0;

    PR_ASSERT(output_buffer != NULL && buffer_len > 0 && ret_key != NULL);

    /* First place key material into the main key. */
    ret = kbkdf_SaveKey(ret_key, output_buffer + buffer_offset, ret_key_size);
    if (ret != CKR_OK) {
        return ret;
    }

    /* Then increment the offset based on PKCS#11 additional key guidelines:
     * no two keys may share the key stream from the same PRF invocation. */
    buffer_offset = kbkdf_IncrementBuffer(buffer_offset, ret_key_size, prf_length);

    if (params->ulAdditionalDerivedKeys > 0) {
        /* Note that the following code is technically incorrect: PKCS#11 v3.0
         * says that _no_ key should be set in the event of failure to derive
         * _any_ key. */
        for (key_offset = 0; key_offset < params->ulAdditionalDerivedKeys; key_offset++) {
            CK_DERIVED_KEY_PTR derived_key = params->pAdditionalDerivedKeys + key_offset;
            SFTKObject *key_obj = NULL;
            size_t key_size = kbkdf_GetDerivedKeySize(derived_key);

            /* Create a new internal key object for this derived key. */
            ret = kbkdf_CreateKey(mech, hSession, derived_key, &key_obj);
            if (ret != CKR_OK) {
                *(derived_key->phKey) = CK_INVALID_HANDLE;
                return ret;
            }

            /* Save the underlying key bytes to the key object. */
            ret = kbkdf_SaveKey(key_obj, output_buffer + buffer_offset, key_size);
            if (ret != CKR_OK) {
                /* When kbkdf_CreateKey(...) exits with an error, it will free
                 * the constructed key object. kbkdf_FinalizeKey(...) also
                 * always frees the key object. In the unlikely event that
                 * kbkdf_SaveKey(...) _does_ fail, we thus need to free it
                 * manually. */
                sftk_FreeObject(key_obj);
                *(derived_key->phKey) = CK_INVALID_HANDLE;
                return ret;
            }

            /* Handle the increment. */
            buffer_offset = kbkdf_IncrementBuffer(buffer_offset, key_size, prf_length);

            /* Finalize this key. */
            ret = kbkdf_FinalizeKey(hSession, derived_key, key_obj);
            if (ret != CKR_OK) {
                *(derived_key->phKey) = CK_INVALID_HANDLE;
                return ret;
            }
        }
    }

    return CKR_OK;
}

/* [ section: KDFs ] */

static CK_RV
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. */
    PRUint32 num_iterations;

    /* Offset in ret_buffer that we're at. */
    size_t buffer_offset = 0;

    /* 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;

    /* Calculate the number of iterations required based on the size of the
     * output buffer. */
    ret = kbkdf_CalculateIterations(CKM_SP800_108_COUNTER_KDF, params, ctx, buffer_length, &num_iterations);
    if (ret != CKR_OK) {
        return ret;
    }

    /*
     * 5.1 - [ KDF in Counter Mode ]
     *
     * Fixed values:
     *      1. h - the length of the PRF in bits (ctx->mac_size)
     *      2. r - the length of the binary representation of the counter i
     *          (params[k: params[k].type == CK_SP800_108_ITERATION_VARIABLE:].data->ulWidthInBits)
     * Input:
     *      1. K_I - the key for the PRF (base_key)
     *      2. label - a binary data field, usually before the separator. Optional.
     *      3. context - a binary data field, usually after the separator. Optional.
     *      4. L - length of the output in bits (output_bitlen)
     *
     * Process:
     *      1. n := ceil(L / h) (num_iterations)
     *      2. if n > 2^r - 1, then indicate an error and stop
     *      3. result(0) = NULL
     *      4. for i = 1 to n, do
     *          a. K(i) = PRF(K_I, [i]_2 || Label || 0x00 || Context || [L]_2)
     *          b. result(i) := result(i - 1) || K(i).
     *      5. return K_O := the leftmost L bits of result(n).
     */
    for (counter = 1; counter <= num_iterations; counter++) {
        if (counter == num_iterations) {
            block_size = buffer_length - buffer_offset;

            /* Assumption: if we've validated our arguments correctly, this
             * should always be true. */
            PR_ASSERT(block_size <= ctx->mac_size);
        }

        /* Add all parameters required by this instance of the KDF to the
         * input stream of the underlying PRF. */
        ret = kbkdf_AddParameters(CKM_SP800_108_COUNTER_KDF, ctx, params, counter, output_bitlen, NULL, 0 /* chaining_prf output */, 0 /* exclude */);
        if (ret != CKR_OK) {
            return ret;
        }

        /* Finalize this iteration of the PRF. */
        ret = sftk_MAC_End(ctx, ret_buffer + buffer_offset, NULL, block_size);
        if (ret != CKR_OK) {
            return ret;
        }

        /* Increment our position in the key material. */
        buffer_offset += block_size;

        if (counter < num_iterations) {
            /* Reset the underlying PRF for the next iteration. Only do this
             * when we have a next iteration since it isn't necessary to do
             * either before the first iteration (MAC is already initialized)
             * or after the last iteration (we won't be called again). */
            ret = sftk_MAC_Reset(ctx);
            if (ret != CKR_OK) {
                return ret;
            }
        }
    }

    return CKR_OK;
}

static CK_RV
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. */
    PRUint32 num_iterations;

    /* Offset in ret_buffer that we're at. */
    size_t buffer_offset = 0;

    /* 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 = (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);
    if (ret != CKR_OK) {
        goto finish;
    }

    /*
     * 5.2 - [ KDF in Feedback Mode ]
     *
     * Fixed values:
     *      1. h - the length of the PRF in bits (ctx->mac_size)
     *      2. r - the length of the binary representation of the counter i
     *          (params[k: params[k].type == CK_SP800_108_OPTIONAL_COUNTER:].data->ulWidthInBits)
     *          Note that it is only specified when the optional counter is requested.
     * Input:
     *      1. K_I - the key for the PRF (base_key)
     *      2. label - a binary data field, usually before the separator. Optional.
     *      3. context - a binary data field, usually after the separator. Optional.
     *      4. IV - a binary data field, initial PRF value. (params->pIV)
     *      5. L - length of the output in bits (output_bitlen)
     *
     * Process:
     *      1. n := ceil(L / h) (num_iterations)
     *      2. if n > 2^32 - 1, then indicate an error and stop
     *      3. result(0) = NULL, K(0) := IV (chaining_value)
     *      4. for i = 1 to n, do
     *          a. K(i) = PRF(K_I, K(i-1) {|| [i]_2} || Label || 0x00 || Context || [L]_2)
     *          b. result(i) := result(i - 1) || K(i).
     *      5. return K_O := the leftmost L bits of result(n).
     */
    for (counter = 1; counter <= num_iterations; counter++) {
        if (counter == num_iterations) {
            block_size = buffer_length - buffer_offset;

            /* Assumption: if we've validated our arguments correctly, this
             * should always be true. */
            PR_ASSERT(block_size <= ctx->mac_size);
        }

        /* Add all parameters required by this instance of the KDF to the
         * input stream of the underlying PRF. */
        ret = kbkdf_AddParameters(CKM_SP800_108_FEEDBACK_KDF, ctx, params, counter, output_bitlen, chaining_value, chaining_length, 0 /* exclude */);
        if (ret != CKR_OK) {
            goto finish;
        }

        if (counter == 1) {
            /* On the first iteration, chaining_value points to the IV from
             * the caller and chaining_length is the length of that IV. We
             * now need to allocate a buffer of suitable length to store the
             * MAC output. */
            chaining_value = PORT_ZNewArray(unsigned char, ctx->mac_size);
            chaining_length = ctx->mac_size;

            if (chaining_value == NULL) {
                ret = CKR_HOST_MEMORY;
                goto finish;
            }
        }

        /* Finalize this iteration of the PRF. Unlike other KDF forms, we
         * first save this to the chaining value so that we can reuse it
         * in the next iteration before copying the necessary length to
         * the output buffer. */
        ret = sftk_MAC_End(ctx, chaining_value, NULL, chaining_length);
        if (ret != CKR_OK) {
            goto finish;
        }

        /* Save as much of the chaining value as we need for output. */
        PORT_Memcpy(ret_buffer + buffer_offset, chaining_value, block_size);

        /* Increment our position in the key material. */
        buffer_offset += block_size;

        if (counter < num_iterations) {
            /* Reset the underlying PRF for the next iteration. Only do this
             * when we have a next iteration since it isn't necessary to do
             * either before the first iteration (MAC is already initialized)
             * or after the last iteration (we won't be called again). */
            ret = sftk_MAC_Reset(ctx);
            if (ret != CKR_OK) {
                goto finish;
            }
        }
    }

finish:
    if (chaining_value != initial_value && chaining_value != NULL) {
        PORT_ZFree(chaining_value, chaining_length);
    }

    return ret;
}

static CK_RV
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. */
    PRUint32 num_iterations;

    /* Offset in ret_buffer that we're at. */
    size_t buffer_offset = 0;

    /* 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. This is used for the first of the double
     * PRF invocations this KDF is named after. This defaults to NULL,
     * signifying that we have to calculate the initial value from params;
     * when non-NULL, we directly add only this value to the PRF. */
    unsigned char *chaining_value = NULL;

    /* Size of the chaining value discussed above. Defaults to 0. */
    size_t chaining_length = 0;

    /* Calculate the number of iterations required based on the size of the
     * output buffer. */
    ret = kbkdf_CalculateIterations(CKM_SP800_108_DOUBLE_PIPELINE_KDF, params, ctx, buffer_length, &num_iterations);
    if (ret != CKR_OK) {
        goto finish;
    }

    /*
     * 5.3 - [ KDF in Double-Pipeline Iteration Mode ]
     *
     * Fixed values:
     *      1. h - the length of the PRF in bits (ctx->mac_size)
     *      2. r - the length of the binary representation of the counter i
     *          (params[k: params[k].type == CK_SP800_108_OPTIONAL_COUNTER:].data->ulWidthInBits)
     *          Note that it is only specified when the optional counter is requested.
     * Input:
     *      1. K_I - the key for the PRF (base_key)
     *      2. label - a binary data field, usually before the separator. Optional.
     *      3. context - a binary data field, usually after the separator. Optional.
     *      4. L - length of the output in bits (output_bitlen)
     *
     * Process:
     *      1. n := ceil(L / h) (num_iterations)
     *      2. if n > 2^32 - 1, then indicate an error and stop
     *      3. result(0) = NULL
     *      4. A(0) := IV := Label || 0x00 || Context || [L]_2
     *      5. for i = 1 to n, do
     *          a. A(i) := PRF(K_I, A(i-1))
     *          b. K(i) := PRF(K_I, A(i) {|| [i]_2} || Label || 0x00 || Context || [L]_2
     *          c. result(i) := result(i-1) || K(i)
     *      6. return K_O := the leftmost L bits of result(n).
     */
    for (counter = 1; counter <= num_iterations; counter++) {
        if (counter == num_iterations) {
            block_size = buffer_length - buffer_offset;

            /* Assumption: if we've validated our arguments correctly, this
             * should always be true. */
            PR_ASSERT(block_size <= ctx->mac_size);
        }

        /* ===== First pipeline: construct A(i) ===== */
        if (counter == 1) {
            /* On the first iteration, we have no chaining value so specify
             * NULL for the pointer and 0 for the length, and exclude the
             * optional counter if it exists. This is what NIST specifies as
             * the IV for the KDF. */
            ret = kbkdf_AddParameters(CKM_SP800_108_DOUBLE_PIPELINE_KDF, ctx, params, counter, output_bitlen, NULL, 0, CK_SP800_108_OPTIONAL_COUNTER);
            if (ret != CKR_OK) {
                goto finish;
            }

            /* Allocate the chaining value so we can save the PRF output. */
            chaining_value = PORT_ZNewArray(unsigned char, ctx->mac_size);
            chaining_length = ctx->mac_size;
            if (chaining_value == NULL) {
                ret = CKR_HOST_MEMORY;
                goto finish;
            }
        } else {
            /* On all other iterations, the next stage of the first pipeline
             * comes directly from this stage. */
            ret = sftk_MAC_Update(ctx, chaining_value, chaining_length);
            if (ret != CKR_OK) {
                goto finish;
            }
        }

        /* Save the PRF output to chaining_value for use in the second
         * pipeline. */
        ret = sftk_MAC_End(ctx, chaining_value, NULL, chaining_length);
        if (ret != CKR_OK) {
            goto finish;
        }

        /* Reset the PRF so we can reuse it for the second pipeline. */
        ret = sftk_MAC_Reset(ctx);
        if (ret != CKR_OK) {
            goto finish;
        }

        /* ===== Second pipeline: construct K(i) ===== */

        /* Add all parameters required by this instance of the KDF to the
         * input stream of the underlying PRF. Note that this includes the
         * chaining value we calculated from the previous pipeline stage. */
        ret = kbkdf_AddParameters(CKM_SP800_108_FEEDBACK_KDF, ctx, params, counter, output_bitlen, chaining_value, chaining_length, 0 /* exclude */);
        if (ret != CKR_OK) {
            goto finish;
        }

        /* Finalize this iteration of the PRF directly to the output buffer.
         * Unlike Feedback mode, this pipeline doesn't influence the previous
         * stage. */
        ret = sftk_MAC_End(ctx, ret_buffer + buffer_offset, NULL, block_size);
        if (ret != CKR_OK) {
            goto finish;
        }

        /* Increment our position in the key material. */
        buffer_offset += block_size;

        if (counter < num_iterations) {
            /* Reset the underlying PRF for the next iteration. Only do this
             * when we have a next iteration since it isn't necessary to do
             * either before the first iteration (MAC is already initialized)
             * or after the last iteration (we won't be called again). */
            ret = sftk_MAC_Reset(ctx);
            if (ret != CKR_OK) {
                goto finish;
            }
        }
    }

finish:
    PORT_ZFree(chaining_value, chaining_length);

    return ret;
}

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)
{
    CK_RV ret;
    /* Context for our underlying PRF function.
     *
     * Zeroing context required unconditional call of sftk_MAC_DestroyContext.
     */
    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
     * last key, so it isn't an even multiple of the PRF output size. */
    unsigned char *output_buffer = NULL;

    /* Size of the above buffer, in bytes. Note that this is technically
     * separate from the below output_bitlen variable due to the presence
     * of additional derived keys. See commentary in kbkdf_CalculateLength.
     */
    size_t buffer_length = 0;

    /* While NIST specifies a maximum length (in bits) for the counter, they
     * don't for the maximum length. It is unlikely, but theoretically
     * possible for output of the PRF to exceed 32 bits while keeping the
     * counter under 2^32. Thus, use a 64-bit variable for the maximum
     * output length.
     *
     * It is unlikely any caller will request this much data in practice.
     * 2^32 invocations of the PRF (for a 512-bit PRF) would be 256GB of
     * data in the KDF key stream alone. The bigger limit is the number of
     * 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;

    /* 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);
    if (ret != CKR_OK) {
        goto finish;
    }

    /* Initialize the underlying PRF state. */
    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);
    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);
            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);
            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);
            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_DestroyContext(&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, mac_size, ret_key, ret_key_size);
    if (ret != CKR_OK) {
        goto finish;
    }

finish:
    PORT_ZFree(output_buffer, buffer_length);

    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);
            PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
            return SECFailure;
        }
        PORT_ZFree(output_buffer, buffer_length);
    }
    return SECSuccess;
}
