+ // Remove keys that may have been created already when the function fails.
+ if (rv != CKR_OK)
+ {
+ if (*phPrivateKey != CK_INVALID_HANDLE)
+ {
+ OSObject* ospriv = (OSObject*)handleManager->getObject(*phPrivateKey);
+ handleManager->destroyObject(*phPrivateKey);
+ if (ospriv) ospriv->destroyObject();
+ *phPrivateKey = CK_INVALID_HANDLE;
+ }
+
+ if (*phPublicKey != CK_INVALID_HANDLE)
+ {
+ OSObject* ospub = (OSObject*)handleManager->getObject(*phPublicKey);
+ handleManager->destroyObject(*phPublicKey);
+ if (ospub) ospub->destroyObject();
+ *phPublicKey = CK_INVALID_HANDLE;
+ }
+ }
+
+ return rv;
+}
+
+// Derive a DH secret
+CK_RV SoftHSM::deriveDH
+(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hBaseKey,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phKey,
+ CK_KEY_TYPE keyType,
+ CK_BBOOL isOnToken,
+ CK_BBOOL isPrivate)
+{
+ *phKey = CK_INVALID_HANDLE;
+
+ if (pMechanism->pParameter == NULL_PTR) return CKR_MECHANISM_PARAM_INVALID;
+ if (pMechanism->ulParameterLen == 0) return CKR_MECHANISM_PARAM_INVALID;
+
+ // Get the session
+ Session* session = (Session*)handleManager->getSession(hSession);
+ if (session == NULL)
+ return CKR_SESSION_HANDLE_INVALID;
+
+ // Get the token
+ Token* token = session->getToken();
+ if (token == NULL)
+ return CKR_GENERAL_ERROR;
+
+ // Extract desired parameter information
+ size_t byteLen = 0;
+ bool checkValue = true;
+ for (CK_ULONG i = 0; i < ulCount; i++)
+ {
+ switch (pTemplate[i].type)
+ {
+ case CKA_VALUE:
+ INFO_MSG("CKA_VALUE must not be included");
+ return CKR_ATTRIBUTE_READ_ONLY;
+ case CKA_VALUE_LEN:
+ if (pTemplate[i].ulValueLen != sizeof(CK_ULONG))
+ {
+ INFO_MSG("CKA_VALUE_LEN does not have the size of CK_ULONG");
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ byteLen = *(CK_ULONG*)pTemplate[i].pValue;
+ break;
+ case CKA_CHECK_VALUE:
+ if (pTemplate[i].ulValueLen > 0)
+ {
+ INFO_MSG("CKA_CHECK_VALUE must be a no-value (0 length) entry");
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ checkValue = false;
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Check the length
+ switch (keyType)
+ {
+ case CKK_GENERIC_SECRET:
+ if (byteLen == 0)
+ {
+ INFO_MSG("CKA_VALUE_LEN must be set");
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ break;
+#ifndef WITH_FIPS
+ case CKK_DES:
+ if (byteLen != 0)
+ {
+ INFO_MSG("CKA_VALUE_LEN must not be set");
+ return CKR_ATTRIBUTE_READ_ONLY;
+ }
+ byteLen = 8;
+ break;
+#endif
+ case CKK_DES2:
+ if (byteLen != 0)
+ {
+ INFO_MSG("CKA_VALUE_LEN must not be set");
+ return CKR_ATTRIBUTE_READ_ONLY;
+ }
+ byteLen = 16;
+ break;
+ case CKK_DES3:
+ if (byteLen != 0)
+ {
+ INFO_MSG("CKA_VALUE_LEN must not be set");
+ return CKR_ATTRIBUTE_READ_ONLY;
+ }
+ byteLen = 24;
+ break;
+ case CKK_AES:
+ if (byteLen != 16 && byteLen != 24 && byteLen != 32)
+ {
+ INFO_MSG("CKA_VALUE_LEN must be 16, 24, or 32");
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ break;
+ default:
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ // Get the base key handle
+ OSObject *baseKey = (OSObject *)handleManager->getObject(hBaseKey);
+ if (baseKey == NULL || !baseKey->isValid())
+ return CKR_KEY_HANDLE_INVALID;
+
+ // Get the DH algorithm handler
+ AsymmetricAlgorithm* dh = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::DH);
+ if (dh == NULL)
+ return CKR_MECHANISM_INVALID;
+
+ // Get the keys
+ PrivateKey* privateKey = dh->newPrivateKey();
+ if (privateKey == NULL)
+ {
+ CryptoFactory::i()->recycleAsymmetricAlgorithm(dh);
+ return CKR_HOST_MEMORY;
+ }
+ if (getDHPrivateKey((DHPrivateKey*)privateKey, token, baseKey) != CKR_OK)
+ {
+ dh->recyclePrivateKey(privateKey);
+ CryptoFactory::i()->recycleAsymmetricAlgorithm(dh);
+ return CKR_GENERAL_ERROR;
+ }
+
+ ByteString mechParameters;
+ mechParameters.resize(pMechanism->ulParameterLen);
+ memcpy(&mechParameters[0], pMechanism->pParameter, pMechanism->ulParameterLen);
+ PublicKey* publicKey = dh->newPublicKey();
+ if (publicKey == NULL)
+ {
+ dh->recyclePrivateKey(privateKey);
+ CryptoFactory::i()->recycleAsymmetricAlgorithm(dh);
+ return CKR_HOST_MEMORY;
+ }
+ if (getDHPublicKey((DHPublicKey*)publicKey, (DHPrivateKey*)privateKey, mechParameters) != CKR_OK)
+ {
+ dh->recyclePrivateKey(privateKey);
+ dh->recyclePublicKey(publicKey);
+ CryptoFactory::i()->recycleAsymmetricAlgorithm(dh);
+ return CKR_GENERAL_ERROR;
+ }
+
+ // Derive the secret
+ SymmetricKey* secret = NULL;
+ CK_RV rv = CKR_OK;
+ if (!dh->deriveKey(&secret, publicKey, privateKey))
+ rv = CKR_GENERAL_ERROR;
+ dh->recyclePrivateKey(privateKey);
+ dh->recyclePublicKey(publicKey);
+
+ // Create the secret object using C_CreateObject
+ const CK_ULONG maxAttribs = 32;
+ CK_OBJECT_CLASS objClass = CKO_SECRET_KEY;
+ CK_ATTRIBUTE secretAttribs[maxAttribs] = {
+ { CKA_CLASS, &objClass, sizeof(objClass) },
+ { CKA_TOKEN, &isOnToken, sizeof(isOnToken) },
+ { CKA_PRIVATE, &isPrivate, sizeof(isPrivate) },
+ { CKA_KEY_TYPE, &keyType, sizeof(keyType) },
+ };
+ CK_ULONG secretAttribsCount = 4;
+
+ // Add the additional
+ if (ulCount > (maxAttribs - secretAttribsCount))
+ rv = CKR_TEMPLATE_INCONSISTENT;
+ for (CK_ULONG i=0; i < ulCount && rv == CKR_OK; ++i)
+ {
+ switch (pTemplate[i].type)
+ {
+ case CKA_CLASS:
+ case CKA_TOKEN:
+ case CKA_PRIVATE:
+ case CKA_KEY_TYPE:
+ case CKA_CHECK_VALUE:
+ continue;
+ default:
+ secretAttribs[secretAttribsCount++] = pTemplate[i];
+ }
+ }
+
+ if (rv == CKR_OK)
+ rv = this->CreateObject(hSession, secretAttribs, secretAttribsCount, phKey, OBJECT_OP_DERIVE);
+
+ // Store the attributes that are being supplied
+ if (rv == CKR_OK)
+ {
+ OSObject* osobject = (OSObject*)handleManager->getObject(*phKey);
+ if (osobject == NULL_PTR || !osobject->isValid()) {
+ rv = CKR_FUNCTION_FAILED;
+ } else if (osobject->startTransaction()) {
+ bool bOK = true;
+
+ // Common Attributes
+ bOK = bOK && osobject->setAttribute(CKA_LOCAL,false);
+
+ // Common Secret Key Attributes
+ if (baseKey->getBooleanValue(CKA_ALWAYS_SENSITIVE, false))
+ {
+ bool bAlwaysSensitive = osobject->getBooleanValue(CKA_SENSITIVE, false);
+ bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,bAlwaysSensitive);
+ }
+ else
+ {
+ bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,false);
+ }
+ if (baseKey->getBooleanValue(CKA_NEVER_EXTRACTABLE, true))
+ {
+ bool bNeverExtractable = osobject->getBooleanValue(CKA_EXTRACTABLE, false) == false;
+ bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE,bNeverExtractable);
+ }
+ else
+ {
+ bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE,false);
+ }
+
+ // Secret Attributes
+ ByteString secretValue = secret->getKeyBits();
+ ByteString value;
+ ByteString plainKCV;
+ ByteString kcv;
+
+ if (byteLen > secretValue.size())
+ {
+ INFO_MSG("The derived secret is too short");
+ bOK = false;
+ }
+ else
+ {
+ // Truncate value when requested, remove from the leading end
+ if (byteLen < secretValue.size())
+ secretValue.split(secretValue.size() - byteLen);
+
+ // Fix the odd parity for DES
+ if (keyType == CKK_DES ||
+ keyType == CKK_DES2 ||
+ keyType == CKK_DES3)
+ {
+ for (size_t i = 0; i < secretValue.size(); i++)
+ {
+ secretValue[i] = odd_parity[secretValue[i]];
+ }
+ }
+
+ // Get the KCV
+ switch (keyType)
+ {
+ case CKK_GENERIC_SECRET:
+ secret->setBitLen(byteLen * 8);
+ plainKCV = secret->getKeyCheckValue();
+ break;
+ case CKK_DES:
+ case CKK_DES2:
+ case CKK_DES3:
+ secret->setBitLen(byteLen * 7);
+ plainKCV = ((DESKey*)secret)->getKeyCheckValue();
+ break;
+ case CKK_AES:
+ secret->setBitLen(byteLen * 8);
+ plainKCV = ((AESKey*)secret)->getKeyCheckValue();
+ break;
+ default:
+ bOK = false;
+ break;
+ }
+
+ if (isPrivate)
+ {
+ token->encrypt(secretValue, value);
+ token->encrypt(plainKCV, kcv);
+ }
+ else
+ {
+ value = secretValue;
+ kcv = plainKCV;
+ }
+ }
+ bOK = bOK && osobject->setAttribute(CKA_VALUE, value);
+ if (checkValue)
+ bOK = bOK && osobject->setAttribute(CKA_CHECK_VALUE, kcv);
+
+ if (bOK)
+ bOK = osobject->commitTransaction();
+ else
+ osobject->abortTransaction();
+
+ if (!bOK)
+ rv = CKR_FUNCTION_FAILED;
+ } else
+ rv = CKR_FUNCTION_FAILED;
+ }
+
+ // Clean up
+ dh->recycleSymmetricKey(secret);
+ CryptoFactory::i()->recycleAsymmetricAlgorithm(dh);
+
+ // Remove secret that may have been created already when the function fails.