X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/SecurityTests/cspxutils/contextReuse/contextReuse.cpp diff --git a/SecurityTests/cspxutils/contextReuse/contextReuse.cpp b/SecurityTests/cspxutils/contextReuse/contextReuse.cpp new file mode 100644 index 00000000..993d1799 --- /dev/null +++ b/SecurityTests/cspxutils/contextReuse/contextReuse.cpp @@ -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 +#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