1/* Copyright (c) 1998,2003-2006,2008 Apple Inc. 2 * 3 * caVerify.cpp 4 * 5 * Verify proper detection of basicConstraints.cA 6 */ 7 8#include <utilLib/common.h> 9#include <utilLib/cspwrap.h> 10#include <security_cdsa_utils/cuFileIo.h> 11#include <clAppUtils/CertBuilderApp.h> 12#include <clAppUtils/clutils.h> 13#include <clAppUtils/timeStr.h> 14#include <clAppUtils/tpUtils.h> 15#include <security_cdsa_utils/cuPrintCert.h> 16#include <stdlib.h> 17#include <stdio.h> 18#include <string.h> 19#include <Security/cssm.h> 20#include <Security/x509defs.h> 21#include <Security/oidsattr.h> 22#include <Security/oidscert.h> 23#include <Security/oidsalg.h> 24#include <Security/certextensions.h> 25#include <Security/cssmapple.h> 26#include <string.h> 27 28/* define nonzero to build on Puma */ 29#define PUMA_BUILD 0 30 31/* default key and signature algorithm */ 32#define SIG_ALG_DEFAULT CSSM_ALGID_SHA1WithRSA 33#define KEY_ALG_DEFAULT CSSM_ALGID_RSA 34 35#define NUM_CERTS_DEF 5 /* default is random from 2 to this */ 36#define NUM_LOOPS_DEF 100 37 38#if PUMA_BUILD 39extern "C" { 40 void cssmPerror(const char *how, CSSM_RETURN error); 41} 42#endif /* PUMA_BUILD */ 43 44static void usage(char **argv) 45{ 46 printf("Usage: %s [options]\n", argv[0]); 47 printf("Options:\n"); 48 printf(" n=numCerts (default=random from 2 to %d)\n", NUM_CERTS_DEF); 49 printf(" a=alg where alg is s(RSA/SHA1), 5(RSA/MD5), f(FEE/MD5), " 50 "F(FEE/SHA1), e(ECDSA), E(ANSI/ECDSA), 6(ECDSA/SHA256)\n"); 51 printf(" k=keySizeInBits\n"); 52 printf(" c (dump certs on error)\n"); 53 printf(" l=numLoops (default = %d)\n", NUM_LOOPS_DEF); 54 exit(1); 55} 56 57void showCerts( 58 const CSSM_DATA *certs, 59 unsigned numCerts) 60{ 61 unsigned i; 62 63 for(i=0; i<numCerts; i++) { 64 printf("======== cert %d ========\n", i); 65 printCert(certs[i].Data, certs[i].Length, CSSM_FALSE); 66 printf("\n\n"); 67 } 68} 69 70void writeCerts( 71 const CSSM_DATA *certs, 72 unsigned numCerts) 73{ 74 unsigned i; 75 char fileName[80]; 76 77 for(i=0; i<numCerts; i++) { 78 sprintf(fileName, "caVerifyCert%u.cer", i); 79 writeFile(fileName, certs[i].Data, certs[i].Length); 80 printf("....wrote %lu bytes to %s\n", certs[i].Length, fileName); 81 } 82} 83 84/* 85 * Generate a cert chain using specified key pairs and extensions. 86 * The last cert in the chain (certs[numCerts-1]) is a root cert, 87 * self-signed. 88 */ 89static CSSM_RETURN tpGenCertsExten( 90 CSSM_CSP_HANDLE cspHand, 91 CSSM_CL_HANDLE clHand, 92 CSSM_ALGORITHMS sigAlg, /* CSSM_ALGID_SHA1WithRSA, etc. */ 93 unsigned numCerts, 94 /* per-cert arrays */ 95 const char *nameBase, /* C string */ 96 CSSM_KEY_PTR pubKeys, /* array of public keys */ 97 CSSM_KEY_PTR privKeys, /* array of private keys */ 98 CSSM_X509_EXTENSION **extensions, 99 unsigned *numExtensions, 100 CSSM_DATA_PTR certs, /* array of certs RETURNED here */ 101 const char *notBeforeStr, /* from genTimeAtNowPlus() */ 102 const char *notAfterStr) /* from genTimeAtNowPlus() */ 103 104{ 105 int dex; 106 CSSM_RETURN crtn; 107 CSSM_X509_NAME *issuerName = NULL; 108 CSSM_X509_NAME *subjectName = NULL; 109 CSSM_X509_TIME *notBefore; // UTC-style "not before" time 110 CSSM_X509_TIME *notAfter; // UTC-style "not after" time 111 CSSM_DATA_PTR rawCert = NULL; // from CSSM_CL_CertCreateTemplate 112 CSSM_DATA signedCert; // from CSSM_CL_CertSign 113 uint32 rtn; 114 CSSM_KEY_PTR signerKey; // signs the cert 115 CSSM_CC_HANDLE signContext; 116 char nameStr[100]; 117 CSSM_DATA_PTR thisCert; // ptr into certs[] 118 CB_NameOid nameOid; 119 120 nameOid.oid = &CSSMOID_OrganizationName; // const 121 nameOid.string = nameStr; 122 123 /* main loop - once per keypair/cert - starting at end/root */ 124 for(dex=numCerts-1; dex>=0; dex--) { 125 thisCert = &certs[dex]; 126 127 thisCert->Data = NULL; 128 thisCert->Length = 0; 129 130 sprintf(nameStr, "%s%04d", nameBase, dex); 131 if(issuerName == NULL) { 132 /* last (root) cert - subject same as issuer */ 133 issuerName = CB_BuildX509Name(&nameOid, 1); 134 /* self-signed */ 135 signerKey = &privKeys[dex]; 136 } 137 else { 138 /* previous subject becomes current issuer */ 139 CB_FreeX509Name(issuerName); 140 issuerName = subjectName; 141 signerKey = &privKeys[dex+1]; 142 } 143 subjectName = CB_BuildX509Name(&nameOid, 1); 144 if((subjectName == NULL) || (issuerName == NULL)) { 145 printf("Error creating X509Names\n"); 146 crtn = CSSMERR_CSSM_MEMORY_ERROR; 147 break; 148 } 149 150 /* 151 * not before/after in Y2k-compliant generalized time format. 152 * These come preformatted from our caller. 153 */ 154 notBefore = CB_BuildX509Time(0, notBeforeStr); 155 notAfter = CB_BuildX509Time(0, notAfterStr); 156 157 /* 158 * Cook up cert template 159 * Note serial number would be app-specified in real world 160 */ 161 rawCert = CB_MakeCertTemplate(clHand, 162 0x12345 + dex, // serial number 163 issuerName, 164 subjectName, 165 notBefore, 166 notAfter, 167 &pubKeys[dex], 168 sigAlg, 169 NULL, // subj unique ID 170 NULL, // issuer unique ID 171 extensions[dex], // extensions 172 numExtensions[dex]); // numExtensions 173 174 if(rawCert == NULL) { 175 crtn = CSSM_ERRCODE_INTERNAL_ERROR; 176 break; 177 } 178 179 /* Free the stuff we allocd to get here */ 180 CB_FreeX509Time(notBefore); 181 CB_FreeX509Time(notAfter); 182 183 /**** sign the cert ****/ 184 /* 1. get a signing context */ 185 crtn = CSSM_CSP_CreateSignatureContext(cspHand, 186 sigAlg, 187 NULL, // no passphrase for now 188 signerKey, 189 &signContext); 190 if(crtn) { 191 printError("CreateSignatureContext", crtn); 192 break; 193 } 194 195 /* 2. use CL to sign the cert */ 196 signedCert.Data = NULL; 197 signedCert.Length = 0; 198 crtn = CSSM_CL_CertSign(clHand, 199 signContext, 200 rawCert, // CertToBeSigned 201 NULL, // SignScope per spec 202 0, // ScopeSize per spec 203 &signedCert); 204 if(crtn) { 205 printError("CSSM_CL_CertSign", crtn); 206 break; 207 } 208 209 /* 3. delete signing context */ 210 crtn = CSSM_DeleteContext(signContext); 211 if(crtn) { 212 printError("CSSM_DeleteContext", crtn); 213 break; 214 } 215 216 /* 217 * CSSM_CL_CertSign() returned us a mallocd CSSM_DATA. Copy 218 * its fields to caller's cert. 219 */ 220 certs[dex] = signedCert; 221 222 /* and the raw unsigned cert as well */ 223 appFreeCssmData(rawCert, CSSM_TRUE); 224 rtn = 0; 225 } 226 227 /* free resources */ 228 if(issuerName != NULL) { 229 CB_FreeX509Name(issuerName); 230 } 231 if(subjectName != NULL) { 232 CB_FreeX509Name(subjectName); 233 } 234 return crtn; 235} 236 237static int doTest( 238 CSSM_CSP_HANDLE cspHand, // CSP handle 239 CSSM_CL_HANDLE clHand, // CL handle 240 CSSM_TP_HANDLE tpHand, // TP handle 241 unsigned numCerts, // >= 2 242 CSSM_KEY_PTR pubKeys, 243 CSSM_KEY_PTR privKeys, 244 CSSM_ALGORITHMS sigAlg, 245 CSSM_BOOL expectFail, 246 CSSM_BOOL dumpCerts, 247 CSSM_BOOL quiet) 248{ 249 CSSM_DATA_PTR certs; 250 CSSM_X509_EXTENSION **extens; 251 unsigned *numExtens; 252 char *notBeforeStr = genTimeAtNowPlus(0); 253 char *notAfterStr = genTimeAtNowPlus(10000); 254 unsigned certDex; 255 CE_BasicConstraints *bc; 256 CSSM_X509_EXTENSION *thisExten; 257 CE_BasicConstraints *thisBc; 258 const char *failMode = "not set - internal error"; 259 CSSM_RETURN crtn; 260 unsigned badCertDex = 0; 261 262 certs = (CSSM_DATA_PTR)malloc(sizeof(CSSM_DATA) * numCerts); 263 memset(certs, 0, sizeof(CSSM_DATA) * numCerts); 264 265 /* 266 * For now just zero or one extension per cert - basicConstraints. 267 * Eventually we'll want to test keyUsage as well. 268 */ 269 extens = (CSSM_X509_EXTENSION **)malloc(sizeof(CSSM_X509_EXTENSION *) * numCerts); 270 memset(extens, 0, sizeof(CSSM_X509_EXTENSION *) * numCerts); 271 numExtens = (unsigned *)malloc(sizeof(unsigned) * numCerts); 272 bc = (CE_BasicConstraints *)malloc(sizeof(CE_BasicConstraints) * numCerts); 273 274 /* 275 * Set up all extensions for success 276 */ 277 for(certDex=0; certDex<numCerts; certDex++) { 278 extens[certDex] = (CSSM_X509_EXTENSION *)malloc(sizeof(CSSM_X509_EXTENSION)); 279 numExtens[certDex] = 1; 280 thisExten = extens[certDex]; 281 thisBc = &bc[certDex]; 282 283 thisExten->extnId = CSSMOID_BasicConstraints; 284 thisExten->critical = CSSM_TRUE; 285 thisExten->format = CSSM_X509_DATAFORMAT_PARSED; 286 thisExten->value.parsedValue = thisBc; 287 thisExten->BERvalue.Data = NULL; 288 thisExten->BERvalue.Length = 0; 289 290 if(certDex == 0) { 291 /* leaf - flip coin to determine presence of basicConstraints */ 292 int coin = genRand(1,2); 293 if(coin == 1) { 294 /* basicConstraints, !cA */ 295 thisBc->cA = CSSM_FALSE; 296 thisBc->pathLenConstraintPresent = CSSM_FALSE; 297 thisBc->pathLenConstraint = 0; 298 } 299 else { 300 /* !basicConstraints, !cA by default */ 301 numExtens[certDex] = 0; 302 } 303 } 304 else if(certDex == (numCerts-1)) { 305 /* root - flip coin to determine presence of basicConstraints */ 306 int coin = genRand(1,2); 307 if(coin == 1) { 308 /* basicConstraints, cA */ 309 thisBc->cA = CSSM_TRUE; 310 /* flip coin to determine present of pathLenConstraint */ 311 coin = genRand(1,2); 312 if(coin == 1) { 313 thisBc->pathLenConstraintPresent = CSSM_FALSE; 314 thisBc->pathLenConstraint = 0; 315 } 316 else { 317 thisBc->pathLenConstraintPresent = CSSM_TRUE; 318 thisBc->pathLenConstraint = genRand(certDex-1, numCerts+1); 319 } 320 } 321 else { 322 /* !basicConstraints, cA by default */ 323 numExtens[certDex] = 0; 324 } 325 } 326 else { 327 /* intermediate = cA required */ 328 thisBc->cA = CSSM_TRUE; 329 /* flip coin to determine presence of pathLenConstraint */ 330 int coin = genRand(1,2); 331 if(coin == 1) { 332 thisBc->pathLenConstraintPresent = CSSM_FALSE; 333 thisBc->pathLenConstraint = 0; 334 } 335 else { 336 thisBc->pathLenConstraintPresent = CSSM_TRUE; 337 thisBc->pathLenConstraint = genRand(certDex-1, numCerts+1); 338 } 339 } 340 } 341 342 if(expectFail) { 343 /* introduce a failure */ 344 if(numCerts == 2) { 345 /* only possible failure is explicit !cA in root */ 346 /* don't assume presence of BC exten */ 347 badCertDex = 1; 348 thisExten = extens[badCertDex]; 349 thisBc = &bc[badCertDex]; 350 thisBc->cA = CSSM_FALSE; 351 thisBc->pathLenConstraintPresent = CSSM_FALSE; 352 bc->pathLenConstraint = 0; 353 numExtens[badCertDex] = 1; 354 failMode = "Explicit !cA in root"; 355 } 356 else { 357 /* roll the dice to select an intermediate cert */ 358 badCertDex = genRand(1, numCerts-2); 359 thisExten = extens[badCertDex]; 360 if((thisExten == NULL) || (numExtens[badCertDex] == 0)) { 361 printf("***INTERNAL SCREWUP\n"); 362 exit(1); 363 } 364 thisBc = &bc[badCertDex]; 365 366 /* 367 * roll die: fail by 368 * -- no BasicConstraints 369 * -- !cA 370 * -- bad pathLenConstraint 371 */ 372 int die = genRand(1,3); 373 if((die == 1) && 374 (badCertDex != 1)) { // last cA doesn't need pathLenConstraint 375 thisBc->pathLenConstraintPresent = CSSM_TRUE; 376 thisBc->pathLenConstraint = badCertDex - 2; // one short 377 failMode = "Short pathLenConstraint"; 378 } 379 else if(die == 2) { 380 thisBc->cA = CSSM_FALSE; 381 failMode = "Explicit !cA in intermediate"; 382 } 383 else { 384 /* no extension */ 385 numExtens[badCertDex] = 0; 386 failMode = "No BasicConstraints in intermediate"; 387 } 388 } 389 } 390 if(!quiet && expectFail) { 391 printf(" ...bad cert at index %d: %s\n", badCertDex, failMode); 392 } 393 394 /* here we go - create cert chain */ 395 crtn = tpGenCertsExten(cspHand, 396 clHand, 397 sigAlg, 398 numCerts, 399 "caVerify", // nameBase 400 pubKeys, 401 privKeys, 402 extens, 403 numExtens, 404 certs, 405 notBeforeStr, 406 notAfterStr); 407 if(crtn) { 408 printError("tpGenCertsExten", crtn); 409 return crtn; // and leak like crazy 410 } 411 412 CSSM_CERTGROUP cgrp; 413 memset(&cgrp, 0, sizeof(CSSM_CERTGROUP)); 414 cgrp.NumCerts = numCerts; 415 #if PUMA_BUILD 416 cgrp.CertGroupType = CSSM_CERTGROUP_ENCODED_CERT; 417 #else 418 /* Jaguar */ 419 cgrp.CertGroupType = CSSM_CERTGROUP_DATA; 420 #endif /* PUMA_BUILD */ 421 cgrp.CertType = CSSM_CERT_X_509v3; 422 cgrp.CertEncoding = CSSM_CERT_ENCODING_DER; 423 cgrp.GroupList.CertList = certs; 424 425 #if PUMA_BUILD 426 crtn = tpCertGroupVerify(tpHand, 427 clHand, 428 cspHand, 429 NULL, // DlDbList 430 &CSSMOID_APPLE_X509_BASIC, // SSL requires built-in root match 431 &cgrp, 432 /* pass in OUR ROOT as anchors */ 433 (CSSM_DATA_PTR)&certs[numCerts-1], // anchorCerts 434 1, 435 CSSM_TP_STOP_ON_POLICY, 436 CSSM_FALSE, // allowExpired 437 NULL); // vfyResult 438 #else 439 /* Jaguar */ 440 crtn = tpCertGroupVerify(tpHand, 441 clHand, 442 cspHand, 443 NULL, // DlDbList 444 &CSSMOID_APPLE_TP_SSL, // may want to parameterize this 445 NULL, // fieldOpts for server name 446 NULL, // actionDataPtr for allow expired 447 NULL, // policyOpts 448 &cgrp, 449 /* pass in OUR ROOT as anchors */ 450 (CSSM_DATA_PTR)&certs[numCerts-1], // anchorCerts 451 1, 452 CSSM_TP_STOP_ON_POLICY, 453 NULL, // cssmTimeStr 454 NULL); // vfyResult 455 #endif /* PUMA_BUILD */ 456 if(expectFail) { 457 if(crtn != CSSMERR_TP_VERIFY_ACTION_FAILED) { 458 cssmPerror("***Expected error TP_VERIFY_ACTION_FAILED; got ", crtn); 459 printf(" Expected failure due to %s\n", failMode); 460 if(dumpCerts) { 461 showCerts(certs, numCerts); 462 writeCerts(certs, numCerts); 463 } 464 return testError(quiet); 465 } 466 } 467 else if(crtn) { 468 cssmPerror("Unexpected failure on tpCertGroupVerify", crtn); 469 if(dumpCerts) { 470 showCerts(certs, numCerts); 471 } 472 return testError(quiet); 473 } 474 475 /* clean up */ 476 return 0; 477} 478 479int main(int argc, char **argv) 480{ 481 CSSM_CL_HANDLE clHand; // CL handle 482 CSSM_CSP_HANDLE cspHand; // CSP handle 483 CSSM_TP_HANDLE tpHand; 484 CSSM_KEY_PTR pubKeys; 485 CSSM_KEY_PTR privKeys; 486 CSSM_RETURN crtn; 487 int arg; 488 unsigned certDex; 489 unsigned loopNum; 490 unsigned maxCerts; 491 492 /* user-spec'd variables */ 493 CSSM_ALGORITHMS keyAlg = KEY_ALG_DEFAULT; 494 CSSM_ALGORITHMS sigAlg = SIG_ALG_DEFAULT; 495 uint32 keySizeInBits = CSP_KEY_SIZE_DEFAULT; 496 unsigned numCerts = 0; // means random per loop 497 unsigned numLoops = NUM_LOOPS_DEF; 498 CSSM_BOOL quiet = CSSM_FALSE; 499 CSSM_BOOL dumpCerts = CSSM_FALSE; 500 501 for(arg=1; arg<argc; arg++) { 502 switch(argv[arg][0]) { 503 case 'a': 504 if((argv[arg][1] == '\0') || (argv[arg][2] == '\0')) { 505 usage(argv); 506 } 507 switch(argv[arg][2]) { 508 case 's': 509 keyAlg = CSSM_ALGID_RSA; 510 sigAlg = CSSM_ALGID_SHA1WithRSA; 511 break; 512 case '5': 513 keyAlg = CSSM_ALGID_RSA; 514 sigAlg = CSSM_ALGID_MD5WithRSA; 515 break; 516 case 'f': 517 keyAlg = CSSM_ALGID_FEE; 518 sigAlg = CSSM_ALGID_FEE_MD5; 519 break; 520 case 'F': 521 keyAlg = CSSM_ALGID_FEE; 522 sigAlg = CSSM_ALGID_FEE_SHA1; 523 break; 524 case 'e': 525 keyAlg = CSSM_ALGID_FEE; 526 sigAlg = CSSM_ALGID_SHA1WithECDSA; 527 break; 528 case 'E': 529 keyAlg = CSSM_ALGID_ECDSA; 530 sigAlg = CSSM_ALGID_SHA1WithECDSA; 531 break; 532 case '6': 533 keyAlg = CSSM_ALGID_ECDSA; 534 sigAlg = CSSM_ALGID_SHA256WithECDSA; 535 break; 536 default: 537 usage(argv); 538 } 539 break; 540 case 'k': 541 keySizeInBits = atoi(&argv[arg][2]); 542 break; 543 case 'l': 544 numLoops = atoi(&argv[arg][2]); 545 break; 546 case 'n': 547 numCerts = atoi(&argv[arg][2]); 548 break; 549 case 'c': 550 dumpCerts = CSSM_TRUE; 551 break; 552 case 'q': 553 quiet = CSSM_TRUE; 554 break; 555 default: 556 usage(argv); 557 } 558 } 559 560 /* connect to CL, TP, and CSP */ 561 clHand = clStartup(); 562 if(clHand == 0) { 563 return 0; 564 } 565 tpHand = tpStartup(); 566 if(tpHand == 0) { 567 return 0; 568 } 569 cspHand = cspStartup(); 570 if(cspHand == 0) { 571 return 0; 572 } 573 574 if(numCerts == 0) { 575 maxCerts = NUM_CERTS_DEF; // random, this is the max $ 576 } 577 else { 578 maxCerts = numCerts; // user-specd 579 } 580 581 printf("Starting caVerify; args: "); 582 for(unsigned i=1; i<(unsigned)argc; i++) { 583 printf("%s ", argv[i]); 584 } 585 printf("\n"); 586 587 /* cook up maxCerts key pairs */ 588 if(!quiet) { 589 printf("generating key pairs...\n"); 590 } 591 pubKeys = (CSSM_KEY_PTR)malloc(sizeof(CSSM_KEY) * maxCerts); 592 privKeys = (CSSM_KEY_PTR)malloc(sizeof(CSSM_KEY) * maxCerts); 593 for(certDex=0; certDex<maxCerts; certDex++) { 594 crtn = cspGenKeyPair(cspHand, 595 keyAlg, 596 "a key pair", 597 9, 598 keySizeInBits, 599 &pubKeys[certDex], 600 CSSM_FALSE, // pubIsRef - should work both ways, but not yet 601 CSSM_KEYUSE_VERIFY, 602 CSSM_KEYBLOB_RAW_FORMAT_NONE, 603 &privKeys[certDex], 604 CSSM_TRUE, // privIsRef - doesn't matter 605 CSSM_KEYUSE_SIGN, 606 CSSM_KEYBLOB_RAW_FORMAT_NONE, 607 CSSM_FALSE); 608 if(crtn) { 609 printError("cspGenKeyPair", crtn); 610 printf("***error generating key pair. Aborting.\n"); 611 exit(1); 612 } 613 } 614 615 for(loopNum=0; loopNum<numLoops; loopNum++) { 616 unsigned thisNumCerts; 617 618 /* random: num certs and whether this loop is to test a failure */ 619 if(numCerts) { 620 thisNumCerts = numCerts; 621 } 622 else { 623 thisNumCerts = genRand(2, NUM_CERTS_DEF); 624 } 625 int coin = genRand(1,2); 626 CSSM_BOOL expectFail = (coin == 1) ? CSSM_TRUE : CSSM_FALSE; 627 if(!quiet) { 628 printf("...loop %d numCerts %u expectFail %s\n", loopNum, 629 thisNumCerts, expectFail ? "TRUE" : "FALSE"); 630 } 631 if(doTest(cspHand, 632 clHand, 633 tpHand, 634 thisNumCerts, 635 pubKeys, 636 privKeys, 637 sigAlg, 638 expectFail, 639 dumpCerts, 640 quiet)) { 641 break; 642 } 643 } 644 /* clean up */ 645 return 0; 646} 647 648 649