]> git.saurik.com Git - apple/security.git/blobdiff - Keychain/KeyItem.cpp
Security-164.1.tar.gz
[apple/security.git] / Keychain / KeyItem.cpp
index bfb10ec0edea4479b0e54150fdece00265f13eb8..5e41c74faa0bb735bbe48802869e47ad98f3341b 100644 (file)
 //
 #include <Security/KeyItem.h>
 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
 //
 #include <Security/KeyItem.h>
 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
+#include <Security/cssmtype.h>
+#include <Security/Access.h>
+#include <Security/Keychains.h>
+#include <Security/KeyItem.h>
+#include <Security/wrapkey.h>
+#include <Security/genkey.h>
+#include <Security/globals.h>
+#include "clNssUtils.h"
+#include "KCEventNotifier.h"
+
+// @@@ This needs to be shared.
+static CSSM_DB_NAME_ATTR(kSecKeyPrintName, 1, "PrintName", 0, NULL, BLOB);
+static CSSM_DB_NAME_ATTR(kSecKeyLabel, 6, "Label", 0, NULL, BLOB);
+static CSSM_DB_NAME_ATTR(kSecApplicationTag, 7, "ApplicationTag", 0, NULL, BLOB);
 
 using namespace KeychainCore;
 
 KeyItem::KeyItem(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId) :
        ItemImpl(keychain, primaryKey, uniqueId),
 
 using namespace KeychainCore;
 
 KeyItem::KeyItem(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId) :
        ItemImpl(keychain, primaryKey, uniqueId),
-       mKey(NULL)
+       mKey()
 {
 }
 
 KeyItem::KeyItem(const Keychain &keychain, const PrimaryKey &primaryKey)  :
        ItemImpl(keychain, primaryKey),
 {
 }
 
 KeyItem::KeyItem(const Keychain &keychain, const PrimaryKey &primaryKey)  :
        ItemImpl(keychain, primaryKey),
-       mKey(NULL)
+       mKey()
 {
 }
 
 KeyItem::KeyItem(KeyItem &keyItem) :
        ItemImpl(keyItem),
 {
 }
 
 KeyItem::KeyItem(KeyItem &keyItem) :
        ItemImpl(keyItem),
-       mKey(NULL)
+       mKey()
 {
 {
+       // @@@ this doesn't work for keys that are not in a keychain.
 }
 
 }
 
-KeyItem::~KeyItem()
+KeyItem::KeyItem(const CssmClient::Key &key) :
+    ItemImpl(key->keyClass() + CSSM_DL_DB_RECORD_PUBLIC_KEY, (OSType)0, (UInt32)0, (const void*)NULL),
+       mKey(key)
+{
+       if (key->keyClass() > CSSM_KEYCLASS_SESSION_KEY)
+               MacOSError::throwMe(paramErr);
+}
+
+KeyItem::~KeyItem() throw()
 {
 {
-       if (mKey)
-       {
-               CssmClient::SSDbUniqueRecord uniqueId(ssDbUniqueRecord());
-               uniqueId->database()->csp()->freeKey(*mKey);
-               uniqueId->allocator().free(mKey);
-       }
 }
 
 void
 KeyItem::update()
 {
 }
 
 void
 KeyItem::update()
 {
-       MacOSError::throwMe(unimpErr);
+       ItemImpl::update();
 }
 
 Item
 }
 
 Item
-KeyItem::copyTo(const Keychain &keychain)
+KeyItem::copyTo(const Keychain &keychain, Access *newAccess)
 {
 {
-       MacOSError::throwMe(unimpErr);
+       if (!keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP)
+               MacOSError::throwMe(errSecInvalidKeychain);
+
+       /* Get the destination keychains db. */
+       SSDb ssDb(safe_cast<SSDbImpl *>(&(*keychain->database())));
+
+       /* Make sure mKey is valid. */
+       key();
+
+       // 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, 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 */)));
+
+       /* 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);
+       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);
+
+       /* 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;
+       ResourceControlContext rcc;
+       maker.initialOwner(rcc, NULL);
+       unwrap.aclEntry(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, kSecKeyLabel, 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(kSecKeyLabel);
+       oldDbAttributes.add(kSecKeyPrintName);
+       oldDbAttributes.add(kSecApplicationTag);
+       mUniqueId->get(&oldDbAttributes, NULL);
+       uniqueId->modify(recordType(), &oldDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
+
+       /* Set the acl and owner on the unwrapped key. */
+       access->setAccess(*unwrappedKey, maker);
+
+       /* Return a keychain items which represents the new key.  */
+       Item item(keychain->item(recordType(), uniqueId));
+
+    KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, item);
+
+       return item;
 }
 
 void
 }
 
 void
@@ -81,18 +185,575 @@ KeyItem::ssDbUniqueRecord()
        return CssmClient::SSDbUniqueRecord(safe_cast<Security::CssmClient::SSDbUniqueRecordImpl *>(impl));
 }
 
        return CssmClient::SSDbUniqueRecord(safe_cast<Security::CssmClient::SSDbUniqueRecordImpl *>(impl));
 }
 
