#include "AppleCSP.h"
#include "AppleCSPSession.h"
#include "AppleCSPUtils.h"
#include <stdio.h>
#include "cspdebugging.h"
#include <security_cdsa_plugin/CSPsession.h>
#include <security_utilities/alloc.h>
#ifdef BSAFE_CSP_ENABLE
#include "bsafecsp.h"
#include "bsafecspi.h"
#endif
#ifdef CRYPTKIT_CSP_ENABLE
#include "cryptkitcsp.h"
#include "FEEKeys.h"
#endif
#include <miscAlgFactory.h>
#ifdef ASC_CSP_ENABLE
#include "ascFactory.h"
#endif
#include <RSA_DSA_csp.h>
#include <RSA_DSA_keys.h>
#include <DH_csp.h>
#include <DH_keys.h>
#include "YarrowConnection.h"
AppleCSPPlugin::AppleCSPPlugin() :
normAllocator(Allocator::standard(Allocator::normal)),
privAllocator(Allocator::standard(Allocator::sensitive)),
#ifdef BSAFE_CSP_ENABLE
bSafe4Factory(new BSafeFactory(&normAllocator, &privAllocator)),
#endif
#ifdef CRYPTKIT_CSP_ENABLE
cryptKitFactory(new CryptKitFactory(&normAllocator, &privAllocator)),
#endif
miscAlgFactory(new MiscAlgFactory(&normAllocator, &privAllocator)),
#ifdef ASC_CSP_ENABLE
ascAlgFactory(new AscAlgFactory(&normAllocator, &privAllocator)),
#endif
rsaDsaAlgFactory(new RSA_DSA_Factory(&normAllocator, &privAllocator)),
dhAlgFactory(new DH_Factory(&normAllocator, &privAllocator))
{
}
AppleCSPPlugin::~AppleCSPPlugin()
{
#ifdef BSAFE_CSP_ENABLE
delete bSafe4Factory;
#endif
#ifdef CRYPTKIT_CSP_ENABLE
delete cryptKitFactory;
#endif
delete miscAlgFactory;
#ifdef ASC_CSP_ENABLE
delete ascAlgFactory;
#endif
delete rsaDsaAlgFactory;
delete dhAlgFactory;
}
PluginSession *AppleCSPPlugin::makeSession(
CSSM_MODULE_HANDLE handle,
const CSSM_VERSION &version,
uint32 subserviceId,
CSSM_SERVICE_TYPE subserviceType,
CSSM_ATTACH_FLAGS attachFlags,
const CSSM_UPCALLS &upcalls)
{
switch (subserviceType) {
case CSSM_SERVICE_CSP:
return new AppleCSPSession(handle,
*this,
version,
subserviceId,
subserviceType,
attachFlags,
upcalls);
default:
CssmError::throwMe(CSSMERR_CSSM_INVALID_SERVICE_MASK);
return 0; }
}
AppleCSPSession::AppleCSPSession(
CSSM_MODULE_HANDLE handle,
AppleCSPPlugin &plug,
const CSSM_VERSION &version,
uint32 subserviceId,
CSSM_SERVICE_TYPE subserviceType,
CSSM_ATTACH_FLAGS attachFlags,
const CSSM_UPCALLS &upcalls)
: CSPFullPluginSession(handle,
plug,
version,
subserviceId,
subserviceType,
attachFlags,
upcalls),
#ifdef BSAFE_CSP_ENABLE
bSafe4Factory(*(dynamic_cast<BSafeFactory *>(plug.bSafe4Factory))),
#endif
#ifdef CRYPTKIT_CSP_ENABLE
cryptKitFactory(*(dynamic_cast<CryptKitFactory *>(plug.cryptKitFactory))),
#endif
miscAlgFactory(*(dynamic_cast<MiscAlgFactory *>(plug.miscAlgFactory))),
#ifdef ASC_CSP_ENABLE
ascAlgFactory(*(dynamic_cast<AscAlgFactory *>(plug.ascAlgFactory))),
#endif
rsaDsaAlgFactory(*(dynamic_cast<RSA_DSA_Factory *>(plug.rsaDsaAlgFactory))),
dhAlgFactory(*(dynamic_cast<DH_Factory *>(plug.dhAlgFactory))),
normAllocator(*this),
privAllocator(plug.privAlloc())
{
}
AppleCSPSession::~AppleCSPSession()
{
}
CSPFullPluginSession::CSPContext *
AppleCSPSession::contextCreate(
CSSM_CC_HANDLE handle,
const Context &context)
{
return NULL;
}
void AppleCSPSession::setupContext(
CSPContext * &cspCtx,
const Context &context,
bool encoding)
{
#ifdef BSAFE_CSP_ENABLE
if (bSafe4Factory.setup(*this, cspCtx, context)) {
CASSERT(cspCtx != NULL);
return;
}
#endif
if (rsaDsaAlgFactory.setup(*this, cspCtx, context)) {
CASSERT(cspCtx != NULL);
return;
}
if (miscAlgFactory.setup(*this, cspCtx, context)) {
CASSERT(cspCtx != NULL);
return;
}
if (dhAlgFactory.setup(*this, cspCtx, context)) {
CASSERT(cspCtx != NULL);
return;
}
#ifdef CRYPTKIT_CSP_ENABLE
if (cryptKitFactory.setup(*this, cspCtx, context)) {
CASSERT(cspCtx != NULL);
return;
}
#endif
#ifdef ASC_CSP_ENABLE
if (ascAlgFactory.setup(*this, cspCtx, context)) {
CASSERT(cspCtx != NULL);
return;
}
#endif
if(setup(cspCtx, context)) {
CASSERT(cspCtx != NULL);
return;
}
dprintf0("AppleCSPSession::setupContext: invalid algorithm\n");
CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
}
bool AppleCSPSession::setup(
CSPFullPluginSession::CSPContext * &cspCtx,
const Context &context)
{
if (cspCtx) {
return false; }
switch(context.type()) {
case CSSM_ALGCLASS_RANDOMGEN:
switch (context.algorithm()) {
case CSSM_ALGID_APPLE_YARROW:
cspCtx = new YarrowContext(*this);
return true;
default:
return false;
}
default:
return false;
}
return false;
}
YarrowContext::YarrowContext(AppleCSPSession &session)
: AppleCSPContext(session)
{
}
YarrowContext::~YarrowContext()
{
}
void YarrowContext::init(
const Context &context,
bool encoding)
{
outSize = context.getInt(CSSM_ATTRIBUTE_OUTPUT_SIZE,
CSSMERR_CSP_INVALID_ATTR_OUTPUT_SIZE);
CssmCryptoData *cseed = context.get<CssmCryptoData>(CSSM_ATTRIBUTE_SEED);
if(cseed == NULL) {
return;
}
CssmData seed = (*cseed)();
if((seed.Length == 0) ||
(seed.Data == NULL)) {
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_SEED);
}
session().addEntropy((size_t)seed.Length, seed.Data);
}
void YarrowContext::final(
CssmData &out)
{
session().getRandomBytes((size_t)out.Length, out.Data);
}
static KeyRef CssmDataToKeyRef(
const CSSM_DATA &data)
{
if(data.Length != sizeof(KeyRef)) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE);
}
uint8 *cp = data.Data + sizeof(KeyRef) - 1;
KeyRef keyRef = 0;
for(unsigned dex=0; dex<sizeof(KeyRef); dex++) {
keyRef <<= 8;
keyRef |= *cp--;
}
return keyRef;
}
static void keyRefToCssmData(
KeyRef keyRef,
CSSM_DATA &data,
Allocator &allocator)
{
if(data.Length > sizeof(keyRef)) {
memset(data.Data + sizeof(keyRef), 0, data.Length - sizeof(keyRef));
}
else if(data.Length < sizeof(keyRef)) {
allocator.free(data.Data);
data.Data = NULL;
data.Length = 0;
}
setUpData(data, sizeof(keyRef), allocator);
uint8 *cp = data.Data;
for(unsigned i=0; i<sizeof(keyRef); i++) {
*cp++ = keyRef & 0xff;
keyRef >>= 8;
}
}
BinaryKey *AppleCSPSession::lookupKeyRef(
KeyRef keyRef)
{
const BinaryKey *binKey;
keyMap::iterator it = refKeyMap.find(keyRef);
if(it == refKeyMap.end()) {
return NULL;
}
binKey = it->second;
assert(binKey == reinterpret_cast<const BinaryKey *>(keyRef));
assert(binKey->mKeyRef == keyRef);
return const_cast<BinaryKey *>(binKey);
}
void AppleCSPSession::addRefKey(
BinaryKey &binKey,
CssmKey &cssmKey)
{
KeyRef keyRef = reinterpret_cast<KeyRef>(&binKey);
binKey.mKeyRef = keyRef;
binKey.mKeyHeader = CssmKey::Header::overlay(cssmKey.KeyHeader);
{
StLock<Mutex> _(refKeyMapLock);
assert(lookupKeyRef(keyRef) == NULL);
refKeyMap[keyRef] = &binKey;
}
cssmKey.KeyHeader.BlobType = CSSM_KEYBLOB_REFERENCE;
cssmKey.KeyHeader.Format = CSSM_KEYBLOB_REF_FORMAT_INTEGER;
keyRefToCssmData(keyRef, cssmKey.KeyData, normAllocator);
secdebug("freeKey", "CSP addRefKey key %p keyData %p keyRef %p",
&cssmKey, cssmKey.KeyData.Data, &binKey);
}
BinaryKey & AppleCSPSession::lookupRefKey(
const CssmKey &cssmKey)
{
KeyRef keyRef;
BinaryKey *binKey;
keyRef = CssmDataToKeyRef(cssmKey.KeyData);
{
StLock<Mutex> _(refKeyMapLock);
binKey = lookupKeyRef(keyRef);
}
if(binKey == NULL) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE);
}
assert(Guid::overlay(binKey->mKeyHeader.CspId) == plugin.myGuid());
CSSM_KEYHEADER localHdr = cssmKey.KeyHeader;
localHdr.BlobType = binKey->mKeyHeader.BlobType;
localHdr.Format = binKey->mKeyHeader.Format;
if(memcmp(&localHdr, &binKey->mKeyHeader, sizeof(CSSM_KEYHEADER))) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE);
}
return (*binKey);
}
void AppleCSPSession::FreeKey(
const AccessCredentials *AccessCred,
CssmKey &KeyPtr,
CSSM_BOOL Delete)
{
if((KeyPtr.blobType() == CSSM_KEYBLOB_REFERENCE) &&
(KeyPtr.cspGuid() == plugin.myGuid())) {
KeyRef keyRef = CssmDataToKeyRef(KeyPtr.KeyData);
{
StLock<Mutex> _(refKeyMapLock);
BinaryKey *binKey = lookupKeyRef(keyRef);
if(binKey != NULL) {
secdebug("freeKey", "CSP FreeKey key %p keyData %p binKey %p",
&KeyPtr, KeyPtr.KeyData.Data, binKey);
try {
refKeyMap.erase(keyRef);
delete binKey;
}
catch (...) {
errorLog0("Error deleting/erasing known "
"ref key\n");
}
}
else {
secdebug("freeKey", "CSP freeKey unknown key");
}
}
}
CSPFullPluginSession::FreeKey(AccessCred, KeyPtr, Delete);
}
void AppleCSPSession::PassThrough(
CSSM_CC_HANDLE CCHandle,
const Context &Context,
uint32 PassThroughId,
const void *InData,
void **OutData)
{
*OutData = NULL;
if(Context.type() != CSSM_ALGCLASS_NONE) {
CssmError::throwMe(CSSMERR_CSP_INVALID_CONTEXT);
}
switch(PassThroughId) {
case CSSM_APPLECSP_KEYDIGEST:
{
CssmKey &key = Context.get<CssmKey>(
CSSM_ATTRIBUTE_KEY,
CSSMERR_CSP_MISSING_ATTR_KEY);
switch(key.keyClass()) {
case CSSM_KEYCLASS_PUBLIC_KEY:
case CSSM_KEYCLASS_PRIVATE_KEY:
case CSSM_KEYCLASS_SESSION_KEY:
break;
default:
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
}
CssmData blobToHash;
switch(key.blobType()) {
case CSSM_KEYBLOB_RAW:
{
CSPKeyInfoProvider *provider = infoProvider(key);
bool converted =
provider->getHashableBlob(privAllocator, blobToHash);
if(converted) {
delete provider;
break;
}
BinaryKey *binKey;
CSSM_KEYATTR_FLAGS flags = 0; provider->CssmKeyToBinary(NULL, flags,
&binKey);
binKey->mKeyHeader =
CssmKey::Header::overlay(key.KeyHeader);
CSSM_KEYBLOB_FORMAT rawFormat;
rawFormat = CSSM_KEYBLOB_RAW_FORMAT_DIGEST;
CSSM_KEYATTR_FLAGS attrFlags = 0;
binKey->generateKeyBlob(privAllocator,
blobToHash,
rawFormat,
*this,
NULL,
attrFlags);
delete binKey;
delete provider;
break;
}
case CSSM_KEYBLOB_REFERENCE:
{
BinaryKey &binKey = lookupRefKey(key);
CSSM_KEYBLOB_FORMAT rawFormat;
rawFormat = CSSM_KEYBLOB_RAW_FORMAT_DIGEST;
CSSM_KEYATTR_FLAGS attrFlags = 0;
binKey.generateKeyBlob(privAllocator,
blobToHash,
rawFormat,
*this,
NULL,
attrFlags);
}
break;
default:
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
}
CSSM_DATA_PTR outHash = NULL;
try {
outHash =
(CSSM_DATA_PTR)normAllocator.malloc(sizeof(CSSM_DATA));
outHash->Data =
(uint8 *)normAllocator.malloc(SHA1_DIGEST_SIZE);
outHash->Length = SHA1_DIGEST_SIZE;
}
catch(...) {
freeCssmData(blobToHash, privAllocator);
throw;
}
cspGenSha1Hash(blobToHash.data(), blobToHash.length(),
outHash->Data);
freeCssmData(blobToHash, privAllocator);
*OutData = outHash;
return;
}
default:
CssmError::throwMe(CSSMERR_CSP_INVALID_PASSTHROUGH_ID);
}
}
void AppleCSPSession::getKeySize(const CssmKey &key,
CSSM_KEY_SIZE &size)
{
CSPKeyInfoProvider *provider = infoProvider(key);
try {
provider->QueryKeySizeInBits(size);
}
catch(...) {
delete provider;
throw;
}
delete provider;
}
void AppleCSPSession::getRandomBytes(size_t length, uint8 *cp)
{
try {
cspGetRandomBytes(cp, (unsigned)length);
}
catch(...) {
errorLog0("CSP: YarrowClient failure\n");
}
}
void AppleCSPSession::addEntropy(size_t length, const uint8 *cp)
{
try {
cspAddEntropy(cp, (unsigned)length);
}
catch(...) {
#if CSP_ALLOW_FEE_RNG
return;
#else
throw;
#endif
}
}
CSPKeyInfoProvider *AppleCSPSession::infoProvider(
const CssmKey &key)
{
CSPKeyInfoProvider *provider = NULL;
#ifdef BSAFE_CSP_ENABLE
provider = BSafe::BSafeKeyInfoProvider::provider(key, *this);
if(provider != NULL) {
return provider;
}
#endif
provider = RSAKeyInfoProvider::provider(key, *this);
if(provider != NULL) {
return provider;
}
provider = SymmetricKeyInfoProvider::provider(key, *this);
if(provider != NULL) {
return provider;
}
#ifdef CRYPTKIT_CSP_ENABLE
provider = CryptKit::FEEKeyInfoProvider::provider(key, *this);
if(provider != NULL) {
return provider;
}
#endif
provider = DSAKeyInfoProvider::provider(key, *this);
if(provider != NULL) {
return provider;
}
provider = DHKeyInfoProvider::provider(key, *this);
if(provider != NULL) {
return provider;
}
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
}