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_KEY_PRINT_NAME_ATTR_VALUE "Imported Private Key"
#define SEC_PUBKEY_PRINT_NAME_ATTR_VALUE "Imported Key"
static CSSM_RETURN impExpSetPrivKeyLabel(
CSSM_CSP_HANDLE cspHand, CSSM_DL_DB_HANDLE dlDbHand, CSSM_KEY_PTR key,
const CSSM_DATA *existKeyLabel, const CSSM_DATA *newPrintName,
CssmOwnedData &newLabel) {
CSSM_QUERY query;
CSSM_SELECTION_PREDICATE predicate;
CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
CSSM_RETURN crtn;
CSSM_HANDLE resultHand = 0;
CSSM_DATA keyDigest = {0, NULL};
crtn = impExpKeyDigest(cspHand, key, &keyDigest);
if(crtn) {
return crtn;
}
newLabel.copy(keyDigest);
query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
query.Conjunctive = CSSM_DB_NONE;
query.NumSelectionPredicates = 1;
predicate.DbOperator = CSSM_DB_EQUAL;
predicate.Attribute.Info.AttributeNameFormat =
CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
predicate.Attribute.Info.Label.AttributeName = "Label";
predicate.Attribute.Info.AttributeFormat =
CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
predicate.Attribute.Value = (CSSM_DATA_PTR)existKeyLabel;
query.SelectionPredicate = &predicate;
query.QueryLimits.TimeLimit = 0; query.QueryLimits.SizeLimit = 1; query.QueryFlags = 0;
CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
CSSM_DB_ATTRIBUTE_DATA attr[2];
attr[0].Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr[0].Info.Label.AttributeName = SEC_KEY_HASH_ATTR_NAME;
attr[0].Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
attr[1].Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr[1].Info.Label.AttributeName = SEC_KEY_PRINT_NAME_ATTR_NAME;
attr[1].Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
recordAttrs.NumberOfAttributes = 2;
recordAttrs.AttributeData = attr;
crtn = CSSM_DL_DataGetFirst(dlDbHand,
&query,
&resultHand,
&recordAttrs,
NULL, &record);
if(crtn != CSSM_OK) {
SecImpExpDbg("CSSM_DL_DataGetFirst error");
record = NULL;
resultHand = 0;
goto errOut;
}
impExpFreeCssmMemory(dlDbHand.DLHandle, attr[0].Value->Data);
impExpFreeCssmMemory(dlDbHand.DLHandle, attr[0].Value);
impExpFreeCssmMemory(dlDbHand.DLHandle, attr[1].Value->Data);
impExpFreeCssmMemory(dlDbHand.DLHandle, attr[1].Value);
attr[0].Value = &keyDigest;
attr[1].Value = const_cast<CSSM_DATA *>(newPrintName);
crtn = CSSM_DL_DataModify(dlDbHand,
CSSM_DL_DB_RECORD_PRIVATE_KEY,
record,
&recordAttrs,
NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
if(crtn) {
SecImpExpDbg("CSSM_DL_DataModify error");
goto errOut;
}
errOut:
if(resultHand) {
CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
}
if(record) {
CSSM_DL_FreeUniqueRecord(dlDbHand, record);
}
if(keyDigest.Data) {
impExpFreeCssmMemory(cspHand, keyDigest.Data);
}
return crtn;
}
OSStatus impExpImportRawKey(
CFDataRef inData,
SecExternalFormat externForm,
SecExternalItemType itemType,
CSSM_ALGORITHMS keyAlg,
SecKeychainRef importKeychain, CSSM_CSP_HANDLE cspHand, SecItemImportExportFlags flags,
const SecKeyImportExportParameters *keyParams, 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,
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);
CssmClient::SSDb ssDb(safe_cast<CssmClient::SSDbImpl *>(&(*keychain->database())));
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);
KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, 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,
CFMutableArrayRef outArray) {
CSSM_CC_HANDLE ccHand = 0;
CSSM_RETURN crtn;
CSSM_DATA labelData;
CSSM_KEY_PTR unwrappedKey = NULL;
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());
assert(unwrapParams != NULL);
assert(cspHand != 0);
if(importKeychain) {
ortn = SecKeychainGetDLDBHandle(importKeychain, &dlDbHandle);
if(ortn) {
return ortn;
}
dlDbPtr = &dlDbHandle;
}
unwrappedKey = (CSSM_KEY_PTR)malloc(sizeof(CSSM_KEY));
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_PRIVATE_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_PUBKEY_PRINT_NAME_ATTR_VALUE;
labelData.Length = strlen(SEC_PUBKEY_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",
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",
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",
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_PRIVATE_KEY) && (dlDbPtr != NULL)) {
CSSM_DATA newPrintName;
newPrintName.Data = (uint8 *)SEC_KEY_PRINT_NAME_ATTR_VALUE;
newPrintName.Length = strlen((char *)newPrintName.Data);
crtn = impExpSetPrivKeyLabel(cspHand, *dlDbPtr, unwrappedKey,
&labelData, &newPrintName, keyLabel);
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) {
SecKeyRef keyRef;
OSStatus ortn;
ortn = SecKeyCreate(unwrappedKey, &keyRef);
if(ortn) {
SecImpExpDbg("SecKeyCreate failure");
goto errOut;
}
CFArrayAppendValue(outArray, keyRef);
}
if(importKeychain) {
impExpKeyNotify(importKeychain, keyLabel.get(), *unwrappedKey);
}
errOut:
if(ccHand != 0) {
CSSM_DeleteContext(ccHand);
}
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 *iv)
{
OSStatus ortn;
CSSM_RETURN crtn;
const CSSM_KEY *unwrappedKey;
ortn = SecKeyGetCSSMKey(secKey, &unwrappedKey);
if(ortn) {
SecImpExpDbg("impExpExportKeyCommon SecKeyGetCSSMKey error");
return ortn;
}
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 descData = {0, 0};
memset(wrappedKey, 0, sizeof(wrappedKey));
crtn = CSSM_WrapKey(ccHand,
creds,
unwrappedKey,
&descData,
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;
}