-const CssmKey &
-KeyItem::cssmKey()
+CssmClient::Key &
+KeyItem::key()
 {
        if (!mKey)
        {
                CssmClient::SSDbUniqueRecord uniqueId(ssDbUniqueRecord());
                CssmDataContainer dataBlob(uniqueId->allocator());
                uniqueId->get(NULL, &dataBlob);
 {
        if (!mKey)
        {
                CssmClient::SSDbUniqueRecord uniqueId(ssDbUniqueRecord());
                CssmDataContainer dataBlob(uniqueId->allocator());
                uniqueId->get(NULL, &dataBlob);
-               mKey = reinterpret_cast<CssmKey *>(dataBlob.Data);
-               dataBlob.Data = NULL;
-               dataBlob.Length = 0;
+               mKey = CssmClient::Key(uniqueId->database()->csp(), *reinterpret_cast<CssmKey *>(dataBlob.Data));
        }
 
        }
 
-       return *mKey;
+       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();
+}
+
+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(),
+                         NULL,
+                         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);
+
+       AclFactory factory;
+       switch (credentialType)
+       {
+       case kSecCredentialTypeDefault:
+               return globals().credentials();
+       case kSecCredentialTypeWithUI:
+               return factory.promptCred();
+       case kSecCredentialTypeNoUI:
+               return factory.nullCred();
+       default:
+               MacOSError::throwMe(paramErr);
+       }
+}
+
+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);
+
+       SSDb ssDb(safe_cast<SSDbImpl *>(&(*keychain->database())));
+       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, 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;
+
+       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, kSecKeyLabel, 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, kSecKeyLabel, 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;
+
+               std::string description(initialAccess->promptDescription());
+               // 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(kSecKeyLabel, pubKeyHash);
+               pubDbAttributes.add(kSecKeyPrintName, description);
+               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(kSecKeyLabel, pubKeyHash);
+               privDbAttributes.add(kSecKeyPrintName, description);
+               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);
+
+               // Make the public key acl completely open
+               Access pubKeyAccess;
+               pubKeyAccess.setAccess(*publicKey, maker);
+
+               // Create keychain items which will represent the keys.
+               outPublicKey = safe_cast<KeyItem*>(&(*keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId)));
+               outPrivateKey = safe_cast<KeyItem*>(&(*keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId)));
+       }
+       catch (...)
+       {
+               if (freeKeys)
+               {
+                       // Delete the keys if something goes wrong so we don't end up with inaccesable 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);
+}
+
+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);
+
+       SSDb ssDb(safe_cast<SSDbImpl *>(&(*keychain->database())));
+       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;
+       // @@@ Potentially provide a credential argument which allows us to unwrap 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;
+
+       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 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, kSecKeyLabel, 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, kSecKeyLabel, 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);
+
+               std::string description(initialAccess->promptDescription());
+               // 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(kSecKeyPrintName, description);
+               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(kSecKeyPrintName, description);
+               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
+               Access pubKeyAccess;
+               pubKeyAccess.setAccess(*publicKey, maker);
+
+               // Create keychain items which will represent the keys.
+               outPublicKey = safe_cast<KeyItem*>(&(*keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId)));
+               outPrivateKey = safe_cast<KeyItem*>(&(*keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId)));
+       }
+       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);
+}
+
+KeyItem *
+KeyItem::generate(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;
+       KeyItem *outKey;
+
+       if (keychain)
+       {
+               if (!keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP)
+                       MacOSError::throwMe(errSecInvalidKeychain);
+       
+               ssDb = SSDb(safe_cast<SSDbImpl *>(&(*keychain->database())));
+               csp = keychain->csp();
+
+               // Generate a random label to use initially
+               CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
+               random.generate(label, label.Length);
+               plabel = &label;
+       }
+       else
+       {
+               // Not a persistant 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 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.
+               cred = maker.cred();
+               prcc = &rcc;
+       }
+
+       CSSM_KEY cssmKey;
+       
+       CSSM_CC_HANDLE ccHandle = 0;
+
+       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 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 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, kSecKeyLabel, label);
+                       CssmClient::Key key;
+                       if (!dbCursor->nextKey(&dbAttributes, key, uniqueId))
+                               MacOSError::throwMe(errSecItemNotFound);
+               
+                       // Finally fix the acl and owner of the key to the specified access control settings.
+                       if (initialAccess)
+                               initialAccess->setAccess(*key, maker);
+
+                       // Create keychain items which will represent the keys.
+                       outKey = safe_cast<KeyItem*>(&(*keychain->item(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, uniqueId)));
+               }
+               else
+               {
+                       CssmClient::Key tempKey(csp, cssmKey);
+                       outKey = new KeyItem(tempKey);
+               }
+       }
+       catch (...)
+       {
+               if (freeKey)
+               {
+                       // Delete the keys if something goes wrong so we don't end up with inaccesable 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);
+
+       return outKey;
 }
 }