| Index: mozilla/security/nss/lib/pk11wrap/pk11skey.c
|
| ===================================================================
|
| --- mozilla/security/nss/lib/pk11wrap/pk11skey.c (revision 158129)
|
| +++ mozilla/security/nss/lib/pk11wrap/pk11skey.c (working copy)
|
| @@ -813,28 +813,53 @@
|
| }
|
|
|
| /*
|
| - * Make sure the slot we are in the correct slot for the operation
|
| + * Make sure the slot we are in is the correct slot for the operation
|
| + * by verifying that it supports all of the specified mechanism types.
|
| */
|
| PK11SymKey *
|
| -pk11_ForceSlot(PK11SymKey *symKey,CK_MECHANISM_TYPE type,
|
| - CK_ATTRIBUTE_TYPE operation)
|
| +pk11_ForceSlotMultiple(PK11SymKey *symKey, CK_MECHANISM_TYPE *type,
|
| + int mechCount, CK_ATTRIBUTE_TYPE operation)
|
| {
|
| PK11SlotInfo *slot = symKey->slot;
|
| PK11SymKey *newKey = NULL;
|
| + PRBool needToCopy = PR_FALSE;
|
| + int i;
|
|
|
| - if ((slot== NULL) || !PK11_DoesMechanism(slot,type)) {
|
| - slot = PK11_GetBestSlot(type,symKey->cx);
|
| + if (slot == NULL) {
|
| + needToCopy = PR_TRUE;
|
| + } else {
|
| + i = 0;
|
| + while ((i < mechCount) && (needToCopy == PR_FALSE)) {
|
| + if (!PK11_DoesMechanism(slot,type[i])) {
|
| + needToCopy = PR_TRUE;
|
| + }
|
| + i++;
|
| + }
|
| + }
|
| +
|
| + if (needToCopy == PR_TRUE) {
|
| + slot = PK11_GetBestSlotMultiple(type,mechCount,symKey->cx);
|
| if (slot == NULL) {
|
| PORT_SetError( SEC_ERROR_NO_MODULE );
|
| return NULL;
|
| }
|
| - newKey = pk11_CopyToSlot(slot, type, operation, symKey);
|
| + newKey = pk11_CopyToSlot(slot, type[0], operation, symKey);
|
| PK11_FreeSlot(slot);
|
| }
|
| return newKey;
|
| }
|
|
|
| +/*
|
| + * Make sure the slot we are in is the correct slot for the operation
|
| + */
|
| PK11SymKey *
|
| +pk11_ForceSlot(PK11SymKey *symKey,CK_MECHANISM_TYPE type,
|
| + CK_ATTRIBUTE_TYPE operation)
|
| +{
|
| + return pk11_ForceSlotMultiple(symKey, &type, 1, operation);
|
| +}
|
| +
|
| +PK11SymKey *
|
| PK11_MoveSymKey(PK11SlotInfo *slot, CK_ATTRIBUTE_TYPE operation,
|
| CK_FLAGS flags, PRBool perm, PK11SymKey *symKey)
|
| {
|
| @@ -1529,6 +1554,239 @@
|
| return symKey;
|
| }
|
|
|
| +/* Create a new key by concatenating base and data
|
| + */
|
| +static PK11SymKey *pk11_ConcatenateBaseAndData(PK11SymKey *base,
|
| + CK_BYTE *data, CK_ULONG dataLen, CK_MECHANISM_TYPE target,
|
| + CK_ATTRIBUTE_TYPE operation)
|
| +{
|
| + CK_KEY_DERIVATION_STRING_DATA mechParams;
|
| + SECItem param;
|
| +
|
| + if (base == NULL) {
|
| + PORT_SetError( SEC_ERROR_INVALID_ARGS );
|
| + return NULL;
|
| + }
|
| +
|
| + mechParams.pData = data;
|
| + mechParams.ulLen = dataLen;
|
| + param.data = (unsigned char *)&mechParams;
|
| + param.len = sizeof(CK_KEY_DERIVATION_STRING_DATA);
|
| +
|
| + return PK11_Derive(base, CKM_CONCATENATE_BASE_AND_DATA,
|
| + ¶m, target, operation, 0);
|
| +}
|
| +
|
| +/* Create a new key by concatenating base and key
|
| + */
|
| +static PK11SymKey *pk11_ConcatenateBaseAndKey(PK11SymKey *base,
|
| + PK11SymKey *key, CK_MECHANISM_TYPE target,
|
| + CK_ATTRIBUTE_TYPE operation, CK_ULONG keySize)
|
| +{
|
| + SECItem param;
|
| +
|
| + if ((base == NULL) || (key == NULL)) {
|
| + PORT_SetError( SEC_ERROR_INVALID_ARGS );
|
| + return NULL;
|
| + }
|
| +
|
| + param.data = (unsigned char *)&(key->objectID);
|
| + param.len = sizeof(CK_OBJECT_HANDLE);
|
| +
|
| + return PK11_Derive(base, CKM_CONCATENATE_BASE_AND_KEY,
|
| + ¶m, target, operation, keySize);
|
| +}
|
| +
|
| +/* Create a new key whose value is the hash of tobehashed.
|
| + * type is the mechanism for the derived key.
|
| + */
|
| +static PK11SymKey *pk11_HashKeyDerivation(PK11SymKey *toBeHashed,
|
| + CK_MECHANISM_TYPE hashMechanism, CK_MECHANISM_TYPE target,
|
| + CK_ATTRIBUTE_TYPE operation, CK_ULONG keySize)
|
| +{
|
| + return PK11_Derive(toBeHashed, hashMechanism, NULL, target, operation, keySize);
|
| +}
|
| +
|
| +/* This function implements the ANSI X9.63 key derivation function
|
| + */
|
| +static PK11SymKey *pk11_ANSIX963Derive(PK11SymKey *sharedSecret,
|
| + CK_EC_KDF_TYPE kdf, SECItem *sharedData,
|
| + CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation,
|
| + CK_ULONG keySize)
|
| +{
|
| + CK_KEY_TYPE keyType;
|
| + CK_MECHANISM_TYPE hashMechanism, mechanismArray[4];
|
| + CK_ULONG derivedKeySize, HashLen, counter, maxCounter, bufferLen;
|
| + CK_ULONG SharedInfoLen;
|
| + CK_BYTE *buffer = NULL;
|
| + PK11SymKey *toBeHashed, *hashOutput;
|
| + PK11SymKey *newSharedSecret = NULL;
|
| + PK11SymKey *oldIntermediateResult, *intermediateResult = NULL;
|
| +
|
| + if (sharedSecret == NULL) {
|
| + PORT_SetError( SEC_ERROR_INVALID_ARGS );
|
| + return NULL;
|
| + }
|
| +
|
| + switch (kdf) {
|
| + case CKD_SHA1_KDF:
|
| + HashLen = SHA1_LENGTH;
|
| + hashMechanism = CKM_SHA1_KEY_DERIVATION;
|
| + break;
|
| + case CKD_SHA224_KDF:
|
| + HashLen = SHA224_LENGTH;
|
| + hashMechanism = CKM_SHA224_KEY_DERIVATION;
|
| + break;
|
| + case CKD_SHA256_KDF:
|
| + HashLen = SHA256_LENGTH;
|
| + hashMechanism = CKM_SHA256_KEY_DERIVATION;
|
| + break;
|
| + case CKD_SHA384_KDF:
|
| + HashLen = SHA384_LENGTH;
|
| + hashMechanism = CKM_SHA384_KEY_DERIVATION;
|
| + break;
|
| + case CKD_SHA512_KDF:
|
| + HashLen = SHA512_LENGTH;
|
| + hashMechanism = CKM_SHA512_KEY_DERIVATION;
|
| + break;
|
| + default:
|
| + PORT_SetError( SEC_ERROR_INVALID_ARGS );
|
| + return NULL;
|
| + }
|
| +
|
| + derivedKeySize = keySize;
|
| + if (derivedKeySize == 0) {
|
| + keyType = PK11_GetKeyType(target,keySize);
|
| + derivedKeySize = pk11_GetPredefinedKeyLength(keyType);
|
| + if (derivedKeySize == 0) {
|
| + derivedKeySize = HashLen;
|
| + }
|
| + }
|
| +
|
| + /* Check that key_len isn't too long. The maximum key length could be
|
| + * greatly increased if the code below did not limit the 4-byte counter
|
| + * to a maximum value of 255. */
|
| + if (derivedKeySize > 254 * HashLen) {
|
| + PORT_SetError( SEC_ERROR_INVALID_ARGS );
|
| + return NULL;
|
| + }
|
| +
|
| + maxCounter = derivedKeySize / HashLen;
|
| + if (derivedKeySize > maxCounter * HashLen)
|
| + maxCounter++;
|
| +
|
| + if ((sharedData == NULL) || (sharedData->data == NULL))
|
| + SharedInfoLen = 0;
|
| + else
|
| + SharedInfoLen = sharedData->len;
|
| +
|
| + bufferLen = SharedInfoLen + 4;
|
| +
|
| + /* Populate buffer with Counter || sharedData
|
| + * where Counter is 0x00000001. */
|
| + buffer = (unsigned char *)PORT_Alloc(bufferLen);
|
| + if (buffer == NULL) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + return NULL;
|
| + }
|
| +
|
| + buffer[0] = 0;
|
| + buffer[1] = 0;
|
| + buffer[2] = 0;
|
| + buffer[3] = 1;
|
| + if (SharedInfoLen > 0) {
|
| + PORT_Memcpy(&buffer[4], sharedData->data, SharedInfoLen);
|
| + }
|
| +
|
| + /* Look for a slot that supports the mechanisms needed
|
| + * to implement the ANSI X9.63 KDF as well as the
|
| + * target mechanism.
|
| + */
|
| + mechanismArray[0] = CKM_CONCATENATE_BASE_AND_DATA;
|
| + mechanismArray[1] = hashMechanism;
|
| + mechanismArray[2] = CKM_CONCATENATE_BASE_AND_KEY;
|
| + mechanismArray[3] = target;
|
| +
|
| + newSharedSecret = pk11_ForceSlotMultiple(sharedSecret,
|
| + mechanismArray, 4, operation);
|
| + if (newSharedSecret != NULL) {
|
| + sharedSecret = newSharedSecret;
|
| + }
|
| +
|
| + for(counter=1; counter <= maxCounter; counter++) {
|
| + /* Concatenate shared_secret and buffer */
|
| + toBeHashed = pk11_ConcatenateBaseAndData(sharedSecret, buffer,
|
| + bufferLen, hashMechanism, operation);
|
| + if (toBeHashed == NULL) {
|
| + goto loser;
|
| + }
|
| +
|
| + /* Hash value */
|
| + if (maxCounter == 1) {
|
| + /* In this case the length of the key to be derived is
|
| + * less than or equal to the length of the hash output.
|
| + * So, the output of the hash operation will be the
|
| + * dervied key. */
|
| + hashOutput = pk11_HashKeyDerivation(toBeHashed, hashMechanism,
|
| + target, operation, keySize);
|
| + } else {
|
| + /* In this case, the output of the hash operation will be
|
| + * concatenated with other data to create the derived key. */
|
| + hashOutput = pk11_HashKeyDerivation(toBeHashed, hashMechanism,
|
| + CKM_CONCATENATE_BASE_AND_KEY, operation, 0);
|
| + }
|
| + PK11_FreeSymKey(toBeHashed);
|
| + if (hashOutput == NULL) {
|
| + goto loser;
|
| + }
|
| +
|
| + /* Append result to intermediate result, if necessary */
|
| + oldIntermediateResult = intermediateResult;
|
| +
|
| + if (oldIntermediateResult == NULL) {
|
| + intermediateResult = hashOutput;
|
| + } else {
|
| + if (counter == maxCounter) {
|
| + /* This is the final concatenation, and so the output
|
| + * will be the derived key. */
|
| + intermediateResult =
|
| + pk11_ConcatenateBaseAndKey(oldIntermediateResult,
|
| + hashOutput, target, operation, keySize);
|
| + } else {
|
| + /* The output of this concatenation will be concatenated
|
| + * with other data to create the derived key. */
|
| + intermediateResult =
|
| + pk11_ConcatenateBaseAndKey(oldIntermediateResult,
|
| + hashOutput, CKM_CONCATENATE_BASE_AND_KEY,
|
| + operation, 0);
|
| + }
|
| +
|
| + PK11_FreeSymKey(hashOutput);
|
| + PK11_FreeSymKey(oldIntermediateResult);
|
| + if (intermediateResult == NULL) {
|
| + goto loser;
|
| + }
|
| + }
|
| +
|
| + /* Increment counter (assumes maxCounter < 255) */
|
| + buffer[3]++;
|
| + }
|
| +
|
| + PORT_ZFree(buffer, bufferLen);
|
| + if (newSharedSecret != NULL)
|
| + PK11_FreeSymKey(newSharedSecret);
|
| + return intermediateResult;
|
| +
|
| +loser:
|
| + if (buffer != NULL)
|
| + PORT_ZFree(buffer, bufferLen);
|
| + if (newSharedSecret != NULL)
|
| + PK11_FreeSymKey(newSharedSecret);
|
| + if (intermediateResult != NULL)
|
| + PK11_FreeSymKey(intermediateResult);
|
| + return NULL;
|
| +}
|
| +
|
| /*
|
| * This Generates a wrapping key based on a privateKey, publicKey, and two
|
| * random numbers. For Mail usage RandomB should be NULL. In the Sender's
|
| @@ -1677,7 +1935,7 @@
|
| keyType = PK11_GetKeyType(target,keySize);
|
| key_size = keySize;
|
| if (key_size == 0) {
|
| - if (pk11_GetPredefinedKeyLength(keyType)) {
|
| + if ((key_size = pk11_GetPredefinedKeyLength(keyType))) {
|
| templateCount --;
|
| } else {
|
| /* sigh, some tokens can't figure this out and require
|
| @@ -1737,6 +1995,23 @@
|
| return NULL;
|
| }
|
|
|
| +/* Returns the size of the public key, or 0 if there
|
| + * is an error. */
|
| +static CK_ULONG
|
| +pk11_ECPubKeySize(SECItem *publicValue)
|
| +{
|
| + if (publicValue->data[0] == 0x04) {
|
| + /* key encoded in uncompressed form */
|
| + return((publicValue->len - 1)/2);
|
| + } else if ( (publicValue->data[0] == 0x02) ||
|
| + (publicValue->data[0] == 0x03)) {
|
| + /* key encoded in compressed form */
|
| + return(publicValue->len - 1);
|
| + }
|
| + /* key encoding not recognized */
|
| + return(0);
|
| +}
|
| +
|
| static PK11SymKey *
|
| pk11_PubDeriveECKeyWithKDF(
|
| SECKEYPrivateKey *privKey, SECKEYPublicKey *pubKey,
|
| @@ -1747,6 +2022,7 @@
|
| {
|
| PK11SlotInfo *slot = privKey->pkcs11Slot;
|
| PK11SymKey *symKey;
|
| + PK11SymKey *SharedSecret;
|
| CK_MECHANISM mechanism;
|
| CK_RV crv;
|
| CK_BBOOL cktrue = CK_TRUE;
|
| @@ -1762,7 +2038,9 @@
|
| PORT_SetError(SEC_ERROR_BAD_KEY);
|
| return NULL;
|
| }
|
| - if ((kdf < CKD_NULL) || (kdf > CKD_SHA1_KDF)) {
|
| + if ((kdf != CKD_NULL) && (kdf != CKD_SHA1_KDF) &&
|
| + (kdf != CKD_SHA224_KDF) && (kdf != CKD_SHA256_KDF) &&
|
| + (kdf != CKD_SHA384_KDF) && (kdf != CKD_SHA512_KDF)) {
|
| PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
|
| return NULL;
|
| }
|
| @@ -1785,18 +2063,34 @@
|
| keyType = PK11_GetKeyType(target,keySize);
|
| key_size = keySize;
|
| if (key_size == 0) {
|
| - if (pk11_GetPredefinedKeyLength(keyType)) {
|
| + if ((key_size = pk11_GetPredefinedKeyLength(keyType))) {
|
| templateCount --;
|
| } else {
|
| /* sigh, some tokens can't figure this out and require
|
| * CKA_VALUE_LEN to be set */
|
| switch (kdf) {
|
| case CKD_NULL:
|
| - key_size = (pubKey->u.ec.publicValue.len-1)/2;
|
| + key_size = pk11_ECPubKeySize(&pubKey->u.ec.publicValue);
|
| + if (key_size == 0) {
|
| + PK11_FreeSymKey(symKey);
|
| + return NULL;
|
| + }
|
| break;
|
| case CKD_SHA1_KDF:
|
| key_size = SHA1_LENGTH;
|
| break;
|
| + case CKD_SHA224_KDF:
|
| + key_size = SHA224_LENGTH;
|
| + break;
|
| + case CKD_SHA256_KDF:
|
| + key_size = SHA256_LENGTH;
|
| + break;
|
| + case CKD_SHA384_KDF:
|
| + key_size = SHA384_LENGTH;
|
| + break;
|
| + case CKD_SHA512_KDF:
|
| + key_size = SHA512_LENGTH;
|
| + break;
|
| default:
|
| PORT_Assert(!"Invalid CKD");
|
| PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
|
| @@ -1832,7 +2126,7 @@
|
| pk11_ExitKeyMonitor(symKey);
|
|
|
| /* old PKCS #11 spec was ambiguous on what needed to be passed,
|
| - * try this again with and encoded public key */
|
| + * try this again with an encoded public key */
|
| if (crv != CKR_OK) {
|
| SECItem *pubValue = SEC_ASN1EncodeItem(NULL, NULL,
|
| &pubKey->u.ec.publicValue,
|
| @@ -1849,6 +2143,60 @@
|
| templateCount, &symKey->objectID);
|
| pk11_ExitKeyMonitor(symKey);
|
|
|
| + if ((crv != CKR_OK) && (kdf != CKD_NULL)) {
|
| + /* Some PKCS #11 libraries cannot perform the key derivation
|
| + * function. So, try calling C_DeriveKey with CKD_NULL and then
|
| + * performing the KDF separately.
|
| + */
|
| + CK_ULONG derivedKeySize = key_size;
|
| +
|
| + keyType = CKK_GENERIC_SECRET;
|
| + key_size = pk11_ECPubKeySize(&pubKey->u.ec.publicValue);
|
| + if (key_size == 0) {
|
| + SECITEM_FreeItem(pubValue,PR_TRUE);
|
| + goto loser;
|
| + }
|
| + SharedSecret = symKey;
|
| + SharedSecret->size = key_size;
|
| +
|
| + mechParams->kdf = CKD_NULL;
|
| + mechParams->ulSharedDataLen = 0;
|
| + mechParams->pSharedData = NULL;
|
| + mechParams->ulPublicDataLen = pubKey->u.ec.publicValue.len;
|
| + mechParams->pPublicData = pubKey->u.ec.publicValue.data;
|
| +
|
| + pk11_EnterKeyMonitor(SharedSecret);
|
| + crv = PK11_GETTAB(slot)->C_DeriveKey(SharedSecret->session,
|
| + &mechanism, privKey->pkcs11ID, keyTemplate,
|
| + templateCount, &SharedSecret->objectID);
|
| + pk11_ExitKeyMonitor(SharedSecret);
|
| +
|
| + if (crv != CKR_OK) {
|
| + /* old PKCS #11 spec was ambiguous on what needed to be passed,
|
| + * try this one final time with an encoded public key */
|
| + mechParams->ulPublicDataLen = pubValue->len;
|
| + mechParams->pPublicData = pubValue->data;
|
| +
|
| + pk11_EnterKeyMonitor(SharedSecret);
|
| + crv = PK11_GETTAB(slot)->C_DeriveKey(SharedSecret->session,
|
| + &mechanism, privKey->pkcs11ID, keyTemplate,
|
| + templateCount, &SharedSecret->objectID);
|
| + pk11_ExitKeyMonitor(SharedSecret);
|
| + }
|
| +
|
| + /* Perform KDF. */
|
| + if (crv == CKR_OK) {
|
| + symKey = pk11_ANSIX963Derive(SharedSecret, kdf,
|
| + sharedData, target, operation,
|
| + derivedKeySize);
|
| + PK11_FreeSymKey(SharedSecret);
|
| + if (symKey == NULL) {
|
| + SECITEM_FreeItem(pubValue,PR_TRUE);
|
| + PORT_ZFree(mechParams, sizeof(CK_ECDH1_DERIVE_PARAMS));
|
| + return NULL;
|
| + }
|
| + }
|
| + }
|
| SECITEM_FreeItem(pubValue,PR_TRUE);
|
| }
|
|
|
|
|