diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /security/nss/lib/softoken/kbkdf.c | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/nss/lib/softoken/kbkdf.c')
-rw-r--r-- | security/nss/lib/softoken/kbkdf.c | 1520 |
1 files changed, 1520 insertions, 0 deletions
diff --git a/security/nss/lib/softoken/kbkdf.c b/security/nss/lib/softoken/kbkdf.c new file mode 100644 index 0000000000..c6021ef5e5 --- /dev/null +++ b/security/nss/lib/softoken/kbkdf.c @@ -0,0 +1,1520 @@ +#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 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 (!(/* The following types aren't defined in NSS yet. */ + /* params->prfType != CKM_3DES_CMAC && */ + params->prfType == CKM_AES_CMAC || /* allow */ + /* We allow any HMAC except MD2 and MD5. */ + params->prfType != CKM_MD2_HMAC || /* disallow */ + params->prfType != CKM_MD5_HMAC || /* disallow */ + sftk_HMACMechanismToHash(params->prfType) != HASH_AlgNULL /* Valid HMAC <-> HASH isn't NULL */ + )) { + 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_Finish(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_Finish(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_Finish(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_Finish(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_Destroy. + */ + sftk_MACCtx ctx = { 0 }; + + /* We need one buffers large enough to fit the entire KDF key stream for + * all iterations of the PRF. This needs only include to the end of the + * 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_Destroy(&ctx, PR_FALSE); + return ret; +} + +/* [ section: PKCS#11 entry ] */ + +CK_RV +kbkdf_Dispatch(CK_MECHANISM_TYPE mech, CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, SFTKObject *prf_key, SFTKObject *ret_key, CK_ULONG ret_key_size) +{ + /* This handles boilerplate common to all KBKDF types. Instead of placing + * this in pkcs11c.c, place it here to reduce clutter. */ + + CK_RV ret; + + /* Assumptions about our calling environment. */ + PR_ASSERT(pMechanism != NULL && prf_key != NULL && ret_key != NULL); + + /* Validate that the caller passed parameters. */ + if (pMechanism->pParameter == NULL) { + return CKR_MECHANISM_PARAM_INVALID; + } + + /* Create a common set of parameters to use for all KDF types. This + * separates out the KDF parameters from the Feedback-specific IV, + * allowing us to use a common type for all calls. */ + CK_SP800_108_KDF_PARAMS kdf_params = { 0 }; + CK_BYTE_PTR initial_value = NULL; + CK_ULONG initial_value_length = 0; + unsigned char *output_buffer = NULL; + size_t buffer_length = 0; + unsigned int mac_size = 0; + + /* Split Feedback-specific IV from remaining KDF parameters. */ + ret = kbkdf_LoadParameters(mech, pMechanism, &kdf_params, &initial_value, &initial_value_length); + if (ret != CKR_OK) { + goto finish; + } + /* let rawDispatch handle the rest. We split this out so we could + * handle the POST test without accessing pkcs #11 objects. */ + ret = kbkdf_RawDispatch(mech, &kdf_params, initial_value, + initial_value_length, prf_key, NULL, 0, + &output_buffer, &buffer_length, &mac_size, + ret_key_size); + if (ret != CKR_OK) { + goto finish; + } + + /* Write the output of the PRF into the appropriate keys. */ + ret = kbkdf_SaveKeys(mech, hSession, &kdf_params, output_buffer, buffer_length, 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); + return SECFailure; + } + PORT_ZFree(output_buffer, buffer_length); + } + return SECSuccess; +} |