]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_sd_cspdl/lib/SDContext.cpp
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / libsecurity_sd_cspdl / lib / SDContext.cpp
diff --git a/OSX/libsecurity_sd_cspdl/lib/SDContext.cpp b/OSX/libsecurity_sd_cspdl/lib/SDContext.cpp
new file mode 100644 (file)
index 0000000..27881a9
--- /dev/null
@@ -0,0 +1,604 @@
+/*
+ * Copyright (c) 2004,2011-2012,2014 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The 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.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+//
+// SDContext - cryptographic contexts for the security server
+//
+#include "SDContext.h"
+
+#include "SDCSPSession.h"
+#include "SDKey.h"
+#include <security_utilities/debugging.h>
+
+#define ssCryptDebug(args...)  secdebug("ssCrypt", ## args)
+
+using namespace SecurityServer;
+
+//
+// SDContext
+//
+SDContext::SDContext(SDCSPSession &session)
+: mSession(session), mContext(NULL)
+{
+}
+
+void SDContext::clearOutBuf()
+{
+       if(mOutBuf.Data) {
+               mSession.free(mOutBuf.Data);
+               mOutBuf.clear();
+       }
+}
+
+void SDContext::copyOutBuf(CssmData &out)
+{
+       if(out.length() < mOutBuf.length()) {
+               CssmError::throwMe(CSSMERR_CSP_OUTPUT_LENGTH_ERROR);
+       }
+       memmove(out.Data, mOutBuf.Data, mOutBuf.Length);
+       out.Length = mOutBuf.Length;
+       clearOutBuf();
+}
+
+void
+SDContext::init(const Context &context,
+                               bool /* encoding */) // @@@ should be removed from API since it's already in mDirection
+{
+       mContext = &context;
+       clearOutBuf();
+}
+
+SecurityServer::ClientSession &
+SDContext::clientSession()
+{
+       return mSession.clientSession();
+}
+
+
+//
+// SDRandomContext -- Context for GenerateRandom operations
+//
+SDRandomContext::SDRandomContext(SDCSPSession &session) : SDContext(session) {}
+
+void
+SDRandomContext::init(const Context &context, bool encoding)
+{
+       SDContext::init(context, encoding);
+
+       // set/freeze output size
+       mOutSize = context.getInt(CSSM_ATTRIBUTE_OUTPUT_SIZE, CSSMERR_CSP_MISSING_ATTR_OUTPUT_SIZE);
+
+#if 0
+       // seed the PRNG (if specified)
+       if (const CssmCryptoData *seed = context.get<CssmCryptoData>(CSSM_ATTRIBUTE_SEED)) {
+               const CssmData &seedValue = (*seed)();
+               clientSession().seedRandom(seedValue);
+       }
+#endif
+}
+
+size_t 
+SDRandomContext::outputSize(bool final, size_t inSize)
+{
+       return mOutSize;
+}
+
+void
+SDRandomContext::final(CssmData &out)
+{
+       clientSession().generateRandom(*mContext, out);
+}
+
+
+// signature contexts
+SDSignatureContext::SDSignatureContext(SDCSPSession &session) 
+       : SDContext(session),
+               mKeyHandle(noKey),
+               mNullDigest(NULL),
+               mDigest(NULL)
+{
+       /* nothing else for now */
+}
+
+SDSignatureContext::~SDSignatureContext()
+{
+       delete mNullDigest;
+       delete mDigest;
+}
+
+void SDSignatureContext::init(const Context &context, bool signing)
+{
+       SDContext::init(context, signing);
+
+       /* reusable: skip everything except resetting digest state */
+       if((mNullDigest != NULL) || (mDigest != NULL)) {
+               if(mNullDigest != NULL) {
+                       mNullDigest->digestInit();
+               }
+               return;
+       }
+       
+       /* snag key from context */
+       const CssmKey &keyInContext =
+               context.get<const CssmKey>(CSSM_ATTRIBUTE_KEY,
+                                                                  CSSMERR_CSP_MISSING_ATTR_KEY);
+       mKeyHandle = mSession.lookupKey(keyInContext).keyHandle();
+       
+       /* get digest alg and sig alg from Context.algorithm */
+       switch(context.algorithm()) {
+               /*** DSA ***/
+               case CSSM_ALGID_SHA1WithDSA:
+                       mDigestAlg = CSSM_ALGID_SHA1;
+                       mSigAlg = CSSM_ALGID_DSA;
+                       break;
+               case CSSM_ALGID_DSA:                            // Raw
+                       mDigestAlg = CSSM_ALGID_NONE;
+                       mSigAlg = CSSM_ALGID_DSA;
+                       break;
+               /*** RSA ***/
+               case CSSM_ALGID_SHA1WithRSA:
+                       mDigestAlg = CSSM_ALGID_SHA1;
+                       mSigAlg = CSSM_ALGID_RSA;
+                       break;
+               case CSSM_ALGID_MD5WithRSA:
+                       mDigestAlg = CSSM_ALGID_MD5;
+                       mSigAlg = CSSM_ALGID_RSA;
+                       break;
+               case CSSM_ALGID_MD2WithRSA:
+                       mDigestAlg = CSSM_ALGID_MD2;
+                       mSigAlg = CSSM_ALGID_RSA;
+                       break;
+               case CSSM_ALGID_RSA:                            // Raw
+                       mDigestAlg = CSSM_ALGID_NONE;
+                       mSigAlg = CSSM_ALGID_RSA;
+                       break;
+               /*** FEE ***/
+               case CSSM_ALGID_FEE_SHA1:
+                       mDigestAlg = CSSM_ALGID_SHA1;
+                       mSigAlg = CSSM_ALGID_FEE;
+                       break;
+               case CSSM_ALGID_FEE_MD5:
+                       mDigestAlg = CSSM_ALGID_MD5;
+                       mSigAlg = CSSM_ALGID_FEE;
+                       break;
+               case CSSM_ALGID_FEE:                            // Raw
+                       mDigestAlg = CSSM_ALGID_NONE;
+                       mSigAlg = CSSM_ALGID_FEE;
+                       break;
+               /*** ECDSA ***/
+               case CSSM_ALGID_SHA1WithECDSA:
+                       mDigestAlg = CSSM_ALGID_SHA1;
+                       mSigAlg = CSSM_ALGID_ECDSA;
+                       break;
+               case CSSM_ALGID_SHA224WithECDSA:
+                       mDigestAlg = CSSM_ALGID_SHA224;
+                       mSigAlg = CSSM_ALGID_ECDSA;
+                       break;
+               case CSSM_ALGID_SHA256WithECDSA:
+                       mDigestAlg = CSSM_ALGID_SHA256;
+                       mSigAlg = CSSM_ALGID_ECDSA;
+                       break;
+               case CSSM_ALGID_SHA384WithECDSA:
+                       mDigestAlg = CSSM_ALGID_SHA384;
+                       mSigAlg = CSSM_ALGID_ECDSA;
+                       break;
+               case CSSM_ALGID_SHA512WithECDSA:
+                       mDigestAlg = CSSM_ALGID_SHA512;
+                       mSigAlg = CSSM_ALGID_ECDSA;
+                       break;
+               case CSSM_ALGID_ECDSA:                          // Raw
+                       mDigestAlg = CSSM_ALGID_NONE;
+                       mSigAlg = CSSM_ALGID_ECDSA;
+                       break;
+               default:
+                       CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
+       }
+               
+       /* set up mNullDigest or mDigest */
+       if(mDigestAlg == CSSM_ALGID_NONE) {
+               mNullDigest = new NullDigest();
+       }
+       else {
+               mDigest = new CssmClient::Digest(mSession.mRawCsp, mDigestAlg);
+       }
+}
+
+/* 
+ * for raw sign/verify - optionally called after init.
+ * Note that in init (in this case), we set mDigestAlg to ALGID_NONE and set up
+ * a NullDigest. We now overwrite mDigestAlg, and we'll useÊthis
+ * new value when we do the actual sign/vfy.
+ */
+void SDSignatureContext::setDigestAlgorithm(CSSM_ALGORITHMS digestAlg)
+{
+       mDigestAlg = digestAlg;
+}
+
+void SDSignatureContext::update(const CssmData &data)
+{
+       /* Note that for this context, we really can not deal with an out-of-sequence
+        * update --> final(true, 0) --> update since we lose the pending digest state
+        * when we perform the implied final() during outputSize(true, 0). */
+       assert(mOutBuf.Data == NULL);
+       
+       /* add incoming data to digest or accumulator */
+       if(mNullDigest) {
+               mNullDigest->digestUpdate(data.data(), data.length());
+       }
+       else {
+               mDigest->digest(data);
+       }
+}
+
+size_t SDSignatureContext::outputSize(bool final, size_t inSize)
+{
+       if(!final) {
+               ssCryptDebug("===sig outputSize !final\n");
+               return 0;
+       }
+       if(!encoding()) {
+               ssCryptDebug("===sig outputSize final, !encoding\n");
+               /* don't see why this is even called... */
+               return 0;
+       }
+       if(inSize == 0) {
+               /* 
+                * This is the implied signal to go for it. Note that in this case,
+                * we can not go back and re-do the op in case of an unexpected
+                * sequence of update/outputSize(final, 0)/final - we lose the digest 
+                * state. Perhaps we should save the digest...? But still it would
+                * be impossible to do another update. 
+                */
+               clearOutBuf();
+               sign(mOutBuf);
+               ssCryptDebug("===sig outputSize(pre-op) %u", (unsigned)mOutBuf.Length);
+               return (size_t)mOutBuf.Length;
+       }
+       else {
+               /* out-of-band case, ask CSP via SS */
+               uint32 outSize = clientSession().getOutputSize(*mContext, 
+                       mKeyHandle, 
+                       /* FIXME - what to use for inSize here - we don't want to 
+                        * interrogate mDigest, as that would result in another RPC...
+                        * and signature size is not related to input size...right? */
+                       (uint32)inSize,
+                       true);
+               ssCryptDebug("===sig outputSize(RPC) %u", (unsigned)outSize);
+               return (size_t)outSize;
+       }
+}
+
+/* sign */
+
+/* first the common routine shared by final and outputSize */
+void SDSignatureContext::sign(CssmData &sig)
+{
+       /* we have to pass down a modified Context, thus.... */
+       Context tempContext = *mContext;
+       tempContext.AlgorithmType = mSigAlg;
+       
+       if(mNullDigest) {
+               CssmData dData(const_cast<void *>(mNullDigest->digestPtr()), 
+                       mNullDigest->digestSizeInBytes());      
+               clientSession().generateSignature(tempContext,
+                       mKeyHandle,
+                       dData, 
+                       sig,
+                       mDigestAlg);
+       }
+       else {
+               CssmAutoData d (mDigest->allocator ());
+                       d.set((*mDigest) ());
+                       
+                       clientSession().generateSignature(tempContext,
+                               mKeyHandle,
+                               d, 
+                               sig,
+                               mDigestAlg);
+       }
+}
+
+/* this is the one called by CSPFullPluginSession */
+void SDSignatureContext::final(CssmData &sig)
+{
+       if(mOutBuf.Data) {
+               /* normal final case in which the actual RPC via SS was done in the
+                * previous outputSize() call. */
+               ssCryptDebug("===final via pre-op and copy");
+               copyOutBuf(sig);
+               return;
+       }
+       
+       ssCryptDebug("===final via RPC");
+       sign(sig);
+}
+
+/* verify */
+void
+SDSignatureContext::final(const CssmData &sig)
+{
+       /* we have to pass down a modified Context, thus.... */
+       Context tempContext = *mContext;
+       tempContext.AlgorithmType = mSigAlg;
+
+       if(mNullDigest) {
+               CssmData dData(const_cast<void *>(mNullDigest->digestPtr()), 
+                       mNullDigest->digestSizeInBytes());
+               clientSession().verifySignature(tempContext,
+                       mKeyHandle,
+                       dData, 
+                       sig,
+                       mDigestAlg);
+       }
+       else {
+               clientSession().verifySignature(tempContext,
+                       mKeyHandle,
+                       (*mDigest)(), 
+                       sig,
+                       mDigestAlg);
+       }
+}
+
+
+//
+// SDCryptContext -- Context for Encrypt and Decrypt operations
+//
+SDCryptContext::SDCryptContext(SDCSPSession &session)
+       : SDContext(session), mKeyHandle(noKey)
+{
+       /* nothing for now */
+}
+
+
+SDCryptContext::~SDCryptContext()
+{
+       /* nothing for now */
+}
+
+void
+SDCryptContext::init(const Context &context, bool encoding)
+{
+       ssCryptDebug("===init");
+       SDContext::init(context, encoding);
+
+       /* reusable; reset accumulator */
+       mNullDigest.digestInit();
+
+       const CssmKey &keyInContext =
+               context.get<const CssmKey>(CSSM_ATTRIBUTE_KEY,
+                                                                  CSSMERR_CSP_MISSING_ATTR_KEY);
+       mKeyHandle = mSession.lookupKey(keyInContext).keyHandle();
+}
+
+size_t
+SDCryptContext::inputSize(size_t outSize)
+{
+       ssCryptDebug("===inputSize  outSize=%u", (unsigned)outSize);
+       return UINT_MAX;
+}
+
+size_t
+SDCryptContext::outputSize(bool final, size_t inSize)
+{
+       ssCryptDebug("===outputSize final %d inSize=%u", final, (unsigned)inSize);
+       if(!final) {
+               /* we buffer until final; no intermediate output */
+               return 0;
+       }
+       size_t inBufSize = mNullDigest.digestSizeInBytes();
+       if(inSize == 0) {
+               /* This is the implied signal to go for it */
+               clearOutBuf();
+               if(inBufSize == 0) {
+                       return 0;
+               }
+               const CssmData in(const_cast<void *>(mNullDigest.digestPtr()), inBufSize);
+               if (encoding()) {
+                       clientSession().encrypt(*mContext, mKeyHandle, in, mOutBuf);
+               }
+               else {
+                       clientSession().decrypt(*mContext, mKeyHandle, in, mOutBuf);
+               }
+               /* leave the accumulator as is in case of unexpected sequence */
+               ssCryptDebug("   ===outSize(pre-op) %u", (unsigned)mOutBuf.Length);
+               return mOutBuf.Length;
+       }
+       else {
+               /* out-of-band case, ask CSP via SS */
+               uint32 outSize = clientSession().getOutputSize(*mContext, 
+                       mKeyHandle, 
+                       (uint32)(inBufSize + inSize),
+                       encoding());
+               ssCryptDebug("   ===outSize(RPC) %u", (unsigned)outSize);
+               return (size_t)outSize;
+       }
+}
+
+void
+SDCryptContext::minimumProgress(size_t &in, size_t &out)
+{
+       in = 1;
+       out = 0;
+}
+
+void
+SDCryptContext::update(void *inp, size_t &inSize, void *outp, size_t &outSize)
+{
+       ssCryptDebug("===update inSize=%u", (unsigned)inSize);
+       /* add incoming data to accumulator */
+       mNullDigest.digestUpdate(inp, inSize);
+       outSize = 0;
+       clearOutBuf();
+}
+
+void
+SDCryptContext::final(CssmData &out)
+{
+       if(mOutBuf.Data != NULL) {
+               /* normal final case in which the actual RPC via SS was done in the
+                * previous outputSize() call. A memcpy is needed here because 
+                * CSPFullPluginSession has just allocated the buf size we need. */
+               ssCryptDebug("===final via pre-op and copy");
+               copyOutBuf(out);
+               return;
+       }
+       
+       /* when is this path taken...? */
+       ssCryptDebug("===final via RPC");
+       size_t inSize = mNullDigest.digestSizeInBytes();
+       if(!inSize) return;
+
+       const CssmData in(const_cast<void *>(mNullDigest.digestPtr()), inSize);
+       IFDEBUG(size_t origOutSize = out.length());
+       if (encoding()) {
+               clientSession().encrypt(*mContext, mKeyHandle, in, out);
+       }
+       else {
+               clientSession().decrypt(*mContext, mKeyHandle, in, out);
+       }
+       assert(out.length() <= origOutSize);
+       mNullDigest.digestInit();
+}
+
+// Digest, using raw CSP
+SDDigestContext::SDDigestContext(SDCSPSession &session)
+       : SDContext(session), mDigest(NULL)
+{
+       
+}
+
+SDDigestContext::~SDDigestContext()
+{
+       delete mDigest;
+}
+
+void SDDigestContext::init(const Context &context, bool encoding)
+{
+       CSSM_ALGORITHMS alg;
+       
+       SDContext::init(context, encoding);
+       alg = context.algorithm();
+       mDigest = new CssmClient::Digest(mSession.mRawCsp, alg);
+}
+
+void SDDigestContext::update(const CssmData &data)
+{
+       mDigest->digest(data);
+}
+
+void SDDigestContext::final(CssmData &out)
+{
+       (*mDigest)(out);
+}
+
+size_t SDDigestContext::outputSize(bool final, size_t inSize)
+{
+       if(!final) {
+               return 0;
+       }
+       else {
+               return (size_t)mDigest->getOutputSize((uint32)inSize);
+       }
+}
+
+// MACContext - common class for MAC generate, verify
+SDMACContext::SDMACContext(SDCSPSession &session)
+       : SDContext(session), mKeyHandle(noKey)
+{
+
+}
+
+void SDMACContext::init(const Context &context, bool encoding)
+{
+       SDContext::init(context, encoding);
+       
+       /* reusable; reset accumulator */
+       mNullDigest.digestInit();
+       
+       /* snag key from context */
+       const CssmKey &keyInContext =
+               context.get<const CssmKey>(CSSM_ATTRIBUTE_KEY,
+                                                                  CSSMERR_CSP_MISSING_ATTR_KEY);
+       mKeyHandle = mSession.lookupKey(keyInContext).keyHandle();
+}
+
+void SDMACContext::update(const CssmData &data)
+{
+       /* add incoming data to accumulator */
+       mNullDigest.digestUpdate(data.data(), data.length());
+}
+
+size_t SDMACContext::outputSize(bool final, size_t inSize)
+{
+       if(!final) {
+               ssCryptDebug("===mac outputSize !final\n");
+               return 0;
+       }
+       if(!encoding()) {
+               ssCryptDebug("===mac outputSize final, !encoding\n");
+               /* don't see why this is even called... */
+               return 0;
+       }
+       if(inSize == 0) {
+               /* 
+                * This is the implied signal to go for it.  
+                */
+               clearOutBuf();
+               genMac(mOutBuf);
+               ssCryptDebug("===mac outputSize(pre-op) %u", (unsigned)mOutBuf.Length);
+               return (size_t)mOutBuf.Length;
+       }
+       else {
+               /* out-of-band case, ask CSP via SS */
+               uint32 outSize = clientSession().getOutputSize(*mContext, 
+                       mKeyHandle, 
+                       (uint32)(inSize + mNullDigest.digestSizeInBytes()),
+                       true);
+               ssCryptDebug("===mac outputSize(RPC) %u", (unsigned)outSize);
+               return (size_t)outSize;
+       }
+}
+
+/* generate */
+
+/* first the common routine used by final() and outputSize() */
+void SDMACContext::genMac(CssmData &mac)
+{
+       CssmData allData(const_cast<void *>(mNullDigest.digestPtr()), 
+               mNullDigest.digestSizeInBytes());
+       clientSession().generateMac(*mContext, mKeyHandle, allData, mac);
+}
+
+void SDMACContext::final(CssmData &mac)
+{
+       genMac(mac);
+}
+
+/* verify */
+void SDMACContext::final(const CssmData &mac)
+{
+       CssmData allData(const_cast<void *>(mNullDigest.digestPtr()), 
+               mNullDigest.digestSizeInBytes());
+       clientSession().verifyMac(*mContext, mKeyHandle, allData, mac);
+}