--- /dev/null
+/*
+ * 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 <security_keychain/KeyItem.h>
+#include <Security/cssmtype.h>
+#include <security_keychain/Access.h>
+#include <security_keychain/Keychains.h>
+#include <security_keychain/KeyItem.h>
+#include <security_cdsa_client/wrapkey.h>
+#include <security_cdsa_client/genkey.h>
+#include <security_cdsa_client/signclient.h>
+#include <security_cdsa_client/cryptoclient.h>
+
+#include <security_keychain/Globals.h>
+#include "KCEventNotifier.h"
+#include <CommonCrypto/CommonDigest.h>
+#include <SecBase.h>
+
+// @@@ 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<SSDbImpl*>(&(*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> 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<SSDbImpl*>(&(*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> 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<Security::CssmClient::SSDbUniqueRecordImpl *>(impl);
+ if (simpl == NULL)
+ {
+ CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
+ }
+
+ return CssmClient::SSDbUniqueRecord(simpl);
+}
+
+CssmClient::Key &
+KeyItem::key()
+{
+ StLock<Mutex>_(mMutex);
+ if (!mKey)
+ {
+ CssmClient::SSDbUniqueRecord uniqueId(ssDbUniqueRecord());
+ CssmDataContainer dataBlob(uniqueId->allocator());
+ uniqueId->get(NULL, &dataBlob);
+ mKey = CssmClient::Key(uniqueId->database()->csp(), *reinterpret_cast<CssmKey *>(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<Access> initialAccess,
+ SecPointer<KeyItem> &outPublicKey,
+ SecPointer<KeyItem> &outPrivateKey)
+{
+ bool freeKeys = false;
+ bool deleteContext = false;
+
+ if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
+ MacOSError::throwMe(errSecInvalidKeychain);
+
+ SSDbImpl* impl = dynamic_cast<SSDbImpl*>(&(*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<CssmData *>(outData);
+ CssmData &pubKeyHash = *cssmData;
+
+ auto_ptr<string>privDescription;
+ auto_ptr<string>pubDescription;
+ 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<Access> 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<KeyItem*>(&(*publicKeyItem));
+ if (impl == NULL)
+ {
+ CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
+ }
+
+ outPublicKey = impl;
+
+ impl = dynamic_cast<KeyItem*>(&(*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<Access> initialAccess,
+ SecPointer<KeyItem> &outPublicKey,
+ SecPointer<KeyItem> &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<SSDbImpl *>(&(*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<CssmData *>(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_ptr<string>privDescription;
+ auto_ptr<string>pubDescription;
+ 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<Access> 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<KeyItem*>(&(*publicKeyItem));
+ if (impl == NULL)
+ {
+ CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
+ }
+
+ outPublicKey = impl;
+
+ impl = dynamic_cast<KeyItem*>(&(*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>
+KeyItem::generateWithAttributes(const SecKeychainAttributeList *attrList,
+ Keychain keychain,
+ CSSM_ALGORITHMS algorithm,
+ uint32 keySizeInBits,
+ CSSM_CC_HANDLE contextHandle,
+ CSSM_KEYUSE keyUsage,
+ uint32 keyAttr,
+ SecPointer<Access> 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<SSDbImpl *>(&(*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*>(&*keyItem);
+ if (item == NULL)
+ {
+ CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
+ }
+
+ return item;
+}
+
+SecPointer<KeyItem>
+KeyItem::generate(Keychain keychain,
+ CSSM_ALGORITHMS algorithm,
+ uint32 keySizeInBits,
+ CSSM_CC_HANDLE contextHandle,
+ CSSM_KEYUSE keyUsage,
+ uint32 keyAttr,
+ SecPointer<Access> 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;
+ }
+
+ case kSecPaddingSigRaw:
+ {
+ paddingAlg = CSSM_PADDING_SIGRAW;
+ break;
+ }
+
+ default:
+ {
+ paddingAlg = CSSM_PADDING_NONE;
+ break;
+ }
+ }
+
+ Sign signContext(csp(), baseAlg);
+ signContext.key(key());
+ signContext.cred(credentials);
+ // Fields required for CSSM_CSP_CreateSignatureContext set above. Using add instead of set ensures
+ // that the context is constructed before the set is attempted, which would fail silently otherwise.
+ signContext.add(CSSM_ATTRIBUTE_PADDING, paddingAlg);
+
+ 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;
+ }
+
+ case kSecPaddingSigRaw:
+ {
+ paddingAlg = CSSM_PADDING_SIGRAW;
+ break;
+ }
+
+ default:
+ {
+ paddingAlg = CSSM_PADDING_NONE;
+ break;
+ }
+ }
+
+ Verify verifyContext(csp(), baseAlg);
+ verifyContext.key(key());
+ verifyContext.cred(credentials);
+ // Fields required for CSSM_CSP_CreateSignatureContext set above. Using add instead of set ensures
+ // that the context is constructed before the set is attempted, which would fail silently otherwise.
+ verifyContext.add(CSSM_ATTRIBUTE_PADDING, paddingAlg);
+
+ 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;
+}
+