/* Copyright (c) 2001,2003-2006,2008 Apple Inc. * * keyStore.c - basic key pair store/lookup routines. * * create key pair, varying pub is permanent, priv is permanent; * sign some data with priv; * lookUpPub = lookup pub by label; * vfy with lookUpPub; * lookUpPriv = lookup priv by label; * if(privIsPerm) { * ensure lookUpPriv == priv; * freeKey(lookUpPriv); * obtainedPriv = obtainPubFromPriv(lookUpPub); * ensure obtainedPriv == priv; * freeKey(obtainedPriv); * delete priv; // cspwrap does implicit freeKey here... * } * else { * free priv; * } * lookUpPriv = lookup priv by label; verify fail; * lookUpPriv = obtainPubFromPriv(pub); verify fail; * freeKey(lookUpPub); * if pub is permament { * lookUpPub = lookup pub by label; verify OK; * deleteKey(lookUpPub); * lookUpPub = lookup pub by label; verify fail; * } * lookUpPub = lookup pub by label; verify fail; */ #include #include #include #include #include #include #include "cspwrap.h" #include "common.h" #include "cspdlTesting.h" #define LOOPS_DEF 10 #define MAX_DATA_SIZE 100 #define DB_NAME "keyStore.db" /* can't lookup non-permanent keys! */ #define FORCE_PUB_PERMANENT 0 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(" p(ermanent keys, implies l=1)\n"); printf(" k=keyChainFile\n"); printf(" n(o sign/verify)\n"); printf(" N(o lookup of nonexistent keys)\n"); printf(" x (privKey always extractable)\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 /* * NULL wrap, error tolerant. */ CSSM_RETURN cspNullWrapKey( CSSM_CSP_HANDLE cspHand, const CSSM_KEY *refKey, CSSM_KEY_PTR rawKey) // RETURNED on success, caller must FreeKey { CSSM_CC_HANDLE ccHand; CSSM_RETURN crtn; CSSM_ACCESS_CREDENTIALS creds; CSSM_DATA descData = {0, 0}; memset(rawKey, 0, sizeof(CSSM_KEY)); memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS)); crtn = CSSM_CSP_CreateSymmetricContext(cspHand, CSSM_ALGID_NONE, CSSM_ALGMODE_NONE, &creds, // passPhrase, NULL, // wrappingKey, NULL, // initVector, CSSM_PADDING_NONE, 0, // Params &ccHand); if(crtn) { printError("cspNullWrapKey/CreateContext", crtn); return crtn; } crtn = CSSM_WrapKey(ccHand, &creds, refKey, &descData, // DescriptiveData rawKey); if(CSSM_DeleteContext(ccHand)) { printf("CSSM_DeleteContext failure\n"); } return crtn; } /* * 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" /* * when true, keyref in key obtained from DL differs from * keyref in key from CSP */ #define DL_REF_KEYS_DIFFER 1 /* * ObtainPrivateKeyFromPublicKey doesn't work yet */ #define DO_OBTAIN_FROM_PUB CSPDL_OBTAIN_PRIV_FROM_PUB /* we're assumed to be logged in for access to private objects */ 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 doSignVerify, CSSM_BOOL doFailedLookup, CSSM_DATA_PTR ptext, CSSM_BOOL verbose, CSSM_BOOL quiet, uint32 keyGenAlg, uint32 sigAlg) { CSSM_KEY pubKey; // from GenerateKeyPair CSSM_KEY privKey; CSSM_KEY_PTR lookUpPub; // from cspLookUpKeyByLabel, etc. CSSM_KEY_PTR lookUpPriv; CSSM_RETURN crtn; CSSM_DATA sig; CSSM_DATA labelData; CSSM_KEY obtainedPriv; uint32 pubAttr; uint32 privAttr; CSSM_KEY rawPrivKey; labelData.Data = (uint8 *)KEY_LABEL; labelData.Length = strlen(KEY_LABEL); CSSM_BOOL doLookup; /* 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; */ doLookup = CSSM_TRUE; if(verbose) { if(pubIsPerm) { printf(" ...lookup pub by label\n"); } else { if(doFailedLookup) { printf(" ...lookup (nonexistent) pub by label\n"); } else { doLookup = CSSM_FALSE; lookUpPub = NULL; } } } if(doLookup) { lookUpPub = cspLookUpKeyByLabel(dlHand, dbHand, &labelData, CKT_Public); } if(pubIsPerm) { if(lookUpPub == NULL) { printf("lookup pub by label failed\n"); return testError(quiet); } /* sign some data with priv; */ sig.Data = NULL; sig.Length = 0; if(doSignVerify) { if(cspSign(cspHand, sigAlg, &privKey, ptext, &sig)) { return testError(quiet); } } /* verify header compare */ if(memcmp(&lookUpPub->KeyHeader, &pubKey.KeyHeader, sizeof(CSSM_KEYHEADER))) { printf("**pubKey header miscompare\n"); return testError(quiet); } /* vfy with lookUpPub; */ if(doSignVerify) { if(cspSigVerify(cspHand, sigAlg, lookUpPub, ptext, &sig, CSSM_OK)) { return testError(quiet); } CSSM_FREE(sig.Data); sig.Data = NULL; sig.Data = 0; } } else { if(doLookup && (lookUpPub != NULL)) { printf("***Unexpected success on cspLookUpKeyByLabel(pub, not perm)\n"); return testError(quiet); } } /* * Ensure proper behavior of extractable bit */ if(verbose) { printf(" ...null wrap %s private key\n", privIsExtractable ? "EXTRACTABLE" : "!EXTRACTABLE"); } crtn = cspNullWrapKey(cspHand, &privKey, &rawPrivKey); if(privIsExtractable) { if(crtn) { printError("Null Wrap(extractable private key)", crtn); return testError(quiet); } if(verbose) { printf(" ...free rawPrivKey\n"); } cspFreeKey(cspHand, &rawPrivKey); } else { if(crtn != CSSMERR_CSP_INVALID_KEYATTR_MASK) { printError("Null Wrap of !extractable private key: expected " "INVALID_KEYATTR_MASK, got", crtn); if(crtn == CSSM_OK) { cspFreeKey(cspHand, &rawPrivKey); } return testError(quiet); } } /* lookUpPriv = lookup priv by label; ensure == privKey; */ doLookup = CSSM_TRUE; if(verbose) { if(privIsPerm) { printf(" ...lookup priv by label\n"); } else { if(doFailedLookup) { printf(" ...lookup (nonexistent) priv by label\n"); } else { doLookup = CSSM_FALSE; lookUpPriv = NULL; } } } if(doLookup) { lookUpPriv = cspLookUpKeyByLabel(dlHand, dbHand, &labelData, CKT_Private); } if(privIsPerm) { if(lookUpPriv == NULL) { printf("lookup priv by label failed\n"); return testError(quiet); } /* note we "know" that both keys are ref keys...*/ if(lookUpPriv->KeyData.Length != privKey.KeyData.Length) { printf("priv key data length mismatch\n"); return testError(quiet); } #if DL_REF_KEYS_DIFFER if(!memcmp(lookUpPriv->KeyData.Data, privKey.KeyData.Data, privKey.KeyData.Length)) { printf("priv key ref data mismatch\n"); return testError(quiet); } #else /* DL_REF_KEYS_DIFFER */ if(memcmp(lookUpPriv->KeyData.Data, privKey.KeyData.Data, privKey.KeyData.Length)) { printf("unexpected priv key ref data match\n"); return testError(quiet); } #endif /* DL_REF_KEYS_DIFFER */ /* verify header compare in any case */ if(memcmp(&lookUpPriv->KeyHeader, &privKey.KeyHeader, sizeof(CSSM_KEYHEADER))) { printf("**privKey header miscompare\n"); return testError(quiet); } /* sign with lookUpPriv, verify with pubKey */ sig.Data = NULL; sig.Length = 0; if(doSignVerify) { if(verbose) { printf(" ...sign with lookup priv\n"); } if(cspSign(cspHand, sigAlg, lookUpPriv, ptext, &sig)) { return testError(quiet); } if(verbose) { printf(" ...verify with pub\n"); } if(cspSigVerify(cspHand, sigAlg, &pubKey, ptext, &sig, CSSM_OK)) { printf("***sign with lookUpPriv, vfy with pub FAILED\n"); return testError(quiet); } CSSM_FREE(sig.Data); sig.Data = NULL; sig.Data = 0; } /* 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 DO_OBTAIN_FROM_PUB /* obtainedPriv = obtainPubFromPriv(pub); ensure == priv; */ if(verbose) { printf(" ...ObtainPrivateKeyFromPublicKey\n"); } obtainedPriv.KeyData.Data = NULL; obtainedPriv.KeyData.Length = 0; crtn = CSSM_CSP_ObtainPrivateKeyFromPublicKey(cspHand, lookUpPub, &obtainedPriv); if(crtn) { printError("ObtainPrivateKeyFromPublicKey", crtn); return testError(quiet); } /* free obtainedPriv from cache, but it's permanent */ if(verbose) { printf(" ...free obtainedPriv\n"); } if(cspFreeKey(cspHand, &obtainedPriv)) { printf("Error on cspFreeKey(obtainedPriv)\n"); return testError(quiet); } #endif /* DO_OBTAIN_FROM_PUB */ 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); } /* lookUpPriv = lookup priv by label; verify fail; */ if(doFailedLookup) { if(verbose) { printf(" ...lookup (nonexistent) priv by label\n"); } lookUpPriv = cspLookUpKeyByLabel(dlHand, dbHand, &labelData, CKT_Private); if(lookUpPriv != NULL) { printf("Unexpected success on cspLookUpKeyByLabel(priv)\n"); return testError(quiet); } } else { lookUpPriv = NULL; } } } else if(doLookup) { /* !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(verbose) { printf(" ...free privKey\n"); } if(cspFreeKey(cspHand, &privKey)) { printf("Error on cspFreeKey(privKey)\n"); return testError(quiet); } } /* CSP, DL have no knowledge of privKey or its variations */ /* obtainedPriv = obtainPubFromPriv(pub); verify fail;*/ /* Note this should be safe even if DO_OBTAIN_FROM_PUB == 0 */ obtainedPriv.KeyData.Data = NULL; obtainedPriv.KeyData.Length = 0; if(verbose) { printf(" ...obtain (nonexistent) priv by public\n"); } crtn = CSSM_CSP_ObtainPrivateKeyFromPublicKey(cspHand, &pubKey, &obtainedPriv); switch(crtn) { case CSSM_OK: printf("Unexpected success on ObtainPrivateKeyFromPublicKey\n"); return testError(quiet); case CSSMERR_CSP_PRIVATE_KEY_NOT_FOUND: break; #if !CSPDL_OBTAIN_PRIV_FROM_PUB case CSSMERR_CSP_FUNCTION_NOT_IMPLEMENTED: /* OK */ break; #endif default: printf("Unexpected err ObtainPrivateKeyFromPublicKey\n"); printError("got this", crtn); return testError(quiet); } /* 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); } } if(lookUpPub) { 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 && doFailedLookup) { /* 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_DATA ptext; 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; uint32 sigAlg = CSSM_ALGID_FEE_MD5; 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; CSSM_BOOL doSignVerify = CSSM_TRUE; CSSM_BOOL doFailedLookup = CSSM_TRUE; CSSM_BOOL privAlwaysExtractable = CSSM_FALSE; dbName[0] = '\0'; for(arg=1; arg