Source code

Revision control

Copy as Markdown

Other Tools

#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;
}