]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_apple_cspdl/lib/SSContext.cpp
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / libsecurity_apple_cspdl / lib / SSContext.cpp
diff --git a/OSX/libsecurity_apple_cspdl/lib/SSContext.cpp b/OSX/libsecurity_apple_cspdl/lib/SSContext.cpp
new file mode 100644 (file)
index 0000000..3d5de0a
--- /dev/null
@@ -0,0 +1,622 @@
+/*
+ * 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.
+ */
+
+
+//
+// SSContext - cryptographic contexts for the security server
+//
+#include "SSContext.h"
+
+#include "SSCSPSession.h"
+#include "SSKey.h"
+#include <security_utilities/debugging.h>
+
+#define ssCryptDebug(args...)  secdebug("ssCrypt", ## args)
+
+using namespace SecurityServer;
+
+//
+// SSContext
+//
+SSContext::SSContext(SSCSPSession &session)
+: mSession(session), mContext(NULL)
+{
+}
+
+void SSContext::clearOutBuf()
+{
+       if(mOutBuf.Data) {
+               mSession.free(mOutBuf.Data);
+               mOutBuf.clear();
+       }
+}
+
+void SSContext::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
+SSContext::init(const Context &context,
+                               bool /* encoding */) // @@@ should be removed from API since it's already in mDirection
+{
+       mContext = &context;
+       clearOutBuf();
+}
+
+SecurityServer::ClientSession &
+SSContext::clientSession()
+{
+       return mSession.clientSession();
+}
+
+
+//
+// SSRandomContext -- Context for GenerateRandom operations
+//
+SSRandomContext::SSRandomContext(SSCSPSession &session) : SSContext(session) {}
+
+void
+SSRandomContext::init(const Context &context, bool encoding)
+{
+       SSContext::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 
+SSRandomContext::outputSize(bool final, size_t inSize)
+{
+       return mOutSize;
+}
+
+void
+SSRandomContext::final(CssmData &out)
+{
+       clientSession().generateRandom(*mContext, out);
+}
+
+
+// signature contexts
+SSSignatureContext::SSSignatureContext(SSCSPSession &session) 
+       : SSContext(session),
+               mKeyHandle(noKey),
+               mNullDigest(NULL),
+               mDigest(NULL)
+{
+       /* nothing else for now */
+}
+
+SSSignatureContext::~SSSignatureContext()
+{
+       delete mNullDigest;
+       delete mDigest;
+}
+
+void SSSignatureContext::init(const Context &context, bool signing)
+{
+       SSContext::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_SHA256WithRSA:
+                       mDigestAlg = CSSM_ALGID_SHA256;
+                       mSigAlg = CSSM_ALGID_RSA;
+                       break;
+               case CSSM_ALGID_SHA224WithRSA:
+                       mDigestAlg = CSSM_ALGID_SHA224;
+                       mSigAlg = CSSM_ALGID_RSA;
+                       break;
+               case CSSM_ALGID_SHA384WithRSA:
+                       mDigestAlg = CSSM_ALGID_SHA384;
+                       mSigAlg = CSSM_ALGID_RSA;
+                       break;
+               case CSSM_ALGID_SHA512WithRSA:
+                       mDigestAlg = CSSM_ALGID_SHA512;
+                       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 SSSignatureContext::setDigestAlgorithm(CSSM_ALGORITHMS digestAlg)
+{
+       mDigestAlg = digestAlg;
+}
+
+void SSSignatureContext::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 SSSignatureContext::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 SSSignatureContext::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 SSSignatureContext::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
+SSSignatureContext::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 {
+               CssmData digst = (*mDigest)();
+               try {
+                       clientSession().verifySignature(tempContext,
+                               mKeyHandle,
+                               digst, 
+                               sig,
+                               mDigestAlg);
+               }
+               catch (...) {
+                       mDigest->allocator().free(digst.Data);
+                       throw;
+               }
+               mDigest->allocator().free(digst.Data);
+       }
+}
+
+
+//
+// SSCryptContext -- Context for Encrypt and Decrypt operations
+//
+SSCryptContext::SSCryptContext(SSCSPSession &session)
+       : SSContext(session), mKeyHandle(noKey)
+{
+       /* nothing for now */
+}
+
+
+SSCryptContext::~SSCryptContext()
+{
+       /* nothing for now */
+}
+
+void
+SSCryptContext::init(const Context &context, bool encoding)
+{
+       ssCryptDebug("===init");
+       SSContext::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
+SSCryptContext::inputSize(size_t outSize)
+{
+       ssCryptDebug("===inputSize  outSize=%u", (unsigned)outSize);
+       return UINT_MAX;
+}
+
+size_t
+SSCryptContext::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
+SSCryptContext::minimumProgress(size_t &in, size_t &out)
+{
+       in = 1;
+       out = 0;
+}
+
+void
+SSCryptContext::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
+SSCryptContext::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
+SSDigestContext::SSDigestContext(SSCSPSession &session)
+       : SSContext(session), mDigest(NULL)
+{
+       
+}
+
+SSDigestContext::~SSDigestContext()
+{
+       delete mDigest;
+}
+
+void SSDigestContext::init(const Context &context, bool encoding)
+{
+       CSSM_ALGORITHMS alg;
+       
+       SSContext::init(context, encoding);
+       alg = context.algorithm();
+       mDigest = new CssmClient::Digest(mSession.mRawCsp, alg);
+}
+
+void SSDigestContext::update(const CssmData &data)
+{
+       mDigest->digest(data);
+}
+
+void SSDigestContext::final(CssmData &out)
+{
+       (*mDigest)(out);
+}
+
+size_t SSDigestContext::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
+SSMACContext::SSMACContext(SSCSPSession &session)
+       : SSContext(session), mKeyHandle(noKey)
+{
+
+}
+
+void SSMACContext::init(const Context &context, bool encoding)
+{
+       SSContext::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 SSMACContext::update(const CssmData &data)
+{
+       /* add incoming data to accumulator */
+       mNullDigest.digestUpdate(data.data(), data.length());
+}
+
+size_t SSMACContext::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 SSMACContext::genMac(CssmData &mac)
+{
+       CssmData allData(const_cast<void *>(mNullDigest.digestPtr()), 
+               mNullDigest.digestSizeInBytes());
+       clientSession().generateMac(*mContext, mKeyHandle, allData, mac);
+}
+
+void SSMACContext::final(CssmData &mac)
+{
+       genMac(mac);
+}
+
+/* verify */
+void SSMACContext::final(const CssmData &mac)
+{
+       CssmData allData(const_cast<void *>(mNullDigest.digestPtr()), 
+               mNullDigest.digestSizeInBytes());
+       clientSession().verifyMac(*mContext, mKeyHandle, allData, mac);
+}