X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/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 index 00000000..3d5de0ab --- /dev/null +++ b/OSX/libsecurity_apple_cspdl/lib/SSContext.cpp @@ -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 + +#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(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(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(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(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(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(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(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(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(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(mNullDigest.digestPtr()), + mNullDigest.digestSizeInBytes()); + clientSession().verifyMac(*mContext, mKeyHandle, allData, mac); +}