/* * 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. */ // // CSPsession - Plugin framework for CSP plugin modules // #include #include #include 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(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(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 ¶ms, 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 _(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 _(contextMapLock); contextUpdate(ccHandle, context, contextMap[ccHandle]); } break; case CSSM_CONTEXT_EVENT_DELETE: { StLock _(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(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(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 ¶m, 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 _(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 _(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 _(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 _(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(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(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; }