4 * Verify proper operation of symmetric CSP algorithms when CSSM_CC_HANDLE
5 * (crypto context) is reused. Tests specifically for Radar 4551700, which
6 * dealt with a problem with the Gladman AES implementation handling the
7 * same context for an encrypt followed by a decrypt; other situations
8 * are tested here (e.g. encrypt followed by another encrypt including CBC)
9 * as well as all CSP symmetric algorithms.
15 #include <Security/cssm.h>
16 #include <Security/cssmapple.h>
20 #include "cspdlTesting.h"
27 #define MIN_DATA_SIZE 8
28 #define MAX_DATA_SIZE 10000 /* bytes */
29 #define MAX_KEY_SIZE MAX_KEY_SIZE_RC245_BYTES /* bytes */
30 #define LOOP_NOTIFY 20
32 #define RAW_MODE CSSM_ALGMODE_ECB /* doesn't work for BSAFE */
33 #define RAW_MODE_STREAM CSSM_ALGMODE_NONE
34 #define COOKED_MODE CSSM_ALGMODE_CBCPadIV8
36 #define RAW_MODE_STR "ECB"
37 #define RAW_MODE_STREAM_STR "None"
38 #define COOKED_MODE_STR "CBC/Pad"
41 * Enumerate algs our own way to allow iteration.
44 ALG_ASC
= 1, // not tested - no reference available
51 ALG_AES192
, /* 192 bit block */
52 ALG_AES256
, /* 256 bit block */
56 #define ALG_FIRST ALG_ASC
57 #define ALG_LAST ALG_CAST
59 static void usage(char **argv
)
61 printf("usage: %s [options]\n", argv
[0]);
62 printf(" Options:\n");
63 printf(" a=algorithm (d=DES; 3=3DES3; 2=RC2; 4=RC4; 5=RC5; a=AES; A=AES192; \n");
64 printf(" 6=AES256; b=Blowfish; c=CAST; s=ASC; default=all)\n");
65 printf(" l=loops (default=%d; 0=forever)\n", LOOPS_DEF
);
66 printf(" k=keySizeInBits\n");
67 printf(" m=maxPtextSize (default=%d)\n", MAX_DATA_SIZE
);
68 printf(" n=minPtextSize (default=%d)\n", MIN_DATA_SIZE
);
69 printf(" p=pauseInterval (default=0, no pause)\n");
70 printf(" D (CSP/DL; default = bare CSP)\n");
71 printf(" v(erbose)\n");
77 #define LOG_STAGED_OPS 0
79 #define soprintf(s) printf s
85 * Multipurpose encrypt. Like cspStagedEncrypt(), but it takes a
86 * context handle and doesn't have as many options.
88 static CSSM_RETURN
stagedEncrypt(
89 CSSM_CSP_HANDLE cspHand
,
90 CSSM_CC_HANDLE cryptHand
,
91 uint32 algorithm
, // CSSM_ALGID_FEED, etc.
92 uint32 cipherBlockSizeBytes
,// optional
93 const CSSM_DATA
*iv
, // init vector, optional
94 const CSSM_DATA
*ptext
,
95 CSSM_DATA_PTR ctext
, // mallocd by caller, must be big enough!
96 CSSM_BOOL multiUpdates
) // false:single update, true:multi updates
99 CSSM_SIZE bytesEncrypted
; // per update
100 CSSM_SIZE bytesEncryptedTotal
= 0;
101 CSSM_RETURN ocrtn
= CSSM_OK
; // 'our' crtn
102 unsigned toMove
; // remaining
103 unsigned thisMove
; // bytes to encrypt on this update
104 CSSM_DATA thisPtext
; // running ptr into ptext
105 CSSM_DATA thisCtext
; // running ptr into ctext
106 CSSM_BOOL restoreErr
= CSSM_FALSE
;
107 CSSM_RETURN savedErr
= CSSM_OK
;
110 if(cipherBlockSizeBytes
) {
111 crtn
= AddContextAttribute(cryptHand
,
112 CSSM_ATTRIBUTE_BLOCK_SIZE
,
116 cipherBlockSizeBytes
);
118 printError("CSSM_UpdateContextAttributes", crtn
);
126 memset(ctext
->Data
, 0, ctext
->Length
);
127 ctextLen
= ctext
->Length
;
129 crtn
= CSSM_EncryptDataInit(cryptHand
);
131 printError("CSSM_EncryptDataInit", crtn
);
136 toMove
= ptext
->Length
;
139 thisMove
= genRand(1, toMove
);
142 /* just do one pass thru this loop */
145 thisPtext
.Length
= thisMove
;
146 crtn
= CSSM_EncryptDataUpdate(cryptHand
,
153 printError("CSSM_EncryptDataUpdate", crtn
);
157 soprintf(("*** EncryptDataUpdate: ptextLen 0x%x bytesEncrypted 0x%x\n",
158 (unsigned)thisMove
, (unsigned)bytesEncrypted
));
160 // NOTE: We return the proper length in ctext....
161 ctextLen
-= bytesEncrypted
; // bump out ptr
162 thisCtext
.Length
= ctextLen
;
163 thisCtext
.Data
+= bytesEncrypted
;
164 bytesEncryptedTotal
+= bytesEncrypted
;
165 thisPtext
.Data
+= thisMove
; // bump in ptr
169 crtn
= CSSM_EncryptDataFinal(cryptHand
, &thisCtext
);
171 printError("CSSM_EncryptDataFinal", crtn
);
173 restoreErr
= CSSM_TRUE
;
176 soprintf(("*** EncryptDataFinal: bytesEncrypted 0x%x\n",
177 (unsigned)thisCtext
.Length
));
178 bytesEncryptedTotal
+= thisCtext
.Length
;
179 ctext
->Length
= bytesEncryptedTotal
;
182 /* give caller the error from the encrypt */
189 * Multipurpose decrypt. Like cspStagedDecrypt(), but it takes a
190 * context handle and doesn't have as many options.
192 CSSM_RETURN
stagedDecrypt(
193 CSSM_CSP_HANDLE cspHand
,
194 CSSM_CC_HANDLE cryptHand
,
195 uint32 algorithm
, // CSSM_ALGID_FEED, etc.
196 uint32 cipherBlockSizeBytes
,// optional
197 const CSSM_DATA
*iv
, // init vector, optional
198 const CSSM_DATA
*ctext
,
199 CSSM_DATA_PTR ptext
, // mallocd by caller, must be big enough!
200 CSSM_BOOL multiUpdates
) // false:single update, true:multi updates
203 CSSM_SIZE bytesDecrypted
; // per update
204 CSSM_SIZE bytesDecryptedTotal
= 0;
205 CSSM_RETURN ocrtn
= CSSM_OK
; // 'our' crtn
206 unsigned toMove
; // remaining
207 unsigned thisMove
; // bytes to decrypt on this update
208 CSSM_DATA thisCtext
; // running ptr into ptext
209 CSSM_DATA thisPtext
; // running ptr into ctext
212 if(cipherBlockSizeBytes
) {
213 crtn
= AddContextAttribute(cryptHand
,
214 CSSM_ATTRIBUTE_BLOCK_SIZE
,
218 cipherBlockSizeBytes
);
220 printError("CSSM_UpdateContextAttributes", crtn
);
228 memset(ptext
->Data
, 0, ptext
->Length
);
229 ptextLen
= ptext
->Length
;
231 crtn
= CSSM_DecryptDataInit(cryptHand
);
233 printError("CSSM_DecryptDataInit", crtn
);
238 toMove
= ctext
->Length
;
241 thisMove
= genRand(1, toMove
);
244 /* just do one pass thru this loop */
247 thisCtext
.Length
= thisMove
;
248 crtn
= CSSM_DecryptDataUpdate(cryptHand
,
255 printError("CSSM_DecryptDataUpdate", crtn
);
259 soprintf(("*** DecryptDataUpdate: ctextLen 0x%x bytesDecrypted 0x%x\n",
260 (unsigned)thisMove
, (unsigned)bytesDecrypted
));
262 // NOTE: We return the proper length in ptext....
263 ptextLen
-= bytesDecrypted
; // bump out ptr
264 thisPtext
.Length
= ptextLen
;
265 thisPtext
.Data
+= bytesDecrypted
;
266 bytesDecryptedTotal
+= bytesDecrypted
;
267 thisCtext
.Data
+= thisMove
; // bump in ptr
271 crtn
= CSSM_DecryptDataFinal(cryptHand
, &thisPtext
);
273 printError("CSSM_DecryptDataFinal", crtn
);
277 soprintf(("*** DecryptDataFinal: bytesEncrypted 0x%x\n",
278 (unsigned)thisPtext
.Length
));
279 bytesDecryptedTotal
+= thisPtext
.Length
;
280 ptext
->Length
= bytesDecryptedTotal
;
286 CSSM_CSP_HANDLE cspHand
,
287 const CSSM_DATA
*ptext
,
288 const CSSM_DATA
*ctext1
,
289 const CSSM_DATA
*ctext2
,
290 const CSSM_DATA
*rptext
,
291 const CSSM_DATA
*keyData
,
293 uint32 keyAlg
, // CSSM_ALGID_xxx of the key
294 uint32 encrAlg
, // encrypt/decrypt
297 uint32 keySizeInBits
,
298 uint32 cipherBlockSizeBytes
,
306 CSSM_CC_HANDLE ccHand1
= 0;
307 CSSM_CC_HANDLE ccHand2
= 0;
310 uint8 dummy
[cipherBlockSizeBytes
];
311 CSSM_DATA dummyData
= {cipherBlockSizeBytes
, dummy
};
314 * generate two equivalent keys key1 and key2;
315 * generate two CC handles ccHand1, ccHand2;
316 * encrypt dummy data with ccHand1 to get it cooked;
317 * encrypt ptext with ccHand1 ==> ctext1;
318 * encrypt ptext with ccHand2 ==> ctext2;
319 * Compare ctext1 and ctext2;
320 * decrypt ctext1 with ccHand1, compare with ptext;
322 crtn
= cspGenSymKeyWithBits(cspHand
, keyAlg
, CSSM_KEYUSE_ANY
,
323 keyData
, keySizeInBits
/ 8, &key1
);
327 crtn
= cspGenSymKeyWithBits(cspHand
, keyAlg
, CSSM_KEYUSE_ANY
,
328 keyData
, keySizeInBits
/ 8, &key2
);
332 ccHand1
= genCryptHandle(cspHand
,
339 0, // effectiveKeySizeInBits
342 return CSSMERR_CSP_INTERNAL_ERROR
;
344 ccHand2
= genCryptHandle(cspHand
,
351 0, // effectiveKeySizeInBits
354 return CSSMERR_CSP_INTERNAL_ERROR
;
357 /* dummy encrypt to heat up ccHand1 */
358 appGetRandomBytes(dummy
, sizeof(dummy
));
360 crtn
= stagedEncrypt(cspHand
, ccHand1
, encrAlg
, cipherBlockSizeBytes
,
361 iv
, &dummyData
, &lctext1
, CSSM_FALSE
);
366 /* encrypt ptext with ccHand1 and ccHand2, compare ctext */
368 crtn
= stagedEncrypt(cspHand
, ccHand1
, encrAlg
, cipherBlockSizeBytes
,
369 iv
, ptext
, &lctext1
, CSSM_TRUE
);
374 crtn
= stagedEncrypt(cspHand
, ccHand2
, encrAlg
, cipherBlockSizeBytes
,
375 iv
, ptext
, &lctext2
, CSSM_TRUE
);
379 if(!appCompareCssmData(&lctext1
, &lctext2
)) {
380 printf("***Ciphertext miscompare\n");
381 if(testError(quiet
)) {
386 /* decrypt with ccHand1, compare with ptext */
388 crtn
= stagedDecrypt(cspHand
, ccHand1
, encrAlg
, cipherBlockSizeBytes
,
389 iv
, &lctext1
, &lrptext
, CSSM_TRUE
);
393 if(!appCompareCssmData(&lctext1
, &lctext2
)) {
394 printf("***Plaintext miscompare\n");
395 if(testError(quiet
)) {
401 CSSM_DeleteContext(ccHand1
);
404 CSSM_DeleteContext(ccHand2
);
410 int main(int argc
, char **argv
)
419 CSSM_CSP_HANDLE cspHand
;
421 uint32 keyAlg
; // CSSM_ALGID_xxx of the key
422 uint32 encrAlg
; // CSSM_ALGID_xxx of encr/decr
423 unsigned currAlg
; // ALG_xxx
424 uint32 keySizeInBits
;
427 CSSM_DATA initVector
;
431 const char *rawModeStr
;
432 const char *cookedModeStr
;
433 uint32 algBlockSizeBytes
;
438 CSSM_BOOL keySizeSpec
= CSSM_FALSE
; // false: use rand key size
439 unsigned minAlg
= ALG_FIRST
;
440 unsigned maxAlg
= ALG_LAST
;
441 unsigned loops
= LOOPS_DEF
;
442 CSSM_BOOL verbose
= CSSM_FALSE
;
443 CSSM_BOOL quiet
= CSSM_FALSE
;
444 unsigned pauseInterval
= 0;
446 CSSM_BOOL bareCsp
= CSSM_TRUE
;
447 unsigned maxPtextSize
= MAX_DATA_SIZE
;
448 unsigned minPtextSize
= MIN_DATA_SIZE
;
450 for(arg
=1; arg
<argc
; arg
++) {
459 minAlg
= maxAlg
= ALG_DES
;
462 minAlg
= maxAlg
= ALG_3DES
;
465 minAlg
= maxAlg
= ALG_RC2
;
468 minAlg
= maxAlg
= ALG_RC4
;
471 minAlg
= maxAlg
= ALG_RC5
;
474 minAlg
= maxAlg
= ALG_AES
;
477 minAlg
= maxAlg
= ALG_AES192
;
480 minAlg
= maxAlg
= ALG_AES256
;
483 minAlg
= maxAlg
= ALG_BFISH
;
486 minAlg
= maxAlg
= ALG_CAST
;
489 minAlg
= maxAlg
= ALG_ASC
;
496 loops
= atoi(&argp
[2]);
499 keySizeInBits
= atoi(&argp
[2]);
500 keySizeSpec
= CSSM_TRUE
;
506 bareCsp
= CSSM_FALSE
;
509 maxPtextSize
= atoi(&argp
[2]);
512 minPtextSize
= atoi(&argp
[2]);
518 pauseInterval
= atoi(&argp
[2]);;
525 ptext
.Data
= (uint8
*)CSSM_MALLOC(maxPtextSize
);
526 if(ptext
.Data
== NULL
) {
527 printf("Insufficient heap space\n");
530 /* ptext length set in test loop */
531 appSetupCssmData(&ctext1
, 2 * maxPtextSize
);
532 appSetupCssmData(&ctext2
, 2 * maxPtextSize
);
533 appSetupCssmData(&rptext
, 2 * maxPtextSize
);
535 keyData
.Data
= (uint8
*)CSSM_MALLOC(MAX_KEY_SIZE
);
536 if(keyData
.Data
== NULL
) {
537 printf("Insufficient heap space\n");
540 keyData
.Length
= MAX_KEY_SIZE
;
542 initVector
.Data
= (uint8
*)"someStrangeInitVect";
544 testStartBanner("contextReuse", argc
, argv
);
546 cspHand
= cspDlDbStartup(bareCsp
, NULL
);
552 printf("Top of test; hit CR to proceed: ");
555 for(currAlg
=minAlg
; currAlg
<=maxAlg
; currAlg
++) {
556 /* some default values... */
557 padding
= CSSM_PADDING_PKCS5
;
559 cookedMode
= COOKED_MODE
;
560 rawModeStr
= RAW_MODE_STR
;
561 cookedModeStr
= COOKED_MODE_STR
;
562 padding
= CSSM_PADDING_PKCS5
;
566 encrAlg
= keyAlg
= CSSM_ALGID_DES
;
568 algBlockSizeBytes
= 8;
571 /* currently the only one with different key and encr algs */
572 keyAlg
= CSSM_ALGID_3DES_3KEY
;
573 encrAlg
= CSSM_ALGID_3DES_3KEY_EDE
;
575 algBlockSizeBytes
= 8;
578 encrAlg
= keyAlg
= CSSM_ALGID_RC2
;
580 algBlockSizeBytes
= 8;
583 encrAlg
= keyAlg
= CSSM_ALGID_RC4
;
585 algBlockSizeBytes
= 0;
586 rawMode
= RAW_MODE_STREAM
;
587 cookedMode
= RAW_MODE_STREAM
;
588 rawModeStr
= RAW_MODE_STREAM_STR
;
589 cookedModeStr
= RAW_MODE_STREAM_STR
;
592 encrAlg
= keyAlg
= CSSM_ALGID_RC5
;
594 algBlockSizeBytes
= 8;
597 encrAlg
= keyAlg
= CSSM_ALGID_AES
;
599 algBlockSizeBytes
= 16;
602 encrAlg
= keyAlg
= CSSM_ALGID_AES
;
604 algBlockSizeBytes
= 24;
607 encrAlg
= keyAlg
= CSSM_ALGID_AES
;
609 algBlockSizeBytes
= 32;
612 encrAlg
= keyAlg
= CSSM_ALGID_BLOWFISH
;
614 algBlockSizeBytes
= 8;
617 encrAlg
= keyAlg
= CSSM_ALGID_CAST
;
619 algBlockSizeBytes
= 8;
623 /* assume for now all algs require IV */
624 initVector
.Length
= algBlockSizeBytes
? algBlockSizeBytes
: 8;
626 if(!quiet
|| verbose
) {
627 printf("Testing alg %s\n", algStr
);
629 for(loop
=1; ; loop
++) {
630 /* mix up raw/cooked */
633 CSSM_BOOL paddingEnabled
;
637 modeStr
= rawModeStr
;
641 modeStr
= cookedModeStr
;
644 case CSSM_ALGMODE_CBCPadIV8
:
645 paddingEnabled
= CSSM_TRUE
;
648 /* all others - right? */
649 paddingEnabled
= CSSM_FALSE
;
652 minTextSize
= minPtextSize
; // default
653 if(!paddingEnabled
&& algBlockSizeBytes
&& (minTextSize
< algBlockSizeBytes
)) {
654 /* i.e., no padding, adjust min ptext size */
655 minTextSize
= algBlockSizeBytes
;
657 simpleGenData(&ptext
, minTextSize
, maxPtextSize
);
658 if(!paddingEnabled
&& algBlockSizeBytes
) {
660 ptext
.Length
= (ptext
.Length
/ algBlockSizeBytes
) * algBlockSizeBytes
;
663 simpleGenData(&keyData
, MAX_KEY_SIZE
, MAX_KEY_SIZE
);
666 /* random but byte-aligned */
667 keySizeInBits
= randKeySizeBits(keyAlg
, OT_Encrypt
);
668 keySizeInBits
= (keySizeInBits
+ 7) & ~7;
670 /* else constant, spec'd by user, may be 0 (default per alg) */
672 if(verbose
|| ((loop
% LOOP_NOTIFY
) == 0)) {
673 if(algBlockSizeBytes
) {
674 printf("..loop %d text size %lu keySizeBits %u"
675 " blockSize %u mode %s\n",
676 loop
, (unsigned long)ptext
.Length
, (unsigned)keySizeInBits
,
677 (unsigned)algBlockSizeBytes
, modeStr
);
680 printf("..loop %d text size %lu keySizeBits %u"
682 loop
, (unsigned long)ptext
.Length
, (unsigned)keySizeInBits
,
705 if(pauseInterval
&& ((loop
% pauseInterval
) == 0)) {
708 printf("Hit CR to proceed, q to abort: ");
714 if(loops
&& (loop
== loops
)) {
725 cspShutdown(cspHand
, bareCsp
);
728 printf("ModuleDetach/Unload complete; hit CR to exit: ");
731 if((rtn
== 0) && !quiet
) {
732 printf("%s test complete\n", argv
[0]);
734 CSSM_FREE(ptext
.Data
);
735 CSSM_FREE(keyData
.Data
);