]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_apple_csp/lib/AppleCSP.cpp
Security-55163.44.tar.gz
[apple/security.git] / 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 (file)
index 0000000..7fd55f1
--- /dev/null
@@ -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 <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"
+
+//
+// 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<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())
+{
+       // 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<CssmCryptoData>(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); dex++) {
+               keyRef <<= 8;
+               keyRef |= *cp--;
+       }
+       return keyRef;
+}
+
+// Place a KeyRef into a CSSM_DATA, mallocing if necessary.
+static void keyRefToCssmData(
+       KeyRef                  keyRef,
+       CSSM_DATA               &data,
+       Allocator       &allocator)
+{
+       if(data.Length > 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<sizeof(keyRef); i++) {
+               *cp++ = keyRef & 0xff;
+               keyRef >>= 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<const BinaryKey *>(keyRef));
+       assert(binKey->mKeyRef == keyRef);
+       return const_cast<BinaryKey *>(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<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);
+}
+       
+// 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<Mutex> _(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<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);
+}
+
+/* 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<CssmKey>(
+                               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);
+}
+