--- /dev/null
+/*
+ * 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);
+}