SecImportExportCrypto.cpp [plain text]
#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>
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#define SEC_KEY_HASH_ATTR_NAME "Label"
#define SEC_KEY_PRINT_NAME_ATTR_NAME "PrintName"
#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"
static CSSM_RETURN impExpSetKeyLabel(
CSSM_CSP_HANDLE cspHand, CSSM_DL_DB_HANDLE dlDbHand, SecKeychainRef kcRef, CSSM_KEY_PTR cssmKey,
const CSSM_DATA *existKeyLabel, const CSSM_DATA *newPrintName,
CssmOwnedData &newLabel, SecKeyRef *secKey) {
CSSM_RETURN crtn;
CSSM_DATA keyDigest = {0, NULL};
crtn = impExpKeyDigest(cspHand, cssmKey, &keyDigest);
if(crtn) {
return crtn;
}
newLabel.copy(keyDigest);
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
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) {
impExpFreeCssmMemory(cspHand, keyDigest.Data);
}
if(srchRef) {
CFRelease(srchRef);
}
return crtn;
}
OSStatus impExpImportRawKey(
CFDataRef inData,
SecExternalFormat externForm,
SecExternalItemType itemType,
CSSM_ALGORITHMS keyAlg,
SecKeychainRef importKeychain, CSSM_CSP_HANDLE cspHand, SecItemImportExportFlags flags,
const SecKeyImportExportParameters *keyParams, const char *printName, CFMutableArrayRef outArray) {
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;
crtn = impExpKeyForm(externForm, itemType, keyAlg, &format, &keyClass);
memset(&wrappedKey, 0, sizeof(CSSM_KEY));
wrappedKey.KeyData.Length = CFDataGetLength(inData);
wrappedKey.KeyData.Data = (uint8 *)CFDataGetBytePtr(inData);
hdr.HeaderVersion = CSSM_KEYHEADER_VERSION;
hdr.BlobType = CSSM_KEYBLOB_RAW;
hdr.Format = format;
hdr.AlgorithmId = keyAlg;
hdr.KeyClass = keyClass;
hdr.KeyAttr = CSSM_KEYATTR_EXTRACTABLE;
hdr.KeyUsage = CSSM_KEYUSE_ANY;
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;
OSStatus impExpKeyNotify(
SecKeychainRef importKeychain,
const CssmData &keyLabel, const CSSM_KEY &cssmKey) {
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<CssmClient::SSDbImpl *>(&(*keychain->database()));
if (impl == NULL) {
CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
}
CssmClient::SSDb ssDb(impl);
CssmClient::DbAttributes dbAttributes;
CssmClient::DbUniqueRecord uniqueId;
CssmClient::SSDbCursor dbCursor(ssDb, 3); 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;
}
Item keyItem = keychain->item(recordType, uniqueId);
keychain->postEvent(kSecAddEvent, keyItem);
return noErr;
}
#define SEC_RANDOM_LABEL_LEN 16
#define SEC_KEYATTR_RETURN_MASK \
(CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_RETURN_NONE)
OSStatus impExpImportKeyCommon(
const CSSM_KEY *wrappedKey,
SecKeychainRef importKeychain, CSSM_CSP_HANDLE cspHand, SecItemImportExportFlags flags,
const SecKeyImportExportParameters *keyParams, const impExpKeyUnwrapParams *unwrapParams,
const char *printName, CFMutableArrayRef outArray) {
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; CSSM_KEYATTR_FLAGS keyAttributes = 0; 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));
crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
unwrapParams->encrAlg,
unwrapParams->encrMode,
&nullCreds,
unwrapParams->unwrappingKey,
unwrapParams->iv.Data ? &unwrapParams->iv : NULL,
unwrapParams->encrPad,
0, &ccHand);
if(crtn) {
goto errOut;
}
if(dlDbPtr) {
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)) {
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;
}
else {
labelData.Data = (uint8 *)SEC_SESSIONKEY_PRINT_NAME_ATTR_VALUE;
labelData.Length = strlen(SEC_SESSIONKEY_PRINT_NAME_ATTR_VALUE);
keyLabel.copy(labelData);
}
if(keyParams) {
keyUsage = keyParams->keyUsage;
keyAttributes = keyParams->keyAttributes;
}
if(keyUsage == 0) {
keyUsage = CSSM_KEYUSE_ANY;
}
if(keyAttributes == 0) {
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 {
keyAttributes &= ~SEC_KEYATTR_RETURN_MASK;
keyAttributes |= CSSM_KEYATTR_RETURN_REF;
}
if( (dlDbPtr != NULL) && (hdr.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY) && ( (keyParams == NULL) || !(keyParams->flags & kSecKeyNoAccessControl) )
) {
memset(&rcc, 0, sizeof(rcc));
maker.initialOwner(rcc);
rccPtr = &rcc;
}
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) {
if(unwrapParams->blockSizeInBits != 64) {
SecImpExpDbg("WARNING impExpImportKeyCommon: setting block size to %lu",
(unsigned long)unwrapParams->blockSizeInBits);
crtn = impExpAddContextAttribute(ccHand,
CSSM_ATTRIBUTE_BLOCK_SIZE,
sizeof(uint32),
(void *)unwrapParams->blockSizeInBits);
if(crtn) {
SecImpExpDbg("impExpImportKeyCommon: CSSM_UpdateContextAttributes error");
goto errOut;
}
}
}
crtn = CSSM_UnwrapKey(ccHand,
NULL, (const CSSM_WRAP_KEY *)wrappedKey,
keyUsage,
keyAttributes,
&labelData,
rccPtr, &unwrappedKey,
&descrData); if(crtn != CSSM_OK) {
SecImpExpDbg("CSSM_UnwrapKey failure");
if(crtn == CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA) {
crtn = errSecDuplicateItem;
}
goto errOut;
}
if((hdr.KeyClass != CSSM_KEYCLASS_SESSION_KEY) && (dlDbPtr != NULL)) {
CSSM_DATA newPrintName;
if(printName) {
newPrintName.Data = (uint8 *)printName;
}
else {
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;
}
}
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) {
if(e.error != CSSMERR_CSP_FUNCTION_NOT_IMPLEMENTED) {
crtn = e.error;
}
}
catch(...) {
SecImpExpDbg("keyImport: exception on setAccess\n");
crtn = errSecAuthFailed;
}
}
if(outArray) {
if(secKeyRef == NULL) {
assert(importKeychain == NULL);
ortn = SecKeyCreateWithCSSMKey(&unwrappedKey, &secKeyRef);
if(ortn) {
SecImpExpDbg("SecKeyCreateWithCSSMKey failure");
crtn = ortn;
goto errOut;
}
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) {
CSSM_FreeKey(cspHand, NULL, &unwrappedKey, CSSM_FALSE);
}
return crtn;
}
CSSM_RETURN impExpExportKeyCommon(
CSSM_CSP_HANDLE cspHand, SecKeyRef secKey,
CSSM_KEY_PTR wrappingKey,
CSSM_KEY_PTR wrappedKey, CSSM_ALGORITHMS wrapAlg,
CSSM_ENCRYPT_MODE wrapMode,
CSSM_PADDING wrapPad,
CSSM_KEYBLOB_FORMAT wrapFormat, CSSM_ATTRIBUTE_TYPE blobAttrType, CSSM_KEYBLOB_FORMAT blobForm, const CSSM_DATA *descData, 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;
}
CSSM_ACCESS_CREDENTIALS nullCreds;
memset(&nullCreds, 0, sizeof(nullCreds));
const CSSM_ACCESS_CREDENTIALS *creds = &nullCreds;
CSSM_KEYCLASS keyClass = unwrappedKey->KeyHeader.KeyClass;
if(keyClass == CSSM_KEYCLASS_PRIVATE_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, wrappingKey,
iv,
wrapPad,
0, &ccHand);
if(ortn) {
SecImpExpDbg("impExpExportKeyCommon CSSM_CSP_CreateSymmetricContext error");
return crtn;
}
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:
{
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;
}