]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_cdsa_plugin/lib/CSPsession.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_cdsa_plugin / lib / CSPsession.cpp
diff --git a/Security/libsecurity_cdsa_plugin/lib/CSPsession.cpp b/Security/libsecurity_cdsa_plugin/lib/CSPsession.cpp
new file mode 100644 (file)
index 0000000..41f4ee8
--- /dev/null
@@ -0,0 +1,1156 @@
+/*
+ * Copyright (c) 2000-2001,2011-2012,2014 Apple 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.
+ */
+
+
+//
+// CSPsession - Plugin framework for CSP plugin modules
+//
+#include <security_cdsa_plugin/CSPsession.h>
+#include <security_cdsa_plugin/cssmplugin.h>
+#include <security_cdsa_utilities/cssmbridge.h>
+
+
+typedef CSPFullPluginSession::CSPContext CSPContext;
+
+
+//
+// PluginContext construction
+//
+CSPPluginSession::PluginContext::~PluginContext()
+{ /* virtual */ }
+
+CSPFullPluginSession::AlgorithmFactory::~AlgorithmFactory()
+{ /* virtual */ }
+
+
+//
+// Internal utilities
+//
+CssmData CSPFullPluginSession::makeBuffer(size_t size, Allocator &alloc)
+{
+       return CssmData(alloc.malloc(size), size);
+}
+
+inline size_t CSPFullPluginSession::totalBufferSize(const CssmData *data, uint32 count)
+{
+       size_t size = 0;
+       for (uint32 n = 0; n < count; n++)
+               size += data[n].length();
+       return size;
+}
+
+
+//
+// Notify a context that its underlying CSSM context has (well, may have) changed.
+// The default reaction is to ask the frame to delete the context and start over.
+//
+bool CSPPluginSession::PluginContext::changed(const Context &context)
+{
+    return false;      // delete me, please
+}
+
+
+//
+// The Session's init() function calls your setupContext() method to prepare
+// it for action, then calls the context's init() method.
+//
+CSPContext *CSPFullPluginSession::init(CSSM_CC_HANDLE ccHandle,
+                                                                       CSSM_CONTEXT_TYPE type,
+                                                                       const Context &context, bool encoding)
+{
+    CSPContext *ctx = getContext<CSPContext>(ccHandle);
+    checkOperation(context.type(), type);
+
+    // ask the implementation to set up an internal context
+    setupContext(ctx, context, encoding);
+    assert(ctx != NULL);       // must have context now (@@@ throw INTERNAL_ERROR instead?)
+    ctx->mType = context.type();
+    ctx->mDirection = encoding;
+    setContext(ccHandle, ctx);
+
+    // initialize the context and return it
+    ctx->init(context, encoding);
+    return ctx;
+}
+
+
+//
+// Retrieve a context for a staged operation in progress.
+//
+CSPContext *CSPFullPluginSession::getStagedContext(CSSM_CC_HANDLE ccHandle,
+       CSSM_CONTEXT_TYPE type, bool encoding)
+{
+       CSPContext *ctx = getContext<CSPContext>(ccHandle);
+       if (ctx == NULL)
+               CssmError::throwMe(CSSMERR_CSP_INVALID_CONTEXT);        //@@@ better diagnostic?
+       checkOperation(ctx->type(), type);
+       if (ctx->encoding() != encoding)
+               CssmError::throwMe(CSSMERR_CSP_INVALID_CONTEXT);
+       return ctx;
+}
+
+
+//
+// The Session's checkState() function is called for subsequent staged operations
+// (update/final) to verify that the user didn't screw up the sequencing.
+//
+void CSPFullPluginSession::checkOperation(CSSM_CONTEXT_TYPE ctxType, CSSM_CONTEXT_TYPE opType)
+{
+    switch (opType) {
+        case CSSM_ALGCLASS_NONE:       // no check
+            return;
+        case CSSM_ALGCLASS_CRYPT:      // symmetric or asymmetric encryption
+            if (ctxType == CSSM_ALGCLASS_SYMMETRIC ||
+                ctxType == CSSM_ALGCLASS_ASYMMETRIC)
+                return;
+        default:                                       // plain match
+            if (ctxType == opType)
+                return;
+    }
+    CssmError::throwMe(CSSMERR_CSP_INVALID_CONTEXT);
+}
+
+
+//
+// The default implementations of the primary context operations throw internal
+// errors. You must implement any of these that are actually called by the
+// operations involved. The others, of course, can be left alone.
+//
+void CSPContext::init(const Context &context, bool encoding)
+{ CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); }
+
+void CSPContext::update(const CssmData &data)
+{ CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); }
+
+void CSPContext::update(void *inp, size_t &inSize, void *outp, size_t &outSize)
+{ CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); }
+
+void CSPContext::final(CssmData &out)
+{ CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); }
+
+void CSPContext::final(const CssmData &in)
+{ CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); }
+
+void CSPContext::generate(const Context &, CssmKey &pubKey, CssmKey &privKey)
+{ CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); }
+
+void CSPContext::generate(const Context &, uint32, CssmData &params,
+                                                uint32 &attrCount, Context::Attr * &attrs)
+{ CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); }
+
+size_t CSPContext::inputSize(size_t outSize)
+{ CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); }
+
+size_t CSPContext::outputSize(bool final, size_t inSize)
+{ CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); }
+
+void CSPContext::minimumProgress(size_t &in, size_t &out)
+{ CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); }
+
+CSPFullPluginSession::CSPContext *CSPContext::clone(Allocator &)
+{ CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED); }
+
+void CSPContext::setDigestAlgorithm(CSSM_ALGORITHMS digestAlg)
+{ CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); }
+
+void CSPContext::update(const CssmData *in,
+                                               uint32 inCount, Writer &writer)
+{
+    const CssmData *lastIn = in + inCount;
+    CssmData current;
+    for (;;) {
+        if (current.length() == 0) {
+            if (in == lastIn)
+                return;                // all done
+            current = *in++;
+                       continue; // Just in case next block is zero length too.
+        }
+        // match up current input and output buffers
+        void *outP; size_t outSize;
+        writer.nextBlock(outP, outSize);
+        size_t inSize = inputSize(outSize);
+        if (inSize > current.length())
+            inSize = current.length(); // cap to remaining input buffer
+        if (inSize > 0) {
+            // we can stuff into the current output buffer - do it
+            update(current.data(), inSize, outP, outSize);
+            current.use(inSize);
+            writer.use(outSize);
+        } else {
+            // We have remaining output buffer space, but not enough
+            // for the algorithm to make progress with it. We must proceed with
+            // a bounce buffer and split it manually into this and the next buffer(s).
+            size_t minOutput;
+            minimumProgress(inSize, minOutput);
+            assert(minOutput > outSize);               // PluginContext consistency (not fatal)
+            char splitBuffer[128];
+            assert(minOutput <= sizeof(splitBuffer)); // @@@ static buffer for now
+            outSize = sizeof(splitBuffer);
+            if (current.length() < inSize)
+                inSize = current.length();     // cap to data remaining in input buffer
+            update(current.data(), inSize, splitBuffer, outSize);
+            assert(inSize > 0);                                // progress made
+            writer.put(splitBuffer, outSize);  // stuff into buffer, the hard way
+            current.use(inSize);
+        }
+    }
+}
+
+void CSPContext::final(CssmData &out, Allocator &alloc)
+{
+    size_t needed = outputSize(true, 0);
+    if (out) {
+        if (out.length() < needed)
+            CssmError::throwMe(CSSMERR_CSP_OUTPUT_LENGTH_ERROR);
+    } else {
+        out = makeBuffer(needed, alloc);
+    }
+    final(out);
+}
+
+void CSPContext::final(Writer &writer, Allocator &alloc)
+{
+    if (size_t needed = outputSize(true, 0)) {
+        // need to generate additional output
+        writer.allocate(needed, alloc);                // belt + suspender
+    
+        void *addr; size_t size;
+        writer.nextBlock(addr, size);          // next single block available
+        if (needed <= size) {                          // rest fits into one block
+            CssmData chunk(addr, size);
+            final(chunk);
+            writer.use(chunk.length());
+        } else {                                                       // need to split it up
+            char splitBuffer[128];
+            assert(needed <= sizeof(splitBuffer));
+            CssmData chunk(splitBuffer, sizeof(splitBuffer));
+            final(chunk);
+            writer.put(chunk.data(), chunk.length());
+        }
+    }
+}
+
+
+//
+// Default context response functions
+//
+CSPPluginSession::PluginContext *
+CSPPluginSession::contextCreate(CSSM_CC_HANDLE, const Context &)
+{
+       return NULL;    // request no local context
+}
+
+void CSPPluginSession::contextUpdate(CSSM_CC_HANDLE ccHandle,
+                                     const Context &context, PluginContext * &ctx)
+{
+    // call update notifier in context object
+    if (ctx && !ctx->changed(context)) {
+        // context requested that it be removed
+        delete ctx;
+        ctx = NULL;
+    }
+}
+
+void CSPPluginSession::contextDelete(CSSM_CC_HANDLE, const Context &, PluginContext *)
+{
+    // do nothing (you can't prohibit deletion here)
+}
+
+
+//
+// Default event notification handler.
+// This default handler calls the virtual context* methods to dispose of context actions.
+//
+void CSPPluginSession::EventNotify(CSSM_CONTEXT_EVENT event,
+                                   CSSM_CC_HANDLE ccHandle, const Context &context)
+{
+    switch (event) {
+        case CSSM_CONTEXT_EVENT_CREATE:
+            if (PluginContext *ctx = contextCreate(ccHandle, context)) {
+                               StLock<Mutex> _(contextMapLock);
+                assert(contextMap[ccHandle] == NULL);  // check context re-creation
+                contextMap[ccHandle] = ctx;
+            }
+            break;
+        case CSSM_CONTEXT_EVENT_UPDATE:
+            // note that the handler can change the map entry (even to NULL, if desired)
+                       {
+                               StLock<Mutex> _(contextMapLock);
+                               contextUpdate(ccHandle, context, contextMap[ccHandle]);
+                       }
+            break;
+        case CSSM_CONTEXT_EVENT_DELETE:
+                       {
+                               StLock<Mutex> _(contextMapLock);
+                               if (PluginContext *ctx = contextMap[ccHandle]) {
+                                       contextDelete(ccHandle, context, ctx);
+                                       delete ctx;
+                               }
+                               contextMap.erase(ccHandle);
+                       }
+                       break;
+        default:
+            CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);   // unexpected event code
+    }
+}
+
+
+//
+// Defaults for methods you *should* implement.
+// If you don't, they'll throw UNIMPLEMENTED.
+//
+void CSPFullPluginSession::getKeySize(const CssmKey &key, CSSM_KEY_SIZE &size)
+{ unimplemented(); }
+
+//
+// Encryption and decryption
+//
+void CSPFullPluginSession::EncryptData(CSSM_CC_HANDLE ccHandle,
+                                       const Context &context,
+                                       const CssmData clearBufs[],
+                                       uint32 clearBufCount,
+                                       CssmData cipherBufs[],
+                                       uint32 cipherBufCount,
+                                       CSSM_SIZE &bytesEncrypted,
+                                       CssmData &remData,
+                                       CSSM_PRIVILEGE privilege)
+{
+    Writer writer(cipherBufs, cipherBufCount, &remData);
+    CSPContext *ctx = init(ccHandle, CSSM_ALGCLASS_CRYPT, context, true);
+    size_t outNeeded = ctx->outputSize(true, totalBufferSize(clearBufs, clearBufCount));
+    writer.allocate(outNeeded, *this);
+    ctx->update(clearBufs, clearBufCount, writer);
+    ctx->final(writer, *this);
+    bytesEncrypted = writer.close();
+}
+
+void CSPFullPluginSession::EncryptDataInit(CSSM_CC_HANDLE ccHandle,
+                             const Context &context,
+                             CSSM_PRIVILEGE Privilege)
+{
+    init(ccHandle, CSSM_ALGCLASS_CRYPT, context, true);
+}
+
+void CSPFullPluginSession::EncryptDataUpdate(CSSM_CC_HANDLE ccHandle,
+                                         const CssmData clearBufs[],
+                                         uint32 clearBufCount,
+                                         CssmData cipherBufs[],
+                                         uint32 cipherBufCount,
+                                         CSSM_SIZE &bytesEncrypted)
+{
+    CSPContext *alg = getStagedContext(ccHandle, CSSM_ALGCLASS_CRYPT, true);
+    Writer writer(cipherBufs, cipherBufCount);
+    size_t outNeeded = alg->outputSize(false, totalBufferSize(clearBufs, clearBufCount));
+    writer.allocate(outNeeded, *this);
+    alg->update(clearBufs, clearBufCount, writer);
+    bytesEncrypted = writer.close();
+}
+
+void CSPFullPluginSession::EncryptDataFinal(CSSM_CC_HANDLE ccHandle,
+                              CssmData &remData)
+{
+    getStagedContext(ccHandle, CSSM_ALGCLASS_CRYPT, true)->final(remData, *this);
+}
+
+
+void CSPFullPluginSession::DecryptData(CSSM_CC_HANDLE ccHandle,
+                                   const Context &context,
+                                   const CssmData cipherBufs[],
+                                   uint32 cipherBufCount,
+                                   CssmData clearBufs[],
+                                   uint32 clearBufCount,
+                                   CSSM_SIZE &bytesDecrypted,
+                                   CssmData &remData,
+                                   CSSM_PRIVILEGE privilege)
+{
+    Writer writer(clearBufs, clearBufCount, &remData);
+    CSPContext *ctx = init(ccHandle, CSSM_ALGCLASS_CRYPT, context, false);
+    size_t outNeeded = ctx->outputSize(true, totalBufferSize(cipherBufs, cipherBufCount));
+    writer.allocate(outNeeded, *this);
+    ctx->update(cipherBufs, cipherBufCount, writer);
+    ctx->final(writer, *this);
+    bytesDecrypted = writer.close();
+}
+
+void CSPFullPluginSession::DecryptDataInit(CSSM_CC_HANDLE ccHandle,
+                             const Context &context,
+                             CSSM_PRIVILEGE Privilege)
+{
+    init(ccHandle, CSSM_ALGCLASS_CRYPT, context, false);
+}
+
+void CSPFullPluginSession::DecryptDataUpdate(CSSM_CC_HANDLE ccHandle,
+                               const CssmData cipherBufs[],
+                               uint32 cipherBufCount,
+                               CssmData clearBufs[],
+                               uint32 clearBufCount,
+                               CSSM_SIZE &bytesDecrypted)
+{
+    CSPContext *ctx = getStagedContext(ccHandle, CSSM_ALGCLASS_CRYPT, false);
+    Writer writer(clearBufs, clearBufCount);
+    size_t outNeeded = ctx->outputSize(false, totalBufferSize(cipherBufs, cipherBufCount));
+    writer.allocate(outNeeded, *this);
+    ctx->update(cipherBufs, cipherBufCount, writer);
+    bytesDecrypted = writer.close();
+}
+
+void CSPFullPluginSession::DecryptDataFinal(CSSM_CC_HANDLE ccHandle,
+                                      CssmData &remData)
+{
+    getStagedContext(ccHandle, CSSM_ALGCLASS_CRYPT, false)->final(remData, *this);
+}
+               
+void CSPFullPluginSession::QuerySize(CSSM_CC_HANDLE ccHandle,
+                                                                        const Context &context,
+                                                                        CSSM_BOOL encrypt,
+                                                                        uint32 querySizeCount,
+                                                                        QuerySizeData *dataBlock)
+{
+       if (querySizeCount == 0)
+               return; // nothing ventured, nothing gained
+       CSPContext *ctx = getContext<CSPContext>(ccHandle);     // existing context?
+       if (ctx == NULL)        // force internal context creation (as best we can)
+               ctx = init(ccHandle, context.type(), context, encrypt);
+       // If QuerySizeCount > 1, we assume this inquires about a staged
+       // operation, and the LAST item gets the 'final' treatment.
+       //@@@ Intel revised algspec says "use the staged flag" -- TBD
+       for (uint32 n = 0; n < querySizeCount; n++) {
+               // the outputSize() call might throw CSSMERR_CSP_QUERY_SIZE_UNKNOWN
+               dataBlock[n].SizeOutputBlock =
+                       (uint32)ctx->outputSize(n == querySizeCount-1, dataBlock[n].inputSize());
+       }
+       //@@@ if we forced a context creation, should we discard it now?
+}
+
+
+//
+// Key wrapping and unwrapping.
+//
+void CSPFullPluginSession::WrapKey(CSSM_CC_HANDLE CCHandle,
+                                                               const Context &Context,
+                                                               const AccessCredentials &AccessCred,
+                                                               const CssmKey &Key,
+                                                               const CssmData *DescriptiveData,
+                                                               CssmKey &WrappedKey,
+                                                               CSSM_PRIVILEGE Privilege)
+{
+       unimplemented();
+}
+
+void CSPFullPluginSession::UnwrapKey(CSSM_CC_HANDLE CCHandle,
+                                                               const Context &Context,
+                                                               const CssmKey *PublicKey,
+                                                               const CssmKey &WrappedKey,
+                                                               uint32 KeyUsage,
+                                                               uint32 KeyAttr,
+                                                               const CssmData *KeyLabel,
+                                                               const CSSM_RESOURCE_CONTROL_CONTEXT *CredAndAclEntry,
+                                                               CssmKey &UnwrappedKey,
+                                                               CssmData &DescriptiveData,
+                                                               CSSM_PRIVILEGE Privilege)
+{
+       unimplemented();
+}
+
+void CSPFullPluginSession::DeriveKey(CSSM_CC_HANDLE CCHandle,
+                                                       const Context &Context,
+                                                       CssmData &Param,
+                                                       uint32 KeyUsage,
+                                                       uint32 KeyAttr,
+                                                       const CssmData *KeyLabel,
+                                                       const CSSM_RESOURCE_CONTROL_CONTEXT *CredAndAclEntry,
+                                                       CssmKey &DerivedKey)
+{
+       unimplemented();
+}
+
+
+//
+// Message Authentication Codes.
+// Almost like signatures (signatures with symmetric keys), though the
+// underlying implementation may be somewhat different.
+//
+void CSPFullPluginSession::GenerateMac(CSSM_CC_HANDLE ccHandle,
+                                   const Context &context,
+                                   const CssmData dataBufs[],
+                                   uint32 dataBufCount,
+                                   CssmData &mac)
+{
+    GenerateMacInit(ccHandle, context);
+    GenerateMacUpdate(ccHandle, dataBufs, dataBufCount);
+    GenerateMacFinal(ccHandle, mac);
+}
+
+void CSPFullPluginSession::GenerateMacInit(CSSM_CC_HANDLE ccHandle,
+                                           const Context &context)
+{
+    init(ccHandle, CSSM_ALGCLASS_MAC, context, true);
+}
+
+void CSPFullPluginSession::GenerateMacUpdate(CSSM_CC_HANDLE ccHandle,
+                                             const CssmData dataBufs[],
+                                             uint32 dataBufCount)
+{
+    getStagedContext(ccHandle, CSSM_ALGCLASS_MAC, true)->update(dataBufs, dataBufCount);
+}
+
+void CSPFullPluginSession::GenerateMacFinal(CSSM_CC_HANDLE ccHandle,
+                                            CssmData &mac)
+{
+    getStagedContext(ccHandle, CSSM_ALGCLASS_MAC, true)->final(mac, *this);
+}
+
+void CSPFullPluginSession::VerifyMac(CSSM_CC_HANDLE ccHandle,
+                                     const Context &context,
+                                     const CssmData dataBufs[],
+                                     uint32 dataBufCount,
+                                     const CssmData &mac)
+{
+    VerifyMacInit(ccHandle, context);
+    VerifyMacUpdate(ccHandle, dataBufs, dataBufCount);
+    VerifyMacFinal(ccHandle, mac);
+}
+
+void CSPFullPluginSession::VerifyMacInit(CSSM_CC_HANDLE ccHandle,
+                                         const Context &context)
+{
+    init(ccHandle, CSSM_ALGCLASS_MAC, context, false);
+}
+
+void CSPFullPluginSession::VerifyMacUpdate(CSSM_CC_HANDLE ccHandle,
+                                           const CssmData dataBufs[],
+                                           uint32 dataBufCount)
+{
+    getStagedContext(ccHandle, CSSM_ALGCLASS_MAC, false)->update(dataBufs, dataBufCount);
+}
+
+void CSPFullPluginSession::VerifyMacFinal(CSSM_CC_HANDLE ccHandle,
+                                          const CssmData &mac)
+{
+    getStagedContext(ccHandle, CSSM_ALGCLASS_MAC, false)->final(mac);
+}
+
+
+//
+// Signatures
+//
+void CSPFullPluginSession::SignData(CSSM_CC_HANDLE ccHandle,
+                                const Context &context,
+                                const CssmData dataBufs[],
+                                uint32 dataBufCount,
+                                CSSM_ALGORITHMS digestAlgorithm,
+                                CssmData &Signature)
+{
+       SignDataInit(ccHandle, context);
+       if(digestAlgorithm != CSSM_ALGID_NONE) {
+               getStagedContext(ccHandle, CSSM_ALGCLASS_SIGNATURE, 
+                       true)->setDigestAlgorithm(digestAlgorithm);
+       }
+       SignDataUpdate(ccHandle, dataBufs, dataBufCount);
+       SignDataFinal(ccHandle, Signature);
+}
+
+void CSPFullPluginSession::SignDataInit(CSSM_CC_HANDLE ccHandle,
+                                    const Context &context)
+{
+    init(ccHandle, CSSM_ALGCLASS_SIGNATURE, context, true);
+}
+
+void CSPFullPluginSession::SignDataUpdate(CSSM_CC_HANDLE ccHandle,
+                    const CssmData dataBufs[],
+                    uint32 dataBufCount)
+{
+    getStagedContext(ccHandle, CSSM_ALGCLASS_SIGNATURE, true)->update(dataBufs, dataBufCount);
+}
+
+void CSPFullPluginSession::SignDataFinal(CSSM_CC_HANDLE ccHandle,
+                           CssmData &signature)
+{
+    getStagedContext(ccHandle, CSSM_ALGCLASS_SIGNATURE, true)->final(signature, *this);
+}
+
+
+void CSPFullPluginSession::VerifyData(CSSM_CC_HANDLE ccHandle,
+                                  const Context &context,
+                                  const CssmData dataBufs[],
+                                  uint32 dataBufCount,
+                                  CSSM_ALGORITHMS digestAlgorithm,
+                                  const CssmData &Signature)
+{
+       VerifyDataInit(ccHandle, context);
+       if(digestAlgorithm != CSSM_ALGID_NONE) {
+               getStagedContext(ccHandle, CSSM_ALGCLASS_SIGNATURE, 
+                       false)->setDigestAlgorithm(digestAlgorithm);
+       }
+       VerifyDataUpdate(ccHandle, dataBufs, dataBufCount);
+       VerifyDataFinal(ccHandle, Signature);
+}
+
+void CSPFullPluginSession::VerifyDataInit(CSSM_CC_HANDLE ccHandle, const Context &context)
+{
+    init(ccHandle, CSSM_ALGCLASS_SIGNATURE, context, false);
+}
+
+void CSPFullPluginSession::VerifyDataUpdate(CSSM_CC_HANDLE ccHandle,
+                                        const CssmData dataBufs[],
+                                        uint32 dataBufCount)
+{
+    getStagedContext(ccHandle, CSSM_ALGCLASS_SIGNATURE, false)->update(dataBufs, dataBufCount);
+}
+
+void CSPFullPluginSession::VerifyDataFinal(CSSM_CC_HANDLE ccHandle,
+                     const CssmData &signature)
+{
+    getStagedContext(ccHandle, CSSM_ALGCLASS_SIGNATURE, false)->final(signature);
+}
+
+
+//
+// Digesting
+//
+void CSPFullPluginSession::DigestData(CSSM_CC_HANDLE ccHandle,
+               const Context &context,
+               const CssmData dataBufs[],
+               uint32 DataBufCount,
+               CssmData &Digest)
+{
+    DigestDataInit(ccHandle, context);
+    DigestDataUpdate(ccHandle, dataBufs, DataBufCount);
+    DigestDataFinal(ccHandle, Digest);
+}
+
+void CSPFullPluginSession::DigestDataInit(CSSM_CC_HANDLE ccHandle, const Context &context)
+{
+    init(ccHandle, CSSM_ALGCLASS_DIGEST, context);
+}
+
+void CSPFullPluginSession::DigestDataUpdate(CSSM_CC_HANDLE ccHandle,
+                              const CssmData dataBufs[],
+                              uint32 dataBufCount)
+{
+    getStagedContext(ccHandle, CSSM_ALGCLASS_DIGEST)->update(dataBufs, dataBufCount);
+}
+
+void CSPFullPluginSession::DigestDataFinal(CSSM_CC_HANDLE ccHandle,
+                                           CssmData &digest)
+{
+    getStagedContext(ccHandle, CSSM_ALGCLASS_DIGEST)->final(digest, *this);
+}
+
+void CSPFullPluginSession::DigestDataClone(CSSM_CC_HANDLE ccHandle,
+                                           CSSM_CC_HANDLE clonedCCHandle)
+{
+    CSPContext *cloned = getStagedContext(ccHandle, CSSM_ALGCLASS_DIGEST)->clone(*this);
+    cloned->mDirection = true;
+    cloned->mType = CSSM_ALGCLASS_DIGEST;
+    setContext(clonedCCHandle, cloned);
+}
+
+
+//
+// Key generation, Derivation, and inquiry
+//
+void CSPFullPluginSession::GenerateKey(CSSM_CC_HANDLE ccHandle,
+                         const Context &context,
+                         uint32 keyUsage,
+                         uint32 keyAttr,
+                         const CssmData *keyLabel,
+                         const CSSM_RESOURCE_CONTROL_CONTEXT *credAndAclEntry,
+                         CssmKey &key,
+                         CSSM_PRIVILEGE privilege)
+{
+    CSPContext *alg = init(ccHandle, CSSM_ALGCLASS_KEYGEN, context);
+    setKey(key, context, CSSM_KEYCLASS_SESSION_KEY, keyAttr, keyUsage);
+    CssmKey blank;             // dummy 2nd key (not used)
+    alg->generate(context, key, blank);
+}
+
+class ContextMinder
+{
+private:
+       CSSM_CC_HANDLE mHandle;
+
+public:
+       ContextMinder(CSSM_CC_HANDLE ccHandle) : mHandle(ccHandle) {}
+       ~ContextMinder() {CSSM_DeleteContext(mHandle);}
+};
+
+
+
+void CSPFullPluginSession::GenerateKeyPair(CSSM_CC_HANDLE ccHandle,
+                             const Context &context,
+                             uint32 publicKeyUsage,
+                             uint32 publicKeyAttr,
+                             const CssmData *publicKeyLabel,
+                             CssmKey &publicKey,
+                             uint32 privateKeyUsage,
+                             uint32 privateKeyAttr,
+                             const CssmData *privateKeyLabel,
+                             const CSSM_RESOURCE_CONTROL_CONTEXT *credAndAclEntry,
+                             CssmKey &privateKey,
+                             CSSM_PRIVILEGE privilege)
+{
+    CSPContext *alg = init(ccHandle, CSSM_ALGCLASS_KEYGEN, context);
+    
+    setKey(publicKey, context, CSSM_KEYCLASS_PUBLIC_KEY, publicKeyAttr, publicKeyUsage);
+    setKey(privateKey, context, CSSM_KEYCLASS_PRIVATE_KEY, privateKeyAttr, privateKeyUsage);
+    alg->generate(context, publicKey, privateKey);
+       
+    //@@@ handle labels
+    //@@@ handle reference keys
+
+       bool encryptPublic = publicKeyUsage & CSSM_KEYUSE_ENCRYPT;
+       bool encryptPrivate = privateKeyUsage & CSSM_KEYUSE_ENCRYPT;
+
+       if (!(encryptPublic || encryptPrivate))
+       {
+               return ;
+       }
+       
+       // time to do the FIPS required test!
+       CSSM_CSP_HANDLE moduleHandle = handle();
+       CSSM_CC_HANDLE encryptHandle;
+       CSSM_ACCESS_CREDENTIALS nullCreds;
+       memset(&nullCreds, 0, sizeof(nullCreds));
+
+       CSSM_KEY_PTR encryptingKey, decryptingKey;
+       if (encryptPublic)
+       {
+               encryptingKey = &publicKey;
+               decryptingKey = &privateKey;
+       }
+       else
+       {
+               encryptingKey = &privateKey;
+               decryptingKey = &publicKey;
+       }
+       
+       // make data to be encrypted
+       unsigned bytesInKey = encryptingKey->KeyHeader.LogicalKeySizeInBits / 8;
+       u_int8_t buffer[bytesInKey];
+       unsigned i;
+       
+       for (i = 0; i < bytesInKey; ++i)
+       {
+               buffer[i] = i;
+       }
+       
+       CSSM_DATA clearBuf = {bytesInKey, buffer};
+       CSSM_DATA cipherBuf; // have the CSP allocate the resulting memory
+       CSSM_SIZE bytesEncrypted;
+       CSSM_DATA remData = {0, NULL};
+       CSSM_DATA decryptedBuf = {bytesInKey, buffer};
+       
+       CSSM_RETURN result = CSSM_CSP_CreateAsymmetricContext(moduleHandle, encryptingKey->KeyHeader.AlgorithmId,  &nullCreds, encryptingKey, CSSM_PADDING_NONE, &encryptHandle);
+       if (result != CSSM_OK)
+       {
+               CssmError::throwMe(result);
+       }
+       
+       ContextMinder encryptMinder(encryptHandle); // auto throw away if we error out
+       
+       CSSM_QUERY_SIZE_DATA qsData;
+       qsData.SizeInputBlock = bytesInKey;
+       result = CSSM_QuerySize(encryptHandle, CSSM_TRUE, 1, &qsData);
+       if (result == CSSMERR_CSP_INVALID_ALGORITHM)
+       {
+               return;
+       }
+       
+       uint8 cipherBuffer[qsData.SizeOutputBlock];
+       cipherBuf.Length = qsData.SizeOutputBlock;
+       cipherBuf.Data = cipherBuffer;
+
+       // do the encryption
+       result = CSSM_EncryptData(encryptHandle, &clearBuf, 1, &cipherBuf, 1, &bytesEncrypted, &remData);
+       if (result != CSSM_OK)
+       {
+               CssmError::throwMe(result);
+       }
+       
+       // check the result
+       if (memcmp(cipherBuf.Data, clearBuf.Data, clearBuf.Length) == 0)
+       {
+               // we have a match, that's not good news...
+               abort();
+       }
+       
+       // clean up
+       if (remData.Data != NULL)
+       {
+               free(remData.Data);
+       }
+       
+       // make a context to perform the decryption
+       CSSM_CC_HANDLE decryptHandle;
+       result = CSSM_CSP_CreateAsymmetricContext(moduleHandle, encryptingKey->KeyHeader.AlgorithmId, &nullCreds, decryptingKey, CSSM_PADDING_NONE, &decryptHandle);
+       ContextMinder decryptMinder(decryptHandle);
+
+       if (result != CSSM_OK)
+       {
+               CssmError::throwMe(result);
+       }
+       
+       result = CSSM_DecryptData(decryptHandle, &cipherBuf, 1, &decryptedBuf, 1, &bytesEncrypted, &remData);
+       if (result != CSSM_OK)
+       {
+               CssmError::throwMe(result);
+       }
+       
+       // check the results
+       for (i = 0; i < bytesInKey; ++i)
+       {
+               if (decryptedBuf.Data[i] != (i & 0xFF))
+               {
+                       // bad news
+                       abort();
+               }
+       }
+       
+       if (remData.Data != NULL)
+       {
+               free(remData.Data);
+       }
+}
+
+void CSPFullPluginSession::ObtainPrivateKeyFromPublicKey(const CssmKey &PublicKey,
+                                                                                                                CssmKey &PrivateKey)
+{
+       unimplemented();
+}
+
+void CSPFullPluginSession::QueryKeySizeInBits(CSSM_CC_HANDLE ccHandle,
+                                               const Context *context,
+                                               const CssmKey *key,
+                                               CSSM_KEY_SIZE &keySize)
+{
+       if (context) {
+               getKeySize(context->get<CssmKey>(CSSM_ATTRIBUTE_KEY, CSSMERR_CSP_MISSING_ATTR_KEY),
+                       keySize);
+       } else {
+               getKeySize(CssmKey::required(key), keySize);
+       }
+}
+
+
+//
+// Free a key object.
+//
+void CSPFullPluginSession::FreeKey(const AccessCredentials *AccessCred,
+                                                                  CssmKey &key,
+                                                                  CSSM_BOOL Delete)
+{
+       free(key.data());
+}
+
+
+//
+// Random number and parameter generation
+//
+void CSPFullPluginSession::GenerateRandom(CSSM_CC_HANDLE ccHandle,
+                                          const Context &context,
+                                          CssmData &randomNumber)
+{
+    init(ccHandle, CSSM_ALGCLASS_RANDOMGEN, context)->final(randomNumber, *this);
+}
+
+void CSPFullPluginSession::GenerateAlgorithmParams(CSSM_CC_HANDLE ccHandle,
+                                    const Context &context,
+                                    uint32 paramBits,
+                                    CssmData &param,
+                                    uint32 &attrCount,
+                                    CSSM_CONTEXT_ATTRIBUTE_PTR &attrs)
+{
+    Context::Attr *attrList;
+    init(ccHandle, CSSM_ALGCLASS_NONE, context)->generate(context, paramBits,
+                                                          param, attrCount, attrList);
+    attrs = attrList;
+}
+
+
+//
+// Login/Logout and token operational maintainance.
+// These mean little without support by the actual implementation, but we can help...
+// @@@ Should this be in CSP[non-Full]PluginSession?
+//
+void CSPFullPluginSession::Login(const AccessCredentials &AccessCred,
+                       const CssmData *LoginName,
+                       const void *Reserved)
+{
+       if (Reserved != NULL)
+               CssmError::throwMe(CSSM_ERRCODE_INVALID_POINTER);
+               
+       // default implementation refuses to log in
+       //@@@ should hand it to implementation virtual defaulting to this
+       CssmError::throwMe(CSSMERR_CSP_INVALID_LOGIN_NAME);
+}
+
+void CSPFullPluginSession::Logout()
+{
+       if (!loggedIn(false))
+               CssmError::throwMe(CSSMERR_CSP_NOT_LOGGED_IN);
+}
+
+void CSPFullPluginSession::VerifyDevice(const CssmData &DeviceCert)
+{
+       CssmError::throwMe(CSSMERR_CSP_DEVICE_VERIFY_FAILED);
+}
+
+void CSPFullPluginSession::GetOperationalStatistics(CSPOperationalStatistics &statistics)
+{
+       memset(&statistics, 0, sizeof(statistics));
+       statistics.UserAuthenticated = loggedIn();
+       //@@@ collect device flags - capability matrix setup?
+       //@@@ collect token limitation parameters (static) - capability matrix setup?
+       //@@@ collect token statistics (dynamic) - dynamic accounting call-downs?
+}
+
+
+//
+// Utterly miscellaneous, rarely used, strange functions
+//
+void CSPFullPluginSession::RetrieveCounter(CssmData &Counter)
+{
+       unimplemented();
+}
+
+void CSPFullPluginSession::RetrieveUniqueId(CssmData &UniqueID)
+{
+       unimplemented();
+}
+
+void CSPFullPluginSession::GetTimeValue(CSSM_ALGORITHMS TimeAlgorithm, CssmData &TimeData)
+{
+       unimplemented();
+}
+
+
+//
+// ACL retrieval and change operations
+//
+void CSPFullPluginSession::GetKeyOwner(const CssmKey &Key,
+               CSSM_ACL_OWNER_PROTOTYPE &Owner)
+{
+       unimplemented();
+}
+
+void CSPFullPluginSession::ChangeKeyOwner(const AccessCredentials &AccessCred,
+               const CssmKey &Key,
+               const CSSM_ACL_OWNER_PROTOTYPE &NewOwner)
+{
+       unimplemented();
+}
+
+void CSPFullPluginSession::GetKeyAcl(const CssmKey &Key,
+               const CSSM_STRING *SelectionTag,
+               uint32 &NumberOfAclInfos,
+               CSSM_ACL_ENTRY_INFO_PTR &AclInfos)
+{
+       unimplemented();
+}
+
+void CSPFullPluginSession::ChangeKeyAcl(const AccessCredentials &AccessCred,
+               const CSSM_ACL_EDIT &AclEdit,
+               const CssmKey &Key)
+{
+       unimplemented();
+}
+
+void CSPFullPluginSession::GetLoginOwner(CSSM_ACL_OWNER_PROTOTYPE &Owner)
+{
+       unimplemented();
+}
+
+void CSPFullPluginSession::ChangeLoginOwner(const AccessCredentials &AccessCred,
+               const CSSM_ACL_OWNER_PROTOTYPE &NewOwner)
+{
+       unimplemented();
+}
+
+void CSPFullPluginSession::GetLoginAcl(const CSSM_STRING *SelectionTag,
+               uint32 &NumberOfAclInfos,
+               CSSM_ACL_ENTRY_INFO_PTR &AclInfos)
+{
+       unimplemented();
+}
+
+void CSPFullPluginSession::ChangeLoginAcl(const AccessCredentials &AccessCred,
+               const CSSM_ACL_EDIT &AclEdit)
+{
+       unimplemented();
+}
+
+
+
+//
+// Passthroughs (by default, unimplemented)
+//
+void CSPFullPluginSession::PassThrough(CSSM_CC_HANDLE CCHandle,
+                                                                               const Context &Context,
+                                                                               uint32 PassThroughId,
+                                                                               const void *InData,
+                                                                               void **OutData)
+{
+       unimplemented();
+}
+
+
+//
+// KeyPool -- ReferencedKey management functionality
+//
+KeyPool::KeyPool()
+{
+}
+
+KeyPool::~KeyPool()
+{
+       StLock<Mutex> _(mKeyMapLock);
+       // Delete every ReferencedKey in the pool, but be careful to deactivate them first
+       // to keep them from calling erase (which would cause deadlock since we already hold mKeyMapLock).
+       KeyMap::iterator end = mKeyMap.end();
+       for (KeyMap::iterator it = mKeyMap.begin(); it != end; ++it)
+       {
+               try
+               {
+                       it->second->deactivate();
+               }
+               catch(...) {}
+               delete it->second;
+       }
+       mKeyMap.clear();
+}
+
+void
+KeyPool::add(ReferencedKey &referencedKey)
+{
+       StLock<Mutex> _(mKeyMapLock);
+       bool inserted;
+    inserted = mKeyMap.insert(KeyMap::value_type(referencedKey.keyReference(), &referencedKey)).second;
+       // Since add is only called from the constructor of ReferencedKey we should
+       // never add a key that is already in mKeyMap
+       assert(inserted);
+}
+
+ReferencedKey &
+KeyPool::findKey(const CSSM_KEY &key) const
+{
+       return findKeyReference(ReferencedKey::keyReference(key));
+}
+
+ReferencedKey &
+KeyPool::findKeyReference(ReferencedKey::KeyReference keyReference) const
+{
+       StLock<Mutex> _(mKeyMapLock);
+       KeyMap::const_iterator it = mKeyMap.find(keyReference);
+       if (it == mKeyMap.end())
+               CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE);
+
+       return *it->second;
+}
+
+void
+KeyPool::erase(ReferencedKey &referencedKey)
+{
+       erase(referencedKey.keyReference());
+}
+
+ReferencedKey &
+KeyPool::erase(ReferencedKey::KeyReference keyReference)
+{
+       StLock<Mutex> _(mKeyMapLock);
+       KeyMap::iterator it = mKeyMap.find(keyReference);
+       if (it == mKeyMap.end())
+               CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE);
+
+       ReferencedKey &referencedKey = *it->second;
+       mKeyMap.erase(it);
+       return referencedKey;
+}
+
+// Erase keyReference from mKeyMap, free the ioKey, and delete the ReferencedKey
+void
+KeyPool::freeKey(Allocator &allocator, CSSM_KEY &ioKey)
+{
+       delete &erase(ReferencedKey::freeReferenceKey(allocator, ioKey));
+}
+
+//
+// ReferencedKey class
+//
+ReferencedKey::ReferencedKey(KeyPool &keyPool) : mKeyPool(&keyPool)
+{
+       mKeyPool->add(*this);
+}
+
+ReferencedKey::~ReferencedKey()
+{
+       if (isActive())
+               mKeyPool->erase(*this);
+}
+
+ReferencedKey::KeyReference
+ReferencedKey::keyReference()
+{
+       // @@@ Possibly check isActive() and return an invalid reference if it is not set.
+       return reinterpret_cast<ReferencedKey::KeyReference>(this);
+}
+
+//
+// Making, retrieving and freeing Key references of CssmKeys
+//
+void
+ReferencedKey::makeReferenceKey(Allocator &allocator, KeyReference keyReference, CSSM_KEY &key)
+{
+       key.KeyHeader.BlobType = CSSM_KEYBLOB_REFERENCE;
+       key.KeyHeader.Format = CSSM_KEYBLOB_REF_FORMAT_INTEGER;
+       key.KeyData.Length = sizeof(KeyReference);
+       key.KeyData.Data = allocator.alloc<uint8>(sizeof(KeyReference));
+       uint8 *cp = key.KeyData.Data;
+       for (int i = sizeof(KeyReference); --i >= 0;)
+       {
+               cp[i] = keyReference & 0xff;
+               keyReference = keyReference >> 8;
+       }
+}
+
+ReferencedKey::KeyReference
+ReferencedKey::keyReference(const CSSM_KEY &key)
+{
+       if (key.KeyHeader.BlobType != CSSM_KEYBLOB_REFERENCE
+               || key.KeyHeader.Format != CSSM_KEYBLOB_REF_FORMAT_INTEGER
+               || key.KeyData.Length != sizeof(KeyReference)
+               || key.KeyData.Data == NULL)
+               CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE);
+
+       const uint8 *cp = key.KeyData.Data;
+       KeyReference keyReference = 0;
+       for (uint32 i = 0; i < sizeof(KeyReference); ++i)
+               keyReference = (keyReference << 8) + cp[i];
+
+       return keyReference;
+}
+
+ReferencedKey::KeyReference
+ReferencedKey::freeReferenceKey(Allocator &allocator, CSSM_KEY &key)
+{
+       KeyReference aKeyReference = keyReference(key);
+       allocator.free(key.KeyData.Data);
+       key.KeyData.Data = NULL;
+       key.KeyData.Length = 0;
+       return aKeyReference;
+}