X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/72a12576750f52947eb043106ba5c12c0d07decf..b1ab9ed8d0e0f1c3b66d7daa8fd5564444c56195:/libsecurity_apple_csp/lib/BlockCryptor.cpp diff --git a/libsecurity_apple_csp/lib/BlockCryptor.cpp b/libsecurity_apple_csp/lib/BlockCryptor.cpp new file mode 100644 index 00000000..85d3bbbd --- /dev/null +++ b/libsecurity_apple_csp/lib/BlockCryptor.cpp @@ -0,0 +1,636 @@ +/* + * 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 + +#define BlockCryptDebug(args...) secdebug("blockCrypt", ## args) +#define bprintf(args...) secdebug("blockCryptBuf", ## args) +#define ioprintf(args...) secdebug("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; skip if algorithm does its own chaining */ + if((mMode == BCM_CBC) && !encoding() && !mCbcCapable) { + 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(!mCbcCapable) { + 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); + bool doCbc = (mMode == BCM_CBC) && !mCbcCapable; + + assert(mInBuf != NULL); + mOpStarted = true; + + if(mInBufSize) { + /* attempt to fill mInBuf from inp */ + toMove = mInBlockSize - mInBufSize; + if(toMove > uInSize) { + toMove = uInSize; + } + if(encoding() && doCbc) { + /* xor into last cipherblock or IV */ + for(i=0; i mInBuf */ + if(leftOver) { + if(encoding() && doCbc) { + /* 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; +} + + +