blob: b06efc2c46dd5924540858d35b373ae89f145ff3 [file] [log] [blame]
/*
* 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();
}