+/*
+ * 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;
+}