]> git.saurik.com Git - apple/security.git/blobdiff - SecurityTests/cspxutils/contextReuse/contextReuse.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / SecurityTests / cspxutils / contextReuse / contextReuse.cpp
diff --git a/SecurityTests/cspxutils/contextReuse/contextReuse.cpp b/SecurityTests/cspxutils/contextReuse/contextReuse.cpp
new file mode 100644 (file)
index 0000000..993d179
--- /dev/null
@@ -0,0 +1,739 @@
+/* 
+ * contextReuse.cpp 
+ *
+ * Verify proper operation of symmetric CSP algorithms when CSSM_CC_HANDLE 
+ * (crypto context) is reused. Tests specifically for Radar 4551700, which 
+ * dealt with a problem with the Gladman AES implementation handling the 
+ * same context for an encrypt followed by a decrypt; other situations
+ * are tested here (e.g. encrypt followed by another encrypt including CBC)
+ * as well as all CSP symmetric algorithms. 
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <Security/cssm.h>
+#include <Security/cssmapple.h>
+#include "cspwrap.h"
+#include "common.h"
+#include <string.h>
+#include "cspdlTesting.h"
+
+/*
+ * Defaults.
+ */
+#define LOOPS_DEF              200
+
+#define MIN_DATA_SIZE  8
+#define MAX_DATA_SIZE  10000                                                   /* bytes */
+#define MAX_KEY_SIZE   MAX_KEY_SIZE_RC245_BYTES                /* bytes */
+#define LOOP_NOTIFY            20
+
+#define RAW_MODE                       CSSM_ALGMODE_ECB                /* doesn't work for BSAFE */
+#define RAW_MODE_STREAM                CSSM_ALGMODE_NONE
+#define COOKED_MODE                    CSSM_ALGMODE_CBCPadIV8
+
+#define RAW_MODE_STR           "ECB"
+#define RAW_MODE_STREAM_STR    "None"
+#define COOKED_MODE_STR                "CBC/Pad"
+
+/*
+ * Enumerate algs our own way to allow iteration.
+ */
+typedef enum {
+       ALG_ASC = 1,                    // not tested - no reference available
+       ALG_DES = 1,
+       ALG_RC2,
+       ALG_RC4,
+       ALG_RC5,
+       ALG_3DES,
+       ALG_AES,
+       ALG_AES192,             /* 192 bit block */
+       ALG_AES256,             /* 256 bit block */
+       ALG_BFISH,
+       ALG_CAST
+} SymAlg;
+#define ALG_FIRST                      ALG_ASC
+#define ALG_LAST                       ALG_CAST
+
+static void usage(char **argv)
+{
+       printf("usage: %s [options]\n", argv[0]);
+       printf("   Options:\n");
+       printf("   a=algorithm (d=DES; 3=3DES3; 2=RC2; 4=RC4; 5=RC5; a=AES; A=AES192; \n");
+       printf("                6=AES256; b=Blowfish; c=CAST; s=ASC; default=all)\n");
+       printf("   l=loops (default=%d; 0=forever)\n", LOOPS_DEF);
+       printf("   k=keySizeInBits\n");
+       printf("   m=maxPtextSize (default=%d)\n", MAX_DATA_SIZE);
+       printf("   n=minPtextSize (default=%d)\n", MIN_DATA_SIZE);
+       printf("   p=pauseInterval (default=0, no pause)\n");
+       printf("   D (CSP/DL; default = bare CSP)\n");
+       printf("   v(erbose)\n");
+       printf("   q(uiet)\n");
+       printf("   h(elp)\n");
+       exit(1);
+}
+
+#define LOG_STAGED_OPS                         0
+#if            LOG_STAGED_OPS
+#define soprintf(s)    printf s
+#else
+#define soprintf(s)
+#endif
+
+/* 
+ * Multipurpose encrypt. Like cspStagedEncrypt(), but it takes a
+ * context handle and doesn't have as many options. 
+ */
+static CSSM_RETURN stagedEncrypt(
+       CSSM_CSP_HANDLE cspHand,
+       CSSM_CC_HANDLE  cryptHand,
+       uint32                  algorithm,                      // CSSM_ALGID_FEED, etc.
+       uint32                  cipherBlockSizeBytes,// optional
+       const CSSM_DATA *iv,                            // init vector, optional
+       const CSSM_DATA *ptext,
+       CSSM_DATA_PTR   ctext,                          // mallocd by caller, must be big enough!
+       CSSM_BOOL               multiUpdates)           // false:single update, true:multi updates
+{
+       CSSM_RETURN             crtn;
+       CSSM_SIZE               bytesEncrypted;                 // per update
+       CSSM_SIZE               bytesEncryptedTotal = 0;
+       CSSM_RETURN             ocrtn = CSSM_OK;                // 'our' crtn
+       unsigned                toMove;                                 // remaining
+       unsigned                thisMove;                               // bytes to encrypt on this update
+       CSSM_DATA               thisPtext;                              // running ptr into ptext
+       CSSM_DATA               thisCtext;                              // running ptr into ctext
+       CSSM_BOOL               restoreErr = CSSM_FALSE;
+       CSSM_RETURN             savedErr = CSSM_OK;
+       CSSM_SIZE               ctextLen;
+       
+       if(cipherBlockSizeBytes) {
+               crtn = AddContextAttribute(cryptHand,
+                       CSSM_ATTRIBUTE_BLOCK_SIZE,
+                       sizeof(uint32),
+                       CAT_Uint32,
+                       NULL,
+                       cipherBlockSizeBytes);
+               if(crtn) {
+                       printError("CSSM_UpdateContextAttributes", crtn);
+                       ocrtn = crtn;
+                       goto abort;
+               }
+       }
+       
+       thisPtext = *ptext;
+       thisCtext = *ctext;
+       memset(ctext->Data, 0, ctext->Length);
+       ctextLen = ctext->Length;
+       
+       crtn = CSSM_EncryptDataInit(cryptHand);
+       if(crtn) {
+               printError("CSSM_EncryptDataInit", crtn);
+               ocrtn = crtn;
+               goto abort;
+       }
+       
+       toMove = ptext->Length;
+       while(toMove) {
+               if(multiUpdates) {
+                       thisMove = genRand(1, toMove);
+               }
+               else {
+                       /* just do one pass thru this loop */
+                       thisMove = toMove;
+               }
+               thisPtext.Length = thisMove;
+               crtn = CSSM_EncryptDataUpdate(cryptHand,
+                       &thisPtext,
+                       1,
+                       &thisCtext,
+                       1,
+                       &bytesEncrypted);
+               if(crtn) {
+                       printError("CSSM_EncryptDataUpdate", crtn);
+                       ocrtn = crtn;
+                       goto abort;
+               }
+               soprintf(("*** EncryptDataUpdate: ptextLen 0x%x  bytesEncrypted 0x%x\n",
+                       (unsigned)thisMove, (unsigned)bytesEncrypted));
+
+               // NOTE: We return the proper length in ctext....
+               ctextLen            -= bytesEncrypted;          // bump out ptr
+               thisCtext.Length     = ctextLen;
+               thisCtext.Data      += bytesEncrypted;
+               bytesEncryptedTotal += bytesEncrypted;
+               thisPtext.Data      += thisMove;                        // bump in ptr
+               toMove              -= thisMove;
+       }
+       /* OK, one more */
+       crtn = CSSM_EncryptDataFinal(cryptHand, &thisCtext);
+       if(crtn) {
+               printError("CSSM_EncryptDataFinal", crtn);
+               savedErr = crtn;
+               restoreErr = CSSM_TRUE;
+               goto abort;
+       }
+       soprintf(("*** EncryptDataFinal: bytesEncrypted 0x%x\n",
+               (unsigned)thisCtext.Length));
+       bytesEncryptedTotal += thisCtext.Length;
+       ctext->Length = bytesEncryptedTotal;
+abort:
+       if(restoreErr) {
+               /* give caller the error from the encrypt */
+               ocrtn = savedErr;
+       }
+       return ocrtn;
+}
+
+/* 
+ * Multipurpose decrypt. Like cspStagedDecrypt(), but it takes a
+ * context handle and doesn't have as many options. 
+ */
+CSSM_RETURN stagedDecrypt(
+       CSSM_CSP_HANDLE cspHand,
+       CSSM_CC_HANDLE  cryptHand,
+       uint32                  algorithm,                      // CSSM_ALGID_FEED, etc.
+       uint32                  cipherBlockSizeBytes,// optional
+       const CSSM_DATA *iv,                            // init vector, optional
+       const CSSM_DATA *ctext,
+       CSSM_DATA_PTR   ptext,                          // mallocd by caller, must be big enough!
+       CSSM_BOOL               multiUpdates)           // false:single update, true:multi updates
+{
+       CSSM_RETURN             crtn;
+       CSSM_SIZE               bytesDecrypted;                 // per update
+       CSSM_SIZE               bytesDecryptedTotal = 0;
+       CSSM_RETURN             ocrtn = CSSM_OK;                // 'our' crtn
+       unsigned                toMove;                                 // remaining
+       unsigned                thisMove;                               // bytes to decrypt on this update
+       CSSM_DATA               thisCtext;                              // running ptr into ptext
+       CSSM_DATA               thisPtext;                              // running ptr into ctext
+       CSSM_SIZE               ptextLen;
+       
+       if(cipherBlockSizeBytes) {
+               crtn = AddContextAttribute(cryptHand,
+                       CSSM_ATTRIBUTE_BLOCK_SIZE,
+                       sizeof(uint32),
+                       CAT_Uint32,
+                       NULL,
+                       cipherBlockSizeBytes);
+               if(crtn) {
+                       printError("CSSM_UpdateContextAttributes", crtn);
+                       ocrtn = crtn;
+                       goto abort;
+               }
+       }
+       
+       thisCtext = *ctext;
+       thisPtext = *ptext;
+       memset(ptext->Data, 0, ptext->Length);
+       ptextLen = ptext->Length;
+       
+       crtn = CSSM_DecryptDataInit(cryptHand);
+       if(crtn) {
+               printError("CSSM_DecryptDataInit", crtn);
+               ocrtn = crtn;
+               goto abort;
+       }
+       
+       toMove = ctext->Length;
+       while(toMove) {
+               if(multiUpdates) {
+                       thisMove = genRand(1, toMove);
+               }
+               else {
+                       /* just do one pass thru this loop */
+                       thisMove = toMove;
+               }
+               thisCtext.Length = thisMove;
+               crtn = CSSM_DecryptDataUpdate(cryptHand,
+                       &thisCtext,
+                       1,
+                       &thisPtext,
+                       1,
+                       &bytesDecrypted);
+               if(crtn) {
+                       printError("CSSM_DecryptDataUpdate", crtn);
+                       ocrtn = crtn;
+                       goto abort;
+               }
+               soprintf(("*** DecryptDataUpdate: ctextLen 0x%x  bytesDecrypted 0x%x\n",
+                       (unsigned)thisMove, (unsigned)bytesDecrypted));
+
+               // NOTE: We return the proper length in ptext....
+               ptextLen            -= bytesDecrypted;          // bump out ptr
+               thisPtext.Length     = ptextLen;
+               thisPtext.Data      += bytesDecrypted;
+               bytesDecryptedTotal += bytesDecrypted;
+               thisCtext.Data      += thisMove;                        // bump in ptr
+               toMove              -= thisMove;
+       }
+       /* OK, one more */
+       crtn = CSSM_DecryptDataFinal(cryptHand, &thisPtext);
+       if(crtn) {
+               printError("CSSM_DecryptDataFinal", crtn);
+               ocrtn = crtn;
+               goto abort;
+       }
+       soprintf(("*** DecryptDataFinal: bytesEncrypted 0x%x\n",
+               (unsigned)thisPtext.Length));
+       bytesDecryptedTotal += thisPtext.Length;
+       ptext->Length = bytesDecryptedTotal;
+abort:
+       return ocrtn;
+}
+
+static int doTest(
+       CSSM_CSP_HANDLE         cspHand,
+       const CSSM_DATA         *ptext,
+       const CSSM_DATA         *ctext1,
+       const CSSM_DATA         *ctext2,
+       const CSSM_DATA         *rptext,
+       const CSSM_DATA         *keyData,
+       const CSSM_DATA         *iv,
+       uint32                          keyAlg,                                 // CSSM_ALGID_xxx of the key
+       uint32                          encrAlg,                                // encrypt/decrypt
+       uint32                          encrMode,
+       uint32                          padding,
+       uint32                          keySizeInBits,
+       uint32                          cipherBlockSizeBytes,
+       CSSM_BOOL                       quiet)
+{
+       CSSM_DATA               lctext1;
+       CSSM_DATA               lctext2;
+       CSSM_DATA               lrptext;
+       int                             rtn = 0;
+       CSSM_RETURN             crtn;
+       CSSM_CC_HANDLE  ccHand1 = 0;
+       CSSM_CC_HANDLE  ccHand2 = 0;
+       CSSM_KEY                key1;
+       CSSM_KEY                key2;
+       uint8                   dummy[cipherBlockSizeBytes];
+       CSSM_DATA               dummyData = {cipherBlockSizeBytes, dummy};
+       
+       /* 
+        * generate two equivalent keys key1 and key2;
+        * generate two CC handles ccHand1, ccHand2;
+        * encrypt dummy data with ccHand1 to get it cooked;
+        * encrypt ptext with ccHand1 ==> ctext1;
+        * encrypt ptext with ccHand2 ==> ctext2;
+        * Compare ctext1 and ctext2;
+        * decrypt ctext1 with ccHand1, compare with ptext;
+        */
+       crtn = cspGenSymKeyWithBits(cspHand, keyAlg, CSSM_KEYUSE_ANY,
+               keyData, keySizeInBits / 8, &key1);
+       if(crtn) {
+               return crtn;
+       }
+       crtn = cspGenSymKeyWithBits(cspHand, keyAlg, CSSM_KEYUSE_ANY,
+               keyData, keySizeInBits / 8, &key2);
+       if(crtn) {
+               return crtn;
+       }
+       ccHand1 = genCryptHandle(cspHand, 
+               encrAlg, 
+               encrMode, 
+               padding,
+               &key1, 
+               NULL,           // pubKey
+               iv,
+               0,                      // effectiveKeySizeInBits
+               0);                     // rounds
+       if(ccHand1 == 0) {
+               return CSSMERR_CSP_INTERNAL_ERROR;
+       }
+       ccHand2 = genCryptHandle(cspHand, 
+               encrAlg, 
+               encrMode, 
+               padding,
+               &key2, 
+               NULL,           // pubKey
+               iv,
+               0,                      // effectiveKeySizeInBits
+               0);                     // rounds
+       if(ccHand2 == 0) {
+               return CSSMERR_CSP_INTERNAL_ERROR;
+       }
+        
+       /* dummy encrypt to heat up ccHand1 */
+       appGetRandomBytes(dummy, sizeof(dummy));
+       lctext1 = *ctext1;
+       crtn = stagedEncrypt(cspHand, ccHand1, encrAlg, cipherBlockSizeBytes, 
+               iv, &dummyData, &lctext1, CSSM_FALSE);
+       if(crtn) {
+               return crtn;
+       }
+       
+       /* encrypt ptext with ccHand1 and ccHand2, compare ctext */
+       lctext1 = *ctext1;
+       crtn = stagedEncrypt(cspHand, ccHand1, encrAlg, cipherBlockSizeBytes, 
+               iv, ptext, &lctext1, CSSM_TRUE);
+       if(crtn) {
+               return crtn;
+       }
+       lctext2 = *ctext2;
+       crtn = stagedEncrypt(cspHand, ccHand2, encrAlg, cipherBlockSizeBytes, 
+               iv, ptext, &lctext2, CSSM_TRUE);
+       if(crtn) {
+               return crtn;
+       }
+       if(!appCompareCssmData(&lctext1, &lctext2)) {
+               printf("***Ciphertext miscompare\n");
+               if(testError(quiet)) {
+                       return 1;
+               }
+       }
+
+       /* decrypt with ccHand1, compare with ptext */
+       lrptext = *rptext;
+       crtn = stagedDecrypt(cspHand, ccHand1, encrAlg, cipherBlockSizeBytes, 
+               iv, &lctext1, &lrptext, CSSM_TRUE);
+       if(crtn) {
+               return crtn;
+       }
+       if(!appCompareCssmData(&lctext1, &lctext2)) {
+               printf("***Plaintext miscompare\n");
+               if(testError(quiet)) {
+                       return 1;
+               }
+       }
+       
+       if(ccHand1) {
+               CSSM_DeleteContext(ccHand1);
+       }
+       if(ccHand2) {
+               CSSM_DeleteContext(ccHand2);
+       }
+       return rtn;
+}
+
+
+int main(int argc, char **argv)
+{
+       int                                     arg;
+       char                            *argp;
+       unsigned                        loop;
+       CSSM_DATA                       ptext;
+       CSSM_DATA                       ctext1;
+       CSSM_DATA                       ctext2;
+       CSSM_DATA                       rptext;
+       CSSM_CSP_HANDLE         cspHand;
+       const char                      *algStr;
+       uint32                          keyAlg;                                 // CSSM_ALGID_xxx of the key
+       uint32                          encrAlg;                                // CSSM_ALGID_xxx of encr/decr
+       unsigned                        currAlg;                                // ALG_xxx
+       uint32                          keySizeInBits;
+       int                                     rtn = 0;
+       CSSM_DATA                       keyData;
+       CSSM_DATA                       initVector;
+       uint32                          minTextSize;
+       uint32                          rawMode;
+       uint32                          cookedMode;
+       const char                      *rawModeStr;
+       const char                      *cookedModeStr;
+       uint32                          algBlockSizeBytes;
+       
+       /*
+        * User-spec'd params
+        */
+       CSSM_BOOL       keySizeSpec = CSSM_FALSE;               // false: use rand key size
+       unsigned        minAlg = ALG_FIRST;
+       unsigned        maxAlg = ALG_LAST;
+       unsigned        loops = LOOPS_DEF;
+       CSSM_BOOL       verbose = CSSM_FALSE;
+       CSSM_BOOL       quiet = CSSM_FALSE;
+       unsigned        pauseInterval = 0;
+       uint32          padding;
+       CSSM_BOOL       bareCsp = CSSM_TRUE;
+       unsigned        maxPtextSize = MAX_DATA_SIZE;
+       unsigned        minPtextSize = MIN_DATA_SIZE;
+       
+       for(arg=1; arg<argc; arg++) {
+               argp = argv[arg];
+               switch(argp[0]) {
+                       case 'a':
+                               if(argp[1] != '=') {
+                                       usage(argv);
+                               }
+                               switch(argp[2]) {
+                                       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 'A':
+                                               minAlg = maxAlg = ALG_AES192;
+                                               break;
+                                       case '6':
+                                               minAlg = maxAlg = ALG_AES256;
+                                               break;
+                                       case 'b':
+                                               minAlg = maxAlg = ALG_BFISH;
+                                               break;
+                                       case 'c':
+                                               minAlg = maxAlg = ALG_CAST;
+                                               break;
+                                       case 's':
+                                               minAlg = maxAlg = ALG_ASC;
+                                               break;
+                                       default:
+                                               usage(argv);
+                               }
+                               break;
+                   case 'l':
+                               loops = atoi(&argp[2]);
+                               break;
+                   case 'k':
+                       keySizeInBits = atoi(&argp[2]);
+                       keySizeSpec = CSSM_TRUE;
+                               break;
+                   case 'v':
+                       verbose = CSSM_TRUE;
+                               break;
+                       case 'D':
+                               bareCsp = CSSM_FALSE;
+                               break;
+                       case 'm':
+                               maxPtextSize = atoi(&argp[2]);
+                               break;
+                       case 'n':
+                               minPtextSize = atoi(&argp[2]);
+                               break;
+                   case 'q':
+                       quiet = CSSM_TRUE;
+                               break;
+                   case 'p':
+                       pauseInterval = atoi(&argp[2]);;
+                               break;
+                   case 'h':
+                   default:
+                               usage(argv);
+               }
+       }
+       ptext.Data = (uint8 *)CSSM_MALLOC(maxPtextSize);
+       if(ptext.Data == NULL) {
+               printf("Insufficient heap space\n");
+               exit(1);
+       }
+       /* ptext length set in test loop */
+       appSetupCssmData(&ctext1, 2 * maxPtextSize);
+       appSetupCssmData(&ctext2, 2 * maxPtextSize);
+       appSetupCssmData(&rptext, 2 * maxPtextSize);
+       
+       keyData.Data = (uint8 *)CSSM_MALLOC(MAX_KEY_SIZE);
+       if(keyData.Data == NULL) {
+               printf("Insufficient heap space\n");
+               exit(1);
+       }
+       keyData.Length = MAX_KEY_SIZE;
+
+       initVector.Data = (uint8 *)"someStrangeInitVect";       
+       
+       testStartBanner("contextReuse", argc, argv);
+
+       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++) {
+               /* some default values... */
+               padding           = CSSM_PADDING_PKCS5;
+               rawMode       = RAW_MODE;
+               cookedMode    = COOKED_MODE;
+               rawModeStr        = RAW_MODE_STR;
+               cookedModeStr = COOKED_MODE_STR;
+               padding           = CSSM_PADDING_PKCS5;
+               
+               switch(currAlg) {
+                       case ALG_DES:
+                               encrAlg = keyAlg = CSSM_ALGID_DES;
+                               algStr = "DES";
+                               algBlockSizeBytes  = 8;
+                               break;
+                       case ALG_3DES:
+                               /* currently the only one with different key and encr algs */
+                               keyAlg  = CSSM_ALGID_3DES_3KEY;
+                               encrAlg = CSSM_ALGID_3DES_3KEY_EDE;
+                               algStr = "3DES";
+                               algBlockSizeBytes = 8;
+                               break;
+                       case ALG_RC2:
+                               encrAlg = keyAlg = CSSM_ALGID_RC2;
+                               algStr = "RC2";
+                               algBlockSizeBytes  = 8;
+                               break;
+                       case ALG_RC4:
+                               encrAlg = keyAlg = CSSM_ALGID_RC4;
+                               algStr = "RC4";
+                               algBlockSizeBytes = 0;
+                               rawMode       = RAW_MODE_STREAM;
+                               cookedMode    = RAW_MODE_STREAM;
+                               rawModeStr        = RAW_MODE_STREAM_STR;
+                               cookedModeStr = RAW_MODE_STREAM_STR;
+                               break;
+                       case ALG_RC5:
+                               encrAlg = keyAlg = CSSM_ALGID_RC5;
+                               algStr = "RC5";
+                               algBlockSizeBytes = 8;
+                               break;
+                       case ALG_AES:
+                               encrAlg = keyAlg = CSSM_ALGID_AES;
+                               algStr = "AES";
+                               algBlockSizeBytes = 16;
+                               break;
+                       case ALG_AES192:
+                               encrAlg = keyAlg = CSSM_ALGID_AES;
+                               algStr = "AES192";
+                               algBlockSizeBytes = 24;
+                               break;
+                       case ALG_AES256:
+                               encrAlg = keyAlg = CSSM_ALGID_AES;
+                               algStr = "AES256";
+                               algBlockSizeBytes = 32;
+                               break;
+                       case ALG_BFISH:
+                               encrAlg = keyAlg = CSSM_ALGID_BLOWFISH;
+                               algStr = "Blowfish";
+                               algBlockSizeBytes = 8;
+                               break;
+                       case ALG_CAST:
+                               encrAlg = keyAlg = CSSM_ALGID_CAST;
+                               algStr = "CAST";
+                               algBlockSizeBytes = 8;
+                               break;
+               }
+               
+               /* assume for now all algs require IV */
+               initVector.Length = algBlockSizeBytes ? algBlockSizeBytes : 8;
+               
+               if(!quiet || verbose) {
+                       printf("Testing alg %s\n", algStr);
+               }
+               for(loop=1; ; loop++) {
+                       /* mix up raw/cooked */
+                       uint32 mode;
+                       const char *modeStr;
+                       CSSM_BOOL paddingEnabled;
+                       
+                       if(loop & 1) {
+                               mode = rawMode;
+                               modeStr = rawModeStr;
+                       }
+                       else {
+                               mode = cookedMode;
+                               modeStr = cookedModeStr;
+                       }
+                       switch(mode) {
+                               case CSSM_ALGMODE_CBCPadIV8:
+                                       paddingEnabled = CSSM_TRUE;
+                                       break;
+                               default:
+                                       /* all others - right? */
+                                       paddingEnabled = CSSM_FALSE;
+                                       break;
+                       }
+                       minTextSize = minPtextSize;     // default
+                       if(!paddingEnabled && algBlockSizeBytes && (minTextSize < algBlockSizeBytes)) {
+                               /* i.e., no padding, adjust min ptext size */
+                               minTextSize = algBlockSizeBytes;
+                       }
+                       simpleGenData(&ptext, minTextSize, maxPtextSize);
+                       if(!paddingEnabled && algBlockSizeBytes) {
+                               /* align ptext */
+                               ptext.Length = (ptext.Length / algBlockSizeBytes) * algBlockSizeBytes;
+                       }
+                       
+                       simpleGenData(&keyData, MAX_KEY_SIZE, MAX_KEY_SIZE);
+
+                       if(!keySizeSpec) {
+                               /* random but byte-aligned */
+                               keySizeInBits = randKeySizeBits(keyAlg, OT_Encrypt);
+                               keySizeInBits = (keySizeInBits + 7) & ~7;
+                       }
+                       /* else constant, spec'd by user, may be 0 (default per alg) */
+                       if(!quiet) {
+                               if(verbose || ((loop % LOOP_NOTIFY) == 0)) {
+                                       if(algBlockSizeBytes) {
+                                               printf("..loop %d text size %lu keySizeBits %u"
+                                                       " blockSize %u mode %s\n",
+                                                       loop, (unsigned long)ptext.Length, (unsigned)keySizeInBits,
+                                                       (unsigned)algBlockSizeBytes, modeStr);
+                                       }
+                                       else {
+                                               printf("..loop %d text size %lu keySizeBits %u"
+                                                       " mode %s\n",
+                                                       loop, (unsigned long)ptext.Length, (unsigned)keySizeInBits,
+                                                       modeStr);
+                                       }
+                               }
+                       }
+                       
+                       if(doTest(cspHand,
+                                       &ptext,
+                                       &ctext1,
+                                       &ctext2,
+                                       &rptext,
+                                       &keyData,
+                                       &initVector,
+                                       keyAlg,
+                                       encrAlg,
+                                       mode,
+                                       padding,
+                                       keySizeInBits,
+                                       algBlockSizeBytes,
+                                       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:
+       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(ptext.Data);
+       CSSM_FREE(keyData.Data);
+       return rtn;
+}
+
+