#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...) secinfo("blockCrypt", ## args)
#define bprintf(args...) secinfo("blockCryptBuf", ## args)
#define ioprintf(args...) secinfo("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;
}
void BlockCryptor::setup(
size_t blockSizeIn, size_t blockSizeOut, bool pkcsPad, bool needsFinal, BC_Mode mode, const CssmData *iv) {
if(pkcsPad && needsFinal) {
BlockCryptDebug("BlockCryptor::setup pkcsPad && needsFinal");
CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR);
}
mPkcsPadding = pkcsPad;
mMode = mode;
mNeedFinalData = needsFinal;
if(mInBuf != NULL) {
if(mInBlockSize != blockSizeIn) {
session().free(mInBuf);
mInBuf = NULL;
}
}
if(mInBuf == NULL) {
mInBuf = (uint8 *)session().malloc(blockSizeIn);
}
if((mMode == BCM_CBC) && !encoding() && !mCbcCapable) {
if(mChainBuf != NULL) {
if(mInBlockSize != blockSizeIn) {
session().free(mChainBuf);
mChainBuf = NULL;
}
}
if(mChainBuf == NULL) {
mChainBuf = (uint8 *)session().malloc(blockSizeIn);
}
}
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) {
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_MODE);
}
if(iv->Length < blockSizeIn) {
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_INIT_VECTOR);
}
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;
}
void BlockCryptor::setup(
size_t blockSize, const Context &context)
{
bool padEnable = false;
bool chainEnable = false;
bool ivEnable = false;
CssmData *iv = NULL;
CSSM_ENCRYPT_MODE cssmMode = context.getInt(CSSM_ATTRIBUTE_MODE);
switch (cssmMode) {
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) {
uint32 padding = context.getInt(CSSM_ATTRIBUTE_PADDING); if(blockSize == 8) {
switch(padding) {
case CSSM_PADDING_PKCS7:
case CSSM_PADDING_PKCS5:
case CSSM_PADDING_PKCS1:
break;
default:
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_PADDING);
}
}
else {
switch(padding) {
case CSSM_PADDING_PKCS5: case CSSM_PADDING_PKCS7:
break;
default:
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_PADDING);
}
}
}
if(ivEnable) {
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, chainEnable ? BCM_CBC : BCM_ECB,
iv);
}
void BlockCryptor::update(
void *inp,
size_t &inSize, void *outp,
size_t &outSize) {
uint8 *uInp = (UInt8 *)inp;
uint8 *uOutp = (UInt8 *)outp;
size_t uInSize = inSize; size_t uOutSize = 0; size_t uOutLeft = outSize; 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) {
toMove = mInBlockSize - mInBufSize;
if(toMove > uInSize) {
toMove = uInSize;
}
if(encoding() && doCbc) {
for(i=0; i<toMove; i++) {
mInBuf[mInBufSize + i] ^= *uInp++;
}
}
else {
memmove(mInBuf+mInBufSize, uInp, toMove);
uInp += toMove;
}
uInSize -= toMove;
mInBufSize += toMove;
if((mInBufSize == mInBlockSize) && !((uInSize == 0) && needLeftOver)) {
actMoved = uOutLeft;
if(encoding()) {
encryptBlock(mInBuf, mInBlockSize, uOutp, actMoved, false);
if(doCbc) {
assert(mInBlockSize == actMoved);
memmove(mInBuf, uOutp, mInBlockSize);
}
}
else {
decryptBlock(mInBuf, mInBlockSize, uOutp, actMoved, false);
if(doCbc) {
assert(mInBlockSize == actMoved);
for(i=0; i<mInBlockSize; i++) {
uOutp[i] ^= mChainBuf[i];
}
memmove(mChainBuf, mInBuf, mInBlockSize);
}
}
uOutSize += actMoved;
uOutp += actMoved;
uOutLeft -= actMoved;
mInBufSize = 0;
assert(uOutSize <= outSize);
}
}
if(uInSize == 0) {
outSize = uOutSize;
ioprintf("=== BlockCryptor::update encrypt %d inSize 0x%lx outSize 0x%lx",
encoding() ? 1 : 0, inSize, outSize);
return;
}
size_t leftOver = uInSize % mInBlockSize;
if((leftOver == 0) && needLeftOver) {
leftOver = mInBlockSize;
}
toMove = uInSize - leftOver;
size_t blocks = toMove / mInBlockSize;
if(mMultiBlockCapable && !doCbc && (blocks != 0)) {
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) {
encryptBlock(uInp, mInBlockSize, uOutp, actMoved, false);
}
else {
for(i=0; i<mInBlockSize; i++) {
mInBuf[i] ^= uInp[i];
}
encryptBlock(mInBuf, mInBlockSize, uOutp, actMoved, false);
assert(actMoved == mInBlockSize);
memmove(mInBuf, uOutp, mInBlockSize);
}
uOutSize += actMoved;
uOutp += actMoved;
uInp += mInBlockSize;
uOutLeft -= actMoved;
toMove -= mInBlockSize;
assert(uOutSize <= outSize);
}
}
else {
while(toMove) {
actMoved = uOutLeft;
if(doCbc) {
memmove(mInBuf, uInp, mInBlockSize);
decryptBlock(uInp, mInBlockSize, uOutp, actMoved, false);
assert(mInBlockSize == actMoved);
for(i=0; i<mInBlockSize; i++) {
uOutp[i] ^= mChainBuf[i];
}
memmove(mChainBuf, mInBuf, mInBlockSize);
}
else {
decryptBlock(uInp, mInBlockSize, uOutp, actMoved, false);
}
uOutSize += actMoved;
uOutp += actMoved;
uInp += mInBlockSize;
uOutLeft -= actMoved;
toMove -= mInBlockSize;
assert(uOutSize <= outSize);
}
}
if(leftOver) {
if(encoding() && doCbc) {
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; size_t actMoved;
size_t uOutLeft = out.Length; unsigned i;
bool doCbc = (mMode == BCM_CBC) && !mCbcCapable;
assert(mInBuf != NULL);
mOpStarted = true;
if((mInBufSize == 0) && mNeedFinalData) {
BlockCryptDebug("BlockCryptor::final with no mInBuf data");
CssmError::throwMe(CSSMERR_CSP_INPUT_LENGTH_ERROR);
}
if(encoding()) {
uint8 *ctext = out.Data;
if(mPkcsPadding) {
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;
}
if(mInBufSize) {
actMoved = uOutLeft;
encryptBlock(mInBuf, mInBufSize, ctext, actMoved, true);
uOutSize += actMoved;
mInBufSize = 0;
assert(uOutSize <= out.length());
}
out.length(uOutSize);
}
else {
if(mInBufSize == 0) {
if(mPkcsPadding) {
BlockCryptDebug("BlockCryptor::final decrypt/pad with no mInBuf data");
CssmError::throwMe(CSSMERR_CSP_INPUT_LENGTH_ERROR);
}
else {
ioprintf("=== BlockCryptor::final encrypt 0 outSize 0");
out.length(0);
return;
}
}
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) {
assert(mInBlockSize == actMoved);
for(i=0; i<mInBlockSize; i++) {
ptext[i] ^= mChainBuf[i];
}
}
if(mPkcsPadding) {
assert(actMoved == mOutBlockSize);
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);
}
ioprintf("=== BlockCryptor::final encrypt %d outSize 0x%lx",
encoding() ? 1 : 0, out.Length);
}
void BlockCryptor::minimumProgress(
size_t &inSize,
size_t &outSize)
{
inSize = mInBlockSize - mInBufSize;
if(inSize == 0) {
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) {
size_t inSize;
if(outSize < mOutBlockSize) {
inSize = mInBlockSize - mInBufSize;
if(inSize == 0) {
BlockCryptDebug("BlockCryptor::inputSize: HELP! zero inSize and outSize!\n");
}
}
else {
size_t wholeBlocks = outSize / mOutBlockSize;
assert(wholeBlocks >= 1);
inSize = (wholeBlocks * mInBlockSize) - mInBufSize;
if(inSize == 0) {
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 ) {
size_t rawBytes = inSize + mInBufSize;
size_t rawBlocks = rawBytes / mInBlockSize;
if(encoding() && final && (mPkcsPadding || mNeedFinalData)) {
rawBlocks++;
}
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;
}