+++ /dev/null
-/*
- * 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 <security_utilities/alloc.h>
-#include <Security/cssmerr.h>
-#include <string.h>
-#include <security_utilities/debugging.h>
-#include <security_cdsa_utilities/cssmdata.h>
-
-#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<CssmData>(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<toMove; i++) {
- mInBuf[mInBufSize + i] ^= *uInp++;
- }
- }
- else {
- /* use incoming data as is */
- memmove(mInBuf+mInBufSize, uInp, toMove);
- uInp += toMove;
- }
- uInSize -= toMove;
- mInBufSize += toMove;
- /*
- * Process inBuf if it's full, but skip if no more data in uInp and
- * inBuf might be needed (by us for unpadding on decrypt, or by
- * subclass for anything) for a final call
- */
- if((mInBufSize == mInBlockSize) && !((uInSize == 0) && needLeftOver)) {
- actMoved = uOutLeft;
- if(encoding()) {
- encryptBlock(mInBuf, mInBlockSize, uOutp, actMoved, false);
- if(doCbc) {
- /* save ciphertext for chaining next block */
- assert(mInBlockSize == actMoved);
- memmove(mInBuf, uOutp, mInBlockSize);
- }
- }
- else {
- decryptBlock(mInBuf, mInBlockSize, uOutp, actMoved, false);
- if(doCbc) {
- /* xor in last ciphertext */
- assert(mInBlockSize == actMoved);
- for(i=0; i<mInBlockSize; i++) {
- uOutp[i] ^= mChainBuf[i];
- }
- /* save this ciphertext for next chain */
- memmove(mChainBuf, mInBuf, mInBlockSize);
- }
- }
- uOutSize += actMoved;
- uOutp += actMoved;
- uOutLeft -= actMoved;
- mInBufSize = 0;
- assert(uOutSize <= outSize);
- }
- } /* processing mInBuf */
- if(uInSize == 0) {
- /* done */
- outSize = uOutSize;
- ioprintf("=== BlockCryptor::update encrypt %d inSize 0x%lx outSize 0x%lx",
- encoding() ? 1 : 0, inSize, outSize);
- return;
- }
-
-
- /*
- * en/decrypt even blocks in (remaining) inp.
- */
- size_t leftOver = uInSize % mInBlockSize;
- if((leftOver == 0) && needLeftOver) {
- /*
- * Even blocks coming in, but we really need to leave some data
- * in the buffer (because the subclass asked for it, or we're decrypting
- * with PKCS padding). Save one block for mInBuf.
- */
- leftOver = mInBlockSize;
- }
- toMove = uInSize - leftOver;
- size_t blocks = toMove / mInBlockSize;
- if(mMultiBlockCapable && !doCbc && (blocks != 0)) {
- /*
- * Optimization for algorithms that are multi-block capable and that
- * can do their own CBC (if necessary).
- */
- size_t thisMove = blocks * mInBlockSize;
- actMoved = uOutLeft;
- if(encoding()) {
- encryptBlock(uInp, thisMove, uOutp, actMoved, false);
- }
- else {
- decryptBlock(uInp, thisMove, uOutp, actMoved, false);
- }
- uOutSize += actMoved;
- uOutp += actMoved;
- uInp += thisMove;
- uOutLeft -= actMoved;
- toMove -= thisMove;
- assert(uOutSize <= outSize);
- }
- else if(encoding()) {
- while(toMove) {
- actMoved = uOutLeft;
- if(!doCbc) {
- /* encrypt directly from input to output */
- encryptBlock(uInp, mInBlockSize, uOutp, actMoved, false);
- }
- else {
- /* xor into last ciphertext, encrypt the result */
- for(i=0; i<mInBlockSize; i++) {
- mInBuf[i] ^= uInp[i];
- }
- encryptBlock(mInBuf, mInBlockSize, uOutp, actMoved, false);
-
- /* save new ciphertext for next chain */
- assert(actMoved == mInBlockSize);
- memmove(mInBuf, uOutp, mInBlockSize);
- }
- uOutSize += actMoved;
- uOutp += actMoved;
- uInp += mInBlockSize;
- uOutLeft -= actMoved;
- toMove -= mInBlockSize;
- assert(uOutSize <= outSize);
- } /* main encrypt loop */
-
- }
- else {
- /* decrypting */
- while(toMove) {
- actMoved = uOutLeft;
- if(doCbc) {
- /* save this ciphertext for chain; don't assume in != out */
- memmove(mInBuf, uInp, mInBlockSize);
- decryptBlock(uInp, mInBlockSize, uOutp, actMoved, false);
-
- /* chain in previous ciphertext */
- assert(mInBlockSize == actMoved);
- for(i=0; i<mInBlockSize; i++) {
- uOutp[i] ^= mChainBuf[i];
- }
-
- /* save current ciphertext for next block */
- memmove(mChainBuf, mInBuf, mInBlockSize);
- }
- else {
- /* ECB */
- decryptBlock(uInp, mInBlockSize, uOutp, actMoved, false);
- }
- uOutSize += actMoved;
- uOutp += actMoved;
- uInp += mInBlockSize;
- uOutLeft -= actMoved;
- toMove -= mInBlockSize;
- assert(uOutSize <= outSize);
- } /* main decrypt loop */
-
- }
-
- /* leftover bytes from inp --> mInBuf */
- if(leftOver) {
- if(encoding() && doCbc) {
- /* xor into last cipherblock or IV */
- for(i=0; i<leftOver; i++) {
- mInBuf[i] ^= *uInp++;
- }
- }
- else {
- if(mInBuf && uInp && leftOver) memmove(mInBuf, uInp, leftOver);
- }
- }
-
- mInBufSize = leftOver;
- outSize = uOutSize;
- ioprintf("=== BlockCryptor::update encrypt %d inSize 0x%lx outSize 0x%lx",
- encoding() ? 1 : 0, inSize, outSize);
-}
-
-void BlockCryptor::final(
- CssmData &out)
-{
- size_t uOutSize = 0; // ouput bytes generated
- size_t actMoved;
- size_t uOutLeft = out.Length; // bytes remaining in out
- unsigned i;
- bool doCbc = (mMode == BCM_CBC) && !mCbcCapable;
-
- assert(mInBuf != NULL);
- mOpStarted = true;
- if((mInBufSize == 0) && mNeedFinalData) {
- /* only way this could happen: no update() called (at least not with
- * non-zero input data sizes) */
- BlockCryptDebug("BlockCryptor::final with no mInBuf data");
- CssmError::throwMe(CSSMERR_CSP_INPUT_LENGTH_ERROR);
- }
- if(encoding()) {
- uint8 *ctext = out.Data;
-
- if(mPkcsPadding) {
- /*
- * PKCS5/7 padding: pad byte = size of padding.
- * This assertion courtesy of the limitation on the mutual
- * exclusivity of mPkcsPadding and mNeedFinalData.
- */
- assert(mInBufSize < mInBlockSize);
- size_t padSize = mInBlockSize - mInBufSize;
- uint8 *padPtr = mInBuf + mInBufSize;
- if(!doCbc) {
- for(i=0; i<padSize; i++) {
- *padPtr++ = padSize;
- }
- }
- else {
- for(i=0; i<padSize; i++) {
- *padPtr++ ^= padSize;
- }
- }
- mInBufSize = mInBlockSize;
- } /* PKCS padding */
-
- /*
- * Encrypt final mInBuf. If it's not full, the BlockCryptObject better know
- * how to pad....
- */
- if(mInBufSize) {
- actMoved = uOutLeft;
- encryptBlock(mInBuf, mInBufSize, ctext, actMoved, true);
- uOutSize += actMoved;
- mInBufSize = 0;
- assert(uOutSize <= out.length());
- }
- out.length(uOutSize);
- } /* encrypting */
-
- else {
- if(mInBufSize == 0) {
- if(mPkcsPadding) {
- BlockCryptDebug("BlockCryptor::final decrypt/pad with no mInBuf data");
- CssmError::throwMe(CSSMERR_CSP_INPUT_LENGTH_ERROR);
- }
- else {
- /* simple decrypt op complete */
- ioprintf("=== BlockCryptor::final encrypt 0 outSize 0");
- out.length(0);
- return;
- }
- }
-
- /*
- * Decrypt - must have exactly one block of ciphertext.
- * We trust CSPContext, and our own outputSize(), to have set up
- * the current output buffer with enough space to handle the
- * full size of the decrypt, even though - due to padding - we
- * might actually pass less than that amount back to caller.
- */
- if(mInBufSize != mInBlockSize) {
- BlockCryptDebug("BlockCryptor::final unaligned ciphertext");
- CssmError::throwMe(CSSMERR_CSP_INPUT_LENGTH_ERROR);
- }
-
- uint8 *ptext = out.Data;
- actMoved = uOutLeft;
- decryptBlock(mInBuf, mInBlockSize, ptext, actMoved, true);
- if(doCbc) {
- /* chain in previous ciphertext one more time */
- assert(mInBlockSize == actMoved);
- for(i=0; i<mInBlockSize; i++) {
- ptext[i] ^= mChainBuf[i];
- }
- }
- if(mPkcsPadding) {
- assert(actMoved == mOutBlockSize);
-
- /* ensure integrity of padding byte(s) */
- unsigned padSize = ptext[mOutBlockSize - 1];
- if(padSize > mOutBlockSize) {
- BlockCryptDebug("BlockCryptor::final malformed ciphertext (1)");
- CssmError::throwMe(CSSM_ERRCODE_INVALID_DATA);
- }
- uint8 *padPtr = ptext + mOutBlockSize - padSize;
- for(unsigned i=0; i<padSize; i++) {
- if(*padPtr++ != padSize) {
- BlockCryptDebug("BlockCryptor::final malformed ciphertext "
- "(2)");
- CssmError::throwMe(CSSM_ERRCODE_INVALID_DATA);
- }
- }
- actMoved -= padSize;
- }
- assert(actMoved <= out.length());
- out.length(actMoved);
- } /* decrypting */
- ioprintf("=== BlockCryptor::final encrypt %d outSize 0x%lx",
- encoding() ? 1 : 0, out.Length);
-}
-
-/*
- * These three are only valid for algorithms for which encrypting one block
- * of plaintext always yields exactly one block of ciphertext, and vice versa
- * for decrypt. The block sizes for plaintext and ciphertext do NOT have to be
- * the same. Subclasses (e.g. FEED) which do not meet this criterion will have
- * to override.
- */
-
-void BlockCryptor::minimumProgress(
- size_t &inSize,
- size_t &outSize)
-{
- /* each size = one block (including buffered input) */
- inSize = mInBlockSize - mInBufSize;
- if(inSize == 0) {
- /* i.e., we're holding a whole buffer */
- inSize++;
- }
- outSize = mOutBlockSize;
- bprintf("--- BlockCryptor::minProgres inSize 0x%lx outSize 0x%lx mInBufSize 0x%lx",
- inSize, outSize, mInBufSize);
-}
-
-size_t BlockCryptor::inputSize(
- size_t outSize) // input for given output size
-{
- size_t inSize;
-
- if(outSize < mOutBlockSize) {
- /*
- * Sometimes CSPFullPluginSession calls us like this....in this
- * case the legal inSize is just the remainder of the input buffer,
- * less one byte (in other words, the max we we gobble up without
- * producing any output).
- */
- inSize = mInBlockSize - mInBufSize;
- if(inSize == 0) {
- /* we have a full input buffer! How can this happen!? */
- BlockCryptDebug("BlockCryptor::inputSize: HELP! zero inSize and outSize!\n");
- }
- }
- else {
- /* more-or-less normal case */
- size_t wholeBlocks = outSize / mOutBlockSize;
- assert(wholeBlocks >= 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;
-}
-
-
-