+
+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;
+}