--- /dev/null
+/*
+ * 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 <security_utilities/debugging.h>
+#include <security_utilities/logging.h>
+#include <Security/cssmapple.h>
+
+#define abprintf(args...) secdebug("ascBuf", ## args) /* buffer sizes */
+#define aioprintf(args...) secdebug("ascIo", ## args) /* all I/O */
+
+static Allocator *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(
+ Allocator *normAlloc,
+ Allocator *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)
+{
+ CSSM_SIZE keyLen;
+ uint8 *keyData = NULL;
+ comcryptReturn crtn;
+
+ /* obtain key from context */
+ symmetricKeyBits(context, session(), 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<size_t>(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 */