#include "AppleCSP.h"
#include "AppleCSPSession.h"
#include "AppleCSPUtils.h"
#include <stdio.h>
#include "cspdebugging.h"
#include <Security/CSPsession.h>
#include <Security/cssmalloc.h>
#ifdef BSAFE_CSP_ENABLE
#include <BSafeCSP/bsafecsp.h>
#include <BSafeCSP/bsafecspi.h>
#endif
#ifdef CRYPTKIT_CSP_ENABLE
#include <CryptKitCSP/cryptkitcsp.h>
#include <CryptKitCSP/FEEKeys.h>
#endif
#include <MiscCSPAlgs/miscAlgFactory.h>
#ifdef ASC_CSP_ENABLE
#include <ComCryption/ascFactory.h>
#endif
#include <RSA_DSA/RSA_DSA_csp.h>
#include <RSA_DSA/RSA_DSA_keys.h>
#include <DiffieHellman/DH_csp.h>
#include <DiffieHellman/DH_keys.h>
#include "YarrowConnection.h"
#ifdef NDEBUG
#define CSP_ALLOW_FEE_RNG 0
#else
#ifdef CRYPTKIT_CSP_ENABLE
#define CSP_ALLOW_FEE_RNG 1
#else
#define CSP_ALLOW_FEE_RNG 0
#endif
#endif
AppleCSPPlugin::AppleCSPPlugin() :
normAllocator(CssmAllocator::standard(CssmAllocator::normal)),
privAllocator(CssmAllocator::standard(CssmAllocator::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;
KeyRef keyRef = cp[0];
keyRef |= ((KeyRef)cp[1]) << 8;
keyRef |= ((KeyRef)cp[2]) << 16;
keyRef |= ((KeyRef)cp[3]) << 24;
return keyRef;
}
static void keyRefToCssmData(
KeyRef keyRef,
CSSM_DATA &data,
CssmAllocator &allocator)
{
setUpData(data, sizeof(keyRef), allocator);
uint8 *cp = data.Data;
cp[0] = keyRef & 0xff;
cp[1] = (keyRef >> 8) & 0xff;
cp[2] = (keyRef >> 16) & 0xff;
cp[3] = (keyRef >> 24) & 0xff;
}
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);
}
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) {
try {
refKeyMap.erase(keyRef);
delete binKey;
}
catch (...) {
errorLog0("Error deleting/erasing known "
"ref key\n");
}
}
}
}
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 rawBlob;
bool allocdRawBlob = false;
switch(key.blobType()) {
case CSSM_KEYBLOB_RAW:
rawBlob = CssmData::overlay(key.KeyData);
break;
case CSSM_KEYBLOB_REFERENCE:
{
BinaryKey &binKey = lookupRefKey(key);
CSSM_KEYBLOB_FORMAT rawFormat;
rawFormat = requestedKeyFormat(Context, key);
binKey.generateKeyBlob(privAllocator,
rawBlob,
rawFormat);
}
allocdRawBlob = true; 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(...) {
if(allocdRawBlob) {
freeCssmData(rawBlob, privAllocator);
}
throw;
}
cspGenSha1Hash(rawBlob.data(), rawBlob.length(), outHash->Data);
if(allocdRawBlob) {
freeCssmData(rawBlob, 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);
provider->QueryKeySizeInBits(size);
delete provider;
}
#if CSP_ALLOW_FEE_RNG
#include <CryptKit/feeRandom.h>
static Mutex feeRngMutex;
static feeRand feeRng = NULL;
static void cspRandViaFee(size_t length, uint8 *cp)
{
StLock<Mutex> _(feeRngMutex);
if(feeRng == NULL) {
feeRng = feeRandAlloc();
}
feeRandBytes(feeRng, cp, length);
}
#endif
void AppleCSPSession::getRandomBytes(size_t length, uint8 *cp)
{
try {
cspGetRandomBytes(cp, (unsigned)length);
}
catch(...) {
#if CSP_ALLOW_FEE_RNG
errorLog0("CSP: YarrowClient failure; using FEE RNG\n");
cspRandViaFee(length, cp);
#else
errorLog0("CSP: YarrowClient failure\n");
#endif
}
}
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);
if(provider != NULL) {
return provider;
}
#endif
provider = RSAKeyInfoProvider::provider(key);
if(provider != NULL) {
return provider;
}
provider = SymmetricKeyInfoProvider::provider(key);
if(provider != NULL) {
return provider;
}
#ifdef CRYPTKIT_CSP_ENABLE
provider = CryptKit::FEEKeyInfoProvider::provider(key);
if(provider != NULL) {
return provider;
}
#endif
provider = DSAKeyInfoProvider::provider(key);
if(provider != NULL) {
return provider;
}
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
}
CSPKeyInfoProvider *SymmetricKeyInfoProvider::provider(
const CssmKey &cssmKey)
{
if(cssmKey.blobType() != CSSM_KEYBLOB_RAW) {
errorLog0("KeyInfoProvider deals only with RAW keys!\n");
CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR);
}
if(cssmKey.keyClass() != CSSM_KEYCLASS_SESSION_KEY) {
return NULL;
}
return new SymmetricKeyInfoProvider(cssmKey);
}
SymmetricKeyInfoProvider::SymmetricKeyInfoProvider(
const CssmKey &cssmKey) :
CSPKeyInfoProvider(cssmKey)
{
}
void SymmetricKeyInfoProvider::CssmKeyToBinary(
BinaryKey **binKey)
{
CASSERT(mKey.keyClass() == CSSM_KEYCLASS_SESSION_KEY);
SymmetricBinaryKey *symBinKey = new SymmetricBinaryKey(
mKey.KeyHeader.LogicalKeySizeInBits);
copyCssmData(mKey,
symBinKey->mKeyData,
symBinKey->mAllocator);
*binKey = symBinKey;
}
void SymmetricKeyInfoProvider::QueryKeySizeInBits(
CSSM_KEY_SIZE &keySize)
{
keySize.LogicalKeySizeInBits = keySize.EffectiveKeySizeInBits =
mKey.length() * 8;
}