#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <CommonCrypto/CommonCryptor.h>
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#include "common.h"
#define LOOPS_DEF 500
#define MIN_DATA_SIZE 8
#define MAX_DATA_SIZE 10000
#define MAX_KEY_SIZE kCCKeySizeMaxRC4
#define MAX_BLOCK_SIZE kCCBlockSizeAES128
#define LOOP_NOTIFY 250
typedef enum {
ALG_AES_128 = 1,
ALG_AES_192,
ALG_AES_256,
ALG_DES,
ALG_3DES,
ALG_CAST,
ALG_RC4,
ALG_RC2,
ALG_RC5,
ALG_BFISH,
ALG_ASC,
ALG_NULL
} SymAlg;
#define ALG_FIRST ALG_AES_128
#define ALG_LAST ALG_RC4
#define LOG_SIZE 0
#if LOG_SIZE
#define logSize(s) printf s
#else
#define logSize(s)
#endif
static void usage(char **argv)
{
printf("usage: %s [options]\n", argv[0]);
printf(" Options:\n");
printf(" a=algorithm (d=DES; 3=3DES; a=AES128; n=AES192; A=AES256; \n");
printf(" c=CAST; 4=RC4; default=all)\n");
printf(" l=loops (default=%d; 0=forever)\n", LOOPS_DEF);
printf(" m=maxPtextSize (default=%d)\n", MAX_DATA_SIZE);
printf(" n=minPtextSize (default=%d)\n", MIN_DATA_SIZE);
printf(" k=keySizeInBytes\n");
printf(" p=pauseInterval (default=0, no pause)\n");
printf(" o (no padding, well-aligned plaintext)\n");
printf(" e (ECB only)\n");
printf(" E (CBC only, no ECB)\n");
printf(" u (no multi-update ops)\n");
printf(" U (only multi-update ops)\n");
printf(" x (always allocate context)\n");
printf(" X (never allocate context)\n");
printf(" v(erbose)\n");
printf(" q(uiet)\n");
printf(" h(elp)\n");
exit(1);
}
static void printCCError(const char *str, CCCryptorStatus crtn)
{
const char *errStr;
char unknownStr[200];
switch(crtn) {
case kCCSuccess: errStr = "kCCSuccess"; break;
case kCCParamError: errStr = "kCCParamError"; break;
case kCCBufferTooSmall: errStr = "kCCBufferTooSmall"; break;
case kCCMemoryFailure: errStr = "kCCMemoryFailure"; break;
case kCCAlignmentError: errStr = "kCCAlignmentError"; break;
case kCCDecodeError: errStr = "kCCDecodeError"; break;
case kCCUnimplemented: errStr = "kCCUnimplemented"; break;
default:
sprintf(unknownStr, "Unknown(%ld)\n", (long)crtn);
errStr = unknownStr;
break;
}
printf("***%s returned %s\n", str, errStr);
}
#define CC_MAX_CTX_SIZE kCCContextSizeRC4
#define MARKER_LENGTH 8
#define MARKER_BYTE 0x7e
CCCryptorStatus doCCCrypt(
bool forEncrypt,
CCAlgorithm encrAlg,
bool doCbc,
bool doPadding,
const void *keyBytes, size_t keyLen,
const void *iv,
bool randUpdates,
bool inPlace,
size_t ctxSize,
bool askOutSize,
const uint8_t *inText, size_t inTextLen,
uint8_t **outText, size_t *outTextLen)
{
CCCryptorRef cryptor = NULL;
CCCryptorStatus crtn;
CCOperation op = forEncrypt ? kCCEncrypt : kCCDecrypt;
CCOptions options = 0;
uint8_t *outBuf = NULL;
uint8_t *outp;
const uint8 *inp;
size_t outLen;
size_t toMove;
size_t thisMoveOut;
size_t outBytes;
char ctx[CC_MAX_CTX_SIZE];
uint8_t *textMarker = NULL;
char *ctxMarker = NULL;
unsigned dex;
size_t askedOutSize;
size_t thisOutLen;
if(ctxSize > CC_MAX_CTX_SIZE) {
printf("***HEY! Adjust CC_MAX_CTX_SIZE!\n");
exit(1);
}
if(!doCbc) {
options |= kCCOptionECBMode;
}
if(doPadding) {
options |= kCCOptionPKCS7Padding;
}
outLen = inTextLen;
if(forEncrypt) {
outLen += MAX_BLOCK_SIZE;
}
outBuf = (uint8_t *)malloc(outLen + MARKER_LENGTH);
memset(outBuf, 0xEE, outLen + MARKER_LENGTH);
textMarker = outBuf + outLen;
memset(textMarker, MARKER_BYTE, MARKER_LENGTH);
if(inPlace) {
memmove(outBuf, inText, inTextLen);
inp = outBuf;
}
else {
inp = inText;
}
if(!randUpdates) {
if(askOutSize) {
crtn = CCCrypt(op, encrAlg, options,
keyBytes, keyLen, iv,
inp, inTextLen,
outBuf, 0, &askedOutSize);
if(crtn != kCCBufferTooSmall) {
printf("***Did not get kCCBufferTooSmall as expected\n");
printf(" alg %d inTextLen %lu cbc %d padding %d keyLen %lu\n",
(int)encrAlg, (unsigned long)inTextLen, (int)doCbc, (int)doPadding,
(unsigned long)keyLen);
printCCError("CCCrypt", crtn);
crtn = -1;
goto errOut;
}
outLen = askedOutSize;
}
crtn = CCCrypt(op, encrAlg, options,
keyBytes, keyLen, iv,
inp, inTextLen,
outBuf, outLen, &outLen);
if(crtn) {
printCCError("CCCrypt", crtn);
goto errOut;
}
*outText = outBuf;
*outTextLen = outLen;
goto errOut;
}
if(ctxSize) {
size_t ctxSizeCreated;
if(askOutSize) {
crtn = CCCryptorCreateFromData(op, encrAlg, options,
keyBytes, keyLen, iv,
ctx, 0 ,
&cryptor, &askedOutSize);
if(crtn != kCCBufferTooSmall) {
printf("***Did not get kCCBufferTooSmall as expected\n");
printCCError("CCCryptorCreateFromData", crtn);
crtn = -1;
goto errOut;
}
ctxSize = askedOutSize;
}
crtn = CCCryptorCreateFromData(op, encrAlg, options,
keyBytes, keyLen, iv,
ctx, ctxSize, &cryptor, &ctxSizeCreated);
if(crtn) {
printCCError("CCCryptorCreateFromData", crtn);
return crtn;
}
ctxMarker = ctx + ctxSizeCreated;
memset(ctxMarker, MARKER_BYTE, MARKER_LENGTH);
}
else {
crtn = CCCryptorCreate(op, encrAlg, options,
keyBytes, keyLen, iv,
&cryptor);
if(crtn) {
printCCError("CCCryptorCreate", crtn);
return crtn;
}
}
toMove = inTextLen;
outp = outBuf;
outBytes = 0;
while(toMove) {
uint32 thisMoveIn;
thisMoveIn = genRand(1, toMove);
logSize(("###ptext segment len %lu\n", (unsigned long)thisMoveIn));
if(askOutSize) {
thisOutLen = CCCryptorGetOutputLength(cryptor, thisMoveIn, false);
}
else {
thisOutLen = outLen;
}
crtn = CCCryptorUpdate(cryptor, inp, thisMoveIn,
outp, thisOutLen, &thisMoveOut);
if(crtn) {
printCCError("CCCryptorUpdate", crtn);
goto errOut;
}
inp += thisMoveIn;
toMove -= thisMoveIn;
outp += thisMoveOut;
outLen -= thisMoveOut;
outBytes += thisMoveOut;
}
if(doPadding) {
if(askOutSize) {
thisOutLen = CCCryptorGetOutputLength(cryptor, 0, true);
}
else {
thisOutLen = outLen;
}
crtn = CCCryptorFinal(cryptor, outp, thisOutLen, &thisMoveOut);
}
else {
thisMoveOut = 0;
crtn = kCCSuccess;
}
if(crtn) {
printCCError("CCCryptorFinal", crtn);
goto errOut;
}
outBytes += thisMoveOut;
*outText = outBuf;
*outTextLen = outBytes;
crtn = kCCSuccess;
for(dex=0; dex<MARKER_LENGTH; dex++) {
if(textMarker[dex] != MARKER_BYTE) {
printf("***lib scribbled on our textMarker memory (op=%s)!\n",
forEncrypt ? "encrypt" : "decrypt");
crtn = (CCCryptorStatus)-1;
}
}
if(ctxSize) {
for(dex=0; dex<MARKER_LENGTH; dex++) {
if(ctxMarker[dex] != MARKER_BYTE) {
printf("***lib scribbled on our ctxMarker memory (op=%s)!\n",
forEncrypt ? "encrypt" : "decrypt");
crtn = (CCCryptorStatus)-1;
}
}
}
errOut:
if(crtn) {
if(outBuf) {
free(outBuf);
}
}
if(cryptor) {
CCCryptorRelease(cryptor);
}
return crtn;
}
static int doTest(const uint8_t *ptext,
size_t ptextLen,
CCAlgorithm encrAlg,
bool doCbc,
bool doPadding,
bool nullIV,
uint32 keySizeInBytes,
bool stagedEncr,
bool stagedDecr,
bool inPlace,
size_t ctxSize,
bool askOutSize,
bool quiet)
{
uint8_t keyBytes[MAX_KEY_SIZE];
uint8_t iv[MAX_BLOCK_SIZE];
uint8_t *ivPtrEncrypt;
uint8_t *ivPtrDecrypt;
uint8_t *ctext = NULL;
size_t ctextLen = 0;
uint8_t *rptext = NULL;
size_t rptextLen;
CCCryptorStatus crtn;
int rtn = 0;
appGetRandomBytes(keyBytes, keySizeInBytes);
if(doCbc) {
if(nullIV) {
memset(iv, 0, MAX_BLOCK_SIZE);
if(genRand(1,2) == 1) {
ivPtrEncrypt = NULL;
ivPtrDecrypt = iv;
}
else {
ivPtrEncrypt = iv;
ivPtrDecrypt = NULL;
}
}
else {
appGetRandomBytes(iv, MAX_BLOCK_SIZE);
ivPtrEncrypt = iv;
ivPtrDecrypt = iv;
}
}
else {
ivPtrEncrypt = NULL;
ivPtrDecrypt = NULL;
}
crtn = doCCCrypt(true, encrAlg, doCbc, doPadding,
keyBytes, keySizeInBytes, ivPtrEncrypt,
stagedEncr, inPlace, ctxSize, askOutSize,
ptext, ptextLen,
&ctext, &ctextLen);
if(crtn) {
rtn = testError(quiet);
if(rtn) {
goto abort;
}
}
logSize(("###ctext len %lu\n", ctextLen));
crtn = doCCCrypt(false, encrAlg, doCbc, doPadding,
keyBytes, keySizeInBytes, ivPtrDecrypt,
stagedDecr, inPlace, ctxSize, askOutSize,
ctext, ctextLen,
&rptext, &rptextLen);
if(crtn) {
rtn = testError(quiet);
if(rtn) {
goto abort;
}
}
logSize(("###rptext len %lu\n", rptextLen));
if(ptextLen != rptextLen) {
printf("Ptext length mismatch: expect %lu, got %lu\n", ptextLen, rptextLen);
rtn = testError(quiet);
if(rtn) {
goto abort;
}
}
if(memcmp(ptext, rptext, ptextLen)) {
printf("***data miscompare\n");
rtn = testError(quiet);
}
abort:
if(ctext) {
free(ctext);
}
if(rptext) {
free(rptext);
}
return rtn;
}
bool isBitSet(unsigned bit, unsigned word)
{
if(bit > 31) {
printf("We don't have that many bits\n");
exit(1);
}
unsigned mask = 1 << bit;
return (word & mask) ? true : false;
}
int main(int argc, char **argv)
{
int arg;
char *argp;
unsigned loop;
uint8 *ptext;
size_t ptextLen;
bool stagedEncr = false;
bool stagedDecr = false;
bool doPadding;
bool doCbc = false;
bool nullIV;
const char *algStr;
CCAlgorithm encrAlg;
int i;
int currAlg; uint32 minKeySizeInBytes;
uint32 maxKeySizeInBytes;
uint32 keySizeInBytes = 0;
int rtn = 0;
uint32 blockSize; size_t ctxSize; size_t ctxSizeUsed; bool askOutSize;
bool keySizeSpec = false; SymAlg minAlg = ALG_FIRST;
SymAlg maxAlg = ALG_LAST;
unsigned loops = LOOPS_DEF;
bool verbose = false;
size_t minPtextSize = MIN_DATA_SIZE;
size_t maxPtextSize = MAX_DATA_SIZE;
bool quiet = false;
unsigned pauseInterval = 0;
bool paddingSpec = false; bool cbcSpec = false; bool stagedSpec = false; bool inPlace = false; bool allocCtxSpec = false; bool allocCtx = false;
for(arg=1; arg<argc; arg++) {
argp = argv[arg];
switch(argp[0]) {
case 'a':
if(argp[1] != '=') {
usage(argv);
}
switch(argp[2]) {
case 's':
minAlg = maxAlg = ALG_ASC;
break;
case 'd':
minAlg = maxAlg = ALG_DES;
break;
case '3':
minAlg = maxAlg = ALG_3DES;
break;
case '2':
minAlg = maxAlg = ALG_RC2;
break;
case '4':
minAlg = maxAlg = ALG_RC4;
break;
case '5':
minAlg = maxAlg = ALG_RC5;
break;
case 'a':
minAlg = maxAlg = ALG_AES_128;
break;
case 'n':
minAlg = maxAlg = ALG_AES_192;
break;
case 'A':
minAlg = maxAlg = ALG_AES_256;
break;
case 'b':
minAlg = maxAlg = ALG_BFISH;
break;
case 'c':
minAlg = maxAlg = ALG_CAST;
break;
default:
usage(argv);
}
if(maxAlg > ALG_LAST) {
usage(argv);
}
break;
case 'l':
loops = atoi(&argp[2]);
break;
case 'n':
minPtextSize = atoi(&argp[2]);
break;
case 'm':
maxPtextSize = atoi(&argp[2]);
break;
case 'k':
minKeySizeInBytes = maxKeySizeInBytes = atoi(&argp[2]);
keySizeSpec = true;
break;
case 'x':
allocCtxSpec = true;
allocCtx = true;
break;
case 'X':
allocCtxSpec = true;
allocCtx = false;
break;
case 'v':
verbose = true;
break;
case 'q':
quiet = true;
break;
case 'p':
pauseInterval = atoi(&argp[2]);;
break;
case 'o':
doPadding = false;
paddingSpec = true;
break;
case 'e':
doCbc = false;
cbcSpec = true;
break;
case 'E':
doCbc = true;
cbcSpec = true;
break;
case 'u':
stagedEncr = false;
stagedDecr = false;
stagedSpec = true;
break;
case 'U':
stagedEncr = true;
stagedDecr = true;
stagedSpec = true;
break;
case 'h':
default:
usage(argv);
}
}
ptext = (uint8 *)malloc(maxPtextSize);
if(ptext == NULL) {
printf("Insufficient heap space\n");
exit(1);
}
printf("Starting ccSymTest; args: ");
for(i=1; i<argc; i++) {
printf("%s ", argv[i]);
}
printf("\n");
if(pauseInterval) {
fpurge(stdin);
printf("Top of test; hit CR to proceed: ");
getchar();
}
for(currAlg=minAlg; currAlg<=maxAlg; currAlg++) {
switch(currAlg) {
case ALG_DES:
encrAlg = kCCAlgorithmDES;
blockSize = kCCBlockSizeDES;
minKeySizeInBytes = kCCKeySizeDES;
maxKeySizeInBytes = minKeySizeInBytes;
ctxSize = kCCContextSizeDES;
algStr = "DES";
break;
case ALG_3DES:
encrAlg = kCCAlgorithm3DES;
blockSize = kCCBlockSize3DES;
minKeySizeInBytes = kCCKeySize3DES;
maxKeySizeInBytes = minKeySizeInBytes;
ctxSize = kCCContextSize3DES;
algStr = "3DES";
break;
case ALG_AES_128:
encrAlg = kCCAlgorithmAES128;
blockSize = kCCBlockSizeAES128;
minKeySizeInBytes = kCCKeySizeAES128;
maxKeySizeInBytes = minKeySizeInBytes;
ctxSize = kCCContextSizeAES128;
algStr = "AES128";
break;
case ALG_AES_192:
encrAlg = kCCAlgorithmAES128;
blockSize = kCCBlockSizeAES128;
minKeySizeInBytes = kCCKeySizeAES192;
maxKeySizeInBytes = minKeySizeInBytes;
ctxSize = kCCContextSizeAES128;
algStr = "AES192";
break;
case ALG_AES_256:
encrAlg = kCCAlgorithmAES128;
blockSize = kCCBlockSizeAES128;
minKeySizeInBytes = kCCKeySizeAES256;
maxKeySizeInBytes = minKeySizeInBytes;
ctxSize = kCCContextSizeAES128;
algStr = "AES256";
break;
case ALG_CAST:
encrAlg = kCCAlgorithmCAST;
blockSize = kCCBlockSizeCAST;
minKeySizeInBytes = kCCKeySizeMinCAST;
maxKeySizeInBytes = kCCKeySizeMaxCAST;
ctxSize = kCCContextSizeCAST;
algStr = "CAST";
break;
case ALG_RC4:
encrAlg = kCCAlgorithmRC4;
blockSize = 0;
minKeySizeInBytes = kCCKeySizeMinRC4;
maxKeySizeInBytes = kCCKeySizeMaxRC4;
ctxSize = kCCContextSizeRC4;
algStr = "RC4";
break;
default:
printf("***BRRZAP!\n");
exit(1);
}
if(!quiet || verbose) {
printf("Testing alg %s\n", algStr);
}
for(loop=1; ; loop++) {
ptextLen = genRand(minPtextSize, maxPtextSize);
appGetRandomBytes(ptext, ptextLen);
if(!keySizeSpec) {
if(minKeySizeInBytes == maxKeySizeInBytes) {
keySizeInBytes = minKeySizeInBytes;
}
else {
keySizeInBytes = genRand(minKeySizeInBytes, maxKeySizeInBytes);
}
}
if(blockSize == 0) {
doCbc = false;
doPadding = false;
}
else {
if(!cbcSpec) {
doCbc = isBitSet(0, loop);
}
if(!paddingSpec) {
doPadding = isBitSet(1, loop);
}
}
if(!doPadding && (blockSize != 0)) {
ptextLen = (ptextLen / blockSize) * blockSize;
if(ptextLen == 0) {
ptextLen = blockSize;
}
}
if(!stagedSpec) {
stagedEncr = isBitSet(2, loop);
stagedDecr = isBitSet(3, loop);
}
if(doCbc) {
nullIV = isBitSet(4, loop);
}
else {
nullIV = false;
}
inPlace = isBitSet(5, loop);
if(allocCtxSpec) {
ctxSizeUsed = allocCtx ? ctxSize : 0;
}
else if(isBitSet(6, loop)) {
ctxSizeUsed = ctxSize;
}
else {
ctxSizeUsed = 0;
}
askOutSize = isBitSet(7, loop);
if(!quiet) {
if(verbose || ((loop % LOOP_NOTIFY) == 0)) {
printf("..loop %3d ptextLen %lu keyLen %d cbc=%d padding=%d stagedEncr=%d "
"stagedDecr=%d\n",
loop, (unsigned long)ptextLen, (int)keySizeInBytes,
(int)doCbc, (int)doPadding,
(int)stagedEncr, (int)stagedDecr);
printf(" nullIV %d inPlace %d ctxSize %d askOutSize %d\n",
(int)nullIV, (int)inPlace, (int)ctxSizeUsed, (int)askOutSize);
}
}
if(doTest(ptext, ptextLen,
encrAlg, doCbc, doPadding, nullIV,
keySizeInBytes,
stagedEncr, stagedDecr, inPlace, ctxSizeUsed, askOutSize,
quiet)) {
rtn = 1;
break;
}
if(pauseInterval && ((loop % pauseInterval) == 0)) {
char c;
fpurge(stdin);
printf("Hit CR to proceed, q to abort: ");
c = getchar();
if(c == 'q') {
goto testDone;
}
}
if(loops && (loop == loops)) {
break;
}
}
if(rtn) {
break;
}
}
testDone:
if(pauseInterval) {
fpurge(stdin);
printf("ModuleDetach/Unload complete; hit CR to exit: ");
getchar();
}
if((rtn == 0) && !quiet) {
printf("%s test complete\n", argv[0]);
}
free(ptext);
return rtn;
}