1/* 2 * dhFullTest - Diffie-Hellman exerciser. 3 */ 4#include <stdlib.h> 5#include <stdio.h> 6#include <Security/cssm.h> 7#include "cspwrap.h" 8#include "common.h" 9#include <security_cdsa_utils/cuFileIo.h> 10#include <strings.h> 11#include "cspdlTesting.h" 12 13#define USAGE_DEF "noUsage" 14#define LOOPS_DEF 10 15#define KEY_SIZE_DEF 512 16#define DERIVE_KEY_SIZE 128 17#define DERIVE_KEY_ALG CSSM_ALGID_AES 18#define ENCR_ALG CSSM_ALGID_AES 19#define ENCR_MODE CSSM_ALGMODE_CBCPadIV8 20#define ENCR_PADDING CSSM_PADDING_PKCS7 21#define MAX_PTEXT_SIZE 1024 22 23static void usage(char **argv) 24{ 25 printf("usage: %s [options]\n", argv[0]); 26 printf("Options:\n"); 27 printf(" k=keySize (default = %d)\n", KEY_SIZE_DEF); 28 printf(" l=loops (0=forever)\n"); 29 printf(" p=pauseInterval (default=0, no pause)\n"); 30 printf(" D (CSP/DL; default = bare CSP)\n"); 31 printf(" o=fileName (dump key and param blobs to filename)\n"); 32 printf(" i=filename (obtain param blobs from filename\n"); 33 printf(" q(uiet)\n"); 34 printf(" v(erbose))\n"); 35 exit(1); 36} 37 38/* 39 * Translate blob format to strings. 40 */ 41typedef struct { 42 CSSM_KEYBLOB_FORMAT form; 43 const char *str; 44} BlobDesc; 45 46static const BlobDesc BlobDescs[] = { 47 { CSSM_KEYBLOB_RAW_FORMAT_NONE, "NONE" }, 48 { CSSM_KEYBLOB_RAW_FORMAT_PKCS3, "PKCS3" }, 49 { CSSM_KEYBLOB_RAW_FORMAT_PKCS8, "PKCS8" }, 50 { CSSM_KEYBLOB_RAW_FORMAT_X509, "X509" }, 51}; 52#define NUM_BLOB_DESCS (sizeof(BlobDescs) / sizeof(BlobDesc)) 53 54static const char *blobFormStr( 55 CSSM_KEYBLOB_FORMAT form) 56{ 57 for(unsigned i=0; i<NUM_BLOB_DESCS; i++) { 58 const BlobDesc *bdp = &BlobDescs[i]; 59 if(bdp->form == form) { 60 return bdp->str; 61 } 62 } 63 return "***UNKNOWN BLOB FORM"""; 64} 65 66/* 67 * Generate a Diffie-Hellman key pair. Optionally allows specification of 68 * algorithm parameters, and optionally returns algorithm parameters if 69 * we generate them. 70 */ 71static int dhKeyGen( 72 CSSM_CSP_HANDLE cspHand, 73 CSSM_KEY_PTR pubKey, 74 CSSM_BOOL pubIsRef, 75 CSSM_KEYBLOB_FORMAT pubForm, // NONE, PKCS3, X509 76 CSSM_KEYBLOB_FORMAT expectPubForm, // PKCS3, X509 77 CSSM_KEY_PTR privKey, 78 CSSM_BOOL privIsRef, 79 CSSM_KEYBLOB_FORMAT privForm, // NONE, PKCS3, PKCS8 80 CSSM_KEYBLOB_FORMAT expectPrivForm, // PKCS3, PKCS8 81 const CSSM_DATA *inParams, // optional 82 CSSM_DATA_PTR outParams, // optional, we malloc 83 uint32 keySizeInBits, 84 CSSM_BOOL quiet) 85{ 86 CSSM_RETURN crtn; 87 CSSM_CC_HANDLE ccHand; 88 CSSM_DATA labelData = { strlen(USAGE_DEF), (uint8 *)USAGE_DEF }; 89 90 if(inParams && outParams) { 91 printf("***dhKeyGen: inParams and outParams are mutually " 92 "exclusive.\n"); 93 return -1; 94 } 95 memset(pubKey, 0, sizeof(CSSM_KEY)); 96 memset(privKey, 0, sizeof(CSSM_KEY)); 97 98 crtn = CSSM_CSP_CreateKeyGenContext(cspHand, 99 CSSM_ALGID_DH, 100 keySizeInBits, 101 NULL, // Seed 102 NULL, // Salt 103 NULL, // StartDate 104 NULL, // EndDate 105 inParams, // Params, may be NULL 106 &ccHand); 107 if(crtn) { 108 printError("CSSM_CSP_CreateKeyGenContext", crtn); 109 return testError(quiet); 110 } 111 112 if((inParams == NULL) && (outParams != NULL)) { 113 /* explicitly generate params and return them to caller */ 114 outParams->Data = NULL; 115 outParams->Length = 0; 116 crtn = CSSM_GenerateAlgorithmParams(ccHand, 117 keySizeInBits, outParams); 118 if(crtn) { 119 printError("CSSM_GenerateAlgorithmParams", crtn); 120 return testError(quiet); 121 } 122 } 123 124 if((privForm != CSSM_KEYBLOB_RAW_FORMAT_NONE) && !privIsRef) { 125 crtn = AddContextAttribute(ccHand, 126 CSSM_ATTRIBUTE_PRIVATE_KEY_FORMAT, 127 sizeof(uint32), 128 CAT_Uint32, 129 NULL, 130 privForm); 131 if(crtn) { 132 printError("AddContextAttribute(CSSM_ATTRIBUTE_PRIVATE_KEY_" 133 "FORMAT)", crtn); 134 return crtn; 135 } 136 } 137 if((pubForm != CSSM_KEYBLOB_RAW_FORMAT_NONE) && !pubIsRef) { 138 crtn = AddContextAttribute(ccHand, 139 CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT, 140 sizeof(uint32), 141 CAT_Uint32, 142 NULL, 143 pubForm); 144 if(crtn) { 145 printError("AddContextAttribute(CSSM_ATTRIBUTE_PUBLIC_KEY_" 146 "FORMAT)", crtn); 147 return crtn; 148 } 149 } 150 151 uint32 privAttr; 152 uint32 pubAttr = CSSM_KEYATTR_EXTRACTABLE; 153 if(pubIsRef) { 154 pubAttr |= CSSM_KEYATTR_RETURN_REF; 155 } 156 else { 157 pubAttr |= CSSM_KEYATTR_RETURN_DATA; 158 } 159 if(privIsRef) { 160 privAttr = CSSM_KEYATTR_RETURN_REF; 161 } 162 else { 163 privAttr = CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE; 164 } 165 crtn = CSSM_GenerateKeyPair(ccHand, 166 /* 167 * Public key specification. We specify raw key format 168 * here since we have to have access to the raw public key 169 * bits in order to perform D-H key exchange. 170 */ 171 CSSM_KEYUSE_DERIVE, // only legal use of a Diffie-Hellman key 172 pubAttr, 173 &labelData, 174 pubKey, 175 /* private key specification */ 176 CSSM_KEYUSE_DERIVE, 177 privAttr, 178 &labelData, // same labels 179 NULL, // CredAndAclEntry 180 privKey); 181 if(crtn) { 182 printError("CSSM_GenerateKeyPair", crtn); 183 return testError(quiet); 184 } 185 if(!privIsRef && (privKey->KeyHeader.Format != expectPrivForm)) { 186 printf("***Expected priv format %s got %s\n", 187 blobFormStr(expectPrivForm), 188 blobFormStr(privKey->KeyHeader.Format)); 189 return testError(quiet); 190 } 191 if(!pubIsRef && (pubKey->KeyHeader.Format != expectPubForm)) { 192 printf("***Expected pub format %s got %s\n", 193 blobFormStr(expectPubForm), 194 blobFormStr(pubKey->KeyHeader.Format)); 195 return testError(quiet); 196 } 197 CSSM_DeleteContext(ccHand); 198 return crtn; 199} 200 201/* 202 * Perform Diffie-Hellman key exchange. 203 * Given "our" private key (in the form of a CSSM_KEY) and "their" public 204 * key (in the form of a raw blob of bytes), cook up a symmetric key. 205 */ 206static int dhKeyExchange( 207 CSSM_CSP_HANDLE cspHand, 208 CSSM_KEY_PTR myPrivKey, 209 CSSM_KEY_PTR theirPubKey, 210 CSSM_KEY_PTR derivedKey, // RETURNED 211 uint32 deriveKeySizeInBits, 212 CSSM_ALGORITHMS derivedKeyAlg, 213 uint32 derivedKeyUsage, 214 uint32 derivedKeyAttr, 215 CSSM_BOOL quiet) 216{ 217 CSSM_RETURN crtn; 218 CSSM_ACCESS_CREDENTIALS creds; 219 CSSM_CC_HANDLE ccHand; 220 CSSM_DATA labelData = 221 { strlen(USAGE_DEF), (uint8 *)USAGE_DEF }; 222 223 memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS)); 224 memset(derivedKey, 0, sizeof(CSSM_KEY)); 225 226 crtn = CSSM_CSP_CreateDeriveKeyContext(cspHand, 227 CSSM_ALGID_DH, 228 derivedKeyAlg, 229 deriveKeySizeInBits, 230 &creds, 231 myPrivKey, // BaseKey 232 0, // IterationCount 233 0, // Salt 234 0, // Seed 235 &ccHand); 236 if(crtn) { 237 printError("CSSM_CSP_CreateDeriveKeyContext", crtn); 238 return testError(quiet); 239 } 240 241 /* 242 * Public key passed in as CSSM_DATA *Param - only if 243 * the pub key is in raw PKCS3 form 244 */ 245 CSSM_DATA nullParam = {0, NULL}; 246 CSSM_DATA_PTR paramPtr; 247 CSSM_KEYHEADER &hdr = theirPubKey->KeyHeader; 248 if((hdr.BlobType == CSSM_KEYBLOB_RAW) && 249 (hdr.Format == CSSM_KEYBLOB_RAW_FORMAT_PKCS3)) { 250 /* simple case */ 251 paramPtr = &theirPubKey->KeyData; 252 } 253 else { 254 /* add this pub key as a context attr */ 255 crtn = AddContextAttribute(ccHand, 256 CSSM_ATTRIBUTE_PUBLIC_KEY, 257 sizeof(CSSM_KEY), 258 CAT_Ptr, 259 (void *)theirPubKey, 260 0); 261 if(crtn) { 262 printError("AddContextAttribute(CSSM_ATTRIBUTE_PUBLIC_KEY)", 263 crtn); 264 return crtn; 265 } 266 paramPtr = &nullParam; 267 } 268 269 crtn = CSSM_DeriveKey(ccHand, 270 paramPtr, 271 derivedKeyUsage, 272 derivedKeyAttr, 273 &labelData, 274 NULL, // cread/acl 275 derivedKey); 276 if(crtn) { 277 printError("CSSM_DeriveKey", crtn); 278 } 279 CSSM_DeleteContext(ccHand); 280 return crtn; 281} 282 283static CSSM_DATA someIv = {16, (uint8 *)"Some enchanted init vector" }; 284 285/* 286 * Encrypt ptext with myDeriveKey ==> ctext 287 * decrypt ctext with theirDeriveKey ==> rptext 288 * ensure ptext == rptext 289 */ 290static int doEncryptTest( 291 CSSM_CSP_HANDLE cspHand, 292 const CSSM_DATA *ptext, 293 CSSM_KEY_PTR myDeriveKey, 294 CSSM_KEY_PTR theirDeriveKey, 295 CSSM_ALGORITHMS encrAlg, 296 uint32 encrMode, 297 CSSM_PADDING encrPadding, 298 CSSM_BOOL quiet) 299{ 300 CSSM_DATA ctext = {0, NULL}; 301 CSSM_DATA rptext = {0, NULL}; 302 CSSM_RETURN crtn; 303 304 crtn = cspEncrypt(cspHand, 305 encrAlg, 306 encrMode, 307 encrPadding, 308 myDeriveKey, 309 NULL, // 2nd key 310 0, // effective key size 311 0, // rounds 312 &someIv, 313 ptext, 314 &ctext, 315 CSSM_FALSE); // mallocCtext 316 if(crtn) { 317 return testError(quiet); 318 } 319 crtn = cspDecrypt(cspHand, 320 encrAlg, 321 encrMode, 322 encrPadding, 323 theirDeriveKey, 324 NULL, // 2nd key 325 0, // effective key size 326 0, // rounds 327 &someIv, 328 &ctext, 329 &rptext, 330 CSSM_FALSE); // mallocCtext 331 if(crtn) { 332 return testError(quiet); 333 } 334 if(!appCompareCssmData(ptext, &rptext)) { 335 printf("***DATA MISCOMPARE***\n"); 336 return testError(quiet); 337 } 338 appFree(ctext.Data, NULL); 339 appFree(rptext.Data, NULL); 340 return 0; 341} 342 343int doTest( 344 CSSM_CSP_HANDLE cspHand, 345 const CSSM_DATA *ptext, 346 CSSM_BOOL pubIsRef, 347 CSSM_KEYBLOB_FORMAT pubForm, // NONE, PKCS3, X509 348 CSSM_KEYBLOB_FORMAT expectPubForm, // PKCS3, X509 349 CSSM_BOOL privIsRef, 350 CSSM_KEYBLOB_FORMAT privForm, // NONE, PKCS3, PKCS8 351 CSSM_KEYBLOB_FORMAT expectPrivForm, // PKCS3, PKCS8 352 CSSM_BOOL sym1IsRef, 353 CSSM_BOOL sym2IsRef, 354 const CSSM_DATA *inParams, // optional 355 CSSM_DATA_PTR outParams, // optional 356 uint32 keySizeInBits, 357 CSSM_BOOL quiet) 358{ 359 CSSM_KEY myPriv; 360 CSSM_KEY myPub; 361 CSSM_KEY theirPriv; 362 CSSM_KEY theirPub; 363 int rtn = 0; 364 365 /* generate two key pairs */ 366 if(dhKeyGen(cspHand, 367 &myPub, 368 pubIsRef, 369 pubForm, 370 expectPubForm, 371 &myPriv, 372 privIsRef, 373 privForm, 374 expectPrivForm, 375 inParams, 376 outParams, 377 keySizeInBits, 378 quiet)) { 379 return 1; 380 } 381 382 /* note this MUST match the params either specified or generated 383 * in previous call */ 384 if((inParams == NULL) && (outParams == NULL)) { 385 printf("***BRRZAP! Must provide a way to match D-H parameters!\n"); 386 exit(1); 387 } 388 const CSSM_DATA *theParams = inParams; 389 if(theParams == NULL) { 390 theParams = outParams; 391 } 392 if(dhKeyGen(cspHand, 393 &theirPub, 394 pubIsRef, 395 /* let's always use default for this pair */ 396 CSSM_KEYBLOB_RAW_FORMAT_NONE, 397 CSSM_KEYBLOB_RAW_FORMAT_PKCS3, // we know this is the default 398 &theirPriv, 399 privIsRef, 400 /* let's always use default for this pair */ 401 CSSM_KEYBLOB_RAW_FORMAT_NONE, 402 CSSM_KEYBLOB_RAW_FORMAT_PKCS3, // we know this is the default 403 theParams, 404 NULL, // outParams 405 keySizeInBits, 406 quiet)) { 407 return 1; 408 } 409 410 /* derive two keys, ensure they match */ 411 CSSM_KEY myDerive; 412 CSSM_KEY theirDerive; 413 uint32 myDeriveAttr; 414 uint32 theirDeriveAttr; 415 if(sym1IsRef) { 416 myDeriveAttr = CSSM_KEYATTR_RETURN_REF; 417 } 418 else { 419 myDeriveAttr = 420 CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE; 421 } 422 if(sym2IsRef) { 423 theirDeriveAttr = CSSM_KEYATTR_RETURN_REF; 424 } 425 else { 426 theirDeriveAttr = 427 CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE; 428 } 429 if(dhKeyExchange(cspHand, 430 &myPriv, 431 &theirPub, 432 &myDerive, 433 DERIVE_KEY_SIZE, 434 DERIVE_KEY_ALG, 435 CSSM_KEYUSE_ENCRYPT, 436 myDeriveAttr, 437 quiet)) { 438 return testError(quiet); 439 } 440 if(dhKeyExchange(cspHand, 441 &theirPriv, 442 &myPub, 443 &theirDerive, 444 DERIVE_KEY_SIZE, 445 DERIVE_KEY_ALG, 446 CSSM_KEYUSE_DECRYPT, 447 theirDeriveAttr, 448 quiet)) { 449 return testError(quiet); 450 } 451 452 if(doEncryptTest(cspHand, 453 ptext, 454 &myDerive, 455 &theirDerive, 456 ENCR_ALG, 457 ENCR_MODE, 458 ENCR_PADDING, 459 quiet)) { 460 return 1; 461 } 462 463 cspFreeKey(cspHand, &myPub); 464 cspFreeKey(cspHand, &myPriv); 465 cspFreeKey(cspHand, &theirPub); 466 cspFreeKey(cspHand, &theirPriv); 467 cspFreeKey(cspHand, &myDerive); 468 cspFreeKey(cspHand, &theirDerive); 469 return rtn; 470} 471 472int main(int argc, char **argv) 473{ 474 int arg; 475 char *argp; 476 CSSM_CSP_HANDLE cspHand; 477 unsigned loop; 478 int i; 479 CSSM_DATA inParams = {0, NULL}; 480 CSSM_DATA outParams = {0, NULL}; 481 CSSM_DATA_PTR inParamPtr = NULL; 482 CSSM_DATA_PTR outParamPtr = NULL; 483 CSSM_DATA ptext; 484 CSSM_BOOL pubIsRef; 485 CSSM_BOOL privIsRef; 486 CSSM_BOOL sym1IsRef; 487 CSSM_BOOL sym2IsRef; 488 CSSM_KEYBLOB_FORMAT pubForm; // NONE, PKCS3, X509 489 CSSM_KEYBLOB_FORMAT expectPubForm; // PKCS3, X509 490 CSSM_KEYBLOB_FORMAT privForm; // NONE, PKCS3, PKCS8 491 CSSM_KEYBLOB_FORMAT expectPrivForm; // PKCS3, PKCS8 492 493 /* user-spec'd parameters */ 494 unsigned keySize = KEY_SIZE_DEF; 495 unsigned pauseInterval = 0; 496 unsigned loops = LOOPS_DEF; 497 CSSM_BOOL quiet = CSSM_FALSE; 498 CSSM_BOOL verbose = CSSM_FALSE; 499 CSSM_BOOL bareCsp = CSSM_TRUE; 500 char *inFileName = NULL; 501 char *outFileName = NULL; 502 503 for(arg=1; arg<argc; arg++) { 504 argp = argv[arg]; 505 switch(argp[0]) { 506 case 'k': 507 keySize = atoi(&argp[2]); 508 break; 509 case 'l': 510 loops = atoi(&argp[2]); 511 break; 512 case 'p': 513 pauseInterval = atoi(&argp[2]); 514 break; 515 case 'i': 516 inFileName = &argp[2]; 517 break; 518 case 'o': 519 outFileName = &argp[2]; 520 break; 521 case 'D': 522 bareCsp = CSSM_FALSE; 523 break; 524 case 'q': 525 quiet = CSSM_TRUE; 526 break; 527 case 'v': 528 verbose = CSSM_TRUE; 529 break; 530 default: 531 usage(argv); 532 } 533 } 534 535 cspHand = cspDlDbStartup(bareCsp, NULL); 536 if(cspHand == 0) { 537 exit(1); 538 } 539 540 if(!bareCsp && 541 !CSPDL_DSA_GEN_PARAMS && 542 (inFileName == NULL)) { 543 /* 544 * For now, CSPDL can not do gen parameters. This only 545 * works if we're supplied params externally (which most 546 * likely were generated from the bare CSP). 547 */ 548 printf("*** %s can't run with CSPDL unless you supply DH " 549 "parameters.\n", argv[0]); 550 exit(1); 551 } 552 553 /* optionally fetch algorithm parameters from a file */ 554 if(inFileName) { 555 unsigned len; 556 int r = readFile(inFileName, &inParams.Data, &len); 557 if(r) { 558 printf("***Can't read parameters from %s; aborting.\n", 559 inFileName); 560 exit(1); 561 } 562 inParams.Length = len; 563 /* constant from now on */ 564 inParamPtr = &inParams; 565 } 566 else { 567 /* first time thru, no user-supplied parameters; generate them and 568 * save in outParams */ 569 outParamPtr = &outParams; 570 } 571 572 ptext.Data = (uint8 *)appMalloc(MAX_PTEXT_SIZE, NULL); 573 /* ptext length set in test loop */ 574 575 printf("Starting %s; args: ", argv[0]); 576 for(i=1; i<argc; i++) { 577 printf("%s ", argv[i]); 578 } 579 printf("\n"); 580 for(loop=1; ; loop++) { 581 if(!quiet) { 582 printf("...Loop %d\n", loop); 583 } 584 simpleGenData(&ptext, 10, MAX_PTEXT_SIZE); 585 586 /* mix up raw and ref keys, except for CSPDL which 587 * requires all ref keys */ 588 if(bareCsp) { 589 pubIsRef = (loop & 1) ? CSSM_TRUE : CSSM_FALSE; 590 privIsRef = (loop & 2) ? CSSM_TRUE : CSSM_FALSE; 591 sym1IsRef = (loop & 4) ? CSSM_TRUE : CSSM_FALSE; 592 sym2IsRef = (loop & 8) ? CSSM_TRUE : CSSM_FALSE; 593 } 594 else { 595 pubIsRef = privIsRef = sym1IsRef = sym2IsRef = CSSM_TRUE; 596 } 597 if(!privIsRef) { 598 int die = genRand(1,3); 599 switch(die) { 600 case 1: 601 privForm = CSSM_KEYBLOB_RAW_FORMAT_NONE; 602 expectPrivForm = CSSM_KEYBLOB_RAW_FORMAT_PKCS3; 603 break; 604 case 2: 605 privForm = expectPrivForm = 606 CSSM_KEYBLOB_RAW_FORMAT_PKCS3; 607 break; 608 case 3: 609 privForm = expectPrivForm = 610 CSSM_KEYBLOB_RAW_FORMAT_PKCS8; 611 break; 612 } 613 if(verbose) { 614 printf("...privIsRef false; form %s, expectForm %s\n", 615 blobFormStr(privForm), blobFormStr(expectPrivForm)); 616 } 617 } 618 else { 619 privForm = expectPrivForm = CSSM_KEYBLOB_RAW_FORMAT_NONE; 620 if(verbose) { 621 printf("...privIsRef true\n"); 622 } 623 } 624 if(!pubIsRef) { 625 int die = genRand(1,3); 626 switch(die) { 627 case 1: 628 pubForm = CSSM_KEYBLOB_RAW_FORMAT_NONE; 629 expectPubForm = CSSM_KEYBLOB_RAW_FORMAT_PKCS3; 630 break; 631 case 2: 632 pubForm = expectPubForm = 633 CSSM_KEYBLOB_RAW_FORMAT_PKCS3; 634 break; 635 case 3: 636 pubForm = expectPubForm = 637 CSSM_KEYBLOB_RAW_FORMAT_X509; 638 break; 639 } 640 if(verbose) { 641 printf("...pubIsRef false; form %s, expectForm %s\n", 642 blobFormStr(pubForm), blobFormStr(expectPubForm)); 643 } 644 } 645 else { 646 pubForm = expectPubForm = CSSM_KEYBLOB_RAW_FORMAT_NONE; 647 if(verbose) { 648 printf("...pubIsRef true\n"); 649 } 650 } 651 i = doTest(cspHand, 652 &ptext, 653 pubIsRef, 654 pubForm, 655 expectPubForm, 656 privIsRef, 657 privForm, 658 expectPrivForm, 659 sym1IsRef, 660 sym2IsRef, 661 inParamPtr, 662 outParamPtr, 663 keySize, 664 quiet); 665 if(i) { 666 break; 667 } 668 if(loop == 1) { 669 /* first time thru */ 670 if(outFileName) { 671 /* save parameters for another run */ 672 i = writeFile(outFileName, outParams.Data, outParams.Length); 673 if(i) { 674 printf("***Error writing params to %s; continuing.\n", 675 outFileName); 676 } 677 else { 678 if(!quiet) { 679 printf("...wrote %lu bytes to %s\n", 680 outParams.Length, outFileName); 681 } 682 } 683 } 684 if(!inFileName) { 685 /* from now on, use the parameters we just generated */ 686 inParamPtr = &outParams; 687 } 688 /* and in any case don't fetch any more params */ 689 outParamPtr = NULL; 690 } 691 if(loops && (loop == loops)) { 692 break; 693 } 694 if(pauseInterval && ((loop % pauseInterval) == 0)) { 695 char inch; 696 697 fpurge(stdin); 698 printf("Hit CR to proceed, q to quit: "); 699 inch = getchar(); 700 if(inch == 'q') { 701 break; 702 } 703 } 704 } 705 appFree(ptext.Data, NULL); 706 CSSM_ModuleDetach(cspHand); 707 if(!quiet) { 708 printf("OK\n"); 709 } 710 return 0; 711} 712