/* Copyright (c) 1998,2003-2006,2008 Apple Inc. * * caVerify.cpp * * Verify proper detection of basicConstraints.cA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* define nonzero to build on Puma */ #define PUMA_BUILD 0 /* default key and signature algorithm */ #define SIG_ALG_DEFAULT CSSM_ALGID_SHA1WithRSA #define KEY_ALG_DEFAULT CSSM_ALGID_RSA #define NUM_CERTS_DEF 5 /* default is random from 2 to this */ #define NUM_LOOPS_DEF 100 #if PUMA_BUILD extern "C" { void cssmPerror(const char *how, CSSM_RETURN error); } #endif /* PUMA_BUILD */ static void usage(char **argv) { printf("Usage: %s [options]\n", argv[0]); printf("Options:\n"); printf(" n=numCerts (default=random from 2 to %d)\n", NUM_CERTS_DEF); printf(" a=alg where alg is s(RSA/SHA1), 5(RSA/MD5), f(FEE/MD5), " "F(FEE/SHA1), e(ECDSA), E(ANSI/ECDSA), 6(ECDSA/SHA256)\n"); printf(" k=keySizeInBits\n"); printf(" c (dump certs on error)\n"); printf(" l=numLoops (default = %d)\n", NUM_LOOPS_DEF); exit(1); } void showCerts( const CSSM_DATA *certs, unsigned numCerts) { unsigned i; for(i=0; i=0; dex--) { thisCert = &certs[dex]; thisCert->Data = NULL; thisCert->Length = 0; sprintf(nameStr, "%s%04d", nameBase, dex); if(issuerName == NULL) { /* last (root) cert - subject same as issuer */ issuerName = CB_BuildX509Name(&nameOid, 1); /* self-signed */ signerKey = &privKeys[dex]; } else { /* previous subject becomes current issuer */ CB_FreeX509Name(issuerName); issuerName = subjectName; signerKey = &privKeys[dex+1]; } subjectName = CB_BuildX509Name(&nameOid, 1); if((subjectName == NULL) || (issuerName == NULL)) { printf("Error creating X509Names\n"); crtn = CSSMERR_CSSM_MEMORY_ERROR; break; } /* * not before/after in Y2k-compliant generalized time format. * These come preformatted from our caller. */ notBefore = CB_BuildX509Time(0, notBeforeStr); notAfter = CB_BuildX509Time(0, notAfterStr); /* * Cook up cert template * Note serial number would be app-specified in real world */ rawCert = CB_MakeCertTemplate(clHand, 0x12345 + dex, // serial number issuerName, subjectName, notBefore, notAfter, &pubKeys[dex], sigAlg, NULL, // subj unique ID NULL, // issuer unique ID extensions[dex], // extensions numExtensions[dex]); // numExtensions if(rawCert == NULL) { crtn = CSSM_ERRCODE_INTERNAL_ERROR; break; } /* Free the stuff we allocd to get here */ CB_FreeX509Time(notBefore); CB_FreeX509Time(notAfter); /**** sign the cert ****/ /* 1. get a signing context */ crtn = CSSM_CSP_CreateSignatureContext(cspHand, sigAlg, NULL, // no passphrase for now signerKey, &signContext); if(crtn) { printError("CreateSignatureContext", crtn); break; } /* 2. use CL to sign the cert */ signedCert.Data = NULL; signedCert.Length = 0; crtn = CSSM_CL_CertSign(clHand, signContext, rawCert, // CertToBeSigned NULL, // SignScope per spec 0, // ScopeSize per spec &signedCert); if(crtn) { printError("CSSM_CL_CertSign", crtn); break; } /* 3. delete signing context */ crtn = CSSM_DeleteContext(signContext); if(crtn) { printError("CSSM_DeleteContext", crtn); break; } /* * CSSM_CL_CertSign() returned us a mallocd CSSM_DATA. Copy * its fields to caller's cert. */ certs[dex] = signedCert; /* and the raw unsigned cert as well */ appFreeCssmData(rawCert, CSSM_TRUE); rtn = 0; } /* free resources */ if(issuerName != NULL) { CB_FreeX509Name(issuerName); } if(subjectName != NULL) { CB_FreeX509Name(subjectName); } return crtn; } static int doTest( CSSM_CSP_HANDLE cspHand, // CSP handle CSSM_CL_HANDLE clHand, // CL handle CSSM_TP_HANDLE tpHand, // TP handle unsigned numCerts, // >= 2 CSSM_KEY_PTR pubKeys, CSSM_KEY_PTR privKeys, CSSM_ALGORITHMS sigAlg, CSSM_BOOL expectFail, CSSM_BOOL dumpCerts, CSSM_BOOL quiet) { CSSM_DATA_PTR certs; CSSM_X509_EXTENSION **extens; unsigned *numExtens; char *notBeforeStr = genTimeAtNowPlus(0); char *notAfterStr = genTimeAtNowPlus(10000); unsigned certDex; CE_BasicConstraints *bc; CSSM_X509_EXTENSION *thisExten; CE_BasicConstraints *thisBc; const char *failMode = "not set - internal error"; CSSM_RETURN crtn; unsigned badCertDex = 0; certs = (CSSM_DATA_PTR)malloc(sizeof(CSSM_DATA) * numCerts); memset(certs, 0, sizeof(CSSM_DATA) * numCerts); /* * For now just zero or one extension per cert - basicConstraints. * Eventually we'll want to test keyUsage as well. */ extens = (CSSM_X509_EXTENSION **)malloc(sizeof(CSSM_X509_EXTENSION *) * numCerts); memset(extens, 0, sizeof(CSSM_X509_EXTENSION *) * numCerts); numExtens = (unsigned *)malloc(sizeof(unsigned) * numCerts); bc = (CE_BasicConstraints *)malloc(sizeof(CE_BasicConstraints) * numCerts); /* * Set up all extensions for success */ for(certDex=0; certDexextnId = CSSMOID_BasicConstraints; thisExten->critical = CSSM_TRUE; thisExten->format = CSSM_X509_DATAFORMAT_PARSED; thisExten->value.parsedValue = thisBc; thisExten->BERvalue.Data = NULL; thisExten->BERvalue.Length = 0; if(certDex == 0) { /* leaf - flip coin to determine presence of basicConstraints */ int coin = genRand(1,2); if(coin == 1) { /* basicConstraints, !cA */ thisBc->cA = CSSM_FALSE; thisBc->pathLenConstraintPresent = CSSM_FALSE; thisBc->pathLenConstraint = 0; } else { /* !basicConstraints, !cA by default */ numExtens[certDex] = 0; } } else if(certDex == (numCerts-1)) { /* root - flip coin to determine presence of basicConstraints */ int coin = genRand(1,2); if(coin == 1) { /* basicConstraints, cA */ thisBc->cA = CSSM_TRUE; /* flip coin to determine present of pathLenConstraint */ coin = genRand(1,2); if(coin == 1) { thisBc->pathLenConstraintPresent = CSSM_FALSE; thisBc->pathLenConstraint = 0; } else { thisBc->pathLenConstraintPresent = CSSM_TRUE; thisBc->pathLenConstraint = genRand(certDex-1, numCerts+1); } } else { /* !basicConstraints, cA by default */ numExtens[certDex] = 0; } } else { /* intermediate = cA required */ thisBc->cA = CSSM_TRUE; /* flip coin to determine presence of pathLenConstraint */ int coin = genRand(1,2); if(coin == 1) { thisBc->pathLenConstraintPresent = CSSM_FALSE; thisBc->pathLenConstraint = 0; } else { thisBc->pathLenConstraintPresent = CSSM_TRUE; thisBc->pathLenConstraint = genRand(certDex-1, numCerts+1); } } } if(expectFail) { /* introduce a failure */ if(numCerts == 2) { /* only possible failure is explicit !cA in root */ /* don't assume presence of BC exten */ badCertDex = 1; thisExten = extens[badCertDex]; thisBc = &bc[badCertDex]; thisBc->cA = CSSM_FALSE; thisBc->pathLenConstraintPresent = CSSM_FALSE; bc->pathLenConstraint = 0; numExtens[badCertDex] = 1; failMode = "Explicit !cA in root"; } else { /* roll the dice to select an intermediate cert */ badCertDex = genRand(1, numCerts-2); thisExten = extens[badCertDex]; if((thisExten == NULL) || (numExtens[badCertDex] == 0)) { printf("***INTERNAL SCREWUP\n"); exit(1); } thisBc = &bc[badCertDex]; /* * roll die: fail by * -- no BasicConstraints * -- !cA * -- bad pathLenConstraint */ int die = genRand(1,3); if((die == 1) && (badCertDex != 1)) { // last cA doesn't need pathLenConstraint thisBc->pathLenConstraintPresent = CSSM_TRUE; thisBc->pathLenConstraint = badCertDex - 2; // one short failMode = "Short pathLenConstraint"; } else if(die == 2) { thisBc->cA = CSSM_FALSE; failMode = "Explicit !cA in intermediate"; } else { /* no extension */ numExtens[badCertDex] = 0; failMode = "No BasicConstraints in intermediate"; } } } if(!quiet && expectFail) { printf(" ...bad cert at index %d: %s\n", badCertDex, failMode); } /* here we go - create cert chain */ crtn = tpGenCertsExten(cspHand, clHand, sigAlg, numCerts, "caVerify", // nameBase pubKeys, privKeys, extens, numExtens, certs, notBeforeStr, notAfterStr); if(crtn) { printError("tpGenCertsExten", crtn); return crtn; // and leak like crazy } CSSM_CERTGROUP cgrp; memset(&cgrp, 0, sizeof(CSSM_CERTGROUP)); cgrp.NumCerts = numCerts; #if PUMA_BUILD cgrp.CertGroupType = CSSM_CERTGROUP_ENCODED_CERT; #else /* Jaguar */ cgrp.CertGroupType = CSSM_CERTGROUP_DATA; #endif /* PUMA_BUILD */ cgrp.CertType = CSSM_CERT_X_509v3; cgrp.CertEncoding = CSSM_CERT_ENCODING_DER; cgrp.GroupList.CertList = certs; #if PUMA_BUILD crtn = tpCertGroupVerify(tpHand, clHand, cspHand, NULL, // DlDbList &CSSMOID_APPLE_X509_BASIC, // SSL requires built-in root match &cgrp, /* pass in OUR ROOT as anchors */ (CSSM_DATA_PTR)&certs[numCerts-1], // anchorCerts 1, CSSM_TP_STOP_ON_POLICY, CSSM_FALSE, // allowExpired NULL); // vfyResult #else /* Jaguar */ crtn = tpCertGroupVerify(tpHand, clHand, cspHand, NULL, // DlDbList &CSSMOID_APPLE_TP_SSL, // may want to parameterize this NULL, // fieldOpts for server name NULL, // actionDataPtr for allow expired NULL, // policyOpts &cgrp, /* pass in OUR ROOT as anchors */ (CSSM_DATA_PTR)&certs[numCerts-1], // anchorCerts 1, CSSM_TP_STOP_ON_POLICY, NULL, // cssmTimeStr NULL); // vfyResult #endif /* PUMA_BUILD */ if(expectFail) { if(crtn != CSSMERR_TP_VERIFY_ACTION_FAILED) { cssmPerror("***Expected error TP_VERIFY_ACTION_FAILED; got ", crtn); printf(" Expected failure due to %s\n", failMode); if(dumpCerts) { showCerts(certs, numCerts); writeCerts(certs, numCerts); } return testError(quiet); } } else if(crtn) { cssmPerror("Unexpected failure on tpCertGroupVerify", crtn); if(dumpCerts) { showCerts(certs, numCerts); } return testError(quiet); } /* clean up */ return 0; } int main(int argc, char **argv) { CSSM_CL_HANDLE clHand; // CL handle CSSM_CSP_HANDLE cspHand; // CSP handle CSSM_TP_HANDLE tpHand; CSSM_KEY_PTR pubKeys; CSSM_KEY_PTR privKeys; CSSM_RETURN crtn; int arg; unsigned certDex; unsigned loopNum; unsigned maxCerts; /* user-spec'd variables */ CSSM_ALGORITHMS keyAlg = KEY_ALG_DEFAULT; CSSM_ALGORITHMS sigAlg = SIG_ALG_DEFAULT; uint32 keySizeInBits = CSP_KEY_SIZE_DEFAULT; unsigned numCerts = 0; // means random per loop unsigned numLoops = NUM_LOOPS_DEF; CSSM_BOOL quiet = CSSM_FALSE; CSSM_BOOL dumpCerts = CSSM_FALSE; for(arg=1; arg