X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_keychain/lib/KeyItem.cpp?ds=inline diff --git a/Security/libsecurity_keychain/lib/KeyItem.cpp b/Security/libsecurity_keychain/lib/KeyItem.cpp new file mode 100644 index 00000000..b1c4d17c --- /dev/null +++ b/Security/libsecurity_keychain/lib/KeyItem.cpp @@ -0,0 +1,1403 @@ +/* + * Copyright (c) 2002-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@ + */ + +// +// KeyItem.cpp +// +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "KCEventNotifier.h" +#include +#include + +// @@@ This needs to be shared. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-const-variable" +static CSSM_DB_NAME_ATTR(kInfoKeyPrintName, kSecKeyPrintName, (char*) "PrintName", 0, NULL, BLOB); +static CSSM_DB_NAME_ATTR(kInfoKeyLabel, kSecKeyLabel, (char*) "Label", 0, NULL, BLOB); +static CSSM_DB_NAME_ATTR(kInfoKeyApplicationTag, kSecKeyApplicationTag, (char*) "ApplicationTag", 0, NULL, BLOB); +#pragma clang diagnostic pop + +using namespace KeychainCore; +using namespace CssmClient; + +KeyItem::KeyItem(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId) : + ItemImpl(keychain, primaryKey, uniqueId), + mKey(), + algid(NULL), + mPubKeyHash(Allocator::standard()) +{ +} + +KeyItem::KeyItem(const Keychain &keychain, const PrimaryKey &primaryKey) : + ItemImpl(keychain, primaryKey), + mKey(), + algid(NULL), + mPubKeyHash(Allocator::standard()) +{ +} + +KeyItem* KeyItem::make(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId) +{ + KeyItem* k = new KeyItem(keychain, primaryKey, uniqueId); + keychain->addItem(primaryKey, k); + return k; +} + + + +KeyItem* KeyItem::make(const Keychain &keychain, const PrimaryKey &primaryKey) +{ + KeyItem* k = new KeyItem(keychain, primaryKey); + keychain->addItem(primaryKey, k); + return k; +} + + + +KeyItem::KeyItem(KeyItem &keyItem) : + ItemImpl(keyItem), + mKey(), + algid(NULL), + mPubKeyHash(Allocator::standard()) +{ + // @@@ this doesn't work for keys that are not in a keychain. +} + +KeyItem::KeyItem(const CssmClient::Key &key) : + ItemImpl(key->keyClass() + CSSM_DL_DB_RECORD_PUBLIC_KEY, (OSType)0, (UInt32)0, (const void*)NULL), + mKey(key), + algid(NULL), + mPubKeyHash(Allocator::standard()) +{ + if (key->keyClass() > CSSM_KEYCLASS_SESSION_KEY) + MacOSError::throwMe(errSecParam); +} + +KeyItem::~KeyItem() +{ +} + +void +KeyItem::update() +{ + ItemImpl::update(); +} + +Item +KeyItem::copyTo(const Keychain &keychain, Access *newAccess) +{ + if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP)) + MacOSError::throwMe(errSecInvalidKeychain); + + /* Get the destination keychain's db. */ + SSDbImpl* dbImpl = dynamic_cast(&(*keychain->database())); + if (dbImpl == NULL) + { + CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); + } + + SSDb ssDb(dbImpl); + + /* Make sure mKey is valid. */ + const CSSM_KEY *cssmKey = key(); + if (cssmKey && (0==(cssmKey->KeyHeader.KeyAttr & CSSM_KEYATTR_EXTRACTABLE))) + { + MacOSError::throwMe(errSecDataNotAvailable); + } + + // Generate a random label to use initially + CssmClient::CSP appleCsp(gGuidAppleCSP); + CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW); + uint8 labelBytes[20]; + CssmData label(labelBytes, sizeof(labelBytes)); + random.generate(label, (uint32)label.Length); + + /* Set up the ACL for the new key. */ + SecPointer access; + if (newAccess) + access = newAccess; + else + access = new Access(*mKey); + + /* Generate a random 3DES wrapping Key. */ + CssmClient::GenerateKey genKey(csp(), CSSM_ALGID_3DES_3KEY, 192); + CssmClient::Key wrappingKey(genKey(KeySpec(CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP, + CSSM_KEYATTR_EXTRACTABLE /* | CSSM_KEYATTR_RETURN_DATA */))); + + /* make a random IV */ + uint8 ivBytes[8]; + CssmData iv(ivBytes, sizeof(ivBytes)); + random.generate(iv, (uint32)iv.length()); + + /* Extract the key by wrapping it with the wrapping key. */ + CssmClient::WrapKey wrap(csp(), CSSM_ALGID_3DES_3KEY_EDE); + wrap.key(wrappingKey); + wrap.cred(getCredentials(CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED, kSecCredentialTypeDefault)); + wrap.mode(CSSM_ALGMODE_ECBPad); + wrap.padding(CSSM_PADDING_PKCS7); + wrap.initVector(iv); + CssmClient::Key wrappedKey(wrap(mKey)); + + /* Unwrap the new key into the new Keychain. */ + CssmClient::UnwrapKey unwrap(keychain->csp(), CSSM_ALGID_3DES_3KEY_EDE); + unwrap.key(wrappingKey); + unwrap.mode(CSSM_ALGMODE_ECBPad); + unwrap.padding(CSSM_PADDING_PKCS7); + unwrap.initVector(iv); + + /* Setup the dldbHandle in the context. */ + unwrap.add(CSSM_ATTRIBUTE_DL_DB_HANDLE, ssDb->handle()); + + /* Set up an initial aclEntry so we can change it after the unwrap. */ + Access::Maker maker(Allocator::standard(), Access::Maker::kAnyMakerType); + ResourceControlContext rcc; + maker.initialOwner(rcc, NULL); + unwrap.owner(rcc.input()); + + /* Unwrap the key. */ + uint32 usage = mKey->usage(); + /* Work around csp brokeness where it sets all usage bits in the Keyheader when CSSM_KEYUSE_ANY is set. */ + if (usage & CSSM_KEYUSE_ANY) + usage = CSSM_KEYUSE_ANY; + + CssmClient::Key unwrappedKey(unwrap(wrappedKey, KeySpec(usage, + (mKey->attributes() | CSSM_KEYATTR_PERMANENT) & ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE), + label))); + + /* Look up unwrapped key in the DLDB. */ + DbUniqueRecord uniqueId; + SSDbCursor dbCursor(ssDb, 1); + dbCursor->recordType(recordType()); + dbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label); + CssmClient::Key copiedKey; + if (!dbCursor->nextKey(NULL, copiedKey, uniqueId)) + MacOSError::throwMe(errSecItemNotFound); + + /* Copy the Label, PrintName and ApplicationTag attributes from the old key to the new one. */ + dbUniqueRecord(); + DbAttributes oldDbAttributes(mUniqueId->database(), 3); + oldDbAttributes.add(kInfoKeyLabel); + oldDbAttributes.add(kInfoKeyPrintName); + oldDbAttributes.add(kInfoKeyApplicationTag); + mUniqueId->get(&oldDbAttributes, NULL); + try + { + uniqueId->modify(recordType(), &oldDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); + } + catch (CssmError e) + { + // clean up after trying to insert a duplicate key + uniqueId->deleteRecord (); + throw; + } + + /* Set the acl and owner on the unwrapped key. */ + access->setAccess(*unwrappedKey, maker); + + /* Return a keychain item which represents the new key. */ + Item item(keychain->item(recordType(), uniqueId)); + + KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, item); + + return item; +} + +Item +KeyItem::importTo(const Keychain &keychain, Access *newAccess, SecKeychainAttributeList *attrList) +{ + if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP)) + MacOSError::throwMe(errSecInvalidKeychain); + + /* Get the destination keychain's db. */ + SSDbImpl* dbImpl = dynamic_cast(&(*keychain->database())); + if (dbImpl == NULL) + CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); + + SSDb ssDb(dbImpl); + + /* Make sure mKey is valid. */ + /* We can't call key() here, since we won't have a unique record id yet */ + if (!mKey) + CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); + + // Generate a random label to use initially + CssmClient::CSP appleCsp(gGuidAppleCSP); + CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW); + uint8 labelBytes[20]; + CssmData label(labelBytes, sizeof(labelBytes)); + random.generate(label, (uint32)label.Length); + + /* Set up the ACL for the new key. */ + SecPointer access; + if (newAccess) + access = newAccess; + else + access = new Access(*mKey); + + /* Generate a random 3DES wrapping Key. */ + CssmClient::GenerateKey genKey(csp(), CSSM_ALGID_3DES_3KEY, 192); + CssmClient::Key wrappingKey(genKey(KeySpec(CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP, + CSSM_KEYATTR_EXTRACTABLE /* | CSSM_KEYATTR_RETURN_DATA */))); + + /* make a random IV */ + uint8 ivBytes[8]; + CssmData iv(ivBytes, sizeof(ivBytes)); + random.generate(iv, (uint32)iv.length()); + + /* Extract the key by wrapping it with the wrapping key. */ + CssmClient::WrapKey wrap(csp(), CSSM_ALGID_3DES_3KEY_EDE); + wrap.key(wrappingKey); + wrap.cred(getCredentials(CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED, kSecCredentialTypeDefault)); + wrap.mode(CSSM_ALGMODE_ECBPad); + wrap.padding(CSSM_PADDING_PKCS7); + wrap.initVector(iv); + CssmClient::Key wrappedKey(wrap(mKey)); + + /* Unwrap the new key into the new Keychain. */ + CssmClient::UnwrapKey unwrap(keychain->csp(), CSSM_ALGID_3DES_3KEY_EDE); + unwrap.key(wrappingKey); + unwrap.mode(CSSM_ALGMODE_ECBPad); + unwrap.padding(CSSM_PADDING_PKCS7); + unwrap.initVector(iv); + + /* Setup the dldbHandle in the context. */ + unwrap.add(CSSM_ATTRIBUTE_DL_DB_HANDLE, ssDb->handle()); + + /* Set up an initial aclEntry so we can change it after the unwrap. */ + Access::Maker maker(Allocator::standard(), Access::Maker::kAnyMakerType); + ResourceControlContext rcc; + maker.initialOwner(rcc, NULL); + unwrap.owner(rcc.input()); + + /* Unwrap the key. */ + uint32 usage = mKey->usage(); + /* Work around csp brokeness where it sets all usage bits in the Keyheader when CSSM_KEYUSE_ANY is set. */ + if (usage & CSSM_KEYUSE_ANY) + usage = CSSM_KEYUSE_ANY; + + CssmClient::Key unwrappedKey(unwrap(wrappedKey, KeySpec(usage, + (mKey->attributes() | CSSM_KEYATTR_PERMANENT) & ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE), + label))); + + /* Look up unwrapped key in the DLDB. */ + DbUniqueRecord uniqueId; + SSDbCursor dbCursor(ssDb, 1); + dbCursor->recordType(recordType()); + dbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label); + CssmClient::Key copiedKey; + if (!dbCursor->nextKey(NULL, copiedKey, uniqueId)) + MacOSError::throwMe(errSecItemNotFound); + + // Set the initial label, application label, and application tag (if provided) + if (attrList) { + DbAttributes newDbAttributes; + SSDbCursor otherDbCursor(ssDb, 1); + otherDbCursor->recordType(recordType()); + bool checkForDuplicates = false; + + for (UInt32 index=0; index < attrList->count; index++) { + SecKeychainAttribute attr = attrList->attr[index]; + CssmData attrData(attr.data, attr.length); + if (attr.tag == kSecKeyPrintName) { + newDbAttributes.add(kInfoKeyPrintName, attrData); + } + if (attr.tag == kSecKeyLabel) { + newDbAttributes.add(kInfoKeyLabel, attrData); + otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, attrData); + checkForDuplicates = true; + } + if (attr.tag == kSecKeyApplicationTag) { + newDbAttributes.add(kInfoKeyApplicationTag, attrData); + otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyApplicationTag, attrData); + checkForDuplicates = true; + } + } + + DbAttributes otherDbAttributes; + DbUniqueRecord otherUniqueId; + CssmClient::Key otherKey; + try + { + if (checkForDuplicates && otherDbCursor->nextKey(&otherDbAttributes, otherKey, otherUniqueId)) + MacOSError::throwMe(errSecDuplicateItem); + + uniqueId->modify(recordType(), &newDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); + } + catch (CssmError e) + { + // clean up after trying to insert a duplicate key + uniqueId->deleteRecord (); + throw; + } + } + + /* Set the acl and owner on the unwrapped key. */ + access->setAccess(*unwrappedKey, maker); + + /* Return a keychain item which represents the new key. */ + Item item(keychain->item(recordType(), uniqueId)); + + KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, item); + + return item; +} + +void +KeyItem::didModify() +{ +} + +PrimaryKey +KeyItem::add(Keychain &keychain) +{ + MacOSError::throwMe(errSecUnimplemented); +} + +CssmClient::SSDbUniqueRecord +KeyItem::ssDbUniqueRecord() +{ + DbUniqueRecordImpl *impl = &*dbUniqueRecord(); + Security::CssmClient::SSDbUniqueRecordImpl *simpl = dynamic_cast(impl); + if (simpl == NULL) + { + CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); + } + + return CssmClient::SSDbUniqueRecord(simpl); +} + +CssmClient::Key & +KeyItem::key() +{ + if (!mKey) + { + CssmClient::SSDbUniqueRecord uniqueId(ssDbUniqueRecord()); + CssmDataContainer dataBlob(uniqueId->allocator()); + uniqueId->get(NULL, &dataBlob); + mKey = CssmClient::Key(uniqueId->database()->csp(), *reinterpret_cast(dataBlob.Data)); + } + + return mKey; +} + +CssmClient::CSP +KeyItem::csp() +{ + return key()->csp(); +} + + +const CSSM_X509_ALGORITHM_IDENTIFIER& +KeyItem::algorithmIdentifier() +{ +#if 0 + CssmKey *mKey; + CSSM_KEY_TYPE algorithm + CSSM_KEY_PTR cssmKey = (CSSM_KEY_PTR)thisData->Data; +cssmKey->KeyHeader + static void printKeyHeader( + const CSSM_KEYHEADER &hdr) +{ + printf(" Algorithm : "); + switch(hdr.AlgorithmId) { +CSSM_X509_ALGORITHM_IDENTIFIER algID; + +CSSM_OID *CL_algToOid( + CSSM_ALGORITHMS algId) +typedef struct cssm_x509_algorithm_identifier { + CSSM_OID algorithm; + CSSM_DATA parameters; +} CSSM_X509_ALGORITHM_IDENTIFIER, *CSSM_X509_ALGORITHM_IDENTIFIER_PTR; +#endif + + abort(); +} + +/* + * itemID, used to locate Extended Attributes, is the public key hash for keys. + */ +const CssmData &KeyItem::itemID() +{ + if(mPubKeyHash.length() == 0) { + /* + * Fetch the attribute from disk. + */ + UInt32 tag = kSecKeyLabel; + UInt32 format = 0; + SecKeychainAttributeInfo attrInfo = {1, &tag, &format}; + SecKeychainAttributeList *attrList = NULL; + getAttributesAndData(&attrInfo, NULL, &attrList, NULL, NULL); + if((attrList == NULL) || (attrList->count != 1)) { + MacOSError::throwMe(errSecNoSuchAttr); + } + mPubKeyHash.copy(attrList->attr->data, attrList->attr->length); + freeAttributesAndData(attrList, NULL); + } + return mPubKeyHash; +} + + +unsigned int +KeyItem::strengthInBits(const CSSM_X509_ALGORITHM_IDENTIFIER *algid) +{ + // @@@ Make a context with key based on algid and use that to get the effective keysize and not just the logical one. + CSSM_KEY_SIZE keySize = {}; + CSSM_RETURN rv = CSSM_QueryKeySizeInBits (csp()->handle(), + CSSM_INVALID_HANDLE, + key(), + &keySize); + if (rv) + return 0; + + return keySize.LogicalKeySizeInBits; +} + +const AccessCredentials * +KeyItem::getCredentials( + CSSM_ACL_AUTHORIZATION_TAG operation, + SecCredentialType credentialType) +{ + // @@@ Fix this to actually examine the ACL for this key and consider operation and do the right thing. + //AutoAclEntryInfoList aclInfos; + //key()->getAcl(aclInfos); + + bool smartcard = keychain() != NULL ? (keychain()->database()->dl()->guid() == gGuidAppleSdCSPDL) : false; + + AclFactory factory; + switch (credentialType) + { + case kSecCredentialTypeDefault: + return smartcard?globals().smartcardItemCredentials():globals().itemCredentials(); + case kSecCredentialTypeWithUI: + return smartcard?globals().smartcardItemCredentials():factory.promptCred(); + case kSecCredentialTypeNoUI: + return factory.nullCred(); + default: + MacOSError::throwMe(errSecParam); + } +} + +bool +KeyItem::operator == (KeyItem &other) +{ + if (mKey && *mKey) + { + // Pointer compare + return this == &other; + } + + // If keychains are different, then keys are different + Keychain otherKeychain = other.keychain(); + return (mKeychain && otherKeychain && (*mKeychain == *otherKeychain)); +} + +void +KeyItem::createPair( + Keychain keychain, + CSSM_ALGORITHMS algorithm, + uint32 keySizeInBits, + CSSM_CC_HANDLE contextHandle, + CSSM_KEYUSE publicKeyUsage, + uint32 publicKeyAttr, + CSSM_KEYUSE privateKeyUsage, + uint32 privateKeyAttr, + SecPointer initialAccess, + SecPointer &outPublicKey, + SecPointer &outPrivateKey) +{ + bool freeKeys = false; + bool deleteContext = false; + + if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP)) + MacOSError::throwMe(errSecInvalidKeychain); + + SSDbImpl* impl = dynamic_cast(&(*keychain->database())); + if (impl == NULL) + { + CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); + } + + SSDb ssDb(impl); + CssmClient::CSP csp(keychain->csp()); + CssmClient::CSP appleCsp(gGuidAppleCSP); + + // Generate a random label to use initially + CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW); + uint8 labelBytes[20]; + CssmData label(labelBytes, sizeof(labelBytes)); + random.generate(label, (uint32)label.Length); + + // Create a Access::Maker for the initial owner of the private key. + ResourceControlContext rcc; + memset(&rcc, 0, sizeof(rcc)); + Access::Maker maker; + // @@@ Potentially provide a credential argument which allows us to generate keys in the csp. Currently the CSP let's anyone do this, but we might restrict this in the future, f.e. a smartcard could require out of band pin entry before a key can be generated. + maker.initialOwner(rcc); + // Create the cred we need to manipulate the keys until we actually set a new access control for them. + const AccessCredentials *cred = maker.cred(); + + CSSM_KEY publicCssmKey, privateCssmKey; + memset(&publicCssmKey, 0, sizeof(publicCssmKey)); + memset(&privateCssmKey, 0, sizeof(privateCssmKey)); + + CSSM_CC_HANDLE ccHandle = 0; + + Item publicKeyItem, privateKeyItem; + try + { + CSSM_RETURN status; + if (contextHandle) + ccHandle = contextHandle; + else + { + status = CSSM_CSP_CreateKeyGenContext(csp->handle(), algorithm, keySizeInBits, NULL, NULL, NULL, NULL, NULL, &ccHandle); + if (status) + CssmError::throwMe(status); + deleteContext = true; + } + + CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle(); + CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle; + CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } }; + status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes); + if (status) + CssmError::throwMe(status); + + // Generate the keypair + status = CSSM_GenerateKeyPair(ccHandle, publicKeyUsage, publicKeyAttr, &label, &publicCssmKey, privateKeyUsage, privateKeyAttr, &label, &rcc, &privateCssmKey); + if (status) + CssmError::throwMe(status); + freeKeys = true; + + // Find the keys we just generated in the DL to get SecKeyRef's to them + // so we can change the label to be the hash of the public key, and + // fix up other attributes. + + // Look up public key in the DLDB. + DbAttributes pubDbAttributes; + DbUniqueRecord pubUniqueId; + SSDbCursor dbPubCursor(ssDb, 1); + dbPubCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY); + dbPubCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label); + CssmClient::Key publicKey; + if (!dbPubCursor->nextKey(&pubDbAttributes, publicKey, pubUniqueId)) + MacOSError::throwMe(errSecItemNotFound); + + // Look up private key in the DLDB. + DbAttributes privDbAttributes; + DbUniqueRecord privUniqueId; + SSDbCursor dbPrivCursor(ssDb, 1); + dbPrivCursor->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY); + dbPrivCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label); + CssmClient::Key privateKey; + if (!dbPrivCursor->nextKey(&privDbAttributes, privateKey, privUniqueId)) + MacOSError::throwMe(errSecItemNotFound); + + // Convert reference public key to a raw key so we can use it + // in the appleCsp. + CssmClient::WrapKey wrap(csp, CSSM_ALGID_NONE); + wrap.cred(cred); + CssmClient::Key rawPubKey = wrap(publicKey); + + // Calculate the hash of the public key using the appleCSP. + CssmClient::PassThrough passThrough(appleCsp); + void *outData; + CssmData *cssmData; + + /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the + * associated key blob. + * Key is specified in CSSM_CSP_CreatePassThroughContext. + * Hash is allocated bythe CSP, in the App's memory, and returned + * in *outData. */ + passThrough.key(rawPubKey); + passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData); + cssmData = reinterpret_cast(outData); + CssmData &pubKeyHash = *cssmData; + + auto_ptrprivDescription; + auto_ptrpubDescription; + try { + privDescription.reset(new string(initialAccess->promptDescription())); + pubDescription.reset(new string(initialAccess->promptDescription())); + } + catch(...) { + /* this path taken if no promptDescription available, e.g., for complex ACLs */ + privDescription.reset(new string("Private key")); + pubDescription.reset(new string("Public key")); + } + + // Set the label of the public key to the public key hash. + // Set the PrintName of the public key to the description in the acl. + pubDbAttributes.add(kInfoKeyLabel, pubKeyHash); + pubDbAttributes.add(kInfoKeyPrintName, *pubDescription); + pubUniqueId->modify(CSSM_DL_DB_RECORD_PUBLIC_KEY, &pubDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); + + // Set the label of the private key to the public key hash. + // Set the PrintName of the private key to the description in the acl. + privDbAttributes.add(kInfoKeyLabel, pubKeyHash); + privDbAttributes.add(kInfoKeyPrintName, *privDescription); + privUniqueId->modify(CSSM_DL_DB_RECORD_PRIVATE_KEY, &privDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); + + // @@@ Not exception safe! + csp.allocator().free(cssmData->Data); + csp.allocator().free(cssmData); + + // Finally fix the acl and owner of the private key to the specified access control settings. + initialAccess->setAccess(*privateKey, maker); + + if(publicKeyAttr & CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT) { + /* + * Make the public key acl completely open. + * If the key was not encrypted, it already has a wide-open + * ACL (though that is a feature of securityd; it's not + * CDSA-specified behavior). + */ + SecPointer pubKeyAccess(new Access()); + pubKeyAccess->setAccess(*publicKey, maker); + } + + // Create keychain items which will represent the keys. + publicKeyItem = keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId); + privateKeyItem = keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId); + + KeyItem* impl = dynamic_cast(&(*publicKeyItem)); + if (impl == NULL) + { + CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); + } + + outPublicKey = impl; + + impl = dynamic_cast(&(*privateKeyItem)); + if (impl == NULL) + { + CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); + } + + outPrivateKey = impl; + } + catch (...) + { + if (freeKeys) + { + // Delete the keys if something goes wrong so we don't end up with inaccessible keys in the database. + CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, TRUE); + CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, TRUE); + } + + if (deleteContext) + CSSM_DeleteContext(ccHandle); + + throw; + } + + if (freeKeys) + { + CSSM_FreeKey(csp->handle(), NULL, &publicCssmKey, FALSE); + CSSM_FreeKey(csp->handle(), NULL, &privateCssmKey, FALSE); + } + + if (deleteContext) + CSSM_DeleteContext(ccHandle); + + if (keychain && publicKeyItem && privateKeyItem) + { + keychain->postEvent(kSecAddEvent, publicKeyItem); + keychain->postEvent(kSecAddEvent, privateKeyItem); + } +} + +void +KeyItem::importPair( + Keychain keychain, + const CSSM_KEY &publicWrappedKey, + const CSSM_KEY &privateWrappedKey, + SecPointer initialAccess, + SecPointer &outPublicKey, + SecPointer &outPrivateKey) +{ + bool freePublicKey = false; + bool freePrivateKey = false; + bool deleteContext = false; + + if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP)) + MacOSError::throwMe(errSecInvalidKeychain); + + SSDbImpl* impl = dynamic_cast(&(*keychain->database())); + if (impl == NULL) + { + CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); + } + + SSDb ssDb(impl); + CssmClient::CSP csp(keychain->csp()); + CssmClient::CSP appleCsp(gGuidAppleCSP); + + // Create a Access::Maker for the initial owner of the private key. + ResourceControlContext rcc; + memset(&rcc, 0, sizeof(rcc)); + Access::Maker maker(Allocator::standard(), Access::Maker::kAnyMakerType); + // @@@ Potentially provide a credential argument which allows us to unwrap keys in the csp. + // Currently the CSP lets anyone do this, but we might restrict this in the future, e.g. + // a smartcard could require out of band pin entry before a key can be generated. + maker.initialOwner(rcc); + // Create the cred we need to manipulate the keys until we actually set a new access control for them. + const AccessCredentials *cred = maker.cred(); + + CSSM_KEY publicCssmKey, privateCssmKey; + memset(&publicCssmKey, 0, sizeof(publicCssmKey)); + memset(&privateCssmKey, 0, sizeof(privateCssmKey)); + + CSSM_CC_HANDLE ccHandle = 0; + + Item publicKeyItem, privateKeyItem; + try + { + CSSM_RETURN status; + + // Calculate the hash of the public key using the appleCSP. + CssmClient::PassThrough passThrough(appleCsp); + void *outData; + CssmData *cssmData; + + /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the + * associated key blob. + * Key is specified in CSSM_CSP_CreatePassThroughContext. + * Hash is allocated bythe CSP, in the App's memory, and returned + * in *outData. */ + passThrough.key(&publicWrappedKey); + passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData); + cssmData = reinterpret_cast(outData); + CssmData &pubKeyHash = *cssmData; + + status = CSSM_CSP_CreateSymmetricContext(csp->handle(), publicWrappedKey.KeyHeader.WrapAlgorithmId, CSSM_ALGMODE_NONE, NULL, NULL, NULL, CSSM_PADDING_NONE, NULL, &ccHandle); + if (status) + CssmError::throwMe(status); + deleteContext = true; + + CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle(); + CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle; + CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } }; + status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes); + if (status) + CssmError::throwMe(status); + + // Unwrap the the keys + CSSM_DATA descriptiveData = {0, NULL}; + + status = CSSM_UnwrapKey( + ccHandle, + NULL, + &publicWrappedKey, + publicWrappedKey.KeyHeader.KeyUsage, + publicWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT, + &pubKeyHash, + &rcc, + &publicCssmKey, + &descriptiveData); + + if (status) + CssmError::throwMe(status); + freePublicKey = true; + + if (descriptiveData.Data != NULL) + free (descriptiveData.Data); + + status = CSSM_UnwrapKey( + ccHandle, + NULL, + &privateWrappedKey, + privateWrappedKey.KeyHeader.KeyUsage, + privateWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT, + &pubKeyHash, + &rcc, + &privateCssmKey, + &descriptiveData); + + if (status) + CssmError::throwMe(status); + + if (descriptiveData.Data != NULL) + free (descriptiveData.Data); + + freePrivateKey = true; + + // Find the keys we just generated in the DL to get SecKeyRefs to them + // so we can change the label to be the hash of the public key, and + // fix up other attributes. + + // Look up public key in the DLDB. + DbAttributes pubDbAttributes; + DbUniqueRecord pubUniqueId; + SSDbCursor dbPubCursor(ssDb, 1); + dbPubCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY); + dbPubCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, pubKeyHash); + CssmClient::Key publicKey; + if (!dbPubCursor->nextKey(&pubDbAttributes, publicKey, pubUniqueId)) + MacOSError::throwMe(errSecItemNotFound); + + // Look up private key in the DLDB. + DbAttributes privDbAttributes; + DbUniqueRecord privUniqueId; + SSDbCursor dbPrivCursor(ssDb, 1); + dbPrivCursor->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY); + dbPrivCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, pubKeyHash); + CssmClient::Key privateKey; + if (!dbPrivCursor->nextKey(&privDbAttributes, privateKey, privUniqueId)) + MacOSError::throwMe(errSecItemNotFound); + + // @@@ Not exception safe! + csp.allocator().free(cssmData->Data); + csp.allocator().free(cssmData); + + auto_ptrprivDescription; + auto_ptrpubDescription; + try { + privDescription.reset(new string(initialAccess->promptDescription())); + pubDescription.reset(new string(initialAccess->promptDescription())); + } + catch(...) { + /* this path taken if no promptDescription available, e.g., for complex ACLs */ + privDescription.reset(new string("Private key")); + pubDescription.reset(new string("Public key")); + } + + // Set the label of the public key to the public key hash. + // Set the PrintName of the public key to the description in the acl. + pubDbAttributes.add(kInfoKeyPrintName, *pubDescription); + pubUniqueId->modify(CSSM_DL_DB_RECORD_PUBLIC_KEY, &pubDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); + + // Set the label of the private key to the public key hash. + // Set the PrintName of the private key to the description in the acl. + privDbAttributes.add(kInfoKeyPrintName, *privDescription); + privUniqueId->modify(CSSM_DL_DB_RECORD_PRIVATE_KEY, &privDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); + + // Finally fix the acl and owner of the private key to the specified access control settings. + initialAccess->setAccess(*privateKey, maker); + + // Make the public key acl completely open + SecPointer pubKeyAccess(new Access()); + pubKeyAccess->setAccess(*publicKey, maker); + + // Create keychain items which will represent the keys. + publicKeyItem = keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId); + privateKeyItem = keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId); + + KeyItem* impl = dynamic_cast(&(*publicKeyItem)); + if (impl == NULL) + { + CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); + } + + outPublicKey = impl; + + impl = dynamic_cast(&(*privateKeyItem)); + if (impl == NULL) + { + CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); + } + outPrivateKey = impl; + } + catch (...) + { + if (freePublicKey) + CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, TRUE); + if (freePrivateKey) + CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, TRUE); + + if (deleteContext) + CSSM_DeleteContext(ccHandle); + + throw; + } + + if (freePublicKey) + CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, FALSE); + if (freePrivateKey) + CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, FALSE); + + if (deleteContext) + CSSM_DeleteContext(ccHandle); + + if (keychain && publicKeyItem && privateKeyItem) + { + KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, publicKeyItem); + KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, privateKeyItem); + } +} + +SecPointer +KeyItem::generateWithAttributes(const SecKeychainAttributeList *attrList, + Keychain keychain, + CSSM_ALGORITHMS algorithm, + uint32 keySizeInBits, + CSSM_CC_HANDLE contextHandle, + CSSM_KEYUSE keyUsage, + uint32 keyAttr, + SecPointer initialAccess) +{ + CssmClient::CSP appleCsp(gGuidAppleCSP); + CssmClient::CSP csp(NULL); + SSDb ssDb(NULL); + uint8 labelBytes[20]; + CssmData label(labelBytes, sizeof(labelBytes)); + bool freeKey = false; + bool deleteContext = false; + const CSSM_DATA *plabel = NULL; + + if (keychain) + { + if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP)) + MacOSError::throwMe(errSecInvalidKeychain); + + SSDbImpl* impl = dynamic_cast(&(*keychain->database())); + if (impl == NULL) + { + CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); + } + + ssDb = SSDb(impl); + csp = keychain->csp(); + + // Generate a random label to use initially + CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW); + random.generate(label, (uint32)label.Length); + plabel = &label; + } + else + { + // Not a persistent key so create it in the regular csp + csp = appleCsp; + } + + // Create a Access::Maker for the initial owner of the private key. + ResourceControlContext *prcc = NULL, rcc; + const AccessCredentials *cred = NULL; + Access::Maker maker; + if (keychain && initialAccess) + { + memset(&rcc, 0, sizeof(rcc)); + // @@@ Potentially provide a credential argument which allows us to generate keys in the csp. + // Currently the CSP lets anyone do this, but we might restrict this in the future, e.g. a smartcard + // could require out-of-band pin entry before a key can be generated. + maker.initialOwner(rcc); + // Create the cred we need to manipulate the keys until we actually set a new access control for them. + cred = maker.cred(); + prcc = &rcc; + } + + CSSM_KEY cssmKey; + + CSSM_CC_HANDLE ccHandle = 0; + + Item keyItem; + try + { + CSSM_RETURN status; + if (contextHandle) + ccHandle = contextHandle; + else + { + status = CSSM_CSP_CreateKeyGenContext(csp->handle(), algorithm, keySizeInBits, NULL, NULL, NULL, NULL, NULL, &ccHandle); + if (status) + CssmError::throwMe(status); + deleteContext = true; + } + + if (ssDb) + { + CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle(); + CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle; + CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } }; + status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes); + if (status) + CssmError::throwMe(status); + + keyAttr |= CSSM_KEYATTR_PERMANENT; + } + + // Generate the key + status = CSSM_GenerateKey(ccHandle, keyUsage, keyAttr, plabel, prcc, &cssmKey); + if (status) + CssmError::throwMe(status); + + if (ssDb) + { + freeKey = true; + // Find the key we just generated in the DL and get a SecKeyRef + // so we can specify the label attribute(s) and initial ACL. + + // Look up key in the DLDB. + DbAttributes dbAttributes; + DbUniqueRecord uniqueId; + SSDbCursor dbCursor(ssDb, 1); + dbCursor->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY); + dbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label); + CssmClient::Key key; + if (!dbCursor->nextKey(&dbAttributes, key, uniqueId)) + MacOSError::throwMe(errSecItemNotFound); + + // Set the initial label, application label, and application tag (if provided) + if (attrList) { + DbAttributes newDbAttributes; + SSDbCursor otherDbCursor(ssDb, 1); + otherDbCursor->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY); + bool checkForDuplicates = false; + + for (UInt32 index=0; index < attrList->count; index++) { + SecKeychainAttribute attr = attrList->attr[index]; + CssmData attrData(attr.data, attr.length); + if (attr.tag == kSecKeyPrintName) { + newDbAttributes.add(kInfoKeyPrintName, attrData); + } + if (attr.tag == kSecKeyLabel) { + newDbAttributes.add(kInfoKeyLabel, attrData); + otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, attrData); + checkForDuplicates = true; + } + if (attr.tag == kSecKeyApplicationTag) { + newDbAttributes.add(kInfoKeyApplicationTag, attrData); + otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyApplicationTag, attrData); + checkForDuplicates = true; + } + } + + DbAttributes otherDbAttributes; + DbUniqueRecord otherUniqueId; + CssmClient::Key otherKey; + if (checkForDuplicates && otherDbCursor->nextKey(&otherDbAttributes, otherKey, otherUniqueId)) + MacOSError::throwMe(errSecDuplicateItem); + + uniqueId->modify(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, &newDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); + } + + // Finally, fix the acl and owner of the key to the specified access control settings. + if (initialAccess) + initialAccess->setAccess(*key, maker); + + // Create keychain item which will represent the key. + keyItem = keychain->item(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, uniqueId); + } + else + { + CssmClient::Key tempKey(csp, cssmKey); + keyItem = new KeyItem(tempKey); + } + } + catch (...) + { + if (freeKey) + { + // Delete the key if something goes wrong so we don't end up with inaccessible keys in the database. + CSSM_FreeKey(csp->handle(), cred, &cssmKey, TRUE); + } + + if (deleteContext) + CSSM_DeleteContext(ccHandle); + + throw; + } + + if (freeKey) + { + CSSM_FreeKey(csp->handle(), NULL, &cssmKey, FALSE); + } + + if (deleteContext) + CSSM_DeleteContext(ccHandle); + + if (keychain && keyItem) + keychain->postEvent(kSecAddEvent, keyItem); + + KeyItem* item = dynamic_cast(&*keyItem); + if (item == NULL) + { + CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); + } + + return item; +} + +SecPointer +KeyItem::generate(Keychain keychain, + CSSM_ALGORITHMS algorithm, + uint32 keySizeInBits, + CSSM_CC_HANDLE contextHandle, + CSSM_KEYUSE keyUsage, + uint32 keyAttr, + SecPointer initialAccess) +{ + return KeyItem::generateWithAttributes(NULL, keychain, + algorithm, keySizeInBits, contextHandle, + keyUsage, keyAttr, initialAccess); +} + + +void KeyItem::RawSign(SecPadding padding, CSSM_DATA dataToSign, const AccessCredentials *credentials, CSSM_DATA& signature) +{ + CSSM_ALGORITHMS baseAlg = key()->header().algorithm(); + + if ((baseAlg != CSSM_ALGID_RSA) && (baseAlg != CSSM_ALGID_ECDSA)) + { + MacOSError::throwMe(errSecParam); + } + + CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1; + + switch (padding) + { + case kSecPaddingPKCS1: + { + paddingAlg = CSSM_PADDING_PKCS1; + break; + } + + case kSecPaddingPKCS1MD2: + { + baseAlg = CSSM_ALGID_MD2WithRSA; + break; + } + + case kSecPaddingPKCS1MD5: + { + baseAlg = CSSM_ALGID_MD5WithRSA; + break; + } + + case kSecPaddingPKCS1SHA1: + { + baseAlg = CSSM_ALGID_SHA1WithRSA; + break; + } + + default: + { + paddingAlg = CSSM_PADDING_NONE; + break; + } + } + + Sign signContext(csp(), baseAlg); + signContext.key(key()); + signContext.set(CSSM_ATTRIBUTE_PADDING, paddingAlg); + signContext.cred(credentials); + + CssmData data(dataToSign.Data, dataToSign.Length); + signContext.sign(data); + + CssmData sig(signature.Data, signature.Length); + signContext(sig); // yes, this is an accessor. Believe it, or not. + signature.Length = sig.length(); +} + + + +void KeyItem::RawVerify(SecPadding padding, CSSM_DATA dataToVerify, const AccessCredentials *credentials, CSSM_DATA sig) +{ + CSSM_ALGORITHMS baseAlg = key()->header().algorithm(); + if ((baseAlg != CSSM_ALGID_RSA) && (baseAlg != CSSM_ALGID_ECDSA)) + { + MacOSError::throwMe(errSecParam); + } + + CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1; + + switch (padding) + { + case kSecPaddingPKCS1: + { + paddingAlg = CSSM_PADDING_PKCS1; + break; + } + + case kSecPaddingPKCS1MD2: + { + baseAlg = CSSM_ALGID_MD2WithRSA; + break; + } + + case kSecPaddingPKCS1MD5: + { + baseAlg = CSSM_ALGID_MD5WithRSA; + break; + } + + case kSecPaddingPKCS1SHA1: + { + baseAlg = CSSM_ALGID_SHA1WithRSA; + break; + } + + default: + { + paddingAlg = CSSM_PADDING_NONE; + break; + } + } + + Verify verifyContext(csp(), baseAlg); + verifyContext.key(key()); + verifyContext.set(CSSM_ATTRIBUTE_PADDING, paddingAlg); + verifyContext.cred(credentials); + + CssmData data(dataToVerify.Data, dataToVerify.Length); + CssmData signature(sig.Data, sig.Length); + verifyContext.verify(data, signature); +} + + + +void KeyItem::Encrypt(SecPadding padding, CSSM_DATA dataToEncrypt, const AccessCredentials *credentials, CSSM_DATA& encryptedData) +{ + CSSM_ALGORITHMS baseAlg = key()->header().algorithm(); + if (baseAlg != CSSM_ALGID_RSA) + { + MacOSError::throwMe(errSecParam); + } + + CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1; + + switch (padding) + { + case kSecPaddingPKCS1: + { + paddingAlg = CSSM_PADDING_PKCS1; + break; + } + + default: + { + paddingAlg = CSSM_PADDING_NONE; + break; + } + } + + CssmClient::Encrypt encryptContext(csp(), baseAlg); + encryptContext.key(key()); + encryptContext.padding(paddingAlg); + encryptContext.cred(credentials); + + CssmData inData(dataToEncrypt.Data, dataToEncrypt.Length); + CssmData outData(encryptedData.Data, encryptedData.Length); + CssmData remData((void*) NULL, 0); + + encryptedData.Length = encryptContext.encrypt(inData, outData, remData); +} + + + +void KeyItem::Decrypt(SecPadding padding, CSSM_DATA dataToDecrypt, const AccessCredentials *credentials, CSSM_DATA& decryptedData) +{ + CSSM_ALGORITHMS baseAlg = key()->header().algorithm(); + if (baseAlg != CSSM_ALGID_RSA) + { + MacOSError::throwMe(errSecParam); + } + + CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1; + + switch (padding) + { + case kSecPaddingPKCS1: + { + paddingAlg = CSSM_PADDING_PKCS1; + break; + } + + + default: + { + paddingAlg = CSSM_PADDING_NONE; + break; + } + } + + CssmClient::Decrypt decryptContext(csp(), baseAlg); + decryptContext.key(key()); + decryptContext.padding(paddingAlg); + decryptContext.cred(credentials); + + CssmData inData(dataToDecrypt.Data, dataToDecrypt.Length); + CssmData outData(decryptedData.Data, decryptedData.Length); + CssmData remData((void*) NULL, 0); + decryptedData.Length = decryptContext.decrypt(inData, outData, remData); + if (remData.Data != NULL) + { + free(remData.Data); + } +} + +CFHashCode KeyItem::hash() +{ + CFHashCode result = 0; + const CSSM_KEY *cssmKey = key(); + if (NULL != cssmKey) + { + unsigned char digest[CC_SHA256_DIGEST_LENGTH]; + + CFIndex size_of_data = sizeof(CSSM_KEYHEADER) + cssmKey->KeyData.Length; + + CFMutableDataRef temp_cfdata = CFDataCreateMutable(kCFAllocatorDefault, size_of_data); + if (NULL == temp_cfdata) + { + return result; + } + + CFDataAppendBytes(temp_cfdata, (const UInt8 *)cssmKey, sizeof(CSSM_KEYHEADER)); + CFDataAppendBytes(temp_cfdata, cssmKey->KeyData.Data, cssmKey->KeyData.Length); + + if (size_of_data < 80) + { + // If it is less than 80 bytes then CFData can be used + result = CFHash(temp_cfdata); + CFRelease(temp_cfdata); + } + // CFData truncates its hash value to 80 bytes. ???? + // In order to do the 'right thing' a SHA 256 hash will be used to + // include all of the data + else + { + memset(digest, 0, CC_SHA256_DIGEST_LENGTH); + + CC_SHA256((const void *)CFDataGetBytePtr(temp_cfdata), (CC_LONG)CFDataGetLength(temp_cfdata), digest); + + CFDataRef data_to_hash = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, + (const UInt8 *)digest, CC_SHA256_DIGEST_LENGTH, kCFAllocatorNull); + result = CFHash(data_to_hash); + CFRelease(data_to_hash); + CFRelease(temp_cfdata); + } + } + return result; +} +