/* * ascContext.cpp - glue between BlockCrytpor and ComCryption (a.k.a. Apple * Secure Compression). * Written by Doug Mitchell 4/4/2001 */ #ifdef ASC_CSP_ENABLE #include "ascContext.h" #include "ascFactory.h" #include #include #include #define abprintf(args...) secdebug("ascBuf", ## args) /* buffer sizes */ #define aioprintf(args...) secdebug("ascIo", ## args) /* all I/O */ static CssmAllocator *ascAllocator; /* * Comcryption-style memory allocator callbacks */ static void *ccMalloc(unsigned size) { return ascAllocator->malloc(size); } static void ccFree(void *data) { ascAllocator->free(data); } /* Given a ComCryption error, throw appropriate CssmError */ static void throwComcrypt( comcryptReturn crtn, const char *op) /* optional */ { CSSM_RETURN cerr = CSSM_OK; const char *errStr = "Bad Error String"; switch(crtn) { case CCR_SUCCESS: errStr = "CCR_SUCCESS"; break; case CCR_OUTBUFFER_TOO_SMALL: errStr = "CCR_OUTBUFFER_TOO_SMALL"; cerr = CSSMERR_CSP_OUTPUT_LENGTH_ERROR; break; case CCR_MEMORY_ERROR: errStr = "CCR_MEMORY_ERROR"; cerr = CSSMERR_CSP_MEMORY_ERROR; break; case CCR_WRONG_VERSION: errStr = "CCR_WRONG_VERSION"; cerr = CSSMERR_CSP_INVALID_DATA; break; case CCR_BAD_CIPHERTEXT: errStr = "CCR_BAD_CIPHERTEXT"; cerr = CSSMERR_CSP_INVALID_DATA; break; case CCR_INTERNAL: default: errStr = "CCR_INTERNAL"; cerr = CSSMERR_CSP_INTERNAL_ERROR; break; } if(op) { Security::Syslog::error("Apple CSP %s: %s", op, errStr); } if(cerr) { CssmError::throwMe(cerr); } } /* * Algorithm factory. */ AscAlgFactory::AscAlgFactory( CssmAllocator *normAlloc, CssmAllocator *privAlloc) { /* once-per-address-space init */ ascAllocator = privAlloc; comMallocRegister(ccMalloc, ccFree); } bool AscAlgFactory::setup( AppleCSPSession &session, CSPFullPluginSession::CSPContext * &cspCtx, const Context &context) { if(context.algorithm() != CSSM_ALGID_ASC) { return false; } if(cspCtx != NULL) { /* reusing one of ours; OK */ return true; } switch(context.type()) { case CSSM_ALGCLASS_KEYGEN: cspCtx = new AppleSymmKeyGenerator(session, 8, COMCRYPT_MAX_KEYLENGTH * 8, true); // must be byte size return true; case CSSM_ALGCLASS_SYMMETRIC: cspCtx = new ASCContext(session); return true; default: break; } /* not ours */ return false; } ASCContext::~ASCContext() { if(mCcObj != NULL) { comcryptObjFree(mCcObj); } } /* * Standard CSPContext init, called from CSPFullPluginSession::init(). * Reusable, e.g., query followed by en/decrypt. */ void ASCContext::init( const Context &context, bool encrypting) { UInt32 keyLen; UInt8 *keyData = NULL; comcryptReturn crtn; /* obtain key from context */ symmetricKeyBits(context, CSSM_ALGID_ASC, encrypting ? CSSM_KEYUSE_ENCRYPT : CSSM_KEYUSE_DECRYPT, keyData, keyLen); if((keyLen < 1) || (keyLen > COMCRYPT_MAX_KEYLENGTH)) { CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY); } mDecryptBufValid = false; /* optional optimization attribute */ comcryptOptimize optimize = CCO_DEFAULT; uint32 opt = context.getInt(CSSM_ATTRIBUTE_ASC_OPTIMIZATION); switch(opt) { case CSSM_ASC_OPTIMIZE_DEFAULT: optimize = CCO_DEFAULT; break; case CSSM_ASC_OPTIMIZE_SIZE: optimize = CCO_SIZE; break; case CSSM_ASC_OPTIMIZE_SECURITY: optimize = CCO_SECURITY; break; case CSSM_ASC_OPTIMIZE_TIME: optimize = CCO_TIME; break; case CSSM_ASC_OPTIMIZE_TIME_SIZE: optimize = CCO_TIME_SIZE; break; case CSSM_ASC_OPTIMIZE_ASCII: optimize = CCO_ASCII; break; default: CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_ALG_PARAMS); } /* All other context attributes ignored */ /* init the low-level state */ if(mCcObj == NULL) { /* note we allow for context reuse */ mCcObj = comcryptAlloc(); if(mCcObj == NULL) { CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR); } } crtn = comcryptInit(mCcObj, keyData, keyLen, optimize); if(crtn) { throwComcrypt(crtn, "comcryptInit"); } } /* * All of these functions are called by CSPFullPluginSession. */ void ASCContext::update( void *inp, size_t &inSize, // in/out void *outp, size_t &outSize) // in/out { comcryptReturn crtn; unsigned outLen; unsigned char *inText = (unsigned char *)inp; unsigned char *outText = (unsigned char *)outp; if(encoding()) { outLen = outSize; crtn = comcryptData(mCcObj, inText, inSize, outText, &outLen, CCE_MORE_TO_COME); // not used on encrypt if(crtn) { throwComcrypt(crtn, "comcryptData"); } } else { /* * Deal with 1-byte buffer hack. First decrypt the existing buffer... */ if(inSize == 0) { CssmError::throwMe(CSSMERR_CSP_INPUT_LENGTH_ERROR); } unsigned thisOutLen; unsigned partialOutLen = 0; if(mDecryptBufValid) { thisOutLen = outSize; crtn = deComcryptData(mCcObj, &mDecryptBuf, 1, outText, &thisOutLen, CCE_MORE_TO_COME); mDecryptBufValid = false; if(crtn) { throwComcrypt(crtn, "deComcryptData (1)"); } partialOutLen = thisOutLen; outText += thisOutLen; } /* * Now decrypt remaining, less one byte (which is stored in the * buffer). */ thisOutLen = outSize - partialOutLen; crtn = deComcryptData(mCcObj, inText, inSize - 1, outText, &thisOutLen, CCE_MORE_TO_COME); if(crtn) { throwComcrypt(crtn, "deComcryptData (2)"); } outLen = partialOutLen + thisOutLen; mDecryptBuf = inText[inSize - 1]; mDecryptBufValid = true; } outSize = outLen; aioprintf("=== ASC::update encrypt %d inSize %ld outSize %ld", encoding() ? 1 : 0, inSize, outSize); } void ASCContext::final( CssmData &out) { if(encoding()) { out.length(0); } else { /* decrypt buffer hack */ if(!mDecryptBufValid) { CssmError::throwMe(CSSMERR_CSP_INPUT_LENGTH_ERROR); } comcryptReturn crtn; unsigned outLen = out.Length; crtn = deComcryptData(mCcObj, &mDecryptBuf, 1, (unsigned char *)out.Data, &outLen, CCE_END_OF_STREAM); mDecryptBufValid = false; if(crtn) { throwComcrypt(crtn, "deComcryptData (3)"); } out.length(outLen); } aioprintf("=== ASC::final encrypt %d outSize %ld", encoding() ? 1 : 0, out.Length); } size_t ASCContext::inputSize( size_t outSize) // input for given output size { size_t rtn = comcryptMaxInBufSize(mCcObj, outSize, encoding() ? CCOP_COMCRYPT : CCOP_DECOMCRYPT); abprintf("--- ASCContext::inputSize inSize %ld outSize %ld", rtn, outSize); return rtn; } /* * ComCryption's buffer size calculation really does not lend itself to the * requirements here. For example, there is no guarantee that * inputSize(outputSize(x)) == x. We're just going to fudge it and make * apps (or CSPFullPluginSession) alloc plenty more than they need. */ #define ASC_OUTSIZE_FUDGE 1 #define ASC_OUTSIZE_FUDGE_FACTOR 1.2 size_t ASCContext::outputSize( bool final, size_t inSize) // output for given input size { unsigned effectiveInSize = inSize; size_t rtn; if(encoding()) { rtn = comcryptMaxOutBufSize(mCcObj, effectiveInSize, CCOP_COMCRYPT, final); #if ASC_OUTSIZE_FUDGE float newOutSize = rtn; newOutSize *= ASC_OUTSIZE_FUDGE_FACTOR; rtn = static_cast(newOutSize); #endif /* ASC_OUTSIZE_FUDGE */ } else { if(final) { if(mDecryptBufValid) { effectiveInSize++; } } else if(inSize && !mDecryptBufValid) { /* not final and nothing buffered yet - lop off one */ effectiveInSize--; } rtn = comcryptMaxOutBufSize(mCcObj, effectiveInSize, CCOP_DECOMCRYPT, final); } abprintf("--- ASCContext::outputSize inSize %ld outSize %ld final %d ", inSize, rtn, final); return rtn; } void ASCContext::minimumProgress( size_t &in, size_t &out) // minimum progress chunks { if(encoding()) { in = 1; out = comcryptMaxOutBufSize(mCcObj, 1, CCOP_COMCRYPT, 0); } else { if(mDecryptBufValid) { /* use "everything" */ in = 1; } else { in = 0; } out = comcryptMaxOutBufSize(mCcObj, in, CCOP_DECOMCRYPT, 0); } abprintf("--- ASCContext::minProgres in %ld out %ld", in, out); } #endif /* ASC_CSP_ENABLE */