#include <CommonCrypto/CommonCryptor.h>
#include "CommonCryptorPriv.h"
#include <stdlib.h>
#include <strings.h>
#include <CommonCrypto/opensslDES.h>
#include <CommonCrypto/ccCast.h>
#include <stddef.h>
#include <stdbool.h>
#define AES_GLADMAN_STD 0
#define AES_GLADMAN_NEW 1
#if AES_GLADMAN_STD
#include <AES/ccRijndaelGladman.h>
#elif AES_GLADMAN_NEW
#include <CommonCrypto/aesopt.h>
#endif
#define CC_DEBUG 0
#if CC_DEBUG
#include <stdio.h>
#define dprintf(args...) printf(args)
#else
#define dprintf(args...)
#endif
#define CC_MAX_BLOCK_SIZE kCCBlockSizeAES128
static void ccBlockCipherProcessOneBlock(
void *ctx,
const void *blockIn,
void *blockOut);
typedef int (*bcSetKeyFcn)(
void *ctx,
const void *rawKey,
size_t keyLength,
int forEncrypt);
typedef void (*bcSetIvFcn)(
void *ctx,
int forEncrypt,
const void *iv);
typedef void (*bcProcessBlockFcn)(
void *ctx,
const void *blockIn,
void *blockOut);
typedef void (*bcProcessBlocksFcn)(
void *ctx,
const void *blocksIn,
size_t numBlocks,
void *blocksOut);
typedef struct {
CCAlgorithm alg;
size_t blockSize;
size_t minKeySize;
size_t maxKeySize;
bool algDoesCbc;
bcSetKeyFcn setKey;
bcSetIvFcn setIv;
bcProcessBlockFcn encrypt;
bcProcessBlockFcn decrypt;
bcProcessBlocksFcn encryptBlocks;
bcProcessBlocksFcn decryptBlocks;
size_t ctxSize;
} CCAlgInfo;
static const CCAlgInfo bcAlgInfos[] =
{
{ kCCAlgorithmAES128, kCCBlockSizeAES128, kCCKeySizeAES128, kCCKeySizeAES256,
true,
(bcSetKeyFcn)aes_cc_set_key,
(bcSetIvFcn)aes_cc_set_iv,
ccBlockCipherProcessOneBlock,
ccBlockCipherProcessOneBlock,
(bcProcessBlocksFcn)aes_cc_encrypt,
(bcProcessBlocksFcn)aes_cc_decrypt,
sizeof(aes_cc_ctx)
},
{ kCCAlgorithmDES, kCCBlockSizeDES, kCCKeySizeDES, kCCKeySizeDES,
false,
(bcSetKeyFcn)osDesSetkey,
NULL,
(bcProcessBlockFcn)osDesEncrypt,
(bcProcessBlockFcn)osDesDecrypt,
NULL, NULL,
sizeof(DES_key_schedule)
},
{ kCCAlgorithm3DES, kCCBlockSize3DES, kCCKeySize3DES, kCCKeySize3DES,
false,
(bcSetKeyFcn)osDes3Setkey,
NULL,
(bcProcessBlockFcn)osDes3Encrypt,
(bcProcessBlockFcn)osDes3Decrypt,
NULL, NULL,
sizeof(DES3_Schedule)
},
{ kCCAlgorithmCAST, kCCBlockSizeCAST, kCCKeySizeMinCAST, kCCKeySizeMaxCAST,
false,
(bcSetKeyFcn)cast_cc_set_key,
NULL,
(bcProcessBlockFcn)cast_cc_encrypt,
(bcProcessBlockFcn)cast_cc_decrypt,
NULL, NULL,
sizeof(CAST_KEY)
}
};
#define NUM_CC_ALG_INFOS (sizeof(bcAlgInfos) / sizeof(bcAlgInfos[0]))
struct _CCBlockCipherContext {
const CCAlgInfo *algInfo;
bcProcessBlockFcn update;
bcProcessBlocksFcn updateBlocks;
uint8_t inBuf[CC_MAX_BLOCK_SIZE];
size_t inBufSize;
uint8_t chainBuf[CC_MAX_BLOCK_SIZE];
bool encrypting;
bool pkcsPad;
bool cbc;
bool doCbc;
char algCtx[1];
};
typedef struct _CCBlockCipherContext *CCBlockCipherContext;
#define MAC_BLOCK_SIZE 32
static void ccBlockCipherSetIV(
CCBlockCipherContext ctx,
const void *iv)
{
uint8_t *buf;
uint8_t blockSize;
if(ctx->algInfo->algDoesCbc) {
uint8_t nullIv[MAC_BLOCK_SIZE];
if(iv == NULL) {
memset(nullIv, 0, sizeof(nullIv));
iv = nullIv;
}
ctx->algInfo->setIv(ctx->algCtx, ctx->encrypting, iv);
return;
}
blockSize = ctx->algInfo->blockSize;
if(ctx->encrypting) {
buf = ctx->inBuf;
}
else {
buf = ctx->chainBuf;
}
if(iv == NULL) {
memset(buf, 0, blockSize);
}
else {
memmove(buf, iv, blockSize);
}
}
static const CCAlgInfo *ccBlockCipherFindAlgInfo(
CCAlgorithm alg)
{
const CCAlgInfo *algInfo = bcAlgInfos;
unsigned dex;
for(dex=0; dex<NUM_CC_ALG_INFOS; dex++) {
if(algInfo->alg == alg) {
return algInfo;
}
algInfo++;
}
return NULL;
}
static void ccBlockCipherProcessOneBlock(
void *ctx,
const void *blockIn,
void *blockOut)
{
char *ourCtx = (char *)ctx - offsetof(struct _CCBlockCipherContext, algCtx);
CCBlockCipherContext cryptCtx = (CCBlockCipherContext)ourCtx;
cryptCtx->updateBlocks(ctx, blockIn, 1, blockOut);
}
static CCCryptorStatus CCBlockCipherContextSize(
CCOperation op,
CCAlgorithm alg,
size_t *ctxSize)
{
const CCAlgInfo *algInfo = ccBlockCipherFindAlgInfo(alg);
if(algInfo == NULL) {
return kCCParamError;
}
*ctxSize = offsetof(struct _CCBlockCipherContext, algCtx) + algInfo->ctxSize;
return kCCSuccess;
}
static CCCryptorStatus CCBlockCipherInit(
void *ctx,
CCOperation op,
CCAlgorithm alg,
CCOptions options,
const void *key,
size_t keyLength,
const void *iv)
{
const CCAlgInfo *algInfo = ccBlockCipherFindAlgInfo(alg);
CCBlockCipherContext cryptCtx = (CCBlockCipherContext)ctx;
if((algInfo == NULL) || (key == NULL)) {
return kCCParamError;
}
if((keyLength < algInfo->minKeySize) ||
(keyLength > algInfo->maxKeySize)) {
return kCCParamError;
}
cryptCtx->algInfo = algInfo;
switch(op) {
case kCCEncrypt:
cryptCtx->update = algInfo->encrypt;
cryptCtx->updateBlocks = algInfo->encryptBlocks;
cryptCtx->encrypting = true;
break;
case kCCDecrypt:
cryptCtx->update = algInfo->decrypt;
cryptCtx->updateBlocks = algInfo->decryptBlocks;
cryptCtx->encrypting = false;
break;
default:
return kCCParamError;
}
cryptCtx->pkcsPad = (options & kCCOptionPKCS7Padding) ? true : false;
if(!(options & kCCOptionECBMode)) {
cryptCtx->cbc = true;
if(algInfo->algDoesCbc) {
cryptCtx->doCbc = false;
}
else {
cryptCtx->doCbc = true;
}
}
else {
cryptCtx->cbc = false;
cryptCtx->doCbc = false;
}
cryptCtx->inBufSize = 0;
if(algInfo->setKey(cryptCtx->algCtx, key, keyLength, cryptCtx->encrypting)) {
return kCCParamError;
}
if(cryptCtx->cbc) {
ccBlockCipherSetIV(cryptCtx, iv);
}
return kCCSuccess;
}
static CCCryptorStatus CCBlockCipherRelease(
void *ctx)
{
CCBlockCipherContext cryptCtx = (CCBlockCipherContext)ctx;
memset(cryptCtx, 0,
offsetof(struct _CCBlockCipherContext, algCtx) + cryptCtx->algInfo->ctxSize - 1);
return kCCSuccess;
}
static CCCryptorStatus CCBlockCipherUpdate(
void *ctx,
const void *dataIn,
size_t dataInLen,
void *dataOut,
size_t dataOutAvailable,
size_t *dataOutMoved)
{
CCBlockCipherContext cryptCtx = (CCBlockCipherContext)ctx;
uint8_t *uInp = (uint8_t *)dataIn;
uint8_t *uOutp = (uint8_t *)dataOut;
size_t uInSize = dataInLen; size_t uOutSize = 0; size_t uOutLeft = dataOutAvailable; size_t toMove;
size_t blocks;
unsigned i;
bool needLeftOver;
const CCAlgInfo *algInfo;
size_t blockSize;
unsigned leftOver;
if((dataIn == NULL) || (dataOut == NULL) || (dataOutMoved == NULL)) {
return kCCParamError;
}
needLeftOver = !cryptCtx->encrypting && cryptCtx->pkcsPad;
algInfo = cryptCtx->algInfo;
blockSize = algInfo->blockSize;
size_t totalInBytes = dataInLen + cryptCtx->inBufSize;
size_t totalBlocks = totalInBytes / blockSize;
if(needLeftOver && (totalBlocks > 0)) {
if((totalBlocks * blockSize) == totalInBytes) {
totalBlocks--;
}
}
size_t totalOutBytes = totalBlocks * blockSize;
#if 0
dprintf("dataInLen %lu inBufSize %lu totalBlocks %lu dataOutAvailable %lu\n",
(unsigned long)dataInLen, (unsigned long)cryptCtx->inBufSize,
(unsigned long)totalBlocks, (unsigned long)dataOutAvailable);
#endif
if(totalOutBytes > dataOutAvailable) {
dprintf("CCBlockCipherUpdate: o/f encr %d totalOutBytes %lu dataOutAvailable %lu\n",
cryptCtx->encrypting, totalOutBytes, dataOutAvailable);
return kCCBufferTooSmall;
}
if(cryptCtx->inBufSize) {
toMove = blockSize - cryptCtx->inBufSize;
if(toMove > uInSize) {
toMove = uInSize;
}
if(cryptCtx->encrypting && cryptCtx->doCbc) {
uint8_t *dst = &cryptCtx->inBuf[cryptCtx->inBufSize];
for(i=0; i<toMove; i++) {
*dst ^= *uInp++;
dst++;
}
}
else {
memmove(cryptCtx->inBuf+cryptCtx->inBufSize, uInp, toMove);
uInp += toMove;
}
uInSize -= toMove;
cryptCtx->inBufSize += toMove;
if((cryptCtx->inBufSize == blockSize) && !((uInSize == 0) && needLeftOver)) {
if(uOutLeft < blockSize) {
dprintf("kCCBufferTooSmall 3: uOutLeft %lu\n",
(unsigned long)uOutLeft);
return kCCBufferTooSmall;
}
cryptCtx->update(cryptCtx->algCtx, cryptCtx->inBuf, uOutp);
if(cryptCtx->doCbc) {
if(cryptCtx->encrypting) {
memmove(cryptCtx->inBuf, uOutp, blockSize);
}
else {
uint8_t *src = cryptCtx->chainBuf;
for(i=0; i<blockSize; i++) {
uOutp[i] ^= *src++;
}
memmove(cryptCtx->chainBuf, cryptCtx->inBuf, blockSize);
}
}
uOutSize += blockSize;
uOutp += blockSize;
uOutLeft -= blockSize;
cryptCtx->inBufSize = 0;
}
}
if(uInSize == 0) {
*dataOutMoved = uOutSize;
return kCCSuccess;
}
leftOver = uInSize % blockSize;
if((leftOver == 0) && needLeftOver) {
leftOver = blockSize;
}
toMove = uInSize - leftOver;
blocks = toMove / blockSize;
if(cryptCtx->updateBlocks && !cryptCtx->doCbc && (blocks != 0)) {
size_t thisMove = blocks * blockSize;
cryptCtx->updateBlocks(cryptCtx->algCtx, uInp, blocks, uOutp);
uOutSize += thisMove;
uOutp += thisMove;
uInp += thisMove;
uOutLeft -= thisMove;
toMove -= thisMove;
}
else if(cryptCtx->encrypting) {
while(toMove) {
if(uOutLeft < blockSize) {
dprintf("kCCBufferTooSmall 1: uOutLeft %lu\n",
(unsigned long)uOutLeft);
return kCCBufferTooSmall;
}
if(!cryptCtx->doCbc) {
cryptCtx->update(cryptCtx->algCtx, uInp, uOutp);
}
else {
uint8_t *dst = cryptCtx->inBuf;
for(i=0; i<blockSize; i++) {
*dst ^= uInp[i];
dst++;
}
cryptCtx->update(cryptCtx->algCtx, cryptCtx->inBuf, uOutp);
memmove(cryptCtx->inBuf, uOutp, blockSize);
}
uOutSize += blockSize;
uOutp += blockSize;
uInp += blockSize;
uOutLeft -= blockSize;
toMove -= blockSize;
}
}
else {
while(toMove) {
if(uOutLeft < blockSize) {
dprintf("kCCBufferTooSmall 2: uOutLeft %lu toMove %lu\n",
(unsigned long)uOutLeft, (unsigned long)toMove);
return kCCBufferTooSmall;
}
if(cryptCtx->doCbc) {
uint8_t *src = cryptCtx->chainBuf;
memmove(cryptCtx->inBuf, uInp, blockSize);
cryptCtx->update(cryptCtx->algCtx, uInp, uOutp);
for(i=0; i<blockSize; i++) {
uOutp[i] ^= *src++;
}
memmove(cryptCtx->chainBuf, cryptCtx->inBuf, blockSize);
}
else {
cryptCtx->update(cryptCtx->algCtx, uInp, uOutp);
}
uOutSize += blockSize;
uOutp += blockSize;
uInp += blockSize;
uOutLeft -= blockSize;
toMove -= blockSize;
}
}
if(leftOver) {
if(cryptCtx->encrypting && cryptCtx->doCbc) {
uint8_t *dst = cryptCtx->inBuf;
for(i=0; i<leftOver; i++) {
*dst ^= *uInp++;
dst++;
}
}
else {
memmove(cryptCtx->inBuf, uInp, leftOver);
}
}
cryptCtx->inBufSize = leftOver;
*dataOutMoved = uOutSize;
return kCCSuccess;
}
static CCCryptorStatus CCBlockCipherFinal(
void *ctx,
void *dataOut,
size_t dataOutAvailable,
size_t *dataOutMoved)
{
size_t uOutSize = 0; size_t actMoved;
unsigned i;
const CCAlgInfo *algInfo;
size_t blockSize;
CCCryptorStatus ourRtn = kCCSuccess;
CCBlockCipherContext cryptCtx = (CCBlockCipherContext)ctx;
if((dataOut == NULL) || (dataOutMoved == NULL)) {
return kCCParamError;
}
algInfo = cryptCtx->algInfo;
blockSize = algInfo->blockSize;
if(cryptCtx->encrypting) {
size_t required = 0;
if((cryptCtx->inBufSize != 0) || (cryptCtx->pkcsPad)) {
required = blockSize;
}
if(required > dataOutAvailable) {
dprintf("CCBlockCipherFinal: o/f (1): required %lu dataOutAvailable %lu\n",
required, dataOutAvailable);
return kCCBufferTooSmall;
}
if(cryptCtx->pkcsPad) {
size_t padSize = blockSize - cryptCtx->inBufSize;
uint8_t *padPtr = cryptCtx->inBuf + cryptCtx->inBufSize;
if(!cryptCtx->doCbc) {
for(i=0; i<padSize; i++) {
*padPtr++ = padSize;
}
}
else {
for(i=0; i<padSize; i++) {
*padPtr++ ^= padSize;
}
}
cryptCtx->inBufSize = blockSize;
}
if(cryptCtx->inBufSize) {
if(cryptCtx->inBufSize != blockSize) {
ourRtn = kCCParamError;
goto errOut;
}
cryptCtx->update(cryptCtx->algCtx, cryptCtx->inBuf, dataOut);
uOutSize += blockSize;
cryptCtx->inBufSize = 0;
}
*dataOutMoved = uOutSize;
}
else {
if(cryptCtx->inBufSize == 0) {
if(cryptCtx->pkcsPad) {
ourRtn = kCCParamError;
goto errOut;
}
else {
*dataOutMoved = 0;
goto errOut;
}
}
if(cryptCtx->inBufSize != blockSize) {
ourRtn = kCCParamError;
goto errOut;
}
if(dataOutAvailable < blockSize) {
dprintf("CCBlockCipherFinal: o/f (2): dataOutAvailable %lu\n",
(unsigned long)dataOutAvailable);
return kCCBufferTooSmall;
}
cryptCtx->update(cryptCtx->algCtx, cryptCtx->inBuf, dataOut);
if(cryptCtx->doCbc) {
uint8_t *src = cryptCtx->chainBuf;
uint8_t *dst = dataOut;
for(i=0; i<blockSize; i++) {
*dst ^= *src++;
dst++;
}
}
actMoved = blockSize;
if(cryptCtx->pkcsPad) {
unsigned char *cp = (unsigned char *)dataOut;
unsigned padSize = cp[blockSize - 1];
if(padSize > blockSize) {
ourRtn = kCCDecodeError;
goto errOut;
}
uint8_t *padPtr = cp + blockSize - padSize;
unsigned i;
for(i=0; i<padSize; i++) {
if(*padPtr++ != padSize) {
ourRtn = kCCDecodeError;
goto errOut;
}
}
actMoved -= padSize;
}
*dataOutMoved = actMoved;
}
errOut:
return ourRtn;
}
static CCCryptorStatus CCBlockCipherReset(
void *ctx,
const void *iv)
{
CCBlockCipherContext cryptCtx = (CCBlockCipherContext)ctx;
if(cryptCtx->cbc) {
ccBlockCipherSetIV(cryptCtx, iv);
}
cryptCtx->inBufSize = 0;
return kCCSuccess;
}
static size_t CCBlockCipherOutputSize(
void *ctx, size_t inputLength, bool final)
{
CCBlockCipherContext cryptCtx = (CCBlockCipherContext)ctx;
size_t blockSize = cryptCtx->algInfo->blockSize;
size_t totalInBytes = inputLength + cryptCtx->inBufSize;
size_t blocks = totalInBytes / blockSize;
if(final && cryptCtx->encrypting && cryptCtx->pkcsPad) {
blocks++;
}
return blocks * blockSize;
}
static CCCryptorStatus CCBlockCipherOneShotSize(
CCOperation op,
CCAlgorithm alg,
CCOptions options,
size_t inputLen,
size_t *outputLen)
{
const CCAlgInfo *algInfo = ccBlockCipherFindAlgInfo(alg);
size_t totalBlocks;
size_t blockSize;
if(algInfo == NULL) {
return kCCParamError;
}
blockSize = algInfo->blockSize;
totalBlocks = (inputLen + blockSize - 1) / blockSize;
if((op == kCCEncrypt) && (options & kCCOptionPKCS7Padding)) {
if((totalBlocks * blockSize) == inputLen) {
totalBlocks++;
}
}
*outputLen = totalBlocks * blockSize;
return kCCSuccess;
}
const CCCryptSpiCallouts ccBlockCipherCallouts =
{
CCBlockCipherContextSize,
CCBlockCipherInit,
CCBlockCipherRelease,
CCBlockCipherUpdate,
CCBlockCipherFinal,
CCBlockCipherReset,
CCBlockCipherOutputSize,
CCBlockCipherOneShotSize
};