]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_keychain/lib/SecImportExportCrypto.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_keychain / lib / SecImportExportCrypto.cpp
diff --git a/Security/libsecurity_keychain/lib/SecImportExportCrypto.cpp b/Security/libsecurity_keychain/lib/SecImportExportCrypto.cpp
new file mode 100644 (file)
index 0000000..71a4b31
--- /dev/null
@@ -0,0 +1,744 @@
+/*
+ * Copyright (c) 2000-2004,2011-2014 Apple 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 <security_cdsa_utilities/cssmacl.h>
+#include <security_cdsa_utilities/KeySchema.h>
+#include <security_cdsa_utilities/cssmdata.h>
+#include <security_cdsa_utils/cuCdsaUtils.h>
+#include <security_utilities/devrandom.h>
+#include <security_cdsa_client/securestorage.h>
+#include <security_cdsa_client/dlclient.h>
+#include <Security/cssmapi.h>
+
+/*
+ * 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;
+       switch (cssmKey->KeyHeader.KeyClass) {
+               case CSSM_KEYCLASS_PRIVATE_KEY:
+                       itemClass = kSecPrivateKeyItemClass;
+                       break;
+               case CSSM_KEYCLASS_PUBLIC_KEY:
+                       itemClass = kSecPublicKeyItemClass;
+                       break;
+               case CSSM_KEYCLASS_SESSION_KEY:
+                       itemClass = kSecSymmetricKeyItemClass;
+                       break;
+               default:
+                       itemClass = (SecItemClass) 0;
+       }
+       SecKeychainAttribute kcAttr = {kSecKeyLabel, (UInt32)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 == errSecSuccess) {
+               SecImpExpDbg("impExpSetKeyLabel: found second key with same label!");
+               crtn = errSecInternalComponent;
+               goto errOut;
+       }
+       #endif  /* NDEBUG */
+
+       /* modify two attributes... */
+       SecKeychainAttribute modAttrs[2];
+       modAttrs[0].tag    = kSecKeyLabel;
+       modAttrs[0].length = (UInt32)keyDigest.Length;
+       modAttrs[0].data   = keyDigest.Data;
+       modAttrs[1].tag    = kSecKeyPrintName;
+       modAttrs[1].length = (UInt32)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 errSecParam;
+       }
+       assert(importKeychain != NULL);
+       Keychain keychain = KeychainImpl::required(importKeychain);
+
+       SSDbImpl* impl = dynamic_cast<CssmClient::SSDbImpl *>(&(*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 errSecSuccess;
+}
+
+/*
+ * 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; i<randBinaryLen; i++) {
+                       sprintf(randAscii, "%02X", randBinary[i]);
+                       randAscii += 2;
+               }
+               labelData.Data = randLabel;
+               labelData.Length = SEC_RANDOM_LABEL_LEN;
+               /* actual keyLabel value set later */
+       }
+       else {
+               labelData.Data = (uint8 *)SEC_SESSIONKEY_PRINT_NAME_ATTR_VALUE;
+               labelData.Length = strlen(SEC_SESSIONKEY_PRINT_NAME_ATTR_VALUE);
+               keyLabel.copy(labelData);
+       }
+
+       /*
+        * key attr flags and usage. First the defaults.
+        */
+       if(keyParams) {
+               keyUsage = keyParams->keyUsage;
+               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 *)((size_t) 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 *)((size_t)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 *)((size_t)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<KeychainCore::Access> 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;
+       }
+       else if(!(unwrappedKey->KeyHeader.KeyAttr & CSSM_KEYATTR_EXTRACTABLE)) {
+               SecImpExpDbg("impExpExportKeyCommon: CSSM key is non-extractable");
+               return errSecDataNotAvailable;
+       }
+
+       /*
+        * Creds are needed for wrapping private and 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 || keyClass == CSSM_KEYCLASS_SESSION_KEY) {
+               ortn = SecKeyGetCredentials(secKey,
+                       CSSM_ACL_AUTHORIZATION_DECRYPT,
+                       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 *)((size_t)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 *)((size_t)blobForm));
+               if(crtn) {
+                       SecImpExpDbg("impExpExportKeyCommon AddContextAttribute error");
+                       return crtn;
+               }
+       }
+
+       CSSM_DATA dData = {0, 0};
+       if(descData) {
+               dData = *descData;
+       }
+
+       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;
+}