X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/72a12576750f52947eb043106ba5c12c0d07decf..b1ab9ed8d0e0f1c3b66d7daa8fd5564444c56195:/libsecurity_apple_csp/lib/AppleCSP.cpp diff --git a/libsecurity_apple_csp/lib/AppleCSP.cpp b/libsecurity_apple_csp/lib/AppleCSP.cpp new file mode 100644 index 00000000..7fd55f16 --- /dev/null +++ b/libsecurity_apple_csp/lib/AppleCSP.cpp @@ -0,0 +1,661 @@ +/* + * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved. + * + * The contents of this file constitute Original Code as defined in and are + * subject to the Apple Public Source License Version 1.2 (the 'License'). + * You may not use this file except in compliance with the License. Please obtain + * a copy of the License at http://www.apple.com/publicsource and read it before + * using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS + * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT + * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the + * specific language governing rights and limitations under the License. + */ + + +// +// AppleCSP.cpp - top-level plugin and session implementation +// +#include "AppleCSP.h" +#include "AppleCSPSession.h" +#include "AppleCSPUtils.h" +#include +#include "cspdebugging.h" +#include +#include +#ifdef BSAFE_CSP_ENABLE +#include "bsafecsp.h" +#include "bsafecspi.h" +#endif +#ifdef CRYPTKIT_CSP_ENABLE +#include "cryptkitcsp.h" +#include "FEEKeys.h" +#endif +#include +#ifdef ASC_CSP_ENABLE +#include "ascFactory.h" +#endif +#include +#include +#include +#include + +#include "YarrowConnection.h" + +// +// Make and break the plugin object +// +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)) +{ + // misc. once-per-address-space cruft... +} + +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; +} + + +// +// Create a new plugin session, our way +// +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; // placebo + } +} + + +// +// Session constructor +// +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(plug.bSafe4Factory))), + #endif + #ifdef CRYPTKIT_CSP_ENABLE + cryptKitFactory(*(dynamic_cast(plug.cryptKitFactory))), + #endif + miscAlgFactory(*(dynamic_cast(plug.miscAlgFactory))), + #ifdef ASC_CSP_ENABLE + ascAlgFactory(*(dynamic_cast(plug.ascAlgFactory))), + #endif + rsaDsaAlgFactory(*(dynamic_cast(plug.rsaDsaAlgFactory))), + dhAlgFactory(*(dynamic_cast(plug.dhAlgFactory))), + normAllocator(*this), + privAllocator(plug.privAlloc()) +{ + // anything? +} + +AppleCSPSession::~AppleCSPSession() +{ + // anything? +} + +// +// Called at (CSSM) context create time. This is ignored; we do a full +// context setup later, at setupContext time. +// +CSPFullPluginSession::CSPContext * +AppleCSPSession::contextCreate( + CSSM_CC_HANDLE handle, + const Context &context) +{ + return NULL; +} + +// +// Called by CSPFullPluginSession when an op is actually commencing. +// Context can safely assumed to be fully formed and stable for the +// duration of the op; thus we wait until now to set up our +// CSPContext as appropriate to the op. +// +void AppleCSPSession::setupContext( + CSPContext * &cspCtx, + const Context &context, + bool encoding) +{ + /* + * Note we leave the decision as to whether it's OK to + * reuse a context to the individual factories. + */ + #ifdef BSAFE_CSP_ENABLE + /* Give BSAFE the firsrt shot if it's present */ + 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); +} + +/* + * Used for generating crypto contexts at this level. + * Analogous to AlgorithmFactory.setup(). + */ +bool AppleCSPSession::setup( + CSPFullPluginSession::CSPContext * &cspCtx, + const Context &context) +{ + if (cspCtx) { + return false; // not ours or already set + } + + switch(context.type()) { + case CSSM_ALGCLASS_RANDOMGEN: + switch (context.algorithm()) { + case CSSM_ALGID_APPLE_YARROW: + cspCtx = new YarrowContext(*this); + return true; + /* other random algs here */ + default: + return false; + } + /* other contexts here */ + default: + return false; + } + /* NOT REACHED */ + return false; + +} + +// +// Context for CSSM_ALGID_APPLE_YARROW. +// +YarrowContext::YarrowContext(AppleCSPSession &session) + : AppleCSPContext(session) +{ + // nothing for now +} + +YarrowContext::~YarrowContext() +{ + // nothing for now +} + +// +// Only job here is to snag the length and process the optional seed argument +// +void YarrowContext::init( + const Context &context, + bool encoding) +{ + /* stash requested length for use later in outputSize() */ + outSize = context.getInt(CSSM_ATTRIBUTE_OUTPUT_SIZE, + CSSMERR_CSP_INVALID_ATTR_OUTPUT_SIZE); + + /* optional seed */ + CssmCryptoData *cseed = context.get(CSSM_ATTRIBUTE_SEED); + if(cseed == NULL) { + /* we're done */ + 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); +} + +/*** + *** Binary Key support. + ***/ + +// Given a CSSM_DATA, extract its KeyRef. +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)) { + /* don't leave old raw key material lying around */ + memset(data.Data + sizeof(keyRef), 0, data.Length - sizeof(keyRef)); + } + else if(data.Length < sizeof(keyRef)) { + /* not enough space for even a keyRef, force realloc */ + allocator.free(data.Data); + data.Data = NULL; + data.Length = 0; + } + setUpData(data, sizeof(keyRef), allocator); + + uint8 *cp = data.Data; + for(unsigned i=0; i>= 8; + } +} + +// Look up a BinaryKey by its KeyRef. Returns NULL if not +// found. refKeyMapLock held on entry and exit. +BinaryKey *AppleCSPSession::lookupKeyRef( + KeyRef keyRef) +{ + const BinaryKey *binKey; + + // use safe version, don't create new entry if this key + // isn't there + keyMap::iterator it = refKeyMap.find(keyRef); + if(it == refKeyMap.end()) { + return NULL; + } + binKey = it->second; + assert(binKey == reinterpret_cast(keyRef)); + assert(binKey->mKeyRef == keyRef); + return const_cast(binKey); +} + +// add a BinaryKey to our refKeyMap. Sets up cssmKey +// as appropriate. +void AppleCSPSession::addRefKey( + BinaryKey &binKey, + CssmKey &cssmKey) +{ + // for now, KeyRef is just the address of the BinaryKey + KeyRef keyRef = reinterpret_cast(&binKey); + + binKey.mKeyRef = keyRef; + binKey.mKeyHeader = CssmKey::Header::overlay(cssmKey.KeyHeader); + { + StLock _(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); +} + +// Given a CssmKey in reference form, obtain the associated +// BinaryKey. Throws CSSMERR_CSP_INVALID_KEY_REFERENCE if +// key not found in session key map. +BinaryKey & AppleCSPSession::lookupRefKey( + const CssmKey &cssmKey) +{ + KeyRef keyRef; + BinaryKey *binKey; + + keyRef = CssmDataToKeyRef(cssmKey.KeyData); + { + StLock _(refKeyMapLock); + binKey = lookupKeyRef(keyRef); + } + if(binKey == NULL) { + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE); + } + assert(Guid::overlay(binKey->mKeyHeader.CspId) == plugin.myGuid()); + + /* + * Verify sensitive fields have not changed between when the BinaryKey was + * created/stored and when the caller passed in the ref key. + * Some fields were changed by addRefKey, so make a local copy.... + */ + 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); +} + +// CSPFullPluginSession declares & implements this. +// Note that we ignore the delete argument; since we don't +// store anything, freeing is the same as deleting. +void AppleCSPSession::FreeKey( + const AccessCredentials *AccessCred, + CssmKey &KeyPtr, + CSSM_BOOL Delete) +{ + + if((KeyPtr.blobType() == CSSM_KEYBLOB_REFERENCE) && + (KeyPtr.cspGuid() == plugin.myGuid())) { + // it's a ref key we generated - delete associated BinaryKey + KeyRef keyRef = CssmDataToKeyRef(KeyPtr.KeyData); + { + StLock _(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); +} + +/* Passthrough, used for key digest */ +void AppleCSPSession::PassThrough( + CSSM_CC_HANDLE CCHandle, + const Context &Context, + uint32 PassThroughId, + const void *InData, + void **OutData) +{ + *OutData = NULL; + + /* validate context */ + if(Context.type() != CSSM_ALGCLASS_NONE) { + CssmError::throwMe(CSSMERR_CSP_INVALID_CONTEXT); + } + + switch(PassThroughId) { + case CSSM_APPLECSP_KEYDIGEST: + { + CssmKey &key = Context.get( + CSSM_ATTRIBUTE_KEY, + CSSMERR_CSP_MISSING_ATTR_KEY); + + /* validate key as best we can */ + 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); + } + + /* + * Ref key: obtain binary, ask it for blob + * Raw key: get info provider, ask it for the blob. This + * allows for an optimized path which avoids + * converting to a BinaryKey. + */ + CssmData blobToHash; + switch(key.blobType()) { + case CSSM_KEYBLOB_RAW: + { + CSPKeyInfoProvider *provider = infoProvider(key); + bool converted = + provider->getHashableBlob(privAllocator, blobToHash); + if(converted) { + /* took optimized case; proceed */ + delete provider; + break; + } + + /* convert to BinaryKey and ask it to do the work */ + BinaryKey *binKey; + CSSM_KEYATTR_FLAGS flags = 0; // not used + provider->CssmKeyToBinary(NULL, // no paramKey + 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); + } + + /* obtain sha1 hash of blobToHash */ + + 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); + } + /* NOT REACHED */ +} + +/* + * CSPSession version of QueryKeySizeInBits. + */ +void AppleCSPSession::getKeySize(const CssmKey &key, + CSSM_KEY_SIZE &size) +{ + CSPKeyInfoProvider *provider = infoProvider(key); + try { + provider->QueryKeySizeInBits(size); + } + catch(...) { + /* don't leak this on error */ + 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 support. + ***/ + +/* + * Find a CSPKeyInfoProvider subclass for the specified key. + */ +CSPKeyInfoProvider *AppleCSPSession::infoProvider( + const CssmKey &key) +{ + CSPKeyInfoProvider *provider = NULL; + + #ifdef BSAFE_CSP_ENABLE + /* Give BSAFE first shot, if it's here */ + 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); +} +