]> git.saurik.com Git - apple/security.git/blobdiff - SecurityTests/cspxutils/ccSymTest/ccSymTest.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / SecurityTests / cspxutils / ccSymTest / ccSymTest.cpp
diff --git a/SecurityTests/cspxutils/ccSymTest/ccSymTest.cpp b/SecurityTests/cspxutils/ccSymTest/ccSymTest.cpp
new file mode 100644 (file)
index 0000000..8340150
--- /dev/null
@@ -0,0 +1,777 @@
+/* Copyright (c) 2006,2008 Apple Inc.
+ *
+ * ccSymTest.c - test CommonCrypto symmetric encrypt/decrypt.
+ */
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <CommonCrypto/CommonCryptor.h>
+#include "common.h"
+#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
+
+/*
+ * Defaults.
+ */
+#define LOOPS_DEF              500
+#define MIN_DATA_SIZE  8
+#define MAX_DATA_SIZE  10000                                           /* bytes */
+#define MAX_KEY_SIZE   kCCKeySizeMaxRC4                        /* bytes */
+#define MAX_BLOCK_SIZE kCCBlockSizeAES128                      /* bytes */
+#define LOOP_NOTIFY            250
+
+/*
+ * Enumerate algs our own way to allow iteration.
+ */
+typedef enum {
+       ALG_AES_128 = 1,        /* 128 bit block, 128 bit key */
+       ALG_AES_192,            /* 128 bit block, 192 bit key */
+       ALG_AES_256,            /* 128 bit block, 256 bit key */
+       ALG_DES,
+       ALG_3DES,
+       ALG_CAST,
+       ALG_RC4,
+       /* these aren't in CommonCrypto (yet?) */
+       ALG_RC2,
+       ALG_RC5,
+       ALG_BFISH,
+       ALG_ASC,
+       ALG_NULL                                        /* normally not used */
+} 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);
+}
+
+/* max context size */
+#define CC_MAX_CTX_SIZE        kCCContextSizeRC4
+
+/* 
+ * We write a marker at end of expected output and at end of caller-allocated 
+ * CCCryptorRef, and check at the end to make sure they weren't written 
+ */
+#define MARKER_LENGTH  8
+#define MARKER_BYTE            0x7e
+
+/* 
+ * Test harness for CCCryptor with lots of options. 
+ */
+CCCryptorStatus doCCCrypt(
+       bool forEncrypt,
+       CCAlgorithm encrAlg,                    
+       bool doCbc,
+       bool doPadding,
+       const void *keyBytes, size_t keyLen,
+       const void *iv,
+       bool randUpdates,
+       bool inPlace,                                                           /* !doPadding only */
+       size_t ctxSize,                                                         /* if nonzero, we allocate ctx */
+       bool askOutSize,
+       const uint8_t *inText, size_t inTextLen,
+       uint8_t **outText, size_t *outTextLen)          /* both returned, WE malloc */
+{
+       CCCryptorRef    cryptor = NULL;
+       CCCryptorStatus crtn;
+       CCOperation             op = forEncrypt ? kCCEncrypt : kCCDecrypt;
+       CCOptions               options = 0;
+       uint8_t                 *outBuf = NULL;                 /* mallocd output buffer */
+       uint8_t                 *outp;                                  /* running ptr into outBuf */
+       const uint8             *inp;                                   /* running ptr into inText */
+       size_t                  outLen;                                 /* bytes remaining in outBuf */
+       size_t                  toMove;                                 /* bytes remaining in inText */
+       size_t                  thisMoveOut;                    /* output from CCCryptUpdate()/CCCryptFinal() */
+       size_t                  outBytes;                               /* total bytes actually produced in outBuf */
+       char                    ctx[CC_MAX_CTX_SIZE];   /* for CCCryptorCreateFromData() */
+       uint8_t                 *textMarker = NULL;             /* 8 bytes of marker here after expected end of 
+                                                                                        * output */
+       char                    *ctxMarker = NULL;              /* ditto for caller-provided context */
+       unsigned                dex;
+       size_t                  askedOutSize;                   /* from the lib */
+       size_t                  thisOutLen;                             /* dataOutAvailable we use */
+       
+       if(ctxSize > CC_MAX_CTX_SIZE) {
+               printf("***HEY! Adjust CC_MAX_CTX_SIZE!\n");
+               exit(1);
+       }
+       if(!doCbc) {
+               options |= kCCOptionECBMode;
+       }
+       if(doPadding) {
+               options |= kCCOptionPKCS7Padding;
+       }
+       
+       /* just hack this one */
+       outLen = inTextLen;
+       if(forEncrypt) {
+               outLen += MAX_BLOCK_SIZE;
+       }
+       
+       outBuf = (uint8_t *)malloc(outLen + MARKER_LENGTH);
+       
+       /* library should not touch this memory */
+       textMarker = outBuf + outLen;
+       memset(textMarker, MARKER_BYTE, MARKER_LENGTH);
+       
+       /* subsequent errors to errOut: */
+
+       if(inPlace) {
+               memmove(outBuf, inText, inTextLen);
+               inp = outBuf;
+       }
+       else {
+               inp = inText;
+       }
+
+       if(!randUpdates) {
+               /* one shot */
+               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;
+       }
+       
+       /* random multi updates */
+       if(ctxSize) {
+               size_t ctxSizeCreated;
+               
+               if(askOutSize) {
+                       crtn = CCCryptorCreateFromData(op, encrAlg, options,
+                               keyBytes, keyLen, iv,
+                               ctx, 0 /* ctxSize */,
+                               &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;             /* total to go */
+       outp = outBuf;
+       outBytes = 0;                   /* bytes actually produced in outBuf */
+       
+       while(toMove) {
+               uint32 thisMoveIn;                      /* input to CCryptUpdate() */
+               
+               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) {
+               /* Final is not needed if padding is disabled */
+               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,                    /* if CBC, use NULL IV */
+       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;          /* mallocd by doCCCrypt */
+       size_t                  ctextLen = 0;
+       uint8_t                 *rptext = NULL;         /* mallocd by doCCCrypt */
+       size_t                  rptextLen;
+       CCCryptorStatus crtn;
+       int                             rtn = 0;
+       
+       /* random key */
+       appGetRandomBytes(keyBytes, keySizeInBytes);
+       
+       /* random IV if needed */
+       if(doCbc) {
+               if(nullIV) {
+                       memset(iv, 0, MAX_BLOCK_SIZE);
+                       
+                       /* flip a coin, give one side NULL, the other size zeroes */
+                       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)); 
+       
+       /* compare ptext, rptext */
+       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;
+       bool                            stagedDecr;
+       bool                            doPadding;
+       bool                            doCbc;
+       bool                            nullIV;
+       const char                      *algStr;
+       CCAlgorithm                     encrAlg;        
+       int                                     i;
+       int                                     currAlg;                // ALG_xxx
+       uint32                          minKeySizeInBytes;
+       uint32                          maxKeySizeInBytes;
+       uint32                          keySizeInBytes;
+       int                                     rtn = 0;
+       uint32                          blockSize;              // for noPadding case
+       size_t                          ctxSize;                // always set per alg
+       size_t                          ctxSizeUsed;    // passed to doTest
+       bool                            askOutSize;             // inquire output size each op
+       
+       /*
+        * User-spec'd params
+        */
+       bool            keySizeSpec = false;            // false: use rand key size
+       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;            // true: user calls doPadding, const
+       bool            cbcSpec = false;                        // ditto for doCbc
+       bool            stagedSpec = false;                     // ditto for stagedEncr and stagedDecr
+       bool            inPlace = false;                        // en/decrypt in place for ECB
+       bool            allocCtxSpec = false;           // use allocCtx
+       bool            allocCtx = false;                       // allocate context ourself
+       
+       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) {
+                                       /* we left them in the switch but we can't use them */
+                                       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);
+       }
+       /* ptext length set in test loop */
+       
+       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);
+                       
+                       /* per-loop settings */
+                       if(!keySizeSpec) {
+                               if(minKeySizeInBytes == maxKeySizeInBytes) {
+                                       keySizeInBytes = minKeySizeInBytes;
+                               }
+                               else {
+                                       keySizeInBytes = genRand(minKeySizeInBytes, maxKeySizeInBytes);
+                               }
+                       }
+                       if(blockSize == 0) {
+                               /* stream cipher */
+                               doCbc = false;
+                               doPadding = false;
+                       }
+                       else {
+                               if(!cbcSpec) {
+                                       doCbc = isBitSet(0, loop);
+                               }
+                               if(!paddingSpec) {
+                                       doPadding = isBitSet(1, loop);
+                               }
+                       }
+                       if(!doPadding && (blockSize != 0)) {
+                               /* align plaintext */
+                               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;
+                       }
+               }       /* main loop */
+               if(rtn) {
+                       break;
+               }
+               
+       }       /* for algs */
+       
+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;
+}