/* * Copyright (c) 2000-2001 Apple Computer, 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. */ /* * BlockCryptor.cpp - common context for block-oriented encryption algorithms * * Created March 5 2001 by dmitch */ #include "BlockCryptor.h" #include "BinaryKey.h" #include "AppleCSPSession.h" #include #include #include #include #include #include #define BlockCryptDebug(args...) debug("blockCrypt", ## args) #define bprintf(args...) debug("blockCryptBuf", ## args) #define ioprintf(args...) debug("blockCryptIo", ## args) BlockCryptor::~BlockCryptor() { if(mInBuf) { memset(mInBuf, 0, mInBlockSize); session().free(mInBuf); mInBuf = NULL; } if(mChainBuf) { memset(mChainBuf, 0, mInBlockSize); session().free(mChainBuf); mChainBuf = NULL; } mInBufSize = 0; } /* * Reusable setup functions called from subclass's init. * This is the general purpose one.... */ void BlockCryptor::setup( size_t blockSizeIn, // block size of input size_t blockSizeOut, // block size of output bool pkcsPad, // this class performs PKCS{5,7} padding bool needsFinal, // needs final update with valid data BC_Mode mode, // ECB, CBC const CssmData *iv) // init vector, required for CBC //Ê must be at least blockSizeIn bytes { if(pkcsPad && needsFinal) { BlockCryptDebug("BlockCryptor::setup pkcsPad && needsFinal"); CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR); } mPkcsPadding = pkcsPad; mMode = mode; mNeedFinalData = needsFinal; /* set up inBuf, all configurations */ if(mInBuf != NULL) { /* only reuse if same size */ if(mInBlockSize != blockSizeIn) { session().free(mInBuf); mInBuf = NULL; } } if(mInBuf == NULL) { mInBuf = (uint8 *)session().malloc(blockSizeIn); } /* set up chain buf, decrypt/CBC only */ if((mMode == BCM_CBC) && !encoding()) { if(mChainBuf != NULL) { /* only reuse if same size */ if(mInBlockSize != blockSizeIn) { session().free(mChainBuf); mChainBuf = NULL; } } if(mChainBuf == NULL) { mChainBuf = (uint8 *)session().malloc(blockSizeIn); } } /* IV iff CBC mode, and ensure IV is big enough */ switch(mMode) { case BCM_ECB: if(iv != NULL) { CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_INIT_VECTOR); } break; case BCM_CBC: if(iv == NULL) { CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR); } if(blockSizeIn != blockSizeOut) { /* no can do, must be same block sizes */ CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_MODE); } if(iv->Length < blockSizeIn) { /* not enough IV */ CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_INIT_VECTOR); } /* save IV as appropriate */ if(encoding()) { memmove(mInBuf, iv->Data, blockSizeIn); } else { assert(mChainBuf != NULL); memmove(mChainBuf, iv->Data, blockSizeIn); } break; } mInBlockSize = blockSizeIn; mInBufSize = 0; mOutBlockSize = blockSizeOut; mOpStarted = false; } /* * This one is used by simple, well-behaved algorithms which don't do their own * padding and which rely on us to do everything but one-block-at-a-time * encrypt and decrypt. */ void BlockCryptor::setup( size_t blockSize, // block size of input and output const Context &context) { bool padEnable = false; bool chainEnable = false; bool ivEnable = false; CssmData *iv = NULL; /* * Validate context * IV optional per mode * pad optional per mode * Currently we ignore extraneous attributes (e.g., it's OK to pass in * an IV if the mode doesn't specify it), mainly for simplifying test routines. */ CSSM_ENCRYPT_MODE cssmMode = context.getInt(CSSM_ATTRIBUTE_MODE); switch (cssmMode) { /* no mode attr --> 0 == CSSM_ALGMODE_NONE, not currently supported */ case CSSM_ALGMODE_CBCPadIV8: padEnable = true; ivEnable = true; chainEnable = true; break; case CSSM_ALGMODE_CBC_IV8: ivEnable = true; chainEnable = true; break; case CSSM_ALGMODE_ECB: break; case CSSM_ALGMODE_ECBPad: padEnable = true; break; default: errorLog1("DESContext::init: illegal mode (%d)\n", (int)cssmMode); CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_MODE); } if(padEnable) { /* validate padding type */ uint32 padding = context.getInt(CSSM_ATTRIBUTE_PADDING); // 0 ==> PADDING_NONE if(blockSize == 8) { switch(padding) { /* backwards compatibility - used to be PKCS1, should be PKCS5 or 7 */ case CSSM_PADDING_PKCS7: case CSSM_PADDING_PKCS5: case CSSM_PADDING_PKCS1: //Êthis goes away soon /* OK */ break; default: CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_PADDING); } } else { switch(padding) { case CSSM_PADDING_PKCS5: // this goes away soon case CSSM_PADDING_PKCS7: /* OK */ break; default: CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_PADDING); } } } if(ivEnable) { /* make sure there's an IV in the context of sufficient length */ iv = context.get(CSSM_ATTRIBUTE_INIT_VECTOR); if(iv == NULL) { CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR); } if(iv->Length < blockSize) { CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_INIT_VECTOR); } } setup(blockSize, blockSize, padEnable, false, // needsFinal chainEnable ? BCM_CBC : BCM_ECB, iv); } /* * Update always leaves some data in mInBuf if: * mNeedsFinalData is true, or * decrypting and mPkcsPadding true. * Also, we always process all of the input (except on error). */ void BlockCryptor::update( void *inp, size_t &inSize, // in/out void *outp, size_t &outSize) // in/out { UInt8 *uInp = (UInt8 *)inp; UInt8 *uOutp = (UInt8 *)outp; size_t uInSize = inSize; // input bytes to go size_t uOutSize = 0; // ouput bytes generated size_t uOutLeft = outSize; // bytes remaining in outp size_t toMove; size_t actMoved; unsigned i; bool needLeftOver = mNeedFinalData || (!encoding() && mPkcsPadding); assert(mInBuf != NULL); mOpStarted = true; if(mInBufSize) { /* attempt to fill mInBuf from inp */ toMove = mInBlockSize - mInBufSize; if(toMove > uInSize) { toMove = uInSize; } if(encoding() && (mMode == BCM_CBC)) { /* xor into last cipherblock or IV */ for(i=0; i mInBuf */ if(leftOver) { if(encoding() && (mMode == BCM_CBC)) { /* xor into last cipherblock or IV */ for(i=0; i mOutBlockSize) { BlockCryptDebug("BlockCryptor::final malformed ciphertext (1)"); CssmError::throwMe(CSSM_ERRCODE_INVALID_DATA); } UInt8 *padPtr = ptext + mOutBlockSize - padSize; for(unsigned i=0; i= 1); inSize = (wholeBlocks * mInBlockSize) - mInBufSize; if(inSize == 0) { /* i.e., we're holding a whole buffer */ inSize++; } } bprintf("--- BlockCryptor::inputSize inSize 0x%lx outSize 0x%lx mInBufSize 0x%lx", inSize, outSize, mInBufSize); return inSize; } size_t BlockCryptor::outputSize( bool final, size_t inSize = 0) // output for given input size { size_t rawBytes = inSize + mInBufSize; // huh?Êdon't round this up! //size_t rawBlocks = (rawBytes + mInBlockSize - 1) / mInBlockSize; size_t rawBlocks = rawBytes / mInBlockSize; /* * encrypting: always get one additional block on final() if we're padding * or (we presume) the subclass is padding. Note that we * truncated when calculating rawBlocks; to finish out on the * final block, we (or our subclass) will either have to pad * out the current partial block, or cook up a full pad block if * mInBufSize is currently zero. Subclasses which pad some other * way need to override this method. * * decrypting: outsize always <= insize */ if(encoding() && final && (mPkcsPadding || mNeedFinalData)) { rawBlocks++; } /* FIXME - optimize for needFinalData? (can squeak by with smaller outSize) */ size_t rtn = rawBlocks * mOutBlockSize; bprintf("--- BlockCryptor::outputSize inSize 0x%lx outSize 0x%lx final %d " "inBufSize 0x%lx", inSize, rtn, final, mInBufSize); return rtn; }