X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_keychain/lib/SecImportExportCrypto.cpp?ds=sidebyside diff --git a/Security/libsecurity_keychain/lib/SecImportExportCrypto.cpp b/Security/libsecurity_keychain/lib/SecImportExportCrypto.cpp new file mode 100644 index 00000000..71a4b315 --- /dev/null +++ b/Security/libsecurity_keychain/lib/SecImportExportCrypto.cpp @@ -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 +#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; + 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(&(*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; 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 *)((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 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; +}