/* 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.
*/
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);
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;
}
/* 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;
}
/* 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;
}
/* 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;
}
/* 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;
}
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;
}
/* 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;
}
}
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. */
/* Find the two attributes we care about. */ if (template->type == CKA_KEY_TYPE) {
keyType = *(CK_KEY_TYPE *)template->pValue;
} elseif (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. */
/* 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;
/* 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;
}
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;
}
/* 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;
}
/* 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);
}
/* 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;
}
}
}
/* 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;
}
}
}
/* 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. */ unsignedchar *chaining_value = (unsignedchar *)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(unsignedchar, 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;
}
}
}
/* 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. */ unsignedchar *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(unsignedchar, 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;
}
}
}
/* 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. */ unsignedchar *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(unsignedchar, 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;
}
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. */
/* 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; unsignedchar *output_buffer = NULL;
size_t buffer_length = 0; unsignedint 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;
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.23Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.