diff options
Diffstat (limited to 'SoftHSMv2/src/lib/SoftHSM.cpp')
-rw-r--r-- | SoftHSMv2/src/lib/SoftHSM.cpp | 11178 |
1 files changed, 11178 insertions, 0 deletions
diff --git a/SoftHSMv2/src/lib/SoftHSM.cpp b/SoftHSMv2/src/lib/SoftHSM.cpp new file mode 100644 index 0000000..b06efc2 --- /dev/null +++ b/SoftHSMv2/src/lib/SoftHSM.cpp @@ -0,0 +1,11178 @@ +/* + * Copyright (c) 2010 SURFnet bv + * Copyright (c) 2010 .SE (The Internet Infrastructure Foundation) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/***************************************************************************** + SoftHSM.cpp + + The implementation of the SoftHSM's main class + *****************************************************************************/ + +#include "config.h" +#include "log.h" +#include "access.h" +#include "Configuration.h" +#include "SimpleConfigLoader.h" +#include "MutexFactory.h" +#include "SecureMemoryRegistry.h" +#include "CryptoFactory.h" +#include "AsymmetricAlgorithm.h" +#include "SymmetricAlgorithm.h" +#include "AESKey.h" +#include "DESKey.h" +#include "RNG.h" +#include "RSAParameters.h" +#include "RSAPublicKey.h" +#include "RSAPrivateKey.h" +#include "DSAParameters.h" +#include "DSAPublicKey.h" +#include "DSAPrivateKey.h" +#include "ECPublicKey.h" +#include "ECPrivateKey.h" +#include "ECParameters.h" +#include "DHParameters.h" +#include "DHPublicKey.h" +#include "DHPrivateKey.h" +#include "GOSTPublicKey.h" +#include "GOSTPrivateKey.h" +#include "cryptoki.h" +#include "SoftHSM.h" +#include "osmutex.h" +#include "SessionManager.h" +#include "SessionObjectStore.h" +#include "HandleManager.h" +#include "P11Objects.h" +#include "odd.h" + +#if defined(WITH_OPENSSL) +#include "OSSLCryptoFactory.h" +#else +#include "BotanCryptoFactory.h" +#endif + +#include <stdlib.h> + +// Initialise the one-and-only instance + +#ifdef HAVE_CXX11 + +std::unique_ptr<MutexFactory> MutexFactory::instance(nullptr); +std::unique_ptr<SecureMemoryRegistry> SecureMemoryRegistry::instance(nullptr); +#if defined(WITH_OPENSSL) +std::unique_ptr<OSSLCryptoFactory> OSSLCryptoFactory::instance(nullptr); +#else +std::unique_ptr<BotanCryptoFactory> BotanCryptoFactory::instance(nullptr); +#endif +std::unique_ptr<SoftHSM> SoftHSM::instance(nullptr); + +#else + +std::auto_ptr<MutexFactory> MutexFactory::instance(NULL); +std::auto_ptr<SecureMemoryRegistry> SecureMemoryRegistry::instance(NULL); +#if defined(WITH_OPENSSL) +std::auto_ptr<OSSLCryptoFactory> OSSLCryptoFactory::instance(NULL); +#else +std::auto_ptr<BotanCryptoFactory> BotanCryptoFactory::instance(NULL); +#endif +std::auto_ptr<SoftHSM> SoftHSM::instance(NULL); + +#endif + +static CK_RV newP11Object(CK_OBJECT_CLASS objClass, CK_KEY_TYPE keyType, CK_CERTIFICATE_TYPE certType, P11Object **p11object) +{ + switch(objClass) { + case CKO_DATA: + *p11object = new P11DataObj(); + break; + case CKO_CERTIFICATE: + if (certType == CKC_X_509) + *p11object = new P11X509CertificateObj(); + else if (certType == CKC_OPENPGP) + *p11object = new P11OpenPGPPublicKeyObj(); + else + return CKR_ATTRIBUTE_VALUE_INVALID; + break; + case CKO_PUBLIC_KEY: + if (keyType == CKK_RSA) + *p11object = new P11RSAPublicKeyObj(); + else if (keyType == CKK_DSA) + *p11object = new P11DSAPublicKeyObj(); + else if (keyType == CKK_EC) + *p11object = new P11ECPublicKeyObj(); + else if (keyType == CKK_DH) + *p11object = new P11DHPublicKeyObj(); + else if (keyType == CKK_GOSTR3410) + *p11object = new P11GOSTPublicKeyObj(); + else + return CKR_ATTRIBUTE_VALUE_INVALID; + break; + case CKO_PRIVATE_KEY: + // we need to know the type too + if (keyType == CKK_RSA) + *p11object = new P11RSAPrivateKeyObj(); + else if (keyType == CKK_DSA) + *p11object = new P11DSAPrivateKeyObj(); + else if (keyType == CKK_EC) + *p11object = new P11ECPrivateKeyObj(); + else if (keyType == CKK_DH) + *p11object = new P11DHPrivateKeyObj(); + else if (keyType == CKK_GOSTR3410) + *p11object = new P11GOSTPrivateKeyObj(); + else + return CKR_ATTRIBUTE_VALUE_INVALID; + break; + case CKO_SECRET_KEY: + if ((keyType == CKK_GENERIC_SECRET) || + (keyType == CKK_MD5_HMAC) || + (keyType == CKK_SHA_1_HMAC) || + (keyType == CKK_SHA224_HMAC) || + (keyType == CKK_SHA256_HMAC) || + (keyType == CKK_SHA384_HMAC) || + (keyType == CKK_SHA512_HMAC)) + { + P11GenericSecretKeyObj* key = new P11GenericSecretKeyObj(); + *p11object = key; + key->setKeyType(keyType); + } + else if (keyType == CKK_AES) + { + *p11object = new P11AESSecretKeyObj(); + } + else if ((keyType == CKK_DES) || + (keyType == CKK_DES2) || + (keyType == CKK_DES3)) + { + P11DESSecretKeyObj* key = new P11DESSecretKeyObj(); + *p11object = key; + key->setKeyType(keyType); + } + else if (keyType == CKK_GOST28147) + { + *p11object = new P11GOSTSecretKeyObj(); + } + else + return CKR_ATTRIBUTE_VALUE_INVALID; + break; + case CKO_DOMAIN_PARAMETERS: + if (keyType == CKK_DSA) + *p11object = new P11DSADomainObj(); + else if (keyType == CKK_DH) + *p11object = new P11DHDomainObj(); + else + return CKR_ATTRIBUTE_VALUE_INVALID; + break; + default: + return CKR_ATTRIBUTE_VALUE_INVALID; // invalid value for a valid argument + } + return CKR_OK; +} + +static CK_RV extractObjectInformation(CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount, + CK_OBJECT_CLASS &objClass, + CK_KEY_TYPE &keyType, + CK_CERTIFICATE_TYPE &certType, + CK_BBOOL &isOnToken, + CK_BBOOL &isPrivate, + bool bImplicit) +{ + bool bHasClass = false; + bool bHasKeyType = false; + bool bHasCertType = false; + bool bHasPrivate = false; + + // Extract object information + for (CK_ULONG i = 0; i < ulCount; ++i) + { + switch (pTemplate[i].type) + { + case CKA_CLASS: + if (pTemplate[i].ulValueLen == sizeof(CK_OBJECT_CLASS)) + { + objClass = *(CK_OBJECT_CLASS_PTR)pTemplate[i].pValue; + bHasClass = true; + } + break; + case CKA_KEY_TYPE: + if (pTemplate[i].ulValueLen == sizeof(CK_KEY_TYPE)) + { + keyType = *(CK_KEY_TYPE*)pTemplate[i].pValue; + bHasKeyType = true; + } + break; + case CKA_CERTIFICATE_TYPE: + if (pTemplate[i].ulValueLen == sizeof(CK_CERTIFICATE_TYPE)) + { + certType = *(CK_CERTIFICATE_TYPE*)pTemplate[i].pValue; + bHasCertType = true; + } + break; + case CKA_TOKEN: + if (pTemplate[i].ulValueLen == sizeof(CK_BBOOL)) + { + isOnToken = *(CK_BBOOL*)pTemplate[i].pValue; + } + break; + case CKA_PRIVATE: + if (pTemplate[i].ulValueLen == sizeof(CK_BBOOL)) + { + isPrivate = *(CK_BBOOL*)pTemplate[i].pValue; + bHasPrivate = true; + } + break; + default: + break; + } + } + + if (bImplicit) + { + return CKR_OK; + } + + if (!bHasClass) + { + return CKR_TEMPLATE_INCOMPLETE; + } + + bool bKeyTypeRequired = (objClass == CKO_PUBLIC_KEY || objClass == CKO_PRIVATE_KEY || objClass == CKO_SECRET_KEY); + if (bKeyTypeRequired && !bHasKeyType) + { + return CKR_TEMPLATE_INCOMPLETE; + } + + if (objClass == CKO_CERTIFICATE) + { + if (!bHasCertType) + { + return CKR_TEMPLATE_INCOMPLETE; + } + if (!bHasPrivate) + { + // Change default value for certificates + isPrivate = CK_FALSE; + } + } + + if (objClass == CKO_PUBLIC_KEY && !bHasPrivate) + { + // Change default value for public keys + isPrivate = CK_FALSE; + } + + return CKR_OK; +} + +static CK_RV newP11Object(OSObject *object, P11Object **p11object) +{ + CK_OBJECT_CLASS objClass = object->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED); + CK_KEY_TYPE keyType = CKK_RSA; + CK_CERTIFICATE_TYPE certType = CKC_X_509; + if (object->attributeExists(CKA_KEY_TYPE)) + keyType = object->getUnsignedLongValue(CKA_KEY_TYPE, CKK_RSA); + if (object->attributeExists(CKA_CERTIFICATE_TYPE)) + certType = object->getUnsignedLongValue(CKA_CERTIFICATE_TYPE, CKC_X_509); + CK_RV rv = newP11Object(objClass,keyType,certType,p11object); + if (rv != CKR_OK) + return rv; + if (!(*p11object)->init(object)) + return CKR_GENERAL_ERROR; // something went wrong that shouldn't have. + return CKR_OK; +} + +#ifdef notyet +static CK_ATTRIBUTE bsAttribute(CK_ATTRIBUTE_TYPE type, const ByteString &value) +{ + CK_ATTRIBUTE attr = {type, (CK_VOID_PTR)value.const_byte_str(), value.size() }; + return attr; +} +#endif + +/***************************************************************************** + Implementation of SoftHSM class specific functions + *****************************************************************************/ + +// Return the one-and-only instance +SoftHSM* SoftHSM::i() +{ + if (!instance.get()) + { + instance.reset(new SoftHSM()); + } + + return instance.get(); +} + +void SoftHSM::reset() +{ + if (instance.get()) + instance.reset(); +} + +// Constructor +SoftHSM::SoftHSM() +{ + isInitialised = false; + isRemovable = false; + sessionObjectStore = NULL; + objectStore = NULL; + slotManager = NULL; + sessionManager = NULL; + handleManager = NULL; +} + +// Destructor +SoftHSM::~SoftHSM() +{ + if (handleManager != NULL) delete handleManager; + if (sessionManager != NULL) delete sessionManager; + if (slotManager != NULL) delete slotManager; + if (objectStore != NULL) delete objectStore; + if (sessionObjectStore != NULL) delete sessionObjectStore; +} + +/***************************************************************************** + Implementation of PKCS #11 functions + *****************************************************************************/ + +// PKCS #11 initialisation function +CK_RV SoftHSM::C_Initialize(CK_VOID_PTR pInitArgs) +{ + CK_C_INITIALIZE_ARGS_PTR args; + + // Check if PKCS#11 is already initialized + if (isInitialised) + { + ERROR_MSG("SoftHSM is already initialized"); + return CKR_CRYPTOKI_ALREADY_INITIALIZED; + } + + // Do we have any arguments? + if (pInitArgs != NULL_PTR) + { + args = (CK_C_INITIALIZE_ARGS_PTR)pInitArgs; + + // Must be set to NULL_PTR in this version of PKCS#11 + if (args->pReserved != NULL_PTR) + { + ERROR_MSG("pReserved must be set to NULL_PTR"); + return CKR_ARGUMENTS_BAD; + } + + // Can we spawn our own threads? + // if (args->flags & CKF_LIBRARY_CANT_CREATE_OS_THREADS) + // { + // DEBUG_MSG("Cannot create threads if CKF_LIBRARY_CANT_CREATE_OS_THREADS is set"); + // return CKR_NEED_TO_CREATE_THREADS; + // } + + // Are we not supplied with mutex functions? + if + ( + args->CreateMutex == NULL_PTR && + args->DestroyMutex == NULL_PTR && + args->LockMutex == NULL_PTR && + args->UnlockMutex == NULL_PTR + ) + { + // Can we use our own mutex functions? + if (args->flags & CKF_OS_LOCKING_OK) + { + // Use our own mutex functions. + MutexFactory::i()->setCreateMutex(OSCreateMutex); + MutexFactory::i()->setDestroyMutex(OSDestroyMutex); + MutexFactory::i()->setLockMutex(OSLockMutex); + MutexFactory::i()->setUnlockMutex(OSUnlockMutex); + MutexFactory::i()->enable(); + } + else + { + // The external application is not using threading + MutexFactory::i()->disable(); + } + } + else + { + // We must have all mutex functions + if + ( + args->CreateMutex == NULL_PTR || + args->DestroyMutex == NULL_PTR || + args->LockMutex == NULL_PTR || + args->UnlockMutex == NULL_PTR + ) + { + ERROR_MSG("Not all mutex functions are supplied"); + return CKR_ARGUMENTS_BAD; + } + + // We could use our own mutex functions if the flag is set, + // but we use the external functions in both cases. + + // Load the external mutex functions + MutexFactory::i()->setCreateMutex(args->CreateMutex); + MutexFactory::i()->setDestroyMutex(args->DestroyMutex); + MutexFactory::i()->setLockMutex(args->LockMutex); + MutexFactory::i()->setUnlockMutex(args->UnlockMutex); + MutexFactory::i()->enable(); + } + } + else + { + // No concurrent access by multiple threads + MutexFactory::i()->disable(); + } + + // Initiate SecureMemoryRegistry + if (SecureMemoryRegistry::i() == NULL) + { + ERROR_MSG("Could not load the SecureMemoryRegistry"); + return CKR_GENERAL_ERROR; + } + + // Build the CryptoFactory + if (CryptoFactory::i() == NULL) + { + ERROR_MSG("Could not load the CryptoFactory"); + return CKR_GENERAL_ERROR; + } + +#ifdef WITH_FIPS + // Check the FIPS status + if (!CryptoFactory::i()->getFipsSelfTestStatus()) + { + ERROR_MSG("The FIPS self test failed"); + return CKR_FIPS_SELF_TEST_FAILED; + } +#endif + + // (Re)load the configuration + if (!Configuration::i()->reload(SimpleConfigLoader::i())) + { + ERROR_MSG("Could not load the configuration"); + return CKR_GENERAL_ERROR; + } + + // Configure the log level + if (!setLogLevel(Configuration::i()->getString("log.level", DEFAULT_LOG_LEVEL))) + { + ERROR_MSG("Could not set the log level"); + return CKR_GENERAL_ERROR; + } + + // Configure object store storage backend used by all tokens. + if (!ObjectStoreToken::selectBackend(Configuration::i()->getString("objectstore.backend", DEFAULT_OBJECTSTORE_BACKEND))) + { + ERROR_MSG("Could not set the storage backend"); + return CKR_GENERAL_ERROR; + } + + sessionObjectStore = new SessionObjectStore(); + + // Load the object store + objectStore = new ObjectStore(Configuration::i()->getString("directories.tokendir", DEFAULT_TOKENDIR)); + if (!objectStore->isValid()) + { + WARNING_MSG("Could not load the object store"); + delete objectStore; + objectStore = NULL; + delete sessionObjectStore; + sessionObjectStore = NULL; + return CKR_GENERAL_ERROR; + } + + isRemovable = Configuration::i()->getBool("slots.removable", false); + + // Load the slot manager + slotManager = new SlotManager(objectStore); + + // Load the session manager + sessionManager = new SessionManager(); + + // Load the handle manager + handleManager = new HandleManager(); + + // Set the state to initialised + isInitialised = true; + + return CKR_OK; +} + +// PKCS #11 finalisation function +CK_RV SoftHSM::C_Finalize(CK_VOID_PTR pReserved) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Must be set to NULL_PTR in this version of PKCS#11 + if (pReserved != NULL_PTR) return CKR_ARGUMENTS_BAD; + + if (handleManager != NULL) delete handleManager; + handleManager = NULL; + if (sessionManager != NULL) delete sessionManager; + sessionManager = NULL; + if (slotManager != NULL) delete slotManager; + slotManager = NULL; + if (objectStore != NULL) delete objectStore; + objectStore = NULL; + if (sessionObjectStore != NULL) delete sessionObjectStore; + sessionObjectStore = NULL; + CryptoFactory::reset(); + SecureMemoryRegistry::reset(); + + isInitialised = false; + + SoftHSM::reset(); + return CKR_OK; +} + +// Return information about the PKCS #11 module +CK_RV SoftHSM::C_GetInfo(CK_INFO_PTR pInfo) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + if (pInfo == NULL_PTR) return CKR_ARGUMENTS_BAD; + + pInfo->cryptokiVersion.major = CRYPTOKI_VERSION_MAJOR; + pInfo->cryptokiVersion.minor = CRYPTOKI_VERSION_MINOR; + memset(pInfo->manufacturerID, ' ', 32); + memcpy(pInfo->manufacturerID, "SoftHSM", 7); + pInfo->flags = 0; + memset(pInfo->libraryDescription, ' ', 32); +#ifdef WITH_FIPS + memcpy(pInfo->libraryDescription, "Implementation of PKCS11+FIPS", 29); +#else + memcpy(pInfo->libraryDescription, "Implementation of PKCS11", 24); +#endif + pInfo->libraryVersion.major = VERSION_MAJOR; + pInfo->libraryVersion.minor = VERSION_MINOR; + + return CKR_OK; +} + +// Return a list of available slots +CK_RV SoftHSM::C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + return slotManager->getSlotList(objectStore, tokenPresent, pSlotList, pulCount); +} + +// Return information about a slot +CK_RV SoftHSM::C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) +{ + CK_RV rv; + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + Slot* slot = slotManager->getSlot(slotID); + if (slot == NULL) + { + return CKR_SLOT_ID_INVALID; + } + + rv = slot->getSlotInfo(pInfo); + if (rv != CKR_OK) { + return rv; + } + + if (isRemovable) { + pInfo->flags |= CKF_REMOVABLE_DEVICE; + } + + return CKR_OK; +} + +// Return information about a token in a slot +CK_RV SoftHSM::C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + Slot* slot = slotManager->getSlot(slotID); + if (slot == NULL) + { + return CKR_SLOT_ID_INVALID; + } + + Token* token = slot->getToken(); + if (token == NULL) + { + return CKR_TOKEN_NOT_PRESENT; + } + + return token->getTokenInfo(pInfo); +} + +// Return the list of supported mechanisms for a given slot +CK_RV SoftHSM::C_GetMechanismList(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG_PTR pulCount) +{ + // A list with the supported mechanisms + CK_ULONG nrSupportedMechanisms = 61; +#ifdef WITH_ECC + nrSupportedMechanisms += 3; +#endif +#ifdef WITH_FIPS + nrSupportedMechanisms -= 9; +#endif +#ifdef WITH_GOST + nrSupportedMechanisms += 5; +#endif +#ifdef HAVE_AES_KEY_WRAP_PAD + nrSupportedMechanisms += 1; +#endif +#ifdef WITH_RAW_PSS + nrSupportedMechanisms += 1; // CKM_RSA_PKCS_PSS +#endif +#ifdef WITH_AES_GCM + nrSupportedMechanisms += 1; +#endif + + CK_MECHANISM_TYPE supportedMechanisms[] = + { +#ifndef WITH_FIPS + CKM_MD5, +#endif + CKM_SHA_1, + CKM_SHA224, + CKM_SHA256, + CKM_SHA384, + CKM_SHA512, +#ifndef WITH_FIPS + CKM_MD5_HMAC, +#endif + CKM_SHA_1_HMAC, + CKM_SHA224_HMAC, + CKM_SHA256_HMAC, + CKM_SHA384_HMAC, + CKM_SHA512_HMAC, + CKM_RSA_PKCS_KEY_PAIR_GEN, + CKM_RSA_PKCS, + CKM_RSA_X_509, +#ifndef WITH_FIPS + CKM_MD5_RSA_PKCS, +#endif + CKM_SHA1_RSA_PKCS, + CKM_RSA_PKCS_OAEP, + CKM_SHA224_RSA_PKCS, + CKM_SHA256_RSA_PKCS, + CKM_SHA384_RSA_PKCS, + CKM_SHA512_RSA_PKCS, +#ifdef WITH_RAW_PSS + CKM_RSA_PKCS_PSS, +#endif + CKM_SHA1_RSA_PKCS_PSS, + CKM_SHA224_RSA_PKCS_PSS, + CKM_SHA256_RSA_PKCS_PSS, + CKM_SHA384_RSA_PKCS_PSS, + CKM_SHA512_RSA_PKCS_PSS, +#ifndef WITH_FIPS + CKM_DES_KEY_GEN, +#endif + CKM_DES2_KEY_GEN, + CKM_DES3_KEY_GEN, +#ifndef WITH_FIPS + CKM_DES_ECB, + CKM_DES_CBC, + CKM_DES_CBC_PAD, + CKM_DES_ECB_ENCRYPT_DATA, + CKM_DES_CBC_ENCRYPT_DATA, +#endif + CKM_DES3_ECB, + CKM_DES3_CBC, + CKM_DES3_CBC_PAD, + CKM_DES3_ECB_ENCRYPT_DATA, + CKM_DES3_CBC_ENCRYPT_DATA, + CKM_DES3_CMAC, + CKM_AES_KEY_GEN, + CKM_AES_ECB, + CKM_AES_CBC, + CKM_AES_CBC_PAD, + CKM_AES_CTR, +#ifdef WITH_AES_GCM + CKM_AES_GCM, +#endif + CKM_AES_KEY_WRAP, +#ifdef HAVE_AES_KEY_WRAP_PAD + CKM_AES_KEY_WRAP_PAD, +#endif + CKM_AES_ECB_ENCRYPT_DATA, + CKM_AES_CBC_ENCRYPT_DATA, + CKM_AES_CMAC, + CKM_DSA_PARAMETER_GEN, + CKM_DSA_KEY_PAIR_GEN, + CKM_DSA, + CKM_DSA_SHA1, + CKM_DSA_SHA224, + CKM_DSA_SHA256, + CKM_DSA_SHA384, + CKM_DSA_SHA512, + CKM_DH_PKCS_KEY_PAIR_GEN, + CKM_DH_PKCS_PARAMETER_GEN, + CKM_DH_PKCS_DERIVE, +#ifdef WITH_ECC + CKM_EC_KEY_PAIR_GEN, + CKM_ECDSA, + CKM_ECDH1_DERIVE, +#endif +#ifdef WITH_GOST + CKM_GOSTR3411, + CKM_GOSTR3411_HMAC, + CKM_GOSTR3410_KEY_PAIR_GEN, + CKM_GOSTR3410, + CKM_GOSTR3410_WITH_GOSTR3411 +#endif + }; + + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + if (pulCount == NULL_PTR) return CKR_ARGUMENTS_BAD; + + Slot* slot = slotManager->getSlot(slotID); + if (slot == NULL) + { + return CKR_SLOT_ID_INVALID; + } + + if (pMechanismList == NULL_PTR) + { + *pulCount = nrSupportedMechanisms; + + return CKR_OK; + } + + if (*pulCount < nrSupportedMechanisms) + { + *pulCount = nrSupportedMechanisms; + + return CKR_BUFFER_TOO_SMALL; + } + + *pulCount = nrSupportedMechanisms; + + for (CK_ULONG i = 0; i < nrSupportedMechanisms; i ++) + { + pMechanismList[i] = supportedMechanisms[i]; + } + + return CKR_OK; +} + +// Return more information about a mechanism for a given slot +CK_RV SoftHSM::C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR pInfo) +{ + unsigned long rsaMinSize, rsaMaxSize; + unsigned long dsaMinSize, dsaMaxSize; + unsigned long dhMinSize, dhMaxSize; +#ifdef WITH_ECC + unsigned long ecdsaMinSize, ecdsaMaxSize; + unsigned long ecdhMinSize, ecdhMaxSize; +#endif + + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + if (pInfo == NULL_PTR) return CKR_ARGUMENTS_BAD; + + Slot* slot = slotManager->getSlot(slotID); + if (slot == NULL) + { + return CKR_SLOT_ID_INVALID; + } + + AsymmetricAlgorithm* rsa = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::RSA); + if (rsa != NULL) + { + rsaMinSize = rsa->getMinKeySize(); + rsaMaxSize = rsa->getMaxKeySize(); + } + else + { + return CKR_GENERAL_ERROR; + } + CryptoFactory::i()->recycleAsymmetricAlgorithm(rsa); + + AsymmetricAlgorithm* dsa = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::DSA); + if (dsa != NULL) + { + dsaMinSize = dsa->getMinKeySize(); + // Limitation in PKCS#11 + if (dsaMinSize < 512) + { + dsaMinSize = 512; + } + + dsaMaxSize = dsa->getMaxKeySize(); + // Limitation in PKCS#11 + if (dsaMaxSize > 1024) + { + dsaMaxSize = 1024; + } + } + else + { + return CKR_GENERAL_ERROR; + } + CryptoFactory::i()->recycleAsymmetricAlgorithm(dsa); + + AsymmetricAlgorithm* dh = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::DH); + if (dh != NULL) + { + dhMinSize = dh->getMinKeySize(); + dhMaxSize = dh->getMaxKeySize(); + } + else + { + return CKR_GENERAL_ERROR; + } + CryptoFactory::i()->recycleAsymmetricAlgorithm(dh); + +#ifdef WITH_ECC + AsymmetricAlgorithm* ecdsa = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::ECDSA); + if (ecdsa != NULL) + { + ecdsaMinSize = ecdsa->getMinKeySize(); + ecdsaMaxSize = ecdsa->getMaxKeySize(); + } + else + { + return CKR_GENERAL_ERROR; + } + CryptoFactory::i()->recycleAsymmetricAlgorithm(ecdsa); + + AsymmetricAlgorithm* ecdh = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::ECDH); + if (ecdh != NULL) + { + ecdhMinSize = ecdh->getMinKeySize(); + ecdhMaxSize = ecdh->getMaxKeySize(); + } + else + { + return CKR_GENERAL_ERROR; + } + CryptoFactory::i()->recycleAsymmetricAlgorithm(ecdh); +#endif + + switch (type) + { +#ifndef WITH_FIPS + case CKM_MD5: +#endif + case CKM_SHA_1: + case CKM_SHA224: + case CKM_SHA256: + case CKM_SHA384: + case CKM_SHA512: + // Key size is not in use + pInfo->ulMinKeySize = 0; + pInfo->ulMaxKeySize = 0; + pInfo->flags = CKF_DIGEST; + break; +#ifndef WITH_FIPS + case CKM_MD5_HMAC: + pInfo->ulMinKeySize = 16; + pInfo->ulMaxKeySize = 512; + pInfo->flags = CKF_SIGN | CKF_VERIFY; + break; +#endif + case CKM_SHA_1_HMAC: + pInfo->ulMinKeySize = 20; + pInfo->ulMaxKeySize = 512; + pInfo->flags = CKF_SIGN | CKF_VERIFY; + break; + case CKM_SHA224_HMAC: + pInfo->ulMinKeySize = 28; + pInfo->ulMaxKeySize = 512; + pInfo->flags = CKF_SIGN | CKF_VERIFY; + break; + case CKM_SHA256_HMAC: + pInfo->ulMinKeySize = 32; + pInfo->ulMaxKeySize = 512; + pInfo->flags = CKF_SIGN | CKF_VERIFY; + break; + case CKM_SHA384_HMAC: + pInfo->ulMinKeySize = 48; + pInfo->ulMaxKeySize = 512; + pInfo->flags = CKF_SIGN | CKF_VERIFY; + break; + case CKM_SHA512_HMAC: + pInfo->ulMinKeySize = 64; + pInfo->ulMaxKeySize = 512; + pInfo->flags = CKF_SIGN | CKF_VERIFY; + break; + case CKM_RSA_PKCS_KEY_PAIR_GEN: + pInfo->ulMinKeySize = rsaMinSize; + pInfo->ulMaxKeySize = rsaMaxSize; + pInfo->flags = CKF_GENERATE_KEY_PAIR; + break; + case CKM_RSA_PKCS: + pInfo->ulMinKeySize = rsaMinSize; + pInfo->ulMaxKeySize = rsaMaxSize; + pInfo->flags = CKF_SIGN | CKF_VERIFY | CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP; + break; + case CKM_RSA_X_509: + pInfo->ulMinKeySize = rsaMinSize; + pInfo->ulMaxKeySize = rsaMaxSize; + pInfo->flags = CKF_SIGN | CKF_VERIFY | CKF_ENCRYPT | CKF_DECRYPT; + break; +#ifndef WITH_FIPS + case CKM_MD5_RSA_PKCS: +#endif + case CKM_SHA1_RSA_PKCS: + case CKM_SHA224_RSA_PKCS: + case CKM_SHA256_RSA_PKCS: + case CKM_SHA384_RSA_PKCS: + case CKM_SHA512_RSA_PKCS: +#ifdef WITH_RAW_PSS + case CKM_RSA_PKCS_PSS: +#endif + case CKM_SHA1_RSA_PKCS_PSS: + case CKM_SHA224_RSA_PKCS_PSS: + case CKM_SHA256_RSA_PKCS_PSS: + case CKM_SHA384_RSA_PKCS_PSS: + case CKM_SHA512_RSA_PKCS_PSS: + pInfo->ulMinKeySize = rsaMinSize; + pInfo->ulMaxKeySize = rsaMaxSize; + pInfo->flags = CKF_SIGN | CKF_VERIFY; + break; + case CKM_RSA_PKCS_OAEP: + pInfo->ulMinKeySize = rsaMinSize; + pInfo->ulMaxKeySize = rsaMaxSize; + pInfo->flags = CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP; + break; +#ifndef WITH_FIPS + case CKM_DES_KEY_GEN: +#endif + case CKM_DES2_KEY_GEN: + case CKM_DES3_KEY_GEN: + // Key size is not in use + pInfo->ulMinKeySize = 0; + pInfo->ulMaxKeySize = 0; + pInfo->flags = CKF_GENERATE; + break; +#ifndef WITH_FIPS + case CKM_DES_ECB: + case CKM_DES_CBC: + case CKM_DES_CBC_PAD: +#endif + case CKM_DES3_ECB: + case CKM_DES3_CBC: + case CKM_DES3_CBC_PAD: + // Key size is not in use + pInfo->ulMinKeySize = 0; + pInfo->ulMaxKeySize = 0; + pInfo->flags = CKF_ENCRYPT | CKF_DECRYPT; + break; + case CKM_DES3_CMAC: + // Key size is not in use + pInfo->ulMinKeySize = 0; + pInfo->ulMaxKeySize = 0; + pInfo->flags = CKF_SIGN | CKF_VERIFY; + break; + case CKM_AES_KEY_GEN: + pInfo->ulMinKeySize = 16; + pInfo->ulMaxKeySize = 32; + pInfo->flags = CKF_GENERATE; + break; + case CKM_AES_ECB: + case CKM_AES_CBC: + case CKM_AES_CBC_PAD: + case CKM_AES_CTR: +#ifdef WITH_AES_GCM + case CKM_AES_GCM: +#endif + pInfo->ulMinKeySize = 16; + pInfo->ulMaxKeySize = 32; + pInfo->flags = CKF_ENCRYPT | CKF_DECRYPT; + break; + case CKM_AES_KEY_WRAP: + pInfo->ulMinKeySize = 16; + pInfo->ulMaxKeySize = 0x80000000; + pInfo->flags = CKF_WRAP | CKF_UNWRAP; + break; +#ifdef HAVE_AES_KEY_WRAP_PAD + case CKM_AES_KEY_WRAP_PAD: + pInfo->ulMinKeySize = 1; + pInfo->ulMaxKeySize = 0x80000000; + pInfo->flags = CKF_WRAP | CKF_UNWRAP; + break; +#endif +#ifndef WITH_FIPS + case CKM_DES_ECB_ENCRYPT_DATA: + case CKM_DES_CBC_ENCRYPT_DATA: +#endif + case CKM_DES3_ECB_ENCRYPT_DATA: + case CKM_DES3_CBC_ENCRYPT_DATA: + case CKM_AES_ECB_ENCRYPT_DATA: + case CKM_AES_CBC_ENCRYPT_DATA: + // Key size is not in use + pInfo->ulMinKeySize = 0; + pInfo->ulMaxKeySize = 0; + pInfo->flags = CKF_DERIVE; + break; + case CKM_AES_CMAC: + pInfo->ulMinKeySize = 16; + pInfo->ulMaxKeySize = 32; + pInfo->flags = CKF_SIGN | CKF_VERIFY; + break; + case CKM_DSA_PARAMETER_GEN: + pInfo->ulMinKeySize = dsaMinSize; + pInfo->ulMaxKeySize = dsaMaxSize; + pInfo->flags = CKF_GENERATE; + break; + case CKM_DSA_KEY_PAIR_GEN: + pInfo->ulMinKeySize = dsaMinSize; + pInfo->ulMaxKeySize = dsaMaxSize; + pInfo->flags = CKF_GENERATE_KEY_PAIR; + break; + case CKM_DSA: + case CKM_DSA_SHA1: + case CKM_DSA_SHA224: + case CKM_DSA_SHA256: + case CKM_DSA_SHA384: + case CKM_DSA_SHA512: + pInfo->ulMinKeySize = dsaMinSize; + pInfo->ulMaxKeySize = dsaMaxSize; + pInfo->flags = CKF_SIGN | CKF_VERIFY; + break; + case CKM_DH_PKCS_KEY_PAIR_GEN: + pInfo->ulMinKeySize = dhMinSize; + pInfo->ulMaxKeySize = dhMaxSize; + pInfo->flags = CKF_GENERATE_KEY_PAIR; + break; + case CKM_DH_PKCS_PARAMETER_GEN: + pInfo->ulMinKeySize = dhMinSize; + pInfo->ulMaxKeySize = dhMaxSize; + pInfo->flags = CKF_GENERATE; + break; + case CKM_DH_PKCS_DERIVE: + pInfo->ulMinKeySize = dhMinSize; + pInfo->ulMaxKeySize = dhMaxSize; + pInfo->flags = CKF_DERIVE; + break; +#ifdef WITH_ECC + case CKM_EC_KEY_PAIR_GEN: + pInfo->ulMinKeySize = ecdsaMinSize; + pInfo->ulMaxKeySize = ecdsaMaxSize; +#define CKF_EC_COMMOM (CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS) + pInfo->flags = CKF_GENERATE_KEY_PAIR | CKF_EC_COMMOM; + break; + case CKM_ECDSA: + pInfo->ulMinKeySize = ecdsaMinSize; + pInfo->ulMaxKeySize = ecdsaMaxSize; + pInfo->flags = CKF_SIGN | CKF_VERIFY | CKF_EC_COMMOM; + break; + case CKM_ECDH1_DERIVE: + pInfo->ulMinKeySize = ecdhMinSize; + pInfo->ulMaxKeySize = ecdhMaxSize; + pInfo->flags = CKF_DERIVE; + break; +#endif +#ifdef WITH_GOST + case CKM_GOSTR3411: + // Key size is not in use + pInfo->ulMinKeySize = 0; + pInfo->ulMaxKeySize = 0; + pInfo->flags = CKF_DIGEST; + break; + case CKM_GOSTR3411_HMAC: + // Key size is not in use + pInfo->ulMinKeySize = 32; + pInfo->ulMaxKeySize = 512; + pInfo->flags = CKF_SIGN | CKF_VERIFY; + break; + case CKM_GOSTR3410_KEY_PAIR_GEN: + // Key size is not in use + pInfo->ulMinKeySize = 0; + pInfo->ulMaxKeySize = 0; + pInfo->flags = CKF_GENERATE_KEY_PAIR; + break; + case CKM_GOSTR3410: + // Key size is not in use + pInfo->ulMinKeySize = 0; + pInfo->ulMaxKeySize = 0; + pInfo->flags = CKF_SIGN | CKF_VERIFY; + break; + case CKM_GOSTR3410_WITH_GOSTR3411: + // Key size is not in use + pInfo->ulMinKeySize = 0; + pInfo->ulMaxKeySize = 0; + pInfo->flags = CKF_SIGN | CKF_VERIFY; + break; +#endif + default: + DEBUG_MSG("The selected mechanism is not supported"); + return CKR_MECHANISM_INVALID; + break; + } + + return CKR_OK; +} + +// Initialise the token in the specified slot +CK_RV SoftHSM::C_InitToken(CK_SLOT_ID slotID, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen, CK_UTF8CHAR_PTR pLabel) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + Slot* slot = slotManager->getSlot(slotID); + if (slot == NULL) + { + return CKR_SLOT_ID_INVALID; + } + + // Check if any session is open with this token. + if (sessionManager->haveSession(slotID)) + { + return CKR_SESSION_EXISTS; + } + + // Check the PIN + if (pPin == NULL_PTR) return CKR_ARGUMENTS_BAD; + if (ulPinLen < MIN_PIN_LEN || ulPinLen > MAX_PIN_LEN) return CKR_PIN_INCORRECT; + + ByteString soPIN(pPin, ulPinLen); + + return slot->initToken(soPIN, pLabel); +} + +// Initialise the user PIN +CK_RV SoftHSM::C_InitPIN(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // The SO must be logged in + if (session->getState() != CKS_RW_SO_FUNCTIONS) return CKR_USER_NOT_LOGGED_IN; + + // Get the token + Token* token = session->getToken(); + if (token == NULL) return CKR_GENERAL_ERROR; + + // Check the PIN + if (pPin == NULL_PTR) return CKR_ARGUMENTS_BAD; + if (ulPinLen < MIN_PIN_LEN || ulPinLen > MAX_PIN_LEN) return CKR_PIN_LEN_RANGE; + + ByteString userPIN(pPin, ulPinLen); + + return token->initUserPIN(userPIN); +} + +// Change the PIN +CK_RV SoftHSM::C_SetPIN(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pOldPin, CK_ULONG ulOldLen, CK_UTF8CHAR_PTR pNewPin, CK_ULONG ulNewLen) +{ + CK_RV rv = CKR_OK; + + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check the new PINs + if (pOldPin == NULL_PTR) return CKR_ARGUMENTS_BAD; + if (pNewPin == NULL_PTR) return CKR_ARGUMENTS_BAD; + if (ulNewLen < MIN_PIN_LEN || ulNewLen > MAX_PIN_LEN) return CKR_PIN_LEN_RANGE; + + ByteString oldPIN(pOldPin, ulOldLen); + ByteString newPIN(pNewPin, ulNewLen); + + // Get the token + Token* token = session->getToken(); + if (token == NULL) return CKR_GENERAL_ERROR; + + switch (session->getState()) + { + case CKS_RW_PUBLIC_SESSION: + case CKS_RW_USER_FUNCTIONS: + rv = token->setUserPIN(oldPIN, newPIN); + break; + case CKS_RW_SO_FUNCTIONS: + rv = token->setSOPIN(oldPIN, newPIN); + break; + default: + return CKR_SESSION_READ_ONLY; + } + + return rv; +} + +// Open a new session to the specified slot +CK_RV SoftHSM::C_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, CK_NOTIFY notify, CK_SESSION_HANDLE_PTR phSession) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + Slot* slot = slotManager->getSlot(slotID); + + CK_RV rv = sessionManager->openSession(slot, flags, pApplication, notify, phSession); + if (rv != CKR_OK) + return rv; + + // Get a pointer to the session object and store it in the handle manager. + Session* session = sessionManager->getSession(*phSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + *phSession = handleManager->addSession(slotID,session); + + return CKR_OK; +} + +// Close the given session +CK_RV SoftHSM::C_CloseSession(CK_SESSION_HANDLE hSession) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Tell the handle manager the session has been closed. + handleManager->sessionClosed(hSession); + + + // Tell the session object store that the session has closed. + sessionObjectStore->sessionClosed(hSession); + + // Tell the session manager the session has been closed. + return sessionManager->closeSession(session->getHandle()); +} + +// Close all open sessions +CK_RV SoftHSM::C_CloseAllSessions(CK_SLOT_ID slotID) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Get the slot + Slot* slot = slotManager->getSlot(slotID); + if (slot == NULL) return CKR_SLOT_ID_INVALID; + + // Get the token + Token* token = slot->getToken(); + if (token == NULL) return CKR_TOKEN_NOT_PRESENT; + + // Tell the handle manager all sessions were closed for the given slotID. + // The handle manager should then remove all session and object handles for this slot. + handleManager->allSessionsClosed(slotID); + + // Tell the session object store that all sessions were closed for the given slotID. + // The session object store should then remove all session objects for this slot. + sessionObjectStore->allSessionsClosed(slotID); + + // Finally tell the session manager tho close all sessions for the given slot. + // This will also trigger a logout on the associated token to occur. + return sessionManager->closeAllSessions(slot); +} + +// Retrieve information about the specified session +CK_RV SoftHSM::C_GetSessionInfo(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + return session->getInfo(pInfo); +} + +// Determine the state of a running operation in a session +CK_RV SoftHSM::C_GetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR /*pOperationState*/, CK_ULONG_PTR /*pulOperationStateLen*/) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + return CKR_FUNCTION_NOT_SUPPORTED; +} + +// Set the operation sate in a session +CK_RV SoftHSM::C_SetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR /*pOperationState*/, CK_ULONG /*ulOperationStateLen*/, CK_OBJECT_HANDLE /*hEncryptionKey*/, CK_OBJECT_HANDLE /*hAuthenticationKey*/) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + return CKR_FUNCTION_NOT_SUPPORTED; +} + +// Login on the token in the specified session +CK_RV SoftHSM::C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen) +{ + CK_RV rv = CKR_OK; + + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Get the PIN + if (pPin == NULL_PTR) return CKR_ARGUMENTS_BAD; + ByteString pin(pPin, ulPinLen); + + // Get the token + Token* token = session->getToken(); + if (token == NULL) return CKR_GENERAL_ERROR; + + switch (userType) + { + case CKU_SO: + // There cannot exist a R/O session on this slot + if (sessionManager->haveROSession(session->getSlot()->getSlotID())) return CKR_SESSION_READ_ONLY_EXISTS; + + // Login + rv = token->loginSO(pin); + break; + case CKU_USER: + // Login + rv = token->loginUser(pin); + break; + case CKU_CONTEXT_SPECIFIC: + // Check if re-authentication is required + if (!session->getReAuthentication()) return CKR_OPERATION_NOT_INITIALIZED; + + // Re-authenticate + rv = token->reAuthenticate(pin); + if (rv == CKR_OK) session->setReAuthentication(false); + break; + default: + return CKR_USER_TYPE_INVALID; + } + + return rv; +} + +// Log out of the token in the specified session +CK_RV SoftHSM::C_Logout(CK_SESSION_HANDLE hSession) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // 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; + + // Logout + token->logout(); + + // [PKCS#11 v2.40, C_Logout] When logout is successful... + // a. Any of the application's handles to private objects become invalid. + // b. Even if a user is later logged back into the token those handles remain invalid. + // c. All private session objects from sessions belonging to the application are destroyed. + + // Have the handle manager remove all handles pointing to private objects for this slot. + CK_SLOT_ID slotID = session->getSlot()->getSlotID(); + handleManager->tokenLoggedOut(slotID); + sessionObjectStore->tokenLoggedOut(slotID); + + return CKR_OK; +} + +// Create a new object on the token in the specified session using the given attribute template +CK_RV SoftHSM::C_CreateObject(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject) +{ + return this->CreateObject(hSession,pTemplate,ulCount,phObject,OBJECT_OP_CREATE); +} + +// Create a copy of the object with the specified handle +CK_RV SoftHSM::C_CopyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phNewObject) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pTemplate == NULL_PTR) return CKR_ARGUMENTS_BAD; + if (phNewObject == NULL_PTR) return CKR_ARGUMENTS_BAD; + *phNewObject = CK_INVALID_HANDLE; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Get the slot + Slot* slot = session->getSlot(); + if (slot == NULL_PTR) return CKR_GENERAL_ERROR; + + // Get the token + Token* token = session->getToken(); + if (token == NULL_PTR) return CKR_GENERAL_ERROR; + + // Check the object handle. + OSObject *object = (OSObject *)handleManager->getObject(hObject); + if (object == NULL_PTR || !object->isValid()) return CKR_OBJECT_HANDLE_INVALID; + + CK_BBOOL wasOnToken = object->getBooleanValue(CKA_TOKEN, false); + CK_BBOOL wasPrivate = object->getBooleanValue(CKA_PRIVATE, true); + + // Check read user credentials + CK_RV rv = haveRead(session->getState(), wasOnToken, wasPrivate); + if (rv != CKR_OK) + { + if (rv == CKR_USER_NOT_LOGGED_IN) + INFO_MSG("User is not authorized"); + + return rv; + } + + // Check if the object is copyable + CK_BBOOL isCopyable = object->getBooleanValue(CKA_COPYABLE, true); + if (!isCopyable) return CKR_ACTION_PROHIBITED; + + // Extract critical information from the template + CK_BBOOL isOnToken = wasOnToken; + CK_BBOOL isPrivate = wasPrivate; + + for (CK_ULONG i = 0; i < ulCount; i++) + { + if ((pTemplate[i].type == CKA_TOKEN) && (pTemplate[i].ulValueLen == sizeof(CK_BBOOL))) + { + isOnToken = *(CK_BBOOL*)pTemplate[i].pValue; + continue; + } + if ((pTemplate[i].type == CKA_PRIVATE) && (pTemplate[i].ulValueLen == sizeof(CK_BBOOL))) + { + isPrivate = *(CK_BBOOL*)pTemplate[i].pValue; + continue; + } + } + + // Check privacy does not downgrade + if (wasPrivate && !isPrivate) return CKR_TEMPLATE_INCONSISTENT; + + // Check write user credentials + rv = haveWrite(session->getState(), isOnToken, isPrivate); + if (rv != CKR_OK) + { + if (rv == CKR_USER_NOT_LOGGED_IN) + INFO_MSG("User is not authorized"); + if (rv == CKR_SESSION_READ_ONLY) + INFO_MSG("Session is read-only"); + + return rv; + } + + // Create the object in session or on the token + OSObject *newobject = NULL_PTR; + if (isOnToken) + { + newobject = (OSObject*) token->createObject(); + } + else + { + newobject = sessionObjectStore->createObject(slot->getSlotID(), hSession, isPrivate != CK_FALSE); + } + if (newobject == NULL) return CKR_GENERAL_ERROR; + + // Copy attributes from object class (CKA_CLASS=0 so the first) + if (!newobject->startTransaction()) + { + newobject->destroyObject(); + return CKR_FUNCTION_FAILED; + } + + CK_ATTRIBUTE_TYPE attrType = CKA_CLASS; + do + { + if (!object->attributeExists(attrType)) + { + rv = CKR_FUNCTION_FAILED; + break; + } + + OSAttribute attr = object->getAttribute(attrType); + + // Upgrade privacy has to encrypt byte strings + if (!wasPrivate && isPrivate && + attr.isByteStringAttribute() && + attr.getByteStringValue().size() != 0) + { + ByteString value; + if (!token->encrypt(attr.getByteStringValue(), value) || + !newobject->setAttribute(attrType, value)) + { + rv = CKR_FUNCTION_FAILED; + break; + } + } + else + { + if (!newobject->setAttribute(attrType, attr)) + { + rv = CKR_FUNCTION_FAILED; + break; + } + } + attrType = object->nextAttributeType(attrType); + } + while (attrType != CKA_CLASS); + + if (rv != CKR_OK) + { + newobject->abortTransaction(); + } + else if (!newobject->commitTransaction()) + { + rv = CKR_FUNCTION_FAILED; + } + + if (rv != CKR_OK) + { + newobject->destroyObject(); + return rv; + } + + // Get the new P11 object + P11Object* newp11object = NULL; + rv = newP11Object(newobject,&newp11object); + if (rv != CKR_OK) + { + newobject->destroyObject(); + return rv; + } + + // Apply the template + rv = newp11object->saveTemplate(token, isPrivate != CK_FALSE, pTemplate, ulCount, OBJECT_OP_COPY); + delete newp11object; + + if (rv != CKR_OK) + { + newobject->destroyObject(); + return rv; + } + + // Set handle + if (isOnToken) + { + *phNewObject = handleManager->addTokenObject(slot->getSlotID(), isPrivate != CK_FALSE, newobject); + } + else + { + *phNewObject = handleManager->addSessionObject(slot->getSlotID(), hSession, isPrivate != CK_FALSE, newobject); + } + + return CKR_OK; +} + +// Destroy the specified object +CK_RV SoftHSM::C_DestroyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // 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_PTR) return CKR_GENERAL_ERROR; + + // Check the object handle. + OSObject *object = (OSObject *)handleManager->getObject(hObject); + if (object == NULL_PTR || !object->isValid()) return CKR_OBJECT_HANDLE_INVALID; + + CK_BBOOL isOnToken = object->getBooleanValue(CKA_TOKEN, false); + CK_BBOOL isPrivate = object->getBooleanValue(CKA_PRIVATE, true); + + // Check user credentials + CK_RV rv = haveWrite(session->getState(), isOnToken, isPrivate); + if (rv != CKR_OK) + { + if (rv == CKR_USER_NOT_LOGGED_IN) + INFO_MSG("User is not authorized"); + if (rv == CKR_SESSION_READ_ONLY) + INFO_MSG("Session is read-only"); + + return rv; + } + + // Check if the object is destroyable + CK_BBOOL isDestroyable = object->getBooleanValue(CKA_DESTROYABLE, true); + if (!isDestroyable) return CKR_ACTION_PROHIBITED; + + // Tell the handleManager to forget about the object. + handleManager->destroyObject(hObject); + + // Destroy the object + if (!object->destroyObject()) + return CKR_FUNCTION_FAILED; + + return CKR_OK; +} + +// Determine the size of the specified object +CK_RV SoftHSM::C_GetObjectSize(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ULONG_PTR pulSize) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pulSize == NULL) return CKR_ARGUMENTS_BAD; + + // 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_PTR) return CKR_GENERAL_ERROR; + + // Check the object handle. + OSObject *object = (OSObject *)handleManager->getObject(hObject); + if (object == NULL_PTR || !object->isValid()) return CKR_OBJECT_HANDLE_INVALID; + + *pulSize = CK_UNAVAILABLE_INFORMATION; + + return CKR_OK; +} + +// Retrieve the specified attributes for the given object +CK_RV SoftHSM::C_GetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pTemplate == NULL) return CKR_ARGUMENTS_BAD; + + // 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; + + // Check the object handle. + OSObject *object = (OSObject *)handleManager->getObject(hObject); + if (object == NULL_PTR || !object->isValid()) return CKR_OBJECT_HANDLE_INVALID; + + CK_BBOOL isOnToken = object->getBooleanValue(CKA_TOKEN, false); + CK_BBOOL isPrivate = object->getBooleanValue(CKA_PRIVATE, true); + + // Check read user credentials + CK_RV rv = haveRead(session->getState(), isOnToken, isPrivate); + if (rv != CKR_OK) + { + if (rv == CKR_USER_NOT_LOGGED_IN) + INFO_MSG("User is not authorized"); + + // CKR_USER_NOT_LOGGED_IN is not a valid return code for this function, + // so we use CKR_GENERAL_ERROR. + return CKR_GENERAL_ERROR; + } + + // Wrap a P11Object around the OSObject so we can access the attributes in the + // context of the object in which it is defined. + P11Object* p11object = NULL; + rv = newP11Object(object,&p11object); + if (rv != CKR_OK) + return rv; + + // Ask the P11Object to fill the template with attribute values. + rv = p11object->loadTemplate(token, pTemplate,ulCount); + delete p11object; + return rv; +} + +// Change or set the value of the specified attributes on the specified object +CK_RV SoftHSM::C_SetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pTemplate == NULL) return CKR_ARGUMENTS_BAD; + + // 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; + + // Check the object handle. + OSObject *object = (OSObject *)handleManager->getObject(hObject); + if (object == NULL_PTR || !object->isValid()) return CKR_OBJECT_HANDLE_INVALID; + + CK_BBOOL isOnToken = object->getBooleanValue(CKA_TOKEN, false); + CK_BBOOL isPrivate = object->getBooleanValue(CKA_PRIVATE, true); + + // Check user credentials + CK_RV rv = haveWrite(session->getState(), isOnToken, isPrivate); + if (rv != CKR_OK) + { + if (rv == CKR_USER_NOT_LOGGED_IN) + INFO_MSG("User is not authorized"); + if (rv == CKR_SESSION_READ_ONLY) + INFO_MSG("Session is read-only"); + + return rv; + } + + // Check if the object is modifiable + CK_BBOOL isModifiable = object->getBooleanValue(CKA_MODIFIABLE, true); + if (!isModifiable) return CKR_ACTION_PROHIBITED; + + // Wrap a P11Object around the OSObject so we can access the attributes in the + // context of the object in which it is defined. + P11Object* p11object = NULL; + rv = newP11Object(object,&p11object); + if (rv != CKR_OK) + return rv; + + // Ask the P11Object to save the template with attribute values. + rv = p11object->saveTemplate(token, isPrivate != CK_FALSE, pTemplate,ulCount,OBJECT_OP_SET); + delete p11object; + return rv; +} + +// Initialise object search in the specified session using the specified attribute template as search parameters +CK_RV SoftHSM::C_FindObjectsInit(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Get the slot + Slot* slot = session->getSlot(); + if (slot == NULL_PTR) return CKR_GENERAL_ERROR; + + // Determine whether we have a public session or not. + bool isPublicSession; + switch (session->getState()) { + case CKS_RO_USER_FUNCTIONS: + case CKS_RW_USER_FUNCTIONS: + isPublicSession = false; + break; + default: + isPublicSession = true; + } + + // Get the token + Token* token = session->getToken(); + if (token == NULL_PTR) return CKR_GENERAL_ERROR; + + // Check if we have another operation + if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE; + + session->setOpType(SESSION_OP_FIND); + FindOperation *findOp = FindOperation::create(); + + // Check if we are out of memory + if (findOp == NULL_PTR) return CKR_HOST_MEMORY; + + std::set<OSObject*> allObjects; + token->getObjects(allObjects); + sessionObjectStore->getObjects(slot->getSlotID(),allObjects); + + std::set<CK_OBJECT_HANDLE> handles; + std::set<OSObject*>::iterator it; + for (it=allObjects.begin(); it != allObjects.end(); ++it) + { + // Refresh object and check if it is valid + if (!(*it)->isValid()) { + DEBUG_MSG("Object is not valid, skipping"); + continue; + } + + // Determine if the object has CKA_PRIVATE set to CK_TRUE + bool isPrivateObject = (*it)->getBooleanValue(CKA_PRIVATE, true); + + // If the object is private, and we are in a public session then skip it ! + if (isPublicSession && isPrivateObject) + continue; // skip object + + // Perform the actual attribute matching. + bool bAttrMatch = true; // We let an empty template match everything. + for (CK_ULONG i=0; i<ulCount; ++i) + { + bAttrMatch = false; + + if (!(*it)->attributeExists(pTemplate[i].type)) + break; + + OSAttribute attr = (*it)->getAttribute(pTemplate[i].type); + + if (attr.isBooleanAttribute()) + { + if (sizeof(CK_BBOOL) != pTemplate[i].ulValueLen) + break; + bool bTemplateValue = (*(CK_BBOOL*)pTemplate[i].pValue == CK_TRUE); + if (attr.getBooleanValue() != bTemplateValue) + break; + } + else + { + if (attr.isUnsignedLongAttribute()) + { + if (sizeof(CK_ULONG) != pTemplate[i].ulValueLen) + break; + CK_ULONG ulTemplateValue = *(CK_ULONG_PTR)pTemplate[i].pValue; + if (attr.getUnsignedLongValue() != ulTemplateValue) + break; + } + else + { + if (attr.isByteStringAttribute()) + { + ByteString bsAttrValue; + if (isPrivateObject && attr.getByteStringValue().size() != 0) + { + if (!token->decrypt(attr.getByteStringValue(), bsAttrValue)) + { + delete findOp; + return CKR_GENERAL_ERROR; + } + } + else + bsAttrValue = attr.getByteStringValue(); + + if (bsAttrValue.size() != pTemplate[i].ulValueLen) + break; + if (pTemplate[i].ulValueLen != 0) + { + ByteString bsTemplateValue((const unsigned char*)pTemplate[i].pValue, pTemplate[i].ulValueLen); + if (bsAttrValue != bsTemplateValue) + break; + } + } + else + break; + } + } + // The attribute matched ! + bAttrMatch = true; + } + + if (bAttrMatch) + { + CK_SLOT_ID slotID = slot->getSlotID(); + bool isOnToken = (*it)->getBooleanValue(CKA_TOKEN, false); + bool isPrivate = (*it)->getBooleanValue(CKA_PRIVATE, true); + // Create an object handle for every returned object. + CK_OBJECT_HANDLE hObject; + if (isOnToken) + hObject = handleManager->addTokenObject(slotID,isPrivate,*it); + else + hObject = handleManager->addSessionObject(slotID,hSession,isPrivate,*it); + if (hObject == CK_INVALID_HANDLE) + { + delete findOp; + return CKR_GENERAL_ERROR; + } + handles.insert(hObject); + } + } + + // Storing the object handles for the find will protect the library + // whenever a stale object handle is used to access the library. + findOp->setHandles(handles); + + session->setFindOp(findOp); + + return CKR_OK; +} + +// Continue the search for objects in the specified session +CK_RV SoftHSM::C_FindObjects(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE_PTR phObject, CK_ULONG ulMaxObjectCount, CK_ULONG_PTR pulObjectCount) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + if (phObject == NULL_PTR) return CKR_ARGUMENTS_BAD; + if (pulObjectCount == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we are doing the correct operation + if (session->getOpType() != SESSION_OP_FIND) return CKR_OPERATION_NOT_INITIALIZED; + + // return the object handles that have been added to the find operation. + FindOperation *findOp = session->getFindOp(); + if (findOp == NULL) return CKR_GENERAL_ERROR; + + // Ask the find operation to retrieve the object handles + *pulObjectCount = findOp->retrieveHandles(phObject,ulMaxObjectCount); + + // Erase the object handles from the find operation. + findOp->eraseHandles(0,*pulObjectCount); + + return CKR_OK; +} + +// Finish searching for objects +CK_RV SoftHSM::C_FindObjectsFinal(CK_SESSION_HANDLE hSession) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we are doing the correct operation + if (session->getOpType() != SESSION_OP_FIND) return CKR_OPERATION_NOT_INITIALIZED; + + session->resetOp(); + return CKR_OK; +} + +// Encrypt*/Decrypt*() is for Symmetrical ciphers too +static bool isSymMechanism(CK_MECHANISM_PTR pMechanism) +{ + if (pMechanism == NULL_PTR) return false; + + switch(pMechanism->mechanism) { + case CKM_DES_ECB: + case CKM_DES_CBC: + case CKM_DES_CBC_PAD: + case CKM_DES3_ECB: + case CKM_DES3_CBC: + case CKM_DES3_CBC_PAD: + case CKM_AES_ECB: + case CKM_AES_CBC: + case CKM_AES_CBC_PAD: + case CKM_AES_CTR: + case CKM_AES_GCM: + return true; + default: + return false; + } +} + +// SymAlgorithm version of C_EncryptInit +CK_RV SoftHSM::SymEncryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we have another operation + if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE; + + // Get the token + Token* token = session->getToken(); + if (token == NULL) return CKR_GENERAL_ERROR; + + // Check the key handle. + OSObject *key = (OSObject *)handleManager->getObject(hKey); + if (key == NULL_PTR || !key->isValid()) return CKR_OBJECT_HANDLE_INVALID; + + CK_BBOOL isOnToken = key->getBooleanValue(CKA_TOKEN, false); + CK_BBOOL isPrivate = key->getBooleanValue(CKA_PRIVATE, true); + + // Check read user credentials + CK_RV rv = haveRead(session->getState(), isOnToken, isPrivate); + if (rv != CKR_OK) + { + if (rv == CKR_USER_NOT_LOGGED_IN) + INFO_MSG("User is not authorized"); + + return rv; + } + + // Check if key can be used for encryption + if (!key->getBooleanValue(CKA_ENCRYPT, false)) + return CKR_KEY_FUNCTION_NOT_PERMITTED; + + // Check if the specified mechanism is allowed for the key + if (!isMechanismPermitted(key, pMechanism)) + return CKR_MECHANISM_INVALID; + + // Get the symmetric algorithm matching the mechanism + SymAlgo::Type algo = SymAlgo::Unknown; + SymMode::Type mode = SymMode::Unknown; + bool padding = false; + ByteString iv; + size_t bb = 8; + size_t counterBits = 0; + ByteString aad; + size_t tagBytes = 0; + switch(pMechanism->mechanism) { +#ifndef WITH_FIPS + case CKM_DES_ECB: + algo = SymAlgo::DES; + mode = SymMode::ECB; + bb = 7; + break; + case CKM_DES_CBC: + algo = SymAlgo::DES; + mode = SymMode::CBC; + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen == 0) + { + DEBUG_MSG("CBC mode requires an init vector"); + return CKR_ARGUMENTS_BAD; + } + iv.resize(pMechanism->ulParameterLen); + memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen); + bb = 7; + break; + case CKM_DES_CBC_PAD: + algo = SymAlgo::DES; + mode = SymMode::CBC; + padding = true; + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen == 0) + { + DEBUG_MSG("CBC mode requires an init vector"); + return CKR_ARGUMENTS_BAD; + } + iv.resize(pMechanism->ulParameterLen); + memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen); + bb = 7; + break; +#endif + case CKM_DES3_ECB: + algo = SymAlgo::DES3; + mode = SymMode::ECB; + bb = 7; + break; + case CKM_DES3_CBC: + algo = SymAlgo::DES3; + mode = SymMode::CBC; + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen == 0) + { + DEBUG_MSG("CBC mode requires an init vector"); + return CKR_ARGUMENTS_BAD; + } + iv.resize(pMechanism->ulParameterLen); + memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen); + bb = 7; + break; + case CKM_DES3_CBC_PAD: + algo = SymAlgo::DES3; + mode = SymMode::CBC; + padding = true; + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen == 0) + { + DEBUG_MSG("CBC mode requires an init vector"); + return CKR_ARGUMENTS_BAD; + } + iv.resize(pMechanism->ulParameterLen); + memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen); + bb = 7; + break; + case CKM_AES_ECB: + algo = SymAlgo::AES; + mode = SymMode::ECB; + break; + case CKM_AES_CBC: + algo = SymAlgo::AES; + mode = SymMode::CBC; + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen == 0) + { + DEBUG_MSG("CBC mode requires an init vector"); + return CKR_ARGUMENTS_BAD; + } + iv.resize(pMechanism->ulParameterLen); + memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen); + break; + case CKM_AES_CBC_PAD: + algo = SymAlgo::AES; + mode = SymMode::CBC; + padding = true; + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen == 0) + { + DEBUG_MSG("CBC mode requires an init vector"); + return CKR_ARGUMENTS_BAD; + } + iv.resize(pMechanism->ulParameterLen); + memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen); + break; + case CKM_AES_CTR: + algo = SymAlgo::AES; + mode = SymMode::CTR; + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen != sizeof(CK_AES_CTR_PARAMS)) + { + DEBUG_MSG("CTR mode requires a counter block"); + return CKR_ARGUMENTS_BAD; + } + counterBits = CK_AES_CTR_PARAMS_PTR(pMechanism->pParameter)->ulCounterBits; + if (counterBits == 0 || counterBits > 128) + { + DEBUG_MSG("Invalid ulCounterBits"); + return CKR_MECHANISM_PARAM_INVALID; + } + iv.resize(16); + memcpy(&iv[0], CK_AES_CTR_PARAMS_PTR(pMechanism->pParameter)->cb, 16); + break; +#ifdef WITH_AES_GCM + case CKM_AES_GCM: + algo = SymAlgo::AES; + mode = SymMode::GCM; + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen != sizeof(CK_GCM_PARAMS)) + { + DEBUG_MSG("GCM mode requires parameters"); + return CKR_ARGUMENTS_BAD; + } + iv.resize(CK_GCM_PARAMS_PTR(pMechanism->pParameter)->ulIvLen); + memcpy(&iv[0], CK_GCM_PARAMS_PTR(pMechanism->pParameter)->pIv, CK_GCM_PARAMS_PTR(pMechanism->pParameter)->ulIvLen); + aad.resize(CK_GCM_PARAMS_PTR(pMechanism->pParameter)->ulAADLen); + memcpy(&aad[0], CK_GCM_PARAMS_PTR(pMechanism->pParameter)->pAAD, CK_GCM_PARAMS_PTR(pMechanism->pParameter)->ulAADLen); + tagBytes = CK_GCM_PARAMS_PTR(pMechanism->pParameter)->ulTagBits; + if (tagBytes > 128 || tagBytes % 8 != 0) + { + DEBUG_MSG("Invalid ulTagBits value"); + return CKR_ARGUMENTS_BAD; + } + tagBytes = tagBytes / 8; + break; +#endif + default: + return CKR_MECHANISM_INVALID; + } + SymmetricAlgorithm* cipher = CryptoFactory::i()->getSymmetricAlgorithm(algo); + if (cipher == NULL) return CKR_MECHANISM_INVALID; + + SymmetricKey* secretkey = new SymmetricKey(); + + if (getSymmetricKey(secretkey, token, key) != CKR_OK) + { + cipher->recycleKey(secretkey); + CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); + return CKR_GENERAL_ERROR; + } + + // adjust key bit length + secretkey->setBitLen(secretkey->getKeyBits().size() * bb); + + // Initialize encryption + if (!cipher->encryptInit(secretkey, mode, iv, padding, counterBits, aad, tagBytes)) + { + cipher->recycleKey(secretkey); + CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); + return CKR_MECHANISM_INVALID; + } + + session->setOpType(SESSION_OP_ENCRYPT); + session->setSymmetricCryptoOp(cipher); + session->setAllowMultiPartOp(true); + session->setAllowSinglePartOp(true); + session->setSymmetricKey(secretkey); + + return CKR_OK; +} + +// AsymAlgorithm version of C_EncryptInit +CK_RV SoftHSM::AsymEncryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we have another operation + if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE; + + // Get the token + Token* token = session->getToken(); + if (token == NULL) return CKR_GENERAL_ERROR; + + // Check the key handle. + OSObject *key = (OSObject *)handleManager->getObject(hKey); + if (key == NULL_PTR || !key->isValid()) return CKR_OBJECT_HANDLE_INVALID; + + CK_BBOOL isOnToken = key->getBooleanValue(CKA_TOKEN, false); + CK_BBOOL isPrivate = key->getBooleanValue(CKA_PRIVATE, true); + + // Check read user credentials + CK_RV rv = haveRead(session->getState(), isOnToken, isPrivate); + if (rv != CKR_OK) + { + if (rv == CKR_USER_NOT_LOGGED_IN) + INFO_MSG("User is not authorized"); + + return rv; + } + + // Check if key can be used for encryption + if (!key->getBooleanValue(CKA_ENCRYPT, false)) + return CKR_KEY_FUNCTION_NOT_PERMITTED; + + // Get the asymmetric algorithm matching the mechanism + AsymMech::Type mechanism; + bool isRSA = false; + switch(pMechanism->mechanism) { + case CKM_RSA_PKCS: + mechanism = AsymMech::RSA_PKCS; + isRSA = true; + break; + case CKM_RSA_X_509: + mechanism = AsymMech::RSA; + isRSA = true; + break; + case CKM_RSA_PKCS_OAEP: + rv = MechParamCheckRSAPKCSOAEP(pMechanism); + if (rv != CKR_OK) + return rv; + + mechanism = AsymMech::RSA_PKCS_OAEP; + isRSA = true; + break; + default: + return CKR_MECHANISM_INVALID; + } + + AsymmetricAlgorithm* asymCrypto = NULL; + PublicKey* publicKey = NULL; + if (isRSA) + { + asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::RSA); + if (asymCrypto == NULL) return CKR_MECHANISM_INVALID; + + publicKey = asymCrypto->newPublicKey(); + if (publicKey == NULL) + { + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_HOST_MEMORY; + } + + if (getRSAPublicKey((RSAPublicKey*)publicKey, token, key) != CKR_OK) + { + asymCrypto->recyclePublicKey(publicKey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_GENERAL_ERROR; + } + } + else + { + return CKR_MECHANISM_INVALID; + } + + session->setOpType(SESSION_OP_ENCRYPT); + session->setAsymmetricCryptoOp(asymCrypto); + session->setMechanism(mechanism); + session->setAllowMultiPartOp(false); + session->setAllowSinglePartOp(true); + session->setPublicKey(publicKey); + + return CKR_OK; +} + +// Initialise encryption using the specified object and mechanism +CK_RV SoftHSM::C_EncryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + if (isSymMechanism(pMechanism)) + return SymEncryptInit(hSession, pMechanism, hKey); + else + return AsymEncryptInit(hSession, pMechanism, hKey); +} + +// SymAlgorithm version of C_Encrypt +static CK_RV SymEncrypt(Session* session, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen) +{ + SymmetricAlgorithm* cipher = session->getSymmetricCryptoOp(); + if (cipher == NULL || !session->getAllowSinglePartOp()) + { + session->resetOp(); + return CKR_OPERATION_NOT_INITIALIZED; + } + + // Check data size + CK_ULONG maxSize = ulDataLen + cipher->getTagBytes(); + if (cipher->isBlockCipher()) + { + CK_ULONG remainder = ulDataLen % cipher->getBlockSize(); + if (cipher->getPaddingMode() == false && remainder != 0) + { + session->resetOp(); + return CKR_DATA_LEN_RANGE; + } + + // Round up to block size + if (remainder != 0) + { + maxSize = ulDataLen + cipher->getBlockSize() - remainder; + } + else if (cipher->getPaddingMode() == true) + { + maxSize = ulDataLen + cipher->getBlockSize(); + } + } + if (!cipher->checkMaximumBytes(ulDataLen)) + { + session->resetOp(); + return CKR_DATA_LEN_RANGE; + } + + if (pEncryptedData == NULL_PTR) + { + *pulEncryptedDataLen = maxSize; + return CKR_OK; + } + + // Check buffer size + if (*pulEncryptedDataLen < maxSize) + { + *pulEncryptedDataLen = maxSize; + return CKR_BUFFER_TOO_SMALL; + } + + // Get the data + ByteString data(pData, ulDataLen); + ByteString encryptedData; + + // Encrypt the data + if (!cipher->encryptUpdate(data, encryptedData)) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + + // Finalize encryption + ByteString encryptedFinal; + if (!cipher->encryptFinal(encryptedFinal)) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + encryptedData += encryptedFinal; + encryptedData.resize(maxSize); + + memcpy(pEncryptedData, encryptedData.byte_str(), encryptedData.size()); + *pulEncryptedDataLen = encryptedData.size(); + + session->resetOp(); + return CKR_OK; +} + +// AsymAlgorithm version of C_Encrypt +static CK_RV AsymEncrypt(Session* session, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen) +{ + AsymmetricAlgorithm* asymCrypto = session->getAsymmetricCryptoOp(); + AsymMech::Type mechanism = session->getMechanism(); + PublicKey* publicKey = session->getPublicKey(); + if (asymCrypto == NULL || !session->getAllowSinglePartOp() || publicKey == NULL) + { + session->resetOp(); + return CKR_OPERATION_NOT_INITIALIZED; + } + + // Size of the encrypted data + CK_ULONG size = publicKey->getOutputLength(); + + if (pEncryptedData == NULL_PTR) + { + *pulEncryptedDataLen = size; + return CKR_OK; + } + + // Check buffer size + if (*pulEncryptedDataLen < size) + { + *pulEncryptedDataLen = size; + return CKR_BUFFER_TOO_SMALL; + } + + // Get the data + ByteString data; + ByteString encryptedData; + + // We must allow input length <= k and therfore need to prepend the data with zeroes. + if (mechanism == AsymMech::RSA) { + data.wipe(size-ulDataLen); + } + + data += ByteString(pData, ulDataLen); + + // Encrypt the data + if (!asymCrypto->encrypt(publicKey,data,encryptedData,mechanism)) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + + // Check size + if (encryptedData.size() != size) + { + ERROR_MSG("The size of the encrypted data differs from the size of the mechanism"); + session->resetOp(); + return CKR_GENERAL_ERROR; + } + memcpy(pEncryptedData, encryptedData.byte_str(), size); + *pulEncryptedDataLen = size; + + session->resetOp(); + return CKR_OK; +} + +// Perform a single operation encryption operation in the specified session +CK_RV SoftHSM::C_Encrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pData == NULL_PTR) return CKR_ARGUMENTS_BAD; + if (pulEncryptedDataLen == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we are doing the correct operation + if (session->getOpType() != SESSION_OP_ENCRYPT) + return CKR_OPERATION_NOT_INITIALIZED; + + if (session->getSymmetricCryptoOp() != NULL) + return SymEncrypt(session, pData, ulDataLen, + pEncryptedData, pulEncryptedDataLen); + else + return AsymEncrypt(session, pData, ulDataLen, + pEncryptedData, pulEncryptedDataLen); +} + +// SymAlgorithm version of C_EncryptUpdate +static CK_RV SymEncryptUpdate(Session* session, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen) +{ + SymmetricAlgorithm* cipher = session->getSymmetricCryptoOp(); + if (cipher == NULL || !session->getAllowMultiPartOp()) + { + session->resetOp(); + return CKR_OPERATION_NOT_INITIALIZED; + } + + // Check data size + size_t blockSize = cipher->getBlockSize(); + size_t remainingSize = cipher->getBufferSize(); + CK_ULONG maxSize = ulDataLen + remainingSize; + if (cipher->isBlockCipher()) + { + int nrOfBlocks = (ulDataLen + remainingSize) / blockSize; + maxSize = nrOfBlocks * blockSize; + } + if (!cipher->checkMaximumBytes(ulDataLen)) + { + session->resetOp(); + return CKR_DATA_LEN_RANGE; + } + + // Check data size + if (pEncryptedData == NULL_PTR) + { + *pulEncryptedDataLen = maxSize; + return CKR_OK; + } + + // Check output buffer size + if (*pulEncryptedDataLen < maxSize) + { + DEBUG_MSG("ulDataLen: %#5x output buffer size: %#5x blockSize: %#3x remainingSize: %#4x maxSize: %#5x", + ulDataLen, *pulEncryptedDataLen, blockSize, remainingSize, maxSize); + *pulEncryptedDataLen = maxSize; + return CKR_BUFFER_TOO_SMALL; + } + + // Get the data + ByteString data(pData, ulDataLen); + ByteString encryptedData; + + // Encrypt the data + if (!cipher->encryptUpdate(data, encryptedData)) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + DEBUG_MSG("ulDataLen: %#5x output buffer size: %#5x blockSize: %#3x remainingSize: %#4x maxSize: %#5x encryptedData.size(): %#5x", + ulDataLen, *pulEncryptedDataLen, blockSize, remainingSize, maxSize, encryptedData.size()); + + // Check output size from crypto. Unrecoverable error if to large. + if (*pulEncryptedDataLen < encryptedData.size()) + { + session->resetOp(); + ERROR_MSG("EncryptUpdate returning too much data. Length of output data buffer is %i but %i bytes was returned by the encrypt.", + *pulEncryptedDataLen, encryptedData.size()); + return CKR_GENERAL_ERROR; + } + + if (encryptedData.size() > 0) + { + memcpy(pEncryptedData, encryptedData.byte_str(), encryptedData.size()); + } + *pulEncryptedDataLen = encryptedData.size(); + + return CKR_OK; +} + +// Feed data to the running encryption operation in a session +CK_RV SoftHSM::C_EncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pData == NULL_PTR) return CKR_ARGUMENTS_BAD; + if (pulEncryptedDataLen == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we are doing the correct operation + if (session->getOpType() != SESSION_OP_ENCRYPT) + return CKR_OPERATION_NOT_INITIALIZED; + + if (session->getSymmetricCryptoOp() != NULL) + return SymEncryptUpdate(session, pData, ulDataLen, + pEncryptedData, pulEncryptedDataLen); + else + return CKR_FUNCTION_NOT_SUPPORTED; +} + +// SymAlgorithm version of C_EncryptFinal +static CK_RV SymEncryptFinal(Session* session, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen) +{ + SymmetricAlgorithm* cipher = session->getSymmetricCryptoOp(); + if (cipher == NULL || !session->getAllowMultiPartOp()) + { + session->resetOp(); + return CKR_OPERATION_NOT_INITIALIZED; + } + + // Check data size + size_t remainingSize = cipher->getBufferSize() + cipher->getTagBytes(); + CK_ULONG size = remainingSize; + if (cipher->isBlockCipher()) + { + size_t blockSize = cipher->getBlockSize(); + bool isPadding = cipher->getPaddingMode(); + if ((remainingSize % blockSize) != 0 && !isPadding) + { + session->resetOp(); + DEBUG_MSG("Remaining buffer size is not an integral of the block size. Block size: %#2x Remaining size: %#2x", + blockSize, remainingSize); + return CKR_DATA_LEN_RANGE; + } + // when padding: an integral of the block size that is longer than the remaining data. + size = isPadding ? ((remainingSize + blockSize) / blockSize) * blockSize : remainingSize; + } + + // Give required output buffer size. + if (pEncryptedData == NULL_PTR) + { + *pulEncryptedDataLen = size; + return CKR_OK; + } + + // Check output buffer size + if (*pulEncryptedDataLen < size) + { + DEBUG_MSG("output buffer size: %#5x size: %#5x", + *pulEncryptedDataLen, size); + *pulEncryptedDataLen = size; + return CKR_BUFFER_TOO_SMALL; + } + + // Finalize encryption + ByteString encryptedFinal; + if (!cipher->encryptFinal(encryptedFinal)) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + DEBUG_MSG("output buffer size: %#2x size: %#2x encryptedFinal.size(): %#2x", + *pulEncryptedDataLen, size, encryptedFinal.size()); + + // Check output size from crypto. Unrecoverable error if to large. + if (*pulEncryptedDataLen < encryptedFinal.size()) + { + session->resetOp(); + ERROR_MSG("EncryptFinal returning too much data. Length of output data buffer is %i but %i bytes was returned by the encrypt.", + *pulEncryptedDataLen, encryptedFinal.size()); + return CKR_GENERAL_ERROR; + } + + if (encryptedFinal.size() > 0) + { + memcpy(pEncryptedData, encryptedFinal.byte_str(), encryptedFinal.size()); + } + *pulEncryptedDataLen = encryptedFinal.size(); + + session->resetOp(); + return CKR_OK; +} + +// Finalise the encryption operation +CK_RV SoftHSM::C_EncryptFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we are doing the correct operation + if (session->getOpType() != SESSION_OP_ENCRYPT) return CKR_OPERATION_NOT_INITIALIZED; + + if (session->getSymmetricCryptoOp() != NULL) + return SymEncryptFinal(session, pEncryptedData, pulEncryptedDataLen); + else + return CKR_FUNCTION_NOT_SUPPORTED; +} + +// SymAlgorithm version of C_DecryptInit +CK_RV SoftHSM::SymDecryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // 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; + + // Check if we have another operation + if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE; + + // Check the key handle. + OSObject *key = (OSObject *)handleManager->getObject(hKey); + if (key == NULL_PTR || !key->isValid()) return CKR_OBJECT_HANDLE_INVALID; + + CK_BBOOL isOnToken = key->getBooleanValue(CKA_TOKEN, false); + CK_BBOOL isPrivate = key->getBooleanValue(CKA_PRIVATE, true); + + // Check read user credentials + CK_RV rv = haveRead(session->getState(), isOnToken, isPrivate); + if (rv != CKR_OK) + { + if (rv == CKR_USER_NOT_LOGGED_IN) + INFO_MSG("User is not authorized"); + + return rv; + } + + // Check if key can be used for decryption + if (!key->getBooleanValue(CKA_DECRYPT, false)) + return CKR_KEY_FUNCTION_NOT_PERMITTED; + + + // Check if the specified mechanism is allowed for the key + if (!isMechanismPermitted(key, pMechanism)) + return CKR_MECHANISM_INVALID; + + // Get the symmetric algorithm matching the mechanism + SymAlgo::Type algo = SymAlgo::Unknown; + SymMode::Type mode = SymMode::Unknown; + bool padding = false; + ByteString iv; + size_t bb = 8; + size_t counterBits = 0; + ByteString aad; + size_t tagBytes = 0; + switch(pMechanism->mechanism) { +#ifndef WITH_FIPS + case CKM_DES_ECB: + algo = SymAlgo::DES; + mode = SymMode::ECB; + bb = 7; + break; + case CKM_DES_CBC: + algo = SymAlgo::DES; + mode = SymMode::CBC; + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen == 0) + { + DEBUG_MSG("CBC mode requires an init vector"); + return CKR_ARGUMENTS_BAD; + } + iv.resize(pMechanism->ulParameterLen); + memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen); + bb = 7; + break; + case CKM_DES_CBC_PAD: + algo = SymAlgo::DES; + mode = SymMode::CBC; + padding = true; + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen == 0) + { + DEBUG_MSG("CBC mode requires an init vector"); + return CKR_ARGUMENTS_BAD; + } + iv.resize(pMechanism->ulParameterLen); + memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen); + bb = 7; + break; +#endif + case CKM_DES3_ECB: + algo = SymAlgo::DES3; + mode = SymMode::ECB; + bb = 7; + break; + case CKM_DES3_CBC: + algo = SymAlgo::DES3; + mode = SymMode::CBC; + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen == 0) + { + DEBUG_MSG("CBC mode requires an init vector"); + return CKR_ARGUMENTS_BAD; + } + iv.resize(pMechanism->ulParameterLen); + memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen); + bb = 7; + break; + case CKM_DES3_CBC_PAD: + algo = SymAlgo::DES3; + mode = SymMode::CBC; + padding = true; + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen == 0) + { + DEBUG_MSG("CBC mode requires an init vector"); + return CKR_ARGUMENTS_BAD; + } + iv.resize(pMechanism->ulParameterLen); + memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen); + bb = 7; + break; + case CKM_AES_ECB: + algo = SymAlgo::AES; + mode = SymMode::ECB; + break; + case CKM_AES_CBC: + algo = SymAlgo::AES; + mode = SymMode::CBC; + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen == 0) + { + DEBUG_MSG("CBC mode requires an init vector"); + return CKR_ARGUMENTS_BAD; + } + iv.resize(pMechanism->ulParameterLen); + memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen); + break; + case CKM_AES_CBC_PAD: + algo = SymAlgo::AES; + mode = SymMode::CBC; + padding = true; + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen == 0) + { + DEBUG_MSG("CBC mode requires an init vector"); + return CKR_ARGUMENTS_BAD; + } + iv.resize(pMechanism->ulParameterLen); + memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen); + break; + case CKM_AES_CTR: + algo = SymAlgo::AES; + mode = SymMode::CTR; + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen != sizeof(CK_AES_CTR_PARAMS)) + { + DEBUG_MSG("CTR mode requires a counter block"); + return CKR_ARGUMENTS_BAD; + } + counterBits = CK_AES_CTR_PARAMS_PTR(pMechanism->pParameter)->ulCounterBits; + if (counterBits == 0 || counterBits > 128) + { + DEBUG_MSG("Invalid ulCounterBits"); + return CKR_MECHANISM_PARAM_INVALID; + } + iv.resize(16); + memcpy(&iv[0], CK_AES_CTR_PARAMS_PTR(pMechanism->pParameter)->cb, 16); + break; +#ifdef WITH_AES_GCM + case CKM_AES_GCM: + algo = SymAlgo::AES; + mode = SymMode::GCM; + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen != sizeof(CK_GCM_PARAMS)) + { + DEBUG_MSG("GCM mode requires parameters"); + return CKR_ARGUMENTS_BAD; + } + iv.resize(CK_GCM_PARAMS_PTR(pMechanism->pParameter)->ulIvLen); + memcpy(&iv[0], CK_GCM_PARAMS_PTR(pMechanism->pParameter)->pIv, CK_GCM_PARAMS_PTR(pMechanism->pParameter)->ulIvLen); + aad.resize(CK_GCM_PARAMS_PTR(pMechanism->pParameter)->ulAADLen); + memcpy(&aad[0], CK_GCM_PARAMS_PTR(pMechanism->pParameter)->pAAD, CK_GCM_PARAMS_PTR(pMechanism->pParameter)->ulAADLen); + tagBytes = CK_GCM_PARAMS_PTR(pMechanism->pParameter)->ulTagBits; + if (tagBytes > 128 || tagBytes % 8 != 0) + { + DEBUG_MSG("Invalid ulTagBits value"); + return CKR_ARGUMENTS_BAD; + } + tagBytes = tagBytes / 8; + break; +#endif + default: + return CKR_MECHANISM_INVALID; + } + SymmetricAlgorithm* cipher = CryptoFactory::i()->getSymmetricAlgorithm(algo); + if (cipher == NULL) return CKR_MECHANISM_INVALID; + + SymmetricKey* secretkey = new SymmetricKey(); + + if (getSymmetricKey(secretkey, token, key) != CKR_OK) + { + cipher->recycleKey(secretkey); + CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); + return CKR_GENERAL_ERROR; + } + + // adjust key bit length + secretkey->setBitLen(secretkey->getKeyBits().size() * bb); + + // Initialize decryption + if (!cipher->decryptInit(secretkey, mode, iv, padding, counterBits, aad, tagBytes)) + { + cipher->recycleKey(secretkey); + CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); + return CKR_MECHANISM_INVALID; + } + + session->setOpType(SESSION_OP_DECRYPT); + session->setSymmetricCryptoOp(cipher); + session->setAllowMultiPartOp(true); + session->setAllowSinglePartOp(true); + session->setSymmetricKey(secretkey); + + return CKR_OK; +} + +// AsymAlgorithm version of C_DecryptInit +CK_RV SoftHSM::AsymDecryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // 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; + + // Check if we have another operation + if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE; + + // Check the key handle. + OSObject *key = (OSObject *)handleManager->getObject(hKey); + if (key == NULL_PTR || !key->isValid()) return CKR_OBJECT_HANDLE_INVALID; + + CK_BBOOL isOnToken = key->getBooleanValue(CKA_TOKEN, false); + CK_BBOOL isPrivate = key->getBooleanValue(CKA_PRIVATE, true); + + // Check read user credentials + CK_RV rv = haveRead(session->getState(), isOnToken, isPrivate); + if (rv != CKR_OK) + { + if (rv == CKR_USER_NOT_LOGGED_IN) + INFO_MSG("User is not authorized"); + + return rv; + } + + // Check if key can be used for decryption + if (!key->getBooleanValue(CKA_DECRYPT, false)) + return CKR_KEY_FUNCTION_NOT_PERMITTED; + + // Check if the specified mechanism is allowed for the key + if (!isMechanismPermitted(key, pMechanism)) + return CKR_MECHANISM_INVALID; + + // Get the asymmetric algorithm matching the mechanism + AsymMech::Type mechanism = AsymMech::Unknown; + bool isRSA = false; + switch(pMechanism->mechanism) { + case CKM_RSA_PKCS: + mechanism = AsymMech::RSA_PKCS; + isRSA = true; + break; + case CKM_RSA_X_509: + mechanism = AsymMech::RSA; + isRSA = true; + break; + case CKM_RSA_PKCS_OAEP: + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_OAEP_PARAMS)) + { + DEBUG_MSG("pParameter must be of type CK_RSA_PKCS_OAEP_PARAMS"); + return CKR_ARGUMENTS_BAD; + } + if (CK_RSA_PKCS_OAEP_PARAMS_PTR(pMechanism->pParameter)->hashAlg != CKM_SHA_1) + { + DEBUG_MSG("hashAlg must be CKM_SHA_1"); + return CKR_ARGUMENTS_BAD; + } + if (CK_RSA_PKCS_OAEP_PARAMS_PTR(pMechanism->pParameter)->mgf != CKG_MGF1_SHA1) + { + DEBUG_MSG("mgf must be CKG_MGF1_SHA1"); + return CKR_ARGUMENTS_BAD; + } + + mechanism = AsymMech::RSA_PKCS_OAEP; + isRSA = true; + break; + default: + return CKR_MECHANISM_INVALID; + } + + AsymmetricAlgorithm* asymCrypto = NULL; + PrivateKey* privateKey = NULL; + if (isRSA) + { + asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::RSA); + if (asymCrypto == NULL) return CKR_MECHANISM_INVALID; + + privateKey = asymCrypto->newPrivateKey(); + if (privateKey == NULL) + { + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_HOST_MEMORY; + } + + if (getRSAPrivateKey((RSAPrivateKey*)privateKey, token, key) != CKR_OK) + { + asymCrypto->recyclePrivateKey(privateKey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_GENERAL_ERROR; + } + } + else + { + return CKR_MECHANISM_INVALID; + } + + // Check if re-authentication is required + if (key->getBooleanValue(CKA_ALWAYS_AUTHENTICATE, false)) + { + session->setReAuthentication(true); + } + + session->setOpType(SESSION_OP_DECRYPT); + session->setAsymmetricCryptoOp(asymCrypto); + session->setMechanism(mechanism); + session->setAllowMultiPartOp(false); + session->setAllowSinglePartOp(true); + session->setPrivateKey(privateKey); + + return CKR_OK; +} + +// Initialise decryption using the specified object +CK_RV SoftHSM::C_DecryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + if (isSymMechanism(pMechanism)) + return SymDecryptInit(hSession, pMechanism, hKey); + else + return AsymDecryptInit(hSession, pMechanism, hKey); +} + +// SymAlgorithm version of C_Decrypt +static CK_RV SymDecrypt(Session* session, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) +{ + SymmetricAlgorithm* cipher = session->getSymmetricCryptoOp(); + if (cipher == NULL || !session->getAllowSinglePartOp()) + { + session->resetOp(); + return CKR_OPERATION_NOT_INITIALIZED; + } + + // Check encrypted data size + if (cipher->isBlockCipher() && ulEncryptedDataLen % cipher->getBlockSize() != 0) + { + session->resetOp(); + return CKR_ENCRYPTED_DATA_LEN_RANGE; + } + if (!cipher->checkMaximumBytes(ulEncryptedDataLen)) + { + session->resetOp(); + return CKR_ENCRYPTED_DATA_LEN_RANGE; + } + + if (pData == NULL_PTR) + { + *pulDataLen = ulEncryptedDataLen; + return CKR_OK; + } + + // Check buffer size + if (*pulDataLen < ulEncryptedDataLen) + { + *pulDataLen = ulEncryptedDataLen; + return CKR_BUFFER_TOO_SMALL; + } + + // Get the data + ByteString encryptedData(pEncryptedData, ulEncryptedDataLen); + ByteString data; + + // Decrypt the data + if (!cipher->decryptUpdate(encryptedData,data)) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + + // Finalize decryption + ByteString dataFinal; + if (!cipher->decryptFinal(dataFinal)) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + data += dataFinal; + if (data.size() > ulEncryptedDataLen) + { + data.resize(ulEncryptedDataLen); + } + + if (data.size() != 0) + { + memcpy(pData, data.byte_str(), data.size()); + } + *pulDataLen = data.size(); + + session->resetOp(); + return CKR_OK; + +} + +// AsymAlgorithm version of C_Decrypt +static CK_RV AsymDecrypt(Session* session, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) +{ + AsymmetricAlgorithm* asymCrypto = session->getAsymmetricCryptoOp(); + AsymMech::Type mechanism = session->getMechanism(); + PrivateKey* privateKey = session->getPrivateKey(); + if (asymCrypto == NULL || !session->getAllowSinglePartOp() || privateKey == NULL) + { + session->resetOp(); + return CKR_OPERATION_NOT_INITIALIZED; + } + + // Check if re-authentication is required + if (session->getReAuthentication()) + { + session->resetOp(); + return CKR_USER_NOT_LOGGED_IN; + } + + // Size of the data + CK_ULONG size = privateKey->getOutputLength(); + if (pData == NULL_PTR) + { + *pulDataLen = size; + return CKR_OK; + } + + // Check buffer size + if (*pulDataLen < size) + { + *pulDataLen = size; + return CKR_BUFFER_TOO_SMALL; + } + + // Get the data + ByteString encryptedData(pEncryptedData, ulEncryptedDataLen); + ByteString data; + + // Decrypt the data + if (!asymCrypto->decrypt(privateKey,encryptedData,data,mechanism)) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + + // Check size + if (data.size() > size) + { + ERROR_MSG("The size of the decrypted data exceeds the size of the mechanism"); + session->resetOp(); + return CKR_GENERAL_ERROR; + } + if (data.size() != 0) + { + memcpy(pData, data.byte_str(), data.size()); + } + *pulDataLen = data.size(); + + session->resetOp(); + return CKR_OK; + +} + +// Perform a single operation decryption in the given session +CK_RV SoftHSM::C_Decrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pEncryptedData == NULL_PTR) return CKR_ARGUMENTS_BAD; + if (pulDataLen == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we are doing the correct operation + if (session->getOpType() != SESSION_OP_DECRYPT) + return CKR_OPERATION_NOT_INITIALIZED; + + if (session->getSymmetricCryptoOp() != NULL) + return SymDecrypt(session, pEncryptedData, ulEncryptedDataLen, + pData, pulDataLen); + else + return AsymDecrypt(session, pEncryptedData, ulEncryptedDataLen, + pData, pulDataLen); +} + +// SymAlgorithm version of C_DecryptUpdate +static CK_RV SymDecryptUpdate(Session* session, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pDataLen) +{ + SymmetricAlgorithm* cipher = session->getSymmetricCryptoOp(); + if (cipher == NULL || !session->getAllowMultiPartOp()) + { + session->resetOp(); + return CKR_OPERATION_NOT_INITIALIZED; + } + + // Check encrypted data size + size_t blockSize = cipher->getBlockSize(); + size_t remainingSize = cipher->getBufferSize(); + CK_ULONG maxSize = ulEncryptedDataLen + remainingSize; + if (cipher->isBlockCipher()) + { + // There must always be one block left in padding mode if next operation is DecryptFinal. + // To guarantee that one byte is removed in padding mode when the number of blocks is calculated. + size_t paddingAdjustByte = cipher->getPaddingMode() ? 1 : 0; + int nrOfBlocks = (ulEncryptedDataLen + remainingSize - paddingAdjustByte) / blockSize; + maxSize = nrOfBlocks * blockSize; + } + if (!cipher->checkMaximumBytes(ulEncryptedDataLen)) + { + session->resetOp(); + return CKR_ENCRYPTED_DATA_LEN_RANGE; + } + + // Give required output buffer size. + if (pData == NULL_PTR) + { + *pDataLen = maxSize; + return CKR_OK; + } + + // Check output buffer size + if (*pDataLen < maxSize) + { + DEBUG_MSG("Output buffer too short ulEncryptedDataLen: %#5x output buffer size: %#5x blockSize: %#3x remainingSize: %#4x maxSize: %#5x", + ulEncryptedDataLen, *pDataLen, blockSize, remainingSize, maxSize); + *pDataLen = maxSize; + return CKR_BUFFER_TOO_SMALL; + } + + // Get the data + ByteString data(pEncryptedData, ulEncryptedDataLen); + ByteString decryptedData; + + // Encrypt the data + if (!cipher->decryptUpdate(data, decryptedData)) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + DEBUG_MSG("ulEncryptedDataLen: %#5x output buffer size: %#5x blockSize: %#3x remainingSize: %#4x maxSize: %#5x decryptedData.size(): %#5x", + ulEncryptedDataLen, *pDataLen, blockSize, remainingSize, maxSize, decryptedData.size()); + + // Check output size from crypto. Unrecoverable error if to large. + if (*pDataLen < decryptedData.size()) + { + session->resetOp(); + ERROR_MSG("DecryptUpdate returning too much data. Length of output data buffer is %i but %i bytes was returned by the decrypt.", + *pDataLen, decryptedData.size()); + return CKR_GENERAL_ERROR; + } + + if (decryptedData.size() > 0) + { + memcpy(pData, decryptedData.byte_str(), decryptedData.size()); + } + *pDataLen = decryptedData.size(); + + return CKR_OK; +} + + +// Feed data to the running decryption operation in a session +CK_RV SoftHSM::C_DecryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pDataLen) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pEncryptedData == NULL_PTR) return CKR_ARGUMENTS_BAD; + if (pDataLen == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we are doing the correct operation + if (session->getOpType() != SESSION_OP_DECRYPT) + return CKR_OPERATION_NOT_INITIALIZED; + + if (session->getSymmetricCryptoOp() != NULL) + return SymDecryptUpdate(session, pEncryptedData, ulEncryptedDataLen, + pData, pDataLen); + else + return CKR_FUNCTION_NOT_SUPPORTED; +} + +static CK_RV SymDecryptFinal(Session* session, CK_BYTE_PTR pDecryptedData, CK_ULONG_PTR pulDecryptedDataLen) +{ + SymmetricAlgorithm* cipher = session->getSymmetricCryptoOp(); + if (cipher == NULL || !session->getAllowMultiPartOp()) + { + session->resetOp(); + return CKR_OPERATION_NOT_INITIALIZED; + } + + // Check encrypted data size + size_t remainingSize = cipher->getBufferSize(); + CK_ULONG size = remainingSize; + if (cipher->isBlockCipher()) + { + size_t blockSize = cipher->getBlockSize(); + if (remainingSize % blockSize != 0) + { + session->resetOp(); + DEBUG_MSG("Remaining data length is not an integral of the block size. Block size: %#2x Remaining size: %#2x", + blockSize, remainingSize); + return CKR_ENCRYPTED_DATA_LEN_RANGE; + } + // It is at least one padding byte. If no padding the all remains will be returned. + size_t paddingAdjustByte = cipher->getPaddingMode() ? 1 : 0; + size = remainingSize - paddingAdjustByte; + } + + // Give required output buffer size. + if (pDecryptedData == NULL_PTR) + { + *pulDecryptedDataLen = size; + return CKR_OK; + } + + // Check output buffer size + if (*pulDecryptedDataLen < size) + { + DEBUG_MSG("output buffer size: %#5x size: %#5x", + *pulDecryptedDataLen, size); + *pulDecryptedDataLen = size; + return CKR_BUFFER_TOO_SMALL; + } + + // Finalize decryption + ByteString decryptedFinal; + if (!cipher->decryptFinal(decryptedFinal)) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + DEBUG_MSG("output buffer size: %#2x size: %#2x decryptedFinal.size(): %#2x", + *pulDecryptedDataLen, size, decryptedFinal.size()); + + // Check output size from crypto. Unrecoverable error if to large. + if (*pulDecryptedDataLen < decryptedFinal.size()) + { + session->resetOp(); + ERROR_MSG("DecryptFinal returning too much data. Length of output data buffer is %i but %i bytes was returned by the encrypt.", + *pulDecryptedDataLen, decryptedFinal.size()); + return CKR_GENERAL_ERROR; + } + + if (decryptedFinal.size() > 0) + { + memcpy(pDecryptedData, decryptedFinal.byte_str(), decryptedFinal.size()); + } + *pulDecryptedDataLen = decryptedFinal.size(); + + session->resetOp(); + return CKR_OK; +} + +// Finalise the decryption operation +CK_RV SoftHSM::C_DecryptFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG_PTR pDataLen) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we are doing the correct operation + if (session->getOpType() != SESSION_OP_DECRYPT) return CKR_OPERATION_NOT_INITIALIZED; + + if (session->getSymmetricCryptoOp() != NULL) + return SymDecryptFinal(session, pData, pDataLen); + else + return CKR_FUNCTION_NOT_SUPPORTED; +} + +// Initialise digesting using the specified mechanism in the specified session +CK_RV SoftHSM::C_DigestInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we have another operation + if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE; + + // Get the mechanism + HashAlgo::Type algo = HashAlgo::Unknown; + switch(pMechanism->mechanism) { +#ifndef WITH_FIPS + case CKM_MD5: + algo = HashAlgo::MD5; + break; +#endif + case CKM_SHA_1: + algo = HashAlgo::SHA1; + break; + case CKM_SHA224: + algo = HashAlgo::SHA224; + break; + case CKM_SHA256: + algo = HashAlgo::SHA256; + break; + case CKM_SHA384: + algo = HashAlgo::SHA384; + break; + case CKM_SHA512: + algo = HashAlgo::SHA512; + break; +#ifdef WITH_GOST + case CKM_GOSTR3411: + algo = HashAlgo::GOST; + break; +#endif + default: + return CKR_MECHANISM_INVALID; + } + HashAlgorithm* hash = CryptoFactory::i()->getHashAlgorithm(algo); + if (hash == NULL) return CKR_MECHANISM_INVALID; + + // Initialize hashing + if (hash->hashInit() == false) + { + CryptoFactory::i()->recycleHashAlgorithm(hash); + return CKR_GENERAL_ERROR; + } + + session->setOpType(SESSION_OP_DIGEST); + session->setDigestOp(hash); + session->setHashAlgo(algo); + + return CKR_OK; +} + +// Digest the specified data in a one-pass operation and return the resulting digest +CK_RV SoftHSM::C_Digest(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pulDigestLen == NULL_PTR) return CKR_ARGUMENTS_BAD; + if (pData == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we are doing the correct operation + if (session->getOpType() != SESSION_OP_DIGEST) return CKR_OPERATION_NOT_INITIALIZED; + + // Return size + CK_ULONG size = session->getDigestOp()->getHashSize(); + if (pDigest == NULL_PTR) + { + *pulDigestLen = size; + return CKR_OK; + } + + // Check buffer size + if (*pulDigestLen < size) + { + *pulDigestLen = size; + return CKR_BUFFER_TOO_SMALL; + } + + // Get the data + ByteString data(pData, ulDataLen); + + // Digest the data + if (session->getDigestOp()->hashUpdate(data) == false) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + + // Get the digest + ByteString digest; + if (session->getDigestOp()->hashFinal(digest) == false) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + + // Check size + if (digest.size() != size) + { + ERROR_MSG("The size of the digest differ from the size of the mechanism"); + session->resetOp(); + return CKR_GENERAL_ERROR; + } + memcpy(pDigest, digest.byte_str(), size); + *pulDigestLen = size; + + session->resetOp(); + + return CKR_OK; +} + +// Update a running digest operation +CK_RV SoftHSM::C_DigestUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pPart == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we are doing the correct operation + if (session->getOpType() != SESSION_OP_DIGEST) return CKR_OPERATION_NOT_INITIALIZED; + + // Get the data + ByteString data(pPart, ulPartLen); + + // Digest the data + if (session->getDigestOp()->hashUpdate(data) == false) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + + return CKR_OK; +} + +// Update a running digest operation by digesting a secret key with the specified handle +CK_RV SoftHSM::C_DigestKey(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we are doing the correct operation + if (session->getOpType() != SESSION_OP_DIGEST) return CKR_OPERATION_NOT_INITIALIZED; + + // Get the token + Token* token = session->getToken(); + if (token == NULL) return CKR_GENERAL_ERROR; + + // Check the key handle. + OSObject *key = (OSObject *)handleManager->getObject(hObject); + if (key == NULL_PTR || !key->isValid()) return CKR_KEY_HANDLE_INVALID; + + CK_BBOOL isOnToken = key->getBooleanValue(CKA_TOKEN, false); + CK_BBOOL isPrivate = key->getBooleanValue(CKA_PRIVATE, true); + + // Check read user credentials + CK_RV rv = haveRead(session->getState(), isOnToken, isPrivate); + if (rv != CKR_OK) + { + if (rv == CKR_USER_NOT_LOGGED_IN) + INFO_MSG("User is not authorized"); + + // CKR_USER_NOT_LOGGED_IN is not a valid return code for this function, + // so we use CKR_GENERAL_ERROR. + return CKR_GENERAL_ERROR; + } + + // Whitelist + HashAlgo::Type algo = session->getHashAlgo(); + if (algo != HashAlgo::SHA1 && + algo != HashAlgo::SHA224 && + algo != HashAlgo::SHA256 && + algo != HashAlgo::SHA384 && + algo != HashAlgo::SHA512) + { + // Parano... + if (!key->getBooleanValue(CKA_EXTRACTABLE, false)) + return CKR_KEY_INDIGESTIBLE; + if (key->getBooleanValue(CKA_SENSITIVE, false)) + return CKR_KEY_INDIGESTIBLE; + } + + // Get value + if (!key->attributeExists(CKA_VALUE)) + return CKR_KEY_INDIGESTIBLE; + ByteString keybits; + if (isPrivate) + { + if (!token->decrypt(key->getByteStringValue(CKA_VALUE), keybits)) + return CKR_GENERAL_ERROR; + } + else + { + keybits = key->getByteStringValue(CKA_VALUE); + } + + // Digest the value + if (session->getDigestOp()->hashUpdate(keybits) == false) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + + return CKR_OK; +} + +// Finalise the digest operation in the specified session and return the digest +CK_RV SoftHSM::C_DigestFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pulDigestLen == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we are doing the correct operation + if (session->getOpType() != SESSION_OP_DIGEST) return CKR_OPERATION_NOT_INITIALIZED; + + // Return size + CK_ULONG size = session->getDigestOp()->getHashSize(); + if (pDigest == NULL_PTR) + { + *pulDigestLen = size; + return CKR_OK; + } + + // Check buffer size + if (*pulDigestLen < size) + { + *pulDigestLen = size; + return CKR_BUFFER_TOO_SMALL; + } + + // Get the digest + ByteString digest; + if (session->getDigestOp()->hashFinal(digest) == false) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + + // Check size + if (digest.size() != size) + { + ERROR_MSG("The size of the digest differ from the size of the mechanism"); + session->resetOp(); + return CKR_GENERAL_ERROR; + } + memcpy(pDigest, digest.byte_str(), size); + *pulDigestLen = size; + + session->resetOp(); + + return CKR_OK; +} + +// Sign*/Verify*() is for MACs too +static bool isMacMechanism(CK_MECHANISM_PTR pMechanism) +{ + if (pMechanism == NULL_PTR) return false; + + switch(pMechanism->mechanism) { + case CKM_MD5_HMAC: + case CKM_SHA_1_HMAC: + case CKM_SHA224_HMAC: + case CKM_SHA256_HMAC: + case CKM_SHA384_HMAC: + case CKM_SHA512_HMAC: +#ifdef WITH_GOST + case CKM_GOSTR3411_HMAC: +#endif + case CKM_DES3_CMAC: + case CKM_AES_CMAC: + return true; + default: + return false; + } +} + +// MacAlgorithm version of C_SignInit +CK_RV SoftHSM::MacSignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we have another operation + if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE; + + // Get the token + Token* token = session->getToken(); + if (token == NULL) return CKR_GENERAL_ERROR; + + // Check the key handle. + OSObject *key = (OSObject *)handleManager->getObject(hKey); + if (key == NULL_PTR || !key->isValid()) return CKR_OBJECT_HANDLE_INVALID; + + CK_BBOOL isOnToken = key->getBooleanValue(CKA_TOKEN, false); + CK_BBOOL isPrivate = key->getBooleanValue(CKA_PRIVATE, true); + + // Check read user credentials + CK_RV rv = haveRead(session->getState(), isOnToken, isPrivate); + if (rv != CKR_OK) + { + if (rv == CKR_USER_NOT_LOGGED_IN) + INFO_MSG("User is not authorized"); + + return rv; + } + + // Check if key can be used for signing + if (!key->getBooleanValue(CKA_SIGN, false)) + return CKR_KEY_FUNCTION_NOT_PERMITTED; + + // Check if the specified mechanism is allowed for the key + if (!isMechanismPermitted(key, pMechanism)) + return CKR_MECHANISM_INVALID; + + // Get key info + CK_KEY_TYPE keyType = key->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED); + + // Get the MAC algorithm matching the mechanism + // Also check mechanism constraints + MacAlgo::Type algo = MacAlgo::Unknown; + size_t bb = 8; + size_t minSize = 0; + switch(pMechanism->mechanism) { +#ifndef WITH_FIPS + case CKM_MD5_HMAC: + if (keyType != CKK_GENERIC_SECRET && keyType != CKK_MD5_HMAC) + return CKR_KEY_TYPE_INCONSISTENT; + minSize = 16; + algo = MacAlgo::HMAC_MD5; + break; +#endif + case CKM_SHA_1_HMAC: + if (keyType != CKK_GENERIC_SECRET && keyType != CKK_SHA_1_HMAC) + return CKR_KEY_TYPE_INCONSISTENT; + minSize = 20; + algo = MacAlgo::HMAC_SHA1; + break; + case CKM_SHA224_HMAC: + if (keyType != CKK_GENERIC_SECRET && keyType != CKK_SHA224_HMAC) + return CKR_KEY_TYPE_INCONSISTENT; + minSize = 28; + algo = MacAlgo::HMAC_SHA224; + break; + case CKM_SHA256_HMAC: + if (keyType != CKK_GENERIC_SECRET && keyType != CKK_SHA256_HMAC) + return CKR_KEY_TYPE_INCONSISTENT; + minSize = 32; + algo = MacAlgo::HMAC_SHA256; + break; + case CKM_SHA384_HMAC: + if (keyType != CKK_GENERIC_SECRET && keyType != CKK_SHA384_HMAC) + return CKR_KEY_TYPE_INCONSISTENT; + minSize = 48; + algo = MacAlgo::HMAC_SHA384; + break; + case CKM_SHA512_HMAC: + if (keyType != CKK_GENERIC_SECRET && keyType != CKK_SHA512_HMAC) + return CKR_KEY_TYPE_INCONSISTENT; + minSize = 64; + algo = MacAlgo::HMAC_SHA512; + break; +#ifdef WITH_GOST + case CKM_GOSTR3411_HMAC: + if (keyType != CKK_GENERIC_SECRET && keyType != CKK_GOST28147) + return CKR_KEY_TYPE_INCONSISTENT; + minSize = 32; + algo = MacAlgo::HMAC_GOST; + break; +#endif + case CKM_DES3_CMAC: + if (keyType != CKK_DES2 && keyType != CKK_DES3) + return CKR_KEY_TYPE_INCONSISTENT; + algo = MacAlgo::CMAC_DES; + bb = 7; + break; + case CKM_AES_CMAC: + if (keyType != CKK_AES) + return CKR_KEY_TYPE_INCONSISTENT; + algo = MacAlgo::CMAC_AES; + break; + default: + return CKR_MECHANISM_INVALID; + } + MacAlgorithm* mac = CryptoFactory::i()->getMacAlgorithm(algo); + if (mac == NULL) return CKR_MECHANISM_INVALID; + + SymmetricKey* privkey = new SymmetricKey(); + + if (getSymmetricKey(privkey, token, key) != CKR_OK) + { + mac->recycleKey(privkey); + CryptoFactory::i()->recycleMacAlgorithm(mac); + return CKR_GENERAL_ERROR; + } + + // Adjust key bit length + privkey->setBitLen(privkey->getKeyBits().size() * bb); + + // Check key size + if (privkey->getBitLen() < (minSize*8)) + { + mac->recycleKey(privkey); + CryptoFactory::i()->recycleMacAlgorithm(mac); + return CKR_KEY_SIZE_RANGE; + } + + // Initialize signing + if (!mac->signInit(privkey)) + { + mac->recycleKey(privkey); + CryptoFactory::i()->recycleMacAlgorithm(mac); + return CKR_MECHANISM_INVALID; + } + + session->setOpType(SESSION_OP_SIGN); + session->setMacOp(mac); + session->setAllowMultiPartOp(true); + session->setAllowSinglePartOp(true); + session->setSymmetricKey(privkey); + + return CKR_OK; +} + +// AsymmetricAlgorithm version of C_SignInit +CK_RV SoftHSM::AsymSignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we have another operation + if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE; + + // Get the token + Token* token = session->getToken(); + if (token == NULL) return CKR_GENERAL_ERROR; + + // Check the key handle. + OSObject *key = (OSObject *)handleManager->getObject(hKey); + if (key == NULL_PTR || !key->isValid()) return CKR_OBJECT_HANDLE_INVALID; + + CK_BBOOL isOnToken = key->getBooleanValue(CKA_TOKEN, false); + CK_BBOOL isPrivate = key->getBooleanValue(CKA_PRIVATE, true); + + // Check read user credentials + CK_RV rv = haveRead(session->getState(), isOnToken, isPrivate); + if (rv != CKR_OK) + { + if (rv == CKR_USER_NOT_LOGGED_IN) + INFO_MSG("User is not authorized"); + + return rv; + } + + // Check if key can be used for signing + if (!key->getBooleanValue(CKA_SIGN, false)) + return CKR_KEY_FUNCTION_NOT_PERMITTED; + + // Check if the specified mechanism is allowed for the key + if (!isMechanismPermitted(key, pMechanism)) + return CKR_MECHANISM_INVALID; + + // Get the asymmetric algorithm matching the mechanism + AsymMech::Type mechanism = AsymMech::Unknown; + void* param = NULL; + size_t paramLen = 0; + RSA_PKCS_PSS_PARAMS pssParam; + bool bAllowMultiPartOp; + bool isRSA = false; + bool isDSA = false; + bool isECDSA = false; + switch(pMechanism->mechanism) { + case CKM_RSA_PKCS: + mechanism = AsymMech::RSA_PKCS; + bAllowMultiPartOp = false; + isRSA = true; + break; + case CKM_RSA_X_509: + mechanism = AsymMech::RSA; + bAllowMultiPartOp = false; + isRSA = true; + break; +#ifndef WITH_FIPS + case CKM_MD5_RSA_PKCS: + mechanism = AsymMech::RSA_MD5_PKCS; + bAllowMultiPartOp = true; + isRSA = true; + break; +#endif + case CKM_SHA1_RSA_PKCS: + mechanism = AsymMech::RSA_SHA1_PKCS; + bAllowMultiPartOp = true; + isRSA = true; + break; + case CKM_SHA224_RSA_PKCS: + mechanism = AsymMech::RSA_SHA224_PKCS; + bAllowMultiPartOp = true; + isRSA = true; + break; + case CKM_SHA256_RSA_PKCS: + mechanism = AsymMech::RSA_SHA256_PKCS; + bAllowMultiPartOp = true; + isRSA = true; + break; + case CKM_SHA384_RSA_PKCS: + mechanism = AsymMech::RSA_SHA384_PKCS; + bAllowMultiPartOp = true; + isRSA = true; + break; + case CKM_SHA512_RSA_PKCS: + mechanism = AsymMech::RSA_SHA512_PKCS; + bAllowMultiPartOp = true; + isRSA = true; + break; +#ifdef WITH_RAW_PSS + case CKM_RSA_PKCS_PSS: + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS)) + { + ERROR_MSG("Invalid RSA-PSS parameters"); + return CKR_ARGUMENTS_BAD; + } + mechanism = AsymMech::RSA_PKCS_PSS; + unsigned long allowedMgf; + + switch(CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg) { + case CKM_SHA_1: + pssParam.hashAlg = HashAlgo::SHA1; + pssParam.mgf = AsymRSAMGF::MGF1_SHA1; + allowedMgf = CKG_MGF1_SHA1; + break; + case CKM_SHA224: + pssParam.hashAlg = HashAlgo::SHA224; + pssParam.mgf = AsymRSAMGF::MGF1_SHA224; + allowedMgf = CKG_MGF1_SHA224; + break; + case CKM_SHA256: + pssParam.hashAlg = HashAlgo::SHA256; + pssParam.mgf = AsymRSAMGF::MGF1_SHA256; + allowedMgf = CKG_MGF1_SHA256; + break; + case CKM_SHA384: + pssParam.hashAlg = HashAlgo::SHA384; + pssParam.mgf = AsymRSAMGF::MGF1_SHA384; + allowedMgf = CKG_MGF1_SHA384; + break; + case CKM_SHA512: + pssParam.hashAlg = HashAlgo::SHA512; + pssParam.mgf = AsymRSAMGF::MGF1_SHA512; + allowedMgf = CKG_MGF1_SHA512; + break; + default: + ERROR_MSG("Invalid RSA-PSS hash"); + return CKR_ARGUMENTS_BAD; + } + + if (CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != allowedMgf) { + ERROR_MSG("Hash and MGF don't match"); + return CKR_ARGUMENTS_BAD; + } + + pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen; + param = &pssParam; + paramLen = sizeof(pssParam); + bAllowMultiPartOp = false; + isRSA = true; + break; +#endif + case CKM_SHA1_RSA_PKCS_PSS: + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) || + CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg != CKM_SHA_1 || + CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != CKG_MGF1_SHA1) + { + ERROR_MSG("Invalid parameters"); + return CKR_ARGUMENTS_BAD; + } + mechanism = AsymMech::RSA_SHA1_PKCS_PSS; + pssParam.hashAlg = HashAlgo::SHA1; + pssParam.mgf = AsymRSAMGF::MGF1_SHA1; + pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen; + param = &pssParam; + paramLen = sizeof(pssParam); + bAllowMultiPartOp = true; + isRSA = true; + break; + case CKM_SHA224_RSA_PKCS_PSS: + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) || + CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg != CKM_SHA224 || + CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != CKG_MGF1_SHA224) + { + ERROR_MSG("Invalid parameters"); + return CKR_ARGUMENTS_BAD; + } + mechanism = AsymMech::RSA_SHA224_PKCS_PSS; + pssParam.hashAlg = HashAlgo::SHA224; + pssParam.mgf = AsymRSAMGF::MGF1_SHA224; + pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen; + param = &pssParam; + paramLen = sizeof(pssParam); + bAllowMultiPartOp = true; + isRSA = true; + break; + case CKM_SHA256_RSA_PKCS_PSS: + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) || + CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg != CKM_SHA256 || + CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != CKG_MGF1_SHA256) + { + ERROR_MSG("Invalid parameters"); + return CKR_ARGUMENTS_BAD; + } + mechanism = AsymMech::RSA_SHA256_PKCS_PSS; + pssParam.hashAlg = HashAlgo::SHA256; + pssParam.mgf = AsymRSAMGF::MGF1_SHA256; + pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen; + param = &pssParam; + paramLen = sizeof(pssParam); + bAllowMultiPartOp = true; + isRSA = true; + break; + case CKM_SHA384_RSA_PKCS_PSS: + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) || + CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg != CKM_SHA384 || + CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != CKG_MGF1_SHA384) + { + ERROR_MSG("Invalid parameters"); + return CKR_ARGUMENTS_BAD; + } + mechanism = AsymMech::RSA_SHA384_PKCS_PSS; + pssParam.hashAlg = HashAlgo::SHA384; + pssParam.mgf = AsymRSAMGF::MGF1_SHA384; + pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen; + param = &pssParam; + paramLen = sizeof(pssParam); + bAllowMultiPartOp = true; + isRSA = true; + break; + case CKM_SHA512_RSA_PKCS_PSS: + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) || + CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg != CKM_SHA512 || + CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != CKG_MGF1_SHA512) + { + ERROR_MSG("Invalid parameters"); + return CKR_ARGUMENTS_BAD; + } + mechanism = AsymMech::RSA_SHA512_PKCS_PSS; + pssParam.hashAlg = HashAlgo::SHA512; + pssParam.mgf = AsymRSAMGF::MGF1_SHA512; + pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen; + param = &pssParam; + paramLen = sizeof(pssParam); + bAllowMultiPartOp = true; + isRSA = true; + break; + case CKM_DSA: + mechanism = AsymMech::DSA; + bAllowMultiPartOp = false; + isDSA = true; + break; + case CKM_DSA_SHA1: + mechanism = AsymMech::DSA_SHA1; + bAllowMultiPartOp = true; + isDSA = true; + break; + case CKM_DSA_SHA224: + mechanism = AsymMech::DSA_SHA224; + bAllowMultiPartOp = true; + isDSA = true; + break; + case CKM_DSA_SHA256: + mechanism = AsymMech::DSA_SHA256; + bAllowMultiPartOp = true; + isDSA = true; + break; + case CKM_DSA_SHA384: + mechanism = AsymMech::DSA_SHA384; + bAllowMultiPartOp = true; + isDSA = true; + break; + case CKM_DSA_SHA512: + mechanism = AsymMech::DSA_SHA512; + bAllowMultiPartOp = true; + isDSA = true; + break; +#ifdef WITH_ECC + case CKM_ECDSA: + mechanism = AsymMech::ECDSA; + bAllowMultiPartOp = false; + isECDSA = true; + break; +#endif +#ifdef WITH_GOST + case CKM_GOSTR3410: + mechanism = AsymMech::GOST; + bAllowMultiPartOp = false; + break; + case CKM_GOSTR3410_WITH_GOSTR3411: + mechanism = AsymMech::GOST_GOST; + bAllowMultiPartOp = true; + break; +#endif + default: + return CKR_MECHANISM_INVALID; + } + + AsymmetricAlgorithm* asymCrypto = NULL; + PrivateKey* privateKey = NULL; + if (isRSA) + { + asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::RSA); + if (asymCrypto == NULL) return CKR_MECHANISM_INVALID; + + privateKey = asymCrypto->newPrivateKey(); + if (privateKey == NULL) + { + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_HOST_MEMORY; + } + + if (getRSAPrivateKey((RSAPrivateKey*)privateKey, token, key) != CKR_OK) + { + asymCrypto->recyclePrivateKey(privateKey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_GENERAL_ERROR; + } + } + else if (isDSA) + { + asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::DSA); + if (asymCrypto == NULL) return CKR_MECHANISM_INVALID; + + privateKey = asymCrypto->newPrivateKey(); + if (privateKey == NULL) + { + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_HOST_MEMORY; + } + + if (getDSAPrivateKey((DSAPrivateKey*)privateKey, token, key) != CKR_OK) + { + asymCrypto->recyclePrivateKey(privateKey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_GENERAL_ERROR; + } + } +#ifdef WITH_ECC + else if (isECDSA) + { + asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::ECDSA); + if (asymCrypto == NULL) return CKR_MECHANISM_INVALID; + + privateKey = asymCrypto->newPrivateKey(); + if (privateKey == NULL) + { + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_HOST_MEMORY; + } + + if (getECPrivateKey((ECPrivateKey*)privateKey, token, key) != CKR_OK) + { + asymCrypto->recyclePrivateKey(privateKey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_GENERAL_ERROR; + } + } +#endif + else + { +#ifdef WITH_GOST + asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::GOST); + if (asymCrypto == NULL) return CKR_MECHANISM_INVALID; + + privateKey = asymCrypto->newPrivateKey(); + if (privateKey == NULL) + { + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_HOST_MEMORY; + } + + if (getGOSTPrivateKey((GOSTPrivateKey*)privateKey, token, key) != CKR_OK) + { + asymCrypto->recyclePrivateKey(privateKey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_GENERAL_ERROR; + } +#else + return CKR_MECHANISM_INVALID; +#endif + } + + // Initialize signing + if (bAllowMultiPartOp && !asymCrypto->signInit(privateKey,mechanism,param,paramLen)) + { + asymCrypto->recyclePrivateKey(privateKey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_MECHANISM_INVALID; + } + + // Check if re-authentication is required + if (key->getBooleanValue(CKA_ALWAYS_AUTHENTICATE, false)) + { + session->setReAuthentication(true); + } + + session->setOpType(SESSION_OP_SIGN); + session->setAsymmetricCryptoOp(asymCrypto); + session->setMechanism(mechanism); + session->setParameters(param, paramLen); + session->setAllowMultiPartOp(bAllowMultiPartOp); + session->setAllowSinglePartOp(true); + session->setPrivateKey(privateKey); + + return CKR_OK; +} + +// Initialise a signing operation using the specified key and mechanism +CK_RV SoftHSM::C_SignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + if (isMacMechanism(pMechanism)) + return MacSignInit(hSession, pMechanism, hKey); + else + return AsymSignInit(hSession, pMechanism, hKey); +} + +// MacAlgorithm version of C_Sign +static CK_RV MacSign(Session* session, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) +{ + MacAlgorithm* mac = session->getMacOp(); + if (mac == NULL || !session->getAllowSinglePartOp()) + { + session->resetOp(); + return CKR_OPERATION_NOT_INITIALIZED; + } + + // Size of the signature + CK_ULONG size = mac->getMacSize(); + if (pSignature == NULL_PTR) + { + *pulSignatureLen = size; + return CKR_OK; + } + + // Check buffer size + if (*pulSignatureLen < size) + { + *pulSignatureLen = size; + return CKR_BUFFER_TOO_SMALL; + } + + // Get the data + ByteString data(pData, ulDataLen); + + // Sign the data + if (!mac->signUpdate(data)) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + + // Get the signature + ByteString signature; + if (!mac->signFinal(signature)) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + + // Check size + if (signature.size() != size) + { + ERROR_MSG("The size of the signature differs from the size of the mechanism"); + session->resetOp(); + return CKR_GENERAL_ERROR; + } + memcpy(pSignature, signature.byte_str(), size); + *pulSignatureLen = size; + + session->resetOp(); + return CKR_OK; +} + +// AsymmetricAlgorithm version of C_Sign +static CK_RV AsymSign(Session* session, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) +{ + AsymmetricAlgorithm* asymCrypto = session->getAsymmetricCryptoOp(); + AsymMech::Type mechanism = session->getMechanism(); + PrivateKey* privateKey = session->getPrivateKey(); + size_t paramLen; + void* param = session->getParameters(paramLen); + if (asymCrypto == NULL || !session->getAllowSinglePartOp() || privateKey == NULL) + { + session->resetOp(); + return CKR_OPERATION_NOT_INITIALIZED; + } + + // Check if re-authentication is required + if (session->getReAuthentication()) + { + session->resetOp(); + return CKR_USER_NOT_LOGGED_IN; + } + + // Size of the signature + CK_ULONG size = privateKey->getOutputLength(); + if (pSignature == NULL_PTR) + { + *pulSignatureLen = size; + return CKR_OK; + } + + // Check buffer size + if (*pulSignatureLen < size) + { + *pulSignatureLen = size; + return CKR_BUFFER_TOO_SMALL; + } + + // Get the data + ByteString data; + + // We must allow input length <= k and therfore need to prepend the data with zeroes. + if (mechanism == AsymMech::RSA) { + data.wipe(size-ulDataLen); + } + + data += ByteString(pData, ulDataLen); + ByteString signature; + + // Sign the data + if (session->getAllowMultiPartOp()) + { + if (!asymCrypto->signUpdate(data) || + !asymCrypto->signFinal(signature)) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + } + else if (!asymCrypto->sign(privateKey,data,signature,mechanism,param,paramLen)) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + + // Check size + if (signature.size() != size) + { + ERROR_MSG("The size of the signature differs from the size of the mechanism"); + session->resetOp(); + return CKR_GENERAL_ERROR; + } + memcpy(pSignature, signature.byte_str(), size); + *pulSignatureLen = size; + + session->resetOp(); + return CKR_OK; +} + +// Sign the data in a single pass operation +CK_RV SoftHSM::C_Sign(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pData == NULL_PTR) return CKR_ARGUMENTS_BAD; + if (pulSignatureLen == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we are doing the correct operation + if (session->getOpType() != SESSION_OP_SIGN) + return CKR_OPERATION_NOT_INITIALIZED; + + if (session->getMacOp() != NULL) + return MacSign(session, pData, ulDataLen, + pSignature, pulSignatureLen); + else + return AsymSign(session, pData, ulDataLen, + pSignature, pulSignatureLen); +} + +// MacAlgorithm version of C_SignUpdate +static CK_RV MacSignUpdate(Session* session, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) +{ + MacAlgorithm* mac = session->getMacOp(); + if (mac == NULL || !session->getAllowMultiPartOp()) + { + session->resetOp(); + return CKR_OPERATION_NOT_INITIALIZED; + } + + // Get the part + ByteString part(pPart, ulPartLen); + + // Sign the data + if (!mac->signUpdate(part)) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + + session->setAllowSinglePartOp(false); + return CKR_OK; +} + +// AsymmetricAlgorithm version of C_SignUpdate +static CK_RV AsymSignUpdate(Session* session, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) +{ + AsymmetricAlgorithm* asymCrypto = session->getAsymmetricCryptoOp(); + if (asymCrypto == NULL || !session->getAllowMultiPartOp()) + { + session->resetOp(); + return CKR_OPERATION_NOT_INITIALIZED; + } + + // Check if re-authentication is required + if (session->getReAuthentication()) + { + session->resetOp(); + return CKR_USER_NOT_LOGGED_IN; + } + + // Get the part + ByteString part(pPart, ulPartLen); + + // Sign the data + if (!asymCrypto->signUpdate(part)) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + + session->setAllowSinglePartOp(false); + return CKR_OK; +} + +// Update a running signing operation with additional data +CK_RV SoftHSM::C_SignUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pPart == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we are doing the correct operation + if (session->getOpType() != SESSION_OP_SIGN) + return CKR_OPERATION_NOT_INITIALIZED; + + if (session->getMacOp() != NULL) + return MacSignUpdate(session, pPart, ulPartLen); + else + return AsymSignUpdate(session, pPart, ulPartLen); +} + +// MacAlgorithm version of C_SignFinal +static CK_RV MacSignFinal(Session* session, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) +{ + MacAlgorithm* mac = session->getMacOp(); + if (mac == NULL) + { + session->resetOp(); + return CKR_OPERATION_NOT_INITIALIZED; + } + + // Size of the signature + CK_ULONG size = mac->getMacSize(); + if (pSignature == NULL_PTR) + { + *pulSignatureLen = size; + return CKR_OK; + } + + // Check buffer size + if (*pulSignatureLen < size) + { + *pulSignatureLen = size; + return CKR_BUFFER_TOO_SMALL; + } + + // Get the signature + ByteString signature; + if (!mac->signFinal(signature)) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + + // Check size + if (signature.size() != size) + { + ERROR_MSG("The size of the signature differs from the size of the mechanism"); + session->resetOp(); + return CKR_GENERAL_ERROR; + } + memcpy(pSignature, signature.byte_str(), size); + *pulSignatureLen = size; + + session->resetOp(); + return CKR_OK; +} + +// AsymmetricAlgorithm version of C_SignFinal +static CK_RV AsymSignFinal(Session* session, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) +{ + AsymmetricAlgorithm* asymCrypto = session->getAsymmetricCryptoOp(); + PrivateKey* privateKey = session->getPrivateKey(); + if (asymCrypto == NULL || privateKey == NULL) + { + session->resetOp(); + return CKR_OPERATION_NOT_INITIALIZED; + } + + // Check if re-authentication is required + if (session->getReAuthentication()) + { + session->resetOp(); + return CKR_USER_NOT_LOGGED_IN; + } + + // Size of the signature + CK_ULONG size = privateKey->getOutputLength(); + if (pSignature == NULL_PTR) + { + *pulSignatureLen = size; + return CKR_OK; + } + + // Check buffer size + if (*pulSignatureLen < size) + { + *pulSignatureLen = size; + return CKR_BUFFER_TOO_SMALL; + } + + // Get the signature + ByteString signature; + if (!asymCrypto->signFinal(signature)) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + + // Check size + if (signature.size() != size) + { + ERROR_MSG("The size of the signature differs from the size of the mechanism"); + session->resetOp(); + return CKR_GENERAL_ERROR; + } + memcpy(pSignature, signature.byte_str(), size); + *pulSignatureLen = size; + + session->resetOp(); + return CKR_OK; +} + +// Finalise a running signing operation and return the signature +CK_RV SoftHSM::C_SignFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pulSignatureLen == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we are doing the correct operation + if (session->getOpType() != SESSION_OP_SIGN || !session->getAllowMultiPartOp()) + return CKR_OPERATION_NOT_INITIALIZED; + + if (session->getMacOp() != NULL) + return MacSignFinal(session, pSignature, pulSignatureLen); + else + return AsymSignFinal(session, pSignature, pulSignatureLen); +} + +// Initialise a signing operation that allows recovery of the signed data +CK_RV SoftHSM::C_SignRecoverInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR /*pMechanism*/, CK_OBJECT_HANDLE /*hKey*/) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we have another operation + if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE; + + return CKR_FUNCTION_NOT_SUPPORTED; +} + +// Perform a single part signing operation that allows recovery of the signed data +CK_RV SoftHSM::C_SignRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR /*pData*/, CK_ULONG /*ulDataLen*/, CK_BYTE_PTR /*pSignature*/, CK_ULONG_PTR /*pulSignatureLen*/) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + return CKR_FUNCTION_NOT_SUPPORTED; +} + +// MacAlgorithm version of C_VerifyInit +CK_RV SoftHSM::MacVerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we have another operation + if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE; + + // Get the token + Token* token = session->getToken(); + if (token == NULL) return CKR_GENERAL_ERROR; + + // Check the key handle. + OSObject *key = (OSObject *)handleManager->getObject(hKey); + if (key == NULL_PTR || !key->isValid()) return CKR_OBJECT_HANDLE_INVALID; + + CK_BBOOL isOnToken = key->getBooleanValue(CKA_TOKEN, false); + CK_BBOOL isPrivate = key->getBooleanValue(CKA_PRIVATE, true); + + // Check read user credentials + CK_RV rv = haveRead(session->getState(), isOnToken, isPrivate); + if (rv != CKR_OK) + { + if (rv == CKR_USER_NOT_LOGGED_IN) + INFO_MSG("User is not authorized"); + + return rv; + } + + // Check if key can be used for verifying + if (!key->getBooleanValue(CKA_VERIFY, false)) + return CKR_KEY_FUNCTION_NOT_PERMITTED; + + // Check if the specified mechanism is allowed for the key + if (!isMechanismPermitted(key, pMechanism)) + return CKR_MECHANISM_INVALID; + + // Get key info + CK_KEY_TYPE keyType = key->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED); + + // Get the MAC algorithm matching the mechanism + // Also check mechanism constraints + MacAlgo::Type algo = MacAlgo::Unknown; + size_t bb = 8; + size_t minSize = 0; + switch(pMechanism->mechanism) { +#ifndef WITH_FIPS + case CKM_MD5_HMAC: + if (keyType != CKK_GENERIC_SECRET && keyType != CKK_MD5_HMAC) + return CKR_KEY_TYPE_INCONSISTENT; + minSize = 16; + algo = MacAlgo::HMAC_MD5; + break; +#endif + case CKM_SHA_1_HMAC: + if (keyType != CKK_GENERIC_SECRET && keyType != CKK_SHA_1_HMAC) + return CKR_KEY_TYPE_INCONSISTENT; + minSize = 20; + algo = MacAlgo::HMAC_SHA1; + break; + case CKM_SHA224_HMAC: + if (keyType != CKK_GENERIC_SECRET && keyType != CKK_SHA224_HMAC) + return CKR_KEY_TYPE_INCONSISTENT; + minSize = 28; + algo = MacAlgo::HMAC_SHA224; + break; + case CKM_SHA256_HMAC: + if (keyType != CKK_GENERIC_SECRET && keyType != CKK_SHA256_HMAC) + return CKR_KEY_TYPE_INCONSISTENT; + minSize = 32; + algo = MacAlgo::HMAC_SHA256; + break; + case CKM_SHA384_HMAC: + if (keyType != CKK_GENERIC_SECRET && keyType != CKK_SHA384_HMAC) + return CKR_KEY_TYPE_INCONSISTENT; + minSize = 48; + algo = MacAlgo::HMAC_SHA384; + break; + case CKM_SHA512_HMAC: + if (keyType != CKK_GENERIC_SECRET && keyType != CKK_SHA512_HMAC) + return CKR_KEY_TYPE_INCONSISTENT; + minSize = 64; + algo = MacAlgo::HMAC_SHA512; + break; +#ifdef WITH_GOST + case CKM_GOSTR3411_HMAC: + if (keyType != CKK_GENERIC_SECRET && keyType != CKK_GOST28147) + return CKR_KEY_TYPE_INCONSISTENT; + minSize = 32; + algo = MacAlgo::HMAC_GOST; + break; +#endif + case CKM_DES3_CMAC: + if (keyType != CKK_DES2 && keyType != CKK_DES3) + return CKR_KEY_TYPE_INCONSISTENT; + algo = MacAlgo::CMAC_DES; + bb = 7; + break; + case CKM_AES_CMAC: + if (keyType != CKK_AES) + return CKR_KEY_TYPE_INCONSISTENT; + algo = MacAlgo::CMAC_AES; + break; + default: + return CKR_MECHANISM_INVALID; + } + MacAlgorithm* mac = CryptoFactory::i()->getMacAlgorithm(algo); + if (mac == NULL) return CKR_MECHANISM_INVALID; + + SymmetricKey* pubkey = new SymmetricKey(); + + if (getSymmetricKey(pubkey, token, key) != CKR_OK) + { + mac->recycleKey(pubkey); + CryptoFactory::i()->recycleMacAlgorithm(mac); + return CKR_GENERAL_ERROR; + } + + // Adjust key bit length + pubkey->setBitLen(pubkey->getKeyBits().size() * bb); + + // Check key size + if (pubkey->getBitLen() < (minSize*8)) + { + mac->recycleKey(pubkey); + CryptoFactory::i()->recycleMacAlgorithm(mac); + return CKR_KEY_SIZE_RANGE; + } + + // Initialize verifying + if (!mac->verifyInit(pubkey)) + { + mac->recycleKey(pubkey); + CryptoFactory::i()->recycleMacAlgorithm(mac); + return CKR_MECHANISM_INVALID; + } + + session->setOpType(SESSION_OP_VERIFY); + session->setMacOp(mac); + session->setAllowMultiPartOp(true); + session->setAllowSinglePartOp(true); + session->setSymmetricKey(pubkey); + + return CKR_OK; +} + +// AsymmetricAlgorithm version of C_VerifyInit +CK_RV SoftHSM::AsymVerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we have another operation + if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE; + + // Get the token + Token* token = session->getToken(); + if (token == NULL) return CKR_GENERAL_ERROR; + + // Check the key handle. + OSObject *key = (OSObject *)handleManager->getObject(hKey); + if (key == NULL_PTR || !key->isValid()) return CKR_OBJECT_HANDLE_INVALID; + + CK_BBOOL isOnToken = key->getBooleanValue(CKA_TOKEN, false); + CK_BBOOL isPrivate = key->getBooleanValue(CKA_PRIVATE, true); + + // Check read user credentials + CK_RV rv = haveRead(session->getState(), isOnToken, isPrivate); + if (rv != CKR_OK) + { + if (rv == CKR_USER_NOT_LOGGED_IN) + INFO_MSG("User is not authorized"); + + return rv; + } + + // Check if key can be used for verifying + if (!key->getBooleanValue(CKA_VERIFY, false)) + return CKR_KEY_FUNCTION_NOT_PERMITTED; + + // Check if the specified mechanism is allowed for the key + if (!isMechanismPermitted(key, pMechanism)) + return CKR_MECHANISM_INVALID; + + // Get the asymmetric algorithm matching the mechanism + AsymMech::Type mechanism = AsymMech::Unknown; + void* param = NULL; + size_t paramLen = 0; + RSA_PKCS_PSS_PARAMS pssParam; + bool bAllowMultiPartOp; + bool isRSA = false; + bool isDSA = false; + bool isECDSA = false; + switch(pMechanism->mechanism) { + case CKM_RSA_PKCS: + mechanism = AsymMech::RSA_PKCS; + bAllowMultiPartOp = false; + isRSA = true; + break; + case CKM_RSA_X_509: + mechanism = AsymMech::RSA; + bAllowMultiPartOp = false; + isRSA = true; + break; +#ifndef WITH_FIPS + case CKM_MD5_RSA_PKCS: + mechanism = AsymMech::RSA_MD5_PKCS; + bAllowMultiPartOp = true; + isRSA = true; + break; +#endif + case CKM_SHA1_RSA_PKCS: + mechanism = AsymMech::RSA_SHA1_PKCS; + bAllowMultiPartOp = true; + isRSA = true; + break; + case CKM_SHA224_RSA_PKCS: + mechanism = AsymMech::RSA_SHA224_PKCS; + bAllowMultiPartOp = true; + isRSA = true; + break; + case CKM_SHA256_RSA_PKCS: + mechanism = AsymMech::RSA_SHA256_PKCS; + bAllowMultiPartOp = true; + isRSA = true; + break; + case CKM_SHA384_RSA_PKCS: + mechanism = AsymMech::RSA_SHA384_PKCS; + bAllowMultiPartOp = true; + isRSA = true; + break; + case CKM_SHA512_RSA_PKCS: + mechanism = AsymMech::RSA_SHA512_PKCS; + bAllowMultiPartOp = true; + isRSA = true; + break; +#ifdef WITH_RAW_PSS + case CKM_RSA_PKCS_PSS: + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS)) + { + ERROR_MSG("Invalid parameters"); + return CKR_ARGUMENTS_BAD; + } + mechanism = AsymMech::RSA_PKCS_PSS; + + unsigned long expectedMgf; + switch(CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg) { + case CKM_SHA_1: + pssParam.hashAlg = HashAlgo::SHA1; + pssParam.mgf = AsymRSAMGF::MGF1_SHA1; + expectedMgf = CKG_MGF1_SHA1; + break; + case CKM_SHA224: + pssParam.hashAlg = HashAlgo::SHA224; + pssParam.mgf = AsymRSAMGF::MGF1_SHA224; + expectedMgf = CKG_MGF1_SHA224; + break; + case CKM_SHA256: + pssParam.hashAlg = HashAlgo::SHA256; + pssParam.mgf = AsymRSAMGF::MGF1_SHA256; + expectedMgf = CKG_MGF1_SHA256; + break; + case CKM_SHA384: + pssParam.hashAlg = HashAlgo::SHA384; + pssParam.mgf = AsymRSAMGF::MGF1_SHA384; + expectedMgf = CKG_MGF1_SHA384; + break; + case CKM_SHA512: + pssParam.hashAlg = HashAlgo::SHA512; + pssParam.mgf = AsymRSAMGF::MGF1_SHA512; + expectedMgf = CKG_MGF1_SHA512; + break; + default: + return CKR_ARGUMENTS_BAD; + } + + if (CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != expectedMgf) { + return CKR_ARGUMENTS_BAD; + } + + pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen; + param = &pssParam; + paramLen = sizeof(pssParam); + bAllowMultiPartOp = false; + isRSA = true; + break; +#endif + case CKM_SHA1_RSA_PKCS_PSS: + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) || + CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg != CKM_SHA_1 || + CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != CKG_MGF1_SHA1) + { + ERROR_MSG("Invalid parameters"); + return CKR_ARGUMENTS_BAD; + } + mechanism = AsymMech::RSA_SHA1_PKCS_PSS; + pssParam.hashAlg = HashAlgo::SHA1; + pssParam.mgf = AsymRSAMGF::MGF1_SHA1; + pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen; + param = &pssParam; + paramLen = sizeof(pssParam); + bAllowMultiPartOp = true; + isRSA = true; + break; + case CKM_SHA224_RSA_PKCS_PSS: + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) || + CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg != CKM_SHA224 || + CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != CKG_MGF1_SHA224) + { + ERROR_MSG("Invalid parameters"); + return CKR_ARGUMENTS_BAD; + } + mechanism = AsymMech::RSA_SHA224_PKCS_PSS; + pssParam.hashAlg = HashAlgo::SHA224; + pssParam.mgf = AsymRSAMGF::MGF1_SHA224; + pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen; + param = &pssParam; + paramLen = sizeof(pssParam); + bAllowMultiPartOp = true; + isRSA = true; + break; + case CKM_SHA256_RSA_PKCS_PSS: + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) || + CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg != CKM_SHA256 || + CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != CKG_MGF1_SHA256) + { + ERROR_MSG("Invalid parameters"); + return CKR_ARGUMENTS_BAD; + } + mechanism = AsymMech::RSA_SHA256_PKCS_PSS; + pssParam.hashAlg = HashAlgo::SHA256; + pssParam.mgf = AsymRSAMGF::MGF1_SHA256; + pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen; + param = &pssParam; + paramLen = sizeof(pssParam); + bAllowMultiPartOp = true; + isRSA = true; + break; + case CKM_SHA384_RSA_PKCS_PSS: + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) || + CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg != CKM_SHA384 || + CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != CKG_MGF1_SHA384) + { + ERROR_MSG("Invalid parameters"); + return CKR_ARGUMENTS_BAD; + } + mechanism = AsymMech::RSA_SHA384_PKCS_PSS; + pssParam.hashAlg = HashAlgo::SHA384; + pssParam.mgf = AsymRSAMGF::MGF1_SHA384; + pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen; + param = &pssParam; + paramLen = sizeof(pssParam); + bAllowMultiPartOp = true; + isRSA = true; + break; + case CKM_SHA512_RSA_PKCS_PSS: + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) || + CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg != CKM_SHA512 || + CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != CKG_MGF1_SHA512) + { + ERROR_MSG("Invalid parameters"); + return CKR_ARGUMENTS_BAD; + } + mechanism = AsymMech::RSA_SHA512_PKCS_PSS; + pssParam.hashAlg = HashAlgo::SHA512; + pssParam.mgf = AsymRSAMGF::MGF1_SHA512; + pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen; + param = &pssParam; + paramLen = sizeof(pssParam); + bAllowMultiPartOp = true; + isRSA = true; + break; + case CKM_DSA: + mechanism = AsymMech::DSA; + bAllowMultiPartOp = false; + isDSA = true; + break; + case CKM_DSA_SHA1: + mechanism = AsymMech::DSA_SHA1; + bAllowMultiPartOp = true; + isDSA = true; + break; + case CKM_DSA_SHA224: + mechanism = AsymMech::DSA_SHA224; + bAllowMultiPartOp = true; + isDSA = true; + break; + case CKM_DSA_SHA256: + mechanism = AsymMech::DSA_SHA256; + bAllowMultiPartOp = true; + isDSA = true; + break; + case CKM_DSA_SHA384: + mechanism = AsymMech::DSA_SHA384; + bAllowMultiPartOp = true; + isDSA = true; + break; + case CKM_DSA_SHA512: + mechanism = AsymMech::DSA_SHA512; + bAllowMultiPartOp = true; + isDSA = true; + break; +#ifdef WITH_ECC + case CKM_ECDSA: + mechanism = AsymMech::ECDSA; + bAllowMultiPartOp = false; + isECDSA = true; + break; +#endif +#ifdef WITH_GOST + case CKM_GOSTR3410: + mechanism = AsymMech::GOST; + bAllowMultiPartOp = false; + break; + case CKM_GOSTR3410_WITH_GOSTR3411: + mechanism = AsymMech::GOST_GOST; + bAllowMultiPartOp = true; + break; +#endif + default: + return CKR_MECHANISM_INVALID; + } + + AsymmetricAlgorithm* asymCrypto = NULL; + PublicKey* publicKey = NULL; + if (isRSA) + { + asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::RSA); + if (asymCrypto == NULL) return CKR_MECHANISM_INVALID; + + publicKey = asymCrypto->newPublicKey(); + if (publicKey == NULL) + { + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_HOST_MEMORY; + } + + if (getRSAPublicKey((RSAPublicKey*)publicKey, token, key) != CKR_OK) + { + asymCrypto->recyclePublicKey(publicKey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_GENERAL_ERROR; + } + } + else if (isDSA) + { + asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::DSA); + if (asymCrypto == NULL) return CKR_MECHANISM_INVALID; + + publicKey = asymCrypto->newPublicKey(); + if (publicKey == NULL) + { + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_HOST_MEMORY; + } + + if (getDSAPublicKey((DSAPublicKey*)publicKey, token, key) != CKR_OK) + { + asymCrypto->recyclePublicKey(publicKey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_GENERAL_ERROR; + } + } +#ifdef WITH_ECC + else if (isECDSA) + { + asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::ECDSA); + if (asymCrypto == NULL) return CKR_MECHANISM_INVALID; + + publicKey = asymCrypto->newPublicKey(); + if (publicKey == NULL) + { + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_HOST_MEMORY; + } + + if (getECPublicKey((ECPublicKey*)publicKey, token, key) != CKR_OK) + { + asymCrypto->recyclePublicKey(publicKey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_GENERAL_ERROR; + } + } +#endif + else + { +#ifdef WITH_GOST + asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::GOST); + if (asymCrypto == NULL) return CKR_MECHANISM_INVALID; + + publicKey = asymCrypto->newPublicKey(); + if (publicKey == NULL) + { + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_HOST_MEMORY; + } + + if (getGOSTPublicKey((GOSTPublicKey*)publicKey, token, key) != CKR_OK) + { + asymCrypto->recyclePublicKey(publicKey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_GENERAL_ERROR; + } +#else + return CKR_MECHANISM_INVALID; +#endif + } + + // Initialize verifying + if (bAllowMultiPartOp && !asymCrypto->verifyInit(publicKey,mechanism,param,paramLen)) + { + asymCrypto->recyclePublicKey(publicKey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_MECHANISM_INVALID; + } + + session->setOpType(SESSION_OP_VERIFY); + session->setAsymmetricCryptoOp(asymCrypto); + session->setMechanism(mechanism); + session->setParameters(param, paramLen); + session->setAllowMultiPartOp(bAllowMultiPartOp); + session->setAllowSinglePartOp(true); + session->setPublicKey(publicKey); + + return CKR_OK; +} + +// Initialise a verification operation using the specified key and mechanism +CK_RV SoftHSM::C_VerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + if (isMacMechanism(pMechanism)) + return MacVerifyInit(hSession, pMechanism, hKey); + else + return AsymVerifyInit(hSession, pMechanism, hKey); +} + +// MacAlgorithm version of C_Verify +static CK_RV MacVerify(Session* session, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) +{ + MacAlgorithm* mac = session->getMacOp(); + if (mac == NULL || !session->getAllowSinglePartOp()) + { + session->resetOp(); + return CKR_OPERATION_NOT_INITIALIZED; + } + + // Size of the signature + CK_ULONG size = mac->getMacSize(); + + // Check buffer size + if (ulSignatureLen != size) + { + ERROR_MSG("The size of the signature differs from the size of the mechanism"); + session->resetOp(); + return CKR_SIGNATURE_LEN_RANGE; + } + + // Get the data + ByteString data(pData, ulDataLen); + + // Verify the data + if (!mac->verifyUpdate(data)) + { + session->resetOp(); + return CKR_GENERAL_ERROR; + } + + // Get the signature + ByteString signature(pSignature, ulSignatureLen); + + // Verify the signature + if (!mac->verifyFinal(signature)) + { + session->resetOp(); + return CKR_SIGNATURE_INVALID; + } + + session->resetOp(); + return CKR_OK; +} + +// AsymmetricAlgorithm version of C_Verify +static CK_RV AsymVerify(Session* session, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) +{ + AsymmetricAlgorithm* asymCrypto = session->getAsymmetricCryptoOp(); + AsymMech::Type mechanism = session->getMechanism(); + PublicKey* publicKey = session->getPublicKey(); + size_t paramLen; + void* param = session->getParameters(paramLen); + if (asymCrypto == NULL || !session->getAllowSinglePartOp() || publicKey == NULL) + { + session->resetOp(); + return CKR_OPERATION_NOT_INITIALIZED; + } + + // Size of the signature + CK_ULONG size = publicKey->getOutputLength(); + + // Check buffer size + if (ulSignatureLen != size) + { + ERROR_MSG("The size of the signature differs from the size of the mechanism"); + session->resetOp(); + return CKR_SIGNATURE_LEN_RANGE; + } + + // Get the data + ByteString data; + + // We must allow input length <= k and therfore need to prepend the data with zeroes. + if (mechanism == AsymMech::RSA) { + data.wipe(size-ulDataLen); + } + + data += ByteString(pData, ulDataLen); + ByteString signature(pSignature, ulSignatureLen); + + // Verify the data + if (session->getAllowMultiPartOp()) + { + if (!asymCrypto->verifyUpdate(data) || + !asymCrypto->verifyFinal(signature)) + { + session->resetOp(); + return CKR_SIGNATURE_INVALID; + } + } + else if (!asymCrypto->verify(publicKey,data,signature,mechanism,param,paramLen)) + { + session->resetOp(); + return CKR_SIGNATURE_INVALID; + } + + session->resetOp(); + return CKR_OK; +} + +// Perform a single pass verification operation +CK_RV SoftHSM::C_Verify(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pData == NULL_PTR) return CKR_ARGUMENTS_BAD; + if (pSignature == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we are doing the correct operation + if (session->getOpType() != SESSION_OP_VERIFY) + return CKR_OPERATION_NOT_INITIALIZED; + + if (session->getMacOp() != NULL) + return MacVerify(session, pData, ulDataLen, + pSignature, ulSignatureLen); + else + return AsymVerify(session, pData, ulDataLen, + pSignature, ulSignatureLen); +} + +// MacAlgorithm version of C_VerifyUpdate +static CK_RV MacVerifyUpdate(Session* session, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) +{ + MacAlgorithm* mac = session->getMacOp(); + if (mac == NULL || !session->getAllowMultiPartOp()) + { + session->resetOp(); + return CKR_OPERATION_NOT_INITIALIZED; + } + + // Get the part + ByteString part(pPart, ulPartLen); + + // Verify the data + if (!mac->verifyUpdate(part)) + { + // verifyUpdate can't fail for a logical reason, so we assume total breakdown. + session->resetOp(); + return CKR_GENERAL_ERROR; + } + + session->setAllowSinglePartOp(false); + return CKR_OK; +} + +// AsymmetricAlgorithm version of C_VerifyUpdate +static CK_RV AsymVerifyUpdate(Session* session, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) +{ + AsymmetricAlgorithm* asymCrypto = session->getAsymmetricCryptoOp(); + if (asymCrypto == NULL || !session->getAllowMultiPartOp()) + { + session->resetOp(); + return CKR_OPERATION_NOT_INITIALIZED; + } + + // Get the part + ByteString part(pPart, ulPartLen); + + // Verify the data + if (!asymCrypto->verifyUpdate(part)) + { + // verifyUpdate can't fail for a logical reason, so we assume total breakdown. + session->resetOp(); + return CKR_GENERAL_ERROR; + } + + session->setAllowSinglePartOp(false); + return CKR_OK; +} + +// Update a running verification operation with additional data +CK_RV SoftHSM::C_VerifyUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pPart == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we are doing the correct operation + if (session->getOpType() != SESSION_OP_VERIFY) + return CKR_OPERATION_NOT_INITIALIZED; + + if (session->getMacOp() != NULL) + return MacVerifyUpdate(session, pPart, ulPartLen); + else + return AsymVerifyUpdate(session, pPart, ulPartLen); +} + +// MacAlgorithm version of C_SignFinal +static CK_RV MacVerifyFinal(Session* session, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) +{ + MacAlgorithm* mac = session->getMacOp(); + if (mac == NULL) + { + session->resetOp(); + return CKR_OPERATION_NOT_INITIALIZED; + } + + // Size of the signature + CK_ULONG size = mac->getMacSize(); + + // Check buffer size + if (ulSignatureLen != size) + { + ERROR_MSG("The size of the signature differs from the size of the mechanism"); + session->resetOp(); + return CKR_SIGNATURE_LEN_RANGE; + } + + // Get the signature + ByteString signature(pSignature, ulSignatureLen); + + // Verify the data + if (!mac->verifyFinal(signature)) + { + session->resetOp(); + return CKR_SIGNATURE_INVALID; + } + + session->resetOp(); + return CKR_OK; +} + +// AsymmetricAlgorithm version of C_VerifyFinal +static CK_RV AsymVerifyFinal(Session* session, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) +{ + AsymmetricAlgorithm* asymCrypto = session->getAsymmetricCryptoOp(); + PublicKey* publicKey = session->getPublicKey(); + if (asymCrypto == NULL || publicKey == NULL) + { + session->resetOp(); + return CKR_OPERATION_NOT_INITIALIZED; + } + + // Size of the signature + CK_ULONG size = publicKey->getOutputLength(); + + // Check buffer size + if (ulSignatureLen != size) + { + ERROR_MSG("The size of the signature differs from the size of the mechanism"); + session->resetOp(); + return CKR_SIGNATURE_LEN_RANGE; + } + + // Get the data + ByteString signature(pSignature, ulSignatureLen); + + // Verify the data + if (!asymCrypto->verifyFinal(signature)) + { + session->resetOp(); + return CKR_SIGNATURE_INVALID; + } + + session->resetOp(); + return CKR_OK; +} + +// Finalise the verification operation and check the signature +CK_RV SoftHSM::C_VerifyFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pSignature == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we are doing the correct operation + if (session->getOpType() != SESSION_OP_VERIFY || !session->getAllowMultiPartOp()) + return CKR_OPERATION_NOT_INITIALIZED; + + if (session->getMacOp() != NULL) + return MacVerifyFinal(session, pSignature, ulSignatureLen); + else + return AsymVerifyFinal(session, pSignature, ulSignatureLen); +} + +// Initialise a verification operation the allows recovery of the signed data from the signature +CK_RV SoftHSM::C_VerifyRecoverInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR /*pMechanism*/, CK_OBJECT_HANDLE /*hKey*/) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check if we have another operation + if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE; + + return CKR_FUNCTION_NOT_SUPPORTED; +} + +// Perform a single part verification operation and recover the signed data +CK_RV SoftHSM::C_VerifyRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR /*pSignature*/, CK_ULONG /*ulSignatureLen*/, CK_BYTE_PTR /*pData*/, CK_ULONG_PTR /*pulDataLen*/) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + return CKR_FUNCTION_NOT_SUPPORTED; +} + +// Update a running multi-part encryption and digesting operation +CK_RV SoftHSM::C_DigestEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR /*pPart*/, CK_ULONG /*ulPartLen*/, CK_BYTE_PTR /*pEncryptedPart*/, CK_ULONG_PTR /*pulEncryptedPartLen*/) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + return CKR_FUNCTION_NOT_SUPPORTED; +} + +// Update a running multi-part decryption and digesting operation +CK_RV SoftHSM::C_DecryptDigestUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR /*pPart*/, CK_ULONG /*ulPartLen*/, CK_BYTE_PTR /*pDecryptedPart*/, CK_ULONG_PTR /*pulDecryptedPartLen*/) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + return CKR_FUNCTION_NOT_SUPPORTED; +} + +// Update a running multi-part signing and encryption operation +CK_RV SoftHSM::C_SignEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR /*pPart*/, CK_ULONG /*ulPartLen*/, CK_BYTE_PTR /*pEncryptedPart*/, CK_ULONG_PTR /*pulEncryptedPartLen*/) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + return CKR_FUNCTION_NOT_SUPPORTED; +} + +// Update a running multi-part decryption and verification operation +CK_RV SoftHSM::C_DecryptVerifyUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR /*pEncryptedPart*/, CK_ULONG /*ulEncryptedPartLen*/, CK_BYTE_PTR /*pPart*/, CK_ULONG_PTR /*pulPartLen*/) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + return CKR_FUNCTION_NOT_SUPPORTED; +} + +// Generate a secret key or a domain parameter set using the specified mechanism +CK_RV SoftHSM::C_GenerateKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phKey) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD; + if (phKey == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check the mechanism, only accept DSA and DH parameters + // and symmetric ciphers + CK_OBJECT_CLASS objClass; + CK_KEY_TYPE keyType; + switch (pMechanism->mechanism) + { + case CKM_DSA_PARAMETER_GEN: + objClass = CKO_DOMAIN_PARAMETERS; + keyType = CKK_DSA; + break; + case CKM_DH_PKCS_PARAMETER_GEN: + objClass = CKO_DOMAIN_PARAMETERS; + keyType = CKK_DH; + break; +#ifndef WITH_FIPS + case CKM_DES_KEY_GEN: + objClass = CKO_SECRET_KEY; + keyType = CKK_DES; + break; +#endif + case CKM_DES2_KEY_GEN: + objClass = CKO_SECRET_KEY; + keyType = CKK_DES2; + break; + case CKM_DES3_KEY_GEN: + objClass = CKO_SECRET_KEY; + keyType = CKK_DES3; + break; + case CKM_AES_KEY_GEN: + objClass = CKO_SECRET_KEY; + keyType = CKK_AES; + break; + default: + return CKR_MECHANISM_INVALID; + } + + // Extract information from the template that is needed to create the object. + CK_BBOOL isOnToken = CK_FALSE; + CK_BBOOL isPrivate = CK_TRUE; + CK_CERTIFICATE_TYPE dummy; + bool isImplicit = true; + extractObjectInformation(pTemplate, ulCount, objClass, keyType, dummy, isOnToken, isPrivate, isImplicit); + + // Report errors and/or unexpected usage. + if (objClass != CKO_SECRET_KEY && objClass != CKO_DOMAIN_PARAMETERS) + return CKR_ATTRIBUTE_VALUE_INVALID; + if (pMechanism->mechanism == CKM_DSA_PARAMETER_GEN && + (objClass != CKO_DOMAIN_PARAMETERS || keyType != CKK_DSA)) + return CKR_TEMPLATE_INCONSISTENT; + if (pMechanism->mechanism == CKM_DH_PKCS_PARAMETER_GEN && + (objClass != CKO_DOMAIN_PARAMETERS || keyType != CKK_DH)) + return CKR_TEMPLATE_INCONSISTENT; + if (pMechanism->mechanism == CKM_DES_KEY_GEN && + (objClass != CKO_SECRET_KEY || keyType != CKK_DES)) + return CKR_TEMPLATE_INCONSISTENT; + if (pMechanism->mechanism == CKM_DES2_KEY_GEN && + (objClass != CKO_SECRET_KEY || keyType != CKK_DES2)) + return CKR_TEMPLATE_INCONSISTENT; + if (pMechanism->mechanism == CKM_DES3_KEY_GEN && + (objClass != CKO_SECRET_KEY || keyType != CKK_DES3)) + return CKR_TEMPLATE_INCONSISTENT; + if (pMechanism->mechanism == CKM_AES_KEY_GEN && + (objClass != CKO_SECRET_KEY || keyType != CKK_AES)) + return CKR_TEMPLATE_INCONSISTENT; + + // Check authorization + CK_RV rv = haveWrite(session->getState(), isOnToken, isPrivate); + if (rv != CKR_OK) + { + if (rv == CKR_USER_NOT_LOGGED_IN) + INFO_MSG("User is not authorized"); + if (rv == CKR_SESSION_READ_ONLY) + INFO_MSG("Session is read-only"); + + return rv; + } + + // Generate DSA domain parameters + if (pMechanism->mechanism == CKM_DSA_PARAMETER_GEN) + { + return this->generateDSAParameters(hSession, pTemplate, ulCount, phKey, isOnToken, isPrivate); + } + + // Generate DH domain parameters + if (pMechanism->mechanism == CKM_DH_PKCS_PARAMETER_GEN) + { + return this->generateDHParameters(hSession, pTemplate, ulCount, phKey, isOnToken, isPrivate); + } + + // Generate DES secret key + if (pMechanism->mechanism == CKM_DES_KEY_GEN) + { + return this->generateDES(hSession, pTemplate, ulCount, phKey, isOnToken, isPrivate); + } + + // Generate DES2 secret key + if (pMechanism->mechanism == CKM_DES2_KEY_GEN) + { + return this->generateDES2(hSession, pTemplate, ulCount, phKey, isOnToken, isPrivate); + } + + // Generate DES3 secret key + if (pMechanism->mechanism == CKM_DES3_KEY_GEN) + { + return this->generateDES3(hSession, pTemplate, ulCount, phKey, isOnToken, isPrivate); + } + + // Generate AES secret key + if (pMechanism->mechanism == CKM_AES_KEY_GEN) + { + return this->generateAES(hSession, pTemplate, ulCount, phKey, isOnToken, isPrivate); + } + + return CKR_GENERAL_ERROR; +} + +// Generate a key-pair using the specified mechanism +CK_RV SoftHSM::C_GenerateKeyPair +( + CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_ATTRIBUTE_PTR pPublicKeyTemplate, + CK_ULONG ulPublicKeyAttributeCount, + CK_ATTRIBUTE_PTR pPrivateKeyTemplate, + CK_ULONG ulPrivateKeyAttributeCount, + CK_OBJECT_HANDLE_PTR phPublicKey, + CK_OBJECT_HANDLE_PTR phPrivateKey +) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD; + if (phPublicKey == NULL_PTR) return CKR_ARGUMENTS_BAD; + if (phPrivateKey == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check the mechanism, only accept RSA, DSA, EC and DH key pair generation. + CK_KEY_TYPE keyType; + switch (pMechanism->mechanism) + { + case CKM_RSA_PKCS_KEY_PAIR_GEN: + keyType = CKK_RSA; + break; + case CKM_DSA_KEY_PAIR_GEN: + keyType = CKK_DSA; + break; + case CKM_DH_PKCS_KEY_PAIR_GEN: + keyType = CKK_DH; + break; +#ifdef WITH_ECC + case CKM_EC_KEY_PAIR_GEN: + keyType = CKK_EC; + break; +#endif +#ifdef WITH_GOST + case CKM_GOSTR3410_KEY_PAIR_GEN: + keyType = CKK_GOSTR3410; + break; +#endif + default: + return CKR_MECHANISM_INVALID; + } + CK_CERTIFICATE_TYPE dummy; + + // Extract information from the public key template that is needed to create the object. + CK_OBJECT_CLASS publicKeyClass = CKO_PUBLIC_KEY; + CK_BBOOL ispublicKeyToken = CK_FALSE; + CK_BBOOL ispublicKeyPrivate = CK_FALSE; + bool isPublicKeyImplicit = true; + extractObjectInformation(pPublicKeyTemplate, ulPublicKeyAttributeCount, publicKeyClass, keyType, dummy, ispublicKeyToken, ispublicKeyPrivate, isPublicKeyImplicit); + + // Report errors caused by accidental template mix-ups in the application using this cryptoki lib. + if (publicKeyClass != CKO_PUBLIC_KEY) + return CKR_ATTRIBUTE_VALUE_INVALID; + if (pMechanism->mechanism == CKM_RSA_PKCS_KEY_PAIR_GEN && keyType != CKK_RSA) + return CKR_TEMPLATE_INCONSISTENT; + if (pMechanism->mechanism == CKM_DSA_KEY_PAIR_GEN && keyType != CKK_DSA) + return CKR_TEMPLATE_INCONSISTENT; + if (pMechanism->mechanism == CKM_EC_KEY_PAIR_GEN && keyType != CKK_EC) + return CKR_TEMPLATE_INCONSISTENT; + if (pMechanism->mechanism == CKM_DH_PKCS_KEY_PAIR_GEN && keyType != CKK_DH) + return CKR_TEMPLATE_INCONSISTENT; + if (pMechanism->mechanism == CKM_GOSTR3410_KEY_PAIR_GEN && keyType != CKK_GOSTR3410) + return CKR_TEMPLATE_INCONSISTENT; + + // Extract information from the private key template that is needed to create the object. + CK_OBJECT_CLASS privateKeyClass = CKO_PRIVATE_KEY; + CK_BBOOL isprivateKeyToken = CK_FALSE; + CK_BBOOL isprivateKeyPrivate = CK_TRUE; + bool isPrivateKeyImplicit = true; + extractObjectInformation(pPrivateKeyTemplate, ulPrivateKeyAttributeCount, privateKeyClass, keyType, dummy, isprivateKeyToken, isprivateKeyPrivate, isPrivateKeyImplicit); + + // Report errors caused by accidental template mix-ups in the application using this cryptoki lib. + if (privateKeyClass != CKO_PRIVATE_KEY) + return CKR_ATTRIBUTE_VALUE_INVALID; + if (pMechanism->mechanism == CKM_RSA_PKCS_KEY_PAIR_GEN && keyType != CKK_RSA) + return CKR_TEMPLATE_INCONSISTENT; + if (pMechanism->mechanism == CKM_DSA_KEY_PAIR_GEN && keyType != CKK_DSA) + return CKR_TEMPLATE_INCONSISTENT; + if (pMechanism->mechanism == CKM_EC_KEY_PAIR_GEN && keyType != CKK_EC) + return CKR_TEMPLATE_INCONSISTENT; + if (pMechanism->mechanism == CKM_DH_PKCS_KEY_PAIR_GEN && keyType != CKK_DH) + return CKR_TEMPLATE_INCONSISTENT; + if (pMechanism->mechanism == CKM_GOSTR3410_KEY_PAIR_GEN && keyType != CKK_GOSTR3410) + return CKR_TEMPLATE_INCONSISTENT; + + // Check user credentials + CK_RV rv = haveWrite(session->getState(), ispublicKeyToken || isprivateKeyToken, ispublicKeyPrivate || isprivateKeyPrivate); + if (rv != CKR_OK) + { + if (rv == CKR_USER_NOT_LOGGED_IN) + INFO_MSG("User is not authorized"); + if (rv == CKR_SESSION_READ_ONLY) + INFO_MSG("Session is read-only"); + + return rv; + } + + // Generate RSA keys + if (pMechanism->mechanism == CKM_RSA_PKCS_KEY_PAIR_GEN) + { + return this->generateRSA(hSession, + pPublicKeyTemplate, ulPublicKeyAttributeCount, + pPrivateKeyTemplate, ulPrivateKeyAttributeCount, + phPublicKey, phPrivateKey, + ispublicKeyToken, ispublicKeyPrivate, isprivateKeyToken, isprivateKeyPrivate); + } + + // Generate DSA keys + if (pMechanism->mechanism == CKM_DSA_KEY_PAIR_GEN) + { + return this->generateDSA(hSession, + pPublicKeyTemplate, ulPublicKeyAttributeCount, + pPrivateKeyTemplate, ulPrivateKeyAttributeCount, + phPublicKey, phPrivateKey, + ispublicKeyToken, ispublicKeyPrivate, isprivateKeyToken, isprivateKeyPrivate); + } + + // Generate EC keys + if (pMechanism->mechanism == CKM_EC_KEY_PAIR_GEN) + { + return this->generateEC(hSession, + pPublicKeyTemplate, ulPublicKeyAttributeCount, + pPrivateKeyTemplate, ulPrivateKeyAttributeCount, + phPublicKey, phPrivateKey, + ispublicKeyToken, ispublicKeyPrivate, isprivateKeyToken, isprivateKeyPrivate); + } + + // Generate DH keys + if (pMechanism->mechanism == CKM_DH_PKCS_KEY_PAIR_GEN) + { + return this->generateDH(hSession, + pPublicKeyTemplate, ulPublicKeyAttributeCount, + pPrivateKeyTemplate, ulPrivateKeyAttributeCount, + phPublicKey, phPrivateKey, + ispublicKeyToken, ispublicKeyPrivate, isprivateKeyToken, isprivateKeyPrivate); + } + + // Generate GOST keys + if (pMechanism->mechanism == CKM_GOSTR3410_KEY_PAIR_GEN) + { + return this->generateGOST(hSession, + pPublicKeyTemplate, ulPublicKeyAttributeCount, + pPrivateKeyTemplate, ulPrivateKeyAttributeCount, + phPublicKey, phPrivateKey, + ispublicKeyToken, ispublicKeyPrivate, isprivateKeyToken, isprivateKeyPrivate); + } + + return CKR_GENERAL_ERROR; +} + +// Internal: Wrap blob using symmetric key +CK_RV SoftHSM::WrapKeySym +( + CK_MECHANISM_PTR pMechanism, + Token* token, + OSObject* wrapKey, + ByteString& keydata, + ByteString& wrapped +) +{ + // Get the symmetric algorithm matching the mechanism + SymAlgo::Type algo = SymAlgo::Unknown; + SymWrap::Type mode = SymWrap::Unknown; + size_t bb = 8; +#ifdef HAVE_AES_KEY_WRAP + CK_ULONG wrappedlen = keydata.size(); + + // [PKCS#11 v2.40, 2.14.3 AES Key Wrap] + // A key whose length is not a multiple of the AES Key Wrap block + // size (8 bytes) will be zero padded to fit. + CK_ULONG alignment = wrappedlen % 8; + if (alignment != 0) + { + keydata.resize(wrappedlen + 8 - alignment); + memset(&keydata[wrappedlen], 0, 8 - alignment); + wrappedlen = keydata.size(); + } +#endif + switch(pMechanism->mechanism) { +#ifdef HAVE_AES_KEY_WRAP + case CKM_AES_KEY_WRAP: + if ((wrappedlen < 16) || ((wrappedlen % 8) != 0)) + return CKR_KEY_SIZE_RANGE; + algo = SymAlgo::AES; + mode = SymWrap::AES_KEYWRAP; + break; +#endif +#ifdef HAVE_AES_KEY_WRAP_PAD + case CKM_AES_KEY_WRAP_PAD: + algo = SymAlgo::AES; + mode = SymWrap::AES_KEYWRAP_PAD; + break; +#endif + default: + return CKR_MECHANISM_INVALID; + } + SymmetricAlgorithm* cipher = CryptoFactory::i()->getSymmetricAlgorithm(algo); + if (cipher == NULL) return CKR_MECHANISM_INVALID; + + SymmetricKey* wrappingkey = new SymmetricKey(); + + if (getSymmetricKey(wrappingkey, token, wrapKey) != CKR_OK) + { + cipher->recycleKey(wrappingkey); + CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); + return CKR_GENERAL_ERROR; + } + + // adjust key bit length + wrappingkey->setBitLen(wrappingkey->getKeyBits().size() * bb); + + // Wrap the key + if (!cipher->wrapKey(wrappingkey, mode, keydata, wrapped)) + { + cipher->recycleKey(wrappingkey); + CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); + return CKR_GENERAL_ERROR; + } + + cipher->recycleKey(wrappingkey); + CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); + return CKR_OK; +} + +// Internal: Wrap blob using asymmetric key +CK_RV SoftHSM::WrapKeyAsym +( + CK_MECHANISM_PTR pMechanism, + Token* token, + OSObject* wrapKey, + ByteString& keydata, + ByteString& wrapped +) +{ + const size_t bb = 8; + AsymAlgo::Type algo = AsymAlgo::Unknown; + AsymMech::Type mech = AsymMech::Unknown; + + CK_ULONG modulus_length; + switch(pMechanism->mechanism) { + case CKM_RSA_PKCS: + case CKM_RSA_PKCS_OAEP: + algo = AsymAlgo::RSA; + if (!wrapKey->attributeExists(CKA_MODULUS_BITS)) + return CKR_GENERAL_ERROR; + modulus_length = wrapKey->getUnsignedLongValue(CKA_MODULUS_BITS, 0); + // adjust key bit length + modulus_length /= bb; + break; + + default: + return CKR_MECHANISM_INVALID; + } + + switch(pMechanism->mechanism) { + case CKM_RSA_PKCS: + mech = AsymMech::RSA_PKCS; + // RFC 3447 section 7.2.1 + if (keydata.size() > modulus_length - 11) + return CKR_KEY_SIZE_RANGE; + break; + + case CKM_RSA_PKCS_OAEP: + mech = AsymMech::RSA_PKCS_OAEP; + // SHA-1 is the only supported option + // PKCS#11 2.40 draft 2 section 2.1.8: input length <= k-2-2hashLen + if (keydata.size() > modulus_length - 2 - 2 * 160 / 8) + return CKR_KEY_SIZE_RANGE; + break; + + default: + return CKR_MECHANISM_INVALID; + } + + AsymmetricAlgorithm* cipher = CryptoFactory::i()->getAsymmetricAlgorithm(algo); + if (cipher == NULL) return CKR_MECHANISM_INVALID; + + PublicKey* publicKey = cipher->newPublicKey(); + if (publicKey == NULL) + { + CryptoFactory::i()->recycleAsymmetricAlgorithm(cipher); + return CKR_HOST_MEMORY; + } + + switch(pMechanism->mechanism) { + case CKM_RSA_PKCS: + case CKM_RSA_PKCS_OAEP: + if (getRSAPublicKey((RSAPublicKey*)publicKey, token, wrapKey) != CKR_OK) + { + cipher->recyclePublicKey(publicKey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(cipher); + return CKR_GENERAL_ERROR; + } + break; + + default: + return CKR_MECHANISM_INVALID; + } + // Wrap the key + if (!cipher->wrapKey(publicKey, keydata, wrapped, mech)) + { + cipher->recyclePublicKey(publicKey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(cipher); + return CKR_GENERAL_ERROR; + } + + cipher->recyclePublicKey(publicKey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(cipher); + + return CKR_OK; +} + + +// Wrap the specified key using the specified wrapping key and mechanism +CK_RV SoftHSM::C_WrapKey +( + CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hWrappingKey, + CK_OBJECT_HANDLE hKey, + CK_BYTE_PTR pWrappedKey, + CK_ULONG_PTR pulWrappedKeyLen +) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD; + if (pulWrappedKeyLen == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + CK_RV rv; + // Check the mechanism, only accept advanced AES key wrapping and RSA + switch(pMechanism->mechanism) + { +#ifdef HAVE_AES_KEY_WRAP + case CKM_AES_KEY_WRAP: +#endif +#ifdef HAVE_AES_KEY_WRAP_PAD + case CKM_AES_KEY_WRAP_PAD: +#endif + case CKM_RSA_PKCS: + // Does not handle optional init vector + if (pMechanism->pParameter != NULL_PTR || + pMechanism->ulParameterLen != 0) + return CKR_ARGUMENTS_BAD; + break; + case CKM_RSA_PKCS_OAEP: + rv = MechParamCheckRSAPKCSOAEP(pMechanism); + if (rv != CKR_OK) + return rv; + break; + + default: + return CKR_MECHANISM_INVALID; + } + + // Get the token + Token* token = session->getToken(); + if (token == NULL) return CKR_GENERAL_ERROR; + + // Check the wrapping key handle. + OSObject *wrapKey = (OSObject *)handleManager->getObject(hWrappingKey); + if (wrapKey == NULL_PTR || !wrapKey->isValid()) return CKR_WRAPPING_KEY_HANDLE_INVALID; + + CK_BBOOL isWrapKeyOnToken = wrapKey->getBooleanValue(CKA_TOKEN, false); + CK_BBOOL isWrapKeyPrivate = wrapKey->getBooleanValue(CKA_PRIVATE, true); + + // Check user credentials for the wrapping key + rv = haveRead(session->getState(), isWrapKeyOnToken, isWrapKeyPrivate); + if (rv != CKR_OK) + { + if (rv == CKR_USER_NOT_LOGGED_IN) + INFO_MSG("User is not authorized"); + + return rv; + } + + // Check wrapping key class and type + if ((pMechanism->mechanism == CKM_AES_KEY_WRAP || pMechanism->mechanism == CKM_AES_KEY_WRAP_PAD) && wrapKey->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED) != CKO_SECRET_KEY) + return CKR_WRAPPING_KEY_TYPE_INCONSISTENT; + if ((pMechanism->mechanism == CKM_RSA_PKCS || pMechanism->mechanism == CKM_RSA_PKCS_OAEP) && wrapKey->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED) != CKO_PUBLIC_KEY) + return CKR_WRAPPING_KEY_TYPE_INCONSISTENT; + if (pMechanism->mechanism == CKM_AES_KEY_WRAP && wrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_AES) + return CKR_WRAPPING_KEY_TYPE_INCONSISTENT; + if (pMechanism->mechanism == CKM_AES_KEY_WRAP_PAD && wrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_AES) + return CKR_WRAPPING_KEY_TYPE_INCONSISTENT; + if ((pMechanism->mechanism == CKM_RSA_PKCS || pMechanism->mechanism == CKM_RSA_PKCS_OAEP) && wrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_RSA) + return CKR_WRAPPING_KEY_TYPE_INCONSISTENT; + + // Check if the wrapping key can be used for wrapping + if (wrapKey->getBooleanValue(CKA_WRAP, false) == false) + return CKR_KEY_FUNCTION_NOT_PERMITTED; + + // Check if the specified mechanism is allowed for the wrapping key + if (!isMechanismPermitted(wrapKey, pMechanism)) + return CKR_MECHANISM_INVALID; + + // Check the to be wrapped key handle. + OSObject *key = (OSObject *)handleManager->getObject(hKey); + if (key == NULL_PTR || !key->isValid()) return CKR_KEY_HANDLE_INVALID; + + CK_BBOOL isKeyOnToken = key->getBooleanValue(CKA_TOKEN, false); + CK_BBOOL isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, true); + + // Check user credentials for the to be wrapped key + rv = haveRead(session->getState(), isKeyOnToken, isKeyPrivate); + if (rv != CKR_OK) + { + if (rv == CKR_USER_NOT_LOGGED_IN) + INFO_MSG("User is not authorized"); + + return rv; + } + + // Check if the to be wrapped key can be wrapped + if (key->getBooleanValue(CKA_EXTRACTABLE, false) == false) + return CKR_KEY_UNEXTRACTABLE; + if (key->getBooleanValue(CKA_WRAP_WITH_TRUSTED, false) && wrapKey->getBooleanValue(CKA_TRUSTED, false) == false) + return CKR_KEY_NOT_WRAPPABLE; + + // Check the class + CK_OBJECT_CLASS keyClass = key->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED); + if (keyClass != CKO_SECRET_KEY && keyClass != CKO_PRIVATE_KEY) + return CKR_KEY_NOT_WRAPPABLE; + // CKM_RSA_PKCS and CKM_RSA_PKCS_OAEP can be used only on SECRET keys: PKCS#11 2.40 draft 2 section 2.1.6 PKCS #1 v1.5 RSA & section 2.1.8 PKCS #1 RSA OAEP + if ((pMechanism->mechanism == CKM_RSA_PKCS || pMechanism->mechanism == CKM_RSA_PKCS_OAEP) && keyClass != CKO_SECRET_KEY) + return CKR_KEY_NOT_WRAPPABLE; + + // Verify the wrap template attribute + if (wrapKey->attributeExists(CKA_WRAP_TEMPLATE)) + { + OSAttribute attr = wrapKey->getAttribute(CKA_WRAP_TEMPLATE); + + if (attr.isAttributeMapAttribute()) + { + typedef std::map<CK_ATTRIBUTE_TYPE,OSAttribute> attrmap_type; + + const attrmap_type& map = attr.getAttributeMapValue(); + + for (attrmap_type::const_iterator it = map.begin(); it != map.end(); ++it) + { + if (!key->attributeExists(it->first)) + { + return CKR_KEY_NOT_WRAPPABLE; + } + + OSAttribute keyAttr = key->getAttribute(it->first); + ByteString v1, v2; + if (!keyAttr.peekValue(v1) || !it->second.peekValue(v2) || (v1 != v2)) + { + return CKR_KEY_NOT_WRAPPABLE; + } + } + } + } + + // Get the key data to encrypt + ByteString keydata; + if (keyClass == CKO_SECRET_KEY) + { + if (isKeyPrivate) + { + bool bOK = token->decrypt(key->getByteStringValue(CKA_VALUE), keydata); + if (!bOK) return CKR_GENERAL_ERROR; + } + else + { + keydata = key->getByteStringValue(CKA_VALUE); + } + } + else + { + CK_KEY_TYPE keyType = key->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED); + AsymAlgo::Type alg = AsymAlgo::Unknown; + switch (keyType) { + case CKK_RSA: + alg = AsymAlgo::RSA; + break; + case CKK_DSA: + alg = AsymAlgo::DSA; + break; + case CKK_DH: + alg = AsymAlgo::DH; + break; +#ifdef WITH_ECC + case CKK_EC: + // can be ecdh too but it doesn't matter + alg = AsymAlgo::ECDSA; + break; +#endif + default: + return CKR_KEY_NOT_WRAPPABLE; + } + AsymmetricAlgorithm* asymCrypto = NULL; + PrivateKey* privateKey = NULL; + asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(alg); + if (asymCrypto == NULL) + return CKR_GENERAL_ERROR; + privateKey = asymCrypto->newPrivateKey(); + if (privateKey == NULL) + { + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_HOST_MEMORY; + } + switch (keyType) { + case CKK_RSA: + rv = getRSAPrivateKey((RSAPrivateKey*)privateKey, token, key); + break; + case CKK_DSA: + rv = getDSAPrivateKey((DSAPrivateKey*)privateKey, token, key); + break; + case CKK_DH: + rv = getDHPrivateKey((DHPrivateKey*)privateKey, token, key); + break; +#ifdef WITH_ECC + case CKK_EC: + rv = getECPrivateKey((ECPrivateKey*)privateKey, token, key); + break; +#endif + } + if (rv != CKR_OK) + { + asymCrypto->recyclePrivateKey(privateKey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_GENERAL_ERROR; + } + keydata = privateKey->PKCS8Encode(); + asymCrypto->recyclePrivateKey(privateKey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + } + if (keydata.size() == 0) + return CKR_KEY_NOT_WRAPPABLE; + + keyClass = wrapKey->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED); + ByteString wrapped; + if (keyClass == CKO_SECRET_KEY) + rv = SoftHSM::WrapKeySym(pMechanism, token, wrapKey, keydata, wrapped); + else + rv = SoftHSM::WrapKeyAsym(pMechanism, token, wrapKey, keydata, wrapped); + if (rv != CKR_OK) + return rv; + + if (pWrappedKey != NULL) { + if (*pulWrappedKeyLen >= wrapped.size()) + memcpy(pWrappedKey, wrapped.byte_str(), wrapped.size()); + else + rv = CKR_BUFFER_TOO_SMALL; + } + + *pulWrappedKeyLen = wrapped.size(); + return rv; +} + +// Internal: Unwrap blob using symmetric key +CK_RV SoftHSM::UnwrapKeySym +( + CK_MECHANISM_PTR pMechanism, + ByteString& wrapped, + Token* token, + OSObject* unwrapKey, + ByteString& keydata +) +{ + // Get the symmetric algorithm matching the mechanism + SymAlgo::Type algo = SymAlgo::Unknown; + SymWrap::Type mode = SymWrap::Unknown; + size_t bb = 8; + switch(pMechanism->mechanism) { +#ifdef HAVE_AES_KEY_WRAP + case CKM_AES_KEY_WRAP: + algo = SymAlgo::AES; + mode = SymWrap::AES_KEYWRAP; + break; +#endif +#ifdef HAVE_AES_KEY_WRAP_PAD + case CKM_AES_KEY_WRAP_PAD: + algo = SymAlgo::AES; + mode = SymWrap::AES_KEYWRAP_PAD; + break; +#endif + default: + return CKR_MECHANISM_INVALID; + } + SymmetricAlgorithm* cipher = CryptoFactory::i()->getSymmetricAlgorithm(algo); + if (cipher == NULL) return CKR_MECHANISM_INVALID; + + SymmetricKey* unwrappingkey = new SymmetricKey(); + + if (getSymmetricKey(unwrappingkey, token, unwrapKey) != CKR_OK) + { + cipher->recycleKey(unwrappingkey); + CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); + return CKR_GENERAL_ERROR; + } + + // adjust key bit length + unwrappingkey->setBitLen(unwrappingkey->getKeyBits().size() * bb); + + // Unwrap the key + CK_RV rv = CKR_OK; + if (!cipher->unwrapKey(unwrappingkey, mode, wrapped, keydata)) + rv = CKR_GENERAL_ERROR; + cipher->recycleKey(unwrappingkey); + CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); + return rv; +} + +// Internal: Unwrap blob using asymmetric key +CK_RV SoftHSM::UnwrapKeyAsym +( + CK_MECHANISM_PTR pMechanism, + ByteString& wrapped, + Token* token, + OSObject* unwrapKey, + ByteString& keydata +) +{ + // Get the symmetric algorithm matching the mechanism + AsymAlgo::Type algo = AsymAlgo::Unknown; + AsymMech::Type mode = AsymMech::Unknown; + switch(pMechanism->mechanism) { + case CKM_RSA_PKCS: + algo = AsymAlgo::RSA; + mode = AsymMech::RSA_PKCS; + break; + + case CKM_RSA_PKCS_OAEP: + algo = AsymAlgo::RSA; + mode = AsymMech::RSA_PKCS_OAEP; + break; + + default: + return CKR_MECHANISM_INVALID; + } + AsymmetricAlgorithm* cipher = CryptoFactory::i()->getAsymmetricAlgorithm(algo); + if (cipher == NULL) return CKR_MECHANISM_INVALID; + + PrivateKey* unwrappingkey = cipher->newPrivateKey(); + if (unwrappingkey == NULL) + { + CryptoFactory::i()->recycleAsymmetricAlgorithm(cipher); + return CKR_HOST_MEMORY; + } + + switch(pMechanism->mechanism) { + case CKM_RSA_PKCS: + case CKM_RSA_PKCS_OAEP: + if (getRSAPrivateKey((RSAPrivateKey*)unwrappingkey, token, unwrapKey) != CKR_OK) + { + cipher->recyclePrivateKey(unwrappingkey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(cipher); + return CKR_GENERAL_ERROR; + } + break; + + default: + return CKR_MECHANISM_INVALID; + } + + // Unwrap the key + CK_RV rv = CKR_OK; + if (!cipher->unwrapKey(unwrappingkey, wrapped, keydata, mode)) + rv = CKR_GENERAL_ERROR; + cipher->recyclePrivateKey(unwrappingkey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(cipher); + return rv; +} + +// Unwrap the specified key using the specified unwrapping key +CK_RV SoftHSM::C_UnwrapKey +( + CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hUnwrappingKey, + CK_BYTE_PTR pWrappedKey, + CK_ULONG ulWrappedKeyLen, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount, + CK_OBJECT_HANDLE_PTR hKey +) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD; + if (pWrappedKey == NULL_PTR) return CKR_ARGUMENTS_BAD; + if (pTemplate == NULL_PTR) return CKR_ARGUMENTS_BAD; + if (hKey == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + CK_RV rv; + // Check the mechanism + switch(pMechanism->mechanism) + { +#ifdef HAVE_AES_KEY_WRAP + case CKM_AES_KEY_WRAP: + if ((ulWrappedKeyLen < 24) || ((ulWrappedKeyLen % 8) != 0)) + return CKR_WRAPPED_KEY_LEN_RANGE; + // Does not handle optional init vector + if (pMechanism->pParameter != NULL_PTR || + pMechanism->ulParameterLen != 0) + return CKR_ARGUMENTS_BAD; + break; +#endif +#ifdef HAVE_AES_KEY_WRAP_PAD + case CKM_AES_KEY_WRAP_PAD: + if ((ulWrappedKeyLen < 16) || ((ulWrappedKeyLen % 8) != 0)) + return CKR_WRAPPED_KEY_LEN_RANGE; + // Does not handle optional init vector + if (pMechanism->pParameter != NULL_PTR || + pMechanism->ulParameterLen != 0) + return CKR_ARGUMENTS_BAD; + break; +#endif + case CKM_RSA_PKCS: + // Input length checks needs to be done later when unwrapping key is known + break; + case CKM_RSA_PKCS_OAEP: + rv = MechParamCheckRSAPKCSOAEP(pMechanism); + if (rv != CKR_OK) + return rv; + break; + + default: + return CKR_MECHANISM_INVALID; + } + + // Get the token + Token* token = session->getToken(); + if (token == NULL) return CKR_GENERAL_ERROR; + + // Check the unwrapping key handle. + OSObject *unwrapKey = (OSObject *)handleManager->getObject(hUnwrappingKey); + if (unwrapKey == NULL_PTR || !unwrapKey->isValid()) return CKR_UNWRAPPING_KEY_HANDLE_INVALID; + + CK_BBOOL isUnwrapKeyOnToken = unwrapKey->getBooleanValue(CKA_TOKEN, false); + CK_BBOOL isUnwrapKeyPrivate = unwrapKey->getBooleanValue(CKA_PRIVATE, true); + + // Check user credentials + rv = haveRead(session->getState(), isUnwrapKeyOnToken, isUnwrapKeyPrivate); + if (rv != CKR_OK) + { + if (rv == CKR_USER_NOT_LOGGED_IN) + INFO_MSG("User is not authorized"); + + return rv; + } + + // Check unwrapping key class and type + if ((pMechanism->mechanism == CKM_AES_KEY_WRAP || pMechanism->mechanism == CKM_AES_KEY_WRAP_PAD) && unwrapKey->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED) != CKO_SECRET_KEY) + return CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT; + if (pMechanism->mechanism == CKM_AES_KEY_WRAP && unwrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_AES) + return CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT; + if (pMechanism->mechanism == CKM_AES_KEY_WRAP_PAD && unwrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_AES) + return CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT; + if ((pMechanism->mechanism == CKM_RSA_PKCS || pMechanism->mechanism == CKM_RSA_PKCS_OAEP) && unwrapKey->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED) != CKO_PRIVATE_KEY) + return CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT; + if ((pMechanism->mechanism == CKM_RSA_PKCS || pMechanism->mechanism == CKM_RSA_PKCS_OAEP) && unwrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_RSA) + return CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT; + + // Check if the unwrapping key can be used for unwrapping + if (unwrapKey->getBooleanValue(CKA_UNWRAP, false) == false) + return CKR_KEY_FUNCTION_NOT_PERMITTED; + + // Check if the specified mechanism is allowed for the unwrap key + if (!isMechanismPermitted(unwrapKey, pMechanism)) + return CKR_MECHANISM_INVALID; + + // Extract information from the template that is needed to create the object. + CK_OBJECT_CLASS objClass; + CK_KEY_TYPE keyType; + CK_BBOOL isOnToken = CK_FALSE; + CK_BBOOL isPrivate = CK_TRUE; + CK_CERTIFICATE_TYPE dummy; + bool isImplicit = false; + rv = extractObjectInformation(pTemplate, ulCount, objClass, keyType, dummy, isOnToken, isPrivate, isImplicit); + if (rv != CKR_OK) + { + ERROR_MSG("Mandatory attribute not present in template"); + return rv; + } + + // Report errors and/or unexpected usage. + if (objClass != CKO_SECRET_KEY && objClass != CKO_PRIVATE_KEY) + return CKR_ATTRIBUTE_VALUE_INVALID; + // Key type will be handled at object creation + + // Check authorization + rv = haveWrite(session->getState(), isOnToken, isPrivate); + if (rv != CKR_OK) + { + if (rv == CKR_USER_NOT_LOGGED_IN) + INFO_MSG("User is not authorized"); + if (rv == CKR_SESSION_READ_ONLY) + INFO_MSG("Session is read-only"); + + return rv; + } + + // Build unwrapped key template + const CK_ULONG maxAttribs = 32; + 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)) + return CKR_TEMPLATE_INCONSISTENT; + for (CK_ULONG i = 0; i < ulCount; ++i) + { + switch (pTemplate[i].type) + { + case CKA_CLASS: + case CKA_TOKEN: + case CKA_PRIVATE: + case CKA_KEY_TYPE: + continue; + default: + secretAttribs[secretAttribsCount++] = pTemplate[i]; + } + } + + // Apply the unwrap template + if (unwrapKey->attributeExists(CKA_UNWRAP_TEMPLATE)) + { + OSAttribute unwrapAttr = unwrapKey->getAttribute(CKA_UNWRAP_TEMPLATE); + + if (unwrapAttr.isAttributeMapAttribute()) + { + typedef std::map<CK_ATTRIBUTE_TYPE,OSAttribute> attrmap_type; + + const attrmap_type& map = unwrapAttr.getAttributeMapValue(); + + for (attrmap_type::const_iterator it = map.begin(); it != map.end(); ++it) + { + CK_ATTRIBUTE* attr = NULL; + for (CK_ULONG i = 0; i < secretAttribsCount; ++i) + { + if (it->first == secretAttribs[i].type) + { + if (attr != NULL) + { + return CKR_TEMPLATE_INCONSISTENT; + } + attr = &secretAttribs[i]; + ByteString value; + it->second.peekValue(value); + if (attr->ulValueLen != value.size()) + { + return CKR_TEMPLATE_INCONSISTENT; + } + if (memcmp(attr->pValue, value.const_byte_str(), value.size()) != 0) + { + return CKR_TEMPLATE_INCONSISTENT; + } + } + } + if (attr == NULL) + { + return CKR_TEMPLATE_INCONSISTENT; + } + } + } + } + + *hKey = CK_INVALID_HANDLE; + + // Unwrap the key + ByteString wrapped(pWrappedKey, ulWrappedKeyLen); + ByteString keydata; + if (unwrapKey->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED) == CKO_SECRET_KEY) + rv = UnwrapKeySym(pMechanism, wrapped, token, unwrapKey, keydata); + else if (unwrapKey->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED) == CKO_PRIVATE_KEY) + rv = UnwrapKeyAsym(pMechanism, wrapped, token, unwrapKey, keydata); + else + rv = CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT; + if (rv != CKR_OK) + return rv; + + // Create the secret object using C_CreateObject + rv = this->CreateObject(hSession, secretAttribs, secretAttribsCount, hKey, OBJECT_OP_UNWRAP); + + // Store the attributes that are being supplied + if (rv == CKR_OK) + { + OSObject* osobject = (OSObject*)handleManager->getObject(*hKey); + if (osobject == NULL_PTR || !osobject->isValid()) + rv = CKR_FUNCTION_FAILED; + if (osobject->startTransaction()) + { + bool bOK = true; + + // Common Attributes + bOK = bOK && osobject->setAttribute(CKA_LOCAL, false); + + // Common Secret Key Attributes + bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE, false); + bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE, false); + + // Secret Attributes + if (objClass == CKO_SECRET_KEY) + { + ByteString value; + if (isPrivate) + token->encrypt(keydata, value); + else + value = keydata; + bOK = bOK && osobject->setAttribute(CKA_VALUE, value); + } + else if (keyType == CKK_RSA) + { + bOK = bOK && setRSAPrivateKey(osobject, keydata, token, isPrivate != CK_FALSE); + } + else if (keyType == CKK_DSA) + { + bOK = bOK && setDSAPrivateKey(osobject, keydata, token, isPrivate != CK_FALSE); + } + else if (keyType == CKK_DH) + { + bOK = bOK && setDHPrivateKey(osobject, keydata, token, isPrivate != CK_FALSE); + } + else if (keyType == CKK_EC) + { + bOK = bOK && setECPrivateKey(osobject, keydata, token, isPrivate != CK_FALSE); + } + else + bOK = false; + + if (bOK) + bOK = osobject->commitTransaction(); + else + osobject->abortTransaction(); + + if (!bOK) + rv = CKR_FUNCTION_FAILED; + } + else + rv = CKR_FUNCTION_FAILED; + } + + // Remove secret that may have been created already when the function fails. + if (rv != CKR_OK) + { + if (*hKey != CK_INVALID_HANDLE) + { + OSObject* obj = (OSObject*)handleManager->getObject(*hKey); + handleManager->destroyObject(*hKey); + if (obj) obj->destroyObject(); + *hKey = CK_INVALID_HANDLE; + } + + } + + return rv; +} + +// Derive a key from the specified base key +CK_RV SoftHSM::C_DeriveKey +( + CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hBaseKey, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount, + CK_OBJECT_HANDLE_PTR phKey +) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD; + if (pTemplate == NULL_PTR) return CKR_ARGUMENTS_BAD; + if (phKey == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Check the mechanism, only accept DH and ECDH derive + switch (pMechanism->mechanism) + { + case CKM_DH_PKCS_DERIVE: +#ifdef WITH_ECC + case CKM_ECDH1_DERIVE: +#endif +#ifndef WITH_FIPS + case CKM_DES_ECB_ENCRYPT_DATA: + case CKM_DES_CBC_ENCRYPT_DATA: +#endif + case CKM_DES3_ECB_ENCRYPT_DATA: + case CKM_DES3_CBC_ENCRYPT_DATA: + case CKM_AES_ECB_ENCRYPT_DATA: + case CKM_AES_CBC_ENCRYPT_DATA: + break; + default: + ERROR_MSG("Invalid mechanism"); + return CKR_MECHANISM_INVALID; + } + + // Get the token + Token* token = session->getToken(); + if (token == NULL) return CKR_GENERAL_ERROR; + + // Check the key handle. + OSObject *key = (OSObject *)handleManager->getObject(hBaseKey); + if (key == NULL_PTR || !key->isValid()) return CKR_OBJECT_HANDLE_INVALID; + + CK_BBOOL isKeyOnToken = key->getBooleanValue(CKA_TOKEN, false); + CK_BBOOL isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, true); + + // Check user credentials + CK_RV rv = haveRead(session->getState(), isKeyOnToken, isKeyPrivate); + if (rv != CKR_OK) + { + if (rv == CKR_USER_NOT_LOGGED_IN) + INFO_MSG("User is not authorized"); + + return rv; + } + + // Check if key can be used for derive + if (!key->getBooleanValue(CKA_DERIVE, false)) + return CKR_KEY_FUNCTION_NOT_PERMITTED; + + // Check if the specified mechanism is allowed for the key + if (!isMechanismPermitted(key, pMechanism)) + return CKR_MECHANISM_INVALID; + + // Extract information from the template that is needed to create the object. + CK_OBJECT_CLASS objClass; + CK_KEY_TYPE keyType; + CK_BBOOL isOnToken = CK_FALSE; + CK_BBOOL isPrivate = CK_TRUE; + CK_CERTIFICATE_TYPE dummy; + bool isImplicit = false; + rv = extractObjectInformation(pTemplate, ulCount, objClass, keyType, dummy, isOnToken, isPrivate, isImplicit); + if (rv != CKR_OK) + { + ERROR_MSG("Mandatory attribute not present in template"); + return rv; + } + + // Report errors and/or unexpected usage. + if (objClass != CKO_SECRET_KEY) + return CKR_ATTRIBUTE_VALUE_INVALID; + if (keyType != CKK_GENERIC_SECRET && + keyType != CKK_DES && + keyType != CKK_DES2 && + keyType != CKK_DES3 && + keyType != CKK_AES) + return CKR_TEMPLATE_INCONSISTENT; + + // Check authorization + rv = haveWrite(session->getState(), isOnToken, isPrivate); + if (rv != CKR_OK) + { + if (rv == CKR_USER_NOT_LOGGED_IN) + INFO_MSG("User is not authorized"); + if (rv == CKR_SESSION_READ_ONLY) + INFO_MSG("Session is read-only"); + + return rv; + } + + // Derive DH secret + if (pMechanism->mechanism == CKM_DH_PKCS_DERIVE) + { + // Check key class and type + if (key->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED) != CKO_PRIVATE_KEY) + return CKR_KEY_TYPE_INCONSISTENT; + if (key->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_DH) + return CKR_KEY_TYPE_INCONSISTENT; + + return this->deriveDH(hSession, pMechanism, hBaseKey, pTemplate, ulCount, phKey, keyType, isOnToken, isPrivate); + } + +#ifdef WITH_ECC + // Derive ECDH secret + if (pMechanism->mechanism == CKM_ECDH1_DERIVE) + { + // Check key class and type + if (key->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED) != CKO_PRIVATE_KEY) + return CKR_KEY_TYPE_INCONSISTENT; + if (key->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_EC) + return CKR_KEY_TYPE_INCONSISTENT; + + return this->deriveECDH(hSession, pMechanism, hBaseKey, pTemplate, ulCount, phKey, keyType, isOnToken, isPrivate); + } +#endif + + // Derive symmetric secret + if (pMechanism->mechanism == CKM_DES_ECB_ENCRYPT_DATA || + pMechanism->mechanism == CKM_DES_CBC_ENCRYPT_DATA || + pMechanism->mechanism == CKM_DES3_ECB_ENCRYPT_DATA || + pMechanism->mechanism == CKM_DES3_CBC_ENCRYPT_DATA || + pMechanism->mechanism == CKM_AES_ECB_ENCRYPT_DATA || + pMechanism->mechanism == CKM_AES_CBC_ENCRYPT_DATA) + { + // Check key class and type + CK_KEY_TYPE baseKeyType = key->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED); + if (key->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED) != CKO_SECRET_KEY) + return CKR_KEY_TYPE_INCONSISTENT; + if (pMechanism->mechanism == CKM_DES_ECB_ENCRYPT_DATA && + baseKeyType != CKK_DES) + return CKR_KEY_TYPE_INCONSISTENT; + if (pMechanism->mechanism == CKM_DES_CBC_ENCRYPT_DATA && + baseKeyType != CKK_DES) + return CKR_KEY_TYPE_INCONSISTENT; + if (pMechanism->mechanism == CKM_DES3_ECB_ENCRYPT_DATA && + baseKeyType != CKK_DES2 && baseKeyType != CKK_DES3) + return CKR_KEY_TYPE_INCONSISTENT; + if (pMechanism->mechanism == CKM_DES3_CBC_ENCRYPT_DATA && + baseKeyType != CKK_DES2 && baseKeyType != CKK_DES3) + return CKR_KEY_TYPE_INCONSISTENT; + if (pMechanism->mechanism == CKM_AES_ECB_ENCRYPT_DATA && + baseKeyType != CKK_AES) + return CKR_KEY_TYPE_INCONSISTENT; + if (pMechanism->mechanism == CKM_AES_CBC_ENCRYPT_DATA && + baseKeyType != CKK_AES) + return CKR_KEY_TYPE_INCONSISTENT; + + return this->deriveSymmetric(hSession, pMechanism, hBaseKey, pTemplate, ulCount, phKey, keyType, isOnToken, isPrivate); + } + + return CKR_MECHANISM_INVALID; +} + +// Seed the random number generator with new data +CK_RV SoftHSM::C_SeedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed, CK_ULONG ulSeedLen) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pSeed == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Get the RNG + RNG* rng = CryptoFactory::i()->getRNG(); + if (rng == NULL) return CKR_GENERAL_ERROR; + + // Seed the RNG + ByteString seed(pSeed, ulSeedLen); + rng->seed(seed); + + return CKR_OK; +} + +// Generate the specified amount of random data +CK_RV SoftHSM::C_GenerateRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pRandomData, CK_ULONG ulRandomLen) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pRandomData == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Get the RNG + RNG* rng = CryptoFactory::i()->getRNG(); + if (rng == NULL) return CKR_GENERAL_ERROR; + + // Generate random data + ByteString randomData; + if (!rng->generateRandom(randomData, ulRandomLen)) return CKR_GENERAL_ERROR; + + // Return random data + if (ulRandomLen != 0) + { + memcpy(pRandomData, randomData.byte_str(), ulRandomLen); + } + + return CKR_OK; +} + +// Legacy function +CK_RV SoftHSM::C_GetFunctionStatus(CK_SESSION_HANDLE hSession) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + return CKR_FUNCTION_NOT_PARALLEL; +} + +// Legacy function +CK_RV SoftHSM::C_CancelFunction(CK_SESSION_HANDLE hSession) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + return CKR_FUNCTION_NOT_PARALLEL; +} + +// Wait or poll for a slot event on the specified slot +CK_RV SoftHSM::C_WaitForSlotEvent(CK_FLAGS /*flags*/, CK_SLOT_ID_PTR /*pSlot*/, CK_VOID_PTR /*pReserved*/) +{ + return CKR_FUNCTION_NOT_SUPPORTED; +} + +// Generate an AES secret key +CK_RV SoftHSM::generateAES +(CK_SESSION_HANDLE hSession, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount, + CK_OBJECT_HANDLE_PTR phKey, + CK_BBOOL isOnToken, + CK_BBOOL isPrivate) +{ + *phKey = CK_INVALID_HANDLE; + + // 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 keyLen = 0; + bool checkValue = true; + for (CK_ULONG i = 0; i < ulCount; i++) + { + switch (pTemplate[i].type) + { + 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; + } + keyLen = *(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; + } + } + + // CKA_VALUE_LEN must be specified + if (keyLen == 0) + { + INFO_MSG("Missing CKA_VALUE_LEN in pTemplate"); + return CKR_TEMPLATE_INCOMPLETE; + } + + // keyLen must be 16, 24, or 32 + if (keyLen != 16 && keyLen != 24 && keyLen != 32) + { + INFO_MSG("bad AES key length"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + + // Generate the secret key + AESKey* key = new AESKey(keyLen * 8); + SymmetricAlgorithm* aes = CryptoFactory::i()->getSymmetricAlgorithm(SymAlgo::AES); + if (aes == NULL) + { + ERROR_MSG("Could not get SymmetricAlgorithm"); + delete key; + return CKR_GENERAL_ERROR; + } + RNG* rng = CryptoFactory::i()->getRNG(); + if (rng == NULL) + { + ERROR_MSG("Could not get RNG"); + aes->recycleKey(key); + CryptoFactory::i()->recycleSymmetricAlgorithm(aes); + return CKR_GENERAL_ERROR; + } + if (!aes->generateKey(*key, rng)) + { + ERROR_MSG("Could not generate AES secret key"); + aes->recycleKey(key); + CryptoFactory::i()->recycleSymmetricAlgorithm(aes); + return CKR_GENERAL_ERROR; + } + + CK_RV rv = CKR_OK; + + // Create the secret key object using C_CreateObject + const CK_ULONG maxAttribs = 32; + CK_OBJECT_CLASS objClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_AES; + CK_ATTRIBUTE keyAttribs[maxAttribs] = { + { CKA_CLASS, &objClass, sizeof(objClass) }, + { CKA_TOKEN, &isOnToken, sizeof(isOnToken) }, + { CKA_PRIVATE, &isPrivate, sizeof(isPrivate) }, + { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, + }; + CK_ULONG keyAttribsCount = 4; + + // Add the additional + if (ulCount > (maxAttribs - keyAttribsCount)) + 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: + keyAttribs[keyAttribsCount++] = pTemplate[i]; + } + } + + if (rv == CKR_OK) + rv = this->CreateObject(hSession, keyAttribs, keyAttribsCount, phKey,OBJECT_OP_GENERATE); + + // 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,true); + CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_AES_KEY_GEN; + bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism); + + // Common Secret Key Attributes + bool bAlwaysSensitive = osobject->getBooleanValue(CKA_SENSITIVE, false); + bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,bAlwaysSensitive); + bool bNeverExtractable = osobject->getBooleanValue(CKA_EXTRACTABLE, false) == false; + bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE, bNeverExtractable); + + // AES Secret Key Attributes + ByteString value; + ByteString kcv; + if (isPrivate) + { + token->encrypt(key->getKeyBits(), value); + token->encrypt(key->getKeyCheckValue(), kcv); + } + else + { + value = key->getKeyBits(); + kcv = key->getKeyCheckValue(); + } + 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 + aes->recycleKey(key); + CryptoFactory::i()->recycleSymmetricAlgorithm(aes); + + // Remove the key that may have been created already when the function fails. + if (rv != CKR_OK) + { + if (*phKey != CK_INVALID_HANDLE) + { + OSObject* oskey = (OSObject*)handleManager->getObject(*phKey); + handleManager->destroyObject(*phKey); + if (oskey) oskey->destroyObject(); + *phKey = CK_INVALID_HANDLE; + } + } + + return rv; +} + +// Generate a DES secret key +CK_RV SoftHSM::generateDES +(CK_SESSION_HANDLE hSession, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount, + CK_OBJECT_HANDLE_PTR phKey, + CK_BBOOL isOnToken, + CK_BBOOL isPrivate) +{ + *phKey = CK_INVALID_HANDLE; + + // 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 + bool checkValue = true; + for (CK_ULONG i = 0; i < ulCount; i++) + { + switch (pTemplate[i].type) + { + 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; + } + } + + // Generate the secret key + DESKey* key = new DESKey(56); + SymmetricAlgorithm* des = CryptoFactory::i()->getSymmetricAlgorithm(SymAlgo::DES); + if (des == NULL) + { + ERROR_MSG("Could not get SymmetricAlgorithm"); + delete key; + return CKR_GENERAL_ERROR; + } + RNG* rng = CryptoFactory::i()->getRNG(); + if (rng == NULL) + { + ERROR_MSG("Could not get RNG"); + des->recycleKey(key); + CryptoFactory::i()->recycleSymmetricAlgorithm(des); + return CKR_GENERAL_ERROR; + } + if (!des->generateKey(*key, rng)) + { + ERROR_MSG("Could not generate DES secret key"); + des->recycleKey(key); + CryptoFactory::i()->recycleSymmetricAlgorithm(des); + return CKR_GENERAL_ERROR; + } + + CK_RV rv = CKR_OK; + + // Create the secret key object using C_CreateObject + const CK_ULONG maxAttribs = 32; + CK_OBJECT_CLASS objClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_DES; + CK_ATTRIBUTE keyAttribs[maxAttribs] = { + { CKA_CLASS, &objClass, sizeof(objClass) }, + { CKA_TOKEN, &isOnToken, sizeof(isOnToken) }, + { CKA_PRIVATE, &isPrivate, sizeof(isPrivate) }, + { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, + }; + CK_ULONG keyAttribsCount = 4; + + // Add the additional + if (ulCount > (maxAttribs - keyAttribsCount)) + 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: + keyAttribs[keyAttribsCount++] = pTemplate[i]; + } + } + + if (rv == CKR_OK) + rv = this->CreateObject(hSession, keyAttribs, keyAttribsCount, phKey,OBJECT_OP_GENERATE); + + // 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,true); + CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_DES_KEY_GEN; + bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism); + + // Common Secret Key Attributes + bool bAlwaysSensitive = osobject->getBooleanValue(CKA_SENSITIVE, false); + bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,bAlwaysSensitive); + bool bNeverExtractable = osobject->getBooleanValue(CKA_EXTRACTABLE, false) == false; + bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE, bNeverExtractable); + + // DES Secret Key Attributes + ByteString value; + ByteString kcv; + if (isPrivate) + { + token->encrypt(key->getKeyBits(), value); + token->encrypt(key->getKeyCheckValue(), kcv); + } + else + { + value = key->getKeyBits(); + kcv = key->getKeyCheckValue(); + } + 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 + des->recycleKey(key); + CryptoFactory::i()->recycleSymmetricAlgorithm(des); + + // Remove the key that may have been created already when the function fails. + if (rv != CKR_OK) + { + if (*phKey != CK_INVALID_HANDLE) + { + OSObject* oskey = (OSObject*)handleManager->getObject(*phKey); + handleManager->destroyObject(*phKey); + if (oskey) oskey->destroyObject(); + *phKey = CK_INVALID_HANDLE; + } + } + + return rv; +} + +// Generate a DES2 secret key +CK_RV SoftHSM::generateDES2 +(CK_SESSION_HANDLE hSession, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount, + CK_OBJECT_HANDLE_PTR phKey, + CK_BBOOL isOnToken, + CK_BBOOL isPrivate) +{ + *phKey = CK_INVALID_HANDLE; + + // 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 + bool checkValue = true; + for (CK_ULONG i = 0; i < ulCount; i++) + { + switch (pTemplate[i].type) + { + 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; + } + } + + // Generate the secret key + DESKey* key = new DESKey(112); + SymmetricAlgorithm* des = CryptoFactory::i()->getSymmetricAlgorithm(SymAlgo::DES3); + if (des == NULL) + { + ERROR_MSG("Could not get SymmetricAlgorith"); + delete key; + return CKR_GENERAL_ERROR; + } + RNG* rng = CryptoFactory::i()->getRNG(); + if (rng == NULL) + { + ERROR_MSG("Could not get RNG"); + des->recycleKey(key); + CryptoFactory::i()->recycleSymmetricAlgorithm(des); + return CKR_GENERAL_ERROR; + } + if (!des->generateKey(*key, rng)) + { + ERROR_MSG("Could not generate DES secret key"); + des->recycleKey(key); + CryptoFactory::i()->recycleSymmetricAlgorithm(des); + return CKR_GENERAL_ERROR; + } + + CK_RV rv = CKR_OK; + + // Create the secret key object using C_CreateObject + const CK_ULONG maxAttribs = 32; + CK_OBJECT_CLASS objClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_DES2; + CK_ATTRIBUTE keyAttribs[maxAttribs] = { + { CKA_CLASS, &objClass, sizeof(objClass) }, + { CKA_TOKEN, &isOnToken, sizeof(isOnToken) }, + { CKA_PRIVATE, &isPrivate, sizeof(isPrivate) }, + { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, + }; + CK_ULONG keyAttribsCount = 4; + + // Add the additional + if (ulCount > (maxAttribs - keyAttribsCount)) + 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: + keyAttribs[keyAttribsCount++] = pTemplate[i]; + } + } + + if (rv == CKR_OK) + rv = this->CreateObject(hSession, keyAttribs, keyAttribsCount, phKey,OBJECT_OP_GENERATE); + + // 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,true); + CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_DES2_KEY_GEN; + bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism); + + // Common Secret Key Attributes + bool bAlwaysSensitive = osobject->getBooleanValue(CKA_SENSITIVE, false); + bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,bAlwaysSensitive); + bool bNeverExtractable = osobject->getBooleanValue(CKA_EXTRACTABLE, false) == false; + bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE, bNeverExtractable); + + // DES Secret Key Attributes + ByteString value; + ByteString kcv; + if (isPrivate) + { + token->encrypt(key->getKeyBits(), value); + token->encrypt(key->getKeyCheckValue(), kcv); + } + else + { + value = key->getKeyBits(); + kcv = key->getKeyCheckValue(); + } + 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 + des->recycleKey(key); + CryptoFactory::i()->recycleSymmetricAlgorithm(des); + + // Remove the key that may have been created already when the function fails. + if (rv != CKR_OK) + { + if (*phKey != CK_INVALID_HANDLE) + { + OSObject* oskey = (OSObject*)handleManager->getObject(*phKey); + handleManager->destroyObject(*phKey); + if (oskey) oskey->destroyObject(); + *phKey = CK_INVALID_HANDLE; + } + } + + return rv; +} + +// Generate a DES3 secret key +CK_RV SoftHSM::generateDES3 +(CK_SESSION_HANDLE hSession, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount, + CK_OBJECT_HANDLE_PTR phKey, + CK_BBOOL isOnToken, + CK_BBOOL isPrivate) +{ + *phKey = CK_INVALID_HANDLE; + + // 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 + bool checkValue = true; + for (CK_ULONG i = 0; i < ulCount; i++) + { + switch (pTemplate[i].type) + { + 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; + } + } + + // Generate the secret key + DESKey* key = new DESKey(168); + SymmetricAlgorithm* des = CryptoFactory::i()->getSymmetricAlgorithm(SymAlgo::DES3); + if (des == NULL) + { + ERROR_MSG("Could not get SymmetricAlgorithm"); + delete key; + return CKR_GENERAL_ERROR; + } + RNG* rng = CryptoFactory::i()->getRNG(); + if (rng == NULL) + { + ERROR_MSG("Could not get RNG"); + des->recycleKey(key); + CryptoFactory::i()->recycleSymmetricAlgorithm(des); + return CKR_GENERAL_ERROR; + } + if (!des->generateKey(*key, rng)) + { + ERROR_MSG("Could not generate DES secret key"); + des->recycleKey(key); + CryptoFactory::i()->recycleSymmetricAlgorithm(des); + return CKR_GENERAL_ERROR; + } + + CK_RV rv = CKR_OK; + + // Create the secret key object using C_CreateObject + const CK_ULONG maxAttribs = 32; + CK_OBJECT_CLASS objClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_DES3; + CK_ATTRIBUTE keyAttribs[maxAttribs] = { + { CKA_CLASS, &objClass, sizeof(objClass) }, + { CKA_TOKEN, &isOnToken, sizeof(isOnToken) }, + { CKA_PRIVATE, &isPrivate, sizeof(isPrivate) }, + { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, + }; + CK_ULONG keyAttribsCount = 4; + + // Add the additional + if (ulCount > (maxAttribs - keyAttribsCount)) + 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: + keyAttribs[keyAttribsCount++] = pTemplate[i]; + } + } + + if (rv == CKR_OK) + rv = this->CreateObject(hSession, keyAttribs, keyAttribsCount, phKey,OBJECT_OP_GENERATE); + + // 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,true); + CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_DES3_KEY_GEN; + bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism); + + // Common Secret Key Attributes + bool bAlwaysSensitive = osobject->getBooleanValue(CKA_SENSITIVE, false); + bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,bAlwaysSensitive); + bool bNeverExtractable = osobject->getBooleanValue(CKA_EXTRACTABLE, false) == false; + bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE, bNeverExtractable); + + // DES Secret Key Attributes + ByteString value; + ByteString kcv; + if (isPrivate) + { + token->encrypt(key->getKeyBits(), value); + token->encrypt(key->getKeyCheckValue(), kcv); + } + else + { + value = key->getKeyBits(); + kcv = key->getKeyCheckValue(); + } + 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 + des->recycleKey(key); + CryptoFactory::i()->recycleSymmetricAlgorithm(des); + + // Remove the key that may have been created already when the function fails. + if (rv != CKR_OK) + { + if (*phKey != CK_INVALID_HANDLE) + { + OSObject* oskey = (OSObject*)handleManager->getObject(*phKey); + handleManager->destroyObject(*phKey); + if (oskey) oskey->destroyObject(); + *phKey = CK_INVALID_HANDLE; + } + } + + return rv; +} + +// Generate an RSA key pair +CK_RV SoftHSM::generateRSA +(CK_SESSION_HANDLE hSession, + CK_ATTRIBUTE_PTR pPublicKeyTemplate, + CK_ULONG ulPublicKeyAttributeCount, + CK_ATTRIBUTE_PTR pPrivateKeyTemplate, + CK_ULONG ulPrivateKeyAttributeCount, + CK_OBJECT_HANDLE_PTR phPublicKey, + CK_OBJECT_HANDLE_PTR phPrivateKey, + CK_BBOOL isPublicKeyOnToken, + CK_BBOOL isPublicKeyPrivate, + CK_BBOOL isPrivateKeyOnToken, + CK_BBOOL isPrivateKeyPrivate +) +{ + *phPublicKey = CK_INVALID_HANDLE; + *phPrivateKey = CK_INVALID_HANDLE; + + // 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 key information: bitlen and public exponent + size_t bitLen = 0; + ByteString exponent("010001"); + for (CK_ULONG i = 0; i < ulPublicKeyAttributeCount; i++) + { + switch (pPublicKeyTemplate[i].type) + { + case CKA_MODULUS_BITS: + if (pPublicKeyTemplate[i].ulValueLen != sizeof(CK_ULONG)) + { + INFO_MSG("CKA_MODULUS_BITS does not have the size of CK_ULONG"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + bitLen = *(CK_ULONG*)pPublicKeyTemplate[i].pValue; + break; + case CKA_PUBLIC_EXPONENT: + exponent = ByteString((unsigned char*)pPublicKeyTemplate[i].pValue, pPublicKeyTemplate[i].ulValueLen); + break; + default: + break; + } + } + + // CKA_MODULUS_BITS must be specified to be able to generate a key pair. + if (bitLen == 0) { + INFO_MSG("Missing CKA_MODULUS_BITS in pPublicKeyTemplate"); + return CKR_TEMPLATE_INCOMPLETE; + } + + // Set the parameters + RSAParameters p; + p.setE(exponent); + p.setBitLength(bitLen); + + // Generate key pair + AsymmetricKeyPair* kp = NULL; + AsymmetricAlgorithm* rsa = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::RSA); + if (rsa == NULL) + return CKR_GENERAL_ERROR; + if (!rsa->generateKeyPair(&kp, &p)) + { + ERROR_MSG("Could not generate key pair"); + CryptoFactory::i()->recycleAsymmetricAlgorithm(rsa); + return CKR_GENERAL_ERROR; + } + + RSAPublicKey* pub = (RSAPublicKey*) kp->getPublicKey(); + RSAPrivateKey* priv = (RSAPrivateKey*) kp->getPrivateKey(); + + CK_RV rv = CKR_OK; + + // Create a public key using C_CreateObject + if (rv == CKR_OK) + { + const CK_ULONG maxAttribs = 32; + CK_OBJECT_CLASS publicKeyClass = CKO_PUBLIC_KEY; + CK_KEY_TYPE publicKeyType = CKK_RSA; + CK_ATTRIBUTE publicKeyAttribs[maxAttribs] = { + { CKA_CLASS, &publicKeyClass, sizeof(publicKeyClass) }, + { CKA_TOKEN, &isPublicKeyOnToken, sizeof(isPublicKeyOnToken) }, + { CKA_PRIVATE, &isPublicKeyPrivate, sizeof(isPublicKeyPrivate) }, + { CKA_KEY_TYPE, &publicKeyType, sizeof(publicKeyType) }, + }; + CK_ULONG publicKeyAttribsCount = 4; + + // Add the additional + if (ulPublicKeyAttributeCount > (maxAttribs - publicKeyAttribsCount)) + rv = CKR_TEMPLATE_INCONSISTENT; + for (CK_ULONG i=0; i < ulPublicKeyAttributeCount && rv == CKR_OK; ++i) + { + switch (pPublicKeyTemplate[i].type) + { + case CKA_CLASS: + case CKA_TOKEN: + case CKA_PRIVATE: + case CKA_KEY_TYPE: + case CKA_PUBLIC_EXPONENT: + continue; + default: + publicKeyAttribs[publicKeyAttribsCount++] = pPublicKeyTemplate[i]; + } + } + + if (rv == CKR_OK) + rv = this->CreateObject(hSession,publicKeyAttribs,publicKeyAttribsCount,phPublicKey,OBJECT_OP_GENERATE); + + // Store the attributes that are being supplied by the key generation to the object + if (rv == CKR_OK) + { + OSObject* osobject = (OSObject*)handleManager->getObject(*phPublicKey); + if (osobject == NULL_PTR || !osobject->isValid()) { + rv = CKR_FUNCTION_FAILED; + } else if (osobject->startTransaction()) { + bool bOK = true; + + // Common Key Attributes + bOK = bOK && osobject->setAttribute(CKA_LOCAL,true); + CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_RSA_PKCS_KEY_PAIR_GEN; + bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism); + + // RSA Public Key Attributes + ByteString modulus; + ByteString publicExponent; + if (isPublicKeyPrivate) + { + token->encrypt(pub->getN(), modulus); + token->encrypt(pub->getE(), publicExponent); + } + else + { + modulus = pub->getN(); + publicExponent = pub->getE(); + } + bOK = bOK && osobject->setAttribute(CKA_MODULUS, modulus); + bOK = bOK && osobject->setAttribute(CKA_PUBLIC_EXPONENT, publicExponent); + + if (bOK) + bOK = osobject->commitTransaction(); + else + osobject->abortTransaction(); + + if (!bOK) + rv = CKR_FUNCTION_FAILED; + } else + rv = CKR_FUNCTION_FAILED; + } + } + + // Create a private key using C_CreateObject + if (rv == CKR_OK) + { + const CK_ULONG maxAttribs = 32; + CK_OBJECT_CLASS privateKeyClass = CKO_PRIVATE_KEY; + CK_KEY_TYPE privateKeyType = CKK_RSA; + CK_ATTRIBUTE privateKeyAttribs[maxAttribs] = { + { CKA_CLASS, &privateKeyClass, sizeof(privateKeyClass) }, + { CKA_TOKEN, &isPrivateKeyOnToken, sizeof(isPrivateKeyOnToken) }, + { CKA_PRIVATE, &isPrivateKeyPrivate, sizeof(isPrivateKeyPrivate) }, + { CKA_KEY_TYPE, &privateKeyType, sizeof(privateKeyType) }, + }; + CK_ULONG privateKeyAttribsCount = 4; + if (ulPrivateKeyAttributeCount > (maxAttribs - privateKeyAttribsCount)) + rv = CKR_TEMPLATE_INCONSISTENT; + for (CK_ULONG i=0; i < ulPrivateKeyAttributeCount && rv == CKR_OK; ++i) + { + switch (pPrivateKeyTemplate[i].type) + { + case CKA_CLASS: + case CKA_TOKEN: + case CKA_PRIVATE: + case CKA_KEY_TYPE: + continue; + default: + privateKeyAttribs[privateKeyAttribsCount++] = pPrivateKeyTemplate[i]; + } + } + + if (rv == CKR_OK) + rv = this->CreateObject(hSession,privateKeyAttribs,privateKeyAttribsCount,phPrivateKey,OBJECT_OP_GENERATE); + + // Store the attributes that are being supplied by the key generation to the object + if (rv == CKR_OK) + { + OSObject* osobject = (OSObject*)handleManager->getObject(*phPrivateKey); + if (osobject == NULL_PTR || !osobject->isValid()) { + rv = CKR_FUNCTION_FAILED; + } else if (osobject->startTransaction()) { + bool bOK = true; + + // Common Key Attributes + bOK = bOK && osobject->setAttribute(CKA_LOCAL,true); + CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_RSA_PKCS_KEY_PAIR_GEN; + bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism); + + // Common Private Key Attributes + bool bAlwaysSensitive = osobject->getBooleanValue(CKA_SENSITIVE, false); + bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,bAlwaysSensitive); + bool bNeverExtractable = osobject->getBooleanValue(CKA_EXTRACTABLE, false) == false; + bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE, bNeverExtractable); + + // RSA Private Key Attributes + ByteString modulus; + ByteString publicExponent; + ByteString privateExponent; + ByteString prime1; + ByteString prime2; + ByteString exponent1; + ByteString exponent2; + ByteString coefficient; + if (isPrivateKeyPrivate) + { + token->encrypt(priv->getN(), modulus); + token->encrypt(priv->getE(), publicExponent); + token->encrypt(priv->getD(), privateExponent); + token->encrypt(priv->getP(), prime1); + token->encrypt(priv->getQ(), prime2); + token->encrypt(priv->getDP1(), exponent1); + token->encrypt(priv->getDQ1(), exponent2); + token->encrypt(priv->getPQ(), coefficient); + } + else + { + modulus = priv->getN(); + publicExponent = priv->getE(); + privateExponent = priv->getD(); + prime1 = priv->getP(); + prime2 = priv->getQ(); + exponent1 = priv->getDP1(); + exponent2 = priv->getDQ1(); + coefficient = priv->getPQ(); + } + bOK = bOK && osobject->setAttribute(CKA_MODULUS, modulus); + bOK = bOK && osobject->setAttribute(CKA_PUBLIC_EXPONENT, publicExponent); + bOK = bOK && osobject->setAttribute(CKA_PRIVATE_EXPONENT, privateExponent); + bOK = bOK && osobject->setAttribute(CKA_PRIME_1, prime1); + bOK = bOK && osobject->setAttribute(CKA_PRIME_2, prime2); + bOK = bOK && osobject->setAttribute(CKA_EXPONENT_1,exponent1); + bOK = bOK && osobject->setAttribute(CKA_EXPONENT_2, exponent2); + bOK = bOK && osobject->setAttribute(CKA_COEFFICIENT, coefficient); + + if (bOK) + bOK = osobject->commitTransaction(); + else + osobject->abortTransaction(); + + if (!bOK) + rv = CKR_FUNCTION_FAILED; + } else + rv = CKR_FUNCTION_FAILED; + } + } + + // Clean up + rsa->recycleKeyPair(kp); + CryptoFactory::i()->recycleAsymmetricAlgorithm(rsa); + + // 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; +} + +// Generate a DSA key pair +CK_RV SoftHSM::generateDSA +(CK_SESSION_HANDLE hSession, + CK_ATTRIBUTE_PTR pPublicKeyTemplate, + CK_ULONG ulPublicKeyAttributeCount, + CK_ATTRIBUTE_PTR pPrivateKeyTemplate, + CK_ULONG ulPrivateKeyAttributeCount, + CK_OBJECT_HANDLE_PTR phPublicKey, + CK_OBJECT_HANDLE_PTR phPrivateKey, + CK_BBOOL isPublicKeyOnToken, + CK_BBOOL isPublicKeyPrivate, + CK_BBOOL isPrivateKeyOnToken, + CK_BBOOL isPrivateKeyPrivate) +{ + *phPublicKey = CK_INVALID_HANDLE; + *phPrivateKey = CK_INVALID_HANDLE; + + // 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 key information + ByteString prime; + ByteString subprime; + ByteString generator; + for (CK_ULONG i = 0; i < ulPublicKeyAttributeCount; i++) + { + switch (pPublicKeyTemplate[i].type) + { + case CKA_PRIME: + prime = ByteString((unsigned char*)pPublicKeyTemplate[i].pValue, pPublicKeyTemplate[i].ulValueLen); + break; + case CKA_SUBPRIME: + subprime = ByteString((unsigned char*)pPublicKeyTemplate[i].pValue, pPublicKeyTemplate[i].ulValueLen); + break; + case CKA_BASE: + generator = ByteString((unsigned char*)pPublicKeyTemplate[i].pValue, pPublicKeyTemplate[i].ulValueLen); + break; + default: + break; + } + } + + // The parameters must be specified to be able to generate a key pair. + if (prime.size() == 0 || subprime.size() == 0 || generator.size() == 0) { + INFO_MSG("Missing parameter(s) in pPublicKeyTemplate"); + return CKR_TEMPLATE_INCOMPLETE; + } + + // Set the parameters + DSAParameters p; + p.setP(prime); + p.setQ(subprime); + p.setG(generator); + + // Generate key pair + AsymmetricKeyPair* kp = NULL; + AsymmetricAlgorithm* dsa = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::DSA); + if (dsa == NULL) return CKR_GENERAL_ERROR; + if (!dsa->generateKeyPair(&kp, &p)) + { + ERROR_MSG("Could not generate key pair"); + CryptoFactory::i()->recycleAsymmetricAlgorithm(dsa); + return CKR_GENERAL_ERROR; + } + + DSAPublicKey* pub = (DSAPublicKey*) kp->getPublicKey(); + DSAPrivateKey* priv = (DSAPrivateKey*) kp->getPrivateKey(); + + CK_RV rv = CKR_OK; + + // Create a public key using C_CreateObject + if (rv == CKR_OK) + { + const CK_ULONG maxAttribs = 32; + CK_OBJECT_CLASS publicKeyClass = CKO_PUBLIC_KEY; + CK_KEY_TYPE publicKeyType = CKK_DSA; + CK_ATTRIBUTE publicKeyAttribs[maxAttribs] = { + { CKA_CLASS, &publicKeyClass, sizeof(publicKeyClass) }, + { CKA_TOKEN, &isPublicKeyOnToken, sizeof(isPublicKeyOnToken) }, + { CKA_PRIVATE, &isPublicKeyPrivate, sizeof(isPublicKeyPrivate) }, + { CKA_KEY_TYPE, &publicKeyType, sizeof(publicKeyType) }, + }; + CK_ULONG publicKeyAttribsCount = 4; + + // Add the additional + if (ulPublicKeyAttributeCount > (maxAttribs - publicKeyAttribsCount)) + rv = CKR_TEMPLATE_INCONSISTENT; + for (CK_ULONG i=0; i < ulPublicKeyAttributeCount && rv == CKR_OK; ++i) + { + switch (pPublicKeyTemplate[i].type) + { + case CKA_CLASS: + case CKA_TOKEN: + case CKA_PRIVATE: + case CKA_KEY_TYPE: + continue; + default: + publicKeyAttribs[publicKeyAttribsCount++] = pPublicKeyTemplate[i]; + } + } + + if (rv == CKR_OK) + rv = this->CreateObject(hSession,publicKeyAttribs,publicKeyAttribsCount,phPublicKey,OBJECT_OP_GENERATE); + + // Store the attributes that are being supplied by the key generation to the object + if (rv == CKR_OK) + { + OSObject* osobject = (OSObject*)handleManager->getObject(*phPublicKey); + if (osobject == NULL_PTR || !osobject->isValid()) { + rv = CKR_FUNCTION_FAILED; + } else if (osobject->startTransaction()) { + bool bOK = true; + + // Common Key Attributes + bOK = bOK && osobject->setAttribute(CKA_LOCAL,true); + CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_DSA_KEY_PAIR_GEN; + bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism); + + // DSA Public Key Attributes + ByteString value; + if (isPublicKeyPrivate) + { + token->encrypt(pub->getY(), value); + } + else + { + value = pub->getY(); + } + bOK = bOK && osobject->setAttribute(CKA_VALUE, value); + + if (bOK) + bOK = osobject->commitTransaction(); + else + osobject->abortTransaction(); + + if (!bOK) + rv = CKR_FUNCTION_FAILED; + } else + rv = CKR_FUNCTION_FAILED; + } + } + + // Create a private key using C_CreateObject + if (rv == CKR_OK) + { + const CK_ULONG maxAttribs = 32; + CK_OBJECT_CLASS privateKeyClass = CKO_PRIVATE_KEY; + CK_KEY_TYPE privateKeyType = CKK_DSA; + CK_ATTRIBUTE privateKeyAttribs[maxAttribs] = { + { CKA_CLASS, &privateKeyClass, sizeof(privateKeyClass) }, + { CKA_TOKEN, &isPrivateKeyOnToken, sizeof(isPrivateKeyOnToken) }, + { CKA_PRIVATE, &isPrivateKeyPrivate, sizeof(isPrivateKeyPrivate) }, + { CKA_KEY_TYPE, &privateKeyType, sizeof(privateKeyType) }, + }; + CK_ULONG privateKeyAttribsCount = 4; + if (ulPrivateKeyAttributeCount > (maxAttribs - privateKeyAttribsCount)) + rv = CKR_TEMPLATE_INCONSISTENT; + for (CK_ULONG i=0; i < ulPrivateKeyAttributeCount && rv == CKR_OK; ++i) + { + switch (pPrivateKeyTemplate[i].type) + { + case CKA_CLASS: + case CKA_TOKEN: + case CKA_PRIVATE: + case CKA_KEY_TYPE: + continue; + default: + privateKeyAttribs[privateKeyAttribsCount++] = pPrivateKeyTemplate[i]; + } + } + + if (rv == CKR_OK) + rv = this->CreateObject(hSession,privateKeyAttribs,privateKeyAttribsCount,phPrivateKey,OBJECT_OP_GENERATE); + + // Store the attributes that are being supplied by the key generation to the object + if (rv == CKR_OK) + { + OSObject* osobject = (OSObject*)handleManager->getObject(*phPrivateKey); + if (osobject == NULL_PTR || !osobject->isValid()) { + rv = CKR_FUNCTION_FAILED; + } else if (osobject->startTransaction()) { + bool bOK = true; + + // Common Key Attributes + bOK = bOK && osobject->setAttribute(CKA_LOCAL,true); + CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_DSA_KEY_PAIR_GEN; + bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism); + + // Common Private Key Attributes + bool bAlwaysSensitive = osobject->getBooleanValue(CKA_SENSITIVE, false); + bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,bAlwaysSensitive); + bool bNeverExtractable = osobject->getBooleanValue(CKA_EXTRACTABLE, false) == false; + bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE, bNeverExtractable); + + // DSA Private Key Attributes + ByteString bPrime; + ByteString bSubprime; + ByteString bGenerator; + ByteString bValue; + if (isPrivateKeyPrivate) + { + token->encrypt(priv->getP(), bPrime); + token->encrypt(priv->getQ(), bSubprime); + token->encrypt(priv->getG(), bGenerator); + token->encrypt(priv->getX(), bValue); + } + else + { + bPrime = priv->getP(); + bSubprime = priv->getQ(); + bGenerator = priv->getG(); + bValue = priv->getX(); + } + bOK = bOK && osobject->setAttribute(CKA_PRIME, bPrime); + bOK = bOK && osobject->setAttribute(CKA_SUBPRIME, bSubprime); + bOK = bOK && osobject->setAttribute(CKA_BASE, bGenerator); + bOK = bOK && osobject->setAttribute(CKA_VALUE, bValue); + + if (bOK) + bOK = osobject->commitTransaction(); + else + osobject->abortTransaction(); + + if (!bOK) + rv = CKR_FUNCTION_FAILED; + } else + rv = CKR_FUNCTION_FAILED; + } + } + + // Clean up + dsa->recycleKeyPair(kp); + CryptoFactory::i()->recycleAsymmetricAlgorithm(dsa); + + // 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; +} + +// Generate a DSA domain parameter set +CK_RV SoftHSM::generateDSAParameters +(CK_SESSION_HANDLE hSession, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount, + CK_OBJECT_HANDLE_PTR phKey, + CK_BBOOL isOnToken, + CK_BBOOL isPrivate) +{ + *phKey = CK_INVALID_HANDLE; + + // 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 bitLen = 0; + size_t qLen = 0; + for (CK_ULONG i = 0; i < ulCount; i++) + { + switch (pTemplate[i].type) + { + case CKA_PRIME_BITS: + if (pTemplate[i].ulValueLen != sizeof(CK_ULONG)) + { + INFO_MSG("CKA_PRIME_BITS does not have the size of CK_ULONG"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + bitLen = *(CK_ULONG*)pTemplate[i].pValue; + break; + case CKA_SUBPRIME_BITS: + if (pTemplate[i].ulValueLen != sizeof(CK_ULONG)) + { + INFO_MSG("CKA_SUBPRIME_BITS does not have the size of CK_ULONG"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + qLen = *(CK_ULONG*)pTemplate[i].pValue; + break; + default: + break; + } + } + + // CKA_PRIME_BITS must be specified + if (bitLen == 0) + { + INFO_MSG("Missing CKA_PRIME_BITS in pTemplate"); + return CKR_TEMPLATE_INCOMPLETE; + } + + // No real choice for CKA_SUBPRIME_BITS + if ((qLen != 0) && + (((bitLen >= 2048) && (qLen != 256)) || + ((bitLen < 2048) && (qLen != 160)))) + INFO_MSG("CKA_SUBPRIME_BITS is ignored"); + + + // Generate domain parameters + AsymmetricParameters* p = NULL; + AsymmetricAlgorithm* dsa = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::DSA); + if (dsa == NULL) return CKR_GENERAL_ERROR; + if (!dsa->generateParameters(&p, (void *)bitLen)) + { + ERROR_MSG("Could not generate parameters"); + CryptoFactory::i()->recycleAsymmetricAlgorithm(dsa); + return CKR_GENERAL_ERROR; + } + + DSAParameters* params = (DSAParameters*) p; + + CK_RV rv = CKR_OK; + + // Create the domain parameter object using C_CreateObject + const CK_ULONG maxAttribs = 32; + CK_OBJECT_CLASS objClass = CKO_DOMAIN_PARAMETERS; + CK_KEY_TYPE keyType = CKK_DSA; + CK_ATTRIBUTE paramsAttribs[maxAttribs] = { + { CKA_CLASS, &objClass, sizeof(objClass) }, + { CKA_TOKEN, &isOnToken, sizeof(isOnToken) }, + { CKA_PRIVATE, &isPrivate, sizeof(isPrivate) }, + { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, + }; + CK_ULONG paramsAttribsCount = 4; + + // Add the additional + if (ulCount > (maxAttribs - paramsAttribsCount)) + 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: + continue; + default: + paramsAttribs[paramsAttribsCount++] = pTemplate[i]; + } + } + + if (rv == CKR_OK) + rv = this->CreateObject(hSession, paramsAttribs, paramsAttribsCount, phKey,OBJECT_OP_GENERATE); + + // 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,true); + CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_DSA_PARAMETER_GEN; + bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism); + + // DSA Domain Parameters Attributes + ByteString prime; + ByteString subprime; + ByteString generator; + if (isPrivate) + { + token->encrypt(params->getP(), prime); + token->encrypt(params->getQ(), subprime); + token->encrypt(params->getG(), generator); + } + else + { + prime = params->getP(); + subprime = params->getQ(); + generator = params->getG(); + } + bOK = bOK && osobject->setAttribute(CKA_PRIME, prime); + bOK = bOK && osobject->setAttribute(CKA_SUBPRIME, subprime); + bOK = bOK && osobject->setAttribute(CKA_BASE, generator); + + if (bOK) + bOK = osobject->commitTransaction(); + else + osobject->abortTransaction(); + + if (!bOK) + rv = CKR_FUNCTION_FAILED; + } else + rv = CKR_FUNCTION_FAILED; + } + + // Clean up + dsa->recycleParameters(p); + CryptoFactory::i()->recycleAsymmetricAlgorithm(dsa); + + // Remove parameters that may have been created already when the function fails. + if (rv != CKR_OK) + { + if (*phKey != CK_INVALID_HANDLE) + { + OSObject* osparams = (OSObject*)handleManager->getObject(*phKey); + handleManager->destroyObject(*phKey); + if (osparams) osparams->destroyObject(); + *phKey = CK_INVALID_HANDLE; + } + } + + return rv; +} + +// Generate an EC key pair +CK_RV SoftHSM::generateEC +(CK_SESSION_HANDLE hSession, + CK_ATTRIBUTE_PTR pPublicKeyTemplate, + CK_ULONG ulPublicKeyAttributeCount, + CK_ATTRIBUTE_PTR pPrivateKeyTemplate, + CK_ULONG ulPrivateKeyAttributeCount, + CK_OBJECT_HANDLE_PTR phPublicKey, + CK_OBJECT_HANDLE_PTR phPrivateKey, + CK_BBOOL isPublicKeyOnToken, + CK_BBOOL isPublicKeyPrivate, + CK_BBOOL isPrivateKeyOnToken, + CK_BBOOL isPrivateKeyPrivate) +{ + *phPublicKey = CK_INVALID_HANDLE; + *phPrivateKey = CK_INVALID_HANDLE; + + // 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 key information + ByteString params; + for (CK_ULONG i = 0; i < ulPublicKeyAttributeCount; i++) + { + switch (pPublicKeyTemplate[i].type) + { + case CKA_EC_PARAMS: + params = ByteString((unsigned char*)pPublicKeyTemplate[i].pValue, pPublicKeyTemplate[i].ulValueLen); + break; + default: + break; + } + } + + // The parameters must be specified to be able to generate a key pair. + if (params.size() == 0) { + INFO_MSG("Missing parameter(s) in pPublicKeyTemplate"); + return CKR_TEMPLATE_INCOMPLETE; + } + + // Set the parameters + ECParameters p; + p.setEC(params); + + // Generate key pair + AsymmetricKeyPair* kp = NULL; + AsymmetricAlgorithm* ec = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::ECDSA); + if (ec == NULL) return CKR_GENERAL_ERROR; + if (!ec->generateKeyPair(&kp, &p)) + { + ERROR_MSG("Could not generate key pair"); + CryptoFactory::i()->recycleAsymmetricAlgorithm(ec); + return CKR_GENERAL_ERROR; + } + + ECPublicKey* pub = (ECPublicKey*) kp->getPublicKey(); + ECPrivateKey* priv = (ECPrivateKey*) kp->getPrivateKey(); + + CK_RV rv = CKR_OK; + + // Create a public key using C_CreateObject + if (rv == CKR_OK) + { + const CK_ULONG maxAttribs = 32; + CK_OBJECT_CLASS publicKeyClass = CKO_PUBLIC_KEY; + CK_KEY_TYPE publicKeyType = CKK_EC; + CK_ATTRIBUTE publicKeyAttribs[maxAttribs] = { + { CKA_CLASS, &publicKeyClass, sizeof(publicKeyClass) }, + { CKA_TOKEN, &isPublicKeyOnToken, sizeof(isPublicKeyOnToken) }, + { CKA_PRIVATE, &isPublicKeyPrivate, sizeof(isPublicKeyPrivate) }, + { CKA_KEY_TYPE, &publicKeyType, sizeof(publicKeyType) }, + }; + CK_ULONG publicKeyAttribsCount = 4; + + // Add the additional + if (ulPublicKeyAttributeCount > (maxAttribs - publicKeyAttribsCount)) + rv = CKR_TEMPLATE_INCONSISTENT; + for (CK_ULONG i=0; i < ulPublicKeyAttributeCount && rv == CKR_OK; ++i) + { + switch (pPublicKeyTemplate[i].type) + { + case CKA_CLASS: + case CKA_TOKEN: + case CKA_PRIVATE: + case CKA_KEY_TYPE: + continue; + default: + publicKeyAttribs[publicKeyAttribsCount++] = pPublicKeyTemplate[i]; + } + } + + if (rv == CKR_OK) + rv = this->CreateObject(hSession,publicKeyAttribs,publicKeyAttribsCount,phPublicKey,OBJECT_OP_GENERATE); + + // Store the attributes that are being supplied by the key generation to the object + if (rv == CKR_OK) + { + OSObject* osobject = (OSObject*)handleManager->getObject(*phPublicKey); + if (osobject == NULL_PTR || !osobject->isValid()) { + rv = CKR_FUNCTION_FAILED; + } else if (osobject->startTransaction()) { + bool bOK = true; + + // Common Key Attributes + bOK = bOK && osobject->setAttribute(CKA_LOCAL,true); + CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_EC_KEY_PAIR_GEN; + bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism); + + // EC Public Key Attributes + ByteString point; + if (isPublicKeyPrivate) + { + token->encrypt(pub->getQ(), point); + } + else + { + point = pub->getQ(); + } + bOK = bOK && osobject->setAttribute(CKA_EC_POINT, point); + + if (bOK) + bOK = osobject->commitTransaction(); + else + osobject->abortTransaction(); + + if (!bOK) + rv = CKR_FUNCTION_FAILED; + } else + rv = CKR_FUNCTION_FAILED; + } + } + + // Create a private key using C_CreateObject + if (rv == CKR_OK) + { + const CK_ULONG maxAttribs = 32; + CK_OBJECT_CLASS privateKeyClass = CKO_PRIVATE_KEY; + CK_KEY_TYPE privateKeyType = CKK_EC; + CK_ATTRIBUTE privateKeyAttribs[maxAttribs] = { + { CKA_CLASS, &privateKeyClass, sizeof(privateKeyClass) }, + { CKA_TOKEN, &isPrivateKeyOnToken, sizeof(isPrivateKeyOnToken) }, + { CKA_PRIVATE, &isPrivateKeyPrivate, sizeof(isPrivateKeyPrivate) }, + { CKA_KEY_TYPE, &privateKeyType, sizeof(privateKeyType) }, + }; + CK_ULONG privateKeyAttribsCount = 4; + if (ulPrivateKeyAttributeCount > (maxAttribs - privateKeyAttribsCount)) + rv = CKR_TEMPLATE_INCONSISTENT; + for (CK_ULONG i=0; i < ulPrivateKeyAttributeCount && rv == CKR_OK; ++i) + { + switch (pPrivateKeyTemplate[i].type) + { + case CKA_CLASS: + case CKA_TOKEN: + case CKA_PRIVATE: + case CKA_KEY_TYPE: + continue; + default: + privateKeyAttribs[privateKeyAttribsCount++] = pPrivateKeyTemplate[i]; + } + } + + if (rv == CKR_OK) + rv = this->CreateObject(hSession,privateKeyAttribs,privateKeyAttribsCount,phPrivateKey,OBJECT_OP_GENERATE); + + // Store the attributes that are being supplied by the key generation to the object + if (rv == CKR_OK) + { + OSObject* osobject = (OSObject*)handleManager->getObject(*phPrivateKey); + if (osobject == NULL_PTR || !osobject->isValid()) { + rv = CKR_FUNCTION_FAILED; + } else if (osobject->startTransaction()) { + bool bOK = true; + + // Common Key Attributes + bOK = bOK && osobject->setAttribute(CKA_LOCAL,true); + CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_EC_KEY_PAIR_GEN; + bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism); + + // Common Private Key Attributes + bool bAlwaysSensitive = osobject->getBooleanValue(CKA_SENSITIVE, false); + bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,bAlwaysSensitive); + bool bNeverExtractable = osobject->getBooleanValue(CKA_EXTRACTABLE, false) == false; + bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE, bNeverExtractable); + + // EC Private Key Attributes + ByteString group; + ByteString value; + if (isPrivateKeyPrivate) + { + token->encrypt(priv->getEC(), group); + token->encrypt(priv->getD(), value); + } + else + { + group = priv->getEC(); + value = priv->getD(); + } + bOK = bOK && osobject->setAttribute(CKA_EC_PARAMS, group); + bOK = bOK && osobject->setAttribute(CKA_VALUE, value); + + if (bOK) + bOK = osobject->commitTransaction(); + else + osobject->abortTransaction(); + + if (!bOK) + rv = CKR_FUNCTION_FAILED; + } else + rv = CKR_FUNCTION_FAILED; + } + } + + // Clean up + ec->recycleKeyPair(kp); + CryptoFactory::i()->recycleAsymmetricAlgorithm(ec); + + // 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; +} + +// Generate a DH key pair +CK_RV SoftHSM::generateDH +(CK_SESSION_HANDLE hSession, + CK_ATTRIBUTE_PTR pPublicKeyTemplate, + CK_ULONG ulPublicKeyAttributeCount, + CK_ATTRIBUTE_PTR pPrivateKeyTemplate, + CK_ULONG ulPrivateKeyAttributeCount, + CK_OBJECT_HANDLE_PTR phPublicKey, + CK_OBJECT_HANDLE_PTR phPrivateKey, + CK_BBOOL isPublicKeyOnToken, + CK_BBOOL isPublicKeyPrivate, + CK_BBOOL isPrivateKeyOnToken, + CK_BBOOL isPrivateKeyPrivate) +{ + *phPublicKey = CK_INVALID_HANDLE; + *phPrivateKey = CK_INVALID_HANDLE; + + // 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 key information + ByteString prime; + ByteString generator; + for (CK_ULONG i = 0; i < ulPublicKeyAttributeCount; i++) + { + switch (pPublicKeyTemplate[i].type) + { + case CKA_PRIME: + prime = ByteString((unsigned char*)pPublicKeyTemplate[i].pValue, pPublicKeyTemplate[i].ulValueLen); + break; + case CKA_BASE: + generator = ByteString((unsigned char*)pPublicKeyTemplate[i].pValue, pPublicKeyTemplate[i].ulValueLen); + break; + default: + break; + } + } + + // The parameters must be specified to be able to generate a key pair. + if (prime.size() == 0 || generator.size() == 0) { + INFO_MSG("Missing parameter(s) in pPublicKeyTemplate"); + return CKR_TEMPLATE_INCOMPLETE; + } + + // Extract optional bit length + size_t bitLen = 0; + for (CK_ULONG i = 0; i < ulPrivateKeyAttributeCount; i++) + { + switch (pPrivateKeyTemplate[i].type) + { + case CKA_VALUE_BITS: + bitLen = *(CK_ULONG*)pPrivateKeyTemplate[i].pValue; + break; + default: + break; + } + } + + // Set the parameters + DHParameters p; + p.setP(prime); + p.setG(generator); + p.setXBitLength(bitLen); + + // Generate key pair + AsymmetricKeyPair* kp = NULL; + AsymmetricAlgorithm* dh = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::DH); + if (dh == NULL) return CKR_GENERAL_ERROR; + if (!dh->generateKeyPair(&kp, &p)) + { + ERROR_MSG("Could not generate key pair"); + CryptoFactory::i()->recycleAsymmetricAlgorithm(dh); + return CKR_GENERAL_ERROR; + } + + DHPublicKey* pub = (DHPublicKey*) kp->getPublicKey(); + DHPrivateKey* priv = (DHPrivateKey*) kp->getPrivateKey(); + + CK_RV rv = CKR_OK; + + // Create a public key using C_CreateObject + if (rv == CKR_OK) + { + const CK_ULONG maxAttribs = 32; + CK_OBJECT_CLASS publicKeyClass = CKO_PUBLIC_KEY; + CK_KEY_TYPE publicKeyType = CKK_DH; + CK_ATTRIBUTE publicKeyAttribs[maxAttribs] = { + { CKA_CLASS, &publicKeyClass, sizeof(publicKeyClass) }, + { CKA_TOKEN, &isPublicKeyOnToken, sizeof(isPublicKeyOnToken) }, + { CKA_PRIVATE, &isPublicKeyPrivate, sizeof(isPublicKeyPrivate) }, + { CKA_KEY_TYPE, &publicKeyType, sizeof(publicKeyType) }, + }; + CK_ULONG publicKeyAttribsCount = 4; + + // Add the additional + if (ulPublicKeyAttributeCount > (maxAttribs - publicKeyAttribsCount)) + rv = CKR_TEMPLATE_INCONSISTENT; + for (CK_ULONG i=0; i < ulPublicKeyAttributeCount && rv == CKR_OK; ++i) + { + switch (pPublicKeyTemplate[i].type) + { + case CKA_CLASS: + case CKA_TOKEN: + case CKA_PRIVATE: + case CKA_KEY_TYPE: + continue; + default: + publicKeyAttribs[publicKeyAttribsCount++] = pPublicKeyTemplate[i]; + } + } + + if (rv == CKR_OK) + rv = this->CreateObject(hSession,publicKeyAttribs,publicKeyAttribsCount,phPublicKey,OBJECT_OP_GENERATE); + + // Store the attributes that are being supplied by the key generation to the object + if (rv == CKR_OK) + { + OSObject* osobject = (OSObject*)handleManager->getObject(*phPublicKey); + if (osobject == NULL_PTR || !osobject->isValid()) { + rv = CKR_FUNCTION_FAILED; + } else if (osobject->startTransaction()) { + bool bOK = true; + + // Common Key Attributes + bOK = bOK && osobject->setAttribute(CKA_LOCAL,true); + CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_DH_PKCS_KEY_PAIR_GEN; + bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism); + + // DH Public Key Attributes + ByteString value; + if (isPublicKeyPrivate) + { + token->encrypt(pub->getY(), value); + } + else + { + value = pub->getY(); + } + bOK = bOK && osobject->setAttribute(CKA_VALUE, value); + + if (bOK) + bOK = osobject->commitTransaction(); + else + osobject->abortTransaction(); + + if (!bOK) + rv = CKR_FUNCTION_FAILED; + } else + rv = CKR_FUNCTION_FAILED; + } + } + + // Create a private key using C_CreateObject + if (rv == CKR_OK) + { + const CK_ULONG maxAttribs = 32; + CK_OBJECT_CLASS privateKeyClass = CKO_PRIVATE_KEY; + CK_KEY_TYPE privateKeyType = CKK_DH; + CK_ATTRIBUTE privateKeyAttribs[maxAttribs] = { + { CKA_CLASS, &privateKeyClass, sizeof(privateKeyClass) }, + { CKA_TOKEN, &isPrivateKeyOnToken, sizeof(isPrivateKeyOnToken) }, + { CKA_PRIVATE, &isPrivateKeyPrivate, sizeof(isPrivateKeyPrivate) }, + { CKA_KEY_TYPE, &privateKeyType, sizeof(privateKeyType) }, + }; + CK_ULONG privateKeyAttribsCount = 4; + if (ulPrivateKeyAttributeCount > (maxAttribs - privateKeyAttribsCount)) + rv = CKR_TEMPLATE_INCONSISTENT; + for (CK_ULONG i=0; i < ulPrivateKeyAttributeCount && rv == CKR_OK; ++i) + { + switch (pPrivateKeyTemplate[i].type) + { + case CKA_CLASS: + case CKA_TOKEN: + case CKA_PRIVATE: + case CKA_KEY_TYPE: + continue; + default: + privateKeyAttribs[privateKeyAttribsCount++] = pPrivateKeyTemplate[i]; + } + } + + if (rv == CKR_OK) + rv = this->CreateObject(hSession,privateKeyAttribs,privateKeyAttribsCount,phPrivateKey,OBJECT_OP_GENERATE); + + // Store the attributes that are being supplied by the key generation to the object + if (rv == CKR_OK) + { + OSObject* osobject = (OSObject*)handleManager->getObject(*phPrivateKey); + if (osobject == NULL_PTR || !osobject->isValid()) { + rv = CKR_FUNCTION_FAILED; + } else if (osobject->startTransaction()) { + bool bOK = true; + + // Common Key Attributes + bOK = bOK && osobject->setAttribute(CKA_LOCAL,true); + CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_DH_PKCS_KEY_PAIR_GEN; + bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism); + + // Common Private Key Attributes + bool bAlwaysSensitive = osobject->getBooleanValue(CKA_SENSITIVE, false); + bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,bAlwaysSensitive); + bool bNeverExtractable = osobject->getBooleanValue(CKA_EXTRACTABLE, false) == false; + bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE, bNeverExtractable); + + // DH Private Key Attributes + ByteString bPrime; + ByteString bGenerator; + ByteString bValue; + if (isPrivateKeyPrivate) + { + token->encrypt(priv->getP(), bPrime); + token->encrypt(priv->getG(), bGenerator); + token->encrypt(priv->getX(), bValue); + } + else + { + bPrime = priv->getP(); + bGenerator = priv->getG(); + bValue = priv->getX(); + } + bOK = bOK && osobject->setAttribute(CKA_PRIME, bPrime); + bOK = bOK && osobject->setAttribute(CKA_BASE, bGenerator); + bOK = bOK && osobject->setAttribute(CKA_VALUE, bValue); + + if (bitLen == 0) + { + bOK = bOK && osobject->setAttribute(CKA_VALUE_BITS, (unsigned long)priv->getX().bits()); + } + + if (bOK) + bOK = osobject->commitTransaction(); + else + osobject->abortTransaction(); + + if (!bOK) + rv = CKR_FUNCTION_FAILED; + } else + rv = CKR_FUNCTION_FAILED; + } + } + + // Clean up + dh->recycleKeyPair(kp); + CryptoFactory::i()->recycleAsymmetricAlgorithm(dh); + + // 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; +} + +// Generate a DH domain parameter set +CK_RV SoftHSM::generateDHParameters +(CK_SESSION_HANDLE hSession, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount, + CK_OBJECT_HANDLE_PTR phKey, + CK_BBOOL isOnToken, + CK_BBOOL isPrivate) +{ + *phKey = CK_INVALID_HANDLE; + + // 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 bitLen = 0; + for (CK_ULONG i = 0; i < ulCount; i++) + { + switch (pTemplate[i].type) + { + case CKA_PRIME_BITS: + if (pTemplate[i].ulValueLen != sizeof(CK_ULONG)) + { + INFO_MSG("CKA_PRIME_BITS does not have the size of CK_ULONG"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + bitLen = *(CK_ULONG*)pTemplate[i].pValue; + break; + default: + break; + } + } + + // CKA_PRIME_BITS must be specified + if (bitLen == 0) + { + INFO_MSG("Missing CKA_PRIME_BITS in pTemplate"); + return CKR_TEMPLATE_INCOMPLETE; + } + + // Generate domain parameters + AsymmetricParameters* p = NULL; + AsymmetricAlgorithm* dh = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::DH); + if (dh == NULL) return CKR_GENERAL_ERROR; + if (!dh->generateParameters(&p, (void *)bitLen)) + { + ERROR_MSG("Could not generate parameters"); + CryptoFactory::i()->recycleAsymmetricAlgorithm(dh); + return CKR_GENERAL_ERROR; + } + + DHParameters* params = (DHParameters*) p; + + CK_RV rv = CKR_OK; + + // Create the domain parameter object using C_CreateObject + const CK_ULONG maxAttribs = 32; + CK_OBJECT_CLASS objClass = CKO_DOMAIN_PARAMETERS; + CK_KEY_TYPE keyType = CKK_DH; + CK_ATTRIBUTE paramsAttribs[maxAttribs] = { + { CKA_CLASS, &objClass, sizeof(objClass) }, + { CKA_TOKEN, &isOnToken, sizeof(isOnToken) }, + { CKA_PRIVATE, &isPrivate, sizeof(isPrivate) }, + { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, + }; + CK_ULONG paramsAttribsCount = 4; + + // Add the additional + if (ulCount > (maxAttribs - paramsAttribsCount)) + 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: + continue; + default: + paramsAttribs[paramsAttribsCount++] = pTemplate[i]; + } + } + + if (rv == CKR_OK) + rv = this->CreateObject(hSession, paramsAttribs, paramsAttribsCount, phKey,OBJECT_OP_GENERATE); + + // 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,true); + CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_DH_PKCS_PARAMETER_GEN; + bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism); + + // DH Domain Parameters Attributes + ByteString prime; + ByteString generator; + if (isPrivate) + { + token->encrypt(params->getP(), prime); + token->encrypt(params->getG(), generator); + } + else + { + prime = params->getP(); + generator = params->getG(); + } + bOK = bOK && osobject->setAttribute(CKA_PRIME, prime); + bOK = bOK && osobject->setAttribute(CKA_BASE, generator); + + if (bOK) + bOK = osobject->commitTransaction(); + else + osobject->abortTransaction(); + + if (!bOK) + rv = CKR_FUNCTION_FAILED; + } else + rv = CKR_FUNCTION_FAILED; + } + + // Clean up + dh->recycleParameters(p); + CryptoFactory::i()->recycleAsymmetricAlgorithm(dh); + + // Remove parameters that may have been created already when the function fails. + if (rv != CKR_OK) + { + if (*phKey != CK_INVALID_HANDLE) + { + OSObject* osparams = (OSObject*)handleManager->getObject(*phKey); + handleManager->destroyObject(*phKey); + if (osparams) osparams->destroyObject(); + *phKey = CK_INVALID_HANDLE; + } + } + + return rv; +} + +// Generate a GOST key pair +CK_RV SoftHSM::generateGOST +(CK_SESSION_HANDLE hSession, + CK_ATTRIBUTE_PTR pPublicKeyTemplate, + CK_ULONG ulPublicKeyAttributeCount, + CK_ATTRIBUTE_PTR pPrivateKeyTemplate, + CK_ULONG ulPrivateKeyAttributeCount, + CK_OBJECT_HANDLE_PTR phPublicKey, + CK_OBJECT_HANDLE_PTR phPrivateKey, + CK_BBOOL isPublicKeyOnToken, + CK_BBOOL isPublicKeyPrivate, + CK_BBOOL isPrivateKeyOnToken, + CK_BBOOL isPrivateKeyPrivate) +{ + *phPublicKey = CK_INVALID_HANDLE; + *phPrivateKey = CK_INVALID_HANDLE; + + // 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 key information + ByteString param_3410; + ByteString param_3411; + ByteString param_28147; + for (CK_ULONG i = 0; i < ulPublicKeyAttributeCount; i++) + { + switch (pPublicKeyTemplate[i].type) + { + case CKA_GOSTR3410_PARAMS: + param_3410 = ByteString((unsigned char*)pPublicKeyTemplate[i].pValue, pPublicKeyTemplate[i].ulValueLen); + break; + case CKA_GOSTR3411_PARAMS: + param_3411 = ByteString((unsigned char*)pPublicKeyTemplate[i].pValue, pPublicKeyTemplate[i].ulValueLen); + break; + case CKA_GOST28147_PARAMS: + param_28147 = ByteString((unsigned char*)pPublicKeyTemplate[i].pValue, pPublicKeyTemplate[i].ulValueLen); + break; + default: + break; + } + } + + // The parameters must be specified to be able to generate a key pair. + if (param_3410.size() == 0 || param_3411.size() == 0) { + INFO_MSG("Missing parameter(s) in pPublicKeyTemplate"); + return CKR_TEMPLATE_INCOMPLETE; + } + + // Set the parameters + ECParameters p; + p.setEC(param_3410); + + // Generate key pair + AsymmetricKeyPair* kp = NULL; + AsymmetricAlgorithm* gost = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::GOST); + if (gost == NULL) return CKR_GENERAL_ERROR; + if (!gost->generateKeyPair(&kp, &p)) + { + ERROR_MSG("Could not generate key pair"); + CryptoFactory::i()->recycleAsymmetricAlgorithm(gost); + return CKR_GENERAL_ERROR; + } + + GOSTPublicKey* pub = (GOSTPublicKey*) kp->getPublicKey(); + GOSTPrivateKey* priv = (GOSTPrivateKey*) kp->getPrivateKey(); + + CK_RV rv = CKR_OK; + + // Create a public key using C_CreateObject + if (rv == CKR_OK) + { + const CK_ULONG maxAttribs = 32; + CK_OBJECT_CLASS publicKeyClass = CKO_PUBLIC_KEY; + CK_KEY_TYPE publicKeyType = CKK_GOSTR3410; + CK_ATTRIBUTE publicKeyAttribs[maxAttribs] = { + { CKA_CLASS, &publicKeyClass, sizeof(publicKeyClass) }, + { CKA_TOKEN, &isPublicKeyOnToken, sizeof(isPublicKeyOnToken) }, + { CKA_PRIVATE, &isPublicKeyPrivate, sizeof(isPublicKeyPrivate) }, + { CKA_KEY_TYPE, &publicKeyType, sizeof(publicKeyType) }, + }; + CK_ULONG publicKeyAttribsCount = 4; + + // Add the additional + if (ulPublicKeyAttributeCount > (maxAttribs - publicKeyAttribsCount)) + rv = CKR_TEMPLATE_INCONSISTENT; + for (CK_ULONG i=0; i < ulPublicKeyAttributeCount && rv == CKR_OK; ++i) + { + switch (pPublicKeyTemplate[i].type) + { + case CKA_CLASS: + case CKA_TOKEN: + case CKA_PRIVATE: + case CKA_KEY_TYPE: + continue; + default: + publicKeyAttribs[publicKeyAttribsCount++] = pPublicKeyTemplate[i]; + } + } + + if (rv == CKR_OK) + rv = this->CreateObject(hSession,publicKeyAttribs,publicKeyAttribsCount,phPublicKey,OBJECT_OP_GENERATE); + + // Store the attributes that are being supplied by the key generation to the object + if (rv == CKR_OK) + { + OSObject* osobject = (OSObject*)handleManager->getObject(*phPublicKey); + if (osobject == NULL_PTR || !osobject->isValid()) { + rv = CKR_FUNCTION_FAILED; + } else if (osobject->startTransaction()) { + bool bOK = true; + + // Common Key Attributes + bOK = bOK && osobject->setAttribute(CKA_LOCAL,true); + CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_EC_KEY_PAIR_GEN; + bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism); + + // EC Public Key Attributes + ByteString point; + if (isPublicKeyPrivate) + { + token->encrypt(pub->getQ(), point); + } + else + { + point = pub->getQ(); + } + bOK = bOK && osobject->setAttribute(CKA_VALUE, point); + + if (bOK) + bOK = osobject->commitTransaction(); + else + osobject->abortTransaction(); + + if (!bOK) + rv = CKR_FUNCTION_FAILED; + } else + rv = CKR_FUNCTION_FAILED; + } + } + + // Create a private key using C_CreateObject + if (rv == CKR_OK) + { + const CK_ULONG maxAttribs = 32; + CK_OBJECT_CLASS privateKeyClass = CKO_PRIVATE_KEY; + CK_KEY_TYPE privateKeyType = CKK_GOSTR3410; + CK_ATTRIBUTE privateKeyAttribs[maxAttribs] = { + { CKA_CLASS, &privateKeyClass, sizeof(privateKeyClass) }, + { CKA_TOKEN, &isPrivateKeyOnToken, sizeof(isPrivateKeyOnToken) }, + { CKA_PRIVATE, &isPrivateKeyPrivate, sizeof(isPrivateKeyPrivate) }, + { CKA_KEY_TYPE, &privateKeyType, sizeof(privateKeyType) }, + }; + CK_ULONG privateKeyAttribsCount = 4; + if (ulPrivateKeyAttributeCount > (maxAttribs - privateKeyAttribsCount)) + rv = CKR_TEMPLATE_INCONSISTENT; + for (CK_ULONG i=0; i < ulPrivateKeyAttributeCount && rv == CKR_OK; ++i) + { + switch (pPrivateKeyTemplate[i].type) + { + case CKA_CLASS: + case CKA_TOKEN: + case CKA_PRIVATE: + case CKA_KEY_TYPE: + continue; + default: + privateKeyAttribs[privateKeyAttribsCount++] = pPrivateKeyTemplate[i]; + } + } + + if (rv == CKR_OK) + rv = this->CreateObject(hSession,privateKeyAttribs,privateKeyAttribsCount,phPrivateKey,OBJECT_OP_GENERATE); + + // Store the attributes that are being supplied by the key generation to the object + if (rv == CKR_OK) + { + OSObject* osobject = (OSObject*)handleManager->getObject(*phPrivateKey); + if (osobject == NULL_PTR || !osobject->isValid()) { + rv = CKR_FUNCTION_FAILED; + } else if (osobject->startTransaction()) { + bool bOK = true; + + // Common Key Attributes + bOK = bOK && osobject->setAttribute(CKA_LOCAL,true); + CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_EC_KEY_PAIR_GEN; + bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism); + + // Common Private Key Attributes + bool bAlwaysSensitive = osobject->getBooleanValue(CKA_SENSITIVE, false); + bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,bAlwaysSensitive); + bool bNeverExtractable = osobject->getBooleanValue(CKA_EXTRACTABLE, false) == false; + bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE, bNeverExtractable); + + // GOST Private Key Attributes + ByteString value; + ByteString param_a; + ByteString param_b; + ByteString param_c; + if (isPrivateKeyPrivate) + { + token->encrypt(priv->getD(), value); + token->encrypt(priv->getEC(), param_a); + token->encrypt(param_3411, param_b); + token->encrypt(param_28147, param_c); + } + else + { + value = priv->getD(); + param_a = priv->getEC(); + param_b = param_3411; + param_c = param_28147; + } + bOK = bOK && osobject->setAttribute(CKA_VALUE, value); + bOK = bOK && osobject->setAttribute(CKA_GOSTR3410_PARAMS, param_a); + bOK = bOK && osobject->setAttribute(CKA_GOSTR3411_PARAMS, param_b); + bOK = bOK && osobject->setAttribute(CKA_GOST28147_PARAMS, param_c); + + if (bOK) + bOK = osobject->commitTransaction(); + else + osobject->abortTransaction(); + + if (!bOK) + rv = CKR_FUNCTION_FAILED; + } else + rv = CKR_FUNCTION_FAILED; + } + } + + // Clean up + gost->recycleKeyPair(kp); + CryptoFactory::i()->recycleAsymmetricAlgorithm(gost); + + // 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. + if (rv != CKR_OK) + { + if (*phKey != CK_INVALID_HANDLE) + { + OSObject* ossecret = (OSObject*)handleManager->getObject(*phKey); + handleManager->destroyObject(*phKey); + if (ossecret) ossecret->destroyObject(); + *phKey = CK_INVALID_HANDLE; + } + } + + return rv; +} + +// Derive an ECDH secret +CK_RV SoftHSM::deriveECDH +(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) +{ +#ifdef WITH_ECC + *phKey = CK_INVALID_HANDLE; + + if ((pMechanism->pParameter == NULL_PTR) || + (pMechanism->ulParameterLen != sizeof(CK_ECDH1_DERIVE_PARAMS))) + { + DEBUG_MSG("pParameter must be of type CK_ECDH1_DERIVE_PARAMS"); + return CKR_MECHANISM_PARAM_INVALID; + } + if (CK_ECDH1_DERIVE_PARAMS_PTR(pMechanism->pParameter)->kdf != CKD_NULL) + { + DEBUG_MSG("kdf must be CKD_NULL"); + return CKR_MECHANISM_PARAM_INVALID; + } + if ((CK_ECDH1_DERIVE_PARAMS_PTR(pMechanism->pParameter)->ulSharedDataLen != 0) || + (CK_ECDH1_DERIVE_PARAMS_PTR(pMechanism->pParameter)->pSharedData != NULL_PTR)) + { + DEBUG_MSG("there must be no shared data"); + return CKR_MECHANISM_PARAM_INVALID; + } + if ((CK_ECDH1_DERIVE_PARAMS_PTR(pMechanism->pParameter)->ulPublicDataLen == 0) || + (CK_ECDH1_DERIVE_PARAMS_PTR(pMechanism->pParameter)->pPublicData == NULL_PTR)) + { + DEBUG_MSG("there must be a public data"); + 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 + // byteLen == 0 impiles return max size the ECC can derive + switch (keyType) + { + case CKK_GENERIC_SECRET: + break; +#ifndef WITH_FIPS + case CKK_DES: + if (byteLen != 0 && byteLen != 8) + { + INFO_MSG("CKA_VALUE_LEN must be 0 or 8"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + byteLen = 8; + break; +#endif + case CKK_DES2: + if (byteLen != 0 && byteLen != 16) + { + INFO_MSG("CKA_VALUE_LEN must be 0 or 16"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + byteLen = 16; + break; + case CKK_DES3: + if (byteLen != 0 && byteLen != 24) + { + INFO_MSG("CKA_VALUE_LEN must be 0 or 24"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + byteLen = 24; + break; + case CKK_AES: + if (byteLen != 0 && byteLen != 16 && byteLen != 24 && byteLen != 32) + { + INFO_MSG("CKA_VALUE_LEN must be 0, 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 ECDH algorithm handler + AsymmetricAlgorithm* ecdh = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::ECDH); + if (ecdh == NULL) + return CKR_MECHANISM_INVALID; + + // Get the keys + PrivateKey* privateKey = ecdh->newPrivateKey(); + if (privateKey == NULL) + { + CryptoFactory::i()->recycleAsymmetricAlgorithm(ecdh); + return CKR_HOST_MEMORY; + } + if (getECPrivateKey((ECPrivateKey*)privateKey, token, baseKey) != CKR_OK) + { + ecdh->recyclePrivateKey(privateKey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(ecdh); + return CKR_GENERAL_ERROR; + } + + ByteString publicData; + publicData.resize(CK_ECDH1_DERIVE_PARAMS_PTR(pMechanism->pParameter)->ulPublicDataLen); + memcpy(&publicData[0], + CK_ECDH1_DERIVE_PARAMS_PTR(pMechanism->pParameter)->pPublicData, + CK_ECDH1_DERIVE_PARAMS_PTR(pMechanism->pParameter)->ulPublicDataLen); + PublicKey* publicKey = ecdh->newPublicKey(); + if (publicKey == NULL) + { + ecdh->recyclePrivateKey(privateKey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(ecdh); + return CKR_HOST_MEMORY; + } + if (getECDHPublicKey((ECPublicKey*)publicKey, (ECPrivateKey*)privateKey, publicData) != CKR_OK) + { + ecdh->recyclePrivateKey(privateKey); + ecdh->recyclePublicKey(publicKey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(ecdh); + return CKR_GENERAL_ERROR; + } + + // Derive the secret + SymmetricKey* secret = NULL; + CK_RV rv = CKR_OK; + if (!ecdh->deriveKey(&secret, publicKey, privateKey)) + rv = CKR_GENERAL_ERROR; + ecdh->recyclePrivateKey(privateKey); + ecdh->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; + + // For generic and AES keys: + // default to return max size available. + if (byteLen == 0) + { + switch (keyType) + { + case CKK_GENERIC_SECRET: + byteLen = secretValue.size(); + break; + case CKK_AES: + if (secretValue.size() >= 32) + byteLen = 32; + else if (secretValue.size() >= 24) + byteLen = 24; + else + byteLen = 16; + } + } + + 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 + ecdh->recycleSymmetricKey(secret); + CryptoFactory::i()->recycleAsymmetricAlgorithm(ecdh); + + // Remove secret that may have been created already when the function fails. + if (rv != CKR_OK) + { + if (*phKey != CK_INVALID_HANDLE) + { + OSObject* ossecret = (OSObject*)handleManager->getObject(*phKey); + handleManager->destroyObject(*phKey); + if (ossecret) ossecret->destroyObject(); + *phKey = CK_INVALID_HANDLE; + } + } + + return rv; +#else + return CKR_MECHANISM_INVALID; +#endif +} + +// Derive an symmetric secret +CK_RV SoftHSM::deriveSymmetric +(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) + { + DEBUG_MSG("pParameter must be supplied"); + return CKR_MECHANISM_PARAM_INVALID; + } + + ByteString data; + + if ((pMechanism->mechanism == CKM_DES_ECB_ENCRYPT_DATA || + pMechanism->mechanism == CKM_DES3_ECB_ENCRYPT_DATA) && + pMechanism->ulParameterLen == sizeof(CK_KEY_DERIVATION_STRING_DATA)) + { + CK_BYTE_PTR pData = CK_KEY_DERIVATION_STRING_DATA_PTR(pMechanism->pParameter)->pData; + CK_ULONG ulLen = CK_KEY_DERIVATION_STRING_DATA_PTR(pMechanism->pParameter)->ulLen; + if (ulLen == 0 || pData == NULL_PTR) + { + DEBUG_MSG("There must be data in the parameter"); + return CKR_MECHANISM_PARAM_INVALID; + } + if (ulLen % 8 != 0) + { + DEBUG_MSG("The data must be a multiple of 8 bytes long"); + return CKR_MECHANISM_PARAM_INVALID; + } + data.resize(ulLen); + memcpy(&data[0], + pData, + ulLen); + } + else if ((pMechanism->mechanism == CKM_DES_CBC_ENCRYPT_DATA || + pMechanism->mechanism == CKM_DES3_CBC_ENCRYPT_DATA) && + pMechanism->ulParameterLen == sizeof(CK_DES_CBC_ENCRYPT_DATA_PARAMS)) + { + CK_BYTE_PTR pData = CK_DES_CBC_ENCRYPT_DATA_PARAMS_PTR(pMechanism->pParameter)->pData; + CK_ULONG length = CK_DES_CBC_ENCRYPT_DATA_PARAMS_PTR(pMechanism->pParameter)->length; + if (length == 0 || pData == NULL_PTR) + { + DEBUG_MSG("There must be data in the parameter"); + return CKR_MECHANISM_PARAM_INVALID; + } + if (length % 8 != 0) + { + DEBUG_MSG("The data must be a multiple of 8 bytes long"); + return CKR_MECHANISM_PARAM_INVALID; + } + data.resize(length); + memcpy(&data[0], + pData, + length); + } + else if (pMechanism->mechanism == CKM_AES_ECB_ENCRYPT_DATA && + pMechanism->ulParameterLen == sizeof(CK_KEY_DERIVATION_STRING_DATA)) + { + CK_BYTE_PTR pData = CK_KEY_DERIVATION_STRING_DATA_PTR(pMechanism->pParameter)->pData; + CK_ULONG ulLen = CK_KEY_DERIVATION_STRING_DATA_PTR(pMechanism->pParameter)->ulLen; + if (ulLen == 0 || pData == NULL_PTR) + { + DEBUG_MSG("There must be data in the parameter"); + return CKR_MECHANISM_PARAM_INVALID; + } + if (ulLen % 16 != 0) + { + DEBUG_MSG("The data must be a multiple of 16 bytes long"); + return CKR_MECHANISM_PARAM_INVALID; + } + data.resize(ulLen); + memcpy(&data[0], + pData, + ulLen); + } + else if ((pMechanism->mechanism == CKM_AES_CBC_ENCRYPT_DATA) && + pMechanism->ulParameterLen == sizeof(CK_AES_CBC_ENCRYPT_DATA_PARAMS)) + { + CK_BYTE_PTR pData = CK_AES_CBC_ENCRYPT_DATA_PARAMS_PTR(pMechanism->pParameter)->pData; + CK_ULONG length = CK_AES_CBC_ENCRYPT_DATA_PARAMS_PTR(pMechanism->pParameter)->length; + if (length == 0 || pData == NULL_PTR) + { + DEBUG_MSG("There must be data in the parameter"); + return CKR_MECHANISM_PARAM_INVALID; + } + if (length % 16 != 0) + { + DEBUG_MSG("The data must be a multiple of 16 bytes long"); + return CKR_MECHANISM_PARAM_INVALID; + } + data.resize(length); + memcpy(&data[0], + pData, + length); + } + else + { + DEBUG_MSG("pParameter is invalid"); + 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 symmetric algorithm matching the mechanism + SymAlgo::Type algo = SymAlgo::Unknown; + SymMode::Type mode = SymMode::Unknown; + bool padding = false; + ByteString iv; + size_t bb = 8; + switch(pMechanism->mechanism) { +#ifndef WITH_FIPS + case CKM_DES_ECB_ENCRYPT_DATA: + algo = SymAlgo::DES; + mode = SymMode::ECB; + bb = 7; + break; + case CKM_DES_CBC_ENCRYPT_DATA: + algo = SymAlgo::DES; + mode = SymMode::CBC; + bb = 7; + iv.resize(8); + memcpy(&iv[0], + &(CK_DES_CBC_ENCRYPT_DATA_PARAMS_PTR(pMechanism->pParameter)->iv[0]), + 8); + break; +#endif + case CKM_DES3_ECB_ENCRYPT_DATA: + algo = SymAlgo::DES3; + mode = SymMode::ECB; + bb = 7; + break; + case CKM_DES3_CBC_ENCRYPT_DATA: + algo = SymAlgo::DES3; + mode = SymMode::CBC; + bb = 7; + iv.resize(8); + memcpy(&iv[0], + &(CK_DES_CBC_ENCRYPT_DATA_PARAMS_PTR(pMechanism->pParameter)->iv[0]), + 8); + break; + case CKM_AES_ECB_ENCRYPT_DATA: + algo = SymAlgo::AES; + mode = SymMode::ECB; + break; + case CKM_AES_CBC_ENCRYPT_DATA: + algo = SymAlgo::AES; + mode = SymMode::CBC; + iv.resize(16); + memcpy(&iv[0], + &(CK_AES_CBC_ENCRYPT_DATA_PARAMS_PTR(pMechanism->pParameter)->iv[0]), + 16); + break; + default: + return CKR_MECHANISM_INVALID; + } + + // Check the key handle + OSObject *baseKey = (OSObject *)handleManager->getObject(hBaseKey); + if (baseKey == NULL_PTR || !baseKey->isValid()) return CKR_OBJECT_HANDLE_INVALID; + + SymmetricAlgorithm* cipher = CryptoFactory::i()->getSymmetricAlgorithm(algo); + if (cipher == NULL) return CKR_MECHANISM_INVALID; + + SymmetricKey* secretkey = new SymmetricKey(); + + if (getSymmetricKey(secretkey, token, baseKey) != CKR_OK) + { + cipher->recycleKey(secretkey); + CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); + return CKR_GENERAL_ERROR; + } + + // adjust key bit length + secretkey->setBitLen(secretkey->getKeyBits().size() * bb); + + // Initialize encryption + if (!cipher->encryptInit(secretkey, mode, iv, padding)) + { + cipher->recycleKey(secretkey); + CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); + return CKR_MECHANISM_INVALID; + } + + // Get the data + ByteString secretValue; + + // Encrypt the data + if (!cipher->encryptUpdate(data, secretValue)) + { + cipher->recycleKey(secretkey); + CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); + return CKR_GENERAL_ERROR; + } + + // Finalize encryption + ByteString encryptedFinal; + if (!cipher->encryptFinal(encryptedFinal)) + { + cipher->recycleKey(secretkey); + CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); + return CKR_GENERAL_ERROR; + } + cipher->recycleKey(secretkey); + CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); + secretValue += encryptedFinal; + + // 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 + CK_RV rv = CKR_OK; + 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); + } + + 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 trailing end + if (byteLen < secretValue.size()) + secretValue.resize(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 + SymmetricKey* secret = new SymmetricKey(); + secret->setKeyBits(secretValue); + 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; + } + delete secret; + + 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; + } + + // Remove secret that may have been created already when the function fails. + if (rv != CKR_OK) + { + if (*phKey != CK_INVALID_HANDLE) + { + OSObject* ossecret = (OSObject*)handleManager->getObject(*phKey); + handleManager->destroyObject(*phKey); + if (ossecret) ossecret->destroyObject(); + *phKey = CK_INVALID_HANDLE; + } + } + + return rv; +} + +CK_RV SoftHSM::CreateObject(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject, int op) +{ + if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED; + + if (pTemplate == NULL_PTR) return CKR_ARGUMENTS_BAD; + if (phObject == NULL_PTR) return CKR_ARGUMENTS_BAD; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + // Get the slot + Slot* slot = session->getSlot(); + if (slot == NULL_PTR) return CKR_GENERAL_ERROR; + + // Get the token + Token* token = session->getToken(); + if (token == NULL_PTR) return CKR_GENERAL_ERROR; + + // Extract information from the template that is needed to create the object. + CK_OBJECT_CLASS objClass = CKO_DATA; + CK_KEY_TYPE keyType = CKK_RSA; + CK_CERTIFICATE_TYPE certType = CKC_X_509; + CK_BBOOL isOnToken = CK_FALSE; + CK_BBOOL isPrivate = CK_TRUE; + bool isImplicit = false; + CK_RV rv = extractObjectInformation(pTemplate,ulCount,objClass,keyType,certType, isOnToken, isPrivate, isImplicit); + if (rv != CKR_OK) + { + ERROR_MSG("Mandatory attribute not present in template"); + return rv; + } + + // Check user credentials + rv = haveWrite(session->getState(), isOnToken, isPrivate); + if (rv != CKR_OK) + { + if (rv == CKR_USER_NOT_LOGGED_IN) + INFO_MSG("User is not authorized"); + if (rv == CKR_SESSION_READ_ONLY) + INFO_MSG("Session is read-only"); + + return rv; + } + + // Change order of attributes + const CK_ULONG maxAttribs = 32; + CK_ATTRIBUTE attribs[maxAttribs]; + CK_ATTRIBUTE saveAttribs[maxAttribs]; + CK_ULONG attribsCount = 0; + CK_ULONG saveAttribsCount = 0; + if (ulCount > maxAttribs) + { + return CKR_TEMPLATE_INCONSISTENT; + } + for (CK_ULONG i=0; i < ulCount; i++) + { + switch (pTemplate[i].type) + { + case CKA_CHECK_VALUE: + saveAttribs[saveAttribsCount++] = pTemplate[i]; + break; + default: + attribs[attribsCount++] = pTemplate[i]; + } + } + for (CK_ULONG i=0; i < saveAttribsCount; i++) + { + attribs[attribsCount++] = saveAttribs[i]; + } + + P11Object* p11object = NULL; + rv = newP11Object(objClass,keyType,certType,&p11object); + if (rv != CKR_OK) + return rv; + + // Create the object in session or on the token + OSObject *object = NULL_PTR; + if (isOnToken) + { + object = (OSObject*) token->createObject(); + } + else + { + object = sessionObjectStore->createObject(slot->getSlotID(), hSession, isPrivate != CK_FALSE); + } + + if (object == NULL || !p11object->init(object)) + { + delete p11object; + return CKR_GENERAL_ERROR; + } + + rv = p11object->saveTemplate(token, isPrivate != CK_FALSE, attribs,attribsCount,op); + delete p11object; + if (rv != CKR_OK) + return rv; + + if (op == OBJECT_OP_CREATE) + { + if (objClass == CKO_PUBLIC_KEY && + (!object->startTransaction() || + !object->setAttribute(CKA_LOCAL, false) || + !object->commitTransaction())) + { + return CKR_GENERAL_ERROR; + } + + if ((objClass == CKO_SECRET_KEY || objClass == CKO_PRIVATE_KEY) && + (!object->startTransaction() || + !object->setAttribute(CKA_LOCAL, false) || + !object->setAttribute(CKA_ALWAYS_SENSITIVE, false) || + !object->setAttribute(CKA_NEVER_EXTRACTABLE, false) || + !object->commitTransaction())) + { + return CKR_GENERAL_ERROR; + } + } + + if (isOnToken) + { + *phObject = handleManager->addTokenObject(slot->getSlotID(), isPrivate != CK_FALSE, object); + } else { + *phObject = handleManager->addSessionObject(slot->getSlotID(), hSession, isPrivate != CK_FALSE, object); + } + + return CKR_OK; +} + +CK_RV SoftHSM::getRSAPrivateKey(RSAPrivateKey* privateKey, Token* token, OSObject* key) +{ + if (privateKey == NULL) return CKR_ARGUMENTS_BAD; + if (token == NULL) return CKR_ARGUMENTS_BAD; + if (key == NULL) return CKR_ARGUMENTS_BAD; + + // Get the CKA_PRIVATE attribute, when the attribute is not present use default false + bool isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, false); + + // RSA Private Key Attributes + ByteString modulus; + ByteString publicExponent; + ByteString privateExponent; + ByteString prime1; + ByteString prime2; + ByteString exponent1; + ByteString exponent2; + ByteString coefficient; + if (isKeyPrivate) + { + bool bOK = true; + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_MODULUS), modulus); + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_PUBLIC_EXPONENT), publicExponent); + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_PRIVATE_EXPONENT), privateExponent); + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_PRIME_1), prime1); + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_PRIME_2), prime2); + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_EXPONENT_1), exponent1); + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_EXPONENT_2), exponent2); + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_COEFFICIENT), coefficient); + if (!bOK) + return CKR_GENERAL_ERROR; + } + else + { + modulus = key->getByteStringValue(CKA_MODULUS); + publicExponent = key->getByteStringValue(CKA_PUBLIC_EXPONENT); + privateExponent = key->getByteStringValue(CKA_PRIVATE_EXPONENT); + prime1 = key->getByteStringValue(CKA_PRIME_1); + prime2 = key->getByteStringValue(CKA_PRIME_2); + exponent1 = key->getByteStringValue(CKA_EXPONENT_1); + exponent2 = key->getByteStringValue(CKA_EXPONENT_2); + coefficient = key->getByteStringValue(CKA_COEFFICIENT); + } + + privateKey->setN(modulus); + privateKey->setE(publicExponent); + privateKey->setD(privateExponent); + privateKey->setP(prime1); + privateKey->setQ(prime2); + privateKey->setDP1(exponent1); + privateKey->setDQ1(exponent2); + privateKey->setPQ(coefficient); + + return CKR_OK; +} + +CK_RV SoftHSM::getRSAPublicKey(RSAPublicKey* publicKey, Token* token, OSObject* key) +{ + if (publicKey == NULL) return CKR_ARGUMENTS_BAD; + if (token == NULL) return CKR_ARGUMENTS_BAD; + if (key == NULL) return CKR_ARGUMENTS_BAD; + + // Get the CKA_PRIVATE attribute, when the attribute is not present use default false + bool isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, false); + + // RSA Public Key Attributes + ByteString modulus; + ByteString publicExponent; + if (isKeyPrivate) + { + bool bOK = true; + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_MODULUS), modulus); + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_PUBLIC_EXPONENT), publicExponent); + if (!bOK) + return CKR_GENERAL_ERROR; + } + else + { + modulus = key->getByteStringValue(CKA_MODULUS); + publicExponent = key->getByteStringValue(CKA_PUBLIC_EXPONENT); + } + + publicKey->setN(modulus); + publicKey->setE(publicExponent); + + return CKR_OK; +} + +CK_RV SoftHSM::getDSAPrivateKey(DSAPrivateKey* privateKey, Token* token, OSObject* key) +{ + if (privateKey == NULL) return CKR_ARGUMENTS_BAD; + if (token == NULL) return CKR_ARGUMENTS_BAD; + if (key == NULL) return CKR_ARGUMENTS_BAD; + + // Get the CKA_PRIVATE attribute, when the attribute is not present use default false + bool isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, false); + + // DSA Private Key Attributes + ByteString prime; + ByteString subprime; + ByteString generator; + ByteString value; + if (isKeyPrivate) + { + bool bOK = true; + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_PRIME), prime); + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_SUBPRIME), subprime); + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_BASE), generator); + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_VALUE), value); + if (!bOK) + return CKR_GENERAL_ERROR; + } + else + { + prime = key->getByteStringValue(CKA_PRIME); + subprime = key->getByteStringValue(CKA_SUBPRIME); + generator = key->getByteStringValue(CKA_BASE); + value = key->getByteStringValue(CKA_VALUE); + } + + privateKey->setP(prime); + privateKey->setQ(subprime); + privateKey->setG(generator); + privateKey->setX(value); + + return CKR_OK; +} + +CK_RV SoftHSM::getDSAPublicKey(DSAPublicKey* publicKey, Token* token, OSObject* key) +{ + if (publicKey == NULL) return CKR_ARGUMENTS_BAD; + if (token == NULL) return CKR_ARGUMENTS_BAD; + if (key == NULL) return CKR_ARGUMENTS_BAD; + + // Get the CKA_PRIVATE attribute, when the attribute is not present use default false + bool isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, false); + + // DSA Public Key Attributes + ByteString prime; + ByteString subprime; + ByteString generator; + ByteString value; + if (isKeyPrivate) + { + bool bOK = true; + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_PRIME), prime); + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_SUBPRIME), subprime); + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_BASE), generator); + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_VALUE), value); + if (!bOK) + return CKR_GENERAL_ERROR; + } + else + { + prime = key->getByteStringValue(CKA_PRIME); + subprime = key->getByteStringValue(CKA_SUBPRIME); + generator = key->getByteStringValue(CKA_BASE); + value = key->getByteStringValue(CKA_VALUE); + } + + publicKey->setP(prime); + publicKey->setQ(subprime); + publicKey->setG(generator); + publicKey->setY(value); + + return CKR_OK; +} + +CK_RV SoftHSM::getECPrivateKey(ECPrivateKey* privateKey, Token* token, OSObject* key) +{ + if (privateKey == NULL) return CKR_ARGUMENTS_BAD; + if (token == NULL) return CKR_ARGUMENTS_BAD; + if (key == NULL) return CKR_ARGUMENTS_BAD; + + // Get the CKA_PRIVATE attribute, when the attribute is not present use default false + bool isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, false); + + // EC Private Key Attributes + ByteString group; + ByteString value; + if (isKeyPrivate) + { + bool bOK = true; + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_EC_PARAMS), group); + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_VALUE), value); + if (!bOK) + return CKR_GENERAL_ERROR; + } + else + { + group = key->getByteStringValue(CKA_EC_PARAMS); + value = key->getByteStringValue(CKA_VALUE); + } + + privateKey->setEC(group); + privateKey->setD(value); + + return CKR_OK; +} + +CK_RV SoftHSM::getECPublicKey(ECPublicKey* publicKey, Token* token, OSObject* key) +{ + if (publicKey == NULL) return CKR_ARGUMENTS_BAD; + if (token == NULL) return CKR_ARGUMENTS_BAD; + if (key == NULL) return CKR_ARGUMENTS_BAD; + + // Get the CKA_PRIVATE attribute, when the attribute is not present use default false + bool isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, false); + + // EC Public Key Attributes + ByteString group; + ByteString point; + if (isKeyPrivate) + { + bool bOK = true; + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_EC_PARAMS), group); + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_EC_POINT), point); + if (!bOK) + return CKR_GENERAL_ERROR; + } + else + { + group = key->getByteStringValue(CKA_EC_PARAMS); + point = key->getByteStringValue(CKA_EC_POINT); + } + + publicKey->setEC(group); + publicKey->setQ(point); + + return CKR_OK; +} + +CK_RV SoftHSM::getDHPrivateKey(DHPrivateKey* privateKey, Token* token, OSObject* key) +{ + if (privateKey == NULL) return CKR_ARGUMENTS_BAD; + if (token == NULL) return CKR_ARGUMENTS_BAD; + if (key == NULL) return CKR_ARGUMENTS_BAD; + + // Get the CKA_PRIVATE attribute, when the attribute is not present use default false + bool isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, false); + + // DH Private Key Attributes + ByteString prime; + ByteString generator; + ByteString value; + if (isKeyPrivate) + { + bool bOK = true; + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_PRIME), prime); + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_BASE), generator); + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_VALUE), value); + if (!bOK) + return CKR_GENERAL_ERROR; + } + else + { + prime = key->getByteStringValue(CKA_PRIME); + generator = key->getByteStringValue(CKA_BASE); + value = key->getByteStringValue(CKA_VALUE); + } + + privateKey->setP(prime); + privateKey->setG(generator); + privateKey->setX(value); + + return CKR_OK; +} + +CK_RV SoftHSM::getDHPublicKey(DHPublicKey* publicKey, DHPrivateKey* privateKey, ByteString& pubParams) +{ + if (publicKey == NULL) return CKR_ARGUMENTS_BAD; + if (privateKey == NULL) return CKR_ARGUMENTS_BAD; + + // Copy Domain Parameters from Private Key + publicKey->setP(privateKey->getP()); + publicKey->setG(privateKey->getG()); + + // Set value + publicKey->setY(pubParams); + + return CKR_OK; +} + +CK_RV SoftHSM::getECDHPublicKey(ECPublicKey* publicKey, ECPrivateKey* privateKey, ByteString& pubData) +{ + if (publicKey == NULL) return CKR_ARGUMENTS_BAD; + if (privateKey == NULL) return CKR_ARGUMENTS_BAD; + + // Copy Domain Parameters from Private Key + publicKey->setEC(privateKey->getEC()); + + // Set value + ByteString data = getECDHPubData(pubData); + publicKey->setQ(data); + + return CKR_OK; +} + +// ECDH pubData can be in RAW or DER format. +// Need to convert RAW as SoftHSM uses DER. +ByteString SoftHSM::getECDHPubData(ByteString& pubData) +{ + size_t len = pubData.size(); + size_t controlOctets = 2; + if (len == 65 || len == 97 || len == 133) + { + // Raw: Length matches the public key size of P-256, P-384, or P-521 + controlOctets = 0; + } + else if (len < controlOctets || pubData[0] != 0x04) + { + // Raw: Too short or does not start with 0x04 + controlOctets = 0; + } + else if (pubData[1] < 0x80) + { + // Raw: Length octet does not match remaining data length + if (pubData[1] != (len - controlOctets)) controlOctets = 0; + } + else + { + size_t lengthOctets = pubData[1] & 0x7F; + controlOctets += lengthOctets; + + if (controlOctets >= len) + { + // Raw: Too short + controlOctets = 0; + } + else + { + ByteString length(&pubData[2], lengthOctets); + + if (length.long_val() != (len - controlOctets)) + { + // Raw: Length octets does not match remaining data length + controlOctets = 0; + } + } + } + + // DER format + if (controlOctets != 0) return pubData; + + // RAW format + ByteString header; + if (len < 0x80) + { + header.resize(2); + header[0] = (unsigned char)0x04; + header[1] = (unsigned char)(len & 0x7F); + } + else + { + // Count significate bytes + size_t bytes = sizeof(size_t); + for(; bytes > 0; bytes--) + { + size_t value = len >> ((bytes - 1) * 8); + if (value & 0xFF) break; + } + + // Set header data + header.resize(2 + bytes); + header[0] = (unsigned char)0x04; + header[1] = (unsigned char)(0x80 | bytes); + for (size_t i = 1; i <= bytes; i++) + { + header[2+bytes-i] = (unsigned char) (len & 0xFF); + len >>= 8; + } + } + + return header + pubData; +} + +CK_RV SoftHSM::getGOSTPrivateKey(GOSTPrivateKey* privateKey, Token* token, OSObject* key) +{ + if (privateKey == NULL) return CKR_ARGUMENTS_BAD; + if (token == NULL) return CKR_ARGUMENTS_BAD; + if (key == NULL) return CKR_ARGUMENTS_BAD; + + // Get the CKA_PRIVATE attribute, when the attribute is not present use default false + bool isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, false); + + // GOST Private Key Attributes + ByteString value; + ByteString param; + if (isKeyPrivate) + { + bool bOK = true; + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_VALUE), value); + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_GOSTR3410_PARAMS), param); + if (!bOK) + return CKR_GENERAL_ERROR; + } + else + { + value = key->getByteStringValue(CKA_VALUE); + param = key->getByteStringValue(CKA_GOSTR3410_PARAMS); + } + + privateKey->setD(value); + privateKey->setEC(param); + + return CKR_OK; +} + +CK_RV SoftHSM::getGOSTPublicKey(GOSTPublicKey* publicKey, Token* token, OSObject* key) +{ + if (publicKey == NULL) return CKR_ARGUMENTS_BAD; + if (token == NULL) return CKR_ARGUMENTS_BAD; + if (key == NULL) return CKR_ARGUMENTS_BAD; + + // Get the CKA_PRIVATE attribute, when the attribute is not present use default false + bool isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, false); + + // GOST Public Key Attributes + ByteString point; + ByteString param; + if (isKeyPrivate) + { + bool bOK = true; + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_VALUE), point); + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_GOSTR3410_PARAMS), param); + if (!bOK) + return CKR_GENERAL_ERROR; + } + else + { + point = key->getByteStringValue(CKA_VALUE); + param = key->getByteStringValue(CKA_GOSTR3410_PARAMS); + } + + publicKey->setQ(point); + publicKey->setEC(param); + + return CKR_OK; +} + +CK_RV SoftHSM::getSymmetricKey(SymmetricKey* skey, Token* token, OSObject* key) +{ + if (skey == NULL) return CKR_ARGUMENTS_BAD; + if (token == NULL) return CKR_ARGUMENTS_BAD; + if (key == NULL) return CKR_ARGUMENTS_BAD; + + // Get the CKA_PRIVATE attribute, when the attribute is not present use default false + bool isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, false); + + ByteString keybits; + if (isKeyPrivate) + { + if (!token->decrypt(key->getByteStringValue(CKA_VALUE), keybits)) + return CKR_GENERAL_ERROR; + } + else + { + keybits = key->getByteStringValue(CKA_VALUE); + } + + skey->setKeyBits(keybits); + + return CKR_OK; +} + +bool SoftHSM::setRSAPrivateKey(OSObject* key, const ByteString &ber, Token* token, bool isPrivate) const +{ + AsymmetricAlgorithm* rsa = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::RSA); + if (rsa == NULL) + return false; + PrivateKey* priv = rsa->newPrivateKey(); + if (priv == NULL) + { + CryptoFactory::i()->recycleAsymmetricAlgorithm(rsa); + return false; + } + if (!priv->PKCS8Decode(ber)) + { + rsa->recyclePrivateKey(priv); + CryptoFactory::i()->recycleAsymmetricAlgorithm(rsa); + return false; + } + // RSA Private Key Attributes + ByteString modulus; + ByteString publicExponent; + ByteString privateExponent; + ByteString prime1; + ByteString prime2; + ByteString exponent1; + ByteString exponent2; + ByteString coefficient; + if (isPrivate) + { + token->encrypt(((RSAPrivateKey*)priv)->getN(), modulus); + token->encrypt(((RSAPrivateKey*)priv)->getE(), publicExponent); + token->encrypt(((RSAPrivateKey*)priv)->getD(), privateExponent); + token->encrypt(((RSAPrivateKey*)priv)->getP(), prime1); + token->encrypt(((RSAPrivateKey*)priv)->getQ(), prime2); + token->encrypt(((RSAPrivateKey*)priv)->getDP1(), exponent1); + token->encrypt(((RSAPrivateKey*)priv)->getDQ1(), exponent2); + token->encrypt(((RSAPrivateKey*)priv)->getPQ(), coefficient); + } + else + { + modulus = ((RSAPrivateKey*)priv)->getN(); + publicExponent = ((RSAPrivateKey*)priv)->getE(); + privateExponent = ((RSAPrivateKey*)priv)->getD(); + prime1 = ((RSAPrivateKey*)priv)->getP(); + prime2 = ((RSAPrivateKey*)priv)->getQ(); + exponent1 = ((RSAPrivateKey*)priv)->getDP1(); + exponent2 = ((RSAPrivateKey*)priv)->getDQ1(); + coefficient = ((RSAPrivateKey*)priv)->getPQ(); + } + bool bOK = true; + bOK = bOK && key->setAttribute(CKA_MODULUS, modulus); + bOK = bOK && key->setAttribute(CKA_PUBLIC_EXPONENT, publicExponent); + bOK = bOK && key->setAttribute(CKA_PRIVATE_EXPONENT, privateExponent); + bOK = bOK && key->setAttribute(CKA_PRIME_1, prime1); + bOK = bOK && key->setAttribute(CKA_PRIME_2, prime2); + bOK = bOK && key->setAttribute(CKA_EXPONENT_1,exponent1); + bOK = bOK && key->setAttribute(CKA_EXPONENT_2, exponent2); + bOK = bOK && key->setAttribute(CKA_COEFFICIENT, coefficient); + + rsa->recyclePrivateKey(priv); + CryptoFactory::i()->recycleAsymmetricAlgorithm(rsa); + + return bOK; +} + +bool SoftHSM::setDSAPrivateKey(OSObject* key, const ByteString &ber, Token* token, bool isPrivate) const +{ + AsymmetricAlgorithm* dsa = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::DSA); + if (dsa == NULL) + return false; + PrivateKey* priv = dsa->newPrivateKey(); + if (priv == NULL) + { + CryptoFactory::i()->recycleAsymmetricAlgorithm(dsa); + return false; + } + if (!priv->PKCS8Decode(ber)) + { + dsa->recyclePrivateKey(priv); + CryptoFactory::i()->recycleAsymmetricAlgorithm(dsa); + return false; + } + // DSA Private Key Attributes + ByteString prime; + ByteString subprime; + ByteString generator; + ByteString value; + if (isPrivate) + { + token->encrypt(((DSAPrivateKey*)priv)->getP(), prime); + token->encrypt(((DSAPrivateKey*)priv)->getQ(), subprime); + token->encrypt(((DSAPrivateKey*)priv)->getG(), generator); + token->encrypt(((DSAPrivateKey*)priv)->getX(), value); + } + else + { + prime = ((DSAPrivateKey*)priv)->getP(); + subprime = ((DSAPrivateKey*)priv)->getQ(); + generator = ((DSAPrivateKey*)priv)->getG(); + value = ((DSAPrivateKey*)priv)->getX(); + } + bool bOK = true; + bOK = bOK && key->setAttribute(CKA_PRIME, prime); + bOK = bOK && key->setAttribute(CKA_SUBPRIME, subprime); + bOK = bOK && key->setAttribute(CKA_BASE, generator); + bOK = bOK && key->setAttribute(CKA_VALUE, value); + + dsa->recyclePrivateKey(priv); + CryptoFactory::i()->recycleAsymmetricAlgorithm(dsa); + + return bOK; +} + +bool SoftHSM::setDHPrivateKey(OSObject* key, const ByteString &ber, Token* token, bool isPrivate) const +{ + AsymmetricAlgorithm* dh = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::DH); + if (dh == NULL) + return false; + PrivateKey* priv = dh->newPrivateKey(); + if (priv == NULL) + { + CryptoFactory::i()->recycleAsymmetricAlgorithm(dh); + return false; + } + if (!priv->PKCS8Decode(ber)) + { + dh->recyclePrivateKey(priv); + CryptoFactory::i()->recycleAsymmetricAlgorithm(dh); + return false; + } + // DH Private Key Attributes + ByteString prime; + ByteString generator; + ByteString value; + if (isPrivate) + { + token->encrypt(((DHPrivateKey*)priv)->getP(), prime); + token->encrypt(((DHPrivateKey*)priv)->getG(), generator); + token->encrypt(((DHPrivateKey*)priv)->getX(), value); + } + else + { + prime = ((DHPrivateKey*)priv)->getP(); + generator = ((DHPrivateKey*)priv)->getG(); + value = ((DHPrivateKey*)priv)->getX(); + } + bool bOK = true; + bOK = bOK && key->setAttribute(CKA_PRIME, prime); + bOK = bOK && key->setAttribute(CKA_BASE, generator); + bOK = bOK && key->setAttribute(CKA_VALUE, value); + + dh->recyclePrivateKey(priv); + CryptoFactory::i()->recycleAsymmetricAlgorithm(dh); + + return bOK; +} +bool SoftHSM::setECPrivateKey(OSObject* key, const ByteString &ber, Token* token, bool isPrivate) const +{ + AsymmetricAlgorithm* ecc = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::ECDSA); + if (ecc == NULL) + return false; + PrivateKey* priv = ecc->newPrivateKey(); + if (priv == NULL) + { + CryptoFactory::i()->recycleAsymmetricAlgorithm(ecc); + return false; + } + if (!priv->PKCS8Decode(ber)) + { + ecc->recyclePrivateKey(priv); + CryptoFactory::i()->recycleAsymmetricAlgorithm(ecc); + return false; + } + // EC Private Key Attributes + ByteString group; + ByteString value; + if (isPrivate) + { + token->encrypt(((ECPrivateKey*)priv)->getEC(), group); + token->encrypt(((ECPrivateKey*)priv)->getD(), value); + } + else + { + group = ((ECPrivateKey*)priv)->getEC(); + value = ((ECPrivateKey*)priv)->getD(); + } + bool bOK = true; + bOK = bOK && key->setAttribute(CKA_EC_PARAMS, group); + bOK = bOK && key->setAttribute(CKA_VALUE, value); + + ecc->recyclePrivateKey(priv); + CryptoFactory::i()->recycleAsymmetricAlgorithm(ecc); + + return bOK; +} + +CK_RV SoftHSM::MechParamCheckRSAPKCSOAEP(CK_MECHANISM_PTR pMechanism) +{ + // This is a programming error + if (pMechanism->mechanism != CKM_RSA_PKCS_OAEP) { + ERROR_MSG("MechParamCheckRSAPKCSOAEP called on wrong mechanism"); + return CKR_GENERAL_ERROR; + } + + if (pMechanism->pParameter == NULL_PTR || + pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_OAEP_PARAMS)) + { + ERROR_MSG("pParameter must be of type CK_RSA_PKCS_OAEP_PARAMS"); + return CKR_ARGUMENTS_BAD; + } + + CK_RSA_PKCS_OAEP_PARAMS_PTR params = (CK_RSA_PKCS_OAEP_PARAMS_PTR)pMechanism->pParameter; + if (params->hashAlg != CKM_SHA_1) + { + ERROR_MSG("hashAlg must be CKM_SHA_1"); + return CKR_ARGUMENTS_BAD; + } + if (params->mgf != CKG_MGF1_SHA1) + { + ERROR_MSG("mgf must be CKG_MGF1_SHA1"); + return CKR_ARGUMENTS_BAD; + } + if (params->source != CKZ_DATA_SPECIFIED) + { + ERROR_MSG("source must be CKZ_DATA_SPECIFIED"); + return CKR_ARGUMENTS_BAD; + } + if (params->pSourceData != NULL) + { + ERROR_MSG("pSourceData must be NULL"); + return CKR_ARGUMENTS_BAD; + } + if (params->ulSourceDataLen != 0) + { + ERROR_MSG("ulSourceDataLen must be 0"); + return CKR_ARGUMENTS_BAD; + } + return CKR_OK; +} + +bool SoftHSM::isMechanismPermitted(OSObject* key, CK_MECHANISM_PTR pMechanism) { + OSAttribute attribute = key->getAttribute(CKA_ALLOWED_MECHANISMS); + std::set<CK_MECHANISM_TYPE> allowed = attribute.getMechanismTypeSetValue(); + if (allowed.empty()) { + return true; + } + + return allowed.find(pMechanism->mechanism) != allowed.end(); +} |