#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <Security/cssm.h>
#include <Security/cssmapple.h>
#include "cspwrap.h"
#include "common.h"
#include "cspdlTesting.h"
#define LOOPS_DEF 50
#define MIN_PTEXT_SIZE 8
#define MAX_PTEXT_SIZE 0x10000
typedef enum {
ALG_ASC = 1,
ALG_DES,
ALG_RC2,
ALG_RC4,
ALG_RC5,
ALG_3DES,
ALG_AES,
ALG_BFISH,
ALG_CAST,
ALG_NULL
} SymAlg;
#define ALG_FIRST ALG_ASC
#define ALG_LAST ALG_CAST
#define PBE_ENABLE 0
#define PWD_LENGTH_MAX 64
#define MAX_DATA_SIZE (100000 + 100)
#define LOOP_NOTIFY 20
#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 (s=ASC; d=DES; 3=3DES; 2=RC2; 4=RC4; 5=RC5; a=AES;\n");
printf(" b=Blowfish; c=CAST; n=Null; default=all)\n");
printf(" l=loops (default=%d; 0=forever)\n", LOOPS_DEF);
printf(" n=minPtextSize (default=%d)\n", MIN_PTEXT_SIZE);
printf(" x=maxPtextSize (default=%d)\n", MAX_PTEXT_SIZE);
printf(" k=keySizeInBits\n");
printf(" r(eference keys only)\n");
printf(" e(xport)\n");
printf(" d (no DB open)\n");
printf(" p=pauseInterval (default=0, no pause)\n");
printf(" o (no padding, well-aligned plaintext)\n");
printf(" u (no multi-update ops)\n");
printf(" U (only multi-update ops)\n");
printf(" m (CSP mallocs out bufs)\n");
printf(" D (CSP/DL; default = bare CSP)\n");
printf(" K (key gen only)\n");
printf(" v(erbose)\n");
printf(" q(uiet)\n");
printf(" h(elp)\n");
exit(1);
}
static CSSM_DATA seedData = {8, (uint8 *)"12345678"};
#define PBE_DERIVE_ALG_ODD CSSM_ALGID_PKCS5_PBKDF1_MD5
#define PBE_DERIVE_ALG_EVEN CSSM_ALGID_PKCS5_PBKDF1_SHA1
#define EQUAL_TEXT_IN_PLACE 1
static int doTest(CSSM_CSP_HANDLE cspHand,
CSSM_DATA_PTR ptext,
uint32 keyAlg, uint32 encrAlg, uint32 mode,
uint32 padding,
uint32 effectiveKeySizeInBits,
CSSM_BOOL refKey,
CSSM_DATA_PTR pwd, CSSM_BOOL stagedEncr,
CSSM_BOOL stagedDecr,
CSSM_BOOL mallocPtext, CSSM_BOOL mallocCtext, CSSM_BOOL quiet,
CSSM_BOOL keyGenOnly,
CSSM_BOOL expectEqualText) {
CSSM_KEY_PTR symKey = NULL;
CSSM_DATA ctext = {0, NULL};
CSSM_DATA rptext = {0, NULL};
CSSM_RETURN crtn;
int rtn = 0;
uint32 keySizeInBits;
CSSM_DATA initVector;
uint32 rounds = 0;
keySizeInBits = (effectiveKeySizeInBits + 7) & ~7;
if(keySizeInBits == effectiveKeySizeInBits) {
effectiveKeySizeInBits = 0;
}
if(encrAlg == CSSM_ALGID_RC5) {
unsigned die = genRand(1,3);
switch(die) {
case 1:
rounds = 8;
break;
case 2:
rounds = 12;
break;
case 3:
rounds = 16;
break;
}
}
if(pwd == NULL) {
symKey = cspGenSymKey(cspHand,
keyAlg,
"noLabel",
7,
CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT,
keySizeInBits,
refKey);
}
else {
uint32 pbeAlg;
initVector.Data = NULL; initVector.Length = 0;
if(ptext->Data[0] & 1) {
pbeAlg = PBE_DERIVE_ALG_ODD;
}
else {
pbeAlg = PBE_DERIVE_ALG_EVEN;
}
symKey = cspDeriveKey(cspHand,
pbeAlg,
keyAlg,
"noLabel",
7,
CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT,
keySizeInBits,
refKey,
pwd,
&seedData,
1, &initVector);
if(initVector.Data != NULL) {
CSSM_FREE(initVector.Data);
}
}
if(symKey == NULL) {
rtn = testError(quiet);
goto abort;
}
if(keyGenOnly) {
rtn = 0;
goto abort;
}
initVector.Data = (uint8 *)"someStrangeInitVect";
switch(encrAlg) {
case CSSM_ALGID_AES:
case CSSM_ALGID_NONE:
initVector.Length = 16;
break;
default:
initVector.Length = 8;
break;
}
if(stagedEncr) {
crtn = cspStagedEncrypt(cspHand,
encrAlg,
mode,
padding,
symKey,
NULL, effectiveKeySizeInBits,
0, rounds,
&initVector,
ptext,
&ctext,
CSSM_TRUE); }
else {
const CSSM_DATA *ptextPtr = ptext;
if(expectEqualText && mallocCtext && CSPDL_NOPAD_ENFORCE_SIZE) {
ctext.Data = (uint8 *)appMalloc(ptext->Length, NULL);
if(ctext.Data == NULL) {
printf("memmory failure\n");
rtn = testError(quiet);
goto abort;
}
ctext.Length = ptext->Length;
#if EQUAL_TEXT_IN_PLACE
memmove(ctext.Data, ptext->Data, ptext->Length);
ptextPtr = &ctext;
#endif
}
crtn = cspEncrypt(cspHand,
encrAlg,
mode,
padding,
symKey,
NULL, effectiveKeySizeInBits,
rounds,
&initVector,
ptextPtr,
&ctext,
mallocCtext);
}
if(crtn) {
rtn = testError(quiet);
goto abort;
}
if(expectEqualText && (ptext->Length != ctext.Length)) {
printf("***ctext/ptext length mismatch: ptextLen %lu ctextLen %lu\n",
ptext->Length, ctext.Length);
rtn = testError(quiet);
if(rtn) {
goto abort;
}
}
logSize(("###ctext len %lu\n", ctext.Length));
if(stagedDecr) {
crtn = cspStagedDecrypt(cspHand,
encrAlg,
mode,
padding,
symKey,
NULL, effectiveKeySizeInBits,
0, rounds,
&initVector,
&ctext,
&rptext,
CSSM_TRUE); }
else {
const CSSM_DATA *ctextPtr = &ctext;
if(expectEqualText && mallocPtext && CSPDL_NOPAD_ENFORCE_SIZE) {
rptext.Data = (uint8 *)appMalloc(ctext.Length, NULL);
if(rptext.Data == NULL) {
printf("memmory failure\n");
rtn = testError(quiet);
goto abort;
}
rptext.Length = ctext.Length;
#if EQUAL_TEXT_IN_PLACE
memmove(rptext.Data, ctext.Data, ctext.Length);
ctextPtr = &rptext;
#endif
}
crtn = cspDecrypt(cspHand,
encrAlg,
mode,
padding,
symKey,
NULL, effectiveKeySizeInBits,
rounds,
&initVector,
ctextPtr,
&rptext,
mallocPtext);
}
if(crtn) {
rtn = testError(quiet);
goto abort;
}
logSize(("###rptext len %lu\n", rptext.Length));
if(ptext->Length != rptext.Length) {
printf("Ptext length mismatch: expect %lu, got %lu\n", ptext->Length, rptext.Length);
rtn = testError(quiet);
if(rtn) {
goto abort;
}
}
if(memcmp(ptext->Data, rptext.Data, ptext->Length)) {
printf("***data miscompare\n");
rtn = testError(quiet);
}
abort:
if(symKey != NULL) {
if(cspFreeKey(cspHand, symKey)) {
printf("Error freeing privKey\n");
rtn = 1;
}
CSSM_FREE(symKey);
}
appFreeCssmData(&rptext, CSSM_FALSE);
appFreeCssmData(&ctext, CSSM_FALSE);
return rtn;
}
int main(int argc, char **argv)
{
int arg;
char *argp;
unsigned loop;
CSSM_DATA ptext;
CSSM_CSP_HANDLE cspHand;
CSSM_BOOL stagedEncr;
CSSM_BOOL stagedDecr;
CSSM_BOOL mallocCtext;
CSSM_BOOL mallocPtext;
CSSM_BOOL refKey;
const char *algStr;
uint32 keyAlg; uint32 encrAlg; int i;
int currAlg; CSSM_DATA_PTR pPwd;
CSSM_DATA pwd;
uint32 actKeySizeInBits;
int rtn = 0;
uint32 blockSize; CSSM_BOOL expectEqualText;
CSSM_BOOL keySizeSpec = CSSM_FALSE; SymAlg minAlg = ALG_FIRST;
SymAlg maxAlg = ALG_LAST;
unsigned loops = LOOPS_DEF;
CSSM_BOOL verbose = CSSM_FALSE;
unsigned minPtextSize = MIN_PTEXT_SIZE;
unsigned maxPtextSize = MAX_PTEXT_SIZE;
CSSM_BOOL quiet = CSSM_FALSE;
unsigned pauseInterval = 0;
uint32 mode;
uint32 padding;
CSSM_BOOL noDbOpen = CSSM_FALSE;
CSSM_BOOL bareCsp = CSSM_TRUE;
CSSM_BOOL keyGenOnly = CSSM_FALSE;
CSSM_BOOL noPadding = CSSM_FALSE;
CSSM_BOOL multiEnable = CSSM_TRUE;
CSSM_BOOL multiOnly = CSSM_FALSE;
CSSM_BOOL refKeysOnly = CSSM_FALSE;
CSSM_BOOL cspMallocs = CSSM_FALSE;
#if macintosh
argc = ccommand(&argv);
#endif
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;
break;
case 'b':
minAlg = maxAlg = ALG_BFISH;
break;
case 'c':
minAlg = maxAlg = ALG_CAST;
break;
case 'n':
minAlg = maxAlg = ALG_NULL;
break;
default:
usage(argv);
}
break;
case 'l':
loops = atoi(&argp[2]);
break;
case 'n':
minPtextSize = atoi(&argp[2]);
break;
case 'x':
maxPtextSize = atoi(&argp[2]);
break;
case 'r':
refKeysOnly = CSSM_TRUE;
break;
case 'k':
actKeySizeInBits = atoi(&argp[2]);
keySizeSpec = CSSM_TRUE;
break;
case 'v':
verbose = CSSM_TRUE;
break;
case 'D':
bareCsp = CSSM_FALSE;
#if CSPDL_ALL_KEYS_ARE_REF
refKeysOnly = CSSM_TRUE;
#endif
break;
case 'q':
quiet = CSSM_TRUE;
break;
case 'p':
pauseInterval = atoi(&argp[2]);;
break;
case 'o':
noPadding = CSSM_TRUE;
break;
case 'd':
noDbOpen = CSSM_TRUE;
break;
case 'K':
keyGenOnly = CSSM_TRUE;
break;
case 'u':
multiEnable = CSSM_FALSE;
break;
case 'U':
multiOnly = CSSM_TRUE;
break;
case 'm':
cspMallocs = CSSM_TRUE;
break;
case 'h':
default:
usage(argv);
}
}
if(multiOnly && !multiEnable) {
printf("***can't specify multi disable and multi only\n");
exit(1);
}
if(minPtextSize > maxPtextSize) {
printf("***minPtextSize must be <= maxPtextSize\n");
usage(argv);
}
pwd.Data = (uint8 *)CSSM_MALLOC(PWD_LENGTH_MAX);
ptext.Data = (uint8 *)CSSM_MALLOC(maxPtextSize);
if(ptext.Data == NULL) {
printf("Insufficient heap space\n");
exit(1);
}
printf("Starting symTest; args: ");
for(i=1; i<argc; i++) {
printf("%s ", argv[i]);
}
printf("\n");
cspHand = cspDlDbStartup(bareCsp, NULL);
if(cspHand == 0) {
exit(1);
}
if(pauseInterval) {
fpurge(stdin);
printf("Top of test; hit CR to proceed: ");
getchar();
}
for(currAlg=minAlg; currAlg<=maxAlg; currAlg++) {
mode = CSSM_ALGMODE_NONE;
padding = CSSM_PADDING_NONE;
blockSize = 0; expectEqualText = CSSM_FALSE;
switch(currAlg) {
case ALG_ASC:
encrAlg = keyAlg = CSSM_ALGID_ASC;
algStr = "ASC";
break;
case ALG_DES:
encrAlg = keyAlg = CSSM_ALGID_DES;
algStr = "DES";
if(noPadding) {
mode = CSSM_ALGMODE_CBC_IV8;
blockSize = 8;
expectEqualText = CSSM_TRUE;
}
else {
mode = CSSM_ALGMODE_CBCPadIV8;
padding = CSSM_PADDING_PKCS1;
}
break;
case ALG_3DES:
keyAlg = CSSM_ALGID_3DES_3KEY;
encrAlg = CSSM_ALGID_3DES_3KEY_EDE;
algStr = "3DES";
if(noPadding) {
mode = CSSM_ALGMODE_CBC_IV8;
blockSize = 8;
expectEqualText = CSSM_TRUE;
}
else {
mode = CSSM_ALGMODE_CBCPadIV8;
padding = CSSM_PADDING_PKCS1;
}
break;
case ALG_RC2:
encrAlg = keyAlg = CSSM_ALGID_RC2;
algStr = "RC2";
if(noPadding) {
mode = CSSM_ALGMODE_CBC_IV8;
blockSize = 8;
expectEqualText = CSSM_TRUE;
}
else {
mode = CSSM_ALGMODE_CBCPadIV8;
padding = CSSM_PADDING_PKCS1; }
break;
case ALG_RC4:
encrAlg = keyAlg = CSSM_ALGID_RC4;
algStr = "RC4";
mode = CSSM_ALGMODE_NONE;
expectEqualText = CSSM_TRUE; break;
case ALG_RC5:
encrAlg = keyAlg = CSSM_ALGID_RC5;
algStr = "RC5";
if(noPadding) {
mode = CSSM_ALGMODE_CBC_IV8;
blockSize = 8;
expectEqualText = CSSM_TRUE;
}
else {
mode = CSSM_ALGMODE_CBCPadIV8;
padding = CSSM_PADDING_PKCS1; }
break;
case ALG_AES:
encrAlg = keyAlg = CSSM_ALGID_AES;
algStr = "AES";
if(noPadding) {
mode = CSSM_ALGMODE_CBC_IV8;
blockSize = 16;
expectEqualText = CSSM_TRUE;
}
else {
mode = CSSM_ALGMODE_CBCPadIV8;
padding = CSSM_PADDING_PKCS5;
}
break;
case ALG_BFISH:
encrAlg = keyAlg = CSSM_ALGID_BLOWFISH;
algStr = "Blowfish";
if(noPadding) {
mode = CSSM_ALGMODE_CBC_IV8;
blockSize = 8;
expectEqualText = CSSM_TRUE;
}
else {
mode = CSSM_ALGMODE_CBCPadIV8;
padding = CSSM_PADDING_PKCS5;
}
break;
case ALG_CAST:
encrAlg = keyAlg = CSSM_ALGID_CAST;
algStr = "CAST";
if(noPadding) {
mode = CSSM_ALGMODE_CBC_IV8;
blockSize = 8;
expectEqualText = CSSM_TRUE;
}
else {
mode = CSSM_ALGMODE_CBCPadIV8;
padding = CSSM_PADDING_PKCS5;
}
break;
case ALG_NULL:
encrAlg = keyAlg = CSSM_ALGID_NONE;
algStr = "NULL";
if(noPadding) {
mode = CSSM_ALGMODE_CBC_IV8;
blockSize = 16;
expectEqualText = CSSM_TRUE;
}
else {
mode = CSSM_ALGMODE_CBCPadIV8;
padding = CSSM_PADDING_PKCS5;
}
break;
}
if(!quiet || verbose) {
printf("Testing alg %s\n", algStr);
}
for(loop=1; ; loop++) {
simpleGenData(&ptext, minPtextSize, maxPtextSize);
if(blockSize) {
ptext.Length = ((ptext.Length + blockSize - 1) / blockSize) * blockSize;
}
if(!keySizeSpec) {
actKeySizeInBits = randKeySizeBits(keyAlg, OT_Encrypt);
}
pPwd = (loop & 1) ? &pwd : NULL;
if(multiEnable) {
if(multiOnly) {
stagedEncr = stagedDecr = CSSM_TRUE;
}
else {
stagedEncr = (loop & 2) ? CSSM_TRUE : CSSM_FALSE;
stagedDecr = (loop & 4) ? CSSM_TRUE : CSSM_FALSE;
}
}
else {
stagedEncr = CSSM_FALSE;
stagedDecr = CSSM_FALSE;
}
if(!stagedEncr && !cspMallocs) {
mallocCtext = (ptext.Data[0] & 1) ? CSSM_TRUE : CSSM_FALSE;
}
else {
mallocCtext = CSSM_FALSE;
}
if(!stagedDecr && !cspMallocs) {
mallocPtext = (ptext.Data[0] & 2) ? CSSM_TRUE : CSSM_FALSE;
}
else {
mallocPtext = CSSM_FALSE;
}
if(refKeysOnly) {
refKey = CSSM_TRUE;
}
else {
refKey = (ptext.Data[0] & 4) ? CSSM_TRUE : CSSM_FALSE;
}
#if !PBE_ENABLE
pPwd = NULL;
#endif
if(!quiet) {
if(verbose || ((loop % LOOP_NOTIFY) == 0)) {
printf("..loop %d text size %lu keySizeBits %u\n",
loop, (unsigned long)ptext.Length, (unsigned)actKeySizeInBits);
if(verbose) {
printf(" refKey %d derive %d stagedEncr %d stagedDecr %d mallocCtext %d "
"mallocPtext %d\n",
(int)refKey, (pPwd == NULL) ? 0 : 1, (int)stagedEncr, (int)stagedDecr,
(int)mallocCtext, (int)mallocPtext);
}
}
}
#if PBE_ENABLE
if(pPwd != NULL) {
simpleGenData(pPwd, APPLE_PBE_MIN_PASSWORD, PWD_LENGTH_MAX);
}
#endif
if(doTest(cspHand,
&ptext,
keyAlg,
encrAlg,
mode,
padding,
actKeySizeInBits,
refKey,
pPwd,
stagedEncr,
stagedDecr,
mallocPtext,
mallocCtext,
quiet,
keyGenOnly,
expectEqualText)) {
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:
cspShutdown(cspHand, bareCsp);
if(pauseInterval) {
fpurge(stdin);
printf("ModuleDetach/Unload complete; hit CR to exit: ");
getchar();
}
if((rtn == 0) && !quiet) {
printf("%s test complete\n", argv[0]);
}
CSSM_FREE(pwd.Data);
CSSM_FREE(ptext.Data);
return rtn;
}