--- /dev/null
+/*
+ * secTime.cpp - measure performance of Sec* ops
+ */
+
+#include <Security/Security.h>
+#include <security_cdsa_utils/cuFileIo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <security_utilities/devrandom.h>
+#include <clAppUtils/certVerify.h>
+#include <clAppUtils/clutils.h>
+#include <utilLib/common.h>
+
+/*
+ * Hard coded test params
+ */
+
+/*
+ * The keychain we open and search for certs.
+ * MUST exist in ~/Library/Keychains.
+ * YOU can create it with
+ * % certtool c k=secTimeKc c p=secTimeKc Z
+ */
+#define ST_KC_NAME "secTimeKc"
+
+/*
+ * Certs to verify with SecTrust. We have a variety because the timing on
+ * this test is highly dependent on the position of the verifying anchor
+ * within the system anchors list. (This does not apply to Trust Settings).
+ */
+typedef struct {
+ const char *certFileName;
+ const char *hostName;
+} CertToVerify;
+
+static const CertToVerify certsToVerify[] =
+{
+ {
+ /* issuer: Secure Server Certification Authority */
+ "amazon_v3.100.cer",
+ "www.amazon.com"
+ },
+ {
+ /* issuer: Equifax Secure Certificate Authority */
+ "firstamlink.cer",
+ "www.firstamlink.com"
+ },
+};
+
+#define NUM_ST_CERTS (sizeof(certsToVerify) / sizeof(certsToVerify[0]))
+
+/* explicit anchor for certsToVerify[0] */
+#define ST_ANCHOR_NAME "SecureServer.509.cer" /* verifies ST_CERT_HOST */
+
+/*
+ * Cert chain for cgv3*()
+ */
+#define THAWTE_LEAF "dmitchThawte.cer"
+#define THAWTE_CA "ThawteCA.cer"
+#define THAWTE_ROOT "ThawteRoot.cer"
+
+static void usage(char **argv)
+{
+ printf("Usage: %s [option ...]\n", argv[0]);
+ printf("Options:\n");
+ printf(" t=testspec; default=all\n");
+ printf(" test specs: o keychainOpen\n");
+ printf(" s keychainSearch\n");
+ printf(" e secTrustEvaluate\n");
+ printf(" k SecKeychainCopySearchList\n");
+ printf(" v TP CertGroupVerify, system anchor\n");
+ printf(" V TP CertGroupVerify, explicit anchor\n");
+ printf(" 3 TP CertGroupVerify, 3 certs w/anchor\n");
+ printf(" l=loops (only valid if testspec is given)\n");
+ exit(1);
+}
+
+static void printSecErr(
+ const char *op,
+ OSStatus ortn)
+{
+ printf("%s returned %ld\n", op, (unsigned long)ortn);
+}
+
+/*
+ * Struct passed around to test-specific functions.
+ */
+typedef struct {
+ const char *testName;
+ void *testPriv;
+} TestParams;
+
+/*
+ * Each subtest has three functions - init, run, cleanup, called out
+ * from main().
+ *
+ * Init's job is one-time setup - open files, setup buffers, etc.
+ * PErsistent state can be saved in TestParams->testPriv.
+ */
+typedef OSStatus (*testInitFcn)(TestParams *testParams);
+
+/*
+ * Run's job is to perform 1 iteration with as
+ * little overhead as possible. Return the time spect actually
+ * doing the deed in *timeSpent.
+ */
+typedef OSStatus (*testRunFcn)(TestParams *testParams,
+ unsigned loopNum,
+ CFAbsoluteTime *timeSpent);
+
+/*
+ * CLeanu up any resources allocd in init.
+ */
+typedef OSStatus (*testCleanupFcn)(TestParams *testParams);
+
+/*
+ * Static declaration of a test
+ */
+typedef struct {
+ const char *testName;
+ unsigned loops;
+ testInitFcn init;
+ testRunFcn run;
+ testCleanupFcn cleanup;
+ char testSpec; // for t=xxx cmd line opt
+} TestDefs;
+
+#pragma mark ---- Individual tests ----
+
+#ifdef use_these_as_a_template
+
+static OSStatus xxxInit(
+ TestParams *testParams)
+{
+ return noErr;
+}
+
+static OSStatus xxRun(
+ TestParams *testParams,
+ unsigned loopNum,
+ CFAbsoluteTime *timeSpent)
+{
+ CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
+ /* do the op here */
+ *timeSpent = CFAbsoluteTimeGetCurrent() - startTime;
+ return noErr;
+}
+
+static OSStatus xxxCleanup(
+ TestParams *testParams)
+{
+ return noErr;
+}
+#endif /* template */
+
+#pragma mark -- keychain open --
+
+static OSStatus kcOpenInit(
+ TestParams *testParams)
+{
+ return noErr;
+}
+
+static OSStatus kcOpenRun(
+ TestParams *testParams,
+ unsigned loopNum,
+ CFAbsoluteTime *timeSpent)
+{
+ SecKeychainRef kcRef;
+ SecKeychainStatus status;
+
+ CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
+ OSStatus ortn = SecKeychainOpen(ST_KC_NAME, &kcRef);
+ if(ortn) {
+ printSecErr("SecKeychainOpen", ortn);
+ return ortn;
+ }
+ ortn = SecKeychainGetStatus(kcRef, &status);
+ if(ortn) {
+ printSecErr("SecKeychainGetStatus", ortn);
+ CFRelease(kcRef);
+ if(errSecNoSuchKeychain == ortn) {
+ printf("The keychain %s does not exist. Please create it"
+ " and populate it like so:\n", ST_KC_NAME);
+ printf(" certtool c k=%s c p=%s Z\n",
+ ST_KC_NAME, ST_KC_NAME);
+ }
+ return ortn;
+ }
+ CFAbsoluteTime endTime = CFAbsoluteTimeGetCurrent();
+ *timeSpent = endTime - startTime;
+ CFRelease(kcRef);
+ return noErr;
+}
+
+static OSStatus kcOpenCleanup(
+ TestParams *testParams)
+{
+ return noErr;
+}
+
+#pragma mark -- keychain lookup --
+
+/*
+ * Private *testPriv is a kcRef.
+ */
+static OSStatus kcSearchInit(
+ TestParams *testParams)
+{
+ SecKeychainRef kcRef;
+ OSStatus ortn = SecKeychainOpen(ST_KC_NAME, &kcRef);
+ if(ortn) {
+ printSecErr("SecKeychainOpen", ortn);
+ return ortn;
+ }
+ testParams->testPriv = (void *)kcRef;
+ return noErr;
+}
+
+static OSStatus kcSearchRun(
+ TestParams *testParams,
+ unsigned loopNum,
+ CFAbsoluteTime *timeSpent)
+{
+ SecKeychainRef kcRef = (SecKeychainRef)testParams->testPriv;
+ SecKeychainSearchRef srchRef = NULL;
+ SecKeychainItemRef certRef = NULL;
+ CFAbsoluteTime endTime;
+ CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
+
+ /* search for any cert */
+ OSStatus ortn = SecKeychainSearchCreateFromAttributes(kcRef,
+ kSecCertificateItemClass,
+ NULL, // no attrs
+ &srchRef);
+ if(ortn) {
+ printSecErr("SecKeychainSearchCreateFromAttributes", ortn);
+ return ortn;
+ }
+ ortn = SecKeychainSearchCopyNext(srchRef, &certRef);
+ if(ortn) {
+ printSecErr("SecKeychainSearchCopyNext", ortn);
+ goto done;
+ }
+ endTime = CFAbsoluteTimeGetCurrent();
+ *timeSpent = endTime - startTime;
+done:
+ if(srchRef) {
+ CFRelease(srchRef);
+ }
+ if(certRef) {
+ CFRelease(certRef);
+ }
+ return ortn;
+}
+
+
+static OSStatus kcSearchCleanup(
+ TestParams *testParams)
+{
+ SecKeychainRef kcRef = (SecKeychainRef)testParams->testPriv;
+ CFRelease(kcRef);
+ return noErr;
+}
+
+#pragma mark -- SecTrustEvaluate --
+
+/*
+ * Priv data is an array of SecCertificateRef containing certs to evaluate.
+ * Each run evaluates one of them.
+ */
+static OSStatus secTrustInit(
+ TestParams *testParams)
+{
+ unsigned char *certData;
+ unsigned certLen;
+ CSSM_DATA cdata;
+
+ SecCertificateRef *certRefs;
+
+ certRefs = (SecCertificateRef *)malloc(
+ sizeof(SecCertificateRef) * NUM_ST_CERTS);
+
+ for(unsigned dex=0; dex<NUM_ST_CERTS; dex++) {
+ if(readFile(certsToVerify[dex].certFileName, &certData, &certLen)) {
+ printf("***Can not find cert file %s. Aborting.\n",
+ certsToVerify[dex].certFileName);
+ return -1;
+ }
+ cdata.Data = certData;
+ cdata.Length = certLen;
+ SecCertificateRef certRef;
+ OSStatus ortn = SecCertificateCreateFromData(&cdata,
+ CSSM_CERT_X_509v3,
+ CSSM_CERT_ENCODING_DER,
+ &certRef);
+ if(ortn) {
+ printSecErr("SecCertificateCreateFromData", ortn);
+ return ortn;
+ }
+ free(certData); // mallocd by readFile()
+ certRefs[dex] = certRef;
+ }
+ testParams->testPriv = certRefs;
+ return noErr;
+}
+
+static OSStatus secTrustRun(
+ TestParams *testParams,
+ unsigned loopNum,
+ CFAbsoluteTime *timeSpent)
+{
+ unsigned whichDex = loopNum % NUM_ST_CERTS;
+ SecCertificateRef *certRefs = (SecCertificateRef *)testParams->testPriv;
+ SecCertificateRef certRef = certRefs[whichDex];
+
+ /*
+ * We measure the whole enchilada, that's what SecureTransport
+ * has to do
+ */
+ CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
+
+ CFMutableArrayRef certs;
+ certs = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks);
+ CFArrayInsertValueAtIndex(certs, 0, certRef);
+
+ SecPolicyRef policy = NULL;
+ SecPolicySearchRef policySearch = NULL;
+
+ OSStatus ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3,
+ &CSSMOID_APPLE_TP_SSL,
+ NULL, // policy opts
+ &policySearch);
+ if(ortn) {
+ printSecErr("SecPolicySearchCreate", ortn);
+ return ortn;
+ }
+
+ ortn = SecPolicySearchCopyNext(policySearch, &policy);
+ if(ortn) {
+ printSecErr("SecPolicySearchCopyNext", ortn);
+ return ortn;
+ }
+ CFRelease(policySearch);
+
+ SecTrustRef secTrust;
+ ortn = SecTrustCreateWithCertificates(certs, policy, &secTrust);
+ if(ortn) {
+ printSecErr("SecTrustCreateWithCertificates", ortn);
+ return ortn;
+ }
+ /* no action data for now */
+
+ SecTrustResultType secTrustResult;
+ ortn = SecTrustEvaluate(secTrust, &secTrustResult);
+ if(ortn) {
+ printSecErr("SecTrustEvaluate", ortn);
+ return ortn;
+ }
+
+ CFRelease(certs);
+ CFRelease(secTrust);
+ CFRelease(policy);
+
+ CFAbsoluteTime endTime = CFAbsoluteTimeGetCurrent();
+ *timeSpent = endTime - startTime;
+
+ return noErr;
+}
+
+static OSStatus secTrustCleanup(
+ TestParams *testParams)
+{
+ SecCertificateRef *certRefs = (SecCertificateRef*)testParams->testPriv;
+ for(unsigned dex=0; dex<NUM_ST_CERTS; dex++) {
+ CFRelease(certRefs[dex]);
+ }
+ free(certRefs);
+ return noErr;
+}
+
+#pragma mark -- SecKeychainCopySearchList --
+
+static OSStatus kcCSLInit(
+ TestParams *testParams)
+{
+ return noErr;
+}
+
+static OSStatus kcCSLRun(
+ TestParams *testParams,
+ unsigned loopNum,
+ CFAbsoluteTime *timeSpent)
+{
+ CFArrayRef sl;
+ CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
+ OSStatus ortn = SecKeychainCopySearchList(&sl);
+ if(ortn) {
+ printSecErr("SecKeychainCopySearchList", ortn);
+ return ortn;
+ }
+ CFRelease(sl);
+ *timeSpent = CFAbsoluteTimeGetCurrent() - startTime;
+ return noErr;
+}
+
+static OSStatus kcCSLCleanup(
+ TestParams *testParams)
+{
+ return noErr;
+}
+
+#pragma mark -- CSSM_TP_CertGroupVerify, system anchors --
+
+/* private data allocated in cgvInit */
+typedef struct {
+ CSSM_TP_HANDLE tpHand;
+ CSSM_CL_HANDLE clHand;
+ CSSM_CSP_HANDLE cspHand;
+ BlobList *certs[NUM_ST_CERTS];
+ BlobList *anchors; /* cgvAnchor* only */
+} CgvParams;
+
+static OSStatus cgvInit(
+ TestParams *testParams)
+{
+ CgvParams *cgvParams = (CgvParams *)malloc(sizeof(CgvParams));
+ memset(cgvParams, 0, sizeof(CgvParams));
+ cgvParams->tpHand = tpStartup();
+ cgvParams->clHand = clStartup();
+ cgvParams->cspHand = cspStartup();
+ for(unsigned dex=0; dex<NUM_ST_CERTS; dex++) {
+ cgvParams->certs[dex] = new BlobList();
+ cgvParams->certs[dex]->addFile(certsToVerify[dex].certFileName);
+ }
+ cgvParams->anchors = NULL;
+ testParams->testPriv = cgvParams;
+ return noErr;
+}
+
+static OSStatus cgvRun(
+ TestParams *testParams,
+ unsigned loopNum,
+ CFAbsoluteTime *timeSpent)
+{
+ CgvParams *cgvParams = (CgvParams *)testParams->testPriv;
+ BlobList nullList;
+ unsigned whichDex = loopNum % NUM_ST_CERTS;
+ BlobList *certBlob = cgvParams->certs[whichDex];
+
+ CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
+ int rtn = certVerifySimple(
+ cgvParams->tpHand,
+ cgvParams->clHand,
+ cgvParams->cspHand,
+ *certBlob, // contains one cert, the subject
+ nullList, // roots
+ CSSM_TRUE, // useSystemAnchors
+ CSSM_FALSE, // leafCertIsCa
+ CSSM_FALSE, // allow expired root
+ CVP_SSL,
+ certsToVerify[whichDex].hostName,
+ CSSM_FALSE, // sslClient
+ NULL, // senderEmail
+ 0, // key use
+ NULL, // expected error str
+ 0, // numCertErrors
+ NULL,
+ 0, // numCertStatus
+ NULL,
+ CSSM_FALSE, // trustSettings
+ CSSM_TRUE, // quiet
+ CSSM_FALSE); // verbose
+ *timeSpent = CFAbsoluteTimeGetCurrent() - startTime;
+ if(rtn) {
+ printf("***certVerify error\n");
+ return (OSStatus)rtn;
+ }
+ return noErr;
+}
+
+static OSStatus cgvCleanup(
+ TestParams *testParams)
+{
+ CgvParams *cgvParams = (CgvParams *)testParams->testPriv;
+ CSSM_ModuleDetach(cgvParams->cspHand);
+ CSSM_ModuleDetach(cgvParams->tpHand);
+ CSSM_ModuleDetach(cgvParams->clHand);
+ for(unsigned dex=0; dex<NUM_ST_CERTS; dex++) {
+ delete cgvParams->certs[dex];
+ }
+ if(cgvParams->anchors) {
+ delete(cgvParams->anchors);
+ }
+ free(cgvParams);
+ return noErr;
+}
+
+#pragma mark -- CSSM_TP_CertGroupVerify, explicit anchors --
+
+static OSStatus cgvAnchorInit(
+ TestParams *testParams)
+{
+ CgvParams *cgvParams = (CgvParams *)malloc(sizeof(CgvParams));
+ memset(cgvParams, 0, sizeof(CgvParams));
+ cgvParams->tpHand = tpStartup();
+ cgvParams->clHand = clStartup();
+ cgvParams->cspHand = cspStartup();
+ cgvParams->certs[0] = new BlobList();
+ cgvParams->certs[0]->addFile(certsToVerify[0].certFileName);
+ cgvParams->anchors = new BlobList();
+ cgvParams->anchors->addFile(ST_ANCHOR_NAME);
+ testParams->testPriv = cgvParams;
+ return noErr;
+}
+
+static OSStatus cgvAnchorRun(
+ TestParams *testParams,
+ unsigned loopNum,
+ CFAbsoluteTime *timeSpent)
+{
+ CgvParams *cgvParams = (CgvParams *)testParams->testPriv;
+ BlobList nullList;
+
+ CertVerifyArgs vfyArgs;
+ memset(&vfyArgs, 0, sizeof(vfyArgs));
+ vfyArgs.version = CERT_VFY_ARGS_VERS;
+
+ vfyArgs.tpHand = cgvParams->tpHand;
+ vfyArgs.clHand = cgvParams->clHand;
+ vfyArgs.cspHand = cgvParams->cspHand;
+ vfyArgs.certs = cgvParams->certs[0];
+ vfyArgs.roots = cgvParams->anchors;
+ vfyArgs.allowUnverified = CSSM_TRUE;
+ vfyArgs.vfyPolicy = CVP_SSL;
+ vfyArgs.revokePolicy = CRP_None;
+ vfyArgs.sslHost = certsToVerify[0].hostName;
+ vfyArgs.revokePolicy = CRP_None;
+ vfyArgs.quiet = CSSM_TRUE;
+
+ CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
+ int rtn = certVerify(&vfyArgs);
+ *timeSpent = CFAbsoluteTimeGetCurrent() - startTime;
+ if(rtn) {
+ printf("***certVerify error\n");
+ return (OSStatus)rtn;
+ }
+ return noErr;
+}
+
+/* cleanup - use cgvCleanup() */
+
+#pragma mark -- CSSM_TP_CertGroupVerify, 3 certs with anchor --
+
+static OSStatus cgv3Init(
+ TestParams *testParams)
+{
+ CgvParams *cgvParams = (CgvParams *)malloc(sizeof(CgvParams));
+ memset(cgvParams, 0, sizeof(CgvParams));
+ cgvParams->tpHand = tpStartup();
+ cgvParams->clHand = clStartup();
+ cgvParams->cspHand = cspStartup();
+ cgvParams->certs[0] = new BlobList();
+ cgvParams->certs[0]->addFile(THAWTE_LEAF);
+ cgvParams->certs[0]->addFile(THAWTE_CA);
+ cgvParams->certs[0]->addFile(THAWTE_ROOT);
+ cgvParams->anchors = new BlobList();
+ cgvParams->anchors->addFile(THAWTE_ROOT);
+ testParams->testPriv = cgvParams;
+ return noErr;
+}
+
+static OSStatus cgv3Run(
+ TestParams *testParams,
+ unsigned loopNum,
+ CFAbsoluteTime *timeSpent)
+{
+ CgvParams *cgvParams = (CgvParams *)testParams->testPriv;
+ BlobList nullList;
+
+ CertVerifyArgs vfyArgs;
+ memset(&vfyArgs, 0, sizeof(vfyArgs));
+ vfyArgs.version = CERT_VFY_ARGS_VERS;
+
+ vfyArgs.tpHand = cgvParams->tpHand;
+ vfyArgs.clHand = cgvParams->clHand;
+ vfyArgs.cspHand = cgvParams->cspHand;
+ vfyArgs.certs = cgvParams->certs[0]; /* that's three certs */
+ vfyArgs.roots = cgvParams->anchors;
+ vfyArgs.allowUnverified = CSSM_TRUE;
+ vfyArgs.vfyPolicy = CVP_Basic;
+ vfyArgs.revokePolicy = CRP_None;
+ vfyArgs.sslHost = certsToVerify[0].hostName;
+ vfyArgs.revokePolicy = CRP_None;
+ vfyArgs.quiet = CSSM_TRUE;
+
+ CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
+ int rtn = certVerify(&vfyArgs);
+ *timeSpent = CFAbsoluteTimeGetCurrent() - startTime;
+ if(rtn) {
+ printf("***certVerify error\n");
+ return (OSStatus)rtn;
+ }
+ return noErr;
+}
+
+static OSStatus cgv3Cleanup(
+ TestParams *testParams)
+{
+ CgvParams *cgvParams = (CgvParams *)testParams->testPriv;
+ CSSM_ModuleDetach(cgvParams->cspHand);
+ CSSM_ModuleDetach(cgvParams->tpHand);
+ CSSM_ModuleDetach(cgvParams->clHand);
+ delete cgvParams->certs[0];
+ delete(cgvParams->anchors);
+ free(cgvParams);
+ return noErr;
+}
+
+#pragma mark ---- Static array of all tests ----
+
+static TestDefs testDefs[] =
+{
+ { "Keychain open",
+ 100,
+ kcOpenInit,
+ kcOpenRun,
+ kcOpenCleanup,
+ 'o',
+ },
+ { "Keychain cert search",
+ 100,
+ kcSearchInit,
+ kcSearchRun,
+ kcSearchCleanup,
+ 's',
+ },
+ { "SecTrustEvaluate",
+ 100,
+ secTrustInit,
+ secTrustRun,
+ secTrustCleanup,
+ 'e',
+ },
+ { "TP CertGroupVerify, system anchors",
+ 100,
+ cgvInit,
+ cgvRun,
+ cgvCleanup,
+ 'v',
+ },
+ { "TP CertGroupVerify, explicit anchor",
+ 100,
+ cgvAnchorInit,
+ cgvAnchorRun,
+ cgvCleanup,
+ 'V',
+ },
+ { "TP CertGroupVerify, 3 certs with anchor",
+ 100,
+ cgv3Init,
+ cgv3Run,
+ cgv3Cleanup,
+ '3',
+ },
+ { "SecKeychainCopySearchList",
+ 100,
+ kcCSLInit,
+ kcCSLRun,
+ kcCSLCleanup,
+ 'k',
+ },
+};
+
+#define NUM_TESTS (sizeof(testDefs) / sizeof(testDefs[0]))
+
+int main(int argc, char **argv)
+{
+ TestParams testParams;
+ TestDefs *testDef;
+ OSStatus ortn;
+ int arg;
+ char *argp;
+ unsigned cmdLoops = 0; // can be specified in cmd line
+ // if not, use TestDefs.loops
+ char testSpec = '\0'; // allows specification of one test
+ // otherwise run all
+
+ for(arg=1; arg<argc; arg++) {
+ argp = argv[arg];
+ switch(argp[0]) {
+ case 't':
+ testSpec = argp[2];
+ break;
+ case 'l':
+ cmdLoops = atoi(&argp[2]);
+ break;
+ default:
+ usage(argv);
+ }
+ }
+
+ for(unsigned testNum=0; testNum<NUM_TESTS; testNum++) {
+ testDef = &testDefs[testNum];
+ unsigned loopCount;
+
+ if(testSpec && (testDef->testSpec != testSpec)) {
+ continue;
+ }
+ printf("%s:\n", testDef->testName);
+ ortn = testDef->init(&testParams);
+ if(ortn) {
+ exit(1);
+ }
+ if(cmdLoops) {
+ /* user specified */
+ loopCount = cmdLoops;
+ }
+ else {
+ /* default */
+ loopCount = testDef->loops;
+ }
+ CFAbsoluteTime totalTime = 0;
+ CFAbsoluteTime thisTime;
+ for(unsigned loop=0; loop<loopCount; loop++) {
+ ortn = testDef->run(&testParams, loop, &thisTime);
+ if(ortn) {
+ exit(1);
+ }
+ totalTime += thisTime;
+ }
+ testDef->cleanup(&testParams);
+ printf(" %3.2f ms per op\n", (totalTime / loopCount) * 1000.0);
+ }
+ return 0;
+}