1/* 2 * macCompat.c - test compatibilty of two different implementations of a 3 * given MAC algorithm - one in the standard AppleCSP, 4 * one in BSAFE. 5 * 6 * Written by Doug Mitchell. 7 */ 8 9#include <stdlib.h> 10#include <stdio.h> 11#include <time.h> 12#include <Security/cssm.h> 13#include <Security/cssmapple.h> 14#include "cspwrap.h" 15#include "common.h" 16#include "bsafeUtils.h" 17#include <string.h> 18#include "cspdlTesting.h" 19#include <openssl/hmac.h> 20 21/* 22 * Defaults. 23 */ 24#define LOOPS_DEF 200 25#define MIN_EXP 2 /* for data size 10**exp */ 26#define DEFAULT_MAX_EXP 4 27#define MAX_EXP 5 28 29#define MAX_DATA_SIZE (100000 + 100) /* bytes */ 30#define MIN_KEY_SIZE 20 /* bytes - should be smaller */ 31#define MAX_KEY_SIZE 64 /* bytes */ 32#define LOOP_NOTIFY 20 33 34/* 35 * Enumerate algs our own way to allow iteration. 36 */ 37#define ALG_MD5 1 38#define ALG_SHA1 2 39#define ALG_SHA1_LEGACY 3 40#define ALG_FIRST ALG_MD5 41#define ALG_LAST ALG_SHA1_LEGACY 42 43static void usage(char **argv) 44{ 45 printf("usage: %s [options]\n", argv[0]); 46 printf(" Options:\n"); 47 printf(" l=loops (default=%d; 0=forever)\n", LOOPS_DEF); 48 printf(" n=minExp (default=%d)\n", MIN_EXP); 49 printf(" x=maxExp (default=%d, max=%d)\n", DEFAULT_MAX_EXP, MAX_EXP); 50 printf(" k=keySizeInBytes\n"); 51 printf(" P=plainTextLen\n"); 52 printf(" z (keys and plaintext all zeroes)\n"); 53 printf(" p=pauseInterval (default=0, no pause)\n"); 54 printf(" D (CSP/DL; default = bare CSP)\n"); 55 printf(" v(erbose)\n"); 56 printf(" q(uiet)\n"); 57 printf(" h(elp)\n"); 58 exit(1); 59} 60 61/* 62 * generate MAC using reference BSAFE with either one update 63 * (updateSizes == NULL) or specified set of update sizes. 64 */ 65static CSSM_RETURN genMacBSAFE( 66 CSSM_ALGORITHMS macAlg, 67 const CSSM_DATA *key, // raw key bytes 68 const CSSM_DATA *inText, 69 unsigned *updateSizes, // NULL --> random updates 70 // else null-terminated list of sizes 71 CSSM_DATA_PTR outText) // mallocd and returned 72{ 73 CSSM_RETURN crtn; 74 BU_KEY buKey; 75 76 crtn = buGenSymKey(key->Length * 8, key, &buKey); 77 if(crtn) { 78 return crtn; 79 } 80 crtn = buGenMac(buKey, 81 macAlg, 82 inText, 83 updateSizes, 84 outText); 85 buFreeKey(buKey); 86 return crtn; 87} 88 89/* 90 * Produce HMACMD5 with openssl. 91 */ 92static int doHmacMD5Ref( 93 const CSSM_DATA *key, // raw key bytes 94 const CSSM_DATA *inText, 95 CSSM_DATA_PTR outText) // mallocd and returned 96{ 97 const EVP_MD *md = EVP_md5(); 98 unsigned md_len = 16; 99 appSetupCssmData(outText, 16); 100 HMAC(md, key->Data, (int)key->Length, 101 inText->Data, inText->Length, 102 (unsigned char *)outText->Data, &md_len); 103 return 0; 104} 105 106/* 107 * Generate MAC, CSP, specified set of update sizes 108 */ 109static CSSM_RETURN cspGenMacWithSizes(CSSM_CSP_HANDLE cspHand, 110 uint32 algorithm, 111 CSSM_KEY_PTR key, // session key 112 const CSSM_DATA *text, 113 unsigned *updateSizes, // null-terminated list of sizes 114 CSSM_DATA_PTR mac) // RETURNED 115{ 116 CSSM_CC_HANDLE macHand; 117 CSSM_RETURN crtn; 118 CSSM_DATA currData = *text; 119 120 crtn = CSSM_CSP_CreateMacContext(cspHand, 121 algorithm, 122 key, 123 &macHand); 124 if(crtn) { 125 printError("CSSM_CSP_CreateMacContext", crtn); 126 return crtn; 127 } 128 crtn = CSSM_GenerateMacInit(macHand); 129 if(crtn) { 130 printError("CSSM_GenerateMacInit", crtn); 131 goto abort; 132 } 133 /* CSP mallocs */ 134 mac->Data = NULL; 135 mac->Length = 0; 136 137 while(*updateSizes) { 138 currData.Length = *updateSizes; 139 crtn = CSSM_GenerateMacUpdate(macHand, 140 &currData, 141 1); 142 if(crtn) { 143 printError("CSSM_GenerateMacUpdate", crtn); 144 goto abort; 145 } 146 currData.Data += *updateSizes; 147 updateSizes++; 148 } 149 crtn = CSSM_GenerateMacFinal(macHand, mac); 150 if(crtn) { 151 printError("CSSM_GenerateMacFinal", crtn); 152 } 153abort: 154 crtn = CSSM_DeleteContext(macHand); 155 if(crtn) { 156 printError("CSSM_DeleteContext", crtn); 157 } 158 return crtn; 159} 160 161/* 162 * Verify MAC, CSP, specified set of update sizes 163 */ 164static CSSM_RETURN cspVfyMacWithSizes(CSSM_CSP_HANDLE cspHand, 165 uint32 algorithm, 166 CSSM_KEY_PTR key, // session key 167 const CSSM_DATA *text, 168 unsigned *updateSizes, // null-terminated list of sizes 169 const CSSM_DATA *mac) 170{ 171 CSSM_CC_HANDLE macHand; 172 CSSM_RETURN crtn; 173 CSSM_DATA currData = *text; 174 175 crtn = CSSM_CSP_CreateMacContext(cspHand, 176 algorithm, 177 key, 178 &macHand); 179 if(crtn) { 180 printError("CSSM_CSP_CreateMacContext", crtn); 181 return crtn; 182 } 183 crtn = CSSM_VerifyMacInit(macHand); 184 if(crtn) { 185 printError("CSSM_VerifyMacInit", crtn); 186 goto abort; 187 } 188 189 while(*updateSizes) { 190 currData.Length = *updateSizes; 191 crtn = CSSM_VerifyMacUpdate(macHand, 192 &currData, 193 1); 194 if(crtn) { 195 printError("CSSM_GenerateMacUpdate", crtn); 196 goto abort; 197 } 198 currData.Data += *updateSizes; 199 updateSizes++; 200 } 201 crtn = CSSM_VerifyMacFinal(macHand, mac); 202 if(crtn) { 203 printError("CSSM_GenerateMacFinal", crtn); 204 } 205abort: 206 crtn = CSSM_DeleteContext(macHand); 207 if(crtn) { 208 printError("CSSM_DeleteContext", crtn); 209 } 210 return crtn; 211} 212 213/* 214 * Generate or verify MAC using CSP with either random-sized staged updates 215 * (updateSizes == NULL) or specified set of update sizes. 216 */ 217static CSSM_RETURN genMacCSSM( 218 CSSM_CSP_HANDLE cspHand, 219 CSSM_ALGORITHMS macAlg, 220 CSSM_ALGORITHMS keyAlg, 221 CSSM_BOOL doGen, 222 const CSSM_DATA *key, // raw key bytes 223 CSSM_BOOL genRaw, // first generate raw key (CSPDL) 224 const CSSM_DATA *inText, 225 unsigned *updateSizes, // NULL --> random updates 226 // else null-terminated list of sizes 227 CSSM_DATA_PTR outText) // mallocd and returned if doGen 228{ 229 CSSM_KEY_PTR symKey; 230 CSSM_KEY refKey; // in case of genRaw 231 CSSM_BOOL refKeyGenerated = CSSM_FALSE; 232 CSSM_RETURN crtn; 233 234 if(genRaw) { 235 crtn = cspGenSymKeyWithBits(cspHand, 236 keyAlg, 237 CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY, 238 key, 239 key->Length, 240 &refKey); 241 if(crtn) { 242 return crtn; 243 } 244 symKey = &refKey; 245 refKeyGenerated = CSSM_TRUE; 246 } 247 else { 248 /* cook up a raw symmetric key */ 249 symKey = cspGenSymKey(cspHand, 250 keyAlg, 251 "noLabel", 252 8, 253 CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY, 254 key->Length * 8, 255 CSSM_FALSE); // ref key 256 if(symKey == NULL) { 257 return CSSM_ERRCODE_INTERNAL_ERROR; 258 } 259 if(symKey->KeyData.Length != key->Length) { 260 printf("***Generated key size error (exp %lu, got %lu)\n", 261 key->Length, symKey->KeyData.Length); 262 return CSSM_ERRCODE_INTERNAL_ERROR; 263 } 264 memmove(symKey->KeyData.Data, key->Data, key->Length); 265 } 266 if(doGen) { 267 /* CSP mallocs */ 268 outText->Data = NULL; 269 outText->Length = 0; 270 } 271 272 /* go for it */ 273 if(doGen) { 274 if(updateSizes) { 275 crtn = cspGenMacWithSizes(cspHand, 276 macAlg, 277 symKey, 278 inText, 279 updateSizes, 280 outText); 281 } 282 else { 283 crtn = cspStagedGenMac(cspHand, 284 macAlg, 285 symKey, 286 inText, 287 CSSM_TRUE, // multiUpdates 288 CSSM_FALSE, // mallocMac 289 outText); 290 } 291 } 292 else { 293 if(updateSizes) { 294 crtn = cspVfyMacWithSizes(cspHand, 295 macAlg, 296 symKey, 297 inText, 298 updateSizes, 299 outText); 300 } 301 else { 302 crtn = cspMacVerify(cspHand, 303 macAlg, 304 symKey, 305 inText, 306 outText, 307 CSSM_OK); 308 } 309 } 310 cspFreeKey(cspHand, symKey); 311 if(!refKeyGenerated) { 312 /* key itself mallocd by cspGenSymKey */ 313 CSSM_FREE(symKey); 314 } 315 return crtn; 316} 317 318#define LOG_FREQ 20 319#define MAX_FIXED_UPDATES 5 320 321static int doTest(CSSM_CSP_HANDLE cspHand, 322 const CSSM_DATA *ptext, 323 const CSSM_DATA *keyData, 324 CSSM_BOOL genRaw, // first generate raw key (CSPDL) 325 uint32 macAlg, 326 uint32 keyAlg, 327 CSSM_BOOL fixedUpdates, // for testing CSSM_ALGID_SHA1HMAC_LEGACY 328 CSSM_BOOL quiet) 329{ 330 CSSM_DATA macRef = {0, NULL}; // MAC, BSAFE reference 331 CSSM_DATA macTest = {0, NULL}; // MAC, CSP test 332 int rtn = 0; 333 CSSM_RETURN crtn; 334 unsigned updateSizes[MAX_FIXED_UPDATES+1]; 335 unsigned *updateSizesPtr; 336 337 if(fixedUpdates) { 338 /* calculate up to MAX_FIXED_UPDATES update sizes which add up to 339 * ptext->Length */ 340 int i; 341 unsigned bytesToGo = ptext->Length; 342 343 memset(updateSizes, 0, sizeof(unsigned) * (MAX_FIXED_UPDATES+1)); 344 for(i=0; i<MAX_FIXED_UPDATES; i++) { 345 updateSizes[i] = genRand(1, bytesToGo); 346 bytesToGo -= updateSizes[i]; 347 if(bytesToGo == 0) { 348 break; 349 } 350 } 351 updateSizesPtr = updateSizes; 352 } 353 else { 354 /* 355 * CSP : random updates 356 * BSAFE, openssl: single one-shot update 357 */ 358 updateSizesPtr = NULL; 359 } 360 /* 361 * generate with each method; 362 * verify MACs compare; 363 * verify with test code; 364 */ 365 if(macAlg == CSSM_ALGID_MD5HMAC) { 366 doHmacMD5Ref(keyData, ptext, &macRef); 367 crtn = CSSM_OK; 368 } 369 else { 370 crtn = genMacBSAFE(macAlg, 371 keyData, 372 ptext, 373 updateSizesPtr, 374 &macRef); 375 } 376 if(crtn) { 377 return testError(quiet); 378 } 379 crtn = genMacCSSM(cspHand, 380 macAlg, 381 keyAlg, 382 CSSM_TRUE, 383 keyData, 384 genRaw, 385 ptext, 386 updateSizesPtr, 387 &macTest); 388 if(crtn) { 389 return testError(quiet); 390 } 391 392 /* ensure both methods resulted in same MAC */ 393 if(macRef.Length != macTest.Length) { 394 printf("MAC length mismatch (1)\n"); 395 rtn = testError(quiet); 396 if(rtn) { 397 goto abort; 398 } 399 } 400 if(memcmp(macRef.Data, macTest.Data, macTest.Length)) { 401 printf("MAC miscompare\n"); 402 rtn = testError(quiet); 403 if(rtn) { 404 goto abort; 405 } 406 } 407 408 /* verify with the test method */ 409 crtn = genMacCSSM(cspHand, 410 macAlg, 411 keyAlg, 412 CSSM_FALSE, 413 keyData, 414 genRaw, 415 ptext, 416 updateSizesPtr, 417 &macTest); 418 if(crtn) { 419 printf("***Unexpected MAC verify failure\n"); 420 rtn = testError(quiet); 421 } 422 else { 423 rtn = 0; 424 } 425abort: 426 if(macTest.Length) { 427 CSSM_FREE(macTest.Data); 428 } 429 if(macRef.Length) { 430 CSSM_FREE(macRef.Data); 431 } 432 return rtn; 433} 434 435 436int main(int argc, char **argv) 437{ 438 int arg; 439 char *argp; 440 unsigned loop; 441 CSSM_DATA ptext; 442 CSSM_CSP_HANDLE cspHand; 443 const char *algStr; 444 uint32 macAlg; // CSSM_ALGID_xxx 445 uint32 keyAlg; // CSSM_ALGID_xxx 446 int i; 447 unsigned currAlg; // ALG_xxx 448 int rtn = 0; 449 CSSM_DATA keyData; 450 CSSM_BOOL genRaw = CSSM_FALSE; // first generate raw key (CSPDL) 451 452 /* 453 * User-spec'd params 454 */ 455 unsigned minAlg = ALG_FIRST; 456 unsigned maxAlg = ALG_LAST; 457 unsigned loops = LOOPS_DEF; 458 CSSM_BOOL verbose = CSSM_FALSE; 459 unsigned minExp = MIN_EXP; 460 unsigned maxExp = DEFAULT_MAX_EXP; 461 CSSM_BOOL quiet = CSSM_FALSE; 462 unsigned pauseInterval = 0; 463 CSSM_BOOL bareCsp = CSSM_TRUE; 464 CSSM_BOOL fixedUpdates; 465 CSSM_BOOL allZeroes = CSSM_FALSE; 466 unsigned keySizeSpecd = 0; 467 unsigned ptextLenSpecd = 0; 468 469 for(arg=1; arg<argc; arg++) { 470 argp = argv[arg]; 471 switch(argp[0]) { 472 case 'l': 473 loops = atoi(&argp[2]); 474 break; 475 case 'n': 476 minExp = atoi(&argp[2]); 477 break; 478 case 'x': 479 maxExp = atoi(&argp[2]); 480 if(maxExp > MAX_EXP) { 481 usage(argv); 482 } 483 break; 484 case 'v': 485 verbose = CSSM_TRUE; 486 break; 487 case 'D': 488 bareCsp = CSSM_FALSE; 489 #if CSPDL_ALL_KEYS_ARE_REF 490 genRaw = CSSM_TRUE; 491 #endif 492 break; 493 case 'q': 494 quiet = CSSM_TRUE; 495 break; 496 case 'p': 497 pauseInterval = atoi(&argp[2]); 498 break; 499 case 'z': 500 allZeroes = CSSM_TRUE; 501 break; 502 case 'k': 503 keySizeSpecd = atoi(&argp[2]); 504 break; 505 case 'P': 506 ptextLenSpecd = atoi(&argp[2]); 507 break; 508 case 'h': 509 default: 510 usage(argv); 511 } 512 } 513 if(minExp > maxExp) { 514 printf("***minExp must be <= maxExp\n"); 515 usage(argv); 516 } 517 ptext.Data = (uint8 *)CSSM_MALLOC(MAX_DATA_SIZE); 518 if(ptext.Data == NULL) { 519 printf("Insufficient heap space\n"); 520 exit(1); 521 } 522 /* ptext length set in test loop */ 523 524 keyData.Data = (uint8 *)CSSM_MALLOC(MAX_KEY_SIZE); 525 if(keyData.Data == NULL) { 526 printf("Insufficient heap space\n"); 527 exit(1); 528 } 529 /* key length set in test loop */ 530 531 printf("Starting macCompat; args: "); 532 for(i=1; i<argc; i++) { 533 printf("%s ", argv[i]); 534 } 535 printf("\n"); 536 cspHand = cspDlDbStartup(bareCsp, NULL); 537 if(cspHand == 0) { 538 exit(1); 539 } 540 if(pauseInterval) { 541 fpurge(stdin); 542 printf("Top of test; hit CR to proceed: "); 543 getchar(); 544 } 545 for(currAlg=minAlg; currAlg<=maxAlg; currAlg++) { 546 if((currAlg == ALG_SHA1_LEGACY) && !bareCsp && !CSPDL_SHA1HMAC_LEGACY_ENABLE) { 547 continue; 548 } 549 550 /* some default values... */ 551 switch(currAlg) { 552 case ALG_MD5: 553 macAlg = CSSM_ALGID_MD5HMAC; 554 keyAlg = CSSM_ALGID_MD5HMAC; 555 algStr = "MD5"; 556 fixedUpdates = CSSM_FALSE; 557 break; 558 case ALG_SHA1: 559 macAlg = CSSM_ALGID_SHA1HMAC; 560 keyAlg = CSSM_ALGID_SHA1HMAC; 561 algStr = "SHA1"; 562 fixedUpdates = CSSM_FALSE; 563 break; 564 case ALG_SHA1_LEGACY: 565 macAlg = CSSM_ALGID_SHA1HMAC_LEGACY; 566 keyAlg = CSSM_ALGID_SHA1HMAC; 567 algStr = "SHA1_LEGACY"; 568 fixedUpdates = CSSM_TRUE; 569 break; 570 default: 571 printf("***Brrzap. Bad alg.\n"); 572 exit(1); 573 } 574 575 if(!quiet || verbose) { 576 printf("Testing alg %s\n", algStr); 577 } 578 for(loop=1; ; loop++) { 579 /* random ptext and key */ 580 ptext.Length = genData(ptext.Data, minExp, maxExp, DT_Random); 581 if(ptextLenSpecd) { 582 ptext.Length = ptextLenSpecd; 583 } 584 if(allZeroes) { 585 memset(ptext.Data, 0, ptext.Length); 586 } 587 if(macAlg == CSSM_ALGID_SHA1HMAC_LEGACY) { 588 simpleGenData(&keyData, 20, 20); 589 } 590 else { 591 simpleGenData(&keyData, MIN_KEY_SIZE, MAX_KEY_SIZE); 592 if(keySizeSpecd) { 593 keyData.Length = keySizeSpecd; 594 } 595 } 596 if(allZeroes) { 597 memset(keyData.Data, 0, keyData.Length); 598 } 599 if(!quiet) { 600 if(verbose || ((loop % LOOP_NOTIFY) == 0)) { 601 printf("..loop %d text size %lu keySize %lu\n", 602 loop, ptext.Length, keyData.Length); 603 } 604 } 605 606 if(doTest(cspHand, 607 &ptext, 608 &keyData, 609 genRaw, 610 macAlg, 611 keyAlg, 612 fixedUpdates, 613 quiet)) { 614 rtn = 1; 615 break; 616 } 617 if(pauseInterval && ((loop % pauseInterval) == 0)) { 618 char c; 619 fpurge(stdin); 620 printf("Hit CR to proceed, q to abort: "); 621 c = getchar(); 622 if(c == 'q') { 623 goto testDone; 624 } 625 } 626 if(loops && (loop == loops)) { 627 break; 628 } 629 } /* main loop */ 630 if(rtn) { 631 break; 632 } 633 634 } /* for algs */ 635 636testDone: 637 cspShutdown(cspHand, bareCsp); 638 if(pauseInterval) { 639 fpurge(stdin); 640 printf("ModuleDetach/Unload complete; hit CR to exit: "); 641 getchar(); 642 } 643 if((rtn == 0) && !quiet) { 644 printf("%s test complete\n", argv[0]); 645 } 646 CSSM_FREE(ptext.Data); 647 CSSM_FREE(keyData.Data); 648 return rtn; 649} 650 651 652