--- /dev/null
+/* Copyright (c) 2004-2005 Apple Computer, Inc.
+ *
+ * keyStoreLeak.c - simple generate/store key, lookup, delete loop; for malloc
+ * debug testing
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include <unistd.h>
+#include <Security/cssm.h>
+#include "cspwrap.h"
+#include "common.h"
+#include "cspdlTesting.h"
+
+#define LOOPS_DEF 10
+#define DB_NAME "keyStore.db"
+
+static void usage(char **argv)
+{
+ printf("usage: %s [options]\n", argv[0]);
+ printf(" Options:\n");
+ printf(" l=loops (default=%d; 0=forever)\n", LOOPS_DEF);
+ printf(" r(RSA; default = FEE)\n");
+ printf(" k=keyChainFile\n");
+ printf(" P(ause for MallocDebug)\n");
+ printf(" v(erbose)\n");
+ printf(" q(uiet)\n");
+ printf(" h(elp)\n");
+ exit(1);
+}
+
+#define FEE_PRIV_DATA_SIZE 20
+
+/*
+ * Generate key pair, default size. This is like cspGenKeyPair in cspwrap.c,
+ * tweaked for this test To allow varying permanent attribute.
+ */
+static CSSM_RETURN genKeyPair(CSSM_CSP_HANDLE cspHand,
+ CSSM_DL_HANDLE dlHand,
+ CSSM_DB_HANDLE dbHand,
+ const CSSM_DATA_PTR keyLabel,
+ CSSM_KEY_PTR pubKey, // mallocd by caller
+ uint32 pubKeyUsage, // CSSM_KEYUSE_ENCRYPT, etc.
+ uint32 pubKeyAttr,
+ CSSM_KEY_PTR privKey, // mallocd by caller
+ uint32 privKeyUsage, // CSSM_KEYUSE_DECRYPT, etc.
+ uint32 privKeyAttr,
+ uint32 keyGenAlg)
+{
+ CSSM_RETURN crtn;
+ CSSM_CC_HANDLE ccHand;
+ CSSM_RETURN ocrtn = CSSM_OK;
+ uint32 keySize;
+
+ if(keyGenAlg == CSSM_ALGID_FEE) {
+ keySize = CSP_FEE_KEY_SIZE_DEFAULT;
+ }
+ else {
+ keySize = CSP_RSA_KEY_SIZE_DEFAULT;
+ }
+ memset(pubKey, 0, sizeof(CSSM_KEY));
+ memset(privKey, 0, sizeof(CSSM_KEY));
+
+ crtn = CSSM_CSP_CreateKeyGenContext(cspHand,
+ keyGenAlg,
+ keySize,
+ NULL, // Seed
+ NULL, // Salt
+ NULL, // StartDate
+ NULL, // EndDate
+ NULL, // Params
+ &ccHand);
+ if(crtn) {
+ printError("CSSM_CSP_CreateKeyGenContext", crtn);
+ ocrtn = crtn;
+ goto abort;
+ }
+
+ /* add in DL/DB to context */
+ crtn = cspAddDlDbToContext(ccHand, dlHand, dbHand);
+ if(crtn) {
+ ocrtn = crtn;
+ goto abort;
+ }
+ crtn = CSSM_GenerateKeyPair(ccHand,
+ pubKeyUsage,
+ pubKeyAttr,
+ keyLabel,
+ pubKey,
+ privKeyUsage,
+ privKeyAttr,
+ keyLabel, // same labels
+ NULL, // cred/acl
+ privKey);
+ if(crtn) {
+ printError("CSSM_GenerateKeyPair", crtn);
+ ocrtn = crtn;
+ goto abort;
+ }
+
+ /* basic checks...*/
+ if(privKey->KeyHeader.BlobType != CSSM_KEYBLOB_REFERENCE) {
+ printf("privKey blob type: exp %u got %u\n",
+ CSSM_KEYBLOB_REFERENCE, (unsigned)privKey->KeyHeader.BlobType);
+ }
+ if(pubKeyAttr & CSSM_KEYATTR_RETURN_REF) {
+ if(pubKey->KeyHeader.BlobType != CSSM_KEYBLOB_REFERENCE) {
+ printf("pubKey blob type: exp %u got %u\n",
+ CSSM_KEYBLOB_REFERENCE, (unsigned)privKey->KeyHeader.BlobType);
+ ocrtn = -1;
+ goto abort;
+ }
+ }
+ else {
+ if(pubKey->KeyHeader.BlobType != CSSM_KEYBLOB_RAW) {
+ printf("pubKey blob type: exp %u got %u\n",
+ CSSM_KEYBLOB_RAW, (unsigned)privKey->KeyHeader.BlobType);
+ ocrtn = -1;
+ goto abort;
+ }
+ }
+
+abort:
+ if(ccHand != 0) {
+ crtn = CSSM_DeleteContext(ccHand);
+ if(crtn) {
+ printError("CSSM_DeleteContext", crtn);
+ ocrtn = crtn;
+ }
+ }
+ return ocrtn;
+}
+
+#define KEY_LABEL "testKey"
+
+static int doTest(CSSM_CSP_HANDLE cspHand,
+ CSSM_DL_HANDLE dlHand,
+ CSSM_DB_HANDLE dbHand,
+ CSSM_BOOL pubIsPerm, // pub is permanent
+ CSSM_BOOL privIsPerm, // priv is permanent
+ CSSM_BOOL privIsExtractable,
+ CSSM_BOOL permKeys, // leave them in the KC
+ CSSM_BOOL verbose,
+ CSSM_BOOL quiet,
+ uint32 keyGenAlg)
+{
+ CSSM_KEY pubKey; // from GenerateKeyPair
+ CSSM_KEY privKey;
+ CSSM_KEY_PTR lookUpPub; // from cspLookUpKeyByLabel, etc.
+ CSSM_KEY_PTR lookUpPriv;
+ CSSM_RETURN crtn;
+ CSSM_DATA labelData;
+ uint32 pubAttr;
+ uint32 privAttr;
+ labelData.Data = (uint8 *)KEY_LABEL;
+ labelData.Length = strlen(KEY_LABEL);
+
+ /* create key pair */
+ if(verbose) {
+ printf(" ...generating key pair (pubIsPerm %d privIsPerm %d privIsExtract"
+ " %d)\n", (int)pubIsPerm, (int)privIsPerm, (int)privIsExtractable);
+ }
+ pubAttr = CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_RETURN_REF;
+ if(pubIsPerm) {
+ pubAttr |= CSSM_KEYATTR_PERMANENT;
+ }
+
+ /*
+ * To use a NULL wrap to test detection of !EXTRACTABLE, we're relying on
+ * being able to create !SENSITIVE private keys. We'll make 'em sensitive
+ * if we're not trying to null wrap them.
+ */
+ privAttr = CSSM_KEYATTR_RETURN_REF;
+ if(privIsPerm) {
+ privAttr |= CSSM_KEYATTR_PERMANENT;
+ }
+ if(privIsExtractable) {
+ privAttr |= CSSM_KEYATTR_EXTRACTABLE;
+ }
+ else {
+ privAttr |= CSSM_KEYATTR_SENSITIVE;
+ }
+ #if CSPDL_KEYATTR_PRIVATE
+ privAttr |= CSSM_KEYATTR_PRIVATE;
+ #endif
+ crtn = genKeyPair(cspHand,
+ dlHand,
+ dbHand,
+ &labelData,
+ &pubKey,
+ CSSM_KEYUSE_VERIFY, // pubKeyUsage
+ pubAttr,
+ &privKey,
+ CSSM_KEYUSE_SIGN,
+ privAttr,
+ keyGenAlg);
+ if(crtn) {
+ return testError(quiet);
+ }
+
+ /* lookUpPub = lookup pub by label; */
+ if(verbose) {
+ if(pubIsPerm) {
+ printf(" ...lookup pub by label\n");
+ }
+ else {
+ printf(" ...lookup (nonexistent) pub by label\n");
+ }
+ }
+ lookUpPub = cspLookUpKeyByLabel(dlHand, dbHand, &labelData, CKT_Public);
+ if(pubIsPerm) {
+ if(lookUpPub == NULL) {
+ printf("lookup pub by label failed\n");
+ return testError(quiet);
+ }
+ }
+ else {
+ if(lookUpPub != NULL) {
+ printf("***Unexpected success on cspLookUpKeyByLabel(pub, not perm)\n");
+ return testError(quiet);
+ }
+ }
+
+ /* lookUpPriv = lookup priv by label; ensure == privKey; */
+ if(verbose) {
+ if(privIsPerm) {
+ printf(" ...lookup priv by label\n");
+ }
+ else {
+ printf(" ...lookup (nonexistent) priv by label\n");
+ }
+ }
+ lookUpPriv = cspLookUpKeyByLabel(dlHand, dbHand, &labelData, CKT_Private);
+ if(privIsPerm) {
+ if(lookUpPriv == NULL) {
+ printf("lookup priv by label failed\n");
+ return testError(quiet);
+ }
+
+ /* free lookUpPriv from cache, but it's permanent */
+ if(verbose) {
+ printf(" ...free lookupPriv\n");
+ }
+ if(cspFreeKey(cspHand, lookUpPriv)) {
+ printf("Error on cspFreeKey(lookUpPriv)\n");
+ return testError(quiet);
+ }
+ CSSM_FREE(lookUpPriv); // mallocd during lookup
+ lookUpPriv = NULL;
+
+ if(!permKeys) {
+ /* delete priv - implies freeKey as well */
+ if(verbose) {
+ printf(" ...delete privKey\n");
+ }
+ crtn = cspDeleteKey(cspHand, dlHand, dbHand, &labelData, &privKey);
+ if(crtn) {
+ printf("Error deleting priv\n");
+ return testError(quiet);
+ }
+ }
+ }
+ else {
+ /* !privIsPerm - just free it and it's all gone */
+ if(lookUpPriv != NULL) {
+ printf("***Unexpected success on cspLookUpKeyByLabel(priv, not perm)\n");
+ return testError(quiet);
+ }
+ if(cspFreeKey(cspHand, &privKey)) {
+ printf("Error on cspFreeKey(privKey)\n");
+ return testError(quiet);
+ }
+ }
+ /* CSP, DL have no knowledge of privKey or its variations */
+
+ /* free one or both copies of pub as appropriate */
+ if(verbose) {
+ printf(" ...free pubKey\n");
+ }
+ crtn = cspFreeKey(cspHand, &pubKey);
+ if(crtn) {
+ printf("Error freeing pubKey\n");
+ return testError(quiet);
+ }
+ if(pubIsPerm) {
+ if(verbose) {
+ printf(" ...free lookUpPub\n");
+ }
+ crtn = cspFreeKey(cspHand, lookUpPub);
+ if(crtn) {
+ printf("Error freeing lookUpPub\n");
+ return testError(quiet);
+ }
+ }
+ CSSM_FREE(lookUpPub); // mallocd by lookup in any case
+
+ if(pubIsPerm) {
+ /* pub should still be there in DL */
+ /* lookUpPub = lookup pub by label; verify OK; */
+ if(verbose) {
+ printf(" ...lookup pub by label (2)\n");
+ }
+ lookUpPub = cspLookUpKeyByLabel(dlHand, dbHand, &labelData, CKT_Public);
+ if(lookUpPub == NULL) {
+ printf("lookup pub by label (2) failed\n");
+ return testError(quiet);
+ }
+
+ if(!permKeys) {
+ /* now really delete it */
+ if(verbose) {
+ printf(" ...delete lookUpPub\n");
+ }
+ crtn = cspDeleteKey(cspHand, dlHand, dbHand, &labelData, lookUpPub);
+ if(crtn) {
+ printf("Error deleting lookUpPub\n");
+ return testError(quiet);
+ }
+ CSSM_FREE(lookUpPub); // mallocd by lookup
+ }
+ }
+ /* else freeKey should have removed all trace */
+
+ if(!permKeys) {
+ /* lookUpPub = lookup pub by label; verify fail; */
+ if(verbose) {
+ printf(" ...lookup (nonexistent) pub by label\n");
+ }
+ lookUpPub = cspLookUpKeyByLabel(dlHand, dbHand, &labelData, CKT_Public);
+ if(lookUpPub != NULL) {
+ printf("Unexpected success on cspLookUpKeyByLabel(pub) (2)\n");
+ return testError(quiet);
+ }
+ }
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int arg;
+ char *argp;
+ unsigned loop;
+ CSSM_CSP_HANDLE cspHand;
+ CSSM_DB_HANDLE dbHand;
+ CSSM_DL_HANDLE dlHand;
+ CSSM_BOOL pubIsPerm;
+ CSSM_BOOL privIsPerm;
+ CSSM_BOOL privIsExtractable;
+ uint32 keyGenAlg = CSSM_ALGID_FEE;
+ int rtn = 0;
+ CSSM_RETURN crtn;
+
+ /*
+ * User-spec'd params
+ */
+ unsigned loops = LOOPS_DEF;
+ CSSM_BOOL verbose = CSSM_FALSE;
+ CSSM_BOOL quiet = CSSM_FALSE;
+ CSSM_BOOL permKeys = CSSM_FALSE;
+ char dbName[100]; /* DB_NAME_pid */
+ CSSM_BOOL useExistDb = CSSM_FALSE;
+ CSSM_BOOL doPause = CSSM_FALSE;
+
+ dbName[0] = '\0';
+
+ for(arg=1; arg<argc; arg++) {
+ argp = argv[arg];
+ switch(argp[0]) {
+ case 'l':
+ loops = atoi(&argp[2]);
+ break;
+ case 'v':
+ verbose = CSSM_TRUE;
+ break;
+ case 'q':
+ quiet = CSSM_TRUE;
+ break;
+ case 'p':
+ permKeys = CSSM_TRUE;
+ loops = 1;
+ break;
+ case 'r':
+ keyGenAlg = CSSM_ALGID_RSA;
+ break;
+ case 'k':
+ memmove(dbName, &argp[2], strlen(&argp[2]) + 1);
+ useExistDb = CSSM_TRUE;
+ break;
+ case 'P':
+ doPause = CSSM_TRUE;
+ break;
+ case 'h':
+ default:
+ usage(argv);
+ }
+ }
+
+ if(dbName[0] == '\0') {
+ sprintf(dbName, "%s_%d", DB_NAME, (int)getpid());
+ }
+
+ testStartBanner("keyStore", argc, argv);
+
+ /* attach to CSP/DL */
+ cspHand = cspDlDbStartup(CSSM_FALSE, NULL);
+ if(cspHand == 0) {
+ exit(1);
+ }
+ dlHand = dlStartup();
+ if(dlHand == 0) {
+ exit(1);
+ }
+ if(useExistDb) {
+ /* this one may well require SecurityAgent UI */
+ crtn = dbCreateOpen(dlHand, dbName, CSSM_FALSE, CSSM_FALSE, NULL,
+ &dbHand);
+ }
+ else {
+ /* hands-free test */
+ crtn = dbCreateOpen(dlHand, dbName, CSSM_TRUE, CSSM_TRUE, dbName,
+ &dbHand);
+ }
+ if(crtn) {
+ exit(1);
+ }
+ for(loop=1; ; loop++) {
+
+ if(!quiet) {
+ printf("...loop %d\n", loop);
+ }
+
+ if(permKeys) {
+ pubIsPerm = privIsPerm = CSSM_TRUE;
+ }
+ else {
+ /* mix up pubIsPerm, privIsPerm */
+ pubIsPerm = (loop & 1) ? CSSM_TRUE : CSSM_FALSE;
+ privIsPerm = (loop & 2) ? CSSM_TRUE : CSSM_FALSE;
+ }
+ privIsExtractable = (loop & 4) ? CSSM_TRUE : CSSM_FALSE;
+ if(doTest(cspHand,
+ dlHand,
+ dbHand,
+ pubIsPerm,
+ privIsPerm,
+ privIsExtractable,
+ permKeys,
+ verbose,
+ quiet,
+ keyGenAlg)) {
+ rtn = 1;
+ break;
+ }
+ if(loops && (loop == loops)) {
+ break;
+ }
+ if(doPause) {
+ fpurge(stdin);
+ printf("CR to continue: ");
+ getchar();
+ }
+ }
+
+ cspShutdown(cspHand, CSSM_FALSE);
+ /* FIXME - DB close? DL shutdown? */
+ if((rtn == 0) && !quiet) {
+ printf("%s test complete\n", argv[0]);
+ }
+ if(rtn == 0) {
+ /* be nice: if we ran OK delete the cruft DB we created */
+ unlink(dbName);
+ }
+ return rtn;
+}