/* * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * SecImportExportCrypto.cpp - low-level crypto routines for wrapping and unwrapping * keys. */ #include "SecImportExport.h" #include "SecImportExportCrypto.h" #include "SecImportExportUtils.h" #include "Keychains.h" #include "Access.h" #include "Item.h" #include "SecKeyPriv.h" #include "KCEventNotifier.h" #include #include #include #include #include #include #include #include #include /* * Key attrribute names and values. * * This is where the public key hash goes. */ #define SEC_KEY_HASH_ATTR_NAME "Label" /* * This is where the publicly visible name goes. */ #define SEC_KEY_PRINT_NAME_ATTR_NAME "PrintName" /* * Default values we ultimately assign to the PrintName attr. */ #define SEC_PRIVKEY_PRINT_NAME_ATTR_VALUE "Imported Private Key" #define SEC_PUBKEY_PRINT_NAME_ATTR_VALUE "Imported Public Key" #define SEC_SESSIONKEY_PRINT_NAME_ATTR_VALUE "Imported Key" /* * Set private key's Label and PrintName attributes. On entry Label * is typically a random string to faciliate finding the key in a DL; * the PrintName is currently set to the same value by the DL. We * replace the Label attr with the public key hash and the PrintName * attr with a caller-supplied value. */ static CSSM_RETURN impExpSetKeyLabel( CSSM_CSP_HANDLE cspHand, // where the key lives CSSM_DL_DB_HANDLE dlDbHand, // ditto SecKeychainRef kcRef, // ditto CSSM_KEY_PTR cssmKey, const CSSM_DATA *existKeyLabel, // existing label, a random string const CSSM_DATA *newPrintName, CssmOwnedData &newLabel, // RETURNED as what we set SecKeyRef *secKey) // RETURNED { CSSM_RETURN crtn; CSSM_DATA keyDigest = {0, NULL}; crtn = impExpKeyDigest(cspHand, cssmKey, &keyDigest); if(crtn) { return crtn; } /* caller needs this for subsequent DL lookup */ newLabel.copy(keyDigest); /* Find this key as a SecKeychainItem */ SecItemClass itemClass = (cssmKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY) ? kSecPrivateKeyItemClass : kSecPublicKeyItemClass; SecKeychainAttribute kcAttr = {kSecKeyLabel, existKeyLabel->Length, existKeyLabel->Data}; SecKeychainAttributeList kcAttrList = {1, &kcAttr}; SecKeychainSearchRef srchRef = NULL; OSStatus ortn; SecKeychainItemRef itemRef = NULL; ortn = SecKeychainSearchCreateFromAttributes(kcRef, itemClass, &kcAttrList, &srchRef); if(ortn) { SecImpExpDbg("SecKeychainSearchCreateFromAttributes error"); crtn = ortn; goto errOut; } ortn = SecKeychainSearchCopyNext(srchRef, &itemRef); if(ortn) { SecImpExpDbg("SecKeychainSearchCopyNext error"); crtn = ortn; goto errOut; } #ifndef NDEBUG ortn = SecKeychainSearchCopyNext(srchRef, &itemRef); if(ortn == noErr) { SecImpExpDbg("impExpSetKeyLabel: found second key with same label!"); crtn = internalComponentErr; goto errOut; } #endif /* NDEBUG */ /* modify two attributes... */ SecKeychainAttribute modAttrs[2]; modAttrs[0].tag = kSecKeyLabel; modAttrs[0].length = keyDigest.Length; modAttrs[0].data = keyDigest.Data; modAttrs[1].tag = kSecKeyPrintName; modAttrs[1].length = newPrintName->Length; modAttrs[1].data = newPrintName->Data; kcAttrList.count = 2; kcAttrList.attr = modAttrs; ortn = SecKeychainItemModifyAttributesAndData(itemRef, &kcAttrList, 0, NULL); if(ortn) { SecImpExpDbg("SecKeychainItemModifyAttributesAndData error"); crtn = ortn; goto errOut; } *secKey = (SecKeyRef)itemRef; errOut: if(keyDigest.Data) { /* mallocd by CSP */ impExpFreeCssmMemory(cspHand, keyDigest.Data); } if(srchRef) { CFRelease(srchRef); } return crtn; } /* * Import a raw key. This can be used as a lightweight "guess" evaluator * if a handle to the raw CSP is passed in (with no keychain), or as * the real thing which does full keychain import. */ OSStatus impExpImportRawKey( CFDataRef inData, SecExternalFormat externForm, SecExternalItemType itemType, CSSM_ALGORITHMS keyAlg, SecKeychainRef importKeychain, // optional CSSM_CSP_HANDLE cspHand, // required SecItemImportExportFlags flags, const SecKeyImportExportParameters *keyParams, // optional const char *printName, // optional CFMutableArrayRef outArray) // optional, append here { CSSM_RETURN crtn; CSSM_KEY wrappedKey; CSSM_KEYHEADER &hdr = wrappedKey.KeyHeader; CSSM_CSP_HANDLE rawCspHand = 0; CSSM_KEY_SIZE keySize; CSSM_KEYBLOB_FORMAT format; CSSM_KEYCLASS keyClass; /* First convert external format and types to CSSM style. */ crtn = impExpKeyForm(externForm, itemType, keyAlg, &format, &keyClass); /* cook up key to be null-unwrapped */ memset(&wrappedKey, 0, sizeof(CSSM_KEY)); wrappedKey.KeyData.Length = CFDataGetLength(inData); wrappedKey.KeyData.Data = (uint8 *)CFDataGetBytePtr(inData); hdr.HeaderVersion = CSSM_KEYHEADER_VERSION; /* CspId don't care */ hdr.BlobType = CSSM_KEYBLOB_RAW; hdr.Format = format; hdr.AlgorithmId = keyAlg; hdr.KeyClass = keyClass; /* LogicalKeySizeInBits calculated below */ /* attr and usage are for the incoming unwrapped key... */ hdr.KeyAttr = CSSM_KEYATTR_EXTRACTABLE; hdr.KeyUsage = CSSM_KEYUSE_ANY; /* * Get key size in bits from raw CSP. Doing this right now is a good * optimization for the "guessing" case; getting the key size from the * raw CSP involves a full decode on an alg- and format-specific manner. * If we've been given the wrong params, we'll fail right here without * the complication of a full UnwrapKey op. */ rawCspHand = cuCspStartup(CSSM_TRUE); if(rawCspHand == 0) { return CSSMERR_CSSM_ADDIN_LOAD_FAILED; } crtn = CSSM_QueryKeySizeInBits(rawCspHand, CSSM_INVALID_HANDLE, &wrappedKey, &keySize); cuCspDetachUnload(rawCspHand, CSSM_TRUE); if(crtn) { SecImpExpDbg("CSSM_QueryKeySizeInBits error"); return crtn; } hdr.LogicalKeySizeInBits = keySize.LogicalKeySizeInBits; impExpKeyUnwrapParams unwrapParams; memset(&unwrapParams, 0, sizeof(unwrapParams)); unwrapParams.encrAlg = CSSM_ALGID_NONE; unwrapParams.encrMode = CSSM_ALGMODE_NONE; unwrapParams.unwrappingKey = NULL; unwrapParams.encrPad = CSSM_PADDING_NONE; return impExpImportKeyCommon( &wrappedKey, importKeychain, cspHand, flags, keyParams, &unwrapParams, printName, outArray); } using namespace KeychainCore; /* * Post notification of a "new key added" event. * If you know of another way to do this, other than a dlclient-based lookup of the * existing key in order to get a KeychainCore::Item, by all means have at it. */ OSStatus impExpKeyNotify( SecKeychainRef importKeychain, const CssmData &keyLabel, // stored with this, we use it to do a lookup const CSSM_KEY &cssmKey) // unwrapped key in CSSM format { /* * Look up key in the DLDB by label, key class, algorithm, and key size. */ CSSM_DB_RECORDTYPE recordType; const CSSM_KEYHEADER &hdr = cssmKey.KeyHeader; switch(hdr.KeyClass) { case CSSM_KEYCLASS_PUBLIC_KEY: recordType = CSSM_DL_DB_RECORD_PUBLIC_KEY; break; case CSSM_KEYCLASS_PRIVATE_KEY: recordType = CSSM_DL_DB_RECORD_PRIVATE_KEY; break; case CSSM_KEYCLASS_SESSION_KEY: recordType = CSSM_DL_DB_RECORD_SYMMETRIC_KEY; break; default: return paramErr; } assert(importKeychain != NULL); Keychain keychain = KeychainImpl::required(importKeychain); SSDbImpl* impl = dynamic_cast(&(*keychain->database())); if (impl == NULL) // did we go bad? { CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); } CssmClient::SSDb ssDb(impl); CssmClient::DbAttributes dbAttributes; CssmClient::DbUniqueRecord uniqueId; CssmClient::SSDbCursor dbCursor(ssDb, 3); // three attributes dbCursor->recordType(recordType); dbCursor->add(CSSM_DB_EQUAL, KeySchema::Label, keyLabel); dbCursor->add(CSSM_DB_EQUAL, KeySchema::KeyType, hdr.AlgorithmId); dbCursor->add(CSSM_DB_EQUAL, KeySchema::KeySizeInBits, hdr.LogicalKeySizeInBits); CssmClient::Key key; if (!dbCursor->nextKey(&dbAttributes, key, uniqueId)) { SecImpExpDbg("impExpKeyNotify: key not found"); return errSecItemNotFound; } /* * Get a Keychain-style Item, post notification. */ Item keyItem = keychain->item(recordType, uniqueId); keychain->postEvent(kSecAddEvent, keyItem); return noErr; } /* * Size of random label string in ASCII chars to facilitate DL lookup. */ #define SEC_RANDOM_LABEL_LEN 16 #define SEC_KEYATTR_RETURN_MASK \ (CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_RETURN_NONE) /* * Common code to unwrap a key, used for raw keys (which do a NULL unwrap) and * wrapped keys. */ OSStatus impExpImportKeyCommon( const CSSM_KEY *wrappedKey, SecKeychainRef importKeychain, // optional CSSM_CSP_HANDLE cspHand, // required, if importKeychain is // present, must be from there SecItemImportExportFlags flags, const SecKeyImportExportParameters *keyParams, // optional const impExpKeyUnwrapParams *unwrapParams, const char *printName, // optional CFMutableArrayRef outArray) // optional, append here { CSSM_CC_HANDLE ccHand = 0; CSSM_RETURN crtn; CSSM_DATA labelData; CSSM_KEY unwrappedKey; CSSM_DL_DB_HANDLE dlDbHandle; CSSM_DL_DB_HANDLE *dlDbPtr = NULL; OSStatus ortn; CSSM_ACCESS_CREDENTIALS nullCreds; uint8 randLabel[SEC_RANDOM_LABEL_LEN + 1]; CSSM_KEYUSE keyUsage = 0; // default CSSM_KEYATTR_FLAGS keyAttributes = 0; // default const CSSM_KEYHEADER &hdr = wrappedKey->KeyHeader; CSSM_DATA descrData = {0, NULL}; ResourceControlContext rcc; Security::KeychainCore::Access::Maker maker; ResourceControlContext *rccPtr = NULL; SecAccessRef accessRef = keyParams ? keyParams->accessRef : NULL; CssmAutoData keyLabel(Allocator::standard()); SecKeyRef secKeyRef = NULL; bool usedSecKeyCreate = false; assert(unwrapParams != NULL); assert(cspHand != 0); if(importKeychain) { ortn = SecKeychainGetDLDBHandle(importKeychain, &dlDbHandle); if(ortn) { return ortn; } dlDbPtr = &dlDbHandle; } memset(&unwrappedKey, 0, sizeof(CSSM_KEY)); memset(&nullCreds, 0, sizeof(CSSM_ACCESS_CREDENTIALS)); /* context for unwrap */ crtn = CSSM_CSP_CreateSymmetricContext(cspHand, unwrapParams->encrAlg, unwrapParams->encrMode, &nullCreds, unwrapParams->unwrappingKey, unwrapParams->iv.Data ? &unwrapParams->iv : NULL, unwrapParams->encrPad, 0, // Params &ccHand); if(crtn) { goto errOut; } if(dlDbPtr) { /* Importing to a keychain - add DLDB to context */ crtn = impExpAddContextAttribute(ccHand, CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(CSSM_ATTRIBUTE_DL_DB_HANDLE), dlDbPtr); if(crtn) { SecImpExpDbg("impExpImportKeyCommon: CSSM_UpdateContextAttributes error"); goto errOut; } } if((hdr.KeyClass != CSSM_KEYCLASS_SESSION_KEY) && (dlDbPtr != NULL)) { /* Generate random 16-char label to facilitate DL lookup */ char *randAscii = (char *)randLabel; uint8 randBinary[SEC_RANDOM_LABEL_LEN / 2]; unsigned randBinaryLen = SEC_RANDOM_LABEL_LEN / 2; DevRandomGenerator rng; rng.random(randBinary, randBinaryLen); for(unsigned i=0; ikeyUsage; keyAttributes = keyParams->keyAttributes; } if(keyUsage == 0) { /* default */ keyUsage = CSSM_KEYUSE_ANY; } if(keyAttributes == 0) { /* default */ keyAttributes = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE; if(dlDbPtr) { keyAttributes |= CSSM_KEYATTR_PERMANENT; } if(hdr.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY) { keyAttributes |= (CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_EXTRACTABLE); } } else { /* caller-supplied; ensure we're generating a reference key */ keyAttributes &= ~SEC_KEYATTR_RETURN_MASK; keyAttributes |= CSSM_KEYATTR_RETURN_REF; } if( (dlDbPtr != NULL) && // not permanent, no ACL (hdr.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY) && // ACLs only for private key ( (keyParams == NULL) || // NULL --> default ACL !(keyParams->flags & kSecKeyNoAccessControl) // explicity request no ACL ) ) { /* * Prepare to set up either a default ACL or one provided by caller via * keyParams->accessRef. */ memset(&rcc, 0, sizeof(rcc)); maker.initialOwner(rcc); rccPtr = &rcc; } /* * Additional optional parameters: block size, rounds, * effectiveKeySize. * WARNING: block size and rounds, used for RC5, have not been tested. * OpenSSL, as of Panther ship, did not support RC5 encryption. */ if(unwrapParams->effectiveKeySizeInBits != 0) { assert(unwrapParams->unwrappingKey->KeyHeader.AlgorithmId == CSSM_ALGID_RC2); SecImpExpDbg("impExpImportKeyCommon: setting effectiveKeySizeInBits to %lu", (unsigned long)unwrapParams->effectiveKeySizeInBits); crtn = impExpAddContextAttribute(ccHand, CSSM_ATTRIBUTE_EFFECTIVE_BITS, sizeof(uint32), (void *)unwrapParams->effectiveKeySizeInBits); if(crtn) { SecImpExpDbg("impExpImportKeyCommon: CSSM_UpdateContextAttributes error"); goto errOut; } } if(unwrapParams->rounds != 0) { assert(unwrapParams->unwrappingKey->KeyHeader.AlgorithmId == CSSM_ALGID_RC5); SecImpExpDbg("impExpImportKeyCommon: setting rounds to %lu", (unsigned long)unwrapParams->rounds); crtn = impExpAddContextAttribute(ccHand, CSSM_ATTRIBUTE_ROUNDS, sizeof(uint32), (void *)unwrapParams->rounds); if(crtn) { SecImpExpDbg("impExpImportKeyCommon: CSSM_UpdateContextAttributes error"); goto errOut; } } if(unwrapParams->blockSizeInBits != 0) { /* Our RC5 implementation has a fixed block size */ if(unwrapParams->blockSizeInBits != 64) { SecImpExpDbg("WARNING impExpImportKeyCommon: setting block size to %lu", (unsigned long)unwrapParams->blockSizeInBits); /* * With the current CSP this will actually be ignored */ crtn = impExpAddContextAttribute(ccHand, CSSM_ATTRIBUTE_BLOCK_SIZE, sizeof(uint32), (void *)unwrapParams->blockSizeInBits); if(crtn) { SecImpExpDbg("impExpImportKeyCommon: CSSM_UpdateContextAttributes error"); goto errOut; } } } /* Here we go */ crtn = CSSM_UnwrapKey(ccHand, NULL, // public key (const CSSM_WRAP_KEY *)wrappedKey, keyUsage, keyAttributes, &labelData, rccPtr, // CredAndAclEntry &unwrappedKey, &descrData); // required if(crtn != CSSM_OK) { SecImpExpDbg("CSSM_UnwrapKey failure"); if(crtn == CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA) { /* report in a keychain-friendly way */ crtn = errSecDuplicateItem; } goto errOut; } /* Private and public keys: update Label as public key hash */ if((hdr.KeyClass != CSSM_KEYCLASS_SESSION_KEY) && (dlDbPtr != NULL)) { CSSM_DATA newPrintName; if(printName) { /* caller specified */ newPrintName.Data = (uint8 *)printName; } else { /* use defaults */ if(hdr.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY) { newPrintName.Data = (uint8 *)SEC_PRIVKEY_PRINT_NAME_ATTR_VALUE; } else { newPrintName.Data = (uint8 *)SEC_PUBKEY_PRINT_NAME_ATTR_VALUE; } } newPrintName.Length = strlen((char *)newPrintName.Data); #if old_way crtn = impExpSetKeyLabel(cspHand, *dlDbPtr, &unwrappedKey, &labelData, &newPrintName, keyLabel); #else crtn = impExpSetKeyLabel(cspHand, *dlDbPtr, importKeychain, &unwrappedKey, &labelData, &newPrintName, keyLabel, &secKeyRef); #endif if(crtn) { goto errOut; } } /* Private key: adjust ACL as appropriate */ if(rccPtr != NULL) { SecPointer theAccess(accessRef ? KeychainCore::Access::required(accessRef) : new KeychainCore::Access("Imported Private Key")); try { CssmClient::KeyAclBearer bearer(cspHand, unwrappedKey, Allocator::standard()); theAccess->setAccess(bearer, maker); } catch (const CssmError &e) { /* not implemented means we're talking to the raw CSP which does * not implement ACLs */ if(e.error != CSSMERR_CSP_FUNCTION_NOT_IMPLEMENTED) { crtn = e.error; } } catch(...) { SecImpExpDbg("keyImport: exception on setAccess\n"); crtn = errSecAuthFailed; /* ??? */ } } /* * If importKeychain is non-NULL we've already added the key to the keychain. * If importKeychain is NULL, and outArray is non-NULL, we have to use the * half-baked SecKeyCreateWithCSSMKey to give the caller *something*. */ if(outArray) { if(secKeyRef == NULL) { assert(importKeychain == NULL); ortn = SecKeyCreateWithCSSMKey(&unwrappedKey, &secKeyRef); if(ortn) { SecImpExpDbg("SecKeyCreateWithCSSMKey failure"); crtn = ortn; goto errOut; } /* don't CSSM_FreeKey() this key */ usedSecKeyCreate = true; } CFArrayAppendValue(outArray, secKeyRef); } if(importKeychain) { impExpKeyNotify(importKeychain, keyLabel.get(), unwrappedKey); } errOut: if(ccHand != 0) { CSSM_DeleteContext(ccHand); } if(secKeyRef) { CFRelease(secKeyRef); } if((unwrappedKey.KeyData.Data != NULL) && !usedSecKeyCreate) { /* skip this free if we used SecKeyCreateWithCSSMKey() */ CSSM_FreeKey(cspHand, NULL, &unwrappedKey, CSSM_FALSE); } return crtn; } /* * Common code to wrap a key for export. */ CSSM_RETURN impExpExportKeyCommon( CSSM_CSP_HANDLE cspHand, // for all three keys SecKeyRef secKey, CSSM_KEY_PTR wrappingKey, CSSM_KEY_PTR wrappedKey, // RETURNED CSSM_ALGORITHMS wrapAlg, CSSM_ENCRYPT_MODE wrapMode, CSSM_PADDING wrapPad, CSSM_KEYBLOB_FORMAT wrapFormat, // NONE, PKCS7, PKCS8, OPENSSL CSSM_ATTRIBUTE_TYPE blobAttrType, // optional raw key format attr CSSM_KEYBLOB_FORMAT blobForm, // ditto const CSSM_DATA *descData, // optional descriptive data const CSSM_DATA *iv) { OSStatus ortn; CSSM_RETURN crtn; const CSSM_KEY *unwrappedKey; ortn = SecKeyGetCSSMKey(secKey, &unwrappedKey); if(ortn) { SecImpExpDbg("impExpExportKeyCommon SecKeyGetCSSMKey error"); return ortn; } /* * Creds needed only for wrapping private keys. * We bother checking in case we ever want to use this to wrap * session keys. */ CSSM_ACCESS_CREDENTIALS nullCreds; memset(&nullCreds, 0, sizeof(nullCreds)); const CSSM_ACCESS_CREDENTIALS *creds = &nullCreds; // default CSSM_KEYCLASS keyClass = unwrappedKey->KeyHeader.KeyClass; if(keyClass == CSSM_KEYCLASS_PRIVATE_KEY) { ortn = SecKeyGetCredentials(secKey, CSSM_ACL_AUTHORIZATION_DECRYPT, // HACK will change! kSecCredentialTypeDefault, &creds); if(ortn) { SecImpExpDbg("impExpExportKeyCommon SecKeyGetCredentials error"); return ortn; } } CSSM_CC_HANDLE ccHand; crtn = CSSM_CSP_CreateSymmetricContext(cspHand, wrapAlg, wrapMode, &nullCreds, // creds for wrapping key, never a private key here wrappingKey, iv, wrapPad, 0, // Params &ccHand); if(ortn) { SecImpExpDbg("impExpExportKeyCommon CSSM_CSP_CreateSymmetricContext error"); return crtn; } /* a couple of optional caller-specified attributes */ if(wrapFormat != CSSM_KEYBLOB_WRAPPED_FORMAT_NONE) { crtn = impExpAddContextAttribute(ccHand, CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT, sizeof(uint32), (void *)wrapFormat); if(crtn) { SecImpExpDbg("impExpExportKeyCommon AddContextAttribute error (1)"); CSSM_DeleteContext(ccHand); return crtn; } } if(blobAttrType != CSSM_ATTRIBUTE_NONE) { crtn = impExpAddContextAttribute(ccHand, blobAttrType, sizeof(uint32), (void *)blobForm); if(crtn) { SecImpExpDbg("impExpExportKeyCommon AddContextAttribute error"); return crtn; } } CSSM_DATA dData = {0, 0}; if(descData) { dData = *descData; } memset(wrappedKey, 0, sizeof(wrappedKey)); crtn = CSSM_WrapKey(ccHand, creds, unwrappedKey, &dData, wrappedKey); CSSM_DeleteContext(ccHand); switch(crtn) { case CSSM_OK: break; case CSSMERR_CSP_INVALID_KEYATTR_MASK: { /* * This is what comes back when we try to wrap an unextractable * key, or when we null wrap a sensitive key. Give the caller * some useful info. */ CSSM_KEYATTR_FLAGS attr = unwrappedKey->KeyHeader.KeyAttr; if(!(attr & CSSM_KEYATTR_EXTRACTABLE)) { SecImpExpDbg("impExpExportKeyCommon !EXTRACTABLE"); return errSecDataNotAvailable; } if((attr & CSSM_KEYATTR_SENSITIVE) && (wrappingKey == NULL)) { SecImpExpDbg("impExpExportKeyCommon !SENSITIVE, NULL wrap"); return errSecPassphraseRequired; } } default: SecImpExpDbg("impExpExportKeyCommon CSSM_WrapKey error"); } return crtn; }