-/* Copyright (c) 1998,2003-2006,2008 Apple Inc.
- *
- * caVerify.cpp
- *
- * Verify proper detection of basicConstraints.cA
- */
-
-#include <utilLib/common.h>
-#include <utilLib/cspwrap.h>
-#include <security_cdsa_utils/cuFileIo.h>
-#include <clAppUtils/CertBuilderApp.h>
-#include <clAppUtils/clutils.h>
-#include <clAppUtils/timeStr.h>
-#include <clAppUtils/tpUtils.h>
-#include <security_cdsa_utils/cuPrintCert.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <Security/cssm.h>
-#include <Security/x509defs.h>
-#include <Security/oidsattr.h>
-#include <Security/oidscert.h>
-#include <Security/oidsalg.h>
-#include <Security/certextensions.h>
-#include <Security/cssmapple.h>
-#include <string.h>
-
-/* 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<numCerts; i++) {
- printf("======== cert %d ========\n", i);
- printCert(certs[i].Data, certs[i].Length, CSSM_FALSE);
- printf("\n\n");
- }
-}
-
-void writeCerts(
- const CSSM_DATA *certs,
- unsigned numCerts)
-{
- unsigned i;
- char fileName[80];
-
- for(i=0; i<numCerts; i++) {
- sprintf(fileName, "caVerifyCert%u.cer", i);
- writeFile(fileName, certs[i].Data, certs[i].Length);
- printf("....wrote %lu bytes to %s\n", certs[i].Length, fileName);
- }
-}
-
-/*
- * Generate a cert chain using specified key pairs and extensions.
- * The last cert in the chain (certs[numCerts-1]) is a root cert,
- * self-signed.
- */
-static CSSM_RETURN tpGenCertsExten(
- CSSM_CSP_HANDLE cspHand,
- CSSM_CL_HANDLE clHand,
- CSSM_ALGORITHMS sigAlg, /* CSSM_ALGID_SHA1WithRSA, etc. */
- unsigned numCerts,
- /* per-cert arrays */
- const char *nameBase, /* C string */
- CSSM_KEY_PTR pubKeys, /* array of public keys */
- CSSM_KEY_PTR privKeys, /* array of private keys */
- CSSM_X509_EXTENSION **extensions,
- unsigned *numExtensions,
- CSSM_DATA_PTR certs, /* array of certs RETURNED here */
- const char *notBeforeStr, /* from genTimeAtNowPlus() */
- const char *notAfterStr) /* from genTimeAtNowPlus() */
-
-{
- int dex;
- CSSM_RETURN crtn;
- CSSM_X509_NAME *issuerName = NULL;
- CSSM_X509_NAME *subjectName = NULL;
- CSSM_X509_TIME *notBefore; // UTC-style "not before" time
- CSSM_X509_TIME *notAfter; // UTC-style "not after" time
- CSSM_DATA_PTR rawCert = NULL; // from CSSM_CL_CertCreateTemplate
- CSSM_DATA signedCert; // from CSSM_CL_CertSign
- uint32 rtn;
- CSSM_KEY_PTR signerKey; // signs the cert
- CSSM_CC_HANDLE signContext;
- char nameStr[100];
- CSSM_DATA_PTR thisCert; // ptr into certs[]
- CB_NameOid nameOid;
-
- nameOid.oid = &CSSMOID_OrganizationName; // const
- nameOid.string = nameStr;
-
- /* main loop - once per keypair/cert - starting at end/root */
- for(dex=numCerts-1; dex>=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; certDex<numCerts; certDex++) {
- extens[certDex] = (CSSM_X509_EXTENSION *)malloc(sizeof(CSSM_X509_EXTENSION));
- numExtens[certDex] = 1;
- thisExten = extens[certDex];
- thisBc = &bc[certDex];
-
- thisExten->extnId = 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<argc; arg++) {
- switch(argv[arg][0]) {
- case 'a':
- if((argv[arg][1] == '\0') || (argv[arg][2] == '\0')) {
- usage(argv);
- }
- switch(argv[arg][2]) {
- case 's':
- keyAlg = CSSM_ALGID_RSA;
- sigAlg = CSSM_ALGID_SHA1WithRSA;
- break;
- case '5':
- keyAlg = CSSM_ALGID_RSA;
- sigAlg = CSSM_ALGID_MD5WithRSA;
- break;
- case 'f':
- keyAlg = CSSM_ALGID_FEE;
- sigAlg = CSSM_ALGID_FEE_MD5;
- break;
- case 'F':
- keyAlg = CSSM_ALGID_FEE;
- sigAlg = CSSM_ALGID_FEE_SHA1;
- break;
- case 'e':
- keyAlg = CSSM_ALGID_FEE;
- sigAlg = CSSM_ALGID_SHA1WithECDSA;
- break;
- case 'E':
- keyAlg = CSSM_ALGID_ECDSA;
- sigAlg = CSSM_ALGID_SHA1WithECDSA;
- break;
- case '6':
- keyAlg = CSSM_ALGID_ECDSA;
- sigAlg = CSSM_ALGID_SHA256WithECDSA;
- break;
- default:
- usage(argv);
- }
- break;
- case 'k':
- keySizeInBits = atoi(&argv[arg][2]);
- break;
- case 'l':
- numLoops = atoi(&argv[arg][2]);
- break;
- case 'n':
- numCerts = atoi(&argv[arg][2]);
- break;
- case 'c':
- dumpCerts = CSSM_TRUE;
- break;
- case 'q':
- quiet = CSSM_TRUE;
- break;
- default:
- usage(argv);
- }
- }
-
- /* connect to CL, TP, and CSP */
- clHand = clStartup();
- if(clHand == 0) {
- return 0;
- }
- tpHand = tpStartup();
- if(tpHand == 0) {
- return 0;
- }
- cspHand = cspStartup();
- if(cspHand == 0) {
- return 0;
- }
-
- if(numCerts == 0) {
- maxCerts = NUM_CERTS_DEF; // random, this is the max $
- }
- else {
- maxCerts = numCerts; // user-specd
- }
-
- printf("Starting caVerify; args: ");
- for(unsigned i=1; i<(unsigned)argc; i++) {
- printf("%s ", argv[i]);
- }
- printf("\n");
-
- /* cook up maxCerts key pairs */
- if(!quiet) {
- printf("generating key pairs...\n");
- }
- pubKeys = (CSSM_KEY_PTR)malloc(sizeof(CSSM_KEY) * maxCerts);
- privKeys = (CSSM_KEY_PTR)malloc(sizeof(CSSM_KEY) * maxCerts);
- for(certDex=0; certDex<maxCerts; certDex++) {
- crtn = cspGenKeyPair(cspHand,
- keyAlg,
- "a key pair",
- 9,
- keySizeInBits,
- &pubKeys[certDex],
- CSSM_FALSE, // pubIsRef - should work both ways, but not yet
- CSSM_KEYUSE_VERIFY,
- CSSM_KEYBLOB_RAW_FORMAT_NONE,
- &privKeys[certDex],
- CSSM_TRUE, // privIsRef - doesn't matter
- CSSM_KEYUSE_SIGN,
- CSSM_KEYBLOB_RAW_FORMAT_NONE,
- CSSM_FALSE);
- if(crtn) {
- printError("cspGenKeyPair", crtn);
- printf("***error generating key pair. Aborting.\n");
- exit(1);
- }
- }
-
- for(loopNum=0; loopNum<numLoops; loopNum++) {
- unsigned thisNumCerts;
-
- /* random: num certs and whether this loop is to test a failure */
- if(numCerts) {
- thisNumCerts = numCerts;
- }
- else {
- thisNumCerts = genRand(2, NUM_CERTS_DEF);
- }
- int coin = genRand(1,2);
- CSSM_BOOL expectFail = (coin == 1) ? CSSM_TRUE : CSSM_FALSE;
- if(!quiet) {
- printf("...loop %d numCerts %u expectFail %s\n", loopNum,
- thisNumCerts, expectFail ? "TRUE" : "FALSE");
- }
- if(doTest(cspHand,
- clHand,
- tpHand,
- thisNumCerts,
- pubKeys,
- privKeys,
- sigAlg,
- expectFail,
- dumpCerts,
- quiet)) {
- break;
- }
- }
- /* clean up */
- return 0;
-}
-
-