#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(kSecKeyLabel, 6, "Label", 0, NULL, BLOB);
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),
- mKey(NULL)
+ mKey()
{
}
KeyItem::KeyItem(const Keychain &keychain, const PrimaryKey &primaryKey) :
ItemImpl(keychain, primaryKey),
- mKey(NULL)
+ mKey()
{
}
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()
{
- MacOSError::throwMe(unimpErr);
+ ItemImpl::update();
}
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
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);
- 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 *
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)
{
uint32 publicKeyAttr,
CSSM_KEYUSE privateKeyUsage,
uint32 privateKeyAttr,
- RefPointer<Access> initialAccess,
- RefPointer<KeyItem> &outPublicKey,
- RefPointer<KeyItem> &outPrivateKey)
+ SecPointer<Access> initialAccess,
+ SecPointer<KeyItem> &outPublicKey,
+ SecPointer<KeyItem> &outPrivateKey)
{
bool freeKeys = false;
bool deleteContext = false;
Keychain keychain,
const CSSM_KEY &publicWrappedKey,
const CSSM_KEY &privateWrappedKey,
- RefPointer<Access> initialAccess,
- RefPointer<KeyItem> &outPublicKey,
- RefPointer<KeyItem> &outPrivateKey)
+ SecPointer<Access> initialAccess,
+ SecPointer<KeyItem> &outPublicKey,
+ SecPointer<KeyItem> &outPrivateKey)
{
bool freePublicKey = false;
bool freePrivateKey = false;
cssmData = reinterpret_cast<CssmData *>(outData);
CssmData &pubKeyHash = *cssmData;
- status = CSSM_CSP_CreateSymmetricContext(csp->handle(), publicWrappedKey.KeyHeader.AlgorithmId, CSSM_ALGMODE_NONE, NULL, NULL, NULL, CSSM_PADDING_NONE, NULL, &ccHandle);
+ 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;
CssmError::throwMe(status);
// Unwrap the the keys
+ CSSM_DATA descriptiveData = {0, NULL};
+
status = CSSM_UnwrapKey(
ccHandle,
NULL,
&publicWrappedKey,
publicWrappedKey.KeyHeader.KeyUsage,
- publicWrappedKey.KeyHeader.KeyAttr,
+ publicWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT,
&pubKeyHash,
&rcc,
&publicCssmKey,
- NULL);
+ &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,
+ privateWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT,
&pubKeyHash,
&rcc,
&privateCssmKey,
- NULL);
-
+ &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
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;
+}