#include <CommonCrypto/CommonCryptor.h>
#include "CommonCryptorPriv.h"
#include "BlockCipher.h"
#include "StreamCipher.h"
#include <stdlib.h>
#include <strings.h>
#include <stddef.h>
typedef struct _CCCryptor {
bool weMallocd;
size_t contextSize;
CCOperation op;
CCAlgorithm alg;
const CCCryptSpiCallouts *callouts;
char spiCtx[1];
} CCCryptor;
static const CCCryptSpiCallouts *ccSpiCallouts(
CCOperation op,
CCAlgorithm alg)
{
switch(alg) {
case kCCAlgorithmAES128:
case kCCAlgorithmDES:
case kCCAlgorithm3DES:
case kCCAlgorithmCAST:
return &ccBlockCipherCallouts;
case kCCAlgorithmRC4:
return &ccRC4Callouts;
default:
return NULL;
}
}
CCCryptorStatus CCCryptorCreate(
CCOperation op,
CCAlgorithm alg,
CCOptions options,
const void *key,
size_t keyLength,
const void *iv,
CCCryptorRef *cryptorRef)
{
const CCCryptSpiCallouts *callouts;
CCCryptorStatus crtn;
CCCryptor *cryptor = NULL;
size_t requiredLen;
if(cryptorRef == NULL) {
return kCCParamError;
}
callouts = ccSpiCallouts(op, alg);
if(callouts == NULL) {
return kCCParamError;
}
crtn = callouts->contextSize(op, alg, &requiredLen);
if(crtn) {
return crtn;
}
requiredLen += offsetof(CCCryptor, spiCtx);
cryptor = (CCCryptor *)malloc(requiredLen);
if(cryptor == NULL) {
return kCCMemoryFailure;
}
cryptor->weMallocd = true;
cryptor->contextSize = requiredLen;
cryptor->op = op;
cryptor->alg = alg;
cryptor->callouts = callouts;
crtn = callouts->init(cryptor->spiCtx, op, alg, options,
key, keyLength, iv);
if(crtn) {
free(cryptor);
return crtn;
}
*cryptorRef = cryptor;
return kCCSuccess;
}
CCCryptorStatus CCCryptorCreateFromData(
CCOperation op,
CCAlgorithm alg,
CCOptions options,
const void *key,
size_t keyLength,
const void *iv,
const void *data,
size_t dataLength,
CCCryptorRef *cryptorRef,
size_t *dataUsed)
{
const CCCryptSpiCallouts *callouts;
CCCryptorStatus crtn;
CCCryptor *cryptor = NULL;
size_t requiredLen;
if((data == NULL) || (cryptorRef == NULL)) {
return kCCParamError;
}
callouts = ccSpiCallouts(op, alg);
if(callouts == NULL) {
return kCCParamError;
}
crtn = callouts->contextSize(op, alg, &requiredLen);
if(crtn) {
return crtn;
}
requiredLen += offsetof(CCCryptor, spiCtx);
if(dataLength < requiredLen) {
if(dataUsed != NULL) {
*dataUsed = requiredLen;
}
return kCCBufferTooSmall;
}
cryptor = (CCCryptor *)data;
cryptor->weMallocd = false;
cryptor->contextSize = requiredLen;
cryptor->op = op;
cryptor->alg = alg;
cryptor->callouts = callouts;
crtn = callouts->init(cryptor->spiCtx, op, alg, options,
key, keyLength, iv);
if(crtn) {
return crtn;
}
*cryptorRef = cryptor;
if(dataUsed != NULL) {
*dataUsed = requiredLen;
}
return kCCSuccess;
}
CCCryptorStatus CCCryptorRelease(
CCCryptorRef cryptor)
{
bool weMallocd;
size_t zeroSize;
if(cryptor == NULL) {
return kCCParamError;
}
if(cryptor->callouts->release) {
cryptor->callouts->release(cryptor->spiCtx);
zeroSize = offsetof(CCCryptor, spiCtx);
}
else {
zeroSize = cryptor->contextSize;
}
weMallocd = cryptor->weMallocd;
memset(cryptor, 0, zeroSize);
if(weMallocd) {
free(cryptor);
}
return kCCSuccess;
}
CCCryptorStatus CCCryptorUpdate(
CCCryptorRef cryptor,
const void *dataIn,
size_t dataInLength,
void *dataOut,
size_t dataOutAvailable,
size_t *dataOutMoved)
{
if(cryptor == NULL) {
return kCCParamError;
}
return cryptor->callouts->update(cryptor->spiCtx,
dataIn, dataInLength,
dataOut, dataOutAvailable, dataOutMoved);
}
CCCryptorStatus CCCryptorFinal(
CCCryptorRef cryptor,
void *dataOut,
size_t dataOutAvailable,
size_t *dataOutMoved)
{
if(cryptor == NULL) {
return kCCParamError;
}
return cryptor->callouts->final(cryptor->spiCtx,
dataOut, dataOutAvailable, dataOutMoved);
}
size_t CCCryptorGetOutputLength(
CCCryptorRef cryptor,
size_t inputLength,
bool final)
{
if(cryptor == NULL) {
return 0;
}
return cryptor->callouts->outputSize(cryptor->spiCtx,
inputLength, final);
}
CCCryptorStatus CCCryptorReset(
CCCryptorRef cryptor,
const void *iv)
{
if(cryptor == NULL) {
return kCCParamError;
}
if(cryptor->callouts->reset == NULL) {
return kCCUnimplemented;
}
return cryptor->callouts->reset(cryptor->spiCtx, iv);
}
CCCryptorStatus CCCrypt(
CCOperation op,
CCAlgorithm alg,
CCOptions options,
const void *key,
size_t keyLength,
const void *iv,
const void *dataIn,
size_t dataInLength,
void *dataOut,
size_t dataOutAvailable,
size_t *dataOutMoved)
{
const CCCryptSpiCallouts *callouts;
size_t outputSize;
CCCryptorRef cryptor = NULL;
CCCryptorStatus crtn;
size_t totalMoved = 0;
size_t remaining;
size_t thisMove;
char *outp;
if(dataOutMoved == NULL) {
return kCCParamError;
}
callouts = ccSpiCallouts(op, alg);
if(callouts == NULL) {
return kCCParamError;
}
crtn = callouts->oneShotSize(op, alg, options, dataInLength, &outputSize);
if(crtn) {
return crtn;
}
if(outputSize > dataOutAvailable) {
*dataOutMoved = outputSize;
return kCCBufferTooSmall;
}
crtn = CCCryptorCreate(op, alg, options,
key, keyLength, iv, &cryptor);
if(crtn) {
return crtn;
}
remaining = dataOutAvailable;
outp = (char *)dataOut;
if((dataIn != NULL) && (dataInLength != 0)) {
crtn = CCCryptorUpdate(cryptor, dataIn, dataInLength,
outp, remaining, &thisMove);
if(crtn) {
goto errOut;
}
outp += thisMove;
totalMoved += thisMove;
remaining -= thisMove;
}
crtn = CCCryptorFinal(cryptor, outp, remaining, &thisMove);
if(crtn == kCCSuccess) {
totalMoved += thisMove;
*dataOutMoved = totalMoved;
}
errOut:
if(cryptor) {
CCCryptorRelease(cryptor);
}
return crtn;
}