/* * 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 #include #include #include #include #include "cspwrap.h" #include "common.h" #include #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